diff options
Diffstat (limited to '')
-rw-r--r-- | src/spdk/lib/scsi/dev.c | 415 |
1 files changed, 415 insertions, 0 deletions
diff --git a/src/spdk/lib/scsi/dev.c b/src/spdk/lib/scsi/dev.c new file mode 100644 index 00000000..335ffacb --- /dev/null +++ b/src/spdk/lib/scsi/dev.c @@ -0,0 +1,415 @@ +/*- + * BSD LICENSE + * + * Copyright (C) 2008-2012 Daisuke Aoyama <aoyama@peach.ne.jp>. + * Copyright (c) Intel Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "scsi_internal.h" + +static struct spdk_scsi_dev g_devs[SPDK_SCSI_MAX_DEVS]; + +struct spdk_scsi_dev * +spdk_scsi_dev_get_list(void) +{ + return g_devs; +} + +static struct spdk_scsi_dev * +allocate_dev(void) +{ + struct spdk_scsi_dev *dev; + int i; + + for (i = 0; i < SPDK_SCSI_MAX_DEVS; i++) { + dev = &g_devs[i]; + if (!dev->is_allocated) { + memset(dev, 0, sizeof(*dev)); + dev->id = i; + dev->is_allocated = 1; + return dev; + } + } + + return NULL; +} + +static void +free_dev(struct spdk_scsi_dev *dev) +{ + assert(dev->is_allocated == 1); + assert(dev->removed == true); + + dev->is_allocated = 0; +} + +void +spdk_scsi_dev_destruct(struct spdk_scsi_dev *dev) +{ + int lun_cnt; + int i; + + if (dev == NULL || dev->removed) { + return; + } + + dev->removed = true; + 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. + */ + spdk_scsi_lun_destruct(dev->lun[i]); + lun_cnt++; + } + + if (lun_cnt == 0) { + free_dev(dev); + return; + } +} + +static int +spdk_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 = spdk_scsi_dev_find_lowest_free_lun_id(dev); + if (lun_id == -1) { + SPDK_ERRLOG("Free LUN ID is not found\n"); + return -1; + } + } + + lun = spdk_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); + } +} + +/* This typedef exists to work around an astyle 2.05 bug. + * Remove it when astyle is fixed. + */ +typedef struct spdk_scsi_dev _spdk_scsi_dev; + +_spdk_scsi_dev * +spdk_scsi_dev_construct(const char *name, 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); + return NULL; + } + } + + return dev; +} + +void +spdk_scsi_dev_queue_mgmt_task(struct spdk_scsi_dev *dev, + struct spdk_scsi_task *task, + enum spdk_scsi_task_func func) +{ + assert(task != NULL); + + task->function = func; + spdk_scsi_lun_task_mgmt_execute(task, func); +} + +void +spdk_scsi_dev_queue_task(struct spdk_scsi_dev *dev, + struct spdk_scsi_task *task) +{ + assert(task != NULL); + + spdk_scsi_lun_execute_task(task->lun, task); +} + +static struct spdk_scsi_port * +spdk_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 = spdk_scsi_dev_find_free_port(dev); + if (port == NULL) { + assert(false); + return -1; + } + + rc = spdk_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; + } + + spdk_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; + } + _spdk_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 = _spdk_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) +{ + if (lun_id < 0 || lun_id >= SPDK_SCSI_DEV_MAX_LUN) { + return NULL; + } + + return dev->lun[lun_id]; +} + +bool +spdk_scsi_dev_has_pending_tasks(const struct spdk_scsi_dev *dev) +{ + int i; + + for (i = 0; i < SPDK_SCSI_DEV_MAX_LUN; ++i) { + if (dev->lun[i] && spdk_scsi_lun_has_pending_tasks(dev->lun[i])) { + return true; + } + } + + return false; +} |