Locked down out-of-order writes, more tests

The main test additions are the test_powerloss tests, intended to be
high-level tests over difficult/weird powerloss environments (such as
out-of-order writes!):

- test_powerloss_dir_many - 2242 pls
- test_powerloss_file_many - 8856 pls
- test_powerloss_file_pl_fuzz - 384508 pls
- test_powerloss_filedir_pl_fuzz - 268339 pls

But there was also a bunch of other test movement in the late-stage/
high-level tests. I'm trying to keep the core of these tests somewhat
consistent so we have a nice template to extend for future testing, in
case we want to test other environmentalish concerns, but not all of
these tests make sense in all of these contexts:

                        badblocks  powerloss  relocations  exhaustion
  dir_many                      y          y            y
  dir_fuzz                      y                       y           y
  file_many                     y          y            y
  file_fuzz                     y                       y           y
  fwrite_fuzz                   y                                   y
  orphanzombie_fuzz             y                       y           y
  orphanzombiedir_fuzz          y                       y           y
  file_pl_fuzz                             y            y
  filedir_pl_fuzz                          y            y

Why not:

- dir/file_many+exhaustion? - Needs to be unbounded
- dir/file_fuzz+powerloss? - Takes O(n^2)
- fwrite_fuzz+powerloss? - Takes O(n^2)
- fwrite_fuzz+relocations? - Doesn't really test anything
- orphanzombie*_fuzz+powerloss? - Powerloss kills zombies
- file*_pl_fuzz+badblocks? - PL + Badblocks currently incompactible
- file*_pl_fuzz+exhaustion? - PL + Badblocks currently incompactible

---

Of course, in order to actually get out-of-order write testing working,
we need to implement out-of-order write syncing.

Fortunately this was a simple exercise in placing lfsr_bd_sync calls
before any mdir commits where we may have unsynced data:

- in lfsr_file_sync, to sync any pending file data
- in lfsr_mdir_commit, to sync any mroot/mtree changes

We also call lfsr_bd_sync _after_ mdir commits in case users expect to
sequence any filesystem-external operations such as network, UI, etc. In
theory this could be optional, but no users have really requested it
yet, so leave that for a potential future improvement:

- in lfsr_mdir_commit
- in lfsr_formatinited (really just because we don't go through
  lfsr_mdir_commit)

Note that lfsr_rbyd_commit has been relaxed in the scheme. It only
flushes caches, and does _not_ call lfsr_bd_sync. This is useful for
allowing multiple B-tree nodes to be written out-of-order, also long as
the whole thing is synchronized before any mdir commit.

All of these lfsr_bd_sync calls add a bit of code, but not really an
amount to care about:

           code          stack
  before: 33678           2600
  after:  33766 (+0.3%)   2600 (+0.0%)
This commit is contained in:
Christopher Haster
2024-05-31 16:39:29 -05:00
parent bb3ef46cdf
commit 9914897e39
7 changed files with 4813 additions and 1250 deletions
+15 -23
View File
@@ -1676,7 +1676,7 @@ defines.SIZE = [
'4*BLOCK_SIZE',
]
# chunk is more an upper limit here
defines.CHUNK = [32, 8]
defines.CHUNK = [64, 16]
# INIT=0 => no init
# INIT=1 => fill with data
# INIT=2 => truncate to size
@@ -1736,16 +1736,14 @@ code = '''
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
// and a random size, up to the chunk size
lfs_size_t chunk = lfs_min32(
TEST_PRNG(&prng) % CHUNK,
(TEST_PRNG(&prng) % (CHUNK+1-1)) + 1,
SIZE - off);
// update sim
for (lfs_size_t j = 0; j < chunk; j++) {
sim[off+j] = 'a' + (TEST_PRNG(&prng) % 26);
}
if (chunk != 0) {
size = lfs_max32(size, off+chunk);
}
size = lfs_max32(size, off+chunk);
// update file
lfsr_file_seek(&lfs, &file, off, LFS_SEEK_SET) => off;
@@ -1828,7 +1826,7 @@ defines.SIZE = [
'4*BLOCK_SIZE',
]
# chunk is more an upper limit here
defines.CHUNK = [32, 8]
defines.CHUNK = [64, 16]
defines.SEED = 'range(10)'
fuzz = 'SEED'
if = [
@@ -1861,7 +1859,7 @@ code = '''
lfs_soff_t off = TEST_PRNG(&prng) % SIZE;
// and a random size, up to the chunk size
lfs_size_t chunk = lfs_min32(
TEST_PRNG(&prng) % CHUNK,
(TEST_PRNG(&prng) % (CHUNK+1-1)) + 1,
SIZE - off);
// test different seek methods
@@ -1907,7 +1905,7 @@ defines.SIZE = [
'4*BLOCK_SIZE',
]
# chunk is more an upper limit here
defines.CHUNK = [32, 8]
defines.CHUNK = [64, 16]
# INIT=0 => no init
# INIT=1 => fill with data
# INIT=2 => truncate to size
@@ -1956,7 +1954,7 @@ code = '''
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
// and a random size, up to the chunk size
lfs_size_t chunk = lfs_min32(
TEST_PRNG(&prng) % CHUNK,
(TEST_PRNG(&prng) % (CHUNK+1-1)) + 1,
SIZE - off);
// test different seek methods
@@ -1975,9 +1973,7 @@ code = '''
for (lfs_size_t j = 0; j < chunk; j++) {
sim[off+j] = 'a' + (TEST_PRNG(&prng) % 26);
}
if (chunk != 0) {
size = lfs_max32(size, off+chunk);
}
size = lfs_max32(size, off+chunk);
// update the file
lfsr_file_write(&lfs, &file, &sim[off], chunk) => chunk;
@@ -2056,7 +2052,7 @@ defines.SIZE = [
'4*BLOCK_SIZE',
]
# chunk is more an upper limit here
defines.CHUNK = [32, 8]
defines.CHUNK = [64, 16]
# INIT=0 => no init
# INIT=1 => fill with data
# INIT=2 => truncate to size
@@ -2105,7 +2101,7 @@ code = '''
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
// and a random size, up to the chunk size
lfs_size_t chunk = lfs_min32(
TEST_PRNG(&prng) % CHUNK,
(TEST_PRNG(&prng) % (CHUNK+1-1)) + 1,
SIZE - off);
// and if we are reading or writing
uint8_t op = TEST_PRNG(&prng) % 2;
@@ -2128,9 +2124,7 @@ code = '''
for (lfs_size_t j = 0; j < chunk; j++) {
sim[off+j] = 'a' + (TEST_PRNG(&prng) % 26);
}
if (chunk != 0) {
size = lfs_max32(size, off+chunk);
}
size = lfs_max32(size, off+chunk);
// update the file
lfsr_file_write(&lfs, &file, &sim[off], chunk) => chunk;
@@ -2336,7 +2330,7 @@ defines.SIZE = [
'4*BLOCK_SIZE',
]
# chunk is more an upper limit here
defines.CHUNK = [32, 8]
defines.CHUNK = [64, 16]
# INIT=0 => no init
# INIT=1 => fill with data
# INIT=2 => truncate to size
@@ -2401,7 +2395,7 @@ code = '''
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
// and a random size, up to the chunk size
lfs_size_t chunk = lfs_min32(
TEST_PRNG(&prng) % CHUNK,
(TEST_PRNG(&prng) % (CHUNK+1-1)) + 1,
SIZE - off);
// seek
@@ -2411,9 +2405,7 @@ code = '''
for (lfs_size_t j = 0; j < chunk; j++) {
sim[off+j] = 'a' + (TEST_PRNG(&prng) % 26);
}
if (chunk != 0) {
size = lfs_max32(size, off+chunk);
}
size = lfs_max32(size, off+chunk);
// update the file
lfsr_file_write(&lfs, &file, &sim[off], chunk) => chunk;
@@ -2437,7 +2429,7 @@ code = '''
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
// and a random size, up to the chunk size
lfs_size_t chunk = lfs_min32(
TEST_PRNG(&prng) % CHUNK,
(TEST_PRNG(&prng) % (CHUNK+1-1)) + 1,
SIZE - off);
// seek