Leading-login only takes effect when performing "login-by-startup" (ie,
iscsiadm -m node -L ...).  For any nodes with the new
'node.leading_login' config value set to "Yes" (The default is "No"), a
login attempt will be made on each successive iface record until one
succeeds.

The intent is to only have a single iSCSI session for each configured
target at startup but also allow for fallthrough to alternate ifaces in
case of network issues.

Signed-off-by: Jim Ramsay <jim_ram...@dell.com>
---
 etc/iscsid.conf    |    4 ++
 usr/config.h       |    1 +
 usr/idbm.c         |    3 +
 usr/idbm_fields.h  |    1 +
 usr/iscsi_util.c   |    8 +++
 usr/iscsi_util.h   |    1 +
 usr/iscsiadm.c     |  152 ++++++++++++++++++++++++++++++++++++++++++++--------
 usr/session_mgmt.c |   47 ++++++++++++++--
 usr/session_mgmt.h |    4 ++
 9 files changed, 192 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 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..49cf5aa 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,98 @@ 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 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 fdb7357..5093849 100644
--- a/usr/session_mgmt.c
+++ b/usr/session_mgmt.c
@@ -222,19 +222,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 *))
 {
@@ -259,13 +261,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 open-iscsi@googlegroups.com.
To unsubscribe from this group, send email to 
open-iscsi+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/open-iscsi?hl=en.

Reply via email to