* [PATCH net-next v5 2/2] net: hsr: reject unresolved interlink ifindex
From: luka.gejak @ 2026-04-07 16:25 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni; +Cc: netdev, fmaurer, horms, luka.gejak
In-Reply-To: <20260407162502.19462-1-luka.gejak@linux.dev>
From: Luka Gejak <luka.gejak@linux.dev>
In hsr_newlink(), a provided but invalid IFLA_HSR_INTERLINK attribute
was silently ignored if __dev_get_by_index() returned NULL. This leads
to incorrect RedBox topology creation without notifying the user.
Fix this by returning -EINVAL and an extack message when the
interlink attribute is present but cannot be resolved.
Assisted-by: Gemini:Gemini-3.1-flash
Reviewed-by: Felix Maurer <fmaurer@redhat.com>
Signed-off-by: Luka Gejak <luka.gejak@linux.dev>
---
net/hsr/hsr_netlink.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/net/hsr/hsr_netlink.c b/net/hsr/hsr_netlink.c
index db0b0af7a692..f0ca23da3ab9 100644
--- a/net/hsr/hsr_netlink.c
+++ b/net/hsr/hsr_netlink.c
@@ -76,9 +76,14 @@ static int hsr_newlink(struct net_device *dev,
return -EINVAL;
}
- if (data[IFLA_HSR_INTERLINK])
+ if (data[IFLA_HSR_INTERLINK]) {
interlink = __dev_get_by_index(link_net,
nla_get_u32(data[IFLA_HSR_INTERLINK]));
+ if (!interlink) {
+ NL_SET_ERR_MSG_MOD(extack, "Interlink does not exist");
+ return -EINVAL;
+ }
+ }
if (interlink && interlink == link[0]) {
NL_SET_ERR_MSG_MOD(extack, "Interlink and Slave1 are the same");
--
2.53.0
^ permalink raw reply related
* [PATCH net-next v5 1/2] net: hsr: require valid EOT supervision TLV
From: luka.gejak @ 2026-04-07 16:25 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni; +Cc: netdev, fmaurer, horms, luka.gejak
In-Reply-To: <20260407162502.19462-1-luka.gejak@linux.dev>
From: Luka Gejak <luka.gejak@linux.dev>
Supervision frames are only valid if terminated with a zero-length EOT
TLV. The current check fails to reject non-EOT entries as the terminal
TLV, potentially allowing malformed supervision traffic.
Fix this by strictly requiring the terminal TLV to be HSR_TLV_EOT
with a length of zero, and properly linearizing the TLV header before
access.
Assisted-by: Gemini:Gemini-3.1-flash
Signed-off-by: Luka Gejak <luka.gejak@linux.dev>
---
net/hsr/hsr_forward.c | 20 +++++++++-----------
1 file changed, 9 insertions(+), 11 deletions(-)
diff --git a/net/hsr/hsr_forward.c b/net/hsr/hsr_forward.c
index 0aca859c88cb..eb89cc44eac0 100644
--- a/net/hsr/hsr_forward.c
+++ b/net/hsr/hsr_forward.c
@@ -82,35 +82,33 @@ static bool is_supervision_frame(struct hsr_priv *hsr, struct sk_buff *skb)
hsr_sup_tag->tlv.HSR_TLV_length != sizeof(struct hsr_sup_payload))
return false;
- /* Get next tlv */
+ /* Get next TLV */
total_length += hsr_sup_tag->tlv.HSR_TLV_length;
- if (!pskb_may_pull(skb, total_length))
+ if (!pskb_may_pull(skb, total_length + sizeof(struct hsr_sup_tlv)))
return false;
skb_pull(skb, total_length);
hsr_sup_tlv = (struct hsr_sup_tlv *)skb->data;
skb_push(skb, total_length);
- /* if this is a redbox supervision frame we need to verify
- * that more data is available
- */
+ /* If this is a RedBox supervision frame, verify additional data */
if (hsr_sup_tlv->HSR_TLV_type == PRP_TLV_REDBOX_MAC) {
- /* tlv length must be a length of a mac address */
+ /* TLV length must be the size of a MAC address */
if (hsr_sup_tlv->HSR_TLV_length != sizeof(struct hsr_sup_payload))
return false;
- /* make sure another tlv follows */
+ /* Make sure another TLV follows */
total_length += sizeof(struct hsr_sup_tlv) + hsr_sup_tlv->HSR_TLV_length;
- if (!pskb_may_pull(skb, total_length))
+ if (!pskb_may_pull(skb, total_length + sizeof(struct hsr_sup_tlv)))
return false;
- /* get next tlv */
+ /* Get next TLV */
skb_pull(skb, total_length);
hsr_sup_tlv = (struct hsr_sup_tlv *)skb->data;
skb_push(skb, total_length);
}
- /* end of tlvs must follow at the end */
- if (hsr_sup_tlv->HSR_TLV_type == HSR_TLV_EOT &&
+ /* Supervision frame must end with EOT TLV */
+ if (hsr_sup_tlv->HSR_TLV_type != HSR_TLV_EOT ||
hsr_sup_tlv->HSR_TLV_length != 0)
return false;
--
2.53.0
^ permalink raw reply related
* [PATCH net-next v5 0/2] net: hsr: strict supervision TLV validation
From: luka.gejak @ 2026-04-07 16:25 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni; +Cc: netdev, fmaurer, horms, luka.gejak
From: Luka Gejak <luka.gejak@linux.dev>
Changes in v5:
- Reverted TLV loop in Patch 1 to strict sequential parsing per IEC
62439-3.
- Retained pskb_may_pull() logic to ensure memory safety for TLV
headers.
- Dropped Reviewed-by from Patch 1 due to the logic evolving since
original review.
- Added Assisted-by tag for AI-aided translation and formatting to
both patches.
Changes in v4:
- Split from a 4-patch series into 'net' and 'net-next' as requested.
- Implemented a TLV walker in Patch 1 to correctly handle extension
TLVs and avoid regressions on paged frames/non-linearized skbs.
- Corrected pskb_may_pull() logic to include the TLV header size.
History of pre-separation series (v1-v3):
Changes in v3:
- addressed Felix review feedback in the VLAN add unwind fix
- removed the superfluous empty line
Changes in v2:
- picked up Reviewed-by tags on patches 1, 3 and 4
- changes in patch 2 per advice of Felix Maurer
Luka Gejak (2):
net: hsr: require valid EOT supervision TLV
net: hsr: reject unresolved interlink ifindex
net/hsr/hsr_forward.c | 20 +++++++++-----------
net/hsr/hsr_netlink.c | 7 ++++++-
2 files changed, 15 insertions(+), 12 deletions(-)
--
2.53.0
^ permalink raw reply
* Re: [PATCH net-next] vsock/virtio: remove unnecessary call to `virtio_transport_get_ops`
From: Luigi Leonardi @ 2026-04-07 16:24 UTC (permalink / raw)
To: Arseniy Krasnov
Cc: Michael S. Tsirkin, Jason Wang, Xuan Zhuo, Eugenio Pérez,
Stefan Hajnoczi, Stefano Garzarella, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman, kvm,
virtualization, netdev, linux-kernel
In-Reply-To: <a236c546-3a0f-4690-8ff7-ff8934db0539@salutedevices.com>
On Tue, Apr 07, 2026 at 07:16:24PM +0300, Arseniy Krasnov wrote:
>
>
>07.04.2026 17:31, Luigi Leonardi wrote:
>
>> `virtio_transport_send_pkt_info` gets all the transport information
>> from the parameter `t_ops`. There is no need to call
>> `virtio_transport_get_ops()`.
>
>Hm, one more suggestion, but not in this patch: may be we can also remove 'struct vsock_sock *vsk' argument
>from 'virtio_transport_send_pkt_info()', because it is also included in 'virtio_vsock_pkt_info *info' ?
>
Yep, good catch!
Thanks,
Luigi
^ permalink raw reply
* Re: [PATCH net-next] vsock/virtio: remove unnecessary call to `virtio_transport_get_ops`
From: Luigi Leonardi @ 2026-04-07 16:22 UTC (permalink / raw)
To: Michael S. Tsirkin
Cc: Jason Wang, Xuan Zhuo, Eugenio Pérez, Stefan Hajnoczi,
Stefano Garzarella, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Arseniy Krasnov, kvm, virtualization,
netdev, linux-kernel
In-Reply-To: <20260407121515-mutt-send-email-mst@kernel.org>
On Tue, Apr 07, 2026 at 12:16:27PM -0400, Michael S. Tsirkin wrote:
>On Tue, Apr 07, 2026 at 04:31:56PM +0200, Luigi Leonardi wrote:
>> `virtio_transport_send_pkt_info` gets all the transport information
>> from the parameter `t_ops`. There is no need to call
>> `virtio_transport_get_ops()`.
>>
>> Remove it.
>>
>> Fixes: 581512a6dc93 ("vsock/virtio: MSG_ZEROCOPY flag support")
>> Signed-off-by: Luigi Leonardi <leonardi@redhat.com>
>
>Acked-by: Michael S. Tsirkin <mst@redhat.com>
>
>but I think we should drop the Fixes tag.
ok, should I send a v2 without the tag?
Thanks,
Luigi
^ permalink raw reply
* Re: [PATCH net-next v4 3/3] net: phy: add a PHY write barrier when disabling interrupts
From: Charles Perry @ 2026-04-07 16:19 UTC (permalink / raw)
To: Andrew Lunn
Cc: Russell King (Oracle), Charles Perry, netdev, Heiner Kallweit,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
linux-kernel
In-Reply-To: <b40dfafe-7e8f-4686-a743-31b097c84954@lunn.ch>
On Thu, Apr 02, 2026 at 08:09:06PM +0200, Andrew Lunn wrote:
> On Thu, Apr 02, 2026 at 07:03:42PM +0100, Russell King (Oracle) wrote:
> > On Thu, Apr 02, 2026 at 03:31:52PM +0200, Andrew Lunn wrote:
> > > > +static int phy_write_barrier(struct phy_device *phydev)
> > > > +{
> > > > + int err;
> > > > +
> > > > + err = phy_read(phydev, MII_PHYSID1);
> > > > + if (err < 0)
> > > > + return err;
> > >
> > > There are a small number of MDIO busses which don't implement C22,
> > > only C45. You are likely to get -EIO or maybe ENODEV, -EOPNOTSUPP,
> > > -EINVAL for such a read. Returning the error than makes
> > > phy_disable_interrupts() fail, etc.
> >
> > If it returns -EOPNOTSUPP (meaning, at least, that bus->read is not
> > implemented), do we want to issue a C45 read instead?
>
> Maybe.
>
> get_phy_c45_ids() first reads MDIO_MMD_PMAPMD : MII_PHYSID1. That
> seems like a safe option.
Here's where I'm at with this:
static void phy_write_barrier(struct phy_device *phydev)
{
int err;
err = mdiobus_read(phydev->mdio.bus, phydev->mdio.addr, MII_PHYSID1);
if (err == -EOPNOTSUPP)
mdiobus_c45_read(phydev->mdio.bus, phydev->mdio.addr,
__ffs(phydev->c45_ids.mmds_present),
MII_PHYSID1);
}
In part inspired by phylink_phy_read() in phylink.c
Do you think there's any way I can test this on my VSC8574 or VSC8541? It
supports some C45 registers for EEE but not the device discovery part.
Thanks,
Charles
^ permalink raw reply
* Re: [PATCH net-next] vsock/virtio: remove unnecessary call to `virtio_transport_get_ops`
From: Arseniy Krasnov @ 2026-04-07 16:16 UTC (permalink / raw)
To: Luigi Leonardi, Michael S. Tsirkin, Jason Wang, Xuan Zhuo,
Eugenio Pérez, Stefan Hajnoczi, Stefano Garzarella,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman
Cc: kvm, virtualization, netdev, linux-kernel
In-Reply-To: <20260407-remove_parameter-v1-1-e9729360a2be@redhat.com>
07.04.2026 17:31, Luigi Leonardi wrote:
> `virtio_transport_send_pkt_info` gets all the transport information
> from the parameter `t_ops`. There is no need to call
> `virtio_transport_get_ops()`.
Hm, one more suggestion, but not in this patch: may be we can also remove 'struct vsock_sock *vsk' argument
from 'virtio_transport_send_pkt_info()', because it is also included in 'virtio_vsock_pkt_info *info' ?
Thanks
>
> Remove it.
>
> Fixes: 581512a6dc93 ("vsock/virtio: MSG_ZEROCOPY flag support")
> Signed-off-by: Luigi Leonardi <leonardi@redhat.com>
> ---
> I marked this as net-next material, but honsetly I'm not sure if I
> should have targeted net. It's not a bug after all, it's just a cleanup.
> ---
> net/vmw_vsock/virtio_transport_common.c | 2 --
> 1 file changed, 2 deletions(-)
>
> diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c
> index 8a9fb23c6e85..a152a9e208d0 100644
> --- a/net/vmw_vsock/virtio_transport_common.c
> +++ b/net/vmw_vsock/virtio_transport_common.c
> @@ -60,8 +60,6 @@ static bool virtio_transport_can_zcopy(const struct virtio_transport *t_ops,
> return false;
>
> /* Check that transport can send data in zerocopy mode. */
> - t_ops = virtio_transport_get_ops(info->vsk);
> -
> if (t_ops->can_msgzerocopy) {
> int pages_to_send = iov_iter_npages(iov_iter, MAX_SKB_FRAGS);
>
>
> ---
> base-commit: bfe62a454542cfad3379f6ef5680b125f41e20f4
> change-id: 20260407-remove_parameter-f61a3e40cf90
>
> Best regards,
Acked-by: Arseniy Krasnov <avkrasnov@salutedevices.com>
^ permalink raw reply
* Re: [PATCH net-next] vsock/virtio: remove unnecessary call to `virtio_transport_get_ops`
From: Michael S. Tsirkin @ 2026-04-07 16:16 UTC (permalink / raw)
To: Luigi Leonardi
Cc: Jason Wang, Xuan Zhuo, Eugenio Pérez, Stefan Hajnoczi,
Stefano Garzarella, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Arseniy Krasnov, kvm, virtualization,
netdev, linux-kernel
In-Reply-To: <20260407-remove_parameter-v1-1-e9729360a2be@redhat.com>
On Tue, Apr 07, 2026 at 04:31:56PM +0200, Luigi Leonardi wrote:
> `virtio_transport_send_pkt_info` gets all the transport information
> from the parameter `t_ops`. There is no need to call
> `virtio_transport_get_ops()`.
>
> Remove it.
>
> Fixes: 581512a6dc93 ("vsock/virtio: MSG_ZEROCOPY flag support")
> Signed-off-by: Luigi Leonardi <leonardi@redhat.com>
Acked-by: Michael S. Tsirkin <mst@redhat.com>
but I think we should drop the Fixes tag.
> ---
> I marked this as net-next material, but honsetly I'm not sure if I
> should have targeted net. It's not a bug after all, it's just a cleanup.
> ---
> net/vmw_vsock/virtio_transport_common.c | 2 --
> 1 file changed, 2 deletions(-)
>
> diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c
> index 8a9fb23c6e85..a152a9e208d0 100644
> --- a/net/vmw_vsock/virtio_transport_common.c
> +++ b/net/vmw_vsock/virtio_transport_common.c
> @@ -60,8 +60,6 @@ static bool virtio_transport_can_zcopy(const struct virtio_transport *t_ops,
> return false;
>
> /* Check that transport can send data in zerocopy mode. */
> - t_ops = virtio_transport_get_ops(info->vsk);
> -
> if (t_ops->can_msgzerocopy) {
> int pages_to_send = iov_iter_npages(iov_iter, MAX_SKB_FRAGS);
>
>
> ---
> base-commit: bfe62a454542cfad3379f6ef5680b125f41e20f4
> change-id: 20260407-remove_parameter-f61a3e40cf90
>
> Best regards,
> --
> Luigi Leonardi <leonardi@redhat.com>
^ permalink raw reply
* [PATCH net-next v3 4/4] net: dsa: yt921x: Add port qdisc tbf support
From: David Yang @ 2026-04-07 16:05 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, linux-kernel
In-Reply-To: <20260407160559.1747616-1-mmyangfl@gmail.com>
Enable port shaping and support limiting the rate of outgoing traffic.
Signed-off-by: David Yang <mmyangfl@gmail.com>
---
drivers/net/dsa/yt921x.c | 123 +++++++++++++++++++++++++++++++++++++++
drivers/net/dsa/yt921x.h | 65 ++++++++++++++++++++-
2 files changed, 187 insertions(+), 1 deletion(-)
diff --git a/drivers/net/dsa/yt921x.c b/drivers/net/dsa/yt921x.c
index e77bd4a34e10..0c87a0e02148 100644
--- a/drivers/net/dsa/yt921x.c
+++ b/drivers/net/dsa/yt921x.c
@@ -24,6 +24,7 @@
#include <net/dsa.h>
#include <net/dscp.h>
#include <net/ieee8021q.h>
+#include <net/pkt_cls.h>
#include "yt921x.h"
@@ -1330,6 +1331,113 @@ yt921x_dsa_port_policer_add(struct dsa_switch *ds, int port,
return res;
}
+static int
+yt921x_tbf_validate(struct yt921x_priv *priv,
+ const struct tc_tbf_qopt_offload *qopt, int *queuep)
+{
+ struct device *dev = to_device(priv);
+ int queue = -1;
+
+ /* TODO: queue support */
+ if (qopt->parent != TC_H_ROOT) {
+ dev_err(dev, "Parent should be \"root\"\n");
+ return -EOPNOTSUPP;
+ }
+
+ switch (qopt->command) {
+ case TC_TBF_REPLACE: {
+ const struct tc_tbf_qopt_offload_replace_params *p;
+
+ p = &qopt->replace_params;
+
+ if (!p->rate.mpu) {
+ dev_info(dev, "Assuming you want mpu = 64\n");
+ } else if (p->rate.mpu != 64) {
+ dev_err(dev, "mpu other than 64 not supported\n");
+ return -EINVAL;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ *queuep = queue;
+ return 0;
+}
+
+static int
+yt921x_dsa_port_setup_tc_tbf_port(struct dsa_switch *ds, int port,
+ const struct tc_tbf_qopt_offload *qopt)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ u32 ctrls[2];
+ int res;
+
+ switch (qopt->command) {
+ case TC_TBF_DESTROY:
+ ctrls[0] = 0;
+ ctrls[1] = 0;
+ break;
+ case TC_TBF_REPLACE: {
+ const struct tc_tbf_qopt_offload_replace_params *p;
+ struct yt921x_meter meter;
+ u64 burst;
+
+ p = &qopt->replace_params;
+
+ /* where is burst??? */
+ burst = div_u64(priv->port_shape_slot_ns * p->rate.rate_bytes_ps,
+ 1000000000) + 10000;
+ res = yt921x_meter_tfm(priv, port, priv->port_shape_slot_ns,
+ p->rate.rate_bytes_ps, burst,
+ YT921X_METER_SINGLE_BUCKET,
+ YT921X_SHAPE_CIR_MAX,
+ YT921X_SHAPE_CBS_MAX,
+ YT921X_SHAPE_UNIT_MAX, &meter);
+ if (res)
+ return res;
+
+ ctrls[0] = YT921X_PORT_SHAPE_CTRLa_CIR(meter.cir) |
+ YT921X_PORT_SHAPE_CTRLa_CBS(meter.cbs);
+ ctrls[1] = YT921X_PORT_SHAPE_CTRLb_UNIT(meter.unit) |
+ YT921X_PORT_SHAPE_CTRLb_EN;
+ break;
+ }
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_reg64_write(priv, YT921X_PORTn_SHAPE_CTRL(port), ctrls);
+ mutex_unlock(&priv->reg_lock);
+
+ return res;
+}
+
+static int
+yt921x_dsa_port_setup_tc(struct dsa_switch *ds, int port,
+ enum tc_setup_type type, void *type_data)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ int res;
+
+ switch (type) {
+ case TC_SETUP_QDISC_TBF: {
+ const struct tc_tbf_qopt_offload *qopt = type_data;
+ int queue;
+
+ res = yt921x_tbf_validate(priv, qopt, &queue);
+ if (res)
+ return res;
+
+ return yt921x_dsa_port_setup_tc_tbf_port(ds, port, qopt);
+ }
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
static int
yt921x_mirror_del(struct yt921x_priv *priv, int port, bool ingress)
{
@@ -3476,6 +3584,20 @@ static int yt921x_chip_setup_tc(struct yt921x_priv *priv)
return res;
priv->meter_slot_ns = ctrl * op_ns;
+ ctrl = max(priv->port_shape_slot_ns / op_ns,
+ YT921X_PORT_SHAPE_SLOT_MIN);
+ res = yt921x_reg_write(priv, YT921X_PORT_SHAPE_SLOT, ctrl);
+ if (res)
+ return res;
+ priv->port_shape_slot_ns = ctrl * op_ns;
+
+ ctrl = max(priv->queue_shape_slot_ns / op_ns,
+ YT921X_QUEUE_SHAPE_SLOT_MIN);
+ res = yt921x_reg_write(priv, YT921X_QUEUE_SHAPE_SLOT, ctrl);
+ if (res)
+ return res;
+ priv->queue_shape_slot_ns = ctrl * op_ns;
+
return 0;
}
@@ -3632,6 +3754,7 @@ static const struct dsa_switch_ops yt921x_dsa_switch_ops = {
/* rate */
.port_policer_del = yt921x_dsa_port_policer_del,
.port_policer_add = yt921x_dsa_port_policer_add,
+ .port_setup_tc = yt921x_dsa_port_setup_tc,
/* hsr */
.port_hsr_leave = dsa_port_simple_hsr_leave,
.port_hsr_join = dsa_port_simple_hsr_join,
diff --git a/drivers/net/dsa/yt921x.h b/drivers/net/dsa/yt921x.h
index a640672d1f1e..991989a31727 100644
--- a/drivers/net/dsa/yt921x.h
+++ b/drivers/net/dsa/yt921x.h
@@ -524,6 +524,12 @@ enum yt921x_app_selector {
#define YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_TAGGED BIT(1)
#define YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_UNTAGGED BIT(0)
+#define YT921X_PORTn_PRIO_UCAST_QUEUE(port) (0x300200 + 4 * (port))
+#define YT921X_PORT_PRIOm_UCAST_QUEUE_M(m) (7 << (3 * (m)))
+#define YT921X_PORT_PRIOm_UCAST_QUEUE(m, x) ((x) << (3 * (m)))
+#define YT921X_PORTn_PRIO_MCAST_QUEUE(port) (0x300280 + 4 * (port))
+#define YT921X_PORT_PRIOm_MCAST_QUEUE_M(m) (3 << (2 * (m)))
+#define YT921X_PORT_PRIOm_MCAST_QUEUE(m, x) ((x) << (2 * (m)))
#define YT921X_MIRROR 0x300300
#define YT921X_MIRROR_IGR_PORTS_M GENMASK(26, 16)
#define YT921X_MIRROR_IGR_PORTS(x) FIELD_PREP(YT921X_MIRROR_IGR_PORTS_M, (x))
@@ -534,6 +540,47 @@ enum yt921x_app_selector {
#define YT921X_MIRROR_PORT_M GENMASK(3, 0)
#define YT921X_MIRROR_PORT(x) FIELD_PREP(YT921X_MIRROR_PORT_M, (x))
+#define YT921X_QUEUE_SHAPE_SLOT 0x340008
+#define YT921X_QUEUE_SHAPE_SLOT_SLOT_M GENMASK(11, 0)
+#define YT921X_PORT_SHAPE_SLOT 0x34000c
+#define YT921X_PORT_SHAPE_SLOT_SLOT_M GENMASK(11, 0)
+#define YT921X_QUEUEn_SCH(x) (0x341000 + 4 * (x))
+#define YT921X_QUEUE_SCH_E_DWRR_M GENMASK(27, 18)
+#define YT921X_QUEUE_SCH_E_DWRR(x) FIELD_PREP(YT921X_QUEUE_SCH_E_DWRR_M, (x))
+#define YT921X_QUEUE_SCH_C_DWRR_M GENMASK(17, 8)
+#define YT921X_QUEUE_SCH_C_DWRR(x) FIELD_PREP(YT921X_QUEUE_SCH_C_DWRR_M, (x))
+#define YT921X_QUEUE_SCH_E_PRIO_M GENMASK(7, 4)
+#define YT921X_QUEUE_SCH_E_PRIO(x) FIELD_PREP(YT921X_QUEUE_SCH_E_PRIO_M, (x))
+#define YT921X_QUEUE_SCH_C_PRIO_M GENMASK(3, 0)
+#define YT921X_QUEUE_SCH_C_PRIO(x) FIELD_PREP(YT921X_QUEUE_SCH_C_PRIO_M, (x))
+#define YT921X_C_DWRRn(x) (0x342000 + 4 * (x))
+#define YT921X_E_DWRRn(x) (0x343000 + 4 * (x))
+#define YT921X_DWRR_PKT_MODE BIT(0) /* 0: byte rate mode */
+#define YT921X_QUEUEn_SHAPE_CTRL(x) (0x34c000 + 0x10 * (x))
+#define YT921X_QUEUE_SHAPE_CTRLc_TOKEN_OVERFLOW_EN BIT(6)
+#define YT921X_QUEUE_SHAPE_CTRLc_E_EN BIT(5)
+#define YT921X_QUEUE_SHAPE_CTRLc_C_EN BIT(4)
+#define YT921X_QUEUE_SHAPE_CTRLc_PKT_MODE BIT(3) /* 0: byte rate mode */
+#define YT921X_QUEUE_SHAPE_CTRLc_UNIT_M GENMASK(2, 0)
+#define YT921X_QUEUE_SHAPE_CTRLc_UNIT(x) FIELD_PREP(YT921X_QUEUE_SHAPE_CTRLc_UNIT_M, (x))
+#define YT921X_QUEUE_SHAPE_CTRLb_EBS_M GENMASK(31, 18)
+#define YT921X_QUEUE_SHAPE_CTRLb_EBS(x) FIELD_PREP(YT921X_QUEUE_SHAPE_CTRLb_EBS_M, (x))
+#define YT921X_QUEUE_SHAPE_CTRLb_EIR_M GENMASK(17, 0)
+#define YT921X_QUEUE_SHAPE_CTRLb_EIR(x) FIELD_PREP(YT921X_QUEUE_SHAPE_CTRLb_EIR_M, (x))
+#define YT921X_QUEUE_SHAPE_CTRLa_CBS_M GENMASK(31, 18)
+#define YT921X_QUEUE_SHAPE_CTRLa_CBS(x) FIELD_PREP(YT921X_QUEUE_SHAPE_CTRLa_CBS_M, (x))
+#define YT921X_QUEUE_SHAPE_CTRLa_CIR_M GENMASK(17, 0)
+#define YT921X_QUEUE_SHAPE_CTRLa_CIR(x) FIELD_PREP(YT921X_QUEUE_SHAPE_CTRLa_CIR_M, (x))
+#define YT921X_PORTn_SHAPE_CTRL(port) (0x354000 + 8 * (port))
+#define YT921X_PORT_SHAPE_CTRLb_EN BIT(4)
+#define YT921X_PORT_SHAPE_CTRLb_PKT_MODE BIT(3) /* 0: byte rate mode */
+#define YT921X_PORT_SHAPE_CTRLb_UNIT_M GENMASK(2, 0)
+#define YT921X_PORT_SHAPE_CTRLb_UNIT(x) FIELD_PREP(YT921X_PORT_SHAPE_CTRLb_UNIT_M, (x))
+#define YT921X_PORT_SHAPE_CTRLa_CBS_M GENMASK(31, 18)
+#define YT921X_PORT_SHAPE_CTRLa_CBS(x) FIELD_PREP(YT921X_PORT_SHAPE_CTRLa_CBS_M, (x))
+#define YT921X_PORT_SHAPE_CTRLa_CIR_M GENMASK(17, 0)
+#define YT921X_PORT_SHAPE_CTRLa_CIR(x) FIELD_PREP(YT921X_PORT_SHAPE_CTRLa_CIR_M, (x))
+
#define YT921X_EDATA_EXTMODE 0xfb
#define YT921X_EDATA_LEN 0x100
@@ -559,6 +606,11 @@ enum yt921x_fdb_entry_status {
#define YT921X_METER_UNIT_MAX ((1 << 3) - 1)
#define YT921X_METER_CIR_MAX ((1 << 18) - 1)
#define YT921X_METER_CBS_MAX ((1 << 16) - 1)
+#define YT921X_PORT_SHAPE_SLOT_MIN 80
+#define YT921X_QUEUE_SHAPE_SLOT_MIN 132
+#define YT921X_SHAPE_UNIT_MAX ((1 << 3) - 1)
+#define YT921X_SHAPE_CIR_MAX ((1 << 18) - 1)
+#define YT921X_SHAPE_CBS_MAX ((1 << 14) - 1)
#define YT921X_LAG_NUM 2
#define YT921X_LAG_PORT_NUM 4
@@ -576,7 +628,16 @@ enum yt921x_fdb_entry_status {
#define YT921X_TAG_LEN 8
/* 8 internal + 2 external + 1 mcu */
-#define YT921X_PORT_NUM 11
+#define YT921X_PORT_NUM 11
+#define YT921X_UCAST_QUEUE_NUM 8
+#define YT921X_MCAST_QUEUE_NUM 4
+#define YT921X_PORT_QUEUE_NUM \
+ (YT921X_UCAST_QUEUE_NUM + YT921X_MCAST_QUEUE_NUM)
+#define YT921X_UCAST_QUEUE_ID(port, queue) \
+ (YT921X_UCAST_QUEUE_NUM * (port) + (queue))
+#define YT921X_MCAST_QUEUE_ID(port, queue) \
+ (YT921X_UCAST_QUEUE_NUM * YT921X_PORT_NUM + \
+ YT921X_MCAST_QUEUE_NUM * (port) + (queue))
#define yt921x_port_is_internal(port) ((port) < 8)
#define yt921x_port_is_external(port) (8 <= (port) && (port) < 9)
@@ -655,6 +716,8 @@ struct yt921x_priv {
const struct yt921x_info *info;
unsigned int meter_slot_ns;
+ unsigned int port_shape_slot_ns;
+ unsigned int queue_shape_slot_ns;
/* cache of dsa_cpu_ports(ds) */
u16 cpu_ports_mask;
u8 cycle_ns;
--
2.53.0
^ permalink raw reply related
* [PATCH net-next v3 3/4] net: dsa: yt921x: Add port police support
From: David Yang @ 2026-04-07 16:05 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, linux-kernel
In-Reply-To: <20260407160559.1747616-1-mmyangfl@gmail.com>
Enable rate meter ability and support limiting the rate of incoming
traffic.
Signed-off-by: David Yang <mmyangfl@gmail.com>
---
drivers/net/dsa/yt921x.c | 276 ++++++++++++++++++++++++++++++++++++++-
drivers/net/dsa/yt921x.h | 54 ++++++++
2 files changed, 329 insertions(+), 1 deletion(-)
diff --git a/drivers/net/dsa/yt921x.c b/drivers/net/dsa/yt921x.c
index de6aa5835616..e77bd4a34e10 100644
--- a/drivers/net/dsa/yt921x.c
+++ b/drivers/net/dsa/yt921x.c
@@ -262,6 +262,14 @@ yt921x_reg_toggle_bits(struct yt921x_priv *priv, u32 reg, u32 mask, bool set)
* wrapper so that we can handle them consistently.
*/
+static void update_ctrls_unaligned(u32 *lo, u32 *hi, u64 mask, u64 val)
+{
+ *lo &= ~lower_32_bits(mask);
+ *hi &= ~upper_32_bits(mask);
+ *lo |= lower_32_bits(val);
+ *hi |= upper_32_bits(val);
+}
+
static int
yt921x_regs_read(struct yt921x_priv *priv, u32 reg, u32 *vals,
unsigned int num_regs)
@@ -372,6 +380,12 @@ yt921x_reg64_clear_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks)
return yt921x_regs_clear_bits(priv, reg, masks, 2);
}
+static int
+yt921x_reg96_write(struct yt921x_priv *priv, u32 reg, const u32 *vals)
+{
+ return yt921x_regs_write(priv, reg, vals, 3);
+}
+
static int yt921x_reg_mdio_read(void *context, u32 reg, u32 *valp)
{
struct yt921x_reg_mdio *mdio = context;
@@ -1065,6 +1079,13 @@ yt921x_dsa_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_keee *e)
return res;
}
+static int yt921x_mtu_fetch(struct yt921x_priv *priv, int port)
+{
+ struct dsa_port *dp = dsa_to_port(&priv->ds, port);
+
+ return dp->user ? READ_ONCE(dp->user->mtu) : ETH_DATA_LEN;
+}
+
static int
yt921x_dsa_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu)
{
@@ -1096,6 +1117,219 @@ static int yt921x_dsa_port_max_mtu(struct dsa_switch *ds, int port)
return YT921X_FRAME_SIZE_MAX - ETH_HLEN - ETH_FCS_LEN - YT921X_TAG_LEN;
}
+/* v * 2^e */
+static u64 ldexpu64(u64 v, int e)
+{
+ return e >= 0 ? v << e : v >> -e;
+}
+
+/* slot (ns) * rate (/s) / 10^9 (ns/s) = 2^C * token * 4^unit */
+static u32 rate2token(u64 rate, unsigned int slot_ns, int unit, int C)
+{
+ int e = 2 * unit + C + YT921X_TOKEN_RATE_C;
+
+ return div_u64(ldexpu64(slot_ns * rate, -e), 1000000000);
+}
+
+static u64 token2rate(u32 token, unsigned int slot_ns, int unit, int C)
+{
+ int e = 2 * unit + C + YT921X_TOKEN_RATE_C;
+
+ return div_u64(ldexpu64(mul_u32_u32(1000000000, token), e), slot_ns);
+}
+
+/* burst = 2^C * token * 4^unit */
+static u32 burst2token(u64 burst, int unit, int C)
+{
+ return ldexpu64(burst, -(2 * unit + C));
+}
+
+static u64 token2burst(u32 token, int unit, int C)
+{
+ return ldexpu64(token, 2 * unit + C);
+}
+
+struct yt921x_meter {
+ u32 cir;
+ u32 cbs;
+ u32 ebs;
+ int unit;
+};
+
+#define YT921X_METER_PKT_MODE BIT(0)
+#define YT921X_METER_SINGLE_BUCKET BIT(1)
+
+static int
+yt921x_meter_tfm(struct yt921x_priv *priv, int port, unsigned int slot_ns,
+ u64 rate, u64 burst, unsigned int flags,
+ u32 cir_max, u32 cbs_max, int unit_max,
+ struct yt921x_meter *meterp)
+{
+ const int C = flags & YT921X_METER_PKT_MODE ? YT921X_TOKEN_PKT_C :
+ YT921X_TOKEN_BYTE_C;
+ struct device *dev = to_device(priv);
+ struct yt921x_meter meter;
+ u64 burst_est;
+ u64 burst_sug;
+ u64 burst_max;
+ u64 rate_max;
+
+ meter.unit = unit_max;
+ rate_max = token2rate(cir_max, slot_ns, meter.unit, C);
+ burst_max = token2burst(cbs_max, meter.unit, C);
+
+ /* Check for unusual values */
+ if (rate > rate_max || burst > burst_max)
+ return -ERANGE;
+
+ /* Check for matching burst */
+ burst_est = div_u64(slot_ns * rate, 1000000000);
+ burst_sug = burst_est;
+ if (flags & YT921X_METER_PKT_MODE)
+ burst_sug++;
+ else
+ burst_sug += ETH_HLEN + yt921x_mtu_fetch(priv, port) +
+ ETH_FCS_LEN;
+ if (burst_sug > burst)
+ dev_warn(dev,
+ "Consider burst at least %llu to match rate %llu\n",
+ burst_sug, rate);
+
+ /* Select unit */
+ for (; meter.unit > 0; meter.unit--) {
+ if (rate > (rate_max >> 2) || burst > (burst_max >> 2))
+ break;
+ rate_max >>= 2;
+ burst_max >>= 2;
+ }
+
+ /* Calculate information rate and bucket size */
+ meter.cir = rate2token(rate, slot_ns, meter.unit, C);
+ if (!meter.cir)
+ meter.cir = 1;
+ else if (WARN_ON(meter.cir > cir_max))
+ meter.cir = cir_max;
+ meter.cbs = burst2token(burst, meter.unit, C);
+ if (!meter.cbs)
+ meter.cbs = 1;
+ else if (WARN_ON(meter.cbs > cbs_max))
+ meter.cbs = cbs_max;
+
+ /* Cut EBS */
+ meter.ebs = 0;
+ if (!(flags & YT921X_METER_SINGLE_BUCKET)) {
+ /* We don't have a chance to adjust rate when MTU is changed */
+ if (flags & YT921X_METER_PKT_MODE)
+ burst_est++;
+ else
+ burst_est += YT921X_FRAME_SIZE_MAX;
+
+ if (burst_est < burst) {
+ u32 pbs = meter.cbs;
+
+ meter.cbs = burst2token(burst_est, meter.unit, C);
+ if (!meter.cbs) {
+ meter.cbs = 1;
+ } else if (meter.cbs > cbs_max) {
+ WARN_ON(1);
+ meter.cbs = cbs_max;
+ }
+
+ if (pbs > meter.cbs)
+ meter.ebs = pbs - meter.cbs;
+ }
+ }
+
+ dev_dbg(dev,
+ "slot %u ns, rate %llu, burst %llu -> unit %d, cir %u, cbs %u, ebs %u\n",
+ slot_ns, rate, burst,
+ meter.unit, meter.cir, meter.cbs, meter.ebs);
+
+ *meterp = meter;
+ return 0;
+}
+
+static void yt921x_dsa_port_policer_del(struct dsa_switch *ds, int port)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ struct device *dev = to_device(priv);
+ int res;
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_reg_write(priv, YT921X_PORTn_METER(port), 0);
+ mutex_unlock(&priv->reg_lock);
+
+ if (res)
+ dev_err(dev, "Failed to %s port %d: %i\n", "delete policer on",
+ port, res);
+}
+
+static int
+yt921x_dsa_port_policer_add(struct dsa_switch *ds, int port,
+ const struct flow_action_police *policer,
+ struct netlink_ext_ack *extack)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ struct yt921x_meter meter;
+ bool pkt_mode;
+ u32 ctrls[3];
+ u64 burst;
+ u64 rate;
+ u32 ctrl;
+ int res;
+
+ /* mtu defaults to unlimited but we got 2040 here, don't know why */
+ if (policer->peakrate_bytes_ps || policer->avrate || policer->overhead) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "peakrate / avrate / overhead not supported");
+ return -EOPNOTSUPP;
+ }
+ if (policer->exceed.act_id != FLOW_ACTION_DROP ||
+ policer->notexceed.act_id != FLOW_ACTION_ACCEPT) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "conform-exceed other than drop-ok not supported");
+ return -EOPNOTSUPP;
+ }
+
+ pkt_mode = !!policer->rate_pkt_ps;
+ rate = pkt_mode ? policer->rate_pkt_ps : policer->rate_bytes_ps;
+ burst = pkt_mode ? policer->burst_pkt : policer->burst;
+ res = yt921x_meter_tfm(priv, port, priv->meter_slot_ns, rate, burst,
+ pkt_mode ? YT921X_METER_PKT_MODE : 0,
+ YT921X_METER_CIR_MAX, YT921X_METER_CBS_MAX,
+ YT921X_METER_UNIT_MAX, &meter);
+ if (res) {
+ NL_SET_ERR_MSG_MOD(extack, "Unexpected tremendous rate");
+ return res;
+ }
+
+ mutex_lock(&priv->reg_lock);
+ ctrl = YT921X_PORT_METER_ID(port) | YT921X_PORT_METER_EN;
+ res = yt921x_reg_write(priv, YT921X_PORTn_METER(port), ctrl);
+ if (res)
+ goto end;
+
+ ctrls[0] = 0;
+ ctrls[1] = YT921X_METER_CTRLb_CIR(meter.cir);
+ ctrls[2] = YT921X_METER_CTRLc_UNIT(meter.unit) |
+ YT921X_METER_CTRLc_DROP_R |
+ YT921X_METER_CTRLc_TOKEN_OVERFLOW_EN |
+ YT921X_METER_CTRLc_METER_EN;
+ update_ctrls_unaligned(&ctrls[0], &ctrls[1],
+ YT921X_METER_CTRLab_EBS_M,
+ YT921X_METER_CTRLab_EBS(meter.ebs));
+ update_ctrls_unaligned(&ctrls[1], &ctrls[2],
+ YT921X_METER_CTRLbc_CBS_M,
+ YT921X_METER_CTRLbc_CBS(meter.cbs));
+ res = yt921x_reg96_write(priv,
+ YT921X_METERn_CTRL(port + YT921X_METER_NUM),
+ ctrls);
+end:
+ mutex_unlock(&priv->reg_lock);
+
+ return res;
+}
+
static int
yt921x_mirror_del(struct yt921x_priv *priv, int port, bool ingress)
{
@@ -3051,6 +3285,7 @@ static int yt921x_chip_detect(struct yt921x_priv *priv)
u32 chipid;
u32 major;
u32 mode;
+ u32 val;
int res;
res = yt921x_reg_read(priv, YT921X_CHIP_ID, &chipid);
@@ -3085,12 +3320,27 @@ static int yt921x_chip_detect(struct yt921x_priv *priv)
return -ENODEV;
}
+ res = yt921x_reg_read(priv, YT921X_SYS_CLK, &val);
+ if (res)
+ return res;
+ switch (FIELD_GET(YT921X_SYS_CLK_SEL_M, val)) {
+ case 0:
+ priv->cycle_ns = info->major == YT9215_MAJOR ? 8 : 6;
+ break;
+ case YT921X_SYS_CLK_143M:
+ priv->cycle_ns = 7;
+ break;
+ default:
+ priv->cycle_ns = 8;
+ }
+
/* Print chipid here since we are interested in lower 16 bits */
dev_info(dev,
"Motorcomm %s ethernet switch, chipid: 0x%x, chipmode: 0x%x 0x%x\n",
info->name, chipid, mode, extmode);
priv->info = info;
+
return 0;
}
@@ -3212,6 +3462,23 @@ static int yt921x_chip_setup_dsa(struct yt921x_priv *priv)
return 0;
}
+static int yt921x_chip_setup_tc(struct yt921x_priv *priv)
+{
+ unsigned int op_ns;
+ u32 ctrl;
+ int res;
+
+ op_ns = 8 * priv->cycle_ns;
+
+ ctrl = max(priv->meter_slot_ns / op_ns, YT921X_METER_SLOT_MIN);
+ res = yt921x_reg_write(priv, YT921X_METER_SLOT, ctrl);
+ if (res)
+ return res;
+ priv->meter_slot_ns = ctrl * op_ns;
+
+ return 0;
+}
+
static int __maybe_unused yt921x_chip_setup_qos(struct yt921x_priv *priv)
{
u32 ctrl;
@@ -3258,7 +3525,7 @@ static int yt921x_chip_setup(struct yt921x_priv *priv)
u32 ctrl;
int res;
- ctrl = YT921X_FUNC_MIB;
+ ctrl = YT921X_FUNC_MIB | YT921X_FUNC_METER;
res = yt921x_reg_set_bits(priv, YT921X_FUNC, ctrl);
if (res)
return res;
@@ -3267,6 +3534,10 @@ static int yt921x_chip_setup(struct yt921x_priv *priv)
if (res)
return res;
+ res = yt921x_chip_setup_tc(priv);
+ if (res)
+ return res;
+
#if IS_ENABLED(CONFIG_DCB)
res = yt921x_chip_setup_qos(priv);
if (res)
@@ -3358,6 +3629,9 @@ static const struct dsa_switch_ops yt921x_dsa_switch_ops = {
/* mtu */
.port_change_mtu = yt921x_dsa_port_change_mtu,
.port_max_mtu = yt921x_dsa_port_max_mtu,
+ /* rate */
+ .port_policer_del = yt921x_dsa_port_policer_del,
+ .port_policer_add = yt921x_dsa_port_policer_add,
/* hsr */
.port_hsr_leave = dsa_port_simple_hsr_leave,
.port_hsr_join = dsa_port_simple_hsr_join,
diff --git a/drivers/net/dsa/yt921x.h b/drivers/net/dsa/yt921x.h
index 4989d87c2492..a640672d1f1e 100644
--- a/drivers/net/dsa/yt921x.h
+++ b/drivers/net/dsa/yt921x.h
@@ -23,6 +23,7 @@
#define YT921X_RST_HW BIT(31)
#define YT921X_RST_SW BIT(1)
#define YT921X_FUNC 0x80004
+#define YT921X_FUNC_METER BIT(4)
#define YT921X_FUNC_MIB BIT(1)
#define YT921X_CHIP_ID 0x80008
#define YT921X_CHIP_ID_MAJOR GENMASK(31, 16)
@@ -239,6 +240,11 @@
#define YT921X_EDATA_DATA_STATUS_M GENMASK(3, 0)
#define YT921X_EDATA_DATA_STATUS(x) FIELD_PREP(YT921X_EDATA_DATA_STATUS_M, (x))
#define YT921X_EDATA_DATA_IDLE YT921X_EDATA_DATA_STATUS(3)
+#define YT921X_SYS_CLK 0xe0040
+#define YT921X_SYS_CLK_SEL_M GENMASK(1, 0) /* unknown: 167M */
+#define YT9215_SYS_CLK_125M 0
+#define YT9218_SYS_CLK_167M 0
+#define YT921X_SYS_CLK_143M 1
#define YT921X_EXT_MBUS_OP 0x6a000
#define YT921X_INT_MBUS_OP 0xf0000
@@ -465,6 +471,42 @@ enum yt921x_app_selector {
#define YT921X_LAG_HASH_MAC_DA BIT(1)
#define YT921X_LAG_HASH_SRC_PORT BIT(0)
+#define YT921X_PORTn_RATE(port) (0x220000 + 4 * (port))
+#define YT921X_PORT_RATE_GAP_VALUE GENMASK(4, 0) /* default 20 */
+#define YT921X_METER_SLOT 0x220104
+#define YT921X_METER_SLOT_SLOT_M GENMASK(11, 0)
+#define YT921X_PORTn_METER(port) (0x220108 + 4 * (port))
+#define YT921X_PORT_METER_EN BIT(4)
+#define YT921X_PORT_METER_ID_M GENMASK(3, 0)
+#define YT921X_PORT_METER_ID(x) FIELD_PREP(YT921X_PORT_METER_ID_M, (x))
+#define YT921X_METERn_CTRL(x) (0x220800 + 0x10 * (x))
+#define YT921X_METER_CTRLc_METER_EN BIT(14)
+#define YT921X_METER_CTRLc_TOKEN_OVERFLOW_EN BIT(13) /* RFC4115: yellow use unused green bw */
+#define YT921X_METER_CTRLc_DROP_M GENMASK(12, 11)
+#define YT921X_METER_CTRLc_DROP(x) FIELD_PREP(YT921X_METER_CTRLc_DROP_M, (x))
+#define YT921X_METER_CTRLc_DROP_GYR YT921X_METER_CTRLc_DROP(0)
+#define YT921X_METER_CTRLc_DROP_YR YT921X_METER_CTRLc_DROP(1)
+#define YT921X_METER_CTRLc_DROP_R YT921X_METER_CTRLc_DROP(2)
+#define YT921X_METER_CTRLc_DROP_NONE YT921X_METER_CTRLc_DROP(3)
+#define YT921X_METER_CTRLc_COLOR_BLIND BIT(10)
+#define YT921X_METER_CTRLc_UNIT_M GENMASK(9, 7)
+#define YT921X_METER_CTRLc_UNIT(x) FIELD_PREP(YT921X_METER_CTRLc_UNIT_M, (x))
+#define YT921X_METER_CTRLc_BYTE_MODE_INCLUDE_GAP BIT(6) /* +GAP_VALUE bytes each packet */
+#define YT921X_METER_CTRLc_PKT_MODE BIT(5) /* 0: byte rate mode */
+#define YT921X_METER_CTRLc_RFC2698 BIT(4) /* 0: RFC4115 */
+#define YT921X_METER_CTRLbc_CBS_M GENMASK_ULL(35, 20)
+#define YT921X_METER_CTRLbc_CBS(x) FIELD_PREP(YT921X_METER_CTRLbc_CBS_M, (x))
+#define YT921X_METER_CTRLb_CIR_M GENMASK(19, 2)
+#define YT921X_METER_CTRLb_CIR(x) FIELD_PREP(YT921X_METER_CTRLb_CIR_M, (x))
+#define YT921X_METER_CTRLab_EBS_M GENMASK_ULL(33, 18)
+#define YT921X_METER_CTRLab_EBS(x) FIELD_PREP(YT921X_METER_CTRLab_EBS_M, (x))
+#define YT921X_METER_CTRLa_EIR_M GENMASK(17, 0)
+#define YT921X_METER_CTRLa_EIR(x) FIELD_PREP(YT921X_METER_CTRLa_EIR_M, (x))
+#define YT921X_METERn_STAT_EXCESS(x) (0x221000 + 8 * (x))
+#define YT921X_METERn_STAT_COMMITTED(x) (0x221004 + 8 * (x))
+#define YT921X_METER_STAT_TOKEN_M GENMASK(30, 15)
+#define YT921X_METER_STAT_QUEUE_M GENMASK(14, 0)
+
#define YT921X_PORTn_VLAN_CTRL(port) (0x230010 + 4 * (port))
#define YT921X_PORT_VLAN_CTRL_SVLAN_PRIO_EN BIT(31)
#define YT921X_PORT_VLAN_CTRL_CVLAN_PRIO_EN BIT(30)
@@ -508,6 +550,16 @@ enum yt921x_fdb_entry_status {
#define YT921X_MSTI_NUM 16
+#define YT921X_TOKEN_BYTE_C 1 /* 1 token = 2^1 byte */
+#define YT921X_TOKEN_PKT_C -6 /* 1 token = 2^-6 packets */
+#define YT921X_TOKEN_RATE_C -15
+/* for VLAN only, not including port meters */
+#define YT921X_METER_NUM 64
+#define YT921X_METER_SLOT_MIN 80
+#define YT921X_METER_UNIT_MAX ((1 << 3) - 1)
+#define YT921X_METER_CIR_MAX ((1 << 18) - 1)
+#define YT921X_METER_CBS_MAX ((1 << 16) - 1)
+
#define YT921X_LAG_NUM 2
#define YT921X_LAG_PORT_NUM 4
@@ -602,8 +654,10 @@ struct yt921x_priv {
struct dsa_switch ds;
const struct yt921x_info *info;
+ unsigned int meter_slot_ns;
/* cache of dsa_cpu_ports(ds) */
u16 cpu_ports_mask;
+ u8 cycle_ns;
/* protect the access to the switch registers */
struct mutex reg_lock;
--
2.53.0
^ permalink raw reply related
* [PATCH net-next v3 2/4] net: dsa: yt921x: Refactor long register helpers
From: David Yang @ 2026-04-07 16:05 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, linux-kernel
In-Reply-To: <20260407160559.1747616-1-mmyangfl@gmail.com>
Dealing long registers with u64 is good, until you realize there are
longer 96-bit registers.
Refactor reg64 helpers with u32 arrays, so they are aligned with further
reg96 helpers. We do not keep the u64 implement to avoid duplicated
wrappers, although it looks better when we deal with reg64 *only*.
Helpers for reg96 should be added when they are actually used to avoid
function unused warnings.
Signed-off-by: David Yang <mmyangfl@gmail.com>
---
drivers/net/dsa/yt921x.c | 161 ++++++++++++++++++++++++++-------------
drivers/net/dsa/yt921x.h | 36 ++++-----
2 files changed, 128 insertions(+), 69 deletions(-)
diff --git a/drivers/net/dsa/yt921x.c b/drivers/net/dsa/yt921x.c
index 87139448bec3..de6aa5835616 100644
--- a/drivers/net/dsa/yt921x.c
+++ b/drivers/net/dsa/yt921x.c
@@ -255,63 +255,121 @@ yt921x_reg_toggle_bits(struct yt921x_priv *priv, u32 reg, u32 mask, bool set)
return yt921x_reg_update_bits(priv, reg, mask, !set ? 0 : mask);
}
-/* Some registers, like VLANn_CTRL, should always be written in 64-bit, even if
- * you are to write only the lower / upper 32 bits.
+/* Some registers, like VLANn_CTRL, should always be written whole, even if you
+ * only want to write parts of 32 bits.
*
- * There is no such restriction for reading, but we still provide 64-bit read
- * wrappers so that we always handle u64 values.
+ * There is no such restriction for reading, but we still provide the read
+ * wrapper so that we can handle them consistently.
*/
-static int yt921x_reg64_read(struct yt921x_priv *priv, u32 reg, u64 *valp)
+static int
+yt921x_regs_read(struct yt921x_priv *priv, u32 reg, u32 *vals,
+ unsigned int num_regs)
{
- u32 lo;
- u32 hi;
int res;
- res = yt921x_reg_read(priv, reg, &lo);
- if (res)
- return res;
- res = yt921x_reg_read(priv, reg + 4, &hi);
- if (res)
- return res;
+ for (unsigned int i = 0; i < num_regs; i++) {
+ res = yt921x_reg_read(priv, reg + 4 * i, &vals[i]);
+ if (res)
+ return res;
+ }
+
+ return 0;
+}
+
+static int
+yt921x_regs_write(struct yt921x_priv *priv, u32 reg, const u32 *vals,
+ unsigned int num_regs)
+{
+ int res;
+
+ for (unsigned int i = 0; i < num_regs; i++) {
+ res = yt921x_reg_write(priv, reg + 4 * i, vals[i]);
+ if (res)
+ return res;
+ }
- *valp = ((u64)hi << 32) | lo;
return 0;
}
-static int yt921x_reg64_write(struct yt921x_priv *priv, u32 reg, u64 val)
+static int
+yt921x_regs_update_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks,
+ const u32 *vals, unsigned int num_regs)
{
+ bool changed = false;
+ u32 vs[4];
int res;
- res = yt921x_reg_write(priv, reg, (u32)val);
+ BUILD_BUG_ON(num_regs > ARRAY_SIZE(vs));
+
+ res = yt921x_regs_read(priv, reg, vs, num_regs);
if (res)
return res;
- return yt921x_reg_write(priv, reg + 4, (u32)(val >> 32));
+
+ for (unsigned int i = 0; i < num_regs; i++) {
+ u32 u = vs[i];
+
+ u &= ~masks[i];
+ u |= vals[i];
+ if (u != vs[i])
+ changed = true;
+
+ vs[i] = u;
+ }
+
+ if (!changed)
+ return 0;
+
+ return yt921x_regs_write(priv, reg, vs, num_regs);
}
static int
-yt921x_reg64_update_bits(struct yt921x_priv *priv, u32 reg, u64 mask, u64 val)
+yt921x_regs_clear_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks,
+ unsigned int num_regs)
{
+ bool changed = false;
+ u32 vs[4];
int res;
- u64 v;
- u64 u;
- res = yt921x_reg64_read(priv, reg, &v);
+ BUILD_BUG_ON(num_regs > ARRAY_SIZE(vs));
+
+ res = yt921x_regs_read(priv, reg, vs, num_regs);
if (res)
return res;
- u = v;
- u &= ~mask;
- u |= val;
- if (u == v)
+ for (unsigned int i = 0; i < num_regs; i++) {
+ u32 u = vs[i];
+
+ u &= ~masks[i];
+ if (u != vs[i])
+ changed = true;
+
+ vs[i] = u;
+ }
+
+ if (!changed)
return 0;
- return yt921x_reg64_write(priv, reg, u);
+ return yt921x_regs_write(priv, reg, vs, num_regs);
+}
+
+static int
+yt921x_reg64_write(struct yt921x_priv *priv, u32 reg, const u32 *vals)
+{
+ return yt921x_regs_write(priv, reg, vals, 2);
}
-static int yt921x_reg64_clear_bits(struct yt921x_priv *priv, u32 reg, u64 mask)
+static int
+yt921x_reg64_update_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks,
+ const u32 *vals)
{
- return yt921x_reg64_update_bits(priv, reg, mask, 0);
+ return yt921x_regs_update_bits(priv, reg, masks, vals, 2);
+}
+
+static int
+yt921x_reg64_clear_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks)
+{
+ return yt921x_regs_clear_bits(priv, reg, masks, 2);
}
static int yt921x_reg_mdio_read(void *context, u32 reg, u32 *valp)
@@ -1844,33 +1902,31 @@ yt921x_vlan_filtering(struct yt921x_priv *priv, int port, bool vlan_filtering)
return 0;
}
-static int
-yt921x_vlan_del(struct yt921x_priv *priv, int port, u16 vid)
+static int yt921x_vlan_del(struct yt921x_priv *priv, int port, u16 vid)
{
- u64 mask64;
+ u32 masks[2];
- mask64 = YT921X_VLAN_CTRL_PORTS(port) |
- YT921X_VLAN_CTRL_UNTAG_PORTn(port);
+ masks[0] = YT921X_VLAN_CTRLa_PORTn(port);
+ masks[1] = YT921X_VLAN_CTRLb_UNTAG_PORTn(port);
- return yt921x_reg64_clear_bits(priv, YT921X_VLANn_CTRL(vid), mask64);
+ return yt921x_reg64_clear_bits(priv, YT921X_VLANn_CTRL(vid), masks);
}
static int
yt921x_vlan_add(struct yt921x_priv *priv, int port, u16 vid, bool untagged)
{
- u64 mask64;
- u64 ctrl64;
+ u32 masks[2];
+ u32 ctrls[2];
- mask64 = YT921X_VLAN_CTRL_PORTn(port) |
- YT921X_VLAN_CTRL_PORTS(priv->cpu_ports_mask);
- ctrl64 = mask64;
+ masks[0] = YT921X_VLAN_CTRLa_PORTn(port) |
+ YT921X_VLAN_CTRLa_PORTS(priv->cpu_ports_mask);
+ ctrls[0] = masks[0];
- mask64 |= YT921X_VLAN_CTRL_UNTAG_PORTn(port);
- if (untagged)
- ctrl64 |= YT921X_VLAN_CTRL_UNTAG_PORTn(port);
+ masks[1] = YT921X_VLAN_CTRLb_UNTAG_PORTn(port);
+ ctrls[1] = untagged ? masks[1] : 0;
return yt921x_reg64_update_bits(priv, YT921X_VLANn_CTRL(vid),
- mask64, ctrl64);
+ masks, ctrls);
}
static int
@@ -2318,8 +2374,8 @@ yt921x_dsa_vlan_msti_set(struct dsa_switch *ds, struct dsa_bridge bridge,
const struct switchdev_vlan_msti *msti)
{
struct yt921x_priv *priv = to_yt921x_priv(ds);
- u64 mask64;
- u64 ctrl64;
+ u32 masks[2];
+ u32 ctrls[2];
int res;
if (!msti->vid)
@@ -2327,12 +2383,14 @@ yt921x_dsa_vlan_msti_set(struct dsa_switch *ds, struct dsa_bridge bridge,
if (!msti->msti || msti->msti >= YT921X_MSTI_NUM)
return -EINVAL;
- mask64 = YT921X_VLAN_CTRL_STP_ID_M;
- ctrl64 = YT921X_VLAN_CTRL_STP_ID(msti->msti);
+ masks[0] = 0;
+ ctrls[0] = 0;
+ masks[1] = YT921X_VLAN_CTRLb_STP_ID_M;
+ ctrls[1] = YT921X_VLAN_CTRLb_STP_ID(msti->msti);
mutex_lock(&priv->reg_lock);
res = yt921x_reg64_update_bits(priv, YT921X_VLANn_CTRL(msti->vid),
- mask64, ctrl64);
+ masks, ctrls);
mutex_unlock(&priv->reg_lock);
return res;
@@ -3084,7 +3142,7 @@ static int yt921x_chip_setup_dsa(struct yt921x_priv *priv)
{
struct dsa_switch *ds = &priv->ds;
unsigned long cpu_ports_mask;
- u64 ctrl64;
+ u32 ctrls[2];
u32 ctrl;
int port;
int res;
@@ -3145,8 +3203,9 @@ static int yt921x_chip_setup_dsa(struct yt921x_priv *priv)
/* Tagged VID 0 should be treated as untagged, which confuses the
* hardware a lot
*/
- ctrl64 = YT921X_VLAN_CTRL_LEARN_DIS | YT921X_VLAN_CTRL_PORTS_M;
- res = yt921x_reg64_write(priv, YT921X_VLANn_CTRL(0), ctrl64);
+ ctrls[0] = YT921X_VLAN_CTRLa_LEARN_DIS | YT921X_VLAN_CTRLa_PORTS_M;
+ ctrls[1] = 0;
+ res = yt921x_reg64_write(priv, YT921X_VLANn_CTRL(0), ctrls);
if (res)
return res;
diff --git a/drivers/net/dsa/yt921x.h b/drivers/net/dsa/yt921x.h
index 3f129b8d403f..4989d87c2492 100644
--- a/drivers/net/dsa/yt921x.h
+++ b/drivers/net/dsa/yt921x.h
@@ -429,24 +429,24 @@ enum yt921x_app_selector {
#define YT921X_FDB_HW_FLUSH_ON_LINKDOWN BIT(0)
#define YT921X_VLANn_CTRL(vlan) (0x188000 + 8 * (vlan))
-#define YT921X_VLAN_CTRL_UNTAG_PORTS_M GENMASK_ULL(50, 40)
-#define YT921X_VLAN_CTRL_UNTAG_PORTS(x) FIELD_PREP(YT921X_VLAN_CTRL_UNTAG_PORTS_M, (x))
-#define YT921X_VLAN_CTRL_UNTAG_PORTn(port) BIT_ULL((port) + 40)
-#define YT921X_VLAN_CTRL_STP_ID_M GENMASK_ULL(39, 36)
-#define YT921X_VLAN_CTRL_STP_ID(x) FIELD_PREP(YT921X_VLAN_CTRL_STP_ID_M, (x))
-#define YT921X_VLAN_CTRL_SVLAN_EN BIT_ULL(35)
-#define YT921X_VLAN_CTRL_FID_M GENMASK_ULL(34, 23)
-#define YT921X_VLAN_CTRL_FID(x) FIELD_PREP(YT921X_VLAN_CTRL_FID_M, (x))
-#define YT921X_VLAN_CTRL_LEARN_DIS BIT_ULL(22)
-#define YT921X_VLAN_CTRL_PRIO_EN BIT_ULL(21)
-#define YT921X_VLAN_CTRL_PRIO_M GENMASK_ULL(20, 18)
-#define YT921X_VLAN_CTRL_PRIO(x) FIELD_PREP(YT921X_VLAN_CTRL_PRIO_M, (x))
-#define YT921X_VLAN_CTRL_PORTS_M GENMASK_ULL(17, 7)
-#define YT921X_VLAN_CTRL_PORTS(x) FIELD_PREP(YT921X_VLAN_CTRL_PORTS_M, (x))
-#define YT921X_VLAN_CTRL_PORTn(port) BIT_ULL((port) + 7)
-#define YT921X_VLAN_CTRL_BYPASS_1X_AC BIT_ULL(6)
-#define YT921X_VLAN_CTRL_METER_EN BIT_ULL(5)
-#define YT921X_VLAN_CTRL_METER_ID_M GENMASK_ULL(4, 0)
+#define YT921X_VLAN_CTRLb_UNTAG_PORTS_M GENMASK(18, 8)
+#define YT921X_VLAN_CTRLb_UNTAG_PORTS(x) FIELD_PREP(YT921X_VLAN_CTRLb_UNTAG_PORTS_M, (x))
+#define YT921X_VLAN_CTRLb_UNTAG_PORTn(port) BIT((port) + 8)
+#define YT921X_VLAN_CTRLb_STP_ID_M GENMASK(7, 4)
+#define YT921X_VLAN_CTRLb_STP_ID(x) FIELD_PREP(YT921X_VLAN_CTRLb_STP_ID_M, (x))
+#define YT921X_VLAN_CTRLb_SVLAN_EN BIT(3)
+#define YT921X_VLAN_CTRLab_FID_M GENMASK_ULL(34, 23)
+#define YT921X_VLAN_CTRLab_FID(x) FIELD_PREP(YT921X_VLAN_CTRLab_FID_M, (x))
+#define YT921X_VLAN_CTRLa_LEARN_DIS BIT(22)
+#define YT921X_VLAN_CTRLa_PRIO_EN BIT(21)
+#define YT921X_VLAN_CTRLa_PRIO_M GENMASK(20, 18)
+#define YT921X_VLAN_CTRLa_PRIO(x) FIELD_PREP(YT921X_VLAN_CTRLa_PRIO_M, (x))
+#define YT921X_VLAN_CTRLa_PORTS_M GENMASK(17, 7)
+#define YT921X_VLAN_CTRLa_PORTS(x) FIELD_PREP(YT921X_VLAN_CTRLa_PORTS_M, (x))
+#define YT921X_VLAN_CTRLa_PORTn(port) BIT((port) + 7)
+#define YT921X_VLAN_CTRLa_BYPASS_1X_AC BIT(6)
+#define YT921X_VLAN_CTRLa_METER_EN BIT(5)
+#define YT921X_VLAN_CTRLa_METER_ID_M GENMASK(4, 0)
#define YT921X_TPID_IGRn(x) (0x210000 + 4 * (x)) /* [0, 3] */
#define YT921X_TPID_IGR_TPID_M GENMASK(15, 0)
--
2.53.0
^ permalink raw reply related
* [PATCH net-next v3 1/4] net: dsa: pass extack to user tc policers
From: David Yang @ 2026-04-07 16:05 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, UNGLinuxDriver,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, linux-kernel
In-Reply-To: <20260407160559.1747616-1-mmyangfl@gmail.com>
Users may use extack for a friendly error message instead of dumping
everything into dmesg.
Make the according transformations to the two users (sja1105 and
felix).
Signed-off-by: David Yang <mmyangfl@gmail.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
---
drivers/net/dsa/ocelot/felix.c | 3 ++-
drivers/net/dsa/sja1105/sja1105_main.c | 3 ++-
include/net/dsa.h | 3 ++-
net/dsa/user.c | 2 +-
4 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c
index 84cf8e7fb17a..4272ea6e9ca8 100644
--- a/drivers/net/dsa/ocelot/felix.c
+++ b/drivers/net/dsa/ocelot/felix.c
@@ -2001,7 +2001,8 @@ static int felix_cls_flower_stats(struct dsa_switch *ds, int port,
}
static int felix_port_policer_add(struct dsa_switch *ds, int port,
- const struct flow_action_police *policer)
+ const struct flow_action_police *policer,
+ struct netlink_ext_ack *extack)
{
struct ocelot *ocelot = ds->priv;
struct ocelot_policer pol = {
diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c
index c72c2bfdcffb..dbfa45064747 100644
--- a/drivers/net/dsa/sja1105/sja1105_main.c
+++ b/drivers/net/dsa/sja1105/sja1105_main.c
@@ -2847,7 +2847,8 @@ static void sja1105_mirror_del(struct dsa_switch *ds, int port,
}
static int sja1105_port_policer_add(struct dsa_switch *ds, int port,
- const struct flow_action_police *policer)
+ const struct flow_action_police *policer,
+ struct netlink_ext_ack *extack)
{
struct sja1105_l2_policing_entry *policing;
struct sja1105_private *priv = ds->priv;
diff --git a/include/net/dsa.h b/include/net/dsa.h
index 8b6d34e8a6f0..4cc67469cf2e 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -1122,7 +1122,8 @@ struct dsa_switch_ops {
void (*port_mirror_del)(struct dsa_switch *ds, int port,
struct dsa_mall_mirror_tc_entry *mirror);
int (*port_policer_add)(struct dsa_switch *ds, int port,
- const struct flow_action_police *policer);
+ const struct flow_action_police *policer,
+ struct netlink_ext_ack *extack);
void (*port_policer_del)(struct dsa_switch *ds, int port);
int (*port_setup_tc)(struct dsa_switch *ds, int port,
enum tc_setup_type type, void *type_data);
diff --git a/net/dsa/user.c b/net/dsa/user.c
index c4bd6fe90b45..8704c1a3a5b7 100644
--- a/net/dsa/user.c
+++ b/net/dsa/user.c
@@ -1499,7 +1499,7 @@ dsa_user_add_cls_matchall_police(struct net_device *dev,
policer = &mall_tc_entry->policer;
*policer = act->police;
- err = ds->ops->port_policer_add(ds, dp->index, policer);
+ err = ds->ops->port_policer_add(ds, dp->index, policer, extack);
if (err) {
kfree(mall_tc_entry);
return err;
--
2.53.0
^ permalink raw reply related
* [PATCH net-next v3 0/4] net: dsa: yt921x: Add port police/tbf support
From: David Yang @ 2026-04-07 16:05 UTC (permalink / raw)
To: netdev
Cc: David Yang, Vladimir Oltean, UNGLinuxDriver, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, linux-kernel, Vladimir Oltean
v2: https://lore.kernel.org/r/20260402223437.109097-1-mmyangfl@gmail.com
- refine commit messages and code styles, no functional changes
v1: https://lore.kernel.org/r/20260225090853.2021140-1-mmyangfl@gmail.com
- pass extack to user tc policers
- keep reg64 helpers along with reg96
- avoid macros in favor of functions
- adjust log messages
David Yang (4):
net: dsa: pass extack to user tc policers
net: dsa: yt921x: Refactor long register helpers
net: dsa: yt921x: Add port police support
net: dsa: yt921x: Add port qdisc tbf support
drivers/net/dsa/ocelot/felix.c | 3 +-
drivers/net/dsa/sja1105/sja1105_main.c | 3 +-
drivers/net/dsa/yt921x.c | 560 ++++++++++++++++++++++---
drivers/net/dsa/yt921x.h | 155 ++++++-
include/net/dsa.h | 3 +-
net/dsa/user.c | 2 +-
6 files changed, 651 insertions(+), 75 deletions(-)
--
2.53.0
^ permalink raw reply
* Re: [PATCH iwl-net] idpf: fix read_dev_clk_lock spinlock init in idpf_ptp_init()
From: Simon Horman @ 2026-04-07 16:02 UTC (permalink / raw)
To: emil.s.tantilov
Cc: 'Simon Horman', intel-wired-lan, netdev, anthony.l.nguyen,
aleksandr.loktionov, przemyslaw.kitszel, andrew+netdev, davem,
edumazet, kuba, pabeni, richardcochran, milena.olech,
jacob.e.keller, konstantin.ilichev
In-Reply-To: <20260403153538.11516-1-emil.s.tantilov@intel.com>
From: 'Simon Horman' <horms@kernel.org>
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
Full review at: https://sashiko.dev
Simon says: I don't agree with the regression characterisation made by
the AI review - I think this patch is good. But I do think the issues
flagged by the AI review warrant investigation.
---
idpf: fix read_dev_clk_lock spinlock init in idpf_ptp_init()
This commit moves the initialization of read_dev_clk_lock earlier in
idpf_ptp_init() to ensure it is initialized before the PTP auxiliary
worker is scheduled.
> diff --git a/drivers/net/ethernet/intel/idpf/idpf_ptp.c b/drivers/net/ethernet/intel/idpf/idpf_ptp.c
> index eec91c4f0a75a..4a51d2727547d 100644
> --- a/drivers/net/ethernet/intel/idpf/idpf_ptp.c
> +++ b/drivers/net/ethernet/intel/idpf/idpf_ptp.c
> @@ -952,6 +952,8 @@ int idpf_ptp_init(struct idpf_adapter *adapter)
> goto free_ptp;
> }
>
> + spin_lock_init(&adapter->ptp->read_dev_clk_lock);
> +
> err = idpf_ptp_create_clock(adapter);
This patch moves the lock initialization to prevent the worker from accessing
an uninitialized lock. However, idpf_ptp_create_clock() registers the PTP
device, and shortly after, ptp_schedule_worker() is called. Both happen
before the hardware clock baseline and initial time are set later in
idpf_ptp_init().
Could this introduce a regression where userspace opens the PTP device and
interacts with an uninitialized clock, or has its settings overwritten by
the driver's delayed idpf_ptp_settime64() call?
Additionally, could the scheduled worker read the uninitialized zero-time
and overwrite the software cache after the main thread sets the real time?
Moving the device registration and worker scheduling to the very end of
idpf_ptp_init() might resolve these races and naturally fix the lock
initialization ordering.
Also, since this code manages worker synchronization, is there a regression
when updating cached_phc_time and cached_phc_jiffies?
In idpf_ptp_update_cached_phctime(), these variables are updated
sequentially using the WRITE_ONCE() macro.
Because this function can be executed concurrently by the PTP worker
thread and ioctls, thread interleaving could result in an inconsistent
state where one variable is from one thread and the other from another.
Readers like idpf_ptp_extend_ts() load these variables using plain C
accesses without mutual exclusion or a seqlock.
Could this cause valid timestamps to be extended using an incorrect or
outdated baseline, silently corrupting network timestamps?
[ ... ]
^ permalink raw reply
* Re: [PATCH v3 net] af_unix: Give up GC if MSG_PEEK intervened.
From: Kuniyuki Iwashima @ 2026-04-07 16:03 UTC (permalink / raw)
To: Lee Jones
Cc: stable, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Kuniyuki Iwashima, Linus Torvalds,
netdev, Igor Ushakov
In-Reply-To: <20260407155827.GA1993342@google.com>
On Tue, Apr 7, 2026 at 8:58 AM Lee Jones <lee@kernel.org> wrote:
>
> INTENTIONAL TOP POST
>
> I note that this was not sent to Stable, but it should be included please.
It's included in 6.19.y, just a matter of time.
https://lore.kernel.org/stable/20260323134508.596880934@linuxfoundation.org/
^ permalink raw reply
* [PATCH 5/5] sockptr: introduce copy_struct_to_sockptr()
From: Stefan Metzmacher @ 2026-04-07 16:03 UTC (permalink / raw)
To: linux-kernel
Cc: metze, Dmitry Safonov, Dmitry Safonov, Francesco Ruggeri,
Salam Noureddine, David Ahern, David S . Miller, Michal Luczaj,
David Wei, Luiz Augusto von Dentz, Luiz Augusto von Dentz,
Marcel Holtmann, Xin Long, Eric Dumazet, Kuniyuki Iwashima,
Paolo Abeni, Willem de Bruijn, Neal Cardwell, Jakub Kicinski,
Simon Horman, Aleksa Sarai, Christian Brauner, Kees Cook, netdev,
linux-bluetooth
In-Reply-To: <cover.1775576651.git.metze@samba.org>
We already have copy_struct_from_sockptr() as wrapper to
copy_struct_from_user() or copy_struct_from_bounce_buffer(),
so it's good to have copy_struct_to_sockptr()
as well matching the behavior of copy_struct_to_user()
or copy_struct_to_bounce_buffer().
The world would be better without sockptr_t, but having
copy_struct_to_sockptr() is better than open code it
in various places.
I'll use this in my IPPROTO_SMBDIRECT work,
but maybe it will also be useful for others...
IPPROTO_QUIC will likely also use it.
Cc: Dmitry Safonov <0x7f454c46@gmail.com>
Cc: Dmitry Safonov <dima@arista.com>
Cc: Francesco Ruggeri <fruggeri@arista.com>
Cc: Salam Noureddine <noureddine@arista.com>
Cc: David Ahern <dsahern@kernel.org>
Cc: David S. Miller <davem@davemloft.net>
Cc: Michal Luczaj <mhal@rbox.co>
Cc: David Wei <dw@davidwei.uk>
Cc: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Cc: Luiz Augusto von Dentz <luiz.dentz@gmail.com>
Cc: Marcel Holtmann <marcel@holtmann.org>
Cc: Xin Long <lucien.xin@gmail.com>
Cc: Eric Dumazet <edumazet@google.com>
Cc: Kuniyuki Iwashima <kuniyu@google.com>
Cc: Paolo Abeni <pabeni@redhat.com>
Cc: Willem de Bruijn <willemb@google.com>
Cc: Neal Cardwell <ncardwell@google.com>
Cc: Jakub Kicinski <kuba@kernel.org>
Cc: Simon Horman <horms@kernel.org>
Cc: Aleksa Sarai <cyphar@cyphar.com>
Cc: Christian Brauner <brauner@kernel.org>
CC: Kees Cook <keescook@chromium.org>
Cc: netdev@vger.kernel.org
Cc: linux-bluetooth@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Signed-off-by: Stefan Metzmacher <metze@samba.org>
---
include/linux/sockptr.h | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/include/linux/sockptr.h b/include/linux/sockptr.h
index 706a8526cf3c..9c2429c1a570 100644
--- a/include/linux/sockptr.h
+++ b/include/linux/sockptr.h
@@ -107,6 +107,16 @@ static inline int copy_to_sockptr(sockptr_t dst, const void *src, size_t size)
return copy_to_sockptr_offset(dst, 0, src, size);
}
+static inline int
+copy_struct_to_sockptr(sockptr_t dst, size_t usize, const void *src,
+ size_t ksize, bool *ignored_trailing)
+{
+ if (!sockptr_is_kernel(dst))
+ return copy_struct_to_user(dst.user, usize, src, ksize, ignored_trailing);
+
+ return copy_struct_to_bounce_buffer(dst.kernel, usize, src, ksize, ignored_trailing);
+}
+
static inline void *memdup_sockptr_noprof(sockptr_t src, size_t len)
{
void *p = kmalloc_track_caller_noprof(len, GFP_USER | __GFP_NOWARN);
--
2.43.0
^ permalink raw reply related
* [PATCH 4/5] sockptr: let copy_struct_from_sockptr() use copy_struct_from_bounce_buffer()
From: Stefan Metzmacher @ 2026-04-07 16:03 UTC (permalink / raw)
To: linux-kernel
Cc: metze, Dmitry Safonov, Dmitry Safonov, Francesco Ruggeri,
Salam Noureddine, David Ahern, David S . Miller, Michal Luczaj,
David Wei, Luiz Augusto von Dentz, Luiz Augusto von Dentz,
Marcel Holtmann, Xin Long, Eric Dumazet, Kuniyuki Iwashima,
Paolo Abeni, Willem de Bruijn, Neal Cardwell, Jakub Kicinski,
Simon Horman, Aleksa Sarai, Christian Brauner, Kees Cook, netdev,
linux-bluetooth
In-Reply-To: <cover.1775576651.git.metze@samba.org>
The world would be better without sockptr_t, but this at least
simplifies copy_struct_from_sockptr() to be just a dispatcher for
copy_struct_from_user() or copy_struct_from_bounce_buffer() without any
special logic on its own.
Cc: Dmitry Safonov <0x7f454c46@gmail.com>
Cc: Dmitry Safonov <dima@arista.com>
Cc: Francesco Ruggeri <fruggeri@arista.com>
Cc: Salam Noureddine <noureddine@arista.com>
Cc: David Ahern <dsahern@kernel.org>
Cc: David S. Miller <davem@davemloft.net>
Cc: Michal Luczaj <mhal@rbox.co>
Cc: David Wei <dw@davidwei.uk>
Cc: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Cc: Luiz Augusto von Dentz <luiz.dentz@gmail.com>
Cc: Marcel Holtmann <marcel@holtmann.org>
Cc: Xin Long <lucien.xin@gmail.com>
Cc: Eric Dumazet <edumazet@google.com>
Cc: Kuniyuki Iwashima <kuniyu@google.com>
Cc: Paolo Abeni <pabeni@redhat.com>
Cc: Willem de Bruijn <willemb@google.com>
Cc: Neal Cardwell <ncardwell@google.com>
Cc: Jakub Kicinski <kuba@kernel.org>
Cc: Simon Horman <horms@kernel.org>
Cc: Aleksa Sarai <cyphar@cyphar.com>
Cc: Christian Brauner <brauner@kernel.org>
CC: Kees Cook <keescook@chromium.org>
Cc: netdev@vger.kernel.org
Cc: linux-bluetooth@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Signed-off-by: Stefan Metzmacher <metze@samba.org>
---
include/linux/sockptr.h | 16 +---------------
1 file changed, 1 insertion(+), 15 deletions(-)
diff --git a/include/linux/sockptr.h b/include/linux/sockptr.h
index ba88f4d78c1b..706a8526cf3c 100644
--- a/include/linux/sockptr.h
+++ b/include/linux/sockptr.h
@@ -87,24 +87,10 @@ static inline int copy_safe_from_sockptr(void *dst, size_t ksize,
static inline int copy_struct_from_sockptr(void *dst, size_t ksize,
sockptr_t src, size_t usize)
{
- size_t size = min(ksize, usize);
- size_t rest = max(ksize, usize) - size;
-
if (!sockptr_is_kernel(src))
return copy_struct_from_user(dst, ksize, src.user, usize);
- if (usize < ksize) {
- memset(dst + size, 0, rest);
- } else if (usize > ksize) {
- char *p = src.kernel;
-
- while (rest--) {
- if (*p++)
- return -E2BIG;
- }
- }
- memcpy(dst, src.kernel, size);
- return 0;
+ return copy_struct_from_bounce_buffer(dst, ksize, src.kernel, usize);
}
static inline int copy_to_sockptr_offset(sockptr_t dst, size_t offset,
--
2.43.0
^ permalink raw reply related
* [PATCH 3/5] uaccess: add copy_struct_{from,to}_bounce_buffer() helpers
From: Stefan Metzmacher @ 2026-04-07 16:03 UTC (permalink / raw)
To: linux-kernel
Cc: metze, Dmitry Safonov, Dmitry Safonov, Francesco Ruggeri,
Salam Noureddine, David Ahern, David S . Miller, Michal Luczaj,
David Wei, Luiz Augusto von Dentz, Luiz Augusto von Dentz,
Marcel Holtmann, Xin Long, Eric Dumazet, Kuniyuki Iwashima,
Paolo Abeni, Willem de Bruijn, Neal Cardwell, Jakub Kicinski,
Simon Horman, Aleksa Sarai, Christian Brauner, Kees Cook, netdev,
linux-bluetooth
In-Reply-To: <cover.1775576651.git.metze@samba.org>
These are similar to copy_struct_{from,to}_user() but operate
on kernel buffers instead of user buffers.
They can be used when there is a temporary bounce buffer used,
e.g. in msg_control or similar places.
It allows us to have the same logic to handle old vs. current
and current vs. new structures in the same compatible way.
copy_struct_from_sockptr() will also be able to
use copy_struct_from_bounce_buffer() for the kernel
case as follow us patch.
I'll use this in my IPPROTO_SMBDIRECT work,
but maybe it will also be useful for others...
IPPROTO_QUIC will likely also use it.
Cc: Dmitry Safonov <0x7f454c46@gmail.com>
Cc: Dmitry Safonov <dima@arista.com>
Cc: Francesco Ruggeri <fruggeri@arista.com>
Cc: Salam Noureddine <noureddine@arista.com>
Cc: David Ahern <dsahern@kernel.org>
Cc: David S. Miller <davem@davemloft.net>
Cc: Michal Luczaj <mhal@rbox.co>
Cc: David Wei <dw@davidwei.uk>
Cc: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Cc: Luiz Augusto von Dentz <luiz.dentz@gmail.com>
Cc: Marcel Holtmann <marcel@holtmann.org>
Cc: Xin Long <lucien.xin@gmail.com>
Cc: Eric Dumazet <edumazet@google.com>
Cc: Kuniyuki Iwashima <kuniyu@google.com>
Cc: Paolo Abeni <pabeni@redhat.com>
Cc: Willem de Bruijn <willemb@google.com>
Cc: Neal Cardwell <ncardwell@google.com>
Cc: Jakub Kicinski <kuba@kernel.org>
Cc: Simon Horman <horms@kernel.org>
Cc: Aleksa Sarai <cyphar@cyphar.com>
Cc: Christian Brauner <brauner@kernel.org>
CC: Kees Cook <keescook@chromium.org>
Cc: netdev@vger.kernel.org
Cc: linux-bluetooth@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Signed-off-by: Stefan Metzmacher <metze@samba.org>
---
include/linux/uaccess.h | 63 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 63 insertions(+)
diff --git a/include/linux/uaccess.h b/include/linux/uaccess.h
index 1234b5fa4761..a6cd4f48bb99 100644
--- a/include/linux/uaccess.h
+++ b/include/linux/uaccess.h
@@ -513,6 +513,69 @@ copy_struct_to_user(void __user *dst, size_t usize, const void *src,
return 0;
}
+static __always_inline void
+__copy_struct_generic_bounce_buffer(void *dst, size_t dstsize,
+ const void *src, size_t srcsize,
+ bool *ignored_trailing)
+{
+ size_t size = min(dstsize, srcsize);
+ size_t rest = max(dstsize, srcsize) - size;
+
+ /* Deal with trailing bytes. */
+ if (dstsize > srcsize)
+ memset(dst + size, 0, rest);
+ if (ignored_trailing)
+ *ignored_trailing = dstsize < srcsize &&
+ memchr_inv(src + size, 0, rest) != NULL;
+ /* Copy the interoperable parts of the struct. */
+ memcpy(dst, src, size);
+}
+
+/**
+ * This is like copy_struct_from_user(), but the
+ * src buffer was already copied into a kernel
+ * bounce buffer, so it will never return -EFAULT.
+ */
+static __always_inline __must_check int
+copy_struct_from_bounce_buffer(void *dst, size_t dstsize,
+ const void *src, size_t srcsize)
+{
+ bool ignored_trailing;
+
+ /* Double check if ksize is larger than a known object size. */
+ if (WARN_ON_ONCE(dstsize > __builtin_object_size(dst, 1)))
+ return -E2BIG;
+
+ __copy_struct_generic_bounce_buffer(dst, dstsize,
+ src, srcsize,
+ &ignored_trailing);
+ if (unlikely(ignored_trailing))
+ return -E2BIG;
+
+ return 0;
+}
+
+/**
+ * This is like copy_struct_to_user(), but the
+ * dst buffer is a kernel bounce buffer instead
+ * of a direct userspace buffer, so it will never return -EFAULT.
+ */
+static __always_inline __must_check int
+copy_struct_to_bounce_buffer(void *dst, size_t dstsize,
+ const void *src,
+ size_t srcsize,
+ bool *ignored_trailing)
+{
+ /* Double check if srcsize is larger than a known object size. */
+ if (WARN_ON_ONCE(srcsize > __builtin_object_size(src, 1)))
+ return -E2BIG;
+
+ __copy_struct_generic_bounce_buffer(dst, dstsize,
+ src, srcsize,
+ ignored_trailing);
+ return 0;
+}
+
bool copy_from_kernel_nofault_allowed(const void *unsafe_src, size_t size);
long copy_from_kernel_nofault(void *dst, const void *src, size_t size);
--
2.43.0
^ permalink raw reply related
* [PATCH 2/5] sockptr: fix usize check in copy_struct_from_sockptr() for user pointers
From: Stefan Metzmacher @ 2026-04-07 16:03 UTC (permalink / raw)
To: linux-kernel
Cc: metze, Dmitry Safonov, Dmitry Safonov, Francesco Ruggeri,
Salam Noureddine, David Ahern, David S . Miller, Michal Luczaj,
David Wei, Luiz Augusto von Dentz, Luiz Augusto von Dentz,
Marcel Holtmann, Xin Long, Eric Dumazet, Kuniyuki Iwashima,
Paolo Abeni, Willem de Bruijn, Neal Cardwell, Jakub Kicinski,
Simon Horman, Aleksa Sarai, Christian Brauner, Kees Cook, netdev,
linux-bluetooth
In-Reply-To: <cover.1775576651.git.metze@samba.org>
copy_struct_from_user will never hit the check_zeroed_user() call
and will never return -E2BIG if new userspace passed new bits in a
larger structure than the current kernel structure.
As far as I can there are no critical/related uapi changes in
- include/net/bluetooth/bluetooth.h and net/bluetooth/sco.c
after the use of copy_struct_from_sockptr in v6.13-rc3
- include/uapi/linux/tcp.h and net/ipv4/tcp_ao.c
after the use of copy_struct_from_sockptr in v6.6-rc1
So that new callers will get the correct behavior from the start.
Fixes: 4954f17ddefc ("net/tcp: Introduce TCP_AO setsockopt()s")
Fixes: ef84703a911f ("net/tcp: Add TCP-AO getsockopt()s")
Fixes: faadfaba5e01 ("net/tcp: Add TCP_AO_REPAIR")
Fixes: 3e643e4efa1e ("Bluetooth: Improve setsockopt() handling of malformed user input")
Cc: Dmitry Safonov <0x7f454c46@gmail.com>
Cc: Dmitry Safonov <dima@arista.com>
Cc: Francesco Ruggeri <fruggeri@arista.com>
Cc: Salam Noureddine <noureddine@arista.com>
Cc: David Ahern <dsahern@kernel.org>
Cc: David S. Miller <davem@davemloft.net>
Cc: Michal Luczaj <mhal@rbox.co>
Cc: David Wei <dw@davidwei.uk>
Cc: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Cc: Luiz Augusto von Dentz <luiz.dentz@gmail.com>
Cc: Marcel Holtmann <marcel@holtmann.org>
Cc: Xin Long <lucien.xin@gmail.com>
Cc: Eric Dumazet <edumazet@google.com>
Cc: Kuniyuki Iwashima <kuniyu@google.com>
Cc: Paolo Abeni <pabeni@redhat.com>
Cc: Willem de Bruijn <willemb@google.com>
Cc: Neal Cardwell <ncardwell@google.com>
Cc: Jakub Kicinski <kuba@kernel.org>
Cc: Simon Horman <horms@kernel.org>
Cc: Aleksa Sarai <cyphar@cyphar.com>
Cc: Christian Brauner <brauner@kernel.org>
CC: Kees Cook <keescook@chromium.org>
Cc: netdev@vger.kernel.org
Cc: linux-bluetooth@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Signed-off-by: Stefan Metzmacher <metze@samba.org>
---
include/linux/sockptr.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/include/linux/sockptr.h b/include/linux/sockptr.h
index 3e6c8e9d67ae..ba88f4d78c1b 100644
--- a/include/linux/sockptr.h
+++ b/include/linux/sockptr.h
@@ -91,7 +91,7 @@ static inline int copy_struct_from_sockptr(void *dst, size_t ksize,
size_t rest = max(ksize, usize) - size;
if (!sockptr_is_kernel(src))
- return copy_struct_from_user(dst, ksize, src.user, size);
+ return copy_struct_from_user(dst, ksize, src.user, usize);
if (usize < ksize) {
memset(dst + size, 0, rest);
--
2.43.0
^ permalink raw reply related
* [PATCH 1/5] uaccess: fix ignored_trailing logic in copy_struct_to_user()
From: Stefan Metzmacher @ 2026-04-07 16:03 UTC (permalink / raw)
To: linux-kernel
Cc: metze, Dmitry Safonov, Dmitry Safonov, Francesco Ruggeri,
Salam Noureddine, David Ahern, David S . Miller, Michal Luczaj,
David Wei, Luiz Augusto von Dentz, Luiz Augusto von Dentz,
Marcel Holtmann, Xin Long, Eric Dumazet, Kuniyuki Iwashima,
Paolo Abeni, Willem de Bruijn, Neal Cardwell, Jakub Kicinski,
Simon Horman, Aleksa Sarai, Christian Brauner, Kees Cook, netdev,
linux-bluetooth
In-Reply-To: <cover.1775576651.git.metze@samba.org>
Currently all callers pass ignored_trailing=NULL, but I have
code that will make use of.
Now it actually behaves like documented:
* If @usize < @ksize, then the kernel is trying to pass userspace a newer
struct than it supports. Thus we only copy the interoperable portions
(@usize) and ignore the rest (but @ignored_trailing is set to %true if
any of the trailing (@ksize - @usize) bytes are non-zero).
Fixes: 424a55a4a908 ("uaccess: add copy_struct_to_user helper")
Cc: Dmitry Safonov <0x7f454c46@gmail.com>
Cc: Dmitry Safonov <dima@arista.com>
Cc: Francesco Ruggeri <fruggeri@arista.com>
Cc: Salam Noureddine <noureddine@arista.com>
Cc: David Ahern <dsahern@kernel.org>
Cc: David S. Miller <davem@davemloft.net>
Cc: Michal Luczaj <mhal@rbox.co>
Cc: David Wei <dw@davidwei.uk>
Cc: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Cc: Luiz Augusto von Dentz <luiz.dentz@gmail.com>
Cc: Marcel Holtmann <marcel@holtmann.org>
Cc: Xin Long <lucien.xin@gmail.com>
Cc: Eric Dumazet <edumazet@google.com>
Cc: Kuniyuki Iwashima <kuniyu@google.com>
Cc: Paolo Abeni <pabeni@redhat.com>
Cc: Willem de Bruijn <willemb@google.com>
Cc: Neal Cardwell <ncardwell@google.com>
Cc: Jakub Kicinski <kuba@kernel.org>
Cc: Simon Horman <horms@kernel.org>
Cc: Aleksa Sarai <cyphar@cyphar.com>
Cc: Christian Brauner <brauner@kernel.org>
CC: Kees Cook <keescook@chromium.org>
Cc: netdev@vger.kernel.org
Cc: linux-bluetooth@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Signed-off-by: Stefan Metzmacher <metze@samba.org>
---
include/linux/uaccess.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/include/linux/uaccess.h b/include/linux/uaccess.h
index 4fe63169d5a2..1234b5fa4761 100644
--- a/include/linux/uaccess.h
+++ b/include/linux/uaccess.h
@@ -505,7 +505,7 @@ copy_struct_to_user(void __user *dst, size_t usize, const void *src,
return -EFAULT;
}
if (ignored_trailing)
- *ignored_trailing = ksize < usize &&
+ *ignored_trailing = usize < ksize &&
memchr_inv(src + size, 0, rest) != NULL;
/* Copy the interoperable parts of the struct. */
if (copy_to_user(dst, src, size))
--
2.43.0
^ permalink raw reply related
* [PATCH 0/5] uaccess/sockptr: copy_struct_ fixes and more helpers
From: Stefan Metzmacher @ 2026-04-07 16:03 UTC (permalink / raw)
To: linux-kernel
Cc: metze, Dmitry Safonov, Dmitry Safonov, Francesco Ruggeri,
Salam Noureddine, David Ahern, David S . Miller, Michal Luczaj,
David Wei, Luiz Augusto von Dentz, Luiz Augusto von Dentz,
Marcel Holtmann, Xin Long, Eric Dumazet, Kuniyuki Iwashima,
Paolo Abeni, Willem de Bruijn, Neal Cardwell, Jakub Kicinski,
Simon Horman, Aleksa Sarai, Christian Brauner, Kees Cook, netdev,
linux-bluetooth
Hi,
here are some patches related to
copy_struct_{from,to}_{user,sockptr}()
I collected during my work on an IPPROTO_SMBDIRECT
implementation wrapping the smbdirect code used
by cifs.ko and ksmbd.ko.
The first patch fixes copy_struct_to_user()
to behave like documented.
The 2nd patch fixes the case where
copy_struct_from_user() is called by
copy_struct_from_sockptr().
The 3rd patch introduces
copy_struct_{from,to}_bounce_buffer()
as a result of a discussion about the
IPPROTO_QUIC driver in order to
be future prove when handling msg_control
messages in sendmsg and recvmsg.
But I'll likely also use them in my
IPPROTO_SMBDIRECT driver.
The 4th patch makes copy_struct_from_sockptr()
a trivial wrapper switching between
copy_struct_from_user() and
copy_struct_from_bounce_buffer()
The 5th patch introduces copy_struct_to_sockptr()
which I'll also use in my IPPROTO_SMBDIRECT driver.
As future cleanup I was thinking about trick
to hide size_t ksize from the callers, similar
to what was done with kmalloc_obj(). Whould
others think that would be useful?
Stefan Metzmacher (5):
uaccess: fix ignored_trailing logic in copy_struct_to_user()
sockptr: fix usize check in copy_struct_from_sockptr() for user
pointers
uaccess: add copy_struct_{from,to}_bounce_buffer() helpers
sockptr: let copy_struct_from_sockptr() use
copy_struct_from_bounce_buffer()
sockptr: introduce copy_struct_to_sockptr()
include/linux/sockptr.h | 28 ++++++++----------
include/linux/uaccess.h | 65 ++++++++++++++++++++++++++++++++++++++++-
2 files changed, 76 insertions(+), 17 deletions(-)
--
2.43.0
^ permalink raw reply
* Re: [PATCH v3 net] af_unix: Give up GC if MSG_PEEK intervened.
From: Lee Jones @ 2026-04-07 15:58 UTC (permalink / raw)
To: Kuniyuki Iwashima, stable
Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Kuniyuki Iwashima, Linus Torvalds, netdev,
Igor Ushakov
In-Reply-To: <20260311054043.1231316-1-kuniyu@google.com>
INTENTIONAL TOP POST
I note that this was not sent to Stable, but it should be included please.
> Igor Ushakov reported that GC purged the receive queue of
> an alive socket due to a race with MSG_PEEK with a nice repro.
>
> This is the exact same issue previously fixed by commit
> cbcf01128d0a ("af_unix: fix garbage collect vs MSG_PEEK").
>
> After GC was replaced with the current algorithm, the cited
> commit removed the locking dance in unix_peek_fds() and
> reintroduced the same issue.
>
> The problem is that MSG_PEEK bumps a file refcount without
> interacting with GC.
>
> Consider an SCC containing sk-A and sk-B, where sk-A is
> close()d but can be recv()ed via sk-B.
>
> The bad thing happens if sk-A is recv()ed with MSG_PEEK from
> sk-B and sk-B is close()d while GC is checking unix_vertex_dead()
> for sk-A and sk-B.
>
> GC thread User thread
> --------- -----------
> unix_vertex_dead(sk-A)
> -> true <------.
> \
> `------ recv(sk-B, MSG_PEEK)
> invalidate !! -> sk-A's file refcount : 1 -> 2
>
> close(sk-B)
> -> sk-B's file refcount : 2 -> 1
> unix_vertex_dead(sk-B)
> -> true
>
> Initially, sk-A's file refcount is 1 by the inflight fd in sk-B
> recvq. GC thinks sk-A is dead because the file refcount is the
> same as the number of its inflight fds.
>
> However, sk-A's file refcount is bumped silently by MSG_PEEK,
> which invalidates the previous evaluation.
>
> At this moment, sk-B's file refcount is 2; one by the open fd,
> and one by the inflight fd in sk-A. The subsequent close()
> releases one refcount by the former.
>
> Finally, GC incorrectly concludes that both sk-A and sk-B are dead.
>
> One option is to restore the locking dance in unix_peek_fds(),
> but we can resolve this more elegantly thanks to the new algorithm.
>
> The point is that the issue does not occur without the subsequent
> close() and we actually do not need to synchronise MSG_PEEK with
> the dead SCC detection.
>
> When the issue occurs, close() and GC touch the same file refcount.
> If GC sees the refcount being decremented by close(), it can just
> give up garbage-collecting the SCC.
>
> Therefore, we only need to signal the race during MSG_PEEK with
> a proper memory barrier to make it visible to the GC.
>
> Let's use seqcount_t to notify GC when MSG_PEEK occurs and let
> it defer the SCC to the next run.
>
> This way no locking is needed on the MSG_PEEK side, and we can
> avoid imposing a penalty on every MSG_PEEK unnecessarily.
>
> Note that we can retry within unix_scc_dead() if MSG_PEEK is
> detected, but we do not do so to avoid hung task splat from
> abusive MSG_PEEK calls.
>
> Fixes: 118f457da9ed ("af_unix: Remove lock dance in unix_peek_fds().")
> Reported-by: Igor Ushakov <sysroot314@gmail.com>
> Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
> ---
> v3: Check !fpl and add spinlock in unix_peek_fpl()
> v2: https://lore.kernel.org/all/20260309185823.3502204-1-kuniyu@google.com/
> * Use seqcount_t for proper memory barrier
> v1: https://lore.kernel.org/netdev/20260308030406.1825938-1-kuniyu@google.com/
> ---
> net/unix/af_unix.c | 2 ++
> net/unix/af_unix.h | 1 +
> net/unix/garbage.c | 79 ++++++++++++++++++++++++++++++----------------
> 3 files changed, 54 insertions(+), 28 deletions(-)
>
> diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
> index 7eaa5b187fef..b23c33df8b46 100644
> --- a/net/unix/af_unix.c
> +++ b/net/unix/af_unix.c
> @@ -1958,6 +1958,8 @@ static void unix_detach_fds(struct scm_cookie *scm, struct sk_buff *skb)
> static void unix_peek_fds(struct scm_cookie *scm, struct sk_buff *skb)
> {
> scm->fp = scm_fp_dup(UNIXCB(skb).fp);
> +
> + unix_peek_fpl(scm->fp);
> }
>
> static void unix_destruct_scm(struct sk_buff *skb)
> diff --git a/net/unix/af_unix.h b/net/unix/af_unix.h
> index c4f1b2da363d..8119dbeef3a3 100644
> --- a/net/unix/af_unix.h
> +++ b/net/unix/af_unix.h
> @@ -29,6 +29,7 @@ void unix_del_edges(struct scm_fp_list *fpl);
> void unix_update_edges(struct unix_sock *receiver);
> int unix_prepare_fpl(struct scm_fp_list *fpl);
> void unix_destroy_fpl(struct scm_fp_list *fpl);
> +void unix_peek_fpl(struct scm_fp_list *fpl);
> void unix_schedule_gc(struct user_struct *user);
>
> /* SOCK_DIAG */
> diff --git a/net/unix/garbage.c b/net/unix/garbage.c
> index 816e8fa2b062..a7967a345827 100644
> --- a/net/unix/garbage.c
> +++ b/net/unix/garbage.c
> @@ -318,6 +318,25 @@ void unix_destroy_fpl(struct scm_fp_list *fpl)
> unix_free_vertices(fpl);
> }
>
> +static bool gc_in_progress;
> +static seqcount_t unix_peek_seq = SEQCNT_ZERO(unix_peek_seq);
> +
> +void unix_peek_fpl(struct scm_fp_list *fpl)
> +{
> + static DEFINE_SPINLOCK(unix_peek_lock);
> +
> + if (!fpl || !fpl->count_unix)
> + return;
> +
> + if (!READ_ONCE(gc_in_progress))
> + return;
> +
> + /* Invalidate the final refcnt check in unix_vertex_dead(). */
> + spin_lock(&unix_peek_lock);
> + raw_write_seqcount_barrier(&unix_peek_seq);
> + spin_unlock(&unix_peek_lock);
> +}
> +
> static bool unix_vertex_dead(struct unix_vertex *vertex)
> {
> struct unix_edge *edge;
> @@ -351,6 +370,36 @@ static bool unix_vertex_dead(struct unix_vertex *vertex)
> return true;
> }
>
> +static LIST_HEAD(unix_visited_vertices);
> +static unsigned long unix_vertex_grouped_index = UNIX_VERTEX_INDEX_MARK2;
> +
> +static bool unix_scc_dead(struct list_head *scc, bool fast)
> +{
> + struct unix_vertex *vertex;
> + bool scc_dead = true;
> + unsigned int seq;
> +
> + seq = read_seqcount_begin(&unix_peek_seq);
> +
> + list_for_each_entry_reverse(vertex, scc, scc_entry) {
> + /* Don't restart DFS from this vertex. */
> + list_move_tail(&vertex->entry, &unix_visited_vertices);
> +
> + /* Mark vertex as off-stack for __unix_walk_scc(). */
> + if (!fast)
> + vertex->index = unix_vertex_grouped_index;
> +
> + if (scc_dead)
> + scc_dead = unix_vertex_dead(vertex);
> + }
> +
> + /* If MSG_PEEK intervened, defer this SCC to the next round. */
> + if (read_seqcount_retry(&unix_peek_seq, seq))
> + return false;
> +
> + return scc_dead;
> +}
> +
> static void unix_collect_skb(struct list_head *scc, struct sk_buff_head *hitlist)
> {
> struct unix_vertex *vertex;
> @@ -404,9 +453,6 @@ static bool unix_scc_cyclic(struct list_head *scc)
> return false;
> }
>
> -static LIST_HEAD(unix_visited_vertices);
> -static unsigned long unix_vertex_grouped_index = UNIX_VERTEX_INDEX_MARK2;
> -
> static unsigned long __unix_walk_scc(struct unix_vertex *vertex,
> unsigned long *last_index,
> struct sk_buff_head *hitlist)
> @@ -474,9 +520,7 @@ static unsigned long __unix_walk_scc(struct unix_vertex *vertex,
> }
>
> if (vertex->index == vertex->scc_index) {
> - struct unix_vertex *v;
> struct list_head scc;
> - bool scc_dead = true;
>
> /* SCC finalised.
> *
> @@ -485,18 +529,7 @@ static unsigned long __unix_walk_scc(struct unix_vertex *vertex,
> */
> __list_cut_position(&scc, &vertex_stack, &vertex->scc_entry);
>
> - list_for_each_entry_reverse(v, &scc, scc_entry) {
> - /* Don't restart DFS from this vertex in unix_walk_scc(). */
> - list_move_tail(&v->entry, &unix_visited_vertices);
> -
> - /* Mark vertex as off-stack. */
> - v->index = unix_vertex_grouped_index;
> -
> - if (scc_dead)
> - scc_dead = unix_vertex_dead(v);
> - }
> -
> - if (scc_dead) {
> + if (unix_scc_dead(&scc, false)) {
> unix_collect_skb(&scc, hitlist);
> } else {
> if (unix_vertex_max_scc_index < vertex->scc_index)
> @@ -550,19 +583,11 @@ static void unix_walk_scc_fast(struct sk_buff_head *hitlist)
> while (!list_empty(&unix_unvisited_vertices)) {
> struct unix_vertex *vertex;
> struct list_head scc;
> - bool scc_dead = true;
>
> vertex = list_first_entry(&unix_unvisited_vertices, typeof(*vertex), entry);
> list_add(&scc, &vertex->scc_entry);
>
> - list_for_each_entry_reverse(vertex, &scc, scc_entry) {
> - list_move_tail(&vertex->entry, &unix_visited_vertices);
> -
> - if (scc_dead)
> - scc_dead = unix_vertex_dead(vertex);
> - }
> -
> - if (scc_dead) {
> + if (unix_scc_dead(&scc, true)) {
> cyclic_sccs--;
> unix_collect_skb(&scc, hitlist);
> }
> @@ -577,8 +602,6 @@ static void unix_walk_scc_fast(struct sk_buff_head *hitlist)
> cyclic_sccs ? UNIX_GRAPH_CYCLIC : UNIX_GRAPH_NOT_CYCLIC);
> }
>
> -static bool gc_in_progress;
> -
> static void unix_gc(struct work_struct *work)
> {
> struct sk_buff_head hitlist;
> --
> 2.53.0.473.g4a7958ca14-goog
>
--
Lee Jones [李琼斯]
^ permalink raw reply
* Re: [PATCH net-next v3 1/2] dt-bindings: net: document Microchip PIC64-HPSC/HX MDIO controller
From: Conor Dooley @ 2026-04-07 15:57 UTC (permalink / raw)
To: Charles Perry
Cc: netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, devicetree, linux-kernel
In-Reply-To: <20260331123858.1912449-2-charles.perry@microchip.com>
[-- Attachment #1: Type: text/plain, Size: 52 bytes --]
Acked-by: Conor Dooley <conor.dooley@microchip.com>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [PATCH net-next v9 1/4] net: stmmac: Add DW25GMAC support in stmmac core driver
From: Russell King (Oracle) @ 2026-04-07 15:42 UTC (permalink / raw)
To: Jitendra Vegiraju
Cc: netdev, alexandre.torgue, davem, edumazet, kuba, pabeni,
mcoquelin.stm32, bcm-kernel-feedback-list, richardcochran, ast,
daniel, hawk, john.fastabend, rohan.g.thomas, linux-kernel,
linux-stm32, linux-arm-kernel, bpf, andrew+netdev, horms, sdf, me,
siyanteng, prabhakar.mahadev-lad.rj, weishangjuan, wens,
vladimir.oltean, lizhi2, boon.khai.ng, maxime.chevallier,
chenchuangyu, yangtiezhu, ovidiu.panait.rb, chenhuacai,
florian.fainelli, quic_abchauha
In-Reply-To: <adUQHHBD0d3p1OSI@shell.armlinux.org.uk>
On Tue, Apr 07, 2026 at 03:09:32PM +0100, Russell King (Oracle) wrote:
> Not withstanding my comment about the other Synopsys xlgmac driver that
> we have in the kernel...
>
> On Thu, Apr 02, 2026 at 02:36:26PM -0700, Jitendra Vegiraju wrote:
> > From: Jitendra Vegiraju <jitendra.vegiraju@broadcom.com>
> >
> > The DW25GMAC introduced a new DMA architecture called Hyper-DMA (HDMA) for
> > virtualization scalability. This is realized by decoupling physical DMA
> > channels(PDMA) from potentially large number of virtual DMA channels(VDMA).
> > The VDMAs provide software abstraction to driver that map to PDMAs for
> > frame transmission and reception.
> > Since 25GMAC is a derivative of XGMAC, majority of IP is common to both.
> >
> > To add support for the HDMA in 25GMAC, a new instance of dma_ops,
> > dw25gmac400_dma_ops is introduced.
> > To support the current needs, a simple one-to-one mapping of dw25gmac's
> > logical VDMA (channel) to TC to PDMAs is used. Most of the other dma
> > operation functions in existing dwxgamc2_dma.c file are reused whereever
>
> Typo: dwxgmac2_dma.c
>
> > applicable.
> > Added setup function for DW25GMAC's stmmac_hwif_entry in stmmac core.
>
> In a previous review, I questioned the use of DWMAC_CORE_25GMAC and
> asked about its version numberspace. I believe you indicated that the
> version numberspace is the same as the existing XGMAC core.
>
> I'm going to question the value of adding DWMAC_CORE_25GMAC.
>
> 1. What is the value of splitting DWMAC_CORE_25GMAC from
> DWMAC_CORE_XGMAC given that it's in the same versioning numberspace
> as XGMAC, and most tests (via dwmac_is_xgmac()) test for XGMAC or
> 25GMAC?
>
> 2. Have you reviewed all the places that explicitly test for
> DWMAC_CORE_XGMAC, looking at their "false" paths (for non-XGMAC
> cores) to determine whether they are suitable? For example:
>
> if (priv->plat->core_type == DWMAC_CORE_XGMAC)
> ndev->max_mtu = XGMAC_JUMBO_LEN;
> else if (priv->plat->enh_desc || priv->synopsys_id >= DWMAC_CORE_4_00)
> ndev->max_mtu = JUMBO_LEN;
> else
> ndev->max_mtu = SKB_MAX_HEAD(NET_SKB_PAD + NET_IP_ALIGN);
>
> XGMAC can handle a max MTU of 16368, but with your code using
> DWMAC_CORE_25GMAC, we fall back to the next test, which tests the
> IP version against 0x40, and uses a max MTU of 9000. Given that
> DWMAC_CORE_4_00 is a different "version number space" this seems
> wrong.
This is actually wrong - for DWMAC_CORE_25GMAC with a synopsys_id
less than 0x40, this will fall back to SKB_MAX_HEAD(NET_SKB_PAD +
NET_IP_ALIGN) which is as good as "pluck a number out of the air
and watch the driver fall over if you increase the MTU to maximum".
max_mtu ends up being dependent on the system page size, not on any
hardware limitation, which is garbage.
> 3. Looking at the MDIO code, this looks very wrong if you're
> introducing DWMAC_CORE_25GMAC. Have you tested MDIO accesses?
>
> dwxgmac2_setup() is called for DWMAC_CORE_XGMAC core-type. In
> stmmac_mdio_register(), DWMAC_CORE_XGMAC uses different functions
> for MDIO bus access for C22 and C45 from other cores - it uses the
> stmmac_xgmac2_mdio_* functions.
>
> These use stmmac_xgmac2_c45_format() and stmmac_xgmac2_c22_format()
> to format the register values which do not depend on mii.*_mask, but
> do use mii.address and mii.data for the register offsets. Thus, is
> there any point to setting mii.addr_mask and mii.reg_mask ?
>
> For non-DWMAC_CORE_XGMAC cores, we fall back to the stmmac_mdio_*()
> functions, which for non-DWMAC_CORE_GMAC4 will only support Clause
> 22 access, not Clause 45 - which would be very strange for a 25G
> core.
>
> 4. What about the feature printing in
> stmmac_main.c::stmmac_dma_cap_show() ?
>
> 5. What about similar tests in stmmac_est.c and stmmac_ethtool.c ?
Another issue that needs to be looked into is all the tests that
check priv->synopsys_id without checking the core_type. I'm already
concerned that many of these are wrong.
I have some patches now that rename synopsys_id to snpsver (it's really
the Synopsys IP version field which is BCD of the major version and
first digit of the minor version, not some random ID that identifies
the core.)
From what I've ascertained so far:
GMAC100 cores do not have a readable snpsver number, thus this
will be zero.
GMAC cores generally have a snpsver number less than 0x40.
GMAC4 cores may have a version number that overlaps GMAC cores
(see first entry for DWMAC_CORE_GMAC4).
XGMAC and XLGMAC cores each have an entirely separate IP version
number space from GMAC and GMAC4, which are distinguished by their
respective userver.
For example:
/* Only DWMAC core version 5.20 onwards supports HW descriptor prefetch.
*/
if (priv->snpsver < DWMAC_CORE_5_20)
priv->plat->dma_cfg->dche = false;
This will match your 25GMAC cores and the XGMAC cores because they have
a Synopsys IP version number less than 0x52. What saves us there is
that dche is only read by dwmac4_dma_init(), used by dwmac4_dma_ops and
dwmac410_dma_ops. These are all used by hwif entries that require
priv->plat->core_type to be DWMAC_CORE_GMAC4. Thus, I'm changing this
one to:
/* Only DWMAC4 core version 5.20 onwards support HW descriptor prefetch.
*/
if (priv->plat->core_type != DWMAC_CORE_GMAC4 ||
priv->snpsver < DWMAC_CORE_5_20)
priv->plat->dma_cfg->dche = false;
although I'm wondering if that should really be == && to avoid writing
to it for non-GMAC4 cores (it really doesn't matter.)
Then there this:
case HWTSTAMP_FILTER_PTP_V2_EVENT:
/* PTP v2/802.AS1 any layer, any kind of event packet */
config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
ptp_v2 = PTP_TCR_TSVER2ENA;
snap_type_sel = PTP_TCR_SNAPTYPSEL_1;
if (priv->snpsver < DWMAC_CORE_4_10)
ts_event_en = PTP_TCR_TSEVNTENA;
Is PTP_TCR_TSEVNTENA needed for this PTP filter type for XGMAC and
25GMAC cores - because they'll get it because they have snpsver
smaller than 0x41.
Similar here:
if (priv->plat->rx_coe) {
priv->hw->rx_csum = priv->plat->rx_coe;
dev_info(priv->device, "RX Checksum Offload Engine supported\n")+;
if (priv->snpsver < DWMAC_CORE_4_00)
dev_info(priv->device, "COE Type %d\n", priv->hw->rx_csu+m);
}
In stmmac_ethtool.c, stmmac_mac_debug() won't be called for XGMAC or
25GMAC cores, because:
if (priv->snpsver >= DWMAC_CORE_3_50)
stmmac_mac_debug(priv, priv->ioaddr,
(void *)&priv->xstats,
rx_queues_count, tx_queues_count);
snpsver (synopsys_id) will be smaller than 0x35. Is this correct?
The good news is that stmmac_mdio.c at gates all its checks on the
Synopsys IP version against the core type.
I'll post my RFC patches tidying up some of the version mess in the
next day or so.
--
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!
^ permalink raw reply
* Re: [PATCH net-next 0/3] psp: add crypt-offset and spi-threshold attributes
From: Akhilesh Samineni @ 2026-04-07 15:39 UTC (permalink / raw)
To: Jakub Kicinski
Cc: davem, edumazet, pabeni, andrew+netdev, horms, willemb,
daniel.zahka, netdev, linux-kernel, jayakrishnan.udayavarma,
ajit.khaparde, kiran.kella, sachin.suman
In-Reply-To: <20260406181445.1fb8308e@kernel.org>
[-- Attachment #1: Type: text/plain, Size: 577 bytes --]
On Tue, Apr 7, 2026 at 6:44 AM Jakub Kicinski <kuba@kernel.org> wrote:
>
> On Mon, 6 Apr 2026 15:23:02 -0700 Akhilesh Samineni wrote:
> > This series introduces two new configurable per-device attributes to
> > the PSP (PSP Security Protocol) subsystem: crypt-offset and spi-threshold.
>
> Please read this document:
> https://www.kernel.org/doc/html/next/process/maintainer-netdev.html
> --
Thank you for the link. I have reviewed the netdev process documentation.
I’ve ensured my patches pass checkpatch and include the correct maintainers.
> pw-bot: cr
[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 4211 bytes --]
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox