[stgt] [PATCH] Add initial support for SANITIZE

Ronnie Sahlberg ronniesahlberg at gmail.com
Sun May 26 20:59:56 CEST 2013


Add two service actions for SANITIZE:
BLOCK ERASE and OVERWRITE

Add VPD page 0xB1 so we can tell the initiator that blocks can be read
after sanitize without need for first being written by the initiator.

We do not yet support IMMED, all SANITIZE run synchronously.
Thus we do not yet support reading the status with REPORT SENSE or
using EXIT FAILURE MODE.

Later patches can add starting and running the sanitize operations as
a dedicated thread.

Commands to generate SANITIZE operations and test against tgtd are
in libiscsi as :
    iscsi-test-cu iscsi://127.0.0.1/iqn.ronnie.test/1 --test SCSI.Sanitize -V --allow-sanitize

Signed-off-by: Ronnie Sahlberg <ronniesahlberg at gmail.com>
---
 usr/bs_rdwr.c |  144 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 usr/sbc.c     |   98 ++++++++++++++++++++++++++++++++++++++-
 usr/scsi.c    |    5 ++
 usr/scsi.h    |    7 +++
 usr/spc.c     |   28 +++++++++++
 usr/spc.h     |    5 +-
 usr/tgtd.h    |    1 +
 7 files changed, 285 insertions(+), 3 deletions(-)

diff --git a/usr/bs_rdwr.c b/usr/bs_rdwr.c
index 47d2d99..32165eb 100644
--- a/usr/bs_rdwr.c
+++ b/usr/bs_rdwr.c
@@ -58,6 +58,133 @@ static void bs_sync_sync_range(struct scsi_cmd *cmd, uint32_t length,
 		set_medium_error(result, key, asc);
 }
 
+#define SANITIZE_BATCHSIZE 16384
+
+static void sanitize_write_buffer(struct scsi_cmd *cmd,
+		int buf_size, const char *buf,
+		int *result, uint8_t *key, uint16_t *asc)
+{
+	uint64_t offset = 0;
+
+	while (offset != cmd->dev->size) {
+		size_t count, pos;
+
+		if (cmd->dev->size - offset > buf_size)
+			count = buf_size;
+		else
+			count = cmd->dev->size - offset;
+
+		pos = 0;
+		while (pos != count) {
+			size_t written;
+
+			written = pwrite64(cmd->dev->fd,
+					buf + pos, count - pos, offset + pos);
+			if (written == -1) {
+				eprintf("Write failed during sanitize\n");
+				*result = SAM_STAT_CHECK_CONDITION;
+				*key = MEDIUM_ERROR;
+				*asc = ASC_WRITE_ERROR;
+				return;
+			}
+			pos += written;
+		}
+		offset += count;
+	}
+}
+
+static void sanitize_block_erase(struct scsi_cmd *cmd,
+		int *result, uint8_t *key, uint16_t *asc)
+{
+	int blocksize = 1 << cmd->dev->blk_shift;
+	char *buf;
+
+	/* allocate a bunch of blocks so we can batch
+	 * writes to the medium.
+	 */
+	buf = malloc(blocksize * SANITIZE_BATCHSIZE);
+	memset(buf, 0, blocksize * SANITIZE_BATCHSIZE);
+
+	sanitize_write_buffer(cmd, blocksize * SANITIZE_BATCHSIZE, buf,
+		 result, key, asc);
+	free(buf);
+}
+
+static void sanitize_overwrite(struct scsi_cmd *cmd,
+		int *result, uint8_t *key, uint16_t *asc)
+{
+	int blocksize = 1 << cmd->dev->blk_shift;
+	char *buf;
+	int param_len;
+	char *param = scsi_get_out_buffer(cmd);
+	int invert, ocount, pattern_len;
+	int i, offset;
+
+	param_len = scsi_get_out_length(cmd);
+	if (param_len <= 4 || param_len >= blocksize + 5) {
+		eprintf("Invalid data-out size for sanitize overwrite:%d\n",
+			param_len);
+
+		result = SAM_STAT_CHECK_CONDITION;
+		key = ILLEGAL_REQUEST;
+		asc = ASC_INVALID_FIELD_IN_CDB;
+		return;
+	}
+
+	invert = !!(param[0] & 0x80);
+	ocount = param[0] & 0x1f;
+	pattern_len = get_unaligned_be16(&param[2]);
+
+	if (pattern_len + 4 != param_len) {
+		eprintf("Initialization pattern length does not match"
+			" data-out size. %d but data-out is %d\n",
+			pattern_len, param_len);
+
+		result = SAM_STAT_CHECK_CONDITION;
+		key = ILLEGAL_REQUEST;
+		asc = ASC_INVALID_FIELD_IN_CDB;
+		return;
+	}
+
+	/* allocate a bunch of blocks so we can batch
+	 * writes to the medium.
+	 */
+	buf = malloc(blocksize * SANITIZE_BATCHSIZE);
+
+	/* and fill it with the initialization pattern */
+	offset = 0;
+	while (offset != blocksize * SANITIZE_BATCHSIZE) {
+		int count;
+
+		count = blocksize * SANITIZE_BATCHSIZE - offset;
+		if (count > pattern_len)
+			count = pattern_len;
+
+		memcpy(buf + offset, param + 4, count);
+		offset += count;
+	}
+
+	if (invert && !(ocount & 0x01)) {
+		int j;
+		for (j = 0; j < blocksize * SANITIZE_BATCHSIZE; j++)
+			buf[j] = ~buf[j];
+	}
+
+	for (i = 0; i < ocount; i++) {
+		sanitize_write_buffer(cmd, blocksize * SANITIZE_BATCHSIZE,
+			buf,
+			result, key, asc);
+
+		if (invert) {
+			int j;
+			for (j = 0; j < blocksize * SANITIZE_BATCHSIZE; j++)
+				buf[j] = ~buf[j];
+		}
+	}
+
+	free(buf);
+}
+
 static void bs_rdwr_request(struct scsi_cmd *cmd)
 {
 	int ret, fd = cmd->dev->fd;
@@ -357,6 +484,23 @@ verify:
 			tmpbuf += 16;
 		}
 		break;
+	case SANITIZE:
+		switch (cmd->scb[1] & 0x1f) {
+		case SA_BLOCK_ERASE:
+			sanitize_block_erase(cmd, &result, &key, &asc);
+			break;
+		case SA_OVERWRITE:
+			sanitize_overwrite(cmd, &result, &key, &asc);
+			break;
+		default:
+			eprintf("Invalid sanitize service action %d",
+				cmd->scb[1] & 0x1f);
+
+			result = SAM_STAT_CHECK_CONDITION;
+			key = ILLEGAL_REQUEST;
+			asc = ASC_INVALID_FIELD_IN_CDB;
+		}
+		break;
 	default:
 		break;
 	}
diff --git a/usr/sbc.c b/usr/sbc.c
index 1f50191..4fee287 100644
--- a/usr/sbc.c
+++ b/usr/sbc.c
@@ -66,6 +66,102 @@ static off_t find_next_hole(struct scsi_lu *dev, off_t offset)
 #endif
 }
 
+static int sbc_sanitize_overwrite(int host_no, struct scsi_cmd *cmd)
+{
+	int ret;
+	uint16_t param_len;
+	uint16_t asc = ASC_INVALID_FIELD_IN_CDB;
+	uint8_t key = ILLEGAL_REQUEST;
+
+	ret = device_reserved(cmd);
+	if (ret)
+		return SAM_STAT_RESERVATION_CONFLICT;
+
+	if (!cmd->dev->attrs.online) {
+		key = NOT_READY;
+		asc = ASC_MEDIUM_NOT_PRESENT;
+		goto sense;
+	}
+
+	if (cmd->dev->attrs.readonly) {
+		key = DATA_PROTECT;
+		asc = ASC_WRITE_PROTECT;
+		goto sense;
+	}
+
+	param_len = (uint16_t)get_unaligned_be16(&cmd->scb[7]);
+	if (param_len <= 4)
+		goto sense;
+	if (param_len >=  (1 << cmd->dev->blk_shift) + 5)
+		goto sense;
+
+	if (scsi_get_out_length(cmd) != param_len)
+		goto sense;
+
+	scsi_set_out_resid_by_actual(cmd, param_len);
+
+	ret = cmd->dev->bst->bs_cmd_submit(cmd);
+	if (ret) {
+		key = HARDWARE_ERROR;
+		asc = ASC_INTERNAL_TGT_FAILURE;
+	} else
+		return SAM_STAT_GOOD;
+
+sense:
+	scsi_set_in_resid_by_actual(cmd, 0);
+	sense_data_build(cmd, key, asc);
+	return SAM_STAT_CHECK_CONDITION;
+}
+
+static int sbc_sanitize_block_erase(int host_no, struct scsi_cmd *cmd)
+{
+	int ret;
+	uint16_t param_len;
+	uint16_t asc = ASC_INVALID_FIELD_IN_CDB;
+	uint8_t key = ILLEGAL_REQUEST;
+
+	ret = device_reserved(cmd);
+	if (ret)
+		return SAM_STAT_RESERVATION_CONFLICT;
+
+	if (!cmd->dev->attrs.online) {
+		key = NOT_READY;
+		asc = ASC_MEDIUM_NOT_PRESENT;
+		goto sense;
+	}
+
+	if (cmd->dev->attrs.readonly) {
+		key = DATA_PROTECT;
+		asc = ASC_WRITE_PROTECT;
+		goto sense;
+	}
+
+	param_len = (uint16_t)get_unaligned_be16(&cmd->scb[7]);
+	if (param_len != 0)
+		goto sense;
+
+	if (scsi_get_in_length(cmd) != param_len)
+		goto sense;
+
+	ret = cmd->dev->bst->bs_cmd_submit(cmd);
+	if (ret) {
+		key = HARDWARE_ERROR;
+		asc = ASC_INTERNAL_TGT_FAILURE;
+	} else
+		return SAM_STAT_GOOD;
+
+sense:
+	scsi_set_in_resid_by_actual(cmd, 0);
+	sense_data_build(cmd, key, asc);
+	return SAM_STAT_CHECK_CONDITION;
+}
+
+struct service_action sanitize_actions[] = {
+	{SA_OVERWRITE, sbc_sanitize_overwrite},
+	{SA_BLOCK_ERASE, sbc_sanitize_block_erase},
+	{0, NULL},
+};
+
 static int sbc_mode_page_update(struct scsi_cmd *cmd, uint8_t *data, int *changed)
 {
 	uint8_t pcode = data[0] & 0x3f;
@@ -839,7 +935,7 @@ static struct device_type_template sbc_template = {
 		{spc_illegal_op,},
 		{spc_illegal_op,},
 
-		{spc_illegal_op,},
+		{spc_service_action, sanitize_actions,},
 		{spc_illegal_op,},
 		{spc_illegal_op,},
 		{spc_illegal_op,},
diff --git a/usr/scsi.c b/usr/scsi.c
index 2636a5c..78701be 100644
--- a/usr/scsi.c
+++ b/usr/scsi.c
@@ -119,6 +119,9 @@ const unsigned char *get_scsi_cdb_usage_data(unsigned char op, unsigned char sa)
 	static const unsigned char read_capacity[] = {
 	       0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 	       0x00, 0x07};
+	static const unsigned char sanitize[] = {
+	       0xff, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
+	       0xff, 0x07};
 
 	static const unsigned char verify_12[] = {
 	       0xff, 0xf2, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
@@ -165,6 +168,8 @@ const unsigned char *get_scsi_cdb_usage_data(unsigned char op, unsigned char sa)
 	       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x07};
 
 	switch (op) {
+	case SANITIZE:
+		return sanitize;
 	case TEST_UNIT_READY:
 		return test_unit_ready;
 	case REQUEST_SENSE:
diff --git a/usr/scsi.h b/usr/scsi.h
index 1edcfd7..53a035a 100644
--- a/usr/scsi.h
+++ b/usr/scsi.h
@@ -62,6 +62,7 @@
 #define WRITE_SAME            0x41
 #define UNMAP		      0x42
 #define READ_TOC              0x43
+#define SANITIZE              0x48
 #define LOG_SELECT            0x4c
 #define LOG_SENSE             0x4d
 #define MODE_SELECT_10        0x55
@@ -270,4 +271,10 @@
 #define PR_TYPE_WRITE_EXCLUSIVE_ALLREG		0x07
 #define PR_TYPE_EXCLUSIVE_ACCESS_ALLREG		0x08
 
+/* Sanitize service actions */
+#define SA_OVERWRITE				0x01
+#define SA_BLOCK_ERASE				0x02
+#define SA_CRYPTO_ERASE				0x03
+#define SA_EXIT_FAILURE_MODE			0x1f
+
 #endif
diff --git a/usr/spc.c b/usr/spc.c
index 074fdad..2bf4dd4 100644
--- a/usr/spc.c
+++ b/usr/spc.c
@@ -180,6 +180,26 @@ static void update_vpd_b2(struct scsi_lu *lu, void *id)
 	}
 }
 
+static void update_vpd_b1(struct scsi_lu *lu, void *id)
+{
+	struct vpd *vpd_pg = lu->attrs.lu_vpd[PCODE_OFFSET(0xb1)];
+	uint8_t	*data = vpd_pg->data;
+
+	/* medium rotation rate */
+	data[0] = 0;
+	data[1] = 0;
+
+	/* product type */
+	data[2] = 0;
+
+	/* wabereq == wacereq == 01b  form cartor not reported */
+	data[3] = 0x50;
+
+	/* fuab == 1  vbuls == 1 */
+	data[4] = 0x03;
+
+}
+
 static void update_vpd_b0(struct scsi_lu *lu, void *id)
 {
 	struct vpd *vpd_pg = lu->attrs.lu_vpd[PCODE_OFFSET(0xb0)];
@@ -2066,6 +2086,14 @@ int spc_lu_init(struct scsi_lu *lu)
 	lu_vpd[pg]->vpd_update = update_vpd_b0;
 	lu_vpd[pg]->vpd_update(lu, NULL);
 
+	/* VPD page 0xb1 BLOCK DEVICE CHARACTERISTICS*/
+	pg = PCODE_OFFSET(0xb1);
+	lu_vpd[pg] = alloc_vpd(BDC_VPD_LEN);
+	if (!lu_vpd[pg])
+		return -ENOMEM;
+	lu_vpd[pg]->vpd_update = update_vpd_b1;
+	lu_vpd[pg]->vpd_update(lu, NULL);
+
 	/* VPD page 0xb2 LOGICAL BLOCK PROVISIONING*/
 	pg = PCODE_OFFSET(0xb2);
 	lu_vpd[pg] = alloc_vpd(LBP_VPD_LEN);
diff --git a/usr/spc.h b/usr/spc.h
index 0c537e1..43d4ac6 100644
--- a/usr/spc.h
+++ b/usr/spc.h
@@ -1,8 +1,9 @@
 #ifndef __SPC_H
 #define __SPC_H
 
-extern struct service_action maint_in_service_actions[],
-	persistent_reserve_in_actions[], persistent_reserve_out_actions[];
+extern struct service_action maint_in_service_actions[];
+extern struct service_action persistent_reserve_in_actions[];
+extern struct service_action persistent_reserve_out_actions[];
 
 extern int spc_service_action(int host_no, struct scsi_cmd *cmd);
 extern int spc_inquiry(int host_no, struct scsi_cmd *cmd);
diff --git a/usr/tgtd.h b/usr/tgtd.h
index f3d9674..413c0ab 100644
--- a/usr/tgtd.h
+++ b/usr/tgtd.h
@@ -14,6 +14,7 @@ struct concat_buf;
 #define PRODUCT_ID_LEN		16
 #define PRODUCT_REV_LEN		4
 #define BLOCK_LIMITS_VPD_LEN	0x3C
+#define BDC_VPD_LEN		0x3C
 #define LBP_VPD_LEN		4
 
 #define PCODE_SHIFT		7
-- 
1.7.3.1

--
To unsubscribe from this list: send the line "unsubscribe stgt" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html



More information about the stgt mailing list