diff options
Diffstat (limited to 'drivers/scsi/libfc/fc_disc.c')
| -rw-r--r-- | drivers/scsi/libfc/fc_disc.c | 145 |
1 files changed, 69 insertions, 76 deletions
diff --git a/drivers/scsi/libfc/fc_disc.c b/drivers/scsi/libfc/fc_disc.c index 880a9068ca12..60d621ad0024 100644 --- a/drivers/scsi/libfc/fc_disc.c +++ b/drivers/scsi/libfc/fc_disc.c @@ -1,19 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright(c) 2007 - 2008 Intel Corporation. All rights reserved. * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * * Maintained at www.Open-FCoE.org */ @@ -36,7 +24,9 @@ #include <linux/slab.h> #include <linux/err.h> #include <linux/export.h> -#include <asm/unaligned.h> +#include <linux/list.h> + +#include <linux/unaligned.h> #include <scsi/fc/fc_gs.h> @@ -57,30 +47,25 @@ static void fc_disc_restart(struct fc_disc *); /** * fc_disc_stop_rports() - Delete all the remote ports associated with the lport * @disc: The discovery job to stop remote ports on - * - * Locking Note: This function expects that the lport mutex is locked before - * calling it. */ static void fc_disc_stop_rports(struct fc_disc *disc) { - struct fc_lport *lport; struct fc_rport_priv *rdata; - lport = fc_disc_lport(disc); + lockdep_assert_held(&disc->disc_mutex); - mutex_lock(&disc->disc_mutex); - list_for_each_entry_rcu(rdata, &disc->rports, peers) - lport->tt.rport_logoff(rdata); - mutex_unlock(&disc->disc_mutex); + list_for_each_entry(rdata, &disc->rports, peers) { + if (kref_get_unless_zero(&rdata->kref)) { + fc_rport_logoff(rdata); + kref_put(&rdata->kref, fc_rport_destroy); + } + } } /** * fc_disc_recv_rscn_req() - Handle Registered State Change Notification (RSCN) * @disc: The discovery object to which the RSCN applies * @fp: The RSCN frame - * - * Locking Note: This function expects that the disc_mutex is locked - * before it is called. */ static void fc_disc_recv_rscn_req(struct fc_disc *disc, struct fc_frame *fp) { @@ -90,11 +75,12 @@ static void fc_disc_recv_rscn_req(struct fc_disc *disc, struct fc_frame *fp) struct fc_seq_els_data rjt_data; unsigned int len; int redisc = 0; - enum fc_els_rscn_ev_qual ev_qual; enum fc_els_rscn_addr_fmt fmt; LIST_HEAD(disc_ports); struct fc_disc_port *dp, *next; + lockdep_assert_held(&disc->disc_mutex); + lport = fc_disc_lport(disc); FC_DISC_DBG(disc, "Received an RSCN event\n"); @@ -120,8 +106,6 @@ static void fc_disc_recv_rscn_req(struct fc_disc *disc, struct fc_frame *fp) goto reject; for (pp = (void *)(rp + 1); len > 0; len -= sizeof(*pp), pp++) { - ev_qual = pp->rscn_page_flags >> ELS_RSCN_EV_QUAL_BIT; - ev_qual &= ELS_RSCN_EV_QUAL_MASK; fmt = pp->rscn_page_flags >> ELS_RSCN_ADDR_FMT_BIT; fmt &= ELS_RSCN_ADDR_FMT_MASK; /* @@ -150,7 +134,7 @@ static void fc_disc_recv_rscn_req(struct fc_disc *disc, struct fc_frame *fp) break; } } - lport->tt.seq_els_rsp_send(fp, ELS_LS_ACC, NULL); + fc_seq_els_rsp_send(fp, ELS_LS_ACC, NULL); /* * If not doing a complete rediscovery, do GPN_ID on @@ -178,7 +162,7 @@ reject: FC_DISC_DBG(disc, "Received a bad RSCN frame\n"); rjt_data.reason = ELS_RJT_LOGIC; rjt_data.explan = ELS_EXPL_NONE; - lport->tt.seq_els_rsp_send(fp, ELS_LS_RJT, &rjt_data); + fc_seq_els_rsp_send(fp, ELS_LS_RJT, &rjt_data); fc_frame_free(fp); } @@ -214,12 +198,11 @@ static void fc_disc_recv_req(struct fc_lport *lport, struct fc_frame *fp) /** * fc_disc_restart() - Restart discovery * @disc: The discovery object to be restarted - * - * Locking Note: This function expects that the disc mutex - * is already locked. */ static void fc_disc_restart(struct fc_disc *disc) { + lockdep_assert_held(&disc->disc_mutex); + if (!disc->disc_callback) return; @@ -265,16 +248,13 @@ static void fc_disc_start(void (*disc_callback)(struct fc_lport *, * fc_disc_done() - Discovery has been completed * @disc: The discovery context * @event: The discovery completion status - * - * Locking Note: This function expects that the disc mutex is locked before - * it is called. The discovery callback is then made with the lock released, - * and the lock is re-taken before returning from this function */ static void fc_disc_done(struct fc_disc *disc, enum fc_disc_event event) { struct fc_lport *lport = fc_disc_lport(disc); struct fc_rport_priv *rdata; + lockdep_assert_held(&disc->disc_mutex); FC_DISC_DBG(disc, "Discovery complete\n"); disc->pending = 0; @@ -288,16 +268,21 @@ static void fc_disc_done(struct fc_disc *disc, enum fc_disc_event event) * discovery, reverify or log them in. Otherwise, log them out. * Skip ports which were never discovered. These are the dNS port * and ports which were created by PLOGI. + * + * We don't need to use the _rcu variant here as the rport list + * is protected by the disc mutex which is already held on entry. */ - list_for_each_entry_rcu(rdata, &disc->rports, peers) { - if (!rdata->disc_id) + list_for_each_entry(rdata, &disc->rports, peers) { + if (!kref_get_unless_zero(&rdata->kref)) continue; - if (rdata->disc_id == disc->disc_id) - lport->tt.rport_login(rdata); - else - lport->tt.rport_logoff(rdata); + if (rdata->disc_id) { + if (rdata->disc_id == disc->disc_id) + fc_rport_login(rdata); + else + fc_rport_logoff(rdata); + } + kref_put(&rdata->kref, fc_rport_destroy); } - mutex_unlock(&disc->disc_mutex); disc->disc_callback(lport, event); mutex_lock(&disc->disc_mutex); @@ -313,8 +298,8 @@ static void fc_disc_error(struct fc_disc *disc, struct fc_frame *fp) struct fc_lport *lport = fc_disc_lport(disc); unsigned long delay = 0; - FC_DISC_DBG(disc, "Error %ld, retries %d/%d\n", - PTR_ERR(fp), disc->retry_count, + FC_DISC_DBG(disc, "Error %d, retries %d/%d\n", + PTR_ERR_OR_ZERO(fp), disc->retry_count, FC_DISC_RETRY_LIMIT); if (!fp || PTR_ERR(fp) == -FC_EX_TIMEOUT) { @@ -349,16 +334,15 @@ static void fc_disc_error(struct fc_disc *disc, struct fc_frame *fp) /** * fc_disc_gpn_ft_req() - Send Get Port Names by FC-4 type (GPN_FT) request - * @lport: The discovery context - * - * Locking Note: This function expects that the disc_mutex is locked - * before it is called. + * @disc: The discovery context */ static void fc_disc_gpn_ft_req(struct fc_disc *disc) { struct fc_frame *fp; struct fc_lport *lport = fc_disc_lport(disc); + lockdep_assert_held(&disc->disc_mutex); + WARN_ON(!fc_lport_test_ready(lport)); disc->pending = 1; @@ -383,7 +367,7 @@ err: /** * fc_disc_gpn_ft_parse() - Parse the body of the dNS GPN_FT response. - * @lport: The local port the GPN_FT was received on + * @disc: The discovery context * @buf: The GPN_FT response buffer * @len: The size of response buffer * @@ -446,7 +430,7 @@ static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len) if (ids.port_id != lport->port_id && ids.port_name != lport->wwpn) { - rdata = lport->tt.rport_create(lport, ids.port_id); + rdata = fc_rport_create(lport, ids.port_id); if (rdata) { rdata->ids.port_name = ids.port_name; rdata->disc_id = disc->disc_id; @@ -501,7 +485,7 @@ static void fc_disc_timeout(struct work_struct *work) * fc_disc_gpn_ft_resp() - Handle a response frame from Get Port Names (GPN_FT) * @sp: The sequence that the GPN_FT response was received on * @fp: The GPN_FT response frame - * @lp_arg: The discovery context + * @disc_arg: The discovery context * * Locking Note: This function is called without disc mutex held, and * should do all its processing with the mutex held @@ -563,7 +547,7 @@ static void fc_disc_gpn_ft_resp(struct fc_seq *sp, struct fc_frame *fp, event = DISC_EV_FAILED; } if (error) - fc_disc_error(disc, fp); + fc_disc_error(disc, ERR_PTR(error)); else if (event != DISC_EV_NONE) fc_disc_done(disc, event); fc_frame_free(fp); @@ -592,11 +576,14 @@ static void fc_disc_gpn_id_resp(struct fc_seq *sp, struct fc_frame *fp, lport = rdata->local_port; disc = &lport->disc; - mutex_lock(&disc->disc_mutex); if (PTR_ERR(fp) == -FC_EX_CLOSED) goto out; - if (IS_ERR(fp)) - goto redisc; + if (IS_ERR(fp)) { + mutex_lock(&disc->disc_mutex); + fc_disc_restart(disc); + mutex_unlock(&disc->disc_mutex); + goto out; + } cp = fc_frame_payload_get(fp, sizeof(*cp)); if (!cp) @@ -607,37 +594,43 @@ static void fc_disc_gpn_id_resp(struct fc_seq *sp, struct fc_frame *fp, goto redisc; pn = (struct fc_ns_gid_pn *)(cp + 1); port_name = get_unaligned_be64(&pn->fn_wwpn); + mutex_lock(&rdata->rp_mutex); if (rdata->ids.port_name == -1) rdata->ids.port_name = port_name; else if (rdata->ids.port_name != port_name) { FC_DISC_DBG(disc, "GPN_ID accepted. WWPN changed. " "Port-id %6.6x wwpn %16.16llx\n", rdata->ids.port_id, port_name); - lport->tt.rport_logoff(rdata); - - new_rdata = lport->tt.rport_create(lport, - rdata->ids.port_id); + mutex_unlock(&rdata->rp_mutex); + fc_rport_logoff(rdata); + mutex_lock(&lport->disc.disc_mutex); + new_rdata = fc_rport_create(lport, rdata->ids.port_id); + mutex_unlock(&lport->disc.disc_mutex); if (new_rdata) { new_rdata->disc_id = disc->disc_id; - lport->tt.rport_login(new_rdata); + fc_rport_login(new_rdata); } - goto out; + goto free_fp; } rdata->disc_id = disc->disc_id; - lport->tt.rport_login(rdata); + mutex_unlock(&rdata->rp_mutex); + fc_rport_login(rdata); } else if (ntohs(cp->ct_cmd) == FC_FS_RJT) { FC_DISC_DBG(disc, "GPN_ID rejected reason %x exp %x\n", cp->ct_reason, cp->ct_explan); - lport->tt.rport_logoff(rdata); + fc_rport_logoff(rdata); } else { FC_DISC_DBG(disc, "GPN_ID unexpected response code %x\n", ntohs(cp->ct_cmd)); redisc: + mutex_lock(&disc->disc_mutex); fc_disc_restart(disc); + mutex_unlock(&disc->disc_mutex); } +free_fp: + fc_frame_free(fp); out: - mutex_unlock(&disc->disc_mutex); - kref_put(&rdata->kref, lport->tt.rport_destroy); + kref_put(&rdata->kref, fc_rport_destroy); } /** @@ -645,8 +638,6 @@ out: * @lport: The local port to initiate discovery on * @rdata: remote port private data * - * Locking Note: This function expects that the disc_mutex is locked - * before it is called. * On failure, an error code is returned. */ static int fc_disc_gpn_id_req(struct fc_lport *lport, @@ -654,6 +645,7 @@ static int fc_disc_gpn_id_req(struct fc_lport *lport, { struct fc_frame *fp; + lockdep_assert_held(&lport->disc.disc_mutex); fp = fc_frame_alloc(lport, sizeof(struct fc_ct_hdr) + sizeof(struct fc_ns_fid)); if (!fp) @@ -670,15 +662,14 @@ static int fc_disc_gpn_id_req(struct fc_lport *lport, * fc_disc_single() - Discover the directory information for a single target * @lport: The local port the remote port is associated with * @dp: The port to rediscover - * - * Locking Note: This function expects that the disc_mutex is locked - * before it is called. */ static int fc_disc_single(struct fc_lport *lport, struct fc_disc_port *dp) { struct fc_rport_priv *rdata; - rdata = lport->tt.rport_create(lport, dp->port_id); + lockdep_assert_held(&lport->disc.disc_mutex); + + rdata = fc_rport_create(lport, dp->port_id); if (!rdata) return -ENOMEM; rdata->disc_id = 0; @@ -695,7 +686,9 @@ static void fc_disc_stop(struct fc_lport *lport) if (disc->pending) cancel_delayed_work_sync(&disc->disc_work); + mutex_lock(&disc->disc_mutex); fc_disc_stop_rports(disc); + mutex_unlock(&disc->disc_mutex); } /** @@ -708,7 +701,7 @@ static void fc_disc_stop(struct fc_lport *lport) static void fc_disc_stop_final(struct fc_lport *lport) { fc_disc_stop(lport); - lport->tt.rport_flush_queue(); + fc_rport_flush_queue(); } /** @@ -718,7 +711,7 @@ static void fc_disc_stop_final(struct fc_lport *lport) */ void fc_disc_config(struct fc_lport *lport, void *priv) { - struct fc_disc *disc = &lport->disc; + struct fc_disc *disc; if (!lport->tt.disc_start) lport->tt.disc_start = fc_disc_start; |
