Bluetooth: Mesh: access tx msg randomizer

Commit adds implementation of the specification
recommendations regarding randomization of
responses on the access layer.
3.7.3.1 Transmitting an Access messages

Signed-off-by: Aleksandr Khromykh <aleksandr.khromykh@nordicsemi.no>
This commit is contained in:
Aleksandr Khromykh 2023-11-20 16:43:25 +01:00 committed by Carles Cufí
commit d175ac0572
12 changed files with 460 additions and 0 deletions

View file

@ -122,6 +122,8 @@ zephyr_library_sources_ifdef(CONFIG_BT_MESH_SOLICITATION solicitation.c)
zephyr_library_sources_ifdef(CONFIG_BT_MESH_STATISTIC statistic.c)
zephyr_library_sources_ifdef(CONFIG_BT_MESH_ACCESS_DELAYABLE_MSG delayable_msg.c)
if (CONFIG_BT_MESH_USES_TINYCRYPT)
zephyr_library_sources(crypto_tc.c)
else()

View file

@ -628,6 +628,41 @@ config BT_MESH_LABEL_NO_RECOVER
unprovisioned before upgrading to the version with this option). The option is marked as
deprecated to remove the recovery code eventually.
menuconfig BT_MESH_ACCESS_DELAYABLE_MSG
bool "Access layer tx delayable message"
help
Enable following of the message transmitting recommendations, the Access layer
specification. The recommendations are optional.
However, they are strictly recommended if the device participates in the network with
intensive communication where the device receives a lot of requests that require responses.
if BT_MESH_ACCESS_DELAYABLE_MSG
config BT_MESH_ACCESS_DELAYABLE_MSG_COUNT
int "Number of simultaneously delayed messages"
default 4
help
The maximum number of messages the Access layer can manage to delay
at the same time. The number of messages can be handled only if the Access layer
has a sufficient amount of memory to store the model payload for them.
config BT_MESH_ACCESS_DELAYABLE_MSG_CHUNK_SIZE
int "Maximum delayable message storage chunk"
default 20
help
Size of memory that Access layer uses to split model message to. It allocates
a sufficient number of these chunks from the pool to store the full model payload.
config BT_MESH_ACCESS_DELAYABLE_MSG_CHUNK_COUNT
int "Maximum number of available chunks"
default 20
help
The maximum number of available chunks the Access layer allocates to store model payload.
It is recommended to keep chunk size equal to the reasonable small value to prevent
unnecessary memory wasting.
endif # BT_MESH_ACCESS_DELAYABLE_MSG
endmenu # Access layer
menu "Models"

View file

@ -27,6 +27,7 @@
#include "op_agg.h"
#include "settings.h"
#include "va.h"
#include "delayable_msg.h"
#define LOG_LEVEL CONFIG_BT_MESH_ACCESS_LOG_LEVEL
#include <zephyr/logging/log.h>
@ -1518,6 +1519,13 @@ int bt_mesh_model_send(const struct bt_mesh_model *model, struct bt_mesh_msg_ctx
return -EINVAL;
}
#if defined CONFIG_BT_MESH_ACCESS_DELAYABLE_MSG
if (ctx->rnd_delay) {
return bt_mesh_delayable_msg_manage(ctx, msg, bt_mesh_model_elem(model)->rt->addr,
cb, cb_data);
}
#endif
return bt_mesh_access_send(ctx, msg, bt_mesh_model_elem(model)->rt->addr, cb, cb_data);
}
@ -2613,3 +2621,24 @@ uint8_t bt_mesh_comp_parse_page(struct net_buf_simple *buf)
return page;
}
void bt_mesh_access_init(void)
{
#if defined CONFIG_BT_MESH_ACCESS_DELAYABLE_MSG
bt_mesh_delayable_msg_init();
#endif
}
void bt_mesh_access_suspend(void)
{
#if defined CONFIG_BT_MESH_ACCESS_DELAYABLE_MSG
bt_mesh_delayable_msg_stop();
#endif
}
void bt_mesh_access_reset(void)
{
#if defined CONFIG_BT_MESH_ACCESS_DELAYABLE_MSG
bt_mesh_delayable_msg_stop();
#endif
}

View file

@ -96,8 +96,25 @@ void bt_mesh_msg_cb_set(void (*cb)(uint32_t opcode, struct bt_mesh_msg_ctx *ctx,
*
* @param ctx The Bluetooth Mesh message context.
* @param buf The message payload.
* @param src_addr The source address of model
* @param cb Callback function.
* @param cb_data Callback data.
*
* @return 0 on success or negative error code on failure.
*/
int bt_mesh_access_send(struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf, uint16_t src_addr,
const struct bt_mesh_send_cb *cb, void *cb_data);
/** @brief Initialize the Access layer.
*
* Initialize the delayable message mechanism if it has been enabled.
*/
void bt_mesh_access_init(void);
/** @brief Suspend the Access layer.
*/
void bt_mesh_access_suspend(void);
/** @brief Reset the Access layer.
*/
void bt_mesh_access_reset(void);

View file

@ -0,0 +1,314 @@
/*
* Copyright (c) 2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <errno.h>
#include <stdlib.h>
#include <zephyr/sys/slist.h>
#include <zephyr/net/buf.h>
#include <zephyr/bluetooth/mesh.h>
#include "msg.h"
#include "access.h"
#include "net.h"
#define LOG_LEVEL CONFIG_BT_MESH_ACCESS_LOG_LEVEL
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(bt_mesh_delayable_msg);
static void delayable_msg_handler(struct k_work *w);
static bool push_msg_from_delayable_msgs(void);
static struct delayable_msg_chunk {
sys_snode_t node;
uint8_t data[CONFIG_BT_MESH_ACCESS_DELAYABLE_MSG_CHUNK_SIZE];
} delayable_msg_chunks[CONFIG_BT_MESH_ACCESS_DELAYABLE_MSG_CHUNK_COUNT];
static struct delayable_msg_ctx {
sys_snode_t node;
sys_slist_t chunks;
struct bt_mesh_msg_ctx ctx;
uint16_t src_addr;
const struct bt_mesh_send_cb *cb;
void *cb_data;
uint32_t fired_time;
uint16_t len;
} delayable_msgs_ctx[CONFIG_BT_MESH_ACCESS_DELAYABLE_MSG_COUNT];
static struct {
sys_slist_t busy_ctx;
sys_slist_t free_ctx;
sys_slist_t free_chunks;
struct k_work_delayable random_delay;
} access_delayable_msg = {.random_delay = Z_WORK_DELAYABLE_INITIALIZER(delayable_msg_handler)};
static void put_ctx_to_busy_list(struct delayable_msg_ctx *ctx)
{
struct delayable_msg_ctx *curr_ctx;
sys_slist_t *list = &access_delayable_msg.busy_ctx;
sys_snode_t *head = sys_slist_peek_head(list);
sys_snode_t *curr = head;
sys_snode_t *prev = curr;
if (!head) {
sys_slist_append(list, &ctx->node);
return;
}
do {
curr_ctx = CONTAINER_OF(curr, struct delayable_msg_ctx, node);
if (ctx->fired_time < curr_ctx->fired_time) {
if (curr == head) {
sys_slist_prepend(list, &ctx->node);
} else {
sys_slist_insert(list, prev, &ctx->node);
}
return;
}
prev = curr;
} while ((curr = sys_slist_peek_next(curr)));
sys_slist_append(list, &ctx->node);
}
static struct delayable_msg_ctx *peek_pending_msg(void)
{
struct delayable_msg_ctx *pending_msg = NULL;
sys_snode_t *node = sys_slist_peek_head(&access_delayable_msg.busy_ctx);
if (node) {
pending_msg = CONTAINER_OF(node, struct delayable_msg_ctx, node);
}
return pending_msg;
}
static void reschedule_delayable_msg(struct delayable_msg_ctx *msg)
{
uint32_t curr_time;
k_timeout_t delay = K_NO_WAIT;
struct delayable_msg_ctx *pending_msg;
if (msg) {
put_ctx_to_busy_list(msg);
}
pending_msg = peek_pending_msg();
if (!pending_msg) {
return;
}
curr_time = k_uptime_get_32();
if (curr_time < pending_msg->fired_time) {
delay = K_MSEC(pending_msg->fired_time - curr_time);
}
k_work_reschedule(&access_delayable_msg.random_delay, delay);
}
static int allocate_delayable_msg_chunks(struct delayable_msg_ctx *msg, int number)
{
sys_snode_t *node;
for (int i = 0; i < number; i++) {
node = sys_slist_get(&access_delayable_msg.free_chunks);
if (!node) {
LOG_WRN("Unable allocate %u chunks, allocated %u", number, i);
return i;
}
sys_slist_append(&msg->chunks, node);
}
return number;
}
static void release_delayable_msg_chunks(struct delayable_msg_ctx *msg)
{
sys_snode_t *node;
while ((node = sys_slist_get(&msg->chunks))) {
sys_slist_append(&access_delayable_msg.free_chunks, node);
}
}
static struct delayable_msg_ctx *allocate_delayable_msg_ctx(void)
{
struct delayable_msg_ctx *msg;
sys_snode_t *node;
if (sys_slist_is_empty(&access_delayable_msg.free_ctx)) {
LOG_WRN("Purge pending delayable message.");
if (!push_msg_from_delayable_msgs()) {
return NULL;
}
}
node = sys_slist_get(&access_delayable_msg.free_ctx);
msg = CONTAINER_OF(node, struct delayable_msg_ctx, node);
sys_slist_init(&msg->chunks);
return msg;
}
static void release_delayable_msg_ctx(struct delayable_msg_ctx *ctx)
{
if (sys_slist_find_and_remove(&access_delayable_msg.busy_ctx, &ctx->node)) {
sys_slist_append(&access_delayable_msg.free_ctx, &ctx->node);
}
}
static bool push_msg_from_delayable_msgs(void)
{
sys_snode_t *node;
struct delayable_msg_chunk *chunk;
struct delayable_msg_ctx *msg = peek_pending_msg();
uint16_t len = msg->len;
int err;
if (!msg) {
return false;
}
NET_BUF_SIMPLE_DEFINE(buf, BT_MESH_TX_SDU_MAX);
SYS_SLIST_FOR_EACH_NODE(&msg->chunks, node) {
uint16_t tmp = MIN(CONFIG_BT_MESH_ACCESS_DELAYABLE_MSG_CHUNK_SIZE, len);
chunk = CONTAINER_OF(node, struct delayable_msg_chunk, node);
memcpy(net_buf_simple_add(&buf, tmp), chunk->data, tmp);
len -= tmp;
}
msg->ctx.rnd_delay = false;
err = bt_mesh_access_send(&msg->ctx, &buf, msg->src_addr, msg->cb, msg->cb_data);
msg->ctx.rnd_delay = true;
if (err == -EBUSY || err == -ENOBUFS) {
return false;
}
release_delayable_msg_chunks(msg);
release_delayable_msg_ctx(msg);
if (err && msg->cb && msg->cb->start) {
msg->cb->start(0, err, msg->cb_data);
}
return true;
}
static void delayable_msg_handler(struct k_work *w)
{
if (!push_msg_from_delayable_msgs()) {
sys_snode_t *node = sys_slist_get(&access_delayable_msg.busy_ctx);
struct delayable_msg_ctx *pending_msg =
CONTAINER_OF(node, struct delayable_msg_ctx, node);
pending_msg->fired_time += 10;
reschedule_delayable_msg(pending_msg);
} else {
reschedule_delayable_msg(NULL);
}
}
int bt_mesh_delayable_msg_manage(struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf,
uint16_t src_addr, const struct bt_mesh_send_cb *cb, void *cb_data)
{
sys_snode_t *node;
struct delayable_msg_ctx *msg;
uint16_t random_delay;
int total_number = DIV_ROUND_UP(buf->size, CONFIG_BT_MESH_ACCESS_DELAYABLE_MSG_CHUNK_SIZE);
int allocated_number = 0;
uint16_t len = buf->len;
if (atomic_test_bit(bt_mesh.flags, BT_MESH_SUSPENDED)) {
LOG_WRN("Refusing to allocate message context while suspended");
return -ENODEV;
}
if (total_number > CONFIG_BT_MESH_ACCESS_DELAYABLE_MSG_CHUNK_COUNT) {
return -EINVAL;
}
msg = allocate_delayable_msg_ctx();
if (!msg) {
LOG_WRN("No available free delayable message context.");
return -ENOMEM;
}
do {
allocated_number +=
allocate_delayable_msg_chunks(msg, total_number - allocated_number);
if (total_number > allocated_number) {
LOG_DBG("Unable allocate %u chunks, allocated %u", total_number,
allocated_number);
if (!push_msg_from_delayable_msgs()) {
LOG_WRN("No available chunk memory.");
release_delayable_msg_chunks(msg);
release_delayable_msg_ctx(msg);
return -ENOMEM;
}
}
} while (total_number > allocated_number);
SYS_SLIST_FOR_EACH_NODE(&msg->chunks, node) {
uint16_t tmp = MIN(CONFIG_BT_MESH_ACCESS_DELAYABLE_MSG_CHUNK_SIZE, buf->len);
struct delayable_msg_chunk *chunk =
CONTAINER_OF(node, struct delayable_msg_chunk, node);
memcpy(chunk->data, net_buf_simple_pull_mem(buf, tmp), tmp);
}
bt_rand(&random_delay, sizeof(uint16_t));
random_delay = 20 + random_delay % (BT_MESH_ADDR_IS_UNICAST(ctx->recv_dst) ? 30 : 480);
msg->fired_time = k_uptime_get_32() + random_delay;
msg->ctx = *ctx;
msg->src_addr = src_addr;
msg->cb = cb;
msg->cb_data = cb_data;
msg->len = len;
reschedule_delayable_msg(msg);
return 0;
}
void bt_mesh_delayable_msg_init(void)
{
sys_slist_init(&access_delayable_msg.busy_ctx);
sys_slist_init(&access_delayable_msg.free_ctx);
sys_slist_init(&access_delayable_msg.free_chunks);
for (int i = 0; i < CONFIG_BT_MESH_ACCESS_DELAYABLE_MSG_COUNT; i++) {
sys_slist_append(&access_delayable_msg.free_ctx, &delayable_msgs_ctx[i].node);
}
for (int i = 0; i < CONFIG_BT_MESH_ACCESS_DELAYABLE_MSG_CHUNK_COUNT; i++) {
sys_slist_append(&access_delayable_msg.free_chunks, &delayable_msg_chunks[i].node);
}
}
void bt_mesh_delayable_msg_stop(void)
{
sys_snode_t *node;
struct delayable_msg_ctx *ctx;
k_work_cancel_delayable(&access_delayable_msg.random_delay);
while ((node = sys_slist_peek_head(&access_delayable_msg.busy_ctx))) {
ctx = CONTAINER_OF(node, struct delayable_msg_ctx, node);
release_delayable_msg_chunks(ctx);
release_delayable_msg_ctx(ctx);
if (ctx->cb && ctx->cb->start) {
ctx->cb->start(0, -ENODEV, ctx->cb_data);
}
}
}

View file

@ -0,0 +1,16 @@
/*
* Copyright (c) 2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_INCLUDE_BLUETOOTH_MESH_DELAYABLE_MSG_H__
#define ZEPHYR_INCLUDE_BLUETOOTH_MESH_DELAYABLE_MSG_H__
int bt_mesh_delayable_msg_manage(struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf,
uint16_t src_addr, const struct bt_mesh_send_cb *cb,
void *cb_data);
void bt_mesh_delayable_msg_init(void);
void bt_mesh_delayable_msg_stop(void);
#endif /* ZEPHYR_INCLUDE_BLUETOOTH_MESH_DELAYABLE_MSG_H__ */

View file

@ -359,6 +359,7 @@ void bt_mesh_reset(void)
*/
(void)k_work_cancel_delayable(&bt_mesh.ivu_timer);
bt_mesh_access_reset();
bt_mesh_model_reset();
bt_mesh_cfg_default_set();
bt_mesh_trans_reset();
@ -459,6 +460,8 @@ int bt_mesh_suspend(void)
bt_mesh_model_foreach(model_suspend, NULL);
bt_mesh_access_suspend();
err = bt_mesh_adv_disable();
if (err) {
atomic_clear_bit(bt_mesh.flags, BT_MESH_SUSPENDED);
@ -558,6 +561,7 @@ int bt_mesh_init(const struct bt_mesh_prov *prov,
bt_mesh_cfg_default_set();
bt_mesh_net_init();
bt_mesh_trans_init();
bt_mesh_access_init();
bt_mesh_hb_init();
bt_mesh_beacon_init();
bt_mesh_adv_init();