net: trickle: Do not initialize a work item from its handler
Initializing a work item from its handler will destroy the content of
the kernel structures used to process the work item. This can lead to a
system crash for example when the delayed work is being rescheduled when
the previous run is already queued for processing but not yet executed.
Fix this by initializing the work item once during trickle timer
creation and moving the logic, previously achieved by switching the work
handler, into the new work handler.
Signed-off-by: Robert Lubos <robert.lubos@nordicsemi.no>
diff --git a/include/net/trickle.h b/include/net/trickle.h
index 896da89..430612d 100644
--- a/include/net/trickle.h
+++ b/include/net/trickle.h
@@ -60,6 +60,7 @@
uint8_t c; /**< Consistency counter */
uint32_t Imax_abs; /**< Max interval size in ms (not doublings) */
+ bool double_to;
struct k_work_delayable timer;
net_trickle_cb_t cb; /**< Callback to be called when timer expires */
diff --git a/subsys/net/ip/trickle.c b/subsys/net/ip/trickle.c
index 037a984..0dafee1 100644
--- a/subsys/net/ip/trickle.c
+++ b/subsys/net/ip/trickle.c
@@ -50,11 +50,8 @@
return I + (sys_rand32_get() % I);
}
-static void double_interval_timeout(struct k_work *work)
+static void double_interval_timeout(struct net_trickle *trickle)
{
- struct net_trickle *trickle = CONTAINER_OF(work,
- struct net_trickle,
- timer);
uint32_t rand_time;
uint32_t last_end = get_end(trickle);
@@ -80,7 +77,8 @@
NET_DBG("doubling time %u", rand_time);
trickle->Istart = k_uptime_get_32() + rand_time;
- k_work_init_delayable(&trickle->timer, trickle_timeout);
+ trickle->double_to = false;
+
k_work_reschedule(&trickle->timer, K_MSEC(rand_time));
NET_DBG("last end %u new end %u for %u I %u",
@@ -100,16 +98,13 @@
NET_DBG("Clock wrap");
}
- k_work_init_delayable(&trickle->timer, double_interval_timeout);
+ trickle->double_to = true;
+
k_work_reschedule(&trickle->timer, K_MSEC(diff));
}
-static void trickle_timeout(struct k_work *work)
+static void inteval_timeout(struct net_trickle *trickle)
{
- struct net_trickle *trickle = CONTAINER_OF(work,
- struct net_trickle,
- timer);
-
NET_DBG("Trickle timeout at %d", k_uptime_get_32());
if (trickle->cb) {
@@ -125,6 +120,19 @@
}
}
+static void trickle_timeout(struct k_work *work)
+{
+ struct net_trickle *trickle = CONTAINER_OF(work,
+ struct net_trickle,
+ timer);
+
+ if (trickle->double_to) {
+ double_interval_timeout(trickle);
+ } else {
+ inteval_timeout(trickle);
+ }
+}
+
static void setup_new_interval(struct net_trickle *trickle)
{
uint32_t t;
@@ -180,6 +188,7 @@
trickle->cb = cb;
trickle->user_data = user_data;
+ trickle->double_to = false;
/* Random I in [Imin , Imax] */
trickle->I = trickle->Imin +