From nobody Thu Sep 24 23:04:13 2026 Received: from mail-pl1-f197.google.com (mail-pl1-f197.google.com [209.85.214.197]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id AEA1548F828 for ; Fri, 18 Sep 2026 20:06:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.197 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789762017; cv=none; b=WWRKSbiNzJykvi/L/5/XTmKX8zlRevhWbZPfNRtOUMRbvl/phOOaTPwwG2Hi1t0mvAkRCsBNzhPEJTKXdqb7HSw3Y+3qp/tJb0UAoxorQEMO2Rxq60GhIXiUhcOaC5UMtnSBVc4X3XYB+lX1x6HMCoeG8jE9/dE1SfOB9qam+Jk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789762017; c=relaxed/simple; bh=Qmx1MY7JdETNFyztRy/Ida3zafoZraY7bxdibPhkQNM=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=HYXvdP0kOAnflzdUcyrki583jHKeHlSOmQsQUjrmSBTeVp20WXNMIaF2yVEUcFVQcwXpPT8qwUcS0hu0hYDVhPRZq1ScZM5mAl1dI1/zD5OCiSo1IvCCS1Mq9SRe4esyavSNxRCtFQRtH0L9J1OVOvbBIOcgC0G8D4wSgeBQIdo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--dmatlack.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=MCGTObLO; arc=none smtp.client-ip=209.85.214.197 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--dmatlack.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="MCGTObLO" Received: by mail-pl1-f197.google.com with SMTP id d9443c01a7336-2d6df0a1e18so19895595ad.1 for ; Fri, 18 Sep 2026 13:06:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1789762015; x=1790366815; darn=vger.kernel.org; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:from:to:cc:subject:date:message-id:reply-to :content-type; bh=vMZxoJzxf5EwoOEJT6PgaqZk5axcphqe1c88PqDiOfI=; b=MCGTObLOiTazzCIgqEHE91ithihiXYuHyuCWpFOJKvGrmiqU6lKthq9yLRfX8B3J2w PGP6LjR5Bdnk2cAxMZMicMySZJmIkAO3vyn3eLhzramtWaypT4ZmmHzheuDXWHJ3+aDV MYsApexYqM0fF6r9jJsL/GgpYmk5F/ys88WOnRlaVWQ4xKNw5YD9OtuaVQ7WBSyPXoCW Ck9Jtg90QLauxRy6wsjvYJJ71g6o848LPwbSkJF3JY+F3mQdocQcq+vaumN0/kgnw36a ZDUEjnWV6FFDmRBCttMxPOZeA+HhsTWTxsWSUyeOYI5+m6ZgutOk0P0lqBLMJzd+p984 rlXA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1789762015; x=1790366815; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=vMZxoJzxf5EwoOEJT6PgaqZk5axcphqe1c88PqDiOfI=; b=ayNsTZf7vsYCKGMyae9tDPte75x+J8k/1kXpRalP+t6ZWw866RbwJ+TM8dcPp77IZK kF1dOBjC0sVR1bOYHIKZ5mSbRcRv1R9szt/eVkEubTP4DJKXR4V4Dp89MfJlp5fNp9XO FUDdUea9h5FkEBCU6scHBHh4wYytNhDv4CXYpucweYpLd3W/m0qbWPfzqKd6D1zlYAlC c4lrQoE8Lny2mOh8sZQfhCr67Pk2wUgcPfvytE0sFLNkhd2KNTLJ3JuBzCJtvtFnqRFl plFHSciFzQBH/lqrplyC6iAf+PGH8Ohcy/Rvkb06B3MjGhKwS71LLoYF/sbuzGPPYblv HZsA== X-Forwarded-Encrypted: i=1; AKwUvBxx4Itb/bDcx55/u0ucnIk0y23HAXTy5Hp6XnkWaulfokgM3a/IGbsXxyT3mXSgzcW0emnsTgeNkCqFzgU=@vger.kernel.org X-Gm-Message-State: AFuF++kYOxnzRDhqeNCB8Xxo3CFZxy3SjQmRK5zCaOMUG0NuGGqYadzi pi97Oij2sPd2tocL0g60Kx+3FARep2bjxdIxmjATW4sjN9RLrLo+il9Y+s21+ynsVCIHJ/0MYpM 76iezcehx9jMdBA== X-Received: from plad17.prod.google.com ([2002:a17:902:e151:b0:2db:79ec:b693]) (user=dmatlack job=prod-delivery.src-stubby-dispatcher) by 2002:a17:903:98b:b0:2dd:c100:7cb7 with SMTP id d9443c01a7336-2ddc1007d77mr8305955ad.51.1789762014774; Fri, 18 Sep 2026 13:06:54 -0700 (PDT) Date: Fri, 18 Sep 2026 20:06:27 +0000 In-Reply-To: <20260918200640.887030-1-dmatlack@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260918200640.887030-1-dmatlack@google.com> X-Mailer: git-send-email 2.55.0.1082.g2b9226bbc0-goog Message-ID: <20260918200640.887030-2-dmatlack@google.com> Subject: [PATCH v9 01/13] PCI: liveupdate: Set up FLB handler for the PCI core From: David Matlack To: kexec@lists.infradead.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, linux-pci@vger.kernel.org Cc: Adithya Jayachandran , Alexander Graf , Alex Williamson , Bjorn Helgaas , Chris Li , David Matlack , David Rientjes , Jacob Pan , Jason Gunthorpe , Jonathan Corbet , Josh Hilke , Leon Romanovsky , Lukas Wunner , Mike Rapoport , Parav Pandit , Pasha Tatashin , Pranjal Shrivastava , Pratyush Yadav , Randy Dunlap , Saeed Mahameed , Samiullah Khawaja , Shuah Khan , Vipin Sharma , William Tu , Yi Liu Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Set up a File-Lifecycle-Bound (FLB) handler so that the PCI core can preserve its own state across a Live Update kexec. Preserving a PCI device across kexec requires preserving two independent sets of state: - Driver state, e.g. everything vfio-pci needs so that userspace can keep using the device in the new kernel. The driver preserves this itself and the PCI core is not involved. - PCI core state, e.g. which devices are preserved, so that the new kernel knows not to disturb them while they are still running and doing DMA. That is what this commit adds, serialized into struct pci_ser. Userspace, not the kernel, decides which devices are preserved, and it does so through the Live Update Orchestrator's (LUO) support for file preservation: a driver exposes a file that represents a single PCI device, and userspace preserves that device with ioctl(LIVEUPDATE_SESSION_PRESERVE_FD) on that file. Binding preservation to a file gives it proper lifecycle management, e.g. the preservation is undone if userspace cancels it or goes away. How a driver exposes that file is up to the driver and invisible to the PCI core (vfio-pci variant drivers, the first intended use-case, use their per-device cdev). LUO only knows that a file was preserved; it does not know that it represents a PCI device, or which one. Bridging that gap, drivers register their liveupdate_file_handler with the PCI core: pci_liveupdate_register_flb(driver_file_handler); pci_liveupdate_unregister_flb(driver_file_handler); LUO then refcounts the PCI core's FLB against the files preserved by that handler, and that refcount drives the lifetime of struct pci_ser: - On the first preserved file, luo_flb_file_preserve_one() calls pci_flb_preserve(), which allocates struct pci_ser and preserves it with KHO. - On the last unpreserved file (i.e. preservation cancelled), liveupdate_flb_put_outgoing() calls pci_flb_unpreserve(), which unpreserves and frees struct pci_ser. - In the next kernel, pci_flb_retrieve() hands the PCI core the struct pci_ser built by the previous kernel, whenever the PCI core asks for it (e.g. during enumeration), and pci_flb_finish() frees it once the PCI core is done with it. So the flow for preserving a device, once a driver has registered, looks like this: ioctl(LIVEUPDATE_SESSION_PRESERVE_FD) luo_session_preserve_fd() luo_preserve_file() luo_flb_file_preserve() luo_flb_file_preserve_one() # only on the first preserved file pci_flb_preserve() # alloc + KHO-preserve pci_ser fh->ops->preserve() # driver callback, e.g. vfio-pci Note that struct pci_ser is deliberately not allocated when a driver calls pci_liveupdate_register_flb(). A driver can be loaded for the lifetime of the machine without ever preserving a device, and there is no reason to allocate memory and hand it to the next kernel in that case. Letting LUO own the lifetime also means the PCI core does not have to duplicate LUO's refcounting and unwind logic for preservation failures, session aborts and fd close, and the incoming side (retrieve/finish) comes from the same object rather than requiring a separate KHO FDT entry owned by the PCI core. Note: This commit only allocates struct pci_ser and preserves it across Live Update. A subsequent commit adds pci_liveupdate_preserve(), the API drivers call from their fh->ops->preserve() callback to tell the PCI core exactly which devices are being preserved. Note: There is no reason to check for kho_is_enabled() since it can be assumed to return true. If KHO was not enabled then Live Update would not be enabled and these routines would never run. Reviewed-by: Pranjal Shrivastava Reviewed-by: Samiullah Khawaja Signed-off-by: David Matlack --- Documentation/core-api/liveupdate.rst | 4 + MAINTAINERS | 13 ++ drivers/pci/Kconfig | 15 ++ drivers/pci/Makefile | 1 + drivers/pci/liveupdate.c | 206 ++++++++++++++++++++++++++ include/linux/kho/abi/pci.h | 65 ++++++++ include/linux/pci.h | 1 + include/linux/pci_liveupdate.h | 30 ++++ 8 files changed, 335 insertions(+) create mode 100644 drivers/pci/liveupdate.c create mode 100644 include/linux/kho/abi/pci.h create mode 100644 include/linux/pci_liveupdate.h diff --git a/Documentation/core-api/liveupdate.rst b/Documentation/core-api= /liveupdate.rst index 5a292d0f3706..b3c689e633c1 100644 --- a/Documentation/core-api/liveupdate.rst +++ b/Documentation/core-api/liveupdate.rst @@ -1,5 +1,7 @@ .. SPDX-License-Identifier: GPL-2.0 =20 +.. _luo: + =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D Live Update Orchestrator =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D @@ -18,6 +20,8 @@ LUO Preserving File Descriptors .. kernel-doc:: kernel/liveupdate/luo_file.c :doc: LUO File Descriptors =20 +.. _flb: + LUO File Lifecycle Bound Global Data =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D .. kernel-doc:: kernel/liveupdate/luo_flb.c diff --git a/MAINTAINERS b/MAINTAINERS index 3a19da74d00c..bb9ef5460b5c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -21046,6 +21046,19 @@ L: linux-pci@vger.kernel.org S: Supported F: Documentation/PCI/pci-error-recovery.rst =20 +PCI LIVE UPDATE +M: David Matlack +R: Pasha Tatashin +R: Mike Rapoport +R: Pratyush Yadav +L: kexec@lists.infradead.org +L: linux-pci@vger.kernel.org +S: Maintained +T: git git://git.kernel.org/pub/scm/linux/kernel/git/liveupdate/linux.git +F: drivers/pci/liveupdate.c +F: include/linux/kho/abi/pci.h +F: include/linux/pci_liveupdate.h + PCI MSI DRIVER FOR ALTERA MSI IP L: linux-pci@vger.kernel.org S: Orphan diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 0c7408509ba2..3781e2b5f095 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -271,6 +271,21 @@ config VGA_ARB_MAX_GPUS Reserves space in the kernel to maintain resource locking for multiple GPUS. The overhead for each GPU is very small. =20 +config PCI_LIVEUPDATE + bool "PCI Live Update Support" + depends on PCI && LIVEUPDATE + help + Enable PCI core support for preserving PCI devices across Live + Update. This, in combination with support in a device's driver, + enables PCI devices to run and perform memory transactions + uninterrupted during a kexec for Live Update. + + This option should only be enabled by users who plan to use Live + Update for kernel upgrades and require preserving PCI devices during + those upgrades. + + If unsure, say N. + source "drivers/pci/hotplug/Kconfig" source "drivers/pci/controller/Kconfig" source "drivers/pci/endpoint/Kconfig" diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 41ebc3b9a518..e8d003cb6757 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_PROC_FS) +=3D proc.o obj-$(CONFIG_SYSFS) +=3D pci-sysfs.o slot.o obj-$(CONFIG_ACPI) +=3D pci-acpi.o obj-$(CONFIG_GENERIC_PCI_IOMAP) +=3D iomap.o +obj-$(CONFIG_PCI_LIVEUPDATE) +=3D liveupdate.o endif =20 obj-$(CONFIG_OF) +=3D of.o diff --git a/drivers/pci/liveupdate.c b/drivers/pci/liveupdate.c new file mode 100644 index 000000000000..66dbee0bd3cf --- /dev/null +++ b/drivers/pci/liveupdate.c @@ -0,0 +1,206 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright (c) 2026, Google LLC. + * David Matlack + */ + +/** + * DOC: PCI Live Update + * + * The PCI subsystem participates in the Live Update process to enable dri= vers + * to preserve their PCI devices across kexec. + * + * Preserving a device requires preserving two independent sets of state: = the + * driver's own state, which the driver preserves with no involvement from= the + * PCI core, and the PCI core's state about the device, which the next ker= nel + * needs so that enumeration does not disturb a device that is still runni= ng. + * This file implements the latter. + * + * :ref:`FLB ` Data + * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + * + * Userspace decides which devices are preserved, using :ref:`LUO ` f= ile + * preservation: a driver exposes a file that represents a single PCI devi= ce, + * and userspace preserves the device with + * ``ioctl(LIVEUPDATE_SESSION_PRESERVE_FD)`` on that file. Binding preserv= ation + * to a file gives it proper lifecycle management, e.g. the preservation is + * undone if userspace cancels it or goes away. How a driver exposes that = file + * is up to the driver and invisible to the PCI core (vfio-pci variant dri= vers, + * the first intended use-case, use their per-device cdev). + * + * LUO only knows that a file was preserved; it does not know that the file + * represents a PCI device. Drivers therefore register their + * struct liveupdate_file_handler with the PCI core: + * + * * ``pci_liveupdate_register_flb(driver_file_handler)`` + * * ``pci_liveupdate_unregister_flb(driver_file_handler)`` + * + * LUO then refcounts the PCI core's FLB against the files preserved by th= at + * handler, and that refcount drives the lifetime of struct pci_ser: + * pci_flb_preserve() allocates and preserves it when the first file is + * preserved, and pci_flb_unpreserve() frees it when the last file is + * unpreserved. In the next kernel, pci_flb_retrieve() hands the PCI core = the + * struct pci_ser built by the previous kernel, whenever the PCI core asks= for + * it (e.g. during enumeration), and pci_flb_finish() frees it once the PCI + * core is done with it. + * + * Call Flow + * --------- + * + * :: + * + * # Driver initialization + * pci_liveupdate_register_flb(fh) + * + * # Userspace: ioctl(LIVEUPDATE_SESSION_PRESERVE_FD, devfd) + * luo_preserve_file() + * luo_flb_file_preserve() + * luo_flb_file_preserve_one() # first preserved file only + * pci_flb_preserve() # alloc and preserve struct pci_= ser + * fh->ops->preserve() # driver callback + * + * # Userspace: preservation cancelled or session torn down + * luo_file_unpreserve_files() + * luo_flb_file_unpreserve() + * liveupdate_flb_put_outgoing() # last unpreserved file only + * pci_flb_unpreserve() # free struct pci_ser + * + * # ---------------- kexec ---------------- + * + * # New kernel: the PCI core asks for the previous kernel's state + * liveupdate_flb_get_incoming() + * luo_flb_retrieve_one() # first request only + * pci_flb_retrieve() # previous kernel's struct pci_s= er + * + * # Userspace: ioctl(LIVEUPDATE_SESSION_FINISH) + * luo_file_finish_one() + * fh->ops->finish() # driver callback + * luo_flb_file_finish() + * liveupdate_flb_put_incoming() # last incoming file only + * pci_flb_finish() # free struct pci_ser + */ + +#define pr_fmt(fmt) "PCI: liveupdate: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * struct pci_flb_outgoing - Outgoing PCI FLB object + * @ser: Pointer to the preserved struct pci_ser. + * @block_set: The KHO block set holding the outgoing devices. + * + * This structure holds the runtime state for the outgoing PCI Live Update + * state. It wraps the serialized pci_ser and the block_set used to manage + * the serialized entries. + */ +struct pci_flb_outgoing { + struct pci_ser *ser; + struct kho_block_set block_set; +}; + +static int pci_flb_preserve(struct liveupdate_flb_op_args *args) +{ + struct pci_flb_outgoing *outgoing __free(kfree) =3D NULL; + struct pci_ser *ser; + + outgoing =3D kzalloc_obj(*outgoing); + if (!outgoing) + return -ENOMEM; + + ser =3D kho_alloc_preserve(sizeof(*ser)); + if (IS_ERR(ser)) + return PTR_ERR(ser); + + ser->version =3D PCI_LUO_FLB_VERSION; + ser->nr_devices =3D 0; + ser->devices =3D 0; + + outgoing->ser =3D ser; + kho_block_set_init(&outgoing->block_set, sizeof(struct pci_dev_ser)); + + args->obj =3D no_free_ptr(outgoing); + args->data =3D virt_to_phys(ser); + pr_debug("Preserved struct pci_ser (0x%llx)\n", args->data); + return 0; +} + +static void pci_flb_unpreserve(struct liveupdate_flb_op_args *args) +{ + struct pci_flb_outgoing *outgoing =3D args->obj; + + pr_debug("Unpreserving struct pci_ser (0x%llx)\n", args->data); + + WARN_ON(outgoing->ser->nr_devices); + kho_block_set_destroy(&outgoing->block_set); + kho_unpreserve_free(outgoing->ser); + kfree(outgoing); +} + +static int pci_flb_retrieve(struct liveupdate_flb_op_args *args) +{ + pr_debug("Retrieving struct pci_ser (0x%llx)\n", args->data); + args->obj =3D phys_to_virt(args->data); + return 0; +} + +static void pci_flb_finish(struct liveupdate_flb_op_args *args) +{ + pr_debug("Finished struct pci_ser (0x%llx)\n", args->data); + kho_restore_free(args->obj); +} + +static struct liveupdate_flb_ops pci_liveupdate_flb_ops =3D { + .preserve =3D pci_flb_preserve, + .unpreserve =3D pci_flb_unpreserve, + .retrieve =3D pci_flb_retrieve, + .finish =3D pci_flb_finish, + .owner =3D THIS_MODULE, +}; + +static struct liveupdate_flb pci_liveupdate_flb =3D { + .ops =3D &pci_liveupdate_flb_ops, + .compatible =3D PCI_LUO_FLB_COMPATIBLE, +}; + +/** + * pci_liveupdate_register_flb() - Register a file handler with the PCI co= re + * @fh: The file handler to register. + * + * Drivers that support preserving PCI devices across Live Update must call + * pci_liveupdate_register_flb() to register their + * struct liveupdate_file_handler with the PCI core, typically at module i= nit, + * and always before any file managed by @fh can be preserved. + * + * Registering links the PCI core's FLB to @fh, so that LUO allocates the = PCI + * core's outgoing struct pci_ser (via pci_flb_preserve()) when the first = file + * managed by any registered handler is preserved, and frees it (via + * pci_flb_unpreserve()) when the last such file is unpreserved. + * + * Return: 0 on success, <0 on failure. + */ +int pci_liveupdate_register_flb(struct liveupdate_file_handler *fh) +{ + pr_debug("Registering file handler \"%s\"\n", fh->compatible); + return liveupdate_register_flb(fh, &pci_liveupdate_flb); +} +EXPORT_SYMBOL_GPL(pci_liveupdate_register_flb); + +/** + * pci_liveupdate_unregister_flb() - Unregister a file handler with the PC= I core + * @fh: The file handler to unregister. + */ +void pci_liveupdate_unregister_flb(struct liveupdate_file_handler *fh) +{ + pr_debug("Unregistering file handler \"%s\"\n", fh->compatible); + liveupdate_unregister_flb(fh, &pci_liveupdate_flb); +} +EXPORT_SYMBOL_GPL(pci_liveupdate_unregister_flb); diff --git a/include/linux/kho/abi/pci.h b/include/linux/kho/abi/pci.h new file mode 100644 index 000000000000..4096e3cd3324 --- /dev/null +++ b/include/linux/kho/abi/pci.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * Copyright (c) 2026, Google LLC. + * David Matlack + */ + +#ifndef _LINUX_KHO_ABI_PCI_H +#define _LINUX_KHO_ABI_PCI_H + +#include +#include +#include + +/** + * DOC: PCI File-Lifecycle Bound (FLB) Live Update ABI + * + * This header defines the ABI for preserving core PCI state across kexec = using + * Live Update File-Lifecycle Bound (FLB) data. + * + * This interface is a contract. Any modification to any of the serializat= ion + * structs defined here constitutes a breaking change. Such changes require + * incrementing the version number in the PCI_LUO_FLB_VERSION number. + */ + +#define PCI_LUO_FLB_COMPATIBLE "pci" +#define PCI_LUO_FLB_VERSION 1 + +/** + * struct pci_dev_ser - Serialized state about a single PCI device. + * + * @domain: The device's PCI domain number (segment). + * @bdf: The device's PCI bus, device, and function number. + * @refcount: Reference count used by the PCI core to keep track of whethe= r it + * is done using a device's struct pci_dev_ser. The value of the + * refcount is equal to 1 when the struct pci_dev_ser is in use= , and + * 0 otherwise. + */ +struct pci_dev_ser { + u32 domain; + u16 bdf; + u16 refcount; +} __packed; + +/** + * struct pci_ser - PCI Subsystem Live Update State + * + * This struct tracks state about all devices that are being preserved acr= oss + * a Live Update for the next kernel. It contains only state owned by the = PCI + * core; the state a driver needs to resume its device is preserved separa= tely + * by that driver. + * + * @version: The version of the "pci" FLB struct. This field must never be + * deleted, moved, or resized, as the kernel depends on always b= eing + * able to check the struct pci_ser version number. + * @nr_devices: The number of devices that were preserved. + * @devices: Physical address of the first KHO block containing pci_dev_se= r. + */ +struct pci_ser { + u32 version; + u32 nr_devices; + u64 devices; +} __packed; + +#endif /* _LINUX_KHO_ABI_PCI_H */ diff --git a/include/linux/pci.h b/include/linux/pci.h index d31a8d107b1e..95b723aecb08 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -43,6 +43,7 @@ #include =20 #include +#include =20 #define PCI_STATUS_ERROR_BITS (PCI_STATUS_DETECTED_PARITY | \ PCI_STATUS_SIG_SYSTEM_ERROR | \ diff --git a/include/linux/pci_liveupdate.h b/include/linux/pci_liveupdate.h new file mode 100644 index 000000000000..8ec98beefcb4 --- /dev/null +++ b/include/linux/pci_liveupdate.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * PCI Live Update support (Public/Driver API) + * + * Copyright (c) 2026, Google LLC. + * David Matlack + */ +#ifndef LINUX_PCI_LIVEUPDATE_H +#define LINUX_PCI_LIVEUPDATE_H + +#include +#include + +struct pci_dev; + +#ifdef CONFIG_PCI_LIVEUPDATE +int pci_liveupdate_register_flb(struct liveupdate_file_handler *fh); +void pci_liveupdate_unregister_flb(struct liveupdate_file_handler *fh); +#else +static inline int pci_liveupdate_register_flb(struct liveupdate_file_handl= er *fh) +{ + return -EOPNOTSUPP; +} + +static inline void pci_liveupdate_unregister_flb(struct liveupdate_file_ha= ndler *fh) +{ +} +#endif + +#endif /* LINUX_PCI_LIVEUPDATE_H */ --=20 2.55.0.1082.g2b9226bbc0-goog From nobody Thu Sep 24 23:04:13 2026 Received: from mail-pl1-f199.google.com (mail-pl1-f199.google.com [209.85.214.199]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 82A635208C4 for ; Fri, 18 Sep 2026 20:06:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.199 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789762019; cv=none; b=CXyvOCILIQFutoTEJ4VSu8H6DWXlnkQZ594+Fzj5pjkqUpLIOvPTfaxGlJ/WYVux3q2YnkL2Gp8ZOcRi/x8ucjWEa9VGRrAnUi/rUtv+ZvIlZc7kN1m/Gi3ykDDH00emtsHO4yp1OCw7WACSMdDWE4VEgFkJsP7IWQPQUwaMluc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789762019; c=relaxed/simple; bh=pAaigM/99t6cVs+BMxU+NdSbMYHTndUTEFRksz6cfm0=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=tyLlqh1d9VQv9HsHQ6QaMkF24yWvhC0XxTKNtHuqVW/dvEPif8A4Ls7y4qxRfBAQ1wAhTnO3oeVaW/cMQWpgq+osh35GRT2HPwnThn76LRxLuYGxNZASKWqzk8o6Izy2nHWLiabQppKys7n46hYK7kt7msyo6GT5SPRfPleRFMk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--dmatlack.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=bIsa2sCB; arc=none smtp.client-ip=209.85.214.199 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--dmatlack.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="bIsa2sCB" Received: by mail-pl1-f199.google.com with SMTP id d9443c01a7336-2dd53f2b27cso14747905ad.0 for ; Fri, 18 Sep 2026 13:06:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1789762016; x=1790366816; darn=vger.kernel.org; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:from:to:cc:subject:date:message-id:reply-to :content-type; bh=Ct2oZmMMqiZqS2gqo7YSORMBPVLQ4WC/tqkRDMbJuzU=; b=bIsa2sCBCDkhEkCiys3HCerk5CzvgFVyo9g+sYpcXXjtL0cw5i7uyvs/8lyU1oZgXK rJqAlO5Codmzv/DxvHszQ1DXeSe+8FXNZZ8vTN1GaSk4dajA3Z9/PV0oFfDzzTVxTi1q l21J/iqvHlHU/EaXEP9+MtrO7lbVpzk5wKnhTQDaPsLz6n8eXphNOhN1BIO5wl7EhBdW RxNMi7KXzxU9n17O/r/EUfQkz41uB0sauraAoGwmmAhgcbw5u2Ytx8cVtQzs3q639xSe 0Op+WzyFzCAR2rh2vw7XyAAT2zuqERtLcEpRwcOyCIVapgoFttp+eaXEhBI3FGmpiG5+ lDBw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1789762016; x=1790366816; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=Ct2oZmMMqiZqS2gqo7YSORMBPVLQ4WC/tqkRDMbJuzU=; b=FeQhL13Kptno2va6TVLlklx53sRwSf6T3l6kLt2HCJa3pV5OCoE/V5WpgqwIKSBUid mTNy9Id8LMjHPRHk6nEUR5FVvcWIYAKcWBJNOyy+ERs84Chx/BJuhRO4izvnIjRbJfsE c5VEFajZJaJE8at6hf+P/uFj1NvgmDPokHDh8tbrczHit+5R0xg1p1USh9D/46DHKzQp QCSvfq/mkfn7fPLl6xNQ/oYrSg453j2PxOuAIhSRE8HGM3XTI8RagJLcq+5VJWi6FzzI GbN8NyjHlhFYIhhmq5HJnJ0srikERlNl8uZb03rYuUfdp4pEb0JHV4ol7x8Ur79PXIBU 97rA== X-Forwarded-Encrypted: i=1; AKwUvBwZ03jlBGqc+Wviu6farfi03t11PNYAMvSNf/AelOS/e43szUwyUh+5KDT2ASFUS8PzdmeISKZ7zrZU/hw=@vger.kernel.org X-Gm-Message-State: AFuF++lXcrPClEUm+tSFKM0owRi3vef8UADSW2PvuhFMi08XdRWL32MK YlZtOl2PBSkmWveqdISGDPkKSVxKBHcXeSGRhz+QYQFJ/dLx1S2XJ7RtrBZ6mg3zljLvkha5A9a bDUC+x4u6c63r4w== X-Received: from plat5.prod.google.com ([2002:a17:902:e1c5:b0:2dd:388a:cfae]) (user=dmatlack job=prod-delivery.src-stubby-dispatcher) by 2002:a17:903:1a8c:b0:2dd:c100:4b7c with SMTP id d9443c01a7336-2ddc1005f28mr9783735ad.51.1789762015597; Fri, 18 Sep 2026 13:06:55 -0700 (PDT) Date: Fri, 18 Sep 2026 20:06:28 +0000 In-Reply-To: <20260918200640.887030-1-dmatlack@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260918200640.887030-1-dmatlack@google.com> X-Mailer: git-send-email 2.55.0.1082.g2b9226bbc0-goog Message-ID: <20260918200640.887030-3-dmatlack@google.com> Subject: [PATCH v9 02/13] PCI: liveupdate: Track outgoing preserved PCI devices From: David Matlack To: kexec@lists.infradead.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, linux-pci@vger.kernel.org Cc: Adithya Jayachandran , Alexander Graf , Alex Williamson , Bjorn Helgaas , Chris Li , David Matlack , David Rientjes , Jacob Pan , Jason Gunthorpe , Jonathan Corbet , Josh Hilke , Leon Romanovsky , Lukas Wunner , Mike Rapoport , Parav Pandit , Pasha Tatashin , Pranjal Shrivastava , Pratyush Yadav , Randy Dunlap , Saeed Mahameed , Samiullah Khawaja , Shuah Khan , Vipin Sharma , William Tu , Yi Liu Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add APIs to allow drivers to notify the PCI core of which devices are being preserved across a Live Update for the next kernel, i.e. "outgoing" devices. Drivers must notify the PCI core when devices are preserved so that the PCI core can update its FLB data (struct pci_ser) and track the list of outgoing devices. pci_liveupdate_preserve() notifies the PCI core that a device must be preserved across Live Update. pci_liveupdate_unpreserve() reverses this (cancels the preservation of the device). This tracking ensures the PCI core is fully aware of which devices may need special handling during shutdown and kexec, and so the list of preserved devices can be handed off to the next kernel. For now, the API only supports preserving non-VF devices on a root bus (not behind an PCI-to-PCI bridges). Reviewed-by: Pranjal Shrivastava Reviewed-by: Pasha Tatashin Reviewed-by: Bjorn Helgaas Reviewed-by: Samiullah Khawaja Signed-off-by: David Matlack --- drivers/pci/liveupdate.c | 211 +++++++++++++++++++++++++++++++++ drivers/pci/liveupdate.h | 21 ++++ drivers/pci/probe.c | 2 + include/linux/pci.h | 3 + include/linux/pci_liveupdate.h | 21 ++++ 5 files changed, 258 insertions(+) create mode 100644 drivers/pci/liveupdate.h diff --git a/drivers/pci/liveupdate.c b/drivers/pci/liveupdate.c index 66dbee0bd3cf..e0ca537d0cb3 100644 --- a/drivers/pci/liveupdate.c +++ b/drivers/pci/liveupdate.c @@ -59,6 +59,7 @@ * luo_flb_file_preserve_one() # first preserved file only * pci_flb_preserve() # alloc and preserve struct pci_= ser * fh->ops->preserve() # driver callback + * pci_liveupdate_preserve(dev) # record this device in struct p= ci_ser * * # Userspace: preservation cancelled or session torn down * luo_file_unpreserve_files() @@ -79,6 +80,27 @@ * luo_flb_file_finish() * liveupdate_flb_put_incoming() # last incoming file only * pci_flb_finish() # free struct pci_ser + * + * Device Tracking + * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + * + * Drivers must notify the PCI core when specific devices are preserved or + * unpreserved with the following APIs: + * + * * ``pci_liveupdate_preserve(pci_dev)`` + * * ``pci_liveupdate_unpreserve(pci_dev)`` + * + * This allows the PCI core to keep its FLB data (struct pci_ser) up to da= te + * with the list of **outgoing** preserved devices for the next kernel. + * + * Restrictions + * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + * + * The PCI core enforces the following restrictions on which devices can be + * preserved. These may be relaxed in the future: + * + * * The device cannot be a Virtual Function (VF). + * * The device cannot be behind a PCI-to-PCI bridge. */ =20 #define pr_fmt(fmt) "PCI: liveupdate: " fmt @@ -93,6 +115,21 @@ #include #include =20 +#include "liveupdate.h" + +/** + * struct pci_liveupdate_global - Global state for PCI Live Update support + * @rwsem: Reader/writer semaphore used to protect the incoming and outgoi= ng + * FLBs, and the references to them in struct pci_dev. + */ +struct pci_liveupdate_global { + struct rw_semaphore rwsem; +}; + +static struct pci_liveupdate_global pci_liveupdate =3D { + .rwsem =3D __RWSEM_INITIALIZER(pci_liveupdate.rwsem), +}; + /** * struct pci_flb_outgoing - Outgoing PCI FLB object * @ser: Pointer to the preserved struct pci_ser. @@ -171,6 +208,180 @@ static struct liveupdate_flb pci_liveupdate_flb =3D { .compatible =3D PCI_LUO_FLB_COMPATIBLE, }; =20 +static void pci_liveupdate_flb_put_outgoing(void) +{ + liveupdate_flb_put_outgoing(&pci_liveupdate_flb); +} + +static struct pci_flb_outgoing *pci_liveupdate_flb_get_outgoing(void) +{ + struct pci_flb_outgoing *outgoing =3D NULL; + int ret; + + ret =3D liveupdate_flb_get_outgoing(&pci_liveupdate_flb, (void **)&outgoi= ng); + if (ret) + return ERR_PTR(ret); + + if (!outgoing) + return ERR_PTR(-ENOENT); + + return outgoing; +} + +static struct pci_dev_ser *pci_flb_alloc_dev_ser(struct pci_flb_outgoing *= outgoing) +{ + struct pci_dev_ser *dev_ser; + struct kho_block_set_it it; + u64 count =3D 0; + int err; + + kho_block_set_it_init(&it, &outgoing->block_set); + + /* Try to find an existing, previously unpreserved, entry. */ + while ((dev_ser =3D kho_block_set_it_read_entry(&it))) { + if (!dev_ser->refcount) + return dev_ser; + + count++; + } + + /* Otherwise grow the block set and reserve a new entry. */ + err =3D kho_block_set_grow(&outgoing->block_set, count + 1); + if (err) + return ERR_PTR(err); + + if (!count) + kho_block_set_it_init(&it, &outgoing->block_set); + + /* This should always succeed since kho_block_set_grow() succeeded. */ + dev_ser =3D kho_block_set_it_reserve_entry(&it); + if (WARN_ON_ONCE(!dev_ser)) + return ERR_PTR(-ENOSPC); + + return dev_ser; +} + +static void pci_liveupdate_unpreserve_device(struct pci_flb_outgoing *outg= oing, + struct pci_dev *dev) +{ + struct pci_dev_ser *dev_ser =3D dev->liveupdate.outgoing; + + if (!dev_ser) { + pci_warn(dev, "Cannot unpreserve device that is not preserved\n"); + return; + } + + pci_info(dev, "Device will no longer be preserved across next Live Update= \n"); + outgoing->ser->nr_devices--; + memset(dev_ser, 0, sizeof(*dev_ser)); + dev->liveupdate.outgoing =3D NULL; +} + +static int pci_liveupdate_preserve_device(struct pci_flb_outgoing *outgoin= g, + struct pci_dev *dev) +{ + struct pci_dev_ser *dev_ser; + + if (dev->is_virtfn) { + pci_warn(dev, "Cannot preserve Virtual Functions\n"); + return -EINVAL; + } + + if (dev->liveupdate.outgoing) { + pci_warn(dev, "Device is already preserved\n"); + return -EBUSY; + } + + if (!pci_is_root_bus(dev->bus)) { + pci_warn(dev, "Cannot preserve devices behind bridges\n"); + return -EINVAL; + } + + dev_ser =3D pci_flb_alloc_dev_ser(outgoing); + if (IS_ERR(dev_ser)) + return PTR_ERR(dev_ser); + + pci_info(dev, "Device will be preserved across next Live Update\n"); + outgoing->ser->nr_devices++; + outgoing->ser->devices =3D kho_block_set_head_pa(&outgoing->block_set); + + dev_ser->domain =3D pci_domain_nr(dev->bus); + dev_ser->bdf =3D pci_dev_id(dev); + dev_ser->refcount++; + + dev->liveupdate.outgoing =3D dev_ser; + return 0; +} + +/** + * pci_liveupdate_preserve() - Preserve a PCI device across Live Update + * @dev: The PCI device to preserve. + * + * pci_liveupdate_preserve() notifies the PCI core that a PCI device shoul= d be + * preserved across the next Live Update. Drivers are expected to call + * pci_liveupdate_preserve() from their struct liveupdate_file_handler + * preserve() callback to ensure the outgoing struct pci_ser is already se= t up. + * + * Returns: 0 on success, <0 on failure. + */ +int pci_liveupdate_preserve(struct pci_dev *dev) +{ + struct pci_flb_outgoing *outgoing =3D NULL; + int ret; + + guard(rwsem_write)(&pci_liveupdate.rwsem); + + outgoing =3D pci_liveupdate_flb_get_outgoing(); + if (IS_ERR(outgoing)) + return PTR_ERR(outgoing); + + ret =3D pci_liveupdate_preserve_device(outgoing, dev); + + pci_liveupdate_flb_put_outgoing(); + return ret; +} +EXPORT_SYMBOL_GPL(pci_liveupdate_preserve); + +/** + * pci_liveupdate_unpreserve() - Cancel preservation of a PCI device + * @dev: The PCI device to unpreserve. + * + * pci_liveupdate_unpreserve() notifies the PCI core that a PCI device sho= uld no + * longer be preserved across the next Live Update. Drivers are expected t= o call + * pci_liveupdate_unpreserve() from their struct liveupdate_file_handler + * unpreserve() callback to ensure the outgoing struct pci_ser is already = set + * up. + */ +void pci_liveupdate_unpreserve(struct pci_dev *dev) +{ + struct pci_flb_outgoing *outgoing =3D NULL; + + guard(rwsem_write)(&pci_liveupdate.rwsem); + + outgoing =3D pci_liveupdate_flb_get_outgoing(); + if (IS_ERR(outgoing)) { + pci_warn(dev, "Cannot unpreserve device without outgoing Live Update sta= te\n"); + return; + } + + pci_liveupdate_unpreserve_device(outgoing, dev); + pci_liveupdate_flb_put_outgoing(); +} +EXPORT_SYMBOL_GPL(pci_liveupdate_unpreserve); + +void pci_liveupdate_cleanup_device(struct pci_dev *dev) +{ + /* + * It should be safe to READ_ONCE() outside of the rwsem during cleanup + * since there should no longer be any references to @dev on the system. + * + * This should never happen in practice. Drivers should block removal + * while a device is preserved. + */ + if (READ_ONCE(dev->liveupdate.outgoing)) + pci_WARN(dev, 1, "Destroying outgoing-preserved device!\n"); +} + /** * pci_liveupdate_register_flb() - Register a file handler with the PCI co= re * @fh: The file handler to register. diff --git a/drivers/pci/liveupdate.h b/drivers/pci/liveupdate.h new file mode 100644 index 000000000000..b2335581f8d0 --- /dev/null +++ b/drivers/pci/liveupdate.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * PCI Live Update support (core API) + * + * Copyright (c) 2026, Google LLC. + * David Matlack + */ +#ifndef DRIVERS_PCI_LIVEUPDATE_H +#define DRIVERS_PCI_LIVEUPDATE_H + +#include + +#ifdef CONFIG_PCI_LIVEUPDATE +void pci_liveupdate_cleanup_device(struct pci_dev *dev); +#else +static inline void pci_liveupdate_cleanup_device(struct pci_dev *dev) +{ +} +#endif + +#endif /* DRIVERS_PCI_LIVEUPDATE_H */ diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 27008e2ea5af..2a37e5d3e8e3 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -24,6 +24,7 @@ #include #include #include +#include "liveupdate.h" #include "pci.h" =20 static struct resource busn_resource =3D { @@ -2485,6 +2486,7 @@ static void pci_release_dev(struct device *dev) =20 pci_dev =3D to_pci_dev(dev); pci_release_capabilities(pci_dev); + pci_liveupdate_cleanup_device(pci_dev); pci_release_of_node(pci_dev); pcibios_release_device(pci_dev); pci_bus_put(pci_dev->bus); diff --git a/include/linux/pci.h b/include/linux/pci.h index 95b723aecb08..76abe884e3dc 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -599,6 +599,9 @@ struct pci_dev { u8 tph_mode; /* TPH mode */ u8 tph_req_type; /* TPH requester type */ #endif +#ifdef CONFIG_PCI_LIVEUPDATE + struct pci_liveupdate liveupdate; +#endif }; =20 static inline struct pci_dev *pci_physfn(struct pci_dev *dev) diff --git a/include/linux/pci_liveupdate.h b/include/linux/pci_liveupdate.h index 8ec98beefcb4..894052ad6961 100644 --- a/include/linux/pci_liveupdate.h +++ b/include/linux/pci_liveupdate.h @@ -8,14 +8,26 @@ #ifndef LINUX_PCI_LIVEUPDATE_H #define LINUX_PCI_LIVEUPDATE_H =20 +#include #include +#include #include =20 +/** + * struct pci_liveupdate - PCI Live Update state for a struct pci_dev + * @outgoing: State preserved for the next kernel. + */ +struct pci_liveupdate { + struct pci_dev_ser *outgoing; +}; + struct pci_dev; =20 #ifdef CONFIG_PCI_LIVEUPDATE int pci_liveupdate_register_flb(struct liveupdate_file_handler *fh); void pci_liveupdate_unregister_flb(struct liveupdate_file_handler *fh); +int pci_liveupdate_preserve(struct pci_dev *dev); +void pci_liveupdate_unpreserve(struct pci_dev *dev); #else static inline int pci_liveupdate_register_flb(struct liveupdate_file_handl= er *fh) { @@ -25,6 +37,15 @@ static inline int pci_liveupdate_register_flb(struct liv= eupdate_file_handler *fh static inline void pci_liveupdate_unregister_flb(struct liveupdate_file_ha= ndler *fh) { } + +static inline int pci_liveupdate_preserve(struct pci_dev *dev) +{ + return -EOPNOTSUPP; +} + +static inline void pci_liveupdate_unpreserve(struct pci_dev *dev) +{ +} #endif =20 #endif /* LINUX_PCI_LIVEUPDATE_H */ --=20 2.55.0.1082.g2b9226bbc0-goog From nobody Thu Sep 24 23:04:13 2026 Received: from mail-pg1-f197.google.com (mail-pg1-f197.google.com [209.85.215.197]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4B1765237BA for ; Fri, 18 Sep 2026 20:06:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.197 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789762020; cv=none; b=YzuK+6QfYMVB72aUHrHAn3AAuedD6gXIP01cA5Jkvk36ul8ekmNnQTQA5Nuago1jmP6t/0VSfWHkwpgo/yoysr+lJ0fwUnFJQU55lMJ+H6FUzgPLM2Y6b0gQSlJUy80j7vPQlACO32a5P4dzspPE6YhcpO5LrhTdBu6tMvLqiTQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789762020; c=relaxed/simple; bh=66japuh8WuBDfpXDi5FCG+mSzPIhHxZM4KdSyUUVHCk=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=sAWzT4lBdkljSDpbOq1UKRYLJj8KOa5mb5l/SjP/EtYkrnQGiag7bmL87UkSh0LbsbJyW+047DFy58NJeIHozVfOBjLOhcv0xxsrx8Gq0tenJqzxLxNsDV+5eT/IO6BaqW2QyTMGOjNnFDNOaMq7ZPQKnYhQs1mu557/1I4OOCw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--dmatlack.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=eFs+EDoJ; arc=none smtp.client-ip=209.85.215.197 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--dmatlack.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="eFs+EDoJ" Received: by mail-pg1-f197.google.com with SMTP id 41be03b00d2f7-cc435388657so1531786a12.3 for ; Fri, 18 Sep 2026 13:06:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1789762017; x=1790366817; darn=vger.kernel.org; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:from:to:cc:subject:date:message-id:reply-to :content-type; bh=vbzZGi55dJ2nAk/bTrq7kQJL43h4zsV8Aw0NUk1rJVg=; b=eFs+EDoJpp1rp7Y0jIFP7OVwoLV9A1JoVBAy6SJ5SM6OSR89FcjCUMk3etXsV2T0N3 xE9aiVQOCJ6MalKTHCK86i/T5+eUAqGvfFDqXvB9r4FBhC8N4HGBpb94QPZetZNVyj6b 8VYZsFCT1iPdEXOOplOvv6FfoaMJ5BRPuWvH0j9xlqY0HdWRWx3tcmbaTV0hF5J2K2HQ BGuG2sVKv8Z0nz8DwjJYg58majjG4eoSHYiSFQ6gEQq1b69jaGA7kzRHI1keoriBEsCY vkI1j4oCqzKL8BBraJ0jdxoYNWfNf4boTNIhpSackMweOpx1U7KAox25QsZMjz9w+XGS VNfw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1789762017; x=1790366817; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=vbzZGi55dJ2nAk/bTrq7kQJL43h4zsV8Aw0NUk1rJVg=; b=d5CcrYm/5BXT606XFzKAy2/MMeuZkfJJbOioNd9BvcA2zyTazKsxFX4IidtupXj16d zVDFWS0WZ1vannTm+OZCRpqz2Vd9tQpdt844eN4kUeHZzpj9aKpwQCIKXtfKnlMKqhLR hWwmUuKKKtEUKgXZW52E+3Gk3nPlKqyWDrXgoUaFuK2UFvo5Gq63SCk/FqYSsklJ0/TP SodE5lHQRbM85XyvcJKeM00fqhG/MhaNOoowW6N6zqhuY6ZWVORqNWCARc7wd5/NY+Bs AgYpFrE/5X+N3GZPWV9uk3iLlx8zTcSs0xNPEyR4+Zz/vd7kRa5uSAzDJPPJ9p1UW8dC aRzg== X-Forwarded-Encrypted: i=1; AKwUvBxi40yscfW/wFAqTIlwNZY5VqGcLYL5D8q2zX3KEnuD2x2Av46ISLsLo5Nqub3etygOJ+cuiQlxpY8Vc7Y=@vger.kernel.org X-Gm-Message-State: AFuF++lAtgoqd90K6z3BY5lGsRHU4DrEDuale90SZBvpNGirheDNxqWc Q6wZSXygQJYO8Anl5rOz3esH05hqz8BRM8i7l3iRjFedPIw5mJWbHHyOdFRBP8fbRl9Uue2J1CM UWuW78Tt1XJZ7zA== X-Received: from pgav24.prod.google.com ([2002:a05:6a02:2dd8:b0:cc5:a49:270f]) (user=dmatlack job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6a21:35c6:b0:3dd:a248:9c1c with SMTP id adf61e73a8af0-3dda248a06fmr654547637.46.1789762016381; Fri, 18 Sep 2026 13:06:56 -0700 (PDT) Date: Fri, 18 Sep 2026 20:06:29 +0000 In-Reply-To: <20260918200640.887030-1-dmatlack@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260918200640.887030-1-dmatlack@google.com> X-Mailer: git-send-email 2.55.0.1082.g2b9226bbc0-goog Message-ID: <20260918200640.887030-4-dmatlack@google.com> Subject: [PATCH v9 03/13] PCI: liveupdate: Track incoming preserved PCI devices From: David Matlack To: kexec@lists.infradead.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, linux-pci@vger.kernel.org Cc: Adithya Jayachandran , Alexander Graf , Alex Williamson , Bjorn Helgaas , Chris Li , David Matlack , David Rientjes , Jacob Pan , Jason Gunthorpe , Jonathan Corbet , Josh Hilke , Leon Romanovsky , Lukas Wunner , Mike Rapoport , Parav Pandit , Pasha Tatashin , Pranjal Shrivastava , Pratyush Yadav , Randy Dunlap , Saeed Mahameed , Samiullah Khawaja , Shuah Khan , Vipin Sharma , William Tu , Yi Liu Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" During PCI enumeration, the previous kernel might have passed state about devices that were preserved across kexec. The PCI core needs to fetch this state to identify which devices are "incoming" and require special handling. Add pci_liveupdate_setup_device() which is called during device setup to fetch the serialized state (struct pci_ser) from the Live Update Orchestrator. The first time this happens, pci_flb_retrieve() will run and convert the array of pci_dev_ser structs into an xarray so that it can be looked up efficiently. If a device is found in the xarray, the PCI core stores a pointer to its state in dev->liveupdate_incoming until pci_liveupdate_finish() is called by the driver. This pointer allows the PCI core and drivers to apply Live Update-specific logic to incoming devices in subsequent commits. Drivers can check if a device is an incoming preserved device (e.g. during probe) by calling pci_liveupdate_is_incoming(). Note that any error during pci_flb_retrieve() must be treated as fatal. The previous kernel handed off PCI devices that are performing DMA and the current kernel cannot safely take over those devices without this state. CONFIG_64BIT is now required to enable CONFIG_PCI_LIVEUPDATE so that the domain and bdf can be guaranteed to fit in an unsigned long and be used as the xarray key. Reviewed-by: Pranjal Shrivastava Reviewed-by: Samiullah Khawaja Signed-off-by: David Matlack --- MAINTAINERS | 1 + drivers/pci/Kconfig | 2 +- drivers/pci/liveupdate.c | 286 ++++++++++++++++++++++++++++++++- drivers/pci/liveupdate.h | 5 + drivers/pci/probe.c | 3 + include/linux/pci_liveupdate.h | 13 ++ 6 files changed, 303 insertions(+), 7 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index bb9ef5460b5c..3eacaa98775c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -21056,6 +21056,7 @@ L: linux-pci@vger.kernel.org S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/liveupdate/linux.git F: drivers/pci/liveupdate.c +F: drivers/pci/liveupdate.h F: include/linux/kho/abi/pci.h F: include/linux/pci_liveupdate.h =20 diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 3781e2b5f095..8af20f558086 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -273,7 +273,7 @@ config VGA_ARB_MAX_GPUS =20 config PCI_LIVEUPDATE bool "PCI Live Update Support" - depends on PCI && LIVEUPDATE + depends on PCI && LIVEUPDATE && 64BIT help Enable PCI core support for preserving PCI devices across Live Update. This, in combination with support in a device's driver, diff --git a/drivers/pci/liveupdate.c b/drivers/pci/liveupdate.c index e0ca537d0cb3..00776260ad6f 100644 --- a/drivers/pci/liveupdate.c +++ b/drivers/pci/liveupdate.c @@ -45,6 +45,11 @@ * it (e.g. during enumeration), and pci_flb_finish() frees it once the PCI * core is done with it. * + * State handed over by the previous kernel is trusted. The PCI core valid= ates + * it only far enough to detect an incompatible or corrupt hand over, and = makes + * no attempt to defend against deliberate modification, since a previous = kernel + * able to corrupt preserved state is able to corrupt arbitrary memory any= way. + * * Call Flow * --------- * @@ -69,14 +74,17 @@ * * # ---------------- kexec ---------------- * - * # New kernel: the PCI core asks for the previous kernel's state - * liveupdate_flb_get_incoming() - * luo_flb_retrieve_one() # first request only - * pci_flb_retrieve() # previous kernel's struct pci_s= er + * # New kernel: PCI enumeration + * pci_setup_device() + * pci_liveupdate_setup_device() + * liveupdate_flb_get_incoming() + * luo_flb_retrieve_one() # first request only + * pci_flb_retrieve() # previous kernel's struct pci_s= er * * # Userspace: ioctl(LIVEUPDATE_SESSION_FINISH) * luo_file_finish_one() * fh->ops->finish() # driver callback + * pci_liveupdate_finish(dev) # release this device's pci_dev_= ser * luo_flb_file_finish() * liveupdate_flb_put_incoming() # last incoming file only * pci_flb_finish() # free struct pci_ser @@ -93,6 +101,20 @@ * This allows the PCI core to keep its FLB data (struct pci_ser) up to da= te * with the list of **outgoing** preserved devices for the next kernel. * + * After kexec, whenever a device is enumerated, the PCI core will check i= f it + * is an **incoming** preserved device (i.e. preserved by the previous ker= nel) + * by checking the incoming FLB data (struct pci_ser). + * + * Drivers must notify the PCI core when an **incoming** device is done + * participating in the incoming Live Update with the following API: + * + * * ``pci_liveupdate_finish(pci_dev)`` + * + * The PCI core does not enforce any ordering of ``pci_liveupdate_finish()= `` and + * ``pci_liveupdate_preserve()``, i.e., a PCI device can be **outgoing** + * (preserved for next kernel) and **incoming** (preserved by previous ker= nel) + * at the same time. + * * Restrictions * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D * @@ -144,6 +166,27 @@ struct pci_flb_outgoing { struct kho_block_set block_set; }; =20 +/** + * struct pci_flb_incoming - Incoming PCI FLB object + * @ser: The incoming struct pci_ser from the previous kernel. + * @xa: Xarray used to quickly lookup devices in @ser. + * @block_set: The KHO block set holding the incoming devices. + * + * This structure holds the runtime state for the incoming PCI Live Update + * state. It wraps the serialized pci_ser, the block_set used to restore + * the serialized entries, and an xarray for fast lookups. + */ +struct pci_flb_incoming { + struct pci_ser *ser; + struct xarray xa; + struct kho_block_set block_set; +}; + +static unsigned long pci_ser_xa_key(u32 domain, u16 bdf) +{ + return (unsigned long)domain << 16 | bdf; +} + static int pci_flb_preserve(struct liveupdate_flb_op_args *args) { struct pci_flb_outgoing *outgoing __free(kfree) =3D NULL; @@ -182,17 +225,106 @@ static void pci_flb_unpreserve(struct liveupdate_flb= _op_args *args) kfree(outgoing); } =20 +/* + * Any failure here is fatal. The previous kernel handed over devices that= are + * still performing DMA, and this kernel cannot identify them without this + * state. Continuing would let the PCI core reassign bus numbers and rebind + * drivers underneath live devices, so fail loudly instead of unwinding. + */ static int pci_flb_retrieve(struct liveupdate_flb_op_args *args) { + struct pci_ser *ser =3D phys_to_virt(args->data); + struct pci_flb_incoming *incoming; + struct pci_dev_ser *dev_ser; + struct kho_block_set_it it; + int ret; + pr_debug("Retrieving struct pci_ser (0x%llx)\n", args->data); - args->obj =3D phys_to_virt(args->data); + + if (ser->version !=3D PCI_LUO_FLB_VERSION) + panic("Incoming PCI FLB version (v%d) is incompatible with this kernel (= v%d)\n", + ser->version, PCI_LUO_FLB_VERSION); + + incoming =3D kzalloc_obj(*incoming); + if (!incoming) + panic("Failed to allocate struct pci_flb_incoming\n"); + + incoming->ser =3D ser; + xa_init(&incoming->xa); + + kho_block_set_init(&incoming->block_set, sizeof(struct pci_dev_ser)); + ret =3D kho_block_set_restore(&incoming->block_set, ser->devices); + if (ret) + panic("Failed to restore devices KHO block set (%d)\n", ret); + + kho_block_set_it_init(&it, &incoming->block_set); + while ((dev_ser =3D kho_block_set_it_read_entry(&it))) { + unsigned long key; + + if (!dev_ser->refcount) + continue; + + key =3D pci_ser_xa_key(dev_ser->domain, dev_ser->bdf); + ret =3D xa_insert(&incoming->xa, key, dev_ser, GFP_KERNEL); + if (ret) + panic("Failed to insert PCI device %04x:%02x:%02x.%d into xarray (%d)\n= ", + dev_ser->domain, PCI_BUS_NUM(dev_ser->bdf), + PCI_SLOT(dev_ser->bdf), PCI_FUNC(dev_ser->bdf), + ret); + } + + args->obj =3D incoming; return 0; } =20 +static void pci_check_all_devices_finished(struct pci_flb_incoming *incomi= ng) +{ + struct pci_dev_ser *dev_ser; + unsigned long index; + u32 nr_devices; + + /* + * nr_devices is only decremented by pci_liveupdate_finish_device(). + * This runs once the last reference to the incoming FLB is dropped, so + * there are no finishers left in flight. + */ + nr_devices =3D incoming->ser->nr_devices; + if (nr_devices =3D=3D 0) + return; + + /* + * Report the unfinished devices from the incoming FLB rather than by + * walking struct pci_dev, so that devices that never showed up after + * kexec, or that were destroyed before they finished, are identified + * as well. + */ + xa_for_each(&incoming->xa, index, dev_ser) { + if (!dev_ser->refcount) + continue; + + pr_emerg("%04x:%02x:%02x.%d was never finished!\n", + dev_ser->domain, PCI_BUS_NUM(dev_ser->bdf), + PCI_SLOT(dev_ser->bdf), PCI_FUNC(dev_ser->bdf)); + } + + /* + * This should only happen if a driver violated the contract to call + * pci_liveupdate_finish() (something is extremely broken). + */ + panic("%u preserved device(s) were never finished!\n", nr_devices); +} + static void pci_flb_finish(struct liveupdate_flb_op_args *args) { + struct pci_flb_incoming *incoming =3D args->obj; + pr_debug("Finished struct pci_ser (0x%llx)\n", args->data); - kho_restore_free(args->obj); + pci_check_all_devices_finished(incoming); + + xa_destroy(&incoming->xa); + kho_block_set_destroy(&incoming->block_set); + kho_restore_free(incoming->ser); + kfree(incoming); } =20 static struct liveupdate_flb_ops pci_liveupdate_flb_ops =3D { @@ -369,6 +501,75 @@ void pci_liveupdate_unpreserve(struct pci_dev *dev) } EXPORT_SYMBOL_GPL(pci_liveupdate_unpreserve); =20 +static struct pci_flb_incoming *pci_liveupdate_flb_get_incoming(void) +{ + struct pci_flb_incoming *incoming =3D NULL; + int ret; + + ret =3D liveupdate_flb_get_incoming(&pci_liveupdate_flb, (void **)&incomi= ng); + + /* Live Update is not enabled. */ + if (ret =3D=3D -EOPNOTSUPP) + return NULL; + + /* Live Update is enabled, but there is no incoming FLB data. */ + if (ret =3D=3D -ENODATA) + return NULL; + + /* + * Live Update is enabled and there is incoming FLB data, but none of it + * matches pci_liveupdate_flb.compatible. + */ + if (ret =3D=3D -ENOENT) + return NULL; + + /* + * There is incoming FLB data that matches pci_liveupdate_flb.compatible + * but retrieve failed (pci_flb_retrieve() returned an error or LUO + * failed to acquire a reference to pci_liveupdate_flb_ops.owner). + */ + if (ret) + panic("Failed to retrieve incoming FLB data (%d)\n", ret); + + return incoming; +} + +static void pci_liveupdate_flb_put_incoming(void) +{ + liveupdate_flb_put_incoming(&pci_liveupdate_flb); +} + +void pci_liveupdate_setup_device(struct pci_dev *dev) +{ + struct pci_flb_incoming *incoming; + struct pci_dev_ser *dev_ser; + unsigned long key; + + guard(rwsem_write)(&pci_liveupdate.rwsem); + + incoming =3D pci_liveupdate_flb_get_incoming(); + if (!incoming) + return; + + key =3D pci_ser_xa_key(pci_domain_nr(dev->bus), pci_dev_id(dev)); + dev_ser =3D xa_load(&incoming->xa, key); + + /* + * This device was not preserved across Live Update, or it was preserved + * but has already been probed and gone through pci_liveupdate_finish(), + * e.g. due to removing and re-adding the device. Either way, it's not + * treated as incoming-preserved. + */ + if (!dev_ser || !dev_ser->refcount) { + pci_liveupdate_flb_put_incoming(); + return; + } + + pci_info(dev, "Device was preserved by previous kernel across Live Update= \n"); + dev->liveupdate.incoming =3D dev_ser; + pci_liveupdate_flb_put_incoming(); +} + void pci_liveupdate_cleanup_device(struct pci_dev *dev) { /* @@ -380,7 +581,80 @@ void pci_liveupdate_cleanup_device(struct pci_dev *dev) */ if (READ_ONCE(dev->liveupdate.outgoing)) pci_WARN(dev, 1, "Destroying outgoing-preserved device!\n"); + + if (READ_ONCE(dev->liveupdate.incoming)) + pci_WARN(dev, 1, "Destroying incoming-preserved device!\n"); +} + +static void pci_liveupdate_finish_device(struct pci_ser *ser, struct pci_d= ev *dev) +{ + if (!dev->liveupdate.incoming) { + pci_warn(dev, "Cannot finish preserving an unpreserved device\n"); + return; + } + + if (dev->liveupdate.incoming->refcount !=3D 1) { + pci_WARN(dev, 1, "Preserved device has a corrupted refcount!\n"); + return; + } + + /* + * Drop the refcount so this device does not get treated as an incoming + * device again, e.g. in case pci_liveupdate_setup_device() gets called + * again because the device is hot-plugged. + */ + dev->liveupdate.incoming->refcount =3D 0; + + pci_info(dev, "Device is finished participating in Live Update\n"); + dev->liveupdate.incoming =3D NULL; + ser->nr_devices--; +} + +/** + * pci_liveupdate_finish() - Finish the preservation of a PCI device + * @dev: The PCI device + * + * pci_liveupdate_finish() notifies the PCI core that a PCI device that was + * preserved across the previous Live Update has finished participating in= Live + * Update. Drivers must call pci_liveupdate_finish() from their struct + * liveupdate_file_handler finish() callback to ensure the incoming struct + * pci_ser is allocated. + */ +void pci_liveupdate_finish(struct pci_dev *dev) +{ + struct pci_flb_incoming *incoming; + + guard(rwsem_write)(&pci_liveupdate.rwsem); + + incoming =3D pci_liveupdate_flb_get_incoming(); + if (!incoming) { + pci_warn(dev, "Cannot finish preserving device without incoming FLB\n"); + return; + } + + pci_liveupdate_finish_device(incoming->ser, dev); + pci_liveupdate_flb_put_incoming(); +} +EXPORT_SYMBOL_GPL(pci_liveupdate_finish); + +/** + * pci_liveupdate_is_incoming() - Check if a device is incoming-preserved + * @dev: The PCI device to check + * + * Check if a device was preserved across Live Update by the previous kern= el, + * i.e. the device is incoming-preserved. Note that a device is only consi= dered + * incoming-preserved prior to pci_liveupdate_finish(). It is up to driver= s to + * synchronize usage of pci_liveupdate_is_incoming() with their own call to + * pci_liveupdate_finish() to avoid acting on stale data. + * + * Returns: True if the device is incoming-preserved, false otherwise. + */ +bool pci_liveupdate_is_incoming(struct pci_dev *dev) +{ + guard(rwsem_read)(&pci_liveupdate.rwsem); + return dev->liveupdate.incoming; } +EXPORT_SYMBOL_GPL(pci_liveupdate_is_incoming); =20 /** * pci_liveupdate_register_flb() - Register a file handler with the PCI co= re diff --git a/drivers/pci/liveupdate.h b/drivers/pci/liveupdate.h index b2335581f8d0..eaaa3559fd77 100644 --- a/drivers/pci/liveupdate.h +++ b/drivers/pci/liveupdate.h @@ -11,8 +11,13 @@ #include =20 #ifdef CONFIG_PCI_LIVEUPDATE +void pci_liveupdate_setup_device(struct pci_dev *dev); void pci_liveupdate_cleanup_device(struct pci_dev *dev); #else +static inline void pci_liveupdate_setup_device(struct pci_dev *dev) +{ +} + static inline void pci_liveupdate_cleanup_device(struct pci_dev *dev) { } diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 2a37e5d3e8e3..ad7fdf0d56b6 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -2065,6 +2065,8 @@ int pci_setup_device(struct pci_dev *dev) if (pci_early_dump) early_dump_pci_device(dev); =20 + pci_liveupdate_setup_device(dev); + /* Need to have dev->class ready */ dev->cfg_size =3D pci_cfg_space_size(dev); =20 @@ -2188,6 +2190,7 @@ int pci_setup_device(struct pci_dev *dev) default: /* unknown header */ pci_err(dev, "unknown header type %02x, ignoring device\n", dev->hdr_type); + pci_liveupdate_cleanup_device(dev); pci_release_of_node(dev); return -EIO; =20 diff --git a/include/linux/pci_liveupdate.h b/include/linux/pci_liveupdate.h index 894052ad6961..710026ada2d5 100644 --- a/include/linux/pci_liveupdate.h +++ b/include/linux/pci_liveupdate.h @@ -16,9 +16,11 @@ /** * struct pci_liveupdate - PCI Live Update state for a struct pci_dev * @outgoing: State preserved for the next kernel. + * @incoming: State preserved by the previous kernel. */ struct pci_liveupdate { struct pci_dev_ser *outgoing; + struct pci_dev_ser *incoming; }; =20 struct pci_dev; @@ -28,6 +30,8 @@ int pci_liveupdate_register_flb(struct liveupdate_file_ha= ndler *fh); void pci_liveupdate_unregister_flb(struct liveupdate_file_handler *fh); int pci_liveupdate_preserve(struct pci_dev *dev); void pci_liveupdate_unpreserve(struct pci_dev *dev); +void pci_liveupdate_finish(struct pci_dev *dev); +bool pci_liveupdate_is_incoming(struct pci_dev *dev); #else static inline int pci_liveupdate_register_flb(struct liveupdate_file_handl= er *fh) { @@ -46,6 +50,15 @@ static inline int pci_liveupdate_preserve(struct pci_dev= *dev) static inline void pci_liveupdate_unpreserve(struct pci_dev *dev) { } + +static inline void pci_liveupdate_finish(struct pci_dev *dev) +{ +} + +static inline bool pci_liveupdate_is_incoming(struct pci_dev *dev) +{ + return false; +} #endif =20 #endif /* LINUX_PCI_LIVEUPDATE_H */ --=20 2.55.0.1082.g2b9226bbc0-goog From nobody Thu Sep 24 23:04:13 2026 Received: from mail-pl1-f199.google.com (mail-pl1-f199.google.com [209.85.214.199]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 20B3148F01F for ; Fri, 18 Sep 2026 20:06:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.199 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789762019; cv=none; b=ufbxYtg3kJXw4RxAVLcMFVvMyeDbLJMTwzpW/hwWzhCfgpE1PPaLBv/sbpVLK7b2BpXtqBP+xMz8jQRO2fkm7N/4StdFcXplDjJOghcXM9KLDZnFHm5Q+BRF7qnOJ4QK7ZZs+5cbtwKBNKe3WtvfVK4flZRGdtRIX8xCtSQiMmA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789762019; c=relaxed/simple; bh=gI0fncRiDYrkTvLtoHzbZmQWSjU1781MXxP2rDHUBdQ=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=jTVKkvRDv3dbC9HL0BI6VrMnyyH+LJj6wRyQjkHZ9HasS904tdC/ue0yvL2Wyk2svN5aNOjNSQbYhsHduN9hPspOtx2gFgzjmtiYB6EpaMXKYWVKq91vI/btUeumzgqN0SNL3UtHgtNdcSBrViqInP/HN4B7TMBGX4Rpwjut9S4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--dmatlack.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=Qgd4TNcV; arc=none smtp.client-ip=209.85.214.199 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--dmatlack.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="Qgd4TNcV" Received: by mail-pl1-f199.google.com with SMTP id d9443c01a7336-2dd7d0751efso12550175ad.0 for ; Fri, 18 Sep 2026 13:06:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1789762017; x=1790366817; darn=vger.kernel.org; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:from:to:cc:subject:date:message-id:reply-to :content-type; bh=VDTO4eHKdMRuuk9UsFB+WlCX7AwxRYK2xSqBvBKhZIs=; b=Qgd4TNcVRh4a9JKozwd447xwC6SpYxlenDZ42t+yIX5gYOgw0D5iPS2RWQz997rPEF U7A/YEEO2legBVNitIkkSQyNHh/usKNRkSQ5WIwV1gUP4xp4iHnRnA/fX8zkAgMIJQL+ mKTEpGyDFGHlupdEShnVkP+Dpz2BOUsi80BzwvnaO4vA3g7IkjAHDDFngRCAd+CgHMZt fUmDE8Z12hnh/lmUGUTpbszr/Rm9fICx8rQtlDeUrFIQSHXCkP4vcb6k8L+nLLTB2ybg zB/4ZQNub89FUzXHr26sqDBkzfsVZVD8aGpDqsSUukLdhpHNTMsN11tWef0prN4FlplY U7Qg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1789762017; x=1790366817; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=VDTO4eHKdMRuuk9UsFB+WlCX7AwxRYK2xSqBvBKhZIs=; b=0qYs1nRhq82ELTwLRopeNgZZYGdgbDhGkI61xgCvy/697otFapknWbPxoI/ErNiYli FRSfKIx2kQqGY8T7sTyrMLXSjUPjGggKeG3/XJny6l2UoQjn3Dq7l4IfOnT7VY8GJ1nO Q3uGZ8S+AaxpzJng6Q0IpnYph9GcNgh1LOkKf5DZdnWn9qW8l/olGVcYtblwYpAy3NVm rzvrjP+39ze3pv9a4OkX5cTMESRzZg6P/M8eqxDkQjw2NgJGBdcSWrgcusnLt9/nOI7u i+U/1xKCo9uy3TbPqcpk5JNNDjL+MpzWot+zT4+kqqbYQLfLs1k/vN4FHWchTzvX4ngr rEpQ== X-Forwarded-Encrypted: i=1; AKwUvByiEXkU/4lfl8Sz4wo2lHqGtz/z0htPO4OZWQg85CQqKYAx1NWfLJLrpYTmokXKpVtEqDJUdp9nPaATN0g=@vger.kernel.org X-Gm-Message-State: AFuF++nICRdsXeouNUmJ+7Rfi08xOSNOTGpBmZQXP4hJNFv7KrzawcJb yXytJTf3cAb8CEK4Yh34FWGANlZG+rTgZE3rO/2jqmfRuaZlqtE0LoCDEeV+gCEHP/UsvESVPdF ngSzKCph8TOQ7Dg== X-Received: from plmk4.prod.google.com ([2002:a17:903:1804:b0:2dd:be98:5b81]) (user=dmatlack job=prod-delivery.src-stubby-dispatcher) by 2002:a17:902:ef48:b0:2cf:7db9:e13e with SMTP id d9443c01a7336-2ddb20fe772mr43004845ad.3.1789762017240; Fri, 18 Sep 2026 13:06:57 -0700 (PDT) Date: Fri, 18 Sep 2026 20:06:30 +0000 In-Reply-To: <20260918200640.887030-1-dmatlack@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260918200640.887030-1-dmatlack@google.com> X-Mailer: git-send-email 2.55.0.1082.g2b9226bbc0-goog Message-ID: <20260918200640.887030-5-dmatlack@google.com> Subject: [PATCH v9 04/13] PCI: liveupdate: Document driver binding responsibilities From: David Matlack To: kexec@lists.infradead.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, linux-pci@vger.kernel.org Cc: Adithya Jayachandran , Alexander Graf , Alex Williamson , Bjorn Helgaas , Chris Li , David Matlack , David Rientjes , Jacob Pan , Jason Gunthorpe , Jonathan Corbet , Josh Hilke , Leon Romanovsky , Lukas Wunner , Mike Rapoport , Parav Pandit , Pasha Tatashin , Pranjal Shrivastava , Pratyush Yadav , Randy Dunlap , Saeed Mahameed , Samiullah Khawaja , Shuah Khan , Vipin Sharma , William Tu , Yi Liu Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Document how driver binding works during a Live Update and what the PCI core expects of drivers and users. Note that this is only a description of the current division of responsibilities. These can change in the future if we decide. Reviewed-by: Pasha Tatashin Reviewed-by: Pranjal Shrivastava Reviewed-by: Samiullah Khawaja Signed-off-by: David Matlack --- drivers/pci/liveupdate.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/pci/liveupdate.c b/drivers/pci/liveupdate.c index 00776260ad6f..ec8db86ed66d 100644 --- a/drivers/pci/liveupdate.c +++ b/drivers/pci/liveupdate.c @@ -123,6 +123,20 @@ * * * The device cannot be a Virtual Function (VF). * * The device cannot be behind a PCI-to-PCI bridge. + * + * Driver Binding + * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + * + * In the outgoing kernel, the driver must ensure that it does not release= a + * device between pci_liveupdate_preserve() and pci_liveupdate_unpreserve(= ). + * + * In the incoming kernel, the driver must ensure that it does not release= a + * preserved device between probe() and pci_liveupdate_finish(). + * + * It is the user's responsibility to ensure that incoming preserved devic= es are + * bound to the correct driver. The PCI core does not protect against a de= vice + * getting preserved by driver A in the outgoing kernel and then getting b= ound + * to driver B in the incoming kernel. */ =20 #define pr_fmt(fmt) "PCI: liveupdate: " fmt --=20 2.55.0.1082.g2b9226bbc0-goog From nobody Thu Sep 24 23:04:13 2026 Received: from mail-pl1-f199.google.com (mail-pl1-f199.google.com [209.85.214.199]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D17A0361964 for ; Fri, 18 Sep 2026 20:06:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.199 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789762022; cv=none; b=aZwalW1nrkHJqDYqZOBDS4Qlr/GwuJs4+OWYZppVNeF6rdb8IzNDC84OpuvClBQKa2vruVm8nafz7zGDm4gPCszyYMDp3/dRvbFH/0+4qbN8epRkGgmnTDccQrAdU5NMNljAlAcHMhWoZDjGJfDKG07Z7UT5B0+dLhCoXjTYN7M= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789762022; c=relaxed/simple; bh=zWFlXN9OGp6An+xWtdjWA3hJp9SS26McmEz7RkrDzIE=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=BoTGMPQN0iLrza18qk6Y0u5glsIPTxII773IYRQNoOtzX4B6ColowWmDiKiQEQjE0yXH0bx6gTlEHvGUfIj0ppCdAYgibWsFK6tt+/4+2/mcusTAep2tB3WQuA8NjFJ93dUiZqHJmglmqkjasBZLGMNufowcUPP6cMYcCuyY4H4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--dmatlack.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=H/eTdbBU; arc=none smtp.client-ip=209.85.214.199 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--dmatlack.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="H/eTdbBU" Received: by mail-pl1-f199.google.com with SMTP id d9443c01a7336-2dc92350888so18273125ad.3 for ; Fri, 18 Sep 2026 13:06:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1789762018; x=1790366818; darn=vger.kernel.org; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:from:to:cc:subject:date:message-id:reply-to :content-type; bh=YSbW92ll6VraodQnupXl7opG78OubPyViHFwv8TLCbk=; b=H/eTdbBUmfWlqe61AyyzifkSwCAmCcIkJj5ZmPeNuSMpClCwIvvnw47aY00gvl0CvI Ect9G8CTrp59iBGHunk3B1eGT/pveoHTNRGb7BA5Q2UJYP1euPeu8cXUmpQk3x3+Pyfn S5QBMBKQe+2NYjJ18jfWxmLGKA8KL85r8wtY7O/5cEhVqG9CHm5qKCYQC5y+In5oEIpF +N0dqq6RZF5W7BAAf3/Qf103ZSmmJItMlsy/HnZdClD2Hb4WRRxWh8uoTTO6m+4F2nQJ Rpa+h6iqWIzE5GzIGLCZoTNhbEcK4QZXWD7QAyPkRjkvg6c9goDMVlaoA8QYncwLWvqE owRA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1789762018; x=1790366818; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=YSbW92ll6VraodQnupXl7opG78OubPyViHFwv8TLCbk=; b=iQ8lYWx+BWhNC/RDDPCxCwHlsPAOFOHuEl/Q4fbxX4Ao0wtjuHL+Iv/3F4xQSHDfxO 63xIIRXlij1FSl6nUXTmS3fQ6j5rT/GNN+CP2g8OkwbvlRxwf53ykISAv2PJO19uARaI 7RQTv2b6eBP71C8Fo3HZ83+gRLEXeric6O0Dvj6eUDqfHMXbfuvQIcJW/9JU7Uc2e4Ol bTvBM3T73nd4Mm0RR0ylazGSaZsluObMzwWf3n0L0FFki1a0/nMAnWqdLxCTUfnMY340 PYjEnwfLjmOK2MKtNQFVuWhrJiHMQ0CW78LyF+XDfywaxBy52ME4GGpUasI4Hss7Llbu suQQ== X-Forwarded-Encrypted: i=1; AKwUvBz8VRhrHjSArSQzQsHr9trzLWv9jTV+2CcI6a8Cd8iCBCDljgEDR6itNCoaIx3tPo/KH9Ib6FI36JAqrYQ=@vger.kernel.org X-Gm-Message-State: AFuF++nxyXrBxTBbNliqj9SnFBRC2Q5o7V0D+Xoi/aP8Hz6q7l3BCbvS Kc01Q3EBnKTYUMyuaxK6eZaPyrtRx7a6GxCw2Qg8HDjHFst0um+9DmBd+heoCnOsgoPmFjAAFmA Olr147hrR3RUZCQ== X-Received: from pgbcn9.prod.google.com ([2002:a05:6a02:a89:b0:cc1:be9e:35ef]) (user=dmatlack job=prod-delivery.src-stubby-dispatcher) by 2002:a17:903:4b28:b0:2dd:c100:a5e7 with SMTP id d9443c01a7336-2ddc100a6e5mr8872815ad.59.1789762018048; Fri, 18 Sep 2026 13:06:58 -0700 (PDT) Date: Fri, 18 Sep 2026 20:06:31 +0000 In-Reply-To: <20260918200640.887030-1-dmatlack@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260918200640.887030-1-dmatlack@google.com> X-Mailer: git-send-email 2.55.0.1082.g2b9226bbc0-goog Message-ID: <20260918200640.887030-6-dmatlack@google.com> Subject: [PATCH v9 05/13] PCI: liveupdate: Auto-preserve upstream bridges across Live Update From: David Matlack To: kexec@lists.infradead.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, linux-pci@vger.kernel.org Cc: Adithya Jayachandran , Alexander Graf , Alex Williamson , Bjorn Helgaas , Chris Li , David Matlack , David Rientjes , Jacob Pan , Jason Gunthorpe , Jonathan Corbet , Josh Hilke , Leon Romanovsky , Lukas Wunner , Mike Rapoport , Parav Pandit , Pasha Tatashin , Pranjal Shrivastava , Pratyush Yadav , Randy Dunlap , Saeed Mahameed , Samiullah Khawaja , Shuah Khan , Vipin Sharma , William Tu , Yi Liu Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" When a PCI device is preserved across a Live Update, all of its upstream bridges up to the root port must also be preserved. This enables the PCI core and any drivers bound to the bridges to manage bridges correctly across a Live Update. Notably, this will be used in subsequent commits to ensure that preserved devices can continue performing memory transactions without a disruption or change in routing. To preserve bridges, the PCI core tracks the number of downstream devices preserved under each bridge using a reference count in struct pci_dev_ser. This allows a bridge to remain preserved until all its downstream preserved devices are unpreserved or finish their participation in the Live Update. Reviewed-by: Pasha Tatashin Reviewed-by: Pranjal Shrivastava Signed-off-by: David Matlack --- drivers/pci/liveupdate.c | 125 +++++++++++++++++++++++++++--------- include/linux/kho/abi/pci.h | 5 +- include/linux/pci.h | 3 + 3 files changed, 99 insertions(+), 34 deletions(-) diff --git a/drivers/pci/liveupdate.c b/drivers/pci/liveupdate.c index ec8db86ed66d..825df024eec4 100644 --- a/drivers/pci/liveupdate.c +++ b/drivers/pci/liveupdate.c @@ -122,7 +122,6 @@ * preserved. These may be relaxed in the future: * * * The device cannot be a Virtual Function (VF). - * * The device cannot be behind a PCI-to-PCI bridge. * * Driver Binding * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D @@ -137,6 +136,18 @@ * bound to the correct driver. The PCI core does not protect against a de= vice * getting preserved by driver A in the outgoing kernel and then getting b= ound * to driver B in the incoming kernel. + * + * PCI-to-PCI Bridges + * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + * + * Any PCI-to-PCI bridges upstream of a preserved device are automatically + * preserved when the device is preserved. The PCI core keeps track of the + * number of downstream devices that are preserved under a bridge so that = the + * bridge is only unpreserved once all downstream devices are unpreserved. + * + * This enables the PCI core and any drivers bound to the bridge to partic= ipate + * in the Live Update so that preserved endpoints can continue issuing mem= ory + * transactions during the Live Update. */ =20 #define pr_fmt(fmt) "PCI: liveupdate: " fmt @@ -407,55 +418,84 @@ static struct pci_dev_ser *pci_flb_alloc_dev_ser(stru= ct pci_flb_outgoing *outgoi return dev_ser; } =20 -static void pci_liveupdate_unpreserve_device(struct pci_flb_outgoing *outg= oing, - struct pci_dev *dev) +static int pci_liveupdate_unpreserve_device(struct pci_flb_outgoing *outgo= ing, + struct pci_dev *dev) { struct pci_dev_ser *dev_ser =3D dev->liveupdate.outgoing; =20 if (!dev_ser) { pci_warn(dev, "Cannot unpreserve device that is not preserved\n"); - return; + return -EINVAL; } =20 + if (!dev_ser->refcount) { + pci_WARN(dev, 1, "Preserved device has a 0 refcount!\n"); + return -EINVAL; + } + + if (--dev_ser->refcount) + return 0; + pci_info(dev, "Device will no longer be preserved across next Live Update= \n"); outgoing->ser->nr_devices--; memset(dev_ser, 0, sizeof(*dev_ser)); dev->liveupdate.outgoing =3D NULL; + return 0; +} + +static void pci_liveupdate_unpreserve_path(struct pci_flb_outgoing *outgoi= ng, + struct pci_dev *dev, + struct pci_dev *end) +{ + for_each_pci_dev_in_path(dev) { + if (dev =3D=3D end) + break; + + if (pci_liveupdate_unpreserve_device(outgoing, dev)) + return; + } } =20 static int pci_liveupdate_preserve_device(struct pci_flb_outgoing *outgoin= g, struct pci_dev *dev) { - struct pci_dev_ser *dev_ser; - if (dev->is_virtfn) { pci_warn(dev, "Cannot preserve Virtual Functions\n"); return -EINVAL; } =20 - if (dev->liveupdate.outgoing) { + /* + * Endpoint devices should not be preserved more than once. + * Bridges are preserved once for every downstream device that + * is preserved. + */ + if (dev->liveupdate.outgoing && !dev->subordinate) { pci_warn(dev, "Device is already preserved\n"); return -EBUSY; } =20 - if (!pci_is_root_bus(dev->bus)) { - pci_warn(dev, "Cannot preserve devices behind bridges\n"); + if (dev->liveupdate.outgoing && !dev->liveupdate.outgoing->refcount) { + pci_WARN(dev, 1, "Preserved device with 0 refcount!\n"); return -EINVAL; } =20 - dev_ser =3D pci_flb_alloc_dev_ser(outgoing); - if (IS_ERR(dev_ser)) - return PTR_ERR(dev_ser); + if (!dev->liveupdate.outgoing) { + struct pci_dev_ser *dev_ser; =20 - pci_info(dev, "Device will be preserved across next Live Update\n"); - outgoing->ser->nr_devices++; - outgoing->ser->devices =3D kho_block_set_head_pa(&outgoing->block_set); + dev_ser =3D pci_flb_alloc_dev_ser(outgoing); + if (IS_ERR(dev_ser)) + return PTR_ERR(dev_ser); =20 - dev_ser->domain =3D pci_domain_nr(dev->bus); - dev_ser->bdf =3D pci_dev_id(dev); - dev_ser->refcount++; + pci_info(dev, "Device will be preserved across next Live Update\n"); + outgoing->ser->nr_devices++; + outgoing->ser->devices =3D kho_block_set_head_pa(&outgoing->block_set); + + dev_ser->domain =3D pci_domain_nr(dev->bus); + dev_ser->bdf =3D pci_dev_id(dev); + dev->liveupdate.outgoing =3D dev_ser; + } =20 - dev->liveupdate.outgoing =3D dev_ser; + dev->liveupdate.outgoing->refcount++; return 0; } =20 @@ -468,12 +508,16 @@ static int pci_liveupdate_preserve_device(struct pci_= flb_outgoing *outgoing, * pci_liveupdate_preserve() from their struct liveupdate_file_handler * preserve() callback to ensure the outgoing struct pci_ser is already se= t up. * + * pci_liveupdate_preserve() automatically preserves all bridges upstream = of + * @dev. + * * Returns: 0 on success, <0 on failure. */ int pci_liveupdate_preserve(struct pci_dev *dev) { struct pci_flb_outgoing *outgoing =3D NULL; - int ret; + struct pci_dev *start =3D dev; + int ret =3D -ENODEV; =20 guard(rwsem_write)(&pci_liveupdate.rwsem); =20 @@ -481,7 +525,13 @@ int pci_liveupdate_preserve(struct pci_dev *dev) if (IS_ERR(outgoing)) return PTR_ERR(outgoing); =20 - ret =3D pci_liveupdate_preserve_device(outgoing, dev); + for_each_pci_dev_in_path(dev) { + ret =3D pci_liveupdate_preserve_device(outgoing, dev); + if (ret) { + pci_liveupdate_unpreserve_path(outgoing, start, dev); + break; + } + } =20 pci_liveupdate_flb_put_outgoing(); return ret; @@ -497,6 +547,9 @@ EXPORT_SYMBOL_GPL(pci_liveupdate_preserve); * pci_liveupdate_unpreserve() from their struct liveupdate_file_handler * unpreserve() callback to ensure the outgoing struct pci_ser is already = set * up. + * + * pci_liveupdate_unpreserve() automatically unpreserves all bridges upstr= eam of + * @dev. */ void pci_liveupdate_unpreserve(struct pci_dev *dev) { @@ -510,7 +563,7 @@ void pci_liveupdate_unpreserve(struct pci_dev *dev) return; } =20 - pci_liveupdate_unpreserve_device(outgoing, dev); + pci_liveupdate_unpreserve_path(outgoing, dev, /*end=3D*/NULL); pci_liveupdate_flb_put_outgoing(); } EXPORT_SYMBOL_GPL(pci_liveupdate_unpreserve); @@ -600,28 +653,30 @@ void pci_liveupdate_cleanup_device(struct pci_dev *de= v) pci_WARN(dev, 1, "Destroying incoming-preserved device!\n"); } =20 -static void pci_liveupdate_finish_device(struct pci_ser *ser, struct pci_d= ev *dev) +static int pci_liveupdate_finish_device(struct pci_ser *ser, struct pci_de= v *dev) { if (!dev->liveupdate.incoming) { pci_warn(dev, "Cannot finish preserving an unpreserved device\n"); - return; + return -EINVAL; } =20 - if (dev->liveupdate.incoming->refcount !=3D 1) { - pci_WARN(dev, 1, "Preserved device has a corrupted refcount!\n"); - return; + if (!dev->liveupdate.incoming->refcount) { + pci_WARN(dev, 1, "Preserved device has a 0 refcount!\n"); + return -EINVAL; } =20 /* - * Drop the refcount so this device does not get treated as an incoming - * device again, e.g. in case pci_liveupdate_setup_device() gets called - * again because the device is hot-plugged. + * Decrement the refcount so this device does not get treated as an + * incoming device again, e.g. in case pci_liveupdate_setup_device() + * gets called again because the device is hot-plugged. */ - dev->liveupdate.incoming->refcount =3D 0; + if (--dev->liveupdate.incoming->refcount) + return 0; =20 pci_info(dev, "Device is finished participating in Live Update\n"); dev->liveupdate.incoming =3D NULL; ser->nr_devices--; + return 0; } =20 /** @@ -633,6 +688,8 @@ static void pci_liveupdate_finish_device(struct pci_ser= *ser, struct pci_dev *de * Update. Drivers must call pci_liveupdate_finish() from their struct * liveupdate_file_handler finish() callback to ensure the incoming struct * pci_ser is allocated. + * + * pci_liveupdate_finish() automatically finishes all bridges upstream of = @dev. */ void pci_liveupdate_finish(struct pci_dev *dev) { @@ -646,7 +703,11 @@ void pci_liveupdate_finish(struct pci_dev *dev) return; } =20 - pci_liveupdate_finish_device(incoming->ser, dev); + for_each_pci_dev_in_path(dev) { + if (pci_liveupdate_finish_device(incoming->ser, dev)) + break; + } + pci_liveupdate_flb_put_incoming(); } EXPORT_SYMBOL_GPL(pci_liveupdate_finish); diff --git a/include/linux/kho/abi/pci.h b/include/linux/kho/abi/pci.h index 4096e3cd3324..9485ed73c351 100644 --- a/include/linux/kho/abi/pci.h +++ b/include/linux/kho/abi/pci.h @@ -24,7 +24,7 @@ */ =20 #define PCI_LUO_FLB_COMPATIBLE "pci" -#define PCI_LUO_FLB_VERSION 1 +#define PCI_LUO_FLB_VERSION 2 =20 /** * struct pci_dev_ser - Serialized state about a single PCI device. @@ -33,7 +33,8 @@ * @bdf: The device's PCI bus, device, and function number. * @refcount: Reference count used by the PCI core to keep track of whethe= r it * is done using a device's struct pci_dev_ser. The value of the - * refcount is equal to 1 when the struct pci_dev_ser is in use= , and + * refcount is equal to the number of preserved devices at or b= elow + * it in the PCI hierarchy when the struct pci_dev_ser is in us= e, and * 0 otherwise. */ struct pci_dev_ser { diff --git a/include/linux/pci.h b/include/linux/pci.h index 76abe884e3dc..b35ac263c451 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -836,6 +836,9 @@ static inline struct pci_dev *pci_upstream_bridge(struc= t pci_dev *dev) return dev->bus->self; } =20 +#define for_each_pci_dev_in_path(dev) \ + for (; dev; dev =3D pci_upstream_bridge(dev)) + #ifdef CONFIG_PCI_MSI static inline bool pci_dev_msi_enabled(struct pci_dev *pci_dev) { --=20 2.55.0.1082.g2b9226bbc0-goog From nobody Thu Sep 24 23:04:13 2026 Received: from mail-pf1-f197.google.com (mail-pf1-f197.google.com [209.85.210.197]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DF52F525A90 for ; Fri, 18 Sep 2026 20:06:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.197 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789762022; cv=none; b=Bzcvt2DPptc9T/UpyE/9ikeN/bIGFR9iOOJqrN8h0cEeZUcH5K4dInL14WgUKN/jJlRVEVvdbIDnfucYCciLMDzdNUi8Lxr3v4AYSmsrcqDe3M2jbSesPJvIIlWt7cSmgQVZtWa9hiaf3KvuWenTStA1/7mKKr6OxqtzNNhIvbY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789762022; c=relaxed/simple; bh=NsqawONAa7UHfutIt0pI7Ufn0FuxrG8aUEi0ec1OE14=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=Nrny6vdFmlgYNUAWd14y03sAkdeqWg3MT0tveqjTwYeSA11mpxoocFEH9HY3wuUG0KqEZs1rs/Q6y26eaUxIFj2g9jH3E/CTQPE71pR3UkLgyRIAKgcRZo5gxqDp/ZYjfZqWUvsIgJaLLjXSPKMyKkDcJyEG8TE29otFrZMnt5U= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--dmatlack.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=KdFPOxly; arc=none smtp.client-ip=209.85.210.197 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--dmatlack.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="KdFPOxly" Received: by mail-pf1-f197.google.com with SMTP id d2e1a72fcca58-8627258ef12so2424724b3a.2 for ; Fri, 18 Sep 2026 13:06:59 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1789762019; x=1790366819; darn=vger.kernel.org; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:from:to:cc:subject:date:message-id:reply-to :content-type; bh=xo8UJHtlxlUvwILpRPy7TNgBxku4CDc7VxpXGrQJEKs=; b=KdFPOxlymdYE+I+McAvc1e3SaVh3Ih47S9lFz7Ejn7nbGnXbDAqDgiLXSkApzcdpEc clugsccS23y1gxKWuj/7Qb1myClcGu2pMZMqJ8Aoc7E+TSaKHRdXc94M+fIv3ChZKX2c b/0r/GEhPj0DNGXSm5OqipvU1vK9zCZBTLZMIQ77LO3YqW2mFliBT4FXBTPQa2+Wq9C6 ihMn+VXkpggSyU27bi7eOFw4L4Sa5bO+o5ZyA+Syfm0QNVLQ+Cs1X3MS4AAEKK3/409W 9L+uyA6o0utZPRZXD9LpEKKLzqs7hDswOv2har1VFA6Vnb2EDrkG14mLb4spqmMgZRW6 1xqA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1789762019; x=1790366819; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=xo8UJHtlxlUvwILpRPy7TNgBxku4CDc7VxpXGrQJEKs=; b=Vf07ksWz7rPpREKVNAnaksBwMFKdIgdMpm8N05nrsXlUS+BSt8CJjYfkhwX+UZVqCA YvKx9SZsJsOmi4GLR+rpRCuYiy2JzNcTDiDxgrmkc/bCO06sYGRQKOaWc5WHO7tCXog/ 0bQt3dy+UfU43emDr78lOJ1GqkFrJHhqbYYtfep1HadsRd9vcPG6TjAGTZBdqs3ni2MW QTj08FNnf6F11XIQGayz7HqvMmPzAmL6yqi1cL7hQE798rF98U4zVHLFRW+cXnEvMlfu FEtV5wPVoCJBQswzc1L4A6jyE6VjeccRjXYsYjJB4ierHZK2feSeYimjZsgFdD9l45D3 AYDw== X-Forwarded-Encrypted: i=1; AKwUvBxe4darZF3oVftSqH9jiwQBZX4oflI/QXrunihiIifdzXpzzETK3eOOteOJJp7iXzL1rsvWJzbfu9Oilfo=@vger.kernel.org X-Gm-Message-State: AFuF++lxMDaz3qRFq5tnsUKaAEMvD6nlqfsYMKse9eGXzt94S4AU3znm uto5MTsYitckIJx5jq6CANhemjuhbqjHxAxK4OPjS3gF+3paxm9ETANTzEZieBVI5ODAxnXEzwE mMPWOj32JM7yYDA== X-Received: from pgbck10.prod.google.com ([2002:a05:6a02:90a:b0:c92:460e:4f73]) (user=dmatlack job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6a21:329c:b0:3dd:a197:ede3 with SMTP id adf61e73a8af0-3dda197f83dmr1008455637.50.1789762018870; Fri, 18 Sep 2026 13:06:58 -0700 (PDT) Date: Fri, 18 Sep 2026 20:06:32 +0000 In-Reply-To: <20260918200640.887030-1-dmatlack@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260918200640.887030-1-dmatlack@google.com> X-Mailer: git-send-email 2.55.0.1082.g2b9226bbc0-goog Message-ID: <20260918200640.887030-7-dmatlack@google.com> Subject: [PATCH v9 06/13] PCI: liveupdate: Preserve bus numbers during Live Update From: David Matlack To: kexec@lists.infradead.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, linux-pci@vger.kernel.org Cc: Adithya Jayachandran , Alexander Graf , Alex Williamson , Bjorn Helgaas , Chris Li , David Matlack , David Rientjes , Jacob Pan , Jason Gunthorpe , Jonathan Corbet , Josh Hilke , Leon Romanovsky , Lukas Wunner , Mike Rapoport , Parav Pandit , Pasha Tatashin , Pranjal Shrivastava , Pratyush Yadav , Randy Dunlap , Saeed Mahameed , Samiullah Khawaja , Shuah Khan , Vipin Sharma , William Tu , Yi Liu Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Keep the secondary and subordinate bus numbers that the previous kernel programmed into bridges, rather than assigning new ones, if the previous kernel preserved any device across a Live Update. Do this even on architectures that would otherwise always assign bus numbers themselves, e.g. when pci=3Dassign-busses is passed. Preserved devices must be allowed to continue performing memory transactions across a Live Update, so the kernel cannot change the fabric topology. Changing the bus numbers of a bridge changes the RequesterIDs of the devices below it, which would require disabling and flushing any in-flight memory transactions first. Apply the policy globally rather than only to the paths that contain preserved devices. Bus numbers have to be preserved above a preserved device anyway, since an upstream bridge cannot expand its window. A global policy matches the scope of pcibios_assign_all_busses(), and gives an answer that cannot change part way through the two passes of a bridge scan. Bridges that do not have bus numbers are still assigned new ones, so hot-adding a bridge keeps working, both during and after a Live Update. The two-pass bridge scan guarantees such bridges are only assigned bus numbers above those already claimed by preserved bridges. The exception is a bridge that was preserved but comes up without a valid bus number configuration, e.g. because it was reset during kexec. Refuse to assign it new bus numbers, since that would silently change the BDF of every preserved device in its hierarchy. Also refuse to assign bus numbers to the other bridges on the same bus, since the bus numbers of the failed bridge can no longer be read from hardware and handing them out would let an unrelated device inherit the BDF of a preserved device. Require that CONFIG_CARDBUS is not enabled to enable CONFIG_PCI_LIVEUPDATE since preserving bus numbers on PCI-to-CardBus bridges requires additional work but is not a priority at the moment. Signed-off-by: David Matlack --- .../admin-guide/kernel-parameters.txt | 7 +- drivers/pci/Kconfig | 2 +- drivers/pci/liveupdate.c | 114 ++++++++++++++++++ drivers/pci/liveupdate.h | 13 ++ drivers/pci/probe.c | 11 +- include/linux/pci_liveupdate.h | 5 + 6 files changed, 146 insertions(+), 6 deletions(-) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentatio= n/admin-guide/kernel-parameters.txt index 68647ff4bdd2..7eabf6320abf 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -5170,7 +5170,12 @@ Kernel parameters explicitly which ones they are. assign-busses [X86] Always assign all PCI bus numbers ourselves, overriding - whatever the firmware may have done. + whatever the firmware may have done. Ignored + if any device was preserved across a Live + Update, where the kernel must preserve the + PCI topology (including bus numbers) to + avoid interrupting ongoing memory transactions + of preserved devices. usepirqmask [X86] Honor the possible IRQ mask stored in the BIOS $PIR table. This is needed on some systems with broken BIOSes, notably diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 8af20f558086..16fbd4212e0f 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -273,7 +273,7 @@ config VGA_ARB_MAX_GPUS =20 config PCI_LIVEUPDATE bool "PCI Live Update Support" - depends on PCI && LIVEUPDATE && 64BIT + depends on PCI && LIVEUPDATE && 64BIT && !CARDBUS help Enable PCI core support for preserving PCI devices across Live Update. This, in combination with support in a device's driver, diff --git a/drivers/pci/liveupdate.c b/drivers/pci/liveupdate.c index 825df024eec4..686887a6c8d9 100644 --- a/drivers/pci/liveupdate.c +++ b/drivers/pci/liveupdate.c @@ -148,6 +148,32 @@ * This enables the PCI core and any drivers bound to the bridge to partic= ipate * in the Live Update so that preserved endpoints can continue issuing mem= ory * transactions during the Live Update. + * + * BDF Stability + * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + * + * The PCI core guarantees that preserved devices can be identified by the= same + * bus, device, and function numbers for as long as they are preserved + * (including across kexec). To accomplish this, the PCI core keeps the + * secondary and subordinate bus numbers that the previous kernel programm= ed + * into bridges, if the previous kernel preserved any device. This is true= even + * on architectures that always assign new bus numbers during scanning. The + * kernel assumes the previous kernel established a sane bus topology acro= ss + * kexec. + * + * Bridges that do not have bus numbers are assigned new ones as usual, so + * hot-adding a bridge keeps working, both during and after a Live Update.= The + * two-pass bridge scan ensures such bridges are only assigned bus numbers= above + * those already claimed by preserved bridges. + * + * If a preserved bridge comes up without a valid bus number configuration= , e.g. + * because it was reset during kexec, the PCI core refuses to assign it ne= w bus + * numbers and does not enumerate anything below it. Assigning new bus num= bers + * would silently change the BDF of every preserved device in its hierarch= y. The + * PCI core also stops assigning bus numbers to the other bridges on the s= ame + * bus, since the bus numbers of the failed bridge can no longer be read f= rom + * hardware and handing them to another bridge would let an unrelated devi= ce + * inherit the BDF of a preserved device. */ =20 #define pr_fmt(fmt) "PCI: liveupdate: " fmt @@ -168,9 +194,13 @@ * struct pci_liveupdate_global - Global state for PCI Live Update support * @rwsem: Reader/writer semaphore used to protect the incoming and outgoi= ng * FLBs, and the references to them in struct pci_dev. + * @had_incoming: True if the previous kernel preserved at least one PCI d= evice. + * Set when the incoming FLB is retrieved and never cleared= , so + * it stays true after Live Update finishes. */ struct pci_liveupdate_global { struct rw_semaphore rwsem; + bool had_incoming; }; =20 static struct pci_liveupdate_global pci_liveupdate =3D { @@ -298,6 +328,14 @@ static int pci_flb_retrieve(struct liveupdate_flb_op_a= rgs *args) ret); } =20 + /* + * Remember that the previous kernel preserved devices for the lifetime + * of this kernel, even after Live Update finishes and the incoming FLB + * is freed. See pci_liveupdate_preserve_bus_numbers(). + */ + if (!xa_empty(&incoming->xa)) + pci_liveupdate.had_incoming =3D true; + args->obj =3D incoming; return 0; } @@ -606,6 +644,80 @@ static void pci_liveupdate_flb_put_incoming(void) liveupdate_flb_put_incoming(&pci_liveupdate_flb); } =20 +/** + * pci_liveupdate_preserve_bus_numbers() - Determine if the PCI core should + * preserve bus numbers when scann= ing + * bridges. + * + * This function is called by the PCI core when it is scanning a bridge. It + * determines whether the PCI core should preserve the secondary and subor= dinate + * bus numbers that the previous kernel programmed into that bridge, rathe= r than + * assigning new ones. This is necessary to keep RequesterIDs constant for + * preserved devices issuing memory transactions. + * + * Bus numbers are preserved everywhere, and for the lifetime of the kerne= l, if + * the previous kernel preserved any device. Bus numbers have to be preser= ved + * above a preserved device anyway, since an upstream bridge cannot expand= its + * window. Applying the same policy everywhere matches the scope of + * pcibios_assign_all_busses(), and gives an answer that cannot change par= t way + * through the two passes of a bridge scan. + * + * The incoming FLB is retrieved while setting up the first device, which = always + * happens before any bridge is scanned, so this returns the same answer f= or the + * entire enumeration. + * + * Note that this does not prevent the PCI core from assigning bus numbers= to + * bridges that do not have any, e.g. bridges that are hot-added after the + * Live Update. See pci_liveupdate_refuse_bus_numbers() for the one case w= here + * the PCI core must refuse to do so. + * + * Return: True if bus numbers should be preserved, false otherwise. + */ +bool pci_liveupdate_preserve_bus_numbers(void) +{ + return pci_liveupdate.had_incoming; +} + +/** + * pci_liveupdate_refuse_bus_numbers() - Determine if the PCI core must re= fuse + * to assign bus numbers to the prov= ided + * bridge. + * @bus: The PCI bus the bus numbers would be assigned from. + * @dev: The PCI bridge device the bus numbers would be assigned to. + * + * This function is called by the PCI core before it assigns bus numbers t= o a + * bridge that does not have any. + * + * A bridge that was preserved by the previous kernel but came up without a + * valid bus number configuration, e.g. because it was reset during kexec,= is + * left alone by the PCI core and therefore has no child bus once the firs= t pass + * of the bridge scan is done. + * + * The PCI core must not assign bus numbers from @bus while such a bridge = is on + * it, including to the failed bridge itself. Assigning new bus numbers to= the + * failed bridge would silently change the BDF of every preserved device i= n its + * hierarchy. Its bus numbers cannot be read from hardware anymore either,= so + * they cannot be excluded from assignment, and handing them to another br= idge + * would let an unrelated device inherit the BDF of a preserved device. + * + * Return: True if @dev must not be assigned bus numbers, false otherwise. + */ +bool pci_liveupdate_refuse_bus_numbers(struct pci_bus *bus, struct pci_dev= *dev) +{ + struct pci_dev *bridge; + + for_each_pci_bridge(bridge, bus) { + if (!bridge->liveupdate.was_incoming || bridge->subordinate) + continue; + + pci_err(dev, "Not assigning bus numbers, preserved bridge %s lost its bu= s number configuration\n", + pci_name(bridge)); + return true; + } + + return false; +} + void pci_liveupdate_setup_device(struct pci_dev *dev) { struct pci_flb_incoming *incoming; @@ -634,6 +746,8 @@ void pci_liveupdate_setup_device(struct pci_dev *dev) =20 pci_info(dev, "Device was preserved by previous kernel across Live Update= \n"); dev->liveupdate.incoming =3D dev_ser; + dev->liveupdate.was_incoming =3D true; + pci_liveupdate_flb_put_incoming(); } =20 diff --git a/drivers/pci/liveupdate.h b/drivers/pci/liveupdate.h index eaaa3559fd77..e5d2a19d2ca2 100644 --- a/drivers/pci/liveupdate.h +++ b/drivers/pci/liveupdate.h @@ -13,6 +13,8 @@ #ifdef CONFIG_PCI_LIVEUPDATE void pci_liveupdate_setup_device(struct pci_dev *dev); void pci_liveupdate_cleanup_device(struct pci_dev *dev); +bool pci_liveupdate_preserve_bus_numbers(void); +bool pci_liveupdate_refuse_bus_numbers(struct pci_bus *bus, struct pci_dev= *dev); #else static inline void pci_liveupdate_setup_device(struct pci_dev *dev) { @@ -21,6 +23,17 @@ static inline void pci_liveupdate_setup_device(struct pc= i_dev *dev) static inline void pci_liveupdate_cleanup_device(struct pci_dev *dev) { } + +static inline bool pci_liveupdate_preserve_bus_numbers(void) +{ + return false; +} + +static inline bool pci_liveupdate_refuse_bus_numbers(struct pci_bus *bus, + struct pci_dev *dev) +{ + return false; +} #endif =20 #endif /* DRIVERS_PCI_LIVEUPDATE_H */ diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index ad7fdf0d56b6..debe0ad1ef68 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1397,6 +1397,8 @@ static int pci_scan_bridge_extend(struct pci_bus *bus= , struct pci_dev *dev, int max, unsigned int available_buses, int pass) { + bool preserve_bus_numbers =3D !pcibios_assign_all_busses() || + pci_liveupdate_preserve_bus_numbers(); struct pci_bus *child; u32 buses; u16 bctl; @@ -1449,8 +1451,7 @@ static int pci_scan_bridge_extend(struct pci_bus *bus= , struct pci_dev *dev, goto out; } =20 - if ((secondary || subordinate) && - !pcibios_assign_all_busses() && !broken) { + if ((secondary || subordinate) && preserve_bus_numbers && !broken) { unsigned int cmax, buses; =20 /* @@ -1492,8 +1493,7 @@ static int pci_scan_bridge_extend(struct pci_bus *bus= , struct pci_dev *dev, * do in the second pass. */ if (!pass) { - if (pcibios_assign_all_busses() || broken) - + if (!preserve_bus_numbers || broken) /* * Temporarily disable forwarding of the * configuration cycles on all bridges in @@ -1507,6 +1507,9 @@ static int pci_scan_bridge_extend(struct pci_bus *bus= , struct pci_dev *dev, goto out; } =20 + if (pci_liveupdate_refuse_bus_numbers(bus, dev)) + goto out; + /* Clear errors */ pci_write_config_word(dev, PCI_STATUS, 0xffff); =20 diff --git a/include/linux/pci_liveupdate.h b/include/linux/pci_liveupdate.h index 710026ada2d5..d45a5b524909 100644 --- a/include/linux/pci_liveupdate.h +++ b/include/linux/pci_liveupdate.h @@ -17,10 +17,15 @@ * struct pci_liveupdate - PCI Live Update state for a struct pci_dev * @outgoing: State preserved for the next kernel. * @incoming: State preserved by the previous kernel. + * @was_incoming: True if this struct pci_dev was incoming-preserved when = it was + * set up, i.e. it was matched to state preserved by the pr= evious + * kernel. Unlike @incoming, this is never cleared, so it s= tays + * true after the device finishes participating in Live Upd= ate. */ struct pci_liveupdate { struct pci_dev_ser *outgoing; struct pci_dev_ser *incoming; + bool was_incoming; }; =20 struct pci_dev; --=20 2.55.0.1082.g2b9226bbc0-goog From nobody Thu Sep 24 23:04:13 2026 Received: from mail-pl1-f198.google.com (mail-pl1-f198.google.com [209.85.214.198]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DABB4525A9F for ; Fri, 18 Sep 2026 20:07:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.198 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789762024; cv=none; b=TH1DinRz3JU+2/rVshtxsEtNTUT5UoybHQ46XiyWqqC/jlGa8LBiiRFx6OBQnd2JJRYN+spSSHAX3EWhWJRefRGG1xyJbK6Ef0IFqHlziHZ8RGIP+tN3VaXZ88yf0FM9s2OgGUuaH3GgPUzGPOYFj5nmfrkcfYhMfrtiuEA2O/c= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789762024; c=relaxed/simple; bh=ZD3KS7DBDYWBFB8hwoUBxQdCUMdHz+nSMWic4WvQWYM=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=YNl6GjFJ9Da3D8NrtciC06Sq5o3qu0hlJPj6H5nses+CcROBDKwZfzSQGX2f6WBXN37Y7s9QiEuFce+wrxp0OJaxZ7X4IPeOfvpvaVFwHI1VqYY4qPt3AW/58Ep+n/IA+Sg9lvMm7z8O/490KgSDpr5UQn98+blTEipNWyyQ7oI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--dmatlack.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=QfXpPuns; arc=none smtp.client-ip=209.85.214.198 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--dmatlack.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="QfXpPuns" Received: by mail-pl1-f198.google.com with SMTP id d9443c01a7336-2ce7dfd33ffso14183885ad.0 for ; Fri, 18 Sep 2026 13:07:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1789762020; x=1790366820; darn=vger.kernel.org; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:from:to:cc:subject:date:message-id:reply-to :content-type; bh=NNhCAqWpdqP/8unoyYkwhtURYEVhgOr7+m0jRabjXBA=; b=QfXpPuns7rVxUs9VwLxWw7dyYHSvyA4jEmRwe2O0qTylnn8K5rP/k8KuGUXfFp0U88 Q+Rfym1UjXfnRN1BsQA+t9MbYLj/K2Y9GSw8g/EPz1Kq4NkMb97aBK5NHaRPIcB5pbQo lTCkTsVCBVcBYNX9wRxpZpKc2avO+YWN0ZrEDL8HLXoXUR/DGp0OLD2QdU8QEO6EE+Gt 1ApyeNV1AS1nUWvUfPNdGJVsfEwqcfnpJ/dvFX/lpPl9Cwu0nqmyTf4+wd3U9yhJkSVu GKqYcLdN4Js/X42r3dR/sYBhmVqN7JPVvbb8FYmsrKaCF+m/aBQYB6bGijt9+RscYzj2 tkXg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1789762020; x=1790366820; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=NNhCAqWpdqP/8unoyYkwhtURYEVhgOr7+m0jRabjXBA=; b=bywpBXl7GFDn5IMG/LNfkfgdeO+2tnweRV7RiwjdfkWoXKuQtdKmPjm7aYc/9JFR0X WSn3EZzSaJQlQK6WYPa5HsaBaS4QsYYOI1LQq0M2TcTB0zH8yEaatCerTFZEs50kd7je YJOqrh2K89EDmVwgzeFtIVvxr3wmMRPmqnZfaaX1OB1nLK3y981m7JDICWjaP+6ZubJt PUOXvpEBHcijZ444nh1dQSbM3F06Vc8y8E+XStRjPPsOkmzr34jIFRz5mBHn5ox3mI4K Fm5COhgJDY0JUoZBRm8wTVZZIqyvJPcBFC5K/usniD8SyIj5T3pZDTdrKKdPO+tmQ+KU zsOg== X-Forwarded-Encrypted: i=1; AKwUvBxnkPGCreVIOYYBRIXIhnN5oZEh1+3RdwipAYCRA76Ly/0xpJ1Oc9c6qSjhh3MxvKVU+G/UbNrUxxI8zyY=@vger.kernel.org X-Gm-Message-State: AFuF++le2qBH31kDRSgqrOxmf2CY9mZMTMwcySJnWDhE4E1nYZRkgStD Fg02bxwVA8Z7ssNOCArNuX86bDXdQ7Sk4rI3h3UjeF/62Mvt2sfmGyfm2cLDL6QrBgHu7hX8PRa BcyCT/GnVnrWikg== X-Received: from plec5.prod.google.com ([2002:a17:902:f305:b0:2dd:2f59:daa8]) (user=dmatlack job=prod-delivery.src-stubby-dispatcher) by 2002:a17:903:9cf:b0:2dd:c053:e662 with SMTP id d9443c01a7336-2ddc053e6aemr12608265ad.40.1789762019617; Fri, 18 Sep 2026 13:06:59 -0700 (PDT) Date: Fri, 18 Sep 2026 20:06:33 +0000 In-Reply-To: <20260918200640.887030-1-dmatlack@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260918200640.887030-1-dmatlack@google.com> X-Mailer: git-send-email 2.55.0.1082.g2b9226bbc0-goog Message-ID: <20260918200640.887030-8-dmatlack@google.com> Subject: [PATCH v9 07/13] PCI: Refactor matching logic for pci_dev_acs_ops From: David Matlack To: kexec@lists.infradead.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, linux-pci@vger.kernel.org Cc: Adithya Jayachandran , Alexander Graf , Alex Williamson , Bjorn Helgaas , Chris Li , David Matlack , David Rientjes , Jacob Pan , Jason Gunthorpe , Jonathan Corbet , Josh Hilke , Leon Romanovsky , Lukas Wunner , Mike Rapoport , Parav Pandit , Pasha Tatashin , Pranjal Shrivastava , Pratyush Yadav , Randy Dunlap , Saeed Mahameed , Samiullah Khawaja , Shuah Khan , Vipin Sharma , William Tu , Yi Liu Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Refactor the logic to match devices to pci_dev_acs_ops by factoring out the device matching loop into its own routine, pci_dev_acs_ops_get(). This eliminates duplicate code between pci_dev_specific_enable_acs() and pci_dev_specific_disable_acs_redir(), and will also be used in a subsequent commit to check if a device requires device-specific enable_acs() during a Live Update. No functional change intended. Reviewed-by: Pranjal Shrivastava Reviewed-by: Pasha Tatashin Reviewed-by: Bjorn Helgaas Signed-off-by: David Matlack --- drivers/pci/quirks.c | 51 ++++++++++++++++++-------------------------- 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index de9bbccda21f..7aee30734303 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -5377,9 +5377,6 @@ static void pci_quirk_enable_intel_rp_mpc_acs(struct = pci_dev *dev) */ static int pci_quirk_enable_intel_pch_acs(struct pci_dev *dev) { - if (!pci_quirk_intel_pch_acs_match(dev)) - return -ENOTTY; - if (pci_quirk_enable_intel_lpc_acs(dev)) { pci_warn(dev, "Failed to enable Intel PCH ACS quirk\n"); return 0; @@ -5399,9 +5396,6 @@ static int pci_quirk_enable_intel_spt_pch_acs(struct = pci_dev *dev) int pos; u32 cap, ctrl; =20 - if (!pci_quirk_intel_spt_pch_acs_match(dev)) - return -ENOTTY; - pos =3D dev->acs_cap; if (!pos) return -ENOTTY; @@ -5429,9 +5423,6 @@ static int pci_quirk_disable_intel_spt_pch_acs_redir(= struct pci_dev *dev) int pos; u32 cap, ctrl; =20 - if (!pci_quirk_intel_spt_pch_acs_match(dev)) - return -ENOTTY; - pos =3D dev->acs_cap; if (!pos) return -ENOTTY; @@ -5451,56 +5442,56 @@ static int pci_quirk_disable_intel_spt_pch_acs_redi= r(struct pci_dev *dev) static const struct pci_dev_acs_ops { u16 vendor; u16 device; + bool (*match)(struct pci_dev *dev); int (*enable_acs)(struct pci_dev *dev); int (*disable_acs_redir)(struct pci_dev *dev); } pci_dev_acs_ops[] =3D { { PCI_VENDOR_ID_INTEL, PCI_ANY_ID, + .match =3D pci_quirk_intel_pch_acs_match, .enable_acs =3D pci_quirk_enable_intel_pch_acs, }, { PCI_VENDOR_ID_INTEL, PCI_ANY_ID, + .match =3D pci_quirk_intel_spt_pch_acs_match, .enable_acs =3D pci_quirk_enable_intel_spt_pch_acs, .disable_acs_redir =3D pci_quirk_disable_intel_spt_pch_acs_redir, }, }; =20 -int pci_dev_specific_enable_acs(struct pci_dev *dev) +static const struct pci_dev_acs_ops *pci_dev_acs_ops_get(struct pci_dev *d= ev) { const struct pci_dev_acs_ops *p; - int i, ret; + int i; =20 for (i =3D 0; i < ARRAY_SIZE(pci_dev_acs_ops); i++) { p =3D &pci_dev_acs_ops[i]; if ((p->vendor =3D=3D dev->vendor || p->vendor =3D=3D (u16)PCI_ANY_ID) && (p->device =3D=3D dev->device || - p->device =3D=3D (u16)PCI_ANY_ID) && - p->enable_acs) { - ret =3D p->enable_acs(dev); - if (ret >=3D 0) - return ret; + p->device =3D=3D (u16)PCI_ANY_ID)) { + if (!p->match || p->match(dev)) + return p; } } =20 + return NULL; +} + +int pci_dev_specific_enable_acs(struct pci_dev *dev) +{ + const struct pci_dev_acs_ops *p =3D pci_dev_acs_ops_get(dev); + + if (p && p->enable_acs) + return p->enable_acs(dev); + return -ENOTTY; } =20 int pci_dev_specific_disable_acs_redir(struct pci_dev *dev) { - const struct pci_dev_acs_ops *p; - int i, ret; + const struct pci_dev_acs_ops *p =3D pci_dev_acs_ops_get(dev); =20 - for (i =3D 0; i < ARRAY_SIZE(pci_dev_acs_ops); i++) { - p =3D &pci_dev_acs_ops[i]; - if ((p->vendor =3D=3D dev->vendor || - p->vendor =3D=3D (u16)PCI_ANY_ID) && - (p->device =3D=3D dev->device || - p->device =3D=3D (u16)PCI_ANY_ID) && - p->disable_acs_redir) { - ret =3D p->disable_acs_redir(dev); - if (ret >=3D 0) - return ret; - } - } + if (p && p->disable_acs_redir) + return p->disable_acs_redir(dev); =20 return -ENOTTY; } --=20 2.55.0.1082.g2b9226bbc0-goog From nobody Thu Sep 24 23:04:13 2026 Received: from mail-pl1-f200.google.com (mail-pl1-f200.google.com [209.85.214.200]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 45C09527586 for ; Fri, 18 Sep 2026 20:07:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.200 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789762025; cv=none; b=dEaK1ILnN3ae9RPlTgfa3TCtEHXAfWrIoZFXx/2N6R360vFUExsXHikLO42qb079u/pcWqgj5RY6tM0HPoriEC2M2r183UHJxN7+j10Xcn1rMj1qwB9n+xWmF4USZbXPJJT4j6ahU/Y2fhCSaGqzWLLbyzdzra+mLChtm81exKk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789762025; c=relaxed/simple; bh=nrGrLrEDt4kOjCaEFRcXdThoDJLk6Rvpwa3mIPITmHk=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=Wi0QIrKGUwZ/SePMc0OiWnhyWK5yS2YFg1YE5Q+HymQSTuQcdeQpufs6TjyS4Fe0m/VxU6Jz093BvcYZ4+qRxBB6ptwZnzqC4WQbjPukjDhm4UnF1J547kPO0gVOQS6T7i3N7vV+F3iURA2q23TsMagAsZOpDgtBhn+3Fr/OVSw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--dmatlack.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=tuz3zl4q; arc=none smtp.client-ip=209.85.214.200 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--dmatlack.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="tuz3zl4q" Received: by mail-pl1-f200.google.com with SMTP id d9443c01a7336-2d94a158dc8so20610005ad.2 for ; Fri, 18 Sep 2026 13:07:01 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1789762021; x=1790366821; darn=vger.kernel.org; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:from:to:cc:subject:date:message-id:reply-to :content-type; bh=8ZYph3SzF8DNc3MuGEDpVyyWuyM4ge/DoPLpl5bxHLs=; b=tuz3zl4qdhd1D08FiXYd038W04/jggNwNzpbkx4FgLJPk8G/6dUnAAvYrkGBEms7IA XG3/aumOzCGDeqV2J23HKmvrPJ91ik6a9xTgIWPM3H+b5u0I2vIt0DmfULkqmPO4Lxr2 HKheEbpYcUyyJaHLIEK7PFnlf14vL/EBFkq6X/7aAlNC6quAvbsgva7LX+de3ceRCwRb +si/UHZZ9/eLKa2CYs84lfLYbT3BmDK58lb3Z7SgeL5Ae9emmgPBMAwo23S83TZ4ohP4 58Qk5bGkZQd+SMwKdsPL5liXX4TSm3AzVMOqNOdA5UxzJCHsD9gYn3FJ6jLfJjYLZ1Pn hHlw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1789762021; x=1790366821; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=8ZYph3SzF8DNc3MuGEDpVyyWuyM4ge/DoPLpl5bxHLs=; b=tB+dpBaSFl6DukdJPoyQxLILhJay4HdpqbMgNRHfYlnRc1wAQt4sbo1U8Eq4JJCIex 1qfcu05JHAv9R88IMnkolfg6lm0ewjChN3Fgu88AD0RjsJ+jjIBMTIqpLa+G6AFyhywp yp1HIqYWCDmkxrXjDvBn9Le30NfIP62BvHHX7NL2+A8yXfNJQfSlhUSFX84Dnb/va0s0 Fa/tT9S/eGvKVCl9EUPY8vHge55jy+Xtq6RdpPiu0sXjg6eUzSA0uxpS7egp51mKFTMe tn26+G3qwrv9OaR3LhJJSLAjg0Tju/NXnrZVRGlM1bTJ9dfcSZqhlGffB8AH82S6raz/ 21gA== X-Forwarded-Encrypted: i=1; AKwUvBwsOToCjJvBciLVucYlWeG3RH1aHeHd9LzT5u3SfOclKI3AlOUHcSMfTXmHsLtzKakMHIG3MCcRB80abxQ=@vger.kernel.org X-Gm-Message-State: AFuF++lN4EA3718mi6QNQWbM6KLXVq26CfEW7j8uOZLMCVzJr3srVdg/ 0WoeYR3j7LqKJ5Wmyl+KDSNmQf3iMHYAFCoa3eMe9poo53Vx/Fd8IOCQ1PNJ51btR7OiCe89ExW uL+YIsPbsjGe/UA== X-Received: from plld5.prod.google.com ([2002:a17:902:7285:b0:2dd:4e69:703b]) (user=dmatlack job=prod-delivery.src-stubby-dispatcher) by 2002:a17:903:2ecb:b0:2dd:c100:9439 with SMTP id d9443c01a7336-2ddc1009c48mr9484545ad.55.1789762020378; Fri, 18 Sep 2026 13:07:00 -0700 (PDT) Date: Fri, 18 Sep 2026 20:06:34 +0000 In-Reply-To: <20260918200640.887030-1-dmatlack@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260918200640.887030-1-dmatlack@google.com> X-Mailer: git-send-email 2.55.0.1082.g2b9226bbc0-goog Message-ID: <20260918200640.887030-9-dmatlack@google.com> Subject: [PATCH v9 08/13] PCI: Save and restore the ACS Control register From: David Matlack To: kexec@lists.infradead.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, linux-pci@vger.kernel.org Cc: Adithya Jayachandran , Alexander Graf , Alex Williamson , Bjorn Helgaas , Chris Li , David Matlack , David Rientjes , Jacob Pan , Jason Gunthorpe , Jonathan Corbet , Josh Hilke , Leon Romanovsky , Lukas Wunner , Mike Rapoport , Parav Pandit , Pasha Tatashin , Pranjal Shrivastava , Pratyush Yadav , Randy Dunlap , Saeed Mahameed , Samiullah Khawaja , Shuah Khan , Vipin Sharma , William Tu , Yi Liu Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Save the ACS Control register in pci_save_state() and write it back in pci_restore_state(), instead of recomputing the ACS controls from scratch with pci_enable_acs(). This makes ACS symmetric with the rest of a device's saved state. Today pci_save_state() ignores ACS entirely and pci_restore_state() re-enables the ACS controls from the kernel's current ACS policy. As a result, a device can come out of a reset with different ACS controls than it went in with, e.g. any controls programmed outside of pci_enable_acs() are silently dropped. pci_enable_acs() runs when a driver binds to a device (pci_dma_configure()), i.e. after pci_bus_add_device() has already saved the device's state. Refresh the saved ACS Control register there as well, otherwise a subsequent reset would revert ACS back to the configuration left behind by firmware. Devices that rely on device-specific quirks to enable an ACS equivalent keep that configuration outside of the ACS Control register, so keep configuring ACS from scratch for them. Do the same for devices that have no saved ACS state at all. Reviewed-by: Bjorn Helgaas Assisted-by: Claude:claude-opus-5 Signed-off-by: David Matlack --- drivers/pci/pci.c | 66 +++++++++++++++++++++++++++++++++++++++++++- drivers/pci/pci.h | 5 ++++ drivers/pci/quirks.c | 7 +++++ 3 files changed, 77 insertions(+), 1 deletion(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index b2879a6be5f8..dd25c01736b4 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1021,6 +1021,55 @@ static void pci_std_enable_acs(struct pci_dev *dev, = struct pci_acs *caps) caps->ctrl |=3D (dev->acs_capabilities & PCI_ACS_TB); } =20 +/** + * pci_save_acs_state - save the ACS Control register + * @dev: the PCI device + * + * Record the ACS controls currently programmed in hardware so that + * pci_restore_acs_state() can reapply them after a reset. + */ +static void pci_save_acs_state(struct pci_dev *dev) +{ + struct pci_cap_saved_state *save_state; + + if (!dev->acs_cap) + return; + + save_state =3D pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_ACS); + if (!save_state) + return; + + pci_read_config_word(dev, dev->acs_cap + PCI_ACS_CTRL, + (u16 *)&save_state->cap.data[0]); +} + +/** + * pci_restore_acs_state - restore the ACS Control register + * @dev: the PCI device + */ +static void pci_restore_acs_state(struct pci_dev *dev) +{ + struct pci_cap_saved_state *save_state =3D NULL; + + if (dev->acs_cap && !pci_need_dev_specific_enable_acs(dev)) + save_state =3D pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_ACS); + + /* + * Devices that rely on device-specific quirks to enable an ACS + * equivalent keep that configuration outside of the ACS Control + * register, so there is nothing useful to restore for them. Configure + * ACS from scratch instead, which also covers devices that have no + * saved ACS state at all. + */ + if (!save_state) { + pci_enable_acs(dev); + return; + } + + pci_write_config_word(dev, dev->acs_cap + PCI_ACS_CTRL, + *(u16 *)&save_state->cap.data[0]); +} + /** * pci_enable_acs - enable ACS if hardware support it * @dev: the PCI device @@ -1057,6 +1106,15 @@ void pci_enable_acs(struct pci_dev *dev) __pci_config_acs(dev, &caps, config_acs_param, 0, 0); =20 pci_write_config_word(dev, pos + PCI_ACS_CTRL, caps.ctrl); + + /* + * pci_enable_acs() runs when a driver binds to the device, i.e. after + * pci_bus_add_device() has already saved the device's state. Refresh + * the saved ACS Control register so that a subsequent reset restores + * the controls programmed here rather than the ones left behind by + * firmware. + */ + pci_save_acs_state(dev); } =20 /** @@ -1800,6 +1858,7 @@ int pci_save_state(struct pci_dev *dev) pci_save_aer_state(dev); pci_save_ptm_state(dev); pci_save_tph_state(dev); + pci_save_acs_state(dev); return pci_save_vc_state(dev); } EXPORT_SYMBOL(pci_save_state); @@ -1877,7 +1936,7 @@ void pci_restore_state(struct pci_dev *dev) pci_restore_msi_state(dev); =20 /* Restore ACS and IOV configuration state */ - pci_enable_acs(dev); + pci_restore_acs_state(dev); pci_restore_iov_state(dev); =20 dev->state_saved =3D false; @@ -3532,6 +3591,11 @@ void pci_allocate_cap_save_buffers(struct pci_dev *d= ev) if (error) pci_err(dev, "unable to allocate suspend buffer for LTR\n"); =20 + error =3D pci_add_ext_cap_save_buffer(dev, PCI_EXT_CAP_ID_ACS, + sizeof(u16)); + if (error) + pci_err(dev, "unable to allocate suspend buffer for ACS\n"); + pci_allocate_vc_save_buffers(dev); } =20 diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index ba3c3fddddc2..037c1674f164 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -1095,6 +1095,7 @@ void pci_acs_init(struct pci_dev *dev); void pci_enable_acs(struct pci_dev *dev); #ifdef CONFIG_PCI_QUIRKS int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags); +bool pci_need_dev_specific_enable_acs(struct pci_dev *dev); int pci_dev_specific_enable_acs(struct pci_dev *dev); int pci_dev_specific_disable_acs_redir(struct pci_dev *dev); void pci_disable_broken_acs_cap(struct pci_dev *pdev); @@ -1105,6 +1106,10 @@ static inline int pci_dev_specific_acs_enabled(struc= t pci_dev *dev, { return -ENOTTY; } +static inline bool pci_need_dev_specific_enable_acs(struct pci_dev *dev) +{ + return false; +} static inline int pci_dev_specific_enable_acs(struct pci_dev *dev) { return -ENOTTY; diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 7aee30734303..e500c202d2ec 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -5476,6 +5476,13 @@ static const struct pci_dev_acs_ops *pci_dev_acs_ops= _get(struct pci_dev *dev) return NULL; } =20 +bool pci_need_dev_specific_enable_acs(struct pci_dev *dev) +{ + const struct pci_dev_acs_ops *p =3D pci_dev_acs_ops_get(dev); + + return p && p->enable_acs; +} + int pci_dev_specific_enable_acs(struct pci_dev *dev) { const struct pci_dev_acs_ops *p =3D pci_dev_acs_ops_get(dev); --=20 2.55.0.1082.g2b9226bbc0-goog From nobody Thu Sep 24 23:04:13 2026 Received: from mail-pf1-f199.google.com (mail-pf1-f199.google.com [209.85.210.199]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 46E5A527587 for ; Fri, 18 Sep 2026 20:07:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.199 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789762025; cv=none; b=GAThhd9Q02ObdMxe5kBAuQo05P2b1SquEbiHSfu5WTPbchDwMxtuYowOD1a/suvFo+viklkQ0WDlKKrUct6IUi6yfZH3EyGSA/tKU2AEJCerPoJXyBHoNZLbcdSaD37ZxHLcKF1jZlfWIJT6UfT2opJNdFFA03ZtyYWxaTWtksk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789762025; c=relaxed/simple; bh=R4Az/uRUuF1PiT5xSJs/E4kzyzqFdBSW3WJkEp2Js04=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=X/Gh9JyqLPSW61iaja9XVeZkA9TfxqRwPv1fu/xskONowTAnn132FR4ExOkb9W0dwC8Kt2+0szjFgYngDxABesm4SG55EVRXCXZRBp4s8twZN67ZhJ9CiqCWa5B5ktakDCKOZchilQaNcpOuNxOPaDIbye2bEFjjt/sJbGpshOo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--dmatlack.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=RzSGmqLp; arc=none smtp.client-ip=209.85.210.199 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--dmatlack.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="RzSGmqLp" Received: by mail-pf1-f199.google.com with SMTP id d2e1a72fcca58-86a43fc3527so2920077b3a.1 for ; Fri, 18 Sep 2026 13:07:01 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1789762021; x=1790366821; darn=vger.kernel.org; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:from:to:cc:subject:date:message-id:reply-to :content-type; bh=9ZcqrsSF0ioZzw9gFyyhQYx/Js3WyQJlrd8b2TpGaYs=; b=RzSGmqLpCtG5g6LeEX9pvglnVRXlzVQoqbaVXexeJ0VU7mhlxWnvLnEaPI70ioWj2Y DTMS86vDme7ulFFMBBMb6GcHg0AU0W49Aiob5b69y5nUab7hxhBbM1i/kXNVvribigCP H8rGoUJeMpCucAXzzhWjVIfgKi0/aOqjafCguw/pBxFoMR8nqAP8A3/USbeAHTpARAs9 JjyHmR+It1jsNPWLrDcs5TXu9X5FkR1xkhc877Rgm+xRiYop3bDm+uyyLj84wFdX5751 jN5YnaSg9ZCc3fp14K1bu8sFkQMCSzmYaXMOAyGJMmKzKXZc/XseQK4RI4prCw0Azarl U2Kw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1789762021; x=1790366821; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=9ZcqrsSF0ioZzw9gFyyhQYx/Js3WyQJlrd8b2TpGaYs=; b=M0/f2I8OS6IF0lIPmVV+IUasEkbxvM+3Bb3TccRuevbHJkkFvCzgnaix1hnB3ShpcY fGddCmrr0tHsZzoO93ZYhFrlD8OljFcTwxLxmzIiHnrSfeEU1w6Xi2lLBE8quRX84CsJ 1o2UNJ2v7UbYohZcoshlD4XMiz2ddc/y2Sq6SLRHCswMJdfDT97iq+pLQFU5NyuJIRi4 RMTee50bwrghQT2oEvSFt1GVD6uHbDO7TV77finL6gAVoOui9k2hQRkoirxwQlwrMo95 Ej6BS3N3DnSChvie0mjsy4fp2MADa1DDx6eB0fMSoifLa6w4c05N8HjOJyViIniAE7Uy J1SA== X-Forwarded-Encrypted: i=1; AKwUvByyy56p3xzQfeN3NVJLRTFaPyLeTXGfHah1q7Or7rgOzIH3ZUuYAHntWdiwHF6MFKhm5j729PpVkLBCamY=@vger.kernel.org X-Gm-Message-State: AFuF++nQMK24Ux75Je8OzJ8nfRHBq/dDVGXXKgTQptgnxEtLzzfvCzah Hg4j7Yx2PYzhs1hhF4JfKxCfsYBv41XOflVEn5/iKcUjms+qCL+q02B5Pn230jurPewWunAqaOg K//ezyg1jEmJWWQ== X-Received: from pgvn14.prod.google.com ([2002:a65:63ce:0:b0:cc1:bda2:d0a3]) (user=dmatlack job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6a21:7017:b0:3c8:e304:99d0 with SMTP id adf61e73a8af0-3dd721125e9mr11870192637.0.1789762021113; Fri, 18 Sep 2026 13:07:01 -0700 (PDT) Date: Fri, 18 Sep 2026 20:06:35 +0000 In-Reply-To: <20260918200640.887030-1-dmatlack@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260918200640.887030-1-dmatlack@google.com> X-Mailer: git-send-email 2.55.0.1082.g2b9226bbc0-goog Message-ID: <20260918200640.887030-10-dmatlack@google.com> Subject: [PATCH v9 09/13] PCI: liveupdate: Adopt ACS controls in incoming preserved devices From: David Matlack To: kexec@lists.infradead.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, linux-pci@vger.kernel.org Cc: Adithya Jayachandran , Alexander Graf , Alex Williamson , Bjorn Helgaas , Chris Li , David Matlack , David Rientjes , Jacob Pan , Jason Gunthorpe , Jonathan Corbet , Josh Hilke , Leon Romanovsky , Lukas Wunner , Mike Rapoport , Parav Pandit , Pasha Tatashin , Pranjal Shrivastava , Pratyush Yadav , Randy Dunlap , Saeed Mahameed , Samiullah Khawaja , Shuah Khan , Vipin Sharma , William Tu , Yi Liu Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Adopt Access Control Services (ACS) controls on all incoming preserved devices (endpoints and upstream bridges) during a Live Update. Inheriting ACS flags avoids changing routing rules while memory transactions are in flight from preserved devices. This is also strictly necessary to ensure that IOMMU group assignments do not change during or after Live Update. The adopted controls are recorded by the pci_save_state() call in pci_bus_add_device(), which runs before drivers bind, so they are automatically reapplied by pci_restore_state() if the device is reset. If that save buffer could not be allocated there is nowhere to record the adopted controls, and they would be silently lost by the first reset. Program ACS from scratch in that case, which is a better outcome than leaving ACS disabled. To simplify ACS inheritance, reject preserving any devices that require quirks to enable ACS as those quirks would also have to take Live Update into account. Signed-off-by: David Matlack --- drivers/pci/liveupdate.c | 78 ++++++++++++++++++++++++++++++++++++++++ drivers/pci/liveupdate.h | 6 ++++ drivers/pci/pci.c | 5 +++ 3 files changed, 89 insertions(+) diff --git a/drivers/pci/liveupdate.c b/drivers/pci/liveupdate.c index 686887a6c8d9..0145399b3834 100644 --- a/drivers/pci/liveupdate.c +++ b/drivers/pci/liveupdate.c @@ -123,6 +123,9 @@ * * * The device cannot be a Virtual Function (VF). * + * * The device cannot require device-specific quirks to enable Access + * Control Services (ACS). + * * Driver Binding * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D * @@ -174,6 +177,18 @@ * bus, since the bus numbers of the failed bridge can no longer be read f= rom * hardware and handing them to another bridge would let an unrelated devi= ce * inherit the BDF of a preserved device. + * + * Handling Preserved Devices + * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D + * + * The PCI core treats preserved devices differently than non-preserved de= vices. + * This section enumerates those differences. + * + * * The PCI core adopts all ACS controls enabled on incoming preserved d= evices + * rather than assigning new ones. This ensures that TLPs are routed th= e same + * way after Live Update and ensures that IOMMU groups do not change. N= ote + * that a device will use its adopted ACS controls for the lifetime of = its + * struct pci_dev (i.e. even after pci_liveupdate_finish()). */ =20 #define pr_fmt(fmt) "PCI: liveupdate: " fmt @@ -189,6 +204,7 @@ #include =20 #include "liveupdate.h" +#include "pci.h" =20 /** * struct pci_liveupdate_global - Global state for PCI Live Update support @@ -502,6 +518,16 @@ static int pci_liveupdate_preserve_device(struct pci_f= lb_outgoing *outgoing, return -EINVAL; } =20 + /* + * Do not preserve devices that rely on device-specific ACS equivalents + * (for now) since that would complicate keeping ACS constant across + * Live Update. + */ + if (pci_need_dev_specific_enable_acs(dev)) { + pci_warn(dev, "Refusing to preserve device that relies on ACS quirks\n"); + return -EINVAL; + } + /* * Endpoint devices should not be preserved more than once. * Bridges are preserved once for every downstream device that @@ -826,6 +852,58 @@ void pci_liveupdate_finish(struct pci_dev *dev) } EXPORT_SYMBOL_GPL(pci_liveupdate_finish); =20 +/** + * pci_liveupdate_adopt_acs() - Adopt ACS controls + * @dev: The PCI device to adopt ACS controls for + * + * For devices preserved across a Live Update, leave the ACS controls + * established by the previous kernel alone instead of programming new one= s. + * The adopted controls are recorded by the pci_save_state() call in + * pci_bus_add_device(), so they are reapplied by pci_restore_state() if t= he + * device is subsequently reset. + * + * Return: 0 on success, or -EINVAL if the device was not preserved, requi= res + * device-specific quirks, or has nowhere to record the adopted controls. + */ +int pci_liveupdate_adopt_acs(struct pci_dev *dev) +{ + /* + * Check if the device was preserved over a previous Live Update (even + * if it has already gone through pci_liveupdate_finish()). This ensures + * that the device continues to use the ACS controls established by the + * previous kernel. + */ + if (!dev->liveupdate.was_incoming) + return -EINVAL; + + /* + * The previous kernel should not have preserved any devices that + * require device-specific quirks to enable ACS, but if such a device is + * detected (e.g. new device-specific ACS quirk in the current kernel), + * log a big warning and fall back to the normal enable ACS path. + */ + if (pci_need_dev_specific_enable_acs(dev)) { + pci_warn(dev, "Device-specific quirk required to enable ACS!\n"); + WARN_ON_ONCE(true); + return -EINVAL; + } + + /* + * Adopting the previous kernel's controls depends on them being + * captured in the ACS save buffer, so that they are reapplied if the + * device is later reset. Without that buffer, e.g. because it could + * not be allocated under memory pressure, the adopted controls would + * be silently lost by the first reset. Program ACS from scratch + * instead, which is a better outcome than leaving ACS disabled. + */ + if (dev->acs_cap && !pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_ACS)) { + pci_err(dev, "No ACS save buffer, not adopting ACS controls\n"); + return -EINVAL; + } + + return 0; +} + /** * pci_liveupdate_is_incoming() - Check if a device is incoming-preserved * @dev: The PCI device to check diff --git a/drivers/pci/liveupdate.h b/drivers/pci/liveupdate.h index e5d2a19d2ca2..d4721ffcecb2 100644 --- a/drivers/pci/liveupdate.h +++ b/drivers/pci/liveupdate.h @@ -15,6 +15,7 @@ void pci_liveupdate_setup_device(struct pci_dev *dev); void pci_liveupdate_cleanup_device(struct pci_dev *dev); bool pci_liveupdate_preserve_bus_numbers(void); bool pci_liveupdate_refuse_bus_numbers(struct pci_bus *bus, struct pci_dev= *dev); +int pci_liveupdate_adopt_acs(struct pci_dev *dev); #else static inline void pci_liveupdate_setup_device(struct pci_dev *dev) { @@ -34,6 +35,11 @@ static inline bool pci_liveupdate_refuse_bus_numbers(str= uct pci_bus *bus, { return false; } + +static inline int pci_liveupdate_adopt_acs(struct pci_dev *dev) +{ + return -EINVAL; +} #endif =20 #endif /* DRIVERS_PCI_LIVEUPDATE_H */ diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index dd25c01736b4..47d8229115b8 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -35,6 +35,8 @@ #include #include #include + +#include "liveupdate.h" #include "pci.h" =20 DEFINE_MUTEX(pci_slot_mutex); @@ -1080,6 +1082,9 @@ void pci_enable_acs(struct pci_dev *dev) bool enable_acs =3D false; int pos; =20 + if (!pci_liveupdate_adopt_acs(dev)) + return; + /* If an iommu is present we start with kernel default caps */ if (pci_acs_enable) { if (pci_dev_specific_enable_acs(dev)) --=20 2.55.0.1082.g2b9226bbc0-goog From nobody Thu Sep 24 23:04:13 2026 Received: from mail-pl1-f198.google.com (mail-pl1-f198.google.com [209.85.214.198]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D1972527599 for ; Fri, 18 Sep 2026 20:07:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.198 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789762025; cv=none; b=kN6co4sF7eFSdW26ivzP7pAOKFQ4+Doia56UH/uDCdTIfnlpXDIRxke8X9arKNxGGmUW5uR/k7qBZMMu+vJ7mBeKuN/x4L8AW9PW/Eqchonebf0Md9rsS4lfGVZkKC1NKAzWmGlnU7TD4dUzvKnvJ8UCfFdWMn0oONMqeKX3S5Y= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789762025; c=relaxed/simple; bh=Bzapy6bcJbixrqPHS2k7cavep+t2AZYH7zuQMqtEwjw=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=sSJhVavjZlCwnAHBZcoSrJpx65uc45ZXgPgtR9uqRFg+J29seDc0ErT4vWha3j2LRjn6CbiA0tJtdsYAaFomSg9Y5sv9+Ft2/oqvAP9RblzkSJSi/qZabZLS/Tc/yjdNJ+87dbxmdDwvMY+2B1p7KoCB9Kzvr8LHrEv8qffJfgM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--dmatlack.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=K7s81NnF; arc=none smtp.client-ip=209.85.214.198 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--dmatlack.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="K7s81NnF" Received: by mail-pl1-f198.google.com with SMTP id d9443c01a7336-2dd753a52bbso20145585ad.0 for ; Fri, 18 Sep 2026 13:07:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1789762022; x=1790366822; darn=vger.kernel.org; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:from:to:cc:subject:date:message-id:reply-to :content-type; bh=9BR71s6OXVqtI8pG8LyTaxh9Qe5FvWSAHHddp0nR9J8=; b=K7s81NnFcVOaUYwEZ3aobyEyUuH9Z51wNmk8TbQpytO4fLDRlRElvYqn63y3jn1LIj J3Jjsea+48QUP+D536Dm/uUSMtJvfqJYidsG2k248vgj5kWB6oRRmlwHnz1B4yA3f2QD aIfn6fnp625Jul1QRGojHLJv6lLaccPmRSFmCBjodsZsPZoAvo05Ns2YorJJn8YzeX0j 1CiYuFvhg3mpzRUlijQMHQ9EXV9zukvmwOKFgV2X64Ju7wRHwsowP4g5B5PAXI72+PmM aFaJl0peBPOxWbi799mcVMVr8e8JAW3nTgLXEe+KjnUscV9NIoHcHaRl/9iWvpRwpva2 /NtQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1789762022; x=1790366822; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=9BR71s6OXVqtI8pG8LyTaxh9Qe5FvWSAHHddp0nR9J8=; b=ETNZQ/DMIU4MsIAoAFblpCLdZaD/dnMgCoz8pzbLhf/coHanHGhUNskl8gHqSyzX5b uSRvTBMrMoNjTP/hGxjHuSIGRdG6VJ4QABBIYpzUbPmbK8zMPOGEj+H8tm6wsuSCBYNW V8df878KZnH9OjzZ43+CjeVRRsdpt9rhhTropVcgbjmJwYfsGNpqqRNdfJ1nuSgVjH4S D4nOqv1WkMHWUnfdKTZXe7JVjFM9k2v0RAy+Ci5qmLZVV+0xEO/Vyj09f7lP/ulESkfB bDOMF3d9lF5jqwL7MvqvnjNqyl8GCsWqEL1h6wGBVmnvjytCbzCwYpGtcm0Ac4Erm4JN SRuA== X-Forwarded-Encrypted: i=1; AKwUvByscyFlUV/wTKgiM5RZYrzcFd35RBStH4H4ZXUZ01pwAVQ4K80+29H+Unv6NBAUDqMWEPlGeSd7Nwt12AI=@vger.kernel.org X-Gm-Message-State: AFuF++mxcTv60/iwbXbmAamHTmBW7rBGKohq/o2KrnUGmglvOH3Q7hqE HOitBRnPcYfIAhV1aqXNU5/eOZXEoy56OocEE8xg9YMN4og6lgtKe1jiNVdSWSzTMnbufwyeawT ceP+xV/GyvkN/jA== X-Received: from pldv5.prod.google.com ([2002:a17:902:ca85:b0:2d9:e5f:1d26]) (user=dmatlack job=prod-delivery.src-stubby-dispatcher) by 2002:a17:902:ab8f:b0:2dd:c100:a5ea with SMTP id d9443c01a7336-2ddc100a6aemr7241285ad.62.1789762021877; Fri, 18 Sep 2026 13:07:01 -0700 (PDT) Date: Fri, 18 Sep 2026 20:06:36 +0000 In-Reply-To: <20260918200640.887030-1-dmatlack@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260918200640.887030-1-dmatlack@google.com> X-Mailer: git-send-email 2.55.0.1082.g2b9226bbc0-goog Message-ID: <20260918200640.887030-11-dmatlack@google.com> Subject: [PATCH v9 10/13] PCI: liveupdate: Adopt ARI Forwarding Enable on preserved bridges From: David Matlack To: kexec@lists.infradead.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, linux-pci@vger.kernel.org Cc: Adithya Jayachandran , Alexander Graf , Alex Williamson , Bjorn Helgaas , Chris Li , David Matlack , David Rientjes , Jacob Pan , Jason Gunthorpe , Jonathan Corbet , Josh Hilke , Leon Romanovsky , Lukas Wunner , Mike Rapoport , Parav Pandit , Pasha Tatashin , Pranjal Shrivastava , Pratyush Yadav , Randy Dunlap , Saeed Mahameed , Samiullah Khawaja , Shuah Khan , Vipin Sharma , William Tu , Yi Liu Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Adopt the ARI Forwarding Enable bit on preserved bridges and update pci_dev->ari_enabled accordingly during a Live Update. This ensures that the preserved devices on the bridge's secondary bus can be identified with the same expanded 8-bit function number after a Live Update. Reviewed-by: Bjorn Helgaas Reviewed-by: Pranjal Shrivastava Signed-off-by: David Matlack --- drivers/pci/liveupdate.c | 30 ++++++++++++++++++++++++++++++ drivers/pci/liveupdate.h | 6 ++++++ drivers/pci/pci.c | 8 +++++++- 3 files changed, 43 insertions(+), 1 deletion(-) diff --git a/drivers/pci/liveupdate.c b/drivers/pci/liveupdate.c index 0145399b3834..3c9a10a892fc 100644 --- a/drivers/pci/liveupdate.c +++ b/drivers/pci/liveupdate.c @@ -189,6 +189,10 @@ * way after Live Update and ensures that IOMMU groups do not change. N= ote * that a device will use its adopted ACS controls for the lifetime of = its * struct pci_dev (i.e. even after pci_liveupdate_finish()). + * + * * The PCI core adopts ARI Forwarding Enable on all bridges with downst= ream + * preserved devices to ensure that all preserved devices on the bridge= 's + * secondary bus are addressable after the Live Update. */ =20 #define pr_fmt(fmt) "PCI: liveupdate: " fmt @@ -904,6 +908,32 @@ int pci_liveupdate_adopt_acs(struct pci_dev *dev) return 0; } =20 +/** + * pci_liveupdate_adopt_ari() - Adopt ARI configuration + * @dev: The PCI device to adopt ARI configuration for + * + * For devices preserved across a Live Update, read the ARI state from + * hardware and adopt it. This ensures the device continues to use the ARI + * configuration established by the previous kernel. + * + * Return: 0 on success, or -EINVAL if the device was not preserved. + */ +int pci_liveupdate_adopt_ari(struct pci_dev *dev) +{ + u16 val; + + guard(rwsem_read)(&pci_liveupdate.rwsem); + + if (!dev->liveupdate.incoming) + return -EINVAL; + + pcie_capability_read_word(dev, PCI_EXP_DEVCTL2, &val); + + /* Safe to modify dev->ari_enabled bitfield during enumeration. */ + dev->ari_enabled =3D !!(val & PCI_EXP_DEVCTL2_ARI); + return 0; +} + /** * pci_liveupdate_is_incoming() - Check if a device is incoming-preserved * @dev: The PCI device to check diff --git a/drivers/pci/liveupdate.h b/drivers/pci/liveupdate.h index d4721ffcecb2..90b2754ecc0c 100644 --- a/drivers/pci/liveupdate.h +++ b/drivers/pci/liveupdate.h @@ -16,6 +16,7 @@ void pci_liveupdate_cleanup_device(struct pci_dev *dev); bool pci_liveupdate_preserve_bus_numbers(void); bool pci_liveupdate_refuse_bus_numbers(struct pci_bus *bus, struct pci_dev= *dev); int pci_liveupdate_adopt_acs(struct pci_dev *dev); +int pci_liveupdate_adopt_ari(struct pci_dev *dev); #else static inline void pci_liveupdate_setup_device(struct pci_dev *dev) { @@ -40,6 +41,11 @@ static inline int pci_liveupdate_adopt_acs(struct pci_de= v *dev) { return -EINVAL; } + +static inline int pci_liveupdate_adopt_ari(struct pci_dev *dev) +{ + return -EINVAL; +} #endif =20 #endif /* DRIVERS_PCI_LIVEUPDATE_H */ diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 47d8229115b8..0ae405b86b2f 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3625,7 +3625,7 @@ void pci_configure_ari(struct pci_dev *dev) u32 cap; struct pci_dev *bridge; =20 - if (pcie_ari_disabled || !pci_is_pcie(dev) || dev->devfn) + if (!pci_is_pcie(dev) || dev->devfn) return; =20 bridge =3D dev->bus->self; @@ -3636,6 +3636,12 @@ void pci_configure_ari(struct pci_dev *dev) if (!(cap & PCI_EXP_DEVCAP2_ARI)) return; =20 + if (!pci_liveupdate_adopt_ari(bridge)) + return; + + if (pcie_ari_disabled) + return; + if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ARI)) { pcie_capability_set_word(bridge, PCI_EXP_DEVCTL2, PCI_EXP_DEVCTL2_ARI); --=20 2.55.0.1082.g2b9226bbc0-goog From nobody Thu Sep 24 23:04:13 2026 Received: from mail-pg1-f197.google.com (mail-pg1-f197.google.com [209.85.215.197]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id AFDF75275B6 for ; Fri, 18 Sep 2026 20:07:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.197 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789762026; cv=none; b=ijce1LjE95OlM+bWKwM6nVL3BjGEgb2DZ1mNiVVUmpJzH7JdKoU59bbCmf8Gqi8ijwyyPsLNNosqEhI4yA5CFXI4b8M1UhMJZaEUPZS+9AXKjyrTNbFb4zmMUO4bGoGPuCvLxcWszfUeq+QehHuhXWc15ViWXRwDmxm5kBCE4xM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789762026; c=relaxed/simple; bh=QrywdoolN15JiKsqZMowqLAM7mFL6Y+xqQxJhjNOwG8=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=eXTilm44rXfosmEXF/LD3XZLVv/G9bV4Aj0Ue9AAQOEFNYKwLQcfI/cc9xeXPzRzYseX5FC8uSgevCpjAd63ckJ6JbuhfbqwXTJwYxYTNihHpXpjKFdAxgbwL50GPnkoh8Lu/c60cY+jK8WvyE3iDYNacorJ2hWwhUGCHzTebx4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--dmatlack.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=qRJiIZFK; arc=none smtp.client-ip=209.85.215.197 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--dmatlack.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="qRJiIZFK" Received: by mail-pg1-f197.google.com with SMTP id 41be03b00d2f7-cc1eb205d31so1456520a12.2 for ; Fri, 18 Sep 2026 13:07:03 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1789762023; x=1790366823; darn=vger.kernel.org; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:from:to:cc:subject:date:message-id:reply-to :content-type; bh=DtzV4A7ubsZUgCkixUUqpezuoGcnB31d+FYFVqWSkUU=; b=qRJiIZFKll6SRnnopTcx8GRSkjJjRz2IkkDlYtRPjTMdq6zadrD/ioLEzIy8DInbl/ x/00RuI2F+U274yvNt7eKnSX5cv4bhP5DDNSL2y4e1qBKNHIhKY8HqNZUOdOriZaAJrh BYCIA87ZO8YbNNLT2JYDUGMsdvnZB6KgJO2l1ZZ8GJxEvXMbjWwiEcF8F0iEHht7+wc7 QGUFxErPNE4HA6Wr9ImTrs80hI/tKph3fSxzBcTg2PH3lpiPx7YDveKMSZXOCwcurQYH jSUXP7e/ZV7oavKI4CjhvVW8afUCaKO79vBMZS+EZgwI1ceCh5CwVfCqjAQnywlofbAG 7sbg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1789762023; x=1790366823; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=DtzV4A7ubsZUgCkixUUqpezuoGcnB31d+FYFVqWSkUU=; b=v9+QH2RC5kKKFNrs4nm/7Pw6e5YEUP0lUB0LcbIOeG9W2x3uZs0Gn3T775wJA33FgR Xy6sUKDlMGKYHPTB2IW3Qy4a2VYpmEqGJ1FlJ78Xs++GsM2tJv7KrUqSXC0u+qPDEXLs Ps7bcwwSVxjniV1NuIgarBTNYOQ1UL64O/nU315j+arvp7Js56j5zRTRHRRj0UsnFAk1 SRgIaZZjEidglhdRqKis7vjyoqdOeQxHkcl8dAW0OqVsjAszkul4eDEpIty7qySVgqgx dawSnWNOImfKy95Bboo16DXw7iBzLHXuRzgra7tYGccPe8vsV7J6XveZ/6xdXJJgVOA7 sqdA== X-Forwarded-Encrypted: i=1; AKwUvBzXP1UrNgcSDACfi2yoPY6qTUR0hIdRjIKwflsMpQLCGmpANXgEPHZuik+lFoX4ByXpb2B3MjttMVWZw6g=@vger.kernel.org X-Gm-Message-State: AFuF++nHPp8v7ibnPU7dUF74a05R5jsZwU2VxzTLWTUoamFnORCoREgL rd5JhrXfNbjAcLWQ1LpH9tN49HYM5vHVxM+bq8XHO9V5VxV3r/VGk1jSWmAA+4B8eKZqCW5gvby Ja2T4xnlMoBjQCg== X-Received: from pgvc3.prod.google.com ([2002:a65:6183:0:b0:cc4:56ea:7ae7]) (user=dmatlack job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6a21:1b81:b0:3da:bea4:6215 with SMTP id adf61e73a8af0-3dd8c52948cmr7756540637.19.1789762022690; Fri, 18 Sep 2026 13:07:02 -0700 (PDT) Date: Fri, 18 Sep 2026 20:06:37 +0000 In-Reply-To: <20260918200640.887030-1-dmatlack@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260918200640.887030-1-dmatlack@google.com> X-Mailer: git-send-email 2.55.0.1082.g2b9226bbc0-goog Message-ID: <20260918200640.887030-12-dmatlack@google.com> Subject: [PATCH v9 11/13] PCI: liveupdate: Freeze preservation status during shutdown From: David Matlack To: kexec@lists.infradead.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, linux-pci@vger.kernel.org Cc: Adithya Jayachandran , Alexander Graf , Alex Williamson , Bjorn Helgaas , Chris Li , David Matlack , David Rientjes , Jacob Pan , Jason Gunthorpe , Jonathan Corbet , Josh Hilke , Leon Romanovsky , Lukas Wunner , Mike Rapoport , Parav Pandit , Pasha Tatashin , Pranjal Shrivastava , Pratyush Yadav , Randy Dunlap , Saeed Mahameed , Samiullah Khawaja , Shuah Khan , Vipin Sharma , William Tu , Yi Liu Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Freeze a device's outgoing preservation status (preserved or not preserved) during shutdown. This enables the PCI core and drivers to safely make decisions based on the device's preservation status during shutdown. Note that pci_liveupdate_freeze() is triggered by the PCI core rather than from drivers participating in Live Update so that all devices can have their status frozen (i.e. prevent non-preserved devices from getting preserved late). Reviewed-by: Pranjal Shrivastava Reviewed-by: Pasha Tatashin Reviewed-by: Samiullah Khawaja Reviewed-by: Bjorn Helgaas Signed-off-by: David Matlack --- drivers/pci/liveupdate.c | 16 ++++++++++++++++ drivers/pci/liveupdate.h | 5 +++++ drivers/pci/pci-driver.c | 2 ++ include/linux/pci_liveupdate.h | 3 +++ 4 files changed, 26 insertions(+) diff --git a/drivers/pci/liveupdate.c b/drivers/pci/liveupdate.c index 3c9a10a892fc..496ba74c26ce 100644 --- a/drivers/pci/liveupdate.c +++ b/drivers/pci/liveupdate.c @@ -481,6 +481,11 @@ static int pci_liveupdate_unpreserve_device(struct pci= _flb_outgoing *outgoing, { struct pci_dev_ser *dev_ser =3D dev->liveupdate.outgoing; =20 + if (dev->liveupdate.frozen) { + pci_warn(dev, "Cannot unpreserve device after it is frozen!\n"); + return -EINVAL; + } + if (!dev_ser) { pci_warn(dev, "Cannot unpreserve device that is not preserved\n"); return -EINVAL; @@ -547,6 +552,11 @@ static int pci_liveupdate_preserve_device(struct pci_f= lb_outgoing *outgoing, return -EINVAL; } =20 + if (dev->liveupdate.frozen) { + pci_warn(dev, "Cannot preserve device after it is frozen!\n"); + return -EINVAL; + } + if (!dev->liveupdate.outgoing) { struct pci_dev_ser *dev_ser; =20 @@ -797,6 +807,12 @@ void pci_liveupdate_cleanup_device(struct pci_dev *dev) pci_WARN(dev, 1, "Destroying incoming-preserved device!\n"); } =20 +void pci_liveupdate_freeze(struct pci_dev *dev) +{ + guard(rwsem_write)(&pci_liveupdate.rwsem); + dev->liveupdate.frozen =3D true; +} + static int pci_liveupdate_finish_device(struct pci_ser *ser, struct pci_de= v *dev) { if (!dev->liveupdate.incoming) { diff --git a/drivers/pci/liveupdate.h b/drivers/pci/liveupdate.h index 90b2754ecc0c..5aac19f7bee6 100644 --- a/drivers/pci/liveupdate.h +++ b/drivers/pci/liveupdate.h @@ -13,6 +13,7 @@ #ifdef CONFIG_PCI_LIVEUPDATE void pci_liveupdate_setup_device(struct pci_dev *dev); void pci_liveupdate_cleanup_device(struct pci_dev *dev); +void pci_liveupdate_freeze(struct pci_dev *dev); bool pci_liveupdate_preserve_bus_numbers(void); bool pci_liveupdate_refuse_bus_numbers(struct pci_bus *bus, struct pci_dev= *dev); int pci_liveupdate_adopt_acs(struct pci_dev *dev); @@ -26,6 +27,10 @@ static inline void pci_liveupdate_cleanup_device(struct = pci_dev *dev) { } =20 +static inline void pci_liveupdate_freeze(struct pci_dev *dev) +{ +} + static inline bool pci_liveupdate_preserve_bus_numbers(void) { return false; diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index e16aa59dd7ac..dff60a03204f 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -21,6 +21,7 @@ #include #include #include +#include "liveupdate.h" #include "pci.h" #include "pcie/portdrv.h" =20 @@ -559,6 +560,7 @@ static void pci_device_shutdown(struct device *dev) struct pci_dev *pci_dev =3D to_pci_dev(dev); struct pci_driver *drv =3D pci_dev->driver; =20 + pci_liveupdate_freeze(pci_dev); pm_runtime_resume(dev); =20 if (drv && drv->shutdown) diff --git a/include/linux/pci_liveupdate.h b/include/linux/pci_liveupdate.h index d45a5b524909..bb79348769b7 100644 --- a/include/linux/pci_liveupdate.h +++ b/include/linux/pci_liveupdate.h @@ -21,11 +21,14 @@ * set up, i.e. it was matched to state preserved by the pr= evious * kernel. Unlike @incoming, this is never cleared, so it s= tays * true after the device finishes participating in Live Upd= ate. + * @frozen: True if the outgoing preservation status of this device is fro= zen + * and thus cannot be changed. */ struct pci_liveupdate { struct pci_dev_ser *outgoing; struct pci_dev_ser *incoming; bool was_incoming; + bool frozen; }; =20 struct pci_dev; --=20 2.55.0.1082.g2b9226bbc0-goog From nobody Thu Sep 24 23:04:13 2026 Received: from mail-pl1-f197.google.com (mail-pl1-f197.google.com [209.85.214.197]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 76BFB528425 for ; Fri, 18 Sep 2026 20:07:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.197 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789762027; cv=none; b=CKMVG5i5nFfwIQKWeIJW6oXgJxGdk5ZVwFVTxqcfLLHwTqwwTzNpvrrOR/gpVHfAFw+Rtt2K65/0twXd9Kgb0f1Xr1T2XzTlz6Hc2l97A7nUi7QKgBi5MiY1kP1CCV16Ke6wVymGVHMp+MLv7imylZy1HS7bpGlBsUEezb/lhbo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789762027; c=relaxed/simple; bh=71uGNjjTFBcC6CcgDOl0OA0ORlR5yq5Q4UzPCRZXBhs=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=ao3phguo2bck5Sbc6q7MAN+HBdHmyA1d5WMtWbsHt8OWOLmEQD6wO6P4RyVHt7sNBJG8Yxgrje9SJp2WV3IGoeSFXLtLXVzRhPjFgz5nrrZs9xbdXzd0bnu/gy4AASn3ap2blPzD6TLl2FHlolCOT9TNvQaMsI774uICe9IRoxo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--dmatlack.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=Czy1qOXv; arc=none smtp.client-ip=209.85.214.197 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--dmatlack.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="Czy1qOXv" Received: by mail-pl1-f197.google.com with SMTP id d9443c01a7336-2dd753a52bbso20146065ad.0 for ; Fri, 18 Sep 2026 13:07:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1789762024; x=1790366824; darn=vger.kernel.org; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:from:to:cc:subject:date:message-id:reply-to :content-type; bh=siMAWwJv4yjE1eEtNkD6j1Z4yk/PjEJKUuLYDAAYM4E=; b=Czy1qOXvAm3CD82QgEwLk+tMR8kjVJkkQaLU0s00FSF1NiNooA/xJ2wtOluZRsfTxh IRsoBxGfsEWO4mgOeaFpodEyixmgUQ2eVKpzsuhzHX/FsY5Anr+/o31M1TySataFfbgP QW8+NUFhBAwJjsp3cjtLaSYfsV2kk8MZE6zdjCK1LKoFwaZYjMclPUfrmoJavkObzXKG FmahUZiFUzBmefjKJ7k7l9/u2/zzkqE5QToozpar5Rq3LnALYE7M5Jraipvh0+e9fuTr O/RCsfYKC8b6N/RghD+i/ohdPLQVix1f1yN3OXVCqBpmDYbSKoUAvZ4vTct9btrn3HtS J5Mw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1789762024; x=1790366824; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=siMAWwJv4yjE1eEtNkD6j1Z4yk/PjEJKUuLYDAAYM4E=; b=Zbi+LEynhlPmF5Sikv0P7RYczZUq3upolA6fJYld4Xf+e5GHv++k3geCLVMpMoZObt vDktSa8rYJsNRzhwloHosI+05aODG8G42XunXoPLURJBUBVXXRRDopilM8xokJ/qULAJ WT25AKvfr8l6xBWhiNhKauMkMBu82QPp41QDYnDcqoi87rxfG5sFS3xKoznCsB/P+U3t ltTJ8AuIR+Ue7KBuWNX+ft7kFttWH3JV26imxbj8VsKZ/cB98o0c5bUHGoKxHw9imCKE KL/mYVqDCzNnvYewcbytpzjah5Hd+5KYaTaavEuezT75DkKo0EtpLjQs+NHr7zxpIQyE UC6Q== X-Forwarded-Encrypted: i=1; AKwUvBxqnExTWFIYzfPx3bVaVT5MACwhhO+uoh0tsfcomd37OJfmb1K1M5Y/9xEmHypDdIw6tOVFF7iitf1Nyu4=@vger.kernel.org X-Gm-Message-State: AFuF++nMB+Y1qqzhQ53/N17VVaeSJHfOISZ2wAh2N45TzKopnRHntQom 63+3bVP2IN/PbeLET1fpakvksL5jPgzjv17v52iXpaHGMyE6hZmATcqvRxSnF/Z5l/bF2ysCo0D Z2yYDh7qvTf3/yQ== X-Received: from pgbgf16.prod.google.com ([2002:a05:6a02:2cd0:b0:cc5:1124:470d]) (user=dmatlack job=prod-delivery.src-stubby-dispatcher) by 2002:a17:902:7209:b0:2dd:c100:a5dd with SMTP id d9443c01a7336-2ddc100a681mr6689305ad.49.1789762023439; Fri, 18 Sep 2026 13:07:03 -0700 (PDT) Date: Fri, 18 Sep 2026 20:06:38 +0000 In-Reply-To: <20260918200640.887030-1-dmatlack@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260918200640.887030-1-dmatlack@google.com> X-Mailer: git-send-email 2.55.0.1082.g2b9226bbc0-goog Message-ID: <20260918200640.887030-13-dmatlack@google.com> Subject: [PATCH v9 12/13] PCI: liveupdate: Do not disable bus mastering on preserved devices during kexec From: David Matlack To: kexec@lists.infradead.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, linux-pci@vger.kernel.org Cc: Adithya Jayachandran , Alexander Graf , Alex Williamson , Bjorn Helgaas , Chris Li , David Matlack , David Rientjes , Jacob Pan , Jason Gunthorpe , Jonathan Corbet , Josh Hilke , Leon Romanovsky , Lukas Wunner , Mike Rapoport , Parav Pandit , Pasha Tatashin , Pranjal Shrivastava , Pratyush Yadav , Randy Dunlap , Saeed Mahameed , Samiullah Khawaja , Shuah Khan , Vipin Sharma , William Tu , Yi Liu Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Do not disable bus mastering on outgoing preserved devices during pci_device_shutdown() for kexec. Preserved devices must be allowed to perform memory transactions during a Live Update to ensure continuous operation. Clearing the bus mastering bit would prevent these devices from issuing any memory requests while the new kernel boots. Because bridges upstream of preserved endpoint devices are also automatically preserved, this change also avoids clearing bus mastering on them. This is critical because clearing bus mastering on an upstream bridge prevents the bridge from forwarding memory requests upstream (i.e. it would prevent the endpoint device from accessing system RAM and doing peer-to-peer transactions with devices not downstream of the bridge). Reviewed-by: Pranjal Shrivastava Reviewed-by: Pasha Tatashin Reviewed-by: Samiullah Khawaja Reviewed-by: Bjorn Helgaas Signed-off-by: David Matlack --- drivers/pci/liveupdate.c | 11 +++++++++++ drivers/pci/liveupdate.h | 6 ++++++ drivers/pci/pci-driver.c | 7 +++++-- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/drivers/pci/liveupdate.c b/drivers/pci/liveupdate.c index 496ba74c26ce..51695782ed42 100644 --- a/drivers/pci/liveupdate.c +++ b/drivers/pci/liveupdate.c @@ -193,6 +193,10 @@ * * The PCI core adopts ARI Forwarding Enable on all bridges with downst= ream * preserved devices to ensure that all preserved devices on the bridge= 's * secondary bus are addressable after the Live Update. + * + * * The PCI core does not disable bus mastering on outgoing preserved de= vices + * during kexec. This allows preserved devices to issue memory transact= ions + * throughout the Live Update. */ =20 #define pr_fmt(fmt) "PCI: liveupdate: " fmt @@ -950,6 +954,13 @@ int pci_liveupdate_adopt_ari(struct pci_dev *dev) return 0; } =20 +bool pci_liveupdate_is_outgoing(struct pci_dev *dev) +{ + guard(rwsem_read)(&pci_liveupdate.rwsem); + pci_WARN_ONCE(dev, !dev->liveupdate.frozen, "Preservation status is unsta= ble!\n"); + return dev->liveupdate.outgoing; +} + /** * pci_liveupdate_is_incoming() - Check if a device is incoming-preserved * @dev: The PCI device to check diff --git a/drivers/pci/liveupdate.h b/drivers/pci/liveupdate.h index 5aac19f7bee6..333ddd8f36c5 100644 --- a/drivers/pci/liveupdate.h +++ b/drivers/pci/liveupdate.h @@ -18,6 +18,7 @@ bool pci_liveupdate_preserve_bus_numbers(void); bool pci_liveupdate_refuse_bus_numbers(struct pci_bus *bus, struct pci_dev= *dev); int pci_liveupdate_adopt_acs(struct pci_dev *dev); int pci_liveupdate_adopt_ari(struct pci_dev *dev); +bool pci_liveupdate_is_outgoing(struct pci_dev *dev); #else static inline void pci_liveupdate_setup_device(struct pci_dev *dev) { @@ -51,6 +52,11 @@ static inline int pci_liveupdate_adopt_ari(struct pci_de= v *dev) { return -EINVAL; } + +static inline bool pci_liveupdate_is_outgoing(struct pci_dev *dev) +{ + return false; +} #endif =20 #endif /* DRIVERS_PCI_LIVEUPDATE_H */ diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index dff60a03204f..c1ac2ef025e2 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -569,11 +569,14 @@ static void pci_device_shutdown(struct device *dev) /* * If this is a kexec reboot, turn off Bus Master bit on the * device to tell it to not continue to do DMA. Don't touch - * devices in D3cold or unknown states. + * devices being preserved for Live Update or in D3cold or + * unknown states. + * * If it is not a kexec reboot, firmware will hit the PCI * devices with big hammer and stop their DMA any way. */ - if (kexec_in_progress && (pci_dev->current_state <=3D PCI_D3hot)) + if (kexec_in_progress && !pci_liveupdate_is_outgoing(pci_dev) && + pci_dev->current_state <=3D PCI_D3hot) pci_clear_master(pci_dev); } =20 --=20 2.55.0.1082.g2b9226bbc0-goog From nobody Thu Sep 24 23:04:13 2026 Received: from mail-pg1-f200.google.com (mail-pg1-f200.google.com [209.85.215.200]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5AA7C526ABD for ; Fri, 18 Sep 2026 20:07:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.200 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789762027; cv=none; b=XYKopg7uge4x71mZaoSdrc/cKO1IMdZ/tpRfMGgUmpPvjVrRMMI0kJE4UbkAncMalkkNiWHivFDXe1rT12SBLvUX4O5EsWskh68Z94xJ2/BbMsU7DwPGRSOVE9ppo6C/9+CZ54jILHLNRpOamooZnvtJTiGVtTAt0z891p3fAbE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789762027; c=relaxed/simple; bh=UfzWz/Vy8dC/P8FblFhguwkyXeiWSwXDoG++vSs9Azc=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=bTGVB7UF2fB4S2pMZJjqd8qHJwwFOgxRH7GCf+5Oe9QJgl+blGErbA/HZpZEJnb6AsqqHHoGaCUApENvvTDseQlmAkZqiM0kiMpALE7KqlPJToXPAXZdc7izv6nS+kVmLB63I88jBjEqy3WEmsKpY5g+q4CeWYWL31xSxO/jjPU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--dmatlack.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=YQrr/qsv; arc=none smtp.client-ip=209.85.215.200 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--dmatlack.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="YQrr/qsv" Received: by mail-pg1-f200.google.com with SMTP id 41be03b00d2f7-cc1bbd7d6e6so1099244a12.3 for ; Fri, 18 Sep 2026 13:07:05 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1789762024; x=1790366824; darn=vger.kernel.org; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:from:to:cc:subject:date:message-id:reply-to :content-type; bh=P9IxdwpsNG1RvdTFOh7VCNlg1Z6K9O4n/2FHxAEnlFE=; b=YQrr/qsv2XanYxitvXUnijhvSAL5Ea09XnZsjg6uQEojnST7bk+MlKtZgeaYGVbrZ5 D1+Niqsp4bQTXL+3oXSNMvl0sgcJOYkRylwByRxhkhdS5D0ffunsFb73nxDJuksM6cOi cKUrBz/GQFk4eT9PKeODML4NU4hGLIH773sWeGW0nxCG5MH2BKqOenr0BUxvdz2rObpx tT30L+hB+d2VT6nQzK/zdN7kexxQ0X8J8vhuvLZFNJ5ZRb5j/EpeXZMZ+JPTCmtn7HKx oGIOhLwfvxEwmwD2oUainAJRCXom7AxuXXSPL5BZu3CrVtR7Jz1mFfyoJhHh8tA+oNLQ 74RQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1789762024; x=1790366824; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=P9IxdwpsNG1RvdTFOh7VCNlg1Z6K9O4n/2FHxAEnlFE=; b=fnrpol5QtQ/Ayi13MbzL2SNvkdQ/1PIvNOhtAap76fFpzed0LEL7btf/gX3wvmfkwd d+5uqC483Nt+Dzvp4Hk3PhuIYu5pbqlQLYRBt3Bq299p154niazPcZMrx+VpY60ozL6n x6m3CgENs1PW1igsFxdAfhDhVsAm+rYdYBN88RuwxjHKgLv6Rfr1bGuWrzicd3Tb8iHV N+0ZTsyTyicO0Qm8gVUkiYlz68fY1BimN2FydUHiEki/CsXVtRorXlEtY7dC8AIMD7ZS 5mhzh3LDE2lD2v8+UQzzMUKvh8L5dSq/8mtQs2cIxXKh/U5KufabvYqQ/r4MPFdHWfAj TH5Q== X-Forwarded-Encrypted: i=1; AKwUvBxlw3T4aRRuuN1u8kxHJwFaP7BMRGFRqSQ4mtFF9pJuvlIfE+wQ55XdR5Ilf8kR83278ZkRgtYRtPKUHhU=@vger.kernel.org X-Gm-Message-State: AFuF++ljJemOco+V3YBiMYCqwEMXXkIev/lb949fv/5oe0JlJm04eHi6 TCt9AiaLl5eDqO4eBAYjJ+kiznptNmpeR1wEhRjHsOZ6Wm9qa+YVZaN0dM14L6XMLLWj2e8N9ie pZa6HKlFulGa1Ww== X-Received: from pgbeh14.prod.google.com ([2002:a05:6a02:256e:b0:cc4:b44e:9200]) (user=dmatlack job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6a21:6083:b0:3dd:64fa:5520 with SMTP id adf61e73a8af0-3dd8c4287dbmr7496380637.9.1789762024286; Fri, 18 Sep 2026 13:07:04 -0700 (PDT) Date: Fri, 18 Sep 2026 20:06:39 +0000 In-Reply-To: <20260918200640.887030-1-dmatlack@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260918200640.887030-1-dmatlack@google.com> X-Mailer: git-send-email 2.55.0.1082.g2b9226bbc0-goog Message-ID: <20260918200640.887030-14-dmatlack@google.com> Subject: [PATCH v9 13/13] Documentation: PCI: Add documentation for Live Update From: David Matlack To: kexec@lists.infradead.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, linux-pci@vger.kernel.org Cc: Adithya Jayachandran , Alexander Graf , Alex Williamson , Bjorn Helgaas , Chris Li , David Matlack , David Rientjes , Jacob Pan , Jason Gunthorpe , Jonathan Corbet , Josh Hilke , Leon Romanovsky , Lukas Wunner , Mike Rapoport , Parav Pandit , Pasha Tatashin , Pranjal Shrivastava , Pratyush Yadav , Randy Dunlap , Saeed Mahameed , Samiullah Khawaja , Shuah Khan , Vipin Sharma , William Tu , Yi Liu Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add documentation files for the PCI subsystem's participation in Live Update. These documentation files are generated from the kernel-doc comments in the PCI Live Update source code. They describe the File-Lifecycle-Bound (FLB) API, the device tracking API, and the specific policies applied to preserved devices (such as bus number inheritance and bus mastering preservation). Reviewed-by: Pranjal Shrivastava Reviewed-by: Samiullah Khawaja Reviewed-by: Bjorn Helgaas Signed-off-by: David Matlack --- Documentation/PCI/index.rst | 1 + Documentation/PCI/liveupdate.rst | 35 +++++++++++++++++++++++++++ Documentation/core-api/liveupdate.rst | 1 + MAINTAINERS | 1 + 4 files changed, 38 insertions(+) create mode 100644 Documentation/PCI/liveupdate.rst diff --git a/Documentation/PCI/index.rst b/Documentation/PCI/index.rst index 5d720d2a415e..23fb737ac969 100644 --- a/Documentation/PCI/index.rst +++ b/Documentation/PCI/index.rst @@ -20,3 +20,4 @@ PCI Bus Subsystem controller/index boot-interrupts tph + liveupdate diff --git a/Documentation/PCI/liveupdate.rst b/Documentation/PCI/liveupdat= e.rst new file mode 100644 index 000000000000..96b1d7f5df3a --- /dev/null +++ b/Documentation/PCI/liveupdate.rst @@ -0,0 +1,35 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D +PCI Support for Live Update +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D + +.. kernel-doc:: drivers/pci/liveupdate.c + :doc: PCI Live Update + +Driver API +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +.. kernel-doc:: drivers/pci/liveupdate.c + :export: + +Internal API +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +.. kernel-doc:: drivers/pci/liveupdate.c + :internal: + +Live Update ABI +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +.. kernel-doc:: include/linux/kho/abi/pci.h + :doc: PCI File-Lifecycle Bound (FLB) Live Update ABI + +.. kernel-doc:: include/linux/kho/abi/pci.h + :internal: + +See Also +=3D=3D=3D=3D=3D=3D=3D=3D + + * :doc:`/core-api/liveupdate` + * :doc:`/core-api/kho/index` diff --git a/Documentation/core-api/liveupdate.rst b/Documentation/core-api= /liveupdate.rst index b3c689e633c1..2bce2644eba2 100644 --- a/Documentation/core-api/liveupdate.rst +++ b/Documentation/core-api/liveupdate.rst @@ -74,3 +74,4 @@ See Also =20 - :doc:`Live Update uAPI ` - :doc:`/core-api/kho/index` +- :doc:`PCI ` diff --git a/MAINTAINERS b/MAINTAINERS index 3eacaa98775c..5d1b58271989 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -21055,6 +21055,7 @@ L: kexec@lists.infradead.org L: linux-pci@vger.kernel.org S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/liveupdate/linux.git +F: Documentation/PCI/liveupdate.rst F: drivers/pci/liveupdate.c F: drivers/pci/liveupdate.h F: include/linux/kho/abi/pci.h --=20 2.55.0.1082.g2b9226bbc0-goog