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(smp: rn, state: csio_rns_ready); |
92 | } |
93 | |
94 | static int |
95 | csio_is_rnode_uninit(struct csio_rnode *rn) |
96 | { |
97 | return csio_match_state(smp: rn, state: 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), q: wwpn, size: 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(pool: 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(element: rn, pool: 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(element: rn, pool: 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; |
305 | uint32_t vnp_flowid = 0; |
306 | __be32 *port_id; |
307 | |
308 | port_id = (__be32 *)&rdevp->r_id[0]; |
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, flowid: rdev_flowid); |
322 | if (!rn) { |
323 | |
324 | /* Drop events with duplicate flowid */ |
325 | if (csio_rn_dup_flowid(ln, rdev_flowid, vnp_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, wwpn: 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 | portid: ((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), q: rdevp->wwpn, size: 8)) { |
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); |
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), q: rdevp->wwpn, size: 8)) |
380 | goto found_rnode; |
381 | |
382 | /* Search for rnode that have same wwpn */ |
383 | match_rn = csio_rn_lookup_wwpn(ln, wwpn: 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, flowid: 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; |
450 | __be32 *did; |
451 | |
452 | did = (__be32 *) &rdevp->r_id[0]; |
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(p: rdevp->wwnn, q: null, size: 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(p: rdevp->wwpn, q: null, size: 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); |
540 | rn->rn_sparm.csp.sp_bb_data = rdevp->rcv_fr_sz; |
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); |
543 | |
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(lock: &hw->lock); |
554 | csio_reg_rnode(rn); |
555 | spin_lock_irq(lock: &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(head: &rn->host_cmpl_q)) { |
573 | csio_dbg(hw, "Returning completion queue I/Os\n" ); |
574 | list_splice_tail_init(list: &rn->host_cmpl_q, head: &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(lock: &hw->lock); |
584 | csio_unreg_rnode(rn); |
585 | spin_lock_irq(lock: &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, rdevp: rn->rdev_entry); |
615 | if (!ret) { |
616 | csio_set_state(smp: &rn->sm, state: 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, rdevp: 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(smp: &rn->sm, state: 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(smp: &rn->sm, state: 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(smp: &rn->sm, state: csio_rns_uninit); |
698 | __csio_unreg_rnode(rn); |
699 | break; |
700 | |
701 | case CSIO_RNFE_NAME_MISSING: |
702 | csio_set_state(smp: &rn->sm, state: 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, rdevp: rn->rdev_entry); |
740 | if (!ret) { |
741 | csio_set_state(smp: &rn->sm, state: csio_rns_ready); |
742 | __csio_reg_rnode(rn); |
743 | } else { |
744 | CSIO_INC_STATS(rn, n_err_inval); |
745 | csio_post_event(smp: &rn->sm, evt: 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(smp: &rn->sm, state: csio_rns_uninit); |
765 | break; |
766 | |
767 | case CSIO_RNFE_NAME_MISSING: |
768 | csio_set_state(smp: &rn->sm, state: 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, rdevp: rn->rdev_entry); |
799 | if (!ret) { |
800 | csio_set_state(smp: &rn->sm, state: csio_rns_ready); |
801 | __csio_reg_rnode(rn); |
802 | } else { |
803 | CSIO_INC_STATS(rn, n_err_inval); |
804 | csio_post_event(smp: &rn->sm, evt: 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(smp: &rn->sm, state: 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(smp: &rn->sm, evt: 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 | * @fwevt: firmware event to handle |
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(smp: &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(smp: &rn->sm, state: csio_rns_uninit); |
907 | INIT_LIST_HEAD(list: &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(new: &rn->sm.sm_list, head: &ln->rnhead); |
912 | |
913 | return 0; |
914 | } |
915 | |
916 | static void |
917 | csio_rnode_exit(struct csio_rnode *rn) |
918 | { |
919 | list_del_init(entry: &rn->sm.sm_list); |
920 | CSIO_DB_ASSERT(list_empty(&rn->host_cmpl_q)); |
921 | } |
922 | |