[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>
This commit is contained in:
Andrew Vasquez 2008-04-03 13:13:18 -07:00 committed by James Bottomley
parent c6952483b0
commit 0971de7f56
4 changed files with 102 additions and 0 deletions

View 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;

View 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.

View 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: */

View 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 "