diff mbox series

[v1,2/7] scsi: scsi_debug: Add READ BLOCK LIMITS and modify LOAD for tapes

Message ID 20250210191232.185207-3-Kai.Makisara@kolumbus.fi (mailing list archive)
State Superseded
Headers show
Series scsi: scsi_debug: Add more tape support | expand

Commit Message

Kai Mäkisara Feb. 10, 2025, 7:12 p.m. UTC
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>
---
 drivers/scsi/scsi_debug.c | 127 ++++++++++++++++++++++++++++++++++++--
 1 file changed, 121 insertions(+), 6 deletions(-)

Comments

kernel test robot Feb. 11, 2025, 10:41 a.m. UTC | #1
Hi Kai,

kernel test robot noticed the following build warnings:

[auto build test WARNING on jejb-scsi/for-next]
[also build test WARNING on mkp-scsi/for-next linus/master v6.14-rc2 next-20250210]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Kai-M-kisara/scsi-scsi_debug-First-fixes-for-tapes/20250211-031623
base:   https://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi.git for-next
patch link:    https://lore.kernel.org/r/20250210191232.185207-3-Kai.Makisara%40kolumbus.fi
patch subject: [PATCH v1 2/7] scsi: scsi_debug: Add READ BLOCK LIMITS and modify LOAD for tapes
config: x86_64-kexec (https://download.01.org/0day-ci/archive/20250211/202502111805.dXd61ARr-lkp@intel.com/config)
compiler: clang version 19.1.3 (https://github.com/llvm/llvm-project ab51eccf88f5321e7c60591c5546b254b6afab99)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250211/202502111805.dXd61ARr-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202502111805.dXd61ARr-lkp@intel.com/

All warnings (new ones prefixed by >>):

   In file included from drivers/scsi/scsi_debug.c:31:
   In file included from include/linux/scatterlist.h:8:
   In file included from include/linux/mm.h:2224:
   include/linux/vmstat.h:504:43: warning: arithmetic between different enumeration types ('enum zone_stat_item' and 'enum numa_stat_item') [-Wenum-enum-conversion]
     504 |         return vmstat_text[NR_VM_ZONE_STAT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~ ^
     505 |                            item];
         |                            ~~~~
   include/linux/vmstat.h:511:43: warning: arithmetic between different enumeration types ('enum zone_stat_item' and 'enum numa_stat_item') [-Wenum-enum-conversion]
     511 |         return vmstat_text[NR_VM_ZONE_STAT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~ ^
     512 |                            NR_VM_NUMA_EVENT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~~
   include/linux/vmstat.h:524:43: warning: arithmetic between different enumeration types ('enum zone_stat_item' and 'enum numa_stat_item') [-Wenum-enum-conversion]
     524 |         return vmstat_text[NR_VM_ZONE_STAT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~ ^
     525 |                            NR_VM_NUMA_EVENT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~~
>> drivers/scsi/scsi_debug.c:2926:3: warning: variable 'len' is uninitialized when used here [-Wuninitialized]
    2926 |                 len += resp_partition_m_pg(ap, pcontrol, target);
         |                 ^~~
   drivers/scsi/scsi_debug.c:2791:28: note: initialize the variable 'len' to silence this warning
    2791 |         u32 alloc_len, offset, len;
         |                                   ^
         |                                    = 0
   4 warnings generated.


vim +/len +2926 drivers/scsi/scsi_debug.c

  2785	
  2786	static int resp_mode_sense(struct scsi_cmnd *scp,
  2787				   struct sdebug_dev_info *devip)
  2788	{
  2789		int pcontrol, pcode, subpcode, bd_len;
  2790		unsigned char dev_spec;
  2791		u32 alloc_len, offset, len;
  2792		int target_dev_id;
  2793		int target = scp->device->id;
  2794		unsigned char *ap;
  2795		unsigned char *arr __free(kfree);
  2796		unsigned char *cmd = scp->cmnd;
  2797		bool dbd, llbaa, msense_6, is_disk, is_zbc, is_tape;
  2798	
  2799		arr = kzalloc(SDEBUG_MAX_MSENSE_SZ, GFP_ATOMIC);
  2800		if (!arr)
  2801			return -ENOMEM;
  2802		dbd = !!(cmd[1] & 0x8);		/* disable block descriptors */
  2803		pcontrol = (cmd[2] & 0xc0) >> 6;
  2804		pcode = cmd[2] & 0x3f;
  2805		subpcode = cmd[3];
  2806		msense_6 = (MODE_SENSE == cmd[0]);
  2807		llbaa = msense_6 ? false : !!(cmd[1] & 0x10);
  2808		is_disk = (sdebug_ptype == TYPE_DISK);
  2809		is_zbc = devip->zoned;
  2810		is_tape = (sdebug_ptype == TYPE_TAPE);
  2811		if ((is_disk || is_zbc || is_tape) && !dbd)
  2812			bd_len = llbaa ? 16 : 8;
  2813		else
  2814			bd_len = 0;
  2815		alloc_len = msense_6 ? cmd[4] : get_unaligned_be16(cmd + 7);
  2816		if (0x3 == pcontrol) {  /* Saving values not supported */
  2817			mk_sense_buffer(scp, ILLEGAL_REQUEST, SAVING_PARAMS_UNSUP, 0);
  2818			return check_condition_result;
  2819		}
  2820		target_dev_id = ((devip->sdbg_host->shost->host_no + 1) * 2000) +
  2821				(devip->target * 1000) - 3;
  2822		/* for disks+zbc set DPOFUA bit and clear write protect (WP) bit */
  2823		if (is_disk || is_zbc) {
  2824			dev_spec = 0x10;	/* =0x90 if WP=1 implies read-only */
  2825			if (sdebug_wp)
  2826				dev_spec |= 0x80;
  2827		} else
  2828			dev_spec = 0x0;
  2829		if (msense_6) {
  2830			arr[2] = dev_spec;
  2831			arr[3] = bd_len;
  2832			offset = 4;
  2833		} else {
  2834			arr[3] = dev_spec;
  2835			if (16 == bd_len)
  2836				arr[4] = 0x1;	/* set LONGLBA bit */
  2837			arr[7] = bd_len;	/* assume 255 or less */
  2838			offset = 8;
  2839		}
  2840		ap = arr + offset;
  2841		if ((bd_len > 0) && (!sdebug_capacity))
  2842			sdebug_capacity = get_sdebug_capacity();
  2843	
  2844		if (8 == bd_len) {
  2845			if (sdebug_capacity > 0xfffffffe)
  2846				put_unaligned_be32(0xffffffff, ap + 0);
  2847			else
  2848				put_unaligned_be32(sdebug_capacity, ap + 0);
  2849			if (is_tape) {
  2850				ap[0] = devip->tape_density;
  2851				put_unaligned_be16(devip->tape_blksize, ap + 6);
  2852			} else
  2853				put_unaligned_be16(sdebug_sector_size, ap + 6);
  2854			offset += bd_len;
  2855			ap = arr + offset;
  2856		} else if (16 == bd_len) {
  2857			if (is_tape) {
  2858				mk_sense_invalid_fld(scp, SDEB_IN_DATA, 1, 4);
  2859				return check_condition_result;
  2860			}
  2861			put_unaligned_be64((u64)sdebug_capacity, ap + 0);
  2862			put_unaligned_be32(sdebug_sector_size, ap + 12);
  2863			offset += bd_len;
  2864			ap = arr + offset;
  2865		}
  2866		if (cmd[2] == 0)
  2867			goto only_bd; /* Only block descriptor requested */
  2868	
  2869		/*
  2870		 * N.B. If len>0 before resp_*_pg() call, then form of that call should be:
  2871		 *        len += resp_*_pg(ap + len, pcontrol, target);
  2872		 */
  2873		switch (pcode) {
  2874		case 0x1:	/* Read-Write error recovery page, direct access */
  2875			if (subpcode > 0x0 && subpcode < 0xff)
  2876				goto bad_subpcode;
  2877			len = resp_err_recov_pg(ap, pcontrol, target);
  2878			offset += len;
  2879			break;
  2880		case 0x2:	/* Disconnect-Reconnect page, all devices */
  2881			if (subpcode > 0x0 && subpcode < 0xff)
  2882				goto bad_subpcode;
  2883			len = resp_disconnect_pg(ap, pcontrol, target);
  2884			offset += len;
  2885			break;
  2886		case 0x3:       /* Format device page, direct access */
  2887			if (subpcode > 0x0 && subpcode < 0xff)
  2888				goto bad_subpcode;
  2889			if (is_disk) {
  2890				len = resp_format_pg(ap, pcontrol, target);
  2891				offset += len;
  2892			} else {
  2893				goto bad_pcode;
  2894			}
  2895			break;
  2896		case 0x8:	/* Caching page, direct access */
  2897			if (subpcode > 0x0 && subpcode < 0xff)
  2898				goto bad_subpcode;
  2899			if (is_disk || is_zbc) {
  2900				len = resp_caching_pg(ap, pcontrol, target);
  2901				offset += len;
  2902			} else {
  2903				goto bad_pcode;
  2904			}
  2905			break;
  2906		case 0xa:	/* Control Mode page, all devices */
  2907			switch (subpcode) {
  2908			case 0:
  2909				len = resp_ctrl_m_pg(ap, pcontrol, target);
  2910				break;
  2911			case 0x05:
  2912				len = resp_grouping_m_pg(ap, pcontrol, target);
  2913				break;
  2914			case 0xff:
  2915				len = resp_ctrl_m_pg(ap, pcontrol, target);
  2916				len += resp_grouping_m_pg(ap + len, pcontrol, target);
  2917				break;
  2918			default:
  2919				goto bad_subpcode;
  2920			}
  2921			offset += len;
  2922			break;
  2923		case 0x11:	/* Partition Mode Page (tape) */
  2924			if (!is_tape)
  2925				goto bad_pcode;
> 2926			len += resp_partition_m_pg(ap, pcontrol, target);
  2927			offset += len;
  2928			break;
  2929		case 0x19:	/* if spc==1 then sas phy, control+discover */
  2930			if (subpcode > 0x2 && subpcode < 0xff)
  2931				goto bad_subpcode;
  2932			len = 0;
  2933			if ((0x0 == subpcode) || (0xff == subpcode))
  2934				len += resp_sas_sf_m_pg(ap + len, pcontrol, target);
  2935			if ((0x1 == subpcode) || (0xff == subpcode))
  2936				len += resp_sas_pcd_m_spg(ap + len, pcontrol, target,
  2937							  target_dev_id);
  2938			if ((0x2 == subpcode) || (0xff == subpcode))
  2939				len += resp_sas_sha_m_spg(ap + len, pcontrol);
  2940			offset += len;
  2941			break;
  2942		case 0x1c:	/* Informational Exceptions Mode page, all devices */
  2943			if (subpcode > 0x0 && subpcode < 0xff)
  2944				goto bad_subpcode;
  2945			len = resp_iec_m_pg(ap, pcontrol, target);
  2946			offset += len;
  2947			break;
  2948		case 0x3f:	/* Read all Mode pages */
  2949			if (subpcode > 0x0 && subpcode < 0xff)
  2950				goto bad_subpcode;
  2951			len = resp_err_recov_pg(ap, pcontrol, target);
  2952			len += resp_disconnect_pg(ap + len, pcontrol, target);
  2953			if (is_disk) {
  2954				len += resp_format_pg(ap + len, pcontrol, target);
  2955				len += resp_caching_pg(ap + len, pcontrol, target);
  2956			} else if (is_zbc) {
  2957				len += resp_caching_pg(ap + len, pcontrol, target);
  2958			}
  2959			len += resp_ctrl_m_pg(ap + len, pcontrol, target);
  2960			if (0xff == subpcode)
  2961				len += resp_grouping_m_pg(ap + len, pcontrol, target);
  2962			len += resp_sas_sf_m_pg(ap + len, pcontrol, target);
  2963			if (0xff == subpcode) {
  2964				len += resp_sas_pcd_m_spg(ap + len, pcontrol, target,
  2965							  target_dev_id);
  2966				len += resp_sas_sha_m_spg(ap + len, pcontrol);
  2967			}
  2968			len += resp_iec_m_pg(ap + len, pcontrol, target);
  2969			offset += len;
  2970			break;
  2971		default:
  2972			goto bad_pcode;
  2973		}
  2974	only_bd:
  2975		if (msense_6)
  2976			arr[0] = offset - 1;
  2977		else
  2978			put_unaligned_be16((offset - 2), arr + 0);
  2979		return fill_from_dev_buffer(scp, arr, min_t(u32, alloc_len, offset));
  2980	
  2981	bad_pcode:
  2982		mk_sense_invalid_fld(scp, SDEB_IN_CDB, 2, 5);
  2983		return check_condition_result;
  2984	
  2985	bad_subpcode:
  2986		mk_sense_invalid_fld(scp, SDEB_IN_CDB, 3, -1);
  2987		return check_condition_result;
  2988	}
  2989
diff mbox series

Patch

diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index 4da0c259390b..5c662f3d97e3 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);