[SCSI] qla2xxx: Add asynchronous-login support.
[pandora-kernel.git] / drivers / scsi / qla2xxx / qla_init.c
index f2ce8e3..37c99a2 100644 (file)
@@ -40,6 +40,210 @@ static struct qla_chip_state_84xx *qla84xx_get_chip(struct scsi_qla_host *);
 static int qla84xx_init_chip(scsi_qla_host_t *);
 static int qla25xx_init_queues(struct qla_hw_data *);
 
+/* SRB Extensions ---------------------------------------------------------- */
+
+static void
+qla2x00_ctx_sp_timeout(unsigned long __data)
+{
+       srb_t *sp = (srb_t *)__data;
+       struct srb_ctx *ctx;
+       fc_port_t *fcport = sp->fcport;
+       struct qla_hw_data *ha = fcport->vha->hw;
+       struct req_que *req;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+       req = ha->req_q_map[0];
+       req->outstanding_cmds[sp->handle] = NULL;
+       ctx = sp->ctx;
+       ctx->timeout(sp);
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+       ctx->free(sp);
+}
+
+static void
+qla2x00_ctx_sp_free(srb_t *sp)
+{
+       struct srb_ctx *ctx = sp->ctx;
+
+       kfree(ctx);
+       mempool_free(sp, sp->fcport->vha->hw->srb_mempool);
+}
+
+inline srb_t *
+qla2x00_get_ctx_sp(scsi_qla_host_t *vha, fc_port_t *fcport, size_t size,
+    unsigned long tmo)
+{
+       srb_t *sp;
+       struct qla_hw_data *ha = vha->hw;
+       struct srb_ctx *ctx;
+
+       sp = mempool_alloc(ha->srb_mempool, GFP_KERNEL);
+       if (!sp)
+               goto done;
+       ctx = kzalloc(size, GFP_KERNEL);
+       if (!ctx) {
+               mempool_free(sp, ha->srb_mempool);
+               goto done;
+       }
+
+       memset(sp, 0, sizeof(*sp));
+       sp->fcport = fcport;
+       sp->ctx = ctx;
+       ctx->free = qla2x00_ctx_sp_free;
+
+       init_timer(&ctx->timer);
+       if (!tmo)
+               goto done;
+       ctx->timer.expires = jiffies + tmo * HZ;
+       ctx->timer.data = (unsigned long)sp;
+       ctx->timer.function = qla2x00_ctx_sp_timeout;
+       add_timer(&ctx->timer);
+done:
+       return sp;
+}
+
+/* Asynchronous Login/Logout Routines -------------------------------------- */
+
+#define ELS_TMO_2_RATOV(ha) ((ha)->r_a_tov / 10 * 2)
+
+static void
+qla2x00_async_logio_timeout(srb_t *sp)
+{
+       fc_port_t *fcport = sp->fcport;
+       struct srb_logio *lio = sp->ctx;
+
+       DEBUG2(printk(KERN_WARNING
+           "scsi(%ld:%x): Async-%s timeout.\n",
+           fcport->vha->host_no, sp->handle,
+           lio->ctx.type == SRB_LOGIN_CMD ? "login": "logout"));
+
+       if (lio->ctx.type == SRB_LOGIN_CMD)
+               qla2x00_post_async_logout_work(fcport->vha, fcport, NULL);
+}
+
+int
+qla2x00_async_login(struct scsi_qla_host *vha, fc_port_t *fcport,
+    uint16_t *data)
+{
+       struct qla_hw_data *ha = vha->hw;
+       srb_t *sp;
+       struct srb_logio *lio;
+       int rval;
+
+       rval = QLA_FUNCTION_FAILED;
+       sp = qla2x00_get_ctx_sp(vha, fcport, sizeof(struct srb_logio),
+           ELS_TMO_2_RATOV(ha) + 2);
+       if (!sp)
+               goto done;
+
+       lio = sp->ctx;
+       lio->ctx.type = SRB_LOGIN_CMD;
+       lio->ctx.timeout = qla2x00_async_logio_timeout;
+       lio->flags |= SRB_LOGIN_COND_PLOGI;
+       if (data[1] & QLA_LOGIO_LOGIN_RETRIED)
+               lio->flags |= SRB_LOGIN_RETRIED;
+       rval = qla2x00_start_sp(sp);
+       if (rval != QLA_SUCCESS)
+               goto done_free_sp;
+
+       DEBUG2(printk(KERN_DEBUG
+           "scsi(%ld:%x): Async-login - loop-id=%x portid=%02x%02x%02x "
+           "retries=%d.\n", fcport->vha->host_no, sp->handle, fcport->loop_id,
+           fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa,
+           fcport->login_retry));
+       return rval;
+
+done_free_sp:
+       del_timer_sync(&lio->ctx.timer);
+       lio->ctx.free(sp);
+done:
+       return rval;
+}
+
+int
+qla2x00_async_logout(struct scsi_qla_host *vha, fc_port_t *fcport)
+{
+       struct qla_hw_data *ha = vha->hw;
+       srb_t *sp;
+       struct srb_logio *lio;
+       int rval;
+
+       rval = QLA_FUNCTION_FAILED;
+       sp = qla2x00_get_ctx_sp(vha, fcport, sizeof(struct srb_logio),
+           ELS_TMO_2_RATOV(ha) + 2);
+       if (!sp)
+               goto done;
+
+       lio = sp->ctx;
+       lio->ctx.type = SRB_LOGOUT_CMD;
+       lio->ctx.timeout = qla2x00_async_logio_timeout;
+       rval = qla2x00_start_sp(sp);
+       if (rval != QLA_SUCCESS)
+               goto done_free_sp;
+
+       DEBUG2(printk(KERN_DEBUG
+           "scsi(%ld:%x): Async-logout - loop-id=%x portid=%02x%02x%02x.\n",
+           fcport->vha->host_no, sp->handle, fcport->loop_id,
+           fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa));
+       return rval;
+
+done_free_sp:
+       del_timer_sync(&lio->ctx.timer);
+       lio->ctx.free(sp);
+done:
+       return rval;
+}
+
+int
+qla2x00_async_login_done(struct scsi_qla_host *vha, fc_port_t *fcport,
+    uint16_t *data)
+{
+       int rval;
+       uint8_t opts = 0;
+
+       switch (data[0]) {
+       case MBS_COMMAND_COMPLETE:
+               if (fcport->flags & FCF_TAPE_PRESENT)
+                       opts |= BIT_1;
+               rval = qla2x00_get_port_database(vha, fcport, opts);
+               if (rval != QLA_SUCCESS)
+                       qla2x00_mark_device_lost(vha, fcport, 1, 0);
+               else
+                       qla2x00_update_fcport(vha, fcport);
+               break;
+       case MBS_COMMAND_ERROR:
+               if (data[1] & QLA_LOGIO_LOGIN_RETRIED)
+                       set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
+               else
+                       qla2x00_mark_device_lost(vha, fcport, 1, 0);
+               break;
+       case MBS_PORT_ID_USED:
+               fcport->loop_id = data[1];
+               qla2x00_post_async_login_work(vha, fcport, NULL);
+               break;
+       case MBS_LOOP_ID_USED:
+               fcport->loop_id++;
+               rval = qla2x00_find_new_loop_id(vha, fcport);
+               if (rval != QLA_SUCCESS) {
+                       qla2x00_mark_device_lost(vha, fcport, 1, 0);
+                       break;
+               }
+               qla2x00_post_async_login_work(vha, fcport, NULL);
+               break;
+       }
+       return QLA_SUCCESS;
+}
+
+int
+qla2x00_async_logout_done(struct scsi_qla_host *vha, fc_port_t *fcport,
+    uint16_t *data)
+{
+       qla2x00_mark_device_lost(vha, fcport, 1, 0);
+       return QLA_SUCCESS;
+}
+
 /****************************************************************************/
 /*                QLogic ISP2x00 Hardware Support Functions.                */
 /****************************************************************************/
@@ -987,7 +1191,6 @@ qla2x00_setup_chip(scsi_qla_host_t *vha)
                                    ha->phy_version);
                                if (rval != QLA_SUCCESS)
                                        goto failed;
-
                                ha->flags.npiv_supported = 0;
                                if (IS_QLA2XXX_MIDTYPE(ha) &&
                                         (ha->fw_attributes & BIT_2)) {
@@ -1978,7 +2181,7 @@ qla2x00_rport_del(void *data)
        struct fc_rport *rport;
 
        spin_lock_irq(fcport->vha->host->host_lock);
-       rport = fcport->drport;
+       rport = fcport->drport ? fcport->drport: fcport->rport;
        fcport->drport = NULL;
        spin_unlock_irq(fcport->vha->host->host_lock);
        if (rport)
@@ -2345,8 +2548,7 @@ qla2x00_reg_remote_port(scsi_qla_host_t *vha, fc_port_t *fcport)
        struct fc_rport *rport;
        struct qla_hw_data *ha = vha->hw;
 
-       if (fcport->drport)
-               qla2x00_rport_del(fcport);
+       qla2x00_rport_del(fcport);
 
        rport_ids.node_name = wwn_to_u64(fcport->node_name);
        rport_ids.port_name = wwn_to_u64(fcport->port_name);
@@ -3039,6 +3241,12 @@ qla2x00_fabric_dev_login(scsi_qla_host_t *vha, fc_port_t *fcport,
        rval = QLA_SUCCESS;
        retry = 0;
 
+       if (IS_ALOGIO_CAPABLE(ha)) {
+               rval = qla2x00_post_async_login_work(vha, fcport, NULL);
+               if (!rval)
+                       return rval;
+       }
+
        rval = qla2x00_fabric_login(vha, fcport, next_loopid);
        if (rval == QLA_SUCCESS) {
                /* Send an ADISC to tape devices.*/
@@ -3244,7 +3452,7 @@ qla2x00_loop_resync(scsi_qla_host_t *vha)
        struct req_que *req;
        struct rsp_que *rsp;
 
-       if (ql2xmultique_tag)
+       if (vha->hw->flags.cpu_affinity_enabled)
                req = vha->hw->req_q_map[0];
        else
                req = vha->req;
@@ -4264,7 +4472,7 @@ qla24xx_configure_vhba(scsi_qla_host_t *vha)
                return -EINVAL;
 
        rval = qla2x00_fw_ready(base_vha);
-       if (ql2xmultique_tag)
+       if (ha->flags.cpu_affinity_enabled)
                req = ha->req_q_map[0];
        else
                req = vha->req;