Fix parity writer error propagation in io_flush and abort sync on parity write failures

This commit is contained in:
Andrea Mazzoleni 2026-08-17 18:28:26 +02:00
parent fa0a77f84c
commit ef6fa54ee2
4 changed files with 69 additions and 27 deletions

View file

@ -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_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_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_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. * 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 */ /* 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) static void io_write_next_mono(struct snapraid_io* io, block_off_t blockcur, int skip, int* writer_error)
{ {
unsigned i;
(void)blockcur; (void)blockcur;
(void)skip; (void)skip;
/* report errors */ /* report errors */
for (i = 0; i < IO_WRITER_ERROR_MAX; ++i) io_writer_error_get(io, writer_error);
writer_error[i] = io->writer_error[i];
} }
static void io_refresh_mono(struct snapraid_io* io) static void io_refresh_mono(struct snapraid_io* io)
@ -182,9 +212,10 @@ static void io_refresh_mono(struct snapraid_io* io)
(void)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) 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]; worker = &io->writer_map[i];
task = &worker->task_map[0]; task = &worker->task_map[0];
io->writer_error[i] = 0;
/* do the work */ /* do the work */
if (task->state != TASK_STATE_EMPTY) if (task->state != TASK_STATE_EMPTY) {
worker->func(worker, task); worker->func(worker, task);
io_writer_error_add(io, task->state);
}
/* return the position */ /* return the position */
*pos = i; *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) static struct snapraid_task* io_writer_step(struct snapraid_worker* worker, int state)
{ {
struct snapraid_io* io = worker->io; struct snapraid_io* io = worker->io;
int error_index;
/* the synchronization is protected by the io mutex */ /* the synchronization is protected by the io mutex */
thread_mutex_lock(&io->io_mutex); thread_mutex_lock(&io->io_mutex);
/* counts the number of errors in the global state */ /* counts the number of errors in the global state */
error_index = state - IO_WRITER_ERROR_BASE; io_writer_error_add(io, state);
if (error_index >= 0 && error_index < IO_WRITER_ERROR_MAX)
++io->writer_error[error_index];
worker->busy = 0; 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); thread_mutex_lock(&io->io_mutex);
/* report errors */ /* report errors */
for (i = 0; i < IO_WRITER_ERROR_MAX; ++i) { io_writer_error_get(io, writer_error);
writer_error[i] = io->writer_error[i];
io->writer_error[i] = 0;
}
if (skip) { if (skip) {
/* skip the next write */ /* skip the next write */
@ -533,7 +559,7 @@ static void io_refresh_thread(struct snapraid_io* io)
thread_mutex_unlock(&io->io_mutex); 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; 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) thread_mutex_unlock(&io->io_mutex);
break;
/* /*
* Wait for something to complete. * Wait for something to complete.

View file

@ -504,8 +504,10 @@ extern void (*io_refresh)(struct snapraid_io* io);
/** /**
* Flush all the writes. * 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 #endif

View file

@ -5878,8 +5878,19 @@ int state_flush(struct snapraid_state* state, struct snapraid_io* io, struct sna
{ {
unsigned l; unsigned l;
if (io) if (io) {
io_flush(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 */ /* flush all parity handles to ensure data is written to disk */
for (l = 0; l < state->level; ++l) { for (l = 0; l < state->level; ++l) {

View file

@ -629,8 +629,7 @@ static void sync_parity_writer(struct snapraid_worker* worker, struct snapraid_t
if (is_hw(errno)) { if (is_hw(errno)) {
log_fatal_errno(errno, lev_config_name(level)); log_fatal_errno(errno, lev_config_name(level));
/* continue until the error limit is reached */ task->state = TASK_STATE_IOERROR;
task->state = TASK_STATE_IOERROR_CONTINUE;
} else { } else {
log_fatal_errno(errno, lev_config_name(level)); log_fatal_errno(errno, lev_config_name(level));
log_fatal(errno, "Stopping at block %" PRIu64 "\n", blockcur); log_fatal(errno, "Stopping at block %" PRIu64 "\n", blockcur);