From 09e7262be85a848f4b648e53f8cb5ea11977da8a Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Tue, 21 May 2019 11:12:45 +0200 Subject: [PATCH] net/iface: Coalesce all DAD timers through one This reduces the size of struct net_if_addr by 24 bytes by moving the k_delayed_work attribute into net_if core code. Then each net_if_addr can be added to the timer handler via a slist. This does not make much gain if the system has only 1 unicast IPv6 address. It's a nice memory improvment once it has 2+ unicast IPv6 address. Note that having IPv4 enabled along with IPv6 will also see memory improvements since both IPv6 and IPv4 use the same struct net_if_addr. Fixes #8728 Signed-off-by: Tomasz Bursztyka --- include/net/net_if.h | 3 +- subsys/net/ip/net_if.c | 88 +++++++++++++++++++++++++++++------------- 2 files changed, 64 insertions(+), 27 deletions(-) diff --git a/include/net/net_if.h b/include/net/net_if.h index 1ff0655f762..4f38de77bbd 100644 --- a/include/net/net_if.h +++ b/include/net/net_if.h @@ -56,7 +56,8 @@ struct net_if_addr { #if defined(CONFIG_NET_IPV6_DAD) /** Duplicate address detection (DAD) timer */ - struct k_delayed_work dad_timer; + sys_snode_t dad_node; + u32_t dad_start; #endif /** How the IP address was set */ enum net_addr_type addr_type; diff --git a/subsys/net/ip/net_if.c b/subsys/net/ip/net_if.c index 279f0ac0cc9..656bd32912e 100644 --- a/subsys/net/ip/net_if.c +++ b/subsys/net/ip/net_if.c @@ -59,6 +59,12 @@ static struct k_delayed_work prefix_lifetime_timer; /* Track currently active IPv6 prefix lifetime timers */ static sys_slist_t active_prefix_lifetime_timers; +#if defined(CONFIG_NET_IPV6_DAD) +/** Duplicate address detection (DAD) timer */ +static struct k_delayed_work dad_timer; +static sys_slist_t active_dad_timers; +#endif + static struct { struct net_if_ipv6 ipv6; struct net_if *iface; @@ -570,31 +576,55 @@ static void join_mcast_nodes(struct net_if *iface, struct in6_addr *addr) static void dad_timeout(struct k_work *work) { - /* This means that the DAD succeed. */ - struct net_if_addr *tmp, *ifaddr = CONTAINER_OF(work, - struct net_if_addr, - dad_timer); - struct net_if *iface = NULL; + u32_t current_time = k_uptime_get_32(); + struct net_if_addr *ifaddr, *next; - NET_DBG("DAD succeeded for %s", - log_strdup(net_sprint_ipv6_addr(&ifaddr->address.in6_addr))); + ARG_UNUSED(work); - ifaddr->addr_state = NET_ADDR_PREFERRED; + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&active_dad_timers, + ifaddr, next, dad_node) { + struct net_if_addr *tmp; + struct net_if *iface; - /* Because we do not know the interface at this point, we need to - * lookup for it. - */ - tmp = net_if_ipv6_addr_lookup(&ifaddr->address.in6_addr, &iface); - if (tmp == ifaddr) { - net_mgmt_event_notify_with_info(NET_EVENT_IPV6_DAD_SUCCEED, - iface, - &ifaddr->address.in6_addr, - sizeof(struct in6_addr)); + if ((s32_t)(ifaddr->dad_start + + DAD_TIMEOUT - current_time) > 0) { + break; + } - /* The address gets added to neighbor cache which is not needed - * in this case as the address is our own one. + /* Removing the ifaddr from active_dad_timers list */ + sys_slist_remove(&active_dad_timers, NULL, &ifaddr->dad_node); + + NET_DBG("DAD succeeded for %s", + log_strdup(net_sprint_ipv6_addr( + &ifaddr->address.in6_addr))); + + ifaddr->addr_state = NET_ADDR_PREFERRED; + + /* Because we do not know the interface at this point, + * we need to lookup for it. */ - net_ipv6_nbr_rm(iface, &ifaddr->address.in6_addr); + iface = NULL; + tmp = net_if_ipv6_addr_lookup(&ifaddr->address.in6_addr, + &iface); + if (tmp == ifaddr) { + net_mgmt_event_notify_with_info( + NET_EVENT_IPV6_DAD_SUCCEED, + iface, &ifaddr->address.in6_addr, + sizeof(struct in6_addr)); + + /* The address gets added to neighbor cache which is not + * needed in this case as the address is our own one. + */ + net_ipv6_nbr_rm(iface, &ifaddr->address.in6_addr); + } + + ifaddr = NULL; + } + + if (ifaddr) { + k_delayed_work_submit(&dad_timer, + ifaddr->dad_start + + DAD_TIMEOUT - current_time); } } @@ -615,7 +645,12 @@ static void net_if_ipv6_start_dad(struct net_if *iface, ifaddr->dad_count = 1U; if (!net_ipv6_start_dad(iface, ifaddr)) { - k_delayed_work_submit(&ifaddr->dad_timer, DAD_TIMEOUT); + ifaddr->dad_start = k_uptime_get_32(); + sys_slist_append(&active_dad_timers, &ifaddr->dad_node); + + if (!k_delayed_work_remaining_get(&dad_timer)) { + k_delayed_work_submit(&dad_timer, DAD_TIMEOUT); + } } } else { NET_DBG("Interface %p is down, starting DAD for %s later.", @@ -676,7 +711,7 @@ void net_if_ipv6_dad_failed(struct net_if *iface, const struct in6_addr *addr) return; } - k_delayed_work_cancel(&ifaddr->dad_timer); + sys_slist_find_and_remove(&active_dad_timers, &ifaddr->dad_node); net_mgmt_event_notify_with_info(NET_EVENT_IPV6_DAD_FAILED, iface, &ifaddr->address.in6_addr, @@ -685,9 +720,10 @@ void net_if_ipv6_dad_failed(struct net_if *iface, const struct in6_addr *addr) net_if_ipv6_addr_rm(iface, addr); } -static inline void iface_ipv6_dad_init(struct net_if_addr *ifaddr) +static inline void iface_ipv6_dad_init(void) { - k_delayed_work_init(&ifaddr->dad_timer, dad_timeout); + k_delayed_work_init(&dad_timer, dad_timeout); + sys_slist_init(&active_dad_timers); } #else @@ -1027,8 +1063,6 @@ static inline void net_if_addr_init(struct net_if_addr *ifaddr, ifaddr->addr_type = addr_type; net_ipaddr_copy(&ifaddr->address.in6_addr, addr); - iface_ipv6_dad_init(ifaddr); - /* FIXME - set the mcast addr for this node */ if (vlifetime) { @@ -2172,6 +2206,8 @@ static void iface_ipv6_init(int if_count) { int i; + iface_ipv6_dad_init(); + k_delayed_work_init(&address_lifetime_timer, address_lifetime_timeout); k_delayed_work_init(&prefix_lifetime_timer, prefix_lifetime_timeout);