Commit | Line | Data |
---|---|---|
a3667aae NKI |
1 | /* |
2 | * This file is part of the Chelsio FCoE driver for Linux. | |
3 | * | |
4 | * Copyright (c) 2008-2012 Chelsio Communications, Inc. All rights reserved. | |
5 | * | |
6 | * This software is available to you under a choice of one of two | |
7 | * licenses. You may choose to be licensed under the terms of the GNU | |
8 | * General Public License (GPL) Version 2, available from the file | |
9 | * COPYING in the main directory of this source tree, or the | |
10 | * OpenIB.org BSD license below: | |
11 | * | |
12 | * Redistribution and use in source and binary forms, with or | |
13 | * without modification, are permitted provided that the following | |
14 | * conditions are met: | |
15 | * | |
16 | * - Redistributions of source code must retain the above | |
17 | * copyright notice, this list of conditions and the following | |
18 | * disclaimer. | |
19 | * | |
20 | * - Redistributions in binary form must reproduce the above | |
21 | * copyright notice, this list of conditions and the following | |
22 | * disclaimer in the documentation and/or other materials | |
23 | * provided with the distribution. | |
24 | * | |
25 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
26 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
27 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
28 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | |
29 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | |
30 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
31 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
32 | * SOFTWARE. | |
33 | */ | |
34 | ||
35 | #include <linux/string.h> | |
36 | #include <scsi/scsi_device.h> | |
37 | #include <scsi/scsi_transport_fc.h> | |
38 | #include <scsi/fc/fc_els.h> | |
39 | #include <scsi/fc/fc_fs.h> | |
40 | ||
41 | #include "csio_hw.h" | |
42 | #include "csio_lnode.h" | |
43 | #include "csio_rnode.h" | |
44 | ||
45 | static int csio_rnode_init(struct csio_rnode *, struct csio_lnode *); | |
46 | static void csio_rnode_exit(struct csio_rnode *); | |
47 | ||
48 | /* Static machine forward declarations */ | |
49 | static void csio_rns_uninit(struct csio_rnode *, enum csio_rn_ev); | |
50 | static void csio_rns_ready(struct csio_rnode *, enum csio_rn_ev); | |
51 | static void csio_rns_offline(struct csio_rnode *, enum csio_rn_ev); | |
52 | static void csio_rns_disappeared(struct csio_rnode *, enum csio_rn_ev); | |
53 | ||
54 | /* RNF event mapping */ | |
55 | static enum csio_rn_ev fwevt_to_rnevt[] = { | |
56 | CSIO_RNFE_NONE, /* None */ | |
57 | CSIO_RNFE_LOGGED_IN, /* PLOGI_ACC_RCVD */ | |
58 | CSIO_RNFE_NONE, /* PLOGI_RJT_RCVD */ | |
59 | CSIO_RNFE_PLOGI_RECV, /* PLOGI_RCVD */ | |
60 | CSIO_RNFE_LOGO_RECV, /* PLOGO_RCVD */ | |
61 | CSIO_RNFE_PRLI_DONE, /* PRLI_ACC_RCVD */ | |
62 | CSIO_RNFE_NONE, /* PRLI_RJT_RCVD */ | |
63 | CSIO_RNFE_PRLI_RECV, /* PRLI_RCVD */ | |
64 | CSIO_RNFE_PRLO_RECV, /* PRLO_RCVD */ | |
65 | CSIO_RNFE_NONE, /* NPORT_ID_CHGD */ | |
66 | CSIO_RNFE_LOGO_RECV, /* FLOGO_RCVD */ | |
67 | CSIO_RNFE_NONE, /* CLR_VIRT_LNK_RCVD */ | |
68 | CSIO_RNFE_LOGGED_IN, /* FLOGI_ACC_RCVD */ | |
69 | CSIO_RNFE_NONE, /* FLOGI_RJT_RCVD */ | |
70 | CSIO_RNFE_LOGGED_IN, /* FDISC_ACC_RCVD */ | |
71 | CSIO_RNFE_NONE, /* FDISC_RJT_RCVD */ | |
72 | CSIO_RNFE_NONE, /* FLOGI_TMO_MAX_RETRY */ | |
73 | CSIO_RNFE_NONE, /* IMPL_LOGO_ADISC_ACC */ | |
74 | CSIO_RNFE_NONE, /* IMPL_LOGO_ADISC_RJT */ | |
75 | CSIO_RNFE_NONE, /* IMPL_LOGO_ADISC_CNFLT */ | |
76 | CSIO_RNFE_NONE, /* PRLI_TMO */ | |
77 | CSIO_RNFE_NONE, /* ADISC_TMO */ | |
78 | CSIO_RNFE_NAME_MISSING, /* RSCN_DEV_LOST */ | |
79 | CSIO_RNFE_NONE, /* SCR_ACC_RCVD */ | |
80 | CSIO_RNFE_NONE, /* ADISC_RJT_RCVD */ | |
81 | CSIO_RNFE_NONE, /* LOGO_SNT */ | |
82 | CSIO_RNFE_LOGO_RECV, /* PROTO_ERR_IMPL_LOGO */ | |
83 | }; | |
84 | ||
85 | #define CSIO_FWE_TO_RNFE(_evt) ((_evt > PROTO_ERR_IMPL_LOGO) ? \ | |
86 | CSIO_RNFE_NONE : \ | |
87 | fwevt_to_rnevt[_evt]) | |
88 | int | |
89 | csio_is_rnode_ready(struct csio_rnode *rn) | |
90 | { | |
91 | return csio_match_state(rn, csio_rns_ready); | |
92 | } | |
93 | ||
94 | static int | |
95 | csio_is_rnode_uninit(struct csio_rnode *rn) | |
96 | { | |
97 | return csio_match_state(rn, csio_rns_uninit); | |
98 | } | |
99 | ||
100 | static int | |
101 | csio_is_rnode_wka(uint8_t rport_type) | |
102 | { | |
103 | if ((rport_type == FLOGI_VFPORT) || | |
104 | (rport_type == FDISC_VFPORT) || | |
105 | (rport_type == NS_VNPORT) || | |
106 | (rport_type == FDMI_VNPORT)) | |
107 | return 1; | |
108 | ||
109 | return 0; | |
110 | } | |
111 | ||
112 | /* | |
113 | * csio_rn_lookup - Finds the rnode with the given flowid | |
114 | * @ln - lnode | |
115 | * @flowid - flowid. | |
116 | * | |
117 | * Does the rnode lookup on the given lnode and flowid.If no matching entry | |
118 | * found, NULL is returned. | |
119 | */ | |
120 | static struct csio_rnode * | |
121 | csio_rn_lookup(struct csio_lnode *ln, uint32_t flowid) | |
122 | { | |
123 | struct csio_rnode *rnhead = (struct csio_rnode *) &ln->rnhead; | |
124 | struct list_head *tmp; | |
125 | struct csio_rnode *rn; | |
126 | ||
127 | list_for_each(tmp, &rnhead->sm.sm_list) { | |
128 | rn = (struct csio_rnode *) tmp; | |
129 | if (rn->flowid == flowid) | |
130 | return rn; | |
131 | } | |
132 | ||
133 | return NULL; | |
134 | } | |
135 | ||
136 | /* | |
137 | * csio_rn_lookup_wwpn - Finds the rnode with the given wwpn | |
138 | * @ln: lnode | |
139 | * @wwpn: wwpn | |
140 | * | |
141 | * Does the rnode lookup on the given lnode and wwpn. If no matching entry | |
142 | * found, NULL is returned. | |
143 | */ | |
144 | static struct csio_rnode * | |
145 | csio_rn_lookup_wwpn(struct csio_lnode *ln, uint8_t *wwpn) | |
146 | { | |
147 | struct csio_rnode *rnhead = (struct csio_rnode *) &ln->rnhead; | |
148 | struct list_head *tmp; | |
149 | struct csio_rnode *rn; | |
150 | ||
151 | list_for_each(tmp, &rnhead->sm.sm_list) { | |
152 | rn = (struct csio_rnode *) tmp; | |
153 | if (!memcmp(csio_rn_wwpn(rn), wwpn, 8)) | |
154 | return rn; | |
155 | } | |
156 | ||
157 | return NULL; | |
158 | } | |
159 | ||
160 | /** | |
161 | * csio_rnode_lookup_portid - Finds the rnode with the given portid | |
162 | * @ln: lnode | |
163 | * @portid: port id | |
164 | * | |
165 | * Lookup the rnode list for a given portid. If no matching entry | |
166 | * found, NULL is returned. | |
167 | */ | |
168 | struct csio_rnode * | |
169 | csio_rnode_lookup_portid(struct csio_lnode *ln, uint32_t portid) | |
170 | { | |
171 | struct csio_rnode *rnhead = (struct csio_rnode *) &ln->rnhead; | |
172 | struct list_head *tmp; | |
173 | struct csio_rnode *rn; | |
174 | ||
175 | list_for_each(tmp, &rnhead->sm.sm_list) { | |
176 | rn = (struct csio_rnode *) tmp; | |
177 | if (rn->nport_id == portid) | |
178 | return rn; | |
179 | } | |
180 | ||
181 | return NULL; | |
182 | } | |
183 | ||
184 | static int | |
185 | csio_rn_dup_flowid(struct csio_lnode *ln, uint32_t rdev_flowid, | |
186 | uint32_t *vnp_flowid) | |
187 | { | |
188 | struct csio_rnode *rnhead; | |
189 | struct list_head *tmp, *tmp1; | |
190 | struct csio_rnode *rn; | |
191 | struct csio_lnode *ln_tmp; | |
192 | struct csio_hw *hw = csio_lnode_to_hw(ln); | |
193 | ||
194 | list_for_each(tmp1, &hw->sln_head) { | |
195 | ln_tmp = (struct csio_lnode *) tmp1; | |
196 | if (ln_tmp == ln) | |
197 | continue; | |
198 | ||
199 | rnhead = (struct csio_rnode *)&ln_tmp->rnhead; | |
200 | list_for_each(tmp, &rnhead->sm.sm_list) { | |
201 | ||
202 | rn = (struct csio_rnode *) tmp; | |
203 | if (csio_is_rnode_ready(rn)) { | |
204 | if (rn->flowid == rdev_flowid) { | |
205 | *vnp_flowid = csio_ln_flowid(ln_tmp); | |
206 | return 1; | |
207 | } | |
208 | } | |
209 | } | |
210 | } | |
211 | ||
212 | return 0; | |
213 | } | |
214 | ||
215 | static struct csio_rnode * | |
216 | csio_alloc_rnode(struct csio_lnode *ln) | |
217 | { | |
218 | struct csio_hw *hw = csio_lnode_to_hw(ln); | |
219 | ||
220 | struct csio_rnode *rn = mempool_alloc(hw->rnode_mempool, GFP_ATOMIC); | |
221 | if (!rn) | |
222 | goto err; | |
223 | ||
224 | memset(rn, 0, sizeof(struct csio_rnode)); | |
225 | if (csio_rnode_init(rn, ln)) | |
226 | goto err_free; | |
227 | ||
228 | CSIO_INC_STATS(ln, n_rnode_alloc); | |
229 | ||
230 | return rn; | |
231 | ||
232 | err_free: | |
233 | mempool_free(rn, hw->rnode_mempool); | |
234 | err: | |
235 | CSIO_INC_STATS(ln, n_rnode_nomem); | |
236 | return NULL; | |
237 | } | |
238 | ||
239 | static void | |
240 | csio_free_rnode(struct csio_rnode *rn) | |
241 | { | |
242 | struct csio_hw *hw = csio_lnode_to_hw(csio_rnode_to_lnode(rn)); | |
243 | ||
244 | csio_rnode_exit(rn); | |
245 | CSIO_INC_STATS(rn->lnp, n_rnode_free); | |
246 | mempool_free(rn, hw->rnode_mempool); | |
247 | } | |
248 | ||
249 | /* | |
250 | * csio_get_rnode - Gets rnode with the given flowid | |
251 | * @ln - lnode | |
252 | * @flowid - flow id. | |
253 | * | |
254 | * Does the rnode lookup on the given lnode and flowid. If no matching | |
255 | * rnode found, then new rnode with given npid is allocated and returned. | |
256 | */ | |
257 | static struct csio_rnode * | |
258 | csio_get_rnode(struct csio_lnode *ln, uint32_t flowid) | |
259 | { | |
260 | struct csio_rnode *rn; | |
261 | ||
262 | rn = csio_rn_lookup(ln, flowid); | |
263 | if (!rn) { | |
264 | rn = csio_alloc_rnode(ln); | |
265 | if (!rn) | |
266 | return NULL; | |
267 | ||
268 | rn->flowid = flowid; | |
269 | } | |
270 | ||
271 | return rn; | |
272 | } | |
273 | ||
274 | /* | |
275 | * csio_put_rnode - Frees the given rnode | |
276 | * @ln - lnode | |
277 | * @flowid - flow id. | |
278 | * | |
279 | * Does the rnode lookup on the given lnode and flowid. If no matching | |
280 | * rnode found, then new rnode with given npid is allocated and returned. | |
281 | */ | |
282 | void | |
283 | csio_put_rnode(struct csio_lnode *ln, struct csio_rnode *rn) | |
284 | { | |
285 | CSIO_DB_ASSERT(csio_is_rnode_uninit(rn) != 0); | |
286 | csio_free_rnode(rn); | |
287 | } | |
288 | ||
289 | /* | |
290 | * csio_confirm_rnode - confirms rnode based on wwpn. | |
291 | * @ln: lnode | |
292 | * @rdev_flowid: remote device flowid | |
293 | * @rdevp: remote device params | |
294 | * This routines searches other rnode in list having same wwpn of new rnode. | |
295 | * If there is a match, then matched rnode is returned and otherwise new rnode | |
296 | * is returned. | |
297 | * returns rnode. | |
298 | */ | |
299 | struct csio_rnode * | |
300 | csio_confirm_rnode(struct csio_lnode *ln, uint32_t rdev_flowid, | |
301 | struct fcoe_rdev_entry *rdevp) | |
302 | { | |
303 | uint8_t rport_type; | |
304 | struct csio_rnode *rn, *match_rn; | |
d69630e8 | 305 | uint32_t vnp_flowid = 0; |
5036f0a0 | 306 | __be32 *port_id; |
a3667aae | 307 | |
5036f0a0 | 308 | port_id = (__be32 *)&rdevp->r_id[0]; |
a3667aae NKI |
309 | rport_type = |
310 | FW_RDEV_WR_RPORT_TYPE_GET(rdevp->rd_xfer_rdy_to_rport_type); | |
311 | ||
312 | /* Drop rdev event for cntrl port */ | |
313 | if (rport_type == FAB_CTLR_VNPORT) { | |
314 | csio_ln_dbg(ln, | |
315 | "Unhandled rport_type:%d recv in rdev evt " | |
316 | "ssni:x%x\n", rport_type, rdev_flowid); | |
317 | return NULL; | |
318 | } | |
319 | ||
320 | /* Lookup on flowid */ | |
321 | rn = csio_rn_lookup(ln, rdev_flowid); | |
322 | if (!rn) { | |
323 | ||
324 | /* Drop events with duplicate flowid */ | |
325 | if (csio_rn_dup_flowid(ln, rdev_flowid, &vnp_flowid)) { | |
326 | csio_ln_warn(ln, | |
327 | "ssni:%x already active on vnpi:%x", | |
328 | rdev_flowid, vnp_flowid); | |
329 | return NULL; | |
330 | } | |
331 | ||
332 | /* Lookup on wwpn for NPORTs */ | |
333 | rn = csio_rn_lookup_wwpn(ln, rdevp->wwpn); | |
334 | if (!rn) | |
335 | goto alloc_rnode; | |
336 | ||
337 | } else { | |
338 | /* Lookup well-known ports with nport id */ | |
339 | if (csio_is_rnode_wka(rport_type)) { | |
340 | match_rn = csio_rnode_lookup_portid(ln, | |
341 | ((ntohl(*port_id) >> 8) & CSIO_DID_MASK)); | |
342 | if (match_rn == NULL) { | |
343 | csio_rn_flowid(rn) = CSIO_INVALID_IDX; | |
344 | goto alloc_rnode; | |
345 | } | |
346 | ||
347 | /* | |
348 | * Now compare the wwpn to confirm that | |
349 | * same port relogged in. If so update the matched rn. | |
350 | * Else, go ahead and alloc a new rnode. | |
351 | */ | |
352 | if (!memcmp(csio_rn_wwpn(match_rn), rdevp->wwpn, 8)) { | |
d69630e8 AB |
353 | if (rn == match_rn) |
354 | goto found_rnode; | |
355 | csio_ln_dbg(ln, | |
356 | "nport_id:x%x and wwpn:%llx" | |
357 | " match for ssni:x%x\n", | |
358 | rn->nport_id, | |
359 | wwn_to_u64(rdevp->wwpn), | |
360 | rdev_flowid); | |
a3667aae NKI |
361 | if (csio_is_rnode_ready(rn)) { |
362 | csio_ln_warn(ln, | |
363 | "rnode is already" | |
364 | "active ssni:x%x\n", | |
365 | rdev_flowid); | |
366 | CSIO_ASSERT(0); | |
367 | } | |
368 | csio_rn_flowid(rn) = CSIO_INVALID_IDX; | |
369 | rn = match_rn; | |
370 | ||
371 | /* Update rn */ | |
372 | goto found_rnode; | |
373 | } | |
374 | csio_rn_flowid(rn) = CSIO_INVALID_IDX; | |
375 | goto alloc_rnode; | |
376 | } | |
377 | ||
378 | /* wwpn match */ | |
379 | if (!memcmp(csio_rn_wwpn(rn), rdevp->wwpn, 8)) | |
380 | goto found_rnode; | |
381 | ||
382 | /* Search for rnode that have same wwpn */ | |
383 | match_rn = csio_rn_lookup_wwpn(ln, rdevp->wwpn); | |
384 | if (match_rn != NULL) { | |
385 | csio_ln_dbg(ln, | |
386 | "ssni:x%x changed for rport name(wwpn):%llx " | |
387 | "did:x%x\n", rdev_flowid, | |
388 | wwn_to_u64(rdevp->wwpn), | |
389 | match_rn->nport_id); | |
390 | csio_rn_flowid(rn) = CSIO_INVALID_IDX; | |
391 | rn = match_rn; | |
392 | } else { | |
393 | csio_ln_dbg(ln, | |
394 | "rnode wwpn mismatch found ssni:x%x " | |
395 | "name(wwpn):%llx\n", | |
396 | rdev_flowid, | |
397 | wwn_to_u64(csio_rn_wwpn(rn))); | |
398 | if (csio_is_rnode_ready(rn)) { | |
399 | csio_ln_warn(ln, | |
400 | "rnode is already active " | |
401 | "wwpn:%llx ssni:x%x\n", | |
402 | wwn_to_u64(csio_rn_wwpn(rn)), | |
403 | rdev_flowid); | |
404 | CSIO_ASSERT(0); | |
405 | } | |
406 | csio_rn_flowid(rn) = CSIO_INVALID_IDX; | |
407 | goto alloc_rnode; | |
408 | } | |
409 | } | |
410 | ||
411 | found_rnode: | |
412 | csio_ln_dbg(ln, "found rnode:%p ssni:x%x name(wwpn):%llx\n", | |
413 | rn, rdev_flowid, wwn_to_u64(rdevp->wwpn)); | |
414 | ||
415 | /* Update flowid */ | |
416 | csio_rn_flowid(rn) = rdev_flowid; | |
417 | ||
418 | /* update rdev entry */ | |
419 | rn->rdev_entry = rdevp; | |
420 | CSIO_INC_STATS(ln, n_rnode_match); | |
421 | return rn; | |
422 | ||
423 | alloc_rnode: | |
424 | rn = csio_get_rnode(ln, rdev_flowid); | |
425 | if (!rn) | |
426 | return NULL; | |
427 | ||
428 | csio_ln_dbg(ln, "alloc rnode:%p ssni:x%x name(wwpn):%llx\n", | |
429 | rn, rdev_flowid, wwn_to_u64(rdevp->wwpn)); | |
430 | ||
431 | /* update rdev entry */ | |
432 | rn->rdev_entry = rdevp; | |
433 | return rn; | |
434 | } | |
435 | ||
436 | /* | |
437 | * csio_rn_verify_rparams - verify rparams. | |
438 | * @ln: lnode | |
439 | * @rn: rnode | |
440 | * @rdevp: remote device params | |
441 | * returns success if rparams are verified. | |
442 | */ | |
443 | static int | |
444 | csio_rn_verify_rparams(struct csio_lnode *ln, struct csio_rnode *rn, | |
445 | struct fcoe_rdev_entry *rdevp) | |
446 | { | |
447 | uint8_t null[8]; | |
448 | uint8_t rport_type; | |
449 | uint8_t fc_class; | |
5036f0a0 | 450 | __be32 *did; |
a3667aae | 451 | |
5036f0a0 | 452 | did = (__be32 *) &rdevp->r_id[0]; |
a3667aae NKI |
453 | rport_type = |
454 | FW_RDEV_WR_RPORT_TYPE_GET(rdevp->rd_xfer_rdy_to_rport_type); | |
455 | switch (rport_type) { | |
456 | case FLOGI_VFPORT: | |
457 | rn->role = CSIO_RNFR_FABRIC; | |
458 | if (((ntohl(*did) >> 8) & CSIO_DID_MASK) != FC_FID_FLOGI) { | |
459 | csio_ln_err(ln, "ssni:x%x invalid fabric portid\n", | |
460 | csio_rn_flowid(rn)); | |
461 | return -EINVAL; | |
462 | } | |
463 | /* NPIV support */ | |
464 | if (FW_RDEV_WR_NPIV_GET(rdevp->vft_to_qos)) | |
465 | ln->flags |= CSIO_LNF_NPIVSUPP; | |
466 | ||
467 | break; | |
468 | ||
469 | case NS_VNPORT: | |
470 | rn->role = CSIO_RNFR_NS; | |
471 | if (((ntohl(*did) >> 8) & CSIO_DID_MASK) != FC_FID_DIR_SERV) { | |
472 | csio_ln_err(ln, "ssni:x%x invalid fabric portid\n", | |
473 | csio_rn_flowid(rn)); | |
474 | return -EINVAL; | |
475 | } | |
476 | break; | |
477 | ||
478 | case REG_FC4_VNPORT: | |
479 | case REG_VNPORT: | |
480 | rn->role = CSIO_RNFR_NPORT; | |
481 | if (rdevp->event_cause == PRLI_ACC_RCVD || | |
482 | rdevp->event_cause == PRLI_RCVD) { | |
483 | if (FW_RDEV_WR_TASK_RETRY_ID_GET( | |
484 | rdevp->enh_disc_to_tgt)) | |
485 | rn->fcp_flags |= FCP_SPPF_OVLY_ALLOW; | |
486 | ||
487 | if (FW_RDEV_WR_RETRY_GET(rdevp->enh_disc_to_tgt)) | |
488 | rn->fcp_flags |= FCP_SPPF_RETRY; | |
489 | ||
490 | if (FW_RDEV_WR_CONF_CMPL_GET(rdevp->enh_disc_to_tgt)) | |
491 | rn->fcp_flags |= FCP_SPPF_CONF_COMPL; | |
492 | ||
493 | if (FW_RDEV_WR_TGT_GET(rdevp->enh_disc_to_tgt)) | |
494 | rn->role |= CSIO_RNFR_TARGET; | |
495 | ||
496 | if (FW_RDEV_WR_INI_GET(rdevp->enh_disc_to_tgt)) | |
497 | rn->role |= CSIO_RNFR_INITIATOR; | |
498 | } | |
499 | ||
500 | break; | |
501 | ||
502 | case FDMI_VNPORT: | |
503 | case FAB_CTLR_VNPORT: | |
504 | rn->role = 0; | |
505 | break; | |
506 | ||
507 | default: | |
508 | csio_ln_err(ln, "ssni:x%x invalid rport type recv x%x\n", | |
509 | csio_rn_flowid(rn), rport_type); | |
510 | return -EINVAL; | |
511 | } | |
512 | ||
513 | /* validate wwpn/wwnn for Name server/remote port */ | |
514 | if (rport_type == REG_VNPORT || rport_type == NS_VNPORT) { | |
515 | memset(null, 0, 8); | |
516 | if (!memcmp(rdevp->wwnn, null, 8)) { | |
517 | csio_ln_err(ln, | |
518 | "ssni:x%x invalid wwnn received from" | |
519 | " rport did:x%x\n", | |
520 | csio_rn_flowid(rn), | |
521 | (ntohl(*did) & CSIO_DID_MASK)); | |
522 | return -EINVAL; | |
523 | } | |
524 | ||
525 | if (!memcmp(rdevp->wwpn, null, 8)) { | |
526 | csio_ln_err(ln, | |
527 | "ssni:x%x invalid wwpn received from" | |
528 | " rport did:x%x\n", | |
529 | csio_rn_flowid(rn), | |
530 | (ntohl(*did) & CSIO_DID_MASK)); | |
531 | return -EINVAL; | |
532 | } | |
533 | ||
534 | } | |
535 | ||
536 | /* Copy wwnn, wwpn and nport id */ | |
537 | rn->nport_id = (ntohl(*did) >> 8) & CSIO_DID_MASK; | |
538 | memcpy(csio_rn_wwnn(rn), rdevp->wwnn, 8); | |
539 | memcpy(csio_rn_wwpn(rn), rdevp->wwpn, 8); | |
5036f0a0 | 540 | rn->rn_sparm.csp.sp_bb_data = rdevp->rcv_fr_sz; |
a3667aae NKI |
541 | fc_class = FW_RDEV_WR_CLASS_GET(rdevp->vft_to_qos); |
542 | rn->rn_sparm.clsp[fc_class - 1].cp_class = htons(FC_CPC_VALID); | |
5036f0a0 | 543 | |
a3667aae NKI |
544 | return 0; |
545 | } | |
546 | ||
547 | static void | |
548 | __csio_reg_rnode(struct csio_rnode *rn) | |
549 | { | |
550 | struct csio_lnode *ln = csio_rnode_to_lnode(rn); | |
551 | struct csio_hw *hw = csio_lnode_to_hw(ln); | |
552 | ||
553 | spin_unlock_irq(&hw->lock); | |
554 | csio_reg_rnode(rn); | |
555 | spin_lock_irq(&hw->lock); | |
556 | ||
557 | if (rn->role & CSIO_RNFR_TARGET) | |
558 | ln->n_scsi_tgts++; | |
559 | ||
560 | if (rn->nport_id == FC_FID_MGMT_SERV) | |
561 | csio_ln_fdmi_start(ln, (void *) rn); | |
562 | } | |
563 | ||
564 | static void | |
565 | __csio_unreg_rnode(struct csio_rnode *rn) | |
566 | { | |
567 | struct csio_lnode *ln = csio_rnode_to_lnode(rn); | |
568 | struct csio_hw *hw = csio_lnode_to_hw(ln); | |
569 | LIST_HEAD(tmp_q); | |
570 | int cmpl = 0; | |
571 | ||
572 | if (!list_empty(&rn->host_cmpl_q)) { | |
573 | csio_dbg(hw, "Returning completion queue I/Os\n"); | |
574 | list_splice_tail_init(&rn->host_cmpl_q, &tmp_q); | |
575 | cmpl = 1; | |
576 | } | |
577 | ||
578 | if (rn->role & CSIO_RNFR_TARGET) { | |
579 | ln->n_scsi_tgts--; | |
580 | ln->last_scan_ntgts--; | |
581 | } | |
582 | ||
583 | spin_unlock_irq(&hw->lock); | |
584 | csio_unreg_rnode(rn); | |
585 | spin_lock_irq(&hw->lock); | |
586 | ||
587 | /* Cleanup I/Os that were waiting for rnode to unregister */ | |
588 | if (cmpl) | |
589 | csio_scsi_cleanup_io_q(csio_hw_to_scsim(hw), &tmp_q); | |
590 | ||
591 | } | |
592 | ||
593 | /*****************************************************************************/ | |
594 | /* START: Rnode SM */ | |
595 | /*****************************************************************************/ | |
596 | ||
597 | /* | |
598 | * csio_rns_uninit - | |
599 | * @rn - rnode | |
600 | * @evt - SM event. | |
601 | * | |
602 | */ | |
603 | static void | |
604 | csio_rns_uninit(struct csio_rnode *rn, enum csio_rn_ev evt) | |
605 | { | |
606 | struct csio_lnode *ln = csio_rnode_to_lnode(rn); | |
607 | int ret = 0; | |
608 | ||
609 | CSIO_INC_STATS(rn, n_evt_sm[evt]); | |
610 | ||
611 | switch (evt) { | |
612 | case CSIO_RNFE_LOGGED_IN: | |
613 | case CSIO_RNFE_PLOGI_RECV: | |
614 | ret = csio_rn_verify_rparams(ln, rn, rn->rdev_entry); | |
615 | if (!ret) { | |
616 | csio_set_state(&rn->sm, csio_rns_ready); | |
617 | __csio_reg_rnode(rn); | |
618 | } else { | |
619 | CSIO_INC_STATS(rn, n_err_inval); | |
620 | } | |
621 | break; | |
622 | case CSIO_RNFE_LOGO_RECV: | |
623 | csio_ln_dbg(ln, | |
624 | "ssni:x%x Ignoring event %d recv " | |
625 | "in rn state[uninit]\n", csio_rn_flowid(rn), evt); | |
626 | CSIO_INC_STATS(rn, n_evt_drop); | |
627 | break; | |
628 | default: | |
629 | csio_ln_dbg(ln, | |
630 | "ssni:x%x unexp event %d recv " | |
631 | "in rn state[uninit]\n", csio_rn_flowid(rn), evt); | |
632 | CSIO_INC_STATS(rn, n_evt_unexp); | |
633 | break; | |
634 | } | |
635 | } | |
636 | ||
637 | /* | |
638 | * csio_rns_ready - | |
639 | * @rn - rnode | |
640 | * @evt - SM event. | |
641 | * | |
642 | */ | |
643 | static void | |
644 | csio_rns_ready(struct csio_rnode *rn, enum csio_rn_ev evt) | |
645 | { | |
646 | struct csio_lnode *ln = csio_rnode_to_lnode(rn); | |
647 | int ret = 0; | |
648 | ||
649 | CSIO_INC_STATS(rn, n_evt_sm[evt]); | |
650 | ||
651 | switch (evt) { | |
652 | case CSIO_RNFE_LOGGED_IN: | |
653 | case CSIO_RNFE_PLOGI_RECV: | |
654 | csio_ln_dbg(ln, | |
655 | "ssni:x%x Ignoring event %d recv from did:x%x " | |
656 | "in rn state[ready]\n", csio_rn_flowid(rn), evt, | |
657 | rn->nport_id); | |
658 | CSIO_INC_STATS(rn, n_evt_drop); | |
659 | break; | |
660 | ||
661 | case CSIO_RNFE_PRLI_DONE: | |
662 | case CSIO_RNFE_PRLI_RECV: | |
663 | ret = csio_rn_verify_rparams(ln, rn, rn->rdev_entry); | |
664 | if (!ret) | |
665 | __csio_reg_rnode(rn); | |
666 | else | |
667 | CSIO_INC_STATS(rn, n_err_inval); | |
668 | ||
669 | break; | |
670 | case CSIO_RNFE_DOWN: | |
671 | csio_set_state(&rn->sm, csio_rns_offline); | |
672 | __csio_unreg_rnode(rn); | |
673 | ||
674 | /* FW expected to internally aborted outstanding SCSI WRs | |
675 | * and return all SCSI WRs to host with status "ABORTED". | |
676 | */ | |
677 | break; | |
678 | ||
679 | case CSIO_RNFE_LOGO_RECV: | |
680 | csio_set_state(&rn->sm, csio_rns_offline); | |
681 | ||
682 | __csio_unreg_rnode(rn); | |
683 | ||
684 | /* FW expected to internally aborted outstanding SCSI WRs | |
685 | * and return all SCSI WRs to host with status "ABORTED". | |
686 | */ | |
687 | break; | |
688 | ||
689 | case CSIO_RNFE_CLOSE: | |
690 | /* | |
691 | * Each rnode receives CLOSE event when driver is removed or | |
692 | * device is reset | |
693 | * Note: All outstanding IOs on remote port need to returned | |
694 | * to uppper layer with appropriate error before sending | |
695 | * CLOSE event | |
696 | */ | |
697 | csio_set_state(&rn->sm, csio_rns_uninit); | |
698 | __csio_unreg_rnode(rn); | |
699 | break; | |
700 | ||
701 | case CSIO_RNFE_NAME_MISSING: | |
702 | csio_set_state(&rn->sm, csio_rns_disappeared); | |
703 | __csio_unreg_rnode(rn); | |
704 | ||
705 | /* | |
706 | * FW expected to internally aborted outstanding SCSI WRs | |
707 | * and return all SCSI WRs to host with status "ABORTED". | |
708 | */ | |
709 | ||
710 | break; | |
711 | ||
712 | default: | |
713 | csio_ln_dbg(ln, | |
714 | "ssni:x%x unexp event %d recv from did:x%x " | |
715 | "in rn state[uninit]\n", csio_rn_flowid(rn), evt, | |
716 | rn->nport_id); | |
717 | CSIO_INC_STATS(rn, n_evt_unexp); | |
718 | break; | |
719 | } | |
720 | } | |
721 | ||
722 | /* | |
723 | * csio_rns_offline - | |
724 | * @rn - rnode | |
725 | * @evt - SM event. | |
726 | * | |
727 | */ | |
728 | static void | |
729 | csio_rns_offline(struct csio_rnode *rn, enum csio_rn_ev evt) | |
730 | { | |
731 | struct csio_lnode *ln = csio_rnode_to_lnode(rn); | |
732 | int ret = 0; | |
733 | ||
734 | CSIO_INC_STATS(rn, n_evt_sm[evt]); | |
735 | ||
736 | switch (evt) { | |
737 | case CSIO_RNFE_LOGGED_IN: | |
738 | case CSIO_RNFE_PLOGI_RECV: | |
739 | ret = csio_rn_verify_rparams(ln, rn, rn->rdev_entry); | |
740 | if (!ret) { | |
741 | csio_set_state(&rn->sm, csio_rns_ready); | |
742 | __csio_reg_rnode(rn); | |
743 | } else { | |
744 | CSIO_INC_STATS(rn, n_err_inval); | |
745 | csio_post_event(&rn->sm, CSIO_RNFE_CLOSE); | |
746 | } | |
747 | break; | |
748 | ||
749 | case CSIO_RNFE_DOWN: | |
750 | csio_ln_dbg(ln, | |
751 | "ssni:x%x Ignoring event %d recv from did:x%x " | |
752 | "in rn state[offline]\n", csio_rn_flowid(rn), evt, | |
753 | rn->nport_id); | |
754 | CSIO_INC_STATS(rn, n_evt_drop); | |
755 | break; | |
756 | ||
757 | case CSIO_RNFE_CLOSE: | |
758 | /* Each rnode receives CLOSE event when driver is removed or | |
759 | * device is reset | |
760 | * Note: All outstanding IOs on remote port need to returned | |
761 | * to uppper layer with appropriate error before sending | |
762 | * CLOSE event | |
763 | */ | |
764 | csio_set_state(&rn->sm, csio_rns_uninit); | |
765 | break; | |
766 | ||
767 | case CSIO_RNFE_NAME_MISSING: | |
768 | csio_set_state(&rn->sm, csio_rns_disappeared); | |
769 | break; | |
770 | ||
771 | default: | |
772 | csio_ln_dbg(ln, | |
773 | "ssni:x%x unexp event %d recv from did:x%x " | |
774 | "in rn state[offline]\n", csio_rn_flowid(rn), evt, | |
775 | rn->nport_id); | |
776 | CSIO_INC_STATS(rn, n_evt_unexp); | |
777 | break; | |
778 | } | |
779 | } | |
780 | ||
781 | /* | |
782 | * csio_rns_disappeared - | |
783 | * @rn - rnode | |
784 | * @evt - SM event. | |
785 | * | |
786 | */ | |
787 | static void | |
788 | csio_rns_disappeared(struct csio_rnode *rn, enum csio_rn_ev evt) | |
789 | { | |
790 | struct csio_lnode *ln = csio_rnode_to_lnode(rn); | |
791 | int ret = 0; | |
792 | ||
793 | CSIO_INC_STATS(rn, n_evt_sm[evt]); | |
794 | ||
795 | switch (evt) { | |
796 | case CSIO_RNFE_LOGGED_IN: | |
797 | case CSIO_RNFE_PLOGI_RECV: | |
798 | ret = csio_rn_verify_rparams(ln, rn, rn->rdev_entry); | |
799 | if (!ret) { | |
800 | csio_set_state(&rn->sm, csio_rns_ready); | |
801 | __csio_reg_rnode(rn); | |
802 | } else { | |
803 | CSIO_INC_STATS(rn, n_err_inval); | |
804 | csio_post_event(&rn->sm, CSIO_RNFE_CLOSE); | |
805 | } | |
806 | break; | |
807 | ||
808 | case CSIO_RNFE_CLOSE: | |
809 | /* Each rnode receives CLOSE event when driver is removed or | |
810 | * device is reset. | |
811 | * Note: All outstanding IOs on remote port need to returned | |
812 | * to uppper layer with appropriate error before sending | |
813 | * CLOSE event | |
814 | */ | |
815 | csio_set_state(&rn->sm, csio_rns_uninit); | |
816 | break; | |
817 | ||
818 | case CSIO_RNFE_DOWN: | |
819 | case CSIO_RNFE_NAME_MISSING: | |
820 | csio_ln_dbg(ln, | |
821 | "ssni:x%x Ignoring event %d recv from did x%x" | |
822 | "in rn state[disappeared]\n", csio_rn_flowid(rn), | |
823 | evt, rn->nport_id); | |
824 | break; | |
825 | ||
826 | default: | |
827 | csio_ln_dbg(ln, | |
828 | "ssni:x%x unexp event %d recv from did x%x" | |
829 | "in rn state[disappeared]\n", csio_rn_flowid(rn), | |
830 | evt, rn->nport_id); | |
831 | CSIO_INC_STATS(rn, n_evt_unexp); | |
832 | break; | |
833 | } | |
834 | } | |
835 | ||
836 | /*****************************************************************************/ | |
837 | /* END: Rnode SM */ | |
838 | /*****************************************************************************/ | |
839 | ||
840 | /* | |
841 | * csio_rnode_devloss_handler - Device loss event handler | |
842 | * @rn: rnode | |
843 | * | |
844 | * Post event to close rnode SM and free rnode. | |
845 | */ | |
846 | void | |
847 | csio_rnode_devloss_handler(struct csio_rnode *rn) | |
848 | { | |
849 | struct csio_lnode *ln = csio_rnode_to_lnode(rn); | |
850 | ||
851 | /* ignore if same rnode came back as online */ | |
852 | if (csio_is_rnode_ready(rn)) | |
853 | return; | |
854 | ||
855 | csio_post_event(&rn->sm, CSIO_RNFE_CLOSE); | |
856 | ||
857 | /* Free rn if in uninit state */ | |
858 | if (csio_is_rnode_uninit(rn)) | |
859 | csio_put_rnode(ln, rn); | |
860 | } | |
861 | ||
862 | /** | |
863 | * csio_rnode_fwevt_handler - Event handler for firmware rnode events. | |
864 | * @rn: rnode | |
865 | * | |
866 | */ | |
867 | void | |
868 | csio_rnode_fwevt_handler(struct csio_rnode *rn, uint8_t fwevt) | |
869 | { | |
870 | struct csio_lnode *ln = csio_rnode_to_lnode(rn); | |
871 | enum csio_rn_ev evt; | |
872 | ||
873 | evt = CSIO_FWE_TO_RNFE(fwevt); | |
874 | if (!evt) { | |
875 | csio_ln_err(ln, "ssni:x%x Unhandled FW Rdev event: %d\n", | |
876 | csio_rn_flowid(rn), fwevt); | |
877 | CSIO_INC_STATS(rn, n_evt_unexp); | |
878 | return; | |
879 | } | |
880 | CSIO_INC_STATS(rn, n_evt_fw[fwevt]); | |
881 | ||
882 | /* Track previous & current events for debugging */ | |
883 | rn->prev_evt = rn->cur_evt; | |
884 | rn->cur_evt = fwevt; | |
885 | ||
886 | /* Post event to rnode SM */ | |
887 | csio_post_event(&rn->sm, evt); | |
888 | ||
889 | /* Free rn if in uninit state */ | |
890 | if (csio_is_rnode_uninit(rn)) | |
891 | csio_put_rnode(ln, rn); | |
892 | } | |
893 | ||
894 | /* | |
895 | * csio_rnode_init - Initialize rnode. | |
896 | * @rn: RNode | |
897 | * @ln: Associated lnode | |
898 | * | |
899 | * Caller is responsible for holding the lock. The lock is required | |
900 | * to be held for inserting the rnode in ln->rnhead list. | |
901 | */ | |
902 | static int | |
903 | csio_rnode_init(struct csio_rnode *rn, struct csio_lnode *ln) | |
904 | { | |
905 | csio_rnode_to_lnode(rn) = ln; | |
906 | csio_init_state(&rn->sm, csio_rns_uninit); | |
907 | INIT_LIST_HEAD(&rn->host_cmpl_q); | |
908 | csio_rn_flowid(rn) = CSIO_INVALID_IDX; | |
909 | ||
910 | /* Add rnode to list of lnodes->rnhead */ | |
911 | list_add_tail(&rn->sm.sm_list, &ln->rnhead); | |
912 | ||
913 | return 0; | |
914 | } | |
915 | ||
916 | static void | |
917 | csio_rnode_exit(struct csio_rnode *rn) | |
918 | { | |
919 | list_del_init(&rn->sm.sm_list); | |
920 | CSIO_DB_ASSERT(list_empty(&rn->host_cmpl_q)); | |
921 | } |