Skip to content

Commit b662f75

Browse files
committed
bluetooth: conn: Use a separate workqueue for connection TX notify
Use a separate workqueue instead of system workqueue for connection TX notify processing. This makes Bluetooth stack more independent from the system workqueue. Signed-off-by: Marek Pieta <[email protected]>
1 parent e1cacb3 commit b662f75

File tree

4 files changed

+95
-33
lines changed

4 files changed

+95
-33
lines changed

subsys/bluetooth/host/Kconfig

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,26 @@ config BT_DRIVER_RX_HIGH_PRIO
129129
int
130130
default 6
131131

132+
config BT_CONN_TX_WQ
133+
bool "Use a separate workqueue for connection TX notify processing"
134+
help
135+
Use a separate workqueue instead of system workqueue for
136+
bt_conn_tx_notify processing. The option can be used to make Bluetooth
137+
stack more independed from the system workqueue.
138+
139+
if BT_CONN_TX_WQ
140+
141+
config BT_CONN_TX_WQ_STACK_SIZE
142+
int "Stack size of workqueue for connection TX notify processing"
143+
default 1200
144+
145+
config BT_CONN_TX_WQ_PRIO
146+
# Hidden option for cooperative connection TX notify workqueue priority
147+
int
148+
default 8
149+
150+
endif # BT_CONN_TX_WQ
151+
132152
menu "Bluetooth Host"
133153

134154
if BT_HCI_HOST

subsys/bluetooth/host/conn.c

Lines changed: 72 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,13 @@ LOG_MODULE_REGISTER(bt_conn);
5252

5353
K_FIFO_DEFINE(free_tx);
5454

55+
#if defined(CONFIG_BT_CONN_TX_WQ)
56+
static bool conn_tx_workq_initialized;
57+
static struct k_work_q conn_tx_workq;
58+
static K_KERNEL_STACK_DEFINE(conn_tx_workq_thread_stack,
59+
CONFIG_BT_CONN_TX_WQ_STACK_SIZE);
60+
#endif /* CONFIG_BT_CONN_TX_WQ */
61+
5562
static void tx_free(struct bt_conn_tx *tx);
5663

5764
static void conn_tx_destroy(struct bt_conn *conn, struct bt_conn_tx *tx)
@@ -253,11 +260,32 @@ static void tx_free(struct bt_conn_tx *tx)
253260
k_fifo_put(&free_tx, tx);
254261
}
255262

256-
#if defined(CONFIG_BT_CONN_TX)
257-
static void tx_notify(struct bt_conn *conn)
263+
static struct k_work_q *tx_notify_workqueue_get(void)
258264
{
259-
__ASSERT_NO_MSG(k_current_get() ==
260-
k_work_queue_thread_get(&k_sys_work_q));
265+
#if defined(CONFIG_BT_CONN_TX_WQ)
266+
return &conn_tx_workq;
267+
#else
268+
return &k_sys_work_q;
269+
#endif /* CONFIG_BT_CONN_TX_WQ */
270+
}
271+
272+
void bt_conn_tx_notify(struct bt_conn *conn, bool wait_for_completion)
273+
{
274+
#if defined(CONFIG_BT_CONN_TX)
275+
if (k_current_get() != k_work_queue_thread_get(tx_notify_workqueue_get())) {
276+
/* Ensure that function is called only from a single context. */
277+
struct k_work_sync sync;
278+
int err;
279+
280+
err = k_work_submit_to_queue(tx_notify_workqueue_get(),
281+
&conn->tx_complete_work);
282+
__ASSERT(err >= 0, "couldn't submit (err %d)", err);
283+
284+
if (wait_for_completion) {
285+
(void)k_work_flush(&conn->tx_complete_work, &sync);
286+
}
287+
return;
288+
}
261289

262290
LOG_DBG("conn %p", conn);
263291

@@ -299,8 +327,8 @@ static void tx_notify(struct bt_conn *conn)
299327
LOG_DBG("raise TX IRQ");
300328
bt_tx_irq_raise();
301329
}
302-
}
303330
#endif /* CONFIG_BT_CONN_TX */
331+
}
304332

305333
struct bt_conn *bt_conn_new(struct bt_conn *conns, size_t size)
306334
{
@@ -439,38 +467,15 @@ static void bt_acl_recv(struct bt_conn *conn, struct net_buf *buf, uint8_t flags
439467
bt_l2cap_recv(conn, buf, true);
440468
}
441469

442-
static void wait_for_tx_work(struct bt_conn *conn)
443-
{
444-
#if defined(CONFIG_BT_CONN_TX)
445-
LOG_DBG("conn %p", conn);
446-
447-
if (IS_ENABLED(CONFIG_BT_RECV_WORKQ_SYS) ||
448-
k_current_get() == k_work_queue_thread_get(&k_sys_work_q)) {
449-
tx_notify(conn);
450-
} else {
451-
struct k_work_sync sync;
452-
int err;
453-
454-
err = k_work_submit(&conn->tx_complete_work);
455-
__ASSERT(err >= 0, "couldn't submit (err %d)", err);
456-
457-
k_work_flush(&conn->tx_complete_work, &sync);
458-
}
459-
LOG_DBG("done");
460-
#else
461-
ARG_UNUSED(conn);
462-
#endif /* CONFIG_BT_CONN_TX */
463-
}
464-
465470
void bt_conn_recv(struct bt_conn *conn, struct net_buf *buf, uint8_t flags)
466471
{
467472
/* Make sure we notify any pending TX callbacks before processing
468473
* new data for this connection.
469474
*
470475
* Always do so from the same context for sanity. In this case that will
471-
* be the system workqueue.
476+
* be either a dedicated Bluetooth connection TX workqueue or system workqueue.
472477
*/
473-
wait_for_tx_work(conn);
478+
bt_conn_tx_notify(conn, true);
474479

475480
LOG_DBG("handle %u len %u flags %02x", conn->handle, buf->len, flags);
476481

@@ -778,6 +783,16 @@ static void conn_destroy(struct bt_conn *conn, void *data)
778783
void bt_conn_cleanup_all(void)
779784
{
780785
bt_conn_foreach(BT_CONN_TYPE_ALL, conn_destroy, NULL);
786+
#if defined(CONFIG_BT_CONN_TX_WQ)
787+
if (conn_tx_workq_initialized) {
788+
int ret = k_work_queue_drain(&conn_tx_workq, true);
789+
790+
/* No works are expected to be pending at this point. */
791+
if (ret) {
792+
LOG_ERR("conn_tx_workq drain returned: %d", ret);
793+
}
794+
}
795+
#endif /* CONFIG_BT_CONN_TX_WQ */
781796
}
782797

783798
#if defined(CONFIG_BT_CONN)
@@ -1240,7 +1255,7 @@ void bt_conn_set_state(struct bt_conn *conn, bt_conn_state_t state)
12401255
*/
12411256
switch (old_state) {
12421257
case BT_CONN_DISCONNECT_COMPLETE:
1243-
wait_for_tx_work(conn);
1258+
bt_conn_tx_notify(conn, true);
12441259

12451260
bt_conn_reset_rx_state(conn);
12461261

@@ -1636,7 +1651,11 @@ static void tx_complete_work(struct k_work *work)
16361651

16371652
LOG_DBG("conn %p", conn);
16381653

1639-
tx_notify(conn);
1654+
/* Ensure single execution context. */
1655+
__ASSERT_NO_MSG(k_current_get() ==
1656+
k_work_queue_thread_get(tx_notify_workqueue_get()));
1657+
1658+
bt_conn_tx_notify(conn, false);
16401659
}
16411660
#endif /* CONFIG_BT_CONN_TX */
16421661

@@ -4054,6 +4073,27 @@ int bt_conn_init(void)
40544073
}
40554074
}
40564075

4076+
#if defined(CONFIG_BT_CONN_TX_WQ)
4077+
if (conn_tx_workq_initialized) {
4078+
err = k_work_queue_unplug(&conn_tx_workq);
4079+
if (err) {
4080+
LOG_ERR("conn_tx_workq unplug returned: %d", err);
4081+
}
4082+
} else {
4083+
static const struct k_work_queue_config cfg = {
4084+
.name = "BT CONN TX WQ",
4085+
.no_yield = false,
4086+
.essential = false,
4087+
};
4088+
4089+
k_work_queue_init(&conn_tx_workq);
4090+
k_work_queue_start(&conn_tx_workq, conn_tx_workq_thread_stack,
4091+
K_THREAD_STACK_SIZEOF(conn_tx_workq_thread_stack),
4092+
K_PRIO_COOP(CONFIG_BT_CONN_TX_WQ_PRIO), &cfg);
4093+
conn_tx_workq_initialized = true;
4094+
}
4095+
#endif /* CONFIG_BT_CONN_TX_WQ */
4096+
40574097
return 0;
40584098
}
40594099

subsys/bluetooth/host/conn_internal.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,8 @@ static inline void *closure_data(void *storage)
361361
return ((struct closure *)storage)->data;
362362
}
363363

364+
void bt_conn_tx_notify(struct bt_conn *conn, bool wait_for_completion);
365+
364366
void bt_conn_reset_rx_state(struct bt_conn *conn);
365367

366368
/* Process incoming data for a connection */

subsys/bluetooth/host/hci_core.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -605,7 +605,7 @@ static void hci_num_completed_packets(struct net_buf *buf)
605605
atomic_dec(&conn->in_ll);
606606

607607
/* TX context free + callback happens in there */
608-
k_work_submit(&conn->tx_complete_work);
608+
bt_conn_tx_notify(conn, false);
609609
}
610610

611611
bt_conn_unref(conn);

0 commit comments

Comments
 (0)