From d67df8eb344908567a81c91483fc819cc6f6892d Mon Sep 17 00:00:00 2001 From: Vinayak Kariappa Chettimada Date: Tue, 8 Jul 2025 20:41:09 +0200 Subject: [PATCH 1/2] Bluetooth: Controller: Fix flash sync start from starving connection Fix flash sync start from repeatedly forcing connection event be skipped. Space new flash operation to be placed in the past so that it does not force itself on to an overlapping connection event. Signed-off-by: Vinayak Kariappa Chettimada --- .../controller/flash/soc_flash_nrf_ticker.c | 47 +++++++++++++++---- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/subsys/bluetooth/controller/flash/soc_flash_nrf_ticker.c b/subsys/bluetooth/controller/flash/soc_flash_nrf_ticker.c index 32b908a5ab05..bc27f81bcba7 100644 --- a/subsys/bluetooth/controller/flash/soc_flash_nrf_ticker.c +++ b/subsys/bluetooth/controller/flash/soc_flash_nrf_ticker.c @@ -26,14 +26,13 @@ #define FLASH_SYNC_SWITCHING_TIME (FLASH_RADIO_ABORT_DELAY_US +\ FLASH_RADIO_WORK_DELAY_US) -struct ticker_sync_context { - uint32_t interval; /* timeslot interval. */ - uint32_t slot; /* timeslot length. */ - uint32_t ticks_begin; /* timeslot begin timestamp */ +static struct { + uint32_t interval; /* timeslot interval. */ + uint32_t slot; /* timeslot length. */ + uint32_t ticks_begin; /* timeslot begin timestamp */ + uint32_t ticks_previous; /* timeslot previous reference */ int result; -}; - -static struct ticker_sync_context _ticker_sync_context; +} _ticker_sync_context; /* semaphore for synchronization of flash operations */ static struct k_sem sem_sync; @@ -151,6 +150,7 @@ static void time_slot_callback_abort(uint32_t ticks_at_expire, void *context) { ll_radio_state_abort(); + time_slot_delay(ticks_at_expire, HAL_TICKER_US_TO_TICKS(FLASH_RADIO_WORK_DELAY_US), time_slot_callback_work, @@ -163,6 +163,18 @@ static void time_slot_callback_prepare(uint32_t ticks_at_expire, uint16_t lazy, uint8_t force, void *context) { + uint32_t ticks_elapsed; + uint32_t ticks_now; + + /* Skip flash operation if in the past, this can be when requested in the past due to + * flash operations requested too often. + */ + ticks_now = ticker_ticks_now_get(); + ticks_elapsed = ticker_ticks_diff_get(ticks_now, ticks_at_expire); + if (ticks_elapsed > HAL_TICKER_US_TO_TICKS(FLASH_RADIO_WORK_DELAY_US)) { + return; + } + #if defined(CONFIG_BT_CTLR_LOW_LAT) time_slot_callback_abort(ticks_at_expire, ticks_drift, remainder, lazy, force, context); @@ -178,6 +190,7 @@ static void time_slot_callback_prepare(uint32_t ticks_at_expire, int nrf_flash_sync_init(void) { k_sem_init(&sem_sync, 0, 1); + return 0; } @@ -193,10 +206,28 @@ void nrf_flash_sync_set_context(uint32_t duration) int nrf_flash_sync_exe(struct flash_op_desc *op_desc) { uint8_t instance_index; + uint32_t ticks_elapsed; + uint32_t ticks_slot; + uint32_t ticks_now; uint8_t ticker_id; uint32_t ret; int result; + /* Check if flash operation request too often that can starve connection events. + * We will request to start ticker in the past so that it does not `force` itself onto + * scheduling by causing connection events to be skipped. + */ + ticks_now = ticker_ticks_now_get(); + ticks_elapsed = ticker_ticks_diff_get(ticks_now, _ticker_sync_context.ticks_previous); + _ticker_sync_context.ticks_previous = ticks_now; + ticks_slot = HAL_TICKER_US_TO_TICKS(_ticker_sync_context.slot); + if (ticks_elapsed < ticks_slot) { + uint32_t ticks_interval = HAL_TICKER_US_TO_TICKS(_ticker_sync_context.interval); + + /* Set ticker start reference one flash slot interval in the past */ + ticks_now = ticker_ticks_diff_get(ticks_now, ticks_interval); + } + /* Get the ticker instance and ticker id for flash operations */ ll_timeslice_ticker_id_get(&instance_index, &ticker_id); @@ -205,7 +236,7 @@ int nrf_flash_sync_exe(struct flash_op_desc *op_desc) 3, /* user id for thread mode */ /* (MAYFLY_CALL_ID_PROGRAM) */ ticker_id, /* flash ticker id */ - ticker_ticks_now_get(), /* current tick */ + ticks_now, /* current tick */ 0, /* first int. immediately */ /* period */ HAL_TICKER_US_TO_TICKS( From f58d9a033664a734ee8fb7d7c7f4fcbe3a46f5e3 Mon Sep 17 00:00:00 2001 From: Vinayak Kariappa Chettimada Date: Wed, 9 Jul 2025 17:05:27 +0200 Subject: [PATCH 2/2] Bluetooth: Controller: Fix missing ticker force reset on being lazy Fix missing ticker force reset when the ticker expiry was skipped or rescheduled. Signed-off-by: Vinayak Kariappa Chettimada --- subsys/bluetooth/controller/ticker/ticker.c | 1 + 1 file changed, 1 insertion(+) diff --git a/subsys/bluetooth/controller/ticker/ticker.c b/subsys/bluetooth/controller/ticker/ticker.c index fb8e2fba97f8..065e9981c5f0 100644 --- a/subsys/bluetooth/controller/ticker/ticker.c +++ b/subsys/bluetooth/controller/ticker/ticker.c @@ -1380,6 +1380,7 @@ void ticker_worker(void *param) * ticker_job_reschedule_in_window when completed. */ ticker->lazy_current++; + ticker->force = 0U; if ((ticker->must_expire == 0U) || (ticker->lazy_periodic >= ticker->lazy_current) ||