numam-spdk/lib/scsi/scsi_bdev.c
Jim Harris be9a3b9f69 bdev: pass descriptors for I/O operations
This enables checking permissions - for example,
spdk_bdev_write will fail if the descriptor was not
created with write permissions.

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

Reviewed-on: https://review.gerrithub.io/369493
Tested-by: SPDK Automated Test System <sys_sgsw@intel.com>
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
Reviewed-by: Daniel Verkamp <daniel.verkamp@intel.com>
2017-07-14 13:31:30 -04:00

1970 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_naa_ieee_extended(const char *name, uint8_t *buf)
{
int i, value, count = 0;
uint64_t 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;
}
}
local_value = *(uint64_t *)buf;
/*
* see spc3r23 7.6.3.6.2,
* NAA IEEE Extended identifer format
*/
local_value &= 0x0fff000000ffffffull;
/* NAA 02, and 00 03 47 for IEEE Intel */
local_value |= 0x2000000347000000ull;
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 < SPDK_SCSI_DEV_MAX_LUN; i++) {
if (dev->lun[i] == NULL)
continue;
if (alloc_len - (hlen + len) < 8)
return -1;
lun_id = (uint64_t)i;
if (SPDK_SCSI_DEV_MAX_LUN <= 0x0100) {
/* below 256 */
method = 0x00U;
fmt_lun = (method & 0x03U) << 62;
fmt_lun |= (lun_id & 0x00ffU) << 48;
} else if (SPDK_SCSI_DEV_MAX_LUN <= 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 protocol_id = dev->protocol_id;
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 = protocol_id;
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_naa_ieee_extended(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 = protocol_id;
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 = protocol_id;
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 = protocol_id;
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 = protocol_id;
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 = protocol_id;
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 = protocol_id;
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;
task->bdev_io = bdev_io;
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;
task->bdev_io = bdev_io;
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;
int rc;
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);
rc = spdk_bdev_readv(task->desc, task->ch, task->iovs,
task->iovcnt, offset, nbytes,
spdk_bdev_scsi_task_complete_cmd, task);
if (rc) {
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;
int rc;
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;
rc = spdk_bdev_writev(task->desc, task->ch, task->iovs,
task->iovcnt, offset, task->length,
spdk_bdev_scsi_task_complete_cmd,
task);
if (rc) {
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;
int rc;
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;
}
rc = spdk_bdev_flush(task->desc, task->ch, offset, nbytes,
spdk_bdev_scsi_task_complete_cmd, task);
if (rc) {
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;
int rc;
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_dma_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;
}
rc = spdk_bdev_unmap(task->desc, task->ch, desc,
bdesc_count, spdk_bdev_scsi_task_complete_cmd,
task);
if (rc) {
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_dma_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_dma_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;
/* FALLTHROUGH */
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_dma_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_dma_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;
/* FALLTHROUGH */
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;
/* FALLTHROUGH */
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_dma_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(task->desc, task->ch,
spdk_bdev_scsi_task_complete_mgmt, task);
}