diff options
author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2012-05-24 12:13:01 +0400 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2012-05-24 12:13:01 +0400 |
commit | e644dae645e167d154c0526358940986682a72b0 (patch) | |
tree | 972993c6568085b8d407fc7e13de10f4b93c651d /drivers/net/ethernet/ibm | |
parent | 899c612d74d4a242158a4db20367388d6299c028 (diff) | |
parent | 86809173ce32ef03bd4d0389dfc72df0c805e9c4 (diff) | |
download | linux-e644dae645e167d154c0526358940986682a72b0.tar.xz |
Merge branch 'next' into for-linus
Diffstat (limited to 'drivers/net/ethernet/ibm')
27 files changed, 74 insertions, 1764 deletions
diff --git a/drivers/net/ethernet/ibm/Kconfig b/drivers/net/ethernet/ibm/Kconfig index 9e16f3fa97b2..b9773d229192 100644 --- a/drivers/net/ethernet/ibm/Kconfig +++ b/drivers/net/ethernet/ibm/Kconfig @@ -29,10 +29,6 @@ config IBMVETH To compile this driver as a module, choose M here. The module will be called ibmveth. -config ISERIES_VETH - tristate "iSeries Virtual Ethernet driver support" - depends on PPC_ISERIES - source "drivers/net/ethernet/ibm/emac/Kconfig" config EHEA diff --git a/drivers/net/ethernet/ibm/Makefile b/drivers/net/ethernet/ibm/Makefile index 5a7d4e9ac803..2f04e71a5926 100644 --- a/drivers/net/ethernet/ibm/Makefile +++ b/drivers/net/ethernet/ibm/Makefile @@ -3,6 +3,5 @@ # obj-$(CONFIG_IBMVETH) += ibmveth.o -obj-$(CONFIG_ISERIES_VETH) += iseries_veth.o obj-$(CONFIG_IBM_EMAC) += emac/ obj-$(CONFIG_EHEA) += ehea/ diff --git a/drivers/net/ethernet/ibm/ehea/ehea.h b/drivers/net/ethernet/ibm/ehea/ehea.h index 6650068c996c..b8e46cc31e53 100644 --- a/drivers/net/ethernet/ibm/ehea/ehea.h +++ b/drivers/net/ethernet/ibm/ehea/ehea.h @@ -1,5 +1,5 @@ /* - * linux/drivers/net/ehea/ehea.h + * linux/drivers/net/ethernet/ibm/ehea/ehea.h * * eHEA ethernet device driver for IBM eServer System p * diff --git a/drivers/net/ethernet/ibm/ehea/ehea_ethtool.c b/drivers/net/ethernet/ibm/ehea/ehea_ethtool.c index 6bdd8e36e564..95837b99a464 100644 --- a/drivers/net/ethernet/ibm/ehea/ehea_ethtool.c +++ b/drivers/net/ethernet/ibm/ehea/ehea_ethtool.c @@ -1,5 +1,5 @@ /* - * linux/drivers/net/ehea/ehea_ethtool.c + * linux/drivers/net/ethernet/ibm/ehea/ehea_ethtool.c * * eHEA ethernet device driver for IBM eServer System p * diff --git a/drivers/net/ethernet/ibm/ehea/ehea_hw.h b/drivers/net/ethernet/ibm/ehea/ehea_hw.h index 1a2fe4dc3eb3..180d4128a711 100644 --- a/drivers/net/ethernet/ibm/ehea/ehea_hw.h +++ b/drivers/net/ethernet/ibm/ehea/ehea_hw.h @@ -1,5 +1,5 @@ /* - * linux/drivers/net/ehea/ehea_hw.h + * linux/drivers/net/ethernet/ibm/ehea/ehea_hw.h * * eHEA ethernet device driver for IBM eServer System p * diff --git a/drivers/net/ethernet/ibm/ehea/ehea_main.c b/drivers/net/ethernet/ibm/ehea/ehea_main.c index 5d5fb2627184..3516e17a399d 100644 --- a/drivers/net/ethernet/ibm/ehea/ehea_main.c +++ b/drivers/net/ethernet/ibm/ehea/ehea_main.c @@ -1,5 +1,5 @@ /* - * linux/drivers/net/ehea/ehea_main.c + * linux/drivers/net/ethernet/ibm/ehea/ehea_main.c * * eHEA ethernet device driver for IBM eServer System p * @@ -336,7 +336,9 @@ static struct rtnl_link_stats64 *ehea_get_stats64(struct net_device *dev, stats->tx_bytes = tx_bytes; stats->rx_packets = rx_packets; - return &port->stats; + stats->multicast = port->stats.multicast; + stats->rx_errors = port->stats.rx_errors; + return stats; } static void ehea_update_stats(struct work_struct *work) @@ -2980,7 +2982,6 @@ static struct ehea_port *ehea_setup_single_port(struct ehea_adapter *adapter, dev = alloc_etherdev_mq(sizeof(struct ehea_port), EHEA_MAX_PORT_RES); if (!dev) { - pr_err("no mem for net_device\n"); ret = -ENOMEM; goto out_err; } diff --git a/drivers/net/ethernet/ibm/ehea/ehea_phyp.c b/drivers/net/ethernet/ibm/ehea/ehea_phyp.c index 0506967b9044..30f903332e92 100644 --- a/drivers/net/ethernet/ibm/ehea/ehea_phyp.c +++ b/drivers/net/ethernet/ibm/ehea/ehea_phyp.c @@ -1,5 +1,5 @@ /* - * linux/drivers/net/ehea/ehea_phyp.c + * linux/drivers/net/ethernet/ibm/ehea/ehea_phyp.c * * eHEA ethernet device driver for IBM eServer System p * diff --git a/drivers/net/ethernet/ibm/ehea/ehea_phyp.h b/drivers/net/ethernet/ibm/ehea/ehea_phyp.h index 2f8174c248bc..52c456ec4d6c 100644 --- a/drivers/net/ethernet/ibm/ehea/ehea_phyp.h +++ b/drivers/net/ethernet/ibm/ehea/ehea_phyp.h @@ -1,5 +1,5 @@ /* - * linux/drivers/net/ehea/ehea_phyp.h + * linux/drivers/net/ethernet/ibm/ehea/ehea_phyp.h * * eHEA ethernet device driver for IBM eServer System p * diff --git a/drivers/net/ethernet/ibm/ehea/ehea_qmr.c b/drivers/net/ethernet/ibm/ehea/ehea_qmr.c index c25b05b94daa..4fb47f14dbfe 100644 --- a/drivers/net/ethernet/ibm/ehea/ehea_qmr.c +++ b/drivers/net/ethernet/ibm/ehea/ehea_qmr.c @@ -1,5 +1,5 @@ /* - * linux/drivers/net/ehea/ehea_qmr.c + * linux/drivers/net/ethernet/ibm/ehea/ehea_qmr.c * * eHEA ethernet device driver for IBM eServer System p * diff --git a/drivers/net/ethernet/ibm/ehea/ehea_qmr.h b/drivers/net/ethernet/ibm/ehea/ehea_qmr.h index 337a47ecf4aa..8e4a70c20ab7 100644 --- a/drivers/net/ethernet/ibm/ehea/ehea_qmr.h +++ b/drivers/net/ethernet/ibm/ehea/ehea_qmr.h @@ -1,5 +1,5 @@ /* - * linux/drivers/net/ehea/ehea_qmr.h + * linux/drivers/net/ethernet/ibm/ehea/ehea_qmr.h * * eHEA ethernet device driver for IBM eServer System p * diff --git a/drivers/net/ethernet/ibm/emac/core.c b/drivers/net/ethernet/ibm/emac/core.c index 2abce965c7bd..a0fe6e3fce61 100644 --- a/drivers/net/ethernet/ibm/emac/core.c +++ b/drivers/net/ethernet/ibm/emac/core.c @@ -1,5 +1,5 @@ /* - * drivers/net/ibm_newemac/core.c + * drivers/net/ethernet/ibm/emac/core.c * * Driver for PowerPC 4xx on-chip ethernet controller. * @@ -434,6 +434,11 @@ static inline u32 emac_iff2rmr(struct net_device *ndev) else if (!netdev_mc_empty(ndev)) r |= EMAC_RMR_MAE; + if (emac_has_feature(dev, EMAC_APM821XX_REQ_JUMBO_FRAME_SIZE)) { + r &= ~EMAC4_RMR_MJS_MASK; + r |= EMAC4_RMR_MJS(ndev->mtu); + } + return r; } @@ -965,6 +970,7 @@ static int emac_resize_rx_ring(struct emac_instance *dev, int new_mtu) int rx_sync_size = emac_rx_sync_size(new_mtu); int rx_skb_size = emac_rx_skb_size(new_mtu); int i, ret = 0; + int mr1_jumbo_bit_change = 0; mutex_lock(&dev->link_lock); emac_netif_stop(dev); @@ -1013,7 +1019,15 @@ static int emac_resize_rx_ring(struct emac_instance *dev, int new_mtu) } skip: /* Check if we need to change "Jumbo" bit in MR1 */ - if ((new_mtu > ETH_DATA_LEN) ^ (dev->ndev->mtu > ETH_DATA_LEN)) { + if (emac_has_feature(dev, EMAC_APM821XX_REQ_JUMBO_FRAME_SIZE)) { + mr1_jumbo_bit_change = (new_mtu > ETH_DATA_LEN) || + (dev->ndev->mtu > ETH_DATA_LEN); + } else { + mr1_jumbo_bit_change = (new_mtu > ETH_DATA_LEN) ^ + (dev->ndev->mtu > ETH_DATA_LEN); + } + + if (mr1_jumbo_bit_change) { /* This is to prevent starting RX channel in emac_rx_enable() */ set_bit(MAL_COMMAC_RX_STOPPED, &dev->commac.flags); @@ -2471,6 +2485,7 @@ static int __devinit emac_init_phy(struct emac_instance *dev) /* Disable any PHY features not supported by the platform */ dev->phy.def->features &= ~dev->phy_feat_exc; + dev->phy.features &= ~dev->phy_feat_exc; /* Setup initial link parameters */ if (dev->phy.features & SUPPORTED_Autoneg) { @@ -2568,6 +2583,11 @@ static int __devinit emac_init_config(struct emac_instance *dev) if (of_device_is_compatible(np, "ibm,emac-405ex") || of_device_is_compatible(np, "ibm,emac-405exr")) dev->features |= EMAC_FTR_440EP_PHY_CLK_FIX; + if (of_device_is_compatible(np, "ibm,emac-apm821xx")) { + dev->features |= (EMAC_APM821XX_REQ_JUMBO_FRAME_SIZE | + EMAC_FTR_APM821XX_NO_HALF_DUPLEX | + EMAC_FTR_460EX_PHY_CLK_FIX); + } } else if (of_device_is_compatible(np, "ibm,emac4")) { dev->features |= EMAC_FTR_EMAC4; if (of_device_is_compatible(np, "ibm,emac-440gx")) @@ -2706,11 +2726,9 @@ static int __devinit emac_probe(struct platform_device *ofdev) /* Allocate our net_device structure */ err = -ENOMEM; ndev = alloc_etherdev(sizeof(struct emac_instance)); - if (!ndev) { - printk(KERN_ERR "%s: could not allocate ethernet device!\n", - np->full_name); + if (!ndev) goto err_gone; - } + dev = netdev_priv(ndev); dev->ndev = ndev; dev->ofdev = ofdev; @@ -2818,6 +2836,13 @@ static int __devinit emac_probe(struct platform_device *ofdev) dev->stop_timeout = STOP_TIMEOUT_100; INIT_DELAYED_WORK(&dev->link_work, emac_link_timer); + /* Some SoCs like APM821xx does not support Half Duplex mode. */ + if (emac_has_feature(dev, EMAC_FTR_APM821XX_NO_HALF_DUPLEX)) { + dev->phy_feat_exc = (SUPPORTED_1000baseT_Half | + SUPPORTED_100baseT_Half | + SUPPORTED_10baseT_Half); + } + /* Find PHY if any */ err = emac_init_phy(dev); if (err != 0) diff --git a/drivers/net/ethernet/ibm/emac/core.h b/drivers/net/ethernet/ibm/emac/core.h index fa3ec57935fa..70074792bdef 100644 --- a/drivers/net/ethernet/ibm/emac/core.h +++ b/drivers/net/ethernet/ibm/emac/core.h @@ -1,5 +1,5 @@ /* - * drivers/net/ibm_newemac/core.h + * drivers/net/ethernet/ibm/emac/core.h * * Driver for PowerPC 4xx on-chip ethernet controller. * @@ -325,7 +325,14 @@ struct emac_instance { * Set if we need phy clock workaround for 460ex or 460gt */ #define EMAC_FTR_460EX_PHY_CLK_FIX 0x00000400 - +/* + * APM821xx requires Jumbo frame size set explicitly + */ +#define EMAC_APM821XX_REQ_JUMBO_FRAME_SIZE 0x00000800 +/* + * APM821xx does not support Half Duplex mode + */ +#define EMAC_FTR_APM821XX_NO_HALF_DUPLEX 0x00001000 /* Right now, we don't quite handle the always/possible masks on the * most optimal way as we don't have a way to say something like @@ -353,7 +360,9 @@ enum { EMAC_FTR_NO_FLOW_CONTROL_40x | #endif EMAC_FTR_460EX_PHY_CLK_FIX | - EMAC_FTR_440EP_PHY_CLK_FIX, + EMAC_FTR_440EP_PHY_CLK_FIX | + EMAC_APM821XX_REQ_JUMBO_FRAME_SIZE | + EMAC_FTR_APM821XX_NO_HALF_DUPLEX, }; static inline int emac_has_feature(struct emac_instance *dev, diff --git a/drivers/net/ethernet/ibm/emac/debug.c b/drivers/net/ethernet/ibm/emac/debug.c index 8c6c1e2a8750..b16b4828b64d 100644 --- a/drivers/net/ethernet/ibm/emac/debug.c +++ b/drivers/net/ethernet/ibm/emac/debug.c @@ -1,5 +1,5 @@ /* - * drivers/net/ibm_newemac/debug.c + * drivers/net/ethernet/ibm/emac/debug.c * * Driver for PowerPC 4xx on-chip ethernet controller, debug print routines. * diff --git a/drivers/net/ethernet/ibm/emac/debug.h b/drivers/net/ethernet/ibm/emac/debug.h index 90477fe69d0c..59a92d5870b5 100644 --- a/drivers/net/ethernet/ibm/emac/debug.h +++ b/drivers/net/ethernet/ibm/emac/debug.h @@ -1,5 +1,5 @@ /* - * drivers/net/ibm_newemac/debug.h + * drivers/net/ethernet/ibm/emac/debug.h * * Driver for PowerPC 4xx on-chip ethernet controller, debug print routines. * diff --git a/drivers/net/ethernet/ibm/emac/emac.h b/drivers/net/ethernet/ibm/emac/emac.h index 1568278d759a..5afcc27ceebb 100644 --- a/drivers/net/ethernet/ibm/emac/emac.h +++ b/drivers/net/ethernet/ibm/emac/emac.h @@ -1,5 +1,5 @@ /* - * drivers/net/ibm_newemac/emac.h + * drivers/net/ethernet/ibm/emac/emac.h * * Register definitions for PowerPC 4xx on-chip ethernet contoller * @@ -212,6 +212,8 @@ struct emac_regs { #define EMAC4_RMR_RFAF_64_1024 0x00000006 #define EMAC4_RMR_RFAF_128_2048 0x00000007 #define EMAC4_RMR_BASE EMAC4_RMR_RFAF_128_2048 +#define EMAC4_RMR_MJS_MASK 0x0001fff8 +#define EMAC4_RMR_MJS(s) (((s) << 3) & EMAC4_RMR_MJS_MASK) /* EMACx_ISR & EMACx_ISER */ #define EMAC4_ISR_TXPE 0x20000000 diff --git a/drivers/net/ethernet/ibm/emac/mal.c b/drivers/net/ethernet/ibm/emac/mal.c index f3c50b97ec61..479e43e2f1ef 100644 --- a/drivers/net/ethernet/ibm/emac/mal.c +++ b/drivers/net/ethernet/ibm/emac/mal.c @@ -1,5 +1,5 @@ /* - * drivers/net/ibm_newemac/mal.c + * drivers/net/ethernet/ibm/emac/mal.c * * Memory Access Layer (MAL) support * diff --git a/drivers/net/ethernet/ibm/emac/mal.h b/drivers/net/ethernet/ibm/emac/mal.h index d06f985bda32..e431a32e3d69 100644 --- a/drivers/net/ethernet/ibm/emac/mal.h +++ b/drivers/net/ethernet/ibm/emac/mal.h @@ -1,5 +1,5 @@ /* - * drivers/net/ibm_newemac/mal.h + * drivers/net/ethernet/ibm/emac/mal.h * * Memory Access Layer (MAL) support * diff --git a/drivers/net/ethernet/ibm/emac/phy.c b/drivers/net/ethernet/ibm/emac/phy.c index ab4e5969fe65..d3b9d103353e 100644 --- a/drivers/net/ethernet/ibm/emac/phy.c +++ b/drivers/net/ethernet/ibm/emac/phy.c @@ -1,5 +1,5 @@ /* - * drivers/net/ibm_newemac/phy.c + * drivers/net/ethernet/ibm/emac/phy.c * * Driver for PowerPC 4xx on-chip ethernet controller, PHY support. * Borrowed from sungem_phy.c, though I only kept the generic MII diff --git a/drivers/net/ethernet/ibm/emac/phy.h b/drivers/net/ethernet/ibm/emac/phy.h index 5d2bf4cbe50b..d7e41ec37467 100644 --- a/drivers/net/ethernet/ibm/emac/phy.h +++ b/drivers/net/ethernet/ibm/emac/phy.h @@ -1,5 +1,5 @@ /* - * drivers/net/ibm_newemac/phy.h + * drivers/net/ethernet/ibm/emac/phy.h * * Driver for PowerPC 4xx on-chip ethernet controller, PHY support * diff --git a/drivers/net/ethernet/ibm/emac/rgmii.c b/drivers/net/ethernet/ibm/emac/rgmii.c index 4fa53f3def64..d3123282e18e 100644 --- a/drivers/net/ethernet/ibm/emac/rgmii.c +++ b/drivers/net/ethernet/ibm/emac/rgmii.c @@ -1,5 +1,5 @@ /* - * drivers/net/ibm_newemac/rgmii.c + * drivers/net/ethernet/ibm/emac/rgmii.c * * Driver for PowerPC 4xx on-chip ethernet controller, RGMII bridge support. * @@ -237,11 +237,8 @@ static int __devinit rgmii_probe(struct platform_device *ofdev) rc = -ENOMEM; dev = kzalloc(sizeof(struct rgmii_instance), GFP_KERNEL); - if (dev == NULL) { - printk(KERN_ERR "%s: could not allocate RGMII device!\n", - np->full_name); + if (dev == NULL) goto err_gone; - } mutex_init(&dev->lock); dev->ofdev = ofdev; diff --git a/drivers/net/ethernet/ibm/emac/rgmii.h b/drivers/net/ethernet/ibm/emac/rgmii.h index 9296b6c5f920..668bceeff4a2 100644 --- a/drivers/net/ethernet/ibm/emac/rgmii.h +++ b/drivers/net/ethernet/ibm/emac/rgmii.h @@ -1,5 +1,5 @@ /* - * drivers/net/ibm_newemac/rgmii.h + * drivers/net/ethernet/ibm/emac/rgmii.h * * Driver for PowerPC 4xx on-chip ethernet controller, RGMII bridge support. * diff --git a/drivers/net/ethernet/ibm/emac/tah.c b/drivers/net/ethernet/ibm/emac/tah.c index 5f51bf7c9dc5..872912ef518d 100644 --- a/drivers/net/ethernet/ibm/emac/tah.c +++ b/drivers/net/ethernet/ibm/emac/tah.c @@ -1,5 +1,5 @@ /* - * drivers/net/ibm_newemac/tah.c + * drivers/net/ethernet/ibm/emac/tah.c * * Driver for PowerPC 4xx on-chip ethernet controller, TAH support. * @@ -96,11 +96,8 @@ static int __devinit tah_probe(struct platform_device *ofdev) rc = -ENOMEM; dev = kzalloc(sizeof(struct tah_instance), GFP_KERNEL); - if (dev == NULL) { - printk(KERN_ERR "%s: could not allocate TAH device!\n", - np->full_name); + if (dev == NULL) goto err_gone; - } mutex_init(&dev->lock); dev->ofdev = ofdev; diff --git a/drivers/net/ethernet/ibm/emac/tah.h b/drivers/net/ethernet/ibm/emac/tah.h index 3437ab4964c7..350b7096a041 100644 --- a/drivers/net/ethernet/ibm/emac/tah.h +++ b/drivers/net/ethernet/ibm/emac/tah.h @@ -1,5 +1,5 @@ /* - * drivers/net/ibm_newemac/tah.h + * drivers/net/ethernet/ibm/emac/tah.h * * Driver for PowerPC 4xx on-chip ethernet controller, TAH support. * diff --git a/drivers/net/ethernet/ibm/emac/zmii.c b/drivers/net/ethernet/ibm/emac/zmii.c index 97449e786d61..415e9b4d5408 100644 --- a/drivers/net/ethernet/ibm/emac/zmii.c +++ b/drivers/net/ethernet/ibm/emac/zmii.c @@ -1,5 +1,5 @@ /* - * drivers/net/ibm_newemac/zmii.c + * drivers/net/ethernet/ibm/emac/zmii.c * * Driver for PowerPC 4xx on-chip ethernet controller, ZMII bridge support. * @@ -240,11 +240,8 @@ static int __devinit zmii_probe(struct platform_device *ofdev) rc = -ENOMEM; dev = kzalloc(sizeof(struct zmii_instance), GFP_KERNEL); - if (dev == NULL) { - printk(KERN_ERR "%s: could not allocate ZMII device!\n", - np->full_name); + if (dev == NULL) goto err_gone; - } mutex_init(&dev->lock); dev->ofdev = ofdev; diff --git a/drivers/net/ethernet/ibm/emac/zmii.h b/drivers/net/ethernet/ibm/emac/zmii.h index ceaed823a83c..455bfb085493 100644 --- a/drivers/net/ethernet/ibm/emac/zmii.h +++ b/drivers/net/ethernet/ibm/emac/zmii.h @@ -1,5 +1,5 @@ /* - * drivers/net/ibm_newemac/zmii.h + * drivers/net/ethernet/ibm/emac/zmii.h * * Driver for PowerPC 4xx on-chip ethernet controller, ZMII bridge support. * diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c index e877371680a9..9010cea68bc3 100644 --- a/drivers/net/ethernet/ibm/ibmveth.c +++ b/drivers/net/ethernet/ibm/ibmveth.c @@ -1616,11 +1616,8 @@ static struct vio_driver ibmveth_driver = { .probe = ibmveth_probe, .remove = ibmveth_remove, .get_desired_dma = ibmveth_get_desired_dma, - .driver = { - .name = ibmveth_driver_name, - .owner = THIS_MODULE, - .pm = &ibmveth_pm_ops, - } + .name = ibmveth_driver_name, + .pm = &ibmveth_pm_ops, }; static int __init ibmveth_module_init(void) diff --git a/drivers/net/ethernet/ibm/iseries_veth.c b/drivers/net/ethernet/ibm/iseries_veth.c deleted file mode 100644 index acc31af6594a..000000000000 --- a/drivers/net/ethernet/ibm/iseries_veth.c +++ /dev/null @@ -1,1710 +0,0 @@ -/* File veth.c created by Kyle A. Lucke on Mon Aug 7 2000. */ -/* - * IBM eServer iSeries Virtual Ethernet Device Driver - * Copyright (C) 2001 Kyle A. Lucke (klucke@us.ibm.com), IBM Corp. - * Substantially cleaned up by: - * Copyright (C) 2003 David Gibson <dwg@au1.ibm.com>, IBM Corporation. - * Copyright (C) 2004-2005 Michael Ellerman, IBM Corporation. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA - * - * - * This module implements the virtual ethernet device for iSeries LPAR - * Linux. It uses hypervisor message passing to implement an - * ethernet-like network device communicating between partitions on - * the iSeries. - * - * The iSeries LPAR hypervisor currently allows for up to 16 different - * virtual ethernets. These are all dynamically configurable on - * OS/400 partitions, but dynamic configuration is not supported under - * Linux yet. An ethXX network device will be created for each - * virtual ethernet this partition is connected to. - * - * - This driver is responsible for routing packets to and from other - * partitions. The MAC addresses used by the virtual ethernets - * contains meaning and must not be modified. - * - * - Having 2 virtual ethernets to the same remote partition DOES NOT - * double the available bandwidth. The 2 devices will share the - * available hypervisor bandwidth. - * - * - If you send a packet to your own mac address, it will just be - * dropped, you won't get it on the receive side. - * - * - Multicast is implemented by sending the frame frame to every - * other partition. It is the responsibility of the receiving - * partition to filter the addresses desired. - * - * Tunable parameters: - * - * VETH_NUMBUFFERS: This compile time option defaults to 120. It - * controls how much memory Linux will allocate per remote partition - * it is communicating with. It can be thought of as the maximum - * number of packets outstanding to a remote partition at a time. - */ - -#include <linux/module.h> -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/ioport.h> -#include <linux/kernel.h> -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <linux/skbuff.h> -#include <linux/init.h> -#include <linux/delay.h> -#include <linux/mm.h> -#include <linux/ethtool.h> -#include <linux/if_ether.h> -#include <linux/slab.h> - -#include <asm/abs_addr.h> -#include <asm/iseries/mf.h> -#include <asm/uaccess.h> -#include <asm/firmware.h> -#include <asm/iseries/hv_lp_config.h> -#include <asm/iseries/hv_types.h> -#include <asm/iseries/hv_lp_event.h> -#include <asm/iommu.h> -#include <asm/vio.h> - -#undef DEBUG - -MODULE_AUTHOR("Kyle Lucke <klucke@us.ibm.com>"); -MODULE_DESCRIPTION("iSeries Virtual ethernet driver"); -MODULE_LICENSE("GPL"); - -#define VETH_EVENT_CAP (0) -#define VETH_EVENT_FRAMES (1) -#define VETH_EVENT_MONITOR (2) -#define VETH_EVENT_FRAMES_ACK (3) - -#define VETH_MAX_ACKS_PER_MSG (20) -#define VETH_MAX_FRAMES_PER_MSG (6) - -struct veth_frames_data { - u32 addr[VETH_MAX_FRAMES_PER_MSG]; - u16 len[VETH_MAX_FRAMES_PER_MSG]; - u32 eofmask; -}; -#define VETH_EOF_SHIFT (32-VETH_MAX_FRAMES_PER_MSG) - -struct veth_frames_ack_data { - u16 token[VETH_MAX_ACKS_PER_MSG]; -}; - -struct veth_cap_data { - u8 caps_version; - u8 rsvd1; - u16 num_buffers; - u16 ack_threshold; - u16 rsvd2; - u32 ack_timeout; - u32 rsvd3; - u64 rsvd4[3]; -}; - -struct veth_lpevent { - struct HvLpEvent base_event; - union { - struct veth_cap_data caps_data; - struct veth_frames_data frames_data; - struct veth_frames_ack_data frames_ack_data; - } u; - -}; - -#define DRV_NAME "iseries_veth" -#define DRV_VERSION "2.0" - -#define VETH_NUMBUFFERS (120) -#define VETH_ACKTIMEOUT (1000000) /* microseconds */ -#define VETH_MAX_MCAST (12) - -#define VETH_MAX_MTU (9000) - -#if VETH_NUMBUFFERS < 10 -#define ACK_THRESHOLD (1) -#elif VETH_NUMBUFFERS < 20 -#define ACK_THRESHOLD (4) -#elif VETH_NUMBUFFERS < 40 -#define ACK_THRESHOLD (10) -#else -#define ACK_THRESHOLD (20) -#endif - -#define VETH_STATE_SHUTDOWN (0x0001) -#define VETH_STATE_OPEN (0x0002) -#define VETH_STATE_RESET (0x0004) -#define VETH_STATE_SENTMON (0x0008) -#define VETH_STATE_SENTCAPS (0x0010) -#define VETH_STATE_GOTCAPACK (0x0020) -#define VETH_STATE_GOTCAPS (0x0040) -#define VETH_STATE_SENTCAPACK (0x0080) -#define VETH_STATE_READY (0x0100) - -struct veth_msg { - struct veth_msg *next; - struct veth_frames_data data; - int token; - int in_use; - struct sk_buff *skb; - struct device *dev; -}; - -struct veth_lpar_connection { - HvLpIndex remote_lp; - struct delayed_work statemachine_wq; - struct veth_msg *msgs; - int num_events; - struct veth_cap_data local_caps; - - struct kobject kobject; - struct timer_list ack_timer; - - struct timer_list reset_timer; - unsigned int reset_timeout; - unsigned long last_contact; - int outstanding_tx; - - spinlock_t lock; - unsigned long state; - HvLpInstanceId src_inst; - HvLpInstanceId dst_inst; - struct veth_lpevent cap_event, cap_ack_event; - u16 pending_acks[VETH_MAX_ACKS_PER_MSG]; - u32 num_pending_acks; - - int num_ack_events; - struct veth_cap_data remote_caps; - u32 ack_timeout; - - struct veth_msg *msg_stack_head; -}; - -struct veth_port { - struct device *dev; - u64 mac_addr; - HvLpIndexMap lpar_map; - - /* queue_lock protects the stopped_map and dev's queue. */ - spinlock_t queue_lock; - HvLpIndexMap stopped_map; - - /* mcast_gate protects promiscuous, num_mcast & mcast_addr. */ - rwlock_t mcast_gate; - int promiscuous; - int num_mcast; - u64 mcast_addr[VETH_MAX_MCAST]; - - struct kobject kobject; -}; - -static HvLpIndex this_lp; -static struct veth_lpar_connection *veth_cnx[HVMAXARCHITECTEDLPS]; /* = 0 */ -static struct net_device *veth_dev[HVMAXARCHITECTEDVIRTUALLANS]; /* = 0 */ - -static int veth_start_xmit(struct sk_buff *skb, struct net_device *dev); -static void veth_recycle_msg(struct veth_lpar_connection *, struct veth_msg *); -static void veth_wake_queues(struct veth_lpar_connection *cnx); -static void veth_stop_queues(struct veth_lpar_connection *cnx); -static void veth_receive(struct veth_lpar_connection *, struct veth_lpevent *); -static void veth_release_connection(struct kobject *kobject); -static void veth_timed_ack(unsigned long ptr); -static void veth_timed_reset(unsigned long ptr); - -/* - * Utility functions - */ - -#define veth_info(fmt, args...) \ - printk(KERN_INFO DRV_NAME ": " fmt, ## args) - -#define veth_error(fmt, args...) \ - printk(KERN_ERR DRV_NAME ": Error: " fmt, ## args) - -#ifdef DEBUG -#define veth_debug(fmt, args...) \ - printk(KERN_DEBUG DRV_NAME ": " fmt, ## args) -#else -#define veth_debug(fmt, args...) do {} while (0) -#endif - -/* You must hold the connection's lock when you call this function. */ -static inline void veth_stack_push(struct veth_lpar_connection *cnx, - struct veth_msg *msg) -{ - msg->next = cnx->msg_stack_head; - cnx->msg_stack_head = msg; -} - -/* You must hold the connection's lock when you call this function. */ -static inline struct veth_msg *veth_stack_pop(struct veth_lpar_connection *cnx) -{ - struct veth_msg *msg; - - msg = cnx->msg_stack_head; - if (msg) - cnx->msg_stack_head = cnx->msg_stack_head->next; - - return msg; -} - -/* You must hold the connection's lock when you call this function. */ -static inline int veth_stack_is_empty(struct veth_lpar_connection *cnx) -{ - return cnx->msg_stack_head == NULL; -} - -static inline HvLpEvent_Rc -veth_signalevent(struct veth_lpar_connection *cnx, u16 subtype, - HvLpEvent_AckInd ackind, HvLpEvent_AckType acktype, - u64 token, - u64 data1, u64 data2, u64 data3, u64 data4, u64 data5) -{ - return HvCallEvent_signalLpEventFast(cnx->remote_lp, - HvLpEvent_Type_VirtualLan, - subtype, ackind, acktype, - cnx->src_inst, - cnx->dst_inst, - token, data1, data2, data3, - data4, data5); -} - -static inline HvLpEvent_Rc veth_signaldata(struct veth_lpar_connection *cnx, - u16 subtype, u64 token, void *data) -{ - u64 *p = (u64 *) data; - - return veth_signalevent(cnx, subtype, HvLpEvent_AckInd_NoAck, - HvLpEvent_AckType_ImmediateAck, - token, p[0], p[1], p[2], p[3], p[4]); -} - -struct veth_allocation { - struct completion c; - int num; -}; - -static void veth_complete_allocation(void *parm, int number) -{ - struct veth_allocation *vc = (struct veth_allocation *)parm; - - vc->num = number; - complete(&vc->c); -} - -static int veth_allocate_events(HvLpIndex rlp, int number) -{ - struct veth_allocation vc = - { COMPLETION_INITIALIZER_ONSTACK(vc.c), 0 }; - - mf_allocate_lp_events(rlp, HvLpEvent_Type_VirtualLan, - sizeof(struct veth_lpevent), number, - &veth_complete_allocation, &vc); - wait_for_completion(&vc.c); - - return vc.num; -} - -/* - * sysfs support - */ - -struct veth_cnx_attribute { - struct attribute attr; - ssize_t (*show)(struct veth_lpar_connection *, char *buf); - ssize_t (*store)(struct veth_lpar_connection *, const char *buf); -}; - -static ssize_t veth_cnx_attribute_show(struct kobject *kobj, - struct attribute *attr, char *buf) -{ - struct veth_cnx_attribute *cnx_attr; - struct veth_lpar_connection *cnx; - - cnx_attr = container_of(attr, struct veth_cnx_attribute, attr); - cnx = container_of(kobj, struct veth_lpar_connection, kobject); - - if (!cnx_attr->show) - return -EIO; - - return cnx_attr->show(cnx, buf); -} - -#define CUSTOM_CNX_ATTR(_name, _format, _expression) \ -static ssize_t _name##_show(struct veth_lpar_connection *cnx, char *buf)\ -{ \ - return sprintf(buf, _format, _expression); \ -} \ -struct veth_cnx_attribute veth_cnx_attr_##_name = __ATTR_RO(_name) - -#define SIMPLE_CNX_ATTR(_name) \ - CUSTOM_CNX_ATTR(_name, "%lu\n", (unsigned long)cnx->_name) - -SIMPLE_CNX_ATTR(outstanding_tx); -SIMPLE_CNX_ATTR(remote_lp); -SIMPLE_CNX_ATTR(num_events); -SIMPLE_CNX_ATTR(src_inst); -SIMPLE_CNX_ATTR(dst_inst); -SIMPLE_CNX_ATTR(num_pending_acks); -SIMPLE_CNX_ATTR(num_ack_events); -CUSTOM_CNX_ATTR(ack_timeout, "%d\n", jiffies_to_msecs(cnx->ack_timeout)); -CUSTOM_CNX_ATTR(reset_timeout, "%d\n", jiffies_to_msecs(cnx->reset_timeout)); -CUSTOM_CNX_ATTR(state, "0x%.4lX\n", cnx->state); -CUSTOM_CNX_ATTR(last_contact, "%d\n", cnx->last_contact ? - jiffies_to_msecs(jiffies - cnx->last_contact) : 0); - -#define GET_CNX_ATTR(_name) (&veth_cnx_attr_##_name.attr) - -static struct attribute *veth_cnx_default_attrs[] = { - GET_CNX_ATTR(outstanding_tx), - GET_CNX_ATTR(remote_lp), - GET_CNX_ATTR(num_events), - GET_CNX_ATTR(reset_timeout), - GET_CNX_ATTR(last_contact), - GET_CNX_ATTR(state), - GET_CNX_ATTR(src_inst), - GET_CNX_ATTR(dst_inst), - GET_CNX_ATTR(num_pending_acks), - GET_CNX_ATTR(num_ack_events), - GET_CNX_ATTR(ack_timeout), - NULL -}; - -static const struct sysfs_ops veth_cnx_sysfs_ops = { - .show = veth_cnx_attribute_show -}; - -static struct kobj_type veth_lpar_connection_ktype = { - .release = veth_release_connection, - .sysfs_ops = &veth_cnx_sysfs_ops, - .default_attrs = veth_cnx_default_attrs -}; - -struct veth_port_attribute { - struct attribute attr; - ssize_t (*show)(struct veth_port *, char *buf); - ssize_t (*store)(struct veth_port *, const char *buf); -}; - -static ssize_t veth_port_attribute_show(struct kobject *kobj, - struct attribute *attr, char *buf) -{ - struct veth_port_attribute *port_attr; - struct veth_port *port; - - port_attr = container_of(attr, struct veth_port_attribute, attr); - port = container_of(kobj, struct veth_port, kobject); - - if (!port_attr->show) - return -EIO; - - return port_attr->show(port, buf); -} - -#define CUSTOM_PORT_ATTR(_name, _format, _expression) \ -static ssize_t _name##_show(struct veth_port *port, char *buf) \ -{ \ - return sprintf(buf, _format, _expression); \ -} \ -struct veth_port_attribute veth_port_attr_##_name = __ATTR_RO(_name) - -#define SIMPLE_PORT_ATTR(_name) \ - CUSTOM_PORT_ATTR(_name, "%lu\n", (unsigned long)port->_name) - -SIMPLE_PORT_ATTR(promiscuous); -SIMPLE_PORT_ATTR(num_mcast); -CUSTOM_PORT_ATTR(lpar_map, "0x%X\n", port->lpar_map); -CUSTOM_PORT_ATTR(stopped_map, "0x%X\n", port->stopped_map); -CUSTOM_PORT_ATTR(mac_addr, "0x%llX\n", port->mac_addr); - -#define GET_PORT_ATTR(_name) (&veth_port_attr_##_name.attr) -static struct attribute *veth_port_default_attrs[] = { - GET_PORT_ATTR(mac_addr), - GET_PORT_ATTR(lpar_map), - GET_PORT_ATTR(stopped_map), - GET_PORT_ATTR(promiscuous), - GET_PORT_ATTR(num_mcast), - NULL -}; - -static const struct sysfs_ops veth_port_sysfs_ops = { - .show = veth_port_attribute_show -}; - -static struct kobj_type veth_port_ktype = { - .sysfs_ops = &veth_port_sysfs_ops, - .default_attrs = veth_port_default_attrs -}; - -/* - * LPAR connection code - */ - -static inline void veth_kick_statemachine(struct veth_lpar_connection *cnx) -{ - schedule_delayed_work(&cnx->statemachine_wq, 0); -} - -static void veth_take_cap(struct veth_lpar_connection *cnx, - struct veth_lpevent *event) -{ - unsigned long flags; - - spin_lock_irqsave(&cnx->lock, flags); - /* Receiving caps may mean the other end has just come up, so - * we need to reload the instance ID of the far end */ - cnx->dst_inst = - HvCallEvent_getTargetLpInstanceId(cnx->remote_lp, - HvLpEvent_Type_VirtualLan); - - if (cnx->state & VETH_STATE_GOTCAPS) { - veth_error("Received a second capabilities from LPAR %d.\n", - cnx->remote_lp); - event->base_event.xRc = HvLpEvent_Rc_BufferNotAvailable; - HvCallEvent_ackLpEvent((struct HvLpEvent *) event); - } else { - memcpy(&cnx->cap_event, event, sizeof(cnx->cap_event)); - cnx->state |= VETH_STATE_GOTCAPS; - veth_kick_statemachine(cnx); - } - spin_unlock_irqrestore(&cnx->lock, flags); -} - -static void veth_take_cap_ack(struct veth_lpar_connection *cnx, - struct veth_lpevent *event) -{ - unsigned long flags; - - spin_lock_irqsave(&cnx->lock, flags); - if (cnx->state & VETH_STATE_GOTCAPACK) { - veth_error("Received a second capabilities ack from LPAR %d.\n", - cnx->remote_lp); - } else { - memcpy(&cnx->cap_ack_event, event, - sizeof(cnx->cap_ack_event)); - cnx->state |= VETH_STATE_GOTCAPACK; - veth_kick_statemachine(cnx); - } - spin_unlock_irqrestore(&cnx->lock, flags); -} - -static void veth_take_monitor_ack(struct veth_lpar_connection *cnx, - struct veth_lpevent *event) -{ - unsigned long flags; - - spin_lock_irqsave(&cnx->lock, flags); - veth_debug("cnx %d: lost connection.\n", cnx->remote_lp); - - /* Avoid kicking the statemachine once we're shutdown. - * It's unnecessary and it could break veth_stop_connection(). */ - - if (! (cnx->state & VETH_STATE_SHUTDOWN)) { - cnx->state |= VETH_STATE_RESET; - veth_kick_statemachine(cnx); - } - spin_unlock_irqrestore(&cnx->lock, flags); -} - -static void veth_handle_ack(struct veth_lpevent *event) -{ - HvLpIndex rlp = event->base_event.xTargetLp; - struct veth_lpar_connection *cnx = veth_cnx[rlp]; - - BUG_ON(! cnx); - - switch (event->base_event.xSubtype) { - case VETH_EVENT_CAP: - veth_take_cap_ack(cnx, event); - break; - case VETH_EVENT_MONITOR: - veth_take_monitor_ack(cnx, event); - break; - default: - veth_error("Unknown ack type %d from LPAR %d.\n", - event->base_event.xSubtype, rlp); - } -} - -static void veth_handle_int(struct veth_lpevent *event) -{ - HvLpIndex rlp = event->base_event.xSourceLp; - struct veth_lpar_connection *cnx = veth_cnx[rlp]; - unsigned long flags; - int i, acked = 0; - - BUG_ON(! cnx); - - switch (event->base_event.xSubtype) { - case VETH_EVENT_CAP: - veth_take_cap(cnx, event); - break; - case VETH_EVENT_MONITOR: - /* do nothing... this'll hang out here til we're dead, - * and the hypervisor will return it for us. */ - break; - case VETH_EVENT_FRAMES_ACK: - spin_lock_irqsave(&cnx->lock, flags); - - for (i = 0; i < VETH_MAX_ACKS_PER_MSG; ++i) { - u16 msgnum = event->u.frames_ack_data.token[i]; - - if (msgnum < VETH_NUMBUFFERS) { - veth_recycle_msg(cnx, cnx->msgs + msgnum); - cnx->outstanding_tx--; - acked++; - } - } - - if (acked > 0) { - cnx->last_contact = jiffies; - veth_wake_queues(cnx); - } - - spin_unlock_irqrestore(&cnx->lock, flags); - break; - case VETH_EVENT_FRAMES: - veth_receive(cnx, event); - break; - default: - veth_error("Unknown interrupt type %d from LPAR %d.\n", - event->base_event.xSubtype, rlp); - } -} - -static void veth_handle_event(struct HvLpEvent *event) -{ - struct veth_lpevent *veth_event = (struct veth_lpevent *)event; - - if (hvlpevent_is_ack(event)) - veth_handle_ack(veth_event); - else - veth_handle_int(veth_event); -} - -static int veth_process_caps(struct veth_lpar_connection *cnx) -{ - struct veth_cap_data *remote_caps = &cnx->remote_caps; - int num_acks_needed; - - /* Convert timer to jiffies */ - cnx->ack_timeout = remote_caps->ack_timeout * HZ / 1000000; - - if ( (remote_caps->num_buffers == 0) || - (remote_caps->ack_threshold > VETH_MAX_ACKS_PER_MSG) || - (remote_caps->ack_threshold == 0) || - (cnx->ack_timeout == 0) ) { - veth_error("Received incompatible capabilities from LPAR %d.\n", - cnx->remote_lp); - return HvLpEvent_Rc_InvalidSubtypeData; - } - - num_acks_needed = (remote_caps->num_buffers - / remote_caps->ack_threshold) + 1; - - /* FIXME: locking on num_ack_events? */ - if (cnx->num_ack_events < num_acks_needed) { - int num; - - num = veth_allocate_events(cnx->remote_lp, - num_acks_needed-cnx->num_ack_events); - if (num > 0) - cnx->num_ack_events += num; - - if (cnx->num_ack_events < num_acks_needed) { - veth_error("Couldn't allocate enough ack events " - "for LPAR %d.\n", cnx->remote_lp); - - return HvLpEvent_Rc_BufferNotAvailable; - } - } - - - return HvLpEvent_Rc_Good; -} - -/* FIXME: The gotos here are a bit dubious */ -static void veth_statemachine(struct work_struct *work) -{ - struct veth_lpar_connection *cnx = - container_of(work, struct veth_lpar_connection, - statemachine_wq.work); - int rlp = cnx->remote_lp; - int rc; - - spin_lock_irq(&cnx->lock); - - restart: - if (cnx->state & VETH_STATE_RESET) { - if (cnx->state & VETH_STATE_OPEN) - HvCallEvent_closeLpEventPath(cnx->remote_lp, - HvLpEvent_Type_VirtualLan); - - /* - * Reset ack data. This prevents the ack_timer actually - * doing anything, even if it runs one more time when - * we drop the lock below. - */ - memset(&cnx->pending_acks, 0xff, sizeof (cnx->pending_acks)); - cnx->num_pending_acks = 0; - - cnx->state &= ~(VETH_STATE_RESET | VETH_STATE_SENTMON - | VETH_STATE_OPEN | VETH_STATE_SENTCAPS - | VETH_STATE_GOTCAPACK | VETH_STATE_GOTCAPS - | VETH_STATE_SENTCAPACK | VETH_STATE_READY); - - /* Clean up any leftover messages */ - if (cnx->msgs) { - int i; - for (i = 0; i < VETH_NUMBUFFERS; ++i) - veth_recycle_msg(cnx, cnx->msgs + i); - } - - cnx->outstanding_tx = 0; - veth_wake_queues(cnx); - - /* Drop the lock so we can do stuff that might sleep or - * take other locks. */ - spin_unlock_irq(&cnx->lock); - - del_timer_sync(&cnx->ack_timer); - del_timer_sync(&cnx->reset_timer); - - spin_lock_irq(&cnx->lock); - - if (cnx->state & VETH_STATE_RESET) - goto restart; - - /* Hack, wait for the other end to reset itself. */ - if (! (cnx->state & VETH_STATE_SHUTDOWN)) { - schedule_delayed_work(&cnx->statemachine_wq, 5 * HZ); - goto out; - } - } - - if (cnx->state & VETH_STATE_SHUTDOWN) - /* It's all over, do nothing */ - goto out; - - if ( !(cnx->state & VETH_STATE_OPEN) ) { - if (! cnx->msgs || (cnx->num_events < (2 + VETH_NUMBUFFERS)) ) - goto cant_cope; - - HvCallEvent_openLpEventPath(rlp, HvLpEvent_Type_VirtualLan); - cnx->src_inst = - HvCallEvent_getSourceLpInstanceId(rlp, - HvLpEvent_Type_VirtualLan); - cnx->dst_inst = - HvCallEvent_getTargetLpInstanceId(rlp, - HvLpEvent_Type_VirtualLan); - cnx->state |= VETH_STATE_OPEN; - } - - if ( (cnx->state & VETH_STATE_OPEN) && - !(cnx->state & VETH_STATE_SENTMON) ) { - rc = veth_signalevent(cnx, VETH_EVENT_MONITOR, - HvLpEvent_AckInd_DoAck, - HvLpEvent_AckType_DeferredAck, - 0, 0, 0, 0, 0, 0); - - if (rc == HvLpEvent_Rc_Good) { - cnx->state |= VETH_STATE_SENTMON; - } else { - if ( (rc != HvLpEvent_Rc_PartitionDead) && - (rc != HvLpEvent_Rc_PathClosed) ) - veth_error("Error sending monitor to LPAR %d, " - "rc = %d\n", rlp, rc); - - /* Oh well, hope we get a cap from the other - * end and do better when that kicks us */ - goto out; - } - } - - if ( (cnx->state & VETH_STATE_OPEN) && - !(cnx->state & VETH_STATE_SENTCAPS)) { - u64 *rawcap = (u64 *)&cnx->local_caps; - - rc = veth_signalevent(cnx, VETH_EVENT_CAP, - HvLpEvent_AckInd_DoAck, - HvLpEvent_AckType_ImmediateAck, - 0, rawcap[0], rawcap[1], rawcap[2], - rawcap[3], rawcap[4]); - - if (rc == HvLpEvent_Rc_Good) { - cnx->state |= VETH_STATE_SENTCAPS; - } else { - if ( (rc != HvLpEvent_Rc_PartitionDead) && - (rc != HvLpEvent_Rc_PathClosed) ) - veth_error("Error sending caps to LPAR %d, " - "rc = %d\n", rlp, rc); - - /* Oh well, hope we get a cap from the other - * end and do better when that kicks us */ - goto out; - } - } - - if ((cnx->state & VETH_STATE_GOTCAPS) && - !(cnx->state & VETH_STATE_SENTCAPACK)) { - struct veth_cap_data *remote_caps = &cnx->remote_caps; - - memcpy(remote_caps, &cnx->cap_event.u.caps_data, - sizeof(*remote_caps)); - - spin_unlock_irq(&cnx->lock); - rc = veth_process_caps(cnx); - spin_lock_irq(&cnx->lock); - - /* We dropped the lock, so recheck for anything which - * might mess us up */ - if (cnx->state & (VETH_STATE_RESET|VETH_STATE_SHUTDOWN)) - goto restart; - - cnx->cap_event.base_event.xRc = rc; - HvCallEvent_ackLpEvent((struct HvLpEvent *)&cnx->cap_event); - if (rc == HvLpEvent_Rc_Good) - cnx->state |= VETH_STATE_SENTCAPACK; - else - goto cant_cope; - } - - if ((cnx->state & VETH_STATE_GOTCAPACK) && - (cnx->state & VETH_STATE_GOTCAPS) && - !(cnx->state & VETH_STATE_READY)) { - if (cnx->cap_ack_event.base_event.xRc == HvLpEvent_Rc_Good) { - /* Start the ACK timer */ - cnx->ack_timer.expires = jiffies + cnx->ack_timeout; - add_timer(&cnx->ack_timer); - cnx->state |= VETH_STATE_READY; - } else { - veth_error("Caps rejected by LPAR %d, rc = %d\n", - rlp, cnx->cap_ack_event.base_event.xRc); - goto cant_cope; - } - } - - out: - spin_unlock_irq(&cnx->lock); - return; - - cant_cope: - /* FIXME: we get here if something happens we really can't - * cope with. The link will never work once we get here, and - * all we can do is not lock the rest of the system up */ - veth_error("Unrecoverable error on connection to LPAR %d, shutting down" - " (state = 0x%04lx)\n", rlp, cnx->state); - cnx->state |= VETH_STATE_SHUTDOWN; - spin_unlock_irq(&cnx->lock); -} - -static int veth_init_connection(u8 rlp) -{ - struct veth_lpar_connection *cnx; - struct veth_msg *msgs; - int i; - - if ( (rlp == this_lp) || - ! HvLpConfig_doLpsCommunicateOnVirtualLan(this_lp, rlp) ) - return 0; - - cnx = kzalloc(sizeof(*cnx), GFP_KERNEL); - if (! cnx) - return -ENOMEM; - - cnx->remote_lp = rlp; - spin_lock_init(&cnx->lock); - INIT_DELAYED_WORK(&cnx->statemachine_wq, veth_statemachine); - - init_timer(&cnx->ack_timer); - cnx->ack_timer.function = veth_timed_ack; - cnx->ack_timer.data = (unsigned long) cnx; - - init_timer(&cnx->reset_timer); - cnx->reset_timer.function = veth_timed_reset; - cnx->reset_timer.data = (unsigned long) cnx; - cnx->reset_timeout = 5 * HZ * (VETH_ACKTIMEOUT / 1000000); - - memset(&cnx->pending_acks, 0xff, sizeof (cnx->pending_acks)); - - veth_cnx[rlp] = cnx; - - /* This gets us 1 reference, which is held on behalf of the driver - * infrastructure. It's released at module unload. */ - kobject_init(&cnx->kobject, &veth_lpar_connection_ktype); - - msgs = kcalloc(VETH_NUMBUFFERS, sizeof(struct veth_msg), GFP_KERNEL); - if (! msgs) { - veth_error("Can't allocate buffers for LPAR %d.\n", rlp); - return -ENOMEM; - } - - cnx->msgs = msgs; - - for (i = 0; i < VETH_NUMBUFFERS; i++) { - msgs[i].token = i; - veth_stack_push(cnx, msgs + i); - } - - cnx->num_events = veth_allocate_events(rlp, 2 + VETH_NUMBUFFERS); - - if (cnx->num_events < (2 + VETH_NUMBUFFERS)) { - veth_error("Can't allocate enough events for LPAR %d.\n", rlp); - return -ENOMEM; - } - - cnx->local_caps.num_buffers = VETH_NUMBUFFERS; - cnx->local_caps.ack_threshold = ACK_THRESHOLD; - cnx->local_caps.ack_timeout = VETH_ACKTIMEOUT; - - return 0; -} - -static void veth_stop_connection(struct veth_lpar_connection *cnx) -{ - if (!cnx) - return; - - spin_lock_irq(&cnx->lock); - cnx->state |= VETH_STATE_RESET | VETH_STATE_SHUTDOWN; - veth_kick_statemachine(cnx); - spin_unlock_irq(&cnx->lock); - - /* ensure the statemachine runs now and waits for its completion */ - flush_delayed_work_sync(&cnx->statemachine_wq); -} - -static void veth_destroy_connection(struct veth_lpar_connection *cnx) -{ - if (!cnx) - return; - - if (cnx->num_events > 0) - mf_deallocate_lp_events(cnx->remote_lp, - HvLpEvent_Type_VirtualLan, - cnx->num_events, - NULL, NULL); - if (cnx->num_ack_events > 0) - mf_deallocate_lp_events(cnx->remote_lp, - HvLpEvent_Type_VirtualLan, - cnx->num_ack_events, - NULL, NULL); - - kfree(cnx->msgs); - veth_cnx[cnx->remote_lp] = NULL; - kfree(cnx); -} - -static void veth_release_connection(struct kobject *kobj) -{ - struct veth_lpar_connection *cnx; - cnx = container_of(kobj, struct veth_lpar_connection, kobject); - veth_stop_connection(cnx); - veth_destroy_connection(cnx); -} - -/* - * net_device code - */ - -static int veth_open(struct net_device *dev) -{ - netif_start_queue(dev); - return 0; -} - -static int veth_close(struct net_device *dev) -{ - netif_stop_queue(dev); - return 0; -} - -static int veth_change_mtu(struct net_device *dev, int new_mtu) -{ - if ((new_mtu < 68) || (new_mtu > VETH_MAX_MTU)) - return -EINVAL; - dev->mtu = new_mtu; - return 0; -} - -static void veth_set_multicast_list(struct net_device *dev) -{ - struct veth_port *port = netdev_priv(dev); - unsigned long flags; - - write_lock_irqsave(&port->mcast_gate, flags); - - if ((dev->flags & IFF_PROMISC) || (dev->flags & IFF_ALLMULTI) || - (netdev_mc_count(dev) > VETH_MAX_MCAST)) { - port->promiscuous = 1; - } else { - struct netdev_hw_addr *ha; - - port->promiscuous = 0; - - /* Update table */ - port->num_mcast = 0; - - netdev_for_each_mc_addr(ha, dev) { - u8 *addr = ha->addr; - u64 xaddr = 0; - - memcpy(&xaddr, addr, ETH_ALEN); - port->mcast_addr[port->num_mcast] = xaddr; - port->num_mcast++; - } - } - - write_unlock_irqrestore(&port->mcast_gate, flags); -} - -static void veth_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) -{ - strncpy(info->driver, DRV_NAME, sizeof(info->driver) - 1); - info->driver[sizeof(info->driver) - 1] = '\0'; - strncpy(info->version, DRV_VERSION, sizeof(info->version) - 1); - info->version[sizeof(info->version) - 1] = '\0'; -} - -static int veth_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) -{ - ecmd->supported = (SUPPORTED_1000baseT_Full - | SUPPORTED_Autoneg | SUPPORTED_FIBRE); - ecmd->advertising = (SUPPORTED_1000baseT_Full - | SUPPORTED_Autoneg | SUPPORTED_FIBRE); - ecmd->port = PORT_FIBRE; - ecmd->transceiver = XCVR_INTERNAL; - ecmd->phy_address = 0; - ecmd->speed = SPEED_1000; - ecmd->duplex = DUPLEX_FULL; - ecmd->autoneg = AUTONEG_ENABLE; - ecmd->maxtxpkt = 120; - ecmd->maxrxpkt = 120; - return 0; -} - -static const struct ethtool_ops ops = { - .get_drvinfo = veth_get_drvinfo, - .get_settings = veth_get_settings, - .get_link = ethtool_op_get_link, -}; - -static const struct net_device_ops veth_netdev_ops = { - .ndo_open = veth_open, - .ndo_stop = veth_close, - .ndo_start_xmit = veth_start_xmit, - .ndo_change_mtu = veth_change_mtu, - .ndo_set_rx_mode = veth_set_multicast_list, - .ndo_set_mac_address = NULL, - .ndo_validate_addr = eth_validate_addr, -}; - -static struct net_device *veth_probe_one(int vlan, - struct vio_dev *vio_dev) -{ - struct net_device *dev; - struct veth_port *port; - struct device *vdev = &vio_dev->dev; - int i, rc; - const unsigned char *mac_addr; - - mac_addr = vio_get_attribute(vio_dev, "local-mac-address", NULL); - if (mac_addr == NULL) - mac_addr = vio_get_attribute(vio_dev, "mac-address", NULL); - if (mac_addr == NULL) { - veth_error("Unable to fetch MAC address from device tree.\n"); - return NULL; - } - - dev = alloc_etherdev(sizeof (struct veth_port)); - if (! dev) { - veth_error("Unable to allocate net_device structure!\n"); - return NULL; - } - - port = netdev_priv(dev); - - spin_lock_init(&port->queue_lock); - rwlock_init(&port->mcast_gate); - port->stopped_map = 0; - - for (i = 0; i < HVMAXARCHITECTEDLPS; i++) { - HvLpVirtualLanIndexMap map; - - if (i == this_lp) - continue; - map = HvLpConfig_getVirtualLanIndexMapForLp(i); - if (map & (0x8000 >> vlan)) - port->lpar_map |= (1 << i); - } - port->dev = vdev; - - memcpy(dev->dev_addr, mac_addr, ETH_ALEN); - - dev->mtu = VETH_MAX_MTU; - - memcpy(&port->mac_addr, mac_addr, ETH_ALEN); - - dev->netdev_ops = &veth_netdev_ops; - SET_ETHTOOL_OPS(dev, &ops); - - SET_NETDEV_DEV(dev, vdev); - - rc = register_netdev(dev); - if (rc != 0) { - veth_error("Failed registering net device for vlan%d.\n", vlan); - free_netdev(dev); - return NULL; - } - - kobject_init(&port->kobject, &veth_port_ktype); - if (0 != kobject_add(&port->kobject, &dev->dev.kobj, "veth_port")) - veth_error("Failed adding port for %s to sysfs.\n", dev->name); - - veth_info("%s attached to iSeries vlan %d (LPAR map = 0x%.4X)\n", - dev->name, vlan, port->lpar_map); - - return dev; -} - -/* - * Tx path - */ - -static int veth_transmit_to_one(struct sk_buff *skb, HvLpIndex rlp, - struct net_device *dev) -{ - struct veth_lpar_connection *cnx = veth_cnx[rlp]; - struct veth_port *port = netdev_priv(dev); - HvLpEvent_Rc rc; - struct veth_msg *msg = NULL; - unsigned long flags; - - if (! cnx) - return 0; - - spin_lock_irqsave(&cnx->lock, flags); - - if (! (cnx->state & VETH_STATE_READY)) - goto no_error; - - if ((skb->len - ETH_HLEN) > VETH_MAX_MTU) - goto drop; - - msg = veth_stack_pop(cnx); - if (! msg) - goto drop; - - msg->in_use = 1; - msg->skb = skb_get(skb); - - msg->data.addr[0] = dma_map_single(port->dev, skb->data, - skb->len, DMA_TO_DEVICE); - - if (dma_mapping_error(port->dev, msg->data.addr[0])) - goto recycle_and_drop; - - msg->dev = port->dev; - msg->data.len[0] = skb->len; - msg->data.eofmask = 1 << VETH_EOF_SHIFT; - - rc = veth_signaldata(cnx, VETH_EVENT_FRAMES, msg->token, &msg->data); - - if (rc != HvLpEvent_Rc_Good) - goto recycle_and_drop; - - /* If the timer's not already running, start it now. */ - if (0 == cnx->outstanding_tx) - mod_timer(&cnx->reset_timer, jiffies + cnx->reset_timeout); - - cnx->last_contact = jiffies; - cnx->outstanding_tx++; - - if (veth_stack_is_empty(cnx)) - veth_stop_queues(cnx); - - no_error: - spin_unlock_irqrestore(&cnx->lock, flags); - return 0; - - recycle_and_drop: - veth_recycle_msg(cnx, msg); - drop: - spin_unlock_irqrestore(&cnx->lock, flags); - return 1; -} - -static void veth_transmit_to_many(struct sk_buff *skb, - HvLpIndexMap lpmask, - struct net_device *dev) -{ - int i, success, error; - - success = error = 0; - - for (i = 0; i < HVMAXARCHITECTEDLPS; i++) { - if ((lpmask & (1 << i)) == 0) - continue; - - if (veth_transmit_to_one(skb, i, dev)) - error = 1; - else - success = 1; - } - - if (error) - dev->stats.tx_errors++; - - if (success) { - dev->stats.tx_packets++; - dev->stats.tx_bytes += skb->len; - } -} - -static int veth_start_xmit(struct sk_buff *skb, struct net_device *dev) -{ - unsigned char *frame = skb->data; - struct veth_port *port = netdev_priv(dev); - HvLpIndexMap lpmask; - - if (is_unicast_ether_addr(frame)) { - /* unicast packet */ - HvLpIndex rlp = frame[5]; - - if ( ! ((1 << rlp) & port->lpar_map) ) { - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - - lpmask = 1 << rlp; - } else { - lpmask = port->lpar_map; - } - - veth_transmit_to_many(skb, lpmask, dev); - - dev_kfree_skb(skb); - - return NETDEV_TX_OK; -} - -/* You must hold the connection's lock when you call this function. */ -static void veth_recycle_msg(struct veth_lpar_connection *cnx, - struct veth_msg *msg) -{ - u32 dma_address, dma_length; - - if (msg->in_use) { - msg->in_use = 0; - dma_address = msg->data.addr[0]; - dma_length = msg->data.len[0]; - - if (!dma_mapping_error(msg->dev, dma_address)) - dma_unmap_single(msg->dev, dma_address, dma_length, - DMA_TO_DEVICE); - - if (msg->skb) { - dev_kfree_skb_any(msg->skb); - msg->skb = NULL; - } - - memset(&msg->data, 0, sizeof(msg->data)); - veth_stack_push(cnx, msg); - } else if (cnx->state & VETH_STATE_OPEN) { - veth_error("Non-pending frame (# %d) acked by LPAR %d.\n", - cnx->remote_lp, msg->token); - } -} - -static void veth_wake_queues(struct veth_lpar_connection *cnx) -{ - int i; - - for (i = 0; i < HVMAXARCHITECTEDVIRTUALLANS; i++) { - struct net_device *dev = veth_dev[i]; - struct veth_port *port; - unsigned long flags; - - if (! dev) - continue; - - port = netdev_priv(dev); - - if (! (port->lpar_map & (1<<cnx->remote_lp))) - continue; - - spin_lock_irqsave(&port->queue_lock, flags); - - port->stopped_map &= ~(1 << cnx->remote_lp); - - if (0 == port->stopped_map && netif_queue_stopped(dev)) { - veth_debug("cnx %d: woke queue for %s.\n", - cnx->remote_lp, dev->name); - netif_wake_queue(dev); - } - spin_unlock_irqrestore(&port->queue_lock, flags); - } -} - -static void veth_stop_queues(struct veth_lpar_connection *cnx) -{ - int i; - - for (i = 0; i < HVMAXARCHITECTEDVIRTUALLANS; i++) { - struct net_device *dev = veth_dev[i]; - struct veth_port *port; - - if (! dev) - continue; - - port = netdev_priv(dev); - - /* If this cnx is not on the vlan for this port, continue */ - if (! (port->lpar_map & (1 << cnx->remote_lp))) - continue; - - spin_lock(&port->queue_lock); - - netif_stop_queue(dev); - port->stopped_map |= (1 << cnx->remote_lp); - - veth_debug("cnx %d: stopped queue for %s, map = 0x%x.\n", - cnx->remote_lp, dev->name, port->stopped_map); - - spin_unlock(&port->queue_lock); - } -} - -static void veth_timed_reset(unsigned long ptr) -{ - struct veth_lpar_connection *cnx = (struct veth_lpar_connection *)ptr; - unsigned long trigger_time, flags; - - /* FIXME is it possible this fires after veth_stop_connection()? - * That would reschedule the statemachine for 5 seconds and probably - * execute it after the module's been unloaded. Hmm. */ - - spin_lock_irqsave(&cnx->lock, flags); - - if (cnx->outstanding_tx > 0) { - trigger_time = cnx->last_contact + cnx->reset_timeout; - - if (trigger_time < jiffies) { - cnx->state |= VETH_STATE_RESET; - veth_kick_statemachine(cnx); - veth_error("%d packets not acked by LPAR %d within %d " - "seconds, resetting.\n", - cnx->outstanding_tx, cnx->remote_lp, - cnx->reset_timeout / HZ); - } else { - /* Reschedule the timer */ - trigger_time = jiffies + cnx->reset_timeout; - mod_timer(&cnx->reset_timer, trigger_time); - } - } - - spin_unlock_irqrestore(&cnx->lock, flags); -} - -/* - * Rx path - */ - -static inline int veth_frame_wanted(struct veth_port *port, u64 mac_addr) -{ - int wanted = 0; - int i; - unsigned long flags; - - if ( (mac_addr == port->mac_addr) || (mac_addr == 0xffffffffffff0000) ) - return 1; - - read_lock_irqsave(&port->mcast_gate, flags); - - if (port->promiscuous) { - wanted = 1; - goto out; - } - - for (i = 0; i < port->num_mcast; ++i) { - if (port->mcast_addr[i] == mac_addr) { - wanted = 1; - break; - } - } - - out: - read_unlock_irqrestore(&port->mcast_gate, flags); - - return wanted; -} - -struct dma_chunk { - u64 addr; - u64 size; -}; - -#define VETH_MAX_PAGES_PER_FRAME ( (VETH_MAX_MTU+PAGE_SIZE-2)/PAGE_SIZE + 1 ) - -static inline void veth_build_dma_list(struct dma_chunk *list, - unsigned char *p, unsigned long length) -{ - unsigned long done; - int i = 1; - - /* FIXME: skbs are contiguous in real addresses. Do we - * really need to break it into PAGE_SIZE chunks, or can we do - * it just at the granularity of iSeries real->absolute - * mapping? Indeed, given the way the allocator works, can we - * count on them being absolutely contiguous? */ - list[0].addr = iseries_hv_addr(p); - list[0].size = min(length, - PAGE_SIZE - ((unsigned long)p & ~PAGE_MASK)); - - done = list[0].size; - while (done < length) { - list[i].addr = iseries_hv_addr(p + done); - list[i].size = min(length-done, PAGE_SIZE); - done += list[i].size; - i++; - } -} - -static void veth_flush_acks(struct veth_lpar_connection *cnx) -{ - HvLpEvent_Rc rc; - - rc = veth_signaldata(cnx, VETH_EVENT_FRAMES_ACK, - 0, &cnx->pending_acks); - - if (rc != HvLpEvent_Rc_Good) - veth_error("Failed acking frames from LPAR %d, rc = %d\n", - cnx->remote_lp, (int)rc); - - cnx->num_pending_acks = 0; - memset(&cnx->pending_acks, 0xff, sizeof(cnx->pending_acks)); -} - -static void veth_receive(struct veth_lpar_connection *cnx, - struct veth_lpevent *event) -{ - struct veth_frames_data *senddata = &event->u.frames_data; - int startchunk = 0; - int nchunks; - unsigned long flags; - HvLpDma_Rc rc; - - do { - u16 length = 0; - struct sk_buff *skb; - struct dma_chunk local_list[VETH_MAX_PAGES_PER_FRAME]; - struct dma_chunk remote_list[VETH_MAX_FRAMES_PER_MSG]; - u64 dest; - HvLpVirtualLanIndex vlan; - struct net_device *dev; - struct veth_port *port; - - /* FIXME: do we need this? */ - memset(local_list, 0, sizeof(local_list)); - memset(remote_list, 0, sizeof(remote_list)); - - /* a 0 address marks the end of the valid entries */ - if (senddata->addr[startchunk] == 0) - break; - - /* make sure that we have at least 1 EOF entry in the - * remaining entries */ - if (! (senddata->eofmask >> (startchunk + VETH_EOF_SHIFT))) { - veth_error("Missing EOF fragment in event " - "eofmask = 0x%x startchunk = %d\n", - (unsigned)senddata->eofmask, - startchunk); - break; - } - - /* build list of chunks in this frame */ - nchunks = 0; - do { - remote_list[nchunks].addr = - (u64) senddata->addr[startchunk+nchunks] << 32; - remote_list[nchunks].size = - senddata->len[startchunk+nchunks]; - length += remote_list[nchunks].size; - } while (! (senddata->eofmask & - (1 << (VETH_EOF_SHIFT + startchunk + nchunks++)))); - - /* length == total length of all chunks */ - /* nchunks == # of chunks in this frame */ - - if ((length - ETH_HLEN) > VETH_MAX_MTU) { - veth_error("Received oversize frame from LPAR %d " - "(length = %d)\n", - cnx->remote_lp, length); - continue; - } - - skb = alloc_skb(length, GFP_ATOMIC); - if (!skb) - continue; - - veth_build_dma_list(local_list, skb->data, length); - - rc = HvCallEvent_dmaBufList(HvLpEvent_Type_VirtualLan, - event->base_event.xSourceLp, - HvLpDma_Direction_RemoteToLocal, - cnx->src_inst, - cnx->dst_inst, - HvLpDma_AddressType_RealAddress, - HvLpDma_AddressType_TceIndex, - iseries_hv_addr(&local_list), - iseries_hv_addr(&remote_list), - length); - if (rc != HvLpDma_Rc_Good) { - dev_kfree_skb_irq(skb); - continue; - } - - vlan = skb->data[9]; - dev = veth_dev[vlan]; - if (! dev) { - /* - * Some earlier versions of the driver sent - * broadcasts down all connections, even to lpars - * that weren't on the relevant vlan. So ignore - * packets belonging to a vlan we're not on. - * We can also be here if we receive packets while - * the driver is going down, because then dev is NULL. - */ - dev_kfree_skb_irq(skb); - continue; - } - - port = netdev_priv(dev); - dest = *((u64 *) skb->data) & 0xFFFFFFFFFFFF0000; - - if ((vlan > HVMAXARCHITECTEDVIRTUALLANS) || !port) { - dev_kfree_skb_irq(skb); - continue; - } - if (! veth_frame_wanted(port, dest)) { - dev_kfree_skb_irq(skb); - continue; - } - - skb_put(skb, length); - skb->protocol = eth_type_trans(skb, dev); - skb_checksum_none_assert(skb); - netif_rx(skb); /* send it up */ - dev->stats.rx_packets++; - dev->stats.rx_bytes += length; - } while (startchunk += nchunks, startchunk < VETH_MAX_FRAMES_PER_MSG); - - /* Ack it */ - spin_lock_irqsave(&cnx->lock, flags); - BUG_ON(cnx->num_pending_acks > VETH_MAX_ACKS_PER_MSG); - - cnx->pending_acks[cnx->num_pending_acks++] = - event->base_event.xCorrelationToken; - - if ( (cnx->num_pending_acks >= cnx->remote_caps.ack_threshold) || - (cnx->num_pending_acks >= VETH_MAX_ACKS_PER_MSG) ) - veth_flush_acks(cnx); - - spin_unlock_irqrestore(&cnx->lock, flags); -} - -static void veth_timed_ack(unsigned long ptr) -{ - struct veth_lpar_connection *cnx = (struct veth_lpar_connection *) ptr; - unsigned long flags; - - /* Ack all the events */ - spin_lock_irqsave(&cnx->lock, flags); - if (cnx->num_pending_acks > 0) - veth_flush_acks(cnx); - - /* Reschedule the timer */ - cnx->ack_timer.expires = jiffies + cnx->ack_timeout; - add_timer(&cnx->ack_timer); - spin_unlock_irqrestore(&cnx->lock, flags); -} - -static int veth_remove(struct vio_dev *vdev) -{ - struct veth_lpar_connection *cnx; - struct net_device *dev; - struct veth_port *port; - int i; - - dev = veth_dev[vdev->unit_address]; - - if (! dev) - return 0; - - port = netdev_priv(dev); - - for (i = 0; i < HVMAXARCHITECTEDLPS; i++) { - cnx = veth_cnx[i]; - - if (cnx && (port->lpar_map & (1 << i))) { - /* Drop our reference to connections on our VLAN */ - kobject_put(&cnx->kobject); - } - } - - veth_dev[vdev->unit_address] = NULL; - kobject_del(&port->kobject); - kobject_put(&port->kobject); - unregister_netdev(dev); - free_netdev(dev); - - return 0; -} - -static int veth_probe(struct vio_dev *vdev, const struct vio_device_id *id) -{ - int i = vdev->unit_address; - struct net_device *dev; - struct veth_port *port; - - dev = veth_probe_one(i, vdev); - if (dev == NULL) { - veth_remove(vdev); - return 1; - } - veth_dev[i] = dev; - - port = netdev_priv(dev); - - /* Start the state machine on each connection on this vlan. If we're - * the first dev to do so this will commence link negotiation */ - for (i = 0; i < HVMAXARCHITECTEDLPS; i++) { - struct veth_lpar_connection *cnx; - - if (! (port->lpar_map & (1 << i))) - continue; - - cnx = veth_cnx[i]; - if (!cnx) - continue; - - kobject_get(&cnx->kobject); - veth_kick_statemachine(cnx); - } - - return 0; -} - -/** - * veth_device_table: Used by vio.c to match devices that we - * support. - */ -static struct vio_device_id veth_device_table[] __devinitdata = { - { "network", "IBM,iSeries-l-lan" }, - { "", "" } -}; -MODULE_DEVICE_TABLE(vio, veth_device_table); - -static struct vio_driver veth_driver = { - .id_table = veth_device_table, - .probe = veth_probe, - .remove = veth_remove, - .driver = { - .name = DRV_NAME, - .owner = THIS_MODULE, - } -}; - -/* - * Module initialization/cleanup - */ - -static void __exit veth_module_cleanup(void) -{ - int i; - struct veth_lpar_connection *cnx; - - /* Disconnect our "irq" to stop events coming from the Hypervisor. */ - HvLpEvent_unregisterHandler(HvLpEvent_Type_VirtualLan); - - for (i = 0; i < HVMAXARCHITECTEDLPS; ++i) { - cnx = veth_cnx[i]; - - if (!cnx) - continue; - - /* Cancel work queued from Hypervisor callbacks */ - cancel_delayed_work_sync(&cnx->statemachine_wq); - /* Remove the connection from sysfs */ - kobject_del(&cnx->kobject); - /* Drop the driver's reference to the connection */ - kobject_put(&cnx->kobject); - } - - /* Unregister the driver, which will close all the netdevs and stop - * the connections when they're no longer referenced. */ - vio_unregister_driver(&veth_driver); -} -module_exit(veth_module_cleanup); - -static int __init veth_module_init(void) -{ - int i; - int rc; - - if (!firmware_has_feature(FW_FEATURE_ISERIES)) - return -ENODEV; - - this_lp = HvLpConfig_getLpIndex_outline(); - - for (i = 0; i < HVMAXARCHITECTEDLPS; ++i) { - rc = veth_init_connection(i); - if (rc != 0) - goto error; - } - - HvLpEvent_registerHandler(HvLpEvent_Type_VirtualLan, - &veth_handle_event); - - rc = vio_register_driver(&veth_driver); - if (rc != 0) - goto error; - - for (i = 0; i < HVMAXARCHITECTEDLPS; ++i) { - struct kobject *kobj; - - if (!veth_cnx[i]) - continue; - - kobj = &veth_cnx[i]->kobject; - /* If the add failes, complain but otherwise continue */ - if (0 != driver_add_kobj(&veth_driver.driver, kobj, - "cnx%.2d", veth_cnx[i]->remote_lp)) - veth_error("cnx %d: Failed adding to sysfs.\n", i); - } - - return 0; - -error: - for (i = 0; i < HVMAXARCHITECTEDLPS; ++i) { - veth_destroy_connection(veth_cnx[i]); - } - - return rc; -} -module_init(veth_module_init); |