This is an automated email from the ASF dual-hosted git repository. xiaoxiang pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit 5b52ab13c80c8ffa2e76d4088919593e8f6470ba Author: wenquan1 <[email protected]> AuthorDate: Mon Sep 22 16:38:23 2025 +0800 net/arp: support queue iob when arp_out failed arp_out will replace the dev->d_iob to arp request if the iob destination address is not exist in arp table, this mechanism cause this iob lost result in first received ping no response or first synack retransmit. to fix this bug, we queue the iob if arp_out failed, allocate a new iob to send arp request, after the arp request completed, then we retry send the queue iob packet. Signed-off-by: wenquan1 <[email protected]> --- include/nuttx/net/netdev.h | 4 ++ net/arp/Kconfig | 12 ++++- net/arp/arp.h | 30 +++++++++++ net/arp/arp_out.c | 9 ++++ net/arp/arp_table.c | 124 +++++++++++++++++++++++++++++++++++++++++---- net/devif/devif_poll.c | 90 +++++++++++++++++++++++++++----- 6 files changed, 244 insertions(+), 25 deletions(-) diff --git a/include/nuttx/net/netdev.h b/include/nuttx/net/netdev.h index 291aa853a0f..40cb84d2b1b 100644 --- a/include/nuttx/net/netdev.h +++ b/include/nuttx/net/netdev.h @@ -432,6 +432,10 @@ struct net_driver_s struct iob_queue_s d_fragout; #endif +#ifdef CONFIG_NET_ARP_SEND_QUEUE + struct iob_queue_s d_arpout; +#endif + /* The d_buf array is used to hold incoming and outgoing packets. The * device driver should place incoming data into this buffer. When sending * data, the device driver should read the link level headers and the diff --git a/net/arp/Kconfig b/net/arp/Kconfig index 99c0c2451e2..96524ae5c48 100644 --- a/net/arp/Kconfig +++ b/net/arp/Kconfig @@ -60,12 +60,20 @@ config NET_ARP_GRATUITOUS config NET_ARP_SEND bool "ARP send" - default y + default !NET_ARP_SEND_QUEUE ---help--- Enable logic to send ARP requests if the target IP address mapping does not appear in the ARP table. -if NET_ARP_SEND +config NET_ARP_SEND_QUEUE + bool "Iob queue in arp entry support" + default n + depends on IOB_NCHAINS > 0 + ---help--- + Enable queue iob to arp entry when arp_out failed to wait for arp + finished, then resend queue iobs. + +if NET_ARP_SEND || NET_ARP_SEND_QUEUE config ARP_SEND_MAXTRIES int "ARP send retries" diff --git a/net/arp/arp.h b/net/arp/arp.h index 06a715b93db..1f3df14aa22 100644 --- a/net/arp/arp.h +++ b/net/arp/arp.h @@ -166,6 +166,10 @@ struct arp_entry_s clock_t at_time; /* Time of last usage */ uint8_t at_flags; /* Flags, examples: ATF_PERM */ FAR struct net_driver_s *at_dev; /* The device driver structure */ +#ifdef CONFIG_NET_ARP_SEND_QUEUE + struct iob_queue_s at_queue; /* Queue iobs to wait arp complete */ + struct work_s at_work; /* Arp response timeout handle */ +#endif }; /**************************************************************************** @@ -609,6 +613,32 @@ void arp_acd_setup(FAR struct net_driver_s *dev); #endif /* CONFIG_NET_ARP_ACD */ +/**************************************************************************** + * Name: arp_queue_iob + * + * Description: + * Queue an IOB which L2 layer is unfinished to the target arp entry's + * deley queue which in progress waiting for an ARP response + * + * Input Parameters: + * dev - The device driver structure + * ipaddr - The IP address as an inaddr_t + * iob - The IOB to be queued + * + * Returned Value: + * Zero (OK) if the ARP table entry was successfully modified. A negated + * errno value is returned on any error. + * + * Assumptions + * The network is locked to assure exclusive access to the ARP table + * + ****************************************************************************/ + +#ifdef CONFIG_NET_ARP_SEND_QUEUE +int arp_queue_iob(FAR struct net_driver_s *dev, in_addr_t ipaddr, + FAR struct iob_s *iob); +#endif + #else /* CONFIG_NET_ARP */ /* If ARP is disabled, stub out all ARP interfaces */ diff --git a/net/arp/arp_out.c b/net/arp/arp_out.c index 120b2deef10..db20dab62cb 100644 --- a/net/arp/arp_out.c +++ b/net/arp/arp_out.c @@ -275,7 +275,12 @@ void arp_out(FAR struct net_driver_s *dev) * to prevent arp flood. */ +#ifdef CONFIG_NET_ARP_SEND_QUEUE + arp_queue_iob(dev, ipaddr, dev->d_iob); + netdev_iob_clear(dev); +#else dev->d_len = 0; +#endif return; } @@ -284,6 +289,10 @@ void arp_out(FAR struct net_driver_s *dev) */ arp_update(dev, ipaddr, NULL, 0); +#ifdef CONFIG_NET_ARP_SEND_QUEUE + arp_queue_iob(dev, ipaddr, dev->d_iob); + netdev_iob_clear(dev); +#endif /* The destination address was not in our ARP table, so we overwrite * the IP packet with an ARP request. diff --git a/net/arp/arp_table.c b/net/arp/arp_table.c index 7c65b0f498f..8fae076bbe3 100644 --- a/net/arp/arp_table.c +++ b/net/arp/arp_table.c @@ -269,6 +269,14 @@ static void arp_get_arpreq(FAR struct arpreq *output, } #endif +#ifdef CONFIG_NET_ARP_SEND_QUEUE +static void arp_unreach_work(FAR void *param) +{ + FAR struct arp_entry_s *tabptr = (FAR struct arp_entry_s *)param; + iob_free_queue(&tabptr->at_queue); +} +#endif + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -301,8 +309,10 @@ int arp_update(FAR struct net_driver_s *dev, in_addr_t ipaddr, FAR struct arp_entry_s *tabptr = &g_arptable[0]; #ifdef CONFIG_NETLINK_ROUTE struct arpreq arp_notify; - bool found = false; bool new_entry; +#endif +#if defined(CONFIG_NETLINK_ROUTE) || defined(CONFIG_NET_ARP_SEND_QUEUE) + bool found = false; #endif int i; @@ -324,7 +334,7 @@ int arp_update(FAR struct net_driver_s *dev, in_addr_t ipaddr, /* An old entry found, break. */ tabptr = &g_arptable[i]; -#ifdef CONFIG_NETLINK_ROUTE +#if defined(CONFIG_NETLINK_ROUTE) || defined(CONFIG_NET_ARP_SEND_QUEUE) found = true; #endif break; @@ -337,6 +347,26 @@ int arp_update(FAR struct net_driver_s *dev, in_addr_t ipaddr, } } + if ((tabptr->at_flags & ATF_PERM) != 0 && (flags & ATF_PERM) == 0) + { + return -ENOSPC; + } + +#ifdef CONFIG_NET_ARP_SEND_QUEUE + if (!found && tabptr->at_ipaddr != 0) + { + /* arp entry will be replaced, clean delayed iobs if exist */ + + work_cancel_sync(LPWORK, &tabptr->at_work); + iob_free_queue(&tabptr->at_queue); + } + else if (found && ethaddr != NULL) + { + work_cancel_sync(LPWORK, &tabptr->at_work); + iob_concat_queue(&dev->d_arpout, &tabptr->at_queue); + } +#endif + if (ethaddr == NULL) { ethaddr = g_zero_ethaddr.ether_addr_octet; @@ -361,14 +391,11 @@ int arp_update(FAR struct net_driver_s *dev, in_addr_t ipaddr, * information. */ - if ((tabptr->at_flags & ATF_PERM) == 0 || (flags & ATF_PERM) != 0) - { - memcpy(tabptr->at_ethaddr.ether_addr_octet, ethaddr, ETHER_ADDR_LEN); - tabptr->at_ipaddr = ipaddr; - tabptr->at_time = clock_systime_ticks(); - tabptr->at_flags = flags; - tabptr->at_dev = dev; - } + memcpy(tabptr->at_ethaddr.ether_addr_octet, ethaddr, ETHER_ADDR_LEN); + tabptr->at_ipaddr = ipaddr; + tabptr->at_time = clock_systime_ticks(); + tabptr->at_flags = flags; + tabptr->at_dev = dev; /* Notify the new entry */ @@ -380,6 +407,28 @@ int arp_update(FAR struct net_driver_s *dev, in_addr_t ipaddr, } #endif +#ifdef CONFIG_NET_ARP_SEND_QUEUE + if (!IOB_QEMPTY(&dev->d_arpout)) + { + /* in Rx context, we need to backup the dev iob and related members, + * as some dirvers netdev_txnotify_dev() execute transmit in sync mode + * which will modify iob and other members. + * we need to restore the dev iob and related members after + * netdev_txnotify_dev() return because iob will be used in left + * bottom Rx process. + */ + + uint16_t len = dev->d_len; + FAR struct iob_s *iob = dev->d_iob; + + dev->d_iob = NULL; + dev->d_buf = NULL; + netdev_txnotify_dev(dev); + netdev_iob_replace(dev, iob); + dev->d_len = len; + } +#endif + return OK; } @@ -567,6 +616,11 @@ void arp_cleanup(FAR struct net_driver_s *dev) { if (dev == g_arptable[i].at_dev) { +#ifdef CONFIG_NET_ARP_SEND_QUEUE + work_cancel_sync(LPWORK, &g_arptable[i].at_work); + iob_free_queue(&g_arptable[i].at_queue); +#endif + memset(&g_arptable[i], 0, sizeof(g_arptable[i])); } } @@ -622,5 +676,55 @@ unsigned int arp_snapshot(FAR struct arpreq *snapshot, } #endif +/**************************************************************************** + * Name: arp_queue_iob + * + * Description: + * Queue an IOB which L2 layer is unfinished to the target arp entry's + * delay queue while the entry is in progress waiting for an ARP response + * + * Input Parameters: + * dev - The device driver structure + * ipaddr - The IP address as an inaddr_t + * iob - The IOB to be queued + * + * Returned Value: + * Zero (OK) if the ARP table entry was successfully modified. A negated + * errno value is returned on any error. + * + * Assumptions + * The network is locked to assure exclusive access to the ARP table + * + ****************************************************************************/ + +#ifdef CONFIG_NET_ARP_SEND_QUEUE +int arp_queue_iob(FAR struct net_driver_s *dev, in_addr_t ipaddr, + FAR struct iob_s *iob) +{ + FAR struct arp_entry_s *tabptr; + + /* the IPv4 address should in the ARP table and arp in progress. */ + + tabptr = arp_lookup(ipaddr, dev, false); + if (tabptr && memcmp(&tabptr->at_ethaddr, &g_zero_ethaddr, + sizeof(tabptr->at_ethaddr)) == 0) + { + if (iob_tryadd_queue(iob, &tabptr->at_queue) == 0) + { + if (work_available(&tabptr->at_work)) + { + work_queue(LPWORK, &tabptr->at_work, arp_unreach_work, + tabptr, ARP_INPROGRESS_TICK); + } + + return OK; + } + + return -ENOMEM; + } + + return -ENOENT; +} +#endif #endif /* CONFIG_NET_ARP */ #endif /* CONFIG_NET */ diff --git a/net/devif/devif_poll.c b/net/devif/devif_poll.c index 495a941c9d7..fda45637450 100644 --- a/net/devif/devif_poll.c +++ b/net/devif/devif_poll.c @@ -676,12 +676,13 @@ static inline int devif_poll_tcp_connections(FAR struct net_driver_s *dev, #endif /**************************************************************************** - * Name: devif_poll_ipfrag + * Name: devif_poll_queue * * Description: - * Poll all ip fragments for available packets to send. + * Poll iob to send. * * Input Parameters: + * iobq - the iob queue to poll. * dev - NIC Device instance. * callback - the actual sending API provided by each NIC driver. * @@ -694,31 +695,32 @@ static inline int devif_poll_tcp_connections(FAR struct net_driver_s *dev, * ****************************************************************************/ -#ifdef CONFIG_NET_IPFRAG -static int devif_poll_ipfrag(FAR struct net_driver_s *dev, - devif_poll_callback_t callback) +#if defined(CONFIG_NET_ARP_SEND_QUEUE) || defined(CONFIG_NET_IPFRAG) +static int devif_poll_queue(FAR struct iob_queue_s *iobq, + FAR struct net_driver_s *dev, + devif_poll_callback_t callback) { - FAR struct iob_s *frag; + FAR struct iob_s *iob; bool reused = false; int bstop = false; while (!bstop) { - /* Dequeue outgoing fragment from dev->d_fragout */ + /* Dequeue outgoing iob from iobq */ - frag = iob_remove_queue(&dev->d_fragout); - if (frag == NULL) + iob = iob_remove_queue(iobq); + if (iob == NULL) { break; } - /* Frag buffer could be reused for other protocols */ + /* buffer could be reused for other protocols */ reused = true; /* Replace original iob */ - netdev_iob_replace(dev, frag); + netdev_iob_replace(dev, iob); /* build L2 headers */ @@ -732,9 +734,9 @@ static int devif_poll_ipfrag(FAR struct net_driver_s *dev, } } - /* Notify the device driver that ip fragments is available. */ + /* Notify the device driver that iob is available. */ - if (iob_peek_queue(&dev->d_fragout) != NULL) + if (iob_peek_queue(iobq) != NULL) { netdev_txnotify_dev(dev); } @@ -751,6 +753,60 @@ static int devif_poll_ipfrag(FAR struct net_driver_s *dev, } #endif +/**************************************************************************** + * Name: devif_poll_ipfrag + * + * Description: + * Poll all ip fragments for available packets to send. + * + * Input Parameters: + * dev - NIC Device instance. + * callback - the actual sending API provided by each NIC driver. + * + * Returned Value: + * Zero indicated the polling will continue, else stop the polling. + * + * Assumptions: + * This function is called from the MAC device driver with the network + * locked. + * + ****************************************************************************/ + +#ifdef CONFIG_NET_IPFRAG +static int devif_poll_ipfrag(FAR struct net_driver_s *dev, + devif_poll_callback_t callback) +{ + return devif_poll_queue(&dev->d_fragout, dev, callback); +} +#endif + +/**************************************************************************** + * Name: devif_poll_arp + * + * Description: + * Poll all queue iobs with arp finished to send. + * + * Input Parameters: + * dev - NIC Device instance. + * callback - the actual sending API provided by each NIC driver. + * + * Returned Value: + * Zero indicated the polling will continue, else stop the polling. + * + * Assumptions: + * This function is called from the MAC device driver with the network + * locked. + * + ****************************************************************************/ + +#ifdef CONFIG_NET_ARP_SEND_QUEUE +static int devif_poll_arp(FAR struct net_driver_s *dev, + devif_poll_callback_t callback) +{ + return devif_poll_queue(&dev->d_arpout, dev, callback); +} +#endif + /**************************************************************************** * Name: devif_poll_connections * @@ -789,6 +845,14 @@ static int devif_poll_connections(FAR struct net_driver_s *dev, * action. */ +#ifdef CONFIG_NET_ARP_SEND_QUEUE + bstop = devif_poll_arp(dev, callback); + if (bstop) + { + return bstop; + } +#endif + #ifdef CONFIG_NET_IPFRAG /* Traverse all of ip fragments for available packets to transfer */
