[v5] ethdev: add flow API to expand RSS flows

Message ID 2b29e115e43f519e2cc2fa1a075849f53c7bf92a.1530201647.git.nelio.laranjeiro@6wind.com (mailing list archive)
State Accepted, archived
Delegated to: Ferruh Yigit
Headers
Series [v5] ethdev: add flow API to expand RSS flows |

Checks

Context Check Description
ci/checkpatch warning coding style issues
ci/Intel-compilation success Compilation OK

Commit Message

Nélio Laranjeiro June 28, 2018, 4:01 p.m. UTC
  Introduce an helper for PMD to expand easily flows items list with RSS
action into multiple flow items lists with priority information.

For instance a user items list being "eth / end" with rss action types
"ipv4-udp ipv6-udp end" needs to be expanded into three items lists:

 - eth
 - eth / ipv4 / udp
 - eth / ipv6 / udp

to match the user request.  Some drivers are unable to reach such
request without this expansion, this API is there to help those.
Only PMD should use such API for their internal cooking, the application
will still handle a single flow.

Signed-off-by: Nelio Laranjeiro <nelio.laranjeiro@6wind.com>

---

Changes in v5:

- Replace the struct rte_flow_expand_rss field by a flexible array,
- Address all nits.

Changes in v4:

- Replace the expanded algorithm with a graph to support also tunnel
pattern matching.

Changes in v3:

- Fix a segmentation fault due to an uninitialized pointer.

Changes in v2:

- Fix expansion for UDP/TCP layers where L3 may not be in the original
  items list and thus is missing in the expansion.
- Fix size verification for some layers causing a segfault
---
 lib/librte_ethdev/rte_flow.c        | 107 ++++++++++++++++++++++++++++
 lib/librte_ethdev/rte_flow_driver.h |  58 +++++++++++++++
 2 files changed, 165 insertions(+)
  

Comments

Adrien Mazarguil June 28, 2018, 4:09 p.m. UTC | #1
On Thu, Jun 28, 2018 at 06:01:21PM +0200, Nelio Laranjeiro wrote:
> Introduce an helper for PMD to expand easily flows items list with RSS
> action into multiple flow items lists with priority information.
> 
> For instance a user items list being "eth / end" with rss action types
> "ipv4-udp ipv6-udp end" needs to be expanded into three items lists:
> 
>  - eth
>  - eth / ipv4 / udp
>  - eth / ipv6 / udp
> 
> to match the user request.  Some drivers are unable to reach such
> request without this expansion, this API is there to help those.
> Only PMD should use such API for their internal cooking, the application
> will still handle a single flow.
> 
> Signed-off-by: Nelio Laranjeiro <nelio.laranjeiro@6wind.com>
> 
> ---
> 
> Changes in v5:
> 
> - Replace the struct rte_flow_expand_rss field by a flexible array,
> - Address all nits.

Acked-by: Adrien Mazarguil <adrien.mazarguil@6wind.com>

With just a couple of tiny nits, not sure they're worth a v6. See below.

<snip>
> +/** Node object of input graph for rte_flow_expand_rss(). */
> +struct rte_flow_expand_node {
> +	const int *const next;
> +	/**< List of next node indexes. Index 0 is interpreted as a
> +	 * terminator. */
> +	const enum rte_flow_item_type type;
> +	/**< Pattern item type of current node. */
> +	uint64_t rss_types;
> +	/**< RSS types bit-field associated with this node (see ETH_RSS_*
> +	 * definitions).*/
> +};

Multi-line comments should have opening and closing tokens on dedicated
lines.

<snip>
> + * @param[in] graph.

Extra "."
  
Ferruh Yigit June 29, 2018, 4:23 p.m. UTC | #2
On 6/28/2018 5:09 PM, Adrien Mazarguil wrote:
> On Thu, Jun 28, 2018 at 06:01:21PM +0200, Nelio Laranjeiro wrote:
>> Introduce an helper for PMD to expand easily flows items list with RSS
>> action into multiple flow items lists with priority information.
>>
>> For instance a user items list being "eth / end" with rss action types
>> "ipv4-udp ipv6-udp end" needs to be expanded into three items lists:
>>
>>  - eth
>>  - eth / ipv4 / udp
>>  - eth / ipv6 / udp
>>
>> to match the user request.  Some drivers are unable to reach such
>> request without this expansion, this API is there to help those.
>> Only PMD should use such API for their internal cooking, the application
>> will still handle a single flow.
>>
>> Signed-off-by: Nelio Laranjeiro <nelio.laranjeiro@6wind.com>
>>
>> ---
>>
>> Changes in v5:
>>
>> - Replace the struct rte_flow_expand_rss field by a flexible array,
>> - Address all nits.
> 
> Acked-by: Adrien Mazarguil <adrien.mazarguil@6wind.com>
> 
> With just a couple of tiny nits, not sure they're worth a v6. See below.
> 
> <snip>
>> +/** Node object of input graph for rte_flow_expand_rss(). */
>> +struct rte_flow_expand_node {
>> +	const int *const next;
>> +	/**< List of next node indexes. Index 0 is interpreted as a
>> +	 * terminator. */
>> +	const enum rte_flow_item_type type;
>> +	/**< Pattern item type of current node. */
>> +	uint64_t rss_types;
>> +	/**< RSS types bit-field associated with this node (see ETH_RSS_*
>> +	 * definitions).*/
>> +};
> 
> Multi-line comments should have opening and closing tokens on dedicated
> lines.
> 
> <snip>
>> + * @param[in] graph.
> 
> Extra "."

Extra "." is causing doc warning, will fix it while merging. Also will fix above
multi-line doxygen comment syntax while merging.

>
  
Ferruh Yigit June 29, 2018, 4:26 p.m. UTC | #3
On 6/28/2018 5:09 PM, Adrien Mazarguil wrote:
> On Thu, Jun 28, 2018 at 06:01:21PM +0200, Nelio Laranjeiro wrote:
>> Introduce an helper for PMD to expand easily flows items list with RSS
>> action into multiple flow items lists with priority information.
>>
>> For instance a user items list being "eth / end" with rss action types
>> "ipv4-udp ipv6-udp end" needs to be expanded into three items lists:
>>
>>  - eth
>>  - eth / ipv4 / udp
>>  - eth / ipv6 / udp
>>
>> to match the user request.  Some drivers are unable to reach such
>> request without this expansion, this API is there to help those.
>> Only PMD should use such API for their internal cooking, the application
>> will still handle a single flow.
>>
>> Signed-off-by: Nelio Laranjeiro <nelio.laranjeiro@6wind.com>
>>
>> ---
>>
>> Changes in v5:
>>
>> - Replace the struct rte_flow_expand_rss field by a flexible array,
>> - Address all nits.
> 
> Acked-by: Adrien Mazarguil <adrien.mazarguil@6wind.com>

Applied to dpdk-next-net/master, thanks.
  

Patch

diff --git a/lib/librte_ethdev/rte_flow.c b/lib/librte_ethdev/rte_flow.c
index 2e87e59f3..2b13efca7 100644
--- a/lib/librte_ethdev/rte_flow.c
+++ b/lib/librte_ethdev/rte_flow.c
@@ -526,3 +526,110 @@  rte_flow_copy(struct rte_flow_desc *desc, size_t len,
 	}
 	return 0;
 }
+
+/**
+ * Expand RSS flows into several possible flows according to the RSS hash
+ * fields requested and the driver capabilities.
+ */
+int
+rte_flow_expand_rss(struct rte_flow_expand_rss *buf, size_t size,
+		    const struct rte_flow_item *pattern, uint64_t types,
+		    const struct rte_flow_expand_node graph[],
+		    int graph_root_index)
+{
+	const int elt_n = 8;
+	const struct rte_flow_item *item;
+	const struct rte_flow_expand_node *node = &graph[graph_root_index];
+	const int *next_node;
+	const int *stack[elt_n];
+	int stack_pos = 0;
+	struct rte_flow_item flow_items[elt_n];
+	unsigned int i;
+	size_t lsize;
+	size_t user_pattern_size = 0;
+	void *addr = NULL;
+
+	lsize = offsetof(struct rte_flow_expand_rss, entry) +
+		elt_n * sizeof(buf->entry[0]);
+	if (lsize <= size) {
+		buf->entry[0].priority = 0;
+		buf->entry[0].pattern = (void *)&buf->entry[elt_n];
+		buf->entries = 0;
+		addr = buf->entry[0].pattern;
+	}
+	for (item = pattern; item->type != RTE_FLOW_ITEM_TYPE_END; item++) {
+		const struct rte_flow_expand_node *next = NULL;
+
+		for (i = 0; node->next && node->next[i]; ++i) {
+			next = &graph[node->next[i]];
+			if (next->type == item->type)
+				break;
+		}
+		if (next)
+			node = next;
+		user_pattern_size += sizeof(*item);
+	}
+	user_pattern_size += sizeof(*item); /* Handle END item. */
+	lsize += user_pattern_size;
+	/* Copy the user pattern in the first entry of the buffer. */
+	if (lsize <= size) {
+		rte_memcpy(addr, pattern, user_pattern_size);
+		addr = (void *)(((uintptr_t)addr) + user_pattern_size);
+		buf->entries = 1;
+	}
+	/* Start expanding. */
+	memset(flow_items, 0, sizeof(flow_items));
+	user_pattern_size -= sizeof(*item);
+	next_node = node->next;
+	stack[stack_pos] = next_node;
+	node = next_node ? &graph[*next_node] : NULL;
+	while (node) {
+		flow_items[stack_pos].type = node->type;
+		if ((node->rss_types & types) == node->rss_types) {
+			/*
+			 * compute the number of items to copy from the
+			 * expansion and copy it.
+			 * When the stack_pos is 0, there are 1 element in it,
+			 * plus the addition END item.
+			 */
+			int elt = stack_pos + 2;
+
+			flow_items[stack_pos + 1].type = RTE_FLOW_ITEM_TYPE_END;
+			lsize += elt * sizeof(*item) + user_pattern_size;
+			if (lsize <= size) {
+				size_t n = elt * sizeof(*item);
+
+				buf->entry[buf->entries].priority =
+					stack_pos + 1;
+				buf->entry[buf->entries].pattern = addr;
+				buf->entries++;
+				rte_memcpy(addr, buf->entry[0].pattern,
+					   user_pattern_size);
+				addr = (void *)(((uintptr_t)addr) +
+						user_pattern_size);
+				rte_memcpy(addr, flow_items, n);
+				addr = (void *)(((uintptr_t)addr) + n);
+			}
+		}
+		/* Go deeper. */
+		if (node->next) {
+			next_node = node->next;
+			if (stack_pos++ == elt_n) {
+				rte_errno = E2BIG;
+				return -rte_errno;
+			}
+			stack[stack_pos] = next_node;
+		} else if (*(next_node + 1)) {
+			/* Follow up with the next possibility. */
+			++next_node;
+		} else {
+			/* Move to the next path. */
+			if (stack_pos)
+				next_node = stack[--stack_pos];
+			next_node++;
+			stack[stack_pos] = next_node;
+		}
+		node = *next_node ? &graph[*next_node] : NULL;
+	};
+	return lsize;
+}
diff --git a/lib/librte_ethdev/rte_flow_driver.h b/lib/librte_ethdev/rte_flow_driver.h
index 1c90c600d..db87f4a51 100644
--- a/lib/librte_ethdev/rte_flow_driver.h
+++ b/lib/librte_ethdev/rte_flow_driver.h
@@ -114,6 +114,64 @@  struct rte_flow_ops {
 const struct rte_flow_ops *
 rte_flow_ops_get(uint16_t port_id, struct rte_flow_error *error);
 
+/** Helper macro to build input graph for rte_flow_expand_rss(). */
+#define RTE_FLOW_EXPAND_RSS_NEXT(...) \
+	(const int []){ \
+		__VA_ARGS__, 0, \
+	}
+
+/** Node object of input graph for rte_flow_expand_rss(). */
+struct rte_flow_expand_node {
+	const int *const next;
+	/**< List of next node indexes. Index 0 is interpreted as a
+	 * terminator. */
+	const enum rte_flow_item_type type;
+	/**< Pattern item type of current node. */
+	uint64_t rss_types;
+	/**< RSS types bit-field associated with this node (see ETH_RSS_*
+	 * definitions).*/
+};
+
+/** Object returned by rte_flow_expand_rss(). */
+struct rte_flow_expand_rss {
+	uint32_t entries;
+	/**< Number of entries @p patterns and @p priorities. */
+	struct {
+		struct rte_flow_item *pattern; /**< Expanded pattern array. */
+		uint32_t priority; /**< Priority offset for each expansion. */
+	} entry[];
+};
+
+/**
+ * Expand RSS flows into several possible flows according to the RSS hash
+ * fields requested and the driver capabilities.
+ *
+ * @param[out] buf
+ *   Buffer to store the result expansion.
+ * @param[in] size
+ *   Buffer size in bytes. If 0, @p buf can be NULL.
+ * @param[in] pattern
+ *   User flow pattern.
+ * @param[in] types
+ *   RSS types to expand (see ETH_RSS_* definitions).
+ * @param[in] graph.
+ *   Input graph to expand @p pattern according to @p types.
+ * @param[in] graph_root_index
+ *   Index of root node in @p graph, typically 0.
+ *
+ * @return
+ *   A positive value representing the size of @p buf in bytes regardless of
+ *   @p size on success, a negative errno value otherwise and rte_errno is
+ *   set, the following errors are defined:
+ *
+ *   -E2BIG: graph-depth @p graph is too deep.
+ */
+int
+rte_flow_expand_rss(struct rte_flow_expand_rss *buf, size_t size,
+		    const struct rte_flow_item *pattern, uint64_t types,
+		    const struct rte_flow_expand_node graph[],
+		    int graph_root_index);
+
 #ifdef __cplusplus
 }
 #endif