812 lines
21 KiB
C
812 lines
21 KiB
C
/*
|
|
* Copyright (c) 2015 Gurjant Kalsi <me@gurjantkalsi.com>
|
|
*
|
|
* Use of this source code is governed by a MIT-style
|
|
* license that can be found in the LICENSE file or at
|
|
* https://opensource.org/licenses/MIT
|
|
*/
|
|
|
|
#if LK_DEBUGLEVEL > 1
|
|
|
|
#include <lk/err.h>
|
|
#include <math.h>
|
|
#include <platform.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <lk/console_cmd.h>
|
|
|
|
#include <lib/bio.h>
|
|
#include <lib/fs/spifs.h>
|
|
|
|
#define FS_NAME "spifs"
|
|
#define MNT_PATH "/s"
|
|
#define TEST_FILE_PATH "/s/test"
|
|
#define TEST_PATH_MAX_SIZE 16
|
|
|
|
typedef bool(*test_func)(const char *);
|
|
|
|
typedef struct {
|
|
test_func func;
|
|
const char *name;
|
|
uint32_t toc_pages;
|
|
} test;
|
|
|
|
static bool test_empty_after_format(const char *);
|
|
static bool test_double_create_file(const char *);
|
|
static bool test_write_read_normal(const char *);
|
|
static bool test_write_past_eof(const char *);
|
|
static bool test_full_toc(const char *);
|
|
static bool test_full_fs(const char *);
|
|
static bool test_write_past_end_of_capacity(const char *);
|
|
static bool test_rm_reclaim(const char *);
|
|
static bool test_corrupt_toc(const char *);
|
|
static bool test_write_with_offset(const char *);
|
|
static bool test_read_write_big(const char *);
|
|
static bool test_rm_active_dirent(const char *);
|
|
static bool test_truncate_file(const char *);
|
|
|
|
static test tests[] = {
|
|
{&test_empty_after_format, "Test no files in ToC after format.", 1},
|
|
{&test_write_read_normal, "Test the normal read/write file paths.", 1},
|
|
{&test_double_create_file, "Test file cannot be created if it already exists.", 1},
|
|
{&test_write_past_eof, "Test that file can grow up to capacity.", 1},
|
|
{&test_full_toc, "Test that files cannot be created once the ToC is full.", 2},
|
|
{&test_full_fs, "Test that files cannot be created once the device is full.", 1},
|
|
{&test_rm_reclaim, "Test that files can be deleted and that used space is reclaimed.", 1},
|
|
{&test_write_past_end_of_capacity, "Test that we cannot write past the capacity of a file.", 1},
|
|
{&test_corrupt_toc, "Test that FS can be mounted with one corrupt ToC.", 1},
|
|
{&test_write_with_offset, "Test that files can be written to at an offset.", 1},
|
|
{&test_read_write_big, "Test that an unaligned ~10kb buffer can be written and read.", 1},
|
|
{&test_rm_active_dirent, "Test that we can remove a file with an open dirent.", 1},
|
|
{&test_truncate_file, "Test that we can truncate a file.", 1},
|
|
};
|
|
|
|
static bool test_setup(const char *dev_name, uint32_t toc_pages) {
|
|
spifs_format_args_t args = {
|
|
.toc_pages = toc_pages,
|
|
};
|
|
|
|
status_t res = fs_format_device(FS_NAME, dev_name, (void *)&args);
|
|
if (res != NO_ERROR) {
|
|
printf("spifs_format failed dev = %s, toc_pages = %u, retcode = %d\n",
|
|
dev_name, toc_pages, res);
|
|
return false;
|
|
}
|
|
|
|
res = fs_mount(MNT_PATH, FS_NAME, dev_name);
|
|
if (res != NO_ERROR) {
|
|
printf("fs_mount failed path = %s, fs name = %s, dev name = %s,"
|
|
" retcode = %d\n", MNT_PATH, FS_NAME, dev_name, res);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool test_teardown(void) {
|
|
if (fs_unmount(MNT_PATH) != NO_ERROR) {
|
|
printf("Unmount failed\n");
|
|
return false;
|
|
}
|
|
|
|
return true;;
|
|
}
|
|
|
|
static bool test_empty_after_format(const char *dev_name) {
|
|
dirhandle *dhandle;
|
|
status_t err = fs_open_dir(MNT_PATH, &dhandle);
|
|
if (err != NO_ERROR) {
|
|
return false;
|
|
}
|
|
|
|
struct dirent ent;
|
|
if (fs_read_dir(dhandle, &ent) >= 0) {
|
|
fs_close_dir(dhandle);
|
|
return false;
|
|
}
|
|
|
|
fs_close_dir(dhandle);
|
|
return true;
|
|
}
|
|
|
|
static bool test_double_create_file(const char *dev_name) {
|
|
status_t status;
|
|
|
|
struct dirent *ent = malloc(sizeof(*ent));
|
|
size_t num_files = 0;
|
|
|
|
filehandle *handle;
|
|
status = fs_create_file(TEST_FILE_PATH, &handle, 10);
|
|
if (status != NO_ERROR) {
|
|
goto err;
|
|
}
|
|
fs_close_file(handle);
|
|
|
|
filehandle *duphandle;
|
|
status = fs_create_file(TEST_FILE_PATH, &duphandle, 20);
|
|
if (status != ERR_ALREADY_EXISTS) {
|
|
goto err;
|
|
}
|
|
|
|
dirhandle *dhandle;
|
|
status = fs_open_dir(MNT_PATH, &dhandle);
|
|
if (status != NO_ERROR) {
|
|
goto err;
|
|
}
|
|
|
|
while ((status = fs_read_dir(dhandle, ent)) >= 0) {
|
|
num_files++;
|
|
}
|
|
|
|
status = NO_ERROR;
|
|
|
|
fs_close_dir(dhandle);
|
|
|
|
|
|
err:
|
|
free(ent);
|
|
|
|
return status == NO_ERROR ? num_files == 1 : false;
|
|
}
|
|
|
|
static bool test_write_read_normal(const char *dev_name) {
|
|
char test_message[] = "spifs test";
|
|
char test_buf[sizeof(test_message)];
|
|
|
|
bdev_t *dev = bio_open(dev_name);
|
|
if (!dev) {
|
|
return false;
|
|
}
|
|
uint8_t erase_byte = dev->erase_byte;
|
|
bio_close(dev);
|
|
|
|
filehandle *handle;
|
|
status_t status =
|
|
fs_create_file(TEST_FILE_PATH, &handle, sizeof(test_message));
|
|
if (status != NO_ERROR) {
|
|
return false;
|
|
}
|
|
|
|
ssize_t bytes;
|
|
|
|
// New files should be initialized to 'erase_byte'
|
|
bytes = fs_read_file(handle, test_buf, 0, sizeof(test_buf));
|
|
if (bytes != sizeof(test_buf)) {
|
|
return false;
|
|
}
|
|
|
|
for (size_t i = 0; i < sizeof(test_buf); i++) {
|
|
if (test_buf[i] != erase_byte) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bytes = fs_write_file(handle, test_message, 0, sizeof(test_message));
|
|
if (bytes != sizeof(test_message)) {
|
|
return false;
|
|
}
|
|
|
|
bytes = fs_read_file(handle, test_buf, 0, sizeof(test_buf));
|
|
if (bytes != sizeof(test_buf)) {
|
|
return false;
|
|
}
|
|
|
|
status = fs_close_file(handle);
|
|
if (status != NO_ERROR) {
|
|
return false;
|
|
}
|
|
|
|
return strncmp(test_message, test_buf, sizeof(test_message)) == 0;
|
|
}
|
|
|
|
static bool test_write_past_eof(const char *dev_name) {
|
|
char test_message[] = "spifs test";
|
|
|
|
// Create a 0 length file.
|
|
filehandle *handle;
|
|
status_t status =
|
|
fs_create_file(TEST_FILE_PATH, &handle, 0);
|
|
if (status != NO_ERROR) {
|
|
return false;
|
|
}
|
|
|
|
ssize_t bytes = fs_write_file(handle, test_message, 0, sizeof(test_message));
|
|
if (bytes != sizeof(test_message)) {
|
|
return false;
|
|
}
|
|
|
|
// Make sure the file grows.
|
|
struct file_stat stat;
|
|
fs_stat_file(handle, &stat);
|
|
if (stat.is_dir != false && stat.size != sizeof(test_message)) {
|
|
return false;
|
|
}
|
|
|
|
fs_close_file(handle);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool test_full_toc(const char *dev_name) {
|
|
struct fs_stat stat;
|
|
|
|
fs_stat_fs(MNT_PATH, &stat);
|
|
|
|
char test_file_name[TEST_PATH_MAX_SIZE];
|
|
|
|
filehandle *handle;
|
|
for (size_t i = 0; i < stat.free_inodes; i++) {
|
|
memset(test_file_name, 0, TEST_PATH_MAX_SIZE);
|
|
|
|
char filenum[] = "000";
|
|
filenum[0] += i / 100;
|
|
filenum[1] += (i / 10) % 10;
|
|
filenum[2] += i % 10;
|
|
|
|
strlcat(test_file_name, MNT_PATH, sizeof(test_file_name));
|
|
strlcat(test_file_name, "/", sizeof(test_file_name));
|
|
strlcat(test_file_name, filenum, sizeof(test_file_name));
|
|
|
|
status_t status =
|
|
fs_create_file(test_file_name, &handle, 1);
|
|
|
|
if (status != NO_ERROR) {
|
|
return false;
|
|
}
|
|
|
|
fs_close_file(handle);
|
|
}
|
|
|
|
// There shouldn't be enough space for this file since we've exhausted all
|
|
// the inodes.
|
|
|
|
status_t status = fs_create_file(TEST_FILE_PATH, &handle, 1);
|
|
if (status != ERR_TOO_BIG) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool test_rm_reclaim(const char *dev_name) {
|
|
// Create some number of files that's a power of 2;
|
|
size_t n_files = 4;
|
|
|
|
struct fs_stat stat;
|
|
|
|
fs_stat_fs(MNT_PATH, &stat);
|
|
|
|
size_t file_size = stat.free_space / (n_files + 1);
|
|
|
|
char test_file_name[TEST_PATH_MAX_SIZE];
|
|
|
|
filehandle *handle;
|
|
for (size_t i = 0; i < n_files; i++) {
|
|
memset(test_file_name, 0, TEST_PATH_MAX_SIZE);
|
|
|
|
char filenum[] = "000";
|
|
filenum[0] += i / 100;
|
|
filenum[1] += (i / 10) % 10;
|
|
filenum[2] += i % 10;
|
|
|
|
strcat(test_file_name, MNT_PATH);
|
|
strcat(test_file_name, filenum);
|
|
|
|
status_t status =
|
|
fs_create_file(test_file_name, &handle, file_size);
|
|
|
|
if (status != NO_ERROR) {
|
|
return false;
|
|
}
|
|
|
|
fs_close_file(handle);
|
|
}
|
|
|
|
// Try to create a new Big file.
|
|
char filename[] = "BIGFILE";
|
|
memset(test_file_name, 0, TEST_PATH_MAX_SIZE);
|
|
strcat(test_file_name, MNT_PATH);
|
|
strcat(test_file_name, filename);
|
|
|
|
status_t status;
|
|
|
|
// This should fail because there's no more space.
|
|
fs_stat_fs(MNT_PATH, &stat);
|
|
status = fs_create_file(test_file_name, &handle, stat.free_space + 1);
|
|
if (status != ERR_TOO_BIG) {
|
|
return false;
|
|
}
|
|
|
|
// Delete an existing file to make space for the new file.
|
|
char existing_filename[] = "001";
|
|
memset(test_file_name, 0, TEST_PATH_MAX_SIZE);
|
|
strcat(test_file_name, MNT_PATH);
|
|
strcat(test_file_name, existing_filename);
|
|
|
|
status = fs_remove_file(test_file_name);
|
|
if (status != NO_ERROR) {
|
|
return false;
|
|
}
|
|
|
|
|
|
// Now this should go through because we've reclaimed the space.
|
|
status = fs_create_file(test_file_name, &handle, stat.free_space + 1);
|
|
if (status != NO_ERROR) {
|
|
return false;
|
|
}
|
|
|
|
fs_close_file(handle);
|
|
return true;
|
|
}
|
|
|
|
static bool test_full_fs(const char *dev_name) {
|
|
struct fs_stat stat;
|
|
|
|
fs_stat_fs(MNT_PATH, &stat);
|
|
|
|
char second_file_path[TEST_PATH_MAX_SIZE];
|
|
memset(second_file_path, 0, TEST_PATH_MAX_SIZE);
|
|
strcpy(second_file_path, MNT_PATH);
|
|
strcat(second_file_path, "/fail");
|
|
|
|
filehandle *handle;
|
|
status_t status = fs_create_file(TEST_FILE_PATH, &handle, stat.free_space);
|
|
if (status != NO_ERROR) {
|
|
return false;
|
|
}
|
|
|
|
fs_close_file(handle);
|
|
|
|
// There shouldn't be enough space for this file since we've used all the
|
|
// space.
|
|
status = fs_create_file(second_file_path, &handle, 1);
|
|
if (status != ERR_TOO_BIG) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool test_write_past_end_of_capacity(const char *dev_name) {
|
|
filehandle *handle;
|
|
status_t status = fs_create_file(TEST_FILE_PATH, &handle, 0);
|
|
if (status != NO_ERROR) {
|
|
return false;
|
|
}
|
|
|
|
struct file_stat stat;
|
|
status = fs_stat_file(handle, &stat);
|
|
if (status != NO_ERROR) {
|
|
goto finish;
|
|
}
|
|
|
|
// We shouldn't be able to write past the capacity of a file.
|
|
char buf[1];
|
|
status = fs_write_file(handle, buf, stat.capacity, 1);
|
|
if (status == ERR_OUT_OF_RANGE) {
|
|
status = NO_ERROR;
|
|
} else {
|
|
status = ERR_IO;
|
|
}
|
|
|
|
finish:
|
|
fs_close_file(handle);
|
|
return status == NO_ERROR;
|
|
}
|
|
|
|
static bool test_corrupt_toc(const char *dev_name) {
|
|
// Create a zero byte file.
|
|
filehandle *handle;
|
|
status_t status = fs_create_file(TEST_FILE_PATH, &handle, 0);
|
|
if (status != NO_ERROR) {
|
|
return false;
|
|
}
|
|
|
|
// Grow the file to one byte. This should trigger a ToC flush. Now the
|
|
// ToC record for this file should exist in both ToCs. Therefore corrupting
|
|
// either of the ToCs will still yield this file readable.
|
|
char buf[1] = { 'a' };
|
|
status = fs_write_file(handle, buf, 0, 1);
|
|
if (status != 1) {
|
|
return false;
|
|
}
|
|
|
|
status = fs_close_file(handle);
|
|
if (status != NO_ERROR) {
|
|
return false;
|
|
}
|
|
|
|
status = fs_unmount(MNT_PATH);
|
|
if (status != NO_ERROR) {
|
|
return false;
|
|
}
|
|
|
|
// Now we're going to manually corrupt one of the ToCs
|
|
bdev_t *dev = bio_open(dev_name);
|
|
if (!dev) {
|
|
return false;
|
|
}
|
|
|
|
// Directly write 0s to the block that contains the front-ToC.
|
|
size_t block_size = dev->block_size;
|
|
uint8_t *block_buf = memalign(CACHE_LINE, block_size);
|
|
if (!block_buf) {
|
|
return false;
|
|
}
|
|
memset(block_buf, 0, block_size);
|
|
|
|
ssize_t bytes = bio_write_block(dev, block_buf, 0, 1);
|
|
|
|
free(block_buf);
|
|
|
|
bio_close(dev);
|
|
|
|
if (bytes != (ssize_t)block_size) {
|
|
return false;
|
|
}
|
|
|
|
// Mount the FS again and make sure that the file we created is still there.
|
|
status = fs_mount(MNT_PATH, FS_NAME, dev_name);
|
|
if (status != NO_ERROR) {
|
|
return false;
|
|
}
|
|
|
|
status = fs_open_file(TEST_FILE_PATH, &handle);
|
|
if (status != NO_ERROR) {
|
|
return false;
|
|
}
|
|
|
|
struct file_stat stat;
|
|
status = fs_stat_file(handle, &stat);
|
|
if (status != NO_ERROR) {
|
|
return false;
|
|
}
|
|
|
|
status = fs_close_file(handle);
|
|
if (status != NO_ERROR) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool test_write_with_offset(const char *dev_name) {
|
|
size_t repeats = 3;
|
|
char test_message[] = "test";
|
|
size_t msg_len = strnlen(test_message, sizeof(test_message));
|
|
char test_buf[msg_len * repeats];
|
|
|
|
filehandle *handle;
|
|
status_t status = fs_create_file(TEST_FILE_PATH, &handle, msg_len);
|
|
if (status != NO_ERROR) {
|
|
return false;
|
|
}
|
|
|
|
ssize_t bytes;
|
|
for (size_t pos = 0; pos < repeats; pos++) {
|
|
bytes = fs_write_file(handle, test_message, pos * msg_len, msg_len);
|
|
if ((size_t)bytes != msg_len) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bytes = fs_read_file(handle, test_buf, 0, msg_len * repeats);
|
|
if ((size_t)bytes != msg_len * repeats) {
|
|
return false;
|
|
}
|
|
|
|
status = fs_close_file(handle);
|
|
if (status != NO_ERROR) {
|
|
return false;
|
|
}
|
|
|
|
bool success = true;
|
|
for (size_t i = 0; i < repeats; i++) {
|
|
success &= (memcmp(test_message,
|
|
test_buf + i * msg_len,
|
|
msg_len) == 0);
|
|
}
|
|
return success;
|
|
}
|
|
|
|
static bool test_read_write_big(const char *dev_name) {
|
|
bool success = true;
|
|
|
|
size_t buflen = 10013;
|
|
|
|
uint8_t *rbuf = malloc(buflen);
|
|
if (!rbuf) {
|
|
return false;
|
|
}
|
|
|
|
uint8_t *wbuf = malloc(buflen);
|
|
if (!wbuf) {
|
|
free(rbuf);
|
|
return false;
|
|
}
|
|
|
|
for (size_t i = 0; i < buflen; i++) {
|
|
wbuf[i] = rand() % sizeof(uint8_t);
|
|
}
|
|
|
|
filehandle *handle;
|
|
status_t status = fs_create_file(TEST_FILE_PATH, &handle, buflen);
|
|
if (status != NO_ERROR) {
|
|
success = false;
|
|
goto err;
|
|
}
|
|
|
|
ssize_t bytes = fs_write_file(handle, wbuf, 0, buflen);
|
|
if ((size_t)bytes != buflen) {
|
|
success = false;
|
|
goto err;
|
|
}
|
|
|
|
bytes = fs_read_file(handle, rbuf, 0, buflen);
|
|
if ((size_t)bytes != buflen) {
|
|
success = false;
|
|
goto err;
|
|
}
|
|
|
|
for (size_t i = 0; i < buflen; i++) {
|
|
if (wbuf[i] != rbuf[i]) {
|
|
success = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
err:
|
|
success &= fs_close_file(handle) == NO_ERROR;
|
|
|
|
free(rbuf);
|
|
free(wbuf);
|
|
return success;
|
|
}
|
|
|
|
static bool test_rm_active_dirent(const char *dev_name) {
|
|
filehandle *handle;
|
|
status_t status = fs_create_file(TEST_FILE_PATH, &handle, 0);
|
|
if (status != NO_ERROR) {
|
|
return false;
|
|
}
|
|
|
|
status = fs_close_file(handle);
|
|
if (status != NO_ERROR) {
|
|
return false;
|
|
}
|
|
|
|
dirhandle *dhandle;
|
|
status = fs_open_dir(MNT_PATH, &dhandle);
|
|
if (status != NO_ERROR) {
|
|
return false;
|
|
}
|
|
|
|
// Dir handle should now be pointing to the only file in our FS.
|
|
status = fs_remove_file(TEST_FILE_PATH);
|
|
if (status != NO_ERROR) {
|
|
fs_close_dir(dhandle);
|
|
return false;
|
|
}
|
|
|
|
bool success = true;
|
|
struct dirent *ent = malloc(sizeof(*ent));
|
|
if (fs_read_dir(dhandle, ent) >= 0) {
|
|
success = false;
|
|
}
|
|
|
|
success &= fs_close_dir(dhandle) == NO_ERROR;
|
|
free(ent);
|
|
|
|
return success;
|
|
}
|
|
|
|
static bool test_truncate_file(const char *dev_name) {
|
|
filehandle *handle;
|
|
status_t status =
|
|
fs_create_file(TEST_FILE_PATH, &handle, 1024);
|
|
if (status != NO_ERROR) {
|
|
return false;
|
|
}
|
|
|
|
// File size is 1024?
|
|
struct file_stat stat;
|
|
fs_stat_file(handle, &stat);
|
|
if (stat.size != 1024) {
|
|
return false;
|
|
}
|
|
|
|
status = fs_truncate_file(handle, 512);
|
|
if (status != NO_ERROR) {
|
|
return false;
|
|
}
|
|
|
|
fs_stat_file(handle, &stat);
|
|
if (stat.size != 512) {
|
|
return false;
|
|
}
|
|
|
|
return fs_close_file(handle) == NO_ERROR;
|
|
}
|
|
|
|
// Run the SPIFS test suite.
|
|
static int spifs_test(int argc, const console_cmd_args *argv) {
|
|
if (argc != 3) {
|
|
printf("Expected 3 arguments, got %d.\n", argc);
|
|
return -1;
|
|
}
|
|
|
|
// Make sure this block device is legit.
|
|
bdev_t *dev = bio_open(argv[2].str);
|
|
if (!dev) {
|
|
printf("error: could not open block device %s\n", argv[2].str);
|
|
return -1;
|
|
}
|
|
bio_close(dev);
|
|
|
|
size_t passed = 0;
|
|
size_t attempted = 0;
|
|
for (size_t i = 0; i < countof(tests); i++) {
|
|
++attempted;
|
|
if (!test_setup(argv[2].str, tests[i].toc_pages)) {
|
|
printf("Test Setup failed before %s. Exiting.\n", tests[i].name);
|
|
break;
|
|
}
|
|
|
|
if (tests[i].func(argv[2].str)) {
|
|
printf(" [Passed] %s\n", tests[i].name);
|
|
++passed;
|
|
} else {
|
|
printf(" [Failed] %s\n", tests[i].name);
|
|
}
|
|
|
|
if (!test_teardown()) {
|
|
printf("Test teardown failed after %s. Exiting.\n", tests[i].name);
|
|
break;
|
|
}
|
|
}
|
|
printf("\nPassed %zu of %zu tests.\n", passed, attempted);
|
|
|
|
if (attempted != countof(tests)) {
|
|
printf("(Skipped %zu)\n", countof(tests) - attempted);
|
|
}
|
|
|
|
return countof(tests) - passed;
|
|
}
|
|
|
|
// Benchmark SPIFS.
|
|
static int spifs_bench(int argc, const console_cmd_args *argv) {
|
|
if (argc != 3) {
|
|
printf("Expected 3 arguments, got %d.\n", argc);
|
|
return -1;
|
|
}
|
|
|
|
static const size_t test_buffer_length = 4096;
|
|
static const size_t min_bench_bytes = 0x40; // 64b
|
|
static const size_t max_bench_bytes = 0x100000; // 1MiB
|
|
const char *test_file_path = argv[2].str;
|
|
|
|
status_t st;
|
|
filehandle *handle;
|
|
int retcode = 0;
|
|
lk_bigtime_t start, end;
|
|
|
|
uint8_t *test_buffer = malloc(test_buffer_length);
|
|
memset(test_buffer, 0xAB, test_buffer_length);
|
|
|
|
for (size_t file_size = min_bench_bytes;
|
|
file_size <= max_bench_bytes;
|
|
file_size *= 2) {
|
|
|
|
printf(" == Benchmark for %zu bytes == \n", file_size);
|
|
|
|
// Create file benchmark
|
|
start = current_time_hires();
|
|
st = fs_create_file(test_file_path, &handle, file_size);
|
|
end = current_time_hires();
|
|
printf("\tfs_create_file = %llu usecs\n", end - start);
|
|
|
|
if (st != NO_ERROR) {
|
|
printf("SPIFS Benchmark Failed to create a %zu byte file at %s. "
|
|
"Reason = %d.\n", file_size, test_file_path, st);
|
|
retcode = -1;
|
|
goto finish;
|
|
}
|
|
|
|
|
|
// Write File Benchmark
|
|
off_t offset = 0;
|
|
ssize_t n_bytes = 0;
|
|
start = current_time_hires();
|
|
do {
|
|
size_t write_length = MIN(
|
|
test_buffer_length,
|
|
file_size - offset
|
|
);
|
|
n_bytes = fs_write_file(handle, test_buffer, offset, write_length);
|
|
offset += n_bytes;
|
|
} while (n_bytes > 0);
|
|
|
|
end = current_time_hires();
|
|
printf("\tfs_write_file = %llu usecs\n", end - start);
|
|
|
|
if (n_bytes < 0) {
|
|
printf("SPIFS Benchmark Failed to write to file at %s. "
|
|
"Reason = %ld.\n", test_file_path, n_bytes);
|
|
retcode = -1;
|
|
fs_close_file(handle);
|
|
goto finish;
|
|
}
|
|
|
|
// Read File Benchmark
|
|
offset = 0;
|
|
n_bytes = 0;
|
|
start = current_time_hires();
|
|
do {
|
|
n_bytes = fs_read_file(handle, test_buffer, offset,
|
|
test_buffer_length);
|
|
offset += n_bytes;
|
|
} while (n_bytes > 0);
|
|
|
|
end = current_time_hires();
|
|
printf("\tfs_read_file = %llu usecs\n", end - start);
|
|
|
|
if (n_bytes < 0) {
|
|
printf("SPIFS Benchmark Failed to read from file at %s. "
|
|
"Reason = %ld.\n", test_file_path, n_bytes);
|
|
retcode = -1;
|
|
fs_close_file(handle);
|
|
goto finish;
|
|
}
|
|
|
|
st = fs_close_file(handle);
|
|
if (st != NO_ERROR) {
|
|
printf("SPIFS Benchmark Failed to close file at %s. "
|
|
"Reason = %d.\n", test_file_path, st);
|
|
retcode = -1;
|
|
goto finish;
|
|
}
|
|
|
|
start = current_time_hires();
|
|
st = fs_remove_file(test_file_path);
|
|
end = current_time_hires();
|
|
printf("\tfs_remove_file = %llu usecs\n", end - start);
|
|
|
|
if (st != NO_ERROR) {
|
|
printf("SPIFS Benchmark Failed to remove file at %s. "
|
|
"Reason = %d.\n", test_file_path, st);
|
|
retcode = -1;
|
|
goto finish;
|
|
}
|
|
|
|
printf("\n");
|
|
}
|
|
finish:
|
|
free(test_buffer);
|
|
return retcode;
|
|
}
|
|
|
|
static int cmd_spifs(int argc, const console_cmd_args *argv) {
|
|
if (argc < 3) {
|
|
printf("not enough arguments:\n");
|
|
usage:
|
|
printf("%s test <device>\n", argv[0].str);
|
|
printf("%s bench <path>\n", argv[0].str);
|
|
return -1;
|
|
}
|
|
|
|
if (!strcmp(argv[1].str, "test")) {
|
|
return spifs_test(argc, argv);
|
|
} else if (!strcmp(argv[1].str, "bench")) {
|
|
return spifs_bench(argc, argv);
|
|
}
|
|
|
|
// Command not found.
|
|
goto usage;
|
|
}
|
|
|
|
STATIC_COMMAND_START
|
|
STATIC_COMMAND("spifs", "commands related to the spifs implementation.", &cmd_spifs)
|
|
STATIC_COMMAND_END(spifs);
|
|
|
|
#endif // LK_DEBUGLEVEL > 1
|