Spdk/lib/scsi/scsi_bdev.c
Jim Harris 2e3f07ac40 bdev: add spdk_io_channel parameter to spdk_bdev_reset
This ensures that all spdk_bdev_io structures now have
an attached channel, simplifying some future work around
things like counting the number of outstanding IOs for
a given channel (which otherwise would have had to
account specially for resets).

Reset semantics are still that they affect the entire bdev
and not just the channel it was submitted on.

Signed-off-by: Jim Harris <james.r.harris@intel.com>
Change-Id: I8aad21a88faacecfd94bdba350059528eb62c390

Reviewed-on: https://review.gerrithub.io/362251
Reviewed-by: Daniel Verkamp <daniel.verkamp@intel.com>
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
Tested-by: SPDK Automated Test System <sys_sgsw@intel.com>
2017-05-24 11:42:15 -04:00

1953 lines
49 KiB
C

/*-
* BSD LICENSE
*
* Copyright (C) 2008-2012 Daisuke Aoyama <aoyama@peach.ne.jp>.
* Copyright (c) Intel Corporation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "scsi_internal.h"
/*
* TODO: move bdev SCSI error code translation tests to bdev unit test
* and remove this include.
*/
#include "spdk_internal/bdev.h"
#include "spdk/env.h"
#include "spdk/bdev.h"
#include "spdk/endian.h"
#include "spdk/string.h"
#include "spdk/util.h"
#define SPDK_WORK_BLOCK_SIZE (1ULL * 1024ULL * 1024ULL)
#define SPDK_WORK_ATS_BLOCK_SIZE (1ULL * 1024ULL * 1024ULL)
#define MAX_SERIAL_STRING 32
#define DEFAULT_DISK_VENDOR "INTEL"
#define DEFAULT_DISK_REVISION "0001"
#define DEFAULT_DISK_ROTATION_RATE 1 /* Non-rotating medium */
#define DEFAULT_DISK_FORM_FACTOR 0x02 /* 3.5 inch */
#define INQUIRY_OFFSET(field) offsetof(struct spdk_scsi_cdb_inquiry_data, field) + \
sizeof(((struct spdk_scsi_cdb_inquiry_data *)0x0)->field)
static int
spdk_hex2bin(char ch)
{
if ((ch >= '0') && (ch <= '9'))
return ch - '0';
ch = tolower(ch);
if ((ch >= 'a') && (ch <= 'f'))
return ch - 'a' + 10;
return (int)ch;
}
static void
spdk_bdev_scsi_set_local_naa(const char *name, uint8_t *buf)
{
int i, value, count = 0;
uint64_t naa, local_value;
for (i = 0; (i < 16) && (name[i] != '\0'); i++) {
value = spdk_hex2bin(name[i]);
if (i % 2) {
buf[count++] |= value << 4;
} else {
buf[count] = value;
}
}
/* NAA locally assigned filed */
naa = 0x3;
local_value = *(uint64_t *)buf;
local_value = (naa << 60) | (local_value >> 4);
to_be64((void *)buf, local_value);
}
static int
spdk_bdev_scsi_report_luns(struct spdk_scsi_lun *lun,
int sel, uint8_t *data, int alloc_len)
{
struct spdk_scsi_dev *dev;
uint64_t fmt_lun, lun_id, method;
int hlen, len = 0;
int i;
if (alloc_len < 8)
return -1;
if (sel == 0x00) {
/* logical unit with addressing method */
} else if (sel == 0x01) {
/* well known logical unit */
} else if (sel == 0x02) {
/* logical unit */
} else
return -1;
/* LUN LIST LENGTH */
memset(data, 0, 4);
/* Reserved */
memset(&data[4], 0, 4);
hlen = 8;
dev = lun->dev;
for (i = 0; i < dev->maxlun; i++) {
if (dev->lun[i] == NULL)
continue;
if (alloc_len - (hlen + len) < 8)
return -1;
lun_id = (uint64_t)i;
if (dev->maxlun <= 0x0100) {
/* below 256 */
method = 0x00U;
fmt_lun = (method & 0x03U) << 62;
fmt_lun |= (lun_id & 0x00ffU) << 48;
} else if (dev->maxlun <= 0x4000) {
/* below 16384 */
method = 0x01U;
fmt_lun = (method & 0x03U) << 62;
fmt_lun |= (lun_id & 0x3fffU) << 48;
} else {
/* XXX */
fmt_lun = 0;
}
/* LUN */
to_be64(&data[hlen + len], fmt_lun);
len += 8;
}
/* LUN LIST LENGTH */
to_be32(data, len);
return hlen + len;
}
static int
spdk_bdev_scsi_inquiry(struct spdk_bdev *bdev, struct spdk_scsi_task *task,
uint8_t *cdb, uint8_t *data, uint16_t alloc_len)
{
struct spdk_scsi_lun *lun;
struct spdk_scsi_dev *dev;
struct spdk_scsi_port *port;
uint32_t blocks, optimal_blocks;
int hlen = 0, plen, plen2;
uint16_t len = 0;
int pc;
int pd;
int evpd;
int i;
struct spdk_scsi_cdb_inquiry *inq = (struct spdk_scsi_cdb_inquiry *)cdb;
/* standard inquiry command at lease with 36 Bytes */
if (alloc_len < 0x24)
goto inq_error;
lun = task->lun;
dev = lun->dev;
port = task->target_port;
pd = SPDK_SPC_PERIPHERAL_DEVICE_TYPE_DISK;
pc = inq->page_code;
evpd = inq->evpd & 0x1;
if (!evpd && pc) {
spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
return -1;
}
if (evpd) {
struct spdk_scsi_vpd_page *vpage = (struct spdk_scsi_vpd_page *)data;
/* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */
vpage->peripheral = pd;
/* PAGE CODE */
vpage->page_code = pc;
/* Vital product data */
switch (pc) {
case SPDK_SPC_VPD_SUPPORTED_VPD_PAGES:
hlen = 4;
vpage->params[0] = SPDK_SPC_VPD_SUPPORTED_VPD_PAGES;
vpage->params[1] = SPDK_SPC_VPD_UNIT_SERIAL_NUMBER;
vpage->params[2] = SPDK_SPC_VPD_DEVICE_IDENTIFICATION;
vpage->params[3] = SPDK_SPC_VPD_MANAGEMENT_NETWORK_ADDRESSES;
vpage->params[4] = SPDK_SPC_VPD_EXTENDED_INQUIRY_DATA;
vpage->params[5] = SPDK_SPC_VPD_MODE_PAGE_POLICY;
vpage->params[6] = SPDK_SPC_VPD_SCSI_PORTS;
/* SBC Block Limits */
vpage->params[7] = 0xb0;
/* SBC Block Device Characteristics */
vpage->params[8] = 0xb1;
len = 9;
if (spdk_bdev_io_type_supported(bdev, SPDK_BDEV_IO_TYPE_UNMAP)) {
/* SBC Thin Provisioning */
vpage->params[9] = 0xb2;
len++;
}
/* PAGE LENGTH */
to_be16(vpage->alloc_len, len);
break;
case SPDK_SPC_VPD_UNIT_SERIAL_NUMBER: {
const char *name = spdk_bdev_get_name(bdev);
hlen = 4;
/* PRODUCT SERIAL NUMBER */
len = strlen(name) + 1;
if (len > MAX_SERIAL_STRING) {
len = MAX_SERIAL_STRING;
}
memcpy(vpage->params, name, len - 1);
vpage->params[len - 1] = 0;
/* PAGE LENGTH */
to_be16(vpage->alloc_len, len);
break;
}
case SPDK_SPC_VPD_DEVICE_IDENTIFICATION: {
const char *name = spdk_bdev_get_name(bdev);
const char *product_name = spdk_bdev_get_product_name(bdev);
uint8_t *buf = vpage->params;
struct spdk_scsi_desig_desc *desig;
hlen = 4;
/* Check total length by calculated how much space all entries take */
len = sizeof(struct spdk_scsi_desig_desc) + 8;
len += sizeof(struct spdk_scsi_desig_desc) + 8 + 16 + MAX_SERIAL_STRING;
len += sizeof(struct spdk_scsi_desig_desc) + SPDK_SCSI_DEV_MAX_NAME;
len += sizeof(struct spdk_scsi_desig_desc) + SPDK_SCSI_PORT_MAX_NAME_LENGTH;
len += sizeof(struct spdk_scsi_desig_desc) + 4;
len += sizeof(struct spdk_scsi_desig_desc) + 4;
len += sizeof(struct spdk_scsi_desig_desc) + 4;
if (sizeof(struct spdk_scsi_vpd_page) + len > alloc_len) {
spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
return -1;
}
/* Now fill out the designator array */
/* NAA designator */
desig = (struct spdk_scsi_desig_desc *)buf;
desig->code_set = SPDK_SPC_VPD_CODE_SET_BINARY;
desig->protocol_id = SPDK_SPC_PROTOCOL_IDENTIFIER_ISCSI;
desig->type = SPDK_SPC_VPD_IDENTIFIER_TYPE_NAA;
desig->association = SPDK_SPC_VPD_ASSOCIATION_LOGICAL_UNIT;
desig->reserved0 = 0;
desig->piv = 1;
desig->reserved1 = 0;
desig->len = 8;
spdk_bdev_scsi_set_local_naa(name, desig->desig);
len = sizeof(struct spdk_scsi_desig_desc) + 8;
buf += sizeof(struct spdk_scsi_desig_desc) + desig->len;
/* T10 Vendor ID designator */
desig = (struct spdk_scsi_desig_desc *)buf;
desig->code_set = SPDK_SPC_VPD_CODE_SET_ASCII;
desig->protocol_id = SPDK_SPC_PROTOCOL_IDENTIFIER_ISCSI;
desig->type = SPDK_SPC_VPD_IDENTIFIER_TYPE_T10_VENDOR_ID;
desig->association = SPDK_SPC_VPD_ASSOCIATION_LOGICAL_UNIT;
desig->reserved0 = 0;
desig->piv = 1;
desig->reserved1 = 0;
desig->len = 8 + 16 + MAX_SERIAL_STRING;
spdk_strcpy_pad(desig->desig, DEFAULT_DISK_VENDOR, 8, ' ');
spdk_strcpy_pad(&desig->desig[8], product_name, 16, ' ');
spdk_strcpy_pad(&desig->desig[24], name, MAX_SERIAL_STRING, ' ');
len += sizeof(struct spdk_scsi_desig_desc) + 8 + 16 + MAX_SERIAL_STRING;
buf += sizeof(struct spdk_scsi_desig_desc) + desig->len;
/* SCSI Device Name designator */
desig = (struct spdk_scsi_desig_desc *)buf;
desig->code_set = SPDK_SPC_VPD_CODE_SET_UTF8;
desig->protocol_id = SPDK_SPC_PROTOCOL_IDENTIFIER_ISCSI;
desig->type = SPDK_SPC_VPD_IDENTIFIER_TYPE_SCSI_NAME;
desig->association = SPDK_SPC_VPD_ASSOCIATION_TARGET_DEVICE;
desig->reserved0 = 0;
desig->piv = 1;
desig->reserved1 = 0;
desig->len = snprintf(desig->desig, SPDK_SCSI_DEV_MAX_NAME, "%s", dev->name);
len += sizeof(struct spdk_scsi_desig_desc) + desig->len;
buf += sizeof(struct spdk_scsi_desig_desc) + desig->len;
/* SCSI Port Name designator */
desig = (struct spdk_scsi_desig_desc *)buf;
desig->code_set = SPDK_SPC_VPD_CODE_SET_UTF8;
desig->protocol_id = SPDK_SPC_PROTOCOL_IDENTIFIER_ISCSI;
desig->type = SPDK_SPC_VPD_IDENTIFIER_TYPE_SCSI_NAME;
desig->association = SPDK_SPC_VPD_ASSOCIATION_TARGET_PORT;
desig->reserved0 = 0;
desig->piv = 1;
desig->reserved1 = 0;
desig->len = snprintf(desig->desig, SPDK_SCSI_PORT_MAX_NAME_LENGTH, "%s", port->name);
len += sizeof(struct spdk_scsi_desig_desc) + desig->len;
buf += sizeof(struct spdk_scsi_desig_desc) + desig->len;
/* Relative Target Port designator */
desig = (struct spdk_scsi_desig_desc *)buf;
desig->code_set = SPDK_SPC_VPD_CODE_SET_BINARY;
desig->protocol_id = SPDK_SPC_PROTOCOL_IDENTIFIER_ISCSI;
desig->type = SPDK_SPC_VPD_IDENTIFIER_TYPE_RELATIVE_TARGET_PORT;
desig->association = SPDK_SPC_VPD_ASSOCIATION_TARGET_PORT;
desig->reserved0 = 0;
desig->piv = 1;
desig->reserved1 = 0;
desig->len = 4;
memset(desig->desig, 0, 2); /* Reserved */
to_be16(&desig->desig[2], port->index);
len += sizeof(struct spdk_scsi_desig_desc) + desig->len;
buf += sizeof(struct spdk_scsi_desig_desc) + desig->len;
/* Target port group designator */
desig = (struct spdk_scsi_desig_desc *)buf;
desig->code_set = SPDK_SPC_VPD_CODE_SET_BINARY;
desig->protocol_id = SPDK_SPC_PROTOCOL_IDENTIFIER_ISCSI;
desig->type = SPDK_SPC_VPD_IDENTIFIER_TYPE_TARGET_PORT_GROUP;
desig->association = SPDK_SPC_VPD_ASSOCIATION_TARGET_PORT;
desig->reserved0 = 0;
desig->piv = 1;
desig->reserved1 = 0;
desig->len = 4;
memset(desig->desig, 0, 4);
len += sizeof(struct spdk_scsi_desig_desc) + desig->len;
buf += sizeof(struct spdk_scsi_desig_desc) + desig->len;
/* Logical unit group designator */
desig = (struct spdk_scsi_desig_desc *)buf;
desig->code_set = SPDK_SPC_VPD_CODE_SET_BINARY;
desig->protocol_id = SPDK_SPC_PROTOCOL_IDENTIFIER_ISCSI;
desig->type = SPDK_SPC_VPD_IDENTIFIER_TYPE_LOGICAL_UNIT_GROUP;
desig->association = SPDK_SPC_VPD_ASSOCIATION_LOGICAL_UNIT;
desig->reserved0 = 0;
desig->piv = 1;
desig->reserved1 = 0;
desig->len = 4;
memset(desig->desig, 0, 2); /* Reserved */
to_be16(&desig->desig[2], dev->id);
len += sizeof(struct spdk_scsi_desig_desc) + desig->len;
to_be16(vpage->alloc_len, len);
break;
}
case SPDK_SPC_VPD_EXTENDED_INQUIRY_DATA: {
struct spdk_scsi_vpd_ext_inquiry *vext = (struct spdk_scsi_vpd_ext_inquiry *)vpage;
memset(vext, 0, sizeof(*vext));
hlen = 4;
/* RTO(3) GRD_CHK(2) APP_CHK(1) REF_CHK(0) */
/* GROUP_SUP(4) PRIOR_SUP(3) HEADSUP(2) ORDSUP(1) SIMPSUP(0) */
vext->sup = SPDK_SCSI_VEXT_HEADSUP | SPDK_SCSI_VEXT_SIMPSUP;
/* NV_SUP(1) V_SUP(0) */
/* Reserved[7-63] */
len = 64 - hlen;
/* PAGE LENGTH */
to_be16(vpage->alloc_len, len);
break;
}
case SPDK_SPC_VPD_MANAGEMENT_NETWORK_ADDRESSES:
/* PAGE LENGTH */
hlen = 4;
to_be16(vpage->alloc_len, len);
break;
case SPDK_SPC_VPD_MODE_PAGE_POLICY: {
struct spdk_scsi_mpage_policy_desc *pdesc =
(struct spdk_scsi_mpage_policy_desc *)vpage->params;
hlen = 4;
/* Mode page policy descriptor 1 */
/* POLICY PAGE CODE(5-0) */
/* all page code */
pdesc->page_code = 0x3f;
/* POLICY SUBPAGE CODE */
/* all sub page */
pdesc->sub_page_code = 0xff;
/* MLUS(7) MODE PAGE POLICY(1-0) */
/* MLUS own copy */
/* Shared MODE PAGE policy */
pdesc->policy = 0;
/* Reserved */
pdesc->reserved = 0;
len += 4;
to_be16(vpage->alloc_len, len);
break;
}
case SPDK_SPC_VPD_SCSI_PORTS: {
/* PAGE LENGTH */
hlen = 4;
/* Identification descriptor list */
for (i = 0; i < dev->num_ports; i++) {
struct spdk_scsi_port_desc *sdesc;
struct spdk_scsi_tgt_port_desc *pdesc;
/* Identification descriptor N */
sdesc = (struct spdk_scsi_port_desc *)&vpage->params[len];
/* Reserved */
sdesc->reserved = 0;
/* RELATIVE PORT IDENTIFIER */
to_be16(&sdesc->rel_port_id, dev->port[i].index);
/* Reserved */
sdesc->reserved2 = 0;
/* INITIATOR PORT TRANSPORTID LENGTH */
sdesc->init_port_len = 0;
/* Reserved */
sdesc->init_port_id = 0;
/* TARGET PORT DESCRIPTORS LENGTH */
sdesc->tgt_desc_len = 0;
len += 12;
plen2 = 0;
/* Target port descriptor 1 */
pdesc = (struct spdk_scsi_tgt_port_desc *)sdesc->tgt_desc;
/* PROTOCOL IDENTIFIER(7-4) CODE SET(3-0) */
pdesc->code_set =
SPDK_SPC_PROTOCOL_IDENTIFIER_ISCSI << 4 |
SPDK_SPC_VPD_CODE_SET_UTF8;
/* PIV(7) ASSOCIATION(5-4) IDENTIFIER TYPE(3-0) */
pdesc->desig_type = SPDK_SPC_VPD_DESIG_PIV |
SPDK_SPC_VPD_ASSOCIATION_TARGET_PORT << 4 |
SPDK_SPC_VPD_IDENTIFIER_TYPE_SCSI_NAME;
/* Reserved */
pdesc->reserved = 0;
/* IDENTIFIER */
plen = snprintf((char *)pdesc->designator,
SPDK_SCSI_PORT_MAX_NAME_LENGTH, "%s",
dev->port[i].name);
pdesc->len = plen;
plen2 += 4 + plen;
/* TARGET PORT DESCRIPTORS LENGTH */
to_be16(&sdesc->tgt_desc_len, plen2);
len += plen2;
}
to_be16(vpage->alloc_len, len);
break;
}
case SPDK_SPC_VPD_BLOCK_LIMITS: {
uint32_t block_size = spdk_bdev_get_block_size(bdev);
/* PAGE LENGTH */
memset(&data[4], 0, 60);
hlen = 4;
/* WSNZ(0) */
/* support zero length in WRITE SAME */
/* MAXIMUM COMPARE AND WRITE LENGTH */
blocks = SPDK_WORK_ATS_BLOCK_SIZE / block_size;
if (blocks > 0xff)
blocks = 0xff;
data[5] = (uint8_t)blocks;
/* force align to 4KB */
if (block_size < 4096) {
optimal_blocks = 4096 / block_size;
} else {
optimal_blocks = 1;
}
/* OPTIMAL TRANSFER LENGTH GRANULARITY */
to_be16(&data[6], optimal_blocks);
blocks = SPDK_WORK_BLOCK_SIZE / block_size;
/* MAXIMUM TRANSFER LENGTH */
to_be32(&data[8], blocks);
/* OPTIMAL TRANSFER LENGTH */
to_be32(&data[12], blocks);
/* MAXIMUM PREFETCH XDREAD XDWRITE TRANSFER LENGTH */
len = 20 - hlen;
if (spdk_bdev_io_type_supported(bdev, SPDK_BDEV_IO_TYPE_UNMAP)) {
uint32_t max_unmap_desc;
/*
* MAXIMUM UNMAP LBA COUNT: indicates the
* maximum number of LBAs that may be
* unmapped by an UNMAP command.
*/
to_be32(&data[20], g_spdk_scsi.scsi_params.max_unmap_lba_count);
/*
* MAXIMUM UNMAP BLOCK DESCRIPTOR COUNT:
* indicates the maximum number of UNMAP
* block descriptors that shall be contained
* in the parameter data transferred to the
* device server for an UNMAP command.
*/
max_unmap_desc = spdk_min(spdk_bdev_get_max_unmap_descriptors(bdev),
g_spdk_scsi.scsi_params.max_unmap_block_descriptor_count);
to_be32(&data[24], max_unmap_desc);
/*
* OPTIMAL UNMAP GRANULARITY: indicates the
* optimal granularity in logical blocks
* for unmap request.
*/
to_be32(&data[28], g_spdk_scsi.scsi_params.optimal_unmap_granularity);
/*
* UNMAP GRANULARITY ALIGNMENT: indicates the
* LBA of the first logical block to which the
* OPTIMAL UNMAP GRANULARITY field applies.
*/
/* not specified */
to_be32(&data[32], g_spdk_scsi.scsi_params.unmap_granularity_alignment);
/*
* UGAVALID(7): bit set to one indicates that
* the UNMAP GRANULARITY ALIGNMENT field is
* valid.
*/
/* not specified */
if (g_spdk_scsi.scsi_params.ugavalid)
data[32] |= 1 << 7;
/*
* MAXIMUM WRITE SAME LENGTH: indicates the
* maximum number of contiguous logical blocks
* that the device server allows to be unmapped
* or written in a single WRITE SAME command.
*/
to_be64(&data[36], g_spdk_scsi.scsi_params.max_write_same_length);
/* Reserved */
/* not specified */
len = 64 - hlen;
}
to_be16(vpage->alloc_len, len);
break;
}
case SPDK_SPC_VPD_BLOCK_DEV_CHARS: {
/* PAGE LENGTH */
hlen = 4;
len = 64 - hlen;
to_be16(&data[4], DEFAULT_DISK_ROTATION_RATE);
/* Reserved */
data[6] = 0;
/* NOMINAL FORM FACTOR(3-0) */
data[7] = DEFAULT_DISK_FORM_FACTOR << 4;
/* Reserved */
memset(&data[8], 0, 64 - 8);
to_be16(vpage->alloc_len, len);
break;
}
case SPDK_SPC_VPD_BLOCK_THIN_PROVISION: {
if (!spdk_bdev_io_type_supported(bdev, SPDK_BDEV_IO_TYPE_UNMAP)) {
SPDK_ERRLOG("unsupported INQUIRY VPD page 0x%x\n", pc);
goto inq_error;
}
hlen = 4;
len = 7;
/*
* PAGE LENGTH : if the DP bit is set to one, then the
* page length shall be set 0004h.
*/
to_be16(&data[2], 0x0004);
/*
* THRESHOLD EXPONENT : it indicates the threshold set
* size in LBAs as a power of 2( i.e., the threshold
* set size = 2 ^ (threshold exponent).
*/
data[4] = 0;
/*
* Set the LBPU bit to indicate the support for UNMAP
* command.
*/
data[5] |= SPDK_SCSI_UNMAP_LBPU;
/*
* Set the provisioning type to thin provision.
*/
data[6] = SPDK_SCSI_UNMAP_THIN_PROVISIONING;
to_be16(vpage->alloc_len, len);
break;
}
default:
if (pc >= 0xc0 && pc <= 0xff) {
SPDK_TRACELOG(SPDK_TRACE_SCSI, "Vendor specific INQUIRY VPD page 0x%x\n", pc);
} else {
SPDK_ERRLOG("unsupported INQUIRY VPD page 0x%x\n", pc);
}
goto inq_error;
}
} else {
struct spdk_scsi_cdb_inquiry_data *inqdata =
(struct spdk_scsi_cdb_inquiry_data *)data;
/* Standard INQUIRY data */
/* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */
inqdata->peripheral = pd;
/* RMB(7) */
inqdata->rmb = 0;
/* VERSION */
/* See SPC3/SBC2/MMC4/SAM2 for more details */
inqdata->version = SPDK_SPC_VERSION_SPC3;
/* NORMACA(5) HISUP(4) RESPONSE DATA FORMAT(3-0) */
/* format 2 */ /* hierarchical support */
inqdata->response = 2 | 1 << 4;
hlen = 5;
/* SCCS(7) ACC(6) TPGS(5-4) 3PC(3) PROTECT(0) */
/* Not support TPGS */
inqdata->flags = 0;
/* MULTIP */
inqdata->flags2 = 0x10;
/* WBUS16(5) SYNC(4) LINKED(3) CMDQUE(1) VS(0) */
/* CMDQUE */
inqdata->flags3 = 0x2;
/* T10 VENDOR IDENTIFICATION */
spdk_strcpy_pad(inqdata->t10_vendor_id, DEFAULT_DISK_VENDOR, 8, ' ');
/* PRODUCT IDENTIFICATION */
spdk_strcpy_pad(inqdata->product_id, spdk_bdev_get_product_name(bdev), 16, ' ');
/* PRODUCT REVISION LEVEL */
spdk_strcpy_pad(inqdata->product_rev, DEFAULT_DISK_REVISION, 4, ' ');
/*
* Standard inquiry data ends here. Only populate remaining fields if alloc_len
* indicates enough space to hold it.
*/
len = INQUIRY_OFFSET(product_rev) - 5;
if (alloc_len >= INQUIRY_OFFSET(vendor)) {
/* Vendor specific */
memset(inqdata->vendor, 0x20, 20);
len += sizeof(inqdata->vendor);
}
if (alloc_len >= INQUIRY_OFFSET(ius)) {
/* CLOCKING(3-2) QAS(1) IUS(0) */
inqdata->ius = 0;
len += sizeof(inqdata->ius);
}
if (alloc_len >= INQUIRY_OFFSET(reserved)) {
/* Reserved */
inqdata->reserved = 0;
len += sizeof(inqdata->reserved);
}
/* VERSION DESCRIPTOR 1-8 */
if (alloc_len >= INQUIRY_OFFSET(reserved) + 2) {
to_be16(&inqdata->desc[0], 0x0960);
len += 2;
}
if (alloc_len >= INQUIRY_OFFSET(reserved) + 4) {
to_be16(&inqdata->desc[2], 0x0300); /* SPC-3 (no version claimed) */
len += 2;
}
if (alloc_len >= INQUIRY_OFFSET(reserved) + 6) {
to_be16(&inqdata->desc[4], 0x320); /* SBC-2 (no version claimed) */
len += 2;
}
if (alloc_len >= INQUIRY_OFFSET(reserved) + 8) {
to_be16(&inqdata->desc[6], 0x0040); /* SAM-2 (no version claimed) */
len += 2;
}
/*
* We only fill out 4 descriptors, but if the allocation length goes past
* that, zero the remaining bytes. This fixes some SCSI compliance tests
* which expect a full 96 bytes to be returned, including the unpopulated
* version descriptors 5-8 (4 * 2 = 8 bytes) plus the 22 bytes of reserved
* space (bytes 74-95) - for a total of 30 bytes.
*/
if (alloc_len > INQUIRY_OFFSET(reserved) + 8) {
i = alloc_len - (INQUIRY_OFFSET(reserved) + 8);
if (i > 30) {
i = 30;
}
memset(&inqdata->desc[8], 0, i);
len += i;
}
/* ADDITIONAL LENGTH */
inqdata->add_len = len;
}
return hlen + len;
inq_error:
task->data_transferred = 0;
spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
SPDK_SCSI_SENSE_NO_SENSE,
SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
return -1;
}
static void
mode_sense_page_init(uint8_t *buf, int len, int page, int subpage)
{
if (!buf)
return;
memset(buf, 0, len);
if (subpage != 0) {
buf[0] = page | 0x40; /* PAGE + SPF=1 */
buf[1] = subpage;
to_be16(&buf[2], len - 4);
} else {
buf[0] = page;
buf[1] = len - 2;
}
}
static int
spdk_bdev_scsi_mode_sense_page(struct spdk_bdev *bdev,
uint8_t *cdb, int pc, int page, int subpage,
uint8_t *data, struct spdk_scsi_task *task)
{
uint8_t *cp = data;
int len = 0;
int plen;
int i;
if (pc == 0x00) {
/* Current values */
} else if (pc == 0x01) {
/* Changeable values */
/* As we currently do not support changeable values,
all parameters are reported as zero. */
} else if (pc == 0x02) {
/* Default values */
} else {
/* Saved values not supported */
spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
SPDK_SCSI_ASC_SAVING_PARAMETERS_NOT_SUPPORTED,
SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
return -1;
}
switch (page) {
case 0x00:
/* Vendor specific */
break;
case 0x01:
/* Read-Write Error Recovery */
SPDK_TRACELOG(SPDK_TRACE_SCSI,
"MODE_SENSE Read-Write Error Recovery\n");
if (subpage != 0x00)
break;
plen = 0x0a + 2;
mode_sense_page_init(cp, plen, page, subpage);
len += plen;
break;
case 0x02:
/* Disconnect-Reconnect */
SPDK_TRACELOG(SPDK_TRACE_SCSI,
"MODE_SENSE Disconnect-Reconnect\n");
if (subpage != 0x00)
break;
plen = 0x0e + 2;
mode_sense_page_init(cp, plen, page, subpage);
len += plen;
break;
case 0x03:
/* Obsolete (Format Device) */
break;
case 0x04:
/* Obsolete (Rigid Disk Geometry) */
break;
case 0x05:
/* Obsolete (Rigid Disk Geometry) */
break;
case 0x06:
/* Reserved */
break;
case 0x07:
/* Verify Error Recovery */
SPDK_TRACELOG(SPDK_TRACE_SCSI,
"MODE_SENSE Verify Error Recovery\n");
if (subpage != 0x00)
break;
plen = 0x0a + 2;
mode_sense_page_init(cp, plen, page, subpage);
len += plen;
break;
case 0x08: {
/* Caching */
SPDK_TRACELOG(SPDK_TRACE_SCSI, "MODE_SENSE Caching\n");
if (subpage != 0x00)
break;
plen = 0x12 + 2;
mode_sense_page_init(cp, plen, page, subpage);
if (cp && spdk_bdev_has_write_cache(bdev) && pc != 0x01)
cp[2] |= 0x4; /* WCE */
/* Read Cache Disable (RCD) = 1 */
if (cp && pc != 0x01)
cp[2] |= 0x1;
len += plen;
break;
}
case 0x09:
/* Obsolete */
break;
case 0x0a:
switch (subpage) {
case 0x00:
/* Control */
SPDK_TRACELOG(SPDK_TRACE_SCSI,
"MODE_SENSE Control\n");
plen = 0x0a + 2;
mode_sense_page_init(cp, plen, page, subpage);
len += plen;
break;
case 0x01:
/* Control Extension */
SPDK_TRACELOG(SPDK_TRACE_SCSI,
"MODE_SENSE Control Extension\n");
plen = 0x1c + 4;
mode_sense_page_init(cp, plen, page, subpage);
len += plen;
break;
case 0xff:
/* All subpages */
len += spdk_bdev_scsi_mode_sense_page(bdev,
cdb, pc, page,
0x00,
cp ? &cp[len] : NULL, task);
len += spdk_bdev_scsi_mode_sense_page(bdev,
cdb, pc, page,
0x01,
cp ? &cp[len] : NULL, task);
break;
default:
/* 0x02-0x3e: Reserved */
break;
}
break;
case 0x0b:
/* Obsolete (Medium Types Supported) */
break;
case 0x0c:
/* Obsolete (Notch And Partitio) */
break;
case 0x0d:
/* Obsolete */
break;
case 0x0e:
case 0x0f:
/* Reserved */
break;
case 0x10:
/* XOR Control */
SPDK_TRACELOG(SPDK_TRACE_SCSI, "MODE_SENSE XOR Control\n");
if (subpage != 0x00)
break;
plen = 0x16 + 2;
mode_sense_page_init(cp, plen, page, subpage);
len += plen;
break;
case 0x11:
case 0x12:
case 0x13:
/* Reserved */
break;
case 0x14:
/* Enclosure Services Management */
break;
case 0x15:
case 0x16:
case 0x17:
/* Reserved */
break;
case 0x18:
/* Protocol-Specific LUN */
break;
case 0x19:
/* Protocol-Specific Port */
break;
case 0x1a:
/* Power Condition */
SPDK_TRACELOG(SPDK_TRACE_SCSI,
"MODE_SENSE Power Condition\n");
if (subpage != 0x00)
break;
plen = 0x0a + 2;
mode_sense_page_init(cp, plen, page, subpage);
len += plen;
break;
case 0x1b:
/* Reserved */
break;
case 0x1c:
/* Informational Exceptions Control */
SPDK_TRACELOG(SPDK_TRACE_SCSI,
"MODE_SENSE Informational Exceptions Control\n");
if (subpage != 0x00)
break;
plen = 0x0a + 2;
mode_sense_page_init(cp, plen, page, subpage);
len += plen;
break;
case 0x1d:
case 0x1e:
case 0x1f:
/* Reserved */
break;
case 0x20:
case 0x21:
case 0x22:
case 0x23:
case 0x24:
case 0x25:
case 0x26:
case 0x27:
case 0x28:
case 0x29:
case 0x2a:
case 0x2b:
case 0x2c:
case 0x2d:
case 0x2e:
case 0x2f:
case 0x30:
case 0x31:
case 0x32:
case 0x33:
case 0x34:
case 0x35:
case 0x36:
case 0x37:
case 0x38:
case 0x39:
case 0x3a:
case 0x3b:
case 0x3c:
case 0x3d:
case 0x3e:
/* Vendor-specific */
break;
case 0x3f:
switch (subpage) {
case 0x00:
/* All mode pages */
for (i = 0x00; i < 0x3e; i ++) {
len += spdk_bdev_scsi_mode_sense_page(
bdev, cdb, pc, i, 0x00,
cp ? &cp[len] : NULL, task);
}
break;
case 0xff:
/* All mode pages and subpages */
for (i = 0x00; i < 0x3e; i ++) {
len += spdk_bdev_scsi_mode_sense_page(
bdev, cdb, pc, i, 0x00,
cp ? &cp[len] : NULL, task);
}
for (i = 0x00; i < 0x3e; i ++) {
len += spdk_bdev_scsi_mode_sense_page(
bdev, cdb, pc, i, 0xff,
cp ? &cp[len] : NULL, task);
}
break;
default:
/* 0x01-0x3e: Reserved */
break;
}
}
return len;
}
static int
spdk_bdev_scsi_mode_sense(struct spdk_bdev *bdev, int md,
uint8_t *cdb, int dbd, int llbaa, int pc,
int page, int subpage, uint8_t *data, struct spdk_scsi_task *task)
{
uint64_t num_blocks = spdk_bdev_get_num_blocks(bdev);
uint32_t block_size = spdk_bdev_get_block_size(bdev);
uint8_t *hdr, *bdesc, *pages;
int hlen;
int blen;
int plen, total;
assert(md == 6 || md == 10);
if (md == 6) {
hlen = 4;
blen = 8; /* For MODE SENSE 6 only short LBA */
} else {
hlen = 8;
blen = llbaa ? 16 : 8;
}
if (dbd) {
blen = 0;
}
pages = data ? &data[hlen + blen] : NULL;
plen = spdk_bdev_scsi_mode_sense_page(bdev, cdb, pc, page,
subpage,
pages, task);
if (plen < 0) {
return -1;
}
total = hlen + blen + plen;
if (data == NULL)
return total;
hdr = &data[0];
if (hlen == 4) {
hdr[0] = total - 1; /* Mode Data Length */
hdr[1] = 0; /* Medium Type */
hdr[2] = 0; /* Device-Specific Parameter */
hdr[3] = blen; /* Block Descripter Length */
} else {
to_be16(&hdr[0], total - 2); /* Mode Data Length */
hdr[2] = 0; /* Medium Type */
hdr[3] = 0; /* Device-Specific Parameter */
hdr[4] = llbaa ? 0x1 : 0; /* Long/short LBA */
hdr[5] = 0; /* Reserved */
to_be16(&hdr[6], blen); /* Block Descripter Length */
}
bdesc = &data[hlen];
if (blen == 16) {
/* Number of Blocks */
to_be64(&bdesc[0], num_blocks);
/* Reserved */
memset(&bdesc[8], 0, 4);
/* Block Length */
to_be32(&bdesc[12], block_size);
} else if (blen == 8) {
/* Number of Blocks */
if (num_blocks > 0xffffffffULL)
memset(&bdesc[0], 0xff, 4);
else
to_be32(&bdesc[0], num_blocks);
/* Block Length */
to_be32(&bdesc[4], block_size);
}
return total;
}
static int
spdk_bdev_scsi_mode_select_page(struct spdk_bdev *bdev,
uint8_t *cdb, int pf, int sp,
uint8_t *data, size_t len)
{
size_t hlen, plen;
int spf, page, subpage;
int rc;
/* vendor specific */
if (pf == 0) {
return 0;
}
if (len < 1) {
return 0;
}
spf = !!(data[0] & 0x40);
page = data[0] & 0x3f;
if (spf) {
/* Sub_page mode page format */
hlen = 4;
if (len < hlen)
return 0;
subpage = data[1];
plen = from_be16(&data[2]);
} else {
/* Page_0 mode page format */
hlen = 2;
if (len < hlen)
return 0;
subpage = 0;
plen = data[1];
}
plen += hlen;
if (len < plen) {
return 0;
}
switch (page) {
case 0x08: { /* Caching */
//int wce;
SPDK_TRACELOG(SPDK_TRACE_SCSI, "MODE_SELECT Caching\n");
if (subpage != 0x00)
break;
if (plen != 0x12 + hlen) {
/* unknown format */
break;
}
// TODO:
//wce = data[2] & 0x4; /* WCE */
//fd = bdev->fd;
//
//rc = fcntl(fd, F_GETFL, 0);
//if (rc != -1) {
// if (wce) {
// SPDK_TRACELOG(SPDK_TRACE_SCSI, "MODE_SELECT Writeback cache enable\n");
// rc = fcntl(fd, F_SETFL, (rc & ~O_FSYNC));
// bdev->write_cache = 1;
// } else {
// rc = fcntl(fd, F_SETFL, (rc | O_FSYNC));
// bdev->write_cache = 0;
// }
//}
break;
}
default:
/* not supported */
break;
}
len -= plen;
if (len != 0) {
rc = spdk_bdev_scsi_mode_select_page(bdev, cdb, pf, sp, &data[plen], len);
if (rc < 0) {
return rc;
}
}
return 0;
}
static void
spdk_bdev_scsi_task_complete_cmd(struct spdk_bdev_io *bdev_io, bool success,
void *cb_arg)
{
struct spdk_scsi_task *task = cb_arg;
int sc, sk, asc, ascq;
spdk_bdev_io_get_scsi_status(bdev_io, &sc, &sk, &asc, &ascq);
spdk_scsi_task_set_status(task, sc, sk, asc, ascq);
spdk_scsi_lun_complete_task(task->lun, task);
}
static void
spdk_bdev_scsi_task_complete_mgmt(struct spdk_bdev_io *bdev_io, bool success,
void *cb_arg)
{
struct spdk_scsi_task *task = cb_arg;
if (success) {
task->response = SPDK_SCSI_TASK_MGMT_RESP_SUCCESS;
}
spdk_scsi_lun_complete_mgmt_task(task->lun, task);
}
static int
spdk_bdev_scsi_read_write_lba_check(struct spdk_scsi_task *primary,
struct spdk_scsi_task *task,
uint64_t lba, uint64_t cmd_num_blocks,
uint64_t bdev_num_blocks)
{
if (!primary) {
/*
* Indicates this task is a primary task, we check whether the LBA and
* range is valid. If such info of primary is valid, all subtasks' are valid.
*/
if (lba >= bdev_num_blocks || cmd_num_blocks > bdev_num_blocks ||
lba > (bdev_num_blocks - cmd_num_blocks)) {
SPDK_TRACELOG(SPDK_TRACE_SCSI, "end of media\n");
spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
SPDK_SCSI_ASC_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE,
SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
return -1;
}
} else {
/*
* Indicates this task is a subtask, we do not need to check the LBA range.
* Need to check condition of primary task.
*/
if (primary->status == SPDK_SCSI_STATUS_CHECK_CONDITION) {
memcpy(task->sense_data, primary->sense_data,
primary->sense_data_len);
task->status = SPDK_SCSI_STATUS_CHECK_CONDITION;
task->sense_data_len = primary->sense_data_len;
return -1;
}
}
return 0;
}
static int
spdk_bdev_scsi_read(struct spdk_bdev *bdev,
struct spdk_scsi_task *task, uint64_t lba,
uint32_t len)
{
uint64_t blen;
uint64_t offset;
uint64_t nbytes;
blen = spdk_bdev_get_block_size(bdev);
lba += (task->offset / blen);
offset = lba * blen;
nbytes = task->length;
SPDK_TRACELOG(SPDK_TRACE_SCSI,
"Read: lba=%"PRIu64", len=%"PRIu64"\n",
lba, (uint64_t)task->length / blen);
task->blockdev_io = spdk_bdev_readv(bdev, task->ch, task->iovs,
task->iovcnt, offset, nbytes,
spdk_bdev_scsi_task_complete_cmd, task);
if (!task->blockdev_io) {
SPDK_ERRLOG("spdk_bdev_readv() failed\n");
spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
SPDK_SCSI_SENSE_NO_SENSE,
SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
return SPDK_SCSI_TASK_COMPLETE;
}
task->data_transferred = nbytes;
task->status = SPDK_SCSI_STATUS_GOOD;
return SPDK_SCSI_TASK_PENDING;
}
static int
spdk_bdev_scsi_write(struct spdk_bdev *bdev,
struct spdk_scsi_task *task, uint64_t lba, uint32_t len)
{
uint64_t blen;
uint64_t offset;
uint64_t nbytes;
struct spdk_scsi_task *primary = task->parent;
if (len == 0) {
task->data_transferred = 0;
spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
SPDK_SCSI_SENSE_NO_SENSE,
SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
return SPDK_SCSI_TASK_COMPLETE;
}
blen = spdk_bdev_get_block_size(bdev);
offset = lba * blen;
nbytes = ((uint64_t)len) * blen;
SPDK_TRACELOG(SPDK_TRACE_SCSI,
"Write: lba=%"PRIu64", len=%u\n",
lba, len);
if (nbytes > task->transfer_len) {
SPDK_ERRLOG("nbytes(%zu) > transfer_len(%u)\n",
(size_t)nbytes, task->transfer_len);
spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
SPDK_SCSI_SENSE_NO_SENSE,
SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
return SPDK_SCSI_TASK_COMPLETE;
}
offset += task->offset;
task->blockdev_io = spdk_bdev_writev(bdev, task->ch, task->iovs,
task->iovcnt, offset, task->length,
spdk_bdev_scsi_task_complete_cmd,
task);
if (!task->blockdev_io) {
SPDK_ERRLOG("spdk_bdev_writev failed\n");
spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
SPDK_SCSI_SENSE_NO_SENSE,
SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
return SPDK_SCSI_TASK_COMPLETE;
} else {
if (!primary) {
task->data_transferred += task->length;
} else {
primary->data_transferred += task->length;
}
}
SPDK_TRACELOG(SPDK_TRACE_SCSI, "Wrote %"PRIu64"/%"PRIu64" bytes\n",
(uint64_t)task->length, nbytes);
task->status = SPDK_SCSI_STATUS_GOOD;
return SPDK_SCSI_TASK_PENDING;
}
static int
spdk_bdev_scsi_sync(struct spdk_bdev *bdev, struct spdk_scsi_task *task,
uint64_t lba, uint32_t num_blocks)
{
uint64_t bdev_num_blocks;
uint64_t blen;
uint64_t offset;
uint64_t nbytes;
if (num_blocks == 0) {
return SPDK_SCSI_TASK_COMPLETE;
}
bdev_num_blocks = spdk_bdev_get_num_blocks(bdev);
blen = spdk_bdev_get_block_size(bdev);
offset = lba * blen;
nbytes = num_blocks * blen;
if (lba >= bdev_num_blocks || num_blocks > bdev_num_blocks ||
lba > (bdev_num_blocks - num_blocks)) {
SPDK_ERRLOG("end of media\n");
spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
SPDK_SCSI_SENSE_NO_SENSE,
SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
return SPDK_SCSI_TASK_COMPLETE;
}
task->blockdev_io = spdk_bdev_flush(bdev, task->ch, offset, nbytes,
spdk_bdev_scsi_task_complete_cmd, task);
if (!task->blockdev_io) {
SPDK_ERRLOG("spdk_bdev_flush() failed\n");
spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
SPDK_SCSI_SENSE_NO_SENSE,
SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
return SPDK_SCSI_TASK_COMPLETE;
}
task->data_transferred = 0;
task->status = SPDK_SCSI_STATUS_GOOD;
return SPDK_SCSI_TASK_PENDING;
}
static int
spdk_bdev_scsi_readwrite(struct spdk_bdev *bdev,
struct spdk_scsi_task *task,
uint64_t lba, uint32_t xfer_len, bool is_read)
{
if (task->dxfer_dir != SPDK_SCSI_DIR_NONE &&
task->dxfer_dir != (is_read ? SPDK_SCSI_DIR_FROM_DEV : SPDK_SCSI_DIR_TO_DEV)) {
SPDK_ERRLOG("Incorrect data direction\n");
spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
SPDK_SCSI_SENSE_NO_SENSE,
SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
return SPDK_SCSI_TASK_COMPLETE;
}
if (spdk_bdev_scsi_read_write_lba_check(task->parent, task, lba,
xfer_len, spdk_bdev_get_num_blocks(bdev)) < 0) {
/* spdk_bdev_scsi_read_write_lba_check() already set the correct sense code */
return SPDK_SCSI_TASK_COMPLETE;
}
if (is_read) {
return spdk_bdev_scsi_read(bdev, task, lba, xfer_len);
} else {
return spdk_bdev_scsi_write(bdev, task, lba, xfer_len);
}
}
static int
spdk_bdev_scsi_unmap(struct spdk_bdev *bdev,
struct spdk_scsi_task *task)
{
uint8_t *data;
struct spdk_scsi_unmap_bdesc *desc;
uint32_t bdesc_count, max_unmap_bdesc_count;
int bdesc_data_len;
int data_len;
if (task->iovcnt == 1) {
data = (uint8_t *)task->iovs[0].iov_base;
data_len = task->iovs[0].iov_len;
} else {
data = spdk_scsi_task_gather_data(task, &data_len);
}
/*
* The UNMAP BLOCK DESCRIPTOR DATA LENGTH field specifies the length in
* bytes of the UNMAP block descriptors that are available to be
* transferred from the Data-Out Buffer. The unmap block descriptor data
* length should be a multiple of 16. If the unmap block descriptor data
* length is not a multiple of 16, then the last unmap block descriptor
* is incomplete and shall be ignored.
*/
bdesc_data_len = from_be16(&data[2]);
bdesc_count = bdesc_data_len / 16;
assert(bdesc_data_len <= data_len);
if (task->iovcnt == 1) {
desc = (struct spdk_scsi_unmap_bdesc *)&data[8];
} else {
desc = spdk_scsi_task_alloc_data(task, bdesc_data_len - 8);
memcpy(desc, &data[8], bdesc_data_len - 8);
spdk_free(data);
}
max_unmap_bdesc_count = spdk_bdev_get_max_unmap_descriptors(bdev);
if (bdesc_count > max_unmap_bdesc_count) {
SPDK_ERRLOG("Error - supported unmap block descriptor count limit"
" is %u\n", max_unmap_bdesc_count);
spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
SPDK_SCSI_SENSE_NO_SENSE,
SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
return SPDK_SCSI_TASK_COMPLETE;
} else if (bdesc_data_len > data_len) {
SPDK_ERRLOG("Error - bdesc_data_len (%d) > data_len (%d)",
bdesc_data_len, data_len);
spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
return SPDK_SCSI_TASK_COMPLETE;
}
task->blockdev_io = spdk_bdev_unmap(bdev, task->ch, desc,
bdesc_count, spdk_bdev_scsi_task_complete_cmd,
task);
if (!task->blockdev_io) {
SPDK_ERRLOG("SCSI Unmapping failed\n");
spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
SPDK_SCSI_SENSE_NO_SENSE,
SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
return SPDK_SCSI_TASK_COMPLETE;
}
return SPDK_SCSI_TASK_PENDING;
}
static int
spdk_bdev_scsi_process_block(struct spdk_bdev *bdev,
struct spdk_scsi_task *task)
{
uint64_t lba;
uint32_t xfer_len;
uint32_t len = 0;
uint8_t *cdb = task->cdb;
/* XXX: We need to support FUA bit for writes! */
switch (cdb[0]) {
case SPDK_SBC_READ_6:
case SPDK_SBC_WRITE_6:
lba = (uint64_t)cdb[1] << 16;
lba |= (uint64_t)cdb[2] << 8;
lba |= (uint64_t)cdb[3];
xfer_len = cdb[4];
if (xfer_len == 0) {
xfer_len = 256;
}
return spdk_bdev_scsi_readwrite(bdev, task, lba, xfer_len,
cdb[0] == SPDK_SBC_READ_6);
case SPDK_SBC_READ_10:
case SPDK_SBC_WRITE_10:
lba = from_be32(&cdb[2]);
xfer_len = from_be16(&cdb[7]);
return spdk_bdev_scsi_readwrite(bdev, task, lba, xfer_len,
cdb[0] == SPDK_SBC_READ_10);
case SPDK_SBC_READ_12:
case SPDK_SBC_WRITE_12:
lba = from_be32(&cdb[2]);
xfer_len = from_be32(&cdb[6]);
return spdk_bdev_scsi_readwrite(bdev, task, lba, xfer_len,
cdb[0] == SPDK_SBC_READ_12);
case SPDK_SBC_READ_16:
case SPDK_SBC_WRITE_16:
lba = from_be64(&cdb[2]);
xfer_len = from_be32(&cdb[10]);
return spdk_bdev_scsi_readwrite(bdev, task, lba, xfer_len,
cdb[0] == SPDK_SBC_READ_16);
case SPDK_SBC_READ_CAPACITY_10: {
uint64_t num_blocks = spdk_bdev_get_num_blocks(bdev);
uint8_t buffer[8];
if (num_blocks - 1 > 0xffffffffULL) {
memset(buffer, 0xff, 4);
} else {
to_be32(buffer, num_blocks - 1);
}
to_be32(&buffer[4], spdk_bdev_get_block_size(bdev));
len = spdk_min(task->length, sizeof(buffer));
if (spdk_scsi_task_scatter_data(task, buffer, len) < 0)
break;
task->data_transferred = len;
task->status = SPDK_SCSI_STATUS_GOOD;
break;
}
case SPDK_SPC_SERVICE_ACTION_IN_16:
switch (cdb[1] & 0x1f) { /* SERVICE ACTION */
case SPDK_SBC_SAI_READ_CAPACITY_16: {
uint8_t buffer[32] = {0};
to_be64(&buffer[0], spdk_bdev_get_num_blocks(bdev) - 1);
to_be32(&buffer[8], spdk_bdev_get_block_size(bdev));
/*
* Set the TPE bit to 1 to indicate thin provisioning.
* The position of TPE bit is the 7th bit in 14th byte
* in READ CAPACITY (16) parameter data.
*/
if (spdk_bdev_io_type_supported(bdev, SPDK_BDEV_IO_TYPE_UNMAP)) {
buffer[14] |= 1 << 7;
}
len = spdk_min(from_be32(&cdb[10]), sizeof(buffer));
if (spdk_scsi_task_scatter_data(task, buffer, len) < 0)
break;
task->data_transferred = len;
task->status = SPDK_SCSI_STATUS_GOOD;
break;
}
default:
return SPDK_SCSI_TASK_UNKNOWN;
}
break;
case SPDK_SBC_SYNCHRONIZE_CACHE_10:
case SPDK_SBC_SYNCHRONIZE_CACHE_16:
if (cdb[0] == SPDK_SBC_SYNCHRONIZE_CACHE_10) {
lba = from_be32(&cdb[2]);
len = from_be16(&cdb[7]);
} else {
lba = from_be64(&cdb[2]);
len = from_be32(&cdb[10]);
}
if (len == 0) {
len = spdk_bdev_get_num_blocks(bdev) - lba;
}
return spdk_bdev_scsi_sync(bdev, task, lba, len);
break;
case SPDK_SBC_UNMAP:
return spdk_bdev_scsi_unmap(bdev, task);
default:
return SPDK_SCSI_TASK_UNKNOWN;
}
return SPDK_SCSI_TASK_COMPLETE;
}
static int
spdk_bdev_scsi_check_len(struct spdk_scsi_task *task, int len, int min_len)
{
if (len >= min_len)
return 0;
/* INVALID FIELD IN CDB */
spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
return -1;
}
static int
spdk_bdev_scsi_process_primary(struct spdk_bdev *bdev,
struct spdk_scsi_task *task)
{
int alloc_len = -1;
int data_len = -1;
uint8_t *cdb = task->cdb;
uint8_t *data = NULL;
int rc = 0;
int pllen, md = 0;
int pf, sp;
int bdlen, llba;
int dbd, pc, page, subpage;
int cmd_parsed = 0;
switch (cdb[0]) {
case SPDK_SPC_INQUIRY:
alloc_len = from_be16(&cdb[3]);
data_len = spdk_max(4096, alloc_len);
data = spdk_zmalloc(data_len, 0, NULL);
assert(data != NULL);
rc = spdk_bdev_scsi_inquiry(bdev, task, cdb, data, data_len);
data_len = spdk_min(rc, data_len);
if (rc < 0) {
break;
}
SPDK_TRACEDUMP(SPDK_TRACE_DEBUG, "INQUIRY", data, data_len);
break;
case SPDK_SPC_REPORT_LUNS: {
int sel;
sel = cdb[2];
SPDK_TRACELOG(SPDK_TRACE_DEBUG, "sel=%x\n", sel);
alloc_len = from_be32(&cdb[6]);
rc = spdk_bdev_scsi_check_len(task, alloc_len, 16);
if (rc < 0) {
break;
}
data_len = spdk_max(4096, alloc_len);
data = spdk_zmalloc(data_len, 0, NULL);
assert(data != NULL);
rc = spdk_bdev_scsi_report_luns(task->lun, sel, data, data_len);
data_len = rc;
if (rc < 0) {
spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
SPDK_SCSI_SENSE_NO_SENSE,
SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
break;
}
SPDK_TRACEDUMP(SPDK_TRACE_DEBUG, "REPORT LUNS", data, data_len);
break;
}
case SPDK_SPC_MODE_SELECT_6:
case SPDK_SPC_MODE_SELECT_10:
if (cdb[0] == SPDK_SPC_MODE_SELECT_6) {
/* MODE_SELECT(6) must have at least a 4 byte header. */
md = 4;
pllen = cdb[4];
} else {
/* MODE_SELECT(10) must have at least an 8 byte header. */
md = 8;
pllen = from_be16(&cdb[7]);
}
if (pllen == 0) {
break;
}
rc = spdk_bdev_scsi_check_len(task, pllen, md);
if (rc < 0) {
break;
}
data = spdk_scsi_task_gather_data(task, &rc);
if (rc < 0) {
break;
}
data_len = rc;
if (cdb[0] == SPDK_SPC_MODE_SELECT_6) {
rc = spdk_bdev_scsi_check_len(task, data_len, 4);
if (rc >= 0) {
bdlen = data[3];
}
} else {
rc = spdk_bdev_scsi_check_len(task, data_len, 8);
if (rc >= 0) {
bdlen = from_be16(&data[6]);
}
}
if (rc < 0) {
break;
}
pf = !!(cdb[1] & 0x10);
sp = !!(cdb[1] & 0x1);
/* page data */
rc = spdk_bdev_scsi_mode_select_page(
bdev, cdb,
pf, sp,
&data[md + bdlen],
pllen - (md + bdlen));
if (rc < 0) {
spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
SPDK_SCSI_SENSE_NO_SENSE,
SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
break;
}
rc = pllen;
data_len = 0;
break;
case SPDK_SPC_MODE_SENSE_6:
alloc_len = cdb[4];
md = 6;
case SPDK_SPC_MODE_SENSE_10:
llba = 0;
if (md == 0) {
alloc_len = from_be16(&cdb[7]);
llba = !!(cdb[1] & 0x10);
md = 10;
}
dbd = !!(cdb[1] & 0x8);
pc = (cdb[2] & 0xc0) >> 6;
page = cdb[2] & 0x3f;
subpage = cdb[3];
/* First call with no buffer to discover needed buffer size */
rc = spdk_bdev_scsi_mode_sense(bdev, md,
cdb, dbd, llba, pc,
page, subpage,
NULL, task);
if (rc < 0) {
break;
}
data_len = rc;
data = spdk_zmalloc(data_len, 0, NULL);
assert(data != NULL);
/* First call with no buffer to discover needed buffer size */
rc = spdk_bdev_scsi_mode_sense(bdev, md,
cdb, dbd, llba, pc,
page, subpage,
data, task);
if (rc < 0) {
/* INVALID FIELD IN CDB */
spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
break;
}
break;
case SPDK_SPC_REQUEST_SENSE: {
int desc;
int sk, asc, ascq;
desc = cdb[1] & 0x1;
if (desc != 0) {
/* INVALID FIELD IN CDB */
spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
break;
}
alloc_len = cdb[4];
/* NO ADDITIONAL SENSE INFORMATION */
sk = SPDK_SCSI_SENSE_NO_SENSE;
asc = 0x00;
ascq = 0x00;
spdk_scsi_task_build_sense_data(task, sk, asc, ascq);
data_len = task->sense_data_len;
data = spdk_zmalloc(data_len, 0, NULL);
assert(data != NULL);
memcpy(data, task->sense_data, data_len);
break;
}
case SPDK_SPC_LOG_SELECT:
SPDK_TRACELOG(SPDK_TRACE_SCSI, "LOG_SELECT\n");
cmd_parsed = 1;
case SPDK_SPC_LOG_SENSE:
if (!cmd_parsed) {
SPDK_TRACELOG(SPDK_TRACE_SCSI, "LOG_SENSE\n");
}
/* INVALID COMMAND OPERATION CODE */
spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
SPDK_SCSI_ASC_INVALID_COMMAND_OPERATION_CODE,
SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
rc = -1;
break;
case SPDK_SPC_TEST_UNIT_READY:
SPDK_TRACELOG(SPDK_TRACE_SCSI, "TEST_UNIT_READY\n");
cmd_parsed = 1;
case SPDK_SBC_START_STOP_UNIT:
if (!cmd_parsed) {
SPDK_TRACELOG(SPDK_TRACE_SCSI, "START_STOP_UNIT\n");
}
rc = 0;
break;
default:
return SPDK_SCSI_TASK_UNKNOWN;
}
if (rc >= 0 && data_len > 0) {
assert(alloc_len >= 0);
spdk_scsi_task_scatter_data(task, data, spdk_min(alloc_len, data_len));
rc = spdk_min(data_len, alloc_len);
}
if (rc >= 0) {
task->data_transferred = rc;
task->status = SPDK_SCSI_STATUS_GOOD;
}
if (data)
spdk_free(data);
return SPDK_SCSI_TASK_COMPLETE;
}
int
spdk_bdev_scsi_execute(struct spdk_bdev *bdev, struct spdk_scsi_task *task)
{
int rc;
if ((rc = spdk_bdev_scsi_process_block(bdev, task)) == SPDK_SCSI_TASK_UNKNOWN) {
if ((rc = spdk_bdev_scsi_process_primary(bdev, task)) == SPDK_SCSI_TASK_UNKNOWN) {
SPDK_TRACELOG(SPDK_TRACE_SCSI, "unsupported SCSI OP=0x%x\n", task->cdb[0]);
/* INVALID COMMAND OPERATION CODE */
spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
SPDK_SCSI_ASC_INVALID_COMMAND_OPERATION_CODE,
SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
return SPDK_SCSI_TASK_COMPLETE;
}
}
return rc;
}
int
spdk_bdev_scsi_reset(struct spdk_bdev *bdev, struct spdk_scsi_task *task)
{
return spdk_bdev_reset(bdev, task->ch, SPDK_BDEV_RESET_SOFT,
spdk_bdev_scsi_task_complete_mgmt, task);
}