zephyr/subsys/bluetooth/mesh/adv.c
Andrew Boie efc5fe07a2 kernel: overhaul unused stack measurement
The existing stack_analyze APIs had some problems:

1. Not properly namespaced
2. Accepted the stack object as a parameter, yet the stack object
   does not contain the necessary information to get the associated
   buffer region, the thread object is needed for this
3. Caused a crash on certain platforms that do not allow inspection
   of unused stack space for the currently running thread
4. No user mode access
5. Separately passed in thread name

We deprecate these functions and add a new API
k_thread_stack_space_get() which addresses all of these issues.

A helper API log_stack_usage() also added which resembles
STACK_ANALYZE() in functionality.

Fixes: #17852

Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
2020-02-08 10:02:35 +02:00

349 lines
7.7 KiB
C

/* Bluetooth Mesh */
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
* Copyright (c) 2017 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr.h>
#include <debug/stack.h>
#include <sys/util.h>
#include <net/buf.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/conn.h>
#include <bluetooth/mesh.h>
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_MESH_DEBUG_ADV)
#define LOG_MODULE_NAME bt_mesh_adv
#include "common/log.h"
#include "host/hci_core.h"
#include "adv.h"
#include "net.h"
#include "foundation.h"
#include "beacon.h"
#include "prov.h"
#include "proxy.h"
/* Convert from ms to 0.625ms units */
#define ADV_SCAN_UNIT(_ms) ((_ms) * 8 / 5)
/* Window and Interval are equal for continuous scanning */
#define MESH_SCAN_INTERVAL_MS 30
#define MESH_SCAN_WINDOW_MS 30
#define MESH_SCAN_INTERVAL ADV_SCAN_UNIT(MESH_SCAN_INTERVAL_MS)
#define MESH_SCAN_WINDOW ADV_SCAN_UNIT(MESH_SCAN_WINDOW_MS)
/* Pre-5.0 controllers enforce a minimum interval of 100ms
* whereas 5.0+ controllers can go down to 20ms.
*/
#define ADV_INT_DEFAULT_MS 100
#define ADV_INT_FAST_MS 20
#if defined(CONFIG_BT_HOST_CRYPTO)
#define ADV_STACK_SIZE 1024
#else
#define ADV_STACK_SIZE 768
#endif
static K_FIFO_DEFINE(adv_queue);
static struct k_thread adv_thread_data;
static K_THREAD_STACK_DEFINE(adv_thread_stack, ADV_STACK_SIZE);
static const u8_t adv_type[] = {
[BT_MESH_ADV_PROV] = BT_DATA_MESH_PROV,
[BT_MESH_ADV_DATA] = BT_DATA_MESH_MESSAGE,
[BT_MESH_ADV_BEACON] = BT_DATA_MESH_BEACON,
[BT_MESH_ADV_URI] = BT_DATA_URI,
};
NET_BUF_POOL_DEFINE(adv_buf_pool, CONFIG_BT_MESH_ADV_BUF_COUNT,
BT_MESH_ADV_DATA_SIZE, BT_MESH_ADV_USER_DATA_SIZE, NULL);
static struct bt_mesh_adv adv_pool[CONFIG_BT_MESH_ADV_BUF_COUNT];
static struct bt_mesh_adv *adv_alloc(int id)
{
return &adv_pool[id];
}
static inline void adv_send_start(u16_t duration, int err,
const struct bt_mesh_send_cb *cb,
void *cb_data)
{
if (cb && cb->start) {
cb->start(duration, err, cb_data);
}
}
static inline void adv_send_end(int err, const struct bt_mesh_send_cb *cb,
void *cb_data)
{
if (cb && cb->end) {
cb->end(err, cb_data);
}
}
static inline void adv_send(struct net_buf *buf)
{
const s32_t adv_int_min = ((bt_dev.hci_version >= BT_HCI_VERSION_5_0) ?
ADV_INT_FAST_MS : ADV_INT_DEFAULT_MS);
const struct bt_mesh_send_cb *cb = BT_MESH_ADV(buf)->cb;
void *cb_data = BT_MESH_ADV(buf)->cb_data;
struct bt_le_adv_param param;
u16_t duration, adv_int;
struct bt_data ad;
int err;
adv_int = MAX(adv_int_min,
BT_MESH_TRANSMIT_INT(BT_MESH_ADV(buf)->xmit));
duration = (MESH_SCAN_WINDOW_MS +
((BT_MESH_TRANSMIT_COUNT(BT_MESH_ADV(buf)->xmit) + 1) *
(adv_int + 10)));
BT_DBG("type %u len %u: %s", BT_MESH_ADV(buf)->type,
buf->len, bt_hex(buf->data, buf->len));
BT_DBG("count %u interval %ums duration %ums",
BT_MESH_TRANSMIT_COUNT(BT_MESH_ADV(buf)->xmit) + 1, adv_int,
duration);
ad.type = adv_type[BT_MESH_ADV(buf)->type];
ad.data_len = buf->len;
ad.data = buf->data;
if (IS_ENABLED(CONFIG_BT_MESH_DEBUG_USE_ID_ADDR)) {
param.options = BT_LE_ADV_OPT_USE_IDENTITY;
} else {
param.options = 0U;
}
param.id = BT_ID_DEFAULT;
param.interval_min = ADV_SCAN_UNIT(adv_int);
param.interval_max = param.interval_min;
err = bt_le_adv_start(&param, &ad, 1, NULL, 0);
net_buf_unref(buf);
adv_send_start(duration, err, cb, cb_data);
if (err) {
BT_ERR("Advertising failed: err %d", err);
return;
}
BT_DBG("Advertising started. Sleeping %u ms", duration);
k_sleep(K_MSEC(duration));
err = bt_le_adv_stop();
adv_send_end(err, cb, cb_data);
if (err) {
BT_ERR("Stopping advertising failed: err %d", err);
return;
}
BT_DBG("Advertising stopped");
}
static void adv_stack_dump(const struct k_thread *thread, void *user_data)
{
ARG_UNUSED(user_data);
log_stack_usage(thread);
}
static void adv_thread(void *p1, void *p2, void *p3)
{
BT_DBG("started");
while (1) {
struct net_buf *buf;
if (IS_ENABLED(CONFIG_BT_MESH_PROXY)) {
buf = net_buf_get(&adv_queue, K_NO_WAIT);
while (!buf) {
s32_t timeout;
timeout = bt_mesh_proxy_adv_start();
BT_DBG("Proxy Advertising up to %d ms",
timeout);
buf = net_buf_get(&adv_queue, timeout);
bt_mesh_proxy_adv_stop();
}
} else {
buf = net_buf_get(&adv_queue, K_FOREVER);
}
if (!buf) {
continue;
}
/* busy == 0 means this was canceled */
if (BT_MESH_ADV(buf)->busy) {
BT_MESH_ADV(buf)->busy = 0U;
adv_send(buf);
} else {
net_buf_unref(buf);
}
log_stack_usage(&adv_thread_data);
k_thread_foreach(adv_stack_dump, NULL);
/* Give other threads a chance to run */
k_yield();
}
}
void bt_mesh_adv_update(void)
{
BT_DBG("");
k_fifo_cancel_wait(&adv_queue);
}
struct net_buf *bt_mesh_adv_create_from_pool(struct net_buf_pool *pool,
bt_mesh_adv_alloc_t get_id,
enum bt_mesh_adv_type type,
u8_t xmit, s32_t timeout)
{
struct bt_mesh_adv *adv;
struct net_buf *buf;
if (atomic_test_bit(bt_mesh.flags, BT_MESH_SUSPENDED)) {
BT_WARN("Refusing to allocate buffer while suspended");
return NULL;
}
buf = net_buf_alloc(pool, timeout);
if (!buf) {
return NULL;
}
adv = get_id(net_buf_id(buf));
BT_MESH_ADV(buf) = adv;
(void)memset(adv, 0, sizeof(*adv));
adv->type = type;
adv->xmit = xmit;
return buf;
}
struct net_buf *bt_mesh_adv_create(enum bt_mesh_adv_type type, u8_t xmit,
s32_t timeout)
{
return bt_mesh_adv_create_from_pool(&adv_buf_pool, adv_alloc, type,
xmit, timeout);
}
void bt_mesh_adv_send(struct net_buf *buf, const struct bt_mesh_send_cb *cb,
void *cb_data)
{
BT_DBG("type 0x%02x len %u: %s", BT_MESH_ADV(buf)->type, buf->len,
bt_hex(buf->data, buf->len));
BT_MESH_ADV(buf)->cb = cb;
BT_MESH_ADV(buf)->cb_data = cb_data;
BT_MESH_ADV(buf)->busy = 1U;
net_buf_put(&adv_queue, net_buf_ref(buf));
}
static void bt_mesh_scan_cb(const bt_addr_le_t *addr, s8_t rssi,
u8_t adv_type, struct net_buf_simple *buf)
{
if (adv_type != BT_LE_ADV_NONCONN_IND) {
return;
}
BT_DBG("len %u: %s", buf->len, bt_hex(buf->data, buf->len));
while (buf->len > 1) {
struct net_buf_simple_state state;
u8_t len, type;
len = net_buf_simple_pull_u8(buf);
/* Check for early termination */
if (len == 0U) {
return;
}
if (len > buf->len) {
BT_WARN("AD malformed");
return;
}
net_buf_simple_save(buf, &state);
type = net_buf_simple_pull_u8(buf);
buf->len = len - 1;
switch (type) {
case BT_DATA_MESH_MESSAGE:
bt_mesh_net_recv(buf, rssi, BT_MESH_NET_IF_ADV);
break;
#if defined(CONFIG_BT_MESH_PB_ADV)
case BT_DATA_MESH_PROV:
bt_mesh_pb_adv_recv(buf);
break;
#endif
case BT_DATA_MESH_BEACON:
bt_mesh_beacon_recv(buf);
break;
default:
break;
}
net_buf_simple_restore(buf, &state);
net_buf_simple_pull(buf, len);
}
}
void bt_mesh_adv_init(void)
{
k_thread_create(&adv_thread_data, adv_thread_stack,
K_THREAD_STACK_SIZEOF(adv_thread_stack), adv_thread,
NULL, NULL, NULL, K_PRIO_COOP(7), 0, K_NO_WAIT);
k_thread_name_set(&adv_thread_data, "BT Mesh adv");
}
int bt_mesh_scan_enable(void)
{
struct bt_le_scan_param scan_param = {
.type = BT_HCI_LE_SCAN_PASSIVE,
.filter_dup = BT_HCI_LE_SCAN_FILTER_DUP_DISABLE,
.interval = MESH_SCAN_INTERVAL,
.window = MESH_SCAN_WINDOW };
int err;
BT_DBG("");
err = bt_le_scan_start(&scan_param, bt_mesh_scan_cb);
if (err && err != -EALREADY) {
BT_ERR("starting scan failed (err %d)", err);
return err;
}
return 0;
}
int bt_mesh_scan_disable(void)
{
int err;
BT_DBG("");
err = bt_le_scan_stop();
if (err && err != -EALREADY) {
BT_ERR("stopping scan failed (err %d)", err);
return err;
}
return 0;
}