net: lwm2m: Fix retransmission logic

The retaransmission logic was not correct in the lwm2m_engine, and could
lead to faulty behavior in case multiple messages were pending for
retransmission in the queue.

1. Since there is a singe delayed work item for entire retransmission
   queue, `coap_pending_next_to_expire` should be called before
   scheduling next timeout, to identify which message is going to expire
   next (and when). Currently, the engine always set next timeout, based
   on timeout from the message being currently re-transmitted.
2. In case the message was re-transmitted several times, and is removed
   from the retansmission queue due to a timeout, next retransmission
   should be scheduled, in case there are other messages on the queue.
3. Verify the timeout of the earliest message to expire in the
   retransmission handler. In case messages from the beginning of the
   queue were removed, we might need to schedule the retransmission
   again, instead of sending message rightaway.
4. `lwm2m_send_message` is not handling retransmissions anyway, so
   there's no need to check send attempts. Instead, verify
   retransmission work item is already pending, and update its timeout
   if needed.

Signed-off-by: Robert Lubos <robert.lubos@nordicsemi.no>
This commit is contained in:
Robert Lubos 2020-04-24 10:33:24 +02:00 committed by Jukka Rissanen
commit afb73d1d09

View file

@ -1037,13 +1037,16 @@ int lwm2m_send_message(struct lwm2m_message *msg)
}
if (msg->type == COAP_TYPE_CON) {
/* don't re-queue the retransmit work on retransmits */
if (msg->send_attempts > 1) {
return 0;
}
s32_t remaining = k_delayed_work_remaining_get(
&msg->ctx->retransmit_work);
k_delayed_work_submit(&msg->ctx->retransmit_work,
K_MSEC(msg->pending->timeout));
/* If the item is already pending and its timeout is smaller
* than the new one, skip the submission.
*/
if (remaining == 0 || remaining > msg->pending->timeout) {
k_delayed_work_submit(&msg->ctx->retransmit_work,
K_MSEC(msg->pending->timeout));
}
} else {
lwm2m_reset_message(msg, true);
}
@ -3717,6 +3720,7 @@ static void retransmit_request(struct k_work *work)
struct lwm2m_ctx *client_ctx;
struct lwm2m_message *msg;
struct coap_pending *pending;
s32_t remaining;
client_ctx = CONTAINER_OF(work, struct lwm2m_ctx, retransmit_work);
pending = coap_pending_next_to_expire(client_ctx->pendings,
@ -3725,10 +3729,19 @@ static void retransmit_request(struct k_work *work)
return;
}
remaining = pending->t0 + pending->timeout - k_uptime_get_32();
if (remaining > 0) {
/* First message to expire was removed from the list,
* schedule next.
*/
goto next;
}
msg = find_msg(pending, NULL);
if (!msg) {
LOG_ERR("pending has no valid LwM2M message!");
return;
coap_pending_clear(pending);
goto next;
}
if (!coap_pending_cycle(pending)) {
@ -3742,7 +3755,7 @@ static void retransmit_request(struct k_work *work)
* which balances the ref we made in coap_pending_cycle()
*/
lwm2m_reset_message(msg, true);
return;
goto next;
}
LOG_INF("Resending message: %p", msg);
@ -3752,8 +3765,19 @@ static void retransmit_request(struct k_work *work)
/* don't error here, retry until timeout */
}
k_delayed_work_submit(&client_ctx->retransmit_work,
K_MSEC(pending->timeout));
next:
pending = coap_pending_next_to_expire(client_ctx->pendings,
CONFIG_LWM2M_ENGINE_MAX_PENDING);
if (!pending) {
return;
}
remaining = pending->t0 + pending->timeout - k_uptime_get_32();
if (remaining < 0) {
remaining = 0;
}
k_delayed_work_submit(&client_ctx->retransmit_work, K_MSEC(remaining));
}
static int notify_message_reply_cb(const struct coap_packet *response,