Merge branch '1GbE' of git://git.kernel.org/pub/scm/linux/kernel/git/tnguy/next-queue

Tony Nguyen says:

====================
igc: Add support for Frame Preemption

Faizal Rahim says:

Introduce support for the FPE feature in the IGC driver.

The patches aligns with the upstream FPE API:
https://patchwork.kernel.org/project/netdevbpf/cover/20230220122343.1156614-1-vladimir.oltean@nxp.com/
https://patchwork.kernel.org/project/netdevbpf/cover/20230119122705.73054-1-vladimir.oltean@nxp.com/

It builds upon earlier work:
https://patchwork.kernel.org/project/netdevbpf/cover/20220520011538.1098888-1-vinicius.gomes@intel.com/

The patch series adds the following functionalities to the IGC driver:
a) Configure FPE using `ethtool --set-mm`.
b) Display FPE settings via `ethtool --show-mm`.
c) View FPE statistics using `ethtool --include-statistics --show-mm'.
e) Block setting preemptible tc in taprio since it is not supported yet.
   Existing code already blocks it in mqprio.

Tested:
Enabled CONFIG_PROVE_LOCKING, CONFIG_DEBUG_ATOMIC_SLEEP, CONFIG_DMA_API_DEBUG, and CONFIG_KASAN
1) selftests
2) netdev down/up cycles
3) suspend/resume cycles
4) fpe verification

No bugs or unusual dmesg logs were observed.
Ran 1), 2) and 3) with and without the patch series, compared dmesg and selftest logs - no differences found.

* '1GbE' of git://git.kernel.org/pub/scm/linux/kernel/git/tnguy/next-queue:
  igc: add support to get frame preemption statistics via ethtool
  igc: add support to get MAC Merge data via ethtool
  igc: block setting preemptible traffic class in taprio
  igc: add support to set tx-min-frag-size
  igc: add support for frame preemption verification
  igc: set the RX packet buffer size for TSN mode
  igc: use FIELD_PREP and GENMASK for existing RX packet buffer size
  igc: optimize TX packet buffer utilization for TSN mode
  igc: use FIELD_PREP and GENMASK for existing TX packet buffer size
  igc: rename I225_RXPBSIZE_DEFAULT and I225_TXPBSIZE_DEFAULT
  igc: rename xdp_get_tx_ring() for non-xdp usage
  net: ethtool: mm: reset verification status when link is down
  net: ethtool: mm: extract stmmac verification logic into common library
  net: stmmac: move frag_size handling out of spin_lock
====================

Link: https://patch.msgid.link/20250418163822.3519810-1-anthony.l.nguyen@intel.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2025-04-23 18:31:55 -07:00
commit 3fec58f5a4
17 changed files with 875 additions and 220 deletions

View File

@ -369,6 +369,7 @@ config IGC
default n
depends on PCI
depends on PTP_1588_CLOCK_OPTIONAL
depends on ETHTOOL_NETLINK
help
This driver supports Intel(R) Ethernet Controller I225-LM/I225-V
family of adapters.

View File

@ -40,6 +40,11 @@ void igc_ethtool_set_ops(struct net_device *);
#define IGC_MAX_TX_TSTAMP_REGS 4
struct igc_fpe_t {
struct ethtool_mmsv mmsv;
u32 tx_min_frag_size;
};
enum igc_mac_filter_type {
IGC_MAC_FILTER_TYPE_DST = 0,
IGC_MAC_FILTER_TYPE_SRC
@ -333,6 +338,8 @@ struct igc_adapter {
struct timespec64 period;
} perout[IGC_N_PEROUT];
struct igc_fpe_t fpe;
/* LEDs */
struct mutex led_mutex;
struct igc_led_classdev *leds;
@ -388,10 +395,11 @@ extern char igc_driver_name[];
#define IGC_FLAG_TSN_QBV_ENABLED BIT(17)
#define IGC_FLAG_TSN_QAV_ENABLED BIT(18)
#define IGC_FLAG_TSN_LEGACY_ENABLED BIT(19)
#define IGC_FLAG_TSN_PREEMPT_ENABLED BIT(20)
#define IGC_FLAG_TSN_ANY_ENABLED \
(IGC_FLAG_TSN_QBV_ENABLED | IGC_FLAG_TSN_QAV_ENABLED | \
IGC_FLAG_TSN_LEGACY_ENABLED)
IGC_FLAG_TSN_LEGACY_ENABLED | IGC_FLAG_TSN_PREEMPT_ENABLED)
#define IGC_FLAG_RSS_FIELD_IPV4_UDP BIT(6)
#define IGC_FLAG_RSS_FIELD_IPV6_UDP BIT(7)
@ -736,7 +744,10 @@ struct igc_nfc_rule *igc_get_nfc_rule(struct igc_adapter *adapter,
u32 location);
int igc_add_nfc_rule(struct igc_adapter *adapter, struct igc_nfc_rule *rule);
void igc_del_nfc_rule(struct igc_adapter *adapter, struct igc_nfc_rule *rule);
void igc_disable_empty_addr_recv(struct igc_adapter *adapter);
int igc_enable_empty_addr_recv(struct igc_adapter *adapter);
struct igc_ring *igc_get_tx_ring(struct igc_adapter *adapter, int cpu);
void igc_flush_tx_descriptors(struct igc_ring *ring);
void igc_ptp_init(struct igc_adapter *adapter);
void igc_ptp_reset(struct igc_adapter *adapter);
void igc_ptp_suspend(struct igc_adapter *adapter);

View File

@ -49,6 +49,7 @@ struct igc_adv_tx_context_desc {
#define IGC_ADVTXD_DCMD_DEXT 0x20000000 /* Descriptor extension (1=Adv) */
#define IGC_ADVTXD_DCMD_VLE 0x40000000 /* VLAN pkt enable */
#define IGC_ADVTXD_DCMD_TSE 0x80000000 /* TCP Seg enable */
#define IGC_ADVTXD_PAYLEN_MASK 0XFFFFC000 /* Adv desc PAYLEN mask */
#define IGC_ADVTXD_PAYLEN_SHIFT 14 /* Adv desc PAYLEN shift */
#define IGC_RAR_ENTRIES 16

View File

@ -308,6 +308,8 @@
#define IGC_TXD_DTYP_C 0x00000000 /* Context Descriptor */
#define IGC_TXD_POPTS_IXSM 0x01 /* Insert IP checksum */
#define IGC_TXD_POPTS_TXSM 0x02 /* Insert TCP/UDP checksum */
#define IGC_TXD_POPTS_SMD_MASK 0x3000 /* Indicates whether it's SMD-V or SMD-R */
#define IGC_TXD_CMD_EOP 0x01000000 /* End of Packet */
#define IGC_TXD_CMD_IC 0x04000000 /* Insert Checksum */
#define IGC_TXD_CMD_DEXT 0x20000000 /* Desc extension (0 = legacy) */
@ -363,6 +365,8 @@
#define IGC_SRRCTL_TIMER0SEL(timer) (((timer) & 0x3) << 17)
/* Receive Descriptor bit definitions */
#define IGC_RXD_STAT_SMD_TYPE_V 0x01 /* SMD-V Packet */
#define IGC_RXD_STAT_SMD_TYPE_R 0x02 /* SMD-R Packet */
#define IGC_RXD_STAT_EOP 0x02 /* End of Packet */
#define IGC_RXD_STAT_IXSM 0x04 /* Ignore checksum */
#define IGC_RXD_STAT_UDPCS 0x10 /* UDP xsum calculated */
@ -372,7 +376,8 @@
#define IGC_RXDEXT_STATERR_LB 0x00040000
/* Advanced Receive Descriptor bit definitions */
#define IGC_RXDADV_STAT_TSIP 0x08000 /* timestamp in packet */
#define IGC_RXDADV_STAT_SMD_TYPE_MASK 0x06000
#define IGC_RXDADV_STAT_TSIP 0x08000 /* timestamp in packet */
#define IGC_RXDEXT_STATERR_L4E 0x20000000
#define IGC_RXDEXT_STATERR_IPE 0x40000000
@ -396,11 +401,47 @@
#define IGC_RCTL_PMCF 0x00800000 /* pass MAC control frames */
#define IGC_RCTL_SECRC 0x04000000 /* Strip Ethernet CRC */
#define I225_RXPBSIZE_DEFAULT 0x000000A2 /* RXPBSIZE default */
#define I225_TXPBSIZE_DEFAULT 0x04000014 /* TXPBSIZE default */
#define IGC_RXPBS_CFG_TS_EN 0x80000000 /* Timestamp in Rx buffer */
/* Mask for RX packet buffer size */
#define IGC_RXPBSIZE_EXP_MASK GENMASK(5, 0)
#define IGC_BMC2OSPBSIZE_MASK GENMASK(11, 6)
#define IGC_RXPBSIZE_BE_MASK GENMASK(17, 12)
/* Mask for timestamp in RX buffer */
#define IGC_RXPBS_CFG_TS_EN_MASK GENMASK(31, 31)
/* High-priority RX packet buffer size (KB). Used for Express traffic when preemption is enabled */
#define IGC_RXPBSIZE_EXP(x) FIELD_PREP(IGC_RXPBSIZE_EXP_MASK, (x))
/* BMC to OS packet buffer size in KB */
#define IGC_BMC2OSPBSIZE(x) FIELD_PREP(IGC_BMC2OSPBSIZE_MASK, (x))
/* Low-priority RX packet buffer size (KB). Used for BE traffic when preemption is enabled */
#define IGC_RXPBSIZE_BE(x) FIELD_PREP(IGC_RXPBSIZE_BE_MASK, (x))
/* Enable RX packet buffer for timestamp descriptor, saving 16 bytes per packet if set */
#define IGC_RXPBS_CFG_TS_EN FIELD_PREP(IGC_RXPBS_CFG_TS_EN_MASK, 1)
/* Default value following I225/I226 SW User Manual Section 8.3.1 */
#define IGC_RXPBSIZE_EXP_BMC_DEFAULT ( \
IGC_RXPBSIZE_EXP(34) | IGC_BMC2OSPBSIZE(2))
#define IGC_RXPBSIZE_EXP_BMC_BE_TSN ( \
IGC_RXPBSIZE_EXP(15) | IGC_BMC2OSPBSIZE(2) | IGC_RXPBSIZE_BE(15))
#define IGC_TXPBSIZE_TSN 0x04145145 /* 5k bytes buffer for each queue */
/* Mask for TX packet buffer size */
#define IGC_TXPB0SIZE_MASK GENMASK(5, 0)
#define IGC_TXPB1SIZE_MASK GENMASK(11, 6)
#define IGC_TXPB2SIZE_MASK GENMASK(17, 12)
#define IGC_TXPB3SIZE_MASK GENMASK(23, 18)
/* Mask for OS to BMC packet buffer size */
#define IGC_OS2BMCPBSIZE_MASK GENMASK(29, 24)
/* TX Packet buffer size in KB */
#define IGC_TXPB0SIZE(x) FIELD_PREP(IGC_TXPB0SIZE_MASK, (x))
#define IGC_TXPB1SIZE(x) FIELD_PREP(IGC_TXPB1SIZE_MASK, (x))
#define IGC_TXPB2SIZE(x) FIELD_PREP(IGC_TXPB2SIZE_MASK, (x))
#define IGC_TXPB3SIZE(x) FIELD_PREP(IGC_TXPB3SIZE_MASK, (x))
/* OS to BMC packet buffer size in KB */
#define IGC_OS2BMCPBSIZE(x) FIELD_PREP(IGC_OS2BMCPBSIZE_MASK, (x))
/* Default value following I225/I226 SW User Manual Section 8.3.2 */
#define IGC_TXPBSIZE_DEFAULT ( \
IGC_TXPB0SIZE(20) | IGC_TXPB1SIZE(0) | IGC_TXPB2SIZE(0) | \
IGC_TXPB3SIZE(0) | IGC_OS2BMCPBSIZE(4))
#define IGC_TXPBSIZE_TSN ( \
IGC_TXPB0SIZE(7) | IGC_TXPB1SIZE(7) | IGC_TXPB2SIZE(7) | \
IGC_TXPB3SIZE(7) | IGC_OS2BMCPBSIZE(4))
#define IGC_DTXMXPKTSZ_TSN 0x19 /* 1600 bytes of max TX DMA packet size */
#define IGC_DTXMXPKTSZ_DEFAULT 0x98 /* 9728-byte Jumbo frames */
@ -539,8 +580,10 @@
/* Transmit Scheduling */
#define IGC_TQAVCTRL_TRANSMIT_MODE_TSN 0x00000001
#define IGC_TQAVCTRL_PREEMPT_ENA 0x00000002
#define IGC_TQAVCTRL_ENHANCED_QAV 0x00000008
#define IGC_TQAVCTRL_FUTSCDDIS 0x00000080
#define IGC_TQAVCTRL_MIN_FRAG_MASK 0x0000C000
#define IGC_TXQCTL_QUEUE_MODE_LAUNCHT 0x00000001
#define IGC_TXQCTL_STRICT_CYCLE 0x00000002

View File

@ -8,6 +8,7 @@
#include "igc.h"
#include "igc_diag.h"
#include "igc_tsn.h"
/* forward declaration */
struct igc_stats {
@ -1781,6 +1782,83 @@ static int igc_ethtool_set_eee(struct net_device *netdev,
return 0;
}
static int igc_ethtool_get_mm(struct net_device *netdev,
struct ethtool_mm_state *cmd)
{
struct igc_adapter *adapter = netdev_priv(netdev);
struct igc_fpe_t *fpe = &adapter->fpe;
ethtool_mmsv_get_mm(&fpe->mmsv, cmd);
cmd->tx_min_frag_size = fpe->tx_min_frag_size;
cmd->rx_min_frag_size = IGC_RX_MIN_FRAG_SIZE;
return 0;
}
static int igc_ethtool_set_mm(struct net_device *netdev,
struct ethtool_mm_cfg *cmd,
struct netlink_ext_ack *extack)
{
struct igc_adapter *adapter = netdev_priv(netdev);
struct igc_fpe_t *fpe = &adapter->fpe;
fpe->tx_min_frag_size = igc_fpe_get_supported_frag_size(cmd->tx_min_frag_size);
if (fpe->tx_min_frag_size != cmd->tx_min_frag_size)
NL_SET_ERR_MSG_MOD(extack,
"tx-min-frag-size value set is unsupported. Rounded up to supported value (64, 128, 192, 256)");
if (fpe->mmsv.pmac_enabled != cmd->pmac_enabled) {
if (cmd->pmac_enabled)
static_branch_inc(&igc_fpe_enabled);
else
static_branch_dec(&igc_fpe_enabled);
}
ethtool_mmsv_set_mm(&fpe->mmsv, cmd);
return igc_tsn_offload_apply(adapter);
}
/**
* igc_ethtool_get_frame_ass_error - Get the frame assembly error count.
* @reg_value: Register value for IGC_PRMEXCPRCNT
* Return: The count of frame assembly errors.
*/
static u64 igc_ethtool_get_frame_ass_error(u32 reg_value)
{
/* Out of order statistics */
u32 ooo_frame_cnt, ooo_frag_cnt;
u32 miss_frame_frag_cnt;
ooo_frame_cnt = FIELD_GET(IGC_PRMEXCPRCNT_OOO_FRAME_CNT, reg_value);
ooo_frag_cnt = FIELD_GET(IGC_PRMEXCPRCNT_OOO_FRAG_CNT, reg_value);
miss_frame_frag_cnt = FIELD_GET(IGC_PRMEXCPRCNT_MISS_FRAME_FRAG_CNT,
reg_value);
return ooo_frame_cnt + ooo_frag_cnt + miss_frame_frag_cnt;
}
static u64 igc_ethtool_get_frame_smd_error(u32 reg_value)
{
return FIELD_GET(IGC_PRMEXCPRCNT_OOO_SMDC, reg_value);
}
static void igc_ethtool_get_mm_stats(struct net_device *dev,
struct ethtool_mm_stats *stats)
{
struct igc_adapter *adapter = netdev_priv(dev);
struct igc_hw *hw = &adapter->hw;
u32 reg_value;
reg_value = rd32(IGC_PRMEXCPRCNT);
stats->MACMergeFrameAssErrorCount = igc_ethtool_get_frame_ass_error(reg_value);
stats->MACMergeFrameSmdErrorCount = igc_ethtool_get_frame_smd_error(reg_value);
stats->MACMergeFrameAssOkCount = rd32(IGC_PRMPTDRCNT);
stats->MACMergeFragCountRx = rd32(IGC_PRMEVNTRCNT);
stats->MACMergeFragCountTx = rd32(IGC_PRMEVNTTCNT);
}
static int igc_ethtool_get_link_ksettings(struct net_device *netdev,
struct ethtool_link_ksettings *cmd)
{
@ -2076,6 +2154,9 @@ static const struct ethtool_ops igc_ethtool_ops = {
.get_link_ksettings = igc_ethtool_get_link_ksettings,
.set_link_ksettings = igc_ethtool_set_link_ksettings,
.self_test = igc_ethtool_diag_test,
.get_mm = igc_ethtool_get_mm,
.get_mm_stats = igc_ethtool_get_mm_stats,
.set_mm = igc_ethtool_set_mm,
};
void igc_ethtool_set_ops(struct net_device *netdev)

View File

@ -2464,8 +2464,7 @@ static int igc_xdp_init_tx_descriptor(struct igc_ring *ring,
return -ENOMEM;
}
static struct igc_ring *igc_xdp_get_tx_ring(struct igc_adapter *adapter,
int cpu)
struct igc_ring *igc_get_tx_ring(struct igc_adapter *adapter, int cpu)
{
int index = cpu;
@ -2489,7 +2488,7 @@ static int igc_xdp_xmit_back(struct igc_adapter *adapter, struct xdp_buff *xdp)
if (unlikely(!xdpf))
return -EFAULT;
ring = igc_xdp_get_tx_ring(adapter, cpu);
ring = igc_get_tx_ring(adapter, cpu);
nq = txring_txq(ring);
__netif_tx_lock(nq, cpu);
@ -2549,7 +2548,7 @@ static int igc_xdp_run_prog(struct igc_adapter *adapter, struct xdp_buff *xdp)
}
/* This function assumes __netif_tx_lock is held by the caller. */
static void igc_flush_tx_descriptors(struct igc_ring *ring)
void igc_flush_tx_descriptors(struct igc_ring *ring)
{
/* Once tail pointer is updated, hardware can fetch the descriptors
* any time so we issue a write membar here to ensure all memory
@ -2566,7 +2565,7 @@ static void igc_finalize_xdp(struct igc_adapter *adapter, int status)
struct igc_ring *ring;
if (status & IGC_XDP_TX) {
ring = igc_xdp_get_tx_ring(adapter, cpu);
ring = igc_get_tx_ring(adapter, cpu);
nq = txring_txq(ring);
__netif_tx_lock(nq, cpu);
@ -2638,6 +2637,14 @@ static int igc_clean_rx_irq(struct igc_q_vector *q_vector, const int budget)
size -= IGC_TS_HDR_LEN;
}
if (igc_fpe_is_pmac_enabled(adapter) &&
igc_fpe_handle_mpacket(adapter, rx_desc, size, pktbuf)) {
/* Advance the ring next-to-clean */
igc_is_non_eop(rx_ring, rx_desc);
cleaned_count++;
continue;
}
if (!skb) {
xdp_init_buff(&ctx.xdp, truesize, &rx_ring->xdp_rxq);
xdp_prepare_buff(&ctx.xdp, pktbuf - igc_rx_offset(rx_ring),
@ -3145,6 +3152,11 @@ static bool igc_clean_tx_irq(struct igc_q_vector *q_vector, int napi_budget)
if (!(eop_desc->wb.status & cpu_to_le32(IGC_TXD_STAT_DD)))
break;
if (igc_fpe_is_pmac_enabled(adapter) &&
igc_fpe_transmitted_smd_v(tx_desc))
ethtool_mmsv_event_handle(&adapter->fpe.mmsv,
ETHTOOL_MMSV_LD_SENT_VERIFY_MPACKET);
/* Hold the completions while there's a pending tx hardware
* timestamp request from XDP Tx metadata.
*/
@ -4036,6 +4048,30 @@ static int igc_uc_unsync(struct net_device *netdev, const unsigned char *addr)
return 0;
}
/**
* igc_enable_empty_addr_recv - Enable Rx of packets with all-zeroes MAC address
* @adapter: Pointer to the igc_adapter structure.
*
* Frame preemption verification requires that packets with the all-zeroes
* MAC address are allowed to be received by the driver. This function adds the
* all-zeroes destination address to the list of acceptable addresses.
*
* Return: 0 on success, negative value otherwise.
*/
int igc_enable_empty_addr_recv(struct igc_adapter *adapter)
{
u8 empty[ETH_ALEN] = {};
return igc_add_mac_filter(adapter, IGC_MAC_FILTER_TYPE_DST, empty, -1);
}
void igc_disable_empty_addr_recv(struct igc_adapter *adapter)
{
u8 empty[ETH_ALEN] = {};
igc_del_mac_filter(adapter, IGC_MAC_FILTER_TYPE_DST, empty);
}
/**
* igc_set_rx_mode - Secondary Unicast, Multicast and Promiscuous mode set
* @netdev: network interface device structure
@ -5311,6 +5347,9 @@ void igc_down(struct igc_adapter *adapter)
igc_disable_all_tx_rings_hw(adapter);
igc_clean_all_tx_rings(adapter);
igc_clean_all_rx_rings(adapter);
if (adapter->fpe.mmsv.pmac_enabled)
ethtool_mmsv_stop(&adapter->fpe.mmsv);
}
void igc_reinit_locked(struct igc_adapter *adapter)
@ -5835,6 +5874,10 @@ static void igc_watchdog_task(struct work_struct *work)
*/
igc_tsn_adjust_txtime_offset(adapter);
if (adapter->fpe.mmsv.pmac_enabled)
ethtool_mmsv_link_state_handle(&adapter->fpe.mmsv,
true);
if (adapter->link_speed != SPEED_1000)
goto no_wait;
@ -5870,6 +5913,10 @@ static void igc_watchdog_task(struct work_struct *work)
netdev_info(netdev, "NIC Link is Down\n");
netif_carrier_off(netdev);
if (adapter->fpe.mmsv.pmac_enabled)
ethtool_mmsv_link_state_handle(&adapter->fpe.mmsv,
false);
/* link state has changed, schedule phy info update */
if (!test_bit(__IGC_DOWN, &adapter->state))
mod_timer(&adapter->phy_info_timer,
@ -6439,6 +6486,10 @@ static int igc_save_qbv_schedule(struct igc_adapter *adapter,
if (!validate_schedule(adapter, qopt))
return -EINVAL;
/* preemptible isn't supported yet */
if (qopt->mqprio.preemptible_tcs)
return -EOPNOTSUPP;
igc_ptp_read(adapter, &now);
if (igc_tsn_is_taprio_activated_by_user(adapter) &&
@ -6779,7 +6830,7 @@ static int igc_xdp_xmit(struct net_device *dev, int num_frames,
if (unlikely(flags & ~XDP_XMIT_FLAGS_MASK))
return -EINVAL;
ring = igc_xdp_get_tx_ring(adapter, cpu);
ring = igc_get_tx_ring(adapter, cpu);
nq = txring_txq(ring);
__netif_tx_lock(nq, cpu);
@ -7160,8 +7211,8 @@ static int igc_probe(struct pci_dev *pdev,
}
/* configure RXPBSIZE and TXPBSIZE */
wr32(IGC_RXPBS, I225_RXPBSIZE_DEFAULT);
wr32(IGC_TXPBS, I225_TXPBSIZE_DEFAULT);
wr32(IGC_RXPBS, IGC_RXPBSIZE_EXP_BMC_DEFAULT);
wr32(IGC_TXPBS, IGC_TXPBSIZE_DEFAULT);
timer_setup(&adapter->watchdog_timer, igc_watchdog, 0);
timer_setup(&adapter->phy_info_timer, igc_update_phy_info, 0);
@ -7193,6 +7244,8 @@ static int igc_probe(struct pci_dev *pdev,
igc_tsn_clear_schedule(adapter);
igc_fpe_init(adapter);
/* reset the hardware with the new settings */
igc_reset(adapter);

View File

@ -222,6 +222,22 @@
#define IGC_FTQF(_n) (0x059E0 + (4 * (_n))) /* 5-tuple Queue Fltr */
/* Time sync registers - preemption statistics */
#define IGC_PRMPTDRCNT 0x04284 /* Good RX Preempted Packets */
#define IGC_PRMEVNTTCNT 0x04298 /* TX Preemption event counter */
#define IGC_PRMEVNTRCNT 0x0429C /* RX Preemption event counter */
/* Preemption Exception Counter */
#define IGC_PRMEXCPRCNT 0x42A0
/* Received out of order packets with SMD-C */
#define IGC_PRMEXCPRCNT_OOO_SMDC 0x000000FF
/* Received out of order packets with SMD-C and wrong Frame CNT */
#define IGC_PRMEXCPRCNT_OOO_FRAME_CNT 0x0000FF00
/* Received out of order packets with SMD-C and wrong Frag CNT */
#define IGC_PRMEXCPRCNT_OOO_FRAG_CNT 0x00FF0000
/* Received packets with SMD-S and wrong Frag CNT and Frame CNT */
#define IGC_PRMEXCPRCNT_MISS_FRAME_FRAG_CNT 0xFF000000
/* Transmit Scheduling Registers */
#define IGC_TQAVCTRL 0x3570
#define IGC_TXQCTL(_n) (0x3344 + 0x4 * (_n))

View File

@ -2,9 +2,143 @@
/* Copyright (c) 2019 Intel Corporation */
#include "igc.h"
#include "igc_base.h"
#include "igc_hw.h"
#include "igc_tsn.h"
#define MIN_MULTPLIER_TX_MIN_FRAG 0
#define MAX_MULTPLIER_TX_MIN_FRAG 3
/* Frag size is based on the Section 8.12.2 of the SW User Manual */
#define TX_MIN_FRAG_SIZE 64
#define TX_MAX_FRAG_SIZE (TX_MIN_FRAG_SIZE * \
(MAX_MULTPLIER_TX_MIN_FRAG + 1))
DEFINE_STATIC_KEY_FALSE(igc_fpe_enabled);
static int igc_fpe_init_smd_frame(struct igc_ring *ring,
struct igc_tx_buffer *buffer,
struct sk_buff *skb)
{
dma_addr_t dma = dma_map_single(ring->dev, skb->data, skb->len,
DMA_TO_DEVICE);
if (dma_mapping_error(ring->dev, dma)) {
netdev_err_once(ring->netdev, "Failed to map DMA for TX\n");
return -ENOMEM;
}
buffer->skb = skb;
buffer->protocol = 0;
buffer->bytecount = skb->len;
buffer->gso_segs = 1;
buffer->time_stamp = jiffies;
dma_unmap_len_set(buffer, len, skb->len);
dma_unmap_addr_set(buffer, dma, dma);
return 0;
}
static int igc_fpe_init_tx_descriptor(struct igc_ring *ring,
struct sk_buff *skb,
enum igc_txd_popts_type type)
{
u32 cmd_type, olinfo_status = 0;
struct igc_tx_buffer *buffer;
union igc_adv_tx_desc *desc;
int err;
if (!igc_desc_unused(ring))
return -EBUSY;
buffer = &ring->tx_buffer_info[ring->next_to_use];
err = igc_fpe_init_smd_frame(ring, buffer, skb);
if (err)
return err;
cmd_type = IGC_ADVTXD_DTYP_DATA | IGC_ADVTXD_DCMD_DEXT |
IGC_ADVTXD_DCMD_IFCS | IGC_TXD_DCMD |
buffer->bytecount;
olinfo_status |= FIELD_PREP(IGC_ADVTXD_PAYLEN_MASK, buffer->bytecount);
switch (type) {
case SMD_V:
case SMD_R:
olinfo_status |= FIELD_PREP(IGC_TXD_POPTS_SMD_MASK, type);
break;
}
desc = IGC_TX_DESC(ring, ring->next_to_use);
desc->read.cmd_type_len = cpu_to_le32(cmd_type);
desc->read.olinfo_status = cpu_to_le32(olinfo_status);
desc->read.buffer_addr = cpu_to_le64(dma_unmap_addr(buffer, dma));
netdev_tx_sent_queue(txring_txq(ring), skb->len);
buffer->next_to_watch = desc;
ring->next_to_use = (ring->next_to_use + 1) % ring->count;
return 0;
}
static int igc_fpe_xmit_smd_frame(struct igc_adapter *adapter,
enum igc_txd_popts_type type)
{
int cpu = smp_processor_id();
struct netdev_queue *nq;
struct igc_ring *ring;
struct sk_buff *skb;
int err;
ring = igc_get_tx_ring(adapter, cpu);
nq = txring_txq(ring);
skb = alloc_skb(SMD_FRAME_SIZE, GFP_ATOMIC);
if (!skb)
return -ENOMEM;
skb_put_zero(skb, SMD_FRAME_SIZE);
__netif_tx_lock(nq, cpu);
err = igc_fpe_init_tx_descriptor(ring, skb, type);
igc_flush_tx_descriptors(ring);
__netif_tx_unlock(nq);
return err;
}
static void igc_fpe_send_mpacket(struct ethtool_mmsv *mmsv,
enum ethtool_mpacket type)
{
struct igc_fpe_t *fpe = container_of(mmsv, struct igc_fpe_t, mmsv);
struct igc_adapter *adapter;
int err;
adapter = container_of(fpe, struct igc_adapter, fpe);
if (type == ETHTOOL_MPACKET_VERIFY) {
err = igc_fpe_xmit_smd_frame(adapter, SMD_V);
if (err && net_ratelimit())
netdev_err(adapter->netdev, "Error sending SMD-V\n");
} else if (type == ETHTOOL_MPACKET_RESPONSE) {
err = igc_fpe_xmit_smd_frame(adapter, SMD_R);
if (err && net_ratelimit())
netdev_err(adapter->netdev, "Error sending SMD-R frame\n");
}
}
static const struct ethtool_mmsv_ops igc_mmsv_ops = {
.send_mpacket = igc_fpe_send_mpacket,
};
void igc_fpe_init(struct igc_adapter *adapter)
{
adapter->fpe.tx_min_frag_size = TX_MIN_FRAG_SIZE;
ethtool_mmsv_init(&adapter->fpe.mmsv, adapter->netdev, &igc_mmsv_ops);
}
static bool is_any_launchtime(struct igc_adapter *adapter)
{
int i;
@ -49,6 +183,9 @@ static unsigned int igc_tsn_new_flags(struct igc_adapter *adapter)
if (adapter->strict_priority_enable)
new_flags |= IGC_FLAG_TSN_LEGACY_ENABLED;
if (adapter->fpe.mmsv.pmac_enabled)
new_flags |= IGC_FLAG_TSN_PREEMPT_ENABLED;
return new_flags;
}
@ -125,6 +262,29 @@ static void igc_tsn_tx_arb(struct igc_adapter *adapter, u16 *queue_per_tc)
wr32(IGC_TXARB, txarb);
}
/**
* igc_tsn_set_rxpbsize - Set the receive packet buffer size
* @adapter: Pointer to the igc_adapter structure
* @rxpbs_exp_bmc_be: Value to set the receive packet buffer size, including
* express buffer, BMC buffer, and Best Effort buffer
*
* The IGC_RXPBS register value may include allocations for the Express buffer,
* BMC buffer, Best Effort buffer, and the timestamp descriptor buffer
* (IGC_RXPBS_CFG_TS_EN).
*/
static void igc_tsn_set_rxpbsize(struct igc_adapter *adapter,
u32 rxpbs_exp_bmc_be)
{
struct igc_hw *hw = &adapter->hw;
u32 rxpbs = rd32(IGC_RXPBS);
rxpbs &= ~(IGC_RXPBSIZE_EXP_MASK | IGC_BMC2OSPBSIZE_MASK |
IGC_RXPBSIZE_BE_MASK);
rxpbs |= rxpbs_exp_bmc_be;
wr32(IGC_RXPBS, rxpbs);
}
/* Returns the TSN specific registers to their default values after
* the adapter is reset.
*/
@ -136,15 +296,18 @@ static int igc_tsn_disable_offload(struct igc_adapter *adapter)
int i;
wr32(IGC_GTXOFFSET, 0);
wr32(IGC_TXPBS, I225_TXPBSIZE_DEFAULT);
wr32(IGC_TXPBS, IGC_TXPBSIZE_DEFAULT);
wr32(IGC_DTXMXPKTSZ, IGC_DTXMXPKTSZ_DEFAULT);
igc_tsn_set_rxpbsize(adapter, IGC_RXPBSIZE_EXP_BMC_DEFAULT);
if (igc_is_device_id_i226(hw))
igc_tsn_restore_retx_default(adapter);
tqavctrl = rd32(IGC_TQAVCTRL);
tqavctrl &= ~(IGC_TQAVCTRL_TRANSMIT_MODE_TSN |
IGC_TQAVCTRL_ENHANCED_QAV | IGC_TQAVCTRL_FUTSCDDIS);
IGC_TQAVCTRL_ENHANCED_QAV | IGC_TQAVCTRL_FUTSCDDIS |
IGC_TQAVCTRL_PREEMPT_ENA | IGC_TQAVCTRL_MIN_FRAG_MASK);
wr32(IGC_TQAVCTRL, tqavctrl);
@ -190,18 +353,43 @@ static void igc_tsn_set_retx_qbvfullthreshold(struct igc_adapter *adapter)
wr32(IGC_RETX_CTL, retxctl);
}
static u8 igc_fpe_get_frag_size_mult(const struct igc_fpe_t *fpe)
{
u8 mult = (fpe->tx_min_frag_size / TX_MIN_FRAG_SIZE) - 1;
return clamp_t(u8, mult, MIN_MULTPLIER_TX_MIN_FRAG,
MAX_MULTPLIER_TX_MIN_FRAG);
}
u32 igc_fpe_get_supported_frag_size(u32 frag_size)
{
const u32 supported_sizes[] = {64, 128, 192, 256};
/* Find the smallest supported size that is >= frag_size */
for (int i = 0; i < ARRAY_SIZE(supported_sizes); i++) {
if (frag_size <= supported_sizes[i])
return supported_sizes[i];
}
/* Should not happen */
return TX_MAX_FRAG_SIZE;
}
static int igc_tsn_enable_offload(struct igc_adapter *adapter)
{
struct igc_hw *hw = &adapter->hw;
u32 tqavctrl, baset_l, baset_h;
u32 sec, nsec, cycle;
ktime_t base_time, systim;
u32 frag_size_mult;
int i;
wr32(IGC_TSAUXC, 0);
wr32(IGC_DTXMXPKTSZ, IGC_DTXMXPKTSZ_TSN);
wr32(IGC_TXPBS, IGC_TXPBSIZE_TSN);
igc_tsn_set_rxpbsize(adapter, IGC_RXPBSIZE_EXP_BMC_BE_TSN);
if (igc_is_device_id_i226(hw))
igc_tsn_set_retx_qbvfullthreshold(adapter);
@ -361,10 +549,16 @@ static int igc_tsn_enable_offload(struct igc_adapter *adapter)
wr32(IGC_TXQCTL(i), txqctl);
}
tqavctrl = rd32(IGC_TQAVCTRL) & ~IGC_TQAVCTRL_FUTSCDDIS;
tqavctrl = rd32(IGC_TQAVCTRL) & ~(IGC_TQAVCTRL_FUTSCDDIS |
IGC_TQAVCTRL_PREEMPT_ENA | IGC_TQAVCTRL_MIN_FRAG_MASK);
tqavctrl |= IGC_TQAVCTRL_TRANSMIT_MODE_TSN | IGC_TQAVCTRL_ENHANCED_QAV;
if (adapter->fpe.mmsv.pmac_enabled)
tqavctrl |= IGC_TQAVCTRL_PREEMPT_ENA;
frag_size_mult = igc_fpe_get_frag_size_mult(&adapter->fpe);
tqavctrl |= FIELD_PREP(IGC_TQAVCTRL_MIN_FRAG_MASK, frag_size_mult);
adapter->qbv_count++;
cycle = adapter->cycle_time;
@ -425,6 +619,14 @@ int igc_tsn_reset(struct igc_adapter *adapter)
unsigned int new_flags;
int err = 0;
if (adapter->fpe.mmsv.pmac_enabled) {
err = igc_enable_empty_addr_recv(adapter);
if (err && net_ratelimit())
netdev_err(adapter->netdev, "Error adding empty address to MAC filter\n");
} else {
igc_disable_empty_addr_recv(adapter);
}
new_flags = igc_tsn_new_flags(adapter);
if (!(new_flags & IGC_FLAG_TSN_ANY_ENABLED))

View File

@ -4,9 +4,61 @@
#ifndef _IGC_TSN_H_
#define _IGC_TSN_H_
#define IGC_RX_MIN_FRAG_SIZE 60
#define SMD_FRAME_SIZE 60
enum igc_txd_popts_type {
SMD_V = 0x01,
SMD_R = 0x02,
};
DECLARE_STATIC_KEY_FALSE(igc_fpe_enabled);
void igc_fpe_init(struct igc_adapter *adapter);
u32 igc_fpe_get_supported_frag_size(u32 frag_size);
int igc_tsn_offload_apply(struct igc_adapter *adapter);
int igc_tsn_reset(struct igc_adapter *adapter);
void igc_tsn_adjust_txtime_offset(struct igc_adapter *adapter);
bool igc_tsn_is_taprio_activated_by_user(struct igc_adapter *adapter);
static inline bool igc_fpe_is_pmac_enabled(struct igc_adapter *adapter)
{
return static_branch_unlikely(&igc_fpe_enabled) &&
adapter->fpe.mmsv.pmac_enabled;
}
static inline bool igc_fpe_handle_mpacket(struct igc_adapter *adapter,
union igc_adv_rx_desc *rx_desc,
unsigned int size, void *pktbuf)
{
u32 status_error = le32_to_cpu(rx_desc->wb.upper.status_error);
int smd;
smd = FIELD_GET(IGC_RXDADV_STAT_SMD_TYPE_MASK, status_error);
if (smd != IGC_RXD_STAT_SMD_TYPE_V && smd != IGC_RXD_STAT_SMD_TYPE_R)
return false;
if (size == SMD_FRAME_SIZE && mem_is_zero(pktbuf, SMD_FRAME_SIZE)) {
struct ethtool_mmsv *mmsv = &adapter->fpe.mmsv;
enum ethtool_mmsv_event event;
if (smd == IGC_RXD_STAT_SMD_TYPE_V)
event = ETHTOOL_MMSV_LP_SENT_VERIFY_MPACKET;
else
event = ETHTOOL_MMSV_LP_SENT_RESPONSE_MPACKET;
ethtool_mmsv_event_handle(mmsv, event);
}
return true;
}
static inline bool igc_fpe_transmitted_smd_v(union igc_adv_tx_desc *tx_desc)
{
u32 olinfo_status = le32_to_cpu(tx_desc->read.olinfo_status);
u8 smd = FIELD_GET(IGC_TXD_POPTS_SMD_MASK, olinfo_status);
return smd == SMD_V;
}
#endif /* _IGC_BASE_H */

View File

@ -3,6 +3,7 @@ config STMMAC_ETH
tristate "STMicroelectronics Multi-Gigabit Ethernet driver"
depends on HAS_IOMEM && HAS_DMA
depends on PTP_1588_CLOCK_OPTIONAL
depends on ETHTOOL_NETLINK
select MII
select PCS_XPCS
select PAGE_POOL

View File

@ -149,21 +149,9 @@ struct stmmac_channel {
};
struct stmmac_fpe_cfg {
/* Serialize access to MAC Merge state between ethtool requests
* and link state updates.
*/
spinlock_t lock;
struct ethtool_mmsv mmsv;
const struct stmmac_fpe_reg *reg;
u32 fpe_csr; /* MAC_FPE_CTRL_STS reg cache */
enum ethtool_mm_verify_status status;
struct timer_list verify_timer;
bool verify_enabled;
int verify_retries;
bool pmac_enabled;
u32 verify_time;
bool tx_enabled;
u32 fpe_csr; /* MAC_FPE_CTRL_STS reg cache */
};
struct stmmac_tc_entry {

View File

@ -1210,36 +1210,16 @@ static int stmmac_get_mm(struct net_device *ndev,
struct ethtool_mm_state *state)
{
struct stmmac_priv *priv = netdev_priv(ndev);
unsigned long flags;
u32 frag_size;
if (!stmmac_fpe_supported(priv))
return -EOPNOTSUPP;
spin_lock_irqsave(&priv->fpe_cfg.lock, flags);
state->max_verify_time = STMMAC_FPE_MM_MAX_VERIFY_TIME_MS;
state->verify_enabled = priv->fpe_cfg.verify_enabled;
state->pmac_enabled = priv->fpe_cfg.pmac_enabled;
state->verify_time = priv->fpe_cfg.verify_time;
state->tx_enabled = priv->fpe_cfg.tx_enabled;
state->verify_status = priv->fpe_cfg.status;
state->rx_min_frag_size = ETH_ZLEN;
/* FPE active if common tx_enabled and
* (verification success or disabled(forced))
*/
if (state->tx_enabled &&
(state->verify_status == ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED ||
state->verify_status == ETHTOOL_MM_VERIFY_STATUS_DISABLED))
state->tx_active = true;
else
state->tx_active = false;
frag_size = stmmac_fpe_get_add_frag_size(priv);
state->tx_min_frag_size = ethtool_mm_frag_size_add_to_min(frag_size);
spin_unlock_irqrestore(&priv->fpe_cfg.lock, flags);
ethtool_mmsv_get_mm(&priv->fpe_cfg.mmsv, state);
return 0;
}
@ -1248,8 +1228,6 @@ static int stmmac_set_mm(struct net_device *ndev, struct ethtool_mm_cfg *cfg,
struct netlink_ext_ack *extack)
{
struct stmmac_priv *priv = netdev_priv(ndev);
struct stmmac_fpe_cfg *fpe_cfg = &priv->fpe_cfg;
unsigned long flags;
u32 frag_size;
int err;
@ -1258,23 +1236,8 @@ static int stmmac_set_mm(struct net_device *ndev, struct ethtool_mm_cfg *cfg,
if (err)
return err;
/* Wait for the verification that's currently in progress to finish */
timer_shutdown_sync(&fpe_cfg->verify_timer);
spin_lock_irqsave(&fpe_cfg->lock, flags);
fpe_cfg->verify_enabled = cfg->verify_enabled;
fpe_cfg->pmac_enabled = cfg->pmac_enabled;
fpe_cfg->verify_time = cfg->verify_time;
fpe_cfg->tx_enabled = cfg->tx_enabled;
if (!cfg->verify_enabled)
fpe_cfg->status = ETHTOOL_MM_VERIFY_STATUS_DISABLED;
stmmac_fpe_set_add_frag_size(priv, frag_size);
stmmac_fpe_apply(priv);
spin_unlock_irqrestore(&fpe_cfg->lock, flags);
ethtool_mmsv_set_mm(&priv->fpe_cfg.mmsv, cfg);
return 0;
}

View File

@ -27,12 +27,6 @@
#define STMMAC_MAC_FPE_CTRL_STS_SVER BIT(1)
#define STMMAC_MAC_FPE_CTRL_STS_EFPE BIT(0)
/* FPE link-partner hand-shaking mPacket type */
enum stmmac_mpacket_type {
MPACKET_VERIFY = 0,
MPACKET_RESPONSE = 1,
};
struct stmmac_fpe_reg {
const u32 mac_fpe_reg; /* offset of MAC_FPE_CTRL_STS */
const u32 mtl_fpe_reg; /* offset of MTL_FPE_CTRL_STS */
@ -48,10 +42,10 @@ bool stmmac_fpe_supported(struct stmmac_priv *priv)
priv->hw->mac->fpe_map_preemption_class;
}
static void stmmac_fpe_configure(struct stmmac_priv *priv, bool tx_enable,
bool pmac_enable)
static void stmmac_fpe_configure_tx(struct ethtool_mmsv *mmsv, bool tx_enable)
{
struct stmmac_fpe_cfg *cfg = &priv->fpe_cfg;
struct stmmac_fpe_cfg *cfg = container_of(mmsv, struct stmmac_fpe_cfg, mmsv);
struct stmmac_priv *priv = container_of(cfg, struct stmmac_priv, fpe_cfg);
const struct stmmac_fpe_reg *reg = cfg->reg;
u32 num_rxq = priv->plat->rx_queues_to_use;
void __iomem *ioaddr = priv->ioaddr;
@ -68,6 +62,15 @@ static void stmmac_fpe_configure(struct stmmac_priv *priv, bool tx_enable,
cfg->fpe_csr = 0;
}
writel(cfg->fpe_csr, ioaddr + reg->mac_fpe_reg);
}
static void stmmac_fpe_configure_pmac(struct ethtool_mmsv *mmsv, bool pmac_enable)
{
struct stmmac_fpe_cfg *cfg = container_of(mmsv, struct stmmac_fpe_cfg, mmsv);
struct stmmac_priv *priv = container_of(cfg, struct stmmac_priv, fpe_cfg);
const struct stmmac_fpe_reg *reg = cfg->reg;
void __iomem *ioaddr = priv->ioaddr;
u32 value;
value = readl(ioaddr + reg->int_en_reg);
@ -85,47 +88,45 @@ static void stmmac_fpe_configure(struct stmmac_priv *priv, bool tx_enable,
writel(value, ioaddr + reg->int_en_reg);
}
static void stmmac_fpe_send_mpacket(struct stmmac_priv *priv,
enum stmmac_mpacket_type type)
static void stmmac_fpe_send_mpacket(struct ethtool_mmsv *mmsv,
enum ethtool_mpacket type)
{
const struct stmmac_fpe_reg *reg = priv->fpe_cfg.reg;
struct stmmac_fpe_cfg *cfg = container_of(mmsv, struct stmmac_fpe_cfg, mmsv);
struct stmmac_priv *priv = container_of(cfg, struct stmmac_priv, fpe_cfg);
const struct stmmac_fpe_reg *reg = cfg->reg;
void __iomem *ioaddr = priv->ioaddr;
u32 value = priv->fpe_cfg.fpe_csr;
u32 value = cfg->fpe_csr;
if (type == MPACKET_VERIFY)
if (type == ETHTOOL_MPACKET_VERIFY)
value |= STMMAC_MAC_FPE_CTRL_STS_SVER;
else if (type == MPACKET_RESPONSE)
else if (type == ETHTOOL_MPACKET_RESPONSE)
value |= STMMAC_MAC_FPE_CTRL_STS_SRSP;
writel(value, ioaddr + reg->mac_fpe_reg);
}
static const struct ethtool_mmsv_ops stmmac_mmsv_ops = {
.configure_tx = stmmac_fpe_configure_tx,
.configure_pmac = stmmac_fpe_configure_pmac,
.send_mpacket = stmmac_fpe_send_mpacket,
};
static void stmmac_fpe_event_status(struct stmmac_priv *priv, int status)
{
struct stmmac_fpe_cfg *fpe_cfg = &priv->fpe_cfg;
struct ethtool_mmsv *mmsv = &fpe_cfg->mmsv;
/* This is interrupt context, just spin_lock() */
spin_lock(&fpe_cfg->lock);
if (status == FPE_EVENT_UNKNOWN)
return;
if (!fpe_cfg->pmac_enabled || status == FPE_EVENT_UNKNOWN)
goto unlock_out;
/* LP has sent verify mPacket */
if ((status & FPE_EVENT_RVER) == FPE_EVENT_RVER)
stmmac_fpe_send_mpacket(priv, MPACKET_RESPONSE);
ethtool_mmsv_event_handle(mmsv, ETHTOOL_MMSV_LP_SENT_VERIFY_MPACKET);
/* Local has sent verify mPacket */
if ((status & FPE_EVENT_TVER) == FPE_EVENT_TVER &&
fpe_cfg->status != ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED)
fpe_cfg->status = ETHTOOL_MM_VERIFY_STATUS_VERIFYING;
if ((status & FPE_EVENT_TVER) == FPE_EVENT_TVER)
ethtool_mmsv_event_handle(mmsv, ETHTOOL_MMSV_LD_SENT_VERIFY_MPACKET);
/* LP has sent response mPacket */
if ((status & FPE_EVENT_RRSP) == FPE_EVENT_RRSP &&
fpe_cfg->status == ETHTOOL_MM_VERIFY_STATUS_VERIFYING)
fpe_cfg->status = ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED;
unlock_out:
spin_unlock(&fpe_cfg->lock);
if ((status & FPE_EVENT_RRSP) == FPE_EVENT_RRSP)
ethtool_mmsv_event_handle(mmsv, ETHTOOL_MMSV_LP_SENT_RESPONSE_MPACKET);
}
void stmmac_fpe_irq_status(struct stmmac_priv *priv)
@ -164,119 +165,16 @@ void stmmac_fpe_irq_status(struct stmmac_priv *priv)
stmmac_fpe_event_status(priv, status);
}
/**
* stmmac_fpe_verify_timer - Timer for MAC Merge verification
* @t: timer_list struct containing private info
*
* Verify the MAC Merge capability in the local TX direction, by
* transmitting Verify mPackets up to 3 times. Wait until link
* partner responds with a Response mPacket, otherwise fail.
*/
static void stmmac_fpe_verify_timer(struct timer_list *t)
{
struct stmmac_fpe_cfg *fpe_cfg = from_timer(fpe_cfg, t, verify_timer);
struct stmmac_priv *priv = container_of(fpe_cfg, struct stmmac_priv,
fpe_cfg);
unsigned long flags;
bool rearm = false;
spin_lock_irqsave(&fpe_cfg->lock, flags);
switch (fpe_cfg->status) {
case ETHTOOL_MM_VERIFY_STATUS_INITIAL:
case ETHTOOL_MM_VERIFY_STATUS_VERIFYING:
if (fpe_cfg->verify_retries != 0) {
stmmac_fpe_send_mpacket(priv, MPACKET_VERIFY);
rearm = true;
} else {
fpe_cfg->status = ETHTOOL_MM_VERIFY_STATUS_FAILED;
}
fpe_cfg->verify_retries--;
break;
case ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED:
stmmac_fpe_configure(priv, true, true);
break;
default:
break;
}
if (rearm) {
mod_timer(&fpe_cfg->verify_timer,
jiffies + msecs_to_jiffies(fpe_cfg->verify_time));
}
spin_unlock_irqrestore(&fpe_cfg->lock, flags);
}
static void stmmac_fpe_verify_timer_arm(struct stmmac_fpe_cfg *fpe_cfg)
{
if (fpe_cfg->pmac_enabled && fpe_cfg->tx_enabled &&
fpe_cfg->verify_enabled &&
fpe_cfg->status != ETHTOOL_MM_VERIFY_STATUS_FAILED &&
fpe_cfg->status != ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED) {
timer_setup(&fpe_cfg->verify_timer, stmmac_fpe_verify_timer, 0);
mod_timer(&fpe_cfg->verify_timer, jiffies);
}
}
void stmmac_fpe_init(struct stmmac_priv *priv)
{
priv->fpe_cfg.verify_retries = STMMAC_FPE_MM_MAX_VERIFY_RETRIES;
priv->fpe_cfg.verify_time = STMMAC_FPE_MM_MAX_VERIFY_TIME_MS;
priv->fpe_cfg.status = ETHTOOL_MM_VERIFY_STATUS_DISABLED;
timer_setup(&priv->fpe_cfg.verify_timer, stmmac_fpe_verify_timer, 0);
spin_lock_init(&priv->fpe_cfg.lock);
ethtool_mmsv_init(&priv->fpe_cfg.mmsv, priv->dev,
&stmmac_mmsv_ops);
if ((!priv->fpe_cfg.reg || !priv->hw->mac->fpe_map_preemption_class) &&
priv->dma_cap.fpesel)
dev_info(priv->device, "FPE is not supported by driver.\n");
}
void stmmac_fpe_apply(struct stmmac_priv *priv)
{
struct stmmac_fpe_cfg *fpe_cfg = &priv->fpe_cfg;
/* If verification is disabled, configure FPE right away.
* Otherwise let the timer code do it.
*/
if (!fpe_cfg->verify_enabled) {
stmmac_fpe_configure(priv, fpe_cfg->tx_enabled,
fpe_cfg->pmac_enabled);
} else {
fpe_cfg->status = ETHTOOL_MM_VERIFY_STATUS_INITIAL;
fpe_cfg->verify_retries = STMMAC_FPE_MM_MAX_VERIFY_RETRIES;
if (netif_running(priv->dev))
stmmac_fpe_verify_timer_arm(fpe_cfg);
}
}
void stmmac_fpe_link_state_handle(struct stmmac_priv *priv, bool is_up)
{
struct stmmac_fpe_cfg *fpe_cfg = &priv->fpe_cfg;
unsigned long flags;
timer_shutdown_sync(&fpe_cfg->verify_timer);
spin_lock_irqsave(&fpe_cfg->lock, flags);
if (is_up && fpe_cfg->pmac_enabled) {
/* VERIFY process requires pmac enabled when NIC comes up */
stmmac_fpe_configure(priv, false, true);
/* New link => maybe new partner => new verification process */
stmmac_fpe_apply(priv);
} else {
/* No link => turn off EFPE */
stmmac_fpe_configure(priv, false, false);
}
spin_unlock_irqrestore(&fpe_cfg->lock, flags);
}
int stmmac_fpe_get_add_frag_size(struct stmmac_priv *priv)
{
const struct stmmac_fpe_reg *reg = priv->fpe_cfg.reg;

View File

@ -9,15 +9,10 @@
#include <linux/types.h>
#include <linux/netdevice.h>
#define STMMAC_FPE_MM_MAX_VERIFY_RETRIES 3
#define STMMAC_FPE_MM_MAX_VERIFY_TIME_MS 128
struct stmmac_priv;
void stmmac_fpe_link_state_handle(struct stmmac_priv *priv, bool is_up);
bool stmmac_fpe_supported(struct stmmac_priv *priv);
void stmmac_fpe_init(struct stmmac_priv *priv);
void stmmac_fpe_apply(struct stmmac_priv *priv);
void stmmac_fpe_irq_status(struct stmmac_priv *priv);
int stmmac_fpe_get_add_frag_size(struct stmmac_priv *priv);
void stmmac_fpe_set_add_frag_size(struct stmmac_priv *priv, u32 add_frag_size);

View File

@ -946,7 +946,7 @@ static void stmmac_mac_link_down(struct phylink_config *config,
stmmac_set_eee_pls(priv, priv->hw, false);
if (stmmac_fpe_supported(priv))
stmmac_fpe_link_state_handle(priv, false);
ethtool_mmsv_link_state_handle(&priv->fpe_cfg.mmsv, false);
}
static void stmmac_mac_link_up(struct phylink_config *config,
@ -1064,7 +1064,7 @@ static void stmmac_mac_link_up(struct phylink_config *config,
stmmac_set_eee_pls(priv, priv->hw, true);
if (stmmac_fpe_supported(priv))
stmmac_fpe_link_state_handle(priv, true);
ethtool_mmsv_link_state_handle(&priv->fpe_cfg.mmsv, true);
if (priv->plat->flags & STMMAC_FLAG_HWTSTAMP_CORRECT_LATENCY)
stmmac_hwtstamp_correct_latency(priv, priv);
@ -4152,7 +4152,7 @@ static int stmmac_release(struct net_device *dev)
stmmac_release_ptp(priv);
if (stmmac_fpe_supported(priv))
timer_shutdown_sync(&priv->fpe_cfg.verify_timer);
ethtool_mmsv_stop(&priv->fpe_cfg.mmsv);
pm_runtime_put(priv->device);
@ -7871,7 +7871,7 @@ int stmmac_suspend(struct device *dev)
rtnl_unlock();
if (stmmac_fpe_supported(priv))
timer_shutdown_sync(&priv->fpe_cfg.verify_timer);
ethtool_mmsv_stop(&priv->fpe_cfg.mmsv);
return 0;
}

View File

@ -17,9 +17,13 @@
#include <linux/compat.h>
#include <linux/if_ether.h>
#include <linux/netlink.h>
#include <linux/timer_types.h>
#include <uapi/linux/ethtool.h>
#include <uapi/linux/net_tstamp.h>
#define ETHTOOL_MM_MAX_VERIFY_TIME_MS 128
#define ETHTOOL_MM_MAX_VERIFY_RETRIES 3
struct compat_ethtool_rx_flow_spec {
u32 flow_type;
union ethtool_flow_union h_u;
@ -718,6 +722,75 @@ struct ethtool_mm_stats {
u64 MACMergeHoldCount;
};
enum ethtool_mmsv_event {
ETHTOOL_MMSV_LP_SENT_VERIFY_MPACKET,
ETHTOOL_MMSV_LD_SENT_VERIFY_MPACKET,
ETHTOOL_MMSV_LP_SENT_RESPONSE_MPACKET,
};
/* MAC Merge verification mPacket type */
enum ethtool_mpacket {
ETHTOOL_MPACKET_VERIFY,
ETHTOOL_MPACKET_RESPONSE,
};
struct ethtool_mmsv;
/**
* struct ethtool_mmsv_ops - Operations for MAC Merge Software Verification
* @configure_tx: Driver callback for the event where the preemptible TX
* becomes active or inactive. Preemptible traffic
* classes must be committed to hardware only while
* preemptible TX is active.
* @configure_pmac: Driver callback for the event where the pMAC state
* changes as result of an administrative setting
* (ethtool) or a call to ethtool_mmsv_link_state_handle().
* @send_mpacket: Driver-provided method for sending a Verify or a Response
* mPacket.
*/
struct ethtool_mmsv_ops {
void (*configure_tx)(struct ethtool_mmsv *mmsv, bool tx_active);
void (*configure_pmac)(struct ethtool_mmsv *mmsv, bool pmac_enabled);
void (*send_mpacket)(struct ethtool_mmsv *mmsv, enum ethtool_mpacket mpacket);
};
/**
* struct ethtool_mmsv - MAC Merge Software Verification
* @ops: operations for MAC Merge Software Verification
* @dev: pointer to net_device structure
* @lock: serialize access to MAC Merge state between
* ethtool requests and link state updates.
* @status: current verification FSM state
* @verify_timer: timer for verification in local TX direction
* @verify_enabled: indicates if verification is enabled
* @verify_retries: number of retries for verification
* @pmac_enabled: indicates if the preemptible MAC is enabled
* @verify_time: time for verification in milliseconds
* @tx_enabled: indicates if transmission is enabled
*/
struct ethtool_mmsv {
const struct ethtool_mmsv_ops *ops;
struct net_device *dev;
spinlock_t lock;
enum ethtool_mm_verify_status status;
struct timer_list verify_timer;
bool verify_enabled;
int verify_retries;
bool pmac_enabled;
u32 verify_time;
bool tx_enabled;
};
void ethtool_mmsv_stop(struct ethtool_mmsv *mmsv);
void ethtool_mmsv_link_state_handle(struct ethtool_mmsv *mmsv, bool up);
void ethtool_mmsv_event_handle(struct ethtool_mmsv *mmsv,
enum ethtool_mmsv_event event);
void ethtool_mmsv_get_mm(struct ethtool_mmsv *mmsv,
struct ethtool_mm_state *state);
void ethtool_mmsv_set_mm(struct ethtool_mmsv *mmsv, struct ethtool_mm_cfg *cfg);
void ethtool_mmsv_init(struct ethtool_mmsv *mmsv, struct net_device *dev,
const struct ethtool_mmsv_ops *ops);
/**
* struct ethtool_rxfh_param - RXFH (RSS) parameters
* @hfunc: Defines the current RSS hash function used by HW (or to be set to).

View File

@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2022-2023 NXP
* Copyright 2022-2025 NXP
* Copyright 2024 Furong Xu <0x1207@gmail.com>
*/
#include "common.h"
#include "netlink.h"
@ -282,3 +283,279 @@ bool ethtool_dev_mm_supported(struct net_device *dev)
return supported;
}
EXPORT_SYMBOL_GPL(ethtool_dev_mm_supported);
static void ethtool_mmsv_configure_tx(struct ethtool_mmsv *mmsv,
bool tx_active)
{
if (mmsv->ops->configure_tx)
mmsv->ops->configure_tx(mmsv, tx_active);
}
static void ethtool_mmsv_configure_pmac(struct ethtool_mmsv *mmsv,
bool pmac_enabled)
{
if (mmsv->ops->configure_pmac)
mmsv->ops->configure_pmac(mmsv, pmac_enabled);
}
static void ethtool_mmsv_send_mpacket(struct ethtool_mmsv *mmsv,
enum ethtool_mpacket mpacket)
{
if (mmsv->ops->send_mpacket)
mmsv->ops->send_mpacket(mmsv, mpacket);
}
/**
* ethtool_mmsv_verify_timer - Timer for MAC Merge verification
* @t: timer_list struct containing private info
*
* Verify the MAC Merge capability in the local TX direction, by
* transmitting Verify mPackets up to 3 times. Wait until link
* partner responds with a Response mPacket, otherwise fail.
*/
static void ethtool_mmsv_verify_timer(struct timer_list *t)
{
struct ethtool_mmsv *mmsv = from_timer(mmsv, t, verify_timer);
unsigned long flags;
bool rearm = false;
spin_lock_irqsave(&mmsv->lock, flags);
switch (mmsv->status) {
case ETHTOOL_MM_VERIFY_STATUS_INITIAL:
case ETHTOOL_MM_VERIFY_STATUS_VERIFYING:
if (mmsv->verify_retries != 0) {
ethtool_mmsv_send_mpacket(mmsv, ETHTOOL_MPACKET_VERIFY);
rearm = true;
} else {
mmsv->status = ETHTOOL_MM_VERIFY_STATUS_FAILED;
}
mmsv->verify_retries--;
break;
case ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED:
ethtool_mmsv_configure_tx(mmsv, true);
break;
default:
break;
}
if (rearm) {
mod_timer(&mmsv->verify_timer,
jiffies + msecs_to_jiffies(mmsv->verify_time));
}
spin_unlock_irqrestore(&mmsv->lock, flags);
}
static void ethtool_mmsv_verify_timer_arm(struct ethtool_mmsv *mmsv)
{
if (mmsv->pmac_enabled && mmsv->tx_enabled && mmsv->verify_enabled &&
mmsv->status != ETHTOOL_MM_VERIFY_STATUS_FAILED &&
mmsv->status != ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED) {
timer_setup(&mmsv->verify_timer, ethtool_mmsv_verify_timer, 0);
mod_timer(&mmsv->verify_timer, jiffies);
}
}
static void ethtool_mmsv_apply(struct ethtool_mmsv *mmsv)
{
/* If verification is disabled, configure FPE right away.
* Otherwise let the timer code do it.
*/
if (!mmsv->verify_enabled) {
ethtool_mmsv_configure_pmac(mmsv, mmsv->pmac_enabled);
ethtool_mmsv_configure_tx(mmsv, mmsv->tx_enabled);
} else {
mmsv->status = ETHTOOL_MM_VERIFY_STATUS_INITIAL;
mmsv->verify_retries = ETHTOOL_MM_MAX_VERIFY_RETRIES;
if (netif_running(mmsv->dev))
ethtool_mmsv_verify_timer_arm(mmsv);
}
}
/**
* ethtool_mmsv_stop() - Stop MAC Merge Software Verification
* @mmsv: MAC Merge Software Verification state
*
* Drivers should call this method in a state where the hardware is
* about to lose state, like ndo_stop() or suspend(), and turning off
* MAC Merge features would be superfluous. Otherwise, prefer
* ethtool_mmsv_link_state_handle() with up=false.
*/
void ethtool_mmsv_stop(struct ethtool_mmsv *mmsv)
{
timer_shutdown_sync(&mmsv->verify_timer);
}
EXPORT_SYMBOL_GPL(ethtool_mmsv_stop);
/**
* ethtool_mmsv_link_state_handle() - Inform MAC Merge Software Verification
* of link state changes
* @mmsv: MAC Merge Software Verification state
* @up: True if device carrier is up and able to pass verification packets
*
* Calling context is expected to be from a task, interrupts enabled.
*/
void ethtool_mmsv_link_state_handle(struct ethtool_mmsv *mmsv, bool up)
{
unsigned long flags;
ethtool_mmsv_stop(mmsv);
spin_lock_irqsave(&mmsv->lock, flags);
if (up && mmsv->pmac_enabled) {
/* VERIFY process requires pMAC enabled when NIC comes up */
ethtool_mmsv_configure_pmac(mmsv, true);
/* New link => maybe new partner => new verification process */
ethtool_mmsv_apply(mmsv);
} else {
/* Reset the reported verification state while the link is down */
if (mmsv->verify_enabled)
mmsv->status = ETHTOOL_MM_VERIFY_STATUS_INITIAL;
/* No link or pMAC not enabled */
ethtool_mmsv_configure_pmac(mmsv, false);
ethtool_mmsv_configure_tx(mmsv, false);
}
spin_unlock_irqrestore(&mmsv->lock, flags);
}
EXPORT_SYMBOL_GPL(ethtool_mmsv_link_state_handle);
/**
* ethtool_mmsv_event_handle() - Inform MAC Merge Software Verification
* of interrupt-based events
* @mmsv: MAC Merge Software Verification state
* @event: Event which took place (packet transmission or reception)
*
* Calling context expects to have interrupts disabled.
*/
void ethtool_mmsv_event_handle(struct ethtool_mmsv *mmsv,
enum ethtool_mmsv_event event)
{
/* This is interrupt context, just spin_lock() */
spin_lock(&mmsv->lock);
if (!mmsv->pmac_enabled)
goto unlock;
switch (event) {
case ETHTOOL_MMSV_LP_SENT_VERIFY_MPACKET:
/* Link partner has sent verify mPacket */
ethtool_mmsv_send_mpacket(mmsv, ETHTOOL_MPACKET_RESPONSE);
break;
case ETHTOOL_MMSV_LD_SENT_VERIFY_MPACKET:
/* Local device has sent verify mPacket */
if (mmsv->status != ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED)
mmsv->status = ETHTOOL_MM_VERIFY_STATUS_VERIFYING;
break;
case ETHTOOL_MMSV_LP_SENT_RESPONSE_MPACKET:
/* Link partner has sent response mPacket */
if (mmsv->status == ETHTOOL_MM_VERIFY_STATUS_VERIFYING)
mmsv->status = ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED;
break;
}
unlock:
spin_unlock(&mmsv->lock);
}
EXPORT_SYMBOL_GPL(ethtool_mmsv_event_handle);
static bool ethtool_mmsv_is_tx_active(struct ethtool_mmsv *mmsv)
{
/* TX is active if administratively enabled, and verification either
* succeeded, or was administratively disabled.
*/
return mmsv->tx_enabled &&
(mmsv->status == ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED ||
mmsv->status == ETHTOOL_MM_VERIFY_STATUS_DISABLED);
}
/**
* ethtool_mmsv_get_mm() - get_mm() hook for MAC Merge Software Verification
* @mmsv: MAC Merge Software Verification state
* @state: see struct ethtool_mm_state
*
* Drivers are expected to call this from their ethtool_ops :: get_mm()
* method.
*/
void ethtool_mmsv_get_mm(struct ethtool_mmsv *mmsv,
struct ethtool_mm_state *state)
{
unsigned long flags;
spin_lock_irqsave(&mmsv->lock, flags);
state->max_verify_time = ETHTOOL_MM_MAX_VERIFY_TIME_MS;
state->verify_enabled = mmsv->verify_enabled;
state->pmac_enabled = mmsv->pmac_enabled;
state->verify_time = mmsv->verify_time;
state->tx_enabled = mmsv->tx_enabled;
state->verify_status = mmsv->status;
state->tx_active = ethtool_mmsv_is_tx_active(mmsv);
spin_unlock_irqrestore(&mmsv->lock, flags);
}
EXPORT_SYMBOL_GPL(ethtool_mmsv_get_mm);
/**
* ethtool_mmsv_set_mm() - set_mm() hook for MAC Merge Software Verification
* @mmsv: MAC Merge Software Verification state
* @cfg: see struct ethtool_mm_cfg
*
* Drivers are expected to call this from their ethtool_ops :: set_mm()
* method.
*/
void ethtool_mmsv_set_mm(struct ethtool_mmsv *mmsv, struct ethtool_mm_cfg *cfg)
{
unsigned long flags;
/* Wait for the verification that's currently in progress to finish */
ethtool_mmsv_stop(mmsv);
spin_lock_irqsave(&mmsv->lock, flags);
mmsv->verify_enabled = cfg->verify_enabled;
mmsv->pmac_enabled = cfg->pmac_enabled;
mmsv->verify_time = cfg->verify_time;
mmsv->tx_enabled = cfg->tx_enabled;
if (!cfg->verify_enabled)
mmsv->status = ETHTOOL_MM_VERIFY_STATUS_DISABLED;
ethtool_mmsv_apply(mmsv);
spin_unlock_irqrestore(&mmsv->lock, flags);
}
EXPORT_SYMBOL_GPL(ethtool_mmsv_set_mm);
/**
* ethtool_mmsv_init() - Initialize MAC Merge Software Verification state
* @mmsv: MAC Merge Software Verification state
* @dev: Pointer to network interface
* @ops: Methods for implementing the generic functionality
*
* The MAC Merge Software Verification is a timer- and event-based state
* machine intended for network interfaces which lack a hardware-based
* TX verification process (as per IEEE 802.3 clause 99.4.3). The timer
* is managed by the core code, whereas events are supplied by the
* driver explicitly calling one of the other API functions.
*/
void ethtool_mmsv_init(struct ethtool_mmsv *mmsv, struct net_device *dev,
const struct ethtool_mmsv_ops *ops)
{
mmsv->ops = ops;
mmsv->dev = dev;
mmsv->verify_retries = ETHTOOL_MM_MAX_VERIFY_RETRIES;
mmsv->verify_time = ETHTOOL_MM_MAX_VERIFY_TIME_MS;
mmsv->status = ETHTOOL_MM_VERIFY_STATUS_DISABLED;
timer_setup(&mmsv->verify_timer, ethtool_mmsv_verify_timer, 0);
spin_lock_init(&mmsv->lock);
}
EXPORT_SYMBOL_GPL(ethtool_mmsv_init);