From: Jakub Kicinski <kuba@kernel.org>
To: mhun512@gmail.com
Cc: Jakub Kicinski <kuba@kernel.org>,
qiang.zhao@nxp.com, horms@kernel.org, andrew+netdev@lunn.ch,
davem@davemloft.net, edumazet@google.com, pabeni@redhat.com,
khc@pm.waw.pl, netdev@vger.kernel.org,
linuxppc-dev@lists.ozlabs.org, linux-kernel@vger.kernel.org,
stable@vger.kernel.org, ae878000@gmail.com
Subject: Re: [PATCH net v2] net: wan: fsl_ucc_hdlc: release HDLC device on remove
Date: Wed, 5 Aug 2026 19:05:42 -0700 [thread overview]
Message-ID: <20260806020541.2011936-2-kuba@kernel.org> (raw)
In-Reply-To: <20260803133048.42650-1-mhun512@gmail.com>
Could you make a patch series which fixes all the bugs reported
here which look real?
---
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
---
net: wan: fsl_ucc_hdlc: release HDLC device on remove
The patch makes ucc_hdlc_remove() unregister and free the HDLC netdev
before freeing the ucc_hdlc_private object, and adds dev_close() to the
generic unregister_hdlc_device() helper so ndo_stop runs while the HDLC
protocol is still attached.
Link to v1: https://lore.kernel.org/netdev/20260728171545.49247-1-mhun512@gmail.com/
> diff --git a/drivers/net/wan/fsl_ucc_hdlc.c b/drivers/net/wan/fsl_ucc_hdlc.c
> index 809f21fb93f56..4c843c94cf436 100644
> --- a/drivers/net/wan/fsl_ucc_hdlc.c
> +++ b/drivers/net/wan/fsl_ucc_hdlc.c
> @@ -1255,6 +1255,8 @@ static void ucc_hdlc_remove(struct platform_device *pdev)
> {
> struct ucc_hdlc_private *priv = dev_get_drvdata(&pdev->dev);
>
> + unregister_hdlc_device(priv->ndev);
> +
> uhdlc_memclean(priv);
[ ... ]
> diff --git a/drivers/net/wan/hdlc.c b/drivers/net/wan/hdlc.c
> index cbed10b1d862e..a38f90d3006ed 100644
> --- a/drivers/net/wan/hdlc.c
> +++ b/drivers/net/wan/hdlc.c
> @@ -263,6 +263,7 @@ EXPORT_SYMBOL(alloc_hdlcdev);
> void unregister_hdlc_device(struct net_device *dev)
> {
> rtnl_lock();
> + dev_close(dev);
> detach_hdlc_protocol(dev);
> unregister_netdevice(dev);
> rtnl_unlock();
[Severity: Medium]
Should this hunk be a separate patch? The subject is scoped to
fsl_ucc_hdlc and the Fixes tag points at c19b6d246a35, but
unregister_hdlc_device() is exported generic HDLC code with eight other
callers: c101_cleanup(), n2_destroy_card(), pc300too_remove_one(),
pci200syn_remove_one(), wanxl_pci_remove_one(), hss_remove_one(),
qmc_hdlc_remove() and farsync's fst_add_one()/fst_remove_one().
For all of those, detach_hdlc_protocol() -> hdlc_setup_dev() previously
cleared IFF_UP:
drivers/net/wan/hdlc.c:hdlc_setup_dev() {
...
dev->flags = IFF_POINTOPOINT | IFF_NOARP;
...
}
so netif_close_many() dropped the device and ndo_stop never ran. After
this change every one of those drivers runs its close routine during
unregister. Were the other users audited? The changelog does not say,
and with Cc: stable plus a driver-only Fixes tag the core behaviour
change lands in stable as if it were an fsl_ucc_hdlc-only fix.
One call site that looks worth a sentence in the changelog is the
farsync error unwind:
drivers/net/wan/farsync.c:fst_add_one() {
...
rx_dma_fail:
fst_disable_intr(card);
for (i = 0 ; i < card->nports ; i++)
unregister_hdlc_device(card->ports[i].dev);
...
}
Here fst_close() can now run after fst_disable_intr() and after the rx
DMA buffer has been freed. Was that considered?
[Severity: High]
This isn't a bug introduced by this patch, but the function this hunk
newly drives can dereference a NULL protocol pointer. uhdlc_open()
calls uhdlc_close() precisely when hdlc_open() failed:
drivers/net/wan/fsl_ucc_hdlc.c:uhdlc_open() {
...
rc = hdlc_open(dev);
if (rc)
uhdlc_close(dev);
...
}
For a freshly probed device the only failure hdlc_open() can return is
the no-protocol case, since ucc_hdlc_probe() only calls
register_hdlc_device() and never attaches a protocol:
drivers/net/wan/hdlc.c:hdlc_open() {
...
if (!hdlc->proto)
return -ENOSYS; /* no protocol attached */
...
}
uhdlc_close() then calls hdlc_close() unconditionally, and hdlc_close()
has no NULL check. hdlc->carrier is initialised to 1 in hdlc_setup(),
so hdlc_proto_stop() reads hdlc->proto->stop, and the tail of
hdlc_close() dereferences hdlc->proto again:
drivers/net/wan/hdlc.c:hdlc_close() {
...
if (hdlc->carrier)
hdlc_proto_stop(dev);
...
if (hdlc->proto->close)
hdlc->proto->close(dev);
}
Can "ip link set hdlc0 up" before any IF_PROTO_ ioctl oops here? Peer
drivers such as c101_open() and fst_open() call hdlc_open() first and
just propagate the error without entering their close routine. Since
this patch makes hdlc_close() reachable from the unregister path too,
would it make sense to fix this at the same time, either by calling
hdlc_open() before the hardware is enabled or by making hdlc_close()
tolerate a NULL proto?
[Severity: High]
This is a pre-existing issue, but the new dev_close() gives it a second
trigger, and this one runs with rtnl_mutex held. uhdlc_suspend()
disables NAPI and can then return an error without re-enabling it:
drivers/net/wan/fsl_ucc_hdlc.c:uhdlc_suspend() {
...
napi_disable(&priv->napi);
...
priv->ucc_pram_bak = kmalloc_obj(*priv->ucc_pram_bak);
if (!priv->ucc_pram_bak)
return -ENOMEM;
...
}
The PM core does not call resume for a device whose suspend failed, so
the device stays running with NAPI disabled. uhdlc_close() then calls
napi_disable() again:
drivers/net/wan/fsl_ucc_hdlc.c:uhdlc_close() {
...
napi_disable(&priv->napi);
...
}
and the wait in napi_disable_locked() has no timeout or alternative exit
condition:
net/core/dev.c:napi_disable_locked() {
...
while (val & (NAPIF_STATE_SCHED | NAPIF_STATE_NPSVC)) {
usleep_range(20, 200);
val = READ_ONCE(n->state);
}
...
}
Those bits are only cleared by napi_enable(), which never runs on that
path. With this patch the call chain becomes ucc_hdlc_remove() ->
unregister_hdlc_device() -> rtnl_lock(); dev_close() -> netif_close() ->
uhdlc_close(), so can an unbind or module removal after a failed suspend
spin forever while holding rtnl_mutex? Would fixing uhdlc_suspend() to
call napi_enable() before returning -ENOMEM be preferable to leaving
this reachable from the unbind path?
--
pw-bot: cr
prev parent reply other threads:[~2026-08-06 2:06 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-03 13:30 [PATCH net v2] net: wan: fsl_ucc_hdlc: release HDLC device on remove Myeonghun Pak
2026-08-06 2:05 ` Jakub Kicinski [this message]
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=20260806020541.2011936-2-kuba@kernel.org \
--to=kuba@kernel.org \
--cc=ae878000@gmail.com \
--cc=andrew+netdev@lunn.ch \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=horms@kernel.org \
--cc=khc@pm.waw.pl \
--cc=linux-kernel@vger.kernel.org \
--cc=linuxppc-dev@lists.ozlabs.org \
--cc=mhun512@gmail.com \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=qiang.zhao@nxp.com \
--cc=stable@vger.kernel.org \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.