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"); 61bd0a1d6cSMasanari Iida MODULE_PARM_DESC(create, " Creates fcoe instance on an 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"); 71bd0a1d6cSMasanari Iida MODULE_PARM_DESC(destroy, " Destroys fcoe instance on an ethernet interface"); 72fdecf31bSYi Zou 73fdecf31bSYi Zou module_param_call(enable, fcoe_transport_enable, NULL, NULL, S_IWUSR); 74fdecf31bSYi Zou __MODULE_PARM_TYPE(enable, "string"); 75bd0a1d6cSMasanari Iida MODULE_PARM_DESC(enable, " Enables fcoe on an ethernet interface."); 76fdecf31bSYi Zou 77fdecf31bSYi Zou module_param_call(disable, fcoe_transport_disable, NULL, NULL, S_IWUSR); 78fdecf31bSYi Zou __MODULE_PARM_TYPE(disable, "string"); 79bd0a1d6cSMasanari Iida MODULE_PARM_DESC(disable, " Disables fcoe on an 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 860b924e55SJohannes Thumshirn static const struct { 870b924e55SJohannes Thumshirn u32 fc_port_speed; 880b924e55SJohannes Thumshirn #define SPEED_2000 2000 890b924e55SJohannes Thumshirn #define SPEED_4000 4000 900b924e55SJohannes Thumshirn #define SPEED_8000 8000 910b924e55SJohannes Thumshirn #define SPEED_16000 16000 920b924e55SJohannes Thumshirn #define SPEED_32000 32000 930b924e55SJohannes Thumshirn u32 eth_port_speed; 940b924e55SJohannes Thumshirn } fcoe_port_speed_mapping[] = { 950b924e55SJohannes Thumshirn { FC_PORTSPEED_1GBIT, SPEED_1000 }, 960b924e55SJohannes Thumshirn { FC_PORTSPEED_2GBIT, SPEED_2000 }, 970b924e55SJohannes Thumshirn { FC_PORTSPEED_4GBIT, SPEED_4000 }, 980b924e55SJohannes Thumshirn { FC_PORTSPEED_8GBIT, SPEED_8000 }, 990b924e55SJohannes Thumshirn { FC_PORTSPEED_10GBIT, SPEED_10000 }, 1000b924e55SJohannes Thumshirn { FC_PORTSPEED_16GBIT, SPEED_16000 }, 1010b924e55SJohannes Thumshirn { FC_PORTSPEED_20GBIT, SPEED_20000 }, 1020b924e55SJohannes Thumshirn { FC_PORTSPEED_25GBIT, SPEED_25000 }, 1030b924e55SJohannes Thumshirn { FC_PORTSPEED_32GBIT, SPEED_32000 }, 1040b924e55SJohannes Thumshirn { FC_PORTSPEED_40GBIT, SPEED_40000 }, 1050b924e55SJohannes Thumshirn { FC_PORTSPEED_50GBIT, SPEED_50000 }, 1060b924e55SJohannes Thumshirn { FC_PORTSPEED_100GBIT, SPEED_100000 }, 1070b924e55SJohannes Thumshirn }; 1080b924e55SJohannes Thumshirn 1090b924e55SJohannes Thumshirn static inline u32 eth2fc_speed(u32 eth_port_speed) 1100b924e55SJohannes Thumshirn { 1110b924e55SJohannes Thumshirn int i; 1120b924e55SJohannes Thumshirn 113182d3f84SVincent Stehlé for (i = 0; i < ARRAY_SIZE(fcoe_port_speed_mapping); i++) { 1140b924e55SJohannes Thumshirn if (fcoe_port_speed_mapping[i].eth_port_speed == eth_port_speed) 1150b924e55SJohannes Thumshirn return fcoe_port_speed_mapping[i].fc_port_speed; 1160b924e55SJohannes Thumshirn } 1170b924e55SJohannes Thumshirn 1180b924e55SJohannes Thumshirn return FC_PORTSPEED_UNKNOWN; 1190b924e55SJohannes Thumshirn } 1200b924e55SJohannes Thumshirn 12103702689SYi Zou /** 12203702689SYi Zou * fcoe_link_speed_update() - Update the supported and actual link speeds 12303702689SYi Zou * @lport: The local port to update speeds for 12403702689SYi Zou * 12503702689SYi Zou * Returns: 0 if the ethtool query was successful 12603702689SYi Zou * -1 if the ethtool query failed 12703702689SYi Zou */ 12803702689SYi Zou int fcoe_link_speed_update(struct fc_lport *lport) 12903702689SYi Zou { 13003702689SYi Zou struct net_device *netdev = fcoe_get_netdev(lport); 131008eb736SDavid Decotigny struct ethtool_link_ksettings ecmd; 13203702689SYi Zou 133008eb736SDavid Decotigny if (!__ethtool_get_link_ksettings(netdev, &ecmd)) { 134b8d23dc6SChris Leech lport->link_supported_speeds &= ~(FC_PORTSPEED_1GBIT | 135b8d23dc6SChris Leech FC_PORTSPEED_10GBIT | 136b8d23dc6SChris Leech FC_PORTSPEED_20GBIT | 137b8d23dc6SChris Leech FC_PORTSPEED_40GBIT); 138b8d23dc6SChris Leech 139008eb736SDavid Decotigny if (ecmd.link_modes.supported[0] & ( 140008eb736SDavid Decotigny SUPPORTED_1000baseT_Half | 141b8d23dc6SChris Leech SUPPORTED_1000baseT_Full | 142b8d23dc6SChris Leech SUPPORTED_1000baseKX_Full)) 14303702689SYi Zou lport->link_supported_speeds |= FC_PORTSPEED_1GBIT; 144b8d23dc6SChris Leech 145008eb736SDavid Decotigny if (ecmd.link_modes.supported[0] & ( 146008eb736SDavid Decotigny SUPPORTED_10000baseT_Full | 147b8d23dc6SChris Leech SUPPORTED_10000baseKX4_Full | 148b8d23dc6SChris Leech SUPPORTED_10000baseKR_Full | 149b8d23dc6SChris Leech SUPPORTED_10000baseR_FEC)) 150b8d23dc6SChris Leech lport->link_supported_speeds |= FC_PORTSPEED_10GBIT; 151b8d23dc6SChris Leech 152008eb736SDavid Decotigny if (ecmd.link_modes.supported[0] & ( 153008eb736SDavid Decotigny SUPPORTED_20000baseMLD2_Full | 154b8d23dc6SChris Leech SUPPORTED_20000baseKR2_Full)) 155b8d23dc6SChris Leech lport->link_supported_speeds |= FC_PORTSPEED_20GBIT; 156b8d23dc6SChris Leech 157008eb736SDavid Decotigny if (ecmd.link_modes.supported[0] & ( 158008eb736SDavid Decotigny SUPPORTED_40000baseKR4_Full | 159b8d23dc6SChris Leech SUPPORTED_40000baseCR4_Full | 160b8d23dc6SChris Leech SUPPORTED_40000baseSR4_Full | 161b8d23dc6SChris Leech SUPPORTED_40000baseLR4_Full)) 162b8d23dc6SChris Leech lport->link_supported_speeds |= FC_PORTSPEED_40GBIT; 163b8d23dc6SChris Leech 1640b924e55SJohannes Thumshirn lport->link_speed = eth2fc_speed(ecmd.base.speed); 16503702689SYi Zou return 0; 16603702689SYi Zou } 16703702689SYi Zou return -1; 16803702689SYi Zou } 16903702689SYi Zou EXPORT_SYMBOL_GPL(fcoe_link_speed_update); 17003702689SYi Zou 17157c2728fSYi Zou /** 17257c2728fSYi Zou * __fcoe_get_lesb() - Get the Link Error Status Block (LESB) for a given lport 17357c2728fSYi Zou * @lport: The local port to update speeds for 17457c2728fSYi Zou * @fc_lesb: Pointer to the LESB to be filled up 17557c2728fSYi Zou * @netdev: Pointer to the netdev that is associated with the lport 17657c2728fSYi Zou * 17757c2728fSYi Zou * Note, the Link Error Status Block (LESB) for FCoE is defined in FC-BB-6 17857c2728fSYi Zou * Clause 7.11 in v1.04. 17957c2728fSYi Zou */ 180814740d5SBhanu Prakash Gollapudi void __fcoe_get_lesb(struct fc_lport *lport, 181814740d5SBhanu Prakash Gollapudi struct fc_els_lesb *fc_lesb, 182814740d5SBhanu Prakash Gollapudi struct net_device *netdev) 183814740d5SBhanu Prakash Gollapudi { 184814740d5SBhanu Prakash Gollapudi unsigned int cpu; 185814740d5SBhanu Prakash Gollapudi u32 lfc, vlfc, mdac; 1861bd49b48SVasu Dev struct fc_stats *stats; 187814740d5SBhanu Prakash Gollapudi struct fcoe_fc_els_lesb *lesb; 188814740d5SBhanu Prakash Gollapudi struct rtnl_link_stats64 temp; 189814740d5SBhanu Prakash Gollapudi 190814740d5SBhanu Prakash Gollapudi lfc = 0; 191814740d5SBhanu Prakash Gollapudi vlfc = 0; 192814740d5SBhanu Prakash Gollapudi mdac = 0; 193814740d5SBhanu Prakash Gollapudi lesb = (struct fcoe_fc_els_lesb *)fc_lesb; 194814740d5SBhanu Prakash Gollapudi memset(lesb, 0, sizeof(*lesb)); 195814740d5SBhanu Prakash Gollapudi for_each_possible_cpu(cpu) { 1961bd49b48SVasu Dev stats = per_cpu_ptr(lport->stats, cpu); 1971bd49b48SVasu Dev lfc += stats->LinkFailureCount; 1981bd49b48SVasu Dev vlfc += stats->VLinkFailureCount; 1991bd49b48SVasu Dev mdac += stats->MissDiscAdvCount; 200814740d5SBhanu Prakash Gollapudi } 201814740d5SBhanu Prakash Gollapudi lesb->lesb_link_fail = htonl(lfc); 202814740d5SBhanu Prakash Gollapudi lesb->lesb_vlink_fail = htonl(vlfc); 203814740d5SBhanu Prakash Gollapudi lesb->lesb_miss_fka = htonl(mdac); 204814740d5SBhanu Prakash Gollapudi lesb->lesb_fcs_error = 205814740d5SBhanu Prakash Gollapudi htonl(dev_get_stats(netdev, &temp)->rx_crc_errors); 206814740d5SBhanu Prakash Gollapudi } 207814740d5SBhanu Prakash Gollapudi EXPORT_SYMBOL_GPL(__fcoe_get_lesb); 208814740d5SBhanu Prakash Gollapudi 20957c2728fSYi Zou /** 21057c2728fSYi Zou * fcoe_get_lesb() - Fill the FCoE Link Error Status Block 21157c2728fSYi Zou * @lport: the local port 21257c2728fSYi Zou * @fc_lesb: the link error status block 21357c2728fSYi Zou */ 21457c2728fSYi Zou void fcoe_get_lesb(struct fc_lport *lport, 21557c2728fSYi Zou struct fc_els_lesb *fc_lesb) 21657c2728fSYi Zou { 21757c2728fSYi Zou struct net_device *netdev = fcoe_get_netdev(lport); 21857c2728fSYi Zou 21957c2728fSYi Zou __fcoe_get_lesb(lport, fc_lesb, netdev); 22057c2728fSYi Zou } 22157c2728fSYi Zou EXPORT_SYMBOL_GPL(fcoe_get_lesb); 22257c2728fSYi Zou 22357c2728fSYi Zou /** 22457c2728fSYi Zou * fcoe_ctlr_get_lesb() - Get the Link Error Status Block (LESB) for a given 22557c2728fSYi Zou * fcoe controller device 22657c2728fSYi Zou * @ctlr_dev: The given fcoe controller device 22757c2728fSYi Zou * 22857c2728fSYi Zou */ 22957c2728fSYi Zou void fcoe_ctlr_get_lesb(struct fcoe_ctlr_device *ctlr_dev) 23057c2728fSYi Zou { 23157c2728fSYi Zou struct fcoe_ctlr *fip = fcoe_ctlr_device_priv(ctlr_dev); 23257c2728fSYi Zou struct net_device *netdev = fcoe_get_netdev(fip->lp); 233418a8cfeSYi Zou struct fc_els_lesb *fc_lesb; 23457c2728fSYi Zou 235418a8cfeSYi Zou fc_lesb = (struct fc_els_lesb *)(&ctlr_dev->lesb); 236418a8cfeSYi Zou __fcoe_get_lesb(fip->lp, fc_lesb, netdev); 23757c2728fSYi Zou } 23857c2728fSYi Zou EXPORT_SYMBOL_GPL(fcoe_ctlr_get_lesb); 23957c2728fSYi Zou 240d834895cSBhanu Prakash Gollapudi void fcoe_wwn_to_str(u64 wwn, char *buf, int len) 241d834895cSBhanu Prakash Gollapudi { 242d834895cSBhanu Prakash Gollapudi u8 wwpn[8]; 243d834895cSBhanu Prakash Gollapudi 244d834895cSBhanu Prakash Gollapudi u64_to_wwn(wwn, wwpn); 245d834895cSBhanu Prakash Gollapudi snprintf(buf, len, "%02x%02x%02x%02x%02x%02x%02x%02x", 246d834895cSBhanu Prakash Gollapudi wwpn[0], wwpn[1], wwpn[2], wwpn[3], 247d834895cSBhanu Prakash Gollapudi wwpn[4], wwpn[5], wwpn[6], wwpn[7]); 248d834895cSBhanu Prakash Gollapudi } 249d834895cSBhanu Prakash Gollapudi EXPORT_SYMBOL_GPL(fcoe_wwn_to_str); 250d834895cSBhanu Prakash Gollapudi 251d834895cSBhanu Prakash Gollapudi /** 252d834895cSBhanu Prakash Gollapudi * fcoe_validate_vport_create() - Validate a vport before creating it 253d834895cSBhanu Prakash Gollapudi * @vport: NPIV port to be created 254d834895cSBhanu Prakash Gollapudi * 255d834895cSBhanu Prakash Gollapudi * This routine is meant to add validation for a vport before creating it 256d834895cSBhanu Prakash Gollapudi * via fcoe_vport_create(). 257d834895cSBhanu Prakash Gollapudi * Current validations are: 258d834895cSBhanu Prakash Gollapudi * - WWPN supplied is unique for given lport 259d834895cSBhanu Prakash Gollapudi */ 260d834895cSBhanu Prakash Gollapudi int fcoe_validate_vport_create(struct fc_vport *vport) 261d834895cSBhanu Prakash Gollapudi { 262d834895cSBhanu Prakash Gollapudi struct Scsi_Host *shost = vport_to_shost(vport); 263d834895cSBhanu Prakash Gollapudi struct fc_lport *n_port = shost_priv(shost); 264d834895cSBhanu Prakash Gollapudi struct fc_lport *vn_port; 265d834895cSBhanu Prakash Gollapudi int rc = 0; 266d834895cSBhanu Prakash Gollapudi char buf[32]; 267d834895cSBhanu Prakash Gollapudi 268d834895cSBhanu Prakash Gollapudi mutex_lock(&n_port->lp_mutex); 269d834895cSBhanu Prakash Gollapudi 270d834895cSBhanu Prakash Gollapudi fcoe_wwn_to_str(vport->port_name, buf, sizeof(buf)); 271d834895cSBhanu Prakash Gollapudi /* Check if the wwpn is not same as that of the lport */ 272d834895cSBhanu Prakash Gollapudi if (!memcmp(&n_port->wwpn, &vport->port_name, sizeof(u64))) { 273d834895cSBhanu Prakash Gollapudi LIBFCOE_TRANSPORT_DBG("vport WWPN 0x%s is same as that of the " 274d834895cSBhanu Prakash Gollapudi "base port WWPN\n", buf); 275d834895cSBhanu Prakash Gollapudi rc = -EINVAL; 276d834895cSBhanu Prakash Gollapudi goto out; 277d834895cSBhanu Prakash Gollapudi } 278d834895cSBhanu Prakash Gollapudi 279d834895cSBhanu Prakash Gollapudi /* Check if there is any existing vport with same wwpn */ 280d834895cSBhanu Prakash Gollapudi list_for_each_entry(vn_port, &n_port->vports, list) { 281d834895cSBhanu Prakash Gollapudi if (!memcmp(&vn_port->wwpn, &vport->port_name, sizeof(u64))) { 282d834895cSBhanu Prakash Gollapudi LIBFCOE_TRANSPORT_DBG("vport with given WWPN 0x%s " 283d834895cSBhanu Prakash Gollapudi "already exists\n", buf); 284d834895cSBhanu Prakash Gollapudi rc = -EINVAL; 285d834895cSBhanu Prakash Gollapudi break; 286d834895cSBhanu Prakash Gollapudi } 287d834895cSBhanu Prakash Gollapudi } 288d834895cSBhanu Prakash Gollapudi out: 289d834895cSBhanu Prakash Gollapudi mutex_unlock(&n_port->lp_mutex); 290d834895cSBhanu Prakash Gollapudi return rc; 291d834895cSBhanu Prakash Gollapudi } 292d834895cSBhanu Prakash Gollapudi EXPORT_SYMBOL_GPL(fcoe_validate_vport_create); 293d834895cSBhanu Prakash Gollapudi 294d834895cSBhanu Prakash Gollapudi /** 295d834895cSBhanu Prakash Gollapudi * fcoe_get_wwn() - Get the world wide name from LLD if it supports it 296d834895cSBhanu Prakash Gollapudi * @netdev: the associated net device 297d834895cSBhanu Prakash Gollapudi * @wwn: the output WWN 298d834895cSBhanu Prakash Gollapudi * @type: the type of WWN (WWPN or WWNN) 299d834895cSBhanu Prakash Gollapudi * 300d834895cSBhanu Prakash Gollapudi * Returns: 0 for success 301d834895cSBhanu Prakash Gollapudi */ 302d834895cSBhanu Prakash Gollapudi int fcoe_get_wwn(struct net_device *netdev, u64 *wwn, int type) 303d834895cSBhanu Prakash Gollapudi { 304d834895cSBhanu Prakash Gollapudi const struct net_device_ops *ops = netdev->netdev_ops; 305d834895cSBhanu Prakash Gollapudi 306d834895cSBhanu Prakash Gollapudi if (ops->ndo_fcoe_get_wwn) 307d834895cSBhanu Prakash Gollapudi return ops->ndo_fcoe_get_wwn(netdev, wwn, type); 308d834895cSBhanu Prakash Gollapudi return -EINVAL; 309d834895cSBhanu Prakash Gollapudi } 310d834895cSBhanu Prakash Gollapudi EXPORT_SYMBOL_GPL(fcoe_get_wwn); 311d834895cSBhanu Prakash Gollapudi 312fdecf31bSYi Zou /** 3138597ae8bSBhanu Prakash Gollapudi * fcoe_fc_crc() - Calculates the CRC for a given frame 3148597ae8bSBhanu Prakash Gollapudi * @fp: The frame to be checksumed 3158597ae8bSBhanu Prakash Gollapudi * 3168597ae8bSBhanu Prakash Gollapudi * This uses crc32() routine to calculate the CRC for a frame 3178597ae8bSBhanu Prakash Gollapudi * 3188597ae8bSBhanu Prakash Gollapudi * Return: The 32 bit CRC value 3198597ae8bSBhanu Prakash Gollapudi */ 3208597ae8bSBhanu Prakash Gollapudi u32 fcoe_fc_crc(struct fc_frame *fp) 3218597ae8bSBhanu Prakash Gollapudi { 3228597ae8bSBhanu Prakash Gollapudi struct sk_buff *skb = fp_skb(fp); 3238597ae8bSBhanu Prakash Gollapudi struct skb_frag_struct *frag; 3248597ae8bSBhanu Prakash Gollapudi unsigned char *data; 3258597ae8bSBhanu Prakash Gollapudi unsigned long off, len, clen; 3268597ae8bSBhanu Prakash Gollapudi u32 crc; 3278597ae8bSBhanu Prakash Gollapudi unsigned i; 3288597ae8bSBhanu Prakash Gollapudi 3298597ae8bSBhanu Prakash Gollapudi crc = crc32(~0, skb->data, skb_headlen(skb)); 3308597ae8bSBhanu Prakash Gollapudi 3318597ae8bSBhanu Prakash Gollapudi for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { 3328597ae8bSBhanu Prakash Gollapudi frag = &skb_shinfo(skb)->frags[i]; 3338597ae8bSBhanu Prakash Gollapudi off = frag->page_offset; 3349e903e08SEric Dumazet len = skb_frag_size(frag); 3358597ae8bSBhanu Prakash Gollapudi while (len > 0) { 3368597ae8bSBhanu Prakash Gollapudi clen = min(len, PAGE_SIZE - (off & ~PAGE_MASK)); 337165c68d5SIan Campbell data = kmap_atomic( 33877dfce07SCong Wang skb_frag_page(frag) + (off >> PAGE_SHIFT)); 3398597ae8bSBhanu Prakash Gollapudi crc = crc32(crc, data + (off & ~PAGE_MASK), clen); 34077dfce07SCong Wang kunmap_atomic(data); 3418597ae8bSBhanu Prakash Gollapudi off += clen; 3428597ae8bSBhanu Prakash Gollapudi len -= clen; 3438597ae8bSBhanu Prakash Gollapudi } 3448597ae8bSBhanu Prakash Gollapudi } 3458597ae8bSBhanu Prakash Gollapudi return crc; 3468597ae8bSBhanu Prakash Gollapudi } 3478597ae8bSBhanu Prakash Gollapudi EXPORT_SYMBOL_GPL(fcoe_fc_crc); 3488597ae8bSBhanu Prakash Gollapudi 3498597ae8bSBhanu Prakash Gollapudi /** 3508597ae8bSBhanu Prakash Gollapudi * fcoe_start_io() - Start FCoE I/O 3518597ae8bSBhanu Prakash Gollapudi * @skb: The packet to be transmitted 3528597ae8bSBhanu Prakash Gollapudi * 3538597ae8bSBhanu Prakash Gollapudi * This routine is called from the net device to start transmitting 3548597ae8bSBhanu Prakash Gollapudi * FCoE packets. 3558597ae8bSBhanu Prakash Gollapudi * 3568597ae8bSBhanu Prakash Gollapudi * Returns: 0 for success 3578597ae8bSBhanu Prakash Gollapudi */ 3588597ae8bSBhanu Prakash Gollapudi int fcoe_start_io(struct sk_buff *skb) 3598597ae8bSBhanu Prakash Gollapudi { 3608597ae8bSBhanu Prakash Gollapudi struct sk_buff *nskb; 3618597ae8bSBhanu Prakash Gollapudi int rc; 3628597ae8bSBhanu Prakash Gollapudi 3638597ae8bSBhanu Prakash Gollapudi nskb = skb_clone(skb, GFP_ATOMIC); 3648597ae8bSBhanu Prakash Gollapudi if (!nskb) 3658597ae8bSBhanu Prakash Gollapudi return -ENOMEM; 3668597ae8bSBhanu Prakash Gollapudi rc = dev_queue_xmit(nskb); 3678597ae8bSBhanu Prakash Gollapudi if (rc != 0) 3688597ae8bSBhanu Prakash Gollapudi return rc; 3698597ae8bSBhanu Prakash Gollapudi kfree_skb(skb); 3708597ae8bSBhanu Prakash Gollapudi return 0; 3718597ae8bSBhanu Prakash Gollapudi } 3728597ae8bSBhanu Prakash Gollapudi EXPORT_SYMBOL_GPL(fcoe_start_io); 3738597ae8bSBhanu Prakash Gollapudi 3748597ae8bSBhanu Prakash Gollapudi 3758597ae8bSBhanu Prakash Gollapudi /** 3768597ae8bSBhanu Prakash Gollapudi * fcoe_clean_pending_queue() - Dequeue a skb and free it 3778597ae8bSBhanu Prakash Gollapudi * @lport: The local port to dequeue a skb on 3788597ae8bSBhanu Prakash Gollapudi */ 3798597ae8bSBhanu Prakash Gollapudi void fcoe_clean_pending_queue(struct fc_lport *lport) 3808597ae8bSBhanu Prakash Gollapudi { 3818597ae8bSBhanu Prakash Gollapudi struct fcoe_port *port = lport_priv(lport); 3828597ae8bSBhanu Prakash Gollapudi struct sk_buff *skb; 3838597ae8bSBhanu Prakash Gollapudi 3848597ae8bSBhanu Prakash Gollapudi spin_lock_bh(&port->fcoe_pending_queue.lock); 3858597ae8bSBhanu Prakash Gollapudi while ((skb = __skb_dequeue(&port->fcoe_pending_queue)) != NULL) { 3868597ae8bSBhanu Prakash Gollapudi spin_unlock_bh(&port->fcoe_pending_queue.lock); 3878597ae8bSBhanu Prakash Gollapudi kfree_skb(skb); 3888597ae8bSBhanu Prakash Gollapudi spin_lock_bh(&port->fcoe_pending_queue.lock); 3898597ae8bSBhanu Prakash Gollapudi } 3908597ae8bSBhanu Prakash Gollapudi spin_unlock_bh(&port->fcoe_pending_queue.lock); 3918597ae8bSBhanu Prakash Gollapudi } 3928597ae8bSBhanu Prakash Gollapudi EXPORT_SYMBOL_GPL(fcoe_clean_pending_queue); 3938597ae8bSBhanu Prakash Gollapudi 3948597ae8bSBhanu Prakash Gollapudi /** 3958597ae8bSBhanu Prakash Gollapudi * fcoe_check_wait_queue() - Attempt to clear the transmit backlog 3968597ae8bSBhanu Prakash Gollapudi * @lport: The local port whose backlog is to be cleared 3978597ae8bSBhanu Prakash Gollapudi * 3988597ae8bSBhanu Prakash Gollapudi * This empties the wait_queue, dequeues the head of the wait_queue queue 3998597ae8bSBhanu Prakash Gollapudi * and calls fcoe_start_io() for each packet. If all skb have been 4008597ae8bSBhanu Prakash Gollapudi * transmitted it returns the qlen. If an error occurs it restores 4018597ae8bSBhanu Prakash Gollapudi * wait_queue (to try again later) and returns -1. 4028597ae8bSBhanu Prakash Gollapudi * 4038597ae8bSBhanu Prakash Gollapudi * The wait_queue is used when the skb transmit fails. The failed skb 4048597ae8bSBhanu Prakash Gollapudi * will go in the wait_queue which will be emptied by the timer function or 4058597ae8bSBhanu Prakash Gollapudi * by the next skb transmit. 4068597ae8bSBhanu Prakash Gollapudi */ 4078597ae8bSBhanu Prakash Gollapudi void fcoe_check_wait_queue(struct fc_lport *lport, struct sk_buff *skb) 4088597ae8bSBhanu Prakash Gollapudi { 4098597ae8bSBhanu Prakash Gollapudi struct fcoe_port *port = lport_priv(lport); 4108597ae8bSBhanu Prakash Gollapudi int rc; 4118597ae8bSBhanu Prakash Gollapudi 4128597ae8bSBhanu Prakash Gollapudi spin_lock_bh(&port->fcoe_pending_queue.lock); 4138597ae8bSBhanu Prakash Gollapudi 4148597ae8bSBhanu Prakash Gollapudi if (skb) 4158597ae8bSBhanu Prakash Gollapudi __skb_queue_tail(&port->fcoe_pending_queue, skb); 4168597ae8bSBhanu Prakash Gollapudi 4178597ae8bSBhanu Prakash Gollapudi if (port->fcoe_pending_queue_active) 4188597ae8bSBhanu Prakash Gollapudi goto out; 4198597ae8bSBhanu Prakash Gollapudi port->fcoe_pending_queue_active = 1; 4208597ae8bSBhanu Prakash Gollapudi 4218597ae8bSBhanu Prakash Gollapudi while (port->fcoe_pending_queue.qlen) { 4228597ae8bSBhanu Prakash Gollapudi /* keep qlen > 0 until fcoe_start_io succeeds */ 4238597ae8bSBhanu Prakash Gollapudi port->fcoe_pending_queue.qlen++; 4248597ae8bSBhanu Prakash Gollapudi skb = __skb_dequeue(&port->fcoe_pending_queue); 4258597ae8bSBhanu Prakash Gollapudi 4268597ae8bSBhanu Prakash Gollapudi spin_unlock_bh(&port->fcoe_pending_queue.lock); 4278597ae8bSBhanu Prakash Gollapudi rc = fcoe_start_io(skb); 4288597ae8bSBhanu Prakash Gollapudi spin_lock_bh(&port->fcoe_pending_queue.lock); 4298597ae8bSBhanu Prakash Gollapudi 4308597ae8bSBhanu Prakash Gollapudi if (rc) { 4318597ae8bSBhanu Prakash Gollapudi __skb_queue_head(&port->fcoe_pending_queue, skb); 4328597ae8bSBhanu Prakash Gollapudi /* undo temporary increment above */ 4338597ae8bSBhanu Prakash Gollapudi port->fcoe_pending_queue.qlen--; 4348597ae8bSBhanu Prakash Gollapudi break; 4358597ae8bSBhanu Prakash Gollapudi } 4368597ae8bSBhanu Prakash Gollapudi /* undo temporary increment above */ 4378597ae8bSBhanu Prakash Gollapudi port->fcoe_pending_queue.qlen--; 4388597ae8bSBhanu Prakash Gollapudi } 4398597ae8bSBhanu Prakash Gollapudi 4408597ae8bSBhanu Prakash Gollapudi if (port->fcoe_pending_queue.qlen < port->min_queue_depth) 4418597ae8bSBhanu Prakash Gollapudi lport->qfull = 0; 4428597ae8bSBhanu Prakash Gollapudi if (port->fcoe_pending_queue.qlen && !timer_pending(&port->timer)) 4438597ae8bSBhanu Prakash Gollapudi mod_timer(&port->timer, jiffies + 2); 4448597ae8bSBhanu Prakash Gollapudi port->fcoe_pending_queue_active = 0; 4458597ae8bSBhanu Prakash Gollapudi out: 4468597ae8bSBhanu Prakash Gollapudi if (port->fcoe_pending_queue.qlen > port->max_queue_depth) 4478597ae8bSBhanu Prakash Gollapudi lport->qfull = 1; 4488597ae8bSBhanu Prakash Gollapudi spin_unlock_bh(&port->fcoe_pending_queue.lock); 4498597ae8bSBhanu Prakash Gollapudi } 4508597ae8bSBhanu Prakash Gollapudi EXPORT_SYMBOL_GPL(fcoe_check_wait_queue); 4518597ae8bSBhanu Prakash Gollapudi 4528597ae8bSBhanu Prakash Gollapudi /** 4538597ae8bSBhanu Prakash Gollapudi * fcoe_queue_timer() - The fcoe queue timer 4548597ae8bSBhanu Prakash Gollapudi * @lport: The local port 4558597ae8bSBhanu Prakash Gollapudi * 4568597ae8bSBhanu Prakash Gollapudi * Calls fcoe_check_wait_queue on timeout 4578597ae8bSBhanu Prakash Gollapudi */ 4588597ae8bSBhanu Prakash Gollapudi void fcoe_queue_timer(ulong lport) 4598597ae8bSBhanu Prakash Gollapudi { 4608597ae8bSBhanu Prakash Gollapudi fcoe_check_wait_queue((struct fc_lport *)lport, NULL); 4618597ae8bSBhanu Prakash Gollapudi } 4628597ae8bSBhanu Prakash Gollapudi EXPORT_SYMBOL_GPL(fcoe_queue_timer); 4638597ae8bSBhanu Prakash Gollapudi 4648597ae8bSBhanu Prakash Gollapudi /** 4658597ae8bSBhanu Prakash Gollapudi * fcoe_get_paged_crc_eof() - Allocate a page to be used for the trailer CRC 4668597ae8bSBhanu Prakash Gollapudi * @skb: The packet to be transmitted 4678597ae8bSBhanu Prakash Gollapudi * @tlen: The total length of the trailer 4688597ae8bSBhanu Prakash Gollapudi * @fps: The fcoe context 4698597ae8bSBhanu Prakash Gollapudi * 4708597ae8bSBhanu Prakash Gollapudi * This routine allocates a page for frame trailers. The page is re-used if 4718597ae8bSBhanu Prakash Gollapudi * there is enough room left on it for the current trailer. If there isn't 4728597ae8bSBhanu Prakash Gollapudi * enough buffer left a new page is allocated for the trailer. Reference to 4738597ae8bSBhanu Prakash Gollapudi * the page from this function as well as the skbs using the page fragments 4748597ae8bSBhanu Prakash Gollapudi * ensure that the page is freed at the appropriate time. 4758597ae8bSBhanu Prakash Gollapudi * 4768597ae8bSBhanu Prakash Gollapudi * Returns: 0 for success 4778597ae8bSBhanu Prakash Gollapudi */ 4788597ae8bSBhanu Prakash Gollapudi int fcoe_get_paged_crc_eof(struct sk_buff *skb, int tlen, 4798597ae8bSBhanu Prakash Gollapudi struct fcoe_percpu_s *fps) 4808597ae8bSBhanu Prakash Gollapudi { 4818597ae8bSBhanu Prakash Gollapudi struct page *page; 4828597ae8bSBhanu Prakash Gollapudi 4838597ae8bSBhanu Prakash Gollapudi page = fps->crc_eof_page; 4848597ae8bSBhanu Prakash Gollapudi if (!page) { 4858597ae8bSBhanu Prakash Gollapudi page = alloc_page(GFP_ATOMIC); 4868597ae8bSBhanu Prakash Gollapudi if (!page) 4878597ae8bSBhanu Prakash Gollapudi return -ENOMEM; 4888597ae8bSBhanu Prakash Gollapudi 4898597ae8bSBhanu Prakash Gollapudi fps->crc_eof_page = page; 4908597ae8bSBhanu Prakash Gollapudi fps->crc_eof_offset = 0; 4918597ae8bSBhanu Prakash Gollapudi } 4928597ae8bSBhanu Prakash Gollapudi 4938597ae8bSBhanu Prakash Gollapudi get_page(page); 4948597ae8bSBhanu Prakash Gollapudi skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags, page, 4958597ae8bSBhanu Prakash Gollapudi fps->crc_eof_offset, tlen); 4968597ae8bSBhanu Prakash Gollapudi skb->len += tlen; 4978597ae8bSBhanu Prakash Gollapudi skb->data_len += tlen; 4988597ae8bSBhanu Prakash Gollapudi skb->truesize += tlen; 4998597ae8bSBhanu Prakash Gollapudi fps->crc_eof_offset += sizeof(struct fcoe_crc_eof); 5008597ae8bSBhanu Prakash Gollapudi 5018597ae8bSBhanu Prakash Gollapudi if (fps->crc_eof_offset >= PAGE_SIZE) { 5028597ae8bSBhanu Prakash Gollapudi fps->crc_eof_page = NULL; 5038597ae8bSBhanu Prakash Gollapudi fps->crc_eof_offset = 0; 5048597ae8bSBhanu Prakash Gollapudi put_page(page); 5058597ae8bSBhanu Prakash Gollapudi } 5068597ae8bSBhanu Prakash Gollapudi 5078597ae8bSBhanu Prakash Gollapudi return 0; 5088597ae8bSBhanu Prakash Gollapudi } 5098597ae8bSBhanu Prakash Gollapudi EXPORT_SYMBOL_GPL(fcoe_get_paged_crc_eof); 5108597ae8bSBhanu Prakash Gollapudi 5118597ae8bSBhanu Prakash Gollapudi /** 512fdecf31bSYi Zou * fcoe_transport_lookup - find an fcoe transport that matches a netdev 513fdecf31bSYi Zou * @netdev: The netdev to look for from all attached transports 514fdecf31bSYi Zou * 515fdecf31bSYi Zou * Returns : ptr to the fcoe transport that supports this netdev or NULL 516fdecf31bSYi Zou * if not found. 517fdecf31bSYi Zou * 518fdecf31bSYi Zou * The ft_mutex should be held when this is called 519fdecf31bSYi Zou */ 520fdecf31bSYi Zou static struct fcoe_transport *fcoe_transport_lookup(struct net_device *netdev) 521fdecf31bSYi Zou { 522fdecf31bSYi Zou struct fcoe_transport *ft = NULL; 523fdecf31bSYi Zou 524fdecf31bSYi Zou list_for_each_entry(ft, &fcoe_transports, list) 525fdecf31bSYi Zou if (ft->match && ft->match(netdev)) 526fdecf31bSYi Zou return ft; 527fdecf31bSYi Zou return NULL; 528fdecf31bSYi Zou } 529fdecf31bSYi Zou 530fdecf31bSYi Zou /** 531fdecf31bSYi Zou * fcoe_transport_attach - Attaches an FCoE transport 532fdecf31bSYi Zou * @ft: The fcoe transport to be attached 533fdecf31bSYi Zou * 534fdecf31bSYi Zou * Returns : 0 for success 535fdecf31bSYi Zou */ 536fdecf31bSYi Zou int fcoe_transport_attach(struct fcoe_transport *ft) 537fdecf31bSYi Zou { 538fdecf31bSYi Zou int rc = 0; 539fdecf31bSYi Zou 540fdecf31bSYi Zou mutex_lock(&ft_mutex); 541fdecf31bSYi Zou if (ft->attached) { 542fdecf31bSYi Zou LIBFCOE_TRANSPORT_DBG("transport %s already attached\n", 543fdecf31bSYi Zou ft->name); 544fdecf31bSYi Zou rc = -EEXIST; 545fdecf31bSYi Zou goto out_attach; 546fdecf31bSYi Zou } 547fdecf31bSYi Zou 548fdecf31bSYi Zou /* Add default transport to the tail */ 549fdecf31bSYi Zou if (strcmp(ft->name, FCOE_TRANSPORT_DEFAULT)) 550fdecf31bSYi Zou list_add(&ft->list, &fcoe_transports); 551fdecf31bSYi Zou else 552fdecf31bSYi Zou list_add_tail(&ft->list, &fcoe_transports); 553fdecf31bSYi Zou 554fdecf31bSYi Zou ft->attached = true; 555fdecf31bSYi Zou LIBFCOE_TRANSPORT_DBG("attaching transport %s\n", ft->name); 556fdecf31bSYi Zou 557fdecf31bSYi Zou out_attach: 558fdecf31bSYi Zou mutex_unlock(&ft_mutex); 559fdecf31bSYi Zou return rc; 560fdecf31bSYi Zou } 561fdecf31bSYi Zou EXPORT_SYMBOL(fcoe_transport_attach); 562fdecf31bSYi Zou 563fdecf31bSYi Zou /** 5644ef7fb15SYi Zou * fcoe_transport_detach - Detaches an FCoE transport 565fdecf31bSYi Zou * @ft: The fcoe transport to be attached 566fdecf31bSYi Zou * 567fdecf31bSYi Zou * Returns : 0 for success 568fdecf31bSYi Zou */ 569fdecf31bSYi Zou int fcoe_transport_detach(struct fcoe_transport *ft) 570fdecf31bSYi Zou { 571fdecf31bSYi Zou int rc = 0; 57269922fcdSYi Zou struct fcoe_netdev_mapping *nm = NULL, *tmp; 573fdecf31bSYi Zou 574fdecf31bSYi Zou mutex_lock(&ft_mutex); 575fdecf31bSYi Zou if (!ft->attached) { 576fdecf31bSYi Zou LIBFCOE_TRANSPORT_DBG("transport %s already detached\n", 577fdecf31bSYi Zou ft->name); 578fdecf31bSYi Zou rc = -ENODEV; 579fdecf31bSYi Zou goto out_attach; 580fdecf31bSYi Zou } 581fdecf31bSYi Zou 58269922fcdSYi Zou /* remove netdev mapping for this transport as it is going away */ 58369922fcdSYi Zou mutex_lock(&fn_mutex); 58469922fcdSYi Zou list_for_each_entry_safe(nm, tmp, &fcoe_netdevs, list) { 58569922fcdSYi Zou if (nm->ft == ft) { 58669922fcdSYi Zou LIBFCOE_TRANSPORT_DBG("transport %s going away, " 58769922fcdSYi Zou "remove its netdev mapping for %s\n", 58869922fcdSYi Zou ft->name, nm->netdev->name); 58969922fcdSYi Zou list_del(&nm->list); 59069922fcdSYi Zou kfree(nm); 59169922fcdSYi Zou } 59269922fcdSYi Zou } 59369922fcdSYi Zou mutex_unlock(&fn_mutex); 59469922fcdSYi Zou 595fdecf31bSYi Zou list_del(&ft->list); 596fdecf31bSYi Zou ft->attached = false; 597fdecf31bSYi Zou LIBFCOE_TRANSPORT_DBG("detaching transport %s\n", ft->name); 598fdecf31bSYi Zou 599fdecf31bSYi Zou out_attach: 600fdecf31bSYi Zou mutex_unlock(&ft_mutex); 601fdecf31bSYi Zou return rc; 602fdecf31bSYi Zou 603fdecf31bSYi Zou } 604fdecf31bSYi Zou EXPORT_SYMBOL(fcoe_transport_detach); 605fdecf31bSYi Zou 606fdecf31bSYi Zou static int fcoe_transport_show(char *buffer, const struct kernel_param *kp) 607fdecf31bSYi Zou { 608fdecf31bSYi Zou int i, j; 609fdecf31bSYi Zou struct fcoe_transport *ft = NULL; 610fdecf31bSYi Zou 611fdecf31bSYi Zou i = j = sprintf(buffer, "Attached FCoE transports:"); 612fdecf31bSYi Zou mutex_lock(&ft_mutex); 613fdecf31bSYi Zou list_for_each_entry(ft, &fcoe_transports, list) { 614a01a5a57SYi Zou if (i >= PAGE_SIZE - IFNAMSIZ) 615fdecf31bSYi Zou break; 616a01a5a57SYi Zou i += snprintf(&buffer[i], IFNAMSIZ, "%s ", ft->name); 617fdecf31bSYi Zou } 618fdecf31bSYi Zou mutex_unlock(&ft_mutex); 619fdecf31bSYi Zou if (i == j) 620fdecf31bSYi Zou i += snprintf(&buffer[i], IFNAMSIZ, "none"); 621fdecf31bSYi Zou return i; 622fdecf31bSYi Zou } 623fdecf31bSYi Zou 624fdecf31bSYi Zou static int __init fcoe_transport_init(void) 625fdecf31bSYi Zou { 62670be6344SBhanu Prakash Gollapudi register_netdevice_notifier(&libfcoe_notifier); 627fdecf31bSYi Zou return 0; 628fdecf31bSYi Zou } 629fdecf31bSYi Zou 63087098bddSMark Rustad static int fcoe_transport_exit(void) 631fdecf31bSYi Zou { 632fdecf31bSYi Zou struct fcoe_transport *ft; 633fdecf31bSYi Zou 63470be6344SBhanu Prakash Gollapudi unregister_netdevice_notifier(&libfcoe_notifier); 635fdecf31bSYi Zou mutex_lock(&ft_mutex); 636fdecf31bSYi Zou list_for_each_entry(ft, &fcoe_transports, list) 637fdecf31bSYi Zou printk(KERN_ERR "FCoE transport %s is still attached!\n", 638fdecf31bSYi Zou ft->name); 639fdecf31bSYi Zou mutex_unlock(&ft_mutex); 640fdecf31bSYi Zou return 0; 641fdecf31bSYi Zou } 642fdecf31bSYi Zou 643fdecf31bSYi Zou 644fdecf31bSYi Zou static int fcoe_add_netdev_mapping(struct net_device *netdev, 645fdecf31bSYi Zou struct fcoe_transport *ft) 646fdecf31bSYi Zou { 647fdecf31bSYi Zou struct fcoe_netdev_mapping *nm; 648fdecf31bSYi Zou 649fdecf31bSYi Zou nm = kmalloc(sizeof(*nm), GFP_KERNEL); 650fdecf31bSYi Zou if (!nm) { 651fdecf31bSYi Zou printk(KERN_ERR "Unable to allocate netdev_mapping"); 652fdecf31bSYi Zou return -ENOMEM; 653fdecf31bSYi Zou } 654fdecf31bSYi Zou 655fdecf31bSYi Zou nm->netdev = netdev; 656fdecf31bSYi Zou nm->ft = ft; 657fdecf31bSYi Zou 65870be6344SBhanu Prakash Gollapudi mutex_lock(&fn_mutex); 659fdecf31bSYi Zou list_add(&nm->list, &fcoe_netdevs); 66070be6344SBhanu Prakash Gollapudi mutex_unlock(&fn_mutex); 661fdecf31bSYi Zou return 0; 662fdecf31bSYi Zou } 663fdecf31bSYi Zou 664fdecf31bSYi Zou 665fdecf31bSYi Zou static void fcoe_del_netdev_mapping(struct net_device *netdev) 666fdecf31bSYi Zou { 667fdecf31bSYi Zou struct fcoe_netdev_mapping *nm = NULL, *tmp; 668fdecf31bSYi Zou 66970be6344SBhanu Prakash Gollapudi mutex_lock(&fn_mutex); 670fdecf31bSYi Zou list_for_each_entry_safe(nm, tmp, &fcoe_netdevs, list) { 671fdecf31bSYi Zou if (nm->netdev == netdev) { 672fdecf31bSYi Zou list_del(&nm->list); 673fdecf31bSYi Zou kfree(nm); 67470be6344SBhanu Prakash Gollapudi mutex_unlock(&fn_mutex); 675fdecf31bSYi Zou return; 676fdecf31bSYi Zou } 677fdecf31bSYi Zou } 67870be6344SBhanu Prakash Gollapudi mutex_unlock(&fn_mutex); 679fdecf31bSYi Zou } 680fdecf31bSYi Zou 681fdecf31bSYi Zou 682fdecf31bSYi Zou /** 683fdecf31bSYi Zou * fcoe_netdev_map_lookup - find the fcoe transport that matches the netdev on which 684fdecf31bSYi Zou * it was created 685fdecf31bSYi Zou * 686fdecf31bSYi Zou * Returns : ptr to the fcoe transport that supports this netdev or NULL 687fdecf31bSYi Zou * if not found. 688fdecf31bSYi Zou * 689fdecf31bSYi Zou * The ft_mutex should be held when this is called 690fdecf31bSYi Zou */ 691fdecf31bSYi Zou static struct fcoe_transport *fcoe_netdev_map_lookup(struct net_device *netdev) 692fdecf31bSYi Zou { 693fdecf31bSYi Zou struct fcoe_transport *ft = NULL; 694fdecf31bSYi Zou struct fcoe_netdev_mapping *nm; 695fdecf31bSYi Zou 69670be6344SBhanu Prakash Gollapudi mutex_lock(&fn_mutex); 697fdecf31bSYi Zou list_for_each_entry(nm, &fcoe_netdevs, list) { 698fdecf31bSYi Zou if (netdev == nm->netdev) { 699fdecf31bSYi Zou ft = nm->ft; 70070be6344SBhanu Prakash Gollapudi mutex_unlock(&fn_mutex); 701fdecf31bSYi Zou return ft; 702fdecf31bSYi Zou } 703fdecf31bSYi Zou } 704fdecf31bSYi Zou 70570be6344SBhanu Prakash Gollapudi mutex_unlock(&fn_mutex); 706fdecf31bSYi Zou return NULL; 707fdecf31bSYi Zou } 708fdecf31bSYi Zou 709fdecf31bSYi Zou /** 710fdecf31bSYi Zou * fcoe_if_to_netdev() - Parse a name buffer to get a net device 711fdecf31bSYi Zou * @buffer: The name of the net device 712fdecf31bSYi Zou * 713fdecf31bSYi Zou * Returns: NULL or a ptr to net_device 714fdecf31bSYi Zou */ 715fdecf31bSYi Zou static struct net_device *fcoe_if_to_netdev(const char *buffer) 716fdecf31bSYi Zou { 717fdecf31bSYi Zou char *cp; 718fdecf31bSYi Zou char ifname[IFNAMSIZ + 2]; 719fdecf31bSYi Zou 720fdecf31bSYi Zou if (buffer) { 721fdecf31bSYi Zou strlcpy(ifname, buffer, IFNAMSIZ); 722fdecf31bSYi Zou cp = ifname + strlen(ifname); 723fdecf31bSYi Zou while (--cp >= ifname && *cp == '\n') 724fdecf31bSYi Zou *cp = '\0'; 725fdecf31bSYi Zou return dev_get_by_name(&init_net, ifname); 726fdecf31bSYi Zou } 727fdecf31bSYi Zou return NULL; 728fdecf31bSYi Zou } 729fdecf31bSYi Zou 730fdecf31bSYi Zou /** 73170be6344SBhanu Prakash Gollapudi * libfcoe_device_notification() - Handler for net device events 73270be6344SBhanu Prakash Gollapudi * @notifier: The context of the notification 73370be6344SBhanu Prakash Gollapudi * @event: The type of event 73470be6344SBhanu Prakash Gollapudi * @ptr: The net device that the event was on 73570be6344SBhanu Prakash Gollapudi * 73670be6344SBhanu Prakash Gollapudi * This function is called by the Ethernet driver in case of link change event. 73770be6344SBhanu Prakash Gollapudi * 73870be6344SBhanu Prakash Gollapudi * Returns: 0 for success 73970be6344SBhanu Prakash Gollapudi */ 74070be6344SBhanu Prakash Gollapudi static int libfcoe_device_notification(struct notifier_block *notifier, 74170be6344SBhanu Prakash Gollapudi ulong event, void *ptr) 74270be6344SBhanu Prakash Gollapudi { 743351638e7SJiri Pirko struct net_device *netdev = netdev_notifier_info_to_dev(ptr); 74470be6344SBhanu Prakash Gollapudi 74570be6344SBhanu Prakash Gollapudi switch (event) { 74670be6344SBhanu Prakash Gollapudi case NETDEV_UNREGISTER: 747b99fbf6aSRobert Love LIBFCOE_TRANSPORT_DBG("NETDEV_UNREGISTER %s\n", 74870be6344SBhanu Prakash Gollapudi netdev->name); 74970be6344SBhanu Prakash Gollapudi fcoe_del_netdev_mapping(netdev); 75070be6344SBhanu Prakash Gollapudi break; 75170be6344SBhanu Prakash Gollapudi } 75270be6344SBhanu Prakash Gollapudi return NOTIFY_OK; 75370be6344SBhanu Prakash Gollapudi } 75470be6344SBhanu Prakash Gollapudi 7556a891b07SRobert Love ssize_t fcoe_ctlr_create_store(struct bus_type *bus, 7566a891b07SRobert Love const char *buf, size_t count) 7576a891b07SRobert Love { 7586a891b07SRobert Love struct net_device *netdev = NULL; 7596a891b07SRobert Love struct fcoe_transport *ft = NULL; 7606a891b07SRobert Love int rc = 0; 7616a891b07SRobert Love int err; 7626a891b07SRobert Love 7636a891b07SRobert Love mutex_lock(&ft_mutex); 7646a891b07SRobert Love 7656a891b07SRobert Love netdev = fcoe_if_to_netdev(buf); 7666a891b07SRobert Love if (!netdev) { 7676a891b07SRobert Love LIBFCOE_TRANSPORT_DBG("Invalid device %s.\n", buf); 7686a891b07SRobert Love rc = -ENODEV; 7696a891b07SRobert Love goto out_nodev; 7706a891b07SRobert Love } 7716a891b07SRobert Love 7726a891b07SRobert Love ft = fcoe_netdev_map_lookup(netdev); 7736a891b07SRobert Love if (ft) { 7746a891b07SRobert Love LIBFCOE_TRANSPORT_DBG("transport %s already has existing " 7756a891b07SRobert Love "FCoE instance on %s.\n", 7766a891b07SRobert Love ft->name, netdev->name); 7776a891b07SRobert Love rc = -EEXIST; 7786a891b07SRobert Love goto out_putdev; 7796a891b07SRobert Love } 7806a891b07SRobert Love 7816a891b07SRobert Love ft = fcoe_transport_lookup(netdev); 7826a891b07SRobert Love if (!ft) { 7836a891b07SRobert Love LIBFCOE_TRANSPORT_DBG("no FCoE transport found for %s.\n", 7846a891b07SRobert Love netdev->name); 7856a891b07SRobert Love rc = -ENODEV; 7866a891b07SRobert Love goto out_putdev; 7876a891b07SRobert Love } 7886a891b07SRobert Love 7896a891b07SRobert Love /* pass to transport create */ 7906a891b07SRobert Love err = ft->alloc ? ft->alloc(netdev) : -ENODEV; 7916a891b07SRobert Love if (err) { 7926a891b07SRobert Love fcoe_del_netdev_mapping(netdev); 7936a891b07SRobert Love rc = -ENOMEM; 7946a891b07SRobert Love goto out_putdev; 7956a891b07SRobert Love } 7966a891b07SRobert Love 7976a891b07SRobert Love err = fcoe_add_netdev_mapping(netdev, ft); 7986a891b07SRobert Love if (err) { 7996a891b07SRobert Love LIBFCOE_TRANSPORT_DBG("failed to add new netdev mapping " 8006a891b07SRobert Love "for FCoE transport %s for %s.\n", 8016a891b07SRobert Love ft->name, netdev->name); 8026a891b07SRobert Love rc = -ENODEV; 8036a891b07SRobert Love goto out_putdev; 8046a891b07SRobert Love } 8056a891b07SRobert Love 806a2ceb1fbSRobert Love LIBFCOE_TRANSPORT_DBG("transport %s succeeded to create fcoe on %s.\n", 807a2ceb1fbSRobert Love ft->name, netdev->name); 8086a891b07SRobert Love 8096a891b07SRobert Love out_putdev: 8106a891b07SRobert Love dev_put(netdev); 8116a891b07SRobert Love out_nodev: 8126a891b07SRobert Love mutex_unlock(&ft_mutex); 8136a891b07SRobert Love if (rc) 8146a891b07SRobert Love return rc; 8156a891b07SRobert Love return count; 8166a891b07SRobert Love } 8176a891b07SRobert Love 8186a891b07SRobert Love ssize_t fcoe_ctlr_destroy_store(struct bus_type *bus, 8196a891b07SRobert Love const char *buf, size_t count) 8206a891b07SRobert Love { 8216a891b07SRobert Love int rc = -ENODEV; 8226a891b07SRobert Love struct net_device *netdev = NULL; 8236a891b07SRobert Love struct fcoe_transport *ft = NULL; 8246a891b07SRobert Love 8256a891b07SRobert Love mutex_lock(&ft_mutex); 8266a891b07SRobert Love 8276a891b07SRobert Love netdev = fcoe_if_to_netdev(buf); 8286a891b07SRobert Love if (!netdev) { 8296a891b07SRobert Love LIBFCOE_TRANSPORT_DBG("invalid device %s.\n", buf); 8306a891b07SRobert Love goto out_nodev; 8316a891b07SRobert Love } 8326a891b07SRobert Love 8336a891b07SRobert Love ft = fcoe_netdev_map_lookup(netdev); 8346a891b07SRobert Love if (!ft) { 8356a891b07SRobert Love LIBFCOE_TRANSPORT_DBG("no FCoE transport found for %s.\n", 8366a891b07SRobert Love netdev->name); 8376a891b07SRobert Love goto out_putdev; 8386a891b07SRobert Love } 8396a891b07SRobert Love 8406a891b07SRobert Love /* pass to transport destroy */ 8416a891b07SRobert Love rc = ft->destroy(netdev); 8426a891b07SRobert Love if (rc) 8436a891b07SRobert Love goto out_putdev; 8446a891b07SRobert Love 8456a891b07SRobert Love fcoe_del_netdev_mapping(netdev); 8466a891b07SRobert Love LIBFCOE_TRANSPORT_DBG("transport %s %s to destroy fcoe on %s.\n", 8476a891b07SRobert Love ft->name, (rc) ? "failed" : "succeeded", 8486a891b07SRobert Love netdev->name); 8496a891b07SRobert Love rc = count; /* required for successful return */ 8506a891b07SRobert Love out_putdev: 8516a891b07SRobert Love dev_put(netdev); 8526a891b07SRobert Love out_nodev: 8536a891b07SRobert Love mutex_unlock(&ft_mutex); 8546a891b07SRobert Love return rc; 8556a891b07SRobert Love } 8566a891b07SRobert Love EXPORT_SYMBOL(fcoe_ctlr_destroy_store); 85770be6344SBhanu Prakash Gollapudi 85870be6344SBhanu Prakash Gollapudi /** 859fdecf31bSYi Zou * fcoe_transport_create() - Create a fcoe interface 860fdecf31bSYi Zou * @buffer: The name of the Ethernet interface to create on 861fdecf31bSYi Zou * @kp: The associated kernel param 862fdecf31bSYi Zou * 863fdecf31bSYi Zou * Called from sysfs. This holds the ft_mutex while calling the 864fdecf31bSYi Zou * registered fcoe transport's create function. 865fdecf31bSYi Zou * 866fdecf31bSYi Zou * Returns: 0 for success 867fdecf31bSYi Zou */ 868fdecf31bSYi Zou static int fcoe_transport_create(const char *buffer, struct kernel_param *kp) 869fdecf31bSYi Zou { 870fdecf31bSYi Zou int rc = -ENODEV; 871fdecf31bSYi Zou struct net_device *netdev = NULL; 872fdecf31bSYi Zou struct fcoe_transport *ft = NULL; 873fdecf31bSYi Zou enum fip_state fip_mode = (enum fip_state)(long)kp->arg; 874fdecf31bSYi Zou 875b3960afeSRobert Love mutex_lock(&ft_mutex); 876b3960afeSRobert Love 877fdecf31bSYi Zou netdev = fcoe_if_to_netdev(buffer); 878fdecf31bSYi Zou if (!netdev) { 879fdecf31bSYi Zou LIBFCOE_TRANSPORT_DBG("Invalid device %s.\n", buffer); 880fdecf31bSYi Zou goto out_nodev; 881fdecf31bSYi Zou } 882fdecf31bSYi Zou 883fdecf31bSYi Zou ft = fcoe_netdev_map_lookup(netdev); 884fdecf31bSYi Zou if (ft) { 885fdecf31bSYi Zou LIBFCOE_TRANSPORT_DBG("transport %s already has existing " 886fdecf31bSYi Zou "FCoE instance on %s.\n", 887fdecf31bSYi Zou ft->name, netdev->name); 888fdecf31bSYi Zou rc = -EEXIST; 889fdecf31bSYi Zou goto out_putdev; 890fdecf31bSYi Zou } 891fdecf31bSYi Zou 892fdecf31bSYi Zou ft = fcoe_transport_lookup(netdev); 893fdecf31bSYi Zou if (!ft) { 894fdecf31bSYi Zou LIBFCOE_TRANSPORT_DBG("no FCoE transport found for %s.\n", 895fdecf31bSYi Zou netdev->name); 896fdecf31bSYi Zou goto out_putdev; 897fdecf31bSYi Zou } 898fdecf31bSYi Zou 899fdecf31bSYi Zou rc = fcoe_add_netdev_mapping(netdev, ft); 900fdecf31bSYi Zou if (rc) { 901fdecf31bSYi Zou LIBFCOE_TRANSPORT_DBG("failed to add new netdev mapping " 902fdecf31bSYi Zou "for FCoE transport %s for %s.\n", 903fdecf31bSYi Zou ft->name, netdev->name); 904fdecf31bSYi Zou goto out_putdev; 905fdecf31bSYi Zou } 906fdecf31bSYi Zou 907fdecf31bSYi Zou /* pass to transport create */ 908fdecf31bSYi Zou rc = ft->create ? ft->create(netdev, fip_mode) : -ENODEV; 909fdecf31bSYi Zou if (rc) 910fdecf31bSYi Zou fcoe_del_netdev_mapping(netdev); 911fdecf31bSYi Zou 912fdecf31bSYi Zou LIBFCOE_TRANSPORT_DBG("transport %s %s to create fcoe on %s.\n", 913fdecf31bSYi Zou ft->name, (rc) ? "failed" : "succeeded", 914fdecf31bSYi Zou netdev->name); 915fdecf31bSYi Zou 916fdecf31bSYi Zou out_putdev: 917fdecf31bSYi Zou dev_put(netdev); 918fdecf31bSYi Zou out_nodev: 919fdecf31bSYi Zou mutex_unlock(&ft_mutex); 920fdecf31bSYi Zou return rc; 921fdecf31bSYi Zou } 922fdecf31bSYi Zou 923fdecf31bSYi Zou /** 924fdecf31bSYi Zou * fcoe_transport_destroy() - Destroy a FCoE interface 925fdecf31bSYi Zou * @buffer: The name of the Ethernet interface to be destroyed 926fdecf31bSYi Zou * @kp: The associated kernel parameter 927fdecf31bSYi Zou * 928fdecf31bSYi Zou * Called from sysfs. This holds the ft_mutex while calling the 929fdecf31bSYi Zou * registered fcoe transport's destroy function. 930fdecf31bSYi Zou * 931fdecf31bSYi Zou * Returns: 0 for success 932fdecf31bSYi Zou */ 933fdecf31bSYi Zou static int fcoe_transport_destroy(const char *buffer, struct kernel_param *kp) 934fdecf31bSYi Zou { 935fdecf31bSYi Zou int rc = -ENODEV; 936fdecf31bSYi Zou struct net_device *netdev = NULL; 937fdecf31bSYi Zou struct fcoe_transport *ft = NULL; 938fdecf31bSYi Zou 939b3960afeSRobert Love mutex_lock(&ft_mutex); 940b3960afeSRobert Love 941fdecf31bSYi Zou netdev = fcoe_if_to_netdev(buffer); 942fdecf31bSYi Zou if (!netdev) { 943fdecf31bSYi Zou LIBFCOE_TRANSPORT_DBG("invalid device %s.\n", buffer); 944fdecf31bSYi Zou goto out_nodev; 945fdecf31bSYi Zou } 946fdecf31bSYi Zou 947fdecf31bSYi Zou ft = fcoe_netdev_map_lookup(netdev); 948fdecf31bSYi Zou if (!ft) { 949fdecf31bSYi Zou LIBFCOE_TRANSPORT_DBG("no FCoE transport found for %s.\n", 950fdecf31bSYi Zou netdev->name); 951fdecf31bSYi Zou goto out_putdev; 952fdecf31bSYi Zou } 953fdecf31bSYi Zou 954fdecf31bSYi Zou /* pass to transport destroy */ 955fdecf31bSYi Zou rc = ft->destroy ? ft->destroy(netdev) : -ENODEV; 956fdecf31bSYi Zou fcoe_del_netdev_mapping(netdev); 957fdecf31bSYi Zou LIBFCOE_TRANSPORT_DBG("transport %s %s to destroy fcoe on %s.\n", 958fdecf31bSYi Zou ft->name, (rc) ? "failed" : "succeeded", 959fdecf31bSYi Zou netdev->name); 960fdecf31bSYi Zou 961fdecf31bSYi Zou out_putdev: 962fdecf31bSYi Zou dev_put(netdev); 963fdecf31bSYi Zou out_nodev: 964fdecf31bSYi Zou mutex_unlock(&ft_mutex); 965fdecf31bSYi Zou return rc; 966fdecf31bSYi Zou } 967fdecf31bSYi Zou 968fdecf31bSYi Zou /** 969fdecf31bSYi Zou * fcoe_transport_disable() - Disables a FCoE interface 970fdecf31bSYi Zou * @buffer: The name of the Ethernet interface to be disabled 971fdecf31bSYi Zou * @kp: The associated kernel parameter 972fdecf31bSYi Zou * 973fdecf31bSYi Zou * Called from sysfs. 974fdecf31bSYi Zou * 975fdecf31bSYi Zou * Returns: 0 for success 976fdecf31bSYi Zou */ 977fdecf31bSYi Zou static int fcoe_transport_disable(const char *buffer, struct kernel_param *kp) 978fdecf31bSYi Zou { 979fdecf31bSYi Zou int rc = -ENODEV; 980fdecf31bSYi Zou struct net_device *netdev = NULL; 981fdecf31bSYi Zou struct fcoe_transport *ft = NULL; 982fdecf31bSYi Zou 983b3960afeSRobert Love mutex_lock(&ft_mutex); 984b3960afeSRobert Love 985fdecf31bSYi Zou netdev = fcoe_if_to_netdev(buffer); 986fdecf31bSYi Zou if (!netdev) 987fdecf31bSYi Zou goto out_nodev; 988fdecf31bSYi Zou 989fdecf31bSYi Zou ft = fcoe_netdev_map_lookup(netdev); 990fdecf31bSYi Zou if (!ft) 991fdecf31bSYi Zou goto out_putdev; 992fdecf31bSYi Zou 993fdecf31bSYi Zou rc = ft->disable ? ft->disable(netdev) : -ENODEV; 994fdecf31bSYi Zou 995fdecf31bSYi Zou out_putdev: 996fdecf31bSYi Zou dev_put(netdev); 997fdecf31bSYi Zou out_nodev: 998fdecf31bSYi Zou mutex_unlock(&ft_mutex); 999fdecf31bSYi Zou return rc; 1000fdecf31bSYi Zou } 1001fdecf31bSYi Zou 1002fdecf31bSYi Zou /** 1003fdecf31bSYi Zou * fcoe_transport_enable() - Enables a FCoE interface 1004fdecf31bSYi Zou * @buffer: The name of the Ethernet interface to be enabled 1005fdecf31bSYi Zou * @kp: The associated kernel parameter 1006fdecf31bSYi Zou * 1007fdecf31bSYi Zou * Called from sysfs. 1008fdecf31bSYi Zou * 1009fdecf31bSYi Zou * Returns: 0 for success 1010fdecf31bSYi Zou */ 1011fdecf31bSYi Zou static int fcoe_transport_enable(const char *buffer, struct kernel_param *kp) 1012fdecf31bSYi Zou { 1013fdecf31bSYi Zou int rc = -ENODEV; 1014fdecf31bSYi Zou struct net_device *netdev = NULL; 1015fdecf31bSYi Zou struct fcoe_transport *ft = NULL; 1016fdecf31bSYi Zou 1017b3960afeSRobert Love mutex_lock(&ft_mutex); 1018b3960afeSRobert Love 1019fdecf31bSYi Zou netdev = fcoe_if_to_netdev(buffer); 1020fdecf31bSYi Zou if (!netdev) 1021fdecf31bSYi Zou goto out_nodev; 1022fdecf31bSYi Zou 1023fdecf31bSYi Zou ft = fcoe_netdev_map_lookup(netdev); 1024fdecf31bSYi Zou if (!ft) 1025fdecf31bSYi Zou goto out_putdev; 1026fdecf31bSYi Zou 1027fdecf31bSYi Zou rc = ft->enable ? ft->enable(netdev) : -ENODEV; 1028fdecf31bSYi Zou 1029fdecf31bSYi Zou out_putdev: 1030fdecf31bSYi Zou dev_put(netdev); 1031fdecf31bSYi Zou out_nodev: 1032fdecf31bSYi Zou mutex_unlock(&ft_mutex); 1033fdecf31bSYi Zou return rc; 1034fdecf31bSYi Zou } 1035fdecf31bSYi Zou 1036fdecf31bSYi Zou /** 1037fdecf31bSYi Zou * libfcoe_init() - Initialization routine for libfcoe.ko 1038fdecf31bSYi Zou */ 1039fdecf31bSYi Zou static int __init libfcoe_init(void) 1040fdecf31bSYi Zou { 10419a74e884SRobert Love int rc = 0; 1042fdecf31bSYi Zou 10439a74e884SRobert Love rc = fcoe_transport_init(); 10449a74e884SRobert Love if (rc) 10459a74e884SRobert Love return rc; 10469a74e884SRobert Love 10479a74e884SRobert Love rc = fcoe_sysfs_setup(); 10489a74e884SRobert Love if (rc) 10499a74e884SRobert Love fcoe_transport_exit(); 10509a74e884SRobert Love 10519a74e884SRobert Love return rc; 1052fdecf31bSYi Zou } 1053fdecf31bSYi Zou module_init(libfcoe_init); 1054fdecf31bSYi Zou 1055fdecf31bSYi Zou /** 1056fdecf31bSYi Zou * libfcoe_exit() - Tear down libfcoe.ko 1057fdecf31bSYi Zou */ 1058fdecf31bSYi Zou static void __exit libfcoe_exit(void) 1059fdecf31bSYi Zou { 10609a74e884SRobert Love fcoe_sysfs_teardown(); 1061fdecf31bSYi Zou fcoe_transport_exit(); 1062fdecf31bSYi Zou } 1063fdecf31bSYi Zou module_exit(libfcoe_exit); 1064