nuttx/net/mld/mld_timer.c
zhanghongyu c481374fb8 devif_poll: only call the corresponding xxx_poll when there is data to be sent
reduce the execution consumption of irrelevant code

testing the TX rates of TCP and UDP based on the Infineon board can
increase them by 13%.

Signed-off-by: zhanghongyu <zhanghongyu@xiaomi.com>
2026-01-02 00:04:16 +08:00

489 lines
13 KiB
C

/****************************************************************************
* net/mld/mld_timer.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/compiler.h>
#include <assert.h>
#include <debug.h>
#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/netdev.h>
#include <nuttx/net/netstats.h>
#include <nuttx/net/mld.h>
#include "devif/devif.h"
#include "netdev/netdev.h"
#include "mld/mld.h"
#include "utils/utils.h"
#ifdef CONFIG_NET_MLD
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define MLD_FREE_WORK 4
/****************************************************************************
* Private Data
****************************************************************************/
/* This is a small pool of work structures that are used for rare, general
* timer and MLDv1 compatibility timers.
*
* REVISIT: A better place would be in the device structure. But then we
* would either have to pass a device structure in the timeout (which could
* become stale) or look up the device using an interface index. But we
* cannot access the network device list from the timer interrupt handler.
*/
static struct work_s g_mld_work[MLD_FREE_WORK];
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: mld_find_work
*
* Description:
* Fin an available work queue structure.
*
* Assumptions:
* Called only from the timer interrupt handler with interrupts disabled.
*
****************************************************************************/
static FAR struct work_s *mld_find_work(void)
{
int i;
for (i = 0; i < MLD_FREE_WORK; i++)
{
if (work_available(&g_mld_work[i]))
{
return &g_mld_work[i];
}
}
return NULL;
}
/****************************************************************************
* Name: mld_gendog_work
*
* Description:
* General query watchdog timeout work.
*
* Assumptions:
* This function is called from a work queue thread.
*
****************************************************************************/
static void mld_gendog_work(FAR void *arg)
{
FAR struct net_driver_s *dev;
int ifindex;
/* Recover the interface index and find the device associated with the
* index.
*/
ifindex = (intptr_t)arg;
DEBUGASSERT(ifindex > 0);
dev = netdev_findbyindex(ifindex);
if (dev == NULL)
{
/* This could be a normal consequence if the device is unregistered
* while the timer is pending.
*/
fwarn("WARNING: No device associated with ifindex=%d\n", ifindex);
net_unlock();
return;
}
netdev_lock(dev);
/* Check for an Other Querier Present Timeout. This timer is set in non-
* Querier mode to detect the case where we have lost the Querier.
*/
if (!IS_MLD_QUERIER(dev->d_mld.flags))
{
/* We are not the Querier. This is an Other Querier Present Timeout.
* If this timeout expires, it means that there are no Queriers for
* the group. Let's revert to Querier mode.
*/
SET_MLD_QUERIER(dev->d_mld.flags);
}
/* Check if this we are the Querier */
if (IS_MLD_QUERIER(dev->d_mld.flags))
{
/* Schedule (and forget) the general query. */
MLD_STATINCR(g_netstats.mld.query_sched);
SET_MLD_GENPEND(dev->d_mld.flags);
/* Notify the device that we have a packet to send */
netdev_txnotify_dev(dev, MLD_POLL);
/* Restart the Querier timer */
mld_start_gentimer(dev, MSEC2TICK(MLD_QUERY_MSEC));
}
else
{
/* Not the Querier... Restart the Other Querier Present Timeout */
mld_start_gentimer(dev, MSEC2TICK(MLD_OQUERY_MSEC));
}
netdev_unlock(dev);
}
/****************************************************************************
* Name: mld_gendog_timout
*
* Description:
* General query watchdog handler.
*
* Assumptions:
* This function is called from the context of the timer interrupt handler.
*
****************************************************************************/
static void mld_gendog_timout(wdparm_t arg)
{
FAR struct work_s *work;
int ret;
mldinfo("Timeout!\n");
DEBUGASSERT(arg != 0);
/* Find an available work structure. This is awkward. */
work = mld_find_work();
DEBUGASSERT(work != NULL);
if (work == NULL)
{
mlderr("ERROR: No free work structures.\n");
}
else
{
/* Perform the general query-related operations on (preferably) the
* low priority work queue.
*/
ret = work_queue(LPWORK, work, mld_gendog_work, (FAR void *)arg, 0);
if (ret < 0)
{
mlderr("ERROR: Failed to queue general query work: %d\n", ret);
}
}
}
/****************************************************************************
* Name: mld_v1dog_work
*
* Description:
* Timeout watchdog work
*
* Assumptions:
* This function is called from a work queue thread.
*
****************************************************************************/
static void mld_v1dog_work(FAR void *arg)
{
FAR struct net_driver_s *dev;
int ifindex;
/* Recover the interface index and find the device associated with the
* index.
*/
ifindex = (intptr_t)arg;
DEBUGASSERT(ifindex > 0);
dev = netdev_findbyindex(ifindex);
if (dev == NULL)
{
/* This could be a normal consequence if the device is unregistered
* while the timer is pending.
*/
fwarn("WARNING: No device associated with ifindex=%d\n", ifindex);
}
else
{
/* Exit MLDv1 compatibility mode. */
netdev_lock(dev);
CLR_MLD_V1COMPAT(dev->d_mld.flags);
netdev_unlock(dev);
/* REVISIT: Whenever a host changes its compatibility mode, it cancels
* all of its pending responses and retransmission timers.
*/
}
}
/****************************************************************************
* Name: mld_v1dog_timout
*
* Description:
* Timeout watchdog handler.
*
* Assumptions:
* This function is called from the context of the timer interrupt handler.
*
****************************************************************************/
static void mld_v1dog_timout(wdparm_t arg)
{
FAR struct work_s *work;
int ret;
mldinfo("Timeout!\n");
DEBUGASSERT(arg != 0);
/* Find an available work structure. This is awkward. */
work = mld_find_work();
DEBUGASSERT(work != NULL);
if (work == NULL)
{
mlderr("ERROR: No free work structures.\n");
}
else
{
/* Perform the general query-related operations on (preferably) the
* low priority work queue.
*/
ret = work_queue(LPWORK, work, mld_v1dog_work, (FAR void *)arg, 0);
if (ret < 0)
{
mlderr("ERROR: Failed to queue MLDv1 timeout work: %d\n", ret);
}
}
}
/****************************************************************************
* Name: mld_polldog_work
*
* Description:
* Timeout watchdog work
*
* Assumptions:
* This function is called from a work queue thread.
*
****************************************************************************/
static void mld_polldog_work(FAR void *arg)
{
FAR struct mld_group_s *group;
FAR struct net_driver_s *dev;
int ret;
/* Recover the reference to the group */
group = (FAR struct mld_group_s *)arg;
DEBUGASSERT(group != NULL);
/* Check if this a new join to the multicast group. */
if (IS_MLD_STARTUP(group->flags))
{
MLD_STATINCR(g_netstats.mld.report_sched);
/* Get a reference to the device serving the sub-net */
dev = netdev_findbyindex(group->ifindex);
if (dev == NULL)
{
/* This could be a normal consequence if the device is
* unregistered while the timer is pending.
*/
fwarn("WARNING: No device associated with ifindex=%d\n",
group->ifindex);
return;
}
/* Schedule (and forget) the Report. */
netdev_lock(dev);
ret = mld_schedmsg(group, mld_report_msgtype(dev));
netdev_unlock(dev);
if (ret < 0)
{
mlderr("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.
*/
if (group->count > 0)
{
/* Decrement the count and restart the timer */
group->count--;
mld_start_polltimer(group, MSEC2TICK(MLD_UNSOLREPORT_MSEC));
}
else
{
/* Terminate the start-up sequence */
CLR_MLD_STARTUP(group->flags);
}
}
}
/****************************************************************************
* Name: mld_polldog_timout
*
* Description:
* Timeout watchdog handler.
*
* Assumptions:
* This function is called from the context of the timer interrupt handler.
*
****************************************************************************/
static void mld_polldog_timout(wdparm_t arg)
{
FAR struct mld_group_s *group;
int ret;
mldinfo("Timeout!\n");
/* Recover the reference to the group */
group = (FAR struct mld_group_s *)arg;
DEBUGASSERT(group != NULL);
/* Perform the timeout-related operations on (preferably) the low priority
* work queue.
*/
ret = work_queue(LPWORK, &group->work, mld_polldog_work, group, 0);
if (ret < 0)
{
mlderr("ERROR: Failed to queue timeout work: %d\n", ret);
}
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: mld_start_gentimer
*
* Description:
* Start/Re-start the general query timer.
*
****************************************************************************/
void mld_start_gentimer(FAR struct net_driver_s *dev, clock_t ticks)
{
int ret;
/* Start the timer */
mldinfo("ticks: %lu\n", (unsigned long)ticks);
ret = wd_start(&dev->d_mld.gendog, ticks,
mld_gendog_timout, dev->d_ifindex);
DEBUGASSERT(ret == OK);
UNUSED(ret);
}
/****************************************************************************
* Name: mld_start_v1timer
*
* Description:
* Start the MLDv1 compatibility timer.
*
* REVISIT: This should be a single global timer, not a per-group timer.
*
****************************************************************************/
void mld_start_v1timer(FAR struct net_driver_s *dev, clock_t ticks)
{
int ret;
/* Start the timer */
mldinfo("ticks: %lu\n", (unsigned long)ticks);
ret = wd_start(&dev->d_mld.v1dog, ticks,
mld_v1dog_timout, dev->d_ifindex);
DEBUGASSERT(ret == OK);
UNUSED(ret);
}
/****************************************************************************
* Name: mld_start_polltimer
*
* Description:
* Start the MLD poll timer.
*
****************************************************************************/
void mld_start_polltimer(FAR struct mld_group_s *group, clock_t ticks)
{
int ret;
/* Start the timer */
mldinfo("ticks: %lu\n", (unsigned long)ticks);
ret = wd_start(&group->polldog, ticks,
mld_polldog_timout, (wdparm_t)group);
DEBUGASSERT(ret == OK);
UNUSED(ret);
}
#endif /* CONFIG_NET_MLD */