blob: 3fcbba1718631a5c529826681a2d0aced617aa46 [file] [log] [blame]
Robert Love42e9a922008-12-09 15:10:17 -08001/*
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/err.h>
37#include <asm/unaligned.h>
38
39#include <scsi/fc/fc_gs.h>
40
41#include <scsi/libfc.h>
42
43#define FC_DISC_RETRY_LIMIT 3 /* max retries */
44#define FC_DISC_RETRY_DELAY 500UL /* (msecs) delay */
45
Robert Love42e9a922008-12-09 15:10:17 -080046static void fc_disc_gpn_ft_req(struct fc_disc *);
47static void fc_disc_gpn_ft_resp(struct fc_seq *, struct fc_frame *, void *);
Joe Eykholtf211fa52009-08-25 14:01:01 -070048static int fc_disc_new_target(struct fc_disc *, struct fc_rport_priv *,
Robert Love42e9a922008-12-09 15:10:17 -080049 struct fc_rport_identifiers *);
Joe Eykholt786681b2009-08-25 14:01:29 -070050static void fc_disc_done(struct fc_disc *, enum fc_disc_event);
Robert Love42e9a922008-12-09 15:10:17 -080051static void fc_disc_timeout(struct work_struct *);
52static void fc_disc_single(struct fc_disc *, struct fc_disc_port *);
53static void fc_disc_restart(struct fc_disc *);
54
55/**
Robert Love34f42a02009-02-27 10:55:45 -080056 * fc_disc_lookup_rport() - lookup a remote port by port_id
Robert Love42e9a922008-12-09 15:10:17 -080057 * @lport: Fibre Channel host port instance
58 * @port_id: remote port port_id to match
59 */
Joe Eykholt9fb9d322009-08-25 14:00:50 -070060struct fc_rport_priv *fc_disc_lookup_rport(const struct fc_lport *lport,
61 u32 port_id)
Robert Love42e9a922008-12-09 15:10:17 -080062{
63 const struct fc_disc *disc = &lport->disc;
Joe Eykholtab28f1f2009-08-25 14:00:34 -070064 struct fc_rport_priv *rdata;
Robert Love42e9a922008-12-09 15:10:17 -080065
66 list_for_each_entry(rdata, &disc->rports, peers) {
Joe Eykholt9e9d0452009-08-25 14:01:18 -070067 if (rdata->ids.port_id == port_id &&
68 rdata->rp_state != RPORT_ST_DELETE)
Joe Eykholt9fb9d322009-08-25 14:00:50 -070069 return rdata;
Robert Love42e9a922008-12-09 15:10:17 -080070 }
Joe Eykholt9fb9d322009-08-25 14:00:50 -070071 return NULL;
Robert Love42e9a922008-12-09 15:10:17 -080072}
73
74/**
Robert Love34f42a02009-02-27 10:55:45 -080075 * fc_disc_stop_rports() - delete all the remote ports associated with the lport
Robert Love42e9a922008-12-09 15:10:17 -080076 * @disc: The discovery job to stop rports on
77 *
78 * Locking Note: This function expects that the lport mutex is locked before
79 * calling it.
80 */
81void fc_disc_stop_rports(struct fc_disc *disc)
82{
83 struct fc_lport *lport;
Joe Eykholtab28f1f2009-08-25 14:00:34 -070084 struct fc_rport_priv *rdata, *next;
Robert Love42e9a922008-12-09 15:10:17 -080085
86 lport = disc->lport;
87
88 mutex_lock(&disc->disc_mutex);
Joe Eykholt9e9d0452009-08-25 14:01:18 -070089 list_for_each_entry_safe(rdata, next, &disc->rports, peers)
Joe Eykholt9fb9d322009-08-25 14:00:50 -070090 lport->tt.rport_logoff(rdata);
Robert Love42e9a922008-12-09 15:10:17 -080091 mutex_unlock(&disc->disc_mutex);
92}
93
94/**
Robert Love34f42a02009-02-27 10:55:45 -080095 * fc_disc_recv_rscn_req() - Handle Registered State Change Notification (RSCN)
Robert Love42e9a922008-12-09 15:10:17 -080096 * @sp: Current sequence of the RSCN exchange
97 * @fp: RSCN Frame
98 * @lport: Fibre Channel host port instance
99 *
100 * Locking Note: This function expects that the disc_mutex is locked
101 * before it is called.
102 */
103static void fc_disc_recv_rscn_req(struct fc_seq *sp, struct fc_frame *fp,
104 struct fc_disc *disc)
105{
106 struct fc_lport *lport;
Joe Eykholtab28f1f2009-08-25 14:00:34 -0700107 struct fc_rport_priv *rdata;
Robert Love42e9a922008-12-09 15:10:17 -0800108 struct fc_els_rscn *rp;
109 struct fc_els_rscn_page *pp;
110 struct fc_seq_els_data rjt_data;
111 unsigned int len;
112 int redisc = 0;
113 enum fc_els_rscn_ev_qual ev_qual;
114 enum fc_els_rscn_addr_fmt fmt;
115 LIST_HEAD(disc_ports);
116 struct fc_disc_port *dp, *next;
117
118 lport = disc->lport;
119
Robert Love74147052009-06-10 15:31:10 -0700120 FC_DISC_DBG(disc, "Received an RSCN event\n");
Robert Love42e9a922008-12-09 15:10:17 -0800121
122 /* make sure the frame contains an RSCN message */
123 rp = fc_frame_payload_get(fp, sizeof(*rp));
124 if (!rp)
125 goto reject;
126 /* make sure the page length is as expected (4 bytes) */
127 if (rp->rscn_page_len != sizeof(*pp))
128 goto reject;
129 /* get the RSCN payload length */
130 len = ntohs(rp->rscn_plen);
131 if (len < sizeof(*rp))
132 goto reject;
133 /* make sure the frame contains the expected payload */
134 rp = fc_frame_payload_get(fp, len);
135 if (!rp)
136 goto reject;
137 /* payload must be a multiple of the RSCN page size */
138 len -= sizeof(*rp);
139 if (len % sizeof(*pp))
140 goto reject;
141
142 for (pp = (void *)(rp + 1); len > 0; len -= sizeof(*pp), pp++) {
143 ev_qual = pp->rscn_page_flags >> ELS_RSCN_EV_QUAL_BIT;
144 ev_qual &= ELS_RSCN_EV_QUAL_MASK;
145 fmt = pp->rscn_page_flags >> ELS_RSCN_ADDR_FMT_BIT;
146 fmt &= ELS_RSCN_ADDR_FMT_MASK;
147 /*
148 * if we get an address format other than port
149 * (area, domain, fabric), then do a full discovery
150 */
151 switch (fmt) {
152 case ELS_ADDR_FMT_PORT:
Robert Love74147052009-06-10 15:31:10 -0700153 FC_DISC_DBG(disc, "Port address format for port "
154 "(%6x)\n", ntoh24(pp->rscn_fid));
Robert Love42e9a922008-12-09 15:10:17 -0800155 dp = kzalloc(sizeof(*dp), GFP_KERNEL);
156 if (!dp) {
157 redisc = 1;
158 break;
159 }
160 dp->lp = lport;
161 dp->ids.port_id = ntoh24(pp->rscn_fid);
162 dp->ids.port_name = -1;
163 dp->ids.node_name = -1;
164 dp->ids.roles = FC_RPORT_ROLE_UNKNOWN;
165 list_add_tail(&dp->peers, &disc_ports);
166 break;
167 case ELS_ADDR_FMT_AREA:
168 case ELS_ADDR_FMT_DOM:
169 case ELS_ADDR_FMT_FAB:
170 default:
Robert Love74147052009-06-10 15:31:10 -0700171 FC_DISC_DBG(disc, "Address format is (%d)\n", fmt);
Robert Love42e9a922008-12-09 15:10:17 -0800172 redisc = 1;
173 break;
174 }
175 }
176 lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL);
177 if (redisc) {
Robert Love74147052009-06-10 15:31:10 -0700178 FC_DISC_DBG(disc, "RSCN received: rediscovering\n");
Robert Love42e9a922008-12-09 15:10:17 -0800179 fc_disc_restart(disc);
180 } else {
Robert Love74147052009-06-10 15:31:10 -0700181 FC_DISC_DBG(disc, "RSCN received: not rediscovering. "
182 "redisc %d state %d in_prog %d\n",
183 redisc, lport->state, disc->pending);
Robert Love42e9a922008-12-09 15:10:17 -0800184 list_for_each_entry_safe(dp, next, &disc_ports, peers) {
185 list_del(&dp->peers);
Joe Eykholt9fb9d322009-08-25 14:00:50 -0700186 rdata = lport->tt.rport_lookup(lport, dp->ids.port_id);
187 if (rdata) {
Joe Eykholt9fb9d322009-08-25 14:00:50 -0700188 lport->tt.rport_logoff(rdata);
Robert Love42e9a922008-12-09 15:10:17 -0800189 }
190 fc_disc_single(disc, dp);
191 }
192 }
193 fc_frame_free(fp);
194 return;
195reject:
Robert Love74147052009-06-10 15:31:10 -0700196 FC_DISC_DBG(disc, "Received a bad RSCN frame\n");
Robert Love42e9a922008-12-09 15:10:17 -0800197 rjt_data.fp = NULL;
198 rjt_data.reason = ELS_RJT_LOGIC;
199 rjt_data.explan = ELS_EXPL_NONE;
200 lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
201 fc_frame_free(fp);
202}
203
204/**
Robert Love34f42a02009-02-27 10:55:45 -0800205 * fc_disc_recv_req() - Handle incoming requests
Robert Love42e9a922008-12-09 15:10:17 -0800206 * @sp: Current sequence of the request exchange
207 * @fp: The frame
208 * @lport: The FC local port
209 *
210 * Locking Note: This function is called from the EM and will lock
211 * the disc_mutex before calling the handler for the
212 * request.
213 */
214static void fc_disc_recv_req(struct fc_seq *sp, struct fc_frame *fp,
215 struct fc_lport *lport)
216{
217 u8 op;
218 struct fc_disc *disc = &lport->disc;
219
220 op = fc_frame_payload_op(fp);
221 switch (op) {
222 case ELS_RSCN:
223 mutex_lock(&disc->disc_mutex);
224 fc_disc_recv_rscn_req(sp, fp, disc);
225 mutex_unlock(&disc->disc_mutex);
226 break;
227 default:
Robert Love74147052009-06-10 15:31:10 -0700228 FC_DISC_DBG(disc, "Received an unsupported request, "
229 "the opcode is (%x)\n", op);
Robert Love42e9a922008-12-09 15:10:17 -0800230 break;
231 }
232}
233
234/**
Robert Love34f42a02009-02-27 10:55:45 -0800235 * fc_disc_restart() - Restart discovery
Robert Love42e9a922008-12-09 15:10:17 -0800236 * @lport: FC discovery context
237 *
238 * Locking Note: This function expects that the disc mutex
239 * is already locked.
240 */
241static void fc_disc_restart(struct fc_disc *disc)
242{
Joe Eykholtab28f1f2009-08-25 14:00:34 -0700243 struct fc_rport_priv *rdata, *next;
Robert Love42e9a922008-12-09 15:10:17 -0800244 struct fc_lport *lport = disc->lport;
245
Robert Love74147052009-06-10 15:31:10 -0700246 FC_DISC_DBG(disc, "Restarting discovery\n");
Robert Love42e9a922008-12-09 15:10:17 -0800247
Joe Eykholt9e9d0452009-08-25 14:01:18 -0700248 list_for_each_entry_safe(rdata, next, &disc->rports, peers)
Joe Eykholt9fb9d322009-08-25 14:00:50 -0700249 lport->tt.rport_logoff(rdata);
Robert Love42e9a922008-12-09 15:10:17 -0800250
251 disc->requested = 1;
252 if (!disc->pending)
253 fc_disc_gpn_ft_req(disc);
254}
255
256/**
Robert Love34f42a02009-02-27 10:55:45 -0800257 * fc_disc_start() - Fibre Channel Target discovery
Robert Love42e9a922008-12-09 15:10:17 -0800258 * @lport: FC local port
259 *
260 * Returns non-zero if discovery cannot be started.
261 */
262static void fc_disc_start(void (*disc_callback)(struct fc_lport *,
263 enum fc_disc_event),
264 struct fc_lport *lport)
265{
Joe Eykholt9fb9d322009-08-25 14:00:50 -0700266 struct fc_rport_priv *rdata;
Robert Love42e9a922008-12-09 15:10:17 -0800267 struct fc_disc *disc = &lport->disc;
268
269 /*
270 * At this point we may have a new disc job or an existing
271 * one. Either way, let's lock when we make changes to it
272 * and send the GPN_FT request.
273 */
274 mutex_lock(&disc->disc_mutex);
275
276 disc->disc_callback = disc_callback;
277
278 /*
279 * If not ready, or already running discovery, just set request flag.
280 */
281 disc->requested = 1;
282
283 if (disc->pending) {
284 mutex_unlock(&disc->disc_mutex);
285 return;
286 }
287
288 /*
289 * Handle point-to-point mode as a simple discovery
290 * of the remote port. Yucky, yucky, yuck, yuck!
291 */
Joe Eykholt9fb9d322009-08-25 14:00:50 -0700292 rdata = disc->lport->ptp_rp;
293 if (rdata) {
Joe Eykholtf211fa52009-08-25 14:01:01 -0700294 kref_get(&rdata->kref);
295 if (!fc_disc_new_target(disc, rdata, &rdata->ids)) {
Joe Eykholt786681b2009-08-25 14:01:29 -0700296 fc_disc_done(disc, DISC_EV_SUCCESS);
Robert Love42e9a922008-12-09 15:10:17 -0800297 }
Joe Eykholtf211fa52009-08-25 14:01:01 -0700298 kref_put(&rdata->kref, rdata->local_port->tt.rport_destroy);
Robert Love42e9a922008-12-09 15:10:17 -0800299 } else {
300 fc_disc_gpn_ft_req(disc); /* get ports by FC-4 type */
301 }
302
303 mutex_unlock(&disc->disc_mutex);
304}
305
Robert Love42e9a922008-12-09 15:10:17 -0800306/**
Robert Love34f42a02009-02-27 10:55:45 -0800307 * fc_disc_new_target() - Handle new target found by discovery
Robert Love42e9a922008-12-09 15:10:17 -0800308 * @lport: FC local port
Joe Eykholtf211fa52009-08-25 14:01:01 -0700309 * @rdata: The previous FC remote port priv (NULL if new remote port)
Robert Love42e9a922008-12-09 15:10:17 -0800310 * @ids: Identifiers for the new FC remote port
311 *
312 * Locking Note: This function expects that the disc_mutex is locked
313 * before it is called.
314 */
315static int fc_disc_new_target(struct fc_disc *disc,
Joe Eykholtf211fa52009-08-25 14:01:01 -0700316 struct fc_rport_priv *rdata,
Robert Love42e9a922008-12-09 15:10:17 -0800317 struct fc_rport_identifiers *ids)
318{
319 struct fc_lport *lport = disc->lport;
Robert Love42e9a922008-12-09 15:10:17 -0800320 int error = 0;
321
Joe Eykholtf211fa52009-08-25 14:01:01 -0700322 if (rdata && ids->port_name) {
323 if (rdata->ids.port_name == -1) {
Robert Love42e9a922008-12-09 15:10:17 -0800324 /*
325 * Set WWN and fall through to notify of create.
326 */
Joe Eykholtf211fa52009-08-25 14:01:01 -0700327 rdata->ids.port_name = ids->port_name;
328 rdata->ids.node_name = ids->node_name;
329 } else if (rdata->ids.port_name != ids->port_name) {
Robert Love42e9a922008-12-09 15:10:17 -0800330 /*
331 * This is a new port with the same FCID as
332 * a previously-discovered port. Presumably the old
333 * port logged out and a new port logged in and was
334 * assigned the same FCID. This should be rare.
335 * Delete the old one and fall thru to re-create.
336 */
Joe Eykholt9fb9d322009-08-25 14:00:50 -0700337 lport->tt.rport_logoff(rdata);
Joe Eykholtf211fa52009-08-25 14:01:01 -0700338 rdata = NULL;
Robert Love42e9a922008-12-09 15:10:17 -0800339 }
340 }
341 if (((ids->port_name != -1) || (ids->port_id != -1)) &&
342 ids->port_id != fc_host_port_id(lport->host) &&
343 ids->port_name != lport->wwpn) {
Joe Eykholtf211fa52009-08-25 14:01:01 -0700344 if (!rdata) {
Joe Eykholt19f97e32009-08-25 14:01:55 -0700345 rdata = lport->tt.rport_create(lport, ids);
346 if (!rdata)
347 error = -ENOMEM;
Robert Love42e9a922008-12-09 15:10:17 -0800348 }
Joe Eykholt83455922009-08-25 14:02:01 -0700349 if (rdata)
Joe Eykholt9fb9d322009-08-25 14:00:50 -0700350 lport->tt.rport_login(rdata);
Robert Love42e9a922008-12-09 15:10:17 -0800351 }
352 return error;
353}
354
355/**
Robert Love34f42a02009-02-27 10:55:45 -0800356 * fc_disc_done() - Discovery has been completed
Robert Love42e9a922008-12-09 15:10:17 -0800357 * @disc: FC discovery context
Joe Eykholt786681b2009-08-25 14:01:29 -0700358 * @event: discovery completion status
359 *
Abhijeet Joglekar0d228c02009-04-21 16:26:52 -0700360 * Locking Note: This function expects that the disc mutex is locked before
361 * it is called. The discovery callback is then made with the lock released,
362 * and the lock is re-taken before returning from this function
Robert Love42e9a922008-12-09 15:10:17 -0800363 */
Joe Eykholt786681b2009-08-25 14:01:29 -0700364static void fc_disc_done(struct fc_disc *disc, enum fc_disc_event event)
Robert Love42e9a922008-12-09 15:10:17 -0800365{
366 struct fc_lport *lport = disc->lport;
367
Robert Love74147052009-06-10 15:31:10 -0700368 FC_DISC_DBG(disc, "Discovery complete\n");
Robert Love42e9a922008-12-09 15:10:17 -0800369
Robert Love42e9a922008-12-09 15:10:17 -0800370 if (disc->requested)
371 fc_disc_gpn_ft_req(disc);
372 else
373 disc->pending = 0;
Abhijeet Joglekar0d228c02009-04-21 16:26:52 -0700374
375 mutex_unlock(&disc->disc_mutex);
376 disc->disc_callback(lport, event);
377 mutex_lock(&disc->disc_mutex);
Robert Love42e9a922008-12-09 15:10:17 -0800378}
379
380/**
Robert Love34f42a02009-02-27 10:55:45 -0800381 * fc_disc_error() - Handle error on dNS request
Robert Love42e9a922008-12-09 15:10:17 -0800382 * @disc: FC discovery context
383 * @fp: The frame pointer
384 */
385static void fc_disc_error(struct fc_disc *disc, struct fc_frame *fp)
386{
387 struct fc_lport *lport = disc->lport;
388 unsigned long delay = 0;
Robert Love74147052009-06-10 15:31:10 -0700389
390 FC_DISC_DBG(disc, "Error %ld, retries %d/%d\n",
391 PTR_ERR(fp), disc->retry_count,
392 FC_DISC_RETRY_LIMIT);
Robert Love42e9a922008-12-09 15:10:17 -0800393
394 if (!fp || PTR_ERR(fp) == -FC_EX_TIMEOUT) {
395 /*
396 * Memory allocation failure, or the exchange timed out,
397 * retry after delay.
398 */
399 if (disc->retry_count < FC_DISC_RETRY_LIMIT) {
400 /* go ahead and retry */
401 if (!fp)
402 delay = msecs_to_jiffies(FC_DISC_RETRY_DELAY);
403 else {
404 delay = msecs_to_jiffies(lport->e_d_tov);
405
406 /* timeout faster first time */
407 if (!disc->retry_count)
408 delay /= 4;
409 }
410 disc->retry_count++;
411 schedule_delayed_work(&disc->disc_work, delay);
Joe Eykholt786681b2009-08-25 14:01:29 -0700412 } else
413 fc_disc_done(disc, DISC_EV_FAILED);
Robert Love42e9a922008-12-09 15:10:17 -0800414 }
415}
416
417/**
Robert Love34f42a02009-02-27 10:55:45 -0800418 * fc_disc_gpn_ft_req() - Send Get Port Names by FC-4 type (GPN_FT) request
Robert Love42e9a922008-12-09 15:10:17 -0800419 * @lport: FC discovery context
420 *
421 * Locking Note: This function expects that the disc_mutex is locked
422 * before it is called.
423 */
424static void fc_disc_gpn_ft_req(struct fc_disc *disc)
425{
426 struct fc_frame *fp;
427 struct fc_lport *lport = disc->lport;
428
429 WARN_ON(!fc_lport_test_ready(lport));
430
431 disc->pending = 1;
432 disc->requested = 0;
433
434 disc->buf_len = 0;
435 disc->seq_count = 0;
436 fp = fc_frame_alloc(lport,
437 sizeof(struct fc_ct_hdr) +
438 sizeof(struct fc_ns_gid_ft));
439 if (!fp)
440 goto err;
441
Joe Eykholta46f3272009-08-25 14:00:55 -0700442 if (lport->tt.elsct_send(lport, 0, fp,
Robert Love42e9a922008-12-09 15:10:17 -0800443 FC_NS_GPN_FT,
444 fc_disc_gpn_ft_resp,
445 disc, lport->e_d_tov))
446 return;
447err:
448 fc_disc_error(disc, fp);
449}
450
451/**
Joe Eykholt786681b2009-08-25 14:01:29 -0700452 * fc_disc_gpn_ft_parse() - Parse the body of the dNS GPN_FT response.
Robert Love42e9a922008-12-09 15:10:17 -0800453 * @lport: Fibre Channel host port instance
454 * @buf: GPN_FT response buffer
455 * @len: size of response buffer
Joe Eykholt786681b2009-08-25 14:01:29 -0700456 *
457 * Goes through the list of IDs and names resulting from a request.
Robert Love42e9a922008-12-09 15:10:17 -0800458 */
459static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len)
460{
461 struct fc_lport *lport;
462 struct fc_gpn_ft_resp *np;
463 char *bp;
464 size_t plen;
465 size_t tlen;
466 int error = 0;
Joe Eykholt795d86f2009-08-25 14:00:39 -0700467 struct fc_rport_identifiers ids;
Joe Eykholtab28f1f2009-08-25 14:00:34 -0700468 struct fc_rport_priv *rdata;
Robert Love42e9a922008-12-09 15:10:17 -0800469
470 lport = disc->lport;
471
472 /*
473 * Handle partial name record left over from previous call.
474 */
475 bp = buf;
476 plen = len;
477 np = (struct fc_gpn_ft_resp *)bp;
478 tlen = disc->buf_len;
479 if (tlen) {
480 WARN_ON(tlen >= sizeof(*np));
481 plen = sizeof(*np) - tlen;
482 WARN_ON(plen <= 0);
483 WARN_ON(plen >= sizeof(*np));
484 if (plen > len)
485 plen = len;
486 np = &disc->partial_buf;
487 memcpy((char *)np + tlen, bp, plen);
488
489 /*
490 * Set bp so that the loop below will advance it to the
491 * first valid full name element.
492 */
493 bp -= tlen;
494 len += tlen;
495 plen += tlen;
496 disc->buf_len = (unsigned char) plen;
497 if (plen == sizeof(*np))
498 disc->buf_len = 0;
499 }
500
501 /*
502 * Handle full name records, including the one filled from above.
503 * Normally, np == bp and plen == len, but from the partial case above,
504 * bp, len describe the overall buffer, and np, plen describe the
505 * partial buffer, which if would usually be full now.
506 * After the first time through the loop, things return to "normal".
507 */
508 while (plen >= sizeof(*np)) {
Joe Eykholt795d86f2009-08-25 14:00:39 -0700509 ids.port_id = ntoh24(np->fp_fid);
510 ids.port_name = ntohll(np->fp_wwpn);
511 ids.node_name = -1;
512 ids.roles = FC_RPORT_ROLE_UNKNOWN;
Robert Love42e9a922008-12-09 15:10:17 -0800513
Joe Eykholt795d86f2009-08-25 14:00:39 -0700514 if (ids.port_id != fc_host_port_id(lport->host) &&
515 ids.port_name != lport->wwpn) {
Joe Eykholt9fb9d322009-08-25 14:00:50 -0700516 rdata = lport->tt.rport_create(lport, &ids);
Joe Eykholt83455922009-08-25 14:02:01 -0700517 if (rdata)
Joe Eykholt9fb9d322009-08-25 14:00:50 -0700518 lport->tt.rport_login(rdata);
Joe Eykholt83455922009-08-25 14:02:01 -0700519 else
Robert Love74147052009-06-10 15:31:10 -0700520 printk(KERN_WARNING "libfc: Failed to allocate "
521 "memory for the newly discovered port "
Joe Eykholt795d86f2009-08-25 14:00:39 -0700522 "(%6x)\n", ids.port_id);
Robert Love42e9a922008-12-09 15:10:17 -0800523 }
524
525 if (np->fp_flags & FC_NS_FID_LAST) {
Joe Eykholt786681b2009-08-25 14:01:29 -0700526 fc_disc_done(disc, DISC_EV_SUCCESS);
Robert Love42e9a922008-12-09 15:10:17 -0800527 len = 0;
528 break;
529 }
530 len -= sizeof(*np);
531 bp += sizeof(*np);
532 np = (struct fc_gpn_ft_resp *)bp;
533 plen = len;
534 }
535
536 /*
537 * Save any partial record at the end of the buffer for next time.
538 */
539 if (error == 0 && len > 0 && len < sizeof(*np)) {
540 if (np != &disc->partial_buf) {
Robert Love74147052009-06-10 15:31:10 -0700541 FC_DISC_DBG(disc, "Partial buffer remains "
542 "for discovery\n");
Robert Love42e9a922008-12-09 15:10:17 -0800543 memcpy(&disc->partial_buf, np, len);
544 }
545 disc->buf_len = (unsigned char) len;
546 } else {
547 disc->buf_len = 0;
548 }
549 return error;
550}
551
Robert Love34f42a02009-02-27 10:55:45 -0800552/**
553 * fc_disc_timeout() - Retry handler for the disc component
554 * @work: Structure holding disc obj that needs retry discovery
555 *
Robert Love42e9a922008-12-09 15:10:17 -0800556 * Handle retry of memory allocation for remote ports.
557 */
558static void fc_disc_timeout(struct work_struct *work)
559{
560 struct fc_disc *disc = container_of(work,
561 struct fc_disc,
562 disc_work.work);
563 mutex_lock(&disc->disc_mutex);
564 if (disc->requested && !disc->pending)
565 fc_disc_gpn_ft_req(disc);
566 mutex_unlock(&disc->disc_mutex);
567}
568
569/**
Robert Love34f42a02009-02-27 10:55:45 -0800570 * fc_disc_gpn_ft_resp() - Handle a response frame from Get Port Names (GPN_FT)
Robert Love42e9a922008-12-09 15:10:17 -0800571 * @sp: Current sequence of GPN_FT exchange
572 * @fp: response frame
573 * @lp_arg: Fibre Channel host port instance
574 *
Abhijeet Joglekar0d228c02009-04-21 16:26:52 -0700575 * Locking Note: This function is called without disc mutex held, and
576 * should do all its processing with the mutex held
Robert Love42e9a922008-12-09 15:10:17 -0800577 */
578static void fc_disc_gpn_ft_resp(struct fc_seq *sp, struct fc_frame *fp,
579 void *disc_arg)
580{
581 struct fc_disc *disc = disc_arg;
582 struct fc_ct_hdr *cp;
583 struct fc_frame_header *fh;
584 unsigned int seq_cnt;
585 void *buf = NULL;
586 unsigned int len;
587 int error;
588
Abhijeet Joglekar0d228c02009-04-21 16:26:52 -0700589 mutex_lock(&disc->disc_mutex);
Robert Love74147052009-06-10 15:31:10 -0700590 FC_DISC_DBG(disc, "Received a GPN_FT response\n");
Robert Love42e9a922008-12-09 15:10:17 -0800591
592 if (IS_ERR(fp)) {
593 fc_disc_error(disc, fp);
Abhijeet Joglekar0d228c02009-04-21 16:26:52 -0700594 mutex_unlock(&disc->disc_mutex);
Robert Love42e9a922008-12-09 15:10:17 -0800595 return;
596 }
597
598 WARN_ON(!fc_frame_is_linear(fp)); /* buffer must be contiguous */
599 fh = fc_frame_header_get(fp);
600 len = fr_len(fp) - sizeof(*fh);
601 seq_cnt = ntohs(fh->fh_seq_cnt);
602 if (fr_sof(fp) == FC_SOF_I3 && seq_cnt == 0 &&
603 disc->seq_count == 0) {
604 cp = fc_frame_payload_get(fp, sizeof(*cp));
605 if (!cp) {
Robert Love74147052009-06-10 15:31:10 -0700606 FC_DISC_DBG(disc, "GPN_FT response too short, len %d\n",
607 fr_len(fp));
Robert Love42e9a922008-12-09 15:10:17 -0800608 } else if (ntohs(cp->ct_cmd) == FC_FS_ACC) {
609
Robert Love34f42a02009-02-27 10:55:45 -0800610 /* Accepted, parse the response. */
Robert Love42e9a922008-12-09 15:10:17 -0800611 buf = cp + 1;
612 len -= sizeof(*cp);
613 } else if (ntohs(cp->ct_cmd) == FC_FS_RJT) {
Robert Love74147052009-06-10 15:31:10 -0700614 FC_DISC_DBG(disc, "GPN_FT rejected reason %x exp %x "
615 "(check zoning)\n", cp->ct_reason,
616 cp->ct_explan);
Joe Eykholt786681b2009-08-25 14:01:29 -0700617 fc_disc_done(disc, DISC_EV_FAILED);
Robert Love42e9a922008-12-09 15:10:17 -0800618 } else {
Robert Love74147052009-06-10 15:31:10 -0700619 FC_DISC_DBG(disc, "GPN_FT unexpected response code "
620 "%x\n", ntohs(cp->ct_cmd));
Robert Love42e9a922008-12-09 15:10:17 -0800621 }
622 } else if (fr_sof(fp) == FC_SOF_N3 &&
623 seq_cnt == disc->seq_count) {
624 buf = fh + 1;
625 } else {
Robert Love74147052009-06-10 15:31:10 -0700626 FC_DISC_DBG(disc, "GPN_FT unexpected frame - out of sequence? "
627 "seq_cnt %x expected %x sof %x eof %x\n",
628 seq_cnt, disc->seq_count, fr_sof(fp), fr_eof(fp));
Robert Love42e9a922008-12-09 15:10:17 -0800629 }
630 if (buf) {
631 error = fc_disc_gpn_ft_parse(disc, buf, len);
632 if (error)
633 fc_disc_error(disc, fp);
634 else
635 disc->seq_count++;
636 }
637 fc_frame_free(fp);
Abhijeet Joglekar0d228c02009-04-21 16:26:52 -0700638
639 mutex_unlock(&disc->disc_mutex);
Robert Love42e9a922008-12-09 15:10:17 -0800640}
641
642/**
Robert Love34f42a02009-02-27 10:55:45 -0800643 * fc_disc_single() - Discover the directory information for a single target
Robert Love42e9a922008-12-09 15:10:17 -0800644 * @lport: FC local port
645 * @dp: The port to rediscover
646 *
647 * Locking Note: This function expects that the disc_mutex is locked
648 * before it is called.
649 */
650static void fc_disc_single(struct fc_disc *disc, struct fc_disc_port *dp)
651{
652 struct fc_lport *lport;
Joe Eykholtab28f1f2009-08-25 14:00:34 -0700653 struct fc_rport_priv *rdata;
Robert Love42e9a922008-12-09 15:10:17 -0800654
655 lport = disc->lport;
656
657 if (dp->ids.port_id == fc_host_port_id(lport->host))
658 goto out;
659
Joe Eykholt9fb9d322009-08-25 14:00:50 -0700660 rdata = lport->tt.rport_create(lport, &dp->ids);
661 if (rdata) {
Robert Love42e9a922008-12-09 15:10:17 -0800662 kfree(dp);
Joe Eykholt9fb9d322009-08-25 14:00:50 -0700663 lport->tt.rport_login(rdata);
Robert Love42e9a922008-12-09 15:10:17 -0800664 }
665 return;
666out:
667 kfree(dp);
668}
669
670/**
Robert Love34f42a02009-02-27 10:55:45 -0800671 * fc_disc_stop() - Stop discovery for a given lport
Robert Love42e9a922008-12-09 15:10:17 -0800672 * @lport: The lport that discovery should stop for
673 */
674void fc_disc_stop(struct fc_lport *lport)
675{
676 struct fc_disc *disc = &lport->disc;
677
678 if (disc) {
679 cancel_delayed_work_sync(&disc->disc_work);
680 fc_disc_stop_rports(disc);
681 }
682}
683
684/**
Robert Love34f42a02009-02-27 10:55:45 -0800685 * fc_disc_stop_final() - Stop discovery for a given lport
Robert Love42e9a922008-12-09 15:10:17 -0800686 * @lport: The lport that discovery should stop for
687 *
688 * This function will block until discovery has been
689 * completely stopped and all rports have been deleted.
690 */
691void fc_disc_stop_final(struct fc_lport *lport)
692{
693 fc_disc_stop(lport);
694 lport->tt.rport_flush_queue();
695}
696
697/**
Robert Love34f42a02009-02-27 10:55:45 -0800698 * fc_disc_init() - Initialize the discovery block
Robert Love42e9a922008-12-09 15:10:17 -0800699 * @lport: FC local port
700 */
701int fc_disc_init(struct fc_lport *lport)
702{
703 struct fc_disc *disc;
704
705 if (!lport->tt.disc_start)
706 lport->tt.disc_start = fc_disc_start;
707
708 if (!lport->tt.disc_stop)
709 lport->tt.disc_stop = fc_disc_stop;
710
711 if (!lport->tt.disc_stop_final)
712 lport->tt.disc_stop_final = fc_disc_stop_final;
713
714 if (!lport->tt.disc_recv_req)
715 lport->tt.disc_recv_req = fc_disc_recv_req;
716
717 if (!lport->tt.rport_lookup)
718 lport->tt.rport_lookup = fc_disc_lookup_rport;
719
720 disc = &lport->disc;
721 INIT_DELAYED_WORK(&disc->disc_work, fc_disc_timeout);
722 mutex_init(&disc->disc_mutex);
723 INIT_LIST_HEAD(&disc->rports);
Robert Love42e9a922008-12-09 15:10:17 -0800724
725 disc->lport = lport;
Robert Love42e9a922008-12-09 15:10:17 -0800726
727 return 0;
728}
729EXPORT_SYMBOL(fc_disc_init);