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')