Skip to content
Draft

Bt taskq #97288

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions samples/bluetooth/peripheral_identity/prj.conf
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ CONFIG_BT_PRIVACY=y
CONFIG_BT_DEVICE_NAME="Zephyr Peripheral"
CONFIG_BT_GAP_AUTO_UPDATE_CONN_PARAMS=n

CONFIG_BT_MAX_CONN=62
CONFIG_BT_ID_MAX=62
CONFIG_BT_MAX_CONN=61
CONFIG_BT_ID_MAX=61

# CONFIG_BT_SMP=y
# CONFIG_BT_MAX_PAIRED=62
1 change: 1 addition & 0 deletions subsys/bluetooth/host/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ zephyr_library_sources_ifdef(CONFIG_BT_MONITOR monitor.c)
zephyr_library_sources_ifdef(CONFIG_BT_SETTINGS settings.c)
zephyr_library_sources_ifdef(CONFIG_BT_HOST_CCM aes_ccm.c)
zephyr_library_sources_ifdef(CONFIG_BT_LONG_WQ long_wq.c)
zephyr_library_sources(bt_taskq.c)

if(CONFIG_BT_HCI_HOST)
zephyr_library_sources(
Expand Down
1 change: 1 addition & 0 deletions subsys/bluetooth/host/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ menu "Bluetooth Host"

if BT_HCI_HOST

rsource "Kconfig.bt_taskq"
rsource "../mesh/Kconfig"
rsource "../audio/Kconfig"

Expand Down
58 changes: 58 additions & 0 deletions subsys/bluetooth/host/Kconfig.bt_taskq
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# bt_taskq configuration options

# Copyright (c) 2025 Nordic Semiconductor
# SPDX-License-Identifier: Apache-2.0

choice BT_TASKQ_CONTEXT
prompt "bt_taskq thread selection"
# nRF51 is too small to have a dedicated thread
default BT_TASKQ_SYSTEM_WORKQUEUE if SOC_SERIES_NRF51X
default BT_TASKQ_DEDICATED
help
Selects in which context the bt_taskq runs.

bt_taskq work is quick and non-blocking and must not be
blocked by other work. It should be on a work queue that
is exclusively for non-blocking work.

config BT_TASKQ_DEDICATED
bool "Dedicated thread"
help
When this option is selected, the bt_taskq runs on a
dedicated thread. This is the default and safe option.

config BT_TASKQ_SYSTEM_WORKQUEUE
bool "System workqueue"
help
When this option is selected, the bt_taskq is the system
workqueue.

WARNING: This is safe only if there is no blocking work on
the system workqueue.

This is currently NEVER SAFE to use as the Host itself
puts blocking work on the system workqueue. For now, this
option exists for users that need to free up RAM by not
having an extra thread and are willing to accept the risk
of deadlocks. When using this option, it is advised to
have a watchdog to recover from deadlocks. Risk of
deadlocks can be mitigated by being mindful of buffers and
whole-system analysis.

endchoice

config BT_TASKQ_STACK_SIZE_WITH_PROMPT
bool "bt_taskq thread stack size override"
depends on BT_TASKQ_DEDICATED

config BT_TASKQ_STACK_SIZE
int
default 1024
prompt "bt_taskq thread stack size" if BT_TASKQ_STACK_SIZE_WITH_PROMPT

config BT_TASKQ_THREAD_PRIO
# Hidden option

Check warning on line 54 in subsys/bluetooth/host/Kconfig.bt_taskq

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

LEADING_SPACE

subsys/bluetooth/host/Kconfig.bt_taskq:54 please, no spaces at the start of a line
int
# -1 is the least urgent cooperative priority.
# tx_processor() needs a cooperative thread for now.
default -1
18 changes: 17 additions & 1 deletion subsys/bluetooth/host/att.c
Original file line number Diff line number Diff line change
Expand Up @@ -248,8 +248,13 @@
}
#endif /* CONFIG_BT_ATT_ERR_TO_STR */

static void att_tx_destroy(struct net_buf *buf)
static void att_tx_destroy_work_handler(struct k_work *work);
static K_WORK_DEFINE(att_tx_destroy_work, att_tx_destroy_work_handler);
static sys_slist_t tx_destroy_queue;

static void att_tx_destroy_work_handler(struct k_work *work)
{
struct net_buf *buf = net_buf_slist_get(&tx_destroy_queue);
struct bt_att_tx_meta_data *p_meta = att_get_tx_meta_data(buf);
struct bt_att_tx_meta_data meta;

Expand Down Expand Up @@ -278,6 +283,17 @@
if (meta.opcode != 0) {
att_on_sent_cb(&meta);
}

if (!sys_slist_is_empty(&tx_destroy_queue)) {
k_work_submit_to_queue(bt_workq_chosen, &att_tx_destroy_work);
}
}

static void att_tx_destroy(struct net_buf *buf)
{
/* We need to invoke `att_on_sent_cb` which may block. Defer to bt_workq. */
net_buf_slist_put(&tx_destroy_queue, buf);
k_work_submit_to_queue(bt_workq_chosen, &att_tx_destroy_work); /* att_tx_destroy_work_handler */

Check warning on line 296 in subsys/bluetooth/host/att.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

LONG_LINE_COMMENT

subsys/bluetooth/host/att.c:296 line length of 104 exceeds 100 columns
}

NET_BUF_POOL_DEFINE(att_pool, CONFIG_BT_ATT_TX_COUNT,
Expand Down
42 changes: 42 additions & 0 deletions subsys/bluetooth/host/bt_taskq.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/* bt_taskq.c - Workqueue for quick non-blocking Bluetooth tasks */

/*
* Copyright (c) 2025 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/autoconf.h>
#include <zephyr/init.h>
#include <zephyr/kernel.h>
#include <zephyr/kernel/thread_stack.h>
#include <zephyr/sys/util_macro.h>
#include <zephyr/toolchain.h>

static K_THREAD_STACK_DEFINE(bt_taskq_stack, CONFIG_BT_TASKQ_STACK_SIZE);
static struct k_work_q bt_taskq;

__maybe_unused static int bt_taskq_init(void)
{
struct k_work_queue_config cfg = {};

if (IS_ENABLED(CONFIG_THREAD_NAME)) {
cfg.name = "bt_taskq";
}

k_work_queue_start(&bt_taskq, bt_taskq_stack, K_THREAD_STACK_SIZEOF(bt_taskq_stack),
CONFIG_BT_TASKQ_THREAD_PRIO, &cfg);

return 0;
}

#if defined(CONFIG_BT_TASKQ_DEDICATED)
/* The init priority is set to POST_KERNEL 999, the last level
* before APPLICATION.
*/
SYS_INIT(bt_taskq_init, POST_KERNEL, 999);
#endif /* CONFIG_BT_TASKQ_DEDICATED */

/* Exports */
struct k_work_q *const bt_taskq_chosen =
COND_CODE_1(CONFIG_BT_TASKQ_DEDICATED, (&bt_taskq), (&k_sys_work_q));
40 changes: 40 additions & 0 deletions subsys/bluetooth/host/bt_taskq.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/* bt_taskq.h - Workqueue for quick non-blocking Bluetooth tasks */

/*
* Copyright (c) 2025 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/kernel.h>

/**
* @brief Bluetooth task workqueue
*
* bt_taskq is a workqueue intended for quick non-blocking work
* items ("tasks") in the Bluetooth subsystem. This workqueue
* must always exist and is not controlled by any Kconfig
* option.
*
* Blocking means "waiting for something while running". A
* task is NOT allowed to block. If a task need to "wait", it
* should instead return immediately and schedule itself to run
* later.
*
* Work items submitted to this queue should be:
* - Quick to execute (non-blocking).
* - Not perform long-running operations.
* - Not block.
*
* @warning Non-blocking violation pitfalls:
* - net_buf_unref() on a foreign buffer could have a blocking
* destroy callback
* - Any user-defined callback might be blocking
* - Avoid any operations that could sleep or block the thread
*
* Use bt_long_wq for long-running or potentially blocking
* operations instead.
*
* Available in APPLICATION initialization level and later.
*/
extern struct k_work_q *const bt_taskq_chosen;
16 changes: 9 additions & 7 deletions subsys/bluetooth/host/hci_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@

#include "addr_internal.h"
#include "adv.h"
#include "bt_taskq.h"
#include "common/hci_common_internal.h"
#include "common/bt_str.h"
#include "common/rpa.h"
Expand Down Expand Up @@ -124,6 +125,9 @@ static struct k_work_q bt_workq;
static K_KERNEL_STACK_DEFINE(rx_thread_stack, CONFIG_BT_RX_STACK_SIZE);
#endif /* CONFIG_BT_RECV_WORKQ_BT */

struct k_work_q *const bt_workq_chosen =
COND_CODE_1(CONFIG_BT_RECV_WORKQ_BT, (&bt_workq), (&k_sys_work_q));

static void init_work(struct k_work *work);

struct bt_dev bt_dev = {
Expand Down Expand Up @@ -475,7 +479,7 @@ int bt_hci_cmd_send_sync(uint16_t opcode, struct net_buf *buf,
/* Since the commands are now processed in the syswq, we cannot suspend
* and wait. We have to send the command from the current context.
*/
if (k_current_get() == &k_sys_work_q.thread) {
if (!IS_ENABLED(CONFIG_BT_TASKQ_DEDICATED) && k_current_get() == &k_sys_work_q.thread) {
/* drain the command queue until we get to send the command of interest. */
struct net_buf *cmd = NULL;

Expand Down Expand Up @@ -4350,13 +4354,11 @@ static void hci_event_prio(struct net_buf *buf)

static void rx_queue_put(struct net_buf *buf)
{
int err;

net_buf_slist_put(&bt_dev.rx_queue, buf);

#if defined(CONFIG_BT_RECV_WORKQ_SYS)
const int err = k_work_submit(&rx_work);
#elif defined(CONFIG_BT_RECV_WORKQ_BT)
const int err = k_work_submit_to_queue(&bt_workq, &rx_work);
#endif /* CONFIG_BT_RECV_WORKQ_SYS */
err = k_work_submit_to_queue(bt_workq_chosen, &rx_work);
if (err < 0) {
LOG_ERR("Could not submit rx_work: %d", err);
}
Expand Down Expand Up @@ -5036,5 +5038,5 @@ static K_WORK_DEFINE(tx_work, tx_processor);
void bt_tx_irq_raise(void)
{
LOG_DBG("kick TX");
k_work_submit(&tx_work);
k_work_submit_to_queue(bt_taskq_chosen, &tx_work);
}
1 change: 1 addition & 0 deletions subsys/bluetooth/host/hci_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,7 @@ struct bt_dev {
#endif
};

extern struct k_work_q *const bt_workq_chosen;
extern struct bt_dev bt_dev;
extern const struct bt_conn_auth_cb *bt_auth;
extern sys_slist_t bt_auth_info_cbs;
Expand Down
Loading