fdc8c8caf1
There are a number of nuanced cases to watch out for when mixing sync,
desync, and "noop syncs" (sync when no write operation has occured):
1. Noop-sync after unrelated write:
op a state b state
in-sync in-sync
write(a) unsync in-sync
sync(b) in-sync in-sync
In this case, a should be clobbered by b when b syncs. But this
gets tricky since b is still up to date with the disk, so b's
unsynced flag is not set.
The solution here is to just unconditionally broadcast all sync
operations irregardless of on-disk state. This is all in-device
anyways, so it shouldn't really add any overhead.
2. Noop-sync after unrelated write-sync after desync:
op a state b state
in-sync in-sync
desync(b) in-sync desync
write(a) unsync desync
sync(a) in-sync' desync
sync(b) in-sync in-sync
In this case, a should again be clobbered by b, even though a is
in-sync with the disk. This is not tricky because of a's state, but
because b doesn't know it is no longer in-sync with the disk.
The solution here is to set the unsynced flag on all desynced files
when an unrelated file is synced. This way, b knows it needs to
update disk if sync is called. We already scan all opened files to
update in-sync files, so this has very little cost.
3. Readonly-sync after unrelated write-sync after desync?
This is basically the same as 2., but involves a readonly file:
op a state b state (rdonly)
in-sync in-sync
desync(b) in-sync desync
write(a) unsync desync
sync(a) in-sync' desync
sync(b) ??? in-sync
In this case, I have no idea what should happen.
I would guess the least surprising result would be for b to write
its contents to a/disk? Bringing everything in-sync?
But this implies that b, a readonly file, should write to disk.
This isn't the only place a read operation would result in a write.
RDWR files, for example, can flush buffers during a file read. But at
least there, the file is open RDWR, not strictly RDONLY.
It seems like writing during sync on a readonly file breaks some sort
of invariant users expect.
But the alternative: Dropping the current state of b in favor of a's
state, is inconsistent with sync on WRONLY/RDWR files, and seems like
it breaks some sort of invariant about sync modifying the current
file's state...
Given this situation, I think the best course of action is to just
disallow sync on readonly files. It is now an assert.
There is some precedent for this, upstream we already omit sync when
compiled in LFS_READONLY mode. Though this does deviate from POSIX
behavior...
Worst case, by asserting, this leaves us free to introduce different
readonly-sync behavior in the future without breaking backwards
compatibility.
---
Maybe there should be some sort of lfsr_file_resync function to
discard current changes? Though this can be done with a close+open
cycle, so I think the value would be low.
Added tests over these cases and fixed where they broke, except for 3.,
lfsr_file_sync and lfsr_file_flush get asserts now to prevent their use
on readonly files.
Also added a couple more specific tests to cover cases I was concerned
about.
3165 lines
103 KiB
TOML
3165 lines
103 KiB
TOML
# Test multiple open file handles in different r/w configurations
|
|
after = 'test_fwrite'
|
|
|
|
|
|
# Some specific tests
|
|
[cases.test_fsync_sync_wrr]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// a - writer
|
|
// b - reader kept open, recvs updates from a
|
|
// c - reader kept closed, checks disk state
|
|
lfsr_file_t a;
|
|
lfsr_file_t b;
|
|
lfsr_file_t c;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_open(&lfs, &a, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_open(&lfs, &b, "jello", LFS_O_RDONLY) => 0;
|
|
|
|
// write to a
|
|
lfsr_file_write(&lfs, &a, "hello!", strlen("hello!"))
|
|
=> strlen("hello!");
|
|
|
|
// should not show up in b yet
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => 0;
|
|
// or on disk
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => 0;
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
|
|
// sync a
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// now our write should show up in b
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
|
|
// lets rewrite a
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "bonjour!", strlen("bonjour!"))
|
|
=> strlen("bonjour!");
|
|
|
|
// b should still have previous contents
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
|
|
// sync a
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// now our write should show up in b
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
|
|
// lets rewrite a one last time
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "ohayo!", strlen("ohayo!"))
|
|
=> strlen("ohayo!");
|
|
|
|
// b should still have previous contents
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
|
|
// close a
|
|
lfsr_file_close(&lfs, &a) => 0;
|
|
|
|
// now our write should show up in b
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
|
|
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
|
|
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
|
|
lfsr_file_close(&lfs, &b) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_sync_wwrr]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// a - writer
|
|
// b - writer
|
|
// c - reader kept open, recvs updates from a/b
|
|
// d - reader kept closed, checks disk state
|
|
lfsr_file_t a;
|
|
lfsr_file_t b;
|
|
lfsr_file_t c;
|
|
lfsr_file_t d;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_open(&lfs, &a, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_open(&lfs, &b, "jello", LFS_O_WRONLY) => 0;
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
|
|
// write to a and b
|
|
lfsr_file_write(&lfs, &a, "hello!", strlen("hello!"))
|
|
=> strlen("hello!");
|
|
lfsr_file_write(&lfs, &b, "bonjour!", strlen("bonjour!"))
|
|
=> strlen("bonjour!");
|
|
|
|
// should not show up in c yet
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => 0;
|
|
// or on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => 0;
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync a
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// now our write should show up in c
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync b
|
|
lfsr_file_sync(&lfs, &b) => 0;
|
|
|
|
// b's contents were clobbered, so we should see a
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// lets rewrite b
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_write(&lfs, &b, "ohayo!", strlen("ohayo!"))
|
|
=> strlen("ohayo!");
|
|
|
|
// c should still have previous contents
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync b
|
|
lfsr_file_sync(&lfs, &b) => 0;
|
|
|
|
// now our write should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!");
|
|
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!");
|
|
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// lets rewrite a one last time
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "zdrasti!", strlen("zdrasti!"))
|
|
=> strlen("zdrasti!");
|
|
|
|
// c should still have previous contents
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!");
|
|
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!");
|
|
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// close a and b
|
|
lfsr_file_close(&lfs, &a) => 0;
|
|
lfsr_file_close(&lfs, &b) => 0;
|
|
|
|
// now our write should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_sync_wwrr_noop]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// a - writer
|
|
// b - writer
|
|
// c - reader kept open, recvs updates from a/b
|
|
// d - reader kept closed, checks disk state
|
|
lfsr_file_t a;
|
|
lfsr_file_t b;
|
|
lfsr_file_t c;
|
|
lfsr_file_t d;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_open(&lfs, &a, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_open(&lfs, &b, "jello", LFS_O_WRONLY) => 0;
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
|
|
// write to a and b
|
|
lfsr_file_write(&lfs, &a, "hello!", strlen("hello!"))
|
|
=> strlen("hello!");
|
|
lfsr_file_write(&lfs, &b, "bonjour!", strlen("bonjour!"))
|
|
=> strlen("bonjour!");
|
|
|
|
// should not show up in c yet
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => 0;
|
|
// or on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => 0;
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync a
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// now our write should show up in c
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync b
|
|
lfsr_file_sync(&lfs, &b) => 0;
|
|
|
|
// b's contents were clobbered, so we should see a
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// lets rewrite b
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_write(&lfs, &b, "ohayo!", strlen("ohayo!"))
|
|
=> strlen("ohayo!");
|
|
|
|
// c should still have previous contents
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync a, without doing anything
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// c should still have previous contents
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync b, this gets tricky
|
|
lfsr_file_sync(&lfs, &b) => 0;
|
|
|
|
// b's contents were clobbered, so we should see a
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// lets rewrite b one last time, this time with sync
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_write(&lfs, &b, "zdrasti!", strlen("zdrasti!"))
|
|
=> strlen("zdrasti!");
|
|
lfsr_file_sync(&lfs, &b) => 0;
|
|
|
|
// now our write should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// close a and b
|
|
lfsr_file_close(&lfs, &a) => 0;
|
|
lfsr_file_close(&lfs, &b) => 0;
|
|
|
|
// our write should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_sync_wwrr_append]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// a - writer
|
|
// b - writer
|
|
// c - reader kept open, recvs updates from a/b
|
|
// d - reader kept closed, checks disk state
|
|
lfsr_file_t a;
|
|
lfsr_file_t b;
|
|
lfsr_file_t c;
|
|
lfsr_file_t d;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_open(&lfs, &a, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_APPEND) => 0;
|
|
lfsr_file_open(&lfs, &b, "jello", LFS_O_WRONLY | LFS_O_APPEND) => 0;
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
|
|
// write to a and b
|
|
lfsr_file_write(&lfs, &a, "hello!", strlen("hello!"))
|
|
=> strlen("hello!");
|
|
lfsr_file_write(&lfs, &b, "bonjour!", strlen("bonjour!"))
|
|
=> strlen("bonjour!");
|
|
|
|
// should not show up in c yet
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => 0;
|
|
// or on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => 0;
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync a
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// now our write should show up in c
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync b
|
|
lfsr_file_sync(&lfs, &b) => 0;
|
|
|
|
// b's contents were clobbered, so we should see a
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// lets rewrite b
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_write(&lfs, &b, "ohayo!", strlen("ohayo!"))
|
|
=> strlen("ohayo!");
|
|
|
|
// c should still have previous contents
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync b
|
|
lfsr_file_sync(&lfs, &b) => 0;
|
|
|
|
// now our write should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!");
|
|
assert(memcmp(rbuf, "hello!ohayo!", strlen("hello!ohayo!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!");
|
|
assert(memcmp(rbuf, "hello!ohayo!", strlen("hello!ohayo!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// lets rewrite a one last time
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "zdrasti!", strlen("zdrasti!"))
|
|
=> strlen("zdrasti!");
|
|
|
|
// c should still have previous contents
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!");
|
|
assert(memcmp(rbuf, "hello!ohayo!", strlen("hello!ohayo!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!");
|
|
assert(memcmp(rbuf, "hello!ohayo!", strlen("hello!ohayo!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// close a and b
|
|
lfsr_file_close(&lfs, &a) => 0;
|
|
lfsr_file_close(&lfs, &b) => 0;
|
|
|
|
// now our write should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf))
|
|
=> strlen("hello!ohayo!zdrasti!");
|
|
assert(memcmp(rbuf, "hello!ohayo!zdrasti!",
|
|
strlen("hello!ohayo!zdrasti!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf))
|
|
=> strlen("hello!ohayo!zdrasti!");
|
|
assert(memcmp(rbuf, "hello!ohayo!zdrasti!",
|
|
strlen("hello!ohayo!zdrasti!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
|
|
# Test multiple readers, this shouldn't really have any issues
|
|
[cases.test_fsync_rrrr]
|
|
defines.R = 4
|
|
defines.SIZE = [
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// read R handles in parallel
|
|
lfsr_file_t readers[R];
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_open(&lfs, &readers[r], "jello", LFS_O_RDONLY) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &readers[r], rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(rbuf, &before[i], CHUNK) == 0);
|
|
}
|
|
}
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_close(&lfs, &readers[r]) => 0;
|
|
}
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_rrrr_fuzz]
|
|
defines.R = 4
|
|
defines.SEED = 'range(10)'
|
|
defines.N = 20
|
|
defines.SIZE = [
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = SEED;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// read R handles in parallel
|
|
lfsr_file_t readers[R];
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_open(&lfs, &readers[r], "jello", LFS_O_RDONLY) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
// choose a random offset
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
lfs_size_t size = lfs_min32(CHUNK, SIZE - off);
|
|
lfsr_file_seek(&lfs, &readers[r], off, LFS_SEEK_SET) => off;
|
|
|
|
// read
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &readers[r], rbuf, CHUNK) => size;
|
|
assert(memcmp(rbuf, &before[off], size) == 0);
|
|
}
|
|
}
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_close(&lfs, &readers[r]) => 0;
|
|
}
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# Test one writer, multiple readers
|
|
[cases.test_fsync_wrrr]
|
|
defines.R = 4
|
|
# 0 => no sync, readers not updated
|
|
# 1 => sync via lfsr_file_sync
|
|
# 2 => sync via LFS_O_SYNC
|
|
defines.SYNC = [0, 1, 2]
|
|
# 0 => no flush
|
|
# 1 => flush via lfsr_file_flush
|
|
# 2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.SIZE = [
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t after[SIZE];
|
|
memcpy(after, before, SIZE);
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// write 1 handle, read R handles in parallel
|
|
lfsr_file_t writer;
|
|
lfsr_file_t readers[R];
|
|
lfsr_file_open(&lfs, &writer, "jello",
|
|
LFS_O_WRONLY
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_open(&lfs, &readers[r], "jello", LFS_O_RDONLY) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &writer, wbuf, CHUNK) => CHUNK;
|
|
memcpy(&after[i], wbuf, CHUNK);
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &writer) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &writer) => 0;
|
|
}
|
|
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &readers[r], rbuf, CHUNK) => CHUNK;
|
|
if (SYNC == 0) {
|
|
assert(memcmp(rbuf, &before[i], CHUNK) == 0);
|
|
} else {
|
|
assert(memcmp(rbuf, &after[i], CHUNK) == 0);
|
|
}
|
|
}
|
|
}
|
|
lfsr_file_close(&lfs, &writer) => 0;
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_close(&lfs, &readers[r]) => 0;
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, after, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_wrrr_fuzz]
|
|
defines.R = 4
|
|
# 0 => no sync, readers not updated
|
|
# 1 => sync via lfsr_file_sync
|
|
# 2 => sync via LFS_O_SYNC
|
|
defines.SYNC = [0, 1, 2]
|
|
# 0 => no flush
|
|
# 1 => flush via lfsr_file_flush
|
|
# 2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.SEED = 'range(10)'
|
|
defines.N = 20
|
|
defines.SIZE = [
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t after[SIZE];
|
|
memcpy(after, before, SIZE);
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// write 1 handle, read R handles in parallel
|
|
lfsr_file_t writer;
|
|
lfsr_file_t readers[R];
|
|
lfsr_file_open(&lfs, &writer, "jello",
|
|
LFS_O_WRONLY
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_open(&lfs, &readers[r], "jello", LFS_O_RDONLY) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// choose a random offset
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
lfs_size_t size = lfs_min32(CHUNK, SIZE - off);
|
|
lfsr_file_seek(&lfs, &writer, off, LFS_SEEK_SET) => off;
|
|
|
|
// write
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < size; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &writer, wbuf, size) => size;
|
|
memcpy(&after[off], wbuf, size);
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &writer) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &writer) => 0;
|
|
}
|
|
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
// choose a random offset
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
lfs_size_t size = lfs_min32(CHUNK, SIZE - off);
|
|
lfsr_file_seek(&lfs, &readers[r], off, LFS_SEEK_SET) => off;
|
|
|
|
// read
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &readers[r], rbuf, CHUNK) => size;
|
|
if (SYNC == 0) {
|
|
assert(memcmp(rbuf, &before[off], size) == 0);
|
|
} else {
|
|
assert(memcmp(rbuf, &after[off], size) == 0);
|
|
}
|
|
}
|
|
}
|
|
lfsr_file_close(&lfs, &writer) => 0;
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_close(&lfs, &readers[r]) => 0;
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, after, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# Test multiple writers
|
|
[cases.test_fsync_wwww]
|
|
defines.W = 4
|
|
# 0 => no sync, readers not updated
|
|
# 1 => sync via lfsr_file_sync
|
|
# 2 => sync via LFS_O_SYNC
|
|
defines.SYNC = [0, 1, 2]
|
|
# 0 => no flush
|
|
# 1 => flush via lfsr_file_flush
|
|
# 2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.SIZE = [
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t after[SIZE];
|
|
memcpy(after, before, SIZE);
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// write W handles in parallel
|
|
lfsr_file_t writers[W];
|
|
for (lfs_size_t w = 0; w < W; w++) {
|
|
lfsr_file_open(&lfs, &writers[w], "jello",
|
|
LFS_O_WRONLY
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
for (lfs_size_t w = 0; w < W; w++) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &writers[w], wbuf, CHUNK) => CHUNK;
|
|
if (SYNC == 0) {
|
|
if (w == 0) {
|
|
memcpy(&after[i], wbuf, CHUNK);
|
|
}
|
|
} else {
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &writers[w]) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &writers[w]) => 0;
|
|
}
|
|
memcpy(&after[i], wbuf, CHUNK);
|
|
}
|
|
}
|
|
}
|
|
for (lfs_size_t w = 0; w < W; w++) {
|
|
lfsr_file_close(&lfs, &writers[w]) => 0;
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, after, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_wwww_fuzz]
|
|
defines.W = 4
|
|
# 0 => no sync, readers not updated
|
|
# 1 => sync via lfsr_file_sync
|
|
# 2 => sync via LFS_O_SYNC
|
|
defines.SYNC = [0, 1, 2]
|
|
# 0 => no flush
|
|
# 1 => flush via lfsr_file_flush
|
|
# 2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.SEED = 'range(10)'
|
|
defines.N = 20
|
|
defines.SIZE = [
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t after[SIZE];
|
|
memcpy(after, before, SIZE);
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// write W files in parallel
|
|
lfsr_file_t writers[W];
|
|
for (lfs_size_t w = 0; w < W; w++) {
|
|
lfsr_file_open(&lfs, &writers[w], "jello",
|
|
LFS_O_WRONLY
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
for (lfs_size_t w = 0; w < W; w++) {
|
|
// choose a random offset
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
lfs_size_t size = lfs_min32(CHUNK, SIZE - off);
|
|
lfsr_file_seek(&lfs, &writers[w], off, LFS_SEEK_SET) => off;
|
|
|
|
// write
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < size; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &writers[w], wbuf, size) => size;
|
|
if (SYNC == 0) {
|
|
if (w == 0) {
|
|
memcpy(&after[off], wbuf, size);
|
|
}
|
|
} else {
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &writers[w]) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &writers[w]) => 0;
|
|
}
|
|
memcpy(&after[off], wbuf, size);
|
|
}
|
|
}
|
|
}
|
|
for (lfs_size_t w = 0; w < W; w++) {
|
|
lfsr_file_close(&lfs, &writers[w]) => 0;
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, after, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# Test multiple writers and multiple readers
|
|
[cases.test_fsync_wwrr]
|
|
defines.W = 4
|
|
defines.R = 4
|
|
# 0 => no sync, readers not updated
|
|
# 1 => sync via lfsr_file_sync
|
|
# 2 => sync via LFS_O_SYNC
|
|
defines.SYNC = [0, 1, 2]
|
|
# 0 => no flush
|
|
# 1 => flush via lfsr_file_flush
|
|
# 2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.SIZE = [
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t after[SIZE];
|
|
memcpy(after, before, SIZE);
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// write W handles, R handles in parallel
|
|
lfsr_file_t writers[W];
|
|
lfsr_file_t readers[R];
|
|
for (lfs_size_t w = 0; w < W; w++) {
|
|
lfsr_file_open(&lfs, &writers[w], "jello",
|
|
LFS_O_WRONLY
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
}
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_open(&lfs, &readers[r], "jello", LFS_O_RDONLY) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
for (lfs_size_t w = 0; w < W; w++) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &writers[w], wbuf, CHUNK) => CHUNK;
|
|
if (SYNC == 0) {
|
|
if (w == 0) {
|
|
memcpy(&after[i], wbuf, CHUNK);
|
|
}
|
|
} else {
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &writers[w]) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &writers[w]) => 0;
|
|
}
|
|
memcpy(&after[i], wbuf, CHUNK);
|
|
}
|
|
}
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &readers[r], rbuf, CHUNK) => CHUNK;
|
|
if (SYNC == 0) {
|
|
assert(memcmp(rbuf, &before[i], CHUNK) == 0);
|
|
} else {
|
|
assert(memcmp(rbuf, &after[i], CHUNK) == 0);
|
|
}
|
|
}
|
|
}
|
|
for (lfs_size_t w = 0; w < W; w++) {
|
|
lfsr_file_close(&lfs, &writers[w]) => 0;
|
|
}
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_close(&lfs, &readers[r]) => 0;
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, after, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_wwrr_fuzz]
|
|
defines.W = 4
|
|
defines.R = 4
|
|
# 0 => no sync, readers not updated
|
|
# 1 => sync via lfsr_file_sync
|
|
# 2 => sync via LFS_O_SYNC
|
|
defines.SYNC = [0, 1, 2]
|
|
# 0 => no flush
|
|
# 1 => flush via lfsr_file_flush
|
|
# 2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.SEED = 'range(10)'
|
|
defines.N = 20
|
|
defines.SIZE = [
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t after[SIZE];
|
|
memcpy(after, before, SIZE);
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// write W files in parallel
|
|
lfsr_file_t writers[W];
|
|
lfsr_file_t readers[R];
|
|
for (lfs_size_t w = 0; w < W; w++) {
|
|
lfsr_file_open(&lfs, &writers[w], "jello",
|
|
LFS_O_WRONLY
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
}
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_open(&lfs, &readers[r], "jello", LFS_O_RDONLY) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
for (lfs_size_t w = 0; w < W; w++) {
|
|
// choose a random offset
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
lfs_size_t size = lfs_min32(CHUNK, SIZE - off);
|
|
lfsr_file_seek(&lfs, &writers[w], off, LFS_SEEK_SET) => off;
|
|
|
|
// write
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < size; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &writers[w], wbuf, size) => size;
|
|
if (SYNC == 0) {
|
|
if (w == 0) {
|
|
memcpy(&after[off], wbuf, size);
|
|
}
|
|
} else {
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &writers[w]) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &writers[w]) => 0;
|
|
}
|
|
memcpy(&after[off], wbuf, size);
|
|
}
|
|
}
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
// choose a random offset
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
lfs_size_t size = lfs_min32(CHUNK, SIZE - off);
|
|
lfsr_file_seek(&lfs, &readers[r], off, LFS_SEEK_SET) => off;
|
|
|
|
// read
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &readers[r], rbuf, CHUNK) => size;
|
|
if (SYNC == 0) {
|
|
assert(memcmp(rbuf, &before[off], size) == 0);
|
|
} else {
|
|
assert(memcmp(rbuf, &after[off], size) == 0);
|
|
}
|
|
}
|
|
}
|
|
for (lfs_size_t w = 0; w < W; w++) {
|
|
lfsr_file_close(&lfs, &writers[w]) => 0;
|
|
}
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_close(&lfs, &readers[r]) => 0;
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, after, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# Test multiple rd/wrers
|
|
[cases.test_fsync_rwrw]
|
|
defines.RW = 4
|
|
# 0 => no sync, readers not updated
|
|
# 1 => sync via lfsr_file_sync
|
|
# 2 => sync via LFS_O_SYNC
|
|
defines.SYNC = [0, 1, 2]
|
|
# 0 => no flush
|
|
# 1 => flush via lfsr_file_flush
|
|
# 2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.SIZE = [
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t between[RW][SIZE];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
memcpy(between[rw], before, SIZE);
|
|
}
|
|
uint8_t after[SIZE];
|
|
memcpy(after, before, SIZE);
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// write RW rdwrs in parallel
|
|
lfsr_file_t rdwrs[RW];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
lfsr_file_open(&lfs, &rdwrs[rw], "jello",
|
|
LFS_O_RDWR
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], i, LFS_SEEK_SET) => i;
|
|
lfsr_file_write(&lfs, &rdwrs[rw], wbuf, CHUNK) => CHUNK;
|
|
memcpy(&between[rw][i], wbuf, CHUNK);
|
|
if (SYNC == 0) {
|
|
if (rw == 0) {
|
|
memcpy(&after[i], wbuf, CHUNK);
|
|
}
|
|
} else {
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
memcpy(&after[i], wbuf, CHUNK);
|
|
}
|
|
}
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], i, LFS_SEEK_SET) => i;
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &rdwrs[rw], rbuf, CHUNK) => CHUNK;
|
|
if (SYNC == 0) {
|
|
assert(memcmp(rbuf, &between[rw][i], CHUNK) == 0);
|
|
} else {
|
|
assert(memcmp(rbuf, &after[i], CHUNK) == 0);
|
|
}
|
|
}
|
|
}
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
lfsr_file_close(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, after, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_rwrw_fuzz]
|
|
defines.RW = 4
|
|
# 0 => no sync, readers not updated
|
|
# 1 => sync via lfsr_file_sync
|
|
# 2 => sync via LFS_O_SYNC
|
|
defines.SYNC = [0, 1, 2]
|
|
# 0 => no flush
|
|
# 1 => flush via lfsr_file_flush
|
|
# 2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.SEED = 'range(10)'
|
|
defines.N = 20
|
|
defines.SIZE = [
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t between[RW][SIZE];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
memcpy(between[rw], before, SIZE);
|
|
}
|
|
uint8_t after[SIZE];
|
|
memcpy(after, before, SIZE);
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// write RW rdwrs in parallel
|
|
lfsr_file_t rdwrs[RW];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
lfsr_file_open(&lfs, &rdwrs[rw], "jello",
|
|
LFS_O_RDWR
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// choose a random offset
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
lfs_size_t size = lfs_min32(CHUNK, SIZE - off);
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off;
|
|
|
|
// write
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < size; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &rdwrs[rw], wbuf, size) => size;
|
|
memcpy(&between[rw][off], wbuf, size);
|
|
if (SYNC == 0) {
|
|
if (rw == 0) {
|
|
memcpy(&after[off], wbuf, size);
|
|
}
|
|
} else {
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
memcpy(&after[off], wbuf, size);
|
|
}
|
|
}
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// choose a random offset
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
lfs_size_t size = lfs_min32(CHUNK, SIZE - off);
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off;
|
|
|
|
// read
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &rdwrs[rw], rbuf, CHUNK) => size;
|
|
if (SYNC == 0) {
|
|
assert(memcmp(rbuf, &between[rw][off], size) == 0);
|
|
} else {
|
|
assert(memcmp(rbuf, &after[off], size) == 0);
|
|
}
|
|
}
|
|
}
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
lfsr_file_close(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, after, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# Test multiple rw files without fixed size
|
|
[cases.test_fsync_rwrw_sparse_fuzz]
|
|
defines.RW = 4
|
|
# 0 => no sync, readers not updated
|
|
# 1 => sync via lfsr_file_sync
|
|
# 2 => sync via LFS_O_SYNC
|
|
defines.SYNC = [0, 1, 2]
|
|
# 0 => no flush
|
|
# 1 => flush via lfsr_file_flush
|
|
# 2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.SEED = 'range(10)'
|
|
defines.N = 40
|
|
defines.SIZE = [
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
uint8_t between[RW][SIZE];
|
|
lfs_size_t between_size[RW];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
between_size[rw] = 0;
|
|
}
|
|
uint8_t after[SIZE];
|
|
lfs_size_t after_size = 0;
|
|
|
|
// write RW rdwrs in parallel
|
|
lfsr_file_t rdwrs[RW];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// open files
|
|
lfsr_file_open(&lfs, &rdwrs[rw], "jello",
|
|
LFS_O_RDWR
|
|
| LFS_O_CREAT
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// choose a random operation
|
|
uint8_t op = TEST_PRNG(&prng) % 2;
|
|
|
|
// writing?
|
|
if (op == 0) {
|
|
// choose a random offset
|
|
lfs_off_t off = (between_size[rw] > 0)
|
|
? TEST_PRNG(&prng) % between_size[rw]
|
|
: 0;
|
|
lfs_size_t size = lfs_min32(CHUNK, SIZE - off);
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < size; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
// write
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off;
|
|
lfsr_file_write(&lfs, &rdwrs[rw], wbuf, size) => size;
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
|
|
// update sim
|
|
if (off > between_size[rw]) {
|
|
memset(&between[rw][between_size[rw]],
|
|
0,
|
|
off - between_size[rw]);
|
|
}
|
|
memcpy(&between[rw][off], wbuf, size);
|
|
between_size[rw] = lfs_max32(off + size, between_size[rw]);
|
|
|
|
// reading?
|
|
} else if (op == 1) {
|
|
// choose a random offset
|
|
lfs_off_t off = (between_size[rw] > 0)
|
|
? TEST_PRNG(&prng) % between_size[rw]
|
|
: 0;
|
|
lfs_size_t size = lfs_min32(CHUNK, between_size[rw] - off);
|
|
|
|
// read
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off;
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &rdwrs[rw], rbuf, CHUNK) => size;
|
|
assert(memcmp(rbuf, &between[rw][off], size) == 0);
|
|
}
|
|
|
|
// broadcast sim?
|
|
if (SYNC) {
|
|
memcpy(after, between[rw], SIZE);
|
|
after_size = between_size[rw];
|
|
|
|
for (lfs_size_t rw_ = 0; rw_ < RW; rw_++) {
|
|
memcpy(between[rw_], between[rw], SIZE);
|
|
between_size[rw_] = between_size[rw];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// close files
|
|
lfsr_file_close(&lfs, &rdwrs[rw]) => 0;
|
|
|
|
// broadcast sim one last time?
|
|
memcpy(after, between[rw], SIZE);
|
|
after_size = between_size[rw];
|
|
|
|
for (lfs_size_t rw_ = 0; rw_ < RW; rw_++) {
|
|
memcpy(between[rw_], between[rw], SIZE);
|
|
between_size[rw_] = between_size[rw];
|
|
}
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => after_size;
|
|
assert(memcmp(rbuf, after, after_size) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# Test multiple rw files while also truncating/fruncating
|
|
[cases.test_fsync_rwtfrwtf_sparse_fuzz]
|
|
defines.RW = 4
|
|
# 0 => no sync, readers not updated
|
|
# 1 => sync via lfsr_file_sync
|
|
# 2 => sync via LFS_O_SYNC
|
|
defines.SYNC = [0, 1, 2]
|
|
# 0 => no flush
|
|
# 1 => flush via lfsr_file_flush
|
|
# 2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.SEED = 'range(10)'
|
|
defines.N = 40
|
|
defines.SIZE = [
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
uint8_t between[RW][SIZE];
|
|
lfs_size_t between_size[RW];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
between_size[rw] = 0;
|
|
}
|
|
uint8_t after[SIZE];
|
|
lfs_size_t after_size = 0;
|
|
|
|
// write RW rdwrs in parallel
|
|
lfsr_file_t rdwrs[RW];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// open files
|
|
lfsr_file_open(&lfs, &rdwrs[rw], "jello",
|
|
LFS_O_RDWR
|
|
| LFS_O_CREAT
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// choose a random operation
|
|
uint8_t op = TEST_PRNG(&prng) % 4;
|
|
|
|
// writing?
|
|
if (op == 0) {
|
|
// choose a random offset
|
|
lfs_off_t off = (between_size[rw] > 0)
|
|
? TEST_PRNG(&prng) % between_size[rw]
|
|
: 0;
|
|
lfs_size_t size = lfs_min32(CHUNK, SIZE - off);
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < size; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
// write
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off;
|
|
lfsr_file_write(&lfs, &rdwrs[rw], wbuf, size) => size;
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
|
|
// update sim
|
|
if (off > between_size[rw]) {
|
|
memset(&between[rw][between_size[rw]],
|
|
0,
|
|
off - between_size[rw]);
|
|
}
|
|
memcpy(&between[rw][off], wbuf, size);
|
|
between_size[rw] = lfs_max32(off + size, between_size[rw]);
|
|
|
|
// reading?
|
|
} else if (op == 1) {
|
|
// choose a random offset
|
|
lfs_off_t off = (between_size[rw] > 0)
|
|
? TEST_PRNG(&prng) % between_size[rw]
|
|
: 0;
|
|
lfs_size_t size = lfs_min32(CHUNK, between_size[rw] - off);
|
|
|
|
// read
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off;
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &rdwrs[rw], rbuf, CHUNK) => size;
|
|
assert(memcmp(rbuf, &between[rw][off], size) == 0);
|
|
|
|
// truncating?
|
|
} else if (op == 2) {
|
|
// choose a random new file size
|
|
lfs_off_t size = TEST_PRNG(&prng) % SIZE;
|
|
|
|
// truncate
|
|
lfsr_file_truncate(&lfs, &rdwrs[rw], size) => 0;
|
|
if (FLUSH == 1) { // (flush does nothing)
|
|
lfsr_file_flush(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
|
|
// update the sim
|
|
if (size > between_size[rw]) {
|
|
memset(&between[rw][between_size[rw]],
|
|
0,
|
|
size - between_size[rw]);
|
|
}
|
|
between_size[rw] = size;
|
|
|
|
// fruncating?
|
|
} else if (op == 3) {
|
|
// choose a random new file size
|
|
lfs_off_t size = TEST_PRNG(&prng) % SIZE;
|
|
|
|
// fruncate
|
|
lfsr_file_fruncate(&lfs, &rdwrs[rw], size) => 0;
|
|
if (FLUSH == 1) { // (flush does nothing)
|
|
lfsr_file_flush(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
|
|
// update the sim
|
|
if (size > between_size[rw]) {
|
|
memmove(&between[rw][size - between_size[rw]],
|
|
between[rw],
|
|
between_size[rw]);
|
|
memset(between[rw],
|
|
0,
|
|
size - between_size[rw]);
|
|
} else {
|
|
memmove(between[rw],
|
|
&between[rw][between_size[rw] - size],
|
|
size);
|
|
}
|
|
between_size[rw] = size;
|
|
}
|
|
|
|
// broadcast sim?
|
|
if (SYNC) {
|
|
memcpy(after, between[rw], SIZE);
|
|
after_size = between_size[rw];
|
|
|
|
for (lfs_size_t rw_ = 0; rw_ < RW; rw_++) {
|
|
memcpy(between[rw_], between[rw], SIZE);
|
|
between_size[rw_] = between_size[rw];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// close files
|
|
lfsr_file_close(&lfs, &rdwrs[rw]) => 0;
|
|
|
|
// broadcast sim one last time?
|
|
memcpy(after, between[rw], SIZE);
|
|
after_size = between_size[rw];
|
|
|
|
for (lfs_size_t rw_ = 0; rw_ < RW; rw_++) {
|
|
memcpy(between[rw_], between[rw], SIZE);
|
|
between_size[rw_] = between_size[rw];
|
|
}
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => after_size;
|
|
assert(memcmp(rbuf, after, after_size) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
|
|
# Desynced files make things interesting
|
|
|
|
# Some specific tests
|
|
[cases.test_fsync_desync_wdrr]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// a - writer
|
|
// b - reader kept open, recvs updates from a
|
|
// c - reader kept closed, checks disk state
|
|
lfsr_file_t a;
|
|
lfsr_file_t b;
|
|
lfsr_file_t c;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_open(&lfs, &a, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_open(&lfs, &b, "jello", LFS_O_RDONLY) => 0;
|
|
|
|
// write to a and sync
|
|
lfsr_file_write(&lfs, &a, "hello!", strlen("hello!"))
|
|
=> strlen("hello!");
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// our write should show up in b
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
|
|
// now mark as desync, rewrite, and close
|
|
lfsr_file_desync(&lfs, &a) => 0;
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "bonjour!", strlen("bonjour!"))
|
|
=> strlen("bonjour!");
|
|
lfsr_file_close(&lfs, &a) => 0;
|
|
|
|
// b should still have previous contents
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
|
|
// reopen a, mark as desync, rewrite a, and sync
|
|
lfsr_file_open(&lfs, &a, "jello", LFS_O_WRONLY) => 0;
|
|
lfsr_file_desync(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "ohayo!", strlen("ohayo!"))
|
|
=> strlen("ohayo!");
|
|
// this sync should clear the desync
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// now our write should show up in b
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("ohayo!");
|
|
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!");
|
|
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
|
|
// rewrite a, close, desync flag should have been cleared
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "zdrasti!", strlen("zdrasti!"))
|
|
=> strlen("zdrasti!");
|
|
lfsr_file_close(&lfs, &a) => 0;
|
|
|
|
// our write should show up in b
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
|
|
lfsr_file_close(&lfs, &b) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_desync_wrrd]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// a - writer
|
|
// b - reader kept open, recvs updates from a
|
|
// c - desynced reader
|
|
lfsr_file_t a;
|
|
lfsr_file_t b;
|
|
lfsr_file_t c;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_open(&lfs, &a, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_open(&lfs, &b, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY | LFS_O_DESYNC) => 0;
|
|
|
|
// write to a and sync
|
|
lfsr_file_write(&lfs, &a, "hello!", strlen("hello!"))
|
|
=> strlen("hello!");
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// our write should show up in b
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// but not in c
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => 0;
|
|
// reopen c, should now be up to date
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY | LFS_O_DESYNC) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
|
|
// rewrite and sync
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "bonjour!", strlen("bonjour!"))
|
|
=> strlen("bonjour!");
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// our write should show up in b
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
// but not in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// reopen c, should now be up to date
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY | LFS_O_DESYNC) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
|
|
// rewrite and close
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "ohayo!", strlen("ohayo!"))
|
|
=> strlen("ohayo!");
|
|
lfsr_file_close(&lfs, &a) => 0;
|
|
|
|
// our write should show up in b
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
|
|
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
|
|
// but not in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
// reopen c, should now be up to date
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY | LFS_O_DESYNC) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
|
|
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
|
|
|
|
lfsr_file_close(&lfs, &b) => 0;
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_desync_wdwdrr]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// a - writer
|
|
// b - writer
|
|
// c - reader kept open, recvs updates from a
|
|
// d - reader kept closed, checks disk state
|
|
lfsr_file_t a;
|
|
lfsr_file_t b;
|
|
lfsr_file_t c;
|
|
lfsr_file_t d;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_open(&lfs, &a, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_open(&lfs, &b, "jello", LFS_O_WRONLY) => 0;
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
|
|
// write to a and sync
|
|
lfsr_file_write(&lfs, &a, "hello!", strlen("hello!"))
|
|
=> strlen("hello!");
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// our write should show up in c
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// mark b as desync, rewrite, and close
|
|
lfsr_file_desync(&lfs, &b) => 0;
|
|
lfsr_file_write(&lfs, &b, "bonjour!", strlen("bonjour!"))
|
|
=> strlen("bonjour!");
|
|
lfsr_file_close(&lfs, &b) => 0;
|
|
|
|
// c should still have previous contents
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// reopen b, mark as desync, rewrite
|
|
lfsr_file_open(&lfs, &b, "jello", LFS_O_WRONLY) => 0;
|
|
lfsr_file_desync(&lfs, &b) => 0;
|
|
lfsr_file_write(&lfs, &b, "ohayo!", strlen("ohayo!"))
|
|
=> strlen("ohayo!");
|
|
// rewrite a, sync
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "zdrasti!", strlen("zdrasti!"))
|
|
=> strlen("zdrasti!");
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// a should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync b
|
|
lfsr_file_sync(&lfs, &b) => 0;
|
|
|
|
// b should show up in c, without a's changes
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!");
|
|
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!");
|
|
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// rewrite b, close, desync flag should have been cleared
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_write(&lfs, &b, "annyeong!", strlen("annyeong!"))
|
|
=> strlen("annyeong!");
|
|
lfsr_file_close(&lfs, &b) => 0;
|
|
|
|
// our write should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("annyeong!");
|
|
assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("annyeong!");
|
|
assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// rewrite a, close
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "czesc!", strlen("czesc!"))
|
|
=> strlen("czesc!");
|
|
lfsr_file_close(&lfs, &a) => 0;
|
|
|
|
// our write should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("czesc!ng!");
|
|
assert(memcmp(rbuf, "czesc!ng!", strlen("czesc!ng!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("czesc!ng!");
|
|
assert(memcmp(rbuf, "czesc!ng!", strlen("czesc!ng!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_desync_wdwdrr_noop]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// a - writer
|
|
// b - writer
|
|
// c - reader kept open, recvs updates from a
|
|
// d - reader kept closed, checks disk state
|
|
lfsr_file_t a;
|
|
lfsr_file_t b;
|
|
lfsr_file_t c;
|
|
lfsr_file_t d;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_open(&lfs, &a, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_open(&lfs, &b, "jello", LFS_O_WRONLY) => 0;
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
|
|
// write to a and sync
|
|
lfsr_file_write(&lfs, &a, "hello!", strlen("hello!"))
|
|
=> strlen("hello!");
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// our write should show up in c
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// mark b as desync, this should freeze its contents
|
|
lfsr_file_desync(&lfs, &b) => 0;
|
|
// rewrite a, sync
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "bonjour!", strlen("bonjour!"))
|
|
=> strlen("bonjour!");
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// a should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync b, this may be tricky since we haven't touched b
|
|
lfsr_file_sync(&lfs, &b) => 0;
|
|
|
|
// b should show up in c, without a's changes
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// rewrite b, close, desync flag should have been cleared
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_write(&lfs, &b, "ohayo!", strlen("ohayo!"))
|
|
=> strlen("ohayo!");
|
|
lfsr_file_close(&lfs, &b) => 0;
|
|
|
|
// our write should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!");
|
|
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!");
|
|
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// rewrite a, close
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "zdrasti!", strlen("zdrasti!"))
|
|
=> strlen("zdrasti!");
|
|
lfsr_file_close(&lfs, &a) => 0;
|
|
|
|
// our write should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_desync_wdwdrr_append]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// a - writer
|
|
// b - writer
|
|
// c - reader kept open, recvs updates from a
|
|
// d - reader kept closed, checks disk state
|
|
lfsr_file_t a;
|
|
lfsr_file_t b;
|
|
lfsr_file_t c;
|
|
lfsr_file_t d;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_open(&lfs, &a, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_APPEND) => 0;
|
|
lfsr_file_open(&lfs, &b, "jello", LFS_O_WRONLY | LFS_O_APPEND) => 0;
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
|
|
// write to a and sync
|
|
lfsr_file_write(&lfs, &a, "hello!", strlen("hello!"))
|
|
=> strlen("hello!");
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// our write should show up in c
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// mark b as desync, write, and close
|
|
lfsr_file_desync(&lfs, &b) => 0;
|
|
lfsr_file_write(&lfs, &b, "bonjour!", strlen("bonjour!"))
|
|
=> strlen("bonjour!");
|
|
lfsr_file_close(&lfs, &b) => 0;
|
|
|
|
// c should still have previous contents
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// reopen b, mark as desync, write
|
|
lfsr_file_open(&lfs, &b, "jello", LFS_O_WRONLY | LFS_O_APPEND) => 0;
|
|
lfsr_file_desync(&lfs, &b) => 0;
|
|
lfsr_file_write(&lfs, &b, "ohayo!", strlen("ohayo!"))
|
|
=> strlen("ohayo!");
|
|
// write a, sync
|
|
lfsr_file_write(&lfs, &a, "zdrasti!", strlen("zdrasti!"))
|
|
=> strlen("zdrasti!");
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// a should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!zdrasti!");
|
|
assert(memcmp(rbuf, "hello!zdrasti!", strlen("hello!zdrasti!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!zdrasti!");
|
|
assert(memcmp(rbuf, "hello!zdrasti!", strlen("hello!zdrasti!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync b
|
|
lfsr_file_sync(&lfs, &b) => 0;
|
|
|
|
// b should show up in c, without a's changes
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!");
|
|
assert(memcmp(rbuf, "hello!ohayo!", strlen("hello!ohayo!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!");
|
|
assert(memcmp(rbuf, "hello!ohayo!", strlen("hello!ohayo!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// write b, close, desync flag should have been cleared
|
|
lfsr_file_write(&lfs, &b, "annyeong!", strlen("annyeong!"))
|
|
=> strlen("annyeong!");
|
|
lfsr_file_close(&lfs, &b) => 0;
|
|
|
|
// our write should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf))
|
|
=> strlen("hello!ohayo!annyeong!");
|
|
assert(memcmp(rbuf, "hello!ohayo!annyeong!",
|
|
strlen("hello!ohayo!annyeong!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf))
|
|
=> strlen("hello!ohayo!annyeong!");
|
|
assert(memcmp(rbuf, "hello!ohayo!annyeong!",
|
|
strlen("hello!ohayo!annyeong!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// write a, close
|
|
lfsr_file_write(&lfs, &a, "czesc!", strlen("czesc!"))
|
|
=> strlen("czesc!");
|
|
lfsr_file_close(&lfs, &a) => 0;
|
|
|
|
// our write should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf))
|
|
=> strlen("hello!ohayo!annyeong!czesc!");
|
|
assert(memcmp(rbuf, "hello!ohayo!annyeong!czesc!",
|
|
strlen("hello!ohayo!annyeong!czesc!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf))
|
|
=> strlen("hello!ohayo!annyeong!czesc!");
|
|
assert(memcmp(rbuf, "hello!ohayo!annyeong!czesc!",
|
|
strlen("hello!ohayo!annyeong!czesc!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
|
|
# Test one desynced writer, multiple readers
|
|
[cases.test_fsync_drrr]
|
|
defines.R = 4
|
|
# 0 => no sync, readers not updated
|
|
# 1 => sync via lfsr_file_sync
|
|
# 2 => sync via LFS_O_SYNC
|
|
defines.SYNC = [0, 1, 2]
|
|
# 0 => no flush
|
|
# 1 => flush via lfsr_file_flush
|
|
# 2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.SIZE = [
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t after[SIZE];
|
|
memcpy(after, before, SIZE);
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// write 1 handle, read R handles in parallel
|
|
lfsr_file_t writer;
|
|
lfsr_file_t readers[R];
|
|
lfsr_file_open(&lfs, &writer, "jello",
|
|
LFS_O_WRONLY
|
|
| LFS_O_DESYNC
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_open(&lfs, &readers[r], "jello", LFS_O_RDONLY) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &writer, wbuf, CHUNK) => CHUNK;
|
|
memcpy(&after[i], wbuf, CHUNK);
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &writer) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &writer) => 0;
|
|
}
|
|
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &readers[r], rbuf, CHUNK) => CHUNK;
|
|
if (SYNC == 0) {
|
|
assert(memcmp(rbuf, &before[i], CHUNK) == 0);
|
|
} else {
|
|
assert(memcmp(rbuf, &after[i], CHUNK) == 0);
|
|
}
|
|
}
|
|
}
|
|
lfsr_file_close(&lfs, &writer) => 0;
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_close(&lfs, &readers[r]) => 0;
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
if (SYNC == 0) {
|
|
assert(memcmp(rbuf, before, SIZE) == 0);
|
|
} else {
|
|
assert(memcmp(rbuf, after, SIZE) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_drrr_fuzz]
|
|
defines.R = 4
|
|
# 0 => no sync, readers not updated
|
|
# 1 => sync via lfsr_file_sync
|
|
# 2 => sync via LFS_O_SYNC
|
|
defines.SYNC = [0, 1, 2]
|
|
# 0 => no flush
|
|
# 1 => flush via lfsr_file_flush
|
|
# 2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.SEED = 'range(10)'
|
|
defines.N = 20
|
|
defines.SIZE = [
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t after[SIZE];
|
|
memcpy(after, before, SIZE);
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// write 1 handle, read R handles in parallel
|
|
lfsr_file_t writer;
|
|
lfsr_file_t readers[R];
|
|
lfsr_file_open(&lfs, &writer, "jello",
|
|
LFS_O_WRONLY
|
|
| LFS_O_DESYNC
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_open(&lfs, &readers[r], "jello", LFS_O_RDONLY) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// choose a random offset
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
lfs_size_t size = lfs_min32(CHUNK, SIZE - off);
|
|
lfsr_file_seek(&lfs, &writer, off, LFS_SEEK_SET) => off;
|
|
|
|
// write
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < size; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &writer, wbuf, size) => size;
|
|
memcpy(&after[off], wbuf, size);
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &writer) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &writer) => 0;
|
|
}
|
|
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
// choose a random offset
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
lfs_size_t size = lfs_min32(CHUNK, SIZE - off);
|
|
lfsr_file_seek(&lfs, &readers[r], off, LFS_SEEK_SET) => off;
|
|
|
|
// read
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &readers[r], rbuf, CHUNK) => size;
|
|
if (SYNC == 0) {
|
|
assert(memcmp(rbuf, &before[off], size) == 0);
|
|
} else {
|
|
assert(memcmp(rbuf, &after[off], size) == 0);
|
|
}
|
|
}
|
|
}
|
|
lfsr_file_close(&lfs, &writer) => 0;
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_close(&lfs, &readers[r]) => 0;
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
if (SYNC == 0) {
|
|
assert(memcmp(rbuf, before, SIZE) == 0);
|
|
} else {
|
|
assert(memcmp(rbuf, after, SIZE) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# Test one writer, multiple desynced readers
|
|
[cases.test_fsync_wddd]
|
|
defines.R = 4
|
|
# 0 => no sync, readers not updated
|
|
# 1 => sync via lfsr_file_sync
|
|
# 2 => sync via LFS_O_SYNC
|
|
defines.SYNC = [0, 1, 2]
|
|
# 0 => no flush
|
|
# 1 => flush via lfsr_file_flush
|
|
# 2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.SIZE = [
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t after[SIZE];
|
|
memcpy(after, before, SIZE);
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// write 1 handle, read R handles in parallel
|
|
lfsr_file_t writer;
|
|
lfsr_file_t readers[R];
|
|
lfsr_file_open(&lfs, &writer, "jello",
|
|
LFS_O_WRONLY
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_open(&lfs, &readers[r], "jello",
|
|
LFS_O_RDONLY | LFS_O_DESYNC) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &writer, wbuf, CHUNK) => CHUNK;
|
|
memcpy(&after[i], wbuf, CHUNK);
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &writer) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &writer) => 0;
|
|
}
|
|
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &readers[r], rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(rbuf, &before[i], CHUNK) == 0);
|
|
}
|
|
}
|
|
lfsr_file_close(&lfs, &writer) => 0;
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_close(&lfs, &readers[r]) => 0;
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, after, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_wddd_fuzz]
|
|
defines.R = 4
|
|
# 0 => no sync, readers not updated
|
|
# 1 => sync via lfsr_file_sync
|
|
# 2 => sync via LFS_O_SYNC
|
|
defines.SYNC = [0, 1, 2]
|
|
# 0 => no flush
|
|
# 1 => flush via lfsr_file_flush
|
|
# 2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.SEED = 'range(10)'
|
|
defines.N = 20
|
|
defines.SIZE = [
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t after[SIZE];
|
|
memcpy(after, before, SIZE);
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// write 1 handle, read R handles in parallel
|
|
lfsr_file_t writer;
|
|
lfsr_file_t readers[R];
|
|
lfsr_file_open(&lfs, &writer, "jello",
|
|
LFS_O_WRONLY
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_open(&lfs, &readers[r], "jello",
|
|
LFS_O_RDONLY | LFS_O_DESYNC) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// choose a random offset
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
lfs_size_t size = lfs_min32(CHUNK, SIZE - off);
|
|
lfsr_file_seek(&lfs, &writer, off, LFS_SEEK_SET) => off;
|
|
|
|
// write
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < size; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &writer, wbuf, size) => size;
|
|
memcpy(&after[off], wbuf, size);
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &writer) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &writer) => 0;
|
|
}
|
|
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
// choose a random offset
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
lfs_size_t size = lfs_min32(CHUNK, SIZE - off);
|
|
lfsr_file_seek(&lfs, &readers[r], off, LFS_SEEK_SET) => off;
|
|
|
|
// read
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &readers[r], rbuf, CHUNK) => size;
|
|
assert(memcmp(rbuf, &before[off], size) == 0);
|
|
}
|
|
}
|
|
lfsr_file_close(&lfs, &writer) => 0;
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_close(&lfs, &readers[r]) => 0;
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, after, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# Test multiple desynced rd/wrers
|
|
[cases.test_fsync_rwdrwd]
|
|
defines.RW = 4
|
|
# 0 => no sync, readers not updated
|
|
# 1 => sync via lfsr_file_sync
|
|
# 2 => sync via LFS_O_SYNC
|
|
defines.SYNC = [0, 1, 2]
|
|
# 0 => no flush
|
|
# 1 => flush via lfsr_file_flush
|
|
# 2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.SIZE = [
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t between[RW][SIZE];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
memcpy(between[rw], before, SIZE);
|
|
}
|
|
uint8_t after[SIZE];
|
|
memcpy(after, before, SIZE);
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// write RW rdwrs in parallel
|
|
lfsr_file_t rdwrs[RW];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
lfsr_file_open(&lfs, &rdwrs[rw], "jello",
|
|
LFS_O_RDWR
|
|
| LFS_O_DESYNC
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], i, LFS_SEEK_SET) => i;
|
|
lfsr_file_write(&lfs, &rdwrs[rw], wbuf, CHUNK) => CHUNK;
|
|
memcpy(&between[rw][i], wbuf, CHUNK);
|
|
if (SYNC == 0) {
|
|
if (rw == 0) {
|
|
memcpy(&after[i], wbuf, CHUNK);
|
|
}
|
|
} else {
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
if (i == 0) {
|
|
memcpy(after, between[rw], SIZE);
|
|
} else {
|
|
memcpy(&after[i], wbuf, CHUNK);
|
|
}
|
|
}
|
|
}
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], i, LFS_SEEK_SET) => i;
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &rdwrs[rw], rbuf, CHUNK) => CHUNK;
|
|
if (SYNC == 0) {
|
|
assert(memcmp(rbuf, &between[rw][i], CHUNK) == 0);
|
|
} else {
|
|
assert(memcmp(rbuf, &after[i], CHUNK) == 0);
|
|
}
|
|
}
|
|
}
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
lfsr_file_close(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
if (SYNC == 0) {
|
|
assert(memcmp(rbuf, before, SIZE) == 0);
|
|
} else {
|
|
assert(memcmp(rbuf, after, SIZE) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_rwdrwd_fuzz]
|
|
defines.RW = 4
|
|
# 0 => no sync, readers not updated
|
|
# 1 => sync via lfsr_file_sync
|
|
# 2 => sync via LFS_O_SYNC
|
|
defines.SYNC = [0, 1, 2]
|
|
# 0 => no flush
|
|
# 1 => flush via lfsr_file_flush
|
|
# 2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.SEED = 'range(10)'
|
|
defines.N = 20
|
|
defines.SIZE = [
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t between[RW][SIZE];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
memcpy(between[rw], before, SIZE);
|
|
}
|
|
uint8_t after[SIZE];
|
|
memcpy(after, before, SIZE);
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// write RW rdwrs in parallel
|
|
lfsr_file_t rdwrs[RW];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
lfsr_file_open(&lfs, &rdwrs[rw], "jello",
|
|
LFS_O_RDWR
|
|
| LFS_O_DESYNC
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// choose a random offset
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
lfs_size_t size = lfs_min32(CHUNK, SIZE - off);
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off;
|
|
|
|
// write
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < size; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &rdwrs[rw], wbuf, size) => size;
|
|
memcpy(&between[rw][off], wbuf, size);
|
|
if (SYNC == 0) {
|
|
if (rw == 0) {
|
|
memcpy(&after[off], wbuf, size);
|
|
}
|
|
} else {
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
if (i == 0) {
|
|
memcpy(after, between[rw], SIZE);
|
|
} else {
|
|
memcpy(&after[off], wbuf, size);
|
|
}
|
|
}
|
|
}
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// choose a random offset
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
lfs_size_t size = lfs_min32(CHUNK, SIZE - off);
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off;
|
|
|
|
// read
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &rdwrs[rw], rbuf, CHUNK) => size;
|
|
if (SYNC == 0) {
|
|
assert(memcmp(rbuf, &between[rw][off], size) == 0);
|
|
} else {
|
|
assert(memcmp(rbuf, &after[off], size) == 0);
|
|
}
|
|
}
|
|
}
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
lfsr_file_close(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
if (SYNC == 0) {
|
|
assert(memcmp(rbuf, before, SIZE) == 0);
|
|
} else {
|
|
assert(memcmp(rbuf, after, SIZE) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# Test multiple rwd files without fixed size
|
|
[cases.test_fsync_rwdrwd_sparse_fuzz]
|
|
defines.RW = 4
|
|
# 0 => no flush
|
|
# 1 => flush via lfsr_file_flush
|
|
# 2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.SEED = 'range(10)'
|
|
defines.N = 40
|
|
defines.SIZE = [
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
uint8_t between[RW][SIZE];
|
|
lfs_size_t between_size[RW];
|
|
bool between_desync[RW];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
between_size[rw] = 0;
|
|
between_desync[rw] = false;
|
|
}
|
|
uint8_t after[SIZE];
|
|
lfs_size_t after_size = 0;
|
|
|
|
// write RW rdwrs in parallel
|
|
lfsr_file_t rdwrs[RW];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// open files
|
|
lfsr_file_open(&lfs, &rdwrs[rw], "jello",
|
|
LFS_O_RDWR
|
|
| LFS_O_CREAT
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// choose a random operation
|
|
uint8_t op = TEST_PRNG(&prng) % 2;
|
|
// choose a random sync state
|
|
uint8_t sync = TEST_PRNG(&prng) % 3;
|
|
|
|
// writing?
|
|
if (op == 0) {
|
|
// choose a random offset
|
|
lfs_off_t off = (between_size[rw] > 0)
|
|
? TEST_PRNG(&prng) % between_size[rw]
|
|
: 0;
|
|
lfs_size_t size = lfs_min32(CHUNK, SIZE - off);
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < size; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
// write
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off;
|
|
lfsr_file_write(&lfs, &rdwrs[rw], wbuf, size) => size;
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
|
|
// update sim
|
|
if (off > between_size[rw]) {
|
|
memset(&between[rw][between_size[rw]],
|
|
0,
|
|
off - between_size[rw]);
|
|
}
|
|
memcpy(&between[rw][off], wbuf, size);
|
|
between_size[rw] = lfs_max32(off + size, between_size[rw]);
|
|
|
|
// reading?
|
|
} else if (op == 1) {
|
|
// choose a random offset
|
|
lfs_off_t off = (between_size[rw] > 0)
|
|
? TEST_PRNG(&prng) % between_size[rw]
|
|
: 0;
|
|
lfs_size_t size = lfs_min32(CHUNK, between_size[rw] - off);
|
|
|
|
// read
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off;
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &rdwrs[rw], rbuf, CHUNK) => size;
|
|
assert(memcmp(rbuf, &between[rw][off], size) == 0);
|
|
}
|
|
|
|
// desync?
|
|
if (sync == 0) {
|
|
lfsr_file_desync(&lfs, &rdwrs[rw]) => 0;
|
|
between_desync[rw] = true;
|
|
|
|
// sync?
|
|
} else if (sync == 1) {
|
|
lfsr_file_sync(&lfs, &rdwrs[rw]) => 0;
|
|
between_desync[rw] = false;
|
|
|
|
// otherwise no change
|
|
}
|
|
|
|
// broadcast sim?
|
|
if (sync == 1) {
|
|
memcpy(after, between[rw], SIZE);
|
|
after_size = between_size[rw];
|
|
|
|
for (lfs_size_t rw_ = 0; rw_ < RW; rw_++) {
|
|
if (!between_desync[rw_]) {
|
|
memcpy(between[rw_], between[rw], SIZE);
|
|
between_size[rw_] = between_size[rw];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// close files
|
|
lfsr_file_close(&lfs, &rdwrs[rw]) => 0;
|
|
|
|
// broadcast sim one last time?
|
|
if (!between_desync[rw]) {
|
|
memcpy(after, between[rw], SIZE);
|
|
after_size = between_size[rw];
|
|
|
|
for (lfs_size_t rw_ = 0; rw_ < RW; rw_++) {
|
|
if (!between_desync[rw_]) {
|
|
memcpy(between[rw_], between[rw], SIZE);
|
|
between_size[rw_] = between_size[rw];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => after_size;
|
|
assert(memcmp(rbuf, after, after_size) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# Test multiple rwd files while also truncating/fruncating
|
|
[cases.test_fsync_rwtfdrwtfd_sparse_fuzz]
|
|
defines.RW = 4
|
|
# 0 => no flush
|
|
# 1 => flush via lfsr_file_flush
|
|
# 2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.SEED = 'range(10)'
|
|
defines.N = 40
|
|
defines.SIZE = [
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
uint8_t between[RW][SIZE];
|
|
lfs_size_t between_size[RW];
|
|
bool between_desync[RW];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
between_size[rw] = 0;
|
|
between_desync[rw] = false;
|
|
}
|
|
uint8_t after[SIZE];
|
|
lfs_size_t after_size = 0;
|
|
|
|
// write RW rdwrs in parallel
|
|
lfsr_file_t rdwrs[RW];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// open files
|
|
lfsr_file_open(&lfs, &rdwrs[rw], "jello",
|
|
LFS_O_RDWR
|
|
| LFS_O_CREAT
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// choose a random operation
|
|
uint8_t op = TEST_PRNG(&prng) % 4;
|
|
// choose a random sync state
|
|
uint8_t sync = TEST_PRNG(&prng) % 3;
|
|
|
|
// writing?
|
|
if (op == 0) {
|
|
// choose a random offset
|
|
lfs_off_t off = (between_size[rw] > 0)
|
|
? TEST_PRNG(&prng) % between_size[rw]
|
|
: 0;
|
|
lfs_size_t size = lfs_min32(CHUNK, SIZE - off);
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < size; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
// write
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off;
|
|
lfsr_file_write(&lfs, &rdwrs[rw], wbuf, size) => size;
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
|
|
// update sim
|
|
if (off > between_size[rw]) {
|
|
memset(&between[rw][between_size[rw]],
|
|
0,
|
|
off - between_size[rw]);
|
|
}
|
|
memcpy(&between[rw][off], wbuf, size);
|
|
between_size[rw] = lfs_max32(off + size, between_size[rw]);
|
|
|
|
// reading?
|
|
} else if (op == 1) {
|
|
// choose a random offset
|
|
lfs_off_t off = (between_size[rw] > 0)
|
|
? TEST_PRNG(&prng) % between_size[rw]
|
|
: 0;
|
|
lfs_size_t size = lfs_min32(CHUNK, between_size[rw] - off);
|
|
|
|
// read
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off;
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &rdwrs[rw], rbuf, CHUNK) => size;
|
|
assert(memcmp(rbuf, &between[rw][off], size) == 0);
|
|
|
|
// truncating?
|
|
} else if (op == 2) {
|
|
// choose a random new file size
|
|
lfs_off_t size = TEST_PRNG(&prng) % SIZE;
|
|
|
|
// truncate
|
|
lfsr_file_truncate(&lfs, &rdwrs[rw], size) => 0;
|
|
|
|
// update the sim
|
|
if (size > between_size[rw]) {
|
|
memset(&between[rw][between_size[rw]],
|
|
0,
|
|
size - between_size[rw]);
|
|
}
|
|
between_size[rw] = size;
|
|
|
|
// fruncating?
|
|
} else if (op == 3) {
|
|
// choose a random new file size
|
|
lfs_off_t size = TEST_PRNG(&prng) % SIZE;
|
|
|
|
// fruncate
|
|
lfsr_file_fruncate(&lfs, &rdwrs[rw], size) => 0;
|
|
|
|
// update the sim
|
|
if (size > between_size[rw]) {
|
|
memmove(&between[rw][size - between_size[rw]],
|
|
between[rw],
|
|
between_size[rw]);
|
|
memset(between[rw],
|
|
0,
|
|
size - between_size[rw]);
|
|
} else {
|
|
memmove(between[rw],
|
|
&between[rw][between_size[rw] - size],
|
|
size);
|
|
}
|
|
between_size[rw] = size;
|
|
}
|
|
|
|
// desync?
|
|
if (sync == 0) {
|
|
lfsr_file_desync(&lfs, &rdwrs[rw]) => 0;
|
|
between_desync[rw] = true;
|
|
|
|
// sync?
|
|
} else if (sync == 1) {
|
|
lfsr_file_sync(&lfs, &rdwrs[rw]) => 0;
|
|
between_desync[rw] = false;
|
|
|
|
// otherwise no change
|
|
}
|
|
|
|
// broadcast sim?
|
|
if (sync == 1) {
|
|
memcpy(after, between[rw], SIZE);
|
|
after_size = between_size[rw];
|
|
|
|
for (lfs_size_t rw_ = 0; rw_ < RW; rw_++) {
|
|
if (!between_desync[rw_]) {
|
|
memcpy(between[rw_], between[rw], SIZE);
|
|
between_size[rw_] = between_size[rw];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// close files
|
|
lfsr_file_close(&lfs, &rdwrs[rw]) => 0;
|
|
|
|
// broadcast sim one last time?
|
|
if (!between_desync[rw]) {
|
|
memcpy(after, between[rw], SIZE);
|
|
after_size = between_size[rw];
|
|
|
|
for (lfs_size_t rw_ = 0; rw_ < RW; rw_++) {
|
|
if (!between_desync[rw_]) {
|
|
memcpy(between[rw_], between[rw], SIZE);
|
|
between_size[rw_] = between_size[rw];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => after_size;
|
|
assert(memcmp(rbuf, after, after_size) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|