zephyr/drivers/wifi/esp_at/esp_socket.c

239 lines
5.4 KiB
C
Raw Normal View History

/*
* Copyright (c) 2019 Tobias Svehagen
drivers: wifi: esp: add thread-safety on esp_socket operations Change type of esp_socket->flags from uint8_t to atomic_t, so that read and write access to those flags is done in atomic (thread-safe) manner. Introduce esp_socket_ref() and esp_socket_unref() functions, which operate on atomic refcount variable. esp_socket_ref() role is to increase refcount if it was already non-zero. If it was zero then NULL is returned, which means that socket is not used by net_context at the moment. Role of refcount: * socket instance is assured to be between net_offload->get() and net_offload->put() when refcount > 0, * makes sure that socket instance can be used (its members can be dereferenced) when refcount > 0, * 'context' member is always valid and its members can be dereferenced when refcount > 0. esp_socket_get() gets unused socket, as previously. Additionally it sets refcount to 1 at the end of call, which basically means that from that point such socket can be referenced by other parts of the driver. Each esp_socket_get() call should be followed by esp_socket_unref() and esp_socket_put() to properly invalidate socket and prevent other parts of driver from using it. Add ESP_SOCK_WORKQ_STOPPED flag, which is now used to prevent scheduling more work into driver workqueue. This flag is set in net_offload->put() callback, so that no more socket work (such as processing RX/TX packets or closing socket because of errors) is submitted after that. Introduce mutex lock, which has following role: * protects dst, connect_cb + conn_user_data, recv_cb + recv_user_data, * assures that checking ESP_SOCK_WORKQ_STOPPED flag and actually submitting (or not if net_offload->put was already called) new socket work to workqueue is done in atomic way. As there is a mechanism to prevent submitting new work items to workqueue when net_offload->put() has been executed, then there is no need to explicitly call esp_socket_ref() in esp_workq thread. This is because one reference is being held by net_context (after calling net_context->get()). This is why all the esp_socket_in_use() were simply dropped. Code running from esp_rx thread on the other hand always uses esp_socket_ref_from_link_id() helper function (which is backed by esp_socket_ref()), so that it replaces previous esp_socket_in_use() calls and additionally makes sure that socket stays valid ("in use") until esp_socket_unref() is called. Signed-off-by: Marcin Niestroj <m.niestroj@grinn-global.com>
2020-12-03 22:43:42 +01:00
* Copyright (c) 2020 Grinn
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp.h"
#include <logging/log.h>
LOG_MODULE_DECLARE(wifi_esp_at, CONFIG_WIFI_LOG_LEVEL);
#define RX_NET_PKT_ALLOC_TIMEOUT \
K_MSEC(CONFIG_WIFI_ESP_AT_RX_NET_PKT_ALLOC_TIMEOUT)
struct esp_workq_flush_data {
struct k_work work;
struct k_sem sem;
};
drivers: wifi: esp: add thread-safety on esp_socket operations Change type of esp_socket->flags from uint8_t to atomic_t, so that read and write access to those flags is done in atomic (thread-safe) manner. Introduce esp_socket_ref() and esp_socket_unref() functions, which operate on atomic refcount variable. esp_socket_ref() role is to increase refcount if it was already non-zero. If it was zero then NULL is returned, which means that socket is not used by net_context at the moment. Role of refcount: * socket instance is assured to be between net_offload->get() and net_offload->put() when refcount > 0, * makes sure that socket instance can be used (its members can be dereferenced) when refcount > 0, * 'context' member is always valid and its members can be dereferenced when refcount > 0. esp_socket_get() gets unused socket, as previously. Additionally it sets refcount to 1 at the end of call, which basically means that from that point such socket can be referenced by other parts of the driver. Each esp_socket_get() call should be followed by esp_socket_unref() and esp_socket_put() to properly invalidate socket and prevent other parts of driver from using it. Add ESP_SOCK_WORKQ_STOPPED flag, which is now used to prevent scheduling more work into driver workqueue. This flag is set in net_offload->put() callback, so that no more socket work (such as processing RX/TX packets or closing socket because of errors) is submitted after that. Introduce mutex lock, which has following role: * protects dst, connect_cb + conn_user_data, recv_cb + recv_user_data, * assures that checking ESP_SOCK_WORKQ_STOPPED flag and actually submitting (or not if net_offload->put was already called) new socket work to workqueue is done in atomic way. As there is a mechanism to prevent submitting new work items to workqueue when net_offload->put() has been executed, then there is no need to explicitly call esp_socket_ref() in esp_workq thread. This is because one reference is being held by net_context (after calling net_context->get()). This is why all the esp_socket_in_use() were simply dropped. Code running from esp_rx thread on the other hand always uses esp_socket_ref_from_link_id() helper function (which is backed by esp_socket_ref()), so that it replaces previous esp_socket_in_use() calls and additionally makes sure that socket stays valid ("in use") until esp_socket_unref() is called. Signed-off-by: Marcin Niestroj <m.niestroj@grinn-global.com>
2020-12-03 22:43:42 +01:00
struct esp_socket *esp_socket_get(struct esp_data *data,
struct net_context *context)
{
drivers: wifi: esp: add thread-safety on esp_socket operations Change type of esp_socket->flags from uint8_t to atomic_t, so that read and write access to those flags is done in atomic (thread-safe) manner. Introduce esp_socket_ref() and esp_socket_unref() functions, which operate on atomic refcount variable. esp_socket_ref() role is to increase refcount if it was already non-zero. If it was zero then NULL is returned, which means that socket is not used by net_context at the moment. Role of refcount: * socket instance is assured to be between net_offload->get() and net_offload->put() when refcount > 0, * makes sure that socket instance can be used (its members can be dereferenced) when refcount > 0, * 'context' member is always valid and its members can be dereferenced when refcount > 0. esp_socket_get() gets unused socket, as previously. Additionally it sets refcount to 1 at the end of call, which basically means that from that point such socket can be referenced by other parts of the driver. Each esp_socket_get() call should be followed by esp_socket_unref() and esp_socket_put() to properly invalidate socket and prevent other parts of driver from using it. Add ESP_SOCK_WORKQ_STOPPED flag, which is now used to prevent scheduling more work into driver workqueue. This flag is set in net_offload->put() callback, so that no more socket work (such as processing RX/TX packets or closing socket because of errors) is submitted after that. Introduce mutex lock, which has following role: * protects dst, connect_cb + conn_user_data, recv_cb + recv_user_data, * assures that checking ESP_SOCK_WORKQ_STOPPED flag and actually submitting (or not if net_offload->put was already called) new socket work to workqueue is done in atomic way. As there is a mechanism to prevent submitting new work items to workqueue when net_offload->put() has been executed, then there is no need to explicitly call esp_socket_ref() in esp_workq thread. This is because one reference is being held by net_context (after calling net_context->get()). This is why all the esp_socket_in_use() were simply dropped. Code running from esp_rx thread on the other hand always uses esp_socket_ref_from_link_id() helper function (which is backed by esp_socket_ref()), so that it replaces previous esp_socket_in_use() calls and additionally makes sure that socket stays valid ("in use") until esp_socket_unref() is called. Signed-off-by: Marcin Niestroj <m.niestroj@grinn-global.com>
2020-12-03 22:43:42 +01:00
struct esp_socket *sock = data->sockets;
struct esp_socket *sock_end = sock + ARRAY_SIZE(data->sockets);
drivers: wifi: esp: add thread-safety on esp_socket operations Change type of esp_socket->flags from uint8_t to atomic_t, so that read and write access to those flags is done in atomic (thread-safe) manner. Introduce esp_socket_ref() and esp_socket_unref() functions, which operate on atomic refcount variable. esp_socket_ref() role is to increase refcount if it was already non-zero. If it was zero then NULL is returned, which means that socket is not used by net_context at the moment. Role of refcount: * socket instance is assured to be between net_offload->get() and net_offload->put() when refcount > 0, * makes sure that socket instance can be used (its members can be dereferenced) when refcount > 0, * 'context' member is always valid and its members can be dereferenced when refcount > 0. esp_socket_get() gets unused socket, as previously. Additionally it sets refcount to 1 at the end of call, which basically means that from that point such socket can be referenced by other parts of the driver. Each esp_socket_get() call should be followed by esp_socket_unref() and esp_socket_put() to properly invalidate socket and prevent other parts of driver from using it. Add ESP_SOCK_WORKQ_STOPPED flag, which is now used to prevent scheduling more work into driver workqueue. This flag is set in net_offload->put() callback, so that no more socket work (such as processing RX/TX packets or closing socket because of errors) is submitted after that. Introduce mutex lock, which has following role: * protects dst, connect_cb + conn_user_data, recv_cb + recv_user_data, * assures that checking ESP_SOCK_WORKQ_STOPPED flag and actually submitting (or not if net_offload->put was already called) new socket work to workqueue is done in atomic way. As there is a mechanism to prevent submitting new work items to workqueue when net_offload->put() has been executed, then there is no need to explicitly call esp_socket_ref() in esp_workq thread. This is because one reference is being held by net_context (after calling net_context->get()). This is why all the esp_socket_in_use() were simply dropped. Code running from esp_rx thread on the other hand always uses esp_socket_ref_from_link_id() helper function (which is backed by esp_socket_ref()), so that it replaces previous esp_socket_in_use() calls and additionally makes sure that socket stays valid ("in use") until esp_socket_unref() is called. Signed-off-by: Marcin Niestroj <m.niestroj@grinn-global.com>
2020-12-03 22:43:42 +01:00
for (; sock < sock_end; sock++) {
if (!esp_socket_flags_test_and_set(sock, ESP_SOCK_IN_USE)) {
/* here we should configure all the stuff needed */
sock->context = context;
context->offload_context = sock;
drivers: wifi: esp: add thread-safety on esp_socket operations Change type of esp_socket->flags from uint8_t to atomic_t, so that read and write access to those flags is done in atomic (thread-safe) manner. Introduce esp_socket_ref() and esp_socket_unref() functions, which operate on atomic refcount variable. esp_socket_ref() role is to increase refcount if it was already non-zero. If it was zero then NULL is returned, which means that socket is not used by net_context at the moment. Role of refcount: * socket instance is assured to be between net_offload->get() and net_offload->put() when refcount > 0, * makes sure that socket instance can be used (its members can be dereferenced) when refcount > 0, * 'context' member is always valid and its members can be dereferenced when refcount > 0. esp_socket_get() gets unused socket, as previously. Additionally it sets refcount to 1 at the end of call, which basically means that from that point such socket can be referenced by other parts of the driver. Each esp_socket_get() call should be followed by esp_socket_unref() and esp_socket_put() to properly invalidate socket and prevent other parts of driver from using it. Add ESP_SOCK_WORKQ_STOPPED flag, which is now used to prevent scheduling more work into driver workqueue. This flag is set in net_offload->put() callback, so that no more socket work (such as processing RX/TX packets or closing socket because of errors) is submitted after that. Introduce mutex lock, which has following role: * protects dst, connect_cb + conn_user_data, recv_cb + recv_user_data, * assures that checking ESP_SOCK_WORKQ_STOPPED flag and actually submitting (or not if net_offload->put was already called) new socket work to workqueue is done in atomic way. As there is a mechanism to prevent submitting new work items to workqueue when net_offload->put() has been executed, then there is no need to explicitly call esp_socket_ref() in esp_workq thread. This is because one reference is being held by net_context (after calling net_context->get()). This is why all the esp_socket_in_use() were simply dropped. Code running from esp_rx thread on the other hand always uses esp_socket_ref_from_link_id() helper function (which is backed by esp_socket_ref()), so that it replaces previous esp_socket_in_use() calls and additionally makes sure that socket stays valid ("in use") until esp_socket_unref() is called. Signed-off-by: Marcin Niestroj <m.niestroj@grinn-global.com>
2020-12-03 22:43:42 +01:00
sock->connect_cb = NULL;
sock->recv_cb = NULL;
drivers: wifi: esp: add thread-safety on esp_socket operations Change type of esp_socket->flags from uint8_t to atomic_t, so that read and write access to those flags is done in atomic (thread-safe) manner. Introduce esp_socket_ref() and esp_socket_unref() functions, which operate on atomic refcount variable. esp_socket_ref() role is to increase refcount if it was already non-zero. If it was zero then NULL is returned, which means that socket is not used by net_context at the moment. Role of refcount: * socket instance is assured to be between net_offload->get() and net_offload->put() when refcount > 0, * makes sure that socket instance can be used (its members can be dereferenced) when refcount > 0, * 'context' member is always valid and its members can be dereferenced when refcount > 0. esp_socket_get() gets unused socket, as previously. Additionally it sets refcount to 1 at the end of call, which basically means that from that point such socket can be referenced by other parts of the driver. Each esp_socket_get() call should be followed by esp_socket_unref() and esp_socket_put() to properly invalidate socket and prevent other parts of driver from using it. Add ESP_SOCK_WORKQ_STOPPED flag, which is now used to prevent scheduling more work into driver workqueue. This flag is set in net_offload->put() callback, so that no more socket work (such as processing RX/TX packets or closing socket because of errors) is submitted after that. Introduce mutex lock, which has following role: * protects dst, connect_cb + conn_user_data, recv_cb + recv_user_data, * assures that checking ESP_SOCK_WORKQ_STOPPED flag and actually submitting (or not if net_offload->put was already called) new socket work to workqueue is done in atomic way. As there is a mechanism to prevent submitting new work items to workqueue when net_offload->put() has been executed, then there is no need to explicitly call esp_socket_ref() in esp_workq thread. This is because one reference is being held by net_context (after calling net_context->get()). This is why all the esp_socket_in_use() were simply dropped. Code running from esp_rx thread on the other hand always uses esp_socket_ref_from_link_id() helper function (which is backed by esp_socket_ref()), so that it replaces previous esp_socket_in_use() calls and additionally makes sure that socket stays valid ("in use") until esp_socket_unref() is called. Signed-off-by: Marcin Niestroj <m.niestroj@grinn-global.com>
2020-12-03 22:43:42 +01:00
atomic_inc(&sock->refcount);
drivers: wifi: esp: add thread-safety on esp_socket operations Change type of esp_socket->flags from uint8_t to atomic_t, so that read and write access to those flags is done in atomic (thread-safe) manner. Introduce esp_socket_ref() and esp_socket_unref() functions, which operate on atomic refcount variable. esp_socket_ref() role is to increase refcount if it was already non-zero. If it was zero then NULL is returned, which means that socket is not used by net_context at the moment. Role of refcount: * socket instance is assured to be between net_offload->get() and net_offload->put() when refcount > 0, * makes sure that socket instance can be used (its members can be dereferenced) when refcount > 0, * 'context' member is always valid and its members can be dereferenced when refcount > 0. esp_socket_get() gets unused socket, as previously. Additionally it sets refcount to 1 at the end of call, which basically means that from that point such socket can be referenced by other parts of the driver. Each esp_socket_get() call should be followed by esp_socket_unref() and esp_socket_put() to properly invalidate socket and prevent other parts of driver from using it. Add ESP_SOCK_WORKQ_STOPPED flag, which is now used to prevent scheduling more work into driver workqueue. This flag is set in net_offload->put() callback, so that no more socket work (such as processing RX/TX packets or closing socket because of errors) is submitted after that. Introduce mutex lock, which has following role: * protects dst, connect_cb + conn_user_data, recv_cb + recv_user_data, * assures that checking ESP_SOCK_WORKQ_STOPPED flag and actually submitting (or not if net_offload->put was already called) new socket work to workqueue is done in atomic way. As there is a mechanism to prevent submitting new work items to workqueue when net_offload->put() has been executed, then there is no need to explicitly call esp_socket_ref() in esp_workq thread. This is because one reference is being held by net_context (after calling net_context->get()). This is why all the esp_socket_in_use() were simply dropped. Code running from esp_rx thread on the other hand always uses esp_socket_ref_from_link_id() helper function (which is backed by esp_socket_ref()), so that it replaces previous esp_socket_in_use() calls and additionally makes sure that socket stays valid ("in use") until esp_socket_unref() is called. Signed-off-by: Marcin Niestroj <m.niestroj@grinn-global.com>
2020-12-03 22:43:42 +01:00
return sock;
}
}
return NULL;
}
int esp_socket_put(struct esp_socket *sock)
{
drivers: wifi: esp: add thread-safety on esp_socket operations Change type of esp_socket->flags from uint8_t to atomic_t, so that read and write access to those flags is done in atomic (thread-safe) manner. Introduce esp_socket_ref() and esp_socket_unref() functions, which operate on atomic refcount variable. esp_socket_ref() role is to increase refcount if it was already non-zero. If it was zero then NULL is returned, which means that socket is not used by net_context at the moment. Role of refcount: * socket instance is assured to be between net_offload->get() and net_offload->put() when refcount > 0, * makes sure that socket instance can be used (its members can be dereferenced) when refcount > 0, * 'context' member is always valid and its members can be dereferenced when refcount > 0. esp_socket_get() gets unused socket, as previously. Additionally it sets refcount to 1 at the end of call, which basically means that from that point such socket can be referenced by other parts of the driver. Each esp_socket_get() call should be followed by esp_socket_unref() and esp_socket_put() to properly invalidate socket and prevent other parts of driver from using it. Add ESP_SOCK_WORKQ_STOPPED flag, which is now used to prevent scheduling more work into driver workqueue. This flag is set in net_offload->put() callback, so that no more socket work (such as processing RX/TX packets or closing socket because of errors) is submitted after that. Introduce mutex lock, which has following role: * protects dst, connect_cb + conn_user_data, recv_cb + recv_user_data, * assures that checking ESP_SOCK_WORKQ_STOPPED flag and actually submitting (or not if net_offload->put was already called) new socket work to workqueue is done in atomic way. As there is a mechanism to prevent submitting new work items to workqueue when net_offload->put() has been executed, then there is no need to explicitly call esp_socket_ref() in esp_workq thread. This is because one reference is being held by net_context (after calling net_context->get()). This is why all the esp_socket_in_use() were simply dropped. Code running from esp_rx thread on the other hand always uses esp_socket_ref_from_link_id() helper function (which is backed by esp_socket_ref()), so that it replaces previous esp_socket_in_use() calls and additionally makes sure that socket stays valid ("in use") until esp_socket_unref() is called. Signed-off-by: Marcin Niestroj <m.niestroj@grinn-global.com>
2020-12-03 22:43:42 +01:00
atomic_clear(&sock->flags);
return 0;
}
drivers: wifi: esp: add thread-safety on esp_socket operations Change type of esp_socket->flags from uint8_t to atomic_t, so that read and write access to those flags is done in atomic (thread-safe) manner. Introduce esp_socket_ref() and esp_socket_unref() functions, which operate on atomic refcount variable. esp_socket_ref() role is to increase refcount if it was already non-zero. If it was zero then NULL is returned, which means that socket is not used by net_context at the moment. Role of refcount: * socket instance is assured to be between net_offload->get() and net_offload->put() when refcount > 0, * makes sure that socket instance can be used (its members can be dereferenced) when refcount > 0, * 'context' member is always valid and its members can be dereferenced when refcount > 0. esp_socket_get() gets unused socket, as previously. Additionally it sets refcount to 1 at the end of call, which basically means that from that point such socket can be referenced by other parts of the driver. Each esp_socket_get() call should be followed by esp_socket_unref() and esp_socket_put() to properly invalidate socket and prevent other parts of driver from using it. Add ESP_SOCK_WORKQ_STOPPED flag, which is now used to prevent scheduling more work into driver workqueue. This flag is set in net_offload->put() callback, so that no more socket work (such as processing RX/TX packets or closing socket because of errors) is submitted after that. Introduce mutex lock, which has following role: * protects dst, connect_cb + conn_user_data, recv_cb + recv_user_data, * assures that checking ESP_SOCK_WORKQ_STOPPED flag and actually submitting (or not if net_offload->put was already called) new socket work to workqueue is done in atomic way. As there is a mechanism to prevent submitting new work items to workqueue when net_offload->put() has been executed, then there is no need to explicitly call esp_socket_ref() in esp_workq thread. This is because one reference is being held by net_context (after calling net_context->get()). This is why all the esp_socket_in_use() were simply dropped. Code running from esp_rx thread on the other hand always uses esp_socket_ref_from_link_id() helper function (which is backed by esp_socket_ref()), so that it replaces previous esp_socket_in_use() calls and additionally makes sure that socket stays valid ("in use") until esp_socket_unref() is called. Signed-off-by: Marcin Niestroj <m.niestroj@grinn-global.com>
2020-12-03 22:43:42 +01:00
struct esp_socket *esp_socket_ref(struct esp_socket *sock)
{
drivers: wifi: esp: add thread-safety on esp_socket operations Change type of esp_socket->flags from uint8_t to atomic_t, so that read and write access to those flags is done in atomic (thread-safe) manner. Introduce esp_socket_ref() and esp_socket_unref() functions, which operate on atomic refcount variable. esp_socket_ref() role is to increase refcount if it was already non-zero. If it was zero then NULL is returned, which means that socket is not used by net_context at the moment. Role of refcount: * socket instance is assured to be between net_offload->get() and net_offload->put() when refcount > 0, * makes sure that socket instance can be used (its members can be dereferenced) when refcount > 0, * 'context' member is always valid and its members can be dereferenced when refcount > 0. esp_socket_get() gets unused socket, as previously. Additionally it sets refcount to 1 at the end of call, which basically means that from that point such socket can be referenced by other parts of the driver. Each esp_socket_get() call should be followed by esp_socket_unref() and esp_socket_put() to properly invalidate socket and prevent other parts of driver from using it. Add ESP_SOCK_WORKQ_STOPPED flag, which is now used to prevent scheduling more work into driver workqueue. This flag is set in net_offload->put() callback, so that no more socket work (such as processing RX/TX packets or closing socket because of errors) is submitted after that. Introduce mutex lock, which has following role: * protects dst, connect_cb + conn_user_data, recv_cb + recv_user_data, * assures that checking ESP_SOCK_WORKQ_STOPPED flag and actually submitting (or not if net_offload->put was already called) new socket work to workqueue is done in atomic way. As there is a mechanism to prevent submitting new work items to workqueue when net_offload->put() has been executed, then there is no need to explicitly call esp_socket_ref() in esp_workq thread. This is because one reference is being held by net_context (after calling net_context->get()). This is why all the esp_socket_in_use() were simply dropped. Code running from esp_rx thread on the other hand always uses esp_socket_ref_from_link_id() helper function (which is backed by esp_socket_ref()), so that it replaces previous esp_socket_in_use() calls and additionally makes sure that socket stays valid ("in use") until esp_socket_unref() is called. Signed-off-by: Marcin Niestroj <m.niestroj@grinn-global.com>
2020-12-03 22:43:42 +01:00
atomic_val_t ref;
drivers: wifi: esp: add thread-safety on esp_socket operations Change type of esp_socket->flags from uint8_t to atomic_t, so that read and write access to those flags is done in atomic (thread-safe) manner. Introduce esp_socket_ref() and esp_socket_unref() functions, which operate on atomic refcount variable. esp_socket_ref() role is to increase refcount if it was already non-zero. If it was zero then NULL is returned, which means that socket is not used by net_context at the moment. Role of refcount: * socket instance is assured to be between net_offload->get() and net_offload->put() when refcount > 0, * makes sure that socket instance can be used (its members can be dereferenced) when refcount > 0, * 'context' member is always valid and its members can be dereferenced when refcount > 0. esp_socket_get() gets unused socket, as previously. Additionally it sets refcount to 1 at the end of call, which basically means that from that point such socket can be referenced by other parts of the driver. Each esp_socket_get() call should be followed by esp_socket_unref() and esp_socket_put() to properly invalidate socket and prevent other parts of driver from using it. Add ESP_SOCK_WORKQ_STOPPED flag, which is now used to prevent scheduling more work into driver workqueue. This flag is set in net_offload->put() callback, so that no more socket work (such as processing RX/TX packets or closing socket because of errors) is submitted after that. Introduce mutex lock, which has following role: * protects dst, connect_cb + conn_user_data, recv_cb + recv_user_data, * assures that checking ESP_SOCK_WORKQ_STOPPED flag and actually submitting (or not if net_offload->put was already called) new socket work to workqueue is done in atomic way. As there is a mechanism to prevent submitting new work items to workqueue when net_offload->put() has been executed, then there is no need to explicitly call esp_socket_ref() in esp_workq thread. This is because one reference is being held by net_context (after calling net_context->get()). This is why all the esp_socket_in_use() were simply dropped. Code running from esp_rx thread on the other hand always uses esp_socket_ref_from_link_id() helper function (which is backed by esp_socket_ref()), so that it replaces previous esp_socket_in_use() calls and additionally makes sure that socket stays valid ("in use") until esp_socket_unref() is called. Signed-off-by: Marcin Niestroj <m.niestroj@grinn-global.com>
2020-12-03 22:43:42 +01:00
do {
ref = atomic_get(&sock->refcount);
if (!ref) {
return NULL;
}
drivers: wifi: esp: add thread-safety on esp_socket operations Change type of esp_socket->flags from uint8_t to atomic_t, so that read and write access to those flags is done in atomic (thread-safe) manner. Introduce esp_socket_ref() and esp_socket_unref() functions, which operate on atomic refcount variable. esp_socket_ref() role is to increase refcount if it was already non-zero. If it was zero then NULL is returned, which means that socket is not used by net_context at the moment. Role of refcount: * socket instance is assured to be between net_offload->get() and net_offload->put() when refcount > 0, * makes sure that socket instance can be used (its members can be dereferenced) when refcount > 0, * 'context' member is always valid and its members can be dereferenced when refcount > 0. esp_socket_get() gets unused socket, as previously. Additionally it sets refcount to 1 at the end of call, which basically means that from that point such socket can be referenced by other parts of the driver. Each esp_socket_get() call should be followed by esp_socket_unref() and esp_socket_put() to properly invalidate socket and prevent other parts of driver from using it. Add ESP_SOCK_WORKQ_STOPPED flag, which is now used to prevent scheduling more work into driver workqueue. This flag is set in net_offload->put() callback, so that no more socket work (such as processing RX/TX packets or closing socket because of errors) is submitted after that. Introduce mutex lock, which has following role: * protects dst, connect_cb + conn_user_data, recv_cb + recv_user_data, * assures that checking ESP_SOCK_WORKQ_STOPPED flag and actually submitting (or not if net_offload->put was already called) new socket work to workqueue is done in atomic way. As there is a mechanism to prevent submitting new work items to workqueue when net_offload->put() has been executed, then there is no need to explicitly call esp_socket_ref() in esp_workq thread. This is because one reference is being held by net_context (after calling net_context->get()). This is why all the esp_socket_in_use() were simply dropped. Code running from esp_rx thread on the other hand always uses esp_socket_ref_from_link_id() helper function (which is backed by esp_socket_ref()), so that it replaces previous esp_socket_in_use() calls and additionally makes sure that socket stays valid ("in use") until esp_socket_unref() is called. Signed-off-by: Marcin Niestroj <m.niestroj@grinn-global.com>
2020-12-03 22:43:42 +01:00
} while (!atomic_cas(&sock->refcount, ref, ref + 1));
drivers: wifi: esp: add thread-safety on esp_socket operations Change type of esp_socket->flags from uint8_t to atomic_t, so that read and write access to those flags is done in atomic (thread-safe) manner. Introduce esp_socket_ref() and esp_socket_unref() functions, which operate on atomic refcount variable. esp_socket_ref() role is to increase refcount if it was already non-zero. If it was zero then NULL is returned, which means that socket is not used by net_context at the moment. Role of refcount: * socket instance is assured to be between net_offload->get() and net_offload->put() when refcount > 0, * makes sure that socket instance can be used (its members can be dereferenced) when refcount > 0, * 'context' member is always valid and its members can be dereferenced when refcount > 0. esp_socket_get() gets unused socket, as previously. Additionally it sets refcount to 1 at the end of call, which basically means that from that point such socket can be referenced by other parts of the driver. Each esp_socket_get() call should be followed by esp_socket_unref() and esp_socket_put() to properly invalidate socket and prevent other parts of driver from using it. Add ESP_SOCK_WORKQ_STOPPED flag, which is now used to prevent scheduling more work into driver workqueue. This flag is set in net_offload->put() callback, so that no more socket work (such as processing RX/TX packets or closing socket because of errors) is submitted after that. Introduce mutex lock, which has following role: * protects dst, connect_cb + conn_user_data, recv_cb + recv_user_data, * assures that checking ESP_SOCK_WORKQ_STOPPED flag and actually submitting (or not if net_offload->put was already called) new socket work to workqueue is done in atomic way. As there is a mechanism to prevent submitting new work items to workqueue when net_offload->put() has been executed, then there is no need to explicitly call esp_socket_ref() in esp_workq thread. This is because one reference is being held by net_context (after calling net_context->get()). This is why all the esp_socket_in_use() were simply dropped. Code running from esp_rx thread on the other hand always uses esp_socket_ref_from_link_id() helper function (which is backed by esp_socket_ref()), so that it replaces previous esp_socket_in_use() calls and additionally makes sure that socket stays valid ("in use") until esp_socket_unref() is called. Signed-off-by: Marcin Niestroj <m.niestroj@grinn-global.com>
2020-12-03 22:43:42 +01:00
return sock;
}
void esp_socket_unref(struct esp_socket *sock)
{
atomic_val_t ref;
do {
ref = atomic_get(&sock->refcount);
if (!ref) {
return;
}
} while (!atomic_cas(&sock->refcount, ref, ref - 1));
k_sem_give(&sock->sem_free);
}
void esp_socket_init(struct esp_data *data)
{
struct esp_socket *sock;
int i;
for (i = 0; i < ARRAY_SIZE(data->sockets); ++i) {
sock = &data->sockets[i];
sock->idx = i;
drivers: wifi: esp: add thread-safety on esp_socket operations Change type of esp_socket->flags from uint8_t to atomic_t, so that read and write access to those flags is done in atomic (thread-safe) manner. Introduce esp_socket_ref() and esp_socket_unref() functions, which operate on atomic refcount variable. esp_socket_ref() role is to increase refcount if it was already non-zero. If it was zero then NULL is returned, which means that socket is not used by net_context at the moment. Role of refcount: * socket instance is assured to be between net_offload->get() and net_offload->put() when refcount > 0, * makes sure that socket instance can be used (its members can be dereferenced) when refcount > 0, * 'context' member is always valid and its members can be dereferenced when refcount > 0. esp_socket_get() gets unused socket, as previously. Additionally it sets refcount to 1 at the end of call, which basically means that from that point such socket can be referenced by other parts of the driver. Each esp_socket_get() call should be followed by esp_socket_unref() and esp_socket_put() to properly invalidate socket and prevent other parts of driver from using it. Add ESP_SOCK_WORKQ_STOPPED flag, which is now used to prevent scheduling more work into driver workqueue. This flag is set in net_offload->put() callback, so that no more socket work (such as processing RX/TX packets or closing socket because of errors) is submitted after that. Introduce mutex lock, which has following role: * protects dst, connect_cb + conn_user_data, recv_cb + recv_user_data, * assures that checking ESP_SOCK_WORKQ_STOPPED flag and actually submitting (or not if net_offload->put was already called) new socket work to workqueue is done in atomic way. As there is a mechanism to prevent submitting new work items to workqueue when net_offload->put() has been executed, then there is no need to explicitly call esp_socket_ref() in esp_workq thread. This is because one reference is being held by net_context (after calling net_context->get()). This is why all the esp_socket_in_use() were simply dropped. Code running from esp_rx thread on the other hand always uses esp_socket_ref_from_link_id() helper function (which is backed by esp_socket_ref()), so that it replaces previous esp_socket_in_use() calls and additionally makes sure that socket stays valid ("in use") until esp_socket_unref() is called. Signed-off-by: Marcin Niestroj <m.niestroj@grinn-global.com>
2020-12-03 22:43:42 +01:00
sock->link_id = i;
atomic_clear(&sock->refcount);
atomic_clear(&sock->flags);
k_mutex_init(&sock->lock);
k_sem_init(&sock->sem_data_ready, 0, 1);
k_work_init(&sock->connect_work, esp_connect_work);
k_work_init(&sock->recvdata_work, esp_recvdata_work);
k_work_init(&sock->close_work, esp_close_work);
k_work_init(&sock->send_work, esp_send_work);
k_fifo_init(&sock->tx_fifo);
}
}
static struct net_pkt *esp_socket_prepare_pkt(struct esp_socket *sock,
struct net_buf *src,
size_t offset, size_t len)
{
struct esp_data *data = esp_socket_to_dev(sock);
struct net_buf *frag;
struct net_pkt *pkt;
size_t to_copy;
pkt = net_pkt_rx_alloc_with_buffer(data->net_iface, len, AF_UNSPEC,
0, RX_NET_PKT_ALLOC_TIMEOUT);
if (!pkt) {
return NULL;
}
frag = src;
/* find the right fragment to start copying from */
while (frag && offset >= frag->len) {
offset -= frag->len;
frag = frag->frags;
}
/* traverse the fragment chain until len bytes are copied */
while (frag && len > 0) {
to_copy = MIN(len, frag->len - offset);
if (net_pkt_write(pkt, frag->data + offset, to_copy) != 0) {
net_pkt_unref(pkt);
return NULL;
}
/* to_copy is always <= len */
len -= to_copy;
frag = frag->frags;
/* after the first iteration, this value will be 0 */
offset = 0;
}
net_pkt_set_context(pkt, sock->context);
net_pkt_cursor_init(pkt);
return pkt;
}
void esp_socket_rx(struct esp_socket *sock, struct net_buf *buf,
size_t offset, size_t len)
{
struct net_pkt *pkt;
drivers: wifi: esp: add thread-safety on esp_socket operations Change type of esp_socket->flags from uint8_t to atomic_t, so that read and write access to those flags is done in atomic (thread-safe) manner. Introduce esp_socket_ref() and esp_socket_unref() functions, which operate on atomic refcount variable. esp_socket_ref() role is to increase refcount if it was already non-zero. If it was zero then NULL is returned, which means that socket is not used by net_context at the moment. Role of refcount: * socket instance is assured to be between net_offload->get() and net_offload->put() when refcount > 0, * makes sure that socket instance can be used (its members can be dereferenced) when refcount > 0, * 'context' member is always valid and its members can be dereferenced when refcount > 0. esp_socket_get() gets unused socket, as previously. Additionally it sets refcount to 1 at the end of call, which basically means that from that point such socket can be referenced by other parts of the driver. Each esp_socket_get() call should be followed by esp_socket_unref() and esp_socket_put() to properly invalidate socket and prevent other parts of driver from using it. Add ESP_SOCK_WORKQ_STOPPED flag, which is now used to prevent scheduling more work into driver workqueue. This flag is set in net_offload->put() callback, so that no more socket work (such as processing RX/TX packets or closing socket because of errors) is submitted after that. Introduce mutex lock, which has following role: * protects dst, connect_cb + conn_user_data, recv_cb + recv_user_data, * assures that checking ESP_SOCK_WORKQ_STOPPED flag and actually submitting (or not if net_offload->put was already called) new socket work to workqueue is done in atomic way. As there is a mechanism to prevent submitting new work items to workqueue when net_offload->put() has been executed, then there is no need to explicitly call esp_socket_ref() in esp_workq thread. This is because one reference is being held by net_context (after calling net_context->get()). This is why all the esp_socket_in_use() were simply dropped. Code running from esp_rx thread on the other hand always uses esp_socket_ref_from_link_id() helper function (which is backed by esp_socket_ref()), so that it replaces previous esp_socket_in_use() calls and additionally makes sure that socket stays valid ("in use") until esp_socket_unref() is called. Signed-off-by: Marcin Niestroj <m.niestroj@grinn-global.com>
2020-12-03 22:43:42 +01:00
atomic_val_t flags;
drivers: wifi: esp: add thread-safety on esp_socket operations Change type of esp_socket->flags from uint8_t to atomic_t, so that read and write access to those flags is done in atomic (thread-safe) manner. Introduce esp_socket_ref() and esp_socket_unref() functions, which operate on atomic refcount variable. esp_socket_ref() role is to increase refcount if it was already non-zero. If it was zero then NULL is returned, which means that socket is not used by net_context at the moment. Role of refcount: * socket instance is assured to be between net_offload->get() and net_offload->put() when refcount > 0, * makes sure that socket instance can be used (its members can be dereferenced) when refcount > 0, * 'context' member is always valid and its members can be dereferenced when refcount > 0. esp_socket_get() gets unused socket, as previously. Additionally it sets refcount to 1 at the end of call, which basically means that from that point such socket can be referenced by other parts of the driver. Each esp_socket_get() call should be followed by esp_socket_unref() and esp_socket_put() to properly invalidate socket and prevent other parts of driver from using it. Add ESP_SOCK_WORKQ_STOPPED flag, which is now used to prevent scheduling more work into driver workqueue. This flag is set in net_offload->put() callback, so that no more socket work (such as processing RX/TX packets or closing socket because of errors) is submitted after that. Introduce mutex lock, which has following role: * protects dst, connect_cb + conn_user_data, recv_cb + recv_user_data, * assures that checking ESP_SOCK_WORKQ_STOPPED flag and actually submitting (or not if net_offload->put was already called) new socket work to workqueue is done in atomic way. As there is a mechanism to prevent submitting new work items to workqueue when net_offload->put() has been executed, then there is no need to explicitly call esp_socket_ref() in esp_workq thread. This is because one reference is being held by net_context (after calling net_context->get()). This is why all the esp_socket_in_use() were simply dropped. Code running from esp_rx thread on the other hand always uses esp_socket_ref_from_link_id() helper function (which is backed by esp_socket_ref()), so that it replaces previous esp_socket_in_use() calls and additionally makes sure that socket stays valid ("in use") until esp_socket_unref() is called. Signed-off-by: Marcin Niestroj <m.niestroj@grinn-global.com>
2020-12-03 22:43:42 +01:00
flags = esp_socket_flags(sock);
if (!(flags & ESP_SOCK_CONNECTED) ||
(flags & ESP_SOCK_CLOSE_PENDING)) {
LOG_DBG("Received data on closed link %d", sock->link_id);
return;
}
pkt = esp_socket_prepare_pkt(sock, buf, offset, len);
if (!pkt) {
LOG_ERR("Failed to get net_pkt: len %zu", len);
if (esp_socket_type(sock) == SOCK_STREAM) {
drivers: wifi: esp: add thread-safety on esp_socket operations Change type of esp_socket->flags from uint8_t to atomic_t, so that read and write access to those flags is done in atomic (thread-safe) manner. Introduce esp_socket_ref() and esp_socket_unref() functions, which operate on atomic refcount variable. esp_socket_ref() role is to increase refcount if it was already non-zero. If it was zero then NULL is returned, which means that socket is not used by net_context at the moment. Role of refcount: * socket instance is assured to be between net_offload->get() and net_offload->put() when refcount > 0, * makes sure that socket instance can be used (its members can be dereferenced) when refcount > 0, * 'context' member is always valid and its members can be dereferenced when refcount > 0. esp_socket_get() gets unused socket, as previously. Additionally it sets refcount to 1 at the end of call, which basically means that from that point such socket can be referenced by other parts of the driver. Each esp_socket_get() call should be followed by esp_socket_unref() and esp_socket_put() to properly invalidate socket and prevent other parts of driver from using it. Add ESP_SOCK_WORKQ_STOPPED flag, which is now used to prevent scheduling more work into driver workqueue. This flag is set in net_offload->put() callback, so that no more socket work (such as processing RX/TX packets or closing socket because of errors) is submitted after that. Introduce mutex lock, which has following role: * protects dst, connect_cb + conn_user_data, recv_cb + recv_user_data, * assures that checking ESP_SOCK_WORKQ_STOPPED flag and actually submitting (or not if net_offload->put was already called) new socket work to workqueue is done in atomic way. As there is a mechanism to prevent submitting new work items to workqueue when net_offload->put() has been executed, then there is no need to explicitly call esp_socket_ref() in esp_workq thread. This is because one reference is being held by net_context (after calling net_context->get()). This is why all the esp_socket_in_use() were simply dropped. Code running from esp_rx thread on the other hand always uses esp_socket_ref_from_link_id() helper function (which is backed by esp_socket_ref()), so that it replaces previous esp_socket_in_use() calls and additionally makes sure that socket stays valid ("in use") until esp_socket_unref() is called. Signed-off-by: Marcin Niestroj <m.niestroj@grinn-global.com>
2020-12-03 22:43:42 +01:00
if (!esp_socket_flags_test_and_set(sock,
ESP_SOCK_CLOSE_PENDING)) {
esp_socket_work_submit(sock, &sock->close_work);
}
}
return;
}
#ifdef CONFIG_NET_SOCKETS
/* We need to claim the net_context mutex here so that the ordering of
* net_context and socket mutex claims matches the TX code path. Failure
* to do so can lead to deadlocks.
*/
if (sock->context->cond.lock) {
k_mutex_lock(sock->context->cond.lock, K_FOREVER);
}
#endif /* CONFIG_NET_SOCKETS */
k_mutex_lock(&sock->lock, K_FOREVER);
if (sock->recv_cb) {
sock->recv_cb(sock->context, pkt, NULL, NULL,
0, sock->recv_user_data);
k_sem_give(&sock->sem_data_ready);
} else {
/* Discard */
drivers: wifi: esp: add thread-safety on esp_socket operations Change type of esp_socket->flags from uint8_t to atomic_t, so that read and write access to those flags is done in atomic (thread-safe) manner. Introduce esp_socket_ref() and esp_socket_unref() functions, which operate on atomic refcount variable. esp_socket_ref() role is to increase refcount if it was already non-zero. If it was zero then NULL is returned, which means that socket is not used by net_context at the moment. Role of refcount: * socket instance is assured to be between net_offload->get() and net_offload->put() when refcount > 0, * makes sure that socket instance can be used (its members can be dereferenced) when refcount > 0, * 'context' member is always valid and its members can be dereferenced when refcount > 0. esp_socket_get() gets unused socket, as previously. Additionally it sets refcount to 1 at the end of call, which basically means that from that point such socket can be referenced by other parts of the driver. Each esp_socket_get() call should be followed by esp_socket_unref() and esp_socket_put() to properly invalidate socket and prevent other parts of driver from using it. Add ESP_SOCK_WORKQ_STOPPED flag, which is now used to prevent scheduling more work into driver workqueue. This flag is set in net_offload->put() callback, so that no more socket work (such as processing RX/TX packets or closing socket because of errors) is submitted after that. Introduce mutex lock, which has following role: * protects dst, connect_cb + conn_user_data, recv_cb + recv_user_data, * assures that checking ESP_SOCK_WORKQ_STOPPED flag and actually submitting (or not if net_offload->put was already called) new socket work to workqueue is done in atomic way. As there is a mechanism to prevent submitting new work items to workqueue when net_offload->put() has been executed, then there is no need to explicitly call esp_socket_ref() in esp_workq thread. This is because one reference is being held by net_context (after calling net_context->get()). This is why all the esp_socket_in_use() were simply dropped. Code running from esp_rx thread on the other hand always uses esp_socket_ref_from_link_id() helper function (which is backed by esp_socket_ref()), so that it replaces previous esp_socket_in_use() calls and additionally makes sure that socket stays valid ("in use") until esp_socket_unref() is called. Signed-off-by: Marcin Niestroj <m.niestroj@grinn-global.com>
2020-12-03 22:43:42 +01:00
net_pkt_unref(pkt);
}
k_mutex_unlock(&sock->lock);
#ifdef CONFIG_NET_SOCKETS
if (sock->context->cond.lock) {
k_mutex_unlock(sock->context->cond.lock);
}
#endif /* CONFIG_NET_SOCKETS */
}
void esp_socket_close(struct esp_socket *sock)
{
struct esp_data *dev = esp_socket_to_dev(sock);
char cmd_buf[sizeof("AT+CIPCLOSE=0")];
int ret;
snprintk(cmd_buf, sizeof(cmd_buf), "AT+CIPCLOSE=%d",
sock->link_id);
ret = esp_cmd_send(dev, NULL, 0, cmd_buf, ESP_CMD_TIMEOUT);
if (ret < 0) {
/* FIXME:
* If link doesn't close correctly here, esp_get could
* allocate a socket with an already open link.
*/
LOG_ERR("Failed to close link %d, ret %d",
sock->link_id, ret);
}
}
static void esp_workq_flush_work(struct k_work *work)
{
struct esp_workq_flush_data *flush =
CONTAINER_OF(work, struct esp_workq_flush_data, work);
k_sem_give(&flush->sem);
}
drivers: wifi: esp: add thread-safety on esp_socket operations Change type of esp_socket->flags from uint8_t to atomic_t, so that read and write access to those flags is done in atomic (thread-safe) manner. Introduce esp_socket_ref() and esp_socket_unref() functions, which operate on atomic refcount variable. esp_socket_ref() role is to increase refcount if it was already non-zero. If it was zero then NULL is returned, which means that socket is not used by net_context at the moment. Role of refcount: * socket instance is assured to be between net_offload->get() and net_offload->put() when refcount > 0, * makes sure that socket instance can be used (its members can be dereferenced) when refcount > 0, * 'context' member is always valid and its members can be dereferenced when refcount > 0. esp_socket_get() gets unused socket, as previously. Additionally it sets refcount to 1 at the end of call, which basically means that from that point such socket can be referenced by other parts of the driver. Each esp_socket_get() call should be followed by esp_socket_unref() and esp_socket_put() to properly invalidate socket and prevent other parts of driver from using it. Add ESP_SOCK_WORKQ_STOPPED flag, which is now used to prevent scheduling more work into driver workqueue. This flag is set in net_offload->put() callback, so that no more socket work (such as processing RX/TX packets or closing socket because of errors) is submitted after that. Introduce mutex lock, which has following role: * protects dst, connect_cb + conn_user_data, recv_cb + recv_user_data, * assures that checking ESP_SOCK_WORKQ_STOPPED flag and actually submitting (or not if net_offload->put was already called) new socket work to workqueue is done in atomic way. As there is a mechanism to prevent submitting new work items to workqueue when net_offload->put() has been executed, then there is no need to explicitly call esp_socket_ref() in esp_workq thread. This is because one reference is being held by net_context (after calling net_context->get()). This is why all the esp_socket_in_use() were simply dropped. Code running from esp_rx thread on the other hand always uses esp_socket_ref_from_link_id() helper function (which is backed by esp_socket_ref()), so that it replaces previous esp_socket_in_use() calls and additionally makes sure that socket stays valid ("in use") until esp_socket_unref() is called. Signed-off-by: Marcin Niestroj <m.niestroj@grinn-global.com>
2020-12-03 22:43:42 +01:00
void esp_socket_workq_stop_and_flush(struct esp_socket *sock)
{
struct esp_workq_flush_data flush;
k_work_init(&flush.work, esp_workq_flush_work);
k_sem_init(&flush.sem, 0, 1);
drivers: wifi: esp: add thread-safety on esp_socket operations Change type of esp_socket->flags from uint8_t to atomic_t, so that read and write access to those flags is done in atomic (thread-safe) manner. Introduce esp_socket_ref() and esp_socket_unref() functions, which operate on atomic refcount variable. esp_socket_ref() role is to increase refcount if it was already non-zero. If it was zero then NULL is returned, which means that socket is not used by net_context at the moment. Role of refcount: * socket instance is assured to be between net_offload->get() and net_offload->put() when refcount > 0, * makes sure that socket instance can be used (its members can be dereferenced) when refcount > 0, * 'context' member is always valid and its members can be dereferenced when refcount > 0. esp_socket_get() gets unused socket, as previously. Additionally it sets refcount to 1 at the end of call, which basically means that from that point such socket can be referenced by other parts of the driver. Each esp_socket_get() call should be followed by esp_socket_unref() and esp_socket_put() to properly invalidate socket and prevent other parts of driver from using it. Add ESP_SOCK_WORKQ_STOPPED flag, which is now used to prevent scheduling more work into driver workqueue. This flag is set in net_offload->put() callback, so that no more socket work (such as processing RX/TX packets or closing socket because of errors) is submitted after that. Introduce mutex lock, which has following role: * protects dst, connect_cb + conn_user_data, recv_cb + recv_user_data, * assures that checking ESP_SOCK_WORKQ_STOPPED flag and actually submitting (or not if net_offload->put was already called) new socket work to workqueue is done in atomic way. As there is a mechanism to prevent submitting new work items to workqueue when net_offload->put() has been executed, then there is no need to explicitly call esp_socket_ref() in esp_workq thread. This is because one reference is being held by net_context (after calling net_context->get()). This is why all the esp_socket_in_use() were simply dropped. Code running from esp_rx thread on the other hand always uses esp_socket_ref_from_link_id() helper function (which is backed by esp_socket_ref()), so that it replaces previous esp_socket_in_use() calls and additionally makes sure that socket stays valid ("in use") until esp_socket_unref() is called. Signed-off-by: Marcin Niestroj <m.niestroj@grinn-global.com>
2020-12-03 22:43:42 +01:00
k_mutex_lock(&sock->lock, K_FOREVER);
esp_socket_flags_set(sock, ESP_SOCK_WORKQ_STOPPED);
__esp_socket_work_submit(sock, &flush.work);
k_mutex_unlock(&sock->lock);
k_sem_take(&flush.sem, K_FOREVER);
}