DPDK patches and discussions
 help / color / mirror / Atom feed
* [dpdk-dev] [PATCH] net/i40e: add checking for messages from VF
@ 2019-08-20  7:18 alvinx.zhang
  2019-08-22  4:18 ` Ye Xiaolong
  2019-08-28  7:13 ` [dpdk-dev] [PATCH v2] " alvinx.zhang
  0 siblings, 2 replies; 14+ messages in thread
From: alvinx.zhang @ 2019-08-20  7:18 UTC (permalink / raw)
  To: beilei.xing; +Cc: dev, Alvin Zhang

From: Alvin Zhang <alvinx.zhang@intel.com>

If VF driver in VM continuous sending invalid messages by mailbox,
it will waste CPU cycles on PF driver and impact other VF drivers
configuration. New feature can count the numbers of invalid and
unsupported messages from VFs, when the statistics from a VF
exceed maximum limit, PF driver will ignore any message from that
VF for some seconds.

Signed-off-by: Alvin Zhang <alvinx.zhang@intel.com>
---
 drivers/net/i40e/i40e_ethdev.c |  80 +++++++++++++++++
 drivers/net/i40e/i40e_ethdev.h |  30 +++++++
 drivers/net/i40e/i40e_pf.c     | 189 ++++++++++++++++++++++++++++++++---------
 3 files changed, 258 insertions(+), 41 deletions(-)

diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index 4e40b7a..045ba49 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -44,6 +44,7 @@
 #define ETH_I40E_SUPPORT_MULTI_DRIVER	"support-multi-driver"
 #define ETH_I40E_QUEUE_NUM_PER_VF_ARG	"queue-num-per-vf"
 #define ETH_I40E_USE_LATEST_VEC	"use-latest-supported-vec"
+#define ETH_I40E_MAX_VF_WRONG_MSG	"vf_max_wrong_msg"
 
 #define I40E_CLEAR_PXE_WAIT_MS     200
 
@@ -406,6 +407,7 @@ static int i40e_sw_tunnel_filter_insert(struct i40e_pf *pf,
 	ETH_I40E_SUPPORT_MULTI_DRIVER,
 	ETH_I40E_QUEUE_NUM_PER_VF_ARG,
 	ETH_I40E_USE_LATEST_VEC,
+	ETH_I40E_MAX_VF_WRONG_MSG,
 	NULL};
 
 static const struct rte_pci_id pci_id_i40e_map[] = {
@@ -1256,6 +1258,82 @@ static inline void i40e_config_automask(struct i40e_pf *pf)
 	return 0;
 }
 
+static int
+read_vf_msg_check_info(__rte_unused const char *key,
+			       const char *value,
+			       void *opaque)
+{
+	struct i40e_wrong_vf_msg info;
+
+	memset(&info, 0, sizeof(info));
+	/*
+	 * VF message checking function need 3 parameters, max_invalid,
+	 * max_unsupported and silence_seconds.
+	 * When continuous invalid or unsupported message statistics
+	 * from a VF exceed the limitation of 'max_invalid' or
+	 * 'max_unsupported', PF will ignore any message from that VF for
+	 * 'silence_seconds' seconds.
+	 */
+	if (sscanf(value, "%u:%u:%lu", &info.max_invalid,
+			&info.max_unsupport, &info.silence_seconds)
+			!= 3) {
+		PMD_DRV_LOG(ERR, "vf_max_wrong_msg error! format like: "
+				"vf_max_wrong_msg=4:6:60");
+		return -EINVAL;
+	}
+
+	/*
+	 * If invalid or unsupported message checking function is enabled
+	 * by setting max_invalid or max_unsupport variable to not zero,
+	 * 'slience_seconds' must be greater than zero.
+	 */
+	if ((info.max_invalid | info.max_unsupport) &&
+			!info.silence_seconds) {
+		PMD_DRV_LOG(ERR, "vf_max_wrong_msg error! last integer"
+				" must be larger than zero");
+		return -EINVAL;
+	}
+
+	memcpy(opaque, &info, sizeof(struct i40e_wrong_vf_msg));
+	return 0;
+}
+
+static int
+i40e_parse_vf_msg_check_info(struct rte_eth_dev *dev,
+		struct i40e_wrong_vf_msg *wrong_info)
+{
+	int ret = 0;
+	int kvargs_count;
+	struct rte_kvargs *kvlist;
+
+	/* reset all to zero */
+	memset(wrong_info, 0, sizeof(*wrong_info));
+
+	if (!dev->device->devargs)
+		return ret;
+
+	kvlist = rte_kvargs_parse(dev->device->devargs->args, valid_keys);
+	if (!kvlist)
+		return -EINVAL;
+
+	kvargs_count = rte_kvargs_count(kvlist, ETH_I40E_MAX_VF_WRONG_MSG);
+	if (!kvargs_count)
+		goto free_end;
+
+	if (kvargs_count > 1)
+		PMD_DRV_LOG(WARNING, "More than one argument \"%s\" and only "
+				"the first invalid or last valid one is used !",
+				ETH_I40E_MAX_VF_WRONG_MSG);
+
+	if (rte_kvargs_process(kvlist, ETH_I40E_MAX_VF_WRONG_MSG,
+			read_vf_msg_check_info, wrong_info) < 0)
+		ret = -EINVAL;
+
+free_end:
+	rte_kvargs_free(kvlist);
+	return ret;
+}
+
 #define I40E_ALARM_INTERVAL 50000 /* us */
 
 static int
@@ -1328,6 +1406,8 @@ static inline void i40e_config_automask(struct i40e_pf *pf)
 		return -EIO;
 	}
 
+	/* read VF message checking function parameters */
+	i40e_parse_vf_msg_check_info(dev, &pf->wrong_vf_msg_conf);
 	/* Check if need to support multi-driver */
 	i40e_support_multi_driver(dev);
 	/* Check if users want the latest supported vec path */
diff --git a/drivers/net/i40e/i40e_ethdev.h b/drivers/net/i40e/i40e_ethdev.h
index 38ac3ea..6ab5762 100644
--- a/drivers/net/i40e/i40e_ethdev.h
+++ b/drivers/net/i40e/i40e_ethdev.h
@@ -426,6 +426,23 @@ struct i40e_pf_vf {
 	/* version of the virtchnl from VF */
 	struct virtchnl_version_info version;
 	uint32_t request_caps; /* offload caps requested from VF */
+
+	/*
+	 * Counter of message from VF
+	 * invalid_cmd, invalid command since last valid command
+	 * unsupport_cmd, unsupported command since last valid command
+	 * invalid_total, total invalid command
+	 * unsupport_total, total unsupported command
+	 * ignored_cmd, ignored command in silence
+	 */
+	uint16_t invalid_cmd;
+	uint16_t unsupport_cmd;
+	uint64_t invalid_total;
+	uint64_t unsupport_total;
+	uint64_t ignored_cmd;
+
+	/* cycle of stop ignoring VF message */
+	uint64_t silence_end_cycle;
 };
 
 /*
@@ -900,6 +917,17 @@ struct i40e_rte_flow_rss_conf {
 	uint16_t queue[I40E_MAX_Q_PER_TC]; /**< Queues indices to use. */
 };
 
+struct i40e_wrong_vf_msg {
+	uint32_t max_invalid; /* maximal continuous invalid message from VF */
+	/* maximal continuous unsupported message from VF */
+	uint32_t max_unsupport;
+	/**
+	 * silence seconds when VF send much more invalid or unsupported
+	 * message
+	 */
+	uint64_t silence_seconds;
+};
+
 /*
  * Structure to store private data specific for PF instance.
  */
@@ -975,6 +1003,8 @@ struct i40e_pf {
 	struct i40e_customized_pctype customized_pctype[I40E_CUSTOMIZED_MAX];
 	/* Switch Domain Id */
 	uint16_t switch_domain_id;
+
+	struct i40e_wrong_vf_msg wrong_vf_msg_conf;
 };
 
 enum pending_msg {
diff --git a/drivers/net/i40e/i40e_pf.c b/drivers/net/i40e/i40e_pf.c
index b28d02e..be290e2 100644
--- a/drivers/net/i40e/i40e_pf.c
+++ b/drivers/net/i40e/i40e_pf.c
@@ -297,7 +297,7 @@
 		i40e_pf_host_send_msg_to_vf(vf,
 					    VIRTCHNL_OP_GET_VF_RESOURCES,
 					    I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	/* only have 1 VSI by default */
@@ -488,7 +488,7 @@
 		i40e_pf_host_send_msg_to_vf(vf,
 					    VIRTCHNL_OP_CONFIG_VSI_QUEUES,
 					    I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	if (!msg || vc_vqci->num_queue_pairs > vsi->nb_qps ||
@@ -655,7 +655,7 @@
 			vf,
 			VIRTCHNL_OP_CONFIG_IRQ_MAP,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	if (msg == NULL || msglen < sizeof(struct virtchnl_irq_map_info)) {
@@ -795,7 +795,7 @@
 			vf,
 			VIRTCHNL_OP_DISABLE_QUEUES,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	if (msg == NULL || msglen != sizeof(*q_sel)) {
@@ -830,7 +830,7 @@
 			vf,
 			VIRTCHNL_OP_ADD_ETH_ADDR,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	memset(&filter, 0 , sizeof(struct i40e_mac_filter_info));
@@ -876,7 +876,7 @@
 			vf,
 			VIRTCHNL_OP_DEL_ETH_ADDR,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	if (msg == NULL || msglen <= sizeof(*addr_list)) {
@@ -917,7 +917,7 @@
 			vf,
 			VIRTCHNL_OP_ADD_VLAN,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	if (msg == NULL || msglen <= sizeof(*vlan_filter_list)) {
@@ -958,7 +958,7 @@
 			vf,
 			VIRTCHNL_OP_DEL_VLAN,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	if (msg == NULL || msglen <= sizeof(*vlan_filter_list)) {
@@ -999,7 +999,7 @@
 			vf,
 			VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	if (msg == NULL || msglen != sizeof(*promisc)) {
@@ -1031,16 +1031,18 @@
 {
 	i40e_update_vsi_stats(vf->vsi);
 
-	if (b_op)
+	if (b_op) {
 		i40e_pf_host_send_msg_to_vf(vf, VIRTCHNL_OP_GET_STATS,
 					    I40E_SUCCESS,
 					    (uint8_t *)&vf->vsi->eth_stats,
 					    sizeof(vf->vsi->eth_stats));
-	else
+	} else {
 		i40e_pf_host_send_msg_to_vf(vf, VIRTCHNL_OP_GET_STATS,
 					    I40E_NOT_SUPPORTED,
 					    (uint8_t *)&vf->vsi->eth_stats,
 					    sizeof(vf->vsi->eth_stats));
+		return I40E_NOT_SUPPORTED;
+	}
 
 	return I40E_SUCCESS;
 }
@@ -1055,7 +1057,7 @@
 			vf,
 			VIRTCHNL_OP_ENABLE_VLAN_STRIPPING,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	ret = i40e_vsi_config_vlan_stripping(vf->vsi, TRUE);
@@ -1078,7 +1080,7 @@
 			vf,
 			VIRTCHNL_OP_DISABLE_VLAN_STRIPPING,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	ret = i40e_vsi_config_vlan_stripping(vf->vsi, FALSE);
@@ -1106,7 +1108,7 @@
 			vf,
 			VIRTCHNL_OP_CONFIG_RSS_LUT,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	if (!msg || msglen <= sizeof(struct virtchnl_rss_lut)) {
@@ -1247,6 +1249,7 @@
 	struct i40e_pf *pf;
 	uint32_t req_pairs = vfres->num_queue_pairs;
 	uint32_t cur_pairs = vf->vsi->nb_used_qps;
+	int ret = I40E_SUCCESS;
 
 	pf = vf->pf;
 
@@ -1256,12 +1259,14 @@
 	if (req_pairs == 0) {
 		PMD_DRV_LOG(ERR, "VF %d tried to request 0 queues. Ignoring.\n",
 			    vf->vf_idx);
+		ret = I40E_ERR_PARAM;
 	} else if (req_pairs > I40E_MAX_QP_NUM_PER_VF) {
 		PMD_DRV_LOG(ERR,
 			    "VF %d tried to request more than %d queues.\n",
 			    vf->vf_idx,
 			    I40E_MAX_QP_NUM_PER_VF);
 		vfres->num_queue_pairs = I40E_MAX_QP_NUM_PER_VF;
+		ret = I40E_ERR_PARAM;
 	} else if (req_pairs > cur_pairs + pf->qp_pool.num_free) {
 		PMD_DRV_LOG(ERR, "VF %d requested %d queues (rounded to %d) "
 			"but only %d available\n",
@@ -1277,11 +1282,12 @@
 		pf->vf_nb_qps = req_pairs;
 		i40e_pf_host_process_cmd_reset_vf(vf);
 
-		return 0;
+		return I40E_SUCCESS;
 	}
 
-	return i40e_pf_host_send_msg_to_vf(vf, VIRTCHNL_OP_REQUEST_QUEUES, 0,
+	i40e_pf_host_send_msg_to_vf(vf, VIRTCHNL_OP_REQUEST_QUEUES, 0,
 				(u8 *)vfres, sizeof(*vfres));
+	return ret;
 }
 
 void
@@ -1291,6 +1297,14 @@
 			   uint8_t *msg,
 			   uint16_t msglen)
 {
+	/* Ratio of succeed to invalid or unsupported message.
+	 * When PF read a invalid or unsupported message, the corresponding
+	 * counter will increased by ratio.
+	 * When PF read a succeed message, the invalid and unsupported
+	 * message counter will decreased by 1.
+	 */
+	const uint16_t ratio = 2;
+
 	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
 	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
 	struct i40e_pf_vf *vf;
@@ -1306,11 +1320,19 @@
 	}
 
 	vf = &pf->vfs[vf_id];
+
+	/* if timer not end, ignore the message and return */
+	if (rte_get_timer_cycles() < vf->silence_end_cycle) {
+		vf->ignored_cmd++;
+		return;
+	}
+
 	if (!vf->vsi) {
 		PMD_DRV_LOG(ERR, "NO VSI associated with VF found");
 		i40e_pf_host_send_msg_to_vf(vf, opcode,
 			I40E_ERR_NO_AVAILABLE_VSI, NULL, 0);
-		return;
+		ret = I40E_ERR_NO_AVAILABLE_VSI;
+		goto err_cmd;
 	}
 
 	/* perform basic checks on the msg */
@@ -1331,14 +1353,15 @@
 
 	if (ret) {
 		PMD_DRV_LOG(ERR, "Invalid message from VF %u, opcode %u, len %u",
-			    vf_id, opcode, msglen);
+				vf_id, opcode, msglen);
 		i40e_pf_host_send_msg_to_vf(vf, opcode,
-					    I40E_ERR_PARAM, NULL, 0);
-		return;
+				I40E_ERR_PARAM, NULL, 0);
+		ret = I40E_ERR_PARAM;
+		goto err_cmd;
 	}
 
 	/**
-	 * initialise structure to send to user application
+	 * initialize structure to send to user application
 	 * will return response from user in retval field
 	 */
 	ret_param.retval = RTE_PMD_I40E_MB_EVENT_PROCEED;
@@ -1373,78 +1396,84 @@
 		break;
 	case VIRTCHNL_OP_GET_VF_RESOURCES:
 		PMD_DRV_LOG(INFO, "OP_GET_VF_RESOURCES received");
-		i40e_pf_host_process_cmd_get_vf_resource(vf, msg, b_op);
+		ret = i40e_pf_host_process_cmd_get_vf_resource(vf, msg, b_op);
 		break;
 	case VIRTCHNL_OP_CONFIG_VSI_QUEUES:
 		PMD_DRV_LOG(INFO, "OP_CONFIG_VSI_QUEUES received");
-		i40e_pf_host_process_cmd_config_vsi_queues(vf, msg,
+		ret = i40e_pf_host_process_cmd_config_vsi_queues(vf, msg,
 							   msglen, b_op);
 		break;
 	case VIRTCHNL_OP_CONFIG_IRQ_MAP:
 		PMD_DRV_LOG(INFO, "OP_CONFIG_IRQ_MAP received");
-		i40e_pf_host_process_cmd_config_irq_map(vf, msg, msglen, b_op);
+		ret = i40e_pf_host_process_cmd_config_irq_map(vf, msg,
+				msglen, b_op);
 		break;
 	case VIRTCHNL_OP_ENABLE_QUEUES:
 		PMD_DRV_LOG(INFO, "OP_ENABLE_QUEUES received");
 		if (b_op) {
-			i40e_pf_host_process_cmd_enable_queues(vf, msg, msglen);
+			ret = i40e_pf_host_process_cmd_enable_queues(vf,
+					msg, msglen);
 			i40e_notify_vf_link_status(dev, vf);
 		} else {
-			i40e_pf_host_send_msg_to_vf(
-				vf, VIRTCHNL_OP_ENABLE_QUEUES,
-				I40E_NOT_SUPPORTED, NULL, 0);
+			i40e_pf_host_send_msg_to_vf(vf,
+					VIRTCHNL_OP_ENABLE_QUEUES,
+					I40E_NOT_SUPPORTED, NULL, 0);
+			ret = I40E_NOT_SUPPORTED;
 		}
 		break;
 	case VIRTCHNL_OP_DISABLE_QUEUES:
 		PMD_DRV_LOG(INFO, "OP_DISABLE_QUEUE received");
-		i40e_pf_host_process_cmd_disable_queues(vf, msg, msglen, b_op);
+		ret = i40e_pf_host_process_cmd_disable_queues(vf,
+				msg, msglen, b_op);
 		break;
 	case VIRTCHNL_OP_ADD_ETH_ADDR:
 		PMD_DRV_LOG(INFO, "OP_ADD_ETHER_ADDRESS received");
-		i40e_pf_host_process_cmd_add_ether_address(vf, msg,
+		ret = i40e_pf_host_process_cmd_add_ether_address(vf, msg,
 							   msglen, b_op);
 		break;
 	case VIRTCHNL_OP_DEL_ETH_ADDR:
 		PMD_DRV_LOG(INFO, "OP_DEL_ETHER_ADDRESS received");
-		i40e_pf_host_process_cmd_del_ether_address(vf, msg,
+		ret = i40e_pf_host_process_cmd_del_ether_address(vf, msg,
 							   msglen, b_op);
 		break;
 	case VIRTCHNL_OP_ADD_VLAN:
 		PMD_DRV_LOG(INFO, "OP_ADD_VLAN received");
-		i40e_pf_host_process_cmd_add_vlan(vf, msg, msglen, b_op);
+		ret = i40e_pf_host_process_cmd_add_vlan(vf, msg, msglen, b_op);
 		break;
 	case VIRTCHNL_OP_DEL_VLAN:
 		PMD_DRV_LOG(INFO, "OP_DEL_VLAN received");
-		i40e_pf_host_process_cmd_del_vlan(vf, msg, msglen, b_op);
+		ret = i40e_pf_host_process_cmd_del_vlan(vf, msg, msglen, b_op);
 		break;
 	case VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE:
 		PMD_DRV_LOG(INFO, "OP_CONFIG_PROMISCUOUS_MODE received");
-		i40e_pf_host_process_cmd_config_promisc_mode(vf, msg,
+		ret = i40e_pf_host_process_cmd_config_promisc_mode(vf, msg,
 							     msglen, b_op);
 		break;
 	case VIRTCHNL_OP_GET_STATS:
 		PMD_DRV_LOG(INFO, "OP_GET_STATS received");
-		i40e_pf_host_process_cmd_get_stats(vf, b_op);
+		ret = i40e_pf_host_process_cmd_get_stats(vf, b_op);
 		break;
 	case VIRTCHNL_OP_ENABLE_VLAN_STRIPPING:
 		PMD_DRV_LOG(INFO, "OP_ENABLE_VLAN_STRIPPING received");
-		i40e_pf_host_process_cmd_enable_vlan_strip(vf, b_op);
+		ret = i40e_pf_host_process_cmd_enable_vlan_strip(vf, b_op);
 		break;
 	case VIRTCHNL_OP_DISABLE_VLAN_STRIPPING:
 		PMD_DRV_LOG(INFO, "OP_DISABLE_VLAN_STRIPPING received");
-		i40e_pf_host_process_cmd_disable_vlan_strip(vf, b_op);
+		ret = i40e_pf_host_process_cmd_disable_vlan_strip(vf, b_op);
 		break;
 	case VIRTCHNL_OP_CONFIG_RSS_LUT:
 		PMD_DRV_LOG(INFO, "OP_CONFIG_RSS_LUT received");
-		i40e_pf_host_process_cmd_set_rss_lut(vf, msg, msglen, b_op);
+		ret = i40e_pf_host_process_cmd_set_rss_lut(vf, msg,
+				msglen, b_op);
 		break;
 	case VIRTCHNL_OP_CONFIG_RSS_KEY:
 		PMD_DRV_LOG(INFO, "OP_CONFIG_RSS_KEY received");
-		i40e_pf_host_process_cmd_set_rss_key(vf, msg, msglen, b_op);
+		ret = i40e_pf_host_process_cmd_set_rss_key(vf, msg,
+				msglen, b_op);
 		break;
 	case VIRTCHNL_OP_REQUEST_QUEUES:
 		PMD_DRV_LOG(INFO, "OP_REQUEST_QUEUES received");
-		i40e_pf_host_process_cmd_request_queues(vf, msg);
+		ret = i40e_pf_host_process_cmd_request_queues(vf, msg);
 		break;
 
 	/* Don't add command supported below, which will
@@ -1452,10 +1481,83 @@
 	 */
 	default:
 		PMD_DRV_LOG(ERR, "%u received, not supported", opcode);
-		i40e_pf_host_send_msg_to_vf(vf, opcode, I40E_ERR_PARAM,
+		i40e_pf_host_send_msg_to_vf(vf, opcode, I40E_NOT_SUPPORTED,
 								NULL, 0);
+		ret = I40E_NOT_SUPPORTED;
 		break;
 	}
+
+err_cmd:
+	/* If the amount of successive invalid or unsupported message from a
+	 * VF exceed maximal limitation, PF will start a timer.
+	 * Before the timer timed out, PF should ignore any message from
+	 * this VF.
+	 * Once the timer timed out, PF will accept a message from the VF,
+	 * if this message is still invalid or unsupported, PF will reset
+	 * and start the timer again. Otherwise if the first message execute
+	 * success,PF will decrease invalid and unsupported message counter
+	 * by 1.
+	 */
+	vf->silence_end_cycle = 0;
+	switch (ret) {
+	case I40E_SUCCESS:
+		if (!vf->invalid_cmd && !vf->unsupport_cmd)
+			break;
+
+		if (vf->unsupport_cmd)
+			vf->unsupport_cmd--;
+		if (vf->invalid_cmd)
+			vf->invalid_cmd--;
+
+		if (!vf->invalid_cmd && !vf->unsupport_cmd)
+			PMD_DRV_LOG(WARNING, "VF %u message, ignored %lu, "
+					"invalid %lu, unsupported %lu", vf_id,
+					vf->ignored_cmd, vf->invalid_total,
+					vf->unsupport_total);
+		break;
+
+	case I40E_ERR_PARAM:
+	case I40E_ERR_NO_AVAILABLE_VSI:
+		vf->invalid_total++;
+		if (!pf->wrong_vf_msg_conf.max_invalid)
+			break;
+		if (vf->invalid_cmd >= pf->wrong_vf_msg_conf.max_invalid
+				* ratio) {
+			PMD_DRV_LOG(ERR, "VF %u too much continuous invalid"
+					" message(%u, maximum %u, total %lu)!",
+					vf_id, vf->invalid_cmd / ratio + 1,
+					pf->wrong_vf_msg_conf.max_invalid,
+					vf->invalid_total);
+			vf->silence_end_cycle = rte_get_timer_cycles() +
+					pf->wrong_vf_msg_conf.silence_seconds
+					* rte_get_timer_hz();
+		} else {
+			vf->invalid_cmd += ratio;
+		}
+		break;
+
+	case I40E_NOT_SUPPORTED:
+		vf->unsupport_total++;
+		if (!pf->wrong_vf_msg_conf.max_unsupport)
+			break;
+		if (vf->unsupport_cmd >= pf->wrong_vf_msg_conf.max_unsupport
+				* ratio) {
+			PMD_DRV_LOG(ERR, "VF %u too much continuous unsupported"
+					" message(%u, maximum %u, total %lu)!",
+					vf_id, vf->unsupport_cmd / ratio + 1,
+					pf->wrong_vf_msg_conf.max_unsupport,
+					vf->unsupport_total);
+			vf->silence_end_cycle = rte_get_timer_cycles() +
+					pf->wrong_vf_msg_conf.silence_seconds
+					* rte_get_timer_hz();
+		} else {
+			vf->unsupport_cmd += ratio;
+		}
+		break;
+	default:
+		break;
+	}
+	return;
 }
 
 int
@@ -1493,6 +1595,11 @@
 		pf->vfs[i].pf = pf;
 		pf->vfs[i].state = I40E_VF_INACTIVE;
 		pf->vfs[i].vf_idx = i;
+
+		pf->vfs[i].invalid_cmd = 0;
+		pf->vfs[i].unsupport_cmd = 0;
+		pf->vfs[i].silence_end_cycle = 0;
+
 		ret = i40e_pf_host_vf_reset(&pf->vfs[i], 0);
 		if (ret != I40E_SUCCESS)
 			goto fail;
-- 
1.8.3.1


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [dpdk-dev] [PATCH] net/i40e: add checking for messages from VF
  2019-08-20  7:18 [dpdk-dev] [PATCH] net/i40e: add checking for messages from VF alvinx.zhang
@ 2019-08-22  4:18 ` Ye Xiaolong
  2019-08-28  7:13 ` [dpdk-dev] [PATCH v2] " alvinx.zhang
  1 sibling, 0 replies; 14+ messages in thread
From: Ye Xiaolong @ 2019-08-22  4:18 UTC (permalink / raw)
  To: alvinx.zhang; +Cc: beilei.xing, dev

On 08/20, alvinx.zhang@intel.com wrote:
>From: Alvin Zhang <alvinx.zhang@intel.com>
>
>If VF driver in VM continuous sending invalid messages by mailbox,
>it will waste CPU cycles on PF driver and impact other VF drivers
>configuration. New feature can count the numbers of invalid and
>unsupported messages from VFs, when the statistics from a VF
>exceed maximum limit, PF driver will ignore any message from that
>VF for some seconds.
>
>Signed-off-by: Alvin Zhang <alvinx.zhang@intel.com>
>---
> drivers/net/i40e/i40e_ethdev.c |  80 +++++++++++++++++
> drivers/net/i40e/i40e_ethdev.h |  30 +++++++
> drivers/net/i40e/i40e_pf.c     | 189 ++++++++++++++++++++++++++++++++---------
> 3 files changed, 258 insertions(+), 41 deletions(-)
>
>diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
>index 4e40b7a..045ba49 100644
>--- a/drivers/net/i40e/i40e_ethdev.c
>+++ b/drivers/net/i40e/i40e_ethdev.c
>@@ -44,6 +44,7 @@
> #define ETH_I40E_SUPPORT_MULTI_DRIVER	"support-multi-driver"
> #define ETH_I40E_QUEUE_NUM_PER_VF_ARG	"queue-num-per-vf"
> #define ETH_I40E_USE_LATEST_VEC	"use-latest-supported-vec"
>+#define ETH_I40E_MAX_VF_WRONG_MSG	"vf_max_wrong_msg"
> 
> #define I40E_CLEAR_PXE_WAIT_MS     200
> 
>@@ -406,6 +407,7 @@ static int i40e_sw_tunnel_filter_insert(struct i40e_pf *pf,
> 	ETH_I40E_SUPPORT_MULTI_DRIVER,
> 	ETH_I40E_QUEUE_NUM_PER_VF_ARG,
> 	ETH_I40E_USE_LATEST_VEC,
>+	ETH_I40E_MAX_VF_WRONG_MSG,
> 	NULL};
> 
> static const struct rte_pci_id pci_id_i40e_map[] = {
>@@ -1256,6 +1258,82 @@ static inline void i40e_config_automask(struct i40e_pf *pf)
> 	return 0;
> }
> 
>+static int
>+read_vf_msg_check_info(__rte_unused const char *key,
>+			       const char *value,
>+			       void *opaque)
>+{
>+	struct i40e_wrong_vf_msg info;
>+
>+	memset(&info, 0, sizeof(info));
>+	/*
>+	 * VF message checking function need 3 parameters, max_invalid,
>+	 * max_unsupported and silence_seconds.
>+	 * When continuous invalid or unsupported message statistics
>+	 * from a VF exceed the limitation of 'max_invalid' or
>+	 * 'max_unsupported', PF will ignore any message from that VF for
>+	 * 'silence_seconds' seconds.
>+	 */
>+	if (sscanf(value, "%u:%u:%lu", &info.max_invalid,
>+			&info.max_unsupport, &info.silence_seconds)
>+			!= 3) {
>+		PMD_DRV_LOG(ERR, "vf_max_wrong_msg error! format like: "
>+				"vf_max_wrong_msg=4:6:60");
>+		return -EINVAL;
>+	}
>+
>+	/*
>+	 * If invalid or unsupported message checking function is enabled
>+	 * by setting max_invalid or max_unsupport variable to not zero,
>+	 * 'slience_seconds' must be greater than zero.
>+	 */
>+	if ((info.max_invalid | info.max_unsupport) &&

info.max_invalid || info.max_unsupport?

And I prefer to use unsupported in your variable names for unsupport is not a 
valid word.

>+			!info.silence_seconds) {
>+		PMD_DRV_LOG(ERR, "vf_max_wrong_msg error! last integer"
>+				" must be larger than zero");
>+		return -EINVAL;
>+	}
>+
>+	memcpy(opaque, &info, sizeof(struct i40e_wrong_vf_msg));
>+	return 0;
>+}
>+
>+static int
>+i40e_parse_vf_msg_check_info(struct rte_eth_dev *dev,
>+		struct i40e_wrong_vf_msg *wrong_info)
>+{
>+	int ret = 0;
>+	int kvargs_count;
>+	struct rte_kvargs *kvlist;
>+
>+	/* reset all to zero */
>+	memset(wrong_info, 0, sizeof(*wrong_info));
>+
>+	if (!dev->device->devargs)
>+		return ret;
>+
>+	kvlist = rte_kvargs_parse(dev->device->devargs->args, valid_keys);
>+	if (!kvlist)
>+		return -EINVAL;
>+
>+	kvargs_count = rte_kvargs_count(kvlist, ETH_I40E_MAX_VF_WRONG_MSG);
>+	if (!kvargs_count)
>+		goto free_end;
>+
>+	if (kvargs_count > 1)
>+		PMD_DRV_LOG(WARNING, "More than one argument \"%s\" and only "
>+				"the first invalid or last valid one is used !",
>+				ETH_I40E_MAX_VF_WRONG_MSG);

What about we just allow 1 wrong msg argument?

>+
>+	if (rte_kvargs_process(kvlist, ETH_I40E_MAX_VF_WRONG_MSG,
>+			read_vf_msg_check_info, wrong_info) < 0)
>+		ret = -EINVAL;
>+
>+free_end:
>+	rte_kvargs_free(kvlist);
>+	return ret;
>+}
>+
> #define I40E_ALARM_INTERVAL 50000 /* us */
> 
> static int
>@@ -1328,6 +1406,8 @@ static inline void i40e_config_automask(struct i40e_pf *pf)
> 		return -EIO;
> 	}
> 
>+	/* read VF message checking function parameters */
>+	i40e_parse_vf_msg_check_info(dev, &pf->wrong_vf_msg_conf);
> 	/* Check if need to support multi-driver */
> 	i40e_support_multi_driver(dev);
> 	/* Check if users want the latest supported vec path */
>diff --git a/drivers/net/i40e/i40e_ethdev.h b/drivers/net/i40e/i40e_ethdev.h
>index 38ac3ea..6ab5762 100644
>--- a/drivers/net/i40e/i40e_ethdev.h
>+++ b/drivers/net/i40e/i40e_ethdev.h
>@@ -426,6 +426,23 @@ struct i40e_pf_vf {
> 	/* version of the virtchnl from VF */
> 	struct virtchnl_version_info version;
> 	uint32_t request_caps; /* offload caps requested from VF */
>+
>+	/*
>+	 * Counter of message from VF
>+	 * invalid_cmd, invalid command since last valid command
>+	 * unsupport_cmd, unsupported command since last valid command
>+	 * invalid_total, total invalid command
>+	 * unsupport_total, total unsupported command
>+	 * ignored_cmd, ignored command in silence
>+	 */
>+	uint16_t invalid_cmd;
>+	uint16_t unsupport_cmd;
>+	uint64_t invalid_total;
>+	uint64_t unsupport_total;
>+	uint64_t ignored_cmd;

What about rename them to invalid_cmd_cnt/unsupported_cmd_cnt/ignored_cmd_cnt according to
their meanings?

>+
>+	/* cycle of stop ignoring VF message */
>+	uint64_t silence_end_cycle;
> };
> 

[snip]

>@@ -1291,6 +1297,14 @@
> 			   uint8_t *msg,
> 			   uint16_t msglen)
> {
>+	/* Ratio of succeed to invalid or unsupported message.
>+	 * When PF read a invalid or unsupported message, the corresponding
>+	 * counter will increased by ratio.
>+	 * When PF read a succeed message, the invalid and unsupported
>+	 * message counter will decreased by 1.
>+	 */
>+	const uint16_t ratio = 2;
>+

What's the benefit of using ratio?

> 	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
> 	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
> 	struct i40e_pf_vf *vf;
>@@ -1306,11 +1320,19 @@
> 	}
> 
> 	vf = &pf->vfs[vf_id];
>+
>+	/* if timer not end, ignore the message and return */
>+	if (rte_get_timer_cycles() < vf->silence_end_cycle) {
>+		vf->ignored_cmd++;
>+		return;
>+	}
>+
> 	if (!vf->vsi) {
> 		PMD_DRV_LOG(ERR, "NO VSI associated with VF found");
> 		i40e_pf_host_send_msg_to_vf(vf, opcode,
> 			I40E_ERR_NO_AVAILABLE_VSI, NULL, 0);
>-		return;
>+		ret = I40E_ERR_NO_AVAILABLE_VSI;
>+		goto err_cmd;
> 	}
> 
> 	/* perform basic checks on the msg */
>@@ -1331,14 +1353,15 @@
> 
> 	if (ret) {
> 		PMD_DRV_LOG(ERR, "Invalid message from VF %u, opcode %u, len %u",
>-			    vf_id, opcode, msglen);
>+				vf_id, opcode, msglen);
> 		i40e_pf_host_send_msg_to_vf(vf, opcode,
>-					    I40E_ERR_PARAM, NULL, 0);
>-		return;
>+				I40E_ERR_PARAM, NULL, 0);
>+		ret = I40E_ERR_PARAM;
>+		goto err_cmd;
> 	}
> 
> 	/**
>-	 * initialise structure to send to user application
>+	 * initialize structure to send to user application
> 	 * will return response from user in retval field
> 	 */
> 	ret_param.retval = RTE_PMD_I40E_MB_EVENT_PROCEED;
>@@ -1373,78 +1396,84 @@
> 		break;
> 	case VIRTCHNL_OP_GET_VF_RESOURCES:
> 		PMD_DRV_LOG(INFO, "OP_GET_VF_RESOURCES received");
>-		i40e_pf_host_process_cmd_get_vf_resource(vf, msg, b_op);
>+		ret = i40e_pf_host_process_cmd_get_vf_resource(vf, msg, b_op);
> 		break;
> 	case VIRTCHNL_OP_CONFIG_VSI_QUEUES:
> 		PMD_DRV_LOG(INFO, "OP_CONFIG_VSI_QUEUES received");
>-		i40e_pf_host_process_cmd_config_vsi_queues(vf, msg,
>+		ret = i40e_pf_host_process_cmd_config_vsi_queues(vf, msg,
> 							   msglen, b_op);
> 		break;
> 	case VIRTCHNL_OP_CONFIG_IRQ_MAP:
> 		PMD_DRV_LOG(INFO, "OP_CONFIG_IRQ_MAP received");
>-		i40e_pf_host_process_cmd_config_irq_map(vf, msg, msglen, b_op);
>+		ret = i40e_pf_host_process_cmd_config_irq_map(vf, msg,
>+				msglen, b_op);
> 		break;
> 	case VIRTCHNL_OP_ENABLE_QUEUES:
> 		PMD_DRV_LOG(INFO, "OP_ENABLE_QUEUES received");
> 		if (b_op) {
>-			i40e_pf_host_process_cmd_enable_queues(vf, msg, msglen);
>+			ret = i40e_pf_host_process_cmd_enable_queues(vf,
>+					msg, msglen);
> 			i40e_notify_vf_link_status(dev, vf);
> 		} else {
>-			i40e_pf_host_send_msg_to_vf(
>-				vf, VIRTCHNL_OP_ENABLE_QUEUES,
>-				I40E_NOT_SUPPORTED, NULL, 0);
>+			i40e_pf_host_send_msg_to_vf(vf,
>+					VIRTCHNL_OP_ENABLE_QUEUES,
>+					I40E_NOT_SUPPORTED, NULL, 0);
>+			ret = I40E_NOT_SUPPORTED;
> 		}
> 		break;
> 	case VIRTCHNL_OP_DISABLE_QUEUES:
> 		PMD_DRV_LOG(INFO, "OP_DISABLE_QUEUE received");
>-		i40e_pf_host_process_cmd_disable_queues(vf, msg, msglen, b_op);
>+		ret = i40e_pf_host_process_cmd_disable_queues(vf,
>+				msg, msglen, b_op);
> 		break;
> 	case VIRTCHNL_OP_ADD_ETH_ADDR:
> 		PMD_DRV_LOG(INFO, "OP_ADD_ETHER_ADDRESS received");
>-		i40e_pf_host_process_cmd_add_ether_address(vf, msg,
>+		ret = i40e_pf_host_process_cmd_add_ether_address(vf, msg,
> 							   msglen, b_op);
> 		break;
> 	case VIRTCHNL_OP_DEL_ETH_ADDR:
> 		PMD_DRV_LOG(INFO, "OP_DEL_ETHER_ADDRESS received");
>-		i40e_pf_host_process_cmd_del_ether_address(vf, msg,
>+		ret = i40e_pf_host_process_cmd_del_ether_address(vf, msg,
> 							   msglen, b_op);
> 		break;
> 	case VIRTCHNL_OP_ADD_VLAN:
> 		PMD_DRV_LOG(INFO, "OP_ADD_VLAN received");
>-		i40e_pf_host_process_cmd_add_vlan(vf, msg, msglen, b_op);
>+		ret = i40e_pf_host_process_cmd_add_vlan(vf, msg, msglen, b_op);
> 		break;
> 	case VIRTCHNL_OP_DEL_VLAN:
> 		PMD_DRV_LOG(INFO, "OP_DEL_VLAN received");
>-		i40e_pf_host_process_cmd_del_vlan(vf, msg, msglen, b_op);
>+		ret = i40e_pf_host_process_cmd_del_vlan(vf, msg, msglen, b_op);
> 		break;
> 	case VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE:
> 		PMD_DRV_LOG(INFO, "OP_CONFIG_PROMISCUOUS_MODE received");
>-		i40e_pf_host_process_cmd_config_promisc_mode(vf, msg,
>+		ret = i40e_pf_host_process_cmd_config_promisc_mode(vf, msg,
> 							     msglen, b_op);
> 		break;
> 	case VIRTCHNL_OP_GET_STATS:
> 		PMD_DRV_LOG(INFO, "OP_GET_STATS received");
>-		i40e_pf_host_process_cmd_get_stats(vf, b_op);
>+		ret = i40e_pf_host_process_cmd_get_stats(vf, b_op);
> 		break;
> 	case VIRTCHNL_OP_ENABLE_VLAN_STRIPPING:
> 		PMD_DRV_LOG(INFO, "OP_ENABLE_VLAN_STRIPPING received");
>-		i40e_pf_host_process_cmd_enable_vlan_strip(vf, b_op);
>+		ret = i40e_pf_host_process_cmd_enable_vlan_strip(vf, b_op);
> 		break;
> 	case VIRTCHNL_OP_DISABLE_VLAN_STRIPPING:
> 		PMD_DRV_LOG(INFO, "OP_DISABLE_VLAN_STRIPPING received");
>-		i40e_pf_host_process_cmd_disable_vlan_strip(vf, b_op);
>+		ret = i40e_pf_host_process_cmd_disable_vlan_strip(vf, b_op);
> 		break;
> 	case VIRTCHNL_OP_CONFIG_RSS_LUT:
> 		PMD_DRV_LOG(INFO, "OP_CONFIG_RSS_LUT received");
>-		i40e_pf_host_process_cmd_set_rss_lut(vf, msg, msglen, b_op);
>+		ret = i40e_pf_host_process_cmd_set_rss_lut(vf, msg,
>+				msglen, b_op);
> 		break;
> 	case VIRTCHNL_OP_CONFIG_RSS_KEY:
> 		PMD_DRV_LOG(INFO, "OP_CONFIG_RSS_KEY received");
>-		i40e_pf_host_process_cmd_set_rss_key(vf, msg, msglen, b_op);
>+		ret = i40e_pf_host_process_cmd_set_rss_key(vf, msg,
>+				msglen, b_op);
> 		break;
> 	case VIRTCHNL_OP_REQUEST_QUEUES:
> 		PMD_DRV_LOG(INFO, "OP_REQUEST_QUEUES received");
>-		i40e_pf_host_process_cmd_request_queues(vf, msg);
>+		ret = i40e_pf_host_process_cmd_request_queues(vf, msg);
> 		break;
> 
> 	/* Don't add command supported below, which will
>@@ -1452,10 +1481,83 @@
> 	 */
> 	default:
> 		PMD_DRV_LOG(ERR, "%u received, not supported", opcode);
>-		i40e_pf_host_send_msg_to_vf(vf, opcode, I40E_ERR_PARAM,
>+		i40e_pf_host_send_msg_to_vf(vf, opcode, I40E_NOT_SUPPORTED,
> 								NULL, 0);
>+		ret = I40E_NOT_SUPPORTED;
> 		break;
> 	}
>+
>+err_cmd:
>+	/* If the amount of successive invalid or unsupported message from a
>+	 * VF exceed maximal limitation, PF will start a timer.
>+	 * Before the timer timed out, PF should ignore any message from
>+	 * this VF.
>+	 * Once the timer timed out, PF will accept a message from the VF,
>+	 * if this message is still invalid or unsupported, PF will reset
>+	 * and start the timer again. Otherwise if the first message execute
>+	 * success,PF will decrease invalid and unsupported message counter
>+	 * by 1.
>+	 */
>+	vf->silence_end_cycle = 0;
>+	switch (ret) {
>+	case I40E_SUCCESS:
>+		if (!vf->invalid_cmd && !vf->unsupport_cmd)
>+			break;
>+
>+		if (vf->unsupport_cmd)
>+			vf->unsupport_cmd--;
>+		if (vf->invalid_cmd)
>+			vf->invalid_cmd--;
>+
>+		if (!vf->invalid_cmd && !vf->unsupport_cmd)
>+			PMD_DRV_LOG(WARNING, "VF %u message, ignored %lu, "
>+					"invalid %lu, unsupported %lu", vf_id,
>+					vf->ignored_cmd, vf->invalid_total,
>+					vf->unsupport_total);
>+		break;
>+
>+	case I40E_ERR_PARAM:
>+	case I40E_ERR_NO_AVAILABLE_VSI:
>+		vf->invalid_total++;
>+		if (!pf->wrong_vf_msg_conf.max_invalid)
>+			break;
>+		if (vf->invalid_cmd >= pf->wrong_vf_msg_conf.max_invalid
>+				* ratio) {
>+			PMD_DRV_LOG(ERR, "VF %u too much continuous invalid"
>+					" message(%u, maximum %u, total %lu)!",
>+					vf_id, vf->invalid_cmd / ratio + 1,
>+					pf->wrong_vf_msg_conf.max_invalid,
>+					vf->invalid_total);
>+			vf->silence_end_cycle = rte_get_timer_cycles() +
>+					pf->wrong_vf_msg_conf.silence_seconds
>+					* rte_get_timer_hz();
>+		} else {
>+			vf->invalid_cmd += ratio;
>+		}
>+		break;
>+
>+	case I40E_NOT_SUPPORTED:
>+		vf->unsupport_total++;
>+		if (!pf->wrong_vf_msg_conf.max_unsupport)
>+			break;
>+		if (vf->unsupport_cmd >= pf->wrong_vf_msg_conf.max_unsupport
>+				* ratio) {
>+			PMD_DRV_LOG(ERR, "VF %u too much continuous unsupported"
>+					" message(%u, maximum %u, total %lu)!",
>+					vf_id, vf->unsupport_cmd / ratio + 1,
>+					pf->wrong_vf_msg_conf.max_unsupport,
>+					vf->unsupport_total);
>+			vf->silence_end_cycle = rte_get_timer_cycles() +
>+					pf->wrong_vf_msg_conf.silence_seconds
>+					* rte_get_timer_hz();
>+		} else {
>+			vf->unsupport_cmd += ratio;
>+		}
>+		break;
>+	default:
>+		break;
>+	}
>+	return;
> }
> 
> int
>@@ -1493,6 +1595,11 @@
> 		pf->vfs[i].pf = pf;
> 		pf->vfs[i].state = I40E_VF_INACTIVE;
> 		pf->vfs[i].vf_idx = i;
>+
>+		pf->vfs[i].invalid_cmd = 0;
>+		pf->vfs[i].unsupport_cmd = 0;
>+		pf->vfs[i].silence_end_cycle = 0;
>+
> 		ret = i40e_pf_host_vf_reset(&pf->vfs[i], 0);
> 		if (ret != I40E_SUCCESS)
> 			goto fail;
>-- 
>1.8.3.1
>

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [dpdk-dev] [PATCH v2] net/i40e: add checking for messages from VF
  2019-08-20  7:18 [dpdk-dev] [PATCH] net/i40e: add checking for messages from VF alvinx.zhang
  2019-08-22  4:18 ` Ye Xiaolong
@ 2019-08-28  7:13 ` alvinx.zhang
  2019-09-09 10:01   ` [dpdk-dev] [PATCH v3] net/i40e: validate all " alvinx.zhang
  1 sibling, 1 reply; 14+ messages in thread
From: alvinx.zhang @ 2019-08-28  7:13 UTC (permalink / raw)
  To: qi.z.zhang; +Cc: dev, Alvin Zhang

From: Alvin Zhang <alvinx.zhang@intel.com>

If VF driver in VM continuous sending invalid messages by mailbox,
it will waste CPU cycles on PF driver and impact other VF drivers
configuration. I40E PF PMD can count the numbers of invalid and
unsupported messages from VFs, when the counter of statistics from
a VF exceed maximum limitation, PF driver will ignore all of
messages from that VF for some seconds.

Signed-off-by: Alvin Zhang <alvinx.zhang@intel.com>
---
 doc/guides/nics/i40e.rst       |  12 +++
 drivers/net/i40e/i40e_ethdev.c |  82 ++++++++++++++++++
 drivers/net/i40e/i40e_ethdev.h |  32 +++++++
 drivers/net/i40e/i40e_pf.c     | 185 ++++++++++++++++++++++++++++++++---------
 4 files changed, 270 insertions(+), 41 deletions(-)

diff --git a/doc/guides/nics/i40e.rst b/doc/guides/nics/i40e.rst
index 0884e15..5c7bdb0 100644
--- a/doc/guides/nics/i40e.rst
+++ b/doc/guides/nics/i40e.rst
@@ -185,6 +185,18 @@ Runtime Config Options
 
   -w 84:00.0,use-latest-supported-vec=1
 
+- ``Enable validation for VF message`` (default ``not enabled``)
+
+  The i40e PF PMD supports message validation which from VFs. This will count the
+  numbers of continuous invalid and unsupported messages from VFs. If the counter of
+  statistics from a VF exceed maximum limitation, PF driver will ignore all of messages
+  from that VF for some seconds. Using the ``devargs`` option ``vf_max_wrong_msg``,
+  user can specify how many continuous invalid and unsupported message that PF driver
+  could tolerate and how many seconds during which PF driver will ignore all of
+  messages from a VF, for example::
+
+  -w 84:00.0,vf_max_wrong_msg=4,6,30
+
 Vector RX Pre-conditions
 ~~~~~~~~~~~~~~~~~~~~~~~~
 For Vector RX it is assumed that the number of descriptor rings will be a power
diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index 4e40b7a..e17cd66 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -44,6 +44,7 @@
 #define ETH_I40E_SUPPORT_MULTI_DRIVER	"support-multi-driver"
 #define ETH_I40E_QUEUE_NUM_PER_VF_ARG	"queue-num-per-vf"
 #define ETH_I40E_USE_LATEST_VEC	"use-latest-supported-vec"
+#define ETH_I40E_MAX_VF_WRONG_MSG	"vf_max_wrong_msg"
 
 #define I40E_CLEAR_PXE_WAIT_MS     200
 
@@ -406,6 +407,7 @@ static int i40e_sw_tunnel_filter_insert(struct i40e_pf *pf,
 	ETH_I40E_SUPPORT_MULTI_DRIVER,
 	ETH_I40E_QUEUE_NUM_PER_VF_ARG,
 	ETH_I40E_USE_LATEST_VEC,
+	ETH_I40E_MAX_VF_WRONG_MSG,
 	NULL};
 
 static const struct rte_pci_id pci_id_i40e_map[] = {
@@ -1256,6 +1258,84 @@ static inline void i40e_config_automask(struct i40e_pf *pf)
 	return 0;
 }
 
+static int
+read_vf_msg_check_info(__rte_unused const char *key,
+			       const char *value,
+			       void *opaque)
+{
+	struct i40e_wrong_vf_msg info;
+
+	memset(&info, 0, sizeof(info));
+	/*
+	 * VF message checking function need 3 parameters, max_invalid,
+	 * max_unsupported and silence_seconds.
+	 * When continuous invalid or unsupported message statistics
+	 * from a VF exceed the limitation of 'max_invalid' or
+	 * 'max_unsupported', PF will ignore any message from that VF for
+	 * 'silence_seconds' seconds.
+	 */
+	if (sscanf(value, "%u:%u:%lu", &info.max_invalid,
+			&info.max_unsupported, &info.silence_seconds)
+			!= 3) {
+		PMD_DRV_LOG(ERR, "vf_max_wrong_msg error! format like: "
+				"vf_max_wrong_msg=4:6:60");
+		return -EINVAL;
+	}
+
+	/*
+	 * If invalid or unsupported message checking function is enabled
+	 * by setting max_invalid or max_unsupported variable to not zero,
+	 * 'slience_seconds' must be greater than zero.
+	 */
+	if ((info.max_invalid || info.max_unsupported) &&
+			!info.silence_seconds) {
+		PMD_DRV_LOG(ERR, "vf_max_wrong_msg error! last integer"
+				" must be larger than zero");
+		return -EINVAL;
+	}
+
+	memcpy(opaque, &info, sizeof(struct i40e_wrong_vf_msg));
+	return 0;
+}
+
+static int
+i40e_parse_vf_msg_check_info(struct rte_eth_dev *dev,
+		struct i40e_wrong_vf_msg *wrong_info)
+{
+	int ret = 0;
+	int kvargs_count;
+	struct rte_kvargs *kvlist;
+
+	/* reset all to zero */
+	memset(wrong_info, 0, sizeof(*wrong_info));
+
+	if (!dev->device->devargs)
+		return ret;
+
+	kvlist = rte_kvargs_parse(dev->device->devargs->args, valid_keys);
+	if (!kvlist)
+		return -EINVAL;
+
+	kvargs_count = rte_kvargs_count(kvlist, ETH_I40E_MAX_VF_WRONG_MSG);
+	if (!kvargs_count)
+		goto free_end;
+
+	if (kvargs_count > 1) {
+		PMD_DRV_LOG(ERR, "More than one argument \"%s\"!",
+				ETH_I40E_MAX_VF_WRONG_MSG);
+		ret = -EINVAL;
+		goto free_end;
+	}
+
+	if (rte_kvargs_process(kvlist, ETH_I40E_MAX_VF_WRONG_MSG,
+			read_vf_msg_check_info, wrong_info) < 0)
+		ret = -EINVAL;
+
+free_end:
+	rte_kvargs_free(kvlist);
+	return ret;
+}
+
 #define I40E_ALARM_INTERVAL 50000 /* us */
 
 static int
@@ -1328,6 +1408,8 @@ static inline void i40e_config_automask(struct i40e_pf *pf)
 		return -EIO;
 	}
 
+	/* read VF message checking function parameters */
+	i40e_parse_vf_msg_check_info(dev, &pf->wrong_vf_msg_conf);
 	/* Check if need to support multi-driver */
 	i40e_support_multi_driver(dev);
 	/* Check if users want the latest supported vec path */
diff --git a/drivers/net/i40e/i40e_ethdev.h b/drivers/net/i40e/i40e_ethdev.h
index 38ac3ea..d4d98cb 100644
--- a/drivers/net/i40e/i40e_ethdev.h
+++ b/drivers/net/i40e/i40e_ethdev.h
@@ -426,6 +426,25 @@ struct i40e_pf_vf {
 	/* version of the virtchnl from VF */
 	struct virtchnl_version_info version;
 	uint32_t request_caps; /* offload caps requested from VF */
+
+	/*
+	 * Counter of message from VF
+	 * invalid_cmd_cnt, invalid command since last valid command
+	 * unsupported_cmd_cnt, unsupported command since last valid command
+	 * invalid_total, total invalid command
+	 * unsupported_total, total unsupported command
+	 * ignored_cmd_cnt, ignored command in silence
+	 */
+	uint16_t invalid_cmd_cnt;
+	uint16_t unsupported_cmd_cnt;
+	uint64_t invalid_total;
+	uint64_t unsupported_total;
+	uint64_t ignored_cmd_cnt;
+
+	/* cycle of stop ignoring VF message */
+	uint64_t silence_end_cycle;
+	/* cycle of receive last invalid or unsupported message from VF*/
+	uint64_t last_wrong_msg_cycle;
 };
 
 /*
@@ -900,6 +919,17 @@ struct i40e_rte_flow_rss_conf {
 	uint16_t queue[I40E_MAX_Q_PER_TC]; /**< Queues indices to use. */
 };
 
+struct i40e_wrong_vf_msg {
+	uint32_t max_invalid; /* maximal continuous invalid message from VF */
+	/* maximal continuous unsupported message from VF */
+	uint32_t max_unsupported;
+	/*
+	 * silence seconds when VF send much more invalid or unsupported
+	 * message
+	 */
+	uint64_t silence_seconds;
+};
+
 /*
  * Structure to store private data specific for PF instance.
  */
@@ -975,6 +1005,8 @@ struct i40e_pf {
 	struct i40e_customized_pctype customized_pctype[I40E_CUSTOMIZED_MAX];
 	/* Switch Domain Id */
 	uint16_t switch_domain_id;
+
+	struct i40e_wrong_vf_msg wrong_vf_msg_conf;
 };
 
 enum pending_msg {
diff --git a/drivers/net/i40e/i40e_pf.c b/drivers/net/i40e/i40e_pf.c
index b28d02e..c94fc51 100644
--- a/drivers/net/i40e/i40e_pf.c
+++ b/drivers/net/i40e/i40e_pf.c
@@ -297,7 +297,7 @@
 		i40e_pf_host_send_msg_to_vf(vf,
 					    VIRTCHNL_OP_GET_VF_RESOURCES,
 					    I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	/* only have 1 VSI by default */
@@ -488,7 +488,7 @@
 		i40e_pf_host_send_msg_to_vf(vf,
 					    VIRTCHNL_OP_CONFIG_VSI_QUEUES,
 					    I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	if (!msg || vc_vqci->num_queue_pairs > vsi->nb_qps ||
@@ -655,7 +655,7 @@
 			vf,
 			VIRTCHNL_OP_CONFIG_IRQ_MAP,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	if (msg == NULL || msglen < sizeof(struct virtchnl_irq_map_info)) {
@@ -795,7 +795,7 @@
 			vf,
 			VIRTCHNL_OP_DISABLE_QUEUES,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	if (msg == NULL || msglen != sizeof(*q_sel)) {
@@ -830,7 +830,7 @@
 			vf,
 			VIRTCHNL_OP_ADD_ETH_ADDR,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	memset(&filter, 0 , sizeof(struct i40e_mac_filter_info));
@@ -876,7 +876,7 @@
 			vf,
 			VIRTCHNL_OP_DEL_ETH_ADDR,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	if (msg == NULL || msglen <= sizeof(*addr_list)) {
@@ -917,7 +917,7 @@
 			vf,
 			VIRTCHNL_OP_ADD_VLAN,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	if (msg == NULL || msglen <= sizeof(*vlan_filter_list)) {
@@ -958,7 +958,7 @@
 			vf,
 			VIRTCHNL_OP_DEL_VLAN,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	if (msg == NULL || msglen <= sizeof(*vlan_filter_list)) {
@@ -999,7 +999,7 @@
 			vf,
 			VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	if (msg == NULL || msglen != sizeof(*promisc)) {
@@ -1031,16 +1031,18 @@
 {
 	i40e_update_vsi_stats(vf->vsi);
 
-	if (b_op)
+	if (b_op) {
 		i40e_pf_host_send_msg_to_vf(vf, VIRTCHNL_OP_GET_STATS,
 					    I40E_SUCCESS,
 					    (uint8_t *)&vf->vsi->eth_stats,
 					    sizeof(vf->vsi->eth_stats));
-	else
+	} else {
 		i40e_pf_host_send_msg_to_vf(vf, VIRTCHNL_OP_GET_STATS,
 					    I40E_NOT_SUPPORTED,
 					    (uint8_t *)&vf->vsi->eth_stats,
 					    sizeof(vf->vsi->eth_stats));
+		return I40E_NOT_SUPPORTED;
+	}
 
 	return I40E_SUCCESS;
 }
@@ -1055,7 +1057,7 @@
 			vf,
 			VIRTCHNL_OP_ENABLE_VLAN_STRIPPING,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	ret = i40e_vsi_config_vlan_stripping(vf->vsi, TRUE);
@@ -1078,7 +1080,7 @@
 			vf,
 			VIRTCHNL_OP_DISABLE_VLAN_STRIPPING,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	ret = i40e_vsi_config_vlan_stripping(vf->vsi, FALSE);
@@ -1106,7 +1108,7 @@
 			vf,
 			VIRTCHNL_OP_CONFIG_RSS_LUT,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	if (!msg || msglen <= sizeof(struct virtchnl_rss_lut)) {
@@ -1247,6 +1249,7 @@
 	struct i40e_pf *pf;
 	uint32_t req_pairs = vfres->num_queue_pairs;
 	uint32_t cur_pairs = vf->vsi->nb_used_qps;
+	int ret = I40E_SUCCESS;
 
 	pf = vf->pf;
 
@@ -1256,12 +1259,14 @@
 	if (req_pairs == 0) {
 		PMD_DRV_LOG(ERR, "VF %d tried to request 0 queues. Ignoring.\n",
 			    vf->vf_idx);
+		ret = I40E_ERR_PARAM;
 	} else if (req_pairs > I40E_MAX_QP_NUM_PER_VF) {
 		PMD_DRV_LOG(ERR,
 			    "VF %d tried to request more than %d queues.\n",
 			    vf->vf_idx,
 			    I40E_MAX_QP_NUM_PER_VF);
 		vfres->num_queue_pairs = I40E_MAX_QP_NUM_PER_VF;
+		ret = I40E_ERR_PARAM;
 	} else if (req_pairs > cur_pairs + pf->qp_pool.num_free) {
 		PMD_DRV_LOG(ERR, "VF %d requested %d queues (rounded to %d) "
 			"but only %d available\n",
@@ -1277,11 +1282,12 @@
 		pf->vf_nb_qps = req_pairs;
 		i40e_pf_host_process_cmd_reset_vf(vf);
 
-		return 0;
+		return I40E_SUCCESS;
 	}
 
-	return i40e_pf_host_send_msg_to_vf(vf, VIRTCHNL_OP_REQUEST_QUEUES, 0,
+	i40e_pf_host_send_msg_to_vf(vf, VIRTCHNL_OP_REQUEST_QUEUES, 0,
 				(u8 *)vfres, sizeof(*vfres));
+	return ret;
 }
 
 void
@@ -1306,11 +1312,19 @@
 	}
 
 	vf = &pf->vfs[vf_id];
+
+	/* if timer not end, ignore the message and return */
+	if (rte_get_timer_cycles() < vf->silence_end_cycle) {
+		vf->ignored_cmd_cnt++;
+		return;
+	}
+
 	if (!vf->vsi) {
 		PMD_DRV_LOG(ERR, "NO VSI associated with VF found");
 		i40e_pf_host_send_msg_to_vf(vf, opcode,
 			I40E_ERR_NO_AVAILABLE_VSI, NULL, 0);
-		return;
+		ret = I40E_ERR_NO_AVAILABLE_VSI;
+		goto err_cmd;
 	}
 
 	/* perform basic checks on the msg */
@@ -1331,14 +1345,15 @@
 
 	if (ret) {
 		PMD_DRV_LOG(ERR, "Invalid message from VF %u, opcode %u, len %u",
-			    vf_id, opcode, msglen);
+				vf_id, opcode, msglen);
 		i40e_pf_host_send_msg_to_vf(vf, opcode,
-					    I40E_ERR_PARAM, NULL, 0);
-		return;
+				I40E_ERR_PARAM, NULL, 0);
+		ret = I40E_ERR_PARAM;
+		goto err_cmd;
 	}
 
 	/**
-	 * initialise structure to send to user application
+	 * initialize structure to send to user application
 	 * will return response from user in retval field
 	 */
 	ret_param.retval = RTE_PMD_I40E_MB_EVENT_PROCEED;
@@ -1373,78 +1388,84 @@
 		break;
 	case VIRTCHNL_OP_GET_VF_RESOURCES:
 		PMD_DRV_LOG(INFO, "OP_GET_VF_RESOURCES received");
-		i40e_pf_host_process_cmd_get_vf_resource(vf, msg, b_op);
+		ret = i40e_pf_host_process_cmd_get_vf_resource(vf, msg, b_op);
 		break;
 	case VIRTCHNL_OP_CONFIG_VSI_QUEUES:
 		PMD_DRV_LOG(INFO, "OP_CONFIG_VSI_QUEUES received");
-		i40e_pf_host_process_cmd_config_vsi_queues(vf, msg,
+		ret = i40e_pf_host_process_cmd_config_vsi_queues(vf, msg,
 							   msglen, b_op);
 		break;
 	case VIRTCHNL_OP_CONFIG_IRQ_MAP:
 		PMD_DRV_LOG(INFO, "OP_CONFIG_IRQ_MAP received");
-		i40e_pf_host_process_cmd_config_irq_map(vf, msg, msglen, b_op);
+		ret = i40e_pf_host_process_cmd_config_irq_map(vf, msg,
+				msglen, b_op);
 		break;
 	case VIRTCHNL_OP_ENABLE_QUEUES:
 		PMD_DRV_LOG(INFO, "OP_ENABLE_QUEUES received");
 		if (b_op) {
-			i40e_pf_host_process_cmd_enable_queues(vf, msg, msglen);
+			ret = i40e_pf_host_process_cmd_enable_queues(vf,
+					msg, msglen);
 			i40e_notify_vf_link_status(dev, vf);
 		} else {
-			i40e_pf_host_send_msg_to_vf(
-				vf, VIRTCHNL_OP_ENABLE_QUEUES,
-				I40E_NOT_SUPPORTED, NULL, 0);
+			i40e_pf_host_send_msg_to_vf(vf,
+					VIRTCHNL_OP_ENABLE_QUEUES,
+					I40E_NOT_SUPPORTED, NULL, 0);
+			ret = I40E_NOT_SUPPORTED;
 		}
 		break;
 	case VIRTCHNL_OP_DISABLE_QUEUES:
 		PMD_DRV_LOG(INFO, "OP_DISABLE_QUEUE received");
-		i40e_pf_host_process_cmd_disable_queues(vf, msg, msglen, b_op);
+		ret = i40e_pf_host_process_cmd_disable_queues(vf,
+				msg, msglen, b_op);
 		break;
 	case VIRTCHNL_OP_ADD_ETH_ADDR:
 		PMD_DRV_LOG(INFO, "OP_ADD_ETHER_ADDRESS received");
-		i40e_pf_host_process_cmd_add_ether_address(vf, msg,
+		ret = i40e_pf_host_process_cmd_add_ether_address(vf, msg,
 							   msglen, b_op);
 		break;
 	case VIRTCHNL_OP_DEL_ETH_ADDR:
 		PMD_DRV_LOG(INFO, "OP_DEL_ETHER_ADDRESS received");
-		i40e_pf_host_process_cmd_del_ether_address(vf, msg,
+		ret = i40e_pf_host_process_cmd_del_ether_address(vf, msg,
 							   msglen, b_op);
 		break;
 	case VIRTCHNL_OP_ADD_VLAN:
 		PMD_DRV_LOG(INFO, "OP_ADD_VLAN received");
-		i40e_pf_host_process_cmd_add_vlan(vf, msg, msglen, b_op);
+		ret = i40e_pf_host_process_cmd_add_vlan(vf, msg, msglen, b_op);
 		break;
 	case VIRTCHNL_OP_DEL_VLAN:
 		PMD_DRV_LOG(INFO, "OP_DEL_VLAN received");
-		i40e_pf_host_process_cmd_del_vlan(vf, msg, msglen, b_op);
+		ret = i40e_pf_host_process_cmd_del_vlan(vf, msg, msglen, b_op);
 		break;
 	case VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE:
 		PMD_DRV_LOG(INFO, "OP_CONFIG_PROMISCUOUS_MODE received");
-		i40e_pf_host_process_cmd_config_promisc_mode(vf, msg,
+		ret = i40e_pf_host_process_cmd_config_promisc_mode(vf, msg,
 							     msglen, b_op);
 		break;
 	case VIRTCHNL_OP_GET_STATS:
 		PMD_DRV_LOG(INFO, "OP_GET_STATS received");
-		i40e_pf_host_process_cmd_get_stats(vf, b_op);
+		ret = i40e_pf_host_process_cmd_get_stats(vf, b_op);
 		break;
 	case VIRTCHNL_OP_ENABLE_VLAN_STRIPPING:
 		PMD_DRV_LOG(INFO, "OP_ENABLE_VLAN_STRIPPING received");
-		i40e_pf_host_process_cmd_enable_vlan_strip(vf, b_op);
+		ret = i40e_pf_host_process_cmd_enable_vlan_strip(vf, b_op);
 		break;
 	case VIRTCHNL_OP_DISABLE_VLAN_STRIPPING:
 		PMD_DRV_LOG(INFO, "OP_DISABLE_VLAN_STRIPPING received");
-		i40e_pf_host_process_cmd_disable_vlan_strip(vf, b_op);
+		ret = i40e_pf_host_process_cmd_disable_vlan_strip(vf, b_op);
 		break;
 	case VIRTCHNL_OP_CONFIG_RSS_LUT:
 		PMD_DRV_LOG(INFO, "OP_CONFIG_RSS_LUT received");
-		i40e_pf_host_process_cmd_set_rss_lut(vf, msg, msglen, b_op);
+		ret = i40e_pf_host_process_cmd_set_rss_lut(vf, msg,
+				msglen, b_op);
 		break;
 	case VIRTCHNL_OP_CONFIG_RSS_KEY:
 		PMD_DRV_LOG(INFO, "OP_CONFIG_RSS_KEY received");
-		i40e_pf_host_process_cmd_set_rss_key(vf, msg, msglen, b_op);
+		ret = i40e_pf_host_process_cmd_set_rss_key(vf, msg,
+				msglen, b_op);
 		break;
 	case VIRTCHNL_OP_REQUEST_QUEUES:
 		PMD_DRV_LOG(INFO, "OP_REQUEST_QUEUES received");
-		i40e_pf_host_process_cmd_request_queues(vf, msg);
+		ret = i40e_pf_host_process_cmd_request_queues(vf, msg);
 		break;
 
 	/* Don't add command supported below, which will
@@ -1452,10 +1473,86 @@
 	 */
 	default:
 		PMD_DRV_LOG(ERR, "%u received, not supported", opcode);
-		i40e_pf_host_send_msg_to_vf(vf, opcode, I40E_ERR_PARAM,
+		i40e_pf_host_send_msg_to_vf(vf, opcode, I40E_NOT_SUPPORTED,
 								NULL, 0);
+		ret = I40E_NOT_SUPPORTED;
 		break;
 	}
+
+err_cmd:
+	/* If the amount of invalid or unsupported message been received
+	 * from a VF exceed maximal limitation, PF will start a timer.
+	 * Before the timer timed out, PF should ignore any message from
+	 * that VF.
+	 * Once the timer timed out, PF will accept a message from the VF,
+	 * if this message is still invalid or unsupported, PF will
+	 * restart the timer and enter another loop.
+	 * If the last commond execute succeed and within 'silence_seconds'
+	 * period of time, no invalid or unsupported message been
+	 * received from same VF, the counter of 'invalid/unsupported'
+	 * message for that VF will be reset to zero.
+	 */
+	vf->silence_end_cycle = 0;
+	switch (ret) {
+	case I40E_SUCCESS:
+		if ((vf->invalid_cmd_cnt || vf->unsupported_cmd_cnt) &&
+				rte_get_timer_cycles() >=
+				vf->last_wrong_msg_cycle +
+				pf->wrong_vf_msg_conf.silence_seconds *
+				rte_get_timer_hz()) {
+			PMD_DRV_LOG(WARNING, "VF %u message, ignored %lu, "
+					"invalid %lu, unsupported %lu", vf_id,
+					vf->ignored_cmd_cnt, vf->invalid_total,
+					vf->unsupported_total);
+			vf->unsupported_cmd_cnt = 0;
+			vf->invalid_cmd_cnt = 0;
+		}
+		break;
+
+	case I40E_ERR_PARAM:
+	case I40E_ERR_NO_AVAILABLE_VSI:
+		vf->invalid_total++;
+		if (!pf->wrong_vf_msg_conf.max_invalid)
+			break;
+		vf->invalid_cmd_cnt++;
+		if (vf->invalid_cmd_cnt >
+				pf->wrong_vf_msg_conf.max_invalid) {
+			PMD_DRV_LOG(ERR, "VF %u too much continuous invalid"
+					" message(%u, maximum %u, total %lu)!",
+					vf_id, vf->invalid_cmd_cnt,
+					pf->wrong_vf_msg_conf.max_invalid,
+					vf->invalid_total);
+			vf->silence_end_cycle = rte_get_timer_cycles() +
+					pf->wrong_vf_msg_conf.silence_seconds
+					* rte_get_timer_hz();
+		}
+
+		vf->last_wrong_msg_cycle = rte_get_timer_cycles();
+		break;
+
+	case I40E_NOT_SUPPORTED:
+		vf->unsupported_total++;
+		if (!pf->wrong_vf_msg_conf.max_unsupported)
+			break;
+		vf->unsupported_cmd_cnt++;
+		if (vf->unsupported_cmd_cnt >
+				pf->wrong_vf_msg_conf.max_unsupported) {
+			PMD_DRV_LOG(ERR, "VF %u too much continuous unsupported"
+					" message(%u, maximum %u, total %lu)!",
+					vf_id, vf->unsupported_cmd_cnt,
+					pf->wrong_vf_msg_conf.max_unsupported,
+					vf->unsupported_total);
+			vf->silence_end_cycle = rte_get_timer_cycles() +
+					pf->wrong_vf_msg_conf.silence_seconds
+					* rte_get_timer_hz();
+		}
+
+		vf->last_wrong_msg_cycle = rte_get_timer_cycles();
+		break;
+	default:
+		break;
+	}
+	return;
 }
 
 int
@@ -1493,6 +1590,12 @@
 		pf->vfs[i].pf = pf;
 		pf->vfs[i].state = I40E_VF_INACTIVE;
 		pf->vfs[i].vf_idx = i;
+
+		pf->vfs[i].invalid_cmd_cnt = 0;
+		pf->vfs[i].unsupported_cmd_cnt = 0;
+		pf->vfs[i].silence_end_cycle = 0;
+		pf->vfs[i].last_wrong_msg_cycle = 0;
+
 		ret = i40e_pf_host_vf_reset(&pf->vfs[i], 0);
 		if (ret != I40E_SUCCESS)
 			goto fail;
-- 
1.8.3.1


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [dpdk-dev] [PATCH v3] net/i40e: validate all messages from VF
  2019-08-28  7:13 ` [dpdk-dev] [PATCH v2] " alvinx.zhang
@ 2019-09-09 10:01   ` alvinx.zhang
  2019-09-09 15:11     ` [dpdk-dev] [PATCH v4] " alvinx.zhang
  0 siblings, 1 reply; 14+ messages in thread
From: alvinx.zhang @ 2019-09-09 10:01 UTC (permalink / raw)
  To: qi.z.zhang, beilei.xing; +Cc: dev, Alvin Zhang

From: Alvin Zhang <alvinx.zhang@intel.com>

If VF driver in VM continuous sending invalid messages by mailbox,
it will waste CPU cycles on PF driver and impact other VF drivers
configuration. I40E PF PMD can count the numbers of invalid and
unsupported messages from VFs, when the counter of statistics from
a VF exceed maximum limitation, PF driver will ignore all of
messages from that VF for some seconds.

Signed-off-by: Alvin Zhang <alvinx.zhang@intel.com>
---
 doc/guides/nics/i40e.rst       |  12 +++
 drivers/net/i40e/i40e_ethdev.c |  82 ++++++++++++++++++
 drivers/net/i40e/i40e_ethdev.h |  32 +++++++
 drivers/net/i40e/i40e_pf.c     | 185 ++++++++++++++++++++++++++++++++---------
 4 files changed, 270 insertions(+), 41 deletions(-)

diff --git a/doc/guides/nics/i40e.rst b/doc/guides/nics/i40e.rst
index 0884e15..5c7bdb0 100644
--- a/doc/guides/nics/i40e.rst
+++ b/doc/guides/nics/i40e.rst
@@ -185,6 +185,18 @@ Runtime Config Options
 
   -w 84:00.0,use-latest-supported-vec=1
 
+- ``Enable validation for VF message`` (default ``not enabled``)
+
+  The i40e PF PMD supports message validation which from VFs. This will count the
+  numbers of continuous invalid and unsupported messages from VFs. If the counter of
+  statistics from a VF exceed maximum limitation, PF driver will ignore all of messages
+  from that VF for some seconds. Using the ``devargs`` option ``vf_max_wrong_msg``,
+  user can specify how many continuous invalid and unsupported message that PF driver
+  could tolerate and how many seconds during which PF driver will ignore all of
+  messages from a VF, for example::
+
+  -w 84:00.0,vf_max_wrong_msg=4,6,30
+
 Vector RX Pre-conditions
 ~~~~~~~~~~~~~~~~~~~~~~~~
 For Vector RX it is assumed that the number of descriptor rings will be a power
diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index 4e40b7a..3d09fc2 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -44,6 +44,7 @@
 #define ETH_I40E_SUPPORT_MULTI_DRIVER	"support-multi-driver"
 #define ETH_I40E_QUEUE_NUM_PER_VF_ARG	"queue-num-per-vf"
 #define ETH_I40E_USE_LATEST_VEC	"use-latest-supported-vec"
+#define ETH_I40E_MAX_VF_WRONG_MSG	"vf_max_wrong_msg"
 
 #define I40E_CLEAR_PXE_WAIT_MS     200
 
@@ -406,6 +407,7 @@ static int i40e_sw_tunnel_filter_insert(struct i40e_pf *pf,
 	ETH_I40E_SUPPORT_MULTI_DRIVER,
 	ETH_I40E_QUEUE_NUM_PER_VF_ARG,
 	ETH_I40E_USE_LATEST_VEC,
+	ETH_I40E_MAX_VF_WRONG_MSG,
 	NULL};
 
 static const struct rte_pci_id pci_id_i40e_map[] = {
@@ -1256,6 +1258,84 @@ static inline void i40e_config_automask(struct i40e_pf *pf)
 	return 0;
 }
 
+static int
+read_vf_msg_check_info(__rte_unused const char *key,
+			       const char *value,
+			       void *opaque)
+{
+	struct i40e_wrong_vf_msg info;
+
+	memset(&info, 0, sizeof(info));
+	/*
+	 * VF message checking function need 3 parameters, max_invalid,
+	 * max_unsupported and silence_seconds.
+	 * When continuous invalid or unsupported message statistics
+	 * from a VF exceed the limitation of 'max_invalid' or
+	 * 'max_unsupported', PF will ignore any message from that VF for
+	 * 'silence_seconds' seconds.
+	 */
+	if (sscanf(value, "%u:%u:%u", &info.max_invalid,
+			&info.max_unsupported, &info.silence_seconds)
+			!= 3) {
+		PMD_DRV_LOG(ERR, "vf_max_wrong_msg error! format like: "
+				"vf_max_wrong_msg=4:6:60");
+		return -EINVAL;
+	}
+
+	/*
+	 * If invalid or unsupported message checking function is enabled
+	 * by setting max_invalid or max_unsupported variable to not zero,
+	 * 'slience_seconds' must be greater than zero.
+	 */
+	if ((info.max_invalid || info.max_unsupported) &&
+			!info.silence_seconds) {
+		PMD_DRV_LOG(ERR, "vf_max_wrong_msg error! last integer"
+				" must be larger than zero");
+		return -EINVAL;
+	}
+
+	memcpy(opaque, &info, sizeof(struct i40e_wrong_vf_msg));
+	return 0;
+}
+
+static int
+i40e_parse_vf_msg_check_info(struct rte_eth_dev *dev,
+		struct i40e_wrong_vf_msg *wrong_info)
+{
+	int ret = 0;
+	int kvargs_count;
+	struct rte_kvargs *kvlist;
+
+	/* reset all to zero */
+	memset(wrong_info, 0, sizeof(*wrong_info));
+
+	if (!dev->device->devargs)
+		return ret;
+
+	kvlist = rte_kvargs_parse(dev->device->devargs->args, valid_keys);
+	if (!kvlist)
+		return -EINVAL;
+
+	kvargs_count = rte_kvargs_count(kvlist, ETH_I40E_MAX_VF_WRONG_MSG);
+	if (!kvargs_count)
+		goto free_end;
+
+	if (kvargs_count > 1) {
+		PMD_DRV_LOG(ERR, "More than one argument \"%s\"!",
+				ETH_I40E_MAX_VF_WRONG_MSG);
+		ret = -EINVAL;
+		goto free_end;
+	}
+
+	if (rte_kvargs_process(kvlist, ETH_I40E_MAX_VF_WRONG_MSG,
+			read_vf_msg_check_info, wrong_info) < 0)
+		ret = -EINVAL;
+
+free_end:
+	rte_kvargs_free(kvlist);
+	return ret;
+}
+
 #define I40E_ALARM_INTERVAL 50000 /* us */
 
 static int
@@ -1328,6 +1408,8 @@ static inline void i40e_config_automask(struct i40e_pf *pf)
 		return -EIO;
 	}
 
+	/* read VF message checking function parameters */
+	i40e_parse_vf_msg_check_info(dev, &pf->wrong_vf_msg_conf);
 	/* Check if need to support multi-driver */
 	i40e_support_multi_driver(dev);
 	/* Check if users want the latest supported vec path */
diff --git a/drivers/net/i40e/i40e_ethdev.h b/drivers/net/i40e/i40e_ethdev.h
index 38ac3ea..24e10c5 100644
--- a/drivers/net/i40e/i40e_ethdev.h
+++ b/drivers/net/i40e/i40e_ethdev.h
@@ -426,6 +426,25 @@ struct i40e_pf_vf {
 	/* version of the virtchnl from VF */
 	struct virtchnl_version_info version;
 	uint32_t request_caps; /* offload caps requested from VF */
+
+	/*
+	 * Counter of message from VF
+	 * invalid_cmd_cnt, invalid command since last valid command
+	 * unsupported_cmd_cnt, unsupported command since last valid command
+	 * invalid_total, total invalid command
+	 * unsupported_total, total unsupported command
+	 * ignored_cmd_cnt, ignored command in silence
+	 */
+	uint16_t invalid_cmd_cnt;
+	uint16_t unsupported_cmd_cnt;
+	uint32_t invalid_total;
+	uint32_t unsupported_total;
+	uint32_t ignored_cmd_cnt;
+
+	/* cycle of stop ignoring VF message */
+	uint64_t silence_end_cycle;
+	/* cycle of receive last invalid or unsupported message from VF*/
+	uint64_t last_wrong_msg_cycle;
 };
 
 /*
@@ -900,6 +919,17 @@ struct i40e_rte_flow_rss_conf {
 	uint16_t queue[I40E_MAX_Q_PER_TC]; /**< Queues indices to use. */
 };
 
+struct i40e_wrong_vf_msg {
+	uint32_t max_invalid; /* maximal continuous invalid message from VF */
+	/* maximal continuous unsupported message from VF */
+	uint32_t max_unsupported;
+	/*
+	 * silence seconds when VF send much more invalid or unsupported
+	 * message
+	 */
+	uint32_t silence_seconds;
+};
+
 /*
  * Structure to store private data specific for PF instance.
  */
@@ -975,6 +1005,8 @@ struct i40e_pf {
 	struct i40e_customized_pctype customized_pctype[I40E_CUSTOMIZED_MAX];
 	/* Switch Domain Id */
 	uint16_t switch_domain_id;
+
+	struct i40e_wrong_vf_msg wrong_vf_msg_conf;
 };
 
 enum pending_msg {
diff --git a/drivers/net/i40e/i40e_pf.c b/drivers/net/i40e/i40e_pf.c
index b28d02e..8835f9c 100644
--- a/drivers/net/i40e/i40e_pf.c
+++ b/drivers/net/i40e/i40e_pf.c
@@ -297,7 +297,7 @@
 		i40e_pf_host_send_msg_to_vf(vf,
 					    VIRTCHNL_OP_GET_VF_RESOURCES,
 					    I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	/* only have 1 VSI by default */
@@ -488,7 +488,7 @@
 		i40e_pf_host_send_msg_to_vf(vf,
 					    VIRTCHNL_OP_CONFIG_VSI_QUEUES,
 					    I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	if (!msg || vc_vqci->num_queue_pairs > vsi->nb_qps ||
@@ -655,7 +655,7 @@
 			vf,
 			VIRTCHNL_OP_CONFIG_IRQ_MAP,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	if (msg == NULL || msglen < sizeof(struct virtchnl_irq_map_info)) {
@@ -795,7 +795,7 @@
 			vf,
 			VIRTCHNL_OP_DISABLE_QUEUES,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	if (msg == NULL || msglen != sizeof(*q_sel)) {
@@ -830,7 +830,7 @@
 			vf,
 			VIRTCHNL_OP_ADD_ETH_ADDR,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	memset(&filter, 0 , sizeof(struct i40e_mac_filter_info));
@@ -876,7 +876,7 @@
 			vf,
 			VIRTCHNL_OP_DEL_ETH_ADDR,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	if (msg == NULL || msglen <= sizeof(*addr_list)) {
@@ -917,7 +917,7 @@
 			vf,
 			VIRTCHNL_OP_ADD_VLAN,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	if (msg == NULL || msglen <= sizeof(*vlan_filter_list)) {
@@ -958,7 +958,7 @@
 			vf,
 			VIRTCHNL_OP_DEL_VLAN,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	if (msg == NULL || msglen <= sizeof(*vlan_filter_list)) {
@@ -999,7 +999,7 @@
 			vf,
 			VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	if (msg == NULL || msglen != sizeof(*promisc)) {
@@ -1031,16 +1031,18 @@
 {
 	i40e_update_vsi_stats(vf->vsi);
 
-	if (b_op)
+	if (b_op) {
 		i40e_pf_host_send_msg_to_vf(vf, VIRTCHNL_OP_GET_STATS,
 					    I40E_SUCCESS,
 					    (uint8_t *)&vf->vsi->eth_stats,
 					    sizeof(vf->vsi->eth_stats));
-	else
+	} else {
 		i40e_pf_host_send_msg_to_vf(vf, VIRTCHNL_OP_GET_STATS,
 					    I40E_NOT_SUPPORTED,
 					    (uint8_t *)&vf->vsi->eth_stats,
 					    sizeof(vf->vsi->eth_stats));
+		return I40E_NOT_SUPPORTED;
+	}
 
 	return I40E_SUCCESS;
 }
@@ -1055,7 +1057,7 @@
 			vf,
 			VIRTCHNL_OP_ENABLE_VLAN_STRIPPING,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	ret = i40e_vsi_config_vlan_stripping(vf->vsi, TRUE);
@@ -1078,7 +1080,7 @@
 			vf,
 			VIRTCHNL_OP_DISABLE_VLAN_STRIPPING,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	ret = i40e_vsi_config_vlan_stripping(vf->vsi, FALSE);
@@ -1106,7 +1108,7 @@
 			vf,
 			VIRTCHNL_OP_CONFIG_RSS_LUT,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	if (!msg || msglen <= sizeof(struct virtchnl_rss_lut)) {
@@ -1247,6 +1249,7 @@
 	struct i40e_pf *pf;
 	uint32_t req_pairs = vfres->num_queue_pairs;
 	uint32_t cur_pairs = vf->vsi->nb_used_qps;
+	int ret = I40E_SUCCESS;
 
 	pf = vf->pf;
 
@@ -1256,12 +1259,14 @@
 	if (req_pairs == 0) {
 		PMD_DRV_LOG(ERR, "VF %d tried to request 0 queues. Ignoring.\n",
 			    vf->vf_idx);
+		ret = I40E_ERR_PARAM;
 	} else if (req_pairs > I40E_MAX_QP_NUM_PER_VF) {
 		PMD_DRV_LOG(ERR,
 			    "VF %d tried to request more than %d queues.\n",
 			    vf->vf_idx,
 			    I40E_MAX_QP_NUM_PER_VF);
 		vfres->num_queue_pairs = I40E_MAX_QP_NUM_PER_VF;
+		ret = I40E_ERR_PARAM;
 	} else if (req_pairs > cur_pairs + pf->qp_pool.num_free) {
 		PMD_DRV_LOG(ERR, "VF %d requested %d queues (rounded to %d) "
 			"but only %d available\n",
@@ -1277,11 +1282,12 @@
 		pf->vf_nb_qps = req_pairs;
 		i40e_pf_host_process_cmd_reset_vf(vf);
 
-		return 0;
+		return I40E_SUCCESS;
 	}
 
-	return i40e_pf_host_send_msg_to_vf(vf, VIRTCHNL_OP_REQUEST_QUEUES, 0,
+	i40e_pf_host_send_msg_to_vf(vf, VIRTCHNL_OP_REQUEST_QUEUES, 0,
 				(u8 *)vfres, sizeof(*vfres));
+	return ret;
 }
 
 void
@@ -1306,11 +1312,19 @@
 	}
 
 	vf = &pf->vfs[vf_id];
+
+	/* if timer not end, ignore the message and return */
+	if (rte_get_timer_cycles() < vf->silence_end_cycle) {
+		vf->ignored_cmd_cnt++;
+		return;
+	}
+
 	if (!vf->vsi) {
 		PMD_DRV_LOG(ERR, "NO VSI associated with VF found");
 		i40e_pf_host_send_msg_to_vf(vf, opcode,
 			I40E_ERR_NO_AVAILABLE_VSI, NULL, 0);
-		return;
+		ret = I40E_ERR_NO_AVAILABLE_VSI;
+		goto err_cmd;
 	}
 
 	/* perform basic checks on the msg */
@@ -1331,14 +1345,15 @@
 
 	if (ret) {
 		PMD_DRV_LOG(ERR, "Invalid message from VF %u, opcode %u, len %u",
-			    vf_id, opcode, msglen);
+				vf_id, opcode, msglen);
 		i40e_pf_host_send_msg_to_vf(vf, opcode,
-					    I40E_ERR_PARAM, NULL, 0);
-		return;
+				I40E_ERR_PARAM, NULL, 0);
+		ret = I40E_ERR_PARAM;
+		goto err_cmd;
 	}
 
 	/**
-	 * initialise structure to send to user application
+	 * initialize structure to send to user application
 	 * will return response from user in retval field
 	 */
 	ret_param.retval = RTE_PMD_I40E_MB_EVENT_PROCEED;
@@ -1373,78 +1388,84 @@
 		break;
 	case VIRTCHNL_OP_GET_VF_RESOURCES:
 		PMD_DRV_LOG(INFO, "OP_GET_VF_RESOURCES received");
-		i40e_pf_host_process_cmd_get_vf_resource(vf, msg, b_op);
+		ret = i40e_pf_host_process_cmd_get_vf_resource(vf, msg, b_op);
 		break;
 	case VIRTCHNL_OP_CONFIG_VSI_QUEUES:
 		PMD_DRV_LOG(INFO, "OP_CONFIG_VSI_QUEUES received");
-		i40e_pf_host_process_cmd_config_vsi_queues(vf, msg,
+		ret = i40e_pf_host_process_cmd_config_vsi_queues(vf, msg,
 							   msglen, b_op);
 		break;
 	case VIRTCHNL_OP_CONFIG_IRQ_MAP:
 		PMD_DRV_LOG(INFO, "OP_CONFIG_IRQ_MAP received");
-		i40e_pf_host_process_cmd_config_irq_map(vf, msg, msglen, b_op);
+		ret = i40e_pf_host_process_cmd_config_irq_map(vf, msg,
+				msglen, b_op);
 		break;
 	case VIRTCHNL_OP_ENABLE_QUEUES:
 		PMD_DRV_LOG(INFO, "OP_ENABLE_QUEUES received");
 		if (b_op) {
-			i40e_pf_host_process_cmd_enable_queues(vf, msg, msglen);
+			ret = i40e_pf_host_process_cmd_enable_queues(vf,
+					msg, msglen);
 			i40e_notify_vf_link_status(dev, vf);
 		} else {
-			i40e_pf_host_send_msg_to_vf(
-				vf, VIRTCHNL_OP_ENABLE_QUEUES,
-				I40E_NOT_SUPPORTED, NULL, 0);
+			i40e_pf_host_send_msg_to_vf(vf,
+					VIRTCHNL_OP_ENABLE_QUEUES,
+					I40E_NOT_SUPPORTED, NULL, 0);
+			ret = I40E_NOT_SUPPORTED;
 		}
 		break;
 	case VIRTCHNL_OP_DISABLE_QUEUES:
 		PMD_DRV_LOG(INFO, "OP_DISABLE_QUEUE received");
-		i40e_pf_host_process_cmd_disable_queues(vf, msg, msglen, b_op);
+		ret = i40e_pf_host_process_cmd_disable_queues(vf,
+				msg, msglen, b_op);
 		break;
 	case VIRTCHNL_OP_ADD_ETH_ADDR:
 		PMD_DRV_LOG(INFO, "OP_ADD_ETHER_ADDRESS received");
-		i40e_pf_host_process_cmd_add_ether_address(vf, msg,
+		ret = i40e_pf_host_process_cmd_add_ether_address(vf, msg,
 							   msglen, b_op);
 		break;
 	case VIRTCHNL_OP_DEL_ETH_ADDR:
 		PMD_DRV_LOG(INFO, "OP_DEL_ETHER_ADDRESS received");
-		i40e_pf_host_process_cmd_del_ether_address(vf, msg,
+		ret = i40e_pf_host_process_cmd_del_ether_address(vf, msg,
 							   msglen, b_op);
 		break;
 	case VIRTCHNL_OP_ADD_VLAN:
 		PMD_DRV_LOG(INFO, "OP_ADD_VLAN received");
-		i40e_pf_host_process_cmd_add_vlan(vf, msg, msglen, b_op);
+		ret = i40e_pf_host_process_cmd_add_vlan(vf, msg, msglen, b_op);
 		break;
 	case VIRTCHNL_OP_DEL_VLAN:
 		PMD_DRV_LOG(INFO, "OP_DEL_VLAN received");
-		i40e_pf_host_process_cmd_del_vlan(vf, msg, msglen, b_op);
+		ret = i40e_pf_host_process_cmd_del_vlan(vf, msg, msglen, b_op);
 		break;
 	case VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE:
 		PMD_DRV_LOG(INFO, "OP_CONFIG_PROMISCUOUS_MODE received");
-		i40e_pf_host_process_cmd_config_promisc_mode(vf, msg,
+		ret = i40e_pf_host_process_cmd_config_promisc_mode(vf, msg,
 							     msglen, b_op);
 		break;
 	case VIRTCHNL_OP_GET_STATS:
 		PMD_DRV_LOG(INFO, "OP_GET_STATS received");
-		i40e_pf_host_process_cmd_get_stats(vf, b_op);
+		ret = i40e_pf_host_process_cmd_get_stats(vf, b_op);
 		break;
 	case VIRTCHNL_OP_ENABLE_VLAN_STRIPPING:
 		PMD_DRV_LOG(INFO, "OP_ENABLE_VLAN_STRIPPING received");
-		i40e_pf_host_process_cmd_enable_vlan_strip(vf, b_op);
+		ret = i40e_pf_host_process_cmd_enable_vlan_strip(vf, b_op);
 		break;
 	case VIRTCHNL_OP_DISABLE_VLAN_STRIPPING:
 		PMD_DRV_LOG(INFO, "OP_DISABLE_VLAN_STRIPPING received");
-		i40e_pf_host_process_cmd_disable_vlan_strip(vf, b_op);
+		ret = i40e_pf_host_process_cmd_disable_vlan_strip(vf, b_op);
 		break;
 	case VIRTCHNL_OP_CONFIG_RSS_LUT:
 		PMD_DRV_LOG(INFO, "OP_CONFIG_RSS_LUT received");
-		i40e_pf_host_process_cmd_set_rss_lut(vf, msg, msglen, b_op);
+		ret = i40e_pf_host_process_cmd_set_rss_lut(vf, msg,
+				msglen, b_op);
 		break;
 	case VIRTCHNL_OP_CONFIG_RSS_KEY:
 		PMD_DRV_LOG(INFO, "OP_CONFIG_RSS_KEY received");
-		i40e_pf_host_process_cmd_set_rss_key(vf, msg, msglen, b_op);
+		ret = i40e_pf_host_process_cmd_set_rss_key(vf, msg,
+				msglen, b_op);
 		break;
 	case VIRTCHNL_OP_REQUEST_QUEUES:
 		PMD_DRV_LOG(INFO, "OP_REQUEST_QUEUES received");
-		i40e_pf_host_process_cmd_request_queues(vf, msg);
+		ret = i40e_pf_host_process_cmd_request_queues(vf, msg);
 		break;
 
 	/* Don't add command supported below, which will
@@ -1452,10 +1473,86 @@
 	 */
 	default:
 		PMD_DRV_LOG(ERR, "%u received, not supported", opcode);
-		i40e_pf_host_send_msg_to_vf(vf, opcode, I40E_ERR_PARAM,
+		i40e_pf_host_send_msg_to_vf(vf, opcode, I40E_NOT_SUPPORTED,
 								NULL, 0);
+		ret = I40E_NOT_SUPPORTED;
 		break;
 	}
+
+err_cmd:
+	/* If the amount of invalid or unsupported message been received
+	 * from a VF exceed maximal limitation, PF will start a timer.
+	 * Before the timer timed out, PF should ignore any message from
+	 * that VF.
+	 * Once the timer timed out, PF will accept a message from the VF,
+	 * if this message is still invalid or unsupported, PF will
+	 * restart the timer and enter another loop.
+	 * If the last commond execute succeed and within 'silence_seconds'
+	 * period of time, no invalid or unsupported message been
+	 * received from same VF, the counter of 'invalid/unsupported'
+	 * message for that VF will be reset to zero.
+	 */
+	vf->silence_end_cycle = 0;
+	switch (ret) {
+	case I40E_SUCCESS:
+		if ((vf->invalid_cmd_cnt || vf->unsupported_cmd_cnt) &&
+				rte_get_timer_cycles() >=
+				vf->last_wrong_msg_cycle +
+				pf->wrong_vf_msg_conf.silence_seconds *
+				rte_get_timer_hz()) {
+			PMD_DRV_LOG(WARNING, "VF %u message, ignored %u, "
+					"invalid %u, unsupported %u", vf_id,
+					vf->ignored_cmd_cnt, vf->invalid_total,
+					vf->unsupported_total);
+			vf->unsupported_cmd_cnt = 0;
+			vf->invalid_cmd_cnt = 0;
+		}
+		break;
+
+	case I40E_ERR_PARAM:
+	case I40E_ERR_NO_AVAILABLE_VSI:
+		vf->invalid_total++;
+		if (!pf->wrong_vf_msg_conf.max_invalid)
+			break;
+		vf->invalid_cmd_cnt++;
+		if (vf->invalid_cmd_cnt >
+				pf->wrong_vf_msg_conf.max_invalid) {
+			PMD_DRV_LOG(ERR, "VF %u too much continuous invalid"
+					" message(%u, maximum %u, total %u)!",
+					vf_id, vf->invalid_cmd_cnt,
+					pf->wrong_vf_msg_conf.max_invalid,
+					vf->invalid_total);
+			vf->silence_end_cycle = rte_get_timer_cycles() +
+					pf->wrong_vf_msg_conf.silence_seconds
+					* rte_get_timer_hz();
+		}
+
+		vf->last_wrong_msg_cycle = rte_get_timer_cycles();
+		break;
+
+	case I40E_NOT_SUPPORTED:
+		vf->unsupported_total++;
+		if (!pf->wrong_vf_msg_conf.max_unsupported)
+			break;
+		vf->unsupported_cmd_cnt++;
+		if (vf->unsupported_cmd_cnt >
+				pf->wrong_vf_msg_conf.max_unsupported) {
+			PMD_DRV_LOG(ERR, "VF %u too much continuous unsupported"
+					" message(%u, maximum %u, total %u)!",
+					vf_id, vf->unsupported_cmd_cnt,
+					pf->wrong_vf_msg_conf.max_unsupported,
+					vf->unsupported_total);
+			vf->silence_end_cycle = rte_get_timer_cycles() +
+					pf->wrong_vf_msg_conf.silence_seconds
+					* rte_get_timer_hz();
+		}
+
+		vf->last_wrong_msg_cycle = rte_get_timer_cycles();
+		break;
+	default:
+		break;
+	}
+	return;
 }
 
 int
@@ -1493,6 +1590,12 @@
 		pf->vfs[i].pf = pf;
 		pf->vfs[i].state = I40E_VF_INACTIVE;
 		pf->vfs[i].vf_idx = i;
+
+		pf->vfs[i].invalid_cmd_cnt = 0;
+		pf->vfs[i].unsupported_cmd_cnt = 0;
+		pf->vfs[i].silence_end_cycle = 0;
+		pf->vfs[i].last_wrong_msg_cycle = 0;
+
 		ret = i40e_pf_host_vf_reset(&pf->vfs[i], 0);
 		if (ret != I40E_SUCCESS)
 			goto fail;
-- 
1.8.3.1


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [dpdk-dev] [PATCH v4] net/i40e: validate all messages from VF
  2019-09-09 15:11     ` [dpdk-dev] [PATCH v4] " alvinx.zhang
@ 2019-09-09 13:34       ` Ye Xiaolong
  2019-09-10 17:12       ` alvinx.zhang
  1 sibling, 0 replies; 14+ messages in thread
From: Ye Xiaolong @ 2019-09-09 13:34 UTC (permalink / raw)
  To: alvinx.zhang; +Cc: qi.z.zhang, beilei.xing, dev

Hi, alvinx


Could you add some changelog to your patch, it would be easier for reviewers/readers
to know what changes you've made compared to last version.

Thanks,
Xiaolong

On 09/09, alvinx.zhang@intel.com wrote:
>From: Alvin Zhang <alvinx.zhang@intel.com>
>
>If VF driver in VM continuous sending invalid messages by mailbox,
>it will waste CPU cycles on PF driver and impact other VF drivers
>configuration. I40E PF PMD can count the numbers of invalid and
>unsupported messages from VFs, when the counter of statistics from
>a VF exceed maximum limitation, PF driver will ignore all of
>messages from that VF for some seconds.
>
>Signed-off-by: Alvin Zhang <alvinx.zhang@intel.com>
>---
> doc/guides/nics/i40e.rst       |  12 +++
> drivers/net/i40e/i40e_ethdev.c |  82 ++++++++++++++++++
> drivers/net/i40e/i40e_ethdev.h |  32 +++++++
> drivers/net/i40e/i40e_pf.c     | 185 ++++++++++++++++++++++++++++++++---------
> 4 files changed, 270 insertions(+), 41 deletions(-)
>
>diff --git a/doc/guides/nics/i40e.rst b/doc/guides/nics/i40e.rst
>index 0884e15..935706d 100644
>--- a/doc/guides/nics/i40e.rst
>+++ b/doc/guides/nics/i40e.rst
>@@ -185,6 +185,18 @@ Runtime Config Options
> 
>   -w 84:00.0,use-latest-supported-vec=1
> 
>+- ``Enable validation for VF message`` (default ``not enabled``)
>+
>+  The i40e PF PMD supports message validation which from VFs. This will count the
>+  numbers of continuous invalid and unsupported messages from VFs. If the counter of
>+  statistics from a VF exceed maximum limitation, PF driver will ignore all of messages
>+  from that VF for some seconds. Using the ``devargs`` option ``vf_max_wrong_msg``,
>+  user can specify how many continuous invalid and unsupported message that PF driver
>+  could tolerate and how many seconds during which PF driver will ignore all of
>+  messages from a VF, for example::
>+
>+  -w 84:00.0,vf_max_wrong_msg=4:6:30
>+
> Vector RX Pre-conditions
> ~~~~~~~~~~~~~~~~~~~~~~~~
> For Vector RX it is assumed that the number of descriptor rings will be a power
>diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
>index 4e40b7a..3d09fc2 100644
>--- a/drivers/net/i40e/i40e_ethdev.c
>+++ b/drivers/net/i40e/i40e_ethdev.c
>@@ -44,6 +44,7 @@
> #define ETH_I40E_SUPPORT_MULTI_DRIVER	"support-multi-driver"
> #define ETH_I40E_QUEUE_NUM_PER_VF_ARG	"queue-num-per-vf"
> #define ETH_I40E_USE_LATEST_VEC	"use-latest-supported-vec"
>+#define ETH_I40E_MAX_VF_WRONG_MSG	"vf_max_wrong_msg"
> 
> #define I40E_CLEAR_PXE_WAIT_MS     200
> 
>@@ -406,6 +407,7 @@ static int i40e_sw_tunnel_filter_insert(struct i40e_pf *pf,
> 	ETH_I40E_SUPPORT_MULTI_DRIVER,
> 	ETH_I40E_QUEUE_NUM_PER_VF_ARG,
> 	ETH_I40E_USE_LATEST_VEC,
>+	ETH_I40E_MAX_VF_WRONG_MSG,
> 	NULL};
> 
> static const struct rte_pci_id pci_id_i40e_map[] = {
>@@ -1256,6 +1258,84 @@ static inline void i40e_config_automask(struct i40e_pf *pf)
> 	return 0;
> }
> 
>+static int
>+read_vf_msg_check_info(__rte_unused const char *key,
>+			       const char *value,
>+			       void *opaque)
>+{
>+	struct i40e_wrong_vf_msg info;
>+
>+	memset(&info, 0, sizeof(info));
>+	/*
>+	 * VF message checking function need 3 parameters, max_invalid,
>+	 * max_unsupported and silence_seconds.
>+	 * When continuous invalid or unsupported message statistics
>+	 * from a VF exceed the limitation of 'max_invalid' or
>+	 * 'max_unsupported', PF will ignore any message from that VF for
>+	 * 'silence_seconds' seconds.
>+	 */
>+	if (sscanf(value, "%u:%u:%u", &info.max_invalid,
>+			&info.max_unsupported, &info.silence_seconds)
>+			!= 3) {
>+		PMD_DRV_LOG(ERR, "vf_max_wrong_msg error! format like: "
>+				"vf_max_wrong_msg=4:6:60");
>+		return -EINVAL;
>+	}
>+
>+	/*
>+	 * If invalid or unsupported message checking function is enabled
>+	 * by setting max_invalid or max_unsupported variable to not zero,
>+	 * 'slience_seconds' must be greater than zero.
>+	 */
>+	if ((info.max_invalid || info.max_unsupported) &&
>+			!info.silence_seconds) {
>+		PMD_DRV_LOG(ERR, "vf_max_wrong_msg error! last integer"
>+				" must be larger than zero");
>+		return -EINVAL;
>+	}
>+
>+	memcpy(opaque, &info, sizeof(struct i40e_wrong_vf_msg));
>+	return 0;
>+}
>+
>+static int
>+i40e_parse_vf_msg_check_info(struct rte_eth_dev *dev,
>+		struct i40e_wrong_vf_msg *wrong_info)
>+{
>+	int ret = 0;
>+	int kvargs_count;
>+	struct rte_kvargs *kvlist;
>+
>+	/* reset all to zero */
>+	memset(wrong_info, 0, sizeof(*wrong_info));
>+
>+	if (!dev->device->devargs)
>+		return ret;
>+
>+	kvlist = rte_kvargs_parse(dev->device->devargs->args, valid_keys);
>+	if (!kvlist)
>+		return -EINVAL;
>+
>+	kvargs_count = rte_kvargs_count(kvlist, ETH_I40E_MAX_VF_WRONG_MSG);
>+	if (!kvargs_count)
>+		goto free_end;
>+
>+	if (kvargs_count > 1) {
>+		PMD_DRV_LOG(ERR, "More than one argument \"%s\"!",
>+				ETH_I40E_MAX_VF_WRONG_MSG);
>+		ret = -EINVAL;
>+		goto free_end;
>+	}
>+
>+	if (rte_kvargs_process(kvlist, ETH_I40E_MAX_VF_WRONG_MSG,
>+			read_vf_msg_check_info, wrong_info) < 0)
>+		ret = -EINVAL;
>+
>+free_end:
>+	rte_kvargs_free(kvlist);
>+	return ret;
>+}
>+
> #define I40E_ALARM_INTERVAL 50000 /* us */
> 
> static int
>@@ -1328,6 +1408,8 @@ static inline void i40e_config_automask(struct i40e_pf *pf)
> 		return -EIO;
> 	}
> 
>+	/* read VF message checking function parameters */
>+	i40e_parse_vf_msg_check_info(dev, &pf->wrong_vf_msg_conf);
> 	/* Check if need to support multi-driver */
> 	i40e_support_multi_driver(dev);
> 	/* Check if users want the latest supported vec path */
>diff --git a/drivers/net/i40e/i40e_ethdev.h b/drivers/net/i40e/i40e_ethdev.h
>index 38ac3ea..24e10c5 100644
>--- a/drivers/net/i40e/i40e_ethdev.h
>+++ b/drivers/net/i40e/i40e_ethdev.h
>@@ -426,6 +426,25 @@ struct i40e_pf_vf {
> 	/* version of the virtchnl from VF */
> 	struct virtchnl_version_info version;
> 	uint32_t request_caps; /* offload caps requested from VF */
>+
>+	/*
>+	 * Counter of message from VF
>+	 * invalid_cmd_cnt, invalid command since last valid command
>+	 * unsupported_cmd_cnt, unsupported command since last valid command
>+	 * invalid_total, total invalid command
>+	 * unsupported_total, total unsupported command
>+	 * ignored_cmd_cnt, ignored command in silence
>+	 */
>+	uint16_t invalid_cmd_cnt;
>+	uint16_t unsupported_cmd_cnt;
>+	uint32_t invalid_total;
>+	uint32_t unsupported_total;
>+	uint32_t ignored_cmd_cnt;
>+
>+	/* cycle of stop ignoring VF message */
>+	uint64_t silence_end_cycle;
>+	/* cycle of receive last invalid or unsupported message from VF*/
>+	uint64_t last_wrong_msg_cycle;
> };
> 
> /*
>@@ -900,6 +919,17 @@ struct i40e_rte_flow_rss_conf {
> 	uint16_t queue[I40E_MAX_Q_PER_TC]; /**< Queues indices to use. */
> };
> 
>+struct i40e_wrong_vf_msg {
>+	uint32_t max_invalid; /* maximal continuous invalid message from VF */
>+	/* maximal continuous unsupported message from VF */
>+	uint32_t max_unsupported;
>+	/*
>+	 * silence seconds when VF send much more invalid or unsupported
>+	 * message
>+	 */
>+	uint32_t silence_seconds;
>+};
>+
> /*
>  * Structure to store private data specific for PF instance.
>  */
>@@ -975,6 +1005,8 @@ struct i40e_pf {
> 	struct i40e_customized_pctype customized_pctype[I40E_CUSTOMIZED_MAX];
> 	/* Switch Domain Id */
> 	uint16_t switch_domain_id;
>+
>+	struct i40e_wrong_vf_msg wrong_vf_msg_conf;
> };
> 
> enum pending_msg {
>diff --git a/drivers/net/i40e/i40e_pf.c b/drivers/net/i40e/i40e_pf.c
>index b28d02e..8835f9c 100644
>--- a/drivers/net/i40e/i40e_pf.c
>+++ b/drivers/net/i40e/i40e_pf.c
>@@ -297,7 +297,7 @@
> 		i40e_pf_host_send_msg_to_vf(vf,
> 					    VIRTCHNL_OP_GET_VF_RESOURCES,
> 					    I40E_NOT_SUPPORTED, NULL, 0);
>-		return ret;
>+		return I40E_NOT_SUPPORTED;
> 	}
> 
> 	/* only have 1 VSI by default */
>@@ -488,7 +488,7 @@
> 		i40e_pf_host_send_msg_to_vf(vf,
> 					    VIRTCHNL_OP_CONFIG_VSI_QUEUES,
> 					    I40E_NOT_SUPPORTED, NULL, 0);
>-		return ret;
>+		return I40E_NOT_SUPPORTED;
> 	}
> 
> 	if (!msg || vc_vqci->num_queue_pairs > vsi->nb_qps ||
>@@ -655,7 +655,7 @@
> 			vf,
> 			VIRTCHNL_OP_CONFIG_IRQ_MAP,
> 			I40E_NOT_SUPPORTED, NULL, 0);
>-		return ret;
>+		return I40E_NOT_SUPPORTED;
> 	}
> 
> 	if (msg == NULL || msglen < sizeof(struct virtchnl_irq_map_info)) {
>@@ -795,7 +795,7 @@
> 			vf,
> 			VIRTCHNL_OP_DISABLE_QUEUES,
> 			I40E_NOT_SUPPORTED, NULL, 0);
>-		return ret;
>+		return I40E_NOT_SUPPORTED;
> 	}
> 
> 	if (msg == NULL || msglen != sizeof(*q_sel)) {
>@@ -830,7 +830,7 @@
> 			vf,
> 			VIRTCHNL_OP_ADD_ETH_ADDR,
> 			I40E_NOT_SUPPORTED, NULL, 0);
>-		return ret;
>+		return I40E_NOT_SUPPORTED;
> 	}
> 
> 	memset(&filter, 0 , sizeof(struct i40e_mac_filter_info));
>@@ -876,7 +876,7 @@
> 			vf,
> 			VIRTCHNL_OP_DEL_ETH_ADDR,
> 			I40E_NOT_SUPPORTED, NULL, 0);
>-		return ret;
>+		return I40E_NOT_SUPPORTED;
> 	}
> 
> 	if (msg == NULL || msglen <= sizeof(*addr_list)) {
>@@ -917,7 +917,7 @@
> 			vf,
> 			VIRTCHNL_OP_ADD_VLAN,
> 			I40E_NOT_SUPPORTED, NULL, 0);
>-		return ret;
>+		return I40E_NOT_SUPPORTED;
> 	}
> 
> 	if (msg == NULL || msglen <= sizeof(*vlan_filter_list)) {
>@@ -958,7 +958,7 @@
> 			vf,
> 			VIRTCHNL_OP_DEL_VLAN,
> 			I40E_NOT_SUPPORTED, NULL, 0);
>-		return ret;
>+		return I40E_NOT_SUPPORTED;
> 	}
> 
> 	if (msg == NULL || msglen <= sizeof(*vlan_filter_list)) {
>@@ -999,7 +999,7 @@
> 			vf,
> 			VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE,
> 			I40E_NOT_SUPPORTED, NULL, 0);
>-		return ret;
>+		return I40E_NOT_SUPPORTED;
> 	}
> 
> 	if (msg == NULL || msglen != sizeof(*promisc)) {
>@@ -1031,16 +1031,18 @@
> {
> 	i40e_update_vsi_stats(vf->vsi);
> 
>-	if (b_op)
>+	if (b_op) {
> 		i40e_pf_host_send_msg_to_vf(vf, VIRTCHNL_OP_GET_STATS,
> 					    I40E_SUCCESS,
> 					    (uint8_t *)&vf->vsi->eth_stats,
> 					    sizeof(vf->vsi->eth_stats));
>-	else
>+	} else {
> 		i40e_pf_host_send_msg_to_vf(vf, VIRTCHNL_OP_GET_STATS,
> 					    I40E_NOT_SUPPORTED,
> 					    (uint8_t *)&vf->vsi->eth_stats,
> 					    sizeof(vf->vsi->eth_stats));
>+		return I40E_NOT_SUPPORTED;
>+	}
> 
> 	return I40E_SUCCESS;
> }
>@@ -1055,7 +1057,7 @@
> 			vf,
> 			VIRTCHNL_OP_ENABLE_VLAN_STRIPPING,
> 			I40E_NOT_SUPPORTED, NULL, 0);
>-		return ret;
>+		return I40E_NOT_SUPPORTED;
> 	}
> 
> 	ret = i40e_vsi_config_vlan_stripping(vf->vsi, TRUE);
>@@ -1078,7 +1080,7 @@
> 			vf,
> 			VIRTCHNL_OP_DISABLE_VLAN_STRIPPING,
> 			I40E_NOT_SUPPORTED, NULL, 0);
>-		return ret;
>+		return I40E_NOT_SUPPORTED;
> 	}
> 
> 	ret = i40e_vsi_config_vlan_stripping(vf->vsi, FALSE);
>@@ -1106,7 +1108,7 @@
> 			vf,
> 			VIRTCHNL_OP_CONFIG_RSS_LUT,
> 			I40E_NOT_SUPPORTED, NULL, 0);
>-		return ret;
>+		return I40E_NOT_SUPPORTED;
> 	}
> 
> 	if (!msg || msglen <= sizeof(struct virtchnl_rss_lut)) {
>@@ -1247,6 +1249,7 @@
> 	struct i40e_pf *pf;
> 	uint32_t req_pairs = vfres->num_queue_pairs;
> 	uint32_t cur_pairs = vf->vsi->nb_used_qps;
>+	int ret = I40E_SUCCESS;
> 
> 	pf = vf->pf;
> 
>@@ -1256,12 +1259,14 @@
> 	if (req_pairs == 0) {
> 		PMD_DRV_LOG(ERR, "VF %d tried to request 0 queues. Ignoring.\n",
> 			    vf->vf_idx);
>+		ret = I40E_ERR_PARAM;
> 	} else if (req_pairs > I40E_MAX_QP_NUM_PER_VF) {
> 		PMD_DRV_LOG(ERR,
> 			    "VF %d tried to request more than %d queues.\n",
> 			    vf->vf_idx,
> 			    I40E_MAX_QP_NUM_PER_VF);
> 		vfres->num_queue_pairs = I40E_MAX_QP_NUM_PER_VF;
>+		ret = I40E_ERR_PARAM;
> 	} else if (req_pairs > cur_pairs + pf->qp_pool.num_free) {
> 		PMD_DRV_LOG(ERR, "VF %d requested %d queues (rounded to %d) "
> 			"but only %d available\n",
>@@ -1277,11 +1282,12 @@
> 		pf->vf_nb_qps = req_pairs;
> 		i40e_pf_host_process_cmd_reset_vf(vf);
> 
>-		return 0;
>+		return I40E_SUCCESS;
> 	}
> 
>-	return i40e_pf_host_send_msg_to_vf(vf, VIRTCHNL_OP_REQUEST_QUEUES, 0,
>+	i40e_pf_host_send_msg_to_vf(vf, VIRTCHNL_OP_REQUEST_QUEUES, 0,
> 				(u8 *)vfres, sizeof(*vfres));
>+	return ret;
> }
> 
> void
>@@ -1306,11 +1312,19 @@
> 	}
> 
> 	vf = &pf->vfs[vf_id];
>+
>+	/* if timer not end, ignore the message and return */
>+	if (rte_get_timer_cycles() < vf->silence_end_cycle) {
>+		vf->ignored_cmd_cnt++;
>+		return;
>+	}
>+
> 	if (!vf->vsi) {
> 		PMD_DRV_LOG(ERR, "NO VSI associated with VF found");
> 		i40e_pf_host_send_msg_to_vf(vf, opcode,
> 			I40E_ERR_NO_AVAILABLE_VSI, NULL, 0);
>-		return;
>+		ret = I40E_ERR_NO_AVAILABLE_VSI;
>+		goto err_cmd;
> 	}
> 
> 	/* perform basic checks on the msg */
>@@ -1331,14 +1345,15 @@
> 
> 	if (ret) {
> 		PMD_DRV_LOG(ERR, "Invalid message from VF %u, opcode %u, len %u",
>-			    vf_id, opcode, msglen);
>+				vf_id, opcode, msglen);
> 		i40e_pf_host_send_msg_to_vf(vf, opcode,
>-					    I40E_ERR_PARAM, NULL, 0);
>-		return;
>+				I40E_ERR_PARAM, NULL, 0);
>+		ret = I40E_ERR_PARAM;
>+		goto err_cmd;
> 	}
> 
> 	/**
>-	 * initialise structure to send to user application
>+	 * initialize structure to send to user application
> 	 * will return response from user in retval field
> 	 */
> 	ret_param.retval = RTE_PMD_I40E_MB_EVENT_PROCEED;
>@@ -1373,78 +1388,84 @@
> 		break;
> 	case VIRTCHNL_OP_GET_VF_RESOURCES:
> 		PMD_DRV_LOG(INFO, "OP_GET_VF_RESOURCES received");
>-		i40e_pf_host_process_cmd_get_vf_resource(vf, msg, b_op);
>+		ret = i40e_pf_host_process_cmd_get_vf_resource(vf, msg, b_op);
> 		break;
> 	case VIRTCHNL_OP_CONFIG_VSI_QUEUES:
> 		PMD_DRV_LOG(INFO, "OP_CONFIG_VSI_QUEUES received");
>-		i40e_pf_host_process_cmd_config_vsi_queues(vf, msg,
>+		ret = i40e_pf_host_process_cmd_config_vsi_queues(vf, msg,
> 							   msglen, b_op);
> 		break;
> 	case VIRTCHNL_OP_CONFIG_IRQ_MAP:
> 		PMD_DRV_LOG(INFO, "OP_CONFIG_IRQ_MAP received");
>-		i40e_pf_host_process_cmd_config_irq_map(vf, msg, msglen, b_op);
>+		ret = i40e_pf_host_process_cmd_config_irq_map(vf, msg,
>+				msglen, b_op);
> 		break;
> 	case VIRTCHNL_OP_ENABLE_QUEUES:
> 		PMD_DRV_LOG(INFO, "OP_ENABLE_QUEUES received");
> 		if (b_op) {
>-			i40e_pf_host_process_cmd_enable_queues(vf, msg, msglen);
>+			ret = i40e_pf_host_process_cmd_enable_queues(vf,
>+					msg, msglen);
> 			i40e_notify_vf_link_status(dev, vf);
> 		} else {
>-			i40e_pf_host_send_msg_to_vf(
>-				vf, VIRTCHNL_OP_ENABLE_QUEUES,
>-				I40E_NOT_SUPPORTED, NULL, 0);
>+			i40e_pf_host_send_msg_to_vf(vf,
>+					VIRTCHNL_OP_ENABLE_QUEUES,
>+					I40E_NOT_SUPPORTED, NULL, 0);
>+			ret = I40E_NOT_SUPPORTED;
> 		}
> 		break;
> 	case VIRTCHNL_OP_DISABLE_QUEUES:
> 		PMD_DRV_LOG(INFO, "OP_DISABLE_QUEUE received");
>-		i40e_pf_host_process_cmd_disable_queues(vf, msg, msglen, b_op);
>+		ret = i40e_pf_host_process_cmd_disable_queues(vf,
>+				msg, msglen, b_op);
> 		break;
> 	case VIRTCHNL_OP_ADD_ETH_ADDR:
> 		PMD_DRV_LOG(INFO, "OP_ADD_ETHER_ADDRESS received");
>-		i40e_pf_host_process_cmd_add_ether_address(vf, msg,
>+		ret = i40e_pf_host_process_cmd_add_ether_address(vf, msg,
> 							   msglen, b_op);
> 		break;
> 	case VIRTCHNL_OP_DEL_ETH_ADDR:
> 		PMD_DRV_LOG(INFO, "OP_DEL_ETHER_ADDRESS received");
>-		i40e_pf_host_process_cmd_del_ether_address(vf, msg,
>+		ret = i40e_pf_host_process_cmd_del_ether_address(vf, msg,
> 							   msglen, b_op);
> 		break;
> 	case VIRTCHNL_OP_ADD_VLAN:
> 		PMD_DRV_LOG(INFO, "OP_ADD_VLAN received");
>-		i40e_pf_host_process_cmd_add_vlan(vf, msg, msglen, b_op);
>+		ret = i40e_pf_host_process_cmd_add_vlan(vf, msg, msglen, b_op);
> 		break;
> 	case VIRTCHNL_OP_DEL_VLAN:
> 		PMD_DRV_LOG(INFO, "OP_DEL_VLAN received");
>-		i40e_pf_host_process_cmd_del_vlan(vf, msg, msglen, b_op);
>+		ret = i40e_pf_host_process_cmd_del_vlan(vf, msg, msglen, b_op);
> 		break;
> 	case VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE:
> 		PMD_DRV_LOG(INFO, "OP_CONFIG_PROMISCUOUS_MODE received");
>-		i40e_pf_host_process_cmd_config_promisc_mode(vf, msg,
>+		ret = i40e_pf_host_process_cmd_config_promisc_mode(vf, msg,
> 							     msglen, b_op);
> 		break;
> 	case VIRTCHNL_OP_GET_STATS:
> 		PMD_DRV_LOG(INFO, "OP_GET_STATS received");
>-		i40e_pf_host_process_cmd_get_stats(vf, b_op);
>+		ret = i40e_pf_host_process_cmd_get_stats(vf, b_op);
> 		break;
> 	case VIRTCHNL_OP_ENABLE_VLAN_STRIPPING:
> 		PMD_DRV_LOG(INFO, "OP_ENABLE_VLAN_STRIPPING received");
>-		i40e_pf_host_process_cmd_enable_vlan_strip(vf, b_op);
>+		ret = i40e_pf_host_process_cmd_enable_vlan_strip(vf, b_op);
> 		break;
> 	case VIRTCHNL_OP_DISABLE_VLAN_STRIPPING:
> 		PMD_DRV_LOG(INFO, "OP_DISABLE_VLAN_STRIPPING received");
>-		i40e_pf_host_process_cmd_disable_vlan_strip(vf, b_op);
>+		ret = i40e_pf_host_process_cmd_disable_vlan_strip(vf, b_op);
> 		break;
> 	case VIRTCHNL_OP_CONFIG_RSS_LUT:
> 		PMD_DRV_LOG(INFO, "OP_CONFIG_RSS_LUT received");
>-		i40e_pf_host_process_cmd_set_rss_lut(vf, msg, msglen, b_op);
>+		ret = i40e_pf_host_process_cmd_set_rss_lut(vf, msg,
>+				msglen, b_op);
> 		break;
> 	case VIRTCHNL_OP_CONFIG_RSS_KEY:
> 		PMD_DRV_LOG(INFO, "OP_CONFIG_RSS_KEY received");
>-		i40e_pf_host_process_cmd_set_rss_key(vf, msg, msglen, b_op);
>+		ret = i40e_pf_host_process_cmd_set_rss_key(vf, msg,
>+				msglen, b_op);
> 		break;
> 	case VIRTCHNL_OP_REQUEST_QUEUES:
> 		PMD_DRV_LOG(INFO, "OP_REQUEST_QUEUES received");
>-		i40e_pf_host_process_cmd_request_queues(vf, msg);
>+		ret = i40e_pf_host_process_cmd_request_queues(vf, msg);
> 		break;
> 
> 	/* Don't add command supported below, which will
>@@ -1452,10 +1473,86 @@
> 	 */
> 	default:
> 		PMD_DRV_LOG(ERR, "%u received, not supported", opcode);
>-		i40e_pf_host_send_msg_to_vf(vf, opcode, I40E_ERR_PARAM,
>+		i40e_pf_host_send_msg_to_vf(vf, opcode, I40E_NOT_SUPPORTED,
> 								NULL, 0);
>+		ret = I40E_NOT_SUPPORTED;
> 		break;
> 	}
>+
>+err_cmd:
>+	/* If the amount of invalid or unsupported message been received
>+	 * from a VF exceed maximal limitation, PF will start a timer.
>+	 * Before the timer timed out, PF should ignore any message from
>+	 * that VF.
>+	 * Once the timer timed out, PF will accept a message from the VF,
>+	 * if this message is still invalid or unsupported, PF will
>+	 * restart the timer and enter another loop.
>+	 * If the last commond execute succeed and within 'silence_seconds'
>+	 * period of time, no invalid or unsupported message been
>+	 * received from same VF, the counter of 'invalid/unsupported'
>+	 * message for that VF will be reset to zero.
>+	 */
>+	vf->silence_end_cycle = 0;
>+	switch (ret) {
>+	case I40E_SUCCESS:
>+		if ((vf->invalid_cmd_cnt || vf->unsupported_cmd_cnt) &&
>+				rte_get_timer_cycles() >=
>+				vf->last_wrong_msg_cycle +
>+				pf->wrong_vf_msg_conf.silence_seconds *
>+				rte_get_timer_hz()) {
>+			PMD_DRV_LOG(WARNING, "VF %u message, ignored %u, "
>+					"invalid %u, unsupported %u", vf_id,
>+					vf->ignored_cmd_cnt, vf->invalid_total,
>+					vf->unsupported_total);
>+			vf->unsupported_cmd_cnt = 0;
>+			vf->invalid_cmd_cnt = 0;
>+		}
>+		break;
>+
>+	case I40E_ERR_PARAM:
>+	case I40E_ERR_NO_AVAILABLE_VSI:
>+		vf->invalid_total++;
>+		if (!pf->wrong_vf_msg_conf.max_invalid)
>+			break;
>+		vf->invalid_cmd_cnt++;
>+		if (vf->invalid_cmd_cnt >
>+				pf->wrong_vf_msg_conf.max_invalid) {
>+			PMD_DRV_LOG(ERR, "VF %u too much continuous invalid"
>+					" message(%u, maximum %u, total %u)!",
>+					vf_id, vf->invalid_cmd_cnt,
>+					pf->wrong_vf_msg_conf.max_invalid,
>+					vf->invalid_total);
>+			vf->silence_end_cycle = rte_get_timer_cycles() +
>+					pf->wrong_vf_msg_conf.silence_seconds
>+					* rte_get_timer_hz();
>+		}
>+
>+		vf->last_wrong_msg_cycle = rte_get_timer_cycles();
>+		break;
>+
>+	case I40E_NOT_SUPPORTED:
>+		vf->unsupported_total++;
>+		if (!pf->wrong_vf_msg_conf.max_unsupported)
>+			break;
>+		vf->unsupported_cmd_cnt++;
>+		if (vf->unsupported_cmd_cnt >
>+				pf->wrong_vf_msg_conf.max_unsupported) {
>+			PMD_DRV_LOG(ERR, "VF %u too much continuous unsupported"
>+					" message(%u, maximum %u, total %u)!",
>+					vf_id, vf->unsupported_cmd_cnt,
>+					pf->wrong_vf_msg_conf.max_unsupported,
>+					vf->unsupported_total);
>+			vf->silence_end_cycle = rte_get_timer_cycles() +
>+					pf->wrong_vf_msg_conf.silence_seconds
>+					* rte_get_timer_hz();
>+		}
>+
>+		vf->last_wrong_msg_cycle = rte_get_timer_cycles();
>+		break;
>+	default:
>+		break;
>+	}
>+	return;
> }
> 
> int
>@@ -1493,6 +1590,12 @@
> 		pf->vfs[i].pf = pf;
> 		pf->vfs[i].state = I40E_VF_INACTIVE;
> 		pf->vfs[i].vf_idx = i;
>+
>+		pf->vfs[i].invalid_cmd_cnt = 0;
>+		pf->vfs[i].unsupported_cmd_cnt = 0;
>+		pf->vfs[i].silence_end_cycle = 0;
>+		pf->vfs[i].last_wrong_msg_cycle = 0;
>+
> 		ret = i40e_pf_host_vf_reset(&pf->vfs[i], 0);
> 		if (ret != I40E_SUCCESS)
> 			goto fail;
>-- 
>1.8.3.1
>

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [dpdk-dev] [PATCH v4] net/i40e: validate all messages from VF
  2019-09-09 10:01   ` [dpdk-dev] [PATCH v3] net/i40e: validate all " alvinx.zhang
@ 2019-09-09 15:11     ` alvinx.zhang
  2019-09-09 13:34       ` Ye Xiaolong
  2019-09-10 17:12       ` alvinx.zhang
  0 siblings, 2 replies; 14+ messages in thread
From: alvinx.zhang @ 2019-09-09 15:11 UTC (permalink / raw)
  To: qi.z.zhang, beilei.xing; +Cc: dev, Alvin Zhang

From: Alvin Zhang <alvinx.zhang@intel.com>

If VF driver in VM continuous sending invalid messages by mailbox,
it will waste CPU cycles on PF driver and impact other VF drivers
configuration. I40E PF PMD can count the numbers of invalid and
unsupported messages from VFs, when the counter of statistics from
a VF exceed maximum limitation, PF driver will ignore all of
messages from that VF for some seconds.

Signed-off-by: Alvin Zhang <alvinx.zhang@intel.com>
---
 doc/guides/nics/i40e.rst       |  12 +++
 drivers/net/i40e/i40e_ethdev.c |  82 ++++++++++++++++++
 drivers/net/i40e/i40e_ethdev.h |  32 +++++++
 drivers/net/i40e/i40e_pf.c     | 185 ++++++++++++++++++++++++++++++++---------
 4 files changed, 270 insertions(+), 41 deletions(-)

diff --git a/doc/guides/nics/i40e.rst b/doc/guides/nics/i40e.rst
index 0884e15..935706d 100644
--- a/doc/guides/nics/i40e.rst
+++ b/doc/guides/nics/i40e.rst
@@ -185,6 +185,18 @@ Runtime Config Options
 
   -w 84:00.0,use-latest-supported-vec=1
 
+- ``Enable validation for VF message`` (default ``not enabled``)
+
+  The i40e PF PMD supports message validation which from VFs. This will count the
+  numbers of continuous invalid and unsupported messages from VFs. If the counter of
+  statistics from a VF exceed maximum limitation, PF driver will ignore all of messages
+  from that VF for some seconds. Using the ``devargs`` option ``vf_max_wrong_msg``,
+  user can specify how many continuous invalid and unsupported message that PF driver
+  could tolerate and how many seconds during which PF driver will ignore all of
+  messages from a VF, for example::
+
+  -w 84:00.0,vf_max_wrong_msg=4:6:30
+
 Vector RX Pre-conditions
 ~~~~~~~~~~~~~~~~~~~~~~~~
 For Vector RX it is assumed that the number of descriptor rings will be a power
diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index 4e40b7a..3d09fc2 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -44,6 +44,7 @@
 #define ETH_I40E_SUPPORT_MULTI_DRIVER	"support-multi-driver"
 #define ETH_I40E_QUEUE_NUM_PER_VF_ARG	"queue-num-per-vf"
 #define ETH_I40E_USE_LATEST_VEC	"use-latest-supported-vec"
+#define ETH_I40E_MAX_VF_WRONG_MSG	"vf_max_wrong_msg"
 
 #define I40E_CLEAR_PXE_WAIT_MS     200
 
@@ -406,6 +407,7 @@ static int i40e_sw_tunnel_filter_insert(struct i40e_pf *pf,
 	ETH_I40E_SUPPORT_MULTI_DRIVER,
 	ETH_I40E_QUEUE_NUM_PER_VF_ARG,
 	ETH_I40E_USE_LATEST_VEC,
+	ETH_I40E_MAX_VF_WRONG_MSG,
 	NULL};
 
 static const struct rte_pci_id pci_id_i40e_map[] = {
@@ -1256,6 +1258,84 @@ static inline void i40e_config_automask(struct i40e_pf *pf)
 	return 0;
 }
 
+static int
+read_vf_msg_check_info(__rte_unused const char *key,
+			       const char *value,
+			       void *opaque)
+{
+	struct i40e_wrong_vf_msg info;
+
+	memset(&info, 0, sizeof(info));
+	/*
+	 * VF message checking function need 3 parameters, max_invalid,
+	 * max_unsupported and silence_seconds.
+	 * When continuous invalid or unsupported message statistics
+	 * from a VF exceed the limitation of 'max_invalid' or
+	 * 'max_unsupported', PF will ignore any message from that VF for
+	 * 'silence_seconds' seconds.
+	 */
+	if (sscanf(value, "%u:%u:%u", &info.max_invalid,
+			&info.max_unsupported, &info.silence_seconds)
+			!= 3) {
+		PMD_DRV_LOG(ERR, "vf_max_wrong_msg error! format like: "
+				"vf_max_wrong_msg=4:6:60");
+		return -EINVAL;
+	}
+
+	/*
+	 * If invalid or unsupported message checking function is enabled
+	 * by setting max_invalid or max_unsupported variable to not zero,
+	 * 'slience_seconds' must be greater than zero.
+	 */
+	if ((info.max_invalid || info.max_unsupported) &&
+			!info.silence_seconds) {
+		PMD_DRV_LOG(ERR, "vf_max_wrong_msg error! last integer"
+				" must be larger than zero");
+		return -EINVAL;
+	}
+
+	memcpy(opaque, &info, sizeof(struct i40e_wrong_vf_msg));
+	return 0;
+}
+
+static int
+i40e_parse_vf_msg_check_info(struct rte_eth_dev *dev,
+		struct i40e_wrong_vf_msg *wrong_info)
+{
+	int ret = 0;
+	int kvargs_count;
+	struct rte_kvargs *kvlist;
+
+	/* reset all to zero */
+	memset(wrong_info, 0, sizeof(*wrong_info));
+
+	if (!dev->device->devargs)
+		return ret;
+
+	kvlist = rte_kvargs_parse(dev->device->devargs->args, valid_keys);
+	if (!kvlist)
+		return -EINVAL;
+
+	kvargs_count = rte_kvargs_count(kvlist, ETH_I40E_MAX_VF_WRONG_MSG);
+	if (!kvargs_count)
+		goto free_end;
+
+	if (kvargs_count > 1) {
+		PMD_DRV_LOG(ERR, "More than one argument \"%s\"!",
+				ETH_I40E_MAX_VF_WRONG_MSG);
+		ret = -EINVAL;
+		goto free_end;
+	}
+
+	if (rte_kvargs_process(kvlist, ETH_I40E_MAX_VF_WRONG_MSG,
+			read_vf_msg_check_info, wrong_info) < 0)
+		ret = -EINVAL;
+
+free_end:
+	rte_kvargs_free(kvlist);
+	return ret;
+}
+
 #define I40E_ALARM_INTERVAL 50000 /* us */
 
 static int
@@ -1328,6 +1408,8 @@ static inline void i40e_config_automask(struct i40e_pf *pf)
 		return -EIO;
 	}
 
+	/* read VF message checking function parameters */
+	i40e_parse_vf_msg_check_info(dev, &pf->wrong_vf_msg_conf);
 	/* Check if need to support multi-driver */
 	i40e_support_multi_driver(dev);
 	/* Check if users want the latest supported vec path */
diff --git a/drivers/net/i40e/i40e_ethdev.h b/drivers/net/i40e/i40e_ethdev.h
index 38ac3ea..24e10c5 100644
--- a/drivers/net/i40e/i40e_ethdev.h
+++ b/drivers/net/i40e/i40e_ethdev.h
@@ -426,6 +426,25 @@ struct i40e_pf_vf {
 	/* version of the virtchnl from VF */
 	struct virtchnl_version_info version;
 	uint32_t request_caps; /* offload caps requested from VF */
+
+	/*
+	 * Counter of message from VF
+	 * invalid_cmd_cnt, invalid command since last valid command
+	 * unsupported_cmd_cnt, unsupported command since last valid command
+	 * invalid_total, total invalid command
+	 * unsupported_total, total unsupported command
+	 * ignored_cmd_cnt, ignored command in silence
+	 */
+	uint16_t invalid_cmd_cnt;
+	uint16_t unsupported_cmd_cnt;
+	uint32_t invalid_total;
+	uint32_t unsupported_total;
+	uint32_t ignored_cmd_cnt;
+
+	/* cycle of stop ignoring VF message */
+	uint64_t silence_end_cycle;
+	/* cycle of receive last invalid or unsupported message from VF*/
+	uint64_t last_wrong_msg_cycle;
 };
 
 /*
@@ -900,6 +919,17 @@ struct i40e_rte_flow_rss_conf {
 	uint16_t queue[I40E_MAX_Q_PER_TC]; /**< Queues indices to use. */
 };
 
+struct i40e_wrong_vf_msg {
+	uint32_t max_invalid; /* maximal continuous invalid message from VF */
+	/* maximal continuous unsupported message from VF */
+	uint32_t max_unsupported;
+	/*
+	 * silence seconds when VF send much more invalid or unsupported
+	 * message
+	 */
+	uint32_t silence_seconds;
+};
+
 /*
  * Structure to store private data specific for PF instance.
  */
@@ -975,6 +1005,8 @@ struct i40e_pf {
 	struct i40e_customized_pctype customized_pctype[I40E_CUSTOMIZED_MAX];
 	/* Switch Domain Id */
 	uint16_t switch_domain_id;
+
+	struct i40e_wrong_vf_msg wrong_vf_msg_conf;
 };
 
 enum pending_msg {
diff --git a/drivers/net/i40e/i40e_pf.c b/drivers/net/i40e/i40e_pf.c
index b28d02e..8835f9c 100644
--- a/drivers/net/i40e/i40e_pf.c
+++ b/drivers/net/i40e/i40e_pf.c
@@ -297,7 +297,7 @@
 		i40e_pf_host_send_msg_to_vf(vf,
 					    VIRTCHNL_OP_GET_VF_RESOURCES,
 					    I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	/* only have 1 VSI by default */
@@ -488,7 +488,7 @@
 		i40e_pf_host_send_msg_to_vf(vf,
 					    VIRTCHNL_OP_CONFIG_VSI_QUEUES,
 					    I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	if (!msg || vc_vqci->num_queue_pairs > vsi->nb_qps ||
@@ -655,7 +655,7 @@
 			vf,
 			VIRTCHNL_OP_CONFIG_IRQ_MAP,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	if (msg == NULL || msglen < sizeof(struct virtchnl_irq_map_info)) {
@@ -795,7 +795,7 @@
 			vf,
 			VIRTCHNL_OP_DISABLE_QUEUES,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	if (msg == NULL || msglen != sizeof(*q_sel)) {
@@ -830,7 +830,7 @@
 			vf,
 			VIRTCHNL_OP_ADD_ETH_ADDR,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	memset(&filter, 0 , sizeof(struct i40e_mac_filter_info));
@@ -876,7 +876,7 @@
 			vf,
 			VIRTCHNL_OP_DEL_ETH_ADDR,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	if (msg == NULL || msglen <= sizeof(*addr_list)) {
@@ -917,7 +917,7 @@
 			vf,
 			VIRTCHNL_OP_ADD_VLAN,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	if (msg == NULL || msglen <= sizeof(*vlan_filter_list)) {
@@ -958,7 +958,7 @@
 			vf,
 			VIRTCHNL_OP_DEL_VLAN,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	if (msg == NULL || msglen <= sizeof(*vlan_filter_list)) {
@@ -999,7 +999,7 @@
 			vf,
 			VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	if (msg == NULL || msglen != sizeof(*promisc)) {
@@ -1031,16 +1031,18 @@
 {
 	i40e_update_vsi_stats(vf->vsi);
 
-	if (b_op)
+	if (b_op) {
 		i40e_pf_host_send_msg_to_vf(vf, VIRTCHNL_OP_GET_STATS,
 					    I40E_SUCCESS,
 					    (uint8_t *)&vf->vsi->eth_stats,
 					    sizeof(vf->vsi->eth_stats));
-	else
+	} else {
 		i40e_pf_host_send_msg_to_vf(vf, VIRTCHNL_OP_GET_STATS,
 					    I40E_NOT_SUPPORTED,
 					    (uint8_t *)&vf->vsi->eth_stats,
 					    sizeof(vf->vsi->eth_stats));
+		return I40E_NOT_SUPPORTED;
+	}
 
 	return I40E_SUCCESS;
 }
@@ -1055,7 +1057,7 @@
 			vf,
 			VIRTCHNL_OP_ENABLE_VLAN_STRIPPING,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	ret = i40e_vsi_config_vlan_stripping(vf->vsi, TRUE);
@@ -1078,7 +1080,7 @@
 			vf,
 			VIRTCHNL_OP_DISABLE_VLAN_STRIPPING,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	ret = i40e_vsi_config_vlan_stripping(vf->vsi, FALSE);
@@ -1106,7 +1108,7 @@
 			vf,
 			VIRTCHNL_OP_CONFIG_RSS_LUT,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	if (!msg || msglen <= sizeof(struct virtchnl_rss_lut)) {
@@ -1247,6 +1249,7 @@
 	struct i40e_pf *pf;
 	uint32_t req_pairs = vfres->num_queue_pairs;
 	uint32_t cur_pairs = vf->vsi->nb_used_qps;
+	int ret = I40E_SUCCESS;
 
 	pf = vf->pf;
 
@@ -1256,12 +1259,14 @@
 	if (req_pairs == 0) {
 		PMD_DRV_LOG(ERR, "VF %d tried to request 0 queues. Ignoring.\n",
 			    vf->vf_idx);
+		ret = I40E_ERR_PARAM;
 	} else if (req_pairs > I40E_MAX_QP_NUM_PER_VF) {
 		PMD_DRV_LOG(ERR,
 			    "VF %d tried to request more than %d queues.\n",
 			    vf->vf_idx,
 			    I40E_MAX_QP_NUM_PER_VF);
 		vfres->num_queue_pairs = I40E_MAX_QP_NUM_PER_VF;
+		ret = I40E_ERR_PARAM;
 	} else if (req_pairs > cur_pairs + pf->qp_pool.num_free) {
 		PMD_DRV_LOG(ERR, "VF %d requested %d queues (rounded to %d) "
 			"but only %d available\n",
@@ -1277,11 +1282,12 @@
 		pf->vf_nb_qps = req_pairs;
 		i40e_pf_host_process_cmd_reset_vf(vf);
 
-		return 0;
+		return I40E_SUCCESS;
 	}
 
-	return i40e_pf_host_send_msg_to_vf(vf, VIRTCHNL_OP_REQUEST_QUEUES, 0,
+	i40e_pf_host_send_msg_to_vf(vf, VIRTCHNL_OP_REQUEST_QUEUES, 0,
 				(u8 *)vfres, sizeof(*vfres));
+	return ret;
 }
 
 void
@@ -1306,11 +1312,19 @@
 	}
 
 	vf = &pf->vfs[vf_id];
+
+	/* if timer not end, ignore the message and return */
+	if (rte_get_timer_cycles() < vf->silence_end_cycle) {
+		vf->ignored_cmd_cnt++;
+		return;
+	}
+
 	if (!vf->vsi) {
 		PMD_DRV_LOG(ERR, "NO VSI associated with VF found");
 		i40e_pf_host_send_msg_to_vf(vf, opcode,
 			I40E_ERR_NO_AVAILABLE_VSI, NULL, 0);
-		return;
+		ret = I40E_ERR_NO_AVAILABLE_VSI;
+		goto err_cmd;
 	}
 
 	/* perform basic checks on the msg */
@@ -1331,14 +1345,15 @@
 
 	if (ret) {
 		PMD_DRV_LOG(ERR, "Invalid message from VF %u, opcode %u, len %u",
-			    vf_id, opcode, msglen);
+				vf_id, opcode, msglen);
 		i40e_pf_host_send_msg_to_vf(vf, opcode,
-					    I40E_ERR_PARAM, NULL, 0);
-		return;
+				I40E_ERR_PARAM, NULL, 0);
+		ret = I40E_ERR_PARAM;
+		goto err_cmd;
 	}
 
 	/**
-	 * initialise structure to send to user application
+	 * initialize structure to send to user application
 	 * will return response from user in retval field
 	 */
 	ret_param.retval = RTE_PMD_I40E_MB_EVENT_PROCEED;
@@ -1373,78 +1388,84 @@
 		break;
 	case VIRTCHNL_OP_GET_VF_RESOURCES:
 		PMD_DRV_LOG(INFO, "OP_GET_VF_RESOURCES received");
-		i40e_pf_host_process_cmd_get_vf_resource(vf, msg, b_op);
+		ret = i40e_pf_host_process_cmd_get_vf_resource(vf, msg, b_op);
 		break;
 	case VIRTCHNL_OP_CONFIG_VSI_QUEUES:
 		PMD_DRV_LOG(INFO, "OP_CONFIG_VSI_QUEUES received");
-		i40e_pf_host_process_cmd_config_vsi_queues(vf, msg,
+		ret = i40e_pf_host_process_cmd_config_vsi_queues(vf, msg,
 							   msglen, b_op);
 		break;
 	case VIRTCHNL_OP_CONFIG_IRQ_MAP:
 		PMD_DRV_LOG(INFO, "OP_CONFIG_IRQ_MAP received");
-		i40e_pf_host_process_cmd_config_irq_map(vf, msg, msglen, b_op);
+		ret = i40e_pf_host_process_cmd_config_irq_map(vf, msg,
+				msglen, b_op);
 		break;
 	case VIRTCHNL_OP_ENABLE_QUEUES:
 		PMD_DRV_LOG(INFO, "OP_ENABLE_QUEUES received");
 		if (b_op) {
-			i40e_pf_host_process_cmd_enable_queues(vf, msg, msglen);
+			ret = i40e_pf_host_process_cmd_enable_queues(vf,
+					msg, msglen);
 			i40e_notify_vf_link_status(dev, vf);
 		} else {
-			i40e_pf_host_send_msg_to_vf(
-				vf, VIRTCHNL_OP_ENABLE_QUEUES,
-				I40E_NOT_SUPPORTED, NULL, 0);
+			i40e_pf_host_send_msg_to_vf(vf,
+					VIRTCHNL_OP_ENABLE_QUEUES,
+					I40E_NOT_SUPPORTED, NULL, 0);
+			ret = I40E_NOT_SUPPORTED;
 		}
 		break;
 	case VIRTCHNL_OP_DISABLE_QUEUES:
 		PMD_DRV_LOG(INFO, "OP_DISABLE_QUEUE received");
-		i40e_pf_host_process_cmd_disable_queues(vf, msg, msglen, b_op);
+		ret = i40e_pf_host_process_cmd_disable_queues(vf,
+				msg, msglen, b_op);
 		break;
 	case VIRTCHNL_OP_ADD_ETH_ADDR:
 		PMD_DRV_LOG(INFO, "OP_ADD_ETHER_ADDRESS received");
-		i40e_pf_host_process_cmd_add_ether_address(vf, msg,
+		ret = i40e_pf_host_process_cmd_add_ether_address(vf, msg,
 							   msglen, b_op);
 		break;
 	case VIRTCHNL_OP_DEL_ETH_ADDR:
 		PMD_DRV_LOG(INFO, "OP_DEL_ETHER_ADDRESS received");
-		i40e_pf_host_process_cmd_del_ether_address(vf, msg,
+		ret = i40e_pf_host_process_cmd_del_ether_address(vf, msg,
 							   msglen, b_op);
 		break;
 	case VIRTCHNL_OP_ADD_VLAN:
 		PMD_DRV_LOG(INFO, "OP_ADD_VLAN received");
-		i40e_pf_host_process_cmd_add_vlan(vf, msg, msglen, b_op);
+		ret = i40e_pf_host_process_cmd_add_vlan(vf, msg, msglen, b_op);
 		break;
 	case VIRTCHNL_OP_DEL_VLAN:
 		PMD_DRV_LOG(INFO, "OP_DEL_VLAN received");
-		i40e_pf_host_process_cmd_del_vlan(vf, msg, msglen, b_op);
+		ret = i40e_pf_host_process_cmd_del_vlan(vf, msg, msglen, b_op);
 		break;
 	case VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE:
 		PMD_DRV_LOG(INFO, "OP_CONFIG_PROMISCUOUS_MODE received");
-		i40e_pf_host_process_cmd_config_promisc_mode(vf, msg,
+		ret = i40e_pf_host_process_cmd_config_promisc_mode(vf, msg,
 							     msglen, b_op);
 		break;
 	case VIRTCHNL_OP_GET_STATS:
 		PMD_DRV_LOG(INFO, "OP_GET_STATS received");
-		i40e_pf_host_process_cmd_get_stats(vf, b_op);
+		ret = i40e_pf_host_process_cmd_get_stats(vf, b_op);
 		break;
 	case VIRTCHNL_OP_ENABLE_VLAN_STRIPPING:
 		PMD_DRV_LOG(INFO, "OP_ENABLE_VLAN_STRIPPING received");
-		i40e_pf_host_process_cmd_enable_vlan_strip(vf, b_op);
+		ret = i40e_pf_host_process_cmd_enable_vlan_strip(vf, b_op);
 		break;
 	case VIRTCHNL_OP_DISABLE_VLAN_STRIPPING:
 		PMD_DRV_LOG(INFO, "OP_DISABLE_VLAN_STRIPPING received");
-		i40e_pf_host_process_cmd_disable_vlan_strip(vf, b_op);
+		ret = i40e_pf_host_process_cmd_disable_vlan_strip(vf, b_op);
 		break;
 	case VIRTCHNL_OP_CONFIG_RSS_LUT:
 		PMD_DRV_LOG(INFO, "OP_CONFIG_RSS_LUT received");
-		i40e_pf_host_process_cmd_set_rss_lut(vf, msg, msglen, b_op);
+		ret = i40e_pf_host_process_cmd_set_rss_lut(vf, msg,
+				msglen, b_op);
 		break;
 	case VIRTCHNL_OP_CONFIG_RSS_KEY:
 		PMD_DRV_LOG(INFO, "OP_CONFIG_RSS_KEY received");
-		i40e_pf_host_process_cmd_set_rss_key(vf, msg, msglen, b_op);
+		ret = i40e_pf_host_process_cmd_set_rss_key(vf, msg,
+				msglen, b_op);
 		break;
 	case VIRTCHNL_OP_REQUEST_QUEUES:
 		PMD_DRV_LOG(INFO, "OP_REQUEST_QUEUES received");
-		i40e_pf_host_process_cmd_request_queues(vf, msg);
+		ret = i40e_pf_host_process_cmd_request_queues(vf, msg);
 		break;
 
 	/* Don't add command supported below, which will
@@ -1452,10 +1473,86 @@
 	 */
 	default:
 		PMD_DRV_LOG(ERR, "%u received, not supported", opcode);
-		i40e_pf_host_send_msg_to_vf(vf, opcode, I40E_ERR_PARAM,
+		i40e_pf_host_send_msg_to_vf(vf, opcode, I40E_NOT_SUPPORTED,
 								NULL, 0);
+		ret = I40E_NOT_SUPPORTED;
 		break;
 	}
+
+err_cmd:
+	/* If the amount of invalid or unsupported message been received
+	 * from a VF exceed maximal limitation, PF will start a timer.
+	 * Before the timer timed out, PF should ignore any message from
+	 * that VF.
+	 * Once the timer timed out, PF will accept a message from the VF,
+	 * if this message is still invalid or unsupported, PF will
+	 * restart the timer and enter another loop.
+	 * If the last commond execute succeed and within 'silence_seconds'
+	 * period of time, no invalid or unsupported message been
+	 * received from same VF, the counter of 'invalid/unsupported'
+	 * message for that VF will be reset to zero.
+	 */
+	vf->silence_end_cycle = 0;
+	switch (ret) {
+	case I40E_SUCCESS:
+		if ((vf->invalid_cmd_cnt || vf->unsupported_cmd_cnt) &&
+				rte_get_timer_cycles() >=
+				vf->last_wrong_msg_cycle +
+				pf->wrong_vf_msg_conf.silence_seconds *
+				rte_get_timer_hz()) {
+			PMD_DRV_LOG(WARNING, "VF %u message, ignored %u, "
+					"invalid %u, unsupported %u", vf_id,
+					vf->ignored_cmd_cnt, vf->invalid_total,
+					vf->unsupported_total);
+			vf->unsupported_cmd_cnt = 0;
+			vf->invalid_cmd_cnt = 0;
+		}
+		break;
+
+	case I40E_ERR_PARAM:
+	case I40E_ERR_NO_AVAILABLE_VSI:
+		vf->invalid_total++;
+		if (!pf->wrong_vf_msg_conf.max_invalid)
+			break;
+		vf->invalid_cmd_cnt++;
+		if (vf->invalid_cmd_cnt >
+				pf->wrong_vf_msg_conf.max_invalid) {
+			PMD_DRV_LOG(ERR, "VF %u too much continuous invalid"
+					" message(%u, maximum %u, total %u)!",
+					vf_id, vf->invalid_cmd_cnt,
+					pf->wrong_vf_msg_conf.max_invalid,
+					vf->invalid_total);
+			vf->silence_end_cycle = rte_get_timer_cycles() +
+					pf->wrong_vf_msg_conf.silence_seconds
+					* rte_get_timer_hz();
+		}
+
+		vf->last_wrong_msg_cycle = rte_get_timer_cycles();
+		break;
+
+	case I40E_NOT_SUPPORTED:
+		vf->unsupported_total++;
+		if (!pf->wrong_vf_msg_conf.max_unsupported)
+			break;
+		vf->unsupported_cmd_cnt++;
+		if (vf->unsupported_cmd_cnt >
+				pf->wrong_vf_msg_conf.max_unsupported) {
+			PMD_DRV_LOG(ERR, "VF %u too much continuous unsupported"
+					" message(%u, maximum %u, total %u)!",
+					vf_id, vf->unsupported_cmd_cnt,
+					pf->wrong_vf_msg_conf.max_unsupported,
+					vf->unsupported_total);
+			vf->silence_end_cycle = rte_get_timer_cycles() +
+					pf->wrong_vf_msg_conf.silence_seconds
+					* rte_get_timer_hz();
+		}
+
+		vf->last_wrong_msg_cycle = rte_get_timer_cycles();
+		break;
+	default:
+		break;
+	}
+	return;
 }
 
 int
@@ -1493,6 +1590,12 @@
 		pf->vfs[i].pf = pf;
 		pf->vfs[i].state = I40E_VF_INACTIVE;
 		pf->vfs[i].vf_idx = i;
+
+		pf->vfs[i].invalid_cmd_cnt = 0;
+		pf->vfs[i].unsupported_cmd_cnt = 0;
+		pf->vfs[i].silence_end_cycle = 0;
+		pf->vfs[i].last_wrong_msg_cycle = 0;
+
 		ret = i40e_pf_host_vf_reset(&pf->vfs[i], 0);
 		if (ret != I40E_SUCCESS)
 			goto fail;
-- 
1.8.3.1


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [dpdk-dev] [PATCH v4] net/i40e: validate all messages from VF
  2019-09-09 15:11     ` [dpdk-dev] [PATCH v4] " alvinx.zhang
  2019-09-09 13:34       ` Ye Xiaolong
@ 2019-09-10 17:12       ` alvinx.zhang
  2019-09-18 15:39         ` [dpdk-dev] [PATCH v5] " alvinx.zhang
  1 sibling, 1 reply; 14+ messages in thread
From: alvinx.zhang @ 2019-09-10 17:12 UTC (permalink / raw)
  To: qi.z.zhang, beilei.xing; +Cc: dev, Alvin Zhang

From: Alvin Zhang <alvinx.zhang@intel.com>

If VF driver in VM continuous sending invalid messages by mailbox,
it will waste CPU cycles on PF driver and impact other VF drivers
configuration. I40E PF PMD can count the numbers of invalid and
unsupported messages from VFs, when the counter of statistics from
a VF exceed maximum limitation, PF driver will ignore all of
messages from that VF for some seconds.

Signed-off-by: Alvin Zhang <alvinx.zhang@intel.com>
---

v4: change the instruction file "i40e.rst".
v3: resolve the compile issue on ubuntu 32-bit system.
v2: modify codes according to the comments.

 doc/guides/nics/i40e.rst       |  12 +++
 drivers/net/i40e/i40e_ethdev.c |  82 ++++++++++++++++++
 drivers/net/i40e/i40e_ethdev.h |  32 +++++++
 drivers/net/i40e/i40e_pf.c     | 185 ++++++++++++++++++++++++++++++++---------
 4 files changed, 270 insertions(+), 41 deletions(-)

diff --git a/doc/guides/nics/i40e.rst b/doc/guides/nics/i40e.rst
index 0884e15..935706d 100644
--- a/doc/guides/nics/i40e.rst
+++ b/doc/guides/nics/i40e.rst
@@ -185,6 +185,18 @@ Runtime Config Options
 
   -w 84:00.0,use-latest-supported-vec=1
 
+- ``Enable validation for VF message`` (default ``not enabled``)
+
+  The i40e PF PMD supports message validation which from VFs. This will count the
+  numbers of continuous invalid and unsupported messages from VFs. If the counter of
+  statistics from a VF exceed maximum limitation, PF driver will ignore all of messages
+  from that VF for some seconds. Using the ``devargs`` option ``vf_max_wrong_msg``,
+  user can specify how many continuous invalid and unsupported message that PF driver
+  could tolerate and how many seconds during which PF driver will ignore all of
+  messages from a VF, for example::
+
+  -w 84:00.0,vf_max_wrong_msg=4:6:30
+
 Vector RX Pre-conditions
 ~~~~~~~~~~~~~~~~~~~~~~~~
 For Vector RX it is assumed that the number of descriptor rings will be a power
diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index 4e40b7a..3d09fc2 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -44,6 +44,7 @@
 #define ETH_I40E_SUPPORT_MULTI_DRIVER	"support-multi-driver"
 #define ETH_I40E_QUEUE_NUM_PER_VF_ARG	"queue-num-per-vf"
 #define ETH_I40E_USE_LATEST_VEC	"use-latest-supported-vec"
+#define ETH_I40E_MAX_VF_WRONG_MSG	"vf_max_wrong_msg"
 
 #define I40E_CLEAR_PXE_WAIT_MS     200
 
@@ -406,6 +407,7 @@ static int i40e_sw_tunnel_filter_insert(struct i40e_pf *pf,
 	ETH_I40E_SUPPORT_MULTI_DRIVER,
 	ETH_I40E_QUEUE_NUM_PER_VF_ARG,
 	ETH_I40E_USE_LATEST_VEC,
+	ETH_I40E_MAX_VF_WRONG_MSG,
 	NULL};
 
 static const struct rte_pci_id pci_id_i40e_map[] = {
@@ -1256,6 +1258,84 @@ static inline void i40e_config_automask(struct i40e_pf *pf)
 	return 0;
 }
 
+static int
+read_vf_msg_check_info(__rte_unused const char *key,
+			       const char *value,
+			       void *opaque)
+{
+	struct i40e_wrong_vf_msg info;
+
+	memset(&info, 0, sizeof(info));
+	/*
+	 * VF message checking function need 3 parameters, max_invalid,
+	 * max_unsupported and silence_seconds.
+	 * When continuous invalid or unsupported message statistics
+	 * from a VF exceed the limitation of 'max_invalid' or
+	 * 'max_unsupported', PF will ignore any message from that VF for
+	 * 'silence_seconds' seconds.
+	 */
+	if (sscanf(value, "%u:%u:%u", &info.max_invalid,
+			&info.max_unsupported, &info.silence_seconds)
+			!= 3) {
+		PMD_DRV_LOG(ERR, "vf_max_wrong_msg error! format like: "
+				"vf_max_wrong_msg=4:6:60");
+		return -EINVAL;
+	}
+
+	/*
+	 * If invalid or unsupported message checking function is enabled
+	 * by setting max_invalid or max_unsupported variable to not zero,
+	 * 'slience_seconds' must be greater than zero.
+	 */
+	if ((info.max_invalid || info.max_unsupported) &&
+			!info.silence_seconds) {
+		PMD_DRV_LOG(ERR, "vf_max_wrong_msg error! last integer"
+				" must be larger than zero");
+		return -EINVAL;
+	}
+
+	memcpy(opaque, &info, sizeof(struct i40e_wrong_vf_msg));
+	return 0;
+}
+
+static int
+i40e_parse_vf_msg_check_info(struct rte_eth_dev *dev,
+		struct i40e_wrong_vf_msg *wrong_info)
+{
+	int ret = 0;
+	int kvargs_count;
+	struct rte_kvargs *kvlist;
+
+	/* reset all to zero */
+	memset(wrong_info, 0, sizeof(*wrong_info));
+
+	if (!dev->device->devargs)
+		return ret;
+
+	kvlist = rte_kvargs_parse(dev->device->devargs->args, valid_keys);
+	if (!kvlist)
+		return -EINVAL;
+
+	kvargs_count = rte_kvargs_count(kvlist, ETH_I40E_MAX_VF_WRONG_MSG);
+	if (!kvargs_count)
+		goto free_end;
+
+	if (kvargs_count > 1) {
+		PMD_DRV_LOG(ERR, "More than one argument \"%s\"!",
+				ETH_I40E_MAX_VF_WRONG_MSG);
+		ret = -EINVAL;
+		goto free_end;
+	}
+
+	if (rte_kvargs_process(kvlist, ETH_I40E_MAX_VF_WRONG_MSG,
+			read_vf_msg_check_info, wrong_info) < 0)
+		ret = -EINVAL;
+
+free_end:
+	rte_kvargs_free(kvlist);
+	return ret;
+}
+
 #define I40E_ALARM_INTERVAL 50000 /* us */
 
 static int
@@ -1328,6 +1408,8 @@ static inline void i40e_config_automask(struct i40e_pf *pf)
 		return -EIO;
 	}
 
+	/* read VF message checking function parameters */
+	i40e_parse_vf_msg_check_info(dev, &pf->wrong_vf_msg_conf);
 	/* Check if need to support multi-driver */
 	i40e_support_multi_driver(dev);
 	/* Check if users want the latest supported vec path */
diff --git a/drivers/net/i40e/i40e_ethdev.h b/drivers/net/i40e/i40e_ethdev.h
index 38ac3ea..24e10c5 100644
--- a/drivers/net/i40e/i40e_ethdev.h
+++ b/drivers/net/i40e/i40e_ethdev.h
@@ -426,6 +426,25 @@ struct i40e_pf_vf {
 	/* version of the virtchnl from VF */
 	struct virtchnl_version_info version;
 	uint32_t request_caps; /* offload caps requested from VF */
+
+	/*
+	 * Counter of message from VF
+	 * invalid_cmd_cnt, invalid command since last valid command
+	 * unsupported_cmd_cnt, unsupported command since last valid command
+	 * invalid_total, total invalid command
+	 * unsupported_total, total unsupported command
+	 * ignored_cmd_cnt, ignored command in silence
+	 */
+	uint16_t invalid_cmd_cnt;
+	uint16_t unsupported_cmd_cnt;
+	uint32_t invalid_total;
+	uint32_t unsupported_total;
+	uint32_t ignored_cmd_cnt;
+
+	/* cycle of stop ignoring VF message */
+	uint64_t silence_end_cycle;
+	/* cycle of receive last invalid or unsupported message from VF*/
+	uint64_t last_wrong_msg_cycle;
 };
 
 /*
@@ -900,6 +919,17 @@ struct i40e_rte_flow_rss_conf {
 	uint16_t queue[I40E_MAX_Q_PER_TC]; /**< Queues indices to use. */
 };
 
+struct i40e_wrong_vf_msg {
+	uint32_t max_invalid; /* maximal continuous invalid message from VF */
+	/* maximal continuous unsupported message from VF */
+	uint32_t max_unsupported;
+	/*
+	 * silence seconds when VF send much more invalid or unsupported
+	 * message
+	 */
+	uint32_t silence_seconds;
+};
+
 /*
  * Structure to store private data specific for PF instance.
  */
@@ -975,6 +1005,8 @@ struct i40e_pf {
 	struct i40e_customized_pctype customized_pctype[I40E_CUSTOMIZED_MAX];
 	/* Switch Domain Id */
 	uint16_t switch_domain_id;
+
+	struct i40e_wrong_vf_msg wrong_vf_msg_conf;
 };
 
 enum pending_msg {
diff --git a/drivers/net/i40e/i40e_pf.c b/drivers/net/i40e/i40e_pf.c
index b28d02e..8835f9c 100644
--- a/drivers/net/i40e/i40e_pf.c
+++ b/drivers/net/i40e/i40e_pf.c
@@ -297,7 +297,7 @@
 		i40e_pf_host_send_msg_to_vf(vf,
 					    VIRTCHNL_OP_GET_VF_RESOURCES,
 					    I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	/* only have 1 VSI by default */
@@ -488,7 +488,7 @@
 		i40e_pf_host_send_msg_to_vf(vf,
 					    VIRTCHNL_OP_CONFIG_VSI_QUEUES,
 					    I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	if (!msg || vc_vqci->num_queue_pairs > vsi->nb_qps ||
@@ -655,7 +655,7 @@
 			vf,
 			VIRTCHNL_OP_CONFIG_IRQ_MAP,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	if (msg == NULL || msglen < sizeof(struct virtchnl_irq_map_info)) {
@@ -795,7 +795,7 @@
 			vf,
 			VIRTCHNL_OP_DISABLE_QUEUES,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	if (msg == NULL || msglen != sizeof(*q_sel)) {
@@ -830,7 +830,7 @@
 			vf,
 			VIRTCHNL_OP_ADD_ETH_ADDR,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	memset(&filter, 0 , sizeof(struct i40e_mac_filter_info));
@@ -876,7 +876,7 @@
 			vf,
 			VIRTCHNL_OP_DEL_ETH_ADDR,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	if (msg == NULL || msglen <= sizeof(*addr_list)) {
@@ -917,7 +917,7 @@
 			vf,
 			VIRTCHNL_OP_ADD_VLAN,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	if (msg == NULL || msglen <= sizeof(*vlan_filter_list)) {
@@ -958,7 +958,7 @@
 			vf,
 			VIRTCHNL_OP_DEL_VLAN,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	if (msg == NULL || msglen <= sizeof(*vlan_filter_list)) {
@@ -999,7 +999,7 @@
 			vf,
 			VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	if (msg == NULL || msglen != sizeof(*promisc)) {
@@ -1031,16 +1031,18 @@
 {
 	i40e_update_vsi_stats(vf->vsi);
 
-	if (b_op)
+	if (b_op) {
 		i40e_pf_host_send_msg_to_vf(vf, VIRTCHNL_OP_GET_STATS,
 					    I40E_SUCCESS,
 					    (uint8_t *)&vf->vsi->eth_stats,
 					    sizeof(vf->vsi->eth_stats));
-	else
+	} else {
 		i40e_pf_host_send_msg_to_vf(vf, VIRTCHNL_OP_GET_STATS,
 					    I40E_NOT_SUPPORTED,
 					    (uint8_t *)&vf->vsi->eth_stats,
 					    sizeof(vf->vsi->eth_stats));
+		return I40E_NOT_SUPPORTED;
+	}
 
 	return I40E_SUCCESS;
 }
@@ -1055,7 +1057,7 @@
 			vf,
 			VIRTCHNL_OP_ENABLE_VLAN_STRIPPING,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	ret = i40e_vsi_config_vlan_stripping(vf->vsi, TRUE);
@@ -1078,7 +1080,7 @@
 			vf,
 			VIRTCHNL_OP_DISABLE_VLAN_STRIPPING,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	ret = i40e_vsi_config_vlan_stripping(vf->vsi, FALSE);
@@ -1106,7 +1108,7 @@
 			vf,
 			VIRTCHNL_OP_CONFIG_RSS_LUT,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	if (!msg || msglen <= sizeof(struct virtchnl_rss_lut)) {
@@ -1247,6 +1249,7 @@
 	struct i40e_pf *pf;
 	uint32_t req_pairs = vfres->num_queue_pairs;
 	uint32_t cur_pairs = vf->vsi->nb_used_qps;
+	int ret = I40E_SUCCESS;
 
 	pf = vf->pf;
 
@@ -1256,12 +1259,14 @@
 	if (req_pairs == 0) {
 		PMD_DRV_LOG(ERR, "VF %d tried to request 0 queues. Ignoring.\n",
 			    vf->vf_idx);
+		ret = I40E_ERR_PARAM;
 	} else if (req_pairs > I40E_MAX_QP_NUM_PER_VF) {
 		PMD_DRV_LOG(ERR,
 			    "VF %d tried to request more than %d queues.\n",
 			    vf->vf_idx,
 			    I40E_MAX_QP_NUM_PER_VF);
 		vfres->num_queue_pairs = I40E_MAX_QP_NUM_PER_VF;
+		ret = I40E_ERR_PARAM;
 	} else if (req_pairs > cur_pairs + pf->qp_pool.num_free) {
 		PMD_DRV_LOG(ERR, "VF %d requested %d queues (rounded to %d) "
 			"but only %d available\n",
@@ -1277,11 +1282,12 @@
 		pf->vf_nb_qps = req_pairs;
 		i40e_pf_host_process_cmd_reset_vf(vf);
 
-		return 0;
+		return I40E_SUCCESS;
 	}
 
-	return i40e_pf_host_send_msg_to_vf(vf, VIRTCHNL_OP_REQUEST_QUEUES, 0,
+	i40e_pf_host_send_msg_to_vf(vf, VIRTCHNL_OP_REQUEST_QUEUES, 0,
 				(u8 *)vfres, sizeof(*vfres));
+	return ret;
 }
 
 void
@@ -1306,11 +1312,19 @@
 	}
 
 	vf = &pf->vfs[vf_id];
+
+	/* if timer not end, ignore the message and return */
+	if (rte_get_timer_cycles() < vf->silence_end_cycle) {
+		vf->ignored_cmd_cnt++;
+		return;
+	}
+
 	if (!vf->vsi) {
 		PMD_DRV_LOG(ERR, "NO VSI associated with VF found");
 		i40e_pf_host_send_msg_to_vf(vf, opcode,
 			I40E_ERR_NO_AVAILABLE_VSI, NULL, 0);
-		return;
+		ret = I40E_ERR_NO_AVAILABLE_VSI;
+		goto err_cmd;
 	}
 
 	/* perform basic checks on the msg */
@@ -1331,14 +1345,15 @@
 
 	if (ret) {
 		PMD_DRV_LOG(ERR, "Invalid message from VF %u, opcode %u, len %u",
-			    vf_id, opcode, msglen);
+				vf_id, opcode, msglen);
 		i40e_pf_host_send_msg_to_vf(vf, opcode,
-					    I40E_ERR_PARAM, NULL, 0);
-		return;
+				I40E_ERR_PARAM, NULL, 0);
+		ret = I40E_ERR_PARAM;
+		goto err_cmd;
 	}
 
 	/**
-	 * initialise structure to send to user application
+	 * initialize structure to send to user application
 	 * will return response from user in retval field
 	 */
 	ret_param.retval = RTE_PMD_I40E_MB_EVENT_PROCEED;
@@ -1373,78 +1388,84 @@
 		break;
 	case VIRTCHNL_OP_GET_VF_RESOURCES:
 		PMD_DRV_LOG(INFO, "OP_GET_VF_RESOURCES received");
-		i40e_pf_host_process_cmd_get_vf_resource(vf, msg, b_op);
+		ret = i40e_pf_host_process_cmd_get_vf_resource(vf, msg, b_op);
 		break;
 	case VIRTCHNL_OP_CONFIG_VSI_QUEUES:
 		PMD_DRV_LOG(INFO, "OP_CONFIG_VSI_QUEUES received");
-		i40e_pf_host_process_cmd_config_vsi_queues(vf, msg,
+		ret = i40e_pf_host_process_cmd_config_vsi_queues(vf, msg,
 							   msglen, b_op);
 		break;
 	case VIRTCHNL_OP_CONFIG_IRQ_MAP:
 		PMD_DRV_LOG(INFO, "OP_CONFIG_IRQ_MAP received");
-		i40e_pf_host_process_cmd_config_irq_map(vf, msg, msglen, b_op);
+		ret = i40e_pf_host_process_cmd_config_irq_map(vf, msg,
+				msglen, b_op);
 		break;
 	case VIRTCHNL_OP_ENABLE_QUEUES:
 		PMD_DRV_LOG(INFO, "OP_ENABLE_QUEUES received");
 		if (b_op) {
-			i40e_pf_host_process_cmd_enable_queues(vf, msg, msglen);
+			ret = i40e_pf_host_process_cmd_enable_queues(vf,
+					msg, msglen);
 			i40e_notify_vf_link_status(dev, vf);
 		} else {
-			i40e_pf_host_send_msg_to_vf(
-				vf, VIRTCHNL_OP_ENABLE_QUEUES,
-				I40E_NOT_SUPPORTED, NULL, 0);
+			i40e_pf_host_send_msg_to_vf(vf,
+					VIRTCHNL_OP_ENABLE_QUEUES,
+					I40E_NOT_SUPPORTED, NULL, 0);
+			ret = I40E_NOT_SUPPORTED;
 		}
 		break;
 	case VIRTCHNL_OP_DISABLE_QUEUES:
 		PMD_DRV_LOG(INFO, "OP_DISABLE_QUEUE received");
-		i40e_pf_host_process_cmd_disable_queues(vf, msg, msglen, b_op);
+		ret = i40e_pf_host_process_cmd_disable_queues(vf,
+				msg, msglen, b_op);
 		break;
 	case VIRTCHNL_OP_ADD_ETH_ADDR:
 		PMD_DRV_LOG(INFO, "OP_ADD_ETHER_ADDRESS received");
-		i40e_pf_host_process_cmd_add_ether_address(vf, msg,
+		ret = i40e_pf_host_process_cmd_add_ether_address(vf, msg,
 							   msglen, b_op);
 		break;
 	case VIRTCHNL_OP_DEL_ETH_ADDR:
 		PMD_DRV_LOG(INFO, "OP_DEL_ETHER_ADDRESS received");
-		i40e_pf_host_process_cmd_del_ether_address(vf, msg,
+		ret = i40e_pf_host_process_cmd_del_ether_address(vf, msg,
 							   msglen, b_op);
 		break;
 	case VIRTCHNL_OP_ADD_VLAN:
 		PMD_DRV_LOG(INFO, "OP_ADD_VLAN received");
-		i40e_pf_host_process_cmd_add_vlan(vf, msg, msglen, b_op);
+		ret = i40e_pf_host_process_cmd_add_vlan(vf, msg, msglen, b_op);
 		break;
 	case VIRTCHNL_OP_DEL_VLAN:
 		PMD_DRV_LOG(INFO, "OP_DEL_VLAN received");
-		i40e_pf_host_process_cmd_del_vlan(vf, msg, msglen, b_op);
+		ret = i40e_pf_host_process_cmd_del_vlan(vf, msg, msglen, b_op);
 		break;
 	case VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE:
 		PMD_DRV_LOG(INFO, "OP_CONFIG_PROMISCUOUS_MODE received");
-		i40e_pf_host_process_cmd_config_promisc_mode(vf, msg,
+		ret = i40e_pf_host_process_cmd_config_promisc_mode(vf, msg,
 							     msglen, b_op);
 		break;
 	case VIRTCHNL_OP_GET_STATS:
 		PMD_DRV_LOG(INFO, "OP_GET_STATS received");
-		i40e_pf_host_process_cmd_get_stats(vf, b_op);
+		ret = i40e_pf_host_process_cmd_get_stats(vf, b_op);
 		break;
 	case VIRTCHNL_OP_ENABLE_VLAN_STRIPPING:
 		PMD_DRV_LOG(INFO, "OP_ENABLE_VLAN_STRIPPING received");
-		i40e_pf_host_process_cmd_enable_vlan_strip(vf, b_op);
+		ret = i40e_pf_host_process_cmd_enable_vlan_strip(vf, b_op);
 		break;
 	case VIRTCHNL_OP_DISABLE_VLAN_STRIPPING:
 		PMD_DRV_LOG(INFO, "OP_DISABLE_VLAN_STRIPPING received");
-		i40e_pf_host_process_cmd_disable_vlan_strip(vf, b_op);
+		ret = i40e_pf_host_process_cmd_disable_vlan_strip(vf, b_op);
 		break;
 	case VIRTCHNL_OP_CONFIG_RSS_LUT:
 		PMD_DRV_LOG(INFO, "OP_CONFIG_RSS_LUT received");
-		i40e_pf_host_process_cmd_set_rss_lut(vf, msg, msglen, b_op);
+		ret = i40e_pf_host_process_cmd_set_rss_lut(vf, msg,
+				msglen, b_op);
 		break;
 	case VIRTCHNL_OP_CONFIG_RSS_KEY:
 		PMD_DRV_LOG(INFO, "OP_CONFIG_RSS_KEY received");
-		i40e_pf_host_process_cmd_set_rss_key(vf, msg, msglen, b_op);
+		ret = i40e_pf_host_process_cmd_set_rss_key(vf, msg,
+				msglen, b_op);
 		break;
 	case VIRTCHNL_OP_REQUEST_QUEUES:
 		PMD_DRV_LOG(INFO, "OP_REQUEST_QUEUES received");
-		i40e_pf_host_process_cmd_request_queues(vf, msg);
+		ret = i40e_pf_host_process_cmd_request_queues(vf, msg);
 		break;
 
 	/* Don't add command supported below, which will
@@ -1452,10 +1473,86 @@
 	 */
 	default:
 		PMD_DRV_LOG(ERR, "%u received, not supported", opcode);
-		i40e_pf_host_send_msg_to_vf(vf, opcode, I40E_ERR_PARAM,
+		i40e_pf_host_send_msg_to_vf(vf, opcode, I40E_NOT_SUPPORTED,
 								NULL, 0);
+		ret = I40E_NOT_SUPPORTED;
 		break;
 	}
+
+err_cmd:
+	/* If the amount of invalid or unsupported message been received
+	 * from a VF exceed maximal limitation, PF will start a timer.
+	 * Before the timer timed out, PF should ignore any message from
+	 * that VF.
+	 * Once the timer timed out, PF will accept a message from the VF,
+	 * if this message is still invalid or unsupported, PF will
+	 * restart the timer and enter another loop.
+	 * If the last commond execute succeed and within 'silence_seconds'
+	 * period of time, no invalid or unsupported message been
+	 * received from same VF, the counter of 'invalid/unsupported'
+	 * message for that VF will be reset to zero.
+	 */
+	vf->silence_end_cycle = 0;
+	switch (ret) {
+	case I40E_SUCCESS:
+		if ((vf->invalid_cmd_cnt || vf->unsupported_cmd_cnt) &&
+				rte_get_timer_cycles() >=
+				vf->last_wrong_msg_cycle +
+				pf->wrong_vf_msg_conf.silence_seconds *
+				rte_get_timer_hz()) {
+			PMD_DRV_LOG(WARNING, "VF %u message, ignored %u, "
+					"invalid %u, unsupported %u", vf_id,
+					vf->ignored_cmd_cnt, vf->invalid_total,
+					vf->unsupported_total);
+			vf->unsupported_cmd_cnt = 0;
+			vf->invalid_cmd_cnt = 0;
+		}
+		break;
+
+	case I40E_ERR_PARAM:
+	case I40E_ERR_NO_AVAILABLE_VSI:
+		vf->invalid_total++;
+		if (!pf->wrong_vf_msg_conf.max_invalid)
+			break;
+		vf->invalid_cmd_cnt++;
+		if (vf->invalid_cmd_cnt >
+				pf->wrong_vf_msg_conf.max_invalid) {
+			PMD_DRV_LOG(ERR, "VF %u too much continuous invalid"
+					" message(%u, maximum %u, total %u)!",
+					vf_id, vf->invalid_cmd_cnt,
+					pf->wrong_vf_msg_conf.max_invalid,
+					vf->invalid_total);
+			vf->silence_end_cycle = rte_get_timer_cycles() +
+					pf->wrong_vf_msg_conf.silence_seconds
+					* rte_get_timer_hz();
+		}
+
+		vf->last_wrong_msg_cycle = rte_get_timer_cycles();
+		break;
+
+	case I40E_NOT_SUPPORTED:
+		vf->unsupported_total++;
+		if (!pf->wrong_vf_msg_conf.max_unsupported)
+			break;
+		vf->unsupported_cmd_cnt++;
+		if (vf->unsupported_cmd_cnt >
+				pf->wrong_vf_msg_conf.max_unsupported) {
+			PMD_DRV_LOG(ERR, "VF %u too much continuous unsupported"
+					" message(%u, maximum %u, total %u)!",
+					vf_id, vf->unsupported_cmd_cnt,
+					pf->wrong_vf_msg_conf.max_unsupported,
+					vf->unsupported_total);
+			vf->silence_end_cycle = rte_get_timer_cycles() +
+					pf->wrong_vf_msg_conf.silence_seconds
+					* rte_get_timer_hz();
+		}
+
+		vf->last_wrong_msg_cycle = rte_get_timer_cycles();
+		break;
+	default:
+		break;
+	}
+	return;
 }
 
 int
@@ -1493,6 +1590,12 @@
 		pf->vfs[i].pf = pf;
 		pf->vfs[i].state = I40E_VF_INACTIVE;
 		pf->vfs[i].vf_idx = i;
+
+		pf->vfs[i].invalid_cmd_cnt = 0;
+		pf->vfs[i].unsupported_cmd_cnt = 0;
+		pf->vfs[i].silence_end_cycle = 0;
+		pf->vfs[i].last_wrong_msg_cycle = 0;
+
 		ret = i40e_pf_host_vf_reset(&pf->vfs[i], 0);
 		if (ret != I40E_SUCCESS)
 			goto fail;
-- 
1.8.3.1


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [dpdk-dev] [PATCH v5] net/i40e: validate all messages from VF
  2019-09-10 17:12       ` alvinx.zhang
@ 2019-09-18 15:39         ` alvinx.zhang
  2019-09-18 21:03           ` [dpdk-dev] [PATCH v6] net/i40e: limit the number of VF messages alvinx.zhang
  0 siblings, 1 reply; 14+ messages in thread
From: alvinx.zhang @ 2019-09-18 15:39 UTC (permalink / raw)
  To: qi.z.zhang, beilei.xing, xiaolong.ye; +Cc: dev, Alvin Zhang

From: Alvin Zhang <alvinx.zhang@intel.com>

If VF driver in VM continuous sending invalid messages by mailbox,
it will waste CPU cycles on PF driver and impact other VF drivers
configuration.
PF driver supportes validation for all messages from VF. If any VF
sending much more adminQ messages(valid/invalid/unsupported) to PF
in a period of time, this will trigger the PF's message limiting,
then in the next certain amount of seconds the PF will ignore any
new message from that VF.

Signed-off-by: Alvin Zhang <alvinx.zhang@intel.com>
---

v5: add validating the "valid" message, scheme redesigned
v4: change the instruction file "i40e.rst".
v3: resolve the compile issue on ubuntu 32-bit system.
v2: modify codes according to the comments.
---
 doc/guides/nics/i40e.rst       |  11 +++
 drivers/net/i40e/i40e_ethdev.c |  75 +++++++++++++++
 drivers/net/i40e/i40e_ethdev.h |  47 ++++++++++
 drivers/net/i40e/i40e_pf.c     | 201 ++++++++++++++++++++++++++++++++---------
 4 files changed, 293 insertions(+), 41 deletions(-)

diff --git a/doc/guides/nics/i40e.rst b/doc/guides/nics/i40e.rst
index 0884e15..120e1df 100644
--- a/doc/guides/nics/i40e.rst
+++ b/doc/guides/nics/i40e.rst
@@ -185,6 +185,17 @@ Runtime Config Options
 
   -w 84:00.0,use-latest-supported-vec=1
 
+- ``Enable validation for VF message`` (default ``not enabled``)
+
+  The PF counts valid/invalid/unsupported messages separately from each VF. If in any
+  period seconds one of these message statistics from a VF exceeds maximal limitation,
+  the PF will ignore any new message from that VF for some seconds.
+  Format --
+  "maximal-valid:maximal-invalid:maximal-unsupported@period-seconds:block-seconds"
+  For example::
+
+  -w 84:00.0,vf_msg_cfg=80:10:15@120:180
+
 Vector RX Pre-conditions
 ~~~~~~~~~~~~~~~~~~~~~~~~
 For Vector RX it is assumed that the number of descriptor rings will be a power
diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index 4e40b7a..e842a6f 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -44,6 +44,7 @@
 #define ETH_I40E_SUPPORT_MULTI_DRIVER	"support-multi-driver"
 #define ETH_I40E_QUEUE_NUM_PER_VF_ARG	"queue-num-per-vf"
 #define ETH_I40E_USE_LATEST_VEC	"use-latest-supported-vec"
+#define ETH_I40E_VF_MSG_CFG		"vf_msg_cfg"
 
 #define I40E_CLEAR_PXE_WAIT_MS     200
 
@@ -406,6 +407,7 @@ static int i40e_sw_tunnel_filter_insert(struct i40e_pf *pf,
 	ETH_I40E_SUPPORT_MULTI_DRIVER,
 	ETH_I40E_QUEUE_NUM_PER_VF_ARG,
 	ETH_I40E_USE_LATEST_VEC,
+	ETH_I40E_VF_MSG_CFG,
 	NULL};
 
 static const struct rte_pci_id pci_id_i40e_map[] = {
@@ -1256,6 +1258,77 @@ static inline void i40e_config_automask(struct i40e_pf *pf)
 	return 0;
 }
 
+static int
+read_vf_msg_config(__rte_unused const char *key,
+			       const char *value,
+			       void *opaque)
+{
+	struct i40e_vf_msg_cfg cfg;
+
+	memset(&cfg, 0, sizeof(cfg));
+	if (sscanf(value, "%u:%u:%u@%u:%u", &cfg.max_valid,
+			&cfg.max_invalid, &cfg.max_unsupported,
+			&cfg.period, &cfg.ignore_second)
+			!= 5) {
+		PMD_DRV_LOG(ERR, "vf_msg_cfg error! format like: "
+				"vf_msg_cfg=30:8:10@120:180");
+		return -EINVAL;
+	}
+
+	/*
+	 * If the message validation function been enabled(max_valid,
+	 * max_invalid and max_unsupported not all zero), the 'period'
+	 * and 'ignore_second' must greater than 0.
+	 */
+	if ((cfg.max_valid || cfg.max_invalid || cfg.max_unsupported) &&
+			(!cfg.period || !cfg.ignore_second)) {
+		PMD_DRV_LOG(ERR, "vf_msg_cfg error! the fourth and fifth"
+				" numbers must be greater than 0!");
+		return -EINVAL;
+	}
+
+	memcpy(opaque, &cfg, sizeof(cfg));
+	return 0;
+}
+
+static int
+i40e_parse_vf_msg_config(struct rte_eth_dev *dev,
+		struct i40e_vf_msg_cfg *msg_cfg)
+{
+	int ret = 0;
+	int kvargs_count;
+	struct rte_kvargs *kvlist;
+
+	/* reset all to zero */
+	memset(msg_cfg, 0, sizeof(*msg_cfg));
+
+	if (!dev->device->devargs)
+		return ret;
+
+	kvlist = rte_kvargs_parse(dev->device->devargs->args, valid_keys);
+	if (!kvlist)
+		return -EINVAL;
+
+	kvargs_count = rte_kvargs_count(kvlist, ETH_I40E_VF_MSG_CFG);
+	if (!kvargs_count)
+		goto free_end;
+
+	if (kvargs_count > 1) {
+		PMD_DRV_LOG(ERR, "More than one argument \"%s\"!",
+				ETH_I40E_VF_MSG_CFG);
+		ret = -EINVAL;
+		goto free_end;
+	}
+
+	if (rte_kvargs_process(kvlist, ETH_I40E_VF_MSG_CFG,
+			read_vf_msg_config, msg_cfg) < 0)
+		ret = -EINVAL;
+
+free_end:
+	rte_kvargs_free(kvlist);
+	return ret;
+}
+
 #define I40E_ALARM_INTERVAL 50000 /* us */
 
 static int
@@ -1328,6 +1401,8 @@ static inline void i40e_config_automask(struct i40e_pf *pf)
 		return -EIO;
 	}
 
+	/* read VF message configuration */
+	i40e_parse_vf_msg_config(dev, &pf->vf_msg_cfg);
 	/* Check if need to support multi-driver */
 	i40e_support_multi_driver(dev);
 	/* Check if users want the latest supported vec path */
diff --git a/drivers/net/i40e/i40e_ethdev.h b/drivers/net/i40e/i40e_ethdev.h
index 38ac3ea..1ec0b44 100644
--- a/drivers/net/i40e/i40e_ethdev.h
+++ b/drivers/net/i40e/i40e_ethdev.h
@@ -426,6 +426,28 @@ struct i40e_pf_vf {
 	/* version of the virtchnl from VF */
 	struct virtchnl_version_info version;
 	uint32_t request_caps; /* offload caps requested from VF */
+
+	/*
+	 * Variables for store the arrival timestamp of VF messages.
+	 * If the timestamp of latest valid message stored at
+	 * `valid_msg_timestamps[index % max]` then the timestamp of
+	 * earliest valid message stored at
+	 * `valid_msg_time[(index + 1) % max]`.
+	 * When a new valid message come, the timestamp of this message
+	 * will be stored at `msg_timestamps[(index + 1) % max]` and the
+	 * earliest valid message timestamp is at
+	 * `valid_msg_timestamps[(index + 2) % max]` now...
+	 */
+	uint32_t valid_msg_index;
+	uint32_t invalid_msg_index;
+	uint32_t unsupported_msg_index;
+
+	uint64_t *valid_msg_timestamps;
+	uint64_t *invalid_msg_timestamps;
+	uint64_t *unsupported_msg_timestamps;
+
+	/* cycle of stop ignoring VF message */
+	uint64_t ignore_end_cycle;
 };
 
 /*
@@ -900,6 +922,24 @@ struct i40e_rte_flow_rss_conf {
 	uint16_t queue[I40E_MAX_Q_PER_TC]; /**< Queues indices to use. */
 };
 
+struct i40e_vf_msg_cfg {
+	/* maximal VF valid message during a statistic period */
+	uint32_t max_valid;
+	/* maximal VF invalid message during a statistic period */
+	uint32_t max_invalid;
+	/* maximal VF unsupported message during a statistic period */
+	uint32_t max_unsupported;
+
+	/* statistic period, in second */
+	uint32_t period;
+	/*
+	 * If message statistics(valid, invalid or unsupported) from a VF
+	 * exceed the maximal limitation, the PF will ignore any new
+	 * message from that VF for 'ignor_second' time.
+	 */
+	uint32_t ignore_second;
+};
+
 /*
  * Structure to store private data specific for PF instance.
  */
@@ -975,6 +1015,8 @@ struct i40e_pf {
 	struct i40e_customized_pctype customized_pctype[I40E_CUSTOMIZED_MAX];
 	/* Switch Domain Id */
 	uint16_t switch_domain_id;
+
+	struct i40e_vf_msg_cfg vf_msg_cfg;
 };
 
 enum pending_msg {
@@ -1345,6 +1387,11 @@ int i40e_config_rss_filter(struct i40e_pf *pf,
 	return interval / 2;
 }
 
+static inline uint32_t loop_next(uint32_t cur, uint32_t limit)
+{
+	return (cur >= limit) ? 0 : cur + 1;
+}
+
 #define I40E_VALID_FLOW(flow_type) \
 	((flow_type) == RTE_ETH_FLOW_FRAG_IPV4 || \
 	(flow_type) == RTE_ETH_FLOW_NONFRAG_IPV4_TCP || \
diff --git a/drivers/net/i40e/i40e_pf.c b/drivers/net/i40e/i40e_pf.c
index b28d02e..c949a9b 100644
--- a/drivers/net/i40e/i40e_pf.c
+++ b/drivers/net/i40e/i40e_pf.c
@@ -297,7 +297,7 @@
 		i40e_pf_host_send_msg_to_vf(vf,
 					    VIRTCHNL_OP_GET_VF_RESOURCES,
 					    I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	/* only have 1 VSI by default */
@@ -488,7 +488,7 @@
 		i40e_pf_host_send_msg_to_vf(vf,
 					    VIRTCHNL_OP_CONFIG_VSI_QUEUES,
 					    I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	if (!msg || vc_vqci->num_queue_pairs > vsi->nb_qps ||
@@ -655,7 +655,7 @@
 			vf,
 			VIRTCHNL_OP_CONFIG_IRQ_MAP,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	if (msg == NULL || msglen < sizeof(struct virtchnl_irq_map_info)) {
@@ -795,7 +795,7 @@
 			vf,
 			VIRTCHNL_OP_DISABLE_QUEUES,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	if (msg == NULL || msglen != sizeof(*q_sel)) {
@@ -830,7 +830,7 @@
 			vf,
 			VIRTCHNL_OP_ADD_ETH_ADDR,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	memset(&filter, 0 , sizeof(struct i40e_mac_filter_info));
@@ -876,7 +876,7 @@
 			vf,
 			VIRTCHNL_OP_DEL_ETH_ADDR,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	if (msg == NULL || msglen <= sizeof(*addr_list)) {
@@ -917,7 +917,7 @@
 			vf,
 			VIRTCHNL_OP_ADD_VLAN,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	if (msg == NULL || msglen <= sizeof(*vlan_filter_list)) {
@@ -958,7 +958,7 @@
 			vf,
 			VIRTCHNL_OP_DEL_VLAN,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	if (msg == NULL || msglen <= sizeof(*vlan_filter_list)) {
@@ -999,7 +999,7 @@
 			vf,
 			VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	if (msg == NULL || msglen != sizeof(*promisc)) {
@@ -1031,16 +1031,18 @@
 {
 	i40e_update_vsi_stats(vf->vsi);
 
-	if (b_op)
+	if (b_op) {
 		i40e_pf_host_send_msg_to_vf(vf, VIRTCHNL_OP_GET_STATS,
 					    I40E_SUCCESS,
 					    (uint8_t *)&vf->vsi->eth_stats,
 					    sizeof(vf->vsi->eth_stats));
-	else
+	} else {
 		i40e_pf_host_send_msg_to_vf(vf, VIRTCHNL_OP_GET_STATS,
 					    I40E_NOT_SUPPORTED,
 					    (uint8_t *)&vf->vsi->eth_stats,
 					    sizeof(vf->vsi->eth_stats));
+		return I40E_NOT_SUPPORTED;
+	}
 
 	return I40E_SUCCESS;
 }
@@ -1055,7 +1057,7 @@
 			vf,
 			VIRTCHNL_OP_ENABLE_VLAN_STRIPPING,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	ret = i40e_vsi_config_vlan_stripping(vf->vsi, TRUE);
@@ -1078,7 +1080,7 @@
 			vf,
 			VIRTCHNL_OP_DISABLE_VLAN_STRIPPING,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	ret = i40e_vsi_config_vlan_stripping(vf->vsi, FALSE);
@@ -1106,7 +1108,7 @@
 			vf,
 			VIRTCHNL_OP_CONFIG_RSS_LUT,
 			I40E_NOT_SUPPORTED, NULL, 0);
-		return ret;
+		return I40E_NOT_SUPPORTED;
 	}
 
 	if (!msg || msglen <= sizeof(struct virtchnl_rss_lut)) {
@@ -1247,6 +1249,7 @@
 	struct i40e_pf *pf;
 	uint32_t req_pairs = vfres->num_queue_pairs;
 	uint32_t cur_pairs = vf->vsi->nb_used_qps;
+	int ret = I40E_SUCCESS;
 
 	pf = vf->pf;
 
@@ -1256,12 +1259,14 @@
 	if (req_pairs == 0) {
 		PMD_DRV_LOG(ERR, "VF %d tried to request 0 queues. Ignoring.\n",
 			    vf->vf_idx);
+		ret = I40E_ERR_PARAM;
 	} else if (req_pairs > I40E_MAX_QP_NUM_PER_VF) {
 		PMD_DRV_LOG(ERR,
 			    "VF %d tried to request more than %d queues.\n",
 			    vf->vf_idx,
 			    I40E_MAX_QP_NUM_PER_VF);
 		vfres->num_queue_pairs = I40E_MAX_QP_NUM_PER_VF;
+		ret = I40E_ERR_PARAM;
 	} else if (req_pairs > cur_pairs + pf->qp_pool.num_free) {
 		PMD_DRV_LOG(ERR, "VF %d requested %d queues (rounded to %d) "
 			"but only %d available\n",
@@ -1277,11 +1282,12 @@
 		pf->vf_nb_qps = req_pairs;
 		i40e_pf_host_process_cmd_reset_vf(vf);
 
-		return 0;
+		return I40E_SUCCESS;
 	}
 
-	return i40e_pf_host_send_msg_to_vf(vf, VIRTCHNL_OP_REQUEST_QUEUES, 0,
+	i40e_pf_host_send_msg_to_vf(vf, VIRTCHNL_OP_REQUEST_QUEUES, 0,
 				(u8 *)vfres, sizeof(*vfres));
+	return ret;
 }
 
 void
@@ -1299,6 +1305,10 @@
 	struct rte_pmd_i40e_mb_event_param ret_param;
 	bool b_op = TRUE;
 	int ret;
+	uint32_t next, limit;
+	const char *out_str = NULL;
+	uint64_t *msg_timestamps = NULL;
+	uint64_t cur_cycle, first_cycle;
 
 	if (vf_id > pf->vf_num - 1 || !pf->vfs) {
 		PMD_DRV_LOG(ERR, "invalid argument");
@@ -1306,11 +1316,17 @@
 	}
 
 	vf = &pf->vfs[vf_id];
+
+	/* if timer not end, ignore the message and return */
+	if (rte_get_timer_cycles() < vf->ignore_end_cycle)
+		return;
+
 	if (!vf->vsi) {
 		PMD_DRV_LOG(ERR, "NO VSI associated with VF found");
 		i40e_pf_host_send_msg_to_vf(vf, opcode,
 			I40E_ERR_NO_AVAILABLE_VSI, NULL, 0);
-		return;
+		ret = I40E_ERR_NO_AVAILABLE_VSI;
+		goto err_cmd;
 	}
 
 	/* perform basic checks on the msg */
@@ -1331,14 +1347,15 @@
 
 	if (ret) {
 		PMD_DRV_LOG(ERR, "Invalid message from VF %u, opcode %u, len %u",
-			    vf_id, opcode, msglen);
+				vf_id, opcode, msglen);
 		i40e_pf_host_send_msg_to_vf(vf, opcode,
-					    I40E_ERR_PARAM, NULL, 0);
-		return;
+				I40E_ERR_PARAM, NULL, 0);
+		ret = I40E_ERR_PARAM;
+		goto err_cmd;
 	}
 
 	/**
-	 * initialise structure to send to user application
+	 * initialize structure to send to user application
 	 * will return response from user in retval field
 	 */
 	ret_param.retval = RTE_PMD_I40E_MB_EVENT_PROCEED;
@@ -1373,78 +1390,84 @@
 		break;
 	case VIRTCHNL_OP_GET_VF_RESOURCES:
 		PMD_DRV_LOG(INFO, "OP_GET_VF_RESOURCES received");
-		i40e_pf_host_process_cmd_get_vf_resource(vf, msg, b_op);
+		ret = i40e_pf_host_process_cmd_get_vf_resource(vf, msg, b_op);
 		break;
 	case VIRTCHNL_OP_CONFIG_VSI_QUEUES:
 		PMD_DRV_LOG(INFO, "OP_CONFIG_VSI_QUEUES received");
-		i40e_pf_host_process_cmd_config_vsi_queues(vf, msg,
+		ret = i40e_pf_host_process_cmd_config_vsi_queues(vf, msg,
 							   msglen, b_op);
 		break;
 	case VIRTCHNL_OP_CONFIG_IRQ_MAP:
 		PMD_DRV_LOG(INFO, "OP_CONFIG_IRQ_MAP received");
-		i40e_pf_host_process_cmd_config_irq_map(vf, msg, msglen, b_op);
+		ret = i40e_pf_host_process_cmd_config_irq_map(vf, msg,
+				msglen, b_op);
 		break;
 	case VIRTCHNL_OP_ENABLE_QUEUES:
 		PMD_DRV_LOG(INFO, "OP_ENABLE_QUEUES received");
 		if (b_op) {
-			i40e_pf_host_process_cmd_enable_queues(vf, msg, msglen);
+			ret = i40e_pf_host_process_cmd_enable_queues(vf,
+					msg, msglen);
 			i40e_notify_vf_link_status(dev, vf);
 		} else {
-			i40e_pf_host_send_msg_to_vf(
-				vf, VIRTCHNL_OP_ENABLE_QUEUES,
-				I40E_NOT_SUPPORTED, NULL, 0);
+			i40e_pf_host_send_msg_to_vf(vf,
+					VIRTCHNL_OP_ENABLE_QUEUES,
+					I40E_NOT_SUPPORTED, NULL, 0);
+			ret = I40E_NOT_SUPPORTED;
 		}
 		break;
 	case VIRTCHNL_OP_DISABLE_QUEUES:
 		PMD_DRV_LOG(INFO, "OP_DISABLE_QUEUE received");
-		i40e_pf_host_process_cmd_disable_queues(vf, msg, msglen, b_op);
+		ret = i40e_pf_host_process_cmd_disable_queues(vf,
+				msg, msglen, b_op);
 		break;
 	case VIRTCHNL_OP_ADD_ETH_ADDR:
 		PMD_DRV_LOG(INFO, "OP_ADD_ETHER_ADDRESS received");
-		i40e_pf_host_process_cmd_add_ether_address(vf, msg,
+		ret = i40e_pf_host_process_cmd_add_ether_address(vf, msg,
 							   msglen, b_op);
 		break;
 	case VIRTCHNL_OP_DEL_ETH_ADDR:
 		PMD_DRV_LOG(INFO, "OP_DEL_ETHER_ADDRESS received");
-		i40e_pf_host_process_cmd_del_ether_address(vf, msg,
+		ret = i40e_pf_host_process_cmd_del_ether_address(vf, msg,
 							   msglen, b_op);
 		break;
 	case VIRTCHNL_OP_ADD_VLAN:
 		PMD_DRV_LOG(INFO, "OP_ADD_VLAN received");
-		i40e_pf_host_process_cmd_add_vlan(vf, msg, msglen, b_op);
+		ret = i40e_pf_host_process_cmd_add_vlan(vf, msg, msglen, b_op);
 		break;
 	case VIRTCHNL_OP_DEL_VLAN:
 		PMD_DRV_LOG(INFO, "OP_DEL_VLAN received");
-		i40e_pf_host_process_cmd_del_vlan(vf, msg, msglen, b_op);
+		ret = i40e_pf_host_process_cmd_del_vlan(vf, msg, msglen, b_op);
 		break;
 	case VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE:
 		PMD_DRV_LOG(INFO, "OP_CONFIG_PROMISCUOUS_MODE received");
-		i40e_pf_host_process_cmd_config_promisc_mode(vf, msg,
+		ret = i40e_pf_host_process_cmd_config_promisc_mode(vf, msg,
 							     msglen, b_op);
 		break;
 	case VIRTCHNL_OP_GET_STATS:
 		PMD_DRV_LOG(INFO, "OP_GET_STATS received");
-		i40e_pf_host_process_cmd_get_stats(vf, b_op);
+		ret = i40e_pf_host_process_cmd_get_stats(vf, b_op);
 		break;
 	case VIRTCHNL_OP_ENABLE_VLAN_STRIPPING:
 		PMD_DRV_LOG(INFO, "OP_ENABLE_VLAN_STRIPPING received");
-		i40e_pf_host_process_cmd_enable_vlan_strip(vf, b_op);
+		ret = i40e_pf_host_process_cmd_enable_vlan_strip(vf, b_op);
 		break;
 	case VIRTCHNL_OP_DISABLE_VLAN_STRIPPING:
 		PMD_DRV_LOG(INFO, "OP_DISABLE_VLAN_STRIPPING received");
-		i40e_pf_host_process_cmd_disable_vlan_strip(vf, b_op);
+		ret = i40e_pf_host_process_cmd_disable_vlan_strip(vf, b_op);
 		break;
 	case VIRTCHNL_OP_CONFIG_RSS_LUT:
 		PMD_DRV_LOG(INFO, "OP_CONFIG_RSS_LUT received");
-		i40e_pf_host_process_cmd_set_rss_lut(vf, msg, msglen, b_op);
+		ret = i40e_pf_host_process_cmd_set_rss_lut(vf, msg,
+				msglen, b_op);
 		break;
 	case VIRTCHNL_OP_CONFIG_RSS_KEY:
 		PMD_DRV_LOG(INFO, "OP_CONFIG_RSS_KEY received");
-		i40e_pf_host_process_cmd_set_rss_key(vf, msg, msglen, b_op);
+		ret = i40e_pf_host_process_cmd_set_rss_key(vf, msg,
+				msglen, b_op);
 		break;
 	case VIRTCHNL_OP_REQUEST_QUEUES:
 		PMD_DRV_LOG(INFO, "OP_REQUEST_QUEUES received");
-		i40e_pf_host_process_cmd_request_queues(vf, msg);
+		ret = i40e_pf_host_process_cmd_request_queues(vf, msg);
 		break;
 
 	/* Don't add command supported below, which will
@@ -1452,10 +1475,77 @@
 	 */
 	default:
 		PMD_DRV_LOG(ERR, "%u received, not supported", opcode);
-		i40e_pf_host_send_msg_to_vf(vf, opcode, I40E_ERR_PARAM,
+		i40e_pf_host_send_msg_to_vf(vf, opcode, I40E_NOT_SUPPORTED,
 								NULL, 0);
+		ret = I40E_NOT_SUPPORTED;
+		break;
+	}
+
+err_cmd:
+	/*
+	 * Record the arrival time(cycles) of each message,
+	 * if in any `period` the message statistics from a VF exceed the
+	 * maximal limitation, the PF will block that VF message for
+	 * `ignore_second` seconds.
+	 */
+	switch (ret) {
+	case I40E_ERR_PARAM:
+	case I40E_ERR_NO_AVAILABLE_VSI:
+		if (!pf->vf_msg_cfg.max_invalid)
+			return;
+
+		out_str = "invalid";
+		limit = pf->vf_msg_cfg.max_invalid - 1;
+		next = loop_next(vf->invalid_msg_index, limit);
+		vf->invalid_msg_index = next;
+		msg_timestamps = vf->invalid_msg_timestamps;
+		break;
+
+	case I40E_NOT_SUPPORTED:
+		if (!pf->vf_msg_cfg.max_unsupported)
+			return;
+
+		out_str = "unsupported";
+		limit = pf->vf_msg_cfg.max_unsupported - 1;
+		next = loop_next(vf->unsupported_msg_index, limit);
+		vf->unsupported_msg_index = next;
+		msg_timestamps = vf->unsupported_msg_timestamps;
+		break;
+
+	default:
+		if (!pf->vf_msg_cfg.max_valid)
+			return;
+
+		out_str = "valid";
+		limit = pf->vf_msg_cfg.max_valid - 1;
+		next = loop_next(vf->valid_msg_index, limit);
+		vf->valid_msg_index = next;
+		msg_timestamps = vf->valid_msg_timestamps;
 		break;
 	}
+	cur_cycle = rte_get_timer_cycles();
+	msg_timestamps[next] = cur_cycle;
+	next = loop_next(next, limit);
+	first_cycle = msg_timestamps[next];
+
+	/*
+	 * If the time span from the arrival time of first message to
+	 * the arrival time of current message smaller than `period`,
+	 * that mean too much message in this statistic period.
+	 */
+	if (first_cycle && cur_cycle < first_cycle +
+			(uint64_t)pf->vf_msg_cfg.period * rte_get_timer_hz()) {
+		PMD_DRV_LOG(ERR, "VF %u too much %s messages(%u in %u"
+				" seconds),\n\tany new message from which"
+				" will be ignored during next %u seconds!",
+				vf_id, out_str, limit + 1,
+				(uint32_t)((cur_cycle - first_cycle +
+				rte_get_timer_hz() - 1) / rte_get_timer_hz()),
+				pf->vf_msg_cfg.ignore_second);
+		vf->ignore_end_cycle = rte_get_timer_cycles() +
+				pf->vf_msg_cfg.ignore_second *
+				rte_get_timer_hz();
+	}
 }
 
 int
@@ -1465,6 +1555,8 @@
 	struct i40e_hw *hw = I40E_PF_TO_HW(pf);
 	int ret, i;
 	uint32_t val;
+	size_t size;
+	uint64_t *addr;
 
 	PMD_INIT_FUNC_TRACE();
 
@@ -1489,10 +1581,28 @@
 	I40E_WRITE_REG(hw, I40E_PFGEN_PORTMDIO_NUM, val);
 	I40E_WRITE_FLUSH(hw);
 
+	/* calculate the memory size for storing timestamp of messages */
+	size = (pf->vf_msg_cfg.max_valid + pf->vf_msg_cfg.max_invalid +
+			pf->vf_msg_cfg.max_unsupported) * sizeof(uint64_t);
+
 	for (i = 0; i < pf->vf_num; i++) {
 		pf->vfs[i].pf = pf;
 		pf->vfs[i].state = I40E_VF_INACTIVE;
 		pf->vfs[i].vf_idx = i;
+
+		if (size) {
+			/* allocate memory for store timestamp of messages */
+			addr = (uint64_t *)rte_zmalloc("i40e_pf_vf", size, 0);
+			if (addr == NULL)
+				goto fail;
+			pf->vfs[i].valid_msg_timestamps = addr;
+			pf->vfs[i].invalid_msg_timestamps = addr +
+					pf->vf_msg_cfg.max_valid;
+			pf->vfs[i].unsupported_msg_timestamps =
+					pf->vfs[i].invalid_msg_timestamps +
+					pf->vf_msg_cfg.max_invalid;
+		}
+
 		ret = i40e_pf_host_vf_reset(&pf->vfs[i], 0);
 		if (ret != I40E_SUCCESS)
 			goto fail;
@@ -1505,6 +1615,9 @@
 	return I40E_SUCCESS;
 
 fail:
+	for (; i >= 0; i--)
+		if (pf->vfs[i].valid_msg_timestamps)
+			rte_free(pf->vfs[i].valid_msg_timestamps);
 	rte_free(pf->vfs);
 	i40e_pf_enable_irq0(hw);
 
@@ -1517,6 +1630,7 @@
 	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
 	struct i40e_hw *hw = I40E_PF_TO_HW(pf);
 	uint32_t val;
+	int i;
 
 	PMD_INIT_FUNC_TRACE();
 
@@ -1529,6 +1643,11 @@
 		(pf->vf_nb_qps == 0))
 		return I40E_SUCCESS;
 
+	/* free memory for store timestamp of messages */
+	for (i = 0; i < pf->vf_num; i++)
+		if (pf->vfs[i].valid_msg_timestamps)
+			rte_free(pf->vfs[i].valid_msg_timestamps);
+
 	/* free memory to store VF structure */
 	rte_free(pf->vfs);
 	pf->vfs = NULL;
-- 
1.8.3.1


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [dpdk-dev] [PATCH v6] net/i40e: limit the number of VF messages
  2019-09-18 15:39         ` [dpdk-dev] [PATCH v5] " alvinx.zhang
@ 2019-09-18 21:03           ` alvinx.zhang
  2019-09-20 10:22             ` [dpdk-dev] [PATCH v7] " alvinx.zhang
  0 siblings, 1 reply; 14+ messages in thread
From: alvinx.zhang @ 2019-09-18 21:03 UTC (permalink / raw)
  To: qi.z.zhang; +Cc: dev, Alvin Zhang

From: Alvin Zhang <alvinx.zhang@intel.com>

PF driver supportes counting VF adminQ messages. If any VF driver
sending much more adminQ messages to it's PF driver in a period of
time, it will trigger the PF's message limiting, then in the
next certain amount of seconds the PF driver will ignore any new
adminQ message from that VF.

Signed-off-by: Alvin Zhang <alvinx.zhang@intel.com>
---

v6: combine valid/invalid/unsupported messages to one.
v5: add validating the "valid" message, scheme redesigned.
v4: change the instruction file "i40e.rst".
v3: resolve the compile issue on ubuntu 32-bit system.
v2: modify codes according to the comments.
---
 doc/guides/nics/i40e.rst       | 10 ++++++
 drivers/net/i40e/i40e_ethdev.c | 72 ++++++++++++++++++++++++++++++++++++++++++
 drivers/net/i40e/i40e_ethdev.h | 32 +++++++++++++++++++
 drivers/net/i40e/i40e_pf.c     | 71 +++++++++++++++++++++++++++++++++++++++--
 4 files changed, 183 insertions(+), 2 deletions(-)

diff --git a/doc/guides/nics/i40e.rst b/doc/guides/nics/i40e.rst
index 0884e15..38acf59 100644
--- a/doc/guides/nics/i40e.rst
+++ b/doc/guides/nics/i40e.rst
@@ -185,6 +185,16 @@ Runtime Config Options
 
   -w 84:00.0,use-latest-supported-vec=1
 
+- ``Enable validation for VF message`` (default ``not enabled``)
+
+  The PF counts messages from each VF. If in any period of seconds the message
+  statistic from a VF exceeds maximal limitation, the PF will ignore any new message
+  from that VF for some seconds.
+  Format -- "maximal-message@period-seconds:ignore-seconds"
+  For example::
+
+  -w 84:00.0,vf_msg_cfg=80@120:180
+
 Vector RX Pre-conditions
 ~~~~~~~~~~~~~~~~~~~~~~~~
 For Vector RX it is assumed that the number of descriptor rings will be a power
diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index 4e40b7a..d7829b9 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -44,6 +44,7 @@
 #define ETH_I40E_SUPPORT_MULTI_DRIVER	"support-multi-driver"
 #define ETH_I40E_QUEUE_NUM_PER_VF_ARG	"queue-num-per-vf"
 #define ETH_I40E_USE_LATEST_VEC	"use-latest-supported-vec"
+#define ETH_I40E_VF_MSG_CFG		"vf_msg_cfg"
 
 #define I40E_CLEAR_PXE_WAIT_MS     200
 
@@ -406,6 +407,7 @@ static int i40e_sw_tunnel_filter_insert(struct i40e_pf *pf,
 	ETH_I40E_SUPPORT_MULTI_DRIVER,
 	ETH_I40E_QUEUE_NUM_PER_VF_ARG,
 	ETH_I40E_USE_LATEST_VEC,
+	ETH_I40E_VF_MSG_CFG,
 	NULL};
 
 static const struct rte_pci_id pci_id_i40e_map[] = {
@@ -1256,6 +1258,74 @@ static inline void i40e_config_automask(struct i40e_pf *pf)
 	return 0;
 }
 
+static int
+read_vf_msg_config(__rte_unused const char *key,
+			       const char *value,
+			       void *opaque)
+{
+	struct i40e_vf_msg_cfg cfg;
+
+	memset(&cfg, 0, sizeof(cfg));
+	if (sscanf(value, "%u@%u:%u", &cfg.max_msg, &cfg.period,
+			&cfg.ignore_second) != 3) {
+		PMD_DRV_LOG(ERR, "format error! example: "
+				"%s=60@120:180", ETH_I40E_VF_MSG_CFG);
+		return -EINVAL;
+	}
+
+	/*
+	 * If the message validation function been enabled, the 'period'
+	 * and 'ignore_second' must greater than 0.
+	 */
+	if (cfg.max_msg && (!cfg.period || !cfg.ignore_second)) {
+		PMD_DRV_LOG(ERR, "%s error! the second and third"
+				" number must be greater than 0!",
+				ETH_I40E_VF_MSG_CFG);
+		return -EINVAL;
+	}
+
+	memcpy(opaque, &cfg, sizeof(cfg));
+	return 0;
+}
+
+static int
+i40e_parse_vf_msg_config(struct rte_eth_dev *dev,
+		struct i40e_vf_msg_cfg *msg_cfg)
+{
+	int ret = 0;
+	int kvargs_count;
+	struct rte_kvargs *kvlist;
+
+	/* reset all to zero */
+	memset(msg_cfg, 0, sizeof(*msg_cfg));
+
+	if (!dev->device->devargs)
+		return ret;
+
+	kvlist = rte_kvargs_parse(dev->device->devargs->args, valid_keys);
+	if (!kvlist)
+		return -EINVAL;
+
+	kvargs_count = rte_kvargs_count(kvlist, ETH_I40E_VF_MSG_CFG);
+	if (!kvargs_count)
+		goto free_end;
+
+	if (kvargs_count > 1) {
+		PMD_DRV_LOG(ERR, "More than one argument \"%s\"!",
+				ETH_I40E_VF_MSG_CFG);
+		ret = -EINVAL;
+		goto free_end;
+	}
+
+	if (rte_kvargs_process(kvlist, ETH_I40E_VF_MSG_CFG,
+			read_vf_msg_config, msg_cfg) < 0)
+		ret = -EINVAL;
+
+free_end:
+	rte_kvargs_free(kvlist);
+	return ret;
+}
+
 #define I40E_ALARM_INTERVAL 50000 /* us */
 
 static int
@@ -1328,6 +1398,8 @@ static inline void i40e_config_automask(struct i40e_pf *pf)
 		return -EIO;
 	}
 
+	/* read VF message configuration */
+	i40e_parse_vf_msg_config(dev, &pf->vf_msg_cfg);
 	/* Check if need to support multi-driver */
 	i40e_support_multi_driver(dev);
 	/* Check if users want the latest supported vec path */
diff --git a/drivers/net/i40e/i40e_ethdev.h b/drivers/net/i40e/i40e_ethdev.h
index 38ac3ea..261954b 100644
--- a/drivers/net/i40e/i40e_ethdev.h
+++ b/drivers/net/i40e/i40e_ethdev.h
@@ -426,6 +426,22 @@ struct i40e_pf_vf {
 	/* version of the virtchnl from VF */
 	struct virtchnl_version_info version;
 	uint32_t request_caps; /* offload caps requested from VF */
+
+	/*
+	 * Variables for store the arrival timestamp of VF messages.
+	 * If the timestamp of latest message stored at
+	 * `msg_timestamps[index % max]` then the timestamp of
+	 * earliest message stored at `msg_time[(index + 1) % max]`.
+	 * When a new message come, the timestamp of this message
+	 * will be stored at `msg_timestamps[(index + 1) % max]` and the
+	 * earliest message timestamp is at
+	 * `msg_timestamps[(index + 2) % max]` now...
+	 */
+	uint32_t msg_index;
+	uint64_t *msg_timestamps;
+
+	/* cycle of stop ignoring VF message */
+	uint64_t ignore_end_cycle;
 };
 
 /*
@@ -900,6 +916,20 @@ struct i40e_rte_flow_rss_conf {
 	uint16_t queue[I40E_MAX_Q_PER_TC]; /**< Queues indices to use. */
 };
 
+struct i40e_vf_msg_cfg {
+	/* maximal VF message during a statistic period */
+	uint32_t max_msg;
+
+	/* statistic period, in second */
+	uint32_t period;
+	/*
+	 * If message statistics from a VF exceed the maximal limitation,
+	 * the PF will ignore any new message from that VF for
+	 * 'ignor_second' time.
+	 */
+	uint32_t ignore_second;
+};
+
 /*
  * Structure to store private data specific for PF instance.
  */
@@ -975,6 +1005,8 @@ struct i40e_pf {
 	struct i40e_customized_pctype customized_pctype[I40E_CUSTOMIZED_MAX];
 	/* Switch Domain Id */
 	uint16_t switch_domain_id;
+
+	struct i40e_vf_msg_cfg vf_msg_cfg;
 };
 
 enum pending_msg {
diff --git a/drivers/net/i40e/i40e_pf.c b/drivers/net/i40e/i40e_pf.c
index b28d02e..2e1cbb0 100644
--- a/drivers/net/i40e/i40e_pf.c
+++ b/drivers/net/i40e/i40e_pf.c
@@ -1299,6 +1299,8 @@
 	struct rte_pmd_i40e_mb_event_param ret_param;
 	bool b_op = TRUE;
 	int ret;
+	uint32_t next, limit;
+	uint64_t cur_cycle, first_cycle;
 
 	if (vf_id > pf->vf_num - 1 || !pf->vfs) {
 		PMD_DRV_LOG(ERR, "invalid argument");
@@ -1306,11 +1308,19 @@
 	}
 
 	vf = &pf->vfs[vf_id];
+
+	/* read current cycle */
+	cur_cycle = rte_get_timer_cycles();
+
+	/* if the VF being blocked, ignore the message and return */
+	if (cur_cycle < vf->ignore_end_cycle)
+		return;
+
 	if (!vf->vsi) {
 		PMD_DRV_LOG(ERR, "NO VSI associated with VF found");
 		i40e_pf_host_send_msg_to_vf(vf, opcode,
 			I40E_ERR_NO_AVAILABLE_VSI, NULL, 0);
-		return;
+		goto check;
 	}
 
 	/* perform basic checks on the msg */
@@ -1334,7 +1344,7 @@
 			    vf_id, opcode, msglen);
 		i40e_pf_host_send_msg_to_vf(vf, opcode,
 					    I40E_ERR_PARAM, NULL, 0);
-		return;
+		goto check;
 	}
 
 	/**
@@ -1456,6 +1466,41 @@
 								NULL, 0);
 		break;
 	}
+
+check:
+	/* if message validation not enabled */
+	if (!pf->vf_msg_cfg.max_msg)
+		return;
+
+	/* store current cycle to next place */
+	limit = pf->vf_msg_cfg.max_msg - 1;
+	next = vf->msg_index;
+	next = (next >= limit) ? 0 : (next + 1);
+	vf->msg_index = next;
+	vf->msg_timestamps[next] = cur_cycle;
+
+	/* read the timestamp of earliest message */
+	next = (next >= limit) ? 0 : (next + 1);
+	first_cycle = vf->msg_timestamps[next];
+
+	/*
+	 * If the time span from the arrival time of first message to
+	 * the arrival time of current message smaller than `period`,
+	 * that mean too much message in this statistic period.
+	 */
+	if (first_cycle && cur_cycle < first_cycle +
+			(uint64_t)pf->vf_msg_cfg.period * rte_get_timer_hz()) {
+		PMD_DRV_LOG(ERR, "VF %u too much messages(%u in %u"
+				" seconds),\n\tany new message from which"
+				" will be ignored during next %u seconds!",
+				vf_id, limit + 1,
+				(uint32_t)((cur_cycle - first_cycle +
+				rte_get_timer_hz() - 1) / rte_get_timer_hz()),
+				pf->vf_msg_cfg.ignore_second);
+		vf->ignore_end_cycle = rte_get_timer_cycles() +
+				pf->vf_msg_cfg.ignore_second *
+				rte_get_timer_hz();
+	}
 }
 
 int
@@ -1465,6 +1510,7 @@
 	struct i40e_hw *hw = I40E_PF_TO_HW(pf);
 	int ret, i;
 	uint32_t val;
+	size_t size;
 
 	PMD_INIT_FUNC_TRACE();
 
@@ -1489,10 +1535,22 @@
 	I40E_WRITE_REG(hw, I40E_PFGEN_PORTMDIO_NUM, val);
 	I40E_WRITE_FLUSH(hw);
 
+	/* calculate the memory size for storing timestamp of messages */
+	size = pf->vf_msg_cfg.max_msg * sizeof(uint64_t);
+
 	for (i = 0; i < pf->vf_num; i++) {
 		pf->vfs[i].pf = pf;
 		pf->vfs[i].state = I40E_VF_INACTIVE;
 		pf->vfs[i].vf_idx = i;
+
+		if (size) {
+			/* allocate memory for store timestamp of messages */
+			pf->vfs[i].msg_timestamps = (uint64_t *)
+					rte_zmalloc("i40e_pf_vf", size, 0);
+			if (pf->vfs[i].msg_timestamps == NULL)
+				goto fail;
+		}
+
 		ret = i40e_pf_host_vf_reset(&pf->vfs[i], 0);
 		if (ret != I40E_SUCCESS)
 			goto fail;
@@ -1505,6 +1563,9 @@
 	return I40E_SUCCESS;
 
 fail:
+	for (; i >= 0; i--)
+		if (pf->vfs[i].msg_timestamps)
+			rte_free(pf->vfs[i].msg_timestamps);
 	rte_free(pf->vfs);
 	i40e_pf_enable_irq0(hw);
 
@@ -1517,6 +1578,7 @@
 	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
 	struct i40e_hw *hw = I40E_PF_TO_HW(pf);
 	uint32_t val;
+	int i;
 
 	PMD_INIT_FUNC_TRACE();
 
@@ -1529,6 +1591,11 @@
 		(pf->vf_nb_qps == 0))
 		return I40E_SUCCESS;
 
+	/* free memory for store timestamp of messages */
+	for (i = 0; i < pf->vf_num; i++)
+		if (pf->vfs[i].msg_timestamps)
+			rte_free(pf->vfs[i].msg_timestamps);
+
 	/* free memory to store VF structure */
 	rte_free(pf->vfs);
 	pf->vfs = NULL;
-- 
1.8.3.1


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [dpdk-dev] [PATCH v7] net/i40e: limit the number of VF messages
  2019-09-20 10:22             ` [dpdk-dev] [PATCH v7] " alvinx.zhang
@ 2019-09-20  2:20               ` Zhang, Qi Z
  2019-09-23 23:52               ` Ye Xiaolong
  2019-09-24 14:24               ` [dpdk-dev] [PATCH v8] " alvinx.zhang
  2 siblings, 0 replies; 14+ messages in thread
From: Zhang, Qi Z @ 2019-09-20  2:20 UTC (permalink / raw)
  To: Zhang, AlvinX; +Cc: dev



> -----Original Message-----
> From: Zhang, AlvinX
> Sent: Friday, September 20, 2019 6:23 PM
> To: Zhang, Qi Z <qi.z.zhang@intel.com>
> Cc: dev@dpdk.org; Zhang, AlvinX <alvinx.zhang@intel.com>
> Subject: [PATCH v7] net/i40e: limit the number of VF messages
> 
> From: Alvin Zhang <alvinx.zhang@intel.com>
> 
> PF driver supportes counting VF adminQ messages. If any VF driver sending
> much more adminQ messages to it's PF driver in a period of time, it will trigger
> the PF's message limiting, then in the next certain amount of seconds the PF
> driver will ignore any new adminQ message from that VF.
> 
> Signed-off-by: Alvin Zhang <alvinx.zhang@intel.com>

Acked-by: Qi Zhang <qi.z.zhang@intel.com>

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [dpdk-dev] [PATCH v7] net/i40e: limit the number of VF messages
  2019-09-18 21:03           ` [dpdk-dev] [PATCH v6] net/i40e: limit the number of VF messages alvinx.zhang
@ 2019-09-20 10:22             ` alvinx.zhang
  2019-09-20  2:20               ` Zhang, Qi Z
                                 ` (2 more replies)
  0 siblings, 3 replies; 14+ messages in thread
From: alvinx.zhang @ 2019-09-20 10:22 UTC (permalink / raw)
  To: qi.z.zhang; +Cc: dev, Alvin Zhang

From: Alvin Zhang <alvinx.zhang@intel.com>

PF driver supportes counting VF adminQ messages. If any VF driver
sending much more adminQ messages to it's PF driver in a period of
time, it will trigger the PF's message limiting, then in the
next certain amount of seconds the PF driver will ignore any new
adminQ message from that VF.

Signed-off-by: Alvin Zhang <alvinx.zhang@intel.com>
---

v7: modify codes according to the comments.
v6: combine valid/invalid/unsupported messages to one.
v5: add validating the "valid" message, scheme redesigned.
v4: change the instruction file "i40e.rst".
v3: resolve the compile issue on ubuntu 32-bit system.
v2: modify codes according to the comments.
---
 doc/guides/nics/i40e.rst       | 10 ++++++
 drivers/net/i40e/i40e_ethdev.c | 72 ++++++++++++++++++++++++++++++++++++++++++
 drivers/net/i40e/i40e_ethdev.h | 32 +++++++++++++++++++
 drivers/net/i40e/i40e_pf.c     | 71 +++++++++++++++++++++++++++++++++++++++--
 4 files changed, 183 insertions(+), 2 deletions(-)

diff --git a/doc/guides/nics/i40e.rst b/doc/guides/nics/i40e.rst
index 0884e15..38acf59 100644
--- a/doc/guides/nics/i40e.rst
+++ b/doc/guides/nics/i40e.rst
@@ -185,6 +185,16 @@ Runtime Config Options
 
   -w 84:00.0,use-latest-supported-vec=1
 
+- ``Enable validation for VF message`` (default ``not enabled``)
+
+  The PF counts messages from each VF. If in any period of seconds the message
+  statistic from a VF exceeds maximal limitation, the PF will ignore any new message
+  from that VF for some seconds.
+  Format -- "maximal-message@period-seconds:ignore-seconds"
+  For example::
+
+  -w 84:00.0,vf_msg_cfg=80@120:180
+
 Vector RX Pre-conditions
 ~~~~~~~~~~~~~~~~~~~~~~~~
 For Vector RX it is assumed that the number of descriptor rings will be a power
diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index 4e40b7a..4e2c488 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -44,6 +44,7 @@
 #define ETH_I40E_SUPPORT_MULTI_DRIVER	"support-multi-driver"
 #define ETH_I40E_QUEUE_NUM_PER_VF_ARG	"queue-num-per-vf"
 #define ETH_I40E_USE_LATEST_VEC	"use-latest-supported-vec"
+#define ETH_I40E_VF_MSG_CFG		"vf_msg_cfg"
 
 #define I40E_CLEAR_PXE_WAIT_MS     200
 
@@ -406,6 +407,7 @@ static int i40e_sw_tunnel_filter_insert(struct i40e_pf *pf,
 	ETH_I40E_SUPPORT_MULTI_DRIVER,
 	ETH_I40E_QUEUE_NUM_PER_VF_ARG,
 	ETH_I40E_USE_LATEST_VEC,
+	ETH_I40E_VF_MSG_CFG,
 	NULL};
 
 static const struct rte_pci_id pci_id_i40e_map[] = {
@@ -1256,6 +1258,74 @@ static inline void i40e_config_automask(struct i40e_pf *pf)
 	return 0;
 }
 
+static int
+read_vf_msg_config(__rte_unused const char *key,
+			       const char *value,
+			       void *opaque)
+{
+	struct i40e_vf_msg_cfg cfg;
+
+	memset(&cfg, 0, sizeof(cfg));
+	if (sscanf(value, "%u@%u:%u", &cfg.max_msg, &cfg.period,
+			&cfg.ignore_second) != 3) {
+		PMD_DRV_LOG(ERR, "format error! example: "
+				"%s=60@120:180", ETH_I40E_VF_MSG_CFG);
+		return -EINVAL;
+	}
+
+	/*
+	 * If the message validation function been enabled, the 'period'
+	 * and 'ignore_second' must greater than 0.
+	 */
+	if (cfg.max_msg && (!cfg.period || !cfg.ignore_second)) {
+		PMD_DRV_LOG(ERR, "%s error! the second and third"
+				" number must be greater than 0!",
+				ETH_I40E_VF_MSG_CFG);
+		return -EINVAL;
+	}
+
+	memcpy(opaque, &cfg, sizeof(cfg));
+	return 0;
+}
+
+static int
+i40e_parse_vf_msg_config(struct rte_eth_dev *dev,
+		struct i40e_vf_msg_cfg *msg_cfg)
+{
+	struct rte_kvargs *kvlist;
+	int kvargs_count;
+	int ret = 0;
+
+	/* reset all to zero */
+	memset(msg_cfg, 0, sizeof(*msg_cfg));
+
+	if (!dev->device->devargs)
+		return ret;
+
+	kvlist = rte_kvargs_parse(dev->device->devargs->args, valid_keys);
+	if (!kvlist)
+		return -EINVAL;
+
+	kvargs_count = rte_kvargs_count(kvlist, ETH_I40E_VF_MSG_CFG);
+	if (!kvargs_count)
+		goto free_end;
+
+	if (kvargs_count > 1) {
+		PMD_DRV_LOG(ERR, "More than one argument \"%s\"!",
+				ETH_I40E_VF_MSG_CFG);
+		ret = -EINVAL;
+		goto free_end;
+	}
+
+	if (rte_kvargs_process(kvlist, ETH_I40E_VF_MSG_CFG,
+			read_vf_msg_config, msg_cfg) < 0)
+		ret = -EINVAL;
+
+free_end:
+	rte_kvargs_free(kvlist);
+	return ret;
+}
+
 #define I40E_ALARM_INTERVAL 50000 /* us */
 
 static int
@@ -1328,6 +1398,8 @@ static inline void i40e_config_automask(struct i40e_pf *pf)
 		return -EIO;
 	}
 
+	/* read VF message configuration */
+	i40e_parse_vf_msg_config(dev, &pf->vf_msg_cfg);
 	/* Check if need to support multi-driver */
 	i40e_support_multi_driver(dev);
 	/* Check if users want the latest supported vec path */
diff --git a/drivers/net/i40e/i40e_ethdev.h b/drivers/net/i40e/i40e_ethdev.h
index 38ac3ea..261954b 100644
--- a/drivers/net/i40e/i40e_ethdev.h
+++ b/drivers/net/i40e/i40e_ethdev.h
@@ -426,6 +426,22 @@ struct i40e_pf_vf {
 	/* version of the virtchnl from VF */
 	struct virtchnl_version_info version;
 	uint32_t request_caps; /* offload caps requested from VF */
+
+	/*
+	 * Variables for store the arrival timestamp of VF messages.
+	 * If the timestamp of latest message stored at
+	 * `msg_timestamps[index % max]` then the timestamp of
+	 * earliest message stored at `msg_time[(index + 1) % max]`.
+	 * When a new message come, the timestamp of this message
+	 * will be stored at `msg_timestamps[(index + 1) % max]` and the
+	 * earliest message timestamp is at
+	 * `msg_timestamps[(index + 2) % max]` now...
+	 */
+	uint32_t msg_index;
+	uint64_t *msg_timestamps;
+
+	/* cycle of stop ignoring VF message */
+	uint64_t ignore_end_cycle;
 };
 
 /*
@@ -900,6 +916,20 @@ struct i40e_rte_flow_rss_conf {
 	uint16_t queue[I40E_MAX_Q_PER_TC]; /**< Queues indices to use. */
 };
 
+struct i40e_vf_msg_cfg {
+	/* maximal VF message during a statistic period */
+	uint32_t max_msg;
+
+	/* statistic period, in second */
+	uint32_t period;
+	/*
+	 * If message statistics from a VF exceed the maximal limitation,
+	 * the PF will ignore any new message from that VF for
+	 * 'ignor_second' time.
+	 */
+	uint32_t ignore_second;
+};
+
 /*
  * Structure to store private data specific for PF instance.
  */
@@ -975,6 +1005,8 @@ struct i40e_pf {
 	struct i40e_customized_pctype customized_pctype[I40E_CUSTOMIZED_MAX];
 	/* Switch Domain Id */
 	uint16_t switch_domain_id;
+
+	struct i40e_vf_msg_cfg vf_msg_cfg;
 };
 
 enum pending_msg {
diff --git a/drivers/net/i40e/i40e_pf.c b/drivers/net/i40e/i40e_pf.c
index b28d02e..9ee2be6 100644
--- a/drivers/net/i40e/i40e_pf.c
+++ b/drivers/net/i40e/i40e_pf.c
@@ -1297,6 +1297,8 @@
 	/* AdminQ will pass absolute VF id, transfer to internal vf id */
 	uint16_t vf_id = abs_vf_id - hw->func_caps.vf_base_id;
 	struct rte_pmd_i40e_mb_event_param ret_param;
+	uint64_t first_cycle, cur_cycle;
+	uint32_t next, limit;
 	bool b_op = TRUE;
 	int ret;
 
@@ -1306,11 +1308,19 @@
 	}
 
 	vf = &pf->vfs[vf_id];
+
+	/* read current cycle */
+	cur_cycle = rte_get_timer_cycles();
+
+	/* if the VF being blocked, ignore the message and return */
+	if (cur_cycle < vf->ignore_end_cycle)
+		return;
+
 	if (!vf->vsi) {
 		PMD_DRV_LOG(ERR, "NO VSI associated with VF found");
 		i40e_pf_host_send_msg_to_vf(vf, opcode,
 			I40E_ERR_NO_AVAILABLE_VSI, NULL, 0);
-		return;
+		goto check;
 	}
 
 	/* perform basic checks on the msg */
@@ -1334,7 +1344,7 @@
 			    vf_id, opcode, msglen);
 		i40e_pf_host_send_msg_to_vf(vf, opcode,
 					    I40E_ERR_PARAM, NULL, 0);
-		return;
+		goto check;
 	}
 
 	/**
@@ -1456,6 +1466,41 @@
 								NULL, 0);
 		break;
 	}
+
+check:
+	/* if message validation not enabled */
+	if (!pf->vf_msg_cfg.max_msg)
+		return;
+
+	/* store current cycle to next place */
+	limit = pf->vf_msg_cfg.max_msg - 1;
+	next = vf->msg_index;
+	next = (next >= limit) ? 0 : (next + 1);
+	vf->msg_index = next;
+	vf->msg_timestamps[next] = cur_cycle;
+
+	/* read the timestamp of earliest message */
+	next = (next >= limit) ? 0 : (next + 1);
+	first_cycle = vf->msg_timestamps[next];
+
+	/*
+	 * If the time span from the arrival time of first message to
+	 * the arrival time of current message smaller than `period`,
+	 * that mean too much message in this statistic period.
+	 */
+	if (first_cycle && cur_cycle < first_cycle +
+			(uint64_t)pf->vf_msg_cfg.period * rte_get_timer_hz()) {
+		PMD_DRV_LOG(ERR, "VF %u too much messages(%u in %u"
+				" seconds),\n\tany new message from which"
+				" will be ignored during next %u seconds!",
+				vf_id, limit + 1,
+				(uint32_t)((cur_cycle - first_cycle +
+				rte_get_timer_hz() - 1) / rte_get_timer_hz()),
+				pf->vf_msg_cfg.ignore_second);
+		vf->ignore_end_cycle = rte_get_timer_cycles() +
+				pf->vf_msg_cfg.ignore_second *
+				rte_get_timer_hz();
+	}
 }
 
 int
@@ -1463,6 +1508,7 @@
 {
 	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
 	struct i40e_hw *hw = I40E_PF_TO_HW(pf);
+	size_t size;
 	int ret, i;
 	uint32_t val;
 
@@ -1489,10 +1535,22 @@
 	I40E_WRITE_REG(hw, I40E_PFGEN_PORTMDIO_NUM, val);
 	I40E_WRITE_FLUSH(hw);
 
+	/* calculate the memory size for storing timestamp of messages */
+	size = pf->vf_msg_cfg.max_msg * sizeof(uint64_t);
+
 	for (i = 0; i < pf->vf_num; i++) {
 		pf->vfs[i].pf = pf;
 		pf->vfs[i].state = I40E_VF_INACTIVE;
 		pf->vfs[i].vf_idx = i;
+
+		if (size) {
+			/* allocate memory for store timestamp of messages */
+			pf->vfs[i].msg_timestamps = (uint64_t *)
+					rte_zmalloc("i40e_pf_vf", size, 0);
+			if (pf->vfs[i].msg_timestamps == NULL)
+				goto fail;
+		}
+
 		ret = i40e_pf_host_vf_reset(&pf->vfs[i], 0);
 		if (ret != I40E_SUCCESS)
 			goto fail;
@@ -1505,6 +1563,9 @@
 	return I40E_SUCCESS;
 
 fail:
+	for (; i >= 0; i--)
+		if (pf->vfs[i].msg_timestamps)
+			rte_free(pf->vfs[i].msg_timestamps);
 	rte_free(pf->vfs);
 	i40e_pf_enable_irq0(hw);
 
@@ -1517,6 +1578,7 @@
 	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
 	struct i40e_hw *hw = I40E_PF_TO_HW(pf);
 	uint32_t val;
+	int i;
 
 	PMD_INIT_FUNC_TRACE();
 
@@ -1529,6 +1591,11 @@
 		(pf->vf_nb_qps == 0))
 		return I40E_SUCCESS;
 
+	/* free memory for store timestamp of messages */
+	for (i = 0; i < pf->vf_num; i++)
+		if (pf->vfs[i].msg_timestamps)
+			rte_free(pf->vfs[i].msg_timestamps);
+
 	/* free memory to store VF structure */
 	rte_free(pf->vfs);
 	pf->vfs = NULL;
-- 
1.8.3.1


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [dpdk-dev] [PATCH v7] net/i40e: limit the number of VF messages
  2019-09-20 10:22             ` [dpdk-dev] [PATCH v7] " alvinx.zhang
  2019-09-20  2:20               ` Zhang, Qi Z
@ 2019-09-23 23:52               ` Ye Xiaolong
  2019-09-24 14:24               ` [dpdk-dev] [PATCH v8] " alvinx.zhang
  2 siblings, 0 replies; 14+ messages in thread
From: Ye Xiaolong @ 2019-09-23 23:52 UTC (permalink / raw)
  To: alvinx.zhang; +Cc: qi.z.zhang, dev

On 09/20, alvinx.zhang@intel.com wrote:
>From: Alvin Zhang <alvinx.zhang@intel.com>
>
>PF driver supportes counting VF adminQ messages. If any VF driver

s/supportes/supports/

>sending much more adminQ messages to it's PF driver in a period of

s/sending/sends
s/it's/its

>time, it will trigger the PF's message limiting, then in the

s/limiting/limitation

>next certain amount of seconds the PF driver will ignore any new
>adminQ message from that VF.
>
>Signed-off-by: Alvin Zhang <alvinx.zhang@intel.com>
>---
>
>v7: modify codes according to the comments.
>v6: combine valid/invalid/unsupported messages to one.
>v5: add validating the "valid" message, scheme redesigned.
>v4: change the instruction file "i40e.rst".
>v3: resolve the compile issue on ubuntu 32-bit system.
>v2: modify codes according to the comments.
>---
> doc/guides/nics/i40e.rst       | 10 ++++++
> drivers/net/i40e/i40e_ethdev.c | 72 ++++++++++++++++++++++++++++++++++++++++++
> drivers/net/i40e/i40e_ethdev.h | 32 +++++++++++++++++++
> drivers/net/i40e/i40e_pf.c     | 71 +++++++++++++++++++++++++++++++++++++++--
> 4 files changed, 183 insertions(+), 2 deletions(-)
>
>diff --git a/doc/guides/nics/i40e.rst b/doc/guides/nics/i40e.rst
>index 0884e15..38acf59 100644
>--- a/doc/guides/nics/i40e.rst
>+++ b/doc/guides/nics/i40e.rst
>@@ -185,6 +185,16 @@ Runtime Config Options
> 
>   -w 84:00.0,use-latest-supported-vec=1
> 
>+- ``Enable validation for VF message`` (default ``not enabled``)
>+
>+  The PF counts messages from each VF. If in any period of seconds the message
>+  statistic from a VF exceeds maximal limitation, the PF will ignore any new message
>+  from that VF for some seconds.
>+  Format -- "maximal-message@period-seconds:ignore-seconds"
>+  For example::
>+
>+  -w 84:00.0,vf_msg_cfg=80@120:180
>+
> Vector RX Pre-conditions
> ~~~~~~~~~~~~~~~~~~~~~~~~
> For Vector RX it is assumed that the number of descriptor rings will be a power
>diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
>index 4e40b7a..4e2c488 100644
>--- a/drivers/net/i40e/i40e_ethdev.c
>+++ b/drivers/net/i40e/i40e_ethdev.c
>@@ -44,6 +44,7 @@
> #define ETH_I40E_SUPPORT_MULTI_DRIVER	"support-multi-driver"
> #define ETH_I40E_QUEUE_NUM_PER_VF_ARG	"queue-num-per-vf"
> #define ETH_I40E_USE_LATEST_VEC	"use-latest-supported-vec"
>+#define ETH_I40E_VF_MSG_CFG		"vf_msg_cfg"
> 
> #define I40E_CLEAR_PXE_WAIT_MS     200
> 
>@@ -406,6 +407,7 @@ static int i40e_sw_tunnel_filter_insert(struct i40e_pf *pf,
> 	ETH_I40E_SUPPORT_MULTI_DRIVER,
> 	ETH_I40E_QUEUE_NUM_PER_VF_ARG,
> 	ETH_I40E_USE_LATEST_VEC,
>+	ETH_I40E_VF_MSG_CFG,
> 	NULL};
> 
> static const struct rte_pci_id pci_id_i40e_map[] = {
>@@ -1256,6 +1258,74 @@ static inline void i40e_config_automask(struct i40e_pf *pf)
> 	return 0;
> }
> 
>+static int
>+read_vf_msg_config(__rte_unused const char *key,
>+			       const char *value,
>+			       void *opaque)
>+{
>+	struct i40e_vf_msg_cfg cfg;
>+
>+	memset(&cfg, 0, sizeof(cfg));
>+	if (sscanf(value, "%u@%u:%u", &cfg.max_msg, &cfg.period,
>+			&cfg.ignore_second) != 3) {
>+		PMD_DRV_LOG(ERR, "format error! example: "
>+				"%s=60@120:180", ETH_I40E_VF_MSG_CFG);
>+		return -EINVAL;
>+	}
>+
>+	/*
>+	 * If the message validation function been enabled, the 'period'
>+	 * and 'ignore_second' must greater than 0.
>+	 */
>+	if (cfg.max_msg && (!cfg.period || !cfg.ignore_second)) {
>+		PMD_DRV_LOG(ERR, "%s error! the second and third"
>+				" number must be greater than 0!",
>+				ETH_I40E_VF_MSG_CFG);
>+		return -EINVAL;
>+	}
>+
>+	memcpy(opaque, &cfg, sizeof(cfg));

rte_memcpy is preferred.

And how about
	
	struct i40e_vf_msg_cfg *cfg = opaque;

Then you don't need memset/memcpy at all.


>+	return 0;
>+}
>+
>+static int
>+i40e_parse_vf_msg_config(struct rte_eth_dev *dev,
>+		struct i40e_vf_msg_cfg *msg_cfg)
>+{
>+	struct rte_kvargs *kvlist;
>+	int kvargs_count;
>+	int ret = 0;
>+
>+	/* reset all to zero */
>+	memset(msg_cfg, 0, sizeof(*msg_cfg));

The code is quite straightforward, the comment is not needed.

>+
>+	if (!dev->device->devargs)
>+		return ret;
>+
>+	kvlist = rte_kvargs_parse(dev->device->devargs->args, valid_keys);
>+	if (!kvlist)
>+		return -EINVAL;
>+
>+	kvargs_count = rte_kvargs_count(kvlist, ETH_I40E_VF_MSG_CFG);
>+	if (!kvargs_count)
>+		goto free_end;
>+
>+	if (kvargs_count > 1) {
>+		PMD_DRV_LOG(ERR, "More than one argument \"%s\"!",
>+				ETH_I40E_VF_MSG_CFG);
>+		ret = -EINVAL;
>+		goto free_end;
>+	}
>+
>+	if (rte_kvargs_process(kvlist, ETH_I40E_VF_MSG_CFG,
>+			read_vf_msg_config, msg_cfg) < 0)
>+		ret = -EINVAL;
>+
>+free_end:
>+	rte_kvargs_free(kvlist);
>+	return ret;
>+}
>+
> #define I40E_ALARM_INTERVAL 50000 /* us */
> 
> static int
>@@ -1328,6 +1398,8 @@ static inline void i40e_config_automask(struct i40e_pf *pf)
> 		return -EIO;
> 	}
> 
>+	/* read VF message configuration */

Unnecessary comment.

>+	i40e_parse_vf_msg_config(dev, &pf->vf_msg_cfg);
> 	/* Check if need to support multi-driver */
> 	i40e_support_multi_driver(dev);
> 	/* Check if users want the latest supported vec path */
>diff --git a/drivers/net/i40e/i40e_ethdev.h b/drivers/net/i40e/i40e_ethdev.h
>index 38ac3ea..261954b 100644
>--- a/drivers/net/i40e/i40e_ethdev.h
>+++ b/drivers/net/i40e/i40e_ethdev.h
>@@ -426,6 +426,22 @@ struct i40e_pf_vf {
> 	/* version of the virtchnl from VF */
> 	struct virtchnl_version_info version;
> 	uint32_t request_caps; /* offload caps requested from VF */
>+
>+	/*
>+	 * Variables for store the arrival timestamp of VF messages.
>+	 * If the timestamp of latest message stored at
>+	 * `msg_timestamps[index % max]` then the timestamp of
>+	 * earliest message stored at `msg_time[(index + 1) % max]`.
>+	 * When a new message come, the timestamp of this message
>+	 * will be stored at `msg_timestamps[(index + 1) % max]` and the
>+	 * earliest message timestamp is at
>+	 * `msg_timestamps[(index + 2) % max]` now...
>+	 */
>+	uint32_t msg_index;
>+	uint64_t *msg_timestamps;
>+
>+	/* cycle of stop ignoring VF message */
>+	uint64_t ignore_end_cycle;
> };
> 
> /*
>@@ -900,6 +916,20 @@ struct i40e_rte_flow_rss_conf {
> 	uint16_t queue[I40E_MAX_Q_PER_TC]; /**< Queues indices to use. */
> };
> 
>+struct i40e_vf_msg_cfg {
>+	/* maximal VF message during a statistic period */
>+	uint32_t max_msg;
>+
>+	/* statistic period, in second */
>+	uint32_t period;
>+	/*
>+	 * If message statistics from a VF exceed the maximal limitation,
>+	 * the PF will ignore any new message from that VF for
>+	 * 'ignor_second' time.
>+	 */
>+	uint32_t ignore_second;
>+};
>+
> /*
>  * Structure to store private data specific for PF instance.
>  */
>@@ -975,6 +1005,8 @@ struct i40e_pf {
> 	struct i40e_customized_pctype customized_pctype[I40E_CUSTOMIZED_MAX];
> 	/* Switch Domain Id */
> 	uint16_t switch_domain_id;
>+
>+	struct i40e_vf_msg_cfg vf_msg_cfg;
> };
> 
> enum pending_msg {
>diff --git a/drivers/net/i40e/i40e_pf.c b/drivers/net/i40e/i40e_pf.c
>index b28d02e..9ee2be6 100644
>--- a/drivers/net/i40e/i40e_pf.c
>+++ b/drivers/net/i40e/i40e_pf.c
>@@ -1297,6 +1297,8 @@
> 	/* AdminQ will pass absolute VF id, transfer to internal vf id */
> 	uint16_t vf_id = abs_vf_id - hw->func_caps.vf_base_id;
> 	struct rte_pmd_i40e_mb_event_param ret_param;
>+	uint64_t first_cycle, cur_cycle;
>+	uint32_t next, limit;
> 	bool b_op = TRUE;
> 	int ret;
> 
>@@ -1306,11 +1308,19 @@
> 	}
> 
> 	vf = &pf->vfs[vf_id];
>+
>+	/* read current cycle */

Unnecessary comment.

>+	cur_cycle = rte_get_timer_cycles();
>+
>+	/* if the VF being blocked, ignore the message and return */
>+	if (cur_cycle < vf->ignore_end_cycle)
>+		return;
>+
> 	if (!vf->vsi) {
> 		PMD_DRV_LOG(ERR, "NO VSI associated with VF found");
> 		i40e_pf_host_send_msg_to_vf(vf, opcode,
> 			I40E_ERR_NO_AVAILABLE_VSI, NULL, 0);
>-		return;
>+		goto check;
> 	}
> 
> 	/* perform basic checks on the msg */
>@@ -1334,7 +1344,7 @@
> 			    vf_id, opcode, msglen);
> 		i40e_pf_host_send_msg_to_vf(vf, opcode,
> 					    I40E_ERR_PARAM, NULL, 0);
>-		return;
>+		goto check;
> 	}
> 
> 	/**
>@@ -1456,6 +1466,41 @@
> 								NULL, 0);
> 		break;
> 	}
>+
>+check:
>+	/* if message validation not enabled */
>+	if (!pf->vf_msg_cfg.max_msg)
>+		return;
>+
>+	/* store current cycle to next place */
>+	limit = pf->vf_msg_cfg.max_msg - 1;
>+	next = vf->msg_index;
>+	next = (next >= limit) ? 0 : (next + 1);
>+	vf->msg_index = next;
>+	vf->msg_timestamps[next] = cur_cycle;
>+
>+	/* read the timestamp of earliest message */
>+	next = (next >= limit) ? 0 : (next + 1);
>+	first_cycle = vf->msg_timestamps[next];

Above code block is quite tedious, how about:

vf->msg_timestamps[msg_index++ % (pf->vf_msg_cfg - 1)] = cur_cycle;
first_cycle = vf->msg_timestamps[msg_index % (pf->vf_msg_cfg - 1)];

>+
>+	/*
>+	 * If the time span from the arrival time of first message to
>+	 * the arrival time of current message smaller than `period`,
>+	 * that mean too much message in this statistic period.
>+	 */
>+	if (first_cycle && cur_cycle < first_cycle +
>+			(uint64_t)pf->vf_msg_cfg.period * rte_get_timer_hz()) {
>+		PMD_DRV_LOG(ERR, "VF %u too much messages(%u in %u"

I think we can use WARN level for the log.

>+				" seconds),\n\tany new message from which"
>+				" will be ignored during next %u seconds!",
>+				vf_id, limit + 1,
>+				(uint32_t)((cur_cycle - first_cycle +
>+				rte_get_timer_hz() - 1) / rte_get_timer_hz()),
>+				pf->vf_msg_cfg.ignore_second);
>+		vf->ignore_end_cycle = rte_get_timer_cycles() +
>+				pf->vf_msg_cfg.ignore_second *
>+				rte_get_timer_hz();
>+	}
> }
> 
> int
>@@ -1463,6 +1508,7 @@
> {
> 	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
> 	struct i40e_hw *hw = I40E_PF_TO_HW(pf);
>+	size_t size;
> 	int ret, i;
> 	uint32_t val;
> 
>@@ -1489,10 +1535,22 @@
> 	I40E_WRITE_REG(hw, I40E_PFGEN_PORTMDIO_NUM, val);
> 	I40E_WRITE_FLUSH(hw);
> 
>+	/* calculate the memory size for storing timestamp of messages */
>+	size = pf->vf_msg_cfg.max_msg * sizeof(uint64_t);
>+
> 	for (i = 0; i < pf->vf_num; i++) {
> 		pf->vfs[i].pf = pf;
> 		pf->vfs[i].state = I40E_VF_INACTIVE;
> 		pf->vfs[i].vf_idx = i;
>+
>+		if (size) {
>+			/* allocate memory for store timestamp of messages */
>+			pf->vfs[i].msg_timestamps = (uint64_t *)
>+					rte_zmalloc("i40e_pf_vf", size, 0);

Unnecessary cast.

>+			if (pf->vfs[i].msg_timestamps == NULL)

Need to set ret = -ENOMEM for the failure.

>+				goto fail;
>+		}
>+
> 		ret = i40e_pf_host_vf_reset(&pf->vfs[i], 0);
> 		if (ret != I40E_SUCCESS)
> 			goto fail;
>@@ -1505,6 +1563,9 @@
> 	return I40E_SUCCESS;
> 
> fail:
>+	for (; i >= 0; i--)
>+		if (pf->vfs[i].msg_timestamps)
>+			rte_free(pf->vfs[i].msg_timestamps);

rte_free will check the NULL pointer inside, so the if can be removed.

> 	rte_free(pf->vfs);
> 	i40e_pf_enable_irq0(hw);
> 
>@@ -1517,6 +1578,7 @@
> 	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
> 	struct i40e_hw *hw = I40E_PF_TO_HW(pf);
> 	uint32_t val;
>+	int i;
> 
> 	PMD_INIT_FUNC_TRACE();
> 
>@@ -1529,6 +1591,11 @@
> 		(pf->vf_nb_qps == 0))
> 		return I40E_SUCCESS;
> 
>+	/* free memory for store timestamp of messages */
>+	for (i = 0; i < pf->vf_num; i++)
>+		if (pf->vfs[i].msg_timestamps)
>+			rte_free(pf->vfs[i].msg_timestamps);

Ditto.

>+
> 	/* free memory to store VF structure */
> 	rte_free(pf->vfs);
> 	pf->vfs = NULL;
>-- 
>1.8.3.1
>

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [dpdk-dev] [PATCH v8] net/i40e: limit the number of VF messages
  2019-09-24 14:24               ` [dpdk-dev] [PATCH v8] " alvinx.zhang
@ 2019-09-24  8:47                 ` Ye Xiaolong
  0 siblings, 0 replies; 14+ messages in thread
From: Ye Xiaolong @ 2019-09-24  8:47 UTC (permalink / raw)
  To: alvinx.zhang; +Cc: qi.z.zhang, dev

On 09/24, alvinx.zhang@intel.com wrote:
>From: Alvin Zhang <alvinx.zhang@intel.com>
>
>PF driver supports counting VF adminQ messages. If any VF driver
>sends much more adminQ messages to its PF driver in a period of
>time, it will trigger the PF's message limitation, then in the
>next certain amount of seconds the PF driver will ignore any new
>adminQ message from that VF.
>
>Signed-off-by: Alvin Zhang <alvinx.zhang@intel.com>
>---
>
>v8: modify codes according to the comments.
>v7: modify codes according to the comments.
>v6: combine valid/invalid/unsupported messages to one.
>v5: add validating the "valid" message, scheme redesigned.
>v4: change the instruction file "i40e.rst".
>v3: resolve the compile issue on ubuntu 32-bit system.
>v2: modify codes according to the comments.
>---
> doc/guides/nics/i40e.rst       | 10 ++++++
> drivers/net/i40e/i40e_ethdev.c | 70 ++++++++++++++++++++++++++++++++++++++++++
> drivers/net/i40e/i40e_ethdev.h | 32 +++++++++++++++++++
> drivers/net/i40e/i40e_pf.c     | 65 +++++++++++++++++++++++++++++++++++++--
> 4 files changed, 175 insertions(+), 2 deletions(-)
>

Reviewed-by: Xiaolong Ye <xiaolong.ye@intel.com>

Applied to dpdk-next-net-intel.

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [dpdk-dev] [PATCH v8] net/i40e: limit the number of VF messages
  2019-09-20 10:22             ` [dpdk-dev] [PATCH v7] " alvinx.zhang
  2019-09-20  2:20               ` Zhang, Qi Z
  2019-09-23 23:52               ` Ye Xiaolong
@ 2019-09-24 14:24               ` alvinx.zhang
  2019-09-24  8:47                 ` Ye Xiaolong
  2 siblings, 1 reply; 14+ messages in thread
From: alvinx.zhang @ 2019-09-24 14:24 UTC (permalink / raw)
  To: qi.z.zhang, xiaolong.ye; +Cc: dev, Alvin Zhang

From: Alvin Zhang <alvinx.zhang@intel.com>

PF driver supports counting VF adminQ messages. If any VF driver
sends much more adminQ messages to its PF driver in a period of
time, it will trigger the PF's message limitation, then in the
next certain amount of seconds the PF driver will ignore any new
adminQ message from that VF.

Signed-off-by: Alvin Zhang <alvinx.zhang@intel.com>
---

v8: modify codes according to the comments.
v7: modify codes according to the comments.
v6: combine valid/invalid/unsupported messages to one.
v5: add validating the "valid" message, scheme redesigned.
v4: change the instruction file "i40e.rst".
v3: resolve the compile issue on ubuntu 32-bit system.
v2: modify codes according to the comments.
---
 doc/guides/nics/i40e.rst       | 10 ++++++
 drivers/net/i40e/i40e_ethdev.c | 70 ++++++++++++++++++++++++++++++++++++++++++
 drivers/net/i40e/i40e_ethdev.h | 32 +++++++++++++++++++
 drivers/net/i40e/i40e_pf.c     | 65 +++++++++++++++++++++++++++++++++++++--
 4 files changed, 175 insertions(+), 2 deletions(-)

diff --git a/doc/guides/nics/i40e.rst b/doc/guides/nics/i40e.rst
index 0884e15..38acf59 100644
--- a/doc/guides/nics/i40e.rst
+++ b/doc/guides/nics/i40e.rst
@@ -185,6 +185,16 @@ Runtime Config Options
 
   -w 84:00.0,use-latest-supported-vec=1
 
+- ``Enable validation for VF message`` (default ``not enabled``)
+
+  The PF counts messages from each VF. If in any period of seconds the message
+  statistic from a VF exceeds maximal limitation, the PF will ignore any new message
+  from that VF for some seconds.
+  Format -- "maximal-message@period-seconds:ignore-seconds"
+  For example::
+
+  -w 84:00.0,vf_msg_cfg=80@120:180
+
 Vector RX Pre-conditions
 ~~~~~~~~~~~~~~~~~~~~~~~~
 For Vector RX it is assumed that the number of descriptor rings will be a power
diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index 4e40b7a..d9df44c 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -44,6 +44,7 @@
 #define ETH_I40E_SUPPORT_MULTI_DRIVER	"support-multi-driver"
 #define ETH_I40E_QUEUE_NUM_PER_VF_ARG	"queue-num-per-vf"
 #define ETH_I40E_USE_LATEST_VEC	"use-latest-supported-vec"
+#define ETH_I40E_VF_MSG_CFG		"vf_msg_cfg"
 
 #define I40E_CLEAR_PXE_WAIT_MS     200
 
@@ -406,6 +407,7 @@ static int i40e_sw_tunnel_filter_insert(struct i40e_pf *pf,
 	ETH_I40E_SUPPORT_MULTI_DRIVER,
 	ETH_I40E_QUEUE_NUM_PER_VF_ARG,
 	ETH_I40E_USE_LATEST_VEC,
+	ETH_I40E_VF_MSG_CFG,
 	NULL};
 
 static const struct rte_pci_id pci_id_i40e_map[] = {
@@ -1256,6 +1258,73 @@ static inline void i40e_config_automask(struct i40e_pf *pf)
 	return 0;
 }
 
+static int
+read_vf_msg_config(__rte_unused const char *key,
+			       const char *value,
+			       void *opaque)
+{
+	struct i40e_vf_msg_cfg *cfg = opaque;
+
+	if (sscanf(value, "%u@%u:%u", &cfg->max_msg, &cfg->period,
+			&cfg->ignore_second) != 3) {
+		memset(cfg, 0, sizeof(*cfg));
+		PMD_DRV_LOG(ERR, "format error! example: "
+				"%s=60@120:180", ETH_I40E_VF_MSG_CFG);
+		return -EINVAL;
+	}
+
+	/*
+	 * If the message validation function been enabled, the 'period'
+	 * and 'ignore_second' must greater than 0.
+	 */
+	if (cfg->max_msg && (!cfg->period || !cfg->ignore_second)) {
+		memset(cfg, 0, sizeof(*cfg));
+		PMD_DRV_LOG(ERR, "%s error! the second and third"
+				" number must be greater than 0!",
+				ETH_I40E_VF_MSG_CFG);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int
+i40e_parse_vf_msg_config(struct rte_eth_dev *dev,
+		struct i40e_vf_msg_cfg *msg_cfg)
+{
+	struct rte_kvargs *kvlist;
+	int kvargs_count;
+	int ret = 0;
+
+	memset(msg_cfg, 0, sizeof(*msg_cfg));
+
+	if (!dev->device->devargs)
+		return ret;
+
+	kvlist = rte_kvargs_parse(dev->device->devargs->args, valid_keys);
+	if (!kvlist)
+		return -EINVAL;
+
+	kvargs_count = rte_kvargs_count(kvlist, ETH_I40E_VF_MSG_CFG);
+	if (!kvargs_count)
+		goto free_end;
+
+	if (kvargs_count > 1) {
+		PMD_DRV_LOG(ERR, "More than one argument \"%s\"!",
+				ETH_I40E_VF_MSG_CFG);
+		ret = -EINVAL;
+		goto free_end;
+	}
+
+	if (rte_kvargs_process(kvlist, ETH_I40E_VF_MSG_CFG,
+			read_vf_msg_config, msg_cfg) < 0)
+		ret = -EINVAL;
+
+free_end:
+	rte_kvargs_free(kvlist);
+	return ret;
+}
+
 #define I40E_ALARM_INTERVAL 50000 /* us */
 
 static int
@@ -1328,6 +1397,7 @@ static inline void i40e_config_automask(struct i40e_pf *pf)
 		return -EIO;
 	}
 
+	i40e_parse_vf_msg_config(dev, &pf->vf_msg_cfg);
 	/* Check if need to support multi-driver */
 	i40e_support_multi_driver(dev);
 	/* Check if users want the latest supported vec path */
diff --git a/drivers/net/i40e/i40e_ethdev.h b/drivers/net/i40e/i40e_ethdev.h
index 38ac3ea..261954b 100644
--- a/drivers/net/i40e/i40e_ethdev.h
+++ b/drivers/net/i40e/i40e_ethdev.h
@@ -426,6 +426,22 @@ struct i40e_pf_vf {
 	/* version of the virtchnl from VF */
 	struct virtchnl_version_info version;
 	uint32_t request_caps; /* offload caps requested from VF */
+
+	/*
+	 * Variables for store the arrival timestamp of VF messages.
+	 * If the timestamp of latest message stored at
+	 * `msg_timestamps[index % max]` then the timestamp of
+	 * earliest message stored at `msg_time[(index + 1) % max]`.
+	 * When a new message come, the timestamp of this message
+	 * will be stored at `msg_timestamps[(index + 1) % max]` and the
+	 * earliest message timestamp is at
+	 * `msg_timestamps[(index + 2) % max]` now...
+	 */
+	uint32_t msg_index;
+	uint64_t *msg_timestamps;
+
+	/* cycle of stop ignoring VF message */
+	uint64_t ignore_end_cycle;
 };
 
 /*
@@ -900,6 +916,20 @@ struct i40e_rte_flow_rss_conf {
 	uint16_t queue[I40E_MAX_Q_PER_TC]; /**< Queues indices to use. */
 };
 
+struct i40e_vf_msg_cfg {
+	/* maximal VF message during a statistic period */
+	uint32_t max_msg;
+
+	/* statistic period, in second */
+	uint32_t period;
+	/*
+	 * If message statistics from a VF exceed the maximal limitation,
+	 * the PF will ignore any new message from that VF for
+	 * 'ignor_second' time.
+	 */
+	uint32_t ignore_second;
+};
+
 /*
  * Structure to store private data specific for PF instance.
  */
@@ -975,6 +1005,8 @@ struct i40e_pf {
 	struct i40e_customized_pctype customized_pctype[I40E_CUSTOMIZED_MAX];
 	/* Switch Domain Id */
 	uint16_t switch_domain_id;
+
+	struct i40e_vf_msg_cfg vf_msg_cfg;
 };
 
 enum pending_msg {
diff --git a/drivers/net/i40e/i40e_pf.c b/drivers/net/i40e/i40e_pf.c
index b28d02e..7bf1e79 100644
--- a/drivers/net/i40e/i40e_pf.c
+++ b/drivers/net/i40e/i40e_pf.c
@@ -1297,6 +1297,7 @@
 	/* AdminQ will pass absolute VF id, transfer to internal vf id */
 	uint16_t vf_id = abs_vf_id - hw->func_caps.vf_base_id;
 	struct rte_pmd_i40e_mb_event_param ret_param;
+	uint64_t first_cycle, cur_cycle;
 	bool b_op = TRUE;
 	int ret;
 
@@ -1306,11 +1307,18 @@
 	}
 
 	vf = &pf->vfs[vf_id];
+
+	cur_cycle = rte_get_timer_cycles();
+
+	/* if the VF being blocked, ignore the message and return */
+	if (cur_cycle < vf->ignore_end_cycle)
+		return;
+
 	if (!vf->vsi) {
 		PMD_DRV_LOG(ERR, "NO VSI associated with VF found");
 		i40e_pf_host_send_msg_to_vf(vf, opcode,
 			I40E_ERR_NO_AVAILABLE_VSI, NULL, 0);
-		return;
+		goto check;
 	}
 
 	/* perform basic checks on the msg */
@@ -1334,7 +1342,7 @@
 			    vf_id, opcode, msglen);
 		i40e_pf_host_send_msg_to_vf(vf, opcode,
 					    I40E_ERR_PARAM, NULL, 0);
-		return;
+		goto check;
 	}
 
 	/**
@@ -1456,6 +1464,37 @@
 								NULL, 0);
 		break;
 	}
+
+check:
+	/* if message validation not enabled */
+	if (!pf->vf_msg_cfg.max_msg)
+		return;
+
+	/* store current cycle */
+	vf->msg_timestamps[vf->msg_index++] = cur_cycle;
+	vf->msg_index %= pf->vf_msg_cfg.max_msg;
+
+	/* read the timestamp of earliest message */
+	first_cycle = vf->msg_timestamps[vf->msg_index];
+
+	/*
+	 * If the time span from the arrival time of first message to
+	 * the arrival time of current message smaller than `period`,
+	 * that mean too much message in this statistic period.
+	 */
+	if (first_cycle && cur_cycle < first_cycle +
+			(uint64_t)pf->vf_msg_cfg.period * rte_get_timer_hz()) {
+		PMD_DRV_LOG(WARNING, "VF %u too much messages(%u in %u"
+				" seconds),\n\tany new message from which"
+				" will be ignored during next %u seconds!",
+				vf_id, pf->vf_msg_cfg.max_msg,
+				(uint32_t)((cur_cycle - first_cycle +
+				rte_get_timer_hz() - 1) / rte_get_timer_hz()),
+				pf->vf_msg_cfg.ignore_second);
+		vf->ignore_end_cycle = rte_get_timer_cycles() +
+				pf->vf_msg_cfg.ignore_second *
+				rte_get_timer_hz();
+	}
 }
 
 int
@@ -1463,6 +1502,7 @@
 {
 	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
 	struct i40e_hw *hw = I40E_PF_TO_HW(pf);
+	size_t size;
 	int ret, i;
 	uint32_t val;
 
@@ -1489,10 +1529,24 @@
 	I40E_WRITE_REG(hw, I40E_PFGEN_PORTMDIO_NUM, val);
 	I40E_WRITE_FLUSH(hw);
 
+	/* calculate the memory size for storing timestamp of messages */
+	size = pf->vf_msg_cfg.max_msg * sizeof(uint64_t);
+
 	for (i = 0; i < pf->vf_num; i++) {
 		pf->vfs[i].pf = pf;
 		pf->vfs[i].state = I40E_VF_INACTIVE;
 		pf->vfs[i].vf_idx = i;
+
+		if (size) {
+			/* allocate memory for store timestamp of messages */
+			pf->vfs[i].msg_timestamps =
+					rte_zmalloc("i40e_pf_vf", size, 0);
+			if (pf->vfs[i].msg_timestamps == NULL) {
+				ret = -ENOMEM;
+				goto fail;
+			}
+		}
+
 		ret = i40e_pf_host_vf_reset(&pf->vfs[i], 0);
 		if (ret != I40E_SUCCESS)
 			goto fail;
@@ -1505,6 +1559,8 @@
 	return I40E_SUCCESS;
 
 fail:
+	for (; i >= 0; i--)
+		rte_free(pf->vfs[i].msg_timestamps);
 	rte_free(pf->vfs);
 	i40e_pf_enable_irq0(hw);
 
@@ -1517,6 +1573,7 @@
 	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
 	struct i40e_hw *hw = I40E_PF_TO_HW(pf);
 	uint32_t val;
+	int i;
 
 	PMD_INIT_FUNC_TRACE();
 
@@ -1529,6 +1586,10 @@
 		(pf->vf_nb_qps == 0))
 		return I40E_SUCCESS;
 
+	/* free memory for store timestamp of messages */
+	for (i = 0; i < pf->vf_num; i++)
+		rte_free(pf->vfs[i].msg_timestamps);
+
 	/* free memory to store VF structure */
 	rte_free(pf->vfs);
 	pf->vfs = NULL;
-- 
1.8.3.1


^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2019-09-24  8:50 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-08-20  7:18 [dpdk-dev] [PATCH] net/i40e: add checking for messages from VF alvinx.zhang
2019-08-22  4:18 ` Ye Xiaolong
2019-08-28  7:13 ` [dpdk-dev] [PATCH v2] " alvinx.zhang
2019-09-09 10:01   ` [dpdk-dev] [PATCH v3] net/i40e: validate all " alvinx.zhang
2019-09-09 15:11     ` [dpdk-dev] [PATCH v4] " alvinx.zhang
2019-09-09 13:34       ` Ye Xiaolong
2019-09-10 17:12       ` alvinx.zhang
2019-09-18 15:39         ` [dpdk-dev] [PATCH v5] " alvinx.zhang
2019-09-18 21:03           ` [dpdk-dev] [PATCH v6] net/i40e: limit the number of VF messages alvinx.zhang
2019-09-20 10:22             ` [dpdk-dev] [PATCH v7] " alvinx.zhang
2019-09-20  2:20               ` Zhang, Qi Z
2019-09-23 23:52               ` Ye Xiaolong
2019-09-24 14:24               ` [dpdk-dev] [PATCH v8] " alvinx.zhang
2019-09-24  8:47                 ` Ye Xiaolong

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).