@@ -49,6 +49,7 @@ enum index {
PORT_ID,
GROUP_ID,
PRIORITY_LEVEL,
+ SHARED_ACTION_ID,
/* Top-level command. */
SET,
@@ -60,6 +61,7 @@ enum index {
/* Top-level command. */
FLOW,
/* Sub-level commands. */
+ SHARED_ACTION,
VALIDATE,
CREATE,
DESTROY,
@@ -89,6 +91,18 @@ enum index {
EGRESS,
TRANSFER,
+ /* Shared action arguments */
+ SHARED_ACTION_CREATE,
+ SHARED_ACTION_UPDATE,
+ SHARED_ACTION_DESTROY,
+ SHARED_ACTION_QUERY,
+
+ /* Shared action create arguments */
+ SHARED_ACTION_CREATE_ID,
+
+ /* Shared action destroy arguments */
+ SHARED_ACTION_DESTROY_ID,
+
/* Validate/create pattern. */
PATTERN,
ITEM_PARAM_IS,
@@ -360,6 +374,8 @@ enum index {
ACTION_SET_IPV6_DSCP_VALUE,
ACTION_AGE,
ACTION_AGE_TIMEOUT,
+ ACTION_SHARED,
+ SHARED_ACTION_ID2PTR,
};
/** Maximum size for pattern in struct rte_flow_item_raw. */
@@ -653,6 +669,13 @@ struct buffer {
enum index command; /**< Flow command. */
portid_t port; /**< Affected port ID. */
union {
+ struct {
+ uint32_t *action_id;
+ uint32_t action_id_n;
+ } sa_destroy; /**< Shared action destroy arguments. */
+ struct {
+ uint32_t action_id;
+ } sa; /* Shared action query arguments */
struct {
struct rte_flow_attr attr;
struct rte_flow_item *pattern;
@@ -709,6 +732,19 @@ struct parse_action_priv {
.size = s, \
})
+static const enum index next_sa_create_attr[] = {
+ SHARED_ACTION_CREATE_ID,
+ ACTION_RSS,
+ ZERO,
+};
+
+static const enum index next_sa_subcmd[] = {
+ SHARED_ACTION_CREATE,
+ SHARED_ACTION_UPDATE,
+ SHARED_ACTION_DESTROY,
+ SHARED_ACTION_QUERY,
+};
+
static const enum index next_vc_attr[] = {
GROUP,
PRIORITY,
@@ -743,6 +779,12 @@ static const enum index next_aged_attr[] = {
ZERO,
};
+static const enum index next_sa_destroy_attr[] = {
+ SHARED_ACTION_DESTROY_ID,
+ END,
+ ZERO,
+};
+
static const enum index item_param[] = {
ITEM_PARAM_IS,
ITEM_PARAM_SPEC,
@@ -1193,6 +1235,7 @@ static const enum index next_action[] = {
ACTION_SET_IPV4_DSCP,
ACTION_SET_IPV6_DSCP,
ACTION_AGE,
+ ACTION_SHARED,
ZERO,
};
@@ -1550,6 +1593,15 @@ static int parse_ipv6_addr(struct context *, const struct token *,
static int parse_port(struct context *, const struct token *,
const char *, unsigned int,
void *, unsigned int);
+static int parse_sa(struct context *, const struct token *,
+ const char *, unsigned int,
+ void *, unsigned int);
+static int parse_sa_destroy(struct context *ctx, const struct token *token,
+ const char *str, unsigned int len,
+ void *buf, unsigned int size);
+static int parse_sa_id2ptr(struct context *ctx, const struct token *token,
+ const char *str, unsigned int len, void *buf,
+ unsigned int size);
static int comp_none(struct context *, const struct token *,
unsigned int, char *, unsigned int);
static int comp_boolean(struct context *, const struct token *,
@@ -1688,13 +1740,21 @@ static const struct token token_list[] = {
.call = parse_int,
.comp = comp_none,
},
+ [SHARED_ACTION_ID] = {
+ .name = "{shared_action_id}",
+ .type = "SHARED_ACTION_ID",
+ .help = "shared action id",
+ .call = parse_int,
+ .comp = comp_none,
+ },
/* Top-level command. */
[FLOW] = {
.name = "flow",
.type = "{command} {port_id} [{arg} [...]]",
.help = "manage ingress/egress flow rules",
.next = NEXT(NEXT_ENTRY
- (VALIDATE,
+ (SHARED_ACTION,
+ VALIDATE,
CREATE,
DESTROY,
FLUSH,
@@ -1705,7 +1765,45 @@ static const struct token token_list[] = {
ISOLATE)),
.call = parse_init,
},
+ /* Top-level command. */
+ [SHARED_ACTION] = {
+ .name = "shared_action",
+ .type = "{command} {port_id} [{arg} [...]]",
+ .help = "manage shared actions",
+ .next = NEXT(next_sa_subcmd, NEXT_ENTRY(PORT_ID)),
+ .args = ARGS(ARGS_ENTRY(struct buffer, port)),
+ .call = parse_sa,
+ },
/* Sub-level commands. */
+ [SHARED_ACTION_CREATE] = {
+ .name = "create",
+ .help = "create shared action",
+ .next = NEXT(next_sa_create_attr),
+ .args = ARGS(ARGS_ENTRY(struct buffer, args.vc.attr.group)),
+ .call = parse_sa,
+ },
+ [SHARED_ACTION_UPDATE] = {
+ .name = "update",
+ .help = "update shared action",
+ .next = NEXT(NEXT_ENTRY(ACTION_RSS),
+ NEXT_ENTRY(SHARED_ACTION_ID)),
+ .args = ARGS(ARGS_ENTRY(struct buffer, args.vc.attr.group)),
+ .call = parse_sa,
+ },
+ [SHARED_ACTION_DESTROY] = {
+ .name = "destroy",
+ .help = "destroy shared action",
+ .next = NEXT(NEXT_ENTRY(SHARED_ACTION_DESTROY_ID)),
+ .args = ARGS(ARGS_ENTRY(struct buffer, port)),
+ .call = parse_sa_destroy,
+ },
+ [SHARED_ACTION_QUERY] = {
+ .name = "query",
+ .help = "query shared action",
+ .next = NEXT(NEXT_ENTRY(END), NEXT_ENTRY(SHARED_ACTION_ID)),
+ .args = ARGS(ARGS_ENTRY(struct buffer, args.sa.action_id)),
+ .call = parse_sa,
+ },
[VALIDATE] = {
.name = "validate",
.help = "check whether a flow rule can be created",
@@ -3859,6 +3957,40 @@ static const struct token token_list[] = {
.next = NEXT(action_age, NEXT_ENTRY(UNSIGNED)),
.call = parse_vc_conf,
},
+ /* Shared action destroy arguments. */
+ [SHARED_ACTION_DESTROY_ID] = {
+ .name = "action_id",
+ .help = "specify a shared action id to destroy",
+ .next = NEXT(next_sa_destroy_attr,
+ NEXT_ENTRY(SHARED_ACTION_ID)),
+ .args = ARGS(ARGS_ENTRY_PTR(struct buffer,
+ args.sa_destroy.action_id)),
+ .call = parse_sa_destroy,
+ },
+ /* Shared action create arguments. */
+ [SHARED_ACTION_CREATE_ID] = {
+ .name = "action_id",
+ .help = "specify a shared action id to create",
+ .next = NEXT(NEXT_ENTRY(ACTION_RSS),
+ NEXT_ENTRY(SHARED_ACTION_ID)),
+ .args = ARGS(ARGS_ENTRY(struct buffer, args.vc.attr.group)),
+ },
+ [ACTION_SHARED] = {
+ .name = "shared",
+ .help = "apply shared action by id",
+ .priv = PRIV_ACTION(SHARED, 0),
+ .next = NEXT(NEXT_ENTRY(SHARED_ACTION_ID2PTR)),
+ .args = ARGS(ARGS_ENTRY_ARB(0, sizeof(uint32_t))),
+ .call = parse_vc,
+ },
+ [SHARED_ACTION_ID2PTR] = {
+ .name = "{action_id}",
+ .type = "SHARED_ACTION_ID",
+ .help = "shared action id",
+ .next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
+ .call = parse_sa_id2ptr,
+ .comp = comp_none,
+ },
};
/** Remove and return last entry from argument stack. */
@@ -4043,6 +4175,91 @@ parse_init(struct context *ctx, const struct token *token,
return len;
}
+/** Parse tokens for shared action commands. */
+static int
+parse_sa(struct context *ctx, const struct token *token,
+ const char *str, unsigned int len,
+ void *buf, unsigned int size)
+{
+ struct buffer *out = buf;
+
+ /* Token name must match. */
+ if (parse_default(ctx, token, str, len, NULL, 0) < 0)
+ return -1;
+ /* Nothing else to do if there is no buffer. */
+ if (!out)
+ return len;
+ if (!out->command) {
+ if (ctx->curr != SHARED_ACTION)
+ return -1;
+ if (sizeof(*out) > size)
+ return -1;
+ out->command = ctx->curr;
+ ctx->objdata = 0;
+ ctx->object = out;
+ ctx->objmask = NULL;
+ out->args.vc.data = (uint8_t *)out + size;
+ return len;
+ }
+ switch (ctx->curr) {
+ case SHARED_ACTION_CREATE:
+ case SHARED_ACTION_UPDATE:
+ out->args.vc.actions =
+ (void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
+ sizeof(double));
+ out->args.vc.attr.group = UINT32_MAX;
+ /* fallthrough */
+ case SHARED_ACTION_QUERY:
+ out->command = ctx->curr;
+ ctx->objdata = 0;
+ ctx->object = out;
+ ctx->objmask = NULL;
+ return len;
+ default:
+ return -1;
+ }
+}
+
+
+/** Parse tokens for shared action destroy command. */
+static int
+parse_sa_destroy(struct context *ctx, const struct token *token,
+ const char *str, unsigned int len,
+ void *buf, unsigned int size)
+{
+ struct buffer *out = buf;
+ uint32_t *action_id;
+
+ /* Token name must match. */
+ if (parse_default(ctx, token, str, len, NULL, 0) < 0)
+ return -1;
+ /* Nothing else to do if there is no buffer. */
+ if (!out)
+ return len;
+ if (!out->command || out->command == SHARED_ACTION) {
+ if (ctx->curr != SHARED_ACTION_DESTROY)
+ return -1;
+ if (sizeof(*out) > size)
+ return -1;
+ out->command = ctx->curr;
+ ctx->objdata = 0;
+ ctx->object = out;
+ ctx->objmask = NULL;
+ out->args.sa_destroy.action_id =
+ (void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
+ sizeof(double));
+ return len;
+ }
+ action_id = out->args.sa_destroy.action_id
+ + out->args.sa_destroy.action_id_n++;
+ if ((uint8_t *)action_id > (uint8_t *)out + size)
+ return -1;
+ ctx->objdata = 0;
+ ctx->object = action_id;
+ ctx->objmask = NULL;
+ return len;
+}
+
/** Parse tokens for validate/create commands. */
static int
parse_vc(struct context *ctx, const struct token *token,
@@ -6110,6 +6327,32 @@ parse_port(struct context *ctx, const struct token *token,
return ret;
}
+static int
+parse_sa_id2ptr(struct context *ctx, const struct token *token,
+ const char *str, unsigned int len,
+ void *buf, unsigned int size)
+{
+ struct rte_flow_action *action = ctx->object;
+ uint32_t id;
+ int ret;
+
+ (void)buf;
+ (void)size;
+ ctx->objdata = 0;
+ ctx->object = &id;
+ ctx->objmask = NULL;
+ ret = parse_int(ctx, token, str, len, ctx->object, sizeof(id));
+ ctx->object = action;
+ if (ret != (int)len)
+ return ret;
+ /* set shared action */
+ if (action) {
+ action->conf = port_shared_action_get_by_id(ctx->port, id);
+ ret = (action->conf) ? ret : -1;
+ }
+ return ret;
+}
+
/** Parse set command, initialize output buffer for subsequent tokens. */
static int
parse_set_raw_encap_decap(struct context *ctx, const struct token *token,
@@ -6559,6 +6802,23 @@ static void
cmd_flow_parsed(const struct buffer *in)
{
switch (in->command) {
+ case SHARED_ACTION_CREATE:
+ port_shared_action_create(in->port,
+ in->args.vc.attr.group,
+ in->args.vc.actions);
+ break;
+ case SHARED_ACTION_DESTROY:
+ port_shared_action_destroy(in->port,
+ in->args.sa_destroy.action_id_n,
+ in->args.sa_destroy.action_id);
+ break;
+ case SHARED_ACTION_UPDATE:
+ port_shared_action_update(in->port, in->args.vc.attr.group,
+ in->args.vc.actions);
+ break;
+ case SHARED_ACTION_QUERY:
+ port_shared_action_query(in->port, in->args.sa.action_id);
+ break;
case VALIDATE:
port_flow_validate(in->port, &in->args.vc.attr,
in->args.vc.pattern, in->args.vc.actions);
@@ -1580,6 +1580,223 @@ rss_config_display(struct rte_flow_action_rss *rss_conf)
}
}
+static struct port_shared_action *
+action_get_by_id(portid_t port_id, uint32_t id)
+{
+ struct rte_port *port;
+ struct port_shared_action **ppsa;
+ struct port_shared_action *psa = NULL;
+
+ if (port_id_is_invalid(port_id, ENABLED_WARN) ||
+ port_id == (portid_t)RTE_PORT_ALL)
+ return NULL;
+ port = &ports[port_id];
+ ppsa = &port->actions_list;
+ while (*ppsa) {
+ if ((*ppsa)->id == id) {
+ psa = *ppsa;
+ break;
+ }
+ ppsa = &(*ppsa)->next;
+ }
+ if (!psa)
+ printf("Failed to find shared action #%u on port %u\n",
+ id, port_id);
+ return psa;
+}
+
+static int
+action_alloc(portid_t port_id, uint32_t id,
+ struct port_shared_action **action)
+{
+ struct rte_port *port;
+ struct port_shared_action **ppsa;
+ struct port_shared_action *psa = NULL;
+
+ *action = NULL;
+ if (port_id_is_invalid(port_id, ENABLED_WARN) ||
+ port_id == (portid_t)RTE_PORT_ALL)
+ return -EINVAL;
+ port = &ports[port_id];
+ if (id == UINT32_MAX) {
+ /* taking first available ID */
+ if (port->actions_list) {
+ if (port->actions_list->id == UINT32_MAX - 1) {
+ printf("Highest shared action ID is already"
+ " assigned, delete it first\n");
+ return -ENOMEM;
+ }
+ id = port->actions_list->id + 1;
+ } else {
+ id = 0;
+ }
+ }
+ psa = calloc(1, sizeof(*psa));
+ if (!psa) {
+ printf("Allocation of port %u shared action failed\n",
+ port_id);
+ return -ENOMEM;
+ }
+ ppsa = &port->actions_list;
+ while (*ppsa && (*ppsa)->id > id)
+ ppsa = &(*ppsa)->next;
+ if (*ppsa && (*ppsa)->id == id) {
+ printf("Shared action #%u is already assigned,"
+ " delete it first\n", id);
+ free(psa);
+ return -EINVAL;
+ }
+ psa->next = *ppsa;
+ psa->id = id;
+ *ppsa = psa;
+ *action = psa;
+ return 0;
+}
+
+/** Create shared action */
+int
+port_shared_action_create(portid_t port_id, uint32_t id,
+ const struct rte_flow_action *action)
+{
+ struct port_shared_action *psa;
+ int ret;
+ struct rte_flow_error error;
+
+ ret = action_alloc(port_id, id, &psa);
+ if (ret)
+ return ret;
+ /* Poisoning to make sure PMDs update it in case of error. */
+ memset(&error, 0x22, sizeof(error));
+ psa->action = rte_flow_shared_action_create(port_id, NULL, action,
+ &error);
+ if (!psa->action) {
+ uint32_t destroy_id = psa->id;
+ port_shared_action_destroy(port_id, 1, &destroy_id);
+ return port_flow_complain(&error);
+ }
+ psa->type = action->type;
+ printf("Shared action #%u created\n", psa->id);
+ return 0;
+}
+
+/** Destroy shared action */
+int
+port_shared_action_destroy(portid_t port_id,
+ uint32_t n,
+ const uint32_t *actions)
+{
+ struct rte_port *port;
+ struct port_shared_action **tmp;
+ uint32_t c = 0;
+ int ret = 0;
+
+ if (port_id_is_invalid(port_id, ENABLED_WARN) ||
+ port_id == (portid_t)RTE_PORT_ALL)
+ return -EINVAL;
+ port = &ports[port_id];
+ tmp = &port->actions_list;
+ while (*tmp) {
+ uint32_t i;
+
+ for (i = 0; i != n; ++i) {
+ struct rte_flow_error error;
+ struct port_shared_action *psa = *tmp;
+
+ if (actions[i] != psa->id)
+ continue;
+ /*
+ * Poisoning to make sure PMDs update it in case
+ * of error.
+ */
+ memset(&error, 0x33, sizeof(error));
+
+ if (psa->action && rte_flow_shared_action_destroy(
+ port_id, psa->action, &error)) {
+ ret = port_flow_complain(&error);
+ continue;
+ }
+ *tmp = psa->next;
+ free(psa);
+ printf("Shared action #%u destroyed\n", psa->id);
+ break;
+ }
+ if (i == n)
+ tmp = &(*tmp)->next;
+ ++c;
+ }
+ return ret;
+}
+
+
+/** Get shared action by port + id */
+struct rte_flow_shared_action *
+port_shared_action_get_by_id(portid_t port_id, uint32_t id)
+{
+
+ struct port_shared_action *psa = action_get_by_id(port_id, id);
+
+ return (psa) ? psa->action : NULL;
+}
+
+/** Update shared action */
+int
+port_shared_action_update(portid_t port_id, uint32_t id,
+ const struct rte_flow_action *action)
+{
+ struct rte_flow_error error;
+ struct rte_flow_shared_action *shared_action;
+
+ shared_action = port_shared_action_get_by_id(port_id, id);
+ if (!shared_action)
+ return -EINVAL;
+ if (rte_flow_shared_action_update(port_id, shared_action, action,
+ &error)) {
+ return port_flow_complain(&error);
+ }
+ printf("Shared action #%u updated\n", id);
+ return 0;
+}
+
+int
+port_shared_action_query(portid_t port_id, uint32_t id)
+{
+ struct rte_flow_error error;
+ struct port_shared_action *psa;
+ uint64_t default_data;
+ void *data = NULL;
+ int ret = 0;
+
+ psa = action_get_by_id(port_id, id);
+ if (!psa)
+ return -EINVAL;
+ switch (psa->type) {
+ case RTE_FLOW_ACTION_TYPE_RSS:
+ data = &default_data;
+ break;
+ default:
+ printf("Shared action %u (type: %d) on port %u doesn't support"
+ " query\n", id, psa->type, port_id);
+ return -1;
+ }
+ if (rte_flow_shared_action_query(port_id, psa->action, data, &error))
+ ret = port_flow_complain(&error);
+ switch (psa->type) {
+ case RTE_FLOW_ACTION_TYPE_RSS:
+ if (!ret)
+ printf("Shared RSS action:\n\trefs:%u\n",
+ *((uint32_t *)data));
+ data = NULL;
+ break;
+ default:
+ printf("Shared action %u (type: %d) on port %u doesn't support"
+ " query\n", id, psa->type, port_id);
+ ret = -1;
+ }
+ if (data)
+ free(data);
+ return ret;
+}
+
/** Validate flow rule. */
int
port_flow_validate(portid_t port_id,
@@ -142,6 +142,14 @@ struct port_flow {
uint8_t data[]; /**< Storage for flow rule description */
};
+/* Descriptor for shared action */
+struct port_shared_action {
+ struct port_shared_action *next; /**< Next flow in list. */
+ uint32_t id; /**< Shared action ID. */
+ enum rte_flow_action_type type; /**< Action type. */
+ struct rte_flow_shared_action *action; /**< Shared action handle. */
+};
+
/**
* The data structure associated with each port.
*/
@@ -172,6 +180,8 @@ struct rte_port {
uint32_t mc_addr_nb; /**< nb. of addr. in mc_addr_pool */
uint8_t slave_flag; /**< bonding slave port */
struct port_flow *flow_list; /**< Associated flows. */
+ struct port_shared_action *actions_list;
+ /**< Associated shared actions. */
const struct rte_eth_rxtx_callback *rx_dump_cb[RTE_MAX_QUEUES_PER_PORT+1];
const struct rte_eth_rxtx_callback *tx_dump_cb[RTE_MAX_QUEUES_PER_PORT+1];
/**< metadata value to insert in Tx packets. */
@@ -748,6 +758,14 @@ void port_reg_bit_field_set(portid_t port_id, uint32_t reg_off,
uint8_t bit1_pos, uint8_t bit2_pos, uint32_t value);
void port_reg_display(portid_t port_id, uint32_t reg_off);
void port_reg_set(portid_t port_id, uint32_t reg_off, uint32_t value);
+int port_shared_action_create(portid_t port_id, uint32_t id,
+ const struct rte_flow_action *action);
+int port_shared_action_destroy(portid_t port_id,
+ uint32_t n, const uint32_t *action);
+struct rte_flow_shared_action *port_shared_action_get_by_id(portid_t port_id,
+ uint32_t id);
+int port_shared_action_update(portid_t port_id, uint32_t id,
+ const struct rte_flow_action *action);
int port_flow_validate(portid_t port_id,
const struct rte_flow_attr *attr,
const struct rte_flow_item *pattern,
@@ -756,6 +774,7 @@ int port_flow_create(portid_t port_id,
const struct rte_flow_attr *attr,
const struct rte_flow_item *pattern,
const struct rte_flow_action *actions);
+int port_shared_action_query(portid_t port_id, uint32_t id);
void update_age_action_context(const struct rte_flow_action *actions,
struct port_flow *pf);
int port_flow_destroy(portid_t port_id, uint32_t n, const uint32_t *rule);
@@ -4382,6 +4382,11 @@ This section lists supported actions and their attributes, if any.
- ``dscp_value {unsigned}``: The new DSCP value to be set
+- ``shared``: Use shared action created via
+ ``flow shared_action {port_id} create``
+
+ - ``shared_action_id {unsigned}``: Shared action ID to use
+
Destroying flow rules
~~~~~~~~~~~~~~~~~~~~~
@@ -4671,6 +4676,112 @@ If attach ``destroy`` parameter, the command will destroy all the list aged flow
testpmd> flow aged 0
Port 0 total aged flows: 0
+Creating shared actions
+~~~~~~~~~~~~~~~~~~~~~~~
+``flow shared_action {port_id} create`` creates shared action with optional
+shared action ID. It is bound to ``rte_flow_shared_action_create()``::
+
+ flow shared_action {port_id} create [action_id {shared_action_id}] \
+ {action} / end
+
+If successful, it will show::
+
+ Shared action #[...] created
+
+Otherwise, it will complain either that shared action already exists or that
+some error occurred::
+
+ Shared action #[...] is already assigned, delete it first
+
+::
+
+ Caught error type [...] ([...]): [...]
+
+Create shared rss action with id 100 to queues 1 and 2 on port 0::
+
+ testpmd> flow shared_action 0 create action_id 100 \
+ rss queues 1 2 end / end
+
+Create shared rss action with id assigned by testpmd to queues 1 and 2 on
+port 0::
+
+ testpmd> flow shared_action 0 create action_id \
+ rss queues 0 1 end / end
+
+Updating shared actions
+~~~~~~~~~~~~~~~~~~~~~~~
+``flow shared_action {port_id} update`` updates configuration of the shared
+action from its shared action ID (as returned by
+``flow shared_action {port_id} create``). It is bound to
+``rte_flow_shared_action_update()``::
+
+ flow shared_action {port_id} update {shared_action_id} {action} / end
+
+If successful, it will show::
+
+ Shared action #[...] updated
+
+Otherwise, it will complain either that shared action not found or that some
+error occurred::
+
+ Failed to find shared action #[...] on port [...]
+
+::
+
+ Caught error type [...] ([...]): [...]
+
+Update shared rss action having id 100 on port 0 with rss to queues 0 and 3
+(in create example above rss queues were 1 and 2)::
+
+ testpmd> flow shared_action 0 update 100 rss queues 0 3 end / end
+
+Destroying shared actions
+~~~~~~~~~~~~~~~~~~~~~~~~~
+``flow shared_action {port_id} update`` destroys one or more shared actions
+from their shared action IDs (as returned by
+``flow shared_action {port_id} create``). It is bound to
+``rte_flow_shared_action_destroy()``::
+
+ flow shared_action {port_id} destroy action_id {shared_action_id} [...]
+
+If successful, it will show::
+
+ Shared action #[...] destroyed
+
+It does not report anything for shared action IDs that do not exist.
+The usual error message is shown when a shared action cannot be destroyed::
+
+ Caught error type [...] ([...]): [...]
+
+Destroy shared actions having id 100 & 101::
+
+ testpmd> flow shared_action 0 destroy action_id 100 action_id 101
+
+Query shared actions
+~~~~~~~~~~~~~~~~~~~~
+``flow shared_action {port_id} query`` queries the shared action from its
+shared action ID (as returned by ``flow shared_action {port_id} create``).
+It is bound to ``rte_flow_shared_action_query()``::
+
+ flow shared_action {port_id} query {shared_action_id}
+
+Currently only rss shared action supported. If successful, it will show::
+
+ Shared RSS action:
+ refs:[...]
+
+Otherwise, it will complain either that shared action not found or that some
+error occurred::
+
+ Failed to find shared action #[...] on port [...]
+
+::
+
+ Caught error type [...] ([...]): [...]
+
+Query shared action having id 100::
+
+ testpmd> flow shared_action 0 query 100
Sample QinQ flow rules
~~~~~~~~~~~~~~~~~~~~~~