1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. |
4 | * |
5 | * Maintained at www.Open-FCoE.org |
6 | */ |
7 | |
8 | #include <linux/types.h> |
9 | #include <linux/module.h> |
10 | #include <linux/kernel.h> |
11 | #include <linux/list.h> |
12 | #include <linux/netdevice.h> |
13 | #include <linux/ethtool.h> |
14 | #include <linux/errno.h> |
15 | #include <linux/crc32.h> |
16 | #include <scsi/libfcoe.h> |
17 | |
18 | #include "libfcoe.h" |
19 | |
20 | MODULE_AUTHOR("Open-FCoE.org" ); |
21 | MODULE_DESCRIPTION("FIP discovery protocol and FCoE transport for FCoE HBAs" ); |
22 | MODULE_LICENSE("GPL v2" ); |
23 | |
24 | static int fcoe_transport_create(const char *, const struct kernel_param *); |
25 | static int fcoe_transport_destroy(const char *, const struct kernel_param *); |
26 | static int fcoe_transport_show(char *buffer, const struct kernel_param *kp); |
27 | static struct fcoe_transport *fcoe_transport_lookup(struct net_device *device); |
28 | static struct fcoe_transport *fcoe_netdev_map_lookup(struct net_device *device); |
29 | static int fcoe_transport_enable(const char *, const struct kernel_param *); |
30 | static int fcoe_transport_disable(const char *, const struct kernel_param *); |
31 | static int libfcoe_device_notification(struct notifier_block *notifier, |
32 | ulong event, void *ptr); |
33 | |
34 | static LIST_HEAD(fcoe_transports); |
35 | static DEFINE_MUTEX(ft_mutex); |
36 | static LIST_HEAD(fcoe_netdevs); |
37 | static DEFINE_MUTEX(fn_mutex); |
38 | |
39 | unsigned int libfcoe_debug_logging; |
40 | module_param_named(debug_logging, libfcoe_debug_logging, int, S_IRUGO|S_IWUSR); |
41 | MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels" ); |
42 | |
43 | module_param_call(show, NULL, fcoe_transport_show, NULL, S_IRUSR); |
44 | __MODULE_PARM_TYPE(show, "string" ); |
45 | MODULE_PARM_DESC(show, " Show attached FCoE transports" ); |
46 | |
47 | module_param_call(create, fcoe_transport_create, NULL, |
48 | (void *)FIP_MODE_FABRIC, S_IWUSR); |
49 | __MODULE_PARM_TYPE(create, "string" ); |
50 | MODULE_PARM_DESC(create, " Creates fcoe instance on an ethernet interface" ); |
51 | |
52 | module_param_call(create_vn2vn, fcoe_transport_create, NULL, |
53 | (void *)FIP_MODE_VN2VN, S_IWUSR); |
54 | __MODULE_PARM_TYPE(create_vn2vn, "string" ); |
55 | MODULE_PARM_DESC(create_vn2vn, " Creates a VN_node to VN_node FCoE instance " |
56 | "on an Ethernet interface" ); |
57 | |
58 | module_param_call(destroy, fcoe_transport_destroy, NULL, NULL, S_IWUSR); |
59 | __MODULE_PARM_TYPE(destroy, "string" ); |
60 | MODULE_PARM_DESC(destroy, " Destroys fcoe instance on an ethernet interface" ); |
61 | |
62 | module_param_call(enable, fcoe_transport_enable, NULL, NULL, S_IWUSR); |
63 | __MODULE_PARM_TYPE(enable, "string" ); |
64 | MODULE_PARM_DESC(enable, " Enables fcoe on an ethernet interface." ); |
65 | |
66 | module_param_call(disable, fcoe_transport_disable, NULL, NULL, S_IWUSR); |
67 | __MODULE_PARM_TYPE(disable, "string" ); |
68 | MODULE_PARM_DESC(disable, " Disables fcoe on an ethernet interface." ); |
69 | |
70 | /* notification function for packets from net device */ |
71 | static struct notifier_block libfcoe_notifier = { |
72 | .notifier_call = libfcoe_device_notification, |
73 | }; |
74 | |
75 | static const struct { |
76 | u32 fc_port_speed; |
77 | #define SPEED_2000 2000 |
78 | #define SPEED_4000 4000 |
79 | #define SPEED_8000 8000 |
80 | #define SPEED_16000 16000 |
81 | #define SPEED_32000 32000 |
82 | u32 eth_port_speed; |
83 | } fcoe_port_speed_mapping[] = { |
84 | { FC_PORTSPEED_1GBIT, SPEED_1000 }, |
85 | { FC_PORTSPEED_2GBIT, SPEED_2000 }, |
86 | { FC_PORTSPEED_4GBIT, SPEED_4000 }, |
87 | { FC_PORTSPEED_8GBIT, SPEED_8000 }, |
88 | { FC_PORTSPEED_10GBIT, SPEED_10000 }, |
89 | { FC_PORTSPEED_16GBIT, SPEED_16000 }, |
90 | { FC_PORTSPEED_20GBIT, SPEED_20000 }, |
91 | { FC_PORTSPEED_25GBIT, SPEED_25000 }, |
92 | { FC_PORTSPEED_32GBIT, SPEED_32000 }, |
93 | { FC_PORTSPEED_40GBIT, SPEED_40000 }, |
94 | { FC_PORTSPEED_50GBIT, SPEED_50000 }, |
95 | { FC_PORTSPEED_100GBIT, SPEED_100000 }, |
96 | }; |
97 | |
98 | static inline u32 eth2fc_speed(u32 eth_port_speed) |
99 | { |
100 | int i; |
101 | |
102 | for (i = 0; i < ARRAY_SIZE(fcoe_port_speed_mapping); i++) { |
103 | if (fcoe_port_speed_mapping[i].eth_port_speed == eth_port_speed) |
104 | return fcoe_port_speed_mapping[i].fc_port_speed; |
105 | } |
106 | |
107 | return FC_PORTSPEED_UNKNOWN; |
108 | } |
109 | |
110 | /** |
111 | * fcoe_link_speed_update() - Update the supported and actual link speeds |
112 | * @lport: The local port to update speeds for |
113 | * |
114 | * Returns: 0 if the ethtool query was successful |
115 | * -1 if the ethtool query failed |
116 | */ |
117 | int fcoe_link_speed_update(struct fc_lport *lport) |
118 | { |
119 | struct net_device *netdev = fcoe_get_netdev(lport); |
120 | struct ethtool_link_ksettings ecmd; |
121 | |
122 | if (!__ethtool_get_link_ksettings(dev: netdev, link_ksettings: &ecmd)) { |
123 | lport->link_supported_speeds &= ~(FC_PORTSPEED_1GBIT | |
124 | FC_PORTSPEED_10GBIT | |
125 | FC_PORTSPEED_20GBIT | |
126 | FC_PORTSPEED_40GBIT); |
127 | |
128 | if (ecmd.link_modes.supported[0] & ( |
129 | SUPPORTED_1000baseT_Half | |
130 | SUPPORTED_1000baseT_Full | |
131 | SUPPORTED_1000baseKX_Full)) |
132 | lport->link_supported_speeds |= FC_PORTSPEED_1GBIT; |
133 | |
134 | if (ecmd.link_modes.supported[0] & ( |
135 | SUPPORTED_10000baseT_Full | |
136 | SUPPORTED_10000baseKX4_Full | |
137 | SUPPORTED_10000baseKR_Full | |
138 | SUPPORTED_10000baseR_FEC)) |
139 | lport->link_supported_speeds |= FC_PORTSPEED_10GBIT; |
140 | |
141 | if (ecmd.link_modes.supported[0] & ( |
142 | SUPPORTED_20000baseMLD2_Full | |
143 | SUPPORTED_20000baseKR2_Full)) |
144 | lport->link_supported_speeds |= FC_PORTSPEED_20GBIT; |
145 | |
146 | if (ecmd.link_modes.supported[0] & ( |
147 | SUPPORTED_40000baseKR4_Full | |
148 | SUPPORTED_40000baseCR4_Full | |
149 | SUPPORTED_40000baseSR4_Full | |
150 | SUPPORTED_40000baseLR4_Full)) |
151 | lport->link_supported_speeds |= FC_PORTSPEED_40GBIT; |
152 | |
153 | lport->link_speed = eth2fc_speed(eth_port_speed: ecmd.base.speed); |
154 | return 0; |
155 | } |
156 | return -1; |
157 | } |
158 | EXPORT_SYMBOL_GPL(fcoe_link_speed_update); |
159 | |
160 | /** |
161 | * __fcoe_get_lesb() - Get the Link Error Status Block (LESB) for a given lport |
162 | * @lport: The local port to update speeds for |
163 | * @fc_lesb: Pointer to the LESB to be filled up |
164 | * @netdev: Pointer to the netdev that is associated with the lport |
165 | * |
166 | * Note, the Link Error Status Block (LESB) for FCoE is defined in FC-BB-6 |
167 | * Clause 7.11 in v1.04. |
168 | */ |
169 | void __fcoe_get_lesb(struct fc_lport *lport, |
170 | struct fc_els_lesb *fc_lesb, |
171 | struct net_device *netdev) |
172 | { |
173 | unsigned int cpu; |
174 | u32 lfc, vlfc, mdac; |
175 | struct fc_stats *stats; |
176 | struct fcoe_fc_els_lesb *lesb; |
177 | struct rtnl_link_stats64 temp; |
178 | |
179 | lfc = 0; |
180 | vlfc = 0; |
181 | mdac = 0; |
182 | lesb = (struct fcoe_fc_els_lesb *)fc_lesb; |
183 | memset(lesb, 0, sizeof(*lesb)); |
184 | for_each_possible_cpu(cpu) { |
185 | stats = per_cpu_ptr(lport->stats, cpu); |
186 | lfc += READ_ONCE(stats->LinkFailureCount); |
187 | vlfc += READ_ONCE(stats->VLinkFailureCount); |
188 | mdac += READ_ONCE(stats->MissDiscAdvCount); |
189 | } |
190 | lesb->lesb_link_fail = htonl(lfc); |
191 | lesb->lesb_vlink_fail = htonl(vlfc); |
192 | lesb->lesb_miss_fka = htonl(mdac); |
193 | lesb->lesb_fcs_error = |
194 | htonl(dev_get_stats(netdev, &temp)->rx_crc_errors); |
195 | } |
196 | EXPORT_SYMBOL_GPL(__fcoe_get_lesb); |
197 | |
198 | /** |
199 | * fcoe_get_lesb() - Fill the FCoE Link Error Status Block |
200 | * @lport: the local port |
201 | * @fc_lesb: the link error status block |
202 | */ |
203 | void fcoe_get_lesb(struct fc_lport *lport, |
204 | struct fc_els_lesb *fc_lesb) |
205 | { |
206 | struct net_device *netdev = fcoe_get_netdev(lport); |
207 | |
208 | __fcoe_get_lesb(lport, fc_lesb, netdev); |
209 | } |
210 | EXPORT_SYMBOL_GPL(fcoe_get_lesb); |
211 | |
212 | /** |
213 | * fcoe_ctlr_get_lesb() - Get the Link Error Status Block (LESB) for a given |
214 | * fcoe controller device |
215 | * @ctlr_dev: The given fcoe controller device |
216 | * |
217 | */ |
218 | void fcoe_ctlr_get_lesb(struct fcoe_ctlr_device *ctlr_dev) |
219 | { |
220 | struct fcoe_ctlr *fip = fcoe_ctlr_device_priv(ctlr: ctlr_dev); |
221 | struct net_device *netdev = fcoe_get_netdev(lport: fip->lp); |
222 | struct fc_els_lesb *fc_lesb; |
223 | |
224 | fc_lesb = (struct fc_els_lesb *)(&ctlr_dev->lesb); |
225 | __fcoe_get_lesb(fip->lp, fc_lesb, netdev); |
226 | } |
227 | EXPORT_SYMBOL_GPL(fcoe_ctlr_get_lesb); |
228 | |
229 | void fcoe_wwn_to_str(u64 wwn, char *buf, int len) |
230 | { |
231 | u8 wwpn[8]; |
232 | |
233 | u64_to_wwn(inm: wwn, wwn: wwpn); |
234 | snprintf(buf, size: len, fmt: "%02x%02x%02x%02x%02x%02x%02x%02x" , |
235 | wwpn[0], wwpn[1], wwpn[2], wwpn[3], |
236 | wwpn[4], wwpn[5], wwpn[6], wwpn[7]); |
237 | } |
238 | EXPORT_SYMBOL_GPL(fcoe_wwn_to_str); |
239 | |
240 | /** |
241 | * fcoe_validate_vport_create() - Validate a vport before creating it |
242 | * @vport: NPIV port to be created |
243 | * |
244 | * This routine is meant to add validation for a vport before creating it |
245 | * via fcoe_vport_create(). |
246 | * Current validations are: |
247 | * - WWPN supplied is unique for given lport |
248 | */ |
249 | int fcoe_validate_vport_create(struct fc_vport *vport) |
250 | { |
251 | struct Scsi_Host *shost = vport_to_shost(vport); |
252 | struct fc_lport *n_port = shost_priv(shost); |
253 | struct fc_lport *vn_port; |
254 | int rc = 0; |
255 | char buf[32]; |
256 | |
257 | mutex_lock(&n_port->lp_mutex); |
258 | |
259 | fcoe_wwn_to_str(vport->port_name, buf, sizeof(buf)); |
260 | /* Check if the wwpn is not same as that of the lport */ |
261 | if (!memcmp(p: &n_port->wwpn, q: &vport->port_name, size: sizeof(u64))) { |
262 | LIBFCOE_TRANSPORT_DBG("vport WWPN 0x%s is same as that of the " |
263 | "base port WWPN\n" , buf); |
264 | rc = -EINVAL; |
265 | goto out; |
266 | } |
267 | |
268 | /* Check if there is any existing vport with same wwpn */ |
269 | list_for_each_entry(vn_port, &n_port->vports, list) { |
270 | if (!memcmp(p: &vn_port->wwpn, q: &vport->port_name, size: sizeof(u64))) { |
271 | LIBFCOE_TRANSPORT_DBG("vport with given WWPN 0x%s " |
272 | "already exists\n" , buf); |
273 | rc = -EINVAL; |
274 | break; |
275 | } |
276 | } |
277 | out: |
278 | mutex_unlock(lock: &n_port->lp_mutex); |
279 | return rc; |
280 | } |
281 | EXPORT_SYMBOL_GPL(fcoe_validate_vport_create); |
282 | |
283 | /** |
284 | * fcoe_get_wwn() - Get the world wide name from LLD if it supports it |
285 | * @netdev: the associated net device |
286 | * @wwn: the output WWN |
287 | * @type: the type of WWN (WWPN or WWNN) |
288 | * |
289 | * Returns: 0 for success |
290 | */ |
291 | int fcoe_get_wwn(struct net_device *netdev, u64 *wwn, int type) |
292 | { |
293 | const struct net_device_ops *ops = netdev->netdev_ops; |
294 | |
295 | if (ops->ndo_fcoe_get_wwn) |
296 | return ops->ndo_fcoe_get_wwn(netdev, wwn, type); |
297 | return -EINVAL; |
298 | } |
299 | EXPORT_SYMBOL_GPL(fcoe_get_wwn); |
300 | |
301 | /** |
302 | * fcoe_fc_crc() - Calculates the CRC for a given frame |
303 | * @fp: The frame to be checksumed |
304 | * |
305 | * This uses crc32() routine to calculate the CRC for a frame |
306 | * |
307 | * Return: The 32 bit CRC value |
308 | */ |
309 | u32 fcoe_fc_crc(struct fc_frame *fp) |
310 | { |
311 | struct sk_buff *skb = fp_skb(fp); |
312 | skb_frag_t *frag; |
313 | unsigned char *data; |
314 | unsigned long off, len, clen; |
315 | u32 crc; |
316 | unsigned i; |
317 | |
318 | crc = crc32(~0, skb->data, skb_headlen(skb)); |
319 | |
320 | for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { |
321 | frag = &skb_shinfo(skb)->frags[i]; |
322 | off = skb_frag_off(frag); |
323 | len = skb_frag_size(frag); |
324 | while (len > 0) { |
325 | clen = min(len, PAGE_SIZE - (off & ~PAGE_MASK)); |
326 | data = kmap_atomic( |
327 | page: skb_frag_page(frag) + (off >> PAGE_SHIFT)); |
328 | crc = crc32(crc, data + (off & ~PAGE_MASK), clen); |
329 | kunmap_atomic(data); |
330 | off += clen; |
331 | len -= clen; |
332 | } |
333 | } |
334 | return crc; |
335 | } |
336 | EXPORT_SYMBOL_GPL(fcoe_fc_crc); |
337 | |
338 | /** |
339 | * fcoe_start_io() - Start FCoE I/O |
340 | * @skb: The packet to be transmitted |
341 | * |
342 | * This routine is called from the net device to start transmitting |
343 | * FCoE packets. |
344 | * |
345 | * Returns: 0 for success |
346 | */ |
347 | int fcoe_start_io(struct sk_buff *skb) |
348 | { |
349 | struct sk_buff *nskb; |
350 | int rc; |
351 | |
352 | nskb = skb_clone(skb, GFP_ATOMIC); |
353 | if (!nskb) |
354 | return -ENOMEM; |
355 | rc = dev_queue_xmit(skb: nskb); |
356 | if (rc != 0) |
357 | return rc; |
358 | kfree_skb(skb); |
359 | return 0; |
360 | } |
361 | EXPORT_SYMBOL_GPL(fcoe_start_io); |
362 | |
363 | |
364 | /** |
365 | * fcoe_clean_pending_queue() - Dequeue a skb and free it |
366 | * @lport: The local port to dequeue a skb on |
367 | */ |
368 | void fcoe_clean_pending_queue(struct fc_lport *lport) |
369 | { |
370 | struct fcoe_port *port = lport_priv(lport); |
371 | struct sk_buff *skb; |
372 | |
373 | spin_lock_bh(lock: &port->fcoe_pending_queue.lock); |
374 | while ((skb = __skb_dequeue(list: &port->fcoe_pending_queue)) != NULL) { |
375 | spin_unlock_bh(lock: &port->fcoe_pending_queue.lock); |
376 | kfree_skb(skb); |
377 | spin_lock_bh(lock: &port->fcoe_pending_queue.lock); |
378 | } |
379 | spin_unlock_bh(lock: &port->fcoe_pending_queue.lock); |
380 | } |
381 | EXPORT_SYMBOL_GPL(fcoe_clean_pending_queue); |
382 | |
383 | /** |
384 | * fcoe_check_wait_queue() - Attempt to clear the transmit backlog |
385 | * @lport: The local port whose backlog is to be cleared |
386 | * @skb: The received FIP packet |
387 | * |
388 | * This empties the wait_queue, dequeues the head of the wait_queue queue |
389 | * and calls fcoe_start_io() for each packet. If all skb have been |
390 | * transmitted it returns the qlen. If an error occurs it restores |
391 | * wait_queue (to try again later) and returns -1. |
392 | * |
393 | * The wait_queue is used when the skb transmit fails. The failed skb |
394 | * will go in the wait_queue which will be emptied by the timer function or |
395 | * by the next skb transmit. |
396 | */ |
397 | void fcoe_check_wait_queue(struct fc_lport *lport, struct sk_buff *skb) |
398 | { |
399 | struct fcoe_port *port = lport_priv(lport); |
400 | int rc; |
401 | |
402 | spin_lock_bh(lock: &port->fcoe_pending_queue.lock); |
403 | |
404 | if (skb) |
405 | __skb_queue_tail(list: &port->fcoe_pending_queue, newsk: skb); |
406 | |
407 | if (port->fcoe_pending_queue_active) |
408 | goto out; |
409 | port->fcoe_pending_queue_active = 1; |
410 | |
411 | while (port->fcoe_pending_queue.qlen) { |
412 | /* keep qlen > 0 until fcoe_start_io succeeds */ |
413 | port->fcoe_pending_queue.qlen++; |
414 | skb = __skb_dequeue(list: &port->fcoe_pending_queue); |
415 | |
416 | spin_unlock_bh(lock: &port->fcoe_pending_queue.lock); |
417 | rc = fcoe_start_io(skb); |
418 | spin_lock_bh(lock: &port->fcoe_pending_queue.lock); |
419 | |
420 | if (rc) { |
421 | __skb_queue_head(list: &port->fcoe_pending_queue, newsk: skb); |
422 | /* undo temporary increment above */ |
423 | port->fcoe_pending_queue.qlen--; |
424 | break; |
425 | } |
426 | /* undo temporary increment above */ |
427 | port->fcoe_pending_queue.qlen--; |
428 | } |
429 | |
430 | if (port->fcoe_pending_queue.qlen < port->min_queue_depth) |
431 | lport->qfull = 0; |
432 | if (port->fcoe_pending_queue.qlen && !timer_pending(timer: &port->timer)) |
433 | mod_timer(timer: &port->timer, expires: jiffies + 2); |
434 | port->fcoe_pending_queue_active = 0; |
435 | out: |
436 | if (port->fcoe_pending_queue.qlen > port->max_queue_depth) |
437 | lport->qfull = 1; |
438 | spin_unlock_bh(lock: &port->fcoe_pending_queue.lock); |
439 | } |
440 | EXPORT_SYMBOL_GPL(fcoe_check_wait_queue); |
441 | |
442 | /** |
443 | * fcoe_queue_timer() - The fcoe queue timer |
444 | * @t: Timer context use to obtain the FCoE port |
445 | * |
446 | * Calls fcoe_check_wait_queue on timeout |
447 | */ |
448 | void fcoe_queue_timer(struct timer_list *t) |
449 | { |
450 | struct fcoe_port *port = from_timer(port, t, timer); |
451 | |
452 | fcoe_check_wait_queue(port->lport, NULL); |
453 | } |
454 | EXPORT_SYMBOL_GPL(fcoe_queue_timer); |
455 | |
456 | /** |
457 | * fcoe_get_paged_crc_eof() - Allocate a page to be used for the trailer CRC |
458 | * @skb: The packet to be transmitted |
459 | * @tlen: The total length of the trailer |
460 | * @fps: The fcoe context |
461 | * |
462 | * This routine allocates a page for frame trailers. The page is re-used if |
463 | * there is enough room left on it for the current trailer. If there isn't |
464 | * enough buffer left a new page is allocated for the trailer. Reference to |
465 | * the page from this function as well as the skbs using the page fragments |
466 | * ensure that the page is freed at the appropriate time. |
467 | * |
468 | * Returns: 0 for success |
469 | */ |
470 | int fcoe_get_paged_crc_eof(struct sk_buff *skb, int tlen, |
471 | struct fcoe_percpu_s *fps) |
472 | { |
473 | struct page *page; |
474 | |
475 | page = fps->crc_eof_page; |
476 | if (!page) { |
477 | page = alloc_page(GFP_ATOMIC); |
478 | if (!page) |
479 | return -ENOMEM; |
480 | |
481 | fps->crc_eof_page = page; |
482 | fps->crc_eof_offset = 0; |
483 | } |
484 | |
485 | get_page(page); |
486 | skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags, page, |
487 | off: fps->crc_eof_offset, size: tlen); |
488 | skb->len += tlen; |
489 | skb->data_len += tlen; |
490 | skb->truesize += tlen; |
491 | fps->crc_eof_offset += sizeof(struct fcoe_crc_eof); |
492 | |
493 | if (fps->crc_eof_offset >= PAGE_SIZE) { |
494 | fps->crc_eof_page = NULL; |
495 | fps->crc_eof_offset = 0; |
496 | put_page(page); |
497 | } |
498 | |
499 | return 0; |
500 | } |
501 | EXPORT_SYMBOL_GPL(fcoe_get_paged_crc_eof); |
502 | |
503 | /** |
504 | * fcoe_transport_lookup - find an fcoe transport that matches a netdev |
505 | * @netdev: The netdev to look for from all attached transports |
506 | * |
507 | * Returns : ptr to the fcoe transport that supports this netdev or NULL |
508 | * if not found. |
509 | * |
510 | * The ft_mutex should be held when this is called |
511 | */ |
512 | static struct fcoe_transport *fcoe_transport_lookup(struct net_device *netdev) |
513 | { |
514 | struct fcoe_transport *ft = NULL; |
515 | |
516 | list_for_each_entry(ft, &fcoe_transports, list) |
517 | if (ft->match && ft->match(netdev)) |
518 | return ft; |
519 | return NULL; |
520 | } |
521 | |
522 | /** |
523 | * fcoe_transport_attach - Attaches an FCoE transport |
524 | * @ft: The fcoe transport to be attached |
525 | * |
526 | * Returns : 0 for success |
527 | */ |
528 | int fcoe_transport_attach(struct fcoe_transport *ft) |
529 | { |
530 | int rc = 0; |
531 | |
532 | mutex_lock(&ft_mutex); |
533 | if (ft->attached) { |
534 | LIBFCOE_TRANSPORT_DBG("transport %s already attached\n" , |
535 | ft->name); |
536 | rc = -EEXIST; |
537 | goto out_attach; |
538 | } |
539 | |
540 | /* Add default transport to the tail */ |
541 | if (strcmp(ft->name, FCOE_TRANSPORT_DEFAULT)) |
542 | list_add(new: &ft->list, head: &fcoe_transports); |
543 | else |
544 | list_add_tail(new: &ft->list, head: &fcoe_transports); |
545 | |
546 | ft->attached = true; |
547 | LIBFCOE_TRANSPORT_DBG("attaching transport %s\n" , ft->name); |
548 | |
549 | out_attach: |
550 | mutex_unlock(lock: &ft_mutex); |
551 | return rc; |
552 | } |
553 | EXPORT_SYMBOL(fcoe_transport_attach); |
554 | |
555 | /** |
556 | * fcoe_transport_detach - Detaches an FCoE transport |
557 | * @ft: The fcoe transport to be attached |
558 | * |
559 | * Returns : 0 for success |
560 | */ |
561 | int fcoe_transport_detach(struct fcoe_transport *ft) |
562 | { |
563 | int rc = 0; |
564 | struct fcoe_netdev_mapping *nm = NULL, *tmp; |
565 | |
566 | mutex_lock(&ft_mutex); |
567 | if (!ft->attached) { |
568 | LIBFCOE_TRANSPORT_DBG("transport %s already detached\n" , |
569 | ft->name); |
570 | rc = -ENODEV; |
571 | goto out_attach; |
572 | } |
573 | |
574 | /* remove netdev mapping for this transport as it is going away */ |
575 | mutex_lock(&fn_mutex); |
576 | list_for_each_entry_safe(nm, tmp, &fcoe_netdevs, list) { |
577 | if (nm->ft == ft) { |
578 | LIBFCOE_TRANSPORT_DBG("transport %s going away, " |
579 | "remove its netdev mapping for %s\n" , |
580 | ft->name, nm->netdev->name); |
581 | list_del(entry: &nm->list); |
582 | kfree(objp: nm); |
583 | } |
584 | } |
585 | mutex_unlock(lock: &fn_mutex); |
586 | |
587 | list_del(entry: &ft->list); |
588 | ft->attached = false; |
589 | LIBFCOE_TRANSPORT_DBG("detaching transport %s\n" , ft->name); |
590 | |
591 | out_attach: |
592 | mutex_unlock(lock: &ft_mutex); |
593 | return rc; |
594 | |
595 | } |
596 | EXPORT_SYMBOL(fcoe_transport_detach); |
597 | |
598 | static int fcoe_transport_show(char *buffer, const struct kernel_param *kp) |
599 | { |
600 | int i, j; |
601 | struct fcoe_transport *ft = NULL; |
602 | |
603 | i = j = sprintf(buf: buffer, fmt: "Attached FCoE transports:" ); |
604 | mutex_lock(&ft_mutex); |
605 | list_for_each_entry(ft, &fcoe_transports, list) { |
606 | if (i >= PAGE_SIZE - IFNAMSIZ) |
607 | break; |
608 | i += snprintf(buf: &buffer[i], IFNAMSIZ, fmt: "%s " , ft->name); |
609 | } |
610 | mutex_unlock(lock: &ft_mutex); |
611 | if (i == j) |
612 | i += snprintf(buf: &buffer[i], IFNAMSIZ, fmt: "none" ); |
613 | return i; |
614 | } |
615 | |
616 | static int __init fcoe_transport_init(void) |
617 | { |
618 | register_netdevice_notifier(nb: &libfcoe_notifier); |
619 | return 0; |
620 | } |
621 | |
622 | static int fcoe_transport_exit(void) |
623 | { |
624 | struct fcoe_transport *ft; |
625 | |
626 | unregister_netdevice_notifier(nb: &libfcoe_notifier); |
627 | mutex_lock(&ft_mutex); |
628 | list_for_each_entry(ft, &fcoe_transports, list) |
629 | printk(KERN_ERR "FCoE transport %s is still attached!\n" , |
630 | ft->name); |
631 | mutex_unlock(lock: &ft_mutex); |
632 | return 0; |
633 | } |
634 | |
635 | |
636 | static int fcoe_add_netdev_mapping(struct net_device *netdev, |
637 | struct fcoe_transport *ft) |
638 | { |
639 | struct fcoe_netdev_mapping *nm; |
640 | |
641 | nm = kmalloc(size: sizeof(*nm), GFP_KERNEL); |
642 | if (!nm) { |
643 | printk(KERN_ERR "Unable to allocate netdev_mapping" ); |
644 | return -ENOMEM; |
645 | } |
646 | |
647 | nm->netdev = netdev; |
648 | nm->ft = ft; |
649 | |
650 | mutex_lock(&fn_mutex); |
651 | list_add(new: &nm->list, head: &fcoe_netdevs); |
652 | mutex_unlock(lock: &fn_mutex); |
653 | return 0; |
654 | } |
655 | |
656 | |
657 | static void fcoe_del_netdev_mapping(struct net_device *netdev) |
658 | { |
659 | struct fcoe_netdev_mapping *nm = NULL, *tmp; |
660 | |
661 | mutex_lock(&fn_mutex); |
662 | list_for_each_entry_safe(nm, tmp, &fcoe_netdevs, list) { |
663 | if (nm->netdev == netdev) { |
664 | list_del(entry: &nm->list); |
665 | kfree(objp: nm); |
666 | mutex_unlock(lock: &fn_mutex); |
667 | return; |
668 | } |
669 | } |
670 | mutex_unlock(lock: &fn_mutex); |
671 | } |
672 | |
673 | |
674 | /** |
675 | * fcoe_netdev_map_lookup - find the fcoe transport that matches the netdev on which |
676 | * it was created |
677 | * @netdev: The net device that the FCoE interface is on |
678 | * |
679 | * Returns : ptr to the fcoe transport that supports this netdev or NULL |
680 | * if not found. |
681 | * |
682 | * The ft_mutex should be held when this is called |
683 | */ |
684 | static struct fcoe_transport *fcoe_netdev_map_lookup(struct net_device *netdev) |
685 | { |
686 | struct fcoe_transport *ft = NULL; |
687 | struct fcoe_netdev_mapping *nm; |
688 | |
689 | mutex_lock(&fn_mutex); |
690 | list_for_each_entry(nm, &fcoe_netdevs, list) { |
691 | if (netdev == nm->netdev) { |
692 | ft = nm->ft; |
693 | mutex_unlock(lock: &fn_mutex); |
694 | return ft; |
695 | } |
696 | } |
697 | |
698 | mutex_unlock(lock: &fn_mutex); |
699 | return NULL; |
700 | } |
701 | |
702 | /** |
703 | * fcoe_if_to_netdev() - Parse a name buffer to get a net device |
704 | * @buffer: The name of the net device |
705 | * |
706 | * Returns: NULL or a ptr to net_device |
707 | */ |
708 | static struct net_device *fcoe_if_to_netdev(const char *buffer) |
709 | { |
710 | char *cp; |
711 | char ifname[IFNAMSIZ + 2]; |
712 | |
713 | if (buffer) { |
714 | strscpy(p: ifname, q: buffer, IFNAMSIZ); |
715 | cp = ifname + strlen(ifname); |
716 | while (--cp >= ifname && *cp == '\n') |
717 | *cp = '\0'; |
718 | return dev_get_by_name(net: &init_net, name: ifname); |
719 | } |
720 | return NULL; |
721 | } |
722 | |
723 | /** |
724 | * libfcoe_device_notification() - Handler for net device events |
725 | * @notifier: The context of the notification |
726 | * @event: The type of event |
727 | * @ptr: The net device that the event was on |
728 | * |
729 | * This function is called by the Ethernet driver in case of link change event. |
730 | * |
731 | * Returns: 0 for success |
732 | */ |
733 | static int libfcoe_device_notification(struct notifier_block *notifier, |
734 | ulong event, void *ptr) |
735 | { |
736 | struct net_device *netdev = netdev_notifier_info_to_dev(info: ptr); |
737 | |
738 | switch (event) { |
739 | case NETDEV_UNREGISTER: |
740 | LIBFCOE_TRANSPORT_DBG("NETDEV_UNREGISTER %s\n" , |
741 | netdev->name); |
742 | fcoe_del_netdev_mapping(netdev); |
743 | break; |
744 | } |
745 | return NOTIFY_OK; |
746 | } |
747 | |
748 | ssize_t fcoe_ctlr_create_store(const char *buf, size_t count) |
749 | { |
750 | struct net_device *netdev = NULL; |
751 | struct fcoe_transport *ft = NULL; |
752 | int rc = 0; |
753 | int err; |
754 | |
755 | mutex_lock(&ft_mutex); |
756 | |
757 | netdev = fcoe_if_to_netdev(buffer: buf); |
758 | if (!netdev) { |
759 | LIBFCOE_TRANSPORT_DBG("Invalid device %s.\n" , buf); |
760 | rc = -ENODEV; |
761 | goto out_nodev; |
762 | } |
763 | |
764 | ft = fcoe_netdev_map_lookup(netdev); |
765 | if (ft) { |
766 | LIBFCOE_TRANSPORT_DBG("transport %s already has existing " |
767 | "FCoE instance on %s.\n" , |
768 | ft->name, netdev->name); |
769 | rc = -EEXIST; |
770 | goto out_putdev; |
771 | } |
772 | |
773 | ft = fcoe_transport_lookup(netdev); |
774 | if (!ft) { |
775 | LIBFCOE_TRANSPORT_DBG("no FCoE transport found for %s.\n" , |
776 | netdev->name); |
777 | rc = -ENODEV; |
778 | goto out_putdev; |
779 | } |
780 | |
781 | /* pass to transport create */ |
782 | err = ft->alloc ? ft->alloc(netdev) : -ENODEV; |
783 | if (err) { |
784 | fcoe_del_netdev_mapping(netdev); |
785 | rc = -ENOMEM; |
786 | goto out_putdev; |
787 | } |
788 | |
789 | err = fcoe_add_netdev_mapping(netdev, ft); |
790 | if (err) { |
791 | LIBFCOE_TRANSPORT_DBG("failed to add new netdev mapping " |
792 | "for FCoE transport %s for %s.\n" , |
793 | ft->name, netdev->name); |
794 | rc = -ENODEV; |
795 | goto out_putdev; |
796 | } |
797 | |
798 | LIBFCOE_TRANSPORT_DBG("transport %s succeeded to create fcoe on %s.\n" , |
799 | ft->name, netdev->name); |
800 | |
801 | out_putdev: |
802 | dev_put(dev: netdev); |
803 | out_nodev: |
804 | mutex_unlock(lock: &ft_mutex); |
805 | if (rc) |
806 | return rc; |
807 | return count; |
808 | } |
809 | |
810 | ssize_t fcoe_ctlr_destroy_store(const char *buf, size_t count) |
811 | { |
812 | int rc = -ENODEV; |
813 | struct net_device *netdev = NULL; |
814 | struct fcoe_transport *ft = NULL; |
815 | |
816 | mutex_lock(&ft_mutex); |
817 | |
818 | netdev = fcoe_if_to_netdev(buffer: buf); |
819 | if (!netdev) { |
820 | LIBFCOE_TRANSPORT_DBG("invalid device %s.\n" , buf); |
821 | goto out_nodev; |
822 | } |
823 | |
824 | ft = fcoe_netdev_map_lookup(netdev); |
825 | if (!ft) { |
826 | LIBFCOE_TRANSPORT_DBG("no FCoE transport found for %s.\n" , |
827 | netdev->name); |
828 | goto out_putdev; |
829 | } |
830 | |
831 | /* pass to transport destroy */ |
832 | rc = ft->destroy(netdev); |
833 | if (rc) |
834 | goto out_putdev; |
835 | |
836 | fcoe_del_netdev_mapping(netdev); |
837 | LIBFCOE_TRANSPORT_DBG("transport %s %s to destroy fcoe on %s.\n" , |
838 | ft->name, (rc) ? "failed" : "succeeded" , |
839 | netdev->name); |
840 | rc = count; /* required for successful return */ |
841 | out_putdev: |
842 | dev_put(dev: netdev); |
843 | out_nodev: |
844 | mutex_unlock(lock: &ft_mutex); |
845 | return rc; |
846 | } |
847 | |
848 | /** |
849 | * fcoe_transport_create() - Create a fcoe interface |
850 | * @buffer: The name of the Ethernet interface to create on |
851 | * @kp: The associated kernel param |
852 | * |
853 | * Called from sysfs. This holds the ft_mutex while calling the |
854 | * registered fcoe transport's create function. |
855 | * |
856 | * Returns: 0 for success |
857 | */ |
858 | static int fcoe_transport_create(const char *buffer, |
859 | const struct kernel_param *kp) |
860 | { |
861 | int rc = -ENODEV; |
862 | struct net_device *netdev = NULL; |
863 | struct fcoe_transport *ft = NULL; |
864 | enum fip_mode fip_mode = (enum fip_mode)(uintptr_t)kp->arg; |
865 | |
866 | mutex_lock(&ft_mutex); |
867 | |
868 | netdev = fcoe_if_to_netdev(buffer); |
869 | if (!netdev) { |
870 | LIBFCOE_TRANSPORT_DBG("Invalid device %s.\n" , buffer); |
871 | goto out_nodev; |
872 | } |
873 | |
874 | ft = fcoe_netdev_map_lookup(netdev); |
875 | if (ft) { |
876 | LIBFCOE_TRANSPORT_DBG("transport %s already has existing " |
877 | "FCoE instance on %s.\n" , |
878 | ft->name, netdev->name); |
879 | rc = -EEXIST; |
880 | goto out_putdev; |
881 | } |
882 | |
883 | ft = fcoe_transport_lookup(netdev); |
884 | if (!ft) { |
885 | LIBFCOE_TRANSPORT_DBG("no FCoE transport found for %s.\n" , |
886 | netdev->name); |
887 | goto out_putdev; |
888 | } |
889 | |
890 | rc = fcoe_add_netdev_mapping(netdev, ft); |
891 | if (rc) { |
892 | LIBFCOE_TRANSPORT_DBG("failed to add new netdev mapping " |
893 | "for FCoE transport %s for %s.\n" , |
894 | ft->name, netdev->name); |
895 | goto out_putdev; |
896 | } |
897 | |
898 | /* pass to transport create */ |
899 | rc = ft->create ? ft->create(netdev, fip_mode) : -ENODEV; |
900 | if (rc) |
901 | fcoe_del_netdev_mapping(netdev); |
902 | |
903 | LIBFCOE_TRANSPORT_DBG("transport %s %s to create fcoe on %s.\n" , |
904 | ft->name, (rc) ? "failed" : "succeeded" , |
905 | netdev->name); |
906 | |
907 | out_putdev: |
908 | dev_put(dev: netdev); |
909 | out_nodev: |
910 | mutex_unlock(lock: &ft_mutex); |
911 | return rc; |
912 | } |
913 | |
914 | /** |
915 | * fcoe_transport_destroy() - Destroy a FCoE interface |
916 | * @buffer: The name of the Ethernet interface to be destroyed |
917 | * @kp: The associated kernel parameter |
918 | * |
919 | * Called from sysfs. This holds the ft_mutex while calling the |
920 | * registered fcoe transport's destroy function. |
921 | * |
922 | * Returns: 0 for success |
923 | */ |
924 | static int fcoe_transport_destroy(const char *buffer, |
925 | const struct kernel_param *kp) |
926 | { |
927 | int rc = -ENODEV; |
928 | struct net_device *netdev = NULL; |
929 | struct fcoe_transport *ft = NULL; |
930 | |
931 | mutex_lock(&ft_mutex); |
932 | |
933 | netdev = fcoe_if_to_netdev(buffer); |
934 | if (!netdev) { |
935 | LIBFCOE_TRANSPORT_DBG("invalid device %s.\n" , buffer); |
936 | goto out_nodev; |
937 | } |
938 | |
939 | ft = fcoe_netdev_map_lookup(netdev); |
940 | if (!ft) { |
941 | LIBFCOE_TRANSPORT_DBG("no FCoE transport found for %s.\n" , |
942 | netdev->name); |
943 | goto out_putdev; |
944 | } |
945 | |
946 | /* pass to transport destroy */ |
947 | rc = ft->destroy ? ft->destroy(netdev) : -ENODEV; |
948 | fcoe_del_netdev_mapping(netdev); |
949 | LIBFCOE_TRANSPORT_DBG("transport %s %s to destroy fcoe on %s.\n" , |
950 | ft->name, (rc) ? "failed" : "succeeded" , |
951 | netdev->name); |
952 | |
953 | out_putdev: |
954 | dev_put(dev: netdev); |
955 | out_nodev: |
956 | mutex_unlock(lock: &ft_mutex); |
957 | return rc; |
958 | } |
959 | |
960 | /** |
961 | * fcoe_transport_disable() - Disables a FCoE interface |
962 | * @buffer: The name of the Ethernet interface to be disabled |
963 | * @kp: The associated kernel parameter |
964 | * |
965 | * Called from sysfs. |
966 | * |
967 | * Returns: 0 for success |
968 | */ |
969 | static int fcoe_transport_disable(const char *buffer, |
970 | const struct kernel_param *kp) |
971 | { |
972 | int rc = -ENODEV; |
973 | struct net_device *netdev = NULL; |
974 | struct fcoe_transport *ft = NULL; |
975 | |
976 | mutex_lock(&ft_mutex); |
977 | |
978 | netdev = fcoe_if_to_netdev(buffer); |
979 | if (!netdev) |
980 | goto out_nodev; |
981 | |
982 | ft = fcoe_netdev_map_lookup(netdev); |
983 | if (!ft) |
984 | goto out_putdev; |
985 | |
986 | rc = ft->disable ? ft->disable(netdev) : -ENODEV; |
987 | |
988 | out_putdev: |
989 | dev_put(dev: netdev); |
990 | out_nodev: |
991 | mutex_unlock(lock: &ft_mutex); |
992 | return rc; |
993 | } |
994 | |
995 | /** |
996 | * fcoe_transport_enable() - Enables a FCoE interface |
997 | * @buffer: The name of the Ethernet interface to be enabled |
998 | * @kp: The associated kernel parameter |
999 | * |
1000 | * Called from sysfs. |
1001 | * |
1002 | * Returns: 0 for success |
1003 | */ |
1004 | static int fcoe_transport_enable(const char *buffer, |
1005 | const struct kernel_param *kp) |
1006 | { |
1007 | int rc = -ENODEV; |
1008 | struct net_device *netdev = NULL; |
1009 | struct fcoe_transport *ft = NULL; |
1010 | |
1011 | mutex_lock(&ft_mutex); |
1012 | |
1013 | netdev = fcoe_if_to_netdev(buffer); |
1014 | if (!netdev) |
1015 | goto out_nodev; |
1016 | |
1017 | ft = fcoe_netdev_map_lookup(netdev); |
1018 | if (!ft) |
1019 | goto out_putdev; |
1020 | |
1021 | rc = ft->enable ? ft->enable(netdev) : -ENODEV; |
1022 | |
1023 | out_putdev: |
1024 | dev_put(dev: netdev); |
1025 | out_nodev: |
1026 | mutex_unlock(lock: &ft_mutex); |
1027 | return rc; |
1028 | } |
1029 | |
1030 | /** |
1031 | * libfcoe_init() - Initialization routine for libfcoe.ko |
1032 | */ |
1033 | static int __init libfcoe_init(void) |
1034 | { |
1035 | int rc = 0; |
1036 | |
1037 | rc = fcoe_transport_init(); |
1038 | if (rc) |
1039 | return rc; |
1040 | |
1041 | rc = fcoe_sysfs_setup(); |
1042 | if (rc) |
1043 | fcoe_transport_exit(); |
1044 | |
1045 | return rc; |
1046 | } |
1047 | module_init(libfcoe_init); |
1048 | |
1049 | /** |
1050 | * libfcoe_exit() - Tear down libfcoe.ko |
1051 | */ |
1052 | static void __exit libfcoe_exit(void) |
1053 | { |
1054 | fcoe_sysfs_teardown(); |
1055 | fcoe_transport_exit(); |
1056 | } |
1057 | module_exit(libfcoe_exit); |
1058 | |