@@ -52,6 +52,13 @@ LOG_MODULE_REGISTER(bt_conn);
5252
5353K_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+
5562static void tx_free (struct bt_conn_tx * tx );
5663
5764static 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
305333struct 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-
465470void 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)
778783void 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
0 commit comments