diff options
Diffstat (limited to 'drivers/scsi/fcoe/fcoe_transport.c')
-rw-r--r-- | drivers/scsi/fcoe/fcoe_transport.c | 770 |
1 files changed, 770 insertions, 0 deletions
diff --git a/drivers/scsi/fcoe/fcoe_transport.c b/drivers/scsi/fcoe/fcoe_transport.c new file mode 100644 index 000000000000..258684101bfd --- /dev/null +++ b/drivers/scsi/fcoe/fcoe_transport.c | |||
@@ -0,0 +1,770 @@ | |||
1 | /* | ||
2 | * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms and conditions of the GNU General Public License, | ||
6 | * version 2, as published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
11 | * more details. | ||
12 | * | ||
13 | * You should have received a copy of the GNU General Public License along with | ||
14 | * this program; if not, write to the Free Software Foundation, Inc., | ||
15 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | ||
16 | * | ||
17 | * Maintained at www.Open-FCoE.org | ||
18 | */ | ||
19 | |||
20 | #include <linux/types.h> | ||
21 | #include <linux/module.h> | ||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/list.h> | ||
24 | #include <linux/netdevice.h> | ||
25 | #include <linux/errno.h> | ||
26 | #include <linux/crc32.h> | ||
27 | #include <scsi/libfcoe.h> | ||
28 | |||
29 | #include "libfcoe.h" | ||
30 | |||
31 | MODULE_AUTHOR("Open-FCoE.org"); | ||
32 | MODULE_DESCRIPTION("FIP discovery protocol and FCoE transport for FCoE HBAs"); | ||
33 | MODULE_LICENSE("GPL v2"); | ||
34 | |||
35 | static int fcoe_transport_create(const char *, struct kernel_param *); | ||
36 | static int fcoe_transport_destroy(const char *, struct kernel_param *); | ||
37 | static int fcoe_transport_show(char *buffer, const struct kernel_param *kp); | ||
38 | static struct fcoe_transport *fcoe_transport_lookup(struct net_device *device); | ||
39 | static struct fcoe_transport *fcoe_netdev_map_lookup(struct net_device *device); | ||
40 | static int fcoe_transport_enable(const char *, struct kernel_param *); | ||
41 | static int fcoe_transport_disable(const char *, struct kernel_param *); | ||
42 | static int libfcoe_device_notification(struct notifier_block *notifier, | ||
43 | ulong event, void *ptr); | ||
44 | |||
45 | static LIST_HEAD(fcoe_transports); | ||
46 | static DEFINE_MUTEX(ft_mutex); | ||
47 | static LIST_HEAD(fcoe_netdevs); | ||
48 | static DEFINE_MUTEX(fn_mutex); | ||
49 | |||
50 | unsigned int libfcoe_debug_logging; | ||
51 | module_param_named(debug_logging, libfcoe_debug_logging, int, S_IRUGO|S_IWUSR); | ||
52 | MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels"); | ||
53 | |||
54 | module_param_call(show, NULL, fcoe_transport_show, NULL, S_IRUSR); | ||
55 | __MODULE_PARM_TYPE(show, "string"); | ||
56 | MODULE_PARM_DESC(show, " Show attached FCoE transports"); | ||
57 | |||
58 | module_param_call(create, fcoe_transport_create, NULL, | ||
59 | (void *)FIP_MODE_FABRIC, S_IWUSR); | ||
60 | __MODULE_PARM_TYPE(create, "string"); | ||
61 | MODULE_PARM_DESC(create, " Creates fcoe instance on a ethernet interface"); | ||
62 | |||
63 | module_param_call(create_vn2vn, fcoe_transport_create, NULL, | ||
64 | (void *)FIP_MODE_VN2VN, S_IWUSR); | ||
65 | __MODULE_PARM_TYPE(create_vn2vn, "string"); | ||
66 | MODULE_PARM_DESC(create_vn2vn, " Creates a VN_node to VN_node FCoE instance " | ||
67 | "on an Ethernet interface"); | ||
68 | |||
69 | module_param_call(destroy, fcoe_transport_destroy, NULL, NULL, S_IWUSR); | ||
70 | __MODULE_PARM_TYPE(destroy, "string"); | ||
71 | MODULE_PARM_DESC(destroy, " Destroys fcoe instance on a ethernet interface"); | ||
72 | |||
73 | module_param_call(enable, fcoe_transport_enable, NULL, NULL, S_IWUSR); | ||
74 | __MODULE_PARM_TYPE(enable, "string"); | ||
75 | MODULE_PARM_DESC(enable, " Enables fcoe on a ethernet interface."); | ||
76 | |||
77 | module_param_call(disable, fcoe_transport_disable, NULL, NULL, S_IWUSR); | ||
78 | __MODULE_PARM_TYPE(disable, "string"); | ||
79 | MODULE_PARM_DESC(disable, " Disables fcoe on a ethernet interface."); | ||
80 | |||
81 | /* notification function for packets from net device */ | ||
82 | static struct notifier_block libfcoe_notifier = { | ||
83 | .notifier_call = libfcoe_device_notification, | ||
84 | }; | ||
85 | |||
86 | /** | ||
87 | * fcoe_fc_crc() - Calculates the CRC for a given frame | ||
88 | * @fp: The frame to be checksumed | ||
89 | * | ||
90 | * This uses crc32() routine to calculate the CRC for a frame | ||
91 | * | ||
92 | * Return: The 32 bit CRC value | ||
93 | */ | ||
94 | u32 fcoe_fc_crc(struct fc_frame *fp) | ||
95 | { | ||
96 | struct sk_buff *skb = fp_skb(fp); | ||
97 | struct skb_frag_struct *frag; | ||
98 | unsigned char *data; | ||
99 | unsigned long off, len, clen; | ||
100 | u32 crc; | ||
101 | unsigned i; | ||
102 | |||
103 | crc = crc32(~0, skb->data, skb_headlen(skb)); | ||
104 | |||
105 | for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { | ||
106 | frag = &skb_shinfo(skb)->frags[i]; | ||
107 | off = frag->page_offset; | ||
108 | len = frag->size; | ||
109 | while (len > 0) { | ||
110 | clen = min(len, PAGE_SIZE - (off & ~PAGE_MASK)); | ||
111 | data = kmap_atomic(frag->page + (off >> PAGE_SHIFT), | ||
112 | KM_SKB_DATA_SOFTIRQ); | ||
113 | crc = crc32(crc, data + (off & ~PAGE_MASK), clen); | ||
114 | kunmap_atomic(data, KM_SKB_DATA_SOFTIRQ); | ||
115 | off += clen; | ||
116 | len -= clen; | ||
117 | } | ||
118 | } | ||
119 | return crc; | ||
120 | } | ||
121 | EXPORT_SYMBOL_GPL(fcoe_fc_crc); | ||
122 | |||
123 | /** | ||
124 | * fcoe_start_io() - Start FCoE I/O | ||
125 | * @skb: The packet to be transmitted | ||
126 | * | ||
127 | * This routine is called from the net device to start transmitting | ||
128 | * FCoE packets. | ||
129 | * | ||
130 | * Returns: 0 for success | ||
131 | */ | ||
132 | int fcoe_start_io(struct sk_buff *skb) | ||
133 | { | ||
134 | struct sk_buff *nskb; | ||
135 | int rc; | ||
136 | |||
137 | nskb = skb_clone(skb, GFP_ATOMIC); | ||
138 | if (!nskb) | ||
139 | return -ENOMEM; | ||
140 | rc = dev_queue_xmit(nskb); | ||
141 | if (rc != 0) | ||
142 | return rc; | ||
143 | kfree_skb(skb); | ||
144 | return 0; | ||
145 | } | ||
146 | EXPORT_SYMBOL_GPL(fcoe_start_io); | ||
147 | |||
148 | |||
149 | /** | ||
150 | * fcoe_clean_pending_queue() - Dequeue a skb and free it | ||
151 | * @lport: The local port to dequeue a skb on | ||
152 | */ | ||
153 | void fcoe_clean_pending_queue(struct fc_lport *lport) | ||
154 | { | ||
155 | struct fcoe_port *port = lport_priv(lport); | ||
156 | struct sk_buff *skb; | ||
157 | |||
158 | spin_lock_bh(&port->fcoe_pending_queue.lock); | ||
159 | while ((skb = __skb_dequeue(&port->fcoe_pending_queue)) != NULL) { | ||
160 | spin_unlock_bh(&port->fcoe_pending_queue.lock); | ||
161 | kfree_skb(skb); | ||
162 | spin_lock_bh(&port->fcoe_pending_queue.lock); | ||
163 | } | ||
164 | spin_unlock_bh(&port->fcoe_pending_queue.lock); | ||
165 | } | ||
166 | EXPORT_SYMBOL_GPL(fcoe_clean_pending_queue); | ||
167 | |||
168 | /** | ||
169 | * fcoe_check_wait_queue() - Attempt to clear the transmit backlog | ||
170 | * @lport: The local port whose backlog is to be cleared | ||
171 | * | ||
172 | * This empties the wait_queue, dequeues the head of the wait_queue queue | ||
173 | * and calls fcoe_start_io() for each packet. If all skb have been | ||
174 | * transmitted it returns the qlen. If an error occurs it restores | ||
175 | * wait_queue (to try again later) and returns -1. | ||
176 | * | ||
177 | * The wait_queue is used when the skb transmit fails. The failed skb | ||
178 | * will go in the wait_queue which will be emptied by the timer function or | ||
179 | * by the next skb transmit. | ||
180 | */ | ||
181 | void fcoe_check_wait_queue(struct fc_lport *lport, struct sk_buff *skb) | ||
182 | { | ||
183 | struct fcoe_port *port = lport_priv(lport); | ||
184 | int rc; | ||
185 | |||
186 | spin_lock_bh(&port->fcoe_pending_queue.lock); | ||
187 | |||
188 | if (skb) | ||
189 | __skb_queue_tail(&port->fcoe_pending_queue, skb); | ||
190 | |||
191 | if (port->fcoe_pending_queue_active) | ||
192 | goto out; | ||
193 | port->fcoe_pending_queue_active = 1; | ||
194 | |||
195 | while (port->fcoe_pending_queue.qlen) { | ||
196 | /* keep qlen > 0 until fcoe_start_io succeeds */ | ||
197 | port->fcoe_pending_queue.qlen++; | ||
198 | skb = __skb_dequeue(&port->fcoe_pending_queue); | ||
199 | |||
200 | spin_unlock_bh(&port->fcoe_pending_queue.lock); | ||
201 | rc = fcoe_start_io(skb); | ||
202 | spin_lock_bh(&port->fcoe_pending_queue.lock); | ||
203 | |||
204 | if (rc) { | ||
205 | __skb_queue_head(&port->fcoe_pending_queue, skb); | ||
206 | /* undo temporary increment above */ | ||
207 | port->fcoe_pending_queue.qlen--; | ||
208 | break; | ||
209 | } | ||
210 | /* undo temporary increment above */ | ||
211 | port->fcoe_pending_queue.qlen--; | ||
212 | } | ||
213 | |||
214 | if (port->fcoe_pending_queue.qlen < port->min_queue_depth) | ||
215 | lport->qfull = 0; | ||
216 | if (port->fcoe_pending_queue.qlen && !timer_pending(&port->timer)) | ||
217 | mod_timer(&port->timer, jiffies + 2); | ||
218 | port->fcoe_pending_queue_active = 0; | ||
219 | out: | ||
220 | if (port->fcoe_pending_queue.qlen > port->max_queue_depth) | ||
221 | lport->qfull = 1; | ||
222 | spin_unlock_bh(&port->fcoe_pending_queue.lock); | ||
223 | } | ||
224 | EXPORT_SYMBOL_GPL(fcoe_check_wait_queue); | ||
225 | |||
226 | /** | ||
227 | * fcoe_queue_timer() - The fcoe queue timer | ||
228 | * @lport: The local port | ||
229 | * | ||
230 | * Calls fcoe_check_wait_queue on timeout | ||
231 | */ | ||
232 | void fcoe_queue_timer(ulong lport) | ||
233 | { | ||
234 | fcoe_check_wait_queue((struct fc_lport *)lport, NULL); | ||
235 | } | ||
236 | EXPORT_SYMBOL_GPL(fcoe_queue_timer); | ||
237 | |||
238 | /** | ||
239 | * fcoe_get_paged_crc_eof() - Allocate a page to be used for the trailer CRC | ||
240 | * @skb: The packet to be transmitted | ||
241 | * @tlen: The total length of the trailer | ||
242 | * @fps: The fcoe context | ||
243 | * | ||
244 | * This routine allocates a page for frame trailers. The page is re-used if | ||
245 | * there is enough room left on it for the current trailer. If there isn't | ||
246 | * enough buffer left a new page is allocated for the trailer. Reference to | ||
247 | * the page from this function as well as the skbs using the page fragments | ||
248 | * ensure that the page is freed at the appropriate time. | ||
249 | * | ||
250 | * Returns: 0 for success | ||
251 | */ | ||
252 | int fcoe_get_paged_crc_eof(struct sk_buff *skb, int tlen, | ||
253 | struct fcoe_percpu_s *fps) | ||
254 | { | ||
255 | struct page *page; | ||
256 | |||
257 | page = fps->crc_eof_page; | ||
258 | if (!page) { | ||
259 | page = alloc_page(GFP_ATOMIC); | ||
260 | if (!page) | ||
261 | return -ENOMEM; | ||
262 | |||
263 | fps->crc_eof_page = page; | ||
264 | fps->crc_eof_offset = 0; | ||
265 | } | ||
266 | |||
267 | get_page(page); | ||
268 | skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags, page, | ||
269 | fps->crc_eof_offset, tlen); | ||
270 | skb->len += tlen; | ||
271 | skb->data_len += tlen; | ||
272 | skb->truesize += tlen; | ||
273 | fps->crc_eof_offset += sizeof(struct fcoe_crc_eof); | ||
274 | |||
275 | if (fps->crc_eof_offset >= PAGE_SIZE) { | ||
276 | fps->crc_eof_page = NULL; | ||
277 | fps->crc_eof_offset = 0; | ||
278 | put_page(page); | ||
279 | } | ||
280 | |||
281 | return 0; | ||
282 | } | ||
283 | EXPORT_SYMBOL_GPL(fcoe_get_paged_crc_eof); | ||
284 | |||
285 | /** | ||
286 | * fcoe_transport_lookup - find an fcoe transport that matches a netdev | ||
287 | * @netdev: The netdev to look for from all attached transports | ||
288 | * | ||
289 | * Returns : ptr to the fcoe transport that supports this netdev or NULL | ||
290 | * if not found. | ||
291 | * | ||
292 | * The ft_mutex should be held when this is called | ||
293 | */ | ||
294 | static struct fcoe_transport *fcoe_transport_lookup(struct net_device *netdev) | ||
295 | { | ||
296 | struct fcoe_transport *ft = NULL; | ||
297 | |||
298 | list_for_each_entry(ft, &fcoe_transports, list) | ||
299 | if (ft->match && ft->match(netdev)) | ||
300 | return ft; | ||
301 | return NULL; | ||
302 | } | ||
303 | |||
304 | /** | ||
305 | * fcoe_transport_attach - Attaches an FCoE transport | ||
306 | * @ft: The fcoe transport to be attached | ||
307 | * | ||
308 | * Returns : 0 for success | ||
309 | */ | ||
310 | int fcoe_transport_attach(struct fcoe_transport *ft) | ||
311 | { | ||
312 | int rc = 0; | ||
313 | |||
314 | mutex_lock(&ft_mutex); | ||
315 | if (ft->attached) { | ||
316 | LIBFCOE_TRANSPORT_DBG("transport %s already attached\n", | ||
317 | ft->name); | ||
318 | rc = -EEXIST; | ||
319 | goto out_attach; | ||
320 | } | ||
321 | |||
322 | /* Add default transport to the tail */ | ||
323 | if (strcmp(ft->name, FCOE_TRANSPORT_DEFAULT)) | ||
324 | list_add(&ft->list, &fcoe_transports); | ||
325 | else | ||
326 | list_add_tail(&ft->list, &fcoe_transports); | ||
327 | |||
328 | ft->attached = true; | ||
329 | LIBFCOE_TRANSPORT_DBG("attaching transport %s\n", ft->name); | ||
330 | |||
331 | out_attach: | ||
332 | mutex_unlock(&ft_mutex); | ||
333 | return rc; | ||
334 | } | ||
335 | EXPORT_SYMBOL(fcoe_transport_attach); | ||
336 | |||
337 | /** | ||
338 | * fcoe_transport_attach - Detaches an FCoE transport | ||
339 | * @ft: The fcoe transport to be attached | ||
340 | * | ||
341 | * Returns : 0 for success | ||
342 | */ | ||
343 | int fcoe_transport_detach(struct fcoe_transport *ft) | ||
344 | { | ||
345 | int rc = 0; | ||
346 | |||
347 | mutex_lock(&ft_mutex); | ||
348 | if (!ft->attached) { | ||
349 | LIBFCOE_TRANSPORT_DBG("transport %s already detached\n", | ||
350 | ft->name); | ||
351 | rc = -ENODEV; | ||
352 | goto out_attach; | ||
353 | } | ||
354 | |||
355 | list_del(&ft->list); | ||
356 | ft->attached = false; | ||
357 | LIBFCOE_TRANSPORT_DBG("detaching transport %s\n", ft->name); | ||
358 | |||
359 | out_attach: | ||
360 | mutex_unlock(&ft_mutex); | ||
361 | return rc; | ||
362 | |||
363 | } | ||
364 | EXPORT_SYMBOL(fcoe_transport_detach); | ||
365 | |||
366 | static int fcoe_transport_show(char *buffer, const struct kernel_param *kp) | ||
367 | { | ||
368 | int i, j; | ||
369 | struct fcoe_transport *ft = NULL; | ||
370 | |||
371 | i = j = sprintf(buffer, "Attached FCoE transports:"); | ||
372 | mutex_lock(&ft_mutex); | ||
373 | list_for_each_entry(ft, &fcoe_transports, list) { | ||
374 | i += snprintf(&buffer[i], IFNAMSIZ, "%s ", ft->name); | ||
375 | if (i >= PAGE_SIZE) | ||
376 | break; | ||
377 | } | ||
378 | mutex_unlock(&ft_mutex); | ||
379 | if (i == j) | ||
380 | i += snprintf(&buffer[i], IFNAMSIZ, "none"); | ||
381 | return i; | ||
382 | } | ||
383 | |||
384 | static int __init fcoe_transport_init(void) | ||
385 | { | ||
386 | register_netdevice_notifier(&libfcoe_notifier); | ||
387 | return 0; | ||
388 | } | ||
389 | |||
390 | static int __exit fcoe_transport_exit(void) | ||
391 | { | ||
392 | struct fcoe_transport *ft; | ||
393 | |||
394 | unregister_netdevice_notifier(&libfcoe_notifier); | ||
395 | mutex_lock(&ft_mutex); | ||
396 | list_for_each_entry(ft, &fcoe_transports, list) | ||
397 | printk(KERN_ERR "FCoE transport %s is still attached!\n", | ||
398 | ft->name); | ||
399 | mutex_unlock(&ft_mutex); | ||
400 | return 0; | ||
401 | } | ||
402 | |||
403 | |||
404 | static int fcoe_add_netdev_mapping(struct net_device *netdev, | ||
405 | struct fcoe_transport *ft) | ||
406 | { | ||
407 | struct fcoe_netdev_mapping *nm; | ||
408 | |||
409 | nm = kmalloc(sizeof(*nm), GFP_KERNEL); | ||
410 | if (!nm) { | ||
411 | printk(KERN_ERR "Unable to allocate netdev_mapping"); | ||
412 | return -ENOMEM; | ||
413 | } | ||
414 | |||
415 | nm->netdev = netdev; | ||
416 | nm->ft = ft; | ||
417 | |||
418 | mutex_lock(&fn_mutex); | ||
419 | list_add(&nm->list, &fcoe_netdevs); | ||
420 | mutex_unlock(&fn_mutex); | ||
421 | return 0; | ||
422 | } | ||
423 | |||
424 | |||
425 | static void fcoe_del_netdev_mapping(struct net_device *netdev) | ||
426 | { | ||
427 | struct fcoe_netdev_mapping *nm = NULL, *tmp; | ||
428 | |||
429 | mutex_lock(&fn_mutex); | ||
430 | list_for_each_entry_safe(nm, tmp, &fcoe_netdevs, list) { | ||
431 | if (nm->netdev == netdev) { | ||
432 | list_del(&nm->list); | ||
433 | kfree(nm); | ||
434 | mutex_unlock(&fn_mutex); | ||
435 | return; | ||
436 | } | ||
437 | } | ||
438 | mutex_unlock(&fn_mutex); | ||
439 | } | ||
440 | |||
441 | |||
442 | /** | ||
443 | * fcoe_netdev_map_lookup - find the fcoe transport that matches the netdev on which | ||
444 | * it was created | ||
445 | * | ||
446 | * Returns : ptr to the fcoe transport that supports this netdev or NULL | ||
447 | * if not found. | ||
448 | * | ||
449 | * The ft_mutex should be held when this is called | ||
450 | */ | ||
451 | static struct fcoe_transport *fcoe_netdev_map_lookup(struct net_device *netdev) | ||
452 | { | ||
453 | struct fcoe_transport *ft = NULL; | ||
454 | struct fcoe_netdev_mapping *nm; | ||
455 | |||
456 | mutex_lock(&fn_mutex); | ||
457 | list_for_each_entry(nm, &fcoe_netdevs, list) { | ||
458 | if (netdev == nm->netdev) { | ||
459 | ft = nm->ft; | ||
460 | mutex_unlock(&fn_mutex); | ||
461 | return ft; | ||
462 | } | ||
463 | } | ||
464 | |||
465 | mutex_unlock(&fn_mutex); | ||
466 | return NULL; | ||
467 | } | ||
468 | |||
469 | /** | ||
470 | * fcoe_if_to_netdev() - Parse a name buffer to get a net device | ||
471 | * @buffer: The name of the net device | ||
472 | * | ||
473 | * Returns: NULL or a ptr to net_device | ||
474 | */ | ||
475 | static struct net_device *fcoe_if_to_netdev(const char *buffer) | ||
476 | { | ||
477 | char *cp; | ||
478 | char ifname[IFNAMSIZ + 2]; | ||
479 | |||
480 | if (buffer) { | ||
481 | strlcpy(ifname, buffer, IFNAMSIZ); | ||
482 | cp = ifname + strlen(ifname); | ||
483 | while (--cp >= ifname && *cp == '\n') | ||
484 | *cp = '\0'; | ||
485 | return dev_get_by_name(&init_net, ifname); | ||
486 | } | ||
487 | return NULL; | ||
488 | } | ||
489 | |||
490 | /** | ||
491 | * libfcoe_device_notification() - Handler for net device events | ||
492 | * @notifier: The context of the notification | ||
493 | * @event: The type of event | ||
494 | * @ptr: The net device that the event was on | ||
495 | * | ||
496 | * This function is called by the Ethernet driver in case of link change event. | ||
497 | * | ||
498 | * Returns: 0 for success | ||
499 | */ | ||
500 | static int libfcoe_device_notification(struct notifier_block *notifier, | ||
501 | ulong event, void *ptr) | ||
502 | { | ||
503 | struct net_device *netdev = ptr; | ||
504 | |||
505 | switch (event) { | ||
506 | case NETDEV_UNREGISTER: | ||
507 | printk(KERN_ERR "libfcoe_device_notification: NETDEV_UNREGISTER %s\n", | ||
508 | netdev->name); | ||
509 | fcoe_del_netdev_mapping(netdev); | ||
510 | break; | ||
511 | } | ||
512 | return NOTIFY_OK; | ||
513 | } | ||
514 | |||
515 | |||
516 | /** | ||
517 | * fcoe_transport_create() - Create a fcoe interface | ||
518 | * @buffer: The name of the Ethernet interface to create on | ||
519 | * @kp: The associated kernel param | ||
520 | * | ||
521 | * Called from sysfs. This holds the ft_mutex while calling the | ||
522 | * registered fcoe transport's create function. | ||
523 | * | ||
524 | * Returns: 0 for success | ||
525 | */ | ||
526 | static int fcoe_transport_create(const char *buffer, struct kernel_param *kp) | ||
527 | { | ||
528 | int rc = -ENODEV; | ||
529 | struct net_device *netdev = NULL; | ||
530 | struct fcoe_transport *ft = NULL; | ||
531 | enum fip_state fip_mode = (enum fip_state)(long)kp->arg; | ||
532 | |||
533 | if (!mutex_trylock(&ft_mutex)) | ||
534 | return restart_syscall(); | ||
535 | |||
536 | #ifdef CONFIG_LIBFCOE_MODULE | ||
537 | /* | ||
538 | * Make sure the module has been initialized, and is not about to be | ||
539 | * removed. Module parameter sysfs files are writable before the | ||
540 | * module_init function is called and after module_exit. | ||
541 | */ | ||
542 | if (THIS_MODULE->state != MODULE_STATE_LIVE) | ||
543 | goto out_nodev; | ||
544 | #endif | ||
545 | |||
546 | netdev = fcoe_if_to_netdev(buffer); | ||
547 | if (!netdev) { | ||
548 | LIBFCOE_TRANSPORT_DBG("Invalid device %s.\n", buffer); | ||
549 | goto out_nodev; | ||
550 | } | ||
551 | |||
552 | ft = fcoe_netdev_map_lookup(netdev); | ||
553 | if (ft) { | ||
554 | LIBFCOE_TRANSPORT_DBG("transport %s already has existing " | ||
555 | "FCoE instance on %s.\n", | ||
556 | ft->name, netdev->name); | ||
557 | rc = -EEXIST; | ||
558 | goto out_putdev; | ||
559 | } | ||
560 | |||
561 | ft = fcoe_transport_lookup(netdev); | ||
562 | if (!ft) { | ||
563 | LIBFCOE_TRANSPORT_DBG("no FCoE transport found for %s.\n", | ||
564 | netdev->name); | ||
565 | goto out_putdev; | ||
566 | } | ||
567 | |||
568 | rc = fcoe_add_netdev_mapping(netdev, ft); | ||
569 | if (rc) { | ||
570 | LIBFCOE_TRANSPORT_DBG("failed to add new netdev mapping " | ||
571 | "for FCoE transport %s for %s.\n", | ||
572 | ft->name, netdev->name); | ||
573 | goto out_putdev; | ||
574 | } | ||
575 | |||
576 | /* pass to transport create */ | ||
577 | rc = ft->create ? ft->create(netdev, fip_mode) : -ENODEV; | ||
578 | if (rc) | ||
579 | fcoe_del_netdev_mapping(netdev); | ||
580 | |||
581 | LIBFCOE_TRANSPORT_DBG("transport %s %s to create fcoe on %s.\n", | ||
582 | ft->name, (rc) ? "failed" : "succeeded", | ||
583 | netdev->name); | ||
584 | |||
585 | out_putdev: | ||
586 | dev_put(netdev); | ||
587 | out_nodev: | ||
588 | mutex_unlock(&ft_mutex); | ||
589 | if (rc == -ERESTARTSYS) | ||
590 | return restart_syscall(); | ||
591 | else | ||
592 | return rc; | ||
593 | } | ||
594 | |||
595 | /** | ||
596 | * fcoe_transport_destroy() - Destroy a FCoE interface | ||
597 | * @buffer: The name of the Ethernet interface to be destroyed | ||
598 | * @kp: The associated kernel parameter | ||
599 | * | ||
600 | * Called from sysfs. This holds the ft_mutex while calling the | ||
601 | * registered fcoe transport's destroy function. | ||
602 | * | ||
603 | * Returns: 0 for success | ||
604 | */ | ||
605 | static int fcoe_transport_destroy(const char *buffer, struct kernel_param *kp) | ||
606 | { | ||
607 | int rc = -ENODEV; | ||
608 | struct net_device *netdev = NULL; | ||
609 | struct fcoe_transport *ft = NULL; | ||
610 | |||
611 | if (!mutex_trylock(&ft_mutex)) | ||
612 | return restart_syscall(); | ||
613 | |||
614 | #ifdef CONFIG_LIBFCOE_MODULE | ||
615 | /* | ||
616 | * Make sure the module has been initialized, and is not about to be | ||
617 | * removed. Module parameter sysfs files are writable before the | ||
618 | * module_init function is called and after module_exit. | ||
619 | */ | ||
620 | if (THIS_MODULE->state != MODULE_STATE_LIVE) | ||
621 | goto out_nodev; | ||
622 | #endif | ||
623 | |||
624 | netdev = fcoe_if_to_netdev(buffer); | ||
625 | if (!netdev) { | ||
626 | LIBFCOE_TRANSPORT_DBG("invalid device %s.\n", buffer); | ||
627 | goto out_nodev; | ||
628 | } | ||
629 | |||
630 | ft = fcoe_netdev_map_lookup(netdev); | ||
631 | if (!ft) { | ||
632 | LIBFCOE_TRANSPORT_DBG("no FCoE transport found for %s.\n", | ||
633 | netdev->name); | ||
634 | goto out_putdev; | ||
635 | } | ||
636 | |||
637 | /* pass to transport destroy */ | ||
638 | rc = ft->destroy ? ft->destroy(netdev) : -ENODEV; | ||
639 | fcoe_del_netdev_mapping(netdev); | ||
640 | LIBFCOE_TRANSPORT_DBG("transport %s %s to destroy fcoe on %s.\n", | ||
641 | ft->name, (rc) ? "failed" : "succeeded", | ||
642 | netdev->name); | ||
643 | |||
644 | out_putdev: | ||
645 | dev_put(netdev); | ||
646 | out_nodev: | ||
647 | mutex_unlock(&ft_mutex); | ||
648 | |||
649 | if (rc == -ERESTARTSYS) | ||
650 | return restart_syscall(); | ||
651 | else | ||
652 | return rc; | ||
653 | } | ||
654 | |||
655 | /** | ||
656 | * fcoe_transport_disable() - Disables a FCoE interface | ||
657 | * @buffer: The name of the Ethernet interface to be disabled | ||
658 | * @kp: The associated kernel parameter | ||
659 | * | ||
660 | * Called from sysfs. | ||
661 | * | ||
662 | * Returns: 0 for success | ||
663 | */ | ||
664 | static int fcoe_transport_disable(const char *buffer, struct kernel_param *kp) | ||
665 | { | ||
666 | int rc = -ENODEV; | ||
667 | struct net_device *netdev = NULL; | ||
668 | struct fcoe_transport *ft = NULL; | ||
669 | |||
670 | if (!mutex_trylock(&ft_mutex)) | ||
671 | return restart_syscall(); | ||
672 | |||
673 | #ifdef CONFIG_LIBFCOE_MODULE | ||
674 | /* | ||
675 | * Make sure the module has been initialized, and is not about to be | ||
676 | * removed. Module parameter sysfs files are writable before the | ||
677 | * module_init function is called and after module_exit. | ||
678 | */ | ||
679 | if (THIS_MODULE->state != MODULE_STATE_LIVE) | ||
680 | goto out_nodev; | ||
681 | #endif | ||
682 | |||
683 | netdev = fcoe_if_to_netdev(buffer); | ||
684 | if (!netdev) | ||
685 | goto out_nodev; | ||
686 | |||
687 | ft = fcoe_netdev_map_lookup(netdev); | ||
688 | if (!ft) | ||
689 | goto out_putdev; | ||
690 | |||
691 | rc = ft->disable ? ft->disable(netdev) : -ENODEV; | ||
692 | |||
693 | out_putdev: | ||
694 | dev_put(netdev); | ||
695 | out_nodev: | ||
696 | mutex_unlock(&ft_mutex); | ||
697 | |||
698 | if (rc == -ERESTARTSYS) | ||
699 | return restart_syscall(); | ||
700 | else | ||
701 | return rc; | ||
702 | } | ||
703 | |||
704 | /** | ||
705 | * fcoe_transport_enable() - Enables a FCoE interface | ||
706 | * @buffer: The name of the Ethernet interface to be enabled | ||
707 | * @kp: The associated kernel parameter | ||
708 | * | ||
709 | * Called from sysfs. | ||
710 | * | ||
711 | * Returns: 0 for success | ||
712 | */ | ||
713 | static int fcoe_transport_enable(const char *buffer, struct kernel_param *kp) | ||
714 | { | ||
715 | int rc = -ENODEV; | ||
716 | struct net_device *netdev = NULL; | ||
717 | struct fcoe_transport *ft = NULL; | ||
718 | |||
719 | if (!mutex_trylock(&ft_mutex)) | ||
720 | return restart_syscall(); | ||
721 | |||
722 | #ifdef CONFIG_LIBFCOE_MODULE | ||
723 | /* | ||
724 | * Make sure the module has been initialized, and is not about to be | ||
725 | * removed. Module parameter sysfs files are writable before the | ||
726 | * module_init function is called and after module_exit. | ||
727 | */ | ||
728 | if (THIS_MODULE->state != MODULE_STATE_LIVE) | ||
729 | goto out_nodev; | ||
730 | #endif | ||
731 | |||
732 | netdev = fcoe_if_to_netdev(buffer); | ||
733 | if (!netdev) | ||
734 | goto out_nodev; | ||
735 | |||
736 | ft = fcoe_netdev_map_lookup(netdev); | ||
737 | if (!ft) | ||
738 | goto out_putdev; | ||
739 | |||
740 | rc = ft->enable ? ft->enable(netdev) : -ENODEV; | ||
741 | |||
742 | out_putdev: | ||
743 | dev_put(netdev); | ||
744 | out_nodev: | ||
745 | mutex_unlock(&ft_mutex); | ||
746 | if (rc == -ERESTARTSYS) | ||
747 | return restart_syscall(); | ||
748 | else | ||
749 | return rc; | ||
750 | } | ||
751 | |||
752 | /** | ||
753 | * libfcoe_init() - Initialization routine for libfcoe.ko | ||
754 | */ | ||
755 | static int __init libfcoe_init(void) | ||
756 | { | ||
757 | fcoe_transport_init(); | ||
758 | |||
759 | return 0; | ||
760 | } | ||
761 | module_init(libfcoe_init); | ||
762 | |||
763 | /** | ||
764 | * libfcoe_exit() - Tear down libfcoe.ko | ||
765 | */ | ||
766 | static void __exit libfcoe_exit(void) | ||
767 | { | ||
768 | fcoe_transport_exit(); | ||
769 | } | ||
770 | module_exit(libfcoe_exit); | ||