netvsc: add ethtool ops to get/set RSS key
authorstephen hemminger <stephen@networkplumber.org>
Tue, 24 Jan 2017 21:06:02 +0000 (13:06 -0800)
committerDavid S. Miller <davem@davemloft.net>
Tue, 24 Jan 2017 21:28:58 +0000 (16:28 -0500)
For some cases it is useful to be able to change RSS key value.
For example, replacing RSS key with a symmetric hash.

Signed-off-by: Stephen Hemminger <sthemmin@microsoft.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/hyperv/hyperv_net.h
drivers/net/hyperv/netvsc_drv.c
drivers/net/hyperv/rndis_filter.c

index ccf94c16084c1c88de6e04eddbdcf8264e41492c..5bf21418bbe53926fd3c98be15106150fcb53f97 100644 (file)
@@ -156,6 +156,8 @@ enum rndis_device_state {
        RNDIS_DEV_DATAINITIALIZED,
 };
 
+#define NETVSC_HASH_KEYLEN 40
+
 struct rndis_device {
        struct net_device *ndev;
 
@@ -166,7 +168,8 @@ struct rndis_device {
        spinlock_t request_lock;
        struct list_head req_list;
 
-       unsigned char hw_mac_adr[ETH_ALEN];
+       u8 hw_mac_adr[ETH_ALEN];
+       u8 rss_key[NETVSC_HASH_KEYLEN];
 };
 
 
@@ -194,6 +197,8 @@ int rndis_filter_close(struct netvsc_device *nvdev);
 int rndis_filter_device_add(struct hv_device *dev,
                        void *additional_info);
 void rndis_filter_device_remove(struct hv_device *dev);
+int rndis_filter_set_rss_param(struct rndis_device *rdev,
+                              const u8 *key, int num_queue);
 int rndis_filter_receive(struct hv_device *dev,
                        struct hv_netvsc_packet *pkt,
                        void **data,
index 6ab8b52af965127424cfa01a51ff600a1b2ae448..40a88387a8f5655544a139622e2c7e57344ddcdb 100644 (file)
@@ -1059,6 +1059,48 @@ 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 0;
+}
+
+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;
+
+       if (hfunc)
+               *hfunc = ETH_RSS_HASH_TOP;      /* Toeplitz */
+
+       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;
+
+       if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP)
+               return -EOPNOTSUPP;
+
+       if (!key || memcmp(key, rndis_dev->rss_key, NETVSC_HASH_KEYLEN) == 0)
+               return 0; /* no change */
+
+       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,
@@ -1071,6 +1113,10 @@ static const struct ethtool_ops ethtool_ops = {
        .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 = {
index 8f04be1d74212b46e508c37bce36fb063ff3ced5..ac08aa1b089a301ff33e94aacd1a28e6dcf1bee0 100644 (file)
@@ -57,6 +57,14 @@ struct rndis_request {
        u8 request_ext[RNDIS_EXT_LEN];
 };
 
+static const u8 netvsc_hash_key[NETVSC_HASH_KEYLEN] = {
+       0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2,
+       0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0,
+       0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4,
+       0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c,
+       0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa
+};
+
 static struct rndis_device *get_rndis_device(void)
 {
        struct rndis_device *device;
@@ -729,23 +737,15 @@ cleanup:
        return ret;
 }
 
-static const u8 netvsc_hash_key[] = {
-       0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2,
-       0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0,
-       0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4,
-       0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c,
-       0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa
-};
-#define HASH_KEYLEN ARRAY_SIZE(netvsc_hash_key)
-
-static int rndis_filter_set_rss_param(struct rndis_device *rdev, int num_queue)
+int rndis_filter_set_rss_param(struct rndis_device *rdev,
+                              const u8 *rss_key, int num_queue)
 {
        struct net_device *ndev = rdev->ndev;
        struct rndis_request *request;
        struct rndis_set_request *set;
        struct rndis_set_complete *set_complete;
        u32 extlen = sizeof(struct ndis_recv_scale_param) +
-                    4*ITAB_NUM + HASH_KEYLEN;
+                    4 * ITAB_NUM + NETVSC_HASH_KEYLEN;
        struct ndis_recv_scale_param *rssp;
        u32 *itab;
        u8 *keyp;
@@ -773,7 +773,7 @@ static int rndis_filter_set_rss_param(struct rndis_device *rdev, int num_queue)
                         NDIS_HASH_TCP_IPV6;
        rssp->indirect_tabsize = 4*ITAB_NUM;
        rssp->indirect_taboffset = sizeof(struct ndis_recv_scale_param);
-       rssp->hashkey_size = HASH_KEYLEN;
+       rssp->hashkey_size = NETVSC_HASH_KEYLEN;
        rssp->kashkey_offset = rssp->indirect_taboffset +
                               rssp->indirect_tabsize;
 
@@ -784,8 +784,7 @@ static int rndis_filter_set_rss_param(struct rndis_device *rdev, int num_queue)
 
        /* Set hask key values */
        keyp = (u8 *)((unsigned long)rssp + rssp->kashkey_offset);
-       for (i = 0; i < HASH_KEYLEN; i++)
-               keyp[i] = netvsc_hash_key[i];
+       memcpy(keyp, rss_key, NETVSC_HASH_KEYLEN);
 
        ret = rndis_filter_send_request(rdev, request);
        if (ret != 0)
@@ -793,7 +792,9 @@ static int rndis_filter_set_rss_param(struct rndis_device *rdev, int num_queue)
 
        wait_for_completion(&request->wait_event);
        set_complete = &request->response_msg.msg.set_complete;
-       if (set_complete->status != RNDIS_STATUS_SUCCESS) {
+       if (set_complete->status == RNDIS_STATUS_SUCCESS)
+               memcpy(rdev->rss_key, rss_key, NETVSC_HASH_KEYLEN);
+       else {
                netdev_err(ndev, "Fail to set RSS parameters:0x%x\n",
                           set_complete->status);
                ret = -EINVAL;
@@ -1235,7 +1236,8 @@ int rndis_filter_device_add(struct hv_device *dev,
        net_device->num_chn = 1 +
                init_packet->msg.v5_msg.subchn_comp.num_subchannels;
 
-       ret = rndis_filter_set_rss_param(rndis_device, net_device->num_chn);
+       ret = rndis_filter_set_rss_param(rndis_device, netvsc_hash_key,
+                                        net_device->num_chn);
 
        /*
         * Set the number of sub-channels to be received.