return ppi;
 }
 
-/* Azure hosts don't support non-TCP port numbers in hashing yet. We compute
- * hash for non-TCP traffic with only IP numbers.
+/* Azure hosts don't support non-TCP port numbers in hashing for fragmented
+ * packets. We can use ethtool to change UDP hash level when necessary.
  */
-static inline u32 netvsc_get_hash(struct sk_buff *skb)
+static inline u32 netvsc_get_hash(
+       struct sk_buff *skb,
+       const struct net_device_context *ndc)
 {
        struct flow_keys flow;
        u32 hash;
        if (!skb_flow_dissect_flow_keys(skb, &flow, 0))
                return 0;
 
-       if (flow.basic.ip_proto == IPPROTO_TCP) {
+       if (flow.basic.ip_proto == IPPROTO_TCP ||
+           (flow.basic.ip_proto == IPPROTO_UDP &&
+            ((flow.basic.n_proto == htons(ETH_P_IP) && ndc->udp4_l4_hash) ||
+             (flow.basic.n_proto == htons(ETH_P_IPV6) &&
+              ndc->udp6_l4_hash)))) {
                return skb_get_hash(skb);
        } else {
                if (flow.basic.n_proto == htons(ETH_P_IP))
        struct sock *sk = skb->sk;
        int q_idx;
 
-       q_idx = ndc->tx_send_table[netvsc_get_hash(skb) &
+       q_idx = ndc->tx_send_table[netvsc_get_hash(skb, ndc) &
                                   (VRSS_SEND_TAB_SIZE - 1)];
 
        /* If queue index changed record the new value */
 {
        struct net_device_context *ndc = netdev_priv(dev);
 
+       ndc->udp4_l4_hash = true;
+       ndc->udp6_l4_hash = true;
+
        ndc->speed = SPEED_UNKNOWN;
        ndc->duplex = DUPLEX_FULL;
 }
 }
 
 static int
-netvsc_get_rss_hash_opts(struct ethtool_rxnfc *info)
+netvsc_get_rss_hash_opts(struct net_device_context *ndc,
+                        struct ethtool_rxnfc *info)
 {
        info->data = RXH_IP_SRC | RXH_IP_DST;
 
        case TCP_V4_FLOW:
        case TCP_V6_FLOW:
                info->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
-               /* fallthrough */
+               break;
+
        case UDP_V4_FLOW:
+               if (ndc->udp4_l4_hash)
+                       info->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
+
+               break;
+
        case UDP_V6_FLOW:
+               if (ndc->udp6_l4_hash)
+                       info->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
+
+               break;
+
        case IPV4_FLOW:
        case IPV6_FLOW:
                break;
                return 0;
 
        case ETHTOOL_GRXFH:
-               return netvsc_get_rss_hash_opts(info);
+               return netvsc_get_rss_hash_opts(ndc, info);
        }
        return -EOPNOTSUPP;
 }
 
+static int netvsc_set_rss_hash_opts(struct net_device_context *ndc,
+                                   struct ethtool_rxnfc *info)
+{
+       if (info->data == (RXH_IP_SRC | RXH_IP_DST |
+                          RXH_L4_B_0_1 | RXH_L4_B_2_3)) {
+               if (info->flow_type == UDP_V4_FLOW)
+                       ndc->udp4_l4_hash = true;
+               else if (info->flow_type == UDP_V6_FLOW)
+                       ndc->udp6_l4_hash = true;
+               else
+                       return -EOPNOTSUPP;
+
+               return 0;
+       }
+
+       if (info->data == (RXH_IP_SRC | RXH_IP_DST)) {
+               if (info->flow_type == UDP_V4_FLOW)
+                       ndc->udp4_l4_hash = false;
+               else if (info->flow_type == UDP_V6_FLOW)
+                       ndc->udp6_l4_hash = false;
+               else
+                       return -EOPNOTSUPP;
+
+               return 0;
+       }
+
+       return -EOPNOTSUPP;
+}
+
+static int
+netvsc_set_rxnfc(struct net_device *ndev, struct ethtool_rxnfc *info)
+{
+       struct net_device_context *ndc = netdev_priv(ndev);
+
+       if (info->cmd == ETHTOOL_SRXFH)
+               return netvsc_set_rss_hash_opts(ndc, info);
+
+       return -EOPNOTSUPP;
+}
+
 #ifdef CONFIG_NET_POLL_CONTROLLER
 static void netvsc_poll_controller(struct net_device *dev)
 {
        .set_channels   = netvsc_set_channels,
        .get_ts_info    = ethtool_op_get_ts_info,
        .get_rxnfc      = netvsc_get_rxnfc,
+       .set_rxnfc      = netvsc_set_rxnfc,
        .get_rxfh_key_size = netvsc_get_rxfh_key_size,
        .get_rxfh_indir_size = netvsc_rss_indir_size,
        .get_rxfh       = netvsc_get_rxfh,