summaryrefslogtreecommitdiff
path: root/drivers/scsi/qla2xxx/qla_init.c
diff options
context:
space:
mode:
authorAndrew Vasquez <andrew.vasquez@qlogic.com>2009-08-20 18:06:05 (GMT)
committerJames Bottomley <James.Bottomley@suse.de>2009-09-05 14:41:57 (GMT)
commitac280b670e6d6666667aba02324e2fc50bd96ae7 (patch)
tree4d0d9187b26513ef9f820ab140f29a4f49f678b8 /drivers/scsi/qla2xxx/qla_init.c
parentcf53b069f52ae3f83dec1acd339e3c3a2e979478 (diff)
downloadlinux-ac280b670e6d6666667aba02324e2fc50bd96ae7.tar.xz
[SCSI] qla2xxx: Add asynchronous-login support.
ISPs which support this feature include 23xx and above. Signed-off-by: Andrew Vasquez <andrew.vasquez@qlogic.com> Signed-off-by: Giridhar Malavali <giridhar.malavali@qlogic.com> Signed-off-by: James Bottomley <James.Bottomley@suse.de>
Diffstat (limited to 'drivers/scsi/qla2xxx/qla_init.c')
-rw-r--r--drivers/scsi/qla2xxx/qla_init.c215
1 files changed, 212 insertions, 3 deletions
diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
index 0cbe39e9..37c99a2 100644
--- a/drivers/scsi/qla2xxx/qla_init.c
+++ b/drivers/scsi/qla2xxx/qla_init.c
@@ -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. */
/****************************************************************************/
@@ -1977,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)
@@ -2344,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);
@@ -3038,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.*/