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>
This commit is contained in:
parent
4d2095cc42
commit
a407c59339
|
@ -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)
|
||||||
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)
|
||||||
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) {
|
||||||
frport = fcoe_ctlr_rport(rdata);
|
if (!kref_get_unless_zero(&rdata->kref))
|
||||||
if (!frport->time)
|
|
||||||
continue;
|
continue;
|
||||||
|
frport = fcoe_ctlr_rport(rdata);
|
||||||
|
if (!frport->time) {
|
||||||
|
kref_put(&rdata->kref, lport->tt.rport_destroy);
|
||||||
|
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)
|
||||||
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)
|
||||||
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)
|
||||||
|
|
||||||
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)
|
||||||
* 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,
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
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)
|
||||||
* @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);
|
||||||
*/
|
*/
|
||||||
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)
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue