From 7b911e4641025c126a7327da2a9693da4c3328ba Mon Sep 17 00:00:00 2001 From: Jiacheng Guo Date: Mon, 8 Mar 2021 09:44:58 +0800 Subject: [PATCH] vfs: add support for mutiple selects in eventfd --- .../bluedroid/btc/profile/std/spp/btc_spp.c | 2 +- components/vfs/include/esp_vfs.h | 13 +- components/vfs/include/esp_vfs_eventfd.h | 7 +- components/vfs/test/test_vfs_eventfd.c | 222 +++++++++--- .../vfs/test/test_vfs_eventfd_integration.c | 5 +- components/vfs/vfs.c | 78 ++-- components/vfs/vfs_eventfd.c | 338 ++++++++++++------ 7 files changed, 469 insertions(+), 196 deletions(-) diff --git a/components/bt/host/bluedroid/btc/profile/std/spp/btc_spp.c b/components/bt/host/bluedroid/btc/profile/std/spp/btc_spp.c index a8fe25e352..1608892d01 100644 --- a/components/bt/host/bluedroid/btc/profile/std/spp/btc_spp.c +++ b/components/bt/host/bluedroid/btc/profile/std/spp/btc_spp.c @@ -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; diff --git a/components/vfs/include/esp_vfs.h b/components/vfs/include/esp_vfs.h index ddbabd975c..30689d6d89 100644 --- a/components/vfs/include/esp_vfs.h +++ b/components/vfs/include/esp_vfs.h @@ -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 diff --git a/components/vfs/include/esp_vfs_eventfd.h b/components/vfs/include/esp_vfs_eventfd.h index e8e12337b0..90c7b5939b 100644 --- a/components/vfs/include/esp_vfs_eventfd.h +++ b/components/vfs/include/esp_vfs_eventfd.h @@ -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. diff --git a/components/vfs/test/test_vfs_eventfd.c b/components/vfs/test/test_vfs_eventfd.c index 71a02eefec..5d7732b540 100644 --- a/components/vfs/test/test_vfs_eventfd.c +++ b/components/vfs/test/test_vfs_eventfd.c @@ -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()); +} + diff --git a/components/vfs/test/test_vfs_eventfd_integration.c b/components/vfs/test/test_vfs_eventfd_integration.c index 4e59822127..50e19dbafe 100644 --- a/components/vfs/test/test_vfs_eventfd_integration.c +++ b/components/vfs/test/test_vfs_eventfd_integration.c @@ -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; diff --git a/components/vfs/vfs.c b/components/vfs/vfs.c index 2e3d6ef3c8..6468232c2c 100644 --- a/components/vfs/vfs.c +++ b/components/vfs/vfs.c @@ -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) || diff --git a/components/vfs/vfs_eventfd.c b/components/vfs/vfs_eventfd.c index 3aadda17df..808c1ada88 100644 --- a/components/vfs/vfs_eventfd.c +++ b/components/vfs/vfs_eventfd.c @@ -18,33 +18,66 @@ #include #include #include +#include #include #include #include +#include #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; }