Leading-login support only takes effect when performing
"login-by-startup" (ie, iscsiadm -m node -L ...).  For any nodes with
this value set to "Yes" (The default is "No"), an attempt will be made
on each successive iface record until the first one succeeds.  The intent
is to only have a single iSCSI session for each target regardless of the
number of iface records in the system.

Copyright (c) 2011 Dell Inc.
Signed-off-by: Jim Ramsay <[email protected]>
---
 etc/iscsid.conf    |    4 ++
 usr/config.h       |    1 +
 usr/idbm.c         |    4 ++
 usr/idbm_fields.h  |    1 +
 usr/iscsi_util.c   |    8 +++
 usr/iscsi_util.h   |    1 +
 usr/iscsiadm.c     |  127 ++++++++++++++++++++++++++++++++++++++++++---------
 usr/session_mgmt.c |   47 +++++++++++++++++---
 usr/session_mgmt.h |    4 ++
 9 files changed, 168 insertions(+), 29 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 1b35d97..c01b159 100644
--- a/usr/config.h
+++ b/usr/config.h
@@ -229,6 +229,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 bc49793..97c76d7 100644
--- a/usr/idbm.c
+++ b/usr/idbm.c
@@ -209,6 +209,9 @@ 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
@@ -2423,6 +2426,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..a60f9a6 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 6ce6cf3..4ee32c8 100644
--- a/usr/iscsi_util.c
+++ b/usr/iscsi_util.c
@@ -342,3 +342,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 916042f..54b0154 100644
--- a/usr/iscsiadm.c
+++ b/usr/iscsiadm.c
@@ -379,18 +379,52 @@ 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;
 
-       if (match_startup_mode(rec, mode))
+       // Skip any records that do not match the pattern iface
+       if (!iface_match(pattern_iface, &rec->iface))
                return -1;
 
+       // 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 +432,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 +441,76 @@ 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, "Trying to establish leading-logins using 
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 leading-login 
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.
+               }
+               // 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++;
+                       }
+                       // Also cleanup the list, since 
'iscsi_login_portals_safe' does not
+                       list_del(&rec->list);
+                       free(rec);
+               }
+               if (missed_leading_login) {
+                       log_error("Could not log into 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 3546b8e..509e35f 100644
--- a/usr/session_mgmt.c
+++ b/usr/session_mgmt.c
@@ -216,19 +216,21 @@ 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,
+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 *))
 {
@@ -253,13 +255,46 @@ 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..cd20714 100644
--- a/usr/session_mgmt.h
+++ b/usr/session_mgmt.h
@@ -12,6 +12,10 @@ extern 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 *));
+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);
 extern int iscsi_logout_portals(void *data, int *nr_found, int wait,
-- 
1.7.5.3


-- 
You received this message because you are subscribed to the Google Groups 
"open-iscsi" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/open-iscsi?hl=en.

Reply via email to