This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

FDS garbage collector, what does it do?

Hello everyone,

I am currently porting a project from pstorage to FDS. I am testing the functionalities of FDS including create, update and delete records and I am at the point where I would like to check how does the garbage collector works.

I am confused by what I see.

First example, I create a simple record that I update over and over in a loop, waiting for each update event to occur before going on with the next. At some point I stop these updates and call the garbage collector. Once this executed I dump my memory only to see that the garbage collector does not seem to have any effect... My first record and all the following updates have indeed been invalidated. The most recent is there and can be accessed but the memory has not been erased by the garbage collector as I supposed it would.

So I started to think that the garbage collector needed to see that the memory is full to operate. So I continued my loop until the update command returned the FDS_ERR_NO_SPACE_IN_FLASH error before calling the garbage collector and then call it. Well, nothing happened neither... And when I want to do an update I still have the "memory full" error.

[EDIT] : Here is a project that can be extracted in <SDK_ROOT>/example/peripheral/

FDS_testing.zip so that you can test it.

[EDIT2] : Edited version of the main that checks for record presence after 60 updates and garbage collection call : main.c

Here is my code, maybe you can help me:

#define NVM_INVALID_EVENT      0xFF
#define NVM_INVALID_RECORD     0xFFFFFFFF

static uint32_t     volatile m_nvm_mgt_last_record_id = NVM_INVALID_RECORD;
static fds_evt_id_t volatile m_nvm_mgt_last_event     = NVM_INVALID_EVENT;
static void my_fds_evt_handler(fds_evt_t const * const p_fds_evt)
{
    // Store the current event as last event that happened
    m_nvm_mgt_last_event = p_fds_evt->id;
    
    switch (p_fds_evt->id)
    {
        case FDS_EVT_INIT:
            if (p_fds_evt->result != FDS_SUCCESS)
            {
                // Initialization failed.
                NRF_LOG_ERROR("FDS init failed !\r\n");
            }
            else
            {
                // Managing an init flag so that nothing is done before FDS is ready...
                NRF_LOG_DEBUG("FDS init done !\r\n");
                if(!fds_storage_is_ready())
                {
                    fds_storage_set_ready();
                }
                m_nvm_mgt_last_record_id = NVM_INVALID_RECORD;
            }
            break;
        case FDS_EVT_WRITE:
            if (p_fds_evt->result == FDS_SUCCESS)
            {
                NRF_LOG_DEBUG("FDS Write event, record key = 0x%04x, file id = 0x%04x\r\n",
                        p_fds_evt->write.record_key, p_fds_evt->write.file_id);
                m_nvm_mgt_last_record_id = p_fds_evt->write.record_id;
            }
            break;
        case FDS_EVT_UPDATE:
            if (p_fds_evt->result == FDS_SUCCESS)
            {
                NRF_LOG_DEBUG("FDS Update event, record key = 0x%04x, file id = 0x%04x\r\n",
                        p_fds_evt->write.record_key, p_fds_evt->write.file_id);
                m_nvm_mgt_last_record_id = p_fds_evt->write.record_id;
            }
            break;
        case FDS_EVT_DEL_RECORD:
            if (p_fds_evt->result == FDS_SUCCESS)
            {
                NRF_LOG_DEBUG("FDS delete record event\r\n");
                m_nvm_mgt_last_record_id = p_fds_evt->del.record_id;
            }
            break;
        case FDS_EVT_DEL_FILE:
            if (p_fds_evt->result == FDS_SUCCESS)
            {
                NRF_LOG_DEBUG("FDS delete file event\r\n");
            }
            break;
        case FDS_EVT_GC:
            if (p_fds_evt->result == FDS_SUCCESS)
            {
                NRF_LOG_DEBUG("FDS garbage collection event\r\n");
            }
            break;
        default:
            break;
    }

}
/**@brief       Function that waits for specific flag
 *
 * @param       flag to look at
 */
void fds_test_wait_for(uint32_t record_id, fds_evt_id_t event)
{
    NRF_LOG_DEBUG("Waiting for event :  waited id is 0x%08x, current id is 0x%08x, waited event is %d, current event is %d\r\n",
                record_id, m_nvm_mgt_last_record_id, event, m_nvm_mgt_last_event);

    while((m_nvm_mgt_last_record_id != record_id) || (event != m_nvm_mgt_last_event))
    {
        __WFE();
    }
    // Reset values for next event to wait
    m_nvm_mgt_last_record_id = NVM_INVALID_RECORD;
    m_nvm_mgt_last_event = NVM_INVALID_EVENT;
    NRF_LOG_DEBUG("Out of wait\r\n");
}

static ret_code_t fds_test_init (void)
{
    ret_code_t ret = fds_register(my_fds_evt_handler);
    if (ret != FDS_SUCCESS)
    {
        return ret;
    }
    ret = fds_init();
    if (ret != FDS_SUCCESS)
    {
        return ret;
    }
    fds_test_wait_for(NVM_INVALID_RECORD, FDS_EVT_INIT);
    return NRF_SUCCESS;
}

/**
 * @brief       Function to create and store a file in NVM
 *
 * @param       file_id     File identifier
 * @param       record_id   Record key
 * @param       p_src_data  Pointer to memroy in RAM to copy from
 *                          WARNING: this pointer must be an aligned 4 address
 * @param       data_size   Size IN WORDS of the data to store
 */
ret_code_t fds_store_my_data(uint16_t file_id, uint16_t record_key,
        uint32_t * p_src_data, uint16_t data_word_size, uint32_t * record_id)
{
    fds_record_t        record;
    fds_record_chunk_t  record_chunk;
    fds_record_desc_t   record_desc;

    NRF_LOG_DEBUG("Store data from address 0x%08x, len = %d in words\r\n", (uint32_t) p_src_data, (uint32_t)data_word_size);
    NRF_LOG_DEBUG("File id is 0x%04x, record_key is 0x%04x\r\n", (uint32_t)file_id, (uint32_t)record_key);
    // Set up data.
    record_chunk.p_data         = p_src_data;
    record_chunk.length_words   = data_word_size;

    // Set up record.
    record.file_id              = file_id;
    record.key                  = record_key;
    record.data.p_chunks        = &record_chunk;
    record.data.num_chunks      = 1;

    ret_code_t ret = fds_record_write(&record_desc, &record);
    if (ret != FDS_SUCCESS)
    {
        return ret;
    }
    NRF_LOG_DEBUG("Writing Record ID = %d \r\n", record_desc.record_id);
    *record_id = record_desc.record_id;
    return NRF_SUCCESS;
}

/**@brief       Update an existing record with new data
 *
 * @details     This function needs a record ID to store new data
 *              It can be obtains from file id and record key using fds_get_data_record
 *              method below
 *
 * @param       fds_record_desc_t, pointer on the handle to the record to update
 * @param       p_src_data: Pointer uint32_t to the data to save
 * @param       data_word_size: Length of the data to store in WORDS
 */
ret_code_t fds_update_my_data(fds_record_desc_t * record_desc, uint16_t file_id, uint16_t rec_key,
        uint32_t* p_src_data, uint16_t data_word_size)
{
    ret_code_t err_code;
    fds_record_t        record;
    fds_record_chunk_t  record_chunk;

    // Set up data.
    record_chunk.p_data         = p_src_data;
    record_chunk.length_words   = data_word_size;
    record.data.p_chunks        = &record_chunk;
    record.data.num_chunks      = 1;

    // Set up record.
    record.file_id              = file_id;
    record.key                  = rec_key;


    // Update record
    err_code = fds_record_update(record_desc, &record);
    if (err_code != FDS_SUCCESS)
    {
        NRF_LOG_WARNING("Update data for record id 0x%04x has failed with error code %02x\r\n",
                                      record_desc->record_id, err_code);
        return err_code;
    }
    NRF_LOG_DEBUG("Update Record ID = %d \r\n", record_desc->record_id);
    return NRF_SUCCESS;
}

/*
 * Test to validate basic functions for fds_storage layer, just above Nordic's FDS
 */
static    uint32_t p_data[6] = {0x11111111, 0x22222222, 0x33333333, 0x44444444, 0x55555555, 0x66666666};
void fds_storage_update_test()
{
    uint8_t nb_update = 0;
    uint16_t w_size = 6;
    fds_record_desc_t record_desc;
    uint32_t record_id = 0;
    bool gc_called = false;

    uint32_t val = 0x88889999;

    NRF_LOG_DEBUG("RAM @ of p_data = 0x%08x\r\n", (uint32_t)p_data);
    
    // Create a file
    APP_ERROR_CHECK(fds_store_my_data(0x5555, 0x1212, (uint32_t *)p_data, w_size, &record_id));
    fds_test_wait_for(record_id, FDS_EVT_WRITE);

    NRF_LOG_DEBUG("Stored file 5555/1212: record_id = %d\r\n", record_id);
    
    // Following loop also tested with a lower number so that it wont reach the 
    // end of the allocated memory, and manually call garbage collection after.
    // This is the latest version where I let it reach the end of the flash:
    while(nb_update < 400) 
    {
        NRF_LOG_DEBUG("Update number %d\r\n", nb_update);
        // Change p_data
        p_data[2] = val++;
        p_data[5] = val++;
        // Update the record
        if(fds_update_my_data(&record_desc, 0x5555, 0x1212, p_data, w_size) == FDS_ERR_NO_SPACE_IN_FLASH)
        {
             if(!gc_called)
             {
                  NRF_LOG_DEBUG("MEMORY FULL!!! Calling garbage collector\r\n");
                  fds_gc();
                  fds_test_wait_for(NVM_INVALID_RECORD, FDS_EVT_GC);
                  gc_called = true;
             }
             else
             {
                  // We will end up there, and if you dump your memory at the end
                  // it still contains the invalidates updates...
                  NRF_LOG_DEBUG("Garbage collector already called once\r\n");
                  break;
             }
        }
        else
        {
            fds_test_wait_for(record_desc.record_id, FDS_EVT_UPDATE);
        }
        nb_update++;
    }
}

int main(void)
{
    uint32_t err_code;

    nrf_clock_lf_cfg_t clock_lf_cfg = NRF_CLOCK_LFCLKSRC;

    // Initialize.
    APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
    NRF_LOG_INFO("\r\n\r\n\r\nInit logging done\r\n");

    // Initialize the SoftDevice handler module.
    SOFTDEVICE_HANDLER_INIT(&clock_lf_cfg, NULL);

    // Register with the SoftDevice handler module for BLE events.
    err_code = softdevice_sys_evt_handler_set(sys_evt_dispatch);
    APP_ERROR_CHECK(err_code);

    timers_init();
    buttons_leds_init();

    NRF_LOG_INFO("Start FDS test...\r\n");		
    
    fds_test_init();

    fds_storage_update_test();
    
    // Enter main loop.
    nrf_delay_ms(1000);
    NRF_LOG_DEBUG("\r\n");
    NRF_LOG_DEBUG("End of test\r\n");
    nrf_delay_ms(1000);
    for (;;)
    {
        power_manage();
    }
}
Related