* [PATCH net v4] net: qualcomm: rmnet: Update MTU handling during format changes
@ 2026-11-08 7:57 Subash Abhinov Kasiviswanathan
2026-11-08 8:02 ` netdev-bot+sinfo
0 siblings, 1 reply; 3+ messages in thread
From: Subash Abhinov Kasiviswanathan @ 2026-11-08 7:57 UTC (permalink / raw)
To: sean.tranchetti, andrew+netdev, davem, edumazet, kuba, pabeni
Cc: netdev, linux-kernel, Subash Abhinov Kasiviswanathan
rmnet derives the MTU of its devices from the real device MTU minus the
headroom needed by the egress data format. Several problems exist in
how this is handled when the data format is set or changed:
- The headroom only accounts for the V4 egress checksum header and
ignores the V5 one, so devices using V5 checksum offload get an MTU
that is too large for the real device.
- rmnet_newlink() sizes the first device from the port's data format
before the requested IFLA_RMNET_FLAGS have been applied, so the MTU
does not match the format that ends up being used.
- A real device MTU smaller than the headroom underflows the unsigned
subtraction, and a real device with a jumbo MTU makes the creation of
the first device fail while resizing an existing one clamps it.
- Existing endpoints are shrunk by writing dev->mtu directly. No MTU
notifiers run and userspace gets no link notification. The helper
also returns -1, and changelink flattens every failure into -EINVAL.
- rmnet_changelink() publishes the new data format before resizing the
endpoints and reverts it on failure, so the datapath can observe a
format whose headers do not fit the MTUs. A failed resize can also
leave some endpoints shrunk, and a mux_id change requested in the
same call is kept even though the call returned an error.
Fix these by calculating the headroom from the requested data format,
including the V5 checksum header, and passing that format through the
MTU helpers so that new and existing devices use the same calculation.
Apply IFLA_RMNET_FLAGS before sizing the first device, reject real
device MTUs below the headroom before doing any arithmetic, and clamp
the target to RMNET_MAX_PACKET_SIZE for both new and existing devices.
Resize existing endpoints with dev_set_mtu() so that the normal MTU
notifiers and userspace link notifications are delivered. If resizing
an endpoint fails, restore the endpoints which were already resized and
ignore any errors from the restore, and return the error to the caller
with a message that is not specific to the real device MTU.
Publish the new shared data format only after all MTUs have been
updated, and in rmnet_changelink() move the endpoint to a new mux_id
only after the checks and the MTU update have succeeded, so that a
failed request does not leave a partial change behind.
Fixes: b7f5eb6ba21b ("net: qualcomm: rmnet: Update rmnet device MTU based on real device")
Fixes: b6e5d27e32ef ("net: ethernet: rmnet: Add support for MAPv5 egress packets")
Assisted-by: LLM
Co-developed-by: Sean Tranchetti <sean.tranchetti@oss.qualcomm.com>
Signed-off-by: Sean Tranchetti <sean.tranchetti@oss.qualcomm.com>
Signed-off-by: Subash Abhinov Kasiviswanathan <subash.a.kasiviswanathan@oss.qualcomm.com>
---
v4: Separated patch from the coalescing series into a standalone fix for
mtu behavior. This addresses the comments raised by Sashiko to honor the
room required for MAPv5 format, better aritmetic handling and more
checks for cases where the real device MTU exceeds RMNET_MAX_PACKET_SIZE.
v3: https://lore.kernel.org/all/20261009084351.317632-2-subash.a.kasiviswanathan@oss.qualcomm.com/
v2: https://lore.kernel.org/all/20261008005543.2630828-2-subash.a.kasiviswanathan@oss.qualcomm.com/
.../ethernet/qualcomm/rmnet/rmnet_config.c | 77 +++++++-----
.../ethernet/qualcomm/rmnet/rmnet_config.h | 1 +
.../net/ethernet/qualcomm/rmnet/rmnet_vnd.c | 110 +++++++++++++-----
.../net/ethernet/qualcomm/rmnet/rmnet_vnd.h | 4 +-
4 files changed, 133 insertions(+), 59 deletions(-)
diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c
index 61b04c6c0390..9e66f3804de4 100644
--- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c
+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c
@@ -143,6 +143,14 @@ static int rmnet_newlink(struct net_device *dev,
return -ENODEV;
}
+ if (data[IFLA_RMNET_FLAGS]) {
+ struct ifla_rmnet_flags *flags;
+
+ flags = nla_data(data[IFLA_RMNET_FLAGS]);
+ data_format &= ~flags->mask;
+ data_format |= flags->flags & flags->mask;
+ }
+
ep = kzalloc_obj(*ep);
if (!ep)
return -ENOMEM;
@@ -154,7 +162,8 @@ static int rmnet_newlink(struct net_device *dev,
goto err0;
port = rmnet_get_port_rtnl(real_dev);
- err = rmnet_vnd_newlink(mux_id, dev, port, real_dev, ep, extack);
+ err = rmnet_vnd_newlink(mux_id, dev, port, real_dev, ep, extack,
+ data_format);
if (err)
goto err1;
@@ -162,24 +171,25 @@ static int rmnet_newlink(struct net_device *dev,
if (err < 0)
goto err2;
+ /* Update existing MTUs before publishing the shared data format. */
+ err = rmnet_vnd_update_dev_mtu(port, real_dev, data_format);
+ if (err) {
+ NL_SET_ERR_MSG_MOD(extack, "Failed to update MTU of rmnet devices");
+ goto err3;
+ }
+
port->rmnet_mode = mode;
port->rmnet_dev = dev;
hlist_add_head_rcu(&ep->hlnode, &port->muxed_ep[mux_id]);
- if (data[IFLA_RMNET_FLAGS]) {
- struct ifla_rmnet_flags *flags;
-
- flags = nla_data(data[IFLA_RMNET_FLAGS]);
- data_format &= ~flags->mask;
- data_format |= flags->flags & flags->mask;
- }
-
netdev_dbg(dev, "data format [0x%08X]\n", data_format);
WRITE_ONCE(port->data_format, data_format);
return 0;
+err3:
+ netdev_upper_dev_unlink(real_dev, dev);
err2:
unregister_netdevice(dev);
rmnet_vnd_dellink(mux_id, port, ep);
@@ -301,9 +311,14 @@ static int rmnet_changelink(struct net_device *dev, struct nlattr *tb[],
struct netlink_ext_ack *extack)
{
struct rmnet_priv *priv = netdev_priv(dev);
+ struct rmnet_endpoint *ep = NULL;
+ struct ifla_rmnet_flags *flags;
struct net_device *real_dev;
struct rmnet_port *port;
+ u32 old_data_format;
+ u32 data_format;
u16 mux_id;
+ int err;
if (!dev)
return -ENODEV;
@@ -320,12 +335,17 @@ static int rmnet_changelink(struct net_device *dev, struct nlattr *tb[],
port = rmnet_get_port_rtnl(real_dev);
+ if (data[IFLA_RMNET_FLAGS]) {
+ old_data_format = READ_ONCE(port->data_format);
+ flags = nla_data(data[IFLA_RMNET_FLAGS]);
+ data_format = old_data_format & ~flags->mask;
+ data_format |= flags->flags & flags->mask;
+ }
+
if (data[IFLA_RMNET_MUX_ID]) {
mux_id = nla_get_u16(data[IFLA_RMNET_MUX_ID]);
if (mux_id != priv->mux_id) {
- struct rmnet_endpoint *ep;
-
ep = rmnet_get_endpoint(port, priv->mux_id);
if (!ep)
return -ENODEV;
@@ -335,32 +355,29 @@ static int rmnet_changelink(struct net_device *dev, struct nlattr *tb[],
"MUX ID already exists");
return -EINVAL;
}
-
- hlist_del_init_rcu(&ep->hlnode);
- WRITE_ONCE(ep->mux_id, mux_id);
- hlist_add_head_rcu(&ep->hlnode,
- &port->muxed_ep[mux_id]);
-
- WRITE_ONCE(priv->mux_id, mux_id);
}
}
if (data[IFLA_RMNET_FLAGS]) {
- struct ifla_rmnet_flags *flags;
- u32 old_data_format;
- u32 data_format;
+ err = rmnet_vnd_update_dev_mtu(port, real_dev, data_format);
+ if (err) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Failed to update MTU of rmnet devices");
+ return err;
+ }
- old_data_format = port->data_format;
- flags = nla_data(data[IFLA_RMNET_FLAGS]);
- data_format = old_data_format & ~flags->mask;
- data_format |= flags->flags & flags->mask;
WRITE_ONCE(port->data_format, data_format);
+ }
- if (rmnet_vnd_update_dev_mtu(port, real_dev)) {
- WRITE_ONCE(port->data_format, old_data_format);
- NL_SET_ERR_MSG_MOD(extack, "Invalid MTU on real dev");
- return -EINVAL;
- }
+ /* Nothing below can fail, so move the endpoint only after all the
+ * checks and the MTU update have succeeded.
+ */
+ if (ep) {
+ hlist_del_init_rcu(&ep->hlnode);
+ WRITE_ONCE(ep->mux_id, mux_id);
+ hlist_add_head_rcu(&ep->hlnode, &port->muxed_ep[mux_id]);
+
+ WRITE_ONCE(priv->mux_id, mux_id);
}
return 0;
diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h
index 5adda0323dda..47b8db3206b9 100644
--- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h
+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h
@@ -19,6 +19,7 @@ struct rmnet_endpoint {
struct net_device *egress_dev;
struct hlist_node hlnode;
struct rcu_head rcu;
+ int saved_mtu;
};
struct rmnet_egress_agg_params {
diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c
index 5f921cddf82b..dce436e4c630 100644
--- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c
+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c
@@ -66,29 +66,44 @@ static netdev_tx_t rmnet_vnd_start_xmit(struct sk_buff *skb,
return NETDEV_TX_OK;
}
-static int rmnet_vnd_headroom(struct rmnet_port *port)
+static int rmnet_vnd_headroom(u32 data_format)
{
u32 headroom;
headroom = sizeof(struct rmnet_map_header);
- if (port->data_format & RMNET_FLAGS_EGRESS_MAP_CKSUMV4)
+ if (data_format & RMNET_FLAGS_EGRESS_MAP_CKSUMV4)
headroom += sizeof(struct rmnet_map_ul_csum_header);
+ else if (data_format & RMNET_FLAGS_EGRESS_MAP_CKSUMV5)
+ headroom += sizeof(struct rmnet_map_v5_csum_header);
return headroom;
}
-static int rmnet_vnd_change_mtu(struct net_device *rmnet_dev, int new_mtu)
+/* Largest MTU an rmnet device can use on top of real_dev for the given data
+ * format, or -EINVAL if real_dev cannot even fit the headers.
+ */
+static int rmnet_vnd_target_mtu(struct net_device *real_dev, u32 data_format)
{
- struct rmnet_priv *priv = netdev_priv(rmnet_dev);
- struct rmnet_port *port;
u32 headroom;
- port = rmnet_get_port_rtnl(priv->real_dev);
+ headroom = rmnet_vnd_headroom(data_format);
+ if (real_dev->mtu < headroom)
+ return -EINVAL;
+
+ return min_t(int, real_dev->mtu - headroom, RMNET_MAX_PACKET_SIZE);
+}
+
+static int rmnet_vnd_change_mtu_with_format(struct net_device *rmnet_dev,
+ int new_mtu, u32 data_format)
+{
+ struct rmnet_priv *priv = netdev_priv(rmnet_dev);
+ u32 headroom;
- headroom = rmnet_vnd_headroom(port);
+ headroom = rmnet_vnd_headroom(data_format);
- if (new_mtu < 0 || new_mtu > RMNET_MAX_PACKET_SIZE ||
+ if (priv->real_dev->mtu < headroom ||
+ new_mtu < 0 || new_mtu > RMNET_MAX_PACKET_SIZE ||
new_mtu > (priv->real_dev->mtu - headroom))
return -EINVAL;
@@ -96,6 +111,17 @@ static int rmnet_vnd_change_mtu(struct net_device *rmnet_dev, int new_mtu)
return 0;
}
+static int rmnet_vnd_change_mtu(struct net_device *rmnet_dev, int new_mtu)
+{
+ struct rmnet_priv *priv = netdev_priv(rmnet_dev);
+ struct rmnet_port *port;
+
+ port = rmnet_get_port_rtnl(priv->real_dev);
+
+ return rmnet_vnd_change_mtu_with_format(rmnet_dev, new_mtu,
+ READ_ONCE(port->data_format));
+}
+
static int rmnet_vnd_get_iflink(const struct net_device *dev)
{
struct rmnet_priv *priv = netdev_priv(dev);
@@ -308,11 +334,10 @@ int rmnet_vnd_newlink(u8 id, struct net_device *rmnet_dev,
struct rmnet_port *port,
struct net_device *real_dev,
struct rmnet_endpoint *ep,
- struct netlink_ext_ack *extack)
-
+ struct netlink_ext_ack *extack, u32 data_format)
{
struct rmnet_priv *priv = netdev_priv(rmnet_dev);
- u32 headroom;
+ int new_mtu;
int rc;
if (rmnet_get_endpoint(port, id)) {
@@ -326,9 +351,9 @@ int rmnet_vnd_newlink(u8 id, struct net_device *rmnet_dev,
priv->real_dev = real_dev;
- headroom = rmnet_vnd_headroom(port);
-
- if (rmnet_vnd_change_mtu(rmnet_dev, real_dev->mtu - headroom)) {
+ new_mtu = rmnet_vnd_target_mtu(real_dev, data_format);
+ if (new_mtu < 0 ||
+ rmnet_vnd_change_mtu_with_format(rmnet_dev, new_mtu, data_format)) {
NL_SET_ERR_MSG_MOD(extack, "Invalid MTU on real dev");
return -EINVAL;
}
@@ -375,17 +400,17 @@ int rmnet_vnd_do_flow_control(struct net_device *rmnet_dev, int enable)
return 0;
}
-int rmnet_vnd_validate_real_dev_mtu(struct net_device *real_dev)
+static int rmnet_vnd_validate_mtu(struct rmnet_port *port,
+ struct net_device *real_dev, u32 data_format)
{
struct hlist_node *tmp_ep;
struct rmnet_endpoint *ep;
- struct rmnet_port *port;
unsigned long bkt_ep;
u32 headroom;
- port = rmnet_get_port_rtnl(real_dev);
-
- headroom = rmnet_vnd_headroom(port);
+ headroom = rmnet_vnd_headroom(data_format);
+ if (real_dev->mtu < headroom)
+ return -EINVAL;
hash_for_each_safe(port->muxed_ep, bkt_ep, tmp_ep, ep, hlnode) {
if (ep->egress_dev->mtu > (real_dev->mtu - headroom))
@@ -395,24 +420,55 @@ int rmnet_vnd_validate_real_dev_mtu(struct net_device *real_dev)
return 0;
}
+int rmnet_vnd_validate_real_dev_mtu(struct net_device *real_dev)
+{
+ struct rmnet_port *port;
+
+ port = rmnet_get_port_rtnl(real_dev);
+
+ return rmnet_vnd_validate_mtu(port, real_dev,
+ READ_ONCE(port->data_format));
+}
+
int rmnet_vnd_update_dev_mtu(struct rmnet_port *port,
- struct net_device *real_dev)
+ struct net_device *real_dev, u32 data_format)
{
+ struct rmnet_endpoint *fail_ep = NULL;
struct hlist_node *tmp_ep;
struct rmnet_endpoint *ep;
unsigned long bkt_ep;
- u32 headroom;
+ int new_mtu;
+ int err = 0;
- headroom = rmnet_vnd_headroom(port);
+ new_mtu = rmnet_vnd_target_mtu(real_dev, data_format);
+ if (new_mtu < 0)
+ return new_mtu;
hash_for_each_safe(port->muxed_ep, bkt_ep, tmp_ep, ep, hlnode) {
- if (ep->egress_dev->mtu <= (real_dev->mtu - headroom))
+ ep->saved_mtu = ep->egress_dev->mtu;
+ if (ep->saved_mtu <= new_mtu)
continue;
- if (rmnet_vnd_change_mtu(ep->egress_dev,
- real_dev->mtu - headroom))
- return -1;
+ err = dev_set_mtu(ep->egress_dev, new_mtu);
+ if (err) {
+ fail_ep = ep;
+ break;
+ }
}
- return 0;
+ if (!err)
+ return 0;
+
+ /* Restore the endpoints which were already resized. Failures here
+ * are ignored since the original error is the one to report.
+ */
+ hash_for_each_safe(port->muxed_ep, bkt_ep, tmp_ep, ep, hlnode) {
+ if (ep == fail_ep)
+ break;
+
+ if (ep->egress_dev->mtu != ep->saved_mtu)
+ dev_set_mtu(ep->egress_dev, ep->saved_mtu);
+ }
+
+ return err;
}
diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.h
index c2b2baf86894..901ef36900ac 100644
--- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.h
+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.h
@@ -12,7 +12,7 @@ int rmnet_vnd_newlink(u8 id, struct net_device *rmnet_dev,
struct rmnet_port *port,
struct net_device *real_dev,
struct rmnet_endpoint *ep,
- struct netlink_ext_ack *extack);
+ struct netlink_ext_ack *extack, u32 data_format);
int rmnet_vnd_dellink(u8 id, struct rmnet_port *port,
struct rmnet_endpoint *ep);
void rmnet_vnd_rx_fixup(struct sk_buff *skb, struct net_device *dev);
@@ -21,5 +21,5 @@ void rmnet_vnd_tx_fixup(struct sk_buff *skb, struct net_device *dev);
void rmnet_vnd_setup(struct net_device *dev);
int rmnet_vnd_validate_real_dev_mtu(struct net_device *real_dev);
int rmnet_vnd_update_dev_mtu(struct rmnet_port *port,
- struct net_device *real_dev);
+ struct net_device *real_dev, u32 data_format);
#endif /* _RMNET_VND_H_ */
--
2.34.1
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH net v4] net: qualcomm: rmnet: Update MTU handling during format changes
2026-11-08 7:57 [PATCH net v4] net: qualcomm: rmnet: Update MTU handling during format changes Subash Abhinov Kasiviswanathan
@ 2026-11-08 8:02 ` netdev-bot+sinfo
2026-11-08 8:11 ` subash.a.kasiviswanathan
0 siblings, 1 reply; 3+ messages in thread
From: netdev-bot+sinfo @ 2026-11-08 8:02 UTC (permalink / raw)
To: Subash Abhinov Kasiviswanathan
Cc: sean.tranchetti, andrew+netdev, davem, edumazet, kuba, pabeni,
netdev, linux-kernel
Hi!
This is an automated message. This series looks like a fix, but its
commit messages seem to be missing some information:
- Whether the issue was actually triggered, or is only theoretical
(e.g. found by code inspection). If it was triggered please include
the symptoms, like the stack trace or error messages.
- What hardware the change was tested on. For driver fixes please
mention the device (and if relevant firmware version) used for
testing, or say that the change was not tested on real hardware.
Please do not repost the series just to address the above. Instead,
reply to this email with the missing information, so that reviewers
can take it into account. If the series needs another revision for
other reasons, please include the information in the commit messages
then.
The evaluation is done by an LLM so it may be wrong, if you think
that is the case please reply and explain.
^ permalink raw reply [flat|nested] 3+ messages in thread
* RE: [PATCH net v4] net: qualcomm: rmnet: Update MTU handling during format changes
2026-11-08 8:02 ` netdev-bot+sinfo
@ 2026-11-08 8:11 ` subash.a.kasiviswanathan
0 siblings, 0 replies; 3+ messages in thread
From: subash.a.kasiviswanathan @ 2026-11-08 8:11 UTC (permalink / raw)
To: netdev-bot+sinfo
Cc: sean.tranchetti, andrew+netdev, davem, edumazet, kuba, pabeni,
netdev, linux-kernel
> -----Original Message-----
> From: netdev-bot+sinfo@kernel.org <netdev-bot+sinfo@kernel.org>
> Sent: Sunday, November 8, 2026 1:02 AM
> To: Subash Abhinov Kasiviswanathan
> <subash.a.kasiviswanathan@oss.qualcomm.com>
> Cc: sean.tranchetti@oss.qualcomm.com; andrew+netdev@lunn.ch;
> davem@davemloft.net; edumazet@kernel.org; kuba@kernel.org;
> pabeni@redhat.com; netdev@vger.kernel.org; linux-kernel@vger.kernel.org
> Subject: Re: [PATCH net v4] net: qualcomm: rmnet: Update MTU handling
> during format changes
>
> Hi!
>
> This is an automated message. This series looks like a fix, but its commit
> messages seem to be missing some information:
>
> - Whether the issue was actually triggered, or is only theoretical
> (e.g. found by code inspection). If it was triggered please include
> the symptoms, like the stack trace or error messages.
>
> - What hardware the change was tested on. For driver fixes please
> mention the device (and if relevant firmware version) used for
> testing, or say that the change was not tested on real hardware.
>
> Please do not repost the series just to address the above. Instead, reply to this
> email with the missing information, so that reviewers can take it into account.
> If the series needs another revision for other reasons, please include the
> information in the commit messages then.
>
> The evaluation is done by an LLM so it may be wrong, if you think that is the
> case please reply and explain.
The changes in the patch are addressing the code review comments from Sashiko and not tested on real hardware.
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-11-08 8:12 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-08 7:57 [PATCH net v4] net: qualcomm: rmnet: Update MTU handling during format changes Subash Abhinov Kasiviswanathan
2026-11-08 8:02 ` netdev-bot+sinfo
2026-11-08 8:11 ` subash.a.kasiviswanathan
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®