mirror of
https://github.com/amadvance/snapraid.git
synced 2026-10-26 19:01:57 -04:00
Persist reallocated mapping before parity resize in force-realloc sync
This commit is contained in:
parent
395291bd9a
commit
82be7b07b3
5 changed files with 81 additions and 20 deletions
33
Makefile.am
33
Makefile.am
|
|
@ -1282,6 +1282,39 @@ endif # !HAVE_MEMORY_CHECKER
|
|||
$(MSG) Sync after all the fixes
|
||||
$(TESTENV) $(SNAPRAID) $(CHECKFLAGS) -c $(CONF) sync
|
||||
$(TESTENV) $(SNAPRAID) $(CHECKFLAGS) -c $(CONF) 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
|
||||
dd bs=1024 count=1024 if=/dev/urandom of=bench/disk1/REALLOC_CRASH2
|
||||
$(TESTENV) $(SNAPRAID) $(CHECKFLAGS) -c $(CONF) sync
|
||||
rm bench/disk1/REALLOC_CRASH1
|
||||
$(TESTENV) $(SNAPRAID) $(CHECKFLAGS) -c $(CONF) sync
|
||||
$(TESTENV) $(SNAPRAID) $(CHECKFLAGS) -c $(CONF) locate > bench/realloc_crash_before.txt
|
||||
$(TESTENV) $(SNAPRAID) $(CHECKFLAGS) -c $(CONF) -R --test-kill-after-resize sync
|
||||
$(TESTENV) $(SNAPRAID) $(CHECKFLAGS) -c $(CONF) locate > bench/realloc_crash_after.txt
|
||||
! diff bench/realloc_crash_before.txt bench/realloc_crash_after.txt > /dev/null
|
||||
$(TESTENV) $(SNAPRAID) $(CHECKFLAGS) -c $(CONF) sync
|
||||
$(TESTENV) $(SNAPRAID) $(CHECKFLAGS) -c $(CONF) locate > bench/realloc_crash_final.txt
|
||||
diff bench/realloc_crash_after.txt bench/realloc_crash_final.txt
|
||||
$(TESTENV) $(SNAPRAID) $(CHECKFLAGS) -c $(CONF) check
|
||||
rm bench/disk1/REALLOC_CRASH2
|
||||
$(TESTENV) $(SNAPRAID) $(CHECKFLAGS) -c $(CONF) sync
|
||||
$(MSG) Test interrupted force-realloc-tail post-resize crash
|
||||
dd bs=1024 count=1024 if=/dev/urandom of=bench/disk1/REALLOC_TAIL1
|
||||
dd bs=1024 count=1024 if=/dev/urandom of=bench/disk1/REALLOC_TAIL2
|
||||
$(TESTENV) $(SNAPRAID) $(CHECKFLAGS) -c $(CONF) sync
|
||||
rm bench/disk1/REALLOC_TAIL1
|
||||
$(TESTENV) $(SNAPRAID) $(CHECKFLAGS) -c $(CONF) sync
|
||||
$(TESTENV) $(SNAPRAID) $(CHECKFLAGS) -c $(CONF) locate > bench/realloc_tail_crash_before.txt
|
||||
$(TESTENV) $(SNAPRAID) $(CHECKFLAGS) -c $(CONF) -W 10M --test-kill-after-resize sync
|
||||
$(TESTENV) $(SNAPRAID) $(CHECKFLAGS) -c $(CONF) locate > bench/realloc_tail_crash_after.txt
|
||||
! diff bench/realloc_tail_crash_before.txt bench/realloc_tail_crash_after.txt > /dev/null
|
||||
$(TESTENV) $(SNAPRAID) $(CHECKFLAGS) -c $(CONF) sync
|
||||
$(TESTENV) $(SNAPRAID) $(CHECKFLAGS) -c $(CONF) locate > bench/realloc_tail_crash_final.txt
|
||||
diff bench/realloc_tail_crash_after.txt bench/realloc_tail_crash_final.txt
|
||||
$(TESTENV) $(SNAPRAID) $(CHECKFLAGS) -c $(CONF) check
|
||||
rm bench/disk1/REALLOC_TAIL2
|
||||
$(TESTENV) $(SNAPRAID) $(CHECKFLAGS) -c $(CONF) sync
|
||||
endif # HAVE_THREAD_CHECKER
|
||||
$(MSG) Regression test completed with SUCCESS!
|
||||
$(MSG) Please ignore any error message printed above, they are expected!
|
||||
|
|
|
|||
|
|
@ -250,8 +250,13 @@ void state_locate_mark_tail_blocks_for_resync(struct snapraid_state* state, uint
|
|||
/*
|
||||
* Explicit force-realloc overrides an in-place REBUILD request.
|
||||
* REP makes the block eligible for the normal reallocation path.
|
||||
*
|
||||
* This transition changes persistent block-state semantics. Mark the
|
||||
* state dirty explicitly rather than relying on the following scan to
|
||||
* incidentally do so.
|
||||
*/
|
||||
block_state_set(block, BLOCK_STATE_REP);
|
||||
state->need_write = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -257,6 +257,7 @@ void log_close(const char* file)
|
|||
#define OPT_TEST_SPEED_DISKS_NUMBER 308
|
||||
#define OPT_TEST_SPEED_BLOCKS_SIZE 309
|
||||
#define OPT_TEST_KILL_BEFORE_SYNC 310
|
||||
#define OPT_TEST_KILL_AFTER_RESIZE 311
|
||||
|
||||
|
||||
#if HAVE_GETOPT_LONG
|
||||
|
|
@ -312,7 +313,10 @@ static struct option long_options[] = {
|
|||
/* After syncing, do not write the new content file */
|
||||
{ "test-kill-after-sync", 0, 0, OPT_TEST_KILL_AFTER_SYNC },
|
||||
|
||||
/* Do not execure syncing after writing the new initial content file */
|
||||
/* Do not execute syncing after resizing parity files, before the normal post-resize content write */
|
||||
{ "test-kill-after-resize", 0, 0, OPT_TEST_KILL_AFTER_RESIZE },
|
||||
|
||||
/* Do not execute syncing after writing the new initial content file */
|
||||
{ "test-kill-before-sync", 0, 0, OPT_TEST_KILL_BEFORE_SYNC },
|
||||
|
||||
/* Exit with failure if after check/fix there ARE NOT unrecoverable errors. */
|
||||
|
|
@ -860,6 +864,9 @@ int snapraid_main(int argc, char* argv[])
|
|||
case OPT_TEST_KILL_AFTER_SYNC :
|
||||
opt.kill_after_sync = 1;
|
||||
break;
|
||||
case OPT_TEST_KILL_AFTER_RESIZE :
|
||||
opt.kill_after_resize = 1;
|
||||
break;
|
||||
case OPT_TEST_KILL_BEFORE_SYNC :
|
||||
opt.kill_before_sync = 1;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ struct snapraid_option {
|
|||
int skip_content_access; /**< Skip the content access for commands that don't need it. */
|
||||
int kill_after_sync; /**< Kill the process after sync without saving the final state. */
|
||||
int kill_before_sync; /**< Kill the process before sync after saving the initial state. */
|
||||
int kill_after_resize; /**< Kill the process after parity resize before the normal post-resize state write. */
|
||||
int force_murmur3; /**< Force Murmur3 choice. */
|
||||
int force_spooky2; /**< Force Spooky2 choice. */
|
||||
int force_order; /**< Force sorting order. One of the SORT_* defines. */
|
||||
|
|
|
|||
|
|
@ -140,10 +140,14 @@
|
|||
*
|
||||
* 1. Scan creates CHG, REP and DELETED pending states.
|
||||
* For --force-full, BLK blocks are marked REBUILD before parity resizing.
|
||||
* For --force-realloc, the scan also constructs the new data-to-parity
|
||||
* allocation before physical parity is resized.
|
||||
*
|
||||
* 2. The pending content state is durably written before parity is changed.
|
||||
* 2. The pending content state and, when reallocating, the new allocation
|
||||
* are durably written before physical parity is changed.
|
||||
* A crash after this point therefore leaves enough information to
|
||||
* identify every parity position that may have been modified or needs rebuild.
|
||||
* identify every parity position that may be modified or rebuilt without
|
||||
* relying on a physical layout referenced only by an older content file.
|
||||
*
|
||||
* 3. Sync reads current data and computes new parity. Blocks may already be
|
||||
* changed to BLK/EMPTY in memory while parity writes are still pending.
|
||||
|
|
@ -1966,30 +1970,36 @@ int state_sync(struct snapraid_state* state, block_off_t blockstart, block_off_t
|
|||
|
||||
if (!skip_sync) {
|
||||
/*
|
||||
* Persist the full-rebuild intent before changing physical parity.
|
||||
* Persist the logical state required to recover from a crash before changing
|
||||
* the physical parity layout.
|
||||
*
|
||||
* parity_chsize() may enlarge a truncated parity file. While the parity handle
|
||||
* remains open, physical_reach_size intentionally does not advance merely because
|
||||
* of allocation: it is a physical read/truncation extent, not a parity-validity
|
||||
* marker.
|
||||
* For --force-full, BLK -> REBUILD records that the existing allocation must
|
||||
* be preserved but its physical parity must be regenerated.
|
||||
*
|
||||
* physical_reach_size is not persisted separately. After a crash and reopen, the
|
||||
* physical EOF becomes the reconstructed extent, so the previous short EOF can
|
||||
* no longer be used as evidence that positions beyond it still require work.
|
||||
* For --force-realloc, state_scan() has already completed the requested
|
||||
* reallocation before state_sync() is entered. The in-memory content state
|
||||
* therefore contains the new data-to-parity mapping, with REP/CHG/DELETED
|
||||
* states describing parity that is not yet synchronized.
|
||||
*
|
||||
* For --force-full, that rebuild requirement therefore has to be persisted in
|
||||
* the content state before resizing. BLK -> REBUILD records that physical
|
||||
* parity must be regenerated independently of what EOF or physical_reach_size says.
|
||||
* That post-scan state must be durable before parity_chsize() runs.
|
||||
* Reallocation can move data backward and reduce the required parity size.
|
||||
* If parity were truncated first and the process terminated before the new
|
||||
* mapping was persisted, the old content file could still reference parity
|
||||
* positions that had already been physically discarded.
|
||||
*
|
||||
* This ordering is about preserving rebuild intent across a crash. It is not
|
||||
* needed because bytes below physical_reach_size are assumed valid: parity
|
||||
* correctness is determined separately by block state and parity verification.
|
||||
* The opposite crash ordering is safe: if the new content state is written
|
||||
* and the process terminates before resizing or rewriting parity, its
|
||||
* REP/CHG/DELETED states prevent the old physical parity from being trusted
|
||||
* as synchronized with the new mapping.
|
||||
*/
|
||||
if (state->opt.force_full) {
|
||||
if (state->opt.force_full)
|
||||
state_sync_mark_rebuild(state);
|
||||
|
||||
if (!state->opt.skip_content_write && state->need_write)
|
||||
state_write(state);
|
||||
if ((state->opt.force_full || state->opt.force_realloc)
|
||||
&& !state->opt.skip_content_write
|
||||
&& state->need_write
|
||||
) {
|
||||
state_write(state);
|
||||
}
|
||||
|
||||
msg_progress("Resizing...\n");
|
||||
|
|
@ -2018,6 +2028,11 @@ int state_sync(struct snapraid_state* state, block_off_t blockstart, block_off_t
|
|||
state->need_write = 1;
|
||||
}
|
||||
|
||||
if (state->opt.kill_after_resize) {
|
||||
log_fatal(EUSER, "WARNING! Killing due --test-kill-after-resize option.\n");
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
/* after resizing parity files, refresh again the free info */
|
||||
state_refresh(state);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue