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_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.

View file

@ -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

View file

@ -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) {

View file

@ -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);