summaryrefslogtreecommitdiffstats
path: root/src/spdk/lib/scsi
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:45:59 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:45:59 +0000
commit19fcec84d8d7d21e796c7624e521b60d28ee21ed (patch)
tree42d26aa27d1e3f7c0b8bd3fd14e7d7082f5008dc /src/spdk/lib/scsi
parentInitial commit. (diff)
downloadceph-upstream.tar.xz
ceph-upstream.zip
Adding upstream version 16.2.11+ds.upstream/16.2.11+dsupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/spdk/lib/scsi')
-rw-r--r--src/spdk/lib/scsi/Makefile45
-rw-r--r--src/spdk/lib/scsi/dev.c436
-rw-r--r--src/spdk/lib/scsi/lun.c623
-rw-r--r--src/spdk/lib/scsi/port.c134
-rw-r--r--src/spdk/lib/scsi/scsi.c110
-rw-r--r--src/spdk/lib/scsi/scsi_bdev.c2067
-rw-r--r--src/spdk/lib/scsi/scsi_internal.h214
-rw-r--r--src/spdk/lib/scsi/scsi_pr.c1067
-rw-r--r--src/spdk/lib/scsi/scsi_rpc.c77
-rw-r--r--src/spdk/lib/scsi/spdk_scsi.map49
-rw-r--r--src/spdk/lib/scsi/task.c300
11 files changed, 5122 insertions, 0 deletions
diff --git a/src/spdk/lib/scsi/Makefile b/src/spdk/lib/scsi/Makefile
new file mode 100644
index 000000000..8f8a8c326
--- /dev/null
+++ b/src/spdk/lib/scsi/Makefile
@@ -0,0 +1,45 @@
+#
+# 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
+
+SO_VER := 3
+SO_MINOR := 0
+
+C_SRCS = dev.c lun.c port.c scsi.c scsi_bdev.c scsi_pr.c scsi_rpc.c task.c
+LIBNAME = scsi
+
+SPDK_MAP_FILE = $(abspath $(CURDIR)/spdk_scsi.map)
+
+include $(SPDK_ROOT_DIR)/mk/spdk.lib.mk
diff --git a/src/spdk/lib/scsi/dev.c b/src/spdk/lib/scsi/dev.c
new file mode 100644
index 000000000..6d3cfdf31
--- /dev/null
+++ b/src/spdk/lib/scsi/dev.c
@@ -0,0 +1,436 @@
+/*-
+ * 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 *
+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)
+{
+ assert(dev->is_allocated == 1);
+ assert(dev->removed == true);
+
+ dev->is_allocated = 0;
+
+ if (dev->remove_cb) {
+ dev->remove_cb(dev->remove_ctx, 0);
+ dev->remove_cb = NULL;
+ }
+}
+
+void
+spdk_scsi_dev_destruct(struct spdk_scsi_dev *dev,
+ spdk_scsi_dev_destruct_cb_t cb_fn, void *cb_arg)
+{
+ int lun_cnt;
+ int i;
+
+ if (dev == NULL) {
+ if (cb_fn) {
+ cb_fn(cb_arg, -EINVAL);
+ }
+ return;
+ }
+
+ if (dev->removed) {
+ if (cb_fn) {
+ cb_fn(cb_arg, -EINVAL);
+ }
+ return;
+ }
+
+ dev->removed = true;
+ dev->remove_cb = cb_fn;
+ dev->remove_ctx = cb_arg;
+ lun_cnt = 0;
+
+ for (i = 0; i < SPDK_SCSI_DEV_MAX_LUN; i++) {
+ if (dev->lun[i] == NULL) {
+ continue;
+ }
+
+ /*
+ * LUN will remove itself from this dev when all outstanding IO
+ * is done. When no more LUNs, dev will be deleted.
+ */
+ scsi_lun_destruct(dev->lun[i]);
+ lun_cnt++;
+ }
+
+ if (lun_cnt == 0) {
+ free_dev(dev);
+ return;
+ }
+}
+
+static int
+scsi_dev_find_lowest_free_lun_id(struct spdk_scsi_dev *dev)
+{
+ int i;
+
+ for (i = 0; i < SPDK_SCSI_DEV_MAX_LUN; i++) {
+ if (dev->lun[i] == NULL) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+int
+spdk_scsi_dev_add_lun(struct spdk_scsi_dev *dev, const char *bdev_name, int lun_id,
+ void (*hotremove_cb)(const struct spdk_scsi_lun *, void *),
+ void *hotremove_ctx)
+{
+ struct spdk_bdev *bdev;
+ struct spdk_scsi_lun *lun;
+
+ bdev = spdk_bdev_get_by_name(bdev_name);
+ if (bdev == NULL) {
+ SPDK_ERRLOG("device %s: cannot find bdev '%s' (target %d)\n",
+ dev->name, bdev_name, lun_id);
+ return -1;
+ }
+
+ /* Search the lowest free LUN ID if LUN ID is default */
+ if (lun_id == -1) {
+ lun_id = scsi_dev_find_lowest_free_lun_id(dev);
+ if (lun_id == -1) {
+ SPDK_ERRLOG("Free LUN ID is not found\n");
+ return -1;
+ }
+ }
+
+ lun = scsi_lun_construct(bdev, hotremove_cb, hotremove_ctx);
+ if (lun == NULL) {
+ return -1;
+ }
+
+ lun->id = lun_id;
+ lun->dev = dev;
+ dev->lun[lun_id] = lun;
+ return 0;
+}
+
+void
+spdk_scsi_dev_delete_lun(struct spdk_scsi_dev *dev,
+ struct spdk_scsi_lun *lun)
+{
+ int lun_cnt = 0;
+ int i;
+
+ for (i = 0; i < SPDK_SCSI_DEV_MAX_LUN; i++) {
+ if (dev->lun[i] == lun) {
+ dev->lun[i] = NULL;
+ }
+
+ if (dev->lun[i]) {
+ lun_cnt++;
+ }
+ }
+
+ if (dev->removed == true && lun_cnt == 0) {
+ free_dev(dev);
+ }
+}
+
+struct spdk_scsi_dev *spdk_scsi_dev_construct(const char *name, const char *bdev_name_list[],
+ int *lun_id_list, int num_luns, uint8_t protocol_id,
+ void (*hotremove_cb)(const struct spdk_scsi_lun *, void *),
+ void *hotremove_ctx)
+{
+ struct spdk_scsi_dev *dev;
+ size_t name_len;
+ bool found_lun_0;
+ int i, rc;
+
+ name_len = strlen(name);
+ if (name_len > sizeof(dev->name) - 1) {
+ SPDK_ERRLOG("device %s: name longer than maximum allowed length %zu\n",
+ name, sizeof(dev->name) - 1);
+ return NULL;
+ }
+
+ if (num_luns == 0) {
+ SPDK_ERRLOG("device %s: no LUNs specified\n", name);
+ return NULL;
+ }
+
+ found_lun_0 = false;
+ for (i = 0; i < num_luns; i++) {
+ if (lun_id_list[i] == 0) {
+ found_lun_0 = true;
+ break;
+ }
+ }
+
+ if (!found_lun_0) {
+ SPDK_ERRLOG("device %s: no LUN 0 specified\n", name);
+ return NULL;
+ }
+
+ for (i = 0; i < num_luns; i++) {
+ if (bdev_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;
+ }
+
+ memcpy(dev->name, name, name_len + 1);
+
+ dev->num_ports = 0;
+ dev->protocol_id = protocol_id;
+
+ for (i = 0; i < num_luns; i++) {
+ rc = spdk_scsi_dev_add_lun(dev, bdev_name_list[i], lun_id_list[i],
+ hotremove_cb, hotremove_ctx);
+ if (rc < 0) {
+ spdk_scsi_dev_destruct(dev, NULL, NULL);
+ return NULL;
+ }
+ }
+
+ return dev;
+}
+
+void
+spdk_scsi_dev_queue_mgmt_task(struct spdk_scsi_dev *dev,
+ struct spdk_scsi_task *task)
+{
+ assert(task != NULL);
+
+ scsi_lun_execute_mgmt_task(task->lun, task);
+}
+
+void
+spdk_scsi_dev_queue_task(struct spdk_scsi_dev *dev,
+ struct spdk_scsi_task *task)
+{
+ assert(task != NULL);
+
+ scsi_lun_execute_task(task->lun, task);
+}
+
+static struct spdk_scsi_port *
+scsi_dev_find_free_port(struct spdk_scsi_dev *dev)
+{
+ int i;
+
+ for (i = 0; i < SPDK_SCSI_DEV_MAX_PORTS; i++) {
+ if (!dev->port[i].is_used) {
+ return &dev->port[i];
+ }
+ }
+
+ return NULL;
+}
+
+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 = spdk_scsi_dev_find_port_by_id(dev, id);
+ if (port != NULL) {
+ SPDK_ERRLOG("device already has port(%" PRIu64 ")\n", id);
+ return -1;
+ }
+
+ port = scsi_dev_find_free_port(dev);
+ if (port == NULL) {
+ assert(false);
+ return -1;
+ }
+
+ rc = scsi_port_construct(port, id, dev->num_ports, name);
+ if (rc != 0) {
+ return rc;
+ }
+
+ dev->num_ports++;
+ return 0;
+}
+
+int
+spdk_scsi_dev_delete_port(struct spdk_scsi_dev *dev, uint64_t id)
+{
+ struct spdk_scsi_port *port;
+
+ port = spdk_scsi_dev_find_port_by_id(dev, id);
+ if (port == NULL) {
+ SPDK_ERRLOG("device does not have specified port(%" PRIu64 ")\n", id);
+ return -1;
+ }
+
+ scsi_port_destruct(port);
+
+ 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 < SPDK_SCSI_DEV_MAX_PORTS; i++) {
+ if (!dev->port[i].is_used) {
+ continue;
+ }
+ if (dev->port[i].id == id) {
+ return &dev->port[i];
+ }
+ }
+
+ /* No matching port found. */
+ return NULL;
+}
+
+void
+spdk_scsi_dev_free_io_channels(struct spdk_scsi_dev *dev)
+{
+ int i;
+
+ for (i = 0; i < SPDK_SCSI_DEV_MAX_LUN; i++) {
+ if (dev->lun[i] == NULL) {
+ continue;
+ }
+ scsi_lun_free_io_channel(dev->lun[i]);
+ }
+}
+
+int
+spdk_scsi_dev_allocate_io_channels(struct spdk_scsi_dev *dev)
+{
+ int i, rc;
+
+ for (i = 0; i < SPDK_SCSI_DEV_MAX_LUN; i++) {
+ if (dev->lun[i] == NULL) {
+ continue;
+ }
+ rc = scsi_lun_allocate_io_channel(dev->lun[i]);
+ if (rc < 0) {
+ spdk_scsi_dev_free_io_channels(dev);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+const char *
+spdk_scsi_dev_get_name(const struct spdk_scsi_dev *dev)
+{
+ return dev->name;
+}
+
+int
+spdk_scsi_dev_get_id(const struct spdk_scsi_dev *dev)
+{
+ return dev->id;
+}
+
+struct spdk_scsi_lun *
+spdk_scsi_dev_get_lun(struct spdk_scsi_dev *dev, int lun_id)
+{
+ struct spdk_scsi_lun *lun;
+
+ if (lun_id < 0 || lun_id >= SPDK_SCSI_DEV_MAX_LUN) {
+ return NULL;
+ }
+
+ lun = dev->lun[lun_id];
+
+ if (lun != NULL && !spdk_scsi_lun_is_removing(lun)) {
+ return lun;
+ } else {
+ return NULL;
+ }
+}
+
+bool
+spdk_scsi_dev_has_pending_tasks(const struct spdk_scsi_dev *dev,
+ const struct spdk_scsi_port *initiator_port)
+{
+ int i;
+
+ for (i = 0; i < SPDK_SCSI_DEV_MAX_LUN; ++i) {
+ if (dev->lun[i] &&
+ (scsi_lun_has_pending_tasks(dev->lun[i], initiator_port) ||
+ scsi_lun_has_pending_mgmt_tasks(dev->lun[i], initiator_port))) {
+ return true;
+ }
+ }
+
+ return false;
+}
diff --git a/src/spdk/lib/scsi/lun.c b/src/spdk/lib/scsi/lun.c
new file mode 100644
index 000000000..262137d80
--- /dev/null
+++ b/src/spdk/lib/scsi/lun.c
@@ -0,0 +1,623 @@
+/*-
+ * 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/endian.h"
+#include "spdk/env.h"
+#include "spdk/thread.h"
+#include "spdk/util.h"
+#include "spdk/likely.h"
+
+static void scsi_lun_execute_tasks(struct spdk_scsi_lun *lun);
+static void _scsi_lun_execute_mgmt_task(struct spdk_scsi_lun *lun);
+
+void
+scsi_lun_complete_task(struct spdk_scsi_lun *lun, struct spdk_scsi_task *task)
+{
+ if (lun) {
+ TAILQ_REMOVE(&lun->tasks, task, scsi_link);
+ spdk_trace_record(TRACE_SCSI_TASK_DONE, lun->dev->id, 0, (uintptr_t)task, 0);
+ }
+ task->cpl_fn(task);
+}
+
+static void
+scsi_lun_complete_mgmt_task(struct spdk_scsi_lun *lun, struct spdk_scsi_task *task)
+{
+ TAILQ_REMOVE(&lun->mgmt_tasks, task, scsi_link);
+
+ task->cpl_fn(task);
+
+ /* Try to execute the first pending mgmt task if it exists. */
+ _scsi_lun_execute_mgmt_task(lun);
+}
+
+static bool
+_scsi_lun_has_pending_mgmt_tasks(const struct spdk_scsi_lun *lun)
+{
+ return !TAILQ_EMPTY(&lun->pending_mgmt_tasks);
+}
+
+static bool
+scsi_lun_has_outstanding_mgmt_tasks(const struct spdk_scsi_lun *lun)
+{
+ return !TAILQ_EMPTY(&lun->mgmt_tasks);
+}
+
+static bool
+_scsi_lun_has_pending_tasks(const struct spdk_scsi_lun *lun)
+{
+ return !TAILQ_EMPTY(&lun->pending_tasks);
+}
+
+static bool
+scsi_lun_has_outstanding_tasks(const struct spdk_scsi_lun *lun)
+{
+ return !TAILQ_EMPTY(&lun->tasks);
+}
+
+/* Reset task have to wait until all prior outstanding tasks complete. */
+static int
+scsi_lun_reset_check_outstanding_tasks(void *arg)
+{
+ struct spdk_scsi_task *task = (struct spdk_scsi_task *)arg;
+ struct spdk_scsi_lun *lun = task->lun;
+
+ if (scsi_lun_has_outstanding_tasks(lun)) {
+ return SPDK_POLLER_BUSY;
+ }
+ spdk_poller_unregister(&lun->reset_poller);
+
+ scsi_lun_complete_mgmt_task(lun, task);
+ return SPDK_POLLER_BUSY;
+}
+
+void
+scsi_lun_complete_reset_task(struct spdk_scsi_lun *lun, struct spdk_scsi_task *task)
+{
+ if (task->status == SPDK_SCSI_STATUS_GOOD) {
+ if (scsi_lun_has_outstanding_tasks(lun)) {
+ lun->reset_poller =
+ SPDK_POLLER_REGISTER(scsi_lun_reset_check_outstanding_tasks,
+ task, 10);
+ return;
+ }
+ }
+
+ scsi_lun_complete_mgmt_task(lun, task);
+}
+
+static void
+scsi_lun_append_mgmt_task(struct spdk_scsi_lun *lun,
+ struct spdk_scsi_task *task)
+{
+ TAILQ_INSERT_TAIL(&lun->pending_mgmt_tasks, task, scsi_link);
+}
+
+static void
+_scsi_lun_execute_mgmt_task(struct spdk_scsi_lun *lun)
+{
+ struct spdk_scsi_task *task;
+
+ if (!TAILQ_EMPTY(&lun->mgmt_tasks)) {
+ return;
+ }
+
+ task = TAILQ_FIRST(&lun->pending_mgmt_tasks);
+ if (spdk_likely(task == NULL)) {
+ /* Try to execute all pending tasks */
+ scsi_lun_execute_tasks(lun);
+ return;
+ }
+ TAILQ_REMOVE(&lun->pending_mgmt_tasks, task, scsi_link);
+
+ TAILQ_INSERT_TAIL(&lun->mgmt_tasks, task, scsi_link);
+
+ if (lun->removed) {
+ task->response = SPDK_SCSI_TASK_MGMT_RESP_INVALID_LUN;
+ scsi_lun_complete_mgmt_task(lun, task);
+ return;
+ }
+
+ switch (task->function) {
+ case SPDK_SCSI_TASK_FUNC_ABORT_TASK:
+ task->response = SPDK_SCSI_TASK_MGMT_RESP_REJECT_FUNC_NOT_SUPPORTED;
+ SPDK_ERRLOG("ABORT_TASK failed\n");
+ break;
+
+ case SPDK_SCSI_TASK_FUNC_ABORT_TASK_SET:
+ task->response = SPDK_SCSI_TASK_MGMT_RESP_REJECT_FUNC_NOT_SUPPORTED;
+ SPDK_ERRLOG("ABORT_TASK_SET failed\n");
+ break;
+
+ case SPDK_SCSI_TASK_FUNC_LUN_RESET:
+ bdev_scsi_reset(task);
+ return;
+
+ 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;
+ break;
+ }
+
+ scsi_lun_complete_mgmt_task(lun, task);
+}
+
+void
+scsi_lun_execute_mgmt_task(struct spdk_scsi_lun *lun,
+ struct spdk_scsi_task *task)
+{
+ scsi_lun_append_mgmt_task(lun, task);
+ _scsi_lun_execute_mgmt_task(lun);
+}
+
+static void
+_scsi_lun_execute_task(struct spdk_scsi_lun *lun, struct spdk_scsi_task *task)
+{
+ int rc;
+
+ task->status = SPDK_SCSI_STATUS_GOOD;
+ spdk_trace_record(TRACE_SCSI_TASK_START, lun->dev->id, task->length, (uintptr_t)task, 0);
+ TAILQ_INSERT_TAIL(&lun->tasks, task, scsi_link);
+ if (!lun->removed) {
+ /* Check the command is allowed or not when reservation is exist */
+ if (spdk_unlikely(lun->reservation.flags & SCSI_SPC2_RESERVE)) {
+ rc = scsi2_reserve_check(task);
+ } else {
+ rc = scsi_pr_check(task);
+ }
+ if (spdk_unlikely(rc < 0)) {
+ /* Reservation Conflict */
+ rc = SPDK_SCSI_TASK_COMPLETE;
+ } else {
+ rc = bdev_scsi_execute(task);
+ }
+ } else {
+ spdk_scsi_task_process_abort(task);
+ rc = SPDK_SCSI_TASK_COMPLETE;
+ }
+
+ switch (rc) {
+ case SPDK_SCSI_TASK_PENDING:
+ break;
+
+ case SPDK_SCSI_TASK_COMPLETE:
+ scsi_lun_complete_task(lun, task);
+ break;
+
+ default:
+ abort();
+ }
+}
+
+static void
+scsi_lun_append_task(struct spdk_scsi_lun *lun, struct spdk_scsi_task *task)
+{
+ TAILQ_INSERT_TAIL(&lun->pending_tasks, task, scsi_link);
+}
+
+static void
+scsi_lun_execute_tasks(struct spdk_scsi_lun *lun)
+{
+ struct spdk_scsi_task *task, *task_tmp;
+
+ TAILQ_FOREACH_SAFE(task, &lun->pending_tasks, scsi_link, task_tmp) {
+ TAILQ_REMOVE(&lun->pending_tasks, task, scsi_link);
+ _scsi_lun_execute_task(lun, task);
+ }
+}
+
+void
+scsi_lun_execute_task(struct spdk_scsi_lun *lun, struct spdk_scsi_task *task)
+{
+ if (spdk_unlikely(_scsi_lun_has_pending_mgmt_tasks(lun))) {
+ /* Add the IO task to pending list and wait for completion of
+ * existing mgmt tasks.
+ */
+ scsi_lun_append_task(lun, task);
+ } else if (spdk_unlikely(_scsi_lun_has_pending_tasks(lun))) {
+ /* If there is any pending IO task, append the IO task to the
+ * tail of the pending list, and then execute all pending IO tasks
+ * from the head to submit IO tasks in order.
+ */
+ scsi_lun_append_task(lun, task);
+ scsi_lun_execute_tasks(lun);
+ } else {
+ /* Execute the IO task directly. */
+ _scsi_lun_execute_task(lun, task);
+ }
+}
+
+static void
+_scsi_lun_remove(void *arg)
+{
+ struct spdk_scsi_lun *lun = (struct spdk_scsi_lun *)arg;
+
+ spdk_bdev_close(lun->bdev_desc);
+ spdk_scsi_dev_delete_lun(lun->dev, lun);
+ free(lun);
+}
+
+static void
+scsi_lun_remove(struct spdk_scsi_lun *lun)
+{
+ struct spdk_scsi_pr_registrant *reg, *tmp;
+ struct spdk_thread *thread;
+
+ TAILQ_FOREACH_SAFE(reg, &lun->reg_head, link, tmp) {
+ TAILQ_REMOVE(&lun->reg_head, reg, link);
+ free(reg);
+ }
+
+ thread = spdk_get_thread();
+ if (thread != lun->thread) {
+ spdk_thread_send_msg(lun->thread, _scsi_lun_remove, lun);
+ } else {
+ _scsi_lun_remove(lun);
+ }
+}
+
+static int
+scsi_lun_check_io_channel(void *arg)
+{
+ struct spdk_scsi_lun *lun = (struct spdk_scsi_lun *)arg;
+
+ if (lun->io_channel) {
+ return SPDK_POLLER_BUSY;
+ }
+ spdk_poller_unregister(&lun->hotremove_poller);
+
+ scsi_lun_remove(lun);
+ return SPDK_POLLER_BUSY;
+}
+
+static void
+scsi_lun_notify_hot_remove(struct spdk_scsi_lun *lun)
+{
+ struct spdk_scsi_lun_desc *desc, *tmp;
+
+ if (lun->hotremove_cb) {
+ lun->hotremove_cb(lun, lun->hotremove_ctx);
+ }
+
+ TAILQ_FOREACH_SAFE(desc, &lun->open_descs, link, tmp) {
+ if (desc->hotremove_cb) {
+ desc->hotremove_cb(lun, desc->hotremove_ctx);
+ } else {
+ spdk_scsi_lun_close(desc);
+ }
+ }
+
+ if (lun->io_channel) {
+ lun->hotremove_poller = SPDK_POLLER_REGISTER(scsi_lun_check_io_channel,
+ lun, 10);
+ } else {
+ scsi_lun_remove(lun);
+ }
+}
+
+static int
+scsi_lun_check_outstanding_tasks(void *arg)
+{
+ struct spdk_scsi_lun *lun = (struct spdk_scsi_lun *)arg;
+
+ if (scsi_lun_has_outstanding_tasks(lun) ||
+ scsi_lun_has_outstanding_mgmt_tasks(lun)) {
+ return SPDK_POLLER_BUSY;
+ }
+ spdk_poller_unregister(&lun->hotremove_poller);
+
+ scsi_lun_notify_hot_remove(lun);
+ return SPDK_POLLER_BUSY;
+}
+
+static void
+_scsi_lun_hot_remove(void *arg1)
+{
+ struct spdk_scsi_lun *lun = arg1;
+
+ /* If lun->removed is set, no new task can be submitted to the LUN.
+ * Execute previously queued tasks, which will be immediately aborted.
+ */
+ scsi_lun_execute_tasks(lun);
+
+ /* Then we only need to wait for all outstanding tasks to be completed
+ * before notifying the upper layer about the removal.
+ */
+ if (scsi_lun_has_outstanding_tasks(lun) ||
+ scsi_lun_has_outstanding_mgmt_tasks(lun)) {
+ lun->hotremove_poller = SPDK_POLLER_REGISTER(scsi_lun_check_outstanding_tasks,
+ lun, 10);
+ } else {
+ scsi_lun_notify_hot_remove(lun);
+ }
+}
+
+static void
+scsi_lun_hot_remove(void *remove_ctx)
+{
+ struct spdk_scsi_lun *lun = (struct spdk_scsi_lun *)remove_ctx;
+ struct spdk_thread *thread;
+
+ if (lun->removed) {
+ return;
+ }
+
+ lun->removed = true;
+ if (lun->io_channel == NULL) {
+ _scsi_lun_hot_remove(lun);
+ return;
+ }
+
+ thread = spdk_io_channel_get_thread(lun->io_channel);
+ if (thread != spdk_get_thread()) {
+ spdk_thread_send_msg(thread, _scsi_lun_hot_remove, lun);
+ } else {
+ _scsi_lun_hot_remove(lun);
+ }
+}
+
+/**
+ * \brief Constructs a new spdk_scsi_lun object based on the provided parameters.
+ *
+ * \param bdev bdev associated with this LUN
+ *
+ * \return NULL if bdev == NULL
+ * \return pointer to the new spdk_scsi_lun object otherwise
+ */
+struct spdk_scsi_lun *scsi_lun_construct(struct spdk_bdev *bdev,
+ void (*hotremove_cb)(const struct spdk_scsi_lun *, void *),
+ void *hotremove_ctx)
+{
+ struct spdk_scsi_lun *lun;
+ int rc;
+
+ if (bdev == NULL) {
+ SPDK_ERRLOG("bdev must be non-NULL\n");
+ return NULL;
+ }
+
+ lun = calloc(1, sizeof(*lun));
+ if (lun == NULL) {
+ SPDK_ERRLOG("could not allocate lun\n");
+ return NULL;
+ }
+
+ rc = spdk_bdev_open(bdev, true, scsi_lun_hot_remove, lun, &lun->bdev_desc);
+
+ if (rc != 0) {
+ SPDK_ERRLOG("bdev %s cannot be opened, error=%d\n", spdk_bdev_get_name(bdev), rc);
+ free(lun);
+ return NULL;
+ }
+
+ lun->thread = spdk_get_thread();
+
+ TAILQ_INIT(&lun->tasks);
+ TAILQ_INIT(&lun->pending_tasks);
+ TAILQ_INIT(&lun->mgmt_tasks);
+ TAILQ_INIT(&lun->pending_mgmt_tasks);
+
+ lun->bdev = bdev;
+ lun->io_channel = NULL;
+ lun->hotremove_cb = hotremove_cb;
+ lun->hotremove_ctx = hotremove_ctx;
+ TAILQ_INIT(&lun->open_descs);
+ TAILQ_INIT(&lun->reg_head);
+
+ return lun;
+}
+
+void
+scsi_lun_destruct(struct spdk_scsi_lun *lun)
+{
+ scsi_lun_hot_remove(lun);
+}
+
+int
+spdk_scsi_lun_open(struct spdk_scsi_lun *lun, spdk_scsi_lun_remove_cb_t hotremove_cb,
+ void *hotremove_ctx, struct spdk_scsi_lun_desc **_desc)
+{
+ struct spdk_scsi_lun_desc *desc;
+
+ desc = calloc(1, sizeof(*desc));
+ if (desc == NULL) {
+ SPDK_ERRLOG("calloc() failed for LUN descriptor.\n");
+ return -ENOMEM;
+ }
+
+ TAILQ_INSERT_TAIL(&lun->open_descs, desc, link);
+
+ desc->lun = lun;
+ desc->hotremove_cb = hotremove_cb;
+ desc->hotremove_ctx = hotremove_ctx;
+ *_desc = desc;
+
+ return 0;
+}
+
+void
+spdk_scsi_lun_close(struct spdk_scsi_lun_desc *desc)
+{
+ struct spdk_scsi_lun *lun = desc->lun;
+
+ TAILQ_REMOVE(&lun->open_descs, desc, link);
+ free(desc);
+
+ assert(!TAILQ_EMPTY(&lun->open_descs) || lun->io_channel == NULL);
+}
+
+int
+scsi_lun_allocate_io_channel(struct spdk_scsi_lun *lun)
+{
+ if (lun->io_channel != NULL) {
+ if (spdk_get_thread() == spdk_io_channel_get_thread(lun->io_channel)) {
+ lun->ref++;
+ return 0;
+ }
+ SPDK_ERRLOG("io_channel already allocated for lun %s\n",
+ spdk_bdev_get_name(lun->bdev));
+ return -1;
+ }
+
+ lun->io_channel = spdk_bdev_get_io_channel(lun->bdev_desc);
+ if (lun->io_channel == NULL) {
+ return -1;
+ }
+ lun->ref = 1;
+ return 0;
+}
+
+void
+scsi_lun_free_io_channel(struct spdk_scsi_lun *lun)
+{
+ if (lun->io_channel == NULL) {
+ return;
+ }
+
+ if (spdk_get_thread() != spdk_io_channel_get_thread(lun->io_channel)) {
+ SPDK_ERRLOG("io_channel was freed by different thread\n");
+ return;
+ }
+
+ lun->ref--;
+ if (lun->ref == 0) {
+ spdk_put_io_channel(lun->io_channel);
+ lun->io_channel = NULL;
+ }
+}
+
+int
+spdk_scsi_lun_allocate_io_channel(struct spdk_scsi_lun_desc *desc)
+{
+ struct spdk_scsi_lun *lun = desc->lun;
+
+ return scsi_lun_allocate_io_channel(lun);
+}
+
+void
+spdk_scsi_lun_free_io_channel(struct spdk_scsi_lun_desc *desc)
+{
+ struct spdk_scsi_lun *lun = desc->lun;
+
+ scsi_lun_free_io_channel(lun);
+}
+
+int
+spdk_scsi_lun_get_id(const struct spdk_scsi_lun *lun)
+{
+ return lun->id;
+}
+
+const char *
+spdk_scsi_lun_get_bdev_name(const struct spdk_scsi_lun *lun)
+{
+ return spdk_bdev_get_name(lun->bdev);
+}
+
+const struct spdk_scsi_dev *
+spdk_scsi_lun_get_dev(const struct spdk_scsi_lun *lun)
+{
+ return lun->dev;
+}
+
+bool
+scsi_lun_has_pending_mgmt_tasks(const struct spdk_scsi_lun *lun,
+ const struct spdk_scsi_port *initiator_port)
+{
+ struct spdk_scsi_task *task;
+
+ if (initiator_port == NULL) {
+ return _scsi_lun_has_pending_mgmt_tasks(lun) ||
+ scsi_lun_has_outstanding_mgmt_tasks(lun);
+ }
+
+ TAILQ_FOREACH(task, &lun->pending_mgmt_tasks, scsi_link) {
+ if (task->initiator_port == initiator_port) {
+ return true;
+ }
+ }
+
+ TAILQ_FOREACH(task, &lun->mgmt_tasks, scsi_link) {
+ if (task->initiator_port == initiator_port) {
+ return true;
+ }
+ }
+
+ return false;
+}
+/* This check includes both pending and submitted (outstanding) tasks. */
+bool
+scsi_lun_has_pending_tasks(const struct spdk_scsi_lun *lun,
+ const struct spdk_scsi_port *initiator_port)
+{
+ struct spdk_scsi_task *task;
+
+ if (initiator_port == NULL) {
+ return _scsi_lun_has_pending_tasks(lun) ||
+ scsi_lun_has_outstanding_tasks(lun);
+ }
+
+ TAILQ_FOREACH(task, &lun->pending_tasks, scsi_link) {
+ if (task->initiator_port == initiator_port) {
+ return true;
+ }
+ }
+
+ TAILQ_FOREACH(task, &lun->tasks, scsi_link) {
+ if (task->initiator_port == initiator_port) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool
+spdk_scsi_lun_is_removing(const struct spdk_scsi_lun *lun)
+{
+ return lun->removed;
+}
+
+bool
+spdk_scsi_lun_get_dif_ctx(struct spdk_scsi_lun *lun, struct spdk_scsi_task *task,
+ struct spdk_dif_ctx *dif_ctx)
+{
+ return bdev_scsi_get_dif_ctx(lun->bdev, task, dif_ctx);
+}
diff --git a/src/spdk/lib/scsi/port.c b/src/spdk/lib/scsi/port.c
new file mode 100644
index 000000000..09311bac2
--- /dev/null
+++ b/src/spdk/lib/scsi/port.c
@@ -0,0 +1,134 @@
+/*-
+ * 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/endian.h"
+
+struct spdk_scsi_port *
+spdk_scsi_port_create(uint64_t id, uint16_t index, const char *name)
+{
+ struct spdk_scsi_port *port;
+
+ port = calloc(1, sizeof(struct spdk_scsi_port));
+
+ if (!port) {
+ return NULL;
+ }
+
+ if (scsi_port_construct(port, id, index, name) != 0) {
+ spdk_scsi_port_free(&port);
+ return NULL;
+ }
+
+ return port;
+}
+
+void
+spdk_scsi_port_free(struct spdk_scsi_port **pport)
+{
+ struct spdk_scsi_port *port;
+
+ if (!pport) {
+ return;
+ }
+
+ port = *pport;
+ *pport = NULL;
+ free(port);
+}
+
+int
+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->is_used = 1;
+ port->id = id;
+ port->index = index;
+ snprintf(port->name, sizeof(port->name), "%s", name);
+ return 0;
+}
+
+void
+scsi_port_destruct(struct spdk_scsi_port *port)
+{
+ memset(port, 0, sizeof(struct spdk_scsi_port));
+}
+
+const char *
+spdk_scsi_port_get_name(const struct spdk_scsi_port *port)
+{
+ return port->name;
+}
+
+/*
+ * spc3r23 7.5.4.6 iSCSI initiator port TransportID,
+ * using code format 0x01.
+ */
+void
+spdk_scsi_port_set_iscsi_transport_id(struct spdk_scsi_port *port, char *iscsi_name,
+ uint64_t isid)
+{
+ struct spdk_scsi_iscsi_transport_id *data;
+ uint32_t len;
+ char *name;
+
+ memset(port->transport_id, 0, sizeof(port->transport_id));
+ port->transport_id_len = 0;
+
+ data = (struct spdk_scsi_iscsi_transport_id *)port->transport_id;
+
+ data->protocol_id = (uint8_t)SPDK_SPC_PROTOCOL_IDENTIFIER_ISCSI;
+ data->format = 0x1;
+
+ name = data->name;
+ len = snprintf(name, SPDK_SCSI_MAX_TRANSPORT_ID_LENGTH - sizeof(*data),
+ "%s,i,0x%12.12" PRIx64, iscsi_name, isid);
+ do {
+ name[len++] = '\0';
+ } while (len & 3);
+
+ if (len < 20) {
+ SPDK_ERRLOG("The length of Transport ID should >= 20 bytes\n");
+ return;
+ }
+
+ to_be16(&data->additional_len, len);
+ port->transport_id_len = len + sizeof(*data);
+}
diff --git a/src/spdk/lib/scsi/scsi.c b/src/spdk/lib/scsi/scsi.c
new file mode 100644
index 000000000..c18192e37
--- /dev/null
+++ b/src/spdk/lib/scsi/scsi.c
@@ -0,0 +1,110 @@
+/*-
+ * 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_scsi_globals g_scsi;
+
+int
+spdk_scsi_init(void)
+{
+ int rc;
+
+ rc = pthread_mutex_init(&g_scsi.mutex, NULL);
+ if (rc != 0) {
+ SPDK_ERRLOG("mutex_init() failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+void
+spdk_scsi_fini(void)
+{
+ pthread_mutex_destroy(&g_scsi.mutex);
+}
+
+SPDK_TRACE_REGISTER_FN(scsi_trace, "scsi", TRACE_GROUP_SCSI)
+{
+ 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, "");
+ spdk_trace_register_description("SCSI_TASK_START", TRACE_SCSI_TASK_START,
+ OWNER_SCSI_DEV, OBJECT_SCSI_TASK, 0, 0, "");
+}
+
+uint64_t
+spdk_scsi_lun_id_int_to_fmt(int lun_id)
+{
+ uint64_t fmt_lun, method;
+
+ if (SPDK_SCSI_DEV_MAX_LUN <= 0x0100) {
+ /* below 256 */
+ method = 0x00U;
+ fmt_lun = (method & 0x03U) << 62;
+ fmt_lun |= ((uint64_t)lun_id & 0x00ffU) << 48;
+ } else if (SPDK_SCSI_DEV_MAX_LUN <= 0x4000) {
+ /* below 16384 */
+ method = 0x01U;
+ fmt_lun = (method & 0x03U) << 62;
+ fmt_lun |= ((uint64_t)lun_id & 0x3fffU) << 48;
+ } else {
+ /* XXX */
+ fmt_lun = 0;
+ }
+
+ return fmt_lun;
+}
+
+int
+spdk_scsi_lun_id_fmt_to_int(uint64_t fmt_lun)
+{
+ uint64_t method;
+ int lun_i;
+
+ method = (fmt_lun >> 62) & 0x03U;
+ fmt_lun = fmt_lun >> 48;
+ if (method == 0x00U) {
+ lun_i = (int)(fmt_lun & 0x00ffU);
+ } else if (method == 0x01U) {
+ lun_i = (int)(fmt_lun & 0x3fffU);
+ } else {
+ lun_i = 0xffffU;
+ }
+ return lun_i;
+}
+
+SPDK_LOG_REGISTER_COMPONENT("scsi", SPDK_LOG_SCSI)
diff --git a/src/spdk/lib/scsi/scsi_bdev.c b/src/spdk/lib/scsi/scsi_bdev.c
new file mode 100644
index 000000000..bf0fb5af7
--- /dev/null
+++ b/src/spdk/lib/scsi/scsi_bdev.c
@@ -0,0 +1,2067 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (C) 2008-2012 Daisuke Aoyama <aoyama@peach.ne.jp>.
+ * Copyright (c) Intel Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "scsi_internal.h"
+
+/*
+ * TODO: move bdev SCSI error code translation tests to bdev unit test
+ * and remove this include.
+ */
+#include "spdk/bdev_module.h"
+
+#include "spdk/env.h"
+#include "spdk/bdev.h"
+#include "spdk/endian.h"
+#include "spdk/likely.h"
+#include "spdk/string.h"
+#include "spdk/util.h"
+
+#define SPDK_WORK_BLOCK_SIZE (4ULL * 1024ULL * 1024ULL)
+#define SPDK_WORK_ATS_BLOCK_SIZE (1ULL * 1024ULL * 1024ULL)
+#define MAX_SERIAL_STRING 32
+
+#define DEFAULT_DISK_VENDOR "INTEL"
+#define DEFAULT_DISK_REVISION "0001"
+#define DEFAULT_DISK_ROTATION_RATE 1 /* Non-rotating medium */
+#define DEFAULT_DISK_FORM_FACTOR 0x02 /* 3.5 inch */
+#define DEFAULT_MAX_UNMAP_BLOCK_DESCRIPTOR_COUNT 256
+
+#define INQUIRY_OFFSET(field) offsetof(struct spdk_scsi_cdb_inquiry_data, field) + \
+ sizeof(((struct spdk_scsi_cdb_inquiry_data *)0x0)->field)
+
+static void bdev_scsi_process_block_resubmit(void *arg);
+
+static int
+hex2bin(char ch)
+{
+ if ((ch >= '0') && (ch <= '9')) {
+ return ch - '0';
+ }
+ ch = tolower(ch);
+ if ((ch >= 'a') && (ch <= 'f')) {
+ return ch - 'a' + 10;
+ }
+ return (int)ch;
+}
+
+static void
+bdev_scsi_set_naa_ieee_extended(const char *name, uint8_t *buf)
+{
+ int i, value, count = 0;
+ uint64_t local_value;
+
+ for (i = 0; (i < 16) && (name[i] != '\0'); i++) {
+ value = hex2bin(name[i]);
+ if (i % 2) {
+ buf[count++] |= value << 4;
+ } else {
+ buf[count] = value;
+ }
+ }
+
+ local_value = *(uint64_t *)buf;
+ /*
+ * see spc3r23 7.6.3.6.2,
+ * NAA IEEE Extended identifer format
+ */
+ local_value &= 0x0fff000000ffffffull;
+ /* NAA 02, and 00 03 47 for IEEE Intel */
+ local_value |= 0x2000000347000000ull;
+
+ to_be64((void *)buf, local_value);
+}
+
+static int
+bdev_scsi_report_luns(struct spdk_scsi_lun *lun,
+ int sel, uint8_t *data, int alloc_len)
+{
+ struct spdk_scsi_dev *dev;
+ uint64_t fmt_lun;
+ int hlen, len = 0;
+ int i;
+
+ if (alloc_len < 8) {
+ return -1;
+ }
+
+ if (sel == 0x00) {
+ /* logical unit with addressing method */
+ } else if (sel == 0x01) {
+ /* well known logical unit */
+ } else if (sel == 0x02) {
+ /* logical unit */
+ } else {
+ return -1;
+ }
+
+ /* LUN LIST LENGTH */
+ memset(data, 0, 4);
+
+ /* Reserved */
+ memset(&data[4], 0, 4);
+ hlen = 8;
+
+ dev = lun->dev;
+
+ for (i = 0; i < SPDK_SCSI_DEV_MAX_LUN; i++) {
+ if (dev->lun[i] == NULL) {
+ continue;
+ }
+
+ if (alloc_len - (hlen + len) < 8) {
+ return -1;
+ }
+
+ fmt_lun = spdk_scsi_lun_id_int_to_fmt(i);
+
+ /* LUN */
+ to_be64(&data[hlen + len], fmt_lun);
+ len += 8;
+ }
+
+ /* LUN LIST LENGTH */
+ to_be32(data, len);
+
+ return hlen + len;
+}
+
+static int
+bdev_scsi_pad_scsi_name(char *dst, const char *name)
+{
+ size_t len;
+
+ len = strlen(name);
+ memcpy(dst, name, len);
+ do {
+ dst[len++] = '\0';
+ } while (len & 3);
+
+ return len;
+}
+
+static int
+bdev_scsi_inquiry(struct spdk_bdev *bdev, struct spdk_scsi_task *task,
+ uint8_t *cdb, uint8_t *data, uint16_t alloc_len)
+{
+ struct spdk_scsi_lun *lun;
+ struct spdk_scsi_dev *dev;
+ struct spdk_scsi_port *port;
+ uint32_t blocks, optimal_blocks;
+ int hlen = 0, plen, plen2;
+ uint16_t len = 0;
+ int pc;
+ int pd;
+ int evpd;
+ int i;
+ struct spdk_scsi_cdb_inquiry *inq = (struct spdk_scsi_cdb_inquiry *)cdb;
+
+ /* standard inquiry command at lease with 36 Bytes */
+ if (alloc_len < 0x24) {
+ goto inq_error;
+ }
+
+ lun = task->lun;
+ dev = lun->dev;
+ port = task->target_port;
+
+ pd = SPDK_SPC_PERIPHERAL_DEVICE_TYPE_DISK;
+ pc = inq->page_code;
+ evpd = inq->evpd & 0x1;
+
+ if (!evpd && pc) {
+ spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
+ SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
+ SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
+ SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
+ return -1;
+ }
+
+ if (evpd) {
+ struct spdk_scsi_vpd_page *vpage = (struct spdk_scsi_vpd_page *)data;
+
+ /* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */
+ vpage->peripheral_device_type = pd;
+ vpage->peripheral_qualifier = SPDK_SPC_PERIPHERAL_QUALIFIER_CONNECTED;
+ /* PAGE CODE */
+ vpage->page_code = pc;
+
+ /* Vital product data */
+ switch (pc) {
+ case SPDK_SPC_VPD_SUPPORTED_VPD_PAGES:
+ hlen = 4;
+
+ vpage->params[0] = SPDK_SPC_VPD_SUPPORTED_VPD_PAGES;
+ vpage->params[1] = SPDK_SPC_VPD_UNIT_SERIAL_NUMBER;
+ vpage->params[2] = SPDK_SPC_VPD_DEVICE_IDENTIFICATION;
+ vpage->params[3] = SPDK_SPC_VPD_MANAGEMENT_NETWORK_ADDRESSES;
+ vpage->params[4] = SPDK_SPC_VPD_EXTENDED_INQUIRY_DATA;
+ vpage->params[5] = SPDK_SPC_VPD_MODE_PAGE_POLICY;
+ vpage->params[6] = SPDK_SPC_VPD_SCSI_PORTS;
+ vpage->params[7] = SPDK_SPC_VPD_BLOCK_LIMITS;
+ vpage->params[8] = SPDK_SPC_VPD_BLOCK_DEV_CHARS;
+ len = 9;
+ if (spdk_bdev_io_type_supported(bdev, SPDK_BDEV_IO_TYPE_UNMAP)) {
+ vpage->params[9] = SPDK_SPC_VPD_BLOCK_THIN_PROVISION;
+ len++;
+ }
+
+ /* PAGE LENGTH */
+ to_be16(vpage->alloc_len, len);
+ break;
+
+ case SPDK_SPC_VPD_UNIT_SERIAL_NUMBER: {
+ const char *name = spdk_bdev_get_name(bdev);
+
+ hlen = 4;
+
+ /* PRODUCT SERIAL NUMBER */
+ len = strlen(name) + 1;
+ if (len > MAX_SERIAL_STRING) {
+ len = MAX_SERIAL_STRING;
+ }
+
+ memcpy(vpage->params, name, len - 1);
+ vpage->params[len - 1] = 0;
+
+ /* PAGE LENGTH */
+ to_be16(vpage->alloc_len, len);
+ break;
+ }
+
+ case SPDK_SPC_VPD_DEVICE_IDENTIFICATION: {
+ const char *name = spdk_bdev_get_name(bdev);
+ const char *product_name = spdk_bdev_get_product_name(bdev);
+ uint8_t protocol_id = dev->protocol_id;
+ uint8_t *buf = vpage->params;
+ struct spdk_scsi_desig_desc *desig;
+
+ hlen = 4;
+
+ /* Check total length by calculated how much space all entries take */
+ len = sizeof(struct spdk_scsi_desig_desc) + 8;
+ len += sizeof(struct spdk_scsi_desig_desc) + 8 + 16 + MAX_SERIAL_STRING;
+ len += sizeof(struct spdk_scsi_desig_desc) + SPDK_SCSI_DEV_MAX_NAME + 1;
+ len += sizeof(struct spdk_scsi_desig_desc) + SPDK_SCSI_PORT_MAX_NAME_LENGTH;
+ len += sizeof(struct spdk_scsi_desig_desc) + 4;
+ len += sizeof(struct spdk_scsi_desig_desc) + 4;
+ len += sizeof(struct spdk_scsi_desig_desc) + 4;
+ if (sizeof(struct spdk_scsi_vpd_page) + len > alloc_len) {
+ spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
+ SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
+ SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
+ SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
+ return -1;
+ }
+
+ /* Now fill out the designator array */
+
+ /* NAA designator */
+ desig = (struct spdk_scsi_desig_desc *)buf;
+ desig->code_set = SPDK_SPC_VPD_CODE_SET_BINARY;
+ desig->protocol_id = protocol_id;
+ desig->type = SPDK_SPC_VPD_IDENTIFIER_TYPE_NAA;
+ desig->association = SPDK_SPC_VPD_ASSOCIATION_LOGICAL_UNIT;
+ desig->reserved0 = 0;
+ desig->piv = 1;
+ desig->reserved1 = 0;
+ desig->len = 8;
+ bdev_scsi_set_naa_ieee_extended(name, desig->desig);
+ len = sizeof(struct spdk_scsi_desig_desc) + 8;
+
+ buf += sizeof(struct spdk_scsi_desig_desc) + desig->len;
+
+ /* T10 Vendor ID designator */
+ desig = (struct spdk_scsi_desig_desc *)buf;
+ desig->code_set = SPDK_SPC_VPD_CODE_SET_ASCII;
+ desig->protocol_id = protocol_id;
+ desig->type = SPDK_SPC_VPD_IDENTIFIER_TYPE_T10_VENDOR_ID;
+ desig->association = SPDK_SPC_VPD_ASSOCIATION_LOGICAL_UNIT;
+ desig->reserved0 = 0;
+ desig->piv = 1;
+ desig->reserved1 = 0;
+ desig->len = 8 + 16 + MAX_SERIAL_STRING;
+ spdk_strcpy_pad(desig->desig, DEFAULT_DISK_VENDOR, 8, ' ');
+ spdk_strcpy_pad(&desig->desig[8], product_name, 16, ' ');
+ spdk_strcpy_pad(&desig->desig[24], name, MAX_SERIAL_STRING, ' ');
+ len += sizeof(struct spdk_scsi_desig_desc) + 8 + 16 + MAX_SERIAL_STRING;
+
+ buf += sizeof(struct spdk_scsi_desig_desc) + desig->len;
+
+ /* SCSI Device Name designator */
+ desig = (struct spdk_scsi_desig_desc *)buf;
+ desig->code_set = SPDK_SPC_VPD_CODE_SET_UTF8;
+ desig->protocol_id = protocol_id;
+ desig->type = SPDK_SPC_VPD_IDENTIFIER_TYPE_SCSI_NAME;
+ desig->association = SPDK_SPC_VPD_ASSOCIATION_TARGET_DEVICE;
+ desig->reserved0 = 0;
+ desig->piv = 1;
+ desig->reserved1 = 0;
+ desig->len = bdev_scsi_pad_scsi_name(desig->desig, dev->name);
+ len += sizeof(struct spdk_scsi_desig_desc) + desig->len;
+
+ buf += sizeof(struct spdk_scsi_desig_desc) + desig->len;
+
+ /* SCSI Port Name designator */
+ desig = (struct spdk_scsi_desig_desc *)buf;
+ desig->code_set = SPDK_SPC_VPD_CODE_SET_UTF8;
+ desig->protocol_id = protocol_id;
+ desig->type = SPDK_SPC_VPD_IDENTIFIER_TYPE_SCSI_NAME;
+ desig->association = SPDK_SPC_VPD_ASSOCIATION_TARGET_PORT;
+ desig->reserved0 = 0;
+ desig->piv = 1;
+ desig->reserved1 = 0;
+ desig->len = snprintf(desig->desig, SPDK_SCSI_PORT_MAX_NAME_LENGTH, "%s", port->name);
+ len += sizeof(struct spdk_scsi_desig_desc) + desig->len;
+
+ buf += sizeof(struct spdk_scsi_desig_desc) + desig->len;
+
+ /* Relative Target Port designator */
+ desig = (struct spdk_scsi_desig_desc *)buf;
+ desig->code_set = SPDK_SPC_VPD_CODE_SET_BINARY;
+ desig->protocol_id = protocol_id;
+ desig->type = SPDK_SPC_VPD_IDENTIFIER_TYPE_RELATIVE_TARGET_PORT;
+ desig->association = SPDK_SPC_VPD_ASSOCIATION_TARGET_PORT;
+ desig->reserved0 = 0;
+ desig->piv = 1;
+ desig->reserved1 = 0;
+ desig->len = 4;
+ memset(desig->desig, 0, 2); /* Reserved */
+ to_be16(&desig->desig[2], port->index);
+ len += sizeof(struct spdk_scsi_desig_desc) + desig->len;
+
+ buf += sizeof(struct spdk_scsi_desig_desc) + desig->len;
+
+ /* Target port group designator */
+ desig = (struct spdk_scsi_desig_desc *)buf;
+ desig->code_set = SPDK_SPC_VPD_CODE_SET_BINARY;
+ desig->protocol_id = protocol_id;
+ desig->type = SPDK_SPC_VPD_IDENTIFIER_TYPE_TARGET_PORT_GROUP;
+ desig->association = SPDK_SPC_VPD_ASSOCIATION_TARGET_PORT;
+ desig->reserved0 = 0;
+ desig->piv = 1;
+ desig->reserved1 = 0;
+ desig->len = 4;
+ memset(desig->desig, 0, 4);
+ len += sizeof(struct spdk_scsi_desig_desc) + desig->len;
+
+ buf += sizeof(struct spdk_scsi_desig_desc) + desig->len;
+
+ /* Logical unit group designator */
+ desig = (struct spdk_scsi_desig_desc *)buf;
+ desig->code_set = SPDK_SPC_VPD_CODE_SET_BINARY;
+ desig->protocol_id = protocol_id;
+ desig->type = SPDK_SPC_VPD_IDENTIFIER_TYPE_LOGICAL_UNIT_GROUP;
+ desig->association = SPDK_SPC_VPD_ASSOCIATION_LOGICAL_UNIT;
+ desig->reserved0 = 0;
+ desig->piv = 1;
+ desig->reserved1 = 0;
+ desig->len = 4;
+ memset(desig->desig, 0, 2); /* Reserved */
+ to_be16(&desig->desig[2], dev->id);
+ len += sizeof(struct spdk_scsi_desig_desc) + desig->len;
+
+ to_be16(vpage->alloc_len, len);
+
+ break;
+ }
+
+ case SPDK_SPC_VPD_EXTENDED_INQUIRY_DATA: {
+ struct spdk_scsi_vpd_ext_inquiry *vext = (struct spdk_scsi_vpd_ext_inquiry *)vpage;
+
+ hlen = 4;
+ memset((uint8_t *)vext + hlen, 0, sizeof(*vext) - hlen);
+
+ /* RTO(3) GRD_CHK(2) APP_CHK(1) REF_CHK(0) */
+
+ /* GROUP_SUP(4) PRIOR_SUP(3) HEADSUP(2) ORDSUP(1) SIMPSUP(0) */
+ vext->sup = SPDK_SCSI_VEXT_HEADSUP | SPDK_SCSI_VEXT_SIMPSUP;
+
+ /* NV_SUP(1) V_SUP(0) */
+
+ /* Reserved[7-63] */
+
+ len = 64 - hlen;
+
+ /* PAGE LENGTH */
+ to_be16(vpage->alloc_len, len);
+ break;
+ }
+
+ case SPDK_SPC_VPD_MANAGEMENT_NETWORK_ADDRESSES:
+ /* PAGE LENGTH */
+ hlen = 4;
+
+ to_be16(vpage->alloc_len, len);
+ break;
+
+ case SPDK_SPC_VPD_MODE_PAGE_POLICY: {
+ struct spdk_scsi_mpage_policy_desc *pdesc =
+ (struct spdk_scsi_mpage_policy_desc *)vpage->params;
+
+ hlen = 4;
+
+ /* Mode page policy descriptor 1 */
+
+ /* POLICY PAGE CODE(5-0) */
+ /* all page code */
+ pdesc->page_code = 0x3f;
+
+ /* POLICY SUBPAGE CODE */
+ /* all sub page */
+ pdesc->sub_page_code = 0xff;
+
+ /* MLUS(7) MODE PAGE POLICY(1-0) */
+ /* MLUS own copy */
+ /* Shared MODE PAGE policy */
+ pdesc->policy = 0;
+ /* Reserved */
+ pdesc->reserved = 0;
+
+ len += 4;
+
+ to_be16(vpage->alloc_len, len);
+ break;
+ }
+
+ case SPDK_SPC_VPD_SCSI_PORTS: {
+ /* PAGE LENGTH */
+ hlen = 4;
+
+ /* Identification descriptor list */
+ for (i = 0; i < SPDK_SCSI_DEV_MAX_PORTS; i++) {
+ struct spdk_scsi_port_desc *sdesc;
+ struct spdk_scsi_tgt_port_desc *pdesc;
+
+ if (!dev->port[i].is_used) {
+ continue;
+ }
+
+ /* Identification descriptor N */
+ sdesc = (struct spdk_scsi_port_desc *)&vpage->params[len];
+
+ /* Reserved */
+ sdesc->reserved = 0;
+
+ /* RELATIVE PORT IDENTIFIER */
+ to_be16(&sdesc->rel_port_id, dev->port[i].index);
+
+ /* Reserved */
+ sdesc->reserved2 = 0;
+
+ /* INITIATOR PORT TRANSPORTID LENGTH */
+ sdesc->init_port_len = 0;
+
+ /* Reserved */
+ sdesc->init_port_id = 0;
+
+ /* TARGET PORT DESCRIPTORS LENGTH */
+ sdesc->tgt_desc_len = 0;
+
+ len += 12;
+
+ plen2 = 0;
+ /* Target port descriptor 1 */
+ pdesc = (struct spdk_scsi_tgt_port_desc *)sdesc->tgt_desc;
+
+ /* PROTOCOL IDENTIFIER(7-4) CODE SET(3-0) */
+ pdesc->code_set =
+ SPDK_SPC_PROTOCOL_IDENTIFIER_ISCSI << 4 |
+ SPDK_SPC_VPD_CODE_SET_UTF8;
+
+ /* PIV(7) ASSOCIATION(5-4) IDENTIFIER TYPE(3-0) */
+ pdesc->desig_type = SPDK_SPC_VPD_DESIG_PIV |
+ SPDK_SPC_VPD_ASSOCIATION_TARGET_PORT << 4 |
+ SPDK_SPC_VPD_IDENTIFIER_TYPE_SCSI_NAME;
+
+ /* Reserved */
+ pdesc->reserved = 0;
+
+ /* IDENTIFIER */
+ plen = snprintf((char *)pdesc->designator,
+ SPDK_SCSI_PORT_MAX_NAME_LENGTH, "%s",
+ dev->port[i].name);
+ pdesc->len = plen;
+
+ plen2 += 4 + plen;
+
+ /* TARGET PORT DESCRIPTORS LENGTH */
+ to_be16(&sdesc->tgt_desc_len, plen2);
+
+ len += plen2;
+ }
+
+ to_be16(vpage->alloc_len, len);
+ break;
+ }
+
+ case SPDK_SPC_VPD_BLOCK_LIMITS: {
+ uint32_t block_size = spdk_bdev_get_data_block_size(bdev);
+
+ /* PAGE LENGTH */
+ memset(&data[4], 0, 60);
+
+ hlen = 4;
+
+ /* WSNZ(0) */
+ /* support zero length in WRITE SAME */
+
+ /* MAXIMUM COMPARE AND WRITE LENGTH */
+ blocks = SPDK_WORK_ATS_BLOCK_SIZE / block_size;
+
+ if (blocks > 0xff) {
+ blocks = 0xff;
+ }
+
+ data[5] = (uint8_t)blocks;
+
+ /* force align to 4KB */
+ if (block_size < 4096) {
+ optimal_blocks = 4096 / block_size;
+ } else {
+ optimal_blocks = 1;
+ }
+
+ /* OPTIMAL TRANSFER LENGTH GRANULARITY */
+ to_be16(&data[6], optimal_blocks);
+
+ blocks = SPDK_WORK_BLOCK_SIZE / block_size;
+
+ /* MAXIMUM TRANSFER LENGTH */
+ to_be32(&data[8], blocks);
+ /* OPTIMAL TRANSFER LENGTH */
+ to_be32(&data[12], blocks);
+
+ /* MAXIMUM PREFETCH XDREAD XDWRITE TRANSFER LENGTH */
+
+ len = 20 - hlen;
+
+ if (spdk_bdev_io_type_supported(bdev, SPDK_BDEV_IO_TYPE_UNMAP)) {
+ /*
+ * MAXIMUM UNMAP LBA COUNT: indicates the
+ * maximum number of LBAs that may be
+ * unmapped by an UNMAP command.
+ */
+ /* For now, choose 4MB as the maximum. */
+ to_be32(&data[20], 4194304);
+
+ /*
+ * MAXIMUM UNMAP BLOCK DESCRIPTOR COUNT:
+ * indicates the maximum number of UNMAP
+ * block descriptors that shall be contained
+ * in the parameter data transferred to the
+ * device server for an UNMAP command.
+ * The bdev layer automatically splits unmap
+ * requests, so pick an arbitrary high number here.
+ */
+ to_be32(&data[24], DEFAULT_MAX_UNMAP_BLOCK_DESCRIPTOR_COUNT);
+
+ /*
+ * The UGAVALID bit is left as 0 which means neither the
+ * OPTIMAL UNMAP GRANULARITY nor the UNMAP GRANULARITY
+ * ALIGNMENT fields are valid.
+ */
+
+ /*
+ * MAXIMUM WRITE SAME LENGTH: indicates the
+ * maximum number of contiguous logical blocks
+ * that the device server allows to be unmapped
+ * or written in a single WRITE SAME command.
+ */
+ to_be64(&data[36], 512);
+
+ /* Reserved */
+ /* not specified */
+ len = 64 - hlen;
+ }
+
+ to_be16(vpage->alloc_len, len);
+ break;
+ }
+
+ case SPDK_SPC_VPD_BLOCK_DEV_CHARS: {
+ /* PAGE LENGTH */
+ hlen = 4;
+ len = 64 - hlen;
+
+ to_be16(&data[4], DEFAULT_DISK_ROTATION_RATE);
+
+ /* Reserved */
+ data[6] = 0;
+ /* NOMINAL FORM FACTOR(3-0) */
+ data[7] = DEFAULT_DISK_FORM_FACTOR << 4;
+ /* Reserved */
+ memset(&data[8], 0, 64 - 8);
+
+ to_be16(vpage->alloc_len, len);
+ break;
+ }
+
+ case SPDK_SPC_VPD_BLOCK_THIN_PROVISION: {
+ if (!spdk_bdev_io_type_supported(bdev, SPDK_BDEV_IO_TYPE_UNMAP)) {
+ goto inq_error;
+ }
+
+ hlen = 4;
+ len = 7;
+
+ /*
+ * PAGE LENGTH : if the DP bit is set to one, then the
+ * page length shall be set 0004h.
+ */
+ to_be16(&data[2], 0x0004);
+
+ /*
+ * THRESHOLD EXPONENT : it indicates the threshold set
+ * size in LBAs as a power of 2( i.e., the threshold
+ * set size = 2 ^ (threshold exponent).
+ */
+ data[4] = 0;
+
+ /*
+ * Set the LBPU bit to indicate the support for UNMAP
+ * command.
+ */
+ data[5] |= SPDK_SCSI_UNMAP_LBPU;
+
+ /*
+ * Set the provisioning type to thin provision.
+ */
+ data[6] = SPDK_SCSI_UNMAP_THIN_PROVISIONING;
+
+ to_be16(vpage->alloc_len, len);
+ break;
+ }
+
+ default:
+ if (pc >= 0xc0 && pc <= 0xff) {
+ SPDK_DEBUGLOG(SPDK_LOG_SCSI, "Vendor specific INQUIRY VPD page 0x%x\n", pc);
+ } else {
+ SPDK_ERRLOG("unsupported INQUIRY VPD page 0x%x\n", pc);
+ }
+ goto inq_error;
+ }
+ } else {
+ struct spdk_scsi_cdb_inquiry_data *inqdata =
+ (struct spdk_scsi_cdb_inquiry_data *)data;
+
+ /* Standard INQUIRY data */
+ /* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */
+ inqdata->peripheral_device_type = pd;
+ inqdata->peripheral_qualifier = SPDK_SPC_PERIPHERAL_QUALIFIER_CONNECTED;
+
+ /* RMB(7) */
+ inqdata->rmb = 0;
+
+ /* VERSION */
+ /* See SPC3/SBC2/MMC4/SAM2 for more details */
+ inqdata->version = SPDK_SPC_VERSION_SPC3;
+
+ /* NORMACA(5) HISUP(4) RESPONSE DATA FORMAT(3-0) */
+ /* format 2 */ /* hierarchical support */
+ inqdata->response = 2 | 1 << 4;
+
+ hlen = 5;
+
+ /* SCCS(7) ACC(6) TPGS(5-4) 3PC(3) PROTECT(0) */
+ /* Not support TPGS */
+ inqdata->flags = 0;
+
+ /* MULTIP */
+ inqdata->flags2 = 0x10;
+
+ /* WBUS16(5) SYNC(4) LINKED(3) CMDQUE(1) VS(0) */
+ /* CMDQUE */
+ inqdata->flags3 = 0x2;
+
+ /* T10 VENDOR IDENTIFICATION */
+ spdk_strcpy_pad(inqdata->t10_vendor_id, DEFAULT_DISK_VENDOR, 8, ' ');
+
+ /* PRODUCT IDENTIFICATION */
+ spdk_strcpy_pad(inqdata->product_id, spdk_bdev_get_product_name(bdev), 16, ' ');
+
+ /* PRODUCT REVISION LEVEL */
+ spdk_strcpy_pad(inqdata->product_rev, DEFAULT_DISK_REVISION, 4, ' ');
+
+ /*
+ * Standard inquiry data ends here. Only populate remaining fields if alloc_len
+ * indicates enough space to hold it.
+ */
+ len = INQUIRY_OFFSET(product_rev) - 5;
+
+ if (alloc_len >= INQUIRY_OFFSET(vendor)) {
+ /* Vendor specific */
+ memset(inqdata->vendor, 0x20, 20);
+ len += sizeof(inqdata->vendor);
+ }
+
+ if (alloc_len >= INQUIRY_OFFSET(ius)) {
+ /* CLOCKING(3-2) QAS(1) IUS(0) */
+ inqdata->ius = 0;
+ len += sizeof(inqdata->ius);
+ }
+
+ if (alloc_len >= INQUIRY_OFFSET(reserved)) {
+ /* Reserved */
+ inqdata->reserved = 0;
+ len += sizeof(inqdata->reserved);
+ }
+
+ /* VERSION DESCRIPTOR 1-8 */
+ if (alloc_len >= INQUIRY_OFFSET(reserved) + 2) {
+ to_be16(&inqdata->desc[0], 0x0960);
+ len += 2;
+ }
+
+ if (alloc_len >= INQUIRY_OFFSET(reserved) + 4) {
+ to_be16(&inqdata->desc[2], 0x0300); /* SPC-3 (no version claimed) */
+ len += 2;
+ }
+
+ if (alloc_len >= INQUIRY_OFFSET(reserved) + 6) {
+ to_be16(&inqdata->desc[4], 0x320); /* SBC-2 (no version claimed) */
+ len += 2;
+ }
+
+ if (alloc_len >= INQUIRY_OFFSET(reserved) + 8) {
+ to_be16(&inqdata->desc[6], 0x0040); /* SAM-2 (no version claimed) */
+ len += 2;
+ }
+
+ /*
+ * We only fill out 4 descriptors, but if the allocation length goes past
+ * that, zero the remaining bytes. This fixes some SCSI compliance tests
+ * which expect a full 96 bytes to be returned, including the unpopulated
+ * version descriptors 5-8 (4 * 2 = 8 bytes) plus the 22 bytes of reserved
+ * space (bytes 74-95) - for a total of 30 bytes.
+ */
+ if (alloc_len > INQUIRY_OFFSET(reserved) + 8) {
+ i = alloc_len - (INQUIRY_OFFSET(reserved) + 8);
+ if (i > 30) {
+ i = 30;
+ }
+ memset(&inqdata->desc[8], 0, i);
+ len += i;
+ }
+
+ /* ADDITIONAL LENGTH */
+ inqdata->add_len = len;
+ }
+
+ return hlen + len;
+
+inq_error:
+ task->data_transferred = 0;
+ spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
+ SPDK_SCSI_SENSE_NO_SENSE,
+ SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
+ SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
+ return -1;
+}
+
+static void
+mode_sense_page_init(uint8_t *buf, int len, int page, int subpage)
+{
+ if (!buf) {
+ return;
+ }
+
+ memset(buf, 0, len);
+ if (subpage != 0) {
+ buf[0] = page | 0x40; /* PAGE + SPF=1 */
+ buf[1] = subpage;
+ to_be16(&buf[2], len - 4);
+ } else {
+ buf[0] = page;
+ buf[1] = len - 2;
+ }
+}
+
+static int
+bdev_scsi_mode_sense_page(struct spdk_bdev *bdev,
+ uint8_t *cdb, int pc, int page, int subpage,
+ uint8_t *data, struct spdk_scsi_task *task)
+{
+ uint8_t *cp = data;
+ int len = 0;
+ int plen;
+ int i;
+
+ if (pc == 0x00) {
+ /* Current values */
+ } else if (pc == 0x01) {
+ /* Changeable values */
+ /* As we currently do not support changeable values,
+ all parameters are reported as zero. */
+ } else if (pc == 0x02) {
+ /* Default values */
+ } else {
+ /* Saved values not supported */
+ spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
+ SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
+ SPDK_SCSI_ASC_SAVING_PARAMETERS_NOT_SUPPORTED,
+ SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
+ return -1;
+ }
+
+ switch (page) {
+ case 0x00:
+ /* Vendor specific */
+ break;
+ case 0x01:
+ /* Read-Write Error Recovery */
+ SPDK_DEBUGLOG(SPDK_LOG_SCSI,
+ "MODE_SENSE Read-Write Error Recovery\n");
+ if (subpage != 0x00) {
+ break;
+ }
+ plen = 0x0a + 2;
+ mode_sense_page_init(cp, plen, page, subpage);
+ len += plen;
+ break;
+ case 0x02:
+ /* Disconnect-Reconnect */
+ SPDK_DEBUGLOG(SPDK_LOG_SCSI,
+ "MODE_SENSE Disconnect-Reconnect\n");
+ if (subpage != 0x00) {
+ break;
+ }
+ plen = 0x0e + 2;
+ mode_sense_page_init(cp, plen, page, subpage);
+ len += plen;
+ break;
+ case 0x03:
+ /* Obsolete (Format Device) */
+ break;
+ case 0x04:
+ /* Obsolete (Rigid Disk Geometry) */
+ break;
+ case 0x05:
+ /* Obsolete (Rigid Disk Geometry) */
+ break;
+ case 0x06:
+ /* Reserved */
+ break;
+ case 0x07:
+ /* Verify Error Recovery */
+ SPDK_DEBUGLOG(SPDK_LOG_SCSI,
+ "MODE_SENSE Verify Error Recovery\n");
+
+ if (subpage != 0x00) {
+ break;
+ }
+
+ plen = 0x0a + 2;
+ mode_sense_page_init(cp, plen, page, subpage);
+ len += plen;
+ break;
+ case 0x08: {
+ /* Caching */
+ SPDK_DEBUGLOG(SPDK_LOG_SCSI, "MODE_SENSE Caching\n");
+ if (subpage != 0x00) {
+ break;
+ }
+
+ plen = 0x12 + 2;
+ mode_sense_page_init(cp, plen, page, subpage);
+
+ if (cp && spdk_bdev_has_write_cache(bdev) && pc != 0x01) {
+ cp[2] |= 0x4; /* WCE */
+ }
+
+ /* Read Cache Disable (RCD) = 1 */
+ if (cp && pc != 0x01) {
+ cp[2] |= 0x1;
+ }
+
+ len += plen;
+ break;
+ }
+ case 0x09:
+ /* Obsolete */
+ break;
+ case 0x0a:
+ switch (subpage) {
+ case 0x00:
+ /* Control */
+ SPDK_DEBUGLOG(SPDK_LOG_SCSI,
+ "MODE_SENSE Control\n");
+ plen = 0x0a + 2;
+ mode_sense_page_init(cp, plen, page, subpage);
+ len += plen;
+ break;
+ case 0x01:
+ /* Control Extension */
+ SPDK_DEBUGLOG(SPDK_LOG_SCSI,
+ "MODE_SENSE Control Extension\n");
+ plen = 0x1c + 4;
+ mode_sense_page_init(cp, plen, page, subpage);
+ len += plen;
+ break;
+ case 0xff:
+ /* All subpages */
+ len += bdev_scsi_mode_sense_page(bdev,
+ cdb, pc, page,
+ 0x00,
+ cp ? &cp[len] : NULL, task);
+ len += bdev_scsi_mode_sense_page(bdev,
+ cdb, pc, page,
+ 0x01,
+ cp ? &cp[len] : NULL, task);
+ break;
+ default:
+ /* 0x02-0x3e: Reserved */
+ break;
+ }
+ break;
+ case 0x0b:
+ /* Obsolete (Medium Types Supported) */
+ break;
+ case 0x0c:
+ /* Obsolete (Notch And Partitio) */
+ break;
+ case 0x0d:
+ /* Obsolete */
+ break;
+ case 0x0e:
+ case 0x0f:
+ /* Reserved */
+ break;
+ case 0x10:
+ /* XOR Control */
+ SPDK_DEBUGLOG(SPDK_LOG_SCSI, "MODE_SENSE XOR Control\n");
+ if (subpage != 0x00) {
+ break;
+ }
+ plen = 0x16 + 2;
+ mode_sense_page_init(cp, plen, page, subpage);
+ len += plen;
+ break;
+ case 0x11:
+ case 0x12:
+ case 0x13:
+ /* Reserved */
+ break;
+ case 0x14:
+ /* Enclosure Services Management */
+ break;
+ case 0x15:
+ case 0x16:
+ case 0x17:
+ /* Reserved */
+ break;
+ case 0x18:
+ /* Protocol-Specific LUN */
+ break;
+ case 0x19:
+ /* Protocol-Specific Port */
+ break;
+ case 0x1a:
+ /* Power Condition */
+ SPDK_DEBUGLOG(SPDK_LOG_SCSI,
+ "MODE_SENSE Power Condition\n");
+ if (subpage != 0x00) {
+ break;
+ }
+ plen = 0x0a + 2;
+ mode_sense_page_init(cp, plen, page, subpage);
+ len += plen;
+ break;
+ case 0x1b:
+ /* Reserved */
+ break;
+ case 0x1c:
+ /* Informational Exceptions Control */
+ SPDK_DEBUGLOG(SPDK_LOG_SCSI,
+ "MODE_SENSE Informational Exceptions Control\n");
+ if (subpage != 0x00) {
+ break;
+ }
+
+ plen = 0x0a + 2;
+ mode_sense_page_init(cp, plen, page, subpage);
+ len += plen;
+ break;
+ case 0x1d:
+ case 0x1e:
+ case 0x1f:
+ /* Reserved */
+ break;
+ case 0x20:
+ case 0x21:
+ case 0x22:
+ case 0x23:
+ case 0x24:
+ case 0x25:
+ case 0x26:
+ case 0x27:
+ case 0x28:
+ case 0x29:
+ case 0x2a:
+ case 0x2b:
+ case 0x2c:
+ case 0x2d:
+ case 0x2e:
+ case 0x2f:
+ case 0x30:
+ case 0x31:
+ case 0x32:
+ case 0x33:
+ case 0x34:
+ case 0x35:
+ case 0x36:
+ case 0x37:
+ case 0x38:
+ case 0x39:
+ case 0x3a:
+ case 0x3b:
+ case 0x3c:
+ case 0x3d:
+ case 0x3e:
+ /* Vendor-specific */
+ break;
+ case 0x3f:
+ switch (subpage) {
+ case 0x00:
+ /* All mode pages */
+ for (i = 0x00; i < 0x3e; i ++) {
+ len += bdev_scsi_mode_sense_page(
+ bdev, cdb, pc, i, 0x00,
+ cp ? &cp[len] : NULL, task);
+ }
+ break;
+ case 0xff:
+ /* All mode pages and subpages */
+ for (i = 0x00; i < 0x3e; i ++) {
+ len += bdev_scsi_mode_sense_page(
+ bdev, cdb, pc, i, 0x00,
+ cp ? &cp[len] : NULL, task);
+ }
+ for (i = 0x00; i < 0x3e; i ++) {
+ len += bdev_scsi_mode_sense_page(
+ bdev, cdb, pc, i, 0xff,
+ cp ? &cp[len] : NULL, task);
+ }
+ break;
+ default:
+ /* 0x01-0x3e: Reserved */
+ break;
+ }
+ }
+
+ return len;
+}
+
+static int
+bdev_scsi_mode_sense(struct spdk_bdev *bdev, int md,
+ uint8_t *cdb, int dbd, int llbaa, int pc,
+ int page, int subpage, uint8_t *data, struct spdk_scsi_task *task)
+{
+ uint64_t num_blocks = spdk_bdev_get_num_blocks(bdev);
+ uint32_t block_size = spdk_bdev_get_data_block_size(bdev);
+ uint8_t *hdr, *bdesc, *pages;
+ int hlen;
+ int blen;
+ int plen, total;
+
+ assert(md == 6 || md == 10);
+
+ if (md == 6) {
+ hlen = 4;
+ blen = 8; /* For MODE SENSE 6 only short LBA */
+ } else {
+ hlen = 8;
+ blen = llbaa ? 16 : 8;
+ }
+
+ if (dbd) {
+ blen = 0;
+ }
+
+ pages = data ? &data[hlen + blen] : NULL;
+ plen = bdev_scsi_mode_sense_page(bdev, cdb, pc, page,
+ subpage,
+ pages, task);
+ if (plen < 0) {
+ return -1;
+ }
+
+ total = hlen + blen + plen;
+ if (data == NULL) {
+ return total;
+ }
+
+ hdr = &data[0];
+ if (hlen == 4) {
+ hdr[0] = total - 1; /* Mode Data Length */
+ hdr[1] = 0; /* Medium Type */
+ hdr[2] = 0; /* Device-Specific Parameter */
+ hdr[3] = blen; /* Block Descripter Length */
+ } else {
+ to_be16(&hdr[0], total - 2); /* Mode Data Length */
+ hdr[2] = 0; /* Medium Type */
+ hdr[3] = 0; /* Device-Specific Parameter */
+ hdr[4] = llbaa ? 0x1 : 0; /* Long/short LBA */
+ hdr[5] = 0; /* Reserved */
+ to_be16(&hdr[6], blen); /* Block Descripter Length */
+ }
+
+ bdesc = &data[hlen];
+ if (blen == 16) {
+ /* Number of Blocks */
+ to_be64(&bdesc[0], num_blocks);
+ /* Reserved */
+ memset(&bdesc[8], 0, 4);
+ /* Block Length */
+ to_be32(&bdesc[12], block_size);
+ } else if (blen == 8) {
+ /* Number of Blocks */
+ if (num_blocks > 0xffffffffULL) {
+ memset(&bdesc[0], 0xff, 4);
+ } else {
+ to_be32(&bdesc[0], num_blocks);
+ }
+
+ /* Block Length */
+ to_be32(&bdesc[4], block_size);
+ }
+
+ return total;
+}
+
+static void
+bdev_scsi_task_complete_cmd(struct spdk_bdev_io *bdev_io, bool success,
+ void *cb_arg)
+{
+ struct spdk_scsi_task *task = cb_arg;
+ int sc, sk, asc, ascq;
+
+ spdk_bdev_io_get_scsi_status(bdev_io, &sc, &sk, &asc, &ascq);
+
+ spdk_bdev_free_io(bdev_io);
+
+ spdk_scsi_task_set_status(task, sc, sk, asc, ascq);
+ scsi_lun_complete_task(task->lun, task);
+}
+
+static void
+bdev_scsi_read_task_complete_cmd(struct spdk_bdev_io *bdev_io, bool success,
+ void *cb_arg)
+{
+ struct spdk_scsi_task *task = cb_arg;
+ int sc, sk, asc, ascq;
+
+ task->bdev_io = bdev_io;
+
+ spdk_bdev_io_get_scsi_status(bdev_io, &sc, &sk, &asc, &ascq);
+
+ spdk_scsi_task_set_status(task, sc, sk, asc, ascq);
+ scsi_lun_complete_task(task->lun, task);
+}
+
+static void
+bdev_scsi_task_complete_reset(struct spdk_bdev_io *bdev_io, bool success,
+ void *cb_arg)
+{
+ struct spdk_scsi_task *task = cb_arg;
+
+ spdk_bdev_free_io(bdev_io);
+
+ if (success) {
+ task->response = SPDK_SCSI_TASK_MGMT_RESP_SUCCESS;
+ }
+
+ scsi_lun_complete_reset_task(task->lun, task);
+}
+
+static void
+bdev_scsi_queue_io(struct spdk_scsi_task *task, spdk_bdev_io_wait_cb cb_fn, void *cb_arg)
+{
+ struct spdk_scsi_lun *lun = task->lun;
+ struct spdk_bdev *bdev = lun->bdev;
+ struct spdk_io_channel *ch = lun->io_channel;
+ int rc;
+
+ task->bdev_io_wait.bdev = bdev;
+ task->bdev_io_wait.cb_fn = cb_fn;
+ task->bdev_io_wait.cb_arg = cb_arg;
+
+ rc = spdk_bdev_queue_io_wait(bdev, ch, &task->bdev_io_wait);
+ if (rc != 0) {
+ assert(false);
+ }
+}
+
+static int
+bdev_scsi_sync(struct spdk_bdev *bdev, struct spdk_bdev_desc *bdev_desc,
+ struct spdk_io_channel *bdev_ch, struct spdk_scsi_task *task,
+ uint64_t lba, uint32_t num_blocks)
+{
+ uint64_t bdev_num_blocks;
+ int rc;
+
+ if (num_blocks == 0) {
+ return SPDK_SCSI_TASK_COMPLETE;
+ }
+
+ bdev_num_blocks = spdk_bdev_get_num_blocks(bdev);
+
+ if (lba >= bdev_num_blocks || num_blocks > bdev_num_blocks ||
+ lba > (bdev_num_blocks - num_blocks)) {
+ SPDK_ERRLOG("end of media\n");
+ spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
+ SPDK_SCSI_SENSE_NO_SENSE,
+ SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
+ SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
+ return SPDK_SCSI_TASK_COMPLETE;
+ }
+
+ rc = spdk_bdev_flush_blocks(bdev_desc, bdev_ch, lba, num_blocks,
+ bdev_scsi_task_complete_cmd, task);
+
+ if (rc) {
+ if (rc == -ENOMEM) {
+ bdev_scsi_queue_io(task, bdev_scsi_process_block_resubmit, task);
+ return SPDK_SCSI_TASK_PENDING;
+ }
+ SPDK_ERRLOG("spdk_bdev_flush_blocks() failed\n");
+ spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
+ SPDK_SCSI_SENSE_NO_SENSE,
+ SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
+ SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
+ return SPDK_SCSI_TASK_COMPLETE;
+ }
+ task->data_transferred = 0;
+ return SPDK_SCSI_TASK_PENDING;
+}
+
+static uint64_t
+_bytes_to_blocks(uint32_t block_size, uint64_t offset_bytes, uint64_t *offset_blocks,
+ uint64_t num_bytes, uint64_t *num_blocks)
+{
+ uint8_t shift_cnt;
+
+ /* Avoid expensive div operations if possible. These spdk_u32 functions are very cheap. */
+ if (spdk_likely(spdk_u32_is_pow2(block_size))) {
+ shift_cnt = spdk_u32log2(block_size);
+ *offset_blocks = offset_bytes >> shift_cnt;
+ *num_blocks = num_bytes >> shift_cnt;
+ return (offset_bytes - (*offset_blocks << shift_cnt)) |
+ (num_bytes - (*num_blocks << shift_cnt));
+ } else {
+ *offset_blocks = offset_bytes / block_size;
+ *num_blocks = num_bytes / block_size;
+ return (offset_bytes % block_size) | (num_bytes % block_size);
+ }
+}
+
+static int
+bdev_scsi_readwrite(struct spdk_bdev *bdev, struct spdk_bdev_desc *bdev_desc,
+ struct spdk_io_channel *bdev_ch, struct spdk_scsi_task *task,
+ uint64_t lba, uint32_t xfer_len, bool is_read)
+{
+ uint64_t bdev_num_blocks, offset_blocks, num_blocks;
+ uint32_t max_xfer_len, block_size;
+ int sk = SPDK_SCSI_SENSE_NO_SENSE, asc = SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE;
+ int rc;
+
+ task->data_transferred = 0;
+
+ if (spdk_unlikely(task->dxfer_dir != SPDK_SCSI_DIR_NONE &&
+ task->dxfer_dir != (is_read ? SPDK_SCSI_DIR_FROM_DEV : SPDK_SCSI_DIR_TO_DEV))) {
+ SPDK_ERRLOG("Incorrect data direction\n");
+ goto check_condition;
+ }
+
+ bdev_num_blocks = spdk_bdev_get_num_blocks(bdev);
+ if (spdk_unlikely(bdev_num_blocks <= lba || bdev_num_blocks - lba < xfer_len)) {
+ SPDK_DEBUGLOG(SPDK_LOG_SCSI, "end of media\n");
+ sk = SPDK_SCSI_SENSE_ILLEGAL_REQUEST;
+ asc = SPDK_SCSI_ASC_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+ goto check_condition;
+ }
+
+ if (spdk_unlikely(xfer_len == 0)) {
+ task->status = SPDK_SCSI_STATUS_GOOD;
+ return SPDK_SCSI_TASK_COMPLETE;
+ }
+
+ block_size = spdk_bdev_get_data_block_size(bdev);
+
+ /* Transfer Length is limited to the Block Limits VPD page Maximum Transfer Length */
+ max_xfer_len = SPDK_WORK_BLOCK_SIZE / block_size;
+ if (spdk_unlikely(xfer_len > max_xfer_len)) {
+ SPDK_ERRLOG("xfer_len %" PRIu32 " > maximum transfer length %" PRIu32 "\n",
+ xfer_len, max_xfer_len);
+ sk = SPDK_SCSI_SENSE_ILLEGAL_REQUEST;
+ asc = SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB;
+ goto check_condition;
+ }
+
+ if (!is_read) {
+ /* Additional check for Transfer Length */
+ if (xfer_len * block_size > task->transfer_len) {
+ SPDK_ERRLOG("xfer_len %" PRIu32 " * block_size %" PRIu32 " > transfer_len %u\n",
+ xfer_len, block_size, task->transfer_len);
+ goto check_condition;
+ }
+ }
+
+ if (_bytes_to_blocks(block_size, task->offset, &offset_blocks, task->length, &num_blocks) != 0) {
+ SPDK_ERRLOG("task's offset %" PRIu64 " or length %" PRIu32 " is not block multiple\n",
+ task->offset, task->length);
+ goto check_condition;
+ }
+
+ offset_blocks += lba;
+
+ SPDK_DEBUGLOG(SPDK_LOG_SCSI,
+ "%s: lba=%"PRIu64", len=%"PRIu64"\n",
+ is_read ? "Read" : "Write", offset_blocks, num_blocks);
+
+ if (is_read) {
+ rc = spdk_bdev_readv_blocks(bdev_desc, bdev_ch, task->iovs, task->iovcnt,
+ offset_blocks, num_blocks,
+ bdev_scsi_read_task_complete_cmd, task);
+ } else {
+ rc = spdk_bdev_writev_blocks(bdev_desc, bdev_ch, task->iovs, task->iovcnt,
+ offset_blocks, num_blocks,
+ bdev_scsi_task_complete_cmd, task);
+ }
+
+ if (rc) {
+ if (rc == -ENOMEM) {
+ bdev_scsi_queue_io(task, bdev_scsi_process_block_resubmit, task);
+ return SPDK_SCSI_TASK_PENDING;
+ }
+ SPDK_ERRLOG("spdk_bdev_%s_blocks() failed\n", is_read ? "readv" : "writev");
+ goto check_condition;
+ }
+
+ task->data_transferred = task->length;
+ return SPDK_SCSI_TASK_PENDING;
+
+check_condition:
+ spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION, sk, asc,
+ SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
+ return SPDK_SCSI_TASK_COMPLETE;
+}
+
+struct spdk_bdev_scsi_unmap_ctx {
+ struct spdk_scsi_task *task;
+ struct spdk_scsi_unmap_bdesc desc[DEFAULT_MAX_UNMAP_BLOCK_DESCRIPTOR_COUNT];
+ uint32_t count;
+};
+
+static int bdev_scsi_unmap(struct spdk_bdev *bdev, struct spdk_bdev_desc *bdev_desc,
+ struct spdk_io_channel *bdev_ch, struct spdk_scsi_task *task,
+ struct spdk_bdev_scsi_unmap_ctx *ctx);
+
+static void
+bdev_scsi_task_complete_unmap_cmd(struct spdk_bdev_io *bdev_io, bool success,
+ void *cb_arg)
+{
+ struct spdk_bdev_scsi_unmap_ctx *ctx = cb_arg;
+ struct spdk_scsi_task *task = ctx->task;
+ int sc, sk, asc, ascq;
+
+ ctx->count--;
+
+ task->bdev_io = bdev_io;
+
+ if (task->status == SPDK_SCSI_STATUS_GOOD) {
+ spdk_bdev_io_get_scsi_status(bdev_io, &sc, &sk, &asc, &ascq);
+ spdk_scsi_task_set_status(task, sc, sk, asc, ascq);
+ }
+
+ if (ctx->count == 0) {
+ scsi_lun_complete_task(task->lun, task);
+ free(ctx);
+ }
+}
+
+static int
+__copy_desc(struct spdk_bdev_scsi_unmap_ctx *ctx, uint8_t *data, size_t data_len)
+{
+ uint16_t desc_data_len;
+ uint16_t desc_count;
+
+ if (!data) {
+ return -EINVAL;
+ }
+
+ if (data_len < 8) {
+ /* We can't even get the reported length, so fail. */
+ return -EINVAL;
+ }
+
+ desc_data_len = from_be16(&data[2]);
+ desc_count = desc_data_len / 16;
+
+ if (desc_data_len > (data_len - 8)) {
+ SPDK_ERRLOG("Error - desc_data_len (%u) > data_len (%lu) - 8\n",
+ desc_data_len, data_len);
+ return -EINVAL;
+ }
+
+ if (desc_count > DEFAULT_MAX_UNMAP_BLOCK_DESCRIPTOR_COUNT) {
+ SPDK_ERRLOG("desc_count (%u) greater than max allowed (%u)\n",
+ desc_count, DEFAULT_MAX_UNMAP_BLOCK_DESCRIPTOR_COUNT);
+ return -EINVAL;
+ }
+
+ memcpy(ctx->desc, &data[8], desc_data_len);
+ return desc_count;
+}
+
+static void
+bdev_scsi_unmap_resubmit(void *arg)
+{
+ struct spdk_bdev_scsi_unmap_ctx *ctx = arg;
+ struct spdk_scsi_task *task = ctx->task;
+ struct spdk_scsi_lun *lun = task->lun;
+
+ bdev_scsi_unmap(lun->bdev, lun->bdev_desc, lun->io_channel, task, ctx);
+}
+
+static int
+bdev_scsi_unmap(struct spdk_bdev *bdev, struct spdk_bdev_desc *bdev_desc,
+ struct spdk_io_channel *bdev_ch, struct spdk_scsi_task *task,
+ struct spdk_bdev_scsi_unmap_ctx *ctx)
+{
+ uint8_t *data;
+ int i, desc_count = -1;
+ int data_len;
+ int rc;
+
+ assert(task->status == SPDK_SCSI_STATUS_GOOD);
+
+ if (ctx == NULL) {
+ ctx = calloc(1, sizeof(*ctx));
+ if (!ctx) {
+ spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
+ SPDK_SCSI_SENSE_NO_SENSE,
+ SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
+ SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
+ return SPDK_SCSI_TASK_COMPLETE;
+ }
+
+ ctx->task = task;
+ ctx->count = 0;
+ }
+
+
+ if (task->iovcnt == 1) {
+ data = (uint8_t *)task->iovs[0].iov_base;
+ data_len = task->iovs[0].iov_len;
+ desc_count = __copy_desc(ctx, data, data_len);
+ } else {
+ data = spdk_scsi_task_gather_data(task, &data_len);
+ if (data) {
+ desc_count = __copy_desc(ctx, data, data_len);
+ free(data);
+ }
+ }
+
+ if (desc_count < 0) {
+ spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
+ SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
+ SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
+ SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
+ free(ctx);
+ return SPDK_SCSI_TASK_COMPLETE;
+ }
+
+ for (i = ctx->count; i < desc_count; i++) {
+ struct spdk_scsi_unmap_bdesc *desc;
+ uint64_t offset_blocks;
+ uint64_t num_blocks;
+
+ desc = &ctx->desc[i];
+
+ offset_blocks = from_be64(&desc->lba);
+ num_blocks = from_be32(&desc->block_count);
+
+ if (num_blocks == 0) {
+ continue;
+ }
+
+ ctx->count++;
+ rc = spdk_bdev_unmap_blocks(bdev_desc, bdev_ch, offset_blocks, num_blocks,
+ bdev_scsi_task_complete_unmap_cmd, ctx);
+
+ if (rc) {
+ if (rc == -ENOMEM) {
+ bdev_scsi_queue_io(task, bdev_scsi_unmap_resubmit, ctx);
+ /* Unmap was not yet submitted to bdev */
+ ctx->count--;
+ return SPDK_SCSI_TASK_PENDING;
+ }
+ SPDK_ERRLOG("SCSI Unmapping failed\n");
+ spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
+ SPDK_SCSI_SENSE_NO_SENSE,
+ SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
+ SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
+ ctx->count--;
+ /* We can't complete here - we may have to wait for previously
+ * submitted unmaps to complete */
+ break;
+ }
+ }
+
+ if (ctx->count == 0) {
+ free(ctx);
+ return SPDK_SCSI_TASK_COMPLETE;
+ }
+
+ return SPDK_SCSI_TASK_PENDING;
+}
+
+static int
+bdev_scsi_process_block(struct spdk_scsi_task *task)
+{
+ struct spdk_scsi_lun *lun = task->lun;
+ struct spdk_bdev *bdev = lun->bdev;
+ uint64_t lba;
+ uint32_t xfer_len;
+ uint32_t len = 0;
+ uint8_t *cdb = task->cdb;
+
+ /* XXX: We need to support FUA bit for writes! */
+ switch (cdb[0]) {
+ case SPDK_SBC_READ_6:
+ case SPDK_SBC_WRITE_6:
+ lba = (uint64_t)cdb[1] << 16;
+ lba |= (uint64_t)cdb[2] << 8;
+ lba |= (uint64_t)cdb[3];
+ xfer_len = cdb[4];
+ if (xfer_len == 0) {
+ xfer_len = 256;
+ }
+ return bdev_scsi_readwrite(bdev, lun->bdev_desc, lun->io_channel,
+ task, lba, xfer_len,
+ cdb[0] == SPDK_SBC_READ_6);
+
+ case SPDK_SBC_READ_10:
+ case SPDK_SBC_WRITE_10:
+ lba = from_be32(&cdb[2]);
+ xfer_len = from_be16(&cdb[7]);
+ return bdev_scsi_readwrite(bdev, lun->bdev_desc, lun->io_channel,
+ task, lba, xfer_len,
+ cdb[0] == SPDK_SBC_READ_10);
+
+ case SPDK_SBC_READ_12:
+ case SPDK_SBC_WRITE_12:
+ lba = from_be32(&cdb[2]);
+ xfer_len = from_be32(&cdb[6]);
+ return bdev_scsi_readwrite(bdev, lun->bdev_desc, lun->io_channel,
+ task, lba, xfer_len,
+ cdb[0] == SPDK_SBC_READ_12);
+ case SPDK_SBC_READ_16:
+ case SPDK_SBC_WRITE_16:
+ lba = from_be64(&cdb[2]);
+ xfer_len = from_be32(&cdb[10]);
+ return bdev_scsi_readwrite(bdev, lun->bdev_desc, lun->io_channel,
+ task, lba, xfer_len,
+ cdb[0] == SPDK_SBC_READ_16);
+
+ case SPDK_SBC_READ_CAPACITY_10: {
+ uint64_t num_blocks = spdk_bdev_get_num_blocks(bdev);
+ uint8_t buffer[8];
+
+ if (num_blocks - 1 > 0xffffffffULL) {
+ memset(buffer, 0xff, 4);
+ } else {
+ to_be32(buffer, num_blocks - 1);
+ }
+ to_be32(&buffer[4], spdk_bdev_get_data_block_size(bdev));
+
+ len = spdk_min(task->length, sizeof(buffer));
+ if (spdk_scsi_task_scatter_data(task, buffer, len) < 0) {
+ break;
+ }
+
+ task->data_transferred = len;
+ task->status = SPDK_SCSI_STATUS_GOOD;
+ break;
+ }
+
+ case SPDK_SPC_SERVICE_ACTION_IN_16:
+ switch (cdb[1] & 0x1f) { /* SERVICE ACTION */
+ case SPDK_SBC_SAI_READ_CAPACITY_16: {
+ uint8_t buffer[32] = {0};
+
+ to_be64(&buffer[0], spdk_bdev_get_num_blocks(bdev) - 1);
+ to_be32(&buffer[8], spdk_bdev_get_data_block_size(bdev));
+ /*
+ * Set the TPE bit to 1 to indicate thin provisioning.
+ * The position of TPE bit is the 7th bit in 14th byte
+ * in READ CAPACITY (16) parameter data.
+ */
+ if (spdk_bdev_io_type_supported(bdev, SPDK_BDEV_IO_TYPE_UNMAP)) {
+ buffer[14] |= 1 << 7;
+ }
+
+ len = spdk_min(from_be32(&cdb[10]), sizeof(buffer));
+ if (spdk_scsi_task_scatter_data(task, buffer, len) < 0) {
+ break;
+ }
+
+ task->data_transferred = len;
+ task->status = SPDK_SCSI_STATUS_GOOD;
+ break;
+ }
+
+ default:
+ return SPDK_SCSI_TASK_UNKNOWN;
+ }
+ break;
+
+ case SPDK_SBC_SYNCHRONIZE_CACHE_10:
+ case SPDK_SBC_SYNCHRONIZE_CACHE_16:
+ if (cdb[0] == SPDK_SBC_SYNCHRONIZE_CACHE_10) {
+ lba = from_be32(&cdb[2]);
+ len = from_be16(&cdb[7]);
+ } else {
+ lba = from_be64(&cdb[2]);
+ len = from_be32(&cdb[10]);
+ }
+
+ if (len == 0) {
+ len = spdk_bdev_get_num_blocks(bdev) - lba;
+ }
+
+ return bdev_scsi_sync(bdev, lun->bdev_desc, lun->io_channel, task, lba, len);
+ break;
+
+ case SPDK_SBC_UNMAP:
+ return bdev_scsi_unmap(bdev, lun->bdev_desc, lun->io_channel, task, NULL);
+
+ default:
+ return SPDK_SCSI_TASK_UNKNOWN;
+ }
+
+ return SPDK_SCSI_TASK_COMPLETE;
+}
+
+static void
+bdev_scsi_process_block_resubmit(void *arg)
+{
+ struct spdk_scsi_task *task = arg;
+
+ bdev_scsi_process_block(task);
+}
+
+static int
+bdev_scsi_check_len(struct spdk_scsi_task *task, int len, int min_len)
+{
+ if (len >= min_len) {
+ return 0;
+ }
+
+ /* INVALID FIELD IN CDB */
+ spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
+ SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
+ SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
+ SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
+ return -1;
+}
+
+static int
+bdev_scsi_process_primary(struct spdk_scsi_task *task)
+{
+ struct spdk_scsi_lun *lun = task->lun;
+ struct spdk_bdev *bdev = lun->bdev;
+ int alloc_len = -1;
+ int data_len = -1;
+ uint8_t *cdb = task->cdb;
+ uint8_t *data = NULL;
+ int rc = 0;
+ int pllen, md = 0;
+ int llba;
+ int dbd, pc, page, subpage;
+ int cmd_parsed = 0;
+
+ switch (cdb[0]) {
+ case SPDK_SPC_INQUIRY:
+ alloc_len = from_be16(&cdb[3]);
+ data_len = spdk_max(4096, alloc_len);
+ data = calloc(1, data_len);
+ assert(data != NULL);
+ rc = bdev_scsi_inquiry(bdev, task, cdb, data, data_len);
+ data_len = spdk_min(rc, data_len);
+ if (rc < 0) {
+ break;
+ }
+
+ SPDK_LOGDUMP(SPDK_LOG_SCSI, "INQUIRY", data, data_len);
+ break;
+
+ case SPDK_SPC_REPORT_LUNS: {
+ int sel;
+
+ sel = cdb[2];
+ SPDK_DEBUGLOG(SPDK_LOG_SCSI, "sel=%x\n", sel);
+
+ alloc_len = from_be32(&cdb[6]);
+ rc = bdev_scsi_check_len(task, alloc_len, 16);
+ if (rc < 0) {
+ break;
+ }
+
+ data_len = spdk_max(4096, alloc_len);
+ data = calloc(1, data_len);
+ assert(data != NULL);
+ rc = bdev_scsi_report_luns(task->lun, sel, data, data_len);
+ data_len = rc;
+ if (rc < 0) {
+ spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
+ SPDK_SCSI_SENSE_NO_SENSE,
+ SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
+ SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
+ break;
+ }
+
+ SPDK_LOGDUMP(SPDK_LOG_SCSI, "REPORT LUNS", data, data_len);
+ break;
+ }
+
+ case SPDK_SPC_MODE_SELECT_6:
+ case SPDK_SPC_MODE_SELECT_10:
+ if (cdb[0] == SPDK_SPC_MODE_SELECT_6) {
+ /* MODE_SELECT(6) must have at least a 4 byte header. */
+ md = 4;
+ pllen = cdb[4];
+ } else {
+ /* MODE_SELECT(10) must have at least an 8 byte header. */
+ md = 8;
+ pllen = from_be16(&cdb[7]);
+ }
+
+ if (pllen == 0) {
+ break;
+ }
+
+ rc = bdev_scsi_check_len(task, pllen, md);
+ if (rc < 0) {
+ break;
+ }
+
+ data = spdk_scsi_task_gather_data(task, &rc);
+ if (rc < 0) {
+ break;
+ }
+ data_len = rc;
+
+ rc = bdev_scsi_check_len(task, data_len, spdk_max(pllen, md));
+ if (rc < 0) {
+ break;
+ }
+
+ rc = pllen;
+ data_len = 0;
+ break;
+
+ case SPDK_SPC_MODE_SENSE_6:
+ alloc_len = cdb[4];
+ md = 6;
+ /* FALLTHROUGH */
+ case SPDK_SPC_MODE_SENSE_10:
+ llba = 0;
+
+ if (md == 0) {
+ alloc_len = from_be16(&cdb[7]);
+ llba = !!(cdb[1] & 0x10);
+ md = 10;
+ }
+
+ dbd = !!(cdb[1] & 0x8);
+ pc = (cdb[2] & 0xc0) >> 6;
+ page = cdb[2] & 0x3f;
+ subpage = cdb[3];
+
+ /* First call with no buffer to discover needed buffer size */
+ rc = bdev_scsi_mode_sense(bdev, md,
+ cdb, dbd, llba, pc,
+ page, subpage,
+ NULL, task);
+ if (rc < 0) {
+ break;
+ }
+
+ data_len = rc;
+ data = calloc(1, data_len);
+ assert(data != NULL);
+
+ /* First call with no buffer to discover needed buffer size */
+ rc = bdev_scsi_mode_sense(bdev, md,
+ cdb, dbd, llba, pc,
+ page, subpage,
+ data, task);
+ if (rc < 0) {
+ /* INVALID FIELD IN CDB */
+ spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
+ SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
+ SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
+ SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
+ break;
+ }
+ break;
+
+ case SPDK_SPC_REQUEST_SENSE: {
+ int desc;
+ int sk, asc, ascq;
+
+ desc = cdb[1] & 0x1;
+ if (desc != 0) {
+ /* INVALID FIELD IN CDB */
+ spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
+ SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
+ SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
+ SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
+ break;
+ }
+
+ alloc_len = cdb[4];
+
+ /* NO ADDITIONAL SENSE INFORMATION */
+ sk = SPDK_SCSI_SENSE_NO_SENSE;
+ asc = 0x00;
+ ascq = 0x00;
+
+ spdk_scsi_task_build_sense_data(task, sk, asc, ascq);
+
+ data_len = task->sense_data_len;
+ data = calloc(1, data_len);
+ assert(data != NULL);
+ memcpy(data, task->sense_data, data_len);
+ break;
+ }
+
+ case SPDK_SPC_LOG_SELECT:
+ SPDK_DEBUGLOG(SPDK_LOG_SCSI, "LOG_SELECT\n");
+ cmd_parsed = 1;
+ /* FALLTHROUGH */
+ case SPDK_SPC_LOG_SENSE:
+ if (!cmd_parsed) {
+ SPDK_DEBUGLOG(SPDK_LOG_SCSI, "LOG_SENSE\n");
+ }
+
+ /* INVALID COMMAND OPERATION CODE */
+ spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
+ SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
+ SPDK_SCSI_ASC_INVALID_COMMAND_OPERATION_CODE,
+ SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
+ rc = -1;
+ break;
+
+ case SPDK_SPC_TEST_UNIT_READY:
+ SPDK_DEBUGLOG(SPDK_LOG_SCSI, "TEST_UNIT_READY\n");
+ cmd_parsed = 1;
+ /* FALLTHROUGH */
+ case SPDK_SBC_START_STOP_UNIT:
+ if (!cmd_parsed) {
+ SPDK_DEBUGLOG(SPDK_LOG_SCSI, "START_STOP_UNIT\n");
+ }
+
+ rc = 0;
+ break;
+
+ case SPDK_SPC_PERSISTENT_RESERVE_OUT:
+ pllen = from_be32(&cdb[5]);
+ rc = bdev_scsi_check_len(task, pllen, 24);
+ if (rc < 0) {
+ break;
+ }
+
+ data = spdk_scsi_task_gather_data(task, &rc);
+ if (rc < 0) {
+ break;
+ }
+ data_len = rc;
+ if (data_len < 24) {
+ rc = -1;
+ break;
+ }
+
+ rc = scsi_pr_out(task, cdb, data, data_len);
+ if (rc < 0) {
+ break;
+ }
+ rc = pllen;
+ data_len = 0;
+ break;
+
+ case SPDK_SPC_PERSISTENT_RESERVE_IN:
+ alloc_len = from_be16(&cdb[7]);
+ data_len = alloc_len;
+ data = calloc(1, data_len);
+ assert(data != NULL);
+ rc = scsi_pr_in(task, cdb, data, data_len);
+ break;
+
+ case SPDK_SPC2_RESERVE_6:
+ case SPDK_SPC2_RESERVE_10:
+ rc = scsi2_reserve(task, cdb);
+ if (rc == 0) {
+ if (cdb[0] == SPDK_SPC2_RESERVE_10) {
+ rc = from_be16(&cdb[7]);
+ }
+ data_len = 0;
+ }
+ break;
+
+ case SPDK_SPC2_RELEASE_6:
+ case SPDK_SPC2_RELEASE_10:
+ rc = scsi2_release(task);
+ break;
+
+ default:
+ return SPDK_SCSI_TASK_UNKNOWN;
+ }
+
+ if (rc >= 0 && data_len > 0) {
+ assert(alloc_len >= 0);
+ spdk_scsi_task_scatter_data(task, data, spdk_min(alloc_len, data_len));
+ rc = spdk_min(data_len, alloc_len);
+ }
+
+ if (rc >= 0) {
+ task->data_transferred = rc;
+ task->status = SPDK_SCSI_STATUS_GOOD;
+ }
+
+ if (data) {
+ free(data);
+ }
+
+ return SPDK_SCSI_TASK_COMPLETE;
+}
+
+int
+bdev_scsi_execute(struct spdk_scsi_task *task)
+{
+ int rc;
+
+ if ((rc = bdev_scsi_process_block(task)) == SPDK_SCSI_TASK_UNKNOWN) {
+ if ((rc = bdev_scsi_process_primary(task)) == SPDK_SCSI_TASK_UNKNOWN) {
+ SPDK_DEBUGLOG(SPDK_LOG_SCSI, "unsupported SCSI OP=0x%x\n", task->cdb[0]);
+ /* INVALID COMMAND OPERATION CODE */
+ spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
+ SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
+ SPDK_SCSI_ASC_INVALID_COMMAND_OPERATION_CODE,
+ SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
+ return SPDK_SCSI_TASK_COMPLETE;
+ }
+ }
+
+ return rc;
+}
+
+static void
+bdev_scsi_reset_resubmit(void *arg)
+{
+ struct spdk_scsi_task *task = arg;
+
+ bdev_scsi_reset(task);
+}
+
+void
+bdev_scsi_reset(struct spdk_scsi_task *task)
+{
+ struct spdk_scsi_lun *lun = task->lun;
+ int rc;
+
+ rc = spdk_bdev_reset(lun->bdev_desc, lun->io_channel, bdev_scsi_task_complete_reset,
+ task);
+ if (rc == -ENOMEM) {
+ bdev_scsi_queue_io(task, bdev_scsi_reset_resubmit, task);
+ }
+}
+
+bool
+bdev_scsi_get_dif_ctx(struct spdk_bdev *bdev, struct spdk_scsi_task *task,
+ struct spdk_dif_ctx *dif_ctx)
+{
+ uint32_t ref_tag = 0, dif_check_flags = 0, data_offset;
+ uint8_t *cdb;
+ int rc;
+
+ if (spdk_likely(spdk_bdev_get_md_size(bdev) == 0)) {
+ return false;
+ }
+
+ cdb = task->cdb;
+ data_offset = task->offset;
+
+ /* We use lower 32 bits of LBA as Reference. Tag */
+ switch (cdb[0]) {
+ case SPDK_SBC_READ_6:
+ case SPDK_SBC_WRITE_6:
+ ref_tag = (uint32_t)cdb[1] << 16;
+ ref_tag |= (uint32_t)cdb[2] << 8;
+ ref_tag |= (uint32_t)cdb[3];
+ break;
+ case SPDK_SBC_READ_10:
+ case SPDK_SBC_WRITE_10:
+ case SPDK_SBC_READ_12:
+ case SPDK_SBC_WRITE_12:
+ ref_tag = from_be32(&cdb[2]);
+ break;
+ case SPDK_SBC_READ_16:
+ case SPDK_SBC_WRITE_16:
+ ref_tag = (uint32_t)from_be64(&cdb[2]);
+ break;
+ default:
+ return false;
+ }
+
+ if (spdk_bdev_is_dif_check_enabled(bdev, SPDK_DIF_CHECK_TYPE_REFTAG)) {
+ dif_check_flags |= SPDK_DIF_FLAGS_REFTAG_CHECK;
+ }
+
+ if (spdk_bdev_is_dif_check_enabled(bdev, SPDK_DIF_CHECK_TYPE_GUARD)) {
+ dif_check_flags |= SPDK_DIF_FLAGS_GUARD_CHECK;
+ }
+
+ rc = spdk_dif_ctx_init(dif_ctx,
+ spdk_bdev_get_block_size(bdev),
+ spdk_bdev_get_md_size(bdev),
+ spdk_bdev_is_md_interleaved(bdev),
+ spdk_bdev_is_dif_head_of_md(bdev),
+ spdk_bdev_get_dif_type(bdev),
+ dif_check_flags,
+ ref_tag, 0, 0, data_offset, 0);
+
+ return (rc == 0) ? true : false;
+}
diff --git a/src/spdk/lib/scsi/scsi_internal.h b/src/spdk/lib/scsi/scsi_internal.h
new file mode 100644
index 000000000..2da3a99a8
--- /dev/null
+++ b/src/spdk/lib/scsi/scsi_internal.h
@@ -0,0 +1,214 @@
+/*-
+ * 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 "spdk/stdinc.h"
+
+#include "spdk/bdev.h"
+#include "spdk/scsi.h"
+#include "spdk/scsi_spec.h"
+#include "spdk/trace.h"
+#include "spdk/dif.h"
+
+#include "spdk_internal/log.h"
+
+enum {
+ SPDK_SCSI_TASK_UNKNOWN = -1,
+ SPDK_SCSI_TASK_COMPLETE,
+ SPDK_SCSI_TASK_PENDING,
+};
+
+struct spdk_scsi_port {
+ uint8_t is_used;
+ uint64_t id;
+ uint16_t index;
+ uint16_t transport_id_len;
+ char transport_id[SPDK_SCSI_MAX_TRANSPORT_ID_LENGTH];
+ char name[SPDK_SCSI_PORT_MAX_NAME_LENGTH];
+};
+
+/* Registrant with I_T nextus */
+struct spdk_scsi_pr_registrant {
+ uint64_t rkey;
+ uint16_t relative_target_port_id;
+ uint16_t transport_id_len;
+ char transport_id[SPDK_SCSI_MAX_TRANSPORT_ID_LENGTH];
+ char initiator_port_name[SPDK_SCSI_PORT_MAX_NAME_LENGTH];
+ char target_port_name[SPDK_SCSI_PORT_MAX_NAME_LENGTH];
+ struct spdk_scsi_port *initiator_port;
+ struct spdk_scsi_port *target_port;
+ TAILQ_ENTRY(spdk_scsi_pr_registrant) link;
+};
+
+#define SCSI_SPC2_RESERVE 0x00000001U
+
+/* Reservation with LU_SCOPE */
+struct spdk_scsi_pr_reservation {
+ uint32_t flags;
+ struct spdk_scsi_pr_registrant *holder;
+ enum spdk_scsi_pr_type_code rtype;
+ uint64_t crkey;
+};
+
+struct spdk_scsi_dev {
+ int id;
+ int is_allocated;
+ bool removed;
+ spdk_scsi_dev_destruct_cb_t remove_cb;
+ void *remove_ctx;
+
+ char name[SPDK_SCSI_DEV_MAX_NAME + 1];
+
+ struct spdk_scsi_lun *lun[SPDK_SCSI_DEV_MAX_LUN];
+
+ int num_ports;
+ struct spdk_scsi_port port[SPDK_SCSI_DEV_MAX_PORTS];
+
+ uint8_t protocol_id;
+};
+
+struct spdk_scsi_lun_desc {
+ struct spdk_scsi_lun *lun;
+ spdk_scsi_lun_remove_cb_t hotremove_cb;
+ void *hotremove_ctx;
+ TAILQ_ENTRY(spdk_scsi_lun_desc) link;
+};
+
+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 bdev associated with this LUN. */
+ struct spdk_bdev *bdev;
+
+ /** Descriptor for opened block device. */
+ struct spdk_bdev_desc *bdev_desc;
+
+ /** The thread which opens this LUN. */
+ struct spdk_thread *thread;
+
+ /** I/O channel for the bdev associated with this LUN. */
+ struct spdk_io_channel *io_channel;
+
+ /** The reference number for this LUN, thus we can correctly free the io_channel */
+ uint32_t ref;
+
+ /** Poller to release the resource of the lun when it is hot removed */
+ struct spdk_poller *hotremove_poller;
+
+ /** The LUN is removed */
+ bool removed;
+
+ /** Callback to be fired when LUN removal is first triggered. */
+ void (*hotremove_cb)(const struct spdk_scsi_lun *lun, void *arg);
+
+ /** Argument for hotremove_cb */
+ void *hotremove_ctx;
+
+ /** Registrant head for I_T nexus */
+ TAILQ_HEAD(, spdk_scsi_pr_registrant) reg_head;
+ /** Persistent Reservation Generation */
+ uint32_t pr_generation;
+ /** Reservation for the LUN */
+ struct spdk_scsi_pr_reservation reservation;
+ /** Reservation holder for SPC2 RESERVE(6) and RESERVE(10) */
+ struct spdk_scsi_pr_registrant scsi2_holder;
+
+ /** List of open descriptors for this LUN. */
+ TAILQ_HEAD(, spdk_scsi_lun_desc) open_descs;
+
+ /** submitted tasks */
+ TAILQ_HEAD(tasks, spdk_scsi_task) tasks;
+
+ /** pending tasks */
+ TAILQ_HEAD(pending_tasks, spdk_scsi_task) pending_tasks;
+
+ /** submitted management tasks */
+ TAILQ_HEAD(mgmt_tasks, spdk_scsi_task) mgmt_tasks;
+
+ /** pending management tasks */
+ TAILQ_HEAD(pending_mgmt_tasks, spdk_scsi_task) pending_mgmt_tasks;
+
+ /** poller to check completion of tasks prior to reset */
+ struct spdk_poller *reset_poller;
+};
+
+struct spdk_scsi_lun *scsi_lun_construct(struct spdk_bdev *bdev,
+ void (*hotremove_cb)(const struct spdk_scsi_lun *, void *),
+ void *hotremove_ctx);
+void scsi_lun_destruct(struct spdk_scsi_lun *lun);
+
+void scsi_lun_execute_task(struct spdk_scsi_lun *lun, struct spdk_scsi_task *task);
+void scsi_lun_execute_mgmt_task(struct spdk_scsi_lun *lun, struct spdk_scsi_task *task);
+bool scsi_lun_has_pending_mgmt_tasks(const struct spdk_scsi_lun *lun,
+ const struct spdk_scsi_port *initiator_port);
+void scsi_lun_complete_task(struct spdk_scsi_lun *lun, struct spdk_scsi_task *task);
+void scsi_lun_complete_reset_task(struct spdk_scsi_lun *lun, struct spdk_scsi_task *task);
+bool scsi_lun_has_pending_tasks(const struct spdk_scsi_lun *lun,
+ const struct spdk_scsi_port *initiator_port);
+int scsi_lun_allocate_io_channel(struct spdk_scsi_lun *lun);
+void scsi_lun_free_io_channel(struct spdk_scsi_lun *lun);
+
+struct spdk_scsi_dev *scsi_dev_get_list(void);
+
+int scsi_port_construct(struct spdk_scsi_port *port, uint64_t id,
+ uint16_t index, const char *name);
+void scsi_port_destruct(struct spdk_scsi_port *port);
+
+int bdev_scsi_execute(struct spdk_scsi_task *task);
+void bdev_scsi_reset(struct spdk_scsi_task *task);
+
+bool bdev_scsi_get_dif_ctx(struct spdk_bdev *bdev, struct spdk_scsi_task *task,
+ struct spdk_dif_ctx *dif_ctx);
+
+int scsi_pr_out(struct spdk_scsi_task *task, uint8_t *cdb, uint8_t *data, uint16_t data_len);
+int scsi_pr_in(struct spdk_scsi_task *task, uint8_t *cdb, uint8_t *data, uint16_t data_len);
+int scsi_pr_check(struct spdk_scsi_task *task);
+
+int scsi2_reserve(struct spdk_scsi_task *task, uint8_t *cdb);
+int scsi2_release(struct spdk_scsi_task *task);
+int scsi2_reserve_check(struct spdk_scsi_task *task);
+
+struct spdk_scsi_globals {
+ pthread_mutex_t mutex;
+};
+
+extern struct spdk_scsi_globals g_scsi;
+
+#endif /* SPDK_SCSI_INTERNAL_H */
diff --git a/src/spdk/lib/scsi/scsi_pr.c b/src/spdk/lib/scsi/scsi_pr.c
new file mode 100644
index 000000000..4e17cc2c6
--- /dev/null
+++ b/src/spdk/lib/scsi/scsi_pr.c
@@ -0,0 +1,1067 @@
+/*-
+ * 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 "scsi_internal.h"
+
+#include "spdk/endian.h"
+
+/* Get registrant by I_T nexus */
+static struct spdk_scsi_pr_registrant *
+scsi_pr_get_registrant(struct spdk_scsi_lun *lun,
+ struct spdk_scsi_port *initiator_port,
+ struct spdk_scsi_port *target_port)
+{
+ struct spdk_scsi_pr_registrant *reg, *tmp;
+
+ TAILQ_FOREACH_SAFE(reg, &lun->reg_head, link, tmp) {
+ if (initiator_port == reg->initiator_port &&
+ target_port == reg->target_port) {
+ return reg;
+ }
+ }
+
+ return NULL;
+}
+
+static bool
+scsi2_it_nexus_is_holder(struct spdk_scsi_lun *lun,
+ struct spdk_scsi_port *initiator_port,
+ struct spdk_scsi_port *target_port)
+{
+ struct spdk_scsi_pr_registrant *reg = lun->reservation.holder;
+
+ assert(reg != NULL);
+
+ if ((reg->initiator_port == initiator_port) &&
+ (reg->target_port == target_port)) {
+ return true;
+ }
+
+ return false;
+}
+
+/* Reservation type is all registrants or not */
+static inline bool
+scsi_pr_is_all_registrants_type(struct spdk_scsi_lun *lun)
+{
+ return (lun->reservation.rtype == SPDK_SCSI_PR_WRITE_EXCLUSIVE_ALL_REGS ||
+ lun->reservation.rtype == SPDK_SCSI_PR_EXCLUSIVE_ACCESS_ALL_REGS);
+}
+
+/* Registrant is reservation holder or not */
+static inline bool
+scsi_pr_registrant_is_holder(struct spdk_scsi_lun *lun,
+ struct spdk_scsi_pr_registrant *reg)
+{
+ if (scsi_pr_is_all_registrants_type(lun)) {
+ return true;
+ }
+
+ return (lun->reservation.holder == reg);
+}
+
+/* LUN holds a reservation or not */
+static inline bool
+scsi_pr_has_reservation(struct spdk_scsi_lun *lun)
+{
+ return !(lun->reservation.holder == NULL);
+}
+
+static int
+scsi_pr_register_registrant(struct spdk_scsi_lun *lun,
+ struct spdk_scsi_port *initiator_port,
+ struct spdk_scsi_port *target_port,
+ uint64_t sa_rkey)
+{
+ struct spdk_scsi_pr_registrant *reg;
+
+ /* Register sa_rkey with the I_T nexus */
+ reg = calloc(1, sizeof(*reg));
+ if (!reg) {
+ return -ENOMEM;
+ }
+
+ SPDK_DEBUGLOG(SPDK_LOG_SCSI, "REGISTER: new registrant registered "
+ "with key 0x%"PRIx64"\n", sa_rkey);
+
+ /* New I_T nexus */
+ reg->initiator_port = initiator_port;
+ if (initiator_port) {
+ snprintf(reg->initiator_port_name, sizeof(reg->initiator_port_name), "%s",
+ initiator_port->name);
+ reg->transport_id_len = initiator_port->transport_id_len;
+ memcpy(reg->transport_id, initiator_port->transport_id, reg->transport_id_len);
+ }
+ reg->target_port = target_port;
+ if (target_port) {
+ snprintf(reg->target_port_name, sizeof(reg->target_port_name), "%s",
+ target_port->name);
+ reg->relative_target_port_id = target_port->index;
+ }
+ reg->rkey = sa_rkey;
+ TAILQ_INSERT_TAIL(&lun->reg_head, reg, link);
+ lun->pr_generation++;
+
+ return 0;
+}
+
+static void
+scsi_pr_release_reservation(struct spdk_scsi_lun *lun, struct spdk_scsi_pr_registrant *reg)
+{
+ bool all_regs = false;
+
+ SPDK_DEBUGLOG(SPDK_LOG_SCSI, "REGISTER: release reservation "
+ "with type %u\n", lun->reservation.rtype);
+
+ /* TODO: Unit Attention */
+ all_regs = scsi_pr_is_all_registrants_type(lun);
+ if (all_regs && !TAILQ_EMPTY(&lun->reg_head)) {
+ lun->reservation.holder = TAILQ_FIRST(&lun->reg_head);
+ return;
+ }
+
+ memset(&lun->reservation, 0, sizeof(struct spdk_scsi_pr_reservation));
+}
+
+static void
+scsi_pr_reserve_reservation(struct spdk_scsi_lun *lun,
+ enum spdk_scsi_pr_type_code type,
+ uint64_t rkey,
+ struct spdk_scsi_pr_registrant *holder)
+{
+ lun->reservation.rtype = type;
+ lun->reservation.crkey = rkey;
+ lun->reservation.holder = holder;
+}
+
+static void
+scsi_pr_unregister_registrant(struct spdk_scsi_lun *lun,
+ struct spdk_scsi_pr_registrant *reg)
+{
+ SPDK_DEBUGLOG(SPDK_LOG_SCSI, "REGISTER: unregister registrant\n");
+
+ TAILQ_REMOVE(&lun->reg_head, reg, link);
+ if (scsi_pr_registrant_is_holder(lun, reg)) {
+ scsi_pr_release_reservation(lun, reg);
+ }
+
+ free(reg);
+ lun->pr_generation++;
+}
+
+static void
+scsi_pr_replace_registrant_key(struct spdk_scsi_lun *lun,
+ struct spdk_scsi_pr_registrant *reg,
+ uint64_t sa_rkey)
+{
+ SPDK_DEBUGLOG(SPDK_LOG_SCSI, "REGISTER: replace with new "
+ "reservation key 0x%"PRIx64"\n", sa_rkey);
+ reg->rkey = sa_rkey;
+ lun->pr_generation++;
+}
+
+static int
+scsi_pr_out_reserve(struct spdk_scsi_task *task,
+ enum spdk_scsi_pr_type_code rtype, uint64_t rkey,
+ uint8_t spec_i_pt, uint8_t all_tg_pt, uint8_t aptpl)
+{
+ struct spdk_scsi_lun *lun = task->lun;
+ struct spdk_scsi_pr_registrant *reg;
+
+ SPDK_DEBUGLOG(SPDK_LOG_SCSI, "PR OUT RESERVE: rkey 0x%"PRIx64", requested "
+ "reservation type %u, type %u\n", rkey, rtype, lun->reservation.rtype);
+
+ /* TODO: don't support now */
+ if (spec_i_pt || all_tg_pt || aptpl) {
+ SPDK_ERRLOG("Unspported spec_i_pt/all_tg_pt fields "
+ "or invalid aptpl field\n");
+ spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
+ SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
+ SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
+ SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
+ return -EINVAL;
+ }
+
+ reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
+ /* No registration for the I_T nexus */
+ if (!reg) {
+ SPDK_ERRLOG("No registration\n");
+ goto conflict;
+ }
+
+ /* invalid reservation key */
+ if (reg->rkey != rkey) {
+ SPDK_ERRLOG("Reservation key 0x%"PRIx64" don't match 0x%"PRIx64"\n",
+ rkey, reg->rkey);
+ goto conflict;
+ }
+
+ /* reservation holder already exists */
+ if (scsi_pr_has_reservation(lun)) {
+ if (rtype != lun->reservation.rtype) {
+ SPDK_ERRLOG("Reservation type doesn't match\n");
+ goto conflict;
+ }
+
+ if (!scsi_pr_registrant_is_holder(lun, reg)) {
+ SPDK_ERRLOG("Only 1 holder is allowed for type %u\n", rtype);
+ goto conflict;
+ }
+ } else {
+ /* current I_T nexus is the first reservation holder */
+ scsi_pr_reserve_reservation(lun, rtype, rkey, reg);
+ }
+
+ return 0;
+
+conflict:
+ spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_RESERVATION_CONFLICT,
+ SPDK_SCSI_SENSE_NO_SENSE,
+ SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
+ SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
+ return -EINVAL;
+}
+
+static int
+scsi_pr_out_register(struct spdk_scsi_task *task,
+ enum spdk_scsi_pr_out_service_action_code action,
+ uint64_t rkey, uint64_t sa_rkey,
+ uint8_t spec_i_pt, uint8_t all_tg_pt, uint8_t aptpl)
+{
+ struct spdk_scsi_lun *lun = task->lun;
+ struct spdk_scsi_pr_registrant *reg;
+ int sc, sk, asc;
+
+ SPDK_DEBUGLOG(SPDK_LOG_SCSI, "PR OUT REGISTER: rkey 0x%"PRIx64", "
+ "sa_key 0x%"PRIx64", reservation type %u\n", rkey, sa_rkey, lun->reservation.rtype);
+
+ /* TODO: don't support now */
+ if (spec_i_pt || all_tg_pt || aptpl) {
+ SPDK_ERRLOG("Unsupported spec_i_pt/all_tg_pt/aptpl field\n");
+ sc = SPDK_SCSI_STATUS_CHECK_CONDITION;
+ sk = SPDK_SCSI_SENSE_ILLEGAL_REQUEST;
+ asc = SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB;
+ goto error_exit;
+ }
+
+ reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
+ /* an unregistered I_T nexus session */
+ if (!reg) {
+ if (rkey && (action == SPDK_SCSI_PR_OUT_REGISTER)) {
+ SPDK_ERRLOG("Reservation key field is not empty\n");
+ sc = SPDK_SCSI_STATUS_RESERVATION_CONFLICT;
+ sk = SPDK_SCSI_SENSE_NO_SENSE;
+ asc = SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE;
+ goto error_exit;
+ }
+
+ if (!sa_rkey) {
+ /* Do nothing except return GOOD status */
+ SPDK_DEBUGLOG(SPDK_LOG_SCSI, "REGISTER: service action "
+ "reservation key is zero, do noting\n");
+ return 0;
+ }
+ /* Add a new registrant for the I_T nexus */
+ return scsi_pr_register_registrant(lun, task->initiator_port,
+ task->target_port, sa_rkey);
+ } else {
+ /* a registered I_T nexus */
+ if (rkey != reg->rkey && action == SPDK_SCSI_PR_OUT_REGISTER) {
+ SPDK_ERRLOG("Reservation key 0x%"PRIx64" don't match "
+ "registrant's key 0x%"PRIx64"\n", rkey, reg->rkey);
+ sc = SPDK_SCSI_STATUS_RESERVATION_CONFLICT;
+ sk = SPDK_SCSI_SENSE_NO_SENSE;
+ asc = SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE;
+ goto error_exit;
+ }
+
+ if (!sa_rkey) {
+ /* unregister */
+ scsi_pr_unregister_registrant(lun, reg);
+ } else {
+ /* replace */
+ scsi_pr_replace_registrant_key(lun, reg, sa_rkey);
+ }
+ }
+
+ return 0;
+
+error_exit:
+ spdk_scsi_task_set_status(task, sc, sk, asc, SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE);
+ return -EINVAL;
+}
+
+static int
+scsi_pr_out_release(struct spdk_scsi_task *task,
+ enum spdk_scsi_pr_type_code rtype, uint64_t rkey)
+{
+ struct spdk_scsi_lun *lun = task->lun;
+ struct spdk_scsi_pr_registrant *reg;
+ int sk, asc;
+
+ SPDK_DEBUGLOG(SPDK_LOG_SCSI, "PR OUT RELEASE: rkey 0x%"PRIx64", "
+ "reservation type %u\n", rkey, rtype);
+
+ reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
+ if (!reg) {
+ SPDK_ERRLOG("No registration\n");
+ sk = SPDK_SCSI_SENSE_NOT_READY;
+ asc = SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE;
+ goto check_condition;
+ }
+
+ /* no reservation holder */
+ if (!scsi_pr_has_reservation(lun)) {
+ SPDK_DEBUGLOG(SPDK_LOG_SCSI, "RELEASE: no reservation holder\n");
+ return 0;
+ }
+
+ if (lun->reservation.rtype != rtype || rkey != lun->reservation.crkey) {
+ sk = SPDK_SCSI_SENSE_ILLEGAL_REQUEST;
+ asc = SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB;
+ goto check_condition;
+ }
+
+ /* I_T nexus is not a persistent reservation holder */
+ if (!scsi_pr_registrant_is_holder(lun, reg)) {
+ SPDK_DEBUGLOG(SPDK_LOG_SCSI, "RELEASE: current I_T nexus is not holder\n");
+ return 0;
+ }
+
+ scsi_pr_release_reservation(lun, reg);
+
+ return 0;
+
+check_condition:
+ spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION, sk, asc,
+ SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
+ return -EINVAL;
+}
+
+static int
+scsi_pr_out_clear(struct spdk_scsi_task *task, uint64_t rkey)
+{
+ struct spdk_scsi_lun *lun = task->lun;
+ struct spdk_scsi_pr_registrant *reg, *tmp;
+ int sc, sk, asc;
+
+ SPDK_DEBUGLOG(SPDK_LOG_SCSI, "PR OUT CLEAR: rkey 0x%"PRIx64"\n", rkey);
+
+ reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
+ if (!reg) {
+ SPDK_ERRLOG("No registration\n");
+ sc = SPDK_SCSI_STATUS_CHECK_CONDITION;
+ sk = SPDK_SCSI_SENSE_NOT_READY;
+ asc = SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE;
+ goto error_exit;
+ }
+
+ if (rkey != reg->rkey) {
+ SPDK_ERRLOG("Reservation key 0x%"PRIx64" doesn't match "
+ "registrant's key 0x%"PRIx64"\n", rkey, reg->rkey);
+ sc = SPDK_SCSI_STATUS_RESERVATION_CONFLICT;
+ sk = SPDK_SCSI_SENSE_NO_SENSE;
+ asc = SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE;
+ goto error_exit;
+ }
+
+ TAILQ_FOREACH_SAFE(reg, &lun->reg_head, link, tmp) {
+ scsi_pr_unregister_registrant(lun, reg);
+ }
+
+ return 0;
+
+error_exit:
+ spdk_scsi_task_set_status(task, sc, sk, asc, SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
+ return -EINVAL;
+}
+
+static void
+scsi_pr_remove_all_regs_by_key(struct spdk_scsi_lun *lun, uint64_t sa_rkey)
+{
+ struct spdk_scsi_pr_registrant *reg, *tmp;
+
+ TAILQ_FOREACH_SAFE(reg, &lun->reg_head, link, tmp) {
+ if (reg->rkey == sa_rkey) {
+ scsi_pr_unregister_registrant(lun, reg);
+ }
+ }
+}
+
+static void
+scsi_pr_remove_all_other_regs(struct spdk_scsi_lun *lun, struct spdk_scsi_pr_registrant *reg)
+{
+ struct spdk_scsi_pr_registrant *reg_tmp, *reg_tmp2;
+
+ TAILQ_FOREACH_SAFE(reg_tmp, &lun->reg_head, link, reg_tmp2) {
+ if (reg_tmp != reg) {
+ scsi_pr_unregister_registrant(lun, reg_tmp);
+ }
+ }
+}
+
+static int
+scsi_pr_out_preempt(struct spdk_scsi_task *task,
+ enum spdk_scsi_pr_out_service_action_code action,
+ enum spdk_scsi_pr_type_code rtype,
+ uint64_t rkey, uint64_t sa_rkey)
+{
+ struct spdk_scsi_lun *lun = task->lun;
+ struct spdk_scsi_pr_registrant *reg;
+ bool all_regs = false;
+
+ SPDK_DEBUGLOG(SPDK_LOG_SCSI, "PR OUT PREEMPT: rkey 0x%"PRIx64", sa_rkey 0x%"PRIx64" "
+ "action %u, type %u, reservation type %u\n",
+ rkey, sa_rkey, action, rtype, lun->reservation.rtype);
+
+ /* I_T nexus is not registered */
+ reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
+ if (!reg) {
+ SPDK_ERRLOG("No registration\n");
+ goto conflict;
+ }
+ if (rkey != reg->rkey) {
+ SPDK_ERRLOG("Reservation key 0x%"PRIx64" doesn't match "
+ "registrant's key 0x%"PRIx64"\n", rkey, reg->rkey);
+ goto conflict;
+ }
+
+ /* no persistent reservation */
+ if (!scsi_pr_has_reservation(lun)) {
+ scsi_pr_remove_all_regs_by_key(lun, sa_rkey);
+ SPDK_DEBUGLOG(SPDK_LOG_SCSI, "PREEMPT: no persistent reservation\n");
+ goto exit;
+ }
+
+ all_regs = scsi_pr_is_all_registrants_type(lun);
+
+ if (all_regs) {
+ if (sa_rkey != 0) {
+ scsi_pr_remove_all_regs_by_key(lun, sa_rkey);
+ SPDK_DEBUGLOG(SPDK_LOG_SCSI, "PREEMPT: All registrants type with sa_rkey\n");
+ } else {
+ /* remove all other registrants and release persistent reservation if any */
+ scsi_pr_remove_all_other_regs(lun, reg);
+ /* create persistent reservation using new type and scope */
+ scsi_pr_reserve_reservation(lun, rtype, 0, reg);
+ SPDK_DEBUGLOG(SPDK_LOG_SCSI, "PREEMPT: All registrants type with sa_rkey zeroed\n");
+ }
+ goto exit;
+ }
+
+ assert(lun->reservation.crkey != 0);
+
+ if (sa_rkey != lun->reservation.crkey) {
+ if (!sa_rkey) {
+ SPDK_ERRLOG("Zeroed sa_rkey\n");
+ spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
+ SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
+ SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
+ SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
+ return -EINVAL;
+ }
+ scsi_pr_remove_all_regs_by_key(lun, sa_rkey);
+ goto exit;
+ }
+
+ if (scsi_pr_registrant_is_holder(lun, reg)) {
+ scsi_pr_reserve_reservation(lun, rtype, rkey, reg);
+ SPDK_DEBUGLOG(SPDK_LOG_SCSI, "PREEMPT: preempt itself with type %u\n", rtype);
+ goto exit;
+ }
+
+ /* unregister registrants if any */
+ scsi_pr_remove_all_regs_by_key(lun, sa_rkey);
+ reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
+ if (!reg) {
+ SPDK_ERRLOG("Current I_T nexus registrant was removed\n");
+ goto conflict;
+ }
+
+ /* preempt the holder */
+ scsi_pr_reserve_reservation(lun, rtype, rkey, reg);
+
+exit:
+ lun->pr_generation++;
+ return 0;
+
+conflict:
+ spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_RESERVATION_CONFLICT,
+ SPDK_SCSI_SENSE_NO_SENSE,
+ SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
+ SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
+ return -EINVAL;
+}
+
+int
+scsi_pr_out(struct spdk_scsi_task *task, uint8_t *cdb,
+ uint8_t *data, uint16_t data_len)
+{
+ int rc = -1;
+ uint64_t rkey, sa_rkey;
+ uint8_t spec_i_pt, all_tg_pt, aptpl;
+ enum spdk_scsi_pr_out_service_action_code action;
+ enum spdk_scsi_pr_scope_code scope;
+ enum spdk_scsi_pr_type_code rtype;
+ struct spdk_scsi_pr_out_param_list *param = (struct spdk_scsi_pr_out_param_list *)data;
+
+ action = cdb[1] & 0x0f;
+ scope = (cdb[2] >> 4) & 0x0f;
+ rtype = cdb[2] & 0x0f;
+
+ rkey = from_be64(&param->rkey);
+ sa_rkey = from_be64(&param->sa_rkey);
+ aptpl = param->aptpl;
+ spec_i_pt = param->spec_i_pt;
+ all_tg_pt = param->all_tg_pt;
+
+ switch (action) {
+ case SPDK_SCSI_PR_OUT_REGISTER:
+ case SPDK_SCSI_PR_OUT_REG_AND_IGNORE_KEY:
+ rc = scsi_pr_out_register(task, action, rkey, sa_rkey,
+ spec_i_pt, all_tg_pt, aptpl);
+ break;
+ case SPDK_SCSI_PR_OUT_RESERVE:
+ if (scope != SPDK_SCSI_PR_LU_SCOPE) {
+ goto invalid;
+ }
+ rc = scsi_pr_out_reserve(task, rtype, rkey,
+ spec_i_pt, all_tg_pt, aptpl);
+ break;
+ case SPDK_SCSI_PR_OUT_RELEASE:
+ if (scope != SPDK_SCSI_PR_LU_SCOPE) {
+ goto invalid;
+ }
+ rc = scsi_pr_out_release(task, rtype, rkey);
+ break;
+ case SPDK_SCSI_PR_OUT_CLEAR:
+ rc = scsi_pr_out_clear(task, rkey);
+ break;
+ case SPDK_SCSI_PR_OUT_PREEMPT:
+ if (scope != SPDK_SCSI_PR_LU_SCOPE) {
+ goto invalid;
+ }
+ rc = scsi_pr_out_preempt(task, action, rtype, rkey, sa_rkey);
+ break;
+ default:
+ SPDK_ERRLOG("Invalid service action code %u\n", action);
+ goto invalid;
+ }
+
+ return rc;
+
+invalid:
+ spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
+ SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
+ SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
+ SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
+ return -EINVAL;
+}
+
+static int
+scsi_pr_in_read_keys(struct spdk_scsi_task *task, uint8_t *data,
+ uint16_t data_len)
+{
+ struct spdk_scsi_lun *lun = task->lun;
+ struct spdk_scsi_pr_in_read_keys_data *keys;
+ struct spdk_scsi_pr_registrant *reg, *tmp;
+ uint16_t count = 0;
+
+ SPDK_DEBUGLOG(SPDK_LOG_SCSI, "PR IN READ KEYS\n");
+ keys = (struct spdk_scsi_pr_in_read_keys_data *)data;
+
+ to_be32(&keys->header.pr_generation, lun->pr_generation);
+ TAILQ_FOREACH_SAFE(reg, &lun->reg_head, link, tmp) {
+ if (((count + 1) * 8 + sizeof(keys->header)) > data_len) {
+ break;
+ }
+ to_be64(&keys->rkeys[count], reg->rkey);
+ count++;
+ }
+ to_be32(&keys->header.additional_len, count * 8);
+
+ return (sizeof(keys->header) + count * 8);
+}
+
+static int
+scsi_pr_in_read_reservations(struct spdk_scsi_task *task,
+ uint8_t *data, uint16_t data_len)
+{
+ struct spdk_scsi_lun *lun = task->lun;
+ struct spdk_scsi_pr_in_read_reservations_data *param;
+ bool all_regs = false;
+
+ SPDK_DEBUGLOG(SPDK_LOG_SCSI, "PR IN READ RESERVATIONS\n");
+ param = (struct spdk_scsi_pr_in_read_reservations_data *)(data);
+
+ to_be32(&param->header.pr_generation, lun->pr_generation);
+ if (scsi_pr_has_reservation(lun)) {
+ all_regs = scsi_pr_is_all_registrants_type(lun);
+ if (all_regs) {
+ to_be64(&param->rkey, 0);
+ } else {
+ to_be64(&param->rkey, lun->reservation.crkey);
+ }
+ to_be32(&param->header.additional_len, 16);
+ param->scope = SPDK_SCSI_PR_LU_SCOPE;
+ param->type = lun->reservation.rtype;
+ SPDK_DEBUGLOG(SPDK_LOG_SCSI, "READ RESERVATIONS with valid reservation\n");
+ return sizeof(*param);
+ }
+
+ /* no reservation */
+ to_be32(&param->header.additional_len, 0);
+ SPDK_DEBUGLOG(SPDK_LOG_SCSI, "READ RESERVATIONS no reservation\n");
+ return sizeof(param->header);
+}
+
+static int
+scsi_pr_in_report_capabilities(struct spdk_scsi_task *task,
+ uint8_t *data, uint16_t data_len)
+{
+ struct spdk_scsi_pr_in_report_capabilities_data *param;
+
+ SPDK_DEBUGLOG(SPDK_LOG_SCSI, "PR IN REPORT CAPABILITIES\n");
+ param = (struct spdk_scsi_pr_in_report_capabilities_data *)data;
+
+ memset(param, 0, sizeof(*param));
+ to_be16(&param->length, sizeof(*param));
+ /* Compatible reservation handling to support RESERVE/RELEASE defined in SPC-2 */
+ param->crh = 1;
+ param->tmv = 1;
+ param->wr_ex = 1;
+ param->ex_ac = 1;
+ param->wr_ex_ro = 1;
+ param->ex_ac_ro = 1;
+ param->wr_ex_ar = 1;
+ param->ex_ac_ar = 1;
+
+ return sizeof(*param);
+}
+
+static int
+scsi_pr_in_read_full_status(struct spdk_scsi_task *task,
+ uint8_t *data, uint16_t data_len)
+{
+ struct spdk_scsi_lun *lun = task->lun;
+ struct spdk_scsi_pr_in_full_status_data *param;
+ struct spdk_scsi_pr_in_full_status_desc *desc;
+ struct spdk_scsi_pr_registrant *reg, *tmp;
+ bool all_regs = false;
+ uint32_t add_len = 0;
+
+ SPDK_DEBUGLOG(SPDK_LOG_SCSI, "PR IN READ FULL STATUS\n");
+
+ all_regs = scsi_pr_is_all_registrants_type(lun);
+ param = (struct spdk_scsi_pr_in_full_status_data *)data;
+ to_be32(&param->header.pr_generation, lun->pr_generation);
+
+ TAILQ_FOREACH_SAFE(reg, &lun->reg_head, link, tmp) {
+ desc = (struct spdk_scsi_pr_in_full_status_desc *)
+ ((uint8_t *)param->desc_list + add_len);
+ if (add_len + sizeof(*desc) + sizeof(param->header) > data_len) {
+ break;
+ }
+ add_len += sizeof(*desc);
+ desc->rkey = reg->rkey;
+ if (all_regs || lun->reservation.holder == reg) {
+ desc->r_holder = true;
+ desc->type = lun->reservation.rtype;
+ } else {
+ desc->r_holder = false;
+ desc->type = 0;
+ }
+ desc->all_tg_pt = 0;
+ desc->scope = SPDK_SCSI_PR_LU_SCOPE;
+ desc->relative_target_port_id = reg->relative_target_port_id;
+ if (add_len + reg->transport_id_len + sizeof(param->header) > data_len) {
+ break;
+ }
+ add_len += reg->transport_id_len;
+ memcpy(&desc->transport_id, reg->transport_id, reg->transport_id_len);
+ to_be32(&desc->desc_len, reg->transport_id_len);
+ }
+ to_be32(&param->header.additional_len, add_len);
+
+ return (sizeof(param->header) + add_len);
+}
+
+int
+scsi_pr_in(struct spdk_scsi_task *task, uint8_t *cdb,
+ uint8_t *data, uint16_t data_len)
+{
+ enum spdk_scsi_pr_in_action_code action;
+ int rc = 0;
+
+ action = cdb[1] & 0x1f;
+ if (data_len < sizeof(struct spdk_scsi_pr_in_read_header)) {
+ goto invalid;
+ }
+
+ switch (action) {
+ case SPDK_SCSI_PR_IN_READ_KEYS:
+ rc = scsi_pr_in_read_keys(task, data, data_len);
+ break;
+ case SPDK_SCSI_PR_IN_READ_RESERVATION:
+ if (data_len < sizeof(struct spdk_scsi_pr_in_read_reservations_data)) {
+ goto invalid;
+ }
+ rc = scsi_pr_in_read_reservations(task, data, data_len);
+ break;
+ case SPDK_SCSI_PR_IN_REPORT_CAPABILITIES:
+ rc = scsi_pr_in_report_capabilities(task, data, data_len);
+ break;
+ case SPDK_SCSI_PR_IN_READ_FULL_STATUS:
+ rc = scsi_pr_in_read_full_status(task, data, data_len);
+ break;
+ default:
+ goto invalid;
+ }
+
+ return rc;
+
+invalid:
+ spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
+ SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
+ SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
+ SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
+ return -EINVAL;
+}
+
+int
+scsi_pr_check(struct spdk_scsi_task *task)
+{
+ struct spdk_scsi_lun *lun = task->lun;
+ uint8_t *cdb = task->cdb;
+ enum spdk_scsi_pr_type_code rtype;
+ enum spdk_scsi_pr_out_service_action_code action;
+ struct spdk_scsi_pr_registrant *reg;
+ bool dma_to_device = false;
+
+ /* no reservation holders */
+ if (!scsi_pr_has_reservation(lun)) {
+ return 0;
+ }
+
+ rtype = lun->reservation.rtype;
+ assert(rtype != 0);
+
+ reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
+ /* current I_T nexus hold the reservation */
+ if (scsi_pr_registrant_is_holder(lun, reg)) {
+ return 0;
+ }
+
+ /* reservation is held by other I_T nexus */
+ switch (cdb[0]) {
+ case SPDK_SPC_INQUIRY:
+ case SPDK_SPC_REPORT_LUNS:
+ case SPDK_SPC_REQUEST_SENSE:
+ case SPDK_SPC_LOG_SENSE:
+ case SPDK_SPC_TEST_UNIT_READY:
+ case SPDK_SBC_START_STOP_UNIT:
+ case SPDK_SBC_READ_CAPACITY_10:
+ case SPDK_SPC_PERSISTENT_RESERVE_IN:
+ case SPDK_SPC_SERVICE_ACTION_IN_16:
+ /* CRH enabled, processed by scsi2_reserve() */
+ case SPDK_SPC2_RESERVE_6:
+ case SPDK_SPC2_RESERVE_10:
+ /* CRH enabled, processed by scsi2_release() */
+ case SPDK_SPC2_RELEASE_6:
+ case SPDK_SPC2_RELEASE_10:
+ return 0;
+ case SPDK_SPC_MODE_SELECT_6:
+ case SPDK_SPC_MODE_SELECT_10:
+ case SPDK_SPC_MODE_SENSE_6:
+ case SPDK_SPC_MODE_SENSE_10:
+ case SPDK_SPC_LOG_SELECT:
+ /* I_T nexus is registrant but not holder */
+ if (!reg) {
+ SPDK_DEBUGLOG(SPDK_LOG_SCSI, "CHECK: current I_T nexus "
+ "is not registered, cdb 0x%x\n", cdb[0]);
+ goto conflict;
+ }
+ return 0;
+ case SPDK_SPC_PERSISTENT_RESERVE_OUT:
+ action = cdb[1] & 0x1f;
+ SPDK_DEBUGLOG(SPDK_LOG_SCSI, "CHECK: PR OUT action %u\n", action);
+ switch (action) {
+ case SPDK_SCSI_PR_OUT_RELEASE:
+ case SPDK_SCSI_PR_OUT_CLEAR:
+ case SPDK_SCSI_PR_OUT_PREEMPT:
+ case SPDK_SCSI_PR_OUT_PREEMPT_AND_ABORT:
+ if (!reg) {
+ SPDK_ERRLOG("CHECK: PR OUT action %u\n", action);
+ goto conflict;
+ }
+ return 0;
+ case SPDK_SCSI_PR_OUT_REGISTER:
+ case SPDK_SCSI_PR_OUT_REG_AND_IGNORE_KEY:
+ return 0;
+ case SPDK_SCSI_PR_OUT_REG_AND_MOVE:
+ SPDK_ERRLOG("CHECK: PR OUT action %u\n", action);
+ goto conflict;
+ default:
+ SPDK_ERRLOG("CHECK: PR OUT invalid action %u\n", action);
+ goto conflict;
+ }
+
+ /* For most SBC R/W commands */
+ default:
+ break;
+ }
+
+ switch (cdb[0]) {
+ case SPDK_SBC_READ_6:
+ case SPDK_SBC_READ_10:
+ case SPDK_SBC_READ_12:
+ case SPDK_SBC_READ_16:
+ break;
+ case SPDK_SBC_WRITE_6:
+ case SPDK_SBC_WRITE_10:
+ case SPDK_SBC_WRITE_12:
+ case SPDK_SBC_WRITE_16:
+ case SPDK_SBC_UNMAP:
+ case SPDK_SBC_SYNCHRONIZE_CACHE_10:
+ case SPDK_SBC_SYNCHRONIZE_CACHE_16:
+ dma_to_device = true;
+ break;
+ default:
+ SPDK_ERRLOG("CHECK: unsupported SCSI command cdb 0x%x\n", cdb[0]);
+ goto conflict;
+ }
+
+ switch (rtype) {
+ case SPDK_SCSI_PR_WRITE_EXCLUSIVE:
+ if (dma_to_device) {
+ SPDK_ERRLOG("CHECK: Write Exclusive reservation type "
+ "rejects command 0x%x\n", cdb[0]);
+ goto conflict;
+ }
+ break;
+ case SPDK_SCSI_PR_EXCLUSIVE_ACCESS:
+ SPDK_ERRLOG("CHECK: Exclusive Access reservation type "
+ "rejects command 0x%x\n", cdb[0]);
+ goto conflict;
+ case SPDK_SCSI_PR_WRITE_EXCLUSIVE_REGS_ONLY:
+ case SPDK_SCSI_PR_WRITE_EXCLUSIVE_ALL_REGS:
+ if (!reg && dma_to_device) {
+ SPDK_ERRLOG("CHECK: Registrants only reservation "
+ "type reject command 0x%x\n", cdb[0]);
+ goto conflict;
+ }
+ break;
+ case SPDK_SCSI_PR_EXCLUSIVE_ACCESS_REGS_ONLY:
+ case SPDK_SCSI_PR_EXCLUSIVE_ACCESS_ALL_REGS:
+ if (!reg) {
+ SPDK_ERRLOG("CHECK: All Registrants reservation "
+ "type reject command 0x%x\n", cdb[0]);
+ goto conflict;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+
+conflict:
+ spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_RESERVATION_CONFLICT,
+ SPDK_SCSI_SENSE_NO_SENSE,
+ SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
+ SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
+ return -1;
+}
+
+static int
+scsi2_check_reservation_conflict(struct spdk_scsi_task *task)
+{
+ struct spdk_scsi_lun *lun = task->lun;
+ struct spdk_scsi_pr_registrant *reg;
+ bool conflict = false;
+
+ reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
+ if (reg) {
+ /*
+ * From spc4r31 5.9.3 Exceptions to SPC-2 RESERVE and RELEASE
+ * behavior
+ *
+ * A RESERVE(6) or RESERVE(10) command shall complete with GOOD
+ * status, but no reservation shall be established and the
+ * persistent reservation shall not be changed, if the command
+ * is received from a) and b) below.
+ *
+ * A RELEASE(6) or RELEASE(10) command shall complete with GOOD
+ * status, but the persistent reservation shall not be released,
+ * if the command is received from a) and b)
+ *
+ * a) An I_T nexus that is a persistent reservation holder; or
+ * b) An I_T nexus that is registered if a registrants only or
+ * all registrants type persistent reservation is present.
+ *
+ * In all other cases, a RESERVE(6) command, RESERVE(10) command,
+ * RELEASE(6) command, or RELEASE(10) command shall be processed
+ * as defined in SPC-2.
+ */
+ if (scsi_pr_registrant_is_holder(lun, reg)) {
+ return 1;
+ }
+
+ if (lun->reservation.rtype == SPDK_SCSI_PR_WRITE_EXCLUSIVE_REGS_ONLY ||
+ lun->reservation.rtype == SPDK_SCSI_PR_EXCLUSIVE_ACCESS_REGS_ONLY) {
+ return 1;
+ }
+
+ conflict = true;
+ } else {
+ /*
+ * From spc2r20 5.5.1 Reservations overview:
+ *
+ * If a logical unit has executed a PERSISTENT RESERVE OUT
+ * command with the REGISTER or the REGISTER AND IGNORE
+ * EXISTING KEY service action and is still registered by any
+ * initiator, all RESERVE commands and all RELEASE commands
+ * regardless of initiator shall conflict and shall terminate
+ * with a RESERVATION CONFLICT status.
+ */
+ conflict = TAILQ_EMPTY(&lun->reg_head) ? false : true;
+ }
+
+ if (conflict) {
+ spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_RESERVATION_CONFLICT,
+ SPDK_SCSI_SENSE_NO_SENSE,
+ SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
+ SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+scsi2_reserve(struct spdk_scsi_task *task, uint8_t *cdb)
+{
+ struct spdk_scsi_lun *lun = task->lun;
+ struct spdk_scsi_pr_registrant *reg = &lun->scsi2_holder;
+ int ret;
+
+ /* Obsolete Bits and LongID set, returning ILLEGAL_REQUEST */
+ if (cdb[1] & 0x3) {
+ spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
+ SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
+ SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
+ SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
+ return -1;
+ }
+
+ ret = scsi2_check_reservation_conflict(task);
+ /* PERSISTENT RESERVE is enabled */
+ if (ret == 1) {
+ return 0;
+ } else if (ret < 0) {
+ return ret;
+ }
+
+ /* SPC2 RESERVE */
+ reg->initiator_port = task->initiator_port;
+ if (task->initiator_port) {
+ snprintf(reg->initiator_port_name, sizeof(reg->initiator_port_name), "%s",
+ task->initiator_port->name);
+ reg->transport_id_len = task->initiator_port->transport_id_len;
+ memcpy(reg->transport_id, task->initiator_port->transport_id,
+ reg->transport_id_len);
+ }
+ reg->target_port = task->target_port;
+ if (task->target_port) {
+ snprintf(reg->target_port_name, sizeof(reg->target_port_name), "%s",
+ task->target_port->name);
+ }
+
+ lun->reservation.flags = SCSI_SPC2_RESERVE;
+ lun->reservation.holder = &lun->scsi2_holder;
+
+ return 0;
+}
+
+int
+scsi2_release(struct spdk_scsi_task *task)
+{
+ struct spdk_scsi_lun *lun = task->lun;
+ int ret;
+
+ ret = scsi2_check_reservation_conflict(task);
+ /* PERSISTENT RESERVE is enabled */
+ if (ret == 1) {
+ return 0;
+ } else if (ret < 0) {
+ return ret;
+ }
+
+ assert(lun->reservation.flags & SCSI_SPC2_RESERVE);
+
+ memset(&lun->reservation, 0, sizeof(struct spdk_scsi_pr_reservation));
+ memset(&lun->scsi2_holder, 0, sizeof(struct spdk_scsi_pr_registrant));
+
+ return 0;
+}
+
+int scsi2_reserve_check(struct spdk_scsi_task *task)
+{
+ struct spdk_scsi_lun *lun = task->lun;
+ uint8_t *cdb = task->cdb;
+
+ switch (cdb[0]) {
+ case SPDK_SPC_INQUIRY:
+ case SPDK_SPC2_RELEASE_6:
+ case SPDK_SPC2_RELEASE_10:
+ return 0;
+
+ default:
+ break;
+ }
+
+ /* no reservation holders */
+ if (!scsi_pr_has_reservation(lun)) {
+ return 0;
+ }
+
+ if (scsi2_it_nexus_is_holder(lun, task->initiator_port, task->target_port)) {
+ return 0;
+ }
+
+ spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_RESERVATION_CONFLICT,
+ SPDK_SCSI_SENSE_NO_SENSE,
+ SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
+ SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
+ return -1;
+}
diff --git a/src/spdk/lib/scsi/scsi_rpc.c b/src/spdk/lib/scsi/scsi_rpc.c
new file mode 100644
index 000000000..1938ddac7
--- /dev/null
+++ b/src/spdk/lib/scsi/scsi_rpc.c
@@ -0,0 +1,77 @@
+/*-
+ * 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"
+#include "spdk/util.h"
+
+static void
+rpc_scsi_get_devices(struct spdk_jsonrpc_request *request,
+ const struct spdk_json_val *params)
+{
+ struct spdk_json_write_ctx *w;
+ struct spdk_scsi_dev *devs = scsi_dev_get_list();
+ int i;
+
+ if (params != NULL) {
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ "scsi_get_devices requires no parameters");
+ return;
+ }
+
+ w = spdk_jsonrpc_begin_result(request);
+ 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_named_int32(w, "id", dev->id);
+
+ spdk_json_write_named_string(w, "device_name", dev->name);
+
+ spdk_json_write_object_end(w);
+ }
+ spdk_json_write_array_end(w);
+
+ spdk_jsonrpc_end_result(request, w);
+}
+SPDK_RPC_REGISTER("scsi_get_devices", rpc_scsi_get_devices, SPDK_RPC_RUNTIME)
+SPDK_RPC_REGISTER_ALIAS_DEPRECATED(scsi_get_devices, get_scsi_devices)
diff --git a/src/spdk/lib/scsi/spdk_scsi.map b/src/spdk/lib/scsi/spdk_scsi.map
new file mode 100644
index 000000000..643372699
--- /dev/null
+++ b/src/spdk/lib/scsi/spdk_scsi.map
@@ -0,0 +1,49 @@
+{
+ global:
+
+ # Public functions
+ spdk_scsi_init;
+ spdk_scsi_fini;
+ spdk_scsi_lun_get_id;
+ spdk_scsi_lun_get_bdev_name;
+ spdk_scsi_lun_get_dev;
+ spdk_scsi_lun_is_removing;
+ spdk_scsi_dev_get_name;
+ spdk_scsi_dev_get_id;
+ spdk_scsi_dev_get_lun;
+ spdk_scsi_dev_has_pending_tasks;
+ spdk_scsi_dev_destruct;
+ spdk_scsi_dev_queue_mgmt_task;
+ spdk_scsi_dev_queue_task;
+ spdk_scsi_dev_add_port;
+ spdk_scsi_dev_delete_port;
+ spdk_scsi_dev_find_port_by_id;
+ spdk_scsi_dev_allocate_io_channels;
+ spdk_scsi_dev_free_io_channels;
+ spdk_scsi_dev_construct;
+ spdk_scsi_dev_delete_lun;
+ spdk_scsi_dev_add_lun;
+ spdk_scsi_port_create;
+ spdk_scsi_port_free;
+ spdk_scsi_port_get_name;
+ spdk_scsi_task_construct;
+ spdk_scsi_task_put;
+ spdk_scsi_task_set_data;
+ spdk_scsi_task_scatter_data;
+ spdk_scsi_task_gather_data;
+ spdk_scsi_task_build_sense_data;
+ spdk_scsi_task_set_status;
+ spdk_scsi_task_copy_status;
+ spdk_scsi_task_process_null_lun;
+ spdk_scsi_task_process_abort;
+ spdk_scsi_lun_open;
+ spdk_scsi_lun_close;
+ spdk_scsi_lun_allocate_io_channel;
+ spdk_scsi_lun_free_io_channel;
+ spdk_scsi_lun_get_dif_ctx;
+ spdk_scsi_port_set_iscsi_transport_id;
+ spdk_scsi_lun_id_int_to_fmt;
+ spdk_scsi_lun_id_fmt_to_int;
+
+ local: *;
+};
diff --git a/src/spdk/lib/scsi/task.c b/src/spdk/lib/scsi/task.c
new file mode 100644
index 000000000..7fd8305ec
--- /dev/null
+++ b/src/spdk/lib/scsi/task.c
@@ -0,0 +1,300 @@
+/*-
+ * 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/endian.h"
+#include "spdk/env.h"
+#include "spdk/util.h"
+
+static void
+scsi_task_free_data(struct spdk_scsi_task *task)
+{
+ if (task->alloc_len != 0) {
+ spdk_dma_free(task->iov.iov_base);
+ task->alloc_len = 0;
+ }
+
+ task->iov.iov_base = NULL;
+ task->iov.iov_len = 0;
+}
+
+void
+spdk_scsi_task_put(struct spdk_scsi_task *task)
+{
+ if (!task) {
+ return;
+ }
+
+ assert(task->ref > 0);
+ task->ref--;
+
+ if (task->ref == 0) {
+ struct spdk_bdev_io *bdev_io = task->bdev_io;
+
+ if (bdev_io) {
+ spdk_bdev_free_io(bdev_io);
+ }
+
+ scsi_task_free_data(task);
+
+ task->free_fn(task);
+ }
+}
+
+void
+spdk_scsi_task_construct(struct spdk_scsi_task *task,
+ spdk_scsi_task_cpl cpl_fn,
+ spdk_scsi_task_free free_fn)
+{
+ assert(task != NULL);
+ assert(cpl_fn != NULL);
+ assert(free_fn != NULL);
+
+ task->cpl_fn = cpl_fn;
+ task->free_fn = free_fn;
+
+ task->ref++;
+
+ /*
+ * Pre-fill the iov_buffers to point to the embedded iov
+ */
+ assert(task->iov.iov_base == NULL);
+ task->iovs = &task->iov;
+ task->iovcnt = 1;
+}
+
+static void *
+scsi_task_alloc_data(struct spdk_scsi_task *task, uint32_t alloc_len)
+{
+ assert(task->alloc_len == 0);
+
+ task->iov.iov_base = spdk_dma_zmalloc(alloc_len, 0, NULL);
+ task->iov.iov_len = alloc_len;
+ task->alloc_len = alloc_len;
+
+ return task->iov.iov_base;
+}
+
+int
+spdk_scsi_task_scatter_data(struct spdk_scsi_task *task, const void *src, size_t buf_len)
+{
+ size_t len = 0;
+ size_t buf_left = buf_len;
+ int i;
+ struct iovec *iovs = task->iovs;
+ const uint8_t *pos;
+
+ if (buf_len == 0) {
+ return 0;
+ }
+
+ if (task->iovcnt == 1 && iovs[0].iov_base == NULL) {
+ scsi_task_alloc_data(task, buf_len);
+ iovs[0] = task->iov;
+ }
+
+ for (i = 0; i < task->iovcnt; i++) {
+ assert(iovs[i].iov_base != NULL);
+ len += iovs[i].iov_len;
+ }
+
+ if (len < buf_len) {
+ spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
+ SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
+ SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
+ SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
+ return -1;
+ }
+
+ pos = src;
+
+ for (i = 0; i < task->iovcnt; i++) {
+ len = spdk_min(iovs[i].iov_len, buf_left);
+ buf_left -= len;
+ memcpy(iovs[i].iov_base, pos, len);
+ pos += len;
+ }
+
+ return buf_len;
+}
+
+void *
+spdk_scsi_task_gather_data(struct spdk_scsi_task *task, int *len)
+{
+ int i;
+ struct iovec *iovs = task->iovs;
+ size_t buf_len = 0;
+ uint8_t *buf, *pos;
+
+ for (i = 0; i < task->iovcnt; i++) {
+ assert(iovs[i].iov_base != NULL);
+ buf_len += iovs[i].iov_len;
+ }
+
+ if (buf_len == 0) {
+ *len = 0;
+ return NULL;
+ }
+
+ buf = calloc(1, buf_len);
+ if (buf == NULL) {
+ *len = -1;
+ return NULL;
+ }
+
+ pos = buf;
+ for (i = 0; i < task->iovcnt; i++) {
+ memcpy(pos, iovs[i].iov_base, iovs[i].iov_len);
+ pos += iovs[i].iov_len;
+ }
+
+ *len = buf_len;
+ return buf;
+}
+
+void
+spdk_scsi_task_set_data(struct spdk_scsi_task *task, void *data, uint32_t len)
+{
+ assert(task->iovcnt == 1);
+ assert(task->alloc_len == 0);
+
+ task->iovs[0].iov_base = data;
+ task->iovs[0].iov_len = len;
+}
+
+void
+spdk_scsi_task_build_sense_data(struct spdk_scsi_task *task, int sk, int asc, int ascq)
+{
+ uint8_t *cp;
+ int resp_code;
+
+ resp_code = 0x70; /* Current + Fixed format */
+
+ /* Sense Data */
+ cp = task->sense_data;
+
+ /* 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] = 10;
+
+ /* 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;
+
+ /* SenseLength */
+ task->sense_data_len = 18;
+}
+
+void
+spdk_scsi_task_set_status(struct spdk_scsi_task *task, int sc, int sk,
+ int asc, int ascq)
+{
+ if (sc == SPDK_SCSI_STATUS_CHECK_CONDITION) {
+ spdk_scsi_task_build_sense_data(task, sk, asc, ascq);
+ }
+ task->status = sc;
+}
+
+void
+spdk_scsi_task_copy_status(struct spdk_scsi_task *dst,
+ struct spdk_scsi_task *src)
+{
+ memcpy(dst->sense_data, src->sense_data, src->sense_data_len);
+ dst->sense_data_len = src->sense_data_len;
+ dst->status = src->status;
+}
+
+void
+spdk_scsi_task_process_null_lun(struct spdk_scsi_task *task)
+{
+ uint8_t buffer[36];
+ uint32_t allocation_len;
+ uint32_t data_len;
+
+ task->length = task->transfer_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.
+ */
+ data_len = sizeof(buffer);
+
+ memset(buffer, 0, data_len);
+ /* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */
+ buffer[0] = 0x03 << 5 | 0x1f;
+ /* ADDITIONAL LENGTH */
+ buffer[4] = data_len - 5;
+
+ allocation_len = from_be16(&task->cdb[3]);
+ if (spdk_scsi_task_scatter_data(task, buffer, spdk_min(allocation_len, data_len)) >= 0) {
+ task->data_transferred = data_len;
+ task->status = SPDK_SCSI_STATUS_GOOD;
+ }
+ } else {
+ /* LOGICAL UNIT NOT SUPPORTED */
+ spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
+ SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
+ SPDK_SCSI_ASC_LOGICAL_UNIT_NOT_SUPPORTED,
+ SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
+ task->data_transferred = 0;
+ }
+}
+
+void
+spdk_scsi_task_process_abort(struct spdk_scsi_task *task)
+{
+ spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
+ SPDK_SCSI_SENSE_ABORTED_COMMAND,
+ SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
+ SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
+}