[dpdk-dev] [PATCH] i40e: add link flow control support for FVL

Zhang, Helin helin.zhang at intel.com
Tue Oct 27 04:43:20 CET 2015



> -----Original Message-----
> From: dev [mailto:dev-bounces at dpdk.org] On Behalf Of Zhe Tao
> Sent: Friday, September 25, 2015 1:53 PM
> To: dev at dpdk.org
> Subject: [dpdk-dev] [PATCH] i40e: add link flow control support for FVL
> 
> Feature Add: Rx/Tx flow control support for the i40e
> 
> All the Rx/Tx LFC enable/disable operation is done by the F/W, so PMD driver
> need to use the Set PHY Config AD command to trigger the PHY to do the
> auto-negotiation, after the Tx/Rx pause ability is negotiated, the F/W will help us
> to set the related LFC enable/disable registers.
> PMD driver also need to configure the related registers to control how often to
> send the pause frame and what the value in the pause frame.
> 
> Signed-off-by: Zhe Tao <zhe.tao at intel.com>
> ---
>  drivers/net/i40e/i40e_ethdev.c | 170
> ++++++++++++++++++++++++++++++++++++++++-
>  drivers/net/i40e/i40e_ethdev.h |  12 +++
>  2 files changed, 179 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
> index 2dd9fdc..223ceb9 100644
> --- a/drivers/net/i40e/i40e_ethdev.c
> +++ b/drivers/net/i40e/i40e_ethdev.c
> @@ -81,6 +81,27 @@
> 
>  #define I40E_PRE_TX_Q_CFG_WAIT_US       10 /* 10 us */
> 
> +/* Flow control default timer */
> +#define I40E_DEFAULT_PAUSE_TIME 0xFFFFU
> +
> +/* Flow control default high water */
> +#define I40E_DEFAULT_HIGH_WATER (0x1C40/1024)
> +
> +/* Flow control default low water */
> +#define I40E_DEFAULT_LOW_WATER  (0x1A40/1024)
> +
> +/* Flow control enable fwd bit */
> +#define I40E_PRTMAC_FWD_CTRL   0x00000001
> +
> +/* Receive Packet Buffer size */
> +#define I40E_RXPBSIZE (968 * 1024)
> +
> +/* Kilobytes shift */
> +#define I40E_KILOSHIFT 10
> +
> +/* Receive Average Packet Size in Byte*/ #define
> +I40E_PACKET_AVERAGE_SIZE 128
> +
>  /* Mask of PF interrupt causes */
>  #define I40E_PFINT_ICR0_ENA_MASK ( \
>  		I40E_PFINT_ICR0_ENA_ECC_ERR_MASK | \
> @@ -145,6 +166,8 @@ static void i40e_vlan_strip_queue_set(struct rte_eth_dev
> *dev,  static int i40e_vlan_pvid_set(struct rte_eth_dev *dev, uint16_t pvid, int
> on);  static int i40e_dev_led_on(struct rte_eth_dev *dev);  static int
> i40e_dev_led_off(struct rte_eth_dev *dev);
> +static int i40e_flow_ctrl_get(struct rte_eth_dev *dev,
> +			      struct rte_eth_fc_conf *fc_conf);
>  static int i40e_flow_ctrl_set(struct rte_eth_dev *dev,
>  			      struct rte_eth_fc_conf *fc_conf);  static int
> i40e_priority_flow_ctrl_set(struct rte_eth_dev *dev, @@ -272,6 +295,7 @@
> static const struct eth_dev_ops i40e_eth_dev_ops = {
>  	.tx_queue_release             = i40e_dev_tx_queue_release,
>  	.dev_led_on                   = i40e_dev_led_on,
>  	.dev_led_off                  = i40e_dev_led_off,
> +	.flow_ctrl_get                = i40e_flow_ctrl_get,
>  	.flow_ctrl_set                = i40e_flow_ctrl_set,
>  	.priority_flow_ctrl_set       = i40e_priority_flow_ctrl_set,
>  	.mac_addr_add                 = i40e_macaddr_add,
> @@ -411,6 +435,9 @@ eth_i40e_dev_init(struct rte_eth_dev *dev)
>  	pf->adapter = I40E_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
>  	pf->adapter->eth_dev = dev;
>  	pf->dev_data = dev->data;
> +	pf->fc_conf.pause_time = I40E_DEFAULT_PAUSE_TIME;
> +	pf->fc_conf.high_water[I40E_MAX_TRAFFIC_CLASS] =
> I40E_DEFAULT_HIGH_WATER;
> +	pf->fc_conf.low_water[I40E_MAX_TRAFFIC_CLASS] =
> +I40E_DEFAULT_LOW_WATER;
It would be better to put above changes into i40e_pf_parameter_init().

> 
>  	hw->back = I40E_PF_TO_ADAPTER(pf);
>  	hw->hw_addr = (uint8_t *)(pci_dev->mem_resource[0].addr);
> @@ -1782,12 +1809,149 @@ i40e_dev_led_off(struct rte_eth_dev *dev)  }
> 
>  static int
> -i40e_flow_ctrl_set(__rte_unused struct rte_eth_dev *dev,
> -		   __rte_unused struct rte_eth_fc_conf *fc_conf)
> +i40e_flow_ctrl_get(struct rte_eth_dev *dev, struct rte_eth_fc_conf
> +*fc_conf) {
> +	struct i40e_hw *hw =
> I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
> +	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
> +
> +	fc_conf->pause_time = pf->fc_conf.pause_time;
> +	fc_conf->high_water =
> pf->fc_conf.high_water[I40E_MAX_TRAFFIC_CLASS];
> +	fc_conf->low_water = pf->fc_conf.low_water[I40E_MAX_TRAFFIC_CLASS];
> +
> +	 /* Return current mode according to actual setting*/
> +	switch (hw->fc.current_mode) {
> +	case I40E_FC_FULL:
> +		fc_conf->mode = RTE_FC_FULL;
> +		break;
> +	case I40E_FC_TX_PAUSE:
> +		fc_conf->mode = I40E_FC_TX_PAUSE;
> +		break;
> +	case I40E_FC_RX_PAUSE:
> +		fc_conf->mode = I40E_FC_RX_PAUSE;
> +		break;
> +	case I40E_FC_NONE:
> +		fc_conf->mode = RTE_FC_NONE;
> +		break;
> +	default:
> +		break;
> +	};
> +
> +	return 0;
> +}
> +
> +static int
> +i40e_flow_ctrl_set(struct rte_eth_dev *dev, struct rte_eth_fc_conf
> +*fc_conf)
>  {
> +	uint32_t mflcn_reg, fctrl_reg, reg;
> +	uint32_t max_high_water;
> +	uint8_t i, aq_failure;
> +	int err;
> +	struct i40e_hw *hw;
> +	struct i40e_pf *pf;
> +	enum i40e_fc_mode rte_fcmode_2_i40e_fcmode[] = {
> +		[RTE_FC_NONE] = I40E_FC_NONE,
> +		[RTE_FC_RX_PAUSE] = I40E_FC_RX_PAUSE,
> +		[RTE_FC_TX_PAUSE] = I40E_FC_TX_PAUSE,
> +		[RTE_FC_FULL] = I40E_FC_FULL
> +	};
> +
> +	/* high_water field in the rte_eth_fc_conf using the kilobytes unit */
> +
> +	max_high_water = I40E_RXPBSIZE >> I40E_KILOSHIFT;
> +	if ((fc_conf->high_water > max_high_water) ||
> +			(fc_conf->high_water < fc_conf->low_water)) {
> +		PMD_INIT_LOG(ERR, "Invalid high/low water setup value in KB, "
> +			"High_water must <= %d.", max_high_water);
> +		return -EINVAL;
> +	}
> +
> +	hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
> +	pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
> +	hw->fc.requested_mode = rte_fcmode_2_i40e_fcmode[fc_conf->mode];
> +
> +	pf->fc_conf.pause_time = fc_conf->pause_time;
> +	pf->fc_conf.high_water[I40E_MAX_TRAFFIC_CLASS] =
> fc_conf->high_water;
> +	pf->fc_conf.low_water[I40E_MAX_TRAFFIC_CLASS] = fc_conf->low_water;
> +
>  	PMD_INIT_FUNC_TRACE();
> 
> -	return -ENOSYS;
> +	/* All the link flow control related enable/disable register
> +	 * configuration is handle by the F/W
> +	 */
> +	err = i40e_set_fc(hw, &aq_failure, true);
> +	if (err < 0)
> +		return err;
> +
> +	if (i40e_is_40G_device(hw->device_id)) {
Please write your own function to check if it is 40G or not, as the function above is
not up to date.

> +		/* Configure flow control refresh threshold,
> +		 * the value for stat_tx_pause_refresh_timer[8]
> +		 * is used for global pause operation.
> +		 */
> +
> +		I40E_WRITE_REG(hw,
> +
> I40E_PRTMAC_HSEC_CTL_TX_PAUSE_REFRESH_TIMER(8),
> +			       pf->fc_conf.pause_time);
> +
> +		/* configure the timer value included in transmitted pause
> +		 * frame,
> +		 * the value for stat_tx_pause_quanta[8] is used for global
> +		 * pause operation
> +		 */
> +		I40E_WRITE_REG(hw,
> I40E_PRTMAC_HSEC_CTL_TX_PAUSE_QUANTA(8),
> +			       pf->fc_conf.pause_time);
> +
> +		fctrl_reg = I40E_READ_REG(hw,
> +					  I40E_PRTMAC_HSEC_CTL_RX_FORWARD_CONTROL);
> +
> +		if (fc_conf->mac_ctrl_frame_fwd != 0)
> +			fctrl_reg |= I40E_PRTMAC_FWD_CTRL;
> +		else
> +			fctrl_reg &= ~I40E_PRTMAC_FWD_CTRL;
> +
> +		I40E_WRITE_REG(hw,
> I40E_PRTMAC_HSEC_CTL_RX_FORWARD_CONTROL,
> +			       fctrl_reg);
> +	} else {
> +		/* Configure pause time (2 TCs per register) */
> +		reg = (uint32_t)pf->fc_conf.pause_time * (uint32_t)0x00010001;
> +		for (i = 0; i < I40E_MAX_TRAFFIC_CLASS / 2; i++)
> +			I40E_WRITE_REG(hw, I40E_PRTDCB_FCTTVN(i), reg);
> +
> +		/* Configure flow control refresh threshold value */
> +		I40E_WRITE_REG(hw, I40E_PRTDCB_FCRTV,
> +			       pf->fc_conf.pause_time / 2);
> +
> +		mflcn_reg = I40E_READ_REG(hw, I40E_PRTDCB_MFLCN);
> +
> +		/* set or clear MFLCN.PMCF & MFLCN.DPF bits
> +		 *depending on configuration */
> +		if (fc_conf->mac_ctrl_frame_fwd != 0) {
> +			mflcn_reg |= I40E_PRTDCB_MFLCN_PMCF_MASK;
> +			mflcn_reg &= ~I40E_PRTDCB_MFLCN_DPF_MASK;
> +		} else {
> +			mflcn_reg &= ~I40E_PRTDCB_MFLCN_PMCF_MASK;
> +			mflcn_reg |= I40E_PRTDCB_MFLCN_DPF_MASK;
> +		}
> +
> +		I40E_WRITE_REG(hw, I40E_PRTDCB_MFLCN, mflcn_reg);
> +	}
> +
> +	/* config the water marker both based on the packets and bytes */
> +	I40E_WRITE_REG(hw, I40E_GLRPB_PHW,
> +		       (pf->fc_conf.high_water[I40E_MAX_TRAFFIC_CLASS]
> +		       << I40E_KILOSHIFT) / I40E_PACKET_AVERAGE_SIZE);
> +	I40E_WRITE_REG(hw, I40E_GLRPB_PLW,
> +		       (pf->fc_conf.low_water[I40E_MAX_TRAFFIC_CLASS]
> +		       << I40E_KILOSHIFT) / I40E_PACKET_AVERAGE_SIZE);
> +	I40E_WRITE_REG(hw, I40E_GLRPB_GHW,
> +		       pf->fc_conf.high_water[I40E_MAX_TRAFFIC_CLASS]
> +		       << I40E_KILOSHIFT);
> +	I40E_WRITE_REG(hw, I40E_GLRPB_GLW,
> +		       pf->fc_conf.low_water[I40E_MAX_TRAFFIC_CLASS]
> +		       << I40E_KILOSHIFT);
> +
> +	I40E_WRITE_FLUSH(hw);
> +
> +	return 0;
>  }
> 
>  static int
> diff --git a/drivers/net/i40e/i40e_ethdev.h b/drivers/net/i40e/i40e_ethdev.h
> index 6185657..7f253d3 100644
> --- a/drivers/net/i40e/i40e_ethdev.h
> +++ b/drivers/net/i40e/i40e_ethdev.h
> @@ -282,6 +282,17 @@ struct i40e_pf_vf {  };
> 
>  /*
> + * Structure to store private data for flow control.
> + */
> +struct i40e_fc_conf {
> +	uint16_t pause_time; /* Flow control pause timer */
> +	/* FC high water 0-7 for pfc and 8 for lfc unit:kilobytes */
> +	uint32_t high_water[I40E_MAX_TRAFFIC_CLASS+1];
> +	/* FC low water  0-7 for pfc and 8 for lfc unit:kilobytes */
> +	uint32_t low_water[I40E_MAX_TRAFFIC_CLASS+1];
Would it be clearer to define high/low water for pfc and lfc separately?
Why define them in kilo bytes? Why not in bytes?

Regards,
Helin

> +};
> +
> +/*
>   * Structure to store private data for VMDQ instance
>   */
>  struct i40e_vmdq_info {
> @@ -385,6 +396,7 @@ struct i40e_pf {
>  	struct i40e_vmdq_info *vmdq;
> 
>  	struct i40e_fdir_info fdir; /* flow director info */
> +	struct i40e_fc_conf fc_conf; /* Flow control conf */
>  	struct i40e_mirror_rule_list mirror_list;
>  	uint16_t nb_mirror_rule;   /* The number of mirror rules */
>  };
> --
> 1.9.3



More information about the dev mailing list