Linux SCSI subsystem development
 help / color / mirror / Atom feed
From: Laurence Oberman <loberman@redhat.com>
To: linux-scsi@vger.kernel.org
Cc: "Martin K . Petersen" <mkp@kernel.org>,
	"James E . J . Bottomley" <James.Bottomley@HansenPartnership.com>,
	Kai Makisara <Kai.Makisara@kolumbus.fi>,
	John Meneghini <jmeneghi@redhat.com>,
	emilne@redhat.com, bgurney@redhat.com
Subject: [PATCH v4 3/4] scsi: st: Restore the drive buffering mode after reset
Date: Tue, 29 Sep 2026 15:28:20 -0400	[thread overview]
Message-ID: <20260929192821.997675-4-loberman@redhat.com> (raw)
In-Reply-To: <20260929192821.997675-1-loberman@redhat.com>

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>
---
v4: no changes.
v3: new patch (Kai Mäkisara).

 drivers/scsi/st.c | 43 ++++++++++++++++++++++++++++---------------
 drivers/scsi/st.h |  2 ++
 2 files changed, 30 insertions(+), 15 deletions(-)

diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c
index aa4d2ce..f548197 100644
--- a/drivers/scsi/st.c
+++ b/drivers/scsi/st.c
@@ -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 @@ out:
 }
 
 /*
- * 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);
diff --git a/drivers/scsi/st.h b/drivers/scsi/st.h
index 0d7c4b8..c580024 100644
--- a/drivers/scsi/st.h
+++ b/drivers/scsi/st.h
@@ -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 */
-- 
2.43.0


  parent reply	other threads:[~2026-09-29 19:28 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-29 19:28 [PATCH v4 0/4] scsi: st: Restore drive settings and state after reset and MTLOAD Laurence Oberman
2026-09-29 19:28 ` [PATCH v4 1/4] scsi: st: Restore changed drive settings after reset also for MTLOAD and MTRETEN Laurence Oberman
2026-09-29 19:28 ` [PATCH v4 2/4] scsi: st: Record the tape position after a successful MTLOAD Laurence Oberman
2026-09-29 19:28 ` Laurence Oberman [this message]
2026-09-29 19:28 ` [PATCH v4 4/4] scsi: st: Relock the door after a reset Laurence Oberman
2026-09-30 15:58 ` [PATCH v4 0/4] scsi: st: Restore drive settings and state after reset and MTLOAD "Kai Mäkisara (Kolumbus)"
2026-10-03 14:36 ` Martin K. Petersen (Oracle)

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260929192821.997675-4-loberman@redhat.com \
    --to=loberman@redhat.com \
    --cc=James.Bottomley@HansenPartnership.com \
    --cc=Kai.Makisara@kolumbus.fi \
    --cc=bgurney@redhat.com \
    --cc=emilne@redhat.com \
    --cc=jmeneghi@redhat.com \
    --cc=linux-scsi@vger.kernel.org \
    --cc=mkp@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox