From fba68d61e1cc900f1bbe0bfc099160d436347bb2 Mon Sep 17 00:00:00 2001 From: Pawel Wodkowski Date: Thu, 5 Apr 2018 23:41:19 +0200 Subject: [PATCH] bdev/iscsi: add RPC support and cofnig dump Also, as we are here, switch to new spdk_json_write_named_* API in bdev_iscsi_dump_info_json() Change-Id: Ib29466eb5c6d6496dd1d8efc1f2064577bf56272 Signed-off-by: Pawel Wodkowski Reviewed-on: https://review.gerrithub.io/406635 Tested-by: SPDK Automated Test System Reviewed-by: Jim Harris Reviewed-by: Daniel Verkamp --- lib/bdev/iscsi/Makefile | 2 +- lib/bdev/iscsi/bdev_iscsi.c | 82 ++++++++++++++++------- lib/bdev/iscsi/bdev_iscsi.h | 64 ++++++++++++++++++ lib/bdev/iscsi/bdev_iscsi_rpc.c | 113 ++++++++++++++++++++++++++++++++ scripts/rpc.py | 14 ++++ scripts/rpc/bdev.py | 19 ++++++ 6 files changed, 268 insertions(+), 26 deletions(-) create mode 100644 lib/bdev/iscsi/bdev_iscsi.h create mode 100644 lib/bdev/iscsi/bdev_iscsi_rpc.c diff --git a/lib/bdev/iscsi/Makefile b/lib/bdev/iscsi/Makefile index dfaff337a..4a38886d5 100644 --- a/lib/bdev/iscsi/Makefile +++ b/lib/bdev/iscsi/Makefile @@ -40,7 +40,7 @@ CFLAGS += -I$(SPDK_ROOT_DIR)/lib/bdev/ # this warning so just make sure the warning isn't treated as # an error. CFLAGS += -Wno-error -C_SRCS = bdev_iscsi.c +C_SRCS = bdev_iscsi.c bdev_iscsi_rpc.c LIBNAME = bdev_iscsi include $(SPDK_ROOT_DIR)/mk/spdk.lib.mk diff --git a/lib/bdev/iscsi/bdev_iscsi.c b/lib/bdev/iscsi/bdev_iscsi.c index 850766e9b..9c1bdcf6a 100644 --- a/lib/bdev/iscsi/bdev_iscsi.c +++ b/lib/bdev/iscsi/bdev_iscsi.c @@ -49,6 +49,8 @@ #include "iscsi/iscsi.h" #include "iscsi/scsi-lowlevel.h" +#include "bdev_iscsi.h" + struct bdev_iscsi_lun; #define BDEV_ISCSI_CONNECTION_POLL_US 500 @@ -81,8 +83,6 @@ struct bdev_iscsi_lun { TAILQ_ENTRY(bdev_iscsi_lun) link; }; -typedef void (*spdk_bdev_iscsi_create_cb)(void); - struct bdev_iscsi_io_channel { struct spdk_poller *poller; struct bdev_iscsi_lun *lun; @@ -94,7 +94,9 @@ struct bdev_iscsi_conn_req { char *initiator_iqn; struct iscsi_context *context; spdk_bdev_iscsi_create_cb create_cb; + spdk_bdev_iscsi_create_cb create_cb_arg; TAILQ_ENTRY(bdev_iscsi_conn_req) link; + bool deleted; }; static int @@ -436,17 +438,38 @@ bdev_iscsi_dump_info_json(void *ctx, struct spdk_json_write_ctx *w) return 0; } +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); + + spdk_json_write_named_string(w, "method", "construct_iscsi_bdev"); + + 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); +} + 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, + .write_config_json = bdev_iscsi_write_config_json, }; -static struct spdk_bdev * -create_iscsi_lun(struct iscsi_context *context, char *url, char *initiator_iqn, - const char *name, uint64_t num_blocks, uint32_t block_size) +static int +create_iscsi_lun(struct iscsi_context *context, char *url, char *initiator_iqn, char *name, + uint64_t num_blocks, uint32_t block_size, struct spdk_bdev **bdev) { struct bdev_iscsi_lun *lun; int rc; @@ -454,7 +477,7 @@ create_iscsi_lun(struct iscsi_context *context, char *url, char *initiator_iqn, lun = calloc(sizeof(*lun), 1); if (!lun) { SPDK_ERRLOG("Unable to allocate enough memory for iscsi backend\n"); - return NULL; + return -ENOMEM; } lun->context = context; @@ -463,10 +486,7 @@ create_iscsi_lun(struct iscsi_context *context, char *url, char *initiator_iqn, pthread_mutex_init(&lun->mutex, NULL); - lun->bdev.name = strdup(name); - if (!lun->bdev.name) { - goto error_return; - } + lun->bdev.name = name; lun->bdev.product_name = "iSCSI LUN"; lun->bdev.module = &g_iscsi_bdev_module; lun->bdev.blocklen = block_size; @@ -484,11 +504,12 @@ create_iscsi_lun(struct iscsi_context *context, char *url, char *initiator_iqn, } TAILQ_INSERT_TAIL(&g_iscsi_lun_head, lun, link); - return &lun->bdev; + *bdev = &lun->bdev; + return 0; error_return: iscsi_free_lun(lun); - return NULL; + return rc; } static void @@ -497,25 +518,31 @@ iscsi_readcapacity16_cb(struct iscsi_context *iscsi, int status, { struct bdev_iscsi_conn_req *req = private_data; struct scsi_readcapacity16 *readcap16; - struct spdk_bdev *bdev; + struct spdk_bdev *bdev = NULL; struct scsi_task *task = command_data; if (status != SPDK_SCSI_STATUS_GOOD) { + SPDK_ERRLOG("iSCSI error: %s\n", iscsi_get_error(iscsi)); goto ret; } readcap16 = scsi_datain_unmarshall(task); - bdev = create_iscsi_lun(req->context, req->url, req->initiator_iqn, req->bdev_name, - readcap16->returned_lba + 1, readcap16->block_length); - if (!bdev) { - SPDK_ERRLOG("Unable to create iscsi bdev\n"); + if (!readcap16) { + status = -ENOMEM; + goto ret; + } + + status = create_iscsi_lun(req->context, req->url, req->initiator_iqn, req->bdev_name, + readcap16->returned_lba + 1, readcap16->block_length, &bdev); + if (status) { + SPDK_ERRLOG("Unable to create iscsi bdev: %s (%d)\n", spdk_strerror(-status), status); } ret: TAILQ_REMOVE(&g_iscsi_conn_req, req, link); - req->create_cb(); + req->create_cb(req->create_cb_arg, bdev, status); scsi_free_scsi_task(task); - free(req); + req->deleted = true; } static void @@ -537,8 +564,8 @@ iscsi_connect_cb(struct iscsi_context *iscsi, int status, ret: SPDK_ERRLOG("iSCSI error: %s\n", iscsi_get_error(req->context)); TAILQ_REMOVE(&g_iscsi_conn_req, req, link); - req->create_cb(); - free(req); + req->create_cb(req->create_cb_arg, NULL, status); + req->deleted = true; } static int @@ -561,14 +588,18 @@ iscsi_bdev_conn_poll(void *arg) SPDK_ERRLOG("iscsi_service failed: %s\n", iscsi_get_error(req->context)); } } + + if (req->deleted) { + free(req); + } } return 0; } -static int +int create_iscsi_disk(const char *bdev_name, const char *url, const char *initiator_iqn, - spdk_bdev_iscsi_create_cb cb_fn) + spdk_bdev_iscsi_create_cb cb_fn, void *cb_arg) { struct bdev_iscsi_conn_req *req; struct iscsi_url *iscsi_url = NULL; @@ -595,6 +626,7 @@ create_iscsi_disk(const char *bdev_name, const char *url, const char *initiator_ } req->create_cb = cb_fn; + req->create_cb_arg = cb_arg; iscsi_url = iscsi_parse_full_url(req->context, url); if (iscsi_url == NULL) { @@ -643,7 +675,7 @@ err: } static void -bdev_iscsi_initialize_cb(void) +bdev_iscsi_initialize_cb(void *cb_arg, struct spdk_bdev *bdev, int status) { if (TAILQ_EMPTY(&g_iscsi_conn_req)) { spdk_bdev_module_init_done(&g_iscsi_bdev_module); @@ -678,7 +710,7 @@ bdev_iscsi_initialize(void) break; } - rc = create_iscsi_disk(bdev_name, url, initiator_iqn, bdev_iscsi_initialize_cb); + rc = create_iscsi_disk(bdev_name, url, initiator_iqn, bdev_iscsi_initialize_cb, NULL); if (rc) { break; } diff --git a/lib/bdev/iscsi/bdev_iscsi.h b/lib/bdev/iscsi/bdev_iscsi.h new file mode 100644 index 000000000..b3ec941f4 --- /dev/null +++ b/lib/bdev/iscsi/bdev_iscsi.h @@ -0,0 +1,64 @@ +/*- + * 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. + */ + +#ifndef SPDK_BDEV_ISCSI_H +#define SPDK_BDEV_ISCSI_H + +#include "spdk/bdev.h" + +/** + * SPDK bdev iSCSI callback type. + * + * \param cb_arg Completion callback custom arguments + * \param bdev created bdev + * \param status operation status. Zero on success. + */ +typedef void (*spdk_bdev_iscsi_create_cb)(void *cb_arg, struct spdk_bdev *bdev, int status); + +/** + * Create new iSCSI bdev. + * + * \warning iSCSI URL allow providing login and password. Be careful because + * they will show up in configuration dump. + * + * \param name name for new bdev. + * \param initiator_iqn connection iqn name we identify to target as + * \param url iSCSI URL string. + * \param cb_fn Completion callback + * \param cb_arg Completion callback custom arguments + * \return 0 on success or negative error code. If success bdev with provided name was created. + */ +int create_iscsi_disk(const char *bdev_name, const char *initiator_iqn, const char *url, + spdk_bdev_iscsi_create_cb cb_fn, void *cb_arg); + +#endif // SPDK_BDEV_ISCSI_H diff --git a/lib/bdev/iscsi/bdev_iscsi_rpc.c b/lib/bdev/iscsi/bdev_iscsi_rpc.c new file mode 100644 index 000000000..5478d70ce --- /dev/null +++ b/lib/bdev/iscsi/bdev_iscsi_rpc.c @@ -0,0 +1,113 @@ +/*- + * 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 "bdev_iscsi.h" +#include "spdk/rpc.h" +#include "spdk/util.h" +#include "spdk/string.h" + +#include "spdk_internal/log.h" + +struct rpc_construct_iscsi_bdev { + char *name; + char *initiator_iqn; + char *url; +}; + +static const struct spdk_json_object_decoder rpc_construct_iscsi_bdev_decoders[] = { + {"name", offsetof(struct rpc_construct_iscsi_bdev, name), spdk_json_decode_string}, + {"initiator_iqn", offsetof(struct rpc_construct_iscsi_bdev, initiator_iqn), spdk_json_decode_string}, + {"url", offsetof(struct rpc_construct_iscsi_bdev, url), spdk_json_decode_string}, +}; + +static void +free_rpc_construct_iscsi_bdev(struct rpc_construct_iscsi_bdev *req) +{ + free(req->name); + free(req->initiator_iqn); + free(req->url); +} + +static void +construct_iscsi_bdev_cb(void *cb_arg, struct spdk_bdev *bdev, int status) +{ + struct spdk_jsonrpc_request *request = cb_arg; + struct spdk_json_write_ctx *w; + + if (status > 0) { + spdk_jsonrpc_send_error_response_fmt(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, + "iSCSI error (%d).", status); + } else if (status < 0) { + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, + spdk_strerror(-status)); + } else { + w = spdk_jsonrpc_begin_result(request); + if (w == NULL) { + return; + } + + spdk_json_write_array_begin(w); + spdk_json_write_string(w, spdk_bdev_get_name(bdev)); + spdk_json_write_array_end(w); + spdk_jsonrpc_end_result(request, w); + } +} + +static void +spdk_rpc_construct_iscsi_bdev(struct spdk_jsonrpc_request *request, + const struct spdk_json_val *params) +{ + struct rpc_construct_iscsi_bdev req = {}; + int rc = 0; + + if (spdk_json_decode_object(params, rpc_construct_iscsi_bdev_decoders, + SPDK_COUNTOF(rpc_construct_iscsi_bdev_decoders), + &req)) { + SPDK_ERRLOG("spdk_json_decode_object failed\n"); + rc = -EINVAL; + goto invalid; + } + + rc = create_iscsi_disk(req.name, req.url, req.initiator_iqn, construct_iscsi_bdev_cb, request); + if (rc) { + goto invalid; + } + + free_rpc_construct_iscsi_bdev(&req); + return; + +invalid: + free_rpc_construct_iscsi_bdev(&req); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, spdk_strerror(-rc)); +} +SPDK_RPC_REGISTER("construct_iscsi_bdev", spdk_rpc_construct_iscsi_bdev, SPDK_RPC_RUNTIME) diff --git a/scripts/rpc.py b/scripts/rpc.py index 2156eafcd..9ce47f448 100755 --- a/scripts/rpc.py +++ b/scripts/rpc.py @@ -210,6 +210,20 @@ if __name__ == "__main__": p.add_argument('base_name', help='base bdev name') p.set_defaults(func=construct_error_bdev) + @call_cmd + def construct_iscsi_bdev(args): + rpc.bdev.construct_iscsi_bdev(args.client, + name=args.name, + url=args.url, + initiator_iqn=args.initiator_iqn) + + p = subparsers.add_parser('construct_iscsi_bdev', + help='Add bdev with iSCSI initiator backend') + p.add_argument('-b', '--name', help="Name of the bdev", required=True) + p.add_argument('-i', '--initiator-iqn', help="Initiator IQN", required=True) + p.add_argument('--url', help="iSCSI Lun URL", required=True) + p.set_defaults(func=construct_iscsi_bdev) + @call_cmd def construct_pmem_bdev(args): print_array(rpc.bdev.construct_pmem_bdev(args.client, diff --git a/scripts/rpc/bdev.py b/scripts/rpc/bdev.py index 64dab0986..f4b9fb16d 100755 --- a/scripts/rpc/bdev.py +++ b/scripts/rpc/bdev.py @@ -121,6 +121,25 @@ def construct_error_bdev(client, base_name): return client.call('construct_error_bdev', params) +def construct_iscsi_bdev(client, name, url, initiator_iqn): + """Construct a iSCSI block device. + + Args: + name: name of block device + url: iSCSI URL + initiator_iqn: IQN name to be used by initiator + + Returns: + List of created block devices. + """ + params = { + 'name': name, + 'url': url, + 'initiator_iqn': initiator_iqn, + } + return client.call('construct_iscsi_bdev', params) + + def construct_pmem_bdev(client, pmem_file, name): """Construct a libpmemblk block device.