| /* |
| * Copyright (c) 2019 Peter Bigot Consulting, LLC |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| /* littlefs performance testing */ |
| |
| #include <string.h> |
| #include <stdlib.h> |
| #include <zephyr/kernel.h> |
| #include <zephyr/ztest.h> |
| #include "testfs_tests.h" |
| #include "testfs_lfs.h" |
| #include <lfs.h> |
| |
| #include <zephyr/fs/littlefs.h> |
| |
| #define HELLO "hello" |
| #define GOODBYE "goodbye" |
| |
| static int write_read(const char *tag, |
| struct fs_mount_t *mp, |
| size_t buf_size, |
| size_t nbuf) |
| { |
| const struct lfs_config *lcp = &((const struct fs_littlefs *)mp->fs_data)->cfg; |
| struct testfs_path path; |
| struct fs_statvfs vfs; |
| struct fs_dirent stat; |
| struct fs_file_t file; |
| size_t total = nbuf * buf_size; |
| uint32_t t0; |
| uint32_t t1; |
| uint8_t *buf; |
| int rc; |
| int rv = TC_FAIL; |
| |
| fs_file_t_init(&file); |
| TC_PRINT("clearing %s for %s write/read test\n", |
| mp->mnt_point, tag); |
| if (testfs_lfs_wipe_partition(mp) != TC_PASS) { |
| return TC_FAIL; |
| } |
| |
| rc = fs_mount(mp); |
| if (rc != 0) { |
| TC_PRINT("Mount %s failed: %d\n", mp->mnt_point, rc); |
| return TC_FAIL; |
| } |
| |
| rc = fs_statvfs(mp->mnt_point, &vfs); |
| if (rc != 0) { |
| TC_PRINT("statvfs %s failed: %d\n", mp->mnt_point, rc); |
| goto out_mnt; |
| } |
| |
| TC_PRINT("%s: bsize %lu ; frsize %lu ; blocks %lu ; bfree %lu\n", |
| mp->mnt_point, |
| vfs.f_bsize, vfs.f_frsize, vfs.f_blocks, vfs.f_bfree); |
| TC_PRINT("read_size %u ; prog_size %u ; cache_size %u ; lookahead_size %u\n", |
| lcp->read_size, lcp->prog_size, lcp->cache_size, lcp->lookahead_size); |
| |
| testfs_path_init(&path, mp, |
| "data", |
| TESTFS_PATH_END); |
| |
| buf = calloc(buf_size, sizeof(uint8_t)); |
| if (buf == NULL) { |
| TC_PRINT("Failed to allocate %zu-byte buffer\n", buf_size); |
| goto out_mnt; |
| } |
| |
| for (size_t i = 0; i < buf_size; ++i) { |
| buf[i] = i; |
| } |
| |
| TC_PRINT("creating and writing %zu %zu-byte blocks\n", |
| nbuf, buf_size); |
| |
| rc = fs_open(&file, path.path, FS_O_CREATE | FS_O_RDWR); |
| if (rc != 0) { |
| TC_PRINT("Failed to open %s for write: %d\n", path.path, rc); |
| goto out_buf; |
| } |
| |
| t0 = k_uptime_get_32(); |
| for (size_t i = 0; i < nbuf; ++i) { |
| rc = fs_write(&file, buf, buf_size); |
| if (buf_size != rc) { |
| TC_PRINT("Failed to write buf %zu: %d\n", i, rc); |
| goto out_file; |
| } |
| } |
| t1 = k_uptime_get_32(); |
| |
| if (t1 == t0) { |
| t1++; |
| } |
| |
| (void)fs_close(&file); |
| |
| rc = fs_stat(path.path, &stat); |
| if (rc != 0) { |
| TC_PRINT("Failed to stat %s: %d\n", path.path, rc); |
| goto out_buf; |
| } |
| |
| if (stat.size != total) { |
| TC_PRINT("File size %zu not %zu\n", stat.size, total); |
| goto out_buf; |
| } |
| |
| TC_PRINT("%s write %zu * %zu = %zu bytes in %u ms: " |
| "%u By/s, %u KiBy/s\n", |
| tag, nbuf, buf_size, total, (t1 - t0), |
| (uint32_t)(total * 1000U / (t1 - t0)), |
| (uint32_t)(total * 1000U / (t1 - t0) / 1024U)); |
| |
| rc = fs_open(&file, path.path, FS_O_CREATE | FS_O_RDWR); |
| if (rc != 0) { |
| TC_PRINT("Failed to open %s for write: %d\n", path.path, rc); |
| goto out_buf; |
| } |
| |
| t0 = k_uptime_get_32(); |
| for (size_t i = 0; i < nbuf; ++i) { |
| rc = fs_read(&file, buf, buf_size); |
| if (buf_size != rc) { |
| TC_PRINT("Failed to read buf %zu: %d\n", i, rc); |
| goto out_file; |
| } |
| } |
| t1 = k_uptime_get_32(); |
| |
| if (t1 == t0) { |
| t1++; |
| } |
| |
| TC_PRINT("%s read %zu * %zu = %zu bytes in %u ms: " |
| "%u By/s, %u KiBy/s\n", |
| tag, nbuf, buf_size, total, (t1 - t0), |
| (uint32_t)(total * 1000U / (t1 - t0)), |
| (uint32_t)(total * 1000U / (t1 - t0) / 1024U)); |
| |
| rv = TC_PASS; |
| |
| out_file: |
| (void)fs_close(&file); |
| |
| out_buf: |
| free(buf); |
| |
| out_mnt: |
| (void)fs_unmount(mp); |
| |
| return rv; |
| } |
| |
| static int custom_write_test(const char *tag, |
| const struct fs_mount_t *mp, |
| const struct lfs_config *cfgp, |
| size_t buf_size, |
| size_t nbuf) |
| { |
| struct fs_littlefs data = { |
| .cfg = *cfgp, |
| }; |
| struct fs_mount_t lfs_mnt = { |
| .type = FS_LITTLEFS, |
| .fs_data = &data, |
| .storage_dev = mp->storage_dev, |
| .mnt_point = mp->mnt_point, |
| }; |
| struct lfs_config *lcp = &data.cfg; |
| int rv = TC_FAIL; |
| |
| if (lcp->cache_size == 0) { |
| lcp->cache_size = CONFIG_FS_LITTLEFS_CACHE_SIZE; |
| } |
| if (lcp->lookahead_size == 0) { |
| lcp->lookahead_size = CONFIG_FS_LITTLEFS_LOOKAHEAD_SIZE; |
| } |
| |
| lcp->read_buffer = malloc(lcp->cache_size); |
| lcp->prog_buffer = malloc(lcp->cache_size); |
| lcp->lookahead_buffer = malloc(lcp->lookahead_size); |
| |
| TC_PRINT("bufs %p %p %p\n", lcp->read_buffer, lcp->prog_buffer, lcp->lookahead_buffer); |
| |
| if ((lcp->read_buffer == NULL) |
| || (lcp->prog_buffer == NULL) |
| || (lcp->lookahead_buffer == NULL)) { |
| TC_PRINT("%s buffer allocation failed\n", tag); |
| goto out_free; |
| } |
| |
| rv = write_read(tag, &lfs_mnt, buf_size, nbuf); |
| |
| out_free: |
| if (lcp->read_buffer) { |
| free(lcp->read_buffer); |
| } |
| if (lcp->prog_buffer) { |
| free(lcp->prog_buffer); |
| } |
| if (lcp->lookahead_buffer) { |
| free(lcp->lookahead_buffer); |
| } |
| |
| return rv; |
| } |
| |
| static int small_8_1K_cust(void) |
| { |
| struct lfs_config cfg = { |
| .read_size = LARGE_IO_SIZE, |
| .prog_size = LARGE_IO_SIZE, |
| .cache_size = LARGE_CACHE_SIZE, |
| .lookahead_size = LARGE_LOOKAHEAD_SIZE |
| }; |
| |
| return custom_write_test("small 8x1K bigfile", &testfs_small_mnt, &cfg, 1024, 8); |
| } |
| |
| ZTEST(littlefs, test_lfs_perf) |
| { |
| k_sleep(K_MSEC(100)); /* flush log messages */ |
| zassert_equal(write_read("small 8x1K dflt", |
| &testfs_small_mnt, |
| 1024, 8), |
| TC_PASS, |
| "failed"); |
| |
| if (IS_ENABLED(CONFIG_APP_TEST_CUSTOM)) { |
| k_sleep(K_MSEC(100)); /* flush log messages */ |
| zassert_equal(small_8_1K_cust(), TC_PASS, |
| "failed"); |
| |
| k_sleep(K_MSEC(100)); /* flush log messages */ |
| zassert_equal(write_read("medium 32x2K dflt", |
| &testfs_medium_mnt, |
| 2048, 32), |
| TC_PASS, |
| "failed"); |
| |
| k_sleep(K_MSEC(100)); /* flush log messages */ |
| zassert_equal(write_read("large 64x4K dflt", |
| &testfs_large_mnt, |
| 4096, 64), |
| TC_PASS, |
| "failed"); |
| } |
| } |