net: if: Lower ram usage for IP address lifetime handling
Instead of having one delayed_work struct / IP address, use
only one delayed_work struct for lifetime timer. This saves
over 20 bytes / allocated address struct.
Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
diff --git a/include/net/net_if.h b/include/net/net_if.h
index 98c88ef..6fbf08f 100644
--- a/include/net/net_if.h
+++ b/include/net/net_if.h
@@ -49,8 +49,16 @@
/** IP address */
struct net_addr address;
- /** Timer that triggers renewal */
- struct k_delayed_work lifetime;
+#if defined(CONFIG_NET_IPV6)
+ /** Used for track timers */
+ sys_snode_t node;
+
+ /** Address lifetime timer start time */
+ s64_t lifetime_timer_start;
+
+ /** lifetime timer timeout */
+ u32_t lifetime_timer_timeout;
+#endif
#if defined(CONFIG_NET_IPV6_DAD)
/** Duplicate address detection (DAD) timer */
diff --git a/samples/net/rpl_border_router/src/http.c b/samples/net/rpl_border_router/src/http.c
index 87bdada..784f6e0 100644
--- a/samples/net/rpl_border_router/src/http.c
+++ b/samples/net/rpl_border_router/src/http.c
@@ -12,6 +12,7 @@
#include <zephyr.h>
#include <stdio.h>
+#include <stdlib.h>
/* For Basic auth, we need base64 decoder which can be found
* in mbedtls library.
@@ -497,6 +498,19 @@
return ret;
}
+static inline u32_t remaining_lifetime(struct net_if_addr *ifaddr)
+{
+ s32_t remaining;
+
+ if (ifaddr->lifetime_timer_timeout == 0) {
+ return 0;
+ }
+
+ remaining = k_uptime_get() - ifaddr->lifetime_timer_start;
+
+ return abs(remaining) / K_MSEC(1000);
+}
+
static void append_unicast_addr(struct net_if *iface, struct user_data *data)
{
char addr[NET_IPV6_ADDR_LEN], lifetime[10];
@@ -525,9 +539,8 @@
if (unicast->is_infinite) {
snprintk(lifetime, sizeof(lifetime), "%s", "infinite");
} else {
- snprintk(lifetime, sizeof(lifetime), "%d",
- k_delayed_work_remaining_get(
- &unicast->lifetime));
+ snprintk(lifetime, sizeof(lifetime), "%u",
+ remaining_lifetime(unicast));
}
ret = append_and_send_data(data, false,
diff --git a/subsys/net/ip/ipv6.c b/subsys/net/ip/ipv6.c
index 3a56a09..7397c92 100644
--- a/subsys/net/ip/ipv6.c
+++ b/subsys/net/ip/ipv6.c
@@ -19,6 +19,7 @@
#endif
#include <errno.h>
+#include <stdlib.h>
#include <net/net_core.h>
#include <net/net_pkt.h>
#include <net/net_stats.h>
@@ -2423,9 +2424,17 @@
#define TWO_HOURS (2 * 60 * 60)
-static inline u32_t remaining(struct k_delayed_work *work)
+static inline u32_t remaining_lifetime(struct net_if_addr *ifaddr)
{
- return k_delayed_work_remaining_get(work) / K_SECONDS(1);
+ s32_t remaining;
+
+ if (ifaddr->lifetime_timer_timeout == 0) {
+ return 0;
+ }
+
+ remaining = k_uptime_get() - ifaddr->lifetime_timer_start;
+
+ return abs(remaining) / K_MSEC(1000);
}
static inline void handle_prefix_autonomous(struct net_pkt *pkt,
@@ -2453,7 +2462,7 @@
/* RFC 4862 ch 5.5.3 */
if ((prefix_info->valid_lifetime > TWO_HOURS) ||
(prefix_info->valid_lifetime >
- remaining(&ifaddr->lifetime))) {
+ remaining_lifetime(ifaddr))) {
NET_DBG("Timer updating for address %s "
"long lifetime %u secs",
net_sprint_ipv6_addr(&addr),
diff --git a/subsys/net/ip/net_if.c b/subsys/net/ip/net_if.c
index 095f236..55735f8 100644
--- a/subsys/net/ip/net_if.c
+++ b/subsys/net/ip/net_if.c
@@ -12,6 +12,7 @@
#include <init.h>
#include <kernel.h>
#include <linker/sections.h>
+#include <stdlib.h>
#include <string.h>
#include <net/net_core.h>
#include <net/net_pkt.h>
@@ -46,6 +47,12 @@
static struct net_if_router routers[CONFIG_NET_MAX_ROUTERS];
#if defined(CONFIG_NET_IPV6)
+/* Timer that triggers network address renewal */
+static struct k_delayed_work address_lifetime_timer;
+
+/* Track currently active address lifetime timers */
+static sys_slist_t active_address_lifetime_timers;
+
static struct {
struct net_if_ipv6 ipv6;
struct net_if *iface;
@@ -748,16 +755,96 @@
return NULL;
}
-static void ipv6_addr_expired(struct k_work *work)
+static void address_expired(struct net_if_addr *ifaddr)
{
- struct net_if_addr *ifaddr = CONTAINER_OF(work,
- struct net_if_addr,
- lifetime);
-
NET_DBG("IPv6 address %s is deprecated",
net_sprint_ipv6_addr(&ifaddr->address.in6_addr));
ifaddr->addr_state = NET_ADDR_DEPRECATED;
+ ifaddr->lifetime_timer_timeout = 0;
+
+ sys_slist_find_and_remove(&active_address_lifetime_timers,
+ &ifaddr->node);
+}
+
+static bool address_check_timeout(s64_t start, u32_t time, s64_t timeout)
+{
+ start += time;
+ start = abs(start);
+
+ if (start > timeout) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool address_timedout(struct net_if_addr *ifaddr, s64_t timeout)
+{
+ return address_check_timeout(ifaddr->lifetime_timer_start,
+ ifaddr->lifetime_timer_timeout,
+ timeout);
+}
+
+static u32_t address_manage_timeouts(struct net_if_addr *ifaddr, s64_t timeout)
+{
+ s32_t next_timeout;
+
+ if (address_timedout(ifaddr, timeout)) {
+ address_expired(ifaddr);
+ return UINT32_MAX;
+ }
+
+ next_timeout = timeout - (ifaddr->lifetime_timer_start +
+ ifaddr->lifetime_timer_timeout);
+ return abs(next_timeout);
+}
+
+static void address_lifetime_timeout(struct k_work *work)
+{
+ u32_t timeout_update = UINT32_MAX - 1;
+ s64_t timeout = k_uptime_get();
+ struct net_if_addr *current, *next;
+
+ ARG_UNUSED(work);
+
+ SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&active_address_lifetime_timers,
+ current, next, node) {
+ u32_t next_timeout;
+
+ next_timeout = address_manage_timeouts(current, timeout);
+ if (next_timeout < timeout_update) {
+ timeout_update = next_timeout;
+ }
+ }
+
+ if (timeout_update < (UINT32_MAX - 1)) {
+ NET_DBG("Waiting for %u ms", timeout_update);
+
+ k_delayed_work_submit(&address_lifetime_timer, timeout_update);
+ }
+}
+
+static void address_submit_work(u32_t timeout)
+{
+ if (!k_delayed_work_remaining_get(&address_lifetime_timer) ||
+ timeout < k_delayed_work_remaining_get(&address_lifetime_timer)) {
+ k_delayed_work_cancel(&address_lifetime_timer);
+ k_delayed_work_submit(&address_lifetime_timer, timeout);
+
+ NET_DBG("Next wakeup in %d ms",
+ k_delayed_work_remaining_get(&address_lifetime_timer));
+ }
+}
+
+static void address_start_timer(struct net_if_addr *ifaddr, u32_t vlifetime)
+{
+ sys_slist_append(&active_address_lifetime_timers, &ifaddr->node);
+
+ ifaddr->lifetime_timer_start = k_uptime_get();
+ ifaddr->lifetime_timer_timeout = K_SECONDS(vlifetime);
+
+ address_submit_work(ifaddr->lifetime_timer_timeout);
}
void net_if_ipv6_addr_update_lifetime(struct net_if_addr *ifaddr,
@@ -767,7 +854,7 @@
net_sprint_ipv6_addr(&ifaddr->address.in6_addr),
vlifetime);
- k_delayed_work_submit(&ifaddr->lifetime, K_SECONDS(vlifetime));
+ address_start_timer(ifaddr, vlifetime);
}
static struct net_if_addr *ipv6_addr_find(struct net_if *iface,
@@ -810,8 +897,6 @@
if (vlifetime) {
ifaddr->is_infinite = false;
- k_delayed_work_init(&ifaddr->lifetime, ipv6_addr_expired);
-
NET_DBG("Expiring %s in %u secs", net_sprint_ipv6_addr(addr),
vlifetime);
@@ -948,7 +1033,14 @@
}
if (!ipv6->unicast[i].is_infinite) {
- k_delayed_work_cancel(&ipv6->unicast[i].lifetime);
+ sys_slist_find_and_remove(
+ &active_address_lifetime_timers,
+ &ipv6->unicast[i].node);
+
+ if (sys_slist_is_empty(
+ &active_address_lifetime_timers)) {
+ k_delayed_work_cancel(&address_lifetime_timer);
+ }
}
ipv6->unicast[i].is_used = false;
@@ -2591,6 +2683,8 @@
#endif
#if defined(CONFIG_NET_IPV6)
+ k_delayed_work_init(&address_lifetime_timer, address_lifetime_timeout);
+
if (if_count > ARRAY_SIZE(ipv6_addresses)) {
NET_WARN("You have %lu IPv6 net_if addresses but %d "
"network interfaces", ARRAY_SIZE(ipv6_addresses),