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 <jukka.rissanen@nordicsemi.no>
This commit is contained in:
Jukka Rissanen 2025-03-03 14:45:34 +02:00 committed by Fabio Baltieri
commit 1a5e13a79b
2 changed files with 31 additions and 3 deletions

View file

@ -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;

View file

@ -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: