15d01fa0cSAaron Young /* ldmvsw.c: Sun4v LDOM Virtual Switch Driver. 25d01fa0cSAaron Young * 3867fa150SShannon Nelson * Copyright (C) 2016-2017 Oracle. All rights reserved. 45d01fa0cSAaron Young */ 55d01fa0cSAaron Young 65d01fa0cSAaron Young #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 75d01fa0cSAaron Young 85d01fa0cSAaron Young #include <linux/delay.h> 95d01fa0cSAaron Young #include <linux/etherdevice.h> 105d01fa0cSAaron Young #include <linux/ethtool.h> 115d01fa0cSAaron Young #include <linux/highmem.h> 125d01fa0cSAaron Young #include <linux/if_vlan.h> 135d01fa0cSAaron Young #include <linux/init.h> 145d01fa0cSAaron Young #include <linux/kernel.h> 155d01fa0cSAaron Young #include <linux/module.h> 165d01fa0cSAaron Young #include <linux/mutex.h> 175d01fa0cSAaron Young #include <linux/netdevice.h> 185d01fa0cSAaron Young #include <linux/slab.h> 195d01fa0cSAaron Young #include <linux/types.h> 205d01fa0cSAaron Young 215d01fa0cSAaron Young #if defined(CONFIG_IPV6) 225d01fa0cSAaron Young #include <linux/icmpv6.h> 235d01fa0cSAaron Young #endif 245d01fa0cSAaron Young 255d01fa0cSAaron Young #include <net/ip.h> 265d01fa0cSAaron Young #include <net/icmp.h> 275d01fa0cSAaron Young #include <net/route.h> 285d01fa0cSAaron Young 295d01fa0cSAaron Young #include <asm/vio.h> 305d01fa0cSAaron Young #include <asm/ldc.h> 315d01fa0cSAaron Young 325d01fa0cSAaron Young /* This driver makes use of the common code in sunvnet_common.c */ 335d01fa0cSAaron Young #include "sunvnet_common.h" 345d01fa0cSAaron Young 355d01fa0cSAaron Young /* Length of time before we decide the hardware is hung, 365d01fa0cSAaron Young * and dev->tx_timeout() should be called to fix the problem. 375d01fa0cSAaron Young */ 385d01fa0cSAaron Young #define VSW_TX_TIMEOUT (10 * HZ) 395d01fa0cSAaron Young 405d01fa0cSAaron Young /* Static HW Addr used for the network interfaces representing vsw ports */ 415d01fa0cSAaron Young static u8 vsw_port_hwaddr[ETH_ALEN] = {0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; 425d01fa0cSAaron Young 435d01fa0cSAaron Young #define DRV_MODULE_NAME "ldmvsw" 44867fa150SShannon Nelson #define DRV_MODULE_VERSION "1.2" 45867fa150SShannon Nelson #define DRV_MODULE_RELDATE "March 4, 2017" 465d01fa0cSAaron Young 475d01fa0cSAaron Young static char version[] = 487602011fSShannon Nelson DRV_MODULE_NAME " " DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")"; 495d01fa0cSAaron Young MODULE_AUTHOR("Oracle"); 505d01fa0cSAaron Young MODULE_DESCRIPTION("Sun4v LDOM Virtual Switch Driver"); 515d01fa0cSAaron Young MODULE_LICENSE("GPL"); 525d01fa0cSAaron Young MODULE_VERSION(DRV_MODULE_VERSION); 535d01fa0cSAaron Young 545d01fa0cSAaron Young /* Ordered from largest major to lowest */ 555d01fa0cSAaron Young static struct vio_version vsw_versions[] = { 565d01fa0cSAaron Young { .major = 1, .minor = 8 }, 575d01fa0cSAaron Young { .major = 1, .minor = 7 }, 585d01fa0cSAaron Young { .major = 1, .minor = 6 }, 595d01fa0cSAaron Young { .major = 1, .minor = 0 }, 605d01fa0cSAaron Young }; 615d01fa0cSAaron Young 625d01fa0cSAaron Young static void vsw_get_drvinfo(struct net_device *dev, 635d01fa0cSAaron Young struct ethtool_drvinfo *info) 645d01fa0cSAaron Young { 655d01fa0cSAaron Young strlcpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); 665d01fa0cSAaron Young strlcpy(info->version, DRV_MODULE_VERSION, sizeof(info->version)); 675d01fa0cSAaron Young } 685d01fa0cSAaron Young 695d01fa0cSAaron Young static u32 vsw_get_msglevel(struct net_device *dev) 705d01fa0cSAaron Young { 715d01fa0cSAaron Young struct vnet_port *port = netdev_priv(dev); 725d01fa0cSAaron Young 735d01fa0cSAaron Young return port->vp->msg_enable; 745d01fa0cSAaron Young } 755d01fa0cSAaron Young 765d01fa0cSAaron Young static void vsw_set_msglevel(struct net_device *dev, u32 value) 775d01fa0cSAaron Young { 785d01fa0cSAaron Young struct vnet_port *port = netdev_priv(dev); 795d01fa0cSAaron Young 805d01fa0cSAaron Young port->vp->msg_enable = value; 815d01fa0cSAaron Young } 825d01fa0cSAaron Young 835d01fa0cSAaron Young static const struct ethtool_ops vsw_ethtool_ops = { 845d01fa0cSAaron Young .get_drvinfo = vsw_get_drvinfo, 855d01fa0cSAaron Young .get_msglevel = vsw_get_msglevel, 865d01fa0cSAaron Young .set_msglevel = vsw_set_msglevel, 875d01fa0cSAaron Young .get_link = ethtool_op_get_link, 885d01fa0cSAaron Young }; 895d01fa0cSAaron Young 905d01fa0cSAaron Young static LIST_HEAD(vnet_list); 915d01fa0cSAaron Young static DEFINE_MUTEX(vnet_list_mutex); 925d01fa0cSAaron Young 935d01fa0cSAaron Young /* func arg to vnet_start_xmit_common() to get the proper tx port */ 945d01fa0cSAaron Young static struct vnet_port *vsw_tx_port_find(struct sk_buff *skb, 955d01fa0cSAaron Young struct net_device *dev) 965d01fa0cSAaron Young { 975d01fa0cSAaron Young struct vnet_port *port = netdev_priv(dev); 985d01fa0cSAaron Young 995d01fa0cSAaron Young return port; 1005d01fa0cSAaron Young } 1015d01fa0cSAaron Young 1025d01fa0cSAaron Young static u16 vsw_select_queue(struct net_device *dev, struct sk_buff *skb, 1035d01fa0cSAaron Young void *accel_priv, select_queue_fallback_t fallback) 1045d01fa0cSAaron Young { 1055d01fa0cSAaron Young struct vnet_port *port = netdev_priv(dev); 1065d01fa0cSAaron Young 1075d01fa0cSAaron Young if (!port) 1085d01fa0cSAaron Young return 0; 1095d01fa0cSAaron Young 1105d01fa0cSAaron Young return port->q_index; 1115d01fa0cSAaron Young } 1125d01fa0cSAaron Young 1135d01fa0cSAaron Young /* Wrappers to common functions */ 1145d01fa0cSAaron Young static int vsw_start_xmit(struct sk_buff *skb, struct net_device *dev) 1155d01fa0cSAaron Young { 1165d01fa0cSAaron Young return sunvnet_start_xmit_common(skb, dev, vsw_tx_port_find); 1175d01fa0cSAaron Young } 1185d01fa0cSAaron Young 1195d01fa0cSAaron Young static void vsw_set_rx_mode(struct net_device *dev) 1205d01fa0cSAaron Young { 1215d01fa0cSAaron Young struct vnet_port *port = netdev_priv(dev); 1225d01fa0cSAaron Young 1235d01fa0cSAaron Young return sunvnet_set_rx_mode_common(dev, port->vp); 1245d01fa0cSAaron Young } 1255d01fa0cSAaron Young 126867fa150SShannon Nelson int ldmvsw_open(struct net_device *dev) 127867fa150SShannon Nelson { 128867fa150SShannon Nelson struct vnet_port *port = netdev_priv(dev); 129867fa150SShannon Nelson struct vio_driver_state *vio = &port->vio; 130867fa150SShannon Nelson 131867fa150SShannon Nelson /* reset the channel */ 132867fa150SShannon Nelson vio_link_state_change(vio, LDC_EVENT_RESET); 133867fa150SShannon Nelson vnet_port_reset(port); 134867fa150SShannon Nelson vio_port_up(vio); 135867fa150SShannon Nelson 136867fa150SShannon Nelson return 0; 137867fa150SShannon Nelson } 138867fa150SShannon Nelson EXPORT_SYMBOL_GPL(ldmvsw_open); 139867fa150SShannon Nelson 1405d01fa0cSAaron Young #ifdef CONFIG_NET_POLL_CONTROLLER 1415d01fa0cSAaron Young static void vsw_poll_controller(struct net_device *dev) 1425d01fa0cSAaron Young { 1435d01fa0cSAaron Young struct vnet_port *port = netdev_priv(dev); 1445d01fa0cSAaron Young 1455d01fa0cSAaron Young return sunvnet_poll_controller_common(dev, port->vp); 1465d01fa0cSAaron Young } 1475d01fa0cSAaron Young #endif 1485d01fa0cSAaron Young 1495d01fa0cSAaron Young static const struct net_device_ops vsw_ops = { 150867fa150SShannon Nelson .ndo_open = ldmvsw_open, 1515d01fa0cSAaron Young .ndo_stop = sunvnet_close_common, 1525d01fa0cSAaron Young .ndo_set_rx_mode = vsw_set_rx_mode, 1535d01fa0cSAaron Young .ndo_set_mac_address = sunvnet_set_mac_addr_common, 1545d01fa0cSAaron Young .ndo_validate_addr = eth_validate_addr, 1555d01fa0cSAaron Young .ndo_tx_timeout = sunvnet_tx_timeout_common, 1565d01fa0cSAaron Young .ndo_start_xmit = vsw_start_xmit, 1575d01fa0cSAaron Young .ndo_select_queue = vsw_select_queue, 1585d01fa0cSAaron Young #ifdef CONFIG_NET_POLL_CONTROLLER 1595d01fa0cSAaron Young .ndo_poll_controller = vsw_poll_controller, 1605d01fa0cSAaron Young #endif 1615d01fa0cSAaron Young }; 1625d01fa0cSAaron Young 1635d01fa0cSAaron Young static const char *local_mac_prop = "local-mac-address"; 1645d01fa0cSAaron Young static const char *cfg_handle_prop = "cfg-handle"; 1655d01fa0cSAaron Young 1665d01fa0cSAaron Young static struct vnet *vsw_get_vnet(struct mdesc_handle *hp, 1675d01fa0cSAaron Young u64 port_node, 1685d01fa0cSAaron Young u64 *handle) 1695d01fa0cSAaron Young { 1705d01fa0cSAaron Young struct vnet *vp; 1715d01fa0cSAaron Young struct vnet *iter; 1725d01fa0cSAaron Young const u64 *local_mac = NULL; 1735d01fa0cSAaron Young const u64 *cfghandle = NULL; 1745d01fa0cSAaron Young u64 a; 1755d01fa0cSAaron Young 1765d01fa0cSAaron Young /* Get the parent virtual-network-switch macaddr and cfghandle */ 1775d01fa0cSAaron Young mdesc_for_each_arc(a, hp, port_node, MDESC_ARC_TYPE_BACK) { 1785d01fa0cSAaron Young u64 target = mdesc_arc_target(hp, a); 1795d01fa0cSAaron Young const char *name; 1805d01fa0cSAaron Young 1815d01fa0cSAaron Young name = mdesc_get_property(hp, target, "name", NULL); 1825d01fa0cSAaron Young if (!name || strcmp(name, "virtual-network-switch")) 1835d01fa0cSAaron Young continue; 1845d01fa0cSAaron Young 1855d01fa0cSAaron Young local_mac = mdesc_get_property(hp, target, 1865d01fa0cSAaron Young local_mac_prop, NULL); 1875d01fa0cSAaron Young cfghandle = mdesc_get_property(hp, target, 1885d01fa0cSAaron Young cfg_handle_prop, NULL); 1895d01fa0cSAaron Young break; 1905d01fa0cSAaron Young } 1915d01fa0cSAaron Young if (!local_mac || !cfghandle) 1925d01fa0cSAaron Young return ERR_PTR(-ENODEV); 1935d01fa0cSAaron Young 1945d01fa0cSAaron Young /* find or create associated vnet */ 1955d01fa0cSAaron Young vp = NULL; 1965d01fa0cSAaron Young mutex_lock(&vnet_list_mutex); 1975d01fa0cSAaron Young list_for_each_entry(iter, &vnet_list, list) { 1985d01fa0cSAaron Young if (iter->local_mac == *local_mac) { 1995d01fa0cSAaron Young vp = iter; 2005d01fa0cSAaron Young break; 2015d01fa0cSAaron Young } 2025d01fa0cSAaron Young } 2035d01fa0cSAaron Young 2045d01fa0cSAaron Young if (!vp) { 2055d01fa0cSAaron Young vp = kzalloc(sizeof(*vp), GFP_KERNEL); 2065d01fa0cSAaron Young if (unlikely(!vp)) { 2075d01fa0cSAaron Young mutex_unlock(&vnet_list_mutex); 2085d01fa0cSAaron Young return ERR_PTR(-ENOMEM); 2095d01fa0cSAaron Young } 2105d01fa0cSAaron Young 2115d01fa0cSAaron Young spin_lock_init(&vp->lock); 2125d01fa0cSAaron Young INIT_LIST_HEAD(&vp->port_list); 2135d01fa0cSAaron Young INIT_LIST_HEAD(&vp->list); 2145d01fa0cSAaron Young vp->local_mac = *local_mac; 2155d01fa0cSAaron Young list_add(&vp->list, &vnet_list); 2165d01fa0cSAaron Young } 2175d01fa0cSAaron Young 2185d01fa0cSAaron Young mutex_unlock(&vnet_list_mutex); 2195d01fa0cSAaron Young 2205d01fa0cSAaron Young *handle = (u64)*cfghandle; 2215d01fa0cSAaron Young 2225d01fa0cSAaron Young return vp; 2235d01fa0cSAaron Young } 2245d01fa0cSAaron Young 2255d01fa0cSAaron Young static struct net_device *vsw_alloc_netdev(u8 hwaddr[], 2265d01fa0cSAaron Young struct vio_dev *vdev, 2275d01fa0cSAaron Young u64 handle, 2285d01fa0cSAaron Young u64 port_id) 2295d01fa0cSAaron Young { 2305d01fa0cSAaron Young struct net_device *dev; 2315d01fa0cSAaron Young struct vnet_port *port; 2325d01fa0cSAaron Young int i; 2335d01fa0cSAaron Young 2345d01fa0cSAaron Young dev = alloc_etherdev_mqs(sizeof(*port), VNET_MAX_TXQS, 1); 2355d01fa0cSAaron Young if (!dev) 2365d01fa0cSAaron Young return ERR_PTR(-ENOMEM); 2375d01fa0cSAaron Young dev->needed_headroom = VNET_PACKET_SKIP + 8; 2385d01fa0cSAaron Young dev->needed_tailroom = 8; 2395d01fa0cSAaron Young 2405d01fa0cSAaron Young for (i = 0; i < ETH_ALEN; i++) { 2415d01fa0cSAaron Young dev->dev_addr[i] = hwaddr[i]; 2425d01fa0cSAaron Young dev->perm_addr[i] = dev->dev_addr[i]; 2435d01fa0cSAaron Young } 2445d01fa0cSAaron Young 2455d01fa0cSAaron Young sprintf(dev->name, "vif%d.%d", (int)handle, (int)port_id); 2465d01fa0cSAaron Young 2475d01fa0cSAaron Young dev->netdev_ops = &vsw_ops; 2485d01fa0cSAaron Young dev->ethtool_ops = &vsw_ethtool_ops; 2495d01fa0cSAaron Young dev->watchdog_timeo = VSW_TX_TIMEOUT; 2505d01fa0cSAaron Young 251bc221a34SShannon Nelson dev->hw_features = NETIF_F_HW_CSUM | NETIF_F_SG; 2525d01fa0cSAaron Young dev->features = dev->hw_features; 2535d01fa0cSAaron Young 254540bfe30SJarod Wilson /* MTU range: 68 - 65535 */ 255540bfe30SJarod Wilson dev->min_mtu = ETH_MIN_MTU; 256540bfe30SJarod Wilson dev->max_mtu = VNET_MAX_MTU; 257540bfe30SJarod Wilson 2585d01fa0cSAaron Young SET_NETDEV_DEV(dev, &vdev->dev); 2595d01fa0cSAaron Young 2605d01fa0cSAaron Young return dev; 2615d01fa0cSAaron Young } 2625d01fa0cSAaron Young 2635d01fa0cSAaron Young static struct ldc_channel_config vsw_ldc_cfg = { 2645d01fa0cSAaron Young .event = sunvnet_event_common, 2655d01fa0cSAaron Young .mtu = 64, 2665d01fa0cSAaron Young .mode = LDC_MODE_UNRELIABLE, 2675d01fa0cSAaron Young }; 2685d01fa0cSAaron Young 2695d01fa0cSAaron Young static struct vio_driver_ops vsw_vio_ops = { 2705d01fa0cSAaron Young .send_attr = sunvnet_send_attr_common, 2715d01fa0cSAaron Young .handle_attr = sunvnet_handle_attr_common, 2725d01fa0cSAaron Young .handshake_complete = sunvnet_handshake_complete_common, 2735d01fa0cSAaron Young }; 2745d01fa0cSAaron Young 2755d01fa0cSAaron Young static const char *remote_macaddr_prop = "remote-mac-address"; 2765d01fa0cSAaron Young static const char *id_prop = "id"; 2775d01fa0cSAaron Young 2785d01fa0cSAaron Young static int vsw_port_probe(struct vio_dev *vdev, const struct vio_device_id *id) 2795d01fa0cSAaron Young { 2805d01fa0cSAaron Young struct mdesc_handle *hp; 2815d01fa0cSAaron Young struct vnet_port *port; 2825d01fa0cSAaron Young unsigned long flags; 2835d01fa0cSAaron Young struct vnet *vp; 2845d01fa0cSAaron Young struct net_device *dev; 2855d01fa0cSAaron Young const u64 *rmac; 2865d01fa0cSAaron Young int len, i, err; 2875d01fa0cSAaron Young const u64 *port_id; 2885d01fa0cSAaron Young u64 handle; 2895d01fa0cSAaron Young 2905d01fa0cSAaron Young hp = mdesc_grab(); 2915d01fa0cSAaron Young 2925d01fa0cSAaron Young rmac = mdesc_get_property(hp, vdev->mp, remote_macaddr_prop, &len); 2935d01fa0cSAaron Young err = -ENODEV; 2945d01fa0cSAaron Young if (!rmac) { 2955d01fa0cSAaron Young pr_err("Port lacks %s property\n", remote_macaddr_prop); 2965d01fa0cSAaron Young mdesc_release(hp); 2975d01fa0cSAaron Young return err; 2985d01fa0cSAaron Young } 2995d01fa0cSAaron Young 3005d01fa0cSAaron Young port_id = mdesc_get_property(hp, vdev->mp, id_prop, NULL); 3015d01fa0cSAaron Young err = -ENODEV; 3025d01fa0cSAaron Young if (!port_id) { 3035d01fa0cSAaron Young pr_err("Port lacks %s property\n", id_prop); 3045d01fa0cSAaron Young mdesc_release(hp); 3055d01fa0cSAaron Young return err; 3065d01fa0cSAaron Young } 3075d01fa0cSAaron Young 3085d01fa0cSAaron Young /* Get (or create) the vnet associated with this port */ 3095d01fa0cSAaron Young vp = vsw_get_vnet(hp, vdev->mp, &handle); 3105d01fa0cSAaron Young if (unlikely(IS_ERR(vp))) { 3115d01fa0cSAaron Young err = PTR_ERR(vp); 3125d01fa0cSAaron Young pr_err("Failed to get vnet for vsw-port\n"); 3135d01fa0cSAaron Young mdesc_release(hp); 3145d01fa0cSAaron Young return err; 3155d01fa0cSAaron Young } 3165d01fa0cSAaron Young 3175d01fa0cSAaron Young mdesc_release(hp); 3185d01fa0cSAaron Young 3195d01fa0cSAaron Young dev = vsw_alloc_netdev(vsw_port_hwaddr, vdev, handle, *port_id); 3205d01fa0cSAaron Young if (IS_ERR(dev)) { 3215d01fa0cSAaron Young err = PTR_ERR(dev); 3225d01fa0cSAaron Young pr_err("Failed to alloc netdev for vsw-port\n"); 3235d01fa0cSAaron Young return err; 3245d01fa0cSAaron Young } 3255d01fa0cSAaron Young 3265d01fa0cSAaron Young port = netdev_priv(dev); 3275d01fa0cSAaron Young 3285d01fa0cSAaron Young INIT_LIST_HEAD(&port->list); 3295d01fa0cSAaron Young 3305d01fa0cSAaron Young for (i = 0; i < ETH_ALEN; i++) 3315d01fa0cSAaron Young port->raddr[i] = (*rmac >> (5 - i) * 8) & 0xff; 3325d01fa0cSAaron Young 3335d01fa0cSAaron Young port->vp = vp; 3345d01fa0cSAaron Young port->dev = dev; 3355d01fa0cSAaron Young port->switch_port = 1; 336bc221a34SShannon Nelson port->tso = false; /* no tso in vsw, misbehaves in bridge */ 3375d01fa0cSAaron Young port->tsolen = 0; 3385d01fa0cSAaron Young 3395d01fa0cSAaron Young /* Mark the port as belonging to ldmvsw which directs the 3405d01fa0cSAaron Young * the common code to use the net_device in the vnet_port 3415d01fa0cSAaron Young * rather than the net_device in the vnet (which is used 3425d01fa0cSAaron Young * by sunvnet). This bit is used by the VNET_PORT_TO_NET_DEVICE 3435d01fa0cSAaron Young * macro. 3445d01fa0cSAaron Young */ 3455d01fa0cSAaron Young port->vsw = 1; 3465d01fa0cSAaron Young 3475d01fa0cSAaron Young err = vio_driver_init(&port->vio, vdev, VDEV_NETWORK, 3485d01fa0cSAaron Young vsw_versions, ARRAY_SIZE(vsw_versions), 3495d01fa0cSAaron Young &vsw_vio_ops, dev->name); 3505d01fa0cSAaron Young if (err) 3515d01fa0cSAaron Young goto err_out_free_dev; 3525d01fa0cSAaron Young 3535d01fa0cSAaron Young err = vio_ldc_alloc(&port->vio, &vsw_ldc_cfg, port); 3545d01fa0cSAaron Young if (err) 3555d01fa0cSAaron Young goto err_out_free_dev; 3565d01fa0cSAaron Young 3575d01fa0cSAaron Young dev_set_drvdata(&vdev->dev, port); 3585d01fa0cSAaron Young 3595d01fa0cSAaron Young netif_napi_add(dev, &port->napi, sunvnet_poll_common, 3605d01fa0cSAaron Young NAPI_POLL_WEIGHT); 3615d01fa0cSAaron Young 3625d01fa0cSAaron Young spin_lock_irqsave(&vp->lock, flags); 3635d01fa0cSAaron Young list_add_rcu(&port->list, &vp->port_list); 3645d01fa0cSAaron Young spin_unlock_irqrestore(&vp->lock, flags); 3655d01fa0cSAaron Young 3665d01fa0cSAaron Young setup_timer(&port->clean_timer, sunvnet_clean_timer_expire_common, 3675d01fa0cSAaron Young (unsigned long)port); 3685d01fa0cSAaron Young 3695d01fa0cSAaron Young err = register_netdev(dev); 3705d01fa0cSAaron Young if (err) { 3715d01fa0cSAaron Young pr_err("Cannot register net device, aborting\n"); 3725d01fa0cSAaron Young goto err_out_del_timer; 3735d01fa0cSAaron Young } 3745d01fa0cSAaron Young 3755d01fa0cSAaron Young spin_lock_irqsave(&vp->lock, flags); 3765d01fa0cSAaron Young sunvnet_port_add_txq_common(port); 3775d01fa0cSAaron Young spin_unlock_irqrestore(&vp->lock, flags); 3785d01fa0cSAaron Young 3795d01fa0cSAaron Young napi_enable(&port->napi); 3805d01fa0cSAaron Young vio_port_up(&port->vio); 3815d01fa0cSAaron Young 382867fa150SShannon Nelson /* assure no carrier until we receive an LDC_EVENT_UP, 383867fa150SShannon Nelson * even if the vsw config script tries to force us up 384867fa150SShannon Nelson */ 385867fa150SShannon Nelson netif_carrier_off(dev); 386867fa150SShannon Nelson 3875d01fa0cSAaron Young netdev_info(dev, "LDOM vsw-port %pM\n", dev->dev_addr); 3885d01fa0cSAaron Young 3895d01fa0cSAaron Young pr_info("%s: PORT ( remote-mac %pM%s )\n", dev->name, 3905d01fa0cSAaron Young port->raddr, " switch-port"); 3915d01fa0cSAaron Young 3925d01fa0cSAaron Young return 0; 3935d01fa0cSAaron Young 3945d01fa0cSAaron Young err_out_del_timer: 3955d01fa0cSAaron Young del_timer_sync(&port->clean_timer); 3965d01fa0cSAaron Young list_del_rcu(&port->list); 3975d01fa0cSAaron Young synchronize_rcu(); 3985d01fa0cSAaron Young netif_napi_del(&port->napi); 3995d01fa0cSAaron Young dev_set_drvdata(&vdev->dev, NULL); 4005d01fa0cSAaron Young vio_ldc_free(&port->vio); 4015d01fa0cSAaron Young 4025d01fa0cSAaron Young err_out_free_dev: 4035d01fa0cSAaron Young free_netdev(dev); 4045d01fa0cSAaron Young return err; 4055d01fa0cSAaron Young } 4065d01fa0cSAaron Young 4075d01fa0cSAaron Young static int vsw_port_remove(struct vio_dev *vdev) 4085d01fa0cSAaron Young { 4095d01fa0cSAaron Young struct vnet_port *port = dev_get_drvdata(&vdev->dev); 4105d01fa0cSAaron Young unsigned long flags; 4115d01fa0cSAaron Young 4125d01fa0cSAaron Young if (port) { 4135d01fa0cSAaron Young del_timer_sync(&port->vio.timer); 4145d01fa0cSAaron Young 4155d01fa0cSAaron Young napi_disable(&port->napi); 416b18e5e86SThomas Tai unregister_netdev(port->dev); 4175d01fa0cSAaron Young 4185d01fa0cSAaron Young list_del_rcu(&port->list); 4195d01fa0cSAaron Young 4205d01fa0cSAaron Young synchronize_rcu(); 4215d01fa0cSAaron Young del_timer_sync(&port->clean_timer); 4225d01fa0cSAaron Young spin_lock_irqsave(&port->vp->lock, flags); 4235d01fa0cSAaron Young sunvnet_port_rm_txq_common(port); 4245d01fa0cSAaron Young spin_unlock_irqrestore(&port->vp->lock, flags); 4255d01fa0cSAaron Young netif_napi_del(&port->napi); 4265d01fa0cSAaron Young sunvnet_port_free_tx_bufs_common(port); 4275d01fa0cSAaron Young vio_ldc_free(&port->vio); 4285d01fa0cSAaron Young 4295d01fa0cSAaron Young dev_set_drvdata(&vdev->dev, NULL); 4305d01fa0cSAaron Young 4315d01fa0cSAaron Young free_netdev(port->dev); 4325d01fa0cSAaron Young } 4335d01fa0cSAaron Young 4345d01fa0cSAaron Young return 0; 4355d01fa0cSAaron Young } 4365d01fa0cSAaron Young 4375d01fa0cSAaron Young static void vsw_cleanup(void) 4385d01fa0cSAaron Young { 4395d01fa0cSAaron Young struct vnet *vp; 4405d01fa0cSAaron Young 4415d01fa0cSAaron Young /* just need to free up the vnet list */ 4425d01fa0cSAaron Young mutex_lock(&vnet_list_mutex); 4435d01fa0cSAaron Young while (!list_empty(&vnet_list)) { 4445d01fa0cSAaron Young vp = list_first_entry(&vnet_list, struct vnet, list); 4455d01fa0cSAaron Young list_del(&vp->list); 4465d01fa0cSAaron Young /* vio_unregister_driver() should have cleaned up port_list */ 4475d01fa0cSAaron Young if (!list_empty(&vp->port_list)) 4485d01fa0cSAaron Young pr_err("Ports not removed by VIO subsystem!\n"); 4495d01fa0cSAaron Young kfree(vp); 4505d01fa0cSAaron Young } 4515d01fa0cSAaron Young mutex_unlock(&vnet_list_mutex); 4525d01fa0cSAaron Young } 4535d01fa0cSAaron Young 4545d01fa0cSAaron Young static const struct vio_device_id vsw_port_match[] = { 4555d01fa0cSAaron Young { 4565d01fa0cSAaron Young .type = "vsw-port", 4575d01fa0cSAaron Young }, 4585d01fa0cSAaron Young {}, 4595d01fa0cSAaron Young }; 4605d01fa0cSAaron Young MODULE_DEVICE_TABLE(vio, vsw_port_match); 4615d01fa0cSAaron Young 4625d01fa0cSAaron Young static struct vio_driver vsw_port_driver = { 4635d01fa0cSAaron Young .id_table = vsw_port_match, 4645d01fa0cSAaron Young .probe = vsw_port_probe, 4655d01fa0cSAaron Young .remove = vsw_port_remove, 4665d01fa0cSAaron Young .name = "vsw_port", 4675d01fa0cSAaron Young }; 4685d01fa0cSAaron Young 4695d01fa0cSAaron Young static int __init vsw_init(void) 4705d01fa0cSAaron Young { 4717602011fSShannon Nelson pr_info("%s\n", version); 4725d01fa0cSAaron Young return vio_register_driver(&vsw_port_driver); 4735d01fa0cSAaron Young } 4745d01fa0cSAaron Young 4755d01fa0cSAaron Young static void __exit vsw_exit(void) 4765d01fa0cSAaron Young { 4775d01fa0cSAaron Young vio_unregister_driver(&vsw_port_driver); 4785d01fa0cSAaron Young vsw_cleanup(); 4795d01fa0cSAaron Young } 4805d01fa0cSAaron Young 4815d01fa0cSAaron Young module_init(vsw_init); 4825d01fa0cSAaron Young module_exit(vsw_exit); 483