From a355bc9994fd519e737214fea831c034504dc12d Mon Sep 17 00:00:00 2001 From: Jorge Acosta Date: Mon, 27 Jul 2026 16:19:54 -0600 Subject: [PATCH 01/10] fix: Retrying write operations only after repositioning the drive. fix: Fixing compilation fix: Retrying write operations only after repositioning the drive --- src/tape_drivers/linux/sg/sg_tape.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/tape_drivers/linux/sg/sg_tape.c b/src/tape_drivers/linux/sg/sg_tape.c index 8a13493e..edcf0988 100644 --- a/src/tape_drivers/linux/sg/sg_tape.c +++ b/src/tape_drivers/linux/sg/sg_tape.c @@ -104,6 +104,7 @@ struct sg_global_data global_data; #define MAX_RETRY (100) #define MAX_TAKE_DUMP_ATTEMPTS (10) +#define SOFT_ERROR_MAX_RETRIES (3) /* Forward references (For keep function order to struct tape_ops) */ int sg_readpos(void *device, struct tc_position *pos); @@ -2098,7 +2099,8 @@ int sg_write(void *device, const char *buf, size_t count, struct tc_position *po struct sg_data *priv = (struct sg_data*)device; struct tc_position cur_pos; size_t datacount = count; - int retry_count = 0; + int reconnect_retry_count = 0, soft_error_retry_count = 0; + int TBL_SLEEP_SECS[SOFT_ERROR_MAX_RETRIES] = {45, 60, 75}; // Hardcoded exponentially increasing sleep time ltfs_profiler_add_entry(priv->profiler, NULL, TAPEBEND_REQ_ENTER(REQ_TC_WRITE)); @@ -2145,7 +2147,12 @@ int sg_write(void *device, const char *buf, size_t count, struct tc_position *po } else ret = -EDEV_POR_OR_BUS_RESET; } - } else if (ret == -EDEV_BUFFER_ALLOCATE_ERROR && retry_count < MAX_RETRY) { + } else if (ret == -EDEV_BUFFER_ALLOCATE_ERROR && reconnect_retry_count < MAX_RETRY) { + ret = _handle_block_allocation_failure(device, pos, &reconnect_retry_count, "write"); + if (ret == -EDEV_RETRY) + goto start_write; + } else if (ret == -EDEV_HOST_ERROR && soft_error_retry_count < SOFT_ERROR_MAX_RETRIES) { + sleep(TBL_SLEEP_SECS[soft_error_retry_count]); ret = _handle_block_allocation_failure(device, pos, &retry_count, "write"); if (ret == -EDEV_RETRY) goto start_write; From a6b99580123377b2b30e840f76813621d6f1f326 Mon Sep 17 00:00:00 2001 From: mcardenas Date: Wed, 29 Jul 2026 16:16:11 -0600 Subject: [PATCH 02/10] fix: retry operations _clear_por changes - clear the previous power on reset status and retry - retry in both ibmtape netbsd driver and the sg one - make _clear_por return an int fix: minor changes chore: revert separation of por and allocation issues feat: make block failure generic --- src/tape_drivers/linux/sg/sg_tape.c | 40 ++++++++++++++++------------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/src/tape_drivers/linux/sg/sg_tape.c b/src/tape_drivers/linux/sg/sg_tape.c index edcf0988..f736ab80 100644 --- a/src/tape_drivers/linux/sg/sg_tape.c +++ b/src/tape_drivers/linux/sg/sg_tape.c @@ -54,6 +54,7 @@ #include #include +#include "libltfs/ltfs_error.h" #include "ltfs_copyright.h" #include "libltfs/ltfslogging.h" #include "libltfs/fs.h" @@ -104,7 +105,7 @@ struct sg_global_data global_data; #define MAX_RETRY (100) #define MAX_TAKE_DUMP_ATTEMPTS (10) -#define SOFT_ERROR_MAX_RETRIES (3) +#define POR_MAX_RETRIES (3) /* Forward references (For keep function order to struct tape_ops) */ int sg_readpos(void *device, struct tc_position *pos); @@ -605,7 +606,7 @@ int _raw_tur(const int fd) #define _clear_por(p) _clear_por_raw((p)->dev.fd); -void _clear_por_raw(const int fd) +int _clear_por_raw(const int fd) { int i = 0, ret = -1; @@ -625,6 +626,7 @@ void _clear_por_raw(const int fd) } i++; } + return ret; } #define _get_stable_tur_response(p) _get_stable_tur_response_raw((p)->dev.fd) @@ -1862,16 +1864,11 @@ static int _cdb_read(void *device, char *buf, size_t size, bool sili) return length; } -static inline int _handle_block_allocation_failure(void *device, struct tc_position *pos, - int *retry, char *op) +static inline int _handle_block_write_failure(void *device, struct tc_position *pos, char *op) { int ret = 0; struct tc_position tmp_pos = {0, 0}; - /* Sleep 3 secs to wait garbage correction in kernel side and retry */ - ltfsmsg(LTFS_WARN, 30277W, ++(*retry)); - sleep(3); - ret = sg_readpos(device, &tmp_pos); if (ret == DEVICE_GOOD && pos->partition == tmp_pos.partition) { if (pos->block == tmp_pos.block) { @@ -1985,7 +1982,9 @@ int sg_read(void *device, char *buf, size_t size, priv->use_sili = false; ret = _cdb_read(device, buf, datacount, unusual_size); } else if (ret == -EDEV_BUFFER_ALLOCATE_ERROR && retry_count < MAX_RETRY) { - ret = _handle_block_allocation_failure(device, pos, &retry_count, "read"); + sleep(3); // Wait for kernel GC + ltfsmsg(LTFS_WARN, 30277W, ++retry_count); + ret = _handle_block_write_failure(device, pos, "read"); if (ret == -EDEV_RETRY) goto start_read; } @@ -2099,8 +2098,7 @@ int sg_write(void *device, const char *buf, size_t count, struct tc_position *po struct sg_data *priv = (struct sg_data*)device; struct tc_position cur_pos; size_t datacount = count; - int reconnect_retry_count = 0, soft_error_retry_count = 0; - int TBL_SLEEP_SECS[SOFT_ERROR_MAX_RETRIES] = {45, 60, 75}; // Hardcoded exponentially increasing sleep time + int retry_count = 0, por_retry_count = 0; ltfs_profiler_add_entry(priv->profiler, NULL, TAPEBEND_REQ_ENTER(REQ_TC_WRITE)); @@ -2147,15 +2145,21 @@ int sg_write(void *device, const char *buf, size_t count, struct tc_position *po } else ret = -EDEV_POR_OR_BUS_RESET; } - } else if (ret == -EDEV_BUFFER_ALLOCATE_ERROR && reconnect_retry_count < MAX_RETRY) { - ret = _handle_block_allocation_failure(device, pos, &reconnect_retry_count, "write"); - if (ret == -EDEV_RETRY) - goto start_write; - } else if (ret == -EDEV_HOST_ERROR && soft_error_retry_count < SOFT_ERROR_MAX_RETRIES) { - sleep(TBL_SLEEP_SECS[soft_error_retry_count]); - ret = _handle_block_allocation_failure(device, pos, &retry_count, "write"); + } else if (ret == -EDEV_BUFFER_ALLOCATE_ERROR && retry_count < MAX_RETRY) { + sleep(3); // Wait for kernel GC + ltfsmsg(LTFS_WARN, 30277W, ++retry_count); + ret = _handle_block_write_failure(device, pos, "write"); if (ret == -EDEV_RETRY) goto start_write; + } else if (ret == -EDEV_HOST_ERROR && por_retry_count < POR_MAX_RETRIES) { + por_retry_count++; + sleep(5); + ret = _clear_por(priv); + if (ret == DEVICE_GOOD) { + ret = _handle_block_write_failure(device, pos, "write"); + if (ret == -EDEV_RETRY) + goto start_write; + } } ltfs_profiler_add_entry(priv->profiler, NULL, TAPEBEND_REQ_EXIT(REQ_TC_WRITE)); From 80f081638d5f66c726ae567214a429d5ef0b5212 Mon Sep 17 00:00:00 2001 From: syaoraang Date: Fri, 31 Jul 2026 13:49:05 -0600 Subject: [PATCH 03/10] chore: Fixing identation fix: Avoiding hidding cdb_write return code fix: Using correct return value to check write output --- src/tape_drivers/linux/sg/sg_tape.c | 32 ++++++++++++++--------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/tape_drivers/linux/sg/sg_tape.c b/src/tape_drivers/linux/sg/sg_tape.c index f736ab80..22b420b8 100644 --- a/src/tape_drivers/linux/sg/sg_tape.c +++ b/src/tape_drivers/linux/sg/sg_tape.c @@ -1982,8 +1982,8 @@ int sg_read(void *device, char *buf, size_t size, priv->use_sili = false; ret = _cdb_read(device, buf, datacount, unusual_size); } else if (ret == -EDEV_BUFFER_ALLOCATE_ERROR && retry_count < MAX_RETRY) { - sleep(3); // Wait for kernel GC - ltfsmsg(LTFS_WARN, 30277W, ++retry_count); + sleep(3); // Wait for kernel GC + ltfsmsg(LTFS_WARN, 30277W, ++retry_count); ret = _handle_block_write_failure(device, pos, "read"); if (ret == -EDEV_RETRY) goto start_read; @@ -2093,7 +2093,7 @@ static int _cdb_write(void *device, uint8_t *buf, size_t size, bool *ew, bool *p int sg_write(void *device, const char *buf, size_t count, struct tc_position *pos) { - int ret, ret_fo; + int ret, ret_fo, ret_write = -1; bool ew = false, pew = false; struct sg_data *priv = (struct sg_data*)device; struct tc_position cur_pos; @@ -2128,12 +2128,12 @@ int sg_write(void *device, const char *buf, size_t count, struct tc_position *po } start_write: - ret = _cdb_write(device, (uint8_t *)buf, datacount, &ew, &pew); - if (ret == DEVICE_GOOD) { + ret_write = _cdb_write(device, (uint8_t *)buf, datacount, &ew, &pew); + if (ret_write == DEVICE_GOOD) { pos->block++; pos->early_warning = ew; pos->programmable_early_warning = pew; - } else if (ret == -EDEV_NEED_FAILOVER) { + } else if (ret_write == -EDEV_NEED_FAILOVER) { ret_fo = sg_readpos(device, &cur_pos); if (!ret_fo) { if (pos->partition == cur_pos.partition @@ -2141,30 +2141,30 @@ int sg_write(void *device, const char *buf, size_t count, struct tc_position *po pos->block++; pos->early_warning = cur_pos.early_warning; pos->programmable_early_warning = cur_pos.programmable_early_warning; - ret = DEVICE_GOOD; + ret = ret_write = DEVICE_GOOD; } else ret = -EDEV_POR_OR_BUS_RESET; } - } else if (ret == -EDEV_BUFFER_ALLOCATE_ERROR && retry_count < MAX_RETRY) { - sleep(3); // Wait for kernel GC - ltfsmsg(LTFS_WARN, 30277W, ++retry_count); + } else if (ret_write == -EDEV_BUFFER_ALLOCATE_ERROR && retry_count < MAX_RETRY) { + sleep(3); // Wait for kernel GC + ltfsmsg(LTFS_WARN, 30277W, ++retry_count); ret = _handle_block_write_failure(device, pos, "write"); if (ret == -EDEV_RETRY) goto start_write; - } else if (ret == -EDEV_HOST_ERROR && por_retry_count < POR_MAX_RETRIES) { - por_retry_count++; + } else if (ret_write == -EDEV_HOST_ERROR && por_retry_count < POR_MAX_RETRIES) { + por_retry_count++; sleep(5); ret = _clear_por(priv); if (ret == DEVICE_GOOD) { - ret = _handle_block_write_failure(device, pos, "write"); - if (ret == -EDEV_RETRY) - goto start_write; + ret = _handle_block_write_failure(device, pos, "write"); + if (ret == -EDEV_RETRY) + goto start_write; } } ltfs_profiler_add_entry(priv->profiler, NULL, TAPEBEND_REQ_EXIT(REQ_TC_WRITE)); - return ret; + return ret_write; } int sg_writefm(void *device, size_t count, struct tc_position *pos, bool immed) From 1ddf91e4165bae0382aedcecbb15c4117cfa1981 Mon Sep 17 00:00:00 2001 From: mcardenas Date: Wed, 5 Aug 2026 11:18:43 -0600 Subject: [PATCH 04/10] fix: ret and netbsd driver fix: --- src/tape_drivers/linux/sg/sg_tape.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/tape_drivers/linux/sg/sg_tape.c b/src/tape_drivers/linux/sg/sg_tape.c index 22b420b8..49573b15 100644 --- a/src/tape_drivers/linux/sg/sg_tape.c +++ b/src/tape_drivers/linux/sg/sg_tape.c @@ -1982,8 +1982,8 @@ int sg_read(void *device, char *buf, size_t size, priv->use_sili = false; ret = _cdb_read(device, buf, datacount, unusual_size); } else if (ret == -EDEV_BUFFER_ALLOCATE_ERROR && retry_count < MAX_RETRY) { - sleep(3); // Wait for kernel GC ltfsmsg(LTFS_WARN, 30277W, ++retry_count); + sleep(3); // Wait for kernel GC ret = _handle_block_write_failure(device, pos, "read"); if (ret == -EDEV_RETRY) goto start_read; @@ -2128,12 +2128,12 @@ int sg_write(void *device, const char *buf, size_t count, struct tc_position *po } start_write: - ret_write = _cdb_write(device, (uint8_t *)buf, datacount, &ew, &pew); - if (ret_write == DEVICE_GOOD) { + ret_write = ret = _cdb_write(device, (uint8_t *)buf, datacount, &ew, &pew); + if (ret == DEVICE_GOOD) { pos->block++; pos->early_warning = ew; pos->programmable_early_warning = pew; - } else if (ret_write == -EDEV_NEED_FAILOVER) { + } else if (ret == -EDEV_NEED_FAILOVER) { ret_fo = sg_readpos(device, &cur_pos); if (!ret_fo) { if (pos->partition == cur_pos.partition @@ -2141,30 +2141,32 @@ int sg_write(void *device, const char *buf, size_t count, struct tc_position *po pos->block++; pos->early_warning = cur_pos.early_warning; pos->programmable_early_warning = cur_pos.programmable_early_warning; - ret = ret_write = DEVICE_GOOD; + ret = DEVICE_GOOD; } else ret = -EDEV_POR_OR_BUS_RESET; } - } else if (ret_write == -EDEV_BUFFER_ALLOCATE_ERROR && retry_count < MAX_RETRY) { - sleep(3); // Wait for kernel GC + } else if (ret == -EDEV_BUFFER_ALLOCATE_ERROR && retry_count < MAX_RETRY) { ltfsmsg(LTFS_WARN, 30277W, ++retry_count); + sleep(3); // Wait for kernel GC ret = _handle_block_write_failure(device, pos, "write"); if (ret == -EDEV_RETRY) goto start_write; - } else if (ret_write == -EDEV_HOST_ERROR && por_retry_count < POR_MAX_RETRIES) { + } else if (ret == -EDEV_HOST_ERROR && por_retry_count < POR_MAX_RETRIES) { por_retry_count++; sleep(5); ret = _clear_por(priv); if (ret == DEVICE_GOOD) { - ret = _handle_block_write_failure(device, pos, "write"); - if (ret == -EDEV_RETRY) - goto start_write; - } + ret = _handle_block_write_failure(device, pos, "write"); + if (ret == -EDEV_RETRY) + goto start_write; + ret = DEVICE_GOOD; + } else + ret = ret_write; } ltfs_profiler_add_entry(priv->profiler, NULL, TAPEBEND_REQ_EXIT(REQ_TC_WRITE)); - return ret_write; + return ret; } int sg_writefm(void *device, size_t count, struct tc_position *pos, bool immed) From 94a23c2647d474a0d419ee30606571341f0c5deb Mon Sep 17 00:00:00 2001 From: Jorge Acosta Date: Wed, 12 Aug 2026 11:12:12 -0600 Subject: [PATCH 05/10] fix: Attending Missael's requests fix: Attending Missael's requests fix: Attending Missael's requests fix: write soft error handling (POR and transport failures) Fix: start_block misalignment caused by SIGSTOP (#552) fix: start_block misalignment caused by position mismatch #11 fix: Fixing compilation fix: Fixing compilation chore: Attending Japan team comments * Handling better the return code that was forcefully set as DEVICE_GOOD * Renaming _handle_block_write_failure() to _resolve_position_after_io_cmd_failure() to reduce naming concerns and yet don't repeat code. We don't take inconsideration the EDEV_BUFFER_ALLOCATE_ERROR when called by -EDEV_HOST_ERROR since it doesn't affect that path. fix: Avoiding shadowing the _cdb_write() return value if _clear_por() return value is not DEVICE_GOOD. Fixing in scsipi_ibmtape.c file an issue where the ret_write wasn't changed never but returned anyways. chore: Adding log to mention the retry due the POR chore: Attending Japan team comments on POR issue PR --- messages/tape_linux_sg/root.txt | 1 + src/tape_drivers/linux/sg/sg_tape.c | 35 +++++++++++++++++++---------- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/messages/tape_linux_sg/root.txt b/messages/tape_linux_sg/root.txt index 981fdc1b..e4f11b3e 100644 --- a/messages/tape_linux_sg/root.txt +++ b/messages/tape_linux_sg/root.txt @@ -138,6 +138,7 @@ root:table { 30295I:string { "Have unstable TUR response, start over (Cur = %d, Prev = %d)." } 30296I:string { "Capturing a stable TUR at line %d." } 30297W:string { "Cannot retrieve drive dump: failed to communicate with drive. Tried (%d) times." } + 30298W:string { "Retrying write operation due Power On-Reset event reached. Tried (%d) times." } 30392D:string { "Backend %s %s." } 30393D:string { "Backend %s: %d %s." } diff --git a/src/tape_drivers/linux/sg/sg_tape.c b/src/tape_drivers/linux/sg/sg_tape.c index 49573b15..de405bd9 100644 --- a/src/tape_drivers/linux/sg/sg_tape.c +++ b/src/tape_drivers/linux/sg/sg_tape.c @@ -1864,7 +1864,7 @@ static int _cdb_read(void *device, char *buf, size_t size, bool sili) return length; } -static inline int _handle_block_write_failure(void *device, struct tc_position *pos, char *op) +static inline int _resolve_position_after_io_cmd_failure(void *device, struct tc_position *pos, char *op) { int ret = 0; struct tc_position tmp_pos = {0, 0}; @@ -1983,8 +1983,11 @@ int sg_read(void *device, char *buf, size_t size, ret = _cdb_read(device, buf, datacount, unusual_size); } else if (ret == -EDEV_BUFFER_ALLOCATE_ERROR && retry_count < MAX_RETRY) { ltfsmsg(LTFS_WARN, 30277W, ++retry_count); - sleep(3); // Wait for kernel GC - ret = _handle_block_write_failure(device, pos, "read"); + struct timespec delay_ts; + delay_ts.tv_sec = 3; + delay_ts.tv_nsec = 0; + nanosleep(&delay_ts, NULL); // Wait for kernel GC + ret = _resolve_position_after_io_cmd_failure(device, pos, "read"); if (ret == -EDEV_RETRY) goto start_read; } @@ -2099,6 +2102,7 @@ int sg_write(void *device, const char *buf, size_t count, struct tc_position *po struct tc_position cur_pos; size_t datacount = count; int retry_count = 0, por_retry_count = 0; + struct timespec delay_ts = {0}; ltfs_profiler_add_entry(priv->profiler, NULL, TAPEBEND_REQ_ENTER(REQ_TC_WRITE)); @@ -2147,21 +2151,28 @@ int sg_write(void *device, const char *buf, size_t count, struct tc_position *po } } else if (ret == -EDEV_BUFFER_ALLOCATE_ERROR && retry_count < MAX_RETRY) { ltfsmsg(LTFS_WARN, 30277W, ++retry_count); - sleep(3); // Wait for kernel GC - ret = _handle_block_write_failure(device, pos, "write"); + delay_ts.tv_sec = 3; + delay_ts.tv_nsec = 0; + nanosleep(&delay_ts, NULL); // Wait for kernel GC + ret = _resolve_position_after_io_cmd_failure(device, pos, "write"); if (ret == -EDEV_RETRY) goto start_write; } else if (ret == -EDEV_HOST_ERROR && por_retry_count < POR_MAX_RETRIES) { por_retry_count++; - sleep(5); + delay_ts.tv_sec = 5; + delay_ts.tv_nsec = 0; + nanosleep(&delay_ts, NULL); ret = _clear_por(priv); if (ret == DEVICE_GOOD) { - ret = _handle_block_write_failure(device, pos, "write"); - if (ret == -EDEV_RETRY) - goto start_write; - ret = DEVICE_GOOD; - } else - ret = ret_write; + int handle_ret = _resolve_position_after_io_cmd_failure(device, pos, "write"); + /* If the original command did not reach the driver, or it reached it but after failing there is block mismatch; retry */ + if (handle_ret == -EDEV_RETRY) { + ltfsmsg(LTFS_WARN, 30298W, por_retry_count); + goto start_write; + } + } + // If we could not clear the POR status, just return the _cdb_write() return value + ret = ret_write; } ltfs_profiler_add_entry(priv->profiler, NULL, TAPEBEND_REQ_EXIT(REQ_TC_WRITE)); From 2e084c075387892aca41d3d1995cf42c7b66d6cb Mon Sep 17 00:00:00 2001 From: madjesc Date: Wed, 19 Aug 2026 11:01:51 -0600 Subject: [PATCH 06/10] fix: return is not valid --- src/tape_drivers/linux/sg/sg_tape.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/tape_drivers/linux/sg/sg_tape.c b/src/tape_drivers/linux/sg/sg_tape.c index de405bd9..172954c9 100644 --- a/src/tape_drivers/linux/sg/sg_tape.c +++ b/src/tape_drivers/linux/sg/sg_tape.c @@ -2165,14 +2165,13 @@ int sg_write(void *device, const char *buf, size_t count, struct tc_position *po ret = _clear_por(priv); if (ret == DEVICE_GOOD) { int handle_ret = _resolve_position_after_io_cmd_failure(device, pos, "write"); - /* If the original command did not reach the driver, or it reached it but after failing there is block mismatch; retry */ + /* If the original command did not reach the driver, or it reached it but after failing there is a position mismatch; retry */ if (handle_ret == -EDEV_RETRY) { ltfsmsg(LTFS_WARN, 30298W, por_retry_count); goto start_write; } - } - // If we could not clear the POR status, just return the _cdb_write() return value - ret = ret_write; + } else // If we could not clear the POR status, just return the _cdb_write() return value + ret = ret_write; } ltfs_profiler_add_entry(priv->profiler, NULL, TAPEBEND_REQ_EXIT(REQ_TC_WRITE)); From 24aa07062f2f9c977a1d8699a8293f23e6b168d4 Mon Sep 17 00:00:00 2001 From: Mauricio de Jesus Cardenas Hernandez Date: Wed, 19 Aug 2026 11:56:09 -0600 Subject: [PATCH 07/10] chore: make readability better Implement dylan suggestion Co-authored-by: DYLAN CARLSON --- src/tape_drivers/linux/sg/sg_tape.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/tape_drivers/linux/sg/sg_tape.c b/src/tape_drivers/linux/sg/sg_tape.c index 172954c9..7bbbc356 100644 --- a/src/tape_drivers/linux/sg/sg_tape.c +++ b/src/tape_drivers/linux/sg/sg_tape.c @@ -2170,8 +2170,11 @@ int sg_write(void *device, const char *buf, size_t count, struct tc_position *po ltfsmsg(LTFS_WARN, 30298W, por_retry_count); goto start_write; } - } else // If we could not clear the POR status, just return the _cdb_write() return value - ret = ret_write; + } + // If we could not clear the POR status, just return the _cdb_write() return value + else { + ret = ret_write; + } } ltfs_profiler_add_entry(priv->profiler, NULL, TAPEBEND_REQ_EXIT(REQ_TC_WRITE)); From 4465d15da281c25e3190f73706677b7f06aa36ad Mon Sep 17 00:00:00 2001 From: madjesc Date: Wed, 19 Aug 2026 12:30:30 -0600 Subject: [PATCH 08/10] fix: readibility fix: write soft error handling (POR and transport failures) --- src/tape_drivers/linux/sg/sg_tape.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/tape_drivers/linux/sg/sg_tape.c b/src/tape_drivers/linux/sg/sg_tape.c index 7bbbc356..3d43e897 100644 --- a/src/tape_drivers/linux/sg/sg_tape.c +++ b/src/tape_drivers/linux/sg/sg_tape.c @@ -2170,11 +2170,10 @@ int sg_write(void *device, const char *buf, size_t count, struct tc_position *po ltfsmsg(LTFS_WARN, 30298W, por_retry_count); goto start_write; } + } else { + // If we could not clear the POR status, just return the _cdb_write() return value + ret = ret_write; } - // If we could not clear the POR status, just return the _cdb_write() return value - else { - ret = ret_write; - } } ltfs_profiler_add_entry(priv->profiler, NULL, TAPEBEND_REQ_EXIT(REQ_TC_WRITE)); From ef8789fc462c040b4597f269db9eef1bee8e27bb Mon Sep 17 00:00:00 2001 From: madjesc Date: Wed, 19 Aug 2026 11:01:51 -0600 Subject: [PATCH 09/10] fix: return is not valid chore: make readability better Implement dylan suggestion Co-authored-by: DYLAN CARLSON fix: readibility fix: write soft error handling (POR and transport failures) fix: set SP bit fix: add read retry --- messages/tape_linux_sg/root.txt | 2 +- src/tape_drivers/linux/sg/sg_tape.c | 37 +++++++++++++++++++++++++---- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/messages/tape_linux_sg/root.txt b/messages/tape_linux_sg/root.txt index e4f11b3e..d76bd160 100644 --- a/messages/tape_linux_sg/root.txt +++ b/messages/tape_linux_sg/root.txt @@ -138,7 +138,7 @@ root:table { 30295I:string { "Have unstable TUR response, start over (Cur = %d, Prev = %d)." } 30296I:string { "Capturing a stable TUR at line %d." } 30297W:string { "Cannot retrieve drive dump: failed to communicate with drive. Tried (%d) times." } - 30298W:string { "Retrying write operation due Power On-Reset event reached. Tried (%d) times." } + 30298W:string { "Retrying %s operation due Power On-Reset event reached. Tried (%d) times." } 30392D:string { "Backend %s %s." } 30393D:string { "Backend %s: %d %s." } diff --git a/src/tape_drivers/linux/sg/sg_tape.c b/src/tape_drivers/linux/sg/sg_tape.c index 3d43e897..c4e9a1f1 100644 --- a/src/tape_drivers/linux/sg/sg_tape.c +++ b/src/tape_drivers/linux/sg/sg_tape.c @@ -1921,11 +1921,12 @@ static inline int _resolve_position_after_io_cmd_failure(void *device, struct tc int sg_read(void *device, char *buf, size_t size, struct tc_position *pos, const bool unusual_size) { - int32_t ret = -EDEV_UNKNOWN; + int32_t ret = -EDEV_UNKNOWN, ret_read = -1; struct sg_data *priv = (struct sg_data*)device; size_t datacount = size; struct tc_position pos_retry = {0, 0}; - int retry_count = 0; + int retry_count = 0, por_retry_count = 0; + struct timespec delay_ts = {0}; ltfs_profiler_add_entry(priv->profiler, NULL, TAPEBEND_REQ_ENTER(REQ_TC_READ)); ltfsmsg(LTFS_DEBUG3, 30395D, "read", size, priv->drive_serial); @@ -1950,7 +1951,7 @@ int sg_read(void *device, char *buf, size_t size, } start_read: - ret = _cdb_read(device, buf, datacount, unusual_size); + ret_read = ret = _cdb_read(device, buf, datacount, unusual_size); if (ret == -EDEV_LENGTH_MISMATCH) { if (pos_retry.partition || pos_retry.block) { /* Return error when retry is already executed */ @@ -1990,8 +1991,26 @@ int sg_read(void *device, char *buf, size_t size, ret = _resolve_position_after_io_cmd_failure(device, pos, "read"); if (ret == -EDEV_RETRY) goto start_read; + } else if (ret == -EDEV_HOST_ERROR && por_retry_count < POR_MAX_RETRIES) { + por_retry_count++; + delay_ts.tv_sec = 5; + delay_ts.tv_nsec = 0; + nanosleep(&delay_ts, NULL); + ret = _clear_por(priv); + if (ret == DEVICE_GOOD) { + int handle_ret = _resolve_position_after_io_cmd_failure(device, pos, "read"); + /* If the original command did not reach the driver, or it reached it but after failing there is a position mismatch; retry */ + if (handle_ret == -EDEV_RETRY) { + ltfsmsg(LTFS_WARN, 30298W, "read", por_retry_count); + goto start_read; // NOTE: DO we need to clear the buffer before the retry? + } + } else { + // If we could not clear the POR status, just return the _cbd_read() status + ret = ret_read; + } } + if(ret == -EDEV_FILEMARK_DETECTED) { pos->filemarks++; @@ -2167,7 +2186,7 @@ int sg_write(void *device, const char *buf, size_t count, struct tc_position *po int handle_ret = _resolve_position_after_io_cmd_failure(device, pos, "write"); /* If the original command did not reach the driver, or it reached it but after failing there is a position mismatch; retry */ if (handle_ret == -EDEV_RETRY) { - ltfsmsg(LTFS_WARN, 30298W, por_retry_count); + ltfsmsg(LTFS_WARN, 30298W, "write", por_retry_count); goto start_write; } } else { @@ -3349,7 +3368,15 @@ int sg_modeselect(void *device, unsigned char *buf, const size_t size) /* Build CDB */ cdb[0] = MODE_SELECT10; - cdb[1] = 0x10; /* Set PF bit */ + /* + * Set PF and SP bit + * NOTE: Not all modepages support SP bit. + * The SCSI reference says: + * • SP (Save Pages): Only allowed to be set to one when explicitly mentioned in the description of the + * specific mode page + * Right now SDE and LE does not set any non savable pages, but this could change. + * */ + cdb[1] = 0x11; ltfs_u16tobe(cdb + 7, size); timeout = get_timeout(priv->timeouts, cdb[0]); From 7ee5419c77b2cf0447865140ca4ec4c0fa80603b Mon Sep 17 00:00:00 2001 From: Mauricio Date: Tue, 6 Oct 2026 16:41:40 -0600 Subject: [PATCH 10/10] fix: some errors --- messages/tape_linux_sg/root.txt | 2 ++ src/tape_drivers/linux/sg/sg_tape.c | 30 ++++++++++++++++++----------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/messages/tape_linux_sg/root.txt b/messages/tape_linux_sg/root.txt index d76bd160..8d22abf8 100644 --- a/messages/tape_linux_sg/root.txt +++ b/messages/tape_linux_sg/root.txt @@ -139,6 +139,8 @@ root:table { 30296I:string { "Capturing a stable TUR at line %d." } 30297W:string { "Cannot retrieve drive dump: failed to communicate with drive. Tried (%d) times." } 30298W:string { "Retrying %s operation due Power On-Reset event reached. Tried (%d) times." } + 30299E:string { "Could not clear POR status, device might be faulty, ret: (%d)." } + 30300E:string { "Could resolve block position after failure, ret: (%d)." } 30392D:string { "Backend %s %s." } 30393D:string { "Backend %s: %d %s." } diff --git a/src/tape_drivers/linux/sg/sg_tape.c b/src/tape_drivers/linux/sg/sg_tape.c index c4e9a1f1..d2446ed3 100644 --- a/src/tape_drivers/linux/sg/sg_tape.c +++ b/src/tape_drivers/linux/sg/sg_tape.c @@ -1992,22 +1992,26 @@ int sg_read(void *device, char *buf, size_t size, if (ret == -EDEV_RETRY) goto start_read; } else if (ret == -EDEV_HOST_ERROR && por_retry_count < POR_MAX_RETRIES) { + ltfsmsg(LTFS_WARN, 30298W, "read", por_retry_count); por_retry_count++; delay_ts.tv_sec = 5; delay_ts.tv_nsec = 0; nanosleep(&delay_ts, NULL); ret = _clear_por(priv); if (ret == DEVICE_GOOD) { - int handle_ret = _resolve_position_after_io_cmd_failure(device, pos, "read"); + ret = _resolve_position_after_io_cmd_failure(device, pos, "read"); /* If the original command did not reach the driver, or it reached it but after failing there is a position mismatch; retry */ - if (handle_ret == -EDEV_RETRY) { - ltfsmsg(LTFS_WARN, 30298W, "read", por_retry_count); - goto start_read; // NOTE: DO we need to clear the buffer before the retry? + if (ret == -EDEV_RETRY) { + goto start_read; + } else if (ret == 0) { + ret_read = datacount; // This is OK? Might need to set the block instead of that + } else { + ltfsmsg(LTFS_ERR, 30300E, ret); } } else { - // If we could not clear the POR status, just return the _cbd_read() status - ret = ret_read; + ltfsmsg(LTFS_ERR, 30299E, ret); } + ret = ret_read; } @@ -2177,22 +2181,26 @@ int sg_write(void *device, const char *buf, size_t count, struct tc_position *po if (ret == -EDEV_RETRY) goto start_write; } else if (ret == -EDEV_HOST_ERROR && por_retry_count < POR_MAX_RETRIES) { + ltfsmsg(LTFS_WARN, 30298W, "write", por_retry_count); por_retry_count++; delay_ts.tv_sec = 5; delay_ts.tv_nsec = 0; nanosleep(&delay_ts, NULL); ret = _clear_por(priv); if (ret == DEVICE_GOOD) { - int handle_ret = _resolve_position_after_io_cmd_failure(device, pos, "write"); + ret = _resolve_position_after_io_cmd_failure(device, pos, "write"); /* If the original command did not reach the driver, or it reached it but after failing there is a position mismatch; retry */ - if (handle_ret == -EDEV_RETRY) { - ltfsmsg(LTFS_WARN, 30298W, "write", por_retry_count); + if (ret == -EDEV_RETRY) { goto start_write; + } else if (ret == 0) { + ret_write = datacount; // This is OK? Might need to set the blocksize instead of that + } else { + ltfsmsg(LTFS_ERR, 30300E, ret); } } else { - // If we could not clear the POR status, just return the _cdb_write() return value - ret = ret_write; + ltfsmsg(LTFS_ERR, 30299E, ret); } + ret = ret_write; } ltfs_profiler_add_entry(priv->profiler, NULL, TAPEBEND_REQ_EXIT(REQ_TC_WRITE));