Skip to content

Commit 1408ade

Browse files
cvinayakAnas Nashif
authored andcommitted
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 d043cf9 commit 1408ade

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
@@ -132,6 +132,8 @@ static struct {
132132

133133
uint8_t volatile ticker_id_prepare;
134134
uint8_t volatile ticker_id_event;
135+
uint8_t volatile ticker_id_stop;
136+
135137
enum role volatile role;
136138
enum state state;
137139

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

218220
static void common_init(void);
219221
static void ticker_success_assert(uint32_t status, void *params);
222+
static void ticker_stop_adv_assert(uint32_t status, void *params);
223+
static void ticker_stop_obs_assert(uint32_t status, void *params);
224+
static void ticker_update_adv_assert(uint32_t status, void *params);
225+
static void ticker_update_slave_assert(uint32_t status, void *params);
220226
static void event_inactive(uint32_t ticks_at_expire, uint32_t remainder,
221227
uint16_t lazy, void *context);
222228
static void adv_setup(void);
@@ -739,23 +745,21 @@ static inline uint32_t isr_rx_adv(uint8_t devmatch_ok, uint8_t irkmatch_ok,
739745
ticker_status = ticker_stop(RADIO_TICKER_INSTANCE_ID_RADIO,
740746
RADIO_TICKER_USER_ID_WORKER,
741747
RADIO_TICKER_ID_ADV,
742-
ticker_success_assert, (void *)__LINE__);
743-
LL_ASSERT((ticker_status == TICKER_STATUS_SUCCESS) ||
744-
(ticker_status == TICKER_STATUS_BUSY));
748+
ticker_stop_adv_assert,
749+
(void *)__LINE__);
750+
ticker_stop_adv_assert(ticker_status, (void *)__LINE__);
745751

746752
/* Stop Direct Adv Stopper */
747753
_pdu_adv = (struct pdu_adv *)&_radio.advertiser.adv_data.data
748754
[_radio.advertiser.adv_data.first][0];
749755
if (_pdu_adv->type == PDU_ADV_TYPE_DIRECT_IND) {
750-
ticker_status =
751-
ticker_stop(
752-
RADIO_TICKER_INSTANCE_ID_RADIO,
753-
RADIO_TICKER_USER_ID_WORKER,
754-
RADIO_TICKER_ID_ADV_STOP,
755-
0, /* @todo ticker_success_assert */
756-
0 /* @todo (void *) __LINE__*/);
757-
LL_ASSERT((ticker_status == TICKER_STATUS_SUCCESS) ||
758-
(ticker_status == TICKER_STATUS_BUSY));
756+
/* Advertiser stop can expire while here in this ISR.
757+
* Deferred attempt to stop can fail as it would have
758+
* expired, hence ignore failure.
759+
*/
760+
ticker_stop(RADIO_TICKER_INSTANCE_ID_RADIO,
761+
RADIO_TICKER_USER_ID_WORKER,
762+
RADIO_TICKER_ID_ADV_STOP, NULL, NULL);
759763
}
760764

761765
/* Start Slave */
@@ -947,21 +951,23 @@ static inline uint32_t isr_rx_obs(uint8_t irkmatch_id, uint8_t rssi_ready)
947951
conn->hdr.ticks_xtal_to_start :
948952
conn->hdr.ticks_active_to_start;
949953

950-
/* Stop Observer and start Master */
954+
/* Stop Observer */
951955
ticker_status = ticker_stop(RADIO_TICKER_INSTANCE_ID_RADIO,
952956
RADIO_TICKER_USER_ID_WORKER,
953957
RADIO_TICKER_ID_OBS,
954-
ticker_success_assert,
958+
ticker_stop_obs_assert,
955959
(void *)__LINE__);
956-
LL_ASSERT((ticker_status == TICKER_STATUS_SUCCESS) ||
957-
(ticker_status == TICKER_STATUS_BUSY));
958-
ticker_status = ticker_stop(RADIO_TICKER_INSTANCE_ID_RADIO,
959-
RADIO_TICKER_USER_ID_WORKER,
960-
RADIO_TICKER_ID_OBS_STOP,
961-
0, /* @todo ticker_success_assert */
962-
0 /* @todo (void *) __LINE__ */);
963-
LL_ASSERT((ticker_status == TICKER_STATUS_SUCCESS) ||
964-
(ticker_status == TICKER_STATUS_BUSY));
960+
ticker_stop_obs_assert(ticker_status, (void *)__LINE__);
961+
962+
/* Observer stop can expire while here in this ISR.
963+
* Deferred attempt to stop can fail as it would have
964+
* expired, hence ignore failure.
965+
*/
966+
ticker_stop(RADIO_TICKER_INSTANCE_ID_RADIO,
967+
RADIO_TICKER_USER_ID_WORKER,
968+
RADIO_TICKER_ID_OBS_STOP, NULL, NULL);
969+
970+
/* Start master */
965971
ticker_status =
966972
ticker_start(RADIO_TICKER_INSTANCE_ID_RADIO,
967973
RADIO_TICKER_USER_ID_WORKER,
@@ -2306,16 +2312,25 @@ static inline uint32_t isr_close_adv(void)
23062312
/** @todo use random 0-10 */
23072313
random_delay = 10;
23082314

2315+
/* Call to ticker_update can fail under the race
2316+
* condition where in the Adv role is being stopped but
2317+
* at the same time it is preempted by Adv event that
2318+
* gets into close state. Accept failure when Adv role
2319+
* is being stopped.
2320+
*/
23092321
ticker_status =
23102322
ticker_update(RADIO_TICKER_INSTANCE_ID_RADIO,
23112323
RADIO_TICKER_USER_ID_WORKER,
23122324
RADIO_TICKER_ID_ADV,
2313-
TICKER_US_TO_TICKS(random_delay * 1000),
2325+
TICKER_US_TO_TICKS(random_delay *
2326+
1000),
23142327
0, 0, 0, 0, 0,
2315-
ticker_success_assert,
2328+
ticker_update_adv_assert,
23162329
(void *)__LINE__);
23172330
LL_ASSERT((ticker_status == TICKER_STATUS_SUCCESS) ||
2318-
(ticker_status == TICKER_STATUS_BUSY));
2331+
(ticker_status == TICKER_STATUS_BUSY) ||
2332+
(_radio.ticker_id_stop ==
2333+
RADIO_TICKER_ID_ADV));
23192334
}
23202335
}
23212336

@@ -2343,20 +2358,16 @@ static inline uint32_t isr_close_obs(void)
23432358

23442359
radio_tmr_end_capture();
23452360
} else {
2346-
uint32_t ticker_status;
2347-
23482361
radio_filter_disable();
23492362

23502363
if (_radio.state == STATE_ABORT) {
2351-
ticker_status =
2352-
ticker_stop(
2353-
RADIO_TICKER_INSTANCE_ID_RADIO,
2354-
RADIO_TICKER_USER_ID_WORKER,
2355-
RADIO_TICKER_ID_OBS_STOP,
2356-
0 /** @todo ticker_success_assert */,
2357-
0 /** @todo (void *) __LINE__ */);
2358-
LL_ASSERT((ticker_status == TICKER_STATUS_SUCCESS) ||
2359-
(ticker_status == TICKER_STATUS_BUSY));
2364+
/* Observer stop can expire while here in this ISR.
2365+
* Deferred attempt to stop can fail as it would have
2366+
* expired, hence ignore failure.
2367+
*/
2368+
ticker_stop(RADIO_TICKER_INSTANCE_ID_RADIO,
2369+
RADIO_TICKER_USER_ID_WORKER,
2370+
RADIO_TICKER_ID_OBS_STOP, NULL, NULL);
23602371
}
23612372
}
23622373

@@ -2631,18 +2642,25 @@ static inline void isr_close_conn(void)
26312642
if ((ticks_drift_plus != 0) || (ticks_drift_minus != 0) ||
26322643
(lazy != 0) || (force != 0)) {
26332644
uint32_t ticker_status;
2634-
2645+
uint8_t ticker_id = RADIO_TICKER_ID_FIRST_CONNECTION +
2646+
_radio.conn_curr->handle;
2647+
2648+
/* Call to ticker_update can fail under the race
2649+
* condition where in the Slave role is being stopped but
2650+
* at the same time it is preempted by Slave event that
2651+
* gets into close state. Accept failure when Slave role
2652+
* is being stopped.
2653+
*/
26352654
ticker_status =
26362655
ticker_update(RADIO_TICKER_INSTANCE_ID_RADIO,
26372656
RADIO_TICKER_USER_ID_WORKER,
2638-
RADIO_TICKER_ID_FIRST_CONNECTION +
2639-
_radio.conn_curr->handle,
2657+
ticker_id,
26402658
ticks_drift_plus, ticks_drift_minus, 0, 0,
2641-
lazy, force,
2642-
0 /** @todo ticker_success_assert */,
2643-
0 /** @todo (void *) __LINE__ */);
2659+
lazy, force, ticker_update_slave_assert,
2660+
(void *)(uint32_t)ticker_id);
26442661
LL_ASSERT((ticker_status == TICKER_STATUS_SUCCESS) ||
2645-
(ticker_status == TICKER_STATUS_BUSY));
2662+
(ticker_status == TICKER_STATUS_BUSY) ||
2663+
(_radio.ticker_id_stop == ticker_id));
26462664
}
26472665
}
26482666

@@ -2665,6 +2683,20 @@ static inline void isr_radio_state_close(void)
26652683
break;
26662684

26672685
case ROLE_NONE:
2686+
/* If a role closes graceful while it is being stopped, then
2687+
* Radio ISR will be triggered to process the stop state with
2688+
* no active role at that instance in time.
2689+
* Just reset the state to none. The role has gracefully closed
2690+
* before this ISR run.
2691+
* The above applies to aborting a role event too.
2692+
*/
2693+
LL_ASSERT((_radio.state == STATE_STOP) ||
2694+
(_radio.state == STATE_ABORT));
2695+
2696+
_radio.state = STATE_NONE;
2697+
2698+
return;
2699+
26682700
default:
26692701
LL_ASSERT(0);
26702702
break;
@@ -2783,6 +2815,60 @@ static void ticker_success_assert(uint32_t status, void *params)
27832815
LL_ASSERT(status == TICKER_STATUS_SUCCESS);
27842816
}
27852817

2818+
static void ticker_stop_adv_assert(uint32_t status, void *params)
2819+
{
2820+
ARG_UNUSED(params);
2821+
2822+
if (status == TICKER_STATUS_FAILURE) {
2823+
if (_radio.ticker_id_stop == RADIO_TICKER_ID_ADV) {
2824+
/* ticker_stop failed due to race condition
2825+
* while in role_disable. Let the role_disable
2826+
* be made aware of, so it can return failure
2827+
* (to stop Adv role as it is now transitioned
2828+
* to Slave role).
2829+
*/
2830+
_radio.ticker_id_stop = 0;
2831+
} else {
2832+
LL_ASSERT(0);
2833+
}
2834+
}
2835+
}
2836+
2837+
static void ticker_stop_obs_assert(uint32_t status, void *params)
2838+
{
2839+
ARG_UNUSED(params);
2840+
2841+
if (status == TICKER_STATUS_FAILURE) {
2842+
if (_radio.ticker_id_stop == RADIO_TICKER_ID_OBS) {
2843+
/* ticker_stop failed due to race condition
2844+
* while in role_disable. Let the role_disable
2845+
* be made aware of, so it can return failure
2846+
* (to stop Obs role as it is now transitioned
2847+
* to Master role).
2848+
*/
2849+
_radio.ticker_id_stop = 0;
2850+
} else {
2851+
LL_ASSERT(0);
2852+
}
2853+
}
2854+
}
2855+
2856+
static void ticker_update_adv_assert(uint32_t status, void *params)
2857+
{
2858+
ARG_UNUSED(params);
2859+
2860+
LL_ASSERT((status == TICKER_STATUS_SUCCESS) ||
2861+
(_radio.ticker_id_stop == RADIO_TICKER_ID_ADV));
2862+
}
2863+
2864+
static void ticker_update_slave_assert(uint32_t status, void *params)
2865+
{
2866+
uint8_t ticker_id = (uint32_t)params & 0xFF;
2867+
2868+
LL_ASSERT((status == TICKER_STATUS_SUCCESS) ||
2869+
(_radio.ticker_id_stop == ticker_id));
2870+
}
2871+
27862872
static void mayfly_radio_active(void *params)
27872873
{
27882874
static uint8_t s_active;
@@ -3767,7 +3853,7 @@ static void event_common_prepare(uint32_t ticks_at_expire,
37673853
#endif
37683854
#undef RADIO_DEFERRED_PREEMPT
37693855

3770-
/** Handle change in _ticks_active_to_start */
3856+
/** Handle change in _ticks_active_to_start */
37713857
if (_radio.ticks_active_to_start != _ticks_active_to_start) {
37723858
uint32_t ticks_to_start_new =
37733859
((_radio.ticks_active_to_start <
@@ -6917,6 +7003,10 @@ static uint32_t role_disable(uint8_t ticker_id_primary,
69177003
break;
69187004
}
69197005

7006+
LL_ASSERT(!_radio.ticker_id_stop);
7007+
7008+
_radio.ticker_id_stop = ticker_id_primary;
7009+
69207010
/* Step 1: Is Primary started? Stop the Primary ticker */
69217011
ticker_status =
69227012
ticker_stop(RADIO_TICKER_INSTANCE_ID_RADIO,
@@ -6938,7 +7028,7 @@ static uint32_t role_disable(uint8_t ticker_id_primary,
69387028
}
69397029

69407030
if (ticker_status != TICKER_STATUS_SUCCESS) {
6941-
return 1;
7031+
goto role_disable_cleanup;
69427032
}
69437033

69447034
/* Inside our event, gracefully handle XTAL and Radio actives */
@@ -6949,7 +7039,14 @@ static uint32_t role_disable(uint8_t ticker_id_primary,
69497039
ticks_xtal_to_start, ticks_active_to_start);
69507040
}
69517041

6952-
return 0;
7042+
if (!_radio.ticker_id_stop) {
7043+
ticker_status = TICKER_STATUS_FAILURE;
7044+
}
7045+
7046+
role_disable_cleanup:
7047+
_radio.ticker_id_stop = 0;
7048+
7049+
return ticker_status;
69537050
}
69547051

69557052
uint32_t radio_adv_enable(uint16_t interval, uint8_t chl_map,

0 commit comments

Comments
 (0)