From: Marcus Barrow <mbarrow@redhat.com> Date: Thu, 2 Jul 2009 12:22:47 -0400 Subject: [scsi] qla2xxx: prevent I/O stoppage Message-id: 20090702162247.6351.79590.sendpatchset@file.bos.redhat.com O-Subject: [rhel 5.4 patch] [V2] qla2xxx - prevent I/O stoppage. Bugzilla: 507620 RH-Acked-by: Pete Zaitcev <zaitcev@redhat.com> BZ 507620 - qla2xxx - properly handle event notification in FCoE environment Version 2 of this patch. Addresses Pete Zaitcev's request to put common code in a sub-routine and remove a duplicate debugging printk. This patch corrects a problem found by partners in FCoE environments. The driver's state engine does not properly handle this situation for FCoE and this will cause I/O to stop without resuming. Applies and builds cleanly with 2.6.18-154. Tested by QLogic and partners. This work will be submitted upstream. Correctly handle 'global port-unavailable' AEN. Treat a global port-unavailable PORT_UPDATE (8014h) AEN as a loop-down event. For this case, within the FCoE domain, the 'logical' interface has been terminated, but the driver will not receive the classic LOOP_DOWN AEN. diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h index c576cad..9b21639 100644 --- a/drivers/scsi/qla2xxx/qla_gbl.h +++ b/drivers/scsi/qla2xxx/qla_gbl.h @@ -109,6 +109,7 @@ extern void qla2x00_sp_compl(scsi_qla_host_t *, srb_t *); extern char *qla2x00_get_fw_version_str(struct scsi_qla_host *, char *); +extern void qla2x00_set_loop_down(scsi_qla_host_t *, uint16_t *); extern void qla2x00_mark_device_lost(scsi_qla_host_t *, fc_port_t *, int, int); extern void qla2x00_mark_all_devices_lost(scsi_qla_host_t *, int); diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index 069d3a6..882ee92 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -481,30 +481,7 @@ skip_rio: break; case MBA_LOOP_DOWN: /* Loop Down Event */ - DEBUG2(printk("scsi(%ld): Asynchronous LOOP DOWN (%x).\n", - ha->host_no, mb[1])); - qla_printk(KERN_INFO, ha, "LOOP DOWN detected (%x).\n", mb[1]); - DEBUG2(printk("scsi(%ld): Asynchronous LOOP DOWN " - "(%x %x %x).\n", ha->host_no, mb[1], mb[2], mb[3])); - qla_printk(KERN_INFO, ha, "LOOP DOWN detected (%x %x %x).\n", - mb[1], mb[2], mb[3]); - - if (atomic_read(&ha->loop_state) != LOOP_DOWN) { - atomic_set(&ha->loop_state, LOOP_DOWN); - atomic_set(&ha->loop_down_timer, LOOP_DOWN_TIME); - ha->device_flags |= DFLG_NO_CABLE; - qla2x00_mark_all_devices_lost(ha, 1); - } - - if (ha->parent) { - atomic_set(&ha->vp_state, VP_FAILED); - atomic_set(&ha->vport_last_state, - atomic_read(&ha->vport_state)); - atomic_set(&ha->vport_state, FC_VPORT_FAILED); - } - - ha->flags.management_server_logged_in = 0; - ha->link_data_rate = PORT_SPEED_UNKNOWN; + qla2x00_set_loop_down( ha, mb ); if (ql2xfdmienable) set_bit(REGISTER_FDMI_NEEDED, &ha->dpc_flags); break; @@ -602,6 +579,17 @@ skip_rio: break; case MBA_PORT_UPDATE: /* Port database update */ + /* Only handle SCNs for our Vport index. */ + if (mb[1] != 0xffff && + ha->vp_idx && ha->vp_idx != (mb[3] & 0xff)) + break; + + /* Global event -- port logout or port unavailable. */ + if (mb[1] == 0xffff && mb[2] == 0x7) { + qla2x00_set_loop_down( ha, mb ); + break; + } + /* * If PORT UPDATE is global (recieved LIP_OCCURED/LIP_RESET * event etc. earlier indicating loop is down) then process diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 16eb173..519ac68 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -2139,6 +2139,32 @@ qla2x00_schedule_rport_del(struct scsi_qla_host *ha, fc_port_t *fcport, fc_remote_port_delete(rport); } +void +qla2x00_set_loop_down(scsi_qla_host_t *ha, uint16_t *mb) +{ + DEBUG2(printk("scsi(%ld): Asynchronous LOOP DOWN " + "(%x %x %x).\n", ha->host_no, mb[1], mb[2], mb[3])); + qla_printk(KERN_INFO, ha, "LOOP DOWN detected (%x %x %x).\n", + mb[1], mb[2], mb[3]); + + if (atomic_read(&ha->loop_state) != LOOP_DOWN) { + atomic_set(&ha->loop_state, LOOP_DOWN); + atomic_set(&ha->loop_down_timer, LOOP_DOWN_TIME); + ha->device_flags |= DFLG_NO_CABLE; + qla2x00_mark_all_devices_lost(ha, 1); + } + + if (ha->parent) { + atomic_set(&ha->vp_state, VP_FAILED); + atomic_set(&ha->vport_last_state, + atomic_read(&ha->vport_state)); + atomic_set(&ha->vport_state, FC_VPORT_FAILED); + } + + ha->flags.management_server_logged_in = 0; + ha->link_data_rate = PORT_SPEED_UNKNOWN; +} + /* * qla2x00_mark_device_lost Updates fcport state when device goes offline. *