@@ -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
@@ -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 */
@@ -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 {
@@ -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;