From 773222e365e8f66fd746851d93834330b1177427 Mon Sep 17 00:00:00 2001 From: Vinayak Kariappa Chettimada Date: Sun, 6 Jul 2025 07:23:44 +0200 Subject: [PATCH 1/4] Bluetooth: Controller: Fix stuck forced continuation under throughput Fix stuck forced continuation of connection event under high throughput scenario. At near supervision timeout the force flag is set, but in the next connection event if full connection interval is used for data transmission and a subsequent request to abort happens it was not honoured causing the tx-rx chain to keep continuing until supervision timeout. Signed-off-by: Vinayak Kariappa Chettimada --- .../controller/ll_sw/nordic/lll/lll_conn.c | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_conn.c b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_conn.c index ea5028a00f104..7a8397cc61b33 100644 --- a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_conn.c +++ b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_conn.c @@ -165,15 +165,16 @@ int lll_conn_central_is_abort_cb(void *next, void *curr, { struct lll_conn *lll = curr; - /* Do not abort if near supervision timeout */ - if (lll->forced) { - return 0; - } + if (next != curr) { + /* Do not be aborted by a different event if near supervision timeout */ + if ((lll->forced == 1U) && (trx_cnt < 1U)) { + return 0; + } - /* Do not be aborted by same event if a single central trx has not been - * exchanged. - */ - if ((next == curr) && (trx_cnt < 1U)) { + } else if (trx_cnt < 1U) { + /* Do not be aborted by same event if a single central's Rx has not completed. + * Cases where single trx duration can be greater than connection interval. + */ return -EBUSY; } @@ -187,15 +188,16 @@ int lll_conn_peripheral_is_abort_cb(void *next, void *curr, { struct lll_conn *lll = curr; - /* Do not abort if near supervision timeout */ - if (lll->forced) { - return 0; - } + if (next != curr) { + /* Do not be aborted by a different event if near supervision timeout */ + if ((lll->forced == 1U) && (tx_cnt < 1U)) { + return 0; + } - /* Do not be aborted by same event if a single peripheral trx has not - * been exchanged. - */ - if ((next == curr) && (tx_cnt < 1U)) { + } else if (tx_cnt < 1U) { + /* Do not be aborted by same event if a single peripheral's Tx has not completed. + * Cases where single trx duration can be greater than connection interval. + */ return -EBUSY; } From 5d5e33785002d010defa645e6874359534a2bc5d Mon Sep 17 00:00:00 2001 From: Vinayak Kariappa Chettimada Date: Fri, 11 Jul 2025 09:03:52 +0200 Subject: [PATCH 2/4] Bluetooth: Controller: Fix missing radio status reset Fix missing radio status reset on early abort of central and peripheral prepare. Signed-off-by: Vinayak Kariappa Chettimada --- subsys/bluetooth/controller/ll_sw/nordic/lll/lll.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll.c b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll.c index 7f0a01d64b4f7..8c5f45d78d1f3 100644 --- a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll.c +++ b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll.c @@ -807,6 +807,8 @@ void lll_isr_early_abort(void *param) { int err; + radio_status_reset(); + radio_isr_set(isr_race, param); if (!radio_is_idle()) { radio_disable(); From ddc2f48cb26d2906a5f1bcf1458ec79b2d5af212 Mon Sep 17 00:00:00 2001 From: Vinayak Kariappa Chettimada Date: Fri, 11 Jul 2025 12:27:43 +0200 Subject: [PATCH 3/4] Bluetooth: Controller: Disable connection event continuation on overlap Disable connection event continuation on overlap with same connection's next event. This implementation has bugs and needs to be fixed in subsequent commits. Signed-off-by: Vinayak Kariappa Chettimada --- .../controller/ll_sw/nordic/lll/lll_conn.c | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_conn.c b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_conn.c index 7a8397cc61b33..5a16be942d614 100644 --- a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_conn.c +++ b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_conn.c @@ -69,6 +69,7 @@ static uint8_t crc_valid; static uint8_t is_aborted; static uint16_t tx_cnt; static uint16_t trx_cnt; +static uint8_t trx_busy_iteration; #if defined(CONFIG_BT_CTLR_LE_ENC) static uint8_t mic_state; @@ -153,6 +154,7 @@ void lll_conn_prepare_reset(void) crc_valid = 0U; crc_expire = 0U; is_aborted = 0U; + trx_busy_iteration = 0U; #if defined(CONFIG_BT_CTLR_LE_ENC) mic_state = LLL_CONN_MIC_NONE; @@ -160,6 +162,13 @@ void lll_conn_prepare_reset(void) } #if defined(CONFIG_BT_CENTRAL) +/* Number of times central event being aborted by same event instance be skipped */ +/* FIXME: Increasing this causes event pipeline overflow assertion, add LLL implementation to + * gracefully abort the deferred next event when -EBUSY is returned in this is_abort_cb + * interface. + */ +#define CENTRAL_TRX_BUSY_ITERATION_MAX 0 + int lll_conn_central_is_abort_cb(void *next, void *curr, lll_prepare_cb_t *resume_cb) { @@ -171,7 +180,9 @@ int lll_conn_central_is_abort_cb(void *next, void *curr, return 0; } - } else if (trx_cnt < 1U) { + } else if ((trx_cnt < 1U) && (trx_busy_iteration < CENTRAL_TRX_BUSY_ITERATION_MAX)) { + trx_busy_iteration++; + /* Do not be aborted by same event if a single central's Rx has not completed. * Cases where single trx duration can be greater than connection interval. */ @@ -183,6 +194,13 @@ int lll_conn_central_is_abort_cb(void *next, void *curr, #endif /* CONFIG_BT_CENTRAL */ #if defined(CONFIG_BT_PERIPHERAL) +/* Number of times peripheral event being aborted by same event instance be skipped */ +/* FIXME: Increasing this causes event pipeline overflow assertion, add LLL implementation to + * gracefully abort the deferred next event when -EBUSY is returned in this is_abort_cb + * interface. + */ +#define PERIPHERAL_TRX_BUSY_ITERATION_MAX 0 + int lll_conn_peripheral_is_abort_cb(void *next, void *curr, lll_prepare_cb_t *resume_cb) { @@ -194,7 +212,9 @@ int lll_conn_peripheral_is_abort_cb(void *next, void *curr, return 0; } - } else if (tx_cnt < 1U) { + } else if ((tx_cnt < 1U) && (trx_busy_iteration < PERIPHERAL_TRX_BUSY_ITERATION_MAX)) { + trx_busy_iteration++; + /* Do not be aborted by same event if a single peripheral's Tx has not completed. * Cases where single trx duration can be greater than connection interval. */ From eda2a77153312076d442fc27706bcf6c73766ad6 Mon Sep 17 00:00:00 2001 From: Vinayak Kariappa Chettimada Date: Fri, 11 Jul 2025 16:12:59 +0200 Subject: [PATCH 4/4] Bluetooth: Controller: Introduce prepare deferred feature Introduce prepare being deferred due to previous events desire to continue. In such case the deferred prepare param will have the defer flag set. Signed-off-by: Vinayak Kariappa Chettimada --- subsys/bluetooth/controller/ll_sw/lll.h | 3 ++- subsys/bluetooth/controller/ll_sw/lll_common.c | 2 ++ .../bluetooth/controller/ll_sw/nordic/lll/lll.c | 4 +++- .../controller/ll_sw/nordic/lll/lll_central.c | 16 ++++++++++++++-- .../controller/ll_sw/nordic/lll/lll_conn.c | 4 ++-- .../controller/ll_sw/nordic/lll/lll_peripheral.c | 16 ++++++++++++++-- 6 files changed, 37 insertions(+), 8 deletions(-) diff --git a/subsys/bluetooth/controller/ll_sw/lll.h b/subsys/bluetooth/controller/ll_sw/lll.h index 9b890315cfd91..fe938db4728d1 100644 --- a/subsys/bluetooth/controller/ll_sw/lll.h +++ b/subsys/bluetooth/controller/ll_sw/lll.h @@ -236,7 +236,8 @@ struct lll_prepare_param { #if defined(CONFIG_BT_CTLR_JIT_SCHEDULING) int8_t prio; #endif /* CONFIG_BT_CTLR_JIT_SCHEDULING */ - uint8_t force; + uint8_t force:1; + uint8_t defer:1; void *param; }; diff --git a/subsys/bluetooth/controller/ll_sw/lll_common.c b/subsys/bluetooth/controller/ll_sw/lll_common.c index ed35b4ddd0837..c6c561dbb76dc 100644 --- a/subsys/bluetooth/controller/ll_sw/lll_common.c +++ b/subsys/bluetooth/controller/ll_sw/lll_common.c @@ -62,6 +62,8 @@ int lll_prepare(lll_is_abort_cb_t is_abort_cb, lll_abort_cb_t abort_cb, prepare_param->prio = prio; #endif /* CONFIG_BT_CTLR_JIT_SCHEDULING */ + prepare_param->defer = 0U; + err = lll_prepare_resolve(is_abort_cb, abort_cb, prepare_cb, prepare_param, 0U, 0U); return err; diff --git a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll.c b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll.c index 8c5f45d78d1f3..bd7464b776726 100644 --- a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll.c +++ b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll.c @@ -1318,7 +1318,9 @@ static void preempt(void *param) /* Returns -EBUSY when same curr and next state/role, do not * abort same curr and next event. */ - if (err != -EBUSY) { + if (err == -EBUSY) { + ready->prepare_param.defer = 1U; + } else { /* Let preemptor LLL know about the cancelled prepare */ ready->is_aborted = 1; ready->abort_cb(&ready->prepare_param, ready->prepare_param.param); diff --git a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_central.c b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_central.c index 0fd8a368047e1..8935b8fc409a8 100644 --- a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_central.c +++ b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_central.c @@ -247,12 +247,24 @@ static int prepare_cb(struct lll_prepare_param *p) overhead = lll_preempt_calc(ull, (TICKER_ID_CONN_BASE + lll->handle), ticks_at_event); /* check if preempt to start has changed */ if (overhead) { - LL_ASSERT_OVERHEAD(overhead); + int err; + + if (p->defer == 1U) { + /* We accept the overhead and abort the event; previous event has decided to + * continue and has deferred this event beyond the real time threshold to + * have the radio prepared for this event. + */ + err = 0; + } else { + LL_ASSERT_OVERHEAD(overhead); + + err = -ECANCELED; + } radio_isr_set(lll_isr_abort, lll); radio_disable(); - return -ECANCELED; + return err; } #endif /* !CONFIG_BT_CTLR_XTAL_ADVANCED */ diff --git a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_conn.c b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_conn.c index 5a16be942d614..cf035abedfc9d 100644 --- a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_conn.c +++ b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_conn.c @@ -167,7 +167,7 @@ void lll_conn_prepare_reset(void) * gracefully abort the deferred next event when -EBUSY is returned in this is_abort_cb * interface. */ -#define CENTRAL_TRX_BUSY_ITERATION_MAX 0 +#define CENTRAL_TRX_BUSY_ITERATION_MAX 1 int lll_conn_central_is_abort_cb(void *next, void *curr, lll_prepare_cb_t *resume_cb) @@ -199,7 +199,7 @@ int lll_conn_central_is_abort_cb(void *next, void *curr, * gracefully abort the deferred next event when -EBUSY is returned in this is_abort_cb * interface. */ -#define PERIPHERAL_TRX_BUSY_ITERATION_MAX 0 +#define PERIPHERAL_TRX_BUSY_ITERATION_MAX 1 int lll_conn_peripheral_is_abort_cb(void *next, void *curr, lll_prepare_cb_t *resume_cb) diff --git a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_peripheral.c b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_peripheral.c index 0ed7a63af8d8e..9d569b28116fd 100644 --- a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_peripheral.c +++ b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_peripheral.c @@ -339,12 +339,24 @@ static int prepare_cb(struct lll_prepare_param *p) overhead = lll_preempt_calc(ull, (TICKER_ID_CONN_BASE + lll->handle), ticks_at_event); /* check if preempt to start has changed */ if (overhead) { - LL_ASSERT_OVERHEAD(overhead); + int err; + + if (p->defer == 1U) { + /* We accept the overhead and abort the event; previous event has decided to + * continue and has deferred this event beyond the real time threshold to + * have the radio prepared for this event. + */ + err = 0; + } else { + LL_ASSERT_OVERHEAD(overhead); + + err = -ECANCELED; + } radio_isr_set(lll_isr_abort, lll); radio_disable(); - return -ECANCELED; + return err; } #endif /* CONFIG_BT_CTLR_XTAL_ADVANCED */