From: "Théo Lebrun" <theo.lebrun@bootlin.com>
To: Conor Dooley <conor.dooley@microchip.com>,
Andrew Lunn <andrew+netdev@lunn.ch>,
"David S. Miller" <davem@davemloft.net>,
Eric Dumazet <edumazet@kernel.org>,
Jakub Kicinski <kuba@kernel.org>,
Paolo Abeni <pabeni@redhat.com>
Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
"Nicolai Buchwitz" <nb@tipi-net.de>,
"Paolo Valerio" <pvalerio@redhat.com>,
"Vladimir Kondratiev" <vladimir.kondratiev@mobileye.com>,
"Gregory CLEMENT" <gregory.clement@bootlin.com>,
"Benoît Monin" <benoit.monin@bootlin.com>,
"Tawfik Bayouk" <tawfik.bayouk@mobileye.com>,
"Thomas Petazzoni" <thomas.petazzoni@bootlin.com>,
"Théo Lebrun" <theo.lebrun@bootlin.com>
Subject: [PATCH net-next v5 2/3] net: macb: implement ethtool_ops.get|set_channels()
Date: Fri, 16 Oct 2026 18:58:16 +0200 [thread overview]
Message-ID: <20261016-macb-set-channels-v5-2-90ce0c0b8e75@bootlin.com> (raw)
In-Reply-To: <20261016-macb-set-channels-v5-0-90ce0c0b8e75@bootlin.com>
bp->num_queues is the total number of queues and is constant from probe.
Introduce bp->max_num_queues which takes the current role of
bp->num_queues and allow `0 < bp->num_queues <= bp->max_num_queues`.
MACB/GEM does not know about Rx/Tx specific queues; it only has
combined queues.
set_channels is reserved to devices with MACB_CAPS_QUEUE_DISABLE.
The tieoff workaround would not work as packets would still be routed
into queues with a tieoff descriptor.
Implement .set_channels() operation by refusing if netif_running(). We
need context-swapping support upstream for proper on-the-fly channel
count change.
Note we refuse the operation if any TAPRIO rules are in place on queues
that would get disabled. If we allow the operation to take place, then
a transmission window would be reserved but unusable.
Nit: fix an alignment issue inside gem_ethtool_ops which does not
deserve its own patch.
Signed-off-by: Théo Lebrun <theo.lebrun@bootlin.com>
---
drivers/net/ethernet/cadence/macb.h | 3 +
drivers/net/ethernet/cadence/macb_main.c | 109 ++++++++++++++++++++++++++-----
2 files changed, 95 insertions(+), 17 deletions(-)
diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h
index 82499ec6a9f0..51835de0f2a1 100644
--- a/drivers/net/ethernet/cadence/macb.h
+++ b/drivers/net/ethernet/cadence/macb.h
@@ -1314,6 +1314,9 @@ struct macb {
unsigned int tx_ring_size;
unsigned int num_queues;
+ unsigned int max_num_queues;
+ /* Queue mask gated by the current TAPRIO schedule (RTNL-protected). */
+ u32 taprio_gated_queues;
struct macb_queue queues[MACB_MAX_QUEUES];
spinlock_t lock;
diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c
index 625171fd3f9b..2d069454795b 100644
--- a/drivers/net/ethernet/cadence/macb_main.c
+++ b/drivers/net/ethernet/cadence/macb_main.c
@@ -473,9 +473,26 @@ static void macb_init_buffers(struct macb *bp)
upper_32_bits(bp->queues[0].tx_ring_dma));
}
- for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) {
- queue_writel(queue, RBQP, lower_32_bits(queue->rx_ring_dma));
- queue_writel(queue, TBQP, lower_32_bits(queue->tx_ring_dma));
+ for (q = 0, queue = bp->queues; q < bp->max_num_queues; ++q, ++queue) {
+ if (q < bp->num_queues) {
+ queue_writel(queue, RBQP,
+ lower_32_bits(queue->rx_ring_dma));
+ queue_writel(queue, TBQP,
+ lower_32_bits(queue->tx_ring_dma));
+ } else {
+ /*
+ * macb_set_channels(), which is the only way of writing
+ * to bp->num_queues, is only allowed if
+ * MACB_CAPS_QUEUE_DISABLE.
+ */
+ queue_writel(queue, RBQP, MACB_BIT(QUEUE_DISABLE));
+ queue_writel(queue, TBQP, MACB_BIT(QUEUE_DISABLE));
+
+ /* Disable all interrupts */
+ queue_writel(queue, IDR, -1);
+ queue_readl(queue, ISR);
+ macb_queue_isr_clear(bp, queue, -1);
+ }
}
}
@@ -2044,7 +2061,7 @@ static void macb_quiesce_start(struct macb *bp)
bp->irq_quiesced = true;
spin_unlock_irqrestore(&bp->lock, flags);
- for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue)
+ for (q = 0, queue = bp->queues; q < bp->max_num_queues; ++q, ++queue)
synchronize_irq(queue->irq);
cancel_work_sync(&bp->hresp_err_bh_work);
@@ -2209,6 +2226,7 @@ static irqreturn_t macb_interrupt(int irq, void *dev_id)
struct macb_queue *queue = dev_id;
struct macb *bp = queue->bp;
struct net_device *netdev = bp->netdev;
+ unsigned int q = queue - bp->queues;
bool hresp_err = false;
u32 status;
@@ -2220,16 +2238,18 @@ static irqreturn_t macb_interrupt(int irq, void *dev_id)
spin_lock(&bp->lock);
while (status) {
- /* self-disarm while the netdev is closed */
- if (unlikely(bp->irq_quiesced)) {
+ /* Self-disarm while the netdev is closed or
+ * the current queue is disabled.
+ */
+ if (unlikely(bp->irq_quiesced ||
+ q >= READ_ONCE(bp->num_queues))) {
queue_writel(queue, IDR, -1);
macb_queue_isr_clear(bp, queue, -1);
break;
}
netdev_vdbg(netdev, "queue = %u, isr = 0x%08lx\n",
- (unsigned int)(queue - bp->queues),
- (unsigned long)status);
+ q, (unsigned long)status);
if (status & bp->rx_intr_mask) {
/* There's no point taking any more interrupts
@@ -4247,6 +4267,51 @@ static int gem_set_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd)
return ret;
}
+static void macb_get_channels(struct net_device *netdev,
+ struct ethtool_channels *ch)
+{
+ struct macb *bp = netdev_priv(netdev);
+
+ ch->max_combined = bp->max_num_queues;
+ ch->combined_count = bp->num_queues;
+}
+
+static int macb_set_channels(struct net_device *netdev,
+ struct ethtool_channels *ch)
+{
+ struct macb *bp = netdev_priv(netdev);
+ unsigned int count = ch->combined_count;
+ int ret;
+
+ /* MACB_CAPS_QUEUE_DISABLE means that the field QUEUE_DISABLE/BIT0 in
+ * the per-queue RBQP/TBQP registers disable Tx/Rx queues. If we don't
+ * have that capability we can have multiple queues but we must always
+ * run with all enabled.
+ */
+ if (!(bp->caps & MACB_CAPS_QUEUE_DISABLE))
+ return -EOPNOTSUPP;
+
+ /* TODO: reuse context swapping once it is merged. */
+ if (netif_running(bp->netdev))
+ return -EBUSY;
+
+ /* If we allow disable a queue which has TAPRIO active, we'll keep a
+ * transmission window which will be unusable.
+ */
+ if (bp->taprio_gated_queues >> count) {
+ netdev_err(bp->netdev, "TAPRIO schedule gates queues >= %u; destroy the qdisc before reducing channels\n",
+ count);
+ return -EBUSY;
+ }
+
+ ret = netif_set_real_num_queues(bp->netdev, count, count);
+ if (ret)
+ return ret;
+
+ bp->num_queues = count;
+ return 0;
+}
+
static const struct ethtool_ops macb_ethtool_ops = {
.get_regs_len = macb_get_regs_len,
.get_regs = macb_get_regs,
@@ -4262,6 +4327,8 @@ static const struct ethtool_ops macb_ethtool_ops = {
.set_link_ksettings = macb_set_link_ksettings,
.get_ringparam = macb_get_ringparam,
.set_ringparam = macb_set_ringparam,
+ .get_channels = macb_get_channels,
+ .set_channels = macb_set_channels,
};
static int macb_get_eee(struct net_device *netdev, struct ethtool_keee *eee)
@@ -4296,12 +4363,14 @@ static const struct ethtool_ops gem_ethtool_ops = {
.set_link_ksettings = macb_set_link_ksettings,
.get_ringparam = macb_get_ringparam,
.set_ringparam = macb_set_ringparam,
- .get_rxnfc = gem_get_rxnfc,
- .set_rxnfc = gem_set_rxnfc,
- .get_rx_ring_count = gem_get_rx_ring_count,
- .nway_reset = phy_ethtool_nway_reset,
+ .get_rxnfc = gem_get_rxnfc,
+ .set_rxnfc = gem_set_rxnfc,
+ .get_rx_ring_count = gem_get_rx_ring_count,
+ .nway_reset = phy_ethtool_nway_reset,
.get_eee = macb_get_eee,
.set_eee = macb_set_eee,
+ .get_channels = macb_get_channels,
+ .set_channels = macb_set_channels,
};
static int macb_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd)
@@ -4443,7 +4512,7 @@ static int macb_taprio_setup_replace(struct net_device *netdev,
int err;
if (conf->num_entries > bp->num_queues) {
- netdev_err(netdev, "Too many TAPRIO entries: %zu > %d queues\n",
+ netdev_err(netdev, "Too many TAPRIO entries: %zu > %u active queues\n",
conf->num_entries, bp->num_queues);
return -EINVAL;
}
@@ -4493,7 +4562,7 @@ static int macb_taprio_setup_replace(struct net_device *netdev,
/* gate_mask must not select queues outside the valid queues */
queue_id = order_base_2(entry->gate_mask);
if (queue_id >= bp->num_queues) {
- netdev_err(netdev, "Entry %zu: gate_mask 0x%x exceeds queue range (max_queues=%d)\n",
+ netdev_err(netdev, "Entry %zu: gate_mask 0x%x exceeds active queue range (num_queues=%u)\n",
i, entry->gate_mask, bp->num_queues);
err = -EINVAL;
goto cleanup;
@@ -4553,7 +4622,7 @@ static int macb_taprio_setup_replace(struct net_device *netdev,
/* All validations passed - proceed with hardware configuration */
scoped_guard(spinlock_irqsave, &bp->lock) {
/* Disable ENST queues if running before configuring */
- queue_mask = BIT_U32(bp->num_queues) - 1;
+ queue_mask = BIT_U32(bp->max_num_queues) - 1;
gem_writel(bp, ENST_CONTROL,
queue_mask << GEM_ENST_DISABLE_QUEUE_OFFSET);
@@ -4571,6 +4640,9 @@ static int macb_taprio_setup_replace(struct net_device *netdev,
gem_writel(bp, ENST_CONTROL, configured_queues);
}
+ /* Remember which queues are gated, to validate .set_channels(). */
+ bp->taprio_gated_queues = configured_queues;
+
netdev_info(netdev, "TAPRIO configuration completed successfully: %zu entries, %d queues configured\n",
conf->num_entries, hweight32(configured_queues));
@@ -4587,7 +4659,7 @@ static void macb_taprio_destroy(struct net_device *netdev)
unsigned int q;
netdev_reset_tc(netdev);
- queue_mask = BIT_U32(bp->num_queues) - 1;
+ queue_mask = BIT_U32(bp->max_num_queues) - 1;
scoped_guard(spinlock_irqsave, &bp->lock) {
/* Single disable command for all queues */
@@ -4595,12 +4667,14 @@ static void macb_taprio_destroy(struct net_device *netdev)
queue_mask << GEM_ENST_DISABLE_QUEUE_OFFSET);
/* Clear all queue ENST registers in batch */
- for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) {
+ for (q = 0, queue = bp->queues; q < bp->max_num_queues;
+ ++q, ++queue) {
queue_writel(queue, ENST_START_TIME, 0);
queue_writel(queue, ENST_ON_TIME, 0);
queue_writel(queue, ENST_OFF_TIME, 0);
}
}
+ bp->taprio_gated_queues = 0;
netdev_info(netdev, "TAPRIO destroy: All gates disabled\n");
}
@@ -5972,6 +6046,7 @@ static int macb_probe(struct platform_device *pdev)
bp->macb_reg_writel = hw_writel;
}
bp->num_queues = num_queues;
+ bp->max_num_queues = num_queues;
bp->dma_burst_length = macb_config->dma_burst_length;
bp->pclk = pclk;
bp->hclk = hclk;
--
2.56.0
next prev parent reply other threads:[~2026-10-16 16:58 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-16 16:58 [PATCH net-next v5 0/3] net: macb: implement ethtool set channels count operation Théo Lebrun
2026-10-16 16:58 ` [PATCH net-next v5 1/3] net: macb: add QUEUE_DISABLE capability to 3 compatibles Théo Lebrun
2026-10-16 16:58 ` Théo Lebrun [this message]
2026-10-16 16:58 ` [PATCH net-next v5 3/3] net: macb: distribute evenly Tx SRAM segments Théo Lebrun
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261016-macb-set-channels-v5-2-90ce0c0b8e75@bootlin.com \
--to=theo.lebrun@bootlin.com \
--cc=andrew+netdev@lunn.ch \
--cc=benoit.monin@bootlin.com \
--cc=conor.dooley@microchip.com \
--cc=davem@davemloft.net \
--cc=edumazet@kernel.org \
--cc=gregory.clement@bootlin.com \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=nb@tipi-net.de \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=pvalerio@redhat.com \
--cc=tawfik.bayouk@mobileye.com \
--cc=thomas.petazzoni@bootlin.com \
--cc=vladimir.kondratiev@mobileye.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®