diff --git a/include/nuttx/wqueue.h b/include/nuttx/wqueue.h index c3c18b679c9..31867ad1c99 100644 --- a/include/nuttx/wqueue.h +++ b/include/nuttx/wqueue.h @@ -290,7 +290,8 @@ enum work_evtype_e WORK_TCP_WRITEBUFFER, /* Notify that TCP write buffer is empty */ WORK_TCP_DISCONNECT, /* Notify loss of TCP connection */ WORK_UDP_READAHEAD, /* Notify that UDP read-ahead data is available */ - WORK_UDP_WRITEBUFFER /* Notify that UDP write buffer is empty */ + WORK_UDP_WRITEBUFFER, /* Notify that UDP write buffer is empty */ + WORK_NETLINK_RESPONSE /* Notify thtat Netlink response is available */ }; /* This structure describes one notification and is provided as input to @@ -562,7 +563,7 @@ int work_notifier_teardown(int key); * need to call work_notifier_setup() once again. * * Input Parameters: - * evtype - The type of the event that just occurred. + * evtype - The type of the event that just occurred. * qualifier - Event qualifier to distinguish different cases of the * generic event type. * diff --git a/net/netdev/netdev.h b/net/netdev/netdev.h index b1e3355a36b..3025def08e5 100644 --- a/net/netdev/netdev.h +++ b/net/netdev/netdev.h @@ -501,9 +501,9 @@ int netdev_ipv6_ifconf(FAR struct lifconf *lifc); * Name: netdown_notifier_setup * * Description: - * Set up to perform a callback to the worker function the network goes - * down. The worker function will execute on the high priority worker - * thread. + * Set up to perform a callback to the worker function when the network + * goes down. The worker function will execute on the high priority + * worker thread. * * Input Parameters: * worker - The worker function to execute on the high priority work diff --git a/net/netdev/netdown_notifier.c b/net/netdev/netdown_notifier.c index dbfcc8563ea..371fbbe1006 100644 --- a/net/netdev/netdown_notifier.c +++ b/net/netdev/netdown_notifier.c @@ -58,9 +58,9 @@ * Name: netdown_notifier_setup * * Description: - * Set up to perform a callback to the worker function the network goes - * down. The worker function will execute on the low priority worker - * thread. + * Set up to perform a callback to the worker function when the network + * goes down. The worker function will execute on the high priority + * worker thread. * * Input Parameters: * worker - The worker function to execute on the low priority work diff --git a/net/netlink/Kconfig b/net/netlink/Kconfig index 9b5ee36b7fb..b7d094ea133 100644 --- a/net/netlink/Kconfig +++ b/net/netlink/Kconfig @@ -8,6 +8,8 @@ menu "Netlink Socket Support" config NET_NETLINK bool "Netlink socket support" default n + depends on SCHED_WORKQUEUE + select WQUEUE_NOTIFIER ---help--- Enable support for Netlink-like IPC sockets that will permit user- space applications to interact with network services. @@ -26,23 +28,6 @@ config NETLINK_CONNS ---help--- Maximum number of Netlink connections (all tasks). -config NETLINK_MAXPENDING - int "Max pending responses" - default 1 - ---help--- - This defines the maximum number of threads that can be waiting for - a NetLink response. If there is never more than one recv() or - recvfrom() per socket, then there need be only 1. This only - accounts for a perverse case where more than one thread is waiting - on recv() or recvfrom(). - -config NETLINK_SIGNAL - int "Response notification signal" - default 15 - ---help--- - This is the signal number that is used to wake up threads waiting - for a response to be received. - menu "Netlink Protocols" config NETLINK_ROUTE diff --git a/net/netlink/Make.defs b/net/netlink/Make.defs index b80690aaddc..5e1a58a0936 100644 --- a/net/netlink/Make.defs +++ b/net/netlink/Make.defs @@ -38,7 +38,7 @@ ifeq ($(CONFIG_NET_NETLINK),y) SOCK_CSRCS += netlink_sockif.c -NET_CSRCS += netlink_conn.c +NET_CSRCS += netlink_conn.c netlink_notifier.c ifeq ($(CONFIG_NETLINK_ROUTE),y) NET_CSRCS += netlink_route.c diff --git a/net/netlink/netlink.h b/net/netlink/netlink.h index b4c314dbb32..f7f524feebc 100644 --- a/net/netlink/netlink.h +++ b/net/netlink/netlink.h @@ -90,13 +90,9 @@ struct netlink_conn_s /* poll() support */ + int key; /* used to cancel notifications */ FAR sem_t *pollsem; /* Used to wakeup poll() */ FAR pollevent_t *pollevent; /* poll() wakeup event */ - struct sigaction oact; /* Previous signal action */ - - /* Threads waiting for a response */ - - pid_t waiter[CONFIG_NETLINK_MAXPENDING]; /* Queued response data */ @@ -180,6 +176,68 @@ FAR struct netlink_conn_s *netlink_nextconn(FAR struct netlink_conn_s *conn); FAR struct netlink_conn_s *netlink_active(FAR struct sockaddr_nl *addr); +/**************************************************************************** + * Name: netlink_notifier_setup + * + * Description: + * Set up to perform a callback to the worker function the Netlink + * response data is received. The worker function will execute on the low + * priority worker thread. + * + * Input Parameters: + * worker - The worker function to execute on the low priority work + * queue when Netlink response data is available. + * conn - The Netlink connection where the response is expected. + * arg - A user-defined argument that will be available to the worker + * function when it runs. + * + * Returned Value: + * Zero (OK) is returned if the notification was successfully set up. + * A negated error value is returned if an unexpected error occurred + * and no notification will occur. + * + ****************************************************************************/ + +int netlink_notifier_setup(worker_t worker, FAR struct netlink_conn_s *conn, + FAR void *arg); + +/**************************************************************************** + * Name: netlink_notifier_teardown + * + * Description: + * Eliminate a Netlink response notification previously setup by + * netlink_notifier_setup(). This function should only be called if the + * notification should be aborted prior to the notification. The + * notification will automatically be torn down after the notification. + * + * Input Parameters: + * conn - Teardown the notification for this Netlink connection. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned on + * any failure. + * + ****************************************************************************/ + +int netlink_notifier_teardown(FAR struct netlink_conn_s *conn); + +/**************************************************************************** + * Name: netlink_notifier_signal + * + * Description: + * New Netlink response data is available. Execute worker thread + * functions for all threads that wait for response data. + * + * Input Parameters: + * conn - The Netlink connection where the response was just buffered. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +void netlink_notifier_signal(FAR struct netlink_conn_s *conn); + /**************************************************************************** * Name: netlink_tryget_response * @@ -212,8 +270,9 @@ FAR struct netlink_response_s * * * Returned Value: * The next response from the head of the pending response list is - * always returned. This function will block until a response is - * received if the pending response list is empty. + * returned. This function will block until a response is received if + * the pending response list is empty. NULL will be returned only in the + * event of a failure. * ****************************************************************************/ @@ -238,7 +297,7 @@ bool netlink_check_response(FAR struct socket *psock); * * Description: * Notify a thread until a response be available. The thread will be - * notified via CONFIG_NETLINK_SIGNAL when the response becomes available. + * notified via work queue notifier when the response becomes available. * * Returned Value: * Zero (OK) is returned if the response is already available. Not signal @@ -250,19 +309,6 @@ bool netlink_check_response(FAR struct socket *psock); int netlink_notify_response(FAR struct socket *psock); -/**************************************************************************** - * Name: netlink_notify_cancel - * - * Description: - * Cancel a notification previously created with netlink_notify_response(). - * - * Returned Value: - * Zero (OK) is always returned. - * - ****************************************************************************/ - -int netlink_notify_cancel(FAR struct socket *psock); - /**************************************************************************** * Name: netlink_route_sendto() * diff --git a/net/netlink/netlink_conn.c b/net/netlink/netlink_conn.c index f974f5d833d..0ee661a30c9 100644 --- a/net/netlink/netlink_conn.c +++ b/net/netlink/netlink_conn.c @@ -50,7 +50,6 @@ #include #include -#include #include #include #include @@ -111,112 +110,40 @@ static void _netlink_semgive(FAR sem_t *sem) } /**************************************************************************** - * Name: netlink_notify_waiters + * Name: netlink_response_available * * Description: - * Notify all threads waiting for a response. + * Handle a Netlink response available notification. * - * Assumptions: - * The network is locked. + * Input Parameters: + * Standard work handler parameters + * + * Returned Value: + * None * ****************************************************************************/ -static void netlink_notify_waiters(FAR struct netlink_conn_s *conn) +static void netlink_response_available(FAR void *arg) { - int ret; - int i; - - /* Notify every pending thread. Lock the scheduler while we do this so - * that there is no thrashing: All waiters will be restarted, but only - * the highest priority waiter will get to run and it will receive the - * response. + /* The entire notifier entry is passed to us. That is because we are + * responsible for disposing of the entry via kmm_free() when we are + * finished with it. */ - sched_lock(); - for (i = 0; i < CONFIG_NETLINK_MAXPENDING; i++) - { - if (conn->waiter[i] > 0) - { -#ifdef CONFIG_CAN_PASS_STRUCTS - union sigval value; + FAR struct work_notifier_entry_s *notifier = + (FAR struct work_notifier_entry_s *)arg; + FAR sem_t *waitsem; - /* Send the CONFIG_NETLINK_SIGNAL signal to the waiter. */ + DEBUGASSERT(notifier != NULL && notifier->info.qualifier != NULL); + waitsem = (FAR sem_t *)notifier->info.arg; - value.sival_ptr = conn; - ret = nxsig_queue((int)conn->waiter[i], - (int)CONFIG_NETLINK_SIGNAL, value); -#else - ret = nxsig_queue((int)conn->waiter[i], - (int)CONFIG_NETLINK_SIGNAL, conn); -#endif - if (ret < 0) - { - nerr("ERROR: nxsig_queue() failed: %d\n", ret); - UNUSED(ret); - } + /* Free the notifier entry */ - conn->waiter[i] = NETLINK_NO_WAITER; - } - } + kmm_free(notifier); - sched_unlock(); -} + /* wakeup the waiter */ -/**************************************************************************** - * Name: netlink_add_waiter - * - * Description: - * Add one more waiter to the list of waiters. - * - * Assumptions: - * The network is locked. - * - ****************************************************************************/ - -static int netlink_add_waiter(FAR struct netlink_conn_s *conn) -{ - int i; - - for (i = 0; i < CONFIG_NETLINK_MAXPENDING; i++) - { - if (conn->waiter[i] <= 0) - { - conn->waiter[i] = getpid(); - return OK; - } - } - - nerr("ERROR: Too many waiters\n"); - DEBUGPANIC(); - return -ENOSPC; -} - -/**************************************************************************** - * Name: netlink_remove_waiter - * - * Description: - * Remove a waiter to the list of waiters. - * - * Assumptions: - * The network is locked. - * - ****************************************************************************/ - -static int netlink_remove_waiter(FAR struct netlink_conn_s *conn, - pid_t waiter) -{ - int i; - - for (i = 0; i < CONFIG_NETLINK_MAXPENDING; i++) - { - if (conn->waiter[i] == waiter) - { - conn->waiter[i] = NETLINK_NO_WAITER; - break; - } - } - - return OK; + nxsem_post(waitsem); } /**************************************************************************** @@ -265,7 +192,6 @@ void netlink_initialize(void) FAR struct netlink_conn_s *netlink_alloc(void) { FAR struct netlink_conn_s *conn; - int i; /* The free list is protected by a semaphore (that behaves like a mutex). */ @@ -277,13 +203,6 @@ FAR struct netlink_conn_s *netlink_alloc(void) memset(conn, 0, sizeof(*conn)); - /* With no waiters */ - - for (i = 0; i < CONFIG_NETLINK_MAXPENDING; i++) - { - conn->waiter[i] = NETLINK_NO_WAITER; - } - /* Enqueue the connection into the active list */ dq_addlast(&conn->node, &g_active_netlink_connections); @@ -414,7 +333,7 @@ void netlink_add_response(NETLINK_HANDLE handle, /* Notify any waiters that a response is available */ - netlink_notify_waiters(conn); + netlink_notifier_signal(conn); net_unlock(); } @@ -468,8 +387,9 @@ FAR struct netlink_response_s * * * Returned Value: * The next response from the head of the pending response list is - * always returned. This function will block until a response is - * received if the pending response list is empty. + * returned. This function will block until a response is received if + * the pending response list is empty. NULL will be returned only in the + * event of a failure. * ****************************************************************************/ @@ -478,10 +398,6 @@ FAR struct netlink_response_s * { FAR struct netlink_response_s *resp; FAR struct netlink_conn_s *conn; - FAR struct siginfo info; - unsigned int count; - sigset_t set; - irqstate_t flags; int ret; DEBUGASSERT(psock != NULL && psock->s_conn != NULL); @@ -496,44 +412,64 @@ FAR struct netlink_response_s * net_lock(); while ((resp = netlink_tryget_response(psock)) == NULL) { - /* Add this task as a waiter */ + sem_t waitsem; - ret = netlink_add_waiter(conn); + /* Set up a semaphore to notify us when a response is queued. */ + + (void)sem_init(&waitsem, 0, 0); + (void)nxsem_setprotocol(&waitsem, SEM_PRIO_NONE); + + /* Set up a notifier to post the semaphore when a response is + * received. + */ + + ret = netlink_notifier_setup(netlink_response_available, conn, + &waitsem); if (ret < 0) { - nerr("ERROR: netlink_add_waiter failed: %d\n", ret); + nerr("ERROR: netlink_notifier_setup() failed: %d\n", ret); } - - /* Break any network lock while we wait */ - - flags = enter_critical_section(); - ret = net_breaklock(&count); - if (ret < 0) + else { - /* net_breaklock() would only fail if we were not the holder of - * lock. But we do hold the lock? + /* Call netlink_notify_response() to receive a notification + * when a response has been queued. */ - nerr("ERROR: net_breaklock failed: %d\n", ret); - DEBUGPANIC(); + ret = netlink_notify_response(psock); + if (ret < 0) + { + nerr("ERROR: netlink_notify_response() failed: %d\n", ret); + } + else if (ret == 0) + { + /* The return value of zero means that a response is + * already available and that no notification is + * forthcoming. + */ + } + else + { + /* Otherwise, we have to wait */ + + _netlink_semtake(&waitsem); + } } - /* Wait for a response */ + /* Clean-up the semaphore */ + + sem_destroy(&waitsem); + netlink_notifier_teardown(conn); + + /* Check for any failures */ - sigemptyset(&set); - sigaddset(&set, CONFIG_NETLINK_SIGNAL); - ret = sigwaitinfo(&set, &info); if (ret < 0) { - nerr("ERROR: sigwaitinfo() failed: %d\n", ret); + resp = NULL; + break; } - - /* Restore the network lock */ - - net_restorelock(count); - leave_critical_section(flags); } + net_unlock(); return resp; } @@ -567,7 +503,7 @@ bool netlink_check_response(FAR struct socket *psock) * * Description: * Notify a thread until a response be available. The thread will be - * notified via CONFIG_NETLINK_SIGNAL when the response becomes available. + * notified via work queue notifier when the response becomes available. * * Returned Value: * Zero (OK) is returned if the response is already available. Not signal @@ -580,8 +516,6 @@ bool netlink_check_response(FAR struct socket *psock) int netlink_notify_response(FAR struct socket *psock) { FAR struct netlink_conn_s *conn; - FAR struct siginfo info; - sigset_t set; int ret = 0; DEBUGASSERT(psock != NULL && psock->s_conn != NULL); @@ -592,61 +526,57 @@ int netlink_notify_response(FAR struct socket *psock) net_lock(); if (((FAR struct netlink_response_s *)sq_peek(&conn->resplist)) == NULL) { - /* No.. Add this task as a waiter */ + sem_t waitsem; - ret = netlink_add_waiter(conn); + /* No.. Set up a semaphore to notify us when a response is queued. */ + + (void)sem_init(&waitsem, 0, 0); + (void)nxsem_setprotocol(&waitsem, SEM_PRIO_NONE); + + /* Set up a notifier to post the semaphore when a response is + * received. + */ + + ret = netlink_notifier_setup(netlink_response_available, conn, + &waitsem); if (ret < 0) { - nerr("ERROR: netlink_add_waiter failed: %d\n", ret); + nerr("ERROR: netlink_notifier_setup() failed: %d\n", ret); } else { - /* Set up to signal when a response is available */ + /* Call netlink_notify_response() to receive a notification + * when a response has been queued. + */ - sigemptyset(&set); - sigaddset(&set, CONFIG_NETLINK_SIGNAL); - ret = sigwaitinfo(&set, &info); + ret = netlink_notify_response(psock); if (ret < 0) { - nerr("ERROR: sigwaitinfo() failed: %d\n", ret); + nerr("ERROR: netlink_notify_response() failed: %d\n", ret); + } + else if (ret == 0) + { + /* The return value of zero means that a response is + * already available and that no notification is + * forthcoming. + */ } else { - ret = 1; + /* Otherwise, we have to wait */ + + _netlink_semtake(&waitsem); } } + + /* Tear-down the notifier and the semaphore */ + + netlink_notifier_teardown(conn); + sem_destroy(&waitsem); } net_unlock(); - return ret; -} - -/**************************************************************************** - * Name: netlink_notify_cancel - * - * Description: - * Cancel a notification previously created with netlink_notify_response(). - * - * Returned Value: - * Zero (OK) is always returned. - * - ****************************************************************************/ - -int netlink_notify_cancel(FAR struct socket *psock) -{ - FAR struct netlink_conn_s *conn; - - DEBUGASSERT(psock != NULL && psock->s_conn != NULL); - conn = (FAR struct netlink_conn_s *)psock->s_conn; - - /* Remove this thread as waiter for response notifications for this - * socket. - */ - - net_lock(); - (void)netlink_remove_waiter(conn, getpid()); - net_unlock(); - return OK; + return ret < 0 ? ret : OK; } #endif /* CONFIG_NET_NETLINK */ diff --git a/net/netlink/netlink_notifier.c b/net/netlink/netlink_notifier.c new file mode 100644 index 00000000000..d00d773f423 --- /dev/null +++ b/net/netlink/netlink_notifier.c @@ -0,0 +1,148 @@ +/**************************************************************************** + * net/netlink/netlink_notifier.c + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include + +#include "netlink/netlink.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: netlink_notifier_setup + * + * Description: + * Set up to perform a callback to the worker function the Netlink + * response data is received. The worker function will execute on the low + * priority worker thread. + * + * Input Parameters: + * worker - The worker function to execute on the low priority work + * queue when Netlink response data is available. + * conn - The Netlink connection where the response is expected. + * arg - A user-defined argument that will be available to the worker + * function when it runs. + * + * Returned Value: + * Zero (OK) is returned if the notification was successfully set up. + * A negated error value is returned if an unexpected error occurred + * and no notification will occur. + * + ****************************************************************************/ + +int netlink_notifier_setup(worker_t worker, FAR struct netlink_conn_s *conn, + FAR void *arg) +{ + struct work_notifier_s info; + + DEBUGASSERT(worker != NULL && conn != NULL); + + /* This is just a simple wrapper around work_notifer_setup(). */ + + info.evtype = WORK_NETLINK_RESPONSE; + info.qid = LPWORK; + info.qualifier = conn; + info.arg = arg; + info.worker = worker; + + conn->key = work_notifier_setup(&info); + return OK; +} + +/**************************************************************************** + * Name: netlink_notifier_teardown + * + * Description: + * Eliminate a Netlink response notification previously setup by + * netlink_notifier_setup(). This function should only be called if the + * notification should be aborted prior to the notification. The + * notification will automatically be torn down after the notification. + * + * Input Parameters: + * conn - Teardown the notification for this Netlink connection. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned on + * any failure. + * + ****************************************************************************/ + +int netlink_notifier_teardown(FAR struct netlink_conn_s *conn) +{ + DEBUGASSERT(conn != NULL); + int ret = OK; + + /* This is just a simple wrapper around work_notifier_teardown(). */ + + if (conn->key > 0) + { + ret = work_notifier_teardown(conn->key); + conn->key = 0; + } + + return ret; +} + +/**************************************************************************** + * Name: netlink_notifier_signal + * + * Description: + * New Netlink response data is available. Execute worker thread + * functions for all threads that wait for response data. + * + * Input Parameters: + * conn - The Netlink connection where the response was just buffered. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +void netlink_notifier_signal(FAR struct netlink_conn_s *conn) +{ + /* This is just a simple wrapper around work_notifier_signal(). */ + + return work_notifier_signal(WORK_NETLINK_RESPONSE, conn); +} diff --git a/net/netlink/netlink_sockif.c b/net/netlink/netlink_sockif.c index d3464793a69..378cbaecdbc 100644 --- a/net/netlink/netlink_sockif.c +++ b/net/netlink/netlink_sockif.c @@ -49,8 +49,9 @@ #include #include +#include #include -#include +#include #include #include "netlink/netlink.h" @@ -486,73 +487,45 @@ static int netlink_accept(FAR struct socket *psock, FAR struct sockaddr *addr, return -EOPNOTSUPP; } -/**************************************************************************** - * Name: netlink_notify_teardown - * - * Description: - * Teardown a Netlink response notification. - * - * Input Parameters: - * conn - Netlink connection structure. - * - * Returned Value: - * None - * - ****************************************************************************/ - -static void netlink_notify_teardown(FAR struct netlink_conn_s *conn) -{ - sched_lock(); - if (conn->pollsem != NULL || conn->pollevent != NULL) - { - /* Allow another poll() */ - - conn->pollsem = NULL; - conn->pollevent = NULL; - - /* Detach the signal handler by restoring the previous signal handler - * state. - */ - - (void)nxsig_action(CONFIG_NETLINK_SIGNAL, &conn->oact, NULL, true); - } - - sched_unlock(); -} - /**************************************************************************** * Name: netlink_response_available * * Description: - * Handle a Netlink response available notification + * Handle a Netlink response available notification. * * Input Parameters: - * Standard signal handler parameters + * Standard work handler parameters * * Returned Value: * None * ****************************************************************************/ -static void netlink_response_available(int signo, FAR siginfo_t *siginfo, - FAR void *context) +static void netlink_response_available(FAR void *arg) { - FAR struct netlink_conn_s *conn; - - /* The si_value member should be a reference to a Netlink connection - * structure. + /* The entire notifier entry is passed to us. That is because we are + * responsible for disposing of the entry via kmm_free() when we are + * finished with it. */ - DEBUGASSERT(siginfo != NULL); - conn = (FAR struct netlink_conn_s *)siginfo->si_value.sival_ptr; - DEBUGASSERT(conn != NULL); + FAR struct work_notifier_entry_s *notifier = + (FAR struct work_notifier_entry_s *)arg; + FAR struct netlink_conn_s *conn; - /* This should always be true ... but maybe not in some race condition? - * REVISIT: Is locking the scheduler strong enough Kung Fu here? Could - * the awakened thread try to poll() again on another CPU? + DEBUGASSERT(notifier != NULL && notifier->info.qualifier != NULL); + conn = (FAR struct netlink_conn_s *)notifier->info.qualifier; + + /* Free the notifier entry */ + + kmm_free(notifier); + + /* The following should always be true ... but maybe not in some race + * condition? */ sched_lock(); + net_lock(); + if (conn->pollsem != NULL && conn->pollevent != NULL) { /* Wake up the poll() with POLLIN */ @@ -565,7 +538,12 @@ static void netlink_response_available(int signo, FAR siginfo_t *siginfo, nwarn("WARNING: Missing references in connection.\n"); } - netlink_notify_teardown(conn); + /* Allow another poll() */ + + conn->pollsem = NULL; + conn->pollevent = NULL; + + net_unlock(); sched_unlock(); } @@ -632,57 +610,48 @@ static int netlink_poll(FAR struct socket *psock, FAR struct pollfd *fds, return OK; } - /* Set up to be notified by signal when a response is available if - * POLLIN is requested. + /* Set up to be notified when a response is available if POLLIN is + * requested. */ if ((fds->events & POLLIN) != 0) { - struct sigaction act; - - /* Set up a signal handler to receive the notification when - * a response has been queued. - * REVISIT: Make sure that the CONFIG_NETLINK_SIGNAL is not - * blocked. + /* Some limitations: There can be only a single outstanding POLLIN + * on the Netlink connection. */ if (conn->pollsem != NULL || conn->pollevent != NULL) { - nerr("ERROR: Multiple polls() on socket not supported.\n"); - net_unlock(); - return -EBUSY; + nerr("ERROR: Multiple polls() on socket not supported.\n"); + net_unlock(); + return -EBUSY; } + /* Set up the notification */ + conn->pollsem = fds->sem; conn->pollevent = &fds->revents; - memset(&act, 0, sizeof(act)); - act.sa_sigaction = netlink_response_available; - act.sa_flags = SA_SIGINFO; - ret = nxsig_action(CONFIG_NETLINK_SIGNAL, &act, &conn->oact, - true); + ret = netlink_notifier_setup(netlink_response_available, conn, conn); if (ret < 0) { - nerr("ERROR: nxsig_action() failed: %d\n", ret); - } - else if (conn->oact.sa_handler != SIG_DFL && - conn->oact.sa_handler != NULL) - { - nerr("ERROR: Signal being used for some other purpose\n"); - netlink_notify_teardown(conn); - ret = -ENOEXEC; + nerr("ERROR: netlink_notifier_setup() failed: %d\n", ret); + conn->pollsem = NULL; + conn->pollevent = NULL; } else { - /* Call netlink_notify_response() to receive a signal when - * a response has been queued. + /* Call netlink_notify_response() to receive a notification + * when a response has been queued. */ - ret = netlink_notify_response(psock); + ret = netlink_notify_response(psock); if (ret < 0) { nerr("ERROR: netlink_notify_response() failed: %d\n", ret); - netlink_notify_teardown(conn); + netlink_notifier_teardown(conn); + conn->pollsem = NULL; + conn->pollevent = NULL; } else if (ret == 0) { @@ -691,8 +660,10 @@ static int netlink_poll(FAR struct socket *psock, FAR struct pollfd *fds, * forthcoming. */ - netlink_notify_teardown(conn); - fds->revents = POLLIN; + netlink_notifier_teardown(conn); + conn->pollsem = NULL; + conn->pollevent = NULL; + fds->revents = POLLIN; nxsem_post(fds->sem); } else @@ -714,8 +685,9 @@ static int netlink_poll(FAR struct socket *psock, FAR struct pollfd *fds, { /* Cancel any response notifications */ - ret = netlink_notify_cancel(psock); - netlink_notify_teardown(conn); + ret = netlink_notifier_teardown(conn); + conn->pollsem = NULL; + conn->pollevent = NULL; } return ret; @@ -831,7 +803,7 @@ static ssize_t netlink_sendto(FAR struct socket *psock, FAR const void *buf, #endif default: - ret= -EOPNOTSUPP; + ret = -EOPNOTSUPP; break; } @@ -894,7 +866,7 @@ static ssize_t netlink_recvfrom(FAR struct socket *psock, FAR void *buf, #endif default: - ret= -EOPNOTSUPP; + ret = -EOPNOTSUPP; break; }