[SCSI] libiscsi: fix iscsi transport checks to account for slower links
[cascardo/linux.git] / drivers / scsi / libiscsi.c
index 11bc3e1..c648bd3 100644 (file)
@@ -546,6 +546,9 @@ __iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
                 */
                task = conn->login_task;
        else {
+               if (session->state != ISCSI_STATE_LOGGED_IN)
+                       return NULL;
+
                BUG_ON(conn->c_stage == ISCSI_CONN_INITIAL_STAGE);
                BUG_ON(conn->c_stage == ISCSI_CONN_STOPPED);
 
@@ -729,6 +732,7 @@ iscsi_data_in_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
        if (!(rhdr->flags & ISCSI_FLAG_DATA_STATUS))
                return;
 
+       iscsi_update_cmdsn(conn->session, (struct iscsi_nopin *)hdr);
        sc->result = (DID_OK << 16) | rhdr->cmd_status;
        conn->exp_statsn = be32_to_cpu(rhdr->statsn) + 1;
        if (rhdr->flags & (ISCSI_FLAG_DATA_UNDERFLOW |
@@ -828,7 +832,7 @@ static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
  *
  * The session lock must be held.
  */
-static struct iscsi_task *iscsi_itt_to_task(struct iscsi_conn *conn, itt_t itt)
+struct iscsi_task *iscsi_itt_to_task(struct iscsi_conn *conn, itt_t itt)
 {
        struct iscsi_session *session = conn->session;
        int i;
@@ -845,6 +849,7 @@ static struct iscsi_task *iscsi_itt_to_task(struct iscsi_conn *conn, itt_t itt)
 
        return session->cmds[i];
 }
+EXPORT_SYMBOL_GPL(iscsi_itt_to_task);
 
 /**
  * __iscsi_complete_pdu - complete pdu
@@ -1672,6 +1677,22 @@ static void iscsi_start_tx(struct iscsi_conn *conn)
                iscsi_conn_queue_work(conn);
 }
 
+/*
+ * We want to make sure a ping is in flight. It has timed out.
+ * And we are not busy processing a pdu that is making
+ * progress but got started before the ping and is taking a while
+ * to complete so the ping is just stuck behind it in a queue.
+ */
+static int iscsi_has_ping_timed_out(struct iscsi_conn *conn)
+{
+       if (conn->ping_task &&
+           time_before_eq(conn->last_recv + (conn->recv_timeout * HZ) +
+                          (conn->ping_timeout * HZ), jiffies))
+               return 1;
+       else
+               return 0;
+}
+
 static enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *scmd)
 {
        struct iscsi_cls_session *cls_session;
@@ -1707,16 +1728,20 @@ static enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *scmd)
         * if the ping timedout then we are in the middle of cleaning up
         * and can let the iscsi eh handle it
         */
-       if (time_before_eq(conn->last_recv + (conn->recv_timeout * HZ) +
-                           (conn->ping_timeout * HZ), jiffies))
+       if (iscsi_has_ping_timed_out(conn)) {
                rc = BLK_EH_RESET_TIMER;
+               goto done;
+       }
        /*
         * if we are about to check the transport then give the command
         * more time
         */
        if (time_before_eq(conn->last_recv + (conn->recv_timeout * HZ),
-                          jiffies))
+                          jiffies)) {
                rc = BLK_EH_RESET_TIMER;
+               goto done;
+       }
+
        /* if in the middle of checking the transport then give us more time */
        if (conn->ping_task)
                rc = BLK_EH_RESET_TIMER;
@@ -1743,13 +1768,13 @@ static void iscsi_check_transport_timeouts(unsigned long data)
 
        recv_timeout *= HZ;
        last_recv = conn->last_recv;
-       if (conn->ping_task &&
-           time_before_eq(conn->last_ping + (conn->ping_timeout * HZ),
-                          jiffies)) {
+
+       if (iscsi_has_ping_timed_out(conn)) {
                iscsi_conn_printk(KERN_ERR, conn, "ping timeout of %d secs "
-                                 "expired, last rx %lu, last ping %lu, "
-                                 "now %lu\n", conn->ping_timeout, last_recv,
-                                 conn->last_ping, jiffies);
+                                 "expired, recv timeout %d, last rx %lu, "
+                                 "last ping %lu, now %lu\n",
+                                 conn->ping_timeout, conn->recv_timeout,
+                                 last_recv, conn->last_ping, jiffies);
                spin_unlock(&session->lock);
                iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
                return;
@@ -2564,8 +2589,6 @@ static void iscsi_start_session_recovery(struct iscsi_session *session,
 {
        int old_stop_stage;
 
-       del_timer_sync(&conn->transport_timer);
-
        mutex_lock(&session->eh_mutex);
        spin_lock_bh(&session->lock);
        if (conn->stop_stage == STOP_CONN_TERM) {
@@ -2583,13 +2606,17 @@ static void iscsi_start_session_recovery(struct iscsi_session *session,
                session->state = ISCSI_STATE_TERMINATE;
        else if (conn->stop_stage != STOP_CONN_RECOVER)
                session->state = ISCSI_STATE_IN_RECOVERY;
+       spin_unlock_bh(&session->lock);
 
+       del_timer_sync(&conn->transport_timer);
+       iscsi_suspend_tx(conn);
+
+       spin_lock_bh(&session->lock);
        old_stop_stage = conn->stop_stage;
        conn->stop_stage = flag;
        conn->c_stage = ISCSI_CONN_STOPPED;
        spin_unlock_bh(&session->lock);
 
-       iscsi_suspend_tx(conn);
        /*
         * for connection level recovery we should not calculate
         * header digest. conn->hdr_size used for optimization
@@ -2656,6 +2683,23 @@ int iscsi_conn_bind(struct iscsi_cls_session *cls_session,
 }
 EXPORT_SYMBOL_GPL(iscsi_conn_bind);
 
+static int iscsi_switch_str_param(char **param, char *new_val_buf)
+{
+       char *new_val;
+
+       if (*param) {
+               if (!strcmp(*param, new_val_buf))
+                       return 0;
+       }
+
+       new_val = kstrdup(new_val_buf, GFP_NOIO);
+       if (!new_val)
+               return -ENOMEM;
+
+       kfree(*param);
+       *param = new_val;
+       return 0;
+}
 
 int iscsi_set_param(struct iscsi_cls_conn *cls_conn,
                    enum iscsi_param param, char *buf, int buflen)
@@ -2728,38 +2772,15 @@ int iscsi_set_param(struct iscsi_cls_conn *cls_conn,
                sscanf(buf, "%u", &conn->exp_statsn);
                break;
        case ISCSI_PARAM_USERNAME:
-               kfree(session->username);
-               session->username = kstrdup(buf, GFP_KERNEL);
-               if (!session->username)
-                       return -ENOMEM;
-               break;
+               return iscsi_switch_str_param(&session->username, buf);
        case ISCSI_PARAM_USERNAME_IN:
-               kfree(session->username_in);
-               session->username_in = kstrdup(buf, GFP_KERNEL);
-               if (!session->username_in)
-                       return -ENOMEM;
-               break;
+               return iscsi_switch_str_param(&session->username_in, buf);
        case ISCSI_PARAM_PASSWORD:
-               kfree(session->password);
-               session->password = kstrdup(buf, GFP_KERNEL);
-               if (!session->password)
-                       return -ENOMEM;
-               break;
+               return iscsi_switch_str_param(&session->password, buf);
        case ISCSI_PARAM_PASSWORD_IN:
-               kfree(session->password_in);
-               session->password_in = kstrdup(buf, GFP_KERNEL);
-               if (!session->password_in)
-                       return -ENOMEM;
-               break;
+               return iscsi_switch_str_param(&session->password_in, buf);
        case ISCSI_PARAM_TARGET_NAME:
-               /* this should not change between logins */
-               if (session->targetname)
-                       break;
-
-               session->targetname = kstrdup(buf, GFP_KERNEL);
-               if (!session->targetname)
-                       return -ENOMEM;
-               break;
+               return iscsi_switch_str_param(&session->targetname, buf);
        case ISCSI_PARAM_TPGT:
                sscanf(buf, "%d", &session->tpgt);
                break;
@@ -2767,25 +2788,11 @@ int iscsi_set_param(struct iscsi_cls_conn *cls_conn,
                sscanf(buf, "%d", &conn->persistent_port);
                break;
        case ISCSI_PARAM_PERSISTENT_ADDRESS:
-               /*
-                * this is the address returned in discovery so it should
-                * not change between logins.
-                */
-               if (conn->persistent_address)
-                       break;
-
-               conn->persistent_address = kstrdup(buf, GFP_KERNEL);
-               if (!conn->persistent_address)
-                       return -ENOMEM;
-               break;
+               return iscsi_switch_str_param(&conn->persistent_address, buf);
        case ISCSI_PARAM_IFACE_NAME:
-               if (!session->ifacename)
-                       session->ifacename = kstrdup(buf, GFP_KERNEL);
-               break;
+               return iscsi_switch_str_param(&session->ifacename, buf);
        case ISCSI_PARAM_INITIATOR_NAME:
-               if (!session->initiatorname)
-                       session->initiatorname = kstrdup(buf, GFP_KERNEL);
-               break;
+               return iscsi_switch_str_param(&session->initiatorname, buf);
        default:
                return -ENOSYS;
        }
@@ -2856,10 +2863,7 @@ int iscsi_session_get_param(struct iscsi_cls_session *cls_session,
                len = sprintf(buf, "%s\n", session->ifacename);
                break;
        case ISCSI_PARAM_INITIATOR_NAME:
-               if (!session->initiatorname)
-                       len = sprintf(buf, "%s\n", "unknown");
-               else
-                       len = sprintf(buf, "%s\n", session->initiatorname);
+               len = sprintf(buf, "%s\n", session->initiatorname);
                break;
        default:
                return -ENOSYS;
@@ -2925,29 +2929,16 @@ int iscsi_host_get_param(struct Scsi_Host *shost, enum iscsi_host_param param,
 
        switch (param) {
        case ISCSI_HOST_PARAM_NETDEV_NAME:
-               if (!ihost->netdev)
-                       len = sprintf(buf, "%s\n", "default");
-               else
-                       len = sprintf(buf, "%s\n", ihost->netdev);
+               len = sprintf(buf, "%s\n", ihost->netdev);
                break;
        case ISCSI_HOST_PARAM_HWADDRESS:
-               if (!ihost->hwaddress)
-                       len = sprintf(buf, "%s\n", "default");
-               else
-                       len = sprintf(buf, "%s\n", ihost->hwaddress);
+               len = sprintf(buf, "%s\n", ihost->hwaddress);
                break;
        case ISCSI_HOST_PARAM_INITIATOR_NAME:
-               if (!ihost->initiatorname)
-                       len = sprintf(buf, "%s\n", "unknown");
-               else
-                       len = sprintf(buf, "%s\n", ihost->initiatorname);
+               len = sprintf(buf, "%s\n", ihost->initiatorname);
                break;
        case ISCSI_HOST_PARAM_IPADDRESS:
-               if (!strlen(ihost->local_address))
-                       len = sprintf(buf, "%s\n", "unknown");
-               else
-                       len = sprintf(buf, "%s\n",
-                                     ihost->local_address);
+               len = sprintf(buf, "%s\n", ihost->local_address);
                break;
        default:
                return -ENOSYS;
@@ -2964,17 +2955,11 @@ int iscsi_host_set_param(struct Scsi_Host *shost, enum iscsi_host_param param,
 
        switch (param) {
        case ISCSI_HOST_PARAM_NETDEV_NAME:
-               if (!ihost->netdev)
-                       ihost->netdev = kstrdup(buf, GFP_KERNEL);
-               break;
+               return iscsi_switch_str_param(&ihost->netdev, buf);
        case ISCSI_HOST_PARAM_HWADDRESS:
-               if (!ihost->hwaddress)
-                       ihost->hwaddress = kstrdup(buf, GFP_KERNEL);
-               break;
+               return iscsi_switch_str_param(&ihost->hwaddress, buf);
        case ISCSI_HOST_PARAM_INITIATOR_NAME:
-               if (!ihost->initiatorname)
-                       ihost->initiatorname = kstrdup(buf, GFP_KERNEL);
-               break;
+               return iscsi_switch_str_param(&ihost->initiatorname, buf);
        default:
                return -ENOSYS;
        }