[SCSI] qla2xxx: Add FC-transport Asynchronous Event Notification support.
authorAndrew Vasquez <andrew.vasquez@qlogic.com>
Thu, 3 Apr 2008 20:13:18 +0000 (13:13 -0700)
committerJames Bottomley <James.Bottomley@HansenPartnership.com>
Mon, 7 Apr 2008 17:19:13 +0000 (12:19 -0500)
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>
drivers/scsi/qla2xxx/qla_def.h
drivers/scsi/qla2xxx/qla_gbl.h
drivers/scsi/qla2xxx/qla_isr.c
drivers/scsi/qla2xxx/qla_os.c

index f70c78b..35c730a 100644 (file)
@@ -2115,6 +2115,26 @@ struct qla_msix_entry {
 
 #define        WATCH_INTERVAL          1       /* number of seconds */
 
+/* Work events.  */
+enum qla_work_type {
+       QLA_EVT_AEN,
+};
+
+
+struct qla_work_evt {
+       struct list_head        list;
+       enum qla_work_type      type;
+       u32                     flags;
+#define QLA_EVT_FLAG_FREE      0x1
+
+       union {
+               struct {
+                       enum fc_host_event_code code;
+                       u32 data;
+               } aen;
+       } u;
+};
+
 /*
  * Linux Host Adapter structure
  */
@@ -2354,6 +2374,8 @@ typedef struct scsi_qla_host {
         uint32_t       login_retry_count;
        int             max_q_depth;
 
+       struct list_head        work_list;
+
        /* Fibre Channel Device List. */
        struct list_head        fcports;
 
index 97625d4..ee52f3e 100644 (file)
@@ -67,6 +67,8 @@ extern int num_hosts;
 
 extern int qla2x00_loop_reset(scsi_qla_host_t *);
 extern void qla2x00_abort_all_cmds(scsi_qla_host_t *, int);
+extern int qla2x00_post_aen_work(struct scsi_qla_host *, enum
+    fc_host_event_code, u32);
 
 /*
  * Global Functions in qla_mid.c source file.
index 4e9f410..e9d8a79 100644 (file)
@@ -408,6 +408,7 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb)
                set_bit(REGISTER_FC4_NEEDED, &ha->dpc_flags);
 
                ha->flags.management_server_logged_in = 0;
+               qla2x00_post_aen_work(ha, FCH_EVT_LIP, mb[1]);
                break;
 
        case MBA_LOOP_UP:               /* Loop Up Event */
@@ -427,6 +428,7 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb)
                    link_speed);
 
                ha->flags.management_server_logged_in = 0;
+               qla2x00_post_aen_work(ha, FCH_EVT_LINKUP, ha->link_data_rate);
                break;
 
        case MBA_LOOP_DOWN:             /* Loop Down Event */
@@ -450,6 +452,7 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb)
                ha->link_data_rate = PORT_SPEED_UNKNOWN;
                if (ql2xfdmienable)
                        set_bit(REGISTER_FDMI_NEEDED, &ha->dpc_flags);
+               qla2x00_post_aen_work(ha, FCH_EVT_LINKDOWN, 0);
                break;
 
        case MBA_LIP_RESET:             /* LIP reset occurred */
@@ -473,6 +476,7 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb)
 
                ha->operating_mode = LOOP;
                ha->flags.management_server_logged_in = 0;
+               qla2x00_post_aen_work(ha, FCH_EVT_LIPRESET, mb[1]);
                break;
 
        case MBA_POINT_TO_POINT:        /* Point-to-Point */
@@ -610,6 +614,7 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb)
 
                set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags);
                set_bit(RSCN_UPDATE, &ha->dpc_flags);
+               qla2x00_post_aen_work(ha, FCH_EVT_RSCN, rscn_entry);
                break;
 
        /* case MBA_RIO_RESPONSE: */
index 661a159..eb77067 100644 (file)
@@ -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 "