blob: bbea41e50a57cfd1dff05ec09749427c7b95b5b7 [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
46#define FC_DISC_DELAY 3
47
Robert Love42e9a922008-12-09 15:10:17 -080048static void fc_disc_gpn_ft_req(struct fc_disc *);
49static void fc_disc_gpn_ft_resp(struct fc_seq *, struct fc_frame *, void *);
Joe Eykholtf211fa52009-08-25 14:01:01 -070050static int fc_disc_new_target(struct fc_disc *, struct fc_rport_priv *,
Robert Love42e9a922008-12-09 15:10:17 -080051 struct fc_rport_identifiers *);
Robert Love42e9a922008-12-09 15:10:17 -080052static void fc_disc_done(struct fc_disc *);
53static void fc_disc_timeout(struct work_struct *);
54static void fc_disc_single(struct fc_disc *, struct fc_disc_port *);
55static void fc_disc_restart(struct fc_disc *);
56
57/**
Robert Love34f42a02009-02-27 10:55:45 -080058 * fc_disc_lookup_rport() - lookup a remote port by port_id
Robert Love42e9a922008-12-09 15:10:17 -080059 * @lport: Fibre Channel host port instance
60 * @port_id: remote port port_id to match
61 */
Joe Eykholt9fb9d322009-08-25 14:00:50 -070062struct fc_rport_priv *fc_disc_lookup_rport(const struct fc_lport *lport,
63 u32 port_id)
Robert Love42e9a922008-12-09 15:10:17 -080064{
65 const struct fc_disc *disc = &lport->disc;
Joe Eykholtab28f1f2009-08-25 14:00:34 -070066 struct fc_rport_priv *rdata;
Robert Love42e9a922008-12-09 15:10:17 -080067
68 list_for_each_entry(rdata, &disc->rports, peers) {
Joe Eykholt9e9d0452009-08-25 14:01:18 -070069 if (rdata->ids.port_id == port_id &&
70 rdata->rp_state != RPORT_ST_DELETE)
Joe Eykholt9fb9d322009-08-25 14:00:50 -070071 return rdata;
Robert Love42e9a922008-12-09 15:10:17 -080072 }
Joe Eykholt9fb9d322009-08-25 14:00:50 -070073 return NULL;
Robert Love42e9a922008-12-09 15:10:17 -080074}
75
76/**
Robert Love34f42a02009-02-27 10:55:45 -080077 * fc_disc_stop_rports() - delete all the remote ports associated with the lport
Robert Love42e9a922008-12-09 15:10:17 -080078 * @disc: The discovery job to stop rports on
79 *
80 * Locking Note: This function expects that the lport mutex is locked before
81 * calling it.
82 */
83void fc_disc_stop_rports(struct fc_disc *disc)
84{
85 struct fc_lport *lport;
Joe Eykholtab28f1f2009-08-25 14:00:34 -070086 struct fc_rport_priv *rdata, *next;
Robert Love42e9a922008-12-09 15:10:17 -080087
88 lport = disc->lport;
89
90 mutex_lock(&disc->disc_mutex);
Joe Eykholt9e9d0452009-08-25 14:01:18 -070091 list_for_each_entry_safe(rdata, next, &disc->rports, peers)
Joe Eykholt9fb9d322009-08-25 14:00:50 -070092 lport->tt.rport_logoff(rdata);
Robert Love42e9a922008-12-09 15:10:17 -080093 mutex_unlock(&disc->disc_mutex);
94}
95
96/**
Robert Love34f42a02009-02-27 10:55:45 -080097 * fc_disc_rport_callback() - Event handler for rport events
Robert Love42e9a922008-12-09 15:10:17 -080098 * @lport: The lport which is receiving the event
Joe Eykholt9fb9d322009-08-25 14:00:50 -070099 * @rdata: private remote port data
Robert Love42e9a922008-12-09 15:10:17 -0800100 * @event: The event that occured
101 *
102 * Locking Note: The rport lock should not be held when calling
103 * this function.
104 */
105static void fc_disc_rport_callback(struct fc_lport *lport,
Joe Eykholt9fb9d322009-08-25 14:00:50 -0700106 struct fc_rport_priv *rdata,
Robert Love42e9a922008-12-09 15:10:17 -0800107 enum fc_rport_event event)
108{
Robert Love42e9a922008-12-09 15:10:17 -0800109 struct fc_disc *disc = &lport->disc;
Robert Love42e9a922008-12-09 15:10:17 -0800110
Robert Love74147052009-06-10 15:31:10 -0700111 FC_DISC_DBG(disc, "Received a %d event for port (%6x)\n", event,
Joe Eykholtf211fa52009-08-25 14:01:01 -0700112 rdata->ids.port_id);
Robert Love42e9a922008-12-09 15:10:17 -0800113
Abhijeet Joglekarb4c6f542009-04-21 16:27:04 -0700114 switch (event) {
Joe Eykholt4c0f62b2009-08-25 14:01:12 -0700115 case RPORT_EV_READY:
Abhijeet Joglekarb4c6f542009-04-21 16:27:04 -0700116 break;
117 case RPORT_EV_LOGO:
118 case RPORT_EV_FAILED:
119 case RPORT_EV_STOP:
120 mutex_lock(&disc->disc_mutex);
Joe Eykholt9e9d0452009-08-25 14:01:18 -0700121 list_del(&rdata->peers);
Abhijeet Joglekarb4c6f542009-04-21 16:27:04 -0700122 mutex_unlock(&disc->disc_mutex);
123 break;
124 default:
125 break;
Robert Love42e9a922008-12-09 15:10:17 -0800126 }
127
Robert Love42e9a922008-12-09 15:10:17 -0800128}
129
130/**
Robert Love34f42a02009-02-27 10:55:45 -0800131 * fc_disc_recv_rscn_req() - Handle Registered State Change Notification (RSCN)
Robert Love42e9a922008-12-09 15:10:17 -0800132 * @sp: Current sequence of the RSCN exchange
133 * @fp: RSCN Frame
134 * @lport: Fibre Channel host port instance
135 *
136 * Locking Note: This function expects that the disc_mutex is locked
137 * before it is called.
138 */
139static void fc_disc_recv_rscn_req(struct fc_seq *sp, struct fc_frame *fp,
140 struct fc_disc *disc)
141{
142 struct fc_lport *lport;
Joe Eykholtab28f1f2009-08-25 14:00:34 -0700143 struct fc_rport_priv *rdata;
Robert Love42e9a922008-12-09 15:10:17 -0800144 struct fc_els_rscn *rp;
145 struct fc_els_rscn_page *pp;
146 struct fc_seq_els_data rjt_data;
147 unsigned int len;
148 int redisc = 0;
149 enum fc_els_rscn_ev_qual ev_qual;
150 enum fc_els_rscn_addr_fmt fmt;
151 LIST_HEAD(disc_ports);
152 struct fc_disc_port *dp, *next;
153
154 lport = disc->lport;
155
Robert Love74147052009-06-10 15:31:10 -0700156 FC_DISC_DBG(disc, "Received an RSCN event\n");
Robert Love42e9a922008-12-09 15:10:17 -0800157
158 /* make sure the frame contains an RSCN message */
159 rp = fc_frame_payload_get(fp, sizeof(*rp));
160 if (!rp)
161 goto reject;
162 /* make sure the page length is as expected (4 bytes) */
163 if (rp->rscn_page_len != sizeof(*pp))
164 goto reject;
165 /* get the RSCN payload length */
166 len = ntohs(rp->rscn_plen);
167 if (len < sizeof(*rp))
168 goto reject;
169 /* make sure the frame contains the expected payload */
170 rp = fc_frame_payload_get(fp, len);
171 if (!rp)
172 goto reject;
173 /* payload must be a multiple of the RSCN page size */
174 len -= sizeof(*rp);
175 if (len % sizeof(*pp))
176 goto reject;
177
178 for (pp = (void *)(rp + 1); len > 0; len -= sizeof(*pp), pp++) {
179 ev_qual = pp->rscn_page_flags >> ELS_RSCN_EV_QUAL_BIT;
180 ev_qual &= ELS_RSCN_EV_QUAL_MASK;
181 fmt = pp->rscn_page_flags >> ELS_RSCN_ADDR_FMT_BIT;
182 fmt &= ELS_RSCN_ADDR_FMT_MASK;
183 /*
184 * if we get an address format other than port
185 * (area, domain, fabric), then do a full discovery
186 */
187 switch (fmt) {
188 case ELS_ADDR_FMT_PORT:
Robert Love74147052009-06-10 15:31:10 -0700189 FC_DISC_DBG(disc, "Port address format for port "
190 "(%6x)\n", ntoh24(pp->rscn_fid));
Robert Love42e9a922008-12-09 15:10:17 -0800191 dp = kzalloc(sizeof(*dp), GFP_KERNEL);
192 if (!dp) {
193 redisc = 1;
194 break;
195 }
196 dp->lp = lport;
197 dp->ids.port_id = ntoh24(pp->rscn_fid);
198 dp->ids.port_name = -1;
199 dp->ids.node_name = -1;
200 dp->ids.roles = FC_RPORT_ROLE_UNKNOWN;
201 list_add_tail(&dp->peers, &disc_ports);
202 break;
203 case ELS_ADDR_FMT_AREA:
204 case ELS_ADDR_FMT_DOM:
205 case ELS_ADDR_FMT_FAB:
206 default:
Robert Love74147052009-06-10 15:31:10 -0700207 FC_DISC_DBG(disc, "Address format is (%d)\n", fmt);
Robert Love42e9a922008-12-09 15:10:17 -0800208 redisc = 1;
209 break;
210 }
211 }
212 lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL);
213 if (redisc) {
Robert Love74147052009-06-10 15:31:10 -0700214 FC_DISC_DBG(disc, "RSCN received: rediscovering\n");
Robert Love42e9a922008-12-09 15:10:17 -0800215 fc_disc_restart(disc);
216 } else {
Robert Love74147052009-06-10 15:31:10 -0700217 FC_DISC_DBG(disc, "RSCN received: not rediscovering. "
218 "redisc %d state %d in_prog %d\n",
219 redisc, lport->state, disc->pending);
Robert Love42e9a922008-12-09 15:10:17 -0800220 list_for_each_entry_safe(dp, next, &disc_ports, peers) {
221 list_del(&dp->peers);
Joe Eykholt9fb9d322009-08-25 14:00:50 -0700222 rdata = lport->tt.rport_lookup(lport, dp->ids.port_id);
223 if (rdata) {
Joe Eykholt9fb9d322009-08-25 14:00:50 -0700224 lport->tt.rport_logoff(rdata);
Robert Love42e9a922008-12-09 15:10:17 -0800225 }
226 fc_disc_single(disc, dp);
227 }
228 }
229 fc_frame_free(fp);
230 return;
231reject:
Robert Love74147052009-06-10 15:31:10 -0700232 FC_DISC_DBG(disc, "Received a bad RSCN frame\n");
Robert Love42e9a922008-12-09 15:10:17 -0800233 rjt_data.fp = NULL;
234 rjt_data.reason = ELS_RJT_LOGIC;
235 rjt_data.explan = ELS_EXPL_NONE;
236 lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
237 fc_frame_free(fp);
238}
239
240/**
Robert Love34f42a02009-02-27 10:55:45 -0800241 * fc_disc_recv_req() - Handle incoming requests
Robert Love42e9a922008-12-09 15:10:17 -0800242 * @sp: Current sequence of the request exchange
243 * @fp: The frame
244 * @lport: The FC local port
245 *
246 * Locking Note: This function is called from the EM and will lock
247 * the disc_mutex before calling the handler for the
248 * request.
249 */
250static void fc_disc_recv_req(struct fc_seq *sp, struct fc_frame *fp,
251 struct fc_lport *lport)
252{
253 u8 op;
254 struct fc_disc *disc = &lport->disc;
255
256 op = fc_frame_payload_op(fp);
257 switch (op) {
258 case ELS_RSCN:
259 mutex_lock(&disc->disc_mutex);
260 fc_disc_recv_rscn_req(sp, fp, disc);
261 mutex_unlock(&disc->disc_mutex);
262 break;
263 default:
Robert Love74147052009-06-10 15:31:10 -0700264 FC_DISC_DBG(disc, "Received an unsupported request, "
265 "the opcode is (%x)\n", op);
Robert Love42e9a922008-12-09 15:10:17 -0800266 break;
267 }
268}
269
270/**
Robert Love34f42a02009-02-27 10:55:45 -0800271 * fc_disc_restart() - Restart discovery
Robert Love42e9a922008-12-09 15:10:17 -0800272 * @lport: FC discovery context
273 *
274 * Locking Note: This function expects that the disc mutex
275 * is already locked.
276 */
277static void fc_disc_restart(struct fc_disc *disc)
278{
Joe Eykholtab28f1f2009-08-25 14:00:34 -0700279 struct fc_rport_priv *rdata, *next;
Robert Love42e9a922008-12-09 15:10:17 -0800280 struct fc_lport *lport = disc->lport;
281
Robert Love74147052009-06-10 15:31:10 -0700282 FC_DISC_DBG(disc, "Restarting discovery\n");
Robert Love42e9a922008-12-09 15:10:17 -0800283
Joe Eykholt9e9d0452009-08-25 14:01:18 -0700284 list_for_each_entry_safe(rdata, next, &disc->rports, peers)
Joe Eykholt9fb9d322009-08-25 14:00:50 -0700285 lport->tt.rport_logoff(rdata);
Robert Love42e9a922008-12-09 15:10:17 -0800286
287 disc->requested = 1;
288 if (!disc->pending)
289 fc_disc_gpn_ft_req(disc);
290}
291
292/**
Robert Love34f42a02009-02-27 10:55:45 -0800293 * fc_disc_start() - Fibre Channel Target discovery
Robert Love42e9a922008-12-09 15:10:17 -0800294 * @lport: FC local port
295 *
296 * Returns non-zero if discovery cannot be started.
297 */
298static void fc_disc_start(void (*disc_callback)(struct fc_lport *,
299 enum fc_disc_event),
300 struct fc_lport *lport)
301{
Joe Eykholt9fb9d322009-08-25 14:00:50 -0700302 struct fc_rport_priv *rdata;
Robert Love42e9a922008-12-09 15:10:17 -0800303 struct fc_disc *disc = &lport->disc;
304
305 /*
306 * At this point we may have a new disc job or an existing
307 * one. Either way, let's lock when we make changes to it
308 * and send the GPN_FT request.
309 */
310 mutex_lock(&disc->disc_mutex);
311
312 disc->disc_callback = disc_callback;
313
314 /*
315 * If not ready, or already running discovery, just set request flag.
316 */
317 disc->requested = 1;
318
319 if (disc->pending) {
320 mutex_unlock(&disc->disc_mutex);
321 return;
322 }
323
324 /*
325 * Handle point-to-point mode as a simple discovery
326 * of the remote port. Yucky, yucky, yuck, yuck!
327 */
Joe Eykholt9fb9d322009-08-25 14:00:50 -0700328 rdata = disc->lport->ptp_rp;
329 if (rdata) {
Joe Eykholtf211fa52009-08-25 14:01:01 -0700330 kref_get(&rdata->kref);
331 if (!fc_disc_new_target(disc, rdata, &rdata->ids)) {
Robert Love42e9a922008-12-09 15:10:17 -0800332 disc->event = DISC_EV_SUCCESS;
333 fc_disc_done(disc);
334 }
Joe Eykholtf211fa52009-08-25 14:01:01 -0700335 kref_put(&rdata->kref, rdata->local_port->tt.rport_destroy);
Robert Love42e9a922008-12-09 15:10:17 -0800336 } else {
337 fc_disc_gpn_ft_req(disc); /* get ports by FC-4 type */
338 }
339
340 mutex_unlock(&disc->disc_mutex);
341}
342
343static struct fc_rport_operations fc_disc_rport_ops = {
344 .event_callback = fc_disc_rport_callback,
345};
346
347/**
Robert Love34f42a02009-02-27 10:55:45 -0800348 * fc_disc_new_target() - Handle new target found by discovery
Robert Love42e9a922008-12-09 15:10:17 -0800349 * @lport: FC local port
Joe Eykholtf211fa52009-08-25 14:01:01 -0700350 * @rdata: The previous FC remote port priv (NULL if new remote port)
Robert Love42e9a922008-12-09 15:10:17 -0800351 * @ids: Identifiers for the new FC remote port
352 *
353 * Locking Note: This function expects that the disc_mutex is locked
354 * before it is called.
355 */
356static int fc_disc_new_target(struct fc_disc *disc,
Joe Eykholtf211fa52009-08-25 14:01:01 -0700357 struct fc_rport_priv *rdata,
Robert Love42e9a922008-12-09 15:10:17 -0800358 struct fc_rport_identifiers *ids)
359{
360 struct fc_lport *lport = disc->lport;
Robert Love42e9a922008-12-09 15:10:17 -0800361 int error = 0;
362
Joe Eykholtf211fa52009-08-25 14:01:01 -0700363 if (rdata && ids->port_name) {
364 if (rdata->ids.port_name == -1) {
Robert Love42e9a922008-12-09 15:10:17 -0800365 /*
366 * Set WWN and fall through to notify of create.
367 */
Joe Eykholtf211fa52009-08-25 14:01:01 -0700368 rdata->ids.port_name = ids->port_name;
369 rdata->ids.node_name = ids->node_name;
370 } else if (rdata->ids.port_name != ids->port_name) {
Robert Love42e9a922008-12-09 15:10:17 -0800371 /*
372 * This is a new port with the same FCID as
373 * a previously-discovered port. Presumably the old
374 * port logged out and a new port logged in and was
375 * assigned the same FCID. This should be rare.
376 * Delete the old one and fall thru to re-create.
377 */
Joe Eykholt9fb9d322009-08-25 14:00:50 -0700378 lport->tt.rport_logoff(rdata);
Joe Eykholtf211fa52009-08-25 14:01:01 -0700379 rdata = NULL;
Robert Love42e9a922008-12-09 15:10:17 -0800380 }
381 }
382 if (((ids->port_name != -1) || (ids->port_id != -1)) &&
383 ids->port_id != fc_host_port_id(lport->host) &&
384 ids->port_name != lport->wwpn) {
Joe Eykholtf211fa52009-08-25 14:01:01 -0700385 if (!rdata) {
Joe Eykholt9fb9d322009-08-25 14:00:50 -0700386 rdata = lport->tt.rport_lookup(lport, ids->port_id);
Joe Eykholtf211fa52009-08-25 14:01:01 -0700387 if (!rdata) {
Joe Eykholt9fb9d322009-08-25 14:00:50 -0700388 rdata = lport->tt.rport_create(lport, ids);
Joe Eykholtf211fa52009-08-25 14:01:01 -0700389 if (!rdata)
390 error = -ENOMEM;
Joe Eykholt9e9d0452009-08-25 14:01:18 -0700391 else
392 list_add_tail(&rdata->peers,
393 &disc->rports);
Robert Love42e9a922008-12-09 15:10:17 -0800394 }
Robert Love42e9a922008-12-09 15:10:17 -0800395 }
Joe Eykholtf211fa52009-08-25 14:01:01 -0700396 if (rdata) {
Robert Loved3b33322009-02-27 10:55:29 -0800397 rdata->ops = &fc_disc_rport_ops;
Joe Eykholt9fb9d322009-08-25 14:00:50 -0700398 lport->tt.rport_login(rdata);
Robert Love42e9a922008-12-09 15:10:17 -0800399 }
400 }
401 return error;
402}
403
404/**
Robert Love34f42a02009-02-27 10:55:45 -0800405 * fc_disc_done() - Discovery has been completed
Robert Love42e9a922008-12-09 15:10:17 -0800406 * @disc: FC discovery context
Abhijeet Joglekar0d228c02009-04-21 16:26:52 -0700407 * Locking Note: This function expects that the disc mutex is locked before
408 * it is called. The discovery callback is then made with the lock released,
409 * and the lock is re-taken before returning from this function
Robert Love42e9a922008-12-09 15:10:17 -0800410 */
411static void fc_disc_done(struct fc_disc *disc)
412{
413 struct fc_lport *lport = disc->lport;
Abhijeet Joglekar0d228c02009-04-21 16:26:52 -0700414 enum fc_disc_event event;
Robert Love42e9a922008-12-09 15:10:17 -0800415
Robert Love74147052009-06-10 15:31:10 -0700416 FC_DISC_DBG(disc, "Discovery complete\n");
Robert Love42e9a922008-12-09 15:10:17 -0800417
Abhijeet Joglekar0d228c02009-04-21 16:26:52 -0700418 event = disc->event;
Robert Love42e9a922008-12-09 15:10:17 -0800419 disc->event = DISC_EV_NONE;
420
421 if (disc->requested)
422 fc_disc_gpn_ft_req(disc);
423 else
424 disc->pending = 0;
Abhijeet Joglekar0d228c02009-04-21 16:26:52 -0700425
426 mutex_unlock(&disc->disc_mutex);
427 disc->disc_callback(lport, event);
428 mutex_lock(&disc->disc_mutex);
Robert Love42e9a922008-12-09 15:10:17 -0800429}
430
431/**
Robert Love34f42a02009-02-27 10:55:45 -0800432 * fc_disc_error() - Handle error on dNS request
Robert Love42e9a922008-12-09 15:10:17 -0800433 * @disc: FC discovery context
434 * @fp: The frame pointer
435 */
436static void fc_disc_error(struct fc_disc *disc, struct fc_frame *fp)
437{
438 struct fc_lport *lport = disc->lport;
439 unsigned long delay = 0;
Robert Love74147052009-06-10 15:31:10 -0700440
441 FC_DISC_DBG(disc, "Error %ld, retries %d/%d\n",
442 PTR_ERR(fp), disc->retry_count,
443 FC_DISC_RETRY_LIMIT);
Robert Love42e9a922008-12-09 15:10:17 -0800444
445 if (!fp || PTR_ERR(fp) == -FC_EX_TIMEOUT) {
446 /*
447 * Memory allocation failure, or the exchange timed out,
448 * retry after delay.
449 */
450 if (disc->retry_count < FC_DISC_RETRY_LIMIT) {
451 /* go ahead and retry */
452 if (!fp)
453 delay = msecs_to_jiffies(FC_DISC_RETRY_DELAY);
454 else {
455 delay = msecs_to_jiffies(lport->e_d_tov);
456
457 /* timeout faster first time */
458 if (!disc->retry_count)
459 delay /= 4;
460 }
461 disc->retry_count++;
462 schedule_delayed_work(&disc->disc_work, delay);
463 } else {
464 /* exceeded retries */
465 disc->event = DISC_EV_FAILED;
466 fc_disc_done(disc);
467 }
468 }
469}
470
471/**
Robert Love34f42a02009-02-27 10:55:45 -0800472 * fc_disc_gpn_ft_req() - Send Get Port Names by FC-4 type (GPN_FT) request
Robert Love42e9a922008-12-09 15:10:17 -0800473 * @lport: FC discovery context
474 *
475 * Locking Note: This function expects that the disc_mutex is locked
476 * before it is called.
477 */
478static void fc_disc_gpn_ft_req(struct fc_disc *disc)
479{
480 struct fc_frame *fp;
481 struct fc_lport *lport = disc->lport;
482
483 WARN_ON(!fc_lport_test_ready(lport));
484
485 disc->pending = 1;
486 disc->requested = 0;
487
488 disc->buf_len = 0;
489 disc->seq_count = 0;
490 fp = fc_frame_alloc(lport,
491 sizeof(struct fc_ct_hdr) +
492 sizeof(struct fc_ns_gid_ft));
493 if (!fp)
494 goto err;
495
Joe Eykholta46f3272009-08-25 14:00:55 -0700496 if (lport->tt.elsct_send(lport, 0, fp,
Robert Love42e9a922008-12-09 15:10:17 -0800497 FC_NS_GPN_FT,
498 fc_disc_gpn_ft_resp,
499 disc, lport->e_d_tov))
500 return;
501err:
502 fc_disc_error(disc, fp);
503}
504
505/**
Robert Love34f42a02009-02-27 10:55:45 -0800506 * fc_disc_gpn_ft_parse() - Parse the list of IDs and names resulting from a request
Robert Love42e9a922008-12-09 15:10:17 -0800507 * @lport: Fibre Channel host port instance
508 * @buf: GPN_FT response buffer
509 * @len: size of response buffer
510 */
511static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len)
512{
513 struct fc_lport *lport;
514 struct fc_gpn_ft_resp *np;
515 char *bp;
516 size_t plen;
517 size_t tlen;
518 int error = 0;
Joe Eykholt795d86f2009-08-25 14:00:39 -0700519 struct fc_rport_identifiers ids;
Joe Eykholtab28f1f2009-08-25 14:00:34 -0700520 struct fc_rport_priv *rdata;
Robert Love42e9a922008-12-09 15:10:17 -0800521
522 lport = disc->lport;
523
524 /*
525 * Handle partial name record left over from previous call.
526 */
527 bp = buf;
528 plen = len;
529 np = (struct fc_gpn_ft_resp *)bp;
530 tlen = disc->buf_len;
531 if (tlen) {
532 WARN_ON(tlen >= sizeof(*np));
533 plen = sizeof(*np) - tlen;
534 WARN_ON(plen <= 0);
535 WARN_ON(plen >= sizeof(*np));
536 if (plen > len)
537 plen = len;
538 np = &disc->partial_buf;
539 memcpy((char *)np + tlen, bp, plen);
540
541 /*
542 * Set bp so that the loop below will advance it to the
543 * first valid full name element.
544 */
545 bp -= tlen;
546 len += tlen;
547 plen += tlen;
548 disc->buf_len = (unsigned char) plen;
549 if (plen == sizeof(*np))
550 disc->buf_len = 0;
551 }
552
553 /*
554 * Handle full name records, including the one filled from above.
555 * Normally, np == bp and plen == len, but from the partial case above,
556 * bp, len describe the overall buffer, and np, plen describe the
557 * partial buffer, which if would usually be full now.
558 * After the first time through the loop, things return to "normal".
559 */
560 while (plen >= sizeof(*np)) {
Joe Eykholt795d86f2009-08-25 14:00:39 -0700561 ids.port_id = ntoh24(np->fp_fid);
562 ids.port_name = ntohll(np->fp_wwpn);
563 ids.node_name = -1;
564 ids.roles = FC_RPORT_ROLE_UNKNOWN;
Robert Love42e9a922008-12-09 15:10:17 -0800565
Joe Eykholt795d86f2009-08-25 14:00:39 -0700566 if (ids.port_id != fc_host_port_id(lport->host) &&
567 ids.port_name != lport->wwpn) {
Joe Eykholt9fb9d322009-08-25 14:00:50 -0700568 rdata = lport->tt.rport_create(lport, &ids);
569 if (rdata) {
Robert Love42e9a922008-12-09 15:10:17 -0800570 rdata->ops = &fc_disc_rport_ops;
Joe Eykholt9e9d0452009-08-25 14:01:18 -0700571 list_add_tail(&rdata->peers, &disc->rports);
Joe Eykholt9fb9d322009-08-25 14:00:50 -0700572 lport->tt.rport_login(rdata);
Robert Love42e9a922008-12-09 15:10:17 -0800573 } else
Robert Love74147052009-06-10 15:31:10 -0700574 printk(KERN_WARNING "libfc: Failed to allocate "
575 "memory for the newly discovered port "
Joe Eykholt795d86f2009-08-25 14:00:39 -0700576 "(%6x)\n", ids.port_id);
Robert Love42e9a922008-12-09 15:10:17 -0800577 }
578
579 if (np->fp_flags & FC_NS_FID_LAST) {
580 disc->event = DISC_EV_SUCCESS;
581 fc_disc_done(disc);
582 len = 0;
583 break;
584 }
585 len -= sizeof(*np);
586 bp += sizeof(*np);
587 np = (struct fc_gpn_ft_resp *)bp;
588 plen = len;
589 }
590
591 /*
592 * Save any partial record at the end of the buffer for next time.
593 */
594 if (error == 0 && len > 0 && len < sizeof(*np)) {
595 if (np != &disc->partial_buf) {
Robert Love74147052009-06-10 15:31:10 -0700596 FC_DISC_DBG(disc, "Partial buffer remains "
597 "for discovery\n");
Robert Love42e9a922008-12-09 15:10:17 -0800598 memcpy(&disc->partial_buf, np, len);
599 }
600 disc->buf_len = (unsigned char) len;
601 } else {
602 disc->buf_len = 0;
603 }
604 return error;
605}
606
Robert Love34f42a02009-02-27 10:55:45 -0800607/**
608 * fc_disc_timeout() - Retry handler for the disc component
609 * @work: Structure holding disc obj that needs retry discovery
610 *
Robert Love42e9a922008-12-09 15:10:17 -0800611 * Handle retry of memory allocation for remote ports.
612 */
613static void fc_disc_timeout(struct work_struct *work)
614{
615 struct fc_disc *disc = container_of(work,
616 struct fc_disc,
617 disc_work.work);
618 mutex_lock(&disc->disc_mutex);
619 if (disc->requested && !disc->pending)
620 fc_disc_gpn_ft_req(disc);
621 mutex_unlock(&disc->disc_mutex);
622}
623
624/**
Robert Love34f42a02009-02-27 10:55:45 -0800625 * fc_disc_gpn_ft_resp() - Handle a response frame from Get Port Names (GPN_FT)
Robert Love42e9a922008-12-09 15:10:17 -0800626 * @sp: Current sequence of GPN_FT exchange
627 * @fp: response frame
628 * @lp_arg: Fibre Channel host port instance
629 *
Abhijeet Joglekar0d228c02009-04-21 16:26:52 -0700630 * Locking Note: This function is called without disc mutex held, and
631 * should do all its processing with the mutex held
Robert Love42e9a922008-12-09 15:10:17 -0800632 */
633static void fc_disc_gpn_ft_resp(struct fc_seq *sp, struct fc_frame *fp,
634 void *disc_arg)
635{
636 struct fc_disc *disc = disc_arg;
637 struct fc_ct_hdr *cp;
638 struct fc_frame_header *fh;
639 unsigned int seq_cnt;
640 void *buf = NULL;
641 unsigned int len;
642 int error;
643
Abhijeet Joglekar0d228c02009-04-21 16:26:52 -0700644 mutex_lock(&disc->disc_mutex);
Robert Love74147052009-06-10 15:31:10 -0700645 FC_DISC_DBG(disc, "Received a GPN_FT response\n");
Robert Love42e9a922008-12-09 15:10:17 -0800646
647 if (IS_ERR(fp)) {
648 fc_disc_error(disc, fp);
Abhijeet Joglekar0d228c02009-04-21 16:26:52 -0700649 mutex_unlock(&disc->disc_mutex);
Robert Love42e9a922008-12-09 15:10:17 -0800650 return;
651 }
652
653 WARN_ON(!fc_frame_is_linear(fp)); /* buffer must be contiguous */
654 fh = fc_frame_header_get(fp);
655 len = fr_len(fp) - sizeof(*fh);
656 seq_cnt = ntohs(fh->fh_seq_cnt);
657 if (fr_sof(fp) == FC_SOF_I3 && seq_cnt == 0 &&
658 disc->seq_count == 0) {
659 cp = fc_frame_payload_get(fp, sizeof(*cp));
660 if (!cp) {
Robert Love74147052009-06-10 15:31:10 -0700661 FC_DISC_DBG(disc, "GPN_FT response too short, len %d\n",
662 fr_len(fp));
Robert Love42e9a922008-12-09 15:10:17 -0800663 } else if (ntohs(cp->ct_cmd) == FC_FS_ACC) {
664
Robert Love34f42a02009-02-27 10:55:45 -0800665 /* Accepted, parse the response. */
Robert Love42e9a922008-12-09 15:10:17 -0800666 buf = cp + 1;
667 len -= sizeof(*cp);
668 } else if (ntohs(cp->ct_cmd) == FC_FS_RJT) {
Robert Love74147052009-06-10 15:31:10 -0700669 FC_DISC_DBG(disc, "GPN_FT rejected reason %x exp %x "
670 "(check zoning)\n", cp->ct_reason,
671 cp->ct_explan);
Robert Love42e9a922008-12-09 15:10:17 -0800672 disc->event = DISC_EV_FAILED;
673 fc_disc_done(disc);
674 } else {
Robert Love74147052009-06-10 15:31:10 -0700675 FC_DISC_DBG(disc, "GPN_FT unexpected response code "
676 "%x\n", ntohs(cp->ct_cmd));
Robert Love42e9a922008-12-09 15:10:17 -0800677 }
678 } else if (fr_sof(fp) == FC_SOF_N3 &&
679 seq_cnt == disc->seq_count) {
680 buf = fh + 1;
681 } else {
Robert Love74147052009-06-10 15:31:10 -0700682 FC_DISC_DBG(disc, "GPN_FT unexpected frame - out of sequence? "
683 "seq_cnt %x expected %x sof %x eof %x\n",
684 seq_cnt, disc->seq_count, fr_sof(fp), fr_eof(fp));
Robert Love42e9a922008-12-09 15:10:17 -0800685 }
686 if (buf) {
687 error = fc_disc_gpn_ft_parse(disc, buf, len);
688 if (error)
689 fc_disc_error(disc, fp);
690 else
691 disc->seq_count++;
692 }
693 fc_frame_free(fp);
Abhijeet Joglekar0d228c02009-04-21 16:26:52 -0700694
695 mutex_unlock(&disc->disc_mutex);
Robert Love42e9a922008-12-09 15:10:17 -0800696}
697
698/**
Robert Love34f42a02009-02-27 10:55:45 -0800699 * fc_disc_single() - Discover the directory information for a single target
Robert Love42e9a922008-12-09 15:10:17 -0800700 * @lport: FC local port
701 * @dp: The port to rediscover
702 *
703 * Locking Note: This function expects that the disc_mutex is locked
704 * before it is called.
705 */
706static void fc_disc_single(struct fc_disc *disc, struct fc_disc_port *dp)
707{
708 struct fc_lport *lport;
Joe Eykholtab28f1f2009-08-25 14:00:34 -0700709 struct fc_rport_priv *rdata;
Robert Love42e9a922008-12-09 15:10:17 -0800710
711 lport = disc->lport;
712
713 if (dp->ids.port_id == fc_host_port_id(lport->host))
714 goto out;
715
Joe Eykholt9fb9d322009-08-25 14:00:50 -0700716 rdata = lport->tt.rport_create(lport, &dp->ids);
717 if (rdata) {
Robert Love42e9a922008-12-09 15:10:17 -0800718 rdata->ops = &fc_disc_rport_ops;
719 kfree(dp);
Joe Eykholt9e9d0452009-08-25 14:01:18 -0700720 list_add_tail(&rdata->peers, &disc->rports);
Joe Eykholt9fb9d322009-08-25 14:00:50 -0700721 lport->tt.rport_login(rdata);
Robert Love42e9a922008-12-09 15:10:17 -0800722 }
723 return;
724out:
725 kfree(dp);
726}
727
728/**
Robert Love34f42a02009-02-27 10:55:45 -0800729 * fc_disc_stop() - Stop discovery for a given lport
Robert Love42e9a922008-12-09 15:10:17 -0800730 * @lport: The lport that discovery should stop for
731 */
732void fc_disc_stop(struct fc_lport *lport)
733{
734 struct fc_disc *disc = &lport->disc;
735
736 if (disc) {
737 cancel_delayed_work_sync(&disc->disc_work);
738 fc_disc_stop_rports(disc);
739 }
740}
741
742/**
Robert Love34f42a02009-02-27 10:55:45 -0800743 * fc_disc_stop_final() - Stop discovery for a given lport
Robert Love42e9a922008-12-09 15:10:17 -0800744 * @lport: The lport that discovery should stop for
745 *
746 * This function will block until discovery has been
747 * completely stopped and all rports have been deleted.
748 */
749void fc_disc_stop_final(struct fc_lport *lport)
750{
751 fc_disc_stop(lport);
752 lport->tt.rport_flush_queue();
753}
754
755/**
Robert Love34f42a02009-02-27 10:55:45 -0800756 * fc_disc_init() - Initialize the discovery block
Robert Love42e9a922008-12-09 15:10:17 -0800757 * @lport: FC local port
758 */
759int fc_disc_init(struct fc_lport *lport)
760{
761 struct fc_disc *disc;
762
763 if (!lport->tt.disc_start)
764 lport->tt.disc_start = fc_disc_start;
765
766 if (!lport->tt.disc_stop)
767 lport->tt.disc_stop = fc_disc_stop;
768
769 if (!lport->tt.disc_stop_final)
770 lport->tt.disc_stop_final = fc_disc_stop_final;
771
772 if (!lport->tt.disc_recv_req)
773 lport->tt.disc_recv_req = fc_disc_recv_req;
774
775 if (!lport->tt.rport_lookup)
776 lport->tt.rport_lookup = fc_disc_lookup_rport;
777
778 disc = &lport->disc;
779 INIT_DELAYED_WORK(&disc->disc_work, fc_disc_timeout);
780 mutex_init(&disc->disc_mutex);
781 INIT_LIST_HEAD(&disc->rports);
Robert Love42e9a922008-12-09 15:10:17 -0800782
783 disc->lport = lport;
784 disc->delay = FC_DISC_DELAY;
785 disc->event = DISC_EV_NONE;
786
787 return 0;
788}
789EXPORT_SYMBOL(fc_disc_init);