mirror of
https://github.com/SCST-project/scst.git
synced 2026-05-22 05:01:27 +00:00
Improve FORMAT UNIT
git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@4997 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
@@ -167,6 +167,8 @@ struct scst_vdisk_dev {
|
||||
struct file *fd;
|
||||
struct block_device *bdev;
|
||||
|
||||
uint64_t format_progress_to_do, format_progress_done;
|
||||
|
||||
int virt_id;
|
||||
char name[16+1]; /* Name of the virtual device,
|
||||
must be <= SCSI Model + 1 */
|
||||
@@ -1096,33 +1098,185 @@ static enum compl_status_e vdisk_exec_send_diagnostic(struct vdisk_cmd_params *p
|
||||
|
||||
static enum compl_status_e vdisk_exec_format_unit(struct vdisk_cmd_params *p)
|
||||
{
|
||||
int res = CMD_SUCCEEDED;
|
||||
struct scst_cmd *cmd = p->cmd;
|
||||
struct scst_device *dev = cmd->dev;
|
||||
struct scst_vdisk_dev *virt_dev = dev->dh_priv;
|
||||
int prot_type = 0, pinfo;
|
||||
bool immed = false;
|
||||
|
||||
if (cmd->cdb[1] & 0x10/*FMTDATA*/) {
|
||||
PRINT_ERROR("FORMAT UNIT: FMTDATA not supported (dev %s)",
|
||||
dev->virt_name);
|
||||
TRACE_ENTRY();
|
||||
|
||||
pinfo = (cmd->cdb[1] & 0xC0) >> 6;
|
||||
if (((cmd->cdb[1] & 0x10) == 0) && (pinfo != 0)) {
|
||||
/* FMTDATA zero and FMTPINFO not zero are illegal */
|
||||
scst_set_invalid_field_in_cdb(cmd, 1,
|
||||
SCST_INVAL_FIELD_BIT_OFFS_VALID | 4);
|
||||
SCST_INVAL_FIELD_BIT_OFFS_VALID | 6);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!virt_dev->thin_provisioned)
|
||||
if (cmd->cdb[1] & 0x10) { /* FMTDATA */
|
||||
int length, prot_usage;
|
||||
uint8_t *buf;
|
||||
bool err = false;
|
||||
|
||||
length = scst_get_buf_full_sense(cmd, &buf);
|
||||
TRACE_DBG("length %d", length);
|
||||
if (unlikely(length <= 0))
|
||||
goto out;
|
||||
|
||||
TRACE_BUFF_FLAG(TRACE_DEBUG, "Format buf", buf, 64);
|
||||
|
||||
if (length < 4) {
|
||||
PRINT_ERROR("FORMAT UNIT: too small parameters list "
|
||||
"header %d (dev %s)", length, dev->virt_name);
|
||||
scst_set_cmd_error(cmd,
|
||||
SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb));
|
||||
err = true;
|
||||
goto out_put;
|
||||
}
|
||||
|
||||
prot_usage = buf[0] & 7;
|
||||
immed = buf[1] & 2;
|
||||
|
||||
if ((buf[1] & 8) != 0) {
|
||||
PRINT_ERROR("FORMAT UNIT: initialization pattern not "
|
||||
"supported");
|
||||
scst_set_invalid_field_in_parm_list(cmd, 1,
|
||||
SCST_INVAL_FIELD_BIT_OFFS_VALID | 3);
|
||||
err = true;
|
||||
goto out_put;
|
||||
}
|
||||
|
||||
if (cmd->cdb[1] & 0x20) { /* LONGLIST */
|
||||
if (length < 8) {
|
||||
PRINT_ERROR("FORMAT UNIT: too small long "
|
||||
"parameters list header %d (dev %s)",
|
||||
length, dev->virt_name);
|
||||
scst_set_cmd_error(cmd,
|
||||
SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb));
|
||||
err = true;
|
||||
goto out_put;
|
||||
}
|
||||
if ((buf[3] & 0xF0) != 0) {
|
||||
PRINT_ERROR("FORMAT UNIT: P_I_INFORMATION must "
|
||||
"be 0 (dev %s)", dev->virt_name);
|
||||
scst_set_invalid_field_in_parm_list(cmd, 3,
|
||||
SCST_INVAL_FIELD_BIT_OFFS_VALID | 4);
|
||||
err = true;
|
||||
goto out_put;
|
||||
}
|
||||
if ((buf[3] & 0xF) != 0) {
|
||||
PRINT_ERROR("FORMAT UNIT: PROTECTION INTERVAL "
|
||||
"EXPONENT %d not supported (dev %s)",
|
||||
buf[3] & 0xF, dev->virt_name);
|
||||
scst_set_invalid_field_in_parm_list(cmd, 3,
|
||||
SCST_INVAL_FIELD_BIT_OFFS_VALID | 4);
|
||||
err = true;
|
||||
goto out_put;
|
||||
}
|
||||
} else {
|
||||
/* Nothing to do */
|
||||
}
|
||||
|
||||
out_put:
|
||||
scst_put_buf_full(cmd, buf);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
switch (pinfo) {
|
||||
case 0:
|
||||
switch (prot_usage) {
|
||||
case 0:
|
||||
prot_type = 0;
|
||||
break;
|
||||
default:
|
||||
scst_set_invalid_field_in_parm_list(cmd, 0,
|
||||
SCST_INVAL_FIELD_BIT_OFFS_VALID | 0);
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
switch (prot_usage) {
|
||||
default:
|
||||
scst_set_invalid_field_in_parm_list(cmd, 0,
|
||||
SCST_INVAL_FIELD_BIT_OFFS_VALID | 0);
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
switch (prot_usage) {
|
||||
case 0:
|
||||
prot_type = 1;
|
||||
break;
|
||||
default:
|
||||
scst_set_invalid_field_in_parm_list(cmd, 0,
|
||||
SCST_INVAL_FIELD_BIT_OFFS_VALID | 0);
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
switch (prot_usage) {
|
||||
case 0:
|
||||
prot_type = 2;
|
||||
break;
|
||||
case 1:
|
||||
prot_type = 3;
|
||||
break;
|
||||
default:
|
||||
scst_set_invalid_field_in_parm_list(cmd, 0,
|
||||
SCST_INVAL_FIELD_BIT_OFFS_VALID | 0);
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
sBUG_ON(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
TRACE_DBG("prot_type %d, pinfo %d, immed %d (cmd %p)", prot_type,
|
||||
pinfo, immed, cmd);
|
||||
|
||||
if (prot_type != 0) {
|
||||
PRINT_ERROR("FORMAT UNIT: DIF type %d not supported (dev %s)",
|
||||
prot_type, dev->virt_name);
|
||||
scst_set_invalid_field_in_cdb(cmd, 1,
|
||||
SCST_INVAL_FIELD_BIT_OFFS_VALID | 6);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (immed) {
|
||||
scst_cmd_get(cmd); /* to protect dev */
|
||||
cmd->completed = 1;
|
||||
cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT, SCST_CONTEXT_SAME);
|
||||
res = RUNNING_ASYNC;
|
||||
}
|
||||
|
||||
spin_lock(&virt_dev->flags_lock);
|
||||
virt_dev->format_active = 1;
|
||||
spin_unlock(&virt_dev->flags_lock);
|
||||
|
||||
vdisk_unmap_range(cmd, virt_dev, 0, virt_dev->nblocks);
|
||||
virt_dev->format_progress_done = 0;
|
||||
virt_dev->format_progress_to_do = 100;
|
||||
|
||||
if (virt_dev->thin_provisioned) {
|
||||
int rc = vdisk_unmap_range(cmd, virt_dev, 0, virt_dev->nblocks);
|
||||
if (rc != 0)
|
||||
goto finished;
|
||||
}
|
||||
|
||||
finished:
|
||||
spin_lock(&virt_dev->flags_lock);
|
||||
virt_dev->format_active = 0;
|
||||
spin_unlock(&virt_dev->flags_lock);
|
||||
|
||||
if (immed)
|
||||
scst_cmd_put(cmd);
|
||||
|
||||
out:
|
||||
return CMD_SUCCEEDED;
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
static enum compl_status_e vdisk_invalid_opcode(struct vdisk_cmd_params *p)
|
||||
@@ -2549,14 +2703,50 @@ out:
|
||||
static enum compl_status_e vdisk_exec_request_sense(struct vdisk_cmd_params *p)
|
||||
{
|
||||
struct scst_cmd *cmd = p->cmd;
|
||||
struct scst_device *dev = cmd->dev;
|
||||
struct scst_vdisk_dev *virt_dev = dev->dh_priv;
|
||||
int32_t length, sl;
|
||||
uint8_t *address;
|
||||
uint8_t b[SCST_STANDARD_SENSE_LEN];
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
sl = scst_set_sense(b, sizeof(b), cmd->dev->d_sense,
|
||||
SCST_LOAD_SENSE(scst_sense_no_sense));
|
||||
/*
|
||||
* No need to make it volatile, because at worst we will have a couple
|
||||
* of extra commands refused after formatting actually finished, which
|
||||
* is acceptable.
|
||||
*/
|
||||
if (virt_dev->format_active) {
|
||||
uint64_t d, div;
|
||||
uint16_t v;
|
||||
|
||||
div = virt_dev->format_progress_to_do >> 16;
|
||||
d = virt_dev->format_progress_done;
|
||||
do_div(d, div);
|
||||
v = d;
|
||||
|
||||
TRACE_DBG("Format progress %d", v);
|
||||
|
||||
sl = scst_set_sense(b, sizeof(b), dev->d_sense,
|
||||
SCST_LOAD_SENSE(scst_sense_format_in_progress));
|
||||
|
||||
BUILD_BUG_ON(SCST_STANDARD_SENSE_LEN < 18);
|
||||
if (dev->d_sense) {
|
||||
uint8_t *p = &b[7];
|
||||
int o = 8;
|
||||
*p += 8;
|
||||
b[o] = 2;
|
||||
b[o+1] = 6;
|
||||
b[o+4] = 0x80;
|
||||
put_unaligned_be16(v, &b[o+5]);
|
||||
} else {
|
||||
b[15] = 0x80;
|
||||
put_unaligned_be16(v, &b[16]);
|
||||
}
|
||||
TRACE_BUFF_FLAG(TRACE_DEBUG, "Format sense", b, sizeof(b));
|
||||
} else
|
||||
sl = scst_set_sense(b, sizeof(b), cmd->dev->d_sense,
|
||||
SCST_LOAD_SENSE(scst_sense_no_sense));
|
||||
|
||||
length = scst_get_buf_full_sense(cmd, &address);
|
||||
TRACE_DBG("length %d", length);
|
||||
|
||||
@@ -6394,14 +6394,13 @@ static int get_cdb_info_fmt(struct scst_cmd *cmd,
|
||||
{
|
||||
cmd->op_flags |= SCST_LBA_NOT_VALID;
|
||||
cmd->lba = 0;
|
||||
|
||||
/* It is not really supported anyway */
|
||||
cmd->data_len = 0;
|
||||
|
||||
if (cmd->cdb[1] & 0x10/*FMTDATA*/) {
|
||||
cmd->data_direction = SCST_DATA_WRITE;
|
||||
cmd->op_flags |= SCST_UNKNOWN_LENGTH;
|
||||
}
|
||||
cmd->bufflen = 4096; /* guess */
|
||||
} else
|
||||
cmd->bufflen = 0;
|
||||
cmd->data_len = cmd->bufflen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -731,16 +731,18 @@ static int scst_parse_cmd(struct scst_cmd *cmd)
|
||||
if (cmd->data_direction == SCST_DATA_BIDI)
|
||||
cmd->out_bufflen = min(cmd->expected_out_transfer_len,
|
||||
15*1024*1024);
|
||||
cmd->op_flags &= ~SCST_UNKNOWN_LENGTH;
|
||||
} else {
|
||||
PRINT_ERROR("Unknown data transfer length for opcode "
|
||||
"0x%x (handler %s, target %s)", cmd->cdb[0],
|
||||
devt->name, cmd->tgtt->name);
|
||||
PRINT_BUFFER("Failed CDB", cmd->cdb, cmd->cdb_len);
|
||||
scst_set_cmd_error(cmd,
|
||||
SCST_LOAD_SENSE(scst_sense_invalid_message));
|
||||
goto out_done;
|
||||
if (cmd->bufflen == 0) {
|
||||
PRINT_ERROR("Unknown data transfer length for opcode "
|
||||
"0x%x (handler %s, target %s)", cmd->cdb[0],
|
||||
devt->name, cmd->tgtt->name);
|
||||
PRINT_BUFFER("Failed CDB", cmd->cdb, cmd->cdb_len);
|
||||
scst_set_cmd_error(cmd,
|
||||
SCST_LOAD_SENSE(scst_sense_invalid_message));
|
||||
goto out_done;
|
||||
} /* else we have a guess, so proceed further */
|
||||
}
|
||||
cmd->op_flags &= ~SCST_UNKNOWN_LENGTH;
|
||||
}
|
||||
|
||||
if (unlikely(cmd->cdb[cmd->cdb_len - 1] & CONTROL_BYTE_NACA_BIT)) {
|
||||
|
||||
Reference in New Issue
Block a user