scsi: import SCSI/blockdev translation layer

Change-Id: Ie96943f40ea8be4156d55bc5eeacc567743cf9d6
Signed-off-by: Daniel Verkamp <daniel.verkamp@intel.com>
This commit is contained in:
Daniel Verkamp 2016-07-27 09:32:00 -07:00
parent 8779605323
commit bd4ac74eaf
26 changed files with 5545 additions and 2 deletions

View File

@ -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

209
include/spdk/scsi.h Normal file
View File

@ -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 <stdbool.h>
#include <stdint.h>
#include <sys/uio.h>
#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 */

View File

@ -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)

41
lib/scsi/Makefile Normal file
View File

@ -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

258
lib/scsi/dev.c Normal file
View File

@ -0,0 +1,258 @@
/*-
* BSD LICENSE
*
* Copyright (C) 2008-2012 Daisuke Aoyama <aoyama@peach.ne.jp>.
* Copyright (c) Intel Corporation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "scsi_internal.h"
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);
}
}

392
lib/scsi/lun.c Normal file
View File

@ -0,0 +1,392 @@
/*-
* BSD LICENSE
*
* Copyright (C) 2008-2012 Daisuke Aoyama <aoyama@peach.ne.jp>.
* Copyright (c) Intel Corporation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "scsi_internal.h"
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);
}

120
lib/scsi/lun_db.c Normal file
View File

@ -0,0 +1,120 @@
/*-
* BSD LICENSE
*
* Copyright (C) 2008-2012 Daisuke Aoyama <aoyama@peach.ne.jp>.
* Copyright (c) Intel Corporation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "scsi_internal.h"
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;
}
}

50
lib/scsi/port.c Normal file
View File

@ -0,0 +1,50 @@
/*-
* BSD LICENSE
*
* Copyright (C) 2008-2012 Daisuke Aoyama <aoyama@peach.ne.jp>.
* Copyright (c) Intel Corporation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "scsi_internal.h"
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;
}

149
lib/scsi/scsi.c Normal file
View File

@ -0,0 +1,149 @@
/*-
* BSD LICENSE
*
* Copyright (C) 2008-2012 Daisuke Aoyama <aoyama@peach.ne.jp>.
* Copyright (c) Intel Corporation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "scsi_internal.h"
#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)

1851
lib/scsi/scsi_bdev.c Normal file

File diff suppressed because it is too large Load Diff

230
lib/scsi/scsi_internal.h Normal file
View File

@ -0,0 +1,230 @@
/*-
* BSD LICENSE
*
* Copyright (C) 2008-2012 Daisuke Aoyama <aoyama@peach.ne.jp>.
* Copyright (c) Intel Corporation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SPDK_SCSI_INTERNAL_H
#define SPDK_SCSI_INTERNAL_H
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <inttypes.h>
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/uio.h>
#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 */

174
lib/scsi/scsi_rpc.c Normal file
View File

@ -0,0 +1,174 @@
/*-
* BSD LICENSE
*
* Copyright (C) 2008-2012 Daisuke Aoyama <aoyama@peach.ne.jp>.
* Copyright (c) Intel Corporation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "scsi_internal.h"
#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)

194
lib/scsi/task.c Normal file
View File

@ -0,0 +1,194 @@
/*-
* BSD LICENSE
*
* Copyright (C) 2008-2012 Daisuke Aoyama <aoyama@peach.ne.jp>.
* Copyright (c) Intel Corporation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "scsi_internal.h"
#include <rte_config.h>
#include <rte_debug.h>
#include <rte_malloc.h>
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;
}

View File

@ -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)

44
test/lib/scsi/Makefile Normal file
View File

@ -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

1
test/lib/scsi/dev/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
dev_ut

View File

@ -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

494
test/lib/scsi/dev/dev_ut.c Normal file
View File

@ -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 <stdint.h>
#include <stdlib.h>
#include <string.h>
#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;
}

1
test/lib/scsi/lun/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
lun_ut

View File

@ -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

693
test/lib/scsi/lun/lun_ut.c Normal file
View File

@ -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 <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#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;
}

15
test/lib/scsi/scsi.sh Executable file
View File

@ -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

1
test/lib/scsi/scsi_bdev/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
scsi_bdev_ut

View File

@ -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

View File

@ -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 <stdint.h>
#include <stdlib.h>
#include <string.h>
#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;
}

View File

@ -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