From 59b3479bfd2e4f3d54b49da1d0a6dc6f72064f64 Mon Sep 17 00:00:00 2001 From: Shuhei Matsumoto Date: Tue, 15 May 2018 08:24:19 +0900 Subject: [PATCH] subsystem/iscsi: Add set_iscsi_options RPC to set global params An new RPC set_iscsi_options allocates and set options dynamically. Initialization of iSCSI subsystem skips initialization of options if it is already allocated. To use and test this RPC easily, add python script too. Change-Id: I71e252da6495a194ae9a1a9e3aaae4feb543487a Signed-off-by: Shuhei Matsumoto Reviewed-on: https://review.gerrithub.io/403624 Tested-by: SPDK Automated Test System Reviewed-by: Daniel Verkamp Reviewed-by: Ben Walker --- lib/event/subsystems/iscsi/Makefile | 2 +- lib/event/subsystems/iscsi/iscsi_rpc.c | 113 +++++++++++++++++++++++ lib/iscsi/iscsi.h | 5 ++ lib/iscsi/iscsi_subsystem.c | 120 ++++++++++++++++++++++--- scripts/rpc.py | 25 ++++++ scripts/rpc/iscsi.py | 38 ++++++++ 6 files changed, 289 insertions(+), 14 deletions(-) create mode 100644 lib/event/subsystems/iscsi/iscsi_rpc.c diff --git a/lib/event/subsystems/iscsi/Makefile b/lib/event/subsystems/iscsi/Makefile index d57f59467..f57d9f9cd 100644 --- a/lib/event/subsystems/iscsi/Makefile +++ b/lib/event/subsystems/iscsi/Makefile @@ -35,7 +35,7 @@ SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../../..) include $(SPDK_ROOT_DIR)/mk/spdk.common.mk CFLAGS += -I$(SPDK_ROOT_DIR)/lib -C_SRCS = iscsi.c +C_SRCS = iscsi.c iscsi_rpc.c LIBNAME = event_iscsi include $(SPDK_ROOT_DIR)/mk/spdk.lib.mk diff --git a/lib/event/subsystems/iscsi/iscsi_rpc.c b/lib/event/subsystems/iscsi/iscsi_rpc.c new file mode 100644 index 000000000..64ea2204a --- /dev/null +++ b/lib/event/subsystems/iscsi/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 "iscsi/iscsi.h" +#include "iscsi/conn.h" + +#include "spdk/rpc.h" +#include "spdk/util.h" +#include "spdk/event.h" + +#include "spdk_internal/log.h" + +static const struct spdk_json_object_decoder rpc_set_iscsi_opts_decoders[] = { + {"auth_file", offsetof(struct spdk_iscsi_opts, authfile), spdk_json_decode_string, true}, + {"node_base", offsetof(struct spdk_iscsi_opts, nodebase), spdk_json_decode_string, true}, + {"timeout", offsetof(struct spdk_iscsi_opts, timeout), spdk_json_decode_int32, true}, + {"nop_in_interval", offsetof(struct spdk_iscsi_opts, nopininterval), spdk_json_decode_int32, true}, + {"no_discovery_auth", offsetof(struct spdk_iscsi_opts, no_discovery_auth), spdk_json_decode_bool, true}, + {"req_discovery_auth", offsetof(struct spdk_iscsi_opts, req_discovery_auth), spdk_json_decode_bool, true}, + {"req_discovery_auth_mutual", offsetof(struct spdk_iscsi_opts, req_discovery_auth_mutual), spdk_json_decode_bool, true}, + {"discovery_auth_group", offsetof(struct spdk_iscsi_opts, discovery_auth_group), spdk_json_decode_int32, true}, + {"max_sessions", offsetof(struct spdk_iscsi_opts, MaxSessions), spdk_json_decode_uint32, true}, + {"max_queue_depth", offsetof(struct spdk_iscsi_opts, MaxQueueDepth), spdk_json_decode_uint32, true}, + {"max_connections_per_session", offsetof(struct spdk_iscsi_opts, MaxConnectionsPerSession), spdk_json_decode_uint32, true}, + {"default_time2wait", offsetof(struct spdk_iscsi_opts, DefaultTime2Wait), spdk_json_decode_uint32, true}, + {"default_time2retain", offsetof(struct spdk_iscsi_opts, DefaultTime2Retain), spdk_json_decode_uint32, true}, + {"immediate_data", offsetof(struct spdk_iscsi_opts, ImmediateData), spdk_json_decode_bool, true}, + {"error_recovery_level", offsetof(struct spdk_iscsi_opts, ErrorRecoveryLevel), spdk_json_decode_uint32, true}, + {"allow_duplicated_isid", offsetof(struct spdk_iscsi_opts, AllowDuplicateIsid), spdk_json_decode_bool, true}, + {"min_connections_per_core", offsetof(struct spdk_iscsi_opts, min_connections_per_core), spdk_json_decode_uint32, true}, +}; + +static void +spdk_rpc_iscsi_set_opts(struct spdk_jsonrpc_request *request, + const struct spdk_json_val *params) +{ + struct spdk_iscsi_opts *opts; + struct spdk_json_write_ctx *w; + + if (g_spdk_iscsi_opts != NULL) { + SPDK_ERRLOG("this RPC must not be called more than once.\n"); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, + "Must not call more than once"); + return; + } + + opts = spdk_iscsi_opts_alloc(); + if (opts == NULL) { + SPDK_ERRLOG("spdk_iscsi_opts_alloc() failed.\n"); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, + "Out of memory"); + return; + } + + if (params != NULL) { + if (spdk_json_decode_object(params, rpc_set_iscsi_opts_decoders, + SPDK_COUNTOF(rpc_set_iscsi_opts_decoders), opts)) { + SPDK_ERRLOG("spdk_json_decode_object() failed\n"); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, + "Invalid parameters"); + return; + } + } + + g_spdk_iscsi_opts = spdk_iscsi_opts_copy(opts); + spdk_iscsi_opts_free(opts); + + if (g_spdk_iscsi_opts == NULL) { + SPDK_ERRLOG("spdk_iscsi_opts_copy() failed\n"); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, + "Out of memory"); + return; + } + + w = spdk_jsonrpc_begin_result(request); + if (w == NULL) { + return; + } + + spdk_json_write_bool(w, true); + spdk_jsonrpc_end_result(request, w); +} +SPDK_RPC_REGISTER("set_iscsi_options", spdk_rpc_iscsi_set_opts, SPDK_RPC_STARTUP) diff --git a/lib/iscsi/iscsi.h b/lib/iscsi/iscsi.h index d75e0389e..b80c3160d 100644 --- a/lib/iscsi/iscsi.h +++ b/lib/iscsi/iscsi.h @@ -346,6 +346,7 @@ enum spdk_error_codes { #define xstrdup(s) (s ? strdup(s) : (char *)NULL) extern struct spdk_iscsi_globals g_spdk_iscsi; +extern struct spdk_iscsi_opts *g_spdk_iscsi_opts; struct spdk_iscsi_task; struct spdk_json_write_ctx; @@ -359,6 +360,10 @@ void spdk_shutdown_iscsi_conns_done(void); void spdk_iscsi_config_text(FILE *fp); void spdk_iscsi_config_json(struct spdk_json_write_ctx *w); +struct spdk_iscsi_opts *spdk_iscsi_opts_alloc(void); +void spdk_iscsi_opts_free(struct spdk_iscsi_opts *opts); +struct spdk_iscsi_opts *spdk_iscsi_opts_copy(struct spdk_iscsi_opts *src); + void spdk_iscsi_send_nopin(struct spdk_iscsi_conn *conn); void spdk_iscsi_task_response(struct spdk_iscsi_conn *conn, struct spdk_iscsi_task *task); diff --git a/lib/iscsi/iscsi_subsystem.c b/lib/iscsi/iscsi_subsystem.c index fe5d7f848..b07cf5e7f 100644 --- a/lib/iscsi/iscsi_subsystem.c +++ b/lib/iscsi/iscsi_subsystem.c @@ -47,6 +47,8 @@ #include "spdk_internal/event.h" #include "spdk_internal/log.h" +struct spdk_iscsi_opts *g_spdk_iscsi_opts = NULL; + static spdk_iscsi_init_cb g_init_cb_fn = NULL; static void *g_init_cb_arg = NULL; @@ -397,11 +399,78 @@ spdk_iscsi_opts_init(struct spdk_iscsi_opts *opts) opts->min_connections_per_core = DEFAULT_CONNECTIONS_PER_LCORE; } -static void +struct spdk_iscsi_opts * +spdk_iscsi_opts_alloc(void) +{ + struct spdk_iscsi_opts *opts; + + opts = calloc(1, sizeof(*opts)); + if (!opts) { + SPDK_ERRLOG("calloc() failed for iscsi options\n"); + return NULL; + } + + spdk_iscsi_opts_init(opts); + + return opts; +} + +void spdk_iscsi_opts_free(struct spdk_iscsi_opts *opts) { free(opts->authfile); free(opts->nodebase); + free(opts); +} + +/* Deep copy of spdk_iscsi_opts */ +struct spdk_iscsi_opts * +spdk_iscsi_opts_copy(struct spdk_iscsi_opts *src) +{ + struct spdk_iscsi_opts *dst; + + dst = calloc(1, sizeof(*dst)); + if (!dst) { + SPDK_ERRLOG("calloc() failed for iscsi options\n"); + return NULL; + } + + if (src->authfile) { + dst->authfile = strdup(src->authfile); + if (!dst->authfile) { + free(dst); + SPDK_ERRLOG("failed to strdup for auth file %s\n", src->authfile); + return NULL; + } + } + + if (src->nodebase) { + dst->nodebase = strdup(src->nodebase); + if (!dst->nodebase) { + free(dst->authfile); + free(dst); + SPDK_ERRLOG("failed to strdup for nodebase %s\n", src->nodebase); + return NULL; + } + } + + dst->MaxSessions = src->MaxSessions; + dst->MaxConnectionsPerSession = src->MaxConnectionsPerSession; + dst->MaxQueueDepth = src->MaxQueueDepth; + dst->DefaultTime2Wait = src->DefaultTime2Wait; + dst->DefaultTime2Retain = src->DefaultTime2Retain; + dst->ImmediateData = src->ImmediateData; + dst->AllowDuplicateIsid = src->AllowDuplicateIsid; + dst->ErrorRecoveryLevel = src->ErrorRecoveryLevel; + dst->timeout = src->timeout; + dst->nopininterval = src->nopininterval; + dst->no_discovery_auth = src->no_discovery_auth; + dst->req_discovery_auth = src->req_discovery_auth; + dst->req_discovery_auth_mutual = src->req_discovery_auth; + dst->discovery_auth_group = src->discovery_auth_group; + dst->min_connections_per_core = src->min_connections_per_core; + + return dst; } static int @@ -611,6 +680,36 @@ spdk_iscsi_opts_verify(struct spdk_iscsi_opts *opts) return 0; } +static int +spdk_iscsi_parse_options(struct spdk_iscsi_opts **popts) +{ + struct spdk_iscsi_opts *opts; + struct spdk_conf_section *sp; + int rc; + + opts = spdk_iscsi_opts_alloc(); + if (!opts) { + SPDK_ERRLOG("spdk_iscsi_opts_alloc_failed() failed\n"); + return -ENOMEM; + } + + /* Process parameters */ + SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "spdk_iscsi_read_config_file_parmas\n"); + sp = spdk_conf_find_section(NULL, "iSCSI"); + if (sp != NULL) { + rc = spdk_iscsi_read_config_file_params(sp, opts); + if (rc != 0) { + free(opts); + SPDK_ERRLOG("spdk_iscsi_read_config_file_params() failed\n"); + return rc; + } + } + + *popts = opts; + + return 0; +} + static int spdk_iscsi_set_global_params(struct spdk_iscsi_opts *opts) { @@ -659,29 +758,24 @@ spdk_iscsi_set_global_params(struct spdk_iscsi_opts *opts) static int spdk_iscsi_initialize_global_params(void) { - struct spdk_conf_section *sp; - struct spdk_iscsi_opts opts; int rc; - spdk_iscsi_opts_init(&opts); - - /* Process parameters */ - SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "spdk_iscsi_read_config_file_parmas\n"); - sp = spdk_conf_find_section(NULL, "iSCSI"); - if (sp != NULL) { - rc = spdk_iscsi_read_config_file_params(sp, &opts); + if (!g_spdk_iscsi_opts) { + rc = spdk_iscsi_parse_options(&g_spdk_iscsi_opts); if (rc != 0) { - SPDK_ERRLOG("spdk_iscsi_read_config_file_params() failed\n"); + SPDK_ERRLOG("spdk_iscsi_parse_options() failed\n"); return rc; } } - rc = spdk_iscsi_set_global_params(&opts); - spdk_iscsi_opts_free(&opts); + rc = spdk_iscsi_set_global_params(g_spdk_iscsi_opts); if (rc != 0) { SPDK_ERRLOG("spdk_iscsi_set_global_params() failed\n"); } + spdk_iscsi_opts_free(g_spdk_iscsi_opts); + g_spdk_iscsi_opts = NULL; + return rc; } diff --git a/scripts/rpc.py b/scripts/rpc.py index 67a0c8b26..2156eafcd 100755 --- a/scripts/rpc.py +++ b/scripts/rpc.py @@ -313,6 +313,31 @@ if __name__ == "__main__": p.set_defaults(func=apply_firmware) # iSCSI + def set_iscsi_options(args): + rpc.iscsi.set_iscsi_options(args.client, args) + + p = subparsers.add_parser('set_iscsi_options', help="""Set options of iSCSI subsystem""") + p.add_argument('-f', '--auth-file', help='Path to CHAP shared secret file for discovery session') + p.add_argument('-b', '--node-base', help='Prefix of the name of iSCSI target node') + p.add_argument('-o', '--nop-timeout', help='Timeout in seconds to nop-in request to the initiator', type=int) + p.add_argument('-n', '--nop-in-interval', help='Time interval in secs between nop-in requests by the target', type=int) + p.add_argument('-d', '--no-discovery-auth', help="""CHAP for discovery session should be disabled. + *** Mutually exclusive with --req-discovery-auth""", action='store_true') + p.add_argument('-r', '--req-discovery-auth', help="""CHAP for discovery session should be required. + *** Mutually exclusive with --no-discovery-auth""", action='store_true') + p.add_argument('-m', '--req-discovery-auth-mutual', help='CHAP for discovery session should be mutual', action='store_true') + p.add_argument('-g', '--discovery-auth-group', help="""Authentication group ID for discovery session. + *** Authentication group must be precreated ***""", type=int) + p.add_argument('-a', '--max-sessions', help='Maximum number of sessions in the host.', type=int) + p.add_argument('-c', '--max-connections-per-session', help='Negotiated parameter, MaxConnections.', type=int) + p.add_argument('-w', '--default-time2wait', help='Negotiated parameter, DefaultTime2Wait.', type=int) + p.add_argument('-v', '--default-time2retain', help='Negotiated parameter, DefaultTime2Retain.', type=int) + p.add_argument('-i', '--immediate-data', help='Negotiated parameter, ImmediateData.', action='store_true') + p.add_argument('-l', '--error-recovery-level', help='Negotiated parameter, ErrorRecoveryLevel', type=int) + p.add_argument('-p', '--allow-duplicated-isid', help='Allow duplicated initiator session ID.', action='store_true') + p.add_argument('-u', '--min-connections-per-session', help='Allocation unit of connections per core', type=int) + p.set_defaults(func=set_iscsi_options) + @call_cmd def get_portal_groups(args): print_dict(rpc.iscsi.get_portal_groups(args.client, args)) diff --git a/scripts/rpc/iscsi.py b/scripts/rpc/iscsi.py index e721ffce3..1dd68e2df 100755 --- a/scripts/rpc/iscsi.py +++ b/scripts/rpc/iscsi.py @@ -1,3 +1,41 @@ +def set_iscsi_options(client, args): + params = {} + + if args.auth_file: + params['auth_file'] = args.auth_file + if args.node_base: + params['node_base'] = args.node_base + if args.nop_timeout: + params['nop_timeout'] = args.nop_timeout + if args.nop_in_interval: + params['nop_in_interval'] = args.nop_in_interval + if args.no_discovery_auth: + params['no_discovery_auth'] = args.no_discovery_auth + if args.req_discovery_auth: + params['req_discovery_auth'] = args.req_discovery_auth + if args.req_discovery_auth_mutual: + params['req_discovery_auth_mutual'] = args.req_discovery_auth_mutual + if args.discovery_auth_group: + params['discovery_auth_group'] = args.discovery_auth_group + if args.max_sessions: + params['max_sessions'] = args.max_sessions + if args.max_connections_per_session: + params['max_connections_per_session'] = args.max_connections_per_session + if args.default_time2wait: + params['default_time2wait'] = args.default_time2wait + if args.default_time2retain: + params['default_time2retain'] = args.default_time2retain + if args.immediate_data: + params['immediate_data'] = args.immediate_data + if args.error_recovery_level: + params['error_recovery_level'] = args.error_recovery_level + if args.allow_duplicated_isid: + params['allow_duplicated_isid'] = args.allow_duplicated_isid + if args.min_connections_per_session: + params['min_connections_per_session'] = args.min_connections_per_session + return client.call('set_iscsi_options', params) + + def get_portal_groups(client, args): return client.call('get_portal_groups')