Re: [PATCH v7 03/12] PCI: liveupdate: Track incoming preserved PCI devices

Pasha Tatashin <[email protected]> Tue, 21 Jul 2026 17:55:29 +0000
Newsgroups org.infradead.lists.kexec,org.kernel.vger.linux-doc,org.kernel.vger.linux-kernel,org.kernel.vger.linux-pci,org.kvack.linux-mm
Message-ID <178465652971.412167.17838319728990505256.b4-reply@b4>
On 2026-07-20 16:07:47-07:00, David Matlack wrote:
> On Mon, Jul 20, 2026 at 3:44 PM Pasha Tatashin
> <[email protected]> wrote:
> 
> > On 2026-07-20 14:54:51-07:00, David Matlack wrote:
> >
> > Thanks for the explanation. As I understand, the most straightforward
> > way to avoid holding the permanent reference is indeed to delete
> > dev->liveupdate.incoming entirely and perform an xarray lookup on every
> > access, like this:
> >
> > bool pci_liveupdate_is_incoming(struct pci_dev *dev)
> > {
> >         ...
> >         incoming = pci_liveupdate_flb_get_incoming();
> >         ...
> >         dev_ser = xa_load(&incoming->xa, key);
> >         ...
> >         pci_liveupdate_flb_put_incoming();
> >         return dev_ser && dev_ser->refcount > 0;
> > }
> >
> > However, as you note, this is inefficient because it affects every
> > single device and adds lookup overhead to every access (not sure about
> > the actual cost though, xarray access is pretty fast!).
> >
> > We can, however, still avoid tinkering with the lifecycle of the FLB,
> > and instead treat dev->liveupdate.incoming as a "hint" that we validate
> > on access with a fast, liveness check:
> 
> Can you tell me more about your concern about FLB lifetime?
> 
> The lifetime of the FLB will not be affected by this reference unless
> there is a bug in the driver where it fails to call
> pci_liveupdate_finish() during it's file handler finish callback.

From a design perspective, liveupdate_flb_get/put_incoming() is a 
logical read-lock/unlock pair on the FLB data. We use a refcount for 
optimization and sharing, but holding a get over a long asynchronous gap 
(from boot-time device setup to driver probe) is essentially holding an 
unbound lock.

Unbound locks make it difficult to trace refcount leaks or debug 
lifecycle issues.

> 
> > 1. At Setup: In pci_liveupdate_setup_device(), we do the xarray lookup
> > once, cache the pointer in dev->liveupdate.incoming, and immediately
> > call pci_liveupdate_flb_put_incoming(). We do not hold a permanent
> > reference.
> >
> > 2. On Access: When an accessor runs, instead of doing a full xarray
> > lookup, it just validates the cached pointer's liveness by temporarily
> > securing the FLB:
> >
> > static struct pci_flb_incoming *pci_liveupdate_get_incoming(struct pci_dev *dev)
> > {
> >         struct pci_flb_incoming *incoming;
> >
> >         incoming = pci_liveupdate_flb_get_incoming();
> >         if (!incoming)
> >                 return NULL;
> >
> >         if (dev->liveupdate.incoming)
> >                 return incoming;
> >
> >         pci_liveupdate_flb_put_incoming();
> >         return NULL;
> > }
> >
> > * If get_incoming() returns NULL (the FLB has already finished/freed),
> >   the hint is invalid and the device is no longer incoming.
> 
> This avoids the xarray lookup but still requires taking the incoming
> FLB mutex twice (once for get and once for put) on every access. And
> if there's no incoming PCI FLB, the LUO will iterate over all incoming
> FLBs under the mutex to find it.

Can we do a fast-path check first?

static struct pci_flb_incoming *pci_liveupdate_get_incoming(struct pci_dev *dev)
{
	struct pci_flb_incoming *incoming;

	/* Fast-path to avoid unnecessary FLB querying */
	if (!dev->liveupdate.incoming)
		return NULL;

	incoming = pci_liveupdate_flb_get_incoming();
	if (!incoming)
		return NULL;

	/* Check again, now that FLB is acquired */
	if (dev->liveupdate.incoming)
		return incoming;

	pci_liveupdate_flb_put_incoming();
	return NULL;
}

This seems to gives us the best of both worlds: robust refcount hygiene 
and a sane fast path.

What do you think?