mirror of
https://github.com/torvalds/linux.git
synced 2026-08-01 12:11:59 +02:00
Merge branch 'add-flow-director-for-txgbe'
Jiawen Wu says: ==================== add flow director for txgbe Add flow director support for Wangxun 10Gb NICs. v2 -> v3: https://lore.kernel.org/all/20240605020852.24144-1-jiawenwu@trustnetic.com/ - Wrap the code at 80 chars where possible. (Jakub Kicinski) - Add function description address on kernel-doc. (Jakub Kicinski) - Correct return code. (Simon Horman) - Remove redundant size check. (Hariprasad Kelam) v1 -> v2: https://lore.kernel.org/all/20240529093821.27108-1-jiawenwu@trustnetic.com/ - Fix build warnings reported by kernel test robot. ==================== Link: https://lore.kernel.org/r/20240618101609.3580-1-jiawenwu@trustnetic.com Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
commit
e3511997d7
|
|
@ -43,6 +43,11 @@ static const struct wx_stats wx_gstrings_stats[] = {
|
|||
WX_STAT("alloc_rx_buff_failed", alloc_rx_buff_failed),
|
||||
};
|
||||
|
||||
static const struct wx_stats wx_gstrings_fdir_stats[] = {
|
||||
WX_STAT("fdir_match", stats.fdirmatch),
|
||||
WX_STAT("fdir_miss", stats.fdirmiss),
|
||||
};
|
||||
|
||||
/* drivers allocates num_tx_queues and num_rx_queues symmetrically so
|
||||
* we set the num_rx_queues to evaluate to num_tx_queues. This is
|
||||
* used because we do not have a good way to get the max number of
|
||||
|
|
@ -55,13 +60,17 @@ static const struct wx_stats wx_gstrings_stats[] = {
|
|||
(WX_NUM_TX_QUEUES + WX_NUM_RX_QUEUES) * \
|
||||
(sizeof(struct wx_queue_stats) / sizeof(u64)))
|
||||
#define WX_GLOBAL_STATS_LEN ARRAY_SIZE(wx_gstrings_stats)
|
||||
#define WX_FDIR_STATS_LEN ARRAY_SIZE(wx_gstrings_fdir_stats)
|
||||
#define WX_STATS_LEN (WX_GLOBAL_STATS_LEN + WX_QUEUE_STATS_LEN)
|
||||
|
||||
int wx_get_sset_count(struct net_device *netdev, int sset)
|
||||
{
|
||||
struct wx *wx = netdev_priv(netdev);
|
||||
|
||||
switch (sset) {
|
||||
case ETH_SS_STATS:
|
||||
return WX_STATS_LEN;
|
||||
return (wx->mac.type == wx_mac_sp) ?
|
||||
WX_STATS_LEN + WX_FDIR_STATS_LEN : WX_STATS_LEN;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
|
@ -70,6 +79,7 @@ EXPORT_SYMBOL(wx_get_sset_count);
|
|||
|
||||
void wx_get_strings(struct net_device *netdev, u32 stringset, u8 *data)
|
||||
{
|
||||
struct wx *wx = netdev_priv(netdev);
|
||||
u8 *p = data;
|
||||
int i;
|
||||
|
||||
|
|
@ -77,6 +87,10 @@ void wx_get_strings(struct net_device *netdev, u32 stringset, u8 *data)
|
|||
case ETH_SS_STATS:
|
||||
for (i = 0; i < WX_GLOBAL_STATS_LEN; i++)
|
||||
ethtool_puts(&p, wx_gstrings_stats[i].stat_string);
|
||||
if (wx->mac.type == wx_mac_sp) {
|
||||
for (i = 0; i < WX_FDIR_STATS_LEN; i++)
|
||||
ethtool_puts(&p, wx_gstrings_fdir_stats[i].stat_string);
|
||||
}
|
||||
for (i = 0; i < netdev->num_tx_queues; i++) {
|
||||
ethtool_sprintf(&p, "tx_queue_%u_packets", i);
|
||||
ethtool_sprintf(&p, "tx_queue_%u_bytes", i);
|
||||
|
|
@ -96,7 +110,7 @@ void wx_get_ethtool_stats(struct net_device *netdev,
|
|||
struct wx *wx = netdev_priv(netdev);
|
||||
struct wx_ring *ring;
|
||||
unsigned int start;
|
||||
int i, j;
|
||||
int i, j, k;
|
||||
char *p;
|
||||
|
||||
wx_update_stats(wx);
|
||||
|
|
@ -107,6 +121,13 @@ void wx_get_ethtool_stats(struct net_device *netdev,
|
|||
sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
|
||||
}
|
||||
|
||||
if (wx->mac.type == wx_mac_sp) {
|
||||
for (k = 0; k < WX_FDIR_STATS_LEN; k++) {
|
||||
p = (char *)wx + wx_gstrings_fdir_stats[k].stat_offset;
|
||||
data[i++] = *(u64 *)p;
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0; j < netdev->num_tx_queues; j++) {
|
||||
ring = wx->tx_ring[j];
|
||||
if (!ring) {
|
||||
|
|
@ -172,17 +193,21 @@ EXPORT_SYMBOL(wx_get_pause_stats);
|
|||
|
||||
void wx_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *info)
|
||||
{
|
||||
unsigned int stats_len = WX_STATS_LEN;
|
||||
struct wx *wx = netdev_priv(netdev);
|
||||
|
||||
if (wx->mac.type == wx_mac_sp)
|
||||
stats_len += WX_FDIR_STATS_LEN;
|
||||
|
||||
strscpy(info->driver, wx->driver_name, sizeof(info->driver));
|
||||
strscpy(info->fw_version, wx->eeprom_id, sizeof(info->fw_version));
|
||||
strscpy(info->bus_info, pci_name(wx->pdev), sizeof(info->bus_info));
|
||||
if (wx->num_tx_queues <= WX_NUM_TX_QUEUES) {
|
||||
info->n_stats = WX_STATS_LEN -
|
||||
info->n_stats = stats_len -
|
||||
(WX_NUM_TX_QUEUES - wx->num_tx_queues) *
|
||||
(sizeof(struct wx_queue_stats) / sizeof(u64)) * 2;
|
||||
} else {
|
||||
info->n_stats = WX_STATS_LEN;
|
||||
info->n_stats = stats_len;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(wx_get_drvinfo);
|
||||
|
|
@ -383,6 +408,9 @@ void wx_get_channels(struct net_device *dev,
|
|||
|
||||
/* record RSS queues */
|
||||
ch->combined_count = wx->ring_feature[RING_F_RSS].indices;
|
||||
|
||||
if (test_bit(WX_FLAG_FDIR_CAPABLE, wx->flags))
|
||||
ch->combined_count = wx->ring_feature[RING_F_FDIR].indices;
|
||||
}
|
||||
EXPORT_SYMBOL(wx_get_channels);
|
||||
|
||||
|
|
@ -400,6 +428,9 @@ int wx_set_channels(struct net_device *dev,
|
|||
if (count > wx_max_channels(wx))
|
||||
return -EINVAL;
|
||||
|
||||
if (test_bit(WX_FLAG_FDIR_CAPABLE, wx->flags))
|
||||
wx->ring_feature[RING_F_FDIR].limit = count;
|
||||
|
||||
wx->ring_feature[RING_F_RSS].limit = count;
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -1147,8 +1147,15 @@ static void wx_enable_rx(struct wx *wx)
|
|||
static void wx_set_rxpba(struct wx *wx)
|
||||
{
|
||||
u32 rxpktsize, txpktsize, txpbthresh;
|
||||
u32 pbsize = wx->mac.rx_pb_size;
|
||||
|
||||
rxpktsize = wx->mac.rx_pb_size << WX_RDB_PB_SZ_SHIFT;
|
||||
if (test_bit(WX_FLAG_FDIR_CAPABLE, wx->flags)) {
|
||||
if (test_bit(WX_FLAG_FDIR_HASH, wx->flags) ||
|
||||
test_bit(WX_FLAG_FDIR_PERFECT, wx->flags))
|
||||
pbsize -= 64; /* Default 64KB */
|
||||
}
|
||||
|
||||
rxpktsize = pbsize << WX_RDB_PB_SZ_SHIFT;
|
||||
wr32(wx, WX_RDB_PB_SZ(0), rxpktsize);
|
||||
|
||||
/* Only support an equally distributed Tx packet buffer strategy. */
|
||||
|
|
@ -1261,7 +1268,7 @@ static void wx_configure_port(struct wx *wx)
|
|||
* Stops the receive data path and waits for the HW to internally empty
|
||||
* the Rx security block
|
||||
**/
|
||||
static int wx_disable_sec_rx_path(struct wx *wx)
|
||||
int wx_disable_sec_rx_path(struct wx *wx)
|
||||
{
|
||||
u32 secrx;
|
||||
|
||||
|
|
@ -1271,6 +1278,7 @@ static int wx_disable_sec_rx_path(struct wx *wx)
|
|||
return read_poll_timeout(rd32, secrx, secrx & WX_RSC_ST_RSEC_RDY,
|
||||
1000, 40000, false, wx, WX_RSC_ST);
|
||||
}
|
||||
EXPORT_SYMBOL(wx_disable_sec_rx_path);
|
||||
|
||||
/**
|
||||
* wx_enable_sec_rx_path - Enables the receive data path
|
||||
|
|
@ -1278,11 +1286,12 @@ static int wx_disable_sec_rx_path(struct wx *wx)
|
|||
*
|
||||
* Enables the receive data path.
|
||||
**/
|
||||
static void wx_enable_sec_rx_path(struct wx *wx)
|
||||
void wx_enable_sec_rx_path(struct wx *wx)
|
||||
{
|
||||
wr32m(wx, WX_RSC_CTL, WX_RSC_CTL_RX_DIS, 0);
|
||||
WX_WRITE_FLUSH(wx);
|
||||
}
|
||||
EXPORT_SYMBOL(wx_enable_sec_rx_path);
|
||||
|
||||
static void wx_vlan_strip_control(struct wx *wx, bool enable)
|
||||
{
|
||||
|
|
@ -1499,6 +1508,13 @@ static void wx_configure_tx_ring(struct wx *wx,
|
|||
txdctl |= ring->count / 128 << WX_PX_TR_CFG_TR_SIZE_SHIFT;
|
||||
txdctl |= 0x20 << WX_PX_TR_CFG_WTHRESH_SHIFT;
|
||||
|
||||
ring->atr_count = 0;
|
||||
if (test_bit(WX_FLAG_FDIR_CAPABLE, wx->flags) &&
|
||||
test_bit(WX_FLAG_FDIR_HASH, wx->flags))
|
||||
ring->atr_sample_rate = wx->atr_sample_rate;
|
||||
else
|
||||
ring->atr_sample_rate = 0;
|
||||
|
||||
/* reinitialize tx_buffer_info */
|
||||
memset(ring->tx_buffer_info, 0,
|
||||
sizeof(struct wx_tx_buffer) * ring->count);
|
||||
|
|
@ -1732,7 +1748,9 @@ void wx_configure(struct wx *wx)
|
|||
|
||||
wx_set_rx_mode(wx->netdev);
|
||||
wx_restore_vlan(wx);
|
||||
wx_enable_sec_rx_path(wx);
|
||||
|
||||
if (test_bit(WX_FLAG_FDIR_CAPABLE, wx->flags))
|
||||
wx->configure_fdir(wx);
|
||||
|
||||
wx_configure_tx(wx);
|
||||
wx_configure_rx(wx);
|
||||
|
|
@ -1959,6 +1977,7 @@ int wx_sw_init(struct wx *wx)
|
|||
}
|
||||
|
||||
bitmap_zero(wx->state, WX_STATE_NBITS);
|
||||
bitmap_zero(wx->flags, WX_PF_FLAGS_NBITS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -2333,6 +2352,11 @@ void wx_update_stats(struct wx *wx)
|
|||
hwstats->b2ogprc += rd32(wx, WX_RDM_BMC2OS_CNT);
|
||||
hwstats->rdmdrop += rd32(wx, WX_RDM_DRP_PKT);
|
||||
|
||||
if (wx->mac.type == wx_mac_sp) {
|
||||
hwstats->fdirmatch += rd32(wx, WX_RDB_FDIR_MATCH);
|
||||
hwstats->fdirmiss += rd32(wx, WX_RDB_FDIR_MISS);
|
||||
}
|
||||
|
||||
for (i = 0; i < wx->mac.max_rx_queues; i++)
|
||||
hwstats->qmprc += rd32(wx, WX_PX_MPRC(i));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ void wx_mac_set_default_filter(struct wx *wx, u8 *addr);
|
|||
void wx_flush_sw_mac_table(struct wx *wx);
|
||||
int wx_set_mac(struct net_device *netdev, void *p);
|
||||
void wx_disable_rx(struct wx *wx);
|
||||
int wx_disable_sec_rx_path(struct wx *wx);
|
||||
void wx_enable_sec_rx_path(struct wx *wx);
|
||||
void wx_set_rx_mode(struct net_device *netdev);
|
||||
int wx_change_mtu(struct net_device *netdev, int new_mtu);
|
||||
void wx_disable_rx_queue(struct wx *wx, struct wx_ring *ring);
|
||||
|
|
|
|||
|
|
@ -148,10 +148,11 @@ static struct wx_dec_ptype wx_ptype_lookup[256] = {
|
|||
[0xFD] = WX_PTT(IP, IPV6, IGMV, IPV6, SCTP, PAY4),
|
||||
};
|
||||
|
||||
static struct wx_dec_ptype wx_decode_ptype(const u8 ptype)
|
||||
struct wx_dec_ptype wx_decode_ptype(const u8 ptype)
|
||||
{
|
||||
return wx_ptype_lookup[ptype];
|
||||
}
|
||||
EXPORT_SYMBOL(wx_decode_ptype);
|
||||
|
||||
/* wx_test_staterr - tests bits in Rx descriptor status and error fields */
|
||||
static __le32 wx_test_staterr(union wx_rx_desc *rx_desc,
|
||||
|
|
@ -1453,6 +1454,7 @@ static void wx_tx_csum(struct wx_ring *tx_ring, struct wx_tx_buffer *first,
|
|||
static netdev_tx_t wx_xmit_frame_ring(struct sk_buff *skb,
|
||||
struct wx_ring *tx_ring)
|
||||
{
|
||||
struct wx *wx = netdev_priv(tx_ring->netdev);
|
||||
u16 count = TXD_USE_COUNT(skb_headlen(skb));
|
||||
struct wx_tx_buffer *first;
|
||||
u8 hdr_len = 0, ptype;
|
||||
|
|
@ -1498,6 +1500,10 @@ static netdev_tx_t wx_xmit_frame_ring(struct sk_buff *skb,
|
|||
goto out_drop;
|
||||
else if (!tso)
|
||||
wx_tx_csum(tx_ring, first, ptype);
|
||||
|
||||
if (test_bit(WX_FLAG_FDIR_CAPABLE, wx->flags) && tx_ring->atr_sample_rate)
|
||||
wx->atr(tx_ring, first, ptype);
|
||||
|
||||
wx_tx_map(tx_ring, first, hdr_len);
|
||||
|
||||
return NETDEV_TX_OK;
|
||||
|
|
@ -1574,8 +1580,27 @@ static void wx_set_rss_queues(struct wx *wx)
|
|||
f = &wx->ring_feature[RING_F_RSS];
|
||||
f->indices = f->limit;
|
||||
|
||||
wx->num_rx_queues = f->limit;
|
||||
wx->num_tx_queues = f->limit;
|
||||
if (!(test_bit(WX_FLAG_FDIR_CAPABLE, wx->flags)))
|
||||
goto out;
|
||||
|
||||
clear_bit(WX_FLAG_FDIR_HASH, wx->flags);
|
||||
|
||||
/* Use Flow Director in addition to RSS to ensure the best
|
||||
* distribution of flows across cores, even when an FDIR flow
|
||||
* isn't matched.
|
||||
*/
|
||||
if (f->indices > 1) {
|
||||
f = &wx->ring_feature[RING_F_FDIR];
|
||||
|
||||
f->indices = f->limit;
|
||||
|
||||
if (!(test_bit(WX_FLAG_FDIR_PERFECT, wx->flags)))
|
||||
set_bit(WX_FLAG_FDIR_HASH, wx->flags);
|
||||
}
|
||||
|
||||
out:
|
||||
wx->num_rx_queues = f->indices;
|
||||
wx->num_tx_queues = f->indices;
|
||||
}
|
||||
|
||||
static void wx_set_num_queues(struct wx *wx)
|
||||
|
|
@ -2680,6 +2705,7 @@ int wx_set_features(struct net_device *netdev, netdev_features_t features)
|
|||
{
|
||||
netdev_features_t changed = netdev->features ^ features;
|
||||
struct wx *wx = netdev_priv(netdev);
|
||||
bool need_reset = false;
|
||||
|
||||
if (features & NETIF_F_RXHASH) {
|
||||
wr32m(wx, WX_RDB_RA_CTL, WX_RDB_RA_CTL_RSS_EN,
|
||||
|
|
@ -2697,6 +2723,36 @@ int wx_set_features(struct net_device *netdev, netdev_features_t features)
|
|||
else if (changed & (NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_FILTER))
|
||||
wx_set_rx_mode(netdev);
|
||||
|
||||
if (!(test_bit(WX_FLAG_FDIR_CAPABLE, wx->flags)))
|
||||
return 0;
|
||||
|
||||
/* Check if Flow Director n-tuple support was enabled or disabled. If
|
||||
* the state changed, we need to reset.
|
||||
*/
|
||||
switch (features & NETIF_F_NTUPLE) {
|
||||
case NETIF_F_NTUPLE:
|
||||
/* turn off ATR, enable perfect filters and reset */
|
||||
if (!(test_and_set_bit(WX_FLAG_FDIR_PERFECT, wx->flags)))
|
||||
need_reset = true;
|
||||
|
||||
clear_bit(WX_FLAG_FDIR_HASH, wx->flags);
|
||||
break;
|
||||
default:
|
||||
/* turn off perfect filters, enable ATR and reset */
|
||||
if (test_and_clear_bit(WX_FLAG_FDIR_PERFECT, wx->flags))
|
||||
need_reset = true;
|
||||
|
||||
/* We cannot enable ATR if RSS is disabled */
|
||||
if (wx->ring_feature[RING_F_RSS].limit <= 1)
|
||||
break;
|
||||
|
||||
set_bit(WX_FLAG_FDIR_HASH, wx->flags);
|
||||
break;
|
||||
}
|
||||
|
||||
if (need_reset)
|
||||
wx->do_reset(netdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(wx_set_features);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#ifndef _WX_LIB_H_
|
||||
#define _WX_LIB_H_
|
||||
|
||||
struct wx_dec_ptype wx_decode_ptype(const u8 ptype);
|
||||
void wx_alloc_rx_buffers(struct wx_ring *rx_ring, u16 cleaned_count);
|
||||
u16 wx_desc_unused(struct wx_ring *ring);
|
||||
netdev_tx_t wx_xmit_frame(struct sk_buff *skb,
|
||||
|
|
|
|||
|
|
@ -157,6 +157,8 @@
|
|||
#define WX_RDB_RA_CTL_RSS_IPV6_TCP BIT(21)
|
||||
#define WX_RDB_RA_CTL_RSS_IPV4_UDP BIT(22)
|
||||
#define WX_RDB_RA_CTL_RSS_IPV6_UDP BIT(23)
|
||||
#define WX_RDB_FDIR_MATCH 0x19558
|
||||
#define WX_RDB_FDIR_MISS 0x1955C
|
||||
|
||||
/******************************* PSR Registers *******************************/
|
||||
/* psr control */
|
||||
|
|
@ -503,6 +505,34 @@ enum WX_MSCA_CMD_value {
|
|||
#define WX_PTYPE_TYP_TCP 0x04
|
||||
#define WX_PTYPE_TYP_SCTP 0x05
|
||||
|
||||
/* Packet type non-ip values */
|
||||
enum wx_l2_ptypes {
|
||||
WX_PTYPE_L2_ABORTED = (WX_PTYPE_PKT_MAC),
|
||||
WX_PTYPE_L2_MAC = (WX_PTYPE_PKT_MAC | WX_PTYPE_TYP_MAC),
|
||||
|
||||
WX_PTYPE_L2_IPV4_FRAG = (WX_PTYPE_PKT_IP | WX_PTYPE_TYP_IPFRAG),
|
||||
WX_PTYPE_L2_IPV4 = (WX_PTYPE_PKT_IP | WX_PTYPE_TYP_IP),
|
||||
WX_PTYPE_L2_IPV4_UDP = (WX_PTYPE_PKT_IP | WX_PTYPE_TYP_UDP),
|
||||
WX_PTYPE_L2_IPV4_TCP = (WX_PTYPE_PKT_IP | WX_PTYPE_TYP_TCP),
|
||||
WX_PTYPE_L2_IPV4_SCTP = (WX_PTYPE_PKT_IP | WX_PTYPE_TYP_SCTP),
|
||||
WX_PTYPE_L2_IPV6_FRAG = (WX_PTYPE_PKT_IP | WX_PTYPE_PKT_IPV6 |
|
||||
WX_PTYPE_TYP_IPFRAG),
|
||||
WX_PTYPE_L2_IPV6 = (WX_PTYPE_PKT_IP | WX_PTYPE_PKT_IPV6 |
|
||||
WX_PTYPE_TYP_IP),
|
||||
WX_PTYPE_L2_IPV6_UDP = (WX_PTYPE_PKT_IP | WX_PTYPE_PKT_IPV6 |
|
||||
WX_PTYPE_TYP_UDP),
|
||||
WX_PTYPE_L2_IPV6_TCP = (WX_PTYPE_PKT_IP | WX_PTYPE_PKT_IPV6 |
|
||||
WX_PTYPE_TYP_TCP),
|
||||
WX_PTYPE_L2_IPV6_SCTP = (WX_PTYPE_PKT_IP | WX_PTYPE_PKT_IPV6 |
|
||||
WX_PTYPE_TYP_SCTP),
|
||||
|
||||
WX_PTYPE_L2_TUN4_MAC = (WX_PTYPE_TUN_IPV4 | WX_PTYPE_PKT_IGM),
|
||||
WX_PTYPE_L2_TUN6_MAC = (WX_PTYPE_TUN_IPV6 | WX_PTYPE_PKT_IGM),
|
||||
};
|
||||
|
||||
#define WX_PTYPE_PKT(_pt) ((_pt) & 0x30)
|
||||
#define WX_PTYPE_TYPL4(_pt) ((_pt) & 0x07)
|
||||
|
||||
#define WX_RXD_PKTTYPE(_rxd) \
|
||||
((le32_to_cpu((_rxd)->wb.lower.lo_dword.data) >> 9) & 0xFF)
|
||||
#define WX_RXD_IPV6EX(_rxd) \
|
||||
|
|
@ -552,6 +582,9 @@ enum wx_tx_flags {
|
|||
WX_TX_FLAGS_OUTER_IPV4 = 0x100,
|
||||
WX_TX_FLAGS_LINKSEC = 0x200,
|
||||
WX_TX_FLAGS_IPSEC = 0x400,
|
||||
|
||||
/* software defined flags */
|
||||
WX_TX_FLAGS_SW_VLAN = 0x40,
|
||||
};
|
||||
|
||||
/* VLAN info */
|
||||
|
|
@ -900,7 +933,13 @@ struct wx_ring {
|
|||
*/
|
||||
u16 next_to_use;
|
||||
u16 next_to_clean;
|
||||
u16 next_to_alloc;
|
||||
union {
|
||||
u16 next_to_alloc;
|
||||
struct {
|
||||
u8 atr_sample_rate;
|
||||
u8 atr_count;
|
||||
};
|
||||
};
|
||||
|
||||
struct wx_queue_stats stats;
|
||||
struct u64_stats_sync syncp;
|
||||
|
|
@ -939,6 +978,7 @@ struct wx_ring_feature {
|
|||
enum wx_ring_f_enum {
|
||||
RING_F_NONE = 0,
|
||||
RING_F_RSS,
|
||||
RING_F_FDIR,
|
||||
RING_F_ARRAY_SIZE /* must be last in enum set */
|
||||
};
|
||||
|
||||
|
|
@ -980,15 +1020,26 @@ struct wx_hw_stats {
|
|||
u64 crcerrs;
|
||||
u64 rlec;
|
||||
u64 qmprc;
|
||||
u64 fdirmatch;
|
||||
u64 fdirmiss;
|
||||
};
|
||||
|
||||
enum wx_state {
|
||||
WX_STATE_RESETTING,
|
||||
WX_STATE_NBITS, /* must be last */
|
||||
};
|
||||
|
||||
enum wx_pf_flags {
|
||||
WX_FLAG_FDIR_CAPABLE,
|
||||
WX_FLAG_FDIR_HASH,
|
||||
WX_FLAG_FDIR_PERFECT,
|
||||
WX_PF_FLAGS_NBITS /* must be last */
|
||||
};
|
||||
|
||||
struct wx {
|
||||
unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
|
||||
DECLARE_BITMAP(state, WX_STATE_NBITS);
|
||||
DECLARE_BITMAP(flags, WX_PF_FLAGS_NBITS);
|
||||
|
||||
void *priv;
|
||||
u8 __iomem *hw_addr;
|
||||
|
|
@ -1077,6 +1128,9 @@ struct wx {
|
|||
u64 hw_csum_rx_error;
|
||||
u64 alloc_rx_buff_failed;
|
||||
|
||||
u32 atr_sample_rate;
|
||||
void (*atr)(struct wx_ring *ring, struct wx_tx_buffer *first, u8 ptype);
|
||||
void (*configure_fdir)(struct wx *wx);
|
||||
void (*do_reset)(struct net_device *netdev);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -10,4 +10,5 @@ txgbe-objs := txgbe_main.o \
|
|||
txgbe_hw.o \
|
||||
txgbe_phy.o \
|
||||
txgbe_irq.o \
|
||||
txgbe_fdir.o \
|
||||
txgbe_ethtool.o
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include "../libwx/wx_type.h"
|
||||
#include "../libwx/wx_lib.h"
|
||||
#include "txgbe_type.h"
|
||||
#include "txgbe_fdir.h"
|
||||
#include "txgbe_ethtool.h"
|
||||
|
||||
static int txgbe_set_ringparam(struct net_device *netdev,
|
||||
|
|
@ -79,6 +80,430 @@ static int txgbe_set_channels(struct net_device *dev,
|
|||
return txgbe_setup_tc(dev, netdev_get_num_tc(dev));
|
||||
}
|
||||
|
||||
static int txgbe_get_ethtool_fdir_entry(struct txgbe *txgbe,
|
||||
struct ethtool_rxnfc *cmd)
|
||||
{
|
||||
struct ethtool_rx_flow_spec *fsp =
|
||||
(struct ethtool_rx_flow_spec *)&cmd->fs;
|
||||
union txgbe_atr_input *mask = &txgbe->fdir_mask;
|
||||
struct txgbe_fdir_filter *rule = NULL;
|
||||
struct hlist_node *node;
|
||||
|
||||
/* report total rule count */
|
||||
cmd->data = (1024 << TXGBE_FDIR_PBALLOC_64K) - 2;
|
||||
|
||||
hlist_for_each_entry_safe(rule, node, &txgbe->fdir_filter_list,
|
||||
fdir_node) {
|
||||
if (fsp->location <= rule->sw_idx)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!rule || fsp->location != rule->sw_idx)
|
||||
return -EINVAL;
|
||||
|
||||
/* set flow type field */
|
||||
switch (rule->filter.formatted.flow_type) {
|
||||
case TXGBE_ATR_FLOW_TYPE_TCPV4:
|
||||
fsp->flow_type = TCP_V4_FLOW;
|
||||
break;
|
||||
case TXGBE_ATR_FLOW_TYPE_UDPV4:
|
||||
fsp->flow_type = UDP_V4_FLOW;
|
||||
break;
|
||||
case TXGBE_ATR_FLOW_TYPE_SCTPV4:
|
||||
fsp->flow_type = SCTP_V4_FLOW;
|
||||
break;
|
||||
case TXGBE_ATR_FLOW_TYPE_IPV4:
|
||||
fsp->flow_type = IP_USER_FLOW;
|
||||
fsp->h_u.usr_ip4_spec.ip_ver = ETH_RX_NFC_IP4;
|
||||
fsp->h_u.usr_ip4_spec.proto = 0;
|
||||
fsp->m_u.usr_ip4_spec.proto = 0;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fsp->h_u.tcp_ip4_spec.psrc = rule->filter.formatted.src_port;
|
||||
fsp->m_u.tcp_ip4_spec.psrc = mask->formatted.src_port;
|
||||
fsp->h_u.tcp_ip4_spec.pdst = rule->filter.formatted.dst_port;
|
||||
fsp->m_u.tcp_ip4_spec.pdst = mask->formatted.dst_port;
|
||||
fsp->h_u.tcp_ip4_spec.ip4src = rule->filter.formatted.src_ip[0];
|
||||
fsp->m_u.tcp_ip4_spec.ip4src = mask->formatted.src_ip[0];
|
||||
fsp->h_u.tcp_ip4_spec.ip4dst = rule->filter.formatted.dst_ip[0];
|
||||
fsp->m_u.tcp_ip4_spec.ip4dst = mask->formatted.dst_ip[0];
|
||||
fsp->h_ext.vlan_etype = rule->filter.formatted.flex_bytes;
|
||||
fsp->m_ext.vlan_etype = mask->formatted.flex_bytes;
|
||||
fsp->h_ext.data[1] = htonl(rule->filter.formatted.vm_pool);
|
||||
fsp->m_ext.data[1] = htonl(mask->formatted.vm_pool);
|
||||
fsp->flow_type |= FLOW_EXT;
|
||||
|
||||
/* record action */
|
||||
if (rule->action == TXGBE_RDB_FDIR_DROP_QUEUE)
|
||||
fsp->ring_cookie = RX_CLS_FLOW_DISC;
|
||||
else
|
||||
fsp->ring_cookie = rule->action;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int txgbe_get_ethtool_fdir_all(struct txgbe *txgbe,
|
||||
struct ethtool_rxnfc *cmd,
|
||||
u32 *rule_locs)
|
||||
{
|
||||
struct txgbe_fdir_filter *rule;
|
||||
struct hlist_node *node;
|
||||
int cnt = 0;
|
||||
|
||||
/* report total rule count */
|
||||
cmd->data = (1024 << TXGBE_FDIR_PBALLOC_64K) - 2;
|
||||
|
||||
hlist_for_each_entry_safe(rule, node, &txgbe->fdir_filter_list,
|
||||
fdir_node) {
|
||||
if (cnt == cmd->rule_cnt)
|
||||
return -EMSGSIZE;
|
||||
rule_locs[cnt] = rule->sw_idx;
|
||||
cnt++;
|
||||
}
|
||||
|
||||
cmd->rule_cnt = cnt;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int txgbe_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
|
||||
u32 *rule_locs)
|
||||
{
|
||||
struct wx *wx = netdev_priv(dev);
|
||||
struct txgbe *txgbe = wx->priv;
|
||||
int ret = -EOPNOTSUPP;
|
||||
|
||||
switch (cmd->cmd) {
|
||||
case ETHTOOL_GRXRINGS:
|
||||
cmd->data = wx->num_rx_queues;
|
||||
ret = 0;
|
||||
break;
|
||||
case ETHTOOL_GRXCLSRLCNT:
|
||||
cmd->rule_cnt = txgbe->fdir_filter_count;
|
||||
ret = 0;
|
||||
break;
|
||||
case ETHTOOL_GRXCLSRULE:
|
||||
ret = txgbe_get_ethtool_fdir_entry(txgbe, cmd);
|
||||
break;
|
||||
case ETHTOOL_GRXCLSRLALL:
|
||||
ret = txgbe_get_ethtool_fdir_all(txgbe, cmd, (u32 *)rule_locs);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int txgbe_flowspec_to_flow_type(struct ethtool_rx_flow_spec *fsp,
|
||||
u8 *flow_type)
|
||||
{
|
||||
switch (fsp->flow_type & ~FLOW_EXT) {
|
||||
case TCP_V4_FLOW:
|
||||
*flow_type = TXGBE_ATR_FLOW_TYPE_TCPV4;
|
||||
break;
|
||||
case UDP_V4_FLOW:
|
||||
*flow_type = TXGBE_ATR_FLOW_TYPE_UDPV4;
|
||||
break;
|
||||
case SCTP_V4_FLOW:
|
||||
*flow_type = TXGBE_ATR_FLOW_TYPE_SCTPV4;
|
||||
break;
|
||||
case IP_USER_FLOW:
|
||||
switch (fsp->h_u.usr_ip4_spec.proto) {
|
||||
case IPPROTO_TCP:
|
||||
*flow_type = TXGBE_ATR_FLOW_TYPE_TCPV4;
|
||||
break;
|
||||
case IPPROTO_UDP:
|
||||
*flow_type = TXGBE_ATR_FLOW_TYPE_UDPV4;
|
||||
break;
|
||||
case IPPROTO_SCTP:
|
||||
*flow_type = TXGBE_ATR_FLOW_TYPE_SCTPV4;
|
||||
break;
|
||||
case 0:
|
||||
if (!fsp->m_u.usr_ip4_spec.proto) {
|
||||
*flow_type = TXGBE_ATR_FLOW_TYPE_IPV4;
|
||||
break;
|
||||
}
|
||||
fallthrough;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool txgbe_match_ethtool_fdir_entry(struct txgbe *txgbe,
|
||||
struct txgbe_fdir_filter *input)
|
||||
{
|
||||
struct txgbe_fdir_filter *rule = NULL;
|
||||
struct hlist_node *node2;
|
||||
|
||||
hlist_for_each_entry_safe(rule, node2, &txgbe->fdir_filter_list,
|
||||
fdir_node) {
|
||||
if (rule->filter.formatted.bkt_hash ==
|
||||
input->filter.formatted.bkt_hash &&
|
||||
rule->action == input->action) {
|
||||
wx_dbg(txgbe->wx, "FDIR entry already exist\n");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int txgbe_update_ethtool_fdir_entry(struct txgbe *txgbe,
|
||||
struct txgbe_fdir_filter *input,
|
||||
u16 sw_idx)
|
||||
{
|
||||
struct hlist_node *node = NULL, *parent = NULL;
|
||||
struct txgbe_fdir_filter *rule;
|
||||
struct wx *wx = txgbe->wx;
|
||||
bool deleted = false;
|
||||
int err;
|
||||
|
||||
hlist_for_each_entry_safe(rule, node, &txgbe->fdir_filter_list,
|
||||
fdir_node) {
|
||||
/* hash found, or no matching entry */
|
||||
if (rule->sw_idx >= sw_idx)
|
||||
break;
|
||||
parent = node;
|
||||
}
|
||||
|
||||
/* if there is an old rule occupying our place remove it */
|
||||
if (rule && rule->sw_idx == sw_idx) {
|
||||
/* hardware filters are only configured when interface is up,
|
||||
* and we should not issue filter commands while the interface
|
||||
* is down
|
||||
*/
|
||||
if (netif_running(wx->netdev) &&
|
||||
(!input || rule->filter.formatted.bkt_hash !=
|
||||
input->filter.formatted.bkt_hash)) {
|
||||
err = txgbe_fdir_erase_perfect_filter(wx,
|
||||
&rule->filter,
|
||||
sw_idx);
|
||||
if (err)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
hlist_del(&rule->fdir_node);
|
||||
kfree(rule);
|
||||
txgbe->fdir_filter_count--;
|
||||
deleted = true;
|
||||
}
|
||||
|
||||
/* If we weren't given an input, then this was a request to delete a
|
||||
* filter. We should return -EINVAL if the filter wasn't found, but
|
||||
* return 0 if the rule was successfully deleted.
|
||||
*/
|
||||
if (!input)
|
||||
return deleted ? 0 : -EINVAL;
|
||||
|
||||
/* initialize node and set software index */
|
||||
INIT_HLIST_NODE(&input->fdir_node);
|
||||
|
||||
/* add filter to the list */
|
||||
if (parent)
|
||||
hlist_add_behind(&input->fdir_node, parent);
|
||||
else
|
||||
hlist_add_head(&input->fdir_node,
|
||||
&txgbe->fdir_filter_list);
|
||||
|
||||
/* update counts */
|
||||
txgbe->fdir_filter_count++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int txgbe_add_ethtool_fdir_entry(struct txgbe *txgbe,
|
||||
struct ethtool_rxnfc *cmd)
|
||||
{
|
||||
struct ethtool_rx_flow_spec *fsp =
|
||||
(struct ethtool_rx_flow_spec *)&cmd->fs;
|
||||
struct txgbe_fdir_filter *input;
|
||||
union txgbe_atr_input mask;
|
||||
struct wx *wx = txgbe->wx;
|
||||
int err = -EINVAL;
|
||||
u16 ptype = 0;
|
||||
u8 queue;
|
||||
|
||||
if (!(test_bit(WX_FLAG_FDIR_PERFECT, wx->flags)))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* ring_cookie is a masked into a set of queues and txgbe pools or
|
||||
* we use drop index
|
||||
*/
|
||||
if (fsp->ring_cookie == RX_CLS_FLOW_DISC) {
|
||||
queue = TXGBE_RDB_FDIR_DROP_QUEUE;
|
||||
} else {
|
||||
u32 ring = ethtool_get_flow_spec_ring(fsp->ring_cookie);
|
||||
|
||||
if (ring >= wx->num_rx_queues)
|
||||
return -EINVAL;
|
||||
|
||||
/* Map the ring onto the absolute queue index */
|
||||
queue = wx->rx_ring[ring]->reg_idx;
|
||||
}
|
||||
|
||||
/* Don't allow indexes to exist outside of available space */
|
||||
if (fsp->location >= ((1024 << TXGBE_FDIR_PBALLOC_64K) - 2)) {
|
||||
wx_err(wx, "Location out of range\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
input = kzalloc(sizeof(*input), GFP_ATOMIC);
|
||||
if (!input)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(&mask, 0, sizeof(union txgbe_atr_input));
|
||||
|
||||
/* set SW index */
|
||||
input->sw_idx = fsp->location;
|
||||
|
||||
/* record flow type */
|
||||
if (txgbe_flowspec_to_flow_type(fsp,
|
||||
&input->filter.formatted.flow_type)) {
|
||||
wx_err(wx, "Unrecognized flow type\n");
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
mask.formatted.flow_type = TXGBE_ATR_L4TYPE_IPV6_MASK |
|
||||
TXGBE_ATR_L4TYPE_MASK;
|
||||
|
||||
if (input->filter.formatted.flow_type == TXGBE_ATR_FLOW_TYPE_IPV4)
|
||||
mask.formatted.flow_type &= TXGBE_ATR_L4TYPE_IPV6_MASK;
|
||||
|
||||
/* Copy input into formatted structures */
|
||||
input->filter.formatted.src_ip[0] = fsp->h_u.tcp_ip4_spec.ip4src;
|
||||
mask.formatted.src_ip[0] = fsp->m_u.tcp_ip4_spec.ip4src;
|
||||
input->filter.formatted.dst_ip[0] = fsp->h_u.tcp_ip4_spec.ip4dst;
|
||||
mask.formatted.dst_ip[0] = fsp->m_u.tcp_ip4_spec.ip4dst;
|
||||
input->filter.formatted.src_port = fsp->h_u.tcp_ip4_spec.psrc;
|
||||
mask.formatted.src_port = fsp->m_u.tcp_ip4_spec.psrc;
|
||||
input->filter.formatted.dst_port = fsp->h_u.tcp_ip4_spec.pdst;
|
||||
mask.formatted.dst_port = fsp->m_u.tcp_ip4_spec.pdst;
|
||||
|
||||
if (fsp->flow_type & FLOW_EXT) {
|
||||
input->filter.formatted.vm_pool =
|
||||
(unsigned char)ntohl(fsp->h_ext.data[1]);
|
||||
mask.formatted.vm_pool =
|
||||
(unsigned char)ntohl(fsp->m_ext.data[1]);
|
||||
input->filter.formatted.flex_bytes =
|
||||
fsp->h_ext.vlan_etype;
|
||||
mask.formatted.flex_bytes = fsp->m_ext.vlan_etype;
|
||||
}
|
||||
|
||||
switch (input->filter.formatted.flow_type) {
|
||||
case TXGBE_ATR_FLOW_TYPE_TCPV4:
|
||||
ptype = WX_PTYPE_L2_IPV4_TCP;
|
||||
break;
|
||||
case TXGBE_ATR_FLOW_TYPE_UDPV4:
|
||||
ptype = WX_PTYPE_L2_IPV4_UDP;
|
||||
break;
|
||||
case TXGBE_ATR_FLOW_TYPE_SCTPV4:
|
||||
ptype = WX_PTYPE_L2_IPV4_SCTP;
|
||||
break;
|
||||
case TXGBE_ATR_FLOW_TYPE_IPV4:
|
||||
ptype = WX_PTYPE_L2_IPV4;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
input->filter.formatted.vlan_id = htons(ptype);
|
||||
if (mask.formatted.flow_type & TXGBE_ATR_L4TYPE_MASK)
|
||||
mask.formatted.vlan_id = htons(0xFFFF);
|
||||
else
|
||||
mask.formatted.vlan_id = htons(0xFFF8);
|
||||
|
||||
/* determine if we need to drop or route the packet */
|
||||
if (fsp->ring_cookie == RX_CLS_FLOW_DISC)
|
||||
input->action = TXGBE_RDB_FDIR_DROP_QUEUE;
|
||||
else
|
||||
input->action = fsp->ring_cookie;
|
||||
|
||||
spin_lock(&txgbe->fdir_perfect_lock);
|
||||
|
||||
if (hlist_empty(&txgbe->fdir_filter_list)) {
|
||||
/* save mask and program input mask into HW */
|
||||
memcpy(&txgbe->fdir_mask, &mask, sizeof(mask));
|
||||
err = txgbe_fdir_set_input_mask(wx, &mask);
|
||||
if (err)
|
||||
goto err_unlock;
|
||||
} else if (memcmp(&txgbe->fdir_mask, &mask, sizeof(mask))) {
|
||||
wx_err(wx, "Hardware only supports one mask per port. To change the mask you must first delete all the rules.\n");
|
||||
goto err_unlock;
|
||||
}
|
||||
|
||||
/* apply mask and compute/store hash */
|
||||
txgbe_atr_compute_perfect_hash(&input->filter, &mask);
|
||||
|
||||
/* check if new entry does not exist on filter list */
|
||||
if (txgbe_match_ethtool_fdir_entry(txgbe, input))
|
||||
goto err_unlock;
|
||||
|
||||
/* only program filters to hardware if the net device is running, as
|
||||
* we store the filters in the Rx buffer which is not allocated when
|
||||
* the device is down
|
||||
*/
|
||||
if (netif_running(wx->netdev)) {
|
||||
err = txgbe_fdir_write_perfect_filter(wx, &input->filter,
|
||||
input->sw_idx, queue);
|
||||
if (err)
|
||||
goto err_unlock;
|
||||
}
|
||||
|
||||
txgbe_update_ethtool_fdir_entry(txgbe, input, input->sw_idx);
|
||||
|
||||
spin_unlock(&txgbe->fdir_perfect_lock);
|
||||
|
||||
return 0;
|
||||
err_unlock:
|
||||
spin_unlock(&txgbe->fdir_perfect_lock);
|
||||
err_out:
|
||||
kfree(input);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int txgbe_del_ethtool_fdir_entry(struct txgbe *txgbe,
|
||||
struct ethtool_rxnfc *cmd)
|
||||
{
|
||||
struct ethtool_rx_flow_spec *fsp =
|
||||
(struct ethtool_rx_flow_spec *)&cmd->fs;
|
||||
int err = 0;
|
||||
|
||||
spin_lock(&txgbe->fdir_perfect_lock);
|
||||
err = txgbe_update_ethtool_fdir_entry(txgbe, NULL, fsp->location);
|
||||
spin_unlock(&txgbe->fdir_perfect_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int txgbe_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd)
|
||||
{
|
||||
struct wx *wx = netdev_priv(dev);
|
||||
struct txgbe *txgbe = wx->priv;
|
||||
int ret = -EOPNOTSUPP;
|
||||
|
||||
switch (cmd->cmd) {
|
||||
case ETHTOOL_SRXCLSRLINS:
|
||||
ret = txgbe_add_ethtool_fdir_entry(txgbe, cmd);
|
||||
break;
|
||||
case ETHTOOL_SRXCLSRLDEL:
|
||||
ret = txgbe_del_ethtool_fdir_entry(txgbe, cmd);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct ethtool_ops txgbe_ethtool_ops = {
|
||||
.supported_coalesce_params = ETHTOOL_COALESCE_USECS |
|
||||
ETHTOOL_COALESCE_TX_MAX_FRAMES_IRQ,
|
||||
|
|
@ -100,6 +525,8 @@ static const struct ethtool_ops txgbe_ethtool_ops = {
|
|||
.set_coalesce = wx_set_coalesce,
|
||||
.get_channels = wx_get_channels,
|
||||
.set_channels = txgbe_set_channels,
|
||||
.get_rxnfc = txgbe_get_rxnfc,
|
||||
.set_rxnfc = txgbe_set_rxnfc,
|
||||
.get_msglevel = wx_get_msglevel,
|
||||
.set_msglevel = wx_set_msglevel,
|
||||
};
|
||||
|
|
|
|||
643
drivers/net/ethernet/wangxun/txgbe/txgbe_fdir.c
Normal file
643
drivers/net/ethernet/wangxun/txgbe/txgbe_fdir.c
Normal file
|
|
@ -0,0 +1,643 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2015 - 2024 Beijing WangXun Technology Co., Ltd. */
|
||||
|
||||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "../libwx/wx_type.h"
|
||||
#include "../libwx/wx_lib.h"
|
||||
#include "../libwx/wx_hw.h"
|
||||
#include "txgbe_type.h"
|
||||
#include "txgbe_fdir.h"
|
||||
|
||||
/* These defines allow us to quickly generate all of the necessary instructions
|
||||
* in the function below by simply calling out TXGBE_COMPUTE_SIG_HASH_ITERATION
|
||||
* for values 0 through 15
|
||||
*/
|
||||
#define TXGBE_ATR_COMMON_HASH_KEY \
|
||||
(TXGBE_ATR_BUCKET_HASH_KEY & TXGBE_ATR_SIGNATURE_HASH_KEY)
|
||||
#define TXGBE_COMPUTE_SIG_HASH_ITERATION(_n) \
|
||||
do { \
|
||||
u32 n = (_n); \
|
||||
if (TXGBE_ATR_COMMON_HASH_KEY & (0x01 << n)) \
|
||||
common_hash ^= lo_hash_dword >> n; \
|
||||
else if (TXGBE_ATR_BUCKET_HASH_KEY & (0x01 << n)) \
|
||||
bucket_hash ^= lo_hash_dword >> n; \
|
||||
else if (TXGBE_ATR_SIGNATURE_HASH_KEY & (0x01 << n)) \
|
||||
sig_hash ^= lo_hash_dword << (16 - n); \
|
||||
if (TXGBE_ATR_COMMON_HASH_KEY & (0x01 << (n + 16))) \
|
||||
common_hash ^= hi_hash_dword >> n; \
|
||||
else if (TXGBE_ATR_BUCKET_HASH_KEY & (0x01 << (n + 16))) \
|
||||
bucket_hash ^= hi_hash_dword >> n; \
|
||||
else if (TXGBE_ATR_SIGNATURE_HASH_KEY & (0x01 << (n + 16))) \
|
||||
sig_hash ^= hi_hash_dword << (16 - n); \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* txgbe_atr_compute_sig_hash - Compute the signature hash
|
||||
* @input: input bitstream to compute the hash on
|
||||
* @common: compressed common input dword
|
||||
* @hash: pointer to the computed hash
|
||||
*
|
||||
* This function is almost identical to the function above but contains
|
||||
* several optimizations such as unwinding all of the loops, letting the
|
||||
* compiler work out all of the conditional ifs since the keys are static
|
||||
* defines, and computing two keys at once since the hashed dword stream
|
||||
* will be the same for both keys.
|
||||
**/
|
||||
static void txgbe_atr_compute_sig_hash(union txgbe_atr_hash_dword input,
|
||||
union txgbe_atr_hash_dword common,
|
||||
u32 *hash)
|
||||
{
|
||||
u32 sig_hash = 0, bucket_hash = 0, common_hash = 0;
|
||||
u32 hi_hash_dword, lo_hash_dword, flow_vm_vlan;
|
||||
u32 i;
|
||||
|
||||
/* record the flow_vm_vlan bits as they are a key part to the hash */
|
||||
flow_vm_vlan = ntohl(input.dword);
|
||||
|
||||
/* generate common hash dword */
|
||||
hi_hash_dword = ntohl(common.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 */
|
||||
TXGBE_COMPUTE_SIG_HASH_ITERATION(0);
|
||||
|
||||
/* 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 remaining 30 bit of the key */
|
||||
for (i = 1; i <= 15; i++)
|
||||
TXGBE_COMPUTE_SIG_HASH_ITERATION(i);
|
||||
|
||||
/* combine common_hash result with signature and bucket hashes */
|
||||
bucket_hash ^= common_hash;
|
||||
bucket_hash &= TXGBE_ATR_HASH_MASK;
|
||||
|
||||
sig_hash ^= common_hash << 16;
|
||||
sig_hash &= TXGBE_ATR_HASH_MASK << 16;
|
||||
|
||||
/* return completed signature hash */
|
||||
*hash = sig_hash ^ bucket_hash;
|
||||
}
|
||||
|
||||
#define TXGBE_COMPUTE_BKT_HASH_ITERATION(_n) \
|
||||
do { \
|
||||
u32 n = (_n); \
|
||||
if (TXGBE_ATR_BUCKET_HASH_KEY & (0x01 << n)) \
|
||||
bucket_hash ^= lo_hash_dword >> n; \
|
||||
if (TXGBE_ATR_BUCKET_HASH_KEY & (0x01 << (n + 16))) \
|
||||
bucket_hash ^= hi_hash_dword >> n; \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* txgbe_atr_compute_perfect_hash - Compute the perfect filter hash
|
||||
* @input: input bitstream to compute the hash on
|
||||
* @input_mask: mask for the input bitstream
|
||||
*
|
||||
* This function serves two main purposes. First it applies the input_mask
|
||||
* to the atr_input resulting in a cleaned up atr_input data stream.
|
||||
* Secondly it computes the hash and stores it in the bkt_hash field at
|
||||
* the end of the input byte stream. This way it will be available for
|
||||
* future use without needing to recompute the hash.
|
||||
**/
|
||||
void txgbe_atr_compute_perfect_hash(union txgbe_atr_input *input,
|
||||
union txgbe_atr_input *input_mask)
|
||||
{
|
||||
u32 hi_hash_dword, lo_hash_dword, flow_vm_vlan;
|
||||
u32 bucket_hash = 0;
|
||||
__be32 hi_dword = 0;
|
||||
u32 i = 0;
|
||||
|
||||
/* Apply masks to input data */
|
||||
for (i = 0; i < 11; i++)
|
||||
input->dword_stream[i] &= input_mask->dword_stream[i];
|
||||
|
||||
/* record the flow_vm_vlan bits as they are a key part to the hash */
|
||||
flow_vm_vlan = ntohl(input->dword_stream[0]);
|
||||
|
||||
/* generate common hash dword */
|
||||
for (i = 1; i <= 10; i++)
|
||||
hi_dword ^= input->dword_stream[i];
|
||||
hi_hash_dword = ntohl(hi_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 */
|
||||
TXGBE_COMPUTE_BKT_HASH_ITERATION(0);
|
||||
|
||||
/* 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 remaining 30 bit of the key */
|
||||
for (i = 1; i <= 15; i++)
|
||||
TXGBE_COMPUTE_BKT_HASH_ITERATION(i);
|
||||
|
||||
/* Limit hash to 13 bits since max bucket count is 8K.
|
||||
* Store result at the end of the input stream.
|
||||
*/
|
||||
input->formatted.bkt_hash = (__force __be16)(bucket_hash & 0x1FFF);
|
||||
}
|
||||
|
||||
static int txgbe_fdir_check_cmd_complete(struct wx *wx)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
return read_poll_timeout_atomic(rd32, val,
|
||||
!(val & TXGBE_RDB_FDIR_CMD_CMD_MASK),
|
||||
10, 100, false,
|
||||
wx, TXGBE_RDB_FDIR_CMD);
|
||||
}
|
||||
|
||||
/**
|
||||
* txgbe_fdir_add_signature_filter - Adds a signature hash filter
|
||||
* @wx: pointer to hardware structure
|
||||
* @input: unique input dword
|
||||
* @common: compressed common input dword
|
||||
* @queue: queue index to direct traffic to
|
||||
*
|
||||
* @return: 0 on success and negative on failure
|
||||
**/
|
||||
static int txgbe_fdir_add_signature_filter(struct wx *wx,
|
||||
union txgbe_atr_hash_dword input,
|
||||
union txgbe_atr_hash_dword common,
|
||||
u8 queue)
|
||||
{
|
||||
u32 fdirhashcmd, fdircmd;
|
||||
u8 flow_type;
|
||||
int err;
|
||||
|
||||
/* Get the flow_type in order to program FDIRCMD properly
|
||||
* lowest 2 bits are FDIRCMD.L4TYPE, third lowest bit is FDIRCMD.IPV6
|
||||
* fifth is FDIRCMD.TUNNEL_FILTER
|
||||
*/
|
||||
flow_type = input.formatted.flow_type;
|
||||
switch (flow_type) {
|
||||
case TXGBE_ATR_FLOW_TYPE_TCPV4:
|
||||
case TXGBE_ATR_FLOW_TYPE_UDPV4:
|
||||
case TXGBE_ATR_FLOW_TYPE_SCTPV4:
|
||||
case TXGBE_ATR_FLOW_TYPE_TCPV6:
|
||||
case TXGBE_ATR_FLOW_TYPE_UDPV6:
|
||||
case TXGBE_ATR_FLOW_TYPE_SCTPV6:
|
||||
break;
|
||||
default:
|
||||
wx_err(wx, "Error on flow type input\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* configure FDIRCMD register */
|
||||
fdircmd = TXGBE_RDB_FDIR_CMD_CMD_ADD_FLOW |
|
||||
TXGBE_RDB_FDIR_CMD_FILTER_UPDATE |
|
||||
TXGBE_RDB_FDIR_CMD_LAST | TXGBE_RDB_FDIR_CMD_QUEUE_EN;
|
||||
fdircmd |= TXGBE_RDB_FDIR_CMD_FLOW_TYPE(flow_type);
|
||||
fdircmd |= TXGBE_RDB_FDIR_CMD_RX_QUEUE(queue);
|
||||
|
||||
txgbe_atr_compute_sig_hash(input, common, &fdirhashcmd);
|
||||
fdirhashcmd |= TXGBE_RDB_FDIR_HASH_BUCKET_VALID;
|
||||
wr32(wx, TXGBE_RDB_FDIR_HASH, fdirhashcmd);
|
||||
wr32(wx, TXGBE_RDB_FDIR_CMD, fdircmd);
|
||||
|
||||
wx_dbg(wx, "Tx Queue=%x hash=%x\n", queue, (u32)fdirhashcmd);
|
||||
|
||||
err = txgbe_fdir_check_cmd_complete(wx);
|
||||
if (err)
|
||||
wx_err(wx, "Flow Director command did not complete!\n");
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void txgbe_atr(struct wx_ring *ring, struct wx_tx_buffer *first, u8 ptype)
|
||||
{
|
||||
union txgbe_atr_hash_dword common = { .dword = 0 };
|
||||
union txgbe_atr_hash_dword input = { .dword = 0 };
|
||||
struct wx_q_vector *q_vector = ring->q_vector;
|
||||
struct wx_dec_ptype dptype;
|
||||
union network_header {
|
||||
struct ipv6hdr *ipv6;
|
||||
struct iphdr *ipv4;
|
||||
void *raw;
|
||||
} hdr;
|
||||
struct tcphdr *th;
|
||||
|
||||
/* if ring doesn't have a interrupt vector, cannot perform ATR */
|
||||
if (!q_vector)
|
||||
return;
|
||||
|
||||
ring->atr_count++;
|
||||
dptype = wx_decode_ptype(ptype);
|
||||
if (dptype.etype) {
|
||||
if (WX_PTYPE_TYPL4(ptype) != WX_PTYPE_TYP_TCP)
|
||||
return;
|
||||
hdr.raw = (void *)skb_inner_network_header(first->skb);
|
||||
th = inner_tcp_hdr(first->skb);
|
||||
} else {
|
||||
if (WX_PTYPE_PKT(ptype) != WX_PTYPE_PKT_IP ||
|
||||
WX_PTYPE_TYPL4(ptype) != WX_PTYPE_TYP_TCP)
|
||||
return;
|
||||
hdr.raw = (void *)skb_network_header(first->skb);
|
||||
th = tcp_hdr(first->skb);
|
||||
}
|
||||
|
||||
/* skip this packet since it is invalid or the socket is closing */
|
||||
if (!th || th->fin)
|
||||
return;
|
||||
|
||||
/* sample on all syn packets or once every atr sample count */
|
||||
if (!th->syn && ring->atr_count < ring->atr_sample_rate)
|
||||
return;
|
||||
|
||||
/* reset sample count */
|
||||
ring->atr_count = 0;
|
||||
|
||||
/* src and dst are inverted, think how the receiver sees them
|
||||
*
|
||||
* The input is broken into two sections, a non-compressed section
|
||||
* containing vm_pool, vlan_id, and flow_type. The rest of the data
|
||||
* is XORed together and stored in the compressed dword.
|
||||
*/
|
||||
input.formatted.vlan_id = htons((u16)ptype);
|
||||
|
||||
/* since src port and flex bytes occupy the same word XOR them together
|
||||
* and write the value to source port portion of compressed dword
|
||||
*/
|
||||
if (first->tx_flags & WX_TX_FLAGS_SW_VLAN)
|
||||
common.port.src ^= th->dest ^ first->skb->protocol;
|
||||
else if (first->tx_flags & WX_TX_FLAGS_HW_VLAN)
|
||||
common.port.src ^= th->dest ^ first->skb->vlan_proto;
|
||||
else
|
||||
common.port.src ^= th->dest ^ first->protocol;
|
||||
common.port.dst ^= th->source;
|
||||
|
||||
if (WX_PTYPE_PKT_IPV6 & WX_PTYPE_PKT(ptype)) {
|
||||
input.formatted.flow_type = TXGBE_ATR_FLOW_TYPE_TCPV6;
|
||||
common.ip ^= hdr.ipv6->saddr.s6_addr32[0] ^
|
||||
hdr.ipv6->saddr.s6_addr32[1] ^
|
||||
hdr.ipv6->saddr.s6_addr32[2] ^
|
||||
hdr.ipv6->saddr.s6_addr32[3] ^
|
||||
hdr.ipv6->daddr.s6_addr32[0] ^
|
||||
hdr.ipv6->daddr.s6_addr32[1] ^
|
||||
hdr.ipv6->daddr.s6_addr32[2] ^
|
||||
hdr.ipv6->daddr.s6_addr32[3];
|
||||
} else {
|
||||
input.formatted.flow_type = TXGBE_ATR_FLOW_TYPE_TCPV4;
|
||||
common.ip ^= hdr.ipv4->saddr ^ hdr.ipv4->daddr;
|
||||
}
|
||||
|
||||
/* This assumes the Rx queue and Tx queue are bound to the same CPU */
|
||||
txgbe_fdir_add_signature_filter(q_vector->wx, input, common,
|
||||
ring->queue_index);
|
||||
}
|
||||
|
||||
int txgbe_fdir_set_input_mask(struct wx *wx, union txgbe_atr_input *input_mask)
|
||||
{
|
||||
u32 fdirm = 0, fdirtcpm = 0, flex = 0;
|
||||
|
||||
/* 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.
|
||||
*
|
||||
* This also assumes IPv4 only. IPv6 masking isn't supported at this
|
||||
* point in time.
|
||||
*/
|
||||
|
||||
/* verify bucket hash is cleared on hash generation */
|
||||
if (input_mask->formatted.bkt_hash)
|
||||
wx_dbg(wx, "bucket hash should always be 0 in mask\n");
|
||||
|
||||
/* Program FDIRM and verify partial masks */
|
||||
switch (input_mask->formatted.vm_pool & 0x7F) {
|
||||
case 0x0:
|
||||
fdirm |= TXGBE_RDB_FDIR_OTHER_MSK_POOL;
|
||||
break;
|
||||
case 0x7F:
|
||||
break;
|
||||
default:
|
||||
wx_err(wx, "Error on vm pool mask\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (input_mask->formatted.flow_type & TXGBE_ATR_L4TYPE_MASK) {
|
||||
case 0x0:
|
||||
fdirm |= TXGBE_RDB_FDIR_OTHER_MSK_L4P;
|
||||
if (input_mask->formatted.dst_port ||
|
||||
input_mask->formatted.src_port) {
|
||||
wx_err(wx, "Error on src/dst port mask\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case TXGBE_ATR_L4TYPE_MASK:
|
||||
break;
|
||||
default:
|
||||
wx_err(wx, "Error on flow type mask\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Now mask VM pool and destination IPv6 - bits 5 and 2 */
|
||||
wr32(wx, TXGBE_RDB_FDIR_OTHER_MSK, fdirm);
|
||||
|
||||
flex = rd32(wx, TXGBE_RDB_FDIR_FLEX_CFG(0));
|
||||
flex &= ~TXGBE_RDB_FDIR_FLEX_CFG_FIELD0;
|
||||
flex |= (TXGBE_RDB_FDIR_FLEX_CFG_BASE_MAC |
|
||||
TXGBE_RDB_FDIR_FLEX_CFG_OFST(0x6));
|
||||
|
||||
switch ((__force u16)input_mask->formatted.flex_bytes & 0xFFFF) {
|
||||
case 0x0000:
|
||||
/* Mask Flex Bytes */
|
||||
flex |= TXGBE_RDB_FDIR_FLEX_CFG_MSK;
|
||||
break;
|
||||
case 0xFFFF:
|
||||
break;
|
||||
default:
|
||||
wx_err(wx, "Error on flexible byte mask\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
wr32(wx, TXGBE_RDB_FDIR_FLEX_CFG(0), flex);
|
||||
|
||||
/* store the TCP/UDP port masks, bit reversed from port layout */
|
||||
fdirtcpm = ntohs(input_mask->formatted.dst_port);
|
||||
fdirtcpm <<= TXGBE_RDB_FDIR_PORT_DESTINATION_SHIFT;
|
||||
fdirtcpm |= ntohs(input_mask->formatted.src_port);
|
||||
|
||||
/* write both the same so that UDP and TCP use the same mask */
|
||||
wr32(wx, TXGBE_RDB_FDIR_TCP_MSK, ~fdirtcpm);
|
||||
wr32(wx, TXGBE_RDB_FDIR_UDP_MSK, ~fdirtcpm);
|
||||
wr32(wx, TXGBE_RDB_FDIR_SCTP_MSK, ~fdirtcpm);
|
||||
|
||||
/* store source and destination IP masks (little-enian) */
|
||||
wr32(wx, TXGBE_RDB_FDIR_SA4_MSK,
|
||||
ntohl(~input_mask->formatted.src_ip[0]));
|
||||
wr32(wx, TXGBE_RDB_FDIR_DA4_MSK,
|
||||
ntohl(~input_mask->formatted.dst_ip[0]));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int txgbe_fdir_write_perfect_filter(struct wx *wx,
|
||||
union txgbe_atr_input *input,
|
||||
u16 soft_id, u8 queue)
|
||||
{
|
||||
u32 fdirport, fdirvlan, fdirhash, fdircmd;
|
||||
int err = 0;
|
||||
|
||||
/* currently IPv6 is not supported, must be programmed with 0 */
|
||||
wr32(wx, TXGBE_RDB_FDIR_IP6(2), ntohl(input->formatted.src_ip[0]));
|
||||
wr32(wx, TXGBE_RDB_FDIR_IP6(1), ntohl(input->formatted.src_ip[1]));
|
||||
wr32(wx, TXGBE_RDB_FDIR_IP6(0), ntohl(input->formatted.src_ip[2]));
|
||||
|
||||
/* record the source address (little-endian) */
|
||||
wr32(wx, TXGBE_RDB_FDIR_SA, ntohl(input->formatted.src_ip[0]));
|
||||
|
||||
/* record the first 32 bits of the destination address
|
||||
* (little-endian)
|
||||
*/
|
||||
wr32(wx, TXGBE_RDB_FDIR_DA, ntohl(input->formatted.dst_ip[0]));
|
||||
|
||||
/* record source and destination port (little-endian)*/
|
||||
fdirport = ntohs(input->formatted.dst_port);
|
||||
fdirport <<= TXGBE_RDB_FDIR_PORT_DESTINATION_SHIFT;
|
||||
fdirport |= ntohs(input->formatted.src_port);
|
||||
wr32(wx, TXGBE_RDB_FDIR_PORT, fdirport);
|
||||
|
||||
/* record packet type and flex_bytes (little-endian) */
|
||||
fdirvlan = ntohs(input->formatted.flex_bytes);
|
||||
fdirvlan <<= TXGBE_RDB_FDIR_FLEX_FLEX_SHIFT;
|
||||
fdirvlan |= ntohs(input->formatted.vlan_id);
|
||||
wr32(wx, TXGBE_RDB_FDIR_FLEX, fdirvlan);
|
||||
|
||||
/* configure FDIRHASH register */
|
||||
fdirhash = (__force u32)input->formatted.bkt_hash |
|
||||
TXGBE_RDB_FDIR_HASH_BUCKET_VALID |
|
||||
TXGBE_RDB_FDIR_HASH_SIG_SW_INDEX(soft_id);
|
||||
wr32(wx, TXGBE_RDB_FDIR_HASH, fdirhash);
|
||||
|
||||
/* flush all previous writes to make certain registers are
|
||||
* programmed prior to issuing the command
|
||||
*/
|
||||
WX_WRITE_FLUSH(wx);
|
||||
|
||||
/* configure FDIRCMD register */
|
||||
fdircmd = TXGBE_RDB_FDIR_CMD_CMD_ADD_FLOW |
|
||||
TXGBE_RDB_FDIR_CMD_FILTER_UPDATE |
|
||||
TXGBE_RDB_FDIR_CMD_LAST | TXGBE_RDB_FDIR_CMD_QUEUE_EN;
|
||||
if (queue == TXGBE_RDB_FDIR_DROP_QUEUE)
|
||||
fdircmd |= TXGBE_RDB_FDIR_CMD_DROP;
|
||||
fdircmd |= TXGBE_RDB_FDIR_CMD_FLOW_TYPE(input->formatted.flow_type);
|
||||
fdircmd |= TXGBE_RDB_FDIR_CMD_RX_QUEUE(queue);
|
||||
fdircmd |= TXGBE_RDB_FDIR_CMD_VT_POOL(input->formatted.vm_pool);
|
||||
|
||||
wr32(wx, TXGBE_RDB_FDIR_CMD, fdircmd);
|
||||
err = txgbe_fdir_check_cmd_complete(wx);
|
||||
if (err)
|
||||
wx_err(wx, "Flow Director command did not complete!\n");
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int txgbe_fdir_erase_perfect_filter(struct wx *wx,
|
||||
union txgbe_atr_input *input,
|
||||
u16 soft_id)
|
||||
{
|
||||
u32 fdirhash, fdircmd;
|
||||
int err = 0;
|
||||
|
||||
/* configure FDIRHASH register */
|
||||
fdirhash = (__force u32)input->formatted.bkt_hash;
|
||||
fdirhash |= TXGBE_RDB_FDIR_HASH_SIG_SW_INDEX(soft_id);
|
||||
wr32(wx, TXGBE_RDB_FDIR_HASH, fdirhash);
|
||||
|
||||
/* flush hash to HW */
|
||||
WX_WRITE_FLUSH(wx);
|
||||
|
||||
/* Query if filter is present */
|
||||
wr32(wx, TXGBE_RDB_FDIR_CMD, TXGBE_RDB_FDIR_CMD_CMD_QUERY_REM_FILT);
|
||||
|
||||
err = txgbe_fdir_check_cmd_complete(wx);
|
||||
if (err) {
|
||||
wx_err(wx, "Flow Director command did not complete!\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
fdircmd = rd32(wx, TXGBE_RDB_FDIR_CMD);
|
||||
/* if filter exists in hardware then remove it */
|
||||
if (fdircmd & TXGBE_RDB_FDIR_CMD_FILTER_VALID) {
|
||||
wr32(wx, TXGBE_RDB_FDIR_HASH, fdirhash);
|
||||
WX_WRITE_FLUSH(wx);
|
||||
wr32(wx, TXGBE_RDB_FDIR_CMD,
|
||||
TXGBE_RDB_FDIR_CMD_CMD_REMOVE_FLOW);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* txgbe_fdir_enable - Initialize Flow Director control registers
|
||||
* @wx: pointer to hardware structure
|
||||
* @fdirctrl: value to write to flow director control register
|
||||
**/
|
||||
static void txgbe_fdir_enable(struct wx *wx, u32 fdirctrl)
|
||||
{
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
/* Prime the keys for hashing */
|
||||
wr32(wx, TXGBE_RDB_FDIR_HKEY, TXGBE_ATR_BUCKET_HASH_KEY);
|
||||
wr32(wx, TXGBE_RDB_FDIR_SKEY, TXGBE_ATR_SIGNATURE_HASH_KEY);
|
||||
|
||||
wr32(wx, TXGBE_RDB_FDIR_CTL, fdirctrl);
|
||||
WX_WRITE_FLUSH(wx);
|
||||
ret = read_poll_timeout(rd32, val, val & TXGBE_RDB_FDIR_CTL_INIT_DONE,
|
||||
1000, 10000, false, wx, TXGBE_RDB_FDIR_CTL);
|
||||
|
||||
if (ret < 0)
|
||||
wx_dbg(wx, "Flow Director poll time exceeded!\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* txgbe_init_fdir_signature -Initialize Flow Director sig filters
|
||||
* @wx: pointer to hardware structure
|
||||
**/
|
||||
static void txgbe_init_fdir_signature(struct wx *wx)
|
||||
{
|
||||
u32 fdirctrl = TXGBE_FDIR_PBALLOC_64K;
|
||||
u32 flex = 0;
|
||||
|
||||
flex = rd32(wx, TXGBE_RDB_FDIR_FLEX_CFG(0));
|
||||
flex &= ~TXGBE_RDB_FDIR_FLEX_CFG_FIELD0;
|
||||
|
||||
flex |= (TXGBE_RDB_FDIR_FLEX_CFG_BASE_MAC |
|
||||
TXGBE_RDB_FDIR_FLEX_CFG_OFST(0x6));
|
||||
wr32(wx, TXGBE_RDB_FDIR_FLEX_CFG(0), flex);
|
||||
|
||||
/* Continue setup of fdirctrl register bits:
|
||||
* Move the flexible bytes to use the ethertype - shift 6 words
|
||||
* Set the maximum length per hash bucket to 0xA filters
|
||||
* Send interrupt when 64 filters are left
|
||||
*/
|
||||
fdirctrl |= TXGBE_RDB_FDIR_CTL_HASH_BITS(0xF) |
|
||||
TXGBE_RDB_FDIR_CTL_MAX_LENGTH(0xA) |
|
||||
TXGBE_RDB_FDIR_CTL_FULL_THRESH(4);
|
||||
|
||||
/* write hashes and fdirctrl register, poll for completion */
|
||||
txgbe_fdir_enable(wx, fdirctrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* txgbe_init_fdir_perfect - Initialize Flow Director perfect filters
|
||||
* @wx: pointer to hardware structure
|
||||
**/
|
||||
static void txgbe_init_fdir_perfect(struct wx *wx)
|
||||
{
|
||||
u32 fdirctrl = TXGBE_FDIR_PBALLOC_64K;
|
||||
|
||||
/* Continue setup of fdirctrl register bits:
|
||||
* Turn perfect match filtering on
|
||||
* Report hash in RSS field of Rx wb descriptor
|
||||
* Initialize the drop queue
|
||||
* Move the flexible bytes to use the ethertype - shift 6 words
|
||||
* Set the maximum length per hash bucket to 0xA filters
|
||||
* Send interrupt when 64 (0x4 * 16) filters are left
|
||||
*/
|
||||
fdirctrl |= TXGBE_RDB_FDIR_CTL_PERFECT_MATCH |
|
||||
TXGBE_RDB_FDIR_CTL_DROP_Q(TXGBE_RDB_FDIR_DROP_QUEUE) |
|
||||
TXGBE_RDB_FDIR_CTL_HASH_BITS(0xF) |
|
||||
TXGBE_RDB_FDIR_CTL_MAX_LENGTH(0xA) |
|
||||
TXGBE_RDB_FDIR_CTL_FULL_THRESH(4);
|
||||
|
||||
/* write hashes and fdirctrl register, poll for completion */
|
||||
txgbe_fdir_enable(wx, fdirctrl);
|
||||
}
|
||||
|
||||
static void txgbe_fdir_filter_restore(struct wx *wx)
|
||||
{
|
||||
struct txgbe_fdir_filter *filter;
|
||||
struct txgbe *txgbe = wx->priv;
|
||||
struct hlist_node *node;
|
||||
u8 queue = 0;
|
||||
int ret = 0;
|
||||
|
||||
spin_lock(&txgbe->fdir_perfect_lock);
|
||||
|
||||
if (!hlist_empty(&txgbe->fdir_filter_list))
|
||||
ret = txgbe_fdir_set_input_mask(wx, &txgbe->fdir_mask);
|
||||
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
hlist_for_each_entry_safe(filter, node,
|
||||
&txgbe->fdir_filter_list, fdir_node) {
|
||||
if (filter->action == TXGBE_RDB_FDIR_DROP_QUEUE) {
|
||||
queue = TXGBE_RDB_FDIR_DROP_QUEUE;
|
||||
} else {
|
||||
u32 ring = ethtool_get_flow_spec_ring(filter->action);
|
||||
|
||||
if (ring >= wx->num_rx_queues) {
|
||||
wx_err(wx, "FDIR restore failed, ring:%u\n",
|
||||
ring);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Map the ring onto the absolute queue index */
|
||||
queue = wx->rx_ring[ring]->reg_idx;
|
||||
}
|
||||
|
||||
ret = txgbe_fdir_write_perfect_filter(wx,
|
||||
&filter->filter,
|
||||
filter->sw_idx,
|
||||
queue);
|
||||
if (ret)
|
||||
wx_err(wx, "FDIR restore failed, index:%u\n",
|
||||
filter->sw_idx);
|
||||
}
|
||||
|
||||
unlock:
|
||||
spin_unlock(&txgbe->fdir_perfect_lock);
|
||||
}
|
||||
|
||||
void txgbe_configure_fdir(struct wx *wx)
|
||||
{
|
||||
wx_disable_sec_rx_path(wx);
|
||||
|
||||
if (test_bit(WX_FLAG_FDIR_HASH, wx->flags)) {
|
||||
txgbe_init_fdir_signature(wx);
|
||||
} else if (test_bit(WX_FLAG_FDIR_PERFECT, wx->flags)) {
|
||||
txgbe_init_fdir_perfect(wx);
|
||||
txgbe_fdir_filter_restore(wx);
|
||||
}
|
||||
|
||||
wx_enable_sec_rx_path(wx);
|
||||
}
|
||||
|
||||
void txgbe_fdir_filter_exit(struct wx *wx)
|
||||
{
|
||||
struct txgbe_fdir_filter *filter;
|
||||
struct txgbe *txgbe = wx->priv;
|
||||
struct hlist_node *node;
|
||||
|
||||
spin_lock(&txgbe->fdir_perfect_lock);
|
||||
|
||||
hlist_for_each_entry_safe(filter, node,
|
||||
&txgbe->fdir_filter_list, fdir_node) {
|
||||
hlist_del(&filter->fdir_node);
|
||||
kfree(filter);
|
||||
}
|
||||
txgbe->fdir_filter_count = 0;
|
||||
|
||||
spin_unlock(&txgbe->fdir_perfect_lock);
|
||||
}
|
||||
20
drivers/net/ethernet/wangxun/txgbe/txgbe_fdir.h
Normal file
20
drivers/net/ethernet/wangxun/txgbe/txgbe_fdir.h
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (c) 2015 - 2024 Beijing WangXun Technology Co., Ltd. */
|
||||
|
||||
#ifndef _TXGBE_FDIR_H_
|
||||
#define _TXGBE_FDIR_H_
|
||||
|
||||
void txgbe_atr_compute_perfect_hash(union txgbe_atr_input *input,
|
||||
union txgbe_atr_input *input_mask);
|
||||
void txgbe_atr(struct wx_ring *ring, struct wx_tx_buffer *first, u8 ptype);
|
||||
int txgbe_fdir_set_input_mask(struct wx *wx, union txgbe_atr_input *input_mask);
|
||||
int txgbe_fdir_write_perfect_filter(struct wx *wx,
|
||||
union txgbe_atr_input *input,
|
||||
u16 soft_id, u8 queue);
|
||||
int txgbe_fdir_erase_perfect_filter(struct wx *wx,
|
||||
union txgbe_atr_input *input,
|
||||
u16 soft_id);
|
||||
void txgbe_configure_fdir(struct wx *wx);
|
||||
void txgbe_fdir_filter_exit(struct wx *wx);
|
||||
|
||||
#endif /* _TXGBE_FDIR_H_ */
|
||||
|
|
@ -18,6 +18,7 @@
|
|||
#include "txgbe_hw.h"
|
||||
#include "txgbe_phy.h"
|
||||
#include "txgbe_irq.h"
|
||||
#include "txgbe_fdir.h"
|
||||
#include "txgbe_ethtool.h"
|
||||
|
||||
char txgbe_driver_name[] = "txgbe";
|
||||
|
|
@ -257,6 +258,14 @@ static int txgbe_sw_init(struct wx *wx)
|
|||
num_online_cpus());
|
||||
wx->rss_enabled = true;
|
||||
|
||||
wx->ring_feature[RING_F_FDIR].limit = min_t(int, TXGBE_MAX_FDIR_INDICES,
|
||||
num_online_cpus());
|
||||
set_bit(WX_FLAG_FDIR_CAPABLE, wx->flags);
|
||||
set_bit(WX_FLAG_FDIR_HASH, wx->flags);
|
||||
wx->atr_sample_rate = TXGBE_DEFAULT_ATR_SAMPLE_RATE;
|
||||
wx->atr = txgbe_atr;
|
||||
wx->configure_fdir = txgbe_configure_fdir;
|
||||
|
||||
/* enable itr by default in dynamic mode */
|
||||
wx->rx_itr_setting = 1;
|
||||
wx->tx_itr_setting = 1;
|
||||
|
|
@ -274,6 +283,12 @@ static int txgbe_sw_init(struct wx *wx)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void txgbe_init_fdir(struct txgbe *txgbe)
|
||||
{
|
||||
txgbe->fdir_filter_count = 0;
|
||||
spin_lock_init(&txgbe->fdir_perfect_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* txgbe_open - Called when a network interface is made active
|
||||
* @netdev: network interface device structure
|
||||
|
|
@ -352,6 +367,7 @@ static int txgbe_close(struct net_device *netdev)
|
|||
txgbe_down(wx);
|
||||
wx_free_irq(wx);
|
||||
wx_free_resources(wx);
|
||||
txgbe_fdir_filter_exit(wx);
|
||||
wx_control_hw(wx, false);
|
||||
|
||||
return 0;
|
||||
|
|
@ -660,6 +676,8 @@ static int txgbe_probe(struct pci_dev *pdev,
|
|||
txgbe->wx = wx;
|
||||
wx->priv = txgbe;
|
||||
|
||||
txgbe_init_fdir(txgbe);
|
||||
|
||||
err = txgbe_setup_misc_irq(txgbe);
|
||||
if (err)
|
||||
goto err_release_hw;
|
||||
|
|
|
|||
|
|
@ -89,6 +89,55 @@
|
|||
#define TXGBE_XPCS_IDA_ADDR 0x13000
|
||||
#define TXGBE_XPCS_IDA_DATA 0x13004
|
||||
|
||||
/********************************* Flow Director *****************************/
|
||||
#define TXGBE_RDB_FDIR_DROP_QUEUE 127
|
||||
#define TXGBE_RDB_FDIR_CTL 0x19500
|
||||
#define TXGBE_RDB_FDIR_CTL_INIT_DONE BIT(3)
|
||||
#define TXGBE_RDB_FDIR_CTL_PERFECT_MATCH BIT(4)
|
||||
#define TXGBE_RDB_FDIR_CTL_DROP_Q(v) FIELD_PREP(GENMASK(14, 8), v)
|
||||
#define TXGBE_RDB_FDIR_CTL_HASH_BITS(v) FIELD_PREP(GENMASK(23, 20), v)
|
||||
#define TXGBE_RDB_FDIR_CTL_MAX_LENGTH(v) FIELD_PREP(GENMASK(27, 24), v)
|
||||
#define TXGBE_RDB_FDIR_CTL_FULL_THRESH(v) FIELD_PREP(GENMASK(31, 28), v)
|
||||
#define TXGBE_RDB_FDIR_IP6(_i) (0x1950C + ((_i) * 4)) /* 0-2 */
|
||||
#define TXGBE_RDB_FDIR_SA 0x19518
|
||||
#define TXGBE_RDB_FDIR_DA 0x1951C
|
||||
#define TXGBE_RDB_FDIR_PORT 0x19520
|
||||
#define TXGBE_RDB_FDIR_PORT_DESTINATION_SHIFT 16
|
||||
#define TXGBE_RDB_FDIR_FLEX 0x19524
|
||||
#define TXGBE_RDB_FDIR_FLEX_FLEX_SHIFT 16
|
||||
#define TXGBE_RDB_FDIR_HASH 0x19528
|
||||
#define TXGBE_RDB_FDIR_HASH_SIG_SW_INDEX(v) FIELD_PREP(GENMASK(31, 16), v)
|
||||
#define TXGBE_RDB_FDIR_HASH_BUCKET_VALID BIT(15)
|
||||
#define TXGBE_RDB_FDIR_CMD 0x1952C
|
||||
#define TXGBE_RDB_FDIR_CMD_CMD_MASK GENMASK(1, 0)
|
||||
#define TXGBE_RDB_FDIR_CMD_CMD(v) FIELD_PREP(GENMASK(1, 0), v)
|
||||
#define TXGBE_RDB_FDIR_CMD_CMD_ADD_FLOW TXGBE_RDB_FDIR_CMD_CMD(1)
|
||||
#define TXGBE_RDB_FDIR_CMD_CMD_REMOVE_FLOW TXGBE_RDB_FDIR_CMD_CMD(2)
|
||||
#define TXGBE_RDB_FDIR_CMD_CMD_QUERY_REM_FILT TXGBE_RDB_FDIR_CMD_CMD(3)
|
||||
#define TXGBE_RDB_FDIR_CMD_FILTER_VALID BIT(2)
|
||||
#define TXGBE_RDB_FDIR_CMD_FILTER_UPDATE BIT(3)
|
||||
#define TXGBE_RDB_FDIR_CMD_FLOW_TYPE(v) FIELD_PREP(GENMASK(6, 5), v)
|
||||
#define TXGBE_RDB_FDIR_CMD_DROP BIT(9)
|
||||
#define TXGBE_RDB_FDIR_CMD_LAST BIT(11)
|
||||
#define TXGBE_RDB_FDIR_CMD_QUEUE_EN BIT(15)
|
||||
#define TXGBE_RDB_FDIR_CMD_RX_QUEUE(v) FIELD_PREP(GENMASK(22, 16), v)
|
||||
#define TXGBE_RDB_FDIR_CMD_VT_POOL(v) FIELD_PREP(GENMASK(29, 24), v)
|
||||
#define TXGBE_RDB_FDIR_DA4_MSK 0x1953C
|
||||
#define TXGBE_RDB_FDIR_SA4_MSK 0x19540
|
||||
#define TXGBE_RDB_FDIR_TCP_MSK 0x19544
|
||||
#define TXGBE_RDB_FDIR_UDP_MSK 0x19548
|
||||
#define TXGBE_RDB_FDIR_SCTP_MSK 0x19560
|
||||
#define TXGBE_RDB_FDIR_HKEY 0x19568
|
||||
#define TXGBE_RDB_FDIR_SKEY 0x1956C
|
||||
#define TXGBE_RDB_FDIR_OTHER_MSK 0x19570
|
||||
#define TXGBE_RDB_FDIR_OTHER_MSK_POOL BIT(2)
|
||||
#define TXGBE_RDB_FDIR_OTHER_MSK_L4P BIT(3)
|
||||
#define TXGBE_RDB_FDIR_FLEX_CFG(_i) (0x19580 + ((_i) * 4))
|
||||
#define TXGBE_RDB_FDIR_FLEX_CFG_FIELD0 GENMASK(7, 0)
|
||||
#define TXGBE_RDB_FDIR_FLEX_CFG_BASE_MAC FIELD_PREP(GENMASK(1, 0), 0)
|
||||
#define TXGBE_RDB_FDIR_FLEX_CFG_MSK BIT(2)
|
||||
#define TXGBE_RDB_FDIR_FLEX_CFG_OFST(v) FIELD_PREP(GENMASK(7, 3), v)
|
||||
|
||||
/* Checksum and EEPROM pointers */
|
||||
#define TXGBE_EEPROM_LAST_WORD 0x800
|
||||
#define TXGBE_EEPROM_CHECKSUM 0x2F
|
||||
|
|
@ -112,6 +161,98 @@
|
|||
#define TXGBE_SP_RX_PB_SIZE 512
|
||||
#define TXGBE_SP_TDB_PB_SZ (160 * 1024) /* 160KB Packet Buffer */
|
||||
|
||||
#define TXGBE_DEFAULT_ATR_SAMPLE_RATE 20
|
||||
|
||||
/* Software ATR hash keys */
|
||||
#define TXGBE_ATR_BUCKET_HASH_KEY 0x3DAD14E2
|
||||
#define TXGBE_ATR_SIGNATURE_HASH_KEY 0x174D3614
|
||||
|
||||
/* Software ATR input stream values and masks */
|
||||
#define TXGBE_ATR_HASH_MASK 0x7fff
|
||||
#define TXGBE_ATR_L4TYPE_MASK 0x3
|
||||
#define TXGBE_ATR_L4TYPE_UDP 0x1
|
||||
#define TXGBE_ATR_L4TYPE_TCP 0x2
|
||||
#define TXGBE_ATR_L4TYPE_SCTP 0x3
|
||||
#define TXGBE_ATR_L4TYPE_IPV6_MASK 0x4
|
||||
#define TXGBE_ATR_L4TYPE_TUNNEL_MASK 0x10
|
||||
|
||||
enum txgbe_atr_flow_type {
|
||||
TXGBE_ATR_FLOW_TYPE_IPV4 = 0x0,
|
||||
TXGBE_ATR_FLOW_TYPE_UDPV4 = 0x1,
|
||||
TXGBE_ATR_FLOW_TYPE_TCPV4 = 0x2,
|
||||
TXGBE_ATR_FLOW_TYPE_SCTPV4 = 0x3,
|
||||
TXGBE_ATR_FLOW_TYPE_IPV6 = 0x4,
|
||||
TXGBE_ATR_FLOW_TYPE_UDPV6 = 0x5,
|
||||
TXGBE_ATR_FLOW_TYPE_TCPV6 = 0x6,
|
||||
TXGBE_ATR_FLOW_TYPE_SCTPV6 = 0x7,
|
||||
TXGBE_ATR_FLOW_TYPE_TUNNELED_IPV4 = 0x10,
|
||||
TXGBE_ATR_FLOW_TYPE_TUNNELED_UDPV4 = 0x11,
|
||||
TXGBE_ATR_FLOW_TYPE_TUNNELED_TCPV4 = 0x12,
|
||||
TXGBE_ATR_FLOW_TYPE_TUNNELED_SCTPV4 = 0x13,
|
||||
TXGBE_ATR_FLOW_TYPE_TUNNELED_IPV6 = 0x14,
|
||||
TXGBE_ATR_FLOW_TYPE_TUNNELED_UDPV6 = 0x15,
|
||||
TXGBE_ATR_FLOW_TYPE_TUNNELED_TCPV6 = 0x16,
|
||||
TXGBE_ATR_FLOW_TYPE_TUNNELED_SCTPV6 = 0x17,
|
||||
};
|
||||
|
||||
/* Flow Director ATR input struct. */
|
||||
union txgbe_atr_input {
|
||||
/* Byte layout in order, all values with MSB first:
|
||||
*
|
||||
* vm_pool - 1 byte
|
||||
* flow_type - 1 byte
|
||||
* vlan_id - 2 bytes
|
||||
* dst_ip - 16 bytes
|
||||
* src_ip - 16 bytes
|
||||
* src_port - 2 bytes
|
||||
* dst_port - 2 bytes
|
||||
* flex_bytes - 2 bytes
|
||||
* bkt_hash - 2 bytes
|
||||
*/
|
||||
struct {
|
||||
u8 vm_pool;
|
||||
u8 flow_type;
|
||||
__be16 vlan_id;
|
||||
__be32 dst_ip[4];
|
||||
__be32 src_ip[4];
|
||||
__be16 src_port;
|
||||
__be16 dst_port;
|
||||
__be16 flex_bytes;
|
||||
__be16 bkt_hash;
|
||||
} formatted;
|
||||
__be32 dword_stream[11];
|
||||
};
|
||||
|
||||
/* Flow Director compressed ATR hash input struct */
|
||||
union txgbe_atr_hash_dword {
|
||||
struct {
|
||||
u8 vm_pool;
|
||||
u8 flow_type;
|
||||
__be16 vlan_id;
|
||||
} formatted;
|
||||
__be32 ip;
|
||||
struct {
|
||||
__be16 src;
|
||||
__be16 dst;
|
||||
} port;
|
||||
__be16 flex_bytes;
|
||||
__be32 dword;
|
||||
};
|
||||
|
||||
enum txgbe_fdir_pballoc_type {
|
||||
TXGBE_FDIR_PBALLOC_NONE = 0,
|
||||
TXGBE_FDIR_PBALLOC_64K = 1,
|
||||
TXGBE_FDIR_PBALLOC_128K = 2,
|
||||
TXGBE_FDIR_PBALLOC_256K = 3,
|
||||
};
|
||||
|
||||
struct txgbe_fdir_filter {
|
||||
struct hlist_node fdir_node;
|
||||
union txgbe_atr_input filter;
|
||||
u16 sw_idx;
|
||||
u16 action;
|
||||
};
|
||||
|
||||
/* TX/RX descriptor defines */
|
||||
#define TXGBE_DEFAULT_TXD 512
|
||||
#define TXGBE_DEFAULT_TX_WORK 256
|
||||
|
|
@ -196,6 +337,12 @@ struct txgbe {
|
|||
struct gpio_chip *gpio;
|
||||
unsigned int gpio_irq;
|
||||
unsigned int link_irq;
|
||||
|
||||
/* flow director */
|
||||
struct hlist_head fdir_filter_list;
|
||||
union txgbe_atr_input fdir_mask;
|
||||
int fdir_filter_count;
|
||||
spinlock_t fdir_perfect_lock; /* spinlock for FDIR */
|
||||
};
|
||||
|
||||
#endif /* _TXGBE_TYPE_H_ */
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user