Skip to content

Commit f164b38

Browse files
committed
Bluetooth: Controller: Fix assert on role stop/abort
Call to ticker_stop/update can fail under the condition where in a role is being stopped but at the same time it is preempted by the role event that also uses ticker_stop/ update. Also if a role closes graceful while it is being stopped, the radio ISR will process the stop state with no active role at that instance in time. In this case just reset the state to none, the role has already been gracefully closed before this ISR execution. The above applies to aborting a role event too. This commit adds code to detect these conditions and deterministically recover from it. This commit fixes the assert observed while stopping advertiser in the Bluetooth sample scan_adv. Jira: ZEP-1852 Change-id: I51c8d6e212ef43e3526a199cf7b666a79729c732 Signed-off-by: Vinayak Chettimada <[email protected]>
1 parent 30520fe commit f164b38

File tree

1 file changed

+144
-47
lines changed
  • subsys/bluetooth/controller/ll

1 file changed

+144
-47
lines changed

subsys/bluetooth/controller/ll/ctrl.c

Lines changed: 144 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,8 @@ static struct {
134134

135135
uint8_t volatile ticker_id_prepare;
136136
uint8_t volatile ticker_id_event;
137+
uint8_t volatile ticker_id_stop;
138+
137139
enum role volatile role;
138140
enum state state;
139141

@@ -214,6 +216,10 @@ static uint16_t const gc_lookup_ppm[] = { 500, 250, 150, 100, 75, 50, 30, 20 };
214216

215217
static void common_init(void);
216218
static void ticker_success_assert(uint32_t status, void *params);
219+
static void ticker_stop_adv_assert(uint32_t status, void *params);
220+
static void ticker_stop_obs_assert(uint32_t status, void *params);
221+
static void ticker_update_adv_assert(uint32_t status, void *params);
222+
static void ticker_update_slave_assert(uint32_t status, void *params);
217223
static void event_inactive(uint32_t ticks_at_expire, uint32_t remainder,
218224
uint16_t lazy, void *context);
219225
static void adv_setup(void);
@@ -694,23 +700,21 @@ static inline uint32_t isr_rx_adv(uint8_t devmatch_ok, uint8_t irkmatch_ok,
694700
ticker_status = ticker_stop(RADIO_TICKER_INSTANCE_ID_RADIO,
695701
RADIO_TICKER_USER_ID_WORKER,
696702
RADIO_TICKER_ID_ADV,
697-
ticker_success_assert, (void *)__LINE__);
698-
LL_ASSERT((ticker_status == TICKER_STATUS_SUCCESS) ||
699-
(ticker_status == TICKER_STATUS_BUSY));
703+
ticker_stop_adv_assert,
704+
(void *)__LINE__);
705+
ticker_stop_adv_assert(ticker_status, (void *)__LINE__);
700706

701707
/* Stop Direct Adv Stopper */
702708
_pdu_adv = (struct pdu_adv *)&_radio.advertiser.adv_data.data
703709
[_radio.advertiser.adv_data.first][0];
704710
if (_pdu_adv->type == PDU_ADV_TYPE_DIRECT_IND) {
705-
ticker_status =
706-
ticker_stop(
707-
RADIO_TICKER_INSTANCE_ID_RADIO,
708-
RADIO_TICKER_USER_ID_WORKER,
709-
RADIO_TICKER_ID_ADV_STOP,
710-
0, /* @todo ticker_success_assert */
711-
0 /* @todo (void *) __LINE__*/);
712-
LL_ASSERT((ticker_status == TICKER_STATUS_SUCCESS) ||
713-
(ticker_status == TICKER_STATUS_BUSY));
711+
/* Advertiser stop can expire while here in this ISR.
712+
* Deferred attempt to stop can fail as it would have
713+
* expired, hence ignore failure.
714+
*/
715+
ticker_stop(RADIO_TICKER_INSTANCE_ID_RADIO,
716+
RADIO_TICKER_USER_ID_WORKER,
717+
RADIO_TICKER_ID_ADV_STOP, NULL, NULL);
714718
}
715719

716720
/* Start Slave */
@@ -901,21 +905,23 @@ static inline uint32_t isr_rx_obs(uint8_t irkmatch_id, uint8_t rssi_ready)
901905
conn->hdr.ticks_xtal_to_start :
902906
conn->hdr.ticks_active_to_start;
903907

904-
/* Stop Observer and start Master */
908+
/* Stop Observer */
905909
ticker_status = ticker_stop(RADIO_TICKER_INSTANCE_ID_RADIO,
906910
RADIO_TICKER_USER_ID_WORKER,
907911
RADIO_TICKER_ID_OBS,
908-
ticker_success_assert,
912+
ticker_stop_obs_assert,
909913
(void *)__LINE__);
910-
LL_ASSERT((ticker_status == TICKER_STATUS_SUCCESS) ||
911-
(ticker_status == TICKER_STATUS_BUSY));
912-
ticker_status = ticker_stop(RADIO_TICKER_INSTANCE_ID_RADIO,
913-
RADIO_TICKER_USER_ID_WORKER,
914-
RADIO_TICKER_ID_OBS_STOP,
915-
0, /* @todo ticker_success_assert */
916-
0 /* @todo (void *) __LINE__ */);
917-
LL_ASSERT((ticker_status == TICKER_STATUS_SUCCESS) ||
918-
(ticker_status == TICKER_STATUS_BUSY));
914+
ticker_stop_obs_assert(ticker_status, (void *)__LINE__);
915+
916+
/* Observer stop can expire while here in this ISR.
917+
* Deferred attempt to stop can fail as it would have
918+
* expired, hence ignore failure.
919+
*/
920+
ticker_stop(RADIO_TICKER_INSTANCE_ID_RADIO,
921+
RADIO_TICKER_USER_ID_WORKER,
922+
RADIO_TICKER_ID_OBS_STOP, NULL, NULL);
923+
924+
/* Start master */
919925
ticker_status =
920926
ticker_start(RADIO_TICKER_INSTANCE_ID_RADIO,
921927
RADIO_TICKER_USER_ID_WORKER,
@@ -2102,16 +2108,25 @@ static inline uint32_t isr_close_adv(void)
21022108
/** @todo use random 0-10 */
21032109
random_delay = 10;
21042110

2111+
/* Call to ticker_update can fail under the race
2112+
* condition where in the Adv role is being stopped but
2113+
* at the same time it is preempted by Adv event that
2114+
* gets into close state. Accept failure when Adv role
2115+
* is being stopped.
2116+
*/
21052117
ticker_status =
21062118
ticker_update(RADIO_TICKER_INSTANCE_ID_RADIO,
21072119
RADIO_TICKER_USER_ID_WORKER,
21082120
RADIO_TICKER_ID_ADV,
2109-
TICKER_US_TO_TICKS(random_delay * 1000),
2121+
TICKER_US_TO_TICKS(random_delay *
2122+
1000),
21102123
0, 0, 0, 0, 0,
2111-
ticker_success_assert,
2124+
ticker_update_adv_assert,
21122125
(void *)__LINE__);
21132126
LL_ASSERT((ticker_status == TICKER_STATUS_SUCCESS) ||
2114-
(ticker_status == TICKER_STATUS_BUSY));
2127+
(ticker_status == TICKER_STATUS_BUSY) ||
2128+
(_radio.ticker_id_stop ==
2129+
RADIO_TICKER_ID_ADV));
21152130
}
21162131
}
21172132

@@ -2139,20 +2154,16 @@ static inline uint32_t isr_close_obs(void)
21392154

21402155
radio_tmr_end_capture();
21412156
} else {
2142-
uint32_t ticker_status;
2143-
21442157
radio_filter_disable();
21452158

21462159
if (_radio.state == STATE_ABORT) {
2147-
ticker_status =
2148-
ticker_stop(
2149-
RADIO_TICKER_INSTANCE_ID_RADIO,
2150-
RADIO_TICKER_USER_ID_WORKER,
2151-
RADIO_TICKER_ID_OBS_STOP,
2152-
0 /** @todo ticker_success_assert */,
2153-
0 /** @todo (void *) __LINE__ */);
2154-
LL_ASSERT((ticker_status == TICKER_STATUS_SUCCESS) ||
2155-
(ticker_status == TICKER_STATUS_BUSY));
2160+
/* Observer stop can expire while here in this ISR.
2161+
* Deferred attempt to stop can fail as it would have
2162+
* expired, hence ignore failure.
2163+
*/
2164+
ticker_stop(RADIO_TICKER_INSTANCE_ID_RADIO,
2165+
RADIO_TICKER_USER_ID_WORKER,
2166+
RADIO_TICKER_ID_OBS_STOP, NULL, NULL);
21562167
}
21572168
}
21582169

@@ -2423,18 +2434,25 @@ static inline void isr_close_conn(void)
24232434
if ((ticks_drift_plus != 0) || (ticks_drift_minus != 0) ||
24242435
(lazy != 0) || (force != 0)) {
24252436
uint32_t ticker_status;
2426-
2437+
uint8_t ticker_id = RADIO_TICKER_ID_FIRST_CONNECTION +
2438+
_radio.conn_curr->handle;
2439+
2440+
/* Call to ticker_update can fail under the race
2441+
* condition where in the Slave role is being stopped but
2442+
* at the same time it is preempted by Slave event that
2443+
* gets into close state. Accept failure when Slave role
2444+
* is being stopped.
2445+
*/
24272446
ticker_status =
24282447
ticker_update(RADIO_TICKER_INSTANCE_ID_RADIO,
24292448
RADIO_TICKER_USER_ID_WORKER,
2430-
RADIO_TICKER_ID_FIRST_CONNECTION +
2431-
_radio.conn_curr->handle,
2449+
ticker_id,
24322450
ticks_drift_plus, ticks_drift_minus, 0, 0,
2433-
lazy, force,
2434-
0 /** @todo ticker_success_assert */,
2435-
0 /** @todo (void *) __LINE__ */);
2451+
lazy, force, ticker_update_slave_assert,
2452+
(void *)(uint32_t)ticker_id);
24362453
LL_ASSERT((ticker_status == TICKER_STATUS_SUCCESS) ||
2437-
(ticker_status == TICKER_STATUS_BUSY));
2454+
(ticker_status == TICKER_STATUS_BUSY) ||
2455+
(_radio.ticker_id_stop == ticker_id));
24382456
}
24392457
}
24402458

@@ -2457,6 +2475,20 @@ static inline void isr_radio_state_close(void)
24572475
break;
24582476

24592477
case ROLE_NONE:
2478+
/* If a role closes graceful while it is being stopped, then
2479+
* Radio ISR will be triggered to process the stop state with
2480+
* no active role at that instance in time.
2481+
* Just reset the state to none. The role has gracefully closed
2482+
* before this ISR run.
2483+
* The above applies to aborting a role event too.
2484+
*/
2485+
LL_ASSERT((_radio.state == STATE_STOP) ||
2486+
(_radio.state == STATE_ABORT));
2487+
2488+
_radio.state = STATE_NONE;
2489+
2490+
return;
2491+
24602492
default:
24612493
LL_ASSERT(0);
24622494
break;
@@ -2569,6 +2601,60 @@ static void ticker_success_assert(uint32_t status, void *params)
25692601
LL_ASSERT(status == TICKER_STATUS_SUCCESS);
25702602
}
25712603

2604+
static void ticker_stop_adv_assert(uint32_t status, void *params)
2605+
{
2606+
ARG_UNUSED(params);
2607+
2608+
if (status == TICKER_STATUS_FAILURE) {
2609+
if (_radio.ticker_id_stop == RADIO_TICKER_ID_ADV) {
2610+
/* ticker_stop failed due to race condition
2611+
* while in role_disable. Let the role_disable
2612+
* be made aware of, so it can return failure
2613+
* (to stop Adv role as it is now transitioned
2614+
* to Slave role).
2615+
*/
2616+
_radio.ticker_id_stop = 0;
2617+
} else {
2618+
LL_ASSERT(0);
2619+
}
2620+
}
2621+
}
2622+
2623+
static void ticker_stop_obs_assert(uint32_t status, void *params)
2624+
{
2625+
ARG_UNUSED(params);
2626+
2627+
if (status == TICKER_STATUS_FAILURE) {
2628+
if (_radio.ticker_id_stop == RADIO_TICKER_ID_OBS) {
2629+
/* ticker_stop failed due to race condition
2630+
* while in role_disable. Let the role_disable
2631+
* be made aware of, so it can return failure
2632+
* (to stop Obs role as it is now transitioned
2633+
* to Master role).
2634+
*/
2635+
_radio.ticker_id_stop = 0;
2636+
} else {
2637+
LL_ASSERT(0);
2638+
}
2639+
}
2640+
}
2641+
2642+
static void ticker_update_adv_assert(uint32_t status, void *params)
2643+
{
2644+
ARG_UNUSED(params);
2645+
2646+
LL_ASSERT((status == TICKER_STATUS_SUCCESS) ||
2647+
(_radio.ticker_id_stop == RADIO_TICKER_ID_ADV));
2648+
}
2649+
2650+
static void ticker_update_slave_assert(uint32_t status, void *params)
2651+
{
2652+
uint8_t ticker_id = (uint32_t)params & 0xFF;
2653+
2654+
LL_ASSERT((status == TICKER_STATUS_SUCCESS) ||
2655+
(_radio.ticker_id_stop == ticker_id));
2656+
}
2657+
25722658
static void work_radio_active(void *params)
25732659
{
25742660
static uint8_t s_active;
@@ -3534,7 +3620,7 @@ static void event_common_prepare(uint32_t ticks_at_expire,
35343620
#endif
35353621
#undef RADIO_DEFERRED_PREEMPT
35363622

3537-
/** Handle change in _ticks_active_to_start */
3623+
/** Handle change in _ticks_active_to_start */
35383624
if (_radio.ticks_active_to_start != _ticks_active_to_start) {
35393625
uint32_t ticks_to_start_new =
35403626
((_radio.ticks_active_to_start <
@@ -6585,6 +6671,10 @@ static uint32_t role_disable(uint8_t ticker_id_primary,
65856671
break;
65866672
}
65876673

6674+
LL_ASSERT(!_radio.ticker_id_stop);
6675+
6676+
_radio.ticker_id_stop = ticker_id_primary;
6677+
65886678
/* Step 1: Is Primary started? Stop the Primary ticker */
65896679
ticker_status =
65906680
ticker_stop(RADIO_TICKER_INSTANCE_ID_RADIO,
@@ -6605,7 +6695,7 @@ static uint32_t role_disable(uint8_t ticker_id_primary,
66056695
}
66066696

66076697
if (ticker_status != TICKER_STATUS_SUCCESS) {
6608-
return 1;
6698+
goto role_disable_cleanup;
66096699
}
66106700

66116701
/* Inside our event, gracefully handle XTAL and Radio actives */
@@ -6616,7 +6706,14 @@ static uint32_t role_disable(uint8_t ticker_id_primary,
66166706
ticks_xtal_to_start, ticks_active_to_start);
66176707
}
66186708

6619-
return 0;
6709+
if (!_radio.ticker_id_stop) {
6710+
ticker_status = TICKER_STATUS_FAILURE;
6711+
}
6712+
6713+
role_disable_cleanup:
6714+
_radio.ticker_id_stop = 0;
6715+
6716+
return ticker_status;
66206717
}
66216718

66226719
uint32_t radio_adv_enable(uint16_t interval, uint8_t chl_map,

0 commit comments

Comments
 (0)