test_vfs_fd.c 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. /*
  2. * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. #include <stdio.h>
  7. #include <string.h>
  8. #include <unistd.h>
  9. #include <errno.h>
  10. #include <sys/fcntl.h>
  11. #include "freertos/FreeRTOS.h"
  12. #include "freertos/task.h"
  13. #include "freertos/semphr.h"
  14. #include "esp_vfs.h"
  15. #include "unity.h"
  16. #include "esp_log.h"
  17. #include "test_utils.h"
  18. #include "ccomp_timer.h"
  19. #define VFS_PREF1 "/vfs1"
  20. #define VFS_PREF2 "/vfs2"
  21. #define FILE1 "/file1"
  22. typedef struct {
  23. const char *path;
  24. int fd;
  25. } collision_test_vfs_param_t;
  26. static int collision_test_vfs_open(void* ctx, const char * path, int flags, int mode)
  27. {
  28. const collision_test_vfs_param_t *param = (collision_test_vfs_param_t *) ctx;
  29. if (strcmp(param->path, path) == 0) {
  30. return param->fd;
  31. }
  32. errno = ENOENT;
  33. return -1;
  34. }
  35. static int collision_test_vfs_close(void* ctx, int fd)
  36. {
  37. const collision_test_vfs_param_t *param = (collision_test_vfs_param_t *) ctx;
  38. if (fd == param->fd) {
  39. return 0;
  40. }
  41. errno = EBADF;
  42. return -1;
  43. }
  44. TEST_CASE("FDs from different VFSs don't collide", "[vfs]")
  45. {
  46. collision_test_vfs_param_t param = {
  47. .path = FILE1,
  48. .fd = 1,
  49. };
  50. esp_vfs_t desc = {
  51. .flags = ESP_VFS_FLAG_CONTEXT_PTR,
  52. .open_p = collision_test_vfs_open,
  53. .close_p = collision_test_vfs_close,
  54. };
  55. TEST_ESP_OK( esp_vfs_register(VFS_PREF1, &desc, &param) );
  56. TEST_ESP_OK( esp_vfs_register(VFS_PREF2, &desc, &param) );
  57. const int fd1 = open(VFS_PREF1 FILE1, 0, 0);
  58. const int fd2 = open(VFS_PREF2 FILE1, 0, 0);
  59. TEST_ASSERT_NOT_EQUAL(fd1, -1);
  60. TEST_ASSERT_NOT_EQUAL(fd2, -1);
  61. // Both VFS drivers return local FD 1 but the global FDs returned by
  62. // open() should not be the same
  63. TEST_ASSERT_NOT_EQUAL(fd1, fd2);
  64. TEST_ASSERT_NOT_EQUAL(close(fd1), -1);
  65. TEST_ASSERT_NOT_EQUAL(close(fd2), -1);
  66. TEST_ESP_OK( esp_vfs_unregister(VFS_PREF1) );
  67. TEST_ESP_OK( esp_vfs_unregister(VFS_PREF2) );
  68. }
  69. #define FILE2 "/file2"
  70. #define FILE3 "/file3"
  71. #define FILE4 "/file4"
  72. #define CONCURRENT_TEST_STACK_SIZE (2*1024)
  73. #define CONCURRENT_TEST_MAX_WAIT (1000 / portTICK_PERIOD_MS)
  74. typedef struct {
  75. const char *path;
  76. SemaphoreHandle_t done;
  77. } concurrent_test_task_param_t;
  78. typedef struct {
  79. const char *path;
  80. int local_fd;
  81. } concurrent_test_path_to_fd_t;
  82. static concurrent_test_path_to_fd_t concurrent_test_path_to_fd[] = {
  83. { .path = FILE1, .local_fd = 1 },
  84. { .path = FILE2, .local_fd = 2 },
  85. { .path = FILE3, .local_fd = 3 },
  86. { .path = FILE4, .local_fd = 4 },
  87. };
  88. static int concurrent_test_vfs_open(const char * path, int flags, int mode)
  89. {
  90. for (size_t i = 0; i < sizeof(concurrent_test_path_to_fd)/sizeof(concurrent_test_path_to_fd[0]); ++i) {
  91. if (strcmp(concurrent_test_path_to_fd[i].path, path) == 0) {
  92. // This behaves like UART: opening the same file gives always the
  93. // same local FD (even when opening at the same time multiple FDs)
  94. return concurrent_test_path_to_fd[i].local_fd;
  95. }
  96. }
  97. errno = ENOENT;
  98. return -1;
  99. }
  100. static int concurrent_test_vfs_close(int fd)
  101. {
  102. for (size_t i = 0; i < sizeof(concurrent_test_path_to_fd)/sizeof(concurrent_test_path_to_fd[0]); ++i) {
  103. if (concurrent_test_path_to_fd[i].local_fd == fd) {
  104. return 0;
  105. }
  106. }
  107. errno = EBADF;
  108. return -1;
  109. }
  110. static inline void test_delay_rand_ms(int ms)
  111. {
  112. vTaskDelay((rand() % ms) / portTICK_PERIOD_MS);
  113. }
  114. static void concurrent_task(void *param)
  115. {
  116. concurrent_test_task_param_t *task_param = (concurrent_test_task_param_t *) param;
  117. test_delay_rand_ms(10);
  118. for (int i = 0; i < 10; ++i) {
  119. const int global_fd = open(task_param->path, 0, 0);
  120. TEST_ASSERT_NOT_EQUAL(global_fd, -1);
  121. test_delay_rand_ms(10);
  122. TEST_ASSERT_NOT_EQUAL(close(global_fd), -1);
  123. test_delay_rand_ms(10);
  124. }
  125. xSemaphoreGive(task_param->done);
  126. vTaskDelete(NULL);
  127. }
  128. TEST_CASE("VFS can handle concurrent open/close requests", "[vfs]")
  129. {
  130. esp_vfs_t desc = {
  131. .flags = ESP_VFS_FLAG_DEFAULT,
  132. .open = concurrent_test_vfs_open,
  133. .close = concurrent_test_vfs_close,
  134. };
  135. TEST_ESP_OK( esp_vfs_register(VFS_PREF1, &desc, NULL) );
  136. concurrent_test_task_param_t param1 = { .path = VFS_PREF1 FILE1, .done = xSemaphoreCreateBinary() };
  137. concurrent_test_task_param_t param2 = { .path = VFS_PREF1 FILE1, .done = xSemaphoreCreateBinary() };
  138. concurrent_test_task_param_t param3 = { .path = VFS_PREF1 FILE2, .done = xSemaphoreCreateBinary() };
  139. concurrent_test_task_param_t param4 = { .path = VFS_PREF1 FILE2, .done = xSemaphoreCreateBinary() };
  140. concurrent_test_task_param_t param5 = { .path = VFS_PREF1 FILE3, .done = xSemaphoreCreateBinary() };
  141. concurrent_test_task_param_t param6 = { .path = VFS_PREF1 FILE3, .done = xSemaphoreCreateBinary() };
  142. concurrent_test_task_param_t param7 = { .path = VFS_PREF1 FILE4, .done = xSemaphoreCreateBinary() };
  143. concurrent_test_task_param_t param8 = { .path = VFS_PREF1 FILE4, .done = xSemaphoreCreateBinary() };
  144. TEST_ASSERT_NOT_NULL(param1.done);
  145. TEST_ASSERT_NOT_NULL(param2.done);
  146. TEST_ASSERT_NOT_NULL(param3.done);
  147. TEST_ASSERT_NOT_NULL(param4.done);
  148. TEST_ASSERT_NOT_NULL(param5.done);
  149. TEST_ASSERT_NOT_NULL(param6.done);
  150. TEST_ASSERT_NOT_NULL(param7.done);
  151. TEST_ASSERT_NOT_NULL(param8.done);
  152. const int cpuid0 = 0;
  153. const int cpuid1 = portNUM_PROCESSORS - 1;
  154. srand(time(NULL));
  155. xTaskCreatePinnedToCore(concurrent_task, "t1", CONCURRENT_TEST_STACK_SIZE, &param1, 3, NULL, cpuid0);
  156. xTaskCreatePinnedToCore(concurrent_task, "t2", CONCURRENT_TEST_STACK_SIZE, &param2, 3, NULL, cpuid1);
  157. xTaskCreatePinnedToCore(concurrent_task, "t3", CONCURRENT_TEST_STACK_SIZE, &param3, 3, NULL, cpuid0);
  158. xTaskCreatePinnedToCore(concurrent_task, "t4", CONCURRENT_TEST_STACK_SIZE, &param4, 3, NULL, cpuid1);
  159. xTaskCreatePinnedToCore(concurrent_task, "t5", CONCURRENT_TEST_STACK_SIZE, &param5, 3, NULL, cpuid0);
  160. xTaskCreatePinnedToCore(concurrent_task, "t6", CONCURRENT_TEST_STACK_SIZE, &param6, 3, NULL, cpuid1);
  161. xTaskCreatePinnedToCore(concurrent_task, "t7", CONCURRENT_TEST_STACK_SIZE, &param7, 3, NULL, cpuid0);
  162. xTaskCreatePinnedToCore(concurrent_task, "t8", CONCURRENT_TEST_STACK_SIZE, &param8, 3, NULL, cpuid1);
  163. TEST_ASSERT_EQUAL(xSemaphoreTake(param1.done, CONCURRENT_TEST_MAX_WAIT), pdTRUE);
  164. TEST_ASSERT_EQUAL(xSemaphoreTake(param2.done, CONCURRENT_TEST_MAX_WAIT), pdTRUE);
  165. TEST_ASSERT_EQUAL(xSemaphoreTake(param3.done, CONCURRENT_TEST_MAX_WAIT), pdTRUE);
  166. TEST_ASSERT_EQUAL(xSemaphoreTake(param4.done, CONCURRENT_TEST_MAX_WAIT), pdTRUE);
  167. TEST_ASSERT_EQUAL(xSemaphoreTake(param5.done, CONCURRENT_TEST_MAX_WAIT), pdTRUE);
  168. TEST_ASSERT_EQUAL(xSemaphoreTake(param6.done, CONCURRENT_TEST_MAX_WAIT), pdTRUE);
  169. TEST_ASSERT_EQUAL(xSemaphoreTake(param7.done, CONCURRENT_TEST_MAX_WAIT), pdTRUE);
  170. TEST_ASSERT_EQUAL(xSemaphoreTake(param8.done, CONCURRENT_TEST_MAX_WAIT), pdTRUE);
  171. vSemaphoreDelete(param1.done);
  172. vSemaphoreDelete(param2.done);
  173. vSemaphoreDelete(param3.done);
  174. vSemaphoreDelete(param4.done);
  175. vSemaphoreDelete(param5.done);
  176. vSemaphoreDelete(param6.done);
  177. vSemaphoreDelete(param7.done);
  178. vSemaphoreDelete(param8.done);
  179. TEST_ESP_OK( esp_vfs_unregister(VFS_PREF1) );
  180. }
  181. static int time_test_vfs_open(const char *path, int flags, int mode)
  182. {
  183. return 1;
  184. }
  185. static int time_test_vfs_close(int fd)
  186. {
  187. return 1;
  188. }
  189. static int time_test_vfs_write(int fd, const void *data, size_t size)
  190. {
  191. return size;
  192. }
  193. TEST_CASE("Open & write & close through VFS passes performance test", "[vfs]")
  194. {
  195. esp_vfs_t desc = {
  196. .flags = ESP_VFS_FLAG_DEFAULT,
  197. .open = time_test_vfs_open,
  198. .close = time_test_vfs_close,
  199. .write = time_test_vfs_write,
  200. };
  201. TEST_ESP_OK( esp_vfs_register(VFS_PREF1, &desc, NULL) );
  202. ccomp_timer_start();
  203. const int iter_count = 5000;
  204. for (int i = 0; i < iter_count; ++i) {
  205. const int fd = open(VFS_PREF1 FILE1, 0, 0);
  206. TEST_ASSERT_NOT_EQUAL(fd, -1);
  207. write(fd, "a", 1);
  208. TEST_ASSERT_NOT_EQUAL(close(fd), -1);
  209. }
  210. const int64_t time_diff_us = ccomp_timer_stop();
  211. const int ns_per_iter = (int) (time_diff_us * 1000 / iter_count);
  212. TEST_ESP_OK( esp_vfs_unregister(VFS_PREF1) );
  213. #ifdef CONFIG_SPIRAM
  214. TEST_PERFORMANCE_CCOMP_LESS_THAN(VFS_OPEN_WRITE_CLOSE_TIME_PSRAM, "%dns", ns_per_iter);
  215. #else
  216. TEST_PERFORMANCE_CCOMP_LESS_THAN(VFS_OPEN_WRITE_CLOSE_TIME, "%dns", ns_per_iter);
  217. #endif
  218. }
  219. static int vfs_overlap_test_open(const char * path, int flags, int mode)
  220. {
  221. return 0;
  222. }
  223. static int vfs_overlap_test_close(int fd)
  224. {
  225. return 0;
  226. }
  227. TEST_CASE("esp_vfs_register_fd_range checks for overlap", "[vfs]")
  228. {
  229. esp_vfs_t vfs1 = {
  230. .open = vfs_overlap_test_open,
  231. .close = vfs_overlap_test_close
  232. };
  233. TEST_ESP_OK(esp_vfs_register("/test", &vfs1, NULL));
  234. int fd = open("/test/1", 0, 0);
  235. TEST_ASSERT_NOT_EQUAL(-1, fd);
  236. esp_vfs_t vfs2 = { };
  237. esp_err_t err = esp_vfs_register_fd_range(&vfs2, NULL, fd, fd + 1);
  238. close(fd);
  239. TEST_ESP_OK(esp_vfs_unregister("/test"));
  240. TEST_ESP_ERR(ESP_ERR_INVALID_ARG, err);
  241. }