Detect more hardware errors

This commit is contained in:
Andrea Mazzoleni 2026-02-22 14:58:06 +01:00
parent f5c07e07f5
commit 077cf5de01
6 changed files with 160 additions and 119 deletions

View file

@ -33,25 +33,12 @@
static const char* es(int err)
{
if (err == EIO)
if (is_hw(err))
return "error_io";
else
return "error";
}
static void log_fatal_errno(int err, const char* name)
{
if (err == EIO) {
log_fatal(err, "DANGER! Unexpected input/output error in disk %s. It isn't possible to continue.\n", name);
} else if (err == EACCES) {
log_fatal(err, "WARNING! Grant permission in the disk %s. It isn't possible to continue.\n", name);
} else if (err == ENOSPC) {
log_fatal(err, "WARNING! Ensure there is free space on the disk %s. It isn't possible to continue.\n", name);
} else {
log_fatal(err, "WARNING! Without a working %s disk, it isn't possible to continue.\n", name);
}
}
/**
* A block that failed the hash check, or that was deleted.
*/
@ -1134,7 +1121,7 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna
log_tag("%s:%u:%s:%s: Open error at position %u. %s.\n", es(errno), i, disk->name, esc_tag(file->sub, esc_buffer), file_pos, strerror(errno));
if (errno == EIO) {
if (is_hw(errno)) {
++io_error;
} else {
++soft_error;
@ -1219,7 +1206,7 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna
log_tag("%s:%u:%s:%s: Read error at position %u. %s.\n", es(errno), i, disk->name, esc_tag(file->sub, esc_buffer), file_pos, strerror(errno));
if (errno == EIO) {
if (is_hw(errno)) {
++io_error;
} else {
++soft_error;
@ -1321,7 +1308,7 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna
buffer_recov[l] = 0; /* no parity to use */
if (errno == EIO) {
if (is_hw(errno)) {
++io_error;
} else {
++soft_error;
@ -1569,7 +1556,7 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna
log_error(errno, "Error stating empty file '%s'. %s.\n", path, strerror(errno));
log_tag("empty_%s:%s:%s: Empty file stat error\n", es(errno), disk->name, esc_tag(file->sub, esc_buffer));
if (errno == EIO) {
if (is_hw(errno)) {
++io_error;
} else {
++soft_error;
@ -1689,7 +1676,7 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna
log_error(errno, "Error stating hardlink '%s'. %s.\n", path, strerror(errno));
log_tag("hardlink_%s:%s:%s:%s: Hardlink stat error. %s.\n", es(errno), disk->name, esc_tag(slink->sub, esc_buffer), esc_tag(slink->linkto, esc_buffer_alt), strerror(errno));
if (errno == EIO) {
if (is_hw(errno)) {
++io_error;
} else {
++soft_error;
@ -1723,7 +1710,7 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna
log_error(errno, "Error stating hardlink-to '%s'. %s.\n", pathto, strerror(errno));
log_tag("hardlink_%s:%s:%s:%s: Hardlink to stat error. %s.\n", es(errno), disk->name, esc_tag(slink->sub, esc_buffer), esc_tag(slink->linkto, esc_buffer_alt), strerror(errno));
if (errno == EIO) {
if (is_hw(errno)) {
++io_error;
} else {
++soft_error;
@ -1751,7 +1738,7 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna
log_error(errno, "Error reading symlink '%s'. %s.\n", path, strerror(errno));
log_tag("symlink_%s:%s:%s: Symlink read error. %s.\n", es(errno), disk->name, esc_tag(slink->sub, esc_buffer), strerror(errno));
if (errno == EIO) {
if (is_hw(errno)) {
++io_error;
} else {
++soft_error;
@ -1871,7 +1858,7 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna
log_error(errno, "Error stating dir '%s'. %s.\n", path, strerror(errno));
log_tag("dir_%s:%s:%s: Dir stat error. %s.\n", es(errno), disk->name, esc_tag(dir->sub, esc_buffer), strerror(errno));
if (errno == EIO) {
if (is_hw(errno)) {
++io_error;
} else {
++soft_error;
@ -2141,7 +2128,7 @@ int state_check(struct snapraid_state* state, int fix, block_off_t blockstart, b
ret = parity_open(parity_ptr[l], &state->parity[l], l, state->file_mode, state->block_size, state->opt.parity_limit_size);
if (ret == -1) {
log_tag("parity_%s:%u:%s: Open error. %s.\n", es(errno), blockmax, lev_config_name(l), strerror(errno));
if (errno == EIO) {
if (is_hw(errno)) {
log_fatal_errno(errno, lev_config_name(l));
exit(EXIT_FAILURE);
}
@ -2178,7 +2165,7 @@ int state_check(struct snapraid_state* state, int fix, block_off_t blockstart, b
ret = parity_open(parity_ptr[l], &state->parity[l], l, state->file_mode, state->block_size, state->opt.parity_limit_size);
if (ret == -1) {
log_tag("parity_%s:%u:%s: Open error. %s.\n", es(errno), blockmax, lev_config_name(l), strerror(errno));
if (errno == EIO) {
if (is_hw(errno)) {
log_fatal_errno(errno, lev_config_name(l));
exit(EXIT_FAILURE);
}

View file

@ -30,32 +30,12 @@
static const char* es(int err)
{
if (err == EIO)
if (is_hw(err))
return "error_io";
else
return "error";
}
static void log_fatal_errno(int err, const char* name)
{
if (err == EIO) {
log_fatal(err, "DANGER! Unexpected input/output error in disk %s. It isn't possible to continue.\n", name);
} else if (err == EACCES) {
log_fatal(err, "WARNING! Grant permission in the disk %s. It isn't possible to continue.\n", name);
} else if (err == ENOSPC) {
log_fatal(err, "WARNING! Ensure there is free space on the disk %s. It isn't possible to continue.\n", name);
} else {
log_fatal(err, "WARNING! Without a working %s disk, it isn't possible to continue.\n", name);
}
}
static void log_error_errno(int err, const char* name)
{
if (err == EIO) {
log_fatal(err, "DANGER! Unexpected input/output error in disk %s.\n", name);
}
}
static void dry_data_reader(struct snapraid_worker* worker, struct snapraid_task* task)
{
struct snapraid_io* io = worker->io;
@ -102,7 +82,7 @@ static void dry_data_reader(struct snapraid_worker* worker, struct snapraid_task
log_fatal_errno(errno, disk->name);
log_fatal(errno, "Stopping at block %u\n", blockcur);
if (errno == EIO) {
if (is_hw(errno)) {
task->state = TASK_STATE_IOERROR;
} else {
task->state = TASK_STATE_ERROR;
@ -115,7 +95,7 @@ static void dry_data_reader(struct snapraid_worker* worker, struct snapraid_task
ret = handle_open(handle, task->file, state->file_mode, log_error, 0); /* for missing file don't output a message */
if (ret == -1) {
log_tag("%s:%u:%s:%s: Open error. %s.\n", es(errno), blockcur, disk->name, esc_tag(task->file->sub, esc_buffer), strerror(errno));
if (errno == EIO) {
if (is_hw(errno)) {
/* LCOV_EXCL_START */
log_fatal_errno(errno, disk->name);
log_fatal(errno, "Stopping at block %u\n", blockcur);
@ -131,7 +111,7 @@ static void dry_data_reader(struct snapraid_worker* worker, struct snapraid_task
task->read_size = handle_read(handle, task->file_pos, buffer, state->block_size, log_error, 0);
if (task->read_size == -1) {
log_tag("%s:%u:%s:%s: Read error at position %u. %s.\n", es(errno), blockcur, disk->name, esc_tag(task->file->sub, esc_buffer), task->file_pos, strerror(errno));
if (errno == EIO) {
if (is_hw(errno)) {
/* LCOV_EXCL_START */
log_error_errno(errno, disk->name);
task->state = TASK_STATE_IOERROR_CONTINUE;
@ -163,7 +143,7 @@ static void dry_parity_reader(struct snapraid_worker* worker, struct snapraid_ta
ret = parity_read(parity_handle, blockcur, buffer, state->block_size, log_error);
if (ret == -1) {
log_tag("parity_%s:%u:%s: Read error. %s.\n", es(errno), blockcur, lev_config_name(level), strerror(errno));
if (errno == EIO) {
if (is_hw(errno)) {
/* LCOV_EXCL_START */
log_error_errno(errno, lev_config_name(level));
task->state = TASK_STATE_IOERROR_CONTINUE;
@ -406,7 +386,7 @@ bail:
log_tag("%s:%u:%s:%s: Close error. %s.\n", es(errno), blockcur, disk->name, esc_tag(file->sub, esc_buffer), strerror(errno));
log_fatal_errno(errno, disk->name);
if (errno == EIO) {
if (is_hw(errno)) {
++io_error;
} else {
++soft_error;

View file

@ -30,32 +30,12 @@
static const char* es(int err)
{
if (err == EIO)
if (is_hw(err))
return "error_io";
else
return "error";
}
static void log_fatal_errno(int err, const char* name)
{
if (err == EIO) {
log_fatal(err, "DANGER! Unexpected input/output error in disk %s. It isn't possible to continue.\n", name);
} else if (err == EACCES) {
log_fatal(err, "WARNING! Grant permission in the disk %s. It isn't possible to continue.\n", name);
} else if (err == ENOSPC) {
log_fatal(err, "WARNING! Ensure there is free space on the disk %s. It isn't possible to continue.\n", name);
} else {
log_fatal(err, "WARNING! Without a working %s disk, it isn't possible to continue.\n", name);
}
}
static void log_error_errno(int err, const char* name)
{
if (err == EIO) {
log_fatal(err, "DANGER! Unexpected input/output error in disk %s.\n", name);
}
}
/**
* Buffer for storing the new hashes.
*/
@ -174,7 +154,7 @@ static void scrub_data_reader(struct snapraid_worker* worker, struct snapraid_ta
log_fatal_errno(errno, disk->name);
log_fatal(errno, "Stopping at block %u\n", blockcur);
if (errno == EIO) {
if (is_hw(errno)) {
task->state = TASK_STATE_IOERROR;
} else {
task->state = TASK_STATE_ERROR;
@ -187,7 +167,7 @@ static void scrub_data_reader(struct snapraid_worker* worker, struct snapraid_ta
ret = handle_open(handle, task->file, state->file_mode, log_error, 0); /* for missing file don't output a message */
if (ret == -1) {
log_tag("%s:%u:%s:%s: Open error. %s.\n", es(errno), blockcur, disk->name, esc_tag(task->file->sub, esc_buffer), strerror(errno));
if (errno == EIO) {
if (is_hw(errno)) {
/* LCOV_EXCL_START */
log_fatal_errno(errno, disk->name);
log_fatal(errno, "Stopping at block %u\n", blockcur);
@ -218,7 +198,7 @@ static void scrub_data_reader(struct snapraid_worker* worker, struct snapraid_ta
task->read_size = handle_read(handle, task->file_pos, buffer, state->block_size, log_error, 0);
if (task->read_size == -1) {
log_tag("%s:%u:%s:%s: Read error at position %u. %s.\n", es(errno), blockcur, disk->name, esc_tag(task->file->sub, esc_buffer), task->file_pos, strerror(errno));
if (errno == EIO) {
if (is_hw(errno)) {
/* LCOV_EXCL_START */
log_error_errno(errno, disk->name);
task->state = TASK_STATE_IOERROR_CONTINUE;
@ -250,7 +230,7 @@ static void scrub_parity_reader(struct snapraid_worker* worker, struct snapraid_
ret = parity_read(parity_handle, blockcur, buffer, state->block_size, log_error);
if (ret == -1) {
log_tag("parity_%s:%u:%s: Read error. %s.\n", es(errno), blockcur, lev_config_name(level), strerror(errno));
if (errno == EIO) {
if (is_hw(errno)) {
/* LCOV_EXCL_START */
log_error_errno(errno, lev_config_name(level));
task->state = TASK_STATE_IOERROR_CONTINUE;
@ -742,7 +722,7 @@ bail:
log_tag("%s:%u:%s:%s: Close error. %s.\n", es(errno), blockcur, disk->name, esc_tag(file->sub, esc_buffer), strerror(errno));
log_fatal_errno(errno, disk->name);
if (errno == EIO) {
if (is_hw(errno)) {
++io_error;
} else {
++soft_error;

View file

@ -133,6 +133,114 @@ uint64_t random_u64(void)
return z ^ (z >> 31);
}
/****************************************************************************/
/* error */
int is_hw(int err)
{
if (err == EIO)
return 1;
#ifdef EFSCORRUPTED
if (err == EFSCORRUPTED) /* used by XFS inside the kernel - never expected in user mode as converted to EUCLEAN */
return 1;
#endif
#ifdef EUCLEAN
if (err == EUCLEAN) /* used by XFS at user level instead of EFSCORRUPTED */
return 1;
#endif
#ifdef EBADCRC
if (err == EBADCRC) /* used by BTRFS - never expected in user mode as converted to EIO */
return 1;
#endif
#ifdef EBADMSG
if (err == EBADMSG) /* used by BTRFS - never expected in user mode as converted to EIO */
return 1;
#endif
#ifdef ENOMEDIUM
if (err == ENOMEDIUM) /* medium removed */
return 1;
#endif
#ifdef EHWPOISON
if (err == EHWPOISON) /* hardware memory error */
return 1;
#endif
return 0;
}
void log_fatal_errno(int err, const char* name)
{
if (err == EIO) {
log_fatal(err, "DANGER! Unexpected input/output error in disk %s. It isn't possible to continue.\n", name);
#ifdef EFSCORRUPTED
} else if (err == EFSCORRUPTED) {
log_fatal(err, "DANGER! Unexpected corrupted error in disk %s. It isn't possible to continue.\n", name);
#endif
#ifdef EUCLEAN
} else if (err == EUCLEAN) {
log_fatal(err, "DANGER! Unexpected corrupted error in disk %s. It isn't possible to continue.\n", name);
#endif
#ifdef EBADCRC
} else if (err == EBADCRC) {
log_fatal(err, "DANGER! Unexpected bad crc error in disk %s. It isn't possible to continue.\n", name);
#endif
#ifdef EBADMSG
} else if (err == EBADMSG) {
log_fatal(err, "DANGER! Unexpected bad crc error in disk %s. It isn't possible to continue.\n", name);
#endif
#ifdef ENOMEDIUM
} else if (err == ENOMEDIUM) {
log_fatal(err, "DANGER! Unexpected medium removed in disk %s. It isn't possible to continue.\n", name);
#endif
#ifdef EHWPOISON
} else if (err == EHWPOISON) {
log_fatal(err, "DANGER! Unexpected memory error in disk %s. It isn't possible to continue.\n", name);
#endif
} else if (err == EACCES) {
log_fatal(err, "WARNING! Grant permission in the disk %s. It isn't possible to continue.\n", name);
} else if (err == ENOSPC) {
log_fatal(err, "WARNING! Ensure there is free space on the disk %s. It isn't possible to continue.\n", name);
} else {
log_fatal(err, "WARNING! Without a working %s disk, it isn't possible to continue.\n", name);
}
}
void log_error_errno(int err, const char* name)
{
if (err == EIO) {
log_fatal(err, "DANGER! Unexpected input/output error in disk %s.\n", name);
#ifdef EFSCORRUPTED
} else if (err == EFSCORRUPTED) {
log_fatal(err, "DANGER! Unexpected corrupted error in disk %s.\n", name);
#endif
#ifdef EUCLEAN
} else if (err == EUCLEAN) {
log_fatal(err, "DANGER! Unexpected corrupted error in disk %s.\n", name);
#endif
#ifdef EBADCRC
} else if (err == EBADCRC) {
log_fatal(err, "DANGER! Unexpected bad crc error in disk %s.\n", name);
#endif
#ifdef EBADMSG
} else if (err == EBADMSG) {
log_fatal(err, "DANGER! Unexpected bad crc error in disk %s.\n", name);
#endif
#ifdef ENOMEDIUM
} else if (err == ENOMEDIUM) {
log_fatal(err, "DANGER! Unexpected medium removed in disk %s.\n", name);
#endif
#ifdef EHWPOISON
} else if (err == EHWPOISON) {
log_fatal(err, "DANGER! Unexpected memory error in disk %s.\n", name);
#endif
} else if (err == EACCES) {
log_error(err, "WARNING! Grant permission in the disk %s.\n", name);
} else if (err == ENOSPC) {
log_error(err, "WARNING! Ensure there is free space on the disk %s.\n", name);
} else if (err == ENOENT) {
log_error(err, "WARNING! You cannot modify files while running.\n");
}
}
/****************************************************************************/
/* print */
@ -211,7 +319,7 @@ void log_fatal(int err, const char* format, ...)
lock_msg();
if (stdlog) {
if (err == EIO)
if (is_hw(err))
fprintf(stdlog, "msg:fatal_hardware: ");
else
fprintf(stdlog, "msg:fatal: ");
@ -239,7 +347,7 @@ void log_error(int err, const char* format, ...)
lock_msg();
if (stdlog) {
if (err == EIO)
if (is_hw(err))
fprintf(stdlog, "msg:error_hardware: ");
else
fprintf(stdlog, "msg:error: ");
@ -267,7 +375,7 @@ void log_expected(int err, const char* format, ...)
lock_msg();
if (stdlog) {
if (err == EIO)
if (is_hw(err))
fprintf(stdlog, "msg:error_hardware: "); /* we never expect an hardware error */
else
fprintf(stdlog, "msg:expected: ");

View file

@ -70,6 +70,20 @@ unsigned char random_u8(void);
*/
uint64_t random_u64(void);
/****************************************************************************/
/* error */
/**
* Return if the error is at hardware level
*/
int is_hw(int err);
/**
* Log errors with standard messages
*/
void log_fatal_errno(int err, const char* name);
void log_error_errno(int err, const char* name);
/****************************************************************************/
/* log */

View file

@ -30,40 +30,12 @@
static const char* es(int err)
{
if (err == EIO)
if (is_hw(err))
return "error_io";
else
return "error";
}
static void log_fatal_errno(int err, const char* name)
{
if (err == EIO) {
log_fatal(err, "DANGER! Unexpected input/output error in disk %s. It isn't possible to continue.\n", name);
} else if (err == EACCES) {
log_fatal(err, "WARNING! Grant permission in the disk %s. It isn't possible to continue.\n", name);
} else if (err == ENOSPC) {
log_fatal(err, "WARNING! Ensure there is free space on the disk %s. It isn't possible to continue.\n", name);
} else {
log_fatal(err, "WARNING! Without a working %s disk, it isn't possible to continue.\n", name);
}
}
static void log_error_errno(int err, const char* name)
{
if (err == EIO) {
log_fatal(err, "DANGER! Unexpected input/output error in disk %s.\n", name);
} else if (err == EACCES) {
log_error(err, "WARNING! Grant permission in the disk %s.\n", name);
log_error(err, "Rerun the sync command when finished.\n");
} else if (err == ENOSPC) {
log_error(err, "WARNING! Ensure there is free space on the disk %s.\n", name);
} else if (err == ENOENT) {
log_error(err, "WARNING! You cannot modify files during a sync.\n");
log_error(err, "Rerun the sync command when finished.\n");
}
}
static int state_hash_process(struct snapraid_state* state, block_off_t blockstart, block_off_t blockmax, int* skip_sync)
{
struct snapraid_handle* handle;
@ -183,7 +155,7 @@ static int state_hash_process(struct snapraid_state* state, block_off_t blocksta
log_fatal_errno(errno, disk->name);
log_fatal(errno, "Stopping at block %u\n", blockcur);
if (errno == EIO) {
if (is_hw(errno)) {
++io_error;
} else {
++soft_error;
@ -216,7 +188,7 @@ static int state_hash_process(struct snapraid_state* state, block_off_t blocksta
/* LCOV_EXCL_START */
log_fatal_errno(errno, disk->name);
if (errno == EIO) {
if (is_hw(errno)) {
log_fatal(errno, "Stopping at block %u\n", blockcur);
++io_error;
} else {
@ -259,7 +231,7 @@ static int state_hash_process(struct snapraid_state* state, block_off_t blocksta
log_tag("%s:%u:%s:%s: Read error at position %u. %s.\n", es(errno), blockcur, disk->name, esc_tag(file->sub, esc_buffer), file_pos, strerror(errno));
log_fatal_errno(errno, disk->name);
if (errno == EIO) {
if (is_hw(errno)) {
log_fatal(errno, "Stopping at block %u\n", blockcur);
++io_error;
} else {
@ -349,7 +321,7 @@ static int state_hash_process(struct snapraid_state* state, block_off_t blocksta
log_fatal_errno(errno, disk->name);
log_fatal(errno, "Stopping at block %u\n", blockmax);
if (errno == EIO) {
if (is_hw(errno)) {
++io_error;
} else {
++soft_error;
@ -398,7 +370,7 @@ bail:
log_tag("%s:%u:%s:%s: Close error. %s.\n", es(errno), blockcur, disk->name, esc_tag(file->sub, esc_buffer), strerror(errno));
log_fatal_errno(errno, disk->name);
if (errno == EIO) {
if (is_hw(errno)) {
++io_error;
} else {
++soft_error;
@ -549,7 +521,7 @@ static void sync_data_reader(struct snapraid_worker* worker, struct snapraid_tas
log_fatal_errno(errno, disk->name);
log_fatal(errno, "Stopping at block %u\n", blockcur);
if (errno == EIO) {
if (is_hw(errno)) {
task->state = TASK_STATE_IOERROR;
} else {
task->state = TASK_STATE_ERROR;
@ -582,7 +554,7 @@ static void sync_data_reader(struct snapraid_worker* worker, struct snapraid_tas
/* LCOV_EXCL_START */
log_fatal_errno(errno, disk->name);
if (errno == EIO) {
if (is_hw(errno)) {
log_fatal(errno, "Stopping at block %u\n", blockcur);
task->state = TASK_STATE_IOERROR;
} else {
@ -621,7 +593,7 @@ static void sync_data_reader(struct snapraid_worker* worker, struct snapraid_tas
/* LCOV_EXCL_START */
log_tag("%s:%u:%s:%s: Read error at position %u. %s.\n", es(errno), blockcur, disk->name, esc_tag(task->file->sub, esc_buffer), task->file_pos, strerror(errno));
if (errno == EIO) {
if (is_hw(errno)) {
log_error_errno(errno, disk->name);
/* continue until the error limit is reached */
task->state = TASK_STATE_IOERROR_CONTINUE;
@ -656,7 +628,7 @@ static void sync_parity_writer(struct snapraid_worker* worker, struct snapraid_t
/* LCOV_EXCL_START */
log_tag("parity_%s:%u:%s: Write error. %s.\n", es(errno), blockcur, lev_config_name(level), strerror(errno));
if (errno == EIO) {
if (is_hw(errno)) {
log_error_errno(errno, lev_config_name(level));
/* continue until the error limit is reached */
task->state = TASK_STATE_IOERROR_CONTINUE;
@ -1091,7 +1063,7 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari
if (ret == -1) {
/* LCOV_EXCL_START */
log_tag("parity_%s:%u:%s: Read error. %s.\n", es(errno), blockcur, lev_config_name(l), strerror(errno));
if (errno == EIO) {
if (is_hw(errno)) {
log_error_errno(errno, lev_config_name(l));
if (io_error >= state->opt.io_error_limit) {
log_fatal(errno, "DANGER! Too many input/output errors in the %s disk. It isn't possible to continue.\n", lev_config_name(l));
@ -1441,7 +1413,7 @@ bail:
log_tag("%s:%u:%s:%s: Close error. %s.\n", es(errno), blockcur, disk->name, esc_tag(file->sub, esc_buffer), strerror(errno));
log_fatal_errno(errno, disk->name);
if (errno == EIO) {
if (is_hw(errno)) {
++io_error;
} else {
++soft_error;