From ef6fa54ee262f0f10bdcf66c19f404e0572bffcc Mon Sep 17 00:00:00 2001 From: Andrea Mazzoleni Date: Mon, 17 Aug 2026 18:28:26 +0200 Subject: [PATCH] Fix parity writer error propagation in io_flush and abort sync on parity write failures --- cmdline/io.c | 74 ++++++++++++++++++++++++++++++++++--------------- cmdline/io.h | 4 ++- cmdline/state.c | 15 ++++++++-- cmdline/sync.c | 3 +- 4 files changed, 69 insertions(+), 27 deletions(-) diff --git a/cmdline/io.c b/cmdline/io.c index 391ef29c..a84e9579 100644 --- a/cmdline/io.c +++ b/cmdline/io.c @@ -16,7 +16,7 @@ void (*io_parity_write)(struct snapraid_io* io, unsigned* levcur, unsigned* wait void (*io_write_preset)(struct snapraid_io* io, block_off_t blockcur, int skip) = 0; void (*io_write_next)(struct snapraid_io* io, block_off_t blockcur, int skip, int* writer_error) = 0; void (*io_refresh)(struct snapraid_io* io) = 0; -void (*io_flush)(struct snapraid_io* io) = 0; +void (*io_flush)(struct snapraid_io* io, int* writer_error) = 0; /** * Get the next block position to process. @@ -124,6 +124,39 @@ static void io_writer_sched_empty(struct snapraid_io* io, int task_index, block_ } } +/** + * Add an error to the global writer error counters. + */ +static void io_writer_error_add(struct snapraid_io* io, int state) +{ + int error_index; + + error_index = state - IO_WRITER_ERROR_BASE; + + /* + * Map negative TASK_STATE_* error codes to array indices [0..IO_WRITER_ERROR_MAX - 1]. + * Non-error states (e.g. TASK_STATE_EMPTY, TASK_STATE_DONE) result in an index + * outside this range and are ignored. + */ + if (error_index >= 0 && error_index < IO_WRITER_ERROR_MAX) + ++io->writer_error[error_index]; +} + +/** + * Transfer the accumulated writer errors to the caller and clear the counters. + * Threaded callers must hold io_mutex so no writer can increment a counter + * between its read and clear; the mono-thread path needs no lock. + */ +static void io_writer_error_get(struct snapraid_io* io, int* writer_error) +{ + unsigned i; + + for (i = 0; i < IO_WRITER_ERROR_MAX; ++i) { + writer_error[i] = io->writer_error[i]; + io->writer_error[i] = 0; + } +} + /*****************************************************************************/ /* mono thread */ @@ -167,14 +200,11 @@ static void io_write_preset_mono(struct snapraid_io* io, block_off_t blockcur, i static void io_write_next_mono(struct snapraid_io* io, block_off_t blockcur, int skip, int* writer_error) { - unsigned i; - (void)blockcur; (void)skip; /* report errors */ - for (i = 0; i < IO_WRITER_ERROR_MAX; ++i) - writer_error[i] = io->writer_error[i]; + io_writer_error_get(io, writer_error); } static void io_refresh_mono(struct snapraid_io* io) @@ -182,9 +212,10 @@ static void io_refresh_mono(struct snapraid_io* io) (void)io; } -static void io_flush_mono(struct snapraid_io* io) +static void io_flush_mono(struct snapraid_io* io, int* writer_error) { - (void)io; + /* report errors */ + io_writer_error_get(io, writer_error); } static struct snapraid_task* io_task_read_mono(struct snapraid_io* io, unsigned base, unsigned count, unsigned* pos, unsigned* waiting_map, unsigned* waiting_mac) @@ -237,12 +268,13 @@ static void io_parity_write_mono(struct snapraid_io* io, unsigned* pos, unsigned worker = &io->writer_map[i]; task = &worker->task_map[0]; - io->writer_error[i] = 0; - /* do the work */ - if (task->state != TASK_STATE_EMPTY) + if (task->state != TASK_STATE_EMPTY) { worker->func(worker, task); + io_writer_error_add(io, task->state); + } + /* return the position */ *pos = i; @@ -338,15 +370,12 @@ static struct snapraid_task* io_reader_step(struct snapraid_worker* worker) static struct snapraid_task* io_writer_step(struct snapraid_worker* worker, int state) { struct snapraid_io* io = worker->io; - int error_index; /* the synchronization is protected by the io mutex */ thread_mutex_lock(&io->io_mutex); /* counts the number of errors in the global state */ - error_index = state - IO_WRITER_ERROR_BASE; - if (error_index >= 0 && error_index < IO_WRITER_ERROR_MAX) - ++io->writer_error[error_index]; + io_writer_error_add(io, state); worker->busy = 0; @@ -462,10 +491,7 @@ static void io_write_next_thread(struct snapraid_io* io, block_off_t blockcur, i thread_mutex_lock(&io->io_mutex); /* report errors */ - for (i = 0; i < IO_WRITER_ERROR_MAX; ++i) { - writer_error[i] = io->writer_error[i]; - io->writer_error[i] = 0; - } + io_writer_error_get(io, writer_error); if (skip) { /* skip the next write */ @@ -533,7 +559,7 @@ static void io_refresh_thread(struct snapraid_io* io) thread_mutex_unlock(&io->io_mutex); } -static void io_flush_thread(struct snapraid_io* io) +static void io_flush_thread(struct snapraid_io* io, int* writer_error) { unsigned i; @@ -560,10 +586,14 @@ static void io_flush_thread(struct snapraid_io* io) } } - thread_mutex_unlock(&io->io_mutex); + if (all_done) { + /* report errors */ + io_writer_error_get(io, writer_error); + thread_mutex_unlock(&io->io_mutex); + return; + } - if (all_done) - break; + thread_mutex_unlock(&io->io_mutex); /* * Wait for something to complete. diff --git a/cmdline/io.h b/cmdline/io.h index 1e907bca..f6565835 100644 --- a/cmdline/io.h +++ b/cmdline/io.h @@ -504,8 +504,10 @@ extern void (*io_refresh)(struct snapraid_io* io); /** * Flush all the writes. + * + * \param writer_error Return the number of errors. Vector of IO_WRITER_ERROR_MAX elements. */ -extern void (*io_flush)(struct snapraid_io* io); +extern void (*io_flush)(struct snapraid_io* io, int* writer_error); #endif diff --git a/cmdline/state.c b/cmdline/state.c index d3236a1e..ff1c923d 100644 --- a/cmdline/state.c +++ b/cmdline/state.c @@ -5878,8 +5878,19 @@ int state_flush(struct snapraid_state* state, struct snapraid_io* io, struct sna { unsigned l; - if (io) - io_flush(io); + if (io) { + int writer_error[IO_WRITER_ERROR_MAX]; + unsigned j; + + io_flush(io, writer_error); + + for (j = 0; j < IO_WRITER_ERROR_MAX; ++j) { + if (writer_error[j]) { + errno = EIO; + return -1; + } + } + } /* flush all parity handles to ensure data is written to disk */ for (l = 0; l < state->level; ++l) { diff --git a/cmdline/sync.c b/cmdline/sync.c index c9373236..45186757 100644 --- a/cmdline/sync.c +++ b/cmdline/sync.c @@ -629,8 +629,7 @@ static void sync_parity_writer(struct snapraid_worker* worker, struct snapraid_t if (is_hw(errno)) { log_fatal_errno(errno, lev_config_name(level)); - /* continue until the error limit is reached */ - task->state = TASK_STATE_IOERROR_CONTINUE; + task->state = TASK_STATE_IOERROR; } else { log_fatal_errno(errno, lev_config_name(level)); log_fatal(errno, "Stopping at block %" PRIu64 "\n", blockcur);