From: mingming cao <mmc@linux.ibm.com>
To: Runyu Xiao <runyu.xiao@seu.edu.cn>,
Haren Myneni <haren@linux.ibm.com>,
Rick Lindsley <ricklind@linux.ibm.com>,
Nick Child <nnac123@linux.ibm.com>,
Madhavan Srinivasan <maddy@linux.ibm.com>,
Michael Ellerman <mpe@ellerman.id.au>,
Nicholas Piggin <npiggin@gmail.com>,
Christophe Leroy <chleroy@kernel.org>,
Andrew Lunn <andrew+netdev@lunn.ch>,
"David S . Miller" <davem@davemloft.net>,
Eric Dumazet <edumazet@google.com>,
Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>
Cc: linuxppc-dev@lists.ozlabs.org, netdev@vger.kernel.org,
linux-kernel@vger.kernel.org, stable@vger.kernel.org,
Jianhao Xu <jianhao.xu@seu.edu.cn>
Subject: Re: [PATCH net] net: ibmvnic: defer close from reset allocation failure
Date: Fri, 16 Oct 2026 14:55:24 -0700 [thread overview]
Message-ID: <3ae9b5c4-424f-4048-b78b-5f9bd36fef48@linux.ibm.com> (raw)
In-Reply-To: <20261004103131.1205618-1-runyu.xiao@seu.edu.cn>
On 10/4/26 3:31 AM, Runyu Xiao wrote:
> ibmvnic_reset() can be called by the CRQ tasklet. If allocating the
> reset work item fails, it currently calls ibmvnic_close() after
> dropping rwi_lock, but ibmvnic_close() reaches napi_disable() and
> other sleepable teardown paths.
>
> Defer the close to a work item and keep the work within the adapter
> lifetime. Track removal separately from the visible adapter state so
> ibmvnic_close() cannot make removal look active again after remove()
> has begun. Check that monotonic flag while scheduling reset and
> delayed reset work, and drain all work before releasing the adapter.
>
> Use complete_all() for probe_done because the reset and close workers
> may wait for probe completion concurrently.
>
> The issue was identified by code inspection. No runtime reproduction or
> real hardware testing is available.
>
> Fixes: ed651a10875f ("ibmvnic: Updated reset handling")
> Cc: stable@vger.kernel.org
> Assisted-by: LLM
> Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn>
> ---
> drivers/net/ethernet/ibm/ibmvnic.c | 54 ++++++++++++++++++++++++------
> drivers/net/ethernet/ibm/ibmvnic.h | 2 ++
> 2 files changed, 45 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c
> index 5a510eed33..4da3fd7ce0 100644
> --- a/drivers/net/ethernet/ibm/ibmvnic.c
> +++ b/drivers/net/ethernet/ibm/ibmvnic.c
> @@ -2152,6 +2152,26 @@ static int ibmvnic_close(struct net_device *netdev)
> return rc;
> }
>
> +static void ibmvnic_close_work(struct work_struct *work)
> +{
> + struct ibmvnic_adapter *adapter = container_of(work,
> + struct ibmvnic_adapter,
> + ibmvnic_close_work);
> + unsigned long flags;
> + bool removing;
> +
> + /* The close path can sleep, so run it outside the CRQ tasklet. */
> + wait_for_completion(&adapter->probe_done);
> + rtnl_lock();
> + spin_lock_irqsave(&adapter->rwi_lock, flags);
> + removing = adapter->removing;
> + spin_unlock_irqrestore(&adapter->rwi_lock, flags);
> +
> + if (!removing)
> + ibmvnic_close(adapter->netdev);
> + rtnl_unlock();
> +}
> +
> /**
> * get_hdr_lens - fills list of L2/L3/L4 hdr lens
> * @hdr_field: bitfield determining needed headers
> @@ -3229,9 +3249,12 @@ static void __ibmvnic_reset(struct work_struct *work)
> if (adapter->state == VNIC_PROBING &&
> !wait_for_completion_timeout(&adapter->probe_done, timeout)) {
> dev_err(dev, "Reset thread timed out on probe");
> - queue_delayed_work(system_long_wq,
> - &adapter->ibmvnic_delayed_reset,
> - IBMVNIC_RESET_DELAY);
> + spin_lock_irqsave(&adapter->rwi_lock, flags);
> + if (!adapter->removing)
> + queue_delayed_work(system_long_wq,
> + &adapter->ibmvnic_delayed_reset,
> + IBMVNIC_RESET_DELAY);
> + spin_unlock_irqrestore(&adapter->rwi_lock, flags);
> return;
> }
>
> @@ -3265,7 +3288,7 @@ static void __ibmvnic_reset(struct work_struct *work)
> */
> need_reset = false;
> spin_lock(&adapter->rwi_lock);
> - if (!list_empty(&adapter->rwi_list)) {
> + if (!adapter->removing && !list_empty(&adapter->rwi_list)) {
> if (test_and_set_bit_lock(0, &adapter->resetting)) {
> queue_delayed_work(system_long_wq,
> &adapter->ibmvnic_delayed_reset,
> @@ -3422,7 +3445,8 @@ static int ibmvnic_reset(struct ibmvnic_adapter *adapter,
> * a failover reset scheduled, we will detect and drop the
> * duplicate reset when walking the ->rwi_list below.
> */
> - if (adapter->state == VNIC_REMOVING ||
> + if (adapter->removing ||
> + adapter->state == VNIC_REMOVING ||
> adapter->state == VNIC_REMOVED ||
> (adapter->failover_pending && reason != VNIC_RESET_FAILOVER)) {
> ret = EBUSY;
> @@ -3458,11 +3482,11 @@ static int ibmvnic_reset(struct ibmvnic_adapter *adapter,
>
> ret = 0;
> err:
> - /* ibmvnic_close() below can block, so drop the lock first */
> - spin_unlock_irqrestore(&adapter->rwi_lock, flags);
> -
> if (ret == ENOMEM)
> - ibmvnic_close(netdev);
> + queue_work(system_long_wq, &adapter->ibmvnic_close_work);
> +
> + /* ibmvnic_close() can block, so defer it out of atomic context. */
> + spin_unlock_irqrestore(&adapter->rwi_lock, flags);
>
> return -ret;
> }
> @@ -6466,6 +6490,7 @@ static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id)
> SET_NETDEV_DEV(netdev, &dev->dev);
>
> INIT_WORK(&adapter->ibmvnic_reset, __ibmvnic_reset);
> + INIT_WORK(&adapter->ibmvnic_close_work, ibmvnic_close_work);
> INIT_DELAYED_WORK(&adapter->ibmvnic_delayed_reset,
> __ibmvnic_delayed_reset);
> INIT_LIST_HEAD(&adapter->rwi_list);
> @@ -6568,7 +6593,8 @@ static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id)
> goto cpu_notif_add_failed;
> }
>
> - complete(&adapter->probe_done);
> + complete_all(&adapter->probe_done);
> + flush_work(&adapter->ibmvnic_close_work);
>
> return 0;
>
> @@ -6591,10 +6617,14 @@ static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id)
> /* cleanup worker thread after releasing CRQ so we don't get
> * transport events (i.e new work items for the worker thread).
> */
> + spin_lock_irqsave(&adapter->rwi_lock, flags);
> + adapter->removing = true;
> adapter->state = VNIC_REMOVING;
> - complete(&adapter->probe_done);
> + spin_unlock_irqrestore(&adapter->rwi_lock, flags);
> + complete_all(&adapter->probe_done);
> flush_work(&adapter->ibmvnic_reset);
> flush_delayed_work(&adapter->ibmvnic_delayed_reset);
> + flush_work(&adapter->ibmvnic_close_work);
>
> flush_reset_queue(adapter);
>
> @@ -6620,6 +6650,7 @@ static void ibmvnic_remove(struct vio_dev *dev)
> * from the flush_work() below, can make progress.
> */
> spin_lock(&adapter->rwi_lock);
> + adapter->removing = true;
> adapter->state = VNIC_REMOVING;
> spin_unlock(&adapter->rwi_lock);
>
> @@ -6629,6 +6660,7 @@ static void ibmvnic_remove(struct vio_dev *dev)
>
> flush_work(&adapter->ibmvnic_reset);
> flush_delayed_work(&adapter->ibmvnic_delayed_reset);
> + flush_work(&adapter->ibmvnic_close_work);
>
> rtnl_lock();
> unregister_netdevice(netdev);
> diff --git a/drivers/net/ethernet/ibm/ibmvnic.h b/drivers/net/ethernet/ibm/ibmvnic.h
> index 480dc58707..d8c8a6d230 100644
> --- a/drivers/net/ethernet/ibm/ibmvnic.h
> +++ b/drivers/net/ethernet/ibm/ibmvnic.h
> @@ -1045,6 +1045,7 @@ struct ibmvnic_adapter {
>
> struct tasklet_struct tasklet;
> enum vnic_state state;
> + bool removing;
> /* Used for serialization of state field. When taking both state
> * and rwi locks, take state lock first.
> */
> @@ -1056,6 +1057,7 @@ struct ibmvnic_adapter {
> */
> spinlock_t rwi_lock;
> struct work_struct ibmvnic_reset;
> + struct work_struct ibmvnic_close_work;
> struct delayed_work ibmvnic_delayed_reset;
> unsigned long resetting;
> /* last device reset time */
Thanks for looking at this. The bug is real — deferring close out of
the CRQ tasklet is the right direction.
I walked ibmvnic_close_work() against the probe success path. Can a
probe-time ENOMEM leave the adapter in VNIC_CLOSED with no
init_resources()?
ibmvnic_reset() does not reject VNIC_PROBING, and the CRQ tasklet can
reach it during probe (e.g. CRQ_INIT -> VNIC_RESET_FAILOVER when state
is not VNIC_DOWN, or an XPORT_EVENT -> FATAL/MOBILITY). On GFP_ATOMIC
failure your worker waits on probe_done, then:
probe sets VNIC_PROBED / VNIC_DOWN, register_netdev()
-> complete_all() + flush_work(close_work)
-> ibmvnic_close() sets VNIC_CLOSED
next open sees state == VNIC_CLOSED, skips login/init_resources()
-> ibmvnic_napi_enable() uses adapter->napi[i] while napi is
still NULL (allocated only in init_napi() via init_resources())
Previously the inline close ran under VNIC_PROBING and probe overwrote
the final state afterwards. flush_work() after complete_all() reverses
that.
Should the worker only close when the iface was actually running
(netif_running() / state == VNIC_OPEN)?
While you are here: would dev_close() be better than calling
ibmvnic_close() directly? A bare ndo_stop leaves IFF_UP set when the
device was up (same as the old inline ENOMEM path). Keep rtnl_lock() in
the worker — dev_close() only takes the ops lock; __dev_close_many()
still expects rtnl. And netif_close() is a no-op when !(flags & IFF_UP),
which also avoids this post-probe VNIC_CLOSED case.
Minor on adapter->removing: the commit message is a bit stronger than
what a snapshot under rwi_lock can guarantee vs ibmvnic_remove(). Fine
to simplify in v2 if you prefer guarding VNIC_REMOVING in
__ibmvnic_close() instead of a new bool.
Happy to look at v2.
Thanks,
Mingming
prev parent reply other threads:[~2026-10-16 21:55 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-04 10:31 [PATCH net] net: ibmvnic: defer close from reset allocation failure Runyu Xiao
2026-10-05 10:31 ` netdev-bot+sashiko
2026-10-16 21:55 ` mingming cao [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=3ae9b5c4-424f-4048-b78b-5f9bd36fef48@linux.ibm.com \
--to=mmc@linux.ibm.com \
--cc=andrew+netdev@lunn.ch \
--cc=chleroy@kernel.org \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=haren@linux.ibm.com \
--cc=jianhao.xu@seu.edu.cn \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linuxppc-dev@lists.ozlabs.org \
--cc=maddy@linux.ibm.com \
--cc=mpe@ellerman.id.au \
--cc=netdev@vger.kernel.org \
--cc=nnac123@linux.ibm.com \
--cc=npiggin@gmail.com \
--cc=pabeni@redhat.com \
--cc=ricklind@linux.ibm.com \
--cc=runyu.xiao@seu.edu.cn \
--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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox