scsi: st: Restore the drive buffering mode after reset

A device reset also returns the drive's buffered mode to its default.
check_tape() reads the buffered mode with MODE SENSE on every open, so st
follows the drive and a mode set by the user with MTSETDRVBUFFER is lost
without any indication.  The value is also written back by every later MODE
SELECT, including the one that restores a changed block size.

Unlike density and block size, the buffered mode has no "changed" state
that could be used to restore it.  Record the value set with MTSETDRVBUFFER
and restore it after a reset together with density and block size: when
MTREW, MTSEEK or MTEOM clear the reset condition, and after MTLOAD and
MTRETEN.  It is restored first, so that the MODE SELECTs for density and
block size carry the right value.

The buffered mode is a drive setting, not a property of the medium, so it
is not cleared when a new session starts.

Suggested-by: Kai Mäkisara <Kai.Makisara@kolumbus.fi>
Assisted-by: Claude sashiko
Signed-off-by: Laurence Oberman <loberman@redhat.com>
Reviewed-by: Kai Mäkisara <kai.makisara@kolumbus.fi>
Link: https://patch.msgid.link/20260929192821.997675-4-loberman@redhat.com
Signed-off-by: Martin K. Petersen (Oracle) <mkp@kernel.org>
This commit is contained in:
Laurence Oberman 2026-09-29 15:28:20 -04:00 committed by Martin K. Petersen (Oracle)
parent 54ee0ee9bd
commit 614f374472
2 changed files with 30 additions and 15 deletions

View File

@ -2963,9 +2963,11 @@ static int st_int_ioctl(struct scsi_tape *STp, unsigned int cmd_in, unsigned lon
direction = DMA_TO_DEVICE;
memset((STp->buffer)->b_data, 0, 12);
if (cmd_in == MTSETDRVBUFFER)
if (cmd_in == MTSETDRVBUFFER) {
(STp->buffer)->b_data[2] = (arg & 7) << 4;
else
STp->drv_buffer_changed = 1; /* At least we tried ;-) */
STp->changed_drv_buffer = arg & 7;
} else
(STp->buffer)->b_data[2] =
STp->drv_buffer << 4;
(STp->buffer)->b_data[3] = 8; /* block descriptor length */
@ -3611,16 +3613,19 @@ static long st_common_ioctl(struct scsi_tape *STp, struct st_modedef *STm,
}
/*
* 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.
* Re-apply a drive buffering mode, density and block size that were changed
* by the user before a device reset (a negative value means "not changed").
* The buffering mode goes first: the other MODE SELECTs send the current
* STp->drv_buffer. 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 others.
*/
static void st_restore_changed_settings(struct scsi_tape *STp, int density,
int blksize)
static void st_restore_changed_settings(struct scsi_tape *STp, int drv_buffer,
int density, int blksize)
{
if (drv_buffer >= 0 && st_int_ioctl(STp, MTSETDRVBUFFER, drv_buffer))
st_int_ioctl(STp, MTSETDRVBUFFER, drv_buffer);
if (density >= 0 && st_int_ioctl(STp, MTSETDENSITY, density))
st_int_ioctl(STp, MTSETDENSITY, density);
if (blksize >= 0 && st_int_ioctl(STp, MTSETBLK, blksize))
@ -3635,7 +3640,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;
int restore_drv_buffer = -1, restore_density = -1, restore_blksize = -1;
struct scsi_tape *STp = file->private_data;
struct st_modedef *STm;
struct st_partstat *STps;
@ -3778,6 +3783,9 @@ static long st_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg)
STp->partition = 0;
switch_partition(STp);
}
if (STp->drv_buffer_changed)
st_int_ioctl(STp, MTSETDRVBUFFER,
STp->changed_drv_buffer);
if (STp->density_changed)
st_int_ioctl(STp, MTSETDENSITY, STp->changed_density);
if (STp->blksize_changed)
@ -3789,6 +3797,8 @@ static long st_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg)
* new session, which clears the "changed" flags:
* save the values and restore them afterwards.
*/
if (STp->drv_buffer_changed)
restore_drv_buffer = STp->changed_drv_buffer;
if (STp->density_changed)
restore_density = STp->changed_density;
if (STp->blksize_changed)
@ -3878,7 +3888,8 @@ static long st_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg)
* does not wait and the drive may still be loading.
*/
if (!retval && STp->ready == ST_READY)
st_restore_changed_settings(STp, restore_density,
st_restore_changed_settings(STp, restore_drv_buffer,
restore_density,
restore_blksize);
goto out;
}
@ -3899,7 +3910,8 @@ static long st_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg)
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)) {
(restore_drv_buffer >= 0 || restore_density >= 0 ||
restore_blksize >= 0)) {
/*
* Retension reloads the medium and the drive may
* report a new medium. Let check_tape() start the new
@ -3909,7 +3921,8 @@ static long st_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg)
*/
if (check_tape(STp, file) >= 0 &&
STp->ready == ST_READY)
st_restore_changed_settings(STp, restore_density,
st_restore_changed_settings(STp, restore_drv_buffer,
restore_density,
restore_blksize);
}
goto out;
@ -4517,7 +4530,7 @@ static int st_probe(struct scsi_device *SDp)
tpnt->modes[0].defined = 1;
tpnt->density_changed = tpnt->compression_changed =
tpnt->blksize_changed = 0;
tpnt->blksize_changed = tpnt->drv_buffer_changed = 0;
mutex_init(&tpnt->lock);
idr_preload(GFP_KERNEL);

View File

@ -162,10 +162,12 @@ struct scsi_tape {
unsigned char in_use;
unsigned char blksize_changed;
unsigned char density_changed;
unsigned char drv_buffer_changed;
unsigned char compression_changed;
unsigned char drv_buffer;
unsigned char density;
unsigned char changed_density;
unsigned char changed_drv_buffer;
unsigned char door_locked;
unsigned char autorew_dev; /* auto-rewind device */
unsigned char rew_at_close; /* rewind necessary at close */