Bluetooth: Host: Add choice select whether BT RX

Change CONFIG_BT_RECV_IS_RX_THREAD into a
choice:CONFIG_BT_RECV_CONTEXT with the following options
(names can be discussed further of course):

    CONFIG_BT_RECV_BLOCKING
    CONFIG_BT_RECV_WORKQ_BT
    CONFIG_BT_RECV_WORKQ_SYS

This way users would be able to choose what to run most of
the BLE stack on, they wouldn't be forced to a single model.

We would default to CONFIG_BT_RECV_BLOCKING so that we wouldn't
need to change the system workqueue stack size by default, instead
asking users to do so if they select the CONFIG_BT_RECV_WORKQ_SYS option

Signed-off-by: Lingao Meng <menglingao@xiaomi.com>
This commit is contained in:
Lingao Meng 2022-03-11 11:12:59 +08:00 committed by Carles Cufí
commit 37e561f42e
12 changed files with 137 additions and 78 deletions

View file

@ -15,7 +15,6 @@ config BT_H4
bool "H:4 UART"
select UART_INTERRUPT_DRIVEN
select BT_UART
select BT_RECV_IS_RX_THREAD
depends on SERIAL
help
Bluetooth H:4 UART driver. Requires hardware flow control

View file

@ -340,7 +340,8 @@ static inline void read_payload(void)
reset_rx();
if (evt_flags & BT_HCI_EVT_FLAG_RECV_PRIO) {
if (IS_ENABLED(CONFIG_BT_RECV_BLOCKING) &&
(evt_flags & BT_HCI_EVT_FLAG_RECV_PRIO)) {
BT_DBG("Calling bt_recv_prio(%p)", buf);
bt_recv_prio(buf);
}

View file

@ -1216,7 +1216,8 @@ struct bt_gatt_exchange_params {
* @note Shall only be used once per connection.
*
* The Response comes in callback @p params->func. The callback is run from
* the BT RX thread. @p params must remain valid until start of callback.
* the context specified by 'config BT_RECV_CONTEXT'.
* @p params must remain valid until start of callback.
*
* This function will block while the ATT request queue is full, except when
* called from the BT RX thread, as this would cause a deadlock.
@ -1457,7 +1458,8 @@ struct bt_gatt_read_params {
* offset.
*
* The Response comes in callback @p params->func. The callback is run from
* the BT RX thread. @p params must remain valid until start of callback.
* the context specified by 'config BT_RECV_CONTEXT'.
* @p params must remain valid until start of callback.
*
* This function will block while the ATT request queue is full, except when
* called from the BT RX thread, as this would cause a deadlock.
@ -1504,7 +1506,8 @@ struct bt_gatt_write_params {
/** @brief Write Attribute Value by handle
*
* The Response comes in callback @p params->func. The callback is run from
* the BT RX thread. @p params must remain valid until start of callback.
* the context specified by 'config BT_RECV_CONTEXT'.
* @p params must remain valid until start of callback.
*
* This function will block while the ATT request queue is full, except when
* called from Bluetooth event context. When called from Bluetooth context,
@ -1693,7 +1696,8 @@ struct bt_gatt_subscribe_params {
* subscription was removed by this method.
*
* The Response comes in callback @p params->func. The callback is run from
* the BT RX thread. @p params must remain valid until start of callback.
* the context specified by 'config BT_RECV_CONTEXT'.
* @p params must remain valid until start of callback.
* The Notification callback @p params->notify is also called from the BT RX
* thread.
*

View file

@ -49,7 +49,7 @@ enum {
* Helper for the HCI driver to get HCI event flags that describes rules that.
* must be followed.
*
* When CONFIG_BT_RECV_IS_RX_THREAD is enabled the flags
* When @kconfig{CONFIG_BT_RECV_BLOCKING} is enabled the flags
* BT_HCI_EVT_FLAG_RECV and BT_HCI_EVT_FLAG_RECV_PRIO indicates if the event
* should be given to bt_recv or bt_recv_prio.
*
@ -85,7 +85,7 @@ static inline uint8_t bt_hci_evt_get_flags(uint8_t evt)
* host with data from the controller. The buffer needs to have its type
* set with the help of bt_buf_set_type() before calling this API.
*
* When CONFIG_BT_RECV_IS_RX_THREAD is defined then this API should not be used
* When @kconfig{CONFIG_BT_RECV_BLOCKING} is defined then this API should not be used
* for so-called high priority HCI events, which should instead be delivered to
* the host stack through bt_recv_prio().
*
@ -165,7 +165,7 @@ struct bt_hci_driver {
* is safe to start calling the send() handler.
*
* If the driver uses its own RX thread, i.e.
* CONFIG_BT_RECV_IS_RX_THREAD is set, then this
* @kconfig{CONFIG_BT_RECV_BLOCKING} is set, then this
* function is expected to start that thread.
*
* @return 0 on success or negative error number on failure.
@ -179,7 +179,7 @@ struct bt_hci_driver {
* transport is closed.
*
* If the driver uses its own RX thread, i.e.
* CONFIG_BT_RECV_IS_RX_THREAD is set, then this
* @kconfig{CONFIG_BT_RECV_BLOCKING} is set, then this
* function is expected to abort that thread.
* @return 0 on success or negative error number on failure.
*/

View file

@ -95,7 +95,7 @@ config BT_BUF_ACL_RX_SIZE
config BT_BUF_ACL_RX_COUNT
int "Number of incoming ACL data buffers"
default NET_BUF_RX_COUNT if NET_L2_BT
default 3 if BT_RECV_IS_RX_THREAD
default 3 if BT_RECV_BLOCKING
default 6
range 1 64
help
@ -126,7 +126,7 @@ config BT_BUF_EVT_RX_SIZE
config BT_BUF_EVT_RX_COUNT
int "Number of HCI Event buffers"
default 3 if BT_RECV_IS_RX_THREAD
default 3 if BT_RECV_BLOCKING
default 20 if (BT_MESH && !(BT_BUF_EVT_DISCARDABLE_COUNT > 0))
default 10
range 2 255

View file

@ -96,7 +96,6 @@ choice BT_LL_CHOICE
config BT_LL_SW_SPLIT
bool "Software-based BLE Link Layer"
select BT_RECV_IS_RX_THREAD
select ENTROPY_GENERATOR
select NRF_HW_TIMER0_RESERVED
select NRF_HW_RTC0_RESERVED

View file

@ -68,19 +68,48 @@ config BT_HCI_RESERVE
Headroom that the driver needs for sending and receiving buffers. Add a
new 'default' entry for each new driver.
config BT_RECV_IS_RX_THREAD
# Hidden option set by the HCI driver to indicate that there's
# no need for the host to have its own RX thread.
# If this option has been enabled it is then the responsibility of the
# HCI driver to call bt_recv_prio from a higher priority context than
# bt_recv in order to avoid deadlocks.
# If this option is disabled then only bt_recv should be called.
bool
prompt "bt_recv is called from RX thread" if BT_NO_DRIVER
choice BT_RECV_CONTEXT
prompt "BT RX Thread Selection"
default BT_RECV_BLOCKING if BT_LL_SW_SPLIT || BT_H4
default BT_RECV_WORKQ_BT
help
Selects in which context incoming low priority HCI packets are processed.
The host defines some events as high priority to avoid race conditions and deadlocks.
High priority events are always processed in the context of the caller of bt_recv()
or bt_recv_prio(). The choice will influence RAM usage and how fast incoming HCI
packets are processed.
config BT_RECV_BLOCKING
bool "Process HCI packets in the context of bt_recv() and bt_recv_prio()"
help
When this option is selected, the host will not have its own RX thread.
With this option it is the responsibility of the HCI driver to call
bt_recv_prio from a higher priority context than bt_recv() in order to avoid deadlocks.
config BT_RECV_WORKQ_SYS
bool "Process low priority HCI packets in the system work queue"
help
When this option is selected, the host will process incoming low priority HCI packets
in the system work queue. The HCI driver shall not call bt_recv_prio().
High priority HCI packets will processed in the context of the caller of bt_recv().
The application needs to ensure the system workqueue stack size (SYSTEM_WORKQUEUE_STACK_SIZE)
is large enough, refer to BT_RX_STACK_SIZE for the recommended minimum.
Note: When this option is used, other users of the system work queue will influence the
latency of incoming Bluetooth events.
config BT_RECV_WORKQ_BT
bool "Process low priority HCI packets in the bluetooth-specific work queue"
help
When this option is selected, the host will process incoming low priority HCI packets
in the bluetooth-specific work queue. The HCI driver shall not call bt_recv_prio().
High priority HCI packets will processed in the context of the caller of bt_recv().
The application needs to ensure the bluetooth-specific work queue size is large enough,
refer to BT_RX_STACK_SIZE for the recommended minimum.
endchoice
config BT_RX_STACK_SIZE
int "Size of the receiving thread stack"
depends on BT_HCI_HOST || BT_RECV_IS_RX_THREAD
default 768 if BT_HCI_RAW
default 3092 if BT_MESH_GATT_CLIENT
default 2048 if BT_MESH
@ -98,7 +127,6 @@ config BT_RX_STACK_SIZE
config BT_RX_PRIO
# Hidden option for Co-Operative Rx thread priority
int
depends on BT_HCI_HOST || BT_RECV_IS_RX_THREAD
default 8
config BT_DRIVER_RX_HIGH_PRIO

View file

@ -61,10 +61,14 @@
#define HCI_CMD_TIMEOUT K_SECONDS(10)
/* Stacks for the threads */
#if !defined(CONFIG_BT_RECV_IS_RX_THREAD)
static struct k_thread rx_thread_data;
#if !defined(CONFIG_BT_RECV_BLOCKING)
static void rx_work_handler(struct k_work *work);
static K_WORK_DEFINE(rx_work, rx_work_handler);
#if defined(CONFIG_BT_RECV_WORKQ_BT)
static struct k_work_q bt_workq;
static K_KERNEL_STACK_DEFINE(rx_thread_stack, CONFIG_BT_RX_STACK_SIZE);
#endif
#endif /* CONFIG_BT_RECV_WORKQ_BT */
#endif /* !CONFIG_BT_RECV_BLOCKING */
static struct k_thread tx_thread_data;
static K_KERNEL_STACK_DEFINE(tx_thread_stack, CONFIG_BT_HCI_TX_STACK_SIZE);
@ -82,7 +86,7 @@ struct bt_dev bt_dev = {
.ncmd_sem = Z_SEM_INITIALIZER(bt_dev.ncmd_sem, 0, 1),
#endif
.cmd_tx_queue = Z_FIFO_INITIALIZER(bt_dev.cmd_tx_queue),
#if !defined(CONFIG_BT_RECV_IS_RX_THREAD)
#if !defined(CONFIG_BT_RECV_BLOCKING)
.rx_queue = Z_FIFO_INITIALIZER(bt_dev.rx_queue),
#endif
#if defined(CONFIG_BT_DEVICE_APPEARANCE_DYNAMIC)
@ -3413,6 +3417,22 @@ void hci_event_prio(struct net_buf *buf)
}
}
#if !defined(CONFIG_BT_RECV_BLOCKING)
static void rx_queue_put(struct net_buf *buf)
{
net_buf_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 */
if (err < 0) {
BT_ERR("Could not submit rx_work: %d", err);
}
}
#endif /* !CONFIG_BT_RECV_BLOCKING */
int bt_recv(struct net_buf *buf)
{
bt_monitor_send(bt_monitor_opcode(buf), buf->data, buf->len);
@ -3422,16 +3442,16 @@ int bt_recv(struct net_buf *buf)
switch (bt_buf_get_type(buf)) {
#if defined(CONFIG_BT_CONN)
case BT_BUF_ACL_IN:
#if defined(CONFIG_BT_RECV_IS_RX_THREAD)
#if defined(CONFIG_BT_RECV_BLOCKING)
hci_acl(buf);
#else
net_buf_put(&bt_dev.rx_queue, buf);
rx_queue_put(buf);
#endif
return 0;
#endif /* BT_CONN */
case BT_BUF_EVT:
{
#if defined(CONFIG_BT_RECV_IS_RX_THREAD)
#if defined(CONFIG_BT_RECV_BLOCKING)
hci_event(buf);
#else
struct bt_hci_evt_hdr *hdr = (void *)buf->data;
@ -3442,7 +3462,7 @@ int bt_recv(struct net_buf *buf)
}
if (evt_flags & BT_HCI_EVT_FLAG_RECV) {
net_buf_put(&bt_dev.rx_queue, buf);
rx_queue_put(buf);
}
#endif
return 0;
@ -3450,10 +3470,10 @@ int bt_recv(struct net_buf *buf)
}
#if defined(CONFIG_BT_ISO)
case BT_BUF_ISO_IN:
#if defined(CONFIG_BT_RECV_IS_RX_THREAD)
#if defined(CONFIG_BT_RECV_BLOCKING)
hci_iso(buf);
#else
net_buf_put(&bt_dev.rx_queue, buf);
rx_queue_put(buf);
#endif
return 0;
#endif /* CONFIG_BT_ISO */
@ -3464,7 +3484,6 @@ int bt_recv(struct net_buf *buf)
}
}
#if defined(CONFIG_BT_RECV_IS_RX_THREAD)
int bt_recv_prio(struct net_buf *buf)
{
bt_monitor_send(bt_monitor_opcode(buf), buf->data, buf->len);
@ -3475,7 +3494,6 @@ int bt_recv_prio(struct net_buf *buf)
return 0;
}
#endif /* defined(CONFIG_BT_RECV_IS_RX_THREAD) */
int bt_hci_driver_register(const struct bt_hci_driver *drv)
{
@ -3554,16 +3572,18 @@ static void init_work(struct k_work *work)
}
}
#if !defined(CONFIG_BT_RECV_IS_RX_THREAD)
static void hci_rx_thread(void)
#if !defined(CONFIG_BT_RECV_BLOCKING)
static void rx_work_handler(struct k_work *work)
{
int err;
struct net_buf *buf;
BT_DBG("started");
while (1) {
BT_DBG("calling fifo_get_wait");
buf = net_buf_get(&bt_dev.rx_queue, K_FOREVER);
buf = net_buf_get(&bt_dev.rx_queue, K_NO_WAIT);
if (!buf) {
return;
}
BT_DBG("buf %p type %u len %u", buf, bt_buf_get_type(buf),
buf->len);
@ -3591,10 +3611,20 @@ static void hci_rx_thread(void)
/* Make sure we don't hog the CPU if the rx_queue never
* gets empty.
*/
k_yield();
if (k_fifo_is_empty(&bt_dev.rx_queue)) {
return;
}
#if defined(CONFIG_BT_RECV_WORKQ_SYS)
err = k_work_submit(&rx_work);
#elif defined(CONFIG_BT_RECV_WORKQ_BT)
err = k_work_submit_to_queue(&bt_workq, &rx_work);
#endif
if (err < 0) {
BT_ERR("Could not submit rx_work: %d", err);
}
}
#endif /* !CONFIG_BT_RECV_IS_RX_THREAD */
#endif /* !CONFIG_BT_RECV_BLOCKING */
int bt_enable(bt_ready_cb_t cb)
{
@ -3633,14 +3663,12 @@ int bt_enable(bt_ready_cb_t cb)
0, K_NO_WAIT);
k_thread_name_set(&tx_thread_data, "BT TX");
#if !defined(CONFIG_BT_RECV_IS_RX_THREAD)
#if defined(CONFIG_BT_RECV_WORKQ_BT)
/* RX thread */
k_thread_create(&rx_thread_data, rx_thread_stack,
K_KERNEL_STACK_SIZEOF(rx_thread_stack),
(k_thread_entry_t)hci_rx_thread, NULL, NULL, NULL,
K_PRIO_COOP(CONFIG_BT_RX_PRIO),
0, K_NO_WAIT);
k_thread_name_set(&rx_thread_data, "BT RX");
k_work_queue_start(&bt_workq, rx_thread_stack,
CONFIG_BT_RX_STACK_SIZE,
K_PRIO_COOP(CONFIG_BT_RX_PRIO), NULL);
k_thread_name_set(&bt_workq.thread, "BT RX");
#endif
if (IS_ENABLED(CONFIG_BT_TINYCRYPT_ECC)) {
@ -3699,9 +3727,9 @@ int bt_disable(void)
/* Abort TX thread */
k_thread_abort(&tx_thread_data);
#if !defined(CONFIG_BT_RECV_IS_RX_THREAD)
#if defined(CONFIG_BT_RECV_WORKQ_BT)
/* Abort RX thread */
k_thread_abort(&rx_thread_data);
k_thread_abort(&bt_workq.thread);
#endif
if (IS_ENABLED(CONFIG_BT_TINYCRYPT_ECC)) {

View file

@ -341,7 +341,7 @@ struct bt_dev {
/* Last sent HCI command */
struct net_buf *sent_cmd;
#if !defined(CONFIG_BT_RECV_IS_RX_THREAD)
#if !defined(CONFIG_BT_RECV_BLOCKING)
/* Queue for incoming HCI events & ACL data */
struct k_fifo rx_queue;
#endif

View file

@ -91,7 +91,7 @@ static void send_cmd_status(uint16_t opcode, uint8_t status)
evt->opcode = sys_cpu_to_le16(opcode);
evt->status = status;
if (IS_ENABLED(CONFIG_BT_RECV_IS_RX_THREAD)) {
if (IS_ENABLED(CONFIG_BT_RECV_BLOCKING)) {
bt_recv_prio(buf);
} else {
bt_recv(buf);

View file

@ -4,7 +4,7 @@ CONFIG_ZTEST=y
CONFIG_BT=y
CONFIG_BT_CTLR=n
CONFIG_BT_NO_DRIVER=y
CONFIG_BT_RECV_IS_RX_THREAD=y
CONFIG_BT_RECV_BLOCKING=y
CONFIG_BT_HCI_VS_EVT_USER=y

View file

@ -8,7 +8,7 @@ CONFIG_BT_HCI=n
CONFIG_BT_HCI_RAW=n
CONFIG_BT_OBSERVER=y
CONFIG_BT_NO_DRIVER=y
CONFIG_BT_RECV_IS_RX_THREAD=y
CONFIG_BT_RECV_BLOCKING=y
CONFIG_BT_EXT_ADV=y
CONFIG_BT_DEBUG_LOG=y