flow: add RSS support

This patch enables the RSS configuration through vnet/flow interface
With this RSS feature, users can config the RSS functions for specific flows

Currently, it supports:
  default, toeplitz and symmetric_toeplitz rss function, and
  ipv4-tcp/ipv4-udp/ipv6-tcp/ipv6-ucp flow types

Users can use the following options to combine with above flow
types for more specific hash input set selection:
  l3-src-only, l3-dst-only, l4-src-only, l4-dst-only

Command line:
test flow add dst-ip any proto udp rss function default rss types ipv4-tcp use l3-dst-only
test flow add dst-ip any proto udp rss function toeplitz rss types ipv4-udp use l4-src-only
test flow add dst-ip any proto udp rss function symmetric_toeplitz rss types ipv6-udp use l3-src-only and l3-dst-only

Type: feature

Signed-off-by: Chenmin Sun <chenmin.sun@intel.com>
Change-Id: I213efc76dc8af37f2f63605884f353e05b0f5d2a
diff --git a/src/plugins/dpdk/device/dpdk.h b/src/plugins/dpdk/device/dpdk.h
index a044181..46d36a2 100644
--- a/src/plugins/dpdk/device/dpdk.h
+++ b/src/plugins/dpdk/device/dpdk.h
@@ -410,6 +410,33 @@
 
 void dpdk_update_link_state (dpdk_device_t * xd, f64 now);
 
+#define foreach_dpdk_rss_hf                    \
+  _(0, ETH_RSS_FRAG_IPV4,           "ipv4-frag")    \
+  _(1, ETH_RSS_NONFRAG_IPV4_TCP,    "ipv4-tcp")     \
+  _(2, ETH_RSS_NONFRAG_IPV4_UDP,    "ipv4-udp")     \
+  _(3, ETH_RSS_NONFRAG_IPV4_SCTP,   "ipv4-sctp")    \
+  _(4, ETH_RSS_NONFRAG_IPV4_OTHER,  "ipv4-other")   \
+  _(5, ETH_RSS_IPV4,                "ipv4")         \
+  _(6, ETH_RSS_IPV6_TCP_EX,         "ipv6-tcp-ex")  \
+  _(7, ETH_RSS_IPV6_UDP_EX,         "ipv6-udp-ex")  \
+  _(8, ETH_RSS_FRAG_IPV6,           "ipv6-frag")    \
+  _(9, ETH_RSS_NONFRAG_IPV6_TCP,    "ipv6-tcp")     \
+  _(10, ETH_RSS_NONFRAG_IPV6_UDP,   "ipv6-udp")     \
+  _(11, ETH_RSS_NONFRAG_IPV6_SCTP,  "ipv6-sctp")    \
+  _(12, ETH_RSS_NONFRAG_IPV6_OTHER, "ipv6-other")   \
+  _(13, ETH_RSS_IPV6_EX,            "ipv6-ex")      \
+  _(14, ETH_RSS_IPV6,               "ipv6")         \
+  _(15, ETH_RSS_L2_PAYLOAD,         "l2-payload")   \
+  _(16, ETH_RSS_PORT,               "port")         \
+  _(17, ETH_RSS_VXLAN,              "vxlan")        \
+  _(18, ETH_RSS_GENEVE,             "geneve")       \
+  _(19, ETH_RSS_NVGRE,              "nvgre")        \
+  _(20, ETH_RSS_GTPU,               "gtpu")         \
+  _(60, ETH_RSS_L4_DST_ONLY,        "l4-dst-only")  \
+  _(61, ETH_RSS_L4_SRC_ONLY,        "l4-src-only")  \
+  _(62, ETH_RSS_L3_DST_ONLY,        "l3-dst-only")  \
+  _(63, ETH_RSS_L3_SRC_ONLY,        "l3-src-only")
+
 format_function_t format_dpdk_device_name;
 format_function_t format_dpdk_device;
 format_function_t format_dpdk_device_errors;
diff --git a/src/plugins/dpdk/device/flow.c b/src/plugins/dpdk/device/flow.c
index 279468f..444a76e 100644
--- a/src/plugins/dpdk/device/flow.c
+++ b/src/plugins/dpdk/device/flow.c
@@ -61,6 +61,24 @@
   return true;
 }
 
+static inline void
+dpdk_flow_convert_rss_types (u64 type, u64 * dpdk_rss_type)
+{
+#define BIT_IS_SET(v, b) \
+  ((v) & (u64)1<<(b))
+
+  *dpdk_rss_type = 0;
+
+#undef _
+#define _(n, f, s) \
+      if (n != -1 && BIT_IS_SET(type, n)) \
+        *dpdk_rss_type |= f;
+
+  foreach_dpdk_rss_hf
+#undef _
+    return;
+}
+
 static int
 dpdk_flow_add (dpdk_device_t * xd, vnet_flow_t * f, dpdk_flow_entry_t * fe)
 {
@@ -74,6 +92,7 @@
   struct rte_flow_item_gtp gtp[2] = { };
   struct rte_flow_action_mark mark = { 0 };
   struct rte_flow_action_queue queue = { 0 };
+  struct rte_flow_action_rss rss = { 0 };
   struct rte_flow_item *item, *items = 0;
   struct rte_flow_action *action, *actions = 0;
   bool fate = false;
@@ -265,13 +284,15 @@
 	  item->spec = NULL;
 	  item->mask = NULL;
 	}
-
-      tcp[0].hdr.src_port = clib_host_to_net_u16 (src_port);
-      tcp[1].hdr.src_port = clib_host_to_net_u16 (src_port_mask);
-      tcp[0].hdr.dst_port = clib_host_to_net_u16 (dst_port);
-      tcp[1].hdr.dst_port = clib_host_to_net_u16 (dst_port_mask);
-      item->spec = tcp;
-      item->mask = tcp + 1;
+      else
+	{
+	  tcp[0].hdr.src_port = clib_host_to_net_u16 (src_port);
+	  tcp[1].hdr.src_port = clib_host_to_net_u16 (src_port_mask);
+	  tcp[0].hdr.dst_port = clib_host_to_net_u16 (dst_port);
+	  tcp[1].hdr.dst_port = clib_host_to_net_u16 (dst_port_mask);
+	  item->spec = tcp;
+	  item->mask = tcp + 1;
+	}
     }
   else
     {
@@ -512,6 +533,27 @@
       else
 	fate = true;
     }
+  if (f->actions & VNET_FLOW_ACTION_RSS)
+    {
+      u64 rss_type = 0;
+      vec_add2 (actions, action, 1);
+      action->type = RTE_FLOW_ACTION_TYPE_RSS;
+      action->conf = &rss;
+
+      /* convert types to DPDK rss bitmask */
+      dpdk_flow_convert_rss_types (f->rss_types, &rss_type);
+
+      rss.types = rss_type;
+      rss.func = f->rss_fun;
+
+      if (fate == true)
+	{
+	  rv = VNET_FLOW_ERROR_INTERNAL;
+	  goto done;
+	}
+      else
+	fate = true;
+    }
   if (fate == false)
     {
       vec_add2 (actions, action, 1);
diff --git a/src/plugins/dpdk/device/format.c b/src/plugins/dpdk/device/format.c
index 3d6e80f..dad1505 100644
--- a/src/plugins/dpdk/device/format.c
+++ b/src/plugins/dpdk/device/format.c
@@ -44,28 +44,6 @@
   _ (tx_bytes_ok, q_obytes)                     \
   _ (rx_errors, q_errors)
 
-#define foreach_dpdk_rss_hf                    \
-  _(ETH_RSS_FRAG_IPV4,          "ipv4-frag")   \
-  _(ETH_RSS_NONFRAG_IPV4_TCP,   "ipv4-tcp")    \
-  _(ETH_RSS_NONFRAG_IPV4_UDP,   "ipv4-udp")    \
-  _(ETH_RSS_NONFRAG_IPV4_SCTP,  "ipv4-sctp")   \
-  _(ETH_RSS_NONFRAG_IPV4_OTHER, "ipv4-other")  \
-  _(ETH_RSS_IPV4,               "ipv4")        \
-  _(ETH_RSS_IPV6_TCP_EX,        "ipv6-tcp-ex") \
-  _(ETH_RSS_IPV6_UDP_EX,        "ipv6-udp-ex") \
-  _(ETH_RSS_FRAG_IPV6,          "ipv6-frag")   \
-  _(ETH_RSS_NONFRAG_IPV6_TCP,   "ipv6-tcp")    \
-  _(ETH_RSS_NONFRAG_IPV6_UDP,   "ipv6-udp")    \
-  _(ETH_RSS_NONFRAG_IPV6_SCTP,  "ipv6-sctp")   \
-  _(ETH_RSS_NONFRAG_IPV6_OTHER, "ipv6-other")  \
-  _(ETH_RSS_IPV6_EX,            "ipv6-ex")     \
-  _(ETH_RSS_IPV6,               "ipv6")        \
-  _(ETH_RSS_L2_PAYLOAD,         "l2-payload")  \
-  _(ETH_RSS_PORT,               "port")        \
-  _(ETH_RSS_VXLAN,              "vxlan")       \
-  _(ETH_RSS_GENEVE,             "geneve")      \
-  _(ETH_RSS_NVGRE,              "nvgre")
-
 #define foreach_dpdk_pkt_rx_offload_flag                                \
   _ (PKT_RX_VLAN, "RX packet is a 802.1q VLAN packet")                  \
   _ (PKT_RX_RSS_HASH, "RX packet with RSS hash result")                 \
@@ -402,7 +380,7 @@
   return s;
 }
 
-#define _(v, str)                                            \
+#define _(n, v, str)                                            \
 if (bitmap & v) {                                            \
   if (format_get_indent (s) > 72)                            \
     s = format(s,"\n%U", format_white_space, indent);        \
@@ -947,7 +925,7 @@
       if (0)
 	;
 #undef _
-#define _(f, s)                                 \
+#define _(n, f, s)                                 \
       else if (unformat (input, s))             \
         *rss_fn |= f;
 
diff --git a/src/plugins/dpdk/device/init.c b/src/plugins/dpdk/device/init.c
index 2922ed3..a9e7393 100644
--- a/src/plugins/dpdk/device/init.c
+++ b/src/plugins/dpdk/device/init.c
@@ -440,7 +440,8 @@
 		VNET_FLOW_ACTION_REDIRECT_TO_NODE |
 		VNET_FLOW_ACTION_REDIRECT_TO_QUEUE |
 		VNET_FLOW_ACTION_BUFFER_ADVANCE |
-		VNET_FLOW_ACTION_COUNT | VNET_FLOW_ACTION_DROP;
+		VNET_FLOW_ACTION_COUNT | VNET_FLOW_ACTION_DROP |
+		VNET_FLOW_ACTION_RSS;
 
 	      if (dm->conf->no_tx_checksum_offload == 0)
 		{
diff --git a/src/vnet/flow/flow.h b/src/vnet/flow/flow.h
index bd62135..d6850b2 100644
--- a/src/vnet/flow/flow.h
+++ b/src/vnet/flow/flow.h
@@ -129,7 +129,8 @@
   _(2, BUFFER_ADVANCE, "buffer-advance") \
   _(3, REDIRECT_TO_NODE, "redirect-to-node") \
   _(4, REDIRECT_TO_QUEUE, "redirect-to-queue") \
-  _(5, DROP, "drop")
+  _(5, RSS, "rss") \
+  _(6, DROP, "drop")
 
 typedef enum
 {
@@ -147,6 +148,39 @@
   _( -5, NO_SUCH_INTERFACE, "no such interface")		\
   _( -6, INTERNAL, "internal error")
 
+#define foreach_flow_rss_types                    \
+  _(0, FRAG_IPV4,          "ipv4-frag")   \
+  _(1, IPV4_TCP,           "ipv4-tcp")    \
+  _(2, IPV4_UDP,           "ipv4-udp")    \
+  _(3, IPV4_SCTP,          "ipv4-sctp")   \
+  _(4, IPV4_OTHER,         "ipv4-other")  \
+  _(5, IPV4,               "ipv4")        \
+  _(6, IPV6_TCP_EX,        "ipv6-tcp-ex") \
+  _(7, IPV6_UDP_EX,        "ipv6-udp-ex") \
+  _(8, FRAG_IPV6,          "ipv6-frag")   \
+  _(9, IPV6_TCP,           "ipv6-tcp")    \
+  _(10, IPV6_UDP,          "ipv6-udp")    \
+  _(11, IPV6_SCTP,         "ipv6-sctp")   \
+  _(12, IPV6_OTHER,        "ipv6-other")  \
+  _(13, IPV6_EX,           "ipv6-ex")     \
+  _(14, IPV6,              "ipv6")        \
+  _(15, L2_PAYLOAD,        "l2-payload")  \
+  _(16, PORT,              "port")        \
+  _(17, VXLAN,             "vxlan")       \
+  _(18, GENEVE,            "geneve")      \
+  _(19, NVGRE,             "nvgre")       \
+  _(20, GTPU,              "gtpu")        \
+  _(60, L4_DST_ONLY,       "l4-dst-only") \
+  _(61, L4_SRC_ONLY,       "l4-src-only") \
+  _(62, L3_DST_ONLY,       "l3-dst-only") \
+  _(63, L3_SRC_ONLY,       "l3-src-only")
+
+#define foreach_rss_function           \
+  _(DEFAULT, "default")                \
+  _(TOEPLITZ, "toeplitz")              \
+  _(SIMPLE_XOR, "simple_xor")          \
+  _(SYMMETRIC_TOEPLITZ, "symmetric_toeplitz")
+
 typedef enum
 {
   VNET_FLOW_NO_ERROR = 0,
@@ -169,6 +203,13 @@
     VNET_FLOW_N_TYPES,
 } vnet_flow_type_t;
 
+typedef enum
+{
+#define _(a,b) VNET_RSS_FUNC_##a,
+  foreach_rss_function
+#undef _
+    VNET_RSS_N_TYPES,
+} vnet_rss_function_t;
 
 /*
  * Create typedef struct vnet_flow_XXX_t
@@ -208,6 +249,12 @@
   /* buffer offset for VNET_FLOW_ACTION_BUFFER_ADVANCE */
   i32 buffer_advance;
 
+  /* RSS types, including IPv4/IPv6/TCP/UDP... */
+  u64 rss_types;
+
+  /* RSS functions, including IPv4/IPv6/TCP/UDP... */
+  vnet_rss_function_t rss_fun;
+
   union
   {
 #define _(a,b,c) vnet_flow_##b##_t b;
diff --git a/src/vnet/flow/flow_cli.c b/src/vnet/flow/flow_cli.c
index 500f16b..772f89a 100644
--- a/src/vnet/flow/flow_cli.c
+++ b/src/vnet/flow/flow_cli.c
@@ -296,6 +296,8 @@
   ip_port_and_mask_t dport = { };
   u16 eth_type;
   bool ethernet_set = false;
+  u8 *rss_type[3] = { };
+  u8 *type_str = NULL;
 
   clib_memset (&flow, 0, sizeof (vnet_flow_t));
   flow.index = ~0;
@@ -370,6 +372,65 @@
 	flow.actions |= VNET_FLOW_ACTION_REDIRECT_TO_QUEUE;
       else if (unformat (line_input, "drop"))
 	flow.actions |= VNET_FLOW_ACTION_DROP;
+      else if (unformat (line_input, "rss function"))
+	{
+	  if (0)
+	    ;
+#undef _
+#define _(f, s)                                 \
+        else if (unformat (line_input, s)) \
+          flow.rss_fun = VNET_RSS_FUNC_##f;
+
+	  foreach_rss_function
+#undef _
+	    else
+	    {
+	      return clib_error_return (0, "unknown input `%U'",
+					format_unformat_error, line_input);
+	    }
+
+	  flow.actions |= VNET_FLOW_ACTION_RSS;
+	}
+      else if (unformat (line_input, "rss types"))
+	{
+	  rss_type[0] = NULL;
+	  rss_type[1] = NULL;
+	  rss_type[2] = NULL;
+	  type_str = NULL;
+
+	  if (unformat (line_input, "%s use %s and %s",
+			&rss_type[0], &rss_type[1], &rss_type[2]))
+	    ;
+	  else
+	    if (unformat
+		(line_input, "%s use %s", &rss_type[0], &rss_type[1]))
+	    ;
+	  else if (unformat (line_input, "%s", &rss_type[0]))
+	    ;
+
+#undef _
+#define _(a,b,c)     \
+        else if (!clib_strcmp(c, (const char *)type_str)) \
+          flow.rss_types |= (1ULL<<a);
+
+#define check_rss_types(_str)     \
+        if (_str != NULL) {\
+          type_str = _str;\
+          if (0) \
+	          ; \
+          foreach_flow_rss_types \
+          else \
+          { \
+            return clib_error_return (0, "parse error: '%U'", \
+              format_unformat_error, line_input); \
+          } \
+        }
+
+	  check_rss_types (rss_type[0])
+	    check_rss_types (rss_type[1]) check_rss_types (rss_type[2])
+#undef _
+	    flow.actions |= VNET_FLOW_ACTION_RSS;
+	}
       else if (unformat (line_input, "%U", unformat_vnet_hw_interface, vnm,
 			 &hw_if_index))
 	;
@@ -545,7 +606,7 @@
 
 	  if (flow.ip6_n_tuple.protocol == (ip_protocol_t) ~ 0)
 	    return clib_error_return (0, "Please specify ip protocol");
-	  if ((type != VNET_FLOW_TYPE_IP4_N_TUPLE) &&
+	  if ((type != VNET_FLOW_TYPE_IP6_N_TUPLE) &&
 	      (flow.ip6_n_tuple.protocol != IP_PROTOCOL_UDP))
 	    return clib_error_return (0,
 				      "For GTP related flow, ip protocol must be UDP");