From 1a5e13a79b989c2ed56da227729709c732b46c3f Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Mon, 3 Mar 2025 14:45:34 +0200 Subject: [PATCH] net: if: Release the interface lock early when starting IPv4 ACD In order to avoid any mutex deadlocks between iface->lock and TX lock, release the interface lock before calling a function that will acquire TX lock. See previous commit for similar issue in RS timer handling. So here we create a separate list of ACD addresses that are to be started when network interface comes up without iface->lock held. Signed-off-by: Jukka Rissanen --- include/zephyr/net/net_if.h | 3 +++ subsys/net/ip/net_if.c | 31 ++++++++++++++++++++++++++++--- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/include/zephyr/net/net_if.h b/include/zephyr/net/net_if.h index d1c106876fd..310ce39924e 100644 --- a/include/zephyr/net/net_if.h +++ b/include/zephyr/net/net_if.h @@ -112,6 +112,9 @@ struct net_if_addr { /** Address conflict detection (ACD) timer. */ sys_snode_t acd_node; + /** ACD needed list node */ + sys_snode_t acd_need_node; + /** ACD timeout value. */ k_timepoint_t acd_timeout; diff --git a/subsys/net/ip/net_if.c b/subsys/net/ip/net_if.c index f200339643a..219080dac08 100644 --- a/subsys/net/ip/net_if.c +++ b/subsys/net/ip/net_if.c @@ -4310,7 +4310,9 @@ void net_if_ipv4_start_acd(struct net_if *iface, struct net_if_addr *ifaddr) void net_if_start_acd(struct net_if *iface) { + struct net_if_addr *ifaddr, *next; struct net_if_ipv4 *ipv4; + sys_slist_t acd_needed; int ret; net_if_lock(iface); @@ -4332,6 +4334,11 @@ void net_if_start_acd(struct net_if *iface) ipv4->conflict_cnt = 0; + /* Start ACD for all the addresses that were added earlier when + * the interface was down. + */ + sys_slist_init(&acd_needed); + /* Start ACD for all the addresses that were added earlier when * the interface was down. */ @@ -4343,9 +4350,21 @@ void net_if_start_acd(struct net_if *iface) continue; } - net_if_ipv4_start_acd(iface, &ipv4->unicast[i].ipv4); + sys_slist_prepend(&acd_needed, &ipv4->unicast[i].ipv4.acd_need_node); } + net_if_unlock(iface); + + /* Start ACD for all the addresses without holding the iface lock + * to avoid any possible mutex deadlock issues. + */ + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&acd_needed, + ifaddr, next, acd_need_node) { + net_if_ipv4_start_acd(iface, ifaddr); + } + + return; + out: net_if_unlock(iface); } @@ -4439,7 +4458,8 @@ struct net_if_addr *net_if_ipv4_addr_add(struct net_if *iface, if (!(l2_flags_get(iface) & NET_L2_POINT_TO_POINT) && !net_ipv4_is_addr_loopback(addr)) { - net_if_ipv4_start_acd(iface, ifaddr); + /* ACD is started after the lock is released. */ + ; } else { ifaddr->addr_state = NET_ADDR_PREFERRED; } @@ -4449,7 +4469,12 @@ struct net_if_addr *net_if_ipv4_addr_add(struct net_if *iface, net_mgmt_event_notify_with_info(NET_EVENT_IPV4_ADDR_ADD, iface, &ifaddr->address.in_addr, sizeof(struct in_addr)); - goto out; + + net_if_unlock(iface); + + net_if_ipv4_start_acd(iface, ifaddr); + + return ifaddr; } out: