diff --git a/net/igmp/igmp_msg.c b/net/igmp/igmp_msg.c index 1e1d615eed..35d2e20acf 100644 --- a/net/igmp/igmp_msg.c +++ b/net/igmp/igmp_msg.c @@ -73,6 +73,7 @@ void igmp_schedmsg(FAR struct igmp_group_s *group, uint8_t msgid) { /* The following should be atomic */ + /* REVISIT: Need to notify the device that we have a packet to send */ net_lock(); DEBUGASSERT(!IS_SCHEDMSG(group->flags)); diff --git a/net/igmp/igmp_poll.c b/net/igmp/igmp_poll.c index c9d0a32fe4..0cf23a8849 100644 --- a/net/igmp/igmp_poll.c +++ b/net/igmp/igmp_poll.c @@ -80,6 +80,7 @@ static inline void igmp_sched_send(FAR struct net_driver_s *dev, { in_addr_t *dest; + /* REVISIT: This should be deferred to a work queue */ /* Check what kind of message we need to send. There are only two * possibilities: */ diff --git a/net/inet/ipv4_setsockopt.c b/net/inet/ipv4_setsockopt.c index 3d550ea0e4..fa67d24ada 100644 --- a/net/inet/ipv4_setsockopt.c +++ b/net/inet/ipv4_setsockopt.c @@ -97,6 +97,7 @@ int ipv4_setsockopt(FAR struct socket *psock, int option, * REVISIT: Clone the logic from netdev_ioctl.c here. */ + net_lock(); switch (option) { case IP_MSFILTER: /* Access advanced, full-state filtering API */ @@ -216,6 +217,7 @@ int ipv4_setsockopt(FAR struct socket *psock, int option, break; } + net_unlock(); return ret; #else return -ENOPROTOOPT; diff --git a/net/inet/ipv6_setsockopt.c b/net/inet/ipv6_setsockopt.c index a40991877f..76cbab6a30 100644 --- a/net/inet/ipv6_setsockopt.c +++ b/net/inet/ipv6_setsockopt.c @@ -90,6 +90,7 @@ int ipv6_setsockopt(FAR struct socket *psock, int option, /* Handle MLD-related socket options */ + net_lock(); switch (option) { case IPV6_JOIN_GROUP: /* Join a multicast group */ @@ -144,6 +145,7 @@ int ipv6_setsockopt(FAR struct socket *psock, int option, break; } + net_unlock(); return ret; #else return -ENOPROTOOPT; diff --git a/net/mld/mld.h b/net/mld/mld.h index 53d1071b86..10e370ec12 100644 --- a/net/mld/mld.h +++ b/net/mld/mld.h @@ -110,6 +110,7 @@ #include #include +#include #include #include "devif/devif.h" @@ -180,9 +181,11 @@ struct mld_group_s { struct mld_group_s *next; /* Implements a singly-linked list */ net_ipv6addr_t grpaddr; /* Group IPv6 address */ + struct work_s work; /* For deferred timeout operations */ WDOG_ID wdog; /* WDOG used to detect timeouts */ sem_t sem; /* Used to wait for message transmission */ - volatile uint8_t flags; /* See MLD_ flags definitions */ + uint8_t ifindex; /* Interface index */ + uint8_t flags; /* See MLD_ flags definitions */ uint8_t msgtype; /* Pending message type to send (if non-zero) */ uint8_t count; /* Report repetition count */ }; @@ -332,7 +335,7 @@ void mld_grpfree(FAR struct net_driver_s *dev, * ****************************************************************************/ -void mld_schedmsg(FAR struct mld_group_s *group, uint8_t msgtype); +int mld_schedmsg(FAR struct mld_group_s *group, uint8_t msgtype); /**************************************************************************** * Name: mld_waitmsg @@ -343,7 +346,7 @@ void mld_schedmsg(FAR struct mld_group_s *group, uint8_t msgtype); * ****************************************************************************/ -void mld_waitmsg(FAR struct mld_group_s *group, uint8_t msgtype); +int mld_waitmsg(FAR struct mld_group_s *group, uint8_t msgtype); /**************************************************************************** * Name: mld_poll diff --git a/net/mld/mld_group.c b/net/mld/mld_group.c index 4225aaf855..1aeb87ff7b 100644 --- a/net/mld/mld_group.c +++ b/net/mld/mld_group.c @@ -100,6 +100,9 @@ * Description: * Allocate a new group from heap memory. * + * Assumptions: + * The network is locked. + * ****************************************************************************/ FAR struct mld_group_s *mld_grpalloc(FAR struct net_driver_s *dev, @@ -132,18 +135,17 @@ FAR struct mld_group_s *mld_grpalloc(FAR struct net_driver_s *dev, group->wdog = wd_create(); DEBUGASSERT(group->wdog); + /* Save the interface index */ + + group->ifindex = dev->d_ifindex; + /* All routers start up as a Querier on each of their attached links. */ SET_MLD_QUERIER(group->flags); - /* The network must be locked in order to modify the group list */ - - net_lock(); - /* Add the group structure to the list in the device structure */ sq_addfirst((FAR sq_entry_t *)group, &dev->d_mld_grplist); - net_unlock(); } return group; @@ -155,6 +157,9 @@ FAR struct mld_group_s *mld_grpalloc(FAR struct net_driver_s *dev, * Description: * Find an existing group. * + * Assumptions: + * The network is locked. + * ****************************************************************************/ FAR struct mld_group_s *mld_grpfind(FAR struct net_driver_s *dev, @@ -164,7 +169,6 @@ FAR struct mld_group_s *mld_grpfind(FAR struct net_driver_s *dev, grpinfo("Searching for addr %08x\n", (int)*addr); - net_lock(); for (group = (FAR struct mld_group_s *)dev->d_mld_grplist.head; group; group = group->next) @@ -180,11 +184,11 @@ FAR struct mld_group_s *mld_grpfind(FAR struct net_driver_s *dev, if (net_ipv6addr_cmp(group->grpaddr, addr)) { grpinfo("Match!\n"); + DEBUGASSERT(group->ifindex == dev->d_ifindex); break; } } - net_unlock(); return group; } @@ -218,6 +222,9 @@ FAR struct mld_group_s *mld_grpallocfind(FAR struct net_driver_s *dev, * Description: * Release a previously allocated group. * + * Assumptions: + * The network is locked. + * ****************************************************************************/ void mld_grpfree(FAR struct net_driver_s *dev, FAR struct mld_group_s *group) @@ -226,7 +233,6 @@ void mld_grpfree(FAR struct net_driver_s *dev, FAR struct mld_group_s *group) /* Cancel the wdog */ - net_lock(); wd_cancel(group->wdog); /* Remove the group structure from the group list in the device structure */ @@ -243,10 +249,8 @@ void mld_grpfree(FAR struct net_driver_s *dev, FAR struct mld_group_s *group) /* Then release the group structure resources. */ - net_unlock(); grpinfo("Call sched_kfree()\n"); sched_kfree(group); } #endif /* CONFIG_NET_MLD */ - diff --git a/net/mld/mld_join.c b/net/mld/mld_join.c index bf7d04942a..8c0dd5c152 100644 --- a/net/mld/mld_join.c +++ b/net/mld/mld_join.c @@ -72,40 +72,8 @@ * and IPV6_LEAVE_GROUP must be symmetrical. The format of the ipv6_mreq * structure can be found in include/netinet/in.h * - * State transition diagram for a router in Querier state (RFC 2710): - * ________________ - * | | - * | |timer expired - * timer expired| |(notify routing -, - * (notify routing -)| No Listeners |clear rxmt tmr) - * ------->| Present |<--------- - * | | | | - * | | | | - * | |________________| | --------------- - * | | | | rexmt timer | - * | report received| | | expired | - * | (notify routing +,| | | (send m-a-s | - * | start timer)| | | query, | - * __________|______ | ________|_|______ st rxmt | - * | |<------------ | | tmr) | - * | | | |<------- - * | | report received | | - * | | (start timer, | | - * | | clear rxmt tmr) | | - * | Listeners |<-------------------| Checking | - * | Present | done received | Listeners | - * | | (start timer*, | | - * | | start rxmt timer, | | - * | | send m-a-s query) | | - * --->| |------------------->| | - * | |_________________| |_________________| - * | report received | - * | (start timer) | - * ----------------- - * - * State transition diagram for a router in Non-Querier state is - * similar, but non-Queriers do not send any messages and are only - * driven by message reception (See RFC 2710/3810 or net/mld.h). + * Assumptions: + * The network is locked. * ****************************************************************************/ @@ -113,8 +81,9 @@ int mld_joingroup(FAR const struct ipv6_mreq *mrec) { FAR struct net_driver_s *dev; struct mld_group_s *group; + int ret; - DEBUGASSERT(dev != NULL && mrec != NULL); + DEBUGASSERT(mrec != NULL); /* Get the device from the interface index. Use the default network device * if an interface index of 0 is provided. @@ -131,6 +100,8 @@ int mld_joingroup(FAR const struct ipv6_mreq *mrec) if (dev == NULL) { + nerr("ERROR: No device for this interface index: %u\n", + mrec->ipv6mr_interface); return -ENODEV; } @@ -164,7 +135,13 @@ int mld_joingroup(FAR const struct ipv6_mreq *mrec) /* Send the Version 1 Multicast Listener Report */ MLD_STATINCR(g_netstats.mld.report_sched); - mld_waitmsg(group, ICMPV6_MCAST_LISTEN_REPORT_V1); + ret = mld_waitmsg(group, MLD_SEND_REPORT); + if (ret < 0) + { + nerr("ERROR: Failed to schedule message: %d\n", ret); + mld_grpfree(dev, group); + return ret; + } /* And start the timer at 1 second */ diff --git a/net/mld/mld_leave.c b/net/mld/mld_leave.c index 166ee31d8b..274731f33e 100644 --- a/net/mld/mld_leave.c +++ b/net/mld/mld_leave.c @@ -74,40 +74,8 @@ * and IPV6_LEAVE_GROUP must be symmetrical. The format of the ipv6_mreq * structure can be found in include/netinet/in.h * - * State transition diagram for a router in Querier state (RFC 2710): - * ________________ - * | | - * | |timer expired - * timer expired| |(notify routing -, - * (notify routing -)| No Listeners |clear rxmt tmr) - * ------->| Present |<--------- - * | | | | - * | | | | - * | |________________| | --------------- - * | | | | rexmt timer | - * | report received| | | expired | - * | (notify routing +,| | | (send m-a-s | - * | start timer)| | | query, | - * __________|______ | ________|_|______ st rxmt | - * | |<------------ | | tmr) | - * | | | |<------- - * | | report received | | - * | | (start timer, | | - * | | clear rxmt tmr) | | - * | Listeners |<-------------------| Checking | - * | Present | done received | Listeners | - * | | (start timer*, | | - * | | start rxmt timer, | | - * | | send m-a-s query) | | - * --->| |------------------->| | - * | |_________________| |_________________| - * | report received | - * | (start timer) | - * ----------------- - * - * State transition diagram for a router in Non-Querier state is - * similar, but non-Queriers do not send any messages and are only - * driven by message reception (See RFC 2710/3810 or net/mld.h). + * Assumptions: + * The network is locked. * ****************************************************************************/ @@ -115,6 +83,7 @@ int mld_leavegroup(FAR const struct ipv6_mreq *mrec) { FAR struct net_driver_s *dev; struct mld_group_s *group; + int ret; DEBUGASSERT(dev != NULL && mrec != NULL); @@ -159,11 +128,9 @@ int mld_leavegroup(FAR const struct ipv6_mreq *mrec) * leaving the group. */ - net_lock(); wd_cancel(group->wdog); CLR_MLD_SCHEDMSG(group->flags); CLR_MLD_WAITMSG(group->flags); - net_unlock(); MLD_STATINCR(g_netstats.mld.leaves); @@ -171,10 +138,15 @@ int mld_leavegroup(FAR const struct ipv6_mreq *mrec) if (IS_MLD_LASTREPORT(group->flags)) { - ninfo("Schedule Leave Group message\n"); + ninfo("Schedule Done message\n"); MLD_STATINCR(g_netstats.mld.done_sched); - mld_waitmsg(group, ICMPV6_MCAST_LISTEN_DONE_V1); + + ret = mld_waitmsg(group, MLD_SEND_DONE); + if (ret < 0) + { + nerr("ERROR: Failed to schedule message: %d\n", ret); + } } /* Free the group structure */ diff --git a/net/mld/mld_msg.c b/net/mld/mld_msg.c index ad22706347..176f2df4e4 100644 --- a/net/mld/mld_msg.c +++ b/net/mld/mld_msg.c @@ -45,6 +45,7 @@ #include #include "devif/devif.h" +#include "netdev/netdev.h" #include "mld/mld.h" #ifdef CONFIG_NET_MLD @@ -60,19 +61,34 @@ * Schedule a message to be send at the next driver polling interval. * * Assumptions: - * This function may be called in most any context. + * The network is locked * ****************************************************************************/ -void mld_schedmsg(FAR struct mld_group_s *group, uint8_t msgtype) +int mld_schedmsg(FAR struct mld_group_s *group, uint8_t msgtype) { - /* The following should be atomic */ + FAR struct net_driver_s *dev; + + DEBUGASSERT(group != NULL && !IS_MLD_SCHEDMSG(group->flags)); + DEBUGASSERT(group->ifindex > 0); + + /* Get the device instance associated with the interface index of the group */ + + dev = netdev_findbyindex(group->ifindex); + if (dev == NULL) + { + nerr("ERROR: No device for this interface index: %u\n", + group->ifindex); + return -ENODEV; + } - net_lock(); - DEBUGASSERT(!IS_MLD_SCHEDMSG(group->flags)); group->msgtype = msgtype; SET_MLD_SCHEDMSG(group->flags); - net_unlock(); + + /* Notify the device that we have a packet to send */ + + netdev_txnotify_dev(dev); + return OK; } /**************************************************************************** @@ -82,18 +98,26 @@ void mld_schedmsg(FAR struct mld_group_s *group, uint8_t msgtype) * Schedule a message to be send at the next driver polling interval and * block, waiting for the message to be sent. * + * Assumptions: + * The network is locked + * ****************************************************************************/ -void mld_waitmsg(FAR struct mld_group_s *group, uint8_t msgtype) +int mld_waitmsg(FAR struct mld_group_s *group, uint8_t msgtype) { int ret; /* Schedule to send the message */ - net_lock(); DEBUGASSERT(!IS_MLD_WAITMSG(group->flags)); SET_MLD_WAITMSG(group->flags); - mld_schedmsg(group, msgtype); + + ret = mld_schedmsg(group, msgtype); + if (ret < 0) + { + nerr("ERROR: Failed to schedule the message: %d\n", ret); + goto errout; + } /* Then wait for the message to be sent */ @@ -104,19 +128,24 @@ void mld_waitmsg(FAR struct mld_group_s *group, uint8_t msgtype) while ((ret = net_lockedwait(&group->sem)) < 0) { /* The only error that should occur from net_lockedwait() is if - * the wait is awakened by a signal. + * the wait is awakened by a signal or, perhaps, canceled. */ DEBUGASSERT(ret == -EINTR || ret == -ECANCELED); - } + if (ret != -EINTR && ret != -ECANCELED) + { + break; + } - UNUSED(ret); + ret = OK; + } } /* The message has been sent and we are no longer waiting */ +errout: CLR_MLD_WAITMSG(group->flags); - net_unlock(); + return ret; } #endif /* CONFIG_NET_MLD */ diff --git a/net/mld/mld_timer.c b/net/mld/mld_timer.c index e3714775fa..aa2bd13353 100644 --- a/net/mld/mld_timer.c +++ b/net/mld/mld_timer.c @@ -43,6 +43,7 @@ #include #include +#include #include #include #include @@ -89,10 +90,10 @@ ****************************************************************************/ /**************************************************************************** - * Name: mld_timeout + * Name: mld_timeout_work * * Description: - * Timeout watchdog handler. + * Timeout watchdog work * * Assumptions: * This function is called from the wdog timer handler which runs in the @@ -100,24 +101,29 @@ * ****************************************************************************/ -static void mld_timeout(int argc, uint32_t arg, ...) +static void mld_timeout_work(FAR void *arg) { FAR struct mld_group_s *group; + int ret; - /* If the state is DELAYING_MEMBER then we send a report for this group */ + /* Recover the reference to the group */ - ninfo("Timeout!\n"); group = (FAR struct mld_group_s *)arg; - DEBUGASSERT(argc == 1 && group); + DEBUGASSERT(group != NULL); /* Check if this a new join to the multicast group. */ + net_lock(); if (IS_MLD_STARTUP(group->flags)) { /* Schedule (and forget) the Report. */ MLD_STATINCR(g_netstats.mld.report_sched); - mld_schedmsg(group, MLD_SEND_REPORT); + ret = mld_schedmsg(group, MLD_SEND_REPORT); + if (ret < 0) + { + nerr("ERROR: Failed to schedule message: %d\n", ret); + } /* Send the report until the unsolicited report count goes to zero * then terminate the start-up sequence. @@ -152,12 +158,53 @@ static void mld_timeout(int argc, uint32_t arg, ...) /* Schedule (and forget) the general query. */ MLD_STATINCR(g_netstats.mld.query_sched); - mld_schedmsg(group, MLD_SEND_GENQUERY); + ret = mld_schedmsg(group, MLD_SEND_GENQUERY); + if (ret < 0) + { + nerr("ERROR: Failed to schedule message: %d\n", ret); + } /* Restart the Querier timer */ mld_starttimer(group, MSEC2TICK(MLD_QUERY_MSEC)); } + + net_unlock(); +} + +/**************************************************************************** + * Name: mld_timeout + * + * Description: + * Timeout watchdog handler. + * + * Assumptions: + * This function is called from the wdog timer handler which runs in the + * context of the timer interrupt handler. + * + ****************************************************************************/ + +static void mld_timeout(int argc, uint32_t arg, ...) +{ + FAR struct mld_group_s *group; + int ret; + + ninfo("Timeout!\n"); + + /* Recover the reference to the group */ + + group = (FAR struct mld_group_s *)arg; + DEBUGASSERT(argc == 1 && group != NULL); + + /* Perform the timeout-related operations on (preferably) the low prioirity + * work queue. + */ + + ret = work_queue(LPWORK, &group->work, mld_timeout_work, group, 0); + if (ret < 0) + { + nerr("ERROR: Failed to queue timeout work: %d\n", ret); + } } /****************************************************************************