From: Slark Xiao <slark_xiao@163.com>
To: loic.poulain@oss.qualcomm.com, ryazanov.s.a@gmail.com,
johannes@sipsolutions.net, andrew+netdev@lunn.ch,
davem@davemloft.net, edumazet@google.com, kuba@kernel.org,
pabeni@redhat.com, mani@kernel.org
Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
slark_xiao@163.com, Daniele Palmas <dnlplm@gmail.com>
Subject: [net-next v6 2/8] net: wwan: core: explicit WWAN device reference counting
Date: Thu, 15 Jan 2026 17:54:11 +0800 [thread overview]
Message-ID: <20260115095417.36975-3-slark_xiao@163.com> (raw)
In-Reply-To: <20260115095417.36975-1-slark_xiao@163.com>
From: Sergey Ryazanov <ryazanov.s.a@gmail.com>
We need information about existing WWAN device children since we remove
the device after removing the last child. Previously, we tracked users
implicitly by checking whether ops was registered and existence of a
child device of the wwan_class class. Upcoming GNSS (NMEA) port type
support breaks this approach by introducing a child device of the
gnss_class class.
And a modem driver can easily trigger a kernel Oops by removing regular
(e.g., MBIM, AT) ports first and then removing a GNSS port. The WWAN
device will be unregistered on removal of a last regular WWAN port. And
subsequent GNSS port removal will cause NULL pointer dereference in
simple_recursive_removal().
In order to support ports of classes other than wwan_class, switch to
explicit references counting. Introduce a dedicated counter to the WWAN
device struct, increment it on every wwan_create_dev() call, decrement
on wwan_remove_dev(), and actually unregister the WWAN device when there
are no more references.
Run tested with wwan_hwsim with NMEA support patches applied and
different port removing sequences.
Reported-by: Daniele Palmas <dnlplm@gmail.com>
Closes: https://lore.kernel.org/netdev/CAGRyCJE28yf-rrfkFbzu44ygLEvoUM7fecK1vnrghjG_e9UaRA@mail.gmail.com/
Suggested-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
Signed-off-by: Sergey Ryazanov <ryazanov.s.a@gmail.com>
---
drivers/net/wwan/wwan_core.c | 34 +++++++++++++++-------------------
1 file changed, 15 insertions(+), 19 deletions(-)
diff --git a/drivers/net/wwan/wwan_core.c b/drivers/net/wwan/wwan_core.c
index ade8bbffc93e..e631da58c493 100644
--- a/drivers/net/wwan/wwan_core.c
+++ b/drivers/net/wwan/wwan_core.c
@@ -42,6 +42,9 @@ static struct dentry *wwan_debugfs_dir;
* struct wwan_device - The structure that defines a WWAN device
*
* @id: WWAN device unique ID.
+ * @refcount: Reference count of this WWAN device. When this refcount reaches
+ * zero, the device is deleted. NB: access is protected by global
+ * wwan_register_lock mutex.
* @dev: Underlying device.
* @ops: wwan device ops
* @ops_ctxt: context to pass to ops
@@ -49,6 +52,7 @@ static struct dentry *wwan_debugfs_dir;
*/
struct wwan_device {
unsigned int id;
+ int refcount;
struct device dev;
const struct wwan_ops *ops;
void *ops_ctxt;
@@ -222,8 +226,10 @@ static struct wwan_device *wwan_create_dev(struct device *parent)
/* If wwandev already exists, return it */
wwandev = wwan_dev_get_by_parent(parent);
- if (!IS_ERR(wwandev))
+ if (!IS_ERR(wwandev)) {
+ wwandev->refcount++;
goto done_unlock;
+ }
id = ida_alloc(&wwan_dev_ids, GFP_KERNEL);
if (id < 0) {
@@ -242,6 +248,7 @@ static struct wwan_device *wwan_create_dev(struct device *parent)
wwandev->dev.class = &wwan_class;
wwandev->dev.type = &wwan_dev_type;
wwandev->id = id;
+ wwandev->refcount = 1;
dev_set_name(&wwandev->dev, "wwan%d", wwandev->id);
err = device_register(&wwandev->dev);
@@ -263,30 +270,18 @@ static struct wwan_device *wwan_create_dev(struct device *parent)
return wwandev;
}
-static int is_wwan_child(struct device *dev, void *data)
-{
- return dev->class == &wwan_class;
-}
-
static void wwan_remove_dev(struct wwan_device *wwandev)
{
- int ret;
-
/* Prevent concurrent picking from wwan_create_dev */
mutex_lock(&wwan_register_lock);
- /* WWAN device is created and registered (get+add) along with its first
- * child port, and subsequent port registrations only grab a reference
- * (get). The WWAN device must then be unregistered (del+put) along with
- * its last port, and reference simply dropped (put) otherwise. In the
- * same fashion, we must not unregister it when the ops are still there.
- */
- if (wwandev->ops)
- ret = 1;
- else
- ret = device_for_each_child(&wwandev->dev, NULL, is_wwan_child);
+ if (--wwandev->refcount <= 0) {
+ struct device *child = device_find_any_child(&wwandev->dev);
+
+ put_device(child);
+ if (WARN_ON(wwandev->ops || child) /* Paranoid */
+ goto out_unlock;
- if (!ret) {
#ifdef CONFIG_WWAN_DEBUGFS
debugfs_remove_recursive(wwandev->debugfs_dir);
#endif
@@ -295,6 +290,7 @@ static void wwan_remove_dev(struct wwan_device *wwandev)
put_device(&wwandev->dev);
}
+out_unlock:
mutex_unlock(&wwan_register_lock);
}
--
2.25.1
next prev parent reply other threads:[~2026-01-15 9:55 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-01-15 9:54 [net-next v6 0/8] net: wwan: add NMEA port type support Slark Xiao
2026-01-15 9:54 ` [net-next v6 1/8] net: wwan: core: remove unused port_id field Slark Xiao
2026-01-15 9:54 ` Slark Xiao [this message]
2026-01-15 9:54 ` [net-next v6 3/8] net: wwan: core: split port creation and registration Slark Xiao
2026-01-15 9:54 ` [net-next v6 4/8] net: wwan: core: split port unregister and stop Slark Xiao
2026-01-15 9:54 ` [net-next v6 5/8] net: wwan: add NMEA port support Slark Xiao
2026-01-15 9:54 ` [net-next v6 6/8] net: wwan: hwsim: refactor to support more port types Slark Xiao
2026-01-15 9:54 ` [net-next v6 7/8] net: wwan: hwsim: support NMEA port emulation Slark Xiao
2026-01-15 9:54 ` [net-next v6 8/8] net: wwan: mhi_wwan_ctrl: Add NMEA channel support Slark Xiao
2026-01-15 11:43 ` Re:[net-next v6 0/8] net: wwan: add NMEA port type support Slark Xiao
2026-01-16 2:41 ` [net-next " Jakub Kicinski
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260115095417.36975-3-slark_xiao@163.com \
--to=slark_xiao@163.com \
--cc=andrew+netdev@lunn.ch \
--cc=davem@davemloft.net \
--cc=dnlplm@gmail.com \
--cc=edumazet@google.com \
--cc=johannes@sipsolutions.net \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=loic.poulain@oss.qualcomm.com \
--cc=mani@kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=ryazanov.s.a@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox