Hi Helin, Few nits from me. Konstantin
> -----Original Message----- > From: Zhang, Helin > Sent: Friday, November 28, 2014 12:14 PM > To: dev at dpdk.org > Cc: Cao, Waterman; Cao, Min; Ananyev, Konstantin; Zhang, Helin > Subject: [PATCH v7 3/4] i40e: support of controlling hash functions > > Hash filter control has been implemented for i40e. It includes > getting/setting, > - global hash configurations (hash function type, and symmetric > hash enable per flow type) > - symmetric hash enable per port > > Signed-off-by: Helin Zhang <helin.zhang at intel.com> > --- > lib/librte_ether/rte_eth_ctrl.h | 63 ++++++++ > lib/librte_pmd_i40e/i40e_ethdev.c | 298 > +++++++++++++++++++++++++++++++++++++- > 2 files changed, 359 insertions(+), 2 deletions(-) > > v5 changes: > * Integrated with filter API defined recently. > > v6 changes: > * Implemented the mapping function to convert RSS offload types to > Packet Classification Types, to isolate the real hardware > specific things. > * Removed initialization of global registers in i40e PMD, as global > registers shouldn't be initialized per port. > * Added more annotations to get code more understandable. > * Corrected annotation format for documenation. > > v7 changes: > * Remove swap configurations, as it is not allowed by hardware design. > * Put symmetric hash per flow type and hash function type into > 'RTE_ETH_HASH_FILTER_GLOBAL_CONFIG', as they are controlling global > registers which will affects all the ports of the same NIC. > > diff --git a/lib/librte_ether/rte_eth_ctrl.h b/lib/librte_ether/rte_eth_ctrl.h > index 6ab16c2..827d7ba 100644 > --- a/lib/librte_ether/rte_eth_ctrl.h > +++ b/lib/librte_ether/rte_eth_ctrl.h > @@ -55,6 +55,7 @@ enum rte_filter_type { > RTE_ETH_FILTER_ETHERTYPE, > RTE_ETH_FILTER_TUNNEL, > RTE_ETH_FILTER_FDIR, > + RTE_ETH_FILTER_HASH, > RTE_ETH_FILTER_MAX > }; > > @@ -449,6 +450,68 @@ struct rte_eth_fdir_stats { > uint32_t best_cnt; /**< Number of filters in best effort spaces. */ > }; > > +/** > + * Hash filter information types. > + * - RTE_ETH_HASH_FILTER_SYM_HASH_ENA_PER_PORT is for getting/setting the > + * information/configuration of 'symmetric hash enable' per port. > + * - RTE_ETH_HASH_FILTER_GLOBAL_CONFIG is for getting/setting the global > + * configurations of hash filters. Those global configurations are valid > + * for all ports of the same NIC. > + */ > +enum rte_eth_hash_filter_info_type { > + RTE_ETH_HASH_FILTER_INFO_TYPE_UNKNOWN = 0, > + /** Symmetric hash enable per port */ > + RTE_ETH_HASH_FILTER_SYM_HASH_ENA_PER_PORT, > + /** Configure globally for hash filter */ > + RTE_ETH_HASH_FILTER_GLOBAL_CONFIG, > + RTE_ETH_HASH_FILTER_INFO_TYPE_MAX, > +}; > + > +/** > + * Hash function types. > + */ > +enum rte_eth_hash_function { > + RTE_ETH_HASH_FUNCTION_DEFAULT = 0, > + RTE_ETH_HASH_FUNCTION_TOEPLITZ, /**< Toeplitz */ > + RTE_ETH_HASH_FUNCTION_SIMPLE_XOR, /**< Simple XOR */ > + RTE_ETH_HASH_FUNCTION_MAX, > +}; > + > +#define UINT32_BIT (CHAR_BIT * sizeof(uint32_t)) > +#define RTE_SYM_HASH_MASK_ARRAY_SIZE \ > + (RTE_ALIGN(RTE_ETH_FLOW_TYPE_MAX, UINT32_BIT)/UINT32_BIT) > +/** > + * A structure used to set or get global hash function configurations which > + * include symmetric hash enable per flow type and hash function type. > + * Each bit in sym_hash_enable_mask[] indicates if the symmetric hash of the > + * coresponding flow type is enabled or not. > + * Each bit in valid_bit_mask[] indicates if the coresponding bit in > + * sym_hash_enable_mask[] is valid or not. For the configurations gotten, it > + * also means if the flow type is supported by hardware or not. > + */ > +struct rte_eth_hash_global_conf { > + enum rte_eth_hash_function hash_func; /**< Hash function type */ > + /** Bit mask for symmetric hash enable per flow type */ > + uint32_t sym_hash_enable_mask[RTE_SYM_HASH_MASK_ARRAY_SIZE]; > + /** Bit mask indicates if the coresponding bit is valid */ > + uint32_t valid_bit_mask[RTE_SYM_HASH_MASK_ARRAY_SIZE]; > +}; > + > +/** > + * A structure used to set or get hash filter information, to support filter > + * type of 'RTE_ETH_FILTER_HASH' and its operations. > + */ > +struct rte_eth_hash_filter_info { > + enum rte_eth_hash_filter_info_type info_type; /**< Information type. */ > + /** Details of hash filter infomation */ > + union { > + /* For RTE_ETH_HASH_FILTER_SYM_HASH_ENA_PER_PORT */ > + uint8_t enable; > + /* Global configurations of hash filter */ > + struct rte_eth_hash_global_conf global_conf; > + } info; > +}; > + > #ifdef __cplusplus > } > #endif > diff --git a/lib/librte_pmd_i40e/i40e_ethdev.c > b/lib/librte_pmd_i40e/i40e_ethdev.c > index 8fbe25f..ef8edd4 100644 > --- a/lib/librte_pmd_i40e/i40e_ethdev.c > +++ b/lib/librte_pmd_i40e/i40e_ethdev.c > @@ -93,6 +93,18 @@ > I40E_PFINT_ICR0_ENA_VFLR_MASK | \ > I40E_PFINT_ICR0_ENA_ADMINQ_MASK) > > +#define I40E_FLOW_TYPES ( \ > + (1UL << RTE_ETH_FLOW_TYPE_UDPV4) | \ > + (1UL << RTE_ETH_FLOW_TYPE_TCPV4) | \ > + (1UL << RTE_ETH_FLOW_TYPE_SCTPV4) | \ > + (1UL << RTE_ETH_FLOW_TYPE_IPV4_OTHER) | \ > + (1UL << RTE_ETH_FLOW_TYPE_FRAG_IPV4) | \ > + (1UL << RTE_ETH_FLOW_TYPE_UDPV6) | \ > + (1UL << RTE_ETH_FLOW_TYPE_TCPV6) | \ > + (1UL << RTE_ETH_FLOW_TYPE_SCTPV6) | \ > + (1UL << RTE_ETH_FLOW_TYPE_IPV6_OTHER) | \ > + (1UL << RTE_ETH_FLOW_TYPE_FRAG_IPV6)) > + > static int eth_i40e_dev_init(\ > __attribute__((unused)) struct eth_driver *eth_drv, > struct rte_eth_dev *eth_dev); > @@ -198,6 +210,7 @@ static int i40e_dev_filter_ctrl(struct rte_eth_dev *dev, > enum rte_filter_type filter_type, > enum rte_filter_op filter_op, > void *arg); > +static void i40e_hw_init(struct i40e_hw *hw); > > static struct rte_pci_id pci_id_i40e_map[] = { > #define RTE_PCI_DEV_ID_DECL_I40E(vend, dev) {RTE_PCI_DEVICE(vend, dev)}, > @@ -397,6 +410,9 @@ eth_i40e_dev_init(__rte_unused struct eth_driver *eth_drv, > /* Make sure all is clean before doing PF reset */ > i40e_clear_hw(hw); > > + /* Initialize the hardware */ > + i40e_hw_init(hw); > + > /* Reset here to make sure all is clean for each PF */ > ret = i40e_pf_reset(hw); > if (ret) { > @@ -5120,6 +5136,264 @@ i40e_pf_config_mq_rx(struct i40e_pf *pf) > return ret; > } > > +/* Get the symmetric hash enable configurations per port */ > +static void > +i40e_get_symmetric_hash_enable_per_port(struct i40e_hw *hw, uint8_t *enable) > +{ > + uint32_t reg = I40E_READ_REG(hw, I40E_PRTQF_CTL_0); > + > + *enable = reg & I40E_PRTQF_CTL_0_HSYM_ENA_MASK ? 1 : 0; > +} > + > +/* Set the symmetric hash enable configurations per port */ > +static void > +i40e_set_symmetric_hash_enable_per_port(struct i40e_hw *hw, uint8_t enable) > +{ > + uint32_t reg = I40E_READ_REG(hw, I40E_PRTQF_CTL_0); > + > + if (enable > 0) { > + if (reg & I40E_PRTQF_CTL_0_HSYM_ENA_MASK) { > + PMD_DRV_LOG(INFO, "Symmetric hash has already " > + "been enabled"); > + return; > + } > + reg |= I40E_PRTQF_CTL_0_HSYM_ENA_MASK; > + } else { > + if (!(reg & I40E_PRTQF_CTL_0_HSYM_ENA_MASK)) { > + PMD_DRV_LOG(INFO, "Symmetric hash has already " > + "been disabled"); > + return; > + } > + reg &= ~I40E_PRTQF_CTL_0_HSYM_ENA_MASK; > + } > + I40E_WRITE_REG(hw, I40E_PRTQF_CTL_0, reg); > + I40E_WRITE_FLUSH(hw); > +} > + > +/* > + * Get global configurations of hash function type and symmetric hash enable > + * per flow type (pctype). Note that global configuration means it affects > all > + * the ports on the same NIC. > + */ > +static int > +i40e_get_hash_filter_global_config(struct i40e_hw *hw, > + struct rte_eth_hash_global_conf *g_cfg) > +{ > + uint32_t reg, mask = I40E_FLOW_TYPES; > + enum rte_eth_flow_type i; > + enum i40e_filter_pctype pctype; > + > + memset(g_cfg, 0, sizeof(*g_cfg)); > + reg = I40E_READ_REG(hw, I40E_GLQF_CTL); > + if (reg & I40E_GLQF_CTL_HTOEP_MASK) > + g_cfg->hash_func = RTE_ETH_HASH_FUNCTION_TOEPLITZ; > + else > + g_cfg->hash_func = RTE_ETH_HASH_FUNCTION_SIMPLE_XOR; > + PMD_DRV_LOG(DEBUG, "Hash function is %s", > + (reg & I40E_GLQF_CTL_HTOEP_MASK) ? "Toeplitz" : "Simple XOR"); > + > + for (i = 0; mask && i < RTE_ETH_FLOW_TYPE_MAX; i++) { > + if (!(mask & (1UL << i))) > + continue; > + mask &= ~(1UL << i); > + /* Bit set indicats the coresponding flow type is supported */ > + g_cfg->valid_bit_mask[0] |= (1UL << i); > + pctype = i40e_flowtype_to_pctype(i); > + if (!pctype) > + continue; As I said in offline discussion, we don't need these 2 lines above: If i40e supports that flow type (bit in a mask is set), then we surely should be able to convert it to pctype. > + reg = I40E_READ_REG(hw, I40E_GLQF_HSYM(pctype)); > + if (reg & I40E_GLQF_HSYM_SYMH_ENA_MASK) > + g_cfg->sym_hash_enable_mask[0] |= (1UL << i); > + } > + > + return 0; > +} > + > +static int > +i40e_hash_global_config_check(struct rte_eth_hash_global_conf *g_cfg) > +{ > + uint32_t i; > + uint32_t mask0, i40e_mask = I40E_FLOW_TYPES; > + > + if (g_cfg->hash_func != RTE_ETH_HASH_FUNCTION_TOEPLITZ && > + g_cfg->hash_func != RTE_ETH_HASH_FUNCTION_SIMPLE_XOR && > + g_cfg->hash_func != RTE_ETH_HASH_FUNCTION_DEFAULT) { > + PMD_DRV_LOG(ERR, "Unsupported hash function type %d", > + g_cfg->hash_func); > + return -EINVAL; > + } > + > + /* > + * As i40e supports less than 32 flow types, only first 32 bits need to > + * be checked. > + */ > + mask0 = g_cfg->valid_bit_mask[0]; > + for (i = 0; i < RTE_SYM_HASH_MASK_ARRAY_SIZE; i++) { > + if (i == 0) { > + /* Check if any unsupported flow type configured */ > + if ((mask0 | i40e_mask) ^ i40e_mask) > + goto mask_err; > + } else { > + if (g_cfg->valid_bit_mask[i]) > + goto mask_err; > + } > + } > + > + return 0; > + > +mask_err: > + PMD_DRV_LOG(ERR, "i40e unsupported flow type bit(s) configured"); > + > + return -EINVAL; > +} > + > +/* > + * Set global configurations of hash function type and symmetric hash enable > + * per flow type (pctype). Note any modifying global configuration will > affect > + * all the ports on the same NIC. > + */ > +static int > +i40e_set_hash_filter_global_config(struct i40e_hw *hw, > + struct rte_eth_hash_global_conf *g_cfg) > +{ > + int ret; > + uint32_t i, reg; > + uint32_t mask0 = g_cfg->valid_bit_mask[0]; > + enum i40e_filter_pctype pctype; > + > + /* Check the input parameters */ > + ret = i40e_hash_global_config_check(g_cfg); > + if (ret < 0) > + return ret; > + > + for (i = 0; mask0 && i < UINT32_BIT; i++) { > + if (!(mask0 & (1UL << i))) > + continue; > + mask0 &= ~(1UL << i); > + pctype = i40e_flowtype_to_pctype(i); > + if (!pctype) > + continue; As I said in offline discussion, we don't need these 2 lines above: If i40e supports that flow type (bit in a mask is set), then we surely should be able to convert it to pctype. > + reg = (g_cfg->sym_hash_enable_mask[0] & (1UL << i)) ? > + I40E_GLQF_HSYM_SYMH_ENA_MASK : 0; > + I40E_WRITE_REG(hw, I40E_GLQF_HSYM(pctype), reg); > + } > + > + reg = I40E_READ_REG(hw, I40E_GLQF_CTL); > + if (g_cfg->hash_func == RTE_ETH_HASH_FUNCTION_TOEPLITZ) { > + /* Toeplitz */ > + if (reg & I40E_GLQF_CTL_HTOEP_MASK) { > + PMD_DRV_LOG(DEBUG, "Hash function already set to " > + "Toeplitz"); > + goto out; > + } > + reg |= I40E_GLQF_CTL_HTOEP_MASK; > + } else if (g_cfg->hash_func == RTE_ETH_HASH_FUNCTION_SIMPLE_XOR) { > + /* Simple XOR */ > + if (!(reg & I40E_GLQF_CTL_HTOEP_MASK)) { > + PMD_DRV_LOG(DEBUG, "Hash function already set to " > + "Simple XOR"); > + goto out; > + } > + reg &= ~I40E_GLQF_CTL_HTOEP_MASK; > + } else > + /* Use the default, and keep it as it is */ > + goto out; > + > + I40E_WRITE_REG(hw, I40E_GLQF_CTL, reg); > + > +out: > + I40E_WRITE_FLUSH(hw); > + > + return 0; > +} > + > +static int > +i40e_hash_filter_get(struct i40e_hw *hw, struct rte_eth_hash_filter_info > *info) > +{ > + int ret = 0; > + > + if (!hw || !info) { > + PMD_DRV_LOG(ERR, "Invalid pointer"); > + return -EFAULT; > + } > + > + switch (info->info_type) { > + case RTE_ETH_HASH_FILTER_SYM_HASH_ENA_PER_PORT: > + i40e_get_symmetric_hash_enable_per_port(hw, > + &(info->info.enable)); > + break; > + case RTE_ETH_HASH_FILTER_GLOBAL_CONFIG: > + ret = i40e_get_hash_filter_global_config(hw, > + &(info->info.global_conf)); > + break; > + default: > + PMD_DRV_LOG(ERR, "Hash filter info type (%d) not supported", > + info->info_type); > + ret = -EINVAL; > + break; > + } > + > + return ret; > +} > + > +static int > +i40e_hash_filter_set(struct i40e_hw *hw, struct rte_eth_hash_filter_info > *info) > +{ > + int ret = 0; > + > + if (!hw || !info) { > + PMD_DRV_LOG(ERR, "Invalid pointer"); > + return -EFAULT; > + } > + > + switch (info->info_type) { > + case RTE_ETH_HASH_FILTER_SYM_HASH_ENA_PER_PORT: > + i40e_set_symmetric_hash_enable_per_port(hw, info->info.enable); > + break; > + case RTE_ETH_HASH_FILTER_GLOBAL_CONFIG: > + ret = i40e_set_hash_filter_global_config(hw, > + &(info->info.global_conf)); > + break; > + default: > + PMD_DRV_LOG(ERR, "Hash filter info type (%d) not supported", > + info->info_type); > + ret = -EINVAL; > + break; > + } > + > + return ret; > +} > + > +/* Operations for hash function */ > +static int > +i40e_hash_filter_ctrl(struct rte_eth_dev *dev, > + enum rte_filter_op filter_op, > + void *arg) > +{ > + struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private); > + int ret = 0; > + > + switch (filter_op) { > + case RTE_ETH_FILTER_NOP: > + break; > + case RTE_ETH_FILTER_GET: > + ret = i40e_hash_filter_get(hw, > + (struct rte_eth_hash_filter_info *)arg); > + break; > + case RTE_ETH_FILTER_SET: > + ret = i40e_hash_filter_set(hw, > + (struct rte_eth_hash_filter_info *)arg); > + break; > + default: > + PMD_DRV_LOG(WARNING, "Filter operation (%d) not supported", > + filter_op); > + ret = -ENOTSUP; > + break; > + } > + > + return ret; > +} > + > /* > * Configure ethertype filter, which can director packet by filtering > * with mac address and ether_type or only ether_type > @@ -5222,6 +5496,9 @@ i40e_dev_filter_ctrl(struct rte_eth_dev *dev, > return -EINVAL; > > switch (filter_type) { > + case RTE_ETH_FILTER_HASH: > + ret = i40e_hash_filter_ctrl(dev, filter_op, arg); > + break; > case RTE_ETH_FILTER_MACVLAN: > ret = i40e_mac_filter_handle(dev, filter_op, arg); > break; > @@ -5244,10 +5521,26 @@ i40e_dev_filter_ctrl(struct rte_eth_dev *dev, > return ret; > } > > +/* > + * As some registers wouldn't be reset unless a global hardware reset, > + * hardware initialization is needed to put those registers into an > + * expected initial state. > + */ > +static void > +i40e_hw_init(struct i40e_hw *hw) > +{ > + /* clear the PF Queue Filter control register */ > + I40E_WRITE_REG(hw, I40E_PFQF_CTL_0, 0); > + > + /* Disable symmetric hash per port */ > + i40e_set_symmetric_hash_enable_per_port(hw, 0); > +} > + > enum i40e_filter_pctype > i40e_flowtype_to_pctype(enum rte_eth_flow_type flow_type) > { > - static const enum i40e_filter_pctype pctype_table[] = { > + static const enum i40e_filter_pctype > + pctype_table[RTE_ETH_FLOW_TYPE_MAX] = { > [RTE_ETH_FLOW_TYPE_UDPV4] = I40E_FILTER_PCTYPE_NONF_IPV4_UDP, > [RTE_ETH_FLOW_TYPE_TCPV4] = I40E_FILTER_PCTYPE_NONF_IPV4_TCP, > [RTE_ETH_FLOW_TYPE_SCTPV4] = I40E_FILTER_PCTYPE_NONF_IPV4_SCTP, > @@ -5270,7 +5563,8 @@ i40e_flowtype_to_pctype(enum rte_eth_flow_type > flow_type) > enum rte_eth_flow_type > i40e_pctype_to_flowtype(enum i40e_filter_pctype pctype) > { > - static const enum rte_eth_flow_type flowtype_table[] = { > + static const enum rte_eth_flow_type > + flowtype_table[RTE_ETH_FLOW_TYPE_MAX] = { > [I40E_FILTER_PCTYPE_NONF_IPV4_UDP] = RTE_ETH_FLOW_TYPE_UDPV4, > [I40E_FILTER_PCTYPE_NONF_IPV4_TCP] = RTE_ETH_FLOW_TYPE_TCPV4, > [I40E_FILTER_PCTYPE_NONF_IPV4_SCTP] = RTE_ETH_FLOW_TYPE_SCTPV4, > -- > 1.8.1.4