From 177e9b93bf0595b70f04100ea6a19d2d05da0914 Mon Sep 17 00:00:00 2001 From: Pavel Vasilyev Date: Thu, 20 Apr 2023 18:09:02 +0200 Subject: [PATCH] Bluetooth: Mesh: Add API to store model's user data in settings work Mesh models may have a data that needs to be stored persistently. Currently, the models should call bt_mesh_model_data_store and the store will happen in the calling context. Most likely that it will be called in BT RX thread as this is the context from which model's opcodes handlers are called. Thus, the thread will be blocked until the store is finished. Another issues is that some models may have states that changes frequently. Triggering the store on every state change may wear out flash. Therefore, the models need to implement some postpone mechanism to reduce the flash wear out. The mesh stack has already implemented the mechanism of deferred store with its own settings. The models could use it instead of implementing their own mechanism. In combination with the mesh settings workqueue, the models can store their data without blocking the stack work. Signed-off-by: Pavel Vasilyev --- .../bluetooth/api/mesh/access.rst | 16 +++++++++++++ include/zephyr/bluetooth/mesh/access.h | 23 +++++++++++++++++++ subsys/bluetooth/mesh/access.c | 11 +++++++++ subsys/bluetooth/mesh/access.h | 1 + 4 files changed, 51 insertions(+) diff --git a/doc/connectivity/bluetooth/api/mesh/access.rst b/doc/connectivity/bluetooth/api/mesh/access.rst index 06236f98cb6..37d5d6fddef 100644 --- a/doc/connectivity/bluetooth/api/mesh/access.rst +++ b/doc/connectivity/bluetooth/api/mesh/access.rst @@ -149,6 +149,22 @@ storage. The model can retrieve the data by calling the ``read_cb`` passed as a parameter to the callback. See the :ref:`settings_api` module documentation for details. +When model data changes frequently, storing it on every change may lead to +increased wear of flash. To reduce the wear, the model can postpone storing of +data by calling :c:func:`bt_mesh_model_data_store_schedule`. The stack will +schedule a work item with delay defined by the +:kconfig:option:`CONFIG_BT_MESH_STORE_TIMEOUT` option. When the work item is +running, the stack will call the :c:member:`bt_mesh_model_cb.pending_store` +callback for every model that has requested storing of data. The model can +then call :c:func:`bt_mesh_model_data_store` to store the data. + +If :kconfig:option:`CONFIG_BT_MESH_SETTINGS_WORKQ` is enabled, the +:c:member:`bt_mesh_model_cb.pending_store` callback is called from a dedicated +thread. This allows the stack to process other incoming and outgoing messages +while model data is being stored. It is recommended to use this option and the +:c:func:`bt_mesh_model_data_store_schedule` function when large amount of data +needs to be stored. + API reference ************* diff --git a/include/zephyr/bluetooth/mesh/access.h b/include/zephyr/bluetooth/mesh/access.h index 80e3948f1eb..75fa81c9089 100644 --- a/include/zephyr/bluetooth/mesh/access.h +++ b/include/zephyr/bluetooth/mesh/access.h @@ -662,6 +662,16 @@ struct bt_mesh_model_cb { * @param model Model this callback belongs to. */ void (*const reset)(struct bt_mesh_model *model); + + /** @brief Callback used to store pending model's user data. + * + * Triggered by @ref bt_mesh_model_data_store_schedule. + * + * To store the user data, call @ref bt_mesh_model_data_store. + * + * @param model Model this callback belongs to. + */ + void (*const pending_store)(struct bt_mesh_model *model); }; /** Vendor model ID */ @@ -842,6 +852,19 @@ int bt_mesh_model_data_store(struct bt_mesh_model *mod, bool vnd, const char *name, const void *data, size_t data_len); +/** @brief Schedule the model's user data store in persistent storage. + * + * This function triggers the @ref bt_mesh_model_cb.pending_store callback + * for the corresponding model after delay defined by + * @kconfig{CONFIG_BT_MESH_STORE_TIMEOUT}. + * + * The delay is global for all models. Once scheduled, the callback can + * not be re-scheduled until previous schedule completes. + * + * @param mod Mesh model. + */ +void bt_mesh_model_data_store_schedule(struct bt_mesh_model *mod); + /** @brief Let a model extend another. * * Mesh models may be extended to reuse their functionality, forming a more diff --git a/subsys/bluetooth/mesh/access.c b/subsys/bluetooth/mesh/access.c index d009837c167..d8ba60cbb46 100644 --- a/subsys/bluetooth/mesh/access.c +++ b/subsys/bluetooth/mesh/access.c @@ -1850,6 +1850,11 @@ static void store_pending_mod(struct bt_mesh_model *mod, mod->flags &= ~BT_MESH_MOD_PUB_PENDING; store_pending_mod_pub(mod, vnd); } + + if (mod->flags & BT_MESH_MOD_DATA_PENDING) { + mod->flags &= ~BT_MESH_MOD_DATA_PENDING; + mod->cb->pending_store(mod); + } } void bt_mesh_model_pending_store(void) @@ -2134,3 +2139,9 @@ void bt_mesh_model_settings_commit(void) { bt_mesh_model_foreach(commit_mod, NULL); } + +void bt_mesh_model_data_store_schedule(struct bt_mesh_model *mod) +{ + mod->flags |= BT_MESH_MOD_DATA_PENDING; + bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_MOD_PENDING); +} diff --git a/subsys/bluetooth/mesh/access.h b/subsys/bluetooth/mesh/access.h index 86d7a212344..43ec1839160 100644 --- a/subsys/bluetooth/mesh/access.h +++ b/subsys/bluetooth/mesh/access.h @@ -17,6 +17,7 @@ enum { BT_MESH_MOD_PUB_PENDING = BIT(2), BT_MESH_MOD_EXTENDED = BIT(3), BT_MESH_MOD_DEVKEY_ONLY = BIT(4), + BT_MESH_MOD_DATA_PENDING = BIT(5), }; void bt_mesh_elem_register(struct bt_mesh_elem *elem, uint8_t count);