Re: [PATCH v4 08/18] iommu/vt-d: Clear unpreserved context entries during shutdown
From: Samiullah Khawaja
Date: Wed Aug 26 2026 - 16:30:43 EST
On Wed, Aug 26, 2026 at 04:05:51PM +0800, Baolu Lu wrote:
On 8/8/26 10:27, Samiullah Khawaja wrote:
During normal shutdown the iommu translation is disabled. Since the root
table is preserved during live update, it needs to be cleaned up and the
context entries of the unpreserved devices and root entries for the
unpreserved context tables need to be cleared.
The key assumption here seems to be that, during a live-update kexec,
most devices do not go through the normal iommu release path. Otherwise,
their context entries should already be torn down in the
iommu_release_device path.
Also there is another part that the root table entries of unpreserved
context tables also need to be removed. So even if the devices went
through the release path, the context tables used by those devices are
not removed by the Intel IOMMU driver.
Could you please confirm this assumption and add some short note in the
comments or commit message?
Yes, I can add a short note in the comment here and also in the commit
message.
Signed-off-by: Samiullah Khawaja <skhawaja@xxxxxxxxxx>
---
drivers/iommu/intel/iommu.c | 15 +++-
drivers/iommu/intel/iommu.h | 5 ++
drivers/iommu/intel/liveupdate.c | 140 +++++++++++++++++++++++++++++++
3 files changed, 158 insertions(+), 2 deletions(-)
+
[snip]
+static void clear_unpreserved_context(struct device_domain_info *info, u8 bus, u8 devfn)
+{
+ struct context_entry *context;
+
+ /*
+ * This cleanup is done during shutdown, so it should be fine to only
+ * clear the entries here and issue one global invalidation later to
+ * invalidate all cleared entries.
+ *
+ * Note that the device IOTLB invalidation for unpreserved devices is
+ * skipped this way, but that should not be needed as the devices are
+ * quiesced at this point. This should improve the performance of the
+ * cleanup process and avoids any invalidation timeouts because drivers
+ * might have moved devices to D3 state.
+ */
+ context = iommu_context_addr(info->iommu, bus, devfn, 0);
+ if (context) {
+ context_clear_entry(context);
+ __iommu_flush_cache(info->iommu, context, sizeof(*context));
For tearing down a present context entry, please follow the VT-d
recommended sequence:
- clear only the Present bit,
- flush the updated entry to memory if necessary,
- issue the required cache invalidations,
Do we still need the individual cache invalidations if we issue global
invalidations (context, pasid-cache and iotlb), after clearing all
entries, as they would be done if a new root table was being setup?
Looking at the VT-d specs (Invalidation of Translation Caches), each
invalidation type (cache, pasid and iotlb) defines granularity in both
register and queue based interface. And the granularity indicates that
Global invalidations clear the cached entries for that specific type.
For example following text is used for each cache type (in queue
interface):
Context-cache:
Global Invalidation (01b): All context-cache entries cached at the
remapping hardware are invalidated.
Pasid-cache:
Global Invalidation (11b): All PASID-cache entries are invalidated.
Iotlb:
Global Invalidation (01b):
- All IOTLB entries are invalidated.
- All paging-structure-cache entries are invalidated.
A similar note about using Global invalidation is suggested in the specs
when setting up root table (Set Root Table Pointer Operation).
... software must perform a global invalidate of the contextcache,
PASID-cache (if applicable), and IOTLB, in that order. This is
required to ensure hardware references only the remapping structures
referenced by the new root table pointer and not stale cached entries.
Also please note that this is happening during dmar unit teardown and
system shutdown, and while the context table entries in root table are
being cleared, the memory is not freed until the global invalidation is
issued.
Since this is during shutdown, issuing global invalidations instead of
multiple individual invalidations for devices and aliases is simpler and
would likely also have shutdown time improvements and reduce the
blackout time during liveupdate.
Please let me know if my global invalidations and granularity
understanding is not correct.
I added a comment at the top of this function to explain this, let me
know if you want me to expand it with more details.
- then clear the remaining fields of the entry.
+ }
+}
+
+static int clear_unpreserved_alias_cb(struct pci_dev *pdev, u16 alias, void *data)
+{
+ struct device_domain_info *info = data;
+
+ clear_unpreserved_context(info, PCI_BUS_NUM(alias), alias & 0xff);
+ return 0;
+}
+
+static int clear_unpreserve_context_entry_fn(struct device *dev,
+ struct iommu_device *iommu_dev,
+ void *arg)
+{
+ struct device_domain_info *info;
+ struct context_entry *context;
+
+ info = dev_iommu_priv_get(dev);
+ if (!info)
+ return 0;
+
+ if (!dev_is_pci(dev) || !dev_iommu_preserved_state(dev))
+ goto out_unpreserved;
+
+ /*
+ * PRE use cases are not supported with Live Update and a preservation
+ * attempt on such domains returns an error. But Intel IOMMU driver
+ * enables PRE by default on all devices that support it. For preserved
+ * entries, the PRE needs to be disabled so preserved PCI devices do not
+ * generate PRQs, during kexec, as translations are kept enabled during
+ * live update. There is no need to disable these for DMA aliases.
+ */
+ if (sm_supported(info->iommu)) {
+ context = iommu_context_addr(info->iommu, info->bus, info->devfn, 0);
Nit: please add a brief comment explaining why locking is not needed at
this call site.
Will do in next revision.
+ if (context) {
+ context_clear_sm_pre(context);
For cache invalidation considerations when changing the PRE bit in a
present context entry, please follow the VT-d spec guidance (Table 28,
“Guidance to Software for Invalidations”).
Please see note above the regarding global invalidations.
+ __iommu_flush_cache(info->iommu, context, sizeof(*context));
+ }> + }
+
+ return 0;
+
+out_unpreserved:
+ if (dev_is_pci(dev))
+ pci_for_each_dma_alias(to_pci_dev(dev),
+ clear_unpreserved_alias_cb, info);
+ else
+ clear_unpreserved_context(info, info->bus, info->devfn);
+
+ return 0;
+}
+
+/**
+ * clear_unpreserved_context_entries() - Clear context entries for unpreserved devices
+ * @iommu: Target IOMMU
+ *
+ * Clear the context entries of unpreserved devices during shutdown before kexec.
+ */
+void clear_unpreserved_context_entries(struct intel_iommu *iommu)
+{
+ struct iommu_dev_iter iter = {
+ .fn = clear_unpreserve_context_entry_fn,
+ .iommu = &iommu->iommu,
+ .arg = NULL,
+
+ };
+
+ /*
+ * Clear context entries for unpreserved devices.
+ *
+ * Note that the error can be ignored as the iterator function does not
+ * fail.
+ */
+ iommu_for_each_dev(&iter);
+
+ /* Clear reference to unpreserved context tables */
+ clear_unpreserved_context_root_entries(iommu,
+ iommu_preserved_state(&iommu->iommu));
+
+ /*
+ * Some devices might not have teardown/detached properly depending on
+ * whether a proper device remove is done before kexec is triggered.
+ * Also unpreserved context tables and entries are removed during
+ * shutdown. So issue global invalidations to remove references to
+ * unpreserved tables and entries.
+ */
+ iommu->flush.flush_context(iommu, 0, 0, 0, DMA_CCMD_GLOBAL_INVL);
+ if (sm_supported(iommu))
+ qi_flush_pasid_cache(iommu, 0, QI_PC_GLOBAL, 0);
+ iommu->flush.flush_iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH);
Global invalidations are issued here after doing all the clear work.
+}
+
static void unpreserve_iommu_context_tables(struct intel_iommu *iommu,
struct iommu_hw_ser *ser)
{
Thanks,
baolu
Thanks for looking at this.
Sami