diff --git a/net/netlink/netlink.h b/net/netlink/netlink.h index 0686a5ba23..b4c314dbb3 100644 --- a/net/netlink/netlink.h +++ b/net/netlink/netlink.h @@ -45,6 +45,8 @@ #include #include #include +#include +#include #include #include @@ -86,6 +88,12 @@ struct netlink_conn_s uint8_t crefs; /* Reference counts on this instance */ uint8_t protocol; /* See NETLINK_* definitions */ + /* poll() support */ + + 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]; diff --git a/net/netlink/netlink_conn.c b/net/netlink/netlink_conn.c index c46a103363..61030ea3e2 100644 --- a/net/netlink/netlink_conn.c +++ b/net/netlink/netlink_conn.c @@ -144,13 +144,13 @@ static void netlink_notify_waiters(FAR struct netlink_conn_s *conn) value.sival_ptr = conn; ret = nxsig_queue((int)conn->waiter[i], - (int)CONFIG_NETLINK_SIGNAL, value); + (int)CONFIG_NETLINK_SIGNAL, value); #else ret = nxsig_queue((int)conn->waiter[i], - (int)CONFIG_NETLINK_SIGNAL, conn); + (int)CONFIG_NETLINK_SIGNAL, conn); #endif if (ret < 0) - { + { nerr("ERROR: nxsig_queue() failed: %d\n", ret); UNUSED(ret); } diff --git a/net/netlink/netlink_sockif.c b/net/netlink/netlink_sockif.c index 6934d3b47f..557471196e 100644 --- a/net/netlink/netlink_sockif.c +++ b/net/netlink/netlink_sockif.c @@ -44,11 +44,13 @@ #include #include #include +#include #include #include #include #include +#include #include #include "netlink/netlink.h" @@ -484,6 +486,89 @@ 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 + * + * Input Parameters: + * Standard signal handler parameters + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void netlink_response_available(int signo, FAR siginfo_t *siginfo, + FAR void *context) +{ + FAR struct netlink_conn_s *conn; + + /* The si_value member should be a reference to a Netlink connection + * structure. + */ + + DEBUGASSERT(siginfo != NULL); + conn = (FAR struct netlink_conn_s *)siginfo->si_value.sival_ptr; + DEBUGASSERT(conn != NULL); + + /* 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? + */ + + sched_lock(); + if (conn->pollsem != NULL && conn->pollevent != NULL) + { + /* Wake up the poll() with POLLIN */ + + *conn->pollevent |= POLLIN; + (void)nxsem_post(conn->pollsem); + } + else + { + nwarn("WARNING: Missing references in connection.\n"); + } + + netlink_notify_teardown(conn); + sched_unlock(); +} + /**************************************************************************** * Name: netlink_poll * @@ -510,8 +595,12 @@ static int netlink_accept(FAR struct socket *psock, FAR struct sockaddr *addr, static int netlink_poll(FAR struct socket *psock, FAR struct pollfd *fds, bool setup) { + FAR struct netlink_conn_s *conn; int ret; + DEBUGASSERT(psock != NULL && psock->s_conn != NULL); + conn = (FAR struct netlink_conn_s *)psock->s_conn; + /* Check if we are setting up or tearing down the poll */ if (setup) @@ -549,32 +638,82 @@ static int netlink_poll(FAR struct socket *psock, FAR struct pollfd *fds, if ((fds->events & POLLIN) != 0) { -#if 0 - /* Call netlink_notify_response() to receive a signal when + struct sigaction act; + + /* Set up a signal handler to recive the notification when * a response has been queued. - * - * REVISIT: How shall we pass the FDS info to the signal - * handler? */ -#else -#warning Missing logic for NETLINK POLLIN - nxsem_post(fds->sem); - net_unlock(); - return -ENOSYS; -#endif + if (conn->pollsem != NULL || conn->pollevent != NULL) + { + nerr("ERROR: Multiple polls() on socket not supported.\n"); + net_unlock(); + return -EBUSY; + } + + 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); + 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; + } + else + { + /* Call netlink_notify_response() to receive a signal when + * a response has been queued. + */ + + ret = netlink_notify_response(psock); + if (ret < 0) + { + nerr("ERROR: netlink_notify_response() failed: %d\n", ret); + netlink_notify_teardown(conn); + } + else if (ret == 0) + { + /* The return value of zero means that a response is + * already available and that no notification is + * forthcoming. + */ + + netlink_notify_teardown(conn); + fds->revents = POLLIN; + nxsem_post(fds->sem); + } + else + { + ret = OK; + } + } + } + else + { + /* There will not be any wakeups coming? Probably an error? */ + + ret = OK; } - /* There will not be any wakeups coming? Probably an error? */ - net_unlock(); - ret = OK; } else { /* Cancel any response notifications */ ret = netlink_notify_cancel(psock); + netlink_notify_teardown(conn); } return ret;