Commit d97994dc authored by andrew.vasquez@qlogic.com's avatar andrew.vasquez@qlogic.com Committed by James Bottomley

[SCSI] qla2xxx: Correct synchronization issues during rport addition/deletion.

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: default avatarAndrew Vasquez <andrew.vasquez@qlogic.com>
Signed-off-by: default avatarJames Bottomley <James.Bottomley@SteelEye.com>
parent 1d12d98d
......@@ -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
......
......@@ -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 *);
......
......@@ -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,
......
......@@ -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:
......
......@@ -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) ||
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment