Skip to content

Commit df67327

Browse files
alexeipstgNicholas Bellinger
authored andcommitted
qla2xxx: added sess generations to detect RSCN update races
RSCN processing in qla2xxx driver can run in parallel with ELS/IO processing. As such the decision to remove disappeared fc port's session could be stale, because a new login sequence has occurred since and created a brand new session. Previous mechanism of dealing with this by delaying deletion request was prone to erroneous deletions if the event that was supposed to cancel the deletion never arrived or has been delayed in processing. New mechanism relies on a time-like generation counter to serialize RSCN updates relative to ELS/IO updates. Cc: <stable@vger.kernel.org> # v3.18+ Signed-off-by: Alexei Potashnik <alexei@purestorage.com> Signed-off-by: Himanshu Madhani <himanshu.madhani@qlogic.com> Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
1 parent daddf5c commit df67327

File tree

6 files changed

+83
-26
lines changed

6 files changed

+83
-26
lines changed

drivers/scsi/qla2xxx/qla_dbg.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@
6868
* | | | 0xd101-0xd1fe |
6969
* | | | 0xd214-0xd2fe |
7070
* | Target Mode | 0xe080 | |
71-
* | Target Mode Management | 0xf091 | 0xf002 |
71+
* | Target Mode Management | 0xf096 | 0xf002 |
7272
* | | | 0xf046-0xf049 |
7373
* | Target Mode Task Management | 0x1000d | |
7474
* ----------------------------------------------------------------------

drivers/scsi/qla2xxx/qla_def.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3586,6 +3586,11 @@ typedef struct scsi_qla_host {
35863586
struct list_head qla_sess_op_cmd_list;
35873587
spinlock_t cmd_list_lock;
35883588

3589+
/* Counter to detect races between ELS and RSCN events */
3590+
atomic_t generation_tick;
3591+
/* Time when global fcport update has been scheduled */
3592+
int total_fcport_update_gen;
3593+
35893594
uint32_t vp_abort_cnt;
35903595

35913596
struct fc_vport *fc_vport; /* holds fc_vport * for each vport */

drivers/scsi/qla2xxx/qla_init.c

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2927,24 +2927,14 @@ qla2x00_rport_del(void *data)
29272927
{
29282928
fc_port_t *fcport = data;
29292929
struct fc_rport *rport;
2930-
scsi_qla_host_t *vha = fcport->vha;
29312930
unsigned long flags;
2932-
unsigned long vha_flags;
29332931

29342932
spin_lock_irqsave(fcport->vha->host->host_lock, flags);
29352933
rport = fcport->drport ? fcport->drport: fcport->rport;
29362934
fcport->drport = NULL;
29372935
spin_unlock_irqrestore(fcport->vha->host->host_lock, flags);
2938-
if (rport) {
2936+
if (rport)
29392937
fc_remote_port_delete(rport);
2940-
/*
2941-
* Release the target mode FC NEXUS in qla_target.c code
2942-
* if target mod is enabled.
2943-
*/
2944-
spin_lock_irqsave(&vha->hw->hardware_lock, vha_flags);
2945-
qlt_fc_port_deleted(vha, fcport);
2946-
spin_unlock_irqrestore(&vha->hw->hardware_lock, vha_flags);
2947-
}
29482938
}
29492939

29502940
/**
@@ -3384,6 +3374,7 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha)
33843374
LIST_HEAD(new_fcports);
33853375
struct qla_hw_data *ha = vha->hw;
33863376
struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
3377+
int discovery_gen;
33873378

33883379
/* If FL port exists, then SNS is present */
33893380
if (IS_FWI2_CAPABLE(ha))
@@ -3454,6 +3445,14 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha)
34543445
fcport->scan_state = QLA_FCPORT_SCAN;
34553446
}
34563447

3448+
/* Mark the time right before querying FW for connected ports.
3449+
* This process is long, asynchronous and by the time it's done,
3450+
* collected information might not be accurate anymore. E.g.
3451+
* disconnected port might have re-connected and a brand new
3452+
* session has been created. In this case session's generation
3453+
* will be newer than discovery_gen. */
3454+
qlt_do_generation_tick(vha, &discovery_gen);
3455+
34573456
rval = qla2x00_find_all_fabric_devs(vha, &new_fcports);
34583457
if (rval != QLA_SUCCESS)
34593458
break;
@@ -3505,7 +3504,8 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha)
35053504
atomic_read(&fcport->state),
35063505
fcport->flags, fcport->fc4_type,
35073506
fcport->scan_state);
3508-
qlt_fc_port_deleted(vha, fcport);
3507+
qlt_fc_port_deleted(vha, fcport,
3508+
discovery_gen);
35093509
}
35103510
}
35113511
}
@@ -4282,6 +4282,14 @@ qla2x00_update_fcports(scsi_qla_host_t *base_vha)
42824282
atomic_read(&fcport->state) != FCS_UNCONFIGURED) {
42834283
spin_unlock_irqrestore(&ha->vport_slock, flags);
42844284
qla2x00_rport_del(fcport);
4285+
4286+
/*
4287+
* Release the target mode FC NEXUS in
4288+
* qla_target.c, if target mod is enabled.
4289+
*/
4290+
qlt_fc_port_deleted(vha, fcport,
4291+
base_vha->total_fcport_update_gen);
4292+
42854293
spin_lock_irqsave(&ha->vport_slock, flags);
42864294
}
42874295
}

drivers/scsi/qla2xxx/qla_os.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3230,11 +3230,14 @@ qla2x00_schedule_rport_del(struct scsi_qla_host *vha, fc_port_t *fcport,
32303230
spin_lock_irqsave(vha->host->host_lock, flags);
32313231
fcport->drport = rport;
32323232
spin_unlock_irqrestore(vha->host->host_lock, flags);
3233+
qlt_do_generation_tick(vha, &base_vha->total_fcport_update_gen);
32333234
set_bit(FCPORT_UPDATE_NEEDED, &base_vha->dpc_flags);
32343235
qla2xxx_wake_dpc(base_vha);
32353236
} else {
3237+
int now;
32363238
fc_remote_port_delete(rport);
3237-
qlt_fc_port_deleted(vha, fcport);
3239+
qlt_do_generation_tick(vha, &now);
3240+
qlt_fc_port_deleted(vha, fcport, now);
32383241
}
32393242
}
32403243

drivers/scsi/qla2xxx/qla_target.c

Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,16 @@ static struct workqueue_struct *qla_tgt_wq;
127127
static DEFINE_MUTEX(qla_tgt_mutex);
128128
static LIST_HEAD(qla_tgt_glist);
129129

130+
/* This API intentionally takes dest as a parameter, rather than returning
131+
* int value to avoid caller forgetting to issue wmb() after the store */
132+
void qlt_do_generation_tick(struct scsi_qla_host *vha, int *dest)
133+
{
134+
scsi_qla_host_t *base_vha = pci_get_drvdata(vha->hw->pdev);
135+
*dest = atomic_inc_return(&base_vha->generation_tick);
136+
/* memory barrier */
137+
wmb();
138+
}
139+
130140
/* ha->hardware_lock supposed to be held on entry (to protect tgt->sess_list) */
131141
static struct qla_tgt_sess *qlt_find_sess_by_port_name(
132142
struct qla_tgt *tgt,
@@ -576,10 +586,12 @@ static void qlt_schedule_sess_for_deletion(struct qla_tgt_sess *sess,
576586
sess->expires = jiffies + dev_loss_tmo * HZ;
577587

578588
ql_dbg(ql_dbg_tgt, sess->vha, 0xe048,
579-
"qla_target(%d): session for port %8phC (loop ID %d) scheduled for "
580-
"deletion in %u secs (expires: %lu) immed: %d, logout: %d\n",
581-
sess->vha->vp_idx, sess->port_name, sess->loop_id, dev_loss_tmo,
582-
sess->expires, immediate, sess->logout_on_delete);
589+
"qla_target(%d): session for port %8phC (loop ID %d s_id %02x:%02x:%02x)"
590+
" scheduled for deletion in %u secs (expires: %lu) immed: %d, logout: %d, gen: %#x\n",
591+
sess->vha->vp_idx, sess->port_name, sess->loop_id,
592+
sess->s_id.b.domain, sess->s_id.b.area, sess->s_id.b.al_pa,
593+
dev_loss_tmo, sess->expires, immediate, sess->logout_on_delete,
594+
sess->generation);
583595

584596
if (immediate)
585597
mod_delayed_work(system_wq, &tgt->sess_del_work, 0);
@@ -734,6 +746,9 @@ static struct qla_tgt_sess *qlt_create_sess(
734746

735747
if (sess->local && !local)
736748
sess->local = 0;
749+
750+
qlt_do_generation_tick(vha, &sess->generation);
751+
737752
spin_unlock_irqrestore(&ha->hardware_lock, flags);
738753

739754
return sess;
@@ -795,6 +810,7 @@ static struct qla_tgt_sess *qlt_create_sess(
795810
spin_lock_irqsave(&ha->hardware_lock, flags);
796811
list_add_tail(&sess->sess_list_entry, &vha->vha_tgt.qla_tgt->sess_list);
797812
vha->vha_tgt.qla_tgt->sess_count++;
813+
qlt_do_generation_tick(vha, &sess->generation);
798814
spin_unlock_irqrestore(&ha->hardware_lock, flags);
799815

800816
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf04b,
@@ -808,7 +824,7 @@ static struct qla_tgt_sess *qlt_create_sess(
808824
}
809825

810826
/*
811-
* Called from drivers/scsi/qla2xxx/qla_init.c:qla2x00_reg_remote_port()
827+
* Called from qla2x00_reg_remote_port()
812828
*/
813829
void qlt_fc_port_added(struct scsi_qla_host *vha, fc_port_t *fcport)
814830
{
@@ -874,7 +890,12 @@ void qlt_fc_port_added(struct scsi_qla_host *vha, fc_port_t *fcport)
874890
spin_unlock_irqrestore(&ha->hardware_lock, flags);
875891
}
876892

877-
void qlt_fc_port_deleted(struct scsi_qla_host *vha, fc_port_t *fcport)
893+
/*
894+
* max_gen - specifies maximum session generation
895+
* at which this deletion requestion is still valid
896+
*/
897+
void
898+
qlt_fc_port_deleted(struct scsi_qla_host *vha, fc_port_t *fcport, int max_gen)
878899
{
879900
struct qla_tgt *tgt = vha->vha_tgt.qla_tgt;
880901
struct qla_tgt_sess *sess;
@@ -893,6 +914,15 @@ void qlt_fc_port_deleted(struct scsi_qla_host *vha, fc_port_t *fcport)
893914
return;
894915
}
895916

917+
if (max_gen - sess->generation < 0) {
918+
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf092,
919+
"Ignoring stale deletion request for se_sess %p / sess %p"
920+
" for port %8phC, req_gen %d, sess_gen %d\n",
921+
sess->se_sess, sess, sess->port_name, max_gen,
922+
sess->generation);
923+
return;
924+
}
925+
896926
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf008, "qla_tgt_fc_port_deleted %p", sess);
897927

898928
sess->local = 1;
@@ -3970,7 +4000,7 @@ void qlt_logo_completion_handler(fc_port_t *fcport, int rc)
39704000
{
39714001
if (fcport->tgt_session) {
39724002
if (rc != MBS_COMMAND_COMPLETE) {
3973-
ql_dbg(ql_dbg_tgt_mgt, fcport->vha, 0xf088,
4003+
ql_dbg(ql_dbg_tgt_mgt, fcport->vha, 0xf093,
39744004
"%s: se_sess %p / sess %p from"
39754005
" port %8phC loop_id %#04x s_id %02x:%02x:%02x"
39764006
" LOGO failed: %#x\n",
@@ -4099,6 +4129,7 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha,
40994129
struct imm_ntfy_from_isp *iocb)
41004130
{
41014131
struct qla_tgt *tgt = vha->vha_tgt.qla_tgt;
4132+
struct qla_hw_data *ha = vha->hw;
41024133
struct qla_tgt_sess *sess = NULL;
41034134
uint64_t wwn;
41044135
port_id_t port_id;
@@ -4144,7 +4175,7 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha,
41444175
* without acking, new one will get acked when session
41454176
* deletion completes.
41464177
*/
4147-
ql_log(ql_log_warn, sess->vha, 0xf089,
4178+
ql_log(ql_log_warn, sess->vha, 0xf094,
41484179
"sess %p received double plogi.\n", sess);
41494180

41504181
qlt_swap_imm_ntfy_iocb(iocb, &sess->tm_iocb);
@@ -4201,7 +4232,7 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha,
42014232
* PLOGI could finish. Will force him to re-try,
42024233
* while last one finishes.
42034234
*/
4204-
ql_log(ql_log_warn, sess->vha, 0xf090,
4235+
ql_log(ql_log_warn, sess->vha, 0xf095,
42054236
"sess %p PRLI received, before plogi ack.\n",
42064237
sess);
42074238
qlt_send_term_imm_notif(vha, iocb, 1);
@@ -4213,7 +4244,7 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha,
42134244
* This shouldn't happen under normal circumstances,
42144245
* since we have deleted the old session during PLOGI
42154246
*/
4216-
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf091,
4247+
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf096,
42174248
"PRLI (loop_id %#04x) for existing sess %p (loop_id %#04x)\n",
42184249
sess->loop_id, sess, iocb->u.isp24.nport_handle);
42194250

@@ -4224,7 +4255,14 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha,
42244255
if (wd3_lo & BIT_7)
42254256
sess->conf_compl_supported = 1;
42264257

4227-
res = 1;
4258+
}
4259+
res = 1; /* send notify ack */
4260+
4261+
/* Make session global (not used in fabric mode) */
4262+
if (ha->current_topology != ISP_CFG_F) {
4263+
set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
4264+
set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
4265+
qla2xxx_wake_dpc(vha);
42284266
} else {
42294267
/* todo: else - create sess here. */
42304268
res = 1; /* send notify ack */

drivers/scsi/qla2xxx/qla_target.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -919,6 +919,8 @@ struct qla_tgt_sess {
919919

920920
unsigned char logout_completed;
921921

922+
int generation;
923+
922924
struct se_session *se_sess;
923925
struct scsi_qla_host *vha;
924926
struct qla_tgt *tgt;
@@ -1086,7 +1088,7 @@ extern int qlt_lport_register(void *, u64, u64, u64,
10861088
extern void qlt_lport_deregister(struct scsi_qla_host *);
10871089
extern void qlt_unreg_sess(struct qla_tgt_sess *);
10881090
extern void qlt_fc_port_added(struct scsi_qla_host *, fc_port_t *);
1089-
extern void qlt_fc_port_deleted(struct scsi_qla_host *, fc_port_t *);
1091+
extern void qlt_fc_port_deleted(struct scsi_qla_host *, fc_port_t *, int);
10901092
extern int __init qlt_init(void);
10911093
extern void qlt_exit(void);
10921094
extern void qlt_update_vp_map(struct scsi_qla_host *, int);
@@ -1163,5 +1165,6 @@ extern irqreturn_t qla83xx_msix_atio_q(int, void *);
11631165
extern void qlt_83xx_iospace_config(struct qla_hw_data *);
11641166
extern int qlt_free_qfull_cmds(struct scsi_qla_host *);
11651167
extern void qlt_logo_completion_handler(fc_port_t *, int);
1168+
extern void qlt_do_generation_tick(struct scsi_qla_host *, int *);
11661169

11671170
#endif /* __QLA_TARGET_H */

0 commit comments

Comments
 (0)