1 /*
2 * Copyright (c) 2019 Peter Bigot Consulting, LLC
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /* littlefs performance testing */
8
9 #include <string.h>
10 #include <stdlib.h>
11 #include <zephyr/kernel.h>
12 #include <zephyr/ztest.h>
13 #include "testfs_tests.h"
14 #include "testfs_lfs.h"
15 #include <lfs.h>
16
17 #include <zephyr/fs/littlefs.h>
18
19 #define HELLO "hello"
20 #define GOODBYE "goodbye"
21
write_read(const char * tag,struct fs_mount_t * mp,size_t buf_size,size_t nbuf)22 static int write_read(const char *tag,
23 struct fs_mount_t *mp,
24 size_t buf_size,
25 size_t nbuf)
26 {
27 const struct lfs_config *lcp = &((const struct fs_littlefs *)mp->fs_data)->cfg;
28 struct testfs_path path;
29 struct fs_statvfs vfs;
30 struct fs_dirent stat;
31 struct fs_file_t file;
32 size_t total = nbuf * buf_size;
33 uint32_t t0;
34 uint32_t t1;
35 uint8_t *buf;
36 int rc;
37 int rv = TC_FAIL;
38
39 fs_file_t_init(&file);
40 TC_PRINT("clearing %s for %s write/read test\n",
41 mp->mnt_point, tag);
42 if (testfs_lfs_wipe_partition(mp) != TC_PASS) {
43 return TC_FAIL;
44 }
45
46 rc = fs_mount(mp);
47 if (rc != 0) {
48 TC_PRINT("Mount %s failed: %d\n", mp->mnt_point, rc);
49 return TC_FAIL;
50 }
51
52 rc = fs_statvfs(mp->mnt_point, &vfs);
53 if (rc != 0) {
54 TC_PRINT("statvfs %s failed: %d\n", mp->mnt_point, rc);
55 goto out_mnt;
56 }
57
58 TC_PRINT("%s: bsize %lu ; frsize %lu ; blocks %lu ; bfree %lu\n",
59 mp->mnt_point,
60 vfs.f_bsize, vfs.f_frsize, vfs.f_blocks, vfs.f_bfree);
61 TC_PRINT("read_size %u ; prog_size %u ; cache_size %u ; lookahead_size %u\n",
62 lcp->read_size, lcp->prog_size, lcp->cache_size, lcp->lookahead_size);
63
64 testfs_path_init(&path, mp,
65 "data",
66 TESTFS_PATH_END);
67
68 buf = calloc(buf_size, sizeof(uint8_t));
69 if (buf == NULL) {
70 TC_PRINT("Failed to allocate %zu-byte buffer\n", buf_size);
71 goto out_mnt;
72 }
73
74 for (size_t i = 0; i < buf_size; ++i) {
75 buf[i] = i;
76 }
77
78 TC_PRINT("creating and writing %zu %zu-byte blocks\n",
79 nbuf, buf_size);
80
81 rc = fs_open(&file, path.path, FS_O_CREATE | FS_O_RDWR);
82 if (rc != 0) {
83 TC_PRINT("Failed to open %s for write: %d\n", path.path, rc);
84 goto out_buf;
85 }
86
87 t0 = k_uptime_get_32();
88 for (size_t i = 0; i < nbuf; ++i) {
89 rc = fs_write(&file, buf, buf_size);
90 if (buf_size != rc) {
91 TC_PRINT("Failed to write buf %zu: %d\n", i, rc);
92 goto out_file;
93 }
94 }
95 t1 = k_uptime_get_32();
96
97 if (t1 == t0) {
98 t1++;
99 }
100
101 (void)fs_close(&file);
102
103 rc = fs_stat(path.path, &stat);
104 if (rc != 0) {
105 TC_PRINT("Failed to stat %s: %d\n", path.path, rc);
106 goto out_buf;
107 }
108
109 if (stat.size != total) {
110 TC_PRINT("File size %zu not %zu\n", stat.size, total);
111 goto out_buf;
112 }
113
114 TC_PRINT("%s write %zu * %zu = %zu bytes in %u ms: "
115 "%u By/s, %u KiBy/s\n",
116 tag, nbuf, buf_size, total, (t1 - t0),
117 (uint32_t)(total * 1000U / (t1 - t0)),
118 (uint32_t)(total * 1000U / (t1 - t0) / 1024U));
119
120 rc = fs_open(&file, path.path, FS_O_CREATE | FS_O_RDWR);
121 if (rc != 0) {
122 TC_PRINT("Failed to open %s for write: %d\n", path.path, rc);
123 goto out_buf;
124 }
125
126 t0 = k_uptime_get_32();
127 for (size_t i = 0; i < nbuf; ++i) {
128 rc = fs_read(&file, buf, buf_size);
129 if (buf_size != rc) {
130 TC_PRINT("Failed to read buf %zu: %d\n", i, rc);
131 goto out_file;
132 }
133 }
134 t1 = k_uptime_get_32();
135
136 if (t1 == t0) {
137 t1++;
138 }
139
140 TC_PRINT("%s read %zu * %zu = %zu bytes in %u ms: "
141 "%u By/s, %u KiBy/s\n",
142 tag, nbuf, buf_size, total, (t1 - t0),
143 (uint32_t)(total * 1000U / (t1 - t0)),
144 (uint32_t)(total * 1000U / (t1 - t0) / 1024U));
145
146 rv = TC_PASS;
147
148 out_file:
149 (void)fs_close(&file);
150
151 out_buf:
152 free(buf);
153
154 out_mnt:
155 (void)fs_unmount(mp);
156
157 return rv;
158 }
159
custom_write_test(const char * tag,const struct fs_mount_t * mp,const struct lfs_config * cfgp,size_t buf_size,size_t nbuf)160 static int custom_write_test(const char *tag,
161 const struct fs_mount_t *mp,
162 const struct lfs_config *cfgp,
163 size_t buf_size,
164 size_t nbuf)
165 {
166 struct fs_littlefs data = {
167 .cfg = *cfgp,
168 };
169 struct fs_mount_t lfs_mnt = {
170 .type = FS_LITTLEFS,
171 .fs_data = &data,
172 .storage_dev = mp->storage_dev,
173 .mnt_point = mp->mnt_point,
174 };
175 struct lfs_config *lcp = &data.cfg;
176 int rv = TC_FAIL;
177
178 if (lcp->cache_size == 0) {
179 lcp->cache_size = CONFIG_FS_LITTLEFS_CACHE_SIZE;
180 }
181 if (lcp->lookahead_size == 0) {
182 lcp->lookahead_size = CONFIG_FS_LITTLEFS_LOOKAHEAD_SIZE;
183 }
184
185 lcp->read_buffer = malloc(lcp->cache_size);
186 lcp->prog_buffer = malloc(lcp->cache_size);
187 lcp->lookahead_buffer = malloc(lcp->lookahead_size);
188
189 TC_PRINT("bufs %p %p %p\n", lcp->read_buffer, lcp->prog_buffer, lcp->lookahead_buffer);
190
191 if ((lcp->read_buffer == NULL)
192 || (lcp->prog_buffer == NULL)
193 || (lcp->lookahead_buffer == NULL)) {
194 TC_PRINT("%s buffer allocation failed\n", tag);
195 goto out_free;
196 }
197
198 rv = write_read(tag, &lfs_mnt, buf_size, nbuf);
199
200 out_free:
201 if (lcp->read_buffer) {
202 free(lcp->read_buffer);
203 }
204 if (lcp->prog_buffer) {
205 free(lcp->prog_buffer);
206 }
207 if (lcp->lookahead_buffer) {
208 free(lcp->lookahead_buffer);
209 }
210
211 return rv;
212 }
213
small_8_1K_cust(void)214 static int small_8_1K_cust(void)
215 {
216 struct lfs_config cfg = {
217 .read_size = LARGE_IO_SIZE,
218 .prog_size = LARGE_IO_SIZE,
219 .cache_size = LARGE_CACHE_SIZE,
220 .lookahead_size = LARGE_LOOKAHEAD_SIZE
221 };
222
223 return custom_write_test("small 8x1K bigfile", &testfs_small_mnt, &cfg, 1024, 8);
224 }
225
ZTEST(littlefs,test_lfs_perf)226 ZTEST(littlefs, test_lfs_perf)
227 {
228 k_sleep(K_MSEC(100)); /* flush log messages */
229 zassert_equal(write_read("small 8x1K dflt",
230 &testfs_small_mnt,
231 1024, 8),
232 TC_PASS,
233 "failed");
234
235 if (IS_ENABLED(CONFIG_APP_TEST_CUSTOM)) {
236 k_sleep(K_MSEC(100)); /* flush log messages */
237 zassert_equal(small_8_1K_cust(), TC_PASS,
238 "failed");
239
240 k_sleep(K_MSEC(100)); /* flush log messages */
241 zassert_equal(write_read("medium 32x2K dflt",
242 &testfs_medium_mnt,
243 2048, 32),
244 TC_PASS,
245 "failed");
246
247 k_sleep(K_MSEC(100)); /* flush log messages */
248 zassert_equal(write_read("large 64x4K dflt",
249 &testfs_large_mnt,
250 4096, 64),
251 TC_PASS,
252 "failed");
253 }
254 }
255