1a61127c2SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2fdecf31bSYi Zou /* 3fdecf31bSYi Zou * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. 4fdecf31bSYi Zou * 5fdecf31bSYi Zou * Maintained at www.Open-FCoE.org 6fdecf31bSYi Zou */ 7fdecf31bSYi Zou 8fdecf31bSYi Zou #include <linux/types.h> 9fdecf31bSYi Zou #include <linux/module.h> 10fdecf31bSYi Zou #include <linux/kernel.h> 11fdecf31bSYi Zou #include <linux/list.h> 12fdecf31bSYi Zou #include <linux/netdevice.h> 13cc69837fSJakub Kicinski #include <linux/ethtool.h> 14fdecf31bSYi Zou #include <linux/errno.h> 158597ae8bSBhanu Prakash Gollapudi #include <linux/crc32.h> 16fdecf31bSYi Zou #include <scsi/libfcoe.h> 17fdecf31bSYi Zou 18fdecf31bSYi Zou #include "libfcoe.h" 19fdecf31bSYi Zou 20e01efc33SYi Zou MODULE_AUTHOR("Open-FCoE.org"); 21e01efc33SYi Zou MODULE_DESCRIPTION("FIP discovery protocol and FCoE transport for FCoE HBAs"); 22e01efc33SYi Zou MODULE_LICENSE("GPL v2"); 23e01efc33SYi Zou 24e4dca7b7SKees Cook static int fcoe_transport_create(const char *, const struct kernel_param *); 25e4dca7b7SKees Cook static int fcoe_transport_destroy(const char *, const struct kernel_param *); 26fdecf31bSYi Zou static int fcoe_transport_show(char *buffer, const struct kernel_param *kp); 27fdecf31bSYi Zou static struct fcoe_transport *fcoe_transport_lookup(struct net_device *device); 28fdecf31bSYi Zou static struct fcoe_transport *fcoe_netdev_map_lookup(struct net_device *device); 29e4dca7b7SKees Cook static int fcoe_transport_enable(const char *, const struct kernel_param *); 30e4dca7b7SKees Cook static int fcoe_transport_disable(const char *, const struct kernel_param *); 3170be6344SBhanu Prakash Gollapudi static int libfcoe_device_notification(struct notifier_block *notifier, 3270be6344SBhanu Prakash Gollapudi ulong event, void *ptr); 33fdecf31bSYi Zou 34fdecf31bSYi Zou static LIST_HEAD(fcoe_transports); 35fdecf31bSYi Zou static DEFINE_MUTEX(ft_mutex); 3670be6344SBhanu Prakash Gollapudi static LIST_HEAD(fcoe_netdevs); 3770be6344SBhanu Prakash Gollapudi static DEFINE_MUTEX(fn_mutex); 38fdecf31bSYi Zou 39e01efc33SYi Zou unsigned int libfcoe_debug_logging; 40e01efc33SYi Zou module_param_named(debug_logging, libfcoe_debug_logging, int, S_IRUGO|S_IWUSR); 41e01efc33SYi Zou MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels"); 42e01efc33SYi Zou 43fdecf31bSYi Zou module_param_call(show, NULL, fcoe_transport_show, NULL, S_IRUSR); 44fdecf31bSYi Zou __MODULE_PARM_TYPE(show, "string"); 45fdecf31bSYi Zou MODULE_PARM_DESC(show, " Show attached FCoE transports"); 46fdecf31bSYi Zou 47fdecf31bSYi Zou module_param_call(create, fcoe_transport_create, NULL, 48fdecf31bSYi Zou (void *)FIP_MODE_FABRIC, S_IWUSR); 49fdecf31bSYi Zou __MODULE_PARM_TYPE(create, "string"); 50bd0a1d6cSMasanari Iida MODULE_PARM_DESC(create, " Creates fcoe instance on an ethernet interface"); 51fdecf31bSYi Zou 52fdecf31bSYi Zou module_param_call(create_vn2vn, fcoe_transport_create, NULL, 53fdecf31bSYi Zou (void *)FIP_MODE_VN2VN, S_IWUSR); 54fdecf31bSYi Zou __MODULE_PARM_TYPE(create_vn2vn, "string"); 55fdecf31bSYi Zou MODULE_PARM_DESC(create_vn2vn, " Creates a VN_node to VN_node FCoE instance " 56fdecf31bSYi Zou "on an Ethernet interface"); 57fdecf31bSYi Zou 58fdecf31bSYi Zou module_param_call(destroy, fcoe_transport_destroy, NULL, NULL, S_IWUSR); 59fdecf31bSYi Zou __MODULE_PARM_TYPE(destroy, "string"); 60bd0a1d6cSMasanari Iida MODULE_PARM_DESC(destroy, " Destroys fcoe instance on an ethernet interface"); 61fdecf31bSYi Zou 62fdecf31bSYi Zou module_param_call(enable, fcoe_transport_enable, NULL, NULL, S_IWUSR); 63fdecf31bSYi Zou __MODULE_PARM_TYPE(enable, "string"); 64bd0a1d6cSMasanari Iida MODULE_PARM_DESC(enable, " Enables fcoe on an ethernet interface."); 65fdecf31bSYi Zou 66fdecf31bSYi Zou module_param_call(disable, fcoe_transport_disable, NULL, NULL, S_IWUSR); 67fdecf31bSYi Zou __MODULE_PARM_TYPE(disable, "string"); 68bd0a1d6cSMasanari Iida MODULE_PARM_DESC(disable, " Disables fcoe on an ethernet interface."); 69fdecf31bSYi Zou 7070be6344SBhanu Prakash Gollapudi /* notification function for packets from net device */ 7170be6344SBhanu Prakash Gollapudi static struct notifier_block libfcoe_notifier = { 7270be6344SBhanu Prakash Gollapudi .notifier_call = libfcoe_device_notification, 7370be6344SBhanu Prakash Gollapudi }; 7470be6344SBhanu Prakash Gollapudi 750b924e55SJohannes Thumshirn static const struct { 760b924e55SJohannes Thumshirn u32 fc_port_speed; 770b924e55SJohannes Thumshirn #define SPEED_2000 2000 780b924e55SJohannes Thumshirn #define SPEED_4000 4000 790b924e55SJohannes Thumshirn #define SPEED_8000 8000 800b924e55SJohannes Thumshirn #define SPEED_16000 16000 810b924e55SJohannes Thumshirn #define SPEED_32000 32000 820b924e55SJohannes Thumshirn u32 eth_port_speed; 830b924e55SJohannes Thumshirn } fcoe_port_speed_mapping[] = { 840b924e55SJohannes Thumshirn { FC_PORTSPEED_1GBIT, SPEED_1000 }, 850b924e55SJohannes Thumshirn { FC_PORTSPEED_2GBIT, SPEED_2000 }, 860b924e55SJohannes Thumshirn { FC_PORTSPEED_4GBIT, SPEED_4000 }, 870b924e55SJohannes Thumshirn { FC_PORTSPEED_8GBIT, SPEED_8000 }, 880b924e55SJohannes Thumshirn { FC_PORTSPEED_10GBIT, SPEED_10000 }, 890b924e55SJohannes Thumshirn { FC_PORTSPEED_16GBIT, SPEED_16000 }, 900b924e55SJohannes Thumshirn { FC_PORTSPEED_20GBIT, SPEED_20000 }, 910b924e55SJohannes Thumshirn { FC_PORTSPEED_25GBIT, SPEED_25000 }, 920b924e55SJohannes Thumshirn { FC_PORTSPEED_32GBIT, SPEED_32000 }, 930b924e55SJohannes Thumshirn { FC_PORTSPEED_40GBIT, SPEED_40000 }, 940b924e55SJohannes Thumshirn { FC_PORTSPEED_50GBIT, SPEED_50000 }, 950b924e55SJohannes Thumshirn { FC_PORTSPEED_100GBIT, SPEED_100000 }, 960b924e55SJohannes Thumshirn }; 970b924e55SJohannes Thumshirn 980b924e55SJohannes Thumshirn static inline u32 eth2fc_speed(u32 eth_port_speed) 990b924e55SJohannes Thumshirn { 1000b924e55SJohannes Thumshirn int i; 1010b924e55SJohannes Thumshirn 102182d3f84SVincent Stehlé for (i = 0; i < ARRAY_SIZE(fcoe_port_speed_mapping); i++) { 1030b924e55SJohannes Thumshirn if (fcoe_port_speed_mapping[i].eth_port_speed == eth_port_speed) 1040b924e55SJohannes Thumshirn return fcoe_port_speed_mapping[i].fc_port_speed; 1050b924e55SJohannes Thumshirn } 1060b924e55SJohannes Thumshirn 1070b924e55SJohannes Thumshirn return FC_PORTSPEED_UNKNOWN; 1080b924e55SJohannes Thumshirn } 1090b924e55SJohannes Thumshirn 11003702689SYi Zou /** 11103702689SYi Zou * fcoe_link_speed_update() - Update the supported and actual link speeds 11203702689SYi Zou * @lport: The local port to update speeds for 11303702689SYi Zou * 11403702689SYi Zou * Returns: 0 if the ethtool query was successful 11503702689SYi Zou * -1 if the ethtool query failed 11603702689SYi Zou */ 11703702689SYi Zou int fcoe_link_speed_update(struct fc_lport *lport) 11803702689SYi Zou { 11903702689SYi Zou struct net_device *netdev = fcoe_get_netdev(lport); 120008eb736SDavid Decotigny struct ethtool_link_ksettings ecmd; 12103702689SYi Zou 122008eb736SDavid Decotigny if (!__ethtool_get_link_ksettings(netdev, &ecmd)) { 123b8d23dc6SChris Leech lport->link_supported_speeds &= ~(FC_PORTSPEED_1GBIT | 124b8d23dc6SChris Leech FC_PORTSPEED_10GBIT | 125b8d23dc6SChris Leech FC_PORTSPEED_20GBIT | 126b8d23dc6SChris Leech FC_PORTSPEED_40GBIT); 127b8d23dc6SChris Leech 128008eb736SDavid Decotigny if (ecmd.link_modes.supported[0] & ( 129008eb736SDavid Decotigny SUPPORTED_1000baseT_Half | 130b8d23dc6SChris Leech SUPPORTED_1000baseT_Full | 131b8d23dc6SChris Leech SUPPORTED_1000baseKX_Full)) 13203702689SYi Zou lport->link_supported_speeds |= FC_PORTSPEED_1GBIT; 133b8d23dc6SChris Leech 134008eb736SDavid Decotigny if (ecmd.link_modes.supported[0] & ( 135008eb736SDavid Decotigny SUPPORTED_10000baseT_Full | 136b8d23dc6SChris Leech SUPPORTED_10000baseKX4_Full | 137b8d23dc6SChris Leech SUPPORTED_10000baseKR_Full | 138b8d23dc6SChris Leech SUPPORTED_10000baseR_FEC)) 139b8d23dc6SChris Leech lport->link_supported_speeds |= FC_PORTSPEED_10GBIT; 140b8d23dc6SChris Leech 141008eb736SDavid Decotigny if (ecmd.link_modes.supported[0] & ( 142008eb736SDavid Decotigny SUPPORTED_20000baseMLD2_Full | 143b8d23dc6SChris Leech SUPPORTED_20000baseKR2_Full)) 144b8d23dc6SChris Leech lport->link_supported_speeds |= FC_PORTSPEED_20GBIT; 145b8d23dc6SChris Leech 146008eb736SDavid Decotigny if (ecmd.link_modes.supported[0] & ( 147008eb736SDavid Decotigny SUPPORTED_40000baseKR4_Full | 148b8d23dc6SChris Leech SUPPORTED_40000baseCR4_Full | 149b8d23dc6SChris Leech SUPPORTED_40000baseSR4_Full | 150b8d23dc6SChris Leech SUPPORTED_40000baseLR4_Full)) 151b8d23dc6SChris Leech lport->link_supported_speeds |= FC_PORTSPEED_40GBIT; 152b8d23dc6SChris Leech 1530b924e55SJohannes Thumshirn lport->link_speed = eth2fc_speed(ecmd.base.speed); 15403702689SYi Zou return 0; 15503702689SYi Zou } 15603702689SYi Zou return -1; 15703702689SYi Zou } 15803702689SYi Zou EXPORT_SYMBOL_GPL(fcoe_link_speed_update); 15903702689SYi Zou 16057c2728fSYi Zou /** 16157c2728fSYi Zou * __fcoe_get_lesb() - Get the Link Error Status Block (LESB) for a given lport 16257c2728fSYi Zou * @lport: The local port to update speeds for 16357c2728fSYi Zou * @fc_lesb: Pointer to the LESB to be filled up 16457c2728fSYi Zou * @netdev: Pointer to the netdev that is associated with the lport 16557c2728fSYi Zou * 16657c2728fSYi Zou * Note, the Link Error Status Block (LESB) for FCoE is defined in FC-BB-6 16757c2728fSYi Zou * Clause 7.11 in v1.04. 16857c2728fSYi Zou */ 169814740d5SBhanu Prakash Gollapudi void __fcoe_get_lesb(struct fc_lport *lport, 170814740d5SBhanu Prakash Gollapudi struct fc_els_lesb *fc_lesb, 171814740d5SBhanu Prakash Gollapudi struct net_device *netdev) 172814740d5SBhanu Prakash Gollapudi { 173814740d5SBhanu Prakash Gollapudi unsigned int cpu; 174814740d5SBhanu Prakash Gollapudi u32 lfc, vlfc, mdac; 1751bd49b48SVasu Dev struct fc_stats *stats; 176814740d5SBhanu Prakash Gollapudi struct fcoe_fc_els_lesb *lesb; 177814740d5SBhanu Prakash Gollapudi struct rtnl_link_stats64 temp; 178814740d5SBhanu Prakash Gollapudi 179814740d5SBhanu Prakash Gollapudi lfc = 0; 180814740d5SBhanu Prakash Gollapudi vlfc = 0; 181814740d5SBhanu Prakash Gollapudi mdac = 0; 182814740d5SBhanu Prakash Gollapudi lesb = (struct fcoe_fc_els_lesb *)fc_lesb; 183814740d5SBhanu Prakash Gollapudi memset(lesb, 0, sizeof(*lesb)); 184814740d5SBhanu Prakash Gollapudi for_each_possible_cpu(cpu) { 1851bd49b48SVasu Dev stats = per_cpu_ptr(lport->stats, cpu); 186a912460eSSebastian Andrzej Siewior lfc += READ_ONCE(stats->LinkFailureCount); 187a912460eSSebastian Andrzej Siewior vlfc += READ_ONCE(stats->VLinkFailureCount); 188a912460eSSebastian Andrzej Siewior mdac += READ_ONCE(stats->MissDiscAdvCount); 189814740d5SBhanu Prakash Gollapudi } 190814740d5SBhanu Prakash Gollapudi lesb->lesb_link_fail = htonl(lfc); 191814740d5SBhanu Prakash Gollapudi lesb->lesb_vlink_fail = htonl(vlfc); 192814740d5SBhanu Prakash Gollapudi lesb->lesb_miss_fka = htonl(mdac); 193814740d5SBhanu Prakash Gollapudi lesb->lesb_fcs_error = 194814740d5SBhanu Prakash Gollapudi htonl(dev_get_stats(netdev, &temp)->rx_crc_errors); 195814740d5SBhanu Prakash Gollapudi } 196814740d5SBhanu Prakash Gollapudi EXPORT_SYMBOL_GPL(__fcoe_get_lesb); 197814740d5SBhanu Prakash Gollapudi 19857c2728fSYi Zou /** 19957c2728fSYi Zou * fcoe_get_lesb() - Fill the FCoE Link Error Status Block 20057c2728fSYi Zou * @lport: the local port 20157c2728fSYi Zou * @fc_lesb: the link error status block 20257c2728fSYi Zou */ 20357c2728fSYi Zou void fcoe_get_lesb(struct fc_lport *lport, 20457c2728fSYi Zou struct fc_els_lesb *fc_lesb) 20557c2728fSYi Zou { 20657c2728fSYi Zou struct net_device *netdev = fcoe_get_netdev(lport); 20757c2728fSYi Zou 20857c2728fSYi Zou __fcoe_get_lesb(lport, fc_lesb, netdev); 20957c2728fSYi Zou } 21057c2728fSYi Zou EXPORT_SYMBOL_GPL(fcoe_get_lesb); 21157c2728fSYi Zou 21257c2728fSYi Zou /** 21357c2728fSYi Zou * fcoe_ctlr_get_lesb() - Get the Link Error Status Block (LESB) for a given 21457c2728fSYi Zou * fcoe controller device 21557c2728fSYi Zou * @ctlr_dev: The given fcoe controller device 21657c2728fSYi Zou * 21757c2728fSYi Zou */ 21857c2728fSYi Zou void fcoe_ctlr_get_lesb(struct fcoe_ctlr_device *ctlr_dev) 21957c2728fSYi Zou { 22057c2728fSYi Zou struct fcoe_ctlr *fip = fcoe_ctlr_device_priv(ctlr_dev); 22157c2728fSYi Zou struct net_device *netdev = fcoe_get_netdev(fip->lp); 222418a8cfeSYi Zou struct fc_els_lesb *fc_lesb; 22357c2728fSYi Zou 224418a8cfeSYi Zou fc_lesb = (struct fc_els_lesb *)(&ctlr_dev->lesb); 225418a8cfeSYi Zou __fcoe_get_lesb(fip->lp, fc_lesb, netdev); 22657c2728fSYi Zou } 22757c2728fSYi Zou EXPORT_SYMBOL_GPL(fcoe_ctlr_get_lesb); 22857c2728fSYi Zou 229d834895cSBhanu Prakash Gollapudi void fcoe_wwn_to_str(u64 wwn, char *buf, int len) 230d834895cSBhanu Prakash Gollapudi { 231d834895cSBhanu Prakash Gollapudi u8 wwpn[8]; 232d834895cSBhanu Prakash Gollapudi 233d834895cSBhanu Prakash Gollapudi u64_to_wwn(wwn, wwpn); 234d834895cSBhanu Prakash Gollapudi snprintf(buf, len, "%02x%02x%02x%02x%02x%02x%02x%02x", 235d834895cSBhanu Prakash Gollapudi wwpn[0], wwpn[1], wwpn[2], wwpn[3], 236d834895cSBhanu Prakash Gollapudi wwpn[4], wwpn[5], wwpn[6], wwpn[7]); 237d834895cSBhanu Prakash Gollapudi } 238d834895cSBhanu Prakash Gollapudi EXPORT_SYMBOL_GPL(fcoe_wwn_to_str); 239d834895cSBhanu Prakash Gollapudi 240d834895cSBhanu Prakash Gollapudi /** 241d834895cSBhanu Prakash Gollapudi * fcoe_validate_vport_create() - Validate a vport before creating it 242d834895cSBhanu Prakash Gollapudi * @vport: NPIV port to be created 243d834895cSBhanu Prakash Gollapudi * 244d834895cSBhanu Prakash Gollapudi * This routine is meant to add validation for a vport before creating it 245d834895cSBhanu Prakash Gollapudi * via fcoe_vport_create(). 246d834895cSBhanu Prakash Gollapudi * Current validations are: 247d834895cSBhanu Prakash Gollapudi * - WWPN supplied is unique for given lport 248d834895cSBhanu Prakash Gollapudi */ 249d834895cSBhanu Prakash Gollapudi int fcoe_validate_vport_create(struct fc_vport *vport) 250d834895cSBhanu Prakash Gollapudi { 251d834895cSBhanu Prakash Gollapudi struct Scsi_Host *shost = vport_to_shost(vport); 252d834895cSBhanu Prakash Gollapudi struct fc_lport *n_port = shost_priv(shost); 253d834895cSBhanu Prakash Gollapudi struct fc_lport *vn_port; 254d834895cSBhanu Prakash Gollapudi int rc = 0; 255d834895cSBhanu Prakash Gollapudi char buf[32]; 256d834895cSBhanu Prakash Gollapudi 257d834895cSBhanu Prakash Gollapudi mutex_lock(&n_port->lp_mutex); 258d834895cSBhanu Prakash Gollapudi 259d834895cSBhanu Prakash Gollapudi fcoe_wwn_to_str(vport->port_name, buf, sizeof(buf)); 260d834895cSBhanu Prakash Gollapudi /* Check if the wwpn is not same as that of the lport */ 261d834895cSBhanu Prakash Gollapudi if (!memcmp(&n_port->wwpn, &vport->port_name, sizeof(u64))) { 262d834895cSBhanu Prakash Gollapudi LIBFCOE_TRANSPORT_DBG("vport WWPN 0x%s is same as that of the " 263d834895cSBhanu Prakash Gollapudi "base port WWPN\n", buf); 264d834895cSBhanu Prakash Gollapudi rc = -EINVAL; 265d834895cSBhanu Prakash Gollapudi goto out; 266d834895cSBhanu Prakash Gollapudi } 267d834895cSBhanu Prakash Gollapudi 268d834895cSBhanu Prakash Gollapudi /* Check if there is any existing vport with same wwpn */ 269d834895cSBhanu Prakash Gollapudi list_for_each_entry(vn_port, &n_port->vports, list) { 270d834895cSBhanu Prakash Gollapudi if (!memcmp(&vn_port->wwpn, &vport->port_name, sizeof(u64))) { 271d834895cSBhanu Prakash Gollapudi LIBFCOE_TRANSPORT_DBG("vport with given WWPN 0x%s " 272d834895cSBhanu Prakash Gollapudi "already exists\n", buf); 273d834895cSBhanu Prakash Gollapudi rc = -EINVAL; 274d834895cSBhanu Prakash Gollapudi break; 275d834895cSBhanu Prakash Gollapudi } 276d834895cSBhanu Prakash Gollapudi } 277d834895cSBhanu Prakash Gollapudi out: 278d834895cSBhanu Prakash Gollapudi mutex_unlock(&n_port->lp_mutex); 279d834895cSBhanu Prakash Gollapudi return rc; 280d834895cSBhanu Prakash Gollapudi } 281d834895cSBhanu Prakash Gollapudi EXPORT_SYMBOL_GPL(fcoe_validate_vport_create); 282d834895cSBhanu Prakash Gollapudi 283d834895cSBhanu Prakash Gollapudi /** 284d834895cSBhanu Prakash Gollapudi * fcoe_get_wwn() - Get the world wide name from LLD if it supports it 285d834895cSBhanu Prakash Gollapudi * @netdev: the associated net device 286d834895cSBhanu Prakash Gollapudi * @wwn: the output WWN 287d834895cSBhanu Prakash Gollapudi * @type: the type of WWN (WWPN or WWNN) 288d834895cSBhanu Prakash Gollapudi * 289d834895cSBhanu Prakash Gollapudi * Returns: 0 for success 290d834895cSBhanu Prakash Gollapudi */ 291d834895cSBhanu Prakash Gollapudi int fcoe_get_wwn(struct net_device *netdev, u64 *wwn, int type) 292d834895cSBhanu Prakash Gollapudi { 293d834895cSBhanu Prakash Gollapudi const struct net_device_ops *ops = netdev->netdev_ops; 294d834895cSBhanu Prakash Gollapudi 295d834895cSBhanu Prakash Gollapudi if (ops->ndo_fcoe_get_wwn) 296d834895cSBhanu Prakash Gollapudi return ops->ndo_fcoe_get_wwn(netdev, wwn, type); 297d834895cSBhanu Prakash Gollapudi return -EINVAL; 298d834895cSBhanu Prakash Gollapudi } 299d834895cSBhanu Prakash Gollapudi EXPORT_SYMBOL_GPL(fcoe_get_wwn); 300d834895cSBhanu Prakash Gollapudi 301fdecf31bSYi Zou /** 3028597ae8bSBhanu Prakash Gollapudi * fcoe_fc_crc() - Calculates the CRC for a given frame 3038597ae8bSBhanu Prakash Gollapudi * @fp: The frame to be checksumed 3048597ae8bSBhanu Prakash Gollapudi * 3058597ae8bSBhanu Prakash Gollapudi * This uses crc32() routine to calculate the CRC for a frame 3068597ae8bSBhanu Prakash Gollapudi * 3078597ae8bSBhanu Prakash Gollapudi * Return: The 32 bit CRC value 3088597ae8bSBhanu Prakash Gollapudi */ 3098597ae8bSBhanu Prakash Gollapudi u32 fcoe_fc_crc(struct fc_frame *fp) 3108597ae8bSBhanu Prakash Gollapudi { 3118597ae8bSBhanu Prakash Gollapudi struct sk_buff *skb = fp_skb(fp); 312d7840976SMatthew Wilcox (Oracle) skb_frag_t *frag; 3138597ae8bSBhanu Prakash Gollapudi unsigned char *data; 3148597ae8bSBhanu Prakash Gollapudi unsigned long off, len, clen; 3158597ae8bSBhanu Prakash Gollapudi u32 crc; 3168597ae8bSBhanu Prakash Gollapudi unsigned i; 3178597ae8bSBhanu Prakash Gollapudi 3188597ae8bSBhanu Prakash Gollapudi crc = crc32(~0, skb->data, skb_headlen(skb)); 3198597ae8bSBhanu Prakash Gollapudi 3208597ae8bSBhanu Prakash Gollapudi for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { 3218597ae8bSBhanu Prakash Gollapudi frag = &skb_shinfo(skb)->frags[i]; 322b54c9d5bSJonathan Lemon off = skb_frag_off(frag); 3239e903e08SEric Dumazet len = skb_frag_size(frag); 3248597ae8bSBhanu Prakash Gollapudi while (len > 0) { 3258597ae8bSBhanu Prakash Gollapudi clen = min(len, PAGE_SIZE - (off & ~PAGE_MASK)); 326165c68d5SIan Campbell data = kmap_atomic( 32777dfce07SCong Wang skb_frag_page(frag) + (off >> PAGE_SHIFT)); 3288597ae8bSBhanu Prakash Gollapudi crc = crc32(crc, data + (off & ~PAGE_MASK), clen); 32977dfce07SCong Wang kunmap_atomic(data); 3308597ae8bSBhanu Prakash Gollapudi off += clen; 3318597ae8bSBhanu Prakash Gollapudi len -= clen; 3328597ae8bSBhanu Prakash Gollapudi } 3338597ae8bSBhanu Prakash Gollapudi } 3348597ae8bSBhanu Prakash Gollapudi return crc; 3358597ae8bSBhanu Prakash Gollapudi } 3368597ae8bSBhanu Prakash Gollapudi EXPORT_SYMBOL_GPL(fcoe_fc_crc); 3378597ae8bSBhanu Prakash Gollapudi 3388597ae8bSBhanu Prakash Gollapudi /** 3398597ae8bSBhanu Prakash Gollapudi * fcoe_start_io() - Start FCoE I/O 3408597ae8bSBhanu Prakash Gollapudi * @skb: The packet to be transmitted 3418597ae8bSBhanu Prakash Gollapudi * 3428597ae8bSBhanu Prakash Gollapudi * This routine is called from the net device to start transmitting 3438597ae8bSBhanu Prakash Gollapudi * FCoE packets. 3448597ae8bSBhanu Prakash Gollapudi * 3458597ae8bSBhanu Prakash Gollapudi * Returns: 0 for success 3468597ae8bSBhanu Prakash Gollapudi */ 3478597ae8bSBhanu Prakash Gollapudi int fcoe_start_io(struct sk_buff *skb) 3488597ae8bSBhanu Prakash Gollapudi { 3498597ae8bSBhanu Prakash Gollapudi struct sk_buff *nskb; 3508597ae8bSBhanu Prakash Gollapudi int rc; 3518597ae8bSBhanu Prakash Gollapudi 3528597ae8bSBhanu Prakash Gollapudi nskb = skb_clone(skb, GFP_ATOMIC); 3538597ae8bSBhanu Prakash Gollapudi if (!nskb) 3548597ae8bSBhanu Prakash Gollapudi return -ENOMEM; 3558597ae8bSBhanu Prakash Gollapudi rc = dev_queue_xmit(nskb); 3568597ae8bSBhanu Prakash Gollapudi if (rc != 0) 3578597ae8bSBhanu Prakash Gollapudi return rc; 3588597ae8bSBhanu Prakash Gollapudi kfree_skb(skb); 3598597ae8bSBhanu Prakash Gollapudi return 0; 3608597ae8bSBhanu Prakash Gollapudi } 3618597ae8bSBhanu Prakash Gollapudi EXPORT_SYMBOL_GPL(fcoe_start_io); 3628597ae8bSBhanu Prakash Gollapudi 3638597ae8bSBhanu Prakash Gollapudi 3648597ae8bSBhanu Prakash Gollapudi /** 3658597ae8bSBhanu Prakash Gollapudi * fcoe_clean_pending_queue() - Dequeue a skb and free it 3668597ae8bSBhanu Prakash Gollapudi * @lport: The local port to dequeue a skb on 3678597ae8bSBhanu Prakash Gollapudi */ 3688597ae8bSBhanu Prakash Gollapudi void fcoe_clean_pending_queue(struct fc_lport *lport) 3698597ae8bSBhanu Prakash Gollapudi { 3708597ae8bSBhanu Prakash Gollapudi struct fcoe_port *port = lport_priv(lport); 3718597ae8bSBhanu Prakash Gollapudi struct sk_buff *skb; 3728597ae8bSBhanu Prakash Gollapudi 3738597ae8bSBhanu Prakash Gollapudi spin_lock_bh(&port->fcoe_pending_queue.lock); 3748597ae8bSBhanu Prakash Gollapudi while ((skb = __skb_dequeue(&port->fcoe_pending_queue)) != NULL) { 3758597ae8bSBhanu Prakash Gollapudi spin_unlock_bh(&port->fcoe_pending_queue.lock); 3768597ae8bSBhanu Prakash Gollapudi kfree_skb(skb); 3778597ae8bSBhanu Prakash Gollapudi spin_lock_bh(&port->fcoe_pending_queue.lock); 3788597ae8bSBhanu Prakash Gollapudi } 3798597ae8bSBhanu Prakash Gollapudi spin_unlock_bh(&port->fcoe_pending_queue.lock); 3808597ae8bSBhanu Prakash Gollapudi } 3818597ae8bSBhanu Prakash Gollapudi EXPORT_SYMBOL_GPL(fcoe_clean_pending_queue); 3828597ae8bSBhanu Prakash Gollapudi 3838597ae8bSBhanu Prakash Gollapudi /** 3848597ae8bSBhanu Prakash Gollapudi * fcoe_check_wait_queue() - Attempt to clear the transmit backlog 3858597ae8bSBhanu Prakash Gollapudi * @lport: The local port whose backlog is to be cleared 386f2db5efeSLee Jones * @skb: The received FIP packet 3878597ae8bSBhanu Prakash Gollapudi * 3888597ae8bSBhanu Prakash Gollapudi * This empties the wait_queue, dequeues the head of the wait_queue queue 3898597ae8bSBhanu Prakash Gollapudi * and calls fcoe_start_io() for each packet. If all skb have been 3908597ae8bSBhanu Prakash Gollapudi * transmitted it returns the qlen. If an error occurs it restores 3918597ae8bSBhanu Prakash Gollapudi * wait_queue (to try again later) and returns -1. 3928597ae8bSBhanu Prakash Gollapudi * 3938597ae8bSBhanu Prakash Gollapudi * The wait_queue is used when the skb transmit fails. The failed skb 3948597ae8bSBhanu Prakash Gollapudi * will go in the wait_queue which will be emptied by the timer function or 3958597ae8bSBhanu Prakash Gollapudi * by the next skb transmit. 3968597ae8bSBhanu Prakash Gollapudi */ 3978597ae8bSBhanu Prakash Gollapudi void fcoe_check_wait_queue(struct fc_lport *lport, struct sk_buff *skb) 3988597ae8bSBhanu Prakash Gollapudi { 3998597ae8bSBhanu Prakash Gollapudi struct fcoe_port *port = lport_priv(lport); 4008597ae8bSBhanu Prakash Gollapudi int rc; 4018597ae8bSBhanu Prakash Gollapudi 4028597ae8bSBhanu Prakash Gollapudi spin_lock_bh(&port->fcoe_pending_queue.lock); 4038597ae8bSBhanu Prakash Gollapudi 4048597ae8bSBhanu Prakash Gollapudi if (skb) 4058597ae8bSBhanu Prakash Gollapudi __skb_queue_tail(&port->fcoe_pending_queue, skb); 4068597ae8bSBhanu Prakash Gollapudi 4078597ae8bSBhanu Prakash Gollapudi if (port->fcoe_pending_queue_active) 4088597ae8bSBhanu Prakash Gollapudi goto out; 4098597ae8bSBhanu Prakash Gollapudi port->fcoe_pending_queue_active = 1; 4108597ae8bSBhanu Prakash Gollapudi 4118597ae8bSBhanu Prakash Gollapudi while (port->fcoe_pending_queue.qlen) { 4128597ae8bSBhanu Prakash Gollapudi /* keep qlen > 0 until fcoe_start_io succeeds */ 4138597ae8bSBhanu Prakash Gollapudi port->fcoe_pending_queue.qlen++; 4148597ae8bSBhanu Prakash Gollapudi skb = __skb_dequeue(&port->fcoe_pending_queue); 4158597ae8bSBhanu Prakash Gollapudi 4168597ae8bSBhanu Prakash Gollapudi spin_unlock_bh(&port->fcoe_pending_queue.lock); 4178597ae8bSBhanu Prakash Gollapudi rc = fcoe_start_io(skb); 4188597ae8bSBhanu Prakash Gollapudi spin_lock_bh(&port->fcoe_pending_queue.lock); 4198597ae8bSBhanu Prakash Gollapudi 4208597ae8bSBhanu Prakash Gollapudi if (rc) { 4218597ae8bSBhanu Prakash Gollapudi __skb_queue_head(&port->fcoe_pending_queue, skb); 4228597ae8bSBhanu Prakash Gollapudi /* undo temporary increment above */ 4238597ae8bSBhanu Prakash Gollapudi port->fcoe_pending_queue.qlen--; 4248597ae8bSBhanu Prakash Gollapudi break; 4258597ae8bSBhanu Prakash Gollapudi } 4268597ae8bSBhanu Prakash Gollapudi /* undo temporary increment above */ 4278597ae8bSBhanu Prakash Gollapudi port->fcoe_pending_queue.qlen--; 4288597ae8bSBhanu Prakash Gollapudi } 4298597ae8bSBhanu Prakash Gollapudi 4308597ae8bSBhanu Prakash Gollapudi if (port->fcoe_pending_queue.qlen < port->min_queue_depth) 4318597ae8bSBhanu Prakash Gollapudi lport->qfull = 0; 4328597ae8bSBhanu Prakash Gollapudi if (port->fcoe_pending_queue.qlen && !timer_pending(&port->timer)) 4338597ae8bSBhanu Prakash Gollapudi mod_timer(&port->timer, jiffies + 2); 4348597ae8bSBhanu Prakash Gollapudi port->fcoe_pending_queue_active = 0; 4358597ae8bSBhanu Prakash Gollapudi out: 4368597ae8bSBhanu Prakash Gollapudi if (port->fcoe_pending_queue.qlen > port->max_queue_depth) 4378597ae8bSBhanu Prakash Gollapudi lport->qfull = 1; 4388597ae8bSBhanu Prakash Gollapudi spin_unlock_bh(&port->fcoe_pending_queue.lock); 4398597ae8bSBhanu Prakash Gollapudi } 4408597ae8bSBhanu Prakash Gollapudi EXPORT_SYMBOL_GPL(fcoe_check_wait_queue); 4418597ae8bSBhanu Prakash Gollapudi 4428597ae8bSBhanu Prakash Gollapudi /** 4438597ae8bSBhanu Prakash Gollapudi * fcoe_queue_timer() - The fcoe queue timer 444f2db5efeSLee Jones * @t: Timer context use to obtain the FCoE port 4458597ae8bSBhanu Prakash Gollapudi * 4468597ae8bSBhanu Prakash Gollapudi * Calls fcoe_check_wait_queue on timeout 4478597ae8bSBhanu Prakash Gollapudi */ 44813059106SKees Cook void fcoe_queue_timer(struct timer_list *t) 4498597ae8bSBhanu Prakash Gollapudi { 45013059106SKees Cook struct fcoe_port *port = from_timer(port, t, timer); 45113059106SKees Cook 45213059106SKees Cook fcoe_check_wait_queue(port->lport, NULL); 4538597ae8bSBhanu Prakash Gollapudi } 4548597ae8bSBhanu Prakash Gollapudi EXPORT_SYMBOL_GPL(fcoe_queue_timer); 4558597ae8bSBhanu Prakash Gollapudi 4568597ae8bSBhanu Prakash Gollapudi /** 4578597ae8bSBhanu Prakash Gollapudi * fcoe_get_paged_crc_eof() - Allocate a page to be used for the trailer CRC 4588597ae8bSBhanu Prakash Gollapudi * @skb: The packet to be transmitted 4598597ae8bSBhanu Prakash Gollapudi * @tlen: The total length of the trailer 4608597ae8bSBhanu Prakash Gollapudi * @fps: The fcoe context 4618597ae8bSBhanu Prakash Gollapudi * 4628597ae8bSBhanu Prakash Gollapudi * This routine allocates a page for frame trailers. The page is re-used if 4638597ae8bSBhanu Prakash Gollapudi * there is enough room left on it for the current trailer. If there isn't 4648597ae8bSBhanu Prakash Gollapudi * enough buffer left a new page is allocated for the trailer. Reference to 4658597ae8bSBhanu Prakash Gollapudi * the page from this function as well as the skbs using the page fragments 4668597ae8bSBhanu Prakash Gollapudi * ensure that the page is freed at the appropriate time. 4678597ae8bSBhanu Prakash Gollapudi * 4688597ae8bSBhanu Prakash Gollapudi * Returns: 0 for success 4698597ae8bSBhanu Prakash Gollapudi */ 4708597ae8bSBhanu Prakash Gollapudi int fcoe_get_paged_crc_eof(struct sk_buff *skb, int tlen, 4718597ae8bSBhanu Prakash Gollapudi struct fcoe_percpu_s *fps) 4728597ae8bSBhanu Prakash Gollapudi { 4738597ae8bSBhanu Prakash Gollapudi struct page *page; 4748597ae8bSBhanu Prakash Gollapudi 4758597ae8bSBhanu Prakash Gollapudi page = fps->crc_eof_page; 4768597ae8bSBhanu Prakash Gollapudi if (!page) { 4778597ae8bSBhanu Prakash Gollapudi page = alloc_page(GFP_ATOMIC); 4788597ae8bSBhanu Prakash Gollapudi if (!page) 4798597ae8bSBhanu Prakash Gollapudi return -ENOMEM; 4808597ae8bSBhanu Prakash Gollapudi 4818597ae8bSBhanu Prakash Gollapudi fps->crc_eof_page = page; 4828597ae8bSBhanu Prakash Gollapudi fps->crc_eof_offset = 0; 4838597ae8bSBhanu Prakash Gollapudi } 4848597ae8bSBhanu Prakash Gollapudi 4858597ae8bSBhanu Prakash Gollapudi get_page(page); 4868597ae8bSBhanu Prakash Gollapudi skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags, page, 4878597ae8bSBhanu Prakash Gollapudi fps->crc_eof_offset, tlen); 4888597ae8bSBhanu Prakash Gollapudi skb->len += tlen; 4898597ae8bSBhanu Prakash Gollapudi skb->data_len += tlen; 4908597ae8bSBhanu Prakash Gollapudi skb->truesize += tlen; 4918597ae8bSBhanu Prakash Gollapudi fps->crc_eof_offset += sizeof(struct fcoe_crc_eof); 4928597ae8bSBhanu Prakash Gollapudi 4938597ae8bSBhanu Prakash Gollapudi if (fps->crc_eof_offset >= PAGE_SIZE) { 4948597ae8bSBhanu Prakash Gollapudi fps->crc_eof_page = NULL; 4958597ae8bSBhanu Prakash Gollapudi fps->crc_eof_offset = 0; 4968597ae8bSBhanu Prakash Gollapudi put_page(page); 4978597ae8bSBhanu Prakash Gollapudi } 4988597ae8bSBhanu Prakash Gollapudi 4998597ae8bSBhanu Prakash Gollapudi return 0; 5008597ae8bSBhanu Prakash Gollapudi } 5018597ae8bSBhanu Prakash Gollapudi EXPORT_SYMBOL_GPL(fcoe_get_paged_crc_eof); 5028597ae8bSBhanu Prakash Gollapudi 5038597ae8bSBhanu Prakash Gollapudi /** 504fdecf31bSYi Zou * fcoe_transport_lookup - find an fcoe transport that matches a netdev 505fdecf31bSYi Zou * @netdev: The netdev to look for from all attached transports 506fdecf31bSYi Zou * 507fdecf31bSYi Zou * Returns : ptr to the fcoe transport that supports this netdev or NULL 508fdecf31bSYi Zou * if not found. 509fdecf31bSYi Zou * 510fdecf31bSYi Zou * The ft_mutex should be held when this is called 511fdecf31bSYi Zou */ 512fdecf31bSYi Zou static struct fcoe_transport *fcoe_transport_lookup(struct net_device *netdev) 513fdecf31bSYi Zou { 514fdecf31bSYi Zou struct fcoe_transport *ft = NULL; 515fdecf31bSYi Zou 516fdecf31bSYi Zou list_for_each_entry(ft, &fcoe_transports, list) 517fdecf31bSYi Zou if (ft->match && ft->match(netdev)) 518fdecf31bSYi Zou return ft; 519fdecf31bSYi Zou return NULL; 520fdecf31bSYi Zou } 521fdecf31bSYi Zou 522fdecf31bSYi Zou /** 523fdecf31bSYi Zou * fcoe_transport_attach - Attaches an FCoE transport 524fdecf31bSYi Zou * @ft: The fcoe transport to be attached 525fdecf31bSYi Zou * 526fdecf31bSYi Zou * Returns : 0 for success 527fdecf31bSYi Zou */ 528fdecf31bSYi Zou int fcoe_transport_attach(struct fcoe_transport *ft) 529fdecf31bSYi Zou { 530fdecf31bSYi Zou int rc = 0; 531fdecf31bSYi Zou 532fdecf31bSYi Zou mutex_lock(&ft_mutex); 533fdecf31bSYi Zou if (ft->attached) { 534fdecf31bSYi Zou LIBFCOE_TRANSPORT_DBG("transport %s already attached\n", 535fdecf31bSYi Zou ft->name); 536fdecf31bSYi Zou rc = -EEXIST; 537fdecf31bSYi Zou goto out_attach; 538fdecf31bSYi Zou } 539fdecf31bSYi Zou 540fdecf31bSYi Zou /* Add default transport to the tail */ 541fdecf31bSYi Zou if (strcmp(ft->name, FCOE_TRANSPORT_DEFAULT)) 542fdecf31bSYi Zou list_add(&ft->list, &fcoe_transports); 543fdecf31bSYi Zou else 544fdecf31bSYi Zou list_add_tail(&ft->list, &fcoe_transports); 545fdecf31bSYi Zou 546fdecf31bSYi Zou ft->attached = true; 547fdecf31bSYi Zou LIBFCOE_TRANSPORT_DBG("attaching transport %s\n", ft->name); 548fdecf31bSYi Zou 549fdecf31bSYi Zou out_attach: 550fdecf31bSYi Zou mutex_unlock(&ft_mutex); 551fdecf31bSYi Zou return rc; 552fdecf31bSYi Zou } 553fdecf31bSYi Zou EXPORT_SYMBOL(fcoe_transport_attach); 554fdecf31bSYi Zou 555fdecf31bSYi Zou /** 5564ef7fb15SYi Zou * fcoe_transport_detach - Detaches an FCoE transport 557fdecf31bSYi Zou * @ft: The fcoe transport to be attached 558fdecf31bSYi Zou * 559fdecf31bSYi Zou * Returns : 0 for success 560fdecf31bSYi Zou */ 561fdecf31bSYi Zou int fcoe_transport_detach(struct fcoe_transport *ft) 562fdecf31bSYi Zou { 563fdecf31bSYi Zou int rc = 0; 56469922fcdSYi Zou struct fcoe_netdev_mapping *nm = NULL, *tmp; 565fdecf31bSYi Zou 566fdecf31bSYi Zou mutex_lock(&ft_mutex); 567fdecf31bSYi Zou if (!ft->attached) { 568fdecf31bSYi Zou LIBFCOE_TRANSPORT_DBG("transport %s already detached\n", 569fdecf31bSYi Zou ft->name); 570fdecf31bSYi Zou rc = -ENODEV; 571fdecf31bSYi Zou goto out_attach; 572fdecf31bSYi Zou } 573fdecf31bSYi Zou 57469922fcdSYi Zou /* remove netdev mapping for this transport as it is going away */ 57569922fcdSYi Zou mutex_lock(&fn_mutex); 57669922fcdSYi Zou list_for_each_entry_safe(nm, tmp, &fcoe_netdevs, list) { 57769922fcdSYi Zou if (nm->ft == ft) { 57869922fcdSYi Zou LIBFCOE_TRANSPORT_DBG("transport %s going away, " 57969922fcdSYi Zou "remove its netdev mapping for %s\n", 58069922fcdSYi Zou ft->name, nm->netdev->name); 58169922fcdSYi Zou list_del(&nm->list); 58269922fcdSYi Zou kfree(nm); 58369922fcdSYi Zou } 58469922fcdSYi Zou } 58569922fcdSYi Zou mutex_unlock(&fn_mutex); 58669922fcdSYi Zou 587fdecf31bSYi Zou list_del(&ft->list); 588fdecf31bSYi Zou ft->attached = false; 589fdecf31bSYi Zou LIBFCOE_TRANSPORT_DBG("detaching transport %s\n", ft->name); 590fdecf31bSYi Zou 591fdecf31bSYi Zou out_attach: 592fdecf31bSYi Zou mutex_unlock(&ft_mutex); 593fdecf31bSYi Zou return rc; 594fdecf31bSYi Zou 595fdecf31bSYi Zou } 596fdecf31bSYi Zou EXPORT_SYMBOL(fcoe_transport_detach); 597fdecf31bSYi Zou 598fdecf31bSYi Zou static int fcoe_transport_show(char *buffer, const struct kernel_param *kp) 599fdecf31bSYi Zou { 600fdecf31bSYi Zou int i, j; 601fdecf31bSYi Zou struct fcoe_transport *ft = NULL; 602fdecf31bSYi Zou 603fdecf31bSYi Zou i = j = sprintf(buffer, "Attached FCoE transports:"); 604fdecf31bSYi Zou mutex_lock(&ft_mutex); 605fdecf31bSYi Zou list_for_each_entry(ft, &fcoe_transports, list) { 606a01a5a57SYi Zou if (i >= PAGE_SIZE - IFNAMSIZ) 607fdecf31bSYi Zou break; 608a01a5a57SYi Zou i += snprintf(&buffer[i], IFNAMSIZ, "%s ", ft->name); 609fdecf31bSYi Zou } 610fdecf31bSYi Zou mutex_unlock(&ft_mutex); 611fdecf31bSYi Zou if (i == j) 612fdecf31bSYi Zou i += snprintf(&buffer[i], IFNAMSIZ, "none"); 613fdecf31bSYi Zou return i; 614fdecf31bSYi Zou } 615fdecf31bSYi Zou 616fdecf31bSYi Zou static int __init fcoe_transport_init(void) 617fdecf31bSYi Zou { 61870be6344SBhanu Prakash Gollapudi register_netdevice_notifier(&libfcoe_notifier); 619fdecf31bSYi Zou return 0; 620fdecf31bSYi Zou } 621fdecf31bSYi Zou 62287098bddSMark Rustad static int fcoe_transport_exit(void) 623fdecf31bSYi Zou { 624fdecf31bSYi Zou struct fcoe_transport *ft; 625fdecf31bSYi Zou 62670be6344SBhanu Prakash Gollapudi unregister_netdevice_notifier(&libfcoe_notifier); 627fdecf31bSYi Zou mutex_lock(&ft_mutex); 628fdecf31bSYi Zou list_for_each_entry(ft, &fcoe_transports, list) 629fdecf31bSYi Zou printk(KERN_ERR "FCoE transport %s is still attached!\n", 630fdecf31bSYi Zou ft->name); 631fdecf31bSYi Zou mutex_unlock(&ft_mutex); 632fdecf31bSYi Zou return 0; 633fdecf31bSYi Zou } 634fdecf31bSYi Zou 635fdecf31bSYi Zou 636fdecf31bSYi Zou static int fcoe_add_netdev_mapping(struct net_device *netdev, 637fdecf31bSYi Zou struct fcoe_transport *ft) 638fdecf31bSYi Zou { 639fdecf31bSYi Zou struct fcoe_netdev_mapping *nm; 640fdecf31bSYi Zou 641fdecf31bSYi Zou nm = kmalloc(sizeof(*nm), GFP_KERNEL); 642fdecf31bSYi Zou if (!nm) { 643fdecf31bSYi Zou printk(KERN_ERR "Unable to allocate netdev_mapping"); 644fdecf31bSYi Zou return -ENOMEM; 645fdecf31bSYi Zou } 646fdecf31bSYi Zou 647fdecf31bSYi Zou nm->netdev = netdev; 648fdecf31bSYi Zou nm->ft = ft; 649fdecf31bSYi Zou 65070be6344SBhanu Prakash Gollapudi mutex_lock(&fn_mutex); 651fdecf31bSYi Zou list_add(&nm->list, &fcoe_netdevs); 65270be6344SBhanu Prakash Gollapudi mutex_unlock(&fn_mutex); 653fdecf31bSYi Zou return 0; 654fdecf31bSYi Zou } 655fdecf31bSYi Zou 656fdecf31bSYi Zou 657fdecf31bSYi Zou static void fcoe_del_netdev_mapping(struct net_device *netdev) 658fdecf31bSYi Zou { 659fdecf31bSYi Zou struct fcoe_netdev_mapping *nm = NULL, *tmp; 660fdecf31bSYi Zou 66170be6344SBhanu Prakash Gollapudi mutex_lock(&fn_mutex); 662fdecf31bSYi Zou list_for_each_entry_safe(nm, tmp, &fcoe_netdevs, list) { 663fdecf31bSYi Zou if (nm->netdev == netdev) { 664fdecf31bSYi Zou list_del(&nm->list); 665fdecf31bSYi Zou kfree(nm); 66670be6344SBhanu Prakash Gollapudi mutex_unlock(&fn_mutex); 667fdecf31bSYi Zou return; 668fdecf31bSYi Zou } 669fdecf31bSYi Zou } 67070be6344SBhanu Prakash Gollapudi mutex_unlock(&fn_mutex); 671fdecf31bSYi Zou } 672fdecf31bSYi Zou 673fdecf31bSYi Zou 674fdecf31bSYi Zou /** 675fdecf31bSYi Zou * fcoe_netdev_map_lookup - find the fcoe transport that matches the netdev on which 676fdecf31bSYi Zou * it was created 677f2db5efeSLee Jones * @netdev: The net device that the FCoE interface is on 678fdecf31bSYi Zou * 679fdecf31bSYi Zou * Returns : ptr to the fcoe transport that supports this netdev or NULL 680fdecf31bSYi Zou * if not found. 681fdecf31bSYi Zou * 682fdecf31bSYi Zou * The ft_mutex should be held when this is called 683fdecf31bSYi Zou */ 684fdecf31bSYi Zou static struct fcoe_transport *fcoe_netdev_map_lookup(struct net_device *netdev) 685fdecf31bSYi Zou { 686fdecf31bSYi Zou struct fcoe_transport *ft = NULL; 687fdecf31bSYi Zou struct fcoe_netdev_mapping *nm; 688fdecf31bSYi Zou 68970be6344SBhanu Prakash Gollapudi mutex_lock(&fn_mutex); 690fdecf31bSYi Zou list_for_each_entry(nm, &fcoe_netdevs, list) { 691fdecf31bSYi Zou if (netdev == nm->netdev) { 692fdecf31bSYi Zou ft = nm->ft; 69370be6344SBhanu Prakash Gollapudi mutex_unlock(&fn_mutex); 694fdecf31bSYi Zou return ft; 695fdecf31bSYi Zou } 696fdecf31bSYi Zou } 697fdecf31bSYi Zou 69870be6344SBhanu Prakash Gollapudi mutex_unlock(&fn_mutex); 699fdecf31bSYi Zou return NULL; 700fdecf31bSYi Zou } 701fdecf31bSYi Zou 702fdecf31bSYi Zou /** 703fdecf31bSYi Zou * fcoe_if_to_netdev() - Parse a name buffer to get a net device 704fdecf31bSYi Zou * @buffer: The name of the net device 705fdecf31bSYi Zou * 706fdecf31bSYi Zou * Returns: NULL or a ptr to net_device 707fdecf31bSYi Zou */ 708fdecf31bSYi Zou static struct net_device *fcoe_if_to_netdev(const char *buffer) 709fdecf31bSYi Zou { 710fdecf31bSYi Zou char *cp; 711fdecf31bSYi Zou char ifname[IFNAMSIZ + 2]; 712fdecf31bSYi Zou 713fdecf31bSYi Zou if (buffer) { 714fdecf31bSYi Zou strlcpy(ifname, buffer, IFNAMSIZ); 715fdecf31bSYi Zou cp = ifname + strlen(ifname); 716fdecf31bSYi Zou while (--cp >= ifname && *cp == '\n') 717fdecf31bSYi Zou *cp = '\0'; 718fdecf31bSYi Zou return dev_get_by_name(&init_net, ifname); 719fdecf31bSYi Zou } 720fdecf31bSYi Zou return NULL; 721fdecf31bSYi Zou } 722fdecf31bSYi Zou 723fdecf31bSYi Zou /** 72470be6344SBhanu Prakash Gollapudi * libfcoe_device_notification() - Handler for net device events 72570be6344SBhanu Prakash Gollapudi * @notifier: The context of the notification 72670be6344SBhanu Prakash Gollapudi * @event: The type of event 72770be6344SBhanu Prakash Gollapudi * @ptr: The net device that the event was on 72870be6344SBhanu Prakash Gollapudi * 72970be6344SBhanu Prakash Gollapudi * This function is called by the Ethernet driver in case of link change event. 73070be6344SBhanu Prakash Gollapudi * 73170be6344SBhanu Prakash Gollapudi * Returns: 0 for success 73270be6344SBhanu Prakash Gollapudi */ 73370be6344SBhanu Prakash Gollapudi static int libfcoe_device_notification(struct notifier_block *notifier, 73470be6344SBhanu Prakash Gollapudi ulong event, void *ptr) 73570be6344SBhanu Prakash Gollapudi { 736351638e7SJiri Pirko struct net_device *netdev = netdev_notifier_info_to_dev(ptr); 73770be6344SBhanu Prakash Gollapudi 73870be6344SBhanu Prakash Gollapudi switch (event) { 73970be6344SBhanu Prakash Gollapudi case NETDEV_UNREGISTER: 740b99fbf6aSRobert Love LIBFCOE_TRANSPORT_DBG("NETDEV_UNREGISTER %s\n", 74170be6344SBhanu Prakash Gollapudi netdev->name); 74270be6344SBhanu Prakash Gollapudi fcoe_del_netdev_mapping(netdev); 74370be6344SBhanu Prakash Gollapudi break; 74470be6344SBhanu Prakash Gollapudi } 74570be6344SBhanu Prakash Gollapudi return NOTIFY_OK; 74670be6344SBhanu Prakash Gollapudi } 74770be6344SBhanu Prakash Gollapudi 748*75cff725SGreg Kroah-Hartman ssize_t fcoe_ctlr_create_store(const char *buf, size_t count) 7496a891b07SRobert Love { 7506a891b07SRobert Love struct net_device *netdev = NULL; 7516a891b07SRobert Love struct fcoe_transport *ft = NULL; 7526a891b07SRobert Love int rc = 0; 7536a891b07SRobert Love int err; 7546a891b07SRobert Love 7556a891b07SRobert Love mutex_lock(&ft_mutex); 7566a891b07SRobert Love 7576a891b07SRobert Love netdev = fcoe_if_to_netdev(buf); 7586a891b07SRobert Love if (!netdev) { 7596a891b07SRobert Love LIBFCOE_TRANSPORT_DBG("Invalid device %s.\n", buf); 7606a891b07SRobert Love rc = -ENODEV; 7616a891b07SRobert Love goto out_nodev; 7626a891b07SRobert Love } 7636a891b07SRobert Love 7646a891b07SRobert Love ft = fcoe_netdev_map_lookup(netdev); 7656a891b07SRobert Love if (ft) { 7666a891b07SRobert Love LIBFCOE_TRANSPORT_DBG("transport %s already has existing " 7676a891b07SRobert Love "FCoE instance on %s.\n", 7686a891b07SRobert Love ft->name, netdev->name); 7696a891b07SRobert Love rc = -EEXIST; 7706a891b07SRobert Love goto out_putdev; 7716a891b07SRobert Love } 7726a891b07SRobert Love 7736a891b07SRobert Love ft = fcoe_transport_lookup(netdev); 7746a891b07SRobert Love if (!ft) { 7756a891b07SRobert Love LIBFCOE_TRANSPORT_DBG("no FCoE transport found for %s.\n", 7766a891b07SRobert Love netdev->name); 7776a891b07SRobert Love rc = -ENODEV; 7786a891b07SRobert Love goto out_putdev; 7796a891b07SRobert Love } 7806a891b07SRobert Love 7816a891b07SRobert Love /* pass to transport create */ 7826a891b07SRobert Love err = ft->alloc ? ft->alloc(netdev) : -ENODEV; 7836a891b07SRobert Love if (err) { 7846a891b07SRobert Love fcoe_del_netdev_mapping(netdev); 7856a891b07SRobert Love rc = -ENOMEM; 7866a891b07SRobert Love goto out_putdev; 7876a891b07SRobert Love } 7886a891b07SRobert Love 7896a891b07SRobert Love err = fcoe_add_netdev_mapping(netdev, ft); 7906a891b07SRobert Love if (err) { 7916a891b07SRobert Love LIBFCOE_TRANSPORT_DBG("failed to add new netdev mapping " 7926a891b07SRobert Love "for FCoE transport %s for %s.\n", 7936a891b07SRobert Love ft->name, netdev->name); 7946a891b07SRobert Love rc = -ENODEV; 7956a891b07SRobert Love goto out_putdev; 7966a891b07SRobert Love } 7976a891b07SRobert Love 798a2ceb1fbSRobert Love LIBFCOE_TRANSPORT_DBG("transport %s succeeded to create fcoe on %s.\n", 799a2ceb1fbSRobert Love ft->name, netdev->name); 8006a891b07SRobert Love 8016a891b07SRobert Love out_putdev: 8026a891b07SRobert Love dev_put(netdev); 8036a891b07SRobert Love out_nodev: 8046a891b07SRobert Love mutex_unlock(&ft_mutex); 8056a891b07SRobert Love if (rc) 8066a891b07SRobert Love return rc; 8076a891b07SRobert Love return count; 8086a891b07SRobert Love } 8096a891b07SRobert Love 810*75cff725SGreg Kroah-Hartman ssize_t fcoe_ctlr_destroy_store(const char *buf, size_t count) 8116a891b07SRobert Love { 8126a891b07SRobert Love int rc = -ENODEV; 8136a891b07SRobert Love struct net_device *netdev = NULL; 8146a891b07SRobert Love struct fcoe_transport *ft = NULL; 8156a891b07SRobert Love 8166a891b07SRobert Love mutex_lock(&ft_mutex); 8176a891b07SRobert Love 8186a891b07SRobert Love netdev = fcoe_if_to_netdev(buf); 8196a891b07SRobert Love if (!netdev) { 8206a891b07SRobert Love LIBFCOE_TRANSPORT_DBG("invalid device %s.\n", buf); 8216a891b07SRobert Love goto out_nodev; 8226a891b07SRobert Love } 8236a891b07SRobert Love 8246a891b07SRobert Love ft = fcoe_netdev_map_lookup(netdev); 8256a891b07SRobert Love if (!ft) { 8266a891b07SRobert Love LIBFCOE_TRANSPORT_DBG("no FCoE transport found for %s.\n", 8276a891b07SRobert Love netdev->name); 8286a891b07SRobert Love goto out_putdev; 8296a891b07SRobert Love } 8306a891b07SRobert Love 8316a891b07SRobert Love /* pass to transport destroy */ 8326a891b07SRobert Love rc = ft->destroy(netdev); 8336a891b07SRobert Love if (rc) 8346a891b07SRobert Love goto out_putdev; 8356a891b07SRobert Love 8366a891b07SRobert Love fcoe_del_netdev_mapping(netdev); 8376a891b07SRobert Love LIBFCOE_TRANSPORT_DBG("transport %s %s to destroy fcoe on %s.\n", 8386a891b07SRobert Love ft->name, (rc) ? "failed" : "succeeded", 8396a891b07SRobert Love netdev->name); 8406a891b07SRobert Love rc = count; /* required for successful return */ 8416a891b07SRobert Love out_putdev: 8426a891b07SRobert Love dev_put(netdev); 8436a891b07SRobert Love out_nodev: 8446a891b07SRobert Love mutex_unlock(&ft_mutex); 8456a891b07SRobert Love return rc; 8466a891b07SRobert Love } 84770be6344SBhanu Prakash Gollapudi 84870be6344SBhanu Prakash Gollapudi /** 849fdecf31bSYi Zou * fcoe_transport_create() - Create a fcoe interface 850fdecf31bSYi Zou * @buffer: The name of the Ethernet interface to create on 851fdecf31bSYi Zou * @kp: The associated kernel param 852fdecf31bSYi Zou * 853fdecf31bSYi Zou * Called from sysfs. This holds the ft_mutex while calling the 854fdecf31bSYi Zou * registered fcoe transport's create function. 855fdecf31bSYi Zou * 856fdecf31bSYi Zou * Returns: 0 for success 857fdecf31bSYi Zou */ 858e4dca7b7SKees Cook static int fcoe_transport_create(const char *buffer, 859e4dca7b7SKees Cook const struct kernel_param *kp) 860fdecf31bSYi Zou { 861fdecf31bSYi Zou int rc = -ENODEV; 862fdecf31bSYi Zou struct net_device *netdev = NULL; 863fdecf31bSYi Zou struct fcoe_transport *ft = NULL; 864be5aeee3SBart Van Assche enum fip_mode fip_mode = (enum fip_mode)(uintptr_t)kp->arg; 865fdecf31bSYi Zou 866b3960afeSRobert Love mutex_lock(&ft_mutex); 867b3960afeSRobert Love 868fdecf31bSYi Zou netdev = fcoe_if_to_netdev(buffer); 869fdecf31bSYi Zou if (!netdev) { 870fdecf31bSYi Zou LIBFCOE_TRANSPORT_DBG("Invalid device %s.\n", buffer); 871fdecf31bSYi Zou goto out_nodev; 872fdecf31bSYi Zou } 873fdecf31bSYi Zou 874fdecf31bSYi Zou ft = fcoe_netdev_map_lookup(netdev); 875fdecf31bSYi Zou if (ft) { 876fdecf31bSYi Zou LIBFCOE_TRANSPORT_DBG("transport %s already has existing " 877fdecf31bSYi Zou "FCoE instance on %s.\n", 878fdecf31bSYi Zou ft->name, netdev->name); 879fdecf31bSYi Zou rc = -EEXIST; 880fdecf31bSYi Zou goto out_putdev; 881fdecf31bSYi Zou } 882fdecf31bSYi Zou 883fdecf31bSYi Zou ft = fcoe_transport_lookup(netdev); 884fdecf31bSYi Zou if (!ft) { 885fdecf31bSYi Zou LIBFCOE_TRANSPORT_DBG("no FCoE transport found for %s.\n", 886fdecf31bSYi Zou netdev->name); 887fdecf31bSYi Zou goto out_putdev; 888fdecf31bSYi Zou } 889fdecf31bSYi Zou 890fdecf31bSYi Zou rc = fcoe_add_netdev_mapping(netdev, ft); 891fdecf31bSYi Zou if (rc) { 892fdecf31bSYi Zou LIBFCOE_TRANSPORT_DBG("failed to add new netdev mapping " 893fdecf31bSYi Zou "for FCoE transport %s for %s.\n", 894fdecf31bSYi Zou ft->name, netdev->name); 895fdecf31bSYi Zou goto out_putdev; 896fdecf31bSYi Zou } 897fdecf31bSYi Zou 898fdecf31bSYi Zou /* pass to transport create */ 899fdecf31bSYi Zou rc = ft->create ? ft->create(netdev, fip_mode) : -ENODEV; 900fdecf31bSYi Zou if (rc) 901fdecf31bSYi Zou fcoe_del_netdev_mapping(netdev); 902fdecf31bSYi Zou 903fdecf31bSYi Zou LIBFCOE_TRANSPORT_DBG("transport %s %s to create fcoe on %s.\n", 904fdecf31bSYi Zou ft->name, (rc) ? "failed" : "succeeded", 905fdecf31bSYi Zou netdev->name); 906fdecf31bSYi Zou 907fdecf31bSYi Zou out_putdev: 908fdecf31bSYi Zou dev_put(netdev); 909fdecf31bSYi Zou out_nodev: 910fdecf31bSYi Zou mutex_unlock(&ft_mutex); 911fdecf31bSYi Zou return rc; 912fdecf31bSYi Zou } 913fdecf31bSYi Zou 914fdecf31bSYi Zou /** 915fdecf31bSYi Zou * fcoe_transport_destroy() - Destroy a FCoE interface 916fdecf31bSYi Zou * @buffer: The name of the Ethernet interface to be destroyed 917fdecf31bSYi Zou * @kp: The associated kernel parameter 918fdecf31bSYi Zou * 919fdecf31bSYi Zou * Called from sysfs. This holds the ft_mutex while calling the 920fdecf31bSYi Zou * registered fcoe transport's destroy function. 921fdecf31bSYi Zou * 922fdecf31bSYi Zou * Returns: 0 for success 923fdecf31bSYi Zou */ 924e4dca7b7SKees Cook static int fcoe_transport_destroy(const char *buffer, 925e4dca7b7SKees Cook const struct kernel_param *kp) 926fdecf31bSYi Zou { 927fdecf31bSYi Zou int rc = -ENODEV; 928fdecf31bSYi Zou struct net_device *netdev = NULL; 929fdecf31bSYi Zou struct fcoe_transport *ft = NULL; 930fdecf31bSYi Zou 931b3960afeSRobert Love mutex_lock(&ft_mutex); 932b3960afeSRobert Love 933fdecf31bSYi Zou netdev = fcoe_if_to_netdev(buffer); 934fdecf31bSYi Zou if (!netdev) { 935fdecf31bSYi Zou LIBFCOE_TRANSPORT_DBG("invalid device %s.\n", buffer); 936fdecf31bSYi Zou goto out_nodev; 937fdecf31bSYi Zou } 938fdecf31bSYi Zou 939fdecf31bSYi Zou ft = fcoe_netdev_map_lookup(netdev); 940fdecf31bSYi Zou if (!ft) { 941fdecf31bSYi Zou LIBFCOE_TRANSPORT_DBG("no FCoE transport found for %s.\n", 942fdecf31bSYi Zou netdev->name); 943fdecf31bSYi Zou goto out_putdev; 944fdecf31bSYi Zou } 945fdecf31bSYi Zou 946fdecf31bSYi Zou /* pass to transport destroy */ 947fdecf31bSYi Zou rc = ft->destroy ? ft->destroy(netdev) : -ENODEV; 948fdecf31bSYi Zou fcoe_del_netdev_mapping(netdev); 949fdecf31bSYi Zou LIBFCOE_TRANSPORT_DBG("transport %s %s to destroy fcoe on %s.\n", 950fdecf31bSYi Zou ft->name, (rc) ? "failed" : "succeeded", 951fdecf31bSYi Zou netdev->name); 952fdecf31bSYi Zou 953fdecf31bSYi Zou out_putdev: 954fdecf31bSYi Zou dev_put(netdev); 955fdecf31bSYi Zou out_nodev: 956fdecf31bSYi Zou mutex_unlock(&ft_mutex); 957fdecf31bSYi Zou return rc; 958fdecf31bSYi Zou } 959fdecf31bSYi Zou 960fdecf31bSYi Zou /** 961fdecf31bSYi Zou * fcoe_transport_disable() - Disables a FCoE interface 962fdecf31bSYi Zou * @buffer: The name of the Ethernet interface to be disabled 963fdecf31bSYi Zou * @kp: The associated kernel parameter 964fdecf31bSYi Zou * 965fdecf31bSYi Zou * Called from sysfs. 966fdecf31bSYi Zou * 967fdecf31bSYi Zou * Returns: 0 for success 968fdecf31bSYi Zou */ 969e4dca7b7SKees Cook static int fcoe_transport_disable(const char *buffer, 970e4dca7b7SKees Cook const struct kernel_param *kp) 971fdecf31bSYi Zou { 972fdecf31bSYi Zou int rc = -ENODEV; 973fdecf31bSYi Zou struct net_device *netdev = NULL; 974fdecf31bSYi Zou struct fcoe_transport *ft = NULL; 975fdecf31bSYi Zou 976b3960afeSRobert Love mutex_lock(&ft_mutex); 977b3960afeSRobert Love 978fdecf31bSYi Zou netdev = fcoe_if_to_netdev(buffer); 979fdecf31bSYi Zou if (!netdev) 980fdecf31bSYi Zou goto out_nodev; 981fdecf31bSYi Zou 982fdecf31bSYi Zou ft = fcoe_netdev_map_lookup(netdev); 983fdecf31bSYi Zou if (!ft) 984fdecf31bSYi Zou goto out_putdev; 985fdecf31bSYi Zou 986fdecf31bSYi Zou rc = ft->disable ? ft->disable(netdev) : -ENODEV; 987fdecf31bSYi Zou 988fdecf31bSYi Zou out_putdev: 989fdecf31bSYi Zou dev_put(netdev); 990fdecf31bSYi Zou out_nodev: 991fdecf31bSYi Zou mutex_unlock(&ft_mutex); 992fdecf31bSYi Zou return rc; 993fdecf31bSYi Zou } 994fdecf31bSYi Zou 995fdecf31bSYi Zou /** 996fdecf31bSYi Zou * fcoe_transport_enable() - Enables a FCoE interface 997fdecf31bSYi Zou * @buffer: The name of the Ethernet interface to be enabled 998fdecf31bSYi Zou * @kp: The associated kernel parameter 999fdecf31bSYi Zou * 1000fdecf31bSYi Zou * Called from sysfs. 1001fdecf31bSYi Zou * 1002fdecf31bSYi Zou * Returns: 0 for success 1003fdecf31bSYi Zou */ 1004e4dca7b7SKees Cook static int fcoe_transport_enable(const char *buffer, 1005e4dca7b7SKees Cook const struct kernel_param *kp) 1006fdecf31bSYi Zou { 1007fdecf31bSYi Zou int rc = -ENODEV; 1008fdecf31bSYi Zou struct net_device *netdev = NULL; 1009fdecf31bSYi Zou struct fcoe_transport *ft = NULL; 1010fdecf31bSYi Zou 1011b3960afeSRobert Love mutex_lock(&ft_mutex); 1012b3960afeSRobert Love 1013fdecf31bSYi Zou netdev = fcoe_if_to_netdev(buffer); 1014fdecf31bSYi Zou if (!netdev) 1015fdecf31bSYi Zou goto out_nodev; 1016fdecf31bSYi Zou 1017fdecf31bSYi Zou ft = fcoe_netdev_map_lookup(netdev); 1018fdecf31bSYi Zou if (!ft) 1019fdecf31bSYi Zou goto out_putdev; 1020fdecf31bSYi Zou 1021fdecf31bSYi Zou rc = ft->enable ? ft->enable(netdev) : -ENODEV; 1022fdecf31bSYi Zou 1023fdecf31bSYi Zou out_putdev: 1024fdecf31bSYi Zou dev_put(netdev); 1025fdecf31bSYi Zou out_nodev: 1026fdecf31bSYi Zou mutex_unlock(&ft_mutex); 1027fdecf31bSYi Zou return rc; 1028fdecf31bSYi Zou } 1029fdecf31bSYi Zou 1030fdecf31bSYi Zou /** 1031fdecf31bSYi Zou * libfcoe_init() - Initialization routine for libfcoe.ko 1032fdecf31bSYi Zou */ 1033fdecf31bSYi Zou static int __init libfcoe_init(void) 1034fdecf31bSYi Zou { 10359a74e884SRobert Love int rc = 0; 1036fdecf31bSYi Zou 10379a74e884SRobert Love rc = fcoe_transport_init(); 10389a74e884SRobert Love if (rc) 10399a74e884SRobert Love return rc; 10409a74e884SRobert Love 10419a74e884SRobert Love rc = fcoe_sysfs_setup(); 10429a74e884SRobert Love if (rc) 10439a74e884SRobert Love fcoe_transport_exit(); 10449a74e884SRobert Love 10459a74e884SRobert Love return rc; 1046fdecf31bSYi Zou } 1047fdecf31bSYi Zou module_init(libfcoe_init); 1048fdecf31bSYi Zou 1049fdecf31bSYi Zou /** 1050fdecf31bSYi Zou * libfcoe_exit() - Tear down libfcoe.ko 1051fdecf31bSYi Zou */ 1052fdecf31bSYi Zou static void __exit libfcoe_exit(void) 1053fdecf31bSYi Zou { 10549a74e884SRobert Love fcoe_sysfs_teardown(); 1055fdecf31bSYi Zou fcoe_transport_exit(); 1056fdecf31bSYi Zou } 1057fdecf31bSYi Zou module_exit(libfcoe_exit); 1058