net: lwm2m: Allow to acknowledge request early from the callback

LwM2M engine by default sends piggybacked responses for requests after
all callbacks are executed. This approach however isn't good enough if
the application callback executes some lenghty operations (for instance
during FW update). Delaying the ACK may result in unnecessary
retransmissions.

This commits adds an API function which allows to send an early empty
ACK from the application callback. This prevents further retransmissions
from the server side. After all callbacks are executed, the LwM2M engine
will send the response as a separate CON message.

Signed-off-by: Robert Lubos <robert.lubos@nordicsemi.no>
This commit is contained in:
Robert Lubos 2020-12-01 13:56:34 +01:00 committed by Anas Nashif
commit 4331c05f17
2 changed files with 91 additions and 0 deletions

View file

@ -80,6 +80,13 @@ struct lwm2m_ctx {
struct k_delayed_work retransmit_work;
struct sys_mutex send_lock;
/** A pointer to currently processed request, for internal LwM2M engine
* use. The underlying type is ``struct lwm2m_message``, but since it's
* declared in a private header and not exposed to the application,
* it's stored as a void pointer.
*/
void *processed_req;
#if defined(CONFIG_LWM2M_DTLS_SUPPORT)
/** TLS tag is set by client as a reference used when the
* LwM2M engine calls tls_credential_(add|delete)
@ -831,6 +838,19 @@ int lwm2m_engine_delete_res_inst(char *pathstr);
*/
int lwm2m_engine_start(struct lwm2m_ctx *client_ctx);
/**
* @brief Acknowledge the currently processed request with an empty ACK.
*
* LwM2M engine by default sends piggybacked responses for requests.
* This function allows to send an empty ACK for a request earlier (from the
* application callback). The LwM2M engine will then send the actual response
* as a separate CON message after all callbacks are executed.
*
* @param[in] client_ctx LwM2M context
*
*/
void lwm2m_acknowledge(struct lwm2m_ctx *client_ctx);
/**
* @brief LwM2M RD client events
*

View file

@ -1076,6 +1076,27 @@ cleanup:
return ret;
}
void lwm2m_acknowledge(struct lwm2m_ctx *client_ctx)
{
struct lwm2m_message *request;
if (client_ctx == NULL || client_ctx->processed_req == NULL) {
return;
}
request = (struct lwm2m_message *)client_ctx->processed_req;
if (request->acknowledged) {
return;
}
if (lwm2m_send_empty_ack(client_ctx, request->mid) < 0) {
return;
}
request->acknowledged = true;
}
uint16_t lwm2m_get_rd_data(uint8_t *client_data, uint16_t size)
{
struct lwm2m_engine_obj *obj;
@ -3766,6 +3787,42 @@ error:
return 0;
}
static int lwm2m_response_promote_to_con(struct lwm2m_message *msg)
{
int ret;
msg->type = COAP_TYPE_CON;
msg->mid = coap_next_id();
/* Since the response CoAP packet is already generated at this point,
* tweak the specific fields manually:
* - CoAP message type (byte 0, bits 2 and 3)
* - CoAP message id (bytes 2 and 3)
*/
msg->cpkt.data[0] &= ~(0x3 << 4);
msg->cpkt.data[0] |= (msg->type & 0x3) << 4;
msg->cpkt.data[2] = msg->mid >> 8;
msg->cpkt.data[3] = (uint8_t) msg->mid;
/* Add the packet to the pending list. */
msg->pending = coap_pending_next_unused(
msg->ctx->pendings,
CONFIG_LWM2M_ENGINE_MAX_PENDING);
if (!msg->pending) {
LOG_ERR("Unable to find a free pending to track "
"retransmissions.");
return -ENOMEM;
}
ret = coap_pending_init(msg->pending, &msg->cpkt, &msg->ctx->remote_addr);
if (ret < 0) {
LOG_ERR("Unable to initialize a pending "
"retransmission (err:%d).", ret);
}
return ret;
}
static void lwm2m_udp_receive(struct lwm2m_ctx *client_ctx,
uint8_t *buf, uint16_t buf_len,
struct sockaddr *from_addr,
@ -3867,12 +3924,26 @@ static void lwm2m_udp_receive(struct lwm2m_ctx *client_ctx,
/* skip token generation by default */
msg->tkl = 0;
client_ctx->processed_req = msg;
/* process the response to this request */
r = udp_request_handler(&response, msg);
if (r < 0) {
return;
}
if (msg->acknowledged) {
r = lwm2m_response_promote_to_con(msg);
if (r < 0) {
LOG_ERR("Failed to promote reponse to CON: %d",
r);
lwm2m_reset_message(msg, true);
return;
}
}
client_ctx->processed_req = NULL;
r = lwm2m_send_message(msg);
if (r < 0) {
LOG_ERR("Err sending response: %d", r);