ethdev: add flow API object converter

rte_flow_copy() is bound to duplicate flow rule descriptions
(attributes, pattern and list of actions, all at once), however
applications sometimes need more flexibility, for instance the ability
to duplicate only one of the underlying objects (a single pattern item
or action) or retrieve other properties such as their names.

Instead of adding dedicated functions to handle each possible use case,
this patch introduces rte_flow_conv(), which supports any number of
object conversion operations in an extensible manner.

This patch re-implements rte_flow_copy() as a wrapper to
rte_flow_conv().

Signed-off-by: Adrien Mazarguil <adrien.mazarguil@6wind.com>
Acked-by: Nelio Laranjeiro <nelio.laranjeiro@6wind.com>
This commit is contained in:
Adrien Mazarguil 2018-08-31 11:01:00 +02:00 committed by Ferruh Yigit
parent 1a9d944f5a
commit 063911ee1d
4 changed files with 581 additions and 160 deletions

View File

@ -2419,6 +2419,25 @@ This function initializes ``error`` (if non-NULL) with the provided
parameters and sets ``rte_errno`` to ``code``. A negative error ``code`` is
then returned.
Object conversion
~~~~~~~~~~~~~~~~~
.. code-block:: c
int
rte_flow_conv(enum rte_flow_conv_op op,
void *dst,
size_t size,
const void *src,
struct rte_flow_error *error);
Convert ``src`` to ``dst`` according to operation ``op``. Possible
operations include:
- Attributes, pattern item or action duplication.
- Duplication of an entire pattern or list of actions.
- Duplication of a complete flow rule description.
Caveats
-------

View File

@ -239,6 +239,7 @@ EXPERIMENTAL {
rte_eth_dev_tx_offload_name;
rte_eth_switch_domain_alloc;
rte_eth_switch_domain_free;
rte_flow_conv;
rte_flow_expand_rss;
rte_mtr_capabilities_get;
rte_mtr_create;

View File

@ -288,26 +288,41 @@ rte_flow_error_set(struct rte_flow_error *error,
}
/** Pattern item specification types. */
enum item_spec_type {
ITEM_SPEC,
ITEM_LAST,
ITEM_MASK,
enum rte_flow_conv_item_spec_type {
RTE_FLOW_CONV_ITEM_SPEC,
RTE_FLOW_CONV_ITEM_LAST,
RTE_FLOW_CONV_ITEM_MASK,
};
/** Compute storage space needed by item specification and copy it. */
/**
* Copy pattern item specification.
*
* @param[out] buf
* Output buffer. Can be NULL if @p size is zero.
* @param size
* Size of @p buf in bytes.
* @param[in] item
* Pattern item to copy specification from.
* @param type
* Specification selector for either @p spec, @p last or @p mask.
*
* @return
* Number of bytes needed to store pattern item specification regardless
* of @p size. @p buf contents are truncated to @p size if not large
* enough.
*/
static size_t
flow_item_spec_copy(void *buf, const struct rte_flow_item *item,
enum item_spec_type type)
rte_flow_conv_item_spec(void *buf, const size_t size,
const struct rte_flow_item *item,
enum rte_flow_conv_item_spec_type type)
{
size_t size = 0;
size_t off;
const void *data =
type == ITEM_SPEC ? item->spec :
type == ITEM_LAST ? item->last :
type == ITEM_MASK ? item->mask :
type == RTE_FLOW_CONV_ITEM_SPEC ? item->spec :
type == RTE_FLOW_CONV_ITEM_LAST ? item->last :
type == RTE_FLOW_CONV_ITEM_MASK ? item->mask :
NULL;
if (!item->spec || !data)
goto empty;
switch (item->type) {
union {
const struct rte_flow_item_raw *raw;
@ -324,7 +339,7 @@ flow_item_spec_copy(void *buf, const struct rte_flow_item *item,
union {
struct rte_flow_item_raw *raw;
} dst;
size_t off;
size_t tmp;
case RTE_FLOW_ITEM_TYPE_RAW:
spec.raw = item->spec;
@ -332,41 +347,62 @@ flow_item_spec_copy(void *buf, const struct rte_flow_item *item,
mask.raw = item->mask ? item->mask : &rte_flow_item_raw_mask;
src.raw = data;
dst.raw = buf;
off = RTE_ALIGN_CEIL(sizeof(struct rte_flow_item_raw),
sizeof(*src.raw->pattern));
if (type == ITEM_SPEC ||
(type == ITEM_MASK &&
rte_memcpy(dst.raw,
(&(struct rte_flow_item_raw){
.relative = src.raw->relative,
.search = src.raw->search,
.reserved = src.raw->reserved,
.offset = src.raw->offset,
.limit = src.raw->limit,
.length = src.raw->length,
}),
size > sizeof(*dst.raw) ? sizeof(*dst.raw) : size);
off = sizeof(*dst.raw);
if (type == RTE_FLOW_CONV_ITEM_SPEC ||
(type == RTE_FLOW_CONV_ITEM_MASK &&
((spec.raw->length & mask.raw->length) >=
(last.raw->length & mask.raw->length))))
size = spec.raw->length & mask.raw->length;
tmp = spec.raw->length & mask.raw->length;
else
size = last.raw->length & mask.raw->length;
size = off + size * sizeof(*src.raw->pattern);
if (dst.raw) {
memcpy(dst.raw, src.raw, sizeof(*src.raw));
dst.raw->pattern = memcpy((uint8_t *)dst.raw + off,
src.raw->pattern,
size - off);
tmp = last.raw->length & mask.raw->length;
if (tmp) {
off = RTE_ALIGN_CEIL(off, sizeof(*dst.raw->pattern));
if (size >= off + tmp)
dst.raw->pattern = rte_memcpy
((void *)((uintptr_t)dst.raw + off),
src.raw->pattern, tmp);
off += tmp;
}
break;
default:
size = rte_flow_desc_item[item->type].size;
if (buf)
memcpy(buf, data, size);
off = rte_flow_desc_item[item->type].size;
rte_memcpy(buf, data, (size > off ? off : size));
break;
}
empty:
return RTE_ALIGN_CEIL(size, sizeof(double));
return off;
}
/** Compute storage space needed by action configuration and copy it. */
/**
* Copy action configuration.
*
* @param[out] buf
* Output buffer. Can be NULL if @p size is zero.
* @param size
* Size of @p buf in bytes.
* @param[in] action
* Action to copy configuration from.
*
* @return
* Number of bytes needed to store pattern item specification regardless
* of @p size. @p buf contents are truncated to @p size if not large
* enough.
*/
static size_t
flow_action_conf_copy(void *buf, const struct rte_flow_action *action)
rte_flow_conv_action_conf(void *buf, const size_t size,
const struct rte_flow_action *action)
{
size_t size = 0;
size_t off;
if (!action->conf)
goto empty;
switch (action->type) {
union {
const struct rte_flow_action_rss *rss;
@ -374,49 +410,308 @@ flow_action_conf_copy(void *buf, const struct rte_flow_action *action)
union {
struct rte_flow_action_rss *rss;
} dst;
size_t off;
size_t tmp;
case RTE_FLOW_ACTION_TYPE_RSS:
src.rss = action->conf;
dst.rss = buf;
off = 0;
if (dst.rss)
*dst.rss = (struct rte_flow_action_rss){
rte_memcpy(dst.rss,
(&(struct rte_flow_action_rss){
.func = src.rss->func,
.level = src.rss->level,
.types = src.rss->types,
.key_len = src.rss->key_len,
.queue_num = src.rss->queue_num,
};
off += sizeof(*src.rss);
}),
size > sizeof(*dst.rss) ? sizeof(*dst.rss) : size);
off = sizeof(*dst.rss);
if (src.rss->key_len) {
off = RTE_ALIGN_CEIL(off, sizeof(double));
size = sizeof(*src.rss->key) * src.rss->key_len;
if (dst.rss)
dst.rss->key = memcpy
off = RTE_ALIGN_CEIL(off, sizeof(*dst.rss->key));
tmp = sizeof(*src.rss->key) * src.rss->key_len;
if (size >= off + tmp)
dst.rss->key = rte_memcpy
((void *)((uintptr_t)dst.rss + off),
src.rss->key, size);
off += size;
src.rss->key, tmp);
off += tmp;
}
if (src.rss->queue_num) {
off = RTE_ALIGN_CEIL(off, sizeof(double));
size = sizeof(*src.rss->queue) * src.rss->queue_num;
if (dst.rss)
dst.rss->queue = memcpy
off = RTE_ALIGN_CEIL(off, sizeof(*dst.rss->queue));
tmp = sizeof(*src.rss->queue) * src.rss->queue_num;
if (size >= off + tmp)
dst.rss->queue = rte_memcpy
((void *)((uintptr_t)dst.rss + off),
src.rss->queue, size);
off += size;
src.rss->queue, tmp);
off += tmp;
}
size = off;
break;
default:
size = rte_flow_desc_action[action->type].size;
if (buf)
memcpy(buf, action->conf, size);
off = rte_flow_desc_action[action->type].size;
rte_memcpy(buf, action->conf, (size > off ? off : size));
break;
}
empty:
return RTE_ALIGN_CEIL(size, sizeof(double));
return off;
}
/**
* Copy a list of pattern items.
*
* @param[out] dst
* Destination buffer. Can be NULL if @p size is zero.
* @param size
* Size of @p dst in bytes.
* @param[in] src
* Source pattern items.
* @param num
* Maximum number of pattern items to process from @p src or 0 to process
* the entire list. In both cases, processing stops after
* RTE_FLOW_ITEM_TYPE_END is encountered.
* @param[out] error
* Perform verbose error reporting if not NULL.
*
* @return
* A positive value representing the number of bytes needed to store
* pattern items regardless of @p size on success (@p buf contents are
* truncated to @p size if not large enough), a negative errno value
* otherwise and rte_errno is set.
*/
static int
rte_flow_conv_pattern(struct rte_flow_item *dst,
const size_t size,
const struct rte_flow_item *src,
unsigned int num,
struct rte_flow_error *error)
{
uintptr_t data = (uintptr_t)dst;
size_t off;
size_t ret;
unsigned int i;
for (i = 0, off = 0; !num || i != num; ++i, ++src, ++dst) {
if ((size_t)src->type >= RTE_DIM(rte_flow_desc_item) ||
!rte_flow_desc_item[src->type].name)
return rte_flow_error_set
(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ITEM, src,
"cannot convert unknown item type");
if (size >= off + sizeof(*dst))
*dst = (struct rte_flow_item){
.type = src->type,
};
off += sizeof(*dst);
if (!src->type)
num = i + 1;
}
num = i;
src -= num;
dst -= num;
do {
if (src->spec) {
off = RTE_ALIGN_CEIL(off, sizeof(double));
ret = rte_flow_conv_item_spec
((void *)(data + off),
size > off ? size - off : 0, src,
RTE_FLOW_CONV_ITEM_SPEC);
if (size && size >= off + ret)
dst->spec = (void *)(data + off);
off += ret;
}
if (src->last) {
off = RTE_ALIGN_CEIL(off, sizeof(double));
ret = rte_flow_conv_item_spec
((void *)(data + off),
size > off ? size - off : 0, src,
RTE_FLOW_CONV_ITEM_LAST);
if (size && size >= off + ret)
dst->last = (void *)(data + off);
off += ret;
}
if (src->mask) {
off = RTE_ALIGN_CEIL(off, sizeof(double));
ret = rte_flow_conv_item_spec
((void *)(data + off),
size > off ? size - off : 0, src,
RTE_FLOW_CONV_ITEM_MASK);
if (size && size >= off + ret)
dst->mask = (void *)(data + off);
off += ret;
}
++src;
++dst;
} while (--num);
return off;
}
/**
* Copy a list of actions.
*
* @param[out] dst
* Destination buffer. Can be NULL if @p size is zero.
* @param size
* Size of @p dst in bytes.
* @param[in] src
* Source actions.
* @param num
* Maximum number of actions to process from @p src or 0 to process the
* entire list. In both cases, processing stops after
* RTE_FLOW_ACTION_TYPE_END is encountered.
* @param[out] error
* Perform verbose error reporting if not NULL.
*
* @return
* A positive value representing the number of bytes needed to store
* actions regardless of @p size on success (@p buf contents are truncated
* to @p size if not large enough), a negative errno value otherwise and
* rte_errno is set.
*/
static int
rte_flow_conv_actions(struct rte_flow_action *dst,
const size_t size,
const struct rte_flow_action *src,
unsigned int num,
struct rte_flow_error *error)
{
uintptr_t data = (uintptr_t)dst;
size_t off;
size_t ret;
unsigned int i;
for (i = 0, off = 0; !num || i != num; ++i, ++src, ++dst) {
if ((size_t)src->type >= RTE_DIM(rte_flow_desc_action) ||
!rte_flow_desc_action[src->type].name)
return rte_flow_error_set
(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ACTION,
src, "cannot convert unknown action type");
if (size >= off + sizeof(*dst))
*dst = (struct rte_flow_action){
.type = src->type,
};
off += sizeof(*dst);
if (!src->type)
num = i + 1;
}
num = i;
src -= num;
dst -= num;
do {
if (src->conf) {
off = RTE_ALIGN_CEIL(off, sizeof(double));
ret = rte_flow_conv_action_conf
((void *)(data + off),
size > off ? size - off : 0, src);
if (size && size >= off + ret)
dst->conf = (void *)(data + off);
off += ret;
}
++src;
++dst;
} while (--num);
return off;
}
/**
* Copy flow rule components.
*
* This comprises the flow rule descriptor itself, attributes, pattern and
* actions list. NULL components in @p src are skipped.
*
* @param[out] dst
* Destination buffer. Can be NULL if @p size is zero.
* @param size
* Size of @p dst in bytes.
* @param[in] src
* Source flow rule descriptor.
* @param[out] error
* Perform verbose error reporting if not NULL.
*
* @return
* A positive value representing the number of bytes needed to store all
* components including the descriptor regardless of @p size on success
* (@p buf contents are truncated to @p size if not large enough), a
* negative errno value otherwise and rte_errno is set.
*/
static int
rte_flow_conv_rule(struct rte_flow_conv_rule *dst,
const size_t size,
const struct rte_flow_conv_rule *src,
struct rte_flow_error *error)
{
size_t off;
int ret;
rte_memcpy(dst,
(&(struct rte_flow_conv_rule){
.attr = NULL,
.pattern = NULL,
.actions = NULL,
}),
size > sizeof(*dst) ? sizeof(*dst) : size);
off = sizeof(*dst);
if (src->attr_ro) {
off = RTE_ALIGN_CEIL(off, sizeof(double));
if (size && size >= off + sizeof(*dst->attr))
dst->attr = rte_memcpy
((void *)((uintptr_t)dst + off),
src->attr_ro, sizeof(*dst->attr));
off += sizeof(*dst->attr);
}
if (src->pattern_ro) {
off = RTE_ALIGN_CEIL(off, sizeof(double));
ret = rte_flow_conv_pattern((void *)((uintptr_t)dst + off),
size > off ? size - off : 0,
src->pattern_ro, 0, error);
if (ret < 0)
return ret;
if (size && size >= off + (size_t)ret)
dst->pattern = (void *)((uintptr_t)dst + off);
off += ret;
}
if (src->actions_ro) {
off = RTE_ALIGN_CEIL(off, sizeof(double));
ret = rte_flow_conv_actions((void *)((uintptr_t)dst + off),
size > off ? size - off : 0,
src->actions_ro, 0, error);
if (ret < 0)
return ret;
if (size >= off + (size_t)ret)
dst->actions = (void *)((uintptr_t)dst + off);
off += ret;
}
return off;
}
/** Helper function to convert flow API objects. */
int
rte_flow_conv(enum rte_flow_conv_op op,
void *dst,
size_t size,
const void *src,
struct rte_flow_error *error)
{
switch (op) {
const struct rte_flow_attr *attr;
case RTE_FLOW_CONV_OP_NONE:
return 0;
case RTE_FLOW_CONV_OP_ATTR:
attr = src;
if (size > sizeof(*attr))
size = sizeof(*attr);
rte_memcpy(dst, attr, size);
return sizeof(*attr);
case RTE_FLOW_CONV_OP_ITEM:
return rte_flow_conv_pattern(dst, size, src, 1, error);
case RTE_FLOW_CONV_OP_ACTION:
return rte_flow_conv_actions(dst, size, src, 1, error);
case RTE_FLOW_CONV_OP_PATTERN:
return rte_flow_conv_pattern(dst, size, src, 0, error);
case RTE_FLOW_CONV_OP_ACTIONS:
return rte_flow_conv_actions(dst, size, src, 0, error);
case RTE_FLOW_CONV_OP_RULE:
return rte_flow_conv_rule(dst, size, src, error);
}
return rte_flow_error_set
(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
"unknown object conversion operation");
}
/** Store a full rte_flow description. */
@ -426,105 +721,49 @@ rte_flow_copy(struct rte_flow_desc *desc, size_t len,
const struct rte_flow_item *items,
const struct rte_flow_action *actions)
{
struct rte_flow_desc *fd = NULL;
size_t tmp;
size_t off1 = 0;
size_t off2 = 0;
size_t size = 0;
/*
* Overlap struct rte_flow_conv with struct rte_flow_desc in order
* to convert the former to the latter without wasting space.
*/
struct rte_flow_conv_rule *dst =
len ?
(void *)((uintptr_t)desc +
(offsetof(struct rte_flow_desc, actions) -
offsetof(struct rte_flow_conv_rule, actions))) :
NULL;
size_t dst_size =
len > sizeof(*desc) - sizeof(*dst) ?
len - (sizeof(*desc) - sizeof(*dst)) :
0;
struct rte_flow_conv_rule src = {
.attr_ro = NULL,
.pattern_ro = items,
.actions_ro = actions,
};
int ret;
store:
if (items) {
const struct rte_flow_item *item;
item = items;
if (fd)
fd->items = (void *)&fd->data[off1];
do {
struct rte_flow_item *dst = NULL;
if ((size_t)item->type >=
RTE_DIM(rte_flow_desc_item) ||
!rte_flow_desc_item[item->type].name) {
rte_errno = ENOTSUP;
return 0;
}
if (fd)
dst = memcpy(fd->data + off1, item,
sizeof(*item));
off1 += sizeof(*item);
if (item->spec) {
if (fd)
dst->spec = fd->data + off2;
off2 += flow_item_spec_copy
(fd ? fd->data + off2 : NULL, item,
ITEM_SPEC);
}
if (item->last) {
if (fd)
dst->last = fd->data + off2;
off2 += flow_item_spec_copy
(fd ? fd->data + off2 : NULL, item,
ITEM_LAST);
}
if (item->mask) {
if (fd)
dst->mask = fd->data + off2;
off2 += flow_item_spec_copy
(fd ? fd->data + off2 : NULL, item,
ITEM_MASK);
}
off2 = RTE_ALIGN_CEIL(off2, sizeof(double));
} while ((item++)->type != RTE_FLOW_ITEM_TYPE_END);
off1 = RTE_ALIGN_CEIL(off1, sizeof(double));
RTE_BUILD_BUG_ON(sizeof(struct rte_flow_desc) <
sizeof(struct rte_flow_conv_rule));
if (dst_size &&
(&dst->pattern != &desc->items ||
&dst->actions != &desc->actions ||
(uintptr_t)(dst + 1) != (uintptr_t)(desc + 1))) {
rte_errno = EINVAL;
return 0;
}
if (actions) {
const struct rte_flow_action *action;
action = actions;
if (fd)
fd->actions = (void *)&fd->data[off1];
do {
struct rte_flow_action *dst = NULL;
if ((size_t)action->type >=
RTE_DIM(rte_flow_desc_action) ||
!rte_flow_desc_action[action->type].name) {
rte_errno = ENOTSUP;
return 0;
}
if (fd)
dst = memcpy(fd->data + off1, action,
sizeof(*action));
off1 += sizeof(*action);
if (action->conf) {
if (fd)
dst->conf = fd->data + off2;
off2 += flow_action_conf_copy
(fd ? fd->data + off2 : NULL, action);
}
off2 = RTE_ALIGN_CEIL(off2, sizeof(double));
} while ((action++)->type != RTE_FLOW_ACTION_TYPE_END);
}
if (fd != NULL)
return size;
off1 = RTE_ALIGN_CEIL(off1, sizeof(double));
tmp = RTE_ALIGN_CEIL(offsetof(struct rte_flow_desc, data),
sizeof(double));
size = tmp + off1 + off2;
if (size > len)
return size;
fd = desc;
if (fd != NULL) {
*fd = (const struct rte_flow_desc) {
.size = size,
ret = rte_flow_conv(RTE_FLOW_CONV_OP_RULE, dst, dst_size, &src, NULL);
if (ret < 0)
return 0;
ret += sizeof(*desc) - sizeof(*dst);
rte_memcpy(desc,
(&(struct rte_flow_desc){
.size = ret,
.attr = *attr,
};
tmp -= offsetof(struct rte_flow_desc, data);
off2 = tmp + off1;
off1 = tmp;
goto store;
}
return 0;
.items = dst_size ? dst->pattern : NULL,
.actions = dst_size ? dst->actions : NULL,
}),
len > sizeof(*desc) ? sizeof(*desc) : len);
return ret;
}
/**

View File

@ -18,6 +18,7 @@
#include <stdint.h>
#include <rte_arp.h>
#include <rte_common.h>
#include <rte_ether.h>
#include <rte_eth_ctrl.h>
#include <rte_icmp.h>
@ -1931,6 +1932,119 @@ struct rte_flow_error {
const char *message; /**< Human-readable error message. */
};
/**
* Complete flow rule description.
*
* This object type is used when converting a flow rule description.
*
* @see RTE_FLOW_CONV_OP_RULE
* @see rte_flow_conv()
*/
RTE_STD_C11
struct rte_flow_conv_rule {
union {
const struct rte_flow_attr *attr_ro; /**< RO attributes. */
struct rte_flow_attr *attr; /**< Attributes. */
};
union {
const struct rte_flow_item *pattern_ro; /**< RO pattern. */
struct rte_flow_item *pattern; /**< Pattern items. */
};
union {
const struct rte_flow_action *actions_ro; /**< RO actions. */
struct rte_flow_action *actions; /**< List of actions. */
};
};
/**
* Conversion operations for flow API objects.
*
* @see rte_flow_conv()
*/
enum rte_flow_conv_op {
/**
* No operation to perform.
*
* rte_flow_conv() simply returns 0.
*/
RTE_FLOW_CONV_OP_NONE,
/**
* Convert attributes structure.
*
* This is a basic copy of an attributes structure.
*
* - @p src type:
* @code const struct rte_flow_attr * @endcode
* - @p dst type:
* @code struct rte_flow_attr * @endcode
*/
RTE_FLOW_CONV_OP_ATTR,
/**
* Convert a single item.
*
* Duplicates @p spec, @p last and @p mask but not outside objects.
*
* - @p src type:
* @code const struct rte_flow_item * @endcode
* - @p dst type:
* @code struct rte_flow_item * @endcode
*/
RTE_FLOW_CONV_OP_ITEM,
/**
* Convert a single action.
*
* Duplicates @p conf but not outside objects.
*
* - @p src type:
* @code const struct rte_flow_action * @endcode
* - @p dst type:
* @code struct rte_flow_action * @endcode
*/
RTE_FLOW_CONV_OP_ACTION,
/**
* Convert an entire pattern.
*
* Duplicates all pattern items at once with the same constraints as
* RTE_FLOW_CONV_OP_ITEM.
*
* - @p src type:
* @code const struct rte_flow_item * @endcode
* - @p dst type:
* @code struct rte_flow_item * @endcode
*/
RTE_FLOW_CONV_OP_PATTERN,
/**
* Convert a list of actions.
*
* Duplicates the entire list of actions at once with the same
* constraints as RTE_FLOW_CONV_OP_ACTION.
*
* - @p src type:
* @code const struct rte_flow_action * @endcode
* - @p dst type:
* @code struct rte_flow_action * @endcode
*/
RTE_FLOW_CONV_OP_ACTIONS,
/**
* Convert a complete flow rule description.
*
* Comprises attributes, pattern and actions together at once with
* the usual constraints.
*
* - @p src type:
* @code const struct rte_flow_conv_rule * @endcode
* - @p dst type:
* @code struct rte_flow_conv_rule * @endcode
*/
RTE_FLOW_CONV_OP_RULE,
};
/**
* Check whether a flow rule can be created on a given port.
*
@ -2162,10 +2276,7 @@ rte_flow_error_set(struct rte_flow_error *error,
const char *message);
/**
* Generic flow representation.
*
* This form is sufficient to describe an rte_flow independently from any
* PMD implementation and allows for replayability and identification.
* @see rte_flow_copy()
*/
struct rte_flow_desc {
size_t size; /**< Allocated space including data[]. */
@ -2178,6 +2289,9 @@ struct rte_flow_desc {
/**
* Copy an rte_flow rule description.
*
* This interface is kept for compatibility with older applications but is
* implemented as a wrapper to rte_flow_conv().
*
* @param[in] fd
* Flow rule description.
* @param[in] len
@ -2201,6 +2315,54 @@ rte_flow_copy(struct rte_flow_desc *fd, size_t len,
const struct rte_flow_item *items,
const struct rte_flow_action *actions);
/**
* Flow object conversion helper.
*
* This function performs conversion of various flow API objects to a
* pre-allocated destination buffer. See enum rte_flow_conv_op for possible
* operations and details about each of them.
*
* Since destination buffer must be large enough, it works in a manner
* reminiscent of snprintf():
*
* - If @p size is 0, @p dst may be a NULL pointer, otherwise @p dst must be
* non-NULL.
* - If positive, the returned value represents the number of bytes needed
* to store the conversion of @p src to @p dst according to @p op
* regardless of the @p size parameter.
* - Since no more than @p size bytes can be written to @p dst, output is
* truncated and may be inconsistent when the returned value is larger
* than that.
* - In case of conversion error, a negative error code is returned and
* @p dst contents are unspecified.
*
* @param op
* Operation to perform, related to the object type of @p dst.
* @param[out] dst
* Destination buffer address. Must be suitably aligned by the caller.
* @param size
* Destination buffer size in bytes.
* @param[in] src
* Source object to copy. Depending on @p op, its type may differ from
* that of @p dst.
* @param[out] error
* Perform verbose error reporting if not NULL. Initialized in case of
* error only.
*
* @return
* The number of bytes required to convert @p src to @p dst on success, a
* negative errno value otherwise and rte_errno is set.
*
* @see rte_flow_conv_op
*/
__rte_experimental
int
rte_flow_conv(enum rte_flow_conv_op op,
void *dst,
size_t size,
const void *src,
struct rte_flow_error *error);
#ifdef __cplusplus
}
#endif