Add support for the SANITIZE opcode and implement
BLOCK_ERASE (similar to UNMAP) OVERWRITE and EXIT_FAILURE_MODE
service actions.
All commands except INQUIRY, REPORT_LUNS and REQUEST SENSE are failed
with NOT_READY/SANITIZE_IN_PGROGRESS while a sanitize is
active or a SANITIZE has failed but not yet been cleared.
Signed-off-by: Ronnie Sahlberg <ronniesahlberg@xxxxxxxxx>
---
usr/bs_rdwr.c | 181 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
usr/sbc.c | 188 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
usr/scsi.c | 5 ++
usr/scsi.h | 8 +++
usr/spc.c | 48 +++++++++++++++
usr/spc.h | 5 +-
usr/target.c | 6 ++
usr/tgtd.h | 9 +++
8 files changed, 444 insertions(+), 6 deletions(-)
diff --git a/usr/bs_rdwr.c b/usr/bs_rdwr.c
index 47d2d99..148fc72 100644
--- a/usr/bs_rdwr.c
+++ b/usr/bs_rdwr.c
@@ -58,6 +58,170 @@ static void bs_sync_sync_range(struct scsi_cmd *cmd, uint32_t length,
set_medium_error(result, key, asc);
}
+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;
+ fdatasync(cmd->dev->fd);
+ }
+ offset += count;
+ }
+}
+
+static void sanitize_block_erase(struct scsi_cmd *cmd,
+ int *result, uint8_t *key, uint16_t *asc)
+{
+ int fd = cmd->dev->fd;
+
+ cmd->dev->sanitize_state = LU_SANITIZE_RUNNING;
+ if (unmap_file_region(fd, 0, cmd->dev->size) != 0) {
+ eprintf("Failed to punch hole for"
+ " SANITIZE"
+ " length:%" PRIu64 "\n",
+ cmd->dev->size);
+ *result = SAM_STAT_CHECK_CONDITION;
+ *key = HARDWARE_ERROR;
+ *asc = ASC_INTERNAL_TGT_FAILURE;
+ cmd->dev->sanitize_state = LU_SANITIZE_FAILURE;
+ return;
+ }
+ cmd->dev->sanitize_state = LU_SANITIZE_NONE;
+}
+
+#define SANITIZE_BATCHSIZE 16384
+
+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(¶m[2]);
+
+ if (!ocount) {
+ eprintf("Invalid overwrite count for sanitize overwrite:%d\n",
+ ocount);
+
+ *result = SAM_STAT_CHECK_CONDITION;
+ *key = ILLEGAL_REQUEST;
+ *asc = ASC_INVALID_FIELD_IN_CDB;
+ return;
+ }
+
+ if (!pattern_len) {
+ eprintf("Initialization pattern length was 0\n");
+
+ *result = SAM_STAT_CHECK_CONDITION;
+ *key = ILLEGAL_REQUEST;
+ *asc = ASC_INVALID_FIELD_IN_CDB;
+ return;
+ }
+
+ if (pattern_len > blocksize) {
+ eprintf("Initialization pattern length was > blocksize. " \
+ "%d > %d\n", pattern_len, blocksize);
+
+ *result = SAM_STAT_CHECK_CONDITION;
+ *key = ILLEGAL_REQUEST;
+ *asc = ASC_INVALID_FIELD_IN_CDB;
+ return;
+ }
+
+ 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;
+ }
+
+
+ cmd->dev->sanitize_state = LU_SANITIZE_RUNNING;
+
+ /* 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);
+ cmd->dev->sanitize_state = LU_SANITIZE_NONE;
+}
+
static void bs_rdwr_request(struct scsi_cmd *cmd)
{
int ret, fd = cmd->dev->fd;
@@ -357,6 +521,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 c4f012c..1a2af48 100644
--- a/usr/sbc.c
+++ b/usr/sbc.c
@@ -66,6 +66,118 @@ 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 || cmd->dev->attrs.swp) {
+ key = DATA_PROTECT;
+ asc = ASC_WRITE_PROTECT;
+ goto sense;
+ }
+
+ if (cmd->scb[1] & 0x40
+ || cmd->scb[2]
+ || cmd->scb[3]
+ || cmd->scb[4]
+ || cmd->scb[5]
+ || cmd->scb[6])
+ 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->scb[1] & 0x40
+ || cmd->scb[2]
+ || cmd->scb[3]
+ || cmd->scb[4]
+ || cmd->scb[5]
+ || cmd->scb[6])
+ goto sense;
+
+ if (cmd->dev->attrs.readonly || cmd->dev->attrs.swp) {
+ 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;
@@ -114,6 +226,11 @@ static int sbc_mode_page_update(struct scsi_cmd *cmd, uint8_t *data, int *change
static int sbc_mode_select(int host_no, struct scsi_cmd *cmd)
{
+ if (cmd->dev->sanitize_state != LU_SANITIZE_NONE) {
+ sense_data_build(cmd, NOT_READY, ASC_SANITIZE_IN_PROGRESS);
+ return SAM_STAT_CHECK_CONDITION;
+ }
+
return spc_mode_select(host_no, cmd, sbc_mode_page_update);
}
@@ -121,6 +238,11 @@ static int sbc_mode_sense(int host_no, struct scsi_cmd *cmd)
{
int ret;
+ if (cmd->dev->sanitize_state != LU_SANITIZE_NONE) {
+ sense_data_build(cmd, NOT_READY, ASC_SANITIZE_IN_PROGRESS);
+ return SAM_STAT_CHECK_CONDITION;
+ }
+
ret = spc_mode_sense(host_no, cmd);
/*
@@ -149,6 +271,11 @@ static int sbc_format_unit(int host_no, struct scsi_cmd *cmd)
uint16_t asc = ASC_INVALID_FIELD_IN_CDB;
int ret;
+ if (cmd->dev->sanitize_state != LU_SANITIZE_NONE) {
+ sense_data_build(cmd, NOT_READY, ASC_SANITIZE_IN_PROGRESS);
+ return SAM_STAT_CHECK_CONDITION;
+ }
+
ret = device_reserved(cmd);
if (ret)
return SAM_STAT_RESERVATION_CONFLICT;
@@ -193,6 +320,11 @@ static int sbc_unmap(int host_no, struct scsi_cmd *cmd)
struct scsi_lu *lu = cmd->dev;
int anchor;
+ if (cmd->dev->sanitize_state != LU_SANITIZE_NONE) {
+ sense_data_build(cmd, NOT_READY, ASC_SANITIZE_IN_PROGRESS);
+ return SAM_STAT_CHECK_CONDITION;
+ }
+
ret = device_reserved(cmd);
if (ret)
return SAM_STAT_RESERVATION_CONFLICT;
@@ -248,6 +380,11 @@ static int sbc_rw(int host_no, struct scsi_cmd *cmd)
uint16_t asc = ASC_LUN_NOT_SUPPORTED;
struct scsi_lu *lu = cmd->dev;
+ if (cmd->dev->sanitize_state != LU_SANITIZE_NONE) {
+ sense_data_build(cmd, NOT_READY, ASC_SANITIZE_IN_PROGRESS);
+ return SAM_STAT_CHECK_CONDITION;
+ }
+
ret = device_reserved(cmd);
if (ret)
return SAM_STAT_RESERVATION_CONFLICT;
@@ -401,6 +538,11 @@ sense:
static int sbc_reserve(int host_no, struct scsi_cmd *cmd)
{
+ if (cmd->dev->sanitize_state != LU_SANITIZE_NONE) {
+ sense_data_build(cmd, NOT_READY, ASC_SANITIZE_IN_PROGRESS);
+ return SAM_STAT_CHECK_CONDITION;
+ }
+
if (device_reserve(cmd))
return SAM_STAT_RESERVATION_CONFLICT ;
else
@@ -411,6 +553,11 @@ static int sbc_release(int host_no, struct scsi_cmd *cmd)
{
int ret;
+ if (cmd->dev->sanitize_state != LU_SANITIZE_NONE) {
+ sense_data_build(cmd, NOT_READY, ASC_SANITIZE_IN_PROGRESS);
+ return SAM_STAT_CHECK_CONDITION;
+ }
+
ret = device_release(cmd->c_target->tid, cmd->cmd_itn_id,
cmd->dev->lun, 0);
@@ -426,6 +573,11 @@ static int sbc_read_capacity(int host_no, struct scsi_cmd *cmd)
unsigned char key = ILLEGAL_REQUEST;
uint16_t asc = ASC_LUN_NOT_SUPPORTED;
+ if (cmd->dev->sanitize_state != LU_SANITIZE_NONE) {
+ sense_data_build(cmd, NOT_READY, ASC_SANITIZE_IN_PROGRESS);
+ return SAM_STAT_CHECK_CONDITION;
+ }
+
if (cmd->dev->attrs.removable && !cmd->dev->attrs.online) {
key = NOT_READY;
asc = ASC_MEDIUM_NOT_PRESENT;
@@ -466,6 +618,11 @@ static int sbc_verify(int host_no, struct scsi_cmd *cmd)
uint64_t lba;
uint32_t tl;
+ if (cmd->dev->sanitize_state != LU_SANITIZE_NONE) {
+ sense_data_build(cmd, NOT_READY, ASC_SANITIZE_IN_PROGRESS);
+ return SAM_STAT_CHECK_CONDITION;
+ }
+
if (cmd->dev->attrs.removable && !cmd->dev->attrs.online) {
key = NOT_READY;
asc = ASC_MEDIUM_NOT_PRESENT;
@@ -661,6 +818,24 @@ static int sbc_service_action(int host_no, struct scsi_cmd *cmd)
struct service_action *service_action, *actions;
action = cmd->scb[1] & 0x1f;
+
+ /* Active or failed sanitize operation */
+ if (cmd->dev->sanitize_state != LU_SANITIZE_NONE) {
+ /* Allow SANITIZE/EXIT_FAILURE_MODE to clear a failure
+ * but fail everything else.
+ */
+ if (cmd->dev->sanitize_state == LU_SANITIZE_FAILURE
+ && op == SANITIZE && action == SA_EXIT_FAILURE_MODE) {
+ cmd->dev->sanitize_state = LU_SANITIZE_NONE;
+ return SAM_STAT_GOOD;
+ } else {
+ sense_data_build(cmd, NOT_READY,
+ ASC_SANITIZE_IN_PROGRESS);
+ return SAM_STAT_CHECK_CONDITION;
+ }
+ }
+
+
actions = cmd->dev->dev_type_template.ops[op].service_actions;
service_action = find_service_action(actions, action);
@@ -681,6 +856,11 @@ static int sbc_sync_cache(int host_no, struct scsi_cmd *cmd)
uint8_t key = ILLEGAL_REQUEST;
uint16_t asc = ASC_LUN_NOT_SUPPORTED;
+ if (cmd->dev->sanitize_state != LU_SANITIZE_NONE) {
+ sense_data_build(cmd, NOT_READY, ASC_SANITIZE_IN_PROGRESS);
+ return SAM_STAT_CHECK_CONDITION;
+ }
+
if (device_reserved(cmd))
return SAM_STAT_RESERVATION_CONFLICT;
@@ -864,7 +1044,7 @@ static struct device_type_template sbc_template = {
{spc_illegal_op,},
{spc_illegal_op,},
- {spc_illegal_op,},
+ {sbc_service_action, sanitize_actions,},
{spc_illegal_op,},
{spc_illegal_op,},
{spc_illegal_op,},
@@ -889,8 +1069,8 @@ static struct device_type_template sbc_template = {
{spc_illegal_op,},
{spc_illegal_op,},
{spc_illegal_op,},
- {spc_service_action, persistent_reserve_in_actions,},
- {spc_service_action, persistent_reserve_out_actions,},
+ {sbc_service_action, persistent_reserve_in_actions,},
+ {sbc_service_action, persistent_reserve_out_actions,},
[0x60 ... 0x7f] = {spc_illegal_op,},
@@ -937,7 +1117,7 @@ static struct device_type_template sbc_template = {
{spc_report_luns,},
{spc_illegal_op,},
{spc_illegal_op,},
- {spc_service_action, maint_in_service_actions,},
+ {sbc_service_action, maint_in_service_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..9dbd9f3 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
@@ -178,6 +179,7 @@
#define ASC_CAUSE_NOT_REPORTABLE 0x0400
#define ASC_BECOMING_READY 0x0401
#define ASC_INITIALIZING_REQUIRED 0x0402
+#define ASC_SANITIZE_IN_PROGRESS 0x041b
#define ASC_CLEANING_CART_INSTALLED 0x3003
#define ASC_CLEANING_FAILURE 0x3007
#define ASC_MEDIUM_NOT_PRESENT 0x3a00
@@ -270,4 +272,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 15077ca..8551ea2 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 == 01b */
+ data[3] = 0x40;
+
+ /* 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)];
@@ -373,6 +393,11 @@ int spc_start_stop(int host_no, struct scsi_cmd *cmd)
uint8_t *scb = cmd->scb;
int start, loej, pwrcnd;
+ if (cmd->dev->sanitize_state != LU_SANITIZE_NONE) {
+ sense_data_build(cmd, NOT_READY, ASC_SANITIZE_IN_PROGRESS);
+ return SAM_STAT_CHECK_CONDITION;
+ }
+
scsi_set_in_resid_by_actual(cmd, 0);
if (device_reserved(cmd))
@@ -410,6 +435,10 @@ int spc_test_unit(int host_no, struct scsi_cmd *cmd)
{
/* how should we test a backing-storage file? */
+ if (cmd->dev->sanitize_state != LU_SANITIZE_NONE) {
+ sense_data_build(cmd, NOT_READY, ASC_SANITIZE_IN_PROGRESS);
+ return SAM_STAT_CHECK_CONDITION;
+ }
if (device_reserved(cmd))
return SAM_STAT_RESERVATION_CONFLICT;
if (cmd->dev->attrs.online)
@@ -427,6 +456,11 @@ int spc_prevent_allow_media_removal(int host_no, struct scsi_cmd *cmd)
uint8_t *scb = cmd->scb;
struct it_nexus_lu_info *itn_lu_info = cmd->itn_lu_info;
+ if (cmd->dev->sanitize_state != LU_SANITIZE_NONE) {
+ sense_data_build(cmd, NOT_READY, ASC_SANITIZE_IN_PROGRESS);
+ return SAM_STAT_CHECK_CONDITION;
+ }
+
if (device_reserved(cmd))
return SAM_STAT_RESERVATION_CONFLICT;
@@ -934,6 +968,12 @@ int spc_send_diagnostics(int host_no, struct scsi_cmd *cmd)
uint16_t asc = ASC_INVALID_FIELD_IN_CDB;
uint8_t key = ILLEGAL_REQUEST;
+ if (cmd->dev->sanitize_state != LU_SANITIZE_NONE) {
+ key = NOT_READY;
+ asc = ASC_SANITIZE_IN_PROGRESS;
+ goto sense;
+ }
+
/* we only support SELF-TEST==1 */
if (!(cmd->scb[1] & 0x04))
goto sense;
@@ -2067,6 +2107,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/target.c b/usr/target.c
index b1729b3..c7d210b 100644
--- a/usr/target.c
+++ b/usr/target.c
@@ -588,6 +588,12 @@ tgtadm_err tgt_device_create(int tid, int dev_type, uint64_t lun, char *params,
lu->prgeneration = 0;
lu->pr_holder = NULL;
+ /* TODO: Here we should really read this from stable storage since
+ * active/failed sanitize state are supposed to survive across
+ * target resets/reboots.
+ */
+ lu->sanitize_state = LU_SANITIZE_NONE;
+
lu->cmd_perform = &target_cmd_perform;
lu->cmd_done = &__cmd_done;
diff --git a/usr/tgtd.h b/usr/tgtd.h
index 484e6e9..2b795a5 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
@@ -187,6 +188,12 @@ struct registration {
uint8_t pr_type;
};
+enum lu_sanitize_state {
+ LU_SANITIZE_NONE = 1,
+ LU_SANITIZE_RUNNING,
+ LU_SANITIZE_FAILURE,
+};
+
struct scsi_lu {
int fd;
uint64_t addr; /* persistent mapped address */
@@ -217,6 +224,8 @@ struct scsi_lu {
struct lu_phy_attr attrs;
+ enum lu_sanitize_state sanitize_state;
+
struct list_head registration_list;
uint32_t prgeneration;
struct registration *pr_holder;