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

Mesh assertion during sequence number block allocate

I am encountering an issue with PERSISTENT_STORAGE enabled. Currently I have a device set up to rapidly send out messages to test the sequence number updating and block allocation procedure. I notice that when the sequence number approaches the next block by (NETWORK_SEQNUM_FLASH_BLOCK_THRESHOLD) amount and triggers the seqnum_block_allocate() (see the code snippet below) I get a mesh assertion error.

uint32_t net_state_seqnum_alloc(uint32_t * p_seqnum)
{
    // Attempt to allocate the sequence number before it is fully restored from persistent storage.
    if (!m_status.is_synchronized)
    {
        return NRF_ERROR_FORBIDDEN;
    }

    if (m_net_state.seqnum < m_net_state.seqnum_max_available)
    {
        /* Check if we've reached the seqnum threshold for a state transition. */
        uint32_t threshold = NETWORK_SEQNUM_IV_UPDATE_START_THRESHOLD;
        if (m_net_state.iv_update.state == NET_STATE_IV_UPDATE_IN_PROGRESS)
        {
            threshold = NETWORK_SEQNUM_IV_UPDATE_END_THRESHOLD;
        }
        bool ivu_triggered = false;
        if (m_net_state.seqnum >= threshold)
        {
            m_net_state.iv_update.pending = true;
            ivu_triggered = iv_update_trigger_if_pending();
        }

        if (!ivu_triggered && m_net_state.seqnum >= m_net_state.seqnum_max_available - NETWORK_SEQNUM_FLASH_BLOCK_THRESHOLD)
        {
            seqnum_block_allocate();
        }
        /* Get the sequence number after doing the state updates, as they might
         * trigger changes to it. */
        uint32_t was_masked;
        _DISABLE_IRQS(was_masked);
        *p_seqnum = m_net_state.seqnum++;
        _ENABLE_IRQS(was_masked);

        return NRF_SUCCESS;
    }
    else
    {
        seqnum_block_allocate();
        return NRF_ERROR_FORBIDDEN;
    }
}

Mesh assertion error at line NRF_MESH_ASSERT_DEBUG(*p_flags & MESH_CONFIG_ENTRY_FLAG_BUSY); in the code:

static void backend_evt_handler(const mesh_config_backend_evt_t * p_evt)
{
    const mesh_config_entry_params_t * p_params = entry_params_find(p_evt->id);

    if (p_params == NULL && MESH_CONFIG_BACKEND_EVT_TYPE_ERASE_COMPLETE == p_evt->type)
    { /* Check whether entry with known file id was deleted. */
        const mesh_config_file_params_t * p_file = file_params_find(p_evt->id.file);
        NRF_MESH_ASSERT(p_file);
        return;
    }

    NRF_MESH_ASSERT(p_params);

    mesh_config_entry_flags_t * p_flags = entry_flags_get(p_params, p_evt->id);
    NRF_MESH_ASSERT_DEBUG(*p_flags & MESH_CONFIG_ENTRY_FLAG_BUSY);
    *p_flags &= (mesh_config_entry_flags_t)~MESH_CONFIG_ENTRY_FLAG_BUSY;

    if (p_evt->type == MESH_CONFIG_BACKEND_EVT_TYPE_STORAGE_MEDIUM_FAILURE)
    {
        const nrf_mesh_evt_t evt = {
            .type = NRF_MESH_EVT_CONFIG_STORAGE_FAILURE,
            .params.config_storage_failure = {
                .id = p_evt->id, /*lint !e64 Type mismatch */
            }
        };
        event_handle(&evt);
    }

    if (mesh_config_is_busy())
    {
        dirty_entries_process();
    }
    else
    {
        m_emergency_action = false;
        nrf_mesh_evt_t evt = {.type = NRF_MESH_EVT_CONFIG_STABLE};
        event_handle(&evt);
    }
}

Callstack:

What is this assertion trying to achieve and any ideas why its being triggered?

If I continue to let the application run, the next time it attempts to allocate a sequence block it fails repeatedly until the "if (m_net_state.seqnum < m_net_state.seqnum_max_available)" check fails and starts returning NRF_ERROR_FORBIDDEN.

Using mesh v4.1

Parents
  • It appears that during the call to seqnum_block_allocate, we make a call to dirty_entries_process() which calls the following line:

    status = mesh_config_backend_store(id, buf, p_params->entry_size);

    static void dirty_entries_process(void)
    {
    #if PERSISTENT_STORAGE
        FOR_EACH_ENTRY(p_params)
        {
            const mesh_config_file_params_t * p_file = file_params_find(p_params->p_id->file);
            NRF_MESH_ASSERT(p_file);
            if (p_file->strategy == MESH_CONFIG_STRATEGY_CONTINUOUS ||
               (p_file->strategy == MESH_CONFIG_STRATEGY_ON_POWER_DOWN && m_emergency_action))
            {
                for (uint32_t j = 0; j < p_params->max_count; ++j)
                {
                    if ((p_params->p_state[j] & MESH_CONFIG_ENTRY_FLAG_DIRTY) &&
                       !(p_params->p_state[j] & MESH_CONFIG_ENTRY_FLAG_BUSY))
                    {
                        mesh_config_entry_id_t id = *p_params->p_id;
                        id.record += j;
                        uint32_t status;
    
                        if (p_params->p_state[j] & MESH_CONFIG_ENTRY_FLAG_ACTIVE)
                        {
                            /* The backend has to make a copy, as the buffer is on stack! */
                            uint8_t buf[MESH_CONFIG_ENTRY_MAX_SIZE] __attribute__((aligned(WORD_SIZE)));
    
                            p_params->callbacks.getter(id, buf);
    
                            status = mesh_config_backend_store(id, buf, p_params->entry_size);
                        }
                        else
                        {
                            status = mesh_config_backend_erase(id);
                        }
    
                        switch (status)
                        {
                            case NRF_SUCCESS:
                                p_params->p_state[j] &= (mesh_config_entry_flags_t)~MESH_CONFIG_ENTRY_FLAG_DIRTY;
                                p_params->p_state[j] |= MESH_CONFIG_ENTRY_FLAG_BUSY;
                                break;
                            case NRF_ERROR_NOT_FOUND:
                                /* This can only happen with mesh_config_backend_erase() on not written yet entry. */
                                p_params->p_state[j] &= (mesh_config_entry_flags_t)~MESH_CONFIG_ENTRY_FLAG_DIRTY;
                                break;
                            default:
                                /* Back off if the backend call fails, to allow it to free up some resources */
                                return;
                        }
                    }
                }
            }
        }
    #endif
    }


    This line appears to trigger the backend_evt_handler before the status can be checked in dirty_entries_process() which on NRF_SUCCESS sets the MESH_CONFIG_ENTRY_FLAG_BUSY flag. This is the flag that the NRF_MESH_ASSERT is checking and doesn't seem to be set the first time backend_evt_handler is called.

Reply
  • It appears that during the call to seqnum_block_allocate, we make a call to dirty_entries_process() which calls the following line:

    status = mesh_config_backend_store(id, buf, p_params->entry_size);

    static void dirty_entries_process(void)
    {
    #if PERSISTENT_STORAGE
        FOR_EACH_ENTRY(p_params)
        {
            const mesh_config_file_params_t * p_file = file_params_find(p_params->p_id->file);
            NRF_MESH_ASSERT(p_file);
            if (p_file->strategy == MESH_CONFIG_STRATEGY_CONTINUOUS ||
               (p_file->strategy == MESH_CONFIG_STRATEGY_ON_POWER_DOWN && m_emergency_action))
            {
                for (uint32_t j = 0; j < p_params->max_count; ++j)
                {
                    if ((p_params->p_state[j] & MESH_CONFIG_ENTRY_FLAG_DIRTY) &&
                       !(p_params->p_state[j] & MESH_CONFIG_ENTRY_FLAG_BUSY))
                    {
                        mesh_config_entry_id_t id = *p_params->p_id;
                        id.record += j;
                        uint32_t status;
    
                        if (p_params->p_state[j] & MESH_CONFIG_ENTRY_FLAG_ACTIVE)
                        {
                            /* The backend has to make a copy, as the buffer is on stack! */
                            uint8_t buf[MESH_CONFIG_ENTRY_MAX_SIZE] __attribute__((aligned(WORD_SIZE)));
    
                            p_params->callbacks.getter(id, buf);
    
                            status = mesh_config_backend_store(id, buf, p_params->entry_size);
                        }
                        else
                        {
                            status = mesh_config_backend_erase(id);
                        }
    
                        switch (status)
                        {
                            case NRF_SUCCESS:
                                p_params->p_state[j] &= (mesh_config_entry_flags_t)~MESH_CONFIG_ENTRY_FLAG_DIRTY;
                                p_params->p_state[j] |= MESH_CONFIG_ENTRY_FLAG_BUSY;
                                break;
                            case NRF_ERROR_NOT_FOUND:
                                /* This can only happen with mesh_config_backend_erase() on not written yet entry. */
                                p_params->p_state[j] &= (mesh_config_entry_flags_t)~MESH_CONFIG_ENTRY_FLAG_DIRTY;
                                break;
                            default:
                                /* Back off if the backend call fails, to allow it to free up some resources */
                                return;
                        }
                    }
                }
            }
        }
    #endif
    }


    This line appears to trigger the backend_evt_handler before the status can be checked in dirty_entries_process() which on NRF_SUCCESS sets the MESH_CONFIG_ENTRY_FLAG_BUSY flag. This is the flag that the NRF_MESH_ASSERT is checking and doesn't seem to be set the first time backend_evt_handler is called.

Children
No Data
Related