Skip to content

Commit a407c59

Browse files
hreineckemartinkpetersen
authored andcommitted
scsi: libfc: Fixup disc_mutex handling
The list of attached 'rdata' remote port structures is RCU protected, so there is no need to take the 'disc_mutex' when traversing it. Rather we should be using rcu_read_lock() and kref_get_unless_zero() to validate the entries. We need, however, take the disc_mutex when deleting an entry; otherwise we risk clashes with list_add. Signed-off-by: Hannes Reinecke <hare@suse.com> Acked-by: Johannes Thumshirn <jth@kernel.org> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
1 parent 4d2095c commit a407c59

File tree

4 files changed

+63
-23
lines changed

4 files changed

+63
-23
lines changed

drivers/scsi/fcoe/fcoe_ctlr.c

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2145,9 +2145,15 @@ static void fcoe_ctlr_disc_stop_locked(struct fc_lport *lport)
21452145
{
21462146
struct fc_rport_priv *rdata;
21472147

2148+
rcu_read_lock();
2149+
list_for_each_entry_rcu(rdata, &lport->disc.rports, peers) {
2150+
if (kref_get_unless_zero(&rdata->kref)) {
2151+
lport->tt.rport_logoff(rdata);
2152+
kref_put(&rdata->kref, lport->tt.rport_destroy);
2153+
}
2154+
}
2155+
rcu_read_unlock();
21482156
mutex_lock(&lport->disc.disc_mutex);
2149-
list_for_each_entry_rcu(rdata, &lport->disc.rports, peers)
2150-
lport->tt.rport_logoff(rdata);
21512157
lport->disc.disc_callback = NULL;
21522158
mutex_unlock(&lport->disc.disc_mutex);
21532159
}
@@ -2472,17 +2478,22 @@ static void fcoe_ctlr_vn_add(struct fcoe_ctlr *fip, struct fc_rport_priv *new)
24722478
mutex_unlock(&lport->disc.disc_mutex);
24732479
return;
24742480
}
2481+
mutex_lock(&rdata->rp_mutex);
2482+
mutex_unlock(&lport->disc.disc_mutex);
24752483

24762484
rdata->ops = &fcoe_ctlr_vn_rport_ops;
24772485
rdata->disc_id = lport->disc.disc_id;
24782486

24792487
ids = &rdata->ids;
24802488
if ((ids->port_name != -1 && ids->port_name != new->ids.port_name) ||
2481-
(ids->node_name != -1 && ids->node_name != new->ids.node_name))
2489+
(ids->node_name != -1 && ids->node_name != new->ids.node_name)) {
2490+
mutex_unlock(&rdata->rp_mutex);
24822491
lport->tt.rport_logoff(rdata);
2492+
mutex_lock(&rdata->rp_mutex);
2493+
}
24832494
ids->port_name = new->ids.port_name;
24842495
ids->node_name = new->ids.node_name;
2485-
mutex_unlock(&lport->disc.disc_mutex);
2496+
mutex_unlock(&rdata->rp_mutex);
24862497

24872498
frport = fcoe_ctlr_rport(rdata);
24882499
LIBFCOE_FIP_DBG(fip, "vn_add rport %6.6x %s\n",
@@ -2638,11 +2649,15 @@ static unsigned long fcoe_ctlr_vn_age(struct fcoe_ctlr *fip)
26382649
unsigned long deadline;
26392650

26402651
next_time = jiffies + msecs_to_jiffies(FIP_VN_BEACON_INT * 10);
2641-
mutex_lock(&lport->disc.disc_mutex);
2652+
rcu_read_lock();
26422653
list_for_each_entry_rcu(rdata, &lport->disc.rports, peers) {
2654+
if (!kref_get_unless_zero(&rdata->kref))
2655+
continue;
26432656
frport = fcoe_ctlr_rport(rdata);
2644-
if (!frport->time)
2657+
if (!frport->time) {
2658+
kref_put(&rdata->kref, lport->tt.rport_destroy);
26452659
continue;
2660+
}
26462661
deadline = frport->time +
26472662
msecs_to_jiffies(FIP_VN_BEACON_INT * 25 / 10);
26482663
if (time_after_eq(jiffies, deadline)) {
@@ -2653,8 +2668,9 @@ static unsigned long fcoe_ctlr_vn_age(struct fcoe_ctlr *fip)
26532668
lport->tt.rport_logoff(rdata);
26542669
} else if (time_before(deadline, next_time))
26552670
next_time = deadline;
2671+
kref_put(&rdata->kref, lport->tt.rport_destroy);
26562672
}
2657-
mutex_unlock(&lport->disc.disc_mutex);
2673+
rcu_read_unlock();
26582674
return next_time;
26592675
}
26602676

@@ -2991,12 +3007,17 @@ static void fcoe_ctlr_vn_disc(struct fcoe_ctlr *fip)
29913007
mutex_lock(&disc->disc_mutex);
29923008
callback = disc->pending ? disc->disc_callback : NULL;
29933009
disc->pending = 0;
3010+
mutex_unlock(&disc->disc_mutex);
3011+
rcu_read_lock();
29943012
list_for_each_entry_rcu(rdata, &disc->rports, peers) {
3013+
if (!kref_get_unless_zero(&rdata->kref))
3014+
continue;
29953015
frport = fcoe_ctlr_rport(rdata);
29963016
if (frport->time)
29973017
lport->tt.rport_login(rdata);
3018+
kref_put(&rdata->kref, lport->tt.rport_destroy);
29983019
}
2999-
mutex_unlock(&disc->disc_mutex);
3020+
rcu_read_unlock();
30003021
if (callback)
30013022
callback(lport, DISC_EV_SUCCESS);
30023023
}

drivers/scsi/libfc/fc_disc.c

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,14 @@ static void fc_disc_stop_rports(struct fc_disc *disc)
6868

6969
lport = fc_disc_lport(disc);
7070

71-
mutex_lock(&disc->disc_mutex);
72-
list_for_each_entry_rcu(rdata, &disc->rports, peers)
73-
lport->tt.rport_logoff(rdata);
74-
mutex_unlock(&disc->disc_mutex);
71+
rcu_read_lock();
72+
list_for_each_entry_rcu(rdata, &disc->rports, peers) {
73+
if (kref_get_unless_zero(&rdata->kref)) {
74+
lport->tt.rport_logoff(rdata);
75+
kref_put(&rdata->kref, lport->tt.rport_destroy);
76+
}
77+
}
78+
rcu_read_unlock();
7579
}
7680

7781
/**
@@ -289,15 +293,19 @@ static void fc_disc_done(struct fc_disc *disc, enum fc_disc_event event)
289293
* Skip ports which were never discovered. These are the dNS port
290294
* and ports which were created by PLOGI.
291295
*/
296+
rcu_read_lock();
292297
list_for_each_entry_rcu(rdata, &disc->rports, peers) {
293-
if (!rdata->disc_id)
298+
if (!kref_get_unless_zero(&rdata->kref))
294299
continue;
295-
if (rdata->disc_id == disc->disc_id)
296-
lport->tt.rport_login(rdata);
297-
else
298-
lport->tt.rport_logoff(rdata);
300+
if (rdata->disc_id) {
301+
if (rdata->disc_id == disc->disc_id)
302+
lport->tt.rport_login(rdata);
303+
else
304+
lport->tt.rport_logoff(rdata);
305+
}
306+
kref_put(&rdata->kref, lport->tt.rport_destroy);
299307
}
300-
308+
rcu_read_unlock();
301309
mutex_unlock(&disc->disc_mutex);
302310
disc->disc_callback(lport, event);
303311
mutex_lock(&disc->disc_mutex);
@@ -592,7 +600,6 @@ static void fc_disc_gpn_id_resp(struct fc_seq *sp, struct fc_frame *fp,
592600
lport = rdata->local_port;
593601
disc = &lport->disc;
594602

595-
mutex_lock(&disc->disc_mutex);
596603
if (PTR_ERR(fp) == -FC_EX_CLOSED)
597604
goto out;
598605
if (IS_ERR(fp))
@@ -607,23 +614,27 @@ static void fc_disc_gpn_id_resp(struct fc_seq *sp, struct fc_frame *fp,
607614
goto redisc;
608615
pn = (struct fc_ns_gid_pn *)(cp + 1);
609616
port_name = get_unaligned_be64(&pn->fn_wwpn);
617+
mutex_lock(&rdata->rp_mutex);
610618
if (rdata->ids.port_name == -1)
611619
rdata->ids.port_name = port_name;
612620
else if (rdata->ids.port_name != port_name) {
613621
FC_DISC_DBG(disc, "GPN_ID accepted. WWPN changed. "
614622
"Port-id %6.6x wwpn %16.16llx\n",
615623
rdata->ids.port_id, port_name);
624+
mutex_unlock(&rdata->rp_mutex);
616625
lport->tt.rport_logoff(rdata);
617-
626+
mutex_lock(&lport->disc.disc_mutex);
618627
new_rdata = lport->tt.rport_create(lport,
619628
rdata->ids.port_id);
629+
mutex_unlock(&lport->disc.disc_mutex);
620630
if (new_rdata) {
621631
new_rdata->disc_id = disc->disc_id;
622632
lport->tt.rport_login(new_rdata);
623633
}
624634
goto out;
625635
}
626636
rdata->disc_id = disc->disc_id;
637+
mutex_unlock(&rdata->rp_mutex);
627638
lport->tt.rport_login(rdata);
628639
} else if (ntohs(cp->ct_cmd) == FC_FS_RJT) {
629640
FC_DISC_DBG(disc, "GPN_ID rejected reason %x exp %x\n",
@@ -633,10 +644,11 @@ static void fc_disc_gpn_id_resp(struct fc_seq *sp, struct fc_frame *fp,
633644
FC_DISC_DBG(disc, "GPN_ID unexpected response code %x\n",
634645
ntohs(cp->ct_cmd));
635646
redisc:
647+
mutex_lock(&disc->disc_mutex);
636648
fc_disc_restart(disc);
649+
mutex_unlock(&disc->disc_mutex);
637650
}
638651
out:
639-
mutex_unlock(&disc->disc_mutex);
640652
kref_put(&rdata->kref, lport->tt.rport_destroy);
641653
}
642654

drivers/scsi/libfc/fc_lport.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -237,16 +237,19 @@ static const char *fc_lport_state(struct fc_lport *lport)
237237
* @remote_fid: The FID of the ptp rport
238238
* @remote_wwpn: The WWPN of the ptp rport
239239
* @remote_wwnn: The WWNN of the ptp rport
240+
*
241+
* Locking Note: The lport lock is expected to be held before calling
242+
* this routine.
240243
*/
241244
static void fc_lport_ptp_setup(struct fc_lport *lport,
242245
u32 remote_fid, u64 remote_wwpn,
243246
u64 remote_wwnn)
244247
{
245-
mutex_lock(&lport->disc.disc_mutex);
246248
if (lport->ptp_rdata) {
247249
lport->tt.rport_logoff(lport->ptp_rdata);
248250
kref_put(&lport->ptp_rdata->kref, lport->tt.rport_destroy);
249251
}
252+
mutex_lock(&lport->disc.disc_mutex);
250253
lport->ptp_rdata = lport->tt.rport_create(lport, remote_fid);
251254
kref_get(&lport->ptp_rdata->kref);
252255
lport->ptp_rdata->ids.port_name = remote_wwpn;
@@ -1007,8 +1010,10 @@ EXPORT_SYMBOL(fc_lport_reset);
10071010
*/
10081011
static void fc_lport_reset_locked(struct fc_lport *lport)
10091012
{
1010-
if (lport->dns_rdata)
1013+
if (lport->dns_rdata) {
10111014
lport->tt.rport_logoff(lport->dns_rdata);
1015+
lport->dns_rdata = NULL;
1016+
}
10121017

10131018
if (lport->ptp_rdata) {
10141019
lport->tt.rport_logoff(lport->ptp_rdata);

drivers/scsi/libfc/fc_rport.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,9 @@ static void fc_rport_work(struct work_struct *work)
378378
mutex_unlock(&rdata->rp_mutex);
379379
} else {
380380
FC_RPORT_DBG(rdata, "work delete\n");
381+
mutex_lock(&lport->disc.disc_mutex);
381382
list_del_rcu(&rdata->peers);
383+
mutex_unlock(&lport->disc.disc_mutex);
382384
mutex_unlock(&rdata->rp_mutex);
383385
kref_put(&rdata->kref, lport->tt.rport_destroy);
384386
}

0 commit comments

Comments
 (0)