From: Loic Poulain <loic.poulain@oss.qualcomm.com>
The WWAN core unregisters the device when it has no remaining WWAN ops
or child devices. For NMEA port types, the child is registered under
the GNSS class instead of WWAN, so the core incorrectly assumes there
are no children and unregisters the WWAN device too early. This leads
to a second unregister attempt after the NMEA device is removed.
To fix this issue, we register a virtual WWAN port device along the
GNSS device, this ensures the WWAN device remains registered until
all associated ports, including NMEA, are properly removed.
Reported-by: Daniele Palmas <dnlplm@gmail.com>
Closes: https://lore.kernel.org/netdev/CAGRyCJE28yf-rrfkFbzu44ygLEvoUM7fecK1vnrghjG_e9UaRA@mail.gmail.com/
Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
---
drivers/net/wwan/wwan_core.c | 24 +++++++++++++++---------
1 file changed, 15 insertions(+), 9 deletions(-)
diff --git a/drivers/net/wwan/wwan_core.c b/drivers/net/wwan/wwan_core.c
index 453ff259809c..df35b188cf6f 100644
--- a/drivers/net/wwan/wwan_core.c
+++ b/drivers/net/wwan/wwan_core.c
@@ -456,7 +456,7 @@ static int __wwan_port_dev_assign_name(struct wwan_port *port, const char *fmt)
}
/* Register a regular WWAN port device (e.g. AT, MBIM, etc.) */
-static int wwan_port_register_wwan(struct wwan_port *port)
+static int wwan_port_register_wwan(struct wwan_port *port, bool cdev)
{
struct wwan_device *wwandev = to_wwan_dev(port->dev.parent);
char namefmt[0x20];
@@ -468,7 +468,8 @@ static int wwan_port_register_wwan(struct wwan_port *port)
return minor;
port->dev.class = &wwan_class;
- port->dev.devt = MKDEV(wwan_major, minor);
+ if (cdev)
+ port->dev.devt = MKDEV(wwan_major, minor);
/* allocate unique name based on wwan device id, port type and number */
snprintf(namefmt, sizeof(namefmt), "wwan%u%s%%d", wwandev->id,
@@ -626,6 +627,7 @@ struct wwan_port *wwan_create_port(struct device *parent,
struct wwan_port_caps *caps,
void *drvdata)
{
+ bool cdev = (type == WWAN_PORT_NMEA) ? false : true;
struct wwan_device *wwandev;
struct wwan_port *port;
int err;
@@ -660,16 +662,20 @@ struct wwan_port *wwan_create_port(struct device *parent,
dev_set_drvdata(&port->dev, drvdata);
device_initialize(&port->dev);
- if (port->type == WWAN_PORT_NMEA)
- err = wwan_port_register_gnss(port);
- else
- err = wwan_port_register_wwan(port);
-
+ err = wwan_port_register_wwan(port, cdev);
if (err)
goto error_put_device;
+ if (type == WWAN_PORT_NMEA) {
+ err = wwan_port_register_gnss(port);
+ if (err)
+ goto error_port_unregister;
+ }
+
return port;
+error_port_unregister:
+ wwan_port_unregister_wwan(port);
error_put_device:
put_device(&port->dev);
error_wwandev_remove:
@@ -696,8 +702,8 @@ void wwan_remove_port(struct wwan_port *port)
if (port->type == WWAN_PORT_NMEA)
wwan_port_unregister_gnss(port);
- else
- wwan_port_unregister_wwan(port);
+
+ wwan_port_unregister_wwan(port);
put_device(&port->dev);
--
2.25.1
Hi Slark, Loic, sorry for late joining the discussion, please find a design question below. On 1/5/26 12:20, Slark Xiao wrote: > From: Loic Poulain <loic.poulain@oss.qualcomm.com> > > The WWAN core unregisters the device when it has no remaining WWAN ops > or child devices. For NMEA port types, the child is registered under > the GNSS class instead of WWAN, so the core incorrectly assumes there > are no children and unregisters the WWAN device too early. This leads > to a second unregister attempt after the NMEA device is removed. > > To fix this issue, we register a virtual WWAN port device along the > GNSS device, this ensures the WWAN device remains registered until > all associated ports, including NMEA, are properly removed. wwan core assumes whole responsibility for managing a WWAN device. We already use wwan_create_dev()/wwan_remove_dev() everywhere. But, we are checking the reminding references in an implicit way using device_for_each_child() and registered OPS existence. Thus, we need this trick with a virtual child port. Does it make sense to switch to an explicit reference counting? We can introduce such counter to the wwan_device structure, and increment/decrement it on every wwan_create_dev()/wwan_remove_dev() call. So, we will do device_unregister() upon reference number becoming zero. If it sounds promising, I can send a RFC, let's say, tomorrow. -- Sergey
At 2026-01-07 09:06:05, "Sergey Ryazanov" <ryazanov.s.a@gmail.com> wrote: >Hi Slark, Loic, > >sorry for late joining the discussion, please find a design question below. > >On 1/5/26 12:20, Slark Xiao wrote: >> From: Loic Poulain <loic.poulain@oss.qualcomm.com> >> >> The WWAN core unregisters the device when it has no remaining WWAN ops >> or child devices. For NMEA port types, the child is registered under >> the GNSS class instead of WWAN, so the core incorrectly assumes there >> are no children and unregisters the WWAN device too early. This leads >> to a second unregister attempt after the NMEA device is removed. >> >> To fix this issue, we register a virtual WWAN port device along the >> GNSS device, this ensures the WWAN device remains registered until >> all associated ports, including NMEA, are properly removed. > >wwan core assumes whole responsibility for managing a WWAN device. We >already use wwan_create_dev()/wwan_remove_dev() everywhere. But, we are >checking the reminding references in an implicit way using >device_for_each_child() and registered OPS existence. Thus, we need this >trick with a virtual child port. > >Does it make sense to switch to an explicit reference counting? We can >introduce such counter to the wwan_device structure, and >increment/decrement it on every wwan_create_dev()/wwan_remove_dev() >call. So, we will do device_unregister() upon reference number becoming >zero. > >If it sounds promising, I can send a RFC, let's say, tomorrow. The RFC only for this patch or the existing design? Since there is problem reported in https://patchwork.kernel.org/project/netdevbpf/patch/20260105102018.62731-3-slark_xiao@163.com/#26720828. Currently design: minor = ida_alloc_range(&minors, 0, WWAN_MAX_MINORS - 1, GFP_KERNEL); if (minor < 0) return minor; port->dev.class = &wwan_class; // when cdev is false, no devt was assigned. But wwan_port_destroy() use devt to free if (cdev) port->dev.devt = MKDEV(wwan_major, minor); We need to have a update based on this patch if we want to use this one in this serial.
On January 7, 2026 9:29:24 AM, Slark Xiao <slark_xiao@163.com> wrote: >At 2026-01-07 09:06:05, "Sergey Ryazanov" <ryazanov.s.a@gmail.com> wrote: >>Hi Slark, Loic, >> >>sorry for late joining the discussion, please find a design question below. >> >>On 1/5/26 12:20, Slark Xiao wrote: >>> From: Loic Poulain <loic.poulain@oss.qualcomm.com> >>> >>> The WWAN core unregisters the device when it has no remaining WWAN ops >>> or child devices. For NMEA port types, the child is registered under >>> the GNSS class instead of WWAN, so the core incorrectly assumes there >>> are no children and unregisters the WWAN device too early. This leads >>> to a second unregister attempt after the NMEA device is removed. >>> >>> To fix this issue, we register a virtual WWAN port device along the >>> GNSS device, this ensures the WWAN device remains registered until >>> all associated ports, including NMEA, are properly removed. >> >>wwan core assumes whole responsibility for managing a WWAN device. We >>already use wwan_create_dev()/wwan_remove_dev() everywhere. But, we are >>checking the reminding references in an implicit way using >>device_for_each_child() and registered OPS existence. Thus, we need this >>trick with a virtual child port. >> >>Does it make sense to switch to an explicit reference counting? We can >>introduce such counter to the wwan_device structure, and >>increment/decrement it on every wwan_create_dev()/wwan_remove_dev() >>call. So, we will do device_unregister() upon reference number becoming >>zero. >> >>If it sounds promising, I can send a RFC, let's say, tomorrow. > >The RFC only for this patch or the existing design? Since there is problem >reported in https://patchwork.kernel.org/project/netdevbpf/patch/20260105102018.62731-3-slark_xiao@163.com/#26720828. > >Currently design: > minor = ida_alloc_range(&minors, 0, WWAN_MAX_MINORS - 1, GFP_KERNEL); > if (minor < 0) > return minor; > > port->dev.class = &wwan_class; >// when cdev is false, no devt was assigned. But wwan_port_destroy() use devt to free > if (cdev) > port->dev.devt = MKDEV(wwan_major, minor); > >We need to have a update based on this patch if we want to use this one in this serial. The proposed idea for the WWAN device release will entirely substitute this patch. So, all these issues with the virtual stub port creation should gone as well. -- Sergey
© 2016 - 2026 Red Hat, Inc.