Merge branch 'release' of git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux...
[powerpc.git] / drivers / scsi / libiscsi.c
index a99f2ef..7e6e031 100644 (file)
@@ -333,15 +333,21 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
                debug_scsi("immrsp [op 0x%x cid %d itt 0x%x len %d]\n",
                           opcode, conn->id, mtask->itt, datalen);
 
+               rc = iscsi_check_assign_cmdsn(session,
+                                             (struct iscsi_nopin*)hdr);
+               if (rc)
+                       goto done;
+
                switch(opcode) {
+               case ISCSI_OP_LOGOUT_RSP:
+                       conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1;
+                       /* fall through */
                case ISCSI_OP_LOGIN_RSP:
                case ISCSI_OP_TEXT_RSP:
-               case ISCSI_OP_LOGOUT_RSP:
-                       rc = iscsi_check_assign_cmdsn(session,
-                                                (struct iscsi_nopin*)hdr);
-                       if (rc)
-                               break;
-
+                       /*
+                        * login related PDU's exp_statsn is handled in
+                        * userspace
+                        */
                        rc = iscsi_recv_pdu(conn->cls_conn, hdr, data, datalen);
                        list_del(&mtask->running);
                        if (conn->login_mtask != mtask)
@@ -349,15 +355,12 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
                                            (void*)&mtask, sizeof(void*));
                        break;
                case ISCSI_OP_SCSI_TMFUNC_RSP:
-                       rc = iscsi_check_assign_cmdsn(session,
-                                                (struct iscsi_nopin*)hdr);
-                       if (rc)
-                               break;
-
                        if (datalen) {
                                rc = ISCSI_ERR_PROTO;
                                break;
                        }
+
+                       conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1;
                        conn->tmfrsp_pdus_cnt++;
                        if (conn->tmabort_state == TMABORT_INITIAL) {
                                conn->tmabort_state =
@@ -373,10 +376,6 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
                                rc = ISCSI_ERR_PROTO;
                                break;
                        }
-                       rc = iscsi_check_assign_cmdsn(session,
-                                               (struct iscsi_nopin*)hdr);
-                       if (rc)
-                               break;
                        conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1;
 
                        rc = iscsi_recv_pdu(conn->cls_conn, hdr, data, datalen);
@@ -404,6 +403,7 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
                case ISCSI_OP_REJECT:
                        /* we need sth like iscsi_reject_rsp()*/
                case ISCSI_OP_ASYNC_EVENT:
+                       conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1;
                        /* we need sth like iscsi_async_event_rsp() */
                        rc = ISCSI_ERR_BAD_OPCODE;
                        break;
@@ -414,6 +414,7 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
        } else
                rc = ISCSI_ERR_BAD_ITT;
 
+done:
        return rc;
 }
 EXPORT_SYMBOL_GPL(__iscsi_complete_pdu);
@@ -486,7 +487,12 @@ void iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err)
        unsigned long flags;
 
        spin_lock_irqsave(&session->lock, flags);
-       if (session->conn_cnt == 1 || session->leadconn == conn)
+       if (session->state == ISCSI_STATE_FAILED) {
+               spin_unlock_irqrestore(&session->lock, flags);
+               return;
+       }
+
+       if (conn->stop_stage == 0)
                session->state = ISCSI_STATE_FAILED;
        spin_unlock_irqrestore(&session->lock, flags);
        set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
@@ -507,10 +513,11 @@ EXPORT_SYMBOL_GPL(iscsi_conn_failure);
 static int iscsi_data_xmit(struct iscsi_conn *conn)
 {
        struct iscsi_transport *tt;
+       int rc = 0;
 
        if (unlikely(conn->suspend_tx)) {
                debug_scsi("conn %d Tx suspended!\n", conn->id);
-               return 0;
+               return -ENODATA;
        }
        tt = conn->session->tt;
 
@@ -530,13 +537,15 @@ static int iscsi_data_xmit(struct iscsi_conn *conn)
        BUG_ON(conn->ctask && conn->mtask);
 
        if (conn->ctask) {
-               if (tt->xmit_cmd_task(conn, conn->ctask))
+               rc = tt->xmit_cmd_task(conn, conn->ctask);
+               if (rc)
                        goto again;
                /* done with this in-progress ctask */
                conn->ctask = NULL;
        }
        if (conn->mtask) {
-               if (tt->xmit_mgmt_task(conn, conn->mtask))
+               rc = tt->xmit_mgmt_task(conn, conn->mtask);
+               if (rc)
                        goto again;
                /* done with this in-progress mtask */
                conn->mtask = NULL;
@@ -546,9 +555,12 @@ static int iscsi_data_xmit(struct iscsi_conn *conn)
         if (unlikely(__kfifo_len(conn->immqueue))) {
                while (__kfifo_get(conn->immqueue, (void*)&conn->mtask,
                                   sizeof(void*))) {
+                       spin_lock_bh(&conn->session->lock);
                        list_add_tail(&conn->mtask->running,
                                      &conn->mgmt_run_list);
-                       if (tt->xmit_mgmt_task(conn, conn->mtask))
+                       spin_unlock_bh(&conn->session->lock);
+                       rc = tt->xmit_mgmt_task(conn, conn->mtask);
+                       if (rc)
                                goto again;
                }
                /* done with this mtask */
@@ -562,9 +574,12 @@ static int iscsi_data_xmit(struct iscsi_conn *conn)
                 * iscsi tcp may readd the task to the xmitqueue to send
                 * write data
                 */
+               spin_lock_bh(&conn->session->lock);
                if (list_empty(&conn->ctask->running))
                        list_add_tail(&conn->ctask->running, &conn->run_list);
-               if (tt->xmit_cmd_task(conn, conn->ctask))
+               spin_unlock_bh(&conn->session->lock);
+               rc = tt->xmit_cmd_task(conn, conn->ctask);
+               if (rc)
                        goto again;
        }
        /* done with this ctask */
@@ -574,34 +589,38 @@ static int iscsi_data_xmit(struct iscsi_conn *conn)
         if (unlikely(__kfifo_len(conn->mgmtqueue))) {
                while (__kfifo_get(conn->mgmtqueue, (void*)&conn->mtask,
                                   sizeof(void*))) {
+                       spin_lock_bh(&conn->session->lock);
                        list_add_tail(&conn->mtask->running,
                                      &conn->mgmt_run_list);
-                       if (tt->xmit_mgmt_task(conn, conn->mtask))
+                       spin_unlock_bh(&conn->session->lock);
+                       rc = tt->xmit_mgmt_task(conn, conn->mtask);
+                       if (rc)
                                goto again;
                }
                /* done with this mtask */
                conn->mtask = NULL;
        }
 
-       return 0;
+       return -ENODATA;
 
 again:
        if (unlikely(conn->suspend_tx))
-               return 0;
+               return -ENODATA;
 
-       return -EAGAIN;
+       return rc;
 }
 
 static void iscsi_xmitworker(void *data)
 {
        struct iscsi_conn *conn = data;
-
+       int rc;
        /*
         * serialize Xmit worker on a per-connection basis.
         */
        mutex_lock(&conn->xmitmutex);
-       if (iscsi_data_xmit(conn))
-               scsi_queue_work(conn->session->host, &conn->xmitwork);
+       do {
+               rc = iscsi_data_xmit(conn);
+       } while (rc >= 0 || rc == -EAGAIN);
        mutex_unlock(&conn->xmitmutex);
 }
 
@@ -611,6 +630,7 @@ enum {
        FAILURE_SESSION_FREED,
        FAILURE_WINDOW_CLOSED,
        FAILURE_SESSION_TERMINATE,
+       FAILURE_SESSION_IN_RECOVERY,
        FAILURE_SESSION_RECOVERY_TIMEOUT,
 };
 
@@ -630,18 +650,30 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
 
        spin_lock(&session->lock);
 
-       if (session->state != ISCSI_STATE_LOGGED_IN) {
-               if (session->recovery_failed) {
-                       reason = FAILURE_SESSION_RECOVERY_TIMEOUT;
-                       goto fault;
-               } else if (session->state == ISCSI_STATE_FAILED) {
-                       reason = FAILURE_SESSION_FAILED;
+       /*
+        * ISCSI_STATE_FAILED is a temp. state. The recovery
+        * code will decide what is best to do with command queued
+        * during this time
+        */
+       if (session->state != ISCSI_STATE_LOGGED_IN &&
+           session->state != ISCSI_STATE_FAILED) {
+               /*
+                * to handle the race between when we set the recovery state
+                * and block the session we requeue here (commands could
+                * be entering our queuecommand while a block is starting
+                * up because the block code is not locked)
+                */
+               if (session->state == ISCSI_STATE_IN_RECOVERY) {
+                       reason = FAILURE_SESSION_IN_RECOVERY;
                        goto reject;
-               } else if (session->state == ISCSI_STATE_TERMINATE) {
-                       reason = FAILURE_SESSION_TERMINATE;
-                       goto fault;
                }
-               reason = FAILURE_SESSION_FREED;
+
+               if (session->state == ISCSI_STATE_RECOVERY_FAILED)
+                       reason = FAILURE_SESSION_RECOVERY_TIMEOUT;
+               else if (session->state == ISCSI_STATE_TERMINATE)
+                       reason = FAILURE_SESSION_TERMINATE;
+               else
+                       reason = FAILURE_SESSION_FREED;
                goto fault;
        }
 
@@ -727,9 +759,10 @@ iscsi_conn_send_generic(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
                 */
                mtask = conn->login_mtask;
        else {
-               BUG_ON(conn->c_stage == ISCSI_CONN_INITIAL_STAGE);
-               BUG_ON(conn->c_stage == ISCSI_CONN_STOPPED);
+               BUG_ON(conn->c_stage == ISCSI_CONN_INITIAL_STAGE);
+               BUG_ON(conn->c_stage == ISCSI_CONN_STOPPED);
 
+               nop->exp_statsn = cpu_to_be32(conn->exp_statsn);
                if (!__kfifo_get(session->mgmtpool.queue,
                                 (void*)&mtask, sizeof(void*))) {
                        spin_unlock_bh(&session->lock);
@@ -738,7 +771,7 @@ iscsi_conn_send_generic(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
        }
 
        /*
-        * pre-format CmdSN and ExpStatSN for outgoing PDU.
+        * pre-format CmdSN for outgoing PDU.
         */
        if (hdr->itt != cpu_to_be32(ISCSI_RESERVED_TAG)) {
                hdr->itt = mtask->itt | (conn->id << ISCSI_CID_SHIFT) |
@@ -751,8 +784,6 @@ iscsi_conn_send_generic(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
                /* do not advance CmdSN */
                nop->cmdsn = cpu_to_be32(session->cmdsn);
 
-       nop->exp_statsn = cpu_to_be32(conn->exp_statsn);
-
        if (data_size) {
                memcpy(mtask->data, data, data_size);
                mtask->data_count = data_size;
@@ -803,7 +834,7 @@ void iscsi_session_recovery_timedout(struct iscsi_cls_session *cls_session)
 
        spin_lock_bh(&session->lock);
        if (session->state != ISCSI_STATE_LOGGED_IN) {
-               session->recovery_failed = 1;
+               session->state = ISCSI_STATE_RECOVERY_FAILED;
                if (conn)
                        wake_up(&conn->ehwait);
        }
@@ -838,20 +869,14 @@ failed:
         * we drop the lock here but the leadconn cannot be destoyed while
         * we are in the scsi eh
         */
-       if (fail_session) {
+       if (fail_session)
                iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
-               /*
-                * if userspace cannot respond then we must kick this off
-                * here for it
-                */
-               iscsi_start_session_recovery(session, conn, STOP_CONN_RECOVER);
-       }
 
        debug_scsi("iscsi_eh_host_reset wait for relogin\n");
        wait_event_interruptible(conn->ehwait,
                                 session->state == ISCSI_STATE_TERMINATE ||
                                 session->state == ISCSI_STATE_LOGGED_IN ||
-                                session->recovery_failed);
+                                session->state == ISCSI_STATE_RECOVERY_FAILED);
        if (signal_pending(current))
                flush_signals(current);
 
@@ -940,8 +965,7 @@ static int iscsi_exec_abort_task(struct scsi_cmnd *sc,
        wait_event_interruptible(conn->ehwait,
                                 sc->SCp.phase != session->age ||
                                 session->state != ISCSI_STATE_LOGGED_IN ||
-                                conn->tmabort_state != TMABORT_INITIAL ||
-                                session->recovery_failed);
+                                conn->tmabort_state != TMABORT_INITIAL);
        if (signal_pending(current))
                flush_signals(current);
        del_timer_sync(&conn->tmabort_timer);
@@ -968,7 +992,7 @@ iscsi_remove_##tasktype(struct kfifo *fifo, uint32_t itt)           \
                                                                        \
                if (task->itt == itt) {                                 \
                        debug_scsi("matched task\n");                   \
-                       break;                                          \
+                       return task;                                    \
                }                                                       \
                                                                        \
                __kfifo_put(fifo, (void*)&task, sizeof(void*));         \
@@ -1258,33 +1282,26 @@ iscsi_session_setup(struct iscsi_transport *iscsit,
                if (mgmt_task_size)
                        mtask->dd_data = &mtask[1];
                mtask->itt = ISCSI_MGMT_ITT_OFFSET + cmd_i;
-               mtask->data = kmalloc(DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH,
-                                    GFP_KERNEL);
-               if (!mtask->data) {
-                       int j;
-
-                       for (j = 0; j < cmd_i; j++)
-                               kfree(session->mgmt_cmds[j]->data);
-                       goto immdata_alloc_fail;
-               }
        }
 
        if (scsi_add_host(shost, NULL))
                goto add_host_fail;
 
+       if (!try_module_get(iscsit->owner))
+               goto cls_session_fail;
+
        cls_session = iscsi_create_session(shost, iscsit, 0);
        if (!cls_session)
-               goto cls_session_fail;
+               goto module_put;
        *(unsigned long*)shost->hostdata = (unsigned long)cls_session;
 
        return cls_session;
 
+module_put:
+       module_put(iscsit->owner);
 cls_session_fail:
        scsi_remove_host(shost);
 add_host_fail:
-       for (cmd_i = 0; cmd_i < session->mgmtpool_max; cmd_i++)
-               kfree(session->mgmt_cmds[cmd_i]->data);
-immdata_alloc_fail:
        iscsi_pool_free(&session->mgmtpool, (void**)session->mgmt_cmds);
 mgmtpool_alloc_fail:
        iscsi_pool_free(&session->cmdpool, (void**)session->cmds);
@@ -1305,18 +1322,15 @@ void iscsi_session_teardown(struct iscsi_cls_session *cls_session)
 {
        struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
        struct iscsi_session *session = iscsi_hostdata(shost->hostdata);
-       int cmd_i;
 
        scsi_remove_host(shost);
 
-       for (cmd_i = 0; cmd_i < session->mgmtpool_max; cmd_i++)
-               kfree(session->mgmt_cmds[cmd_i]->data);
-
        iscsi_pool_free(&session->mgmtpool, (void**)session->mgmt_cmds);
        iscsi_pool_free(&session->cmdpool, (void**)session->cmds);
 
        iscsi_destroy_session(cls_session);
        scsi_host_put(shost);
+       module_put(cls_session->transport->owner);
 }
 EXPORT_SYMBOL_GPL(iscsi_session_teardown);
 
@@ -1331,6 +1345,7 @@ iscsi_conn_setup(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
        struct iscsi_session *session = class_to_transport_session(cls_session);
        struct iscsi_conn *conn;
        struct iscsi_cls_conn *cls_conn;
+       char *data;
 
        cls_conn = iscsi_create_conn(cls_session, conn_idx);
        if (!cls_conn)
@@ -1376,12 +1391,20 @@ iscsi_conn_setup(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
        }
        spin_unlock_bh(&session->lock);
 
+       data = kmalloc(DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH, GFP_KERNEL);
+       if (!data)
+               goto login_mtask_data_alloc_fail;
+       conn->login_mtask->data = data;
+
        init_timer(&conn->tmabort_timer);
        mutex_init(&conn->xmitmutex);
        init_waitqueue_head(&conn->ehwait);
 
        return cls_conn;
 
+login_mtask_data_alloc_fail:
+       __kfifo_put(session->mgmtpool.queue, (void*)&conn->login_mtask,
+                   sizeof(void*));
 login_mtask_alloc_fail:
        kfifo_free(conn->mgmtqueue);
 mgmtqueue_alloc_fail:
@@ -1407,8 +1430,8 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn)
        struct iscsi_session *session = conn->session;
        unsigned long flags;
 
-       mutex_lock(&conn->xmitmutex);
        set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
+       mutex_lock(&conn->xmitmutex);
        if (conn->c_stage == ISCSI_CONN_INITIAL_STAGE) {
                if (session->tt->suspend_conn_recv)
                        session->tt->suspend_conn_recv(conn);
@@ -1451,6 +1474,7 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn)
        }
 
        spin_lock_bh(&session->lock);
+       kfree(conn->login_mtask->data);
        __kfifo_put(session->mgmtpool.queue, (void*)&conn->login_mtask,
                    sizeof(void*));
        list_del(&conn->item);
@@ -1493,25 +1517,17 @@ int iscsi_conn_start(struct iscsi_cls_conn *cls_conn)
                 * unblock eh_abort() if it is blocked. re-try all
                 * commands after successful recovery
                 */
-               session->conn_cnt++;
                conn->stop_stage = 0;
                conn->tmabort_state = TMABORT_INITIAL;
                session->age++;
-               session->recovery_failed = 0;
                spin_unlock_bh(&session->lock);
 
                iscsi_unblock_session(session_to_cls(session));
                wake_up(&conn->ehwait);
                return 0;
        case STOP_CONN_TERM:
-               session->conn_cnt++;
                conn->stop_stage = 0;
                break;
-       case STOP_CONN_SUSPEND:
-               conn->stop_stage = 0;
-               clear_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx);
-               clear_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
-               break;
        default:
                break;
        }
@@ -1572,8 +1588,8 @@ static void fail_all_commands(struct iscsi_conn *conn)
        conn->ctask = NULL;
 }
 
-void iscsi_start_session_recovery(struct iscsi_session *session,
-                                 struct iscsi_conn *conn, int flag)
+static void iscsi_start_session_recovery(struct iscsi_session *session,
+                                        struct iscsi_conn *conn, int flag)
 {
        int old_stop_stage;
 
@@ -1585,37 +1601,24 @@ void iscsi_start_session_recovery(struct iscsi_session *session,
 
        /*
         * When this is called for the in_login state, we only want to clean
-        * up the login task and connection.
+        * up the login task and connection. We do not need to block and set
+        * the recovery state again
         */
-       if (conn->stop_stage != STOP_CONN_RECOVER)
-               session->conn_cnt--;
+       if (flag == STOP_CONN_TERM)
+               session->state = ISCSI_STATE_TERMINATE;
+       else if (conn->stop_stage != STOP_CONN_RECOVER)
+               session->state = ISCSI_STATE_IN_RECOVERY;
 
        old_stop_stage = conn->stop_stage;
        conn->stop_stage = flag;
+       conn->c_stage = ISCSI_CONN_STOPPED;
+       set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
        spin_unlock_bh(&session->lock);
 
        if (session->tt->suspend_conn_recv)
                session->tt->suspend_conn_recv(conn);
 
        mutex_lock(&conn->xmitmutex);
-       spin_lock_bh(&session->lock);
-       conn->c_stage = ISCSI_CONN_STOPPED;
-       set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
-
-       if (session->conn_cnt == 0 || session->leadconn == conn)
-               session->state = ISCSI_STATE_FAILED;
-
-       spin_unlock_bh(&session->lock);
-
-       session->tt->terminate_conn(conn);
-       /*
-        * flush queues.
-        */
-       spin_lock_bh(&session->lock);
-       fail_all_commands(conn);
-       flush_control_queues(session, conn);
-       spin_unlock_bh(&session->lock);
-
        /*
         * for connection level recovery we should not calculate
         * header digest. conn->hdr_size used for optimization
@@ -1625,18 +1628,24 @@ void iscsi_start_session_recovery(struct iscsi_session *session,
        if (flag == STOP_CONN_RECOVER) {
                conn->hdrdgst_en = 0;
                conn->datadgst_en = 0;
-
-               /*
-                * if this is called from the eh and and from userspace
-                * then we only need to block once.
-                */
-               if (session->state == ISCSI_STATE_FAILED &&
-                   old_stop_stage != STOP_CONN_RECOVER)
+               if (session->state == ISCSI_STATE_IN_RECOVERY &&
+                   old_stop_stage != STOP_CONN_RECOVER) {
+                       debug_scsi("blocking session\n");
                        iscsi_block_session(session_to_cls(session));
+               }
        }
+
+       session->tt->terminate_conn(conn);
+       /*
+        * flush queues.
+        */
+       spin_lock_bh(&session->lock);
+       fail_all_commands(conn);
+       flush_control_queues(session, conn);
+       spin_unlock_bh(&session->lock);
+
        mutex_unlock(&conn->xmitmutex);
 }
-EXPORT_SYMBOL_GPL(iscsi_start_session_recovery);
 
 void iscsi_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
 {
@@ -1647,20 +1656,6 @@ void iscsi_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
        case STOP_CONN_RECOVER:
        case STOP_CONN_TERM:
                iscsi_start_session_recovery(session, conn, flag);
-               return;
-       case STOP_CONN_SUSPEND:
-               if (session->tt->suspend_conn_recv)
-                       session->tt->suspend_conn_recv(conn);
-
-               mutex_lock(&conn->xmitmutex);
-               spin_lock_bh(&session->lock);
-
-               conn->stop_stage = flag;
-               conn->c_stage = ISCSI_CONN_STOPPED;
-               set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
-
-               spin_unlock_bh(&session->lock);
-               mutex_unlock(&conn->xmitmutex);
                break;
        default:
                printk(KERN_ERR "iscsi: invalid stop flag %d\n", flag);
@@ -1708,6 +1703,185 @@ int iscsi_conn_bind(struct iscsi_cls_session *cls_session,
 }
 EXPORT_SYMBOL_GPL(iscsi_conn_bind);
 
+
+int iscsi_set_param(struct iscsi_cls_conn *cls_conn,
+                   enum iscsi_param param, char *buf, int buflen)
+{
+       struct iscsi_conn *conn = cls_conn->dd_data;
+       struct iscsi_session *session = conn->session;
+       uint32_t value;
+
+       switch(param) {
+       case ISCSI_PARAM_MAX_RECV_DLENGTH:
+               sscanf(buf, "%d", &conn->max_recv_dlength);
+               break;
+       case ISCSI_PARAM_MAX_XMIT_DLENGTH:
+               sscanf(buf, "%d", &conn->max_xmit_dlength);
+               break;
+       case ISCSI_PARAM_HDRDGST_EN:
+               sscanf(buf, "%d", &conn->hdrdgst_en);
+               break;
+       case ISCSI_PARAM_DATADGST_EN:
+               sscanf(buf, "%d", &conn->datadgst_en);
+               break;
+       case ISCSI_PARAM_INITIAL_R2T_EN:
+               sscanf(buf, "%d", &session->initial_r2t_en);
+               break;
+       case ISCSI_PARAM_MAX_R2T:
+               sscanf(buf, "%d", &session->max_r2t);
+               break;
+       case ISCSI_PARAM_IMM_DATA_EN:
+               sscanf(buf, "%d", &session->imm_data_en);
+               break;
+       case ISCSI_PARAM_FIRST_BURST:
+               sscanf(buf, "%d", &session->first_burst);
+               break;
+       case ISCSI_PARAM_MAX_BURST:
+               sscanf(buf, "%d", &session->max_burst);
+               break;
+       case ISCSI_PARAM_PDU_INORDER_EN:
+               sscanf(buf, "%d", &session->pdu_inorder_en);
+               break;
+       case ISCSI_PARAM_DATASEQ_INORDER_EN:
+               sscanf(buf, "%d", &session->dataseq_inorder_en);
+               break;
+       case ISCSI_PARAM_ERL:
+               sscanf(buf, "%d", &session->erl);
+               break;
+       case ISCSI_PARAM_IFMARKER_EN:
+               sscanf(buf, "%d", &value);
+               BUG_ON(value);
+               break;
+       case ISCSI_PARAM_OFMARKER_EN:
+               sscanf(buf, "%d", &value);
+               BUG_ON(value);
+               break;
+       case ISCSI_PARAM_EXP_STATSN:
+               sscanf(buf, "%u", &conn->exp_statsn);
+               break;
+       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;
+       case ISCSI_PARAM_TPGT:
+               sscanf(buf, "%d", &session->tpgt);
+               break;
+       case ISCSI_PARAM_PERSISTENT_PORT:
+               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;
+       default:
+               return -ENOSYS;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(iscsi_set_param);
+
+int iscsi_session_get_param(struct iscsi_cls_session *cls_session,
+                           enum iscsi_param param, char *buf)
+{
+       struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
+       struct iscsi_session *session = iscsi_hostdata(shost->hostdata);
+       int len;
+
+       switch(param) {
+       case ISCSI_PARAM_INITIAL_R2T_EN:
+               len = sprintf(buf, "%d\n", session->initial_r2t_en);
+               break;
+       case ISCSI_PARAM_MAX_R2T:
+               len = sprintf(buf, "%hu\n", session->max_r2t);
+               break;
+       case ISCSI_PARAM_IMM_DATA_EN:
+               len = sprintf(buf, "%d\n", session->imm_data_en);
+               break;
+       case ISCSI_PARAM_FIRST_BURST:
+               len = sprintf(buf, "%u\n", session->first_burst);
+               break;
+       case ISCSI_PARAM_MAX_BURST:
+               len = sprintf(buf, "%u\n", session->max_burst);
+               break;
+       case ISCSI_PARAM_PDU_INORDER_EN:
+               len = sprintf(buf, "%d\n", session->pdu_inorder_en);
+               break;
+       case ISCSI_PARAM_DATASEQ_INORDER_EN:
+               len = sprintf(buf, "%d\n", session->dataseq_inorder_en);
+               break;
+       case ISCSI_PARAM_ERL:
+               len = sprintf(buf, "%d\n", session->erl);
+               break;
+       case ISCSI_PARAM_TARGET_NAME:
+               len = sprintf(buf, "%s\n", session->targetname);
+               break;
+       case ISCSI_PARAM_TPGT:
+               len = sprintf(buf, "%d\n", session->tpgt);
+               break;
+       default:
+               return -ENOSYS;
+       }
+
+       return len;
+}
+EXPORT_SYMBOL_GPL(iscsi_session_get_param);
+
+int iscsi_conn_get_param(struct iscsi_cls_conn *cls_conn,
+                        enum iscsi_param param, char *buf)
+{
+       struct iscsi_conn *conn = cls_conn->dd_data;
+       int len;
+
+       switch(param) {
+       case ISCSI_PARAM_MAX_RECV_DLENGTH:
+               len = sprintf(buf, "%u\n", conn->max_recv_dlength);
+               break;
+       case ISCSI_PARAM_MAX_XMIT_DLENGTH:
+               len = sprintf(buf, "%u\n", conn->max_xmit_dlength);
+               break;
+       case ISCSI_PARAM_HDRDGST_EN:
+               len = sprintf(buf, "%d\n", conn->hdrdgst_en);
+               break;
+       case ISCSI_PARAM_DATADGST_EN:
+               len = sprintf(buf, "%d\n", conn->datadgst_en);
+               break;
+       case ISCSI_PARAM_IFMARKER_EN:
+               len = sprintf(buf, "%d\n", conn->ifmarker_en);
+               break;
+       case ISCSI_PARAM_OFMARKER_EN:
+               len = sprintf(buf, "%d\n", conn->ofmarker_en);
+               break;
+       case ISCSI_PARAM_EXP_STATSN:
+               len = sprintf(buf, "%u\n", conn->exp_statsn);
+               break;
+       case ISCSI_PARAM_PERSISTENT_PORT:
+               len = sprintf(buf, "%d\n", conn->persistent_port);
+               break;
+       case ISCSI_PARAM_PERSISTENT_ADDRESS:
+               len = sprintf(buf, "%s\n", conn->persistent_address);
+               break;
+       default:
+               return -ENOSYS;
+       }
+
+       return len;
+}
+EXPORT_SYMBOL_GPL(iscsi_conn_get_param);
+
 MODULE_AUTHOR("Mike Christie");
 MODULE_DESCRIPTION("iSCSI library functions");
 MODULE_LICENSE("GPL");