Skip to content

Commit 0af3fd6

Browse files
lylezhu2012jhedberg
authored andcommitted
Bluetooth: Classic: RFCOMM: Remove TX thread from DLC
There are two main issues found with using DLC TX thread, Issue 1, the RAM consumption. Every DLC will have a dedicated thread and thread stack. Issue 2, the thread stack overflow issue. There is no way to strike a balance between stack size and RAM consumption. Since the deep of call stack is depended on the upper layer, the thread stack needs to set by application. Due to the thread stack of DLC is dedicated, RAM consumption is the product of the added value and the number of DLCs. Use a TX worker to replace DLC TX thread. Signed-off-by: Lyle Zhu <[email protected]>
1 parent 8fb7f8a commit 0af3fd6

File tree

3 files changed

+100
-85
lines changed

3 files changed

+100
-85
lines changed

include/zephyr/bluetooth/classic/rfcomm.h

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -106,23 +106,20 @@ struct bt_rfcomm_dlc {
106106
/* TX credits, Reuse as a binary sem for MSC FC if CFC is not enabled */
107107
struct k_sem tx_credits;
108108

109+
/* Worker for RFCOMM TX */
110+
struct k_work tx_work;
111+
109112
struct bt_rfcomm_session *session;
110113
struct bt_rfcomm_dlc_ops *ops;
111114
struct bt_rfcomm_dlc *_next;
112115

113116
bt_security_t required_sec_level;
114117
bt_rfcomm_role_t role;
115118

116-
uint16_t mtu;
117-
uint8_t dlci;
118-
uint8_t state;
119-
uint8_t rx_credit;
120-
121-
/* Stack & kernel data for TX thread */
122-
struct k_thread tx_thread;
123-
#if defined(CONFIG_BT_RFCOMM_DLC_STACK_SIZE)
124-
K_KERNEL_STACK_MEMBER(stack, CONFIG_BT_RFCOMM_DLC_STACK_SIZE);
125-
#endif /* CONFIG_BT_RFCOMM_DLC_STACK_SIZE */
119+
uint16_t mtu;
120+
uint8_t dlci;
121+
uint8_t state;
122+
uint8_t rx_credit;
126123
};
127124

128125
struct bt_rfcomm_server {

subsys/bluetooth/host/classic/Kconfig

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -153,17 +153,6 @@ config BT_RFCOMM_TX_MAX
153153
sending buf. Normally this can be left to the default value, which
154154
is equal to the number of session in the stack-internal pool.
155155

156-
config BT_RFCOMM_DLC_STACK_SIZE
157-
int "Stack size of DLC for RFCOMM"
158-
default 512
159-
help
160-
Stack size of DLC for RFCOMM. This is the context from which
161-
all data of upper layer are sent and disconnect
162-
callback to the upper layer. The default value is sufficient
163-
for basic operation, but if the application needs to do
164-
advanced things in its callbacks that require extra stack
165-
space, this value can be increased to accommodate for that.
166-
167156
config BT_HFP_HF
168157
bool "Bluetooth Handsfree profile HF Role support [EXPERIMENTAL]"
169158
depends on PRINTK

subsys/bluetooth/host/classic/rfcomm.c

Lines changed: 93 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,6 @@ LOG_MODULE_REGISTER(bt_rfcomm);
5151

5252
static struct bt_rfcomm_server *servers;
5353

54-
/* Pool for dummy buffers to wake up the tx threads */
55-
NET_BUF_POOL_DEFINE(dummy_pool, CONFIG_BT_MAX_CONN, 0, 0, NULL);
56-
5754
#define RFCOMM_SESSION(_ch) CONTAINER_OF(_ch, \
5855
struct bt_rfcomm_session, br_chan.chan)
5956

@@ -235,13 +232,37 @@ int bt_rfcomm_server_register(struct bt_rfcomm_server *server)
235232
return 0;
236233
}
237234

235+
static void rfcomm_dlc_tx_trigger(struct bt_rfcomm_dlc *dlc)
236+
{
237+
int err;
238+
239+
if ((dlc == NULL) || (dlc->tx_work.handler == NULL)) {
240+
return;
241+
}
242+
243+
LOG_DBG("DLC %p TX trigger", dlc);
244+
245+
err = k_work_submit(&dlc->tx_work);
246+
if (err < 0) {
247+
LOG_ERR("Failed to submit tx work: %d", err);
248+
}
249+
}
250+
251+
static void rfcomm_dlcs_tx_trigger(struct bt_rfcomm_dlc *dlcs)
252+
{
253+
for (struct bt_rfcomm_dlc *dlc = dlcs; dlc != NULL; dlc = dlc->_next) {
254+
rfcomm_dlc_tx_trigger(dlc);
255+
}
256+
}
257+
238258
static void rfcomm_dlc_tx_give_credits(struct bt_rfcomm_dlc *dlc,
239259
uint8_t credits)
240260
{
241261
LOG_DBG("dlc %p credits %u", dlc, credits);
242262

243263
while (credits--) {
244264
k_sem_give(&dlc->tx_credits);
265+
rfcomm_dlc_tx_trigger(dlc);
245266
}
246267

247268
LOG_DBG("dlc %p updated credits %u", dlc, k_sem_count_get(&dlc->tx_credits));
@@ -252,6 +273,8 @@ static void rfcomm_dlc_destroy(struct bt_rfcomm_dlc *dlc)
252273
LOG_DBG("dlc %p", dlc);
253274

254275
k_work_cancel_delayable(&dlc->rtx_work);
276+
k_work_cancel(&dlc->tx_work);
277+
255278
dlc->state = BT_RFCOMM_STATE_IDLE;
256279
dlc->session = NULL;
257280

@@ -274,16 +297,7 @@ static void rfcomm_dlc_disconnect(struct bt_rfcomm_dlc *dlc)
274297

275298
switch (old_state) {
276299
case BT_RFCOMM_STATE_CONNECTED:
277-
/* Queue a dummy buffer to wake up and stop the
278-
* tx thread for states where it was running.
279-
*/
280-
k_fifo_put(&dlc->tx_queue, net_buf_alloc(&dummy_pool, K_NO_WAIT));
281-
282-
/* There could be a writer waiting for credits so return a
283-
* dummy credit to wake it up.
284-
*/
285-
rfcomm_dlc_tx_give_credits(dlc, 1);
286-
k_sem_give(&dlc->session->fc);
300+
rfcomm_dlc_tx_trigger(dlc);
287301
break;
288302
default:
289303
rfcomm_dlc_destroy(dlc);
@@ -465,8 +479,17 @@ static void rfcomm_dlc_init(struct bt_rfcomm_dlc *dlc,
465479
uint8_t dlci,
466480
bt_rfcomm_role_t role)
467481
{
482+
struct k_work_sync sync;
483+
468484
LOG_DBG("dlc %p", dlc);
469485

486+
if (dlc->tx_work.handler != NULL) {
487+
/* Make sure the tx_work is cancelled before reinitializing */
488+
k_work_cancel_sync(&dlc->tx_work, &sync);
489+
/* Reset the work handler to NULL before the DLC connected */
490+
dlc->tx_work.handler = NULL;
491+
}
492+
470493
dlc->dlci = dlci;
471494
dlc->session = session;
472495
dlc->rx_credit = RFCOMM_DEFAULT_CREDIT;
@@ -533,19 +556,30 @@ static int rfcomm_send_dm(struct bt_rfcomm_session *session, uint8_t dlci)
533556
return rfcomm_send(session, buf);
534557
}
535558

536-
static void rfcomm_check_fc(struct bt_rfcomm_dlc *dlc)
559+
static bool rfcomm_check_fc(struct bt_rfcomm_dlc *dlc)
537560
{
538-
LOG_DBG("%p", dlc);
561+
int err;
539562

540563
LOG_DBG("Wait for credits or MSC FC %p", dlc);
541564
/* Wait for credits or MSC FC */
542-
k_sem_take(&dlc->tx_credits, K_FOREVER);
565+
err = k_sem_take(&dlc->tx_credits, K_NO_WAIT);
566+
if (err != 0) {
567+
LOG_DBG("Credits not available");
568+
return false;
569+
}
543570

571+
LOG_DBG("Credits available");
544572
if (dlc->session->cfc == BT_RFCOMM_CFC_SUPPORTED) {
545-
return;
573+
LOG_DBG("Credits available");
574+
return true;
546575
}
547576

548-
k_sem_take(&dlc->session->fc, K_FOREVER);
577+
err = k_sem_take(&dlc->session->fc, K_NO_WAIT);
578+
if (err != 0) {
579+
k_sem_give(&dlc->tx_credits);
580+
LOG_DBG("Flow control not available");
581+
return false;
582+
}
549583

550584
/* Give the sems immediately so that sem will be available for all
551585
* the bufs in the queue. It will be blocked only once all the bufs
@@ -554,6 +588,9 @@ static void rfcomm_check_fc(struct bt_rfcomm_dlc *dlc)
554588
*/
555589
k_sem_give(&dlc->session->fc);
556590
k_sem_give(&dlc->tx_credits);
591+
592+
LOG_DBG("Flow control available");
593+
return true;
557594
}
558595

559596
static void bt_rfcomm_tx_destroy(struct bt_rfcomm_dlc *dlc, struct net_buf *buf)
@@ -581,55 +618,60 @@ static void rfcomm_sent(struct bt_conn *conn, void *user_data, int err)
581618

582619
dlc = (struct bt_rfcomm_dlc *)user_data;
583620

621+
rfcomm_dlc_tx_trigger(dlc);
622+
584623
if (dlc && dlc->ops && dlc->ops->sent) {
585624
dlc->ops->sent(dlc, err);
586625
}
587626
}
588627

589-
static void rfcomm_dlc_tx_thread(void *p1, void *p2, void *p3)
628+
static void rfcomm_dlc_tx_worker(struct k_work *work)
590629
{
591-
struct bt_rfcomm_dlc *dlc = p1;
592-
k_timeout_t timeout = K_FOREVER;
630+
struct bt_rfcomm_dlc *dlc = CONTAINER_OF(work, struct bt_rfcomm_dlc, tx_work);
593631
struct net_buf *buf;
594632

595-
LOG_DBG("Started for dlc %p", dlc);
596-
597-
while (dlc->state == BT_RFCOMM_STATE_CONNECTED ||
598-
dlc->state == BT_RFCOMM_STATE_USER_DISCONNECT) {
599-
/* Get next packet for dlc */
600-
LOG_DBG("Wait for buf %p", dlc);
601-
buf = k_fifo_get(&dlc->tx_queue, timeout);
602-
/* If its dummy buffer or non user disconnect then break */
603-
if ((dlc->state != BT_RFCOMM_STATE_CONNECTED &&
604-
dlc->state != BT_RFCOMM_STATE_USER_DISCONNECT) ||
605-
!buf || !buf->len) {
606-
if (buf) {
607-
bt_rfcomm_tx_destroy(dlc, buf);
608-
net_buf_unref(buf);
609-
}
610-
break;
633+
LOG_DBG("Work for dlc %p state %u", dlc, dlc->state);
634+
635+
if (dlc->state < BT_RFCOMM_STATE_CONNECTED) {
636+
return;
637+
}
638+
639+
if (dlc->state == BT_RFCOMM_STATE_CONNECTED ||
640+
dlc->state == BT_RFCOMM_STATE_USER_DISCONNECT) {
641+
if (k_fifo_is_empty(&dlc->tx_queue) == true) {
642+
goto user_disconnect;
611643
}
612644

613-
rfcomm_check_fc(dlc);
614-
if (dlc->state != BT_RFCOMM_STATE_CONNECTED &&
615-
dlc->state != BT_RFCOMM_STATE_USER_DISCONNECT) {
616-
bt_rfcomm_tx_destroy(dlc, buf);
617-
net_buf_unref(buf);
618-
break;
645+
if (rfcomm_check_fc(dlc) == false) {
646+
LOG_DBG("FC or credit not available");
647+
goto user_disconnect;
619648
}
620649

650+
buf = k_fifo_get(&dlc->tx_queue, K_NO_WAIT);
651+
LOG_DBG("Tx buf %p", buf);
621652
if (rfcomm_send_cb(dlc->session, buf, rfcomm_sent, dlc) < 0) {
622653
/* This fails only if channel is disconnected */
623654
dlc->state = BT_RFCOMM_STATE_DISCONNECTED;
624655
bt_rfcomm_tx_destroy(dlc, buf);
625-
break;
656+
LOG_ERR("Failed to send buffer, disconnected");
657+
goto disconnect;
658+
}
659+
660+
if (k_fifo_is_empty(&dlc->tx_queue) == false) {
661+
rfcomm_dlc_tx_trigger(dlc);
662+
return;
626663
}
627664

665+
user_disconnect:
628666
if (dlc->state == BT_RFCOMM_STATE_USER_DISCONNECT) {
629-
timeout = K_NO_WAIT;
667+
LOG_DBG("Process user disconnect");
668+
goto disconnect;
630669
}
670+
671+
return;
631672
}
632673

674+
disconnect:
633675
LOG_DBG("dlc %p disconnected - cleaning up", dlc);
634676

635677
/* Give back any allocated buffers */
@@ -844,11 +886,7 @@ static void rfcomm_dlc_connected(struct bt_rfcomm_dlc *dlc)
844886
k_work_cancel_delayable(&dlc->rtx_work);
845887

846888
k_fifo_init(&dlc->tx_queue);
847-
k_thread_create(&dlc->tx_thread, dlc->stack,
848-
K_KERNEL_STACK_SIZEOF(dlc->stack),
849-
rfcomm_dlc_tx_thread, dlc, NULL, NULL, K_PRIO_COOP(7),
850-
0, K_NO_WAIT);
851-
k_thread_name_set(&dlc->tx_thread, "BT DLC");
889+
k_work_init(&dlc->tx_work, rfcomm_dlc_tx_worker);
852890

853891
if (dlc->ops && dlc->ops->connected) {
854892
dlc->ops->connected(dlc);
@@ -921,17 +959,7 @@ static int rfcomm_dlc_close(struct bt_rfcomm_dlc *dlc)
921959
break;
922960
case BT_RFCOMM_STATE_CONNECTED:
923961
dlc->state = BT_RFCOMM_STATE_DISCONNECTING;
924-
925-
/* Queue a dummy buffer to wake up and stop the
926-
* tx thread.
927-
*/
928-
k_fifo_put(&dlc->tx_queue,
929-
net_buf_alloc(&dummy_pool, K_NO_WAIT));
930-
931-
/* There could be a writer waiting for credits so return a
932-
* dummy credit to wake it up.
933-
*/
934-
rfcomm_dlc_tx_give_credits(dlc, 1);
962+
rfcomm_dlc_tx_trigger(dlc);
935963
break;
936964
case BT_RFCOMM_STATE_DISCONNECTING:
937965
case BT_RFCOMM_STATE_DISCONNECTED:
@@ -1178,6 +1206,7 @@ static void rfcomm_handle_msc(struct bt_rfcomm_session *session,
11781206
* thread in sem_take().
11791207
*/
11801208
k_sem_give(&dlc->tx_credits);
1209+
rfcomm_dlc_tx_trigger(dlc);
11811210
}
11821211
}
11831212

@@ -1410,6 +1439,7 @@ static void rfcomm_handle_msg(struct bt_rfcomm_session *session,
14101439
*/
14111440
k_sem_give(&session->fc);
14121441
rfcomm_send_fcon(session, BT_RFCOMM_MSG_RESP_CR);
1442+
rfcomm_dlcs_tx_trigger(session->dlcs);
14131443
break;
14141444
case BT_RFCOMM_FCOFF:
14151445
if (session->cfc == BT_RFCOMM_CFC_SUPPORTED) {
@@ -1544,6 +1574,7 @@ int bt_rfcomm_dlc_send(struct bt_rfcomm_dlc *dlc, struct net_buf *buf)
15441574
net_buf_add_u8(buf, fcs);
15451575

15461576
k_fifo_put(&dlc->tx_queue, buf);
1577+
rfcomm_dlc_tx_trigger(dlc);
15471578

15481579
return buf->len;
15491580
}
@@ -1802,9 +1833,7 @@ int bt_rfcomm_dlc_disconnect(struct bt_rfcomm_dlc *dlc)
18021833
* and stop the tx thread.
18031834
*/
18041835
dlc->state = BT_RFCOMM_STATE_USER_DISCONNECT;
1805-
k_fifo_put(&dlc->tx_queue,
1806-
net_buf_alloc(&dummy_pool, K_NO_WAIT));
1807-
1836+
rfcomm_dlc_tx_trigger(dlc);
18081837
k_work_reschedule(&dlc->rtx_work, RFCOMM_DISC_TIMEOUT);
18091838

18101839
return 0;

0 commit comments

Comments
 (0)