public inbox for linux-scsi@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/7] scsi: scsi_debug: Add more tape support
@ 2025-02-13  9:26 Kai Mäkisara
  2025-02-13  9:26 ` [PATCH v2 1/7] scsi: scsi_debug: First fixes for tapes Kai Mäkisara
                   ` (10 more replies)
  0 siblings, 11 replies; 21+ messages in thread
From: Kai Mäkisara @ 2025-02-13  9:26 UTC (permalink / raw)
  To: linux-scsi, dgilbert
  Cc: martin.petersen, James.Bottomley, jmeneghi, Kai Mäkisara

Currently, the scsi_debug driver can create tape devices and the st
driver attaches to those. Nothing much can be done with the tape devices
because scsi_debug does not have support for the tape-specific commands
and features. These patches add some more tape support to the scsi_debug
driver. The end result is simulated drives with a tape having one or two
partitions (one partition is created initially).

The tape is implemented as fixed number (10 000) of 8-byte units.
The first four bytes of a unit contain the type of the unit (data
block, filemark or end-of-data mark). If the units is a data block,
the first four bytes also contain the block length and the remaining
four bytes the first bytes of written data. This allows the user
to use tags to see that the read block is what it was supposed to be.

The following SCSI operations are added or modified:
FORMAT MEDIUM
- added
LOCATE
- added
MODE SELECT
- modified to allow use without page(s) (just header and block descriptor)
  - store density and block size
- partition page added
MODE SENSE
- modified to allow use without page(s) (just header and block descriptor)
  - set density and block size
- partition page added
READ BLOCK LIMITS
- added
READ POSITION
- added
READ
- added tape support for READ (6)
REWIND
- modified to set the tape position
SPACE
- added
START STOP (LOAD)
- modified to return New Medium Unit Attention if tape loaded (not
  according to the standard, but enables testing this UA)
WRITE
- added tape support for WRITE (6)
WRITE FILEMARKS
- added

Changes RFC -> v1:
- rebased to v6.14-rc1
- virtual tape initialization is rewritten and the tape is made shorter
  (10 000 units)
- only one partition is created initially
- tape block allocation is moved to sdev_configure()
- tape blocks are freed in sdev_destroy()
- block size must be multiple of four (SSC standard)
- granularity set to four in READ BLOCK LIMITS
- long LBA not allowed for tapes in MODE SELECT/SENSE
- READ POSITION checks allocation length
- new patch 7 adds support for re-partitioning the tape

Changes v1 -> v2:
- Fixes for bugs reported by the Kernel Test Robot:
  2/7: changed 'len +=' to 'len =' in resp_mode_sense()
  3/7: changed 'for (;' to 'for (i = 0;' in partition_tape()
  4/7: initialized i to zero in resp_space()

Kai Mäkisara (7):
  scsi: scsi_debug: First fixes for tapes
  scsi: scsi_debug: Add READ BLOCK LIMITS and modify LOAD for tapes
  scsi: scsi_debug: Add write support with block lengths  and 4 bytes of
    data
  scsi: scsi_debug: Add read support and update locate for tapes
  scsi: scsi_debug: Add compression mode page for tapes
  scsi: scsi_debug: Reset tape setting at device reset
  scsi: scsi_debug: Add support for partitioning the tape

 drivers/scsi/scsi_debug.c | 775 +++++++++++++++++++++++++++++++++++++-
 1 file changed, 761 insertions(+), 14 deletions(-)

-- 
2.43.0


^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH v2 1/7] scsi: scsi_debug: First fixes for tapes
  2025-02-13  9:26 [PATCH v2 0/7] scsi: scsi_debug: Add more tape support Kai Mäkisara
@ 2025-02-13  9:26 ` Kai Mäkisara
  2025-02-19 16:03   ` John Meneghini
  2025-02-13  9:26 ` [PATCH v2 2/7] scsi: scsi_debug: Add READ BLOCK LIMITS and modify LOAD " Kai Mäkisara
                   ` (9 subsequent siblings)
  10 siblings, 1 reply; 21+ messages in thread
From: Kai Mäkisara @ 2025-02-13  9:26 UTC (permalink / raw)
  To: linux-scsi, dgilbert
  Cc: martin.petersen, James.Bottomley, jmeneghi, Kai Mäkisara

Patch includes the following:
- enable MODE SENSE/SELECT without actual page (to read/write only
  the Block Descriptor)
- store the density code and block size in the Block Descriptor
  (only short version for tapes)
- fix REWIND not to use the wrong page filling function

Signed-off-by: Kai Mäkisara <Kai.Makisara@kolumbus.fi>
---
 drivers/scsi/scsi_debug.c | 55 ++++++++++++++++++++++++++++++++++-----
 1 file changed, 49 insertions(+), 6 deletions(-)

diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index 5ceaa4665e5d..4da0c259390b 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -173,6 +173,10 @@ static const char *sdebug_version_date = "20210520";
 #define DEF_ZBC_MAX_OPEN_ZONES	8
 #define DEF_ZBC_NR_CONV_ZONES	1
 
+/* Default parameters for tape drives */
+#define TAPE_DEF_DENSITY  0x0
+#define TAPE_DEF_BLKSIZE  0
+
 #define SDEBUG_LUN_0_VAL 0
 
 /* bit mask values for sdebug_opts */
@@ -363,6 +367,10 @@ struct sdebug_dev_info {
 	ktime_t create_ts;	/* time since bootup that this device was created */
 	struct sdeb_zone_state *zstate;
 
+	/* For tapes */
+	unsigned int tape_blksize;
+	unsigned int tape_density;
+
 	struct dentry *debugfs_entry;
 	struct spinlock list_lock;
 	struct list_head inject_err_list;
@@ -773,7 +781,7 @@ static const struct opcode_info_t opcode_info_arr[SDEB_I_LAST_ELEM_P1 + 1] = {
 /* 20 */
 	{0, 0x1e, 0, 0, NULL, NULL, /* ALLOW REMOVAL */
 	    {6,  0, 0, 0, 0x3, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
-	{0, 0x1, 0, 0, resp_start_stop, NULL, /* REWIND ?? */
+	{0, 0x1, 0, 0, NULL, NULL, /* REWIND ?? */
 	    {6,  0x1, 0, 0, 0, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
 	{0, 0, 0, F_INV_OP | FF_RESPOND, NULL, NULL, /* ATA_PT */
 	    {0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
@@ -2742,7 +2750,7 @@ static int resp_mode_sense(struct scsi_cmnd *scp,
 	unsigned char *ap;
 	unsigned char *arr __free(kfree);
 	unsigned char *cmd = scp->cmnd;
-	bool dbd, llbaa, msense_6, is_disk, is_zbc;
+	bool dbd, llbaa, msense_6, is_disk, is_zbc, is_tape;
 
 	arr = kzalloc(SDEBUG_MAX_MSENSE_SZ, GFP_ATOMIC);
 	if (!arr)
@@ -2755,7 +2763,8 @@ static int resp_mode_sense(struct scsi_cmnd *scp,
 	llbaa = msense_6 ? false : !!(cmd[1] & 0x10);
 	is_disk = (sdebug_ptype == TYPE_DISK);
 	is_zbc = devip->zoned;
-	if ((is_disk || is_zbc) && !dbd)
+	is_tape = (sdebug_ptype == TYPE_TAPE);
+	if ((is_disk || is_zbc || is_tape) && !dbd)
 		bd_len = llbaa ? 16 : 8;
 	else
 		bd_len = 0;
@@ -2793,15 +2802,25 @@ static int resp_mode_sense(struct scsi_cmnd *scp,
 			put_unaligned_be32(0xffffffff, ap + 0);
 		else
 			put_unaligned_be32(sdebug_capacity, ap + 0);
-		put_unaligned_be16(sdebug_sector_size, ap + 6);
+		if (is_tape) {
+			ap[0] = devip->tape_density;
+			put_unaligned_be16(devip->tape_blksize, ap + 6);
+		} else
+			put_unaligned_be16(sdebug_sector_size, ap + 6);
 		offset += bd_len;
 		ap = arr + offset;
 	} else if (16 == bd_len) {
+		if (is_tape) {
+			mk_sense_invalid_fld(scp, SDEB_IN_DATA, 1, 4);
+			return check_condition_result;
+		}
 		put_unaligned_be64((u64)sdebug_capacity, ap + 0);
 		put_unaligned_be32(sdebug_sector_size, ap + 12);
 		offset += bd_len;
 		ap = arr + offset;
 	}
+	if (cmd[2] == 0)
+		goto only_bd; /* Only block descriptor requested */
 
 	/*
 	 * N.B. If len>0 before resp_*_pg() call, then form of that call should be:
@@ -2902,6 +2921,7 @@ static int resp_mode_sense(struct scsi_cmnd *scp,
 	default:
 		goto bad_pcode;
 	}
+only_bd:
 	if (msense_6)
 		arr[0] = offset - 1;
 	else
@@ -2945,8 +2965,27 @@ static int resp_mode_select(struct scsi_cmnd *scp,
 			    __func__, param_len, res);
 	md_len = mselect6 ? (arr[0] + 1) : (get_unaligned_be16(arr + 0) + 2);
 	bd_len = mselect6 ? arr[3] : get_unaligned_be16(arr + 6);
-	off = bd_len + (mselect6 ? 4 : 8);
-	if (md_len > 2 || off >= res) {
+	off = (mselect6 ? 4 : 8);
+	if (sdebug_ptype == TYPE_TAPE) {
+		int blksize;
+
+		if (bd_len != 8) {
+			mk_sense_invalid_fld(scp, SDEB_IN_DATA,
+					mselect6 ? 3 : 6, -1);
+			return check_condition_result;
+		}
+		blksize = get_unaligned_be16(arr + off + 6);
+		if ((blksize % 4) != 0) {
+			mk_sense_invalid_fld(scp, SDEB_IN_DATA, off + 6, -1);
+			return check_condition_result;
+		}
+		devip->tape_density = arr[off];
+		devip->tape_blksize = blksize;
+	}
+	off += bd_len;
+	if (off >= res)
+		return 0; /* No page written, just descriptors */
+	if (md_len > 2) {
 		mk_sense_invalid_fld(scp, SDEB_IN_DATA, 0, -1);
 		return check_condition_result;
 	}
@@ -5835,6 +5874,10 @@ static struct sdebug_dev_info *sdebug_device_create(
 		} else {
 			devip->zoned = false;
 		}
+		if (sdebug_ptype == TYPE_TAPE) {
+			devip->tape_density = TAPE_DEF_DENSITY;
+			devip->tape_blksize = TAPE_DEF_BLKSIZE;
+		}
 		devip->create_ts = ktime_get_boottime();
 		atomic_set(&devip->stopped, (sdeb_tur_ms_to_ready > 0 ? 2 : 0));
 		spin_lock_init(&devip->list_lock);
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v2 2/7] scsi: scsi_debug: Add READ BLOCK LIMITS and modify LOAD for tapes
  2025-02-13  9:26 [PATCH v2 0/7] scsi: scsi_debug: Add more tape support Kai Mäkisara
  2025-02-13  9:26 ` [PATCH v2 1/7] scsi: scsi_debug: First fixes for tapes Kai Mäkisara
@ 2025-02-13  9:26 ` Kai Mäkisara
  2025-02-19 21:16   ` John Meneghini
  2025-02-13  9:26 ` [PATCH v2 3/7] scsi: scsi_debug: Add write support with block lengths and 4 bytes of data Kai Mäkisara
                   ` (8 subsequent siblings)
  10 siblings, 1 reply; 21+ messages in thread
From: Kai Mäkisara @ 2025-02-13  9:26 UTC (permalink / raw)
  To: linux-scsi, dgilbert
  Cc: martin.petersen, James.Bottomley, jmeneghi, Kai Mäkisara

The changes:
- add READ BLOCK LIMITS (512 - 1048576 bytes)
- make LOAD send New Media UA (not correct by the standard, but
  makes possible to test also this UA)

Signed-off-by: Kai Mäkisara <Kai.Makisara@kolumbus.fi>
---
v1 -> v2:
- changed 'len +=' to 'len =' in resp_mode_sense() to fix the bug reported
  by the Kernel Test Robot

 drivers/scsi/scsi_debug.c | 127 ++++++++++++++++++++++++++++++++++++--
 1 file changed, 121 insertions(+), 6 deletions(-)

diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index 4da0c259390b..21c64f79797a 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -80,6 +80,7 @@ static const char *sdebug_version_date = "20210520";
 #define INVALID_FIELD_IN_CDB 0x24
 #define INVALID_FIELD_IN_PARAM_LIST 0x26
 #define WRITE_PROTECTED 0x27
+#define UA_READY_ASC 0x28
 #define UA_RESET_ASC 0x29
 #define UA_CHANGED_ASC 0x2a
 #define TARGET_CHANGED_ASC 0x3f
@@ -175,7 +176,11 @@ static const char *sdebug_version_date = "20210520";
 
 /* Default parameters for tape drives */
 #define TAPE_DEF_DENSITY  0x0
+#define TAPE_BAD_DENSITY  0x65
 #define TAPE_DEF_BLKSIZE  0
+#define TAPE_MIN_BLKSIZE  512
+#define TAPE_MAX_BLKSIZE  1048576
+#define TAPE_MAX_PARTITIONS 2
 
 #define SDEBUG_LUN_0_VAL 0
 
@@ -220,7 +225,8 @@ static const char *sdebug_version_date = "20210520";
 #define SDEBUG_UA_LUNS_CHANGED 5
 #define SDEBUG_UA_MICROCODE_CHANGED 6	/* simulate firmware change */
 #define SDEBUG_UA_MICROCODE_CHANGED_WO_RESET 7
-#define SDEBUG_NUM_UAS 8
+#define SDEBUG_UA_NOT_READY_TO_READY 8
+#define SDEBUG_NUM_UAS 9
 
 /* when 1==SDEBUG_OPT_MEDIUM_ERR, a medium error is simulated at this
  * sector on read commands: */
@@ -370,6 +376,8 @@ struct sdebug_dev_info {
 	/* For tapes */
 	unsigned int tape_blksize;
 	unsigned int tape_density;
+	unsigned char tape_partition;
+	unsigned int tape_location[TAPE_MAX_PARTITIONS];
 
 	struct dentry *debugfs_entry;
 	struct spinlock list_lock;
@@ -491,14 +499,16 @@ enum sdeb_opcode_index {
 	SDEB_I_ZONE_OUT = 30,		/* 0x94+SA; includes no data xfer */
 	SDEB_I_ZONE_IN = 31,		/* 0x95+SA; all have data-in */
 	SDEB_I_ATOMIC_WRITE_16 = 32,
-	SDEB_I_LAST_ELEM_P1 = 33,	/* keep this last (previous + 1) */
+	SDEB_I_READ_BLOCK_LIMITS = 33,
+	SDEB_I_LOCATE = 34,
+	SDEB_I_LAST_ELEM_P1 = 35,	/* keep this last (previous + 1) */
 };
 
 
 static const unsigned char opcode_ind_arr[256] = {
 /* 0x0; 0x0->0x1f: 6 byte cdbs */
 	SDEB_I_TEST_UNIT_READY, SDEB_I_REZERO_UNIT, 0, SDEB_I_REQUEST_SENSE,
-	    0, 0, 0, 0,
+	    0, SDEB_I_READ_BLOCK_LIMITS, 0, 0,
 	SDEB_I_READ, 0, SDEB_I_WRITE, 0, 0, 0, 0, 0,
 	0, 0, SDEB_I_INQUIRY, 0, 0, SDEB_I_MODE_SELECT, SDEB_I_RESERVE,
 	    SDEB_I_RELEASE,
@@ -506,7 +516,7 @@ static const unsigned char opcode_ind_arr[256] = {
 	    SDEB_I_ALLOW_REMOVAL, 0,
 /* 0x20; 0x20->0x3f: 10 byte cdbs */
 	0, 0, 0, 0, 0, SDEB_I_READ_CAPACITY, 0, 0,
-	SDEB_I_READ, 0, SDEB_I_WRITE, 0, 0, 0, 0, SDEB_I_VERIFY,
+	SDEB_I_READ, 0, SDEB_I_WRITE, SDEB_I_LOCATE, 0, 0, 0, SDEB_I_VERIFY,
 	0, 0, 0, 0, SDEB_I_PRE_FETCH, SDEB_I_SYNC_CACHE, 0, 0,
 	0, 0, 0, SDEB_I_WRITE_BUFFER, 0, 0, 0, 0,
 /* 0x40; 0x40->0x5f: 10 byte cdbs */
@@ -581,6 +591,8 @@ static int resp_open_zone(struct scsi_cmnd *, struct sdebug_dev_info *);
 static int resp_close_zone(struct scsi_cmnd *, struct sdebug_dev_info *);
 static int resp_finish_zone(struct scsi_cmnd *, struct sdebug_dev_info *);
 static int resp_rwp_zone(struct scsi_cmnd *, struct sdebug_dev_info *);
+static int resp_read_blklimits(struct scsi_cmnd *, struct sdebug_dev_info *);
+static int resp_locate(struct scsi_cmnd *, struct sdebug_dev_info *);
 
 static int sdebug_do_add_host(bool mk_new_store);
 static int sdebug_add_host_helper(int per_host_idx);
@@ -808,6 +820,7 @@ static const struct opcode_info_t opcode_info_arr[SDEB_I_LAST_ELEM_P1 + 1] = {
 	    resp_pre_fetch, pre_fetch_iarr,
 	    {10,  0x2, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xc7, 0, 0,
 	     0, 0, 0, 0} },			/* PRE-FETCH (10) */
+						/* READ POSITION (10) */
 
 /* 30 */
 	{ARRAY_SIZE(zone_out_iarr), 0x94, 0x3, F_SA_LOW | F_M_ACCESS,
@@ -823,6 +836,12 @@ static const struct opcode_info_t opcode_info_arr[SDEB_I_LAST_ELEM_P1 + 1] = {
 	    resp_atomic_write, NULL, /* ATOMIC WRITE 16 */
 		{16,  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} },
+	{0, 0x05, 0, F_D_IN, resp_read_blklimits, NULL,    /* READ BLOCK LIMITS (6) */
+	    {6,  0, 0, 0, 0, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
+	{0, 0x2b, 0, F_D_UNKN, resp_locate, NULL,    /* LOCATE (10) */
+	    {10,  0x2, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xc7, 0, 0,
+	     0, 0, 0, 0} },
+
 /* sentinel */
 	{0xff, 0, 0, 0, NULL, NULL,		/* terminating element */
 	    {0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
@@ -1501,6 +1520,12 @@ static int make_ua(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
 			if (sdebug_verbose)
 				cp = "reported luns data has changed";
 			break;
+		case SDEBUG_UA_NOT_READY_TO_READY:
+			mk_sense_buffer(scp, UNIT_ATTENTION, UA_READY_ASC,
+					0);
+			if (sdebug_verbose)
+				cp = "not ready to ready transition/media change";
+			break;
 		default:
 			pr_warn("unexpected unit attention code=%d\n", k);
 			if (sdebug_verbose)
@@ -2204,6 +2229,14 @@ static int resp_start_stop(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
 	changing = (stopped_state != want_stop);
 	if (changing)
 		atomic_xchg(&devip->stopped, want_stop);
+	if (sdebug_ptype == TYPE_TAPE && !want_stop) {
+		int i;
+
+		set_bit(SDEBUG_UA_NOT_READY_TO_READY, devip->uas_bm); /* not legal! */
+		for (i = 0; i < TAPE_MAX_PARTITIONS; i++)
+			devip->tape_location[i] = 0;
+		devip->tape_partition = 0;
+	}
 	if (!changing || (cmd[1] & 0x1))  /* state unchanged or IMMED bit set in cdb */
 		return SDEG_RES_IMMED_MASK;
 	else
@@ -2736,6 +2769,17 @@ static int resp_sas_sha_m_spg(unsigned char *p, int pcontrol)
 	return sizeof(sas_sha_m_pg);
 }
 
+static unsigned char partition_pg[] = {0x11, 12, 1, 0, 0x24, 3, 9, 0,
+	0xff, 0xff, 0x00, 0x00};
+
+static int resp_partition_m_pg(unsigned char *p, int pcontrol, int target)
+{	/* Partition page for mode_sense (tape) */
+	memcpy(p, partition_pg, sizeof(partition_pg));
+	if (pcontrol == 1)
+		memset(p + 2, 0, sizeof(partition_pg) - 2);
+	return sizeof(partition_pg);
+}
+
 /* PAGE_SIZE is more than necessary but provides room for future expansion. */
 #define SDEBUG_MAX_MSENSE_SZ PAGE_SIZE
 
@@ -2876,6 +2920,12 @@ static int resp_mode_sense(struct scsi_cmnd *scp,
 		}
 		offset += len;
 		break;
+	case 0x11:	/* Partition Mode Page (tape) */
+		if (!is_tape)
+			goto bad_pcode;
+		len = resp_partition_m_pg(ap, pcontrol, target);
+		offset += len;
+		break;
 	case 0x19:	/* if spc==1 then sas phy, control+discover */
 		if (subpcode > 0x2 && subpcode < 0xff)
 			goto bad_subpcode;
@@ -2974,9 +3024,16 @@ static int resp_mode_select(struct scsi_cmnd *scp,
 					mselect6 ? 3 : 6, -1);
 			return check_condition_result;
 		}
+		if (arr[off] == TAPE_BAD_DENSITY) {
+			mk_sense_invalid_fld(scp, SDEB_IN_DATA, 0, -1);
+			return check_condition_result;
+		}
 		blksize = get_unaligned_be16(arr + off + 6);
-		if ((blksize % 4) != 0) {
-			mk_sense_invalid_fld(scp, SDEB_IN_DATA, off + 6, -1);
+		if (blksize != 0 &&
+			(blksize < TAPE_MIN_BLKSIZE ||
+				blksize > TAPE_MAX_BLKSIZE ||
+				(blksize % 4) != 0)) {
+			mk_sense_invalid_fld(scp, SDEB_IN_DATA, 1, -1);
 			return check_condition_result;
 		}
 		devip->tape_density = arr[off];
@@ -3177,6 +3234,36 @@ static int resp_log_sense(struct scsi_cmnd *scp,
 		    min_t(u32, len, SDEBUG_MAX_INQ_ARR_SZ));
 }
 
+enum {SDEBUG_READ_BLOCK_LIMITS_ARR_SZ = 6};
+static int resp_read_blklimits(struct scsi_cmnd *scp,
+			struct sdebug_dev_info *devip)
+{
+	unsigned char arr[SDEBUG_READ_BLOCK_LIMITS_ARR_SZ];
+
+	arr[0] = 4;
+	put_unaligned_be24(TAPE_MAX_BLKSIZE, arr + 1);
+	put_unaligned_be16(TAPE_MIN_BLKSIZE, arr + 4);
+	return fill_from_dev_buffer(scp, arr, SDEBUG_READ_BLOCK_LIMITS_ARR_SZ);
+}
+
+static int resp_locate(struct scsi_cmnd *scp,
+		struct sdebug_dev_info *devip)
+{
+	unsigned char *cmd = scp->cmnd;
+
+	if ((cmd[1] & 0x02) != 0) {
+		if (cmd[8] >= TAPE_MAX_PARTITIONS) {
+			mk_sense_invalid_fld(scp, SDEB_IN_CDB, 8, -1);
+			return check_condition_result;
+		}
+		devip->tape_partition = cmd[8];
+	}
+	devip->tape_location[devip->tape_partition] =
+		get_unaligned_be32(cmd + 3);
+
+	return 0;
+}
+
 static inline bool sdebug_dev_is_zoned(struct sdebug_dev_info *devip)
 {
 	return devip->nr_zones != 0;
@@ -4957,7 +5044,10 @@ static int resp_sync_cache(struct scsi_cmnd *scp,
  * a GOOD status otherwise. Model a disk with a big cache and yield
  * CONDITION MET. Actually tries to bring range in main memory into the
  * cache associated with the CPU(s).
+ *
+ * The pcode 0x34 is also used for READ POSITION by tape devices.
  */
+enum {SDEBUG_READ_POSITION_ARR_SZ = 20};
 static int resp_pre_fetch(struct scsi_cmnd *scp,
 			  struct sdebug_dev_info *devip)
 {
@@ -4969,6 +5059,31 @@ static int resp_pre_fetch(struct scsi_cmnd *scp,
 	struct sdeb_store_info *sip = devip2sip(devip, true);
 	u8 *fsp = sip->storep;
 
+	if (sdebug_ptype == TYPE_TAPE) {
+		if (cmd[0] == PRE_FETCH) { /* READ POSITION (10) */
+			int all_length;
+			unsigned char arr[20];
+			unsigned int pos;
+
+			all_length = get_unaligned_be16(cmd + 7);
+			if ((cmd[1] & 0xfe) != 0 ||
+				all_length != 0) { /* only short form */
+				mk_sense_invalid_fld(scp, SDEB_IN_CDB,
+						all_length ? 7 : 1, 0);
+				return check_condition_result;
+			}
+			memset(arr, 0, SDEBUG_READ_POSITION_ARR_SZ);
+			arr[1] = devip->tape_partition;
+			pos = devip->tape_location[devip->tape_partition];
+			put_unaligned_be32(pos, arr + 4);
+			put_unaligned_be32(pos, arr + 8);
+			return fill_from_dev_buffer(scp, arr,
+						SDEBUG_READ_POSITION_ARR_SZ);
+		}
+		mk_sense_invalid_opcode(scp);
+		return check_condition_result;
+	}
+
 	if (cmd[0] == PRE_FETCH) {	/* 10 byte cdb */
 		lba = get_unaligned_be32(cmd + 2);
 		nblks = get_unaligned_be16(cmd + 7);
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v2 3/7] scsi: scsi_debug: Add write support with block lengths and 4 bytes of data
  2025-02-13  9:26 [PATCH v2 0/7] scsi: scsi_debug: Add more tape support Kai Mäkisara
  2025-02-13  9:26 ` [PATCH v2 1/7] scsi: scsi_debug: First fixes for tapes Kai Mäkisara
  2025-02-13  9:26 ` [PATCH v2 2/7] scsi: scsi_debug: Add READ BLOCK LIMITS and modify LOAD " Kai Mäkisara
@ 2025-02-13  9:26 ` Kai Mäkisara
  2025-02-19 21:45   ` John Meneghini
  2025-02-13  9:26 ` [PATCH v2 4/7] scsi: scsi_debug: Add read support and update locate for tapes Kai Mäkisara
                   ` (7 subsequent siblings)
  10 siblings, 1 reply; 21+ messages in thread
From: Kai Mäkisara @ 2025-02-13  9:26 UTC (permalink / raw)
  To: linux-scsi, dgilbert
  Cc: martin.petersen, James.Bottomley, jmeneghi, Kai Mäkisara

The tape is implemented as fixed number (10 000) of 8-byte units.
The first four bytes of a unit contains the type of the unit (data
block, filemark or end-of-data mark). If the units is a data block,
the first four bytes contain the block length and the remaining
four bytes the first bytes of written data. This allows the user
to use tags to see that the read block is what it was supposed to be.

The tape can contain two partitions. Initially it is formatted as one
partition consisting of all 10 000 units.

This patch adds the WRITE(6) command for tapes and the WRITE FILEMARKS (6)
command. The REWIND command is updated.

Signed-off-by: Kai Mäkisara <Kai.Makisara@kolumbus.fi>
---
v1 -> v2:
- changed 'for (;' to 'for (i = 0;' in partition_tape() to fix the bug
  reported by the Kernel Test Robot

 drivers/scsi/scsi_debug.c | 217 +++++++++++++++++++++++++++++++++++++-
 1 file changed, 212 insertions(+), 5 deletions(-)

diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index 21c64f79797a..69cae4c1712a 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -71,6 +71,10 @@ static const char *sdebug_version_date = "20210520";
 #define NO_ADDITIONAL_SENSE 0x0
 #define OVERLAP_ATOMIC_COMMAND_ASC 0x0
 #define OVERLAP_ATOMIC_COMMAND_ASCQ 0x23
+#define FILEMARK_DETECTED_ASCQ 0x1
+#define EOP_EOM_DETECTED_ASCQ 0x2
+#define BEGINNING_OF_P_M_DETECTED_ASCQ 0x4
+#define EOD_DETECTED_ASCQ 0x5
 #define LOGICAL_UNIT_NOT_READY 0x4
 #define LOGICAL_UNIT_COMMUNICATION_FAILURE 0x8
 #define UNRECOVERED_READ_ERR 0x11
@@ -83,6 +87,7 @@ static const char *sdebug_version_date = "20210520";
 #define UA_READY_ASC 0x28
 #define UA_RESET_ASC 0x29
 #define UA_CHANGED_ASC 0x2a
+#define TOO_MANY_IN_PARTITION_ASC 0x3b
 #define TARGET_CHANGED_ASC 0x3f
 #define LUNS_CHANGED_ASCQ 0x0e
 #define INSUFF_RES_ASC 0x55
@@ -180,7 +185,29 @@ static const char *sdebug_version_date = "20210520";
 #define TAPE_DEF_BLKSIZE  0
 #define TAPE_MIN_BLKSIZE  512
 #define TAPE_MAX_BLKSIZE  1048576
+#define TAPE_EW 20
 #define TAPE_MAX_PARTITIONS 2
+#define TAPE_UNITS 10000
+
+/* The tape block data definitions */
+#define TAPE_BLOCK_FM_FLAG   ((u32)0x1 << 30)
+#define TAPE_BLOCK_EOD_FLAG  ((u32)0x2 << 30)
+#define TAPE_BLOCK_MARK_MASK ((u32)0x3 << 30)
+#define TAPE_BLOCK_SIZE_MASK (~TAPE_BLOCK_MARK_MASK)
+#define TAPE_BLOCK_MARK(a) (a & TAPE_BLOCK_MARK_MASK)
+#define TAPE_BLOCK_SIZE(a) (a & TAPE_BLOCK_SIZE_MASK)
+#define IS_TAPE_BLOCK_FM(a)   ((a & TAPE_BLOCK_FM_FLAG) != 0)
+#define IS_TAPE_BLOCK_EOD(a)  ((a & TAPE_BLOCK_EOD_FLAG) != 0)
+
+struct tape_block {
+	u32 fl_size;
+	unsigned char data[4];
+};
+
+/* Flags for sense data */
+#define SENSE_FLAG_FILEMARK  0x80
+#define SENSE_FLAG_EOM 0x40
+#define SENSE_FLAG_ILI 0x20
 
 #define SDEBUG_LUN_0_VAL 0
 
@@ -377,7 +404,10 @@ struct sdebug_dev_info {
 	unsigned int tape_blksize;
 	unsigned int tape_density;
 	unsigned char tape_partition;
+	unsigned char tape_nbr_partitions;
 	unsigned int tape_location[TAPE_MAX_PARTITIONS];
+	unsigned int tape_eop[TAPE_MAX_PARTITIONS];
+	struct tape_block *tape_blocks[TAPE_MAX_PARTITIONS];
 
 	struct dentry *debugfs_entry;
 	struct spinlock list_lock;
@@ -501,7 +531,8 @@ enum sdeb_opcode_index {
 	SDEB_I_ATOMIC_WRITE_16 = 32,
 	SDEB_I_READ_BLOCK_LIMITS = 33,
 	SDEB_I_LOCATE = 34,
-	SDEB_I_LAST_ELEM_P1 = 35,	/* keep this last (previous + 1) */
+	SDEB_I_WRITE_FILEMARKS = 35,
+	SDEB_I_LAST_ELEM_P1 = 36,	/* keep this last (previous + 1) */
 };
 
 
@@ -510,8 +541,8 @@ static const unsigned char opcode_ind_arr[256] = {
 	SDEB_I_TEST_UNIT_READY, SDEB_I_REZERO_UNIT, 0, SDEB_I_REQUEST_SENSE,
 	    0, SDEB_I_READ_BLOCK_LIMITS, 0, 0,
 	SDEB_I_READ, 0, SDEB_I_WRITE, 0, 0, 0, 0, 0,
-	0, 0, SDEB_I_INQUIRY, 0, 0, SDEB_I_MODE_SELECT, SDEB_I_RESERVE,
-	    SDEB_I_RELEASE,
+	SDEB_I_WRITE_FILEMARKS, 0, SDEB_I_INQUIRY, 0, 0,
+	    SDEB_I_MODE_SELECT, SDEB_I_RESERVE, SDEB_I_RELEASE,
 	0, 0, SDEB_I_MODE_SENSE, SDEB_I_START_STOP, 0, SDEB_I_SEND_DIAG,
 	    SDEB_I_ALLOW_REMOVAL, 0,
 /* 0x20; 0x20->0x3f: 10 byte cdbs */
@@ -593,6 +624,8 @@ static int resp_finish_zone(struct scsi_cmnd *, struct sdebug_dev_info *);
 static int resp_rwp_zone(struct scsi_cmnd *, struct sdebug_dev_info *);
 static int resp_read_blklimits(struct scsi_cmnd *, struct sdebug_dev_info *);
 static int resp_locate(struct scsi_cmnd *, struct sdebug_dev_info *);
+static int resp_write_filemarks(struct scsi_cmnd *, struct sdebug_dev_info *);
+static int resp_rewind(struct scsi_cmnd *, struct sdebug_dev_info *);
 
 static int sdebug_do_add_host(bool mk_new_store);
 static int sdebug_add_host_helper(int per_host_idx);
@@ -793,7 +826,7 @@ static const struct opcode_info_t opcode_info_arr[SDEB_I_LAST_ELEM_P1 + 1] = {
 /* 20 */
 	{0, 0x1e, 0, 0, NULL, NULL, /* ALLOW REMOVAL */
 	    {6,  0, 0, 0, 0x3, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
-	{0, 0x1, 0, 0, NULL, NULL, /* REWIND ?? */
+	{0, 0x1, 0, 0, resp_rewind, NULL,
 	    {6,  0x1, 0, 0, 0, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
 	{0, 0, 0, F_INV_OP | FF_RESPOND, NULL, NULL, /* ATA_PT */
 	    {0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
@@ -841,6 +874,8 @@ static const struct opcode_info_t opcode_info_arr[SDEB_I_LAST_ELEM_P1 + 1] = {
 	{0, 0x2b, 0, F_D_UNKN, resp_locate, NULL,    /* LOCATE (10) */
 	    {10,  0x2, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xc7, 0, 0,
 	     0, 0, 0, 0} },
+	{0, 0x10, 0, F_D_IN, resp_write_filemarks, NULL,    /* WRITE FILEMARKS (6) */
+	    {6,  0x01, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
 
 /* sentinel */
 	{0xff, 0, 0, 0, NULL, NULL,		/* terminating element */
@@ -1358,6 +1393,30 @@ static void mk_sense_buffer(struct scsi_cmnd *scp, int key, int asc, int asq)
 			    my_name, key, asc, asq);
 }
 
+/* Sense data that has information fields for tapes */
+static void mk_sense_info_tape(struct scsi_cmnd *scp, int key, int asc, int asq,
+			unsigned int information, unsigned char tape_flags)
+{
+	if (!scp->sense_buffer) {
+		sdev_printk(KERN_ERR, scp->device,
+			    "%s: sense_buffer is NULL\n", __func__);
+		return;
+	}
+	memset(scp->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
+
+	scsi_build_sense(scp, /*sdebug_dsense*/ 0, key, asc, asq);
+	/* only fixed format so far */
+
+	scp->sense_buffer[0] |= 0x80; /* valid */
+	scp->sense_buffer[2] |= tape_flags;
+	put_unaligned_be32(information, &scp->sense_buffer[3]);
+
+	if (sdebug_verbose)
+		sdev_printk(KERN_INFO, scp->device,
+			    "%s:  [sense_key,asc,ascq]: [0x%x,0x%x,0x%x]\n",
+			    my_name, key, asc, asq);
+}
+
 static void mk_sense_invalid_opcode(struct scsi_cmnd *scp)
 {
 	mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_OPCODE, 0);
@@ -3252,7 +3311,7 @@ static int resp_locate(struct scsi_cmnd *scp,
 	unsigned char *cmd = scp->cmnd;
 
 	if ((cmd[1] & 0x02) != 0) {
-		if (cmd[8] >= TAPE_MAX_PARTITIONS) {
+		if (cmd[8] >= devip->tape_nbr_partitions) {
 			mk_sense_invalid_fld(scp, SDEB_IN_CDB, 8, -1);
 			return check_condition_result;
 		}
@@ -3264,6 +3323,71 @@ static int resp_locate(struct scsi_cmnd *scp,
 	return 0;
 }
 
+static int resp_write_filemarks(struct scsi_cmnd *scp,
+		struct sdebug_dev_info *devip)
+{
+	unsigned char *cmd = scp->cmnd;
+	unsigned int i, count, pos;
+	u32 data;
+	int partition = devip->tape_partition;
+
+	if ((cmd[1] & 0xfe) != 0) { /* probably write setmarks, not in >= SCSI-3 */
+		mk_sense_invalid_fld(scp, SDEB_IN_CDB, 1, 1);
+		return check_condition_result;
+	}
+	count = get_unaligned_be24(cmd + 2);
+	data = TAPE_BLOCK_FM_FLAG;
+	for (i = 0, pos = devip->tape_location[partition]; i < count; i++, pos++) {
+		if (pos >= devip->tape_eop[partition] - 1) { /* don't overwrite EOD */
+			devip->tape_location[partition] = devip->tape_eop[partition] - 1;
+			mk_sense_info_tape(scp, VOLUME_OVERFLOW, NO_ADDITIONAL_SENSE,
+					EOP_EOM_DETECTED_ASCQ, count, SENSE_FLAG_EOM);
+			return check_condition_result;
+		}
+		(devip->tape_blocks[partition] + pos)->fl_size = data;
+	}
+	(devip->tape_blocks[partition] + pos)->fl_size =
+		TAPE_BLOCK_EOD_FLAG;
+	devip->tape_location[partition] = pos;
+
+	return 0;
+}
+
+static int resp_rewind(struct scsi_cmnd *scp,
+		struct sdebug_dev_info *devip)
+{
+	devip->tape_location[devip->tape_partition] = 0;
+
+	return 0;
+}
+
+static int partition_tape(struct sdebug_dev_info *devip, int nbr_partitions,
+			int part_0_size, int part_1_size)
+{
+	int i;
+
+	if (part_0_size + part_1_size > TAPE_UNITS)
+		return -1;
+	devip->tape_eop[0] = part_0_size;
+	devip->tape_blocks[0]->fl_size = TAPE_BLOCK_EOD_FLAG;
+	devip->tape_eop[1] = part_1_size;
+	devip->tape_blocks[1] = devip->tape_blocks[0] +
+			devip->tape_eop[0];
+	devip->tape_blocks[1]->fl_size = TAPE_BLOCK_EOD_FLAG;
+
+	for (i = 0 ; i < TAPE_MAX_PARTITIONS; i++)
+		devip->tape_location[i] = 0;
+
+	devip->tape_nbr_partitions = nbr_partitions;
+	devip->tape_partition = 0;
+
+	partition_pg[3] = nbr_partitions - 1;
+	put_unaligned_be16(devip->tape_eop[0], partition_pg + 8);
+	put_unaligned_be16(devip->tape_eop[1], partition_pg + 10);
+
+	return nbr_partitions;
+}
+
 static inline bool sdebug_dev_is_zoned(struct sdebug_dev_info *devip)
 {
 	return devip->nr_zones != 0;
@@ -4304,6 +4428,67 @@ static void unmap_region(struct sdeb_store_info *sip, sector_t lba,
 	}
 }
 
+static int resp_write_tape(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
+{
+	u32 i, num, transfer, size, written = 0;
+	u8 *cmd = scp->cmnd;
+	struct scsi_data_buffer *sdb = &scp->sdb;
+	int partition = devip->tape_partition;
+	int pos = devip->tape_location[partition];
+	struct tape_block *blp;
+	bool fixed, ew;
+
+	if (cmd[0] != WRITE_6) { /* Only Write(6) supported */
+		mk_sense_invalid_opcode(scp);
+		return illegal_condition_result;
+	}
+
+	fixed = (cmd[1] & 1) != 0;
+	transfer = get_unaligned_be24(cmd + 2);
+	if (fixed) {
+		num = transfer;
+		size = devip->tape_blksize;
+	} else {
+		if (transfer < TAPE_MIN_BLKSIZE ||
+			transfer > TAPE_MAX_BLKSIZE) {
+			mk_sense_invalid_fld(scp, SDEB_IN_CDB, 2, -1);
+			return check_condition_result;
+		}
+		num = 1;
+		size = transfer;
+	}
+
+	scsi_set_resid(scp, num * transfer);
+	for (i = 0, blp = devip->tape_blocks[partition] + pos, ew = false;
+	     i < num && pos < devip->tape_eop[partition] - 1; i++, pos++, blp++) {
+		blp->fl_size = size;
+		sg_copy_buffer(sdb->table.sgl, sdb->table.nents,
+			&(blp->data), 4, i * size, true);
+		written += size;
+		scsi_set_resid(scp, num * transfer - written);
+		ew |= (pos == devip->tape_eop[partition] - TAPE_EW);
+	}
+
+	devip->tape_location[partition] = pos;
+	blp->fl_size = TAPE_BLOCK_EOD_FLAG;
+	if (pos >= devip->tape_eop[partition] - 1) {
+		mk_sense_info_tape(scp, VOLUME_OVERFLOW,
+				NO_ADDITIONAL_SENSE, EOP_EOM_DETECTED_ASCQ,
+				fixed ? num - i : transfer,
+				SENSE_FLAG_EOM);
+		return check_condition_result;
+	}
+	if (ew) { /* early warning */
+		mk_sense_info_tape(scp, NO_SENSE,
+				NO_ADDITIONAL_SENSE, EOP_EOM_DETECTED_ASCQ,
+				fixed ? num - i : transfer,
+				SENSE_FLAG_EOM);
+		return check_condition_result;
+	}
+
+	return 0;
+}
+
 static int resp_write_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
 {
 	bool check_prot;
@@ -4316,6 +4501,9 @@ static int resp_write_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
 	u8 *cmd = scp->cmnd;
 	bool meta_data_locked = false;
 
+	if (sdebug_ptype == TYPE_TAPE)
+		return resp_write_tape(scp, devip);
+
 	switch (cmd[0]) {
 	case WRITE_16:
 		ei_lba = 0;
@@ -6063,6 +6251,20 @@ static int scsi_debug_sdev_configure(struct scsi_device *sdp,
 		if (devip == NULL)
 			return 1;  /* no resources, will be marked offline */
 	}
+	if (sdebug_ptype == TYPE_TAPE) {
+		if (!devip->tape_blocks[0]) {
+			devip->tape_blocks[0] =
+				kcalloc(TAPE_UNITS, sizeof(struct tape_block),
+					GFP_KERNEL);
+			if (!devip->tape_blocks[0])
+				return 1;
+		}
+		if (partition_tape(devip, 1, TAPE_UNITS, 0) < 0) {
+			kfree(devip->tape_blocks[0]);
+			devip->tape_blocks[0] = NULL;
+			return 1;
+		}
+	}
 	sdp->hostdata = devip;
 	if (sdebug_no_uld)
 		sdp->no_uld_attach = 1;
@@ -6108,6 +6310,11 @@ static void scsi_debug_sdev_destroy(struct scsi_device *sdp)
 
 	debugfs_remove(devip->debugfs_entry);
 
+	if (sdebug_ptype == TYPE_TAPE) {
+		kfree(devip->tape_blocks[0]);
+		devip->tape_blocks[0] = NULL;
+	}
+
 	/* make this slot available for re-use */
 	devip->used = false;
 	sdp->hostdata = NULL;
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v2 4/7] scsi: scsi_debug: Add read support and update locate for tapes
  2025-02-13  9:26 [PATCH v2 0/7] scsi: scsi_debug: Add more tape support Kai Mäkisara
                   ` (2 preceding siblings ...)
  2025-02-13  9:26 ` [PATCH v2 3/7] scsi: scsi_debug: Add write support with block lengths and 4 bytes of data Kai Mäkisara
@ 2025-02-13  9:26 ` Kai Mäkisara
  2025-02-19 21:56   ` John Meneghini
  2025-02-13  9:26 ` [PATCH v2 5/7] scsi: scsi_debug: Add compression mode page " Kai Mäkisara
                   ` (6 subsequent siblings)
  10 siblings, 1 reply; 21+ messages in thread
From: Kai Mäkisara @ 2025-02-13  9:26 UTC (permalink / raw)
  To: linux-scsi, dgilbert
  Cc: martin.petersen, James.Bottomley, jmeneghi, Kai Mäkisara

Support for the READ (6) and SPACE (6) commands for tapes based on the
previous write patch is added. The LOCATE (10) command is updated to use
the written data.

Signed-off-by: Kai Mäkisara <Kai.Makisara@kolumbus.fi>
---
v1 -> v2:
- initialized i to zero in resp_space() to fix the bug reported by
  the Kernel Test Robot

drivers/scsi/scsi_debug.c | 240 +++++++++++++++++++++++++++++++++++++-
 1 file changed, 235 insertions(+), 5 deletions(-)

diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index 69cae4c1712a..29a9aea30d22 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -532,7 +532,8 @@ enum sdeb_opcode_index {
 	SDEB_I_READ_BLOCK_LIMITS = 33,
 	SDEB_I_LOCATE = 34,
 	SDEB_I_WRITE_FILEMARKS = 35,
-	SDEB_I_LAST_ELEM_P1 = 36,	/* keep this last (previous + 1) */
+	SDEB_I_SPACE = 36,
+	SDEB_I_LAST_ELEM_P1 = 37,	/* keep this last (previous + 1) */
 };
 
 
@@ -541,7 +542,7 @@ static const unsigned char opcode_ind_arr[256] = {
 	SDEB_I_TEST_UNIT_READY, SDEB_I_REZERO_UNIT, 0, SDEB_I_REQUEST_SENSE,
 	    0, SDEB_I_READ_BLOCK_LIMITS, 0, 0,
 	SDEB_I_READ, 0, SDEB_I_WRITE, 0, 0, 0, 0, 0,
-	SDEB_I_WRITE_FILEMARKS, 0, SDEB_I_INQUIRY, 0, 0,
+	SDEB_I_WRITE_FILEMARKS, SDEB_I_SPACE, SDEB_I_INQUIRY, 0, 0,
 	    SDEB_I_MODE_SELECT, SDEB_I_RESERVE, SDEB_I_RELEASE,
 	0, 0, SDEB_I_MODE_SENSE, SDEB_I_START_STOP, 0, SDEB_I_SEND_DIAG,
 	    SDEB_I_ALLOW_REMOVAL, 0,
@@ -625,6 +626,7 @@ static int resp_rwp_zone(struct scsi_cmnd *, struct sdebug_dev_info *);
 static int resp_read_blklimits(struct scsi_cmnd *, struct sdebug_dev_info *);
 static int resp_locate(struct scsi_cmnd *, struct sdebug_dev_info *);
 static int resp_write_filemarks(struct scsi_cmnd *, struct sdebug_dev_info *);
+static int resp_space(struct scsi_cmnd *, struct sdebug_dev_info *);
 static int resp_rewind(struct scsi_cmnd *, struct sdebug_dev_info *);
 
 static int sdebug_do_add_host(bool mk_new_store);
@@ -872,10 +874,12 @@ static const struct opcode_info_t opcode_info_arr[SDEB_I_LAST_ELEM_P1 + 1] = {
 	{0, 0x05, 0, F_D_IN, resp_read_blklimits, NULL,    /* READ BLOCK LIMITS (6) */
 	    {6,  0, 0, 0, 0, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
 	{0, 0x2b, 0, F_D_UNKN, resp_locate, NULL,    /* LOCATE (10) */
-	    {10,  0x2, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xc7, 0, 0,
+	    {10,  0x07, 0, 0xff, 0xff, 0xff, 0xff, 0, 0xff, 0xc7, 0, 0,
 	     0, 0, 0, 0} },
 	{0, 0x10, 0, F_D_IN, resp_write_filemarks, NULL,    /* WRITE FILEMARKS (6) */
 	    {6,  0x01, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
+	{0, 0x11, 0, F_D_IN, resp_space, NULL,    /* SPACE (6) */
+	    {6,  0x07, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
 
 /* sentinel */
 	{0xff, 0, 0, 0, NULL, NULL,		/* terminating element */
@@ -3309,6 +3313,9 @@ static int resp_locate(struct scsi_cmnd *scp,
 		struct sdebug_dev_info *devip)
 {
 	unsigned char *cmd = scp->cmnd;
+	unsigned int i, pos;
+	struct tape_block *blp;
+	int partition;
 
 	if ((cmd[1] & 0x02) != 0) {
 		if (cmd[8] >= devip->tape_nbr_partitions) {
@@ -3317,8 +3324,19 @@ static int resp_locate(struct scsi_cmnd *scp,
 		}
 		devip->tape_partition = cmd[8];
 	}
-	devip->tape_location[devip->tape_partition] =
-		get_unaligned_be32(cmd + 3);
+	pos = get_unaligned_be32(cmd + 3);
+	partition = devip->tape_partition;
+
+	for (i = 0, blp = devip->tape_blocks[partition];
+	     i < pos && i < devip->tape_eop[partition]; i++, blp++)
+		if (IS_TAPE_BLOCK_EOD(blp->fl_size))
+			break;
+	if (i < pos) {
+		devip->tape_location[partition] = i;
+		mk_sense_buffer(scp, BLANK_CHECK, 0x05, 0);
+		return check_condition_result;
+	}
+	devip->tape_location[partition] = pos;
 
 	return 0;
 }
@@ -3353,6 +3371,123 @@ static int resp_write_filemarks(struct scsi_cmnd *scp,
 	return 0;
 }
 
+static int resp_space(struct scsi_cmnd *scp,
+		struct sdebug_dev_info *devip)
+{
+	unsigned char *cmd = scp->cmnd, code;
+	int i = 0, pos, count;
+	struct tape_block *blp;
+	int partition = devip->tape_partition;
+
+	count = get_unaligned_be24(cmd + 2);
+	if ((count & 0x800000) != 0) /* extend negative to 32-bit count */
+		count |= 0xff000000;
+	code = cmd[1] & 0x0f;
+
+	pos = devip->tape_location[partition];
+	if (code == 0) { /* blocks */
+		if (count < 0) {
+			count = (-count);
+			pos -= 1;
+			for (i = 0, blp = devip->tape_blocks[partition] + pos; i < count;
+			     i++) {
+				if (pos < 0)
+					goto is_bop;
+				else if (IS_TAPE_BLOCK_FM(blp->fl_size))
+					goto is_fm;
+				if (i > 0) {
+					pos--;
+					blp--;
+				}
+			}
+		} else if (count > 0) {
+			for (i = 0, blp = devip->tape_blocks[partition] + pos; i < count;
+			     i++, pos++, blp++) {
+				if (IS_TAPE_BLOCK_EOD(blp->fl_size))
+					goto is_eod;
+				if (IS_TAPE_BLOCK_FM(blp->fl_size)) {
+					pos += 1;
+					goto is_fm;
+				}
+				if (pos >= devip->tape_eop[partition])
+					goto is_eop;
+			}
+		}
+	} else if (code == 1) { /* filemarks */
+		if (count < 0) {
+			count = (-count);
+			if (pos == 0)
+				goto is_bop;
+			else {
+				for (i = 0, blp = devip->tape_blocks[partition] + pos;
+				     i < count && pos >= 0; i++, pos--, blp--) {
+					for (pos--, blp-- ; !IS_TAPE_BLOCK_FM(blp->fl_size) &&
+						     pos >= 0; pos--, blp--)
+						; /* empty */
+					if (pos < 0)
+						goto is_bop;
+				}
+			}
+			pos += 1;
+		} else if (count > 0) {
+			for (i = 0, blp = devip->tape_blocks[partition] + pos;
+			     i < count; i++, pos++, blp++) {
+				for ( ; !IS_TAPE_BLOCK_FM(blp->fl_size) &&
+					      !IS_TAPE_BLOCK_EOD(blp->fl_size) &&
+					      pos < devip->tape_eop[partition];
+				      pos++, blp++)
+					; /* empty */
+				if (IS_TAPE_BLOCK_EOD(blp->fl_size))
+					goto is_eod;
+				if (pos >= devip->tape_eop[partition])
+					goto is_eop;
+			}
+		}
+	} else if (code == 3) { /* EOD */
+		for (blp = devip->tape_blocks[partition] + pos;
+		     !IS_TAPE_BLOCK_EOD(blp->fl_size) && pos < devip->tape_eop[partition];
+		     pos++, blp++)
+			; /* empty */
+		if (pos >= devip->tape_eop[partition])
+			goto is_eop;
+	} else {
+		/* sequential filemarks not supported */
+		mk_sense_invalid_fld(scp, SDEB_IN_CDB, 8, -1);
+		return check_condition_result;
+	}
+	devip->tape_location[partition] = pos;
+	return 0;
+
+is_fm:
+	devip->tape_location[partition] = pos;
+	mk_sense_info_tape(scp, NO_SENSE, NO_ADDITIONAL_SENSE,
+			FILEMARK_DETECTED_ASCQ, count - i,
+			SENSE_FLAG_FILEMARK);
+	return check_condition_result;
+
+is_eod:
+	devip->tape_location[partition] = pos;
+	mk_sense_info_tape(scp, BLANK_CHECK, NO_ADDITIONAL_SENSE,
+			EOD_DETECTED_ASCQ, count - i,
+			0);
+	return check_condition_result;
+
+is_bop:
+	devip->tape_location[partition] = 0;
+	mk_sense_info_tape(scp, NO_SENSE, NO_ADDITIONAL_SENSE,
+			BEGINNING_OF_P_M_DETECTED_ASCQ, count - i,
+			SENSE_FLAG_EOM);
+	devip->tape_location[partition] = 0;
+	return check_condition_result;
+
+is_eop:
+	devip->tape_location[partition] = devip->tape_eop[partition] - 1;
+	mk_sense_info_tape(scp, MEDIUM_ERROR, NO_ADDITIONAL_SENSE,
+			EOP_EOM_DETECTED_ASCQ, (unsigned int)i,
+			SENSE_FLAG_EOM);
+	return check_condition_result;
+}
+
 static int resp_rewind(struct scsi_cmnd *scp,
 		struct sdebug_dev_info *devip)
 {
@@ -4121,6 +4256,98 @@ static int prot_verify_read(struct scsi_cmnd *scp, sector_t start_sec,
 	return ret;
 }
 
+static int resp_read_tape(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
+{
+	u32 i, num, transfer, size;
+	u8 *cmd = scp->cmnd;
+	struct scsi_data_buffer *sdb = &scp->sdb;
+	int partition = devip->tape_partition;
+	u32 pos = devip->tape_location[partition];
+	struct tape_block *blp;
+	bool fixed, sili;
+
+	if (cmd[0] != READ_6) { /* Only Read(6) supported */
+		mk_sense_invalid_opcode(scp);
+		return illegal_condition_result;
+	}
+	fixed = (cmd[1] & 0x1) != 0;
+	sili = (cmd[1] & 0x2) != 0;
+	if (fixed && sili) {
+		mk_sense_invalid_fld(scp, SDEB_IN_CDB, 1, 1);
+		return check_condition_result;
+	}
+
+	transfer = get_unaligned_be24(cmd + 2);
+	if (fixed) {
+		num = transfer;
+		size = devip->tape_blksize;
+	} else {
+		if (transfer < TAPE_MIN_BLKSIZE ||
+			transfer > TAPE_MAX_BLKSIZE) {
+			mk_sense_invalid_fld(scp, SDEB_IN_CDB, 2, -1);
+			return check_condition_result;
+		}
+		num = 1;
+		size = transfer;
+	}
+
+	for (i = 0, blp = devip->tape_blocks[partition] + pos;
+	     i < num && pos < devip->tape_eop[partition];
+	     i++, pos++, blp++) {
+		devip->tape_location[partition] = pos + 1;
+		if (IS_TAPE_BLOCK_FM(blp->fl_size)) {
+			mk_sense_info_tape(scp, NO_SENSE, NO_ADDITIONAL_SENSE,
+					FILEMARK_DETECTED_ASCQ, fixed ? num - i : size,
+					SENSE_FLAG_FILEMARK);
+			scsi_set_resid(scp, (num - i) * size);
+			return check_condition_result;
+		}
+		/* Assume no REW */
+		if (IS_TAPE_BLOCK_EOD(blp->fl_size)) {
+			mk_sense_info_tape(scp, BLANK_CHECK, NO_ADDITIONAL_SENSE,
+					EOD_DETECTED_ASCQ, fixed ? num - i : size,
+					0);
+			devip->tape_location[partition] = pos;
+			scsi_set_resid(scp, (num - i) * size);
+			return check_condition_result;
+		}
+		sg_zero_buffer(sdb->table.sgl, sdb->table.nents,
+			size, i * size);
+		sg_copy_buffer(sdb->table.sgl, sdb->table.nents,
+			&(blp->data), 4, i * size, false);
+		if (fixed) {
+			if (blp->fl_size != devip->tape_blksize) {
+				scsi_set_resid(scp, (num - i) * size);
+				mk_sense_info_tape(scp, NO_SENSE, NO_ADDITIONAL_SENSE,
+						0, num - i,
+						SENSE_FLAG_ILI);
+				return check_condition_result;
+			}
+		} else {
+			if (blp->fl_size != size) {
+				if (blp->fl_size < size)
+					scsi_set_resid(scp, size - blp->fl_size);
+				if (!sili) {
+					mk_sense_info_tape(scp, NO_SENSE, NO_ADDITIONAL_SENSE,
+							0, size - blp->fl_size,
+							SENSE_FLAG_ILI);
+					return check_condition_result;
+				}
+			}
+		}
+	}
+	if (pos >= devip->tape_eop[partition]) {
+		mk_sense_info_tape(scp, NO_SENSE, NO_ADDITIONAL_SENSE,
+				EOP_EOM_DETECTED_ASCQ, fixed ? num - i : size,
+				SENSE_FLAG_EOM);
+		devip->tape_location[partition] = pos - 1;
+		return check_condition_result;
+	}
+	devip->tape_location[partition] = pos;
+
+	return 0;
+}
+
 static int resp_read_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
 {
 	bool check_prot;
@@ -4132,6 +4359,9 @@ static int resp_read_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
 	u8 *cmd = scp->cmnd;
 	bool meta_data_locked = false;
 
+	if (sdebug_ptype == TYPE_TAPE)
+		return resp_read_tape(scp, devip);
+
 	switch (cmd[0]) {
 	case READ_16:
 		ei_lba = 0;
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v2 5/7] scsi: scsi_debug: Add compression mode page for tapes
  2025-02-13  9:26 [PATCH v2 0/7] scsi: scsi_debug: Add more tape support Kai Mäkisara
                   ` (3 preceding siblings ...)
  2025-02-13  9:26 ` [PATCH v2 4/7] scsi: scsi_debug: Add read support and update locate for tapes Kai Mäkisara
@ 2025-02-13  9:26 ` Kai Mäkisara
  2025-02-19 21:58   ` John Meneghini
  2025-02-13  9:26 ` [PATCH v2 6/7] scsi: scsi_debug: Reset tape setting at device reset Kai Mäkisara
                   ` (5 subsequent siblings)
  10 siblings, 1 reply; 21+ messages in thread
From: Kai Mäkisara @ 2025-02-13  9:26 UTC (permalink / raw)
  To: linux-scsi, dgilbert
  Cc: martin.petersen, James.Bottomley, jmeneghi, Kai Mäkisara

Add support for compression mode page. The compression status
is saved and returned. No UA is generated.

Signed-off-by: Kai Mäkisara <Kai.Makisara@kolumbus.fi>
---
 drivers/scsi/scsi_debug.c | 33 +++++++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index 29a9aea30d22..0a5cd35c41de 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -405,6 +405,7 @@ struct sdebug_dev_info {
 	unsigned int tape_density;
 	unsigned char tape_partition;
 	unsigned char tape_nbr_partitions;
+	unsigned char tape_dce;
 	unsigned int tape_location[TAPE_MAX_PARTITIONS];
 	unsigned int tape_eop[TAPE_MAX_PARTITIONS];
 	struct tape_block *tape_blocks[TAPE_MAX_PARTITIONS];
@@ -2843,6 +2844,20 @@ static int resp_partition_m_pg(unsigned char *p, int pcontrol, int target)
 	return sizeof(partition_pg);
 }
 
+static int resp_compression_m_pg(unsigned char *p, int pcontrol, int target,
+	unsigned char dce)
+{	/* Compression page for mode_sense (tape) */
+	unsigned char compression_pg[] = {0x0f, 14, 0x40, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 00, 00};
+
+	memcpy(p, compression_pg, sizeof(compression_pg));
+	if (dce)
+		p[2] |= 0x80;
+	if (pcontrol == 1)
+		memset(p + 2, 0, sizeof(compression_pg) - 2);
+	return sizeof(compression_pg);
+}
+
 /* PAGE_SIZE is more than necessary but provides room for future expansion. */
 #define SDEBUG_MAX_MSENSE_SZ PAGE_SIZE
 
@@ -2983,6 +2998,12 @@ static int resp_mode_sense(struct scsi_cmnd *scp,
 		}
 		offset += len;
 		break;
+	case 0xf:	/* Compression Mode Page (tape) */
+		if (!is_tape)
+			goto bad_pcode;
+		len += resp_compression_m_pg(ap, pcontrol, target, devip->tape_dce);
+		offset += len;
+		break;
 	case 0x11:	/* Partition Mode Page (tape) */
 		if (!is_tape)
 			goto bad_pcode;
@@ -3143,6 +3164,14 @@ static int resp_mode_select(struct scsi_cmnd *scp,
 			goto set_mode_changed_ua;
 		}
 		break;
+	case 0xf:       /* Compression mode page */
+		if (sdebug_ptype != TYPE_TAPE)
+			goto bad_pcode;
+		if ((arr[off + 2] & 0x40) != 0) {
+			devip->tape_dce = (arr[off + 2] & 0x80) != 0;
+			return 0;
+		}
+		break;
 	case 0x1c:      /* Informational Exceptions Mode page */
 		if (iec_m_pg[1] == arr[off + 1]) {
 			memcpy(iec_m_pg + 2, arr + off + 2,
@@ -3158,6 +3187,10 @@ static int resp_mode_select(struct scsi_cmnd *scp,
 set_mode_changed_ua:
 	set_bit(SDEBUG_UA_MODE_CHANGED, devip->uas_bm);
 	return 0;
+
+bad_pcode:
+	mk_sense_invalid_fld(scp, SDEB_IN_CDB, 2, 5);
+	return check_condition_result;
 }
 
 static int resp_temp_l_pg(unsigned char *arr)
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v2 6/7] scsi: scsi_debug: Reset tape setting at device reset
  2025-02-13  9:26 [PATCH v2 0/7] scsi: scsi_debug: Add more tape support Kai Mäkisara
                   ` (4 preceding siblings ...)
  2025-02-13  9:26 ` [PATCH v2 5/7] scsi: scsi_debug: Add compression mode page " Kai Mäkisara
@ 2025-02-13  9:26 ` Kai Mäkisara
  2025-02-19 21:58   ` John Meneghini
  2025-02-13  9:26 ` [PATCH v2 7/7] scsi: scsi_debug: Add support for partitioning the tape Kai Mäkisara
                   ` (4 subsequent siblings)
  10 siblings, 1 reply; 21+ messages in thread
From: Kai Mäkisara @ 2025-02-13  9:26 UTC (permalink / raw)
  To: linux-scsi, dgilbert
  Cc: martin.petersen, James.Bottomley, jmeneghi, Kai Mäkisara

Set tape block size, density and compression to default values when the
device is reset (either directly or via target, bus or host reset).

Signed-off-by: Kai Mäkisara <Kai.Makisara@kolumbus.fi>
---
 drivers/scsi/scsi_debug.c | 21 ++++++++++++++++++++-
 1 file changed, 20 insertions(+), 1 deletion(-)

diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index 0a5cd35c41de..7da1758da3f5 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -6772,6 +6772,20 @@ static int sdebug_fail_lun_reset(struct scsi_cmnd *cmnd)
 	return 0;
 }
 
+static void scsi_tape_reset_clear(struct sdebug_dev_info *devip)
+{
+	if (sdebug_ptype == TYPE_TAPE) {
+		int i;
+
+		devip->tape_blksize = TAPE_DEF_BLKSIZE;
+		devip->tape_density = TAPE_DEF_DENSITY;
+		devip->tape_partition = 0;
+		devip->tape_dce = 0;
+		for (i = 0; i < TAPE_MAX_PARTITIONS; i++)
+			devip->tape_location[i] = 0;
+	}
+}
+
 static int scsi_debug_device_reset(struct scsi_cmnd *SCpnt)
 {
 	struct scsi_device *sdp = SCpnt->device;
@@ -6785,8 +6799,10 @@ static int scsi_debug_device_reset(struct scsi_cmnd *SCpnt)
 		sdev_printk(KERN_INFO, sdp, "%s\n", __func__);
 
 	scsi_debug_stop_all_queued(sdp);
-	if (devip)
+	if (devip) {
 		set_bit(SDEBUG_UA_POR, devip->uas_bm);
+		scsi_tape_reset_clear(devip);
+	}
 
 	if (sdebug_fail_lun_reset(SCpnt)) {
 		scmd_printk(KERN_INFO, SCpnt, "fail lun reset 0x%x\n", opcode);
@@ -6824,6 +6840,7 @@ static int scsi_debug_target_reset(struct scsi_cmnd *SCpnt)
 	list_for_each_entry(devip, &sdbg_host->dev_info_list, dev_list) {
 		if (devip->target == sdp->id) {
 			set_bit(SDEBUG_UA_BUS_RESET, devip->uas_bm);
+			scsi_tape_reset_clear(devip);
 			++k;
 		}
 	}
@@ -6855,6 +6872,7 @@ static int scsi_debug_bus_reset(struct scsi_cmnd *SCpnt)
 
 	list_for_each_entry(devip, &sdbg_host->dev_info_list, dev_list) {
 		set_bit(SDEBUG_UA_BUS_RESET, devip->uas_bm);
+		scsi_tape_reset_clear(devip);
 		++k;
 	}
 
@@ -6878,6 +6896,7 @@ static int scsi_debug_host_reset(struct scsi_cmnd *SCpnt)
 		list_for_each_entry(devip, &sdbg_host->dev_info_list,
 				    dev_list) {
 			set_bit(SDEBUG_UA_BUS_RESET, devip->uas_bm);
+			scsi_tape_reset_clear(devip);
 			++k;
 		}
 	}
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v2 7/7] scsi: scsi_debug: Add support for partitioning the tape
  2025-02-13  9:26 [PATCH v2 0/7] scsi: scsi_debug: Add more tape support Kai Mäkisara
                   ` (5 preceding siblings ...)
  2025-02-13  9:26 ` [PATCH v2 6/7] scsi: scsi_debug: Reset tape setting at device reset Kai Mäkisara
@ 2025-02-13  9:26 ` Kai Mäkisara
  2025-02-19 22:17   ` John Meneghini
  2025-02-13 16:12 ` [PATCH v2 0/7] scsi: scsi_debug: Add more tape support John Meneghini
                   ` (3 subsequent siblings)
  10 siblings, 1 reply; 21+ messages in thread
From: Kai Mäkisara @ 2025-02-13  9:26 UTC (permalink / raw)
  To: linux-scsi, dgilbert
  Cc: martin.petersen, James.Bottomley, jmeneghi, Kai Mäkisara

This patch adds support for MEDIUM PARTITION PAGE in MODE SELECT and the
FORMAT MEDIUM command for tapes. After these additions, the virtual tape
can be partitioned containing either one or two partitions. The POFM
flag in the mode page is set, meaning that the FORMAT MEDIUM command
must be used to create the partitioning defined in the mode page.

Signed-off-by: Kai Mäkisara <Kai.Makisara@kolumbus.fi>
---
 drivers/scsi/scsi_debug.c | 108 ++++++++++++++++++++++++++++++++++++--
 1 file changed, 104 insertions(+), 4 deletions(-)

diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index 7da1758da3f5..74b136da55ce 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -188,6 +188,7 @@ static const char *sdebug_version_date = "20210520";
 #define TAPE_EW 20
 #define TAPE_MAX_PARTITIONS 2
 #define TAPE_UNITS 10000
+#define TAPE_PARTITION_1_UNITS 1000
 
 /* The tape block data definitions */
 #define TAPE_BLOCK_FM_FLAG   ((u32)0x1 << 30)
@@ -405,6 +406,9 @@ struct sdebug_dev_info {
 	unsigned int tape_density;
 	unsigned char tape_partition;
 	unsigned char tape_nbr_partitions;
+	unsigned char tape_pending_nbr_partitions;
+	unsigned int tape_pending_part_0_size;
+	unsigned int tape_pending_part_1_size;
 	unsigned char tape_dce;
 	unsigned int tape_location[TAPE_MAX_PARTITIONS];
 	unsigned int tape_eop[TAPE_MAX_PARTITIONS];
@@ -534,14 +538,15 @@ enum sdeb_opcode_index {
 	SDEB_I_LOCATE = 34,
 	SDEB_I_WRITE_FILEMARKS = 35,
 	SDEB_I_SPACE = 36,
-	SDEB_I_LAST_ELEM_P1 = 37,	/* keep this last (previous + 1) */
+	SDEB_I_FORMAT_MEDIUM = 37,
+	SDEB_I_LAST_ELEM_P1 = 38,	/* keep this last (previous + 1) */
 };
 
 
 static const unsigned char opcode_ind_arr[256] = {
 /* 0x0; 0x0->0x1f: 6 byte cdbs */
 	SDEB_I_TEST_UNIT_READY, SDEB_I_REZERO_UNIT, 0, SDEB_I_REQUEST_SENSE,
-	    0, SDEB_I_READ_BLOCK_LIMITS, 0, 0,
+	    SDEB_I_FORMAT_MEDIUM, SDEB_I_READ_BLOCK_LIMITS, 0, 0,
 	SDEB_I_READ, 0, SDEB_I_WRITE, 0, 0, 0, 0, 0,
 	SDEB_I_WRITE_FILEMARKS, SDEB_I_SPACE, SDEB_I_INQUIRY, 0, 0,
 	    SDEB_I_MODE_SELECT, SDEB_I_RESERVE, SDEB_I_RELEASE,
@@ -629,6 +634,7 @@ static int resp_locate(struct scsi_cmnd *, struct sdebug_dev_info *);
 static int resp_write_filemarks(struct scsi_cmnd *, struct sdebug_dev_info *);
 static int resp_space(struct scsi_cmnd *, struct sdebug_dev_info *);
 static int resp_rewind(struct scsi_cmnd *, struct sdebug_dev_info *);
+static int resp_format_medium(struct scsi_cmnd *, struct sdebug_dev_info *);
 
 static int sdebug_do_add_host(bool mk_new_store);
 static int sdebug_add_host_helper(int per_host_idx);
@@ -867,7 +873,7 @@ static const struct opcode_info_t opcode_info_arr[SDEB_I_LAST_ELEM_P1 + 1] = {
 	    resp_report_zones, zone_in_iarr, /* ZONE_IN(16), REPORT ZONES) */
 		{16,  0x0 /* SA */, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0xc7} },
-/* 31 */
+/* 32 */
 	{0, 0x0, 0x0, F_D_OUT | FF_MEDIA_IO,
 	    resp_atomic_write, NULL, /* ATOMIC WRITE 16 */
 		{16,  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
@@ -881,7 +887,9 @@ static const struct opcode_info_t opcode_info_arr[SDEB_I_LAST_ELEM_P1 + 1] = {
 	    {6,  0x01, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
 	{0, 0x11, 0, F_D_IN, resp_space, NULL,    /* SPACE (6) */
 	    {6,  0x07, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
-
+	{0, 0x4, 0, 0, resp_format_medium, NULL,  /* FORMAT MEDIUM (6) */
+	    {6,  0x3, 0x7, 0, 0, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
+/* 38 */
 /* sentinel */
 	{0xff, 0, 0, 0, NULL, NULL,		/* terminating element */
 	    {0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
@@ -2844,6 +2852,51 @@ static int resp_partition_m_pg(unsigned char *p, int pcontrol, int target)
 	return sizeof(partition_pg);
 }
 
+static int process_medium_part_m_pg(struct sdebug_dev_info *devip,
+				unsigned char *new, int pg_len)
+{
+	int new_nbr, p0_size, p1_size;
+
+	if ((new[4] & 0x80) != 0) { /* FDP */
+		partition_pg[4] |= 0x80;
+		devip->tape_pending_nbr_partitions = TAPE_MAX_PARTITIONS;
+		devip->tape_pending_part_0_size = TAPE_UNITS - TAPE_PARTITION_1_UNITS;
+		devip->tape_pending_part_1_size = TAPE_PARTITION_1_UNITS;
+	} else {
+		new_nbr = new[3] + 1;
+		if (new_nbr > TAPE_MAX_PARTITIONS)
+			return 3;
+		if ((new[4] & 0x40) != 0) { /* SDP */
+			p1_size = TAPE_PARTITION_1_UNITS;
+			p0_size = TAPE_UNITS - p1_size;
+			if (p0_size < 100)
+				return 4;
+		} else if ((new[4] & 0x20) != 0) {
+			if (new_nbr > 1) {
+				p0_size = get_unaligned_be16(new + 8);
+				p1_size = get_unaligned_be16(new + 10);
+				if (p1_size == 0xFFFF)
+					p1_size = TAPE_UNITS - p0_size;
+				else if (p0_size == 0xFFFF)
+					p0_size = TAPE_UNITS - p1_size;
+				if (p0_size < 100 || p1_size < 100)
+					return 8;
+			} else {
+				p0_size = TAPE_UNITS;
+				p1_size = 0;
+			}
+		} else
+			return 6;
+		devip->tape_pending_nbr_partitions = new_nbr;
+		devip->tape_pending_part_0_size = p0_size;
+		devip->tape_pending_part_1_size = p1_size;
+		partition_pg[3] = new_nbr;
+		devip->tape_pending_nbr_partitions = new_nbr;
+	}
+
+	return 0;
+}
+
 static int resp_compression_m_pg(unsigned char *p, int pcontrol, int target,
 	unsigned char dce)
 {	/* Compression page for mode_sense (tape) */
@@ -3172,6 +3225,17 @@ static int resp_mode_select(struct scsi_cmnd *scp,
 			return 0;
 		}
 		break;
+	case 0x11:	/* Medium Partition Mode Page (tape) */
+		if (sdebug_ptype == TYPE_TAPE) {
+			int fld;
+
+			fld = process_medium_part_m_pg(devip, &arr[off], pg_len);
+			if (fld == 0)
+				return 0;
+			mk_sense_invalid_fld(scp, SDEB_IN_DATA, fld, -1);
+			return check_condition_result;
+		}
+		break;
 	case 0x1c:      /* Informational Exceptions Mode page */
 		if (iec_m_pg[1] == arr[off + 1]) {
 			memcpy(iec_m_pg + 2, arr + off + 2,
@@ -3556,6 +3620,39 @@ static int partition_tape(struct sdebug_dev_info *devip, int nbr_partitions,
 	return nbr_partitions;
 }
 
+static int resp_format_medium(struct scsi_cmnd *scp,
+			struct sdebug_dev_info *devip)
+{
+	int res = 0;
+	unsigned char *cmd = scp->cmnd;
+
+	if (sdebug_ptype != TYPE_TAPE) {
+		mk_sense_invalid_fld(scp, SDEB_IN_CDB, 0, -1);
+		return check_condition_result;
+	}
+	if (cmd[2] > 2) {
+		mk_sense_invalid_fld(scp, SDEB_IN_DATA, 2, -1);
+		return check_condition_result;
+	}
+	if (cmd[2] != 0) {
+		if (devip->tape_pending_nbr_partitions > 0) {
+			res = partition_tape(devip,
+					devip->tape_pending_nbr_partitions,
+					devip->tape_pending_part_0_size,
+					devip->tape_pending_part_1_size);
+		} else
+			res = partition_tape(devip, devip->tape_nbr_partitions,
+					devip->tape_eop[0], devip->tape_eop[1]);
+	} else
+		res = partition_tape(devip, 1, TAPE_UNITS, 0);
+	if (res < 0)
+		return -EINVAL;
+
+	devip->tape_pending_nbr_partitions = -1;
+
+	return 0;
+}
+
 static inline bool sdebug_dev_is_zoned(struct sdebug_dev_info *devip)
 {
 	return devip->nr_zones != 0;
@@ -6522,6 +6619,7 @@ static int scsi_debug_sdev_configure(struct scsi_device *sdp,
 			if (!devip->tape_blocks[0])
 				return 1;
 		}
+		devip->tape_pending_nbr_partitions = -1;
 		if (partition_tape(devip, 1, TAPE_UNITS, 0) < 0) {
 			kfree(devip->tape_blocks[0]);
 			devip->tape_blocks[0] = NULL;
@@ -6783,6 +6881,8 @@ static void scsi_tape_reset_clear(struct sdebug_dev_info *devip)
 		devip->tape_dce = 0;
 		for (i = 0; i < TAPE_MAX_PARTITIONS; i++)
 			devip->tape_location[i] = 0;
+		devip->tape_pending_nbr_partitions = -1;
+		/* Don't reset partitioning? */
 	}
 }
 
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 0/7] scsi: scsi_debug: Add more tape support
  2025-02-13  9:26 [PATCH v2 0/7] scsi: scsi_debug: Add more tape support Kai Mäkisara
                   ` (6 preceding siblings ...)
  2025-02-13  9:26 ` [PATCH v2 7/7] scsi: scsi_debug: Add support for partitioning the tape Kai Mäkisara
@ 2025-02-13 16:12 ` John Meneghini
  2025-02-19 22:32 ` John Meneghini
                   ` (2 subsequent siblings)
  10 siblings, 0 replies; 21+ messages in thread
From: John Meneghini @ 2025-02-13 16:12 UTC (permalink / raw)
  To: Kai Mäkisara, linux-scsi, dgilbert; +Cc: martin.petersen, James.Bottomley

Thanks for all the patches Kai.

I will work on reviewing and testing these patches ASAP.

BTW, the st driver fixes you delivered in v6.13 and v6.14-rc1 are all working.

We have already resolved some customer reported tape issues and regressions with these
patches and I'm shipping them in RHEL-8.10z and RHEL-9.*.

John A. Meneghini
Senior Principal Platform Storage Engineer
RHEL SST - Platform Storage Group
jmeneghi@redhat.com

On 2/13/25 4:26 AM, Kai Mäkisara wrote:
> Currently, the scsi_debug driver can create tape devices and the st
> driver attaches to those. Nothing much can be done with the tape devices
> because scsi_debug does not have support for the tape-specific commands
> and features. These patches add some more tape support to the scsi_debug
> driver. The end result is simulated drives with a tape having one or two
> partitions (one partition is created initially).
> 
> The tape is implemented as fixed number (10 000) of 8-byte units.
> The first four bytes of a unit contain the type of the unit (data
> block, filemark or end-of-data mark). If the units is a data block,
> the first four bytes also contain the block length and the remaining
> four bytes the first bytes of written data. This allows the user
> to use tags to see that the read block is what it was supposed to be.
> 
> The following SCSI operations are added or modified:
> FORMAT MEDIUM
> - added
> LOCATE
> - added
> MODE SELECT
> - modified to allow use without page(s) (just header and block descriptor)
>    - store density and block size
> - partition page added
> MODE SENSE
> - modified to allow use without page(s) (just header and block descriptor)
>    - set density and block size
> - partition page added
> READ BLOCK LIMITS
> - added
> READ POSITION
> - added
> READ
> - added tape support for READ (6)
> REWIND
> - modified to set the tape position
> SPACE
> - added
> START STOP (LOAD)
> - modified to return New Medium Unit Attention if tape loaded (not
>    according to the standard, but enables testing this UA)
> WRITE
> - added tape support for WRITE (6)
> WRITE FILEMARKS
> - added
> 
> Changes RFC -> v1:
> - rebased to v6.14-rc1
> - virtual tape initialization is rewritten and the tape is made shorter
>    (10 000 units)
> - only one partition is created initially
> - tape block allocation is moved to sdev_configure()
> - tape blocks are freed in sdev_destroy()
> - block size must be multiple of four (SSC standard)
> - granularity set to four in READ BLOCK LIMITS
> - long LBA not allowed for tapes in MODE SELECT/SENSE
> - READ POSITION checks allocation length
> - new patch 7 adds support for re-partitioning the tape
> 
> Changes v1 -> v2:
> - Fixes for bugs reported by the Kernel Test Robot:
>    2/7: changed 'len +=' to 'len =' in resp_mode_sense()
>    3/7: changed 'for (;' to 'for (i = 0;' in partition_tape()
>    4/7: initialized i to zero in resp_space()
> 
> Kai Mäkisara (7):
>    scsi: scsi_debug: First fixes for tapes
>    scsi: scsi_debug: Add READ BLOCK LIMITS and modify LOAD for tapes
>    scsi: scsi_debug: Add write support with block lengths  and 4 bytes of
>      data
>    scsi: scsi_debug: Add read support and update locate for tapes
>    scsi: scsi_debug: Add compression mode page for tapes
>    scsi: scsi_debug: Reset tape setting at device reset
>    scsi: scsi_debug: Add support for partitioning the tape
> 
>   drivers/scsi/scsi_debug.c | 775 +++++++++++++++++++++++++++++++++++++-
>   1 file changed, 761 insertions(+), 14 deletions(-)
> 


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 1/7] scsi: scsi_debug: First fixes for tapes
  2025-02-13  9:26 ` [PATCH v2 1/7] scsi: scsi_debug: First fixes for tapes Kai Mäkisara
@ 2025-02-19 16:03   ` John Meneghini
  0 siblings, 0 replies; 21+ messages in thread
From: John Meneghini @ 2025-02-19 16:03 UTC (permalink / raw)
  To: Kai Mäkisara, linux-scsi, dgilbert; +Cc: martin.petersen, James.Bottomley

Reviewed-by: John Meneghini <jmeneghi@redhat.com>
Tested-by: John Meneghini <jmeneghi@redhat.com>

These patches are particularly important because the REWIND command
now works when using the scsi_debug driver with the st.c.

The problem was so bad I had to remove "mt rewind" from my scsi_debug tape tests.
With this patch I've added "mt rewind" back to my tests.

Thanks!

On 2/13/25 4:26 AM, Kai Mäkisara wrote:
> Patch includes the following:
> - enable MODE SENSE/SELECT without actual page (to read/write only
>    the Block Descriptor)
> - store the density code and block size in the Block Descriptor
>    (only short version for tapes)
> - fix REWIND not to use the wrong page filling function
> 
> Signed-off-by: Kai Mäkisara <Kai.Makisara@kolumbus.fi>
> ---
>   drivers/scsi/scsi_debug.c | 55 ++++++++++++++++++++++++++++++++++-----
>   1 file changed, 49 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
> index 5ceaa4665e5d..4da0c259390b 100644
> --- a/drivers/scsi/scsi_debug.c
> +++ b/drivers/scsi/scsi_debug.c
> @@ -173,6 +173,10 @@ static const char *sdebug_version_date = "20210520";
>   #define DEF_ZBC_MAX_OPEN_ZONES	8
>   #define DEF_ZBC_NR_CONV_ZONES	1
>   
> +/* Default parameters for tape drives */
> +#define TAPE_DEF_DENSITY  0x0
> +#define TAPE_DEF_BLKSIZE  0
> +
>   #define SDEBUG_LUN_0_VAL 0
>   
>   /* bit mask values for sdebug_opts */
> @@ -363,6 +367,10 @@ struct sdebug_dev_info {
>   	ktime_t create_ts;	/* time since bootup that this device was created */
>   	struct sdeb_zone_state *zstate;
>   
> +	/* For tapes */
> +	unsigned int tape_blksize;
> +	unsigned int tape_density;
> +
>   	struct dentry *debugfs_entry;
>   	struct spinlock list_lock;
>   	struct list_head inject_err_list;
> @@ -773,7 +781,7 @@ static const struct opcode_info_t opcode_info_arr[SDEB_I_LAST_ELEM_P1 + 1] = {
>   /* 20 */
>   	{0, 0x1e, 0, 0, NULL, NULL, /* ALLOW REMOVAL */
>   	    {6,  0, 0, 0, 0x3, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
> -	{0, 0x1, 0, 0, resp_start_stop, NULL, /* REWIND ?? */
> +	{0, 0x1, 0, 0, NULL, NULL, /* REWIND ?? */
>   	    {6,  0x1, 0, 0, 0, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
>   	{0, 0, 0, F_INV_OP | FF_RESPOND, NULL, NULL, /* ATA_PT */
>   	    {0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
> @@ -2742,7 +2750,7 @@ static int resp_mode_sense(struct scsi_cmnd *scp,
>   	unsigned char *ap;
>   	unsigned char *arr __free(kfree);
>   	unsigned char *cmd = scp->cmnd;
> -	bool dbd, llbaa, msense_6, is_disk, is_zbc;
> +	bool dbd, llbaa, msense_6, is_disk, is_zbc, is_tape;
>   
>   	arr = kzalloc(SDEBUG_MAX_MSENSE_SZ, GFP_ATOMIC);
>   	if (!arr)
> @@ -2755,7 +2763,8 @@ static int resp_mode_sense(struct scsi_cmnd *scp,
>   	llbaa = msense_6 ? false : !!(cmd[1] & 0x10);
>   	is_disk = (sdebug_ptype == TYPE_DISK);
>   	is_zbc = devip->zoned;
> -	if ((is_disk || is_zbc) && !dbd)
> +	is_tape = (sdebug_ptype == TYPE_TAPE);
> +	if ((is_disk || is_zbc || is_tape) && !dbd)
>   		bd_len = llbaa ? 16 : 8;
>   	else
>   		bd_len = 0;
> @@ -2793,15 +2802,25 @@ static int resp_mode_sense(struct scsi_cmnd *scp,
>   			put_unaligned_be32(0xffffffff, ap + 0);
>   		else
>   			put_unaligned_be32(sdebug_capacity, ap + 0);
> -		put_unaligned_be16(sdebug_sector_size, ap + 6);
> +		if (is_tape) {
> +			ap[0] = devip->tape_density;
> +			put_unaligned_be16(devip->tape_blksize, ap + 6);
> +		} else
> +			put_unaligned_be16(sdebug_sector_size, ap + 6);
>   		offset += bd_len;
>   		ap = arr + offset;
>   	} else if (16 == bd_len) {
> +		if (is_tape) {
> +			mk_sense_invalid_fld(scp, SDEB_IN_DATA, 1, 4);
> +			return check_condition_result;
> +		}
>   		put_unaligned_be64((u64)sdebug_capacity, ap + 0);
>   		put_unaligned_be32(sdebug_sector_size, ap + 12);
>   		offset += bd_len;
>   		ap = arr + offset;
>   	}
> +	if (cmd[2] == 0)
> +		goto only_bd; /* Only block descriptor requested */
>   
>   	/*
>   	 * N.B. If len>0 before resp_*_pg() call, then form of that call should be:
> @@ -2902,6 +2921,7 @@ static int resp_mode_sense(struct scsi_cmnd *scp,
>   	default:
>   		goto bad_pcode;
>   	}
> +only_bd:
>   	if (msense_6)
>   		arr[0] = offset - 1;
>   	else
> @@ -2945,8 +2965,27 @@ static int resp_mode_select(struct scsi_cmnd *scp,
>   			    __func__, param_len, res);
>   	md_len = mselect6 ? (arr[0] + 1) : (get_unaligned_be16(arr + 0) + 2);
>   	bd_len = mselect6 ? arr[3] : get_unaligned_be16(arr + 6);
> -	off = bd_len + (mselect6 ? 4 : 8);
> -	if (md_len > 2 || off >= res) {
> +	off = (mselect6 ? 4 : 8);
> +	if (sdebug_ptype == TYPE_TAPE) {
> +		int blksize;
> +
> +		if (bd_len != 8) {
> +			mk_sense_invalid_fld(scp, SDEB_IN_DATA,
> +					mselect6 ? 3 : 6, -1);
> +			return check_condition_result;
> +		}
> +		blksize = get_unaligned_be16(arr + off + 6);
> +		if ((blksize % 4) != 0) {
> +			mk_sense_invalid_fld(scp, SDEB_IN_DATA, off + 6, -1);
> +			return check_condition_result;
> +		}
> +		devip->tape_density = arr[off];
> +		devip->tape_blksize = blksize;
> +	}
> +	off += bd_len;
> +	if (off >= res)
> +		return 0; /* No page written, just descriptors */
> +	if (md_len > 2) {
>   		mk_sense_invalid_fld(scp, SDEB_IN_DATA, 0, -1);
>   		return check_condition_result;
>   	}
> @@ -5835,6 +5874,10 @@ static struct sdebug_dev_info *sdebug_device_create(
>   		} else {
>   			devip->zoned = false;
>   		}
> +		if (sdebug_ptype == TYPE_TAPE) {
> +			devip->tape_density = TAPE_DEF_DENSITY;
> +			devip->tape_blksize = TAPE_DEF_BLKSIZE;
> +		}
>   		devip->create_ts = ktime_get_boottime();
>   		atomic_set(&devip->stopped, (sdeb_tur_ms_to_ready > 0 ? 2 : 0));
>   		spin_lock_init(&devip->list_lock);


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 2/7] scsi: scsi_debug: Add READ BLOCK LIMITS and modify LOAD for tapes
  2025-02-13  9:26 ` [PATCH v2 2/7] scsi: scsi_debug: Add READ BLOCK LIMITS and modify LOAD " Kai Mäkisara
@ 2025-02-19 21:16   ` John Meneghini
  0 siblings, 0 replies; 21+ messages in thread
From: John Meneghini @ 2025-02-19 21:16 UTC (permalink / raw)
  To: Kai Mäkisara, linux-scsi, dgilbert; +Cc: martin.petersen, James.Bottomley

Reviewed-by: John Meneghini <jmeneghi@redhat.com>
Tested-by: John Meneghini <jmeneghi@redhat.com>

After this patch: READ BLOCK LIMITs works.
                   SEEK works.

[root@to-be-determined ~]# mt -f /dev/nst1 rewind
[Wed Feb 19 16:08:45 2025] st 8:0:0:0: [st1] check_tape: 1087: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:08:45 2025] st 8:0:0:0: [st1] Block limits 512 - 1048576 bytes.
[Wed Feb 19 16:08:45 2025] st 8:0:0:0: [st1] Mode sense. Length 11, medium 0, WBS 0, BLL 8
[Wed Feb 19 16:08:45 2025] st 8:0:0:0: [st1] Density 0, tape length: 388000, drv buffer: 0
[Wed Feb 19 16:08:45 2025] st 8:0:0:0: [st1] Block size: 0, buffer size: 4096 (1 blocks).
[Wed Feb 19 16:08:45 2025] st 8:0:0:0: [st1] check_tape: 1287: CHKRES_READY pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:08:45 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:08:45 2025] st 8:0:0:0: [st1] Rewinding tape.
[Wed Feb 19 16:08:45 2025] st 8:0:0:0: [st1] st_flush: 1409: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:08:45 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0
[root@to-be-determined ~]# dd if=/dev/random count=1024 of=/dev/nst1
[Wed Feb 19 16:08:49 2025] st 8:0:0:0: [st1] check_tape: 1087: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:08:49 2025] st 8:0:0:0: [st1] Block limits 512 - 1048576 bytes.
[Wed Feb 19 16:08:49 2025] st 8:0:0:0: [st1] Mode sense. Length 11, medium 0, WBS 0, BLL 8
[Wed Feb 19 16:08:49 2025] st 8:0:0:0: [st1] Density 0, tape length: 388000, drv buffer: 0
[Wed Feb 19 16:08:49 2025] st 8:0:0:0: [st1] Block size: 0, buffer size: 4096 (1 blocks).
[Wed Feb 19 16:08:49 2025] st 8:0:0:0: [st1] check_tape: 1287: CHKRES_READY pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:08:50 2025] st 8:0:0:0: [st1] st_flush: 1409: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:08:50 2025] st 8:0:0:0: [st1] Number of r/w requests 1024, dio used in 1024, pages 1024.
[Wed Feb 19 16:08:50 2025] st 8:0:0:0: [st1] Async write waits 0, finished 0.
1024+0 records in
1024+0 records out
[Wed Feb 19 16:08:50 2025] st 8:0:0:0: [st1] Buffer flushed, 1 EOF(s) written
524288 bytes (524 kB, 512 KiB) copied, 1.12588 s, 466 kB/s
[root@to-be-determined ~]# mt -f /dev/nst1 rewind
[Wed Feb 19 16:09:00 2025] st 8:0:0:0: [st1] check_tape: 1087: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:09:00 2025] st 8:0:0:0: [st1] Block limits 512 - 1048576 bytes.
[Wed Feb 19 16:09:00 2025] st 8:0:0:0: [st1] Mode sense. Length 11, medium 0, WBS 0, BLL 8
[Wed Feb 19 16:09:00 2025] st 8:0:0:0: [st1] Density 0, tape length: 388000, drv buffer: 0
[Wed Feb 19 16:09:00 2025] st 8:0:0:0: [st1] Block size: 0, buffer size: 4096 (1 blocks).
[Wed Feb 19 16:09:00 2025] st 8:0:0:0: [st1] check_tape: 1287: CHKRES_READY pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:09:00 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:09:00 2025] st 8:0:0:0: [st1] Rewinding tape.
[Wed Feb 19 16:09:00 2025] st 8:0:0:0: [st1] st_flush: 1409: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:09:00 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0
[root@to-be-determined ~]# mt -f /dev/nst1 seek 1
[Wed Feb 19 16:09:04 2025] st 8:0:0:0: [st1] check_tape: 1087: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:09:04 2025] st 8:0:0:0: [st1] Block limits 512 - 1048576 bytes.
[Wed Feb 19 16:09:04 2025] st 8:0:0:0: [st1] Mode sense. Length 11, medium 0, WBS 0, BLL 8
[Wed Feb 19 16:09:04 2025] st 8:0:0:0: [st1] Density 0, tape length: 388000, drv buffer: 0
[Wed Feb 19 16:09:04 2025] st 8:0:0:0: [st1] Block size: 0, buffer size: 4096 (1 blocks).
[Wed Feb 19 16:09:04 2025] st 8:0:0:0: [st1] check_tape: 1287: CHKRES_READY pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:09:04 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:09:04 2025] st 8:0:0:0: [st1] Setting block to 1 and partition to 0.
[Wed Feb 19 16:09:04 2025] st 8:0:0:0: [st1] st_flush: 1409: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:09:04 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0

Before this patch... forget it.  Don't even try.


On 2/13/25 4:26 AM, Kai Mäkisara wrote:
> The changes:
> - add READ BLOCK LIMITS (512 - 1048576 bytes)
> - make LOAD send New Media UA (not correct by the standard, but
>    makes possible to test also this UA)
> 
> Signed-off-by: Kai Mäkisara <Kai.Makisara@kolumbus.fi>
> ---
> v1 -> v2:
> - changed 'len +=' to 'len =' in resp_mode_sense() to fix the bug reported
>    by the Kernel Test Robot
> 
>   drivers/scsi/scsi_debug.c | 127 ++++++++++++++++++++++++++++++++++++--
>   1 file changed, 121 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
> index 4da0c259390b..21c64f79797a 100644
> --- a/drivers/scsi/scsi_debug.c
> +++ b/drivers/scsi/scsi_debug.c
> @@ -80,6 +80,7 @@ static const char *sdebug_version_date = "20210520";
>   #define INVALID_FIELD_IN_CDB 0x24
>   #define INVALID_FIELD_IN_PARAM_LIST 0x26
>   #define WRITE_PROTECTED 0x27
> +#define UA_READY_ASC 0x28
>   #define UA_RESET_ASC 0x29
>   #define UA_CHANGED_ASC 0x2a
>   #define TARGET_CHANGED_ASC 0x3f
> @@ -175,7 +176,11 @@ static const char *sdebug_version_date = "20210520";
>   
>   /* Default parameters for tape drives */
>   #define TAPE_DEF_DENSITY  0x0
> +#define TAPE_BAD_DENSITY  0x65
>   #define TAPE_DEF_BLKSIZE  0
> +#define TAPE_MIN_BLKSIZE  512
> +#define TAPE_MAX_BLKSIZE  1048576
> +#define TAPE_MAX_PARTITIONS 2
>   
>   #define SDEBUG_LUN_0_VAL 0
>   
> @@ -220,7 +225,8 @@ static const char *sdebug_version_date = "20210520";
>   #define SDEBUG_UA_LUNS_CHANGED 5
>   #define SDEBUG_UA_MICROCODE_CHANGED 6	/* simulate firmware change */
>   #define SDEBUG_UA_MICROCODE_CHANGED_WO_RESET 7
> -#define SDEBUG_NUM_UAS 8
> +#define SDEBUG_UA_NOT_READY_TO_READY 8
> +#define SDEBUG_NUM_UAS 9
>   
>   /* when 1==SDEBUG_OPT_MEDIUM_ERR, a medium error is simulated at this
>    * sector on read commands: */
> @@ -370,6 +376,8 @@ struct sdebug_dev_info {
>   	/* For tapes */
>   	unsigned int tape_blksize;
>   	unsigned int tape_density;
> +	unsigned char tape_partition;
> +	unsigned int tape_location[TAPE_MAX_PARTITIONS];
>   
>   	struct dentry *debugfs_entry;
>   	struct spinlock list_lock;
> @@ -491,14 +499,16 @@ enum sdeb_opcode_index {
>   	SDEB_I_ZONE_OUT = 30,		/* 0x94+SA; includes no data xfer */
>   	SDEB_I_ZONE_IN = 31,		/* 0x95+SA; all have data-in */
>   	SDEB_I_ATOMIC_WRITE_16 = 32,
> -	SDEB_I_LAST_ELEM_P1 = 33,	/* keep this last (previous + 1) */
> +	SDEB_I_READ_BLOCK_LIMITS = 33,
> +	SDEB_I_LOCATE = 34,
> +	SDEB_I_LAST_ELEM_P1 = 35,	/* keep this last (previous + 1) */
>   };
>   
>   
>   static const unsigned char opcode_ind_arr[256] = {
>   /* 0x0; 0x0->0x1f: 6 byte cdbs */
>   	SDEB_I_TEST_UNIT_READY, SDEB_I_REZERO_UNIT, 0, SDEB_I_REQUEST_SENSE,
> -	    0, 0, 0, 0,
> +	    0, SDEB_I_READ_BLOCK_LIMITS, 0, 0,
>   	SDEB_I_READ, 0, SDEB_I_WRITE, 0, 0, 0, 0, 0,
>   	0, 0, SDEB_I_INQUIRY, 0, 0, SDEB_I_MODE_SELECT, SDEB_I_RESERVE,
>   	    SDEB_I_RELEASE,
> @@ -506,7 +516,7 @@ static const unsigned char opcode_ind_arr[256] = {
>   	    SDEB_I_ALLOW_REMOVAL, 0,
>   /* 0x20; 0x20->0x3f: 10 byte cdbs */
>   	0, 0, 0, 0, 0, SDEB_I_READ_CAPACITY, 0, 0,
> -	SDEB_I_READ, 0, SDEB_I_WRITE, 0, 0, 0, 0, SDEB_I_VERIFY,
> +	SDEB_I_READ, 0, SDEB_I_WRITE, SDEB_I_LOCATE, 0, 0, 0, SDEB_I_VERIFY,
>   	0, 0, 0, 0, SDEB_I_PRE_FETCH, SDEB_I_SYNC_CACHE, 0, 0,
>   	0, 0, 0, SDEB_I_WRITE_BUFFER, 0, 0, 0, 0,
>   /* 0x40; 0x40->0x5f: 10 byte cdbs */
> @@ -581,6 +591,8 @@ static int resp_open_zone(struct scsi_cmnd *, struct sdebug_dev_info *);
>   static int resp_close_zone(struct scsi_cmnd *, struct sdebug_dev_info *);
>   static int resp_finish_zone(struct scsi_cmnd *, struct sdebug_dev_info *);
>   static int resp_rwp_zone(struct scsi_cmnd *, struct sdebug_dev_info *);
> +static int resp_read_blklimits(struct scsi_cmnd *, struct sdebug_dev_info *);
> +static int resp_locate(struct scsi_cmnd *, struct sdebug_dev_info *);
>   
>   static int sdebug_do_add_host(bool mk_new_store);
>   static int sdebug_add_host_helper(int per_host_idx);
> @@ -808,6 +820,7 @@ static const struct opcode_info_t opcode_info_arr[SDEB_I_LAST_ELEM_P1 + 1] = {
>   	    resp_pre_fetch, pre_fetch_iarr,
>   	    {10,  0x2, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xc7, 0, 0,
>   	     0, 0, 0, 0} },			/* PRE-FETCH (10) */
> +						/* READ POSITION (10) */
>   
>   /* 30 */
>   	{ARRAY_SIZE(zone_out_iarr), 0x94, 0x3, F_SA_LOW | F_M_ACCESS,
> @@ -823,6 +836,12 @@ static const struct opcode_info_t opcode_info_arr[SDEB_I_LAST_ELEM_P1 + 1] = {
>   	    resp_atomic_write, NULL, /* ATOMIC WRITE 16 */
>   		{16,  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
>   		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} },
> +	{0, 0x05, 0, F_D_IN, resp_read_blklimits, NULL,    /* READ BLOCK LIMITS (6) */
> +	    {6,  0, 0, 0, 0, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
> +	{0, 0x2b, 0, F_D_UNKN, resp_locate, NULL,    /* LOCATE (10) */
> +	    {10,  0x2, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xc7, 0, 0,
> +	     0, 0, 0, 0} },
> +
>   /* sentinel */
>   	{0xff, 0, 0, 0, NULL, NULL,		/* terminating element */
>   	    {0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
> @@ -1501,6 +1520,12 @@ static int make_ua(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
>   			if (sdebug_verbose)
>   				cp = "reported luns data has changed";
>   			break;
> +		case SDEBUG_UA_NOT_READY_TO_READY:
> +			mk_sense_buffer(scp, UNIT_ATTENTION, UA_READY_ASC,
> +					0);
> +			if (sdebug_verbose)
> +				cp = "not ready to ready transition/media change";
> +			break;
>   		default:
>   			pr_warn("unexpected unit attention code=%d\n", k);
>   			if (sdebug_verbose)
> @@ -2204,6 +2229,14 @@ static int resp_start_stop(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
>   	changing = (stopped_state != want_stop);
>   	if (changing)
>   		atomic_xchg(&devip->stopped, want_stop);
> +	if (sdebug_ptype == TYPE_TAPE && !want_stop) {
> +		int i;
> +
> +		set_bit(SDEBUG_UA_NOT_READY_TO_READY, devip->uas_bm); /* not legal! */
> +		for (i = 0; i < TAPE_MAX_PARTITIONS; i++)
> +			devip->tape_location[i] = 0;
> +		devip->tape_partition = 0;
> +	}
>   	if (!changing || (cmd[1] & 0x1))  /* state unchanged or IMMED bit set in cdb */
>   		return SDEG_RES_IMMED_MASK;
>   	else
> @@ -2736,6 +2769,17 @@ static int resp_sas_sha_m_spg(unsigned char *p, int pcontrol)
>   	return sizeof(sas_sha_m_pg);
>   }
>   
> +static unsigned char partition_pg[] = {0x11, 12, 1, 0, 0x24, 3, 9, 0,
> +	0xff, 0xff, 0x00, 0x00};
> +
> +static int resp_partition_m_pg(unsigned char *p, int pcontrol, int target)
> +{	/* Partition page for mode_sense (tape) */
> +	memcpy(p, partition_pg, sizeof(partition_pg));
> +	if (pcontrol == 1)
> +		memset(p + 2, 0, sizeof(partition_pg) - 2);
> +	return sizeof(partition_pg);
> +}
> +
>   /* PAGE_SIZE is more than necessary but provides room for future expansion. */
>   #define SDEBUG_MAX_MSENSE_SZ PAGE_SIZE
>   
> @@ -2876,6 +2920,12 @@ static int resp_mode_sense(struct scsi_cmnd *scp,
>   		}
>   		offset += len;
>   		break;
> +	case 0x11:	/* Partition Mode Page (tape) */
> +		if (!is_tape)
> +			goto bad_pcode;
> +		len = resp_partition_m_pg(ap, pcontrol, target);
> +		offset += len;
> +		break;
>   	case 0x19:	/* if spc==1 then sas phy, control+discover */
>   		if (subpcode > 0x2 && subpcode < 0xff)
>   			goto bad_subpcode;
> @@ -2974,9 +3024,16 @@ static int resp_mode_select(struct scsi_cmnd *scp,
>   					mselect6 ? 3 : 6, -1);
>   			return check_condition_result;
>   		}
> +		if (arr[off] == TAPE_BAD_DENSITY) {
> +			mk_sense_invalid_fld(scp, SDEB_IN_DATA, 0, -1);
> +			return check_condition_result;
> +		}
>   		blksize = get_unaligned_be16(arr + off + 6);
> -		if ((blksize % 4) != 0) {
> -			mk_sense_invalid_fld(scp, SDEB_IN_DATA, off + 6, -1);
> +		if (blksize != 0 &&
> +			(blksize < TAPE_MIN_BLKSIZE ||
> +				blksize > TAPE_MAX_BLKSIZE ||
> +				(blksize % 4) != 0)) {
> +			mk_sense_invalid_fld(scp, SDEB_IN_DATA, 1, -1);
>   			return check_condition_result;
>   		}
>   		devip->tape_density = arr[off];
> @@ -3177,6 +3234,36 @@ static int resp_log_sense(struct scsi_cmnd *scp,
>   		    min_t(u32, len, SDEBUG_MAX_INQ_ARR_SZ));
>   }
>   
> +enum {SDEBUG_READ_BLOCK_LIMITS_ARR_SZ = 6};
> +static int resp_read_blklimits(struct scsi_cmnd *scp,
> +			struct sdebug_dev_info *devip)
> +{
> +	unsigned char arr[SDEBUG_READ_BLOCK_LIMITS_ARR_SZ];
> +
> +	arr[0] = 4;
> +	put_unaligned_be24(TAPE_MAX_BLKSIZE, arr + 1);
> +	put_unaligned_be16(TAPE_MIN_BLKSIZE, arr + 4);
> +	return fill_from_dev_buffer(scp, arr, SDEBUG_READ_BLOCK_LIMITS_ARR_SZ);
> +}
> +
> +static int resp_locate(struct scsi_cmnd *scp,
> +		struct sdebug_dev_info *devip)
> +{
> +	unsigned char *cmd = scp->cmnd;
> +
> +	if ((cmd[1] & 0x02) != 0) {
> +		if (cmd[8] >= TAPE_MAX_PARTITIONS) {
> +			mk_sense_invalid_fld(scp, SDEB_IN_CDB, 8, -1);
> +			return check_condition_result;
> +		}
> +		devip->tape_partition = cmd[8];
> +	}
> +	devip->tape_location[devip->tape_partition] =
> +		get_unaligned_be32(cmd + 3);
> +
> +	return 0;
> +}
> +
>   static inline bool sdebug_dev_is_zoned(struct sdebug_dev_info *devip)
>   {
>   	return devip->nr_zones != 0;
> @@ -4957,7 +5044,10 @@ static int resp_sync_cache(struct scsi_cmnd *scp,
>    * a GOOD status otherwise. Model a disk with a big cache and yield
>    * CONDITION MET. Actually tries to bring range in main memory into the
>    * cache associated with the CPU(s).
> + *
> + * The pcode 0x34 is also used for READ POSITION by tape devices.
>    */
> +enum {SDEBUG_READ_POSITION_ARR_SZ = 20};
>   static int resp_pre_fetch(struct scsi_cmnd *scp,
>   			  struct sdebug_dev_info *devip)
>   {
> @@ -4969,6 +5059,31 @@ static int resp_pre_fetch(struct scsi_cmnd *scp,
>   	struct sdeb_store_info *sip = devip2sip(devip, true);
>   	u8 *fsp = sip->storep;
>   
> +	if (sdebug_ptype == TYPE_TAPE) {
> +		if (cmd[0] == PRE_FETCH) { /* READ POSITION (10) */
> +			int all_length;
> +			unsigned char arr[20];
> +			unsigned int pos;
> +
> +			all_length = get_unaligned_be16(cmd + 7);
> +			if ((cmd[1] & 0xfe) != 0 ||
> +				all_length != 0) { /* only short form */
> +				mk_sense_invalid_fld(scp, SDEB_IN_CDB,
> +						all_length ? 7 : 1, 0);
> +				return check_condition_result;
> +			}
> +			memset(arr, 0, SDEBUG_READ_POSITION_ARR_SZ);
> +			arr[1] = devip->tape_partition;
> +			pos = devip->tape_location[devip->tape_partition];
> +			put_unaligned_be32(pos, arr + 4);
> +			put_unaligned_be32(pos, arr + 8);
> +			return fill_from_dev_buffer(scp, arr,
> +						SDEBUG_READ_POSITION_ARR_SZ);
> +		}
> +		mk_sense_invalid_opcode(scp);
> +		return check_condition_result;
> +	}
> +
>   	if (cmd[0] == PRE_FETCH) {	/* 10 byte cdb */
>   		lba = get_unaligned_be32(cmd + 2);
>   		nblks = get_unaligned_be16(cmd + 7);


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 3/7] scsi: scsi_debug: Add write support with block lengths and 4 bytes of data
  2025-02-13  9:26 ` [PATCH v2 3/7] scsi: scsi_debug: Add write support with block lengths and 4 bytes of data Kai Mäkisara
@ 2025-02-19 21:45   ` John Meneghini
  0 siblings, 0 replies; 21+ messages in thread
From: John Meneghini @ 2025-02-19 21:45 UTC (permalink / raw)
  To: Kai Mäkisara, linux-scsi, dgilbert; +Cc: martin.petersen, James.Bottomley

Writing File Marks with scsi_debug.

Before this patch, forget it don't even try.

After this patch... looks like it is working perfectly to me.  Works like a real tape drive.

Reviewed-by: John Meneghini <jmeneghi@redhat.com>
Tested-by: John Meneghini <jmeneghi@redhat.com>

[root@to-be-determined ~]# mt -f /dev/nst1 rewind
[Wed Feb 19 16:33:24 2025] st 8:0:0:0: [st1] check_tape: 1087: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:33:24 2025] st 8:0:0:0: [st1] Block limits 512 - 1048576 bytes.
[Wed Feb 19 16:33:24 2025] st 8:0:0:0: [st1] Mode sense. Length 11, medium 0, WBS 0, BLL 8
[Wed Feb 19 16:33:24 2025] st 8:0:0:0: [st1] Density 0, tape length: 388000, drv buffer: 0
[Wed Feb 19 16:33:24 2025] st 8:0:0:0: [st1] Block size: 0, buffer size: 4096 (1 blocks).
[Wed Feb 19 16:33:24 2025] st 8:0:0:0: [st1] check_tape: 1287: CHKRES_READY pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:33:24 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:33:24 2025] st 8:0:0:0: [st1] Rewinding tape.
[Wed Feb 19 16:33:24 2025] st 8:0:0:0: [st1] st_flush: 1409: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:33:24 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0
[root@to-be-determined ~]# dd if=/dev/random count=1024 of=/dev/nst1
[Wed Feb 19 16:33:34 2025] st 8:0:0:0: [st1] check_tape: 1087: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:33:34 2025] st 8:0:0:0: [st1] Block limits 512 - 1048576 bytes.
[Wed Feb 19 16:33:34 2025] st 8:0:0:0: [st1] Mode sense. Length 11, medium 0, WBS 0, BLL 8
[Wed Feb 19 16:33:34 2025] st 8:0:0:0: [st1] Density 0, tape length: 388000, drv buffer: 0
[Wed Feb 19 16:33:34 2025] st 8:0:0:0: [st1] Block size: 0, buffer size: 4096 (1 blocks).
[Wed Feb 19 16:33:34 2025] st 8:0:0:0: [st1] check_tape: 1287: CHKRES_READY pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:33:35 2025] st 8:0:0:0: [st1] st_flush: 1409: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:33:35 2025] st 8:0:0:0: [st1] Number of r/w requests 1024, dio used in 1024, pages 1024.
[Wed Feb 19 16:33:35 2025] st 8:0:0:0: [st1] Async write waits 0, finished 0.
1024+0 records in
1024+0 records out
524288 bytes (524 kB, 512 KiB) copied, 1.13296 s, 463 kB/s
[Wed Feb 19 16:33:35 2025] st 8:0:0:0: [st1] Buffer flushed, 1 EOF(s) written
[root@to-be-determined ~]# mt -f /dev/nst1 eof 2
[Wed Feb 19 16:33:40 2025] st 8:0:0:0: [st1] check_tape: 1087: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:33:40 2025] st 8:0:0:0: [st1] Block limits 512 - 1048576 bytes.
[Wed Feb 19 16:33:40 2025] st 8:0:0:0: [st1] Mode sense. Length 11, medium 0, WBS 0, BLL 8
[Wed Feb 19 16:33:40 2025] st 8:0:0:0: [st1] Density 0, tape length: 388000, drv buffer: 0
[Wed Feb 19 16:33:40 2025] st 8:0:0:0: [st1] Block size: 0, buffer size: 4096 (1 blocks).
[Wed Feb 19 16:33:40 2025] st 8:0:0:0: [st1] check_tape: 1287: CHKRES_READY pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:33:40 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:33:40 2025] st 8:0:0:0: [st1] Writing 2 filemarks.
[root@to-be-determined ~]# [Wed Feb 19 16:33:40 2025] st 8:0:0:0: [st1] st_flush: 1409: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:33:40 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0

[root@to-be-determined ~]# mt -f /dev/nst1 bsf 2
[Wed Feb 19 16:34:34 2025] st 8:0:0:0: [st1] check_tape: 1087: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:34:34 2025] st 8:0:0:0: [st1] Block limits 512 - 1048576 bytes.
[Wed Feb 19 16:34:34 2025] st 8:0:0:0: [st1] Mode sense. Length 11, medium 0, WBS 0, BLL 8
[Wed Feb 19 16:34:34 2025] st 8:0:0:0: [st1] Density 0, tape length: 388000, drv buffer: 0
[Wed Feb 19 16:34:34 2025] st 8:0:0:0: [st1] Block size: 0, buffer size: 4096 (1 blocks).
[Wed Feb 19 16:34:34 2025] st 8:0:0:0: [st1] check_tape: 1287: CHKRES_READY pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:34:34 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:34:34 2025] st 8:0:0:0: [st1] Spacing tape backward over 2 filemarks.
[root@to-be-determined ~]# [Wed Feb 19 16:34:34 2025] st 8:0:0:0: [st1] st_flush: 1409: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:34:34 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0

## This should fail.

[root@to-be-determined ~]# mt -f /dev/nst1 bsf 1
[Wed Feb 19 16:34:54 2025] st 8:0:0:0: [st1] check_tape: 1087: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:34:54 2025] st 8:0:0:0: [st1] Block limits 512 - 1048576 bytes.
[Wed Feb 19 16:34:54 2025] st 8:0:0:0: [st1] Mode sense. Length 11, medium 0, WBS 0, BLL 8
[Wed Feb 19 16:34:54 2025] st 8:0:0:0: [st1] Density 0, tape length: 388000, drv buffer: 0
[Wed Feb 19 16:34:54 2025] st 8:0:0:0: [st1] Block size: 0, buffer size: 4096 (1 blocks).
[Wed Feb 19 16:34:54 2025] st 8:0:0:0: [st1] check_tape: 1287: CHKRES_READY pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:34:54 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:34:54 2025] st 8:0:0:0: [st1] Spacing tape backward over 1 filemarks.
[Wed Feb 19 16:34:54 2025] st 8:0:0:0: [st1] Error: 2, cmd: 11 1 ff ff ff 0
[Wed Feb 19 16:34:54 2025] st 8:0:0:0: [st1] Sense Key : No Sense [current]
[Wed Feb 19 16:34:54 2025] st 8:0:0:0: [st1] Add. Sense: Beginning-of-partition/medium detected
[Wed Feb 19 16:34:54 2025] st 8:0:0:0: [st1] st_chk_result: 432: pos_unknown 0 was_reset 0/0 ready 0, result 2
/dev/nst1: Input/output error
[Wed Feb 19 16:34:54 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:34:54 2025] st 8:0:0:0: [st1] st_flush: 1409: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:34:54 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0

[root@to-be-determined ~]# mt -f /dev/nst1 rewind
[Wed Feb 19 16:35:39 2025] st 8:0:0:0: [st1] check_tape: 1087: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:35:39 2025] st 8:0:0:0: [st1] Block limits 512 - 1048576 bytes.
[Wed Feb 19 16:35:39 2025] st 8:0:0:0: [st1] Mode sense. Length 11, medium 0, WBS 0, BLL 8
[Wed Feb 19 16:35:39 2025] st 8:0:0:0: [st1] Density 0, tape length: 388000, drv buffer: 0
[Wed Feb 19 16:35:39 2025] st 8:0:0:0: [st1] Block size: 0, buffer size: 4096 (1 blocks).
[Wed Feb 19 16:35:39 2025] st 8:0:0:0: [st1] check_tape: 1287: CHKRES_READY pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:35:39 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:35:39 2025] st 8:0:0:0: [st1] Rewinding tape.
[Wed Feb 19 16:35:39 2025] st 8:0:0:0: [st1] st_flush: 1409: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:35:39 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0

[root@to-be-determined ~]#  mt -f /dev/nst1 status
[Wed Feb 19 16:35:45 2025] st 8:0:0:0: [st1] check_tape: 1087: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:35:45 2025] st 8:0:0:0: [st1] Block limits 512 - 1048576 bytes.
[Wed Feb 19 16:35:45 2025] st 8:0:0:0: [st1] Mode sense. Length 11, medium 0, WBS 0, BLL 8
[Wed Feb 19 16:35:45 2025] st 8:0:0:0: [st1] Density 0, tape length: 388000, drv buffer: 0
[Wed Feb 19 16:35:45 2025] st 8:0:0:0: [st1] Block size: 0, buffer size: 4096 (1 blocks).
[Wed Feb 19 16:35:45 2025] st 8:0:0:0: [st1] check_tape: 1287: CHKRES_READY pos_unknown 0 was_reset 0/0 ready 0
SCSI 2 tape drive:
[Wed Feb 19 16:35:45 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0
File number=0, block number=0, partition=0.
Tape block size 0 bytes. Density code 0x0 (default).
Soft error count since last status=0
General status bits on (41010000):
  BOT ONLINE IM_REP_EN
[Wed Feb 19 16:35:45 2025] st 8:0:0:0: [st1] st_flush: 1409: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:35:45 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0
[root@to-be-determined ~]# mt -f /dev/nst1 fsf 2
[Wed Feb 19 16:35:57 2025] st 8:0:0:0: [st1] check_tape: 1087: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:35:57 2025] st 8:0:0:0: [st1] Block limits 512 - 1048576 bytes.
[Wed Feb 19 16:35:57 2025] st 8:0:0:0: [st1] Mode sense. Length 11, medium 0, WBS 0, BLL 8
[Wed Feb 19 16:35:57 2025] st 8:0:0:0: [st1] Density 0, tape length: 388000, drv buffer: 0
[Wed Feb 19 16:35:57 2025] st 8:0:0:0: [st1] Block size: 0, buffer size: 4096 (1 blocks).
[Wed Feb 19 16:35:57 2025] st 8:0:0:0: [st1] check_tape: 1287: CHKRES_READY pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:35:57 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:35:57 2025] st 8:0:0:0: [st1] Spacing tape forward over 2 filemarks.
[Wed Feb 19 16:35:57 2025] st 8:0:0:0: [st1] st_flush: 1409: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:35:57 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0

#This should fail.
[root@to-be-determined ~]# mt -f /dev/nst1 fsf 1
[Wed Feb 19 16:36:14 2025] st 8:0:0:0: [st1] check_tape: 1087: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:36:14 2025] st 8:0:0:0: [st1] Block limits 512 - 1048576 bytes.
[Wed Feb 19 16:36:14 2025] st 8:0:0:0: [st1] Mode sense. Length 11, medium 0, WBS 0, BLL 8
[Wed Feb 19 16:36:14 2025] st 8:0:0:0: [st1] Density 0, tape length: 388000, drv buffer: 0
[Wed Feb 19 16:36:14 2025] st 8:0:0:0: [st1] Block size: 0, buffer size: 4096 (1 blocks).
[Wed Feb 19 16:36:14 2025] st 8:0:0:0: [st1] check_tape: 1287: CHKRES_READY pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:36:14 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:36:14 2025] st 8:0:0:0: [st1] Spacing tape forward over 1 filemarks.
[Wed Feb 19 16:36:14 2025] st 8:0:0:0: [st1] st_flush: 1409: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:36:14 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0

[root@to-be-determined ~]# mt -f /dev/nst1 status
[Wed Feb 19 16:36:25 2025] st 8:0:0:0: [st1] check_tape: 1087: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:36:25 2025] st 8:0:0:0: [st1] Block limits 512 - 1048576 bytes.
SCSI 2 tape drive:
File number=3, block number=0, partition=0.
Tape block size 0 bytes. Density code 0x0 (default).
Soft error count since last status=0
General status bits on (81010000):
  EOF ONLINE IM_REP_EN
[root@to-be-determined ~]# [Wed Feb 19 16:36:25 2025] st 8:0:0:0: [st1] Mode sense. Length 11, medium 0, WBS 0, BLL 8
[Wed Feb 19 16:36:25 2025] st 8:0:0:0: [st1] Density 0, tape length: 388000, drv buffer: 0
[Wed Feb 19 16:36:25 2025] st 8:0:0:0: [st1] Block size: 0, buffer size: 4096 (1 blocks).
[Wed Feb 19 16:36:25 2025] st 8:0:0:0: [st1] check_tape: 1287: CHKRES_READY pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:36:25 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:36:25 2025] st 8:0:0:0: [st1] st_flush: 1409: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:36:25 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0

John A. Meneghini
Senior Principal Platform Storage Engineer
RHEL SST - Platform Storage Group
jmeneghi@redhat.com


On 2/13/25 4:26 AM, Kai Mäkisara wrote:
> The tape is implemented as fixed number (10 000) of 8-byte units.
> The first four bytes of a unit contains the type of the unit (data
> block, filemark or end-of-data mark). If the units is a data block,
> the first four bytes contain the block length and the remaining
> four bytes the first bytes of written data. This allows the user
> to use tags to see that the read block is what it was supposed to be.
> 
> The tape can contain two partitions. Initially it is formatted as one
> partition consisting of all 10 000 units.
> 
> This patch adds the WRITE(6) command for tapes and the WRITE FILEMARKS (6)
> command. The REWIND command is updated.
> 
> Signed-off-by: Kai Mäkisara <Kai.Makisara@kolumbus.fi>
> ---
> v1 -> v2:
> - changed 'for (;' to 'for (i = 0;' in partition_tape() to fix the bug
>    reported by the Kernel Test Robot
> 
>   drivers/scsi/scsi_debug.c | 217 +++++++++++++++++++++++++++++++++++++-
>   1 file changed, 212 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
> index 21c64f79797a..69cae4c1712a 100644
> --- a/drivers/scsi/scsi_debug.c
> +++ b/drivers/scsi/scsi_debug.c
> @@ -71,6 +71,10 @@ static const char *sdebug_version_date = "20210520";
>   #define NO_ADDITIONAL_SENSE 0x0
>   #define OVERLAP_ATOMIC_COMMAND_ASC 0x0
>   #define OVERLAP_ATOMIC_COMMAND_ASCQ 0x23
> +#define FILEMARK_DETECTED_ASCQ 0x1
> +#define EOP_EOM_DETECTED_ASCQ 0x2
> +#define BEGINNING_OF_P_M_DETECTED_ASCQ 0x4
> +#define EOD_DETECTED_ASCQ 0x5
>   #define LOGICAL_UNIT_NOT_READY 0x4
>   #define LOGICAL_UNIT_COMMUNICATION_FAILURE 0x8
>   #define UNRECOVERED_READ_ERR 0x11
> @@ -83,6 +87,7 @@ static const char *sdebug_version_date = "20210520";
>   #define UA_READY_ASC 0x28
>   #define UA_RESET_ASC 0x29
>   #define UA_CHANGED_ASC 0x2a
> +#define TOO_MANY_IN_PARTITION_ASC 0x3b
>   #define TARGET_CHANGED_ASC 0x3f
>   #define LUNS_CHANGED_ASCQ 0x0e
>   #define INSUFF_RES_ASC 0x55
> @@ -180,7 +185,29 @@ static const char *sdebug_version_date = "20210520";
>   #define TAPE_DEF_BLKSIZE  0
>   #define TAPE_MIN_BLKSIZE  512
>   #define TAPE_MAX_BLKSIZE  1048576
> +#define TAPE_EW 20
>   #define TAPE_MAX_PARTITIONS 2
> +#define TAPE_UNITS 10000
> +
> +/* The tape block data definitions */
> +#define TAPE_BLOCK_FM_FLAG   ((u32)0x1 << 30)
> +#define TAPE_BLOCK_EOD_FLAG  ((u32)0x2 << 30)
> +#define TAPE_BLOCK_MARK_MASK ((u32)0x3 << 30)
> +#define TAPE_BLOCK_SIZE_MASK (~TAPE_BLOCK_MARK_MASK)
> +#define TAPE_BLOCK_MARK(a) (a & TAPE_BLOCK_MARK_MASK)
> +#define TAPE_BLOCK_SIZE(a) (a & TAPE_BLOCK_SIZE_MASK)
> +#define IS_TAPE_BLOCK_FM(a)   ((a & TAPE_BLOCK_FM_FLAG) != 0)
> +#define IS_TAPE_BLOCK_EOD(a)  ((a & TAPE_BLOCK_EOD_FLAG) != 0)
> +
> +struct tape_block {
> +	u32 fl_size;
> +	unsigned char data[4];
> +};
> +
> +/* Flags for sense data */
> +#define SENSE_FLAG_FILEMARK  0x80
> +#define SENSE_FLAG_EOM 0x40
> +#define SENSE_FLAG_ILI 0x20
>   
>   #define SDEBUG_LUN_0_VAL 0
>   
> @@ -377,7 +404,10 @@ struct sdebug_dev_info {
>   	unsigned int tape_blksize;
>   	unsigned int tape_density;
>   	unsigned char tape_partition;
> +	unsigned char tape_nbr_partitions;
>   	unsigned int tape_location[TAPE_MAX_PARTITIONS];
> +	unsigned int tape_eop[TAPE_MAX_PARTITIONS];
> +	struct tape_block *tape_blocks[TAPE_MAX_PARTITIONS];
>   
>   	struct dentry *debugfs_entry;
>   	struct spinlock list_lock;
> @@ -501,7 +531,8 @@ enum sdeb_opcode_index {
>   	SDEB_I_ATOMIC_WRITE_16 = 32,
>   	SDEB_I_READ_BLOCK_LIMITS = 33,
>   	SDEB_I_LOCATE = 34,
> -	SDEB_I_LAST_ELEM_P1 = 35,	/* keep this last (previous + 1) */
> +	SDEB_I_WRITE_FILEMARKS = 35,
> +	SDEB_I_LAST_ELEM_P1 = 36,	/* keep this last (previous + 1) */
>   };
>   
>   
> @@ -510,8 +541,8 @@ static const unsigned char opcode_ind_arr[256] = {
>   	SDEB_I_TEST_UNIT_READY, SDEB_I_REZERO_UNIT, 0, SDEB_I_REQUEST_SENSE,
>   	    0, SDEB_I_READ_BLOCK_LIMITS, 0, 0,
>   	SDEB_I_READ, 0, SDEB_I_WRITE, 0, 0, 0, 0, 0,
> -	0, 0, SDEB_I_INQUIRY, 0, 0, SDEB_I_MODE_SELECT, SDEB_I_RESERVE,
> -	    SDEB_I_RELEASE,
> +	SDEB_I_WRITE_FILEMARKS, 0, SDEB_I_INQUIRY, 0, 0,
> +	    SDEB_I_MODE_SELECT, SDEB_I_RESERVE, SDEB_I_RELEASE,
>   	0, 0, SDEB_I_MODE_SENSE, SDEB_I_START_STOP, 0, SDEB_I_SEND_DIAG,
>   	    SDEB_I_ALLOW_REMOVAL, 0,
>   /* 0x20; 0x20->0x3f: 10 byte cdbs */
> @@ -593,6 +624,8 @@ static int resp_finish_zone(struct scsi_cmnd *, struct sdebug_dev_info *);
>   static int resp_rwp_zone(struct scsi_cmnd *, struct sdebug_dev_info *);
>   static int resp_read_blklimits(struct scsi_cmnd *, struct sdebug_dev_info *);
>   static int resp_locate(struct scsi_cmnd *, struct sdebug_dev_info *);
> +static int resp_write_filemarks(struct scsi_cmnd *, struct sdebug_dev_info *);
> +static int resp_rewind(struct scsi_cmnd *, struct sdebug_dev_info *);
>   
>   static int sdebug_do_add_host(bool mk_new_store);
>   static int sdebug_add_host_helper(int per_host_idx);
> @@ -793,7 +826,7 @@ static const struct opcode_info_t opcode_info_arr[SDEB_I_LAST_ELEM_P1 + 1] = {
>   /* 20 */
>   	{0, 0x1e, 0, 0, NULL, NULL, /* ALLOW REMOVAL */
>   	    {6,  0, 0, 0, 0x3, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
> -	{0, 0x1, 0, 0, NULL, NULL, /* REWIND ?? */
> +	{0, 0x1, 0, 0, resp_rewind, NULL,
>   	    {6,  0x1, 0, 0, 0, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
>   	{0, 0, 0, F_INV_OP | FF_RESPOND, NULL, NULL, /* ATA_PT */
>   	    {0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
> @@ -841,6 +874,8 @@ static const struct opcode_info_t opcode_info_arr[SDEB_I_LAST_ELEM_P1 + 1] = {
>   	{0, 0x2b, 0, F_D_UNKN, resp_locate, NULL,    /* LOCATE (10) */
>   	    {10,  0x2, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xc7, 0, 0,
>   	     0, 0, 0, 0} },
> +	{0, 0x10, 0, F_D_IN, resp_write_filemarks, NULL,    /* WRITE FILEMARKS (6) */
> +	    {6,  0x01, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
>   
>   /* sentinel */
>   	{0xff, 0, 0, 0, NULL, NULL,		/* terminating element */
> @@ -1358,6 +1393,30 @@ static void mk_sense_buffer(struct scsi_cmnd *scp, int key, int asc, int asq)
>   			    my_name, key, asc, asq);
>   }
>   
> +/* Sense data that has information fields for tapes */
> +static void mk_sense_info_tape(struct scsi_cmnd *scp, int key, int asc, int asq,
> +			unsigned int information, unsigned char tape_flags)
> +{
> +	if (!scp->sense_buffer) {
> +		sdev_printk(KERN_ERR, scp->device,
> +			    "%s: sense_buffer is NULL\n", __func__);
> +		return;
> +	}
> +	memset(scp->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
> +
> +	scsi_build_sense(scp, /*sdebug_dsense*/ 0, key, asc, asq);
> +	/* only fixed format so far */
> +
> +	scp->sense_buffer[0] |= 0x80; /* valid */
> +	scp->sense_buffer[2] |= tape_flags;
> +	put_unaligned_be32(information, &scp->sense_buffer[3]);
> +
> +	if (sdebug_verbose)
> +		sdev_printk(KERN_INFO, scp->device,
> +			    "%s:  [sense_key,asc,ascq]: [0x%x,0x%x,0x%x]\n",
> +			    my_name, key, asc, asq);
> +}
> +
>   static void mk_sense_invalid_opcode(struct scsi_cmnd *scp)
>   {
>   	mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_OPCODE, 0);
> @@ -3252,7 +3311,7 @@ static int resp_locate(struct scsi_cmnd *scp,
>   	unsigned char *cmd = scp->cmnd;
>   
>   	if ((cmd[1] & 0x02) != 0) {
> -		if (cmd[8] >= TAPE_MAX_PARTITIONS) {
> +		if (cmd[8] >= devip->tape_nbr_partitions) {
>   			mk_sense_invalid_fld(scp, SDEB_IN_CDB, 8, -1);
>   			return check_condition_result;
>   		}
> @@ -3264,6 +3323,71 @@ static int resp_locate(struct scsi_cmnd *scp,
>   	return 0;
>   }
>   
> +static int resp_write_filemarks(struct scsi_cmnd *scp,
> +		struct sdebug_dev_info *devip)
> +{
> +	unsigned char *cmd = scp->cmnd;
> +	unsigned int i, count, pos;
> +	u32 data;
> +	int partition = devip->tape_partition;
> +
> +	if ((cmd[1] & 0xfe) != 0) { /* probably write setmarks, not in >= SCSI-3 */
> +		mk_sense_invalid_fld(scp, SDEB_IN_CDB, 1, 1);
> +		return check_condition_result;
> +	}
> +	count = get_unaligned_be24(cmd + 2);
> +	data = TAPE_BLOCK_FM_FLAG;
> +	for (i = 0, pos = devip->tape_location[partition]; i < count; i++, pos++) {
> +		if (pos >= devip->tape_eop[partition] - 1) { /* don't overwrite EOD */
> +			devip->tape_location[partition] = devip->tape_eop[partition] - 1;
> +			mk_sense_info_tape(scp, VOLUME_OVERFLOW, NO_ADDITIONAL_SENSE,
> +					EOP_EOM_DETECTED_ASCQ, count, SENSE_FLAG_EOM);
> +			return check_condition_result;
> +		}
> +		(devip->tape_blocks[partition] + pos)->fl_size = data;
> +	}
> +	(devip->tape_blocks[partition] + pos)->fl_size =
> +		TAPE_BLOCK_EOD_FLAG;
> +	devip->tape_location[partition] = pos;
> +
> +	return 0;
> +}
> +
> +static int resp_rewind(struct scsi_cmnd *scp,
> +		struct sdebug_dev_info *devip)
> +{
> +	devip->tape_location[devip->tape_partition] = 0;
> +
> +	return 0;
> +}
> +
> +static int partition_tape(struct sdebug_dev_info *devip, int nbr_partitions,
> +			int part_0_size, int part_1_size)
> +{
> +	int i;
> +
> +	if (part_0_size + part_1_size > TAPE_UNITS)
> +		return -1;
> +	devip->tape_eop[0] = part_0_size;
> +	devip->tape_blocks[0]->fl_size = TAPE_BLOCK_EOD_FLAG;
> +	devip->tape_eop[1] = part_1_size;
> +	devip->tape_blocks[1] = devip->tape_blocks[0] +
> +			devip->tape_eop[0];
> +	devip->tape_blocks[1]->fl_size = TAPE_BLOCK_EOD_FLAG;
> +
> +	for (i = 0 ; i < TAPE_MAX_PARTITIONS; i++)
> +		devip->tape_location[i] = 0;
> +
> +	devip->tape_nbr_partitions = nbr_partitions;
> +	devip->tape_partition = 0;
> +
> +	partition_pg[3] = nbr_partitions - 1;
> +	put_unaligned_be16(devip->tape_eop[0], partition_pg + 8);
> +	put_unaligned_be16(devip->tape_eop[1], partition_pg + 10);
> +
> +	return nbr_partitions;
> +}
> +
>   static inline bool sdebug_dev_is_zoned(struct sdebug_dev_info *devip)
>   {
>   	return devip->nr_zones != 0;
> @@ -4304,6 +4428,67 @@ static void unmap_region(struct sdeb_store_info *sip, sector_t lba,
>   	}
>   }
>   
> +static int resp_write_tape(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
> +{
> +	u32 i, num, transfer, size, written = 0;
> +	u8 *cmd = scp->cmnd;
> +	struct scsi_data_buffer *sdb = &scp->sdb;
> +	int partition = devip->tape_partition;
> +	int pos = devip->tape_location[partition];
> +	struct tape_block *blp;
> +	bool fixed, ew;
> +
> +	if (cmd[0] != WRITE_6) { /* Only Write(6) supported */
> +		mk_sense_invalid_opcode(scp);
> +		return illegal_condition_result;
> +	}
> +
> +	fixed = (cmd[1] & 1) != 0;
> +	transfer = get_unaligned_be24(cmd + 2);
> +	if (fixed) {
> +		num = transfer;
> +		size = devip->tape_blksize;
> +	} else {
> +		if (transfer < TAPE_MIN_BLKSIZE ||
> +			transfer > TAPE_MAX_BLKSIZE) {
> +			mk_sense_invalid_fld(scp, SDEB_IN_CDB, 2, -1);
> +			return check_condition_result;
> +		}
> +		num = 1;
> +		size = transfer;
> +	}
> +
> +	scsi_set_resid(scp, num * transfer);
> +	for (i = 0, blp = devip->tape_blocks[partition] + pos, ew = false;
> +	     i < num && pos < devip->tape_eop[partition] - 1; i++, pos++, blp++) {
> +		blp->fl_size = size;
> +		sg_copy_buffer(sdb->table.sgl, sdb->table.nents,
> +			&(blp->data), 4, i * size, true);
> +		written += size;
> +		scsi_set_resid(scp, num * transfer - written);
> +		ew |= (pos == devip->tape_eop[partition] - TAPE_EW);
> +	}
> +
> +	devip->tape_location[partition] = pos;
> +	blp->fl_size = TAPE_BLOCK_EOD_FLAG;
> +	if (pos >= devip->tape_eop[partition] - 1) {
> +		mk_sense_info_tape(scp, VOLUME_OVERFLOW,
> +				NO_ADDITIONAL_SENSE, EOP_EOM_DETECTED_ASCQ,
> +				fixed ? num - i : transfer,
> +				SENSE_FLAG_EOM);
> +		return check_condition_result;
> +	}
> +	if (ew) { /* early warning */
> +		mk_sense_info_tape(scp, NO_SENSE,
> +				NO_ADDITIONAL_SENSE, EOP_EOM_DETECTED_ASCQ,
> +				fixed ? num - i : transfer,
> +				SENSE_FLAG_EOM);
> +		return check_condition_result;
> +	}
> +
> +	return 0;
> +}
> +
>   static int resp_write_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
>   {
>   	bool check_prot;
> @@ -4316,6 +4501,9 @@ static int resp_write_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
>   	u8 *cmd = scp->cmnd;
>   	bool meta_data_locked = false;
>   
> +	if (sdebug_ptype == TYPE_TAPE)
> +		return resp_write_tape(scp, devip);
> +
>   	switch (cmd[0]) {
>   	case WRITE_16:
>   		ei_lba = 0;
> @@ -6063,6 +6251,20 @@ static int scsi_debug_sdev_configure(struct scsi_device *sdp,
>   		if (devip == NULL)
>   			return 1;  /* no resources, will be marked offline */
>   	}
> +	if (sdebug_ptype == TYPE_TAPE) {
> +		if (!devip->tape_blocks[0]) {
> +			devip->tape_blocks[0] =
> +				kcalloc(TAPE_UNITS, sizeof(struct tape_block),
> +					GFP_KERNEL);
> +			if (!devip->tape_blocks[0])
> +				return 1;
> +		}
> +		if (partition_tape(devip, 1, TAPE_UNITS, 0) < 0) {
> +			kfree(devip->tape_blocks[0]);
> +			devip->tape_blocks[0] = NULL;
> +			return 1;
> +		}
> +	}
>   	sdp->hostdata = devip;
>   	if (sdebug_no_uld)
>   		sdp->no_uld_attach = 1;
> @@ -6108,6 +6310,11 @@ static void scsi_debug_sdev_destroy(struct scsi_device *sdp)
>   
>   	debugfs_remove(devip->debugfs_entry);
>   
> +	if (sdebug_ptype == TYPE_TAPE) {
> +		kfree(devip->tape_blocks[0]);
> +		devip->tape_blocks[0] = NULL;
> +	}
> +
>   	/* make this slot available for re-use */
>   	devip->used = false;
>   	sdp->hostdata = NULL;


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 4/7] scsi: scsi_debug: Add read support and update locate for tapes
  2025-02-13  9:26 ` [PATCH v2 4/7] scsi: scsi_debug: Add read support and update locate for tapes Kai Mäkisara
@ 2025-02-19 21:56   ` John Meneghini
  0 siblings, 0 replies; 21+ messages in thread
From: John Meneghini @ 2025-02-19 21:56 UTC (permalink / raw)
  To: Kai Mäkisara, linux-scsi, dgilbert; +Cc: martin.petersen, James.Bottomley

Reviewed-by: John Meneghini <jmeneghi@redhat.com>
Tested-by: John Meneghini <jmeneghi@redhat.com>

After this patch it looks like fsr and bsr are working well.

Before this patch... forget it. Don't even try if you are using the scsi_debug driver.

[root@to-be-determined ~]# mt -f /dev/nst1 rewind
[Wed Feb 19 16:46:03 2025] st 8:0:0:0: [st1] check_tape: 1087: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:46:03 2025] st 8:0:0:0: [st1] Block limits 512 - 1048576 bytes.
[Wed Feb 19 16:46:03 2025] st 8:0:0:0: [st1] Mode sense. Length 11, medium 0, WBS 0, BLL 8
[Wed Feb 19 16:46:03 2025] st 8:0:0:0: [st1] Density 0, tape length: 388000, drv buffer: 0
[Wed Feb 19 16:46:03 2025] st 8:0:0:0: [st1] Block size: 0, buffer size: 4096 (1 blocks).
[Wed Feb 19 16:46:03 2025] st 8:0:0:0: [st1] check_tape: 1287: CHKRES_READY pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:46:03 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:46:03 2025] st 8:0:0:0: [st1] Rewinding tape.
[Wed Feb 19 16:46:03 2025] st 8:0:0:0: [st1] st_flush: 1409: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:46:03 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0
[root@to-be-determined ~]# mt -f /dev/nst1 fsr 2
[Wed Feb 19 16:46:41 2025] st 8:0:0:0: [st1] check_tape: 1087: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:46:41 2025] st 8:0:0:0: [st1] Block limits 512 - 1048576 bytes.
[Wed Feb 19 16:46:41 2025] st 8:0:0:0: [st1] Mode sense. Length 11, medium 0, WBS 0, BLL 8
[Wed Feb 19 16:46:41 2025] st 8:0:0:0: [st1] Density 0, tape length: 388000, drv buffer: 0
[Wed Feb 19 16:46:41 2025] st 8:0:0:0: [st1] Block size: 0, buffer size: 4096 (1 blocks).
[Wed Feb 19 16:46:41 2025] st 8:0:0:0: [st1] check_tape: 1287: CHKRES_READY pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:46:41 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:46:41 2025] st 8:0:0:0: [st1] Spacing tape forward over 2 blocks.
[Wed Feb 19 16:46:41 2025] st 8:0:0:0: [st1] st_flush: 1409: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:46:41 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0
[root@to-be-determined ~]# mt -f /dev/nst1 status
[Wed Feb 19 16:46:54 2025] st 8:0:0:0: [st1] check_tape: 1087: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:46:54 2025] st 8:0:0:0: [st1] Block limits 512 - 1048576 bytes.
SCSI 2 tape drive:
File number=0, block number=2, partition=0.
Tape block size 0 bytes. Density code 0x0 (default).
Soft error count since last status=0
General status bits on (1010000):
  ONLINE IM_REP_EN
[Wed Feb 19 16:46:54 2025] st 8:0:0:0: [st1] Mode sense. Length 11, medium 0, WBS 0, BLL 8
[Wed Feb 19 16:46:54 2025] st 8:0:0:0: [st1] Density 0, tape length: 388000, drv buffer: 0
[Wed Feb 19 16:46:54 2025] st 8:0:0:0: [st1] Block size: 0, buffer size: 4096 (1 blocks).
[Wed Feb 19 16:46:54 2025] st 8:0:0:0: [st1] check_tape: 1287: CHKRES_READY pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:46:54 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:46:54 2025] st 8:0:0:0: [st1] st_flush: 1409: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:46:54 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0

[root@to-be-determined ~]# mt -f /dev/nst1 bsr 2
[Wed Feb 19 16:47:35 2025] st 8:0:0:0: [st1] check_tape: 1087: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:47:35 2025] st 8:0:0:0: [st1] Block limits 512 - 1048576 bytes.
[Wed Feb 19 16:47:35 2025] st 8:0:0:0: [st1] Mode sense. Length 11, medium 0, WBS 0, BLL 8
[Wed Feb 19 16:47:35 2025] st 8:0:0:0: [st1] Density 0, tape length: 388000, drv buffer: 0
[Wed Feb 19 16:47:35 2025] st 8:0:0:0: [st1] Block size: 0, buffer size: 4096 (1 blocks).
[Wed Feb 19 16:47:35 2025] st 8:0:0:0: [st1] check_tape: 1287: CHKRES_READY pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:47:35 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:47:35 2025] st 8:0:0:0: [st1] Spacing tape backward over 2 blocks.
[Wed Feb 19 16:47:35 2025] st 8:0:0:0: [st1] st_flush: 1409: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:47:35 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0

[root@to-be-determined ~]# mt -f /dev/nst1 status
[Wed Feb 19 16:47:40 2025] st 8:0:0:0: [st1] check_tape: 1087: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:47:40 2025] st 8:0:0:0: [st1] Block limits 512 - 1048576 bytes.
[Wed Feb 19 16:47:40 2025] st 8:0:0:0: [st1] Mode sense. Length 11, medium 0, WBS 0, BLL 8
[Wed Feb 19 16:47:40 2025] st 8:0:0:0: [st1] Density 0, tape length: 388000, drv buffer: 0
[Wed Feb 19 16:47:40 2025] st 8:0:0:0: [st1] Block size: 0, buffer size: 4096 (1 blocks).
[Wed Feb 19 16:47:40 2025] st 8:0:0:0: [st1] check_tape: 1287: CHKRES_READY pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:47:40 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0
SCSI 2 tape drive:
File number=0, block number=0, partition=0.
Tape block size 0 bytes. Density code 0x0 (default).
Soft error count since last status=0
General status bits on (41010000):
  BOT ONLINE IM_REP_EN
[Wed Feb 19 16:47:40 2025] st 8:0:0:0: [st1] st_flush: 1409: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 16:47:40 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0



On 2/13/25 4:26 AM, Kai Mäkisara wrote:
> Support for the READ (6) and SPACE (6) commands for tapes based on the
> previous write patch is added. The LOCATE (10) command is updated to use
> the written data.
> 
> Signed-off-by: Kai Mäkisara <Kai.Makisara@kolumbus.fi>
> ---
> v1 -> v2:
> - initialized i to zero in resp_space() to fix the bug reported by
>    the Kernel Test Robot
> 
> drivers/scsi/scsi_debug.c | 240 +++++++++++++++++++++++++++++++++++++-
>   1 file changed, 235 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
> index 69cae4c1712a..29a9aea30d22 100644
> --- a/drivers/scsi/scsi_debug.c
> +++ b/drivers/scsi/scsi_debug.c
> @@ -532,7 +532,8 @@ enum sdeb_opcode_index {
>   	SDEB_I_READ_BLOCK_LIMITS = 33,
>   	SDEB_I_LOCATE = 34,
>   	SDEB_I_WRITE_FILEMARKS = 35,
> -	SDEB_I_LAST_ELEM_P1 = 36,	/* keep this last (previous + 1) */
> +	SDEB_I_SPACE = 36,
> +	SDEB_I_LAST_ELEM_P1 = 37,	/* keep this last (previous + 1) */
>   };
>   
>   
> @@ -541,7 +542,7 @@ static const unsigned char opcode_ind_arr[256] = {
>   	SDEB_I_TEST_UNIT_READY, SDEB_I_REZERO_UNIT, 0, SDEB_I_REQUEST_SENSE,
>   	    0, SDEB_I_READ_BLOCK_LIMITS, 0, 0,
>   	SDEB_I_READ, 0, SDEB_I_WRITE, 0, 0, 0, 0, 0,
> -	SDEB_I_WRITE_FILEMARKS, 0, SDEB_I_INQUIRY, 0, 0,
> +	SDEB_I_WRITE_FILEMARKS, SDEB_I_SPACE, SDEB_I_INQUIRY, 0, 0,
>   	    SDEB_I_MODE_SELECT, SDEB_I_RESERVE, SDEB_I_RELEASE,
>   	0, 0, SDEB_I_MODE_SENSE, SDEB_I_START_STOP, 0, SDEB_I_SEND_DIAG,
>   	    SDEB_I_ALLOW_REMOVAL, 0,
> @@ -625,6 +626,7 @@ static int resp_rwp_zone(struct scsi_cmnd *, struct sdebug_dev_info *);
>   static int resp_read_blklimits(struct scsi_cmnd *, struct sdebug_dev_info *);
>   static int resp_locate(struct scsi_cmnd *, struct sdebug_dev_info *);
>   static int resp_write_filemarks(struct scsi_cmnd *, struct sdebug_dev_info *);
> +static int resp_space(struct scsi_cmnd *, struct sdebug_dev_info *);
>   static int resp_rewind(struct scsi_cmnd *, struct sdebug_dev_info *);
>   
>   static int sdebug_do_add_host(bool mk_new_store);
> @@ -872,10 +874,12 @@ static const struct opcode_info_t opcode_info_arr[SDEB_I_LAST_ELEM_P1 + 1] = {
>   	{0, 0x05, 0, F_D_IN, resp_read_blklimits, NULL,    /* READ BLOCK LIMITS (6) */
>   	    {6,  0, 0, 0, 0, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
>   	{0, 0x2b, 0, F_D_UNKN, resp_locate, NULL,    /* LOCATE (10) */
> -	    {10,  0x2, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xc7, 0, 0,
> +	    {10,  0x07, 0, 0xff, 0xff, 0xff, 0xff, 0, 0xff, 0xc7, 0, 0,
>   	     0, 0, 0, 0} },
>   	{0, 0x10, 0, F_D_IN, resp_write_filemarks, NULL,    /* WRITE FILEMARKS (6) */
>   	    {6,  0x01, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
> +	{0, 0x11, 0, F_D_IN, resp_space, NULL,    /* SPACE (6) */
> +	    {6,  0x07, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
>   
>   /* sentinel */
>   	{0xff, 0, 0, 0, NULL, NULL,		/* terminating element */
> @@ -3309,6 +3313,9 @@ static int resp_locate(struct scsi_cmnd *scp,
>   		struct sdebug_dev_info *devip)
>   {
>   	unsigned char *cmd = scp->cmnd;
> +	unsigned int i, pos;
> +	struct tape_block *blp;
> +	int partition;
>   
>   	if ((cmd[1] & 0x02) != 0) {
>   		if (cmd[8] >= devip->tape_nbr_partitions) {
> @@ -3317,8 +3324,19 @@ static int resp_locate(struct scsi_cmnd *scp,
>   		}
>   		devip->tape_partition = cmd[8];
>   	}
> -	devip->tape_location[devip->tape_partition] =
> -		get_unaligned_be32(cmd + 3);
> +	pos = get_unaligned_be32(cmd + 3);
> +	partition = devip->tape_partition;
> +
> +	for (i = 0, blp = devip->tape_blocks[partition];
> +	     i < pos && i < devip->tape_eop[partition]; i++, blp++)
> +		if (IS_TAPE_BLOCK_EOD(blp->fl_size))
> +			break;
> +	if (i < pos) {
> +		devip->tape_location[partition] = i;
> +		mk_sense_buffer(scp, BLANK_CHECK, 0x05, 0);
> +		return check_condition_result;
> +	}
> +	devip->tape_location[partition] = pos;
>   
>   	return 0;
>   }
> @@ -3353,6 +3371,123 @@ static int resp_write_filemarks(struct scsi_cmnd *scp,
>   	return 0;
>   }
>   
> +static int resp_space(struct scsi_cmnd *scp,
> +		struct sdebug_dev_info *devip)
> +{
> +	unsigned char *cmd = scp->cmnd, code;
> +	int i = 0, pos, count;
> +	struct tape_block *blp;
> +	int partition = devip->tape_partition;
> +
> +	count = get_unaligned_be24(cmd + 2);
> +	if ((count & 0x800000) != 0) /* extend negative to 32-bit count */
> +		count |= 0xff000000;
> +	code = cmd[1] & 0x0f;
> +
> +	pos = devip->tape_location[partition];
> +	if (code == 0) { /* blocks */
> +		if (count < 0) {
> +			count = (-count);
> +			pos -= 1;
> +			for (i = 0, blp = devip->tape_blocks[partition] + pos; i < count;
> +			     i++) {
> +				if (pos < 0)
> +					goto is_bop;
> +				else if (IS_TAPE_BLOCK_FM(blp->fl_size))
> +					goto is_fm;
> +				if (i > 0) {
> +					pos--;
> +					blp--;
> +				}
> +			}
> +		} else if (count > 0) {
> +			for (i = 0, blp = devip->tape_blocks[partition] + pos; i < count;
> +			     i++, pos++, blp++) {
> +				if (IS_TAPE_BLOCK_EOD(blp->fl_size))
> +					goto is_eod;
> +				if (IS_TAPE_BLOCK_FM(blp->fl_size)) {
> +					pos += 1;
> +					goto is_fm;
> +				}
> +				if (pos >= devip->tape_eop[partition])
> +					goto is_eop;
> +			}
> +		}
> +	} else if (code == 1) { /* filemarks */
> +		if (count < 0) {
> +			count = (-count);
> +			if (pos == 0)
> +				goto is_bop;
> +			else {
> +				for (i = 0, blp = devip->tape_blocks[partition] + pos;
> +				     i < count && pos >= 0; i++, pos--, blp--) {
> +					for (pos--, blp-- ; !IS_TAPE_BLOCK_FM(blp->fl_size) &&
> +						     pos >= 0; pos--, blp--)
> +						; /* empty */
> +					if (pos < 0)
> +						goto is_bop;
> +				}
> +			}
> +			pos += 1;
> +		} else if (count > 0) {
> +			for (i = 0, blp = devip->tape_blocks[partition] + pos;
> +			     i < count; i++, pos++, blp++) {
> +				for ( ; !IS_TAPE_BLOCK_FM(blp->fl_size) &&
> +					      !IS_TAPE_BLOCK_EOD(blp->fl_size) &&
> +					      pos < devip->tape_eop[partition];
> +				      pos++, blp++)
> +					; /* empty */
> +				if (IS_TAPE_BLOCK_EOD(blp->fl_size))
> +					goto is_eod;
> +				if (pos >= devip->tape_eop[partition])
> +					goto is_eop;
> +			}
> +		}
> +	} else if (code == 3) { /* EOD */
> +		for (blp = devip->tape_blocks[partition] + pos;
> +		     !IS_TAPE_BLOCK_EOD(blp->fl_size) && pos < devip->tape_eop[partition];
> +		     pos++, blp++)
> +			; /* empty */
> +		if (pos >= devip->tape_eop[partition])
> +			goto is_eop;
> +	} else {
> +		/* sequential filemarks not supported */
> +		mk_sense_invalid_fld(scp, SDEB_IN_CDB, 8, -1);
> +		return check_condition_result;
> +	}
> +	devip->tape_location[partition] = pos;
> +	return 0;
> +
> +is_fm:
> +	devip->tape_location[partition] = pos;
> +	mk_sense_info_tape(scp, NO_SENSE, NO_ADDITIONAL_SENSE,
> +			FILEMARK_DETECTED_ASCQ, count - i,
> +			SENSE_FLAG_FILEMARK);
> +	return check_condition_result;
> +
> +is_eod:
> +	devip->tape_location[partition] = pos;
> +	mk_sense_info_tape(scp, BLANK_CHECK, NO_ADDITIONAL_SENSE,
> +			EOD_DETECTED_ASCQ, count - i,
> +			0);
> +	return check_condition_result;
> +
> +is_bop:
> +	devip->tape_location[partition] = 0;
> +	mk_sense_info_tape(scp, NO_SENSE, NO_ADDITIONAL_SENSE,
> +			BEGINNING_OF_P_M_DETECTED_ASCQ, count - i,
> +			SENSE_FLAG_EOM);
> +	devip->tape_location[partition] = 0;
> +	return check_condition_result;
> +
> +is_eop:
> +	devip->tape_location[partition] = devip->tape_eop[partition] - 1;
> +	mk_sense_info_tape(scp, MEDIUM_ERROR, NO_ADDITIONAL_SENSE,
> +			EOP_EOM_DETECTED_ASCQ, (unsigned int)i,
> +			SENSE_FLAG_EOM);
> +	return check_condition_result;
> +}
> +
>   static int resp_rewind(struct scsi_cmnd *scp,
>   		struct sdebug_dev_info *devip)
>   {
> @@ -4121,6 +4256,98 @@ static int prot_verify_read(struct scsi_cmnd *scp, sector_t start_sec,
>   	return ret;
>   }
>   
> +static int resp_read_tape(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
> +{
> +	u32 i, num, transfer, size;
> +	u8 *cmd = scp->cmnd;
> +	struct scsi_data_buffer *sdb = &scp->sdb;
> +	int partition = devip->tape_partition;
> +	u32 pos = devip->tape_location[partition];
> +	struct tape_block *blp;
> +	bool fixed, sili;
> +
> +	if (cmd[0] != READ_6) { /* Only Read(6) supported */
> +		mk_sense_invalid_opcode(scp);
> +		return illegal_condition_result;
> +	}
> +	fixed = (cmd[1] & 0x1) != 0;
> +	sili = (cmd[1] & 0x2) != 0;
> +	if (fixed && sili) {
> +		mk_sense_invalid_fld(scp, SDEB_IN_CDB, 1, 1);
> +		return check_condition_result;
> +	}
> +
> +	transfer = get_unaligned_be24(cmd + 2);
> +	if (fixed) {
> +		num = transfer;
> +		size = devip->tape_blksize;
> +	} else {
> +		if (transfer < TAPE_MIN_BLKSIZE ||
> +			transfer > TAPE_MAX_BLKSIZE) {
> +			mk_sense_invalid_fld(scp, SDEB_IN_CDB, 2, -1);
> +			return check_condition_result;
> +		}
> +		num = 1;
> +		size = transfer;
> +	}
> +
> +	for (i = 0, blp = devip->tape_blocks[partition] + pos;
> +	     i < num && pos < devip->tape_eop[partition];
> +	     i++, pos++, blp++) {
> +		devip->tape_location[partition] = pos + 1;
> +		if (IS_TAPE_BLOCK_FM(blp->fl_size)) {
> +			mk_sense_info_tape(scp, NO_SENSE, NO_ADDITIONAL_SENSE,
> +					FILEMARK_DETECTED_ASCQ, fixed ? num - i : size,
> +					SENSE_FLAG_FILEMARK);
> +			scsi_set_resid(scp, (num - i) * size);
> +			return check_condition_result;
> +		}
> +		/* Assume no REW */
> +		if (IS_TAPE_BLOCK_EOD(blp->fl_size)) {
> +			mk_sense_info_tape(scp, BLANK_CHECK, NO_ADDITIONAL_SENSE,
> +					EOD_DETECTED_ASCQ, fixed ? num - i : size,
> +					0);
> +			devip->tape_location[partition] = pos;
> +			scsi_set_resid(scp, (num - i) * size);
> +			return check_condition_result;
> +		}
> +		sg_zero_buffer(sdb->table.sgl, sdb->table.nents,
> +			size, i * size);
> +		sg_copy_buffer(sdb->table.sgl, sdb->table.nents,
> +			&(blp->data), 4, i * size, false);
> +		if (fixed) {
> +			if (blp->fl_size != devip->tape_blksize) {
> +				scsi_set_resid(scp, (num - i) * size);
> +				mk_sense_info_tape(scp, NO_SENSE, NO_ADDITIONAL_SENSE,
> +						0, num - i,
> +						SENSE_FLAG_ILI);
> +				return check_condition_result;
> +			}
> +		} else {
> +			if (blp->fl_size != size) {
> +				if (blp->fl_size < size)
> +					scsi_set_resid(scp, size - blp->fl_size);
> +				if (!sili) {
> +					mk_sense_info_tape(scp, NO_SENSE, NO_ADDITIONAL_SENSE,
> +							0, size - blp->fl_size,
> +							SENSE_FLAG_ILI);
> +					return check_condition_result;
> +				}
> +			}
> +		}
> +	}
> +	if (pos >= devip->tape_eop[partition]) {
> +		mk_sense_info_tape(scp, NO_SENSE, NO_ADDITIONAL_SENSE,
> +				EOP_EOM_DETECTED_ASCQ, fixed ? num - i : size,
> +				SENSE_FLAG_EOM);
> +		devip->tape_location[partition] = pos - 1;
> +		return check_condition_result;
> +	}
> +	devip->tape_location[partition] = pos;
> +
> +	return 0;
> +}
> +
>   static int resp_read_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
>   {
>   	bool check_prot;
> @@ -4132,6 +4359,9 @@ static int resp_read_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
>   	u8 *cmd = scp->cmnd;
>   	bool meta_data_locked = false;
>   
> +	if (sdebug_ptype == TYPE_TAPE)
> +		return resp_read_tape(scp, devip);
> +
>   	switch (cmd[0]) {
>   	case READ_16:
>   		ei_lba = 0;


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 5/7] scsi: scsi_debug: Add compression mode page for tapes
  2025-02-13  9:26 ` [PATCH v2 5/7] scsi: scsi_debug: Add compression mode page " Kai Mäkisara
@ 2025-02-19 21:58   ` John Meneghini
  0 siblings, 0 replies; 21+ messages in thread
From: John Meneghini @ 2025-02-19 21:58 UTC (permalink / raw)
  To: Kai Mäkisara, linux-scsi, dgilbert; +Cc: martin.petersen, James.Bottomley

Reviewed-by: John Meneghini <jmeneghi@redhat.com>
Tested-by: John Meneghini <jmeneghi@redhat.com>

On 2/13/25 4:26 AM, Kai Mäkisara wrote:
> Add support for compression mode page. The compression status
> is saved and returned. No UA is generated.
> 
> Signed-off-by: Kai Mäkisara <Kai.Makisara@kolumbus.fi>
> ---
>   drivers/scsi/scsi_debug.c | 33 +++++++++++++++++++++++++++++++++
>   1 file changed, 33 insertions(+)
> 
> diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
> index 29a9aea30d22..0a5cd35c41de 100644
> --- a/drivers/scsi/scsi_debug.c
> +++ b/drivers/scsi/scsi_debug.c
> @@ -405,6 +405,7 @@ struct sdebug_dev_info {
>   	unsigned int tape_density;
>   	unsigned char tape_partition;
>   	unsigned char tape_nbr_partitions;
> +	unsigned char tape_dce;
>   	unsigned int tape_location[TAPE_MAX_PARTITIONS];
>   	unsigned int tape_eop[TAPE_MAX_PARTITIONS];
>   	struct tape_block *tape_blocks[TAPE_MAX_PARTITIONS];
> @@ -2843,6 +2844,20 @@ static int resp_partition_m_pg(unsigned char *p, int pcontrol, int target)
>   	return sizeof(partition_pg);
>   }
>   
> +static int resp_compression_m_pg(unsigned char *p, int pcontrol, int target,
> +	unsigned char dce)
> +{	/* Compression page for mode_sense (tape) */
> +	unsigned char compression_pg[] = {0x0f, 14, 0x40, 0, 0, 0, 0, 0,
> +		0, 0, 0, 0, 00, 00};
> +
> +	memcpy(p, compression_pg, sizeof(compression_pg));
> +	if (dce)
> +		p[2] |= 0x80;
> +	if (pcontrol == 1)
> +		memset(p + 2, 0, sizeof(compression_pg) - 2);
> +	return sizeof(compression_pg);
> +}
> +
>   /* PAGE_SIZE is more than necessary but provides room for future expansion. */
>   #define SDEBUG_MAX_MSENSE_SZ PAGE_SIZE
>   
> @@ -2983,6 +2998,12 @@ static int resp_mode_sense(struct scsi_cmnd *scp,
>   		}
>   		offset += len;
>   		break;
> +	case 0xf:	/* Compression Mode Page (tape) */
> +		if (!is_tape)
> +			goto bad_pcode;
> +		len += resp_compression_m_pg(ap, pcontrol, target, devip->tape_dce);
> +		offset += len;
> +		break;
>   	case 0x11:	/* Partition Mode Page (tape) */
>   		if (!is_tape)
>   			goto bad_pcode;
> @@ -3143,6 +3164,14 @@ static int resp_mode_select(struct scsi_cmnd *scp,
>   			goto set_mode_changed_ua;
>   		}
>   		break;
> +	case 0xf:       /* Compression mode page */
> +		if (sdebug_ptype != TYPE_TAPE)
> +			goto bad_pcode;
> +		if ((arr[off + 2] & 0x40) != 0) {
> +			devip->tape_dce = (arr[off + 2] & 0x80) != 0;
> +			return 0;
> +		}
> +		break;
>   	case 0x1c:      /* Informational Exceptions Mode page */
>   		if (iec_m_pg[1] == arr[off + 1]) {
>   			memcpy(iec_m_pg + 2, arr + off + 2,
> @@ -3158,6 +3187,10 @@ static int resp_mode_select(struct scsi_cmnd *scp,
>   set_mode_changed_ua:
>   	set_bit(SDEBUG_UA_MODE_CHANGED, devip->uas_bm);
>   	return 0;
> +
> +bad_pcode:
> +	mk_sense_invalid_fld(scp, SDEB_IN_CDB, 2, 5);
> +	return check_condition_result;
>   }
>   
>   static int resp_temp_l_pg(unsigned char *arr)


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 6/7] scsi: scsi_debug: Reset tape setting at device reset
  2025-02-13  9:26 ` [PATCH v2 6/7] scsi: scsi_debug: Reset tape setting at device reset Kai Mäkisara
@ 2025-02-19 21:58   ` John Meneghini
  0 siblings, 0 replies; 21+ messages in thread
From: John Meneghini @ 2025-02-19 21:58 UTC (permalink / raw)
  To: Kai Mäkisara, linux-scsi, dgilbert; +Cc: martin.petersen, James.Bottomley

Reviewed-by: John Meneghini <jmeneghi@redhat.com>
Tested-by: John Meneghini <jmeneghi@redhat.com>


On 2/13/25 4:26 AM, Kai Mäkisara wrote:
> Set tape block size, density and compression to default values when the
> device is reset (either directly or via target, bus or host reset).
> 
> Signed-off-by: Kai Mäkisara <Kai.Makisara@kolumbus.fi>
> ---
>   drivers/scsi/scsi_debug.c | 21 ++++++++++++++++++++-
>   1 file changed, 20 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
> index 0a5cd35c41de..7da1758da3f5 100644
> --- a/drivers/scsi/scsi_debug.c
> +++ b/drivers/scsi/scsi_debug.c
> @@ -6772,6 +6772,20 @@ static int sdebug_fail_lun_reset(struct scsi_cmnd *cmnd)
>   	return 0;
>   }
>   
> +static void scsi_tape_reset_clear(struct sdebug_dev_info *devip)
> +{
> +	if (sdebug_ptype == TYPE_TAPE) {
> +		int i;
> +
> +		devip->tape_blksize = TAPE_DEF_BLKSIZE;
> +		devip->tape_density = TAPE_DEF_DENSITY;
> +		devip->tape_partition = 0;
> +		devip->tape_dce = 0;
> +		for (i = 0; i < TAPE_MAX_PARTITIONS; i++)
> +			devip->tape_location[i] = 0;
> +	}
> +}
> +
>   static int scsi_debug_device_reset(struct scsi_cmnd *SCpnt)
>   {
>   	struct scsi_device *sdp = SCpnt->device;
> @@ -6785,8 +6799,10 @@ static int scsi_debug_device_reset(struct scsi_cmnd *SCpnt)
>   		sdev_printk(KERN_INFO, sdp, "%s\n", __func__);
>   
>   	scsi_debug_stop_all_queued(sdp);
> -	if (devip)
> +	if (devip) {
>   		set_bit(SDEBUG_UA_POR, devip->uas_bm);
> +		scsi_tape_reset_clear(devip);
> +	}
>   
>   	if (sdebug_fail_lun_reset(SCpnt)) {
>   		scmd_printk(KERN_INFO, SCpnt, "fail lun reset 0x%x\n", opcode);
> @@ -6824,6 +6840,7 @@ static int scsi_debug_target_reset(struct scsi_cmnd *SCpnt)
>   	list_for_each_entry(devip, &sdbg_host->dev_info_list, dev_list) {
>   		if (devip->target == sdp->id) {
>   			set_bit(SDEBUG_UA_BUS_RESET, devip->uas_bm);
> +			scsi_tape_reset_clear(devip);
>   			++k;
>   		}
>   	}
> @@ -6855,6 +6872,7 @@ static int scsi_debug_bus_reset(struct scsi_cmnd *SCpnt)
>   
>   	list_for_each_entry(devip, &sdbg_host->dev_info_list, dev_list) {
>   		set_bit(SDEBUG_UA_BUS_RESET, devip->uas_bm);
> +		scsi_tape_reset_clear(devip);
>   		++k;
>   	}
>   
> @@ -6878,6 +6896,7 @@ static int scsi_debug_host_reset(struct scsi_cmnd *SCpnt)
>   		list_for_each_entry(devip, &sdbg_host->dev_info_list,
>   				    dev_list) {
>   			set_bit(SDEBUG_UA_BUS_RESET, devip->uas_bm);
> +			scsi_tape_reset_clear(devip);
>   			++k;
>   		}
>   	}


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 7/7] scsi: scsi_debug: Add support for partitioning the tape
  2025-02-13  9:26 ` [PATCH v2 7/7] scsi: scsi_debug: Add support for partitioning the tape Kai Mäkisara
@ 2025-02-19 22:17   ` John Meneghini
  0 siblings, 0 replies; 21+ messages in thread
From: John Meneghini @ 2025-02-19 22:17 UTC (permalink / raw)
  To: Kai Mäkisara, linux-scsi, dgilbert; +Cc: martin.petersen, James.Bottomley

Reviewed-by: John Meneghini <jmeneghi@redhat.com>
Tested-by: John Meneghini <jmeneghi@redhat.com>

Tested tape partitions with this patch and everything works!

[root@to-be-determined ~]#  mt -f /dev/nst1 stshowoptions
[Wed Feb 19 17:00:20 2025] st 8:0:0:0: [st1] check_tape: 1087: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 17:00:20 2025] st 8:0:0:0: [st1] Block limits 512 - 1048576 bytes.
[Wed Feb 19 17:00:20 2025] st 8:0:0:0: [st1] Mode sense. Length 11, medium 0, WBS 0, BLL 8
[Wed Feb 19 17:00:20 2025] st 8:0:0:0: [st1] Density 0, tape length: 388000, drv buffer: 0
[Wed Feb 19 17:00:20 2025] st 8:0:0:0: [st1] Block size: 0, buffer size: 4096 (1 blocks).
[Wed Feb 19 17:00:20 2025] st 8:0:0:0: [st1] check_tape: 1287: CHKRES_READY pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 17:00:20 2025] st 8:0:0:0: [st1] st_flush: 1409: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 17:00:20 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0
The options set: buffer-writes async-writes read-ahead debug can-bsr

[root@to-be-determined ~]#  mt -f /dev/nst1 stsetoptions can-partitions
[Wed Feb 19 17:03:51 2025] st 8:0:0:0: [st1] check_tape: 1087: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 17:03:51 2025] st 8:0:0:0: [st1] Block limits 512 - 1048576 bytes.
[Wed Feb 19 17:03:51 2025] st 8:0:0:0: [st1] Mode sense. Length 11, medium 0, WBS 0, BLL 8
[Wed Feb 19 17:03:51 2025] st 8:0:0:0: [st1] Density 0, tape length: 388000, drv buffer: 0
[Wed Feb 19 17:03:51 2025] st 8:0:0:0: [st1] Block size: 0, buffer size: 4096 (1 blocks).
[Wed Feb 19 17:03:51 2025] st 8:0:0:0: [st1] check_tape: 1287: CHKRES_READY pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 17:03:51 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 17:03:51 2025] st 8:0:0:0: [st1] Mode 0 options: buffer writes: 1, async writes: 1, read ahead: 1
[Wed Feb 19 17:03:51 2025] st 8:0:0:0: [st1]     can bsr: 1, two FMs: 0, fast mteom: 0, auto lock: 0,
[Wed Feb 19 17:03:51 2025] st 8:0:0:0: [st1]     defs for wr: 0, no block limits: 0, partitions: 1, s2 log: 0
[Wed Feb 19 17:03:51 2025] st 8:0:0:0: [st1]     sysv: 0 nowait: 0 sili: 0 nowait_filemark: 0
[Wed Feb 19 17:03:51 2025] st 8:0:0:0: [st1]     debugging: 1
[Wed Feb 19 17:03:51 2025] st 8:0:0:0: [st1] st_flush: 1409: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 17:03:51 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0

[root@to-be-determined ~]#  mt -f /dev/nst1 stshowoptions
[Wed Feb 19 17:04:13 2025] st 8:0:0:0: [st1] check_tape: 1087: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 17:04:13 2025] st 8:0:0:0: [st1] Block limits 512 - 1048576 bytes.
[Wed Feb 19 17:04:13 2025] st 8:0:0:0: [st1] Mode sense. Length 11, medium 0, WBS 0, BLL 8
[Wed Feb 19 17:04:13 2025] st 8:0:0:0: [st1] Density 0, tape length: 388000, drv buffer: 0
[Wed Feb 19 17:04:13 2025] st 8:0:0:0: [st1] Block size: 0, buffer size: 4096 (1 blocks).
[Wed Feb 19 17:04:13 2025] st 8:0:0:0: [st1] Updating partition number in status.
[Wed Feb 19 17:04:13 2025] st 8:0:0:0: [st1] Got tape pos. blk 0 part 0.
[Wed Feb 19 17:04:13 2025] st 8:0:0:0: [st1] check_tape: 1287: CHKRES_READY pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 17:04:13 2025] st 8:0:0:0: [st1] st_flush: 1409: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 17:04:13 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0
The options set: buffer-writes async-writes read-ahead debug can-bsr can-partitions

[root@to-be-determined ~]#  mt -f /dev/nst1 mkpartition 200
[Wed Feb 19 17:04:44 2025] st 8:0:0:0: [st1] check_tape: 1087: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 17:04:44 2025] st 8:0:0:0: [st1] Block limits 512 - 1048576 bytes.
[Wed Feb 19 17:04:44 2025] st 8:0:0:0: [st1] Mode sense. Length 11, medium 0, WBS 0, BLL 8
[Wed Feb 19 17:04:44 2025] st 8:0:0:0: [st1] Density 0, tape length: 388000, drv buffer: 0
[Wed Feb 19 17:04:44 2025] st 8:0:0:0: [st1] Block size: 0, buffer size: 4096 (1 blocks).
[Wed Feb 19 17:04:44 2025] st 8:0:0:0: [st1] check_tape: 1287: CHKRES_READY pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 17:04:44 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 17:04:44 2025] st 8:0:0:0: [st1] Loading tape.
[Wed Feb 19 17:04:44 2025] st 8:0:0:0: [st1] check_tape: 1087: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 17:04:44 2025] st 8:0:0:0: [st1] Error: 2, cmd: 0 0 0 0 0 0
[Wed Feb 19 17:04:44 2025] st 8:0:0:0: [st1] Sense Key : Unit Attention [current]
[Wed Feb 19 17:04:44 2025] st 8:0:0:0: [st1] Add. Sense: Not ready to ready change, medium may have changed
[Wed Feb 19 17:04:44 2025] st 8:0:0:0: [st1] st_chk_result: 432: pos_unknown 0 was_reset 0/0 ready 0, result 2
[Wed Feb 19 17:04:44 2025] st 8:0:0:0: [st1] New tape session.
[Wed Feb 19 17:04:44 2025] st 8:0:0:0: [st1] New tape session.
[Wed Feb 19 17:04:44 2025] st 8:0:0:0: [st1] check_tape: 1126: CHKRES_NEW_SESS pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 17:04:44 2025] st 8:0:0:0: [st1] Block limits 512 - 1048576 bytes.
[Wed Feb 19 17:04:44 2025] st 8:0:0:0: [st1] Mode sense. Length 11, medium 0, WBS 0, BLL 8
[Wed Feb 19 17:04:44 2025] st 8:0:0:0: [st1] Density 0, tape length: 388000, drv buffer: 0
[Wed Feb 19 17:04:44 2025] st 8:0:0:0: [st1] Block size: 0, buffer size: 4096 (1 blocks).
[Wed Feb 19 17:04:44 2025] st 8:0:0:0: [st1] check_tape: 1287: CHKRES_READY pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 17:04:44 2025] st 8:0:0:0: [st1] Partition page length is 14 bytes.
[Wed Feb 19 17:04:44 2025] st 8:0:0:0: [st1] psd_cnt 2, max.parts 1, nbr_parts 0
[Wed Feb 19 17:04:44 2025] st 8:0:0:0: [st1] Formatting tape with two partitions (1 = 200 MB).
[Wed Feb 19 17:04:44 2025] st 8:0:0:0: [st1] Sending FORMAT MEDIUM
[Wed Feb 19 17:04:44 2025] st 8:0:0:0: [st1] st_flush: 1409: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 17:04:44 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0

[root@to-be-determined ~]# mt -f /dev/nst1 status
[Wed Feb 19 17:05:11 2025] st 8:0:0:0: [st1] check_tape: 1087: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 17:05:11 2025] st 8:0:0:0: [st1] Block limits 512 - 1048576 bytes.
SCSI 2 tape drive:
File number=0, block number=0, partition=0.
Tape block size 0 bytes. Density code 0x0 (default).
Soft error count since last status=0
General status bits on (41010000):
  BOT ONLINE IM_REP_EN

[Wed Feb 19 17:05:11 2025] st 8:0:0:0: [st1] Mode sense. Length 11, medium 0, WBS 0, BLL 8
[Wed Feb 19 17:05:11 2025] st 8:0:0:0: [st1] Density 0, tape length: 388000, drv buffer: 0
[Wed Feb 19 17:05:11 2025] st 8:0:0:0: [st1] Block size: 0, buffer size: 4096 (1 blocks).
[Wed Feb 19 17:05:11 2025] st 8:0:0:0: [st1] check_tape: 1287: CHKRES_READY pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 17:05:11 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 17:05:11 2025] st 8:0:0:0: [st1] st_flush: 1409: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 17:05:11 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0

[root@to-be-determined ~]#  mt -f /dev/nst1 setpartition 1
[Wed Feb 19 17:05:41 2025] st 8:0:0:0: [st1] check_tape: 1087: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 17:05:41 2025] st 8:0:0:0: [st1] Block limits 512 - 1048576 bytes.
[Wed Feb 19 17:05:41 2025] st 8:0:0:0: [st1] Mode sense. Length 11, medium 0, WBS 0, BLL 8
[Wed Feb 19 17:05:41 2025] st 8:0:0:0: [st1] Density 0, tape length: 388000, drv buffer: 0
[Wed Feb 19 17:05:41 2025] st 8:0:0:0: [st1] Block size: 0, buffer size: 4096 (1 blocks).
[Wed Feb 19 17:05:41 2025] st 8:0:0:0: [st1] check_tape: 1287: CHKRES_READY pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 17:05:41 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 17:05:41 2025] st 8:0:0:0: [st1] st_flush: 1409: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 17:05:41 2025] st 8:0:0:0: [st1] Setting block to 0 and partition to 1.
[Wed Feb 19 17:05:41 2025] st 8:0:0:0: [st1] Got tape pos. blk 0 part 0.
[Wed Feb 19 17:05:41 2025] st 8:0:0:0: [st1] Visited block 0 for partition 0 saved.
[Wed Feb 19 17:05:41 2025] st 8:0:0:0: [st1] Trying to change partition from 0 to 1
[Wed Feb 19 17:05:41 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0

[root@to-be-determined ~]# mt -f /dev/nst1 status
[Wed Feb 19 17:05:47 2025] st 8:0:0:0: [st1] check_tape: 1087: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 17:05:47 2025] st 8:0:0:0: [st1] Block limits 512 - 1048576 bytes.
SCSI 2 tape drive:
File number=0, block number=0, partition=1.
Tape block size 0 bytes. Density code 0x0 (default).
Soft error count since last status=0
General status bits on (41010000):
  BOT ONLINE IM_REP_EN
[Wed Feb 19 17:05:47 2025] st 8:0:0:0: [st1] Mode sense. Length 11, medium 0, WBS 0, BLL 8
[Wed Feb 19 17:05:47 2025] st 8:0:0:0: [st1] Density 0, tape length: 388000, drv buffer: 0
[Wed Feb 19 17:05:47 2025] st 8:0:0:0: [st1] Block size: 0, buffer size: 4096 (1 blocks).
[Wed Feb 19 17:05:47 2025] st 8:0:0:0: [st1] check_tape: 1287: CHKRES_READY pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 17:05:47 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 17:05:47 2025] st 8:0:0:0: [st1] st_flush: 1409: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 17:05:47 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0

[root@to-be-determined ~]# dd if=/dev/random count=1024 of=/dev/nst1
[Wed Feb 19 17:06:17 2025] st 8:0:0:0: [st1] check_tape: 1087: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 17:06:17 2025] st 8:0:0:0: [st1] Block limits 512 - 1048576 bytes.
[Wed Feb 19 17:06:17 2025] st 8:0:0:0: [st1] Mode sense. Length 11, medium 0, WBS 0, BLL 8
[Wed Feb 19 17:06:17 2025] st 8:0:0:0: [st1] Density 0, tape length: 388000, drv buffer: 0
[Wed Feb 19 17:06:17 2025] st 8:0:0:0: [st1] Block size: 0, buffer size: 4096 (1 blocks).
[Wed Feb 19 17:06:17 2025] st 8:0:0:0: [st1] check_tape: 1287: CHKRES_READY pos_unknown 0 was_reset 0/0 ready 0
dd: writing to '/dev/nst1': No space left on device
[Wed Feb 19 17:06:17 2025] st 8:0:0:0: [st1] Error: 2, cmd: a 0 0 2 0 0
[Wed Feb 19 17:06:17 2025] st 8:0:0:0: [st1] Sense Key : No Sense [current]
[Wed Feb 19 17:06:17 2025] st 8:0:0:0: [st1] Add. Sense: End-of-partition/medium detected
^^^^
Oops.

[Wed Feb 19 17:06:17 2025] st 8:0:0:0: [st1] st_chk_result: 432: pos_unknown 0 was_reset 0/0 ready 0, result 2
[Wed Feb 19 17:06:17 2025] st 8:0:0:0: [st1] Error on write:
[Wed Feb 19 17:06:17 2025] st 8:0:0:0: [st1] EOM with 512 bytes unwritten.
[Wed Feb 19 17:06:17 2025] st 8:0:0:0: [st1] st_flush: 1409: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 17:06:17 2025] st 8:0:0:0: [st1] Number of r/w requests 181, dio used in 181, pages 181.
[Wed Feb 19 17:06:17 2025] st 8:0:0:0: [st1] Async write waits 0, finished 0.
[Wed Feb 19 17:06:17 2025] st 8:0:0:0: [st1] Buffer flushed, 1 EOF(s) written
181+0 records in
180+0 records out
92160 bytes (92 kB, 90 KiB) copied, 0.200082 s, 461 kB/s


[root@to-be-determined ~]# mt -f /dev/nst1 eof 2
[Wed Feb 19 17:06:44 2025] st 8:0:0:0: [st1] check_tape: 1087: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 17:06:44 2025] st 8:0:0:0: [st1] Block limits 512 - 1048576 bytes.
[Wed Feb 19 17:06:44 2025] st 8:0:0:0: [st1] Mode sense. Length 11, medium 0, WBS 0, BLL 8
[Wed Feb 19 17:06:44 2025] st 8:0:0:0: [st1] Density 0, tape length: 388000, drv buffer: 0
[Wed Feb 19 17:06:44 2025] st 8:0:0:0: [st1] Block size: 0, buffer size: 4096 (1 blocks).
[Wed Feb 19 17:06:44 2025] st 8:0:0:0: [st1] check_tape: 1287: CHKRES_READY pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 17:06:44 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 17:06:44 2025] st 8:0:0:0: [st1] Writing 2 filemarks.

# Interesting, it lets me write a file mark after hitting EOM.

[Wed Feb 19 17:06:44 2025] st 8:0:0:0: [st1] st_flush: 1409: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 17:06:44 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0

[root@to-be-determined ~]# mt -f /dev/nst1 status
[Wed Feb 19 17:06:59 2025] st 8:0:0:0: [st1] check_tape: 1087: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 17:06:59 2025] st 8:0:0:0: [st1] Block limits 512 - 1048576 bytes.
SCSI 2 tape drive:
File number=3, block number=0, partition=1.
Tape block size 0 bytes. Density code 0x0 (default).
Soft error count since last status=0
General status bits on (81010000):
  EOF ONLINE IM_REP_EN

[Wed Feb 19 17:06:59 2025] st 8:0:0:0: [st1] Mode sense. Length 11, medium 0, WBS 0, BLL 8
[Wed Feb 19 17:06:59 2025] st 8:0:0:0: [st1] Density 0, tape length: 388000, drv buffer: 0
[Wed Feb 19 17:06:59 2025] st 8:0:0:0: [st1] Block size: 0, buffer size: 4096 (1 blocks).
[Wed Feb 19 17:06:59 2025] st 8:0:0:0: [st1] check_tape: 1287: CHKRES_READY pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 17:06:59 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 17:06:59 2025] st 8:0:0:0: [st1] st_flush: 1409: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 17:06:59 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0

[root@to-be-determined ~]#  mt -f /dev/nst1 setpartition 0
[Wed Feb 19 17:07:13 2025] st 8:0:0:0: [st1] check_tape: 1087: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 17:07:13 2025] st 8:0:0:0: [st1] Block limits 512 - 1048576 bytes.
[Wed Feb 19 17:07:13 2025] st 8:0:0:0: [st1] Mode sense. Length 11, medium 0, WBS 0, BLL 8
[Wed Feb 19 17:07:13 2025] st 8:0:0:0: [st1] Density 0, tape length: 388000, drv buffer: 0
[Wed Feb 19 17:07:13 2025] st 8:0:0:0: [st1] Block size: 0, buffer size: 4096 (1 blocks).
[Wed Feb 19 17:07:13 2025] st 8:0:0:0: [st1] check_tape: 1287: CHKRES_READY pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 17:07:13 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 17:07:13 2025] st 8:0:0:0: [st1] st_flush: 1409: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 17:07:13 2025] st 8:0:0:0: [st1] Setting block to 0 and partition to 0.
[Wed Feb 19 17:07:13 2025] st 8:0:0:0: [st1] Got tape pos. blk 184 part 1.
[Wed Feb 19 17:07:13 2025] st 8:0:0:0: [st1] Visited block 184 for partition 1 saved.
[Wed Feb 19 17:07:13 2025] st 8:0:0:0: [st1] Trying to change partition from 1 to 0
[Wed Feb 19 17:07:13 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0

[root@to-be-determined ~]# mt -f /dev/nst1 status
[Wed Feb 19 17:07:30 2025] st 8:0:0:0: [st1] check_tape: 1087: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 17:07:30 2025] st 8:0:0:0: [st1] Block limits 512 - 1048576 bytes.
SCSI 2 tape drive:
File number=0, block number=0, partition=0.
Tape block size 0 bytes. Density code 0x0 (default).
Soft error count since last status=0
General status bits on (41010000):
  BOT ONLINE IM_REP_EN
[Wed Feb 19 17:07:30 2025] st 8:0:0:0: [st1] Mode sense. Length 11, medium 0, WBS 0, BLL 8
[Wed Feb 19 17:07:30 2025] st 8:0:0:0: [st1] Density 0, tape length: 388000, drv buffer: 0
[Wed Feb 19 17:07:30 2025] st 8:0:0:0: [st1] Block size: 0, buffer size: 4096 (1 blocks).
[Wed Feb 19 17:07:30 2025] st 8:0:0:0: [st1] check_tape: 1287: CHKRES_READY pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 17:07:30 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 17:07:30 2025] st 8:0:0:0: [st1] st_flush: 1409: pos_unknown 0 was_reset 0/0 ready 0
[Wed Feb 19 17:07:30 2025] st 8:0:0:0: [st1] flush_buffer: 852: pos_unknown 0 was_reset 0/0 ready 0

On 2/13/25 4:26 AM, Kai Mäkisara wrote:
> This patch adds support for MEDIUM PARTITION PAGE in MODE SELECT and the
> FORMAT MEDIUM command for tapes. After these additions, the virtual tape
> can be partitioned containing either one or two partitions. The POFM
> flag in the mode page is set, meaning that the FORMAT MEDIUM command
> must be used to create the partitioning defined in the mode page.
> 
> Signed-off-by: Kai Mäkisara <Kai.Makisara@kolumbus.fi>
> ---
>   drivers/scsi/scsi_debug.c | 108 ++++++++++++++++++++++++++++++++++++--
>   1 file changed, 104 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
> index 7da1758da3f5..74b136da55ce 100644
> --- a/drivers/scsi/scsi_debug.c
> +++ b/drivers/scsi/scsi_debug.c
> @@ -188,6 +188,7 @@ static const char *sdebug_version_date = "20210520";
>   #define TAPE_EW 20
>   #define TAPE_MAX_PARTITIONS 2
>   #define TAPE_UNITS 10000
> +#define TAPE_PARTITION_1_UNITS 1000
>   
>   /* The tape block data definitions */
>   #define TAPE_BLOCK_FM_FLAG   ((u32)0x1 << 30)
> @@ -405,6 +406,9 @@ struct sdebug_dev_info {
>   	unsigned int tape_density;
>   	unsigned char tape_partition;
>   	unsigned char tape_nbr_partitions;
> +	unsigned char tape_pending_nbr_partitions;
> +	unsigned int tape_pending_part_0_size;
> +	unsigned int tape_pending_part_1_size;
>   	unsigned char tape_dce;
>   	unsigned int tape_location[TAPE_MAX_PARTITIONS];
>   	unsigned int tape_eop[TAPE_MAX_PARTITIONS];
> @@ -534,14 +538,15 @@ enum sdeb_opcode_index {
>   	SDEB_I_LOCATE = 34,
>   	SDEB_I_WRITE_FILEMARKS = 35,
>   	SDEB_I_SPACE = 36,
> -	SDEB_I_LAST_ELEM_P1 = 37,	/* keep this last (previous + 1) */
> +	SDEB_I_FORMAT_MEDIUM = 37,
> +	SDEB_I_LAST_ELEM_P1 = 38,	/* keep this last (previous + 1) */
>   };
>   
>   
>   static const unsigned char opcode_ind_arr[256] = {
>   /* 0x0; 0x0->0x1f: 6 byte cdbs */
>   	SDEB_I_TEST_UNIT_READY, SDEB_I_REZERO_UNIT, 0, SDEB_I_REQUEST_SENSE,
> -	    0, SDEB_I_READ_BLOCK_LIMITS, 0, 0,
> +	    SDEB_I_FORMAT_MEDIUM, SDEB_I_READ_BLOCK_LIMITS, 0, 0,
>   	SDEB_I_READ, 0, SDEB_I_WRITE, 0, 0, 0, 0, 0,
>   	SDEB_I_WRITE_FILEMARKS, SDEB_I_SPACE, SDEB_I_INQUIRY, 0, 0,
>   	    SDEB_I_MODE_SELECT, SDEB_I_RESERVE, SDEB_I_RELEASE,
> @@ -629,6 +634,7 @@ static int resp_locate(struct scsi_cmnd *, struct sdebug_dev_info *);
>   static int resp_write_filemarks(struct scsi_cmnd *, struct sdebug_dev_info *);
>   static int resp_space(struct scsi_cmnd *, struct sdebug_dev_info *);
>   static int resp_rewind(struct scsi_cmnd *, struct sdebug_dev_info *);
> +static int resp_format_medium(struct scsi_cmnd *, struct sdebug_dev_info *);
>   
>   static int sdebug_do_add_host(bool mk_new_store);
>   static int sdebug_add_host_helper(int per_host_idx);
> @@ -867,7 +873,7 @@ static const struct opcode_info_t opcode_info_arr[SDEB_I_LAST_ELEM_P1 + 1] = {
>   	    resp_report_zones, zone_in_iarr, /* ZONE_IN(16), REPORT ZONES) */
>   		{16,  0x0 /* SA */, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
>   		 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0xc7} },
> -/* 31 */
> +/* 32 */
>   	{0, 0x0, 0x0, F_D_OUT | FF_MEDIA_IO,
>   	    resp_atomic_write, NULL, /* ATOMIC WRITE 16 */
>   		{16,  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
> @@ -881,7 +887,9 @@ static const struct opcode_info_t opcode_info_arr[SDEB_I_LAST_ELEM_P1 + 1] = {
>   	    {6,  0x01, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
>   	{0, 0x11, 0, F_D_IN, resp_space, NULL,    /* SPACE (6) */
>   	    {6,  0x07, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
> -
> +	{0, 0x4, 0, 0, resp_format_medium, NULL,  /* FORMAT MEDIUM (6) */
> +	    {6,  0x3, 0x7, 0, 0, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
> +/* 38 */
>   /* sentinel */
>   	{0xff, 0, 0, 0, NULL, NULL,		/* terminating element */
>   	    {0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
> @@ -2844,6 +2852,51 @@ static int resp_partition_m_pg(unsigned char *p, int pcontrol, int target)
>   	return sizeof(partition_pg);
>   }
>   
> +static int process_medium_part_m_pg(struct sdebug_dev_info *devip,
> +				unsigned char *new, int pg_len)
> +{
> +	int new_nbr, p0_size, p1_size;
> +
> +	if ((new[4] & 0x80) != 0) { /* FDP */
> +		partition_pg[4] |= 0x80;
> +		devip->tape_pending_nbr_partitions = TAPE_MAX_PARTITIONS;
> +		devip->tape_pending_part_0_size = TAPE_UNITS - TAPE_PARTITION_1_UNITS;
> +		devip->tape_pending_part_1_size = TAPE_PARTITION_1_UNITS;
> +	} else {
> +		new_nbr = new[3] + 1;
> +		if (new_nbr > TAPE_MAX_PARTITIONS)
> +			return 3;
> +		if ((new[4] & 0x40) != 0) { /* SDP */
> +			p1_size = TAPE_PARTITION_1_UNITS;
> +			p0_size = TAPE_UNITS - p1_size;
> +			if (p0_size < 100)
> +				return 4;
> +		} else if ((new[4] & 0x20) != 0) {
> +			if (new_nbr > 1) {
> +				p0_size = get_unaligned_be16(new + 8);
> +				p1_size = get_unaligned_be16(new + 10);
> +				if (p1_size == 0xFFFF)
> +					p1_size = TAPE_UNITS - p0_size;
> +				else if (p0_size == 0xFFFF)
> +					p0_size = TAPE_UNITS - p1_size;
> +				if (p0_size < 100 || p1_size < 100)
> +					return 8;
> +			} else {
> +				p0_size = TAPE_UNITS;
> +				p1_size = 0;
> +			}
> +		} else
> +			return 6;
> +		devip->tape_pending_nbr_partitions = new_nbr;
> +		devip->tape_pending_part_0_size = p0_size;
> +		devip->tape_pending_part_1_size = p1_size;
> +		partition_pg[3] = new_nbr;
> +		devip->tape_pending_nbr_partitions = new_nbr;
> +	}
> +
> +	return 0;
> +}
> +
>   static int resp_compression_m_pg(unsigned char *p, int pcontrol, int target,
>   	unsigned char dce)
>   {	/* Compression page for mode_sense (tape) */
> @@ -3172,6 +3225,17 @@ static int resp_mode_select(struct scsi_cmnd *scp,
>   			return 0;
>   		}
>   		break;
> +	case 0x11:	/* Medium Partition Mode Page (tape) */
> +		if (sdebug_ptype == TYPE_TAPE) {
> +			int fld;
> +
> +			fld = process_medium_part_m_pg(devip, &arr[off], pg_len);
> +			if (fld == 0)
> +				return 0;
> +			mk_sense_invalid_fld(scp, SDEB_IN_DATA, fld, -1);
> +			return check_condition_result;
> +		}
> +		break;
>   	case 0x1c:      /* Informational Exceptions Mode page */
>   		if (iec_m_pg[1] == arr[off + 1]) {
>   			memcpy(iec_m_pg + 2, arr + off + 2,
> @@ -3556,6 +3620,39 @@ static int partition_tape(struct sdebug_dev_info *devip, int nbr_partitions,
>   	return nbr_partitions;
>   }
>   
> +static int resp_format_medium(struct scsi_cmnd *scp,
> +			struct sdebug_dev_info *devip)
> +{
> +	int res = 0;
> +	unsigned char *cmd = scp->cmnd;
> +
> +	if (sdebug_ptype != TYPE_TAPE) {
> +		mk_sense_invalid_fld(scp, SDEB_IN_CDB, 0, -1);
> +		return check_condition_result;
> +	}
> +	if (cmd[2] > 2) {
> +		mk_sense_invalid_fld(scp, SDEB_IN_DATA, 2, -1);
> +		return check_condition_result;
> +	}
> +	if (cmd[2] != 0) {
> +		if (devip->tape_pending_nbr_partitions > 0) {
> +			res = partition_tape(devip,
> +					devip->tape_pending_nbr_partitions,
> +					devip->tape_pending_part_0_size,
> +					devip->tape_pending_part_1_size);
> +		} else
> +			res = partition_tape(devip, devip->tape_nbr_partitions,
> +					devip->tape_eop[0], devip->tape_eop[1]);
> +	} else
> +		res = partition_tape(devip, 1, TAPE_UNITS, 0);
> +	if (res < 0)
> +		return -EINVAL;
> +
> +	devip->tape_pending_nbr_partitions = -1;
> +
> +	return 0;
> +}
> +
>   static inline bool sdebug_dev_is_zoned(struct sdebug_dev_info *devip)
>   {
>   	return devip->nr_zones != 0;
> @@ -6522,6 +6619,7 @@ static int scsi_debug_sdev_configure(struct scsi_device *sdp,
>   			if (!devip->tape_blocks[0])
>   				return 1;
>   		}
> +		devip->tape_pending_nbr_partitions = -1;
>   		if (partition_tape(devip, 1, TAPE_UNITS, 0) < 0) {
>   			kfree(devip->tape_blocks[0]);
>   			devip->tape_blocks[0] = NULL;
> @@ -6783,6 +6881,8 @@ static void scsi_tape_reset_clear(struct sdebug_dev_info *devip)
>   		devip->tape_dce = 0;
>   		for (i = 0; i < TAPE_MAX_PARTITIONS; i++)
>   			devip->tape_location[i] = 0;
> +		devip->tape_pending_nbr_partitions = -1;
> +		/* Don't reset partitioning? */
>   	}
>   }
>   


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 0/7] scsi: scsi_debug: Add more tape support
  2025-02-13  9:26 [PATCH v2 0/7] scsi: scsi_debug: Add more tape support Kai Mäkisara
                   ` (7 preceding siblings ...)
  2025-02-13 16:12 ` [PATCH v2 0/7] scsi: scsi_debug: Add more tape support John Meneghini
@ 2025-02-19 22:32 ` John Meneghini
  2025-02-24  3:19 ` Martin K. Petersen
  2025-03-04  3:19 ` Martin K. Petersen
  10 siblings, 0 replies; 21+ messages in thread
From: John Meneghini @ 2025-02-19 22:32 UTC (permalink / raw)
  To: Kai Mäkisara, martin.petersen; +Cc: James.Bottomley, linux-scsi, dgilbert

Kai,

Thank you for these patches. This is a huge improvement to scsi_debug tape emulator and I am sure everyone will use this.

I must admit that I cheated a little bit with some of the test results I posted here as I did not test each patch,
but rather tested this whole series. I'll also admit I didn't really test the compression stuff.  I'll need
to develop some more tests to do that.

At this point the scsi_debug tape emulator is working as well as a real tape drive.  This will enable
me to greatly simplify and streamline my tape_tests.  I should now be able to consolidate everything
down into one or two test scripts that can be run against either a hardware tape device or the scsi_debug
tape device. There is certainly more room for improvements in my tests and these patches will enable that work.

The latest version of my tests are in the branch.

https://github.com/johnmeneghini/tape_tests/tree/version_5a

I'll work on improving theses tests as soon as this patches get merged upstream.

Reviewed-by: John Meneghini <jmeneghi@redhat.com>
Tested-by: John Meneghini <jmeneghi@redhat.com>

Martin, please merge these patches.

On 2/13/25 4:26 AM, Kai Mäkisara wrote:
> Currently, the scsi_debug driver can create tape devices and the st
> driver attaches to those. Nothing much can be done with the tape devicesTh
> because scsi_debug does not have support for the tape-specific commands
> and features. These patches add some more tape support to the scsi_debug
> driver. The end result is simulated drives with a tape having one or two
> partitions (one partition is created initially).
> 
> The tape is implemented as fixed number (10 000) of 8-byte units.
> The first four bytes of a unit contain the type of the unit (data
> block, filemark or end-of-data mark). If the units is a data block,
> the first four bytes also contain the block length and the remaining
> four bytes the first bytes of written data. This allows the user
> to use tags to see that the read block is what it was supposed to be.
> 
> The following SCSI operations are added or modified:
> FORMAT MEDIUM
> - added
> LOCATE
> - added
> MODE SELECT
> - modified to allow use without page(s) (just header and block descriptor)
>    - store density and block size
a> - partition page added
> MODE SENSE
> - modified to allow use without page(s) (just header and block descriptor)
>    - set density and block size
> - partition page added
> READ BLOCK LIMITS
> - added
> READ POSITION
> - added
> READ
> - added tape support for READ (6)
> REWIND
> - modified to set the tape position
> SPACE
> - added
> START STOP (LOAD)
> - modified to return New Medium Unit Attention if tape loaded (not
>    according to the standard, but enables testing this UA)
> WRITE
> - added tape support for WRITE (6)
> WRITE FILEMARKS
> - added
> 
> Changes RFC -> v1:
> - rebased to v6.14-rc1
> - virtual tape initialization is rewritten and the tape is made shorter
>    (10 000 units)
> - only one partition is created initially
> - tape block allocation is moved to sdev_configure()
> - tape blocks are freed in sdev_destroy()
> - block size must be multiple of four (SSC standard)
> - granularity set to four in READ BLOCK LIMITS
> - long LBA not allowed for tapes in MODE SELECT/SENSE
> - READ POSITION checks allocation length
> - new patch 7 adds support for re-partitioning the tape
> 
> Changes v1 -> v2:
> - Fixes for bugs reported by the Kernel Test Robot:
>    2/7: changed 'len +=' to 'len =' in resp_mode_sense()
>    3/7: changed 'for (;' to 'for (i = 0;' in partition_tape()
>    4/7: initialized i to zero in resp_space()
> 
> Kai Mäkisara (7):
>    scsi: scsi_debug: First fixes for tapes
>    scsi: scsi_debug: Add READ BLOCK LIMITS and modify LOAD for tapes
>    scsi: scsi_debug: Add write support with block lengths  and 4 bytes of
>      data
>    scsi: scsi_debug: Add read support and update locate for tapes
>    scsi: scsi_debug: Add compression mode page for tapes
>    scsi: scsi_debug: Reset tape setting at device reset
>    scsi: scsi_debug: Add support for partitioning the tape
> 
>   drivers/scsi/scsi_debug.c | 775 +++++++++++++++++++++++++++++++++++++-
>   1 file changed, 761 insertions(+), 14 deletions(-)
> 


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 0/7] scsi: scsi_debug: Add more tape support
  2025-02-13  9:26 [PATCH v2 0/7] scsi: scsi_debug: Add more tape support Kai Mäkisara
                   ` (8 preceding siblings ...)
  2025-02-19 22:32 ` John Meneghini
@ 2025-02-24  3:19 ` Martin K. Petersen
  2025-02-24 15:13   ` "Kai Mäkisara (Kolumbus)"
  2025-03-04  3:19 ` Martin K. Petersen
  10 siblings, 1 reply; 21+ messages in thread
From: Martin K. Petersen @ 2025-02-24  3:19 UTC (permalink / raw)
  To: Kai Mäkisara
  Cc: linux-scsi, dgilbert, martin.petersen, James.Bottomley, jmeneghi


Kai,

> Currently, the scsi_debug driver can create tape devices and the st
> driver attaches to those. Nothing much can be done with the tape
> devices because scsi_debug does not have support for the tape-specific
> commands and features. These patches add some more tape support to the
> scsi_debug driver. The end result is simulated drives with a tape
> having one or two partitions (one partition is created initially).

Applied to 6.15/scsi-staging, thanks!

Unless I'm missing something, you'll need to update the report supported
operation codes bitmasks for the commands that are defined in both SBC
and SSC (READ(6), WRITE(6), ...).

-- 
Martin K. Petersen	Oracle Linux Engineering

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 0/7] scsi: scsi_debug: Add more tape support
  2025-02-24  3:19 ` Martin K. Petersen
@ 2025-02-24 15:13   ` "Kai Mäkisara (Kolumbus)"
  2025-02-25  1:44     ` Martin K. Petersen
  0 siblings, 1 reply; 21+ messages in thread
From: "Kai Mäkisara (Kolumbus)" @ 2025-02-24 15:13 UTC (permalink / raw)
  To: Martin K. Petersen
  Cc: linux-scsi, dgilbert, James.Bottomley@hansenpartnership.com,
	jmeneghi



> On 24. Feb 2025, at 5.19, Martin K. Petersen <martin.petersen@oracle.com> wrote:
> 
> 
> Kai,
> 
>> Currently, the scsi_debug driver can create tape devices and the st
>> driver attaches to those. Nothing much can be done with the tape
>> devices because scsi_debug does not have support for the tape-specific
>> commands and features. These patches add some more tape support to the
>> scsi_debug driver. The end result is simulated drives with a tape
>> having one or two partitions (one partition is created initially).
> 
> Applied to 6.15/scsi-staging, thanks!
> 
> Unless I'm missing something, you'll need to update the report supported
> operation codes bitmasks for the commands that are defined in both SBC
> and SSC (READ(6), WRITE(6), ...).
> 
I have tried to keep the changes minimal in definitions used by the existing
code. The definitions for READ (6), WRITE (6), etc., seemed to be permissive
enough not to affect the tape additions.

The command definitions in scsi_debug are common to all devices. This
means that it reports as supported opcodes anything that is defined for
any device. It also tries to execute any command, unless it is restricted
for certain device types in the code. (sg_opcodes reports the tape
commands as supported, so this works also in practice.)

(Conceptually, it would be possible to add a device type mask to
struct opcode_info_t. The code already supports lists of definitions
and making list traversal check the device type mask would enable
using different definitions for different device types. This was not
absolutely necessary to add the tape support, and I tried to avoid
changes in the core functionality of scsi_debug.)

Thanks, Kai


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 0/7] scsi: scsi_debug: Add more tape support
  2025-02-24 15:13   ` "Kai Mäkisara (Kolumbus)"
@ 2025-02-25  1:44     ` Martin K. Petersen
  0 siblings, 0 replies; 21+ messages in thread
From: Martin K. Petersen @ 2025-02-25  1:44 UTC (permalink / raw)
  To: Kai Mäkisara (Kolumbus)
  Cc: Martin K. Petersen, linux-scsi, dgilbert,
	James.Bottomley@hansenpartnership.com, jmeneghi


Hi Kai!

> The command definitions in scsi_debug are common to all devices.

RSOC allows the device to report which fields in a given command are
valid (identified as "Usage data" below).

# sg_opcodes -o 0x8 /dev/sda

  Opcode=0x08
  Command_name: Read(6)
  Command is supported [conforming to SCSI standard]
  No command duration limit mode page
  Multiple Logical Units (MLU): not reported
  Usage data: 08 ff ff ff ff c7

SBC Read(6) and an SSC Read(6) don't have the exact same fields. And
consequently RSOC won't return the correct usage data for Read(6) in
tape mode. CDB byte 1 for tape should be 0x3 (SILI|FIXED) not 0xff
(Which I actually think is a bug, it should be 0x1f for SBC Read(6)
since bits 5-7 are reserved)).

Anyway. It's purely cosmetic. Nothing depends on this in practice, I
don't think.

-- 
Martin K. Petersen	Oracle Linux Engineering

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 0/7] scsi: scsi_debug: Add more tape support
  2025-02-13  9:26 [PATCH v2 0/7] scsi: scsi_debug: Add more tape support Kai Mäkisara
                   ` (9 preceding siblings ...)
  2025-02-24  3:19 ` Martin K. Petersen
@ 2025-03-04  3:19 ` Martin K. Petersen
  10 siblings, 0 replies; 21+ messages in thread
From: Martin K. Petersen @ 2025-03-04  3:19 UTC (permalink / raw)
  To: linux-scsi, dgilbert, Kai Mäkisara
  Cc: Martin K . Petersen, James.Bottomley, jmeneghi

On Thu, 13 Feb 2025 11:26:29 +0200, Kai Mäkisara wrote:

> Currently, the scsi_debug driver can create tape devices and the st
> driver attaches to those. Nothing much can be done with the tape devices
> because scsi_debug does not have support for the tape-specific commands
> and features. These patches add some more tape support to the scsi_debug
> driver. The end result is simulated drives with a tape having one or two
> partitions (one partition is created initially).
> 
> [...]

Applied to 6.15/scsi-queue, thanks!

[1/7] scsi: scsi_debug: First fixes for tapes
      https://git.kernel.org/mkp/scsi/c/f69da85d5d5c
[2/7] scsi: scsi_debug: Add READ BLOCK LIMITS and modify LOAD for tapes
      https://git.kernel.org/mkp/scsi/c/e7795366c41d
[3/7] scsi: scsi_debug: Add write support with block lengths and 4 bytes of data
      https://git.kernel.org/mkp/scsi/c/e1ac21310aaa
[4/7] scsi: scsi_debug: Add read support and update locate for tapes
      https://git.kernel.org/mkp/scsi/c/0744d3114c60
[5/7] scsi: scsi_debug: Add compression mode page for tapes
      https://git.kernel.org/mkp/scsi/c/568354b24c7d
[6/7] scsi: scsi_debug: Reset tape setting at device reset
      https://git.kernel.org/mkp/scsi/c/862a5556b1a4
[7/7] scsi: scsi_debug: Add support for partitioning the tape
      https://git.kernel.org/mkp/scsi/c/23f4e82bb9eb

-- 
Martin K. Petersen	Oracle Linux Engineering

^ permalink raw reply	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2025-03-04  3:19 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-02-13  9:26 [PATCH v2 0/7] scsi: scsi_debug: Add more tape support Kai Mäkisara
2025-02-13  9:26 ` [PATCH v2 1/7] scsi: scsi_debug: First fixes for tapes Kai Mäkisara
2025-02-19 16:03   ` John Meneghini
2025-02-13  9:26 ` [PATCH v2 2/7] scsi: scsi_debug: Add READ BLOCK LIMITS and modify LOAD " Kai Mäkisara
2025-02-19 21:16   ` John Meneghini
2025-02-13  9:26 ` [PATCH v2 3/7] scsi: scsi_debug: Add write support with block lengths and 4 bytes of data Kai Mäkisara
2025-02-19 21:45   ` John Meneghini
2025-02-13  9:26 ` [PATCH v2 4/7] scsi: scsi_debug: Add read support and update locate for tapes Kai Mäkisara
2025-02-19 21:56   ` John Meneghini
2025-02-13  9:26 ` [PATCH v2 5/7] scsi: scsi_debug: Add compression mode page " Kai Mäkisara
2025-02-19 21:58   ` John Meneghini
2025-02-13  9:26 ` [PATCH v2 6/7] scsi: scsi_debug: Reset tape setting at device reset Kai Mäkisara
2025-02-19 21:58   ` John Meneghini
2025-02-13  9:26 ` [PATCH v2 7/7] scsi: scsi_debug: Add support for partitioning the tape Kai Mäkisara
2025-02-19 22:17   ` John Meneghini
2025-02-13 16:12 ` [PATCH v2 0/7] scsi: scsi_debug: Add more tape support John Meneghini
2025-02-19 22:32 ` John Meneghini
2025-02-24  3:19 ` Martin K. Petersen
2025-02-24 15:13   ` "Kai Mäkisara (Kolumbus)"
2025-02-25  1:44     ` Martin K. Petersen
2025-03-04  3:19 ` Martin K. Petersen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox