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) { | |
277 | case FW_PORT_CAP_SPEED_1G: | |
278 | fc_host_speed(shost) = FC_PORTSPEED_1GBIT; | |
279 | break; | |
280 | case FW_PORT_CAP_SPEED_10G: | |
281 | fc_host_speed(shost) = FC_PORTSPEED_10GBIT; | |
282 | break; | |
283 | default: | |
284 | fc_host_speed(shost) = FC_PORTSPEED_UNKNOWN; | |
285 | break; | |
286 | } | |
287 | spin_unlock_irq(&hw->lock); | |
288 | } | |
289 | ||
290 | /* | |
291 | * csio_get_host_fabric_name - Return fabric name | |
292 | * @shost: scsi host. | |
293 | * | |
294 | */ | |
295 | static void | |
296 | csio_get_host_fabric_name(struct Scsi_Host *shost) | |
297 | { | |
298 | struct csio_lnode *ln = shost_priv(shost); | |
299 | struct csio_rnode *rn = NULL; | |
300 | struct csio_hw *hw = csio_lnode_to_hw(ln); | |
301 | ||
302 | spin_lock_irq(&hw->lock); | |
303 | rn = csio_rnode_lookup_portid(ln, FC_FID_FLOGI); | |
304 | if (rn) | |
305 | fc_host_fabric_name(shost) = wwn_to_u64(csio_rn_wwnn(rn)); | |
306 | else | |
307 | fc_host_fabric_name(shost) = 0; | |
308 | spin_unlock_irq(&hw->lock); | |
309 | } | |
310 | ||
311 | /* | |
312 | * csio_get_host_speed - Return FC transport statistics. | |
313 | * @ln: Lnode. | |
314 | * | |
315 | */ | |
316 | static struct fc_host_statistics * | |
317 | csio_get_stats(struct Scsi_Host *shost) | |
318 | { | |
319 | struct csio_lnode *ln = shost_priv(shost); | |
320 | struct csio_hw *hw = csio_lnode_to_hw(ln); | |
321 | struct fc_host_statistics *fhs = &ln->fch_stats; | |
322 | struct fw_fcoe_port_stats fcoe_port_stats; | |
323 | uint64_t seconds; | |
324 | ||
325 | memset(&fcoe_port_stats, 0, sizeof(struct fw_fcoe_port_stats)); | |
326 | csio_get_phy_port_stats(hw, ln->portid, &fcoe_port_stats); | |
327 | ||
5036f0a0 NKI |
328 | fhs->tx_frames += (be64_to_cpu(fcoe_port_stats.tx_bcast_frames) + |
329 | be64_to_cpu(fcoe_port_stats.tx_mcast_frames) + | |
330 | be64_to_cpu(fcoe_port_stats.tx_ucast_frames) + | |
331 | be64_to_cpu(fcoe_port_stats.tx_offload_frames)); | |
332 | fhs->tx_words += (be64_to_cpu(fcoe_port_stats.tx_bcast_bytes) + | |
333 | be64_to_cpu(fcoe_port_stats.tx_mcast_bytes) + | |
334 | be64_to_cpu(fcoe_port_stats.tx_ucast_bytes) + | |
335 | be64_to_cpu(fcoe_port_stats.tx_offload_bytes)) / | |
a3667aae | 336 | CSIO_WORD_TO_BYTE; |
5036f0a0 NKI |
337 | fhs->rx_frames += (be64_to_cpu(fcoe_port_stats.rx_bcast_frames) + |
338 | be64_to_cpu(fcoe_port_stats.rx_mcast_frames) + | |
339 | be64_to_cpu(fcoe_port_stats.rx_ucast_frames)); | |
340 | fhs->rx_words += (be64_to_cpu(fcoe_port_stats.rx_bcast_bytes) + | |
341 | be64_to_cpu(fcoe_port_stats.rx_mcast_bytes) + | |
342 | be64_to_cpu(fcoe_port_stats.rx_ucast_bytes)) / | |
a3667aae | 343 | CSIO_WORD_TO_BYTE; |
5036f0a0 | 344 | fhs->error_frames += be64_to_cpu(fcoe_port_stats.rx_err_frames); |
a3667aae NKI |
345 | fhs->fcp_input_requests += ln->stats.n_input_requests; |
346 | fhs->fcp_output_requests += ln->stats.n_output_requests; | |
347 | fhs->fcp_control_requests += ln->stats.n_control_requests; | |
348 | fhs->fcp_input_megabytes += ln->stats.n_input_bytes >> 20; | |
349 | fhs->fcp_output_megabytes += ln->stats.n_output_bytes >> 20; | |
350 | fhs->link_failure_count = ln->stats.n_link_down; | |
351 | /* Reset stats for the device */ | |
352 | seconds = jiffies_to_msecs(jiffies) - hw->stats.n_reset_start; | |
353 | do_div(seconds, 1000); | |
354 | fhs->seconds_since_last_reset = seconds; | |
355 | ||
356 | return fhs; | |
357 | } | |
358 | ||
359 | /* | |
360 | * csio_set_rport_loss_tmo - Set the rport dev loss timeout | |
361 | * @rport: fc rport. | |
362 | * @timeout: new value for dev loss tmo. | |
363 | * | |
364 | * If timeout is non zero set the dev_loss_tmo to timeout, else set | |
365 | * dev_loss_tmo to one. | |
366 | */ | |
367 | static void | |
368 | csio_set_rport_loss_tmo(struct fc_rport *rport, uint32_t timeout) | |
369 | { | |
370 | if (timeout) | |
371 | rport->dev_loss_tmo = timeout; | |
372 | else | |
373 | rport->dev_loss_tmo = 1; | |
374 | } | |
375 | ||
376 | static void | |
377 | csio_vport_set_state(struct csio_lnode *ln) | |
378 | { | |
379 | struct fc_vport *fc_vport = ln->fc_vport; | |
380 | struct csio_lnode *pln = ln->pln; | |
381 | char state[16]; | |
382 | ||
383 | /* Set fc vport state based on phyiscal lnode */ | |
384 | csio_lnode_state_to_str(pln, state); | |
385 | if (strcmp(state, "READY")) { | |
386 | fc_vport_set_state(fc_vport, FC_VPORT_LINKDOWN); | |
387 | return; | |
388 | } | |
389 | ||
390 | if (!(pln->flags & CSIO_LNF_NPIVSUPP)) { | |
391 | fc_vport_set_state(fc_vport, FC_VPORT_NO_FABRIC_SUPP); | |
392 | return; | |
393 | } | |
394 | ||
395 | /* Set fc vport state based on virtual lnode */ | |
396 | csio_lnode_state_to_str(ln, state); | |
397 | if (strcmp(state, "READY")) { | |
398 | fc_vport_set_state(fc_vport, FC_VPORT_LINKDOWN); | |
399 | return; | |
400 | } | |
401 | fc_vport_set_state(fc_vport, FC_VPORT_ACTIVE); | |
402 | } | |
403 | ||
404 | static int | |
405 | csio_fcoe_alloc_vnp(struct csio_hw *hw, struct csio_lnode *ln) | |
406 | { | |
407 | struct csio_lnode *pln; | |
408 | struct csio_mb *mbp; | |
409 | struct fw_fcoe_vnp_cmd *rsp; | |
410 | int ret = 0; | |
411 | int retry = 0; | |
412 | ||
413 | /* Issue VNP cmd to alloc vport */ | |
414 | /* Allocate Mbox request */ | |
415 | spin_lock_irq(&hw->lock); | |
416 | mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC); | |
417 | if (!mbp) { | |
418 | CSIO_INC_STATS(hw, n_err_nomem); | |
419 | ret = -ENOMEM; | |
420 | goto out; | |
421 | } | |
422 | ||
423 | pln = ln->pln; | |
424 | ln->fcf_flowid = pln->fcf_flowid; | |
425 | ln->portid = pln->portid; | |
426 | ||
427 | csio_fcoe_vnp_alloc_init_mb(ln, mbp, CSIO_MB_DEFAULT_TMO, | |
428 | pln->fcf_flowid, pln->vnp_flowid, 0, | |
429 | csio_ln_wwnn(ln), csio_ln_wwpn(ln), NULL); | |
430 | ||
431 | for (retry = 0; retry < 3; retry++) { | |
432 | /* FW is expected to complete vnp cmd in immediate mode | |
433 | * without much delay. | |
434 | * Otherwise, there will be increase in IO latency since HW | |
435 | * lock is held till completion of vnp mbox cmd. | |
436 | */ | |
437 | ret = csio_mb_issue(hw, mbp); | |
438 | if (ret != -EBUSY) | |
439 | break; | |
440 | ||
441 | /* Retry if mbox returns busy */ | |
442 | spin_unlock_irq(&hw->lock); | |
443 | msleep(2000); | |
444 | spin_lock_irq(&hw->lock); | |
445 | } | |
446 | ||
447 | if (ret) { | |
448 | csio_ln_err(ln, "Failed to issue mbox FCoE VNP command\n"); | |
449 | goto out_free; | |
450 | } | |
451 | ||
452 | /* Process Mbox response of VNP command */ | |
453 | rsp = (struct fw_fcoe_vnp_cmd *)(mbp->mb); | |
e2ac9628 | 454 | if (FW_CMD_RETVAL_G(ntohl(rsp->alloc_to_len16)) != FW_SUCCESS) { |
a3667aae | 455 | csio_ln_err(ln, "FCOE VNP ALLOC cmd returned 0x%x!\n", |
e2ac9628 | 456 | FW_CMD_RETVAL_G(ntohl(rsp->alloc_to_len16))); |
a3667aae NKI |
457 | ret = -EINVAL; |
458 | goto out_free; | |
459 | } | |
460 | ||
461 | ln->vnp_flowid = FW_FCOE_VNP_CMD_VNPI_GET( | |
462 | ntohl(rsp->gen_wwn_to_vnpi)); | |
463 | memcpy(csio_ln_wwnn(ln), rsp->vnport_wwnn, 8); | |
464 | memcpy(csio_ln_wwpn(ln), rsp->vnport_wwpn, 8); | |
465 | ||
466 | csio_ln_dbg(ln, "FCOE VNPI: 0x%x\n", ln->vnp_flowid); | |
467 | csio_ln_dbg(ln, "\tWWNN: %x%x%x%x%x%x%x%x\n", | |
468 | ln->ln_sparm.wwnn[0], ln->ln_sparm.wwnn[1], | |
469 | ln->ln_sparm.wwnn[2], ln->ln_sparm.wwnn[3], | |
470 | ln->ln_sparm.wwnn[4], ln->ln_sparm.wwnn[5], | |
471 | ln->ln_sparm.wwnn[6], ln->ln_sparm.wwnn[7]); | |
472 | csio_ln_dbg(ln, "\tWWPN: %x%x%x%x%x%x%x%x\n", | |
473 | ln->ln_sparm.wwpn[0], ln->ln_sparm.wwpn[1], | |
474 | ln->ln_sparm.wwpn[2], ln->ln_sparm.wwpn[3], | |
475 | ln->ln_sparm.wwpn[4], ln->ln_sparm.wwpn[5], | |
476 | ln->ln_sparm.wwpn[6], ln->ln_sparm.wwpn[7]); | |
477 | ||
478 | out_free: | |
479 | mempool_free(mbp, hw->mb_mempool); | |
480 | out: | |
481 | spin_unlock_irq(&hw->lock); | |
482 | return ret; | |
483 | } | |
484 | ||
485 | static int | |
486 | csio_fcoe_free_vnp(struct csio_hw *hw, struct csio_lnode *ln) | |
487 | { | |
488 | struct csio_lnode *pln; | |
489 | struct csio_mb *mbp; | |
490 | struct fw_fcoe_vnp_cmd *rsp; | |
491 | int ret = 0; | |
492 | int retry = 0; | |
493 | ||
494 | /* Issue VNP cmd to free vport */ | |
495 | /* Allocate Mbox request */ | |
496 | ||
497 | spin_lock_irq(&hw->lock); | |
498 | mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC); | |
499 | if (!mbp) { | |
500 | CSIO_INC_STATS(hw, n_err_nomem); | |
501 | ret = -ENOMEM; | |
502 | goto out; | |
503 | } | |
504 | ||
505 | pln = ln->pln; | |
506 | ||
507 | csio_fcoe_vnp_free_init_mb(ln, mbp, CSIO_MB_DEFAULT_TMO, | |
508 | ln->fcf_flowid, ln->vnp_flowid, | |
509 | NULL); | |
510 | ||
511 | for (retry = 0; retry < 3; retry++) { | |
512 | ret = csio_mb_issue(hw, mbp); | |
513 | if (ret != -EBUSY) | |
514 | break; | |
515 | ||
516 | /* Retry if mbox returns busy */ | |
517 | spin_unlock_irq(&hw->lock); | |
518 | msleep(2000); | |
519 | spin_lock_irq(&hw->lock); | |
520 | } | |
521 | ||
522 | if (ret) { | |
523 | csio_ln_err(ln, "Failed to issue mbox FCoE VNP command\n"); | |
524 | goto out_free; | |
525 | } | |
526 | ||
527 | /* Process Mbox response of VNP command */ | |
528 | rsp = (struct fw_fcoe_vnp_cmd *)(mbp->mb); | |
e2ac9628 | 529 | if (FW_CMD_RETVAL_G(ntohl(rsp->alloc_to_len16)) != FW_SUCCESS) { |
a3667aae | 530 | csio_ln_err(ln, "FCOE VNP FREE cmd returned 0x%x!\n", |
e2ac9628 | 531 | FW_CMD_RETVAL_G(ntohl(rsp->alloc_to_len16))); |
a3667aae NKI |
532 | ret = -EINVAL; |
533 | } | |
534 | ||
535 | out_free: | |
536 | mempool_free(mbp, hw->mb_mempool); | |
537 | out: | |
538 | spin_unlock_irq(&hw->lock); | |
539 | return ret; | |
540 | } | |
541 | ||
542 | static int | |
543 | csio_vport_create(struct fc_vport *fc_vport, bool disable) | |
544 | { | |
545 | struct Scsi_Host *shost = fc_vport->shost; | |
546 | struct csio_lnode *pln = shost_priv(shost); | |
547 | struct csio_lnode *ln = NULL; | |
548 | struct csio_hw *hw = csio_lnode_to_hw(pln); | |
549 | uint8_t wwn[8]; | |
550 | int ret = -1; | |
551 | ||
552 | ln = csio_shost_init(hw, &fc_vport->dev, false, pln); | |
553 | if (!ln) | |
554 | goto error; | |
555 | ||
556 | if (fc_vport->node_name != 0) { | |
557 | u64_to_wwn(fc_vport->node_name, wwn); | |
558 | ||
559 | if (!CSIO_VALID_WWN(wwn)) { | |
560 | csio_ln_err(ln, | |
561 | "vport create failed. Invalid wwnn\n"); | |
562 | goto error; | |
563 | } | |
564 | memcpy(csio_ln_wwnn(ln), wwn, 8); | |
565 | } | |
566 | ||
567 | if (fc_vport->port_name != 0) { | |
568 | u64_to_wwn(fc_vport->port_name, wwn); | |
569 | ||
570 | if (!CSIO_VALID_WWN(wwn)) { | |
571 | csio_ln_err(ln, | |
572 | "vport create failed. Invalid wwpn\n"); | |
573 | goto error; | |
574 | } | |
575 | ||
576 | if (csio_lnode_lookup_by_wwpn(hw, wwn)) { | |
577 | csio_ln_err(ln, | |
578 | "vport create failed. wwpn already exists\n"); | |
579 | goto error; | |
580 | } | |
581 | memcpy(csio_ln_wwpn(ln), wwn, 8); | |
582 | } | |
583 | ||
584 | fc_vport_set_state(fc_vport, FC_VPORT_INITIALIZING); | |
585 | ||
586 | if (csio_fcoe_alloc_vnp(hw, ln)) | |
587 | goto error; | |
588 | ||
589 | *(struct csio_lnode **)fc_vport->dd_data = ln; | |
590 | ln->fc_vport = fc_vport; | |
591 | if (!fc_vport->node_name) | |
592 | fc_vport->node_name = wwn_to_u64(csio_ln_wwnn(ln)); | |
593 | if (!fc_vport->port_name) | |
594 | fc_vport->port_name = wwn_to_u64(csio_ln_wwpn(ln)); | |
595 | csio_fchost_attr_init(ln); | |
596 | return 0; | |
597 | error: | |
598 | if (ln) | |
599 | csio_shost_exit(ln); | |
600 | ||
601 | return ret; | |
602 | } | |
603 | ||
604 | static int | |
605 | csio_vport_delete(struct fc_vport *fc_vport) | |
606 | { | |
607 | struct csio_lnode *ln = *(struct csio_lnode **)fc_vport->dd_data; | |
608 | struct Scsi_Host *shost = csio_ln_to_shost(ln); | |
609 | struct csio_hw *hw = csio_lnode_to_hw(ln); | |
610 | int rmv; | |
611 | ||
612 | spin_lock_irq(&hw->lock); | |
613 | rmv = csio_is_hw_removing(hw); | |
614 | spin_unlock_irq(&hw->lock); | |
615 | ||
616 | if (rmv) { | |
617 | csio_shost_exit(ln); | |
618 | return 0; | |
619 | } | |
620 | ||
621 | /* Quiesce ios and send remove event to lnode */ | |
622 | scsi_block_requests(shost); | |
623 | spin_lock_irq(&hw->lock); | |
624 | csio_scsim_cleanup_io_lnode(csio_hw_to_scsim(hw), ln); | |
625 | csio_lnode_close(ln); | |
626 | spin_unlock_irq(&hw->lock); | |
627 | scsi_unblock_requests(shost); | |
628 | ||
629 | /* Free vnp */ | |
630 | if (fc_vport->vport_state != FC_VPORT_DISABLED) | |
631 | csio_fcoe_free_vnp(hw, ln); | |
632 | ||
633 | csio_shost_exit(ln); | |
634 | return 0; | |
635 | } | |
636 | ||
637 | static int | |
638 | csio_vport_disable(struct fc_vport *fc_vport, bool disable) | |
639 | { | |
640 | struct csio_lnode *ln = *(struct csio_lnode **)fc_vport->dd_data; | |
641 | struct Scsi_Host *shost = csio_ln_to_shost(ln); | |
642 | struct csio_hw *hw = csio_lnode_to_hw(ln); | |
643 | ||
644 | /* disable vport */ | |
645 | if (disable) { | |
646 | /* Quiesce ios and send stop event to lnode */ | |
647 | scsi_block_requests(shost); | |
648 | spin_lock_irq(&hw->lock); | |
649 | csio_scsim_cleanup_io_lnode(csio_hw_to_scsim(hw), ln); | |
650 | csio_lnode_stop(ln); | |
651 | spin_unlock_irq(&hw->lock); | |
652 | scsi_unblock_requests(shost); | |
653 | ||
654 | /* Free vnp */ | |
655 | csio_fcoe_free_vnp(hw, ln); | |
656 | fc_vport_set_state(fc_vport, FC_VPORT_DISABLED); | |
657 | csio_ln_err(ln, "vport disabled\n"); | |
658 | return 0; | |
659 | } else { | |
660 | /* enable vport */ | |
661 | fc_vport_set_state(fc_vport, FC_VPORT_INITIALIZING); | |
662 | if (csio_fcoe_alloc_vnp(hw, ln)) { | |
663 | csio_ln_err(ln, "vport enabled failed.\n"); | |
664 | return -1; | |
665 | } | |
666 | csio_ln_err(ln, "vport enabled\n"); | |
667 | return 0; | |
668 | } | |
669 | } | |
670 | ||
671 | static void | |
672 | csio_dev_loss_tmo_callbk(struct fc_rport *rport) | |
673 | { | |
674 | struct csio_rnode *rn; | |
675 | struct csio_hw *hw; | |
676 | struct csio_lnode *ln; | |
677 | ||
678 | rn = *((struct csio_rnode **)rport->dd_data); | |
679 | ln = csio_rnode_to_lnode(rn); | |
680 | hw = csio_lnode_to_hw(ln); | |
681 | ||
682 | spin_lock_irq(&hw->lock); | |
683 | ||
684 | /* return if driver is being removed or same rnode comes back online */ | |
685 | if (csio_is_hw_removing(hw) || csio_is_rnode_ready(rn)) | |
686 | goto out; | |
687 | ||
688 | csio_ln_dbg(ln, "devloss timeout on rnode:%p portid:x%x flowid:x%x\n", | |
689 | rn, rn->nport_id, csio_rn_flowid(rn)); | |
690 | ||
691 | CSIO_INC_STATS(ln, n_dev_loss_tmo); | |
692 | ||
693 | /* | |
694 | * enqueue devloss event to event worker thread to serialize all | |
695 | * rnode events. | |
696 | */ | |
697 | if (csio_enqueue_evt(hw, CSIO_EVT_DEV_LOSS, &rn, sizeof(rn))) { | |
698 | CSIO_INC_STATS(hw, n_evt_drop); | |
699 | goto out; | |
700 | } | |
701 | ||
702 | if (!(hw->flags & CSIO_HWF_FWEVT_PENDING)) { | |
703 | hw->flags |= CSIO_HWF_FWEVT_PENDING; | |
704 | spin_unlock_irq(&hw->lock); | |
705 | schedule_work(&hw->evtq_work); | |
706 | return; | |
707 | } | |
708 | ||
709 | out: | |
710 | spin_unlock_irq(&hw->lock); | |
711 | } | |
712 | ||
713 | /* FC transport functions template - Physical port */ | |
714 | struct fc_function_template csio_fc_transport_funcs = { | |
715 | .show_host_node_name = 1, | |
716 | .show_host_port_name = 1, | |
717 | .show_host_supported_classes = 1, | |
718 | .show_host_supported_fc4s = 1, | |
719 | .show_host_maxframe_size = 1, | |
720 | ||
721 | .get_host_port_id = csio_get_host_port_id, | |
722 | .show_host_port_id = 1, | |
723 | ||
724 | .get_host_port_type = csio_get_host_port_type, | |
725 | .show_host_port_type = 1, | |
726 | ||
727 | .get_host_port_state = csio_get_host_port_state, | |
728 | .show_host_port_state = 1, | |
729 | ||
730 | .show_host_active_fc4s = 1, | |
731 | .get_host_speed = csio_get_host_speed, | |
732 | .show_host_speed = 1, | |
733 | .get_host_fabric_name = csio_get_host_fabric_name, | |
734 | .show_host_fabric_name = 1, | |
735 | ||
736 | .get_fc_host_stats = csio_get_stats, | |
737 | ||
738 | .dd_fcrport_size = sizeof(struct csio_rnode *), | |
739 | .show_rport_maxframe_size = 1, | |
740 | .show_rport_supported_classes = 1, | |
741 | ||
742 | .set_rport_dev_loss_tmo = csio_set_rport_loss_tmo, | |
743 | .show_rport_dev_loss_tmo = 1, | |
744 | ||
745 | .show_starget_port_id = 1, | |
746 | .show_starget_node_name = 1, | |
747 | .show_starget_port_name = 1, | |
748 | ||
749 | .dev_loss_tmo_callbk = csio_dev_loss_tmo_callbk, | |
750 | .dd_fcvport_size = sizeof(struct csio_lnode *), | |
751 | ||
752 | .vport_create = csio_vport_create, | |
753 | .vport_disable = csio_vport_disable, | |
754 | .vport_delete = csio_vport_delete, | |
755 | }; | |
756 | ||
757 | /* FC transport functions template - Virtual port */ | |
758 | struct fc_function_template csio_fc_transport_vport_funcs = { | |
759 | .show_host_node_name = 1, | |
760 | .show_host_port_name = 1, | |
761 | .show_host_supported_classes = 1, | |
762 | .show_host_supported_fc4s = 1, | |
763 | .show_host_maxframe_size = 1, | |
764 | ||
765 | .get_host_port_id = csio_get_host_port_id, | |
766 | .show_host_port_id = 1, | |
767 | ||
768 | .get_host_port_type = csio_get_host_port_type, | |
769 | .show_host_port_type = 1, | |
770 | ||
771 | .get_host_port_state = csio_get_host_port_state, | |
772 | .show_host_port_state = 1, | |
773 | .show_host_active_fc4s = 1, | |
774 | ||
775 | .get_host_speed = csio_get_host_speed, | |
776 | .show_host_speed = 1, | |
777 | ||
778 | .get_host_fabric_name = csio_get_host_fabric_name, | |
779 | .show_host_fabric_name = 1, | |
780 | ||
781 | .get_fc_host_stats = csio_get_stats, | |
782 | ||
783 | .dd_fcrport_size = sizeof(struct csio_rnode *), | |
784 | .show_rport_maxframe_size = 1, | |
785 | .show_rport_supported_classes = 1, | |
786 | ||
787 | .set_rport_dev_loss_tmo = csio_set_rport_loss_tmo, | |
788 | .show_rport_dev_loss_tmo = 1, | |
789 | ||
790 | .show_starget_port_id = 1, | |
791 | .show_starget_node_name = 1, | |
792 | .show_starget_port_name = 1, | |
793 | ||
794 | .dev_loss_tmo_callbk = csio_dev_loss_tmo_callbk, | |
795 | ||
796 | }; |