[SCSI] qla2xxx: Correct synchronization issues during rport addition/deletion.
authorandrew.vasquez@qlogic.com <andrew.vasquez@qlogic.com>
Fri, 20 Jan 2006 22:53:13 +0000 (14:53 -0800)
committerJames Bottomley <jejb@mulgrave.(none)>
Thu, 26 Jan 2006 13:16:50 +0000 (08:16 -0500)
The driver can typically detect port-loss during an
interrupt context (i.e. via interrogation of a status IOCB's
completion status [CS_PORT_LOGGED_OUT].  Due to the calling
requirements of the fc_rport APIs, the driver would defer
removal of the device to the default workqueue.  If the
work-item was preceded by an event which caused the port to
obtain visibility (relogin successful, target re-logged into
the topology), deferred removal could inadvertently drop the
rport.  The code also no longer defers removal via the
default workqueue, instead opting for use of the driver's
own DPC thread.

Signed-off-by: Andrew Vasquez <andrew.vasquez@qlogic.com>
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
drivers/scsi/qla2xxx/qla_def.h
drivers/scsi/qla2xxx/qla_gbl.h
drivers/scsi/qla2xxx/qla_init.c
drivers/scsi/qla2xxx/qla_isr.c
drivers/scsi/qla2xxx/qla_os.c

index 79d8a914f9d0f4ddb213fd1bd9d5b63f68c39138..bad066e5772acfce0079784ef2fada7c73a9a707 100644 (file)
@@ -1680,7 +1680,8 @@ typedef struct fc_port {
        uint8_t mp_byte;                /* multi-path byte (not used) */
        uint8_t cur_path;               /* current path id */
 
-       struct fc_rport *rport;
+       spinlock_t rport_lock;
+       struct fc_rport *rport, *drport;
        u32 supported_classes;
        struct work_struct rport_add_work;
        struct work_struct rport_del_work;
@@ -2270,6 +2271,7 @@ typedef struct scsi_qla_host {
 #define LOOP_RESET_NEEDED      24
 #define BEACON_BLINK_NEEDED    25
 #define REGISTER_FDMI_NEEDED   26
+#define FCPORT_UPDATE_NEEDED   27
 
        uint32_t        device_flags;
 #define DFLG_LOCAL_DEVICES             BIT_0
index 32be4c14cccb50044a77b1c53b5b9be1eb5cc81d..0c1ec14f307221f23d14dd4131c12958c025dd94 100644 (file)
@@ -47,6 +47,7 @@ extern int qla2x00_local_device_login(scsi_qla_host_t *, uint16_t);
 extern void qla2x00_restart_queues(scsi_qla_host_t *, uint8_t);
 
 extern void qla2x00_rescan_fcports(scsi_qla_host_t *);
+extern void qla2x00_update_fcports(scsi_qla_host_t *);
 
 extern int qla2x00_abort_isp(scsi_qla_host_t *);
 
@@ -70,8 +71,8 @@ extern char *qla2x00_get_fw_version_str(struct scsi_qla_host *, char *);
 
 extern void qla2x00_cmd_timeout(srb_t *);
 
-extern void qla2x00_mark_device_lost(scsi_qla_host_t *, fc_port_t *, int);
-extern void qla2x00_mark_all_devices_lost(scsi_qla_host_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);
 
 extern void qla2x00_blink_led(scsi_qla_host_t *);
 
index a91fea69ad63597b938b9afde16260ebb358d476..4c7caece4ca775318cc4af9ad40dd615c34cccee 100644 (file)
@@ -1688,10 +1688,16 @@ static void
 qla2x00_rport_del(void *data)
 {
        fc_port_t *fcport = data;
+       struct fc_rport *rport;
+       unsigned long flags;
+
+       spin_lock_irqsave(&fcport->rport_lock, flags);
+       rport = fcport->drport;
+       fcport->drport = NULL;
+       spin_unlock_irqrestore(&fcport->rport_lock, flags);
+       if (rport)
+               fc_remote_port_delete(rport);
 
-       if (fcport->rport)
-               fc_remote_port_delete(fcport->rport);
-       fcport->rport = NULL;
 }
 
 /**
@@ -1719,6 +1725,7 @@ qla2x00_alloc_fcport(scsi_qla_host_t *ha, gfp_t flags)
        atomic_set(&fcport->state, FCS_UNCONFIGURED);
        fcport->flags = FCF_RLC_SUPPORT;
        fcport->supported_classes = FC_COS_UNSPECIFIED;
+       spin_lock_init(&fcport->rport_lock);
        INIT_WORK(&fcport->rport_add_work, qla2x00_rport_add, fcport);
        INIT_WORK(&fcport->rport_del_work, qla2x00_rport_del, fcport);
 
@@ -2008,7 +2015,7 @@ qla2x00_probe_for_all_luns(scsi_qla_host_t *ha)
 {
        fc_port_t       *fcport;
 
-       qla2x00_mark_all_devices_lost(ha);
+       qla2x00_mark_all_devices_lost(ha, 0);
        list_for_each_entry(fcport, &ha->fcports, list) {
                if (fcport->port_type != FCT_TARGET)
                        continue;
@@ -2084,24 +2091,29 @@ qla2x00_reg_remote_port(scsi_qla_host_t *ha, fc_port_t *fcport)
 {
        struct fc_rport_identifiers rport_ids;
        struct fc_rport *rport;
+       unsigned long flags;
 
-       if (fcport->rport) {
-               fc_remote_port_delete(fcport->rport);
-               fcport->rport = NULL;
-       }
+       if (fcport->drport)
+               qla2x00_rport_del(fcport);
+       if (fcport->rport)
+               return;
 
        rport_ids.node_name = wwn_to_u64(fcport->node_name);
        rport_ids.port_name = wwn_to_u64(fcport->port_name);
        rport_ids.port_id = fcport->d_id.b.domain << 16 |
            fcport->d_id.b.area << 8 | fcport->d_id.b.al_pa;
        rport_ids.roles = FC_RPORT_ROLE_UNKNOWN;
-       fcport->rport = rport = fc_remote_port_add(ha->host, 0, &rport_ids);
+       rport = fc_remote_port_add(ha->host, 0, &rport_ids);
        if (!rport) {
                qla_printk(KERN_WARNING, ha,
                    "Unable to allocate fc remote port!\n");
                return;
        }
+       spin_lock_irqsave(&fcport->rport_lock, flags);
+       fcport->rport = rport;
        *((fc_port_t **)rport->dd_data) = fcport;
+       spin_unlock_irqrestore(&fcport->rport_lock, flags);
+
        rport->supported_classes = fcport->supported_classes;
 
        rport_ids.roles = FC_RPORT_ROLE_UNKNOWN;
@@ -2217,12 +2229,11 @@ qla2x00_configure_fabric(scsi_qla_host_t *ha)
 
                        if (atomic_read(&fcport->state) == FCS_DEVICE_LOST) {
                                qla2x00_mark_device_lost(ha, fcport,
-                                   ql2xplogiabsentdevice);
+                                   ql2xplogiabsentdevice, 0);
                                if (fcport->loop_id != FC_NO_LOOP_ID &&
                                    (fcport->flags & FCF_TAPE_PRESENT) == 0 &&
                                    fcport->port_type != FCT_INITIATOR &&
                                    fcport->port_type != FCT_BROADCAST) {
-
                                        ha->isp_ops.fabric_logout(ha,
                                            fcport->loop_id,
                                            fcport->d_id.b.domain,
@@ -2694,7 +2705,8 @@ qla2x00_device_resync(scsi_qla_host_t *ha)
                        if (atomic_read(&fcport->state) == FCS_ONLINE) {
                                if (format != 3 ||
                                    fcport->port_type != FCT_INITIATOR) {
-                                       qla2x00_mark_device_lost(ha, fcport, 0);
+                                       qla2x00_mark_device_lost(ha, fcport,
+                                           0, 0);
                                }
                        }
                        fcport->flags &= ~FCF_FARP_DONE;
@@ -2741,8 +2753,7 @@ qla2x00_fabric_dev_login(scsi_qla_host_t *ha, fc_port_t *fcport,
                        ha->isp_ops.fabric_logout(ha, fcport->loop_id,
                            fcport->d_id.b.domain, fcport->d_id.b.area,
                            fcport->d_id.b.al_pa);
-                       qla2x00_mark_device_lost(ha, fcport, 1);
-
+                       qla2x00_mark_device_lost(ha, fcport, 1, 0);
                } else {
                        qla2x00_update_fcport(ha, fcport);
                }
@@ -2855,7 +2866,7 @@ qla2x00_fabric_login(scsi_qla_host_t *ha, fc_port_t *fcport,
                        ha->isp_ops.fabric_logout(ha, fcport->loop_id,
                            fcport->d_id.b.domain, fcport->d_id.b.area,
                            fcport->d_id.b.al_pa);
-                       qla2x00_mark_device_lost(ha, fcport, 1);
+                       qla2x00_mark_device_lost(ha, fcport, 1, 0);
 
                        rval = 1;
                        break;
@@ -2990,6 +3001,17 @@ qla2x00_rescan_fcports(scsi_qla_host_t *ha)
        qla2x00_probe_for_all_luns(ha);
 }
 
+void
+qla2x00_update_fcports(scsi_qla_host_t *ha)
+{
+       fc_port_t *fcport;
+
+       /* Go with deferred removal of rport references. */
+       list_for_each_entry(fcport, &ha->fcports, list)
+               if (fcport->drport)
+                       qla2x00_rport_del(fcport);
+}
+
 /*
 *  qla2x00_abort_isp
 *      Resets ISP and aborts all outstanding commands.
@@ -3019,7 +3041,7 @@ qla2x00_abort_isp(scsi_qla_host_t *ha)
                atomic_set(&ha->loop_down_timer, LOOP_DOWN_TIME);
                if (atomic_read(&ha->loop_state) != LOOP_DOWN) {
                        atomic_set(&ha->loop_state, LOOP_DOWN);
-                       qla2x00_mark_all_devices_lost(ha);
+                       qla2x00_mark_all_devices_lost(ha, 0);
                } else {
                        if (!atomic_read(&ha->loop_down_timer))
                                atomic_set(&ha->loop_down_timer,
index f63af081d4ff8aaf219e2255e06e7a0c334f7c09..71a46fcee8cc47d3b7e0cba5cf0aad80786f8d2b 100644 (file)
@@ -389,7 +389,7 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb)
                if (atomic_read(&ha->loop_state) != LOOP_DOWN) {
                        atomic_set(&ha->loop_state, LOOP_DOWN);
                        atomic_set(&ha->loop_down_timer, LOOP_DOWN_TIME);
-                       qla2x00_mark_all_devices_lost(ha);
+                       qla2x00_mark_all_devices_lost(ha, 1);
                }
 
                set_bit(REGISTER_FC4_NEEDED, &ha->dpc_flags);
@@ -432,7 +432,7 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb)
                        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);
+                       qla2x00_mark_all_devices_lost(ha, 1);
                }
 
                ha->flags.management_server_logged_in = 0;
@@ -453,7 +453,7 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb)
                if (atomic_read(&ha->loop_state) != LOOP_DOWN) {
                        atomic_set(&ha->loop_state, LOOP_DOWN);
                        atomic_set(&ha->loop_down_timer, LOOP_DOWN_TIME);
-                       qla2x00_mark_all_devices_lost(ha);
+                       qla2x00_mark_all_devices_lost(ha, 1);
                }
 
                set_bit(RESET_MARKER_NEEDED, &ha->dpc_flags);
@@ -482,7 +482,7 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb)
                        if (!atomic_read(&ha->loop_down_timer))
                                atomic_set(&ha->loop_down_timer,
                                    LOOP_DOWN_TIME);
-                       qla2x00_mark_all_devices_lost(ha);
+                       qla2x00_mark_all_devices_lost(ha, 1);
                }
 
                if (!(test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags))) {
@@ -506,7 +506,7 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb)
                        if (!atomic_read(&ha->loop_down_timer))
                                atomic_set(&ha->loop_down_timer,
                                    LOOP_DOWN_TIME);
-                       qla2x00_mark_all_devices_lost(ha);
+                       qla2x00_mark_all_devices_lost(ha, 1);
                }
 
                set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags);
@@ -580,7 +580,7 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb)
                 */
                atomic_set(&ha->loop_state, LOOP_UP);
 
-               qla2x00_mark_all_devices_lost(ha);
+               qla2x00_mark_all_devices_lost(ha, 1);
 
                ha->flags.rscn_queue_overflow = 1;
 
@@ -1091,7 +1091,7 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
 
                cp->result = DID_BUS_BUSY << 16;
                if (atomic_read(&fcport->state) == FCS_ONLINE) {
-                       qla2x00_mark_device_lost(ha, fcport, 1);
+                       qla2x00_mark_device_lost(ha, fcport, 1, 1);
                }
                break;
 
@@ -1135,7 +1135,7 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
 
                /* Check to see if logout occurred. */
                if ((le16_to_cpu(sts->status_flags) & SF_LOGOUT_SENT))
-                       qla2x00_mark_device_lost(ha, fcport, 1);
+                       qla2x00_mark_device_lost(ha, fcport, 1, 1);
                break;
 
        case CS_QUEUE_FULL:
index 4916847d84ec9321a58629c0eef15543e73d2873..089e0f500363a3b20d75f68495d097e05e151ef9 100644 (file)
@@ -756,7 +756,7 @@ qla2xxx_eh_device_reset(struct scsi_cmnd *cmd)
                if (ret == SUCCESS) {
                        if (fcport->flags & FC_FABRIC_DEVICE) {
                                ha->isp_ops.fabric_logout(ha, fcport->loop_id);
-                               qla2x00_mark_device_lost(ha, fcport);
+                               qla2x00_mark_device_lost(ha, fcport, 0, 0);
                        }
                }
 #endif
@@ -1642,6 +1642,31 @@ qla2x00_free_device(scsi_qla_host_t *ha)
        pci_disable_device(ha->pdev);
 }
 
+static inline void
+qla2x00_schedule_rport_del(struct scsi_qla_host *ha, fc_port_t *fcport,
+    int defer)
+{
+       unsigned long flags;
+       struct fc_rport *rport;
+
+       if (!fcport->rport)
+               return;
+
+       rport = fcport->rport;
+       if (defer) {
+               spin_lock_irqsave(&fcport->rport_lock, flags);
+               fcport->drport = rport;
+               fcport->rport = NULL;
+               spin_unlock_irqrestore(&fcport->rport_lock, flags);
+               set_bit(FCPORT_UPDATE_NEEDED, &ha->dpc_flags);
+       } else {
+               spin_lock_irqsave(&fcport->rport_lock, flags);
+               fcport->rport = NULL;
+               spin_unlock_irqrestore(&fcport->rport_lock, flags);
+               fc_remote_port_delete(rport);
+       }
+}
+
 /*
  * qla2x00_mark_device_lost Updates fcport state when device goes offline.
  *
@@ -1652,10 +1677,10 @@ qla2x00_free_device(scsi_qla_host_t *ha)
  * Context:
  */
 void qla2x00_mark_device_lost(scsi_qla_host_t *ha, fc_port_t *fcport,
-    int do_login)
+    int do_login, int defer)
 {
-       if (atomic_read(&fcport->state) == FCS_ONLINE && fcport->rport)
-               schedule_work(&fcport->rport_del_work);
+       if (atomic_read(&fcport->state) == FCS_ONLINE)
+               qla2x00_schedule_rport_del(ha, fcport, defer);
 
        /*
         * We may need to retry the login, so don't change the state of the
@@ -1702,7 +1727,7 @@ void qla2x00_mark_device_lost(scsi_qla_host_t *ha, fc_port_t *fcport,
  * Context:
  */
 void
-qla2x00_mark_all_devices_lost(scsi_qla_host_t *ha)
+qla2x00_mark_all_devices_lost(scsi_qla_host_t *ha, int defer)
 {
        fc_port_t *fcport;
 
@@ -1716,10 +1741,13 @@ qla2x00_mark_all_devices_lost(scsi_qla_host_t *ha)
                 */
                if (atomic_read(&fcport->state) == FCS_DEVICE_DEAD)
                        continue;
-               if (atomic_read(&fcport->state) == FCS_ONLINE && fcport->rport)
-                       schedule_work(&fcport->rport_del_work);
+               if (atomic_read(&fcport->state) == FCS_ONLINE)
+                       qla2x00_schedule_rport_del(ha, fcport, defer);
                atomic_set(&fcport->state, FCS_DEVICE_LOST);
        }
+
+       if (defer && ha->dpc_wait && !ha->dpc_active)
+               up(ha->dpc_wait);
 }
 
 /*
@@ -2161,6 +2189,9 @@ qla2x00_do_dpc(void *data)
                            ha->host_no));
                }
 
+               if (test_and_clear_bit(FCPORT_UPDATE_NEEDED, &ha->dpc_flags))
+                       qla2x00_update_fcports(ha);
+
                if (test_and_clear_bit(LOOP_RESET_NEEDED, &ha->dpc_flags)) {
                        DEBUG(printk("scsi(%ld): dpc: sched loop_reset()\n",
                            ha->host_no));
@@ -2469,6 +2500,7 @@ qla2x00_timer(scsi_qla_host_t *ha)
        if ((test_bit(ISP_ABORT_NEEDED, &ha->dpc_flags) ||
            test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags) ||
            test_bit(LOOP_RESET_NEEDED, &ha->dpc_flags) ||
+           test_bit(FCPORT_UPDATE_NEEDED, &ha->dpc_flags) ||
            start_dpc ||
            test_bit(LOGIN_RETRY_NEEDED, &ha->dpc_flags) ||
            test_bit(RESET_MARKER_NEEDED, &ha->dpc_flags) ||