All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/7] scsi_debug: Add ZBC support
@ 2020-04-22 10:42 Damien Le Moal
  2020-04-22 10:42 ` [PATCH 1/7] scsi_debug: add zbc mode and VPD pages Damien Le Moal
                   ` (7 more replies)
  0 siblings, 8 replies; 12+ messages in thread
From: Damien Le Moal @ 2020-04-22 10:42 UTC (permalink / raw)
  To: linux-scsi, Martin K . Petersen; +Cc: Douglas Gilbert

This is the second part of the scsi_debug updates implementing ZBC
support on top of Doug "per_host_store+random parameters, compare" patch
series.

The ZBC emulation implemented allows to emulate both host-managed and
host-aware disks with configurable zone size, number of conventional
zones znd maximum number of open zones. One feature missing is the
emulation of ZBC RC_BASIS which changes the behavior of the READ
CAPACITY command. This is however not a critical point for testing as,
to my knowledge, there are no disks using RC_BASIS on the market today.
RC_BASIS emulation can thus be added as a later patch.

The emulated devices, both host-aware and host-managed, pass libzbc ZBC
conformance tests (SG interface) as well as zonefs tests (block layer
interface).

Damien Le Moal (4):
  scsi_debug: add zone_max_open module parameter
  scsi_debug: add zone_nr_conv module parameter
  scsi_debug: add zone_size_mb module parameter
  scsi_debug: implement zbc host-aware emulation

Douglas Gilbert (3):
  scsi_debug: add zbc mode and VPD pages
  scsi_debug: add zbc zone commands
  scsi_debug: add zbc module parameter

 drivers/scsi/scsi_debug.c | 1016 ++++++++++++++++++++++++++++++++++++-
 1 file changed, 993 insertions(+), 23 deletions(-)

-- 
2.25.3


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

* [PATCH 1/7] scsi_debug: add zbc mode and VPD pages
  2020-04-22 10:42 [PATCH 0/7] scsi_debug: Add ZBC support Damien Le Moal
@ 2020-04-22 10:42 ` Damien Le Moal
  2020-04-22 10:42 ` [PATCH 2/7] scsi_debug: add zbc zone commands Damien Le Moal
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 12+ messages in thread
From: Damien Le Moal @ 2020-04-22 10:42 UTC (permalink / raw)
  To: linux-scsi, Martin K . Petersen; +Cc: Douglas Gilbert

From: Douglas Gilbert <dgilbert@interlog.com>

The ZBC standard "piggy-backs" on many, but not all, of the
facilities in SBC. Add those ZBC mode pages (plus mode
parameter block descriptors (e.g. "WP")) and VPD pages in
common with SBC. Add ZBC specific VPD page for the host-managed
ZBC device type (ptype=0x14).

Signed-off-by: Douglas Gilbert <dgilbert@interlog.com>
Signed-off-by: Damien Le Moal <damien.lemoal@wdc.com>
---
 drivers/scsi/scsi_debug.c | 55 +++++++++++++++++++++++++++++++--------
 1 file changed, 44 insertions(+), 11 deletions(-)

diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index 0eede06fc66c..4aa4d9e58978 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -1445,6 +1445,24 @@ static int inquiry_vpd_b2(unsigned char *arr)
 	return 0x4;
 }
 
+/* Zoned block device characteristics VPD page (ZBC mandatory) */
+static int inquiry_vpd_b6(unsigned char *arr)
+{
+	memset(arr, 0, 0x3c);
+	arr[0] = 0x1; /* set URSWRZ (unrestricted read in seq. wr req zone) */
+	/*
+	 * Set Optimal number of open sequential write preferred zones and
+	 * Optimal number of non-sequentially written sequential write
+	 * preferred zones and Maximum number of open sequential write
+	 * required zones fields to 'not reported' (0xffffffff). Leave other
+	 * fields set to zero.
+	 */
+	put_unaligned_be32(0xffffffff, &arr[4]);
+	put_unaligned_be32(0xffffffff, &arr[8]);
+	put_unaligned_be32(0xffffffff, &arr[12]);
+	return 0x3c;
+}
+
 #define SDEBUG_LONG_INQ_SZ 96
 #define SDEBUG_MAX_INQ_ARR_SZ 584
 
@@ -1454,13 +1472,15 @@ static int resp_inquiry(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
 	unsigned char *arr;
 	unsigned char *cmd = scp->cmnd;
 	int alloc_len, n, ret;
-	bool have_wlun, is_disk;
+	bool have_wlun, is_disk, is_zbc, is_disk_zbc;
 
 	alloc_len = get_unaligned_be16(cmd + 3);
 	arr = kzalloc(SDEBUG_MAX_INQ_ARR_SZ, GFP_ATOMIC);
 	if (! arr)
 		return DID_REQUEUE << 16;
 	is_disk = (sdebug_ptype == TYPE_DISK);
+	is_zbc = (sdebug_ptype == TYPE_ZBC);
+	is_disk_zbc = (is_disk || is_zbc);
 	have_wlun = scsi_is_wlun(scp->device->lun);
 	if (have_wlun)
 		pq_pdt = TYPE_WLUN;	/* present, wlun */
@@ -1498,11 +1518,14 @@ static int resp_inquiry(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
 			arr[n++] = 0x86;  /* extended inquiry */
 			arr[n++] = 0x87;  /* mode page policy */
 			arr[n++] = 0x88;  /* SCSI ports */
-			if (is_disk) {	  /* SBC only */
+			if (is_disk_zbc) {	  /* SBC or ZBC */
 				arr[n++] = 0x89;  /* ATA information */
 				arr[n++] = 0xb0;  /* Block limits */
 				arr[n++] = 0xb1;  /* Block characteristics */
-				arr[n++] = 0xb2;  /* Logical Block Prov */
+				if (is_disk)
+					arr[n++] = 0xb2;  /* LB Provisioning */
+				else if (is_zbc)
+					arr[n++] = 0xb6;  /* ZB dev. char. */
 			}
 			arr[3] = n - 4;	  /* number of supported VPD pages */
 		} else if (0x80 == cmd[2]) { /* unit serial number */
@@ -1541,19 +1564,22 @@ static int resp_inquiry(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
 		} else if (0x88 == cmd[2]) { /* SCSI Ports */
 			arr[1] = cmd[2];	/*sanity */
 			arr[3] = inquiry_vpd_88(&arr[4], target_dev_id);
-		} else if (is_disk && 0x89 == cmd[2]) { /* ATA information */
+		} else if (is_disk_zbc && 0x89 == cmd[2]) { /* ATA info */
 			arr[1] = cmd[2];        /*sanity */
 			n = inquiry_vpd_89(&arr[4]);
 			put_unaligned_be16(n, arr + 2);
-		} else if (is_disk && 0xb0 == cmd[2]) { /* Block limits */
+		} else if (is_disk_zbc && 0xb0 == cmd[2]) { /* Block limits */
 			arr[1] = cmd[2];        /*sanity */
 			arr[3] = inquiry_vpd_b0(&arr[4]);
-		} else if (is_disk && 0xb1 == cmd[2]) { /* Block char. */
+		} else if (is_disk_zbc && 0xb1 == cmd[2]) { /* Block char. */
 			arr[1] = cmd[2];        /*sanity */
 			arr[3] = inquiry_vpd_b1(&arr[4]);
 		} else if (is_disk && 0xb2 == cmd[2]) { /* LB Prov. */
 			arr[1] = cmd[2];        /*sanity */
 			arr[3] = inquiry_vpd_b2(&arr[4]);
+		} else if (is_zbc && cmd[2] == 0xb6) { /* ZB dev. charact. */
+			arr[1] = cmd[2];        /*sanity */
+			arr[3] = inquiry_vpd_b6(&arr[4]);
 		} else {
 			mk_sense_invalid_fld(scp, SDEB_IN_CDB, 2, -1);
 			kfree(arr);
@@ -1591,6 +1617,9 @@ static int resp_inquiry(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
 	} else if (sdebug_ptype == TYPE_TAPE) {	/* SSC-4 rev 3 */
 		put_unaligned_be16(0x525, arr + n);
 		n += 2;
+	} else if (is_zbc) {	/* ZBC BSR INCITS 536 revision 05 */
+		put_unaligned_be16(0x624, arr + n);
+		n += 2;
 	}
 	put_unaligned_be16(0x2100, arr + n);	/* SPL-4 no version claimed */
 	ret = fill_from_dev_buffer(scp, arr,
@@ -2180,7 +2209,7 @@ static int resp_mode_sense(struct scsi_cmnd *scp,
 	unsigned char *ap;
 	unsigned char arr[SDEBUG_MAX_MSENSE_SZ];
 	unsigned char *cmd = scp->cmnd;
-	bool dbd, llbaa, msense_6, is_disk, bad_pcode;
+	bool dbd, llbaa, msense_6, is_disk, is_zbc, bad_pcode;
 
 	dbd = !!(cmd[1] & 0x8);		/* disable block descriptors */
 	pcontrol = (cmd[2] & 0xc0) >> 6;
@@ -2189,7 +2218,8 @@ static int resp_mode_sense(struct scsi_cmnd *scp,
 	msense_6 = (MODE_SENSE == cmd[0]);
 	llbaa = msense_6 ? false : !!(cmd[1] & 0x10);
 	is_disk = (sdebug_ptype == TYPE_DISK);
-	if (is_disk && !dbd)
+	is_zbc = (sdebug_ptype == TYPE_ZBC);
+	if ((is_disk || is_zbc) && !dbd)
 		bd_len = llbaa ? 16 : 8;
 	else
 		bd_len = 0;
@@ -2201,8 +2231,8 @@ static int resp_mode_sense(struct scsi_cmnd *scp,
 	}
 	target_dev_id = ((devip->sdbg_host->shost->host_no + 1) * 2000) +
 			(devip->target * 1000) - 3;
-	/* for disks set DPOFUA bit and clear write protect (WP) bit */
-	if (is_disk) {
+	/* for disks+zbc set DPOFUA bit and clear write protect (WP) bit */
+	if (is_disk || is_zbc) {
 		dev_spec = 0x10;	/* =0x90 if WP=1 implies read-only */
 		if (sdebug_wp)
 			dev_spec |= 0x80;
@@ -2262,7 +2292,7 @@ static int resp_mode_sense(struct scsi_cmnd *scp,
 			bad_pcode = true;
 		break;
 	case 0x8:	/* Caching page, direct access */
-		if (is_disk) {
+		if (is_disk || is_zbc) {
 			len = resp_caching_pg(ap, pcontrol, target);
 			offset += len;
 		} else
@@ -2300,6 +2330,9 @@ static int resp_mode_sense(struct scsi_cmnd *scp,
 						      target);
 				len += resp_caching_pg(ap + len, pcontrol,
 						       target);
+			} else if (is_zbc) {
+				len += resp_caching_pg(ap + len, pcontrol,
+						       target);
 			}
 			len += resp_ctrl_m_pg(ap + len, pcontrol, target);
 			len += resp_sas_sf_m_pg(ap + len, pcontrol, target);
-- 
2.25.3


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

* [PATCH 2/7] scsi_debug: add zbc zone commands
  2020-04-22 10:42 [PATCH 0/7] scsi_debug: Add ZBC support Damien Le Moal
  2020-04-22 10:42 ` [PATCH 1/7] scsi_debug: add zbc mode and VPD pages Damien Le Moal
@ 2020-04-22 10:42 ` Damien Le Moal
  2020-04-22 10:42 ` [PATCH 3/7] scsi_debug: add zbc module parameter Damien Le Moal
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 12+ messages in thread
From: Damien Le Moal @ 2020-04-22 10:42 UTC (permalink / raw)
  To: linux-scsi, Martin K . Petersen; +Cc: Douglas Gilbert

From: Douglas Gilbert <dgilbert@interlog.com>

Add support for the 5 ZBC commands and enough functionality to emulate
a host-managed device with one conventional zone and a set of sequential
write required zones up to the disk capacity.

Signed-off-by: Douglas Gilbert <dgilbert@interlog.com>
Signed-off-by: Damien Le Moal <damien.lemoal@wdc.com>
---
 drivers/scsi/scsi_debug.c | 819 +++++++++++++++++++++++++++++++++++++-
 1 file changed, 804 insertions(+), 15 deletions(-)

diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index 4aa4d9e58978..10214017d478 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -94,6 +94,11 @@ static const char *sdebug_version_date = "20200421";
 #define MICROCODE_CHANGED_ASCQ 0x1	/* with TARGET_CHANGED_ASC */
 #define MICROCODE_CHANGED_WO_RESET_ASCQ 0x16
 #define WRITE_ERROR_ASC 0xc
+#define UNALIGNED_WRITE_ASCQ 0x4
+#define WRITE_BOUNDARY_ASCQ 0x5
+#define READ_INVDATA_ASCQ 0x6
+#define READ_BOUNDARY_ASCQ 0x7
+#define INSUFF_ZONE_ASCQ 0xe
 
 /* Additional Sense Code Qualifier (ASCQ) */
 #define ACK_NAK_TO 0x3
@@ -147,6 +152,10 @@ static const char *sdebug_version_date = "20200421";
 #define DEF_UUID_CTL 0
 #define JDELAY_OVERRIDDEN -9999
 
+/* Default parameters for ZBC drives */
+#define DEF_ZBC_ZONE_SIZE_MB	128
+#define DEF_ZBC_MAX_OPEN_ZONES	8
+
 #define SDEBUG_LUN_0_VAL 0
 
 /* bit mask values for sdebug_opts */
@@ -250,6 +259,24 @@ static const char *sdebug_version_date = "20200421";
 
 #define SDEB_XA_NOT_IN_USE XA_MARK_1
 
+/* enumeration names taken from table 26, zbcr05 */
+enum sdebug_z_cond {
+	ZBC_NOT_WRITE_POINTER	= 0x0,
+	ZC1_EMPTY		= 0x1,
+	ZC2_IMPLICIT_OPEN	= 0x2,
+	ZC3_EXPLICIT_OPEN	= 0x3,
+	ZC4_CLOSED		= 0x4,
+	ZC6_READ_ONLY		= 0xd,
+	ZC5_FULL		= 0xe,
+	ZC7_OFFLINE		= 0xf,
+};
+
+struct sdeb_zone_state {	/* ZBC: per zone state */
+	enum sdebug_z_cond z_cond;
+	unsigned int z_size;
+	sector_t z_start;
+	sector_t z_wp;
+};
 
 struct sdebug_dev_info {
 	struct list_head dev_list;
@@ -262,6 +289,16 @@ struct sdebug_dev_info {
 	atomic_t num_in_q;
 	atomic_t stopped;
 	bool used;
+
+	/* For ZBC devices */
+	unsigned int zsize;
+	unsigned int zsize_shift;
+	unsigned int nr_zones;
+	unsigned int nr_imp_open;
+	unsigned int nr_exp_open;
+	unsigned int nr_closed;
+	unsigned int max_open;
+	struct sdeb_zone_state *zstate;
 };
 
 struct sdebug_host_info {
@@ -369,7 +406,9 @@ enum sdeb_opcode_index {
 	SDEB_I_SYNC_CACHE = 27,		/* 10, 16 */
 	SDEB_I_COMP_WRITE = 28,
 	SDEB_I_PRE_FETCH = 29,		/* 10, 16 */
-	SDEB_I_LAST_ELEM_P1 = 30,	/* keep this last (previous + 1) */
+	SDEB_I_ZONE_OUT = 30,		/* 0x94+SA; includes no data xfer */
+	SDEB_I_ZONE_IN = 31,		/* 0x95+SA; all have data-in */
+	SDEB_I_LAST_ELEM_P1 = 32,	/* keep this last (previous + 1) */
 };
 
 
@@ -401,7 +440,8 @@ static const unsigned char opcode_ind_arr[256] = {
 	0, 0, 0, 0, 0, SDEB_I_ATA_PT, 0, 0,
 	SDEB_I_READ, SDEB_I_COMP_WRITE, SDEB_I_WRITE, 0,
 	0, 0, 0, SDEB_I_VERIFY,
-	SDEB_I_PRE_FETCH, SDEB_I_SYNC_CACHE, 0, SDEB_I_WRITE_SAME, 0, 0, 0, 0,
+	SDEB_I_PRE_FETCH, SDEB_I_SYNC_CACHE, 0, SDEB_I_WRITE_SAME,
+	SDEB_I_ZONE_OUT, SDEB_I_ZONE_IN, 0, 0,
 	0, 0, 0, 0, 0, 0, SDEB_I_SERV_ACT_IN_16, SDEB_I_SERV_ACT_OUT_16,
 /* 0xa0; 0xa0->0xbf: 12 byte cdbs */
 	SDEB_I_REPORT_LUNS, SDEB_I_ATA_PT, 0, SDEB_I_MAINT_IN,
@@ -449,6 +489,11 @@ static int resp_comp_write(struct scsi_cmnd *, struct sdebug_dev_info *);
 static int resp_write_buffer(struct scsi_cmnd *, struct sdebug_dev_info *);
 static int resp_sync_cache(struct scsi_cmnd *, struct sdebug_dev_info *);
 static int resp_pre_fetch(struct scsi_cmnd *, struct sdebug_dev_info *);
+static int resp_report_zones(struct scsi_cmnd *, struct sdebug_dev_info *);
+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 sdebug_do_add_host(bool mk_new_store);
 static int sdebug_add_host_helper(int per_host_idx);
@@ -553,6 +598,24 @@ static const struct opcode_info_t pre_fetch_iarr[] = {
 	     0xff, 0xff, 0xff, 0xff, 0x3f, 0xc7} },	/* PRE-FETCH (16) */
 };
 
+static const struct opcode_info_t zone_out_iarr[] = {	/* ZONE OUT(16) */
+	{0, 0x94, 0x1, F_SA_LOW, resp_close_zone, NULL,
+	    {16, 0x1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	     0xff, 0, 0, 0xff, 0xff, 0x1, 0xc7} },	/* CLOSE ZONE */
+	{0, 0x94, 0x2, F_SA_LOW, resp_finish_zone, NULL,
+	    {16, 0x2, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	     0xff, 0, 0, 0xff, 0xff, 0x1, 0xc7} },	/* FINISH ZONE */
+	{0, 0x94, 0x4, F_SA_LOW, resp_rwp_zone, NULL,
+	    {16, 0x4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	     0xff, 0, 0, 0xff, 0xff, 0x1, 0xc7} },  /* RESET WRITE POINTER */
+};
+
+static const struct opcode_info_t zone_in_iarr[] = {	/* ZONE IN(16) */
+	{0, 0x95, 0x6, F_SA_LOW | F_D_IN, NULL, NULL,
+	    {16, 0x6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	     0xff, 0xff, 0xff, 0xff, 0x3f, 0xc7} }, /* REPORT ZONES */
+};
+
 
 /* This array is accessed via SDEB_I_* values. Make sure all are mapped,
  * plus the terminating elements for logic that scans this table such as
@@ -655,6 +718,15 @@ static const struct opcode_info_t opcode_info_arr[SDEB_I_LAST_ELEM_P1 + 1] = {
 	     0, 0, 0, 0} },			/* PRE-FETCH (10) */
 
 /* 30 */
+	{ARRAY_SIZE(zone_out_iarr), 0x94, 0x3, F_SA_LOW,
+	    resp_open_zone, zone_out_iarr, /* ZONE_OUT(16), OPEN ZONE) */
+		{16,  0x3 /* SA */, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x1, 0xc7} },
+	{ARRAY_SIZE(zone_in_iarr), 0x95, 0x0, F_SA_LOW | F_D_IN,
+	    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} },
+/* 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} },
 };
@@ -742,6 +814,11 @@ static int dix_writes;
 static int dix_reads;
 static int dif_errors;
 
+/* ZBC global data */
+static bool sdeb_zbc_in_use;		/* true when ptype=TYPE_ZBC [0x14] */
+static const int zbc_zone_size_mb;
+static const int zbc_max_open_zones = DEF_ZBC_MAX_OPEN_ZONES;
+
 static int submit_queues = DEF_SUBMIT_QUEUES;  /* > 1 for multi-queue (mq) */
 static struct sdebug_queue *sdebug_q_arr;  /* ptr to array of submit queues */
 
@@ -1446,20 +1523,22 @@ static int inquiry_vpd_b2(unsigned char *arr)
 }
 
 /* Zoned block device characteristics VPD page (ZBC mandatory) */
-static int inquiry_vpd_b6(unsigned char *arr)
+static int inquiry_vpd_b6(struct sdebug_dev_info *devip, unsigned char *arr)
 {
 	memset(arr, 0, 0x3c);
 	arr[0] = 0x1; /* set URSWRZ (unrestricted read in seq. wr req zone) */
 	/*
 	 * Set Optimal number of open sequential write preferred zones and
 	 * Optimal number of non-sequentially written sequential write
-	 * preferred zones and Maximum number of open sequential write
-	 * required zones fields to 'not reported' (0xffffffff). Leave other
-	 * fields set to zero.
+	 * preferred zones fields to 'not reported' (0xffffffff). Leave other
+	 * fields set to zero, apart from Max. number of open swrz_s field.
 	 */
 	put_unaligned_be32(0xffffffff, &arr[4]);
 	put_unaligned_be32(0xffffffff, &arr[8]);
-	put_unaligned_be32(0xffffffff, &arr[12]);
+	if (devip->max_open)
+		put_unaligned_be32(devip->max_open, &arr[12]);
+	else
+		put_unaligned_be32(0xffffffff, &arr[12]);
 	return 0x3c;
 }
 
@@ -1579,7 +1658,7 @@ static int resp_inquiry(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
 			arr[3] = inquiry_vpd_b2(&arr[4]);
 		} else if (is_zbc && cmd[2] == 0xb6) { /* ZB dev. charact. */
 			arr[1] = cmd[2];        /*sanity */
-			arr[3] = inquiry_vpd_b6(&arr[4]);
+			arr[3] = inquiry_vpd_b6(devip, &arr[4]);
 		} else {
 			mk_sense_invalid_fld(scp, SDEB_IN_CDB, 2, -1);
 			kfree(arr);
@@ -2550,9 +2629,185 @@ static int resp_log_sense(struct scsi_cmnd *scp,
 		    min_t(int, len, SDEBUG_MAX_INQ_ARR_SZ));
 }
 
-static inline int check_device_access_params(struct scsi_cmnd *scp,
-	unsigned long long lba, unsigned int num, bool write)
+static inline bool sdebug_dev_is_zoned(struct sdebug_dev_info *devip)
+{
+	return devip->nr_zones != 0;
+}
+
+static struct sdeb_zone_state *zbc_zone(struct sdebug_dev_info *devip,
+					unsigned long long lba)
+{
+	unsigned int zno;
+
+	if (devip->zsize_shift)
+		zno = lba >> devip->zsize_shift;
+	else
+		zno = lba / devip->zsize;
+	return &devip->zstate[zno];
+}
+
+static inline bool zbc_zone_is_conv(struct sdeb_zone_state *zsp)
+{
+	return zsp->z_cond == ZBC_NOT_WRITE_POINTER;
+}
+
+static void zbc_close_zone(struct sdebug_dev_info *devip,
+			   struct sdeb_zone_state *zsp)
+{
+	enum sdebug_z_cond zc;
+
+	if (zbc_zone_is_conv(zsp))
+		return;
+
+	zc = zsp->z_cond;
+	if (!(zc == ZC2_IMPLICIT_OPEN || zc == ZC3_EXPLICIT_OPEN))
+		return;
+
+	if (zc == ZC2_IMPLICIT_OPEN)
+		devip->nr_imp_open--;
+	else
+		devip->nr_exp_open--;
+
+	if (zsp->z_wp == zsp->z_start) {
+		zsp->z_cond = ZC1_EMPTY;
+	} else {
+		zsp->z_cond = ZC4_CLOSED;
+		devip->nr_closed++;
+	}
+}
+
+static void zbc_close_imp_open_zone(struct sdebug_dev_info *devip)
+{
+	struct sdeb_zone_state *zsp = &devip->zstate[0];
+	unsigned int i;
+
+	for (i = 0; i < devip->nr_zones; i++, zsp++) {
+		if (zsp->z_cond == ZC2_IMPLICIT_OPEN) {
+			zbc_close_zone(devip, zsp);
+			return;
+		}
+	}
+}
+
+static void zbc_open_zone(struct sdebug_dev_info *devip,
+			  struct sdeb_zone_state *zsp, bool explicit)
+{
+	enum sdebug_z_cond zc;
+
+	if (zbc_zone_is_conv(zsp))
+		return;
+
+	zc = zsp->z_cond;
+	if ((explicit && zc == ZC3_EXPLICIT_OPEN) ||
+	    (!explicit && zc == ZC2_IMPLICIT_OPEN))
+		return;
+
+	/* Close an implicit open zone if necessary */
+	if (explicit && zsp->z_cond == ZC2_IMPLICIT_OPEN)
+		zbc_close_zone(devip, zsp);
+	else if (devip->max_open &&
+		 devip->nr_imp_open + devip->nr_exp_open >= devip->max_open)
+		zbc_close_imp_open_zone(devip);
+
+	if (zsp->z_cond == ZC4_CLOSED)
+		devip->nr_closed--;
+	if (explicit) {
+		zsp->z_cond = ZC3_EXPLICIT_OPEN;
+		devip->nr_exp_open++;
+	} else {
+		zsp->z_cond = ZC2_IMPLICIT_OPEN;
+		devip->nr_imp_open++;
+	}
+}
+
+static void zbc_inc_wp(struct sdebug_dev_info *devip,
+		       unsigned long long lba, unsigned int num)
+{
+	struct sdeb_zone_state *zsp = zbc_zone(devip, lba);
+
+	if (zbc_zone_is_conv(zsp))
+		return;
+
+	zsp->z_wp += num;
+	if (zsp->z_wp >= zsp->z_start + zsp->z_size)
+		zsp->z_cond = ZC5_FULL;
+}
+
+static int check_zbc_access_params(struct scsi_cmnd *scp,
+			unsigned long long lba, unsigned int num, bool write)
+{
+	struct scsi_device *sdp = scp->device;
+	struct sdebug_dev_info *devip = (struct sdebug_dev_info *)sdp->hostdata;
+	struct sdeb_zone_state *zsp = zbc_zone(devip, lba);
+	struct sdeb_zone_state *zsp_end = zbc_zone(devip, lba + num - 1);
+
+	if (!write) {
+		/* Reads cannot cross zone types boundaries */
+		if (zsp_end != zsp &&
+		    zbc_zone_is_conv(zsp) &&
+		    !zbc_zone_is_conv(zsp_end)) {
+			mk_sense_buffer(scp, ILLEGAL_REQUEST,
+					LBA_OUT_OF_RANGE,
+					READ_INVDATA_ASCQ);
+			return check_condition_result;
+		}
+		return 0;
+	}
+
+	/* No restrictions for writes within conventional zones */
+	if (zbc_zone_is_conv(zsp)) {
+		if (!zbc_zone_is_conv(zsp_end)) {
+			mk_sense_buffer(scp, ILLEGAL_REQUEST,
+					LBA_OUT_OF_RANGE,
+					WRITE_BOUNDARY_ASCQ);
+			return check_condition_result;
+		}
+		return 0;
+	}
+
+	/* Writes cannot cross sequential zone boundaries */
+	if (zsp_end != zsp) {
+		mk_sense_buffer(scp, ILLEGAL_REQUEST,
+				LBA_OUT_OF_RANGE,
+				WRITE_BOUNDARY_ASCQ);
+		return check_condition_result;
+	}
+	/* Cannot write full zones */
+	if (zsp->z_cond == ZC5_FULL) {
+		mk_sense_buffer(scp, ILLEGAL_REQUEST,
+				INVALID_FIELD_IN_CDB, 0);
+		return check_condition_result;
+	}
+	/* Writes must be aligned to the zone WP */
+	if (lba != zsp->z_wp) {
+		mk_sense_buffer(scp, ILLEGAL_REQUEST,
+				LBA_OUT_OF_RANGE,
+				UNALIGNED_WRITE_ASCQ);
+		return check_condition_result;
+	}
+
+	/* Handle implicit open of closed and empty zones */
+	if (zsp->z_cond == ZC1_EMPTY || zsp->z_cond == ZC4_CLOSED) {
+		if (devip->max_open &&
+		    devip->nr_exp_open >= devip->max_open) {
+			mk_sense_buffer(scp, DATA_PROTECT,
+					INSUFF_RES_ASC,
+					INSUFF_ZONE_ASCQ);
+			return check_condition_result;
+		}
+		zbc_open_zone(devip, zsp, false);
+	}
+
+	return 0;
+}
+
+static inline int check_device_access_params
+			(struct scsi_cmnd *scp, unsigned long long lba,
+			 unsigned int num, bool write)
 {
+	struct scsi_device *sdp = scp->device;
+	struct sdebug_dev_info *devip = (struct sdebug_dev_info *)sdp->hostdata;
+
 	if (lba + num > sdebug_capacity) {
 		mk_sense_buffer(scp, ILLEGAL_REQUEST, LBA_OUT_OF_RANGE, 0);
 		return check_condition_result;
@@ -2567,6 +2822,9 @@ static inline int check_device_access_params(struct scsi_cmnd *scp,
 		mk_sense_buffer(scp, DATA_PROTECT, WRITE_PROTECTED, 0x2);
 		return check_condition_result;
 	}
+	if (sdebug_dev_is_zoned(devip))
+		return check_zbc_access_params(scp, lba, num, write);
+
 	return 0;
 }
 
@@ -3153,10 +3411,13 @@ static int resp_write_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
 			sdev_printk(KERN_ERR, scp->device, "Unprotected WR "
 				    "to DIF device\n");
 	}
+
+	write_lock(macc_lckp);
 	ret = check_device_access_params(scp, lba, num, true);
-	if (ret)
+	if (ret) {
+		write_unlock(macc_lckp);
 		return ret;
-	write_lock(macc_lckp);
+	}
 
 	/* DIX + T10 DIF */
 	if (unlikely(sdebug_dix && scsi_prot_sg_count(scp))) {
@@ -3172,6 +3433,9 @@ static int resp_write_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
 	ret = do_device_access(sip, scp, 0, lba, num, true);
 	if (unlikely(scsi_debug_lbp()))
 		map_region(sip, lba, num);
+	/* If ZBC zone then bump its write pointer */
+	if (sdebug_dev_is_zoned(devip))
+		zbc_inc_wp(devip, lba, num);
 	write_unlock(macc_lckp);
 	if (unlikely(-1 == ret))
 		return DID_ERROR << 16;
@@ -3326,6 +3590,9 @@ static int resp_write_scat(struct scsi_cmnd *scp,
 		}
 
 		ret = do_device_access(sip, scp, sg_off, lba, num, true);
+		/* If ZBC zone then bump its write pointer */
+		if (sdebug_dev_is_zoned(devip))
+			zbc_inc_wp(devip, lba, num);
 		if (unlikely(scsi_debug_lbp()))
 			map_region(sip, lba, num);
 		if (unlikely(-1 == ret)) {
@@ -3374,6 +3641,8 @@ static int resp_write_scat(struct scsi_cmnd *scp,
 static int resp_write_same(struct scsi_cmnd *scp, u64 lba, u32 num,
 			   u32 ei_lba, bool unmap, bool ndob)
 {
+	struct scsi_device *sdp = scp->device;
+	struct sdebug_dev_info *devip = (struct sdebug_dev_info *)sdp->hostdata;
 	unsigned long long i;
 	u64 block, lbaa;
 	u32 lb_size = sdebug_sector_size;
@@ -3384,11 +3653,13 @@ static int resp_write_same(struct scsi_cmnd *scp, u64 lba, u32 num,
 	u8 *fs1p;
 	u8 *fsp;
 
+	write_lock(macc_lckp);
+
 	ret = check_device_access_params(scp, lba, num, true);
-	if (ret)
+	if (ret) {
+		write_unlock(macc_lckp);
 		return ret;
-
-	write_lock(macc_lckp);
+	}
 
 	if (unmap && scsi_debug_lbp()) {
 		unmap_region(sip, lba, num);
@@ -3421,6 +3692,9 @@ static int resp_write_same(struct scsi_cmnd *scp, u64 lba, u32 num,
 	}
 	if (scsi_debug_lbp())
 		map_region(sip, lba, num);
+	/* If ZBC zone then bump its write pointer */
+	if (sdebug_dev_is_zoned(devip))
+		zbc_inc_wp(devip, lba, num);
 out:
 	write_unlock(macc_lckp);
 
@@ -3948,6 +4222,426 @@ static int resp_verify(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
 	return ret;
 }
 
+#define RZONES_DESC_HD 64
+
+/* Report zones depending on start LBA nad reporting options */
+static int resp_report_zones(struct scsi_cmnd *scp,
+			     struct sdebug_dev_info *devip)
+{
+	unsigned int i, max_zones, rep_max_zones, nrz = 0;
+	int ret = 0;
+	u32 alloc_len, rep_opts, rep_len;
+	bool partial;
+	u64 lba, zs_lba;
+	u8 *arr = NULL, *desc;
+	u8 *cmd = scp->cmnd;
+	struct sdeb_zone_state *zsp;
+	struct sdeb_store_info *sip = devip2sip(devip);
+	rwlock_t *macc_lckp = sip ? &sip->macc_lck : &sdeb_fake_rw_lck;
+
+	if (!sdebug_dev_is_zoned(devip)) {
+		mk_sense_invalid_opcode(scp);
+		return check_condition_result;
+	}
+	zs_lba = get_unaligned_be64(cmd + 2);
+	alloc_len = get_unaligned_be32(cmd + 10);
+	rep_opts = cmd[14] & 0x3f;
+	partial = cmd[14] & 0x80;
+
+	if (zs_lba >= sdebug_capacity) {
+		mk_sense_buffer(scp, ILLEGAL_REQUEST, LBA_OUT_OF_RANGE, 0);
+		return check_condition_result;
+	}
+
+	max_zones = devip->nr_zones - zs_lba / devip->zsize;
+	rep_max_zones = min((alloc_len - 64) >> ilog2(RZONES_DESC_HD),
+			    max_zones);
+
+	arr = kcalloc(RZONES_DESC_HD, alloc_len, GFP_ATOMIC);
+	if (!arr) {
+		mk_sense_buffer(scp, ILLEGAL_REQUEST, INSUFF_RES_ASC,
+				INSUFF_RES_ASCQ);
+		return check_condition_result;
+	}
+
+	read_lock(macc_lckp);
+
+	desc = arr + 64;
+	for (i = 0; i < max_zones; i++) {
+		lba = zs_lba + devip->zsize * i;
+		if (lba > sdebug_capacity)
+			break;
+		zsp = zbc_zone(devip, lba);
+		switch (rep_opts) {
+		case 0x00:
+			/* All zones */
+			break;
+		case 0x01:
+			/* Empty zones */
+			if (zsp->z_cond != ZC1_EMPTY)
+				continue;
+			break;
+		case 0x02:
+			/* Implicit open zones */
+			if (zsp->z_cond != ZC2_IMPLICIT_OPEN)
+				continue;
+			break;
+		case 0x03:
+			/* Explicit open zones */
+			if (zsp->z_cond != ZC3_EXPLICIT_OPEN)
+				continue;
+			break;
+		case 0x04:
+			/* Closed zones */
+			if (zsp->z_cond != ZC4_CLOSED)
+				continue;
+			break;
+		case 0x05:
+			/* Full zones */
+			if (zsp->z_cond != ZC5_FULL)
+				continue;
+			break;
+		case 0x06:
+		case 0x07:
+		case 0x10:
+		case 0x11:
+			/*
+			 * Read-only, offline, reset WP recommended and
+			 * non-seq-resource-used are not emulated: no zones
+			 * to report;
+			 */
+			continue;
+		case 0x3f:
+			/* Not write pointer (conventional) zones */
+			if (!zbc_zone_is_conv(zsp))
+				continue;
+			break;
+		default:
+			mk_sense_buffer(scp, ILLEGAL_REQUEST,
+					INVALID_FIELD_IN_CDB, 0);
+			ret = check_condition_result;
+			goto fini;
+		}
+
+		if (nrz < rep_max_zones) {
+			/* Fill zone descriptor */
+			if (zbc_zone_is_conv(zsp))
+				desc[0] = 0x1;
+			else
+				desc[0] = 0x2;
+			desc[1] = zsp->z_cond << 4;
+			put_unaligned_be64((u64)zsp->z_size, desc + 8);
+			put_unaligned_be64((u64)zsp->z_start, desc + 16);
+			put_unaligned_be64((u64)zsp->z_wp, desc + 24);
+			desc += 64;
+		}
+
+		if (partial && nrz >= rep_max_zones)
+			break;
+
+		nrz++;
+	}
+
+	/* Report header */
+	put_unaligned_be32(nrz * RZONES_DESC_HD, arr + 0);
+	put_unaligned_be64(sdebug_capacity - 1, arr + 8);
+
+	rep_len = (unsigned long)desc - (unsigned long)arr;
+	ret = fill_from_dev_buffer(scp, arr, min_t(int, alloc_len, rep_len));
+
+fini:
+	read_unlock(macc_lckp);
+	kfree(arr);
+	return ret;
+}
+
+/* Logic transplanted from tcmu-runner, file_zbc.c */
+static void zbc_open_all(struct sdebug_dev_info *devip)
+{
+	struct sdeb_zone_state *zsp = &devip->zstate[0];
+	unsigned int i;
+
+	for (i = 0; i < devip->nr_zones; i++, zsp++) {
+		if (zsp->z_cond == ZC4_CLOSED)
+			zbc_open_zone(devip, &devip->zstate[i], true);
+	}
+}
+
+static int resp_open_zone(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
+{
+	int res = 0;
+	u64 z_id;
+	enum sdebug_z_cond zc;
+	u8 *cmd = scp->cmnd;
+	struct sdeb_zone_state *zsp;
+	bool all = cmd[14] & 0x01;
+	struct sdeb_store_info *sip = devip2sip(devip);
+	rwlock_t *macc_lckp = sip ? &sip->macc_lck : &sdeb_fake_rw_lck;
+
+	if (!sdebug_dev_is_zoned(devip)) {
+		mk_sense_invalid_opcode(scp);
+		return check_condition_result;
+	}
+
+	write_lock(macc_lckp);
+
+	if (all) {
+		/* Check if all closed zones can be open */
+		if (devip->max_open &&
+		    devip->nr_exp_open + devip->nr_closed > devip->max_open) {
+			mk_sense_buffer(scp, DATA_PROTECT, INSUFF_RES_ASC,
+					INSUFF_ZONE_ASCQ);
+			res = check_condition_result;
+			goto fini;
+		}
+		/* Open all closed zones */
+		zbc_open_all(devip);
+		goto fini;
+	}
+
+	/* Open the specified zone */
+	z_id = get_unaligned_be64(cmd + 2);
+	if (z_id >= sdebug_capacity) {
+		mk_sense_buffer(scp, ILLEGAL_REQUEST, LBA_OUT_OF_RANGE, 0);
+		res = check_condition_result;
+		goto fini;
+	}
+
+	zsp = zbc_zone(devip, z_id);
+	if (z_id != zsp->z_start) {
+		mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0);
+		res = check_condition_result;
+		goto fini;
+	}
+	if (zbc_zone_is_conv(zsp)) {
+		mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0);
+		res = check_condition_result;
+		goto fini;
+	}
+
+	zc = zsp->z_cond;
+	if (zc == ZC3_EXPLICIT_OPEN || zc == ZC5_FULL)
+		goto fini;
+
+	if (devip->max_open && devip->nr_exp_open >= devip->max_open) {
+		mk_sense_buffer(scp, DATA_PROTECT, INSUFF_RES_ASC,
+				INSUFF_ZONE_ASCQ);
+		res = check_condition_result;
+		goto fini;
+	}
+
+	if (zc == ZC2_IMPLICIT_OPEN)
+		zbc_close_zone(devip, zsp);
+	zbc_open_zone(devip, zsp, true);
+fini:
+	write_unlock(macc_lckp);
+	return res;
+}
+
+static void zbc_close_all(struct sdebug_dev_info *devip)
+{
+	unsigned int i;
+
+	for (i = 0; i < devip->nr_zones; i++)
+		zbc_close_zone(devip, &devip->zstate[i]);
+}
+
+static int resp_close_zone(struct scsi_cmnd *scp,
+			   struct sdebug_dev_info *devip)
+{
+	int res = 0;
+	u64 z_id;
+	u8 *cmd = scp->cmnd;
+	struct sdeb_zone_state *zsp;
+	bool all = cmd[14] & 0x01;
+	struct sdeb_store_info *sip = devip2sip(devip);
+	rwlock_t *macc_lckp = sip ? &sip->macc_lck : &sdeb_fake_rw_lck;
+
+	if (!sdebug_dev_is_zoned(devip)) {
+		mk_sense_invalid_opcode(scp);
+		return check_condition_result;
+	}
+
+	write_lock(macc_lckp);
+
+	if (all) {
+		zbc_close_all(devip);
+		goto fini;
+	}
+
+	/* Close specified zone */
+	z_id = get_unaligned_be64(cmd + 2);
+	if (z_id >= sdebug_capacity) {
+		mk_sense_buffer(scp, ILLEGAL_REQUEST, LBA_OUT_OF_RANGE, 0);
+		res = check_condition_result;
+		goto fini;
+	}
+
+	zsp = zbc_zone(devip, z_id);
+	if (z_id != zsp->z_start) {
+		mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0);
+		res = check_condition_result;
+		goto fini;
+	}
+	if (zbc_zone_is_conv(zsp)) {
+		mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0);
+		res = check_condition_result;
+		goto fini;
+	}
+
+	zbc_close_zone(devip, zsp);
+fini:
+	write_unlock(macc_lckp);
+	return res;
+}
+
+static void zbc_finish_zone(struct sdebug_dev_info *devip,
+			    struct sdeb_zone_state *zsp, bool empty)
+{
+	enum sdebug_z_cond zc = zsp->z_cond;
+
+	if (zc == ZC4_CLOSED || zc == ZC2_IMPLICIT_OPEN ||
+	    zc == ZC3_EXPLICIT_OPEN || (empty && zc == ZC1_EMPTY)) {
+		if (zc == ZC2_IMPLICIT_OPEN || zc == ZC3_EXPLICIT_OPEN)
+			zbc_close_zone(devip, zsp);
+		if (zsp->z_cond == ZC4_CLOSED)
+			devip->nr_closed--;
+		zsp->z_wp = zsp->z_start + zsp->z_size;
+		zsp->z_cond = ZC5_FULL;
+	}
+}
+
+static void zbc_finish_all(struct sdebug_dev_info *devip)
+{
+	unsigned int i;
+
+	for (i = 0; i < devip->nr_zones; i++)
+		zbc_finish_zone(devip, &devip->zstate[i], false);
+}
+
+static int resp_finish_zone(struct scsi_cmnd *scp,
+			    struct sdebug_dev_info *devip)
+{
+	struct sdeb_zone_state *zsp;
+	int res = 0;
+	u64 z_id;
+	u8 *cmd = scp->cmnd;
+	bool all = cmd[14] & 0x01;
+	struct sdeb_store_info *sip = devip2sip(devip);
+	rwlock_t *macc_lckp = sip ? &sip->macc_lck : &sdeb_fake_rw_lck;
+
+	if (!sdebug_dev_is_zoned(devip)) {
+		mk_sense_invalid_opcode(scp);
+		return check_condition_result;
+	}
+
+	write_lock(macc_lckp);
+
+	if (all) {
+		zbc_finish_all(devip);
+		goto fini;
+	}
+
+	/* Finish the specified zone */
+	z_id = get_unaligned_be64(cmd + 2);
+	if (z_id >= sdebug_capacity) {
+		mk_sense_buffer(scp, ILLEGAL_REQUEST, LBA_OUT_OF_RANGE, 0);
+		res = check_condition_result;
+		goto fini;
+	}
+
+	zsp = zbc_zone(devip, z_id);
+	if (z_id != zsp->z_start) {
+		mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0);
+		res = check_condition_result;
+		goto fini;
+	}
+	if (zbc_zone_is_conv(zsp)) {
+		mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0);
+		res = check_condition_result;
+		goto fini;
+	}
+
+	zbc_finish_zone(devip, zsp, true);
+fini:
+	write_unlock(macc_lckp);
+	return res;
+}
+
+static void zbc_rwp_zone(struct sdebug_dev_info *devip,
+			 struct sdeb_zone_state *zsp)
+{
+	enum sdebug_z_cond zc;
+
+	if (zbc_zone_is_conv(zsp))
+		return;
+
+	zc = zsp->z_cond;
+	if (zc == ZC2_IMPLICIT_OPEN || zc == ZC3_EXPLICIT_OPEN)
+		zbc_close_zone(devip, zsp);
+
+	if (zsp->z_cond == ZC4_CLOSED)
+		devip->nr_closed--;
+
+	zsp->z_wp = zsp->z_start;
+	zsp->z_cond = ZC1_EMPTY;
+}
+
+static void zbc_rwp_all(struct sdebug_dev_info *devip)
+{
+	unsigned int i;
+
+	for (i = 0; i < devip->nr_zones; i++)
+		zbc_rwp_zone(devip, &devip->zstate[i]);
+}
+
+static int resp_rwp_zone(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
+{
+	struct sdeb_zone_state *zsp;
+	int res = 0;
+	u64 z_id;
+	u8 *cmd = scp->cmnd;
+	bool all = cmd[14] & 0x01;
+	struct sdeb_store_info *sip = devip2sip(devip);
+	rwlock_t *macc_lckp = sip ? &sip->macc_lck : &sdeb_fake_rw_lck;
+
+	if (!sdebug_dev_is_zoned(devip)) {
+		mk_sense_invalid_opcode(scp);
+		return check_condition_result;
+	}
+
+	write_lock(macc_lckp);
+
+	if (all) {
+		zbc_rwp_all(devip);
+		goto fini;
+	}
+
+	z_id = get_unaligned_be64(cmd + 2);
+	if (z_id >= sdebug_capacity) {
+		mk_sense_buffer(scp, ILLEGAL_REQUEST, LBA_OUT_OF_RANGE, 0);
+		res = check_condition_result;
+		goto fini;
+	}
+
+	zsp = zbc_zone(devip, z_id);
+	if (z_id != zsp->z_start) {
+		mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0);
+		res = check_condition_result;
+		goto fini;
+	}
+	if (zbc_zone_is_conv(zsp)) {
+		mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0);
+		res = check_condition_result;
+		goto fini;
+	}
+
+	zbc_rwp_zone(devip, zsp);
+fini:
+	write_unlock(macc_lckp);
+	return res;
+}
+
 static struct sdebug_queue *get_queue(struct scsi_cmnd *cmnd)
 {
 	u32 tag = blk_mq_unique_tag(cmnd->request);
@@ -4053,6 +4747,76 @@ static void sdebug_q_cmd_wq_complete(struct work_struct *work)
 static bool got_shared_uuid;
 static uuid_t shared_uuid;
 
+static int sdebug_device_create_zones(struct sdebug_dev_info *devip)
+{
+	struct sdeb_zone_state *zsp;
+	sector_t capacity = get_sdebug_capacity();
+	sector_t zstart = 0;
+	unsigned int i;
+
+	/*
+	 * Set the zone size: if zbc_zone_size_mb is not set, figure out a
+	 * zone size allowing for at least 4 zones on the device. Otherwise,
+	 * use the specified zone size checking that at least 2 zones can be
+	 * created for the device.
+	 */
+	if (!zbc_zone_size_mb) {
+		devip->zsize = (DEF_ZBC_ZONE_SIZE_MB * SZ_1M)
+			>> ilog2(sdebug_sector_size);
+		while (capacity < devip->zsize << 2 && devip->zsize >= 2)
+			devip->zsize >>= 1;
+		if (devip->zsize < 2) {
+			pr_err("Device capacity too small\n");
+			return -EINVAL;
+		}
+	} else {
+		devip->zsize = (zbc_zone_size_mb * SZ_1M)
+			>> ilog2(sdebug_sector_size);
+		if (devip->zsize >= capacity) {
+			pr_err("Zone size too large for device capacity\n");
+			return -EINVAL;
+		}
+	}
+
+	if (is_power_of_2(devip->zsize))
+		devip->zsize_shift = ilog2(devip->zsize);
+	devip->nr_zones = (capacity + devip->zsize - 1) >> devip->zsize_shift;
+
+	/* zbc_max_open_zones can be 0, meaning "not reported" (no limit) */
+	if (zbc_max_open_zones >= devip->nr_zones - 1)
+		devip->max_open = (devip->nr_zones - 1) / 2;
+	else
+		devip->max_open = zbc_max_open_zones;
+
+	devip->zstate = kcalloc(devip->nr_zones,
+				sizeof(struct sdeb_zone_state), GFP_KERNEL);
+	if (!devip->zstate)
+		return -ENOMEM;
+
+	for (i = 0; i < devip->nr_zones; i++) {
+		zsp = &devip->zstate[i];
+
+		zsp->z_start = zstart;
+
+		if (i == 0) {
+			zsp->z_cond = ZBC_NOT_WRITE_POINTER;
+			zsp->z_wp = (sector_t)-1;
+		} else {
+			zsp->z_cond = ZC1_EMPTY;
+			zsp->z_wp = zsp->z_start;
+		}
+
+		if (zsp->z_start + devip->zsize < capacity)
+			zsp->z_size = devip->zsize;
+		else
+			zsp->z_size = capacity - zsp->z_start;
+
+		zstart += zsp->z_size;
+	}
+
+	return 0;
+}
+
 static struct sdebug_dev_info *sdebug_device_create(
 			struct sdebug_host_info *sdbg_host, gfp_t flags)
 {
@@ -4072,6 +4836,13 @@ static struct sdebug_dev_info *sdebug_device_create(
 			}
 		}
 		devip->sdbg_host = sdbg_host;
+		if (sdeb_zbc_in_use) {
+			if (sdebug_device_create_zones(devip)) {
+				kfree(devip);
+				return NULL;
+			}
+		}
+		devip->sdbg_host = sdbg_host;
 		list_add_tail(&devip->dev_list, &sdbg_host->dev_info_list);
 	}
 	return devip;
@@ -5059,7 +5830,13 @@ static ssize_t ptype_store(struct device_driver *ddp, const char *buf,
 {
 	int n;
 
+	/* Cannot change from or to TYPE_ZBC with sysfs */
+	if (sdebug_ptype == TYPE_ZBC)
+		return -EINVAL;
+
 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
+		if (n == TYPE_ZBC)
+			return -EINVAL;
 		sdebug_ptype = n;
 		return count;
 	}
@@ -5318,6 +6095,10 @@ static ssize_t virtual_gb_store(struct device_driver *ddp, const char *buf,
 	int n;
 	bool changed;
 
+	/* Ignore capacity change for ZBC drives for now */
+	if (sdeb_zbc_in_use)
+		return -ENOTSUPP;
+
 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
 		changed = (sdebug_virtual_gb != n);
 		sdebug_virtual_gb = n;
@@ -5708,6 +6489,12 @@ static int __init scsi_debug_init(void)
 	for (k = 0; k < submit_queues; ++k)
 		spin_lock_init(&sdebug_q_arr[k].qc_lock);
 
+	/*
+	 * check for host managed zoned block device specified with ptype=0x14.
+	 */
+	if (sdebug_ptype == TYPE_ZBC)
+		sdeb_zbc_in_use = true;
+
 	if (sdebug_dev_size_mb < 1)
 		sdebug_dev_size_mb = 1;  /* force minimum 1 MB ramdisk */
 	sz = (unsigned long)sdebug_dev_size_mb * 1048576;
@@ -5999,6 +6786,7 @@ static int sdebug_add_host_helper(int per_host_idx)
 	list_for_each_entry_safe(sdbg_devinfo, tmp, &sdbg_host->dev_info_list,
 				 dev_list) {
 		list_del(&sdbg_devinfo->dev_list);
+		kfree(sdbg_devinfo->zstate);
 		kfree(sdbg_devinfo);
 	}
 	kfree(sdbg_host);
@@ -6410,6 +7198,7 @@ static int sdebug_driver_remove(struct device *dev)
 	list_for_each_entry_safe(sdbg_devinfo, tmp, &sdbg_host->dev_info_list,
 				 dev_list) {
 		list_del(&sdbg_devinfo->dev_list);
+		kfree(sdbg_devinfo->zstate);
 		kfree(sdbg_devinfo);
 	}
 
-- 
2.25.3


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

* [PATCH 3/7] scsi_debug: add zbc module parameter
  2020-04-22 10:42 [PATCH 0/7] scsi_debug: Add ZBC support Damien Le Moal
  2020-04-22 10:42 ` [PATCH 1/7] scsi_debug: add zbc mode and VPD pages Damien Le Moal
  2020-04-22 10:42 ` [PATCH 2/7] scsi_debug: add zbc zone commands Damien Le Moal
@ 2020-04-22 10:42 ` Damien Le Moal
  2020-04-22 10:42 ` [PATCH 4/7] scsi_debug: add zone_max_open " Damien Le Moal
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 12+ messages in thread
From: Damien Le Moal @ 2020-04-22 10:42 UTC (permalink / raw)
  To: linux-scsi, Martin K . Petersen; +Cc: Douglas Gilbert

From: Douglas Gilbert <dgilbert@interlog.com>

Add the zbc module parameter to take either:
    0: none         (probably a conventional disk)
    1: host-aware
    2: host-managed

These values are chosen to match 'enum blk_zoned_model' found in
include/linux/blkdev.h . Instead of "none", "no" or "0" can be given.
Instead of "host-aware", "aware or "1" can be given. Instead of
"host-managed", "managed" or "2" can be given.

Note: the zbc parameter can only be given at driver/module load
time; it cannot be changed via sysfs thereafter.

At this time there is no ZBC "host-aware" implementation so that
string (or the value '1') results in a modprobe error.

Signed-off-by: Douglas Gilbert <dgilbert@interlog.com>
Signed-off-by: Damien Le Moal <damien.lemoal@wdc.com>
---
 drivers/scsi/scsi_debug.c | 81 +++++++++++++++++++++++++++++++++++++--
 1 file changed, 78 insertions(+), 3 deletions(-)

diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index 10214017d478..6ef01b97e1ae 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -113,7 +113,9 @@ static const char *sdebug_version_date = "20200421";
 #define DEF_ATO 1
 #define DEF_CDB_LEN 10
 #define DEF_JDELAY   1		/* if > 0 unit is a jiffy */
+#define DEF_DEV_SIZE_PRE_INIT   0
 #define DEF_DEV_SIZE_MB   8
+#define DEF_ZBC_DEV_SIZE_MB   128
 #define DEF_DIF 0
 #define DEF_DIX 0
 #define DEF_PER_HOST_STORE false
@@ -736,7 +738,7 @@ static int sdebug_add_host = DEF_NUM_HOST;  /* in sysfs this is relative */
 static int sdebug_ato = DEF_ATO;
 static int sdebug_cdb_len = DEF_CDB_LEN;
 static int sdebug_jdelay = DEF_JDELAY;	/* if > 0 then unit is jiffies */
-static int sdebug_dev_size_mb = DEF_DEV_SIZE_MB;
+static int sdebug_dev_size_mb = DEF_DEV_SIZE_PRE_INIT;
 static int sdebug_dif = DEF_DIF;
 static int sdebug_dix = DEF_DIX;
 static int sdebug_dsense = DEF_D_SENSE;
@@ -785,6 +787,9 @@ static bool have_dif_prot;
 static bool write_since_sync;
 static bool sdebug_statistics = DEF_STATISTICS;
 static bool sdebug_wp;
+/* Following enum: 0: no zbc, def; 1: host aware; 2: host managed */
+static enum blk_zoned_model sdeb_zbc_model = BLK_ZONED_NONE;
+static char *sdeb_zbc_model_s;
 
 static unsigned int sdebug_store_sectors;
 static sector_t sdebug_capacity;	/* in sectors */
@@ -5534,6 +5539,7 @@ module_param_named(vpd_use_hostno, sdebug_vpd_use_hostno, int,
 module_param_named(wp, sdebug_wp, bool, S_IRUGO | S_IWUSR);
 module_param_named(write_same_length, sdebug_write_same_length, int,
 		   S_IRUGO | S_IWUSR);
+module_param_named(zbc, sdeb_zbc_model_s, charp, S_IRUGO);
 
 MODULE_AUTHOR("Eric Youngdale + Douglas Gilbert");
 MODULE_DESCRIPTION("SCSI debug adapter driver");
@@ -5595,6 +5601,7 @@ MODULE_PARM_DESC(virtual_gb, "virtual gigabyte (GiB) size (def=0 -> use dev_size
 MODULE_PARM_DESC(vpd_use_hostno, "0 -> dev ids ignore hostno (def=1 -> unique dev ids)");
 MODULE_PARM_DESC(wp, "Write Protect (def=0)");
 MODULE_PARM_DESC(write_same_length, "Maximum blocks per WRITE SAME cmd (def=0xffff)");
+MODULE_PARM_DESC(zbc, "'none' [0]; 'aware' [1]; 'managed' [2] (def=0). Can have 'host-' prefix");
 
 #define SDEBUG_INFO_LEN 256
 static char sdebug_info[SDEBUG_INFO_LEN];
@@ -6359,6 +6366,45 @@ static ssize_t cdb_len_store(struct device_driver *ddp, const char *buf,
 }
 static DRIVER_ATTR_RW(cdb_len);
 
+static const char * const zbc_model_strs_a[] = {
+	[BLK_ZONED_NONE] = "none",
+	[BLK_ZONED_HA]   = "host-aware",
+	[BLK_ZONED_HM]   = "host-managed",
+};
+
+static const char * const zbc_model_strs_b[] = {
+	[BLK_ZONED_NONE] = "no",
+	[BLK_ZONED_HA]   = "aware",
+	[BLK_ZONED_HM]   = "managed",
+};
+
+static const char * const zbc_model_strs_c[] = {
+	[BLK_ZONED_NONE] = "0",
+	[BLK_ZONED_HA]   = "1",
+	[BLK_ZONED_HM]   = "2",
+};
+
+static int sdeb_zbc_model_str(const char *cp)
+{
+	int res = sysfs_match_string(zbc_model_strs_a, cp);
+
+	if (res < 0) {
+		res = sysfs_match_string(zbc_model_strs_b, cp);
+		if (res < 0) {
+			res = sysfs_match_string(zbc_model_strs_c, cp);
+			if (sdeb_zbc_model < 0)
+				return -EINVAL;
+		}
+	}
+	return res;
+}
+
+static ssize_t zbc_show(struct device_driver *ddp, char *buf)
+{
+	return scnprintf(buf, PAGE_SIZE, "%s\n",
+			 zbc_model_strs_a[sdeb_zbc_model]);
+}
+static DRIVER_ATTR_RO(zbc);
 
 /* Note: The following array creates attribute files in the
    /sys/bus/pseudo/drivers/scsi_debug directory. The advantage of these
@@ -6401,6 +6447,7 @@ static struct attribute *sdebug_drv_attrs[] = {
 	&driver_attr_strict.attr,
 	&driver_attr_uuid_ctl.attr,
 	&driver_attr_cdb_len.attr,
+	&driver_attr_zbc.attr,
 	NULL,
 };
 ATTRIBUTE_GROUPS(sdebug_drv);
@@ -6490,11 +6537,39 @@ static int __init scsi_debug_init(void)
 		spin_lock_init(&sdebug_q_arr[k].qc_lock);
 
 	/*
-	 * check for host managed zoned block device specified with ptype=0x14.
+	 * check for host managed zoned block device specified with
+	 * ptype=0x14 or zbc=XXX.
 	 */
-	if (sdebug_ptype == TYPE_ZBC)
+	if (sdebug_ptype == TYPE_ZBC) {
+		sdeb_zbc_model = BLK_ZONED_HM;
+	} else if (sdeb_zbc_model_s && *sdeb_zbc_model_s) {
+		k = sdeb_zbc_model_str(sdeb_zbc_model_s);
+		if (k < 0) {
+			ret = k;
+			goto free_vm;
+		}
+		sdeb_zbc_model = k;
+		switch (sdeb_zbc_model) {
+		case BLK_ZONED_NONE:
+			sdebug_ptype = TYPE_DISK;
+			break;
+		case BLK_ZONED_HM:
+			sdebug_ptype = TYPE_ZBC;
+			break;
+		case BLK_ZONED_HA:
+		default:
+			pr_err("Invalid ZBC model\n");
+			return -EINVAL;
+		}
+	}
+	if (sdeb_zbc_model != BLK_ZONED_NONE) {
 		sdeb_zbc_in_use = true;
+		if (sdebug_dev_size_mb == DEF_DEV_SIZE_PRE_INIT)
+			sdebug_dev_size_mb = DEF_ZBC_DEV_SIZE_MB;
+	}
 
+	if (sdebug_dev_size_mb == DEF_DEV_SIZE_PRE_INIT)
+		sdebug_dev_size_mb = DEF_DEV_SIZE_MB;
 	if (sdebug_dev_size_mb < 1)
 		sdebug_dev_size_mb = 1;  /* force minimum 1 MB ramdisk */
 	sz = (unsigned long)sdebug_dev_size_mb * 1048576;
-- 
2.25.3


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

* [PATCH 4/7] scsi_debug: add zone_max_open module parameter
  2020-04-22 10:42 [PATCH 0/7] scsi_debug: Add ZBC support Damien Le Moal
                   ` (2 preceding siblings ...)
  2020-04-22 10:42 ` [PATCH 3/7] scsi_debug: add zbc module parameter Damien Le Moal
@ 2020-04-22 10:42 ` Damien Le Moal
  2020-04-22 10:42 ` [PATCH 5/7] scsi_debug: add zone_nr_conv " Damien Le Moal
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 12+ messages in thread
From: Damien Le Moal @ 2020-04-22 10:42 UTC (permalink / raw)
  To: linux-scsi, Martin K . Petersen; +Cc: Douglas Gilbert

Add the zone_max_open module parameters to control the maximum number
of open zones of a ZBC device. This parameter is ignored for device
types other than 0x14 (zbc=2 case).

Signed-off-by: Douglas Gilbert <dgilbert@interlog.com>
Signed-off-by: Damien Le Moal <damien.lemoal@wdc.com>
---
 drivers/scsi/scsi_debug.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index 6ef01b97e1ae..36aeda10a117 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -822,7 +822,7 @@ static int dif_errors;
 /* ZBC global data */
 static bool sdeb_zbc_in_use;		/* true when ptype=TYPE_ZBC [0x14] */
 static const int zbc_zone_size_mb;
-static const int zbc_max_open_zones = DEF_ZBC_MAX_OPEN_ZONES;
+static int sdeb_zbc_max_open = DEF_ZBC_MAX_OPEN_ZONES;
 
 static int submit_queues = DEF_SUBMIT_QUEUES;  /* > 1 for multi-queue (mq) */
 static struct sdebug_queue *sdebug_q_arr;  /* ptr to array of submit queues */
@@ -4788,10 +4788,10 @@ static int sdebug_device_create_zones(struct sdebug_dev_info *devip)
 	devip->nr_zones = (capacity + devip->zsize - 1) >> devip->zsize_shift;
 
 	/* zbc_max_open_zones can be 0, meaning "not reported" (no limit) */
-	if (zbc_max_open_zones >= devip->nr_zones - 1)
+	if (sdeb_zbc_max_open >= devip->nr_zones - 1)
 		devip->max_open = (devip->nr_zones - 1) / 2;
 	else
-		devip->max_open = zbc_max_open_zones;
+		devip->max_open = sdeb_zbc_max_open;
 
 	devip->zstate = kcalloc(devip->nr_zones,
 				sizeof(struct sdeb_zone_state), GFP_KERNEL);
@@ -5540,6 +5540,7 @@ module_param_named(wp, sdebug_wp, bool, S_IRUGO | S_IWUSR);
 module_param_named(write_same_length, sdebug_write_same_length, int,
 		   S_IRUGO | S_IWUSR);
 module_param_named(zbc, sdeb_zbc_model_s, charp, S_IRUGO);
+module_param_named(zone_max_open, sdeb_zbc_max_open, int, S_IRUGO);
 
 MODULE_AUTHOR("Eric Youngdale + Douglas Gilbert");
 MODULE_DESCRIPTION("SCSI debug adapter driver");
@@ -5602,6 +5603,7 @@ MODULE_PARM_DESC(vpd_use_hostno, "0 -> dev ids ignore hostno (def=1 -> unique de
 MODULE_PARM_DESC(wp, "Write Protect (def=0)");
 MODULE_PARM_DESC(write_same_length, "Maximum blocks per WRITE SAME cmd (def=0xffff)");
 MODULE_PARM_DESC(zbc, "'none' [0]; 'aware' [1]; 'managed' [2] (def=0). Can have 'host-' prefix");
+MODULE_PARM_DESC(zone_max_open, "Maximum number of open zones; [0] for no limit (def=auto)");
 
 #define SDEBUG_INFO_LEN 256
 static char sdebug_info[SDEBUG_INFO_LEN];
-- 
2.25.3


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

* [PATCH 5/7] scsi_debug: add zone_nr_conv module parameter
  2020-04-22 10:42 [PATCH 0/7] scsi_debug: Add ZBC support Damien Le Moal
                   ` (3 preceding siblings ...)
  2020-04-22 10:42 ` [PATCH 4/7] scsi_debug: add zone_max_open " Damien Le Moal
@ 2020-04-22 10:42 ` Damien Le Moal
  2020-04-22 10:42 ` [PATCH 6/7] scsi_debug: add zone_size_mb " Damien Le Moal
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 12+ messages in thread
From: Damien Le Moal @ 2020-04-22 10:42 UTC (permalink / raw)
  To: linux-scsi, Martin K . Petersen; +Cc: Douglas Gilbert

Allow controlling the number of conventional zones of a zbc device
with the new zone_nr_conv module parameter. The default value is 1
and the specified value must be less than the total number of zones
of the device. This parameter is ignored for device types other than
0x14 (zbc=2 case).

Signed-off-by: Douglas Gilbert <dgilbert@interlog.com>
Signed-off-by: Damien Le Moal <damien.lemoal@wdc.com>
---
 drivers/scsi/scsi_debug.c | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index 36aeda10a117..058e6d19b21d 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -157,6 +157,7 @@ static const char *sdebug_version_date = "20200421";
 /* Default parameters for ZBC drives */
 #define DEF_ZBC_ZONE_SIZE_MB	128
 #define DEF_ZBC_MAX_OPEN_ZONES	8
+#define DEF_ZBC_NR_CONV_ZONES	1
 
 #define SDEBUG_LUN_0_VAL 0
 
@@ -296,6 +297,7 @@ struct sdebug_dev_info {
 	unsigned int zsize;
 	unsigned int zsize_shift;
 	unsigned int nr_zones;
+	unsigned int nr_conv_zones;
 	unsigned int nr_imp_open;
 	unsigned int nr_exp_open;
 	unsigned int nr_closed;
@@ -823,6 +825,7 @@ static int dif_errors;
 static bool sdeb_zbc_in_use;		/* true when ptype=TYPE_ZBC [0x14] */
 static const int zbc_zone_size_mb;
 static int sdeb_zbc_max_open = DEF_ZBC_MAX_OPEN_ZONES;
+static int sdeb_zbc_nr_conv = DEF_ZBC_NR_CONV_ZONES;
 
 static int submit_queues = DEF_SUBMIT_QUEUES;  /* > 1 for multi-queue (mq) */
 static struct sdebug_queue *sdebug_q_arr;  /* ptr to array of submit queues */
@@ -4787,6 +4790,12 @@ static int sdebug_device_create_zones(struct sdebug_dev_info *devip)
 		devip->zsize_shift = ilog2(devip->zsize);
 	devip->nr_zones = (capacity + devip->zsize - 1) >> devip->zsize_shift;
 
+	if (sdeb_zbc_nr_conv >= devip->nr_zones) {
+		pr_err("Number of conventional zones too large\n");
+		return -EINVAL;
+	}
+	devip->nr_conv_zones = sdeb_zbc_nr_conv;
+
 	/* zbc_max_open_zones can be 0, meaning "not reported" (no limit) */
 	if (sdeb_zbc_max_open >= devip->nr_zones - 1)
 		devip->max_open = (devip->nr_zones - 1) / 2;
@@ -4803,7 +4812,7 @@ static int sdebug_device_create_zones(struct sdebug_dev_info *devip)
 
 		zsp->z_start = zstart;
 
-		if (i == 0) {
+		if (i < devip->nr_conv_zones) {
 			zsp->z_cond = ZBC_NOT_WRITE_POINTER;
 			zsp->z_wp = (sector_t)-1;
 		} else {
@@ -5541,6 +5550,7 @@ module_param_named(write_same_length, sdebug_write_same_length, int,
 		   S_IRUGO | S_IWUSR);
 module_param_named(zbc, sdeb_zbc_model_s, charp, S_IRUGO);
 module_param_named(zone_max_open, sdeb_zbc_max_open, int, S_IRUGO);
+module_param_named(zone_nr_conv, sdeb_zbc_nr_conv, int, S_IRUGO);
 
 MODULE_AUTHOR("Eric Youngdale + Douglas Gilbert");
 MODULE_DESCRIPTION("SCSI debug adapter driver");
@@ -5604,6 +5614,7 @@ MODULE_PARM_DESC(wp, "Write Protect (def=0)");
 MODULE_PARM_DESC(write_same_length, "Maximum blocks per WRITE SAME cmd (def=0xffff)");
 MODULE_PARM_DESC(zbc, "'none' [0]; 'aware' [1]; 'managed' [2] (def=0). Can have 'host-' prefix");
 MODULE_PARM_DESC(zone_max_open, "Maximum number of open zones; [0] for no limit (def=auto)");
+MODULE_PARM_DESC(zone_nr_conv, "Number of conventional zones (def=1)");
 
 #define SDEBUG_INFO_LEN 256
 static char sdebug_info[SDEBUG_INFO_LEN];
-- 
2.25.3


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

* [PATCH 6/7] scsi_debug: add zone_size_mb module parameter
  2020-04-22 10:42 [PATCH 0/7] scsi_debug: Add ZBC support Damien Le Moal
                   ` (4 preceding siblings ...)
  2020-04-22 10:42 ` [PATCH 5/7] scsi_debug: add zone_nr_conv " Damien Le Moal
@ 2020-04-22 10:42 ` Damien Le Moal
  2020-04-22 10:42 ` [PATCH 7/7] scsi_debug: implement zbc host-aware emulation Damien Le Moal
  2020-05-05  4:41 ` [PATCH 0/7] scsi_debug: Add ZBC support Martin K. Petersen
  7 siblings, 0 replies; 12+ messages in thread
From: Damien Le Moal @ 2020-04-22 10:42 UTC (permalink / raw)
  To: linux-scsi, Martin K . Petersen; +Cc: Douglas Gilbert

Add the zone_size_mb module parameters to control the zone size of a
ZBC device. If the zone size specified is not a divisor of the device
capacity, the last zone of the device will be created as a smaller
"runt" zone. This parameter is ignored for device types other than
0x14 (zbc=2 case).

Note: for testing purposes, zone sizes that are not a power of 2 are
accepted but will result in the drive being rejected by the sd driver.

Signed-off-by: Douglas Gilbert <dgilbert@interlog.com>
Signed-off-by: Damien Le Moal <damien.lemoal@wdc.com>
---
 drivers/scsi/scsi_debug.c | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index 058e6d19b21d..9279ac9bb98d 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -823,7 +823,7 @@ static int dif_errors;
 
 /* ZBC global data */
 static bool sdeb_zbc_in_use;		/* true when ptype=TYPE_ZBC [0x14] */
-static const int zbc_zone_size_mb;
+static int sdeb_zbc_zone_size_mb;
 static int sdeb_zbc_max_open = DEF_ZBC_MAX_OPEN_ZONES;
 static int sdeb_zbc_nr_conv = DEF_ZBC_NR_CONV_ZONES;
 
@@ -4763,12 +4763,12 @@ static int sdebug_device_create_zones(struct sdebug_dev_info *devip)
 	unsigned int i;
 
 	/*
-	 * Set the zone size: if zbc_zone_size_mb is not set, figure out a
-	 * zone size allowing for at least 4 zones on the device. Otherwise,
+	 * Set the zone size: if sdeb_zbc_zone_size_mb is not set, figure out
+	 * a zone size allowing for at least 4 zones on the device. Otherwise,
 	 * use the specified zone size checking that at least 2 zones can be
 	 * created for the device.
 	 */
-	if (!zbc_zone_size_mb) {
+	if (!sdeb_zbc_zone_size_mb) {
 		devip->zsize = (DEF_ZBC_ZONE_SIZE_MB * SZ_1M)
 			>> ilog2(sdebug_sector_size);
 		while (capacity < devip->zsize << 2 && devip->zsize >= 2)
@@ -4778,7 +4778,7 @@ static int sdebug_device_create_zones(struct sdebug_dev_info *devip)
 			return -EINVAL;
 		}
 	} else {
-		devip->zsize = (zbc_zone_size_mb * SZ_1M)
+		devip->zsize = (sdeb_zbc_zone_size_mb * SZ_1M)
 			>> ilog2(sdebug_sector_size);
 		if (devip->zsize >= capacity) {
 			pr_err("Zone size too large for device capacity\n");
@@ -5551,6 +5551,7 @@ module_param_named(write_same_length, sdebug_write_same_length, int,
 module_param_named(zbc, sdeb_zbc_model_s, charp, S_IRUGO);
 module_param_named(zone_max_open, sdeb_zbc_max_open, int, S_IRUGO);
 module_param_named(zone_nr_conv, sdeb_zbc_nr_conv, int, S_IRUGO);
+module_param_named(zone_size_mb, sdeb_zbc_zone_size_mb, int, S_IRUGO);
 
 MODULE_AUTHOR("Eric Youngdale + Douglas Gilbert");
 MODULE_DESCRIPTION("SCSI debug adapter driver");
@@ -5615,6 +5616,7 @@ MODULE_PARM_DESC(write_same_length, "Maximum blocks per WRITE SAME cmd (def=0xff
 MODULE_PARM_DESC(zbc, "'none' [0]; 'aware' [1]; 'managed' [2] (def=0). Can have 'host-' prefix");
 MODULE_PARM_DESC(zone_max_open, "Maximum number of open zones; [0] for no limit (def=auto)");
 MODULE_PARM_DESC(zone_nr_conv, "Number of conventional zones (def=1)");
+MODULE_PARM_DESC(zone_size_mb, "Zone size in MiB (def=auto)");
 
 #define SDEBUG_INFO_LEN 256
 static char sdebug_info[SDEBUG_INFO_LEN];
-- 
2.25.3


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

* [PATCH 7/7] scsi_debug: implement zbc host-aware emulation
  2020-04-22 10:42 [PATCH 0/7] scsi_debug: Add ZBC support Damien Le Moal
                   ` (5 preceding siblings ...)
  2020-04-22 10:42 ` [PATCH 6/7] scsi_debug: add zone_size_mb " Damien Le Moal
@ 2020-04-22 10:42 ` Damien Le Moal
  2020-04-22 16:46   ` Douglas Gilbert
  2020-05-05  4:41 ` [PATCH 0/7] scsi_debug: Add ZBC support Martin K. Petersen
  7 siblings, 1 reply; 12+ messages in thread
From: Damien Le Moal @ 2020-04-22 10:42 UTC (permalink / raw)
  To: linux-scsi, Martin K . Petersen; +Cc: Douglas Gilbert

Implement ZBC host-aware device model emulation. The main changes from
the host-managed emulation are the device type (TYPE_DISK is used),
relaxation of access checks for read and write operations and different
handling of a sequential write preferred zone write pointer as mandated
by the ZBC r05 specifications.

To facilitate the implementation and avoid a lot of "if" statement, the
zmodel field is added to the device information and the z_type field to
the zone state data structure.

Signed-off-by: Damien Le Moal <damien.lemoal@wdc.com>
---
 drivers/scsi/scsi_debug.c | 148 ++++++++++++++++++++++++++------------
 1 file changed, 103 insertions(+), 45 deletions(-)

diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index 9279ac9bb98d..46cd4e64bb9c 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -262,6 +262,13 @@ static const char *sdebug_version_date = "20200421";
 
 #define SDEB_XA_NOT_IN_USE XA_MARK_1
 
+/* Zone types (zbcr05 table 25) */
+enum sdebug_z_type {
+	ZBC_ZONE_TYPE_CNV	= 0x1,
+	ZBC_ZONE_TYPE_SWR	= 0x2,
+	ZBC_ZONE_TYPE_SWP	= 0x3,
+};
+
 /* enumeration names taken from table 26, zbcr05 */
 enum sdebug_z_cond {
 	ZBC_NOT_WRITE_POINTER	= 0x0,
@@ -275,7 +282,9 @@ enum sdebug_z_cond {
 };
 
 struct sdeb_zone_state {	/* ZBC: per zone state */
+	enum sdebug_z_type z_type;
 	enum sdebug_z_cond z_cond;
+	bool z_non_seq_resource;
 	unsigned int z_size;
 	sector_t z_start;
 	sector_t z_wp;
@@ -294,6 +303,7 @@ struct sdebug_dev_info {
 	bool used;
 
 	/* For ZBC devices */
+	enum blk_zoned_model zmodel;
 	unsigned int zsize;
 	unsigned int zsize_shift;
 	unsigned int nr_zones;
@@ -822,7 +832,7 @@ static int dix_reads;
 static int dif_errors;
 
 /* ZBC global data */
-static bool sdeb_zbc_in_use;		/* true when ptype=TYPE_ZBC [0x14] */
+static bool sdeb_zbc_in_use;	/* true for host-aware and host-managed disks */
 static int sdeb_zbc_zone_size_mb;
 static int sdeb_zbc_max_open = DEF_ZBC_MAX_OPEN_ZONES;
 static int sdeb_zbc_nr_conv = DEF_ZBC_NR_CONV_ZONES;
@@ -1500,13 +1510,15 @@ static int inquiry_vpd_b0(unsigned char *arr)
 }
 
 /* Block device characteristics VPD page (SBC-3) */
-static int inquiry_vpd_b1(unsigned char *arr)
+static int inquiry_vpd_b1(struct sdebug_dev_info *devip, unsigned char *arr)
 {
 	memset(arr, 0, 0x3c);
 	arr[0] = 0;
 	arr[1] = 1;	/* non rotating medium (e.g. solid state) */
 	arr[2] = 0;
 	arr[3] = 5;	/* less than 1.8" */
+	if (devip->zmodel == BLK_ZONED_HA)
+		arr[4] = 1 << 4;	/* zoned field = 01b */
 
 	return 0x3c;
 }
@@ -1543,7 +1555,7 @@ static int inquiry_vpd_b6(struct sdebug_dev_info *devip, unsigned char *arr)
 	 */
 	put_unaligned_be32(0xffffffff, &arr[4]);
 	put_unaligned_be32(0xffffffff, &arr[8]);
-	if (devip->max_open)
+	if (sdeb_zbc_model == BLK_ZONED_HM && devip->max_open)
 		put_unaligned_be32(devip->max_open, &arr[12]);
 	else
 		put_unaligned_be32(0xffffffff, &arr[12]);
@@ -1566,7 +1578,7 @@ static int resp_inquiry(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
 	if (! arr)
 		return DID_REQUEUE << 16;
 	is_disk = (sdebug_ptype == TYPE_DISK);
-	is_zbc = (sdebug_ptype == TYPE_ZBC);
+	is_zbc = (devip->zmodel != BLK_ZONED_NONE);
 	is_disk_zbc = (is_disk || is_zbc);
 	have_wlun = scsi_is_wlun(scp->device->lun);
 	if (have_wlun)
@@ -1611,7 +1623,7 @@ static int resp_inquiry(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
 				arr[n++] = 0xb1;  /* Block characteristics */
 				if (is_disk)
 					arr[n++] = 0xb2;  /* LB Provisioning */
-				else if (is_zbc)
+				if (is_zbc)
 					arr[n++] = 0xb6;  /* ZB dev. char. */
 			}
 			arr[3] = n - 4;	  /* number of supported VPD pages */
@@ -1660,7 +1672,7 @@ static int resp_inquiry(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
 			arr[3] = inquiry_vpd_b0(&arr[4]);
 		} else if (is_disk_zbc && 0xb1 == cmd[2]) { /* Block char. */
 			arr[1] = cmd[2];        /*sanity */
-			arr[3] = inquiry_vpd_b1(&arr[4]);
+			arr[3] = inquiry_vpd_b1(devip, &arr[4]);
 		} else if (is_disk && 0xb2 == cmd[2]) { /* LB Prov. */
 			arr[1] = cmd[2];        /*sanity */
 			arr[3] = inquiry_vpd_b2(&arr[4]);
@@ -2305,7 +2317,7 @@ static int resp_mode_sense(struct scsi_cmnd *scp,
 	msense_6 = (MODE_SENSE == cmd[0]);
 	llbaa = msense_6 ? false : !!(cmd[1] & 0x10);
 	is_disk = (sdebug_ptype == TYPE_DISK);
-	is_zbc = (sdebug_ptype == TYPE_ZBC);
+	is_zbc = (devip->zmodel != BLK_ZONED_NONE);
 	if ((is_disk || is_zbc) && !dbd)
 		bd_len = llbaa ? 16 : 8;
 	else
@@ -2656,7 +2668,7 @@ static struct sdeb_zone_state *zbc_zone(struct sdebug_dev_info *devip,
 
 static inline bool zbc_zone_is_conv(struct sdeb_zone_state *zsp)
 {
-	return zsp->z_cond == ZBC_NOT_WRITE_POINTER;
+	return zsp->z_type == ZBC_ZONE_TYPE_CNV;
 }
 
 static void zbc_close_zone(struct sdebug_dev_info *devip,
@@ -2732,13 +2744,42 @@ static void zbc_inc_wp(struct sdebug_dev_info *devip,
 		       unsigned long long lba, unsigned int num)
 {
 	struct sdeb_zone_state *zsp = zbc_zone(devip, lba);
+	unsigned long long n, end, zend = zsp->z_start + zsp->z_size;
 
 	if (zbc_zone_is_conv(zsp))
 		return;
 
-	zsp->z_wp += num;
-	if (zsp->z_wp >= zsp->z_start + zsp->z_size)
-		zsp->z_cond = ZC5_FULL;
+	if (zsp->z_type == ZBC_ZONE_TYPE_SWR) {
+		zsp->z_wp += num;
+		if (zsp->z_wp >= zend)
+			zsp->z_cond = ZC5_FULL;
+		return;
+	}
+
+	while (num) {
+		if (lba != zsp->z_wp)
+			zsp->z_non_seq_resource = true;
+
+		end = lba + num;
+		if (end >= zend) {
+			n = zend - lba;
+			zsp->z_wp = zend;
+		} else if (end > zsp->z_wp) {
+			n = num;
+			zsp->z_wp = end;
+		} else {
+			n = num;
+		}
+		if (zsp->z_wp >= zend)
+			zsp->z_cond = ZC5_FULL;
+
+		num -= n;
+		lba += n;
+		if (num) {
+			zsp++;
+			zend = zsp->z_start + zsp->z_size;
+		}
+	}
 }
 
 static int check_zbc_access_params(struct scsi_cmnd *scp,
@@ -2750,7 +2791,9 @@ static int check_zbc_access_params(struct scsi_cmnd *scp,
 	struct sdeb_zone_state *zsp_end = zbc_zone(devip, lba + num - 1);
 
 	if (!write) {
-		/* Reads cannot cross zone types boundaries */
+		if (devip->zmodel == BLK_ZONED_HA)
+			return 0;
+		/* For host-managed, reads cannot cross zone types boundaries */
 		if (zsp_end != zsp &&
 		    zbc_zone_is_conv(zsp) &&
 		    !zbc_zone_is_conv(zsp_end)) {
@@ -2773,25 +2816,27 @@ static int check_zbc_access_params(struct scsi_cmnd *scp,
 		return 0;
 	}
 
-	/* Writes cannot cross sequential zone boundaries */
-	if (zsp_end != zsp) {
-		mk_sense_buffer(scp, ILLEGAL_REQUEST,
-				LBA_OUT_OF_RANGE,
-				WRITE_BOUNDARY_ASCQ);
-		return check_condition_result;
-	}
-	/* Cannot write full zones */
-	if (zsp->z_cond == ZC5_FULL) {
-		mk_sense_buffer(scp, ILLEGAL_REQUEST,
-				INVALID_FIELD_IN_CDB, 0);
-		return check_condition_result;
-	}
-	/* Writes must be aligned to the zone WP */
-	if (lba != zsp->z_wp) {
-		mk_sense_buffer(scp, ILLEGAL_REQUEST,
-				LBA_OUT_OF_RANGE,
-				UNALIGNED_WRITE_ASCQ);
-		return check_condition_result;
+	if (zsp->z_type == ZBC_ZONE_TYPE_SWR) {
+		/* Writes cannot cross sequential zone boundaries */
+		if (zsp_end != zsp) {
+			mk_sense_buffer(scp, ILLEGAL_REQUEST,
+					LBA_OUT_OF_RANGE,
+					WRITE_BOUNDARY_ASCQ);
+			return check_condition_result;
+		}
+		/* Cannot write full zones */
+		if (zsp->z_cond == ZC5_FULL) {
+			mk_sense_buffer(scp, ILLEGAL_REQUEST,
+					INVALID_FIELD_IN_CDB, 0);
+			return check_condition_result;
+		}
+		/* Writes must be aligned to the zone WP */
+		if (lba != zsp->z_wp) {
+			mk_sense_buffer(scp, ILLEGAL_REQUEST,
+					LBA_OUT_OF_RANGE,
+					UNALIGNED_WRITE_ASCQ);
+			return check_condition_result;
+		}
 	}
 
 	/* Handle implicit open of closed and empty zones */
@@ -4312,13 +4357,16 @@ static int resp_report_zones(struct scsi_cmnd *scp,
 		case 0x06:
 		case 0x07:
 		case 0x10:
-		case 0x11:
 			/*
-			 * Read-only, offline, reset WP recommended and
-			 * non-seq-resource-used are not emulated: no zones
-			 * to report;
+			 * Read-only, offline, reset WP recommended are
+			 * not emulated: no zones to report;
 			 */
 			continue;
+		case 0x11:
+			/* non-seq-resource set */
+			if (!zsp->z_non_seq_resource)
+				continue;
+			break;
 		case 0x3f:
 			/* Not write pointer (conventional) zones */
 			if (!zbc_zone_is_conv(zsp))
@@ -4333,11 +4381,10 @@ static int resp_report_zones(struct scsi_cmnd *scp,
 
 		if (nrz < rep_max_zones) {
 			/* Fill zone descriptor */
-			if (zbc_zone_is_conv(zsp))
-				desc[0] = 0x1;
-			else
-				desc[0] = 0x2;
+			desc[0] = zsp->z_type;
 			desc[1] = zsp->z_cond << 4;
+			if (zsp->z_non_seq_resource)
+				desc[1] |= 1 << 1;
 			put_unaligned_be64((u64)zsp->z_size, desc + 8);
 			put_unaligned_be64((u64)zsp->z_start, desc + 16);
 			put_unaligned_be64((u64)zsp->z_wp, desc + 24);
@@ -4591,6 +4638,7 @@ static void zbc_rwp_zone(struct sdebug_dev_info *devip,
 	if (zsp->z_cond == ZC4_CLOSED)
 		devip->nr_closed--;
 
+	zsp->z_non_seq_resource = false;
 	zsp->z_wp = zsp->z_start;
 	zsp->z_cond = ZC1_EMPTY;
 }
@@ -4796,11 +4844,13 @@ static int sdebug_device_create_zones(struct sdebug_dev_info *devip)
 	}
 	devip->nr_conv_zones = sdeb_zbc_nr_conv;
 
-	/* zbc_max_open_zones can be 0, meaning "not reported" (no limit) */
-	if (sdeb_zbc_max_open >= devip->nr_zones - 1)
-		devip->max_open = (devip->nr_zones - 1) / 2;
-	else
-		devip->max_open = sdeb_zbc_max_open;
+	if (devip->zmodel == BLK_ZONED_HM) {
+		/* zbc_max_open_zones can be 0, meaning "not reported" */
+		if (sdeb_zbc_max_open >= devip->nr_zones - 1)
+			devip->max_open = (devip->nr_zones - 1) / 2;
+		else
+			devip->max_open = sdeb_zbc_max_open;
+	}
 
 	devip->zstate = kcalloc(devip->nr_zones,
 				sizeof(struct sdeb_zone_state), GFP_KERNEL);
@@ -4813,9 +4863,14 @@ static int sdebug_device_create_zones(struct sdebug_dev_info *devip)
 		zsp->z_start = zstart;
 
 		if (i < devip->nr_conv_zones) {
+			zsp->z_type = ZBC_ZONE_TYPE_CNV;
 			zsp->z_cond = ZBC_NOT_WRITE_POINTER;
 			zsp->z_wp = (sector_t)-1;
 		} else {
+			if (devip->zmodel == BLK_ZONED_HM)
+				zsp->z_type = ZBC_ZONE_TYPE_SWR;
+			else
+				zsp->z_type = ZBC_ZONE_TYPE_SWP;
 			zsp->z_cond = ZC1_EMPTY;
 			zsp->z_wp = zsp->z_start;
 		}
@@ -4851,10 +4906,13 @@ static struct sdebug_dev_info *sdebug_device_create(
 		}
 		devip->sdbg_host = sdbg_host;
 		if (sdeb_zbc_in_use) {
+			devip->zmodel = sdeb_zbc_model;
 			if (sdebug_device_create_zones(devip)) {
 				kfree(devip);
 				return NULL;
 			}
+		} else {
+			devip->zmodel = BLK_ZONED_NONE;
 		}
 		devip->sdbg_host = sdbg_host;
 		list_add_tail(&devip->dev_list, &sdbg_host->dev_info_list);
@@ -6566,12 +6624,12 @@ static int __init scsi_debug_init(void)
 		sdeb_zbc_model = k;
 		switch (sdeb_zbc_model) {
 		case BLK_ZONED_NONE:
+		case BLK_ZONED_HA:
 			sdebug_ptype = TYPE_DISK;
 			break;
 		case BLK_ZONED_HM:
 			sdebug_ptype = TYPE_ZBC;
 			break;
-		case BLK_ZONED_HA:
 		default:
 			pr_err("Invalid ZBC model\n");
 			return -EINVAL;
-- 
2.25.3


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

* Re: [PATCH 7/7] scsi_debug: implement zbc host-aware emulation
  2020-04-22 10:42 ` [PATCH 7/7] scsi_debug: implement zbc host-aware emulation Damien Le Moal
@ 2020-04-22 16:46   ` Douglas Gilbert
  2020-04-22 16:50     ` Martin K. Petersen
  2020-04-23  1:33     ` Damien Le Moal
  0 siblings, 2 replies; 12+ messages in thread
From: Douglas Gilbert @ 2020-04-22 16:46 UTC (permalink / raw)
  To: Damien Le Moal, linux-scsi, Martin K . Petersen; +Cc: Hannes Reinecke

On 2020-04-22 6:42 a.m., Damien Le Moal wrote:
> Implement ZBC host-aware device model emulation. The main changes from
> the host-managed emulation are the device type (TYPE_DISK is used),
> relaxation of access checks for read and write operations and different
> handling of a sequential write preferred zone write pointer as mandated
> by the ZBC r05 specifications.
> 
> To facilitate the implementation and avoid a lot of "if" statement, the
> zmodel field is added to the device information and the z_type field to
> the zone state data structure.
> 
> Signed-off-by: Damien Le Moal <damien.lemoal@wdc.com>

This is an unexpected surprise and a chance to exercise previously untested
parts of my user space packages (sg3_utils and sdparm).

A small nit: draft ZBC-2 has added the Zoned Block Device Extension field
which should be set to 1 for host-aware (and 0 for host-managed). It is 0
in both cases at the moment which is strictly speaking correct as 0 is
defined as "not reported". IMO it is more correct to set it to 1 in the
host-aware case :-)

Ralph Weber failed in his attempt to get version strings thrown out at T10.
So we might consider adding a ZBC version descriptor in the host-aware and
host-managed case. [Version descriptors are in the standard INQUIRY
response.]

Wish list: it would be good if lsscsi could indicate in its one line per
device mode that a disk was actually a ZBC host-aware disk. lsscsi is
restricted to datamining in sysfs and some other locations that don't
need root permissions. So it does not issue any SCSI commands. To see a
disk is ZBC host-aware it needs access to the Block Device Characteristics
VPD page, but as far as I can see that is not loaded into sysfs at this
time. Hannes?

Tested-by: Douglas Gilbert <dgilbert@interlog.com>

> ---
>   drivers/scsi/scsi_debug.c | 148 ++++++++++++++++++++++++++------------
>   1 file changed, 103 insertions(+), 45 deletions(-)
> 
> diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
> index 9279ac9bb98d..46cd4e64bb9c 100644
> --- a/drivers/scsi/scsi_debug.c
> +++ b/drivers/scsi/scsi_debug.c
> @@ -262,6 +262,13 @@ static const char *sdebug_version_date = "20200421";
>   
>   #define SDEB_XA_NOT_IN_USE XA_MARK_1
>   
> +/* Zone types (zbcr05 table 25) */
> +enum sdebug_z_type {
> +	ZBC_ZONE_TYPE_CNV	= 0x1,
> +	ZBC_ZONE_TYPE_SWR	= 0x2,
> +	ZBC_ZONE_TYPE_SWP	= 0x3,
> +};
> +
>   /* enumeration names taken from table 26, zbcr05 */
>   enum sdebug_z_cond {
>   	ZBC_NOT_WRITE_POINTER	= 0x0,
> @@ -275,7 +282,9 @@ enum sdebug_z_cond {
>   };
>   
>   struct sdeb_zone_state {	/* ZBC: per zone state */
> +	enum sdebug_z_type z_type;
>   	enum sdebug_z_cond z_cond;
> +	bool z_non_seq_resource;
>   	unsigned int z_size;
>   	sector_t z_start;
>   	sector_t z_wp;
> @@ -294,6 +303,7 @@ struct sdebug_dev_info {
>   	bool used;
>   
>   	/* For ZBC devices */
> +	enum blk_zoned_model zmodel;
>   	unsigned int zsize;
>   	unsigned int zsize_shift;
>   	unsigned int nr_zones;
> @@ -822,7 +832,7 @@ static int dix_reads;
>   static int dif_errors;
>   
>   /* ZBC global data */
> -static bool sdeb_zbc_in_use;		/* true when ptype=TYPE_ZBC [0x14] */
> +static bool sdeb_zbc_in_use;	/* true for host-aware and host-managed disks */
>   static int sdeb_zbc_zone_size_mb;
>   static int sdeb_zbc_max_open = DEF_ZBC_MAX_OPEN_ZONES;
>   static int sdeb_zbc_nr_conv = DEF_ZBC_NR_CONV_ZONES;
> @@ -1500,13 +1510,15 @@ static int inquiry_vpd_b0(unsigned char *arr)
>   }
>   
>   /* Block device characteristics VPD page (SBC-3) */
> -static int inquiry_vpd_b1(unsigned char *arr)
> +static int inquiry_vpd_b1(struct sdebug_dev_info *devip, unsigned char *arr)
>   {
>   	memset(arr, 0, 0x3c);
>   	arr[0] = 0;
>   	arr[1] = 1;	/* non rotating medium (e.g. solid state) */
>   	arr[2] = 0;
>   	arr[3] = 5;	/* less than 1.8" */
> +	if (devip->zmodel == BLK_ZONED_HA)
> +		arr[4] = 1 << 4;	/* zoned field = 01b */
>   
>   	return 0x3c;
>   }
> @@ -1543,7 +1555,7 @@ static int inquiry_vpd_b6(struct sdebug_dev_info *devip, unsigned char *arr)
>   	 */
>   	put_unaligned_be32(0xffffffff, &arr[4]);
>   	put_unaligned_be32(0xffffffff, &arr[8]);
> -	if (devip->max_open)
> +	if (sdeb_zbc_model == BLK_ZONED_HM && devip->max_open)
>   		put_unaligned_be32(devip->max_open, &arr[12]);
>   	else
>   		put_unaligned_be32(0xffffffff, &arr[12]);
> @@ -1566,7 +1578,7 @@ static int resp_inquiry(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
>   	if (! arr)
>   		return DID_REQUEUE << 16;
>   	is_disk = (sdebug_ptype == TYPE_DISK);
> -	is_zbc = (sdebug_ptype == TYPE_ZBC);
> +	is_zbc = (devip->zmodel != BLK_ZONED_NONE);
>   	is_disk_zbc = (is_disk || is_zbc);
>   	have_wlun = scsi_is_wlun(scp->device->lun);
>   	if (have_wlun)
> @@ -1611,7 +1623,7 @@ static int resp_inquiry(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
>   				arr[n++] = 0xb1;  /* Block characteristics */
>   				if (is_disk)
>   					arr[n++] = 0xb2;  /* LB Provisioning */
> -				else if (is_zbc)
> +				if (is_zbc)
>   					arr[n++] = 0xb6;  /* ZB dev. char. */
>   			}
>   			arr[3] = n - 4;	  /* number of supported VPD pages */
> @@ -1660,7 +1672,7 @@ static int resp_inquiry(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
>   			arr[3] = inquiry_vpd_b0(&arr[4]);
>   		} else if (is_disk_zbc && 0xb1 == cmd[2]) { /* Block char. */
>   			arr[1] = cmd[2];        /*sanity */
> -			arr[3] = inquiry_vpd_b1(&arr[4]);
> +			arr[3] = inquiry_vpd_b1(devip, &arr[4]);
>   		} else if (is_disk && 0xb2 == cmd[2]) { /* LB Prov. */
>   			arr[1] = cmd[2];        /*sanity */
>   			arr[3] = inquiry_vpd_b2(&arr[4]);
> @@ -2305,7 +2317,7 @@ static int resp_mode_sense(struct scsi_cmnd *scp,
>   	msense_6 = (MODE_SENSE == cmd[0]);
>   	llbaa = msense_6 ? false : !!(cmd[1] & 0x10);
>   	is_disk = (sdebug_ptype == TYPE_DISK);
> -	is_zbc = (sdebug_ptype == TYPE_ZBC);
> +	is_zbc = (devip->zmodel != BLK_ZONED_NONE);
>   	if ((is_disk || is_zbc) && !dbd)
>   		bd_len = llbaa ? 16 : 8;
>   	else
> @@ -2656,7 +2668,7 @@ static struct sdeb_zone_state *zbc_zone(struct sdebug_dev_info *devip,
>   
>   static inline bool zbc_zone_is_conv(struct sdeb_zone_state *zsp)
>   {
> -	return zsp->z_cond == ZBC_NOT_WRITE_POINTER;
> +	return zsp->z_type == ZBC_ZONE_TYPE_CNV;
>   }
>   
>   static void zbc_close_zone(struct sdebug_dev_info *devip,
> @@ -2732,13 +2744,42 @@ static void zbc_inc_wp(struct sdebug_dev_info *devip,
>   		       unsigned long long lba, unsigned int num)
>   {
>   	struct sdeb_zone_state *zsp = zbc_zone(devip, lba);
> +	unsigned long long n, end, zend = zsp->z_start + zsp->z_size;
>   
>   	if (zbc_zone_is_conv(zsp))
>   		return;
>   
> -	zsp->z_wp += num;
> -	if (zsp->z_wp >= zsp->z_start + zsp->z_size)
> -		zsp->z_cond = ZC5_FULL;
> +	if (zsp->z_type == ZBC_ZONE_TYPE_SWR) {
> +		zsp->z_wp += num;
> +		if (zsp->z_wp >= zend)
> +			zsp->z_cond = ZC5_FULL;
> +		return;
> +	}
> +
> +	while (num) {
> +		if (lba != zsp->z_wp)
> +			zsp->z_non_seq_resource = true;
> +
> +		end = lba + num;
> +		if (end >= zend) {
> +			n = zend - lba;
> +			zsp->z_wp = zend;
> +		} else if (end > zsp->z_wp) {
> +			n = num;
> +			zsp->z_wp = end;
> +		} else {
> +			n = num;
> +		}
> +		if (zsp->z_wp >= zend)
> +			zsp->z_cond = ZC5_FULL;
> +
> +		num -= n;
> +		lba += n;
> +		if (num) {
> +			zsp++;
> +			zend = zsp->z_start + zsp->z_size;
> +		}
> +	}
>   }
>   
>   static int check_zbc_access_params(struct scsi_cmnd *scp,
> @@ -2750,7 +2791,9 @@ static int check_zbc_access_params(struct scsi_cmnd *scp,
>   	struct sdeb_zone_state *zsp_end = zbc_zone(devip, lba + num - 1);
>   
>   	if (!write) {
> -		/* Reads cannot cross zone types boundaries */
> +		if (devip->zmodel == BLK_ZONED_HA)
> +			return 0;
> +		/* For host-managed, reads cannot cross zone types boundaries */
>   		if (zsp_end != zsp &&
>   		    zbc_zone_is_conv(zsp) &&
>   		    !zbc_zone_is_conv(zsp_end)) {
> @@ -2773,25 +2816,27 @@ static int check_zbc_access_params(struct scsi_cmnd *scp,
>   		return 0;
>   	}
>   
> -	/* Writes cannot cross sequential zone boundaries */
> -	if (zsp_end != zsp) {
> -		mk_sense_buffer(scp, ILLEGAL_REQUEST,
> -				LBA_OUT_OF_RANGE,
> -				WRITE_BOUNDARY_ASCQ);
> -		return check_condition_result;
> -	}
> -	/* Cannot write full zones */
> -	if (zsp->z_cond == ZC5_FULL) {
> -		mk_sense_buffer(scp, ILLEGAL_REQUEST,
> -				INVALID_FIELD_IN_CDB, 0);
> -		return check_condition_result;
> -	}
> -	/* Writes must be aligned to the zone WP */
> -	if (lba != zsp->z_wp) {
> -		mk_sense_buffer(scp, ILLEGAL_REQUEST,
> -				LBA_OUT_OF_RANGE,
> -				UNALIGNED_WRITE_ASCQ);
> -		return check_condition_result;
> +	if (zsp->z_type == ZBC_ZONE_TYPE_SWR) {
> +		/* Writes cannot cross sequential zone boundaries */
> +		if (zsp_end != zsp) {
> +			mk_sense_buffer(scp, ILLEGAL_REQUEST,
> +					LBA_OUT_OF_RANGE,
> +					WRITE_BOUNDARY_ASCQ);
> +			return check_condition_result;
> +		}
> +		/* Cannot write full zones */
> +		if (zsp->z_cond == ZC5_FULL) {
> +			mk_sense_buffer(scp, ILLEGAL_REQUEST,
> +					INVALID_FIELD_IN_CDB, 0);
> +			return check_condition_result;
> +		}
> +		/* Writes must be aligned to the zone WP */
> +		if (lba != zsp->z_wp) {
> +			mk_sense_buffer(scp, ILLEGAL_REQUEST,
> +					LBA_OUT_OF_RANGE,
> +					UNALIGNED_WRITE_ASCQ);
> +			return check_condition_result;
> +		}
>   	}
>   
>   	/* Handle implicit open of closed and empty zones */
> @@ -4312,13 +4357,16 @@ static int resp_report_zones(struct scsi_cmnd *scp,
>   		case 0x06:
>   		case 0x07:
>   		case 0x10:
> -		case 0x11:
>   			/*
> -			 * Read-only, offline, reset WP recommended and
> -			 * non-seq-resource-used are not emulated: no zones
> -			 * to report;
> +			 * Read-only, offline, reset WP recommended are
> +			 * not emulated: no zones to report;
>   			 */
>   			continue;
> +		case 0x11:
> +			/* non-seq-resource set */
> +			if (!zsp->z_non_seq_resource)
> +				continue;
> +			break;
>   		case 0x3f:
>   			/* Not write pointer (conventional) zones */
>   			if (!zbc_zone_is_conv(zsp))
> @@ -4333,11 +4381,10 @@ static int resp_report_zones(struct scsi_cmnd *scp,
>   
>   		if (nrz < rep_max_zones) {
>   			/* Fill zone descriptor */
> -			if (zbc_zone_is_conv(zsp))
> -				desc[0] = 0x1;
> -			else
> -				desc[0] = 0x2;
> +			desc[0] = zsp->z_type;
>   			desc[1] = zsp->z_cond << 4;
> +			if (zsp->z_non_seq_resource)
> +				desc[1] |= 1 << 1;
>   			put_unaligned_be64((u64)zsp->z_size, desc + 8);
>   			put_unaligned_be64((u64)zsp->z_start, desc + 16);
>   			put_unaligned_be64((u64)zsp->z_wp, desc + 24);
> @@ -4591,6 +4638,7 @@ static void zbc_rwp_zone(struct sdebug_dev_info *devip,
>   	if (zsp->z_cond == ZC4_CLOSED)
>   		devip->nr_closed--;
>   
> +	zsp->z_non_seq_resource = false;
>   	zsp->z_wp = zsp->z_start;
>   	zsp->z_cond = ZC1_EMPTY;
>   }
> @@ -4796,11 +4844,13 @@ static int sdebug_device_create_zones(struct sdebug_dev_info *devip)
>   	}
>   	devip->nr_conv_zones = sdeb_zbc_nr_conv;
>   
> -	/* zbc_max_open_zones can be 0, meaning "not reported" (no limit) */
> -	if (sdeb_zbc_max_open >= devip->nr_zones - 1)
> -		devip->max_open = (devip->nr_zones - 1) / 2;
> -	else
> -		devip->max_open = sdeb_zbc_max_open;
> +	if (devip->zmodel == BLK_ZONED_HM) {
> +		/* zbc_max_open_zones can be 0, meaning "not reported" */
> +		if (sdeb_zbc_max_open >= devip->nr_zones - 1)
> +			devip->max_open = (devip->nr_zones - 1) / 2;
> +		else
> +			devip->max_open = sdeb_zbc_max_open;
> +	}
>   
>   	devip->zstate = kcalloc(devip->nr_zones,
>   				sizeof(struct sdeb_zone_state), GFP_KERNEL);
> @@ -4813,9 +4863,14 @@ static int sdebug_device_create_zones(struct sdebug_dev_info *devip)
>   		zsp->z_start = zstart;
>   
>   		if (i < devip->nr_conv_zones) {
> +			zsp->z_type = ZBC_ZONE_TYPE_CNV;
>   			zsp->z_cond = ZBC_NOT_WRITE_POINTER;
>   			zsp->z_wp = (sector_t)-1;
>   		} else {
> +			if (devip->zmodel == BLK_ZONED_HM)
> +				zsp->z_type = ZBC_ZONE_TYPE_SWR;
> +			else
> +				zsp->z_type = ZBC_ZONE_TYPE_SWP;
>   			zsp->z_cond = ZC1_EMPTY;
>   			zsp->z_wp = zsp->z_start;
>   		}
> @@ -4851,10 +4906,13 @@ static struct sdebug_dev_info *sdebug_device_create(
>   		}
>   		devip->sdbg_host = sdbg_host;
>   		if (sdeb_zbc_in_use) {
> +			devip->zmodel = sdeb_zbc_model;
>   			if (sdebug_device_create_zones(devip)) {
>   				kfree(devip);
>   				return NULL;
>   			}
> +		} else {
> +			devip->zmodel = BLK_ZONED_NONE;
>   		}
>   		devip->sdbg_host = sdbg_host;
>   		list_add_tail(&devip->dev_list, &sdbg_host->dev_info_list);
> @@ -6566,12 +6624,12 @@ static int __init scsi_debug_init(void)
>   		sdeb_zbc_model = k;
>   		switch (sdeb_zbc_model) {
>   		case BLK_ZONED_NONE:
> +		case BLK_ZONED_HA:
>   			sdebug_ptype = TYPE_DISK;
>   			break;
>   		case BLK_ZONED_HM:
>   			sdebug_ptype = TYPE_ZBC;
>   			break;
> -		case BLK_ZONED_HA:
>   		default:
>   			pr_err("Invalid ZBC model\n");
>   			return -EINVAL;
> 


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

* Re: [PATCH 7/7] scsi_debug: implement zbc host-aware emulation
  2020-04-22 16:46   ` Douglas Gilbert
@ 2020-04-22 16:50     ` Martin K. Petersen
  2020-04-23  1:33     ` Damien Le Moal
  1 sibling, 0 replies; 12+ messages in thread
From: Martin K. Petersen @ 2020-04-22 16:50 UTC (permalink / raw)
  To: Douglas Gilbert
  Cc: Damien Le Moal, linux-scsi, Martin K . Petersen, Hannes Reinecke


Doug,

> To see a disk is ZBC host-aware it needs access to the Block Device
> Characteristics VPD page, but as far as I can see that is not loaded
> into sysfs at this time. Hannes?

That's coming in my sd fixes series later today. Made a ton of changes
in this area.

-- 
Martin K. Petersen	Oracle Linux Engineering

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

* Re: [PATCH 7/7] scsi_debug: implement zbc host-aware emulation
  2020-04-22 16:46   ` Douglas Gilbert
  2020-04-22 16:50     ` Martin K. Petersen
@ 2020-04-23  1:33     ` Damien Le Moal
  1 sibling, 0 replies; 12+ messages in thread
From: Damien Le Moal @ 2020-04-23  1:33 UTC (permalink / raw)
  To: dgilbert, linux-scsi, Martin K . Petersen; +Cc: Hannes Reinecke

On 2020/04/23 1:46, Douglas Gilbert wrote:
> On 2020-04-22 6:42 a.m., Damien Le Moal wrote:
>> Implement ZBC host-aware device model emulation. The main changes from
>> the host-managed emulation are the device type (TYPE_DISK is used),
>> relaxation of access checks for read and write operations and different
>> handling of a sequential write preferred zone write pointer as mandated
>> by the ZBC r05 specifications.
>>
>> To facilitate the implementation and avoid a lot of "if" statement, the
>> zmodel field is added to the device information and the z_type field to
>> the zone state data structure.
>>
>> Signed-off-by: Damien Le Moal <damien.lemoal@wdc.com>
> 
> This is an unexpected surprise and a chance to exercise previously untested
> parts of my user space packages (sg3_utils and sdparm).

Yes, I decided yesterday to add this on top of the series we worked on together.
The main reason iss to allow for writing blktests to test partition handling
with host-aware drives as some special handling was added for these recently. If
an HA drive has partition, its zoned model is automatically changed to "NONE"
and restored to "host-aware" only when the partitions are deleted. Having
blktests cases to do regression testing on that iss I think important as this is
an unusal behavior. And you already had added the host-aware paramter cases so
everything was mostly already wired for this emulation case.

> A small nit: draft ZBC-2 has added the Zoned Block Device Extension field
> which should be set to 1 for host-aware (and 0 for host-managed). It is 0
> in both cases at the moment which is strictly speaking correct as 0 is
> defined as "not reported". IMO it is more correct to set it to 1 in the
> host-aware case :-)
> 
> Ralph Weber failed in his attempt to get version strings thrown out at T10.
> So we might consider adding a ZBC version descriptor in the host-aware and
> host-managed case. [Version descriptors are in the standard INQUIRY
> response.]

ZBC-2 is still in draft state. We should not yet implement against it. Last I
checked with Ralph, the outlook for a stable spec is year end. I am monitoring
that since yhere are a lot more changes that will need to be taken care of on
the kernel side. ZBC-2 (and ZAC-2) extend reset/open/close/finish operations to
operate on a range of zones. At the very least, we will need that support in the
kernel SAT (drivers/ata/libata-scsi.c).

> 
> Wish list: it would be good if lsscsi could indicate in its one line per
> device mode that a disk was actually a ZBC host-aware disk. lsscsi is
> restricted to datamining in sysfs and some other locations that don't
> need root permissions. So it does not issue any SCSI commands. To see a
> disk is ZBC host-aware it needs access to the Block Device Characteristics
> VPD page, but as far as I can see that is not loaded into sysfs at this
> time. Hannes?
> 
> Tested-by: Douglas Gilbert <dgilbert@interlog.com>
> 
>> ---
>>   drivers/scsi/scsi_debug.c | 148 ++++++++++++++++++++++++++------------
>>   1 file changed, 103 insertions(+), 45 deletions(-)
>>
>> diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
>> index 9279ac9bb98d..46cd4e64bb9c 100644
>> --- a/drivers/scsi/scsi_debug.c
>> +++ b/drivers/scsi/scsi_debug.c
>> @@ -262,6 +262,13 @@ static const char *sdebug_version_date = "20200421";
>>   
>>   #define SDEB_XA_NOT_IN_USE XA_MARK_1
>>   
>> +/* Zone types (zbcr05 table 25) */
>> +enum sdebug_z_type {
>> +	ZBC_ZONE_TYPE_CNV	= 0x1,
>> +	ZBC_ZONE_TYPE_SWR	= 0x2,
>> +	ZBC_ZONE_TYPE_SWP	= 0x3,
>> +};
>> +
>>   /* enumeration names taken from table 26, zbcr05 */
>>   enum sdebug_z_cond {
>>   	ZBC_NOT_WRITE_POINTER	= 0x0,
>> @@ -275,7 +282,9 @@ enum sdebug_z_cond {
>>   };
>>   
>>   struct sdeb_zone_state {	/* ZBC: per zone state */
>> +	enum sdebug_z_type z_type;
>>   	enum sdebug_z_cond z_cond;
>> +	bool z_non_seq_resource;
>>   	unsigned int z_size;
>>   	sector_t z_start;
>>   	sector_t z_wp;
>> @@ -294,6 +303,7 @@ struct sdebug_dev_info {
>>   	bool used;
>>   
>>   	/* For ZBC devices */
>> +	enum blk_zoned_model zmodel;
>>   	unsigned int zsize;
>>   	unsigned int zsize_shift;
>>   	unsigned int nr_zones;
>> @@ -822,7 +832,7 @@ static int dix_reads;
>>   static int dif_errors;
>>   
>>   /* ZBC global data */
>> -static bool sdeb_zbc_in_use;		/* true when ptype=TYPE_ZBC [0x14] */
>> +static bool sdeb_zbc_in_use;	/* true for host-aware and host-managed disks */
>>   static int sdeb_zbc_zone_size_mb;
>>   static int sdeb_zbc_max_open = DEF_ZBC_MAX_OPEN_ZONES;
>>   static int sdeb_zbc_nr_conv = DEF_ZBC_NR_CONV_ZONES;
>> @@ -1500,13 +1510,15 @@ static int inquiry_vpd_b0(unsigned char *arr)
>>   }
>>   
>>   /* Block device characteristics VPD page (SBC-3) */
>> -static int inquiry_vpd_b1(unsigned char *arr)
>> +static int inquiry_vpd_b1(struct sdebug_dev_info *devip, unsigned char *arr)
>>   {
>>   	memset(arr, 0, 0x3c);
>>   	arr[0] = 0;
>>   	arr[1] = 1;	/* non rotating medium (e.g. solid state) */
>>   	arr[2] = 0;
>>   	arr[3] = 5;	/* less than 1.8" */
>> +	if (devip->zmodel == BLK_ZONED_HA)
>> +		arr[4] = 1 << 4;	/* zoned field = 01b */
>>   
>>   	return 0x3c;
>>   }
>> @@ -1543,7 +1555,7 @@ static int inquiry_vpd_b6(struct sdebug_dev_info *devip, unsigned char *arr)
>>   	 */
>>   	put_unaligned_be32(0xffffffff, &arr[4]);
>>   	put_unaligned_be32(0xffffffff, &arr[8]);
>> -	if (devip->max_open)
>> +	if (sdeb_zbc_model == BLK_ZONED_HM && devip->max_open)
>>   		put_unaligned_be32(devip->max_open, &arr[12]);
>>   	else
>>   		put_unaligned_be32(0xffffffff, &arr[12]);
>> @@ -1566,7 +1578,7 @@ static int resp_inquiry(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
>>   	if (! arr)
>>   		return DID_REQUEUE << 16;
>>   	is_disk = (sdebug_ptype == TYPE_DISK);
>> -	is_zbc = (sdebug_ptype == TYPE_ZBC);
>> +	is_zbc = (devip->zmodel != BLK_ZONED_NONE);
>>   	is_disk_zbc = (is_disk || is_zbc);
>>   	have_wlun = scsi_is_wlun(scp->device->lun);
>>   	if (have_wlun)
>> @@ -1611,7 +1623,7 @@ static int resp_inquiry(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
>>   				arr[n++] = 0xb1;  /* Block characteristics */
>>   				if (is_disk)
>>   					arr[n++] = 0xb2;  /* LB Provisioning */
>> -				else if (is_zbc)
>> +				if (is_zbc)
>>   					arr[n++] = 0xb6;  /* ZB dev. char. */
>>   			}
>>   			arr[3] = n - 4;	  /* number of supported VPD pages */
>> @@ -1660,7 +1672,7 @@ static int resp_inquiry(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
>>   			arr[3] = inquiry_vpd_b0(&arr[4]);
>>   		} else if (is_disk_zbc && 0xb1 == cmd[2]) { /* Block char. */
>>   			arr[1] = cmd[2];        /*sanity */
>> -			arr[3] = inquiry_vpd_b1(&arr[4]);
>> +			arr[3] = inquiry_vpd_b1(devip, &arr[4]);
>>   		} else if (is_disk && 0xb2 == cmd[2]) { /* LB Prov. */
>>   			arr[1] = cmd[2];        /*sanity */
>>   			arr[3] = inquiry_vpd_b2(&arr[4]);
>> @@ -2305,7 +2317,7 @@ static int resp_mode_sense(struct scsi_cmnd *scp,
>>   	msense_6 = (MODE_SENSE == cmd[0]);
>>   	llbaa = msense_6 ? false : !!(cmd[1] & 0x10);
>>   	is_disk = (sdebug_ptype == TYPE_DISK);
>> -	is_zbc = (sdebug_ptype == TYPE_ZBC);
>> +	is_zbc = (devip->zmodel != BLK_ZONED_NONE);
>>   	if ((is_disk || is_zbc) && !dbd)
>>   		bd_len = llbaa ? 16 : 8;
>>   	else
>> @@ -2656,7 +2668,7 @@ static struct sdeb_zone_state *zbc_zone(struct sdebug_dev_info *devip,
>>   
>>   static inline bool zbc_zone_is_conv(struct sdeb_zone_state *zsp)
>>   {
>> -	return zsp->z_cond == ZBC_NOT_WRITE_POINTER;
>> +	return zsp->z_type == ZBC_ZONE_TYPE_CNV;
>>   }
>>   
>>   static void zbc_close_zone(struct sdebug_dev_info *devip,
>> @@ -2732,13 +2744,42 @@ static void zbc_inc_wp(struct sdebug_dev_info *devip,
>>   		       unsigned long long lba, unsigned int num)
>>   {
>>   	struct sdeb_zone_state *zsp = zbc_zone(devip, lba);
>> +	unsigned long long n, end, zend = zsp->z_start + zsp->z_size;
>>   
>>   	if (zbc_zone_is_conv(zsp))
>>   		return;
>>   
>> -	zsp->z_wp += num;
>> -	if (zsp->z_wp >= zsp->z_start + zsp->z_size)
>> -		zsp->z_cond = ZC5_FULL;
>> +	if (zsp->z_type == ZBC_ZONE_TYPE_SWR) {
>> +		zsp->z_wp += num;
>> +		if (zsp->z_wp >= zend)
>> +			zsp->z_cond = ZC5_FULL;
>> +		return;
>> +	}
>> +
>> +	while (num) {
>> +		if (lba != zsp->z_wp)
>> +			zsp->z_non_seq_resource = true;
>> +
>> +		end = lba + num;
>> +		if (end >= zend) {
>> +			n = zend - lba;
>> +			zsp->z_wp = zend;
>> +		} else if (end > zsp->z_wp) {
>> +			n = num;
>> +			zsp->z_wp = end;
>> +		} else {
>> +			n = num;
>> +		}
>> +		if (zsp->z_wp >= zend)
>> +			zsp->z_cond = ZC5_FULL;
>> +
>> +		num -= n;
>> +		lba += n;
>> +		if (num) {
>> +			zsp++;
>> +			zend = zsp->z_start + zsp->z_size;
>> +		}
>> +	}
>>   }
>>   
>>   static int check_zbc_access_params(struct scsi_cmnd *scp,
>> @@ -2750,7 +2791,9 @@ static int check_zbc_access_params(struct scsi_cmnd *scp,
>>   	struct sdeb_zone_state *zsp_end = zbc_zone(devip, lba + num - 1);
>>   
>>   	if (!write) {
>> -		/* Reads cannot cross zone types boundaries */
>> +		if (devip->zmodel == BLK_ZONED_HA)
>> +			return 0;
>> +		/* For host-managed, reads cannot cross zone types boundaries */
>>   		if (zsp_end != zsp &&
>>   		    zbc_zone_is_conv(zsp) &&
>>   		    !zbc_zone_is_conv(zsp_end)) {
>> @@ -2773,25 +2816,27 @@ static int check_zbc_access_params(struct scsi_cmnd *scp,
>>   		return 0;
>>   	}
>>   
>> -	/* Writes cannot cross sequential zone boundaries */
>> -	if (zsp_end != zsp) {
>> -		mk_sense_buffer(scp, ILLEGAL_REQUEST,
>> -				LBA_OUT_OF_RANGE,
>> -				WRITE_BOUNDARY_ASCQ);
>> -		return check_condition_result;
>> -	}
>> -	/* Cannot write full zones */
>> -	if (zsp->z_cond == ZC5_FULL) {
>> -		mk_sense_buffer(scp, ILLEGAL_REQUEST,
>> -				INVALID_FIELD_IN_CDB, 0);
>> -		return check_condition_result;
>> -	}
>> -	/* Writes must be aligned to the zone WP */
>> -	if (lba != zsp->z_wp) {
>> -		mk_sense_buffer(scp, ILLEGAL_REQUEST,
>> -				LBA_OUT_OF_RANGE,
>> -				UNALIGNED_WRITE_ASCQ);
>> -		return check_condition_result;
>> +	if (zsp->z_type == ZBC_ZONE_TYPE_SWR) {
>> +		/* Writes cannot cross sequential zone boundaries */
>> +		if (zsp_end != zsp) {
>> +			mk_sense_buffer(scp, ILLEGAL_REQUEST,
>> +					LBA_OUT_OF_RANGE,
>> +					WRITE_BOUNDARY_ASCQ);
>> +			return check_condition_result;
>> +		}
>> +		/* Cannot write full zones */
>> +		if (zsp->z_cond == ZC5_FULL) {
>> +			mk_sense_buffer(scp, ILLEGAL_REQUEST,
>> +					INVALID_FIELD_IN_CDB, 0);
>> +			return check_condition_result;
>> +		}
>> +		/* Writes must be aligned to the zone WP */
>> +		if (lba != zsp->z_wp) {
>> +			mk_sense_buffer(scp, ILLEGAL_REQUEST,
>> +					LBA_OUT_OF_RANGE,
>> +					UNALIGNED_WRITE_ASCQ);
>> +			return check_condition_result;
>> +		}
>>   	}
>>   
>>   	/* Handle implicit open of closed and empty zones */
>> @@ -4312,13 +4357,16 @@ static int resp_report_zones(struct scsi_cmnd *scp,
>>   		case 0x06:
>>   		case 0x07:
>>   		case 0x10:
>> -		case 0x11:
>>   			/*
>> -			 * Read-only, offline, reset WP recommended and
>> -			 * non-seq-resource-used are not emulated: no zones
>> -			 * to report;
>> +			 * Read-only, offline, reset WP recommended are
>> +			 * not emulated: no zones to report;
>>   			 */
>>   			continue;
>> +		case 0x11:
>> +			/* non-seq-resource set */
>> +			if (!zsp->z_non_seq_resource)
>> +				continue;
>> +			break;
>>   		case 0x3f:
>>   			/* Not write pointer (conventional) zones */
>>   			if (!zbc_zone_is_conv(zsp))
>> @@ -4333,11 +4381,10 @@ static int resp_report_zones(struct scsi_cmnd *scp,
>>   
>>   		if (nrz < rep_max_zones) {
>>   			/* Fill zone descriptor */
>> -			if (zbc_zone_is_conv(zsp))
>> -				desc[0] = 0x1;
>> -			else
>> -				desc[0] = 0x2;
>> +			desc[0] = zsp->z_type;
>>   			desc[1] = zsp->z_cond << 4;
>> +			if (zsp->z_non_seq_resource)
>> +				desc[1] |= 1 << 1;
>>   			put_unaligned_be64((u64)zsp->z_size, desc + 8);
>>   			put_unaligned_be64((u64)zsp->z_start, desc + 16);
>>   			put_unaligned_be64((u64)zsp->z_wp, desc + 24);
>> @@ -4591,6 +4638,7 @@ static void zbc_rwp_zone(struct sdebug_dev_info *devip,
>>   	if (zsp->z_cond == ZC4_CLOSED)
>>   		devip->nr_closed--;
>>   
>> +	zsp->z_non_seq_resource = false;
>>   	zsp->z_wp = zsp->z_start;
>>   	zsp->z_cond = ZC1_EMPTY;
>>   }
>> @@ -4796,11 +4844,13 @@ static int sdebug_device_create_zones(struct sdebug_dev_info *devip)
>>   	}
>>   	devip->nr_conv_zones = sdeb_zbc_nr_conv;
>>   
>> -	/* zbc_max_open_zones can be 0, meaning "not reported" (no limit) */
>> -	if (sdeb_zbc_max_open >= devip->nr_zones - 1)
>> -		devip->max_open = (devip->nr_zones - 1) / 2;
>> -	else
>> -		devip->max_open = sdeb_zbc_max_open;
>> +	if (devip->zmodel == BLK_ZONED_HM) {
>> +		/* zbc_max_open_zones can be 0, meaning "not reported" */
>> +		if (sdeb_zbc_max_open >= devip->nr_zones - 1)
>> +			devip->max_open = (devip->nr_zones - 1) / 2;
>> +		else
>> +			devip->max_open = sdeb_zbc_max_open;
>> +	}
>>   
>>   	devip->zstate = kcalloc(devip->nr_zones,
>>   				sizeof(struct sdeb_zone_state), GFP_KERNEL);
>> @@ -4813,9 +4863,14 @@ static int sdebug_device_create_zones(struct sdebug_dev_info *devip)
>>   		zsp->z_start = zstart;
>>   
>>   		if (i < devip->nr_conv_zones) {
>> +			zsp->z_type = ZBC_ZONE_TYPE_CNV;
>>   			zsp->z_cond = ZBC_NOT_WRITE_POINTER;
>>   			zsp->z_wp = (sector_t)-1;
>>   		} else {
>> +			if (devip->zmodel == BLK_ZONED_HM)
>> +				zsp->z_type = ZBC_ZONE_TYPE_SWR;
>> +			else
>> +				zsp->z_type = ZBC_ZONE_TYPE_SWP;
>>   			zsp->z_cond = ZC1_EMPTY;
>>   			zsp->z_wp = zsp->z_start;
>>   		}
>> @@ -4851,10 +4906,13 @@ static struct sdebug_dev_info *sdebug_device_create(
>>   		}
>>   		devip->sdbg_host = sdbg_host;
>>   		if (sdeb_zbc_in_use) {
>> +			devip->zmodel = sdeb_zbc_model;
>>   			if (sdebug_device_create_zones(devip)) {
>>   				kfree(devip);
>>   				return NULL;
>>   			}
>> +		} else {
>> +			devip->zmodel = BLK_ZONED_NONE;
>>   		}
>>   		devip->sdbg_host = sdbg_host;
>>   		list_add_tail(&devip->dev_list, &sdbg_host->dev_info_list);
>> @@ -6566,12 +6624,12 @@ static int __init scsi_debug_init(void)
>>   		sdeb_zbc_model = k;
>>   		switch (sdeb_zbc_model) {
>>   		case BLK_ZONED_NONE:
>> +		case BLK_ZONED_HA:
>>   			sdebug_ptype = TYPE_DISK;
>>   			break;
>>   		case BLK_ZONED_HM:
>>   			sdebug_ptype = TYPE_ZBC;
>>   			break;
>> -		case BLK_ZONED_HA:
>>   		default:
>>   			pr_err("Invalid ZBC model\n");
>>   			return -EINVAL;
>>
> 
> 


-- 
Damien Le Moal
Western Digital Research

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

* Re: [PATCH 0/7] scsi_debug: Add ZBC support
  2020-04-22 10:42 [PATCH 0/7] scsi_debug: Add ZBC support Damien Le Moal
                   ` (6 preceding siblings ...)
  2020-04-22 10:42 ` [PATCH 7/7] scsi_debug: implement zbc host-aware emulation Damien Le Moal
@ 2020-05-05  4:41 ` Martin K. Petersen
  7 siblings, 0 replies; 12+ messages in thread
From: Martin K. Petersen @ 2020-05-05  4:41 UTC (permalink / raw)
  To: Damien Le Moal, linux-scsi; +Cc: Martin K . Petersen, Douglas Gilbert

On Wed, 22 Apr 2020 19:42:14 +0900, Damien Le Moal wrote:

> This is the second part of the scsi_debug updates implementing ZBC
> support on top of Doug "per_host_store+random parameters, compare" patch
> series.
> 
> The ZBC emulation implemented allows to emulate both host-managed and
> host-aware disks with configurable zone size, number of conventional
> zones znd maximum number of open zones. One feature missing is the
> emulation of ZBC RC_BASIS which changes the behavior of the READ
> CAPACITY command. This is however not a critical point for testing as,
> to my knowledge, there are no disks using RC_BASIS on the market today.
> RC_BASIS emulation can thus be added as a later patch.
> 
> [...]

Applied to 5.8/scsi-queue, thanks!

[1/7] scsi: scsi_debug: Add ZBC mode and VPD pages
      https://git.kernel.org/mkp/scsi/c/d36da3058ced
[2/7] scsi: scsi_debug: Add ZBC zone commands
      https://git.kernel.org/mkp/scsi/c/f0d1cf9378bd
[3/7] scsi: scsi_debug: Add ZBC module parameter
      https://git.kernel.org/mkp/scsi/c/9267e0eb41fe
[4/7] scsi: scsi_debug: Add zone_max_open module parameter
      https://git.kernel.org/mkp/scsi/c/380603a5bb83
[5/7] scsi: scsi_debug: Add zone_nr_conv module parameter
      https://git.kernel.org/mkp/scsi/c/aa8fecf96b70
[6/7] scsi: scsi_debug: Add zone_size_mb module parameter
      https://git.kernel.org/mkp/scsi/c/98e0a689868c
[7/7] scsi: scsi_debug: Implement ZBC host-aware emulation
      https://git.kernel.org/mkp/scsi/c/64e14ece0700

-- 
Martin K. Petersen	Oracle Linux Engineering

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

end of thread, other threads:[~2020-05-05  4:43 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-04-22 10:42 [PATCH 0/7] scsi_debug: Add ZBC support Damien Le Moal
2020-04-22 10:42 ` [PATCH 1/7] scsi_debug: add zbc mode and VPD pages Damien Le Moal
2020-04-22 10:42 ` [PATCH 2/7] scsi_debug: add zbc zone commands Damien Le Moal
2020-04-22 10:42 ` [PATCH 3/7] scsi_debug: add zbc module parameter Damien Le Moal
2020-04-22 10:42 ` [PATCH 4/7] scsi_debug: add zone_max_open " Damien Le Moal
2020-04-22 10:42 ` [PATCH 5/7] scsi_debug: add zone_nr_conv " Damien Le Moal
2020-04-22 10:42 ` [PATCH 6/7] scsi_debug: add zone_size_mb " Damien Le Moal
2020-04-22 10:42 ` [PATCH 7/7] scsi_debug: implement zbc host-aware emulation Damien Le Moal
2020-04-22 16:46   ` Douglas Gilbert
2020-04-22 16:50     ` Martin K. Petersen
2020-04-23  1:33     ` Damien Le Moal
2020-05-05  4:41 ` [PATCH 0/7] scsi_debug: Add ZBC support Martin K. Petersen

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.