test_vfs_fd.c 9.0 KB

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