blob: 74117fc3db2210adf052fa98f073f15f231d726c [file] [log] [blame]
/*
* Copyright (c) 2019 Peter Bigot Consulting, LLC
*
* SPDX-License-Identifier: Apache-2.0
*/
/* littlefs performance testing */
#include <string.h>
#include <stdlib.h>
#include <kernel.h>
#include <ztest.h>
#include "testfs_tests.h"
#include "testfs_lfs.h"
#include <lfs.h>
#include <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);
}
void test_lfs_perf(void)
{
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");
}
}