diff --git a/subsys/net/lib/lwm2m/lwm2m_message_handling.c b/subsys/net/lib/lwm2m/lwm2m_message_handling.c index 5979a6f3127..62fbe10bdaa 100644 --- a/subsys/net/lib/lwm2m/lwm2m_message_handling.c +++ b/subsys/net/lib/lwm2m/lwm2m_message_handling.c @@ -315,6 +315,9 @@ void lwm2m_reset_message(struct lwm2m_message *msg, bool release) } else { msg->message_timeout_cb = NULL; (void)memset(&msg->cpkt, 0, sizeof(msg->cpkt)); +#if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT) + msg->cache_info = NULL; +#endif } } @@ -338,6 +341,9 @@ int lwm2m_init_message(struct lwm2m_message *msg) } lm2m_message_clear_allocations(msg); +#if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT) + msg->cache_info = NULL; +#endif r = coap_packet_init(&msg->cpkt, msg->msg_data, sizeof(msg->msg_data), COAP_VERSION_1, msg->type, tokenlen, token, msg->code, msg->mid); @@ -1045,17 +1051,31 @@ static int lwm2m_read_resource_data(struct lwm2m_message *msg, void *data_ptr, s return ret; } - static int lwm2m_read_cached_data(struct lwm2m_message *msg, struct lwm2m_time_series_resource *cached_data, uint8_t data_type) { #if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT) int ret; struct lwm2m_time_series_elem buf; + struct lwm2m_cache_read_entry *read_info; size_t length = lwm2m_cache_size(cached_data); LOG_DBG("Read cached data size %u", length); + if (msg->cache_info) { + read_info = &msg->cache_info->read_info[msg->cache_info->entry_size]; + /* Store original timeseries ring buffer get states for failure handling */ + read_info->cache_data = cached_data; + read_info->original_get_base = cached_data->rb.get_base; + read_info->original_get_head = cached_data->rb.get_head; + read_info->original_get_tail = cached_data->rb.get_tail; + msg->cache_info->entry_size++; + if (msg->cache_info->entry_limit) { + length = MIN(length, msg->cache_info->entry_limit); + LOG_DBG("Limited number of read %d", length); + } + } + for (size_t i = 0; i < length; i++) { if (!lwm2m_cache_read(cached_data, &buf)) { @@ -1125,6 +1145,18 @@ static int lwm2m_read_cached_data(struct lwm2m_message *msg, #endif } +static bool lwm2m_accept_timeseries_read(struct lwm2m_message *msg, + struct lwm2m_time_series_resource *cached_data) +{ +#if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT) + if (cached_data && msg->cache_info && lwm2m_cache_size(cached_data) && + msg->out.writer->put_data_timestamp) { + return true; + } +#endif + return false; +} + static int lwm2m_read_handler(struct lwm2m_engine_obj_inst *obj_inst, struct lwm2m_engine_res *res, struct lwm2m_engine_obj_field *obj_field, struct lwm2m_message *msg) { @@ -1188,8 +1220,7 @@ static int lwm2m_read_handler(struct lwm2m_engine_obj_inst *obj_inst, struct lwm cached_data = lwm2m_cache_entry_get_by_object(&temp_path); - if (cached_data && lwm2m_cache_size(cached_data) && - msg->out.writer->put_data_timestamp) { + if (lwm2m_accept_timeseries_read(msg, cached_data)) { /* Content Format Writer have to support timestamp write */ ret = lwm2m_read_cached_data(msg, cached_data, obj_field->data_type); } else { @@ -2407,6 +2438,24 @@ static struct lwm2m_obj_path *lwm2m_read_first_path_ptr(sys_slist_t *lwm2m_path_ return &entry->path; } +static void notify_cached_pending_data_trig(struct observe_node *obs) +{ +#if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT) + struct lwm2m_time_series_resource *cached_data; + struct lwm2m_obj_path_list *entry; + + SYS_SLIST_FOR_EACH_CONTAINER(&obs->path_list, entry, node) { + cached_data = lwm2m_cache_entry_get_by_object(&entry->path); + if (!cached_data || lwm2m_cache_size(cached_data) == 0) { + continue; + } + + /* Trig next send by iMin */ + lwm2m_notify_observer_path(&entry->path); + } +#endif +} + static int notify_message_reply_cb(const struct coap_packet *response, struct coap_reply *reply, const struct sockaddr *from) { @@ -2446,6 +2495,7 @@ static int notify_message_reply_cb(const struct coap_packet *response, struct co lwm2m_read_first_path_ptr(&obs->path_list), reply->user_data); } + notify_cached_pending_data_trig(obs); } } @@ -2472,18 +2522,68 @@ static int do_send_op(struct lwm2m_message *msg, uint16_t content_format, } } +static bool lwm2m_timeseries_data_rebuild(struct lwm2m_message *msg, int error_code) +{ +#if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT) + struct lwm2m_cache_read_info *cache_temp; + + if (error_code != -ENOMEM) { + return false; + } + + cache_temp = msg->cache_info; + + if (!cache_temp || !cache_temp->entry_size) { + return false; + } + + /* Put Ring buffer back to original */ + for (int i = 0; i < cache_temp->entry_size; i++) { + cache_temp->read_info[i].cache_data->rb.get_head = + cache_temp->read_info[i].original_get_head; + cache_temp->read_info[i].cache_data->rb.get_tail = + cache_temp->read_info[i].original_get_tail; + cache_temp->read_info[i].cache_data->rb.get_base = + cache_temp->read_info[i].original_get_base; + } + + if (cache_temp->entry_limit) { + /* Limited number of message build fail also */ + return false; + } + + /* Limit re-build entry count */ + cache_temp->entry_limit = LWM2M_LIMITED_TIMESERIES_RESOURCE_COUNT / cache_temp->entry_size; + cache_temp->entry_size = 0; + + lwm2m_reset_message(msg, false); + LOG_INF("Try re-buildbuild again with limited cache size %d", cache_temp->entry_limit); + return true; +#else + return false; +#endif +} + int generate_notify_message(struct lwm2m_ctx *ctx, struct observe_node *obs, void *user_data) { struct lwm2m_message *msg; struct lwm2m_engine_obj_inst *obj_inst; struct lwm2m_obj_path *path; int ret = 0; +#if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT) + struct lwm2m_cache_read_info cache_temp_info; + + cache_temp_info.entry_size = 0; + cache_temp_info.entry_limit = 0; +#endif msg = lwm2m_get_message(ctx); if (!msg) { LOG_ERR("Unable to get a lwm2m message!"); return -ENOMEM; } +msg_init: + if (!obs->composite) { path = lwm2m_read_first_path_ptr(&obs->path_list); if (!path) { @@ -2527,6 +2627,9 @@ int generate_notify_message(struct lwm2m_ctx *ctx, struct observe_node *obs, voi LOG_ERR("Unable to init lwm2m message! (err: %d)", ret); goto cleanup; } +#if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT) + msg->cache_info = &cache_temp_info; +#endif /* lwm2m_init_message() cleans the coap reply fields, so we assign our data here */ msg->reply->user_data = user_data; @@ -2549,6 +2652,12 @@ int generate_notify_message(struct lwm2m_ctx *ctx, struct observe_node *obs, voi } if (ret < 0) { + if (lwm2m_timeseries_data_rebuild(msg, ret)) { + /* Message Build fail by ENOMEM and data include timeseries data. + * Try rebuild message again by limiting timeseries data entry lenghts. + */ + goto msg_init; + } LOG_ERR("error in multi-format read (err:%d)", ret); goto cleanup; } @@ -2556,6 +2665,9 @@ int generate_notify_message(struct lwm2m_ctx *ctx, struct observe_node *obs, voi obs->active_tx_operation = true; obs->resource_update = false; lwm2m_information_interface_send(msg); +#if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT) + msg->cache_info = NULL; +#endif LOG_DBG("NOTIFY MSG: SENT"); return 0; @@ -2830,6 +2942,45 @@ static void do_send_timeout_cb(struct lwm2m_message *msg) } #endif +#if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT) +static bool init_next_pending_timeseries_data(struct lwm2m_cache_read_info *cache_temp, + sys_slist_t *lwm2m_path_list, + sys_slist_t *lwm2m_path_free_list) +{ + struct lwm2m_obj_path temp; + uint32_t bytes_available = 0; + int ret; + + /* Check do we have still pending data to send */ + for (int i = 0; i < cache_temp->entry_size; i++) { + if (ring_buf_is_empty(&cache_temp->read_info[i].cache_data->rb)) { + /* Skip Emtpy cached buffers */ + continue; + } + + ret = lwm2m_string_to_path(cache_temp->read_info[i].cache_data->path, &temp, '/'); + if (ret < 0) { + return false; + } + /* Add to linked list */ + if (lwm2m_engine_add_path_to_list(lwm2m_path_list, lwm2m_path_free_list, &temp)) { + return false; + } + + bytes_available += ring_buf_size_get(&cache_temp->read_info[i].cache_data->rb); + } + + if (bytes_available == 0) { + return false; + } + + LOG_INF("Allocate a new message for pending data %u", bytes_available); + cache_temp->entry_size = 0; + cache_temp->entry_limit = 0; + return true; +} +#endif + int lwm2m_engine_send(struct lwm2m_ctx *ctx, char const *path_list[], uint8_t path_list_size, bool confirmation_request) { @@ -2843,6 +2994,12 @@ int lwm2m_engine_send(struct lwm2m_ctx *ctx, char const *path_list[], uint8_t pa struct lwm2m_obj_path_list lwm2m_path_list_buf[CONFIG_LWM2M_COMPOSITE_PATH_LIST_SIZE]; sys_slist_t lwm2m_path_list; sys_slist_t lwm2m_path_free_list; +#if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT) + struct lwm2m_cache_read_info cache_temp_info; + + cache_temp_info.entry_size = 0; + cache_temp_info.entry_limit = 0; +#endif /* Validate Connection */ if (!lwm2m_rd_client_is_registred(ctx)) { @@ -2884,7 +3041,9 @@ int lwm2m_engine_send(struct lwm2m_ctx *ctx, char const *path_list[], uint8_t pa } /* Clear path which are part are part of recursive path /1 will include /1/0/1 */ lwm2m_engine_clear_duplicate_path(&lwm2m_path_list, &lwm2m_path_free_list); - +#if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT) +msg_alloc: +#endif /* Allocate Message buffer */ msg = lwm2m_get_message(ctx); if (!msg) { @@ -2892,6 +3051,8 @@ int lwm2m_engine_send(struct lwm2m_ctx *ctx, char const *path_list[], uint8_t pa return -ENOMEM; } +msg_init: + if (confirmation_request) { msg->type = COAP_TYPE_CON; msg->reply_cb = do_send_reply_cb; @@ -2910,6 +3071,10 @@ int lwm2m_engine_send(struct lwm2m_ctx *ctx, char const *path_list[], uint8_t pa if (ret) { goto cleanup; } +#if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT) + msg->cache_info = &cache_temp_info; +#endif + ret = select_writer(&msg->out, content_format); if (ret) { goto cleanup; @@ -2926,12 +3091,36 @@ int lwm2m_engine_send(struct lwm2m_ctx *ctx, char const *path_list[], uint8_t pa ret = do_send_op(msg, content_format, &lwm2m_path_list); lwm2m_registry_unlock(); if (ret < 0) { + if (lwm2m_timeseries_data_rebuild(msg, ret)) { + /* Message Build fail by ENOMEM and data include timeseries data. + * Try rebuild message again by limiting timeseries data entry lenghts. + */ + goto msg_init; + } + LOG_ERR("Send (err:%d)", ret); goto cleanup; } +#if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT) + msg->cache_info = NULL; +#endif LOG_INF("Send op to server (/dp)"); lwm2m_information_interface_send(msg); +#if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT) + if (cache_temp_info.entry_size) { + /* Init Path list for continuous message allocation */ + lwm2m_engine_path_list_init(&lwm2m_path_list, &lwm2m_path_free_list, + lwm2m_path_list_buf, + CONFIG_LWM2M_COMPOSITE_PATH_LIST_SIZE); + + if (init_next_pending_timeseries_data(&cache_temp_info, &lwm2m_path_list, + &lwm2m_path_free_list)) { + goto msg_alloc; + } + } +#endif + return 0; cleanup: lwm2m_reset_message(msg, true); diff --git a/subsys/net/lib/lwm2m/lwm2m_object.h b/subsys/net/lib/lwm2m/lwm2m_object.h index c85514e5811..f9def911f94 100644 --- a/subsys/net/lib/lwm2m/lwm2m_object.h +++ b/subsys/net/lib/lwm2m/lwm2m_object.h @@ -492,6 +492,9 @@ struct lwm2m_message { /** Message transmission handling for TYPE_CON */ struct coap_pending *pending; struct coap_reply *reply; +#if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT) + struct lwm2m_cache_read_info *cache_info; +#endif /** Message configuration */ uint8_t *token; diff --git a/subsys/net/lib/lwm2m/lwm2m_registry.h b/subsys/net/lib/lwm2m/lwm2m_registry.h index f8413157dda..cfaafdce100 100644 --- a/subsys/net/lib/lwm2m/lwm2m_registry.h +++ b/subsys/net/lib/lwm2m/lwm2m_registry.h @@ -212,6 +212,24 @@ struct lwm2m_time_series_resource { struct ring_buf rb; }; +#if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT) + +#define LWM2M_LIMITED_TIMESERIES_RESOURCE_COUNT 20 + +struct lwm2m_cache_read_entry { + struct lwm2m_time_series_resource *cache_data; + int32_t original_get_head; + int32_t original_get_tail; + int32_t original_get_base; +}; + +struct lwm2m_cache_read_info { + struct lwm2m_cache_read_entry read_info[CONFIG_LWM2M_MAX_CACHED_RESOURCES]; + int entry_limit; + int entry_size; +}; +#endif + int lwm2m_engine_data_cache_init(void); struct lwm2m_time_series_resource *lwm2m_cache_entry_get_by_object(struct lwm2m_obj_path *obj_path); struct lwm2m_time_series_resource *lwm2m_cache_entry_get_by_string(char const *resource_path); diff --git a/tests/net/all/prj.conf b/tests/net/all/prj.conf index a6bf6cc838d..523a374ec67 100644 --- a/tests/net/all/prj.conf +++ b/tests/net/all/prj.conf @@ -301,6 +301,7 @@ CONFIG_LWM2M_IPSO_ONOFF_SWITCH=y CONFIG_LWM2M_IPSO_PUSH_BUTTON=y CONFIG_LWM2M_IPSO_CURRENT_SENSOR=y CONFIG_LWM2M_IPSO_FILLING_SENSOR=y +CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT=y # VLAN CONFIG_NET_VLAN=y