From nobody Thu Apr 9 23:24:19 2026 Received: from smtpout-03.galae.net (smtpout-03.galae.net [185.246.85.4]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5524339C653 for ; Thu, 5 Mar 2026 17:20:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.246.85.4 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772731226; cv=none; b=lrbyIYbsE/RoAC9XxRPZGLi7eWgcKY+gabo6K0DS+UIpUfywIwIBRnnBPBOoB9ZAldvO3W5nPxavyTu5gPExfXgtwylgJB1lwns+pWOOGqOXmCkdowM26h6y7RUByPsn4LT2h0BDONxWWfofyhJX4sLacWyVsbFMHyDl/VB+v74= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772731226; c=relaxed/simple; bh=8BSr8Z+djW2ArgZXzFGXq3rMIr8hH0DMZUCahE+APdI=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=gFbmZWVIstAqjm/xSX4V9KhggcttUqe3U46EKY5St1czDwFFu0u5GzSrm5lATIchgiV96FXyGjMXwLNhd3WM3CVAVuQL31x9n7/pKFGRy7OwYMb692HdIGskcC6pMtSjikcY6uRARWF9wCgTsCvgsWFownW9D2avq65FrZljHJs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com; spf=pass smtp.mailfrom=bootlin.com; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b=kslav3oo; arc=none smtp.client-ip=185.246.85.4 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=bootlin.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="kslav3oo" Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-03.galae.net (Postfix) with ESMTPS id 908264E42579; Thu, 5 Mar 2026 17:20:23 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id 66C5A5FF89; Thu, 5 Mar 2026 17:20:23 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 725EA1036990A; Thu, 5 Mar 2026 18:20:21 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1772731222; h=from:subject:date:message-id:to:cc:mime-version:content-type: content-transfer-encoding:in-reply-to:references; bh=riyRiC18Lx2YQbioxa9EAVOQZDST3MG0mMeUtporR+Y=; b=kslav3ooqxTLXMtXJO8JO97KDMPyw01QaFm/ad2z3YjnZ5FessVcjs0CUGHM5xMvu6xYPF H3FOJ6zDK/Gld+86ygeVeOFrkM0aeeiJ1h6oHLTaBoPwD4Xfa4dxorP5hkPkQVe9qrCNRY yLMXTohAoYIBFFunns6UEnYXjvF6Uqg2D+n9BBCj8cDgwtD2CgCOPOrV8awn4fDZ0lXzGY m3vejkV5y8inVwH+Q2L8bpwaNC59lNp+iuwWYznCYU1c5pjIheCyAqyUzP9ZM4aL3eQy4H 7qWpSte9ESLHVr3Pj1RQLQ2hutzIbLaSaUMqLkhFCWO29xYu3SeTNOz/Ir301Q== From: =?utf-8?q?Th=C3=A9o_Lebrun?= Date: Thu, 05 Mar 2026 18:20:14 +0100 Subject: [PATCH net-next 1/2] net: macb: implement ethtool_ops.get|set_channels() Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260305-macb-set-channels-v1-1-28e3a96a3dc3@bootlin.com> References: <20260305-macb-set-channels-v1-0-28e3a96a3dc3@bootlin.com> In-Reply-To: <20260305-macb-set-channels-v1-0-28e3a96a3dc3@bootlin.com> To: Nicolas Ferre , Claudiu Beznea , Andrew Lunn , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Vladimir Kondratiev , Gregory CLEMENT , =?utf-8?q?Beno=C3=AEt_Monin?= , Tawfik Bayouk , Thomas Petazzoni , Paolo Valerio , =?utf-8?q?Th=C3=A9o_Lebrun?= X-Mailer: b4 0.14.3 X-Last-TLS-Session-Version: TLSv1.3 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 <=3D bp->max_num_queues`. MACB/GEM does not know about rx/tx specific queues; it only has combined queues. Implement .set_channels() operation by wrapping ourselves in macb_close() and macb_open() calls if interface is running. This triggers reinit of buffers, which also includes the code that enables/disables only the queues below bp->num_queues, in macb_init_buffers(). If reopen fails, then we do one last attempt and reset num_queues to the previous value and try reopen. We still error out because the .set_channels() operation failed. .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. Nit: fix an alignment issue inside gem_ethtool_ops which does not deserve its own patch. Signed-off-by: Th=C3=A9o Lebrun --- drivers/net/ethernet/cadence/macb.h | 1 + drivers/net/ethernet/cadence/macb_main.c | 100 ++++++++++++++++++++++++++-= ---- 2 files changed, 85 insertions(+), 16 deletions(-) diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cad= ence/macb.h index 87414a2ddf6e..30fa65e2bdf2 100644 --- a/drivers/net/ethernet/cadence/macb.h +++ b/drivers/net/ethernet/cadence/macb.h @@ -1294,6 +1294,7 @@ struct macb { unsigned int tx_ring_size; =20 unsigned int num_queues; + unsigned int max_num_queues; struct macb_queue queues[MACB_MAX_QUEUES]; =20 spinlock_t lock; diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/etherne= t/cadence/macb_main.c index 17f0ad3d7a09..bac83a2b4c4d 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -466,9 +466,24 @@ static void macb_init_buffers(struct macb *bp) upper_32_bits(bp->queues[0].tx_ring_dma)); } =20 - for (q =3D 0, queue =3D 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 =3D 0, queue =3D 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)); + + /* Disable all interrupts */ + queue_writel(queue, IDR, -1); + queue_readl(queue, ISR); + if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE) + queue_writel(queue, ISR, -1); + } } } =20 @@ -3900,8 +3915,8 @@ static int gem_set_rxnfc(struct net_device *netdev, s= truct ethtool_rxnfc *cmd) =20 switch (cmd->cmd) { case ETHTOOL_SRXCLSRLINS: - if ((cmd->fs.location >=3D bp->max_tuples) - || (cmd->fs.ring_cookie >=3D bp->num_queues)) { + if (cmd->fs.location >=3D bp->max_tuples || + cmd->fs.ring_cookie >=3D bp->max_num_queues) { ret =3D -EINVAL; break; } @@ -3919,6 +3934,54 @@ static int gem_set_rxnfc(struct net_device *netdev, = struct ethtool_rxnfc *cmd) return ret; } =20 +static void macb_get_channels(struct net_device *netdev, + struct ethtool_channels *ch) +{ + struct macb *bp =3D netdev_priv(netdev); + + ch->max_combined =3D bp->max_num_queues; + ch->combined_count =3D bp->num_queues; +} + +static int macb_set_channels(struct net_device *netdev, + struct ethtool_channels *ch) +{ + struct macb *bp =3D netdev_priv(netdev); + unsigned int old_count =3D bp->num_queues; + unsigned int count =3D ch->combined_count; + bool running =3D netif_running(bp->dev); + int ret =3D 0; + + if (!(bp->caps & MACB_CAPS_QUEUE_DISABLE)) + return -EOPNOTSUPP; + + if (!count || ch->rx_count || ch->tx_count) + return -EINVAL; + + if (count > bp->max_num_queues) + return -EINVAL; + + if (count =3D=3D old_count) + return 0; + + if (running) + macb_close(bp->dev); + + bp->num_queues =3D count; + netif_set_real_num_queues(bp->dev, count, count); + + if (running) { + ret =3D macb_open(bp->dev); + if (ret) { + bp->num_queues =3D old_count; + netif_set_real_num_queues(bp->dev, old_count, old_count); + macb_open(bp->dev); + } + } + + return ret; +} + static const struct ethtool_ops macb_ethtool_ops =3D { .get_regs_len =3D macb_get_regs_len, .get_regs =3D macb_get_regs, @@ -3934,6 +3997,8 @@ static const struct ethtool_ops macb_ethtool_ops =3D { .set_link_ksettings =3D macb_set_link_ksettings, .get_ringparam =3D macb_get_ringparam, .set_ringparam =3D macb_set_ringparam, + .get_channels =3D macb_get_channels, + .set_channels =3D macb_set_channels, }; =20 static const struct ethtool_ops gem_ethtool_ops =3D { @@ -3954,10 +4019,12 @@ static const struct ethtool_ops gem_ethtool_ops =3D= { .set_link_ksettings =3D macb_set_link_ksettings, .get_ringparam =3D macb_get_ringparam, .set_ringparam =3D macb_set_ringparam, - .get_rxnfc =3D gem_get_rxnfc, - .set_rxnfc =3D gem_set_rxnfc, - .get_rx_ring_count =3D gem_get_rx_ring_count, - .nway_reset =3D phy_ethtool_nway_reset, + .get_rxnfc =3D gem_get_rxnfc, + .set_rxnfc =3D gem_set_rxnfc, + .get_rx_ring_count =3D gem_get_rx_ring_count, + .nway_reset =3D phy_ethtool_nway_reset, + .get_channels =3D macb_get_channels, + .set_channels =3D macb_set_channels, }; =20 static int macb_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) @@ -4098,9 +4165,9 @@ static int macb_taprio_setup_replace(struct net_devic= e *ndev, size_t i; int err; =20 - if (conf->num_entries > bp->num_queues) { + if (conf->num_entries > bp->max_num_queues) { netdev_err(ndev, "Too many TAPRIO entries: %zu > %d queues\n", - conf->num_entries, bp->num_queues); + conf->num_entries, bp->max_num_queues); return -EINVAL; } =20 @@ -4148,9 +4215,9 @@ static int macb_taprio_setup_replace(struct net_devic= e *ndev, =20 /* gate_mask must not select queues outside the valid queues */ queue_id =3D order_base_2(entry->gate_mask); - if (queue_id >=3D bp->num_queues) { + if (queue_id >=3D bp->max_num_queues) { netdev_err(ndev, "Entry %zu: gate_mask 0x%x exceeds queue range (max_qu= eues=3D%d)\n", - i, entry->gate_mask, bp->num_queues); + i, entry->gate_mask, bp->max_num_queues); err =3D -EINVAL; goto cleanup; } @@ -4210,7 +4277,7 @@ static int macb_taprio_setup_replace(struct net_devic= e *ndev, /* All validations passed - proceed with hardware configuration */ scoped_guard(spinlock_irqsave, &bp->lock) { /* Disable ENST queues if running before configuring */ - queue_mask =3D BIT_U32(bp->num_queues) - 1; + queue_mask =3D BIT_U32(bp->max_num_queues) - 1; gem_writel(bp, ENST_CONTROL, queue_mask << GEM_ENST_DISABLE_QUEUE_OFFSET); =20 @@ -4245,7 +4312,7 @@ static void macb_taprio_destroy(struct net_device *nd= ev) unsigned int q; =20 netdev_reset_tc(ndev); - queue_mask =3D BIT_U32(bp->num_queues) - 1; + queue_mask =3D BIT_U32(bp->max_num_queues) - 1; =20 scoped_guard(spinlock_irqsave, &bp->lock) { /* Single disable command for all queues */ @@ -4253,7 +4320,7 @@ static void macb_taprio_destroy(struct net_device *nd= ev) queue_mask << GEM_ENST_DISABLE_QUEUE_OFFSET); =20 /* Clear all queue ENST registers in batch */ - for (q =3D 0, queue =3D bp->queues; q < bp->num_queues; ++q, ++queue) { + for (q =3D 0, queue =3D 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); @@ -5512,6 +5579,7 @@ static int macb_probe(struct platform_device *pdev) bp->macb_reg_writel =3D hw_writel; } bp->num_queues =3D num_queues; + bp->max_num_queues =3D num_queues; bp->dma_burst_length =3D macb_config->dma_burst_length; bp->pclk =3D pclk; bp->hclk =3D hclk; --=20 2.53.0