Page 1 of 1

LWIP thread safety

Posted: Thu Aug 18, 2022 4:49 pm
by IvanFedotov
Hello!

I dont clearly understand about thread safety of netif and lwip:

ESP32 documentations says:
ESP-NETIF.
The APIs it provides are thread safe, even if the underlying TCP/IP stack APIs are not.
So, i cant understand thead safety implimentation when using configuration LWIP_TCPIP_CORE_LOCKING =1.
as I understand, all "internal" lwip function calls must be wrapped in a calls LOCK_TCPIP_CORE() and UNLOCK_TCPIP_CORE() ...

But, for example, at file - esp_netif_lwip.c:

Code: Select all

#define _RUN_IN_LWIP_TASK(function, netif, param) { return esp_netif_lwip_ipc_call(function, netif, (void *)(param)); }

#define _RUN_IN_LWIP_TASK_IF_SUPPORTED(function, netif, param) \
{                                                              \
    if (_IS_NETIF_ANY_POINT2POINT_TYPE(netif)) {               \
        return ESP_ERR_NOT_SUPPORTED;                          \
    }                                                          \
    return esp_netif_lwip_ipc_call(function, netif, (void *)(param)); \
}

/**
 * @brief Initiates a tcpip remote call if called from another task
 * or calls the function directly if executed from lwip task
 */
static inline esp_err_t esp_netif_lwip_ipc_call(esp_netif_api_fn fn, esp_netif_t *netif, void *data)
{
    esp_netif_api_msg_t msg = {
            .esp_netif = netif,
            .data = data,
            .api_fn = fn
    };
#if !LWIP_TCPIP_CORE_LOCKING
    if (tcpip_initialized && g_lwip_task != xTaskGetCurrentTaskHandle()) {
        ESP_LOGD(TAG, "check: remote, if=%p fn=%p\n", netif, fn);
        sys_arch_sem_wait(&api_lock_sem, 0);
        tcpip_send_msg_wait_sem((tcpip_callback_fn)esp_netif_api_cb, &msg, &api_sync_sem);
        sys_sem_signal(&api_lock_sem);
        return msg.ret;
    }
#endif /* !LWIP_TCPIP_CORE_LOCKING */
    ESP_LOGD(TAG, "check: local, if=%p fn=%p\n", netif, fn);
    return fn(&msg);
}
When !LWIP_TCPIP_CORE_LOCKING function will call from lwip thead through message.
But, when LWIP_TCPIP_CORE_LOCKING=1 just calls fn(&msg) fron current thread (can be not lwip thread).

So, when LWIP_TCPIP_CORE_LOCKING=1 and call (for example) esp_netif_set_default_netif:

Code: Select all

static esp_err_t esp_netif_update_default_netif(esp_netif_t *esp_netif, esp_netif_action_t action)
{
    return esp_netif_lwip_ipc_call(esp_netif_update_default_netif_lwip, esp_netif, (void*)action);
}

esp_err_t esp_netif_set_default_netif(esp_netif_t *esp_netif)
{
    return esp_netif_update_default_netif(esp_netif, ESP_NETIF_SET_DEFAULT);
}

/**
 * @brief tcpip thread version of esp_netif_update_default_netif
 *
 * @note This function and all functions called from this must be called from lwip task context
 */
static esp_err_t esp_netif_update_default_netif_lwip(esp_netif_api_msg_t *msg)
{
    esp_netif_t *esp_netif = msg->esp_netif;
    esp_netif_action_t action = (esp_netif_action_t)msg->data;

    ESP_LOGD(TAG, "%s %p", __func__, esp_netif);

    if (s_is_last_default_esp_netif_overridden && action != ESP_NETIF_SET_DEFAULT) {
        // check if manually configured default interface hasn't been destroyed
        s_last_default_esp_netif = esp_netif_is_active(s_last_default_esp_netif);
        if (s_last_default_esp_netif != NULL) {
            return ESP_OK; // still valid -> don't update default netif
        }
        // invalid -> reset the manual override and perform auto update
        s_is_last_default_esp_netif_overridden = false;
    }
    switch (action) {
        case ESP_NETIF_SET_DEFAULT:
            s_last_default_esp_netif = esp_netif;
            s_is_last_default_esp_netif_overridden = true;
            esp_netif_set_default_netif_internal(s_last_default_esp_netif);
        break;
        case ESP_NETIF_STARTED:
        {
            // check if previously default interface hasn't been destroyed in the meantime
            s_last_default_esp_netif = esp_netif_is_active(s_last_default_esp_netif);
            if (s_last_default_esp_netif && esp_netif_is_netif_up(s_last_default_esp_netif)
                && (s_last_default_esp_netif->route_prio > esp_netif->route_prio)) {
                esp_netif_set_default_netif_internal(s_last_default_esp_netif);
            } else if (esp_netif_is_netif_up(esp_netif)) {
                s_last_default_esp_netif = esp_netif;
                esp_netif_set_default_netif_internal(s_last_default_esp_netif);
            }
        }
        break;

        default:
        case ESP_NETIF_STOPPED:
        {
            s_last_default_esp_netif = NULL;
            esp_netif_list_lock();
            esp_netif_t *netif = esp_netif_next_unsafe(NULL);
            while (netif) {
                if (esp_netif_is_netif_up(netif)) {
                    if (s_last_default_esp_netif && esp_netif_is_netif_up(s_last_default_esp_netif)) {
                        if (netif->route_prio > s_last_default_esp_netif->route_prio) {
                            s_last_default_esp_netif = netif;
                        } // else not needed, as the s_last_default_esp_netif is correct
                    } else {
                        // s_last_default is either not set or down, current netif is up
                        s_last_default_esp_netif = netif;
                    }
                }
                netif = esp_netif_next_unsafe(netif);
            }
            esp_netif_list_unlock();
            if (s_last_default_esp_netif && esp_netif_is_netif_up(s_last_default_esp_netif)) {
                esp_netif_set_default_netif_internal(s_last_default_esp_netif);
            }
        }
        break;
    }
    return ESP_OK;
}
/**
 * @brief This function sets default netif no matter which implementation used
 *
 * @param esp_netif handle to network interface
 *
 * @note: This function must be called from lwip thread
 */
static void esp_netif_set_default_netif_internal(esp_netif_t *esp_netif)
{
    if (_IS_NETIF_POINT2POINT_TYPE(esp_netif, PPP_LWIP_NETIF)) {
#if CONFIG_PPP_SUPPORT
        esp_netif_ppp_set_default_netif(esp_netif->netif_handle);
#endif
    } else {
        netif_set_default(esp_netif->lwip_netif);
    }
}

threre no lwip core locking (or I don't understand where it is executed)....

Same with all functions, like

Code: Select all

esp_err_t esp_netif_dhcpc_start(esp_netif_t *esp_netif) _RUN_IN_LWIP_TASK_IF_SUPPORTED(esp_netif_dhcpc_start_api, esp_netif, NULL)