Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid InternalFileSystem corruption caused by simultaneous BLE operation #838

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 64 additions & 32 deletions libraries/InternalFileSytem/src/flash/flash_nrf5x.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,46 @@ extern uint32_t __flash_arduino_start[];
// MACRO TYPEDEF CONSTANT ENUM DECLARATION
//--------------------------------------------------------------------+
static SemaphoreHandle_t _sem = NULL;
static bool _flash_op_failed = false;

void flash_nrf5x_event_cb (uint32_t event)
{
// if (event != NRF_EVT_FLASH_OPERATION_SUCCESS) LOG_LV1("IFLASH", "Flash op Failed");
if ( _sem ) xSemaphoreGive(_sem);
if ( _sem ) {
// Record the result, for consumption by fal_erase or fal_program
// Used to reattempt failed operations
_flash_op_failed = (event == NRF_EVT_FLASH_OPERATION_ERROR);
henrygab marked this conversation as resolved.
Show resolved Hide resolved

// Signal to fal_erase or fal_program that our async flash op is now complete
xSemaphoreGive(_sem);
}
}

// How many retry attempts when performing flash operations
#define MAX_RETRY 20

// Check whether a flash operation was successful, or should be repeated
static bool retry_flash_op (uint32_t op_result, bool sd_enabled) {
henrygab marked this conversation as resolved.
Show resolved Hide resolved
// If busy
if (op_result == NRF_ERROR_BUSY) {
delay(1);
return true; // Retry
}

// If unspecified error
if (op_result != NRF_SUCCESS)
return true; // Retry

// If the soft device is enabled, flash operations run async
// The callback (flash_nrf5x_event_cb) will give semaphore when the flash operation is complete
// The callback also checks for NRF_EVT_FLASH_OPERATION_ERROR, which is not available to us otherwise
if (sd_enabled) {
xSemaphoreTake(_sem, portMAX_DELAY);
if (_flash_op_failed)
return true; // Retry
}

// Success
return false;
}

// Flash Abstraction Layer
Expand Down Expand Up @@ -107,30 +142,28 @@ static bool fal_erase (uint32_t addr)
// Init semaphore for first call
if ( _sem == NULL )
{
_sem = xSemaphoreCreateCounting(10, 0);
_sem = xSemaphoreCreateBinary();
VERIFY(_sem);
}

// retry if busy
uint32_t err;
while ( NRF_ERROR_BUSY == (err = sd_flash_page_erase(addr / FLASH_NRF52_PAGE_SIZE)) )
{
delay(1);
}
VERIFY_STATUS(err, false);

// wait for async event if SD is enabled
// Check if soft device is enabled
// If yes, flash operations are async, so we need to wait for the callback to give the semaphore
uint8_t sd_en = 0;
(void) sd_softdevice_is_enabled(&sd_en);

if ( sd_en ) xSemaphoreTake(_sem, portMAX_DELAY);

return true;
// Make multiple attempts to erase
uint8_t attempt = 0;
while (retry_flash_op(sd_flash_page_erase(addr / FLASH_NRF52_PAGE_SIZE), sd_en)) {
henrygab marked this conversation as resolved.
Show resolved Hide resolved
if (++attempt > MAX_RETRY)
return false; // Failure
}
return true; // Success
}

static uint32_t fal_program (uint32_t dst, void const * src, uint32_t len)
{
// wait for async event if SD is enabled
// Check if soft device is enabled
// If yes, flash operations are async, so we need to wait for the callback to give the semaphore
uint8_t sd_en = 0;
(void) sd_softdevice_is_enabled(&sd_en);

Expand All @@ -140,27 +173,26 @@ static uint32_t fal_program (uint32_t dst, void const * src, uint32_t len)
// https://devzone.nordicsemi.com/f/nordic-q-a/40088/sd_flash_write-cause-nrf_fault_id_sd_assert
// Workaround: write half page at a time.
#if NRF52832_XXAA
while ( NRF_ERROR_BUSY == (err = sd_flash_write((uint32_t*) dst, (uint32_t const *) src, len/4)) )
{
delay(1);
uint8_t attempt = 0;
while (retry_flash_op(sd_flash_write((uint32_t*) dst, (uint32_t const *) src, len/4), sd_en)) {
henrygab marked this conversation as resolved.
Show resolved Hide resolved
if (++attempt > MAX_RETRY)
return 0; // Failure
}
VERIFY_STATUS(err, 0);

if ( sd_en ) xSemaphoreTake(_sem, portMAX_DELAY);
#else
while ( NRF_ERROR_BUSY == (err = sd_flash_write((uint32_t*) dst, (uint32_t const *) src, len/8)) )
{
delay(1);

// First part of block
uint8_t attempt = 0;
while (retry_flash_op(sd_flash_write((uint32_t*) dst, (uint32_t const *) src, len/8), sd_en)) {
if (++attempt > MAX_RETRY)
return 0; // Failure
}
VERIFY_STATUS(err, 0);
if ( sd_en ) xSemaphoreTake(_sem, portMAX_DELAY);

while ( NRF_ERROR_BUSY == (err = sd_flash_write((uint32_t*) (dst+ len/2), (uint32_t const *) (src + len/2), len/8)) )
{
delay(1);
// Second part of block
attempt = 0;
while (retry_flash_op(sd_flash_write((uint32_t*) (dst+ len/2), (uint32_t const *) (src + len/2), len/8), sd_en)) {
if (++attempt > MAX_RETRY)
return 0; // Failure
}
VERIFY_STATUS(err, 0);
if ( sd_en ) xSemaphoreTake(_sem, portMAX_DELAY);
#endif

return len;
Expand Down
Loading