net/iavf: MDD fault diagnostics support on TX paths

Message ID 20231106100359.40424-1-mingjinx.ye@intel.com (mailing list archive)
State Superseded, archived
Delegated to: Qi Zhang
Headers
Series net/iavf: MDD fault diagnostics support on TX paths |

Checks

Context Check Description
ci/loongarch-compilation warning apply patch failure
ci/checkpatch success coding style OK

Commit Message

Mingjin Ye Nov. 6, 2023, 10:03 a.m. UTC
  When an MDD packet is detected, hardware will shutdown the queue.
In a Tx path troubleshooting scenario, modifying the application
code to reselect the Tx path is the only way to enable mbuf
legitimacy check, which makes troubleshooting difficult.

In this patch, the devargs option "mbuf_check" is introduced and the
corresponding diagnostic function is enabled by configuring MDD case.

Argument format: mbuf_check=generic,<mdd_case1>,<mdd_case2>
Currently support MDD case: generic, segment, offload, careful.

Signed-off-by: Mingjin Ye <mingjinx.ye@intel.com>
---
 drivers/net/iavf/iavf.h        |  26 +++++
 drivers/net/iavf/iavf_ethdev.c |  99 ++++++++++++++++++
 drivers/net/iavf/iavf_rxtx.c   | 182 +++++++++++++++++++++++++++++++++
 drivers/net/iavf/iavf_rxtx.h   |   4 +
 4 files changed, 311 insertions(+)
  

Patch

diff --git a/drivers/net/iavf/iavf.h b/drivers/net/iavf/iavf.h
index 04774ce124..ad46fdeddd 100644
--- a/drivers/net/iavf/iavf.h
+++ b/drivers/net/iavf/iavf.h
@@ -113,9 +113,15 @@  struct iavf_ipsec_crypto_stats {
 	} ierrors;
 };
 
+struct iavf_mdd_stats {
+	uint64_t mdd_mbuf_err_count;
+	uint64_t mdd_pkt_err_count;
+};
+
 struct iavf_eth_xstats {
 	struct virtchnl_eth_stats eth_stats;
 	struct iavf_ipsec_crypto_stats ips_stats;
+	struct iavf_mdd_stats mdd_stats;
 };
 
 /* Structure that defines a VSI, associated with a adapter. */
@@ -299,6 +305,13 @@  enum iavf_proto_xtr_type {
 	IAVF_PROTO_XTR_MAX,
 };
 
+enum iavf_mdd_check_type {
+	IAVF_MDD_CHECK_GENERAL,
+	IAVF_MDD_CHECK_SEGMENT,
+	IAVF_MDD_CHECK_OFFLOAD,
+	IAVF_MDD_CHECK_CAREFUL,
+};
+
 /**
  * Cache devargs parse result.
  */
@@ -308,10 +321,21 @@  struct iavf_devargs {
 	uint16_t quanta_size;
 	uint32_t watchdog_period;
 	uint8_t  auto_reset;
+	uint16_t mbuf_check;
 };
 
 struct iavf_security_ctx;
 
+struct iavf_tx_burst_element {
+	TAILQ_ENTRY(iavf_tx_burst_element) next;
+	eth_tx_burst_t tx_pkt_burst;
+};
+
+#define IAVF_MDD_CHECK_F_TX_GENERAL (1ULL << 0)
+#define IAVF_MDD_CHECK_F_TX_SEGMENT (1ULL << 1)
+#define IAVF_MDD_CHECK_F_TX_OFFLOAD (1ULL << 2)
+#define IAVF_MDD_CHECK_F_TX_CAREFUL (1ULL << 3)
+
 /* Structure to store private data for each VF instance. */
 struct iavf_adapter {
 	struct iavf_hw hw;
@@ -326,6 +350,8 @@  struct iavf_adapter {
 	uint32_t ptype_tbl[IAVF_MAX_PKT_TYPE] __rte_cache_min_aligned;
 	bool stopped;
 	bool closed;
+	uint64_t mc_flags; /* mdd check flags. */
+	TAILQ_HEAD(tx_pkt_burst_list, iavf_tx_burst_element) list_tx_pkt_burst;
 	uint16_t fdir_ref_cnt;
 	struct iavf_devargs devargs;
 };
diff --git a/drivers/net/iavf/iavf_ethdev.c b/drivers/net/iavf/iavf_ethdev.c
index 5b2634a4e3..cb0b7491e5 100644
--- a/drivers/net/iavf/iavf_ethdev.c
+++ b/drivers/net/iavf/iavf_ethdev.c
@@ -37,6 +37,7 @@ 
 #define IAVF_PROTO_XTR_ARG         "proto_xtr"
 #define IAVF_QUANTA_SIZE_ARG       "quanta_size"
 #define IAVF_RESET_WATCHDOG_ARG    "watchdog_period"
+#define IAVF_MDD_CHECK_ARG       "mbuf_check"
 #define IAVF_ENABLE_AUTO_RESET_ARG "auto_reset"
 
 uint64_t iavf_timestamp_dynflag;
@@ -46,6 +47,7 @@  static const char * const iavf_valid_args[] = {
 	IAVF_PROTO_XTR_ARG,
 	IAVF_QUANTA_SIZE_ARG,
 	IAVF_RESET_WATCHDOG_ARG,
+	IAVF_MDD_CHECK_ARG,
 	IAVF_ENABLE_AUTO_RESET_ARG,
 	NULL
 };
@@ -187,6 +189,8 @@  static const struct rte_iavf_xstats_name_off rte_iavf_stats_strings[] = {
 			_OFF_OF(ips_stats.ierrors.ipsec_length)},
 	{"inline_ipsec_crypto_ierrors_misc",
 			_OFF_OF(ips_stats.ierrors.misc)},
+	{"mdd_mbuf_error_packets", _OFF_OF(mdd_stats.mdd_mbuf_err_count)},
+	{"mdd_pkt_error_packets", _OFF_OF(mdd_stats.mdd_pkt_err_count)},
 };
 #undef _OFF_OF
 
@@ -1878,6 +1882,9 @@  static int iavf_dev_xstats_get(struct rte_eth_dev *dev,
 {
 	int ret;
 	unsigned int i;
+	struct iavf_tx_queue *txq;
+	uint64_t mdd_mbuf_err_count = 0;
+	uint64_t mdd_pkt_err_count = 0;
 	struct iavf_adapter *adapter =
 		IAVF_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
 	struct iavf_info *vf = IAVF_DEV_PRIVATE_TO_VF(dev->data->dev_private);
@@ -1901,6 +1908,17 @@  static int iavf_dev_xstats_get(struct rte_eth_dev *dev,
 	if (iavf_ipsec_crypto_supported(adapter))
 		iavf_dev_update_ipsec_xstats(dev, &iavf_xtats.ips_stats);
 
+
+	if (adapter->devargs.mbuf_check) {
+		for (i = 0; i < dev->data->nb_tx_queues; i++) {
+			txq = dev->data->tx_queues[i];
+			mdd_mbuf_err_count += txq->mdd_mbuf_err_count;
+			mdd_pkt_err_count += txq->mdd_pkt_err_count;
+		}
+		iavf_xtats.mdd_stats.mdd_mbuf_err_count = mdd_mbuf_err_count;
+		iavf_xtats.mdd_stats.mdd_pkt_err_count = mdd_pkt_err_count;
+	}
+
 	/* loop over xstats array and values from pstats */
 	for (i = 0; i < IAVF_NB_XSTATS; i++) {
 		xstats[i].id = i;
@@ -2283,6 +2301,78 @@  iavf_parse_watchdog_period(__rte_unused const char *key, const char *value, void
 	return 0;
 }
 
+static int
+iavf_parse_mdd_checker(__rte_unused const char *key, const char *value, void *args)
+{
+	static struct {
+		const char *name;
+		enum iavf_mdd_check_type type;
+	} mdd_check_type_map[] = {
+		{ "general", IAVF_MDD_CHECK_GENERAL},
+		{ "segment", IAVF_MDD_CHECK_SEGMENT},
+		{ "offload", IAVF_MDD_CHECK_OFFLOAD},
+		{ "careful", IAVF_MDD_CHECK_CAREFUL},
+	};
+
+	uint64_t *mc_flags = args;
+	int i, j;
+	char *temp_value;
+	int len_value;
+	char **check_list;
+	int check_number, count;
+	char *temp_check;
+	bool is_found = false;
+
+	temp_value = strdup(value);
+	if (temp_value == NULL)
+		return -1;
+
+	len_value = strlen(temp_value);
+	check_number = ARRAY_SIZE(mdd_check_type_map);
+	check_list = malloc(sizeof(char *) * check_number);
+
+	count = rte_strsplit(temp_value, len_value + 1, check_list,
+		check_number, ',');
+
+	if (count <= 0) {
+		free(temp_value);
+		free(check_list);
+		return 0;
+	}
+
+	for (i = 0; i < count; i++) {
+		is_found = false;
+		temp_check = *check_list + i;
+		for (j = 0; j < check_number; j++) {
+			if (!strcmp(mdd_check_type_map[i].name, temp_check)) {
+				is_found = true;
+				switch (mdd_check_type_map[i].type) {
+				case IAVF_MDD_CHECK_GENERAL:
+					*mc_flags |= IAVF_MDD_CHECK_F_TX_GENERAL;
+					break;
+				case IAVF_MDD_CHECK_SEGMENT:
+					*mc_flags |= IAVF_MDD_CHECK_F_TX_SEGMENT;
+					break;
+				case IAVF_MDD_CHECK_OFFLOAD:
+					*mc_flags |= IAVF_MDD_CHECK_F_TX_OFFLOAD;
+					break;
+				case IAVF_MDD_CHECK_CAREFUL:
+					*mc_flags |= IAVF_MDD_CHECK_F_TX_CAREFUL;
+					break;
+				}
+				break;
+			}
+		}
+		if (!is_found)
+			PMD_DRV_LOG(ERR, "Unsupported mdd check type: %s", temp_check);
+	}
+
+	free(check_list);
+	free(temp_value);
+
+	return 0;
+}
+
 static int iavf_parse_devargs(struct rte_eth_dev *dev)
 {
 	struct iavf_adapter *ad =
@@ -2332,6 +2422,14 @@  static int iavf_parse_devargs(struct rte_eth_dev *dev)
 		goto bail;
 	}
 
+	ret = rte_kvargs_process(kvlist, IAVF_MDD_CHECK_ARG,
+				 &iavf_parse_mdd_checker, &ad->mc_flags);
+	if (ret)
+		goto bail;
+
+	if (ad->mc_flags)
+		ad->devargs.mbuf_check = 1;
+
 	ret = rte_kvargs_process(kvlist, IAVF_ENABLE_AUTO_RESET_ARG,
 				 &parse_bool, &ad->devargs.auto_reset);
 	if (ret)
@@ -2706,6 +2804,7 @@  iavf_dev_init(struct rte_eth_dev *eth_dev)
 	hw->back = IAVF_DEV_PRIVATE_TO_ADAPTER(eth_dev->data->dev_private);
 	adapter->dev_data = eth_dev->data;
 	adapter->stopped = 1;
+	TAILQ_INIT(&adapter->list_tx_pkt_burst);
 
 	if (iavf_dev_event_handler_init())
 		goto init_vf_err;
diff --git a/drivers/net/iavf/iavf_rxtx.c b/drivers/net/iavf/iavf_rxtx.c
index c6ef6af1d8..da866462be 100644
--- a/drivers/net/iavf/iavf_rxtx.c
+++ b/drivers/net/iavf/iavf_rxtx.c
@@ -18,6 +18,7 @@ 
 #include <rte_malloc.h>
 #include <rte_ether.h>
 #include <ethdev_driver.h>
+#include <rte_tailq.h>
 #include <rte_tcp.h>
 #include <rte_sctp.h>
 #include <rte_udp.h>
@@ -778,6 +779,7 @@  iavf_dev_tx_queue_setup(struct rte_eth_dev *dev,
 	struct iavf_info *vf =
 		IAVF_DEV_PRIVATE_TO_VF(dev->data->dev_private);
 	struct iavf_tx_queue *txq;
+	struct iavf_vsi *vsi = &vf->vsi;
 	const struct rte_memzone *mz;
 	uint32_t ring_size;
 	uint16_t tx_rs_thresh, tx_free_thresh;
@@ -850,6 +852,7 @@  iavf_dev_tx_queue_setup(struct rte_eth_dev *dev,
 	txq->port_id = dev->data->port_id;
 	txq->offloads = offloads;
 	txq->tx_deferred_start = tx_conf->tx_deferred_start;
+	txq->vsi = vsi;
 
 	if (iavf_ipsec_crypto_supported(adapter))
 		txq->ipsec_crypto_pkt_md_offset =
@@ -1126,6 +1129,19 @@  iavf_reset_queues(struct rte_eth_dev *dev)
 	}
 }
 
+static int
+iavf_tx_pkt_burst_clean(struct iavf_adapter *adapter)
+{
+	struct iavf_tx_burst_element *pos;
+	struct iavf_tx_burst_element *save_next;
+
+	RTE_TAILQ_FOREACH_SAFE(pos, &adapter->list_tx_pkt_burst, next, save_next) {
+		TAILQ_REMOVE(&adapter->list_tx_pkt_burst, pos, next);
+		rte_free(pos);
+	}
+	return 0;
+}
+
 void
 iavf_stop_queues(struct rte_eth_dev *dev)
 {
@@ -1155,6 +1171,8 @@  iavf_stop_queues(struct rte_eth_dev *dev)
 		PMD_DRV_LOG(WARNING, "Fail to stop queues");
 
 	iavf_reset_queues(dev);
+
+	iavf_tx_pkt_burst_clean(adapter);
 }
 
 #define IAVF_RX_FLEX_ERR0_BITS	\
@@ -3626,6 +3644,99 @@  iavf_check_mbuf(struct rte_mbuf *m)
 	return check_ether_type(&info, m);
 }
 
+/* Tx MDD check */
+static uint16_t
+iavf_xmit_pkts_mdd(void *tx_queue, struct rte_mbuf **tx_pkts,
+	      uint16_t nb_pkts)
+{
+	struct iavf_tx_queue *txq = tx_queue;
+	struct rte_mbuf *mb;
+	uint16_t idx;
+	const char *reason = NULL;
+	struct iavf_adapter *adapter = txq->vsi->adapter;
+	uint64_t mdd_mbuf_err_count = 0;
+	uint64_t mdd_pkt_err_count = 0;
+	uint64_t ol_flags;
+	uint16_t nb_desc_ctx, nb_desc_ipsec;
+	uint16_t nb_desc_required;
+
+	for (idx = 0; idx < nb_pkts; idx++) {
+		mb = tx_pkts[idx];
+		ol_flags = mb->ol_flags;
+
+		if (adapter->mc_flags & IAVF_MDD_CHECK_F_TX_GENERAL) {
+			if (rte_mbuf_check(mb, 0, &reason) != 0) {
+				mdd_mbuf_err_count++;
+				continue;
+			}
+			if (mb->data_len > mb->pkt_len ||
+				mb->data_len < IAVF_TX_MIN_PKT_LEN ||
+				mb->data_len > adapter->vf.max_pkt_len) {
+				mdd_pkt_err_count++;
+				continue;
+			}
+		}
+
+
+		if (adapter->mc_flags & IAVF_MDD_CHECK_F_TX_SEGMENT) {
+			if (!(ol_flags & (RTE_MBUF_F_TX_TCP_SEG | RTE_MBUF_F_TX_UDP_SEG))) {
+				/* Check condition for nb_segs > IAVF_TX_MAX_MTU_SEG. */
+				if (mb->nb_segs > IAVF_TX_MAX_MTU_SEG) {
+					mdd_pkt_err_count++;
+					continue;
+				}
+
+				/* TSO: Headers are spread on more than 3 descriptors */
+				nb_desc_ctx =
+					iavf_calc_context_desc(mb->ol_flags, txq->vlan_flag);
+				nb_desc_ipsec = !!(mb->ol_flags & RTE_MBUF_F_TX_SEC_OFFLOAD);
+				nb_desc_required = iavf_calc_pkt_desc(mb) + nb_desc_ctx +
+							nb_desc_ipsec;
+				if (nb_desc_required > 3) {
+					mdd_pkt_err_count++;
+					continue;
+				}
+			} else if ((mb->tso_segsz < IAVF_MIN_TSO_MSS) ||
+				(mb->tso_segsz > IAVF_MAX_TSO_MSS)) {
+				/* MSS outside the range are considered malicious */
+				mdd_pkt_err_count++;
+				continue;
+			}
+		}
+
+		if (adapter->mc_flags & IAVF_MDD_CHECK_F_TX_OFFLOAD) {
+			if (ol_flags & IAVF_TX_OFFLOAD_NOTSUP_MASK) {
+				mdd_pkt_err_count++;
+				continue;
+			}
+
+			if (!rte_validate_tx_offload(mb)) {
+				mdd_pkt_err_count++;
+				continue;
+			}
+		}
+
+		if (adapter->mc_flags & IAVF_MDD_CHECK_F_TX_CAREFUL) {
+			if (!iavf_check_mbuf(mb)) {
+				mdd_pkt_err_count++;
+				continue;
+			}
+		}
+	}
+
+	if (mdd_mbuf_err_count || mdd_pkt_err_count) {
+		if (mdd_mbuf_err_count)
+			rte_atomic_fetch_add_explicit(&txq->mdd_mbuf_err_count,
+					mdd_mbuf_err_count, rte_memory_order_release);
+		if (mdd_pkt_err_count)
+			rte_atomic_fetch_add_explicit(&txq->mdd_pkt_err_count,
+					mdd_pkt_err_count, rte_memory_order_release);
+		return 0;
+	}
+
+	return idx;
+}
+
 /* TX prep functions */
 uint16_t
 iavf_prep_pkts(__rte_unused void *tx_queue, struct rte_mbuf **tx_pkts,
@@ -3703,6 +3814,73 @@  iavf_prep_pkts(__rte_unused void *tx_queue, struct rte_mbuf **tx_pkts,
 	return i;
 }
 
+static int
+iavf_tx_pkt_burst_insert(struct iavf_adapter *adapter, eth_tx_burst_t func)
+{
+	struct iavf_tx_burst_element *elem;
+
+	if (!func) {
+		PMD_DRV_LOG(ERR, "Callback functions cannot be NULL");
+		return -1;
+	}
+
+	elem = rte_malloc(NULL, sizeof(*elem), 0);
+	if (!elem) {
+		PMD_DRV_LOG(ERR, "Unable to allocate memory");
+		return -1;
+	}
+
+	elem->tx_pkt_burst = func;
+	TAILQ_INSERT_TAIL(&adapter->list_tx_pkt_burst, elem, next);
+
+	return 0;
+}
+
+static uint16_t
+iavf_xmit_pkts_chain(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkts)
+{
+	struct iavf_tx_queue *txq = tx_queue;
+	struct iavf_adapter *adapter = txq->vsi->adapter;
+	struct iavf_tx_burst_element *pos;
+	struct iavf_tx_burst_element *save_next;
+	uint16_t ret;
+
+	RTE_TAILQ_FOREACH_SAFE(pos, &adapter->list_tx_pkt_burst, next, save_next) {
+		ret = pos->tx_pkt_burst(tx_queue, tx_pkts, nb_pkts);
+		if (nb_pkts != ret)
+			break;
+	}
+
+	return ret;
+}
+
+/* choose tx interceptors*/
+static void
+iavf_set_tx_interceptors(struct rte_eth_dev *dev)
+{
+	struct iavf_adapter *adapter =
+		IAVF_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
+	eth_tx_burst_t tx_pkt_burst;
+	bool mdd_check = adapter->devargs.mbuf_check;
+
+	if (!mdd_check)
+		return;
+
+	/* Replace tx_pkt_burst in struct rte_eth_dev to
+	 * intercept the purpose of the default TX path.
+	 * All tasks are done at iavf_xmit_pkts_chain.
+	 */
+	tx_pkt_burst = dev->tx_pkt_burst;
+	dev->tx_pkt_burst = iavf_xmit_pkts_chain;
+
+	/* Register all interceptors. We need to pay
+	 * attention to the order of precedence.
+	 */
+	iavf_tx_pkt_burst_insert(adapter, iavf_xmit_pkts_mdd);
+
+	iavf_tx_pkt_burst_insert(adapter, tx_pkt_burst);
+}
+
 /* choose rx function*/
 void
 iavf_set_rx_function(struct rte_eth_dev *dev)
@@ -4018,6 +4196,8 @@  iavf_set_tx_function(struct rte_eth_dev *dev)
 #endif
 		}
 
+		iavf_set_tx_interceptors(dev);
+
 		return;
 	}
 
@@ -4027,6 +4207,8 @@  iavf_set_tx_function(struct rte_eth_dev *dev)
 		    dev->data->port_id);
 	dev->tx_pkt_burst = iavf_xmit_pkts;
 	dev->tx_pkt_prepare = iavf_prep_pkts;
+
+	iavf_set_tx_interceptors(dev);
 }
 
 static int
diff --git a/drivers/net/iavf/iavf_rxtx.h b/drivers/net/iavf/iavf_rxtx.h
index 605ea3f824..251e146eb5 100644
--- a/drivers/net/iavf/iavf_rxtx.h
+++ b/drivers/net/iavf/iavf_rxtx.h
@@ -294,8 +294,12 @@  struct iavf_tx_queue {
 	uint64_t offloads;
 	uint16_t next_dd;              /* next to set RS, for VPMD */
 	uint16_t next_rs;              /* next to check DD,  for VPMD */
+	struct iavf_vsi *vsi;          /**< the VSI this queue belongs to */
 	uint16_t ipsec_crypto_pkt_md_offset;
 
+	uint64_t mdd_mbuf_err_count;
+	uint64_t mdd_pkt_err_count;
+
 	bool q_set;                    /* if rx queue has been configured */
 	bool tx_deferred_start;        /* don't start this queue in dev start */
 	const struct iavf_txq_ops *ops;