Skip to content

Commit 7d45497

Browse files
cvinayakmbolivar-nordic
authored andcommitted
[nrf fromtree] Bluetooth: Controller: Fix scanner window close by using lll_disable
Fix race condition in setting up ISR callback and parameter caused between ULL_HIGH and LLL context. As LLL IRQ is not disabled the parameter and ISR callback would get out of sync causing incorrect parameter supplied to callback and hence leading to development assert in ull_scan_done(). Signed-off-by: Vinayak Kariappa Chettimada <[email protected]> (cherry picked from commit d367447) (cherry picked from commit 073483a)
1 parent f7d4cfa commit 7d45497

File tree

3 files changed

+57
-82
lines changed

3 files changed

+57
-82
lines changed

subsys/bluetooth/controller/ll_sw/nordic/lll/lll.c

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -375,9 +375,14 @@ void lll_done_ull_inc(void)
375375
}
376376
#endif /* CONFIG_BT_CTLR_LOW_LAT_ULL_DONE */
377377

378-
bool lll_is_done(void *param)
378+
bool lll_is_done(void *param, bool *is_resume)
379379
{
380-
/* FIXME: use param to check */
380+
/* NOTE: Current radio event when preempted could put itself in resume
381+
* into the prepare pipeline in which case event.curr.param would
382+
* be set to NULL.
383+
*/
384+
*is_resume = (param != event.curr.param);
385+
381386
return !event.curr.abort_cb;
382387
}
383388

@@ -731,6 +736,10 @@ static struct lll_event *resume_enqueue(lll_prepare_cb_t resume_cb)
731736
{
732737
struct lll_prepare_param prepare_param = {0};
733738

739+
/* Enqueue into prepare pipeline as resume radio event, and remove
740+
* parameter assignment from currently active radio event so that
741+
* done event is not generated.
742+
*/
734743
prepare_param.param = event.curr.param;
735744
event.curr.param = NULL;
736745

subsys/bluetooth/controller/ll_sw/nordic/lll/lll_internal.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
int lll_prepare_done(void *param);
88
int lll_done(void *param);
9-
bool lll_is_done(void *param);
9+
bool lll_is_done(void *param, bool *is_resume);
1010
int lll_is_abort_cb(void *next, void *curr, lll_prepare_cb_t *resume_cb);
1111
void lll_abort_cb(struct lll_prepare_param *prepare_param, void *param);
1212

subsys/bluetooth/controller/ll_sw/nordic/lll/lll_scan.c

Lines changed: 45 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,6 @@ static void isr_abort(void *param);
7979
* (EVENT_OVERHEAD_PREEMPT_US <= EVENT_OVERHEAD_PREEMPT_MIN_US)
8080
*/
8181
static void isr_done_cleanup(void *param);
82-
static void isr_cleanup(void *param);
8382

8483
static inline int isr_rx_pdu(struct lll_scan *lll, struct pdu_adv *pdu_adv_rx,
8584
uint8_t devmatch_ok, uint8_t devmatch_id,
@@ -620,11 +619,8 @@ static void abort_cb(struct lll_prepare_param *prepare_param, void *param)
620619
cpu_sleep();
621620
}
622621
#endif /* CONFIG_BT_CENTRAL */
623-
} else if (IS_ENABLED(CONFIG_BT_CTLR_LOW_LAT)) {
624-
radio_isr_set(isr_done_cleanup, param);
625-
radio_disable();
626622
} else {
627-
radio_isr_set(isr_cleanup, param);
623+
radio_isr_set(isr_done_cleanup, param);
628624
radio_disable();
629625
}
630626
return;
@@ -640,10 +636,17 @@ static void abort_cb(struct lll_prepare_param *prepare_param, void *param)
640636
}
641637

642638
static void ticker_stop_cb(uint32_t ticks_at_expire, uint32_t ticks_drift,
643-
uint32_t remainder, uint16_t lazy, uint8_t force, void *param)
639+
uint32_t remainder, uint16_t lazy, uint8_t force,
640+
void *param)
644641
{
645-
radio_isr_set(isr_done_cleanup, param);
646-
radio_disable();
642+
static memq_link_t link;
643+
static struct mayfly mfy = {0, 0, &link, NULL, lll_disable};
644+
uint32_t ret;
645+
646+
mfy.param = param;
647+
ret = mayfly_enqueue(TICKER_USER_ID_ULL_HIGH, TICKER_USER_ID_LLL, 0,
648+
&mfy);
649+
LL_ASSERT(!ret);
647650
}
648651

649652
static void ticker_op_start_cb(uint32_t status, void *param)
@@ -982,14 +985,15 @@ static void isr_abort(void *param)
982985
static void isr_done_cleanup(void *param)
983986
{
984987
struct lll_scan *lll;
988+
bool is_resume;
985989

986990
/* Clear radio status and events */
987991
lll_isr_status_reset();
988992

989993
/* Under race between duration expire, is_stop is set in this function,
990994
* and event preemption, prevent generating duplicate scan done events.
991995
*/
992-
if (lll_is_done(param)) {
996+
if (lll_is_done(param, &is_resume)) {
993997
return;
994998
}
995999

@@ -1009,67 +1013,26 @@ static void isr_done_cleanup(void *param)
10091013
ticker_stop(TICKER_INSTANCE_ID_CTLR, TICKER_USER_ID_LLL,
10101014
TICKER_ID_SCAN_STOP, NULL, NULL);
10111015

1012-
#if defined(CONFIG_BT_CTLR_ADV_EXT)
1013-
struct event_done_extra *extra;
1014-
1015-
/* Generate Scan done events so that duration and max expiry is
1016-
* detected in ULL.
1017-
*/
1018-
extra = ull_done_extra_type_set(EVENT_DONE_EXTRA_TYPE_SCAN);
1019-
LL_ASSERT(extra);
1016+
#if defined(CONFIG_BT_CTLR_SCAN_INDICATION)
1017+
struct node_rx_hdr *node_rx;
10201018

1021-
/* Prevent scan events in pipeline from being scheduled if duration has
1022-
* expired.
1019+
/* Check if there are enough free node rx available:
1020+
* 1. For generating this scan indication
1021+
* 2. Keep one available free for reception of ACL connection Rx data
1022+
* 3. Keep one available free for reception on ACL connection to NACK
1023+
* the PDU
10231024
*/
1024-
if (unlikely(lll->duration_reload && !lll->duration_expire)) {
1025-
lll->is_stop = 1U;
1026-
}
1027-
1028-
if (lll->is_aux_sched) {
1029-
struct node_rx_pdu *node_rx;
1030-
1031-
lll->is_aux_sched = 0U;
1032-
1033-
node_rx = ull_pdu_rx_alloc();
1034-
LL_ASSERT(node_rx);
1035-
1036-
node_rx->hdr.type = NODE_RX_TYPE_EXT_AUX_RELEASE;
1025+
node_rx = ull_pdu_rx_alloc_peek(3);
1026+
if (node_rx) {
1027+
ull_pdu_rx_alloc();
10371028

1038-
node_rx->hdr.rx_ftr.param = lll;
1029+
/* TODO: add other info by defining a payload struct */
1030+
node_rx->type = NODE_RX_TYPE_SCAN_INDICATION;
10391031

1040-
ull_rx_put(node_rx->hdr.link, node_rx);
1032+
ull_rx_put(node_rx->link, node_rx);
10411033
ull_rx_sched();
10421034
}
1043-
#endif /* CONFIG_BT_CTLR_ADV_EXT */
1044-
1045-
lll_isr_cleanup(param);
1046-
}
1047-
1048-
static void isr_cleanup(void *param)
1049-
{
1050-
/* Clear radio status and events */
1051-
lll_isr_status_reset();
1052-
1053-
/* Do not generate done event for connection initiation, ULL will
1054-
* disable the event when establishing/setting up the connection.
1055-
* Also, do not generate done event when duration expire, as ULL
1056-
* will disable the event.
1057-
* As it was transmission of CONNECT_IND, there is not filters to
1058-
* disable either.
1059-
*/
1060-
if (lll_is_done(param)) {
1061-
return;
1062-
}
1063-
1064-
/* Disable Rx filters, if cleanup after being in Rx state */
1065-
radio_filter_disable();
1066-
1067-
/* Scanner stop can expire while here in this ISR.
1068-
* Deferred attempt to stop can fail as it would have
1069-
* expired, hence ignore failure.
1070-
*/
1071-
ticker_stop(TICKER_INSTANCE_ID_CTLR, TICKER_USER_ID_LLL,
1072-
TICKER_ID_SCAN_STOP, NULL, NULL);
1035+
#endif /* CONFIG_BT_CTLR_SCAN_INDICATION */
10731036

10741037
#if defined(CONFIG_BT_HCI_MESH_EXT)
10751038
if (_radio.advertiser.is_enabled && _radio.advertiser.is_mesh &&
@@ -1078,22 +1041,26 @@ static void isr_cleanup(void *param)
10781041
}
10791042
#endif /* CONFIG_BT_HCI_MESH_EXT */
10801043

1081-
#if defined(CONFIG_BT_CTLR_SCAN_INDICATION)
1082-
struct node_rx_hdr *node_rx = ull_pdu_rx_alloc_peek(3);
1083-
1084-
if (node_rx) {
1085-
ull_pdu_rx_alloc();
1086-
1087-
/* TODO: add other info by defining a payload struct */
1088-
node_rx->type = NODE_RX_TYPE_SCAN_INDICATION;
1044+
#if defined(CONFIG_BT_CTLR_ADV_EXT)
1045+
/* If continuous scan then do not generate scan done when radio event
1046+
* has been placed into prepare pipeline as a resume radio event.
1047+
*/
1048+
if (!is_resume) {
1049+
struct event_done_extra *extra;
10891050

1090-
ull_rx_put(node_rx->link, node_rx);
1091-
ull_rx_sched();
1051+
/* Generate Scan done events so that duration and max expiry is
1052+
* detected in ULL.
1053+
*/
1054+
extra = ull_done_extra_type_set(EVENT_DONE_EXTRA_TYPE_SCAN);
1055+
LL_ASSERT(extra);
10921056
}
1093-
#endif /* CONFIG_BT_CTLR_SCAN_INDICATION */
10941057

1095-
#if defined(CONFIG_BT_CTLR_ADV_EXT)
1096-
struct lll_scan *lll = param;
1058+
/* Prevent scan events in pipeline from being scheduled if duration has
1059+
* expired.
1060+
*/
1061+
if (unlikely(lll->duration_reload && !lll->duration_expire)) {
1062+
lll->is_stop = 1U;
1063+
}
10971064

10981065
if (lll->is_aux_sched) {
10991066
struct node_rx_pdu *node_rx;
@@ -1112,7 +1079,6 @@ static void isr_cleanup(void *param)
11121079
}
11131080
#endif /* CONFIG_BT_CTLR_ADV_EXT */
11141081

1115-
11161082
lll_isr_cleanup(param);
11171083
}
11181084

@@ -1206,7 +1172,7 @@ static inline int isr_rx_pdu(struct lll_scan *lll, struct pdu_adv *pdu_adv_rx,
12061172
lll_prof_cputime_capture();
12071173
}
12081174

1209-
radio_isr_set(isr_cleanup, lll);
1175+
radio_isr_set(isr_done_cleanup, lll);
12101176

12111177
#if defined(HAL_RADIO_GPIO_HAVE_PA_PIN)
12121178
if (IS_ENABLED(CONFIG_BT_CTLR_PROFILE_ISR)) {

0 commit comments

Comments
 (0)