1fdecf31bSYi Zou /* 2fdecf31bSYi Zou * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. 3fdecf31bSYi Zou * 4fdecf31bSYi Zou * This program is free software; you can redistribute it and/or modify it 5fdecf31bSYi Zou * under the terms and conditions of the GNU General Public License, 6fdecf31bSYi Zou * version 2, as published by the Free Software Foundation. 7fdecf31bSYi Zou * 8fdecf31bSYi Zou * This program is distributed in the hope it will be useful, but WITHOUT 9fdecf31bSYi Zou * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10fdecf31bSYi Zou * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 11fdecf31bSYi Zou * more details. 12fdecf31bSYi Zou * 13fdecf31bSYi Zou * You should have received a copy of the GNU General Public License along with 14fdecf31bSYi Zou * this program; if not, write to the Free Software Foundation, Inc., 15fdecf31bSYi Zou * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. 16fdecf31bSYi Zou * 17fdecf31bSYi Zou * Maintained at www.Open-FCoE.org 18fdecf31bSYi Zou */ 19fdecf31bSYi Zou 20fdecf31bSYi Zou #include <linux/types.h> 21fdecf31bSYi Zou #include <linux/module.h> 22fdecf31bSYi Zou #include <linux/kernel.h> 23fdecf31bSYi Zou #include <linux/list.h> 24fdecf31bSYi Zou #include <linux/netdevice.h> 25fdecf31bSYi Zou #include <linux/errno.h> 268597ae8bSBhanu Prakash Gollapudi #include <linux/crc32.h> 27fdecf31bSYi Zou #include <scsi/libfcoe.h> 28fdecf31bSYi Zou 29fdecf31bSYi Zou #include "libfcoe.h" 30fdecf31bSYi Zou 31e01efc33SYi Zou MODULE_AUTHOR("Open-FCoE.org"); 32e01efc33SYi Zou MODULE_DESCRIPTION("FIP discovery protocol and FCoE transport for FCoE HBAs"); 33e01efc33SYi Zou MODULE_LICENSE("GPL v2"); 34e01efc33SYi Zou 35fdecf31bSYi Zou static int fcoe_transport_create(const char *, struct kernel_param *); 36fdecf31bSYi Zou static int fcoe_transport_destroy(const char *, struct kernel_param *); 37fdecf31bSYi Zou static int fcoe_transport_show(char *buffer, const struct kernel_param *kp); 38fdecf31bSYi Zou static struct fcoe_transport *fcoe_transport_lookup(struct net_device *device); 39fdecf31bSYi Zou static struct fcoe_transport *fcoe_netdev_map_lookup(struct net_device *device); 40fdecf31bSYi Zou static int fcoe_transport_enable(const char *, struct kernel_param *); 41fdecf31bSYi Zou static int fcoe_transport_disable(const char *, struct kernel_param *); 4270be6344SBhanu Prakash Gollapudi static int libfcoe_device_notification(struct notifier_block *notifier, 4370be6344SBhanu Prakash Gollapudi ulong event, void *ptr); 44fdecf31bSYi Zou 45fdecf31bSYi Zou static LIST_HEAD(fcoe_transports); 46fdecf31bSYi Zou static DEFINE_MUTEX(ft_mutex); 4770be6344SBhanu Prakash Gollapudi static LIST_HEAD(fcoe_netdevs); 4870be6344SBhanu Prakash Gollapudi static DEFINE_MUTEX(fn_mutex); 49fdecf31bSYi Zou 50e01efc33SYi Zou unsigned int libfcoe_debug_logging; 51e01efc33SYi Zou module_param_named(debug_logging, libfcoe_debug_logging, int, S_IRUGO|S_IWUSR); 52e01efc33SYi Zou MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels"); 53e01efc33SYi Zou 54fdecf31bSYi Zou module_param_call(show, NULL, fcoe_transport_show, NULL, S_IRUSR); 55fdecf31bSYi Zou __MODULE_PARM_TYPE(show, "string"); 56fdecf31bSYi Zou MODULE_PARM_DESC(show, " Show attached FCoE transports"); 57fdecf31bSYi Zou 58fdecf31bSYi Zou module_param_call(create, fcoe_transport_create, NULL, 59fdecf31bSYi Zou (void *)FIP_MODE_FABRIC, S_IWUSR); 60fdecf31bSYi Zou __MODULE_PARM_TYPE(create, "string"); 61fdecf31bSYi Zou MODULE_PARM_DESC(create, " Creates fcoe instance on a ethernet interface"); 62fdecf31bSYi Zou 63fdecf31bSYi Zou module_param_call(create_vn2vn, fcoe_transport_create, NULL, 64fdecf31bSYi Zou (void *)FIP_MODE_VN2VN, S_IWUSR); 65fdecf31bSYi Zou __MODULE_PARM_TYPE(create_vn2vn, "string"); 66fdecf31bSYi Zou MODULE_PARM_DESC(create_vn2vn, " Creates a VN_node to VN_node FCoE instance " 67fdecf31bSYi Zou "on an Ethernet interface"); 68fdecf31bSYi Zou 69fdecf31bSYi Zou module_param_call(destroy, fcoe_transport_destroy, NULL, NULL, S_IWUSR); 70fdecf31bSYi Zou __MODULE_PARM_TYPE(destroy, "string"); 71fdecf31bSYi Zou MODULE_PARM_DESC(destroy, " Destroys fcoe instance on a ethernet interface"); 72fdecf31bSYi Zou 73fdecf31bSYi Zou module_param_call(enable, fcoe_transport_enable, NULL, NULL, S_IWUSR); 74fdecf31bSYi Zou __MODULE_PARM_TYPE(enable, "string"); 75fdecf31bSYi Zou MODULE_PARM_DESC(enable, " Enables fcoe on a ethernet interface."); 76fdecf31bSYi Zou 77fdecf31bSYi Zou module_param_call(disable, fcoe_transport_disable, NULL, NULL, S_IWUSR); 78fdecf31bSYi Zou __MODULE_PARM_TYPE(disable, "string"); 79fdecf31bSYi Zou MODULE_PARM_DESC(disable, " Disables fcoe on a ethernet interface."); 80fdecf31bSYi Zou 8170be6344SBhanu Prakash Gollapudi /* notification function for packets from net device */ 8270be6344SBhanu Prakash Gollapudi static struct notifier_block libfcoe_notifier = { 8370be6344SBhanu Prakash Gollapudi .notifier_call = libfcoe_device_notification, 8470be6344SBhanu Prakash Gollapudi }; 8570be6344SBhanu Prakash Gollapudi 86fdecf31bSYi Zou /** 878597ae8bSBhanu Prakash Gollapudi * fcoe_fc_crc() - Calculates the CRC for a given frame 888597ae8bSBhanu Prakash Gollapudi * @fp: The frame to be checksumed 898597ae8bSBhanu Prakash Gollapudi * 908597ae8bSBhanu Prakash Gollapudi * This uses crc32() routine to calculate the CRC for a frame 918597ae8bSBhanu Prakash Gollapudi * 928597ae8bSBhanu Prakash Gollapudi * Return: The 32 bit CRC value 938597ae8bSBhanu Prakash Gollapudi */ 948597ae8bSBhanu Prakash Gollapudi u32 fcoe_fc_crc(struct fc_frame *fp) 958597ae8bSBhanu Prakash Gollapudi { 968597ae8bSBhanu Prakash Gollapudi struct sk_buff *skb = fp_skb(fp); 978597ae8bSBhanu Prakash Gollapudi struct skb_frag_struct *frag; 988597ae8bSBhanu Prakash Gollapudi unsigned char *data; 998597ae8bSBhanu Prakash Gollapudi unsigned long off, len, clen; 1008597ae8bSBhanu Prakash Gollapudi u32 crc; 1018597ae8bSBhanu Prakash Gollapudi unsigned i; 1028597ae8bSBhanu Prakash Gollapudi 1038597ae8bSBhanu Prakash Gollapudi crc = crc32(~0, skb->data, skb_headlen(skb)); 1048597ae8bSBhanu Prakash Gollapudi 1058597ae8bSBhanu Prakash Gollapudi for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { 1068597ae8bSBhanu Prakash Gollapudi frag = &skb_shinfo(skb)->frags[i]; 1078597ae8bSBhanu Prakash Gollapudi off = frag->page_offset; 1088597ae8bSBhanu Prakash Gollapudi len = frag->size; 1098597ae8bSBhanu Prakash Gollapudi while (len > 0) { 1108597ae8bSBhanu Prakash Gollapudi clen = min(len, PAGE_SIZE - (off & ~PAGE_MASK)); 111165c68d5SIan Campbell data = kmap_atomic( 112165c68d5SIan Campbell skb_frag_page(frag) + (off >> PAGE_SHIFT), 1138597ae8bSBhanu Prakash Gollapudi KM_SKB_DATA_SOFTIRQ); 1148597ae8bSBhanu Prakash Gollapudi crc = crc32(crc, data + (off & ~PAGE_MASK), clen); 1158597ae8bSBhanu Prakash Gollapudi kunmap_atomic(data, KM_SKB_DATA_SOFTIRQ); 1168597ae8bSBhanu Prakash Gollapudi off += clen; 1178597ae8bSBhanu Prakash Gollapudi len -= clen; 1188597ae8bSBhanu Prakash Gollapudi } 1198597ae8bSBhanu Prakash Gollapudi } 1208597ae8bSBhanu Prakash Gollapudi return crc; 1218597ae8bSBhanu Prakash Gollapudi } 1228597ae8bSBhanu Prakash Gollapudi EXPORT_SYMBOL_GPL(fcoe_fc_crc); 1238597ae8bSBhanu Prakash Gollapudi 1248597ae8bSBhanu Prakash Gollapudi /** 1258597ae8bSBhanu Prakash Gollapudi * fcoe_start_io() - Start FCoE I/O 1268597ae8bSBhanu Prakash Gollapudi * @skb: The packet to be transmitted 1278597ae8bSBhanu Prakash Gollapudi * 1288597ae8bSBhanu Prakash Gollapudi * This routine is called from the net device to start transmitting 1298597ae8bSBhanu Prakash Gollapudi * FCoE packets. 1308597ae8bSBhanu Prakash Gollapudi * 1318597ae8bSBhanu Prakash Gollapudi * Returns: 0 for success 1328597ae8bSBhanu Prakash Gollapudi */ 1338597ae8bSBhanu Prakash Gollapudi int fcoe_start_io(struct sk_buff *skb) 1348597ae8bSBhanu Prakash Gollapudi { 1358597ae8bSBhanu Prakash Gollapudi struct sk_buff *nskb; 1368597ae8bSBhanu Prakash Gollapudi int rc; 1378597ae8bSBhanu Prakash Gollapudi 1388597ae8bSBhanu Prakash Gollapudi nskb = skb_clone(skb, GFP_ATOMIC); 1398597ae8bSBhanu Prakash Gollapudi if (!nskb) 1408597ae8bSBhanu Prakash Gollapudi return -ENOMEM; 1418597ae8bSBhanu Prakash Gollapudi rc = dev_queue_xmit(nskb); 1428597ae8bSBhanu Prakash Gollapudi if (rc != 0) 1438597ae8bSBhanu Prakash Gollapudi return rc; 1448597ae8bSBhanu Prakash Gollapudi kfree_skb(skb); 1458597ae8bSBhanu Prakash Gollapudi return 0; 1468597ae8bSBhanu Prakash Gollapudi } 1478597ae8bSBhanu Prakash Gollapudi EXPORT_SYMBOL_GPL(fcoe_start_io); 1488597ae8bSBhanu Prakash Gollapudi 1498597ae8bSBhanu Prakash Gollapudi 1508597ae8bSBhanu Prakash Gollapudi /** 1518597ae8bSBhanu Prakash Gollapudi * fcoe_clean_pending_queue() - Dequeue a skb and free it 1528597ae8bSBhanu Prakash Gollapudi * @lport: The local port to dequeue a skb on 1538597ae8bSBhanu Prakash Gollapudi */ 1548597ae8bSBhanu Prakash Gollapudi void fcoe_clean_pending_queue(struct fc_lport *lport) 1558597ae8bSBhanu Prakash Gollapudi { 1568597ae8bSBhanu Prakash Gollapudi struct fcoe_port *port = lport_priv(lport); 1578597ae8bSBhanu Prakash Gollapudi struct sk_buff *skb; 1588597ae8bSBhanu Prakash Gollapudi 1598597ae8bSBhanu Prakash Gollapudi spin_lock_bh(&port->fcoe_pending_queue.lock); 1608597ae8bSBhanu Prakash Gollapudi while ((skb = __skb_dequeue(&port->fcoe_pending_queue)) != NULL) { 1618597ae8bSBhanu Prakash Gollapudi spin_unlock_bh(&port->fcoe_pending_queue.lock); 1628597ae8bSBhanu Prakash Gollapudi kfree_skb(skb); 1638597ae8bSBhanu Prakash Gollapudi spin_lock_bh(&port->fcoe_pending_queue.lock); 1648597ae8bSBhanu Prakash Gollapudi } 1658597ae8bSBhanu Prakash Gollapudi spin_unlock_bh(&port->fcoe_pending_queue.lock); 1668597ae8bSBhanu Prakash Gollapudi } 1678597ae8bSBhanu Prakash Gollapudi EXPORT_SYMBOL_GPL(fcoe_clean_pending_queue); 1688597ae8bSBhanu Prakash Gollapudi 1698597ae8bSBhanu Prakash Gollapudi /** 1708597ae8bSBhanu Prakash Gollapudi * fcoe_check_wait_queue() - Attempt to clear the transmit backlog 1718597ae8bSBhanu Prakash Gollapudi * @lport: The local port whose backlog is to be cleared 1728597ae8bSBhanu Prakash Gollapudi * 1738597ae8bSBhanu Prakash Gollapudi * This empties the wait_queue, dequeues the head of the wait_queue queue 1748597ae8bSBhanu Prakash Gollapudi * and calls fcoe_start_io() for each packet. If all skb have been 1758597ae8bSBhanu Prakash Gollapudi * transmitted it returns the qlen. If an error occurs it restores 1768597ae8bSBhanu Prakash Gollapudi * wait_queue (to try again later) and returns -1. 1778597ae8bSBhanu Prakash Gollapudi * 1788597ae8bSBhanu Prakash Gollapudi * The wait_queue is used when the skb transmit fails. The failed skb 1798597ae8bSBhanu Prakash Gollapudi * will go in the wait_queue which will be emptied by the timer function or 1808597ae8bSBhanu Prakash Gollapudi * by the next skb transmit. 1818597ae8bSBhanu Prakash Gollapudi */ 1828597ae8bSBhanu Prakash Gollapudi void fcoe_check_wait_queue(struct fc_lport *lport, struct sk_buff *skb) 1838597ae8bSBhanu Prakash Gollapudi { 1848597ae8bSBhanu Prakash Gollapudi struct fcoe_port *port = lport_priv(lport); 1858597ae8bSBhanu Prakash Gollapudi int rc; 1868597ae8bSBhanu Prakash Gollapudi 1878597ae8bSBhanu Prakash Gollapudi spin_lock_bh(&port->fcoe_pending_queue.lock); 1888597ae8bSBhanu Prakash Gollapudi 1898597ae8bSBhanu Prakash Gollapudi if (skb) 1908597ae8bSBhanu Prakash Gollapudi __skb_queue_tail(&port->fcoe_pending_queue, skb); 1918597ae8bSBhanu Prakash Gollapudi 1928597ae8bSBhanu Prakash Gollapudi if (port->fcoe_pending_queue_active) 1938597ae8bSBhanu Prakash Gollapudi goto out; 1948597ae8bSBhanu Prakash Gollapudi port->fcoe_pending_queue_active = 1; 1958597ae8bSBhanu Prakash Gollapudi 1968597ae8bSBhanu Prakash Gollapudi while (port->fcoe_pending_queue.qlen) { 1978597ae8bSBhanu Prakash Gollapudi /* keep qlen > 0 until fcoe_start_io succeeds */ 1988597ae8bSBhanu Prakash Gollapudi port->fcoe_pending_queue.qlen++; 1998597ae8bSBhanu Prakash Gollapudi skb = __skb_dequeue(&port->fcoe_pending_queue); 2008597ae8bSBhanu Prakash Gollapudi 2018597ae8bSBhanu Prakash Gollapudi spin_unlock_bh(&port->fcoe_pending_queue.lock); 2028597ae8bSBhanu Prakash Gollapudi rc = fcoe_start_io(skb); 2038597ae8bSBhanu Prakash Gollapudi spin_lock_bh(&port->fcoe_pending_queue.lock); 2048597ae8bSBhanu Prakash Gollapudi 2058597ae8bSBhanu Prakash Gollapudi if (rc) { 2068597ae8bSBhanu Prakash Gollapudi __skb_queue_head(&port->fcoe_pending_queue, skb); 2078597ae8bSBhanu Prakash Gollapudi /* undo temporary increment above */ 2088597ae8bSBhanu Prakash Gollapudi port->fcoe_pending_queue.qlen--; 2098597ae8bSBhanu Prakash Gollapudi break; 2108597ae8bSBhanu Prakash Gollapudi } 2118597ae8bSBhanu Prakash Gollapudi /* undo temporary increment above */ 2128597ae8bSBhanu Prakash Gollapudi port->fcoe_pending_queue.qlen--; 2138597ae8bSBhanu Prakash Gollapudi } 2148597ae8bSBhanu Prakash Gollapudi 2158597ae8bSBhanu Prakash Gollapudi if (port->fcoe_pending_queue.qlen < port->min_queue_depth) 2168597ae8bSBhanu Prakash Gollapudi lport->qfull = 0; 2178597ae8bSBhanu Prakash Gollapudi if (port->fcoe_pending_queue.qlen && !timer_pending(&port->timer)) 2188597ae8bSBhanu Prakash Gollapudi mod_timer(&port->timer, jiffies + 2); 2198597ae8bSBhanu Prakash Gollapudi port->fcoe_pending_queue_active = 0; 2208597ae8bSBhanu Prakash Gollapudi out: 2218597ae8bSBhanu Prakash Gollapudi if (port->fcoe_pending_queue.qlen > port->max_queue_depth) 2228597ae8bSBhanu Prakash Gollapudi lport->qfull = 1; 2238597ae8bSBhanu Prakash Gollapudi spin_unlock_bh(&port->fcoe_pending_queue.lock); 2248597ae8bSBhanu Prakash Gollapudi } 2258597ae8bSBhanu Prakash Gollapudi EXPORT_SYMBOL_GPL(fcoe_check_wait_queue); 2268597ae8bSBhanu Prakash Gollapudi 2278597ae8bSBhanu Prakash Gollapudi /** 2288597ae8bSBhanu Prakash Gollapudi * fcoe_queue_timer() - The fcoe queue timer 2298597ae8bSBhanu Prakash Gollapudi * @lport: The local port 2308597ae8bSBhanu Prakash Gollapudi * 2318597ae8bSBhanu Prakash Gollapudi * Calls fcoe_check_wait_queue on timeout 2328597ae8bSBhanu Prakash Gollapudi */ 2338597ae8bSBhanu Prakash Gollapudi void fcoe_queue_timer(ulong lport) 2348597ae8bSBhanu Prakash Gollapudi { 2358597ae8bSBhanu Prakash Gollapudi fcoe_check_wait_queue((struct fc_lport *)lport, NULL); 2368597ae8bSBhanu Prakash Gollapudi } 2378597ae8bSBhanu Prakash Gollapudi EXPORT_SYMBOL_GPL(fcoe_queue_timer); 2388597ae8bSBhanu Prakash Gollapudi 2398597ae8bSBhanu Prakash Gollapudi /** 2408597ae8bSBhanu Prakash Gollapudi * fcoe_get_paged_crc_eof() - Allocate a page to be used for the trailer CRC 2418597ae8bSBhanu Prakash Gollapudi * @skb: The packet to be transmitted 2428597ae8bSBhanu Prakash Gollapudi * @tlen: The total length of the trailer 2438597ae8bSBhanu Prakash Gollapudi * @fps: The fcoe context 2448597ae8bSBhanu Prakash Gollapudi * 2458597ae8bSBhanu Prakash Gollapudi * This routine allocates a page for frame trailers. The page is re-used if 2468597ae8bSBhanu Prakash Gollapudi * there is enough room left on it for the current trailer. If there isn't 2478597ae8bSBhanu Prakash Gollapudi * enough buffer left a new page is allocated for the trailer. Reference to 2488597ae8bSBhanu Prakash Gollapudi * the page from this function as well as the skbs using the page fragments 2498597ae8bSBhanu Prakash Gollapudi * ensure that the page is freed at the appropriate time. 2508597ae8bSBhanu Prakash Gollapudi * 2518597ae8bSBhanu Prakash Gollapudi * Returns: 0 for success 2528597ae8bSBhanu Prakash Gollapudi */ 2538597ae8bSBhanu Prakash Gollapudi int fcoe_get_paged_crc_eof(struct sk_buff *skb, int tlen, 2548597ae8bSBhanu Prakash Gollapudi struct fcoe_percpu_s *fps) 2558597ae8bSBhanu Prakash Gollapudi { 2568597ae8bSBhanu Prakash Gollapudi struct page *page; 2578597ae8bSBhanu Prakash Gollapudi 2588597ae8bSBhanu Prakash Gollapudi page = fps->crc_eof_page; 2598597ae8bSBhanu Prakash Gollapudi if (!page) { 2608597ae8bSBhanu Prakash Gollapudi page = alloc_page(GFP_ATOMIC); 2618597ae8bSBhanu Prakash Gollapudi if (!page) 2628597ae8bSBhanu Prakash Gollapudi return -ENOMEM; 2638597ae8bSBhanu Prakash Gollapudi 2648597ae8bSBhanu Prakash Gollapudi fps->crc_eof_page = page; 2658597ae8bSBhanu Prakash Gollapudi fps->crc_eof_offset = 0; 2668597ae8bSBhanu Prakash Gollapudi } 2678597ae8bSBhanu Prakash Gollapudi 2688597ae8bSBhanu Prakash Gollapudi get_page(page); 2698597ae8bSBhanu Prakash Gollapudi skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags, page, 2708597ae8bSBhanu Prakash Gollapudi fps->crc_eof_offset, tlen); 2718597ae8bSBhanu Prakash Gollapudi skb->len += tlen; 2728597ae8bSBhanu Prakash Gollapudi skb->data_len += tlen; 2738597ae8bSBhanu Prakash Gollapudi skb->truesize += tlen; 2748597ae8bSBhanu Prakash Gollapudi fps->crc_eof_offset += sizeof(struct fcoe_crc_eof); 2758597ae8bSBhanu Prakash Gollapudi 2768597ae8bSBhanu Prakash Gollapudi if (fps->crc_eof_offset >= PAGE_SIZE) { 2778597ae8bSBhanu Prakash Gollapudi fps->crc_eof_page = NULL; 2788597ae8bSBhanu Prakash Gollapudi fps->crc_eof_offset = 0; 2798597ae8bSBhanu Prakash Gollapudi put_page(page); 2808597ae8bSBhanu Prakash Gollapudi } 2818597ae8bSBhanu Prakash Gollapudi 2828597ae8bSBhanu Prakash Gollapudi return 0; 2838597ae8bSBhanu Prakash Gollapudi } 2848597ae8bSBhanu Prakash Gollapudi EXPORT_SYMBOL_GPL(fcoe_get_paged_crc_eof); 2858597ae8bSBhanu Prakash Gollapudi 2868597ae8bSBhanu Prakash Gollapudi /** 287fdecf31bSYi Zou * fcoe_transport_lookup - find an fcoe transport that matches a netdev 288fdecf31bSYi Zou * @netdev: The netdev to look for from all attached transports 289fdecf31bSYi Zou * 290fdecf31bSYi Zou * Returns : ptr to the fcoe transport that supports this netdev or NULL 291fdecf31bSYi Zou * if not found. 292fdecf31bSYi Zou * 293fdecf31bSYi Zou * The ft_mutex should be held when this is called 294fdecf31bSYi Zou */ 295fdecf31bSYi Zou static struct fcoe_transport *fcoe_transport_lookup(struct net_device *netdev) 296fdecf31bSYi Zou { 297fdecf31bSYi Zou struct fcoe_transport *ft = NULL; 298fdecf31bSYi Zou 299fdecf31bSYi Zou list_for_each_entry(ft, &fcoe_transports, list) 300fdecf31bSYi Zou if (ft->match && ft->match(netdev)) 301fdecf31bSYi Zou return ft; 302fdecf31bSYi Zou return NULL; 303fdecf31bSYi Zou } 304fdecf31bSYi Zou 305fdecf31bSYi Zou /** 306fdecf31bSYi Zou * fcoe_transport_attach - Attaches an FCoE transport 307fdecf31bSYi Zou * @ft: The fcoe transport to be attached 308fdecf31bSYi Zou * 309fdecf31bSYi Zou * Returns : 0 for success 310fdecf31bSYi Zou */ 311fdecf31bSYi Zou int fcoe_transport_attach(struct fcoe_transport *ft) 312fdecf31bSYi Zou { 313fdecf31bSYi Zou int rc = 0; 314fdecf31bSYi Zou 315fdecf31bSYi Zou mutex_lock(&ft_mutex); 316fdecf31bSYi Zou if (ft->attached) { 317fdecf31bSYi Zou LIBFCOE_TRANSPORT_DBG("transport %s already attached\n", 318fdecf31bSYi Zou ft->name); 319fdecf31bSYi Zou rc = -EEXIST; 320fdecf31bSYi Zou goto out_attach; 321fdecf31bSYi Zou } 322fdecf31bSYi Zou 323fdecf31bSYi Zou /* Add default transport to the tail */ 324fdecf31bSYi Zou if (strcmp(ft->name, FCOE_TRANSPORT_DEFAULT)) 325fdecf31bSYi Zou list_add(&ft->list, &fcoe_transports); 326fdecf31bSYi Zou else 327fdecf31bSYi Zou list_add_tail(&ft->list, &fcoe_transports); 328fdecf31bSYi Zou 329fdecf31bSYi Zou ft->attached = true; 330fdecf31bSYi Zou LIBFCOE_TRANSPORT_DBG("attaching transport %s\n", ft->name); 331fdecf31bSYi Zou 332fdecf31bSYi Zou out_attach: 333fdecf31bSYi Zou mutex_unlock(&ft_mutex); 334fdecf31bSYi Zou return rc; 335fdecf31bSYi Zou } 336fdecf31bSYi Zou EXPORT_SYMBOL(fcoe_transport_attach); 337fdecf31bSYi Zou 338fdecf31bSYi Zou /** 3394ef7fb15SYi Zou * fcoe_transport_detach - Detaches an FCoE transport 340fdecf31bSYi Zou * @ft: The fcoe transport to be attached 341fdecf31bSYi Zou * 342fdecf31bSYi Zou * Returns : 0 for success 343fdecf31bSYi Zou */ 344fdecf31bSYi Zou int fcoe_transport_detach(struct fcoe_transport *ft) 345fdecf31bSYi Zou { 346fdecf31bSYi Zou int rc = 0; 34769922fcdSYi Zou struct fcoe_netdev_mapping *nm = NULL, *tmp; 348fdecf31bSYi Zou 349fdecf31bSYi Zou mutex_lock(&ft_mutex); 350fdecf31bSYi Zou if (!ft->attached) { 351fdecf31bSYi Zou LIBFCOE_TRANSPORT_DBG("transport %s already detached\n", 352fdecf31bSYi Zou ft->name); 353fdecf31bSYi Zou rc = -ENODEV; 354fdecf31bSYi Zou goto out_attach; 355fdecf31bSYi Zou } 356fdecf31bSYi Zou 35769922fcdSYi Zou /* remove netdev mapping for this transport as it is going away */ 35869922fcdSYi Zou mutex_lock(&fn_mutex); 35969922fcdSYi Zou list_for_each_entry_safe(nm, tmp, &fcoe_netdevs, list) { 36069922fcdSYi Zou if (nm->ft == ft) { 36169922fcdSYi Zou LIBFCOE_TRANSPORT_DBG("transport %s going away, " 36269922fcdSYi Zou "remove its netdev mapping for %s\n", 36369922fcdSYi Zou ft->name, nm->netdev->name); 36469922fcdSYi Zou list_del(&nm->list); 36569922fcdSYi Zou kfree(nm); 36669922fcdSYi Zou } 36769922fcdSYi Zou } 36869922fcdSYi Zou mutex_unlock(&fn_mutex); 36969922fcdSYi Zou 370fdecf31bSYi Zou list_del(&ft->list); 371fdecf31bSYi Zou ft->attached = false; 372fdecf31bSYi Zou LIBFCOE_TRANSPORT_DBG("detaching transport %s\n", ft->name); 373fdecf31bSYi Zou 374fdecf31bSYi Zou out_attach: 375fdecf31bSYi Zou mutex_unlock(&ft_mutex); 376fdecf31bSYi Zou return rc; 377fdecf31bSYi Zou 378fdecf31bSYi Zou } 379fdecf31bSYi Zou EXPORT_SYMBOL(fcoe_transport_detach); 380fdecf31bSYi Zou 381fdecf31bSYi Zou static int fcoe_transport_show(char *buffer, const struct kernel_param *kp) 382fdecf31bSYi Zou { 383fdecf31bSYi Zou int i, j; 384fdecf31bSYi Zou struct fcoe_transport *ft = NULL; 385fdecf31bSYi Zou 386fdecf31bSYi Zou i = j = sprintf(buffer, "Attached FCoE transports:"); 387fdecf31bSYi Zou mutex_lock(&ft_mutex); 388fdecf31bSYi Zou list_for_each_entry(ft, &fcoe_transports, list) { 389a01a5a57SYi Zou if (i >= PAGE_SIZE - IFNAMSIZ) 390fdecf31bSYi Zou break; 391a01a5a57SYi Zou i += snprintf(&buffer[i], IFNAMSIZ, "%s ", ft->name); 392fdecf31bSYi Zou } 393fdecf31bSYi Zou mutex_unlock(&ft_mutex); 394fdecf31bSYi Zou if (i == j) 395fdecf31bSYi Zou i += snprintf(&buffer[i], IFNAMSIZ, "none"); 396fdecf31bSYi Zou return i; 397fdecf31bSYi Zou } 398fdecf31bSYi Zou 399fdecf31bSYi Zou static int __init fcoe_transport_init(void) 400fdecf31bSYi Zou { 40170be6344SBhanu Prakash Gollapudi register_netdevice_notifier(&libfcoe_notifier); 402fdecf31bSYi Zou return 0; 403fdecf31bSYi Zou } 404fdecf31bSYi Zou 405fdecf31bSYi Zou static int __exit fcoe_transport_exit(void) 406fdecf31bSYi Zou { 407fdecf31bSYi Zou struct fcoe_transport *ft; 408fdecf31bSYi Zou 40970be6344SBhanu Prakash Gollapudi unregister_netdevice_notifier(&libfcoe_notifier); 410fdecf31bSYi Zou mutex_lock(&ft_mutex); 411fdecf31bSYi Zou list_for_each_entry(ft, &fcoe_transports, list) 412fdecf31bSYi Zou printk(KERN_ERR "FCoE transport %s is still attached!\n", 413fdecf31bSYi Zou ft->name); 414fdecf31bSYi Zou mutex_unlock(&ft_mutex); 415fdecf31bSYi Zou return 0; 416fdecf31bSYi Zou } 417fdecf31bSYi Zou 418fdecf31bSYi Zou 419fdecf31bSYi Zou static int fcoe_add_netdev_mapping(struct net_device *netdev, 420fdecf31bSYi Zou struct fcoe_transport *ft) 421fdecf31bSYi Zou { 422fdecf31bSYi Zou struct fcoe_netdev_mapping *nm; 423fdecf31bSYi Zou 424fdecf31bSYi Zou nm = kmalloc(sizeof(*nm), GFP_KERNEL); 425fdecf31bSYi Zou if (!nm) { 426fdecf31bSYi Zou printk(KERN_ERR "Unable to allocate netdev_mapping"); 427fdecf31bSYi Zou return -ENOMEM; 428fdecf31bSYi Zou } 429fdecf31bSYi Zou 430fdecf31bSYi Zou nm->netdev = netdev; 431fdecf31bSYi Zou nm->ft = ft; 432fdecf31bSYi Zou 43370be6344SBhanu Prakash Gollapudi mutex_lock(&fn_mutex); 434fdecf31bSYi Zou list_add(&nm->list, &fcoe_netdevs); 43570be6344SBhanu Prakash Gollapudi mutex_unlock(&fn_mutex); 436fdecf31bSYi Zou return 0; 437fdecf31bSYi Zou } 438fdecf31bSYi Zou 439fdecf31bSYi Zou 440fdecf31bSYi Zou static void fcoe_del_netdev_mapping(struct net_device *netdev) 441fdecf31bSYi Zou { 442fdecf31bSYi Zou struct fcoe_netdev_mapping *nm = NULL, *tmp; 443fdecf31bSYi Zou 44470be6344SBhanu Prakash Gollapudi mutex_lock(&fn_mutex); 445fdecf31bSYi Zou list_for_each_entry_safe(nm, tmp, &fcoe_netdevs, list) { 446fdecf31bSYi Zou if (nm->netdev == netdev) { 447fdecf31bSYi Zou list_del(&nm->list); 448fdecf31bSYi Zou kfree(nm); 44970be6344SBhanu Prakash Gollapudi mutex_unlock(&fn_mutex); 450fdecf31bSYi Zou return; 451fdecf31bSYi Zou } 452fdecf31bSYi Zou } 45370be6344SBhanu Prakash Gollapudi mutex_unlock(&fn_mutex); 454fdecf31bSYi Zou } 455fdecf31bSYi Zou 456fdecf31bSYi Zou 457fdecf31bSYi Zou /** 458fdecf31bSYi Zou * fcoe_netdev_map_lookup - find the fcoe transport that matches the netdev on which 459fdecf31bSYi Zou * it was created 460fdecf31bSYi Zou * 461fdecf31bSYi Zou * Returns : ptr to the fcoe transport that supports this netdev or NULL 462fdecf31bSYi Zou * if not found. 463fdecf31bSYi Zou * 464fdecf31bSYi Zou * The ft_mutex should be held when this is called 465fdecf31bSYi Zou */ 466fdecf31bSYi Zou static struct fcoe_transport *fcoe_netdev_map_lookup(struct net_device *netdev) 467fdecf31bSYi Zou { 468fdecf31bSYi Zou struct fcoe_transport *ft = NULL; 469fdecf31bSYi Zou struct fcoe_netdev_mapping *nm; 470fdecf31bSYi Zou 47170be6344SBhanu Prakash Gollapudi mutex_lock(&fn_mutex); 472fdecf31bSYi Zou list_for_each_entry(nm, &fcoe_netdevs, list) { 473fdecf31bSYi Zou if (netdev == nm->netdev) { 474fdecf31bSYi Zou ft = nm->ft; 47570be6344SBhanu Prakash Gollapudi mutex_unlock(&fn_mutex); 476fdecf31bSYi Zou return ft; 477fdecf31bSYi Zou } 478fdecf31bSYi Zou } 479fdecf31bSYi Zou 48070be6344SBhanu Prakash Gollapudi mutex_unlock(&fn_mutex); 481fdecf31bSYi Zou return NULL; 482fdecf31bSYi Zou } 483fdecf31bSYi Zou 484fdecf31bSYi Zou /** 485fdecf31bSYi Zou * fcoe_if_to_netdev() - Parse a name buffer to get a net device 486fdecf31bSYi Zou * @buffer: The name of the net device 487fdecf31bSYi Zou * 488fdecf31bSYi Zou * Returns: NULL or a ptr to net_device 489fdecf31bSYi Zou */ 490fdecf31bSYi Zou static struct net_device *fcoe_if_to_netdev(const char *buffer) 491fdecf31bSYi Zou { 492fdecf31bSYi Zou char *cp; 493fdecf31bSYi Zou char ifname[IFNAMSIZ + 2]; 494fdecf31bSYi Zou 495fdecf31bSYi Zou if (buffer) { 496fdecf31bSYi Zou strlcpy(ifname, buffer, IFNAMSIZ); 497fdecf31bSYi Zou cp = ifname + strlen(ifname); 498fdecf31bSYi Zou while (--cp >= ifname && *cp == '\n') 499fdecf31bSYi Zou *cp = '\0'; 500fdecf31bSYi Zou return dev_get_by_name(&init_net, ifname); 501fdecf31bSYi Zou } 502fdecf31bSYi Zou return NULL; 503fdecf31bSYi Zou } 504fdecf31bSYi Zou 505fdecf31bSYi Zou /** 50670be6344SBhanu Prakash Gollapudi * libfcoe_device_notification() - Handler for net device events 50770be6344SBhanu Prakash Gollapudi * @notifier: The context of the notification 50870be6344SBhanu Prakash Gollapudi * @event: The type of event 50970be6344SBhanu Prakash Gollapudi * @ptr: The net device that the event was on 51070be6344SBhanu Prakash Gollapudi * 51170be6344SBhanu Prakash Gollapudi * This function is called by the Ethernet driver in case of link change event. 51270be6344SBhanu Prakash Gollapudi * 51370be6344SBhanu Prakash Gollapudi * Returns: 0 for success 51470be6344SBhanu Prakash Gollapudi */ 51570be6344SBhanu Prakash Gollapudi static int libfcoe_device_notification(struct notifier_block *notifier, 51670be6344SBhanu Prakash Gollapudi ulong event, void *ptr) 51770be6344SBhanu Prakash Gollapudi { 51870be6344SBhanu Prakash Gollapudi struct net_device *netdev = ptr; 51970be6344SBhanu Prakash Gollapudi 52070be6344SBhanu Prakash Gollapudi switch (event) { 52170be6344SBhanu Prakash Gollapudi case NETDEV_UNREGISTER: 52270be6344SBhanu Prakash Gollapudi printk(KERN_ERR "libfcoe_device_notification: NETDEV_UNREGISTER %s\n", 52370be6344SBhanu Prakash Gollapudi netdev->name); 52470be6344SBhanu Prakash Gollapudi fcoe_del_netdev_mapping(netdev); 52570be6344SBhanu Prakash Gollapudi break; 52670be6344SBhanu Prakash Gollapudi } 52770be6344SBhanu Prakash Gollapudi return NOTIFY_OK; 52870be6344SBhanu Prakash Gollapudi } 52970be6344SBhanu Prakash Gollapudi 53070be6344SBhanu Prakash Gollapudi 53170be6344SBhanu Prakash Gollapudi /** 532fdecf31bSYi Zou * fcoe_transport_create() - Create a fcoe interface 533fdecf31bSYi Zou * @buffer: The name of the Ethernet interface to create on 534fdecf31bSYi Zou * @kp: The associated kernel param 535fdecf31bSYi Zou * 536fdecf31bSYi Zou * Called from sysfs. This holds the ft_mutex while calling the 537fdecf31bSYi Zou * registered fcoe transport's create function. 538fdecf31bSYi Zou * 539fdecf31bSYi Zou * Returns: 0 for success 540fdecf31bSYi Zou */ 541fdecf31bSYi Zou static int fcoe_transport_create(const char *buffer, struct kernel_param *kp) 542fdecf31bSYi Zou { 543fdecf31bSYi Zou int rc = -ENODEV; 544fdecf31bSYi Zou struct net_device *netdev = NULL; 545fdecf31bSYi Zou struct fcoe_transport *ft = NULL; 546fdecf31bSYi Zou enum fip_state fip_mode = (enum fip_state)(long)kp->arg; 547fdecf31bSYi Zou 548b3960afeSRobert Love mutex_lock(&ft_mutex); 549b3960afeSRobert Love 550fdecf31bSYi Zou netdev = fcoe_if_to_netdev(buffer); 551fdecf31bSYi Zou if (!netdev) { 552fdecf31bSYi Zou LIBFCOE_TRANSPORT_DBG("Invalid device %s.\n", buffer); 553fdecf31bSYi Zou goto out_nodev; 554fdecf31bSYi Zou } 555fdecf31bSYi Zou 556fdecf31bSYi Zou ft = fcoe_netdev_map_lookup(netdev); 557fdecf31bSYi Zou if (ft) { 558fdecf31bSYi Zou LIBFCOE_TRANSPORT_DBG("transport %s already has existing " 559fdecf31bSYi Zou "FCoE instance on %s.\n", 560fdecf31bSYi Zou ft->name, netdev->name); 561fdecf31bSYi Zou rc = -EEXIST; 562fdecf31bSYi Zou goto out_putdev; 563fdecf31bSYi Zou } 564fdecf31bSYi Zou 565fdecf31bSYi Zou ft = fcoe_transport_lookup(netdev); 566fdecf31bSYi Zou if (!ft) { 567fdecf31bSYi Zou LIBFCOE_TRANSPORT_DBG("no FCoE transport found for %s.\n", 568fdecf31bSYi Zou netdev->name); 569fdecf31bSYi Zou goto out_putdev; 570fdecf31bSYi Zou } 571fdecf31bSYi Zou 572fdecf31bSYi Zou rc = fcoe_add_netdev_mapping(netdev, ft); 573fdecf31bSYi Zou if (rc) { 574fdecf31bSYi Zou LIBFCOE_TRANSPORT_DBG("failed to add new netdev mapping " 575fdecf31bSYi Zou "for FCoE transport %s for %s.\n", 576fdecf31bSYi Zou ft->name, netdev->name); 577fdecf31bSYi Zou goto out_putdev; 578fdecf31bSYi Zou } 579fdecf31bSYi Zou 580fdecf31bSYi Zou /* pass to transport create */ 581fdecf31bSYi Zou rc = ft->create ? ft->create(netdev, fip_mode) : -ENODEV; 582fdecf31bSYi Zou if (rc) 583fdecf31bSYi Zou fcoe_del_netdev_mapping(netdev); 584fdecf31bSYi Zou 585fdecf31bSYi Zou LIBFCOE_TRANSPORT_DBG("transport %s %s to create fcoe on %s.\n", 586fdecf31bSYi Zou ft->name, (rc) ? "failed" : "succeeded", 587fdecf31bSYi Zou netdev->name); 588fdecf31bSYi Zou 589fdecf31bSYi Zou out_putdev: 590fdecf31bSYi Zou dev_put(netdev); 591fdecf31bSYi Zou out_nodev: 592fdecf31bSYi Zou mutex_unlock(&ft_mutex); 593fdecf31bSYi Zou return rc; 594fdecf31bSYi Zou } 595fdecf31bSYi Zou 596fdecf31bSYi Zou /** 597fdecf31bSYi Zou * fcoe_transport_destroy() - Destroy a FCoE interface 598fdecf31bSYi Zou * @buffer: The name of the Ethernet interface to be destroyed 599fdecf31bSYi Zou * @kp: The associated kernel parameter 600fdecf31bSYi Zou * 601fdecf31bSYi Zou * Called from sysfs. This holds the ft_mutex while calling the 602fdecf31bSYi Zou * registered fcoe transport's destroy function. 603fdecf31bSYi Zou * 604fdecf31bSYi Zou * Returns: 0 for success 605fdecf31bSYi Zou */ 606fdecf31bSYi Zou static int fcoe_transport_destroy(const char *buffer, struct kernel_param *kp) 607fdecf31bSYi Zou { 608fdecf31bSYi Zou int rc = -ENODEV; 609fdecf31bSYi Zou struct net_device *netdev = NULL; 610fdecf31bSYi Zou struct fcoe_transport *ft = NULL; 611fdecf31bSYi Zou 612b3960afeSRobert Love mutex_lock(&ft_mutex); 613b3960afeSRobert Love 614fdecf31bSYi Zou netdev = fcoe_if_to_netdev(buffer); 615fdecf31bSYi Zou if (!netdev) { 616fdecf31bSYi Zou LIBFCOE_TRANSPORT_DBG("invalid device %s.\n", buffer); 617fdecf31bSYi Zou goto out_nodev; 618fdecf31bSYi Zou } 619fdecf31bSYi Zou 620fdecf31bSYi Zou ft = fcoe_netdev_map_lookup(netdev); 621fdecf31bSYi Zou if (!ft) { 622fdecf31bSYi Zou LIBFCOE_TRANSPORT_DBG("no FCoE transport found for %s.\n", 623fdecf31bSYi Zou netdev->name); 624fdecf31bSYi Zou goto out_putdev; 625fdecf31bSYi Zou } 626fdecf31bSYi Zou 627fdecf31bSYi Zou /* pass to transport destroy */ 628fdecf31bSYi Zou rc = ft->destroy ? ft->destroy(netdev) : -ENODEV; 629fdecf31bSYi Zou fcoe_del_netdev_mapping(netdev); 630fdecf31bSYi Zou LIBFCOE_TRANSPORT_DBG("transport %s %s to destroy fcoe on %s.\n", 631fdecf31bSYi Zou ft->name, (rc) ? "failed" : "succeeded", 632fdecf31bSYi Zou netdev->name); 633fdecf31bSYi Zou 634fdecf31bSYi Zou out_putdev: 635fdecf31bSYi Zou dev_put(netdev); 636fdecf31bSYi Zou out_nodev: 637fdecf31bSYi Zou mutex_unlock(&ft_mutex); 638fdecf31bSYi Zou return rc; 639fdecf31bSYi Zou } 640fdecf31bSYi Zou 641fdecf31bSYi Zou /** 642fdecf31bSYi Zou * fcoe_transport_disable() - Disables a FCoE interface 643fdecf31bSYi Zou * @buffer: The name of the Ethernet interface to be disabled 644fdecf31bSYi Zou * @kp: The associated kernel parameter 645fdecf31bSYi Zou * 646fdecf31bSYi Zou * Called from sysfs. 647fdecf31bSYi Zou * 648fdecf31bSYi Zou * Returns: 0 for success 649fdecf31bSYi Zou */ 650fdecf31bSYi Zou static int fcoe_transport_disable(const char *buffer, struct kernel_param *kp) 651fdecf31bSYi Zou { 652fdecf31bSYi Zou int rc = -ENODEV; 653fdecf31bSYi Zou struct net_device *netdev = NULL; 654fdecf31bSYi Zou struct fcoe_transport *ft = NULL; 655fdecf31bSYi Zou 656b3960afeSRobert Love mutex_lock(&ft_mutex); 657b3960afeSRobert Love 658fdecf31bSYi Zou netdev = fcoe_if_to_netdev(buffer); 659fdecf31bSYi Zou if (!netdev) 660fdecf31bSYi Zou goto out_nodev; 661fdecf31bSYi Zou 662fdecf31bSYi Zou ft = fcoe_netdev_map_lookup(netdev); 663fdecf31bSYi Zou if (!ft) 664fdecf31bSYi Zou goto out_putdev; 665fdecf31bSYi Zou 666fdecf31bSYi Zou rc = ft->disable ? ft->disable(netdev) : -ENODEV; 667fdecf31bSYi Zou 668fdecf31bSYi Zou out_putdev: 669fdecf31bSYi Zou dev_put(netdev); 670fdecf31bSYi Zou out_nodev: 671fdecf31bSYi Zou mutex_unlock(&ft_mutex); 672fdecf31bSYi Zou 673fdecf31bSYi Zou if (rc == -ERESTARTSYS) 674fdecf31bSYi Zou return restart_syscall(); 675fdecf31bSYi Zou else 676fdecf31bSYi Zou return rc; 677fdecf31bSYi Zou } 678fdecf31bSYi Zou 679fdecf31bSYi Zou /** 680fdecf31bSYi Zou * fcoe_transport_enable() - Enables a FCoE interface 681fdecf31bSYi Zou * @buffer: The name of the Ethernet interface to be enabled 682fdecf31bSYi Zou * @kp: The associated kernel parameter 683fdecf31bSYi Zou * 684fdecf31bSYi Zou * Called from sysfs. 685fdecf31bSYi Zou * 686fdecf31bSYi Zou * Returns: 0 for success 687fdecf31bSYi Zou */ 688fdecf31bSYi Zou static int fcoe_transport_enable(const char *buffer, struct kernel_param *kp) 689fdecf31bSYi Zou { 690fdecf31bSYi Zou int rc = -ENODEV; 691fdecf31bSYi Zou struct net_device *netdev = NULL; 692fdecf31bSYi Zou struct fcoe_transport *ft = NULL; 693fdecf31bSYi Zou 694b3960afeSRobert Love mutex_lock(&ft_mutex); 695b3960afeSRobert Love 696fdecf31bSYi Zou netdev = fcoe_if_to_netdev(buffer); 697fdecf31bSYi Zou if (!netdev) 698fdecf31bSYi Zou goto out_nodev; 699fdecf31bSYi Zou 700fdecf31bSYi Zou ft = fcoe_netdev_map_lookup(netdev); 701fdecf31bSYi Zou if (!ft) 702fdecf31bSYi Zou goto out_putdev; 703fdecf31bSYi Zou 704fdecf31bSYi Zou rc = ft->enable ? ft->enable(netdev) : -ENODEV; 705fdecf31bSYi Zou 706fdecf31bSYi Zou out_putdev: 707fdecf31bSYi Zou dev_put(netdev); 708fdecf31bSYi Zou out_nodev: 709fdecf31bSYi Zou mutex_unlock(&ft_mutex); 710fdecf31bSYi Zou return rc; 711fdecf31bSYi Zou } 712fdecf31bSYi Zou 713fdecf31bSYi Zou /** 714fdecf31bSYi Zou * libfcoe_init() - Initialization routine for libfcoe.ko 715fdecf31bSYi Zou */ 716fdecf31bSYi Zou static int __init libfcoe_init(void) 717fdecf31bSYi Zou { 718fdecf31bSYi Zou fcoe_transport_init(); 719fdecf31bSYi Zou 720fdecf31bSYi Zou return 0; 721fdecf31bSYi Zou } 722fdecf31bSYi Zou module_init(libfcoe_init); 723fdecf31bSYi Zou 724fdecf31bSYi Zou /** 725fdecf31bSYi Zou * libfcoe_exit() - Tear down libfcoe.ko 726fdecf31bSYi Zou */ 727fdecf31bSYi Zou static void __exit libfcoe_exit(void) 728fdecf31bSYi Zou { 729fdecf31bSYi Zou fcoe_transport_exit(); 730fdecf31bSYi Zou } 731fdecf31bSYi Zou module_exit(libfcoe_exit); 732