aboutsummaryrefslogtreecommitdiff
path: root/drivers/scsi/qla2xxx/qla_os.c
diff options
context:
space:
mode:
authorAndrew Vasquez <andrew.vasquez@qlogic.com>2008-04-03 13:13:18 -0700
committerJames Bottomley <James.Bottomley@HansenPartnership.com>2008-04-07 12:19:13 -0500
commit0971de7f56f809f40edae6fd372745e429e970e9 (patch)
tree8ae6829060081a81e62193db81278f6aa5365e08 /drivers/scsi/qla2xxx/qla_os.c
parentc6952483b070ec8a4f2450d1116be908fe59edcc (diff)
[SCSI] qla2xxx: Add FC-transport Asynchronous Event Notification support.
Supported events include LIP, LIP reset, RSCN, link up, and link down. To support AEN (and additional forthcoming features), we also introduce a simple deferred-work construct to manage events which require a non-atomic sleeping-capable context. This work-list is processed as part of the driver's standard DPC routine. Signed-off-by: Andrew Vasquez <andrew.vasquez@qlogic.com> Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Diffstat (limited to 'drivers/scsi/qla2xxx/qla_os.c')
-rw-r--r--drivers/scsi/qla2xxx/qla_os.c73
1 files changed, 73 insertions, 0 deletions
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 661a1596679..eb77067533a 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -1704,6 +1704,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
INIT_LIST_HEAD(&ha->list);
INIT_LIST_HEAD(&ha->fcports);
INIT_LIST_HEAD(&ha->vp_list);
+ INIT_LIST_HEAD(&ha->work_list);
set_bit(0, (unsigned long *) ha->vp_idx_map);
@@ -2197,6 +2198,76 @@ qla2x00_mem_free(scsi_qla_host_t *ha)
kfree(ha->nvram);
}
+struct qla_work_evt *
+qla2x00_alloc_work(struct scsi_qla_host *ha, enum qla_work_type type,
+ int locked)
+{
+ struct qla_work_evt *e;
+
+ e = kzalloc(sizeof(struct qla_work_evt), locked ? GFP_ATOMIC:
+ GFP_KERNEL);
+ if (!e)
+ return NULL;
+
+ INIT_LIST_HEAD(&e->list);
+ e->type = type;
+ e->flags = QLA_EVT_FLAG_FREE;
+ return e;
+}
+
+int
+qla2x00_post_work(struct scsi_qla_host *ha, struct qla_work_evt *e, int locked)
+{
+ unsigned long flags;
+
+ if (!locked)
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ list_add_tail(&e->list, &ha->work_list);
+ qla2xxx_wake_dpc(ha);
+ if (!locked)
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+ return QLA_SUCCESS;
+}
+
+int
+qla2x00_post_aen_work(struct scsi_qla_host *ha, enum fc_host_event_code code,
+ u32 data)
+{
+ struct qla_work_evt *e;
+
+ e = qla2x00_alloc_work(ha, QLA_EVT_AEN, 1);
+ if (!e)
+ return QLA_FUNCTION_FAILED;
+
+ e->u.aen.code = code;
+ e->u.aen.data = data;
+ return qla2x00_post_work(ha, e, 1);
+}
+
+static void
+qla2x00_do_work(struct scsi_qla_host *ha)
+{
+ struct qla_work_evt *e;
+
+ spin_lock_irq(&ha->hardware_lock);
+ while (!list_empty(&ha->work_list)) {
+ e = list_entry(ha->work_list.next, struct qla_work_evt, list);
+ list_del_init(&e->list);
+ spin_unlock_irq(&ha->hardware_lock);
+
+ switch (e->type) {
+ case QLA_EVT_AEN:
+ fc_host_post_event(ha->host, fc_get_event_number(),
+ e->u.aen.code, e->u.aen.data);
+ break;
+ }
+ if (e->flags & QLA_EVT_FLAG_FREE)
+ kfree(e);
+ spin_lock_irq(&ha->hardware_lock);
+ }
+ spin_unlock_irq(&ha->hardware_lock);
+}
+
/**************************************************************************
* qla2x00_do_dpc
* This kernel thread is a task that is schedule by the interrupt handler
@@ -2248,6 +2319,8 @@ qla2x00_do_dpc(void *data)
continue;
}
+ qla2x00_do_work(ha);
+
if (test_and_clear_bit(ISP_ABORT_NEEDED, &ha->dpc_flags)) {
DEBUG(printk("scsi(%ld): dpc: sched "