netvsc: allow get/set of RSS indirection table
[linux-2.6-block.git] / drivers / net / hyperv / netvsc_drv.c
index 05374fce7da4117bbae25871388310f8a9e851ca..a09602e59cf56d9f29028dde8888854073ccf6d5 100644 (file)
 
 #define RING_SIZE_MIN 64
 #define LINKCHANGE_INT (2 * HZ)
-#define NETVSC_HW_FEATURES     (NETIF_F_RXCSUM | \
-                                NETIF_F_SG | \
-                                NETIF_F_TSO | \
-                                NETIF_F_TSO6 | \
-                                NETIF_F_HW_CSUM)
-
-/* Restrict GSO size to account for NVGRE */
-#define NETVSC_GSO_MAX_SIZE    62768
 
 static int ring_size = 128;
 module_param(ring_size, int, S_IRUGO);
 MODULE_PARM_DESC(ring_size, "Ring buffer size (# of pages)");
 
-static int max_num_vrss_chns = 8;
-
 static const u32 default_msg = NETIF_MSG_DRV | NETIF_MSG_PROBE |
                                NETIF_MSG_LINK | NETIF_MSG_IFUP |
                                NETIF_MSG_IFDOWN | NETIF_MSG_RX_ERR |
@@ -323,33 +313,25 @@ static int netvsc_get_slots(struct sk_buff *skb)
        return slots + frag_slots;
 }
 
-static u32 get_net_transport_info(struct sk_buff *skb, u32 *trans_off)
+static u32 net_checksum_info(struct sk_buff *skb)
 {
-       u32 ret_val = TRANSPORT_INFO_NOT_IP;
-
-       if ((eth_hdr(skb)->h_proto != htons(ETH_P_IP)) &&
-               (eth_hdr(skb)->h_proto != htons(ETH_P_IPV6))) {
-               goto not_ip;
-       }
+       if (skb->protocol == htons(ETH_P_IP)) {
+               struct iphdr *ip = ip_hdr(skb);
 
-       *trans_off = skb_transport_offset(skb);
-
-       if ((eth_hdr(skb)->h_proto == htons(ETH_P_IP))) {
-               struct iphdr *iphdr = ip_hdr(skb);
-
-               if (iphdr->protocol == IPPROTO_TCP)
-                       ret_val = TRANSPORT_INFO_IPV4_TCP;
-               else if (iphdr->protocol == IPPROTO_UDP)
-                       ret_val = TRANSPORT_INFO_IPV4_UDP;
+               if (ip->protocol == IPPROTO_TCP)
+                       return TRANSPORT_INFO_IPV4_TCP;
+               else if (ip->protocol == IPPROTO_UDP)
+                       return TRANSPORT_INFO_IPV4_UDP;
        } else {
-               if (ipv6_hdr(skb)->nexthdr == IPPROTO_TCP)
-                       ret_val = TRANSPORT_INFO_IPV6_TCP;
+               struct ipv6hdr *ip6 = ipv6_hdr(skb);
+
+               if (ip6->nexthdr == IPPROTO_TCP)
+                       return TRANSPORT_INFO_IPV6_TCP;
                else if (ipv6_hdr(skb)->nexthdr == IPPROTO_UDP)
-                       ret_val = TRANSPORT_INFO_IPV6_UDP;
+                       return TRANSPORT_INFO_IPV6_UDP;
        }
 
-not_ip:
-       return ret_val;
+       return TRANSPORT_INFO_NOT_IP;
 }
 
 static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
@@ -362,9 +344,6 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
        struct rndis_packet *rndis_pkt;
        u32 rndis_msg_size;
        struct rndis_per_packet_info *ppi;
-       struct ndis_tcp_ip_checksum_info *csum_info;
-       int  hdr_offset;
-       u32 net_trans_info;
        u32 hash;
        u32 skb_length;
        struct hv_page_buffer page_buf[MAX_PAGE_BUFFER_COUNT];
@@ -445,13 +424,7 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
                                VLAN_PRIO_SHIFT;
        }
 
-       net_trans_info = get_net_transport_info(skb, &hdr_offset);
-
-       /*
-        * Setup the sendside checksum offload only if this is not a
-        * GSO packet.
-        */
-       if ((net_trans_info & (INFO_TCP | INFO_UDP)) && skb_is_gso(skb)) {
+       if (skb_is_gso(skb)) {
                struct ndis_tcp_lso_info *lso_info;
 
                rndis_msg_size += NDIS_LSO_PPI_SIZE;
@@ -462,7 +435,7 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
                                                        ppi->ppi_offset);
 
                lso_info->lso_v2_transmit.type = NDIS_TCP_LARGE_SEND_OFFLOAD_V2_TYPE;
-               if (net_trans_info & (INFO_IPV4 << 16)) {
+               if (skb->protocol == htons(ETH_P_IP)) {
                        lso_info->lso_v2_transmit.ip_version =
                                NDIS_TCP_LARGE_SEND_OFFLOAD_IPV4;
                        ip_hdr(skb)->tot_len = 0;
@@ -478,10 +451,12 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
                                ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
                                                 &ipv6_hdr(skb)->daddr, 0, IPPROTO_TCP, 0);
                }
-               lso_info->lso_v2_transmit.tcp_header_offset = hdr_offset;
+               lso_info->lso_v2_transmit.tcp_header_offset = skb_transport_offset(skb);
                lso_info->lso_v2_transmit.mss = skb_shinfo(skb)->gso_size;
        } else if (skb->ip_summed == CHECKSUM_PARTIAL) {
-               if (net_trans_info & INFO_TCP) {
+               if (net_checksum_info(skb) & net_device_ctx->tx_checksum_mask) {
+                       struct ndis_tcp_ip_checksum_info *csum_info;
+
                        rndis_msg_size += NDIS_CSUM_PPI_SIZE;
                        ppi = init_ppi_data(rndis_msg, NDIS_CSUM_PPI_SIZE,
                                            TCPIP_CHKSUM_PKTINFO);
@@ -489,15 +464,25 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
                        csum_info = (struct ndis_tcp_ip_checksum_info *)((void *)ppi +
                                                                         ppi->ppi_offset);
 
-                       if (net_trans_info & (INFO_IPV4 << 16))
+                       csum_info->transmit.tcp_header_offset = skb_transport_offset(skb);
+
+                       if (skb->protocol == htons(ETH_P_IP)) {
                                csum_info->transmit.is_ipv4 = 1;
-                       else
+
+                               if (ip_hdr(skb)->protocol == IPPROTO_TCP)
+                                       csum_info->transmit.tcp_checksum = 1;
+                               else
+                                       csum_info->transmit.udp_checksum = 1;
+                       } else {
                                csum_info->transmit.is_ipv6 = 1;
 
-                       csum_info->transmit.tcp_checksum = 1;
-                       csum_info->transmit.tcp_header_offset = hdr_offset;
+                               if (ipv6_hdr(skb)->nexthdr == IPPROTO_TCP)
+                                       csum_info->transmit.tcp_checksum = 1;
+                               else
+                                       csum_info->transmit.udp_checksum = 1;
+                       }
                } else {
-                       /* UDP checksum (and other) offload is not supported. */
+                       /* Can't do offload of this type of checksum */
                        if (skb_checksum_help(skb))
                                goto drop;
                }
@@ -659,6 +644,7 @@ int netvsc_recv_callback(struct hv_device *device_obj,
         * policy filters on the host). Deliver these via the VF
         * interface in the guest.
         */
+       rcu_read_lock();
        vf_netdev = rcu_dereference(net_device_ctx->vf_netdev);
        if (vf_netdev && (vf_netdev->flags & IFF_UP))
                net = vf_netdev;
@@ -667,6 +653,7 @@ int netvsc_recv_callback(struct hv_device *device_obj,
        skb = netvsc_alloc_recv_skb(net, packet, csum_info, *data, vlan_tci);
        if (unlikely(!skb)) {
                ++net->stats.rx_dropped;
+               rcu_read_unlock();
                return NVSP_STAT_FAIL;
        }
 
@@ -696,6 +683,7 @@ int netvsc_recv_callback(struct hv_device *device_obj,
         * TODO - use NAPI?
         */
        netif_rx(skb);
+       rcu_read_unlock();
 
        return 0;
 }
@@ -719,102 +707,76 @@ static void netvsc_get_channels(struct net_device *net,
        }
 }
 
+static int netvsc_set_queues(struct net_device *net, struct hv_device *dev,
+                            u32 num_chn)
+{
+       struct netvsc_device_info device_info;
+       int ret;
+
+       memset(&device_info, 0, sizeof(device_info));
+       device_info.num_chn = num_chn;
+       device_info.ring_size = ring_size;
+       device_info.max_num_vrss_chns = num_chn;
+
+       ret = rndis_filter_device_add(dev, &device_info);
+       if (ret)
+               return ret;
+
+       ret = netif_set_real_num_tx_queues(net, num_chn);
+       if (ret)
+               return ret;
+
+       ret = netif_set_real_num_rx_queues(net, num_chn);
+
+       return ret;
+}
+
 static int netvsc_set_channels(struct net_device *net,
                               struct ethtool_channels *channels)
 {
        struct net_device_context *net_device_ctx = netdev_priv(net);
        struct hv_device *dev = net_device_ctx->device_ctx;
        struct netvsc_device *nvdev = net_device_ctx->nvdev;
-       struct netvsc_device_info device_info;
-       u32 num_chn;
-       u32 max_chn;
-       int ret = 0;
-       bool recovering = false;
+       unsigned int count = channels->combined_count;
+       int ret;
+
+       /* We do not support separate count for rx, tx, or other */
+       if (count == 0 ||
+           channels->rx_count || channels->tx_count || channels->other_count)
+               return -EINVAL;
+
+       if (count > net->num_tx_queues || count > net->num_rx_queues)
+               return -EINVAL;
 
        if (net_device_ctx->start_remove || !nvdev || nvdev->destroy)
                return -ENODEV;
 
-       num_chn = nvdev->num_chn;
-       max_chn = min_t(u32, nvdev->max_chn, num_online_cpus());
-
-       if (nvdev->nvsp_version < NVSP_PROTOCOL_VERSION_5) {
-               pr_info("vRSS unsupported before NVSP Version 5\n");
+       if (nvdev->nvsp_version < NVSP_PROTOCOL_VERSION_5)
                return -EINVAL;
-       }
 
-       /* We do not support rx, tx, or other */
-       if (!channels ||
-           channels->rx_count ||
-           channels->tx_count ||
-           channels->other_count ||
-           (channels->combined_count < 1))
+       if (count > nvdev->max_chn)
                return -EINVAL;
 
-       if (channels->combined_count > max_chn) {
-               pr_info("combined channels too high, using %d\n", max_chn);
-               channels->combined_count = max_chn;
-       }
-
        ret = netvsc_close(net);
        if (ret)
-               goto out;
+               return ret;
 
- do_set:
        net_device_ctx->start_remove = true;
        rndis_filter_device_remove(dev);
 
-       nvdev->num_chn = channels->combined_count;
-
-       memset(&device_info, 0, sizeof(device_info));
-       device_info.num_chn = nvdev->num_chn; /* passed to RNDIS */
-       device_info.ring_size = ring_size;
-       device_info.max_num_vrss_chns = max_num_vrss_chns;
-
-       ret = rndis_filter_device_add(dev, &device_info);
-       if (ret) {
-               if (recovering) {
-                       netdev_err(net, "unable to add netvsc device (ret %d)\n", ret);
-                       return ret;
-               }
-               goto recover;
-       }
-
-       nvdev = net_device_ctx->nvdev;
-
-       ret = netif_set_real_num_tx_queues(net, nvdev->num_chn);
-       if (ret) {
-               if (recovering) {
-                       netdev_err(net, "could not set tx queue count (ret %d)\n", ret);
-                       return ret;
-               }
-               goto recover;
-       }
-
-       ret = netif_set_real_num_rx_queues(net, nvdev->num_chn);
-       if (ret) {
-               if (recovering) {
-                       netdev_err(net, "could not set rx queue count (ret %d)\n", ret);
-                       return ret;
-               }
-               goto recover;
-       }
+       ret = netvsc_set_queues(net, dev, count);
+       if (ret == 0)
+               nvdev->num_chn = count;
+       else
+               netvsc_set_queues(net, dev, nvdev->num_chn);
 
- out:
        netvsc_open(net);
        net_device_ctx->start_remove = false;
+
        /* We may have missed link change notifications */
        schedule_delayed_work(&net_device_ctx->dwork, 0);
 
        return ret;
-
- recover:
-       /* If the above failed, we attempt to recover through the same
-        * process but with the original number of channels.
-        */
-       netdev_err(net, "could not set channels, recovering\n");
-       recovering = true;
-       channels->combined_count = num_chn;
-       goto do_set;
 }
 
 static bool netvsc_validate_ethtool_ss_cmd(const struct ethtool_cmd *cmd)
@@ -875,8 +837,7 @@ static int netvsc_change_mtu(struct net_device *ndev, int mtu)
        struct netvsc_device *nvdev = ndevctx->nvdev;
        struct hv_device *hdev = ndevctx->device_ctx;
        struct netvsc_device_info device_info;
-       u32 num_chn;
-       int ret = 0;
+       int ret;
 
        if (ndevctx->start_remove || !nvdev || nvdev->destroy)
                return -ENODEV;
@@ -885,8 +846,6 @@ static int netvsc_change_mtu(struct net_device *ndev, int mtu)
        if (ret)
                goto out;
 
-       num_chn = nvdev->num_chn;
-
        ndevctx->start_remove = true;
        rndis_filter_device_remove(hdev);
 
@@ -894,8 +853,8 @@ static int netvsc_change_mtu(struct net_device *ndev, int mtu)
 
        memset(&device_info, 0, sizeof(device_info));
        device_info.ring_size = ring_size;
-       device_info.num_chn = num_chn;
-       device_info.max_num_vrss_chns = max_num_vrss_chns;
+       device_info.num_chn = nvdev->num_chn;
+       device_info.max_num_vrss_chns = nvdev->num_chn;
        rndis_filter_device_add(hdev, &device_info);
 
 out:
@@ -1018,6 +977,48 @@ static void netvsc_get_strings(struct net_device *dev, u32 stringset, u8 *data)
        }
 }
 
+static int
+netvsc_get_rss_hash_opts(struct netvsc_device *nvdev,
+                        struct ethtool_rxnfc *info)
+{
+       info->data = RXH_IP_SRC | RXH_IP_DST;
+
+       switch (info->flow_type) {
+       case TCP_V4_FLOW:
+       case TCP_V6_FLOW:
+               info->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
+               /* fallthrough */
+       case UDP_V4_FLOW:
+       case UDP_V6_FLOW:
+       case IPV4_FLOW:
+       case IPV6_FLOW:
+               break;
+       default:
+               info->data = 0;
+               break;
+       }
+
+       return 0;
+}
+
+static int
+netvsc_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info,
+                u32 *rules)
+{
+       struct net_device_context *ndc = netdev_priv(dev);
+       struct netvsc_device *nvdev = ndc->nvdev;
+
+       switch (info->cmd) {
+       case ETHTOOL_GRXRINGS:
+               info->data = nvdev->num_chn;
+               return 0;
+
+       case ETHTOOL_GRXFH:
+               return netvsc_get_rss_hash_opts(nvdev, info);
+       }
+       return -EOPNOTSUPP;
+}
+
 #ifdef CONFIG_NET_POLL_CONTROLLER
 static void netvsc_poll_controller(struct net_device *net)
 {
@@ -1027,6 +1028,68 @@ static void netvsc_poll_controller(struct net_device *net)
 }
 #endif
 
+static u32 netvsc_get_rxfh_key_size(struct net_device *dev)
+{
+       return NETVSC_HASH_KEYLEN;
+}
+
+static u32 netvsc_rss_indir_size(struct net_device *dev)
+{
+       return ITAB_NUM;
+}
+
+static int netvsc_get_rxfh(struct net_device *dev, u32 *indir, u8 *key,
+                          u8 *hfunc)
+{
+       struct net_device_context *ndc = netdev_priv(dev);
+       struct netvsc_device *ndev = ndc->nvdev;
+       struct rndis_device *rndis_dev = ndev->extension;
+       int i;
+
+       if (hfunc)
+               *hfunc = ETH_RSS_HASH_TOP;      /* Toeplitz */
+
+       if (indir) {
+               for (i = 0; i < ITAB_NUM; i++)
+                       indir[i] = rndis_dev->ind_table[i];
+       }
+
+       if (key)
+               memcpy(key, rndis_dev->rss_key, NETVSC_HASH_KEYLEN);
+
+       return 0;
+}
+
+static int netvsc_set_rxfh(struct net_device *dev, const u32 *indir,
+                          const u8 *key, const u8 hfunc)
+{
+       struct net_device_context *ndc = netdev_priv(dev);
+       struct netvsc_device *ndev = ndc->nvdev;
+       struct rndis_device *rndis_dev = ndev->extension;
+       int i;
+
+       if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP)
+               return -EOPNOTSUPP;
+
+       if (indir) {
+               for (i = 0; i < ITAB_NUM; i++)
+                       if (indir[i] >= dev->num_rx_queues)
+                               return -EINVAL;
+
+               for (i = 0; i < ITAB_NUM; i++)
+                       rndis_dev->ind_table[i] = indir[i];
+       }
+
+       if (!key) {
+               if (!indir)
+                       return 0;
+
+               key = rndis_dev->rss_key;
+       }
+
+       return rndis_filter_set_rss_param(rndis_dev, key, ndev->num_chn);
+}
+
 static const struct ethtool_ops ethtool_ops = {
        .get_drvinfo    = netvsc_get_drvinfo,
        .get_link       = ethtool_op_get_link,
@@ -1038,6 +1101,11 @@ static const struct ethtool_ops ethtool_ops = {
        .get_ts_info    = ethtool_op_get_ts_info,
        .get_settings   = netvsc_get_settings,
        .set_settings   = netvsc_set_settings,
+       .get_rxnfc      = netvsc_get_rxnfc,
+       .get_rxfh_key_size = netvsc_get_rxfh_key_size,
+       .get_rxfh_indir_size = netvsc_rss_indir_size,
+       .get_rxfh       = netvsc_get_rxfh,
+       .set_rxfh       = netvsc_set_rxfh,
 };
 
 static const struct net_device_ops device_ops = {
@@ -1331,7 +1399,7 @@ static int netvsc_probe(struct hv_device *dev,
        int ret;
 
        net = alloc_etherdev_mq(sizeof(struct net_device_context),
-                               num_online_cpus());
+                               VRSS_CHANNEL_MAX);
        if (!net)
                return -ENOMEM;
 
@@ -1369,10 +1437,6 @@ static int netvsc_probe(struct hv_device *dev,
        INIT_LIST_HEAD(&net_device_ctx->reconfig_events);
 
        net->netdev_ops = &device_ops;
-
-       net->hw_features = NETVSC_HW_FEATURES;
-       net->features = NETVSC_HW_FEATURES | NETIF_F_HW_VLAN_CTAG_TX;
-
        net->ethtool_ops = &ethtool_ops;
        SET_NETDEV_DEV(net, &dev->device);
 
@@ -1382,7 +1446,8 @@ static int netvsc_probe(struct hv_device *dev,
        /* Notify the netvsc driver of the new device */
        memset(&device_info, 0, sizeof(device_info));
        device_info.ring_size = ring_size;
-       device_info.max_num_vrss_chns = max_num_vrss_chns;
+       device_info.max_num_vrss_chns = min_t(u32, VRSS_CHANNEL_DEFAULT,
+                                             num_online_cpus());
        ret = rndis_filter_device_add(dev, &device_info);
        if (ret != 0) {
                netdev_err(net, "unable to add netvsc device (ret %d)\n", ret);
@@ -1392,10 +1457,15 @@ static int netvsc_probe(struct hv_device *dev,
        }
        memcpy(net->dev_addr, device_info.mac_adr, ETH_ALEN);
 
+       /* hw_features computed in rndis_filter_device_add */
+       net->features = net->hw_features |
+               NETIF_F_HIGHDMA | NETIF_F_SG |
+               NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX;
+       net->vlan_features = net->features;
+
        nvdev = net_device_ctx->nvdev;
        netif_set_real_num_tx_queues(net, nvdev->num_chn);
        netif_set_real_num_rx_queues(net, nvdev->num_chn);
-       netif_set_gso_max_size(net, NETVSC_GSO_MAX_SIZE);
 
        /* MTU range: 68 - 1500 or 65521 */
        net->min_mtu = NETVSC_MTU_MIN;