Commit a407c593 authored by Hannes Reinecke's avatar Hannes Reinecke Committed by Martin K. Petersen

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: default avatarHannes Reinecke <hare@suse.com>
Acked-by: default avatarJohannes Thumshirn <jth@kernel.org>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent 4d2095cc
...@@ -2145,9 +2145,15 @@ static void fcoe_ctlr_disc_stop_locked(struct fc_lport *lport) ...@@ -2145,9 +2145,15 @@ static void fcoe_ctlr_disc_stop_locked(struct fc_lport *lport)
{ {
struct fc_rport_priv *rdata; struct fc_rport_priv *rdata;
rcu_read_lock();
list_for_each_entry_rcu(rdata, &lport->disc.rports, peers) {
if (kref_get_unless_zero(&rdata->kref)) {
lport->tt.rport_logoff(rdata);
kref_put(&rdata->kref, lport->tt.rport_destroy);
}
}
rcu_read_unlock();
mutex_lock(&lport->disc.disc_mutex); mutex_lock(&lport->disc.disc_mutex);
list_for_each_entry_rcu(rdata, &lport->disc.rports, peers)
lport->tt.rport_logoff(rdata);
lport->disc.disc_callback = NULL; lport->disc.disc_callback = NULL;
mutex_unlock(&lport->disc.disc_mutex); mutex_unlock(&lport->disc.disc_mutex);
} }
...@@ -2472,17 +2478,22 @@ static void fcoe_ctlr_vn_add(struct fcoe_ctlr *fip, struct fc_rport_priv *new) ...@@ -2472,17 +2478,22 @@ static void fcoe_ctlr_vn_add(struct fcoe_ctlr *fip, struct fc_rport_priv *new)
mutex_unlock(&lport->disc.disc_mutex); mutex_unlock(&lport->disc.disc_mutex);
return; return;
} }
mutex_lock(&rdata->rp_mutex);
mutex_unlock(&lport->disc.disc_mutex);
rdata->ops = &fcoe_ctlr_vn_rport_ops; rdata->ops = &fcoe_ctlr_vn_rport_ops;
rdata->disc_id = lport->disc.disc_id; rdata->disc_id = lport->disc.disc_id;
ids = &rdata->ids; ids = &rdata->ids;
if ((ids->port_name != -1 && ids->port_name != new->ids.port_name) || if ((ids->port_name != -1 && ids->port_name != new->ids.port_name) ||
(ids->node_name != -1 && ids->node_name != new->ids.node_name)) (ids->node_name != -1 && ids->node_name != new->ids.node_name)) {
mutex_unlock(&rdata->rp_mutex);
lport->tt.rport_logoff(rdata); lport->tt.rport_logoff(rdata);
mutex_lock(&rdata->rp_mutex);
}
ids->port_name = new->ids.port_name; ids->port_name = new->ids.port_name;
ids->node_name = new->ids.node_name; ids->node_name = new->ids.node_name;
mutex_unlock(&lport->disc.disc_mutex); mutex_unlock(&rdata->rp_mutex);
frport = fcoe_ctlr_rport(rdata); frport = fcoe_ctlr_rport(rdata);
LIBFCOE_FIP_DBG(fip, "vn_add rport %6.6x %s\n", 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) ...@@ -2638,11 +2649,15 @@ static unsigned long fcoe_ctlr_vn_age(struct fcoe_ctlr *fip)
unsigned long deadline; unsigned long deadline;
next_time = jiffies + msecs_to_jiffies(FIP_VN_BEACON_INT * 10); next_time = jiffies + msecs_to_jiffies(FIP_VN_BEACON_INT * 10);
mutex_lock(&lport->disc.disc_mutex); rcu_read_lock();
list_for_each_entry_rcu(rdata, &lport->disc.rports, peers) { list_for_each_entry_rcu(rdata, &lport->disc.rports, peers) {
if (!kref_get_unless_zero(&rdata->kref))
continue;
frport = fcoe_ctlr_rport(rdata); frport = fcoe_ctlr_rport(rdata);
if (!frport->time) if (!frport->time) {
kref_put(&rdata->kref, lport->tt.rport_destroy);
continue; continue;
}
deadline = frport->time + deadline = frport->time +
msecs_to_jiffies(FIP_VN_BEACON_INT * 25 / 10); msecs_to_jiffies(FIP_VN_BEACON_INT * 25 / 10);
if (time_after_eq(jiffies, deadline)) { if (time_after_eq(jiffies, deadline)) {
...@@ -2653,8 +2668,9 @@ static unsigned long fcoe_ctlr_vn_age(struct fcoe_ctlr *fip) ...@@ -2653,8 +2668,9 @@ static unsigned long fcoe_ctlr_vn_age(struct fcoe_ctlr *fip)
lport->tt.rport_logoff(rdata); lport->tt.rport_logoff(rdata);
} else if (time_before(deadline, next_time)) } else if (time_before(deadline, next_time))
next_time = deadline; next_time = deadline;
kref_put(&rdata->kref, lport->tt.rport_destroy);
} }
mutex_unlock(&lport->disc.disc_mutex); rcu_read_unlock();
return next_time; return next_time;
} }
...@@ -2991,12 +3007,17 @@ static void fcoe_ctlr_vn_disc(struct fcoe_ctlr *fip) ...@@ -2991,12 +3007,17 @@ static void fcoe_ctlr_vn_disc(struct fcoe_ctlr *fip)
mutex_lock(&disc->disc_mutex); mutex_lock(&disc->disc_mutex);
callback = disc->pending ? disc->disc_callback : NULL; callback = disc->pending ? disc->disc_callback : NULL;
disc->pending = 0; disc->pending = 0;
mutex_unlock(&disc->disc_mutex);
rcu_read_lock();
list_for_each_entry_rcu(rdata, &disc->rports, peers) { list_for_each_entry_rcu(rdata, &disc->rports, peers) {
if (!kref_get_unless_zero(&rdata->kref))
continue;
frport = fcoe_ctlr_rport(rdata); frport = fcoe_ctlr_rport(rdata);
if (frport->time) if (frport->time)
lport->tt.rport_login(rdata); lport->tt.rport_login(rdata);
kref_put(&rdata->kref, lport->tt.rport_destroy);
} }
mutex_unlock(&disc->disc_mutex); rcu_read_unlock();
if (callback) if (callback)
callback(lport, DISC_EV_SUCCESS); callback(lport, DISC_EV_SUCCESS);
} }
......
...@@ -68,10 +68,14 @@ static void fc_disc_stop_rports(struct fc_disc *disc) ...@@ -68,10 +68,14 @@ static void fc_disc_stop_rports(struct fc_disc *disc)
lport = fc_disc_lport(disc); lport = fc_disc_lport(disc);
mutex_lock(&disc->disc_mutex); rcu_read_lock();
list_for_each_entry_rcu(rdata, &disc->rports, peers) list_for_each_entry_rcu(rdata, &disc->rports, peers) {
lport->tt.rport_logoff(rdata); if (kref_get_unless_zero(&rdata->kref)) {
mutex_unlock(&disc->disc_mutex); lport->tt.rport_logoff(rdata);
kref_put(&rdata->kref, lport->tt.rport_destroy);
}
}
rcu_read_unlock();
} }
/** /**
...@@ -289,15 +293,19 @@ static void fc_disc_done(struct fc_disc *disc, enum fc_disc_event event) ...@@ -289,15 +293,19 @@ static void fc_disc_done(struct fc_disc *disc, enum fc_disc_event event)
* Skip ports which were never discovered. These are the dNS port * Skip ports which were never discovered. These are the dNS port
* and ports which were created by PLOGI. * and ports which were created by PLOGI.
*/ */
rcu_read_lock();
list_for_each_entry_rcu(rdata, &disc->rports, peers) { list_for_each_entry_rcu(rdata, &disc->rports, peers) {
if (!rdata->disc_id) if (!kref_get_unless_zero(&rdata->kref))
continue; continue;
if (rdata->disc_id == disc->disc_id) if (rdata->disc_id) {
lport->tt.rport_login(rdata); if (rdata->disc_id == disc->disc_id)
else lport->tt.rport_login(rdata);
lport->tt.rport_logoff(rdata); else
lport->tt.rport_logoff(rdata);
}
kref_put(&rdata->kref, lport->tt.rport_destroy);
} }
rcu_read_unlock();
mutex_unlock(&disc->disc_mutex); mutex_unlock(&disc->disc_mutex);
disc->disc_callback(lport, event); disc->disc_callback(lport, event);
mutex_lock(&disc->disc_mutex); mutex_lock(&disc->disc_mutex);
...@@ -592,7 +600,6 @@ static void fc_disc_gpn_id_resp(struct fc_seq *sp, struct fc_frame *fp, ...@@ -592,7 +600,6 @@ static void fc_disc_gpn_id_resp(struct fc_seq *sp, struct fc_frame *fp,
lport = rdata->local_port; lport = rdata->local_port;
disc = &lport->disc; disc = &lport->disc;
mutex_lock(&disc->disc_mutex);
if (PTR_ERR(fp) == -FC_EX_CLOSED) if (PTR_ERR(fp) == -FC_EX_CLOSED)
goto out; goto out;
if (IS_ERR(fp)) if (IS_ERR(fp))
...@@ -607,16 +614,19 @@ static void fc_disc_gpn_id_resp(struct fc_seq *sp, struct fc_frame *fp, ...@@ -607,16 +614,19 @@ static void fc_disc_gpn_id_resp(struct fc_seq *sp, struct fc_frame *fp,
goto redisc; goto redisc;
pn = (struct fc_ns_gid_pn *)(cp + 1); pn = (struct fc_ns_gid_pn *)(cp + 1);
port_name = get_unaligned_be64(&pn->fn_wwpn); port_name = get_unaligned_be64(&pn->fn_wwpn);
mutex_lock(&rdata->rp_mutex);
if (rdata->ids.port_name == -1) if (rdata->ids.port_name == -1)
rdata->ids.port_name = port_name; rdata->ids.port_name = port_name;
else if (rdata->ids.port_name != port_name) { else if (rdata->ids.port_name != port_name) {
FC_DISC_DBG(disc, "GPN_ID accepted. WWPN changed. " FC_DISC_DBG(disc, "GPN_ID accepted. WWPN changed. "
"Port-id %6.6x wwpn %16.16llx\n", "Port-id %6.6x wwpn %16.16llx\n",
rdata->ids.port_id, port_name); rdata->ids.port_id, port_name);
mutex_unlock(&rdata->rp_mutex);
lport->tt.rport_logoff(rdata); lport->tt.rport_logoff(rdata);
mutex_lock(&lport->disc.disc_mutex);
new_rdata = lport->tt.rport_create(lport, new_rdata = lport->tt.rport_create(lport,
rdata->ids.port_id); rdata->ids.port_id);
mutex_unlock(&lport->disc.disc_mutex);
if (new_rdata) { if (new_rdata) {
new_rdata->disc_id = disc->disc_id; new_rdata->disc_id = disc->disc_id;
lport->tt.rport_login(new_rdata); lport->tt.rport_login(new_rdata);
...@@ -624,6 +634,7 @@ static void fc_disc_gpn_id_resp(struct fc_seq *sp, struct fc_frame *fp, ...@@ -624,6 +634,7 @@ static void fc_disc_gpn_id_resp(struct fc_seq *sp, struct fc_frame *fp,
goto out; goto out;
} }
rdata->disc_id = disc->disc_id; rdata->disc_id = disc->disc_id;
mutex_unlock(&rdata->rp_mutex);
lport->tt.rport_login(rdata); lport->tt.rport_login(rdata);
} else if (ntohs(cp->ct_cmd) == FC_FS_RJT) { } else if (ntohs(cp->ct_cmd) == FC_FS_RJT) {
FC_DISC_DBG(disc, "GPN_ID rejected reason %x exp %x\n", 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, ...@@ -633,10 +644,11 @@ static void fc_disc_gpn_id_resp(struct fc_seq *sp, struct fc_frame *fp,
FC_DISC_DBG(disc, "GPN_ID unexpected response code %x\n", FC_DISC_DBG(disc, "GPN_ID unexpected response code %x\n",
ntohs(cp->ct_cmd)); ntohs(cp->ct_cmd));
redisc: redisc:
mutex_lock(&disc->disc_mutex);
fc_disc_restart(disc); fc_disc_restart(disc);
mutex_unlock(&disc->disc_mutex);
} }
out: out:
mutex_unlock(&disc->disc_mutex);
kref_put(&rdata->kref, lport->tt.rport_destroy); kref_put(&rdata->kref, lport->tt.rport_destroy);
} }
......
...@@ -237,16 +237,19 @@ static const char *fc_lport_state(struct fc_lport *lport) ...@@ -237,16 +237,19 @@ static const char *fc_lport_state(struct fc_lport *lport)
* @remote_fid: The FID of the ptp rport * @remote_fid: The FID of the ptp rport
* @remote_wwpn: The WWPN of the ptp rport * @remote_wwpn: The WWPN of the ptp rport
* @remote_wwnn: The WWNN of the ptp rport * @remote_wwnn: The WWNN of the ptp rport
*
* Locking Note: The lport lock is expected to be held before calling
* this routine.
*/ */
static void fc_lport_ptp_setup(struct fc_lport *lport, static void fc_lport_ptp_setup(struct fc_lport *lport,
u32 remote_fid, u64 remote_wwpn, u32 remote_fid, u64 remote_wwpn,
u64 remote_wwnn) u64 remote_wwnn)
{ {
mutex_lock(&lport->disc.disc_mutex);
if (lport->ptp_rdata) { if (lport->ptp_rdata) {
lport->tt.rport_logoff(lport->ptp_rdata); lport->tt.rport_logoff(lport->ptp_rdata);
kref_put(&lport->ptp_rdata->kref, lport->tt.rport_destroy); kref_put(&lport->ptp_rdata->kref, lport->tt.rport_destroy);
} }
mutex_lock(&lport->disc.disc_mutex);
lport->ptp_rdata = lport->tt.rport_create(lport, remote_fid); lport->ptp_rdata = lport->tt.rport_create(lport, remote_fid);
kref_get(&lport->ptp_rdata->kref); kref_get(&lport->ptp_rdata->kref);
lport->ptp_rdata->ids.port_name = remote_wwpn; lport->ptp_rdata->ids.port_name = remote_wwpn;
...@@ -1007,8 +1010,10 @@ EXPORT_SYMBOL(fc_lport_reset); ...@@ -1007,8 +1010,10 @@ EXPORT_SYMBOL(fc_lport_reset);
*/ */
static void fc_lport_reset_locked(struct fc_lport *lport) static void fc_lport_reset_locked(struct fc_lport *lport)
{ {
if (lport->dns_rdata) if (lport->dns_rdata) {
lport->tt.rport_logoff(lport->dns_rdata); lport->tt.rport_logoff(lport->dns_rdata);
lport->dns_rdata = NULL;
}
if (lport->ptp_rdata) { if (lport->ptp_rdata) {
lport->tt.rport_logoff(lport->ptp_rdata); lport->tt.rport_logoff(lport->ptp_rdata);
......
...@@ -378,7 +378,9 @@ static void fc_rport_work(struct work_struct *work) ...@@ -378,7 +378,9 @@ static void fc_rport_work(struct work_struct *work)
mutex_unlock(&rdata->rp_mutex); mutex_unlock(&rdata->rp_mutex);
} else { } else {
FC_RPORT_DBG(rdata, "work delete\n"); FC_RPORT_DBG(rdata, "work delete\n");
mutex_lock(&lport->disc.disc_mutex);
list_del_rcu(&rdata->peers); list_del_rcu(&rdata->peers);
mutex_unlock(&lport->disc.disc_mutex);
mutex_unlock(&rdata->rp_mutex); mutex_unlock(&rdata->rp_mutex);
kref_put(&rdata->kref, lport->tt.rport_destroy); kref_put(&rdata->kref, lport->tt.rport_destroy);
} }
......
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