scsi: iscsi: Report connection state in sysfs
authorGabriel Krisman Bertazi <krisman@collabora.com>
Tue, 17 Mar 2020 23:34:22 +0000 (19:34 -0400)
committerMartin K. Petersen <martin.petersen@oracle.com>
Fri, 27 Mar 2020 01:59:20 +0000 (21:59 -0400)
If an iSCSI connection happens to fail while the daemon isn't running (due
to a crash or for another reason), the kernel failure report is not
received.  When the daemon restarts, there is insufficient kernel state in
sysfs for it to know that this happened.  open-iscsi tries to reopen every
connection, but on different initiators, we'd like to know which
connections have failed.

There is session->state, but that has a different lifetime than an iSCSI
connection, so it doesn't directly reflect the connection state.

[mkp: typos]

Link: https://lore.kernel.org/r/20200317233422.532961-1-krisman@collabora.com
Cc: Khazhismel Kumykov <khazhy@google.com>
Suggested-by: Junho Ryu <jayr@google.com>
Reviewed-by: Lee Duncan <lduncan@suse.com>
Signed-off-by: Gabriel Krisman Bertazi <krisman@collabora.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/libiscsi.c
drivers/scsi/scsi_transport_iscsi.c
include/scsi/scsi_transport_iscsi.h

index 70b99c0e2e678c4767af956704504ef39fb2276e..ca488c57ead4e1ce4b145f1521a7971c658247ee 100644 (file)
@@ -3153,13 +3153,18 @@ void iscsi_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
 
        switch (flag) {
        case STOP_CONN_RECOVER:
+               cls_conn->state = ISCSI_CONN_FAILED;
+               break;
        case STOP_CONN_TERM:
-               iscsi_start_session_recovery(session, conn, flag);
+               cls_conn->state = ISCSI_CONN_DOWN;
                break;
        default:
                iscsi_conn_printk(KERN_ERR, conn,
                                  "invalid stop flag %d\n", flag);
+               return;
        }
+
+       iscsi_start_session_recovery(session, conn, flag);
 }
 EXPORT_SYMBOL_GPL(iscsi_conn_stop);
 
index 17a45716a0fe0aa162a354825d5e07befb2d63b1..0ec1b31c75a9c5fbd28a86815f0c816504e17e5b 100644 (file)
@@ -2276,6 +2276,7 @@ iscsi_create_conn(struct iscsi_cls_session *session, int dd_size, uint32_t cid)
        INIT_LIST_HEAD(&conn->conn_list_err);
        conn->transport = transport;
        conn->cid = cid;
+       conn->state = ISCSI_CONN_DOWN;
 
        /* this is released in the dev's release function */
        if (!get_device(&session->dev))
@@ -3709,8 +3710,11 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group)
                break;
        case ISCSI_UEVENT_START_CONN:
                conn = iscsi_conn_lookup(ev->u.start_conn.sid, ev->u.start_conn.cid);
-               if (conn)
+               if (conn) {
                        ev->r.retcode = transport->start_conn(conn);
+                       if (!ev->r.retcode)
+                               conn->state = ISCSI_CONN_UP;
+               }
                else
                        err = -EINVAL;
                break;
@@ -3907,6 +3911,26 @@ iscsi_conn_attr(tcp_xmit_wsf, ISCSI_PARAM_TCP_XMIT_WSF);
 iscsi_conn_attr(tcp_recv_wsf, ISCSI_PARAM_TCP_RECV_WSF);
 iscsi_conn_attr(local_ipaddr, ISCSI_PARAM_LOCAL_IPADDR);
 
+static const char *const connection_state_names[] = {
+       [ISCSI_CONN_UP] = "up",
+       [ISCSI_CONN_DOWN] = "down",
+       [ISCSI_CONN_FAILED] = "failed"
+};
+
+static ssize_t show_conn_state(struct device *dev,
+                              struct device_attribute *attr, char *buf)
+{
+       struct iscsi_cls_conn *conn = iscsi_dev_to_conn(dev->parent);
+       const char *state = "unknown";
+
+       if (conn->state >= 0 &&
+           conn->state < ARRAY_SIZE(connection_state_names))
+               state = connection_state_names[conn->state];
+
+       return sprintf(buf, "%s\n", state);
+}
+static ISCSI_CLASS_ATTR(conn, state, S_IRUGO, show_conn_state,
+                       NULL);
 
 #define iscsi_conn_ep_attr_show(param)                                 \
 static ssize_t show_conn_ep_param_##param(struct device *dev,          \
@@ -3976,6 +4000,7 @@ static struct attribute *iscsi_conn_attrs[] = {
        &dev_attr_conn_tcp_xmit_wsf.attr,
        &dev_attr_conn_tcp_recv_wsf.attr,
        &dev_attr_conn_local_ipaddr.attr,
+       &dev_attr_conn_state.attr,
        NULL,
 };
 
@@ -4047,6 +4072,8 @@ static umode_t iscsi_conn_attr_is_visible(struct kobject *kobj,
                param = ISCSI_PARAM_TCP_RECV_WSF;
        else if (attr == &dev_attr_conn_local_ipaddr.attr)
                param = ISCSI_PARAM_LOCAL_IPADDR;
+       else if (attr == &dev_attr_conn_state.attr)
+               return S_IRUGO;
        else {
                WARN_ONCE(1, "Invalid conn attr");
                return 0;
index fa8814245796b0b5a6966b22ecafdd0c5d239dae..bdcb6d69d1549ae0359cd1f100ae1ba1aa52f733 100644 (file)
@@ -188,6 +188,13 @@ extern void iscsi_ping_comp_event(uint32_t host_no,
                                  uint32_t status, uint32_t pid,
                                  uint32_t data_size, uint8_t *data);
 
+/* iscsi class connection state */
+enum iscsi_connection_state {
+       ISCSI_CONN_UP = 0,
+       ISCSI_CONN_DOWN,
+       ISCSI_CONN_FAILED,
+};
+
 struct iscsi_cls_conn {
        struct list_head conn_list;     /* item in connlist */
        struct list_head conn_list_err; /* item in connlist_err */
@@ -198,6 +205,7 @@ struct iscsi_cls_conn {
        struct iscsi_endpoint *ep;
 
        struct device dev;              /* sysfs transport/container device */
+       enum iscsi_connection_state state;
 };
 
 #define iscsi_dev_to_conn(_dev) \