From: Samiullah Khawaja <skhawaja@google.com>
To: David Woodhouse <dwmw2@infradead.org>,
Lu Baolu <baolu.lu@linux.intel.com>,
Joerg Roedel <joro@8bytes.org>, Will Deacon <will@kernel.org>,
Pasha Tatashin <pasha.tatashin@soleen.com>,
Jason Gunthorpe <jgg@ziepe.ca>,
David Matlack <dmatlack@google.com>
Cc: Samiullah Khawaja <skhawaja@google.com>,
Robin Murphy <robin.murphy@arm.com>,
Pratyush Yadav <pratyush@kernel.org>,
Kevin Tian <kevin.tian@intel.com>,
Alex Williamson <alex@shazbot.org>,
Shuah Khan <shuah@kernel.org>,
iommu@lists.linux.dev, linux-kernel@vger.kernel.org,
kvm@vger.kernel.org, Saeed Mahameed <saeedm@nvidia.com>,
Adithya Jayachandran <ajayachandra@nvidia.com>,
Parav Pandit <parav@nvidia.com>,
Leon Romanovsky <leonro@nvidia.com>, William Tu <witu@nvidia.com>
Subject: [PATCH 1/3] iommu/vt-d: Allow replacing no_pasid iommu_domain
Date: Wed, 7 Jan 2026 20:17:58 +0000 [thread overview]
Message-ID: <20260107201800.2486137-2-skhawaja@google.com> (raw)
In-Reply-To: <20260107201800.2486137-1-skhawaja@google.com>
Intel IOMMU driver already supports replacing IOMMU domains attachments
with PASIDs. Add support for replacing a domain attached with no_pasid.
This includes replacing domains in legacy mode.
Signed-off-by: Samiullah Khawaja <skhawaja@google.com>
---
drivers/iommu/intel/iommu.c | 107 ++++++++++++++++++++++++++----------
1 file changed, 77 insertions(+), 30 deletions(-)
diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
index 134302fbcd92..c0e359fd3ee1 100644
--- a/drivers/iommu/intel/iommu.c
+++ b/drivers/iommu/intel/iommu.c
@@ -1140,6 +1140,7 @@ static void context_present_cache_flush(struct intel_iommu *iommu, u16 did,
}
static int domain_context_mapping_one(struct dmar_domain *domain,
+ struct dmar_domain *old_domain,
struct intel_iommu *iommu,
u8 bus, u8 devfn)
{
@@ -1148,7 +1149,8 @@ static int domain_context_mapping_one(struct dmar_domain *domain,
u16 did = domain_id_iommu(domain, iommu);
int translation = CONTEXT_TT_MULTI_LEVEL;
struct pt_iommu_vtdss_hw_info pt_info;
- struct context_entry *context;
+ struct context_entry *context, new_context;
+ u16 did_old;
int ret;
if (WARN_ON(!intel_domain_is_ss_paging(domain)))
@@ -1166,26 +1168,44 @@ static int domain_context_mapping_one(struct dmar_domain *domain,
goto out_unlock;
ret = 0;
- if (context_present(context) && !context_copied(iommu, bus, devfn))
+ if (!old_domain && (context_present(context) && !context_copied(iommu, bus, devfn)))
goto out_unlock;
+ if (old_domain) {
+ did_old = context_domain_id(context);
+ WARN_ON(did_old != domain_id_iommu(old_domain, iommu));
+ }
+
copied_context_tear_down(iommu, context, bus, devfn);
- context_clear_entry(context);
- context_set_domain_id(context, did);
+ context_set_domain_id(&new_context, did);
if (info && info->ats_supported)
translation = CONTEXT_TT_DEV_IOTLB;
else
translation = CONTEXT_TT_MULTI_LEVEL;
- context_set_address_root(context, pt_info.ssptptr);
- context_set_address_width(context, pt_info.aw);
- context_set_translation_type(context, translation);
- context_set_fault_enable(context);
- context_set_present(context);
+ context_set_address_root(&new_context, pt_info.ssptptr);
+ context_set_address_width(&new_context, pt_info.aw);
+ context_set_translation_type(&new_context, translation);
+ context_set_fault_enable(&new_context);
+ context_set_present(&new_context);
+
+ *context = new_context;
if (!ecap_coherent(iommu->ecap))
clflush_cache_range(context, sizeof(*context));
- context_present_cache_flush(iommu, did, bus, devfn);
+
+ /*
+ * Spec 6.5.3.3, changing a present context entry requires,
+ * - IOTLB invalidation for each effected Domain.
+ * - Issue Device IOTLB invalidation for function.
+ */
+ if (old_domain) {
+ intel_context_flush_no_pasid(info, context, did);
+ intel_context_flush_no_pasid(info, context, did_old);
+ } else {
+ context_present_cache_flush(iommu, did, bus, devfn);
+ }
+
ret = 0;
out_unlock:
@@ -1194,30 +1214,39 @@ static int domain_context_mapping_one(struct dmar_domain *domain,
return ret;
}
+struct domain_context_mapping_data {
+ struct dmar_domain *domain;
+ struct dmar_domain *old_domain;
+};
+
static int domain_context_mapping_cb(struct pci_dev *pdev,
u16 alias, void *opaque)
{
struct device_domain_info *info = dev_iommu_priv_get(&pdev->dev);
struct intel_iommu *iommu = info->iommu;
- struct dmar_domain *domain = opaque;
+ struct domain_context_mapping_data *data = opaque;
- return domain_context_mapping_one(domain, iommu,
+ return domain_context_mapping_one(data->domain, data->old_domain, iommu,
PCI_BUS_NUM(alias), alias & 0xff);
}
static int
-domain_context_mapping(struct dmar_domain *domain, struct device *dev)
+domain_context_mapping(struct dmar_domain *domain,
+ struct dmar_domain *old_domain, struct device *dev)
{
struct device_domain_info *info = dev_iommu_priv_get(dev);
struct intel_iommu *iommu = info->iommu;
u8 bus = info->bus, devfn = info->devfn;
+ struct domain_context_mapping_data data;
int ret;
if (!dev_is_pci(dev))
- return domain_context_mapping_one(domain, iommu, bus, devfn);
+ return domain_context_mapping_one(domain, old_domain, iommu, bus, devfn);
+ data.domain = domain;
+ data.old_domain = old_domain;
ret = pci_for_each_dma_alias(to_pci_dev(dev),
- domain_context_mapping_cb, domain);
+ domain_context_mapping_cb, &data);
if (ret)
return ret;
@@ -1309,18 +1338,28 @@ static int domain_setup_first_level(struct intel_iommu *iommu,
pt_info.gcr3_pt, flags, old);
}
-static int dmar_domain_attach_device(struct dmar_domain *domain,
- struct device *dev)
+static int device_replace_dmar_domain(struct dmar_domain *domain,
+ struct dmar_domain *old_domain,
+ struct device *dev)
{
struct device_domain_info *info = dev_iommu_priv_get(dev);
struct intel_iommu *iommu = info->iommu;
unsigned long flags;
int ret;
+ if (old_domain && dev_is_real_dma_subdevice(dev))
+ return -EOPNOTSUPP;
+
ret = domain_attach_iommu(domain, iommu);
if (ret)
return ret;
+ if (old_domain) {
+ spin_lock_irqsave(&info->domain->lock, flags);
+ list_del(&info->link);
+ spin_unlock_irqrestore(&info->domain->lock, flags);
+ }
+
info->domain = domain;
info->domain_attached = true;
spin_lock_irqsave(&domain->lock, flags);
@@ -1331,27 +1370,27 @@ static int dmar_domain_attach_device(struct dmar_domain *domain,
return 0;
if (!sm_supported(iommu))
- ret = domain_context_mapping(domain, dev);
+ ret = domain_context_mapping(domain, old_domain, dev);
else if (intel_domain_is_fs_paging(domain))
ret = domain_setup_first_level(iommu, domain, dev,
- IOMMU_NO_PASID, NULL);
+ IOMMU_NO_PASID, &old_domain->domain);
else if (intel_domain_is_ss_paging(domain))
ret = domain_setup_second_level(iommu, domain, dev,
- IOMMU_NO_PASID, NULL);
+ IOMMU_NO_PASID, &old_domain->domain);
else if (WARN_ON(true))
ret = -EINVAL;
- if (ret)
- goto out_block_translation;
+ if (!ret)
+ ret = cache_tag_assign_domain(domain, dev, IOMMU_NO_PASID);
- ret = cache_tag_assign_domain(domain, dev, IOMMU_NO_PASID);
if (ret)
- goto out_block_translation;
+ device_block_translation(dev);
- return 0;
+ if (old_domain) {
+ cache_tag_unassign_domain(old_domain, dev, IOMMU_NO_PASID);
+ domain_detach_iommu(old_domain, iommu);
+ }
-out_block_translation:
- device_block_translation(dev);
return ret;
}
@@ -3127,19 +3166,27 @@ static int intel_iommu_attach_device(struct iommu_domain *domain,
struct device *dev,
struct iommu_domain *old)
{
+ struct device_domain_info *info = dev_iommu_priv_get(dev);
int ret;
- device_block_translation(dev);
+ if (dev_is_real_dma_subdevice(dev) ||
+ domain->type != __IOMMU_DOMAIN_PAGING ||
+ !info->domain || &info->domain->domain != old)
+ old = NULL;
+
+ if (!old)
+ device_block_translation(dev);
ret = paging_domain_compatible(domain, dev);
if (ret)
return ret;
- ret = iopf_for_domain_set(domain, dev);
+ ret = iopf_for_domain_replace(domain, old, dev);
if (ret)
return ret;
- ret = dmar_domain_attach_device(to_dmar_domain(domain), dev);
+ ret = device_replace_dmar_domain(to_dmar_domain(domain),
+ old ? to_dmar_domain(old) : NULL, dev);
if (ret)
iopf_for_domain_remove(domain, dev);
--
2.52.0.351.gbe84eed79e-goog
next prev parent reply other threads:[~2026-01-07 20:18 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-01-07 20:17 [PATCH 0/3] iommu/vt-d: Add support to hitless replace IOMMU domain Samiullah Khawaja
2026-01-07 20:17 ` Samiullah Khawaja [this message]
2026-01-07 20:17 ` [PATCH 2/3] vfio: selftests: Add support of creating iommus from iommufd Samiullah Khawaja
2026-01-08 0:29 ` David Matlack
2026-01-09 21:39 ` Samiullah Khawaja
2026-01-07 20:18 ` [PATCH 3/3] vfio: selftests: Add iommufd hwpt replace test Samiullah Khawaja
2026-01-09 0:42 ` kernel test robot
2026-01-07 20:28 ` [PATCH 0/3] iommu/vt-d: Add support to hitless replace IOMMU domain Jason Gunthorpe
2026-01-07 20:46 ` Jason Gunthorpe
2026-01-09 20:06 ` Samiullah Khawaja
2026-01-11 22:14 ` Jason Gunthorpe
2026-01-12 6:57 ` Baolu Lu
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260107201800.2486137-2-skhawaja@google.com \
--to=skhawaja@google.com \
--cc=ajayachandra@nvidia.com \
--cc=alex@shazbot.org \
--cc=baolu.lu@linux.intel.com \
--cc=dmatlack@google.com \
--cc=dwmw2@infradead.org \
--cc=iommu@lists.linux.dev \
--cc=jgg@ziepe.ca \
--cc=joro@8bytes.org \
--cc=kevin.tian@intel.com \
--cc=kvm@vger.kernel.org \
--cc=leonro@nvidia.com \
--cc=linux-kernel@vger.kernel.org \
--cc=parav@nvidia.com \
--cc=pasha.tatashin@soleen.com \
--cc=pratyush@kernel.org \
--cc=robin.murphy@arm.com \
--cc=saeedm@nvidia.com \
--cc=shuah@kernel.org \
--cc=will@kernel.org \
--cc=witu@nvidia.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox