cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

fc_disc.c (20204B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright(c) 2007 - 2008 Intel Corporation. All rights reserved.
      4 *
      5 * Maintained at www.Open-FCoE.org
      6 */
      7
      8/*
      9 * Target Discovery
     10 *
     11 * This block discovers all FC-4 remote ports, including FCP initiators. It
     12 * also handles RSCN events and re-discovery if necessary.
     13 */
     14
     15/*
     16 * DISC LOCKING
     17 *
     18 * The disc mutex is can be locked when acquiring rport locks, but may not
     19 * be held when acquiring the lport lock. Refer to fc_lport.c for more
     20 * details.
     21 */
     22
     23#include <linux/timer.h>
     24#include <linux/slab.h>
     25#include <linux/err.h>
     26#include <linux/export.h>
     27#include <linux/rculist.h>
     28
     29#include <asm/unaligned.h>
     30
     31#include <scsi/fc/fc_gs.h>
     32
     33#include <scsi/libfc.h>
     34
     35#include "fc_libfc.h"
     36
     37#define FC_DISC_RETRY_LIMIT	3	/* max retries */
     38#define FC_DISC_RETRY_DELAY	500UL	/* (msecs) delay */
     39
     40static void fc_disc_gpn_ft_req(struct fc_disc *);
     41static void fc_disc_gpn_ft_resp(struct fc_seq *, struct fc_frame *, void *);
     42static void fc_disc_done(struct fc_disc *, enum fc_disc_event);
     43static void fc_disc_timeout(struct work_struct *);
     44static int fc_disc_single(struct fc_lport *, struct fc_disc_port *);
     45static void fc_disc_restart(struct fc_disc *);
     46
     47/**
     48 * fc_disc_stop_rports() - Delete all the remote ports associated with the lport
     49 * @disc: The discovery job to stop remote ports on
     50 */
     51static void fc_disc_stop_rports(struct fc_disc *disc)
     52{
     53	struct fc_rport_priv *rdata;
     54
     55	lockdep_assert_held(&disc->disc_mutex);
     56
     57	list_for_each_entry(rdata, &disc->rports, peers) {
     58		if (kref_get_unless_zero(&rdata->kref)) {
     59			fc_rport_logoff(rdata);
     60			kref_put(&rdata->kref, fc_rport_destroy);
     61		}
     62	}
     63}
     64
     65/**
     66 * fc_disc_recv_rscn_req() - Handle Registered State Change Notification (RSCN)
     67 * @disc:  The discovery object to which the RSCN applies
     68 * @fp:	   The RSCN frame
     69 */
     70static void fc_disc_recv_rscn_req(struct fc_disc *disc, struct fc_frame *fp)
     71{
     72	struct fc_lport *lport;
     73	struct fc_els_rscn *rp;
     74	struct fc_els_rscn_page *pp;
     75	struct fc_seq_els_data rjt_data;
     76	unsigned int len;
     77	int redisc = 0;
     78	enum fc_els_rscn_ev_qual ev_qual;
     79	enum fc_els_rscn_addr_fmt fmt;
     80	LIST_HEAD(disc_ports);
     81	struct fc_disc_port *dp, *next;
     82
     83	lockdep_assert_held(&disc->disc_mutex);
     84
     85	lport = fc_disc_lport(disc);
     86
     87	FC_DISC_DBG(disc, "Received an RSCN event\n");
     88
     89	/* make sure the frame contains an RSCN message */
     90	rp = fc_frame_payload_get(fp, sizeof(*rp));
     91	if (!rp)
     92		goto reject;
     93	/* make sure the page length is as expected (4 bytes) */
     94	if (rp->rscn_page_len != sizeof(*pp))
     95		goto reject;
     96	/* get the RSCN payload length */
     97	len = ntohs(rp->rscn_plen);
     98	if (len < sizeof(*rp))
     99		goto reject;
    100	/* make sure the frame contains the expected payload */
    101	rp = fc_frame_payload_get(fp, len);
    102	if (!rp)
    103		goto reject;
    104	/* payload must be a multiple of the RSCN page size */
    105	len -= sizeof(*rp);
    106	if (len % sizeof(*pp))
    107		goto reject;
    108
    109	for (pp = (void *)(rp + 1); len > 0; len -= sizeof(*pp), pp++) {
    110		ev_qual = pp->rscn_page_flags >> ELS_RSCN_EV_QUAL_BIT;
    111		ev_qual &= ELS_RSCN_EV_QUAL_MASK;
    112		fmt = pp->rscn_page_flags >> ELS_RSCN_ADDR_FMT_BIT;
    113		fmt &= ELS_RSCN_ADDR_FMT_MASK;
    114		/*
    115		 * if we get an address format other than port
    116		 * (area, domain, fabric), then do a full discovery
    117		 */
    118		switch (fmt) {
    119		case ELS_ADDR_FMT_PORT:
    120			FC_DISC_DBG(disc, "Port address format for port "
    121				    "(%6.6x)\n", ntoh24(pp->rscn_fid));
    122			dp = kzalloc(sizeof(*dp), GFP_KERNEL);
    123			if (!dp) {
    124				redisc = 1;
    125				break;
    126			}
    127			dp->lp = lport;
    128			dp->port_id = ntoh24(pp->rscn_fid);
    129			list_add_tail(&dp->peers, &disc_ports);
    130			break;
    131		case ELS_ADDR_FMT_AREA:
    132		case ELS_ADDR_FMT_DOM:
    133		case ELS_ADDR_FMT_FAB:
    134		default:
    135			FC_DISC_DBG(disc, "Address format is (%d)\n", fmt);
    136			redisc = 1;
    137			break;
    138		}
    139	}
    140	fc_seq_els_rsp_send(fp, ELS_LS_ACC, NULL);
    141
    142	/*
    143	 * If not doing a complete rediscovery, do GPN_ID on
    144	 * the individual ports mentioned in the list.
    145	 * If any of these get an error, do a full rediscovery.
    146	 * In any case, go through the list and free the entries.
    147	 */
    148	list_for_each_entry_safe(dp, next, &disc_ports, peers) {
    149		list_del(&dp->peers);
    150		if (!redisc)
    151			redisc = fc_disc_single(lport, dp);
    152		kfree(dp);
    153	}
    154	if (redisc) {
    155		FC_DISC_DBG(disc, "RSCN received: rediscovering\n");
    156		fc_disc_restart(disc);
    157	} else {
    158		FC_DISC_DBG(disc, "RSCN received: not rediscovering. "
    159			    "redisc %d state %d in_prog %d\n",
    160			    redisc, lport->state, disc->pending);
    161	}
    162	fc_frame_free(fp);
    163	return;
    164reject:
    165	FC_DISC_DBG(disc, "Received a bad RSCN frame\n");
    166	rjt_data.reason = ELS_RJT_LOGIC;
    167	rjt_data.explan = ELS_EXPL_NONE;
    168	fc_seq_els_rsp_send(fp, ELS_LS_RJT, &rjt_data);
    169	fc_frame_free(fp);
    170}
    171
    172/**
    173 * fc_disc_recv_req() - Handle incoming requests
    174 * @lport: The local port receiving the request
    175 * @fp:	   The request frame
    176 *
    177 * Locking Note: This function is called from the EM and will lock
    178 *		 the disc_mutex before calling the handler for the
    179 *		 request.
    180 */
    181static void fc_disc_recv_req(struct fc_lport *lport, struct fc_frame *fp)
    182{
    183	u8 op;
    184	struct fc_disc *disc = &lport->disc;
    185
    186	op = fc_frame_payload_op(fp);
    187	switch (op) {
    188	case ELS_RSCN:
    189		mutex_lock(&disc->disc_mutex);
    190		fc_disc_recv_rscn_req(disc, fp);
    191		mutex_unlock(&disc->disc_mutex);
    192		break;
    193	default:
    194		FC_DISC_DBG(disc, "Received an unsupported request, "
    195			    "the opcode is (%x)\n", op);
    196		fc_frame_free(fp);
    197		break;
    198	}
    199}
    200
    201/**
    202 * fc_disc_restart() - Restart discovery
    203 * @disc: The discovery object to be restarted
    204 */
    205static void fc_disc_restart(struct fc_disc *disc)
    206{
    207	lockdep_assert_held(&disc->disc_mutex);
    208
    209	if (!disc->disc_callback)
    210		return;
    211
    212	FC_DISC_DBG(disc, "Restarting discovery\n");
    213
    214	disc->requested = 1;
    215	if (disc->pending)
    216		return;
    217
    218	/*
    219	 * Advance disc_id.  This is an arbitrary non-zero number that will
    220	 * match the value in the fc_rport_priv after discovery for all
    221	 * freshly-discovered remote ports.  Avoid wrapping to zero.
    222	 */
    223	disc->disc_id = (disc->disc_id + 2) | 1;
    224	disc->retry_count = 0;
    225	fc_disc_gpn_ft_req(disc);
    226}
    227
    228/**
    229 * fc_disc_start() - Start discovery on a local port
    230 * @lport:	   The local port to have discovery started on
    231 * @disc_callback: Callback function to be called when discovery is complete
    232 */
    233static void fc_disc_start(void (*disc_callback)(struct fc_lport *,
    234						enum fc_disc_event),
    235			  struct fc_lport *lport)
    236{
    237	struct fc_disc *disc = &lport->disc;
    238
    239	/*
    240	 * At this point we may have a new disc job or an existing
    241	 * one. Either way, let's lock when we make changes to it
    242	 * and send the GPN_FT request.
    243	 */
    244	mutex_lock(&disc->disc_mutex);
    245	disc->disc_callback = disc_callback;
    246	fc_disc_restart(disc);
    247	mutex_unlock(&disc->disc_mutex);
    248}
    249
    250/**
    251 * fc_disc_done() - Discovery has been completed
    252 * @disc:  The discovery context
    253 * @event: The discovery completion status
    254 */
    255static void fc_disc_done(struct fc_disc *disc, enum fc_disc_event event)
    256{
    257	struct fc_lport *lport = fc_disc_lport(disc);
    258	struct fc_rport_priv *rdata;
    259
    260	lockdep_assert_held(&disc->disc_mutex);
    261	FC_DISC_DBG(disc, "Discovery complete\n");
    262
    263	disc->pending = 0;
    264	if (disc->requested) {
    265		fc_disc_restart(disc);
    266		return;
    267	}
    268
    269	/*
    270	 * Go through all remote ports.	 If they were found in the latest
    271	 * discovery, reverify or log them in.	Otherwise, log them out.
    272	 * Skip ports which were never discovered.  These are the dNS port
    273	 * and ports which were created by PLOGI.
    274	 *
    275	 * We don't need to use the _rcu variant here as the rport list
    276	 * is protected by the disc mutex which is already held on entry.
    277	 */
    278	list_for_each_entry(rdata, &disc->rports, peers) {
    279		if (!kref_get_unless_zero(&rdata->kref))
    280			continue;
    281		if (rdata->disc_id) {
    282			if (rdata->disc_id == disc->disc_id)
    283				fc_rport_login(rdata);
    284			else
    285				fc_rport_logoff(rdata);
    286		}
    287		kref_put(&rdata->kref, fc_rport_destroy);
    288	}
    289	mutex_unlock(&disc->disc_mutex);
    290	disc->disc_callback(lport, event);
    291	mutex_lock(&disc->disc_mutex);
    292}
    293
    294/**
    295 * fc_disc_error() - Handle error on dNS request
    296 * @disc: The discovery context
    297 * @fp:	  The error code encoded as a frame pointer
    298 */
    299static void fc_disc_error(struct fc_disc *disc, struct fc_frame *fp)
    300{
    301	struct fc_lport *lport = fc_disc_lport(disc);
    302	unsigned long delay = 0;
    303
    304	FC_DISC_DBG(disc, "Error %d, retries %d/%d\n",
    305		    PTR_ERR_OR_ZERO(fp), disc->retry_count,
    306		    FC_DISC_RETRY_LIMIT);
    307
    308	if (!fp || PTR_ERR(fp) == -FC_EX_TIMEOUT) {
    309		/*
    310		 * Memory allocation failure, or the exchange timed out,
    311		 * retry after delay.
    312		 */
    313		if (disc->retry_count < FC_DISC_RETRY_LIMIT) {
    314			/* go ahead and retry */
    315			if (!fp)
    316				delay = msecs_to_jiffies(FC_DISC_RETRY_DELAY);
    317			else {
    318				delay = msecs_to_jiffies(lport->e_d_tov);
    319
    320				/* timeout faster first time */
    321				if (!disc->retry_count)
    322					delay /= 4;
    323			}
    324			disc->retry_count++;
    325			schedule_delayed_work(&disc->disc_work, delay);
    326		} else
    327			fc_disc_done(disc, DISC_EV_FAILED);
    328	} else if (PTR_ERR(fp) == -FC_EX_CLOSED) {
    329		/*
    330		 * if discovery fails due to lport reset, clear
    331		 * pending flag so that subsequent discovery can
    332		 * continue
    333		 */
    334		disc->pending = 0;
    335	}
    336}
    337
    338/**
    339 * fc_disc_gpn_ft_req() - Send Get Port Names by FC-4 type (GPN_FT) request
    340 * @disc: The discovery context
    341 */
    342static void fc_disc_gpn_ft_req(struct fc_disc *disc)
    343{
    344	struct fc_frame *fp;
    345	struct fc_lport *lport = fc_disc_lport(disc);
    346
    347	lockdep_assert_held(&disc->disc_mutex);
    348
    349	WARN_ON(!fc_lport_test_ready(lport));
    350
    351	disc->pending = 1;
    352	disc->requested = 0;
    353
    354	disc->buf_len = 0;
    355	disc->seq_count = 0;
    356	fp = fc_frame_alloc(lport,
    357			    sizeof(struct fc_ct_hdr) +
    358			    sizeof(struct fc_ns_gid_ft));
    359	if (!fp)
    360		goto err;
    361
    362	if (lport->tt.elsct_send(lport, 0, fp,
    363				 FC_NS_GPN_FT,
    364				 fc_disc_gpn_ft_resp,
    365				 disc, 3 * lport->r_a_tov))
    366		return;
    367err:
    368	fc_disc_error(disc, NULL);
    369}
    370
    371/**
    372 * fc_disc_gpn_ft_parse() - Parse the body of the dNS GPN_FT response.
    373 * @disc:  The discovery context
    374 * @buf:   The GPN_FT response buffer
    375 * @len:   The size of response buffer
    376 *
    377 * Goes through the list of IDs and names resulting from a request.
    378 */
    379static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len)
    380{
    381	struct fc_lport *lport;
    382	struct fc_gpn_ft_resp *np;
    383	char *bp;
    384	size_t plen;
    385	size_t tlen;
    386	int error = 0;
    387	struct fc_rport_identifiers ids;
    388	struct fc_rport_priv *rdata;
    389
    390	lport = fc_disc_lport(disc);
    391	disc->seq_count++;
    392
    393	/*
    394	 * Handle partial name record left over from previous call.
    395	 */
    396	bp = buf;
    397	plen = len;
    398	np = (struct fc_gpn_ft_resp *)bp;
    399	tlen = disc->buf_len;
    400	disc->buf_len = 0;
    401	if (tlen) {
    402		WARN_ON(tlen >= sizeof(*np));
    403		plen = sizeof(*np) - tlen;
    404		WARN_ON(plen <= 0);
    405		WARN_ON(plen >= sizeof(*np));
    406		if (plen > len)
    407			plen = len;
    408		np = &disc->partial_buf;
    409		memcpy((char *)np + tlen, bp, plen);
    410
    411		/*
    412		 * Set bp so that the loop below will advance it to the
    413		 * first valid full name element.
    414		 */
    415		bp -= tlen;
    416		len += tlen;
    417		plen += tlen;
    418		disc->buf_len = (unsigned char) plen;
    419		if (plen == sizeof(*np))
    420			disc->buf_len = 0;
    421	}
    422
    423	/*
    424	 * Handle full name records, including the one filled from above.
    425	 * Normally, np == bp and plen == len, but from the partial case above,
    426	 * bp, len describe the overall buffer, and np, plen describe the
    427	 * partial buffer, which if would usually be full now.
    428	 * After the first time through the loop, things return to "normal".
    429	 */
    430	while (plen >= sizeof(*np)) {
    431		ids.port_id = ntoh24(np->fp_fid);
    432		ids.port_name = ntohll(np->fp_wwpn);
    433
    434		if (ids.port_id != lport->port_id &&
    435		    ids.port_name != lport->wwpn) {
    436			rdata = fc_rport_create(lport, ids.port_id);
    437			if (rdata) {
    438				rdata->ids.port_name = ids.port_name;
    439				rdata->disc_id = disc->disc_id;
    440			} else {
    441				printk(KERN_WARNING "libfc: Failed to allocate "
    442				       "memory for the newly discovered port "
    443				       "(%6.6x)\n", ids.port_id);
    444				error = -ENOMEM;
    445			}
    446		}
    447
    448		if (np->fp_flags & FC_NS_FID_LAST) {
    449			fc_disc_done(disc, DISC_EV_SUCCESS);
    450			len = 0;
    451			break;
    452		}
    453		len -= sizeof(*np);
    454		bp += sizeof(*np);
    455		np = (struct fc_gpn_ft_resp *)bp;
    456		plen = len;
    457	}
    458
    459	/*
    460	 * Save any partial record at the end of the buffer for next time.
    461	 */
    462	if (error == 0 && len > 0 && len < sizeof(*np)) {
    463		if (np != &disc->partial_buf) {
    464			FC_DISC_DBG(disc, "Partial buffer remains "
    465				    "for discovery\n");
    466			memcpy(&disc->partial_buf, np, len);
    467		}
    468		disc->buf_len = (unsigned char) len;
    469	}
    470	return error;
    471}
    472
    473/**
    474 * fc_disc_timeout() - Handler for discovery timeouts
    475 * @work: Structure holding discovery context that needs to retry discovery
    476 */
    477static void fc_disc_timeout(struct work_struct *work)
    478{
    479	struct fc_disc *disc = container_of(work,
    480					    struct fc_disc,
    481					    disc_work.work);
    482	mutex_lock(&disc->disc_mutex);
    483	fc_disc_gpn_ft_req(disc);
    484	mutex_unlock(&disc->disc_mutex);
    485}
    486
    487/**
    488 * fc_disc_gpn_ft_resp() - Handle a response frame from Get Port Names (GPN_FT)
    489 * @sp:	    The sequence that the GPN_FT response was received on
    490 * @fp:	    The GPN_FT response frame
    491 * @disc_arg: The discovery context
    492 *
    493 * Locking Note: This function is called without disc mutex held, and
    494 *		 should do all its processing with the mutex held
    495 */
    496static void fc_disc_gpn_ft_resp(struct fc_seq *sp, struct fc_frame *fp,
    497				void *disc_arg)
    498{
    499	struct fc_disc *disc = disc_arg;
    500	struct fc_ct_hdr *cp;
    501	struct fc_frame_header *fh;
    502	enum fc_disc_event event = DISC_EV_NONE;
    503	unsigned int seq_cnt;
    504	unsigned int len;
    505	int error = 0;
    506
    507	mutex_lock(&disc->disc_mutex);
    508	FC_DISC_DBG(disc, "Received a GPN_FT response\n");
    509
    510	if (IS_ERR(fp)) {
    511		fc_disc_error(disc, fp);
    512		mutex_unlock(&disc->disc_mutex);
    513		return;
    514	}
    515
    516	WARN_ON(!fc_frame_is_linear(fp));	/* buffer must be contiguous */
    517	fh = fc_frame_header_get(fp);
    518	len = fr_len(fp) - sizeof(*fh);
    519	seq_cnt = ntohs(fh->fh_seq_cnt);
    520	if (fr_sof(fp) == FC_SOF_I3 && seq_cnt == 0 && disc->seq_count == 0) {
    521		cp = fc_frame_payload_get(fp, sizeof(*cp));
    522		if (!cp) {
    523			FC_DISC_DBG(disc, "GPN_FT response too short, len %d\n",
    524				    fr_len(fp));
    525			event = DISC_EV_FAILED;
    526		} else if (ntohs(cp->ct_cmd) == FC_FS_ACC) {
    527
    528			/* Accepted, parse the response. */
    529			len -= sizeof(*cp);
    530			error = fc_disc_gpn_ft_parse(disc, cp + 1, len);
    531		} else if (ntohs(cp->ct_cmd) == FC_FS_RJT) {
    532			FC_DISC_DBG(disc, "GPN_FT rejected reason %x exp %x "
    533				    "(check zoning)\n", cp->ct_reason,
    534				    cp->ct_explan);
    535			event = DISC_EV_FAILED;
    536			if (cp->ct_reason == FC_FS_RJT_UNABL &&
    537			    cp->ct_explan == FC_FS_EXP_FTNR)
    538				event = DISC_EV_SUCCESS;
    539		} else {
    540			FC_DISC_DBG(disc, "GPN_FT unexpected response code "
    541				    "%x\n", ntohs(cp->ct_cmd));
    542			event = DISC_EV_FAILED;
    543		}
    544	} else if (fr_sof(fp) == FC_SOF_N3 && seq_cnt == disc->seq_count) {
    545		error = fc_disc_gpn_ft_parse(disc, fh + 1, len);
    546	} else {
    547		FC_DISC_DBG(disc, "GPN_FT unexpected frame - out of sequence? "
    548			    "seq_cnt %x expected %x sof %x eof %x\n",
    549			    seq_cnt, disc->seq_count, fr_sof(fp), fr_eof(fp));
    550		event = DISC_EV_FAILED;
    551	}
    552	if (error)
    553		fc_disc_error(disc, ERR_PTR(error));
    554	else if (event != DISC_EV_NONE)
    555		fc_disc_done(disc, event);
    556	fc_frame_free(fp);
    557	mutex_unlock(&disc->disc_mutex);
    558}
    559
    560/**
    561 * fc_disc_gpn_id_resp() - Handle a response frame from Get Port Names (GPN_ID)
    562 * @sp:	       The sequence the GPN_ID is on
    563 * @fp:	       The response frame
    564 * @rdata_arg: The remote port that sent the GPN_ID response
    565 *
    566 * Locking Note: This function is called without disc mutex held.
    567 */
    568static void fc_disc_gpn_id_resp(struct fc_seq *sp, struct fc_frame *fp,
    569				void *rdata_arg)
    570{
    571	struct fc_rport_priv *rdata = rdata_arg;
    572	struct fc_rport_priv *new_rdata;
    573	struct fc_lport *lport;
    574	struct fc_disc *disc;
    575	struct fc_ct_hdr *cp;
    576	struct fc_ns_gid_pn *pn;
    577	u64 port_name;
    578
    579	lport = rdata->local_port;
    580	disc = &lport->disc;
    581
    582	if (PTR_ERR(fp) == -FC_EX_CLOSED)
    583		goto out;
    584	if (IS_ERR(fp)) {
    585		mutex_lock(&disc->disc_mutex);
    586		fc_disc_restart(disc);
    587		mutex_unlock(&disc->disc_mutex);
    588		goto out;
    589	}
    590
    591	cp = fc_frame_payload_get(fp, sizeof(*cp));
    592	if (!cp)
    593		goto redisc;
    594	if (ntohs(cp->ct_cmd) == FC_FS_ACC) {
    595		if (fr_len(fp) < sizeof(struct fc_frame_header) +
    596		    sizeof(*cp) + sizeof(*pn))
    597			goto redisc;
    598		pn = (struct fc_ns_gid_pn *)(cp + 1);
    599		port_name = get_unaligned_be64(&pn->fn_wwpn);
    600		mutex_lock(&rdata->rp_mutex);
    601		if (rdata->ids.port_name == -1)
    602			rdata->ids.port_name = port_name;
    603		else if (rdata->ids.port_name != port_name) {
    604			FC_DISC_DBG(disc, "GPN_ID accepted.  WWPN changed. "
    605				    "Port-id %6.6x wwpn %16.16llx\n",
    606				    rdata->ids.port_id, port_name);
    607			mutex_unlock(&rdata->rp_mutex);
    608			fc_rport_logoff(rdata);
    609			mutex_lock(&lport->disc.disc_mutex);
    610			new_rdata = fc_rport_create(lport, rdata->ids.port_id);
    611			mutex_unlock(&lport->disc.disc_mutex);
    612			if (new_rdata) {
    613				new_rdata->disc_id = disc->disc_id;
    614				fc_rport_login(new_rdata);
    615			}
    616			goto free_fp;
    617		}
    618		rdata->disc_id = disc->disc_id;
    619		mutex_unlock(&rdata->rp_mutex);
    620		fc_rport_login(rdata);
    621	} else if (ntohs(cp->ct_cmd) == FC_FS_RJT) {
    622		FC_DISC_DBG(disc, "GPN_ID rejected reason %x exp %x\n",
    623			    cp->ct_reason, cp->ct_explan);
    624		fc_rport_logoff(rdata);
    625	} else {
    626		FC_DISC_DBG(disc, "GPN_ID unexpected response code %x\n",
    627			    ntohs(cp->ct_cmd));
    628redisc:
    629		mutex_lock(&disc->disc_mutex);
    630		fc_disc_restart(disc);
    631		mutex_unlock(&disc->disc_mutex);
    632	}
    633free_fp:
    634	fc_frame_free(fp);
    635out:
    636	kref_put(&rdata->kref, fc_rport_destroy);
    637}
    638
    639/**
    640 * fc_disc_gpn_id_req() - Send Get Port Names by ID (GPN_ID) request
    641 * @lport: The local port to initiate discovery on
    642 * @rdata: remote port private data
    643 *
    644 * On failure, an error code is returned.
    645 */
    646static int fc_disc_gpn_id_req(struct fc_lport *lport,
    647			      struct fc_rport_priv *rdata)
    648{
    649	struct fc_frame *fp;
    650
    651	lockdep_assert_held(&lport->disc.disc_mutex);
    652	fp = fc_frame_alloc(lport, sizeof(struct fc_ct_hdr) +
    653			    sizeof(struct fc_ns_fid));
    654	if (!fp)
    655		return -ENOMEM;
    656	if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, FC_NS_GPN_ID,
    657				  fc_disc_gpn_id_resp, rdata,
    658				  3 * lport->r_a_tov))
    659		return -ENOMEM;
    660	kref_get(&rdata->kref);
    661	return 0;
    662}
    663
    664/**
    665 * fc_disc_single() - Discover the directory information for a single target
    666 * @lport: The local port the remote port is associated with
    667 * @dp:	   The port to rediscover
    668 */
    669static int fc_disc_single(struct fc_lport *lport, struct fc_disc_port *dp)
    670{
    671	struct fc_rport_priv *rdata;
    672
    673	lockdep_assert_held(&lport->disc.disc_mutex);
    674
    675	rdata = fc_rport_create(lport, dp->port_id);
    676	if (!rdata)
    677		return -ENOMEM;
    678	rdata->disc_id = 0;
    679	return fc_disc_gpn_id_req(lport, rdata);
    680}
    681
    682/**
    683 * fc_disc_stop() - Stop discovery for a given lport
    684 * @lport: The local port that discovery should stop on
    685 */
    686static void fc_disc_stop(struct fc_lport *lport)
    687{
    688	struct fc_disc *disc = &lport->disc;
    689
    690	if (disc->pending)
    691		cancel_delayed_work_sync(&disc->disc_work);
    692	mutex_lock(&disc->disc_mutex);
    693	fc_disc_stop_rports(disc);
    694	mutex_unlock(&disc->disc_mutex);
    695}
    696
    697/**
    698 * fc_disc_stop_final() - Stop discovery for a given lport
    699 * @lport: The lport that discovery should stop on
    700 *
    701 * This function will block until discovery has been
    702 * completely stopped and all rports have been deleted.
    703 */
    704static void fc_disc_stop_final(struct fc_lport *lport)
    705{
    706	fc_disc_stop(lport);
    707	fc_rport_flush_queue();
    708}
    709
    710/**
    711 * fc_disc_config() - Configure the discovery layer for a local port
    712 * @lport: The local port that needs the discovery layer to be configured
    713 * @priv: Private data structre for users of the discovery layer
    714 */
    715void fc_disc_config(struct fc_lport *lport, void *priv)
    716{
    717	struct fc_disc *disc;
    718
    719	if (!lport->tt.disc_start)
    720		lport->tt.disc_start = fc_disc_start;
    721
    722	if (!lport->tt.disc_stop)
    723		lport->tt.disc_stop = fc_disc_stop;
    724
    725	if (!lport->tt.disc_stop_final)
    726		lport->tt.disc_stop_final = fc_disc_stop_final;
    727
    728	if (!lport->tt.disc_recv_req)
    729		lport->tt.disc_recv_req = fc_disc_recv_req;
    730
    731	disc = &lport->disc;
    732
    733	disc->priv = priv;
    734}
    735EXPORT_SYMBOL(fc_disc_config);
    736
    737/**
    738 * fc_disc_init() - Initialize the discovery layer for a local port
    739 * @lport: The local port that needs the discovery layer to be initialized
    740 */
    741void fc_disc_init(struct fc_lport *lport)
    742{
    743	struct fc_disc *disc = &lport->disc;
    744
    745	INIT_DELAYED_WORK(&disc->disc_work, fc_disc_timeout);
    746	mutex_init(&disc->disc_mutex);
    747	INIT_LIST_HEAD(&disc->rports);
    748}
    749EXPORT_SYMBOL(fc_disc_init);