summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--etc/iscsid.conf4
-rw-r--r--usr/config.h1
-rw-r--r--usr/idbm.c3
-rw-r--r--usr/idbm_fields.h1
-rw-r--r--usr/iscsi_util.c8
-rw-r--r--usr/iscsi_util.h1
-rw-r--r--usr/iscsiadm.c153
-rw-r--r--usr/session_mgmt.c53
-rw-r--r--usr/session_mgmt.h6
9 files changed, 199 insertions, 31 deletions
diff --git a/etc/iscsid.conf b/etc/iscsid.conf
index 4e8c08d..ef76dc0 100644
--- a/etc/iscsid.conf
+++ b/etc/iscsid.conf
@@ -39,6 +39,10 @@ iscsid.startup = /sbin/iscsid
# To manually startup the session set to "manual". The default is manual.
node.startup = manual
+# For "automatic" startup nodes, setting this to "Yes" will try logins on each
+# available iface until one succeeds, and then stop. The default "No" will try
+# logins on all availble ifaces simultaneously.
+node.leading_login = No
# *************
# CHAP Settings
diff --git a/usr/config.h b/usr/config.h
index 1736382..0d475c2 100644
--- a/usr/config.h
+++ b/usr/config.h
@@ -231,6 +231,7 @@ typedef struct node_rec {
char name[TARGET_NAME_MAXLEN];
int tpgt;
iscsi_startup_e startup;
+ int leading_login;
session_rec_t session;
conn_rec_t conn[ISCSI_CONN_MAX];
iface_rec_t iface;
diff --git a/usr/idbm.c b/usr/idbm.c
index 93b5882..cca32e2 100644
--- a/usr/idbm.c
+++ b/usr/idbm.c
@@ -209,6 +209,8 @@ idbm_recinfo_node(node_rec_t *r, recinfo_t *ri)
__recinfo_int(NODE_TPGT, ri, r, tpgt, IDBM_SHOW, num, 0);
__recinfo_int_o3(NODE_STARTUP, ri, r, startup,
IDBM_SHOW, "manual", "automatic", "onboot", num, 1);
+ __recinfo_int_o2(NODE_LEADING_LOGIN, ri, r, leading_login, IDBM_SHOW,
+ "No", "Yes", num, 1);
/*
* Note: because we do not add the iface.iscsi_ifacename to
* sysfs iscsiadm does some weird matching. We can change the iface
@@ -2400,6 +2402,7 @@ void idbm_node_setup_defaults(node_rec_t *rec)
rec->tpgt = PORTAL_GROUP_TAG_UNKNOWN;
rec->disc_type = DISCOVERY_TYPE_STATIC;
+ rec->leading_login = 0;
rec->session.cmds_max = CMDS_MAX;
rec->session.xmit_thread_priority = XMIT_THREAD_PRIORITY;
rec->session.initial_cmdsn = 0;
diff --git a/usr/idbm_fields.h b/usr/idbm_fields.h
index 824a7a9..904ccdc 100644
--- a/usr/idbm_fields.h
+++ b/usr/idbm_fields.h
@@ -10,6 +10,7 @@
#define NODE_NAME "node.name"
#define NODE_TPGT "node.tpgt"
#define NODE_STARTUP "node.startup"
+#define NODE_LEADING_LOGIN "node.leading_login"
#define NODE_DISC_ADDR "node.discovery_address"
#define NODE_DISC_PORT "node.discovery_port"
#define NODE_DISC_TYPE "node.discovery_type"
diff --git a/usr/iscsi_util.c b/usr/iscsi_util.c
index 9502d37..293ec54 100644
--- a/usr/iscsi_util.c
+++ b/usr/iscsi_util.c
@@ -344,3 +344,11 @@ int iscsi_match_session_count(void *data, struct session_info *info)
return 0;
return -1;
}
+
+int iscsi_match_target(void *data, struct session_info *info)
+{
+ return __iscsi_match_session(data, info->targetname,
+ info->persistent_address,
+ info->persistent_port, NULL,
+ MATCH_ANY_SID);
+}
diff --git a/usr/iscsi_util.h b/usr/iscsi_util.h
index 13a5eb2..110dfa8 100644
--- a/usr/iscsi_util.h
+++ b/usr/iscsi_util.h
@@ -14,6 +14,7 @@ extern int increase_max_files(void);
extern char *str_to_ipport(char *str, int *port, int *tgpt);
extern int iscsi_match_session(void *data, struct session_info *info);
+extern int iscsi_match_target(void *data, struct session_info *info);
extern int iscsi_match_session_count(void *data, struct session_info *info);
extern int __iscsi_match_session(struct node_rec *rec, char *targetname,
char *address, int port,
diff --git a/usr/iscsiadm.c b/usr/iscsiadm.c
index f2f0281..91d886b 100644
--- a/usr/iscsiadm.c
+++ b/usr/iscsiadm.c
@@ -4,6 +4,7 @@
* Copyright (C) 2004 Dmitry Yusupov, Alex Aizman
* Copyright (C) 2006 Mike Christie
* Copyright (C) 2006 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2011 Dell Inc.
* maintained by open-iscsi@googlegroups.com
*
* This program is free software; you can redistribute it and/or modify
@@ -379,18 +380,54 @@ logout_by_startup(char *mode)
return rc;
}
-/*
- * TODO: merged this and logout into the common for_each_matched_rec by making
- * the matching more generic
- */
+struct startup_data {
+ char *mode;
+ struct list_head all_logins;
+ struct list_head leading_logins;
+};
+
+static int link_startup_recs(void *data, struct node_rec *rec)
+{
+ struct startup_data *startup = data;
+ struct node_rec *rec_copy;
+
+ if (match_startup_mode(rec, startup->mode))
+ return -1;
+
+ rec_copy = calloc(1, sizeof(*rec_copy));
+ if (!rec_copy)
+ return ISCSI_ERR_NOMEM;
+ memcpy(rec_copy, rec, sizeof(*rec_copy));
+ INIT_LIST_HEAD(&rec_copy->list);
+
+ if (rec_copy->leading_login)
+ list_add_tail(&rec_copy->list, &startup->leading_logins);
+ else
+ list_add_tail(&rec_copy->list, &startup->all_logins);
+ return 0;
+}
+
static int
-__login_by_startup(void *data, struct list_head *list, struct node_rec *rec)
+__do_leading_login(void *data, struct list_head *list, struct node_rec *rec)
{
- char *mode = data;
+ struct iface_rec *pattern_iface = data;
+ int nr_found;
+
+ /* Skip any records that do not match the pattern iface */
+ if (!iface_match(pattern_iface, &rec->iface))
+ return -1;
- if (match_startup_mode(rec, mode))
+ /*
+ * If there is an existing session that matcthes the target,
+ * the leading login is complete.
+ */
+ if (iscsi_sysfs_for_each_session(rec, &nr_found, iscsi_match_target)) {
+ log_debug(1, "Skipping %s: Already a session for that target",
+ rec->name);
return -1;
+ }
+ /* No existing session: Attempt a login. */
return iscsi_login_portal(NULL, list, rec);
}
@@ -398,7 +435,7 @@ static int
login_by_startup(char *mode)
{
int nr_found = 0, err, rc;
- struct list_head rec_list;
+ struct startup_data startup;
if (!mode || !(!strcmp(mode, "automatic") || !strcmp(mode, "all") ||
!strcmp(mode,"manual") || !strcmp(mode, "onboot"))) {
@@ -407,29 +444,99 @@ login_by_startup(char *mode)
return ISCSI_ERR_INVAL;
}
- INIT_LIST_HEAD(&rec_list);
- err = idbm_for_each_rec(&nr_found, &rec_list, link_recs);
- if (err && !list_empty(&rec_list))
+ /*
+ * Filter all node records that match the given 'mode' into 2 lists:
+ * Those with leading_login enabled, and those without.
+ */
+ startup.mode = mode;
+ INIT_LIST_HEAD(&startup.all_logins);
+ INIT_LIST_HEAD(&startup.leading_logins);
+ err = idbm_for_each_rec(&nr_found, &startup, link_startup_recs);
+ if (err && (!list_empty(&startup.all_logins) ||
+ !list_empty(&startup.leading_logins)))
/* log msg and try to log into what we found */
log_error("Could not read all records: %s",
iscsi_err_to_str(err));
- else if (err && list_empty(&rec_list)) {
- log_error("Could not read node DB: %s.",
- iscsi_err_to_str(err));
+ else if (list_empty(&startup.all_logins) &&
+ list_empty(&startup.leading_logins)) {
+ if (err) {
+ log_error("Could not read node DB: %s.",
+ iscsi_err_to_str(err));
+ } else {
+ log_error("No records found");
+ err = ISCSI_ERR_NO_OBJS_FOUND;
+ }
return err;
- } else if (list_empty(&rec_list)) {
- log_error("No records found");
- return ISCSI_ERR_NO_OBJS_FOUND;
}
rc = err;
- err = iscsi_login_portals(mode, &nr_found, 1, &rec_list,
- __login_by_startup);
- if (err)
- log_error("Could not log into all portals");
+ if (!list_empty(&startup.all_logins)) {
+ log_debug(1, "Logging into normal (non-leading-login) portals");
+ /* Login all regular (non-leading-login) portals first */
+ err = iscsi_login_portals(NULL, &nr_found, 1,
+ &startup.all_logins, iscsi_login_portal);
+ if (err)
+ log_error("Could not log into all portals");
+ if (err && !rc)
+ rc = err;
+ }
+
+ if (!list_empty(&startup.leading_logins)) {
+ /*
+ * For each iface in turn, try to login all portals on that
+ * iface that do not already have a session present.
+ */
+ struct iface_rec *pattern_iface, *tmp_iface;
+ struct node_rec *rec, *tmp_rec;
+ struct list_head iface_list;
+ int missed_leading_login = 0;
+ log_debug(1, "Logging into leading-login portals");
+ INIT_LIST_HEAD(&iface_list);
+ iface_link_ifaces(&iface_list);
+ list_for_each_entry_safe(pattern_iface, tmp_iface, &iface_list,
+ list) {
+ log_debug(1, "Establishing leading-logins via iface %s",
+ pattern_iface->name);
+ err = iscsi_login_portals_safe(pattern_iface, &nr_found,
+ 1,
+ &startup.leading_logins,
+ __do_leading_login);
+ if (err)
+ log_error("Could not log into all portals on "
+ "%s, trying next interface",
+ pattern_iface->name);
+
+ /*
+ * Note: We always try all iface records in case there
+ * are targets that are associated with only a subset
+ * of iface records. __do_leading_login already
+ * prevents duplicate sessions if an iface has succeded
+ * for a particular target.
+ */
+ }
+ /*
+ * Double-check that all leading-login portals have at least
+ * one session
+ */
+ list_for_each_entry_safe(rec, tmp_rec, &startup.leading_logins,
+ list) {
+ if (!iscsi_sysfs_for_each_session(rec, &nr_found,
+ iscsi_match_target))
+ missed_leading_login++;
+ /*
+ * Cleanup the list, since 'iscsi_login_portals_safe'
+ * does not
+ */
+ list_del(&rec->list);
+ free(rec);
+ }
+ if (missed_leading_login) {
+ log_error("Could not login all leading-login portals");
+ if (!rc)
+ rc = ISCSI_ERR_FATAL_LOGIN;
+ }
+ }
- if (err && !rc)
- rc = err;
return rc;
}
diff --git a/usr/session_mgmt.c b/usr/session_mgmt.c
index 7918954..914471c 100644
--- a/usr/session_mgmt.c
+++ b/usr/session_mgmt.c
@@ -225,20 +225,22 @@ int iscsi_login_portal_nowait(struct node_rec *rec)
}
/**
- * iscsi_login_portals - login into portals on @rec_list,
+ * __iscsi_login_portals - login into portals on @rec_list,
* @data: data to pass to login_fn
* @nr_found: returned with number of portals logged into
* @wait: bool indicating if the fn should wait for the result
* @rec_list: list of portals to log into
+ * @clear_list: If set, delete and free rec_list after iterating through.
* @login_fn: list iter function
*
* This will loop over the list of portals and login. It
* will attempt to login asynchronously, and then wait for
* them to complete if wait is set.
*/
-int iscsi_login_portals(void *data, int *nr_found, int wait,
- struct list_head *rec_list,
- int (* login_fn)(void *, struct list_head *,
+static
+int __iscsi_login_portals(void *data, int *nr_found, int wait,
+ struct list_head *rec_list, int clear_list,
+ int (*login_fn)(void *, struct list_head *,
struct node_rec *))
{
struct node_rec *curr_rec, *tmp;
@@ -262,13 +264,50 @@ int iscsi_login_portals(void *data, int *nr_found, int wait,
} else
iscsid_reqs_close(&login_list);
- list_for_each_entry_safe(curr_rec, tmp, rec_list, list) {
- list_del(&curr_rec->list);
- free(curr_rec);
+ if (clear_list) {
+ list_for_each_entry_safe(curr_rec, tmp, rec_list, list) {
+ list_del(&curr_rec->list);
+ free(curr_rec);
+ }
}
return ret;
}
+/**
+ * iscsi_login_portals - login into portals on @rec_list,
+ * @data: data to pass to login_fn
+ * @nr_found: returned with number of portals logged into
+ * @wait: bool indicating if the fn should wait for the result
+ * @rec_list: list of portals to log into. This list is deleted after
+ * iterating through it.
+ * @login_fn: list iter function
+ *
+ * This will loop over the list of portals and login. It
+ * will attempt to login asynchronously, and then wait for
+ * them to complete if wait is set.
+ */
+int iscsi_login_portals(void *data, int *nr_found, int wait,
+ struct list_head *rec_list,
+ int (*login_fn)(void *, struct list_head *,
+ struct node_rec *))
+{
+ return __iscsi_login_portals(data, nr_found, wait, rec_list,
+ 1, login_fn);
+}
+
+/**
+ * iscsi_login_portals_safe - login into portals on @rec_list, but do not
+ * clear out rec_list.
+ */
+int iscsi_login_portals_safe(void *data, int *nr_found, int wait,
+ struct list_head *rec_list,
+ int (*login_fn)(void *, struct list_head *,
+ struct node_rec *))
+{
+ return __iscsi_login_portals(data, nr_found, wait, rec_list,
+ 0, login_fn);
+}
+
static void log_logout_msg(struct session_info *info, int rc)
{
if (rc) {
diff --git a/usr/session_mgmt.h b/usr/session_mgmt.h
index b800fd7..a28bfa4 100644
--- a/usr/session_mgmt.h
+++ b/usr/session_mgmt.h
@@ -10,7 +10,11 @@ extern int iscsi_login_portal(void *data, struct list_head *list,
extern int iscsi_login_portal_nowait(struct node_rec *rec);
extern int iscsi_login_portals(void *data, int *nr_found, int wait,
struct list_head *rec_list,
- int (* login_fn)(void *, struct list_head *,
+ int (*login_fn)(void *, struct list_head *,
+ struct node_rec *));
+extern int iscsi_login_portals_safe(void *data, int *nr_found, int wait,
+ struct list_head *rec_list,
+ int (*login_fn)(void *, struct list_head *,
struct node_rec *));
extern int iscsi_logout_portal(struct session_info *info,
struct list_head *list);