Merge branch 'intel-next-queue-1GbE'

Tony Nguyen says:

====================
Faizal Rahim says:

MAC Merge support for frame preemption was previously added for igc:
https://lore.kernel.org/netdev/20250418163822.3519810-1-anthony.l.nguyen@intel.com/

This series builds on that work and adds support for:
- Harmonizing taprio and mqprio queue priority behavior, based on past
  discussions and suggestions:
  https://lore.kernel.org/all/20250214102206.25dqgut5tbak2rkz@skbuf/
- Enabling preemptible queue support for both taprio and mqprio, with
  priority harmonization as a prerequisite.

Patch organization:
- Patches 1-3: Preparation work for patches 6 and 7
- Patches 4-5: Queue priority harmonization
- Patches 6-7: Add preemptible queue support
====================

Link: https://patch.msgid.link/20250611180314.2059166-1-anthony.l.nguyen@intel.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Paolo Abeni 2025-06-17 14:58:47 +02:00
commit 9149a63288
7 changed files with 189 additions and 43 deletions

View File

@ -43,6 +43,7 @@ void igc_ethtool_set_ops(struct net_device *);
struct igc_fpe_t {
struct ethtool_mmsv mmsv;
u32 tx_min_frag_size;
bool tx_enabled;
};
enum igc_mac_filter_type {
@ -163,6 +164,7 @@ struct igc_ring {
bool launchtime_enable; /* true if LaunchTime is enabled */
ktime_t last_tx_cycle; /* end of the cycle with a launchtime transmission */
ktime_t last_ff_cycle; /* Last cycle with an active first flag */
bool preemptible; /* True if preemptible queue, false if express queue */
u32 start_time;
u32 end_time;
@ -395,6 +397,7 @@ 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_PREEMPT_ENABLED BIT(19)
#define IGC_FLAG_TSN_REVERSE_TXQ_PRIO BIT(20)
#define IGC_FLAG_TSN_ANY_ENABLED \
(IGC_FLAG_TSN_QBV_ENABLED | IGC_FLAG_TSN_QAV_ENABLED | \
@ -485,12 +488,30 @@ static inline u32 igc_rss_type(const union igc_adv_rx_desc *rx_desc)
* descriptors until either it has this many to write back, or the
* ITR timer expires.
*/
#define IGC_RX_PTHRESH 8
#define IGC_RX_HTHRESH 8
#define IGC_TX_PTHRESH 8
#define IGC_TX_HTHRESH 1
#define IGC_RX_WTHRESH 4
#define IGC_TX_WTHRESH 16
#define IGC_RXDCTL_PTHRESH 8
#define IGC_RXDCTL_HTHRESH 8
#define IGC_RXDCTL_WTHRESH 4
/* Ena specific Rx Queue */
#define IGC_RXDCTL_QUEUE_ENABLE 0x02000000
/* Receive Software Flush */
#define IGC_RXDCTL_SWFLUSH 0x04000000
#define IGC_TXDCTL_PTHRESH_MASK GENMASK(4, 0)
#define IGC_TXDCTL_HTHRESH_MASK GENMASK(12, 8)
#define IGC_TXDCTL_WTHRESH_MASK GENMASK(20, 16)
#define IGC_TXDCTL_QUEUE_ENABLE_MASK GENMASK(25, 25)
#define IGC_TXDCTL_SWFLUSH_MASK GENMASK(26, 26)
#define IGC_TXDCTL_PRIORITY_MASK GENMASK(27, 27)
#define IGC_TXDCTL_PTHRESH(x) FIELD_PREP(IGC_TXDCTL_PTHRESH_MASK, (x))
#define IGC_TXDCTL_HTHRESH(x) FIELD_PREP(IGC_TXDCTL_HTHRESH_MASK, (x))
#define IGC_TXDCTL_WTHRESH(x) FIELD_PREP(IGC_TXDCTL_WTHRESH_MASK, (x))
/* Ena specific Tx Queue */
#define IGC_TXDCTL_QUEUE_ENABLE FIELD_PREP(IGC_TXDCTL_QUEUE_ENABLE_MASK, 1)
/* Transmit Software Flush */
#define IGC_TXDCTL_SWFLUSH FIELD_PREP(IGC_TXDCTL_SWFLUSH_MASK, 1)
#define IGC_TXDCTL_PRIORITY(x) FIELD_PREP(IGC_TXDCTL_PRIORITY_MASK, (x))
#define IGC_TXDCTL_PRIORITY_HIGH IGC_TXDCTL_PRIORITY(1)
#define IGC_RX_DMA_ATTR \
(DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_WEAK_ORDERING)

View File

@ -86,14 +86,6 @@ union igc_adv_rx_desc {
} wb; /* writeback */
};
/* Additional Transmit Descriptor Control definitions */
#define IGC_TXDCTL_QUEUE_ENABLE 0x02000000 /* Ena specific Tx Queue */
#define IGC_TXDCTL_SWFLUSH 0x04000000 /* Transmit Software Flush */
/* Additional Receive Descriptor Control definitions */
#define IGC_RXDCTL_QUEUE_ENABLE 0x02000000 /* Ena specific Rx Queue */
#define IGC_RXDCTL_SWFLUSH 0x04000000 /* Receive Software Flush */
/* SRRCTL bit definitions */
#define IGC_SRRCTL_BSIZEPKT_MASK GENMASK(6, 0)
#define IGC_SRRCTL_BSIZEPKT(x) FIELD_PREP(IGC_SRRCTL_BSIZEPKT_MASK, \

View File

@ -588,6 +588,7 @@
#define IGC_TXQCTL_QUEUE_MODE_LAUNCHT 0x00000001
#define IGC_TXQCTL_STRICT_CYCLE 0x00000002
#define IGC_TXQCTL_STRICT_END 0x00000004
#define IGC_TXQCTL_PREEMPTIBLE 0x00000008
#define IGC_TXQCTL_QAV_SEL_MASK 0x000000C0
#define IGC_TXQCTL_QAV_SEL_CBS0 0x00000080
#define IGC_TXQCTL_QAV_SEL_CBS1 0x000000C0

View File

@ -122,9 +122,11 @@ static const char igc_gstrings_test[][ETH_GSTRING_LEN] = {
#define IGC_STATS_LEN \
(IGC_GLOBAL_STATS_LEN + IGC_NETDEV_STATS_LEN + IGC_QUEUE_STATS_LEN)
#define IGC_PRIV_FLAGS_LEGACY_RX BIT(0)
#define IGC_PRIV_FLAGS_REVERSE_TSN_TXQ_PRIO BIT(1)
static const char igc_priv_flags_strings[][ETH_GSTRING_LEN] = {
#define IGC_PRIV_FLAGS_LEGACY_RX BIT(0)
"legacy-rx",
"reverse-tsn-txq-prio",
};
#define IGC_PRIV_FLAGS_STR_LEN ARRAY_SIZE(igc_priv_flags_strings)
@ -1600,6 +1602,9 @@ static u32 igc_ethtool_get_priv_flags(struct net_device *netdev)
if (adapter->flags & IGC_FLAG_RX_LEGACY)
priv_flags |= IGC_PRIV_FLAGS_LEGACY_RX;
if (adapter->flags & IGC_FLAG_TSN_REVERSE_TXQ_PRIO)
priv_flags |= IGC_PRIV_FLAGS_REVERSE_TSN_TXQ_PRIO;
return priv_flags;
}
@ -1608,10 +1613,13 @@ static int igc_ethtool_set_priv_flags(struct net_device *netdev, u32 priv_flags)
struct igc_adapter *adapter = netdev_priv(netdev);
unsigned int flags = adapter->flags;
flags &= ~IGC_FLAG_RX_LEGACY;
flags &= ~(IGC_FLAG_RX_LEGACY | IGC_FLAG_TSN_REVERSE_TXQ_PRIO);
if (priv_flags & IGC_PRIV_FLAGS_LEGACY_RX)
flags |= IGC_FLAG_RX_LEGACY;
if (priv_flags & IGC_PRIV_FLAGS_REVERSE_TSN_TXQ_PRIO)
flags |= IGC_FLAG_TSN_REVERSE_TXQ_PRIO;
if (flags != adapter->flags) {
adapter->flags = flags;

View File

@ -683,9 +683,9 @@ static void igc_configure_rx_ring(struct igc_adapter *adapter,
wr32(IGC_SRRCTL(reg_idx), srrctl);
rxdctl |= IGC_RX_PTHRESH;
rxdctl |= IGC_RX_HTHRESH << 8;
rxdctl |= IGC_RX_WTHRESH << 16;
rxdctl |= IGC_RXDCTL_PTHRESH;
rxdctl |= IGC_RXDCTL_HTHRESH << 8;
rxdctl |= IGC_RXDCTL_WTHRESH << 16;
/* initialize rx_buffer_info */
memset(ring->rx_buffer_info, 0,
@ -749,11 +749,9 @@ static void igc_configure_tx_ring(struct igc_adapter *adapter,
wr32(IGC_TDH(reg_idx), 0);
writel(0, ring->tail);
txdctl |= IGC_TX_PTHRESH;
txdctl |= IGC_TX_HTHRESH << 8;
txdctl |= IGC_TX_WTHRESH << 16;
txdctl |= IGC_TXDCTL_PTHRESH(8) | IGC_TXDCTL_HTHRESH(1) |
IGC_TXDCTL_WTHRESH(16) | IGC_TXDCTL_QUEUE_ENABLE;
txdctl |= IGC_TXDCTL_QUEUE_ENABLE;
wr32(IGC_TXDCTL(reg_idx), txdctl);
}
@ -1687,6 +1685,15 @@ static netdev_tx_t igc_xmit_frame_ring(struct sk_buff *skb,
first->tx_flags = tx_flags;
first->protocol = protocol;
/* For preemptible queue, manually pad the skb so that HW includes
* padding bytes in mCRC calculation
*/
if (tx_ring->preemptible && skb->len < ETH_ZLEN) {
if (skb_padto(skb, ETH_ZLEN))
goto out_drop;
skb_put(skb, ETH_ZLEN - skb->len);
}
tso = igc_tso(tx_ring, first, launch_time, first_flag, &hdr_len);
if (tso < 0)
goto out_drop;
@ -6423,6 +6430,7 @@ static int igc_qbv_clear_schedule(struct igc_adapter *adapter)
ring->start_time = 0;
ring->end_time = NSEC_PER_SEC;
ring->max_sdu = 0;
ring->preemptible = false;
}
spin_lock_irqsave(&adapter->qbv_tx_lock, flags);
@ -6488,9 +6496,12 @@ 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;
if (qopt->mqprio.preemptible_tcs &&
!(adapter->flags & IGC_FLAG_TSN_REVERSE_TXQ_PRIO)) {
NL_SET_ERR_MSG_MOD(qopt->extack,
"reverse-tsn-txq-prio private flag must be enabled before setting preemptible tc");
return -ENODEV;
}
igc_ptp_read(adapter, &now);
@ -6583,6 +6594,8 @@ static int igc_save_qbv_schedule(struct igc_adapter *adapter,
ring->max_sdu = 0;
}
igc_fpe_save_preempt_queue(adapter, &qopt->mqprio);
return 0;
}
@ -6702,7 +6715,8 @@ static int igc_tc_query_caps(struct igc_adapter *adapter,
case TC_SETUP_QDISC_TAPRIO: {
struct tc_taprio_caps *caps = base->caps;
caps->broken_mqprio = true;
if (!(adapter->flags & IGC_FLAG_TSN_REVERSE_TXQ_PRIO))
caps->broken_mqprio = true;
if (hw->mac.type == igc_i225) {
caps->supports_queue_max_sdu = true;
@ -6728,6 +6742,20 @@ static void igc_save_mqprio_params(struct igc_adapter *adapter, u8 num_tc,
adapter->queue_per_tc[i] = offset[i];
}
static bool
igc_tsn_is_tc_to_queue_priority_ordered(struct tc_mqprio_qopt_offload *mqprio)
{
int num_tc = mqprio->qopt.num_tc;
int i;
for (i = 1; i < num_tc; i++) {
if (mqprio->qopt.offset[i - 1] > mqprio->qopt.offset[i])
return false;
}
return true;
}
static int igc_tsn_enable_mqprio(struct igc_adapter *adapter,
struct tc_mqprio_qopt_offload *mqprio)
{
@ -6739,6 +6767,7 @@ static int igc_tsn_enable_mqprio(struct igc_adapter *adapter,
if (!mqprio->qopt.num_tc) {
adapter->strict_priority_enable = false;
igc_fpe_clear_preempt_queue(adapter);
netdev_reset_tc(adapter->netdev);
goto apply;
}
@ -6760,10 +6789,9 @@ static int igc_tsn_enable_mqprio(struct igc_adapter *adapter,
}
}
/* Preemption is not supported yet. */
if (mqprio->preemptible_tcs) {
if (!igc_tsn_is_tc_to_queue_priority_ordered(mqprio)) {
NL_SET_ERR_MSG_MOD(mqprio->extack,
"Preemption is not supported yet");
"tc to queue mapping must preserve increasing priority (higher tc -> higher queue)");
return -EOPNOTSUPP;
}
@ -6786,6 +6814,7 @@ static int igc_tsn_enable_mqprio(struct igc_adapter *adapter,
adapter->queue_per_tc[i] = i;
mqprio->qopt.hw = TC_MQPRIO_HW_OFFLOAD_TCS;
igc_fpe_save_preempt_queue(adapter, mqprio);
apply:
return igc_tsn_offload_apply(adapter);

View File

@ -13,6 +13,13 @@
#define TX_MAX_FRAG_SIZE (TX_MIN_FRAG_SIZE * \
(MAX_MULTPLIER_TX_MIN_FRAG + 1))
enum tx_queue {
TX_QUEUE_0 = 0,
TX_QUEUE_1,
TX_QUEUE_2,
TX_QUEUE_3,
};
DEFINE_STATIC_KEY_FALSE(igc_fpe_enabled);
static int igc_fpe_init_smd_frame(struct igc_ring *ring,
@ -109,6 +116,18 @@ static int igc_fpe_xmit_smd_frame(struct igc_adapter *adapter,
return err;
}
static void igc_fpe_configure_tx(struct ethtool_mmsv *mmsv, bool tx_enable)
{
struct igc_fpe_t *fpe = container_of(mmsv, struct igc_fpe_t, mmsv);
struct igc_adapter *adapter;
adapter = container_of(fpe, struct igc_adapter, fpe);
adapter->fpe.tx_enabled = tx_enable;
/* Update config since tx_enabled affects preemptible queue configuration */
igc_tsn_offload_apply(adapter);
}
static void igc_fpe_send_mpacket(struct ethtool_mmsv *mmsv,
enum ethtool_mpacket type)
{
@ -130,15 +149,59 @@ static void igc_fpe_send_mpacket(struct ethtool_mmsv *mmsv,
}
static const struct ethtool_mmsv_ops igc_mmsv_ops = {
.configure_tx = igc_fpe_configure_tx,
.send_mpacket = igc_fpe_send_mpacket,
};
void igc_fpe_init(struct igc_adapter *adapter)
{
adapter->fpe.tx_min_frag_size = TX_MIN_FRAG_SIZE;
adapter->fpe.tx_enabled = false;
ethtool_mmsv_init(&adapter->fpe.mmsv, adapter->netdev, &igc_mmsv_ops);
}
void igc_fpe_clear_preempt_queue(struct igc_adapter *adapter)
{
for (int i = 0; i < adapter->num_tx_queues; i++) {
struct igc_ring *tx_ring = adapter->tx_ring[i];
tx_ring->preemptible = false;
}
}
static u32 igc_fpe_map_preempt_tc_to_queue(const struct igc_adapter *adapter,
unsigned long preemptible_tcs)
{
struct net_device *dev = adapter->netdev;
u32 i, queue = 0;
for (i = 0; i < dev->num_tc; i++) {
u32 offset, count;
if (!(preemptible_tcs & BIT(i)))
continue;
offset = dev->tc_to_txq[i].offset;
count = dev->tc_to_txq[i].count;
queue |= GENMASK(offset + count - 1, offset);
}
return queue;
}
void igc_fpe_save_preempt_queue(struct igc_adapter *adapter,
const struct tc_mqprio_qopt_offload *mqprio)
{
u32 preemptible_queue = igc_fpe_map_preempt_tc_to_queue(adapter,
mqprio->preemptible_tcs);
for (int i = 0; i < adapter->num_tx_queues; i++) {
struct igc_ring *tx_ring = adapter->tx_ring[i];
tx_ring->preemptible = !!(preemptible_queue & BIT(i));
}
}
static bool is_any_launchtime(struct igc_adapter *adapter)
{
int i;
@ -238,7 +301,7 @@ bool igc_tsn_is_taprio_activated_by_user(struct igc_adapter *adapter)
adapter->taprio_offload_enable;
}
static void igc_tsn_tx_arb(struct igc_adapter *adapter, u16 *queue_per_tc)
static void igc_tsn_tx_arb(struct igc_adapter *adapter, bool reverse_prio)
{
struct igc_hw *hw = &adapter->hw;
u32 txarb;
@ -250,10 +313,17 @@ static void igc_tsn_tx_arb(struct igc_adapter *adapter, u16 *queue_per_tc)
IGC_TXARB_TXQ_PRIO_2_MASK |
IGC_TXARB_TXQ_PRIO_3_MASK);
txarb |= IGC_TXARB_TXQ_PRIO_0(queue_per_tc[3]);
txarb |= IGC_TXARB_TXQ_PRIO_1(queue_per_tc[2]);
txarb |= IGC_TXARB_TXQ_PRIO_2(queue_per_tc[1]);
txarb |= IGC_TXARB_TXQ_PRIO_3(queue_per_tc[0]);
if (reverse_prio) {
txarb |= IGC_TXARB_TXQ_PRIO_0(TX_QUEUE_3);
txarb |= IGC_TXARB_TXQ_PRIO_1(TX_QUEUE_2);
txarb |= IGC_TXARB_TXQ_PRIO_2(TX_QUEUE_1);
txarb |= IGC_TXARB_TXQ_PRIO_3(TX_QUEUE_0);
} else {
txarb |= IGC_TXARB_TXQ_PRIO_0(TX_QUEUE_0);
txarb |= IGC_TXARB_TXQ_PRIO_1(TX_QUEUE_1);
txarb |= IGC_TXARB_TXQ_PRIO_2(TX_QUEUE_2);
txarb |= IGC_TXARB_TXQ_PRIO_3(TX_QUEUE_3);
}
wr32(IGC_TXARB, txarb);
}
@ -286,7 +356,6 @@ static void igc_tsn_set_rxpbsize(struct igc_adapter *adapter,
*/
static int igc_tsn_disable_offload(struct igc_adapter *adapter)
{
u16 queue_per_tc[4] = { 3, 2, 1, 0 };
struct igc_hw *hw = &adapter->hw;
u32 tqavctrl;
int i;
@ -308,9 +377,16 @@ static int igc_tsn_disable_offload(struct igc_adapter *adapter)
wr32(IGC_TQAVCTRL, tqavctrl);
for (i = 0; i < adapter->num_tx_queues; i++) {
int reg_idx = adapter->tx_ring[i]->reg_idx;
u32 txdctl;
wr32(IGC_TXQCTL(i), 0);
wr32(IGC_STQT(i), 0);
wr32(IGC_ENDQT(i), NSEC_PER_SEC);
txdctl = rd32(IGC_TXDCTL(reg_idx));
txdctl &= ~IGC_TXDCTL_PRIORITY_HIGH;
wr32(IGC_TXDCTL(reg_idx), txdctl);
}
wr32(IGC_QBVCYCLET_S, 0);
@ -319,7 +395,7 @@ static int igc_tsn_disable_offload(struct igc_adapter *adapter)
/* Restore the default Tx arbitration: Priority 0 has the highest
* priority and is assigned to queue 0 and so on and so forth.
*/
igc_tsn_tx_arb(adapter, queue_per_tc);
igc_tsn_tx_arb(adapter, false);
adapter->flags &= ~IGC_FLAG_TSN_QBV_ENABLED;
@ -385,15 +461,13 @@ static int igc_tsn_enable_offload(struct igc_adapter *adapter)
if (igc_is_device_id_i226(hw))
igc_tsn_set_retx_qbvfullthreshold(adapter);
if (adapter->strict_priority_enable) {
/* Configure queue priorities according to the user provided
* mapping.
*/
igc_tsn_tx_arb(adapter, adapter->queue_per_tc);
}
if (adapter->strict_priority_enable ||
adapter->flags & IGC_FLAG_TSN_REVERSE_TXQ_PRIO)
igc_tsn_tx_arb(adapter, true);
for (i = 0; i < adapter->num_tx_queues; i++) {
struct igc_ring *ring = adapter->tx_ring[i];
u32 txdctl = rd32(IGC_TXDCTL(ring->reg_idx));
u32 txqctl = 0;
u16 cbs_value;
u32 tqavcc;
@ -427,6 +501,22 @@ static int igc_tsn_enable_offload(struct igc_adapter *adapter)
if (ring->launchtime_enable)
txqctl |= IGC_TXQCTL_QUEUE_MODE_LAUNCHT;
if (!adapter->fpe.tx_enabled) {
/* fpe inactive: clear both flags */
txqctl &= ~IGC_TXQCTL_PREEMPTIBLE;
txdctl &= ~IGC_TXDCTL_PRIORITY_HIGH;
} else if (ring->preemptible) {
/* fpe active + preemptible: enable preemptible queue + set low priority */
txqctl |= IGC_TXQCTL_PREEMPTIBLE;
txdctl &= ~IGC_TXDCTL_PRIORITY_HIGH;
} else {
/* fpe active + express: enable express queue + set high priority */
txqctl &= ~IGC_TXQCTL_PREEMPTIBLE;
txdctl |= IGC_TXDCTL_PRIORITY_HIGH;
}
wr32(IGC_TXDCTL(ring->reg_idx), txdctl);
/* Skip configuring CBS for Q2 and Q3 */
if (i > 1)
goto skip_cbs;

View File

@ -4,6 +4,8 @@
#ifndef _IGC_TSN_H_
#define _IGC_TSN_H_
#include <net/pkt_sched.h>
#define IGC_RX_MIN_FRAG_SIZE 60
#define SMD_FRAME_SIZE 60
@ -15,6 +17,9 @@ enum igc_txd_popts_type {
DECLARE_STATIC_KEY_FALSE(igc_fpe_enabled);
void igc_fpe_init(struct igc_adapter *adapter);
void igc_fpe_clear_preempt_queue(struct igc_adapter *adapter);
void igc_fpe_save_preempt_queue(struct igc_adapter *adapter,
const struct tc_mqprio_qopt_offload *mqprio);
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);