forked from nuttx/nuttx-update
net/mld: Defer all timeout-related operations to the work queue vs. handling within the timer interrupt. Need to notify the device driver for an immediate poll when there are MLD packets waiting to be sent.
This commit is contained in:
parent
1c15aa3b2d
commit
a4dc759b4d
10 changed files with 146 additions and 108 deletions
|
@ -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));
|
||||
|
|
|
@ -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:
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -110,6 +110,7 @@
|
|||
#include <queue.h>
|
||||
#include <semaphore.h>
|
||||
|
||||
#include <nuttx/wqueue.h>
|
||||
#include <nuttx/net/ip.h>
|
||||
|
||||
#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
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
#include <nuttx/net/mld.h>
|
||||
|
||||
#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 */
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
|
||||
#include <nuttx/wdog.h>
|
||||
#include <nuttx/irq.h>
|
||||
#include <nuttx/wqueue.h>
|
||||
#include <nuttx/net/netconfig.h>
|
||||
#include <nuttx/net/net.h>
|
||||
#include <nuttx/net/netstats.h>
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
|
|
Loading…
Reference in a new issue