+/** Print a message out of a flow error. */
+static int
+port_flow_complain(struct rte_flow_error *error)
+{
+ static const char *const errstrlist[] = {
+ [RTE_FLOW_ERROR_TYPE_NONE] = "no error",
+ [RTE_FLOW_ERROR_TYPE_UNSPECIFIED] = "cause unspecified",
+ [RTE_FLOW_ERROR_TYPE_HANDLE] = "flow rule (handle)",
+ [RTE_FLOW_ERROR_TYPE_ATTR_GROUP] = "group field",
+ [RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY] = "priority field",
+ [RTE_FLOW_ERROR_TYPE_ATTR_INGRESS] = "ingress field",
+ [RTE_FLOW_ERROR_TYPE_ATTR_EGRESS] = "egress field",
+ [RTE_FLOW_ERROR_TYPE_ATTR] = "attributes structure",
+ [RTE_FLOW_ERROR_TYPE_ITEM_NUM] = "pattern length",
+ [RTE_FLOW_ERROR_TYPE_ITEM] = "specific pattern item",
+ [RTE_FLOW_ERROR_TYPE_ACTION_NUM] = "number of actions",
+ [RTE_FLOW_ERROR_TYPE_ACTION] = "specific action",
+ };
+ const char *errstr;
+ char buf[32];
+ int err = rte_errno;
+
+ if ((unsigned int)error->type >= RTE_DIM(errstrlist) ||
+ !errstrlist[error->type])
+ errstr = "unknown type";
+ else
+ errstr = errstrlist[error->type];
+ printf("Caught error type %d (%s): %s%s\n",
+ error->type, errstr,
+ error->cause ? (snprintf(buf, sizeof(buf), "cause: %p, ",
+ error->cause), buf) : "",
+ error->message ? error->message : "(no stated reason)");
+ return -err;
+}
+
+/** Validate flow rule. */
+int
+port_flow_validate(portid_t port_id,
+ const struct rte_flow_attr *attr,
+ const struct rte_flow_item *pattern,
+ const struct rte_flow_action *actions)
+{
+ struct rte_flow_error error;
+
+ /* Poisoning to make sure PMDs update it in case of error. */
+ memset(&error, 0x11, sizeof(error));
+ if (rte_flow_validate(port_id, attr, pattern, actions, &error))
+ return port_flow_complain(&error);
+ printf("Flow rule validated\n");
+ return 0;
+}
+
+/** Create flow rule. */
+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)
+{
+ struct rte_flow *flow;
+ struct rte_port *port;
+ struct port_flow *pf;
+ uint32_t id;
+ struct rte_flow_error error;
+
+ /* Poisoning to make sure PMDs update it in case of error. */
+ memset(&error, 0x22, sizeof(error));
+ flow = rte_flow_create(port_id, attr, pattern, actions, &error);
+ if (!flow)
+ return port_flow_complain(&error);
+ port = &ports[port_id];
+ if (port->flow_list) {
+ if (port->flow_list->id == UINT32_MAX) {
+ printf("Highest rule ID is already assigned, delete"
+ " it first");
+ rte_flow_destroy(port_id, flow, NULL);
+ return -ENOMEM;
+ }
+ id = port->flow_list->id + 1;
+ } else
+ id = 0;
+ pf = port_flow_new(attr, pattern, actions);
+ if (!pf) {
+ int err = rte_errno;
+
+ printf("Cannot allocate flow: %s\n", rte_strerror(err));
+ rte_flow_destroy(port_id, flow, NULL);
+ return -err;
+ }
+ pf->next = port->flow_list;
+ pf->id = id;
+ pf->flow = flow;
+ port->flow_list = pf;
+ printf("Flow rule #%u created\n", pf->id);
+ return 0;
+}
+
+/** Destroy a number of flow rules. */
+int
+port_flow_destroy(portid_t port_id, uint32_t n, const uint32_t *rule)
+{
+ struct rte_port *port;
+ struct port_flow **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->flow_list;
+ while (*tmp) {
+ uint32_t i;
+
+ for (i = 0; i != n; ++i) {
+ struct rte_flow_error error;
+ struct port_flow *pf = *tmp;
+
+ if (rule[i] != pf->id)
+ continue;
+ /*
+ * Poisoning to make sure PMDs update it in case
+ * of error.
+ */
+ memset(&error, 0x33, sizeof(error));
+ if (rte_flow_destroy(port_id, pf->flow, &error)) {
+ ret = port_flow_complain(&error);
+ continue;
+ }
+ printf("Flow rule #%u destroyed\n", pf->id);
+ *tmp = pf->next;
+ free(pf);
+ break;
+ }
+ if (i == n)
+ tmp = &(*tmp)->next;
+ ++c;
+ }
+ return ret;
+}
+
+/** Remove all flow rules. */
+int
+port_flow_flush(portid_t port_id)
+{
+ struct rte_flow_error error;
+ struct rte_port *port;
+ int ret = 0;
+
+ /* Poisoning to make sure PMDs update it in case of error. */
+ memset(&error, 0x44, sizeof(error));
+ if (rte_flow_flush(port_id, &error)) {
+ ret = port_flow_complain(&error);
+ if (port_id_is_invalid(port_id, DISABLED_WARN) ||
+ port_id == (portid_t)RTE_PORT_ALL)
+ return ret;
+ }
+ port = &ports[port_id];
+ while (port->flow_list) {
+ struct port_flow *pf = port->flow_list->next;
+
+ free(port->flow_list);
+ port->flow_list = pf;
+ }
+ return ret;
+}
+
+/** Query a flow rule. */
+int
+port_flow_query(portid_t port_id, uint32_t rule,
+ enum rte_flow_action_type action)
+{
+ struct rte_flow_error error;
+ struct rte_port *port;
+ struct port_flow *pf;
+ const char *name;
+ union {
+ struct rte_flow_query_count count;
+ } query;
+
+ if (port_id_is_invalid(port_id, ENABLED_WARN) ||
+ port_id == (portid_t)RTE_PORT_ALL)
+ return -EINVAL;
+ port = &ports[port_id];
+ for (pf = port->flow_list; pf; pf = pf->next)
+ if (pf->id == rule)
+ break;
+ if (!pf) {
+ printf("Flow rule #%u not found\n", rule);
+ return -ENOENT;
+ }
+ if ((unsigned int)action >= RTE_DIM(flow_action) ||
+ !flow_action[action].name)
+ name = "unknown";
+ else
+ name = flow_action[action].name;
+ switch (action) {
+ case RTE_FLOW_ACTION_TYPE_COUNT:
+ break;
+ default:
+ printf("Cannot query action type %d (%s)\n", action, name);
+ 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_query(port_id, pf->flow, action, &query, &error))
+ return port_flow_complain(&error);
+ switch (action) {
+ case RTE_FLOW_ACTION_TYPE_COUNT:
+ printf("%s:\n"
+ " hits_set: %u\n"
+ " bytes_set: %u\n"
+ " hits: %" PRIu64 "\n"
+ " bytes: %" PRIu64 "\n",
+ name,
+ query.count.hits_set,
+ query.count.bytes_set,
+ query.count.hits,
+ query.count.bytes);
+ break;
+ default:
+ printf("Cannot display result for action type %d (%s)\n",
+ action, name);
+ break;
+ }
+ return 0;
+}
+
+/** List flow rules. */
+void
+port_flow_list(portid_t port_id, uint32_t n, const uint32_t group[n])
+{
+ struct rte_port *port;
+ struct port_flow *pf;
+ struct port_flow *list = NULL;
+ uint32_t i;
+
+ if (port_id_is_invalid(port_id, ENABLED_WARN) ||
+ port_id == (portid_t)RTE_PORT_ALL)
+ return;
+ port = &ports[port_id];
+ if (!port->flow_list)
+ return;
+ /* Sort flows by group, priority and ID. */
+ for (pf = port->flow_list; pf != NULL; pf = pf->next) {
+ struct port_flow **tmp;
+
+ if (n) {
+ /* Filter out unwanted groups. */
+ for (i = 0; i != n; ++i)
+ if (pf->attr.group == group[i])
+ break;
+ if (i == n)
+ continue;
+ }
+ tmp = &list;
+ while (*tmp &&
+ (pf->attr.group > (*tmp)->attr.group ||
+ (pf->attr.group == (*tmp)->attr.group &&
+ pf->attr.priority > (*tmp)->attr.priority) ||
+ (pf->attr.group == (*tmp)->attr.group &&
+ pf->attr.priority == (*tmp)->attr.priority &&
+ pf->id > (*tmp)->id)))
+ tmp = &(*tmp)->tmp;
+ pf->tmp = *tmp;
+ *tmp = pf;
+ }
+ printf("ID\tGroup\tPrio\tAttr\tRule\n");
+ for (pf = list; pf != NULL; pf = pf->tmp) {
+ const struct rte_flow_item *item = pf->pattern;
+ const struct rte_flow_action *action = pf->actions;
+
+ printf("%" PRIu32 "\t%" PRIu32 "\t%" PRIu32 "\t%c%c\t",
+ pf->id,
+ pf->attr.group,
+ pf->attr.priority,
+ pf->attr.ingress ? 'i' : '-',
+ pf->attr.egress ? 'e' : '-');
+ while (item->type != RTE_FLOW_ITEM_TYPE_END) {
+ if (item->type != RTE_FLOW_ITEM_TYPE_VOID)
+ printf("%s ", flow_item[item->type].name);
+ ++item;
+ }
+ printf("=>");
+ while (action->type != RTE_FLOW_ACTION_TYPE_END) {
+ if (action->type != RTE_FLOW_ACTION_TYPE_VOID)
+ printf(" %s", flow_action[action->type].name);
+ ++action;
+ }
+ printf("\n");
+ }
+}
+
+/** Restrict ingress traffic to the defined flow rules. */
+int
+port_flow_isolate(portid_t port_id, int set)
+{
+ struct rte_flow_error error;
+
+ /* Poisoning to make sure PMDs update it in case of error. */
+ memset(&error, 0x66, sizeof(error));
+ if (rte_flow_isolate(port_id, set, &error))
+ return port_flow_complain(&error);
+ printf("Ingress traffic on port %u is %s to the defined flow rules\n",
+ port_id,
+ set ? "now restricted" : "not restricted anymore");
+ return 0;
+}
+
+/*
+ * RX/TX ring descriptors display functions.
+ */
+int
+rx_queue_id_is_invalid(queueid_t rxq_id)
+{
+ if (rxq_id < nb_rxq)
+ return 0;
+ printf("Invalid RX queue %d (must be < nb_rxq=%d)\n", rxq_id, nb_rxq);
+ return 1;
+}
+
+int
+tx_queue_id_is_invalid(queueid_t txq_id)
+{
+ if (txq_id < nb_txq)
+ return 0;
+ printf("Invalid TX queue %d (must be < nb_rxq=%d)\n", txq_id, nb_txq);
+ return 1;
+}
+
+static int
+rx_desc_id_is_invalid(uint16_t rxdesc_id)
+{
+ if (rxdesc_id < nb_rxd)
+ return 0;
+ printf("Invalid RX descriptor %d (must be < nb_rxd=%d)\n",
+ rxdesc_id, nb_rxd);
+ return 1;
+}
+
+static int
+tx_desc_id_is_invalid(uint16_t txdesc_id)
+{
+ if (txdesc_id < nb_txd)
+ return 0;
+ printf("Invalid TX descriptor %d (must be < nb_txd=%d)\n",
+ txdesc_id, nb_txd);
+ return 1;
+}
+
+static const struct rte_memzone *
+ring_dma_zone_lookup(const char *ring_name, uint8_t port_id, uint16_t q_id)
+{
+ char mz_name[RTE_MEMZONE_NAMESIZE];
+ const struct rte_memzone *mz;
+
+ snprintf(mz_name, sizeof(mz_name), "%s_%s_%d_%d",
+ ports[port_id].dev_info.driver_name, ring_name, port_id, q_id);
+ mz = rte_memzone_lookup(mz_name);
+ if (mz == NULL)
+ printf("%s ring memory zoneof (port %d, queue %d) not"
+ "found (zone name = %s\n",
+ ring_name, port_id, q_id, mz_name);
+ return mz;
+}
+
+union igb_ring_dword {
+ uint64_t dword;
+ struct {
+#if RTE_BYTE_ORDER == RTE_BIG_ENDIAN
+ uint32_t lo;
+ uint32_t hi;
+#else
+ uint32_t hi;
+ uint32_t lo;
+#endif
+ } words;
+};
+
+struct igb_ring_desc_32_bytes {
+ union igb_ring_dword lo_dword;
+ union igb_ring_dword hi_dword;
+ union igb_ring_dword resv1;
+ union igb_ring_dword resv2;
+};
+
+struct igb_ring_desc_16_bytes {
+ union igb_ring_dword lo_dword;
+ union igb_ring_dword hi_dword;
+};
+
+static void
+ring_rxd_display_dword(union igb_ring_dword dword)
+{
+ printf(" 0x%08X - 0x%08X\n", (unsigned)dword.words.lo,
+ (unsigned)dword.words.hi);
+}
+
+static void
+ring_rx_descriptor_display(const struct rte_memzone *ring_mz,
+#ifndef RTE_LIBRTE_I40E_16BYTE_RX_DESC
+ uint8_t port_id,
+#else
+ __rte_unused uint8_t port_id,
+#endif
+ uint16_t desc_id)
+{
+ struct igb_ring_desc_16_bytes *ring =
+ (struct igb_ring_desc_16_bytes *)ring_mz->addr;
+#ifndef RTE_LIBRTE_I40E_16BYTE_RX_DESC
+ struct rte_eth_dev_info dev_info;
+
+ memset(&dev_info, 0, sizeof(dev_info));
+ rte_eth_dev_info_get(port_id, &dev_info);
+ if (strstr(dev_info.driver_name, "i40e") != NULL) {
+ /* 32 bytes RX descriptor, i40e only */
+ struct igb_ring_desc_32_bytes *ring =
+ (struct igb_ring_desc_32_bytes *)ring_mz->addr;
+ ring[desc_id].lo_dword.dword =
+ rte_le_to_cpu_64(ring[desc_id].lo_dword.dword);
+ ring_rxd_display_dword(ring[desc_id].lo_dword);
+ ring[desc_id].hi_dword.dword =
+ rte_le_to_cpu_64(ring[desc_id].hi_dword.dword);
+ ring_rxd_display_dword(ring[desc_id].hi_dword);
+ ring[desc_id].resv1.dword =
+ rte_le_to_cpu_64(ring[desc_id].resv1.dword);
+ ring_rxd_display_dword(ring[desc_id].resv1);
+ ring[desc_id].resv2.dword =
+ rte_le_to_cpu_64(ring[desc_id].resv2.dword);
+ ring_rxd_display_dword(ring[desc_id].resv2);
+
+ return;
+ }
+#endif
+ /* 16 bytes RX descriptor */
+ ring[desc_id].lo_dword.dword =
+ rte_le_to_cpu_64(ring[desc_id].lo_dword.dword);
+ ring_rxd_display_dword(ring[desc_id].lo_dword);
+ ring[desc_id].hi_dword.dword =
+ rte_le_to_cpu_64(ring[desc_id].hi_dword.dword);
+ ring_rxd_display_dword(ring[desc_id].hi_dword);
+}
+
+static void
+ring_tx_descriptor_display(const struct rte_memzone *ring_mz, uint16_t desc_id)
+{
+ struct igb_ring_desc_16_bytes *ring;
+ struct igb_ring_desc_16_bytes txd;
+
+ ring = (struct igb_ring_desc_16_bytes *)ring_mz->addr;
+ txd.lo_dword.dword = rte_le_to_cpu_64(ring[desc_id].lo_dword.dword);
+ txd.hi_dword.dword = rte_le_to_cpu_64(ring[desc_id].hi_dword.dword);
+ printf(" 0x%08X - 0x%08X / 0x%08X - 0x%08X\n",
+ (unsigned)txd.lo_dword.words.lo,
+ (unsigned)txd.lo_dword.words.hi,
+ (unsigned)txd.hi_dword.words.lo,
+ (unsigned)txd.hi_dword.words.hi);
+}
+
+void
+rx_ring_desc_display(portid_t port_id, queueid_t rxq_id, uint16_t rxd_id)
+{
+ const struct rte_memzone *rx_mz;
+
+ if (port_id_is_invalid(port_id, ENABLED_WARN))
+ return;
+ if (rx_queue_id_is_invalid(rxq_id))
+ return;
+ if (rx_desc_id_is_invalid(rxd_id))
+ return;
+ rx_mz = ring_dma_zone_lookup("rx_ring", port_id, rxq_id);
+ if (rx_mz == NULL)
+ return;
+ ring_rx_descriptor_display(rx_mz, port_id, rxd_id);