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/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, 0, &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(shost->host_lock); | |
88 | *((struct csio_rnode **)rport->dd_data) = rn; | |
89 | spin_unlock_irq(shost->host_lock); | |
90 | ||
91 | sp = &rn->rn_sparm; | |
5036f0a0 | 92 | rport->maxframe_size = ntohs(sp->csp.sp_bb_data); |
a3667aae NKI |
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, 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 | ||
5036f0a0 | 195 | fc_host_maxframe_size(shost) = ntohs(ln->ln_sparm.csp.sp_bb_data); |
a3667aae NKI |
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(&hw->lock); | |
216 | fc_host_port_id(shost) = ln->nport_id; | |
217 | spin_unlock_irq(&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(&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(&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(&hw->lock); | |
252 | ||
253 | csio_lnode_state_to_str(ln, 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(&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(&hw->lock); | |
276 | switch (hw->pport[ln->portid].link_speed) { | |
e1735d9a | 277 | case FW_PORT_CAP32_SPEED_1G: |
a3667aae NKI |
278 | fc_host_speed(shost) = FC_PORTSPEED_1GBIT; |
279 | break; | |
e1735d9a | 280 | case FW_PORT_CAP32_SPEED_10G: |
a3667aae NKI |
281 | fc_host_speed(shost) = FC_PORTSPEED_10GBIT; |
282 | break; | |
e1735d9a VP |
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; | |
a3667aae NKI |
295 | default: |
296 | fc_host_speed(shost) = FC_PORTSPEED_UNKNOWN; | |
297 | break; | |
298 | } | |
299 | spin_unlock_irq(&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(&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(&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 | ||
5036f0a0 NKI |
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)) / | |
a3667aae | 348 | CSIO_WORD_TO_BYTE; |
5036f0a0 NKI |
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)) / | |
a3667aae | 355 | CSIO_WORD_TO_BYTE; |
5036f0a0 | 356 | fhs->error_frames += be64_to_cpu(fcoe_port_stats.rx_err_frames); |
a3667aae NKI |
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(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(pln, state); | |
397 | if (strcmp(state, "READY")) { | |
398 | fc_vport_set_state(fc_vport, FC_VPORT_LINKDOWN); | |
399 | return; | |
400 | } | |
401 | ||
402 | if (!(pln->flags & CSIO_LNF_NPIVSUPP)) { | |
403 | fc_vport_set_state(fc_vport, 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, state); | |
409 | if (strcmp(state, "READY")) { | |
410 | fc_vport_set_state(fc_vport, FC_VPORT_LINKDOWN); | |
411 | return; | |
412 | } | |
413 | fc_vport_set_state(fc_vport, 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(&hw->lock); | |
428 | mbp = mempool_alloc(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(&hw->lock); | |
455 | msleep(2000); | |
456 | spin_lock_irq(&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); | |
e2ac9628 | 466 | if (FW_CMD_RETVAL_G(ntohl(rsp->alloc_to_len16)) != FW_SUCCESS) { |
a3667aae | 467 | csio_ln_err(ln, "FCOE VNP ALLOC cmd returned 0x%x!\n", |
e2ac9628 | 468 | FW_CMD_RETVAL_G(ntohl(rsp->alloc_to_len16))); |
a3667aae NKI |
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(mbp, hw->mb_mempool); | |
492 | out: | |
493 | spin_unlock_irq(&hw->lock); | |
494 | return ret; | |
495 | } | |
496 | ||
497 | static int | |
498 | csio_fcoe_free_vnp(struct csio_hw *hw, struct csio_lnode *ln) | |
499 | { | |
a3667aae NKI |
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(&hw->lock); | |
509 | mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC); | |
510 | if (!mbp) { | |
511 | CSIO_INC_STATS(hw, n_err_nomem); | |
512 | ret = -ENOMEM; | |
513 | goto out; | |
514 | } | |
515 | ||
a3667aae NKI |
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(&hw->lock); | |
527 | msleep(2000); | |
528 | spin_lock_irq(&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); | |
e2ac9628 | 538 | if (FW_CMD_RETVAL_G(ntohl(rsp->alloc_to_len16)) != FW_SUCCESS) { |
a3667aae | 539 | csio_ln_err(ln, "FCOE VNP FREE cmd returned 0x%x!\n", |
e2ac9628 | 540 | FW_CMD_RETVAL_G(ntohl(rsp->alloc_to_len16))); |
a3667aae NKI |
541 | ret = -EINVAL; |
542 | } | |
543 | ||
544 | out_free: | |
545 | mempool_free(mbp, hw->mb_mempool); | |
546 | out: | |
547 | spin_unlock_irq(&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(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(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(fc_vport, FC_VPORT_INITIALIZING); | |
fe35a40e | 594 | ln->fc_vport = fc_vport; |
a3667aae NKI |
595 | |
596 | if (csio_fcoe_alloc_vnp(hw, ln)) | |
597 | goto error; | |
598 | ||
599 | *(struct csio_lnode **)fc_vport->dd_data = ln; | |
a3667aae NKI |
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(&hw->lock); | |
622 | rmv = csio_is_hw_removing(hw); | |
623 | spin_unlock_irq(&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(&hw->lock); | |
633 | csio_scsim_cleanup_io_lnode(csio_hw_to_scsim(hw), ln); | |
634 | csio_lnode_close(ln); | |
635 | spin_unlock_irq(&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(&hw->lock); | |
658 | csio_scsim_cleanup_io_lnode(csio_hw_to_scsim(hw), ln); | |
659 | csio_lnode_stop(ln); | |
660 | spin_unlock_irq(&hw->lock); | |
661 | scsi_unblock_requests(shost); | |
662 | ||
663 | /* Free vnp */ | |
664 | csio_fcoe_free_vnp(hw, ln); | |
665 | fc_vport_set_state(fc_vport, FC_VPORT_DISABLED); | |
666 | csio_ln_err(ln, "vport disabled\n"); | |
667 | return 0; | |
668 | } else { | |
669 | /* enable vport */ | |
670 | fc_vport_set_state(fc_vport, 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(&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(&hw->lock); | |
714 | schedule_work(&hw->evtq_work); | |
715 | return; | |
716 | } | |
717 | ||
718 | out: | |
719 | spin_unlock_irq(&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 | }; |