mirror of
https://github.com/torvalds/linux.git
synced 2026-05-28 00:53:34 +02:00
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:
commit
3fec58f5a4
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
|
|
|
|||
279
net/ethtool/mm.c
279
net/ethtool/mm.c
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user