Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 | /* * Copyright (c) 2023 Husqvarna AB * * SPDX-License-Identifier: Apache-2.0 */ #include "test_fat.h" #ifdef CONFIG_FS_FATFS_REENTRANT #define REENTRANT_TEST_STACK_SIZE 500 #define SEMAPHORE_OP_SUCCESS 0 #define TEST_FILE2 FATFS_MNTP"/tfile2.txt" void tlock_mutex(void *p1, void *p2, void *p3); void tfile2_access(void *p1, void *p2, void *p3); K_THREAD_STACK_DEFINE(tlock_mutex_stack_area, REENTRANT_TEST_STACK_SIZE); K_THREAD_STACK_DEFINE(tfile2_access_stack_area, REENTRANT_TEST_STACK_SIZE); struct k_thread tlock_mutex_data; struct k_thread tfile2_access_data; struct k_sem mutex_unlocked_sem; struct k_sem run_non_thread_sem; static int test_reentrant_access(void) { int res; TC_PRINT("\nReentrant tests:\n"); zassert_ok(k_sem_init(&mutex_unlocked_sem, 0, 1), NULL); zassert_ok(k_sem_init(&run_non_thread_sem, 0, 1), NULL); /* Start mutex locking thread */ k_tid_t tid = k_thread_create(&tlock_mutex_data, tlock_mutex_stack_area, K_THREAD_STACK_SIZEOF(tlock_mutex_stack_area), tlock_mutex, NULL, NULL, NULL, K_PRIO_PREEMPT(0), 0, K_NO_WAIT); /* Make sure thread was able to lock mutex */ k_sem_take(&run_non_thread_sem, K_FOREVER); /* File open should wait here, as the fs is locked. Therefore, automatic switch back to * thread */ TC_PRINT("Open file\n"); res = fs_open(&filep, TEST_FILE, FS_O_CREATE | FS_O_RDWR); zassert_ok(res, "Err: File could not be opened [%d]\n", res); TC_PRINT("File opened\n"); /* Check if mutex thread really unlocked the mutexes */ zassert_equal(SEMAPHORE_OP_SUCCESS, k_sem_take(&mutex_unlocked_sem, K_NO_WAIT), "File open with locked mutex"); /* Cleanup */ res = fs_close(&filep); zassert_ok(res, "Error closing file [%d]\n", res); res = fs_unlink(TEST_FILE); zassert_ok(res, "Error deleting file [%d]\n", res); k_thread_join(tid, K_FOREVER); return res; } static int test_reentrant_parallel_file_access(void) { int res; TC_PRINT("\nParallel reentrant-safe file access test:\n"); TC_PRINT("Open file 1\n"); res = fs_open(&filep, TEST_FILE, FS_O_CREATE | FS_O_RDWR); zassert_ok(res, "Err: File 1 could not be opened [%d]\n", res); TC_PRINT("File 1 opened 1\n"); /* Start 2nd file acces thread */ k_tid_t tid = k_thread_create(&tfile2_access_data, tfile2_access_stack_area, K_THREAD_STACK_SIZEOF(tfile2_access_stack_area), tfile2_access, NULL, NULL, NULL, K_PRIO_PREEMPT(0), 0, K_NO_WAIT); /* Wait for thread to finish accessing file 2 */ k_thread_join(tid, K_FOREVER); /* Check existence of file 2 */ struct fs_file_t filep2; fs_file_t_init(&filep2); TC_PRINT("Check file 2 existence\n"); res = fs_open(&filep2, TEST_FILE2, FA_OPEN_EXISTING | FA_READ); zassert_ok(res, "Err: File 2 does not exist [%d]\n", res); /* Cleanup */ res = fs_close(&filep2); zassert_ok(res, "Error closing file 2 [%d]\n", res); res = fs_unlink(TEST_FILE2); zassert_ok(res, "Error deleting file 2 [%d]\n", res); res = fs_close(&filep); zassert_ok(res, "Error closing file 1 [%d]\n", res); res = fs_unlink(TEST_FILE); zassert_ok(res, "Error deleting file 1 [%d]\n", res); return res; } void release_dirty_mutex(void) { ff_mutex_give(fat_fs.ldrv); } int request_dirty_mutex(void) { return ff_mutex_take(fat_fs.ldrv); } void tlock_mutex(void *p1, void *p2, void *p3) { TC_PRINT("Mutex thread: Started, locking fs\n"); request_dirty_mutex(); TC_PRINT("Mutex thread: Lock acquired, yield to switch back to try to open file\n"); k_sem_give(&run_non_thread_sem); k_yield(); TC_PRINT("Mutex thread: Got back to thread, release mutex now and give semaphore to check " "if file opened\n"); k_sem_give(&mutex_unlocked_sem); release_dirty_mutex(); TC_PRINT("Mutex thread: Lock released, thread terminating\n"); } void tfile2_access(void *p1, void *p2, void *p3) { int res; ssize_t brw; struct fs_file_t filep2; TC_PRINT("File 2 access thread started\n"); /* Init fp for 2nd File for parallel access test */ fs_file_t_init(&filep2); /* open 2nd file */ TC_PRINT("Open file 2\n"); res = fs_open(&filep2, TEST_FILE2, FS_O_CREATE | FS_O_RDWR); zassert_ok(res, "Err: File 2 could not be opened [%d]\n", res); TC_PRINT("File 2 opened 2\n"); /* Verify fs_write() not locked */ brw = fs_write(&filep2, (char *)test_str, strlen(test_str)); if (brw < 0) { TC_PRINT("Failed writing to file [%zd]\n", brw); fs_close(&filep2); return; } if (brw < strlen(test_str)) { TC_PRINT("Unable to complete write. Volume full.\n"); TC_PRINT("Number of bytes written: [%zd]\n", brw); fs_close(&filep2); return; } /* Close file and switch back to test context*/ res = fs_close(&filep2); zassert_ok(res, "Error closing file [%d]\n", res); TC_PRINT("File 2 access thread successfully wrote to file 2\n"); } void test_fat_file_reentrant(void) { zassert_true(test_reentrant_access() == TC_PASS, NULL); zassert_true(test_reentrant_parallel_file_access() == TC_PASS, NULL); } #endif /* CONFIG_FS_FATFS_REENTRANT */ |