xref: /openbmc/linux/drivers/scsi/libfc/fc_disc.c (revision 6456ab5d)
1a61127c2SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
242e9a92fSRobert Love /*
342e9a92fSRobert Love  * Copyright(c) 2007 - 2008 Intel Corporation. All rights reserved.
442e9a92fSRobert Love  *
542e9a92fSRobert Love  * Maintained at www.Open-FCoE.org
642e9a92fSRobert Love  */
742e9a92fSRobert Love 
842e9a92fSRobert Love /*
942e9a92fSRobert Love  * Target Discovery
1042e9a92fSRobert Love  *
1142e9a92fSRobert Love  * This block discovers all FC-4 remote ports, including FCP initiators. It
1242e9a92fSRobert Love  * also handles RSCN events and re-discovery if necessary.
1342e9a92fSRobert Love  */
1442e9a92fSRobert Love 
1542e9a92fSRobert Love /*
1642e9a92fSRobert Love  * DISC LOCKING
1742e9a92fSRobert Love  *
1842e9a92fSRobert Love  * The disc mutex is can be locked when acquiring rport locks, but may not
1942e9a92fSRobert Love  * be held when acquiring the lport lock. Refer to fc_lport.c for more
2042e9a92fSRobert Love  * details.
2142e9a92fSRobert Love  */
2242e9a92fSRobert Love 
2342e9a92fSRobert Love #include <linux/timer.h>
245a0e3ad6STejun Heo #include <linux/slab.h>
2542e9a92fSRobert Love #include <linux/err.h>
2609703660SPaul Gortmaker #include <linux/export.h>
27*6456ab5dSChristophe JAILLET #include <linux/list.h>
28b2d09103SIngo Molnar 
2942e9a92fSRobert Love #include <asm/unaligned.h>
3042e9a92fSRobert Love 
3142e9a92fSRobert Love #include <scsi/fc/fc_gs.h>
3242e9a92fSRobert Love 
3342e9a92fSRobert Love #include <scsi/libfc.h>
3442e9a92fSRobert Love 
358866a5d9SRobert Love #include "fc_libfc.h"
368866a5d9SRobert Love 
3742e9a92fSRobert Love #define FC_DISC_RETRY_LIMIT	3	/* max retries */
3842e9a92fSRobert Love #define FC_DISC_RETRY_DELAY	500UL	/* (msecs) delay */
3942e9a92fSRobert Love 
4042e9a92fSRobert Love static void fc_disc_gpn_ft_req(struct fc_disc *);
4142e9a92fSRobert Love static void fc_disc_gpn_ft_resp(struct fc_seq *, struct fc_frame *, void *);
42786681b9SJoe Eykholt static void fc_disc_done(struct fc_disc *, enum fc_disc_event);
4342e9a92fSRobert Love static void fc_disc_timeout(struct work_struct *);
442ab7e1ecSJoe Eykholt static int fc_disc_single(struct fc_lport *, struct fc_disc_port *);
4542e9a92fSRobert Love static void fc_disc_restart(struct fc_disc *);
4642e9a92fSRobert Love 
4742e9a92fSRobert Love /**
483a3b42bfSRobert Love  * fc_disc_stop_rports() - Delete all the remote ports associated with the lport
493a3b42bfSRobert Love  * @disc: The discovery job to stop remote ports on
5042e9a92fSRobert Love  */
fc_disc_stop_rports(struct fc_disc * disc)51c6b21c93SBart Van Assche static void fc_disc_stop_rports(struct fc_disc *disc)
5242e9a92fSRobert Love {
5342e90414SJoe Eykholt 	struct fc_rport_priv *rdata;
5442e9a92fSRobert Love 
55bc3d12b7SHannes Reinecke 	lockdep_assert_held(&disc->disc_mutex);
5642e9a92fSRobert Love 
57bc3d12b7SHannes Reinecke 	list_for_each_entry(rdata, &disc->rports, peers) {
58a407c593SHannes Reinecke 		if (kref_get_unless_zero(&rdata->kref)) {
59c96c792aSHannes Reinecke 			fc_rport_logoff(rdata);
60944ef968SHannes Reinecke 			kref_put(&rdata->kref, fc_rport_destroy);
61a407c593SHannes Reinecke 		}
62a407c593SHannes Reinecke 	}
6342e9a92fSRobert Love }
6442e9a92fSRobert Love 
6542e9a92fSRobert Love /**
6634f42a07SRobert Love  * fc_disc_recv_rscn_req() - Handle Registered State Change Notification (RSCN)
6792261156SJoe Eykholt  * @disc:  The discovery object to which the RSCN applies
683a3b42bfSRobert Love  * @fp:	   The RSCN frame
6942e9a92fSRobert Love  */
fc_disc_recv_rscn_req(struct fc_disc * disc,struct fc_frame * fp)7092261156SJoe Eykholt static void fc_disc_recv_rscn_req(struct fc_disc *disc, struct fc_frame *fp)
7142e9a92fSRobert Love {
7242e9a92fSRobert Love 	struct fc_lport *lport;
7342e9a92fSRobert Love 	struct fc_els_rscn *rp;
7442e9a92fSRobert Love 	struct fc_els_rscn_page *pp;
7542e9a92fSRobert Love 	struct fc_seq_els_data rjt_data;
7642e9a92fSRobert Love 	unsigned int len;
7742e9a92fSRobert Love 	int redisc = 0;
7842e9a92fSRobert Love 	enum fc_els_rscn_addr_fmt fmt;
7942e9a92fSRobert Love 	LIST_HEAD(disc_ports);
8042e9a92fSRobert Love 	struct fc_disc_port *dp, *next;
8142e9a92fSRobert Love 
82ee35624eSHannes Reinecke 	lockdep_assert_held(&disc->disc_mutex);
83ee35624eSHannes Reinecke 
840685230cSJoe Eykholt 	lport = fc_disc_lport(disc);
8542e9a92fSRobert Love 
867414705eSRobert Love 	FC_DISC_DBG(disc, "Received an RSCN event\n");
8742e9a92fSRobert Love 
8842e9a92fSRobert Love 	/* make sure the frame contains an RSCN message */
8942e9a92fSRobert Love 	rp = fc_frame_payload_get(fp, sizeof(*rp));
9042e9a92fSRobert Love 	if (!rp)
9142e9a92fSRobert Love 		goto reject;
9242e9a92fSRobert Love 	/* make sure the page length is as expected (4 bytes) */
9342e9a92fSRobert Love 	if (rp->rscn_page_len != sizeof(*pp))
9442e9a92fSRobert Love 		goto reject;
9542e9a92fSRobert Love 	/* get the RSCN payload length */
9642e9a92fSRobert Love 	len = ntohs(rp->rscn_plen);
9742e9a92fSRobert Love 	if (len < sizeof(*rp))
9842e9a92fSRobert Love 		goto reject;
9942e9a92fSRobert Love 	/* make sure the frame contains the expected payload */
10042e9a92fSRobert Love 	rp = fc_frame_payload_get(fp, len);
10142e9a92fSRobert Love 	if (!rp)
10242e9a92fSRobert Love 		goto reject;
10342e9a92fSRobert Love 	/* payload must be a multiple of the RSCN page size */
10442e9a92fSRobert Love 	len -= sizeof(*rp);
10542e9a92fSRobert Love 	if (len % sizeof(*pp))
10642e9a92fSRobert Love 		goto reject;
10742e9a92fSRobert Love 
10842e9a92fSRobert Love 	for (pp = (void *)(rp + 1); len > 0; len -= sizeof(*pp), pp++) {
10942e9a92fSRobert Love 		fmt = pp->rscn_page_flags >> ELS_RSCN_ADDR_FMT_BIT;
11042e9a92fSRobert Love 		fmt &= ELS_RSCN_ADDR_FMT_MASK;
11142e9a92fSRobert Love 		/*
11242e9a92fSRobert Love 		 * if we get an address format other than port
11342e9a92fSRobert Love 		 * (area, domain, fabric), then do a full discovery
11442e9a92fSRobert Love 		 */
11542e9a92fSRobert Love 		switch (fmt) {
11642e9a92fSRobert Love 		case ELS_ADDR_FMT_PORT:
1177414705eSRobert Love 			FC_DISC_DBG(disc, "Port address format for port "
118ce8b5df0SChris Leech 				    "(%6.6x)\n", ntoh24(pp->rscn_fid));
11942e9a92fSRobert Love 			dp = kzalloc(sizeof(*dp), GFP_KERNEL);
12042e9a92fSRobert Love 			if (!dp) {
12142e9a92fSRobert Love 				redisc = 1;
12242e9a92fSRobert Love 				break;
12342e9a92fSRobert Love 			}
12442e9a92fSRobert Love 			dp->lp = lport;
1259737e6a7SRobert Love 			dp->port_id = ntoh24(pp->rscn_fid);
12642e9a92fSRobert Love 			list_add_tail(&dp->peers, &disc_ports);
12742e9a92fSRobert Love 			break;
12842e9a92fSRobert Love 		case ELS_ADDR_FMT_AREA:
12942e9a92fSRobert Love 		case ELS_ADDR_FMT_DOM:
13042e9a92fSRobert Love 		case ELS_ADDR_FMT_FAB:
13142e9a92fSRobert Love 		default:
1327414705eSRobert Love 			FC_DISC_DBG(disc, "Address format is (%d)\n", fmt);
13342e9a92fSRobert Love 			redisc = 1;
13442e9a92fSRobert Love 			break;
13542e9a92fSRobert Love 		}
13642e9a92fSRobert Love 	}
1377ab24dd1SHannes Reinecke 	fc_seq_els_rsp_send(fp, ELS_LS_ACC, NULL);
1382ab7e1ecSJoe Eykholt 
1392ab7e1ecSJoe Eykholt 	/*
1402ab7e1ecSJoe Eykholt 	 * If not doing a complete rediscovery, do GPN_ID on
1412ab7e1ecSJoe Eykholt 	 * the individual ports mentioned in the list.
1422ab7e1ecSJoe Eykholt 	 * If any of these get an error, do a full rediscovery.
1432ab7e1ecSJoe Eykholt 	 * In any case, go through the list and free the entries.
1442ab7e1ecSJoe Eykholt 	 */
1452ab7e1ecSJoe Eykholt 	list_for_each_entry_safe(dp, next, &disc_ports, peers) {
1462ab7e1ecSJoe Eykholt 		list_del(&dp->peers);
1472ab7e1ecSJoe Eykholt 		if (!redisc)
1482ab7e1ecSJoe Eykholt 			redisc = fc_disc_single(lport, dp);
1492ab7e1ecSJoe Eykholt 		kfree(dp);
1502ab7e1ecSJoe Eykholt 	}
15142e9a92fSRobert Love 	if (redisc) {
1527414705eSRobert Love 		FC_DISC_DBG(disc, "RSCN received: rediscovering\n");
15342e9a92fSRobert Love 		fc_disc_restart(disc);
15442e9a92fSRobert Love 	} else {
1557414705eSRobert Love 		FC_DISC_DBG(disc, "RSCN received: not rediscovering. "
15642e9a92fSRobert Love 			    "redisc %d state %d in_prog %d\n",
15742e9a92fSRobert Love 			    redisc, lport->state, disc->pending);
15842e9a92fSRobert Love 	}
15942e9a92fSRobert Love 	fc_frame_free(fp);
16042e9a92fSRobert Love 	return;
16142e9a92fSRobert Love reject:
1627414705eSRobert Love 	FC_DISC_DBG(disc, "Received a bad RSCN frame\n");
16342e9a92fSRobert Love 	rjt_data.reason = ELS_RJT_LOGIC;
16442e9a92fSRobert Love 	rjt_data.explan = ELS_EXPL_NONE;
1657ab24dd1SHannes Reinecke 	fc_seq_els_rsp_send(fp, ELS_LS_RJT, &rjt_data);
16642e9a92fSRobert Love 	fc_frame_free(fp);
16742e9a92fSRobert Love }
16842e9a92fSRobert Love 
16942e9a92fSRobert Love /**
17034f42a07SRobert Love  * fc_disc_recv_req() - Handle incoming requests
1713a3b42bfSRobert Love  * @lport: The local port receiving the request
17292261156SJoe Eykholt  * @fp:	   The request frame
17342e9a92fSRobert Love  *
17442e9a92fSRobert Love  * Locking Note: This function is called from the EM and will lock
17542e9a92fSRobert Love  *		 the disc_mutex before calling the handler for the
17642e9a92fSRobert Love  *		 request.
17742e9a92fSRobert Love  */
fc_disc_recv_req(struct fc_lport * lport,struct fc_frame * fp)17892261156SJoe Eykholt static void fc_disc_recv_req(struct fc_lport *lport, struct fc_frame *fp)
17942e9a92fSRobert Love {
18042e9a92fSRobert Love 	u8 op;
18142e9a92fSRobert Love 	struct fc_disc *disc = &lport->disc;
18242e9a92fSRobert Love 
18342e9a92fSRobert Love 	op = fc_frame_payload_op(fp);
18442e9a92fSRobert Love 	switch (op) {
18542e9a92fSRobert Love 	case ELS_RSCN:
18642e9a92fSRobert Love 		mutex_lock(&disc->disc_mutex);
18792261156SJoe Eykholt 		fc_disc_recv_rscn_req(disc, fp);
18842e9a92fSRobert Love 		mutex_unlock(&disc->disc_mutex);
18942e9a92fSRobert Love 		break;
19042e9a92fSRobert Love 	default:
1917414705eSRobert Love 		FC_DISC_DBG(disc, "Received an unsupported request, "
1927414705eSRobert Love 			    "the opcode is (%x)\n", op);
19383383dd1SHillf Danton 		fc_frame_free(fp);
19442e9a92fSRobert Love 		break;
19542e9a92fSRobert Love 	}
19642e9a92fSRobert Love }
19742e9a92fSRobert Love 
19842e9a92fSRobert Love /**
19934f42a07SRobert Love  * fc_disc_restart() - Restart discovery
2003a3b42bfSRobert Love  * @disc: The discovery object to be restarted
20142e9a92fSRobert Love  */
fc_disc_restart(struct fc_disc * disc)20242e9a92fSRobert Love static void fc_disc_restart(struct fc_disc *disc)
20342e9a92fSRobert Love {
204ee35624eSHannes Reinecke 	lockdep_assert_held(&disc->disc_mutex);
205ee35624eSHannes Reinecke 
206935d0fceSJoe Eykholt 	if (!disc->disc_callback)
207935d0fceSJoe Eykholt 		return;
208935d0fceSJoe Eykholt 
2097414705eSRobert Love 	FC_DISC_DBG(disc, "Restarting discovery\n");
21042e9a92fSRobert Love 
21142e9a92fSRobert Love 	disc->requested = 1;
2120f6c6149SJoe Eykholt 	if (disc->pending)
2130f6c6149SJoe Eykholt 		return;
2140f6c6149SJoe Eykholt 
2150f6c6149SJoe Eykholt 	/*
2160f6c6149SJoe Eykholt 	 * Advance disc_id.  This is an arbitrary non-zero number that will
2170f6c6149SJoe Eykholt 	 * match the value in the fc_rport_priv after discovery for all
2180f6c6149SJoe Eykholt 	 * freshly-discovered remote ports.  Avoid wrapping to zero.
2190f6c6149SJoe Eykholt 	 */
2200f6c6149SJoe Eykholt 	disc->disc_id = (disc->disc_id + 2) | 1;
2213667d7e7SJoe Eykholt 	disc->retry_count = 0;
22242e9a92fSRobert Love 	fc_disc_gpn_ft_req(disc);
22342e9a92fSRobert Love }
22442e9a92fSRobert Love 
22542e9a92fSRobert Love /**
2263a3b42bfSRobert Love  * fc_disc_start() - Start discovery on a local port
2273a3b42bfSRobert Love  * @lport:	   The local port to have discovery started on
2283a3b42bfSRobert Love  * @disc_callback: Callback function to be called when discovery is complete
22942e9a92fSRobert Love  */
fc_disc_start(void (* disc_callback)(struct fc_lport *,enum fc_disc_event),struct fc_lport * lport)23042e9a92fSRobert Love static void fc_disc_start(void (*disc_callback)(struct fc_lport *,
23142e9a92fSRobert Love 						enum fc_disc_event),
23242e9a92fSRobert Love 			  struct fc_lport *lport)
23342e9a92fSRobert Love {
23442e9a92fSRobert Love 	struct fc_disc *disc = &lport->disc;
23542e9a92fSRobert Love 
23642e9a92fSRobert Love 	/*
23742e9a92fSRobert Love 	 * At this point we may have a new disc job or an existing
23842e9a92fSRobert Love 	 * one. Either way, let's lock when we make changes to it
23942e9a92fSRobert Love 	 * and send the GPN_FT request.
24042e9a92fSRobert Love 	 */
24142e9a92fSRobert Love 	mutex_lock(&disc->disc_mutex);
24242e9a92fSRobert Love 	disc->disc_callback = disc_callback;
24329d898e9SJoe Eykholt 	fc_disc_restart(disc);
24442e9a92fSRobert Love 	mutex_unlock(&disc->disc_mutex);
24542e9a92fSRobert Love }
24642e9a92fSRobert Love 
24742e9a92fSRobert Love /**
24834f42a07SRobert Love  * fc_disc_done() - Discovery has been completed
2493a3b42bfSRobert Love  * @disc:  The discovery context
2503a3b42bfSRobert Love  * @event: The discovery completion status
25142e9a92fSRobert Love  */
fc_disc_done(struct fc_disc * disc,enum fc_disc_event event)252786681b9SJoe Eykholt static void fc_disc_done(struct fc_disc *disc, enum fc_disc_event event)
25342e9a92fSRobert Love {
2540685230cSJoe Eykholt 	struct fc_lport *lport = fc_disc_lport(disc);
2550f6c6149SJoe Eykholt 	struct fc_rport_priv *rdata;
25642e9a92fSRobert Love 
257ee35624eSHannes Reinecke 	lockdep_assert_held(&disc->disc_mutex);
2587414705eSRobert Love 	FC_DISC_DBG(disc, "Discovery complete\n");
25942e9a92fSRobert Love 
26042e9a92fSRobert Love 	disc->pending = 0;
2610f6c6149SJoe Eykholt 	if (disc->requested) {
2620f6c6149SJoe Eykholt 		fc_disc_restart(disc);
2630f6c6149SJoe Eykholt 		return;
2640f6c6149SJoe Eykholt 	}
2650f6c6149SJoe Eykholt 
2660f6c6149SJoe Eykholt 	/*
2670f6c6149SJoe Eykholt 	 * Go through all remote ports.	 If they were found in the latest
2680f6c6149SJoe Eykholt 	 * discovery, reverify or log them in.	Otherwise, log them out.
2690f6c6149SJoe Eykholt 	 * Skip ports which were never discovered.  These are the dNS port
2700f6c6149SJoe Eykholt 	 * and ports which were created by PLOGI.
271fa519f70SHannes Reinecke 	 *
272fa519f70SHannes Reinecke 	 * We don't need to use the _rcu variant here as the rport list
273fa519f70SHannes Reinecke 	 * is protected by the disc mutex which is already held on entry.
2740f6c6149SJoe Eykholt 	 */
275fa519f70SHannes Reinecke 	list_for_each_entry(rdata, &disc->rports, peers) {
276a407c593SHannes Reinecke 		if (!kref_get_unless_zero(&rdata->kref))
2770f6c6149SJoe Eykholt 			continue;
278a407c593SHannes Reinecke 		if (rdata->disc_id) {
2790f6c6149SJoe Eykholt 			if (rdata->disc_id == disc->disc_id)
28005d7d3b0SHannes Reinecke 				fc_rport_login(rdata);
2810f6c6149SJoe Eykholt 			else
282c96c792aSHannes Reinecke 				fc_rport_logoff(rdata);
2830f6c6149SJoe Eykholt 		}
284944ef968SHannes Reinecke 		kref_put(&rdata->kref, fc_rport_destroy);
285a407c593SHannes Reinecke 	}
2860d228c0fSAbhijeet Joglekar 	mutex_unlock(&disc->disc_mutex);
2870d228c0fSAbhijeet Joglekar 	disc->disc_callback(lport, event);
2880d228c0fSAbhijeet Joglekar 	mutex_lock(&disc->disc_mutex);
28942e9a92fSRobert Love }
29042e9a92fSRobert Love 
29142e9a92fSRobert Love /**
29234f42a07SRobert Love  * fc_disc_error() - Handle error on dNS request
2933a3b42bfSRobert Love  * @disc: The discovery context
2943a3b42bfSRobert Love  * @fp:	  The error code encoded as a frame pointer
29542e9a92fSRobert Love  */
fc_disc_error(struct fc_disc * disc,struct fc_frame * fp)29642e9a92fSRobert Love static void fc_disc_error(struct fc_disc *disc, struct fc_frame *fp)
29742e9a92fSRobert Love {
2980685230cSJoe Eykholt 	struct fc_lport *lport = fc_disc_lport(disc);
29942e9a92fSRobert Love 	unsigned long delay = 0;
3007414705eSRobert Love 
301a9e81c29SYueHaibing 	FC_DISC_DBG(disc, "Error %d, retries %d/%d\n",
302a9e81c29SYueHaibing 		    PTR_ERR_OR_ZERO(fp), disc->retry_count,
30342e9a92fSRobert Love 		    FC_DISC_RETRY_LIMIT);
30442e9a92fSRobert Love 
30542e9a92fSRobert Love 	if (!fp || PTR_ERR(fp) == -FC_EX_TIMEOUT) {
30642e9a92fSRobert Love 		/*
30742e9a92fSRobert Love 		 * Memory allocation failure, or the exchange timed out,
30842e9a92fSRobert Love 		 * retry after delay.
30942e9a92fSRobert Love 		 */
31042e9a92fSRobert Love 		if (disc->retry_count < FC_DISC_RETRY_LIMIT) {
31142e9a92fSRobert Love 			/* go ahead and retry */
31242e9a92fSRobert Love 			if (!fp)
31342e9a92fSRobert Love 				delay = msecs_to_jiffies(FC_DISC_RETRY_DELAY);
31442e9a92fSRobert Love 			else {
31542e9a92fSRobert Love 				delay = msecs_to_jiffies(lport->e_d_tov);
31642e9a92fSRobert Love 
31742e9a92fSRobert Love 				/* timeout faster first time */
31842e9a92fSRobert Love 				if (!disc->retry_count)
31942e9a92fSRobert Love 					delay /= 4;
32042e9a92fSRobert Love 			}
32142e9a92fSRobert Love 			disc->retry_count++;
32242e9a92fSRobert Love 			schedule_delayed_work(&disc->disc_work, delay);
323786681b9SJoe Eykholt 		} else
324786681b9SJoe Eykholt 			fc_disc_done(disc, DISC_EV_FAILED);
32500832084SBhanu Prakash Gollapudi 	} else if (PTR_ERR(fp) == -FC_EX_CLOSED) {
32600832084SBhanu Prakash Gollapudi 		/*
32700832084SBhanu Prakash Gollapudi 		 * if discovery fails due to lport reset, clear
32800832084SBhanu Prakash Gollapudi 		 * pending flag so that subsequent discovery can
32900832084SBhanu Prakash Gollapudi 		 * continue
33000832084SBhanu Prakash Gollapudi 		 */
33100832084SBhanu Prakash Gollapudi 		disc->pending = 0;
33242e9a92fSRobert Love 	}
33342e9a92fSRobert Love }
33442e9a92fSRobert Love 
33542e9a92fSRobert Love /**
33634f42a07SRobert Love  * fc_disc_gpn_ft_req() - Send Get Port Names by FC-4 type (GPN_FT) request
337b1987c88SLee Jones  * @disc: The discovery context
33842e9a92fSRobert Love  */
fc_disc_gpn_ft_req(struct fc_disc * disc)33942e9a92fSRobert Love static void fc_disc_gpn_ft_req(struct fc_disc *disc)
34042e9a92fSRobert Love {
34142e9a92fSRobert Love 	struct fc_frame *fp;
3420685230cSJoe Eykholt 	struct fc_lport *lport = fc_disc_lport(disc);
34342e9a92fSRobert Love 
344ee35624eSHannes Reinecke 	lockdep_assert_held(&disc->disc_mutex);
345ee35624eSHannes Reinecke 
34642e9a92fSRobert Love 	WARN_ON(!fc_lport_test_ready(lport));
34742e9a92fSRobert Love 
34842e9a92fSRobert Love 	disc->pending = 1;
34942e9a92fSRobert Love 	disc->requested = 0;
35042e9a92fSRobert Love 
35142e9a92fSRobert Love 	disc->buf_len = 0;
35242e9a92fSRobert Love 	disc->seq_count = 0;
35342e9a92fSRobert Love 	fp = fc_frame_alloc(lport,
35442e9a92fSRobert Love 			    sizeof(struct fc_ct_hdr) +
35542e9a92fSRobert Love 			    sizeof(struct fc_ns_gid_ft));
35642e9a92fSRobert Love 	if (!fp)
35742e9a92fSRobert Love 		goto err;
35842e9a92fSRobert Love 
359a46f327aSJoe Eykholt 	if (lport->tt.elsct_send(lport, 0, fp,
36042e9a92fSRobert Love 				 FC_NS_GPN_FT,
36142e9a92fSRobert Love 				 fc_disc_gpn_ft_resp,
362b94f8951SJoe Eykholt 				 disc, 3 * lport->r_a_tov))
36342e9a92fSRobert Love 		return;
36442e9a92fSRobert Love err:
3658f550f93SChris Leech 	fc_disc_error(disc, NULL);
36642e9a92fSRobert Love }
36742e9a92fSRobert Love 
36842e9a92fSRobert Love /**
369786681b9SJoe Eykholt  * fc_disc_gpn_ft_parse() - Parse the body of the dNS GPN_FT response.
370ee9ec5c9SLee Jones  * @disc:  The discovery context
3713a3b42bfSRobert Love  * @buf:   The GPN_FT response buffer
3723a3b42bfSRobert Love  * @len:   The size of response buffer
373786681b9SJoe Eykholt  *
374786681b9SJoe Eykholt  * Goes through the list of IDs and names resulting from a request.
37542e9a92fSRobert Love  */
fc_disc_gpn_ft_parse(struct fc_disc * disc,void * buf,size_t len)37642e9a92fSRobert Love static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len)
37742e9a92fSRobert Love {
37842e9a92fSRobert Love 	struct fc_lport *lport;
37942e9a92fSRobert Love 	struct fc_gpn_ft_resp *np;
38042e9a92fSRobert Love 	char *bp;
38142e9a92fSRobert Love 	size_t plen;
38242e9a92fSRobert Love 	size_t tlen;
38342e9a92fSRobert Love 	int error = 0;
384795d86f5SJoe Eykholt 	struct fc_rport_identifiers ids;
385ab28f1fdSJoe Eykholt 	struct fc_rport_priv *rdata;
38642e9a92fSRobert Love 
3870685230cSJoe Eykholt 	lport = fc_disc_lport(disc);
388a1c1e4e7SJoe Eykholt 	disc->seq_count++;
38942e9a92fSRobert Love 
39042e9a92fSRobert Love 	/*
39142e9a92fSRobert Love 	 * Handle partial name record left over from previous call.
39242e9a92fSRobert Love 	 */
39342e9a92fSRobert Love 	bp = buf;
39442e9a92fSRobert Love 	plen = len;
39542e9a92fSRobert Love 	np = (struct fc_gpn_ft_resp *)bp;
39642e9a92fSRobert Love 	tlen = disc->buf_len;
39781a67b97SJoe Eykholt 	disc->buf_len = 0;
39842e9a92fSRobert Love 	if (tlen) {
39942e9a92fSRobert Love 		WARN_ON(tlen >= sizeof(*np));
40042e9a92fSRobert Love 		plen = sizeof(*np) - tlen;
40142e9a92fSRobert Love 		WARN_ON(plen <= 0);
40242e9a92fSRobert Love 		WARN_ON(plen >= sizeof(*np));
40342e9a92fSRobert Love 		if (plen > len)
40442e9a92fSRobert Love 			plen = len;
40542e9a92fSRobert Love 		np = &disc->partial_buf;
40642e9a92fSRobert Love 		memcpy((char *)np + tlen, bp, plen);
40742e9a92fSRobert Love 
40842e9a92fSRobert Love 		/*
40942e9a92fSRobert Love 		 * Set bp so that the loop below will advance it to the
41042e9a92fSRobert Love 		 * first valid full name element.
41142e9a92fSRobert Love 		 */
41242e9a92fSRobert Love 		bp -= tlen;
41342e9a92fSRobert Love 		len += tlen;
41442e9a92fSRobert Love 		plen += tlen;
41542e9a92fSRobert Love 		disc->buf_len = (unsigned char) plen;
41642e9a92fSRobert Love 		if (plen == sizeof(*np))
41742e9a92fSRobert Love 			disc->buf_len = 0;
41842e9a92fSRobert Love 	}
41942e9a92fSRobert Love 
42042e9a92fSRobert Love 	/*
42142e9a92fSRobert Love 	 * Handle full name records, including the one filled from above.
42242e9a92fSRobert Love 	 * Normally, np == bp and plen == len, but from the partial case above,
42342e9a92fSRobert Love 	 * bp, len describe the overall buffer, and np, plen describe the
42442e9a92fSRobert Love 	 * partial buffer, which if would usually be full now.
42542e9a92fSRobert Love 	 * After the first time through the loop, things return to "normal".
42642e9a92fSRobert Love 	 */
42742e9a92fSRobert Love 	while (plen >= sizeof(*np)) {
428795d86f5SJoe Eykholt 		ids.port_id = ntoh24(np->fp_fid);
429795d86f5SJoe Eykholt 		ids.port_name = ntohll(np->fp_wwpn);
43042e9a92fSRobert Love 
4317b2787ecSRobert Love 		if (ids.port_id != lport->port_id &&
432795d86f5SJoe Eykholt 		    ids.port_name != lport->wwpn) {
4332580064bSHannes Reinecke 			rdata = fc_rport_create(lport, ids.port_id);
4349737e6a7SRobert Love 			if (rdata) {
4359737e6a7SRobert Love 				rdata->ids.port_name = ids.port_name;
4360f6c6149SJoe Eykholt 				rdata->disc_id = disc->disc_id;
4379737e6a7SRobert Love 			} else {
4387414705eSRobert Love 				printk(KERN_WARNING "libfc: Failed to allocate "
4397414705eSRobert Love 				       "memory for the newly discovered port "
440ce8b5df0SChris Leech 				       "(%6.6x)\n", ids.port_id);
44181a67b97SJoe Eykholt 				error = -ENOMEM;
44281a67b97SJoe Eykholt 			}
44342e9a92fSRobert Love 		}
44442e9a92fSRobert Love 
44542e9a92fSRobert Love 		if (np->fp_flags & FC_NS_FID_LAST) {
446786681b9SJoe Eykholt 			fc_disc_done(disc, DISC_EV_SUCCESS);
44742e9a92fSRobert Love 			len = 0;
44842e9a92fSRobert Love 			break;
44942e9a92fSRobert Love 		}
45042e9a92fSRobert Love 		len -= sizeof(*np);
45142e9a92fSRobert Love 		bp += sizeof(*np);
45242e9a92fSRobert Love 		np = (struct fc_gpn_ft_resp *)bp;
45342e9a92fSRobert Love 		plen = len;
45442e9a92fSRobert Love 	}
45542e9a92fSRobert Love 
45642e9a92fSRobert Love 	/*
45742e9a92fSRobert Love 	 * Save any partial record at the end of the buffer for next time.
45842e9a92fSRobert Love 	 */
45942e9a92fSRobert Love 	if (error == 0 && len > 0 && len < sizeof(*np)) {
46042e9a92fSRobert Love 		if (np != &disc->partial_buf) {
4617414705eSRobert Love 			FC_DISC_DBG(disc, "Partial buffer remains "
4627414705eSRobert Love 				    "for discovery\n");
46342e9a92fSRobert Love 			memcpy(&disc->partial_buf, np, len);
46442e9a92fSRobert Love 		}
46542e9a92fSRobert Love 		disc->buf_len = (unsigned char) len;
46642e9a92fSRobert Love 	}
46742e9a92fSRobert Love 	return error;
46842e9a92fSRobert Love }
46942e9a92fSRobert Love 
47034f42a07SRobert Love /**
4713a3b42bfSRobert Love  * fc_disc_timeout() - Handler for discovery timeouts
4723a3b42bfSRobert Love  * @work: Structure holding discovery context that needs to retry discovery
47342e9a92fSRobert Love  */
fc_disc_timeout(struct work_struct * work)47442e9a92fSRobert Love static void fc_disc_timeout(struct work_struct *work)
47542e9a92fSRobert Love {
47642e9a92fSRobert Love 	struct fc_disc *disc = container_of(work,
47742e9a92fSRobert Love 					    struct fc_disc,
47842e9a92fSRobert Love 					    disc_work.work);
47942e9a92fSRobert Love 	mutex_lock(&disc->disc_mutex);
48042e9a92fSRobert Love 	fc_disc_gpn_ft_req(disc);
48142e9a92fSRobert Love 	mutex_unlock(&disc->disc_mutex);
48242e9a92fSRobert Love }
48342e9a92fSRobert Love 
48442e9a92fSRobert Love /**
48534f42a07SRobert Love  * fc_disc_gpn_ft_resp() - Handle a response frame from Get Port Names (GPN_FT)
4863a3b42bfSRobert Love  * @sp:	    The sequence that the GPN_FT response was received on
4873a3b42bfSRobert Love  * @fp:	    The GPN_FT response frame
488b1987c88SLee Jones  * @disc_arg: The discovery context
48942e9a92fSRobert Love  *
4900d228c0fSAbhijeet Joglekar  * Locking Note: This function is called without disc mutex held, and
4910d228c0fSAbhijeet Joglekar  *		 should do all its processing with the mutex held
49242e9a92fSRobert Love  */
fc_disc_gpn_ft_resp(struct fc_seq * sp,struct fc_frame * fp,void * disc_arg)49342e9a92fSRobert Love static void fc_disc_gpn_ft_resp(struct fc_seq *sp, struct fc_frame *fp,
49442e9a92fSRobert Love 				void *disc_arg)
49542e9a92fSRobert Love {
49642e9a92fSRobert Love 	struct fc_disc *disc = disc_arg;
49742e9a92fSRobert Love 	struct fc_ct_hdr *cp;
49842e9a92fSRobert Love 	struct fc_frame_header *fh;
499a1c1e4e7SJoe Eykholt 	enum fc_disc_event event = DISC_EV_NONE;
50042e9a92fSRobert Love 	unsigned int seq_cnt;
50142e9a92fSRobert Love 	unsigned int len;
502a1c1e4e7SJoe Eykholt 	int error = 0;
50342e9a92fSRobert Love 
5040d228c0fSAbhijeet Joglekar 	mutex_lock(&disc->disc_mutex);
5057414705eSRobert Love 	FC_DISC_DBG(disc, "Received a GPN_FT response\n");
50642e9a92fSRobert Love 
50742e9a92fSRobert Love 	if (IS_ERR(fp)) {
50842e9a92fSRobert Love 		fc_disc_error(disc, fp);
5090d228c0fSAbhijeet Joglekar 		mutex_unlock(&disc->disc_mutex);
51042e9a92fSRobert Love 		return;
51142e9a92fSRobert Love 	}
51242e9a92fSRobert Love 
51342e9a92fSRobert Love 	WARN_ON(!fc_frame_is_linear(fp));	/* buffer must be contiguous */
51442e9a92fSRobert Love 	fh = fc_frame_header_get(fp);
51542e9a92fSRobert Love 	len = fr_len(fp) - sizeof(*fh);
51642e9a92fSRobert Love 	seq_cnt = ntohs(fh->fh_seq_cnt);
517a1c1e4e7SJoe Eykholt 	if (fr_sof(fp) == FC_SOF_I3 && seq_cnt == 0 && disc->seq_count == 0) {
51842e9a92fSRobert Love 		cp = fc_frame_payload_get(fp, sizeof(*cp));
51942e9a92fSRobert Love 		if (!cp) {
5207414705eSRobert Love 			FC_DISC_DBG(disc, "GPN_FT response too short, len %d\n",
52142e9a92fSRobert Love 				    fr_len(fp));
522883a337cSJoe Eykholt 			event = DISC_EV_FAILED;
52342e9a92fSRobert Love 		} else if (ntohs(cp->ct_cmd) == FC_FS_ACC) {
52442e9a92fSRobert Love 
52534f42a07SRobert Love 			/* Accepted, parse the response. */
52642e9a92fSRobert Love 			len -= sizeof(*cp);
527a1c1e4e7SJoe Eykholt 			error = fc_disc_gpn_ft_parse(disc, cp + 1, len);
52842e9a92fSRobert Love 		} else if (ntohs(cp->ct_cmd) == FC_FS_RJT) {
5297414705eSRobert Love 			FC_DISC_DBG(disc, "GPN_FT rejected reason %x exp %x "
53042e9a92fSRobert Love 				    "(check zoning)\n", cp->ct_reason,
53142e9a92fSRobert Love 				    cp->ct_explan);
532a1c1e4e7SJoe Eykholt 			event = DISC_EV_FAILED;
533c762608bSJoe Eykholt 			if (cp->ct_reason == FC_FS_RJT_UNABL &&
534c762608bSJoe Eykholt 			    cp->ct_explan == FC_FS_EXP_FTNR)
535c762608bSJoe Eykholt 				event = DISC_EV_SUCCESS;
53642e9a92fSRobert Love 		} else {
5377414705eSRobert Love 			FC_DISC_DBG(disc, "GPN_FT unexpected response code "
5387414705eSRobert Love 				    "%x\n", ntohs(cp->ct_cmd));
539883a337cSJoe Eykholt 			event = DISC_EV_FAILED;
54042e9a92fSRobert Love 		}
541a1c1e4e7SJoe Eykholt 	} else if (fr_sof(fp) == FC_SOF_N3 && seq_cnt == disc->seq_count) {
542a1c1e4e7SJoe Eykholt 		error = fc_disc_gpn_ft_parse(disc, fh + 1, len);
54342e9a92fSRobert Love 	} else {
5447414705eSRobert Love 		FC_DISC_DBG(disc, "GPN_FT unexpected frame - out of sequence? "
54542e9a92fSRobert Love 			    "seq_cnt %x expected %x sof %x eof %x\n",
54642e9a92fSRobert Love 			    seq_cnt, disc->seq_count, fr_sof(fp), fr_eof(fp));
547883a337cSJoe Eykholt 		event = DISC_EV_FAILED;
54842e9a92fSRobert Love 	}
54942e9a92fSRobert Love 	if (error)
5506f37e210SDan Carpenter 		fc_disc_error(disc, ERR_PTR(error));
551a1c1e4e7SJoe Eykholt 	else if (event != DISC_EV_NONE)
552a1c1e4e7SJoe Eykholt 		fc_disc_done(disc, event);
55342e9a92fSRobert Love 	fc_frame_free(fp);
5540d228c0fSAbhijeet Joglekar 	mutex_unlock(&disc->disc_mutex);
55542e9a92fSRobert Love }
55642e9a92fSRobert Love 
55742e9a92fSRobert Love /**
5582ab7e1ecSJoe Eykholt  * fc_disc_gpn_id_resp() - Handle a response frame from Get Port Names (GPN_ID)
5593a3b42bfSRobert Love  * @sp:	       The sequence the GPN_ID is on
5603a3b42bfSRobert Love  * @fp:	       The response frame
5613a3b42bfSRobert Love  * @rdata_arg: The remote port that sent the GPN_ID response
5622ab7e1ecSJoe Eykholt  *
5632ab7e1ecSJoe Eykholt  * Locking Note: This function is called without disc mutex held.
5642ab7e1ecSJoe Eykholt  */
fc_disc_gpn_id_resp(struct fc_seq * sp,struct fc_frame * fp,void * rdata_arg)5652ab7e1ecSJoe Eykholt static void fc_disc_gpn_id_resp(struct fc_seq *sp, struct fc_frame *fp,
5662ab7e1ecSJoe Eykholt 				void *rdata_arg)
5672ab7e1ecSJoe Eykholt {
5682ab7e1ecSJoe Eykholt 	struct fc_rport_priv *rdata = rdata_arg;
5692ab7e1ecSJoe Eykholt 	struct fc_rport_priv *new_rdata;
5702ab7e1ecSJoe Eykholt 	struct fc_lport *lport;
5712ab7e1ecSJoe Eykholt 	struct fc_disc *disc;
5722ab7e1ecSJoe Eykholt 	struct fc_ct_hdr *cp;
5732ab7e1ecSJoe Eykholt 	struct fc_ns_gid_pn *pn;
5742ab7e1ecSJoe Eykholt 	u64 port_name;
5752ab7e1ecSJoe Eykholt 
5762ab7e1ecSJoe Eykholt 	lport = rdata->local_port;
5772ab7e1ecSJoe Eykholt 	disc = &lport->disc;
5782ab7e1ecSJoe Eykholt 
5792ab7e1ecSJoe Eykholt 	if (PTR_ERR(fp) == -FC_EX_CLOSED)
5802ab7e1ecSJoe Eykholt 		goto out;
581ec007ef4SJaved Hasan 	if (IS_ERR(fp)) {
582ec007ef4SJaved Hasan 		mutex_lock(&disc->disc_mutex);
583ec007ef4SJaved Hasan 		fc_disc_restart(disc);
584ec007ef4SJaved Hasan 		mutex_unlock(&disc->disc_mutex);
585ec007ef4SJaved Hasan 		goto out;
586ec007ef4SJaved Hasan 	}
5872ab7e1ecSJoe Eykholt 
5882ab7e1ecSJoe Eykholt 	cp = fc_frame_payload_get(fp, sizeof(*cp));
5892ab7e1ecSJoe Eykholt 	if (!cp)
5902ab7e1ecSJoe Eykholt 		goto redisc;
5912ab7e1ecSJoe Eykholt 	if (ntohs(cp->ct_cmd) == FC_FS_ACC) {
5922ab7e1ecSJoe Eykholt 		if (fr_len(fp) < sizeof(struct fc_frame_header) +
5932ab7e1ecSJoe Eykholt 		    sizeof(*cp) + sizeof(*pn))
5942ab7e1ecSJoe Eykholt 			goto redisc;
5952ab7e1ecSJoe Eykholt 		pn = (struct fc_ns_gid_pn *)(cp + 1);
5962ab7e1ecSJoe Eykholt 		port_name = get_unaligned_be64(&pn->fn_wwpn);
597a407c593SHannes Reinecke 		mutex_lock(&rdata->rp_mutex);
5982ab7e1ecSJoe Eykholt 		if (rdata->ids.port_name == -1)
5992ab7e1ecSJoe Eykholt 			rdata->ids.port_name = port_name;
6002ab7e1ecSJoe Eykholt 		else if (rdata->ids.port_name != port_name) {
6012ab7e1ecSJoe Eykholt 			FC_DISC_DBG(disc, "GPN_ID accepted.  WWPN changed. "
6029f8f3aa6SChris Leech 				    "Port-id %6.6x wwpn %16.16llx\n",
6032ab7e1ecSJoe Eykholt 				    rdata->ids.port_id, port_name);
604a407c593SHannes Reinecke 			mutex_unlock(&rdata->rp_mutex);
605c96c792aSHannes Reinecke 			fc_rport_logoff(rdata);
606a407c593SHannes Reinecke 			mutex_lock(&lport->disc.disc_mutex);
6072580064bSHannes Reinecke 			new_rdata = fc_rport_create(lport, rdata->ids.port_id);
608a407c593SHannes Reinecke 			mutex_unlock(&lport->disc.disc_mutex);
6092ab7e1ecSJoe Eykholt 			if (new_rdata) {
6102ab7e1ecSJoe Eykholt 				new_rdata->disc_id = disc->disc_id;
61105d7d3b0SHannes Reinecke 				fc_rport_login(new_rdata);
6122ab7e1ecSJoe Eykholt 			}
613ec007ef4SJaved Hasan 			goto free_fp;
6142ab7e1ecSJoe Eykholt 		}
6152ab7e1ecSJoe Eykholt 		rdata->disc_id = disc->disc_id;
616a407c593SHannes Reinecke 		mutex_unlock(&rdata->rp_mutex);
61705d7d3b0SHannes Reinecke 		fc_rport_login(rdata);
6182ab7e1ecSJoe Eykholt 	} else if (ntohs(cp->ct_cmd) == FC_FS_RJT) {
6192ab7e1ecSJoe Eykholt 		FC_DISC_DBG(disc, "GPN_ID rejected reason %x exp %x\n",
6202ab7e1ecSJoe Eykholt 			    cp->ct_reason, cp->ct_explan);
621c96c792aSHannes Reinecke 		fc_rport_logoff(rdata);
6222ab7e1ecSJoe Eykholt 	} else {
6232ab7e1ecSJoe Eykholt 		FC_DISC_DBG(disc, "GPN_ID unexpected response code %x\n",
6242ab7e1ecSJoe Eykholt 			    ntohs(cp->ct_cmd));
6252ab7e1ecSJoe Eykholt redisc:
626a407c593SHannes Reinecke 		mutex_lock(&disc->disc_mutex);
6272ab7e1ecSJoe Eykholt 		fc_disc_restart(disc);
628a407c593SHannes Reinecke 		mutex_unlock(&disc->disc_mutex);
6292ab7e1ecSJoe Eykholt 	}
630ec007ef4SJaved Hasan free_fp:
631ec007ef4SJaved Hasan 	fc_frame_free(fp);
6322ab7e1ecSJoe Eykholt out:
633944ef968SHannes Reinecke 	kref_put(&rdata->kref, fc_rport_destroy);
6342ab7e1ecSJoe Eykholt }
6352ab7e1ecSJoe Eykholt 
6362ab7e1ecSJoe Eykholt /**
6372ab7e1ecSJoe Eykholt  * fc_disc_gpn_id_req() - Send Get Port Names by ID (GPN_ID) request
6383a3b42bfSRobert Love  * @lport: The local port to initiate discovery on
6392ab7e1ecSJoe Eykholt  * @rdata: remote port private data
6402ab7e1ecSJoe Eykholt  *
6412ab7e1ecSJoe Eykholt  * On failure, an error code is returned.
6422ab7e1ecSJoe Eykholt  */
fc_disc_gpn_id_req(struct fc_lport * lport,struct fc_rport_priv * rdata)6432ab7e1ecSJoe Eykholt static int fc_disc_gpn_id_req(struct fc_lport *lport,
6442ab7e1ecSJoe Eykholt 			      struct fc_rport_priv *rdata)
6452ab7e1ecSJoe Eykholt {
6462ab7e1ecSJoe Eykholt 	struct fc_frame *fp;
6472ab7e1ecSJoe Eykholt 
648ee35624eSHannes Reinecke 	lockdep_assert_held(&lport->disc.disc_mutex);
6492ab7e1ecSJoe Eykholt 	fp = fc_frame_alloc(lport, sizeof(struct fc_ct_hdr) +
6502ab7e1ecSJoe Eykholt 			    sizeof(struct fc_ns_fid));
6512ab7e1ecSJoe Eykholt 	if (!fp)
6522ab7e1ecSJoe Eykholt 		return -ENOMEM;
6532ab7e1ecSJoe Eykholt 	if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, FC_NS_GPN_ID,
654b94f8951SJoe Eykholt 				  fc_disc_gpn_id_resp, rdata,
655b94f8951SJoe Eykholt 				  3 * lport->r_a_tov))
6562ab7e1ecSJoe Eykholt 		return -ENOMEM;
6572ab7e1ecSJoe Eykholt 	kref_get(&rdata->kref);
6582ab7e1ecSJoe Eykholt 	return 0;
6592ab7e1ecSJoe Eykholt }
6602ab7e1ecSJoe Eykholt 
6612ab7e1ecSJoe Eykholt /**
66234f42a07SRobert Love  * fc_disc_single() - Discover the directory information for a single target
6633a3b42bfSRobert Love  * @lport: The local port the remote port is associated with
66442e9a92fSRobert Love  * @dp:	   The port to rediscover
66542e9a92fSRobert Love  */
fc_disc_single(struct fc_lport * lport,struct fc_disc_port * dp)6662ab7e1ecSJoe Eykholt static int fc_disc_single(struct fc_lport *lport, struct fc_disc_port *dp)
66742e9a92fSRobert Love {
668ab28f1fdSJoe Eykholt 	struct fc_rport_priv *rdata;
66942e9a92fSRobert Love 
670ee35624eSHannes Reinecke 	lockdep_assert_held(&lport->disc.disc_mutex);
671ee35624eSHannes Reinecke 
6722580064bSHannes Reinecke 	rdata = fc_rport_create(lport, dp->port_id);
6732ab7e1ecSJoe Eykholt 	if (!rdata)
6742ab7e1ecSJoe Eykholt 		return -ENOMEM;
6752ab7e1ecSJoe Eykholt 	rdata->disc_id = 0;
6762ab7e1ecSJoe Eykholt 	return fc_disc_gpn_id_req(lport, rdata);
67742e9a92fSRobert Love }
67842e9a92fSRobert Love 
67942e9a92fSRobert Love /**
68034f42a07SRobert Love  * fc_disc_stop() - Stop discovery for a given lport
6813a3b42bfSRobert Love  * @lport: The local port that discovery should stop on
68242e9a92fSRobert Love  */
fc_disc_stop(struct fc_lport * lport)683c6b21c93SBart Van Assche static void fc_disc_stop(struct fc_lport *lport)
68442e9a92fSRobert Love {
68542e9a92fSRobert Love 	struct fc_disc *disc = &lport->disc;
68642e9a92fSRobert Love 
687c531b9b4SBhanu Prakash Gollapudi 	if (disc->pending)
68842e9a92fSRobert Love 		cancel_delayed_work_sync(&disc->disc_work);
689bc3d12b7SHannes Reinecke 	mutex_lock(&disc->disc_mutex);
69042e9a92fSRobert Love 	fc_disc_stop_rports(disc);
691bc3d12b7SHannes Reinecke 	mutex_unlock(&disc->disc_mutex);
69242e9a92fSRobert Love }
69342e9a92fSRobert Love 
69442e9a92fSRobert Love /**
69534f42a07SRobert Love  * fc_disc_stop_final() - Stop discovery for a given lport
6963a3b42bfSRobert Love  * @lport: The lport that discovery should stop on
69742e9a92fSRobert Love  *
69842e9a92fSRobert Love  * This function will block until discovery has been
69942e9a92fSRobert Love  * completely stopped and all rports have been deleted.
70042e9a92fSRobert Love  */
fc_disc_stop_final(struct fc_lport * lport)701c6b21c93SBart Van Assche static void fc_disc_stop_final(struct fc_lport *lport)
70242e9a92fSRobert Love {
70342e9a92fSRobert Love 	fc_disc_stop(lport);
7045922a957SHannes Reinecke 	fc_rport_flush_queue();
70542e9a92fSRobert Love }
70642e9a92fSRobert Love 
70742e9a92fSRobert Love /**
7080807619dSRobert Love  * fc_disc_config() - Configure the discovery layer for a local port
7090807619dSRobert Love  * @lport: The local port that needs the discovery layer to be configured
7108a9a7138SRobert Love  * @priv: Private data structre for users of the discovery layer
71142e9a92fSRobert Love  */
fc_disc_config(struct fc_lport * lport,void * priv)7120807619dSRobert Love void fc_disc_config(struct fc_lport *lport, void *priv)
71342e9a92fSRobert Love {
714bc2e1299SColin Ian King 	struct fc_disc *disc;
71542e9a92fSRobert Love 
71642e9a92fSRobert Love 	if (!lport->tt.disc_start)
71742e9a92fSRobert Love 		lport->tt.disc_start = fc_disc_start;
71842e9a92fSRobert Love 
71942e9a92fSRobert Love 	if (!lport->tt.disc_stop)
72042e9a92fSRobert Love 		lport->tt.disc_stop = fc_disc_stop;
72142e9a92fSRobert Love 
72242e9a92fSRobert Love 	if (!lport->tt.disc_stop_final)
72342e9a92fSRobert Love 		lport->tt.disc_stop_final = fc_disc_stop_final;
72442e9a92fSRobert Love 
72542e9a92fSRobert Love 	if (!lport->tt.disc_recv_req)
72642e9a92fSRobert Love 		lport->tt.disc_recv_req = fc_disc_recv_req;
72742e9a92fSRobert Love 
72842e9a92fSRobert Love 	disc = &lport->disc;
7290807619dSRobert Love 
7300807619dSRobert Love 	disc->priv = priv;
7310807619dSRobert Love }
7320807619dSRobert Love EXPORT_SYMBOL(fc_disc_config);
7330807619dSRobert Love 
7340807619dSRobert Love /**
7350807619dSRobert Love  * fc_disc_init() - Initialize the discovery layer for a local port
7360807619dSRobert Love  * @lport: The local port that needs the discovery layer to be initialized
7370807619dSRobert Love  */
fc_disc_init(struct fc_lport * lport)7380807619dSRobert Love void fc_disc_init(struct fc_lport *lport)
7390807619dSRobert Love {
7400807619dSRobert Love 	struct fc_disc *disc = &lport->disc;
7410807619dSRobert Love 
74242e9a92fSRobert Love 	INIT_DELAYED_WORK(&disc->disc_work, fc_disc_timeout);
74342e9a92fSRobert Love 	mutex_init(&disc->disc_mutex);
74442e9a92fSRobert Love 	INIT_LIST_HEAD(&disc->rports);
74542e9a92fSRobert Love }
74642e9a92fSRobert Love EXPORT_SYMBOL(fc_disc_init);
747