From: Adheer Chandravanshi <[email protected]>

Add support to maintain and show some host statistics in iscsid.
This provides following host specific stats:
        iscsi_login_accept_rsps
        iscsi_login_other_fail_rsps
        iscsi_login_negotiate_fails
        iscsi_login_authenticate_fails
        iscsi_login_auth_fail_rsps
        iscsi_login_redirect_rsps
        iscsi_logout_normals
        iscsi_logout_others
        iscsi_connection_timeout_errors
        iscsi_session_failures

Signed-off-by: Adheer Chandravanshi <[email protected]>
---
 include/iscsi_if.h     |   1 +
 usr/discovery.c        |  32 +++++++++-
 usr/event_poll.c       |  12 +++-
 usr/event_poll.h       |   3 +-
 usr/host.c             |  45 ++++++++++++++
 usr/host.h             |  14 +++++
 usr/initiator.c        | 100 +++++++++++++++++++++++++-----
 usr/initiator.h        |  16 ++++-
 usr/initiator_common.c | 162 ++++++++++++++++++++++++++++++++++++++++++++-----
 usr/iscsiadm.c         |  25 +++++---
 usr/iscsid.c           |  12 +++-
 usr/iscsistart.c       |  10 ++-
 usr/mgmt_ipc.c         |  19 ++++++
 usr/mgmt_ipc.h         |   9 +++
 usr/netlink.c          |   3 +-
 usr/transport.c        |   1 +
 usr/transport.h        |   1 +
 17 files changed, 416 insertions(+), 49 deletions(-)

diff --git a/include/iscsi_if.h b/include/iscsi_if.h
index 9d15811..0576dfb 100644
--- a/include/iscsi_if.h
+++ b/include/iscsi_if.h
@@ -536,6 +536,7 @@ enum iscsi_err {
        ISCSI_ERR_XMIT_FAILED           = ISCSI_ERR_BASE + 19,
        ISCSI_ERR_TCP_CONN_CLOSE        = ISCSI_ERR_BASE + 20,
        ISCSI_ERR_SCSI_EH_SESSION_RST   = ISCSI_ERR_BASE + 21,
+       ISCSI_ERR_NOP_TIMEDOUT          = ISCSI_ERR_BASE + 22,
 };
 
 /*
diff --git a/usr/discovery.c b/usr/discovery.c
index 593d226..4d23381 100644
--- a/usr/discovery.c
+++ b/usr/discovery.c
@@ -1060,6 +1060,11 @@ done:
                conn->socket_fd = -1;
        }
        session->id = -1;
+
+       if (session->host) {
+               iscsi_host_put(session->host);
+               session->host = NULL;
+       }
 }
 
 static int iscsi_create_leading_conn(struct iscsi_session *session)
@@ -1104,7 +1109,15 @@ static int iscsi_create_leading_conn(struct 
iscsi_session *session)
                 * if offload is used.
                 */
                session->conn[0].bind_ep = 1;
-               session->hostno = host_no;
+               /*
+                * some offload functions need the host so create now for
+                * them
+                */
+               session->host = iscsi_host_create(host_no);
+               if (!session->host) {
+                       rc = ISCSI_ERR_NOMEM;
+                       goto close_ipc;
+               }
        }
 
        rc = iscsi_host_set_net_params(iface, session);
@@ -1113,7 +1126,7 @@ static int iscsi_create_leading_conn(struct iscsi_session 
*session)
                          rc);
                if (rc != ISCSI_ERR_AGAIN)
                        rc = ISCSI_ERR_INTERNAL;
-               goto close_ipc;
+               goto free_host;
        }
 
        /* create interconnect endpoint */
@@ -1121,7 +1134,7 @@ static int iscsi_create_leading_conn(struct iscsi_session 
*session)
        rc = t->template->ep_connect(conn, 1);
        if (rc < 0) {
                rc = ISCSI_ERR_TRANS;
-               goto close_ipc;
+               goto free_host;
        }
 
        do {
@@ -1150,6 +1163,15 @@ static int iscsi_create_leading_conn(struct 
iscsi_session *session)
                rc = ISCSI_ERR_INTERNAL;
                goto disconnect;
        }
+
+       if (!session->host) {
+               session->host = iscsi_host_create(host_no);
+               if (!session->host) {
+                       rc = ISCSI_ERR_NOMEM;
+                       goto disconnect;
+               }
+       }
+
        log_debug(2, "%s discovery created session %u", __FUNCTION__,
                  session->id);
        session->isid[3] = (session->id >> 16) & 0xff;
@@ -1196,6 +1218,10 @@ disconnect:
                session->id = -1;
        }
 
+free_host:
+       iscsi_host_put(session->host);
+       session->host = NULL;
+
 close_ipc:
        if (conn->socket_fd >= 0) {
                ipc->ctldev_close();
diff --git a/usr/event_poll.c b/usr/event_poll.c
index 209ee02..52391db 100644
--- a/usr/event_poll.c
+++ b/usr/event_poll.c
@@ -38,6 +38,7 @@
 #include "actor.h"
 #include "initiator.h"
 #include "iscsi_err.h"
+#include "host.h"
 
 static unsigned int reap_count;
 
@@ -121,7 +122,8 @@ static int shutdown_wait_pids(void)
 #define POLL_CTRL      0
 #define POLL_IPC       1
 #define POLL_ALARM     2
-#define POLL_MAX       3
+#define POLL_UDEV      3
+#define POLL_MAX       4
 
 static int event_loop_stop;
 static queue_task_t *shutdown_qtask; 
@@ -132,7 +134,8 @@ void event_loop_exit(queue_task_t *qtask)
        event_loop_stop = 1;
 }
 
-void event_loop(struct iscsi_ipc *ipc, int control_fd, int mgmt_ipc_fd)
+void event_loop(struct iscsi_ipc *ipc, int control_fd, int mgmt_ipc_fd,
+               int udev_fd)
 {
        struct pollfd poll_array[POLL_MAX];
        int res, has_shutdown_children = 0;
@@ -156,6 +159,8 @@ void event_loop(struct iscsi_ipc *ipc, int control_fd, int 
mgmt_ipc_fd)
        poll_array[POLL_IPC].events = POLLIN;
        poll_array[POLL_ALARM].fd = sig_fd;
        poll_array[POLL_ALARM].events = POLLIN;
+       poll_array[POLL_UDEV].fd = udev_fd;
+       poll_array[POLL_UDEV].events = POLLIN;
 
        event_loop_stop = 0;
        while (1) {
@@ -192,6 +197,9 @@ void event_loop(struct iscsi_ipc *ipc, int control_fd, int 
mgmt_ipc_fd)
                                        log_debug(1, "Poll was woken by an 
alarm");
                                }
                        }
+
+                       if (poll_array[POLL_UDEV].revents)
+                               udev_event_handle(udev_fd);
                } else if (res < 0) {
                        if (errno == EINTR) {
                                log_debug(1, "event_loop interrupted");
diff --git a/usr/event_poll.h b/usr/event_poll.h
index c0eed5c..0a7ed7c 100644
--- a/usr/event_poll.h
+++ b/usr/event_poll.h
@@ -25,7 +25,8 @@ struct queue_task;
 int shutdown_callback(pid_t pid);
 void reap_proc(void);
 void reap_inc(void);
-void event_loop(struct iscsi_ipc *ipc, int control_fd, int mgmt_ipc_fd);
+void event_loop(struct iscsi_ipc *ipc, int control_fd, int mgmt_ipc_fd,
+               int udev_fd);
 void event_loop_exit(struct queue_task *qtask);
 
 #endif
diff --git a/usr/host.c b/usr/host.c
index f2052d3..b0dc914 100644
--- a/usr/host.c
+++ b/usr/host.c
@@ -35,6 +35,9 @@
 #include "iface.h"
 #include "iscsi_err.h"
 #include "iscsi_netlink.h"
+#include "iscsid_req.h"
+
+LIST_HEAD(iscsi_host);
 
 static int match_host_to_session(void *data, struct session_info *info)
 {
@@ -424,3 +427,45 @@ int chap_build_config(struct iscsi_chap_rec *crec, struct 
iovec *iovs)
 
        return count;
 }
+
+int host_print_login_stats(int host_no)
+{
+       int rc;
+       iscsiadm_req_t req;
+       iscsiadm_rsp_t rsp;
+       struct iscsi_login_stats *stats;
+
+       memset(&req, 0, sizeof(req));
+       req.command = MGMT_IPC_HOST_STATS;
+       req.u.host_login_stats.host_no = host_no;
+
+       rc = iscsid_exec_req(&req, &rsp, 1);
+       if (rc)
+               return rc;
+
+       stats = &rsp.u.host_login_stats.stats;
+
+       printf("Host Login Statistics:\n"
+               "\tiscsi_login_accept_rsps: %llu\n"
+               "\tiscsi_login_other_fail_rsps: %llu\n"
+               "\tiscsi_login_negotiate_fails: %llu\n"
+               "\tiscsi_login_authenticate_fails: %llu\n"
+               "\tiscsi_login_auth_fail_rsps: %llu\n"
+               "\tiscsi_login_redirect_rsps: %llu\n"
+               "\tiscsi_logout_normals: %llu\n"
+               "\tiscsi_logout_others: %llu\n"
+               "\tiscsi_connection_timeout_errors: %llu\n"
+               "\tiscsi_session_failures: %llu\n",
+               (unsigned long long)stats->login_accept_rsps,
+               (unsigned long long)stats->login_other_fail_rsps,
+               (unsigned long long)stats->login_negotiate_fails,
+               (unsigned long long)stats->login_authenticate_fails,
+               (unsigned long long)stats->login_auth_fail_rsps,
+               (unsigned long long)stats->login_redirect_rsps,
+               (unsigned long long)stats->logout_normals,
+               (unsigned long long)stats->logout_others,
+               (unsigned long long)stats->connection_timeout_errors,
+               (unsigned long long)stats->session_failures);
+
+       return 0;
+}
diff --git a/usr/host.h b/usr/host.h
index 149aa0d..1a16db7 100644
--- a/usr/host.h
+++ b/usr/host.h
@@ -16,7 +16,21 @@ struct host_info {
         uint32_t host_no;
 };
 
+struct iscsi_login_stats {
+       uint64_t login_accept_rsps;
+       uint64_t login_other_fail_rsps;
+       uint64_t login_negotiate_fails;
+       uint64_t login_authenticate_fails;
+       uint64_t login_auth_fail_rsps;
+       uint64_t login_redirect_rsps;
+       uint64_t logout_normals;
+       uint64_t logout_others;
+       uint64_t connection_timeout_errors;
+       uint64_t session_failures;
+};
+
 extern int host_info_print(int info_level, uint32_t host_no);
 extern int chap_build_config(struct iscsi_chap_rec *crec, struct iovec *iovs);
+extern int host_print_login_stats(int host_no);
 
 #endif
diff --git a/usr/initiator.c b/usr/initiator.c
index 8cd1896..24f9368 100644
--- a/usr/initiator.c
+++ b/usr/initiator.c
@@ -142,6 +142,7 @@ static void iscsi_ev_context_put(struct iscsi_ev_context 
*ev_context)
        ev_context->allocated = 0;
 }
 
+
 static void session_online_devs(int host_no, int sid)
 {
        iscsi_sysfs_for_each_device(NULL, host_no, sid,
@@ -152,14 +153,18 @@ static conn_login_status_e
 __login_response_status(iscsi_conn_t *conn,
                      enum iscsi_login_status login_status)
 {
+       struct iscsi_login_stats *stats = &conn->session->host->login_stats;
+
        switch (login_status) {
        case LOGIN_OK:
                /* check the status class and detail */
                return CONN_LOGIN_SUCCESS;
        case LOGIN_REDIRECT:
+               stats->login_redirect_rsps++;
                return CONN_LOGIN_IMM_REDIRECT_RETRY;
        case LOGIN_IO_ERROR:
        case LOGIN_REDIRECTION_FAILED:
+               stats->login_other_fail_rsps++;
                return CONN_LOGIN_RETRY;
        default:
                log_error("Login error (Login status %d) on conn %d", conn->id,
@@ -175,11 +180,15 @@ __check_iscsi_status_class(iscsi_session_t *session, int 
cid,
                        uint8_t status_class, uint8_t status_detail)
 {
        iscsi_conn_t *conn = &session->conn[cid];
+       struct iscsi_login_stats *stats = &session->host->login_stats;
 
        switch (status_class) {
        case ISCSI_STATUS_CLS_SUCCESS:
+               stats->login_accept_rsps++;
                return CONN_LOGIN_SUCCESS;
        case ISCSI_STATUS_CLS_REDIRECT:
+               stats->login_redirect_rsps++;
+
                switch (status_detail) {
                case ISCSI_LOGIN_STATUS_TGT_MOVED_TEMP:
                        return CONN_LOGIN_IMM_RETRY;
@@ -204,15 +213,18 @@ __check_iscsi_status_class(iscsi_session_t *session, int 
cid,
                        log_error("session %d login rejected: Initiator "
                               "failed authentication with target",
                                session->id);
+                       stats->login_auth_fail_rsps++;
                        return CONN_LOGIN_AUTH_FAILED;
                case ISCSI_LOGIN_STATUS_TGT_FORBIDDEN:
                        log_error("conn %d login rejected: initiator "
                               "failed authorization with target", conn->id);
+                       stats->login_authenticate_fails++;
                        return CONN_LOGIN_AUTH_FAILED;
                case ISCSI_LOGIN_STATUS_TGT_NOT_FOUND:
                        log_error("conn %d login rejected: initiator "
                               "error - target not found (%02x/%02x)",
                               conn->id, status_class, status_detail);
+                       stats->login_other_fail_rsps++;
                        return CONN_LOGIN_FAILED;
                case ISCSI_LOGIN_STATUS_NO_VERSION:
                        /*
@@ -224,16 +236,30 @@ __check_iscsi_status_class(iscsi_session_t *session, int 
cid,
                               "version (%02x/%02x), non-retryable, "
                               "giving up", conn->id, status_class,
                               status_detail);
+                       stats->login_other_fail_rsps++;
+                       return CONN_LOGIN_FAILED;
+               case ISCSI_LOGIN_STATUS_TGT_REMOVED:
+                       log_error("conn %d login rejected: target removed "
+                                 "(%02x/%02x)", conn->id, status_class,
+                                 status_detail);
+                       stats->login_negotiate_fails++;
+               case ISCSI_LOGIN_STATUS_MISSING_FIELDS:
+                       log_error("conn %d login rejected: missing parameters "
+                                 "(%02x/%02x)", conn->id, status_class,
+                                 status_detail);
+                       stats->login_negotiate_fails++;
                        return CONN_LOGIN_FAILED;
                default:
                        log_error("conn %d login rejected: initiator "
-                              "error (%02x/%02x)", conn->id, status_class,
-                              status_detail);
+                                 "error (%02x/%02x)", conn->id, status_class,
+                                 status_detail);
+                       stats->login_other_fail_rsps++;
                        return CONN_LOGIN_FAILED;
                }
        case ISCSI_STATUS_CLS_TARGET_ERR:
                log_error("conn %d login rejected: target error "
                       "(%02x/%02x)", conn->id, status_class, status_detail);
+               stats->login_other_fail_rsps++;
                /*
                 * We have no idea what the problem is. But spec says initiator
                 * may retry later.
@@ -243,6 +269,7 @@ __check_iscsi_status_class(iscsi_session_t *session, int 
cid,
                log_error("conn %d login response with unknown status "
                       "class 0x%x, detail 0x%x", conn->id, status_class,
                       status_detail);
+               stats->login_other_fail_rsps++;
                break;
        }
 
@@ -409,7 +436,11 @@ __session_create(node_rec_t *rec, struct iscsi_transport 
*t, int *rc)
                          * if offload is used.
                          */
                         session->conn[0].bind_ep = 1;
-                        session->hostno = hostno;
+                       session->host = iscsi_host_create(hostno);
+                       if (!session->host) {
+                               *rc = ISCSI_ERR_NOMEM;
+                               goto free_session;
+                       }
                 } else if (*rc == ISCSI_ERR_HOST_NOT_FOUND) {
                         goto free_session;     
                 } else {
@@ -447,6 +478,7 @@ static void
 __session_destroy(iscsi_session_t *session)
 {
        log_debug(1, "destroying session");
+       iscsi_host_put(session->host);
        list_del(&session->list);
        iscsi_flush_context_pool(session);
        session_release(session);
@@ -871,11 +903,26 @@ static void session_conn_error(void *data)
 
        iscsi_ev_context_put(ev_context);
 
+       /*
+        * kernel could return some errors before session and host is fully
+        * setup
+        */
+       if (session->host)
+               session->host->login_stats.session_failures++;
+
        switch (error) {
        case ISCSI_ERR_INVALID_HOST:
                if (session_conn_shutdown(conn, NULL, ISCSI_SUCCESS))
                        log_error("BUG: Could not shutdown session.");
                break;
+       case ISCSI_ERR_NOP_TIMEDOUT:
+       case ISCSI_ERR_SCSI_EH_SESSION_RST:
+               /*
+                * Connection/session was dropped due to scsi command, TMF, or
+                * Nop-as-ping timing out.
+                */
+               session->host->login_stats.connection_timeout_errors++;
+               /* fall through */
        default:
                __conn_error_handle(session, conn);
        }
@@ -1048,7 +1095,8 @@ setup_full_feature_phase(iscsi_conn_t *conn)
                 * don't want to re-scan it on recovery.
                 */
                if (conn->id == 0)
-                       session_scan_host(session, session->hostno, c->qtask);
+                       session_scan_host(session, session->host->host_no,
+                                         c->qtask);
 
                log_warning("Connection%d:%d to [target: %s, portal: %s,%d] "
                            "through [iface: %s] is operational now",
@@ -1059,7 +1107,7 @@ setup_full_feature_phase(iscsi_conn_t *conn)
        } else {
                session->notify_qtask = NULL;
 
-               session_online_devs(session->hostno, session->id);
+               session_online_devs(session->host->host_no, session->id);
                mgmt_ipc_write_rsp(c->qtask, ISCSI_SUCCESS);
                log_warning("connection%d:%d is operational after recovery "
                            "(%d attempts)", session->id, conn->id,
@@ -1170,6 +1218,7 @@ static void iscsi_recv_nop_in(iscsi_conn_t *conn, struct 
iscsi_hdr *hdr)
 static void iscsi_recv_logout_rsp(iscsi_conn_t *conn, struct iscsi_hdr *hdr)
 {
        struct iscsi_logout_rsp *logout_rsp = (struct iscsi_logout_rsp *)hdr;
+       iscsi_session_t *session = conn->session;
 
        log_debug(3, "Recv: logout response %d", logout_rsp->response);
        if (logout_rsp->response == 2 || logout_rsp->response == 3) {
@@ -1177,6 +1226,12 @@ static void iscsi_recv_logout_rsp(iscsi_conn_t *conn, 
struct iscsi_hdr *hdr)
                log_debug(4, "logout rsp returned time2wait %u",
                          conn->session->def_time2wait);
        }
+
+       if (!logout_rsp->response)
+               session->host->login_stats.logout_normals++;
+       else
+               session->host->login_stats.logout_others++;
+
        /* TODO process the hdr */
        __conn_error_handle(conn->session, conn);
 }
@@ -1203,7 +1258,8 @@ static void iscsi_recv_async_msg(iscsi_conn_t *conn, 
struct iscsi_hdr *hdr)
                }
 
                if (sshdr.asc == 0x3f && sshdr.ascq == 0x0e)
-                       session_scan_host(session, session->hostno, NULL);
+                       session_scan_host(session, session->host->host_no,
+                                         NULL);
                break;
        case ISCSI_ASYNC_MSG_REQUEST_LOGOUT:
                log_warning("Target requests logout within %u seconds for "
@@ -1242,8 +1298,11 @@ static void iscsi_recv_login_rsp(struct iscsi_conn *conn)
        struct iscsi_session *session = conn->session;
        iscsi_login_context_t *c = &conn->login_context;
        int err = ISCSI_ERR_FATAL_LOGIN;
+       struct iscsi_login_stats *stats = &session->host->login_stats;
 
-       if (iscsi_login_rsp(session, c)) {
+       if (!iscsi_login_rsp(session, c)) {
+               stats->login_accept_rsps++;
+       } else {
                log_debug(1, "login_rsp ret (%d)", c->ret);
 
                switch (__login_response_status(conn, c->ret)) {
@@ -1418,7 +1477,7 @@ static void session_increase_wq_priority(struct 
iscsi_session *session)
                *proc_name_end = '\0';
 
                if (sscanf(proc_name, "iscsi_q_%u\n", &host_no) == 1) {
-                       if (host_no == session->hostno) {
+                       if (host_no == session->host->host_no) {
                                if (!setpriority(PRIO_PROCESS, pid,
                                        
session->nrec.session.xmit_thread_priority)) {
                                        closedir(proc_dir);
@@ -1462,9 +1521,16 @@ retry_create:
        }
 
        if (!err) {
-               session->hostno = host_no;
+               if (!session->host) {
+                       session->host = iscsi_host_create(host_no);
+                       if (!session->host) {
+                               /* caller will destroy session for us */
+                               return -ENOMEM;
+                       }
+               }
                session_increase_wq_priority(session);
        }
+
        return err;
 }
 
@@ -1536,6 +1602,7 @@ static void session_conn_poll(void *data)
                        iscsi_login_eh(conn, qtask, ISCSI_ERR_INTERNAL);
                        return;
                }
+
                ev_context->data = qtask;
                /* not connected yet, check later */
                iscsi_sched_ev_context(ev_context, conn, 1, EV_CONN_POLL);
@@ -1550,8 +1617,8 @@ static void session_conn_poll(void *data)
                                err = ISCSI_ERR_INTERNAL;
                                goto cleanup;
                        }
-                       log_debug(3, "created new iSCSI session sid %d host "
-                                 "no %u", session->id, session->hostno);
+                       log_debug(3, "created new iSCSI session sid %d host %u",
+                                 session->id, session->host->host_no);
 
                        err = ipc->create_conn(session->t->handle,
                                        session->id, conn->id, &conn->id);
@@ -1656,7 +1723,7 @@ static void session_conn_process_login(void *data)
         * logged in
         */
        log_debug(3, "session created sid %u host no %d", session->id,
-                 session->hostno);
+                 session->host->host_no);
 
        if (session->r_stage == R_STAGE_NO_CHANGE ||
            session->r_stage == R_STAGE_SESSION_REDIRECT) {
@@ -1664,7 +1731,7 @@ static void session_conn_process_login(void *data)
                 * scan host is one-time deal. We
                 * don't want to re-scan it on recovery.
                 */
-               session_scan_host(session, session->hostno,
+               session_scan_host(session, session->host->host_no,
                                 c->qtask);
                session->notify_qtask = NULL;
 
@@ -1995,6 +2062,7 @@ iscsi_sync_session(node_rec_t *rec, queue_task_t *qtask, 
uint32_t sid)
 {
        iscsi_session_t *session;
        struct iscsi_transport *t;
+       uint32_t hostno;
        int err;
 
        t = iscsi_sysfs_get_transport_by_name(rec->iface.transport_name);
@@ -2006,12 +2074,16 @@ iscsi_sync_session(node_rec_t *rec, queue_task_t 
*qtask, uint32_t sid)
                return ISCSI_ERR_LOGIN;
 
        session->id = sid;
-       session->hostno = iscsi_sysfs_get_host_no_from_sid(sid, &err);
+       hostno = iscsi_sysfs_get_host_no_from_sid(sid, &err);
        if (err) {
                log_error("Could not get hostno for session %d", sid);
                goto destroy_session;
        }
 
+       session->host = iscsi_host_create(hostno);
+       if (!session->host)
+               goto destroy_session;
+
        session->r_stage = R_STAGE_SESSION_REOPEN;
 
        err = sync_conn(session, 0);
diff --git a/usr/initiator.h b/usr/initiator.h
index c11d77f..ca0a191 100644
--- a/usr/initiator.h
+++ b/usr/initiator.h
@@ -32,6 +32,7 @@
 #include "config.h"
 #include "actor.h"
 #include "list.h"
+#include "host.h"
 
 #define ISCSI_CONFIG_ROOT      "/etc/iscsi/"
 
@@ -191,6 +192,13 @@ typedef struct queue_task {
        void *payload;
 } queue_task_t;
 
+struct iscsi_host {
+       struct iscsi_login_stats login_stats;
+       uint32_t host_no;
+       uint32_t ref_count;
+       struct list_head list;
+};
+
 struct iscsi_transport_template;
 struct iscsi_transport;
 
@@ -198,7 +206,7 @@ struct iscsi_transport;
 typedef struct iscsi_session {
        struct list_head list;
        uint32_t id;
-       uint32_t hostno;
+       struct iscsi_host *host;
        char netdev[IFNAMSIZ];
        struct iscsi_transport *t;
        uint8_t use_ipc;
@@ -359,4 +367,10 @@ extern int iscsi_set_net_config(struct iscsi_transport *t,
                                struct iface_rec *iface);
 extern void iscsi_session_init_params(struct iscsi_session *session);
 
+/* initiator host code */
+extern struct iscsi_host *iscsi_host_find_by_host_no(uint32_t host_no);
+extern void iscsi_host_put(struct iscsi_host *host);
+extern struct iscsi_host *iscsi_host_create(uint32_t host_no);
+extern int udev_event_listen(void);
+extern void udev_event_handle(int udev_fd);
 #endif /* INITIATOR_H */
diff --git a/usr/initiator_common.c b/usr/initiator_common.c
index c02643a..e040bde 100644
--- a/usr/initiator_common.c
+++ b/usr/initiator_common.c
@@ -23,6 +23,10 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <errno.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <sys/socket.h>
+#include <linux/netlink.h>
 
 #include "initiator.h"
 #include "transport.h"
@@ -37,6 +41,143 @@
 #include "iscsi_err.h"
 #include "iscsi_net_util.h"
 
+#define UEVENT_BUFFER_SIZE     2048
+
+static LIST_HEAD(iscsi_hosts);
+
+struct iscsi_host *iscsi_host_find_by_host_no(uint32_t host_no)
+{
+       struct iscsi_host *host;
+
+       list_for_each_entry(host, &iscsi_hosts, list) {
+               if (host->host_no == host_no) {
+                       log_debug(7, "Found iSCSI host %d\n", host->host_no);
+                       return host;
+               }
+       }
+
+       log_debug(7, "iSCSI host %d not found\n", host->host_no);
+       return NULL;
+}
+
+static void iscsi_host_get(struct iscsi_host *host)
+{
+       host->ref_count++;
+}
+
+#define SCSI_HOST_SUBSYS                "scsi_host"
+void iscsi_host_put(struct iscsi_host *host)
+{
+       char id[NAME_SIZE];
+       char devpath[PATH_SIZE];
+
+       if (!host)
+               return;
+
+       snprintf(id, sizeof(id), "host%u", host->host_no);
+
+       /* For sotware iSCSI if all sessions are logout, sysfs host will be
+        * destroyed and we will not get devpath */
+       if (!sysfs_lookup_devpath_by_subsys_id(devpath, sizeof(devpath),
+                                              SCSI_HOST_SUBSYS, id)) {
+               log_error("Could not lookup devpath for %s.\n", id);
+               goto free_host;
+       }
+
+       /* If we have valid host path, its offload iSCSI do not destroy host */
+       return;
+free_host:
+       host->ref_count--;
+       if (!host->ref_count) {
+               log_debug(1, "free %s\n", id);
+               list_del(&host->list);
+               free(host);
+       }
+}
+
+void iscsi_offload_host_put(uint32_t host_no)
+{
+       struct iscsi_host *host = iscsi_host_find_by_host_no(host_no);
+
+       if (!host)
+               return;
+
+       log_debug(1, "free host%u\n", host->host_no);
+       list_del(&host->list);
+       free(host);
+}
+
+struct iscsi_host *iscsi_host_create(uint32_t host_no)
+{
+       struct iscsi_host *host = iscsi_host_find_by_host_no(host_no);
+
+       if (!host) {
+               host = calloc(1, sizeof(*host));
+               if (!host)
+                       return NULL;
+               host->host_no = host_no;
+               INIT_LIST_HEAD(&host->list);
+               list_add_tail(&host->list, &iscsi_hosts);
+       }
+
+       iscsi_host_get(host);
+       return host;
+}
+
+int udev_event_listen(void)
+{
+       int udev_fd = -1;
+       int ret = -1;
+       struct sockaddr_nl snl;
+
+       bzero(&snl, sizeof(struct sockaddr_nl));
+       snl.nl_family = AF_NETLINK;
+       snl.nl_pid = getpid();
+       snl.nl_groups = 1;
+
+       udev_fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
+       if (udev_fd < 0) {
+               log_error("Can not create udev event socket\n");
+               return udev_fd;
+       }
+
+       ret = bind(udev_fd, (struct sockaddr *)&snl,
+                  sizeof(struct sockaddr_nl));
+       if (ret < 0) {
+               log_error("Can not bind udev event socket\n");
+               close(udev_fd);
+               return ret;
+       }
+
+       return udev_fd;
+}
+
+void udev_event_handle(int udev_fd)
+{
+       char buf[UEVENT_BUFFER_SIZE] = {0};
+       char *tmp_buf1, *tmp_buf2;
+       char *pos;
+       uint32_t host_no;
+
+       memset(buf, 0, sizeof(buf));
+       recv(udev_fd, &buf, sizeof(buf), 0);
+
+       tmp_buf1 = strstr(buf, "remove");
+       if (tmp_buf1 == NULL)
+               return;
+
+       tmp_buf2 = strstr(tmp_buf1, "iscsi_host");
+       if (tmp_buf2 == NULL)
+               return;
+
+       pos = &tmp_buf2[strlen(tmp_buf2)];
+       while (isdigit(pos[-1]))
+               pos--;
+
+       host_no = atoi(pos);
+       iscsi_offload_host_put(host_no);
+}
+
 struct iscsi_session *session_find_by_sid(uint32_t sid)
 {
        struct iscsi_transport *t;
@@ -299,7 +440,7 @@ int iscsi_host_set_params(struct iscsi_session *session)
        };
 
        for (i = 0; i < MAX_HOST_PARAMS; i++) {
-               if (host_set_param(t, session->hostno,
+               if (host_set_param(t, session->host->host_no,
                                   hosttbl[i].param, hosttbl[i].value,
                                   hosttbl[i].type)) {
                        return EPERM;
@@ -631,20 +772,11 @@ int iscsi_set_net_config(struct iscsi_transport *t, 
iscsi_session_t *session,
        if (t->template->set_net_config) {
                /* uip needs the netdev name */
                struct host_info hinfo;
-               int hostno, rc;
-
-               /* this assumes that the netdev or hw address is going to be
-                  set */
-               hostno = iscsi_sysfs_get_host_no_from_hwinfo(iface, &rc);
-               if (rc) {
-                       log_debug(4, "Couldn't get host no.");
-                       return rc;
-               }
 
                /* uip needs the netdev name */
                if (!strlen(iface->netdev)) {
                        memset(&hinfo, 0, sizeof(hinfo));
-                       hinfo.host_no = hostno;
+                       hinfo.host_no = session->host->host_no;
                        iscsi_sysfs_get_hostinfo_by_host_no(&hinfo);
                        strcpy(iface->netdev, hinfo.iface.netdev);
                }
@@ -692,7 +824,7 @@ int iscsi_host_set_net_params(struct iface_rec *iface,
                netdev = iface->netdev;
        else {
                memset(&hinfo, 0, sizeof(hinfo));
-               hinfo.host_no = session->hostno;
+               hinfo.host_no = session->host->host_no;
                iscsi_sysfs_get_hostinfo_by_host_no(&hinfo);
 
                netdev = hinfo.iface.netdev;
@@ -706,14 +838,14 @@ int iscsi_host_set_net_params(struct iface_rec *iface,
        if (rc != 0)
                return rc;
 
-       rc = host_set_param(t, session->hostno,
+       rc = host_set_param(t, session->host->host_no,
                            ISCSI_HOST_PARAM_IPADDRESS,
                            iface->ipaddress, ISCSI_STRING);
        if (rc)
                return rc;
 
        if (iface_is_bound_by_netdev(iface)) {
-               rc = host_set_param(t, session->hostno,
+               rc = host_set_param(t, session->host->host_no,
                                    ISCSI_HOST_PARAM_NETDEV_NAME,
                                    iface->netdev, ISCSI_STRING);
                if (rc)
@@ -721,7 +853,7 @@ int iscsi_host_set_net_params(struct iface_rec *iface,
        }
 
        if (iface_is_bound_by_hwaddr(iface)) {
-               rc = host_set_param(t, session->hostno,
+               rc = host_set_param(t, session->host->host_no,
                                    ISCSI_HOST_PARAM_HWADDRESS,
                                    iface->hwaddress, ISCSI_STRING);
                if (rc)
diff --git a/usr/iscsiadm.c b/usr/iscsiadm.c
index c6705bd..e06d8ef 100644
--- a/usr/iscsiadm.c
+++ b/usr/iscsiadm.c
@@ -2215,43 +2215,49 @@ static int exec_host_stats_op(int op, int info_level, 
uint32_t host_no)
        int rc = ISCSI_SUCCESS;
        int fd = 0, buf_size = 0;
 
+       /* ignore error incase iscsid is not up */
+       host_print_login_stats(host_no);
+
        t = iscsi_sysfs_get_transport_by_hba(host_no);
        if (!t) {
                log_error("Could not match hostno %u to transport.", host_no);
                rc = ISCSI_ERR_TRANS_NOT_FOUND;
-               goto exit_host_stats;
+               goto done;
        }
 
+       if (!t->template->offload_host_stats)
+               goto done;
+
        buf_size = sizeof(struct iscsi_offload_host_stats) +
                   sizeof(struct iscsi_uevent);
        req_buf = calloc(1, buf_size);
        if (!req_buf) {
                log_error("Could not allocate memory for host stats request.");
                rc = ISCSI_ERR_NOMEM;
-               goto exit_host_stats;
+               goto done;
        }
 
        fd = ipc->ctldev_open();
        if (fd < 0) {
                rc = ISCSI_ERR_INTERNAL;
                log_error("Netlink open failed.");
-               goto exit_host_stats;
+               goto free_buf;
        }
 
        rc = ipc->get_host_stats(t->handle, host_no, req_buf);
        if (rc < 0) {
-               log_error("get_host_stats failed. errno=%d", errno);
+               log_debug(7, "get_host_stats failed. errno=%d", errno);
                rc = ISCSI_ERR;
-               goto exit_host_stats;
+       } else {
+               print_host_stats((struct iscsi_offload_host_stats *)(req_buf +
+                                sizeof(struct iscsi_uevent)));
        }
 
-       print_host_stats((struct iscsi_offload_host_stats *)(req_buf +
-                        sizeof(struct iscsi_uevent)));
-
        ipc->ctldev_close();
 
-exit_host_stats:
+free_buf:
        free(req_buf);
+done:
        return rc;
 }
 
@@ -3063,6 +3069,7 @@ static uint64_t parse_host_info(char *optarg, int *rc)
                        *rc = ISCSI_ERR_INVAL;
                }
        } else {
+               errno = 0;
                host_no = strtoull(optarg, NULL, 10);
                if (errno || (host_no > MAX_HOST_NO)) {
                        if (host_no > MAX_HOST_NO)
diff --git a/usr/iscsid.c b/usr/iscsid.c
index f8ffd23..abbe16a 100644
--- a/usr/iscsid.c
+++ b/usr/iscsid.c
@@ -52,6 +52,7 @@
 #include "discoveryd.h"
 #include "iscsid_req.h"
 #include "iscsi_err.h"
+#include "host.h"
 
 /* global config info */
 struct iscsi_daemon_config daemon_config;
@@ -347,6 +348,7 @@ int main(int argc, char *argv[])
        struct sigaction sa_old;
        struct sigaction sa_new;
        int control_fd;
+       int udev_fd;
        pid_t pid;
 
        while ((ch = getopt_long(argc, argv, "c:i:fd:nu:g:p:vh", long_options,
@@ -413,6 +415,7 @@ int main(int argc, char *argv[])
 
        mgmt_ipc_fd = -1;
        control_fd = -1;
+       udev_fd = -1;
        daemon_config.initiator_name = NULL;
        daemon_config.initiator_alias = NULL;
 
@@ -421,6 +424,12 @@ int main(int argc, char *argv[])
                exit(ISCSI_ERR);
        }
 
+       udev_fd = udev_event_listen();
+       if (udev_fd < 0) {
+               log_close(log_pid);
+               exit(ISCSI_ERR);
+       }
+
        if (daemonize) {
                char buf[64];
                int fd = -1;
@@ -554,12 +563,13 @@ int main(int argc, char *argv[])
                exit(ISCSI_ERR);
        }
 
-       event_loop(ipc, control_fd, mgmt_ipc_fd);
+       event_loop(ipc, control_fd, mgmt_ipc_fd, udev_fd);
 
        idbm_terminate();
        sysfs_cleanup();
        ipc->ctldev_close();
        mgmt_ipc_close(mgmt_ipc_fd);
+       close(udev_fd);
        if (daemon_config.initiator_name)
                free(daemon_config.initiator_name);
        if (daemon_config.initiator_alias)
diff --git a/usr/iscsistart.c b/usr/iscsistart.c
index 7ff2236..18a75d5 100644
--- a/usr/iscsistart.c
+++ b/usr/iscsistart.c
@@ -327,7 +327,7 @@ int main(int argc, char *argv[])
        struct boot_context *context, boot_context;
        struct sigaction sa_old;
        struct sigaction sa_new;
-       int control_fd, mgmt_ipc_fd, err;
+       int control_fd, mgmt_ipc_fd, udev_fd, err;
        pid_t pid;
 
        idbm_node_setup_defaults(&config_rec);
@@ -476,6 +476,12 @@ int main(int argc, char *argv[])
                exit(ISCSI_ERR_NOMEM);
        }
 
+       udev_fd = udev_event_listen();
+       if (udev_fd < 0) {
+               log_error("Could not setup udev socket\n");
+               exit(ISCSI_ERR_NOMEM);
+       }
+
        control_fd = ipc->ctldev_open();
        if (control_fd < 0)
                exit(ISCSI_ERR_NOMEM);
@@ -509,7 +515,7 @@ int main(int argc, char *argv[])
         * Start Main Event Loop
         */
        iscsi_initiator_init();
-       event_loop(ipc, control_fd, mgmt_ipc_fd);
+       event_loop(ipc, control_fd, mgmt_ipc_fd, udev_fd);
        ipc->ctldev_close();
        mgmt_ipc_close(mgmt_ipc_fd);
        free_initiator();
diff --git a/usr/mgmt_ipc.c b/usr/mgmt_ipc.c
index c16bce9..692e8ad 100644
--- a/usr/mgmt_ipc.c
+++ b/usr/mgmt_ipc.c
@@ -37,6 +37,7 @@
 #include "iscsi_ipc.h"
 #include "iscsi_err.h"
 #include "iscsi_util.h"
+#include "host.h"
 
 #define PEERUSER_MAX   64
 #define EXTMSG_MAX     (64 * 1024)
@@ -347,6 +348,23 @@ mgmt_ipc_notify_del_portal(queue_task_t *qtask)
 }
 
 static int
+mgmt_ipc_host_get_login_stats(queue_task_t *qtask)
+{
+       uint32_t host_no = qtask->req.u.host_login_stats.host_no;
+       struct iscsi_host *host = iscsi_host_find_by_host_no(host_no);
+
+       if (host == NULL)
+               return ISCSI_ERR;
+
+       qtask->rsp.u.host_login_stats.host_no = host->host_no;
+       memcpy(&qtask->rsp.u.host_login_stats.stats, &host->login_stats,
+              sizeof(struct iscsi_login_stats));
+       mgmt_ipc_write_rsp(qtask, ISCSI_SUCCESS);
+
+       return ISCSI_SUCCESS;
+}
+
+static int
 mgmt_peeruser(int sock, char *user)
 {
 #if defined(SO_PEERCRED)
@@ -530,6 +548,7 @@ static mgmt_ipc_fn_t *      
mgmt_ipc_functions[__MGMT_IPC_MAX_COMMAND] = {
 [MGMT_IPC_NOTIFY_DEL_NODE]     = mgmt_ipc_notify_del_node,
 [MGMT_IPC_NOTIFY_ADD_PORTAL]   = mgmt_ipc_notify_add_portal,
 [MGMT_IPC_NOTIFY_DEL_PORTAL]   = mgmt_ipc_notify_del_portal,
+[MGMT_IPC_HOST_STATS]          = mgmt_ipc_host_get_login_stats,
 };
 
 void mgmt_ipc_handle(int accept_fd)
diff --git a/usr/mgmt_ipc.h b/usr/mgmt_ipc.h
index 55972ed..0423f56 100644
--- a/usr/mgmt_ipc.h
+++ b/usr/mgmt_ipc.h
@@ -22,6 +22,7 @@
 #include "types.h"
 #include "iscsi_if.h"
 #include "config.h"
+#include "host.h"
 
 #define ISCSIADM_NAMESPACE     "ISCSIADM_ABSTRACT_NAMESPACE"
 #define PEERUSER_MAX           64
@@ -46,6 +47,7 @@ typedef enum iscsiadm_cmd {
        MGMT_IPC_NOTIFY_DEL_NODE        = 17,
        MGMT_IPC_NOTIFY_ADD_PORTAL      = 18,
        MGMT_IPC_NOTIFY_DEL_PORTAL      = 19,
+       MGMT_IPC_HOST_STATS             = 20,
 
        __MGMT_IPC_MAX_COMMAND
 } iscsiadm_cmd_e;
@@ -77,6 +79,9 @@ typedef struct iscsiadm_req {
                        char value[IFNAMSIZ + 1];
 
                } set_host_param;
+               struct ipc_msg_host_login_stats {
+                       int host_no;
+               } host_login_stats;
        } u;
 } iscsiadm_req_t;
 
@@ -103,6 +108,10 @@ typedef struct iscsiadm_rsp {
                        int session_state;
                        int conn_state;
                } session_state;
+               struct ipc_msg_get_host_login_stats {
+                       int host_no;
+                       struct iscsi_login_stats stats;
+               } host_login_stats;
        } u;
 } iscsiadm_rsp_t;
 
diff --git a/usr/netlink.c b/usr/netlink.c
index 2b85efe..d74f723 100644
--- a/usr/netlink.c
+++ b/usr/netlink.c
@@ -862,7 +862,8 @@ ktransport_ep_connect(iscsi_conn_t *conn, int non_blocking)
        if (conn->bind_ep) {
                ev->type = ISCSI_UEVENT_TRANSPORT_EP_CONNECT_THROUGH_HOST;
                ev->u.ep_connect_through_host.non_blocking = non_blocking;
-               ev->u.ep_connect_through_host.host_no = conn->session->hostno;
+               ev->u.ep_connect_through_host.host_no =
+                                               conn->session->host->host_no;
        } else {
                ev->type = ISCSI_UEVENT_TRANSPORT_EP_CONNECT;
                ev->u.ep_connect.non_blocking = non_blocking;
diff --git a/usr/transport.c b/usr/transport.c
index 18b7704..4797641 100644
--- a/usr/transport.c
+++ b/usr/transport.c
@@ -101,6 +101,7 @@ struct iscsi_transport_template qla4xxx = {
        .name           = "qla4xxx",
        .set_host_ip    = SET_HOST_IP_NOT_REQ,
         .bind_ep_required = 1,
+       .offload_host_stats = 1,
        .ep_connect     = ktransport_ep_connect,
        .ep_poll        = ktransport_ep_poll,
        .ep_disconnect  = ktransport_ep_disconnect,
diff --git a/usr/transport.h b/usr/transport.h
index 4d3bdbf..42bc3a5 100644
--- a/usr/transport.h
+++ b/usr/transport.h
@@ -39,6 +39,7 @@ struct iscsi_transport_template {
        uint8_t set_host_ip;
        uint8_t use_boot_info;
         uint8_t bind_ep_required;
+       uint8_t offload_host_stats;
        int (*ep_connect) (struct iscsi_conn *conn, int non_blocking);
        int (*ep_poll) (struct iscsi_conn *conn, int timeout_ms);
        void (*ep_disconnect) (struct iscsi_conn *conn);
-- 
1.8.5.2

-- 
You received this message because you are subscribed to the Google Groups 
"open-iscsi" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at https://groups.google.com/group/open-iscsi.
For more options, visit https://groups.google.com/d/optout.

Reply via email to