numam-dpdk/drivers/net/ixgbe/ixgbe_fdir.c
Jingjing Wu 49b03b18b3 ixgbe: fix flow director flexbytes offset
The flexbytes offset can not be set, because the value is over written
when fdir is enabled.
This patch fixes this issue, and also removes some reduplicate lines.

Fixes: d54a988826 ("ixgbe: support flexpayload configuration of flow director")

Reported-by: David Marchand <david.marchand@6wind.com>
Signed-off-by: Jingjing Wu <jingjing.wu@intel.com>
Tested-by: Gaetan Rivet <gaetan.rivet@6wind.com>
Acked-by: David Marchand <david.marchand@6wind.com>
2015-06-22 11:56:24 +02:00

1159 lines
37 KiB
C

/*-
* BSD LICENSE
*
* Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdarg.h>
#include <errno.h>
#include <sys/queue.h>
#include <rte_interrupts.h>
#include <rte_log.h>
#include <rte_debug.h>
#include <rte_pci.h>
#include <rte_ether.h>
#include <rte_ethdev.h>
#include "ixgbe_logs.h"
#include "base/ixgbe_api.h"
#include "base/ixgbe_common.h"
#include "ixgbe_ethdev.h"
/* To get PBALLOC (Packet Buffer Allocation) bits from FDIRCTRL value */
#define FDIRCTRL_PBALLOC_MASK 0x03
/* For calculating memory required for FDIR filters */
#define PBALLOC_SIZE_SHIFT 15
/* Number of bits used to mask bucket hash for different pballoc sizes */
#define PERFECT_BUCKET_64KB_HASH_MASK 0x07FF /* 11 bits */
#define PERFECT_BUCKET_128KB_HASH_MASK 0x0FFF /* 12 bits */
#define PERFECT_BUCKET_256KB_HASH_MASK 0x1FFF /* 13 bits */
#define SIG_BUCKET_64KB_HASH_MASK 0x1FFF /* 13 bits */
#define SIG_BUCKET_128KB_HASH_MASK 0x3FFF /* 14 bits */
#define SIG_BUCKET_256KB_HASH_MASK 0x7FFF /* 15 bits */
#define IXGBE_DEFAULT_FLEXBYTES_OFFSET 12 /* default flexbytes offset in bytes */
#define IXGBE_FDIR_MAX_FLEX_LEN 2 /* len in bytes of flexbytes */
#define IXGBE_MAX_FLX_SOURCE_OFF 62
#define IXGBE_FDIRCTRL_FLEX_MASK (0x1F << IXGBE_FDIRCTRL_FLEX_SHIFT)
#define IXGBE_FDIRCMD_CMD_INTERVAL_US 10
#define IXGBE_FDIR_FLOW_TYPES ( \
(1 << RTE_ETH_FLOW_NONFRAG_IPV4_UDP) | \
(1 << RTE_ETH_FLOW_NONFRAG_IPV4_TCP) | \
(1 << RTE_ETH_FLOW_NONFRAG_IPV4_SCTP) | \
(1 << RTE_ETH_FLOW_NONFRAG_IPV4_OTHER) | \
(1 << RTE_ETH_FLOW_NONFRAG_IPV6_UDP) | \
(1 << RTE_ETH_FLOW_NONFRAG_IPV6_TCP) | \
(1 << RTE_ETH_FLOW_NONFRAG_IPV6_SCTP) | \
(1 << RTE_ETH_FLOW_NONFRAG_IPV6_OTHER))
#define IPV6_ADDR_TO_MASK(ipaddr, ipv6m) do { \
uint8_t ipv6_addr[16]; \
uint8_t i; \
rte_memcpy(ipv6_addr, (ipaddr), sizeof(ipv6_addr));\
(ipv6m) = 0; \
for (i = 0; i < sizeof(ipv6_addr); i++) { \
if (ipv6_addr[i] == UINT8_MAX) \
(ipv6m) |= 1 << i; \
else if (ipv6_addr[i] != 0) { \
PMD_DRV_LOG(ERR, " invalid IPv6 address mask."); \
return -EINVAL; \
} \
} \
} while (0)
#define IPV6_MASK_TO_ADDR(ipv6m, ipaddr) do { \
uint8_t ipv6_addr[16]; \
uint8_t i; \
for (i = 0; i < sizeof(ipv6_addr); i++) { \
if ((ipv6m) & (1 << i)) \
ipv6_addr[i] = UINT8_MAX; \
else \
ipv6_addr[i] = 0; \
} \
rte_memcpy((ipaddr), ipv6_addr, sizeof(ipv6_addr));\
} while (0)
static int fdir_erase_filter_82599(struct ixgbe_hw *hw, uint32_t fdirhash);
static int fdir_set_input_mask_82599(struct rte_eth_dev *dev,
const struct rte_eth_fdir_masks *input_mask);
static int ixgbe_set_fdir_flex_conf(struct rte_eth_dev *dev,
const struct rte_eth_fdir_flex_conf *conf, uint32_t *fdirctrl);
static int fdir_enable_82599(struct ixgbe_hw *hw, uint32_t fdirctrl);
static int ixgbe_fdir_filter_to_atr_input(
const struct rte_eth_fdir_filter *fdir_filter,
union ixgbe_atr_input *input);
static uint32_t ixgbe_atr_compute_hash_82599(union ixgbe_atr_input *atr_input,
uint32_t key);
static uint32_t atr_compute_sig_hash_82599(union ixgbe_atr_input *input,
enum rte_fdir_pballoc_type pballoc);
static uint32_t atr_compute_perfect_hash_82599(union ixgbe_atr_input *input,
enum rte_fdir_pballoc_type pballoc);
static int fdir_write_perfect_filter_82599(struct ixgbe_hw *hw,
union ixgbe_atr_input *input, uint8_t queue,
uint32_t fdircmd, uint32_t fdirhash);
static int fdir_add_signature_filter_82599(struct ixgbe_hw *hw,
union ixgbe_atr_input *input, u8 queue, uint32_t fdircmd,
uint32_t fdirhash);
static int ixgbe_add_del_fdir_filter(struct rte_eth_dev *dev,
const struct rte_eth_fdir_filter *fdir_filter,
bool del,
bool update);
static int ixgbe_fdir_flush(struct rte_eth_dev *dev);
static void ixgbe_fdir_info_get(struct rte_eth_dev *dev,
struct rte_eth_fdir_info *fdir_info);
static void ixgbe_fdir_stats_get(struct rte_eth_dev *dev,
struct rte_eth_fdir_stats *fdir_stats);
/**
* This function is based on ixgbe_fdir_enable_82599() in base/ixgbe_82599.c.
* It adds extra configuration of fdirctrl that is common for all filter types.
*
* Initialize Flow Director control registers
* @hw: pointer to hardware structure
* @fdirctrl: value to write to flow director control register
**/
static int
fdir_enable_82599(struct ixgbe_hw *hw, uint32_t fdirctrl)
{
int i;
PMD_INIT_FUNC_TRACE();
/* Prime the keys for hashing */
IXGBE_WRITE_REG(hw, IXGBE_FDIRHKEY, IXGBE_ATR_BUCKET_HASH_KEY);
IXGBE_WRITE_REG(hw, IXGBE_FDIRSKEY, IXGBE_ATR_SIGNATURE_HASH_KEY);
/*
* Continue setup of fdirctrl register bits:
* Set the maximum length per hash bucket to 0xA filters
* Send interrupt when 64 filters are left
*/
fdirctrl |= (0xA << IXGBE_FDIRCTRL_MAX_LENGTH_SHIFT) |
(4 << IXGBE_FDIRCTRL_FULL_THRESH_SHIFT);
/*
* Poll init-done after we write the register. Estimated times:
* 10G: PBALLOC = 11b, timing is 60us
* 1G: PBALLOC = 11b, timing is 600us
* 100M: PBALLOC = 11b, timing is 6ms
*
* Multiple these timings by 4 if under full Rx load
*
* So we'll poll for IXGBE_FDIR_INIT_DONE_POLL times, sleeping for
* 1 msec per poll time. If we're at line rate and drop to 100M, then
* this might not finish in our poll time, but we can live with that
* for now.
*/
IXGBE_WRITE_REG(hw, IXGBE_FDIRCTRL, fdirctrl);
IXGBE_WRITE_FLUSH(hw);
for (i = 0; i < IXGBE_FDIR_INIT_DONE_POLL; i++) {
if (IXGBE_READ_REG(hw, IXGBE_FDIRCTRL) &
IXGBE_FDIRCTRL_INIT_DONE)
break;
msec_delay(1);
}
if (i >= IXGBE_FDIR_INIT_DONE_POLL) {
PMD_INIT_LOG(ERR, "Flow Director poll time exceeded "
"during enabling!");
return -ETIMEDOUT;
}
return 0;
}
/*
* Set appropriate bits in fdirctrl for: variable reporting levels, moving
* flexbytes matching field, and drop queue (only for perfect matching mode).
*/
static inline int
configure_fdir_flags(const struct rte_fdir_conf *conf, uint32_t *fdirctrl)
{
*fdirctrl = 0;
switch (conf->pballoc) {
case RTE_FDIR_PBALLOC_64K:
/* 8k - 1 signature filters */
*fdirctrl |= IXGBE_FDIRCTRL_PBALLOC_64K;
break;
case RTE_FDIR_PBALLOC_128K:
/* 16k - 1 signature filters */
*fdirctrl |= IXGBE_FDIRCTRL_PBALLOC_128K;
break;
case RTE_FDIR_PBALLOC_256K:
/* 32k - 1 signature filters */
*fdirctrl |= IXGBE_FDIRCTRL_PBALLOC_256K;
break;
default:
/* bad value */
PMD_INIT_LOG(ERR, "Invalid fdir_conf->pballoc value");
return -EINVAL;
};
/* status flags: write hash & swindex in the rx descriptor */
switch (conf->status) {
case RTE_FDIR_NO_REPORT_STATUS:
/* do nothing, default mode */
break;
case RTE_FDIR_REPORT_STATUS:
/* report status when the packet matches a fdir rule */
*fdirctrl |= IXGBE_FDIRCTRL_REPORT_STATUS;
break;
case RTE_FDIR_REPORT_STATUS_ALWAYS:
/* always report status */
*fdirctrl |= IXGBE_FDIRCTRL_REPORT_STATUS_ALWAYS;
break;
default:
/* bad value */
PMD_INIT_LOG(ERR, "Invalid fdir_conf->status value");
return -EINVAL;
};
*fdirctrl |= (IXGBE_DEFAULT_FLEXBYTES_OFFSET / sizeof(uint16_t)) <<
IXGBE_FDIRCTRL_FLEX_SHIFT;
if (conf->mode == RTE_FDIR_MODE_PERFECT) {
*fdirctrl |= IXGBE_FDIRCTRL_PERFECT_MATCH;
*fdirctrl |= (conf->drop_queue << IXGBE_FDIRCTRL_DROP_Q_SHIFT);
}
return 0;
}
/**
* Reverse the bits in FDIR registers that store 2 x 16 bit masks.
*
* @hi_dword: Bits 31:16 mask to be bit swapped.
* @lo_dword: Bits 15:0 mask to be bit swapped.
*
* Flow director uses several registers to store 2 x 16 bit masks with the
* bits reversed such as FDIRTCPM, FDIRUDPM. The LS bit of the
* mask affects the MS bit/byte of the target. This function reverses the
* bits in these masks.
* **/
static inline uint32_t
reverse_fdir_bitmasks(uint16_t hi_dword, uint16_t lo_dword)
{
uint32_t mask = hi_dword << 16;
mask |= lo_dword;
mask = ((mask & 0x55555555) << 1) | ((mask & 0xAAAAAAAA) >> 1);
mask = ((mask & 0x33333333) << 2) | ((mask & 0xCCCCCCCC) >> 2);
mask = ((mask & 0x0F0F0F0F) << 4) | ((mask & 0xF0F0F0F0) >> 4);
return ((mask & 0x00FF00FF) << 8) | ((mask & 0xFF00FF00) >> 8);
}
/*
* This is based on ixgbe_fdir_set_input_mask_82599() in base/ixgbe_82599.c,
* but makes use of the rte_fdir_masks structure to see which bits to set.
*/
static int
fdir_set_input_mask_82599(struct rte_eth_dev *dev,
const struct rte_eth_fdir_masks *input_mask)
{
struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
struct ixgbe_hw_fdir_info *info =
IXGBE_DEV_PRIVATE_TO_FDIR_INFO(dev->data->dev_private);
/*
* mask VM pool and DIPv6 since there are currently not supported
* mask FLEX byte, it will be set in flex_conf
*/
uint32_t fdirm = IXGBE_FDIRM_POOL | IXGBE_FDIRM_DIPv6 | IXGBE_FDIRM_FLEX;
uint32_t fdirtcpm; /* TCP source and destination port masks. */
uint32_t fdiripv6m; /* IPv6 source and destination masks. */
uint16_t dst_ipv6m = 0;
uint16_t src_ipv6m = 0;
PMD_INIT_FUNC_TRACE();
/*
* Program the relevant mask registers. If src/dst_port or src/dst_addr
* are zero, then assume a full mask for that field. Also assume that
* a VLAN of 0 is unspecified, so mask that out as well. L4type
* cannot be masked out in this implementation.
*/
if (input_mask->dst_port_mask == 0 && input_mask->src_port_mask == 0)
/* use the L4 protocol mask for raw IPv4/IPv6 traffic */
fdirm |= IXGBE_FDIRM_L4P;
if (input_mask->vlan_tci_mask == 0x0FFF)
/* mask VLAN Priority */
fdirm |= IXGBE_FDIRM_VLANP;
else if (input_mask->vlan_tci_mask == 0xE000)
/* mask VLAN ID */
fdirm |= IXGBE_FDIRM_VLANID;
else if (input_mask->vlan_tci_mask == 0)
/* mask VLAN ID and Priority */
fdirm |= IXGBE_FDIRM_VLANID | IXGBE_FDIRM_VLANP;
else if (input_mask->vlan_tci_mask != 0xEFFF) {
PMD_INIT_LOG(ERR, "invalid vlan_tci_mask");
return -EINVAL;
}
info->mask.vlan_tci_mask = input_mask->vlan_tci_mask;
IXGBE_WRITE_REG(hw, IXGBE_FDIRM, fdirm);
/* store the TCP/UDP port masks, bit reversed from port layout */
fdirtcpm = reverse_fdir_bitmasks(input_mask->dst_port_mask,
input_mask->src_port_mask);
/* write all the same so that UDP, TCP and SCTP use the same mask */
IXGBE_WRITE_REG(hw, IXGBE_FDIRTCPM, ~fdirtcpm);
IXGBE_WRITE_REG(hw, IXGBE_FDIRUDPM, ~fdirtcpm);
IXGBE_WRITE_REG(hw, IXGBE_FDIRSCTPM, ~fdirtcpm);
info->mask.src_port_mask = input_mask->src_port_mask;
info->mask.dst_port_mask = input_mask->dst_port_mask;
/* Store source and destination IPv4 masks (big-endian) */
IXGBE_WRITE_REG(hw, IXGBE_FDIRSIP4M, ~(input_mask->ipv4_mask.src_ip));
IXGBE_WRITE_REG(hw, IXGBE_FDIRDIP4M, ~(input_mask->ipv4_mask.dst_ip));
info->mask.src_ipv4_mask = input_mask->ipv4_mask.src_ip;
info->mask.dst_ipv4_mask = input_mask->ipv4_mask.dst_ip;
if (dev->data->dev_conf.fdir_conf.mode == RTE_FDIR_MODE_SIGNATURE) {
/*
* IPv6 mask is only meaningful in signature mode
* Store source and destination IPv6 masks (bit reversed)
*/
IPV6_ADDR_TO_MASK(input_mask->ipv6_mask.src_ip, src_ipv6m);
IPV6_ADDR_TO_MASK(input_mask->ipv6_mask.dst_ip, dst_ipv6m);
fdiripv6m = (dst_ipv6m << 16) | src_ipv6m;
IXGBE_WRITE_REG(hw, IXGBE_FDIRIP6M, ~fdiripv6m);
info->mask.src_ipv6_mask = src_ipv6m;
info->mask.dst_ipv6_mask = dst_ipv6m;
}
return IXGBE_SUCCESS;
}
/*
* ixgbe_check_fdir_flex_conf -check if the flex payload and mask configuration
* arguments are valid
*/
static int
ixgbe_set_fdir_flex_conf(struct rte_eth_dev *dev,
const struct rte_eth_fdir_flex_conf *conf, uint32_t *fdirctrl)
{
struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
struct ixgbe_hw_fdir_info *info =
IXGBE_DEV_PRIVATE_TO_FDIR_INFO(dev->data->dev_private);
const struct rte_eth_flex_payload_cfg *flex_cfg;
const struct rte_eth_fdir_flex_mask *flex_mask;
uint32_t fdirm;
uint16_t flexbytes = 0;
uint16_t i;
fdirm = IXGBE_READ_REG(hw, IXGBE_FDIRM);
if (conf == NULL) {
PMD_DRV_LOG(INFO, "NULL pointer.");
return -EINVAL;
}
for (i = 0; i < conf->nb_payloads; i++) {
flex_cfg = &conf->flex_set[i];
if (flex_cfg->type != RTE_ETH_RAW_PAYLOAD) {
PMD_DRV_LOG(ERR, "unsupported payload type.");
return -EINVAL;
}
if (((flex_cfg->src_offset[0] & 0x1) == 0) &&
(flex_cfg->src_offset[1] == flex_cfg->src_offset[0] + 1) &&
(flex_cfg->src_offset[0] <= IXGBE_MAX_FLX_SOURCE_OFF)) {
*fdirctrl &= ~IXGBE_FDIRCTRL_FLEX_MASK;
*fdirctrl |=
(flex_cfg->src_offset[0] / sizeof(uint16_t)) <<
IXGBE_FDIRCTRL_FLEX_SHIFT;
} else {
PMD_DRV_LOG(ERR, "invalid flexbytes arguments.");
return -EINVAL;
}
}
for (i = 0; i < conf->nb_flexmasks; i++) {
flex_mask = &conf->flex_mask[i];
if (flex_mask->flow_type != RTE_ETH_FLOW_UNKNOWN) {
PMD_DRV_LOG(ERR, "flexmask should be set globally.");
return -EINVAL;
}
flexbytes = (uint16_t)(((flex_mask->mask[0] << 8) & 0xFF00) |
((flex_mask->mask[1]) & 0xFF));
if (flexbytes == UINT16_MAX)
fdirm &= ~IXGBE_FDIRM_FLEX;
else if (flexbytes != 0) {
/* IXGBE_FDIRM_FLEX is set by default when set mask */
PMD_DRV_LOG(ERR, " invalid flexbytes mask arguments.");
return -EINVAL;
}
}
IXGBE_WRITE_REG(hw, IXGBE_FDIRM, fdirm);
info->mask.flex_bytes_mask = flexbytes ? UINT16_MAX : 0;
info->flex_bytes_offset = (uint8_t)((*fdirctrl &
IXGBE_FDIRCTRL_FLEX_MASK) >>
IXGBE_FDIRCTRL_FLEX_SHIFT);
return 0;
}
int
ixgbe_fdir_configure(struct rte_eth_dev *dev)
{
struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
int err;
uint32_t fdirctrl, pbsize;
int i;
PMD_INIT_FUNC_TRACE();
if (hw->mac.type != ixgbe_mac_82599EB &&
hw->mac.type != ixgbe_mac_X540 &&
hw->mac.type != ixgbe_mac_X550 &&
hw->mac.type != ixgbe_mac_X550EM_x)
return -ENOSYS;
err = configure_fdir_flags(&dev->data->dev_conf.fdir_conf, &fdirctrl);
if (err)
return err;
/*
* Before enabling Flow Director, the Rx Packet Buffer size
* must be reduced. The new value is the current size minus
* flow director memory usage size.
*/
pbsize = (1 << (PBALLOC_SIZE_SHIFT + (fdirctrl & FDIRCTRL_PBALLOC_MASK)));
IXGBE_WRITE_REG(hw, IXGBE_RXPBSIZE(0),
(IXGBE_READ_REG(hw, IXGBE_RXPBSIZE(0)) - pbsize));
/*
* The defaults in the HW for RX PB 1-7 are not zero and so should be
* intialized to zero for non DCB mode otherwise actual total RX PB
* would be bigger than programmed and filter space would run into
* the PB 0 region.
*/
for (i = 1; i < 8; i++)
IXGBE_WRITE_REG(hw, IXGBE_RXPBSIZE(i), 0);
err = fdir_set_input_mask_82599(dev, &dev->data->dev_conf.fdir_conf.mask);
if (err < 0) {
PMD_INIT_LOG(ERR, " Error on setting FD mask");
return err;
}
err = ixgbe_set_fdir_flex_conf(dev,
&dev->data->dev_conf.fdir_conf.flex_conf, &fdirctrl);
if (err < 0) {
PMD_INIT_LOG(ERR, " Error on setting FD flexible arguments.");
return err;
}
err = fdir_enable_82599(hw, fdirctrl);
if (err < 0) {
PMD_INIT_LOG(ERR, " Error on enabling FD.");
return err;
}
return 0;
}
/*
* Convert DPDK rte_eth_fdir_filter struct to ixgbe_atr_input union that is used
* by the IXGBE driver code.
*/
static int
ixgbe_fdir_filter_to_atr_input(const struct rte_eth_fdir_filter *fdir_filter,
union ixgbe_atr_input *input)
{
input->formatted.vlan_id = fdir_filter->input.flow_ext.vlan_tci;
input->formatted.flex_bytes = (uint16_t)(
(fdir_filter->input.flow_ext.flexbytes[1] << 8 & 0xFF00) |
(fdir_filter->input.flow_ext.flexbytes[0] & 0xFF));
switch (fdir_filter->input.flow_type) {
case RTE_ETH_FLOW_NONFRAG_IPV4_UDP:
input->formatted.flow_type = IXGBE_ATR_FLOW_TYPE_UDPV4;
break;
case RTE_ETH_FLOW_NONFRAG_IPV4_TCP:
input->formatted.flow_type = IXGBE_ATR_FLOW_TYPE_TCPV4;
break;
case RTE_ETH_FLOW_NONFRAG_IPV4_SCTP:
input->formatted.flow_type = IXGBE_ATR_FLOW_TYPE_SCTPV4;
break;
case RTE_ETH_FLOW_NONFRAG_IPV4_OTHER:
input->formatted.flow_type = IXGBE_ATR_FLOW_TYPE_IPV4;
break;
case RTE_ETH_FLOW_NONFRAG_IPV6_UDP:
input->formatted.flow_type = IXGBE_ATR_FLOW_TYPE_UDPV6;
break;
case RTE_ETH_FLOW_NONFRAG_IPV6_TCP:
input->formatted.flow_type = IXGBE_ATR_FLOW_TYPE_TCPV6;
break;
case RTE_ETH_FLOW_NONFRAG_IPV6_SCTP:
input->formatted.flow_type = IXGBE_ATR_FLOW_TYPE_SCTPV6;
break;
case RTE_ETH_FLOW_NONFRAG_IPV6_OTHER:
input->formatted.flow_type = IXGBE_ATR_FLOW_TYPE_IPV6;
break;
default:
PMD_DRV_LOG(ERR, " Error on flow_type input");
return -EINVAL;
}
switch (fdir_filter->input.flow_type) {
case RTE_ETH_FLOW_NONFRAG_IPV4_UDP:
case RTE_ETH_FLOW_NONFRAG_IPV4_TCP:
input->formatted.src_port =
fdir_filter->input.flow.udp4_flow.src_port;
input->formatted.dst_port =
fdir_filter->input.flow.udp4_flow.dst_port;
/*for SCTP flow type, port and verify_tag are meaningless in ixgbe.*/
case RTE_ETH_FLOW_NONFRAG_IPV4_SCTP:
case RTE_ETH_FLOW_NONFRAG_IPV4_OTHER:
input->formatted.src_ip[0] =
fdir_filter->input.flow.ip4_flow.src_ip;
input->formatted.dst_ip[0] =
fdir_filter->input.flow.ip4_flow.dst_ip;
break;
case RTE_ETH_FLOW_NONFRAG_IPV6_UDP:
case RTE_ETH_FLOW_NONFRAG_IPV6_TCP:
input->formatted.src_port =
fdir_filter->input.flow.udp6_flow.src_port;
input->formatted.dst_port =
fdir_filter->input.flow.udp6_flow.dst_port;
/*for SCTP flow type, port and verify_tag are meaningless in ixgbe.*/
case RTE_ETH_FLOW_NONFRAG_IPV6_SCTP:
case RTE_ETH_FLOW_NONFRAG_IPV6_OTHER:
rte_memcpy(input->formatted.src_ip,
fdir_filter->input.flow.ipv6_flow.src_ip,
sizeof(input->formatted.src_ip));
rte_memcpy(input->formatted.dst_ip,
fdir_filter->input.flow.ipv6_flow.dst_ip,
sizeof(input->formatted.dst_ip));
break;
default:
PMD_DRV_LOG(ERR, " Error on flow_type input");
return -EINVAL;
}
return 0;
}
/*
* The below function is taken from the FreeBSD IXGBE drivers release
* 2.3.8. The only change is not to mask hash_result with IXGBE_ATR_HASH_MASK
* before returning, as the signature hash can use 16bits.
*
* The newer driver has optimised functions for calculating bucket and
* signature hashes. However they don't support IPv6 type packets for signature
* filters so are not used here.
*
* Note that the bkt_hash field in the ixgbe_atr_input structure is also never
* set.
*
* Compute the hashes for SW ATR
* @stream: input bitstream to compute the hash on
* @key: 32-bit hash key
**/
static uint32_t
ixgbe_atr_compute_hash_82599(union ixgbe_atr_input *atr_input,
uint32_t key)
{
/*
* The algorithm is as follows:
* Hash[15:0] = Sum { S[n] x K[n+16] }, n = 0...350
* where Sum {A[n]}, n = 0...n is bitwise XOR of A[0], A[1]...A[n]
* and A[n] x B[n] is bitwise AND between same length strings
*
* K[n] is 16 bits, defined as:
* for n modulo 32 >= 15, K[n] = K[n % 32 : (n % 32) - 15]
* for n modulo 32 < 15, K[n] =
* K[(n % 32:0) | (31:31 - (14 - (n % 32)))]
*
* S[n] is 16 bits, defined as:
* for n >= 15, S[n] = S[n:n - 15]
* for n < 15, S[n] = S[(n:0) | (350:350 - (14 - n))]
*
* To simplify for programming, the algorithm is implemented
* in software this way:
*
* key[31:0], hi_hash_dword[31:0], lo_hash_dword[31:0], hash[15:0]
*
* for (i = 0; i < 352; i+=32)
* hi_hash_dword[31:0] ^= Stream[(i+31):i];
*
* lo_hash_dword[15:0] ^= Stream[15:0];
* lo_hash_dword[15:0] ^= hi_hash_dword[31:16];
* lo_hash_dword[31:16] ^= hi_hash_dword[15:0];
*
* hi_hash_dword[31:0] ^= Stream[351:320];
*
* if(key[0])
* hash[15:0] ^= Stream[15:0];
*
* for (i = 0; i < 16; i++) {
* if (key[i])
* hash[15:0] ^= lo_hash_dword[(i+15):i];
* if (key[i + 16])
* hash[15:0] ^= hi_hash_dword[(i+15):i];
* }
*
*/
__be32 common_hash_dword = 0;
u32 hi_hash_dword, lo_hash_dword, flow_vm_vlan;
u32 hash_result = 0;
u8 i;
/* record the flow_vm_vlan bits as they are a key part to the hash */
flow_vm_vlan = IXGBE_NTOHL(atr_input->dword_stream[0]);
/* generate common hash dword */
for (i = 1; i <= 13; i++)
common_hash_dword ^= atr_input->dword_stream[i];
hi_hash_dword = IXGBE_NTOHL(common_hash_dword);
/* low dword is word swapped version of common */
lo_hash_dword = (hi_hash_dword >> 16) | (hi_hash_dword << 16);
/* apply flow ID/VM pool/VLAN ID bits to hash words */
hi_hash_dword ^= flow_vm_vlan ^ (flow_vm_vlan >> 16);
/* Process bits 0 and 16 */
if (key & 0x0001) hash_result ^= lo_hash_dword;
if (key & 0x00010000) hash_result ^= hi_hash_dword;
/*
* apply flow ID/VM pool/VLAN ID bits to lo hash dword, we had to
* delay this because bit 0 of the stream should not be processed
* so we do not add the vlan until after bit 0 was processed
*/
lo_hash_dword ^= flow_vm_vlan ^ (flow_vm_vlan << 16);
/* process the remaining 30 bits in the key 2 bits at a time */
for (i = 15; i; i-- ) {
if (key & (0x0001 << i)) hash_result ^= lo_hash_dword >> i;
if (key & (0x00010000 << i)) hash_result ^= hi_hash_dword >> i;
}
return hash_result;
}
static uint32_t
atr_compute_perfect_hash_82599(union ixgbe_atr_input *input,
enum rte_fdir_pballoc_type pballoc)
{
if (pballoc == RTE_FDIR_PBALLOC_256K)
return ixgbe_atr_compute_hash_82599(input,
IXGBE_ATR_BUCKET_HASH_KEY) &
PERFECT_BUCKET_256KB_HASH_MASK;
else if (pballoc == RTE_FDIR_PBALLOC_128K)
return ixgbe_atr_compute_hash_82599(input,
IXGBE_ATR_BUCKET_HASH_KEY) &
PERFECT_BUCKET_128KB_HASH_MASK;
else
return ixgbe_atr_compute_hash_82599(input,
IXGBE_ATR_BUCKET_HASH_KEY) &
PERFECT_BUCKET_64KB_HASH_MASK;
}
/**
* ixgbe_fdir_check_cmd_complete - poll to check whether FDIRCMD is complete
* @hw: pointer to hardware structure
*/
static inline int
ixgbe_fdir_check_cmd_complete(struct ixgbe_hw *hw, uint32_t *fdircmd)
{
int i;
for (i = 0; i < IXGBE_FDIRCMD_CMD_POLL; i++) {
*fdircmd = IXGBE_READ_REG(hw, IXGBE_FDIRCMD);
if (!(*fdircmd & IXGBE_FDIRCMD_CMD_MASK))
return 0;
rte_delay_us(IXGBE_FDIRCMD_CMD_INTERVAL_US);
}
return -ETIMEDOUT;
}
/*
* Calculate the hash value needed for signature-match filters. In the FreeBSD
* driver, this is done by the optimised function
* ixgbe_atr_compute_sig_hash_82599(). However that can't be used here as it
* doesn't support calculating a hash for an IPv6 filter.
*/
static uint32_t
atr_compute_sig_hash_82599(union ixgbe_atr_input *input,
enum rte_fdir_pballoc_type pballoc)
{
uint32_t bucket_hash, sig_hash;
if (pballoc == RTE_FDIR_PBALLOC_256K)
bucket_hash = ixgbe_atr_compute_hash_82599(input,
IXGBE_ATR_BUCKET_HASH_KEY) &
SIG_BUCKET_256KB_HASH_MASK;
else if (pballoc == RTE_FDIR_PBALLOC_128K)
bucket_hash = ixgbe_atr_compute_hash_82599(input,
IXGBE_ATR_BUCKET_HASH_KEY) &
SIG_BUCKET_128KB_HASH_MASK;
else
bucket_hash = ixgbe_atr_compute_hash_82599(input,
IXGBE_ATR_BUCKET_HASH_KEY) &
SIG_BUCKET_64KB_HASH_MASK;
sig_hash = ixgbe_atr_compute_hash_82599(input,
IXGBE_ATR_SIGNATURE_HASH_KEY);
return (sig_hash << IXGBE_FDIRHASH_SIG_SW_INDEX_SHIFT) | bucket_hash;
}
/*
* This is based on ixgbe_fdir_write_perfect_filter_82599() in
* base/ixgbe_82599.c, with the ability to set extra flags in FDIRCMD register
* added, and IPv6 support also added. The hash value is also pre-calculated
* as the pballoc value is needed to do it.
*/
static int
fdir_write_perfect_filter_82599(struct ixgbe_hw *hw,
union ixgbe_atr_input *input, uint8_t queue,
uint32_t fdircmd, uint32_t fdirhash)
{
uint32_t fdirport, fdirvlan;
int err = 0;
/* record the IPv4 address (big-endian) */
IXGBE_WRITE_REG(hw, IXGBE_FDIRIPSA, input->formatted.src_ip[0]);
IXGBE_WRITE_REG(hw, IXGBE_FDIRIPDA, input->formatted.dst_ip[0]);
/* record source and destination port (little-endian)*/
fdirport = IXGBE_NTOHS(input->formatted.dst_port);
fdirport <<= IXGBE_FDIRPORT_DESTINATION_SHIFT;
fdirport |= IXGBE_NTOHS(input->formatted.src_port);
IXGBE_WRITE_REG(hw, IXGBE_FDIRPORT, fdirport);
/* record vlan (little-endian) and flex_bytes(big-endian) */
fdirvlan = input->formatted.flex_bytes;
fdirvlan <<= IXGBE_FDIRVLAN_FLEX_SHIFT;
fdirvlan |= IXGBE_NTOHS(input->formatted.vlan_id);
IXGBE_WRITE_REG(hw, IXGBE_FDIRVLAN, fdirvlan);
/* configure FDIRHASH register */
IXGBE_WRITE_REG(hw, IXGBE_FDIRHASH, fdirhash);
/*
* flush all previous writes to make certain registers are
* programmed prior to issuing the command
*/
IXGBE_WRITE_FLUSH(hw);
/* configure FDIRCMD register */
fdircmd |= IXGBE_FDIRCMD_CMD_ADD_FLOW |
IXGBE_FDIRCMD_LAST | IXGBE_FDIRCMD_QUEUE_EN;
fdircmd |= input->formatted.flow_type << IXGBE_FDIRCMD_FLOW_TYPE_SHIFT;
fdircmd |= (uint32_t)queue << IXGBE_FDIRCMD_RX_QUEUE_SHIFT;
fdircmd |= (uint32_t)input->formatted.vm_pool << IXGBE_FDIRCMD_VT_POOL_SHIFT;
IXGBE_WRITE_REG(hw, IXGBE_FDIRCMD, fdircmd);
PMD_DRV_LOG(DEBUG, "Rx Queue=%x hash=%x", queue, fdirhash);
err = ixgbe_fdir_check_cmd_complete(hw, &fdircmd);
if (err < 0)
PMD_DRV_LOG(ERR, "Timeout writing flow director filter.");
return err;
}
/**
* This function is based on ixgbe_atr_add_signature_filter_82599() in
* base/ixgbe_82599.c, but uses a pre-calculated hash value. It also supports
* setting extra fields in the FDIRCMD register, and removes the code that was
* verifying the flow_type field. According to the documentation, a flow type of
* 00 (i.e. not TCP, UDP, or SCTP) is not supported, however it appears to
* work ok...
*
* Adds a signature hash filter
* @hw: pointer to hardware structure
* @input: unique input dword
* @queue: queue index to direct traffic to
* @fdircmd: any extra flags to set in fdircmd register
* @fdirhash: pre-calculated hash value for the filter
**/
static int
fdir_add_signature_filter_82599(struct ixgbe_hw *hw,
union ixgbe_atr_input *input, u8 queue, uint32_t fdircmd,
uint32_t fdirhash)
{
int err = 0;
PMD_INIT_FUNC_TRACE();
/* configure FDIRCMD register */
fdircmd |= IXGBE_FDIRCMD_CMD_ADD_FLOW |
IXGBE_FDIRCMD_LAST | IXGBE_FDIRCMD_QUEUE_EN;
fdircmd |= input->formatted.flow_type << IXGBE_FDIRCMD_FLOW_TYPE_SHIFT;
fdircmd |= (uint32_t)queue << IXGBE_FDIRCMD_RX_QUEUE_SHIFT;
IXGBE_WRITE_REG(hw, IXGBE_FDIRHASH, fdirhash);
IXGBE_WRITE_REG(hw, IXGBE_FDIRCMD, fdircmd);
PMD_DRV_LOG(DEBUG, "Rx Queue=%x hash=%x", queue, fdirhash);
err = ixgbe_fdir_check_cmd_complete(hw, &fdircmd);
if (err < 0)
PMD_DRV_LOG(ERR, "Timeout writing flow director filter.");
return err;
}
/*
* This is based on ixgbe_fdir_erase_perfect_filter_82599() in
* base/ixgbe_82599.c. It is modified to take in the hash as a parameter so
* that it can be used for removing signature and perfect filters.
*/
static int
fdir_erase_filter_82599(struct ixgbe_hw *hw, uint32_t fdirhash)
{
uint32_t fdircmd = 0;
int err = 0;
IXGBE_WRITE_REG(hw, IXGBE_FDIRHASH, fdirhash);
/* flush hash to HW */
IXGBE_WRITE_FLUSH(hw);
/* Query if filter is present */
IXGBE_WRITE_REG(hw, IXGBE_FDIRCMD, IXGBE_FDIRCMD_CMD_QUERY_REM_FILT);
err = ixgbe_fdir_check_cmd_complete(hw, &fdircmd);
if (err < 0) {
PMD_INIT_LOG(ERR, "Timeout querying for flow director filter.");
return err;
}
/* if filter exists in hardware then remove it */
if (fdircmd & IXGBE_FDIRCMD_FILTER_VALID) {
IXGBE_WRITE_REG(hw, IXGBE_FDIRHASH, fdirhash);
IXGBE_WRITE_FLUSH(hw);
IXGBE_WRITE_REG(hw, IXGBE_FDIRCMD,
IXGBE_FDIRCMD_CMD_REMOVE_FLOW);
}
err = ixgbe_fdir_check_cmd_complete(hw, &fdircmd);
if (err < 0)
PMD_INIT_LOG(ERR, "Timeout erasing flow director filter.");
return err;
}
/*
* ixgbe_add_del_fdir_filter - add or remove a flow diretor filter.
* @dev: pointer to the structure rte_eth_dev
* @fdir_filter: fdir filter entry
* @del: 1 - delete, 0 - add
* @update: 1 - update
*/
static int
ixgbe_add_del_fdir_filter(struct rte_eth_dev *dev,
const struct rte_eth_fdir_filter *fdir_filter,
bool del,
bool update)
{
struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
uint32_t fdircmd_flags;
uint32_t fdirhash;
union ixgbe_atr_input input;
uint8_t queue;
bool is_perfect = FALSE;
int err;
struct ixgbe_hw_fdir_info *info =
IXGBE_DEV_PRIVATE_TO_FDIR_INFO(dev->data->dev_private);
if (dev->data->dev_conf.fdir_conf.mode == RTE_FDIR_MODE_NONE)
return -ENOTSUP;
/*
* Sanity check for x550.
* When adding a new filter with flow type set to IPv4-other,
* the flow director mask should be configed before,
* and the L4 protocol and ports are masked.
*/
if ((!del) &&
(hw->mac.type == ixgbe_mac_X550 ||
hw->mac.type == ixgbe_mac_X550EM_x) &&
(fdir_filter->input.flow_type ==
RTE_ETH_FLOW_NONFRAG_IPV4_OTHER) &&
(info->mask.src_port_mask != 0 ||
info->mask.dst_port_mask != 0)) {
PMD_DRV_LOG(ERR, "By this device,"
" IPv4-other is not supported without"
" L4 protocol and ports masked!");
return -ENOTSUP;
}
if (dev->data->dev_conf.fdir_conf.mode == RTE_FDIR_MODE_PERFECT)
is_perfect = TRUE;
memset(&input, 0, sizeof(input));
err = ixgbe_fdir_filter_to_atr_input(fdir_filter, &input);
if (err)
return err;
if (is_perfect) {
if (input.formatted.flow_type & IXGBE_ATR_L4TYPE_IPV6_MASK) {
PMD_DRV_LOG(ERR, "IPv6 is not supported in"
" perfect mode!");
return -ENOTSUP;
}
fdirhash = atr_compute_perfect_hash_82599(&input,
dev->data->dev_conf.fdir_conf.pballoc);
fdirhash |= fdir_filter->soft_id <<
IXGBE_FDIRHASH_SIG_SW_INDEX_SHIFT;
} else
fdirhash = atr_compute_sig_hash_82599(&input,
dev->data->dev_conf.fdir_conf.pballoc);
if (del) {
err = fdir_erase_filter_82599(hw, fdirhash);
if (err < 0)
PMD_DRV_LOG(ERR, "Fail to delete FDIR filter!");
else
PMD_DRV_LOG(DEBUG, "Success to delete FDIR filter!");
return err;
}
/* add or update an fdir filter*/
fdircmd_flags = (update) ? IXGBE_FDIRCMD_FILTER_UPDATE : 0;
if (fdir_filter->action.behavior == RTE_ETH_FDIR_REJECT) {
if (is_perfect) {
queue = dev->data->dev_conf.fdir_conf.drop_queue;
fdircmd_flags |= IXGBE_FDIRCMD_DROP;
} else {
PMD_DRV_LOG(ERR, "Drop option is not supported in"
" signature mode.");
return -EINVAL;
}
} else if (fdir_filter->action.rx_queue < IXGBE_MAX_RX_QUEUE_NUM)
queue = (uint8_t)fdir_filter->action.rx_queue;
else
return -EINVAL;
if (is_perfect) {
err = fdir_write_perfect_filter_82599(hw, &input, queue,
fdircmd_flags, fdirhash);
} else {
err = fdir_add_signature_filter_82599(hw, &input, queue,
fdircmd_flags, fdirhash);
}
if (err < 0)
PMD_DRV_LOG(ERR, "Fail to add FDIR filter!");
else
PMD_DRV_LOG(DEBUG, "Success to add FDIR filter");
return err;
}
static int
ixgbe_fdir_flush(struct rte_eth_dev *dev)
{
struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
struct ixgbe_hw_fdir_info *info =
IXGBE_DEV_PRIVATE_TO_FDIR_INFO(dev->data->dev_private);
int ret;
ret = ixgbe_reinit_fdir_tables_82599(hw);
if (ret < 0) {
PMD_INIT_LOG(ERR, "Failed to re-initialize FD table.");
return ret;
}
info->f_add = 0;
info->f_remove = 0;
info->add = 0;
info->remove = 0;
return ret;
}
#define FDIRENTRIES_NUM_SHIFT 10
static void
ixgbe_fdir_info_get(struct rte_eth_dev *dev, struct rte_eth_fdir_info *fdir_info)
{
struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
struct ixgbe_hw_fdir_info *info =
IXGBE_DEV_PRIVATE_TO_FDIR_INFO(dev->data->dev_private);
uint32_t fdirctrl, max_num;
uint8_t offset;
fdirctrl = IXGBE_READ_REG(hw, IXGBE_FDIRCTRL);
offset = ((fdirctrl & IXGBE_FDIRCTRL_FLEX_MASK) >>
IXGBE_FDIRCTRL_FLEX_SHIFT) * sizeof(uint16_t);
fdir_info->mode = dev->data->dev_conf.fdir_conf.mode;
max_num = (1 << (FDIRENTRIES_NUM_SHIFT +
(fdirctrl & FDIRCTRL_PBALLOC_MASK)));
if (fdir_info->mode == RTE_FDIR_MODE_PERFECT)
fdir_info->guarant_spc = max_num;
else if (fdir_info->mode == RTE_FDIR_MODE_SIGNATURE)
fdir_info->guarant_spc = max_num * 4;
fdir_info->mask.vlan_tci_mask = info->mask.vlan_tci_mask;
fdir_info->mask.ipv4_mask.src_ip = info->mask.src_ipv4_mask;
fdir_info->mask.ipv4_mask.dst_ip = info->mask.dst_ipv4_mask;
IPV6_MASK_TO_ADDR(info->mask.src_ipv6_mask,
fdir_info->mask.ipv6_mask.src_ip);
IPV6_MASK_TO_ADDR(info->mask.dst_ipv6_mask,
fdir_info->mask.ipv6_mask.dst_ip);
fdir_info->mask.src_port_mask = info->mask.src_port_mask;
fdir_info->mask.dst_port_mask = info->mask.dst_port_mask;
fdir_info->max_flexpayload = IXGBE_FDIR_MAX_FLEX_LEN;
fdir_info->flow_types_mask[0] = IXGBE_FDIR_FLOW_TYPES;
fdir_info->flex_payload_unit = sizeof(uint16_t);
fdir_info->max_flex_payload_segment_num = 1;
fdir_info->flex_payload_limit = 62;
fdir_info->flex_conf.nb_payloads = 1;
fdir_info->flex_conf.flex_set[0].type = RTE_ETH_RAW_PAYLOAD;
fdir_info->flex_conf.flex_set[0].src_offset[0] = offset;
fdir_info->flex_conf.flex_set[0].src_offset[1] = offset + 1;
fdir_info->flex_conf.nb_flexmasks = 1;
fdir_info->flex_conf.flex_mask[0].flow_type = RTE_ETH_FLOW_UNKNOWN;
fdir_info->flex_conf.flex_mask[0].mask[0] =
(uint8_t)(info->mask.flex_bytes_mask & 0x00FF);
fdir_info->flex_conf.flex_mask[0].mask[1] =
(uint8_t)((info->mask.flex_bytes_mask & 0xFF00) >> 8);
}
static void
ixgbe_fdir_stats_get(struct rte_eth_dev *dev, struct rte_eth_fdir_stats *fdir_stats)
{
struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
struct ixgbe_hw_fdir_info *info =
IXGBE_DEV_PRIVATE_TO_FDIR_INFO(dev->data->dev_private);
uint32_t reg, max_num;
/* Get the information from registers */
reg = IXGBE_READ_REG(hw, IXGBE_FDIRFREE);
info->collision = (uint16_t)((reg & IXGBE_FDIRFREE_COLL_MASK) >>
IXGBE_FDIRFREE_COLL_SHIFT);
info->free = (uint16_t)((reg & IXGBE_FDIRFREE_FREE_MASK) >>
IXGBE_FDIRFREE_FREE_SHIFT);
reg = IXGBE_READ_REG(hw, IXGBE_FDIRLEN);
info->maxhash = (uint16_t)((reg & IXGBE_FDIRLEN_MAXHASH_MASK) >>
IXGBE_FDIRLEN_MAXHASH_SHIFT);
info->maxlen = (uint8_t)((reg & IXGBE_FDIRLEN_MAXLEN_MASK) >>
IXGBE_FDIRLEN_MAXLEN_SHIFT);
reg = IXGBE_READ_REG(hw, IXGBE_FDIRUSTAT);
info->remove += (reg & IXGBE_FDIRUSTAT_REMOVE_MASK) >>
IXGBE_FDIRUSTAT_REMOVE_SHIFT;
info->add += (reg & IXGBE_FDIRUSTAT_ADD_MASK) >>
IXGBE_FDIRUSTAT_ADD_SHIFT;
reg = IXGBE_READ_REG(hw, IXGBE_FDIRFSTAT) & 0xFFFF;
info->f_remove += (reg & IXGBE_FDIRFSTAT_FREMOVE_MASK) >>
IXGBE_FDIRFSTAT_FREMOVE_SHIFT;
info->f_add += (reg & IXGBE_FDIRFSTAT_FADD_MASK) >>
IXGBE_FDIRFSTAT_FADD_SHIFT;
/* Copy the new information in the fdir parameter */
fdir_stats->collision = info->collision;
fdir_stats->free = info->free;
fdir_stats->maxhash = info->maxhash;
fdir_stats->maxlen = info->maxlen;
fdir_stats->remove = info->remove;
fdir_stats->add = info->add;
fdir_stats->f_remove = info->f_remove;
fdir_stats->f_add = info->f_add;
reg = IXGBE_READ_REG(hw, IXGBE_FDIRCTRL);
max_num = (1 << (FDIRENTRIES_NUM_SHIFT +
(reg & FDIRCTRL_PBALLOC_MASK)));
if (dev->data->dev_conf.fdir_conf.mode == RTE_FDIR_MODE_PERFECT)
fdir_stats->guarant_cnt = max_num - fdir_stats->free;
else if (dev->data->dev_conf.fdir_conf.mode == RTE_FDIR_MODE_SIGNATURE)
fdir_stats->guarant_cnt = max_num * 4 - fdir_stats->free;
}
/*
* ixgbe_fdir_ctrl_func - deal with all operations on flow director.
* @dev: pointer to the structure rte_eth_dev
* @filter_op:operation will be taken
* @arg: a pointer to specific structure corresponding to the filter_op
*/
int
ixgbe_fdir_ctrl_func(struct rte_eth_dev *dev,
enum rte_filter_op filter_op, void *arg)
{
struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
int ret = 0;
if (hw->mac.type != ixgbe_mac_82599EB &&
hw->mac.type != ixgbe_mac_X540 &&
hw->mac.type != ixgbe_mac_X550 &&
hw->mac.type != ixgbe_mac_X550EM_x)
return -ENOTSUP;
if (filter_op == RTE_ETH_FILTER_NOP)
return 0;
if (arg == NULL && filter_op != RTE_ETH_FILTER_FLUSH)
return -EINVAL;
switch (filter_op) {
case RTE_ETH_FILTER_ADD:
ret = ixgbe_add_del_fdir_filter(dev,
(struct rte_eth_fdir_filter *)arg, FALSE, FALSE);
break;
case RTE_ETH_FILTER_UPDATE:
ret = ixgbe_add_del_fdir_filter(dev,
(struct rte_eth_fdir_filter *)arg, FALSE, TRUE);
break;
case RTE_ETH_FILTER_DELETE:
ret = ixgbe_add_del_fdir_filter(dev,
(struct rte_eth_fdir_filter *)arg, TRUE, FALSE);
break;
case RTE_ETH_FILTER_FLUSH:
ret = ixgbe_fdir_flush(dev);
break;
case RTE_ETH_FILTER_INFO:
ixgbe_fdir_info_get(dev, (struct rte_eth_fdir_info *)arg);
break;
case RTE_ETH_FILTER_STATS:
ixgbe_fdir_stats_get(dev, (struct rte_eth_fdir_stats *)arg);
break;
default:
PMD_DRV_LOG(ERR, "unknown operation %u", filter_op);
ret = -EINVAL;
break;
}
return ret;
}