diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index f1c3c4946637..6e5e6f6e3ab6 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -3586,6 +3586,23 @@ static long st_common_ioctl(struct scsi_tape *STp, struct st_modedef *STm, return retval; } +/* + * Re-apply a density and block size that were changed by the user before a + * device reset (a negative value means "not changed"). A unit attention + * still pending after the operation (e.g. new medium after a load) fails the + * first MODE SELECT, so retry each once. As in the other post-reset restore + * path, errors are ignored and one setting failing does not prevent + * restoring the other. + */ +static void st_restore_changed_settings(struct scsi_tape *STp, int density, + int blksize) +{ + if (density >= 0 && st_int_ioctl(STp, MTSETDENSITY, density)) + st_int_ioctl(STp, MTSETDENSITY, density); + if (blksize >= 0 && st_int_ioctl(STp, MTSETBLK, blksize)) + st_int_ioctl(STp, MTSETBLK, blksize); +} + /* The ioctl command */ static long st_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg) { @@ -3594,6 +3611,7 @@ static long st_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg) int retval = 0; unsigned int blk; bool cmd_mtiocget; + int restore_density = -1, restore_blksize = -1; struct scsi_tape *STp = file->private_data; struct st_modedef *STm; struct st_partstat *STps; @@ -3740,6 +3758,17 @@ static long st_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg) st_int_ioctl(STp, MTSETDENSITY, STp->changed_density); if (STp->blksize_changed) st_int_ioctl(STp, MTSETBLK, STp->changed_blksize); + } else if (mtc.mt_op == MTLOAD || mtc.mt_op == MTRETEN) { + /* + * The same medium ends up at BOT, so the settings + * apply as with MTREW. The operation may start a + * new session, which clears the "changed" flags: + * save the values and restore them afterwards. + */ + if (STp->density_changed) + restore_density = STp->changed_density; + if (STp->blksize_changed) + restore_blksize = STp->changed_blksize; } } @@ -3819,6 +3848,14 @@ static long st_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg) if (mtc.mt_op == MTLOAD) { retval = do_load_unload(STp, file, max(1, mtc.mt_count)); + /* + * Restore only if the drive is ready: after an + * immediate-mode load with O_NONBLOCK, check_tape() + * does not wait and the drive may still be loading. + */ + if (!retval && STp->ready == ST_READY) + st_restore_changed_settings(STp, restore_density, + restore_blksize); goto out; } @@ -3837,6 +3874,20 @@ static long st_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg) retval = st_compression(STp, (mtc.mt_count & 1)); else retval = st_int_ioctl(STp, mtc.mt_op, mtc.mt_count); + if (!retval && mtc.mt_op == MTRETEN && !STp->immediate && + (restore_density >= 0 || restore_blksize >= 0)) { + /* + * Retension reloads the medium and the drive may + * report a new medium. Let check_tape() start the new + * session (applying the mode defaults) now, as for + * MTLOAD; otherwise it happens at the next open and + * overrides the restored settings. + */ + if (check_tape(STp, file) >= 0 && + STp->ready == ST_READY) + st_restore_changed_settings(STp, restore_density, + restore_blksize); + } goto out; } if (!STm->defined) {