diff --git a/autotest.sh b/autotest.sh index 29bf57bb40..74b6ed9c02 100755 --- a/autotest.sh +++ b/autotest.sh @@ -63,6 +63,7 @@ time test/lib/ioat/ioat.sh time test/lib/json/json.sh time test/lib/jsonrpc/jsonrpc.sh time test/lib/log/log.sh +time test/lib/scsi/scsi.sh timing_exit lib diff --git a/include/spdk/scsi.h b/include/spdk/scsi.h new file mode 100644 index 0000000000..64c3bf5107 --- /dev/null +++ b/include/spdk/scsi.h @@ -0,0 +1,209 @@ +/*- + * BSD LICENSE + * + * 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. + */ + +/** + * \file + * SCSI to blockdev translation layer + */ + +#ifndef SPDK_SCSI_H +#define SPDK_SCSI_H + +#include +#include +#include + +#include "spdk/queue.h" +#include "spdk/event.h" + +#define SPDK_SCSI_MAX_DEVS 1024 +#define SPDK_SCSI_DEV_MAX_LUN 64 +#define SPDK_SCSI_DEV_MAX_PORTS 4 +#define SPDK_SCSI_DEV_MAX_NAME 255 + +#define SPDK_SCSI_PORT_MAX_NAME_LENGTH 255 + +#define SPDK_SCSI_LUN_MAX_NAME_LENGTH 16 + +enum spdk_scsi_data_dir { + SPDK_SCSI_DIR_NONE = 0, + SPDK_SCSI_DIR_TO_DEV = 1, + SPDK_SCSI_DIR_FROM_DEV = 2, +}; + +enum spdk_scsi_task_func { + SPDK_SCSI_TASK_FUNC_ABORT_TASK = 0, + SPDK_SCSI_TASK_FUNC_ABORT_TASK_SET, + SPDK_SCSI_TASK_FUNC_CLEAR_TASK_SET, + SPDK_SCSI_TASK_FUNC_LUN_RESET, +}; + +enum spdk_scsi_task_type { + SPDK_SCSI_TASK_TYPE_CMD = 0, + SPDK_SCSI_TASK_TYPE_MANAGE, +}; + +struct spdk_scsi_task { + uint8_t type; + uint8_t status; + uint8_t function; /* task mgmt function */ + uint8_t response; /* task mgmt response */ + struct spdk_scsi_lun *lun; + struct spdk_scsi_port *target_port; + struct spdk_scsi_port *initiator_port; + spdk_event_t cb_event; + + uint32_t ref; + uint32_t id; + uint32_t transfer_len; + uint32_t data_out_cnt; + uint32_t dxfer_dir; + uint32_t desired_data_transfer_length; + /* Only valid for Read/Write */ + uint32_t bytes_completed; + uint32_t length; + + /** + * Amount of data actually transferred. Can be less than requested + * transfer_len - i.e. SCSI INQUIRY. + */ + uint32_t data_transferred; + uint32_t alloc_len; + + uint64_t offset; + struct iovec iov; + struct spdk_scsi_task *parent; + + void (*free_fn)(struct spdk_scsi_task *); + + uint8_t *cdb; + uint8_t *iobuf; + + uint8_t sense_data[32]; + size_t sense_data_len; + + uint8_t *rbuf; /* read buffer */ + void *blockdev_io; + + TAILQ_ENTRY(spdk_scsi_task) scsi_link; + + /* + * Pointer to scsi task owner's outstanding + * task counter. Inc/Dec by get/put task functions. + * Note: in the future, we could consider replacing this + * with an owner-provided task management fuction that + * could perform protocol specific task mangement + * operations (such as tracking outstanding tasks). + */ + uint32_t *owner_task_ctr; + + uint32_t abort_id; + TAILQ_HEAD(subtask_list, spdk_scsi_task) subtask_list; +}; + +struct spdk_scsi_port { + struct spdk_scsi_dev *dev; + uint64_t id; + uint16_t index; + char name[SPDK_SCSI_PORT_MAX_NAME_LENGTH]; +}; + +struct spdk_scsi_dev { + int id; + int is_allocated; + + char name[SPDK_SCSI_DEV_MAX_NAME]; + + int maxlun; + struct spdk_scsi_lun *lun[SPDK_SCSI_DEV_MAX_LUN]; + + int num_ports; + struct spdk_scsi_port port[SPDK_SCSI_DEV_MAX_PORTS]; +}; + +void spdk_scsi_dev_destruct(struct spdk_scsi_dev *dev); +void spdk_scsi_dev_queue_mgmt_task(struct spdk_scsi_dev *dev, struct spdk_scsi_task *task); +void spdk_scsi_dev_queue_task(struct spdk_scsi_dev *dev, struct spdk_scsi_task *task); +int spdk_scsi_dev_add_port(struct spdk_scsi_dev *dev, uint64_t id, const char *name); +struct spdk_scsi_port *spdk_scsi_dev_find_port_by_id(struct spdk_scsi_dev *dev, uint64_t id); +void spdk_scsi_dev_print(struct spdk_scsi_dev *dev); + +/** + +\brief Constructs a SCSI device object using the given parameters. + +\param name Name for the SCSI device. +\param queue_depth Queue depth for the SCSI device. This queue depth is + a combined queue depth for all LUNs in the device. +\param lun_list List of LUN objects for the SCSI device. Caller is + responsible for managing the memory containing this list. +\param lun_id_list List of LUN IDs for the LUN in this SCSI device. Caller is + responsible for managing the memory containing this list. + lun_id_list[x] is the LUN ID for lun_list[x]. +\param num_luns Number of entries in lun_list and lun_id_list. +\return The constructed spdk_scsi_dev object. + +*/ +struct spdk_scsi_dev *spdk_scsi_dev_construct(const char *name, + char *lun_name_list[], + int *lun_id_list, + int num_luns); + +void spdk_scsi_dev_delete_lun(struct spdk_scsi_dev *dev, struct spdk_scsi_lun *lun); + + +int spdk_scsi_port_construct(struct spdk_scsi_port *port, uint64_t id, + uint16_t index, const char *name); + + +void spdk_scsi_task_construct(struct spdk_scsi_task *task, uint32_t *owner_task_ctr, + struct spdk_scsi_task *parent); +void spdk_put_task(struct spdk_scsi_task *task); +void spdk_scsi_task_alloc_data(struct spdk_scsi_task *task, uint32_t alloc_len, + uint8_t **data); +int spdk_scsi_task_build_sense_data(struct spdk_scsi_task *task, int sk, int asc, + int ascq); +void spdk_scsi_task_set_check_condition(struct spdk_scsi_task *task, int sk, + int asc, int ascq); + +static inline struct spdk_scsi_task * +spdk_scsi_task_get_primary(struct spdk_scsi_task *task) +{ + if (task->parent) { + return task->parent; + } else { + return task; + } +} + +#endif /* SPDK_SCSI_H */ diff --git a/lib/Makefile b/lib/Makefile index 1cf5fc8213..714bc9aa85 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -34,7 +34,7 @@ SPDK_ROOT_DIR := $(abspath $(CURDIR)/..) include $(SPDK_ROOT_DIR)/mk/spdk.common.mk -DIRS-y += bdev conf copy cunit event json jsonrpc log memory rpc trace util nvme nvmf ioat +DIRS-y += bdev conf copy cunit event json jsonrpc log memory rpc trace util nvme nvmf scsi ioat .PHONY: all clean $(DIRS-y) diff --git a/lib/scsi/Makefile b/lib/scsi/Makefile new file mode 100644 index 0000000000..5d79ddc179 --- /dev/null +++ b/lib/scsi/Makefile @@ -0,0 +1,41 @@ +# +# BSD LICENSE +# +# 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. +# + +SPDK_ROOT_DIR := $(abspath $(CURDIR)/../..) +include $(SPDK_ROOT_DIR)/mk/spdk.common.mk + +CFLAGS += $(DPDK_INC) +C_SRCS = dev.c lun.c lun_db.c port.c scsi.c scsi_bdev.c scsi_rpc.c task.c +LIBNAME = scsi + +include $(SPDK_ROOT_DIR)/mk/spdk.lib.mk diff --git a/lib/scsi/dev.c b/lib/scsi/dev.c new file mode 100644 index 0000000000..a1c269b732 --- /dev/null +++ b/lib/scsi/dev.c @@ -0,0 +1,258 @@ +/*- + * BSD LICENSE + * + * Copyright (C) 2008-2012 Daisuke Aoyama . + * 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" + +static struct spdk_scsi_dev g_devs[SPDK_SCSI_MAX_DEVS]; + +struct spdk_scsi_dev * +spdk_scsi_dev_get_list(void) +{ + return g_devs; +} + +static struct spdk_scsi_dev * +allocate_dev(void) +{ + struct spdk_scsi_dev *dev; + int i; + + for (i = 0; i < SPDK_SCSI_MAX_DEVS; i++) { + dev = &g_devs[i]; + if (!dev->is_allocated) { + memset(dev, 0, sizeof(*dev)); + dev->id = i; + dev->is_allocated = 1; + return dev; + } + } + + return NULL; +} + +static void +free_dev(struct spdk_scsi_dev *dev) +{ + dev->is_allocated = 0; +} + +void +spdk_scsi_dev_destruct(struct spdk_scsi_dev *dev) +{ + int i; + + if (dev == NULL) { + return; + } + + for (i = 0; i < dev->maxlun; i++) { + if (dev->lun[i] == NULL) { + continue; + } + + spdk_scsi_lun_unclaim(dev->lun[i]); + dev->lun[i] = NULL; + } + + free_dev(dev); +} + +static void +spdk_scsi_dev_add_lun(struct spdk_scsi_dev *dev, + struct spdk_scsi_lun *lun, int id) +{ + spdk_scsi_lun_claim(lun); + lun->id = id; + lun->dev = dev; + dev->lun[id] = lun; + if (dev->maxlun <= id) { + dev->maxlun = id + 1; + } +} + +void +spdk_scsi_dev_delete_lun(struct spdk_scsi_dev *dev, + struct spdk_scsi_lun *lun) +{ + int i; + int maxlun = 0; + + for (i = 0; i < dev->maxlun; i++) { + if (dev->lun[i] && dev->lun[i] == lun) + dev->lun[i] = NULL; + } + + for (i = 0; i < dev->maxlun; i++) { + if (dev->lun[i]) { + if (maxlun <= dev->lun[i]->id) { + maxlun = dev->lun[i]->id + 1; + } + } + } + dev->maxlun = maxlun; +} + +/* This typedef exists to work around an astyle 2.05 bug. + * Remove it when astyle is fixed. +*/ +typedef struct spdk_scsi_dev _spdk_scsi_dev; + +_spdk_scsi_dev * +spdk_scsi_dev_construct(const char *name, char *lun_name_list[], int *lun_id_list, int num_luns) +{ + struct spdk_scsi_dev *dev; + struct spdk_bdev *bdev; + struct spdk_scsi_lun *lun; + int i; + + if (num_luns == 0) { + SPDK_ERRLOG("device %s: no LUNs specified\n", name); + return NULL; + } + + if (lun_id_list[0] != 0) { + SPDK_ERRLOG("device %s: no LUN 0 specified\n", name); + return NULL; + } + + for (i = 0; i < num_luns; i++) { + if (lun_name_list[i] == NULL) { + SPDK_ERRLOG("NULL spdk_scsi_lun for LUN %d\n", + lun_id_list[i]); + return NULL; + } + } + + dev = allocate_dev(); + if (dev == NULL) { + return NULL; + } + + strncpy(dev->name, name, SPDK_SCSI_DEV_MAX_NAME); + + dev->num_ports = 0; + dev->maxlun = 0; + + for (i = 0; i < num_luns; i++) { + bdev = spdk_bdev_db_get_by_name(lun_name_list[i]); + if (bdev == NULL) { + free_dev(dev); + return NULL; + } + + lun = spdk_scsi_lun_construct(bdev->name, bdev); + if (lun == NULL) { + free_dev(dev); + return NULL; + } + + spdk_scsi_dev_add_lun(dev, lun, lun_id_list[i]); + } + + return dev; +} + +void +spdk_scsi_dev_queue_mgmt_task(struct spdk_scsi_dev *dev, + struct spdk_scsi_task *task) +{ + assert(task != NULL); + + spdk_scsi_lun_task_mgmt_execute(task); +} + +void +spdk_scsi_dev_queue_task(struct spdk_scsi_dev *dev, + struct spdk_scsi_task *task) +{ + assert(task != NULL); + + /* ready to enqueue, disk is valid for LUN access */ + spdk_scsi_lun_append_task(task->lun, task); + spdk_scsi_lun_execute_tasks(task->lun); +} + +int +spdk_scsi_dev_add_port(struct spdk_scsi_dev *dev, uint64_t id, const char *name) +{ + struct spdk_scsi_port *port; + int rc; + + if (dev->num_ports == SPDK_SCSI_DEV_MAX_PORTS) { + SPDK_ERRLOG("device already has %d ports\n", SPDK_SCSI_DEV_MAX_PORTS); + return -1; + } + + port = &dev->port[dev->num_ports]; + + rc = spdk_scsi_port_construct(port, id, dev->num_ports, name); + if (rc != 0) { + return rc; + } + + dev->num_ports++; + return 0; +} + +struct spdk_scsi_port * +spdk_scsi_dev_find_port_by_id(struct spdk_scsi_dev *dev, uint64_t id) +{ + int i; + + for (i = 0; i < dev->num_ports; i++) { + if (dev->port[i].id == id) { + return &dev->port[i]; + } + } + + /* No matching port found. */ + return NULL; +} + +void +spdk_scsi_dev_print(struct spdk_scsi_dev *dev) +{ + struct spdk_scsi_lun *lun; + int i; + + printf("device %d HDD UNIT\n", dev->id); + + for (i = 0; i < dev->maxlun; i++) { + lun = dev->lun[i]; + if (lun == NULL) + continue; + printf("device %d: LUN%d %s\n", dev->id, i, lun->name); + } +} + diff --git a/lib/scsi/lun.c b/lib/scsi/lun.c new file mode 100644 index 0000000000..d7b1d2964b --- /dev/null +++ b/lib/scsi/lun.c @@ -0,0 +1,392 @@ +/*- + * BSD LICENSE + * + * Copyright (C) 2008-2012 Daisuke Aoyama . + * 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" + +void +spdk_scsi_lun_complete_task(struct spdk_scsi_lun *lun, struct spdk_scsi_task *task) +{ + if (lun) { + spdk_trace_record(TRACE_SCSI_TASK_DONE, lun->dev->id, 0, (uintptr_t)task, 0); + } + spdk_event_call(task->cb_event); + + if (lun && !TAILQ_EMPTY(&lun->pending_tasks)) { + spdk_scsi_lun_execute_tasks(lun); + } +} + +void +spdk_scsi_lun_clear_all(struct spdk_scsi_lun *lun) +{ + struct spdk_scsi_task *task, *task_tmp; + + /* + * This function is called from one location, after the backend LUN + * device was reset. Can assume are no active tasks in the + * backend that need to be terminated. Just need to queue all tasks + * back to frontend for any final processing and cleanup. + * + * Queue the tasks back roughly in the order they were received + * ('cleanup' = oldest, 'tasks' = current, and 'pending' = newest) + */ + + TAILQ_FOREACH_SAFE(task, &lun->tasks, scsi_link, task_tmp) { + TAILQ_REMOVE(&lun->tasks, task, scsi_link); + spdk_scsi_task_set_check_condition(task, SPDK_SCSI_SENSE_ABORTED_COMMAND, 0, 0); + spdk_scsi_lun_complete_task(lun, task); + } + + TAILQ_FOREACH_SAFE(task, &lun->pending_tasks, scsi_link, task_tmp) { + TAILQ_REMOVE(&lun->pending_tasks, task, scsi_link); + spdk_scsi_task_set_check_condition(task, SPDK_SCSI_SENSE_ABORTED_COMMAND, + 0, 0); + spdk_scsi_lun_complete_task(lun, task); + } +} + +static int +spdk_scsi_lun_abort_all(struct spdk_scsi_task *mtask, + struct spdk_scsi_lun *lun, + struct spdk_scsi_port *initiator_port) +{ + if (!lun) { + mtask->response = SPDK_SCSI_TASK_MGMT_RESP_INVALID_LUN; + return -1; + } + + mtask->response = SPDK_SCSI_TASK_MGMT_RESP_REJECT_FUNC_NOT_SUPPORTED; + return -1; +} + +static int +spdk_scsi_lun_abort_task(struct spdk_scsi_task *mtask, + struct spdk_scsi_lun *lun, + struct spdk_scsi_port *initiator_port, + uint32_t task_tag) +{ + if (!lun) { + /* LUN does not exist */ + mtask->response = SPDK_SCSI_TASK_MGMT_RESP_INVALID_LUN; + return -1; + } + + mtask->response = SPDK_SCSI_TASK_MGMT_RESP_REJECT_FUNC_NOT_SUPPORTED; + return -1; +} + +static int +spdk_scsi_lun_reset(struct spdk_scsi_task *mtask, struct spdk_scsi_lun *lun) +{ + if (!lun) { + /* LUN does not exist */ + mtask->response = SPDK_SCSI_TASK_MGMT_RESP_INVALID_LUN; + spdk_scsi_lun_complete_task(NULL, mtask); + return -1; + } + + spdk_bdev_scsi_reset(lun->bdev, mtask); + return 0; +} + +int +spdk_scsi_lun_task_mgmt_execute(struct spdk_scsi_task *task) +{ + int rc; + + if (!task) { + return -1; + } + + switch (task->function) { + case SPDK_SCSI_TASK_FUNC_ABORT_TASK: + rc = spdk_scsi_lun_abort_task(task, task->lun, + task->initiator_port, + task->abort_id); + if (rc < 0) { + SPDK_ERRLOG("ABORT_TASK failed\n"); + } + + break; + + case SPDK_SCSI_TASK_FUNC_ABORT_TASK_SET: + rc = spdk_scsi_lun_abort_all(task, task->lun, + task->initiator_port); + if (rc < 0) { + SPDK_ERRLOG("ABORT_TASK_SET failed\n"); + } + + break; + + case SPDK_SCSI_TASK_FUNC_LUN_RESET: + rc = spdk_scsi_lun_reset(task, task->lun); + if (rc < 0) { + SPDK_ERRLOG("LUN_RESET failed\n"); + } + return rc; + + default: + SPDK_ERRLOG("Unknown Task Management Function!\n"); + /* + * Task management functions other than those above should never + * reach this point having been filtered by the frontend. Reject + * the task as being unsupported. + */ + task->response = SPDK_SCSI_TASK_MGMT_RESP_REJECT_FUNC_NOT_SUPPORTED; + rc = -1; + break; + } + + spdk_scsi_lun_complete_task(task->lun, task); + + return rc; +} + +static void +complete_task_with_no_lun(struct spdk_scsi_task *task) +{ + uint8_t *data; + uint32_t allocation_len; + int data_len; + + if (task->cdb[0] == SPDK_SPC_INQUIRY) { + /* + * SPC-4 states that INQUIRY commands to an unsupported LUN + * must be served with PERIPHERAL QUALIFIER = 0x3 and + * PERIPHERAL DEVICE TYPE = 0x1F. + */ + allocation_len = from_be16(&task->cdb[3]); + spdk_scsi_task_alloc_data(task, allocation_len, &data); + data_len = 36; + memset(data, 0, data_len); + /* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */ + data[0] = 0x03 << 5 | 0x1f; + /* ADDITIONAL LENGTH */ + data[4] = data_len - 5; + task->data_transferred = (uint64_t)data_len; + task->status = SPDK_SCSI_STATUS_GOOD; + } else { + /* LOGICAL UNIT NOT SUPPORTED */ + spdk_scsi_task_set_check_condition(task, + SPDK_SCSI_SENSE_ILLEGAL_REQUEST, 0x25, 0x00); + task->data_transferred = 0; + } + spdk_scsi_lun_complete_task(NULL, task); +} + +void +spdk_scsi_lun_append_task(struct spdk_scsi_lun *lun, struct spdk_scsi_task *task) +{ + if (lun == NULL) { + complete_task_with_no_lun(task); + return; + } + + TAILQ_INSERT_TAIL(&lun->pending_tasks, task, scsi_link); +} + +void +spdk_scsi_lun_execute_tasks(struct spdk_scsi_lun *lun) +{ + struct spdk_scsi_task *task, *task_tmp; + int rc; + + TAILQ_FOREACH_SAFE(task, &lun->pending_tasks, scsi_link, task_tmp) { + task->status = SPDK_SCSI_STATUS_GOOD; + spdk_trace_record(TRACE_SCSI_TASK_START, lun->dev->id, task->length, (uintptr_t)task, 0); + rc = spdk_bdev_scsi_execute(lun->bdev, task); + + /* Task is removed from the pending list if it gets the slot. */ + if (task->status == SPDK_SCSI_STATUS_TASK_SET_FULL) { + break; + } + + TAILQ_REMOVE(&lun->pending_tasks, task, scsi_link); + + switch (rc) { + case SPDK_SCSI_TASK_PENDING: + TAILQ_INSERT_TAIL(&lun->tasks, task, scsi_link); + break; + + case SPDK_SCSI_TASK_COMPLETE: + spdk_scsi_lun_complete_task(lun, task); + break; + + default: + abort(); + } + } +} + +/*! + +\brief Constructs a new spdk_scsi_lun object based on the provided parameters. + +\param name Name for the SCSI LUN. +\param blockdev Blockdev associated with this LUN + +\return NULL if blockdev == NULL +\return pointer to the new spdk_scsi_lun object otherwise + +*/ +_spdk_scsi_lun * +spdk_scsi_lun_construct(const char *name, struct spdk_bdev *bdev) +{ + struct spdk_scsi_lun *lun; + int rc; + + if (bdev == NULL) { + SPDK_ERRLOG("blockdev must be non-NULL\n"); + return NULL; + } + + lun = spdk_lun_db_get_lun(name, 0); + if (lun) { + return lun; + } + + lun = calloc(1, sizeof(*lun)); + if (lun == NULL) { + SPDK_ERRLOG("could not allocate lun\n"); + return NULL; + } + + TAILQ_INIT(&lun->tasks); + TAILQ_INIT(&lun->pending_tasks); + + lun->bdev = bdev; + strncpy(lun->name, name, sizeof(lun->name)); + + rc = spdk_scsi_lun_db_add(lun); + if (rc < 0) { + SPDK_ERRLOG("Unable to add LUN %s to DB\n", lun->name); + free(lun); + return NULL; + } + + return lun; +} + +static int +spdk_scsi_lun_destruct(struct spdk_scsi_lun *lun) +{ + spdk_scsi_lun_db_delete(lun); + + free(lun); + + return 0; +} + +int +spdk_scsi_lun_claim(struct spdk_scsi_lun *lun) +{ + struct spdk_scsi_lun *tmp = spdk_lun_db_get_lun(lun->name, 1); + + if (tmp == NULL) { + return -1; + } + + return 0; +} + +int +spdk_scsi_lun_unclaim(struct spdk_scsi_lun *lun) +{ + spdk_lun_db_put_lun(lun->name); + lun->dev = NULL; + + return 0; +} + +int +spdk_scsi_lun_deletable(const char *name) +{ + int ret = 0; + struct spdk_scsi_lun *lun; + + pthread_mutex_lock(&g_spdk_scsi.mutex); + lun = spdk_lun_db_get_lun(name, 0); + if (lun == NULL) { + ret = -1; + goto out; + } + + if (lun->dev == NULL) { + ret = 0; + goto out; + } + +out: + pthread_mutex_unlock(&g_spdk_scsi.mutex); + return ret; +} + +void +spdk_scsi_lun_delete(const char *lun_name) +{ + struct spdk_scsi_lun *lun; + struct spdk_scsi_dev *dev; + struct spdk_lun_db_entry *current; + + pthread_mutex_lock(&g_spdk_scsi.mutex); + current = spdk_scsi_lun_list_head; + while (current != NULL) { + lun = current->lun; + if (strncmp(lun->name, lun_name, sizeof(lun->name)) == 0) { + break; + } + current = current->next; + } + + if (current == NULL) { + pthread_mutex_unlock(&g_spdk_scsi.mutex); + return; + } + + dev = lun->dev; + + /* Remove the LUN from the device */ + if (dev != NULL) { + spdk_scsi_dev_delete_lun(dev, lun); + } + + /* LUNs are always created in a pair with a blockdev. + * Delete the blockdev associated with this lun. + */ + spdk_bdev_unregister(lun->bdev); + + /* Destroy this lun */ + spdk_scsi_lun_destruct(lun); + pthread_mutex_unlock(&g_spdk_scsi.mutex); +} diff --git a/lib/scsi/lun_db.c b/lib/scsi/lun_db.c new file mode 100644 index 0000000000..9d7d9f3657 --- /dev/null +++ b/lib/scsi/lun_db.c @@ -0,0 +1,120 @@ +/*- + * BSD LICENSE + * + * Copyright (C) 2008-2012 Daisuke Aoyama . + * 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" + +struct spdk_lun_db_entry *spdk_scsi_lun_list_head = NULL; + +int +spdk_scsi_lun_db_add(struct spdk_scsi_lun *lun) +{ + struct spdk_lun_db_entry *new_entry = calloc(1, sizeof(struct spdk_lun_db_entry)); + + if (!new_entry) { + SPDK_ERRLOG("Failed to allocate DB entry\n"); + return -ENOMEM; + } + + new_entry->lun = lun; + new_entry->next = spdk_scsi_lun_list_head; + spdk_scsi_lun_list_head = new_entry; + + return 0; +} + +int +spdk_scsi_lun_db_delete(struct spdk_scsi_lun *lun) +{ + struct spdk_lun_db_entry *prev = NULL; + struct spdk_lun_db_entry *node = spdk_scsi_lun_list_head; + + while (node != NULL) { + if (node->lun == lun) { + if (prev != NULL) { + prev->next = node->next; + } else { + spdk_scsi_lun_list_head = node->next; + } + free(node); + break; + } + prev = node; + node = node->next; + } + + return 0; +} + +struct spdk_scsi_lun * +spdk_lun_db_get_lun(const char *lun_name, int claim_flag) +{ + struct spdk_lun_db_entry *current = spdk_scsi_lun_list_head; + + while (current != NULL) { + struct spdk_scsi_lun *lun = current->lun; + + if (strncmp(lun_name, lun->name, sizeof(lun->name)) == 0) { + if (claim_flag) { + if (current->claimed == 1) + return NULL; + else { + current->claimed = 1; + return lun; + } + } + return lun; + } + + current = current->next; + } + + return NULL; +} + +void +spdk_lun_db_put_lun(const char *lun_name) +{ + struct spdk_lun_db_entry *current = spdk_scsi_lun_list_head; + + while (current != NULL) { + struct spdk_scsi_lun *lun = current->lun; + + if (strncmp(lun_name, lun->name, sizeof(lun->name)) == 0) { + current->claimed = 0; + break; + } + + current = current->next; + } +} diff --git a/lib/scsi/port.c b/lib/scsi/port.c new file mode 100644 index 0000000000..1ede923347 --- /dev/null +++ b/lib/scsi/port.c @@ -0,0 +1,50 @@ +/*- + * BSD LICENSE + * + * Copyright (C) 2008-2012 Daisuke Aoyama . + * 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" + +int +spdk_scsi_port_construct(struct spdk_scsi_port *port, uint64_t id, uint16_t index, + const char *name) +{ + if (strlen(name) > sizeof(port->name)) { + SPDK_ERRLOG("port name too long\n"); + return -1; + } + + port->id = id; + port->index = index; + strncpy(port->name, name, sizeof(port->name)); + return 0; +} diff --git a/lib/scsi/scsi.c b/lib/scsi/scsi.c new file mode 100644 index 0000000000..1add577fa0 --- /dev/null +++ b/lib/scsi/scsi.c @@ -0,0 +1,149 @@ +/*- + * BSD LICENSE + * + * Copyright (C) 2008-2012 Daisuke Aoyama . + * 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" + +#include "spdk/event.h" +#include "spdk/conf.h" + +#define DEFAULT_MAX_UNMAP_LBA_COUNT 4194304 +#define DEFAULT_MAX_UNMAP_BLOCK_DESCRIPTOR_COUNT 1 +#define DEFAULT_OPTIMAL_UNMAP_GRANULARITY 0 +#define DEFAULT_UNMAP_GRANULARITY_ALIGNMENT 0 +#define DEFAULT_UGAVALID 0 +#define DEFAULT_MAX_WRITE_SAME_LENGTH 512 + +struct spdk_scsi_globals g_spdk_scsi; + +static void +spdk_set_default_scsi_parameters(void) +{ + g_spdk_scsi.scsi_params.max_unmap_lba_count = DEFAULT_MAX_UNMAP_LBA_COUNT; + g_spdk_scsi.scsi_params.max_unmap_block_descriptor_count = + DEFAULT_MAX_UNMAP_BLOCK_DESCRIPTOR_COUNT; + g_spdk_scsi.scsi_params.optimal_unmap_granularity = + DEFAULT_OPTIMAL_UNMAP_GRANULARITY; + g_spdk_scsi.scsi_params.unmap_granularity_alignment = + DEFAULT_UNMAP_GRANULARITY_ALIGNMENT; + g_spdk_scsi.scsi_params.ugavalid = DEFAULT_UGAVALID; + g_spdk_scsi.scsi_params.max_write_same_length = DEFAULT_MAX_WRITE_SAME_LENGTH; +} + +static int +spdk_read_config_scsi_parameters(void) +{ + struct spdk_conf_section *sp; + const char *val; + + sp = spdk_conf_find_section(NULL, "Scsi"); + if (sp == NULL) { + spdk_set_default_scsi_parameters(); + return 0; + } + + val = spdk_conf_section_get_val(sp, "MaxUnmapLbaCount"); + g_spdk_scsi.scsi_params.max_unmap_lba_count = (val == NULL) ? + DEFAULT_MAX_UNMAP_LBA_COUNT : strtoul(val, NULL, 10); + + val = spdk_conf_section_get_val(sp, "MaxUnmapBlockDescriptorCount"); + g_spdk_scsi.scsi_params.max_unmap_block_descriptor_count = (val == NULL) ? + DEFAULT_MAX_UNMAP_BLOCK_DESCRIPTOR_COUNT : strtoul(val, NULL, 10); + val = spdk_conf_section_get_val(sp, "OptimalUnmapGranularity"); + g_spdk_scsi.scsi_params.optimal_unmap_granularity = (val == NULL) ? + DEFAULT_OPTIMAL_UNMAP_GRANULARITY : strtoul(val, NULL, 10); + + val = spdk_conf_section_get_val(sp, "UnmapGranularityAlignment"); + g_spdk_scsi.scsi_params.unmap_granularity_alignment = (val == NULL) ? + DEFAULT_UNMAP_GRANULARITY_ALIGNMENT : strtoul(val, NULL, 10); + + val = spdk_conf_section_get_val(sp, "Ugavalid"); + if (val == NULL) { + g_spdk_scsi.scsi_params.ugavalid = DEFAULT_UGAVALID; + } else if (strcasecmp(val, "Yes") == 0) { + g_spdk_scsi.scsi_params.ugavalid = 1; + } else if (strcasecmp(val, "No") == 0) { + g_spdk_scsi.scsi_params.ugavalid = 0; + } else { + SPDK_ERRLOG("unknown value %s\n", val); + return -1; + } + + val = spdk_conf_section_get_val(sp, "MaxWriteSameLength"); + g_spdk_scsi.scsi_params.max_write_same_length = (val == NULL) ? + DEFAULT_MAX_WRITE_SAME_LENGTH : strtoul(val, NULL, 10); + + return 0; +} + +static int +spdk_scsi_subsystem_init(void) +{ + int rc; + + rc = pthread_mutex_init(&g_spdk_scsi.mutex, NULL); + if (rc != 0) { + SPDK_ERRLOG("mutex_init() failed\n"); + return -1; + } + + rc = spdk_read_config_scsi_parameters(); + if (rc < 0) { + SPDK_ERRLOG("spdk_scsi_parameters() failed\n"); + return -1; + } + + return rc; +} + +static int +spdk_scsi_subsystem_fini(void) +{ + pthread_mutex_destroy(&g_spdk_scsi.mutex); + return 0; +} + +SPDK_TRACE_REGISTER_FN(scsi_trace) +{ + spdk_trace_register_owner(OWNER_SCSI_DEV, 'd'); + spdk_trace_register_object(OBJECT_SCSI_TASK, 't'); + spdk_trace_register_description("SCSI TASK DONE", "", TRACE_SCSI_TASK_DONE, + OWNER_SCSI_DEV, OBJECT_SCSI_TASK, 0, 0, 0, ""); + spdk_trace_register_description("SCSI TASK START", "", TRACE_SCSI_TASK_START, + OWNER_SCSI_DEV, OBJECT_SCSI_TASK, 0, 0, 0, ""); +} + +SPDK_SUBSYSTEM_REGISTER(scsi, spdk_scsi_subsystem_init, spdk_scsi_subsystem_fini, NULL) +SPDK_SUBSYSTEM_DEPEND(scsi, bdev) + +SPDK_LOG_REGISTER_TRACE_FLAG("scsi", SPDK_TRACE_SCSI) diff --git a/lib/scsi/scsi_bdev.c b/lib/scsi/scsi_bdev.c new file mode 100644 index 0000000000..ad58a758e2 --- /dev/null +++ b/lib/scsi/scsi_bdev.c @@ -0,0 +1,1851 @@ +/*- + * BSD LICENSE + * + * Copyright (C) 2008-2012 Daisuke Aoyama . + * 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" + +#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 7200 /* 7200 rpm */ +#define DEFAULT_DISK_FORM_FACTOR 0x02 /* 3.5 inch */ + +static void +spdk_strcpy_pad(uint8_t *dst, size_t size, const char *src, int pad) +{ + size_t len; + + len = strlen(src); + if (len < size) { + memcpy(dst, src, len); + memset(dst + len, pad, (size - len)); + } else { + memcpy(dst, src, size); + } +} + +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(char *name, uint8_t *buf) +{ + int i, value, count = 0; + uint64_t naa, local_value; + for (i = 0; (name[i] != '\0') && (i < 16); 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; + 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_check_condition(task, + SPDK_SCSI_SENSE_ILLEGAL_REQUEST, + 0x24, 0x0); + 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 (bdev->thin_provisioning) { + /* SBC Thin Provisioning */ + vpage->params[9] = 0xb2; + len++; + } + + /* PAGE LENGTH */ + to_be16(vpage->alloc_len, len); + break; + + case SPDK_SPC_VPD_UNIT_SERIAL_NUMBER: + hlen = 4; + + /* PRODUCT SERIAL NUMBER */ + len = strlen(bdev->name); + if (len > MAX_SERIAL_STRING) { + len = MAX_SERIAL_STRING; + } + + spdk_strcpy_pad(vpage->params, len, bdev->name, ' '); + + /* PAGE LENGTH */ + to_be16(vpage->alloc_len, len); + break; + + case SPDK_SPC_VPD_DEVICE_IDENTIFICATION: { + 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 > task->alloc_len) { + spdk_scsi_task_set_check_condition(task, + SPDK_SCSI_SENSE_ILLEGAL_REQUEST, + 0x24, 0x0); + 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(bdev->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, 8, DEFAULT_DISK_VENDOR, ' '); + spdk_strcpy_pad(&desig->desig[8], 16, bdev->product_name, ' '); + spdk_strcpy_pad(&desig->desig[24], MAX_SERIAL_STRING, + bdev->name, ' '); + 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; + + /* should not exceed the data_in buffer length */ + if (sizeof(struct spdk_scsi_vpd_page) + len > alloc_len) { + spdk_scsi_task_set_check_condition(task, + SPDK_SCSI_SENSE_ILLEGAL_REQUEST, + 0x24, 0x0); + return -1; + } + 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 */ + sdesc->rel_port_id = htobe16(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 */ + sdesc->tgt_desc_len = htobe16(plen2); + + len += plen2; + } + + to_be16(vpage->alloc_len, len); + break; + } + + case SPDK_SPC_VPD_BLOCK_LIMITS: { + /* 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 / + (uint32_t)bdev->blocklen; + + if (blocks > 0xff) + blocks = 0xff; + + data[5] = (uint8_t)blocks; + + /* force align to 4KB */ + if (bdev->blocklen < 4096) { + blocks = 4096 / (uint32_t)bdev->blocklen; + /* OPTIMAL TRANSFER LENGTH GRANULARITY */ + to_be16(&data[6], blocks); + + /* MAXIMUM TRANSFER LENGTH */ + + /* OPTIMAL TRANSFER LENGTH */ + blocks = SPDK_WORK_BLOCK_SIZE / + (uint32_t)bdev->blocklen; + + to_be32(&data[12], blocks); + + /* MAXIMUM PREFETCH XDREAD XDWRITE TRANSFER LENGTH */ + } else { + blocks = 1; + + /* OPTIMAL TRANSFER LENGTH GRANULARITY */ + to_be16(&data[6], blocks); + + /* MAXIMUM TRANSFER LENGTH */ + + /* OPTIMAL TRANSFER LENGTH */ + blocks = SPDK_WORK_BLOCK_SIZE / + (uint32_t)bdev->blocklen; + to_be32(&data[12], blocks); + + /* MAXIMUM PREFETCH XDREAD XDWRITE TRANSFER LENGTH */ + } + + len = 20 - hlen; + + if (bdev->thin_provisioning) { + /* + * 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. + */ + if (bdev->max_unmap_bdesc_count < + g_spdk_scsi.scsi_params.max_unmap_block_descriptor_count) + to_be32(&data[24], bdev->max_unmap_bdesc_count); + else + to_be32(&data[24], g_spdk_scsi.scsi_params.max_unmap_block_descriptor_count); + + /* + * 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; + + 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, 64 - hlen); + break; + } + + case SPDK_SPC_VPD_BLOCK_THIN_PROVISION: { + if (!bdev->thin_provisioning) { + SPDK_ERRLOG("unsupported INQUIRY VPD page 0x%x\n", pc); + goto inq_error; + } + + hlen = 4; + + /* PAGE CODE */ + data[1] = SPDK_SPC_VPD_BLOCK_THIN_PROVISION; + + /* + * 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, 7); + 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, 8, DEFAULT_DISK_VENDOR, ' '); + + /* PRODUCT IDENTIFICATION */ + spdk_strcpy_pad(inqdata->product_id, 16, + bdev->product_name, ' '); + + /* PRODUCT REVISION LEVEL */ + spdk_strcpy_pad(inqdata->product_rev, 4, DEFAULT_DISK_REVISION, ' '); + + /* Vendor specific */ + memset(inqdata->vendor, 0x20, 20); + + /* CLOCKING(3-2) QAS(1) IUS(0) */ + inqdata->ius = 0; + + /* Reserved */ + inqdata->reserved = 0; + + /* VERSION DESCRIPTOR 1-8 */ + to_be16(inqdata->desc, 0x0960); + to_be16(&inqdata->desc[2], 0x0300); /* SPC-3 (no version claimed) */ + to_be16(&inqdata->desc[4], 0x320); /* SBC-2 (no version claimed) */ + to_be16(&inqdata->desc[6], 0x0040); /* SAM-2 (no version claimed) */ + /* 96 - 74 + 8 */ + /* Reserved[74-95] */ + memset(&inqdata->desc[8], 0, 30); + + len = alloc_len - hlen; + + /* ADDITIONAL LENGTH */ + inqdata->add_len = len; + } + + return hlen + len; + +inq_error: + spdk_scsi_task_set_check_condition(task, + SPDK_SCSI_SENSE_NO_SENSE, + 0x0, 0x0); + return -1; +} + +static void +mode_sense_page_init(uint8_t *buf, int len, int page, int subpage) +{ + 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, int alloc_len) +{ + uint8_t *cp; + int len = 0; + int plen; + int i; + + if (pc == 0x00) { + /* Current values */ + } else if (pc == 0x01) { + /* Changeable values not supported */ + return -1; + } else if (pc == 0x02) { + /* Default values */ + } else { + /* Saved values not supported */ + return -1; + } + + cp = &data[len]; + 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 (bdev->write_cache) + cp[2] |= 0x4; /* WCE */ + // TODO: + //fd = bdev->fd; + //rc = fcntl(fd , F_GETFL, 0); + //if (rc != -1 && !(rc & O_FSYNC)) + // cp[2] |= 0x4; /* WCE=1 */ + //else + // cp[2] &= 0xfb; /* WCE = 0 */ + + /* Read Cache Disable (RCD) = 1 */ + 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, + &data[len], + alloc_len); + len += spdk_bdev_scsi_mode_sense_page(bdev, + cdb, pc, page, + 0x01, + &data[len], + alloc_len); + 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[len], alloc_len); + } + 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[len], alloc_len); + } + for (i = 0x00; i < 0x3e; i ++) { + len += spdk_bdev_scsi_mode_sense_page( + bdev, cdb, pc, i, 0xff, + &cp[len], alloc_len); + } + break; + default: + /* 0x01-0x3e: Reserved */ + break; + } + } + + return len; +} + +static int +spdk_bdev_scsi_mode_sense6(struct spdk_bdev *bdev, + uint8_t *cdb, int dbd, int pc, int page, + int subpage, uint8_t *data, int alloc_len) +{ + uint8_t *cp; + int hlen, len = 0, plen; + int total; + int llbaa = 0; + + data[0] = 0; /* Mode Data Length */ + data[1] = 0; /* Medium Type */ + data[2] = 0; /* Device-Specific Parameter */ + data[3] = 0; /* Block Descripter Length */ + hlen = 4; + + cp = &data[4]; + if (dbd) { /* Disable Block Descripters */ + len = 0; + } else { + if (llbaa) { + /* Number of Blocks */ + to_be64(cp, bdev->blockcnt); + /* Reserved */ + memset(&cp[8], 0, 4); + /* Block Length */ + to_be32(&cp[12], bdev->blocklen); + len = 16; + } else { + /* Number of Blocks */ + if (bdev->blockcnt > 0xffffffffULL) + memset(cp, 0xff, 4); + else + to_be32(cp, bdev->blockcnt); + + /* Block Length */ + to_be32(&cp[4], bdev->blocklen); + len = 8; + } + + cp += len; + } + + data[3] = len; /* Block Descripter Length */ + + plen = spdk_bdev_scsi_mode_sense_page(bdev, cdb, pc, page, + subpage, &cp[0], alloc_len); + if (plen < 0) { + return -1; + } + + total = hlen + len + plen; + data[0] = total - 1; /* Mode Data Length */ + + return total; +} + +static int +spdk_bdev_scsi_mode_sense10(struct spdk_bdev *bdev, + uint8_t *cdb, int dbd, int llbaa, int pc, + int page, int subpage, uint8_t *data, + int alloc_len) +{ + uint8_t *cp; + int hlen, len = 0, plen; + int total; + + /* Mode Data Length */ + /* Medium Type */ + /* Device-Specific Parameter */ + memset(data, 0, 4); + + if (llbaa) { + data[4] = 0x1; /* Long LBA */ + } else { + data[4] = 0; /* Short LBA */ + } + + /* Reserved */ + /* Block Descripter Length */ + memset(&data[5], 0, 3); + hlen = 8; + + cp = &data[8]; + if (dbd) { /* Disable Block Descripters */ + len = 0; + } else { + if (llbaa) { + /* Number of Blocks */ + to_be64(cp, bdev->blockcnt); + /* Reserved */ + memset(&cp[8], 0, 4); + /* Block Length */ + to_be32(&cp[12], bdev->blocklen); + len = 16; + } else { + /* Number of Blocks */ + if (bdev->blockcnt > 0xffffffffULL) + memset(cp, 0xff, 4); + else + to_be32(cp, bdev->blockcnt); + + /* Block Length */ + to_be32(&cp[4], bdev->blocklen); + len = 8; + } + cp += len; + } + + to_be16(&data[6], len); /* Block Descripter Length */ + + plen = spdk_bdev_scsi_mode_sense_page(bdev, cdb, pc, page, + subpage, &cp[0], alloc_len); + if (plen < 0) + return -1; + + total = hlen + len + plen; + to_be16(data, total - 2); /* Mode Data Length */ + + 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(spdk_event_t event) +{ + struct spdk_bdev_io *bdev_io = spdk_event_get_arg2(event); + struct spdk_scsi_task *task = spdk_event_get_arg1(event); + enum spdk_bdev_io_status status = bdev_io->status; + + if (task->type == SPDK_SCSI_TASK_TYPE_CMD) { + if (status != SPDK_BDEV_IO_STATUS_SUCCESS) { + spdk_scsi_task_set_check_condition(task, + SPDK_SCSI_SENSE_ABORTED_COMMAND, 0, 0); + } + + /* If status was not set to CHECK_CONDITION yet, then we can set + * status to GOOD. + */ + if (task->status != SPDK_SCSI_STATUS_CHECK_CONDITION) { + task->status = SPDK_SCSI_STATUS_GOOD; + } + + /* command completed. remove from outstanding task list */ + TAILQ_REMOVE(&task->lun->tasks, task, scsi_link); + } else if (task->type == SPDK_SCSI_TASK_TYPE_MANAGE) { + if (status == SPDK_BDEV_IO_STATUS_SUCCESS) + task->response = SPDK_SCSI_TASK_MGMT_RESP_SUCCESS; + if (task->function == SPDK_SCSI_TASK_FUNC_LUN_RESET) { + spdk_scsi_lun_clear_all(task->lun); + } + } + + if (bdev_io->type == SPDK_BDEV_IO_TYPE_READ) { + task->rbuf = bdev_io->u.read.buf; + } + + spdk_scsi_lun_complete_task(task->lun, task); +} + +static int +spdk_bdev_scsi_read(struct spdk_bdev *bdev, + struct spdk_scsi_task *task, uint64_t lba, + uint32_t len) +{ + uint64_t maxlba; + uint64_t llen; + uint64_t blen; + off_t offset; + uint64_t nbytes; + + maxlba = bdev->blockcnt; + blen = bdev->blocklen; + lba += (task->offset / blen); + offset = lba * blen; + nbytes = task->length; + llen = task->length / blen; + + SPDK_TRACELOG(SPDK_TRACE_SCSI, + "Read: max=%"PRIu64", lba=%"PRIu64", len=%"PRIu64"\n", + maxlba, lba, llen); + + if (lba >= maxlba || llen > maxlba || lba > (maxlba - llen)) { + SPDK_ERRLOG("end of media\n"); + return -1; + } + + task->blockdev_io = spdk_bdev_read(bdev, task->rbuf, nbytes, + offset, spdk_bdev_scsi_task_complete, + task); + if (!task->blockdev_io) { + SPDK_ERRLOG("spdk_bdev_read() failed\n"); + return -1; + } + + task->data_transferred = nbytes; + + return 0; +} + +static int +spdk_bdev_scsi_write(struct spdk_bdev *bdev, + struct spdk_scsi_task *task, uint64_t lba, uint32_t len) +{ + uint64_t maxlba; + uint64_t llen; + uint64_t blen; + off_t offset; + uint64_t nbytes; + struct spdk_scsi_task *primary = task->parent; + + if (len == 0) { + task->data_transferred = 0; + return -1; + } + + maxlba = bdev->blockcnt; + llen = (uint64_t) len; + blen = bdev->blocklen; + offset = lba * blen; + nbytes = llen * blen; + + SPDK_TRACELOG(SPDK_TRACE_SCSI, + "Write: max=%"PRIu64", lba=%"PRIu64", len=%u\n", + maxlba, lba, len); + + if (lba >= maxlba || llen > maxlba || lba > (maxlba - llen)) { + SPDK_ERRLOG("end of media\n"); + return -1; + } + + if (nbytes > task->transfer_len) { + SPDK_ERRLOG("nbytes(%zu) > transfer_len(%u)\n", + (size_t)nbytes, task->transfer_len); + return -1; + } + + offset += task->offset; + task->blockdev_io = spdk_bdev_writev(bdev, &task->iov, + 1, task->length, offset, + spdk_bdev_scsi_task_complete, + task); + + if (!task->blockdev_io) { + SPDK_ERRLOG("spdk_bdev_writev failed\n"); + return -1; + } 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); + return 0; +} + +static int +spdk_bdev_scsi_sync(struct spdk_bdev *bdev, struct spdk_scsi_task *task, + uint64_t lba, uint32_t len) +{ + uint64_t maxlba; + uint64_t llen; + uint64_t blen; + uint64_t offset; + uint64_t nbytes; + + if (len == 0) { + return SPDK_SCSI_TASK_COMPLETE; + } + + maxlba = bdev->blockcnt; + llen = len; + blen = bdev->blocklen; + offset = lba * blen; + nbytes = llen * blen; + + if (lba >= maxlba || llen > maxlba || lba > (maxlba - llen)) { + SPDK_ERRLOG("end of media\n"); + spdk_scsi_task_set_check_condition(task, SPDK_SCSI_SENSE_NO_SENSE, 0x0, 0x0); + return SPDK_SCSI_TASK_COMPLETE; + } + + task->blockdev_io = spdk_bdev_flush(bdev, offset, nbytes, + spdk_bdev_scsi_task_complete, task); + + if (!task->blockdev_io) { + SPDK_ERRLOG("spdk_bdev_flush() failed\n"); + spdk_scsi_task_set_check_condition(task, SPDK_SCSI_SENSE_NO_SENSE, 0x0, 0x0); + 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) +{ + int rc; + + if (task->dxfer_dir == SPDK_SCSI_DIR_FROM_DEV) { + rc = spdk_bdev_scsi_read(bdev, task, lba, xfer_len); + } else if (task->dxfer_dir == SPDK_SCSI_DIR_TO_DEV) { + rc = spdk_bdev_scsi_write(bdev, task, lba, xfer_len); + } else { + SPDK_ERRLOG("Incorrect data direction\n"); + spdk_scsi_task_set_check_condition(task, SPDK_SCSI_SENSE_NO_SENSE, 0x0, 0x0); + return SPDK_SCSI_TASK_COMPLETE; + } + + if (rc < 0) { + SPDK_ERRLOG("disk op (rw) failed\n"); + spdk_scsi_task_set_check_condition(task, SPDK_SCSI_SENSE_NO_SENSE, 0x0, 0x0); + + return SPDK_SCSI_TASK_COMPLETE; + } else { + task->status = SPDK_SCSI_STATUS_GOOD; + } + + return SPDK_SCSI_TASK_PENDING; +} + +static int +spdk_bdev_scsi_unmap(struct spdk_bdev *bdev, + struct spdk_scsi_task *task) +{ + + uint8_t *data; + uint16_t bdesc_data_len, bdesc_count; + + data = (uint8_t *)task->iov.iov_base; + + /* + * 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; + + if (bdesc_count > bdev->max_unmap_bdesc_count) { + SPDK_ERRLOG("Error - supported unmap block descriptor count limit" + " is %u\n", bdev->max_unmap_bdesc_count); + spdk_scsi_task_set_check_condition(task, + SPDK_SCSI_SENSE_NO_SENSE, + 0x0, 0x0); + return SPDK_SCSI_TASK_COMPLETE; + } + + task->blockdev_io = spdk_bdev_unmap(bdev, (struct spdk_scsi_unmap_bdesc *)&data[8], + bdesc_count, spdk_bdev_scsi_task_complete, + task); + + if (!task->blockdev_io) { + SPDK_ERRLOG("SCSI Unmapping failed\n"); + spdk_scsi_task_set_check_condition(task, + SPDK_SCSI_SENSE_NO_SENSE, + 0x0, 0x0); + 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; + uint8_t *data; + + /* XXX: We need to support FUA bit for writes! */ + switch (cdb[0]) { + case SPDK_SBC_READ_6: + case SPDK_SBC_WRITE_6: + lba = (uint32_t)cdb[1] << 16; + lba |= (uint32_t)cdb[2] << 8; + lba |= (uint32_t)cdb[3]; + xfer_len = cdb[4]; + if (xfer_len == 0) { + xfer_len = 256; + } + return spdk_bdev_scsi_readwrite(bdev, task, lba, xfer_len); + + 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); + + 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); + + 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); + + case SPDK_SBC_READ_CAPACITY_10: + spdk_scsi_task_alloc_data(task, 8, &data); + if (bdev->blockcnt - 1 > 0xffffffffULL) { + memset(data, 0xff, 4); + } else { + to_be32(data, bdev->blockcnt - 1); + } + to_be32(&data[4], bdev->blocklen); + task->data_transferred = 8; + 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: + spdk_scsi_task_alloc_data(task, 32, &data); + to_be64(&data[0], bdev->blockcnt - 1); + to_be32(&data[8], (uint32_t)bdev->blocklen); + /* + * 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 (bdev->thin_provisioning) { + data[14] |= 1 << 7; + } + task->data_transferred = 32; + 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 = bdev->blockcnt - 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_process_primary(struct spdk_bdev *bdev, + struct spdk_scsi_task *task) +{ + uint32_t alloc_len; + int data_len; + uint8_t *cdb = task->cdb; + uint8_t *data; + 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]); + spdk_scsi_task_alloc_data(task, alloc_len, &data); + data_len = spdk_bdev_scsi_inquiry(bdev, task, cdb, + data, alloc_len); + if (data_len < 0) { + break; + } + + SPDK_TRACEDUMP(SPDK_TRACE_DEBUG, "INQUIRY", data, data_len); + task->data_transferred = (uint64_t)data_len; + task->status = SPDK_SCSI_STATUS_GOOD; + 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]); + if (alloc_len < 16) { + /* INVALID FIELD IN CDB */ + spdk_scsi_task_set_check_condition(task, + SPDK_SCSI_SENSE_ILLEGAL_REQUEST, 0x24, 0x00); + break; + } + + spdk_scsi_task_alloc_data(task, alloc_len, &data); + data_len = spdk_bdev_scsi_report_luns(task->lun, sel, data, task->alloc_len); + if (data_len < 0) { + spdk_scsi_task_set_check_condition(task, SPDK_SCSI_SENSE_NO_SENSE, 0x0, 0x0); + break; + } + + SPDK_TRACEDUMP(SPDK_TRACE_DEBUG, "REPORT LUNS", data, data_len); + task->data_transferred = (uint64_t)data_len; + task->status = SPDK_SCSI_STATUS_GOOD; + break; + } + + case SPDK_SPC_MODE_SELECT_6: + case SPDK_SPC_MODE_SELECT_10: + data = task->iobuf; + + if (cdb[0] == SPDK_SPC_MODE_SELECT_6) { + md = 4; + pllen = cdb[4]; + } else { + md = 8; + pllen = from_be16(&cdb[7]); + } + + if (pllen == 0) { + task->data_transferred = 0; + task->status = SPDK_SCSI_STATUS_GOOD; + break; + } else if (cdb[0] == SPDK_SPC_MODE_SELECT_6 && pllen < 4) { + /* MODE_SELECT(6) must have at least a 4 byte header. */ + /* INVALID FIELD IN CDB */ + spdk_scsi_task_set_check_condition(task, + SPDK_SCSI_SENSE_ILLEGAL_REQUEST, 0x24, 0x00); + break; + } else if (cdb[0] == SPDK_SPC_MODE_SELECT_10 && pllen < 8) { + /* MODE_SELECT(10) must have at least an 8 byte header. */ + /* INVALID FIELD IN CDB */ + spdk_scsi_task_set_check_condition(task, + SPDK_SCSI_SENSE_ILLEGAL_REQUEST, 0x24, 0x00); + break; + } + + if (cdb[0] == SPDK_SPC_MODE_SELECT_6) { + bdlen = data[3]; + } else { + bdlen = from_be16(&data[6]); + } + + pf = !!(cdb[1] & 0x10); + sp = !!(cdb[1] & 0x1); + + /* page data */ + data_len = spdk_bdev_scsi_mode_select_page( + bdev, cdb, + pf, sp, + &data[md + bdlen], + pllen - (md + bdlen)); + if (data_len != 0) { + spdk_scsi_task_set_check_condition(task, + SPDK_SCSI_SENSE_NO_SENSE, + 0x0, 0x0); + break; + } + + task->data_transferred = pllen; + task->status = SPDK_SCSI_STATUS_GOOD; + 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] & 0xc) >> 6; + page = cdb[2] & 0x3f; + subpage = cdb[3]; + + spdk_scsi_task_alloc_data(task, alloc_len, &data); + + if (md == 6) { + data_len = spdk_bdev_scsi_mode_sense6(bdev, + cdb, dbd, pc, + page, subpage, + data, + alloc_len); + } else { + data_len = spdk_bdev_scsi_mode_sense10(bdev, + cdb, dbd, + llba, pc, + page, + subpage, + data, + alloc_len); + } + + if (data_len < 0) { + /* INVALID FIELD IN CDB */ + spdk_scsi_task_set_check_condition(task, + SPDK_SCSI_SENSE_ILLEGAL_REQUEST, 0x24, 0x00); + break; + } + + task->data_transferred = (uint64_t)data_len; + task->status = SPDK_SCSI_STATUS_GOOD; + 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_check_condition(task, + SPDK_SCSI_SENSE_ILLEGAL_REQUEST, 0x24, 0x00); + break; + } + + alloc_len = cdb[4]; + spdk_scsi_task_alloc_data(task, alloc_len, &data); + + /* NO ADDITIONAL SENSE INFORMATION */ + sk = SPDK_SCSI_SENSE_NO_SENSE; + asc = 0x00; + ascq = 0x00; + + data_len = spdk_scsi_task_build_sense_data(task, sk, asc, ascq); + + /* omit SenseLength */ + data_len -= 2; + memcpy(data, &task->sense_data[2], data_len); + task->data_transferred = (uint64_t)data_len; + task->status = SPDK_SCSI_STATUS_GOOD; + 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_check_condition(task, + SPDK_SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); + 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"); + } + + task->data_transferred = 0; + task->status = SPDK_SCSI_STATUS_GOOD; + break; + + default: + return SPDK_SCSI_TASK_UNKNOWN; + } + + 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_check_condition(task, + SPDK_SCSI_SENSE_ILLEGAL_REQUEST, + 0x20, 0x00); + 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, SPDK_BDEV_RESET_HARD, + spdk_bdev_scsi_task_complete, task); +} diff --git a/lib/scsi/scsi_internal.h b/lib/scsi/scsi_internal.h new file mode 100644 index 0000000000..bc0ef88671 --- /dev/null +++ b/lib/scsi/scsi_internal.h @@ -0,0 +1,230 @@ +/*- + * BSD LICENSE + * + * Copyright (C) 2008-2012 Daisuke Aoyama . + * 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. + */ + +#ifndef SPDK_SCSI_INTERNAL_H +#define SPDK_SCSI_INTERNAL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "spdk/bdev.h" +#include "spdk/bdev_db.h" +#include "spdk/log.h" +#include "spdk/scsi.h" +#include "spdk/scsi_spec.h" +#include "spdk/trace.h" + +enum { + SPDK_SCSI_TASK_UNKNOWN = -1, + SPDK_SCSI_TASK_COMPLETE, + SPDK_SCSI_TASK_PENDING, +}; + +/* + * SAM does not define the value for these service responses. Each transport + * (i.e. SAS, FC, iSCSI) will map these value to transport-specific codes, + * and may add their own. + */ +enum spdk_scsi_task_mgmt_resp { + SPDK_SCSI_TASK_MGMT_RESP_COMPLETE, + SPDK_SCSI_TASK_MGMT_RESP_SUCCESS, + SPDK_SCSI_TASK_MGMT_RESP_REJECT, + SPDK_SCSI_TASK_MGMT_RESP_INVALID_LUN, + SPDK_SCSI_TASK_MGMT_RESP_TARGET_FAILURE, + SPDK_SCSI_TASK_MGMT_RESP_REJECT_FUNC_NOT_SUPPORTED +}; + +#define OWNER_SCSI_DEV 0x10 + +#define OBJECT_SCSI_TASK 0x10 + +#define TRACE_GROUP_SCSI 0x2 +#define TRACE_SCSI_TASK_DONE SPDK_TPOINT_ID(TRACE_GROUP_SCSI, 0x0) +#define TRACE_SCSI_TASK_START SPDK_TPOINT_ID(TRACE_GROUP_SCSI, 0x1) + +/** + +\brief Represents a SCSI LUN. + +LUN modules will implement the function pointers specifically for the LUN +type. For example, NVMe LUNs will implement scsi_execute to translate +the SCSI task to an NVMe command and post it to the NVMe controller. +malloc LUNs will implement scsi_execute to translate the SCSI task and +copy the task's data into or out of the allocated memory buffer. + +*/ +struct spdk_scsi_lun { + /** LUN id for this logical unit. */ + int id; + + /** Pointer to the SCSI device containing this LUN. */ + struct spdk_scsi_dev *dev; + + /** The blockdev associated with this LUN. */ + struct spdk_bdev *bdev; + + /** Name for this LUN. */ + char name[SPDK_SCSI_LUN_MAX_NAME_LENGTH]; + + TAILQ_HEAD(tasks, spdk_scsi_task) tasks; /* submitted tasks */ + TAILQ_HEAD(pending_tasks, spdk_scsi_task) pending_tasks; /* pending tasks */ +}; + +struct spdk_lun_db_entry { + struct spdk_scsi_lun *lun; + int claimed; + struct spdk_lun_db_entry *next; +}; + +extern struct spdk_lun_db_entry *spdk_scsi_lun_list_head; + +/* This typedef exists to work around an astyle 2.05 bug. + * Remove it when astyle is fixed. + */ +typedef struct spdk_scsi_lun _spdk_scsi_lun; + +_spdk_scsi_lun *spdk_scsi_lun_construct(const char *name, struct spdk_bdev *bdev); + +void spdk_scsi_lun_clear_all(struct spdk_scsi_lun *lun); +void spdk_scsi_lun_append_task(struct spdk_scsi_lun *lun, struct spdk_scsi_task *task); +void spdk_scsi_lun_execute_tasks(struct spdk_scsi_lun *lun); +int spdk_scsi_lun_task_mgmt_execute(struct spdk_scsi_task *task); +void spdk_scsi_lun_complete_task(struct spdk_scsi_lun *lun, struct spdk_scsi_task *task); +int spdk_scsi_lun_claim(struct spdk_scsi_lun *lun); +int spdk_scsi_lun_unclaim(struct spdk_scsi_lun *lun); +int spdk_scsi_lun_deletable(const char *name); +void spdk_scsi_lun_delete(const char *lun_name); + +int spdk_scsi_lun_db_add(struct spdk_scsi_lun *lun); +int spdk_scsi_lun_db_delete(struct spdk_scsi_lun *lun); + +struct spdk_scsi_lun *spdk_lun_db_get_lun(const char *lun_name, int claim_flag); +void spdk_lun_db_put_lun(const char *lun_name); + +struct spdk_scsi_dev *spdk_scsi_dev_get_list(void); + +int spdk_bdev_scsi_execute(struct spdk_bdev *bdev, struct spdk_scsi_task *task); +int spdk_bdev_scsi_reset(struct spdk_bdev *bdev, struct spdk_scsi_task *task); + +static inline uint16_t +from_be16(void *ptr) +{ + uint8_t *tmp = (uint8_t *)ptr; + return (((uint16_t)tmp[0] << 8) | tmp[1]); +} + +static inline void +to_be16(void *out, uint16_t in) +{ + uint8_t *tmp = (uint8_t *)out; + tmp[0] = (in >> 8) & 0xFF; + tmp[1] = in & 0xFF; +} + +static inline uint32_t +from_be32(void *ptr) +{ + uint8_t *tmp = (uint8_t *)ptr; + return (((uint32_t)tmp[0] << 24) | + ((uint32_t)tmp[1] << 16) | + ((uint32_t)tmp[2] << 8) | + ((uint32_t)tmp[3])); +} + +static inline void +to_be32(void *out, uint32_t in) +{ + uint8_t *tmp = (uint8_t *)out; + tmp[0] = (in >> 24) & 0xFF; + tmp[1] = (in >> 16) & 0xFF; + tmp[2] = (in >> 8) & 0xFF; + tmp[3] = in & 0xFF; +} + +static inline uint64_t +from_be64(void *ptr) +{ + uint8_t *tmp = (uint8_t *)ptr; + return (((uint64_t)tmp[0] << 56) | + ((uint64_t)tmp[1] << 48) | + ((uint64_t)tmp[2] << 40) | + ((uint64_t)tmp[3] << 32) | + ((uint64_t)tmp[4] << 24) | + ((uint64_t)tmp[5] << 16) | + ((uint64_t)tmp[6] << 8) | + ((uint64_t)tmp[7])); +} + +static inline void +to_be64(void *out, uint64_t in) +{ + uint8_t *tmp = (uint8_t *)out; + tmp[0] = (in >> 56) & 0xFF; + tmp[1] = (in >> 48) & 0xFF; + tmp[2] = (in >> 40) & 0xFF; + tmp[3] = (in >> 32) & 0xFF; + tmp[4] = (in >> 24) & 0xFF; + tmp[5] = (in >> 16) & 0xFF; + tmp[6] = (in >> 8) & 0xFF; + tmp[7] = in & 0xFF; +}; + +struct spdk_scsi_parameters { + uint32_t max_unmap_lba_count; + uint32_t max_unmap_block_descriptor_count; + uint32_t optimal_unmap_granularity; + uint32_t unmap_granularity_alignment; + uint32_t ugavalid; + uint64_t max_write_same_length; +}; + +struct spdk_scsi_globals { + pthread_mutex_t mutex; + struct spdk_scsi_parameters scsi_params; +}; + +extern struct spdk_scsi_globals g_spdk_scsi; + +#endif /* SPDK_SCSI_INTERNAL_H */ diff --git a/lib/scsi/scsi_rpc.c b/lib/scsi/scsi_rpc.c new file mode 100644 index 0000000000..0af4083a70 --- /dev/null +++ b/lib/scsi/scsi_rpc.c @@ -0,0 +1,174 @@ +/*- + * BSD LICENSE + * + * Copyright (C) 2008-2012 Daisuke Aoyama . + * 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" + +#include "spdk/rpc.h" + +static void +spdk_rpc_get_luns(struct spdk_jsonrpc_server_conn *conn, + const struct spdk_json_val *params, + const struct spdk_json_val *id) +{ + struct spdk_json_write_ctx *w; + struct spdk_lun_db_entry *current; + + if (params != NULL) { + spdk_jsonrpc_send_error_response(conn, id, SPDK_JSONRPC_ERROR_INVALID_PARAMS, + "get_luns requires no parameters"); + return; + } + + if (id == NULL) { + return; + } + + w = spdk_jsonrpc_begin_result(conn, id); + spdk_json_write_array_begin(w); + + current = spdk_scsi_lun_list_head; + while (current != NULL) { + struct spdk_scsi_lun *lun = current->lun; + + spdk_json_write_object_begin(w); + spdk_json_write_name(w, "claimed"); + spdk_json_write_bool(w, current->claimed); + spdk_json_write_name(w, "name"); + spdk_json_write_string(w, lun->name); + spdk_json_write_object_end(w); + + current = current->next; + } + + spdk_json_write_array_end(w); + + spdk_jsonrpc_end_result(conn, w); +} +SPDK_RPC_REGISTER("get_luns", spdk_rpc_get_luns) + +struct rpc_delete_lun { + char *name; +}; + +static void +free_rpc_delete_lun(struct rpc_delete_lun *r) +{ + free(r->name); +} + +static const struct spdk_json_object_decoder rpc_delete_lun_decoders[] = { + {"name", offsetof(struct rpc_delete_lun, name), spdk_json_decode_string}, +}; + +static void +spdk_rpc_delete_lun(struct spdk_jsonrpc_server_conn *conn, + const struct spdk_json_val *params, + const struct spdk_json_val *id) +{ + struct rpc_delete_lun req = {}; + struct spdk_json_write_ctx *w; + + if (spdk_json_decode_object(params, rpc_delete_lun_decoders, + sizeof(rpc_delete_lun_decoders) / sizeof(*rpc_delete_lun_decoders), + &req)) { + SPDK_TRACELOG(SPDK_TRACE_DEBUG, "spdk_json_decode_object failed\n"); + goto invalid; + } + + if (spdk_scsi_lun_deletable(req.name)) { + goto invalid; + } + + spdk_scsi_lun_delete(req.name); + free_rpc_delete_lun(&req); + + if (id == NULL) { + return; + } + + w = spdk_jsonrpc_begin_result(conn, id); + spdk_json_write_bool(w, true); + spdk_jsonrpc_end_result(conn, w); + return; + +invalid: + spdk_jsonrpc_send_error_response(conn, id, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters"); + free_rpc_delete_lun(&req); +} +SPDK_RPC_REGISTER("delete_lun", spdk_rpc_delete_lun) + +static void +spdk_rpc_get_scsi_devices(struct spdk_jsonrpc_server_conn *conn, + const struct spdk_json_val *params, + const struct spdk_json_val *id) +{ + struct spdk_json_write_ctx *w; + struct spdk_scsi_dev *devs = spdk_scsi_dev_get_list(); + int i; + + if (params != NULL) { + spdk_jsonrpc_send_error_response(conn, id, SPDK_JSONRPC_ERROR_INVALID_PARAMS, + "get_scsi_devices requires no parameters"); + return; + } + + if (id == NULL) { + return; + } + + w = spdk_jsonrpc_begin_result(conn, id); + spdk_json_write_array_begin(w); + + for (i = 0; i < SPDK_SCSI_MAX_DEVS; i++) { + struct spdk_scsi_dev *dev = &devs[i]; + + if (!dev->is_allocated) { + continue; + } + + spdk_json_write_object_begin(w); + + spdk_json_write_name(w, "id"); + spdk_json_write_int32(w, dev->id); + + spdk_json_write_name(w, "device_name"); + spdk_json_write_string(w, dev->name); + + spdk_json_write_object_end(w); + } + spdk_json_write_array_end(w); + + spdk_jsonrpc_end_result(conn, w); +} +SPDK_RPC_REGISTER("get_scsi_devices", spdk_rpc_get_scsi_devices) diff --git a/lib/scsi/task.c b/lib/scsi/task.c new file mode 100644 index 0000000000..896e137526 --- /dev/null +++ b/lib/scsi/task.c @@ -0,0 +1,194 @@ +/*- + * BSD LICENSE + * + * Copyright (C) 2008-2012 Daisuke Aoyama . + * 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" + +#include +#include +#include + +void +spdk_put_task(struct spdk_scsi_task *task) +{ + if (!task) { + return; + } + + task->ref--; + + if (task->ref == 0) { + struct spdk_bdev_io *bdev_io = task->blockdev_io; + + if (task->parent) { + spdk_put_task(task->parent); + task->parent = NULL; + } + + if (bdev_io) { + /* due to lun reset, the bdev_io status could be pending */ + if (bdev_io->status == SPDK_BDEV_IO_STATUS_PENDING) { + bdev_io->status = SPDK_BDEV_IO_STATUS_FAILED; + } + spdk_bdev_free_io(bdev_io); + } else { + rte_free(task->rbuf); + } + + task->rbuf = NULL; + + RTE_VERIFY(task->owner_task_ctr != NULL); + if (*(task->owner_task_ctr) > 0) { + *(task->owner_task_ctr) -= 1; + } else { + SPDK_ERRLOG("task counter already 0\n"); + } + + task->free_fn(task); + } +} + +void +spdk_scsi_task_construct(struct spdk_scsi_task *task, uint32_t *owner_task_ctr, + struct spdk_scsi_task *parent) +{ + task->ref++; + + RTE_VERIFY(owner_task_ctr != NULL); + task->owner_task_ctr = owner_task_ctr; + *owner_task_ctr += 1; + + if (parent != NULL) { + parent->ref++; + task->parent = parent; + task->type = parent->type; + task->dxfer_dir = parent->dxfer_dir; + task->transfer_len = parent->transfer_len; + task->lun = parent->lun; + task->cdb = parent->cdb; + task->target_port = parent->target_port; + task->initiator_port = parent->initiator_port; + task->id = parent->id; + } +} + +void +spdk_scsi_task_alloc_data(struct spdk_scsi_task *task, uint32_t alloc_len, + uint8_t **data) +{ + /* + * SPDK iSCSI target depends on allocating at least 4096 bytes, even if + * the command requested less. The individual command code (for + * example, INQUIRY) will fill out up to 4096 bytes of data, ignoring + * the allocation length specified in the command. After the individual + * command functions are done, spdk_scsi_lun_execute_tasks() takes + * care of only sending back the amount of data specified in the + * allocation length. + */ + if (alloc_len < 4096) { + alloc_len = 4096; + } + + task->alloc_len = alloc_len; + if (task->rbuf == NULL) { + task->rbuf = rte_malloc(NULL, alloc_len, 0); + } + *data = task->rbuf; + memset(task->rbuf, 0, task->alloc_len); +} + +int +spdk_scsi_task_build_sense_data(struct spdk_scsi_task *task, int sk, int asc, int ascq) +{ + uint8_t *data; + uint8_t *cp; + int resp_code; + int hlen = 0, len, plen; + int total; + + data = task->sense_data; + resp_code = 0x70; /* Current + Fixed format */ + + /* SenseLength */ + memset(data, 0, 2); + hlen = 2; + + /* Sense Data */ + cp = &data[hlen]; + + /* VALID(7) RESPONSE CODE(6-0) */ + cp[0] = 0x80 | resp_code; + /* Obsolete */ + cp[1] = 0; + /* FILEMARK(7) EOM(6) ILI(5) SENSE KEY(3-0) */ + cp[2] = sk & 0xf; + /* INFORMATION */ + memset(&cp[3], 0, 4); + /* ADDITIONAL SENSE LENGTH */ + cp[7] = 0; + len = 8; + + /* COMMAND-SPECIFIC INFORMATION */ + memset(&cp[8], 0, 4); + /* ADDITIONAL SENSE CODE */ + cp[12] = asc; + /* ADDITIONAL SENSE CODE QUALIFIER */ + cp[13] = ascq; + /* FIELD REPLACEABLE UNIT CODE */ + cp[14] = 0; + + /* SKSV(7) SENSE KEY SPECIFIC(6-0,7-0,7-0) */ + cp[15] = 0; + cp[16] = 0; + cp[17] = 0; + /* Additional sense bytes */ + plen = 18 - len; + + /* ADDITIONAL SENSE LENGTH */ + cp[7] = plen; + + total = hlen + len + plen; + + /* SenseLength */ + to_be16(data, total - 2); + task->sense_data_len = total; + + return total; +} + +void +spdk_scsi_task_set_check_condition(struct spdk_scsi_task *task, int sk, int asc, int ascq) +{ + spdk_scsi_task_build_sense_data(task, sk, asc, ascq); + task->status = SPDK_SCSI_STATUS_CHECK_CONDITION; +} diff --git a/test/lib/Makefile b/test/lib/Makefile index a56b5d0696..e27f34facc 100644 --- a/test/lib/Makefile +++ b/test/lib/Makefile @@ -34,7 +34,7 @@ SPDK_ROOT_DIR := $(abspath $(CURDIR)/../..) include $(SPDK_ROOT_DIR)/mk/spdk.common.mk -DIRS-y = bdev event log json jsonrpc nvme nvmf memory ioat +DIRS-y = bdev event log json jsonrpc nvme nvmf memory scsi ioat .PHONY: all clean $(DIRS-y) diff --git a/test/lib/scsi/Makefile b/test/lib/scsi/Makefile new file mode 100644 index 0000000000..295ce65c32 --- /dev/null +++ b/test/lib/scsi/Makefile @@ -0,0 +1,44 @@ +# +# BSD LICENSE +# +# 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. +# + +SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../..) +include $(SPDK_ROOT_DIR)/mk/spdk.common.mk + +DIRS-y = dev lun scsi_bdev + +.PHONY: all clean $(DIRS-y) + +all: $(DIRS-y) +clean: $(DIRS-y) + +include $(SPDK_ROOT_DIR)/mk/spdk.subdirs.mk diff --git a/test/lib/scsi/dev/.gitignore b/test/lib/scsi/dev/.gitignore new file mode 100644 index 0000000000..e325086bb2 --- /dev/null +++ b/test/lib/scsi/dev/.gitignore @@ -0,0 +1 @@ +dev_ut diff --git a/test/lib/scsi/dev/Makefile b/test/lib/scsi/dev/Makefile new file mode 100644 index 0000000000..0ad130d618 --- /dev/null +++ b/test/lib/scsi/dev/Makefile @@ -0,0 +1,54 @@ +# +# BSD LICENSE +# +# 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. +# + +SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../../..) +include $(SPDK_ROOT_DIR)/mk/spdk.common.mk + +SPDK_LIBS += $(SPDK_ROOT_DIR)/lib/log/libspdk_log.a + +CFLAGS += -I$(SPDK_ROOT_DIR)/lib/scsi +LIBS += $(SPDK_LIBS) +LIBS += -lcunit + +APP = dev_ut +C_SRCS = dev_ut.c + +all: $(APP) + +$(APP): $(OBJS) $(SPDK_LIBS) + $(LINK_C) + +clean: + $(CLEAN_C) $(APP) + +include $(SPDK_ROOT_DIR)/mk/spdk.deps.mk diff --git a/test/lib/scsi/dev/dev_ut.c b/test/lib/scsi/dev/dev_ut.c new file mode 100644 index 0000000000..cca8f75cf2 --- /dev/null +++ b/test/lib/scsi/dev/dev_ut.c @@ -0,0 +1,494 @@ +/*- + * BSD LICENSE + * + * 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 +#include +#include + +#include "CUnit/Basic.h" + +#include "dev.c" +#include "port.c" + +static uint32_t g_task_count = 0; +static struct spdk_scsi_lun g_lun = {}; +static struct spdk_bdev g_bdev = {}; + +void +spdk_bdev_unregister(struct spdk_bdev *bdev) +{ +} + +static struct spdk_scsi_task * +spdk_get_task(uint32_t *owner_task_ctr) +{ + struct spdk_scsi_task *task; + + task = calloc(1, sizeof(*task)); + if (!task) { + return NULL; + } + + task->id = g_task_count; + + g_task_count++; + + return task; +} + +void +spdk_put_task(struct spdk_scsi_task *task) +{ + g_task_count--; + + free(task); +} + +_spdk_scsi_lun * +spdk_scsi_lun_construct(const char *name, struct spdk_bdev *bdev) +{ + snprintf(g_lun.name, sizeof(g_lun.name), "%s", name); + g_lun.bdev = bdev; + return &g_lun; +} + +struct spdk_bdev * +spdk_bdev_db_get_by_name(const char *bdev_name) +{ + snprintf(g_bdev.name, sizeof(g_bdev.name), "%s", bdev_name); + return &g_bdev; +} + +int +spdk_scsi_lun_claim(struct spdk_scsi_lun *lun) +{ + return 0; +} + +int +spdk_scsi_lun_unclaim(struct spdk_scsi_lun *lun) +{ + return 0; +} + +int +spdk_scsi_lun_task_mgmt_execute(struct spdk_scsi_task *task) +{ + return 0; +} + +void +spdk_scsi_lun_append_task(struct spdk_scsi_lun *lun, struct spdk_scsi_task *task) +{ +} + +void +spdk_scsi_lun_execute_tasks(struct spdk_scsi_lun *lun) +{ +} + + +static void +dev_destruct_null_dev(void) +{ + /* pass null for the dev */ + spdk_scsi_dev_destruct(NULL); +} + +static void +dev_destruct_zero_luns(void) +{ + struct spdk_scsi_dev dev = { 0 }; + + /* pass maxlun as 0. + * No luns attached to the dev */ + dev.maxlun = 0; + + /* free the dev */ + spdk_scsi_dev_destruct(&dev); +} + +static void +dev_destruct_null_lun(void) +{ + struct spdk_scsi_dev dev = { 0 }; + + dev.maxlun = 1; + /* pass null for the lun */ + dev.lun[0] = NULL; + + /* free the dev */ + spdk_scsi_dev_destruct(&dev); +} + +static void +dev_destruct_success(void) +{ + struct spdk_scsi_dev dev = { 0 }; + struct spdk_scsi_lun lun = { 0 }; + + /* dev with a single lun */ + dev.maxlun = 1; + dev.lun[0] = &lun; + + /* free the dev */ + spdk_scsi_dev_destruct(&dev); +} + +static void +dev_construct_num_luns_zero(void) +{ + struct spdk_scsi_dev *dev; + char *lun_name_list[1] = {}; + int lun_id_list[1] = { 0 }; + + dev = spdk_scsi_dev_construct("Name", lun_name_list, lun_id_list, 0); + + /* dev should be null since we passed num_luns = 0 */ + CU_ASSERT_TRUE(dev == NULL); +} + +static void +dev_construct_no_lun_zero(void) +{ + struct spdk_scsi_dev *dev; + char *lun_name_list[1] = {}; + int lun_id_list[1] = { 0 }; + + lun_id_list[0] = 1; + + dev = spdk_scsi_dev_construct("Name", lun_name_list, lun_id_list, 1); + + /* dev should be null since no LUN0 was specified (lun_id_list[0] = 1) */ + CU_ASSERT_TRUE(dev == NULL); +} + +static void +dev_construct_null_lun(void) +{ + struct spdk_scsi_dev *dev; + char *lun_name_list[1] = {}; + int lun_id_list[1] = { 0 }; + + dev = spdk_scsi_dev_construct("Name", lun_name_list, lun_id_list, 1); + + /* dev should be null since no LUN0 was specified (lun_list[0] = NULL) */ + CU_ASSERT_TRUE(dev == NULL); +} + +static void +dev_construct_success(void) +{ + struct spdk_scsi_dev *dev; + char *lun_name_list[1] = {"malloc0"}; + int lun_id_list[1] = { 0 }; + + dev = spdk_scsi_dev_construct("Name", lun_name_list, lun_id_list, 1); + + /* Successfully constructs and returns a dev */ + CU_ASSERT_TRUE(dev != NULL); + + /* free the dev */ + spdk_scsi_dev_destruct(dev); +} + +static void +dev_queue_mgmt_task_success(void) +{ + struct spdk_scsi_dev *dev; + char *lun_name_list[1] = {"malloc0"}; + int lun_id_list[1] = { 0 }; + struct spdk_scsi_task *task; + + dev = spdk_scsi_dev_construct("Name", lun_name_list, lun_id_list, 1); + + /* Successfully constructs and returns a dev */ + CU_ASSERT_TRUE(dev != NULL); + + task = spdk_get_task(NULL); + + spdk_scsi_dev_queue_mgmt_task(dev, task); + + spdk_put_task(task); + + spdk_scsi_dev_destruct(dev); +} + +static void +dev_queue_task_success(void) +{ + struct spdk_scsi_dev *dev; + char *lun_name_list[1] = {"malloc0"}; + int lun_id_list[1] = { 0 }; + struct spdk_scsi_task *task; + + dev = spdk_scsi_dev_construct("Name", lun_name_list, lun_id_list, 1); + + /* Successfully constructs and returns a dev */ + CU_ASSERT_TRUE(dev != NULL); + + task = spdk_get_task(NULL); + + spdk_scsi_dev_queue_task(dev, task); + + spdk_put_task(task); + + spdk_scsi_dev_destruct(dev); +} + +static void +dev_stop_success(void) +{ + struct spdk_scsi_dev dev = { 0 }; + struct spdk_scsi_task *task; + struct spdk_scsi_task *task_mgmt; + + task = spdk_get_task(NULL); + + spdk_scsi_dev_queue_task(&dev, task); + + task_mgmt = spdk_get_task(NULL); + + /* Enqueue the tasks into dev->task_mgmt_submit_queue*/ + spdk_scsi_dev_queue_mgmt_task(&dev, task_mgmt); + + spdk_put_task(task); + spdk_put_task(task_mgmt); +} + +static void +dev_add_port_max_ports(void) +{ + struct spdk_scsi_dev dev = { 0 }; + const char *name; + int id, rc; + + /* dev is set to SPDK_SCSI_DEV_MAX_PORTS */ + dev.num_ports = SPDK_SCSI_DEV_MAX_PORTS; + name = "Name of Port"; + id = 1; + + rc = spdk_scsi_dev_add_port(&dev, id, name); + + /* returns -1; since the dev already has maximum + * number of ports (SPDK_SCSI_DEV_MAX_PORTS) */ + CU_ASSERT_TRUE(rc < 0); +} + +static void +dev_add_port_construct_failure(void) +{ + struct spdk_scsi_dev dev = { 0 }; + const int port_name_length = SPDK_SCSI_PORT_MAX_NAME_LENGTH + 2; + char name[port_name_length]; + int id, rc; + + dev.num_ports = 1; + /* Set the name such that the length exceeds SPDK_SCSI_PORT_MAX_NAME_LENGTH + * SPDK_SCSI_PORT_MAX_NAME_LENGTH = 256 */ + memset(name, 'a', port_name_length - 1); + name[port_name_length - 1] = '\0'; + id = 1; + + rc = spdk_scsi_dev_add_port(&dev, id, name); + + /* returns -1; since the length of the name exceeds + * SPDK_SCSI_PORT_MAX_NAME_LENGTH */ + CU_ASSERT_TRUE(rc < 0); +} + +static void +dev_add_port_success(void) +{ + struct spdk_scsi_dev dev = { 0 }; + const char *name; + int id, rc; + + dev.num_ports = 1; + name = "Name of Port"; + id = 1; + + rc = spdk_scsi_dev_add_port(&dev, id, name); + + /* successfully adds a port */ + CU_ASSERT_EQUAL(rc, 0); + /* Assert num_ports has been incremented to 2 */ + CU_ASSERT_EQUAL(dev.num_ports, 2); +} + +static void +dev_find_port_by_id_num_ports_zero(void) +{ + struct spdk_scsi_dev dev = { 0 }; + struct spdk_scsi_port *rp_port; + uint64_t id; + + dev.num_ports = 0; + id = 1; + + rp_port = spdk_scsi_dev_find_port_by_id(&dev, id); + + /* returns null; since dev's num_ports is 0 */ + CU_ASSERT_TRUE(rp_port == NULL); +} + +static void +dev_find_port_by_id_id_not_found_failure(void) +{ + struct spdk_scsi_dev dev = { 0 }; + struct spdk_scsi_port *rp_port; + const char *name; + int rc; + uint64_t id, find_id; + + id = 1; + dev.num_ports = 1; + name = "Name of Port"; + find_id = 2; + + /* Add a port with id = 1 */ + rc = spdk_scsi_dev_add_port(&dev, id, name); + + CU_ASSERT_EQUAL(rc, 0); + + /* Find port with id = 2 */ + rp_port = spdk_scsi_dev_find_port_by_id(&dev, find_id); + + /* returns null; failed to find port specified by id = 2 */ + CU_ASSERT_TRUE(rp_port == NULL); +} + +static void +dev_find_port_by_id_success(void) +{ + struct spdk_scsi_dev dev = { 0 }; + struct spdk_scsi_port *rp_port; + const char *name; + int rc; + uint64_t id; + + id = 1; + dev.num_ports = 1; + name = "Name of Port"; + + /* Add a port */ + rc = spdk_scsi_dev_add_port(&dev, id, name); + + CU_ASSERT_EQUAL(rc, 0); + + /* Find port by the same id as the one added above */ + rp_port = spdk_scsi_dev_find_port_by_id(&dev, id); + + /* Successfully found port specified by id */ + CU_ASSERT_TRUE(rp_port != NULL); + if (rp_port != NULL) { + /* Assert the found port's id and name are same as + * the port added. */ + CU_ASSERT_EQUAL(rp_port->id, 1); + CU_ASSERT_STRING_EQUAL(rp_port->name, "Name of Port"); + } +} + +static void +dev_print_success(void) +{ + struct spdk_scsi_dev dev = { 0 }; + struct spdk_scsi_lun lun = { 0 }; + + dev.maxlun = 1; + dev.lun[0] = &lun; + + /* Prints the dev and a list of the LUNs associated with + * the dev */ + spdk_scsi_dev_print(&dev); +} + +int +main(int argc, char **argv) +{ + CU_pSuite suite = NULL; + unsigned int num_failures; + + if (CU_initialize_registry() != CUE_SUCCESS) { + return CU_get_error(); + } + + suite = CU_add_suite("dev_suite", NULL, NULL); + if (suite == NULL) { + CU_cleanup_registry(); + return CU_get_error(); + } + + if ( + CU_add_test(suite, "destruct - null dev", + dev_destruct_null_dev) == NULL + || CU_add_test(suite, "destruct - zero luns", dev_destruct_zero_luns) == NULL + || CU_add_test(suite, "destruct - null lun", dev_destruct_null_lun) == NULL + || CU_add_test(suite, "destruct - success", dev_destruct_success) == NULL + || CU_add_test(suite, "construct - queue depth gt max depth", + dev_construct_num_luns_zero) == NULL + || CU_add_test(suite, "construct - no lun0", + dev_construct_no_lun_zero) == NULL + || CU_add_test(suite, "construct - null lun", + dev_construct_null_lun) == NULL + || CU_add_test(suite, "construct - success", dev_construct_success) == NULL + || CU_add_test(suite, "dev queue task mgmt - success", + dev_queue_mgmt_task_success) == NULL + || CU_add_test(suite, "dev queue task - success", + dev_queue_task_success) == NULL + || CU_add_test(suite, "dev stop - success", dev_stop_success) == NULL + || CU_add_test(suite, "dev add port - max ports", + dev_add_port_max_ports) == NULL + || CU_add_test(suite, "dev add port - construct port failure", + dev_add_port_construct_failure) == NULL + || CU_add_test(suite, "dev add port - success", + dev_add_port_success) == NULL + || CU_add_test(suite, "dev find port by id - num ports zero", + dev_find_port_by_id_num_ports_zero) == NULL + || CU_add_test(suite, "dev find port by id - different port id failure", + dev_find_port_by_id_id_not_found_failure) == NULL + || CU_add_test(suite, "dev find port by id - success", + dev_find_port_by_id_success) == NULL + || CU_add_test(suite, "dev print - success", dev_print_success) == NULL + ) { + CU_cleanup_registry(); + return CU_get_error(); + } + + CU_basic_set_mode(CU_BRM_VERBOSE); + CU_basic_run_tests(); + num_failures = CU_get_number_of_failures(); + CU_cleanup_registry(); + + return num_failures; +} diff --git a/test/lib/scsi/lun/.gitignore b/test/lib/scsi/lun/.gitignore new file mode 100644 index 0000000000..89bd2aaf1a --- /dev/null +++ b/test/lib/scsi/lun/.gitignore @@ -0,0 +1 @@ +lun_ut diff --git a/test/lib/scsi/lun/Makefile b/test/lib/scsi/lun/Makefile new file mode 100644 index 0000000000..5bb1284519 --- /dev/null +++ b/test/lib/scsi/lun/Makefile @@ -0,0 +1,56 @@ +# +# BSD LICENSE +# +# 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. +# + +SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../../..) +include $(SPDK_ROOT_DIR)/mk/spdk.common.mk + +SPDK_LIBS += $(SPDK_ROOT_DIR)/lib/log/libspdk_log.a \ + $(SPDK_ROOT_DIR)/lib/cunit/libspdk_cunit.a + +CFLAGS += -I$(SPDK_ROOT_DIR)/test +CFLAGS += -I$(SPDK_ROOT_DIR)/lib/scsi +LIBS += $(SPDK_LIBS) +LIBS += -lcunit + +APP = lun_ut +C_SRCS = lun_ut.c + +all: $(APP) + +$(APP): $(OBJS) $(SPDK_LIBS) + $(LINK_C) + +clean: + $(CLEAN_C) $(APP) + +include $(SPDK_ROOT_DIR)/mk/spdk.deps.mk diff --git a/test/lib/scsi/lun/lun_ut.c b/test/lib/scsi/lun/lun_ut.c new file mode 100644 index 0000000000..551dc4ac89 --- /dev/null +++ b/test/lib/scsi/lun/lun_ut.c @@ -0,0 +1,693 @@ +/*- + * BSD LICENSE + * + * 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 +#include +#include +#include + +#include "spdk_cunit.h" + +#include "lun.c" +#include "lun_db.c" + +SPDK_LOG_REGISTER_TRACE_FLAG("scsi", SPDK_TRACE_SCSI) + +struct spdk_scsi_globals g_spdk_scsi; + +static bool g_lun_execute_fail = false; +static bool g_lun_task_set_full_flag = false; +static int g_lun_execute_status = SPDK_SCSI_TASK_PENDING; +static uint32_t g_task_count = 0; + +void spdk_trace_record(uint16_t tpoint_id, uint16_t poller_id, uint32_t size, + uint64_t object_id, uint64_t arg1) +{ +} + +static struct spdk_scsi_task * +spdk_get_task(uint32_t *owner_task_ctr) +{ + struct spdk_scsi_task *task; + + task = calloc(1, sizeof(*task)); + if (!task) { + return NULL; + } + + task->id = g_task_count; + + g_task_count++; + + return task; +} + +void +spdk_put_task(struct spdk_scsi_task *task) +{ + g_task_count--; + + if (task->rbuf) { + free(task->rbuf); + } + + free(task); +} + +void spdk_scsi_task_set_check_condition(struct spdk_scsi_task *task, int sk, + int asc, int ascq) +{ + task->status = SPDK_SCSI_STATUS_CHECK_CONDITION; +} + +void +spdk_scsi_task_alloc_data(struct spdk_scsi_task *task, uint32_t alloc_len, + uint8_t **data) +{ + if (alloc_len < 4096) + alloc_len = 4096; + + task->alloc_len = alloc_len; + *data = task->rbuf; +} + +void spdk_scsi_dev_queue_mgmt_task(struct spdk_scsi_dev *dev, + struct spdk_scsi_task *task) +{ +} + +void spdk_scsi_dev_delete_lun(struct spdk_scsi_dev *dev, + struct spdk_scsi_lun *lun) +{ + return; +} + +int +spdk_bdev_scsi_reset(struct spdk_bdev *bdev, struct spdk_scsi_task *task) +{ + return 0; +} + +int +spdk_bdev_scsi_execute(struct spdk_bdev *bdev, struct spdk_scsi_task *task) +{ + if (g_lun_execute_fail) + return -EINVAL; + else { + if (g_lun_task_set_full_flag) + task->status = SPDK_SCSI_STATUS_TASK_SET_FULL; + else + task->status = SPDK_SCSI_STATUS_GOOD; + + if (g_lun_execute_status == SPDK_SCSI_TASK_PENDING) + return g_lun_execute_status; + else if (g_lun_execute_status == SPDK_SCSI_TASK_COMPLETE) + return g_lun_execute_status; + else + return 0; + } +} + +void spdk_bdev_unregister(struct spdk_bdev *bdev) +{ + return; +} + +void spdk_event_call(spdk_event_t event) +{ +} + +static _spdk_scsi_lun * +lun_construct(void) +{ + struct spdk_scsi_lun *lun; + struct spdk_bdev bdev; + + lun = spdk_scsi_lun_construct("lun0", &bdev); + + SPDK_CU_ASSERT_FATAL(lun != NULL); + if (lun != NULL) { + SPDK_CU_ASSERT_FATAL(TAILQ_EMPTY(&lun->pending_tasks)); + } + + return lun; +} + +static void +lun_destruct(struct spdk_scsi_lun *lun) +{ + spdk_scsi_lun_destruct(lun); +} + +static void +lun_task_mgmt_execute_null_task(void) +{ + int rc; + + rc = spdk_scsi_lun_task_mgmt_execute(NULL); + + /* returns -1 since we passed NULL for the task */ + CU_ASSERT_TRUE(rc < 0); + CU_ASSERT_EQUAL(g_task_count, 0); +} + +static void +lun_task_mgmt_execute_abort_task_null_lun_failure(void) +{ + struct spdk_scsi_task *mgmt_task; + struct spdk_scsi_port initiator_port = { 0 }; + int rc; + + mgmt_task = spdk_get_task(NULL); + mgmt_task->function = SPDK_SCSI_TASK_FUNC_ABORT_TASK; + mgmt_task->lun = NULL; + mgmt_task->initiator_port = &initiator_port; + + rc = spdk_scsi_lun_task_mgmt_execute(mgmt_task); + + spdk_put_task(mgmt_task); + + /* returns -1 since we passed NULL for LUN */ + CU_ASSERT_TRUE(rc < 0); + CU_ASSERT_EQUAL(g_task_count, 0); +} + +static void +lun_task_mgmt_execute_abort_task_not_supported(void) +{ + struct spdk_scsi_lun *lun; + struct spdk_scsi_task *task; + struct spdk_scsi_task *mgmt_task; + struct spdk_scsi_port initiator_port = { 0 }; + struct spdk_scsi_dev dev = { 0 }; + uint8_t cdb[6] = { 0 }; + int rc; + + lun = lun_construct(); + lun->dev = &dev; + + mgmt_task = spdk_get_task(NULL); + mgmt_task->function = SPDK_SCSI_TASK_FUNC_ABORT_TASK; + mgmt_task->lun = lun; + mgmt_task->initiator_port = &initiator_port; + + /* Params to add regular task to the lun->tasks */ + task = spdk_get_task(NULL); + task->lun = lun; + task->cdb = cdb; + + /* Set the task's id and abort_id to the same value */ + mgmt_task->abort_id = task->id; + + spdk_scsi_lun_append_task(lun, task); + + /* task should now be on the pending_task list */ + CU_ASSERT(!TAILQ_EMPTY(&lun->pending_tasks)); + + spdk_scsi_lun_execute_tasks(lun); + + /*task should now be on the tasks list */ + CU_ASSERT(!TAILQ_EMPTY(&lun->tasks)); + + rc = spdk_scsi_lun_task_mgmt_execute(mgmt_task); + + /* returns -1 since task abort is not supported */ + CU_ASSERT_TRUE(rc < 0); + CU_ASSERT(mgmt_task->response == SPDK_SCSI_TASK_MGMT_RESP_REJECT_FUNC_NOT_SUPPORTED); + + spdk_put_task(mgmt_task); + spdk_put_task(task); + + lun_destruct(lun); + + CU_ASSERT_EQUAL(g_task_count, 0); +} + +static void +lun_task_mgmt_execute_abort_task_all_null_lun_failure(void) +{ + struct spdk_scsi_task *mgmt_task; + struct spdk_scsi_port initiator_port = { 0 }; + int rc; + + mgmt_task = spdk_get_task(NULL); + mgmt_task->function = SPDK_SCSI_TASK_FUNC_ABORT_TASK_SET; + mgmt_task->lun = NULL; + mgmt_task->initiator_port = &initiator_port; + + rc = spdk_scsi_lun_task_mgmt_execute(mgmt_task); + + /* Returns -1 since we passed NULL for lun */ + CU_ASSERT_TRUE(rc < 0); + + spdk_put_task(mgmt_task); + + CU_ASSERT_EQUAL(g_task_count, 0); +} + +static void +lun_task_mgmt_execute_abort_task_all_not_supported(void) +{ + struct spdk_scsi_lun *lun; + struct spdk_scsi_task *task; + struct spdk_scsi_task *mgmt_task; + struct spdk_scsi_port initiator_port = { 0 }; + struct spdk_scsi_dev dev = { 0 }; + int rc; + uint8_t cdb[6] = { 0 }; + + lun = lun_construct(); + lun->dev = &dev; + + mgmt_task = spdk_get_task(NULL); + mgmt_task->function = SPDK_SCSI_TASK_FUNC_ABORT_TASK_SET; + mgmt_task->lun = lun; + mgmt_task->initiator_port = &initiator_port; + + /* Params to add regular task to the lun->tasks */ + task = spdk_get_task(NULL); + task->initiator_port = &initiator_port; + task->lun = lun; + task->cdb = cdb; + + spdk_scsi_lun_append_task(lun, task); + + /* task should now be on the pending_task list */ + CU_ASSERT(!TAILQ_EMPTY(&lun->pending_tasks)); + + spdk_scsi_lun_execute_tasks(lun); + + /*task should now be on the tasks list */ + CU_ASSERT(!TAILQ_EMPTY(&lun->tasks)); + + rc = spdk_scsi_lun_task_mgmt_execute(mgmt_task); + + /* returns -1 since task abort is not supported */ + CU_ASSERT_TRUE(rc < 0); + CU_ASSERT(mgmt_task->response == SPDK_SCSI_TASK_MGMT_RESP_REJECT_FUNC_NOT_SUPPORTED); + + spdk_put_task(mgmt_task); + spdk_put_task(task); + + lun_destruct(lun); + + CU_ASSERT_EQUAL(g_task_count, 0); +} + +static void +lun_task_mgmt_execute_lun_reset_failure(void) +{ + struct spdk_scsi_task *mgmt_task; + int rc; + + mgmt_task = spdk_get_task(NULL); + mgmt_task->lun = NULL; + mgmt_task->function = SPDK_SCSI_TASK_FUNC_LUN_RESET; + + rc = spdk_scsi_lun_task_mgmt_execute(mgmt_task); + + /* Returns -1 since we passed NULL for lun */ + CU_ASSERT_TRUE(rc < 0); + + spdk_put_task(mgmt_task); + + CU_ASSERT_EQUAL(g_task_count, 0); +} + +static void +lun_task_mgmt_execute_lun_reset(void) +{ + struct spdk_scsi_lun *lun; + struct spdk_scsi_task *mgmt_task; + struct spdk_scsi_dev dev = { 0 }; + int rc; + + lun = lun_construct(); + lun->dev = &dev; + + mgmt_task = spdk_get_task(NULL); + mgmt_task->lun = lun; + mgmt_task->function = SPDK_SCSI_TASK_FUNC_LUN_RESET; + + rc = spdk_scsi_lun_task_mgmt_execute(mgmt_task); + + /* Returns success */ + CU_ASSERT_EQUAL(rc, 0); + + spdk_put_task(mgmt_task); + + lun_destruct(lun); + + CU_ASSERT_EQUAL(g_task_count, 0); +} + +static void +lun_task_mgmt_execute_invalid_case(void) +{ + struct spdk_scsi_lun *lun; + struct spdk_scsi_task *mgmt_task; + struct spdk_scsi_dev dev = { 0 }; + int rc; + + lun = lun_construct(); + lun->dev = &dev; + + mgmt_task = spdk_get_task(NULL); + /* Pass an invalid value to the switch statement */ + mgmt_task->function = 5; + + rc = spdk_scsi_lun_task_mgmt_execute(mgmt_task); + + /* Returns -1 on passing an invalid value to the switch case */ + CU_ASSERT_TRUE(rc < 0); + + spdk_put_task(mgmt_task); + + lun_destruct(lun); + + CU_ASSERT_EQUAL(g_task_count, 0); +} + +static void +lun_append_task_null_lun_task_cdb_spc_inquiry(void) +{ + struct spdk_scsi_task *task; + uint8_t cdb[6] = { 0 }; + + task = spdk_get_task(NULL); + task->cdb = cdb; + task->cdb[0] = SPDK_SPC_INQUIRY; + /* alloc_len >= 4096 */ + task->cdb[3] = 0xFF; + task->cdb[4] = 0xFF; + task->rbuf = malloc(65536); + task->lun = NULL; + + spdk_scsi_lun_append_task(NULL, task); + + CU_ASSERT_EQUAL(task->status, SPDK_SCSI_STATUS_GOOD); + + spdk_put_task(task); + + CU_ASSERT_EQUAL(g_task_count, 0); +} + +static void +lun_append_task_null_lun_alloc_len_lt_4096(void) +{ + struct spdk_scsi_task *task; + uint8_t cdb[6] = { 0 }; + + task = spdk_get_task(NULL); + task->cdb = cdb; + task->cdb[0] = SPDK_SPC_INQUIRY; + /* alloc_len < 4096 */ + task->cdb[3] = 0; + task->cdb[4] = 0; + /* alloc_len is set to a minimal value of 4096 + * Hence, rbuf of size 4096 is allocated*/ + task->rbuf = malloc(4096); + + spdk_scsi_lun_append_task(NULL, task); + + CU_ASSERT_EQUAL(task->status, SPDK_SCSI_STATUS_GOOD); + + spdk_put_task(task); + + CU_ASSERT_EQUAL(g_task_count, 0); +} + +static void +lun_append_task_null_lun_not_supported(void) +{ + struct spdk_scsi_task *task; + uint8_t cdb[6] = { 0 }; + + task = spdk_get_task(NULL); + task->cdb = cdb; + task->lun = NULL; + + spdk_scsi_lun_append_task(NULL, task); + + CU_ASSERT_EQUAL(task->status, SPDK_SCSI_STATUS_CHECK_CONDITION); + /* LUN not supported; task's data transferred should be 0 */ + CU_ASSERT_EQUAL(task->data_transferred, 0); + + spdk_put_task(task); + + CU_ASSERT_EQUAL(g_task_count, 0); +} + +static void +lun_execute_task_set_full(void) +{ + struct spdk_scsi_lun *lun; + struct spdk_scsi_task *task; + struct spdk_scsi_dev dev = { 0 }; + + lun = lun_construct(); + + task = spdk_get_task(NULL); + task->lun = lun; + lun->dev = &dev; + + g_lun_execute_fail = false; + g_lun_task_set_full_flag = true; + + spdk_scsi_lun_append_task(lun, task); + + /* task should now be on the pending_task list */ + CU_ASSERT(!TAILQ_EMPTY(&lun->pending_tasks)); + + /* but the tasks list should still be empty since it has not been + executed yet + */ + CU_ASSERT(TAILQ_EMPTY(&lun->tasks)); + + spdk_scsi_lun_execute_tasks(lun); + + /* Assert the lun's task set is full; hence the function + has failed to add another task to the tasks queue */ + CU_ASSERT(TAILQ_EMPTY(&lun->tasks)); + CU_ASSERT(task->status == SPDK_SCSI_STATUS_TASK_SET_FULL); + + spdk_put_task(task); + + lun_destruct(lun); + + CU_ASSERT_EQUAL(g_task_count, 0); +} + +static void +lun_execute_scsi_task_pending(void) +{ + struct spdk_scsi_lun *lun; + struct spdk_scsi_task *task; + struct spdk_scsi_dev dev = { 0 }; + + lun = lun_construct(); + + task = spdk_get_task(NULL); + task->lun = lun; + lun->dev = &dev; + + g_lun_execute_fail = false; + g_lun_task_set_full_flag = false; + g_lun_execute_status = SPDK_SCSI_TASK_PENDING; + + spdk_scsi_lun_append_task(lun, task); + + /* task should now be on the pending_task list */ + CU_ASSERT(!TAILQ_EMPTY(&lun->pending_tasks)); + + /* but the tasks list should still be empty since it has not been + executed yet + */ + CU_ASSERT(TAILQ_EMPTY(&lun->tasks)); + + spdk_scsi_lun_execute_tasks(lun); + + /* Assert the task has been successfully added to the tasks queue */ + CU_ASSERT(!TAILQ_EMPTY(&lun->tasks)); + + spdk_put_task(task); + + lun_destruct(lun); + + CU_ASSERT_EQUAL(g_task_count, 0); +} + +static void +lun_execute_scsi_task_complete(void) +{ + struct spdk_scsi_lun *lun; + struct spdk_scsi_task *task; + struct spdk_scsi_dev dev = { 0 }; + + lun = lun_construct(); + + task = spdk_get_task(NULL); + task->lun = lun; + lun->dev = &dev; + + g_lun_execute_fail = false; + g_lun_task_set_full_flag = false; + g_lun_execute_status = SPDK_SCSI_TASK_COMPLETE; + + spdk_scsi_lun_append_task(lun, task); + + /* task should now be on the pending_task list */ + CU_ASSERT(!TAILQ_EMPTY(&lun->pending_tasks)); + + /* but the tasks list should still be empty since it has not been + executed yet + */ + CU_ASSERT(TAILQ_EMPTY(&lun->tasks)); + + spdk_scsi_lun_execute_tasks(lun); + + /* Assert the task has not been added to the tasks queue */ + CU_ASSERT(TAILQ_EMPTY(&lun->tasks)); + + spdk_put_task(task); + + lun_destruct(lun); + + CU_ASSERT_EQUAL(g_task_count, 0); +} + +static void +lun_destruct_success(void) +{ + struct spdk_scsi_lun *lun; + int rc; + + lun = lun_construct(); + + rc = spdk_scsi_lun_destruct(lun); + + CU_ASSERT_EQUAL(rc, 0); + CU_ASSERT_EQUAL(g_task_count, 0); +} + +static void +lun_construct_null_ctx(void) +{ + struct spdk_scsi_lun *lun; + + lun = spdk_scsi_lun_construct("lun0", NULL); + + /* lun should be NULL since we passed NULL for the ctx pointer. */ + CU_ASSERT(lun == NULL); + CU_ASSERT_EQUAL(g_task_count, 0); +} + +static void +lun_construct_success(void) +{ + struct spdk_scsi_lun *lun = lun_construct(); + + lun_destruct(lun); + + CU_ASSERT_EQUAL(g_task_count, 0); +} + +int +main(int argc, char **argv) +{ + CU_pSuite suite = NULL; + unsigned int num_failures; + int rc; + + if (CU_initialize_registry() != CUE_SUCCESS) { + return CU_get_error(); + } + + suite = CU_add_suite("lun_suite", NULL, NULL); + if (suite == NULL) { + CU_cleanup_registry(); + return CU_get_error(); + } + + if ( + CU_add_test(suite, "task management - null task failure", + lun_task_mgmt_execute_null_task) == NULL + || CU_add_test(suite, "task management abort task - null lun failure", + lun_task_mgmt_execute_abort_task_null_lun_failure) == NULL + || CU_add_test(suite, "task management abort task - not supported", + lun_task_mgmt_execute_abort_task_not_supported) == NULL + || CU_add_test(suite, "task management abort task set - null lun failure", + lun_task_mgmt_execute_abort_task_all_null_lun_failure) == NULL + || CU_add_test(suite, "task management abort task set - success", + lun_task_mgmt_execute_abort_task_all_not_supported) == NULL + || CU_add_test(suite, "task management - lun reset failure", + lun_task_mgmt_execute_lun_reset_failure) == NULL + || CU_add_test(suite, "task management - lun reset success", + lun_task_mgmt_execute_lun_reset) == NULL + || CU_add_test(suite, "task management - invalid option", + lun_task_mgmt_execute_invalid_case) == NULL + || CU_add_test(suite, "append task - null lun SPDK_SPC_INQUIRY", + lun_append_task_null_lun_task_cdb_spc_inquiry) == NULL + || CU_add_test(suite, "append task - allocated length less than 4096", + lun_append_task_null_lun_alloc_len_lt_4096) == NULL + || CU_add_test(suite, "append task - unsupported lun", + lun_append_task_null_lun_not_supported) == NULL + || CU_add_test(suite, "execute task - task set full", + lun_execute_task_set_full) == NULL + || CU_add_test(suite, "execute task - scsi task pending", + lun_execute_scsi_task_pending) == NULL + || CU_add_test(suite, "execute task - scsi task complete", + lun_execute_scsi_task_complete) == NULL + || CU_add_test(suite, "destruct task - success", lun_destruct_success) == NULL + || CU_add_test(suite, "construct - null ctx", lun_construct_null_ctx) == NULL + || CU_add_test(suite, "construct - success", lun_construct_success) == NULL + ) { + CU_cleanup_registry(); + return CU_get_error(); + } + + CU_basic_set_mode(CU_BRM_VERBOSE); + CU_basic_run_tests(); + num_failures = CU_get_number_of_failures(); + + if (argc > 1) { + rc = spdk_cunit_print_results(argv[1]); + if (rc != 0) { + CU_cleanup_registry(); + return rc; + } + } + + CU_cleanup_registry(); + return num_failures; +} diff --git a/test/lib/scsi/scsi.sh b/test/lib/scsi/scsi.sh new file mode 100755 index 0000000000..9dd01128b4 --- /dev/null +++ b/test/lib/scsi/scsi.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +set -xe + +testdir=$(readlink -f $(dirname $0)) +rootdir=$testdir/../../.. +source $rootdir/scripts/autotest_common.sh + +timing_enter scsi + +$testdir/dev/dev_ut +$testdir/lun/lun_ut +$testdir/scsi_bdev/scsi_bdev_ut + +timing_exit scsi diff --git a/test/lib/scsi/scsi_bdev/.gitignore b/test/lib/scsi/scsi_bdev/.gitignore new file mode 100644 index 0000000000..8f1ecc12c0 --- /dev/null +++ b/test/lib/scsi/scsi_bdev/.gitignore @@ -0,0 +1 @@ +scsi_bdev_ut diff --git a/test/lib/scsi/scsi_bdev/Makefile b/test/lib/scsi/scsi_bdev/Makefile new file mode 100644 index 0000000000..0598b8080f --- /dev/null +++ b/test/lib/scsi/scsi_bdev/Makefile @@ -0,0 +1,56 @@ +# +# BSD LICENSE +# +# 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. +# + +SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../../..) +include $(SPDK_ROOT_DIR)/mk/spdk.common.mk + +SPDK_LIBS += $(SPDK_ROOT_DIR)/lib/log/libspdk_log.a \ + $(SPDK_ROOT_DIR)/lib/cunit/libspdk_cunit.a + +CFLAGS += -I$(SPDK_ROOT_DIR)/test +CFLAGS += -I$(SPDK_ROOT_DIR)/lib/scsi +LIBS += $(SPDK_LIBS) +LIBS += -lcunit + +APP = scsi_bdev_ut +C_SRCS = scsi_bdev_ut.c + +all: $(APP) + +$(APP): $(OBJS) $(SPDK_LIBS) + $(LINK_C) + +clean: + $(CLEAN_C) $(APP) + +include $(SPDK_ROOT_DIR)/mk/spdk.deps.mk diff --git a/test/lib/scsi/scsi_bdev/scsi_bdev_ut.c b/test/lib/scsi/scsi_bdev/scsi_bdev_ut.c new file mode 100644 index 0000000000..74d04399ed --- /dev/null +++ b/test/lib/scsi/scsi_bdev/scsi_bdev_ut.c @@ -0,0 +1,452 @@ +/*- + * BSD LICENSE + * + * 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 +#include +#include + +#include "scsi_bdev.c" + +#include "CUnit/Basic.h" + +SPDK_LOG_REGISTER_TRACE_FLAG("scsi", SPDK_TRACE_SCSI) + +struct spdk_scsi_globals g_spdk_scsi; + +void +spdk_scsi_lun_clear_all(struct spdk_scsi_lun *lun) +{ +} + +void +spdk_scsi_lun_complete_task(struct spdk_scsi_lun *lun, struct spdk_scsi_task *task) +{ +} + +void +spdk_scsi_task_set_check_condition(struct spdk_scsi_task *task, int sk, int asc, int ascq) +{ + spdk_scsi_task_build_sense_data(task, sk, asc, ascq); + task->status = SPDK_SCSI_STATUS_CHECK_CONDITION; +} + +void +spdk_scsi_task_alloc_data(struct spdk_scsi_task *task, uint32_t alloc_len, + uint8_t **data) +{ + if (alloc_len < 4096) { + alloc_len = 4096; + } + + task->alloc_len = alloc_len; + *data = task->rbuf; +} + +int +spdk_scsi_task_build_sense_data(struct spdk_scsi_task *task, int sk, int asc, int ascq) +{ + uint8_t *data; + uint8_t *cp; + int resp_code; + int hlen = 0, len, plen; + int total; + + data = task->sense_data; + resp_code = 0x70; /* Current + Fixed format */ + + /* SenseLength */ + memset(data, 0, 2); + hlen = 2; + + /* Sense Data */ + cp = &data[hlen]; + + /* VALID(7) RESPONSE CODE(6-0) */ + cp[0] = 0x80 | resp_code; + /* Obsolete */ + cp[1] = 0; + /* FILEMARK(7) EOM(6) ILI(5) SENSE KEY(3-0) */ + cp[2] = sk & 0xf; + /* INFORMATION */ + memset(&cp[3], 0, 4); + /* ADDITIONAL SENSE LENGTH */ + cp[7] = 0; + len = 8; + + /* COMMAND-SPECIFIC INFORMATION */ + memset(&cp[8], 0, 4); + /* ADDITIONAL SENSE CODE */ + cp[12] = asc; + /* ADDITIONAL SENSE CODE QUALIFIER */ + cp[13] = ascq; + /* FIELD REPLACEABLE UNIT CODE */ + cp[14] = 0; + + /* SKSV(7) SENSE KEY SPECIFIC(6-0,7-0,7-0) */ + cp[15] = 0; + cp[16] = 0; + cp[17] = 0; + /* Additional sense bytes */ + plen = 18 - len; + + /* ADDITIONAL SENSE LENGTH */ + cp[7] = plen; + + total = hlen + len + plen; + + /* SenseLength */ + to_be16(data, total - 2); + task->sense_data_len = total; + + return total; +} + +struct spdk_bdev_io * +spdk_bdev_read(struct spdk_bdev *bdev, + void *buf, uint64_t nbytes, uint64_t offset, + spdk_bdev_io_completion_cb cb, void *cb_arg) +{ + return NULL; +} + +struct spdk_bdev_io * +spdk_bdev_writev(struct spdk_bdev *bdev, + struct iovec *iov, int iovcnt, + uint64_t len, uint64_t offset, + spdk_bdev_io_completion_cb cb, void *cb_arg) +{ + return NULL; +} + +struct spdk_bdev_io * +spdk_bdev_unmap(struct spdk_bdev *bdev, + struct spdk_scsi_unmap_bdesc *unmap_d, + uint16_t bdesc_count, + spdk_bdev_io_completion_cb cb, void *cb_arg) +{ + return NULL; +} + +int +spdk_bdev_reset(struct spdk_bdev *bdev, int reset_type, + spdk_bdev_io_completion_cb cb, void *cb_arg) +{ + return 0; +} + +struct spdk_bdev_io * +spdk_bdev_flush(struct spdk_bdev *bdev, + uint64_t offset, uint64_t length, + spdk_bdev_io_completion_cb cb, void *cb_arg) +{ + return NULL; +} + +/* + * This test specifically tests a mode select 6 command from the + * Windows SCSI compliance test that caused SPDK to crash. + */ +static void +mode_select_6_test(void) +{ + struct spdk_bdev bdev; + struct spdk_scsi_task task; + struct spdk_scsi_lun lun; + struct spdk_scsi_dev dev; + char cdb[16]; + char data[24]; + int rc; + + cdb[0] = 0x15; + cdb[1] = 0x11; + cdb[2] = 0x00; + cdb[3] = 0x00; + cdb[4] = 0x18; + cdb[5] = 0x00; + task.cdb = cdb; + + snprintf(&dev.name[0], sizeof(dev.name), "spdk_iscsi_translation_test"); + lun.dev = &dev; + task.lun = &lun; + + memset(data, 0, sizeof(data)); + data[4] = 0x08; + data[5] = 0x02; + task.iobuf = data; + + rc = spdk_bdev_scsi_execute(&bdev, &task); + + CU_ASSERT_EQUAL(rc, 0); +} + +/* + * This test specifically tests a mode select 6 command which + * contains no mode pages. + */ +static void +mode_select_6_test2(void) +{ + struct spdk_bdev bdev; + struct spdk_scsi_task task; + struct spdk_scsi_lun lun; + struct spdk_scsi_dev dev; + char cdb[16]; + int rc; + + cdb[0] = 0x15; + cdb[1] = 0x00; + cdb[2] = 0x00; + cdb[3] = 0x00; + cdb[4] = 0x00; + cdb[5] = 0x00; + task.cdb = cdb; + + snprintf(&dev.name[0], sizeof(dev.name), "spdk_iscsi_translation_test"); + lun.dev = &dev; + task.lun = &lun; + + task.iobuf = NULL; + + rc = spdk_bdev_scsi_execute(&bdev, &task); + + CU_ASSERT_EQUAL(rc, 0); +} + +/* + * This test specifically tests a mode sense 6 command which + * return all subpage 00h mode pages. + */ +static void +mode_sense_6_test(void) +{ + struct spdk_bdev bdev; + struct spdk_scsi_task task; + struct spdk_scsi_lun lun; + struct spdk_scsi_dev dev; + char cdb[12]; + unsigned char data[4096]; + int rc = 0; + unsigned char mode_data_len = 0; + unsigned char medium_type = 0; + unsigned char dev_specific_param = 0; + unsigned char blk_descriptor_len = 0; + + memset(&bdev, 0 , sizeof(struct spdk_bdev)); + memset(cdb, 0, sizeof(cdb)); + + cdb[0] = 0x1A; + cdb[2] = 0x3F; + cdb[4] = 0xFF; + task.cdb = cdb; + + snprintf(&dev.name[0], sizeof(dev.name), "spdk_iscsi_translation_test"); + lun.dev = &dev; + task.lun = &lun; + + task.rbuf = data; + + rc = spdk_bdev_scsi_execute(&bdev, &task); + mode_data_len = data[0]; + medium_type = data[1]; + dev_specific_param = data[2]; + blk_descriptor_len = data[3]; + + CU_ASSERT(mode_data_len >= 11); + CU_ASSERT_EQUAL(medium_type, 0); + CU_ASSERT_EQUAL(dev_specific_param, 0); + CU_ASSERT_EQUAL(blk_descriptor_len, 8); + CU_ASSERT_EQUAL(rc, 0); +} + +/* + * This test specifically tests a mode sense 10 command which + * return all subpage 00h mode pages. + */ +static void +mode_sense_10_test(void) +{ + struct spdk_bdev bdev; + struct spdk_scsi_task task; + struct spdk_scsi_lun lun; + struct spdk_scsi_dev dev; + char cdb[12]; + unsigned char data[4096]; + int rc; + unsigned short mode_data_len = 0; + unsigned char medium_type = 0; + unsigned char dev_specific_param = 0; + unsigned short blk_descriptor_len = 0; + + memset(&bdev, 0 , sizeof(struct spdk_bdev)); + memset(cdb, 0, sizeof(cdb)); + cdb[0] = 0x5A; + cdb[2] = 0x3F; + cdb[8] = 0xFF; + task.cdb = cdb; + + snprintf(&dev.name[0], sizeof(dev.name), "spdk_iscsi_translation_test"); + lun.dev = &dev; + task.lun = &lun; + + task.rbuf = data; + + rc = spdk_bdev_scsi_execute(&bdev, &task); + mode_data_len = ((data[0] << 8) + data[1]); + medium_type = data[2]; + dev_specific_param = data[3]; + blk_descriptor_len = ((data[6] << 8) + data[7]); + + CU_ASSERT(mode_data_len >= 14); + CU_ASSERT_EQUAL(medium_type, 0); + CU_ASSERT_EQUAL(dev_specific_param, 0); + CU_ASSERT_EQUAL(blk_descriptor_len, 8); + CU_ASSERT_EQUAL(rc, 0); +} + +/* + * This test specifically tests a scsi inquiry command from the + * Windows SCSI compliance test that failed to return the + * expected SCSI error sense code. + */ +static void +inquiry_evpd_test(void) +{ + struct spdk_bdev bdev; + struct spdk_scsi_task task; + struct spdk_scsi_lun lun; + struct spdk_scsi_dev dev; + char cdb[6]; + char data[4096]; + int rc; + + cdb[0] = 0x12; + cdb[1] = 0x00; // EVPD = 0 + cdb[2] = 0xff; // PageCode non-zero + cdb[3] = 0x00; + cdb[4] = 0xff; + cdb[5] = 0x00; + task.cdb = cdb; + + snprintf(&dev.name[0], sizeof(dev.name), "spdk_iscsi_translation_test"); + lun.dev = &dev; + task.lun = &lun; + + memset(data, 0, 4096); + task.rbuf = data; + + rc = spdk_bdev_scsi_execute(&bdev, &task); + + CU_ASSERT_EQUAL(task.status, SPDK_SCSI_STATUS_CHECK_CONDITION); + CU_ASSERT_EQUAL(task.sense_data[4], (SPDK_SCSI_SENSE_ILLEGAL_REQUEST & 0xf)); + CU_ASSERT_EQUAL(task.sense_data[14], 0x24); + CU_ASSERT_EQUAL(task.sense_data[15], 0x0); + CU_ASSERT_EQUAL(rc, 0); +} + +/* + * This test is to verify specific return data for a standard scsi inquiry + * command: Version + */ +static void +inquiry_standard_test(void) +{ + struct spdk_bdev bdev; + struct spdk_scsi_task task; + struct spdk_scsi_lun lun; + struct spdk_scsi_dev dev; + struct spdk_bdev_fn_table fn_table; + char cdb[6]; + /* expects a 4K internal data buffer */ + char data[4096]; + struct spdk_scsi_cdb_inquiry_data *inq_data; + int rc; + + bdev.fn_table = &fn_table; + + cdb[0] = 0x12; + cdb[1] = 0x00; // EVPD = 0 + cdb[2] = 0x00; // PageCode zero - requesting standard inquiry + cdb[3] = 0x00; + cdb[4] = 0xff; // Indicate data size used by conformance test + cdb[5] = 0x00; + task.cdb = cdb; + + snprintf(&dev.name[0], sizeof(dev.name), "spdk_iscsi_translation_test"); + lun.dev = &dev; + task.lun = &lun; + + memset(data, 0, 4096); + task.rbuf = data; + + rc = spdk_bdev_scsi_execute(&bdev, &task); + + inq_data = (struct spdk_scsi_cdb_inquiry_data *)&data[0]; + + CU_ASSERT_EQUAL(inq_data->version, SPDK_SPC_VERSION_SPC3); + CU_ASSERT_EQUAL(rc, 0); +} + +int +main(int argc, char **argv) +{ + CU_pSuite suite = NULL; + unsigned int num_failures; + + if (CU_initialize_registry() != CUE_SUCCESS) { + return CU_get_error(); + } + + suite = CU_add_suite("translation_suite", NULL, NULL); + if (suite == NULL) { + CU_cleanup_registry(); + return CU_get_error(); + } + + if ( + CU_add_test(suite, "mode select 6 test", mode_select_6_test) == NULL + || CU_add_test(suite, "mode select 6 test2", mode_select_6_test2) == NULL + || CU_add_test(suite, "mode sense 6 test", mode_sense_6_test) == NULL + || CU_add_test(suite, "mode sense 10 test", mode_sense_10_test) == NULL + || CU_add_test(suite, "inquiry evpd test", inquiry_evpd_test) == NULL + || CU_add_test(suite, "inquiry standard test", inquiry_standard_test) == NULL + ) { + CU_cleanup_registry(); + return CU_get_error(); + } + + CU_basic_set_mode(CU_BRM_VERBOSE); + CU_basic_run_tests(); + num_failures = CU_get_number_of_failures(); + CU_cleanup_registry(); + return num_failures; +} diff --git a/unittest.sh b/unittest.sh index 5b202c7414..78632d6f12 100755 --- a/unittest.sh +++ b/unittest.sh @@ -3,6 +3,7 @@ set -xe make config.h CONFIG_WERROR=y +make -C lib/cunit CONFIG_WERROR=y make -C test/lib/nvme/unit CONFIG_WERROR=y @@ -37,3 +38,9 @@ make -C test/lib/nvmf CONFIG_WERROR=y test/lib/nvmf/request/request_ut test/lib/nvmf/session/session_ut test/lib/nvmf/subsystem/subsystem_ut + +make -C test/lib/scsi CONFIG_WERROR=y + +test/lib/scsi/dev/dev_ut +test/lib/scsi/lun/lun_ut +test/lib/scsi/scsi_bdev/scsi_bdev_ut