DPDK patches and discussions
 help / color / mirror / Atom feed
* [dpdk-dev] [DPDK 18.08] ethdev: add flow API to expand RSS flows
@ 2018-05-28  9:54 Nelio Laranjeiro
  2018-05-28 13:33 ` Wiles, Keith
  2018-06-05  8:26 ` [dpdk-dev] [PATCH v2] " Nelio Laranjeiro
  0 siblings, 2 replies; 11+ messages in thread
From: Nelio Laranjeiro @ 2018-05-28  9:54 UTC (permalink / raw)
  To: dev, Adrien Mazarguil

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>
---
 lib/librte_ethdev/rte_flow.c        | 404 ++++++++++++++++++++++++++++
 lib/librte_ethdev/rte_flow_driver.h |  32 +++
 2 files changed, 436 insertions(+)

diff --git a/lib/librte_ethdev/rte_flow.c b/lib/librte_ethdev/rte_flow.c
index 7947529da..0c42fc31c 100644
--- a/lib/librte_ethdev/rte_flow.c
+++ b/lib/librte_ethdev/rte_flow.c
@@ -507,3 +507,407 @@ rte_flow_copy(struct rte_flow_desc *desc, size_t len,
 	}
 	return 0;
 }
+
+/* Copy the existing items list and expand with new items. */
+static int
+rte_flow_expand_rss_item(void *buf, size_t size,
+			 const struct rte_flow_item *items,
+			 const struct rte_flow_item *newitems)
+{
+	void *data = buf;
+	const struct rte_flow_item *item;
+	struct rte_flow_item *dst;
+	size_t data_size = 0;
+
+	dst = data;
+	/* Copy Item structure into buffer. */
+	for (item = items; item->type != RTE_FLOW_ITEM_TYPE_END; ++item) {
+		if (item->type == RTE_FLOW_ITEM_TYPE_VOID)
+			continue;
+		if (data_size + sizeof(*item) <= size) {
+			memcpy(dst, item, sizeof(*item));
+			++dst;
+		}
+		data_size += sizeof(*item);
+	}
+	item = newitems;
+	do {
+		if (item->type == RTE_FLOW_ITEM_TYPE_VOID) {
+			++item;
+			continue;
+		}
+		if (data_size + sizeof(*item) <= size) {
+			memcpy(dst, item, sizeof(*item));
+			++dst;
+		}
+		data_size += sizeof(*item);
+		++item;
+	} while ((item - 1)->type != RTE_FLOW_ITEM_TYPE_END);
+	/**
+	 * Copy Item spec, last, mask into buffer and set pointers
+	 * accordingly.
+	 */
+	dst = data;
+	for (item = items; item->type != RTE_FLOW_ITEM_TYPE_END; ++item) {
+		if (item->type == RTE_FLOW_ITEM_TYPE_VOID)
+			continue;
+		if (item->spec) {
+			size_t s = flow_item_spec_copy(NULL, item, ITEM_SPEC);
+			void *addr = (data_size + s) <= size ?
+				(void *)((uintptr_t)data + data_size) :
+				NULL;
+
+			data_size += flow_item_spec_copy(addr, item, ITEM_SPEC);
+			if (addr)
+				dst->spec = addr;
+		}
+		if (item->last) {
+			size_t s = flow_item_spec_copy(NULL, item, ITEM_LAST);
+			void *addr = (data_size + s) <= size ?
+				(void *)((uintptr_t)data + data_size) :
+				NULL;
+
+			data_size += flow_item_spec_copy(addr, item, ITEM_LAST);
+			if (addr)
+				dst->last = addr;
+		}
+		if (item->mask) {
+			size_t s = flow_item_spec_copy(NULL, item, ITEM_MASK);
+			void *addr = (data_size + s) <= size ?
+				(void *)((uintptr_t)data + data_size) :
+				NULL;
+
+			data_size += flow_item_spec_copy(addr, item, ITEM_MASK);
+			if (addr)
+				dst->mask = addr;
+		}
+		if (data_size <= size)
+			++dst;
+	}
+	return data_size;
+}
+
+/** Verify the expansion is supported by the device. */
+static int
+rte_flow_expand_rss_is_supported(const enum rte_flow_item_type **supported,
+				 const enum rte_flow_item_type *expand)
+{
+	unsigned int i;
+	unsigned int sidx;
+	unsigned int eidx;
+
+	for (i = 0; supported[i]; ++i) {
+		sidx = 0;
+		eidx = 0;
+		while (1) {
+			if (expand[eidx] != supported[i][sidx]) {
+				break;
+			} else if ((expand[eidx] == RTE_FLOW_ITEM_TYPE_END) &&
+				   (supported[i][sidx] ==
+				    RTE_FLOW_ITEM_TYPE_END)) {
+				return 1;
+			} else if ((expand[eidx] == RTE_FLOW_ITEM_TYPE_END) ||
+				   (supported[i][sidx] ==
+				    RTE_FLOW_ITEM_TYPE_END)) {
+				break;
+			} else if (expand[eidx] == RTE_FLOW_ITEM_TYPE_VOID) {
+				++eidx;
+				continue;
+			} else if (supported[i][sidx] ==
+				   RTE_FLOW_ITEM_TYPE_VOID) {
+				++sidx;
+				continue;
+			}
+			++sidx;
+			++eidx;
+		}
+	}
+	return 0;
+}
+
+/** Update internal buffer. */
+static inline void
+rte_flow_expand_rss_update(struct rte_flow_expand_rss *buf, void *addr,
+			   uint32_t priority)
+{
+	buf->priority[buf->entries] = priority;
+	buf->patterns[buf->entries] = addr;
+	buf->entries++;
+}
+
+int
+rte_flow_expand_rss(struct rte_flow_expand_rss *buf, size_t size,
+		    const struct rte_flow_item *pat, uint64_t types,
+		    const enum rte_flow_item_type **supported)
+{
+	const struct rte_flow_item *item;
+	uint32_t priority = 0;
+	struct {
+		uint32_t eth:1; /**< Ethernet item is  present. */
+		uint32_t ipv4:1; /**< IPv4 item is  present. */
+		uint32_t ipv6:1; /**< IPv6 item is  present. */
+		uint32_t ipv6_ex:1; /**< IPv6 EXT item is  present. */
+		uint32_t udp:1; /**< UDP item is  present. */
+		uint32_t tcp:1; /**< TCP item is  present. */
+		uint32_t sctp:1; /**< STCP item is  present. */
+		uint32_t vxlan:1; /**< VXLAN item is  present. */
+		uint32_t geneve:1; /**< GENEVE item is  present. */
+		uint32_t nvgre:1; /**< NVGRE item is  present. */
+	} layer = { .eth = 0 };
+	const struct rte_flow_item end[] = {
+		{ .type = RTE_FLOW_ITEM_TYPE_END },
+	};
+	void *addr;
+	uint32_t off; /**< Offset to write new items data starting from *buf. */
+	uint32_t max_entries;
+
+	for (max_entries = 0; supported[max_entries]; ++max_entries)
+		;
+	off = sizeof(*buf) +
+		/* Size for the list of patterns. */
+		sizeof(*buf->patterns) +
+		RTE_ALIGN_CEIL(max_entries * sizeof(struct rte_flow_item *),
+			       sizeof(void *)) +
+		/* Size for priorities. */
+		sizeof(*buf->priority) +
+		RTE_ALIGN_CEIL(max_entries * sizeof(uint32_t), sizeof(void *));
+	if (off < size) {
+		buf->priority = (void *)(buf + 1);
+		buf->patterns = (void *)&buf->priority[max_entries];
+		buf->patterns[0] = (void *)&buf->patterns[max_entries];
+		addr = buf->patterns[0];
+		buf->entries = 0;
+	}
+	/**
+	 * Parse the pattern and deactivate the bit-field in RSS which cannot
+	 * match anymore the pattern.
+	 */
+	for (item = pat; item->type != RTE_FLOW_ITEM_TYPE_END; ++item) {
+		switch (item->type) {
+		case RTE_FLOW_ITEM_TYPE_ETH:
+			layer.eth = 1;
+			break;
+		case RTE_FLOW_ITEM_TYPE_IPV4:
+			layer.ipv4 = 1;
+			break;
+		case RTE_FLOW_ITEM_TYPE_IPV6:
+			layer.ipv6 = 1;
+			break;
+		case RTE_FLOW_ITEM_TYPE_IPV6_EXT:
+			layer.ipv6_ex = 1;
+			break;
+		case RTE_FLOW_ITEM_TYPE_UDP:
+			layer.udp = 1;
+			break;
+		case RTE_FLOW_ITEM_TYPE_TCP:
+			layer.tcp = 1;
+			break;
+		case RTE_FLOW_ITEM_TYPE_VXLAN:
+			layer.vxlan = 1;
+			break;
+		case RTE_FLOW_ITEM_TYPE_GENEVE:
+			layer.geneve = 1;
+			break;
+		case RTE_FLOW_ITEM_TYPE_NVGRE:
+			layer.nvgre = 1;
+			break;
+		default:
+			break;
+		}
+	}
+	off += rte_flow_expand_rss_item(addr, (off < size) ? size - off : 0,
+					pat, end);
+	if (off <= size) {
+		rte_flow_expand_rss_update(buf, addr, priority);
+		addr = (void *)((uintptr_t)buf + off);
+	}
+	if ((types & ETH_RSS_IP) &&
+	    (!(layer.ipv4 || layer.ipv6 || layer.ipv6_ex))) {
+		++priority;
+		if (types & (ETH_RSS_IPV4 | ETH_RSS_FRAG_IPV4 |
+			     ETH_RSS_NONFRAG_IPV4_OTHER)) {
+			const struct rte_flow_item new[] = {
+				{ .type = RTE_FLOW_ITEM_TYPE_IPV4 },
+				{ .type = RTE_FLOW_ITEM_TYPE_END },
+			};
+			const enum rte_flow_item_type list[] = {
+				RTE_FLOW_ITEM_TYPE_ETH,
+				RTE_FLOW_ITEM_TYPE_IPV4,
+				RTE_FLOW_ITEM_TYPE_END,
+			};
+			int ret;
+
+			ret = rte_flow_expand_rss_is_supported(supported, list);
+			if (ret) {
+				off += rte_flow_expand_rss_item
+					(addr, (off <= size) ? size - off : 0,
+					 pat, new);
+				if (off <= size) {
+					rte_flow_expand_rss_update(buf, addr,
+								   priority);
+					addr = (void *)((uintptr_t)buf + off);
+				}
+			}
+		}
+		if (types & (ETH_RSS_IPV6 | ETH_RSS_FRAG_IPV6 |
+			     ETH_RSS_NONFRAG_IPV6_OTHER)) {
+			const struct rte_flow_item new[] = {
+				{ .type = RTE_FLOW_ITEM_TYPE_IPV6 },
+				{ .type = RTE_FLOW_ITEM_TYPE_END },
+			};
+			const enum rte_flow_item_type list[] = {
+				RTE_FLOW_ITEM_TYPE_ETH,
+				RTE_FLOW_ITEM_TYPE_IPV6,
+				RTE_FLOW_ITEM_TYPE_END,
+			};
+			int ret;
+
+			ret = rte_flow_expand_rss_is_supported(supported, list);
+			if (ret) {
+				off += rte_flow_expand_rss_item
+					(addr, (size < off) ? size - off : 0,
+					 pat, new);
+				if (off <= size) {
+					rte_flow_expand_rss_update(buf, addr,
+								   priority);
+					addr = (void *)((uintptr_t)buf + off);
+				}
+			}
+		}
+		if (types & ETH_RSS_IPV6_EX) {
+			const struct rte_flow_item new[] = {
+				{ .type = RTE_FLOW_ITEM_TYPE_IPV6_EXT },
+				{ .type = RTE_FLOW_ITEM_TYPE_END },
+			};
+			const enum rte_flow_item_type list[] = {
+				RTE_FLOW_ITEM_TYPE_ETH,
+				RTE_FLOW_ITEM_TYPE_IPV6_EXT,
+				RTE_FLOW_ITEM_TYPE_END,
+			};
+			int ret;
+
+			ret = rte_flow_expand_rss_is_supported(supported, list);
+			if (ret) {
+				off += rte_flow_expand_rss_item
+					(addr, (off <= size) ? size - off : 0,
+					 pat, new);
+				if (off <= size) {
+					rte_flow_expand_rss_update(buf, addr,
+								   priority);
+					addr = (void *)((uintptr_t)buf + off);
+				}
+			}
+		}
+	}
+	if (types & (ETH_RSS_TCP | ETH_RSS_UDP)) {
+		++priority;
+		if ((types & ETH_RSS_NONFRAG_IPV4_UDP) &&
+		    !(layer.ipv6 || layer.ipv6_ex || layer.tcp || layer.udp)) {
+			const struct rte_flow_item new[] = {
+				{ .type = RTE_FLOW_ITEM_TYPE_UDP },
+				{ .type = RTE_FLOW_ITEM_TYPE_END },
+			};
+			const enum rte_flow_item_type list[] = {
+				RTE_FLOW_ITEM_TYPE_ETH,
+				RTE_FLOW_ITEM_TYPE_IPV4,
+				RTE_FLOW_ITEM_TYPE_UDP,
+				RTE_FLOW_ITEM_TYPE_END,
+			};
+			int ret;
+
+			ret = rte_flow_expand_rss_is_supported(supported, list);
+			if (ret) {
+				off += rte_flow_expand_rss_item
+					(addr, (off <= size) ? size - off : 0,
+					 pat, new);
+				if (off <= size) {
+					rte_flow_expand_rss_update(buf, addr,
+								   priority);
+					addr = (void *)((uintptr_t)buf + off);
+				}
+			}
+		}
+		if ((types & ETH_RSS_NONFRAG_IPV4_TCP) &&
+		    !(layer.ipv6 || layer.ipv6_ex || layer.tcp || layer.udp)) {
+			const struct rte_flow_item new[] = {
+				{ .type = RTE_FLOW_ITEM_TYPE_TCP },
+				{ .type = RTE_FLOW_ITEM_TYPE_END },
+			};
+			const enum rte_flow_item_type list[] = {
+				RTE_FLOW_ITEM_TYPE_ETH,
+				RTE_FLOW_ITEM_TYPE_IPV4,
+				RTE_FLOW_ITEM_TYPE_TCP,
+				RTE_FLOW_ITEM_TYPE_END,
+			};
+			int ret;
+
+			ret = rte_flow_expand_rss_is_supported(supported, list);
+			if (ret) {
+				off += rte_flow_expand_rss_item
+					(addr, (off <= size) ? size - off : 0,
+					 pat, new);
+				if (off <= size) {
+					rte_flow_expand_rss_update(buf, addr,
+								   priority);
+					addr = (void *)((uintptr_t)buf + off);
+				}
+			}
+		}
+		if ((types & ETH_RSS_NONFRAG_IPV6_UDP) &&
+		    !(layer.ipv4 || layer.tcp || layer.udp)) {
+			const struct rte_flow_item new[] = {
+				{ .type = RTE_FLOW_ITEM_TYPE_UDP },
+				{ .type = RTE_FLOW_ITEM_TYPE_END },
+			};
+			const enum rte_flow_item_type list[] = {
+				RTE_FLOW_ITEM_TYPE_ETH,
+				RTE_FLOW_ITEM_TYPE_IPV6,
+				RTE_FLOW_ITEM_TYPE_UDP,
+				RTE_FLOW_ITEM_TYPE_END,
+			};
+			int ret;
+
+			ret = rte_flow_expand_rss_is_supported(supported, list);
+			if (ret) {
+				off += rte_flow_expand_rss_item
+					(addr, (size < off) ? size - off : 0,
+					 pat, new);
+				if (off <= size) {
+					rte_flow_expand_rss_update(buf, addr,
+								   priority);
+					addr = (void *)((uintptr_t)buf + off);
+				}
+			}
+		}
+		if ((types & (ETH_RSS_NONFRAG_IPV6_TCP |
+			      ETH_RSS_IPV6_TCP_EX)) &&
+		    !(layer.ipv4 || layer.tcp || layer.udp)) {
+			const struct rte_flow_item new[] = {
+				{ .type = RTE_FLOW_ITEM_TYPE_TCP },
+				{ .type = RTE_FLOW_ITEM_TYPE_END },
+			};
+			const enum rte_flow_item_type list[] = {
+				RTE_FLOW_ITEM_TYPE_ETH,
+				(layer.ipv6_ex ?
+				 RTE_FLOW_ITEM_TYPE_IPV6_EXT :
+				 RTE_FLOW_ITEM_TYPE_IPV6),
+				RTE_FLOW_ITEM_TYPE_UDP,
+				RTE_FLOW_ITEM_TYPE_END,
+			};
+			int ret;
+
+			ret = rte_flow_expand_rss_is_supported(supported, list);
+			if (ret) {
+				off += rte_flow_expand_rss_item
+					(addr, (off < size) ? size - off : 0,
+					 pat, new);
+				if (off <= size) {
+					rte_flow_expand_rss_update(buf, addr,
+								   priority);
+					addr = (void *)((uintptr_t)buf + off);
+				}
+			}
+		}
+	}
+	return off;
+}
diff --git a/lib/librte_ethdev/rte_flow_driver.h b/lib/librte_ethdev/rte_flow_driver.h
index 1c90c600d..9058a8715 100644
--- a/lib/librte_ethdev/rte_flow_driver.h
+++ b/lib/librte_ethdev/rte_flow_driver.h
@@ -114,6 +114,38 @@ struct rte_flow_ops {
 const struct rte_flow_ops *
 rte_flow_ops_get(uint16_t port_id, struct rte_flow_error *error);
 
+/**
+ * Expansion structure for RSS flows.
+ */
+struct rte_flow_expand_rss {
+	uint32_t entries; /**< Number of entries in the following arrays. */
+	struct rte_flow_item **patterns; /**< Expanded pattern array. */
+	uint32_t *priority; /**< Priority offset for each expansion. */
+};
+
+/**
+ * Expand RSS flows into several possible flows according to the RSS hash
+ * fields requested and the driver capabilities.
+ *
+ * @param[in,out] buf
+ *   Buffer to store the result expansion.
+ * @param[in] size
+ *   Size in octets of the buffer.
+ * @param[in] pat
+ *   User flow pattern.
+ * @param[in] types
+ *   RSS types expected (see ETH_RSS_*).
+ * @param[in] supported.
+ *   List of support expansion pattern from the device.
+ *
+ * @return
+ *   The size in octets used to expand.
+ */
+int
+rte_flow_expand_rss(struct rte_flow_expand_rss *buf, size_t size,
+		    const struct rte_flow_item *pat, uint64_t types,
+		    const enum rte_flow_item_type **supported);
+
 #ifdef __cplusplus
 }
 #endif
-- 
2.17.0

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [dpdk-dev] [DPDK 18.08] ethdev: add flow API to expand RSS flows
  2018-05-28  9:54 [dpdk-dev] [DPDK 18.08] ethdev: add flow API to expand RSS flows Nelio Laranjeiro
@ 2018-05-28 13:33 ` Wiles, Keith
  2018-06-05  8:26 ` [dpdk-dev] [PATCH v2] " Nelio Laranjeiro
  1 sibling, 0 replies; 11+ messages in thread
From: Wiles, Keith @ 2018-05-28 13:33 UTC (permalink / raw)
  To: Nelio Laranjeiro; +Cc: dev, Adrien Mazarguil

This one too is missing the [PATCH ] keyword in the subject line.

> On May 28, 2018, at 4:54 AM, Nelio Laranjeiro <nelio.laranjeiro@6wind.com> 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>
> ---
> lib/librte_ethdev/rte_flow.c        | 404 ++++++++++++++++++++++++++++
> lib/librte_ethdev/rte_flow_driver.h |  32 +++
> 2 files changed, 436 insertions(+)
> 
> diff --git a/lib/librte_ethdev/rte_flow.c b/lib/librte_ethdev/rte_flow.c
> index 7947529da..0c42fc31c 100644
> --- a/lib/librte_ethdev/rte_flow.c
> +++ b/lib/librte_ethdev/rte_flow.c
> @@ -507,3 +507,407 @@ rte_flow_copy(struct rte_flow_desc *desc, size_t len,
> 	}
> 	return 0;
> }
> +
> +/* Copy the existing items list and expand with new items. */
> +static int
> +rte_flow_expand_rss_item(void *buf, size_t size,
> +			 const struct rte_flow_item *items,
> +			 const struct rte_flow_item *newitems)
> +{
> +	void *data = buf;
> +	const struct rte_flow_item *item;
> +	struct rte_flow_item *dst;
> +	size_t data_size = 0;
> +
> +	dst = data;
> +	/* Copy Item structure into buffer. */
> +	for (item = items; item->type != RTE_FLOW_ITEM_TYPE_END; ++item) {
> +		if (item->type == RTE_FLOW_ITEM_TYPE_VOID)
> +			continue;
> +		if (data_size + sizeof(*item) <= size) {
> +			memcpy(dst, item, sizeof(*item));
> +			++dst;
> +		}
> +		data_size += sizeof(*item);
> +	}
> +	item = newitems;
> +	do {
> +		if (item->type == RTE_FLOW_ITEM_TYPE_VOID) {
> +			++item;
> +			continue;
> +		}
> +		if (data_size + sizeof(*item) <= size) {
> +			memcpy(dst, item, sizeof(*item));
> +			++dst;
> +		}
> +		data_size += sizeof(*item);
> +		++item;
> +	} while ((item - 1)->type != RTE_FLOW_ITEM_TYPE_END);
> +	/**
> +	 * Copy Item spec, last, mask into buffer and set pointers
> +	 * accordingly.
> +	 */
> +	dst = data;
> +	for (item = items; item->type != RTE_FLOW_ITEM_TYPE_END; ++item) {
> +		if (item->type == RTE_FLOW_ITEM_TYPE_VOID)
> +			continue;
> +		if (item->spec) {
> +			size_t s = flow_item_spec_copy(NULL, item, ITEM_SPEC);
> +			void *addr = (data_size + s) <= size ?
> +				(void *)((uintptr_t)data + data_size) :
> +				NULL;
> +
> +			data_size += flow_item_spec_copy(addr, item, ITEM_SPEC);
> +			if (addr)
> +				dst->spec = addr;
> +		}
> +		if (item->last) {
> +			size_t s = flow_item_spec_copy(NULL, item, ITEM_LAST);
> +			void *addr = (data_size + s) <= size ?
> +				(void *)((uintptr_t)data + data_size) :
> +				NULL;
> +
> +			data_size += flow_item_spec_copy(addr, item, ITEM_LAST);
> +			if (addr)
> +				dst->last = addr;
> +		}
> +		if (item->mask) {
> +			size_t s = flow_item_spec_copy(NULL, item, ITEM_MASK);
> +			void *addr = (data_size + s) <= size ?
> +				(void *)((uintptr_t)data + data_size) :
> +				NULL;
> +
> +			data_size += flow_item_spec_copy(addr, item, ITEM_MASK);
> +			if (addr)
> +				dst->mask = addr;
> +		}
> +		if (data_size <= size)
> +			++dst;
> +	}
> +	return data_size;
> +}
> +
> +/** Verify the expansion is supported by the device. */
> +static int
> +rte_flow_expand_rss_is_supported(const enum rte_flow_item_type **supported,
> +				 const enum rte_flow_item_type *expand)
> +{
> +	unsigned int i;
> +	unsigned int sidx;
> +	unsigned int eidx;
> +
> +	for (i = 0; supported[i]; ++i) {
> +		sidx = 0;
> +		eidx = 0;
> +		while (1) {
> +			if (expand[eidx] != supported[i][sidx]) {
> +				break;
> +			} else if ((expand[eidx] == RTE_FLOW_ITEM_TYPE_END) &&
> +				   (supported[i][sidx] ==
> +				    RTE_FLOW_ITEM_TYPE_END)) {
> +				return 1;
> +			} else if ((expand[eidx] == RTE_FLOW_ITEM_TYPE_END) ||
> +				   (supported[i][sidx] ==
> +				    RTE_FLOW_ITEM_TYPE_END)) {
> +				break;
> +			} else if (expand[eidx] == RTE_FLOW_ITEM_TYPE_VOID) {
> +				++eidx;
> +				continue;
> +			} else if (supported[i][sidx] ==
> +				   RTE_FLOW_ITEM_TYPE_VOID) {
> +				++sidx;
> +				continue;
> +			}
> +			++sidx;
> +			++eidx;
> +		}
> +	}
> +	return 0;
> +}
> +
> +/** Update internal buffer. */
> +static inline void
> +rte_flow_expand_rss_update(struct rte_flow_expand_rss *buf, void *addr,
> +			   uint32_t priority)
> +{
> +	buf->priority[buf->entries] = priority;
> +	buf->patterns[buf->entries] = addr;
> +	buf->entries++;
> +}
> +
> +int
> +rte_flow_expand_rss(struct rte_flow_expand_rss *buf, size_t size,
> +		    const struct rte_flow_item *pat, uint64_t types,
> +		    const enum rte_flow_item_type **supported)
> +{
> +	const struct rte_flow_item *item;
> +	uint32_t priority = 0;
> +	struct {
> +		uint32_t eth:1; /**< Ethernet item is  present. */
> +		uint32_t ipv4:1; /**< IPv4 item is  present. */
> +		uint32_t ipv6:1; /**< IPv6 item is  present. */
> +		uint32_t ipv6_ex:1; /**< IPv6 EXT item is  present. */
> +		uint32_t udp:1; /**< UDP item is  present. */
> +		uint32_t tcp:1; /**< TCP item is  present. */
> +		uint32_t sctp:1; /**< STCP item is  present. */
> +		uint32_t vxlan:1; /**< VXLAN item is  present. */
> +		uint32_t geneve:1; /**< GENEVE item is  present. */
> +		uint32_t nvgre:1; /**< NVGRE item is  present. */
> +	} layer = { .eth = 0 };
> +	const struct rte_flow_item end[] = {
> +		{ .type = RTE_FLOW_ITEM_TYPE_END },
> +	};
> +	void *addr;
> +	uint32_t off; /**< Offset to write new items data starting from *buf. */
> +	uint32_t max_entries;
> +
> +	for (max_entries = 0; supported[max_entries]; ++max_entries)
> +		;
> +	off = sizeof(*buf) +
> +		/* Size for the list of patterns. */
> +		sizeof(*buf->patterns) +
> +		RTE_ALIGN_CEIL(max_entries * sizeof(struct rte_flow_item *),
> +			       sizeof(void *)) +
> +		/* Size for priorities. */
> +		sizeof(*buf->priority) +
> +		RTE_ALIGN_CEIL(max_entries * sizeof(uint32_t), sizeof(void *));
> +	if (off < size) {
> +		buf->priority = (void *)(buf + 1);
> +		buf->patterns = (void *)&buf->priority[max_entries];
> +		buf->patterns[0] = (void *)&buf->patterns[max_entries];
> +		addr = buf->patterns[0];
> +		buf->entries = 0;
> +	}
> +	/**
> +	 * Parse the pattern and deactivate the bit-field in RSS which cannot
> +	 * match anymore the pattern.
> +	 */
> +	for (item = pat; item->type != RTE_FLOW_ITEM_TYPE_END; ++item) {
> +		switch (item->type) {
> +		case RTE_FLOW_ITEM_TYPE_ETH:
> +			layer.eth = 1;
> +			break;
> +		case RTE_FLOW_ITEM_TYPE_IPV4:
> +			layer.ipv4 = 1;
> +			break;
> +		case RTE_FLOW_ITEM_TYPE_IPV6:
> +			layer.ipv6 = 1;
> +			break;
> +		case RTE_FLOW_ITEM_TYPE_IPV6_EXT:
> +			layer.ipv6_ex = 1;
> +			break;
> +		case RTE_FLOW_ITEM_TYPE_UDP:
> +			layer.udp = 1;
> +			break;
> +		case RTE_FLOW_ITEM_TYPE_TCP:
> +			layer.tcp = 1;
> +			break;
> +		case RTE_FLOW_ITEM_TYPE_VXLAN:
> +			layer.vxlan = 1;
> +			break;
> +		case RTE_FLOW_ITEM_TYPE_GENEVE:
> +			layer.geneve = 1;
> +			break;
> +		case RTE_FLOW_ITEM_TYPE_NVGRE:
> +			layer.nvgre = 1;
> +			break;
> +		default:
> +			break;
> +		}
> +	}
> +	off += rte_flow_expand_rss_item(addr, (off < size) ? size - off : 0,
> +					pat, end);
> +	if (off <= size) {
> +		rte_flow_expand_rss_update(buf, addr, priority);
> +		addr = (void *)((uintptr_t)buf + off);
> +	}
> +	if ((types & ETH_RSS_IP) &&
> +	    (!(layer.ipv4 || layer.ipv6 || layer.ipv6_ex))) {
> +		++priority;
> +		if (types & (ETH_RSS_IPV4 | ETH_RSS_FRAG_IPV4 |
> +			     ETH_RSS_NONFRAG_IPV4_OTHER)) {
> +			const struct rte_flow_item new[] = {
> +				{ .type = RTE_FLOW_ITEM_TYPE_IPV4 },
> +				{ .type = RTE_FLOW_ITEM_TYPE_END },
> +			};
> +			const enum rte_flow_item_type list[] = {
> +				RTE_FLOW_ITEM_TYPE_ETH,
> +				RTE_FLOW_ITEM_TYPE_IPV4,
> +				RTE_FLOW_ITEM_TYPE_END,
> +			};
> +			int ret;
> +
> +			ret = rte_flow_expand_rss_is_supported(supported, list);
> +			if (ret) {
> +				off += rte_flow_expand_rss_item
> +					(addr, (off <= size) ? size - off : 0,
> +					 pat, new);
> +				if (off <= size) {
> +					rte_flow_expand_rss_update(buf, addr,
> +								   priority);
> +					addr = (void *)((uintptr_t)buf + off);
> +				}
> +			}
> +		}
> +		if (types & (ETH_RSS_IPV6 | ETH_RSS_FRAG_IPV6 |
> +			     ETH_RSS_NONFRAG_IPV6_OTHER)) {
> +			const struct rte_flow_item new[] = {
> +				{ .type = RTE_FLOW_ITEM_TYPE_IPV6 },
> +				{ .type = RTE_FLOW_ITEM_TYPE_END },
> +			};
> +			const enum rte_flow_item_type list[] = {
> +				RTE_FLOW_ITEM_TYPE_ETH,
> +				RTE_FLOW_ITEM_TYPE_IPV6,
> +				RTE_FLOW_ITEM_TYPE_END,
> +			};
> +			int ret;
> +
> +			ret = rte_flow_expand_rss_is_supported(supported, list);
> +			if (ret) {
> +				off += rte_flow_expand_rss_item
> +					(addr, (size < off) ? size - off : 0,
> +					 pat, new);
> +				if (off <= size) {
> +					rte_flow_expand_rss_update(buf, addr,
> +								   priority);
> +					addr = (void *)((uintptr_t)buf + off);
> +				}
> +			}
> +		}
> +		if (types & ETH_RSS_IPV6_EX) {
> +			const struct rte_flow_item new[] = {
> +				{ .type = RTE_FLOW_ITEM_TYPE_IPV6_EXT },
> +				{ .type = RTE_FLOW_ITEM_TYPE_END },
> +			};
> +			const enum rte_flow_item_type list[] = {
> +				RTE_FLOW_ITEM_TYPE_ETH,
> +				RTE_FLOW_ITEM_TYPE_IPV6_EXT,
> +				RTE_FLOW_ITEM_TYPE_END,
> +			};
> +			int ret;
> +
> +			ret = rte_flow_expand_rss_is_supported(supported, list);
> +			if (ret) {
> +				off += rte_flow_expand_rss_item
> +					(addr, (off <= size) ? size - off : 0,
> +					 pat, new);
> +				if (off <= size) {
> +					rte_flow_expand_rss_update(buf, addr,
> +								   priority);
> +					addr = (void *)((uintptr_t)buf + off);
> +				}
> +			}
> +		}
> +	}
> +	if (types & (ETH_RSS_TCP | ETH_RSS_UDP)) {
> +		++priority;
> +		if ((types & ETH_RSS_NONFRAG_IPV4_UDP) &&
> +		    !(layer.ipv6 || layer.ipv6_ex || layer.tcp || layer.udp)) {
> +			const struct rte_flow_item new[] = {
> +				{ .type = RTE_FLOW_ITEM_TYPE_UDP },
> +				{ .type = RTE_FLOW_ITEM_TYPE_END },
> +			};
> +			const enum rte_flow_item_type list[] = {
> +				RTE_FLOW_ITEM_TYPE_ETH,
> +				RTE_FLOW_ITEM_TYPE_IPV4,
> +				RTE_FLOW_ITEM_TYPE_UDP,
> +				RTE_FLOW_ITEM_TYPE_END,
> +			};
> +			int ret;
> +
> +			ret = rte_flow_expand_rss_is_supported(supported, list);
> +			if (ret) {
> +				off += rte_flow_expand_rss_item
> +					(addr, (off <= size) ? size - off : 0,
> +					 pat, new);
> +				if (off <= size) {
> +					rte_flow_expand_rss_update(buf, addr,
> +								   priority);
> +					addr = (void *)((uintptr_t)buf + off);
> +				}
> +			}
> +		}
> +		if ((types & ETH_RSS_NONFRAG_IPV4_TCP) &&
> +		    !(layer.ipv6 || layer.ipv6_ex || layer.tcp || layer.udp)) {
> +			const struct rte_flow_item new[] = {
> +				{ .type = RTE_FLOW_ITEM_TYPE_TCP },
> +				{ .type = RTE_FLOW_ITEM_TYPE_END },
> +			};
> +			const enum rte_flow_item_type list[] = {
> +				RTE_FLOW_ITEM_TYPE_ETH,
> +				RTE_FLOW_ITEM_TYPE_IPV4,
> +				RTE_FLOW_ITEM_TYPE_TCP,
> +				RTE_FLOW_ITEM_TYPE_END,
> +			};
> +			int ret;
> +
> +			ret = rte_flow_expand_rss_is_supported(supported, list);
> +			if (ret) {
> +				off += rte_flow_expand_rss_item
> +					(addr, (off <= size) ? size - off : 0,
> +					 pat, new);
> +				if (off <= size) {
> +					rte_flow_expand_rss_update(buf, addr,
> +								   priority);
> +					addr = (void *)((uintptr_t)buf + off);
> +				}
> +			}
> +		}
> +		if ((types & ETH_RSS_NONFRAG_IPV6_UDP) &&
> +		    !(layer.ipv4 || layer.tcp || layer.udp)) {
> +			const struct rte_flow_item new[] = {
> +				{ .type = RTE_FLOW_ITEM_TYPE_UDP },
> +				{ .type = RTE_FLOW_ITEM_TYPE_END },
> +			};
> +			const enum rte_flow_item_type list[] = {
> +				RTE_FLOW_ITEM_TYPE_ETH,
> +				RTE_FLOW_ITEM_TYPE_IPV6,
> +				RTE_FLOW_ITEM_TYPE_UDP,
> +				RTE_FLOW_ITEM_TYPE_END,
> +			};
> +			int ret;
> +
> +			ret = rte_flow_expand_rss_is_supported(supported, list);
> +			if (ret) {
> +				off += rte_flow_expand_rss_item
> +					(addr, (size < off) ? size - off : 0,
> +					 pat, new);
> +				if (off <= size) {
> +					rte_flow_expand_rss_update(buf, addr,
> +								   priority);
> +					addr = (void *)((uintptr_t)buf + off);
> +				}
> +			}
> +		}
> +		if ((types & (ETH_RSS_NONFRAG_IPV6_TCP |
> +			      ETH_RSS_IPV6_TCP_EX)) &&
> +		    !(layer.ipv4 || layer.tcp || layer.udp)) {
> +			const struct rte_flow_item new[] = {
> +				{ .type = RTE_FLOW_ITEM_TYPE_TCP },
> +				{ .type = RTE_FLOW_ITEM_TYPE_END },
> +			};
> +			const enum rte_flow_item_type list[] = {
> +				RTE_FLOW_ITEM_TYPE_ETH,
> +				(layer.ipv6_ex ?
> +				 RTE_FLOW_ITEM_TYPE_IPV6_EXT :
> +				 RTE_FLOW_ITEM_TYPE_IPV6),
> +				RTE_FLOW_ITEM_TYPE_UDP,
> +				RTE_FLOW_ITEM_TYPE_END,
> +			};
> +			int ret;
> +
> +			ret = rte_flow_expand_rss_is_supported(supported, list);
> +			if (ret) {
> +				off += rte_flow_expand_rss_item
> +					(addr, (off < size) ? size - off : 0,
> +					 pat, new);
> +				if (off <= size) {
> +					rte_flow_expand_rss_update(buf, addr,
> +								   priority);
> +					addr = (void *)((uintptr_t)buf + off);
> +				}
> +			}
> +		}
> +	}
> +	return off;
> +}
> diff --git a/lib/librte_ethdev/rte_flow_driver.h b/lib/librte_ethdev/rte_flow_driver.h
> index 1c90c600d..9058a8715 100644
> --- a/lib/librte_ethdev/rte_flow_driver.h
> +++ b/lib/librte_ethdev/rte_flow_driver.h
> @@ -114,6 +114,38 @@ struct rte_flow_ops {
> const struct rte_flow_ops *
> rte_flow_ops_get(uint16_t port_id, struct rte_flow_error *error);
> 
> +/**
> + * Expansion structure for RSS flows.
> + */
> +struct rte_flow_expand_rss {
> +	uint32_t entries; /**< Number of entries in the following arrays. */
> +	struct rte_flow_item **patterns; /**< Expanded pattern array. */
> +	uint32_t *priority; /**< Priority offset for each expansion. */
> +};
> +
> +/**
> + * Expand RSS flows into several possible flows according to the RSS hash
> + * fields requested and the driver capabilities.
> + *
> + * @param[in,out] buf
> + *   Buffer to store the result expansion.
> + * @param[in] size
> + *   Size in octets of the buffer.
> + * @param[in] pat
> + *   User flow pattern.
> + * @param[in] types
> + *   RSS types expected (see ETH_RSS_*).
> + * @param[in] supported.
> + *   List of support expansion pattern from the device.
> + *
> + * @return
> + *   The size in octets used to expand.
> + */
> +int
> +rte_flow_expand_rss(struct rte_flow_expand_rss *buf, size_t size,
> +		    const struct rte_flow_item *pat, uint64_t types,
> +		    const enum rte_flow_item_type **supported);
> +
> #ifdef __cplusplus
> }
> #endif
> -- 
> 2.17.0
> 

Regards,
Keith

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [dpdk-dev] [PATCH v2] ethdev: add flow API to expand RSS flows
  2018-05-28  9:54 [dpdk-dev] [DPDK 18.08] ethdev: add flow API to expand RSS flows Nelio Laranjeiro
  2018-05-28 13:33 ` Wiles, Keith
@ 2018-06-05  8:26 ` Nelio Laranjeiro
  2018-06-21  7:25   ` [dpdk-dev] [PATCH v3] " Nelio Laranjeiro
  1 sibling, 1 reply; 11+ messages in thread
From: Nelio Laranjeiro @ 2018-06-05  8:26 UTC (permalink / raw)
  To: dev, Adrien Mazarguil

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 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        | 408 ++++++++++++++++++++++++++++
 lib/librte_ethdev/rte_flow_driver.h |  32 +++
 2 files changed, 440 insertions(+)

diff --git a/lib/librte_ethdev/rte_flow.c b/lib/librte_ethdev/rte_flow.c
index b2afba089..e6fed59cd 100644
--- a/lib/librte_ethdev/rte_flow.c
+++ b/lib/librte_ethdev/rte_flow.c
@@ -526,3 +526,411 @@ rte_flow_copy(struct rte_flow_desc *desc, size_t len,
 	}
 	return 0;
 }
+
+/* Copy the existing items list and expand with new items. */
+static int
+rte_flow_expand_rss_item(void *buf, size_t size,
+			 const struct rte_flow_item *items,
+			 const struct rte_flow_item *newitems)
+{
+	void *data = buf;
+	const struct rte_flow_item *item;
+	struct rte_flow_item *dst;
+	size_t data_size = 0;
+
+	dst = data;
+	/* Copy Item structure into buffer. */
+	for (item = items; item->type != RTE_FLOW_ITEM_TYPE_END; ++item) {
+		if (item->type == RTE_FLOW_ITEM_TYPE_VOID)
+			continue;
+		if (data_size + sizeof(*item) <= size) {
+			memcpy(dst, item, sizeof(*item));
+			++dst;
+		}
+		data_size += sizeof(*item);
+	}
+	item = newitems;
+	do {
+		if (item->type == RTE_FLOW_ITEM_TYPE_VOID) {
+			++item;
+			continue;
+		}
+		if (data_size + sizeof(*item) <= size) {
+			memcpy(dst, item, sizeof(*item));
+			++dst;
+		}
+		data_size += sizeof(*item);
+		++item;
+	} while ((item - 1)->type != RTE_FLOW_ITEM_TYPE_END);
+	/**
+	 * Copy Item spec, last, mask into buffer and set pointers
+	 * accordingly.
+	 */
+	dst = data;
+	for (item = items; item->type != RTE_FLOW_ITEM_TYPE_END; ++item) {
+		if (item->type == RTE_FLOW_ITEM_TYPE_VOID)
+			continue;
+		if (item->spec) {
+			size_t s = flow_item_spec_copy(NULL, item, ITEM_SPEC);
+			void *addr = (data_size + s) <= size ?
+				(void *)((uintptr_t)data + data_size) :
+				NULL;
+
+			data_size += flow_item_spec_copy(addr, item, ITEM_SPEC);
+			if (addr)
+				dst->spec = addr;
+		}
+		if (item->last) {
+			size_t s = flow_item_spec_copy(NULL, item, ITEM_LAST);
+			void *addr = (data_size + s) <= size ?
+				(void *)((uintptr_t)data + data_size) :
+				NULL;
+
+			data_size += flow_item_spec_copy(addr, item, ITEM_LAST);
+			if (addr)
+				dst->last = addr;
+		}
+		if (item->mask) {
+			size_t s = flow_item_spec_copy(NULL, item, ITEM_MASK);
+			void *addr = (data_size + s) <= size ?
+				(void *)((uintptr_t)data + data_size) :
+				NULL;
+
+			data_size += flow_item_spec_copy(addr, item, ITEM_MASK);
+			if (addr)
+				dst->mask = addr;
+		}
+		if (data_size <= size)
+			++dst;
+	}
+	return data_size;
+}
+
+/** Verify the expansion is supported by the device. */
+static int
+rte_flow_expand_rss_is_supported(const enum rte_flow_item_type **supported,
+				 const enum rte_flow_item_type *expand)
+{
+	unsigned int i;
+	unsigned int sidx;
+	unsigned int eidx;
+
+	for (i = 0; supported[i]; ++i) {
+		sidx = 0;
+		eidx = 0;
+		while (1) {
+			if (expand[eidx] != supported[i][sidx]) {
+				break;
+			} else if ((expand[eidx] == RTE_FLOW_ITEM_TYPE_END) &&
+				   (supported[i][sidx] ==
+				    RTE_FLOW_ITEM_TYPE_END)) {
+				return 1;
+			} else if ((expand[eidx] == RTE_FLOW_ITEM_TYPE_END) ||
+				   (supported[i][sidx] ==
+				    RTE_FLOW_ITEM_TYPE_END)) {
+				break;
+			} else if (expand[eidx] == RTE_FLOW_ITEM_TYPE_VOID) {
+				++eidx;
+				continue;
+			} else if (supported[i][sidx] ==
+				   RTE_FLOW_ITEM_TYPE_VOID) {
+				++sidx;
+				continue;
+			}
+			++sidx;
+			++eidx;
+		}
+	}
+	return 0;
+}
+
+/** Update internal buffer. */
+static inline void
+rte_flow_expand_rss_update(struct rte_flow_expand_rss *buf, void *addr,
+			   uint32_t priority)
+{
+	buf->priority[buf->entries] = priority;
+	buf->patterns[buf->entries] = addr;
+	buf->entries++;
+}
+
+int
+rte_flow_expand_rss(struct rte_flow_expand_rss *buf, size_t size,
+		    const struct rte_flow_item *pat, uint64_t types,
+		    const enum rte_flow_item_type **supported)
+{
+	const struct rte_flow_item *item;
+	uint32_t priority = 0;
+	struct {
+		uint32_t eth:1; /**< Ethernet item is  present. */
+		uint32_t ipv4:1; /**< IPv4 item is  present. */
+		uint32_t ipv6:1; /**< IPv6 item is  present. */
+		uint32_t ipv6_ex:1; /**< IPv6 EXT item is  present. */
+		uint32_t udp:1; /**< UDP item is  present. */
+		uint32_t tcp:1; /**< TCP item is  present. */
+		uint32_t sctp:1; /**< STCP item is  present. */
+		uint32_t vxlan:1; /**< VXLAN item is  present. */
+		uint32_t geneve:1; /**< GENEVE item is  present. */
+		uint32_t nvgre:1; /**< NVGRE item is  present. */
+	} layer = { .eth = 0 };
+	const struct rte_flow_item end[] = {
+		{ .type = RTE_FLOW_ITEM_TYPE_END },
+	};
+	void *addr;
+	uint32_t off; /**< Offset to write new items data starting from *buf. */
+	uint32_t max_entries;
+
+	for (max_entries = 0; supported[max_entries]; ++max_entries)
+		;
+	off = sizeof(*buf) +
+		/* Size for the list of patterns. */
+		sizeof(*buf->patterns) +
+		RTE_ALIGN_CEIL(max_entries * sizeof(struct rte_flow_item *),
+			       sizeof(void *)) +
+		/* Size for priorities. */
+		sizeof(*buf->priority) +
+		RTE_ALIGN_CEIL(max_entries * sizeof(uint32_t), sizeof(void *));
+	if (off < size) {
+		buf->priority = (void *)(buf + 1);
+		buf->patterns = (void *)&buf->priority[max_entries];
+		buf->patterns[0] = (void *)&buf->patterns[max_entries];
+		addr = buf->patterns[0];
+		buf->entries = 0;
+	}
+	/**
+	 * Parse the pattern and deactivate the bit-field in RSS which cannot
+	 * match anymore the pattern.
+	 */
+	for (item = pat; item->type != RTE_FLOW_ITEM_TYPE_END; ++item) {
+		switch (item->type) {
+		case RTE_FLOW_ITEM_TYPE_ETH:
+			layer.eth = 1;
+			break;
+		case RTE_FLOW_ITEM_TYPE_IPV4:
+			layer.ipv4 = 1;
+			break;
+		case RTE_FLOW_ITEM_TYPE_IPV6:
+			layer.ipv6 = 1;
+			break;
+		case RTE_FLOW_ITEM_TYPE_IPV6_EXT:
+			layer.ipv6_ex = 1;
+			break;
+		case RTE_FLOW_ITEM_TYPE_UDP:
+			layer.udp = 1;
+			break;
+		case RTE_FLOW_ITEM_TYPE_TCP:
+			layer.tcp = 1;
+			break;
+		case RTE_FLOW_ITEM_TYPE_VXLAN:
+			layer.vxlan = 1;
+			break;
+		case RTE_FLOW_ITEM_TYPE_GENEVE:
+			layer.geneve = 1;
+			break;
+		case RTE_FLOW_ITEM_TYPE_NVGRE:
+			layer.nvgre = 1;
+			break;
+		default:
+			break;
+		}
+	}
+	off += rte_flow_expand_rss_item(addr, (off < size) ? size - off : 0,
+					pat, end);
+	if (off <= size) {
+		rte_flow_expand_rss_update(buf, addr, priority);
+		addr = (void *)((uintptr_t)buf + off);
+	}
+	if ((types & ETH_RSS_IP) &&
+	    (!(layer.ipv4 || layer.ipv6 || layer.ipv6_ex))) {
+		++priority;
+		if (types & (ETH_RSS_IPV4 | ETH_RSS_FRAG_IPV4 |
+			     ETH_RSS_NONFRAG_IPV4_OTHER)) {
+			const struct rte_flow_item new[] = {
+				{ .type = RTE_FLOW_ITEM_TYPE_IPV4 },
+				{ .type = RTE_FLOW_ITEM_TYPE_END },
+			};
+			const enum rte_flow_item_type list[] = {
+				RTE_FLOW_ITEM_TYPE_ETH,
+				RTE_FLOW_ITEM_TYPE_IPV4,
+				RTE_FLOW_ITEM_TYPE_END,
+			};
+			int ret;
+
+			ret = rte_flow_expand_rss_is_supported(supported, list);
+			if (ret) {
+				off += rte_flow_expand_rss_item
+					(addr, (off <= size) ? size - off : 0,
+					 pat, new);
+				if (off <= size) {
+					rte_flow_expand_rss_update(buf, addr,
+								   priority);
+					addr = (void *)((uintptr_t)buf + off);
+				}
+			}
+		}
+		if (types & (ETH_RSS_IPV6 | ETH_RSS_FRAG_IPV6 |
+			     ETH_RSS_NONFRAG_IPV6_OTHER)) {
+			const struct rte_flow_item new[] = {
+				{ .type = RTE_FLOW_ITEM_TYPE_IPV6 },
+				{ .type = RTE_FLOW_ITEM_TYPE_END },
+			};
+			const enum rte_flow_item_type list[] = {
+				RTE_FLOW_ITEM_TYPE_ETH,
+				RTE_FLOW_ITEM_TYPE_IPV6,
+				RTE_FLOW_ITEM_TYPE_END,
+			};
+			int ret;
+
+			ret = rte_flow_expand_rss_is_supported(supported, list);
+			if (ret) {
+				off += rte_flow_expand_rss_item
+					(addr, (off <= size) ? size - off : 0,
+					 pat, new);
+				if (off <= size) {
+					rte_flow_expand_rss_update(buf, addr,
+								   priority);
+					addr = (void *)((uintptr_t)buf + off);
+				}
+			}
+		}
+		if (types & ETH_RSS_IPV6_EX) {
+			const struct rte_flow_item new[] = {
+				{ .type = RTE_FLOW_ITEM_TYPE_IPV6_EXT },
+				{ .type = RTE_FLOW_ITEM_TYPE_END },
+			};
+			const enum rte_flow_item_type list[] = {
+				RTE_FLOW_ITEM_TYPE_ETH,
+				RTE_FLOW_ITEM_TYPE_IPV6_EXT,
+				RTE_FLOW_ITEM_TYPE_END,
+			};
+			int ret;
+
+			ret = rte_flow_expand_rss_is_supported(supported, list);
+			if (ret) {
+				off += rte_flow_expand_rss_item
+					(addr, (off <= size) ? size - off : 0,
+					 pat, new);
+				if (off <= size) {
+					rte_flow_expand_rss_update(buf, addr,
+								   priority);
+					addr = (void *)((uintptr_t)buf + off);
+				}
+			}
+		}
+	}
+	if (types & (ETH_RSS_TCP | ETH_RSS_UDP)) {
+		++priority;
+		if ((types & ETH_RSS_NONFRAG_IPV4_UDP) &&
+		    !(layer.ipv6 || layer.ipv6_ex || layer.tcp || layer.udp)) {
+			const struct rte_flow_item new[] = {
+				{ .type = RTE_FLOW_ITEM_TYPE_IPV4 },
+				{ .type = RTE_FLOW_ITEM_TYPE_UDP },
+				{ .type = RTE_FLOW_ITEM_TYPE_END },
+			};
+			const enum rte_flow_item_type list[] = {
+				RTE_FLOW_ITEM_TYPE_ETH,
+				RTE_FLOW_ITEM_TYPE_IPV4,
+				RTE_FLOW_ITEM_TYPE_UDP,
+				RTE_FLOW_ITEM_TYPE_END,
+			};
+			int ret;
+
+			ret = rte_flow_expand_rss_is_supported(supported, list);
+			if (ret) {
+				off += rte_flow_expand_rss_item
+					(addr, (off <= size) ? size - off : 0,
+					 pat, layer.ipv4 ? &new[1] : new);
+				if (off <= size) {
+					rte_flow_expand_rss_update(buf, addr,
+								   priority);
+					addr = (void *)((uintptr_t)buf + off);
+				}
+			}
+		}
+		if ((types & ETH_RSS_NONFRAG_IPV4_TCP) &&
+		    !(layer.ipv6 || layer.ipv6_ex || layer.tcp || layer.udp)) {
+			const struct rte_flow_item new[] = {
+				{ .type = RTE_FLOW_ITEM_TYPE_IPV4 },
+				{ .type = RTE_FLOW_ITEM_TYPE_TCP },
+				{ .type = RTE_FLOW_ITEM_TYPE_END },
+			};
+			const enum rte_flow_item_type list[] = {
+				RTE_FLOW_ITEM_TYPE_ETH,
+				RTE_FLOW_ITEM_TYPE_IPV4,
+				RTE_FLOW_ITEM_TYPE_TCP,
+				RTE_FLOW_ITEM_TYPE_END,
+			};
+			int ret;
+
+			ret = rte_flow_expand_rss_is_supported(supported, list);
+			if (ret) {
+				off += rte_flow_expand_rss_item
+					(addr, (off <= size) ? size - off : 0,
+					 pat, layer.ipv4 ? &new[1] : new);
+				if (off <= size) {
+					rte_flow_expand_rss_update(buf, addr,
+								   priority);
+					addr = (void *)((uintptr_t)buf + off);
+				}
+			}
+		}
+		if ((types & ETH_RSS_NONFRAG_IPV6_UDP) &&
+		    !(layer.ipv4 || layer.tcp || layer.udp)) {
+			const struct rte_flow_item new[] = {
+				{ .type = RTE_FLOW_ITEM_TYPE_IPV6 },
+				{ .type = RTE_FLOW_ITEM_TYPE_UDP },
+				{ .type = RTE_FLOW_ITEM_TYPE_END },
+			};
+			const enum rte_flow_item_type list[] = {
+				RTE_FLOW_ITEM_TYPE_ETH,
+				RTE_FLOW_ITEM_TYPE_IPV6,
+				RTE_FLOW_ITEM_TYPE_UDP,
+				RTE_FLOW_ITEM_TYPE_END,
+			};
+			int ret;
+
+			ret = rte_flow_expand_rss_is_supported(supported, list);
+			if (ret) {
+				off += rte_flow_expand_rss_item
+					(addr, (off <= size) ? size - off : 0,
+					 pat, layer.ipv6 ? &new[1] : new);
+				if (off <= size) {
+					rte_flow_expand_rss_update(buf, addr,
+								   priority);
+					addr = (void *)((uintptr_t)buf + off);
+				}
+			}
+		}
+		if ((types & (ETH_RSS_NONFRAG_IPV6_TCP |
+			      ETH_RSS_IPV6_TCP_EX)) &&
+		    !(layer.ipv4 || layer.tcp || layer.udp)) {
+			const struct rte_flow_item new[] = {
+				{ .type = RTE_FLOW_ITEM_TYPE_IPV6 },
+				{ .type = RTE_FLOW_ITEM_TYPE_TCP },
+				{ .type = RTE_FLOW_ITEM_TYPE_END },
+			};
+			const enum rte_flow_item_type list[] = {
+				RTE_FLOW_ITEM_TYPE_ETH,
+				(layer.ipv6_ex ?
+				 RTE_FLOW_ITEM_TYPE_IPV6_EXT :
+				 RTE_FLOW_ITEM_TYPE_IPV6),
+				RTE_FLOW_ITEM_TYPE_UDP,
+				RTE_FLOW_ITEM_TYPE_END,
+			};
+			int ret;
+
+			ret = rte_flow_expand_rss_is_supported(supported, list);
+			if (ret) {
+				off += rte_flow_expand_rss_item
+					(addr, (off <= size) ? size - off : 0,
+					 pat, layer.ipv6 ? &new[1] : new);
+				if (off <= size) {
+					rte_flow_expand_rss_update(buf, addr,
+								   priority);
+					addr = (void *)((uintptr_t)buf + off);
+				}
+			}
+		}
+	}
+	return off;
+}
diff --git a/lib/librte_ethdev/rte_flow_driver.h b/lib/librte_ethdev/rte_flow_driver.h
index 1c90c600d..9058a8715 100644
--- a/lib/librte_ethdev/rte_flow_driver.h
+++ b/lib/librte_ethdev/rte_flow_driver.h
@@ -114,6 +114,38 @@ struct rte_flow_ops {
 const struct rte_flow_ops *
 rte_flow_ops_get(uint16_t port_id, struct rte_flow_error *error);
 
+/**
+ * Expansion structure for RSS flows.
+ */
+struct rte_flow_expand_rss {
+	uint32_t entries; /**< Number of entries in the following arrays. */
+	struct rte_flow_item **patterns; /**< Expanded pattern array. */
+	uint32_t *priority; /**< Priority offset for each expansion. */
+};
+
+/**
+ * Expand RSS flows into several possible flows according to the RSS hash
+ * fields requested and the driver capabilities.
+ *
+ * @param[in,out] buf
+ *   Buffer to store the result expansion.
+ * @param[in] size
+ *   Size in octets of the buffer.
+ * @param[in] pat
+ *   User flow pattern.
+ * @param[in] types
+ *   RSS types expected (see ETH_RSS_*).
+ * @param[in] supported.
+ *   List of support expansion pattern from the device.
+ *
+ * @return
+ *   The size in octets used to expand.
+ */
+int
+rte_flow_expand_rss(struct rte_flow_expand_rss *buf, size_t size,
+		    const struct rte_flow_item *pat, uint64_t types,
+		    const enum rte_flow_item_type **supported);
+
 #ifdef __cplusplus
 }
 #endif
-- 
2.17.1

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [dpdk-dev] [PATCH v3] ethdev: add flow API to expand RSS flows
  2018-06-05  8:26 ` [dpdk-dev] [PATCH v2] " Nelio Laranjeiro
@ 2018-06-21  7:25   ` Nelio Laranjeiro
  2018-06-27 14:26     ` Thomas Monjalon
  2018-06-27 14:55     ` [dpdk-dev] [PATCH v4] " Nelio Laranjeiro
  0 siblings, 2 replies; 11+ messages in thread
From: Nelio Laranjeiro @ 2018-06-21  7:25 UTC (permalink / raw)
  To: dev, Adrien Mazarguil

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 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        | 408 ++++++++++++++++++++++++++++
 lib/librte_ethdev/rte_flow_driver.h |  32 +++
 2 files changed, 440 insertions(+)

diff --git a/lib/librte_ethdev/rte_flow.c b/lib/librte_ethdev/rte_flow.c
index b2afba089..9d86c20cb 100644
--- a/lib/librte_ethdev/rte_flow.c
+++ b/lib/librte_ethdev/rte_flow.c
@@ -526,3 +526,411 @@ rte_flow_copy(struct rte_flow_desc *desc, size_t len,
 	}
 	return 0;
 }
+
+/* Copy the existing items list and expand with new items. */
+static int
+rte_flow_expand_rss_item(void *buf, size_t size,
+			 const struct rte_flow_item *items,
+			 const struct rte_flow_item *newitems)
+{
+	void *data = buf;
+	const struct rte_flow_item *item;
+	struct rte_flow_item *dst;
+	size_t data_size = 0;
+
+	dst = data;
+	/* Copy Item structure into buffer. */
+	for (item = items; item->type != RTE_FLOW_ITEM_TYPE_END; ++item) {
+		if (item->type == RTE_FLOW_ITEM_TYPE_VOID)
+			continue;
+		if (data_size + sizeof(*item) <= size) {
+			memcpy(dst, item, sizeof(*item));
+			++dst;
+		}
+		data_size += sizeof(*item);
+	}
+	item = newitems;
+	do {
+		if (item->type == RTE_FLOW_ITEM_TYPE_VOID) {
+			++item;
+			continue;
+		}
+		if (data_size + sizeof(*item) <= size) {
+			memcpy(dst, item, sizeof(*item));
+			++dst;
+		}
+		data_size += sizeof(*item);
+		++item;
+	} while ((item - 1)->type != RTE_FLOW_ITEM_TYPE_END);
+	/**
+	 * Copy Item spec, last, mask into buffer and set pointers
+	 * accordingly.
+	 */
+	dst = data;
+	for (item = items; item->type != RTE_FLOW_ITEM_TYPE_END; ++item) {
+		if (item->type == RTE_FLOW_ITEM_TYPE_VOID)
+			continue;
+		if (item->spec) {
+			size_t s = flow_item_spec_copy(NULL, item, ITEM_SPEC);
+			void *addr = (data_size + s) <= size ?
+				(void *)((uintptr_t)data + data_size) :
+				NULL;
+
+			data_size += flow_item_spec_copy(addr, item, ITEM_SPEC);
+			if (addr)
+				dst->spec = addr;
+		}
+		if (item->last) {
+			size_t s = flow_item_spec_copy(NULL, item, ITEM_LAST);
+			void *addr = (data_size + s) <= size ?
+				(void *)((uintptr_t)data + data_size) :
+				NULL;
+
+			data_size += flow_item_spec_copy(addr, item, ITEM_LAST);
+			if (addr)
+				dst->last = addr;
+		}
+		if (item->mask) {
+			size_t s = flow_item_spec_copy(NULL, item, ITEM_MASK);
+			void *addr = (data_size + s) <= size ?
+				(void *)((uintptr_t)data + data_size) :
+				NULL;
+
+			data_size += flow_item_spec_copy(addr, item, ITEM_MASK);
+			if (addr)
+				dst->mask = addr;
+		}
+		if (data_size <= size)
+			++dst;
+	}
+	return data_size;
+}
+
+/** Verify the expansion is supported by the device. */
+static int
+rte_flow_expand_rss_is_supported(const enum rte_flow_item_type **supported,
+				 const enum rte_flow_item_type *expand)
+{
+	unsigned int i;
+	unsigned int sidx;
+	unsigned int eidx;
+
+	for (i = 0; supported[i]; ++i) {
+		sidx = 0;
+		eidx = 0;
+		while (1) {
+			if (expand[eidx] != supported[i][sidx]) {
+				break;
+			} else if ((expand[eidx] == RTE_FLOW_ITEM_TYPE_END) &&
+				   (supported[i][sidx] ==
+				    RTE_FLOW_ITEM_TYPE_END)) {
+				return 1;
+			} else if ((expand[eidx] == RTE_FLOW_ITEM_TYPE_END) ||
+				   (supported[i][sidx] ==
+				    RTE_FLOW_ITEM_TYPE_END)) {
+				break;
+			} else if (expand[eidx] == RTE_FLOW_ITEM_TYPE_VOID) {
+				++eidx;
+				continue;
+			} else if (supported[i][sidx] ==
+				   RTE_FLOW_ITEM_TYPE_VOID) {
+				++sidx;
+				continue;
+			}
+			++sidx;
+			++eidx;
+		}
+	}
+	return 0;
+}
+
+/** Update internal buffer. */
+static inline void
+rte_flow_expand_rss_update(struct rte_flow_expand_rss *buf, void *addr,
+			   uint32_t priority)
+{
+	buf->priority[buf->entries] = priority;
+	buf->patterns[buf->entries] = addr;
+	buf->entries++;
+}
+
+int
+rte_flow_expand_rss(struct rte_flow_expand_rss *buf, size_t size,
+		    const struct rte_flow_item *pat, uint64_t types,
+		    const enum rte_flow_item_type **supported)
+{
+	const struct rte_flow_item *item;
+	uint32_t priority = 0;
+	struct {
+		uint32_t eth:1; /**< Ethernet item is  present. */
+		uint32_t ipv4:1; /**< IPv4 item is  present. */
+		uint32_t ipv6:1; /**< IPv6 item is  present. */
+		uint32_t ipv6_ex:1; /**< IPv6 EXT item is  present. */
+		uint32_t udp:1; /**< UDP item is  present. */
+		uint32_t tcp:1; /**< TCP item is  present. */
+		uint32_t sctp:1; /**< STCP item is  present. */
+		uint32_t vxlan:1; /**< VXLAN item is  present. */
+		uint32_t geneve:1; /**< GENEVE item is  present. */
+		uint32_t nvgre:1; /**< NVGRE item is  present. */
+	} layer = { .eth = 0 };
+	const struct rte_flow_item end[] = {
+		{ .type = RTE_FLOW_ITEM_TYPE_END },
+	};
+	void *addr = NULL;
+	uint32_t off; /**< Offset to write new items data starting from *buf. */
+	uint32_t max_entries;
+
+	for (max_entries = 0; supported[max_entries]; ++max_entries)
+		;
+	off = sizeof(*buf) +
+		/* Size for the list of patterns. */
+		sizeof(*buf->patterns) +
+		RTE_ALIGN_CEIL(max_entries * sizeof(struct rte_flow_item *),
+			       sizeof(void *)) +
+		/* Size for priorities. */
+		sizeof(*buf->priority) +
+		RTE_ALIGN_CEIL(max_entries * sizeof(uint32_t), sizeof(void *));
+	if (off < size) {
+		buf->priority = (void *)(buf + 1);
+		buf->patterns = (void *)&buf->priority[max_entries];
+		buf->patterns[0] = (void *)&buf->patterns[max_entries];
+		addr = buf->patterns[0];
+		buf->entries = 0;
+	}
+	/**
+	 * Parse the pattern and deactivate the bit-field in RSS which cannot
+	 * match anymore the pattern.
+	 */
+	for (item = pat; item->type != RTE_FLOW_ITEM_TYPE_END; ++item) {
+		switch (item->type) {
+		case RTE_FLOW_ITEM_TYPE_ETH:
+			layer.eth = 1;
+			break;
+		case RTE_FLOW_ITEM_TYPE_IPV4:
+			layer.ipv4 = 1;
+			break;
+		case RTE_FLOW_ITEM_TYPE_IPV6:
+			layer.ipv6 = 1;
+			break;
+		case RTE_FLOW_ITEM_TYPE_IPV6_EXT:
+			layer.ipv6_ex = 1;
+			break;
+		case RTE_FLOW_ITEM_TYPE_UDP:
+			layer.udp = 1;
+			break;
+		case RTE_FLOW_ITEM_TYPE_TCP:
+			layer.tcp = 1;
+			break;
+		case RTE_FLOW_ITEM_TYPE_VXLAN:
+			layer.vxlan = 1;
+			break;
+		case RTE_FLOW_ITEM_TYPE_GENEVE:
+			layer.geneve = 1;
+			break;
+		case RTE_FLOW_ITEM_TYPE_NVGRE:
+			layer.nvgre = 1;
+			break;
+		default:
+			break;
+		}
+	}
+	off += rte_flow_expand_rss_item(addr, (off < size) ? size - off : 0,
+					pat, end);
+	if (off <= size) {
+		rte_flow_expand_rss_update(buf, addr, priority);
+		addr = (void *)((uintptr_t)buf + off);
+	}
+	if ((types & ETH_RSS_IP) &&
+	    (!(layer.ipv4 || layer.ipv6 || layer.ipv6_ex))) {
+		++priority;
+		if (types & (ETH_RSS_IPV4 | ETH_RSS_FRAG_IPV4 |
+			     ETH_RSS_NONFRAG_IPV4_OTHER)) {
+			const struct rte_flow_item new[] = {
+				{ .type = RTE_FLOW_ITEM_TYPE_IPV4 },
+				{ .type = RTE_FLOW_ITEM_TYPE_END },
+			};
+			const enum rte_flow_item_type list[] = {
+				RTE_FLOW_ITEM_TYPE_ETH,
+				RTE_FLOW_ITEM_TYPE_IPV4,
+				RTE_FLOW_ITEM_TYPE_END,
+			};
+			int ret;
+
+			ret = rte_flow_expand_rss_is_supported(supported, list);
+			if (ret) {
+				off += rte_flow_expand_rss_item
+					(addr, (off <= size) ? size - off : 0,
+					 pat, new);
+				if (off <= size) {
+					rte_flow_expand_rss_update(buf, addr,
+								   priority);
+					addr = (void *)((uintptr_t)buf + off);
+				}
+			}
+		}
+		if (types & (ETH_RSS_IPV6 | ETH_RSS_FRAG_IPV6 |
+			     ETH_RSS_NONFRAG_IPV6_OTHER)) {
+			const struct rte_flow_item new[] = {
+				{ .type = RTE_FLOW_ITEM_TYPE_IPV6 },
+				{ .type = RTE_FLOW_ITEM_TYPE_END },
+			};
+			const enum rte_flow_item_type list[] = {
+				RTE_FLOW_ITEM_TYPE_ETH,
+				RTE_FLOW_ITEM_TYPE_IPV6,
+				RTE_FLOW_ITEM_TYPE_END,
+			};
+			int ret;
+
+			ret = rte_flow_expand_rss_is_supported(supported, list);
+			if (ret) {
+				off += rte_flow_expand_rss_item
+					(addr, (off <= size) ? size - off : 0,
+					 pat, new);
+				if (off <= size) {
+					rte_flow_expand_rss_update(buf, addr,
+								   priority);
+					addr = (void *)((uintptr_t)buf + off);
+				}
+			}
+		}
+		if (types & ETH_RSS_IPV6_EX) {
+			const struct rte_flow_item new[] = {
+				{ .type = RTE_FLOW_ITEM_TYPE_IPV6_EXT },
+				{ .type = RTE_FLOW_ITEM_TYPE_END },
+			};
+			const enum rte_flow_item_type list[] = {
+				RTE_FLOW_ITEM_TYPE_ETH,
+				RTE_FLOW_ITEM_TYPE_IPV6_EXT,
+				RTE_FLOW_ITEM_TYPE_END,
+			};
+			int ret;
+
+			ret = rte_flow_expand_rss_is_supported(supported, list);
+			if (ret) {
+				off += rte_flow_expand_rss_item
+					(addr, (off <= size) ? size - off : 0,
+					 pat, new);
+				if (off <= size) {
+					rte_flow_expand_rss_update(buf, addr,
+								   priority);
+					addr = (void *)((uintptr_t)buf + off);
+				}
+			}
+		}
+	}
+	if (types & (ETH_RSS_TCP | ETH_RSS_UDP)) {
+		++priority;
+		if ((types & ETH_RSS_NONFRAG_IPV4_UDP) &&
+		    !(layer.ipv6 || layer.ipv6_ex || layer.tcp || layer.udp)) {
+			const struct rte_flow_item new[] = {
+				{ .type = RTE_FLOW_ITEM_TYPE_IPV4 },
+				{ .type = RTE_FLOW_ITEM_TYPE_UDP },
+				{ .type = RTE_FLOW_ITEM_TYPE_END },
+			};
+			const enum rte_flow_item_type list[] = {
+				RTE_FLOW_ITEM_TYPE_ETH,
+				RTE_FLOW_ITEM_TYPE_IPV4,
+				RTE_FLOW_ITEM_TYPE_UDP,
+				RTE_FLOW_ITEM_TYPE_END,
+			};
+			int ret;
+
+			ret = rte_flow_expand_rss_is_supported(supported, list);
+			if (ret) {
+				off += rte_flow_expand_rss_item
+					(addr, (off <= size) ? size - off : 0,
+					 pat, layer.ipv4 ? &new[1] : new);
+				if (off <= size) {
+					rte_flow_expand_rss_update(buf, addr,
+								   priority);
+					addr = (void *)((uintptr_t)buf + off);
+				}
+			}
+		}
+		if ((types & ETH_RSS_NONFRAG_IPV4_TCP) &&
+		    !(layer.ipv6 || layer.ipv6_ex || layer.tcp || layer.udp)) {
+			const struct rte_flow_item new[] = {
+				{ .type = RTE_FLOW_ITEM_TYPE_IPV4 },
+				{ .type = RTE_FLOW_ITEM_TYPE_TCP },
+				{ .type = RTE_FLOW_ITEM_TYPE_END },
+			};
+			const enum rte_flow_item_type list[] = {
+				RTE_FLOW_ITEM_TYPE_ETH,
+				RTE_FLOW_ITEM_TYPE_IPV4,
+				RTE_FLOW_ITEM_TYPE_TCP,
+				RTE_FLOW_ITEM_TYPE_END,
+			};
+			int ret;
+
+			ret = rte_flow_expand_rss_is_supported(supported, list);
+			if (ret) {
+				off += rte_flow_expand_rss_item
+					(addr, (off <= size) ? size - off : 0,
+					 pat, layer.ipv4 ? &new[1] : new);
+				if (off <= size) {
+					rte_flow_expand_rss_update(buf, addr,
+								   priority);
+					addr = (void *)((uintptr_t)buf + off);
+				}
+			}
+		}
+		if ((types & ETH_RSS_NONFRAG_IPV6_UDP) &&
+		    !(layer.ipv4 || layer.tcp || layer.udp)) {
+			const struct rte_flow_item new[] = {
+				{ .type = RTE_FLOW_ITEM_TYPE_IPV6 },
+				{ .type = RTE_FLOW_ITEM_TYPE_UDP },
+				{ .type = RTE_FLOW_ITEM_TYPE_END },
+			};
+			const enum rte_flow_item_type list[] = {
+				RTE_FLOW_ITEM_TYPE_ETH,
+				RTE_FLOW_ITEM_TYPE_IPV6,
+				RTE_FLOW_ITEM_TYPE_UDP,
+				RTE_FLOW_ITEM_TYPE_END,
+			};
+			int ret;
+
+			ret = rte_flow_expand_rss_is_supported(supported, list);
+			if (ret) {
+				off += rte_flow_expand_rss_item
+					(addr, (off <= size) ? size - off : 0,
+					 pat, layer.ipv6 ? &new[1] : new);
+				if (off <= size) {
+					rte_flow_expand_rss_update(buf, addr,
+								   priority);
+					addr = (void *)((uintptr_t)buf + off);
+				}
+			}
+		}
+		if ((types & (ETH_RSS_NONFRAG_IPV6_TCP |
+			      ETH_RSS_IPV6_TCP_EX)) &&
+		    !(layer.ipv4 || layer.tcp || layer.udp)) {
+			const struct rte_flow_item new[] = {
+				{ .type = RTE_FLOW_ITEM_TYPE_IPV6 },
+				{ .type = RTE_FLOW_ITEM_TYPE_TCP },
+				{ .type = RTE_FLOW_ITEM_TYPE_END },
+			};
+			const enum rte_flow_item_type list[] = {
+				RTE_FLOW_ITEM_TYPE_ETH,
+				(layer.ipv6_ex ?
+				 RTE_FLOW_ITEM_TYPE_IPV6_EXT :
+				 RTE_FLOW_ITEM_TYPE_IPV6),
+				RTE_FLOW_ITEM_TYPE_UDP,
+				RTE_FLOW_ITEM_TYPE_END,
+			};
+			int ret;
+
+			ret = rte_flow_expand_rss_is_supported(supported, list);
+			if (ret) {
+				off += rte_flow_expand_rss_item
+					(addr, (off <= size) ? size - off : 0,
+					 pat, layer.ipv6 ? &new[1] : new);
+				if (off <= size) {
+					rte_flow_expand_rss_update(buf, addr,
+								   priority);
+					addr = (void *)((uintptr_t)buf + off);
+				}
+			}
+		}
+	}
+	return off;
+}
diff --git a/lib/librte_ethdev/rte_flow_driver.h b/lib/librte_ethdev/rte_flow_driver.h
index 1c90c600d..9058a8715 100644
--- a/lib/librte_ethdev/rte_flow_driver.h
+++ b/lib/librte_ethdev/rte_flow_driver.h
@@ -114,6 +114,38 @@ struct rte_flow_ops {
 const struct rte_flow_ops *
 rte_flow_ops_get(uint16_t port_id, struct rte_flow_error *error);
 
+/**
+ * Expansion structure for RSS flows.
+ */
+struct rte_flow_expand_rss {
+	uint32_t entries; /**< Number of entries in the following arrays. */
+	struct rte_flow_item **patterns; /**< Expanded pattern array. */
+	uint32_t *priority; /**< Priority offset for each expansion. */
+};
+
+/**
+ * Expand RSS flows into several possible flows according to the RSS hash
+ * fields requested and the driver capabilities.
+ *
+ * @param[in,out] buf
+ *   Buffer to store the result expansion.
+ * @param[in] size
+ *   Size in octets of the buffer.
+ * @param[in] pat
+ *   User flow pattern.
+ * @param[in] types
+ *   RSS types expected (see ETH_RSS_*).
+ * @param[in] supported.
+ *   List of support expansion pattern from the device.
+ *
+ * @return
+ *   The size in octets used to expand.
+ */
+int
+rte_flow_expand_rss(struct rte_flow_expand_rss *buf, size_t size,
+		    const struct rte_flow_item *pat, uint64_t types,
+		    const enum rte_flow_item_type **supported);
+
 #ifdef __cplusplus
 }
 #endif
-- 
2.18.0.rc2

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [dpdk-dev] [PATCH v3] ethdev: add flow API to expand RSS flows
  2018-06-21  7:25   ` [dpdk-dev] [PATCH v3] " Nelio Laranjeiro
@ 2018-06-27 14:26     ` Thomas Monjalon
  2018-06-27 14:55     ` [dpdk-dev] [PATCH v4] " Nelio Laranjeiro
  1 sibling, 0 replies; 11+ messages in thread
From: Thomas Monjalon @ 2018-06-27 14:26 UTC (permalink / raw)
  To: Adrien Mazarguil; +Cc: dev, Nelio Laranjeiro

Adrien, would you like to review this patch please?

21/06/2018 09:25, Nelio Laranjeiro:
> 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>

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [dpdk-dev] [PATCH v4] ethdev: add flow API to expand RSS flows
  2018-06-21  7:25   ` [dpdk-dev] [PATCH v3] " Nelio Laranjeiro
  2018-06-27 14:26     ` Thomas Monjalon
@ 2018-06-27 14:55     ` Nelio Laranjeiro
  2018-06-28 12:33       ` Adrien Mazarguil
  2018-06-28 16:01       ` [dpdk-dev] [PATCH v5] " Nelio Laranjeiro
  1 sibling, 2 replies; 11+ messages in thread
From: Nelio Laranjeiro @ 2018-06-27 14:55 UTC (permalink / raw)
  To: dev, Adrien Mazarguil

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 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        | 105 ++++++++++++++++++++++++++++
 lib/librte_ethdev/rte_flow_driver.h |  48 +++++++++++++
 2 files changed, 153 insertions(+)

diff --git a/lib/librte_ethdev/rte_flow.c b/lib/librte_ethdev/rte_flow.c
index 2e87e59f3..96af18e16 100644
--- a/lib/librte_ethdev/rte_flow.c
+++ b/lib/librte_ethdev/rte_flow.c
@@ -526,3 +526,108 @@ rte_flow_copy(struct rte_flow_desc *desc, size_t len,
 	}
 	return 0;
 }
+
+int
+rte_flow_expand_rss(struct rte_flow_expand_rss *buf, size_t size,
+		    const struct rte_flow_item *pat, uint64_t types,
+		    const struct rte_flow_expand_node nodes[],
+		    const int node_entry_index)
+{
+	const int elt_n = 8;
+	const struct rte_flow_item *item;
+	const struct rte_flow_expand_node *node = &nodes[node_entry_index];
+	const int *next_node;
+	const int *stack[elt_n];
+	int stack_n = 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 = sizeof(*buf) +
+		/* Size for the list of patterns. */
+		sizeof(*buf->patterns) +
+		RTE_ALIGN_CEIL(elt_n * sizeof(*item), sizeof(void *)) +
+		/* Size for priorities. */
+		sizeof(*buf->priority) +
+		RTE_ALIGN_CEIL(elt_n * sizeof(uint32_t), sizeof(void *));
+	if (lsize <= size) {
+		buf->priority = (void *)(buf + 1);
+		buf->priority[0] = 0;
+		buf->patterns = (void *)&buf->priority[elt_n];
+		buf->patterns[0] = (void *)&buf->patterns[elt_n];
+		buf->entries = 0;
+		addr = buf->patterns[0];
+	}
+	for (item = pat; 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 = &nodes[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, pat, user_pattern_size);
+		addr = (void *)(((uintptr_t)addr) + user_pattern_size);
+		buf->priority[buf->entries] = 0;
+		buf->entries = 1;
+	}
+	/* Start expanding. */
+	memset(flow_items, 0, sizeof(flow_items));
+	user_pattern_size -= sizeof(*item);
+	next_node = node->next;
+	stack[stack_n] = next_node;
+	node = next_node ? &nodes[*next_node] : NULL;
+	while (node) {
+		flow_items[stack_n].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_n is 0, there are 1 element in it,
+			 * plus the addition END item.
+			 */
+			int elt = stack_n + 2;
+
+			flow_items[stack_n + 1].type = RTE_FLOW_ITEM_TYPE_END;
+			lsize += elt * sizeof(*item) + user_pattern_size;
+			if (lsize <= size) {
+				size_t n = elt * sizeof(*item);
+
+				buf->priority[buf->entries] = stack_n + 1;
+				buf->patterns[buf->entries++] = addr;
+				rte_memcpy(addr, buf->patterns[0],
+					   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;
+			stack[++stack_n] = next_node;
+		} else if (*(next_node + 1)) {
+			/* Follow up with the next possibility. */
+			++next_node;
+		} else {
+			/* Move to the next path. */
+			if (stack_n)
+				next_node = stack[--stack_n];
+			next_node++;
+			stack[stack_n] = next_node;
+		}
+		node = *next_node ? &nodes[*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..538b46e54 100644
--- a/lib/librte_ethdev/rte_flow_driver.h
+++ b/lib/librte_ethdev/rte_flow_driver.h
@@ -114,6 +114,54 @@ struct rte_flow_ops {
 const struct rte_flow_ops *
 rte_flow_ops_get(uint16_t port_id, struct rte_flow_error *error);
 
+/** Macro to create the expansion graph. */
+#define RTE_FLOW_EXPAND_ITEMS(...) \
+	(const int []){ \
+		__VA_ARGS__, 0, \
+	}
+
+/** structure to generate the expansion graph. */
+struct rte_flow_expand_node {
+	const int *const next; /**< list of items finalised by 0. */
+	const enum rte_flow_item_type type; /**< Item type to add. */
+	uint64_t rss_types; /**< RSS bit-field value. */
+};
+
+/**
+ * Expansion structure for RSS flows.
+ */
+struct rte_flow_expand_rss {
+	uint32_t entries; /**< Number of entries in the following arrays. */
+	struct rte_flow_item **patterns; /**< Expanded pattern array. */
+	uint32_t *priority; /**< Priority offset for each expansion. */
+};
+
+/**
+ * Expand RSS flows into several possible flows according to the RSS hash
+ * fields requested and the driver capabilities.
+ *
+ * @param[in,out] buf
+ *   Buffer to store the result expansion.
+ * @param[in] size
+ *   Size in octets of the buffer.
+ * @param[in] pat
+ *   User flow pattern.
+ * @param[in] types
+ *   RSS types expected (see ETH_RSS_*).
+ * @param[in] nodes.
+ *   Expansion graph of possibilities for the RSS.
+ * @param[in] node_entry_index
+ *   The index in the \p nodes array as start point.
+ *
+ * @return
+ *   The size in octets used to expand.
+ */
+int
+rte_flow_expand_rss(struct rte_flow_expand_rss *buf, size_t size,
+		    const struct rte_flow_item *pat, uint64_t types,
+		    const struct rte_flow_expand_node nodes[],
+		    const int node_entry_index);
+
 #ifdef __cplusplus
 }
 #endif
-- 
2.18.0

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [dpdk-dev] [PATCH v4] ethdev: add flow API to expand RSS flows
  2018-06-27 14:55     ` [dpdk-dev] [PATCH v4] " Nelio Laranjeiro
@ 2018-06-28 12:33       ` Adrien Mazarguil
  2018-06-28 16:01       ` [dpdk-dev] [PATCH v5] " Nelio Laranjeiro
  1 sibling, 0 replies; 11+ messages in thread
From: Adrien Mazarguil @ 2018-06-28 12:33 UTC (permalink / raw)
  To: Nelio Laranjeiro; +Cc: dev

On Wed, Jun 27, 2018 at 04:55:25PM +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 v4:
> 
> - Replace the expanded algorithm with a graph to support also tunnel
> pattern matching.

Looks like a useful helper, nice to see that it's much shorter than the
previous versions. A few nits below, mainly suggestions for documentation.

<snip>
> +
> +int
> +rte_flow_expand_rss(struct rte_flow_expand_rss *buf, size_t size,
> +		    const struct rte_flow_item *pat, uint64_t types,
> +		    const struct rte_flow_expand_node nodes[],
> +		    const int node_entry_index)

Please add short documentation reminder on top of this definition (i.e. a
copy of the first sentence from rte_flow_driver.h).

> +{
> +	const int elt_n = 8;
> +	const struct rte_flow_item *item;
> +	const struct rte_flow_expand_node *node = &nodes[node_entry_index];
> +	const int *next_node;
> +	const int *stack[elt_n];
> +	int stack_n = 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 = sizeof(*buf) +
> +		/* Size for the list of patterns. */
> +		sizeof(*buf->patterns) +
> +		RTE_ALIGN_CEIL(elt_n * sizeof(*item), sizeof(void *)) +
> +		/* Size for priorities. */
> +		sizeof(*buf->priority) +
> +		RTE_ALIGN_CEIL(elt_n * sizeof(uint32_t), sizeof(void *));
> +	if (lsize <= size) {
> +		buf->priority = (void *)(buf + 1);
> +		buf->priority[0] = 0;
> +		buf->patterns = (void *)&buf->priority[elt_n];
> +		buf->patterns[0] = (void *)&buf->patterns[elt_n];
> +		buf->entries = 0;
> +		addr = buf->patterns[0];
> +	}
> +	for (item = pat; 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 = &nodes[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. */

This comment shouldn't be in Doxygen format.

> +	lsize += user_pattern_size;
> +	/* Copy the user pattern in the first entry of the buffer. */
> +	if (lsize <= size) {
> +		rte_memcpy(addr, pat, user_pattern_size);
> +		addr = (void *)(((uintptr_t)addr) + user_pattern_size);
> +		buf->priority[buf->entries] = 0;
> +		buf->entries = 1;
> +	}
> +	/* Start expanding. */
> +	memset(flow_items, 0, sizeof(flow_items));
> +	user_pattern_size -= sizeof(*item);
> +	next_node = node->next;
> +	stack[stack_n] = next_node;
> +	node = next_node ? &nodes[*next_node] : NULL;
> +	while (node) {
> +		flow_items[stack_n].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_n is 0, there are 1 element in it,
> +			 * plus the addition END item.
> +			 */
> +			int elt = stack_n + 2;
> +
> +			flow_items[stack_n + 1].type = RTE_FLOW_ITEM_TYPE_END;
> +			lsize += elt * sizeof(*item) + user_pattern_size;
> +			if (lsize <= size) {
> +				size_t n = elt * sizeof(*item);
> +
> +				buf->priority[buf->entries] = stack_n + 1;
> +				buf->patterns[buf->entries++] = addr;
> +				rte_memcpy(addr, buf->patterns[0],
> +					   user_pattern_size);
> +				addr = (void *)(((uintptr_t)addr) +
> +						user_pattern_size);
> +				rte_memcpy(addr, flow_items, n);
> +				addr = (void *) (((uintptr_t)addr) + n);

Extra space after (void *).

> +			}
> +		}
> +		/* Go deeper. */
> +		if (node->next) {
> +			next_node = node->next;
> +			stack[++stack_n] = next_node;

Since stack[] contains at most elt_n (8) elements, even assuming it's always
plenty enough, I think it would be wise to check for any overflow before
incrementing stack_n and return an error if so.

> +		} else if (*(next_node + 1)) {
> +			/* Follow up with the next possibility. */
> +			++next_node;
> +		} else {
> +			/* Move to the next path. */
> +			if (stack_n)
> +				next_node = stack[--stack_n];
> +			next_node++;
> +			stack[stack_n] = next_node;
> +		}
> +		node = *next_node ? &nodes[*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..538b46e54 100644
> --- a/lib/librte_ethdev/rte_flow_driver.h
> +++ b/lib/librte_ethdev/rte_flow_driver.h
> @@ -114,6 +114,54 @@ struct rte_flow_ops {
>  const struct rte_flow_ops *
>  rte_flow_ops_get(uint16_t port_id, struct rte_flow_error *error);
>  
> +/** Macro to create the expansion graph. */

=> Helper macro to build input graph for rte_flow_expand_rss().

Mostly rewording suggestions from here on.

> +#define RTE_FLOW_EXPAND_ITEMS(...) \

You should keep the same prefix for all objects associated with this
function. Everything should start with "rte_flow_expand_rss" for
consistency.

=> RTE_FLOW_EXPAND_RSS_NEXT(...)

> +	(const int []){ \
> +		__VA_ARGS__, 0, \
> +	}
> +
> +/** structure to generate the expansion graph. */

=> Node object of input graph for rte_flow_expand_rss().

> +struct rte_flow_expand_node {

=> struct rte_flow_expand_rss_node

> +	const int *const next; /**< list of items finalised by 0. */

=> List of next node indexes. Index 0 is interpreted as a terminator.

> +	const enum rte_flow_item_type type; /**< Item type to add. */

=> Pattern item type of current node.

> +	uint64_t rss_types; /**< RSS bit-field value. */

=> RSS types bit-field associated with this node (see ETH_RSS_* definitions).

> +};
> +
> +/**
> + * Expansion structure for RSS flows.
> + */

=> Object returned by rte_flow_expand_rss().

This block could fit a single line by the way.

> +struct rte_flow_expand_rss {
> +	uint32_t entries; /**< Number of entries in the following arrays. */

=> Number of entries in @p patterns and @p priorities.

> +	struct rte_flow_item **patterns; /**< Expanded pattern array. */
> +	uint32_t *priority; /**< Priority offset for each expansion. */

How about priority => priorities since there are as many of them as
patterns?

> +};

Another suggestion regarding this structure definition, since it's entirely
written by rte_flow_expand_rss() based on an arbitrarily-sized user-provided
buffer, and given it's not a public API, a flexible array member could make
sense.

This would highlight the link between patterns and priorities and make the
result more convenient to use thanks to fewer pointers:

 struct rte_flow_expand_rss {
     uint32_t entries;
     struct {
          struct rte_flow_item *pattern;
          uint32_t priority;
     }  entry[];
 };

> +
> +/**
> + * Expand RSS flows into several possible flows according to the RSS hash
> + * fields requested and the driver capabilities.
> + *
> + * @param[in,out] buf

Since this buffer is only written to: @param[in,out] => @param[out]

> + *   Buffer to store the result expansion.

=> Buffer for expansion result. May be truncated if @p size is not large
   enough.

> + * @param[in] size
> + *   Size in octets of the buffer.

=> Buffer size in bytes. If 0, @p buf can be NULL.

> + * @param[in] pat

pat => pattern

> + *   User flow pattern.
> + * @param[in] types
> + *   RSS types expected (see ETH_RSS_*).

=> RSS types to expand (see ETH_RSS_* definitions).

> + * @param[in] nodes.

nodes => graph

> + *   Expansion graph of possibilities for the RSS.

=> Input graph to expand @p pattern according to @p types.

> + * @param[in] node_entry_index

"[in]" is unnecessary since it's not a pointer.

node_entry_index => graph_root_index

> + *   The index in the \p nodes array as start point.

Let's keep "@" instead of "\" for directives.

=> Index of root node in @p graph, typically 0.

> + *
> + * @return
> + *   The size in octets used to expand.

=> 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.

> + */
> +int
> +rte_flow_expand_rss(struct rte_flow_expand_rss *buf, size_t size,
> +		    const struct rte_flow_item *pat, uint64_t types,

pat => pattern

> +		    const struct rte_flow_expand_node nodes[],
> +		    const int node_entry_index);

No need for node_entry_index to be const.

> +
>  #ifdef __cplusplus
>  }
>  #endif
> -- 
> 2.18.0
> 

-- 
Adrien Mazarguil
6WIND

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [dpdk-dev] [PATCH v5] ethdev: add flow API to expand RSS flows
  2018-06-27 14:55     ` [dpdk-dev] [PATCH v4] " Nelio Laranjeiro
  2018-06-28 12:33       ` Adrien Mazarguil
@ 2018-06-28 16:01       ` Nelio Laranjeiro
  2018-06-28 16:09         ` Adrien Mazarguil
  1 sibling, 1 reply; 11+ messages in thread
From: Nelio Laranjeiro @ 2018-06-28 16:01 UTC (permalink / raw)
  To: dev, Adrien Mazarguil

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(+)

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
-- 
2.18.0

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [dpdk-dev] [PATCH v5] ethdev: add flow API to expand RSS flows
  2018-06-28 16:01       ` [dpdk-dev] [PATCH v5] " Nelio Laranjeiro
@ 2018-06-28 16:09         ` Adrien Mazarguil
  2018-06-29 16:23           ` Ferruh Yigit
  2018-06-29 16:26           ` Ferruh Yigit
  0 siblings, 2 replies; 11+ messages in thread
From: Adrien Mazarguil @ 2018-06-28 16:09 UTC (permalink / raw)
  To: Nelio Laranjeiro; +Cc: dev

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 "."

-- 
Adrien Mazarguil
6WIND

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [dpdk-dev] [PATCH v5] ethdev: add flow API to expand RSS flows
  2018-06-28 16:09         ` Adrien Mazarguil
@ 2018-06-29 16:23           ` Ferruh Yigit
  2018-06-29 16:26           ` Ferruh Yigit
  1 sibling, 0 replies; 11+ messages in thread
From: Ferruh Yigit @ 2018-06-29 16:23 UTC (permalink / raw)
  To: Adrien Mazarguil, Nelio Laranjeiro; +Cc: dev

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.

> 

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [dpdk-dev] [PATCH v5] ethdev: add flow API to expand RSS flows
  2018-06-28 16:09         ` Adrien Mazarguil
  2018-06-29 16:23           ` Ferruh Yigit
@ 2018-06-29 16:26           ` Ferruh Yigit
  1 sibling, 0 replies; 11+ messages in thread
From: Ferruh Yigit @ 2018-06-29 16:26 UTC (permalink / raw)
  To: Adrien Mazarguil, Nelio Laranjeiro; +Cc: dev

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.

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2018-06-29 16:26 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-05-28  9:54 [dpdk-dev] [DPDK 18.08] ethdev: add flow API to expand RSS flows Nelio Laranjeiro
2018-05-28 13:33 ` Wiles, Keith
2018-06-05  8:26 ` [dpdk-dev] [PATCH v2] " Nelio Laranjeiro
2018-06-21  7:25   ` [dpdk-dev] [PATCH v3] " Nelio Laranjeiro
2018-06-27 14:26     ` Thomas Monjalon
2018-06-27 14:55     ` [dpdk-dev] [PATCH v4] " Nelio Laranjeiro
2018-06-28 12:33       ` Adrien Mazarguil
2018-06-28 16:01       ` [dpdk-dev] [PATCH v5] " Nelio Laranjeiro
2018-06-28 16:09         ` Adrien Mazarguil
2018-06-29 16:23           ` Ferruh Yigit
2018-06-29 16:26           ` Ferruh Yigit

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).