From db5b65301ddee88d2e093e45ed90511c96ffe3a5 Mon Sep 17 00:00:00 2001 From: Ivan Boule Date: Fri, 16 May 2014 10:58:40 +0200 Subject: [PATCH] ethdev: allow to set RSS hash computation flags and/or key 1) Add a new function "rss_hash_update" in the PMD API to dynamically update the RSS flags and/or the RSS key used by a NIC to compute the RSS hash of input packets. The new function uses the existing data structure "rte_eth_rss_conf" for the argument that contains the new hash flags and/or the new hash key to use. 2) Add the ixgbe-specific function "ixgbe_dev_rss_hash_update" and the igb-specific function "eth_igb_rss_hash_update" to update the RSS hash configuration of ixgbe and igb controllers respectively. Before changing anything, these 2 functions check that the update RSS operation does not attempt to disable RSS, if RSS was enabled at port initialization time, or does not attempt to enable RSS, if RSS was disabled at port initialization time. Note: Configuring the RSS hash flags and the RSS key used by a NIC consists in updating appropriate PCI registers of the NIC. These operations have been manually tested with the interactive commands "write reg" and "write regbit" of the testpmd application. Signed-off-by: Ivan Boule Acked-by: Thomas Monjalon --- lib/librte_ether/rte_ethdev.c | 22 +++++ lib/librte_ether/rte_ethdev.h | 24 ++++++ lib/librte_pmd_e1000/e1000_ethdev.h | 3 + lib/librte_pmd_e1000/igb_ethdev.c | 1 + lib/librte_pmd_e1000/igb_rxtx.c | 123 ++++++++++++++++++--------- lib/librte_pmd_ixgbe/ixgbe_ethdev.c | 1 + lib/librte_pmd_ixgbe/ixgbe_ethdev.h | 3 + lib/librte_pmd_ixgbe/ixgbe_rxtx.c | 126 ++++++++++++++++++++-------- 8 files changed, 228 insertions(+), 75 deletions(-) diff --git a/lib/librte_ether/rte_ethdev.c b/lib/librte_ether/rte_ethdev.c index 473c98b1bf..91375a12e7 100644 --- a/lib/librte_ether/rte_ethdev.c +++ b/lib/librte_ether/rte_ethdev.c @@ -1571,6 +1571,28 @@ rte_eth_dev_rss_reta_query(uint8_t port_id, struct rte_eth_rss_reta *reta_conf) return (*dev->dev_ops->reta_query)(dev, reta_conf); } +int +rte_eth_dev_rss_hash_update(uint8_t port_id, struct rte_eth_rss_conf *rss_conf) +{ + struct rte_eth_dev *dev; + uint16_t rss_hash_protos; + + if (port_id >= nb_ports) { + PMD_DEBUG_TRACE("Invalid port_id=%d\n", port_id); + return (-ENODEV); + } + rss_hash_protos = rss_conf->rss_hf; + if ((rss_hash_protos != 0) && + ((rss_hash_protos & ETH_RSS_PROTO_MASK) == 0)) { + PMD_DEBUG_TRACE("Invalid rss_hash_protos=0x%x\n", + rss_hash_protos); + return (-EINVAL); + } + dev = &rte_eth_devices[port_id]; + FUNC_PTR_OR_ERR_RET(*dev->dev_ops->rss_hash_update, -ENOTSUP); + return (*dev->dev_ops->rss_hash_update)(dev, rss_conf); +} + int rte_eth_led_on(uint8_t port_id) { diff --git a/lib/librte_ether/rte_ethdev.h b/lib/librte_ether/rte_ethdev.h index d5ea46b9ff..e4ad9a7ad9 100644 --- a/lib/librte_ether/rte_ethdev.h +++ b/lib/librte_ether/rte_ethdev.h @@ -331,6 +331,8 @@ struct rte_eth_rss_conf { #define ETH_RSS_IPV4_UDP 0x0040 /**< IPv4/UDP packet. */ #define ETH_RSS_IPV6_UDP 0x0080 /**< IPv6/UDP packet. */ #define ETH_RSS_IPV6_UDP_EX 0x0100 /**< IPv6/UDP with extension headers. */ + +#define ETH_RSS_PROTO_MASK 0x01FF /**< Mask of valid RSS hash protocols */ /* Definitions used for redirection table entry size */ #define ETH_RSS_RETA_NUM_ENTRIES 128 #define ETH_RSS_RETA_MAX_QUEUE 16 @@ -966,6 +968,10 @@ typedef int (*reta_query_t)(struct rte_eth_dev *dev, struct rte_eth_rss_reta *reta_conf); /**< @internal Query RSS redirection table on an Ethernet device */ +typedef int (*rss_hash_update_t)(struct rte_eth_dev *dev, + struct rte_eth_rss_conf *rss_conf); +/**< @internal Update RSS hash configuration of an Ethernet device */ + typedef int (*eth_dev_led_on_t)(struct rte_eth_dev *dev); /**< @internal Turn on SW controllable LED on an Ethernet device */ @@ -1153,6 +1159,8 @@ struct eth_dev_ops { bypass_wd_reset_t bypass_wd_reset; #endif + /** Configure RSS hash protocols. */ + rss_hash_update_t rss_hash_update; }; /** @@ -2725,6 +2733,22 @@ int rte_eth_dev_bypass_wd_timeout_show(uint8_t port, uint32_t *wd_timeout); */ int rte_eth_dev_bypass_wd_reset(uint8_t port); + /** + * Configuration of Receive Side Scaling hash computation of Ethernet device. + * + * @param port + * The port identifier of the Ethernet device. + * @param rss_conf + * The new configuration to use for RSS hash computation on the port. + * @return + * - (0) if successful. + * - (-ENODEV) if port identifier is invalid. + * - (-ENOTSUP) if hardware doesn't support. + * - (-EINVAL) if bad parameter. + */ +int rte_eth_dev_rss_hash_update(uint8_t port_id, + struct rte_eth_rss_conf *rss_conf); + #ifdef __cplusplus } #endif diff --git a/lib/librte_pmd_e1000/e1000_ethdev.h b/lib/librte_pmd_e1000/e1000_ethdev.h index d09064ef50..d9dc8d1eca 100644 --- a/lib/librte_pmd_e1000/e1000_ethdev.h +++ b/lib/librte_pmd_e1000/e1000_ethdev.h @@ -138,6 +138,9 @@ uint16_t eth_igb_recv_pkts(void *rxq, struct rte_mbuf **rx_pkts, uint16_t eth_igb_recv_scattered_pkts(void *rxq, struct rte_mbuf **rx_pkts, uint16_t nb_pkts); +int eth_igb_rss_hash_update(struct rte_eth_dev *dev, + struct rte_eth_rss_conf *rss_conf); + int eth_igbvf_rx_init(struct rte_eth_dev *dev); void eth_igbvf_tx_init(struct rte_eth_dev *dev); diff --git a/lib/librte_pmd_e1000/igb_ethdev.c b/lib/librte_pmd_e1000/igb_ethdev.c index 044eac39aa..92794c87f1 100644 --- a/lib/librte_pmd_e1000/igb_ethdev.c +++ b/lib/librte_pmd_e1000/igb_ethdev.c @@ -194,6 +194,7 @@ static struct eth_dev_ops eth_igb_ops = { .mac_addr_remove = eth_igb_rar_clear, .reta_update = eth_igb_rss_reta_update, .reta_query = eth_igb_rss_reta_query, + .rss_hash_update = eth_igb_rss_hash_update, }; /* diff --git a/lib/librte_pmd_e1000/igb_rxtx.c b/lib/librte_pmd_e1000/igb_rxtx.c index 7fe1780a9a..963770408b 100644 --- a/lib/librte_pmd_e1000/igb_rxtx.c +++ b/lib/librte_pmd_e1000/igb_rxtx.c @@ -1519,53 +1519,28 @@ igb_rss_disable(struct rte_eth_dev *dev) } static void -igb_rss_configure(struct rte_eth_dev *dev) +igb_hw_rss_hash_set(struct e1000_hw *hw, struct rte_eth_rss_conf *rss_conf) { - struct e1000_hw *hw; - uint8_t *hash_key; + uint8_t *hash_key; uint32_t rss_key; uint32_t mrqc; - uint32_t shift; uint16_t rss_hf; uint16_t i; - hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private); - - rss_hf = dev->data->dev_conf.rx_adv_conf.rss_conf.rss_hf; - if (rss_hf == 0) /* Disable RSS. */ { - igb_rss_disable(dev); - return; - } - hash_key = dev->data->dev_conf.rx_adv_conf.rss_conf.rss_key; - if (hash_key == NULL) - hash_key = rss_intel_key; /* Default hash key. */ - - /* Fill in RSS hash key. */ - for (i = 0; i < 10; i++) { - rss_key = hash_key[(i * 4)]; - rss_key |= hash_key[(i * 4) + 1] << 8; - rss_key |= hash_key[(i * 4) + 2] << 16; - rss_key |= hash_key[(i * 4) + 3] << 24; - E1000_WRITE_REG_ARRAY(hw, E1000_RSSRK(0), i, rss_key); - } - - /* Fill in redirection table. */ - shift = (hw->mac.type == e1000_82575) ? 6 : 0; - for (i = 0; i < 128; i++) { - union e1000_reta { - uint32_t dword; - uint8_t bytes[4]; - } reta; - uint8_t q_idx; - - q_idx = (uint8_t) ((dev->data->nb_rx_queues > 1) ? - i % dev->data->nb_rx_queues : 0); - reta.bytes[i & 3] = (uint8_t) (q_idx << shift); - if ((i & 3) == 3) - E1000_WRITE_REG(hw, E1000_RETA(i >> 2), reta.dword); + hash_key = rss_conf->rss_key; + if (hash_key != NULL) { + /* Fill in RSS hash key */ + for (i = 0; i < 10; i++) { + rss_key = hash_key[(i * 4)]; + rss_key |= hash_key[(i * 4) + 1] << 8; + rss_key |= hash_key[(i * 4) + 2] << 16; + rss_key |= hash_key[(i * 4) + 3] << 24; + E1000_WRITE_REG_ARRAY(hw, E1000_RSSRK(0), i, rss_key); + } } - /* Set configured hashing functions in MRQC register. */ + /* Set configured hashing protocols in MRQC register */ + rss_hf = rss_conf->rss_hf; mrqc = E1000_MRQC_ENABLE_RSS_4Q; /* RSS enabled. */ if (rss_hf & ETH_RSS_IPV4) mrqc |= E1000_MRQC_RSS_FIELD_IPV4; @@ -1588,6 +1563,76 @@ igb_rss_configure(struct rte_eth_dev *dev) E1000_WRITE_REG(hw, E1000_MRQC, mrqc); } +int +eth_igb_rss_hash_update(struct rte_eth_dev *dev, + struct rte_eth_rss_conf *rss_conf) +{ + struct e1000_hw *hw; + uint32_t mrqc; + uint16_t rss_hf; + + hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private); + + /* + * Before changing anything, first check that the update RSS operation + * does not attempt to disable RSS, if RSS was enabled at + * initialization time, or does not attempt to enable RSS, if RSS was + * disabled at initialization time. + */ + rss_hf = rss_conf->rss_hf; + mrqc = E1000_READ_REG(hw, E1000_MRQC); + if (!(mrqc & E1000_MRQC_ENABLE_MASK)) { /* RSS disabled */ + if (rss_hf != 0) /* Enable RSS */ + return -(EINVAL); + return 0; /* Nothing to do */ + } + /* RSS enabled */ + if (rss_hf == 0) /* Disable RSS */ + return -(EINVAL); + igb_hw_rss_hash_set(hw, rss_conf); + return 0; +} + +static void +igb_rss_configure(struct rte_eth_dev *dev) +{ + struct rte_eth_rss_conf rss_conf; + struct e1000_hw *hw; + uint32_t shift; + uint16_t i; + + hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private); + + /* Fill in redirection table. */ + shift = (hw->mac.type == e1000_82575) ? 6 : 0; + for (i = 0; i < 128; i++) { + union e1000_reta { + uint32_t dword; + uint8_t bytes[4]; + } reta; + uint8_t q_idx; + + q_idx = (uint8_t) ((dev->data->nb_rx_queues > 1) ? + i % dev->data->nb_rx_queues : 0); + reta.bytes[i & 3] = (uint8_t) (q_idx << shift); + if ((i & 3) == 3) + E1000_WRITE_REG(hw, E1000_RETA(i >> 2), reta.dword); + } + + /* + * Configure the RSS key and the RSS protocols used to compute + * the RSS hash of input packets. + */ + rss_conf = dev->data->dev_conf.rx_adv_conf.rss_conf; + if (rss_conf.rss_hf == 0) { + igb_rss_disable(dev); + return; + } + if (rss_conf.rss_key == NULL) + rss_conf.rss_key = rss_intel_key; /* Default hash key */ + igb_hw_rss_hash_set(hw, &rss_conf); +} + /* * Check if the mac type support VMDq or not. * Return 1 if it supports, otherwise, return 0. diff --git a/lib/librte_pmd_ixgbe/ixgbe_ethdev.c b/lib/librte_pmd_ixgbe/ixgbe_ethdev.c index 113271e63c..0b81f2b6de 100644 --- a/lib/librte_pmd_ixgbe/ixgbe_ethdev.c +++ b/lib/librte_pmd_ixgbe/ixgbe_ethdev.c @@ -307,6 +307,7 @@ static struct eth_dev_ops ixgbe_eth_dev_ops = { .bypass_ver_show = ixgbe_bypass_ver_show, .bypass_wd_reset = ixgbe_bypass_wd_reset, #endif /* RTE_NIC_BYPASS */ + .rss_hash_update = ixgbe_dev_rss_hash_update, }; /* diff --git a/lib/librte_pmd_ixgbe/ixgbe_ethdev.h b/lib/librte_pmd_ixgbe/ixgbe_ethdev.h index 9d7e93f510..06d51ab01f 100644 --- a/lib/librte_pmd_ixgbe/ixgbe_ethdev.h +++ b/lib/librte_pmd_ixgbe/ixgbe_ethdev.h @@ -235,6 +235,9 @@ uint16_t ixgbe_xmit_pkts(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t ixgbe_xmit_pkts_simple(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkts); +int ixgbe_dev_rss_hash_update(struct rte_eth_dev *dev, + struct rte_eth_rss_conf *rss_conf); + /* * Flow director function prototypes */ diff --git a/lib/librte_pmd_ixgbe/ixgbe_rxtx.c b/lib/librte_pmd_ixgbe/ixgbe_rxtx.c index 303144b451..111d1fa21d 100644 --- a/lib/librte_pmd_ixgbe/ixgbe_rxtx.c +++ b/lib/librte_pmd_ixgbe/ixgbe_rxtx.c @@ -2292,49 +2292,29 @@ ixgbe_rss_disable(struct rte_eth_dev *dev) } static void -ixgbe_rss_configure(struct rte_eth_dev *dev) +ixgbe_hw_rss_hash_set(struct ixgbe_hw *hw, struct rte_eth_rss_conf *rss_conf) { - struct ixgbe_hw *hw; - uint8_t *hash_key; - uint32_t rss_key; + uint8_t *hash_key; uint32_t mrqc; - uint32_t reta; + uint32_t rss_key; uint16_t rss_hf; uint16_t i; - uint16_t j; - - PMD_INIT_FUNC_TRACE(); - hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private); - - rss_hf = dev->data->dev_conf.rx_adv_conf.rss_conf.rss_hf; - if (rss_hf == 0) { /* Disable RSS */ - ixgbe_rss_disable(dev); - return; - } - hash_key = dev->data->dev_conf.rx_adv_conf.rss_conf.rss_key; - if (hash_key == NULL) - hash_key = rss_intel_key; /* Default hash key */ - - /* Fill in RSS hash key */ - for (i = 0; i < 10; i++) { - rss_key = hash_key[(i * 4)]; - rss_key |= hash_key[(i * 4) + 1] << 8; - rss_key |= hash_key[(i * 4) + 2] << 16; - rss_key |= hash_key[(i * 4) + 3] << 24; - IXGBE_WRITE_REG_ARRAY(hw, IXGBE_RSSRK(0), i, rss_key); - } - /* Fill in redirection table */ - reta = 0; - for (i = 0, j = 0; i < 128; i++, j++) { - if (j == dev->data->nb_rx_queues) j = 0; - reta = (reta << 8) | j; - if ((i & 3) == 3) - IXGBE_WRITE_REG(hw, IXGBE_RETA(i >> 2), rte_bswap32(reta)); + hash_key = rss_conf->rss_key; + if (hash_key != NULL) { + /* Fill in RSS hash key */ + for (i = 0; i < 10; i++) { + rss_key = hash_key[(i * 4)]; + rss_key |= hash_key[(i * 4) + 1] << 8; + rss_key |= hash_key[(i * 4) + 2] << 16; + rss_key |= hash_key[(i * 4) + 3] << 24; + IXGBE_WRITE_REG_ARRAY(hw, IXGBE_RSSRK(0), i, rss_key); + } } - /* Set configured hashing functions in MRQC register */ - mrqc = IXGBE_MRQC_RSSEN; /* RSS enable */ + /* Set configured hashing protocols in MRQC register */ + rss_hf = rss_conf->rss_hf; + mrqc = IXGBE_MRQC_RSSEN; /* Enable RSS */ if (rss_hf & ETH_RSS_IPV4) mrqc |= IXGBE_MRQC_RSS_FIELD_IPV4; if (rss_hf & ETH_RSS_IPV4_TCP) @@ -2356,6 +2336,80 @@ ixgbe_rss_configure(struct rte_eth_dev *dev) IXGBE_WRITE_REG(hw, IXGBE_MRQC, mrqc); } +int +ixgbe_dev_rss_hash_update(struct rte_eth_dev *dev, + struct rte_eth_rss_conf *rss_conf) +{ + struct ixgbe_hw *hw; + uint32_t mrqc; + uint16_t rss_hf; + + hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private); + + /* + * Excerpt from section 7.1.2.8 Receive-Side Scaling (RSS): + * "RSS enabling cannot be done dynamically while it must be + * preceded by a software reset" + * Before changing anything, first check that the update RSS operation + * does not attempt to disable RSS, if RSS was enabled at + * initialization time, or does not attempt to enable RSS, if RSS was + * disabled at initialization time. + */ + rss_hf = rss_conf->rss_hf; + mrqc = IXGBE_READ_REG(hw, IXGBE_MRQC); + if (!(mrqc & IXGBE_MRQC_RSSEN)) { /* RSS disabled */ + if (rss_hf != 0) /* Enable RSS */ + return -(EINVAL); + return 0; /* Nothing to do */ + } + /* RSS enabled */ + if (rss_hf == 0) /* Disable RSS */ + return -(EINVAL); + ixgbe_hw_rss_hash_set(hw, rss_conf); + return 0; +} + +static void +ixgbe_rss_configure(struct rte_eth_dev *dev) +{ + struct rte_eth_rss_conf rss_conf; + struct ixgbe_hw *hw; + uint32_t reta; + uint16_t i; + uint16_t j; + + PMD_INIT_FUNC_TRACE(); + hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private); + + /* + * Fill in redirection table + * The byte-swap is needed because NIC registers are in + * little-endian order. + */ + reta = 0; + for (i = 0, j = 0; i < 128; i++, j++) { + if (j == dev->data->nb_rx_queues) + j = 0; + reta = (reta << 8) | j; + if ((i & 3) == 3) + IXGBE_WRITE_REG(hw, IXGBE_RETA(i >> 2), + rte_bswap32(reta)); + } + + /* + * Configure the RSS key and the RSS protocols used to compute + * the RSS hash of input packets. + */ + rss_conf = dev->data->dev_conf.rx_adv_conf.rss_conf; + if (rss_conf.rss_hf == 0) { + ixgbe_rss_disable(dev); + return; + } + if (rss_conf.rss_key == NULL) + rss_conf.rss_key = rss_intel_key; /* Default hash key */ + ixgbe_hw_rss_hash_set(hw, &rss_conf); +} + #define NUM_VFTA_REGISTERS 128 #define NIC_RX_BUFFER_SIZE 0x200 -- 2.20.1