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