From 80b58e66d507505ed22593ecda33c2c33d296e43 Mon Sep 17 00:00:00 2001 From: Andrea Mazzoleni Date: Sat, 29 Aug 2026 09:26:28 +0200 Subject: [PATCH] Initialize parity split sizes before pre-resize state checkpoint --- Makefile.am | 55 +++++++++++++++++++++++++++++++++++ cmdline/sync.c | 34 ++++++++++++++++++++++ test/test-realloc-level1.conf | 10 +++++++ test/test-realloc-level2.conf | 11 +++++++ test/test-realloc-split1.conf | 10 +++++++ test/test-realloc-split2.conf | 10 +++++++ 6 files changed, 130 insertions(+) create mode 100644 test/test-realloc-level1.conf create mode 100644 test/test-realloc-level2.conf create mode 100644 test/test-realloc-split1.conf create mode 100644 test/test-realloc-split2.conf diff --git a/Makefile.am b/Makefile.am index 8d46920f..1a2a54ee 100644 --- a/Makefile.am +++ b/Makefile.am @@ -185,6 +185,10 @@ EXTRA_DIST = \ test/test-par4.conf \ test/test-par5.conf \ test/test-par6.conf \ + test/test-realloc-level1.conf \ + test/test-realloc-level2.conf \ + test/test-realloc-split1.conf \ + test/test-realloc-split2.conf \ test/test-par6-hole.conf \ test/test-par6-noaccess.conf \ test/test-par6-rename.conf \ @@ -329,6 +333,10 @@ PARZ = $(srcdir)/test/test-parz.conf PAR4 = $(srcdir)/test/test-par4.conf PAR5 = $(srcdir)/test/test-par5.conf PAR6 = $(srcdir)/test/test-par6.conf +REALLOC_LEVEL1 = $(srcdir)/test/test-realloc-level1.conf +REALLOC_LEVEL2 = $(srcdir)/test/test-realloc-level2.conf +REALLOC_SPLIT1 = $(srcdir)/test/test-realloc-split1.conf +REALLOC_SPLIT2 = $(srcdir)/test/test-realloc-split2.conf SNAP = $(srcdir)/test/test-snap.conf MSG = @echo ===== SNAPRAID = ./checker$(EXEEXT) @@ -1282,6 +1290,53 @@ endif # !HAVE_MEMORY_CHECKER $(MSG) Sync after all the fixes $(TESTENV) $(SNAPRAID) $(CHECKFLAGS) -c $(CONF) sync $(TESTENV) $(SNAPRAID) $(CHECKFLAGS) -c $(CONF) check + rm -rf bench/realloc-level + mkdir -p bench/realloc-level/disk1 + mkdir -p bench/realloc-level/disk2 + mkdir -p bench/realloc-level/disk3 + mkdir -p bench/realloc-level/disk4 + mkdir -p bench/realloc-level/disk5 + mkdir -p bench/realloc-level/disk6 + dd bs=1024 count=2048 if=/dev/urandom of=bench/realloc-level/disk1/data +#### NEW PARITY CONFIGURATION CRASH TESTS #### + $(MSG) Test interrupted force-realloc after adding a parity level + $(TESTENV) $(SNAPRAID) $(CHECKFLAGS) -c $(REALLOC_LEVEL1) sync + $(TESTENV) $(SNAPRAID) $(CHECKFLAGS) -c $(REALLOC_LEVEL2) -R --test-kill-after-resize sync + $(TESTENV) $(SNAPRAID) $(CHECKFLAGS) -c $(REALLOC_LEVEL2) locate > /dev/null + $(TESTENV) $(SNAPRAID) $(CHECKFLAGS) -c $(REALLOC_LEVEL2) sync + $(TESTENV) $(SNAPRAID) $(CHECKFLAGS) -c $(REALLOC_LEVEL2) check + rm -rf bench/realloc-split + mkdir -p bench/realloc-split/disk1 + mkdir -p bench/realloc-split/disk2 + mkdir -p bench/realloc-split/disk3 + mkdir -p bench/realloc-split/disk4 + mkdir -p bench/realloc-split/disk5 + mkdir -p bench/realloc-split/disk6 + dd bs=1024 count=2048 if=/dev/urandom of=bench/realloc-split/disk1/data1 + $(MSG) Test interrupted force-realloc after adding a parity split + $(TESTENV) $(SNAPRAID) $(CHECKFLAGS) -c $(REALLOC_SPLIT1) sync + dd bs=1024 count=2048 if=/dev/urandom of=bench/realloc-split/disk1/data2 + $(TESTENV) $(SNAPRAID) $(CHECKFLAGS) -c $(REALLOC_SPLIT2) -R --test-kill-after-resize sync + $(TESTENV) $(SNAPRAID) $(CHECKFLAGS) -c $(REALLOC_SPLIT2) locate > /dev/null + $(TESTENV) $(SNAPRAID) $(CHECKFLAGS) -c $(REALLOC_SPLIT2) sync + $(TESTENV) $(SNAPRAID) $(CHECKFLAGS) -c $(REALLOC_SPLIT2) check + $(MSG) Test interrupted normal shrink after adding a parity split + rm -rf bench/realloc-split + mkdir -p bench/realloc-split/disk1 + mkdir -p bench/realloc-split/disk2 + mkdir -p bench/realloc-split/disk3 + mkdir -p bench/realloc-split/disk4 + mkdir -p bench/realloc-split/disk5 + mkdir -p bench/realloc-split/disk6 + dd bs=1024 count=2048 if=/dev/urandom of=bench/realloc-split/disk1/data + $(TESTENV) $(SNAPRAID) $(CHECKFLAGS) -c $(REALLOC_SPLIT1) sync + dd bs=1024 count=1024 if=/dev/urandom of=bench/realloc-split/disk1/NORMAL_SPLIT_SHRINK + $(TESTENV) $(SNAPRAID) $(CHECKFLAGS) -c $(REALLOC_SPLIT1) sync + rm bench/realloc-split/disk1/NORMAL_SPLIT_SHRINK + $(TESTENV) $(SNAPRAID) $(CHECKFLAGS) -c $(REALLOC_SPLIT2) --test-kill-after-resize sync + $(TESTENV) $(SNAPRAID) $(CHECKFLAGS) -c $(REALLOC_SPLIT2) locate > /dev/null + $(TESTENV) $(SNAPRAID) $(CHECKFLAGS) -c $(REALLOC_SPLIT2) sync + $(TESTENV) $(SNAPRAID) $(CHECKFLAGS) -c $(REALLOC_SPLIT2) check #### FORCE-REALLOC CRASH TESTS #### $(MSG) Test interrupted force-realloc post-resize crash dd bs=1024 count=1024 if=/dev/urandom of=bench/disk1/REALLOC_CRASH1 diff --git a/cmdline/sync.c b/cmdline/sync.c index ecedd552..71dfc02b 100644 --- a/cmdline/sync.c +++ b/cmdline/sync.c @@ -1861,6 +1861,37 @@ static void state_sync_mark_rebuild(struct snapraid_state* state) state->need_write = 1; } +/* + * Initialize persistent parity split sizes resolved while opening parity. + * + * parity_create() resolves PARITY_SIZE_INVALID in the parity handles using + * the current physical file size, but intentionally doesn't modify the + * persistent parity state. + * + * Before writing a pre-resize recovery checkpoint, copy only these resolved + * initial sizes into the persistent state. Existing valid sizes must be + * preserved because they describe the previously persisted logical split + * layout. + */ +static void state_sync_parity_size(struct snapraid_state* state, struct snapraid_parity_handle* parity_handle) +{ + unsigned l; + unsigned s; + int modified = 0; + + for (l = 0; l < state->level; ++l) { + for (s = 0; s < state->parity[l].split_mac; ++s) { + if (state->parity[l].split_map[s].size == PARITY_SIZE_INVALID) { + state->parity[l].split_map[s].size = parity_handle[l].split_map[s].size; + modified = 1; + } + } + } + + if (modified) + state->need_write = 1; +} + int state_sync(struct snapraid_state* state, block_off_t blockstart, block_off_t blockcount) { block_off_t blockmax; @@ -2024,6 +2055,9 @@ int state_sync(struct snapraid_state* state, block_off_t blockstart, block_off_t if (state->opt.force_full) state_sync_mark_rebuild(state); + if (state->opt.force_full || state->opt.force_realloc || parity_shrink) + state_sync_parity_size(state, parity_handle); + if ((state->opt.force_full || state->opt.force_realloc || parity_shrink) diff --git a/test/test-realloc-level1.conf b/test/test-realloc-level1.conf new file mode 100644 index 00000000..d6b85339 --- /dev/null +++ b/test/test-realloc-level1.conf @@ -0,0 +1,10 @@ +# Test configuration for adding a parity level during force-realloc +blocksize 1 +parity bench/realloc-level/parity +content bench/realloc-level/content +disk disk1 bench/realloc-level/disk1/ +disk disk2 bench/realloc-level/disk2/ +disk disk3 bench/realloc-level/disk3/ +disk disk4 bench/realloc-level/disk4/ +disk disk5 bench/realloc-level/disk5/ +disk disk6 bench/realloc-level/disk6/ diff --git a/test/test-realloc-level2.conf b/test/test-realloc-level2.conf new file mode 100644 index 00000000..81ddba87 --- /dev/null +++ b/test/test-realloc-level2.conf @@ -0,0 +1,11 @@ +# Test configuration for adding a parity level during force-realloc +blocksize 1 +parity bench/realloc-level/parity +2-parity bench/realloc-level/2-parity +content bench/realloc-level/content +disk disk1 bench/realloc-level/disk1/ +disk disk2 bench/realloc-level/disk2/ +disk disk3 bench/realloc-level/disk3/ +disk disk4 bench/realloc-level/disk4/ +disk disk5 bench/realloc-level/disk5/ +disk disk6 bench/realloc-level/disk6/ diff --git a/test/test-realloc-split1.conf b/test/test-realloc-split1.conf new file mode 100644 index 00000000..d9ee50d6 --- /dev/null +++ b/test/test-realloc-split1.conf @@ -0,0 +1,10 @@ +# Test configuration for adding a parity split during force-realloc +blocksize 1 +parity bench/realloc-split/parity.0 +content bench/realloc-split/content +disk disk1 bench/realloc-split/disk1/ +disk disk2 bench/realloc-split/disk2/ +disk disk3 bench/realloc-split/disk3/ +disk disk4 bench/realloc-split/disk4/ +disk disk5 bench/realloc-split/disk5/ +disk disk6 bench/realloc-split/disk6/ diff --git a/test/test-realloc-split2.conf b/test/test-realloc-split2.conf new file mode 100644 index 00000000..3bf7f03f --- /dev/null +++ b/test/test-realloc-split2.conf @@ -0,0 +1,10 @@ +# Test configuration for adding a parity split during force-realloc +blocksize 1 +parity bench/realloc-split/parity.0,bench/realloc-split/parity.1 +content bench/realloc-split/content +disk disk1 bench/realloc-split/disk1/ +disk disk2 bench/realloc-split/disk2/ +disk disk3 bench/realloc-split/disk3/ +disk disk4 bench/realloc-split/disk4/ +disk disk5 bench/realloc-split/disk5/ +disk disk6 bench/realloc-split/disk6/