per Intel policy to include file commit date using git cmd below. The policy does not apply to non-Intel (C) notices. git log --follow -C90% --format=%ad --date default <file> | tail -1 and then pull just the 4 digit year from the result. Intel copyrights were not added to files where Intel either had no contribution ot the contribution lacked substance (ie license header updates, formatting changes, etc). Contribution date used "--follow -C95%" to get the most accurate date. Note that several files in this patch didn't end the license/(c) block with a blank comment line so these were added as the vast majority of files do have this last blank line. Simply there for consistency. Signed-off-by: paul luse <paul.e.luse@intel.com> Change-Id: Id5b7ce4f658fe87132f14139ead58d6e285c04d4 Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/15192 Tested-by: SPDK CI Jenkins <sys_sgci@intel.com> Reviewed-by: Jim Harris <james.r.harris@intel.com> Reviewed-by: Ben Walker <benjamin.walker@intel.com> Community-CI: Mellanox Build Bot
488 lines
9.5 KiB
C
488 lines
9.5 KiB
C
/* SPDX-License-Identifier: BSD-3-Clause
|
|
* Copyright (C) 2008-2012 Daisuke Aoyama <aoyama@peach.ne.jp>.
|
|
* Copyright (C) 2016 Intel Corporation.
|
|
* All rights reserved.
|
|
*/
|
|
|
|
#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;
|
|
TAILQ_INIT(&dev->luns);
|
|
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)
|
|
{
|
|
struct spdk_scsi_lun *lun, *tmp_lun;
|
|
|
|
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;
|
|
|
|
if (TAILQ_EMPTY(&dev->luns)) {
|
|
free_dev(dev);
|
|
return;
|
|
}
|
|
|
|
TAILQ_FOREACH_SAFE(lun, &dev->luns, tailq, tmp_lun) {
|
|
/*
|
|
* LUN will remove itself from this dev when all outstanding IO
|
|
* is done. When no more LUNs, dev will be deleted.
|
|
*/
|
|
scsi_lun_destruct(lun);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Search the lowest free LUN ID if the LUN ID is default, or check if the LUN ID is free otherwise,
|
|
* and also return the LUN which comes just before where we want to insert an new LUN.
|
|
*/
|
|
static int
|
|
scsi_dev_find_free_lun(struct spdk_scsi_dev *dev, int lun_id,
|
|
struct spdk_scsi_lun **prev_lun)
|
|
{
|
|
struct spdk_scsi_lun *lun, *_prev_lun = NULL;
|
|
|
|
if (prev_lun == NULL) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (lun_id == -1) {
|
|
lun_id = 0;
|
|
|
|
TAILQ_FOREACH(lun, &dev->luns, tailq) {
|
|
if (lun->id > lun_id) {
|
|
break;
|
|
}
|
|
lun_id = lun->id + 1;
|
|
_prev_lun = lun;
|
|
}
|
|
|
|
if (lun_id >= SPDK_SCSI_DEV_MAX_LUN) {
|
|
return -ENOSPC;
|
|
}
|
|
} else {
|
|
TAILQ_FOREACH(lun, &dev->luns, tailq) {
|
|
if (lun->id == lun_id) {
|
|
return -EEXIST;
|
|
} else if (lun->id > lun_id) {
|
|
break;
|
|
}
|
|
_prev_lun = lun;
|
|
}
|
|
}
|
|
|
|
*prev_lun = _prev_lun;
|
|
return 0;
|
|
}
|
|
|
|
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)
|
|
{
|
|
return spdk_scsi_dev_add_lun_ext(dev, bdev_name, lun_id,
|
|
NULL, NULL,
|
|
hotremove_cb, hotremove_ctx);
|
|
}
|
|
|
|
int
|
|
spdk_scsi_dev_add_lun_ext(struct spdk_scsi_dev *dev, const char *bdev_name, int lun_id,
|
|
void (*resize_cb)(const struct spdk_scsi_lun *, void *),
|
|
void *resize_ctx,
|
|
void (*hotremove_cb)(const struct spdk_scsi_lun *, void *),
|
|
void *hotremove_ctx)
|
|
{
|
|
struct spdk_scsi_lun *lun, *prev_lun = NULL;
|
|
int rc;
|
|
|
|
if (lun_id >= SPDK_SCSI_DEV_MAX_LUN) {
|
|
SPDK_ERRLOG("LUN ID %d is more than the maximum.\n", lun_id);
|
|
return -1;
|
|
}
|
|
|
|
rc = scsi_dev_find_free_lun(dev, lun_id, &prev_lun);
|
|
if (rc != 0) {
|
|
SPDK_ERRLOG("%s\n", rc == -EEXIST ? "LUN ID is duplicated" : "Free LUN ID is not found");
|
|
return rc;
|
|
}
|
|
|
|
lun = scsi_lun_construct(bdev_name, resize_cb, resize_ctx, hotremove_cb, hotremove_ctx);
|
|
if (lun == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
lun->dev = dev;
|
|
|
|
if (lun_id != -1) {
|
|
lun->id = lun_id;
|
|
} else if (prev_lun == NULL) {
|
|
lun->id = 0;
|
|
} else {
|
|
lun->id = prev_lun->id + 1;
|
|
}
|
|
|
|
if (prev_lun == NULL) {
|
|
TAILQ_INSERT_HEAD(&dev->luns, lun, tailq);
|
|
} else {
|
|
TAILQ_INSERT_AFTER(&dev->luns, prev_lun, lun, tailq);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
spdk_scsi_dev_delete_lun(struct spdk_scsi_dev *dev,
|
|
struct spdk_scsi_lun *lun)
|
|
{
|
|
TAILQ_REMOVE(&dev->luns, lun, tailq);
|
|
|
|
if (dev->removed && TAILQ_EMPTY(&dev->luns)) {
|
|
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)
|
|
{
|
|
return spdk_scsi_dev_construct_ext(name, bdev_name_list, lun_id_list,
|
|
num_luns, protocol_id,
|
|
NULL, NULL,
|
|
hotremove_cb, hotremove_ctx);
|
|
}
|
|
|
|
struct spdk_scsi_dev *spdk_scsi_dev_construct_ext(const char *name, const char *bdev_name_list[],
|
|
int *lun_id_list, int num_luns, uint8_t protocol_id,
|
|
void (*resize_cb)(const struct spdk_scsi_lun *, void *),
|
|
void *resize_ctx,
|
|
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_ext(dev, bdev_name_list[i], lun_id_list[i],
|
|
resize_cb, resize_ctx,
|
|
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)
|
|
{
|
|
struct spdk_scsi_lun *lun, *tmp_lun;
|
|
|
|
TAILQ_FOREACH_SAFE(lun, &dev->luns, tailq, tmp_lun) {
|
|
scsi_lun_free_io_channel(lun);
|
|
}
|
|
}
|
|
|
|
int
|
|
spdk_scsi_dev_allocate_io_channels(struct spdk_scsi_dev *dev)
|
|
{
|
|
struct spdk_scsi_lun *lun, *tmp_lun;
|
|
int rc;
|
|
|
|
TAILQ_FOREACH_SAFE(lun, &dev->luns, tailq, tmp_lun) {
|
|
rc = scsi_lun_allocate_io_channel(lun);
|
|
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;
|
|
|
|
TAILQ_FOREACH(lun, &dev->luns, tailq) {
|
|
if (lun->id == lun_id) {
|
|
if (!spdk_scsi_lun_is_removing(lun)) {
|
|
return lun;
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
struct spdk_scsi_lun *
|
|
spdk_scsi_dev_get_first_lun(struct spdk_scsi_dev *dev)
|
|
{
|
|
struct spdk_scsi_lun *lun;
|
|
|
|
TAILQ_FOREACH(lun, &dev->luns, tailq) {
|
|
if (!spdk_scsi_lun_is_removing(lun)) {
|
|
return lun;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
struct spdk_scsi_lun *
|
|
spdk_scsi_dev_get_next_lun(struct spdk_scsi_lun *prev_lun)
|
|
{
|
|
struct spdk_scsi_dev *dev;
|
|
struct spdk_scsi_lun *lun;
|
|
|
|
if (prev_lun == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
dev = prev_lun->dev;
|
|
|
|
lun = TAILQ_NEXT(prev_lun, tailq);
|
|
if (lun == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
TAILQ_FOREACH_FROM(lun, &dev->luns, tailq) {
|
|
if (!spdk_scsi_lun_is_removing(lun)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return lun;
|
|
}
|
|
|
|
bool
|
|
spdk_scsi_dev_has_pending_tasks(const struct spdk_scsi_dev *dev,
|
|
const struct spdk_scsi_port *initiator_port)
|
|
{
|
|
struct spdk_scsi_lun *lun;
|
|
|
|
TAILQ_FOREACH(lun, &dev->luns, tailq) {
|
|
if (scsi_lun_has_pending_tasks(lun, initiator_port) ||
|
|
scsi_lun_has_pending_mgmt_tasks(lun, initiator_port)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|