blob: 736f9174206446917a702b4e7e451bf4a8f03c36 [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 *);
Joe Eykholt786681b2009-08-25 14:01:29 -070052static void fc_disc_done(struct fc_disc *, enum fc_disc_event);
Robert Love42e9a922008-12-09 15:10:17 -080053static 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)) {
Joe Eykholt786681b2009-08-25 14:01:29 -0700332 fc_disc_done(disc, DISC_EV_SUCCESS);
Robert Love42e9a922008-12-09 15:10:17 -0800333 }
Joe Eykholtf211fa52009-08-25 14:01:01 -0700334 kref_put(&rdata->kref, rdata->local_port->tt.rport_destroy);
Robert Love42e9a922008-12-09 15:10:17 -0800335 } else {
336 fc_disc_gpn_ft_req(disc); /* get ports by FC-4 type */
337 }
338
339 mutex_unlock(&disc->disc_mutex);
340}
341
342static struct fc_rport_operations fc_disc_rport_ops = {
343 .event_callback = fc_disc_rport_callback,
344};
345
346/**
Robert Love34f42a02009-02-27 10:55:45 -0800347 * fc_disc_new_target() - Handle new target found by discovery
Robert Love42e9a922008-12-09 15:10:17 -0800348 * @lport: FC local port
Joe Eykholtf211fa52009-08-25 14:01:01 -0700349 * @rdata: The previous FC remote port priv (NULL if new remote port)
Robert Love42e9a922008-12-09 15:10:17 -0800350 * @ids: Identifiers for the new FC remote port
351 *
352 * Locking Note: This function expects that the disc_mutex is locked
353 * before it is called.
354 */
355static int fc_disc_new_target(struct fc_disc *disc,
Joe Eykholtf211fa52009-08-25 14:01:01 -0700356 struct fc_rport_priv *rdata,
Robert Love42e9a922008-12-09 15:10:17 -0800357 struct fc_rport_identifiers *ids)
358{
359 struct fc_lport *lport = disc->lport;
Robert Love42e9a922008-12-09 15:10:17 -0800360 int error = 0;
361
Joe Eykholtf211fa52009-08-25 14:01:01 -0700362 if (rdata && ids->port_name) {
363 if (rdata->ids.port_name == -1) {
Robert Love42e9a922008-12-09 15:10:17 -0800364 /*
365 * Set WWN and fall through to notify of create.
366 */
Joe Eykholtf211fa52009-08-25 14:01:01 -0700367 rdata->ids.port_name = ids->port_name;
368 rdata->ids.node_name = ids->node_name;
369 } else if (rdata->ids.port_name != ids->port_name) {
Robert Love42e9a922008-12-09 15:10:17 -0800370 /*
371 * This is a new port with the same FCID as
372 * a previously-discovered port. Presumably the old
373 * port logged out and a new port logged in and was
374 * assigned the same FCID. This should be rare.
375 * Delete the old one and fall thru to re-create.
376 */
Joe Eykholt9fb9d322009-08-25 14:00:50 -0700377 lport->tt.rport_logoff(rdata);
Joe Eykholtf211fa52009-08-25 14:01:01 -0700378 rdata = NULL;
Robert Love42e9a922008-12-09 15:10:17 -0800379 }
380 }
381 if (((ids->port_name != -1) || (ids->port_id != -1)) &&
382 ids->port_id != fc_host_port_id(lport->host) &&
383 ids->port_name != lport->wwpn) {
Joe Eykholtf211fa52009-08-25 14:01:01 -0700384 if (!rdata) {
Joe Eykholt9fb9d322009-08-25 14:00:50 -0700385 rdata = lport->tt.rport_lookup(lport, ids->port_id);
Joe Eykholtf211fa52009-08-25 14:01:01 -0700386 if (!rdata) {
Joe Eykholt9fb9d322009-08-25 14:00:50 -0700387 rdata = lport->tt.rport_create(lport, ids);
Joe Eykholtf211fa52009-08-25 14:01:01 -0700388 if (!rdata)
389 error = -ENOMEM;
Joe Eykholt9e9d0452009-08-25 14:01:18 -0700390 else
391 list_add_tail(&rdata->peers,
392 &disc->rports);
Robert Love42e9a922008-12-09 15:10:17 -0800393 }
Robert Love42e9a922008-12-09 15:10:17 -0800394 }
Joe Eykholtf211fa52009-08-25 14:01:01 -0700395 if (rdata) {
Robert Loved3b33322009-02-27 10:55:29 -0800396 rdata->ops = &fc_disc_rport_ops;
Joe Eykholt9fb9d322009-08-25 14:00:50 -0700397 lport->tt.rport_login(rdata);
Robert Love42e9a922008-12-09 15:10:17 -0800398 }
399 }
400 return error;
401}
402
403/**
Robert Love34f42a02009-02-27 10:55:45 -0800404 * fc_disc_done() - Discovery has been completed
Robert Love42e9a922008-12-09 15:10:17 -0800405 * @disc: FC discovery context
Joe Eykholt786681b2009-08-25 14:01:29 -0700406 * @event: discovery completion status
407 *
Abhijeet Joglekar0d228c02009-04-21 16:26:52 -0700408 * Locking Note: This function expects that the disc mutex is locked before
409 * it is called. The discovery callback is then made with the lock released,
410 * and the lock is re-taken before returning from this function
Robert Love42e9a922008-12-09 15:10:17 -0800411 */
Joe Eykholt786681b2009-08-25 14:01:29 -0700412static void fc_disc_done(struct fc_disc *disc, enum fc_disc_event event)
Robert Love42e9a922008-12-09 15:10:17 -0800413{
414 struct fc_lport *lport = disc->lport;
415
Robert Love74147052009-06-10 15:31:10 -0700416 FC_DISC_DBG(disc, "Discovery complete\n");
Robert Love42e9a922008-12-09 15:10:17 -0800417
Robert Love42e9a922008-12-09 15:10:17 -0800418 if (disc->requested)
419 fc_disc_gpn_ft_req(disc);
420 else
421 disc->pending = 0;
Abhijeet Joglekar0d228c02009-04-21 16:26:52 -0700422
423 mutex_unlock(&disc->disc_mutex);
424 disc->disc_callback(lport, event);
425 mutex_lock(&disc->disc_mutex);
Robert Love42e9a922008-12-09 15:10:17 -0800426}
427
428/**
Robert Love34f42a02009-02-27 10:55:45 -0800429 * fc_disc_error() - Handle error on dNS request
Robert Love42e9a922008-12-09 15:10:17 -0800430 * @disc: FC discovery context
431 * @fp: The frame pointer
432 */
433static void fc_disc_error(struct fc_disc *disc, struct fc_frame *fp)
434{
435 struct fc_lport *lport = disc->lport;
436 unsigned long delay = 0;
Robert Love74147052009-06-10 15:31:10 -0700437
438 FC_DISC_DBG(disc, "Error %ld, retries %d/%d\n",
439 PTR_ERR(fp), disc->retry_count,
440 FC_DISC_RETRY_LIMIT);
Robert Love42e9a922008-12-09 15:10:17 -0800441
442 if (!fp || PTR_ERR(fp) == -FC_EX_TIMEOUT) {
443 /*
444 * Memory allocation failure, or the exchange timed out,
445 * retry after delay.
446 */
447 if (disc->retry_count < FC_DISC_RETRY_LIMIT) {
448 /* go ahead and retry */
449 if (!fp)
450 delay = msecs_to_jiffies(FC_DISC_RETRY_DELAY);
451 else {
452 delay = msecs_to_jiffies(lport->e_d_tov);
453
454 /* timeout faster first time */
455 if (!disc->retry_count)
456 delay /= 4;
457 }
458 disc->retry_count++;
459 schedule_delayed_work(&disc->disc_work, delay);
Joe Eykholt786681b2009-08-25 14:01:29 -0700460 } else
461 fc_disc_done(disc, DISC_EV_FAILED);
Robert Love42e9a922008-12-09 15:10:17 -0800462 }
463}
464
465/**
Robert Love34f42a02009-02-27 10:55:45 -0800466 * fc_disc_gpn_ft_req() - Send Get Port Names by FC-4 type (GPN_FT) request
Robert Love42e9a922008-12-09 15:10:17 -0800467 * @lport: FC discovery context
468 *
469 * Locking Note: This function expects that the disc_mutex is locked
470 * before it is called.
471 */
472static void fc_disc_gpn_ft_req(struct fc_disc *disc)
473{
474 struct fc_frame *fp;
475 struct fc_lport *lport = disc->lport;
476
477 WARN_ON(!fc_lport_test_ready(lport));
478
479 disc->pending = 1;
480 disc->requested = 0;
481
482 disc->buf_len = 0;
483 disc->seq_count = 0;
484 fp = fc_frame_alloc(lport,
485 sizeof(struct fc_ct_hdr) +
486 sizeof(struct fc_ns_gid_ft));
487 if (!fp)
488 goto err;
489
Joe Eykholta46f3272009-08-25 14:00:55 -0700490 if (lport->tt.elsct_send(lport, 0, fp,
Robert Love42e9a922008-12-09 15:10:17 -0800491 FC_NS_GPN_FT,
492 fc_disc_gpn_ft_resp,
493 disc, lport->e_d_tov))
494 return;
495err:
496 fc_disc_error(disc, fp);
497}
498
499/**
Joe Eykholt786681b2009-08-25 14:01:29 -0700500 * fc_disc_gpn_ft_parse() - Parse the body of the dNS GPN_FT response.
Robert Love42e9a922008-12-09 15:10:17 -0800501 * @lport: Fibre Channel host port instance
502 * @buf: GPN_FT response buffer
503 * @len: size of response buffer
Joe Eykholt786681b2009-08-25 14:01:29 -0700504 *
505 * Goes through the list of IDs and names resulting from a request.
Robert Love42e9a922008-12-09 15:10:17 -0800506 */
507static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len)
508{
509 struct fc_lport *lport;
510 struct fc_gpn_ft_resp *np;
511 char *bp;
512 size_t plen;
513 size_t tlen;
514 int error = 0;
Joe Eykholt795d86f2009-08-25 14:00:39 -0700515 struct fc_rport_identifiers ids;
Joe Eykholtab28f1f2009-08-25 14:00:34 -0700516 struct fc_rport_priv *rdata;
Robert Love42e9a922008-12-09 15:10:17 -0800517
518 lport = disc->lport;
519
520 /*
521 * Handle partial name record left over from previous call.
522 */
523 bp = buf;
524 plen = len;
525 np = (struct fc_gpn_ft_resp *)bp;
526 tlen = disc->buf_len;
527 if (tlen) {
528 WARN_ON(tlen >= sizeof(*np));
529 plen = sizeof(*np) - tlen;
530 WARN_ON(plen <= 0);
531 WARN_ON(plen >= sizeof(*np));
532 if (plen > len)
533 plen = len;
534 np = &disc->partial_buf;
535 memcpy((char *)np + tlen, bp, plen);
536
537 /*
538 * Set bp so that the loop below will advance it to the
539 * first valid full name element.
540 */
541 bp -= tlen;
542 len += tlen;
543 plen += tlen;
544 disc->buf_len = (unsigned char) plen;
545 if (plen == sizeof(*np))
546 disc->buf_len = 0;
547 }
548
549 /*
550 * Handle full name records, including the one filled from above.
551 * Normally, np == bp and plen == len, but from the partial case above,
552 * bp, len describe the overall buffer, and np, plen describe the
553 * partial buffer, which if would usually be full now.
554 * After the first time through the loop, things return to "normal".
555 */
556 while (plen >= sizeof(*np)) {
Joe Eykholt795d86f2009-08-25 14:00:39 -0700557 ids.port_id = ntoh24(np->fp_fid);
558 ids.port_name = ntohll(np->fp_wwpn);
559 ids.node_name = -1;
560 ids.roles = FC_RPORT_ROLE_UNKNOWN;
Robert Love42e9a922008-12-09 15:10:17 -0800561
Joe Eykholt795d86f2009-08-25 14:00:39 -0700562 if (ids.port_id != fc_host_port_id(lport->host) &&
563 ids.port_name != lport->wwpn) {
Joe Eykholt9fb9d322009-08-25 14:00:50 -0700564 rdata = lport->tt.rport_create(lport, &ids);
565 if (rdata) {
Robert Love42e9a922008-12-09 15:10:17 -0800566 rdata->ops = &fc_disc_rport_ops;
Joe Eykholt9e9d0452009-08-25 14:01:18 -0700567 list_add_tail(&rdata->peers, &disc->rports);
Joe Eykholt9fb9d322009-08-25 14:00:50 -0700568 lport->tt.rport_login(rdata);
Robert Love42e9a922008-12-09 15:10:17 -0800569 } else
Robert Love74147052009-06-10 15:31:10 -0700570 printk(KERN_WARNING "libfc: Failed to allocate "
571 "memory for the newly discovered port "
Joe Eykholt795d86f2009-08-25 14:00:39 -0700572 "(%6x)\n", ids.port_id);
Robert Love42e9a922008-12-09 15:10:17 -0800573 }
574
575 if (np->fp_flags & FC_NS_FID_LAST) {
Joe Eykholt786681b2009-08-25 14:01:29 -0700576 fc_disc_done(disc, DISC_EV_SUCCESS);
Robert Love42e9a922008-12-09 15:10:17 -0800577 len = 0;
578 break;
579 }
580 len -= sizeof(*np);
581 bp += sizeof(*np);
582 np = (struct fc_gpn_ft_resp *)bp;
583 plen = len;
584 }
585
586 /*
587 * Save any partial record at the end of the buffer for next time.
588 */
589 if (error == 0 && len > 0 && len < sizeof(*np)) {
590 if (np != &disc->partial_buf) {
Robert Love74147052009-06-10 15:31:10 -0700591 FC_DISC_DBG(disc, "Partial buffer remains "
592 "for discovery\n");
Robert Love42e9a922008-12-09 15:10:17 -0800593 memcpy(&disc->partial_buf, np, len);
594 }
595 disc->buf_len = (unsigned char) len;
596 } else {
597 disc->buf_len = 0;
598 }
599 return error;
600}
601
Robert Love34f42a02009-02-27 10:55:45 -0800602/**
603 * fc_disc_timeout() - Retry handler for the disc component
604 * @work: Structure holding disc obj that needs retry discovery
605 *
Robert Love42e9a922008-12-09 15:10:17 -0800606 * Handle retry of memory allocation for remote ports.
607 */
608static void fc_disc_timeout(struct work_struct *work)
609{
610 struct fc_disc *disc = container_of(work,
611 struct fc_disc,
612 disc_work.work);
613 mutex_lock(&disc->disc_mutex);
614 if (disc->requested && !disc->pending)
615 fc_disc_gpn_ft_req(disc);
616 mutex_unlock(&disc->disc_mutex);
617}
618
619/**
Robert Love34f42a02009-02-27 10:55:45 -0800620 * fc_disc_gpn_ft_resp() - Handle a response frame from Get Port Names (GPN_FT)
Robert Love42e9a922008-12-09 15:10:17 -0800621 * @sp: Current sequence of GPN_FT exchange
622 * @fp: response frame
623 * @lp_arg: Fibre Channel host port instance
624 *
Abhijeet Joglekar0d228c02009-04-21 16:26:52 -0700625 * Locking Note: This function is called without disc mutex held, and
626 * should do all its processing with the mutex held
Robert Love42e9a922008-12-09 15:10:17 -0800627 */
628static void fc_disc_gpn_ft_resp(struct fc_seq *sp, struct fc_frame *fp,
629 void *disc_arg)
630{
631 struct fc_disc *disc = disc_arg;
632 struct fc_ct_hdr *cp;
633 struct fc_frame_header *fh;
634 unsigned int seq_cnt;
635 void *buf = NULL;
636 unsigned int len;
637 int error;
638
Abhijeet Joglekar0d228c02009-04-21 16:26:52 -0700639 mutex_lock(&disc->disc_mutex);
Robert Love74147052009-06-10 15:31:10 -0700640 FC_DISC_DBG(disc, "Received a GPN_FT response\n");
Robert Love42e9a922008-12-09 15:10:17 -0800641
642 if (IS_ERR(fp)) {
643 fc_disc_error(disc, fp);
Abhijeet Joglekar0d228c02009-04-21 16:26:52 -0700644 mutex_unlock(&disc->disc_mutex);
Robert Love42e9a922008-12-09 15:10:17 -0800645 return;
646 }
647
648 WARN_ON(!fc_frame_is_linear(fp)); /* buffer must be contiguous */
649 fh = fc_frame_header_get(fp);
650 len = fr_len(fp) - sizeof(*fh);
651 seq_cnt = ntohs(fh->fh_seq_cnt);
652 if (fr_sof(fp) == FC_SOF_I3 && seq_cnt == 0 &&
653 disc->seq_count == 0) {
654 cp = fc_frame_payload_get(fp, sizeof(*cp));
655 if (!cp) {
Robert Love74147052009-06-10 15:31:10 -0700656 FC_DISC_DBG(disc, "GPN_FT response too short, len %d\n",
657 fr_len(fp));
Robert Love42e9a922008-12-09 15:10:17 -0800658 } else if (ntohs(cp->ct_cmd) == FC_FS_ACC) {
659
Robert Love34f42a02009-02-27 10:55:45 -0800660 /* Accepted, parse the response. */
Robert Love42e9a922008-12-09 15:10:17 -0800661 buf = cp + 1;
662 len -= sizeof(*cp);
663 } else if (ntohs(cp->ct_cmd) == FC_FS_RJT) {
Robert Love74147052009-06-10 15:31:10 -0700664 FC_DISC_DBG(disc, "GPN_FT rejected reason %x exp %x "
665 "(check zoning)\n", cp->ct_reason,
666 cp->ct_explan);
Joe Eykholt786681b2009-08-25 14:01:29 -0700667 fc_disc_done(disc, DISC_EV_FAILED);
Robert Love42e9a922008-12-09 15:10:17 -0800668 } else {
Robert Love74147052009-06-10 15:31:10 -0700669 FC_DISC_DBG(disc, "GPN_FT unexpected response code "
670 "%x\n", ntohs(cp->ct_cmd));
Robert Love42e9a922008-12-09 15:10:17 -0800671 }
672 } else if (fr_sof(fp) == FC_SOF_N3 &&
673 seq_cnt == disc->seq_count) {
674 buf = fh + 1;
675 } else {
Robert Love74147052009-06-10 15:31:10 -0700676 FC_DISC_DBG(disc, "GPN_FT unexpected frame - out of sequence? "
677 "seq_cnt %x expected %x sof %x eof %x\n",
678 seq_cnt, disc->seq_count, fr_sof(fp), fr_eof(fp));
Robert Love42e9a922008-12-09 15:10:17 -0800679 }
680 if (buf) {
681 error = fc_disc_gpn_ft_parse(disc, buf, len);
682 if (error)
683 fc_disc_error(disc, fp);
684 else
685 disc->seq_count++;
686 }
687 fc_frame_free(fp);
Abhijeet Joglekar0d228c02009-04-21 16:26:52 -0700688
689 mutex_unlock(&disc->disc_mutex);
Robert Love42e9a922008-12-09 15:10:17 -0800690}
691
692/**
Robert Love34f42a02009-02-27 10:55:45 -0800693 * fc_disc_single() - Discover the directory information for a single target
Robert Love42e9a922008-12-09 15:10:17 -0800694 * @lport: FC local port
695 * @dp: The port to rediscover
696 *
697 * Locking Note: This function expects that the disc_mutex is locked
698 * before it is called.
699 */
700static void fc_disc_single(struct fc_disc *disc, struct fc_disc_port *dp)
701{
702 struct fc_lport *lport;
Joe Eykholtab28f1f2009-08-25 14:00:34 -0700703 struct fc_rport_priv *rdata;
Robert Love42e9a922008-12-09 15:10:17 -0800704
705 lport = disc->lport;
706
707 if (dp->ids.port_id == fc_host_port_id(lport->host))
708 goto out;
709
Joe Eykholt9fb9d322009-08-25 14:00:50 -0700710 rdata = lport->tt.rport_create(lport, &dp->ids);
711 if (rdata) {
Robert Love42e9a922008-12-09 15:10:17 -0800712 rdata->ops = &fc_disc_rport_ops;
713 kfree(dp);
Joe Eykholt9e9d0452009-08-25 14:01:18 -0700714 list_add_tail(&rdata->peers, &disc->rports);
Joe Eykholt9fb9d322009-08-25 14:00:50 -0700715 lport->tt.rport_login(rdata);
Robert Love42e9a922008-12-09 15:10:17 -0800716 }
717 return;
718out:
719 kfree(dp);
720}
721
722/**
Robert Love34f42a02009-02-27 10:55:45 -0800723 * fc_disc_stop() - Stop discovery for a given lport
Robert Love42e9a922008-12-09 15:10:17 -0800724 * @lport: The lport that discovery should stop for
725 */
726void fc_disc_stop(struct fc_lport *lport)
727{
728 struct fc_disc *disc = &lport->disc;
729
730 if (disc) {
731 cancel_delayed_work_sync(&disc->disc_work);
732 fc_disc_stop_rports(disc);
733 }
734}
735
736/**
Robert Love34f42a02009-02-27 10:55:45 -0800737 * fc_disc_stop_final() - Stop discovery for a given lport
Robert Love42e9a922008-12-09 15:10:17 -0800738 * @lport: The lport that discovery should stop for
739 *
740 * This function will block until discovery has been
741 * completely stopped and all rports have been deleted.
742 */
743void fc_disc_stop_final(struct fc_lport *lport)
744{
745 fc_disc_stop(lport);
746 lport->tt.rport_flush_queue();
747}
748
749/**
Robert Love34f42a02009-02-27 10:55:45 -0800750 * fc_disc_init() - Initialize the discovery block
Robert Love42e9a922008-12-09 15:10:17 -0800751 * @lport: FC local port
752 */
753int fc_disc_init(struct fc_lport *lport)
754{
755 struct fc_disc *disc;
756
757 if (!lport->tt.disc_start)
758 lport->tt.disc_start = fc_disc_start;
759
760 if (!lport->tt.disc_stop)
761 lport->tt.disc_stop = fc_disc_stop;
762
763 if (!lport->tt.disc_stop_final)
764 lport->tt.disc_stop_final = fc_disc_stop_final;
765
766 if (!lport->tt.disc_recv_req)
767 lport->tt.disc_recv_req = fc_disc_recv_req;
768
769 if (!lport->tt.rport_lookup)
770 lport->tt.rport_lookup = fc_disc_lookup_rport;
771
772 disc = &lport->disc;
773 INIT_DELAYED_WORK(&disc->disc_work, fc_disc_timeout);
774 mutex_init(&disc->disc_mutex);
775 INIT_LIST_HEAD(&disc->rports);
Robert Love42e9a922008-12-09 15:10:17 -0800776
777 disc->lport = lport;
778 disc->delay = FC_DISC_DELAY;
Robert Love42e9a922008-12-09 15:10:17 -0800779
780 return 0;
781}
782EXPORT_SYMBOL(fc_disc_init);