Skip to content

Commit 33a6572

Browse files
alwa-nordicPavelVPV
authored andcommitted
[nrf fromtree] 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]> (cherry picked from commit f101976) Signed-off-by: Kyra Lengfeld <[email protected]>
1 parent f25915c commit 33a6572

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
@@ -5098,24 +5098,99 @@ static bool process_pending_cmd(k_timeout_t timeout)
50985098
static void tx_processor(struct k_work *item)
50995099
{
51005100
LOG_DBG("TX process start");
5101+
5102+
/* Historically, the code in process_pending_cmd() and
5103+
* bt_conn_tx_processor() has been invoked only from
5104+
* cooperative threads. For now, we assume their
5105+
* implementations rely on this and ensure the current
5106+
* thread is cooperative.
5107+
*/
5108+
k_sched_lock();
5109+
51015110
if (process_pending_cmd(K_NO_WAIT)) {
51025111
/* If we processed a command, let the scheduler run before
51035112
* processing another command (or data).
51045113
*/
51055114
bt_tx_irq_raise();
5106-
return;
5115+
goto exit;
51075116
}
51085117

51095118
/* Hand over control to conn to process pending data */
51105119
if (IS_ENABLED(CONFIG_BT_CONN_TX)) {
51115120
bt_conn_tx_processor();
51125121
}
5122+
5123+
exit:
5124+
k_sched_unlock();
51135125
}
51145126

5127+
/**
5128+
* This work item shall never be cancelled.
5129+
*/
51155130
static K_WORK_DEFINE(tx_work, tx_processor);
51165131

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

0 commit comments

Comments
 (0)