X-Git-Url: https://git.openpandora.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=drivers%2Fscsi%2Ffcoe%2Ffcoe.c;h=43added0a17c557541df7df0903fa2633a1487d5;hb=dfc1d0fe3a8b2139295600ab519f24059493e6f6;hp=c15878e881570e6ae6344b92869b1457840e7f69;hpb=187f81b3d8d315c35c73ac0d05b15a04a0ac3ce7;p=pandora-kernel.git diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index c15878e88157..43added0a17c 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2007 - 2008 Intel Corporation. All rights reserved. + * Copyright(c) 2007 - 2009 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -45,12 +45,18 @@ #include "fcoe.h" -static int debug_fcoe; - MODULE_AUTHOR("Open-FCoE.org"); MODULE_DESCRIPTION("FCoE"); MODULE_LICENSE("GPL v2"); +/* Performance tuning parameters for fcoe */ +static unsigned int fcoe_ddp_min; +module_param_named(ddp_min, fcoe_ddp_min, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(ddp_min, "Minimum I/O size in bytes for " \ + "Direct Data Placement (DDP)."); + +DEFINE_MUTEX(fcoe_config_mutex); + /* fcoe host list */ LIST_HEAD(fcoe_hostlist); DEFINE_RWLOCK(fcoe_hostlist_lock); @@ -134,6 +140,178 @@ static struct scsi_host_template fcoe_shost_template = { .max_sectors = 0xffff, }; +static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *ptype, + struct net_device *orig_dev); +/** + * fcoe_interface_setup() + * @fcoe: new fcoe_interface + * @netdev : ptr to the associated netdevice struct + * + * Returns : 0 for success + */ +static int fcoe_interface_setup(struct fcoe_interface *fcoe, + struct net_device *netdev) +{ + struct fcoe_ctlr *fip = &fcoe->ctlr; + struct netdev_hw_addr *ha; + u8 flogi_maddr[ETH_ALEN]; + + fcoe->netdev = netdev; + + /* Do not support for bonding device */ + if ((netdev->priv_flags & IFF_MASTER_ALB) || + (netdev->priv_flags & IFF_SLAVE_INACTIVE) || + (netdev->priv_flags & IFF_MASTER_8023AD)) { + return -EOPNOTSUPP; + } + + /* look for SAN MAC address, if multiple SAN MACs exist, only + * use the first one for SPMA */ + rcu_read_lock(); + for_each_dev_addr(netdev, ha) { + if ((ha->type == NETDEV_HW_ADDR_T_SAN) && + (is_valid_ether_addr(fip->ctl_src_addr))) { + memcpy(fip->ctl_src_addr, ha->addr, ETH_ALEN); + fip->spma = 1; + break; + } + } + rcu_read_unlock(); + + /* setup Source Mac Address */ + if (!fip->spma) + memcpy(fip->ctl_src_addr, netdev->dev_addr, netdev->addr_len); + + /* + * Add FCoE MAC address as second unicast MAC address + * or enter promiscuous mode if not capable of listening + * for multiple unicast MACs. + */ + rtnl_lock(); + memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN); + dev_unicast_add(netdev, flogi_maddr); + if (fip->spma) + dev_unicast_add(netdev, fip->ctl_src_addr); + dev_mc_add(netdev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0); + rtnl_unlock(); + + /* + * setup the receive function from ethernet driver + * on the ethertype for the given device + */ + fcoe->fcoe_packet_type.func = fcoe_rcv; + fcoe->fcoe_packet_type.type = __constant_htons(ETH_P_FCOE); + fcoe->fcoe_packet_type.dev = netdev; + dev_add_pack(&fcoe->fcoe_packet_type); + + fcoe->fip_packet_type.func = fcoe_fip_recv; + fcoe->fip_packet_type.type = htons(ETH_P_FIP); + fcoe->fip_packet_type.dev = netdev; + dev_add_pack(&fcoe->fip_packet_type); + + return 0; +} + +static void fcoe_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb); +static void fcoe_update_src_mac(struct fcoe_ctlr *fip, u8 *old, u8 *new); + +/** + * fcoe_interface_create() + * @netdev: network interface + * + * Returns: pointer to a struct fcoe_interface or NULL on error + */ +static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev) +{ + struct fcoe_interface *fcoe; + + fcoe = kzalloc(sizeof(*fcoe), GFP_KERNEL); + if (!fcoe) { + FCOE_NETDEV_DBG(netdev, "Could not allocate fcoe structure\n"); + return NULL; + } + + kref_init(&fcoe->kref); + + /* + * Initialize FIP. + */ + fcoe_ctlr_init(&fcoe->ctlr); + fcoe->ctlr.send = fcoe_fip_send; + fcoe->ctlr.update_mac = fcoe_update_src_mac; + + fcoe_interface_setup(fcoe, netdev); + + return fcoe; +} + +/** + * fcoe_interface_cleanup() - clean up netdev configurations + * @fcoe: + */ +void fcoe_interface_cleanup(struct fcoe_interface *fcoe) +{ + struct net_device *netdev = fcoe->netdev; + struct fcoe_ctlr *fip = &fcoe->ctlr; + u8 flogi_maddr[ETH_ALEN]; + + /* + * Don't listen for Ethernet packets anymore. + * synchronize_net() ensures that the packet handlers are not running + * on another CPU. dev_remove_pack() would do that, this calls the + * unsyncronized version __dev_remove_pack() to avoid multiple delays. + */ + __dev_remove_pack(&fcoe->fcoe_packet_type); + __dev_remove_pack(&fcoe->fip_packet_type); + synchronize_net(); + + /* tear-down the FCoE controller */ + fcoe_ctlr_destroy(&fcoe->ctlr); + + /* Delete secondary MAC addresses */ + rtnl_lock(); + memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN); + dev_unicast_delete(netdev, flogi_maddr); + if (!is_zero_ether_addr(fip->data_src_addr)) + dev_unicast_delete(netdev, fip->data_src_addr); + if (fip->spma) + dev_unicast_delete(netdev, fip->ctl_src_addr); + dev_mc_delete(netdev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0); + rtnl_unlock(); +} + +/** + * fcoe_interface_release() - fcoe_port kref release function + * @kref: embedded reference count in an fcoe_interface struct + */ +static void fcoe_interface_release(struct kref *kref) +{ + struct fcoe_interface *fcoe; + + fcoe = container_of(kref, struct fcoe_interface, kref); + fcoe_interface_cleanup(fcoe); + kfree(fcoe); +} + +/** + * fcoe_interface_get() + * @fcoe: + */ +static inline void fcoe_interface_get(struct fcoe_interface *fcoe) +{ + kref_get(&fcoe->kref); +} + +/** + * fcoe_interface_put() + * @fcoe: + */ +static inline void fcoe_interface_put(struct fcoe_interface *fcoe) +{ + kref_put(&fcoe->kref, fcoe_interface_release); +} + /** * fcoe_fip_recv - handle a received FIP frame. * @skb: the receive skb @@ -147,10 +325,10 @@ static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *dev, struct packet_type *ptype, struct net_device *orig_dev) { - struct fcoe_softc *fc; + struct fcoe_interface *fcoe; - fc = container_of(ptype, struct fcoe_softc, fip_packet_type); - fcoe_ctlr_recv(&fc->ctlr, skb); + fcoe = container_of(ptype, struct fcoe_interface, fip_packet_type); + fcoe_ctlr_recv(&fcoe->ctlr, skb); return 0; } @@ -161,7 +339,7 @@ static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *dev, */ static void fcoe_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb) { - skb->dev = fcoe_from_ctlr(fip)->real_dev; + skb->dev = fcoe_from_ctlr(fip)->netdev; dev_queue_xmit(skb); } @@ -176,13 +354,13 @@ static void fcoe_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb) */ static void fcoe_update_src_mac(struct fcoe_ctlr *fip, u8 *old, u8 *new) { - struct fcoe_softc *fc; + struct fcoe_interface *fcoe; - fc = fcoe_from_ctlr(fip); + fcoe = fcoe_from_ctlr(fip); rtnl_lock(); if (!is_zero_ether_addr(old)) - dev_unicast_delete(fc->real_dev, old); - dev_unicast_add(fc->real_dev, new); + dev_unicast_delete(fcoe->netdev, old); + dev_unicast_add(fcoe->netdev, new); rtnl_unlock(); } @@ -218,30 +396,6 @@ static int fcoe_lport_config(struct fc_lport *lp) return 0; } -/** - * fcoe_netdev_cleanup() - clean up netdev configurations - * @fc: ptr to the fcoe_softc - */ -void fcoe_netdev_cleanup(struct fcoe_softc *fc) -{ - u8 flogi_maddr[ETH_ALEN]; - - /* Don't listen for Ethernet packets anymore */ - dev_remove_pack(&fc->fcoe_packet_type); - dev_remove_pack(&fc->fip_packet_type); - - /* Delete secondary MAC addresses */ - rtnl_lock(); - memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN); - dev_unicast_delete(fc->real_dev, flogi_maddr); - if (!is_zero_ether_addr(fc->ctlr.data_src_addr)) - dev_unicast_delete(fc->real_dev, fc->ctlr.data_src_addr); - if (fc->ctlr.spma) - dev_unicast_delete(fc->real_dev, fc->ctlr.ctl_src_addr); - dev_mc_delete(fc->real_dev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0); - rtnl_unlock(); -} - /** * fcoe_queue_timer() - fcoe queue timer * @lp: the fc_lport pointer @@ -267,117 +421,53 @@ static int fcoe_netdev_config(struct fc_lport *lp, struct net_device *netdev) { u32 mfs; u64 wwnn, wwpn; - struct fcoe_softc *fc; - u8 flogi_maddr[ETH_ALEN]; - struct netdev_hw_addr *ha; + struct fcoe_interface *fcoe; + struct fcoe_port *port; /* Setup lport private data to point to fcoe softc */ - fc = lport_priv(lp); - fc->ctlr.lp = lp; - fc->real_dev = netdev; - fc->phys_dev = netdev; - - /* Require support for get_pauseparam ethtool op. */ - if (netdev->priv_flags & IFF_802_1Q_VLAN) - fc->phys_dev = vlan_dev_real_dev(netdev); - - /* Do not support for bonding device */ - if ((fc->real_dev->priv_flags & IFF_MASTER_ALB) || - (fc->real_dev->priv_flags & IFF_SLAVE_INACTIVE) || - (fc->real_dev->priv_flags & IFF_MASTER_8023AD)) { - return -EOPNOTSUPP; - } + port = lport_priv(lp); + fcoe = port->fcoe; /* * Determine max frame size based on underlying device and optional * user-configured limit. If the MFS is too low, fcoe_link_ok() * will return 0, so do this first. */ - mfs = fc->real_dev->mtu - (sizeof(struct fcoe_hdr) + - sizeof(struct fcoe_crc_eof)); + mfs = netdev->mtu - (sizeof(struct fcoe_hdr) + + sizeof(struct fcoe_crc_eof)); if (fc_set_mfs(lp, mfs)) return -EINVAL; /* offload features support */ - if (fc->real_dev->features & NETIF_F_SG) + if (netdev->features & NETIF_F_SG) lp->sg_supp = 1; -#ifdef NETIF_F_FCOE_CRC if (netdev->features & NETIF_F_FCOE_CRC) { lp->crc_offload = 1; - printk(KERN_DEBUG "fcoe:%s supports FCCRC offload\n", - netdev->name); + FCOE_NETDEV_DBG(netdev, "Supports FCCRC offload\n"); } -#endif -#ifdef NETIF_F_FSO if (netdev->features & NETIF_F_FSO) { lp->seq_offload = 1; lp->lso_max = netdev->gso_max_size; - printk(KERN_DEBUG "fcoe:%s supports LSO for max len 0x%x\n", - netdev->name, lp->lso_max); + FCOE_NETDEV_DBG(netdev, "Supports LSO for max len 0x%x\n", + lp->lso_max); } -#endif if (netdev->fcoe_ddp_xid) { lp->lro_enabled = 1; lp->lro_xid = netdev->fcoe_ddp_xid; - printk(KERN_DEBUG "fcoe:%s supports LRO for max xid 0x%x\n", - netdev->name, lp->lro_xid); + FCOE_NETDEV_DBG(netdev, "Supports LRO for max xid 0x%x\n", + lp->lro_xid); } - skb_queue_head_init(&fc->fcoe_pending_queue); - fc->fcoe_pending_queue_active = 0; - setup_timer(&fc->timer, fcoe_queue_timer, (unsigned long)lp); + skb_queue_head_init(&port->fcoe_pending_queue); + port->fcoe_pending_queue_active = 0; + setup_timer(&port->timer, fcoe_queue_timer, (unsigned long)lp); - /* look for SAN MAC address, if multiple SAN MACs exist, only - * use the first one for SPMA */ - rcu_read_lock(); - for_each_dev_addr(netdev, ha) { - if ((ha->type == NETDEV_HW_ADDR_T_SAN) && - (is_valid_ether_addr(fc->ctlr.ctl_src_addr))) { - memcpy(fc->ctlr.ctl_src_addr, ha->addr, ETH_ALEN); - fc->ctlr.spma = 1; - break; - } - } - rcu_read_unlock(); - - /* setup Source Mac Address */ - if (!fc->ctlr.spma) - memcpy(fc->ctlr.ctl_src_addr, fc->real_dev->dev_addr, - fc->real_dev->addr_len); - - wwnn = fcoe_wwn_from_mac(fc->real_dev->dev_addr, 1, 0); + wwnn = fcoe_wwn_from_mac(netdev->dev_addr, 1, 0); fc_set_wwnn(lp, wwnn); /* XXX - 3rd arg needs to be vlan id */ - wwpn = fcoe_wwn_from_mac(fc->real_dev->dev_addr, 2, 0); + wwpn = fcoe_wwn_from_mac(netdev->dev_addr, 2, 0); fc_set_wwpn(lp, wwpn); - /* - * Add FCoE MAC address as second unicast MAC address - * or enter promiscuous mode if not capable of listening - * for multiple unicast MACs. - */ - rtnl_lock(); - memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN); - dev_unicast_add(fc->real_dev, flogi_maddr); - if (fc->ctlr.spma) - dev_unicast_add(fc->real_dev, fc->ctlr.ctl_src_addr); - dev_mc_add(fc->real_dev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0); - rtnl_unlock(); - - /* - * setup the receive function from ethernet driver - * on the ethertype for the given device - */ - fc->fcoe_packet_type.func = fcoe_rcv; - fc->fcoe_packet_type.type = __constant_htons(ETH_P_FCOE); - fc->fcoe_packet_type.dev = fc->real_dev; - dev_add_pack(&fc->fcoe_packet_type); - - fc->fip_packet_type.func = fcoe_fip_recv; - fc->fip_packet_type.type = htons(ETH_P_FIP); - fc->fip_packet_type.dev = fc->real_dev; - dev_add_pack(&fc->fip_packet_type); - return 0; } @@ -407,7 +497,8 @@ static int fcoe_shost_config(struct fc_lport *lp, struct Scsi_Host *shost, /* add the new host to the SCSI-ml */ rc = scsi_add_host(lp->host, dev); if (rc) { - FC_DBG("fcoe_shost_config:error on scsi_add_host\n"); + FCOE_NETDEV_DBG(fcoe_netdev(lp), "fcoe_shost_config: " + "error on scsi_add_host\n"); return rc; } sprintf(fc_host_symbolic_name(lp->host), "%s v%s over %s", @@ -417,87 +508,146 @@ static int fcoe_shost_config(struct fc_lport *lp, struct Scsi_Host *shost, return 0; } +/* + * fcoe_oem_match() - match for read types IO + * @fp: the fc_frame for new IO. + * + * Returns : true for read types IO, otherwise returns false. + */ +bool fcoe_oem_match(struct fc_frame *fp) +{ + return fc_fcp_is_read(fr_fsp(fp)) && + (fr_fsp(fp)->data_len > fcoe_ddp_min); +} + /** * fcoe_em_config() - allocates em for this lport - * @lp: the port that em is to allocated for + * @lp: the fcoe that em is to allocated for + * + * Called with write fcoe_hostlist_lock held. * * Returns : 0 on success */ static inline int fcoe_em_config(struct fc_lport *lp) { - BUG_ON(lp->emp); + struct fcoe_port *port = lport_priv(lp); + struct fcoe_interface *fcoe = port->fcoe; + struct fcoe_interface *oldfcoe = NULL; + struct net_device *old_real_dev, *cur_real_dev; + u16 min_xid = FCOE_MIN_XID; + u16 max_xid = FCOE_MAX_XID; + + /* + * Check if need to allocate an em instance for + * offload exchange ids to be shared across all VN_PORTs/lport. + */ + if (!lp->lro_enabled || !lp->lro_xid || (lp->lro_xid >= max_xid)) { + lp->lro_xid = 0; + goto skip_oem; + } + + /* + * Reuse existing offload em instance in case + * it is already allocated on real eth device + */ + if (fcoe->netdev->priv_flags & IFF_802_1Q_VLAN) + cur_real_dev = vlan_dev_real_dev(fcoe->netdev); + else + cur_real_dev = fcoe->netdev; + + list_for_each_entry(oldfcoe, &fcoe_hostlist, list) { + if (oldfcoe->netdev->priv_flags & IFF_802_1Q_VLAN) + old_real_dev = vlan_dev_real_dev(oldfcoe->netdev); + else + old_real_dev = oldfcoe->netdev; + + if (cur_real_dev == old_real_dev) { + fcoe->oem = oldfcoe->oem; + break; + } + } + + if (fcoe->oem) { + if (!fc_exch_mgr_add(lp, fcoe->oem, fcoe_oem_match)) { + printk(KERN_ERR "fcoe_em_config: failed to add " + "offload em:%p on interface:%s\n", + fcoe->oem, fcoe->netdev->name); + return -ENOMEM; + } + } else { + fcoe->oem = fc_exch_mgr_alloc(lp, FC_CLASS_3, + FCOE_MIN_XID, lp->lro_xid, + fcoe_oem_match); + if (!fcoe->oem) { + printk(KERN_ERR "fcoe_em_config: failed to allocate " + "em for offload exches on interface:%s\n", + fcoe->netdev->name); + return -ENOMEM; + } + } - lp->emp = fc_exch_mgr_alloc(lp, FC_CLASS_3, - FCOE_MIN_XID, FCOE_MAX_XID); - if (!lp->emp) + /* + * Exclude offload EM xid range from next EM xid range. + */ + min_xid += lp->lro_xid + 1; + +skip_oem: + if (!fc_exch_mgr_alloc(lp, FC_CLASS_3, min_xid, max_xid, NULL)) { + printk(KERN_ERR "fcoe_em_config: failed to " + "allocate em on interface %s\n", fcoe->netdev->name); return -ENOMEM; + } return 0; } /** * fcoe_if_destroy() - FCoE software HBA tear-down function - * @netdev: ptr to the associated net_device - * - * Returns: 0 if link is OK for use by FCoE. + * @lport: fc_lport to destroy */ -static int fcoe_if_destroy(struct net_device *netdev) +static void fcoe_if_destroy(struct fc_lport *lport) { - struct fc_lport *lp = NULL; - struct fcoe_softc *fc; + struct fcoe_port *port = lport_priv(lport); + struct fcoe_interface *fcoe = port->fcoe; + struct net_device *netdev = fcoe->netdev; - BUG_ON(!netdev); - - printk(KERN_DEBUG "fcoe_if_destroy:interface on %s\n", - netdev->name); - - lp = fcoe_hostlist_lookup(netdev); - if (!lp) - return -ENODEV; - - fc = lport_priv(lp); + FCOE_NETDEV_DBG(netdev, "Destroying interface\n"); /* Logout of the fabric */ - fc_fabric_logoff(lp); + fc_fabric_logoff(lport); /* Remove the instance from fcoe's list */ - fcoe_hostlist_remove(lp); - - /* clean up netdev configurations */ - fcoe_netdev_cleanup(fc); - - /* tear-down the FCoE controller */ - fcoe_ctlr_destroy(&fc->ctlr); + fcoe_hostlist_remove(lport); /* Cleanup the fc_lport */ - fc_lport_destroy(lp); - fc_fcp_destroy(lp); + fc_lport_destroy(lport); + fc_fcp_destroy(lport); - /* Detach from the scsi-ml */ - fc_remove_host(lp->host); - scsi_remove_host(lp->host); + /* Stop the transmit retry timer */ + del_timer_sync(&port->timer); - /* There are no more rports or I/O, free the EM */ - if (lp->emp) - fc_exch_mgr_free(lp->emp); + /* Free existing transmit skbs */ + fcoe_clean_pending_queue(lport); - /* Free the per-CPU receive threads */ - fcoe_percpu_clean(lp); + /* receives may not be stopped until after this */ + fcoe_interface_put(fcoe); - /* Free existing skbs */ - fcoe_clean_pending_queue(lp); + /* Free queued packets for the per-CPU receive threads */ + fcoe_percpu_clean(lport); - /* Stop the timer */ - del_timer_sync(&fc->timer); + /* Detach from the scsi-ml */ + fc_remove_host(lport->host); + scsi_remove_host(lport->host); + + /* There are no more rports or I/O, free the EM */ + fc_exch_mgr_free(lport); /* Free memory used by statistical counters */ - fc_lport_free_stats(lp); + fc_lport_free_stats(lport); /* Release the net_device and Scsi_Host */ - dev_put(fc->real_dev); - scsi_host_put(lp->host); - - return 0; + dev_put(netdev); + scsi_host_put(lport->host); } /* @@ -543,102 +693,97 @@ static struct libfc_function_template fcoe_libfc_fcn_templ = { }; /** - * fcoe_if_create() - this function creates the fcoe interface - * @netdev: pointer the associated netdevice + * fcoe_if_create() - this function creates the fcoe port + * @fcoe: fcoe_interface structure to create an fc_lport instance on + * @parent: device pointer to be the parent in sysfs for the SCSI host * - * Creates fc_lport struct and scsi_host for lport, configures lport - * and starts fabric login. + * Creates fc_lport struct and scsi_host for lport, configures lport. * - * Returns : 0 on success + * Returns : The allocated fc_lport or an error pointer */ -static int fcoe_if_create(struct net_device *netdev) +static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe, + struct device *parent) { int rc; - struct fc_lport *lp = NULL; - struct fcoe_softc *fc; + struct fc_lport *lport = NULL; + struct fcoe_port *port; struct Scsi_Host *shost; + struct net_device *netdev = fcoe->netdev; - BUG_ON(!netdev); - - printk(KERN_DEBUG "fcoe_if_create:interface on %s\n", - netdev->name); - - lp = fcoe_hostlist_lookup(netdev); - if (lp) - return -EEXIST; + FCOE_NETDEV_DBG(netdev, "Create Interface\n"); shost = libfc_host_alloc(&fcoe_shost_template, - sizeof(struct fcoe_softc)); + sizeof(struct fcoe_port)); if (!shost) { - FC_DBG("Could not allocate host structure\n"); - return -ENOMEM; + FCOE_NETDEV_DBG(netdev, "Could not allocate host structure\n"); + rc = -ENOMEM; + goto out; } - lp = shost_priv(shost); - fc = lport_priv(lp); + lport = shost_priv(shost); + port = lport_priv(lport); + port->fcoe = fcoe; /* configure fc_lport, e.g., em */ - rc = fcoe_lport_config(lp); + rc = fcoe_lport_config(lport); if (rc) { - FC_DBG("Could not configure lport\n"); + FCOE_NETDEV_DBG(netdev, "Could not configure lport for the " + "interface\n"); goto out_host_put; } - /* - * Initialize FIP. - */ - fcoe_ctlr_init(&fc->ctlr); - fc->ctlr.send = fcoe_fip_send; - fc->ctlr.update_mac = fcoe_update_src_mac; - /* configure lport network properties */ - rc = fcoe_netdev_config(lp, netdev); + rc = fcoe_netdev_config(lport, netdev); if (rc) { - FC_DBG("Could not configure netdev for the interface\n"); - goto out_netdev_cleanup; + FCOE_NETDEV_DBG(netdev, "Could not configure netdev for the " + "interface\n"); + goto out_lp_destroy; } /* configure lport scsi host properties */ - rc = fcoe_shost_config(lp, shost, &netdev->dev); + rc = fcoe_shost_config(lport, shost, parent); if (rc) { - FC_DBG("Could not configure shost for lport\n"); - goto out_netdev_cleanup; + FCOE_NETDEV_DBG(netdev, "Could not configure shost for the " + "interface\n"); + goto out_lp_destroy; } - /* lport exch manager allocation */ - rc = fcoe_em_config(lp); + /* Initialize the library */ + rc = fcoe_libfc_config(lport, &fcoe_libfc_fcn_templ); if (rc) { - FC_DBG("Could not configure em for lport\n"); - goto out_netdev_cleanup; + FCOE_NETDEV_DBG(netdev, "Could not configure libfc for the " + "interface\n"); + goto out_lp_destroy; } - /* Initialize the library */ - rc = fcoe_libfc_config(lp, &fcoe_libfc_fcn_templ); + /* + * fcoe_em_alloc() and fcoe_hostlist_add() both + * need to be atomic under fcoe_hostlist_lock + * since fcoe_em_alloc() looks for an existing EM + * instance on host list updated by fcoe_hostlist_add(). + */ + write_lock(&fcoe_hostlist_lock); + /* lport exch manager allocation */ + rc = fcoe_em_config(lport); if (rc) { - FC_DBG("Could not configure libfc for lport!\n"); + FCOE_NETDEV_DBG(netdev, "Could not configure the EM for the " + "interface\n"); goto out_lp_destroy; } /* add to lports list */ - fcoe_hostlist_add(lp); - - lp->boot_time = jiffies; - - fc_fabric_login(lp); - - if (!fcoe_link_ok(lp)) - fcoe_ctlr_link_up(&fc->ctlr); + fcoe_hostlist_add(lport); + write_unlock(&fcoe_hostlist_lock); dev_hold(netdev); - - return rc; + fcoe_interface_get(fcoe); + return lport; out_lp_destroy: - fc_exch_mgr_free(lp->emp); /* Free the EM */ -out_netdev_cleanup: - fcoe_netdev_cleanup(fc); + fc_exch_mgr_free(lport); out_host_put: - scsi_host_put(lp->host); - return rc; + scsi_host_put(lport->host); +out: + return ERR_PTR(rc); } /** @@ -653,7 +798,7 @@ static int __init fcoe_if_init(void) fc_attach_transport(&fcoe_transport_function); if (!scsi_transport_fcoe_sw) { - printk(KERN_ERR "fcoe_init:fc_attach_transport() failed\n"); + printk(KERN_ERR "fcoe: Failed to attach to the FC transport\n"); return -ENODEV; } @@ -668,6 +813,7 @@ static int __init fcoe_if_init(void) int __exit fcoe_if_exit(void) { fc_release_transport(scsi_transport_fcoe_sw); + scsi_transport_fcoe_sw = NULL; return 0; } @@ -714,7 +860,7 @@ static void fcoe_percpu_thread_destroy(unsigned int cpu) unsigned targ_cpu = smp_processor_id(); #endif /* CONFIG_SMP */ - printk(KERN_DEBUG "fcoe: Destroying receive thread for CPU %d\n", cpu); + FCOE_DBG("Destroying receive thread for CPU %d\n", cpu); /* Prevent any new skbs from being queued for this CPU. */ p = &per_cpu(fcoe_percpu, cpu); @@ -736,8 +882,8 @@ static void fcoe_percpu_thread_destroy(unsigned int cpu) p0 = &per_cpu(fcoe_percpu, targ_cpu); spin_lock_bh(&p0->fcoe_rx_list.lock); if (p0->thread) { - FC_DBG("Moving frames from CPU %d to CPU %d\n", - cpu, targ_cpu); + FCOE_DBG("Moving frames from CPU %d to CPU %d\n", + cpu, targ_cpu); while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL) __skb_queue_tail(&p0->fcoe_rx_list, skb); @@ -803,12 +949,12 @@ static int fcoe_cpu_callback(struct notifier_block *nfb, switch (action) { case CPU_ONLINE: case CPU_ONLINE_FROZEN: - FC_DBG("CPU %x online: Create Rx thread\n", cpu); + FCOE_DBG("CPU %x online: Create Rx thread\n", cpu); fcoe_percpu_thread_create(cpu); break; case CPU_DEAD: case CPU_DEAD_FROZEN: - FC_DBG("CPU %x offline: Remove Rx thread\n", cpu); + FCOE_DBG("CPU %x offline: Remove Rx thread\n", cpu); fcoe_percpu_thread_destroy(cpu); break; default: @@ -837,33 +983,29 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev, { struct fc_lport *lp; struct fcoe_rcv_info *fr; - struct fcoe_softc *fc; + struct fcoe_interface *fcoe; struct fc_frame_header *fh; struct fcoe_percpu_s *fps; - unsigned short oxid; - unsigned int cpu = 0; + unsigned int cpu; - fc = container_of(ptype, struct fcoe_softc, fcoe_packet_type); - lp = fc->ctlr.lp; + fcoe = container_of(ptype, struct fcoe_interface, fcoe_packet_type); + lp = fcoe->ctlr.lp; if (unlikely(lp == NULL)) { - FC_DBG("cannot find hba structure"); + FCOE_NETDEV_DBG(dev, "Cannot find hba structure"); goto err2; } if (!lp->link_up) goto err2; - if (unlikely(debug_fcoe)) { - FC_DBG("skb_info: len:%d data_len:%d head:%p data:%p tail:%p " - "end:%p sum:%d dev:%s", skb->len, skb->data_len, - skb->head, skb->data, skb_tail_pointer(skb), - skb_end_pointer(skb), skb->csum, - skb->dev ? skb->dev->name : ""); - - } + FCOE_NETDEV_DBG(dev, "skb_info: len:%d data_len:%d head:%p " + "data:%p tail:%p end:%p sum:%d dev:%s", + skb->len, skb->data_len, skb->head, skb->data, + skb_tail_pointer(skb), skb_end_pointer(skb), + skb->csum, skb->dev ? skb->dev->name : ""); /* check for FCOE packet type */ if (unlikely(eth_hdr(skb)->h_proto != htons(ETH_P_FCOE))) { - FC_DBG("wrong FC type frame"); + FCOE_NETDEV_DBG(dev, "Wrong FC type frame"); goto err; } @@ -878,20 +1020,20 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev, skb_set_transport_header(skb, sizeof(struct fcoe_hdr)); fh = (struct fc_frame_header *) skb_transport_header(skb); - oxid = ntohs(fh->fh_ox_id); - fr = fcoe_dev_from_skb(skb); fr->fr_dev = lp; fr->ptype = ptype; -#ifdef CONFIG_SMP /* - * The incoming frame exchange id(oxid) is ANDed with num of online - * cpu bits to get cpu and then this cpu is used for selecting - * a per cpu kernel thread from fcoe_percpu. + * In case the incoming frame's exchange is originated from + * the initiator, then received frame's exchange id is ANDed + * with fc_cpu_mask bits to get the same cpu on which exchange + * was originated, otherwise just use the current cpu. */ - cpu = oxid & (num_online_cpus() - 1); -#endif + if (ntoh24(fh->fh_f_ctl) & FC_FC_EX_CTX) + cpu = ntohs(fh->fh_ox_id) & fc_cpu_mask; + else + cpu = smp_processor_id(); fps = &per_cpu(fcoe_percpu, cpu); spin_lock_bh(&fps->fcoe_rx_list.lock); @@ -901,8 +1043,9 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev, * the first CPU now. For non-SMP systems this * will check the same CPU twice. */ - FC_DBG("CPU is online, but no receive thread ready " - "for incoming skb- using first online CPU.\n"); + FCOE_NETDEV_DBG(dev, "CPU is online, but no receive thread " + "ready for incoming skb- using first online " + "CPU.\n"); spin_unlock_bh(&fps->fcoe_rx_list.lock); cpu = first_cpu(cpu_online_map); @@ -997,7 +1140,7 @@ static int fcoe_get_paged_crc_eof(struct sk_buff *skb, int tlen) * fcoe_fc_crc() - calculates FC CRC in this fcoe skb * @fp: the fc_frame containing data to be checksummed * - * This uses crc32() to calculate the crc for fc frame + * This uses crc32() to calculate the crc for port frame * Return : 32 bit crc */ u32 fcoe_fc_crc(struct fc_frame *fp) @@ -1030,7 +1173,7 @@ u32 fcoe_fc_crc(struct fc_frame *fp) /** * fcoe_xmit() - FCoE frame transmit function - * @lp: the associated local port + * @lp: the associated local fcoe * @fp: the fc_frame to be transmitted * * Return : 0 for success @@ -1047,13 +1190,13 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp) unsigned int hlen; /* header length implies the version */ unsigned int tlen; /* trailer length */ unsigned int elen; /* eth header, may include vlan */ - struct fcoe_softc *fc; + struct fcoe_port *port = lport_priv(lp); + struct fcoe_interface *fcoe = port->fcoe; u8 sof, eof; struct fcoe_hdr *hp; WARN_ON((fr_len(fp) % sizeof(u32)) != 0); - fc = lport_priv(lp); fh = fc_frame_header_get(fp); skb = fp_skb(fp); wlen = skb->len / FCOE_WORD_TO_BYTE; @@ -1064,7 +1207,7 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp) } if (unlikely(fh->fh_r_ctl == FC_RCTL_ELS_REQ) && - fcoe_ctlr_els_send(&fc->ctlr, skb)) + fcoe_ctlr_els_send(&fcoe->ctlr, skb)) return 0; sof = fr_sof(fp); @@ -1086,7 +1229,7 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp) crc = fcoe_fc_crc(fp); } - /* copy fc crc and eof to the skb buff */ + /* copy port crc and eof to the skb buff */ if (skb_is_nonlinear(skb)) { skb_frag_t *frag; if (fcoe_get_paged_crc_eof(skb, tlen)) { @@ -1109,27 +1252,27 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp) cp = NULL; } - /* adjust skb network/transport offsets to match mac/fcoe/fc */ + /* adjust skb network/transport offsets to match mac/fcoe/port */ skb_push(skb, elen + hlen); skb_reset_mac_header(skb); skb_reset_network_header(skb); skb->mac_len = elen; skb->protocol = htons(ETH_P_FCOE); - skb->dev = fc->real_dev; + skb->dev = fcoe->netdev; /* fill up mac and fcoe headers */ eh = eth_hdr(skb); eh->h_proto = htons(ETH_P_FCOE); - if (fc->ctlr.map_dest) + if (fcoe->ctlr.map_dest) fc_fcoe_set_mac(eh->h_dest, fh->fh_d_id); else /* insert GW address */ - memcpy(eh->h_dest, fc->ctlr.dest_addr, ETH_ALEN); + memcpy(eh->h_dest, fcoe->ctlr.dest_addr, ETH_ALEN); - if (unlikely(fc->ctlr.flogi_oxid != FC_XID_UNKNOWN)) - memcpy(eh->h_source, fc->ctlr.ctl_src_addr, ETH_ALEN); + if (unlikely(fcoe->ctlr.flogi_oxid != FC_XID_UNKNOWN)) + memcpy(eh->h_source, fcoe->ctlr.ctl_src_addr, ETH_ALEN); else - memcpy(eh->h_source, fc->ctlr.data_src_addr, ETH_ALEN); + memcpy(eh->h_source, fcoe->ctlr.data_src_addr, ETH_ALEN); hp = (struct fcoe_hdr *)(eh + 1); memset(hp, 0, sizeof(*hp)); @@ -1137,7 +1280,6 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp) FC_FCOE_ENCAPS_VER(hp, FC_FCOE_VER); hp->fcoe_sof = sof; -#ifdef NETIF_F_FSO /* fcoe lso, mss is in max_payload which is non-zero for FCP data */ if (lp->seq_offload && fr_max_payload(fp)) { skb_shinfo(skb)->gso_type = SKB_GSO_FCOE; @@ -1146,7 +1288,6 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp) skb_shinfo(skb)->gso_type = 0; skb_shinfo(skb)->gso_size = 0; } -#endif /* update tx stats: regardless if LLD fails */ stats = fc_lport_get_stats(lp); stats->TxFrames++; @@ -1154,7 +1295,7 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp) /* send down to lld */ fr_dev(fp) = lp; - if (fc->fcoe_pending_queue.qlen) + if (port->fcoe_pending_queue.qlen) fcoe_check_wait_queue(lp, skb); else if (fcoe_start_io(skb)) fcoe_check_wait_queue(lp, skb); @@ -1180,7 +1321,7 @@ int fcoe_percpu_receive_thread(void *arg) struct fcoe_crc_eof crc_eof; struct fc_frame *fp; u8 *mac = NULL; - struct fcoe_softc *fc; + struct fcoe_port *port; struct fcoe_hdr *hp; set_user_nice(current, -20); @@ -1201,24 +1342,22 @@ int fcoe_percpu_receive_thread(void *arg) fr = fcoe_dev_from_skb(skb); lp = fr->fr_dev; if (unlikely(lp == NULL)) { - FC_DBG("invalid HBA Structure"); + FCOE_NETDEV_DBG(skb->dev, "Invalid HBA Structure"); kfree_skb(skb); continue; } - if (unlikely(debug_fcoe)) { - FC_DBG("skb_info: len:%d data_len:%d head:%p data:%p " - "tail:%p end:%p sum:%d dev:%s", - skb->len, skb->data_len, - skb->head, skb->data, skb_tail_pointer(skb), - skb_end_pointer(skb), skb->csum, - skb->dev ? skb->dev->name : ""); - } + FCOE_NETDEV_DBG(skb->dev, "skb_info: len:%d data_len:%d " + "head:%p data:%p tail:%p end:%p sum:%d dev:%s", + skb->len, skb->data_len, + skb->head, skb->data, skb_tail_pointer(skb), + skb_end_pointer(skb), skb->csum, + skb->dev ? skb->dev->name : ""); /* * Save source MAC address before discarding header. */ - fc = lport_priv(lp); + port = lport_priv(lp); if (skb_is_nonlinear(skb)) skb_linearize(skb); /* not ideal */ mac = eth_hdr(skb)->h_source; @@ -1233,7 +1372,7 @@ int fcoe_percpu_receive_thread(void *arg) stats = fc_lport_get_stats(lp); if (unlikely(FC_FCOE_DECAPS_VER(hp) != FC_FCOE_VER)) { if (stats->ErrorFrames < 5) - printk(KERN_WARNING "FCoE version " + printk(KERN_WARNING "fcoe: FCoE version " "mismatch: The frame has " "version %x, but the " "initiator supports version " @@ -1280,13 +1419,13 @@ int fcoe_percpu_receive_thread(void *arg) fh = fc_frame_header_get(fp); if (fh->fh_r_ctl == FC_RCTL_DD_SOL_DATA && fh->fh_type == FC_TYPE_FCP) { - fc_exch_recv(lp, lp->emp, fp); + fc_exch_recv(lp, fp); continue; } if (fr_flags(fp) & FCPHF_CRC_UNCHECKED) { if (le32_to_cpu(fr_crc(fp)) != ~crc32(~0, skb->data, fr_len)) { - if (debug_fcoe || stats->InvalidCRCCount < 5) + if (stats->InvalidCRCCount < 5) printk(KERN_WARNING "fcoe: dropping " "frame with CRC error\n"); stats->InvalidCRCCount++; @@ -1296,12 +1435,12 @@ int fcoe_percpu_receive_thread(void *arg) } fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED; } - if (unlikely(fc->ctlr.flogi_oxid != FC_XID_UNKNOWN) && - fcoe_ctlr_recv_flogi(&fc->ctlr, fp, mac)) { + if (unlikely(port->fcoe->ctlr.flogi_oxid != FC_XID_UNKNOWN) && + fcoe_ctlr_recv_flogi(&port->fcoe->ctlr, fp, mac)) { fc_frame_free(fp); continue; } - fc_exch_recv(lp, lp->emp, fp); + fc_exch_recv(lp, fp); } return 0; } @@ -1321,46 +1460,46 @@ int fcoe_percpu_receive_thread(void *arg) */ static void fcoe_check_wait_queue(struct fc_lport *lp, struct sk_buff *skb) { - struct fcoe_softc *fc = lport_priv(lp); + struct fcoe_port *port = lport_priv(lp); int rc; - spin_lock_bh(&fc->fcoe_pending_queue.lock); + spin_lock_bh(&port->fcoe_pending_queue.lock); if (skb) - __skb_queue_tail(&fc->fcoe_pending_queue, skb); + __skb_queue_tail(&port->fcoe_pending_queue, skb); - if (fc->fcoe_pending_queue_active) + if (port->fcoe_pending_queue_active) goto out; - fc->fcoe_pending_queue_active = 1; + port->fcoe_pending_queue_active = 1; - while (fc->fcoe_pending_queue.qlen) { + while (port->fcoe_pending_queue.qlen) { /* keep qlen > 0 until fcoe_start_io succeeds */ - fc->fcoe_pending_queue.qlen++; - skb = __skb_dequeue(&fc->fcoe_pending_queue); + port->fcoe_pending_queue.qlen++; + skb = __skb_dequeue(&port->fcoe_pending_queue); - spin_unlock_bh(&fc->fcoe_pending_queue.lock); + spin_unlock_bh(&port->fcoe_pending_queue.lock); rc = fcoe_start_io(skb); - spin_lock_bh(&fc->fcoe_pending_queue.lock); + spin_lock_bh(&port->fcoe_pending_queue.lock); if (rc) { - __skb_queue_head(&fc->fcoe_pending_queue, skb); + __skb_queue_head(&port->fcoe_pending_queue, skb); /* undo temporary increment above */ - fc->fcoe_pending_queue.qlen--; + port->fcoe_pending_queue.qlen--; break; } /* undo temporary increment above */ - fc->fcoe_pending_queue.qlen--; + port->fcoe_pending_queue.qlen--; } - if (fc->fcoe_pending_queue.qlen < FCOE_LOW_QUEUE_DEPTH) + if (port->fcoe_pending_queue.qlen < FCOE_LOW_QUEUE_DEPTH) lp->qfull = 0; - if (fc->fcoe_pending_queue.qlen && !timer_pending(&fc->timer)) - mod_timer(&fc->timer, jiffies + 2); - fc->fcoe_pending_queue_active = 0; + if (port->fcoe_pending_queue.qlen && !timer_pending(&port->timer)) + mod_timer(&port->timer, jiffies + 2); + port->fcoe_pending_queue_active = 0; out: - if (fc->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH) + if (port->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH) lp->qfull = 1; - spin_unlock_bh(&fc->fcoe_pending_queue.lock); + spin_unlock_bh(&port->fcoe_pending_queue.lock); return; } @@ -1394,17 +1533,17 @@ static int fcoe_device_notification(struct notifier_block *notifier, ulong event, void *ptr) { struct fc_lport *lp = NULL; - struct net_device *real_dev = ptr; - struct fcoe_softc *fc; + struct net_device *netdev = ptr; + struct fcoe_interface *fcoe; struct fcoe_dev_stats *stats; u32 link_possible = 1; u32 mfs; int rc = NOTIFY_OK; read_lock(&fcoe_hostlist_lock); - list_for_each_entry(fc, &fcoe_hostlist, list) { - if (fc->real_dev == real_dev) { - lp = fc->ctlr.lp; + list_for_each_entry(fcoe, &fcoe_hostlist, list) { + if (fcoe->netdev == netdev) { + lp = fcoe->ctlr.lp; break; } } @@ -1423,20 +1562,20 @@ static int fcoe_device_notification(struct notifier_block *notifier, case NETDEV_CHANGE: break; case NETDEV_CHANGEMTU: - mfs = fc->real_dev->mtu - - (sizeof(struct fcoe_hdr) + - sizeof(struct fcoe_crc_eof)); + mfs = netdev->mtu - (sizeof(struct fcoe_hdr) + + sizeof(struct fcoe_crc_eof)); if (mfs >= FC_MIN_MAX_FRAME) fc_set_mfs(lp, mfs); break; case NETDEV_REGISTER: break; default: - FC_DBG("Unknown event %ld from netdev netlink\n", event); + FCOE_NETDEV_DBG(netdev, "Unknown event %ld " + "from netdev netlink\n", event); } if (link_possible && !fcoe_link_ok(lp)) - fcoe_ctlr_link_up(&fc->ctlr); - else if (fcoe_ctlr_link_down(&fc->ctlr)) { + fcoe_ctlr_link_up(&fcoe->ctlr); + else if (fcoe_ctlr_link_down(&fcoe->ctlr)) { stats = fc_lport_get_stats(lp); stats->LinkFailureCount++; fcoe_clean_pending_queue(lp); @@ -1505,8 +1644,8 @@ static int fcoe_ethdrv_get(const struct net_device *netdev) owner = fcoe_netdev_to_module_owner(netdev); if (owner) { - printk(KERN_DEBUG "fcoe:hold driver module %s for %s\n", - module_name(owner), netdev->name); + FCOE_NETDEV_DBG(netdev, "Hold driver module %s\n", + module_name(owner)); return try_module_get(owner); } return -ENODEV; @@ -1527,8 +1666,8 @@ static int fcoe_ethdrv_put(const struct net_device *netdev) owner = fcoe_netdev_to_module_owner(netdev); if (owner) { - printk(KERN_DEBUG "fcoe:release driver module %s for %s\n", - module_name(owner), netdev->name); + FCOE_NETDEV_DBG(netdev, "Release driver module %s\n", + module_name(owner)); module_put(owner); return 0; } @@ -1544,8 +1683,24 @@ static int fcoe_ethdrv_put(const struct net_device *netdev) */ static int fcoe_destroy(const char *buffer, struct kernel_param *kp) { - int rc; struct net_device *netdev; + struct fcoe_interface *fcoe; + struct fcoe_port *port; + struct fc_lport *lport; + int rc; + + mutex_lock(&fcoe_config_mutex); +#ifdef CONFIG_FCOE_MODULE + /* + * Make sure the module has been initialized, and is not about to be + * removed. Module paramter sysfs files are writable before the + * module_init function is called and after module_exit. + */ + if (THIS_MODULE->state != MODULE_STATE_LIVE) { + rc = -ENODEV; + goto out_nodev; + } +#endif netdev = fcoe_if_to_netdev(buffer); if (!netdev) { @@ -1553,22 +1708,20 @@ static int fcoe_destroy(const char *buffer, struct kernel_param *kp) goto out_nodev; } /* look for existing lport */ - if (!fcoe_hostlist_lookup(netdev)) { + lport = fcoe_hostlist_lookup(netdev); + if (!lport) { rc = -ENODEV; goto out_putdev; } - rc = fcoe_if_destroy(netdev); - if (rc) { - printk(KERN_ERR "fcoe: fcoe_if_destroy(%s) failed\n", - netdev->name); - rc = -EIO; - goto out_putdev; - } + port = lport_priv(lport); + fcoe = port->fcoe; + fcoe_if_destroy(lport); fcoe_ethdrv_put(netdev); rc = 0; out_putdev: dev_put(netdev); out_nodev: + mutex_unlock(&fcoe_config_mutex); return rc; } @@ -1582,8 +1735,23 @@ out_nodev: static int fcoe_create(const char *buffer, struct kernel_param *kp) { int rc; + struct fcoe_interface *fcoe; + struct fc_lport *lport; struct net_device *netdev; + mutex_lock(&fcoe_config_mutex); +#ifdef CONFIG_FCOE_MODULE + /* + * Make sure the module has been initialized, and is not about to be + * removed. Module paramter sysfs files are writable before the + * module_init function is called and after module_exit. + */ + if (THIS_MODULE->state != MODULE_STATE_LIVE) { + rc = -ENODEV; + goto out_nodev; + } +#endif + netdev = fcoe_if_to_netdev(buffer); if (!netdev) { rc = -ENODEV; @@ -1596,27 +1764,50 @@ static int fcoe_create(const char *buffer, struct kernel_param *kp) } fcoe_ethdrv_get(netdev); - rc = fcoe_if_create(netdev); - if (rc) { - printk(KERN_ERR "fcoe: fcoe_if_create(%s) failed\n", + fcoe = fcoe_interface_create(netdev); + if (!fcoe) { + rc = -ENOMEM; + goto out_putdev; + } + + lport = fcoe_if_create(fcoe, &netdev->dev); + if (IS_ERR(lport)) { + printk(KERN_ERR "fcoe: Failed to create interface (%s)\n", netdev->name); fcoe_ethdrv_put(netdev); rc = -EIO; - goto out_putdev; + goto out_free; } + + /* Make this the "master" N_Port */ + fcoe->ctlr.lp = lport; + + /* start FIP Discovery and FLOGI */ + lport->boot_time = jiffies; + fc_fabric_login(lport); + if (!fcoe_link_ok(lport)) + fcoe_ctlr_link_up(&fcoe->ctlr); + rc = 0; +out_free: + /* + * Release from init in fcoe_interface_create(), on success lport + * should be holding a reference taken in fcoe_if_create(). + */ + fcoe_interface_put(fcoe); out_putdev: dev_put(netdev); out_nodev: + mutex_unlock(&fcoe_config_mutex); return rc; } module_param_call(create, fcoe_create, NULL, NULL, S_IWUSR); __MODULE_PARM_TYPE(create, "string"); -MODULE_PARM_DESC(create, "Create fcoe port using net device passed in."); +MODULE_PARM_DESC(create, "Create fcoe fcoe using net device passed in."); module_param_call(destroy, fcoe_destroy, NULL, NULL, S_IWUSR); __MODULE_PARM_TYPE(destroy, "string"); -MODULE_PARM_DESC(destroy, "Destroy fcoe port"); +MODULE_PARM_DESC(destroy, "Destroy fcoe fcoe"); /** * fcoe_link_ok() - Check if link is ok for the fc_lport @@ -1634,32 +1825,28 @@ MODULE_PARM_DESC(destroy, "Destroy fcoe port"); */ int fcoe_link_ok(struct fc_lport *lp) { - struct fcoe_softc *fc = lport_priv(lp); - struct net_device *dev = fc->real_dev; + struct fcoe_port *port = lport_priv(lp); + struct net_device *dev = port->fcoe->netdev; struct ethtool_cmd ecmd = { ETHTOOL_GSET }; - int rc = 0; - if ((dev->flags & IFF_UP) && netif_carrier_ok(dev)) { - dev = fc->phys_dev; - if (dev->ethtool_ops->get_settings) { - dev->ethtool_ops->get_settings(dev, &ecmd); - lp->link_supported_speeds &= - ~(FC_PORTSPEED_1GBIT | FC_PORTSPEED_10GBIT); - if (ecmd.supported & (SUPPORTED_1000baseT_Half | - SUPPORTED_1000baseT_Full)) - lp->link_supported_speeds |= FC_PORTSPEED_1GBIT; - if (ecmd.supported & SUPPORTED_10000baseT_Full) - lp->link_supported_speeds |= - FC_PORTSPEED_10GBIT; - if (ecmd.speed == SPEED_1000) - lp->link_speed = FC_PORTSPEED_1GBIT; - if (ecmd.speed == SPEED_10000) - lp->link_speed = FC_PORTSPEED_10GBIT; - } - } else - rc = -1; + if ((dev->flags & IFF_UP) && netif_carrier_ok(dev) && + (!dev_ethtool_get_settings(dev, &ecmd))) { + lp->link_supported_speeds &= + ~(FC_PORTSPEED_1GBIT | FC_PORTSPEED_10GBIT); + if (ecmd.supported & (SUPPORTED_1000baseT_Half | + SUPPORTED_1000baseT_Full)) + lp->link_supported_speeds |= FC_PORTSPEED_1GBIT; + if (ecmd.supported & SUPPORTED_10000baseT_Full) + lp->link_supported_speeds |= + FC_PORTSPEED_10GBIT; + if (ecmd.speed == SPEED_1000) + lp->link_speed = FC_PORTSPEED_1GBIT; + if (ecmd.speed == SPEED_10000) + lp->link_speed = FC_PORTSPEED_10GBIT; - return rc; + return 0; + } + return -1; } /** @@ -1701,16 +1888,16 @@ void fcoe_percpu_clean(struct fc_lport *lp) */ void fcoe_clean_pending_queue(struct fc_lport *lp) { - struct fcoe_softc *fc = lport_priv(lp); + struct fcoe_port *port = lport_priv(lp); struct sk_buff *skb; - spin_lock_bh(&fc->fcoe_pending_queue.lock); - while ((skb = __skb_dequeue(&fc->fcoe_pending_queue)) != NULL) { - spin_unlock_bh(&fc->fcoe_pending_queue.lock); + spin_lock_bh(&port->fcoe_pending_queue.lock); + while ((skb = __skb_dequeue(&port->fcoe_pending_queue)) != NULL) { + spin_unlock_bh(&port->fcoe_pending_queue.lock); kfree_skb(skb); - spin_lock_bh(&fc->fcoe_pending_queue.lock); + spin_lock_bh(&port->fcoe_pending_queue.lock); } - spin_unlock_bh(&fc->fcoe_pending_queue.lock); + spin_unlock_bh(&port->fcoe_pending_queue.lock); } /** @@ -1727,24 +1914,22 @@ int fcoe_reset(struct Scsi_Host *shost) } /** - * fcoe_hostlist_lookup_softc() - find the corresponding lport by a given device + * fcoe_hostlist_lookup_port() - find the corresponding lport by a given device * @dev: this is currently ptr to net_device * - * Returns: NULL or the located fcoe_softc + * Called with fcoe_hostlist_lock held. + * + * Returns: NULL or the located fcoe_port */ -static struct fcoe_softc * -fcoe_hostlist_lookup_softc(const struct net_device *dev) +static struct fcoe_interface * +fcoe_hostlist_lookup_port(const struct net_device *dev) { - struct fcoe_softc *fc; + struct fcoe_interface *fcoe; - read_lock(&fcoe_hostlist_lock); - list_for_each_entry(fc, &fcoe_hostlist, list) { - if (fc->real_dev == dev) { - read_unlock(&fcoe_hostlist_lock); - return fc; - } + list_for_each_entry(fcoe, &fcoe_hostlist, list) { + if (fcoe->netdev == dev) + return fcoe; } - read_unlock(&fcoe_hostlist_lock); return NULL; } @@ -1756,29 +1941,33 @@ fcoe_hostlist_lookup_softc(const struct net_device *dev) */ struct fc_lport *fcoe_hostlist_lookup(const struct net_device *netdev) { - struct fcoe_softc *fc; + struct fcoe_interface *fcoe; - fc = fcoe_hostlist_lookup_softc(netdev); + read_lock(&fcoe_hostlist_lock); + fcoe = fcoe_hostlist_lookup_port(netdev); + read_unlock(&fcoe_hostlist_lock); - return (fc) ? fc->ctlr.lp : NULL; + return (fcoe) ? fcoe->ctlr.lp : NULL; } /** * fcoe_hostlist_add() - Add a lport to lports list * @lp: ptr to the fc_lport to be added * + * Called with write fcoe_hostlist_lock held. + * * Returns: 0 for success */ -int fcoe_hostlist_add(const struct fc_lport *lp) +int fcoe_hostlist_add(const struct fc_lport *lport) { - struct fcoe_softc *fc; - - fc = fcoe_hostlist_lookup_softc(fcoe_netdev(lp)); - if (!fc) { - fc = lport_priv(lp); - write_lock_bh(&fcoe_hostlist_lock); - list_add_tail(&fc->list, &fcoe_hostlist); - write_unlock_bh(&fcoe_hostlist_lock); + struct fcoe_interface *fcoe; + struct fcoe_port *port; + + fcoe = fcoe_hostlist_lookup_port(fcoe_netdev(lport)); + if (!fcoe) { + port = lport_priv(lport); + fcoe = port->fcoe; + list_add_tail(&fcoe->list, &fcoe_hostlist); } return 0; } @@ -1789,14 +1978,14 @@ int fcoe_hostlist_add(const struct fc_lport *lp) * * Returns: 0 for success */ -int fcoe_hostlist_remove(const struct fc_lport *lp) +int fcoe_hostlist_remove(const struct fc_lport *lport) { - struct fcoe_softc *fc; + struct fcoe_interface *fcoe; - fc = fcoe_hostlist_lookup_softc(fcoe_netdev(lp)); - BUG_ON(!fc); write_lock_bh(&fcoe_hostlist_lock); - list_del(&fc->list); + fcoe = fcoe_hostlist_lookup_port(fcoe_netdev(lport)); + BUG_ON(!fcoe); + list_del(&fcoe->list); write_unlock_bh(&fcoe_hostlist_lock); return 0; @@ -1813,8 +2002,7 @@ static int __init fcoe_init(void) int rc = 0; struct fcoe_percpu_s *p; - INIT_LIST_HEAD(&fcoe_hostlist); - rwlock_init(&fcoe_hostlist_lock); + mutex_lock(&fcoe_config_mutex); for_each_possible_cpu(cpu) { p = &per_cpu(fcoe_percpu, cpu); @@ -1832,15 +2020,18 @@ static int __init fcoe_init(void) /* Setup link change notification */ fcoe_dev_setup(); - fcoe_if_init(); + rc = fcoe_if_init(); + if (rc) + goto out_free; + mutex_unlock(&fcoe_config_mutex); return 0; out_free: for_each_online_cpu(cpu) { fcoe_percpu_thread_destroy(cpu); } - + mutex_unlock(&fcoe_config_mutex); return rc; } module_init(fcoe_init); @@ -1853,21 +2044,24 @@ module_init(fcoe_init); static void __exit fcoe_exit(void) { unsigned int cpu; - struct fcoe_softc *fc, *tmp; + struct fcoe_interface *fcoe, *tmp; + + mutex_lock(&fcoe_config_mutex); fcoe_dev_cleanup(); /* releases the associated fcoe hosts */ - list_for_each_entry_safe(fc, tmp, &fcoe_hostlist, list) - fcoe_if_destroy(fc->real_dev); + list_for_each_entry_safe(fcoe, tmp, &fcoe_hostlist, list) + fcoe_if_destroy(fcoe->ctlr.lp); unregister_hotcpu_notifier(&fcoe_cpu_notifier); - for_each_online_cpu(cpu) { + for_each_online_cpu(cpu) fcoe_percpu_thread_destroy(cpu); - } /* detach from scsi transport */ fcoe_if_exit(); + + mutex_unlock(&fcoe_config_mutex); } module_exit(fcoe_exit);