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/kernel.h> |
36 | #include <linux/string.h> |
37 | #include <linux/delay.h> |
38 | #include <linux/module.h> |
39 | #include <linux/init.h> |
40 | #include <linux/pci.h> |
41 | #include <linux/mm.h> |
42 | #include <linux/jiffies.h> |
43 | #include <scsi/fc/fc_fs.h> |
44 | |
45 | #include "csio_init.h" |
46 | |
47 | static void |
48 | csio_vport_set_state(struct csio_lnode *ln); |
49 | |
50 | /* |
51 | * csio_reg_rnode - Register a remote port with FC transport. |
52 | * @rn: Rnode representing remote port. |
53 | * |
54 | * Call fc_remote_port_add() to register this remote port with FC transport. |
55 | * If remote port is Initiator OR Target OR both, change the role appropriately. |
56 | * |
57 | */ |
58 | void |
59 | csio_reg_rnode(struct csio_rnode *rn) |
60 | { |
61 | struct csio_lnode *ln = csio_rnode_to_lnode(rn); |
62 | struct Scsi_Host *shost = csio_ln_to_shost(ln); |
63 | struct fc_rport_identifiers ids; |
64 | struct fc_rport *rport; |
65 | struct csio_service_parms *sp; |
66 | |
67 | ids.node_name = wwn_to_u64(csio_rn_wwnn(rn)); |
68 | ids.port_name = wwn_to_u64(csio_rn_wwpn(rn)); |
69 | ids.port_id = rn->nport_id; |
70 | ids.roles = FC_RPORT_ROLE_UNKNOWN; |
71 | |
72 | if (rn->role & CSIO_RNFR_INITIATOR || rn->role & CSIO_RNFR_TARGET) { |
73 | rport = rn->rport; |
74 | CSIO_ASSERT(rport != NULL); |
75 | goto update_role; |
76 | } |
77 | |
78 | rn->rport = fc_remote_port_add(shost, channel: 0, ids: &ids); |
79 | if (!rn->rport) { |
80 | csio_ln_err(ln, "Failed to register rport = 0x%x.\n" , |
81 | rn->nport_id); |
82 | return; |
83 | } |
84 | |
85 | ln->num_reg_rnodes++; |
86 | rport = rn->rport; |
87 | spin_lock_irq(lock: shost->host_lock); |
88 | *((struct csio_rnode **)rport->dd_data) = rn; |
89 | spin_unlock_irq(lock: shost->host_lock); |
90 | |
91 | sp = &rn->rn_sparm; |
92 | rport->maxframe_size = ntohs(sp->csp.sp_bb_data); |
93 | if (ntohs(sp->clsp[2].cp_class) & FC_CPC_VALID) |
94 | rport->supported_classes = FC_COS_CLASS3; |
95 | else |
96 | rport->supported_classes = FC_COS_UNSPECIFIED; |
97 | update_role: |
98 | if (rn->role & CSIO_RNFR_INITIATOR) |
99 | ids.roles |= FC_RPORT_ROLE_FCP_INITIATOR; |
100 | if (rn->role & CSIO_RNFR_TARGET) |
101 | ids.roles |= FC_RPORT_ROLE_FCP_TARGET; |
102 | |
103 | if (ids.roles != FC_RPORT_ROLE_UNKNOWN) |
104 | fc_remote_port_rolechg(rport, roles: ids.roles); |
105 | |
106 | rn->scsi_id = rport->scsi_target_id; |
107 | |
108 | csio_ln_dbg(ln, "Remote port x%x role 0x%x registered\n" , |
109 | rn->nport_id, ids.roles); |
110 | } |
111 | |
112 | /* |
113 | * csio_unreg_rnode - Unregister a remote port with FC transport. |
114 | * @rn: Rnode representing remote port. |
115 | * |
116 | * Call fc_remote_port_delete() to unregister this remote port with FC |
117 | * transport. |
118 | * |
119 | */ |
120 | void |
121 | csio_unreg_rnode(struct csio_rnode *rn) |
122 | { |
123 | struct csio_lnode *ln = csio_rnode_to_lnode(rn); |
124 | struct fc_rport *rport = rn->rport; |
125 | |
126 | rn->role &= ~(CSIO_RNFR_INITIATOR | CSIO_RNFR_TARGET); |
127 | fc_remote_port_delete(rport); |
128 | ln->num_reg_rnodes--; |
129 | |
130 | csio_ln_dbg(ln, "Remote port x%x un-registered\n" , rn->nport_id); |
131 | } |
132 | |
133 | /* |
134 | * csio_lnode_async_event - Async events from local port. |
135 | * @ln: lnode representing local port. |
136 | * |
137 | * Async events from local node that FC transport/SCSI ML |
138 | * should be made aware of (Eg: RSCN). |
139 | */ |
140 | void |
141 | csio_lnode_async_event(struct csio_lnode *ln, enum csio_ln_fc_evt fc_evt) |
142 | { |
143 | switch (fc_evt) { |
144 | case CSIO_LN_FC_RSCN: |
145 | /* Get payload of rscn from ln */ |
146 | /* For each RSCN entry */ |
147 | /* |
148 | * fc_host_post_event(shost, |
149 | * fc_get_event_number(), |
150 | * FCH_EVT_RSCN, |
151 | * rscn_entry); |
152 | */ |
153 | break; |
154 | case CSIO_LN_FC_LINKUP: |
155 | /* send fc_host_post_event */ |
156 | /* set vport state */ |
157 | if (csio_is_npiv_ln(ln)) |
158 | csio_vport_set_state(ln); |
159 | |
160 | break; |
161 | case CSIO_LN_FC_LINKDOWN: |
162 | /* send fc_host_post_event */ |
163 | /* set vport state */ |
164 | if (csio_is_npiv_ln(ln)) |
165 | csio_vport_set_state(ln); |
166 | |
167 | break; |
168 | case CSIO_LN_FC_ATTRIB_UPDATE: |
169 | csio_fchost_attr_init(ln); |
170 | break; |
171 | default: |
172 | break; |
173 | } |
174 | } |
175 | |
176 | /* |
177 | * csio_fchost_attr_init - Initialize FC transport attributes |
178 | * @ln: Lnode. |
179 | * |
180 | */ |
181 | void |
182 | csio_fchost_attr_init(struct csio_lnode *ln) |
183 | { |
184 | struct Scsi_Host *shost = csio_ln_to_shost(ln); |
185 | |
186 | fc_host_node_name(shost) = wwn_to_u64(csio_ln_wwnn(ln)); |
187 | fc_host_port_name(shost) = wwn_to_u64(csio_ln_wwpn(ln)); |
188 | |
189 | fc_host_supported_classes(shost) = FC_COS_CLASS3; |
190 | fc_host_max_npiv_vports(shost) = |
191 | (csio_lnode_to_hw(ln))->fres_info.max_vnps; |
192 | fc_host_supported_speeds(shost) = FC_PORTSPEED_10GBIT | |
193 | FC_PORTSPEED_1GBIT; |
194 | |
195 | fc_host_maxframe_size(shost) = ntohs(ln->ln_sparm.csp.sp_bb_data); |
196 | memset(fc_host_supported_fc4s(shost), 0, |
197 | sizeof(fc_host_supported_fc4s(shost))); |
198 | fc_host_supported_fc4s(shost)[7] = 1; |
199 | |
200 | memset(fc_host_active_fc4s(shost), 0, |
201 | sizeof(fc_host_active_fc4s(shost))); |
202 | fc_host_active_fc4s(shost)[7] = 1; |
203 | } |
204 | |
205 | /* |
206 | * csio_get_host_port_id - sysfs entries for nport_id is |
207 | * populated/cached from this function |
208 | */ |
209 | static void |
210 | csio_get_host_port_id(struct Scsi_Host *shost) |
211 | { |
212 | struct csio_lnode *ln = shost_priv(shost); |
213 | struct csio_hw *hw = csio_lnode_to_hw(ln); |
214 | |
215 | spin_lock_irq(lock: &hw->lock); |
216 | fc_host_port_id(shost) = ln->nport_id; |
217 | spin_unlock_irq(lock: &hw->lock); |
218 | } |
219 | |
220 | /* |
221 | * csio_get_port_type - Return FC local port type. |
222 | * @shost: scsi host. |
223 | * |
224 | */ |
225 | static void |
226 | csio_get_host_port_type(struct Scsi_Host *shost) |
227 | { |
228 | struct csio_lnode *ln = shost_priv(shost); |
229 | struct csio_hw *hw = csio_lnode_to_hw(ln); |
230 | |
231 | spin_lock_irq(lock: &hw->lock); |
232 | if (csio_is_npiv_ln(ln)) |
233 | fc_host_port_type(shost) = FC_PORTTYPE_NPIV; |
234 | else |
235 | fc_host_port_type(shost) = FC_PORTTYPE_NPORT; |
236 | spin_unlock_irq(lock: &hw->lock); |
237 | } |
238 | |
239 | /* |
240 | * csio_get_port_state - Return FC local port state. |
241 | * @shost: scsi host. |
242 | * |
243 | */ |
244 | static void |
245 | csio_get_host_port_state(struct Scsi_Host *shost) |
246 | { |
247 | struct csio_lnode *ln = shost_priv(shost); |
248 | struct csio_hw *hw = csio_lnode_to_hw(ln); |
249 | char state[16]; |
250 | |
251 | spin_lock_irq(lock: &hw->lock); |
252 | |
253 | csio_lnode_state_to_str(ln, str: state); |
254 | if (!strcmp(state, "READY" )) |
255 | fc_host_port_state(shost) = FC_PORTSTATE_ONLINE; |
256 | else if (!strcmp(state, "OFFLINE" )) |
257 | fc_host_port_state(shost) = FC_PORTSTATE_LINKDOWN; |
258 | else |
259 | fc_host_port_state(shost) = FC_PORTSTATE_UNKNOWN; |
260 | |
261 | spin_unlock_irq(lock: &hw->lock); |
262 | } |
263 | |
264 | /* |
265 | * csio_get_host_speed - Return link speed to FC transport. |
266 | * @shost: scsi host. |
267 | * |
268 | */ |
269 | static void |
270 | csio_get_host_speed(struct Scsi_Host *shost) |
271 | { |
272 | struct csio_lnode *ln = shost_priv(shost); |
273 | struct csio_hw *hw = csio_lnode_to_hw(ln); |
274 | |
275 | spin_lock_irq(lock: &hw->lock); |
276 | switch (hw->pport[ln->portid].link_speed) { |
277 | case FW_PORT_CAP32_SPEED_1G: |
278 | fc_host_speed(shost) = FC_PORTSPEED_1GBIT; |
279 | break; |
280 | case FW_PORT_CAP32_SPEED_10G: |
281 | fc_host_speed(shost) = FC_PORTSPEED_10GBIT; |
282 | break; |
283 | case FW_PORT_CAP32_SPEED_25G: |
284 | fc_host_speed(shost) = FC_PORTSPEED_25GBIT; |
285 | break; |
286 | case FW_PORT_CAP32_SPEED_40G: |
287 | fc_host_speed(shost) = FC_PORTSPEED_40GBIT; |
288 | break; |
289 | case FW_PORT_CAP32_SPEED_50G: |
290 | fc_host_speed(shost) = FC_PORTSPEED_50GBIT; |
291 | break; |
292 | case FW_PORT_CAP32_SPEED_100G: |
293 | fc_host_speed(shost) = FC_PORTSPEED_100GBIT; |
294 | break; |
295 | default: |
296 | fc_host_speed(shost) = FC_PORTSPEED_UNKNOWN; |
297 | break; |
298 | } |
299 | spin_unlock_irq(lock: &hw->lock); |
300 | } |
301 | |
302 | /* |
303 | * csio_get_host_fabric_name - Return fabric name |
304 | * @shost: scsi host. |
305 | * |
306 | */ |
307 | static void |
308 | csio_get_host_fabric_name(struct Scsi_Host *shost) |
309 | { |
310 | struct csio_lnode *ln = shost_priv(shost); |
311 | struct csio_rnode *rn = NULL; |
312 | struct csio_hw *hw = csio_lnode_to_hw(ln); |
313 | |
314 | spin_lock_irq(lock: &hw->lock); |
315 | rn = csio_rnode_lookup_portid(ln, FC_FID_FLOGI); |
316 | if (rn) |
317 | fc_host_fabric_name(shost) = wwn_to_u64(csio_rn_wwnn(rn)); |
318 | else |
319 | fc_host_fabric_name(shost) = 0; |
320 | spin_unlock_irq(lock: &hw->lock); |
321 | } |
322 | |
323 | /* |
324 | * csio_get_host_speed - Return FC transport statistics. |
325 | * @ln: Lnode. |
326 | * |
327 | */ |
328 | static struct fc_host_statistics * |
329 | csio_get_stats(struct Scsi_Host *shost) |
330 | { |
331 | struct csio_lnode *ln = shost_priv(shost); |
332 | struct csio_hw *hw = csio_lnode_to_hw(ln); |
333 | struct fc_host_statistics *fhs = &ln->fch_stats; |
334 | struct fw_fcoe_port_stats fcoe_port_stats; |
335 | uint64_t seconds; |
336 | |
337 | memset(&fcoe_port_stats, 0, sizeof(struct fw_fcoe_port_stats)); |
338 | csio_get_phy_port_stats(hw, ln->portid, &fcoe_port_stats); |
339 | |
340 | fhs->tx_frames += (be64_to_cpu(fcoe_port_stats.tx_bcast_frames) + |
341 | be64_to_cpu(fcoe_port_stats.tx_mcast_frames) + |
342 | be64_to_cpu(fcoe_port_stats.tx_ucast_frames) + |
343 | be64_to_cpu(fcoe_port_stats.tx_offload_frames)); |
344 | fhs->tx_words += (be64_to_cpu(fcoe_port_stats.tx_bcast_bytes) + |
345 | be64_to_cpu(fcoe_port_stats.tx_mcast_bytes) + |
346 | be64_to_cpu(fcoe_port_stats.tx_ucast_bytes) + |
347 | be64_to_cpu(fcoe_port_stats.tx_offload_bytes)) / |
348 | CSIO_WORD_TO_BYTE; |
349 | fhs->rx_frames += (be64_to_cpu(fcoe_port_stats.rx_bcast_frames) + |
350 | be64_to_cpu(fcoe_port_stats.rx_mcast_frames) + |
351 | be64_to_cpu(fcoe_port_stats.rx_ucast_frames)); |
352 | fhs->rx_words += (be64_to_cpu(fcoe_port_stats.rx_bcast_bytes) + |
353 | be64_to_cpu(fcoe_port_stats.rx_mcast_bytes) + |
354 | be64_to_cpu(fcoe_port_stats.rx_ucast_bytes)) / |
355 | CSIO_WORD_TO_BYTE; |
356 | fhs->error_frames += be64_to_cpu(fcoe_port_stats.rx_err_frames); |
357 | fhs->fcp_input_requests += ln->stats.n_input_requests; |
358 | fhs->fcp_output_requests += ln->stats.n_output_requests; |
359 | fhs->fcp_control_requests += ln->stats.n_control_requests; |
360 | fhs->fcp_input_megabytes += ln->stats.n_input_bytes >> 20; |
361 | fhs->fcp_output_megabytes += ln->stats.n_output_bytes >> 20; |
362 | fhs->link_failure_count = ln->stats.n_link_down; |
363 | /* Reset stats for the device */ |
364 | seconds = jiffies_to_msecs(j: jiffies) - hw->stats.n_reset_start; |
365 | do_div(seconds, 1000); |
366 | fhs->seconds_since_last_reset = seconds; |
367 | |
368 | return fhs; |
369 | } |
370 | |
371 | /* |
372 | * csio_set_rport_loss_tmo - Set the rport dev loss timeout |
373 | * @rport: fc rport. |
374 | * @timeout: new value for dev loss tmo. |
375 | * |
376 | * If timeout is non zero set the dev_loss_tmo to timeout, else set |
377 | * dev_loss_tmo to one. |
378 | */ |
379 | static void |
380 | csio_set_rport_loss_tmo(struct fc_rport *rport, uint32_t timeout) |
381 | { |
382 | if (timeout) |
383 | rport->dev_loss_tmo = timeout; |
384 | else |
385 | rport->dev_loss_tmo = 1; |
386 | } |
387 | |
388 | static void |
389 | csio_vport_set_state(struct csio_lnode *ln) |
390 | { |
391 | struct fc_vport *fc_vport = ln->fc_vport; |
392 | struct csio_lnode *pln = ln->pln; |
393 | char state[16]; |
394 | |
395 | /* Set fc vport state based on phyiscal lnode */ |
396 | csio_lnode_state_to_str(ln: pln, str: state); |
397 | if (strcmp(state, "READY" )) { |
398 | fc_vport_set_state(vport: fc_vport, new_state: FC_VPORT_LINKDOWN); |
399 | return; |
400 | } |
401 | |
402 | if (!(pln->flags & CSIO_LNF_NPIVSUPP)) { |
403 | fc_vport_set_state(vport: fc_vport, new_state: FC_VPORT_NO_FABRIC_SUPP); |
404 | return; |
405 | } |
406 | |
407 | /* Set fc vport state based on virtual lnode */ |
408 | csio_lnode_state_to_str(ln, str: state); |
409 | if (strcmp(state, "READY" )) { |
410 | fc_vport_set_state(vport: fc_vport, new_state: FC_VPORT_LINKDOWN); |
411 | return; |
412 | } |
413 | fc_vport_set_state(vport: fc_vport, new_state: FC_VPORT_ACTIVE); |
414 | } |
415 | |
416 | static int |
417 | csio_fcoe_alloc_vnp(struct csio_hw *hw, struct csio_lnode *ln) |
418 | { |
419 | struct csio_lnode *pln; |
420 | struct csio_mb *mbp; |
421 | struct fw_fcoe_vnp_cmd *rsp; |
422 | int ret = 0; |
423 | int retry = 0; |
424 | |
425 | /* Issue VNP cmd to alloc vport */ |
426 | /* Allocate Mbox request */ |
427 | spin_lock_irq(lock: &hw->lock); |
428 | mbp = mempool_alloc(pool: hw->mb_mempool, GFP_ATOMIC); |
429 | if (!mbp) { |
430 | CSIO_INC_STATS(hw, n_err_nomem); |
431 | ret = -ENOMEM; |
432 | goto out; |
433 | } |
434 | |
435 | pln = ln->pln; |
436 | ln->fcf_flowid = pln->fcf_flowid; |
437 | ln->portid = pln->portid; |
438 | |
439 | csio_fcoe_vnp_alloc_init_mb(ln, mbp, CSIO_MB_DEFAULT_TMO, |
440 | pln->fcf_flowid, pln->vnp_flowid, 0, |
441 | csio_ln_wwnn(ln), csio_ln_wwpn(ln), NULL); |
442 | |
443 | for (retry = 0; retry < 3; retry++) { |
444 | /* FW is expected to complete vnp cmd in immediate mode |
445 | * without much delay. |
446 | * Otherwise, there will be increase in IO latency since HW |
447 | * lock is held till completion of vnp mbox cmd. |
448 | */ |
449 | ret = csio_mb_issue(hw, mbp); |
450 | if (ret != -EBUSY) |
451 | break; |
452 | |
453 | /* Retry if mbox returns busy */ |
454 | spin_unlock_irq(lock: &hw->lock); |
455 | msleep(msecs: 2000); |
456 | spin_lock_irq(lock: &hw->lock); |
457 | } |
458 | |
459 | if (ret) { |
460 | csio_ln_err(ln, "Failed to issue mbox FCoE VNP command\n" ); |
461 | goto out_free; |
462 | } |
463 | |
464 | /* Process Mbox response of VNP command */ |
465 | rsp = (struct fw_fcoe_vnp_cmd *)(mbp->mb); |
466 | if (FW_CMD_RETVAL_G(ntohl(rsp->alloc_to_len16)) != FW_SUCCESS) { |
467 | csio_ln_err(ln, "FCOE VNP ALLOC cmd returned 0x%x!\n" , |
468 | FW_CMD_RETVAL_G(ntohl(rsp->alloc_to_len16))); |
469 | ret = -EINVAL; |
470 | goto out_free; |
471 | } |
472 | |
473 | ln->vnp_flowid = FW_FCOE_VNP_CMD_VNPI_GET( |
474 | ntohl(rsp->gen_wwn_to_vnpi)); |
475 | memcpy(csio_ln_wwnn(ln), rsp->vnport_wwnn, 8); |
476 | memcpy(csio_ln_wwpn(ln), rsp->vnport_wwpn, 8); |
477 | |
478 | csio_ln_dbg(ln, "FCOE VNPI: 0x%x\n" , ln->vnp_flowid); |
479 | csio_ln_dbg(ln, "\tWWNN: %x%x%x%x%x%x%x%x\n" , |
480 | ln->ln_sparm.wwnn[0], ln->ln_sparm.wwnn[1], |
481 | ln->ln_sparm.wwnn[2], ln->ln_sparm.wwnn[3], |
482 | ln->ln_sparm.wwnn[4], ln->ln_sparm.wwnn[5], |
483 | ln->ln_sparm.wwnn[6], ln->ln_sparm.wwnn[7]); |
484 | csio_ln_dbg(ln, "\tWWPN: %x%x%x%x%x%x%x%x\n" , |
485 | ln->ln_sparm.wwpn[0], ln->ln_sparm.wwpn[1], |
486 | ln->ln_sparm.wwpn[2], ln->ln_sparm.wwpn[3], |
487 | ln->ln_sparm.wwpn[4], ln->ln_sparm.wwpn[5], |
488 | ln->ln_sparm.wwpn[6], ln->ln_sparm.wwpn[7]); |
489 | |
490 | out_free: |
491 | mempool_free(element: mbp, pool: hw->mb_mempool); |
492 | out: |
493 | spin_unlock_irq(lock: &hw->lock); |
494 | return ret; |
495 | } |
496 | |
497 | static int |
498 | csio_fcoe_free_vnp(struct csio_hw *hw, struct csio_lnode *ln) |
499 | { |
500 | struct csio_mb *mbp; |
501 | struct fw_fcoe_vnp_cmd *rsp; |
502 | int ret = 0; |
503 | int retry = 0; |
504 | |
505 | /* Issue VNP cmd to free vport */ |
506 | /* Allocate Mbox request */ |
507 | |
508 | spin_lock_irq(lock: &hw->lock); |
509 | mbp = mempool_alloc(pool: hw->mb_mempool, GFP_ATOMIC); |
510 | if (!mbp) { |
511 | CSIO_INC_STATS(hw, n_err_nomem); |
512 | ret = -ENOMEM; |
513 | goto out; |
514 | } |
515 | |
516 | csio_fcoe_vnp_free_init_mb(ln, mbp, CSIO_MB_DEFAULT_TMO, |
517 | ln->fcf_flowid, ln->vnp_flowid, |
518 | NULL); |
519 | |
520 | for (retry = 0; retry < 3; retry++) { |
521 | ret = csio_mb_issue(hw, mbp); |
522 | if (ret != -EBUSY) |
523 | break; |
524 | |
525 | /* Retry if mbox returns busy */ |
526 | spin_unlock_irq(lock: &hw->lock); |
527 | msleep(msecs: 2000); |
528 | spin_lock_irq(lock: &hw->lock); |
529 | } |
530 | |
531 | if (ret) { |
532 | csio_ln_err(ln, "Failed to issue mbox FCoE VNP command\n" ); |
533 | goto out_free; |
534 | } |
535 | |
536 | /* Process Mbox response of VNP command */ |
537 | rsp = (struct fw_fcoe_vnp_cmd *)(mbp->mb); |
538 | if (FW_CMD_RETVAL_G(ntohl(rsp->alloc_to_len16)) != FW_SUCCESS) { |
539 | csio_ln_err(ln, "FCOE VNP FREE cmd returned 0x%x!\n" , |
540 | FW_CMD_RETVAL_G(ntohl(rsp->alloc_to_len16))); |
541 | ret = -EINVAL; |
542 | } |
543 | |
544 | out_free: |
545 | mempool_free(element: mbp, pool: hw->mb_mempool); |
546 | out: |
547 | spin_unlock_irq(lock: &hw->lock); |
548 | return ret; |
549 | } |
550 | |
551 | static int |
552 | csio_vport_create(struct fc_vport *fc_vport, bool disable) |
553 | { |
554 | struct Scsi_Host *shost = fc_vport->shost; |
555 | struct csio_lnode *pln = shost_priv(shost); |
556 | struct csio_lnode *ln = NULL; |
557 | struct csio_hw *hw = csio_lnode_to_hw(pln); |
558 | uint8_t wwn[8]; |
559 | int ret = -1; |
560 | |
561 | ln = csio_shost_init(hw, &fc_vport->dev, false, pln); |
562 | if (!ln) |
563 | goto error; |
564 | |
565 | if (fc_vport->node_name != 0) { |
566 | u64_to_wwn(inm: fc_vport->node_name, wwn); |
567 | |
568 | if (!CSIO_VALID_WWN(wwn)) { |
569 | csio_ln_err(ln, |
570 | "vport create failed. Invalid wwnn\n" ); |
571 | goto error; |
572 | } |
573 | memcpy(csio_ln_wwnn(ln), wwn, 8); |
574 | } |
575 | |
576 | if (fc_vport->port_name != 0) { |
577 | u64_to_wwn(inm: fc_vport->port_name, wwn); |
578 | |
579 | if (!CSIO_VALID_WWN(wwn)) { |
580 | csio_ln_err(ln, |
581 | "vport create failed. Invalid wwpn\n" ); |
582 | goto error; |
583 | } |
584 | |
585 | if (csio_lnode_lookup_by_wwpn(hw, wwn)) { |
586 | csio_ln_err(ln, |
587 | "vport create failed. wwpn already exists\n" ); |
588 | goto error; |
589 | } |
590 | memcpy(csio_ln_wwpn(ln), wwn, 8); |
591 | } |
592 | |
593 | fc_vport_set_state(vport: fc_vport, new_state: FC_VPORT_INITIALIZING); |
594 | ln->fc_vport = fc_vport; |
595 | |
596 | if (csio_fcoe_alloc_vnp(hw, ln)) |
597 | goto error; |
598 | |
599 | *(struct csio_lnode **)fc_vport->dd_data = ln; |
600 | if (!fc_vport->node_name) |
601 | fc_vport->node_name = wwn_to_u64(csio_ln_wwnn(ln)); |
602 | if (!fc_vport->port_name) |
603 | fc_vport->port_name = wwn_to_u64(csio_ln_wwpn(ln)); |
604 | csio_fchost_attr_init(ln); |
605 | return 0; |
606 | error: |
607 | if (ln) |
608 | csio_shost_exit(ln); |
609 | |
610 | return ret; |
611 | } |
612 | |
613 | static int |
614 | csio_vport_delete(struct fc_vport *fc_vport) |
615 | { |
616 | struct csio_lnode *ln = *(struct csio_lnode **)fc_vport->dd_data; |
617 | struct Scsi_Host *shost = csio_ln_to_shost(ln); |
618 | struct csio_hw *hw = csio_lnode_to_hw(ln); |
619 | int rmv; |
620 | |
621 | spin_lock_irq(lock: &hw->lock); |
622 | rmv = csio_is_hw_removing(hw); |
623 | spin_unlock_irq(lock: &hw->lock); |
624 | |
625 | if (rmv) { |
626 | csio_shost_exit(ln); |
627 | return 0; |
628 | } |
629 | |
630 | /* Quiesce ios and send remove event to lnode */ |
631 | scsi_block_requests(shost); |
632 | spin_lock_irq(lock: &hw->lock); |
633 | csio_scsim_cleanup_io_lnode(csio_hw_to_scsim(hw), ln); |
634 | csio_lnode_close(ln); |
635 | spin_unlock_irq(lock: &hw->lock); |
636 | scsi_unblock_requests(shost); |
637 | |
638 | /* Free vnp */ |
639 | if (fc_vport->vport_state != FC_VPORT_DISABLED) |
640 | csio_fcoe_free_vnp(hw, ln); |
641 | |
642 | csio_shost_exit(ln); |
643 | return 0; |
644 | } |
645 | |
646 | static int |
647 | csio_vport_disable(struct fc_vport *fc_vport, bool disable) |
648 | { |
649 | struct csio_lnode *ln = *(struct csio_lnode **)fc_vport->dd_data; |
650 | struct Scsi_Host *shost = csio_ln_to_shost(ln); |
651 | struct csio_hw *hw = csio_lnode_to_hw(ln); |
652 | |
653 | /* disable vport */ |
654 | if (disable) { |
655 | /* Quiesce ios and send stop event to lnode */ |
656 | scsi_block_requests(shost); |
657 | spin_lock_irq(lock: &hw->lock); |
658 | csio_scsim_cleanup_io_lnode(csio_hw_to_scsim(hw), ln); |
659 | csio_lnode_stop(ln); |
660 | spin_unlock_irq(lock: &hw->lock); |
661 | scsi_unblock_requests(shost); |
662 | |
663 | /* Free vnp */ |
664 | csio_fcoe_free_vnp(hw, ln); |
665 | fc_vport_set_state(vport: fc_vport, new_state: FC_VPORT_DISABLED); |
666 | csio_ln_err(ln, "vport disabled\n" ); |
667 | return 0; |
668 | } else { |
669 | /* enable vport */ |
670 | fc_vport_set_state(vport: fc_vport, new_state: FC_VPORT_INITIALIZING); |
671 | if (csio_fcoe_alloc_vnp(hw, ln)) { |
672 | csio_ln_err(ln, "vport enabled failed.\n" ); |
673 | return -1; |
674 | } |
675 | csio_ln_err(ln, "vport enabled\n" ); |
676 | return 0; |
677 | } |
678 | } |
679 | |
680 | static void |
681 | csio_dev_loss_tmo_callbk(struct fc_rport *rport) |
682 | { |
683 | struct csio_rnode *rn; |
684 | struct csio_hw *hw; |
685 | struct csio_lnode *ln; |
686 | |
687 | rn = *((struct csio_rnode **)rport->dd_data); |
688 | ln = csio_rnode_to_lnode(rn); |
689 | hw = csio_lnode_to_hw(ln); |
690 | |
691 | spin_lock_irq(lock: &hw->lock); |
692 | |
693 | /* return if driver is being removed or same rnode comes back online */ |
694 | if (csio_is_hw_removing(hw) || csio_is_rnode_ready(rn)) |
695 | goto out; |
696 | |
697 | csio_ln_dbg(ln, "devloss timeout on rnode:%p portid:x%x flowid:x%x\n" , |
698 | rn, rn->nport_id, csio_rn_flowid(rn)); |
699 | |
700 | CSIO_INC_STATS(ln, n_dev_loss_tmo); |
701 | |
702 | /* |
703 | * enqueue devloss event to event worker thread to serialize all |
704 | * rnode events. |
705 | */ |
706 | if (csio_enqueue_evt(hw, CSIO_EVT_DEV_LOSS, &rn, sizeof(rn))) { |
707 | CSIO_INC_STATS(hw, n_evt_drop); |
708 | goto out; |
709 | } |
710 | |
711 | if (!(hw->flags & CSIO_HWF_FWEVT_PENDING)) { |
712 | hw->flags |= CSIO_HWF_FWEVT_PENDING; |
713 | spin_unlock_irq(lock: &hw->lock); |
714 | schedule_work(work: &hw->evtq_work); |
715 | return; |
716 | } |
717 | |
718 | out: |
719 | spin_unlock_irq(lock: &hw->lock); |
720 | } |
721 | |
722 | /* FC transport functions template - Physical port */ |
723 | struct fc_function_template csio_fc_transport_funcs = { |
724 | .show_host_node_name = 1, |
725 | .show_host_port_name = 1, |
726 | .show_host_supported_classes = 1, |
727 | .show_host_supported_fc4s = 1, |
728 | .show_host_maxframe_size = 1, |
729 | |
730 | .get_host_port_id = csio_get_host_port_id, |
731 | .show_host_port_id = 1, |
732 | |
733 | .get_host_port_type = csio_get_host_port_type, |
734 | .show_host_port_type = 1, |
735 | |
736 | .get_host_port_state = csio_get_host_port_state, |
737 | .show_host_port_state = 1, |
738 | |
739 | .show_host_active_fc4s = 1, |
740 | .get_host_speed = csio_get_host_speed, |
741 | .show_host_speed = 1, |
742 | .get_host_fabric_name = csio_get_host_fabric_name, |
743 | .show_host_fabric_name = 1, |
744 | |
745 | .get_fc_host_stats = csio_get_stats, |
746 | |
747 | .dd_fcrport_size = sizeof(struct csio_rnode *), |
748 | .show_rport_maxframe_size = 1, |
749 | .show_rport_supported_classes = 1, |
750 | |
751 | .set_rport_dev_loss_tmo = csio_set_rport_loss_tmo, |
752 | .show_rport_dev_loss_tmo = 1, |
753 | |
754 | .show_starget_port_id = 1, |
755 | .show_starget_node_name = 1, |
756 | .show_starget_port_name = 1, |
757 | |
758 | .dev_loss_tmo_callbk = csio_dev_loss_tmo_callbk, |
759 | .dd_fcvport_size = sizeof(struct csio_lnode *), |
760 | |
761 | .vport_create = csio_vport_create, |
762 | .vport_disable = csio_vport_disable, |
763 | .vport_delete = csio_vport_delete, |
764 | }; |
765 | |
766 | /* FC transport functions template - Virtual port */ |
767 | struct fc_function_template csio_fc_transport_vport_funcs = { |
768 | .show_host_node_name = 1, |
769 | .show_host_port_name = 1, |
770 | .show_host_supported_classes = 1, |
771 | .show_host_supported_fc4s = 1, |
772 | .show_host_maxframe_size = 1, |
773 | |
774 | .get_host_port_id = csio_get_host_port_id, |
775 | .show_host_port_id = 1, |
776 | |
777 | .get_host_port_type = csio_get_host_port_type, |
778 | .show_host_port_type = 1, |
779 | |
780 | .get_host_port_state = csio_get_host_port_state, |
781 | .show_host_port_state = 1, |
782 | .show_host_active_fc4s = 1, |
783 | |
784 | .get_host_speed = csio_get_host_speed, |
785 | .show_host_speed = 1, |
786 | |
787 | .get_host_fabric_name = csio_get_host_fabric_name, |
788 | .show_host_fabric_name = 1, |
789 | |
790 | .get_fc_host_stats = csio_get_stats, |
791 | |
792 | .dd_fcrport_size = sizeof(struct csio_rnode *), |
793 | .show_rport_maxframe_size = 1, |
794 | .show_rport_supported_classes = 1, |
795 | |
796 | .set_rport_dev_loss_tmo = csio_set_rport_loss_tmo, |
797 | .show_rport_dev_loss_tmo = 1, |
798 | |
799 | .show_starget_port_id = 1, |
800 | .show_starget_node_name = 1, |
801 | .show_starget_port_name = 1, |
802 | |
803 | .dev_loss_tmo_callbk = csio_dev_loss_tmo_callbk, |
804 | |
805 | }; |
806 | |