vfs: add support for mutiple selects in eventfd

This commit is contained in:
Jiacheng Guo 2021-03-08 09:44:58 +08:00
parent 30e9345bc7
commit 7b911e4641
7 changed files with 469 additions and 196 deletions

View File

@ -151,7 +151,7 @@ static spp_slot_t *spp_malloc_slot(void)
break;
}
if (spp_local_param.spp_mode == ESP_SPP_MODE_VFS) {
if (esp_vfs_register_fd(spp_local_param.spp_vfs_id, &(*slot)->fd) != ESP_OK) {
if (esp_vfs_register_fd(spp_local_param.spp_vfs_id, -1, /*permanent=*/true, &(*slot)->fd) != ESP_OK) {
BTC_TRACE_ERROR("%s unable to register fd!", __func__);
err_no = 3;
break;

View File

@ -325,18 +325,29 @@ esp_err_t esp_vfs_register_with_id(const esp_vfs_t *vfs, void *ctx, esp_vfs_id_t
*/
esp_err_t esp_vfs_unregister(const char* base_path);
/**
* Unregister a virtual filesystem with the given index
*
* @param vfs_id The VFS ID returned by esp_vfs_register_with_id
* @return ESP_OK if successful, ESP_ERR_INVALID_STATE if VFS for the given index
* hasn't been registered
*/
esp_err_t esp_vfs_unregister_with_id(esp_vfs_id_t vfs_id);
/**
* Special function for registering another file descriptor for a VFS registered
* by esp_vfs_register_with_id.
*
* @param vfs_id VFS identificator returned by esp_vfs_register_with_id.
* @param local_fd The fd in the local vfs. Passing -1 will set the local fd as the (*fd) value.
* @param permanenent Whether the fd should be treated as permannet (not removed after close())
* @param fd The registered file descriptor will be written to this address.
*
* @return ESP_OK if the registration is successful,
* ESP_ERR_NO_MEM if too many file descriptors are registered,
* ESP_ERR_INVALID_ARG if the arguments are incorrect.
*/
esp_err_t esp_vfs_register_fd(esp_vfs_id_t vfs_id, int *fd);
esp_err_t esp_vfs_register_fd(esp_vfs_id_t vfs_id, int local_fd, bool permanent, int *fd);
/**
* Special function for unregistering a file descriptor belonging to a VFS

View File

@ -20,19 +20,22 @@
#include "esp_err.h"
#define EFD_SUPPORT_ISR (1 << 4)
#define EVENT_VFS_PREFIX "/dev/event"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
size_t eventfd_max_num;
} esp_vfs_eventfd_config_t;
/**
* @brief Registers the event vfs.
*
* @return ESP_OK if successful, ESP_ERR_NO_MEM if too many VFSes are
* registered.
*/
esp_err_t esp_vfs_eventfd_register(void);
esp_err_t esp_vfs_eventfd_register(const esp_vfs_eventfd_config_t *config);
/**
* @brief Unregisters the event vfs.

View File

@ -19,12 +19,15 @@
#include "driver/timer.h"
#include "esp_vfs.h"
#include "sys/_stdint.h"
#include "freertos/FreeRTOS.h"
#include "unity.h"
TEST_CASE("Test eventfd create and close", "[vfs][eventfd]")
TEST_CASE("eventfd create and close", "[vfs][eventfd]")
{
TEST_ESP_OK(esp_vfs_eventfd_register());
esp_vfs_eventfd_config_t config = {
.eventfd_max_num = 5,
};
TEST_ESP_OK(esp_vfs_eventfd_register(&config));
int fd = eventfd(0, 0);
TEST_ASSERT_GREATER_OR_EQUAL(0, fd);
TEST_ASSERT_EQUAL(0, close(fd));
@ -35,9 +38,13 @@ TEST_CASE("Test eventfd create and close", "[vfs][eventfd]")
TEST_ESP_OK(esp_vfs_eventfd_unregister());
}
TEST_CASE("Test eventfd reject unknown flags", "[vfs][eventfd]")
TEST_CASE("eventfd reject unknown flags", "[vfs][eventfd]")
{
TEST_ESP_OK(esp_vfs_eventfd_register());
esp_vfs_eventfd_config_t config = {
.eventfd_max_num = 5,
};
TEST_ESP_OK(esp_vfs_eventfd_register(&config));
int fd = eventfd(0, 1);
TEST_ASSERT_LESS_THAN(0, fd);
TEST_ASSERT_EQUAL(EINVAL, errno);
@ -48,9 +55,13 @@ TEST_CASE("Test eventfd reject unknown flags", "[vfs][eventfd]")
TEST_ESP_OK(esp_vfs_eventfd_unregister());
}
TEST_CASE("Test eventfd read", "[vfs][eventfd]")
TEST_CASE("eventfd read", "[vfs][eventfd]")
{
TEST_ESP_OK(esp_vfs_eventfd_register());
esp_vfs_eventfd_config_t config = {
.eventfd_max_num = 5,
};
TEST_ESP_OK(esp_vfs_eventfd_register(&config));
unsigned int initval = 123;
int fd = eventfd(initval, 0);
TEST_ASSERT_GREATER_OR_EQUAL(0, fd);
@ -64,9 +75,13 @@ TEST_CASE("Test eventfd read", "[vfs][eventfd]")
TEST_ESP_OK(esp_vfs_eventfd_unregister());
}
TEST_CASE("Test eventfd read invalid size", "[vfs][eventfd]")
TEST_CASE("eventfd read invalid size", "[vfs][eventfd]")
{
TEST_ESP_OK(esp_vfs_eventfd_register());
esp_vfs_eventfd_config_t config = {
.eventfd_max_num = 5,
};
TEST_ESP_OK(esp_vfs_eventfd_register(&config));
int fd = eventfd(0, 0);
TEST_ASSERT_GREATER_OR_EQUAL(0, fd);
@ -77,9 +92,13 @@ TEST_CASE("Test eventfd read invalid size", "[vfs][eventfd]")
TEST_ESP_OK(esp_vfs_eventfd_unregister());
}
TEST_CASE("Test eventfd write invalid size", "[vfs][eventfd]")
TEST_CASE("eventfd write invalid size", "[vfs][eventfd]")
{
TEST_ESP_OK(esp_vfs_eventfd_register());
esp_vfs_eventfd_config_t config = {
.eventfd_max_num = 5,
};
TEST_ESP_OK(esp_vfs_eventfd_register(&config));
int fd = eventfd(0, 0);
TEST_ASSERT_GREATER_OR_EQUAL(0, fd);
@ -90,9 +109,13 @@ TEST_CASE("Test eventfd write invalid size", "[vfs][eventfd]")
TEST_ESP_OK(esp_vfs_eventfd_unregister());
}
TEST_CASE("Test eventfd write then read", "[vfs][eventfd]")
TEST_CASE("eventfd write then read", "[vfs][eventfd]")
{
TEST_ESP_OK(esp_vfs_eventfd_register());
esp_vfs_eventfd_config_t config = {
.eventfd_max_num = 5,
};
TEST_ESP_OK(esp_vfs_eventfd_register(&config));
int fd = eventfd(0, 0);
TEST_ASSERT_GREATER_OR_EQUAL(0, fd);
@ -110,9 +133,13 @@ TEST_CASE("Test eventfd write then read", "[vfs][eventfd]")
TEST_ESP_OK(esp_vfs_eventfd_unregister());
}
TEST_CASE("Test eventfd instant select", "[vfs][eventfd]")
TEST_CASE("eventfd instant select", "[vfs][eventfd]")
{
TEST_ESP_OK(esp_vfs_eventfd_register());
esp_vfs_eventfd_config_t config = {
.eventfd_max_num = 5,
};
TEST_ESP_OK(esp_vfs_eventfd_register(&config));
int fd = eventfd(0, 0);
TEST_ASSERT_GREATER_OR_EQUAL(0, fd);
@ -126,12 +153,12 @@ TEST_CASE("Test eventfd instant select", "[vfs][eventfd]")
FD_ZERO(&write_fds);
FD_ZERO(&error_fds);
FD_SET(fd, &read_fds);
printf("fd %d\n", fd);
int ret = select(fd + 1, &read_fds, &write_fds, &error_fds, &zero_time);
TEST_ASSERT_EQUAL(0, ret);
TEST_ASSERT(!FD_ISSET(fd, &read_fds));
uint64_t val = 1;
printf("Write to fd\n");
TEST_ASSERT_EQUAL(sizeof(val), write(fd, &val, sizeof(val)));
FD_ZERO(&read_fds);
FD_ZERO(&write_fds);
@ -149,6 +176,7 @@ TEST_CASE("Test eventfd instant select", "[vfs][eventfd]")
ret = select(fd + 1, &read_fds, &write_fds, &error_fds, &zero_time);
TEST_ASSERT_EQUAL(0, ret);
TEST_ASSERT(!FD_ISSET(fd, &read_fds));
TEST_ASSERT_EQUAL(0, close(fd));
TEST_ESP_OK(esp_vfs_eventfd_unregister());
}
@ -161,47 +189,49 @@ static void signal_task(void *arg)
vTaskDelete(NULL);
}
TEST_CASE("Test eventfd signal from task", "[vfs][eventfd]")
TEST_CASE("eventfd signal from task", "[vfs][eventfd]")
{
TEST_ESP_OK(esp_vfs_eventfd_register());
int fd = eventfd(0, 0);
TEST_ASSERT_GREATER_OR_EQUAL(0, fd);
esp_vfs_eventfd_config_t config = {
.eventfd_max_num = 5,
};
TEST_ESP_OK(esp_vfs_eventfd_register(&config));
xTaskCreate(signal_task, "signal_task", 2048, &fd, 5, NULL);
int fd0 = eventfd(0, 0);
int fd1 = eventfd(0, 0);
int max_fd = fd1 > fd0 ? fd1 : fd0;
TEST_ASSERT_GREATER_OR_EQUAL(0, fd0);
TEST_ASSERT_GREATER_OR_EQUAL(0, fd1);
xTaskCreate(signal_task, "signal_task", 2048, &fd0, 5, NULL);
struct timeval wait_time;
struct timeval zero_time;
fd_set read_fds, write_fds, error_fds;
fd_set read_fds;
FD_ZERO(&read_fds);
FD_ZERO(&write_fds);
FD_ZERO(&error_fds);
FD_SET(fd, &read_fds);
FD_SET(fd0, &read_fds);
FD_SET(fd1, &read_fds);
wait_time.tv_sec = 2;
wait_time.tv_usec = 0;
zero_time.tv_sec = 0;
zero_time.tv_usec = 0;
FD_SET(fd, &read_fds);
int ret = select(fd + 1, &read_fds, &write_fds, &error_fds, &wait_time);
int ret = select(max_fd + 1, &read_fds, NULL, NULL, &wait_time);
printf("Frist select returned\n");
TEST_ASSERT_EQUAL(1, ret);
TEST_ASSERT(FD_ISSET(fd, &read_fds));
TEST_ASSERT(FD_ISSET(fd0, &read_fds));
uint64_t val = 1;
TEST_ASSERT_EQUAL(sizeof(val), write(fd1, &val, sizeof(val)));
FD_ZERO(&read_fds);
FD_ZERO(&write_fds);
FD_ZERO(&error_fds);
FD_SET(fd, &read_fds);
ret = select(fd + 1, &read_fds, &write_fds, &error_fds, &zero_time);
TEST_ASSERT_EQUAL(1, ret);
TEST_ASSERT(FD_ISSET(fd, &read_fds));
FD_SET(fd0, &read_fds);
FD_SET(fd1, &read_fds);
ret = select(max_fd + 1, &read_fds, NULL, NULL, &zero_time);
TEST_ASSERT_EQUAL(2, ret);
TEST_ASSERT(FD_ISSET(fd0, &read_fds));
TEST_ASSERT(FD_ISSET(fd1, &read_fds));
uint64_t val;
TEST_ASSERT_EQUAL(sizeof(val), read(fd, &val, sizeof(val)));
FD_ZERO(&read_fds);
FD_ZERO(&write_fds);
FD_ZERO(&error_fds);
FD_SET(fd, &read_fds);
ret = select(fd + 1, &read_fds, &write_fds, &error_fds, &zero_time);
TEST_ASSERT_EQUAL(0, ret);
TEST_ASSERT(!FD_ISSET(fd, &read_fds));
TEST_ASSERT_EQUAL(0, close(fd0));
TEST_ASSERT_EQUAL(0, close(fd1));
TEST_ESP_OK(esp_vfs_eventfd_unregister());
}
@ -214,20 +244,24 @@ static void IRAM_ATTR eventfd_select_test_isr(void *arg)
assert(ret == sizeof(val));
}
TEST_CASE("Test eventfd signal from ISR", "[vfs][eventfd]")
TEST_CASE("eventfd signal from ISR", "[vfs][eventfd]")
{
TEST_ESP_OK(esp_vfs_eventfd_register());
esp_vfs_eventfd_config_t config = {
.eventfd_max_num = 5,
};
TEST_ESP_OK(esp_vfs_eventfd_register(&config));
int fd = eventfd(0, EFD_SUPPORT_ISR);
TEST_ASSERT_GREATER_OR_EQUAL(0, fd);
timer_config_t config = {
timer_config_t timer_config = {
.divider = 16,
.counter_dir = TIMER_COUNT_UP,
.counter_en = TIMER_PAUSE,
.alarm_en = TIMER_ALARM_EN,
.auto_reload = false,
};
TEST_ESP_OK(timer_init(TIMER_GROUP_0, TIMER_0, &config));
TEST_ESP_OK(timer_init(TIMER_GROUP_0, TIMER_0, &timer_config));
TEST_ESP_OK(timer_set_counter_value(TIMER_GROUP_0, TIMER_0, 0x00000000ULL));
@ -251,5 +285,99 @@ TEST_CASE("Test eventfd signal from ISR", "[vfs][eventfd]")
TEST_ASSERT_EQUAL(1, ret);
TEST_ASSERT(FD_ISSET(fd, &read_fds));
timer_deinit(TIMER_GROUP_0, TIMER_0);
TEST_ASSERT_EQUAL(0, close(fd));
TEST_ESP_OK(esp_vfs_eventfd_unregister());
}
static void close_task(void *arg)
{
int fd = *((int *)arg);
vTaskDelay(pdMS_TO_TICKS(1000));
TEST_ASSERT_EQUAL(0, close(fd));
vTaskDelete(NULL);
}
TEST_CASE("eventfd select closed fd", "[vfs][eventfd]")
{
esp_vfs_eventfd_config_t config = {
.eventfd_max_num = 5,
};
TEST_ESP_OK(esp_vfs_eventfd_register(&config));
int fd = eventfd(0, 0);
TEST_ASSERT_GREATER_OR_EQUAL(0, fd);
xTaskCreate(close_task, "close_task", 2048, &fd, 5, NULL);
struct timeval wait_time;
fd_set read_fds, write_fds, error_fds;
FD_ZERO(&read_fds);
FD_ZERO(&write_fds);
FD_ZERO(&error_fds);
FD_SET(fd, &read_fds);
FD_SET(fd, &error_fds);
wait_time.tv_sec = 2;
wait_time.tv_usec = 0;
int ret = select(fd + 1, &read_fds, &write_fds, &error_fds, &wait_time);
TEST_ASSERT_EQUAL(1, ret);
TEST_ASSERT(FD_ISSET(fd, &error_fds));
TEST_ASSERT_EQUAL(0, close(fd));
TEST_ESP_OK(esp_vfs_eventfd_unregister());
}
typedef struct {
xQueueHandle queue;
int fd;
} select_task_args_t;
static void select_task(void *arg)
{
select_task_args_t *select_arg = (select_task_args_t *)arg;
int fd = select_arg->fd;
struct timeval wait_time;
fd_set read_fds;
FD_ZERO(&read_fds);
FD_SET(fd, &read_fds);
wait_time.tv_sec = 2;
wait_time.tv_usec = 0;
int ret = select(fd + 1, &read_fds, NULL, NULL, &wait_time);
assert(ret == 1);
xQueueSend(select_arg->queue, select_arg, 0);
vTaskDelete(NULL);
}
TEST_CASE("eventfd multiple selects", "[vfs][eventfd]")
{
esp_vfs_eventfd_config_t config = {
.eventfd_max_num = 5,
};
TEST_ESP_OK(esp_vfs_eventfd_register(&config));
int fd = eventfd(0, 0);
TEST_ASSERT_GREATER_OR_EQUAL(0, fd);
select_task_args_t args = {
.queue = xQueueCreate(10, sizeof(select_task_args_t)),
.fd = fd,
};
select_task_args_t ret_args;
xTaskCreate(select_task, "select_task0", 2048, &args, 5, NULL);
xTaskCreate(select_task, "select_task1", 2048, &args, 5, NULL);
uint64_t val = 1;
TEST_ASSERT_EQUAL(sizeof(val), write(fd, &val, sizeof(val)));
vTaskDelay(pdMS_TO_TICKS(100));
TEST_ASSERT(xQueueReceive(args.queue, &ret_args, 0));
TEST_ASSERT_EQUAL(ret_args.fd, fd);
TEST_ASSERT(xQueueReceive(args.queue, &ret_args, 0));
TEST_ASSERT_EQUAL(ret_args.fd, fd);
vQueueDelete(args.queue);
TEST_ASSERT_EQUAL(0, close(fd));
TEST_ESP_OK(esp_vfs_eventfd_unregister());
}

View File

@ -114,7 +114,10 @@ static void worker_task(void *arg)
TEST_CASE("Test eventfd triggered correctly", "[vfs][eventfd]")
{
xTaskCreate(worker_task, "worker_task", 1024, NULL, 5, NULL);
TEST_ESP_OK(esp_vfs_eventfd_register());
esp_vfs_eventfd_config_t config = {
.eventfd_max_num = 5,
};
TEST_ESP_OK(esp_vfs_eventfd_register(&config));
s_timer_fd = eventfd(0, EFD_SUPPORT_ISR);
s_progress_fd = eventfd(0, 0);
int maxFd = s_progress_fd > s_timer_fd ? s_progress_fd : s_timer_fd;

View File

@ -172,6 +172,27 @@ esp_err_t esp_vfs_register_with_id(const esp_vfs_t *vfs, void *ctx, esp_vfs_id_t
return esp_vfs_register_common("", LEN_PATH_PREFIX_IGNORED, vfs, ctx, vfs_id);
}
esp_err_t esp_vfs_unregister_with_id(esp_vfs_id_t vfs_id)
{
if (vfs_id < 0 || vfs_id >= MAX_FDS || s_vfs[vfs_id] == NULL) {
return ESP_ERR_INVALID_ARG;
}
vfs_entry_t* vfs = s_vfs[vfs_id];
free(vfs);
s_vfs[vfs_id] = NULL;
_lock_acquire(&s_fd_table_lock);
// Delete all references from the FD lookup-table
for (int j = 0; j < VFS_MAX_COUNT; ++j) {
if (s_fd_table[j].vfs_index == vfs_id) {
s_fd_table[j] = FD_TABLE_ENTRY_UNUSED;
}
}
_lock_release(&s_fd_table_lock);
return ESP_OK;
}
esp_err_t esp_vfs_unregister(const char* base_path)
{
const size_t base_path_len = strlen(base_path);
@ -182,25 +203,13 @@ esp_err_t esp_vfs_unregister(const char* base_path)
}
if (base_path_len == vfs->path_prefix_len &&
memcmp(base_path, vfs->path_prefix, vfs->path_prefix_len) == 0) {
free(vfs);
s_vfs[i] = NULL;
_lock_acquire(&s_fd_table_lock);
// Delete all references from the FD lookup-table
for (int j = 0; j < MAX_FDS; ++j) {
if (s_fd_table[j].vfs_index == i) {
s_fd_table[j] = FD_TABLE_ENTRY_UNUSED;
}
}
_lock_release(&s_fd_table_lock);
return ESP_OK;
return esp_vfs_unregister_with_id(i);
}
}
return ESP_ERR_INVALID_STATE;
}
esp_err_t esp_vfs_register_fd(esp_vfs_id_t vfs_id, int *fd)
esp_err_t esp_vfs_register_fd(esp_vfs_id_t vfs_id, int local_fd, bool permanent, int *fd)
{
if (vfs_id < 0 || vfs_id >= s_vfs_count || fd == NULL) {
ESP_LOGD(TAG, "Invalid arguments for esp_vfs_register_fd(%d, 0x%x)", vfs_id, (int) fd);
@ -211,9 +220,13 @@ esp_err_t esp_vfs_register_fd(esp_vfs_id_t vfs_id, int *fd)
_lock_acquire(&s_fd_table_lock);
for (int i = 0; i < MAX_FDS; ++i) {
if (s_fd_table[i].vfs_index == -1) {
s_fd_table[i].permanent = true;
s_fd_table[i].permanent = permanent;
s_fd_table[i].vfs_index = vfs_id;
s_fd_table[i].local_fd = i;
if (local_fd >= 0) {
s_fd_table[i].local_fd = local_fd;
} else {
s_fd_table[i].local_fd = i;
}
*fd = i;
ret = ESP_OK;
break;
@ -806,21 +819,23 @@ static int set_global_fd_sets(const fds_triple_t *vfs_fds_triple, int size, fd_s
const fds_triple_t *item = &vfs_fds_triple[i];
if (item->isset) {
for (int fd = 0; fd < MAX_FDS; ++fd) {
const int local_fd = s_fd_table[fd].local_fd; // single read -> no locking is required
if (readfds && esp_vfs_safe_fd_isset(local_fd, &item->readfds)) {
ESP_LOGD(TAG, "FD %d in readfds was set from VFS ID %d", fd, i);
FD_SET(fd, readfds);
++ret;
}
if (writefds && esp_vfs_safe_fd_isset(local_fd, &item->writefds)) {
ESP_LOGD(TAG, "FD %d in writefds was set from VFS ID %d", fd, i);
FD_SET(fd, writefds);
++ret;
}
if (errorfds && esp_vfs_safe_fd_isset(local_fd, &item->errorfds)) {
ESP_LOGD(TAG, "FD %d in errorfds was set from VFS ID %d", fd, i);
FD_SET(fd, errorfds);
++ret;
if (s_fd_table[fd].vfs_index == i) {
const int local_fd = s_fd_table[fd].local_fd; // single read -> no locking is required
if (readfds && esp_vfs_safe_fd_isset(local_fd, &item->readfds)) {
ESP_LOGD(TAG, "FD %d in readfds was set from VFS ID %d", fd, i);
FD_SET(fd, readfds);
++ret;
}
if (writefds && esp_vfs_safe_fd_isset(local_fd, &item->writefds)) {
ESP_LOGD(TAG, "FD %d in writefds was set from VFS ID %d", fd, i);
FD_SET(fd, writefds);
++ret;
}
if (errorfds && esp_vfs_safe_fd_isset(local_fd, &item->errorfds)) {
ESP_LOGD(TAG, "FD %d in errorfds was set from VFS ID %d", fd, i);
FD_SET(fd, errorfds);
++ret;
}
}
}
}
@ -891,6 +906,7 @@ int esp_vfs_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds
}
if (is_socket_fd) {
assert(false);
if (!socket_select) {
// no socket_select found yet so take a look
if (esp_vfs_safe_fd_isset(fd, readfds) ||

View File

@ -18,33 +18,66 @@
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/lock.h>
#include <sys/select.h>
#include <sys/types.h>
#include "esp_err.h"
#include "esp_log.h"
#include "esp_vfs.h"
#include "freertos/FreeRTOS.h"
#include "freertos/portmacro.h"
#include "soc/spinlock.h"
#define FD_INVALID -1
#define NUM_EVENT_FDS 5
#define FD_PENDING_SELECT -2
typedef struct event_select_args_t {
int fd;
fd_set *read_fds;
fd_set *error_fds;
esp_vfs_select_sem_t signal_sem;
struct event_select_args_t *prev_in_fd;
struct event_select_args_t *next_in_fd;
struct event_select_args_t *next_in_args;
} event_select_args_t;
typedef struct {
int fd;
bool support_isr;
volatile bool is_set;
volatile uint64_t value;
fd_set *read_fds;
fd_set *write_fds;
fd_set *error_fds;
esp_vfs_select_sem_t signal_sem;
event_select_args_t *select_args;
_lock_t lock;
spinlock_t data_spin_lock; // only for event fds that support ISR.
} Event;
} event_context_t;
static Event s_events[NUM_EVENT_FDS];
esp_vfs_id_t s_eventfd_vfs_id = -1;
static size_t s_event_size;
static event_context_t *s_events;
static void trigger_select_for_event(event_context_t *event)
{
event_select_args_t *select_args = event->select_args;
while (select_args != NULL) {
esp_vfs_select_triggered(select_args->signal_sem);
select_args = select_args->next_in_fd;
}
}
static void trigger_select_for_event_isr(event_context_t *event, BaseType_t *task_woken)
{
event_select_args_t *select_args = event->select_args;
while (select_args != NULL) {
BaseType_t local_woken;
esp_vfs_select_triggered_isr(select_args->signal_sem, &local_woken);
*task_woken = (local_woken || *task_woken);
select_args = select_args->next_in_fd;
}
}
static esp_err_t event_start_select(int nfds,
fd_set *readfds,
@ -55,42 +88,61 @@ static esp_err_t event_start_select(int nfds,
{
esp_err_t error = ESP_OK;
bool should_trigger = false;
nfds = nfds < s_event_size ? nfds : (int)s_event_size;
event_select_args_t *select_args_list = NULL;
for (size_t i = 0; i < NUM_EVENT_FDS; i++) {
// FIXME: end_select_args should be a list to all select args
for (int i = 0; i < nfds; i++) {
_lock_acquire_recursive(&s_events[i].lock);
int fd = s_events[i].fd;
if (fd != FD_INVALID && fd < nfds) {
if (s_events[i].fd == i) {
if (s_events[i].support_isr) {
portENTER_CRITICAL(&s_events[i].data_spin_lock);
}
s_events[i].signal_sem = signal_sem;
s_events[i].error_fds = exceptfds;
// event fds shouldn't report error
FD_CLR(fd, exceptfds);
event_select_args_t *event_select_args = (event_select_args_t *)malloc(sizeof(event_select_args_t));
event_select_args->fd = i;
event_select_args->signal_sem = signal_sem;
if (FD_ISSET(i, exceptfds)) {
FD_CLR(i, exceptfds);
event_select_args->error_fds = exceptfds;
} else {
event_select_args->error_fds = NULL;
}
FD_CLR(i, exceptfds);
// event fds are always writable
if (FD_ISSET(fd, writefds)) {
s_events[i].write_fds = writefds;
if (FD_ISSET(i, writefds)) {
should_trigger = true;
}
if (FD_ISSET(fd, readfds)) {
s_events[i].read_fds = readfds;
if (FD_ISSET(i, readfds)) {
event_select_args->read_fds = readfds;
if (s_events[i].is_set) {
should_trigger = true;
} else {
FD_CLR(fd, readfds);
FD_CLR(i, readfds);
}
} else {
event_select_args->read_fds = NULL;
}
event_select_args->prev_in_fd = NULL;
event_select_args->next_in_fd = s_events[i].select_args;
if (s_events[i].select_args) {
s_events[i].select_args->prev_in_fd = event_select_args;
}
event_select_args->next_in_args = select_args_list;
select_args_list = event_select_args;
s_events[i].select_args = event_select_args;
if (s_events[i].support_isr) {
portEXIT_CRITICAL(&s_events[i].data_spin_lock);
}
}
_lock_release_recursive(&s_events[i].lock);
}
*end_select_args = select_args_list;
if (should_trigger) {
esp_vfs_select_triggered(signal_sem);
}
@ -100,61 +152,70 @@ static esp_err_t event_start_select(int nfds,
static esp_err_t event_end_select(void *end_select_args)
{
for (size_t i = 0; i < NUM_EVENT_FDS; i++) {
_lock_acquire_recursive(&s_events[i].lock);
if (s_events[i].support_isr) {
portENTER_CRITICAL(&s_events[i].data_spin_lock);
}
memset(&s_events[i].signal_sem, 0, sizeof(s_events[i].signal_sem));
if (s_events[i].read_fds && s_events[i].is_set) {
FD_SET(s_events[i].fd, s_events[i].read_fds);
s_events[i].read_fds = NULL;
}
if (s_events[i].write_fds) {
FD_SET(s_events[i].fd, s_events[i].write_fds);
s_events[i].write_fds = NULL;
event_select_args_t *select_args = (event_select_args_t *)end_select_args;
while (select_args != NULL) {
event_context_t *event = &s_events[select_args->fd];
_lock_acquire_recursive(&event->lock);
if (event->support_isr) {
portENTER_CRITICAL(&event->data_spin_lock);
}
if (s_events[i].support_isr) {
portEXIT_CRITICAL(&s_events[i].data_spin_lock);
if (event->fd != select_args->fd) { // already closed
if (select_args->error_fds) {
FD_SET(select_args->fd, select_args->error_fds);
}
} else {
if (select_args->read_fds && event->is_set) {
FD_SET(select_args->fd, select_args->read_fds);
}
}
_lock_release_recursive(&s_events[i].lock);
event_select_args_t *prev_in_fd = select_args->prev_in_fd;
event_select_args_t *next_in_fd = select_args->next_in_fd;
event_select_args_t *next_in_args = select_args->next_in_args;
if (prev_in_fd != NULL) {
prev_in_fd->next_in_fd = next_in_fd;
} else {
event->select_args = next_in_fd;
}
if (next_in_fd != NULL) {
next_in_fd->prev_in_fd = prev_in_fd;
}
if (prev_in_fd == NULL && next_in_fd == NULL) { // The last pending select
if (event->fd == FD_PENDING_SELECT) {
event->fd = FD_INVALID;
}
}
if (event->support_isr) {
portEXIT_CRITICAL(&event->data_spin_lock);
}
_lock_release_recursive(&event->lock);
free(select_args);
select_args = next_in_args;
}
return ESP_OK;
}
static int event_open(const char *path, int flags, int mode)
{
(void)flags;
(void)mode;
if (path == NULL || path[0] != '/') {
return -1;
}
char *endPath;
int fd = strtol(path + 1, &endPath, 10);
if (endPath == NULL || *endPath != '\0' || fd >= NUM_EVENT_FDS) {
return -1;
}
return fd;
}
static ssize_t signal_event_fd_from_isr(int fd, const void *data, size_t size)
{
BaseType_t task_woken = pdFALSE;
const uint64_t *val = (const uint64_t *)data;
ssize_t ret = size;
portENTER_CRITICAL_ISR(&s_events[fd].data_spin_lock);
s_events[fd].is_set = true;
s_events[fd].value += *val;
if (s_events[fd].signal_sem.sem != NULL) {
esp_vfs_select_triggered_isr(s_events[fd].signal_sem, &task_woken);
if (s_events[fd].fd == fd) {
s_events[fd].is_set = true;
s_events[fd].value += *val;
trigger_select_for_event_isr(&s_events[fd], &task_woken);
} else {
errno = EBADF;
ret = -1;
}
portEXIT_CRITICAL_ISR(&s_events[fd].data_spin_lock);
@ -162,14 +223,14 @@ static ssize_t signal_event_fd_from_isr(int fd, const void *data, size_t size)
if (task_woken) {
portYIELD_FROM_ISR();
}
return size;
return ret;
}
static ssize_t event_write(int fd, const void *data, size_t size)
{
ssize_t ret = -1;
if (fd >= NUM_EVENT_FDS || data == NULL || size != sizeof(uint64_t)) {
if (fd >= s_event_size || data == NULL || size != sizeof(uint64_t)) {
errno = EINVAL;
return ret;
}
@ -182,21 +243,25 @@ static ssize_t event_write(int fd, const void *data, size_t size)
ret = signal_event_fd_from_isr(fd, data, size);
} else {
const uint64_t *val = (const uint64_t *)data;
_lock_acquire_recursive(&s_events[fd].lock);
_lock_acquire_recursive(&s_events[fd].lock);
if (s_events[fd].support_isr) {
portENTER_CRITICAL(&s_events[fd].data_spin_lock);
}
s_events[fd].is_set = true;
s_events[fd].value += *val;
ret = size;
if (s_events[fd].signal_sem.sem != NULL) {
esp_vfs_select_triggered(s_events[fd].signal_sem);
}
if (s_events[fd].support_isr) {
portEXIT_CRITICAL(&s_events[fd].data_spin_lock);
}
if (s_events[fd].fd == fd) {
s_events[fd].is_set = true;
s_events[fd].value += *val;
ret = size;
trigger_select_for_event(&s_events[fd]);
if (s_events[fd].support_isr) {
portEXIT_CRITICAL(&s_events[fd].data_spin_lock);
}
} else {
errno = EBADF;
ret = -1;
}
_lock_release_recursive(&s_events[fd].lock);
}
return ret;
@ -206,32 +271,33 @@ static ssize_t event_read(int fd, void *data, size_t size)
{
ssize_t ret = -1;
if (fd >= NUM_EVENT_FDS) {
errno = EINVAL;
return ret;
}
if (size != sizeof(uint64_t)) {
if (fd >= s_event_size || data == NULL || size != sizeof(uint64_t)) {
errno = EINVAL;
return ret;
}
uint64_t *val = (uint64_t *)data;
_lock_acquire_recursive(&s_events[fd].lock);
if (s_events[fd].support_isr) {
portENTER_CRITICAL(&s_events[fd].data_spin_lock);
}
if (s_events[fd].fd == fd) {
uint64_t *val = (uint64_t *)data;
if (s_events[fd].support_isr) {
portENTER_CRITICAL(&s_events[fd].data_spin_lock);
}
*val = s_events[fd].value;
s_events[fd].is_set = false;
ret = size;
s_events[fd].value = 0;
if (s_events[fd].support_isr) {
portEXIT_CRITICAL(&s_events[fd].data_spin_lock);
}
} else {
errno = EBADF;
ret = -1;
}
if (s_events[fd].support_isr) {
portEXIT_CRITICAL(&s_events[fd].data_spin_lock);
}
_lock_release_recursive(&s_events[fd].lock);
return ret;
}
@ -239,40 +305,55 @@ static int event_close(int fd)
{
int ret = -1;
if (fd >= NUM_EVENT_FDS) {
if (fd >= s_event_size) {
errno = EINVAL;
return ret;
}
_lock_acquire_recursive(&s_events[fd].lock);
if (s_events[fd].fd == fd) {
if (s_events[fd].support_isr) {
portENTER_CRITICAL(&s_events[fd].data_spin_lock);
}
s_events[fd].fd = FD_INVALID;
memset(&s_events[fd].signal_sem, 0, sizeof(s_events[fd].signal_sem));
if (s_events[fd].select_args == NULL) {
s_events[fd].fd = FD_INVALID;
} else {
s_events[fd].fd = FD_PENDING_SELECT;
trigger_select_for_event(&s_events[fd]);
}
s_events[fd].value = 0;
if (s_events[fd].support_isr) {
portEXIT_CRITICAL(&s_events[fd].data_spin_lock);
}
ret = 0;
} else {
errno = EBADF;
}
_lock_release_recursive(&s_events[fd].lock);
_lock_close_recursive(&s_events[fd].lock);
return ret;
}
esp_err_t esp_vfs_eventfd_register(void)
esp_err_t esp_vfs_eventfd_register(const esp_vfs_eventfd_config_t *config)
{
for (size_t i = 0; i < NUM_EVENT_FDS; i++) {
if (config == NULL || config->eventfd_max_num >= MAX_FDS) {
return ESP_ERR_INVALID_ARG;
}
if (s_eventfd_vfs_id != -1) {
return ESP_ERR_INVALID_STATE;
}
s_event_size = config->eventfd_max_num;
s_events = (event_context_t *)calloc(s_event_size, sizeof(event_context_t));
for (size_t i = 0; i < s_event_size; i++) {
_lock_init_recursive(&s_events[i].lock);
s_events[i].fd = FD_INVALID;
}
esp_vfs_t vfs = {
.flags = ESP_VFS_FLAG_DEFAULT,
.write = &event_write,
.open = &event_open,
.open = NULL,
.fstat = NULL,
.close = &event_close,
.read = &event_read,
@ -288,30 +369,52 @@ esp_err_t esp_vfs_eventfd_register(void)
.tcflush = NULL,
#endif // CONFIG_SUPPORT_TERMIOS
};
return esp_vfs_register(EVENT_VFS_PREFIX, &vfs, NULL);
return esp_vfs_register_with_id(&vfs, NULL, &s_eventfd_vfs_id);
}
esp_err_t esp_vfs_eventfd_unregister(void)
{
return esp_vfs_unregister(EVENT_VFS_PREFIX);
if (s_eventfd_vfs_id == -1) {
return ESP_ERR_INVALID_STATE;
}
esp_err_t error = esp_vfs_unregister_with_id(s_eventfd_vfs_id);
if (error == ESP_OK) {
s_eventfd_vfs_id = -1;
}
for (size_t i = 0; i < s_event_size; i++) {
_lock_close_recursive(&s_events[i].lock);
}
free(s_events);
return error;
}
int eventfd(unsigned int initval, int flags)
{
int fd = FD_INVALID;
int global_fd = FD_INVALID;
esp_err_t error = ESP_OK;
if ((flags & (~EFD_SUPPORT_ISR)) != 0) {
errno = EINVAL;
return FD_INVALID;
}
if (s_eventfd_vfs_id == -1) {
errno = EACCES;
return FD_INVALID;
}
for (size_t i = 0; i < NUM_EVENT_FDS; i++) {
bool support_isr = flags & EFD_SUPPORT_ISR;
bool has_allocated = false;
_lock_init_recursive(&s_events[i].lock);
for (size_t i = 0; i < s_event_size; i++) {
_lock_acquire_recursive(&s_events[i].lock);
if (s_events[i].fd == FD_INVALID) {
error = esp_vfs_register_fd(s_eventfd_vfs_id, i, /*permanent=*/false, &global_fd);
if (error != ESP_OK) {
_lock_release_recursive(&s_events[i].lock);
break;
}
bool support_isr = flags & EFD_SUPPORT_ISR;
fd = i;
s_events[i].fd = i;
s_events[i].support_isr = support_isr;
spinlock_initialize(&s_events[i].data_spin_lock);
@ -321,21 +424,30 @@ int eventfd(unsigned int initval, int flags)
}
s_events[i].is_set = false;
s_events[i].value = initval;
memset(&s_events[i].signal_sem, 0, sizeof(s_events[i].signal_sem));
s_events[i].select_args = NULL;
if (support_isr) {
portEXIT_CRITICAL(&s_events[i].data_spin_lock);
}
char fullpath[20];
snprintf(fullpath, sizeof(fullpath), EVENT_VFS_PREFIX "/%d", s_events[i].fd);
fd = open(fullpath, 0, 0);
has_allocated = true;
_lock_release_recursive(&s_events[i].lock);
break;
}
_lock_release_recursive(&s_events[i].lock);
if (has_allocated) {
return fd;
}
}
return FD_INVALID;
switch (error) {
case ESP_OK:
fd = global_fd;
break;
case ESP_ERR_NO_MEM:
errno = ENOMEM;
break;
case ESP_ERR_INVALID_ARG:
errno = EINVAL;
break;
default:
errno = EIO;
break;
}
return fd;
}