Skip to content

Commit f101976

Browse files
alwa-nordicPavelVPV
authored andcommitted
Bluetooth: Host: Run tx processor on its own thread
When thread that TX processor is used to send commands and data to Controller is also used for sync commands sending and command buffer allocation, a deadlock happens. This thread is used to avoid such deadlocks by moving TX processor to its own dedicated thread exclusively used by tx processor only. Co-authored-by: Pavel Vasilyev <[email protected]> Signed-off-by: Pavel Vasilyev <[email protected]> Signed-off-by: Aleksander Wasaznik <[email protected]>
1 parent 0ee5d70 commit f101976

File tree

2 files changed

+96
-1
lines changed

2 files changed

+96
-1
lines changed

subsys/bluetooth/host/Kconfig

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,26 @@ config BT_DRIVER_RX_HIGH_PRIO
133133
int
134134
default 6
135135

136+
config BT_TX_PROCESSOR_THREAD
137+
# This thread is used to send pending HCI Commands, ACL and ISO data to
138+
# Controller.
139+
bool
140+
# This option is automatically selected for all platforms except nRF51
141+
# due to limited RAM on nRF51 devices.
142+
default y if !SOC_SERIES_NRF51X
143+
144+
if BT_TX_PROCESSOR_THREAD
145+
146+
config BT_TX_PROCESSOR_THREAD_PRIO
147+
int
148+
default SYSTEM_WORKQUEUE_PRIORITY
149+
150+
config BT_TX_PROCESSOR_STACK_SIZE
151+
int
152+
default 1024
153+
154+
endif
155+
136156
config BT_CONN_TX_NOTIFY_WQ
137157
bool "Use a separate workqueue for connection TX notify processing [EXPERIMENTAL]"
138158
depends on BT_CONN_TX

subsys/bluetooth/host/hci_core.c

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5103,24 +5103,99 @@ static bool process_pending_cmd(k_timeout_t timeout)
51035103
static void tx_processor(struct k_work *item)
51045104
{
51055105
LOG_DBG("TX process start");
5106+
5107+
/* Historically, the code in process_pending_cmd() and
5108+
* bt_conn_tx_processor() has been invoked only from
5109+
* cooperative threads. For now, we assume their
5110+
* implementations rely on this and ensure the current
5111+
* thread is cooperative.
5112+
*/
5113+
k_sched_lock();
5114+
51065115
if (process_pending_cmd(K_NO_WAIT)) {
51075116
/* If we processed a command, let the scheduler run before
51085117
* processing another command (or data).
51095118
*/
51105119
bt_tx_irq_raise();
5111-
return;
5120+
goto exit;
51125121
}
51135122

51145123
/* Hand over control to conn to process pending data */
51155124
if (IS_ENABLED(CONFIG_BT_CONN_TX)) {
51165125
bt_conn_tx_processor();
51175126
}
5127+
5128+
exit:
5129+
k_sched_unlock();
51185130
}
51195131

5132+
/**
5133+
* This work item shall never be cancelled.
5134+
*/
51205135
static K_WORK_DEFINE(tx_work, tx_processor);
51215136

5137+
#if defined(CONFIG_BT_TX_PROCESSOR_THREAD)
5138+
static K_THREAD_STACK_DEFINE(bt_tx_processor_stack, CONFIG_BT_TX_PROCESSOR_STACK_SIZE);
5139+
5140+
/**
5141+
* This work queue shall never be stopped, drained or plugged.
5142+
*/
5143+
static struct k_work_q bt_tx_processor_workq;
5144+
5145+
static int bt_tx_processor_init(void)
5146+
{
5147+
struct k_work_queue_config cfg = {};
5148+
5149+
if (IS_ENABLED(CONFIG_THREAD_NAME)) {
5150+
cfg.name = "bt_tx_processor";
5151+
}
5152+
5153+
k_work_queue_start(&bt_tx_processor_workq, bt_tx_processor_stack,
5154+
K_THREAD_STACK_SIZEOF(bt_tx_processor_stack),
5155+
CONFIG_BT_TX_PROCESSOR_THREAD_PRIO, &cfg);
5156+
5157+
return 0;
5158+
}
5159+
5160+
/* Priority 999 is the last to run in POST_KERNEL. We don't actually
5161+
* care when it runs, so long as it's before APPLICATION, when
5162+
* `bt_enable()` can be called. Running it last will allow more urgent
5163+
* initializations competing for CPU time to complete first.
5164+
*/
5165+
SYS_INIT(bt_tx_processor_init, POST_KERNEL, 999);
5166+
#endif /* CONFIG_BT_TX_PROCESSOR_THREAD */
5167+
5168+
/**
5169+
* This function shall not be called before init level APPLICATION.
5170+
*/
51225171
void bt_tx_irq_raise(void)
51235172
{
5173+
int __maybe_unused err;
51245174
LOG_DBG("kick TX");
5175+
#if defined(CONFIG_BT_TX_PROCESSOR_THREAD)
5176+
err = k_work_submit_to_queue(&bt_tx_processor_workq, &tx_work);
5177+
__ASSERT(err >= 0, "%d", err);
5178+
/* Assertions:
5179+
*
5180+
* EBUSY shall not occur because `bt_tx_processor_workq` shall
5181+
* never be draining or plugged, and `tx_work` shall never be
5182+
* cancelled.
5183+
*
5184+
* EINVAL is not possible because taking address of variable
5185+
* cannot result in the null pointer.
5186+
*
5187+
* ENODEV shall not occur because `bt_tx_processor_workq` shall
5188+
* never be stopped, is started before init level APPLICATION,
5189+
* and this function shall not be called before init level
5190+
* APPLICATION.
5191+
*
5192+
* The above is an exhaustive list of the API errors.
5193+
*
5194+
* Defensive coding: If any error occurs and asserts are
5195+
* disabled, the program will recover if bt_tx_irq_raise is
5196+
* called again and is successful. No cleanup is needed.
5197+
*/
5198+
#else
51255199
k_work_submit(&tx_work);
5200+
#endif
51265201
}

0 commit comments

Comments
 (0)