+ printf("RSS:\n"
+ " queues:");
+ if (rss_conf->queue_num == 0)
+ printf(" none");
+ for (i = 0; i < rss_conf->queue_num; i++)
+ printf(" %d", rss_conf->queue[i]);
+ printf("\n");
+
+ printf(" function: ");
+ switch (rss_conf->func) {
+ case RTE_ETH_HASH_FUNCTION_DEFAULT:
+ printf("default\n");
+ break;
+ case RTE_ETH_HASH_FUNCTION_TOEPLITZ:
+ printf("toeplitz\n");
+ break;
+ case RTE_ETH_HASH_FUNCTION_SIMPLE_XOR:
+ printf("simple_xor\n");
+ break;
+ case RTE_ETH_HASH_FUNCTION_SYMMETRIC_TOEPLITZ:
+ printf("symmetric_toeplitz\n");
+ break;
+ default:
+ printf("Unknown function\n");
+ return;
+ }
+
+ printf(" types:\n");
+ if (rss_conf->types == 0) {
+ printf(" none\n");
+ return;
+ }
+ for (i = 0; rss_type_table[i].str; i++) {
+ if ((rss_conf->types &
+ rss_type_table[i].rss_type) ==
+ rss_type_table[i].rss_type &&
+ rss_type_table[i].rss_type != 0)
+ printf(" %s\n", rss_type_table[i].str);
+ }
+}
+
+static struct port_indirect_action *
+action_get_by_id(portid_t port_id, uint32_t id)
+{
+ struct rte_port *port;
+ struct port_indirect_action **ppia;
+ struct port_indirect_action *pia = NULL;
+
+ if (port_id_is_invalid(port_id, ENABLED_WARN) ||
+ port_id == (portid_t)RTE_PORT_ALL)
+ return NULL;
+ port = &ports[port_id];
+ ppia = &port->actions_list;
+ while (*ppia) {
+ if ((*ppia)->id == id) {
+ pia = *ppia;
+ break;
+ }
+ ppia = &(*ppia)->next;
+ }
+ if (!pia)
+ fprintf(stderr,
+ "Failed to find indirect action #%u on port %u\n",
+ id, port_id);
+ return pia;
+}
+
+static int
+action_alloc(portid_t port_id, uint32_t id,
+ struct port_indirect_action **action)
+{
+ struct rte_port *port;
+ struct port_indirect_action **ppia;
+ struct port_indirect_action *pia = 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) {
+ fprintf(stderr,
+ "Highest indirect action ID is already assigned, delete it first\n");
+ return -ENOMEM;
+ }
+ id = port->actions_list->id + 1;
+ } else {
+ id = 0;
+ }
+ }
+ pia = calloc(1, sizeof(*pia));
+ if (!pia) {
+ fprintf(stderr,
+ "Allocation of port %u indirect action failed\n",
+ port_id);
+ return -ENOMEM;
+ }
+ ppia = &port->actions_list;
+ while (*ppia && (*ppia)->id > id)
+ ppia = &(*ppia)->next;
+ if (*ppia && (*ppia)->id == id) {
+ fprintf(stderr,
+ "Indirect action #%u is already assigned, delete it first\n",
+ id);
+ free(pia);
+ return -EINVAL;
+ }
+ pia->next = *ppia;
+ pia->id = id;
+ *ppia = pia;
+ *action = pia;
+ return 0;
+}
+
+/** Create indirect action */
+int
+port_action_handle_create(portid_t port_id, uint32_t id,
+ const struct rte_flow_indir_action_conf *conf,
+ const struct rte_flow_action *action)
+{
+ struct port_indirect_action *pia;
+ int ret;
+ struct rte_flow_error error;
+
+ ret = action_alloc(port_id, id, &pia);
+ if (ret)
+ return ret;
+ if (action->type == RTE_FLOW_ACTION_TYPE_AGE) {
+ struct rte_flow_action_age *age =
+ (struct rte_flow_action_age *)(uintptr_t)(action->conf);
+
+ pia->age_type = ACTION_AGE_CONTEXT_TYPE_INDIRECT_ACTION;
+ age->context = &pia->age_type;
+ } else if (action->type == RTE_FLOW_ACTION_TYPE_CONNTRACK) {
+ struct rte_flow_action_conntrack *ct =
+ (struct rte_flow_action_conntrack *)(uintptr_t)(action->conf);
+
+ memcpy(ct, &conntrack_context, sizeof(*ct));
+ }
+ /* Poisoning to make sure PMDs update it in case of error. */
+ memset(&error, 0x22, sizeof(error));
+ pia->handle = rte_flow_action_handle_create(port_id, conf, action,
+ &error);
+ if (!pia->handle) {
+ uint32_t destroy_id = pia->id;
+ port_action_handle_destroy(port_id, 1, &destroy_id);
+ return port_flow_complain(&error);
+ }
+ pia->type = action->type;
+ printf("Indirect action #%u created\n", pia->id);
+ return 0;
+}
+
+/** Destroy indirect action */
+int
+port_action_handle_destroy(portid_t port_id,
+ uint32_t n,
+ const uint32_t *actions)
+{
+ struct rte_port *port;
+ struct port_indirect_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_indirect_action *pia = *tmp;
+
+ if (actions[i] != pia->id)
+ continue;
+ /*
+ * Poisoning to make sure PMDs update it in case
+ * of error.
+ */
+ memset(&error, 0x33, sizeof(error));
+
+ if (pia->handle && rte_flow_action_handle_destroy(
+ port_id, pia->handle, &error)) {
+ ret = port_flow_complain(&error);
+ continue;
+ }
+ *tmp = pia->next;
+ printf("Indirect action #%u destroyed\n", pia->id);
+ free(pia);
+ break;
+ }
+ if (i == n)
+ tmp = &(*tmp)->next;
+ ++c;
+ }
+ return ret;
+}
+
+
+/** Get indirect action by port + id */
+struct rte_flow_action_handle *
+port_action_handle_get_by_id(portid_t port_id, uint32_t id)
+{
+
+ struct port_indirect_action *pia = action_get_by_id(port_id, id);
+
+ return (pia) ? pia->handle : NULL;
+}
+
+/** Update indirect action */
+int
+port_action_handle_update(portid_t port_id, uint32_t id,
+ const struct rte_flow_action *action)
+{
+ struct rte_flow_error error;
+ struct rte_flow_action_handle *action_handle;
+ struct port_indirect_action *pia;
+ const void *update;
+
+ action_handle = port_action_handle_get_by_id(port_id, id);
+ if (!action_handle)
+ return -EINVAL;
+ pia = action_get_by_id(port_id, id);
+ if (!pia)
+ return -EINVAL;
+ switch (pia->type) {
+ case RTE_FLOW_ACTION_TYPE_CONNTRACK:
+ update = action->conf;
+ break;
+ default:
+ update = action;
+ break;
+ }
+ if (rte_flow_action_handle_update(port_id, action_handle, update,
+ &error)) {
+ return port_flow_complain(&error);
+ }
+ printf("Indirect action #%u updated\n", id);
+ return 0;
+}
+
+int
+port_action_handle_query(portid_t port_id, uint32_t id)
+{
+ struct rte_flow_error error;
+ struct port_indirect_action *pia;
+ union {
+ struct rte_flow_query_count count;
+ struct rte_flow_query_age age;
+ struct rte_flow_action_conntrack ct;
+ } query;
+
+ pia = action_get_by_id(port_id, id);
+ if (!pia)
+ return -EINVAL;
+ switch (pia->type) {
+ case RTE_FLOW_ACTION_TYPE_AGE:
+ case RTE_FLOW_ACTION_TYPE_COUNT:
+ break;
+ default:
+ fprintf(stderr,
+ "Indirect action %u (type: %d) on port %u doesn't support query\n",
+ id, pia->type, port_id);
+ return -ENOTSUP;
+ }
+ /* Poisoning to make sure PMDs update it in case of error. */
+ memset(&error, 0x55, sizeof(error));
+ memset(&query, 0, sizeof(query));
+ if (rte_flow_action_handle_query(port_id, pia->handle, &query, &error))
+ return port_flow_complain(&error);
+ switch (pia->type) {
+ case RTE_FLOW_ACTION_TYPE_AGE:
+ printf("Indirect AGE action:\n"
+ " aged: %u\n"
+ " sec_since_last_hit_valid: %u\n"
+ " sec_since_last_hit: %" PRIu32 "\n",
+ query.age.aged,
+ query.age.sec_since_last_hit_valid,
+ query.age.sec_since_last_hit);
+ break;
+ case RTE_FLOW_ACTION_TYPE_COUNT:
+ printf("Indirect COUNT action:\n"
+ " hits_set: %u\n"
+ " bytes_set: %u\n"
+ " hits: %" PRIu64 "\n"
+ " bytes: %" PRIu64 "\n",
+ query.count.hits_set,
+ query.count.bytes_set,
+ query.count.hits,
+ query.count.bytes);
+ break;
+ case RTE_FLOW_ACTION_TYPE_CONNTRACK:
+ printf("Conntrack Context:\n"
+ " Peer: %u, Flow dir: %s, Enable: %u\n"
+ " Live: %u, SACK: %u, CACK: %u\n"
+ " Packet dir: %s, Liberal: %u, State: %u\n"
+ " Factor: %u, Retrans: %u, TCP flags: %u\n"
+ " Last Seq: %u, Last ACK: %u\n"
+ " Last Win: %u, Last End: %u\n",
+ query.ct.peer_port,
+ query.ct.is_original_dir ? "Original" : "Reply",
+ query.ct.enable, query.ct.live_connection,
+ query.ct.selective_ack, query.ct.challenge_ack_passed,
+ query.ct.last_direction ? "Original" : "Reply",
+ query.ct.liberal_mode, query.ct.state,
+ query.ct.max_ack_window, query.ct.retransmission_limit,
+ query.ct.last_index, query.ct.last_seq,
+ query.ct.last_ack, query.ct.last_window,
+ query.ct.last_end);
+ printf(" Original Dir:\n"
+ " scale: %u, fin: %u, ack seen: %u\n"
+ " unacked data: %u\n Sent end: %u,"
+ " Reply end: %u, Max win: %u, Max ACK: %u\n",
+ query.ct.original_dir.scale,
+ query.ct.original_dir.close_initiated,
+ query.ct.original_dir.last_ack_seen,
+ query.ct.original_dir.data_unacked,
+ query.ct.original_dir.sent_end,
+ query.ct.original_dir.reply_end,
+ query.ct.original_dir.max_win,
+ query.ct.original_dir.max_ack);
+ printf(" Reply Dir:\n"
+ " scale: %u, fin: %u, ack seen: %u\n"
+ " unacked data: %u\n Sent end: %u,"
+ " Reply end: %u, Max win: %u, Max ACK: %u\n",
+ query.ct.reply_dir.scale,
+ query.ct.reply_dir.close_initiated,
+ query.ct.reply_dir.last_ack_seen,
+ query.ct.reply_dir.data_unacked,
+ query.ct.reply_dir.sent_end,
+ query.ct.reply_dir.reply_end,
+ query.ct.reply_dir.max_win,
+ query.ct.reply_dir.max_ack);