From: Kai Makisara <kai.makisara@xxxxxxxxxxx> The change for commit 9604eea5bd3a ("scsi: st: Add third party poweron reset handling") was introduced to handle the problem where an unexpected power/on reset resulted in an undetected position lost condition which caused lost data as the driver flushed data and wrote a file mark at the beginning of the tape. To solve this problem code was added which detected the unexpected a power on/reset Unit Attention in st_chk_results(). This correctly set pos_unknown in the driver and prevented further access to the device until the tape is re-positioned or rewound. The problem with this solution is that it is catching POR Unit Attentions that are expected as well as those that are unexpected. This results in an unwanted EIO response to MTIOCGET. The change for commit 3d882cca73be ("scsi: st: Fix input/output error on empty drive reset") attempts to fix this problem by returning success in flush_buffer() when ready != to ST_READY. However, once ST_READY is set, if pos_unknown remains uncleared, MTIOGET can still return EIO. This is confusing as some tape drives will set a POR UA when the system reboots or the driver is reloaded. Fixes: 3d882cca73be ("scsi: st: Fix input/output error on empty drive reset") Closes: https://bugzilla.kernel.org/show_bug.cgi?id=219419 Signed-off-by: Kai Makisara <kai.makisara@xxxxxxxxxxx> Tested-by: John Meneghini <jmeneghi@xxxxxxxxxx> --- drivers/scsi/st.c | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index e9d1cb6c8a86..0260361d19fa 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -3538,6 +3538,7 @@ static long st_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg) int i, cmd_nr, cmd_type, bt; int retval = 0; unsigned int blk; + bool cmd_mtiocget; struct scsi_tape *STp = file->private_data; struct st_modedef *STm; struct st_partstat *STps; @@ -3651,6 +3652,7 @@ static long st_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg) */ if (mtc.mt_op != MTREW && mtc.mt_op != MTOFFL && + mtc.mt_op != MTLOAD && mtc.mt_op != MTRETEN && mtc.mt_op != MTERASE && mtc.mt_op != MTSEEK && @@ -3764,17 +3766,28 @@ static long st_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg) goto out; } + cmd_mtiocget = cmd_type == _IOC_TYPE(MTIOCGET) && cmd_nr == _IOC_NR(MTIOCGET); + if ((i = flush_buffer(STp, 0)) < 0) { - retval = i; - goto out; - } - if (STp->can_partitions && - (i = switch_partition(STp)) < 0) { - retval = i; - goto out; + /* flush fails -> modify status accordingly */ + if (cmd_mtiocget && STp->pos_unknown) { + reset_state(STp); + STp->pos_unknown = 1; + } else { /* return error */ + retval = i; + goto out; + } + } else { /* flush_buffer succeeds */ + if (STp->can_partitions) { + i = switch_partition(STp); + if (i < 0) { + retval = i; + goto out; + } + } } - if (cmd_type == _IOC_TYPE(MTIOCGET) && cmd_nr == _IOC_NR(MTIOCGET)) { + if (cmd_mtiocget) { struct mtget mt_status; if (_IOC_SIZE(cmd_in) != sizeof(struct mtget)) { @@ -3788,7 +3801,7 @@ static long st_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg) ((STp->density << MT_ST_DENSITY_SHIFT) & MT_ST_DENSITY_MASK); mt_status.mt_blkno = STps->drv_block; mt_status.mt_fileno = STps->drv_file; - if (STp->block_size != 0) { + if (STp->block_size != 0 && mt_status.mt_blkno >= 0) { if (STps->rw == ST_WRITING) mt_status.mt_blkno += (STp->buffer)->buffer_bytes / STp->block_size; -- 2.47.0