2017-12-04 17:07:04 +00:00
|
|
|
/*-
|
|
|
|
* 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 "spdk/stdinc.h"
|
|
|
|
|
|
|
|
#include "spdk/bdev.h"
|
|
|
|
#include "spdk/conf.h"
|
|
|
|
#include "spdk/env.h"
|
|
|
|
#include "spdk/fd.h"
|
2018-06-11 20:32:15 +00:00
|
|
|
#include "spdk/thread.h"
|
2017-12-04 17:07:04 +00:00
|
|
|
#include "spdk/json.h"
|
|
|
|
#include "spdk/util.h"
|
|
|
|
#include "spdk/rpc.h"
|
|
|
|
#include "spdk/string.h"
|
2018-07-04 01:46:44 +00:00
|
|
|
#include "spdk/iscsi_spec.h"
|
2017-12-04 17:07:04 +00:00
|
|
|
|
|
|
|
#include "spdk_internal/log.h"
|
2018-05-23 21:01:03 +00:00
|
|
|
#include "spdk/bdev_module.h"
|
2017-12-04 17:07:04 +00:00
|
|
|
|
|
|
|
#include "iscsi/iscsi.h"
|
|
|
|
#include "iscsi/scsi-lowlevel.h"
|
|
|
|
|
2018-04-05 21:41:19 +00:00
|
|
|
#include "bdev_iscsi.h"
|
|
|
|
|
2017-12-04 17:07:04 +00:00
|
|
|
struct bdev_iscsi_lun;
|
|
|
|
|
2018-05-11 11:12:54 +00:00
|
|
|
#define BDEV_ISCSI_CONNECTION_POLL_US 500 /* 0.5 ms */
|
|
|
|
#define BDEV_ISCSI_NO_MASTER_CH_POLL_US 10000 /* 10ms */
|
|
|
|
|
2017-12-04 17:07:04 +00:00
|
|
|
#define DEFAULT_INITIATOR_NAME "iqn.2016-06.io.spdk:init"
|
|
|
|
|
|
|
|
static int bdev_iscsi_initialize(void);
|
2018-03-27 02:42:21 +00:00
|
|
|
static TAILQ_HEAD(, bdev_iscsi_conn_req) g_iscsi_conn_req = TAILQ_HEAD_INITIALIZER(
|
|
|
|
g_iscsi_conn_req);
|
|
|
|
static struct spdk_poller *g_conn_poller = NULL;
|
2017-12-04 17:07:04 +00:00
|
|
|
|
|
|
|
struct bdev_iscsi_io {
|
|
|
|
struct spdk_thread *submit_td;
|
|
|
|
enum spdk_bdev_io_status status;
|
|
|
|
int scsi_status;
|
|
|
|
enum spdk_scsi_sense sk;
|
|
|
|
uint8_t asc;
|
|
|
|
uint8_t ascq;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct bdev_iscsi_lun {
|
|
|
|
struct spdk_bdev bdev;
|
|
|
|
struct iscsi_context *context;
|
2018-04-16 13:59:53 +00:00
|
|
|
char *initiator_iqn;
|
2019-03-27 11:46:20 +00:00
|
|
|
int lun_id;
|
2018-04-16 14:44:28 +00:00
|
|
|
char *url;
|
2017-12-04 17:07:04 +00:00
|
|
|
pthread_mutex_t mutex;
|
|
|
|
uint32_t ch_count;
|
|
|
|
struct spdk_thread *master_td;
|
2018-05-11 11:12:54 +00:00
|
|
|
struct spdk_poller *no_master_ch_poller;
|
|
|
|
struct spdk_thread *no_master_ch_poller_td;
|
2018-07-20 08:37:41 +00:00
|
|
|
bool unmap_supported;
|
2018-10-30 18:43:22 +00:00
|
|
|
struct spdk_poller *poller;
|
2017-12-04 17:07:04 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct bdev_iscsi_io_channel {
|
|
|
|
struct bdev_iscsi_lun *lun;
|
|
|
|
};
|
|
|
|
|
2018-03-27 02:42:21 +00:00
|
|
|
struct bdev_iscsi_conn_req {
|
2018-04-16 14:44:28 +00:00
|
|
|
char *url;
|
2018-03-27 02:42:21 +00:00
|
|
|
char *bdev_name;
|
2018-05-03 23:38:12 +00:00
|
|
|
char *initiator_iqn;
|
2018-03-27 02:42:21 +00:00
|
|
|
struct iscsi_context *context;
|
2018-05-04 00:03:23 +00:00
|
|
|
spdk_bdev_iscsi_create_cb create_cb;
|
2019-07-19 13:21:10 +00:00
|
|
|
void *create_cb_arg;
|
2018-07-20 08:37:41 +00:00
|
|
|
bool unmap_supported;
|
2019-03-27 11:46:20 +00:00
|
|
|
int lun;
|
2019-07-19 13:21:10 +00:00
|
|
|
int status;
|
2018-03-27 02:42:21 +00:00
|
|
|
TAILQ_ENTRY(bdev_iscsi_conn_req) link;
|
|
|
|
};
|
|
|
|
|
2018-07-26 17:44:51 +00:00
|
|
|
static void
|
|
|
|
complete_conn_req(struct bdev_iscsi_conn_req *req, struct spdk_bdev *bdev,
|
|
|
|
int status)
|
|
|
|
{
|
|
|
|
TAILQ_REMOVE(&g_iscsi_conn_req, req, link);
|
|
|
|
req->create_cb(req->create_cb_arg, bdev, status);
|
2019-07-19 13:21:10 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* we are still running in the context of iscsi_service()
|
|
|
|
* so do not tear down its data structures here
|
|
|
|
*/
|
|
|
|
req->status = status;
|
2018-07-26 17:44:51 +00:00
|
|
|
}
|
|
|
|
|
2017-12-04 17:07:04 +00:00
|
|
|
static int
|
|
|
|
bdev_iscsi_get_ctx_size(void)
|
|
|
|
{
|
|
|
|
return sizeof(struct bdev_iscsi_io);
|
|
|
|
}
|
|
|
|
|
2018-07-26 18:01:08 +00:00
|
|
|
static void
|
|
|
|
_iscsi_free_lun(void *arg)
|
2018-04-11 09:31:49 +00:00
|
|
|
{
|
2018-07-26 18:01:08 +00:00
|
|
|
struct bdev_iscsi_lun *lun = arg;
|
|
|
|
|
2018-04-11 09:31:49 +00:00
|
|
|
assert(lun != NULL);
|
2018-07-26 18:01:08 +00:00
|
|
|
iscsi_destroy_context(lun->context);
|
2018-07-26 17:27:32 +00:00
|
|
|
pthread_mutex_destroy(&lun->mutex);
|
2018-04-11 09:31:49 +00:00
|
|
|
free(lun->bdev.name);
|
2018-04-16 14:44:28 +00:00
|
|
|
free(lun->url);
|
2018-05-03 23:38:12 +00:00
|
|
|
free(lun->initiator_iqn);
|
2018-04-11 09:31:49 +00:00
|
|
|
|
2018-07-26 18:01:08 +00:00
|
|
|
spdk_bdev_destruct_done(&lun->bdev, 0);
|
2018-09-10 19:48:56 +00:00
|
|
|
free(lun);
|
2018-04-11 09:31:49 +00:00
|
|
|
}
|
|
|
|
|
2019-07-19 13:21:10 +00:00
|
|
|
static void
|
|
|
|
_bdev_iscsi_conn_req_free(struct bdev_iscsi_conn_req *req)
|
|
|
|
{
|
|
|
|
free(req->initiator_iqn);
|
|
|
|
free(req->bdev_name);
|
|
|
|
free(req->url);
|
|
|
|
/* destroy will call iscsi_disconnect() implicitly if connected */
|
|
|
|
iscsi_destroy_context(req->context);
|
|
|
|
free(req);
|
|
|
|
}
|
|
|
|
|
2018-04-11 09:31:49 +00:00
|
|
|
static void
|
|
|
|
bdev_iscsi_finish(void)
|
|
|
|
{
|
2019-07-19 13:21:10 +00:00
|
|
|
struct bdev_iscsi_conn_req *req, *tmp;
|
2018-07-26 16:51:03 +00:00
|
|
|
|
2019-07-19 13:21:10 +00:00
|
|
|
/* clear out pending connection requests here. We cannot
|
|
|
|
* simply set the state to a non SCSI_STATUS_GOOD state as
|
|
|
|
* the connection poller wont run anymore
|
|
|
|
*/
|
|
|
|
TAILQ_FOREACH_SAFE(req, &g_iscsi_conn_req, link, tmp) {
|
|
|
|
_bdev_iscsi_conn_req_free(req);
|
2018-04-11 09:31:49 +00:00
|
|
|
}
|
|
|
|
|
2018-07-26 16:51:03 +00:00
|
|
|
if (g_conn_poller) {
|
|
|
|
spdk_poller_unregister(&g_conn_poller);
|
2018-04-11 09:31:49 +00:00
|
|
|
}
|
2017-12-04 17:07:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct spdk_bdev_module g_iscsi_bdev_module = {
|
|
|
|
.name = "iscsi",
|
|
|
|
.module_init = bdev_iscsi_initialize,
|
|
|
|
.module_fini = bdev_iscsi_finish,
|
|
|
|
.get_ctx_size = bdev_iscsi_get_ctx_size,
|
2018-03-27 02:42:21 +00:00
|
|
|
.async_init = true,
|
2017-12-04 17:07:04 +00:00
|
|
|
};
|
|
|
|
|
2019-02-05 10:46:48 +00:00
|
|
|
SPDK_BDEV_MODULE_REGISTER(iscsi, &g_iscsi_bdev_module);
|
2017-12-04 17:07:04 +00:00
|
|
|
|
|
|
|
static void
|
|
|
|
_bdev_iscsi_io_complete(void *_iscsi_io)
|
|
|
|
{
|
|
|
|
struct bdev_iscsi_io *iscsi_io = _iscsi_io;
|
|
|
|
|
|
|
|
if (iscsi_io->status == SPDK_BDEV_IO_STATUS_SUCCESS) {
|
|
|
|
spdk_bdev_io_complete_scsi_status(spdk_bdev_io_from_ctx(iscsi_io), iscsi_io->scsi_status,
|
|
|
|
iscsi_io->sk, iscsi_io->asc, iscsi_io->ascq);
|
|
|
|
} else {
|
|
|
|
spdk_bdev_io_complete(spdk_bdev_io_from_ctx(iscsi_io), iscsi_io->status);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bdev_iscsi_io_complete(struct bdev_iscsi_io *iscsi_io, enum spdk_bdev_io_status status)
|
|
|
|
{
|
|
|
|
iscsi_io->status = status;
|
|
|
|
if (iscsi_io->submit_td != NULL) {
|
|
|
|
spdk_thread_send_msg(iscsi_io->submit_td, _bdev_iscsi_io_complete, iscsi_io);
|
|
|
|
} else {
|
|
|
|
_bdev_iscsi_io_complete(iscsi_io);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-05 05:50:56 +00:00
|
|
|
/* Common call back function for read/write/flush command */
|
2017-12-04 17:07:04 +00:00
|
|
|
static void
|
2018-06-05 05:50:56 +00:00
|
|
|
bdev_iscsi_command_cb(struct iscsi_context *context, int status, void *_task, void *_iscsi_io)
|
2017-12-04 17:07:04 +00:00
|
|
|
{
|
|
|
|
struct scsi_task *task = _task;
|
|
|
|
struct bdev_iscsi_io *iscsi_io = _iscsi_io;
|
|
|
|
|
2018-07-20 05:29:50 +00:00
|
|
|
iscsi_io->scsi_status = status;
|
2018-05-03 23:43:29 +00:00
|
|
|
iscsi_io->sk = (uint8_t)task->sense.key;
|
2017-12-04 17:07:04 +00:00
|
|
|
iscsi_io->asc = (task->sense.ascq >> 8) & 0xFF;
|
|
|
|
iscsi_io->ascq = task->sense.ascq & 0xFF;
|
|
|
|
|
|
|
|
scsi_free_scsi_task(task);
|
|
|
|
bdev_iscsi_io_complete(iscsi_io, SPDK_BDEV_IO_STATUS_SUCCESS);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bdev_iscsi_readv(struct bdev_iscsi_lun *lun, struct bdev_iscsi_io *iscsi_io,
|
|
|
|
struct iovec *iov, int iovcnt, uint64_t nbytes, uint64_t lba)
|
|
|
|
{
|
|
|
|
struct scsi_task *task;
|
|
|
|
|
|
|
|
SPDK_DEBUGLOG(SPDK_LOG_ISCSI_INIT, "read %d iovs size %lu to lba: %#lx\n",
|
|
|
|
iovcnt, nbytes, lba);
|
|
|
|
|
2019-03-27 11:46:20 +00:00
|
|
|
task = iscsi_read16_task(lun->context, lun->lun_id, lba, nbytes, lun->bdev.blocklen, 0, 0, 0, 0, 0,
|
2018-06-05 05:50:56 +00:00
|
|
|
bdev_iscsi_command_cb, iscsi_io);
|
2017-12-04 17:07:04 +00:00
|
|
|
if (task == NULL) {
|
|
|
|
SPDK_ERRLOG("failed to get read16_task\n");
|
|
|
|
bdev_iscsi_io_complete(iscsi_io, SPDK_BDEV_IO_STATUS_FAILED);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(LIBISCSI_FEATURE_IOVECTOR)
|
|
|
|
scsi_task_set_iov_in(task, (struct scsi_iovec *)iov, iovcnt);
|
|
|
|
#else
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < iovcnt; i++) {
|
|
|
|
scsi_task_add_data_in_buffer(task, iov[i].iov_len, iov[i].iov_base);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bdev_iscsi_writev(struct bdev_iscsi_lun *lun, struct bdev_iscsi_io *iscsi_io,
|
|
|
|
struct iovec *iov, int iovcnt, uint64_t nbytes, uint64_t lba)
|
|
|
|
{
|
|
|
|
struct scsi_task *task;
|
|
|
|
|
|
|
|
SPDK_DEBUGLOG(SPDK_LOG_ISCSI_INIT, "write %d iovs size %lu to lba: %#lx\n",
|
|
|
|
iovcnt, nbytes, lba);
|
|
|
|
|
2019-03-27 11:46:20 +00:00
|
|
|
task = iscsi_write16_task(lun->context, lun->lun_id, lba, NULL, nbytes, lun->bdev.blocklen, 0, 0, 0,
|
|
|
|
0, 0,
|
2018-06-05 05:50:56 +00:00
|
|
|
bdev_iscsi_command_cb, iscsi_io);
|
2017-12-04 17:07:04 +00:00
|
|
|
if (task == NULL) {
|
|
|
|
SPDK_ERRLOG("failed to get write16_task\n");
|
|
|
|
bdev_iscsi_io_complete(iscsi_io, SPDK_BDEV_IO_STATUS_FAILED);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(LIBISCSI_FEATURE_IOVECTOR)
|
|
|
|
scsi_task_set_iov_out(task, (struct scsi_iovec *)iov, iovcnt);
|
|
|
|
#else
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < iovcnt; i++) {
|
|
|
|
scsi_task_add_data_in_buffer(task, iov[i].iov_len, iov[i].iov_base);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2018-05-11 11:12:54 +00:00
|
|
|
static void
|
|
|
|
bdev_iscsi_destruct_cb(void *ctx)
|
|
|
|
{
|
|
|
|
struct bdev_iscsi_lun *lun = ctx;
|
|
|
|
|
|
|
|
spdk_poller_unregister(&lun->no_master_ch_poller);
|
2018-07-26 18:01:08 +00:00
|
|
|
spdk_io_device_unregister(lun, _iscsi_free_lun);
|
2018-05-11 11:12:54 +00:00
|
|
|
}
|
|
|
|
|
2017-12-04 17:07:04 +00:00
|
|
|
static int
|
|
|
|
bdev_iscsi_destruct(void *ctx)
|
|
|
|
{
|
|
|
|
struct bdev_iscsi_lun *lun = ctx;
|
|
|
|
|
2018-05-11 11:12:54 +00:00
|
|
|
assert(lun->no_master_ch_poller_td);
|
|
|
|
spdk_thread_send_msg(lun->no_master_ch_poller_td, bdev_iscsi_destruct_cb, lun);
|
|
|
|
return 1;
|
2017-12-04 17:07:04 +00:00
|
|
|
}
|
|
|
|
|
2018-06-05 05:50:56 +00:00
|
|
|
static void
|
|
|
|
bdev_iscsi_flush(struct bdev_iscsi_lun *lun, struct bdev_iscsi_io *iscsi_io, uint32_t num_blocks,
|
|
|
|
int immed, uint64_t lba)
|
|
|
|
{
|
|
|
|
struct scsi_task *task;
|
|
|
|
|
2019-03-27 11:46:20 +00:00
|
|
|
task = iscsi_synchronizecache16_task(lun->context, lun->lun_id, lba,
|
2018-06-05 05:50:56 +00:00
|
|
|
num_blocks, 0, immed, bdev_iscsi_command_cb, iscsi_io);
|
|
|
|
if (task == NULL) {
|
|
|
|
SPDK_ERRLOG("failed to get sync16_task\n");
|
|
|
|
bdev_iscsi_io_complete(iscsi_io, SPDK_BDEV_IO_STATUS_FAILED);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-28 02:47:11 +00:00
|
|
|
static void
|
|
|
|
bdev_iscsi_unmap(struct bdev_iscsi_lun *lun, struct bdev_iscsi_io *iscsi_io,
|
|
|
|
uint64_t lba, uint64_t num_blocks)
|
|
|
|
{
|
|
|
|
struct scsi_task *task;
|
|
|
|
struct unmap_list list[1];
|
|
|
|
|
|
|
|
list[0].lba = lba;
|
|
|
|
list[0].num = num_blocks;
|
|
|
|
task = iscsi_unmap_task(lun->context, 0, 0, 0, list, 1,
|
|
|
|
bdev_iscsi_command_cb, iscsi_io);
|
|
|
|
if (task == NULL) {
|
|
|
|
SPDK_ERRLOG("failed to get unmap_task\n");
|
|
|
|
bdev_iscsi_io_complete(iscsi_io, SPDK_BDEV_IO_STATUS_FAILED);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-04 01:46:44 +00:00
|
|
|
static void
|
|
|
|
bdev_iscsi_reset_cb(struct iscsi_context *context __attribute__((unused)), int status,
|
|
|
|
void *command_data, void *private_data)
|
|
|
|
{
|
|
|
|
uint32_t tmf_response;
|
|
|
|
struct bdev_iscsi_io *iscsi_io = private_data;
|
|
|
|
|
|
|
|
tmf_response = *(uint32_t *)command_data;
|
|
|
|
if (tmf_response == ISCSI_TASK_FUNC_RESP_COMPLETE) {
|
|
|
|
bdev_iscsi_io_complete(iscsi_io, SPDK_BDEV_IO_STATUS_SUCCESS);
|
|
|
|
} else {
|
|
|
|
bdev_iscsi_io_complete(iscsi_io, SPDK_BDEV_IO_STATUS_FAILED);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
_bdev_iscsi_reset(void *_bdev_io)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
struct spdk_bdev_io *bdev_io = _bdev_io;
|
|
|
|
struct bdev_iscsi_lun *lun = (struct bdev_iscsi_lun *)bdev_io->bdev->ctxt;
|
|
|
|
struct bdev_iscsi_io *iscsi_io = (struct bdev_iscsi_io *)bdev_io->driver_ctx;
|
|
|
|
struct iscsi_context *context = lun->context;
|
|
|
|
|
2019-03-27 11:46:20 +00:00
|
|
|
rc = iscsi_task_mgmt_lun_reset_async(context, lun->lun_id,
|
2018-07-04 01:46:44 +00:00
|
|
|
bdev_iscsi_reset_cb, iscsi_io);
|
|
|
|
if (rc != 0) {
|
|
|
|
SPDK_ERRLOG("failed to do iscsi reset\n");
|
|
|
|
bdev_iscsi_io_complete(iscsi_io, SPDK_BDEV_IO_STATUS_FAILED);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bdev_iscsi_reset(struct spdk_bdev_io *bdev_io)
|
|
|
|
{
|
|
|
|
struct bdev_iscsi_lun *lun = (struct bdev_iscsi_lun *)bdev_io->bdev->ctxt;
|
|
|
|
spdk_thread_send_msg(lun->master_td, _bdev_iscsi_reset, bdev_io);
|
|
|
|
}
|
|
|
|
|
2017-12-04 17:07:04 +00:00
|
|
|
static int
|
2018-10-30 18:43:22 +00:00
|
|
|
bdev_iscsi_poll_lun(void *_lun)
|
2017-12-04 17:07:04 +00:00
|
|
|
{
|
2018-10-30 18:43:22 +00:00
|
|
|
struct bdev_iscsi_lun *lun = _lun;
|
2018-03-27 02:42:21 +00:00
|
|
|
struct pollfd pfd = {};
|
2017-12-04 17:07:04 +00:00
|
|
|
|
|
|
|
pfd.fd = iscsi_get_fd(lun->context);
|
|
|
|
pfd.events = iscsi_which_events(lun->context);
|
|
|
|
|
|
|
|
if (poll(&pfd, 1, 0) < 0) {
|
|
|
|
SPDK_ERRLOG("poll failed\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pfd.revents != 0) {
|
|
|
|
if (iscsi_service(lun->context, pfd.revents) < 0) {
|
|
|
|
SPDK_ERRLOG("iscsi_service failed: %s\n", iscsi_get_error(lun->context));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-09 21:56:09 +00:00
|
|
|
return -1;
|
2017-12-04 17:07:04 +00:00
|
|
|
}
|
|
|
|
|
2018-05-11 11:12:54 +00:00
|
|
|
static int
|
|
|
|
bdev_iscsi_no_master_ch_poll(void *arg)
|
|
|
|
{
|
|
|
|
struct bdev_iscsi_lun *lun = arg;
|
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
if (pthread_mutex_trylock(&lun->mutex)) {
|
|
|
|
/* Don't care about the error code here. */
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lun->ch_count == 0) {
|
|
|
|
rc = bdev_iscsi_poll_lun(arg);
|
|
|
|
}
|
|
|
|
|
|
|
|
pthread_mutex_unlock(&lun->mutex);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
bdev: Not assert but pass completion status to spdk_bdev_io_get_buf_cb
When the specified buffer size to spdk_bdev_io_get_buf() is greater
than the permitted maximum, spdk_bdev_io_get_buf() asserts simply and
doesn't call the specified callback function.
SPDK SCSI library doesn't allocate read buffer and specifies
expected read buffer size, and expects that it is allocated by
spdk_bdev_io_get_buf().
Bdev perf tool also doesn't allocate read buffer and specifies
expected read buffer size, and expects that it is allocated by
spdk_bdev_io_get_buf().
When we support DIF insert and strip in iSCSI target, the read
buffer size iSCSI initiator requests and the read buffer size iSCSI target
requests will become different.
Even after that, iSCSI initiator and iSCSI target will negotiate correctly
not to cause buffer overflow in spdk_bdev_io_get_buf(), but if iSCSI
initiator ignores the result of negotiation, iSCSI initiator can request
read buffer size larger than the permitted maximum, and can cause
failure in iSCSI target. This is very flagile and should be avoided.
This patch do the following
- Add the completion status of spdk_bdev_io_get_buf() to
spdk_bdev_io_get_buf_cb(),
- spdk_bdev_io_get_buf() calls spdk_bdev_io_get_buf_cb() by setting
success to false, and return.
- spdk_bdev_io_get_buf_cb() in each bdev module calls assert if success
is false.
Subsequent patches will process the case that success is false
in spdk_bdev_io_get_buf_cb().
Change-Id: I76429a86e18a69aa085a353ac94743296d270b82
Signed-off-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
Reviewed-on: https://review.gerrithub.io/c/446045
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>
Reviewed-by: Changpeng Liu <changpeng.liu@intel.com>
Reviewed-by: Ziye Yang <ziye.yang@intel.com>
Reviewed-by: Darek Stojaczyk <dariusz.stojaczyk@intel.com>
2019-02-25 00:34:28 +00:00
|
|
|
static void
|
|
|
|
bdev_iscsi_get_buf_cb(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io,
|
|
|
|
bool success)
|
2017-12-04 17:07:04 +00:00
|
|
|
{
|
2019-02-25 01:43:13 +00:00
|
|
|
if (!success) {
|
|
|
|
spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_FAILED);
|
|
|
|
return;
|
|
|
|
}
|
bdev: Not assert but pass completion status to spdk_bdev_io_get_buf_cb
When the specified buffer size to spdk_bdev_io_get_buf() is greater
than the permitted maximum, spdk_bdev_io_get_buf() asserts simply and
doesn't call the specified callback function.
SPDK SCSI library doesn't allocate read buffer and specifies
expected read buffer size, and expects that it is allocated by
spdk_bdev_io_get_buf().
Bdev perf tool also doesn't allocate read buffer and specifies
expected read buffer size, and expects that it is allocated by
spdk_bdev_io_get_buf().
When we support DIF insert and strip in iSCSI target, the read
buffer size iSCSI initiator requests and the read buffer size iSCSI target
requests will become different.
Even after that, iSCSI initiator and iSCSI target will negotiate correctly
not to cause buffer overflow in spdk_bdev_io_get_buf(), but if iSCSI
initiator ignores the result of negotiation, iSCSI initiator can request
read buffer size larger than the permitted maximum, and can cause
failure in iSCSI target. This is very flagile and should be avoided.
This patch do the following
- Add the completion status of spdk_bdev_io_get_buf() to
spdk_bdev_io_get_buf_cb(),
- spdk_bdev_io_get_buf() calls spdk_bdev_io_get_buf_cb() by setting
success to false, and return.
- spdk_bdev_io_get_buf_cb() in each bdev module calls assert if success
is false.
Subsequent patches will process the case that success is false
in spdk_bdev_io_get_buf_cb().
Change-Id: I76429a86e18a69aa085a353ac94743296d270b82
Signed-off-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
Reviewed-on: https://review.gerrithub.io/c/446045
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>
Reviewed-by: Changpeng Liu <changpeng.liu@intel.com>
Reviewed-by: Ziye Yang <ziye.yang@intel.com>
Reviewed-by: Darek Stojaczyk <dariusz.stojaczyk@intel.com>
2019-02-25 00:34:28 +00:00
|
|
|
|
2017-12-04 17:07:04 +00:00
|
|
|
bdev_iscsi_readv((struct bdev_iscsi_lun *)bdev_io->bdev->ctxt,
|
|
|
|
(struct bdev_iscsi_io *)bdev_io->driver_ctx,
|
|
|
|
bdev_io->u.bdev.iovs,
|
|
|
|
bdev_io->u.bdev.iovcnt,
|
|
|
|
bdev_io->u.bdev.num_blocks * bdev_io->bdev->blocklen,
|
|
|
|
bdev_io->u.bdev.offset_blocks);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void _bdev_iscsi_submit_request(void *_bdev_io)
|
|
|
|
{
|
|
|
|
struct spdk_bdev_io *bdev_io = _bdev_io;
|
|
|
|
struct bdev_iscsi_io *iscsi_io = (struct bdev_iscsi_io *)bdev_io->driver_ctx;
|
|
|
|
struct bdev_iscsi_lun *lun = (struct bdev_iscsi_lun *)bdev_io->bdev->ctxt;
|
|
|
|
|
|
|
|
switch (bdev_io->type) {
|
|
|
|
case SPDK_BDEV_IO_TYPE_READ:
|
|
|
|
spdk_bdev_io_get_buf(bdev_io, bdev_iscsi_get_buf_cb,
|
|
|
|
bdev_io->u.bdev.num_blocks * bdev_io->bdev->blocklen);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SPDK_BDEV_IO_TYPE_WRITE:
|
|
|
|
bdev_iscsi_writev(lun, iscsi_io,
|
|
|
|
bdev_io->u.bdev.iovs,
|
|
|
|
bdev_io->u.bdev.iovcnt,
|
|
|
|
bdev_io->u.bdev.num_blocks * bdev_io->bdev->blocklen,
|
|
|
|
bdev_io->u.bdev.offset_blocks);
|
|
|
|
break;
|
2018-06-05 05:50:56 +00:00
|
|
|
case SPDK_BDEV_IO_TYPE_FLUSH:
|
|
|
|
bdev_iscsi_flush(lun, iscsi_io,
|
|
|
|
bdev_io->u.bdev.num_blocks,
|
|
|
|
ISCSI_IMMEDIATE_DATA_NO,
|
|
|
|
bdev_io->u.bdev.offset_blocks);
|
|
|
|
break;
|
2018-07-04 01:46:44 +00:00
|
|
|
case SPDK_BDEV_IO_TYPE_RESET:
|
|
|
|
bdev_iscsi_reset(bdev_io);
|
|
|
|
break;
|
2018-06-28 02:47:11 +00:00
|
|
|
case SPDK_BDEV_IO_TYPE_UNMAP:
|
|
|
|
bdev_iscsi_unmap(lun, iscsi_io,
|
|
|
|
bdev_io->u.bdev.offset_blocks,
|
|
|
|
bdev_io->u.bdev.num_blocks);
|
|
|
|
break;
|
2017-12-04 17:07:04 +00:00
|
|
|
default:
|
|
|
|
bdev_iscsi_io_complete(iscsi_io, SPDK_BDEV_IO_STATUS_FAILED);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bdev_iscsi_submit_request(struct spdk_io_channel *_ch, struct spdk_bdev_io *bdev_io)
|
|
|
|
{
|
|
|
|
struct spdk_thread *submit_td = spdk_io_channel_get_thread(_ch);
|
|
|
|
struct bdev_iscsi_io *iscsi_io = (struct bdev_iscsi_io *)bdev_io->driver_ctx;
|
|
|
|
struct bdev_iscsi_lun *lun = (struct bdev_iscsi_lun *)bdev_io->bdev->ctxt;
|
|
|
|
|
|
|
|
if (lun->master_td != submit_td) {
|
|
|
|
iscsi_io->submit_td = submit_td;
|
|
|
|
spdk_thread_send_msg(lun->master_td, _bdev_iscsi_submit_request, bdev_io);
|
|
|
|
return;
|
2018-07-20 05:29:50 +00:00
|
|
|
} else {
|
|
|
|
iscsi_io->submit_td = NULL;
|
2017-12-04 17:07:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_bdev_iscsi_submit_request(bdev_io);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
bdev_iscsi_io_type_supported(void *ctx, enum spdk_bdev_io_type io_type)
|
|
|
|
{
|
2018-07-20 08:37:41 +00:00
|
|
|
struct bdev_iscsi_lun *lun = ctx;
|
|
|
|
|
2017-12-04 17:07:04 +00:00
|
|
|
switch (io_type) {
|
|
|
|
case SPDK_BDEV_IO_TYPE_READ:
|
|
|
|
case SPDK_BDEV_IO_TYPE_WRITE:
|
2018-06-05 05:50:56 +00:00
|
|
|
case SPDK_BDEV_IO_TYPE_FLUSH:
|
2018-07-04 01:46:44 +00:00
|
|
|
case SPDK_BDEV_IO_TYPE_RESET:
|
2017-12-04 17:07:04 +00:00
|
|
|
return true;
|
|
|
|
|
2018-07-20 08:37:41 +00:00
|
|
|
case SPDK_BDEV_IO_TYPE_UNMAP:
|
|
|
|
return lun->unmap_supported;
|
2017-12-04 17:07:04 +00:00
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
bdev_iscsi_create_cb(void *io_device, void *ctx_buf)
|
|
|
|
{
|
|
|
|
struct bdev_iscsi_io_channel *ch = ctx_buf;
|
|
|
|
struct bdev_iscsi_lun *lun = io_device;
|
|
|
|
|
|
|
|
pthread_mutex_lock(&lun->mutex);
|
|
|
|
if (lun->ch_count == 0) {
|
|
|
|
assert(lun->master_td == NULL);
|
|
|
|
lun->master_td = spdk_get_thread();
|
2018-10-30 18:43:22 +00:00
|
|
|
lun->poller = spdk_poller_register(bdev_iscsi_poll_lun, lun, 0);
|
2017-12-04 17:07:04 +00:00
|
|
|
ch->lun = lun;
|
|
|
|
}
|
|
|
|
lun->ch_count++;
|
|
|
|
pthread_mutex_unlock(&lun->mutex);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-03-30 22:07:14 +00:00
|
|
|
static void
|
|
|
|
_iscsi_destroy_cb(void *ctx)
|
|
|
|
{
|
|
|
|
struct bdev_iscsi_lun *lun = ctx;
|
|
|
|
|
|
|
|
pthread_mutex_lock(&lun->mutex);
|
|
|
|
|
|
|
|
assert(lun->master_td == spdk_get_thread());
|
|
|
|
assert(lun->ch_count > 0);
|
|
|
|
|
|
|
|
lun->ch_count--;
|
|
|
|
if (lun->ch_count > 0) {
|
|
|
|
pthread_mutex_unlock(&lun->mutex);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
lun->master_td = NULL;
|
|
|
|
spdk_poller_unregister(&lun->poller);
|
|
|
|
|
|
|
|
pthread_mutex_unlock(&lun->mutex);
|
|
|
|
}
|
|
|
|
|
2017-12-04 17:07:04 +00:00
|
|
|
static void
|
|
|
|
bdev_iscsi_destroy_cb(void *io_device, void *ctx_buf)
|
|
|
|
{
|
|
|
|
struct bdev_iscsi_lun *lun = io_device;
|
2020-03-30 22:07:14 +00:00
|
|
|
struct spdk_thread *thread;
|
2017-12-04 17:07:04 +00:00
|
|
|
|
|
|
|
pthread_mutex_lock(&lun->mutex);
|
|
|
|
lun->ch_count--;
|
|
|
|
if (lun->ch_count == 0) {
|
|
|
|
assert(lun->master_td != NULL);
|
2018-05-11 11:12:54 +00:00
|
|
|
|
2020-03-30 22:07:14 +00:00
|
|
|
if (lun->master_td != spdk_get_thread()) {
|
|
|
|
/* The final channel was destroyed on a different thread
|
|
|
|
* than where the first channel was created. Pass a message
|
|
|
|
* to the master thread to unregister the poller. */
|
|
|
|
lun->ch_count++;
|
|
|
|
thread = lun->master_td;
|
|
|
|
pthread_mutex_unlock(&lun->mutex);
|
|
|
|
spdk_thread_send_msg(thread, _iscsi_destroy_cb, lun);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-12-04 17:07:04 +00:00
|
|
|
lun->master_td = NULL;
|
2018-10-30 18:43:22 +00:00
|
|
|
spdk_poller_unregister(&lun->poller);
|
2017-12-04 17:07:04 +00:00
|
|
|
}
|
|
|
|
pthread_mutex_unlock(&lun->mutex);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct spdk_io_channel *
|
|
|
|
bdev_iscsi_get_io_channel(void *ctx)
|
|
|
|
{
|
|
|
|
struct bdev_iscsi_lun *lun = ctx;
|
|
|
|
|
|
|
|
return spdk_get_io_channel(lun);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
bdev_iscsi_dump_info_json(void *ctx, struct spdk_json_write_ctx *w)
|
|
|
|
{
|
|
|
|
struct bdev_iscsi_lun *lun = ctx;
|
|
|
|
|
2019-02-01 05:34:45 +00:00
|
|
|
spdk_json_write_named_object_begin(w, "iscsi");
|
|
|
|
spdk_json_write_named_string(w, "initiator_name", lun->initiator_iqn);
|
|
|
|
spdk_json_write_named_string(w, "url", lun->url);
|
2017-12-04 17:07:04 +00:00
|
|
|
spdk_json_write_object_end(w);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-04-05 21:41:19 +00:00
|
|
|
static void
|
|
|
|
bdev_iscsi_write_config_json(struct spdk_bdev *bdev, struct spdk_json_write_ctx *w)
|
|
|
|
{
|
|
|
|
struct bdev_iscsi_lun *lun = bdev->ctxt;
|
|
|
|
|
|
|
|
pthread_mutex_lock(&lun->mutex);
|
|
|
|
spdk_json_write_object_begin(w);
|
|
|
|
|
2019-08-22 10:51:25 +00:00
|
|
|
spdk_json_write_named_string(w, "method", "bdev_iscsi_create");
|
2018-04-05 21:41:19 +00:00
|
|
|
|
|
|
|
spdk_json_write_named_object_begin(w, "params");
|
|
|
|
spdk_json_write_named_string(w, "name", bdev->name);
|
|
|
|
spdk_json_write_named_string(w, "initiator_iqn", lun->initiator_iqn);
|
|
|
|
spdk_json_write_named_string(w, "url", lun->url);
|
|
|
|
spdk_json_write_object_end(w);
|
|
|
|
|
|
|
|
spdk_json_write_object_end(w);
|
|
|
|
pthread_mutex_unlock(&lun->mutex);
|
|
|
|
}
|
|
|
|
|
2017-12-04 17:07:04 +00:00
|
|
|
static const struct spdk_bdev_fn_table iscsi_fn_table = {
|
|
|
|
.destruct = bdev_iscsi_destruct,
|
|
|
|
.submit_request = bdev_iscsi_submit_request,
|
|
|
|
.io_type_supported = bdev_iscsi_io_type_supported,
|
|
|
|
.get_io_channel = bdev_iscsi_get_io_channel,
|
|
|
|
.dump_info_json = bdev_iscsi_dump_info_json,
|
2018-04-05 21:41:19 +00:00
|
|
|
.write_config_json = bdev_iscsi_write_config_json,
|
2017-12-04 17:07:04 +00:00
|
|
|
};
|
|
|
|
|
2018-04-05 21:41:19 +00:00
|
|
|
static int
|
2019-03-27 11:46:20 +00:00
|
|
|
create_iscsi_lun(struct iscsi_context *context, int lun_id, char *url, char *initiator_iqn,
|
|
|
|
char *name,
|
2018-07-20 08:37:41 +00:00
|
|
|
uint64_t num_blocks, uint32_t block_size, struct spdk_bdev **bdev, bool unmap_supported)
|
2017-12-04 17:07:04 +00:00
|
|
|
{
|
|
|
|
struct bdev_iscsi_lun *lun;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
lun = calloc(sizeof(*lun), 1);
|
|
|
|
if (!lun) {
|
|
|
|
SPDK_ERRLOG("Unable to allocate enough memory for iscsi backend\n");
|
2018-04-05 21:41:19 +00:00
|
|
|
return -ENOMEM;
|
2017-12-04 17:07:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
lun->context = context;
|
2019-03-27 11:46:20 +00:00
|
|
|
lun->lun_id = lun_id;
|
2017-12-04 17:07:04 +00:00
|
|
|
lun->url = url;
|
2018-05-03 23:38:12 +00:00
|
|
|
lun->initiator_iqn = initiator_iqn;
|
2017-12-04 17:07:04 +00:00
|
|
|
|
|
|
|
pthread_mutex_init(&lun->mutex, NULL);
|
|
|
|
|
2018-04-05 21:41:19 +00:00
|
|
|
lun->bdev.name = name;
|
2017-12-04 17:07:04 +00:00
|
|
|
lun->bdev.product_name = "iSCSI LUN";
|
|
|
|
lun->bdev.module = &g_iscsi_bdev_module;
|
|
|
|
lun->bdev.blocklen = block_size;
|
|
|
|
lun->bdev.blockcnt = num_blocks;
|
|
|
|
lun->bdev.ctxt = lun;
|
2018-07-20 08:37:41 +00:00
|
|
|
lun->unmap_supported = unmap_supported;
|
2017-12-04 17:07:04 +00:00
|
|
|
|
|
|
|
lun->bdev.fn_table = &iscsi_fn_table;
|
|
|
|
|
|
|
|
spdk_io_device_register(lun, bdev_iscsi_create_cb, bdev_iscsi_destroy_cb,
|
2018-08-30 20:26:50 +00:00
|
|
|
sizeof(struct bdev_iscsi_io_channel),
|
|
|
|
name);
|
2017-12-04 17:07:04 +00:00
|
|
|
rc = spdk_bdev_register(&lun->bdev);
|
|
|
|
if (rc) {
|
|
|
|
spdk_io_device_unregister(lun, NULL);
|
2018-07-26 17:07:38 +00:00
|
|
|
pthread_mutex_destroy(&lun->mutex);
|
|
|
|
free(lun);
|
|
|
|
return rc;
|
2017-12-04 17:07:04 +00:00
|
|
|
}
|
|
|
|
|
2018-05-11 11:12:54 +00:00
|
|
|
lun->no_master_ch_poller_td = spdk_get_thread();
|
|
|
|
lun->no_master_ch_poller = spdk_poller_register(bdev_iscsi_no_master_ch_poll, lun,
|
|
|
|
BDEV_ISCSI_NO_MASTER_CH_POLL_US);
|
|
|
|
|
2018-04-05 21:41:19 +00:00
|
|
|
*bdev = &lun->bdev;
|
|
|
|
return 0;
|
2017-12-04 17:07:04 +00:00
|
|
|
}
|
|
|
|
|
2018-03-27 02:42:21 +00:00
|
|
|
static void
|
|
|
|
iscsi_readcapacity16_cb(struct iscsi_context *iscsi, int status,
|
|
|
|
void *command_data, void *private_data)
|
|
|
|
{
|
|
|
|
struct bdev_iscsi_conn_req *req = private_data;
|
|
|
|
struct scsi_readcapacity16 *readcap16;
|
2018-04-05 21:41:19 +00:00
|
|
|
struct spdk_bdev *bdev = NULL;
|
2018-03-27 02:42:21 +00:00
|
|
|
struct scsi_task *task = command_data;
|
|
|
|
|
|
|
|
if (status != SPDK_SCSI_STATUS_GOOD) {
|
2018-04-05 21:41:19 +00:00
|
|
|
SPDK_ERRLOG("iSCSI error: %s\n", iscsi_get_error(iscsi));
|
2018-03-27 02:42:21 +00:00
|
|
|
goto ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
readcap16 = scsi_datain_unmarshall(task);
|
2018-04-05 21:41:19 +00:00
|
|
|
if (!readcap16) {
|
|
|
|
status = -ENOMEM;
|
|
|
|
goto ret;
|
|
|
|
}
|
|
|
|
|
2019-03-27 11:46:20 +00:00
|
|
|
status = create_iscsi_lun(req->context, req->lun, req->url, req->initiator_iqn, req->bdev_name,
|
2018-07-20 08:37:41 +00:00
|
|
|
readcap16->returned_lba + 1, readcap16->block_length, &bdev, req->unmap_supported);
|
2018-04-05 21:41:19 +00:00
|
|
|
if (status) {
|
|
|
|
SPDK_ERRLOG("Unable to create iscsi bdev: %s (%d)\n", spdk_strerror(-status), status);
|
2018-03-27 02:42:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ret:
|
|
|
|
scsi_free_scsi_task(task);
|
2018-06-05 10:53:53 +00:00
|
|
|
complete_conn_req(req, bdev, status);
|
2018-03-27 02:42:21 +00:00
|
|
|
}
|
|
|
|
|
2018-07-20 08:37:41 +00:00
|
|
|
static void
|
|
|
|
bdev_iscsi_inquiry_cb(struct iscsi_context *context, int status, void *_task, void *private_data)
|
|
|
|
{
|
|
|
|
struct scsi_task *task = _task;
|
|
|
|
struct scsi_inquiry_logical_block_provisioning *lbp_inq = NULL;
|
|
|
|
struct bdev_iscsi_conn_req *req = private_data;
|
|
|
|
|
|
|
|
if (status == SPDK_SCSI_STATUS_GOOD) {
|
|
|
|
lbp_inq = scsi_datain_unmarshall(task);
|
|
|
|
if (lbp_inq != NULL && lbp_inq->lbpu) {
|
|
|
|
req->unmap_supported = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-27 11:46:20 +00:00
|
|
|
task = iscsi_readcapacity16_task(context, req->lun, iscsi_readcapacity16_cb, req);
|
2018-07-20 08:37:41 +00:00
|
|
|
if (task) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
SPDK_ERRLOG("iSCSI error: %s\n", iscsi_get_error(req->context));
|
|
|
|
complete_conn_req(req, NULL, status);
|
|
|
|
}
|
|
|
|
|
2018-03-27 02:42:21 +00:00
|
|
|
static void
|
|
|
|
iscsi_connect_cb(struct iscsi_context *iscsi, int status,
|
|
|
|
void *command_data, void *private_data)
|
|
|
|
{
|
|
|
|
struct bdev_iscsi_conn_req *req = private_data;
|
|
|
|
struct scsi_task *task;
|
|
|
|
|
|
|
|
if (status != SPDK_SCSI_STATUS_GOOD) {
|
|
|
|
goto ret;
|
|
|
|
}
|
|
|
|
|
2019-03-27 11:46:20 +00:00
|
|
|
task = iscsi_inquiry_task(iscsi, req->lun, 1,
|
2018-07-20 08:37:41 +00:00
|
|
|
SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING,
|
|
|
|
255, bdev_iscsi_inquiry_cb, req);
|
2018-03-27 02:42:21 +00:00
|
|
|
if (task) {
|
|
|
|
return;
|
|
|
|
}
|
2018-05-04 00:03:23 +00:00
|
|
|
|
2018-03-27 02:42:21 +00:00
|
|
|
ret:
|
2018-05-05 01:12:36 +00:00
|
|
|
SPDK_ERRLOG("iSCSI error: %s\n", iscsi_get_error(req->context));
|
2018-06-05 10:53:53 +00:00
|
|
|
complete_conn_req(req, NULL, status);
|
2018-03-27 02:42:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
iscsi_bdev_conn_poll(void *arg)
|
|
|
|
{
|
|
|
|
struct bdev_iscsi_conn_req *req, *tmp;
|
|
|
|
struct pollfd pfd;
|
2018-06-12 02:42:01 +00:00
|
|
|
struct iscsi_context *context;
|
2018-03-27 02:42:21 +00:00
|
|
|
|
|
|
|
TAILQ_FOREACH_SAFE(req, &g_iscsi_conn_req, link, tmp) {
|
2018-06-12 02:42:01 +00:00
|
|
|
context = req->context;
|
|
|
|
pfd.fd = iscsi_get_fd(context);
|
|
|
|
pfd.events = iscsi_which_events(context);
|
2018-03-27 02:42:21 +00:00
|
|
|
pfd.revents = 0;
|
|
|
|
if (poll(&pfd, 1, 0) < 0) {
|
|
|
|
SPDK_ERRLOG("poll failed\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pfd.revents != 0) {
|
2018-06-12 02:42:01 +00:00
|
|
|
if (iscsi_service(context, pfd.revents) < 0) {
|
|
|
|
SPDK_ERRLOG("iscsi_service failed: %s\n", iscsi_get_error(context));
|
2018-03-27 02:42:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-30 22:37:20 +00:00
|
|
|
if (req->status == 0) {
|
|
|
|
/*
|
|
|
|
* The request completed successfully.
|
|
|
|
*/
|
|
|
|
free(req);
|
|
|
|
} else if (req->status > 0) {
|
2019-07-19 13:21:10 +00:00
|
|
|
/*
|
|
|
|
* An error has occurred during connecting. This req has already
|
|
|
|
* been removed from the g_iscsi_conn_req list, but we needed to
|
|
|
|
* wait until iscsi_service unwound before we could free the req.
|
|
|
|
*/
|
|
|
|
_bdev_iscsi_conn_req_free(req);
|
|
|
|
}
|
|
|
|
}
|
2018-06-09 21:56:09 +00:00
|
|
|
return -1;
|
2018-03-27 02:42:21 +00:00
|
|
|
}
|
|
|
|
|
2018-04-05 21:41:19 +00:00
|
|
|
int
|
2018-05-04 00:03:23 +00:00
|
|
|
create_iscsi_disk(const char *bdev_name, const char *url, const char *initiator_iqn,
|
2018-04-05 21:41:19 +00:00
|
|
|
spdk_bdev_iscsi_create_cb cb_fn, void *cb_arg)
|
2018-04-26 14:34:56 +00:00
|
|
|
{
|
|
|
|
struct bdev_iscsi_conn_req *req;
|
2018-04-16 14:44:28 +00:00
|
|
|
struct iscsi_url *iscsi_url = NULL;
|
2018-04-26 14:34:56 +00:00
|
|
|
int rc;
|
|
|
|
|
2018-05-04 00:03:23 +00:00
|
|
|
if (!bdev_name || !url || !initiator_iqn || strlen(initiator_iqn) == 0 || !cb_fn) {
|
2018-04-26 14:34:56 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
req = calloc(1, sizeof(struct bdev_iscsi_conn_req));
|
|
|
|
if (!req) {
|
|
|
|
SPDK_ERRLOG("Cannot allocate pointer of struct bdev_iscsi_conn_req\n");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2019-07-19 13:21:10 +00:00
|
|
|
req->status = SCSI_STATUS_GOOD;
|
2018-04-26 14:34:56 +00:00
|
|
|
req->bdev_name = strdup(bdev_name);
|
2018-04-16 14:44:28 +00:00
|
|
|
req->url = strdup(url);
|
2018-05-03 23:38:12 +00:00
|
|
|
req->initiator_iqn = strdup(initiator_iqn);
|
2018-04-26 14:34:56 +00:00
|
|
|
req->context = iscsi_create_context(initiator_iqn);
|
2018-05-03 23:38:12 +00:00
|
|
|
if (!req->bdev_name || !req->url || !req->initiator_iqn || !req->context) {
|
2018-04-26 14:34:56 +00:00
|
|
|
SPDK_ERRLOG("Out of memory\n");
|
|
|
|
rc = -ENOMEM;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2018-05-04 00:03:23 +00:00
|
|
|
req->create_cb = cb_fn;
|
2018-04-05 21:41:19 +00:00
|
|
|
req->create_cb_arg = cb_arg;
|
2018-05-04 00:03:23 +00:00
|
|
|
|
2018-04-16 14:44:28 +00:00
|
|
|
iscsi_url = iscsi_parse_full_url(req->context, url);
|
|
|
|
if (iscsi_url == NULL) {
|
2018-04-26 14:34:56 +00:00
|
|
|
SPDK_ERRLOG("could not parse URL: %s\n", iscsi_get_error(req->context));
|
|
|
|
rc = -EINVAL;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2019-03-27 11:46:20 +00:00
|
|
|
req->lun = iscsi_url->lun;
|
2018-04-26 14:34:56 +00:00
|
|
|
rc = iscsi_set_session_type(req->context, ISCSI_SESSION_NORMAL);
|
|
|
|
rc = rc ? rc : iscsi_set_header_digest(req->context, ISCSI_HEADER_DIGEST_NONE);
|
2018-05-03 22:42:43 +00:00
|
|
|
rc = rc ? rc : iscsi_set_targetname(req->context, iscsi_url->target);
|
2018-04-16 14:44:28 +00:00
|
|
|
rc = rc ? rc : iscsi_full_connect_async(req->context, iscsi_url->portal, iscsi_url->lun,
|
2018-04-26 14:34:56 +00:00
|
|
|
iscsi_connect_cb, req);
|
2018-05-16 17:56:56 +00:00
|
|
|
if (rc == 0 && iscsi_url->user[0] != '\0') {
|
|
|
|
rc = iscsi_set_initiator_username_pwd(req->context, iscsi_url->user, iscsi_url->passwd);
|
|
|
|
}
|
|
|
|
|
2018-04-26 14:34:56 +00:00
|
|
|
if (rc < 0) {
|
|
|
|
SPDK_ERRLOG("Failed to connect provided URL=%s: %s\n", url, iscsi_get_error(req->context));
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2018-04-16 14:44:28 +00:00
|
|
|
iscsi_destroy_url(iscsi_url);
|
2020-03-30 22:37:20 +00:00
|
|
|
req->status = -1;
|
2018-04-26 14:34:56 +00:00
|
|
|
TAILQ_INSERT_TAIL(&g_iscsi_conn_req, req, link);
|
2018-05-04 00:03:23 +00:00
|
|
|
if (!g_conn_poller) {
|
2018-05-29 18:39:02 +00:00
|
|
|
g_conn_poller = spdk_poller_register(iscsi_bdev_conn_poll, NULL, BDEV_ISCSI_CONNECTION_POLL_US);
|
2018-05-04 00:03:23 +00:00
|
|
|
}
|
|
|
|
|
2018-04-26 14:34:56 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
err:
|
|
|
|
/* iscsi_destroy_url() is not NULL-proof */
|
2018-04-16 14:44:28 +00:00
|
|
|
if (iscsi_url) {
|
|
|
|
iscsi_destroy_url(iscsi_url);
|
2018-04-26 14:34:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (req->context) {
|
|
|
|
iscsi_destroy_context(req->context);
|
|
|
|
}
|
|
|
|
|
2018-05-03 23:38:12 +00:00
|
|
|
free(req->initiator_iqn);
|
2018-04-26 14:34:56 +00:00
|
|
|
free(req->bdev_name);
|
2018-04-16 14:44:28 +00:00
|
|
|
free(req->url);
|
2018-04-26 14:34:56 +00:00
|
|
|
free(req);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2018-06-18 08:07:33 +00:00
|
|
|
void
|
|
|
|
delete_iscsi_disk(struct spdk_bdev *bdev, spdk_delete_iscsi_complete cb_fn, void *cb_arg)
|
|
|
|
{
|
|
|
|
if (!bdev || bdev->module != &g_iscsi_bdev_module) {
|
|
|
|
cb_fn(cb_arg, -ENODEV);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
spdk_bdev_unregister(bdev, cb_fn, cb_arg);
|
|
|
|
}
|
|
|
|
|
2018-05-04 00:03:23 +00:00
|
|
|
static void
|
2018-04-05 21:41:19 +00:00
|
|
|
bdev_iscsi_initialize_cb(void *cb_arg, struct spdk_bdev *bdev, int status)
|
2018-05-04 00:03:23 +00:00
|
|
|
{
|
|
|
|
if (TAILQ_EMPTY(&g_iscsi_conn_req)) {
|
|
|
|
spdk_bdev_module_init_done(&g_iscsi_bdev_module);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-04 17:07:04 +00:00
|
|
|
static int
|
|
|
|
bdev_iscsi_initialize(void)
|
|
|
|
{
|
|
|
|
struct spdk_conf_section *sp;
|
2018-04-26 14:34:56 +00:00
|
|
|
|
|
|
|
const char *url, *bdev_name, *initiator_iqn;
|
2017-12-04 17:07:04 +00:00
|
|
|
int i, rc;
|
|
|
|
|
|
|
|
sp = spdk_conf_find_section(NULL, "iSCSI_Initiator");
|
|
|
|
if (sp == NULL) {
|
2018-05-04 00:03:23 +00:00
|
|
|
spdk_bdev_module_init_done(&g_iscsi_bdev_module);
|
2017-12-04 17:07:04 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-04-16 13:59:53 +00:00
|
|
|
initiator_iqn = spdk_conf_section_get_val(sp, "initiator_name");
|
|
|
|
if (!initiator_iqn) {
|
|
|
|
initiator_iqn = DEFAULT_INITIATOR_NAME;
|
2017-12-04 17:07:04 +00:00
|
|
|
}
|
|
|
|
|
2018-04-26 14:34:56 +00:00
|
|
|
rc = 0;
|
|
|
|
for (i = 0; (url = spdk_conf_section_get_nmval(sp, "URL", i, 0)) != NULL; i++) {
|
2017-12-04 17:07:04 +00:00
|
|
|
bdev_name = spdk_conf_section_get_nmval(sp, "URL", i, 1);
|
|
|
|
if (bdev_name == NULL) {
|
2018-04-26 14:34:56 +00:00
|
|
|
SPDK_ERRLOG("no bdev name specified for URL %s\n", url);
|
|
|
|
rc = -EINVAL;
|
2017-12-04 17:07:04 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2018-04-05 21:41:19 +00:00
|
|
|
rc = create_iscsi_disk(bdev_name, url, initiator_iqn, bdev_iscsi_initialize_cb, NULL);
|
2018-04-26 14:34:56 +00:00
|
|
|
if (rc) {
|
2017-12-04 17:07:04 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-04 00:03:23 +00:00
|
|
|
if (i == 0) {
|
|
|
|
spdk_bdev_module_init_done(&g_iscsi_bdev_module);
|
2018-04-26 14:34:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
2017-12-04 17:07:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SPDK_LOG_REGISTER_COMPONENT("iscsi_init", SPDK_LOG_ISCSI_INIT)
|