diff --git a/sys/netlink/netlink_domain.c b/sys/netlink/netlink_domain.c --- a/sys/netlink/netlink_domain.c +++ b/sys/netlink/netlink_domain.c @@ -46,6 +46,8 @@ #include #include /* priv_check */ +#include + #include #include #include @@ -268,6 +270,18 @@ return (0); } +static int +nlp_thread_init(struct thread *td, void *thunk) +{ + struct nlpcb *nlp = thunk; + + nlp->nl_thread = td; + + cpu_fork_kthread_handler(td, nl_taskqueue_handler, nlp); + + return (0); +} + static int nl_pru_attach(struct socket *so, int proto, struct thread *td) { @@ -312,11 +326,20 @@ refcount_init(&nlp->nl_refcount, 1); nl_init_io(nlp); + error = thread_create(td, NULL, nlp_thread_init, nlp); + if (error != 0) { + free(nlp, M_PCB); + return (error); + } + NL_LOG(LOG_DEBUG2, "thread %p created", nlp->nl_thread); + +#if 0 nlp->nl_taskqueue = taskqueue_create("netlink_socket", M_WAITOK, taskqueue_thread_enqueue, &nlp->nl_taskqueue); TASK_INIT(&nlp->nl_task, 0, nl_taskqueue_handler, nlp); - taskqueue_start_threads(&nlp->nl_taskqueue, 1, PWAIT, - "netlink_socket (PID %u)", nlp->nl_process_id); + taskqueue_start_threads_in_proc(&nlp->nl_taskqueue, 1, PWAIT, + td->td_proc, "netlink_socket (PID %u)", nlp->nl_process_id); +#endif NLCTL_WLOCK(ctl); /* XXX: check ctl is still alive */ @@ -477,9 +500,26 @@ nlp->nl_active = false; NLP_UNLOCK(nlp); + NL_LOG(LOG_DEBUG2, "Waking up thread %p", nlp->nl_thread); + wakeup_any(nlp); + + NLP_LOCK(nlp); + if (!nlp->nl_dead) { + NL_LOG(LOG_DEBUG2, "Waiting till the thread dies"); + mtx_sleep(&nlp->nl_epoch_ctx, &nlp->nl_lock, 0, "dying", 0); + } + NL_LOG(LOG_DEBUG2, "Thread %p set dead to %s", nlp->nl_thread, nlp->nl_dead ? "true" : "false"); + MPASS(nlp->nl_dead); + NLP_UNLOCK(nlp); + + int error = kern_thr_exit(nlp->nl_thread); + NL_LOG(LOG_DEBUG2, "thread_exit(%p) returned %d", nlp->nl_thread, error); + +#if 0 /* Wait till all scheduled work has been completed */ taskqueue_drain_all(nlp->nl_taskqueue); taskqueue_free(nlp->nl_taskqueue); +#endif NLCTL_WLOCK(ctl); NLP_LOCK(nlp); diff --git a/sys/netlink/netlink_io.c b/sys/netlink/netlink_io.c --- a/sys/netlink/netlink_io.c +++ b/sys/netlink/netlink_io.c @@ -129,7 +129,7 @@ { if (!nlp->nl_task_pending) { nlp->nl_task_pending = true; - taskqueue_enqueue(nlp->nl_taskqueue, &nlp->nl_task); + wakeup_any(nlp); NL_LOG(LOG_DEBUG3, "taskqueue scheduled"); } else { NL_LOG(LOG_DEBUG3, "taskqueue schedule skipped"); @@ -301,13 +301,33 @@ } void -nl_taskqueue_handler(void *_arg, int pending) +nl_taskqueue_handler(void *_arg) { struct nlpcb *nlp = (struct nlpcb *)_arg; CURVNET_SET(nlp->nl_socket->so_vnet); - nl_process_received(nlp); + + NL_LOG(LOG_DEBUG2, "work thread %p for nlp %p started", nlp->nl_thread, nlp); + + NLP_LOCK(nlp); + while (true) { + NL_LOG(LOG_DEBUG2, "sleeping / waiting for work"); + mtx_sleep(nlp, &nlp->nl_lock, 0, "nlp sleep", 0); + NL_LOG(LOG_DEBUG2, "worken up, socket is %s", nlp->nl_active ? "active":"dying"); + if (__predict_false(!nlp->nl_active)) + break; + NLP_UNLOCK(nlp); + nl_process_received(nlp); + NLP_LOCK(nlp); + } + nlp->nl_dead = true; + NL_LOG(LOG_DEBUG2, "SET dead to true"); + NLP_UNLOCK(nlp); + CURVNET_RESTORE(); + + wakeup_one(&nlp->nl_epoch_ctx); + NL_LOG(LOG_DEBUG2, "work thread %p for nlp %p ended", nlp->nl_thread, nlp); } static __noinline void diff --git a/sys/netlink/netlink_var.h b/sys/netlink/netlink_var.h --- a/sys/netlink/netlink_var.h +++ b/sys/netlink/netlink_var.h @@ -61,10 +61,10 @@ bool nl_task_pending; bool nl_tx_blocked; /* No new requests accepted */ bool nl_linux; /* true if running under compat */ + bool nl_dead; struct nl_io_queue rx_queue; struct nl_io_queue tx_queue; - struct taskqueue *nl_taskqueue; - struct task nl_task; + struct thread *nl_thread; struct ucred *nl_cred; /* Copy of nl_socket->so_cred */ uint64_t nl_dropped_bytes; uint64_t nl_dropped_messages; @@ -139,7 +139,7 @@ void nl_init_io(struct nlpcb *nlp); void nl_free_io(struct nlpcb *nlp); -void nl_taskqueue_handler(void *_arg, int pending); +void nl_taskqueue_handler(void *_arg); int nl_receive_async(struct mbuf *m, struct socket *so); void nl_process_receive_locked(struct nlpcb *nlp); diff --git a/sys/netlink/route/iface.c b/sys/netlink/route/iface.c --- a/sys/netlink/route/iface.c +++ b/sys/netlink/route/iface.c @@ -928,14 +928,11 @@ } static void -rtnl_handle_ifevent(struct ifnet *ifp, int nlmsg_type, bool modify, int if_flags_mask) +rtnl_handle_ifevent(struct ifnet *ifp, int nlmsg_type, int if_flags_mask) { struct nlmsghdr hdr = { .nlmsg_type = nlmsg_type }; struct nl_writer nw = {}; - if (modify) - hdr.nlmsg_flags |= NLM_F_REPLACE; - if (!nl_has_listeners(NETLINK_ROUTE, RTNLGRP_LINK)) return;