* [PATCH net-next V6 4/4] devlink: Apply eswitch mode boot defaults
From: Mark Bloch @ 2026-07-14 6:17 UTC (permalink / raw)
To: Jiri Pirko, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman
Cc: Saeed Mahameed, Leon Romanovsky, Tariq Toukan, Andrew Lunn,
Jonathan Corbet, Shuah Khan, netdev, linux-rdma, linux-doc,
Mark Bloch
In-Reply-To: <20260714061731.531849-1-mbloch@nvidia.com>
Apply parsed devlink_eswitch_mode= defaults after devlink registration
and after successful reload.
Mark the default mode as pending when a devlink instance is allocated.
Before devl_unlock() releases the instance lock, apply a pending default
when the instance is registered.
Clear the pending state before calling into the driver so the boot
default remains a one-shot operation even if the mode change fails.
For successful reloads that performed DRIVER_REINIT, devlink_reload()
already holds the devlink instance lock and the driver has completed
reload_up(). Clear the pending state and apply the default directly from
the reload path.
Treat an explicit user eswitch mode request as consuming the pending
default mode.
Signed-off-by: Mark Bloch <mbloch@nvidia.com>
---
net/devlink/core.c | 3 ++
net/devlink/default.c | 70 +++++++++++++++++++++++++++++++++++--
net/devlink/dev.c | 6 ++++
net/devlink/devl_internal.h | 5 +++
4 files changed, 82 insertions(+), 2 deletions(-)
diff --git a/net/devlink/core.c b/net/devlink/core.c
index fc14ee5d9dcf..cea4afc27dd9 100644
--- a/net/devlink/core.c
+++ b/net/devlink/core.c
@@ -317,6 +317,7 @@ EXPORT_SYMBOL_GPL(devl_trylock);
void devl_unlock(struct devlink *devlink)
{
+ devlink_default_esw_mode_apply_pending(devlink);
mutex_unlock(&devlink->lock);
}
EXPORT_SYMBOL_GPL(devl_unlock);
@@ -429,6 +430,7 @@ void devl_unregister(struct devlink *devlink)
ASSERT_DEVLINK_REGISTERED(devlink);
devl_assert_locked(devlink);
+ devlink_default_esw_mode_apply_pending_clear(devlink);
devlink_notify_unregister(devlink);
xa_clear_mark(&devlinks, devlink->index, DEVLINK_REGISTERED);
devlink_rel_put(devlink);
@@ -490,6 +492,7 @@ struct devlink *__devlink_alloc(const struct devlink_ops *ops, size_t priv_size,
INIT_LIST_HEAD(&devlink->trap_group_list);
INIT_LIST_HEAD(&devlink->trap_policer_list);
INIT_RCU_WORK(&devlink->rwork, devlink_release);
+ devlink_default_esw_mode_instance_init(devlink);
lockdep_register_key(&devlink->lock_key);
mutex_init(&devlink->lock);
lockdep_set_class(&devlink->lock, &devlink->lock_key);
diff --git a/net/devlink/default.c b/net/devlink/default.c
index 8434af83ea69..77cc356dfac9 100644
--- a/net/devlink/default.c
+++ b/net/devlink/default.c
@@ -10,6 +10,7 @@
static char *devlink_default_esw_mode_param;
static bool devlink_default_esw_mode_match_all;
+static bool devlink_default_esw_mode_enabled;
static enum devlink_eswitch_mode devlink_default_esw_mode;
static LIST_HEAD(devlink_default_esw_mode_nodes);
@@ -154,6 +155,7 @@ static void __init devlink_default_esw_mode_nodes_clear(void)
}
devlink_default_esw_mode_match_all = false;
+ devlink_default_esw_mode_enabled = false;
}
static int __init devlink_default_esw_mode_parse(char *str)
@@ -180,14 +182,78 @@ static int __init devlink_default_esw_mode_parse(char *str)
return err;
err = devlink_default_esw_mode_handles_parse(handles);
- if (err)
+ if (err) {
devlink_default_esw_mode_nodes_clear();
- else
+ } else {
devlink_default_esw_mode = esw_mode;
+ devlink_default_esw_mode_enabled = true;
+ }
return err;
}
+static bool devlink_default_esw_mode_match(struct devlink *devlink)
+{
+ const char *bus_name = devlink_bus_name(devlink);
+ const char *dev_name = devlink_dev_name(devlink);
+ struct devlink_default_esw_mode_node *node;
+
+ if (devlink_default_esw_mode_match_all)
+ return true;
+
+ node = devlink_default_esw_mode_node_find(bus_name, dev_name);
+ return !!node;
+}
+
+void devlink_default_esw_mode_apply_locked(struct devlink *devlink)
+{
+ const struct devlink_ops *ops = devlink->ops;
+ int err;
+
+ devl_assert_locked(devlink);
+
+ if (!devlink_default_esw_mode_match(devlink))
+ return;
+
+ if (!ops->eswitch_mode_set) {
+ if (!devlink_default_esw_mode_match_all)
+ devl_warn(devlink,
+ "devlink_eswitch_mode= selected this device but eswitch mode setting is not supported\n");
+ return;
+ }
+
+ err = devlink_eswitch_mode_set(devlink, devlink_default_esw_mode, NULL);
+ if (err)
+ devl_warn(devlink,
+ "Couldn't apply default eswitch mode, err %d\n",
+ err);
+}
+
+void devlink_default_esw_mode_apply_pending(struct devlink *devlink)
+{
+ devl_assert_locked(devlink);
+
+ if (!devlink->default_esw_mode_apply_pending ||
+ !__devl_is_registered(devlink))
+ return;
+
+ devlink->default_esw_mode_apply_pending = false;
+ devlink_default_esw_mode_apply_locked(devlink);
+}
+
+void devlink_default_esw_mode_instance_init(struct devlink *devlink)
+{
+ devlink->default_esw_mode_apply_pending =
+ devlink_default_esw_mode_enabled;
+}
+
+void devlink_default_esw_mode_apply_pending_clear(struct devlink *devlink)
+{
+ devl_assert_locked(devlink);
+
+ devlink->default_esw_mode_apply_pending = false;
+}
+
static int __init devlink_default_esw_mode_setup(char *str)
{
devlink_default_esw_mode_param = str;
diff --git a/net/devlink/dev.c b/net/devlink/dev.c
index 119ef105d0a7..611bb6bfd492 100644
--- a/net/devlink/dev.c
+++ b/net/devlink/dev.c
@@ -478,6 +478,11 @@ int devlink_reload(struct devlink *devlink, struct net *dest_net,
return err;
WARN_ON(!(*actions_performed & BIT(action)));
+ if (*actions_performed & BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT)) {
+ devlink_default_esw_mode_apply_pending_clear(devlink);
+ devlink_default_esw_mode_apply_locked(devlink);
+ }
+
/* Catch driver on updating the remote action within devlink reload */
WARN_ON(memcmp(remote_reload_stats, devlink->stats.remote_reload_stats,
sizeof(remote_reload_stats)));
@@ -731,6 +736,7 @@ int devlink_nl_eswitch_set_doit(struct sk_buff *skb, struct genl_info *info)
u16 mode;
if (info->attrs[DEVLINK_ATTR_ESWITCH_MODE]) {
+ devlink_default_esw_mode_apply_pending_clear(devlink);
mode = nla_get_u16(info->attrs[DEVLINK_ATTR_ESWITCH_MODE]);
err = devlink_eswitch_mode_set(devlink, mode, info->extack);
if (err)
diff --git a/net/devlink/devl_internal.h b/net/devlink/devl_internal.h
index fe9ad58515d4..97f53394b1c0 100644
--- a/net/devlink/devl_internal.h
+++ b/net/devlink/devl_internal.h
@@ -58,6 +58,7 @@ struct devlink {
struct mutex lock;
struct lock_class_key lock_key;
u8 reload_failed:1;
+ u8 default_esw_mode_apply_pending:1;
refcount_t refcount;
struct rcu_work rwork;
struct devlink_rel *rel;
@@ -73,6 +74,10 @@ struct devlink *__devlink_alloc(const struct devlink_ops *ops, size_t priv_size,
const struct device_driver *dev_driver);
int devlink_default_esw_mode_init(void);
void devlink_default_esw_mode_cleanup(void);
+void devlink_default_esw_mode_instance_init(struct devlink *devlink);
+void devlink_default_esw_mode_apply_locked(struct devlink *devlink);
+void devlink_default_esw_mode_apply_pending(struct devlink *devlink);
+void devlink_default_esw_mode_apply_pending_clear(struct devlink *devlink);
#define devl_warn(devlink, format, args...) \
do { \
--
2.43.0
^ permalink raw reply related
* [PATCH net-next V6 3/4] devlink: Parse eswitch mode boot defaults
From: Mark Bloch @ 2026-07-14 6:17 UTC (permalink / raw)
To: Jiri Pirko, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman
Cc: Saeed Mahameed, Leon Romanovsky, Tariq Toukan, Andrew Lunn,
Jonathan Corbet, Shuah Khan, netdev, linux-rdma, linux-doc,
Mark Bloch
In-Reply-To: <20260714061731.531849-1-mbloch@nvidia.com>
Add devlink_eswitch_mode= kernel command line parsing for a default
eswitch mode.
The supported syntax selects either all devlink handles or one explicit
comma-separated handle list:
devlink_eswitch_mode=*=<mode>
devlink_eswitch_mode=<handle>[,<handle>...]=<mode>
where <mode> is one of legacy, switchdev or switchdev_inactive. All
selected handles receive the same mode. Assigning different modes to
different handle lists in the same parameter value is not supported.
Store the parsed selector and mode in devlink core so the default can be
applied by a downstream patch.
Document the devlink_eswitch_mode= syntax and duplicate handle handling.
Signed-off-by: Mark Bloch <mbloch@nvidia.com>
---
.../admin-guide/kernel-parameters.txt | 25 ++
.../networking/devlink/devlink-defaults.rst | 78 ++++++
Documentation/networking/devlink/index.rst | 1 +
net/devlink/Makefile | 2 +-
net/devlink/core.c | 7 +
net/devlink/default.c | 237 ++++++++++++++++++
net/devlink/devl_internal.h | 2 +
7 files changed, 351 insertions(+), 1 deletion(-)
create mode 100644 Documentation/networking/devlink/devlink-defaults.rst
create mode 100644 net/devlink/default.c
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index b5493a7f8f22..117300dd589c 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -1249,6 +1249,31 @@ Kernel parameters
dell_smm_hwmon.fan_max=
[HW] Maximum configurable fan speed.
+ devlink_eswitch_mode=
+ [NET]
+ Format:
+ <selector>=<mode>
+
+ <selector>:
+ * | <handle>[,<handle>...]
+
+ <handle>:
+ <bus-name>/<dev-name>
+
+ Configure default devlink eswitch mode for matching
+ devlink instances during device initialization.
+
+ <mode>:
+ legacy | switchdev | switchdev_inactive
+
+ Examples:
+ devlink_eswitch_mode=*=switchdev
+ devlink_eswitch_mode=pci/0000:08:00.0=switchdev
+ devlink_eswitch_mode=pci/0000:08:00.0,pci/0000:09:00.1=switchdev_inactive
+
+ See Documentation/networking/devlink/devlink-defaults.rst
+ for the full syntax.
+
dfltcc= [HW,S390]
Format: { on | off | def_only | inf_only | always }
on: s390 zlib hardware support for compression on
diff --git a/Documentation/networking/devlink/devlink-defaults.rst b/Documentation/networking/devlink/devlink-defaults.rst
new file mode 100644
index 000000000000..380c9e99210e
--- /dev/null
+++ b/Documentation/networking/devlink/devlink-defaults.rst
@@ -0,0 +1,78 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==============================
+Devlink Eswitch Mode Defaults
+==============================
+
+Devlink eswitch mode defaults allow the eswitch mode to be provided on the
+kernel command line and applied to matching devlink instances during device
+initialization.
+
+The devlink device is selected by its devlink handle. For PCI devices this is
+the same handle shown by ``devlink dev show``, for example
+``pci/0000:08:00.0``.
+
+Kernel command line syntax
+==========================
+
+Defaults are specified with the ``devlink_eswitch_mode=`` kernel command line
+parameter.
+
+The general syntax is::
+
+ devlink_eswitch_mode=<selector>=<mode>
+
+``<selector>`` is either ``*`` or one or more devlink handles::
+
+ * | <bus-name>/<dev-name>[,<bus-name>/<dev-name>...]
+
+``*`` applies the mode to every devlink instance. All handles in the same
+selector receive the same eswitch mode.
+
+``<mode>`` is one of ``legacy``, ``switchdev`` or ``switchdev_inactive``.
+
+Syntax rules
+------------
+
+The following syntax rules apply:
+
+* Specify the default in one ``devlink_eswitch_mode=`` parameter. Repeated
+ ``devlink_eswitch_mode=`` parameters are not accumulated.
+* The ``devlink_eswitch_mode=`` value is limited by the kernel command line
+ size.
+* Whitespace is not allowed within the parameter value.
+* ``<selector>`` must be either ``*`` or a handle list. ``*`` cannot be
+ combined with explicit handles.
+* ``<bus-name>`` and ``<dev-name>`` must not be empty.
+* ``<dev-name>`` may contain ``:``. This allows PCI names such as
+ ``0000:08:00.0``.
+* Handles must not contain whitespace, ``*``, ``=`` or more than one ``/``.
+* A comma separates handles.
+* Comma-separated default assignments are not supported.
+* Duplicate handles are rejected and the devlink eswitch mode default is
+ ignored.
+
+The eswitch mode default corresponds to the userspace command::
+
+ devlink dev eswitch set <handle> mode <value>
+
+
+Examples
+========
+
+Set all devlink instances to switchdev mode::
+
+ devlink_eswitch_mode=*=switchdev
+
+Set one PCI devlink instance to switchdev mode::
+
+ devlink_eswitch_mode=pci/0000:08:00.0=switchdev
+
+Set two PCI devlink instances to switchdev inactive mode::
+
+ devlink_eswitch_mode=pci/0000:08:00.0,pci/0000:09:00.1=switchdev_inactive
+
+The following is invalid because comma-separated default assignments are not
+supported::
+
+ devlink_eswitch_mode=pci/0000:08:00.0=switchdev,pci/0000:09:00.0=switchdev_inactive
diff --git a/Documentation/networking/devlink/index.rst b/Documentation/networking/devlink/index.rst
index 4745148fecf4..134d2f319922 100644
--- a/Documentation/networking/devlink/index.rst
+++ b/Documentation/networking/devlink/index.rst
@@ -56,6 +56,7 @@ general.
:maxdepth: 1
devlink-dpipe
+ devlink-defaults
devlink-eswitch-attr
devlink-flash
devlink-health
diff --git a/net/devlink/Makefile b/net/devlink/Makefile
index 8f2adb5e5836..99ca0ef7cf1e 100644
--- a/net/devlink/Makefile
+++ b/net/devlink/Makefile
@@ -1,4 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
-obj-y := core.o netlink.o netlink_gen.o dev.o port.o sb.o dpipe.o \
+obj-y := core.o netlink.o netlink_gen.o dev.o default.o port.o sb.o dpipe.o \
resource.o param.o region.o health.o trap.o rate.o linecard.o sh_dev.o
diff --git a/net/devlink/core.c b/net/devlink/core.c
index c53a42e17a58..fc14ee5d9dcf 100644
--- a/net/devlink/core.c
+++ b/net/devlink/core.c
@@ -598,6 +598,10 @@ static int __init devlink_init(void)
{
int err;
+ err = devlink_default_esw_mode_init();
+ if (err)
+ goto out;
+
err = register_pernet_subsys(&devlink_pernet_ops);
if (err)
goto out;
@@ -613,7 +617,10 @@ static int __init devlink_init(void)
out_unreg_pernet_subsys:
unregister_pernet_subsys(&devlink_pernet_ops);
out:
+ if (err)
+ devlink_default_esw_mode_cleanup();
WARN_ON(err);
+
return err;
}
diff --git a/net/devlink/default.c b/net/devlink/default.c
new file mode 100644
index 000000000000..8434af83ea69
--- /dev/null
+++ b/net/devlink/default.c
@@ -0,0 +1,237 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include "devl_internal.h"
+
+static char *devlink_default_esw_mode_param;
+static bool devlink_default_esw_mode_match_all;
+static enum devlink_eswitch_mode devlink_default_esw_mode;
+static LIST_HEAD(devlink_default_esw_mode_nodes);
+
+struct devlink_default_esw_mode_node {
+ struct list_head list;
+ char *bus_name;
+ char *dev_name;
+};
+
+static int __init
+devlink_default_esw_mode_to_value(const char *str,
+ enum devlink_eswitch_mode *mode)
+{
+ if (!strcmp(str, "legacy")) {
+ *mode = DEVLINK_ESWITCH_MODE_LEGACY;
+ return 0;
+ }
+ if (!strcmp(str, "switchdev")) {
+ *mode = DEVLINK_ESWITCH_MODE_SWITCHDEV;
+ return 0;
+ }
+ if (!strcmp(str, "switchdev_inactive")) {
+ *mode = DEVLINK_ESWITCH_MODE_SWITCHDEV_INACTIVE;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int __init
+devlink_default_esw_mode_handle_parse(char *handle, char **bus_name,
+ char **dev_name)
+{
+ char *slash;
+ char *p;
+
+ if (!*handle)
+ return -EINVAL;
+
+ for (p = handle; *p; p++) {
+ if (*p == '*' || *p == '=')
+ return -EINVAL;
+ }
+
+ slash = strchr(handle, '/');
+ if (!slash || slash == handle || !slash[1])
+ return -EINVAL;
+ if (strchr(slash + 1, '/'))
+ return -EINVAL;
+
+ *slash = '\0';
+
+ *bus_name = handle;
+ *dev_name = slash + 1;
+ return 0;
+}
+
+static struct devlink_default_esw_mode_node *
+devlink_default_esw_mode_node_find(const char *bus_name, const char *dev_name)
+{
+ struct devlink_default_esw_mode_node *node;
+
+ list_for_each_entry(node, &devlink_default_esw_mode_nodes, list) {
+ if (!strcmp(node->bus_name, bus_name) &&
+ !strcmp(node->dev_name, dev_name))
+ return node;
+ }
+
+ return NULL;
+}
+
+static int __init
+devlink_default_esw_mode_node_add(const char *bus_name, const char *dev_name)
+{
+ struct devlink_default_esw_mode_node *node;
+
+ if (devlink_default_esw_mode_node_find(bus_name, dev_name))
+ return -EEXIST;
+
+ node = kzalloc_obj(*node);
+ if (!node)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&node->list);
+ node->bus_name = kstrdup(bus_name, GFP_KERNEL);
+ node->dev_name = kstrdup(dev_name, GFP_KERNEL);
+ if (!node->bus_name || !node->dev_name) {
+ kfree(node->bus_name);
+ kfree(node->dev_name);
+ kfree(node);
+ return -ENOMEM;
+ }
+
+ list_add_tail(&node->list, &devlink_default_esw_mode_nodes);
+ return 0;
+}
+
+static int __init devlink_default_esw_mode_handles_parse(char *handles)
+{
+ char *handle;
+ int err;
+
+ if (!strcmp(handles, "*")) {
+ devlink_default_esw_mode_match_all = true;
+ return 0;
+ }
+
+ while ((handle = strsep(&handles, ",")) != NULL) {
+ char *bus_name;
+ char *dev_name;
+
+ err = devlink_default_esw_mode_handle_parse(handle, &bus_name,
+ &dev_name);
+ if (err)
+ return err;
+
+ err = devlink_default_esw_mode_node_add(bus_name, dev_name);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static void __init
+devlink_default_esw_mode_node_free(struct devlink_default_esw_mode_node *node)
+{
+ kfree(node->bus_name);
+ kfree(node->dev_name);
+ kfree(node);
+}
+
+static void __init devlink_default_esw_mode_nodes_clear(void)
+{
+ struct devlink_default_esw_mode_node *node_tmp;
+ struct devlink_default_esw_mode_node *node;
+
+ list_for_each_entry_safe(node, node_tmp,
+ &devlink_default_esw_mode_nodes, list) {
+ list_del(&node->list);
+ devlink_default_esw_mode_node_free(node);
+ }
+
+ devlink_default_esw_mode_match_all = false;
+}
+
+static int __init devlink_default_esw_mode_parse(char *str)
+{
+ enum devlink_eswitch_mode esw_mode;
+ char *separator;
+ char *handles;
+ char *mode;
+ int err;
+
+ if (!*str)
+ return -EINVAL;
+
+ separator = strrchr(str, '=');
+ if (!separator || separator == str || !separator[1])
+ return -EINVAL;
+
+ *separator = '\0';
+ handles = str;
+ mode = separator + 1;
+
+ err = devlink_default_esw_mode_to_value(mode, &esw_mode);
+ if (err)
+ return err;
+
+ err = devlink_default_esw_mode_handles_parse(handles);
+ if (err)
+ devlink_default_esw_mode_nodes_clear();
+ else
+ devlink_default_esw_mode = esw_mode;
+
+ return err;
+}
+
+static int __init devlink_default_esw_mode_setup(char *str)
+{
+ devlink_default_esw_mode_param = str;
+ return 1;
+}
+__setup("devlink_eswitch_mode=", devlink_default_esw_mode_setup);
+
+int __init devlink_default_esw_mode_init(void)
+{
+ char *def;
+ int err;
+
+ if (!devlink_default_esw_mode_param)
+ return 0;
+
+ def = kstrdup(devlink_default_esw_mode_param, GFP_KERNEL);
+ if (!def) {
+ devlink_default_esw_mode_param = NULL;
+ pr_warn("devlink: devlink_eswitch_mode parameter ignored, failed to allocate memory\n");
+ return 0;
+ }
+
+ err = devlink_default_esw_mode_parse(def);
+ kfree(def);
+ if (err == -EEXIST) {
+ devlink_default_esw_mode_param = NULL;
+ pr_warn("devlink: duplicate eswitch mode handles ignored\n");
+ return 0;
+ } else if (err == -EINVAL) {
+ devlink_default_esw_mode_param = NULL;
+ pr_warn("devlink: invalid devlink_eswitch_mode parameter ignored\n");
+ return 0;
+ } else if (err == -ENOMEM) {
+ devlink_default_esw_mode_param = NULL;
+ pr_warn("devlink: devlink_eswitch_mode parameter ignored, failed to allocate memory\n");
+ return 0;
+ } else if (err) {
+ return err;
+ }
+
+ return 0;
+}
+
+void __init devlink_default_esw_mode_cleanup(void)
+{
+ devlink_default_esw_mode_nodes_clear();
+}
diff --git a/net/devlink/devl_internal.h b/net/devlink/devl_internal.h
index af43b7163f78..fe9ad58515d4 100644
--- a/net/devlink/devl_internal.h
+++ b/net/devlink/devl_internal.h
@@ -71,6 +71,8 @@ extern struct genl_family devlink_nl_family;
struct devlink *__devlink_alloc(const struct devlink_ops *ops, size_t priv_size,
struct net *net, struct device *dev,
const struct device_driver *dev_driver);
+int devlink_default_esw_mode_init(void);
+void devlink_default_esw_mode_cleanup(void);
#define devl_warn(devlink, format, args...) \
do { \
--
2.43.0
^ permalink raw reply related
* [PATCH net-next V6 2/4] devlink: Factor out eswitch mode setting
From: Mark Bloch @ 2026-07-14 6:17 UTC (permalink / raw)
To: Jiri Pirko, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman
Cc: Saeed Mahameed, Leon Romanovsky, Tariq Toukan, Andrew Lunn,
Jonathan Corbet, Shuah Khan, netdev, linux-rdma, linux-doc,
Mark Bloch
In-Reply-To: <20260714061731.531849-1-mbloch@nvidia.com>
Move the common eswitch mode set checks into a small helper and use it
from the netlink eswitch set command. This makes the same validation
available to the devlink core path that applies eswitch mode defaults.
Signed-off-by: Mark Bloch <mbloch@nvidia.com>
---
net/devlink/dev.c | 27 ++++++++++++++++++++-------
net/devlink/devl_internal.h | 3 +++
2 files changed, 23 insertions(+), 7 deletions(-)
diff --git a/net/devlink/dev.c b/net/devlink/dev.c
index bcf001554e84..119ef105d0a7 100644
--- a/net/devlink/dev.c
+++ b/net/devlink/dev.c
@@ -702,6 +702,25 @@ int devlink_nl_eswitch_get_doit(struct sk_buff *skb, struct genl_info *info)
return genlmsg_reply(msg, info);
}
+int devlink_eswitch_mode_set(struct devlink *devlink,
+ enum devlink_eswitch_mode mode,
+ struct netlink_ext_ack *extack)
+{
+ const struct devlink_ops *ops = devlink->ops;
+ int err;
+
+ devl_assert_locked(devlink);
+
+ if (!ops->eswitch_mode_set)
+ return -EOPNOTSUPP;
+
+ err = devlink_rates_check(devlink, devlink_rate_is_node, extack);
+ if (err)
+ return err;
+
+ return ops->eswitch_mode_set(devlink, mode, extack);
+}
+
int devlink_nl_eswitch_set_doit(struct sk_buff *skb, struct genl_info *info)
{
struct devlink *devlink = devlink_nl_ctx(info)->devlink;
@@ -712,14 +731,8 @@ int devlink_nl_eswitch_set_doit(struct sk_buff *skb, struct genl_info *info)
u16 mode;
if (info->attrs[DEVLINK_ATTR_ESWITCH_MODE]) {
- if (!ops->eswitch_mode_set)
- return -EOPNOTSUPP;
- err = devlink_rates_check(devlink, devlink_rate_is_node,
- info->extack);
- if (err)
- return err;
mode = nla_get_u16(info->attrs[DEVLINK_ATTR_ESWITCH_MODE]);
- err = ops->eswitch_mode_set(devlink, mode, info->extack);
+ err = devlink_eswitch_mode_set(devlink, mode, info->extack);
if (err)
return err;
}
diff --git a/net/devlink/devl_internal.h b/net/devlink/devl_internal.h
index cdf894ba5a9d..af43b7163f78 100644
--- a/net/devlink/devl_internal.h
+++ b/net/devlink/devl_internal.h
@@ -348,6 +348,9 @@ bool devlink_rate_is_node(const struct devlink_rate *devlink_rate);
int devlink_rates_check(struct devlink *devlink,
bool (*rate_filter)(const struct devlink_rate *),
struct netlink_ext_ack *extack);
+int devlink_eswitch_mode_set(struct devlink *devlink,
+ enum devlink_eswitch_mode mode,
+ struct netlink_ext_ack *extack);
/* Linecards */
unsigned int devlink_linecard_index(struct devlink_linecard *linecard);
--
2.43.0
^ permalink raw reply related
* [PATCH net-next V6 1/4] net/mlx5: Clear FW reset-in-progress bit before reload
From: Mark Bloch @ 2026-07-14 6:17 UTC (permalink / raw)
To: Jiri Pirko, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman
Cc: Saeed Mahameed, Leon Romanovsky, Tariq Toukan, Andrew Lunn,
Jonathan Corbet, Shuah Khan, netdev, linux-rdma, linux-doc,
Mark Bloch, Shay Drori, Moshe Shemesh
In-Reply-To: <20260714061731.531849-1-mbloch@nvidia.com>
mlx5 sets MLX5_FW_RESET_FLAGS_RESET_IN_PROGRESS when acknowledging a sync
reset request. This bit blocks devlink reload and other devlink operations
while the firmware reset is running, but it was kept set until after the
driver reload finished.
Clear the reset-in-progress bit once the reset unload flow is done and PCI
access is back, before reloading the device. For a reset initiated through
devlink, clear it before completing the reload waiter. For a reset reported
through an asynchronous firmware event, keep the unload flow outside
devl_lock, then take devl_lock before clearing the bit and reloading
through the devl-locked load helper.
Reviewed-by: Shay Drori <shayd@nvidia.com>
Reviewed-by: Moshe Shemesh <moshe@nvidia.com>
Signed-off-by: Mark Bloch <mbloch@nvidia.com>
---
.../ethernet/mellanox/mlx5/core/fw_reset.c | 28 +++++++++++--------
1 file changed, 17 insertions(+), 11 deletions(-)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.c b/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.c
index 07440c58713a..7283e5b49eed 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.c
@@ -238,24 +238,30 @@ static void mlx5_fw_reset_complete_reload(struct mlx5_core_dev *dev)
{
struct mlx5_fw_reset *fw_reset = dev->priv.fw_reset;
struct devlink *devlink = priv_to_devlink(dev);
+ int err;
/* if this is the driver that initiated the fw reset, devlink completed the reload */
if (test_bit(MLX5_FW_RESET_FLAGS_PENDING_COMP, &fw_reset->reset_flags)) {
+ clear_bit(MLX5_FW_RESET_FLAGS_RESET_IN_PROGRESS,
+ &fw_reset->reset_flags);
complete(&fw_reset->done);
- } else {
- mlx5_sync_reset_unload_flow(dev, false);
- if (mlx5_health_wait_pci_up(dev))
- mlx5_core_err(dev, "reset reload flow aborted, PCI reads still not working\n");
- else
- mlx5_load_one(dev, true);
- devl_lock(devlink);
- devlink_remote_reload_actions_performed(devlink, 0,
- BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT) |
- BIT(DEVLINK_RELOAD_ACTION_FW_ACTIVATE));
- devl_unlock(devlink);
+ return;
}
+ mlx5_sync_reset_unload_flow(dev, false);
+ err = mlx5_health_wait_pci_up(dev);
+
+ devl_lock(devlink);
clear_bit(MLX5_FW_RESET_FLAGS_RESET_IN_PROGRESS, &fw_reset->reset_flags);
+ if (err)
+ mlx5_core_err(dev, "reset reload flow aborted, PCI reads still not working\n");
+ else
+ mlx5_load_one_devl_locked(dev, true);
+
+ devlink_remote_reload_actions_performed(devlink, 0,
+ BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT) |
+ BIT(DEVLINK_RELOAD_ACTION_FW_ACTIVATE));
+ devl_unlock(devlink);
}
static void mlx5_stop_sync_reset_poll(struct mlx5_core_dev *dev)
--
2.43.0
^ permalink raw reply related
* [PATCH 2/2] net: wwan: qcom_bam_dmux: Alloc RX buffers as a single coherent block
From: Vishnu Santhosh @ 2026-07-14 5:32 UTC (permalink / raw)
To: Stephan Gerhold, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Loic Poulain, Sergey Ryazanov, Johannes Berg
Cc: linux-arm-msm, netdev, devicetree, linux-kernel, Vishnu Santhosh,
chris.lew, Deepak Kumar Singh
In-Reply-To: <20260714-qcom-bam-dmux-vmid-ext-v1-0-3f29da7cca76@oss.qualcomm.com>
On Qualcomm SoCs where the modem (e.g. the mDSP on Shikra, VMID 43 /
NAV) is the AXI master for BAM-DMUX RX transfers and the XPU enforces
per-region access control, each individually DMA-mapped RX buffer
requires its own XPU resource group (RG). With ~16 RGs available, the
32 per-buffer dma_map_single() calls exhaust the table and the first
inbound transfer faults with an XPU violation.
BAM-DMUX is a singleton (exactly one instance per SoC), so the
destination VMID does not need to be a DT property; it is looked up
from the compatible string's match data instead. Add struct
bam_dmux_data with a single vmid field, and a shikra_data instance
hardcoding QCOM_SCM_VMID_NAV for qcom,shikra-bam-dmux.
When match data is present, allocate all BAM_DMUX_NUM_SKB RX buffers as
a single contiguous dma_alloc_coherent() block and SCM-assign that
block to HLOS plus the VMID once at probe. This reduces RG consumption
from 32 to 1. The block is never reclaimed across a modem power cycle
(bam_dmux_power_off() does not touch it), so the probe-time assignment
covers every subsequent restart without re-assigning or reclaiming. It
is reclaimed to HLOS only once, at remove or on a probe error, and if
that reclaim fails it is leaked rather than returned to the page
allocator.
Each rx_skbs[] slot is pre-assigned its virtual and DMA address from
the block, so no per-buffer mapping is needed at power-on. Because the
coherent block is not page-backed, received payload is copied into a
regular netdev skb before handoff to the network stack; this is an
unavoidable extra copy on the XPU-enforced RX path.
Platforms without match data are unaffected: rx_virt stays NULL, no
coherent memory is allocated, and the per-buffer dma_map_single() path
is unchanged.
Co-developed-by: Deepak Kumar Singh <deepak.singh@oss.qualcomm.com>
Signed-off-by: Deepak Kumar Singh <deepak.singh@oss.qualcomm.com>
Signed-off-by: Vishnu Santhosh <vishnu.santhosh@oss.qualcomm.com>
---
drivers/net/wwan/Kconfig | 1 +
drivers/net/wwan/qcom_bam_dmux.c | 134 ++++++++++++++++++++++++++++++++++++---
2 files changed, 125 insertions(+), 10 deletions(-)
diff --git a/drivers/net/wwan/Kconfig b/drivers/net/wwan/Kconfig
index 958dbc7347fa84ee869439bf8b503037faab8bef..1b133c56231615269698140187ca3141dfe48dbf 100644
--- a/drivers/net/wwan/Kconfig
+++ b/drivers/net/wwan/Kconfig
@@ -65,6 +65,7 @@ config MHI_WWAN_MBIM
config QCOM_BAM_DMUX
tristate "Qualcomm BAM-DMUX WWAN network driver"
depends on (DMA_ENGINE && PM && QCOM_SMEM_STATE) || COMPILE_TEST
+ select QCOM_SCM
help
The BAM Data Multiplexer provides access to the network data channels
of modems integrated into many older Qualcomm SoCs, e.g. Qualcomm
diff --git a/drivers/net/wwan/qcom_bam_dmux.c b/drivers/net/wwan/qcom_bam_dmux.c
index cc6ace8d64371eb8d00c638a39b234ee540b83c9..247230b720e6011876d5c429badbb5a1f34fc576 100644
--- a/drivers/net/wwan/qcom_bam_dmux.c
+++ b/drivers/net/wwan/qcom_bam_dmux.c
@@ -9,10 +9,12 @@
#include <linux/completion.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
+#include <linux/firmware/qcom/qcom_scm.h>
#include <linux/if_arp.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/netdevice.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/soc/qcom/smem_state.h>
@@ -62,6 +64,7 @@ struct bam_dmux_skb_dma {
struct bam_dmux *dmux;
struct sk_buff *skb;
dma_addr_t addr;
+ void *rx_virt; /* non-NULL: slot in the coherent RX block */
};
struct bam_dmux {
@@ -75,6 +78,10 @@ struct bam_dmux {
struct completion pc_ack_completion;
struct dma_chan *rx, *tx;
+ /* Single coherent block backing all RX buffers, NULL if unused */
+ void *rx_buf;
+ dma_addr_t rx_buf_dma;
+ u64 rx_buf_perms; /* SCM source-VMID bitmask of rx_buf */
struct bam_dmux_skb_dma rx_skbs[BAM_DMUX_NUM_SKB];
struct bam_dmux_skb_dma tx_skbs[BAM_DMUX_NUM_SKB];
spinlock_t tx_lock; /* Protect tx_skbs, tx_next_skb */
@@ -92,6 +99,10 @@ struct bam_dmux_netdev {
u8 ch;
};
+struct bam_dmux_data {
+ u32 vmid;
+};
+
static void bam_dmux_pc_vote(struct bam_dmux *dmux, bool enable)
{
reinit_completion(&dmux->pc_ack_completion);
@@ -111,6 +122,9 @@ static bool bam_dmux_skb_dma_map(struct bam_dmux_skb_dma *skb_dma,
{
struct device *dev = skb_dma->dmux->dev;
+ if (skb_dma->rx_virt) /* coherent RX slot: addr pre-assigned */
+ return true;
+
skb_dma->addr = dma_map_single(dev, skb_dma->skb->data, skb_dma->skb->len, dir);
if (dma_mapping_error(dev, skb_dma->addr)) {
dev_err(dev, "Failed to DMA map buffer\n");
@@ -124,6 +138,9 @@ static bool bam_dmux_skb_dma_map(struct bam_dmux_skb_dma *skb_dma,
static void bam_dmux_skb_dma_unmap(struct bam_dmux_skb_dma *skb_dma,
enum dma_data_direction dir)
{
+ if (skb_dma->rx_virt) /* coherent RX slot: nothing to unmap */
+ return;
+
dma_unmap_single(skb_dma->dmux->dev, skb_dma->addr, skb_dma->skb->len, dir);
skb_dma->addr = 0;
}
@@ -468,9 +485,10 @@ static bool bam_dmux_skb_dma_submit_rx(struct bam_dmux_skb_dma *skb_dma)
{
struct bam_dmux *dmux = skb_dma->dmux;
struct dma_async_tx_descriptor *desc;
+ size_t len = skb_dma->rx_virt ? BAM_DMUX_BUFFER_SIZE : skb_dma->skb->len;
desc = dmaengine_prep_slave_single(dmux->rx, skb_dma->addr,
- skb_dma->skb->len, DMA_DEV_TO_MEM,
+ len, DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT);
if (!desc) {
dev_err(dmux->dev, "Failed to prepare RX DMA buffer\n");
@@ -485,6 +503,10 @@ static bool bam_dmux_skb_dma_submit_rx(struct bam_dmux_skb_dma *skb_dma)
static bool bam_dmux_skb_dma_queue_rx(struct bam_dmux_skb_dma *skb_dma, gfp_t gfp)
{
+ /* Coherent RX slots have rx_virt and addr pre-assigned at probe. */
+ if (skb_dma->rx_virt)
+ return bam_dmux_skb_dma_submit_rx(skb_dma);
+
if (!skb_dma->skb) {
skb_dma->skb = __netdev_alloc_skb(NULL, BAM_DMUX_BUFFER_SIZE, gfp);
if (!skb_dma->skb)
@@ -499,9 +521,10 @@ static bool bam_dmux_skb_dma_queue_rx(struct bam_dmux_skb_dma *skb_dma, gfp_t gf
static void bam_dmux_cmd_data(struct bam_dmux_skb_dma *skb_dma)
{
struct bam_dmux *dmux = skb_dma->dmux;
- struct sk_buff *skb = skb_dma->skb;
- struct bam_dmux_hdr *hdr = (struct bam_dmux_hdr *)skb->data;
+ struct bam_dmux_hdr *hdr = skb_dma->rx_virt ? skb_dma->rx_virt :
+ (struct bam_dmux_hdr *)skb_dma->skb->data;
struct net_device *netdev = dmux->netdevs[hdr->ch];
+ struct sk_buff *skb;
if (!netdev || !netif_running(netdev)) {
dev_warn(dmux->dev, "Data for inactive channel %u\n", hdr->ch);
@@ -514,10 +537,18 @@ static void bam_dmux_cmd_data(struct bam_dmux_skb_dma *skb_dma)
return;
}
- skb_dma->skb = NULL; /* Hand over to network stack */
-
- skb_pull(skb, sizeof(*hdr));
- skb_trim(skb, hdr->len);
+ if (skb_dma->rx_virt) {
+ /* Coherent block is not page-backed: copy out to a real skb */
+ skb = netdev_alloc_skb(netdev, hdr->len);
+ if (!skb)
+ return;
+ skb_put_data(skb, (u8 *)skb_dma->rx_virt + sizeof(*hdr), hdr->len);
+ } else {
+ skb = skb_dma->skb;
+ skb_dma->skb = NULL; /* Hand over to network stack */
+ skb_pull(skb, sizeof(*hdr));
+ skb_trim(skb, hdr->len);
+ }
skb->dev = netdev;
/* Only Raw-IP/QMAP is supported by this driver */
@@ -574,10 +605,14 @@ static void bam_dmux_rx_callback(void *data)
{
struct bam_dmux_skb_dma *skb_dma = data;
struct bam_dmux *dmux = skb_dma->dmux;
- struct sk_buff *skb = skb_dma->skb;
- struct bam_dmux_hdr *hdr = (struct bam_dmux_hdr *)skb->data;
+ struct bam_dmux_hdr *hdr;
- bam_dmux_skb_dma_unmap(skb_dma, DMA_FROM_DEVICE);
+ if (skb_dma->rx_virt) {
+ hdr = skb_dma->rx_virt; /* coherent RX: no skb to unmap */
+ } else {
+ bam_dmux_skb_dma_unmap(skb_dma, DMA_FROM_DEVICE);
+ hdr = (struct bam_dmux_hdr *)skb_dma->skb->data;
+ }
if (hdr->magic != BAM_DMUX_HDR_MAGIC) {
dev_err(dmux->dev, "Invalid magic in header: %#x\n", hdr->magic);
@@ -644,6 +679,9 @@ static void bam_dmux_free_skbs(struct bam_dmux_skb_dma skbs[],
for (i = 0; i < BAM_DMUX_NUM_SKB; i++) {
struct bam_dmux_skb_dma *skb_dma = &skbs[i];
+ if (skb_dma->rx_virt) /* coherent block freed at remove */
+ continue;
+
if (skb_dma->addr)
bam_dmux_skb_dma_unmap(skb_dma, dir);
if (skb_dma->skb) {
@@ -762,6 +800,71 @@ static int __maybe_unused bam_dmux_runtime_resume(struct device *dev)
return 0;
}
+static int bam_dmux_alloc_coherent_rx(struct bam_dmux *dmux)
+{
+ struct device *dev = dmux->dev;
+ const struct bam_dmux_data *data = of_device_get_match_data(dev);
+ size_t size = BAM_DMUX_NUM_SKB * BAM_DMUX_BUFFER_SIZE;
+ u64 src = BIT_ULL(QCOM_SCM_VMID_HLOS);
+ struct qcom_scm_vmperm dst[2];
+ int i, ret;
+
+ if (!data)
+ return 0;
+
+ if (!qcom_scm_is_available())
+ return -EPROBE_DEFER;
+
+ dst[0].vmid = QCOM_SCM_VMID_HLOS;
+ dst[0].perm = QCOM_SCM_PERM_RW;
+ dst[1].vmid = data->vmid;
+ dst[1].perm = QCOM_SCM_PERM_RW;
+
+ dmux->rx_buf = dma_alloc_coherent(dev, size, &dmux->rx_buf_dma, GFP_KERNEL);
+ if (!dmux->rx_buf)
+ return -ENOMEM;
+
+ for (i = 0; i < BAM_DMUX_NUM_SKB; i++) {
+ dmux->rx_skbs[i].rx_virt = dmux->rx_buf + i * BAM_DMUX_BUFFER_SIZE;
+ dmux->rx_skbs[i].addr = dmux->rx_buf_dma + i * BAM_DMUX_BUFFER_SIZE;
+ }
+
+ ret = qcom_scm_assign_mem(dmux->rx_buf_dma, size, &src, dst, ARRAY_SIZE(dst));
+ if (ret) {
+ dev_err(dev, "SCM assign RX block failed: %d\n", ret);
+ dma_free_coherent(dev, size, dmux->rx_buf, dmux->rx_buf_dma);
+ dmux->rx_buf = NULL;
+ return ret;
+ }
+ dmux->rx_buf_perms = src;
+
+ return 0;
+}
+
+static void bam_dmux_free_coherent_rx(struct bam_dmux *dmux)
+{
+ struct qcom_scm_vmperm hlos = {
+ .vmid = QCOM_SCM_VMID_HLOS,
+ .perm = QCOM_SCM_PERM_RW,
+ };
+ size_t size = BAM_DMUX_NUM_SKB * BAM_DMUX_BUFFER_SIZE;
+
+ if (!dmux->rx_buf)
+ return;
+
+ if (dmux->rx_buf_perms) {
+ if (qcom_scm_assign_mem(dmux->rx_buf_dma, size, &dmux->rx_buf_perms,
+ &hlos, 1)) {
+ dev_err(dmux->dev, "SCM reclaim RX block failed; leaking\n");
+ return;
+ }
+ dmux->rx_buf_perms = 0;
+ }
+
+ dma_free_coherent(dmux->dev, size, dmux->rx_buf, dmux->rx_buf_dma);
+ dmux->rx_buf = NULL;
+}
+
static int bam_dmux_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -809,6 +912,10 @@ static int bam_dmux_probe(struct platform_device *pdev)
dmux->tx_skbs[i].dmux = dmux;
}
+ ret = bam_dmux_alloc_coherent_rx(dmux);
+ if (ret)
+ return ret;
+
/* Runtime PM manages our own power vote.
* Note that the RX path may be active even if we are runtime suspended,
* since it is controlled by the remote side.
@@ -845,6 +952,7 @@ static int bam_dmux_probe(struct platform_device *pdev)
err_disable_pm:
pm_runtime_disable(dev);
pm_runtime_dont_use_autosuspend(dev);
+ bam_dmux_free_coherent_rx(dmux);
return ret;
}
@@ -879,13 +987,19 @@ static void bam_dmux_remove(struct platform_device *pdev)
disable_irq(dmux->pc_irq);
bam_dmux_power_off(dmux);
bam_dmux_free_skbs(dmux->tx_skbs, DMA_TO_DEVICE);
+ bam_dmux_free_coherent_rx(dmux);
}
static const struct dev_pm_ops bam_dmux_pm_ops = {
SET_RUNTIME_PM_OPS(bam_dmux_runtime_suspend, bam_dmux_runtime_resume, NULL)
};
+static const struct bam_dmux_data shikra_data = {
+ .vmid = QCOM_SCM_VMID_NAV,
+};
+
static const struct of_device_id bam_dmux_of_match[] = {
+ { .compatible = "qcom,shikra-bam-dmux", .data = &shikra_data },
{ .compatible = "qcom,bam-dmux" },
{ /* sentinel */ }
};
--
2.34.1
^ permalink raw reply related
* [PATCH 1/2] dt-bindings: net: qcom,bam-dmux: Add qcom,shikra-bam-dmux compatible
From: Vishnu Santhosh @ 2026-07-14 5:32 UTC (permalink / raw)
To: Stephan Gerhold, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Loic Poulain, Sergey Ryazanov, Johannes Berg
Cc: linux-arm-msm, netdev, devicetree, linux-kernel, Vishnu Santhosh,
chris.lew, Deepak Kumar Singh
In-Reply-To: <20260714-qcom-bam-dmux-vmid-ext-v1-0-3f29da7cca76@oss.qualcomm.com>
On platforms where the modem DMAs into the BAM-DMUX RX data buffers and
the XPU enforces per-region access control, each individually
DMA-mapped RX buffer consumes an XPU resource group. With only ~16
groups available, the per-buffer mappings exhaust the table and inbound
transfers fault.
Add qcom,shikra-bam-dmux as an additional compatible for the Shikra SoC,
paired with the generic qcom,bam-dmux fallback, so the driver can match
on it via its of_device_id table.
Co-developed-by: Deepak Kumar Singh <deepak.singh@oss.qualcomm.com>
Signed-off-by: Deepak Kumar Singh <deepak.singh@oss.qualcomm.com>
Signed-off-by: Vishnu Santhosh <vishnu.santhosh@oss.qualcomm.com>
---
Documentation/devicetree/bindings/net/qcom,bam-dmux.yaml | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/net/qcom,bam-dmux.yaml b/Documentation/devicetree/bindings/net/qcom,bam-dmux.yaml
index 33746c238513d72366bc52359fb10f275475b331..27f0fdf285c17d6bfdecd5e59cad09912a5e821b 100644
--- a/Documentation/devicetree/bindings/net/qcom,bam-dmux.yaml
+++ b/Documentation/devicetree/bindings/net/qcom,bam-dmux.yaml
@@ -22,7 +22,13 @@ description: |
properties:
compatible:
- const: qcom,bam-dmux
+ oneOf:
+ - const: qcom,bam-dmux
+ - items:
+ - enum:
+ # Shikra
+ - qcom,shikra-bam-dmux
+ - const: qcom,bam-dmux
interrupts:
description:
--
2.34.1
^ permalink raw reply related
* [PATCH 0/2] net: wwan: qcom_bam_dmux: Alloc RX buffers as a single coherent block
From: Vishnu Santhosh @ 2026-07-14 5:32 UTC (permalink / raw)
To: Stephan Gerhold, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Loic Poulain, Sergey Ryazanov, Johannes Berg
Cc: linux-arm-msm, netdev, devicetree, linux-kernel, Vishnu Santhosh,
chris.lew, Deepak Kumar Singh
On platforms where the modem DMAs into the BAM-DMUX RX data buffers and
the XPU (eXternal Protection Unit) enforces per-region access control,
each individually DMA-mapped RX buffer consumes an XPU resource group.
With only ~16 groups available on Shikra (mDSP, VMID 43 / NAV), the
per-buffer mappings exhaust the table and inbound transfers fault.
This series adds a qcom,shikra-bam-dmux compatible and have the driver
select QCOM_SCM_VMID_NAV internally via that compatible's match data.
When matched, the driver allocates all RX buffers as a single
contiguous coherent block and SCM-assigns it to HLOS plus the VMID
once at probe, consuming one XPU resource group instead of many.
Platforms that do not use the qcom,shikra-bam-dmux compatible are
unaffected: the existing per-buffer dma_map_single() path is
unchanged.
Signed-off-by: Vishnu Santhosh <vishnu.santhosh@oss.qualcomm.com>
---
Vishnu Santhosh (2):
dt-bindings: net: qcom,bam-dmux: Add qcom,shikra-bam-dmux compatible
net: wwan: qcom_bam_dmux: Alloc RX buffers as a single coherent block
.../devicetree/bindings/net/qcom,bam-dmux.yaml | 8 +-
drivers/net/wwan/Kconfig | 1 +
drivers/net/wwan/qcom_bam_dmux.c | 134 +++++++++++++++++++--
3 files changed, 132 insertions(+), 11 deletions(-)
---
base-commit: 3b029c035b34bbc693405ddf759f0e9b920c27f1
change-id: 20260714-qcom-bam-dmux-vmid-ext-d9289db310c1
Best regards,
--
Vishnu Santhosh <vishnu.santhosh@oss.qualcomm.com>
^ permalink raw reply
* Dear netdev@vger.kernel.org,
From: Harry Schofield ESQ @ 2026-07-14 5:26 UTC (permalink / raw)
To: netdev
Re:Good day netdev,
Please let me know if this is best email to send you the project
info.
Kind regards,
Harry Schofield, ceMBA
^ permalink raw reply
* Re: [PATCH 2/2] padata: Remove serialized job support
From: Thomas Huth @ 2026-07-14 4:28 UTC (permalink / raw)
To: Eric Biggers, linux-crypto, Herbert Xu
Cc: netdev, linux-kernel, Steffen Klassert
In-Reply-To: <20260713223234.24812-3-ebiggers@kernel.org>
On 14/07/2026 00.32, Eric Biggers wrote:
> Now that pcrypt has been removed, also remove all the code in padata
> whose only user was pcrypt.
>
> Cc: Steffen Klassert <steffen.klassert@secunet.com>
> Signed-off-by: Eric Biggers <ebiggers@kernel.org>
> ---
> Documentation/core-api/padata.rst | 145 +----
> include/linux/padata.h | 145 +----
> kernel/padata.c | 902 +-----------------------------
> 3 files changed, 16 insertions(+), 1176 deletions(-)
Nice clean-up!
Reviewed-by: Thomas Huth <thuth@redhat.com>
^ permalink raw reply
* Re: [PATCH 1/2] crypto: pcrypt - Remove pcrypt
From: Thomas Huth @ 2026-07-14 4:20 UTC (permalink / raw)
To: Eric Biggers, linux-crypto, Herbert Xu
Cc: netdev, linux-kernel, Steffen Klassert, linux-s390
In-Reply-To: <20260713223234.24812-2-ebiggers@kernel.org>
On 14/07/2026 00.32, Eric Biggers wrote:
> pcrypt was originally intended to improve IPsec performance. However,
> it's no longer useful for that. Reports from the rare cases that anyone
> has actually tried to use it over the years indicate that it actually
> reduces IPsec performance, e.g.:
>
> * https://github.com/libreswan/libreswan/wiki/Internals:-Cryptographic-Acceleration#obsoleted-ipsec-accelerations
> * https://users.strongswan.narkive.com/liqTaTq8/strongswan-problem-with-pcrypt
> * https://unix.stackexchange.com/questions/594336/ipsec-multithreading-via-pcrypt-worse-than-single-thread
>
> It's also undocumented and quite difficult to actually use. Its design
> is also broken, in that any unprivileged program can enable pcrypt
> systemwide at any time (by instantiating it using AF_ALG).
>
> Meanwhile, pcrypt has been a regular source of bugs, including at least
> four that have received CVEs.
>
> Let's just remove it. No one seems to care about it anymore other than
> people looking for vulnerabilities.
>
> Cc: Steffen Klassert <steffen.klassert@secunet.com>
> Signed-off-by: Eric Biggers <ebiggers@kernel.org>
> ---
> MAINTAINERS | 7 -
> arch/loongarch/configs/loongson32_defconfig | 1 -
> arch/loongarch/configs/loongson64_defconfig | 1 -
> arch/s390/configs/debug_defconfig | 1 -
> arch/s390/configs/defconfig | 1 -
> crypto/Kconfig | 10 -
> crypto/Makefile | 1 -
> crypto/pcrypt.c | 394 --------------------
> include/crypto/pcrypt.h | 39 --
> tools/crypto/tcrypt/tcrypt_speed_compare.py | 7 +-
> 10 files changed, 2 insertions(+), 460 deletions(-)
> delete mode 100644 crypto/pcrypt.c
> delete mode 100644 include/crypto/pcrypt.h
Thanks!
Reviewed-by: Thomas Huth <thuth@redhat.com>
^ permalink raw reply
* [PATCH net v5] tipc: fix u16 MTU truncation in media and bearer MTU validation
From: Cen Zhang (Microsoft) @ 2026-07-14 4:15 UTC (permalink / raw)
To: jmaloy, davem, edumazet, kuba, pabeni, horms
Cc: andrew, netdev, tipc-discussion, linux-kernel, vadim.fedorenko,
tung.quang.nguyen, AutonomousCodeSecurity, tgopinath, kys,
blbllhy
Both TIPC_NL_MEDIA_SET and TIPC_NL_BEARER_SET accept user-supplied
MTU values but only enforce a minimum bound, not a maximum. When a user
sets the MTU to a value exceeding U16_MAX (65535), it passes validation
but is silently truncated when assigned to u16 fields l->mtu and
l->advertised_mtu in tipc_link_create(). Values like 65536 (0x10000)
truncate to 0, causing a division by zero in tipc_link_set_queue_limits()
which computes TIPC_MAX_PUBL / (l->mtu / ITEM_SIZE). Other overflowing
values (e.g. 65537-131071) produce small incorrect MTU values, resulting
in link malfunction behaviors.
Crash stack (triggered as unprivileged user via user namespace):
tipc_link_set_queue_limits net/tipc/link.c:2531
tipc_link_create net/tipc/link.c:520
tipc_node_check_dest net/tipc/node.c:1279
tipc_disc_rcv net/tipc/discover.c:252
tipc_rcv net/tipc/node.c:2129
tipc_udp_recv net/tipc/udp_media.c:392
Two independent paths lack the upper bound check:
1. tipc_udp_mtu_bad() -- called from __tipc_nl_media_set() (MEDIA_SET)
2. inline check in __tipc_nl_bearer_set() at bearer.c:1160 (BEARER_SET)
Fix both by rejecting MTU values above U16_MAX.
Fixes: 901271e0403a ("tipc: implement configuration of UDP media MTU")
Reported-by: AutonomousCodeSecurity@microsoft.com
Closes: https://lore.kernel.org/all/CAB8m9WgETt0AjmFwE=F-CKjGXsK6_WDv0=kbYRcC8-noo+amnA@mail.gmail.com
Reviewed-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
Signed-off-by: Cen Zhang (Microsoft) <blbllhy@gmail.com>
---
v5: Drop .min from range struct (min check already in handler)
v4: Add .min check value
v3: Use nla_policy check to limit MTU max value as suggested by Vadim
v2: Solved format issue
Link: https://lore.kernel.org/all/CAB8m9WgETt0AjmFwE=F-CKjGXsK6_WDv0=kbYRcC8-noo+amnA@mail.gmail.com
net/tipc/netlink.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/net/tipc/netlink.c b/net/tipc/netlink.c
index 8336a9664703..1307dd1a9613 100644
--- a/net/tipc/netlink.c
+++ b/net/tipc/netlink.c
@@ -113,12 +113,16 @@ const struct nla_policy tipc_nl_node_policy[TIPC_NLA_NODE_MAX + 1] = {
};
/* Properties valid for media, bearer and link */
+static const struct netlink_range_validation tipc_nl_mtu_range = {
+ .max = U16_MAX,
+};
+
const struct nla_policy tipc_nl_prop_policy[TIPC_NLA_PROP_MAX + 1] = {
[TIPC_NLA_PROP_UNSPEC] = { .type = NLA_UNSPEC },
[TIPC_NLA_PROP_PRIO] = { .type = NLA_U32 },
[TIPC_NLA_PROP_TOL] = { .type = NLA_U32 },
[TIPC_NLA_PROP_WIN] = { .type = NLA_U32 },
- [TIPC_NLA_PROP_MTU] = { .type = NLA_U32 },
+ [TIPC_NLA_PROP_MTU] = NLA_POLICY_FULL_RANGE(NLA_U32, &tipc_nl_mtu_range),
[TIPC_NLA_PROP_BROADCAST] = { .type = NLA_U32 },
[TIPC_NLA_PROP_BROADCAST_RATIO] = { .type = NLA_U32 }
};
--
2.53.0
^ permalink raw reply related
* Maintainers Summit 2026 Call for Topics
From: Theodore Tso @ 2026-07-14 4:14 UTC (permalink / raw)
To: linux-fsdevel, linux-block, linux-kernel, netdev, linux-mm,
ksummit
This year, the Maintainers Summit will be held in Prague, Czech
Republic on Thursday, October 8th, 2026, just after the Linux
Plumber's Conference (October 5 -- 7th).
As in previous years, the Maintainers Summit is invite-only, where the
primary focus will be process issues around Linux Kernel Development.
It will be limited to 30 invitees and a handful of sponsored
attendees.
The Maintainers Summit depends on the development community to bring
forward topics that can benefit from discussion in that setting. What
are the nagging development issues, pain points, or other important
decisions that are not amenable to resolution on the mailing lists?
If you have a topic in mind, please send it to ksummit@lists.linux.dev
with a subject prefix of [MAINTAINERS SUMMIT].
The attendees are jointly selected by Linus Torvlads (who has provided
us with a roughly a dozen maintainers that he would like to attend),
and the program committee, who choose from maintainers and developers
who have been the most active in the past year.
Anybody proposing a topic before July 24th will be added to the list
of potential attendees selected by the program committee; other
potential nominees can be proposed (as a self-nomination or by others)
by sending a note to the ksumimt list saying why that person's
presence would help the discussion.
For an examples of past Maintainers Summit topics, please see the
these LWN articles:
* 2025 https://lwn.net/Articles/1049982/
* 2024 https://lwn.net/Articles/990740/
* 2023 https://lwn.net/Articles/951847/
The program committee this year is composed of the following people:
Christian Brauner
Jon Corbet
Greg KH
Ted Ts'o
Rafael J. Wysocki
^ permalink raw reply
* Re: [PATCH net v4] tipc: fix u16 MTU truncation in media and bearer MTU validation
From: Cen Zhang (Microsoft) @ 2026-07-14 3:50 UTC (permalink / raw)
To: andrew
Cc: AutonomousCodeSecurity, blbllhy, davem, edumazet, horms, jmaloy,
kuba, kys, linux-kernel, netdev, pabeni, tgopinath,
tipc-discussion, tung.quang.nguyen, vadim.fedorenko
In-Reply-To: <3ec7b01a-270a-4b57-af9e-bdbf85313da2@lunn.ch>
IIUC, the MTU here is TIPC protocol-level (stored in struct
tipc_media/tipc_bearer), not device-specific, so netif_set_mtu()
wouldn't apply. I'll prepare v5 shortly with the full range
.max check only.
^ permalink raw reply
* Re: [PATCH net-next 0/3] net: nexthop: per-nexthop UDP dst port for fdb (VXLAN) nexthops
From: Jack Ma @ 2026-07-14 3:43 UTC (permalink / raw)
To: Ido Schimmel, netdev
Cc: David Ahern, David S . Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Shuah Khan, linux-kselftest,
linux-kernel, Jack Ma
In-Reply-To: <20260713122339.GA548566@shredder>
On Mon, Jul 13, 2026 at 03:23:39PM +0300, Ido Schimmel wrote:
> > Some deployments pack several receivers behind a single underlay IP and tell
> > them apart by UDP destination port. To spread flows across such receivers they
>
> I don't understand the "tell them apart" phrasing. Aren't all of these
> receivers interchangeable given you are load balancing between them?
Sorry, that wording conflated two separate things. The load-balancing
targets are interchangeable; the UDP port is not what makes them
distinct services, it is how the underlay addresses each one:
- Addressing: several VTEPs are packed behind one underlay IP, each
reachable at a distinct UDP destination port. The port is the
underlay demux that delivers an encapsulated packet to the right
VTEP -- it selects *where*, not *which service*.
- Load-balancing: when those VTEPs form an HA set (shared inner
MAC/IP), a sender spreads flows across them with an fdb nexthop
group. The legs are interchangeable, but each one lives at a
different (underlay IP, UDP port).
So the legs are interchangeable endpoints that happen to sit at
different (IP, port) addresses. A group can already carry a distinct IP
per leg but not a distinct port, which is the gap here. I'll reword the
cover and patch 1 to say this explicitly.
> I think you will need to provide more details about the use case [...]
> In your use case, why can't the load balancing happen at the target
> host (e.g., using XDP / TC / SO_REUSEPORT / flow classification)?
I think the key difference is that there isn't really a shared
host-side datapath to load-balance in, and the UDP port is doing demux
rather than load-balancing. The shape is a bit unusual, so maybe a
couple of pictures help.
First, how forwarders sit on a receiver node. A forwarder here is
just the pod that terminates the overlay tunnel (it hosts the VXLAN
VTEP) and relays traffic to and from one tenant's workload. Many such
pods share one mesh-routable underlay IP, each demuxed by UDP port:
receiver node -- one mesh-routable underlay IP (NodeIP_A)
+----------------------------------------------------+
| host netns: stateless outer-UDP demux by dst port |
| (host does NOT terminate the tunnel) |
| |
| dst :40000 dst :40001 dst :40002 |
| | | | |
| +-----v----+ +-----v----+ +-----v----+ |
| | pod0 ns | | pod1 ns | | pod2 ns | |
| | vxlan | | vxlan | | vxlan | |
| | VTEP | | VTEP | | VTEP | |
| | decap | | decap | | decap | |
| +----------+ +----------+ +----------+ |
+----------------------------------------------------+
(up to ~10 forwarder pods packed per node)
These packed pods are unrelated -- different tenants on different
VNIs (below) -- so the per-pod UDP port is node-level demux, not an
HA construct. A single forwarder's HA replicas are the orthogonal
axis: anti-affinity spreads them across nodes (never co-resident),
so one member's nexthop group has its legs on distinct node IPs.
But each leg is still reachable only at (node IP, that pod's UDP
port), so within one group the legs differ in IP *and* port. A
group can already carry a distinct IP per leg, but it takes the UDP
port from the device (a single value), so it can't send each leg to
its own port. That last part is really the gap we keep running into.
The pods sharing a node need not be related: each one belongs to a
separate tenant on its own VXLAN VNI, and a node can pack forwarders
for many of them, each on its own UDP port. That per-pod port is what
lets different tenants co-locate without colliding, and the host --
which only demuxes outer UDP -- never has to reason about tenancy.
Zooming into one forwarder pod, there isn't really anything to
load-balance on the host: the tunnel terminates on a vxlan device
inside the pod's own netns, and the pod reaches the customer through a
separate NIC:
one forwarder pod -- its own netns, tenant VNI X
+-------------------------------------------------+
| |
| on/off-ramp NIC <--- customer data plane |
| | on-ramp (ingress) / off-ramp (egress) |
| | inner packet |
| vxlan (VTEP) encap / decap for VNI X, |
| | listens on this pod's UDP port |
| | outer VXLAN UDP |
| eth0 (underlay) NodeIP:port |
| | to peer VTEPs over the |
| v mesh underlay |
| |
+-------------------------------------------------+
The load-balancing already happens at the sender -- the fdb nexthop
group hashes the inner flow and picks a leg. The only missing piece is
that the selected leg can't carry its own UDP port, so the sender
reaches the right pod's IP but always the device's port.
The host-side alternatives seem to assume a shared datapath that isn't
really there in this design:
- SO_REUSEPORT balances sockets within a single netns; here the
receivers are in different netns -- in fact different tenants --
so there isn't a shared host socket to reuse.
- An XDP/TC host fan-out would mean the host has to become a VTEP --
terminate the tunnel and re-dispatch inner traffic across netns
and tenant/VNI boundaries -- which puts the host into the tenant
data path (today it only does a stateless outer-UDP demux) and
largely duplicates what an fdb nexthop group does: hashing an
inner flow across a set of endpoints. The group can already vary
the leg's IP; the port is the part it can't.
On the uAPI cost: I don't think this adds a new datapath concept. A
single fdb entry already carries a per-destination UDP port (NDA_PORT),
and vxlan_xmit_one() already prefers rdst->remote_port when it's set.
NHA_FDB_PORT is meant to be the nexthop analog of that existing
attribute: control-plane only, no datapath change, and backward
compatible (a leg with no port falls back to the device port as today).
Happy to go into the deployment/addressing constraints in more detail if
that would help.
Thanks for the review.
^ permalink raw reply
* [PATCH nf v2] netfilter: ip6tables: set hotdrop for malformed extension header matches
From: Zhixing Chen @ 2026-07-14 3:21 UTC (permalink / raw)
To: Florian Westphal, Pablo Neira Ayuso, Phil Sutter
Cc: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, netfilter-devel, coreteam, netdev, Zhixing Chen
The hbh, srh and ipv6header matches have paths that return false for
malformed IPv6 extension header packets without setting hotdrop.
For hbh, strict option parsing stops when the option type or length field
cannot be read, or when advancing to the next requested option would
exceed the available header data. Mark these packets for hotdrop instead
of treating them as a rule mismatch.
For srh, keep a missing SRH as a normal mismatch, but set hotdrop when
header lookup fails for other reasons, when the SRH fixed header is not
present, when the advertised SRH length exceeds the available skb data,
when segments_left exceeds first_segment, or when SID selector reads fail.
For ipv6header, set hotdrop when the advertised extension header length
exceeds the available skb data.
Returning false treats the packet as a rule mismatch. Set hotdrop for
these malformed packets so they cannot bypass rules intended to drop
packets with these IPv6 extension headers.
Signed-off-by: Zhixing Chen <running910@gmail.com>
---
Changes in v2:
- Use hotdrop labels for hbh and srh paths.
- Mark SRH packets with segments_left greater than first_segment for
hotdrop.
- Drop the redundant ipv6header length check before skb_header_pointer().
v1: https://lore.kernel.org/netdev/20260709063012.33160-1-running910@gmail.com/T/
---
net/ipv6/netfilter/ip6t_hbh.c | 27 ++++++++++---------
| 9 +++----
net/ipv6/netfilter/ip6t_srh.c | 40 ++++++++++++++++++++--------
3 files changed, 47 insertions(+), 29 deletions(-)
diff --git a/net/ipv6/netfilter/ip6t_hbh.c b/net/ipv6/netfilter/ip6t_hbh.c
index 6d1a5d2026a6..1b5dcc92b7da 100644
--- a/net/ipv6/netfilter/ip6t_hbh.c
+++ b/net/ipv6/netfilter/ip6t_hbh.c
@@ -62,21 +62,18 @@ hbh_mt6(const struct sk_buff *skb, struct xt_action_param *par)
NEXTHDR_HOP : NEXTHDR_DEST, NULL, NULL);
if (err < 0) {
if (err != -ENOENT)
- par->hotdrop = true;
+ goto hotdrop;
return false;
}
oh = skb_header_pointer(skb, ptr, sizeof(_optsh), &_optsh);
- if (oh == NULL) {
- par->hotdrop = true;
- return false;
- }
+ if (!oh)
+ goto hotdrop;
hdrlen = ipv6_optlen(oh);
if (skb->len - ptr < hdrlen) {
/* Packet smaller than it's length field */
- par->hotdrop = true;
- return false;
+ goto hotdrop;
}
pr_debug("IPv6 OPTS LEN %u %u ", hdrlen, oh->hdrlen);
@@ -104,8 +101,8 @@ hbh_mt6(const struct sk_buff *skb, struct xt_action_param *par)
break;
tp = skb_header_pointer(skb, ptr, sizeof(_opttype),
&_opttype);
- if (tp == NULL)
- break;
+ if (!tp)
+ goto hotdrop;
/* Type check */
if (*tp != (optinfo->opts[temp] & 0xFF00) >> 8) {
@@ -121,12 +118,12 @@ hbh_mt6(const struct sk_buff *skb, struct xt_action_param *par)
/* length field exists ? */
if (hdrlen < 2)
- break;
+ goto hotdrop;
lp = skb_header_pointer(skb, ptr + 1,
sizeof(_optlen),
&_optlen);
- if (lp == NULL)
- break;
+ if (!lp)
+ goto hotdrop;
spec_len = optinfo->opts[temp] & 0x00FF;
if (spec_len != 0x00FF && spec_len != *lp) {
@@ -147,7 +144,7 @@ hbh_mt6(const struct sk_buff *skb, struct xt_action_param *par)
if ((ptr > skb->len - optlen || hdrlen < optlen) &&
temp < optinfo->optsnr - 1) {
pr_debug("new pointer is too large!\n");
- break;
+ goto hotdrop;
}
ptr += optlen;
hdrlen -= optlen;
@@ -159,6 +156,10 @@ hbh_mt6(const struct sk_buff *skb, struct xt_action_param *par)
}
return false;
+
+hotdrop:
+ par->hotdrop = true;
+ return false;
}
static int hbh_mt6_check(const struct xt_mtchk_param *par)
--git a/net/ipv6/netfilter/ip6t_ipv6header.c b/net/ipv6/netfilter/ip6t_ipv6header.c
index c52ff929c93b..e339cefc7fff 100644
--- a/net/ipv6/netfilter/ip6t_ipv6header.c
+++ b/net/ipv6/netfilter/ip6t_ipv6header.c
@@ -52,9 +52,6 @@ ipv6header_mt6(const struct sk_buff *skb, struct xt_action_param *par)
temp |= MASK_NONE;
break;
}
- /* Is there enough space for the next ext header? */
- if (len < (int)sizeof(struct ipv6_opt_hdr))
- return false;
/* ESP -> evaluate */
if (nexthdr == NEXTHDR_ESP) {
temp |= MASK_ESP;
@@ -99,8 +96,10 @@ ipv6header_mt6(const struct sk_buff *skb, struct xt_action_param *par)
nexthdr = hp->nexthdr;
len -= hdrlen;
ptr += hdrlen;
- if (ptr > skb->len)
- break;
+ if (ptr > skb->len) {
+ par->hotdrop = true;
+ return false;
+ }
}
if (nexthdr != NEXTHDR_NONE && nexthdr != NEXTHDR_ESP)
diff --git a/net/ipv6/netfilter/ip6t_srh.c b/net/ipv6/netfilter/ip6t_srh.c
index db0fd64d8986..321c7f40a24f 100644
--- a/net/ipv6/netfilter/ip6t_srh.c
+++ b/net/ipv6/netfilter/ip6t_srh.c
@@ -27,22 +27,27 @@ static bool srh_mt6(const struct sk_buff *skb, struct xt_action_param *par)
struct ipv6_sr_hdr *srh;
struct ipv6_sr_hdr _srh;
int hdrlen, srhoff = 0;
+ int err;
- if (ipv6_find_hdr(skb, &srhoff, IPPROTO_ROUTING, NULL, NULL) < 0)
+ err = ipv6_find_hdr(skb, &srhoff, IPPROTO_ROUTING, NULL, NULL);
+ if (err < 0) {
+ if (err != -ENOENT)
+ goto hotdrop;
return false;
+ }
srh = skb_header_pointer(skb, srhoff, sizeof(_srh), &_srh);
if (!srh)
- return false;
+ goto hotdrop;
hdrlen = ipv6_optlen(srh);
if (skb->len - srhoff < hdrlen)
- return false;
+ goto hotdrop;
if (srh->type != IPV6_SRCRT_TYPE_4)
return false;
if (srh->segments_left > srh->first_segment)
- return false;
+ goto hotdrop;
/* Next Header matching */
if (srhinfo->mt_flags & IP6T_SRH_NEXTHDR)
@@ -111,6 +116,10 @@ static bool srh_mt6(const struct sk_buff *skb, struct xt_action_param *par)
!(srh->tag == srhinfo->tag)))
return false;
return true;
+
+hotdrop:
+ par->hotdrop = true;
+ return false;
}
static bool srh1_mt6(const struct sk_buff *skb, struct xt_action_param *par)
@@ -121,22 +130,27 @@ static bool srh1_mt6(const struct sk_buff *skb, struct xt_action_param *par)
struct in6_addr _psid, _nsid, _lsid;
struct ipv6_sr_hdr *srh;
struct ipv6_sr_hdr _srh;
+ int err;
- if (ipv6_find_hdr(skb, &srhoff, IPPROTO_ROUTING, NULL, NULL) < 0)
+ err = ipv6_find_hdr(skb, &srhoff, IPPROTO_ROUTING, NULL, NULL);
+ if (err < 0) {
+ if (err != -ENOENT)
+ goto hotdrop;
return false;
+ }
srh = skb_header_pointer(skb, srhoff, sizeof(_srh), &_srh);
if (!srh)
- return false;
+ goto hotdrop;
hdrlen = ipv6_optlen(srh);
if (skb->len - srhoff < hdrlen)
- return false;
+ goto hotdrop;
if (srh->type != IPV6_SRCRT_TYPE_4)
return false;
if (srh->segments_left > srh->first_segment)
- return false;
+ goto hotdrop;
/* Next Header matching */
if (srhinfo->mt_flags & IP6T_SRH_NEXTHDR)
@@ -207,7 +221,7 @@ static bool srh1_mt6(const struct sk_buff *skb, struct xt_action_param *par)
((srh->segments_left + 1) * sizeof(struct in6_addr));
psid = skb_header_pointer(skb, psidoff, sizeof(_psid), &_psid);
if (!psid)
- return false;
+ goto hotdrop;
if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_PSID,
ipv6_masked_addr_cmp(psid, &srhinfo->psid_msk,
&srhinfo->psid_addr)))
@@ -222,7 +236,7 @@ static bool srh1_mt6(const struct sk_buff *skb, struct xt_action_param *par)
((srh->segments_left - 1) * sizeof(struct in6_addr));
nsid = skb_header_pointer(skb, nsidoff, sizeof(_nsid), &_nsid);
if (!nsid)
- return false;
+ goto hotdrop;
if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_NSID,
ipv6_masked_addr_cmp(nsid, &srhinfo->nsid_msk,
&srhinfo->nsid_addr)))
@@ -234,13 +248,17 @@ static bool srh1_mt6(const struct sk_buff *skb, struct xt_action_param *par)
lsidoff = srhoff + sizeof(struct ipv6_sr_hdr);
lsid = skb_header_pointer(skb, lsidoff, sizeof(_lsid), &_lsid);
if (!lsid)
- return false;
+ goto hotdrop;
if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LSID,
ipv6_masked_addr_cmp(lsid, &srhinfo->lsid_msk,
&srhinfo->lsid_addr)))
return false;
}
return true;
+
+hotdrop:
+ par->hotdrop = true;
+ return false;
}
static int srh_mt6_check(const struct xt_mtchk_param *par)
--
2.34.1
^ permalink raw reply related
* Re: [PATCH net-next] octeontx2-af: add new mbox to support sync cycle on rx path
From: Ratheesh Kannoth @ 2026-07-14 3:01 UTC (permalink / raw)
To: linux-kernel, netdev
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, sgoutham, Satha Rao
In-Reply-To: <20260714020723.1811237-1-rkannoth@marvell.com>
On 2026-07-14 at 07:37:23, Ratheesh Kannoth (rkannoth@marvell.com) wrote:
> From: Satha Rao <skoteshwar@marvell.com>
>
> sync ensures that all packets that were in flight are flushed out to
> memory. This can be used to assist in the tearing down of an active RQ.
>
> To complete disabling RQs or disabling SMQ and its SQs, LF software
> send mbox to AF to complete RX_SW_SYNC.
>
> Change-Id: Iaadf5e6b26a7920693fca9921b2050af0e8e2322
> Signed-off-by: Satha Rao <skoteshwar@marvell.com>
> Signed-off-by: Ratheesh Kannoth <rkannoth@marvell.com>
Please ignore the previous submission. It accidentally included a Gerrit Change-Id
and caught a CI warning. I will resubmit a clean version.
"ERROR: Remove Gerrit Change-Id's before submitting upstream WARNING in CI."
pw-bot: changes-requested
^ permalink raw reply
* [PATCH net] i40e: xsk: fix multi-buffer XDP_PASS skb construction
From: Chenguang Zhao @ 2026-07-14 2:51 UTC (permalink / raw)
To: anthony.l.nguyen, przemyslaw.kitszel, andrew+netdev, davem,
edumazet, kuba, pabeni
Cc: intel-wired-lan, netdev, chenguang.zhao, Chenguang Zhao
From: Chenguang Zhao <zhaochenguang@kylinos.cn>
When AF_XDP ZC receives a multi-buffer frame and the XDP program
returns XDP_PASS, i40e_construct_skb_zc() copies frags into a new
skb. The copy used skb_frag_page() as the memcpy source (page
metadata instead of packet data) and passed a virtual address to
__skb_fill_page_desc_noacc(), which expects a struct page *.
Use skb_frag_address() for the copy, attach frags with
skb_add_rx_frag() so len/data_len/truesize are updated, and on
dev_alloc_page() failure free the skb via the shared out path so
xsk_buff_free() still runs and previously attached pages are
released by kfree_skb.
Fixes: 1c9ba9c14658 ("i40e: xsk: add RX multi-buffer support")
Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
---
- Fix memcpy source: use skb_frag_address() instead of skb_frag_page(),
which was copying page metadata rather than packet data.
- Fix frag attachment: pass the allocated struct page * to the skb frag
helper instead of the page virtual address.
- Use skb_add_rx_frag() so skb->len, data_len and truesize are updated
when attaching copied frags.
- On mid-loop dev_alloc_page() failure, go through the shared out path
so previously attached pages are released via kfree_skb and
xsk_buff_free() is still called.
drivers/net/ethernet/intel/i40e/i40e_xsk.c | 13 +++++--------
1 file changed, 5 insertions(+), 8 deletions(-)
diff --git a/drivers/net/ethernet/intel/i40e/i40e_xsk.c b/drivers/net/ethernet/intel/i40e/i40e_xsk.c
index 9f47388eaba5..a4247710c85b 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_xsk.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_xsk.c
@@ -318,22 +318,19 @@ static struct sk_buff *i40e_construct_skb_zc(struct i40e_ring *rx_ring,
goto out;
for (int i = 0; i < nr_frags; i++) {
- struct skb_shared_info *skinfo = skb_shinfo(skb);
skb_frag_t *frag = &sinfo->frags[i];
+ unsigned int frag_size = skb_frag_size(frag);
struct page *page;
- void *addr;
page = dev_alloc_page();
if (!page) {
dev_kfree_skb(skb);
- return NULL;
+ skb = NULL;
+ goto out;
}
- addr = page_to_virt(page);
- memcpy(addr, skb_frag_page(frag), skb_frag_size(frag));
-
- __skb_fill_page_desc_noacc(skinfo, skinfo->nr_frags++,
- addr, 0, skb_frag_size(frag));
+ memcpy(page_to_virt(page), skb_frag_address(frag), frag_size);
+ skb_add_rx_frag(skb, i, page, 0, frag_size, PAGE_SIZE);
}
out:
--
2.25.1
^ permalink raw reply related
* [PATCH] virtio_net: fix spelling of aggressively in comments
From: weimin xiong @ 2026-07-14 2:40 UTC (permalink / raw)
To: netdev; +Cc: mst, jasowangio, xuanzhuo, eperezma, virtualization, xiongweimin
From: xiongweimin <xiongweimin@kylinos.cn>
Two receive-path comments misspell "aggressively" as "agressively".
Signed-off-by: xiongweimin <xiongweimin@kylinos.cn>
---
drivers/net/virtio_net.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index 3e2a5876c..f3c7b28ce 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -3190,7 +3190,7 @@ static int virtnet_open(struct net_device *dev)
for (i = 0; i < vi->max_queue_pairs; i++) {
if (i < vi->curr_queue_pairs)
- /* Pre-fill rq agressively, to make sure we are ready to
+ /* Pre-fill rq aggressively, to make sure we are ready to
* get packets immediately.
*/
try_fill_recv(vi, &vi->rq[i], GFP_KERNEL);
@@ -3419,7 +3419,7 @@ static void virtnet_rx_resume(struct virtnet_info *vi,
bool refill)
{
if (netif_running(vi->dev)) {
- /* Pre-fill rq agressively, to make sure we are ready to get
+ /* Pre-fill rq aggressively, to make sure we are ready to get
* packets immediately.
*/
if (refill)
--
2.43.0
No virus found
Checked by Hillstone Network AntiVirus
^ permalink raw reply related
* [PATCH net v3 3/3] net: stmmac: reset residual action in L3L4 filters on delete
From: muhammad.nazim.amirul.nazle.asmade @ 2026-07-14 2:37 UTC (permalink / raw)
To: netdev
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, rmk+kernel,
maxime.chevallier, Jose.Abreu, linux-kernel
In-Reply-To: <20260714023716.29865-1-muhammad.nazim.amirul.nazle.asmade@altera.com>
From: Nazim Amirul <muhammad.nazim.amirul.nazle.asmade@altera.com>
When deleting an L3/L4 flower filter entry, the action field is not
reset. If a filter was previously configured with a drop action, that
action may persist and affect subsequent filter configurations
unintentionally.
Clear the action field when the filter entry is deleted.
Fixes: 425eabddaf0f ("net: stmmac: Implement L3/L4 Filters using TC Flower")
Signed-off-by: Rohan G Thomas <rohan.g.thomas@altera.com>
Signed-off-by: Nazim Amirul <muhammad.nazim.amirul.nazle.asmade@altera.com>
---
drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c
index 1f8c9f47306b..14cabe76e53e 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c
@@ -661,6 +661,7 @@ static int tc_del_flow(struct stmmac_priv *priv,
entry->in_use = false;
entry->cookie = 0;
entry->is_l4 = false;
+ entry->action = 0;
return ret;
}
--
2.43.7
^ permalink raw reply related
* [PATCH net v3 2/3] net: stmmac: fix l3l4 filter rejecting unsupported offload requests
From: muhammad.nazim.amirul.nazle.asmade @ 2026-07-14 2:37 UTC (permalink / raw)
To: netdev
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, rmk+kernel,
maxime.chevallier, Jose.Abreu, linux-kernel
In-Reply-To: <20260714023716.29865-1-muhammad.nazim.amirul.nazle.asmade@altera.com>
From: Nazim Amirul <muhammad.nazim.amirul.nazle.asmade@altera.com>
The basic flow parser in tc_add_basic_flow() does not validate match
keys before proceeding. Unsupported offload configurations such as
partial protocol masks, non-IPv4 network proto, or non-TCP/UDP transport
proto are silently accepted instead of returning -EOPNOTSUPP.
Add validation to return -EOPNOTSUPP early for:
- No network or transport proto present in the key
- Partial protocol mask (only full mask supported)
- Network proto is not IPv4
- Transport proto is not TCP or UDP
Each rejection includes an extack message so the user knows which part
of the match is unsupported.
Also propagate -EOPNOTSUPP from tc_add_basic_flow() in tc_add_flow()
by returning it directly rather than using break. The break was silently
discarding the error for FLOW_CLS_REPLACE operations where entry->in_use
is already true, causing tc_add_flow() to return 0 (success) for
unsupported replace requests.
Fixes: 425eabddaf0f ("net: stmmac: Implement L3/L4 Filters using TC Flower")
Signed-off-by: Rohan G Thomas <rohan.g.thomas@altera.com>
Signed-off-by: Nazim Amirul <muhammad.nazim.amirul.nazle.asmade@altera.com>
---
.../net/ethernet/stmicro/stmmac/stmmac_tc.c | 34 +++++++++++++++++++
1 file changed, 34 insertions(+)
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c
index d78652718599..1f8c9f47306b 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c
@@ -446,6 +446,7 @@ static int tc_parse_flow_actions(struct stmmac_priv *priv,
}
#define ETHER_TYPE_FULL_MASK cpu_to_be16(~0)
+#define IP_PROTO_FULL_MASK 0xFF
static int tc_add_basic_flow(struct stmmac_priv *priv,
struct flow_cls_offload *cls,
@@ -461,6 +462,37 @@ static int tc_add_basic_flow(struct stmmac_priv *priv,
flow_rule_match_basic(rule, &match);
+ /* Both network proto and transport proto not present in the key */
+ if (!match.mask || !(match.mask->n_proto || match.mask->ip_proto)) {
+ NL_SET_ERR_MSG_MOD(cls->common.extack,
+ "filter must specify network or transport protocol");
+ return -EOPNOTSUPP;
+ }
+
+ /* If the proto is present in the key and is not full mask */
+ if ((match.mask->n_proto && match.mask->n_proto != ETHER_TYPE_FULL_MASK) ||
+ (match.mask->ip_proto && match.mask->ip_proto != IP_PROTO_FULL_MASK)) {
+ NL_SET_ERR_MSG_MOD(cls->common.extack,
+ "only full protocol mask is supported");
+ return -EOPNOTSUPP;
+ }
+
+ /* Network proto is present in the key and is not IPv4 */
+ if (match.mask->n_proto && match.key->n_proto != cpu_to_be16(ETH_P_IP)) {
+ NL_SET_ERR_MSG_MOD(cls->common.extack,
+ "only IPv4 network protocol is supported");
+ return -EOPNOTSUPP;
+ }
+
+ /* Transport proto is present in the key and is not TCP or UDP */
+ if (match.mask->ip_proto &&
+ match.key->ip_proto != IPPROTO_TCP &&
+ match.key->ip_proto != IPPROTO_UDP) {
+ NL_SET_ERR_MSG_MOD(cls->common.extack,
+ "only TCP and UDP transport protocols are supported");
+ return -EOPNOTSUPP;
+ }
+
entry->ip_proto = match.key->ip_proto;
return 0;
}
@@ -598,6 +630,8 @@ static int tc_add_flow(struct stmmac_priv *priv,
ret = tc_flow_parsers[i].fn(priv, cls, entry);
if (!ret)
entry->in_use = true;
+ else if (ret == -EOPNOTSUPP)
+ return ret;
}
if (!entry->in_use)
--
2.43.7
^ permalink raw reply related
* [PATCH net v3 1/3] net: stmmac: xgmac: fix l4 filter port overwrite on register update
From: muhammad.nazim.amirul.nazle.asmade @ 2026-07-14 2:37 UTC (permalink / raw)
To: netdev
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, rmk+kernel,
maxime.chevallier, Jose.Abreu, linux-kernel
In-Reply-To: <20260714023716.29865-1-muhammad.nazim.amirul.nazle.asmade@altera.com>
From: Nazim Amirul <muhammad.nazim.amirul.nazle.asmade@altera.com>
The XGMAC_L4_ADDR register holds both source and destination port
match values. The current implementation overwrites the entire register
when configuring either port, so setting one silently erases the other.
Fix this by reading the register first, then masking and updating only
the relevant field before writing back.
Fixes: 425eabddaf0f ("net: stmmac: Implement L3/L4 Filters using TC Flower")
Signed-off-by: Rohan G Thomas <rohan.g.thomas@altera.com>
Signed-off-by: Nazim Amirul <muhammad.nazim.amirul.nazle.asmade@altera.com>
---
.../ethernet/stmicro/stmmac/dwxgmac2_core.c | 28 +++++++++++--------
1 file changed, 16 insertions(+), 12 deletions(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c
index f02b434bbd50..52054f31376d 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c
@@ -1370,36 +1370,40 @@ static int dwxgmac2_config_l4_filter(struct mac_device_info *hw, u32 filter_no,
value &= ~XGMAC_L4PEN0;
}
- value &= ~(XGMAC_L4SPM0 | XGMAC_L4SPIM0);
- value &= ~(XGMAC_L4DPM0 | XGMAC_L4DPIM0);
if (sa) {
value |= XGMAC_L4SPM0;
if (inv)
value |= XGMAC_L4SPIM0;
+ else
+ value &= ~XGMAC_L4SPIM0;
} else {
value |= XGMAC_L4DPM0;
if (inv)
value |= XGMAC_L4DPIM0;
+ else
+ value &= ~XGMAC_L4DPIM0;
}
ret = dwxgmac2_filter_write(hw, filter_no, XGMAC_L3L4_CTRL, value);
if (ret)
return ret;
- if (sa) {
- value = FIELD_PREP(XGMAC_L4SP0, match);
+ ret = dwxgmac2_filter_read(hw, filter_no, XGMAC_L4_ADDR, &value);
+ if (ret)
+ return ret;
- ret = dwxgmac2_filter_write(hw, filter_no, XGMAC_L4_ADDR, value);
- if (ret)
- return ret;
+ if (sa) {
+ value &= ~XGMAC_L4SP0;
+ value |= FIELD_PREP(XGMAC_L4SP0, match);
} else {
- value = FIELD_PREP(XGMAC_L4DP0, match);
-
- ret = dwxgmac2_filter_write(hw, filter_no, XGMAC_L4_ADDR, value);
- if (ret)
- return ret;
+ value &= ~XGMAC_L4DP0;
+ value |= FIELD_PREP(XGMAC_L4DP0, match);
}
+ ret = dwxgmac2_filter_write(hw, filter_no, XGMAC_L4_ADDR, value);
+ if (ret)
+ return ret;
+
if (!en)
return dwxgmac2_filter_write(hw, filter_no, XGMAC_L3L4_CTRL, 0);
--
2.43.7
^ permalink raw reply related
* [PATCH net v3 0/3] net: stmmac: L3/L4 filter bug fixes
From: muhammad.nazim.amirul.nazle.asmade @ 2026-07-14 2:37 UTC (permalink / raw)
To: netdev
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, rmk+kernel,
maxime.chevallier, Jose.Abreu, linux-kernel
In-Reply-To: <20260714023716.29865-1-muhammad.nazim.amirul.nazle.asmade@altera.com>
From: Nazim Amirul <muhammad.nazim.amirul.nazle.asmade@altera.com>
This series fixes three bugs in the stmmac L3/L4 TC flower filter
implementation for the XGMAC2 core. All three patches target net.
The L3/L4 filter match count statistics patch (originally patch 4/4)
has been split out and will be sent separately against net-next per
Andrew Lunn's review of v1.
Patch 1 fixes a register corruption bug in the L4 filter port configuration.
The XGMAC_L4_ADDR register holds both source and destination port match
values in a single register. The original code overwrites the entire register
when setting either field, silently erasing the other. This is fixed by
using a read-modify-write sequence.
Patch 2 fixes the basic flow match parser to properly reject unsupported
offload requests with -EOPNOTSUPP instead of silently accepting them.
Unsupported cases include partial protocol masks, non-IPv4 network proto,
and non-TCP/UDP transport proto. Extack messages are now included so users
know exactly which part of the match is unsupported. The -EOPNOTSUPP is
also now returned directly instead of using break, which was silently
discarding the error on FLOW_CLS_REPLACE operations.
Patch 3 fixes a stale action bug on filter deletion. When a filter entry
with a drop action is deleted, the action field was not reset, causing
it to persist and potentially affect subsequent filter configurations.
All three patches fix the original L3/L4 filter implementation introduced in
425eabddaf0f ("net: stmmac: Implement L3/L4 Filters using TC Flower").
Changes in v3:
- Patch 2: add extack messages to each -EOPNOTSUPP return (Jakub Kicinski)
- Patch 2: return -EOPNOTSUPP directly instead of break to avoid silently
reporting success on unsupported FLOW_CLS_REPLACE (Sashiko review)
Changes in v2:
- Split patch 4/4 (ethtool stats) out to net-next per Andrew Lunn's review
Nazim Amirul (3):
net: stmmac: xgmac: fix l4 filter port overwrite on register update
net: stmmac: fix l3l4 filter rejecting unsupported offload requests
net: stmmac: reset residual action in L3L4 filters on delete
.../ethernet/stmicro/stmmac/dwxgmac2_core.c | 28 ++++++++-------
.../net/ethernet/stmicro/stmmac/stmmac_tc.c | 35 +++++++++++++++++++
2 files changed, 51 insertions(+), 12 deletions(-)
--
2.43.7
^ permalink raw reply
* [PATCH net v3 0/3] net: stmmac: L3/L4 filter bug fixes
From: muhammad.nazim.amirul.nazle.asmade @ 2026-07-14 2:37 UTC (permalink / raw)
To: netdev
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, rmk+kernel,
maxime.chevallier, Jose.Abreu, linux-kernel
From: Nazim Amirul <muhammad.nazim.amirul.nazle.asmade@altera.com>
This series fixes three bugs in the stmmac L3/L4 TC flower filter
implementation for the XGMAC2 core. All three patches target net.
The L3/L4 filter match count statistics patch (originally patch 4/4)
has been split out and will be sent separately against net-next per
Andrew Lunn's review of v1.
Patch 1 fixes a register corruption bug in the L4 filter port configuration.
The XGMAC_L4_ADDR register holds both source and destination port match
values in a single register. The original code overwrites the entire register
when setting either field, silently erasing the other. This is fixed by
using a read-modify-write sequence.
Patch 2 fixes the basic flow match parser to properly reject unsupported
offload requests with -EOPNOTSUPP instead of silently accepting them.
Unsupported cases include partial protocol masks, non-IPv4 network proto,
and non-TCP/UDP transport proto. Extack messages are now included so users
know exactly which part of the match is unsupported. The -EOPNOTSUPP is
also now returned directly instead of using break, which was silently
discarding the error on FLOW_CLS_REPLACE operations.
Patch 3 fixes a stale action bug on filter deletion. When a filter entry
with a drop action is deleted, the action field was not reset, causing
it to persist and potentially affect subsequent filter configurations.
All three patches fix the original L3/L4 filter implementation introduced in
425eabddaf0f ("net: stmmac: Implement L3/L4 Filters using TC Flower").
Changes in v3:
- Patch 2: add extack messages to each -EOPNOTSUPP return (Jakub Kicinski)
- Patch 2: return -EOPNOTSUPP directly instead of break to avoid silently
reporting success on unsupported FLOW_CLS_REPLACE (Sashiko review)
Changes in v2:
- Split patch 4/4 (ethtool stats) out to net-next per Andrew Lunn's review
Nazim Amirul (3):
net: stmmac: xgmac: fix l4 filter port overwrite on register update
net: stmmac: fix l3l4 filter rejecting unsupported offload requests
net: stmmac: reset residual action in L3L4 filters on delete
.../ethernet/stmicro/stmmac/dwxgmac2_core.c | 28 ++++++++-------
.../net/ethernet/stmicro/stmmac/stmmac_tc.c | 35 +++++++++++++++++++
2 files changed, 51 insertions(+), 12 deletions(-)
--
2.43.7
^ permalink raw reply
* Re: [PATCH v3 0/3] net: stmmac: L3/L4 filter bug fixes
From: Nazle Asmade, Muhammad Nazim Amirul @ 2026-07-14 2:33 UTC (permalink / raw)
To: Maxime Chevallier, netdev@vger.kernel.org
Cc: andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com,
kuba@kernel.org, pabeni@redhat.com, rmk+kernel@armlinux.org.uk,
Jose.Abreu@synopsys.com, linux-kernel@vger.kernel.org
In-Reply-To: <7a861f08-6bfc-4ed8-b8ca-9f4317dde630@bootlin.com>
On 1/7/2026 3:06 pm, Maxime Chevallier wrote:
> Hi,
>
> On 6/30/26 13:56, muhammad.nazim.amirul.nazle.asmade@altera.com wrote:
>> From: Nazim Amirul <muhammad.nazim.amirul.nazle.asmade@altera.com>
>>
>> This series fixes three bugs in the stmmac L3/L4 TC flower filter
>> implementation for the XGMAC2 core. All three patches target net.
>
> A quick note on that, I noticed all your recent series on stmmac are
> missing the tree tag in the subject line. It should be something like
>
> [PATCH net v3 0/3] net: stmmac: L3/L4 filter bug fixes
>
> you can add it when generating patches with :
>
> git format-patch --subject-prefix='PATCH net-next' start..finish
>
> cf https://docs.kernel.org/process/maintainer-netdev.html#indicating-target-tree
>
> You can also use b4 for this.
>
> Thanks,
>
> Maxime
>
Hi Maxime,
Ok let me repost it with the fix.
BR,
Nazim
^ permalink raw reply
* Re: [PATCH rdma-next 08/13] RDMA/cgroup: Scope rdma cgroup device visibility to the net namespace
From: Tao Cui @ 2026-07-14 2:28 UTC (permalink / raw)
To: Jiri Pirko, Michal Koutný
Cc: cui.tao, linux-rdma, cgroups, netdev, linux-s390, linux-kselftest,
jgg, leon, parav, mbloch, cmeiohas, roman.gushchin, bvanassche,
zyjzyj2000, shuah, tj, hannes, alibuda, dust.li, sidraya, wenjia
In-Reply-To: <alSxB0wziQnNuyfn@FV6GYCPJ69>
在 2026/7/13 17:34, Jiri Pirko 写道:
> Thu, Jul 09, 2026 at 03:04:23PM +0200, mkoutny@suse.com wrote:
>> Hi.
>>
>> On Thu, Jul 09, 2026 at 11:55:27AM +0200, Jiri Pirko <jiri@resnulli.us> wrote:
>>> index 993446ab66d0..4523c1884d67 100644
>>> --- a/Documentation/admin-guide/cgroup-v2.rst
>>> +++ b/Documentation/admin-guide/cgroup-v2.rst
>>> @@ -2752,6 +2752,13 @@ RDMA
>>> The "rdma" controller regulates the distribution and accounting of
>>> RDMA resources.
>>>
>>> +When RDMA devices are isolated per network namespace (exclusive mode),
>>> +device names are unique only within a network namespace. The device lines
>>> +below are therefore scoped to the reading or writing process's network
>>> +namespace: only devices accessible from that namespace are listed, and a
>>> +limit is applied to the device of that name in that namespace. Configure
>>> +limits from the same network namespace as the workloads.
>>
>> OK.
>>
>>> --- a/include/linux/cgroup_rdma.h
>>> +++ b/include/linux/cgroup_rdma.h
>>> @@ -7,6 +7,7 @@
>>> #define _CGROUP_RDMA_H
>>>
>>> #include <linux/cgroup.h>
>>> +#include <net/net_namespace.h>
>>>
>>> enum rdmacg_resource_type {
>>> RDMACG_RESOURCE_HCA_HANDLE,
>>> @@ -34,6 +35,15 @@ struct rdmacg_device {
>>> struct list_head dev_node;
>>> struct list_head rpools;
>>> char *name;
>>> + /*
>>> + * Net namespace the device belongs to. @netns_shared mirrors
>>> + * ib_devices_shared_netns: when true the device is visible from every
>>> + * net namespace (shared mode); otherwise @net is the only namespace
>>> + * that may see and configure it. @netns_shared is updated when the
>>> + * sharing mode changes, so use {READ,WRITE}_ONCE() to access it.
>>> + */
>>> + possible_net_t net;
>>> + bool netns_shared;
>>
>> Any reason to store the netns_shared split per device? (IIUC, it's a
>> global parameter.)
>
> No reason, changed.
>
Hi Jiri,
A question on the v2 you mentioned to Michal.
Once netns_shared stops being cached per rdmacg_device,
rdmacg_device_visible() in kernel/cgroup/rdma.c still needs the current
sharing mode, whose authoritative value lives in the IB core
(ib_devices_shared_netns). How do you plan to expose it there without
the generic cgroup controller reaching back into drivers/infiniband/?
Exporting the global, or keeping an IB-side update hook, both feel a bit
awkward; it would be good to see which direction you took.
On the mechanism itself: it's the right call that rdmacg_try_charge()
stays out of the scoping. Charging takes the rdmacg_device pointer
directly (no name lookup), and a task can only charge a device it
already holds a handle to, so applying visibility there would be wrong.
The scoping deliberately touches only the name-based lookup (the write
path) and the enumeration (read/show) paths -- worth keeping that
invariant in mind so a later patch doesn't grow the filter.
Thanks,
Tao> Thanks!
>
>>
>> Thanks,
>> Michal
>
>
>
^ 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