X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=drivers%2Fnet%2Fethernet%2Fintel%2Ffm10k%2Ffm10k_netdev.c;h=b57ea1c09a21aacec533a10547a20acf6616f995;hb=13cb2dad45cc8c8e350abc84de38449b89629c3c;hp=2cda9f99edc38f9d5906d3f2e4695ffc8b903203;hpb=aa3ac822689f5c4d8961a8707edba9b6bf4631a3;p=deliverable%2Flinux.git diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c index 2cda9f99edc3..b57ea1c09a21 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c @@ -20,6 +20,9 @@ #include "fm10k.h" #include +#if IS_ENABLED(CONFIG_VXLAN) +#include +#endif /* CONFIG_VXLAN */ /** * fm10k_setup_tx_resources - allocate Tx resources (Descriptors) @@ -240,6 +243,9 @@ void fm10k_clean_all_tx_rings(struct fm10k_intfc *interface) for (i = 0; i < interface->num_tx_queues; i++) fm10k_clean_tx_ring(interface->tx_ring[i]); + + /* remove any stale timestamp buffers and free them */ + skb_queue_purge(&interface->ts_tx_skb_queue); } /** @@ -365,7 +371,143 @@ static void fm10k_request_glort_range(struct fm10k_intfc *interface) if (hw->mac.dglort_map == FM10K_DGLORTMAP_NONE) return; - interface->glort_count = mask + 1; + /* we support 3 possible GLORT configurations. + * 1: VFs consume all but the last 1 + * 2: VFs and PF split glorts with possible gap between + * 3: VFs allocated first 64, all others belong to PF + */ + if (mask <= hw->iov.total_vfs) { + interface->glort_count = 1; + interface->glort += mask; + } else if (mask < 64) { + interface->glort_count = (mask + 1) / 2; + interface->glort += interface->glort_count; + } else { + interface->glort_count = mask - 63; + interface->glort += 64; + } +} + +/** + * fm10k_del_vxlan_port_all + * @interface: board private structure + * + * This function frees the entire vxlan_port list + **/ +static void fm10k_del_vxlan_port_all(struct fm10k_intfc *interface) +{ + struct fm10k_vxlan_port *vxlan_port; + + /* flush all entries from list */ + vxlan_port = list_first_entry_or_null(&interface->vxlan_port, + struct fm10k_vxlan_port, list); + while (vxlan_port) { + list_del(&vxlan_port->list); + kfree(vxlan_port); + vxlan_port = list_first_entry_or_null(&interface->vxlan_port, + struct fm10k_vxlan_port, + list); + } +} + +/** + * fm10k_restore_vxlan_port + * @interface: board private structure + * + * This function restores the value in the tunnel_cfg register after reset + **/ +static void fm10k_restore_vxlan_port(struct fm10k_intfc *interface) +{ + struct fm10k_hw *hw = &interface->hw; + struct fm10k_vxlan_port *vxlan_port; + + /* only the PF supports configuring tunnels */ + if (hw->mac.type != fm10k_mac_pf) + return; + + vxlan_port = list_first_entry_or_null(&interface->vxlan_port, + struct fm10k_vxlan_port, list); + + /* restore tunnel configuration register */ + fm10k_write_reg(hw, FM10K_TUNNEL_CFG, + (vxlan_port ? ntohs(vxlan_port->port) : 0) | + (ETH_P_TEB << FM10K_TUNNEL_CFG_NVGRE_SHIFT)); +} + +/** + * fm10k_add_vxlan_port + * @netdev: network interface device structure + * @sa_family: Address family of new port + * @port: port number used for VXLAN + * + * This funciton is called when a new VXLAN interface has added a new port + * number to the range that is currently in use for VXLAN. The new port + * number is always added to the tail so that the port number list should + * match the order in which the ports were allocated. The head of the list + * is always used as the VXLAN port number for offloads. + **/ +static void fm10k_add_vxlan_port(struct net_device *dev, + sa_family_t sa_family, __be16 port) { + struct fm10k_intfc *interface = netdev_priv(dev); + struct fm10k_vxlan_port *vxlan_port; + + /* only the PF supports configuring tunnels */ + if (interface->hw.mac.type != fm10k_mac_pf) + return; + + /* existing ports are pulled out so our new entry is always last */ + fm10k_vxlan_port_for_each(vxlan_port, interface) { + if ((vxlan_port->port == port) && + (vxlan_port->sa_family == sa_family)) { + list_del(&vxlan_port->list); + goto insert_tail; + } + } + + /* allocate memory to track ports */ + vxlan_port = kmalloc(sizeof(*vxlan_port), GFP_ATOMIC); + if (!vxlan_port) + return; + vxlan_port->port = port; + vxlan_port->sa_family = sa_family; + +insert_tail: + /* add new port value to list */ + list_add_tail(&vxlan_port->list, &interface->vxlan_port); + + fm10k_restore_vxlan_port(interface); +} + +/** + * fm10k_del_vxlan_port + * @netdev: network interface device structure + * @sa_family: Address family of freed port + * @port: port number used for VXLAN + * + * This funciton is called when a new VXLAN interface has freed a port + * number from the range that is currently in use for VXLAN. The freed + * port is removed from the list and the new head is used to determine + * the port number for offloads. + **/ +static void fm10k_del_vxlan_port(struct net_device *dev, + sa_family_t sa_family, __be16 port) { + struct fm10k_intfc *interface = netdev_priv(dev); + struct fm10k_vxlan_port *vxlan_port; + + if (interface->hw.mac.type != fm10k_mac_pf) + return; + + /* find the port in the list and free it */ + fm10k_vxlan_port_for_each(vxlan_port, interface) { + if ((vxlan_port->port == port) && + (vxlan_port->sa_family == sa_family)) { + list_del(&vxlan_port->list); + kfree(vxlan_port); + break; + } + } + + fm10k_restore_vxlan_port(interface); } /** @@ -404,12 +546,21 @@ int fm10k_open(struct net_device *netdev) fm10k_request_glort_range(interface); /* Notify the stack of the actual queue counts */ + err = netif_set_real_num_tx_queues(netdev, + interface->num_tx_queues); + if (err) + goto err_set_queues; err = netif_set_real_num_rx_queues(netdev, interface->num_rx_queues); if (err) goto err_set_queues; +#if IS_ENABLED(CONFIG_VXLAN) + /* update VXLAN port configuration */ + vxlan_get_rx_port(netdev); + +#endif fm10k_up(interface); return 0; @@ -443,6 +594,8 @@ int fm10k_close(struct net_device *netdev) fm10k_qv_free_irq(interface); + fm10k_del_vxlan_port_all(interface); + fm10k_free_all_tx_resources(interface); fm10k_free_all_rx_resources(interface); @@ -452,7 +605,7 @@ int fm10k_close(struct net_device *netdev) static netdev_tx_t fm10k_xmit_frame(struct sk_buff *skb, struct net_device *dev) { struct fm10k_intfc *interface = netdev_priv(dev); - unsigned int r_idx = 0; + unsigned int r_idx = skb->queue_mapping; int err; if ((skb->protocol == htons(ETH_P_8021Q)) && @@ -505,6 +658,10 @@ static netdev_tx_t fm10k_xmit_frame(struct sk_buff *skb, struct net_device *dev) __skb_put(skb, pad_len); } + /* prepare packet for hardware time stamping */ + if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) + fm10k_ts_tx_enqueue(interface, skb); + if (r_idx >= interface->num_tx_queues) r_idx %= interface->num_tx_queues; @@ -628,14 +785,14 @@ static int fm10k_update_vid(struct net_device *netdev, u16 vid, bool set) if (!(netdev->flags & IFF_PROMISC)) { err = hw->mac.ops.update_vlan(hw, vid, 0, set); if (err) - return err; + goto err_out; } /* update our base MAC address */ err = hw->mac.ops.update_uc_addr(hw, interface->glort, hw->mac.addr, vid, set, 0); if (err) - return err; + goto err_out; /* set vid prior to syncing/unsyncing the VLAN */ interface->vid = vid + (set ? VLAN_N_VID : 0); @@ -644,9 +801,10 @@ static int fm10k_update_vid(struct net_device *netdev, u16 vid, bool set) __dev_uc_unsync(netdev, fm10k_uc_vlan_unsync); __dev_mc_unsync(netdev, fm10k_mc_vlan_unsync); +err_out: fm10k_mbx_unlock(interface); - return 0; + return err; } static int fm10k_vlan_rx_add_vid(struct net_device *netdev, @@ -844,6 +1002,21 @@ void fm10k_restore_rx_state(struct fm10k_intfc *interface) int xcast_mode; u16 vid, glort; + /* restore our address if perm_addr is set */ + if (hw->mac.type == fm10k_mac_vf) { + if (is_valid_ether_addr(hw->mac.perm_addr)) { + ether_addr_copy(hw->mac.addr, hw->mac.perm_addr); + ether_addr_copy(netdev->perm_addr, hw->mac.perm_addr); + ether_addr_copy(netdev->dev_addr, hw->mac.perm_addr); + netdev->addr_assign_type &= ~NET_ADDR_RANDOM; + } + + if (hw->mac.vlan_override) + netdev->features &= ~NETIF_F_HW_VLAN_CTAG_RX; + else + netdev->features |= NETIF_F_HW_VLAN_CTAG_RX; + } + /* record glort for this interface */ glort = interface->glort; @@ -892,6 +1065,9 @@ void fm10k_restore_rx_state(struct fm10k_intfc *interface) /* record updated xcast mode state */ interface->xcast_mode = xcast_mode; + + /* Restore tunnel configuration */ + fm10k_restore_vxlan_port(interface); } void fm10k_reset_rx_state(struct fm10k_intfc *interface) @@ -1013,6 +1189,167 @@ int fm10k_setup_tc(struct net_device *dev, u8 tc) return 0; } +static int fm10k_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) +{ + switch (cmd) { + case SIOCGHWTSTAMP: + return fm10k_get_ts_config(netdev, ifr); + case SIOCSHWTSTAMP: + return fm10k_set_ts_config(netdev, ifr); + default: + return -EOPNOTSUPP; + } +} + +static void fm10k_assign_l2_accel(struct fm10k_intfc *interface, + struct fm10k_l2_accel *l2_accel) +{ + struct fm10k_ring *ring; + int i; + + for (i = 0; i < interface->num_rx_queues; i++) { + ring = interface->rx_ring[i]; + rcu_assign_pointer(ring->l2_accel, l2_accel); + } + + interface->l2_accel = l2_accel; +} + +static void *fm10k_dfwd_add_station(struct net_device *dev, + struct net_device *sdev) +{ + struct fm10k_intfc *interface = netdev_priv(dev); + struct fm10k_l2_accel *l2_accel = interface->l2_accel; + struct fm10k_l2_accel *old_l2_accel = NULL; + struct fm10k_dglort_cfg dglort = { 0 }; + struct fm10k_hw *hw = &interface->hw; + int size = 0, i; + u16 glort; + + /* allocate l2 accel structure if it is not available */ + if (!l2_accel) { + /* verify there is enough free GLORTs to support l2_accel */ + if (interface->glort_count < 7) + return ERR_PTR(-EBUSY); + + size = offsetof(struct fm10k_l2_accel, macvlan[7]); + l2_accel = kzalloc(size, GFP_KERNEL); + if (!l2_accel) + return ERR_PTR(-ENOMEM); + + l2_accel->size = 7; + l2_accel->dglort = interface->glort; + + /* update pointers */ + fm10k_assign_l2_accel(interface, l2_accel); + /* do not expand if we are at our limit */ + } else if ((l2_accel->count == FM10K_MAX_STATIONS) || + (l2_accel->count == (interface->glort_count - 1))) { + return ERR_PTR(-EBUSY); + /* expand if we have hit the size limit */ + } else if (l2_accel->count == l2_accel->size) { + old_l2_accel = l2_accel; + size = offsetof(struct fm10k_l2_accel, + macvlan[(l2_accel->size * 2) + 1]); + l2_accel = kzalloc(size, GFP_KERNEL); + if (!l2_accel) + return ERR_PTR(-ENOMEM); + + memcpy(l2_accel, old_l2_accel, + offsetof(struct fm10k_l2_accel, + macvlan[old_l2_accel->size])); + + l2_accel->size = (old_l2_accel->size * 2) + 1; + + /* update pointers */ + fm10k_assign_l2_accel(interface, l2_accel); + kfree_rcu(old_l2_accel, rcu); + } + + /* add macvlan to accel table, and record GLORT for position */ + for (i = 0; i < l2_accel->size; i++) { + if (!l2_accel->macvlan[i]) + break; + } + + /* record station */ + l2_accel->macvlan[i] = sdev; + l2_accel->count++; + + /* configure default DGLORT mapping for RSS/DCB */ + dglort.idx = fm10k_dglort_pf_rss; + dglort.inner_rss = 1; + dglort.rss_l = fls(interface->ring_feature[RING_F_RSS].mask); + dglort.pc_l = fls(interface->ring_feature[RING_F_QOS].mask); + dglort.glort = interface->glort; + dglort.shared_l = fls(l2_accel->size); + hw->mac.ops.configure_dglort_map(hw, &dglort); + + /* Add rules for this specific dglort to the switch */ + fm10k_mbx_lock(interface); + + glort = l2_accel->dglort + 1 + i; + hw->mac.ops.update_xcast_mode(hw, glort, FM10K_XCAST_MODE_MULTI); + hw->mac.ops.update_uc_addr(hw, glort, sdev->dev_addr, 0, true, 0); + + fm10k_mbx_unlock(interface); + + return sdev; +} + +static void fm10k_dfwd_del_station(struct net_device *dev, void *priv) +{ + struct fm10k_intfc *interface = netdev_priv(dev); + struct fm10k_l2_accel *l2_accel = ACCESS_ONCE(interface->l2_accel); + struct fm10k_dglort_cfg dglort = { 0 }; + struct fm10k_hw *hw = &interface->hw; + struct net_device *sdev = priv; + int i; + u16 glort; + + if (!l2_accel) + return; + + /* search table for matching interface */ + for (i = 0; i < l2_accel->size; i++) { + if (l2_accel->macvlan[i] == sdev) + break; + } + + /* exit if macvlan not found */ + if (i == l2_accel->size) + return; + + /* Remove any rules specific to this dglort */ + fm10k_mbx_lock(interface); + + glort = l2_accel->dglort + 1 + i; + hw->mac.ops.update_xcast_mode(hw, glort, FM10K_XCAST_MODE_NONE); + hw->mac.ops.update_uc_addr(hw, glort, sdev->dev_addr, 0, false, 0); + + fm10k_mbx_unlock(interface); + + /* record removal */ + l2_accel->macvlan[i] = NULL; + l2_accel->count--; + + /* configure default DGLORT mapping for RSS/DCB */ + dglort.idx = fm10k_dglort_pf_rss; + dglort.inner_rss = 1; + dglort.rss_l = fls(interface->ring_feature[RING_F_RSS].mask); + dglort.pc_l = fls(interface->ring_feature[RING_F_QOS].mask); + dglort.glort = interface->glort; + if (l2_accel) + dglort.shared_l = fls(l2_accel->size); + hw->mac.ops.configure_dglort_map(hw, &dglort); + + /* If table is empty remove it */ + if (l2_accel->count == 0) { + fm10k_assign_l2_accel(interface, NULL); + kfree_rcu(l2_accel, rcu); + } +} + static const struct net_device_ops fm10k_netdev_ops = { .ndo_open = fm10k_open, .ndo_stop = fm10k_close, @@ -1026,6 +1363,15 @@ static const struct net_device_ops fm10k_netdev_ops = { .ndo_set_rx_mode = fm10k_set_rx_mode, .ndo_get_stats64 = fm10k_get_stats64, .ndo_setup_tc = fm10k_setup_tc, + .ndo_set_vf_mac = fm10k_ndo_set_vf_mac, + .ndo_set_vf_vlan = fm10k_ndo_set_vf_vlan, + .ndo_set_vf_rate = fm10k_ndo_set_vf_bw, + .ndo_get_vf_config = fm10k_ndo_get_vf_config, + .ndo_add_vxlan_port = fm10k_add_vxlan_port, + .ndo_del_vxlan_port = fm10k_del_vxlan_port, + .ndo_do_ioctl = fm10k_ioctl, + .ndo_dfwd_add_station = fm10k_dfwd_add_station, + .ndo_dfwd_del_station = fm10k_dfwd_del_station, }; #define DEFAULT_DEBUG_LEVEL_SHIFT 3 @@ -1048,16 +1394,33 @@ struct net_device *fm10k_alloc_netdev(void) interface->msg_enable = (1 << DEFAULT_DEBUG_LEVEL_SHIFT) - 1; /* configure default features */ - dev->features |= NETIF_F_SG; + dev->features |= NETIF_F_IP_CSUM | + NETIF_F_IPV6_CSUM | + NETIF_F_SG | + NETIF_F_TSO | + NETIF_F_TSO6 | + NETIF_F_TSO_ECN | + NETIF_F_GSO_UDP_TUNNEL | + NETIF_F_RXHASH | + NETIF_F_RXCSUM; /* all features defined to this point should be changeable */ dev->hw_features |= dev->features; + /* allow user to enable L2 forwarding acceleration */ + dev->hw_features |= NETIF_F_HW_L2FW_DOFFLOAD; + /* configure VLAN features */ dev->vlan_features |= dev->features; /* configure tunnel offloads */ - dev->hw_enc_features = NETIF_F_SG; + dev->hw_enc_features = NETIF_F_IP_CSUM | + NETIF_F_TSO | + NETIF_F_TSO6 | + NETIF_F_TSO_ECN | + NETIF_F_GSO_UDP_TUNNEL | + NETIF_F_IPV6_CSUM | + NETIF_F_SG; /* we want to leave these both on as we cannot disable VLAN tag * insertion or stripping on the hardware since it is contained