Linux Audio

Check our new training course

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