Skip to content

Commit ba949f4

Browse files
LuBaolujoergroedel
authored andcommitted
iommu/vt-d: Refactor iommu information of each domain
When a DMA domain is attached to a device, it needs to allocate a domain ID from its IOMMU. Currently, the domain ID information is stored in two static arrays embedded in the domain structure. This can lead to memory waste when the driver is running on a small platform. This optimizes these static arrays by replacing them with an xarray and consuming memory on demand. Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com> Reviewed-by: Kevin Tian <kevin.tian@intel.com> Reviewed-by: Steve Wahl <steve.wahl@hpe.com> Link: https://lore.kernel.org/r/20220702015610.2849494-4-baolu.lu@linux.intel.com Signed-off-by: Joerg Roedel <jroedel@suse.de>
1 parent 913432f commit ba949f4

4 files changed

Lines changed: 93 additions & 63 deletions

File tree

drivers/iommu/intel/iommu.c

Lines changed: 71 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -254,10 +254,6 @@ static inline void context_clear_entry(struct context_entry *context)
254254
static struct dmar_domain *si_domain;
255255
static int hw_pass_through = 1;
256256

257-
#define for_each_domain_iommu(idx, domain) \
258-
for (idx = 0; idx < g_num_of_iommus; idx++) \
259-
if (domain->iommu_refcnt[idx])
260-
261257
struct dmar_rmrr_unit {
262258
struct list_head list; /* list of rmrr units */
263259
struct acpi_dmar_header *hdr; /* ACPI header */
@@ -453,16 +449,16 @@ static inline bool iommu_paging_structure_coherency(struct intel_iommu *iommu)
453449

454450
static void domain_update_iommu_coherency(struct dmar_domain *domain)
455451
{
452+
struct iommu_domain_info *info;
456453
struct dmar_drhd_unit *drhd;
457454
struct intel_iommu *iommu;
458455
bool found = false;
459-
int i;
456+
unsigned long i;
460457

461458
domain->iommu_coherency = true;
462-
463-
for_each_domain_iommu(i, domain) {
459+
xa_for_each(&domain->iommu_array, i, info) {
464460
found = true;
465-
if (!iommu_paging_structure_coherency(g_iommus[i])) {
461+
if (!iommu_paging_structure_coherency(info->iommu)) {
466462
domain->iommu_coherency = false;
467463
break;
468464
}
@@ -1495,7 +1491,7 @@ static void iommu_flush_iotlb_psi(struct intel_iommu *iommu,
14951491
unsigned int aligned_pages = __roundup_pow_of_two(pages);
14961492
unsigned int mask = ilog2(aligned_pages);
14971493
uint64_t addr = (uint64_t)pfn << VTD_PAGE_SHIFT;
1498-
u16 did = domain->iommu_did[iommu->seq_id];
1494+
u16 did = domain_id_iommu(domain, iommu);
14991495

15001496
BUG_ON(pages == 0);
15011497

@@ -1565,11 +1561,12 @@ static inline void __mapping_notify_one(struct intel_iommu *iommu,
15651561
static void intel_flush_iotlb_all(struct iommu_domain *domain)
15661562
{
15671563
struct dmar_domain *dmar_domain = to_dmar_domain(domain);
1568-
int idx;
1564+
struct iommu_domain_info *info;
1565+
unsigned long idx;
15691566

1570-
for_each_domain_iommu(idx, dmar_domain) {
1571-
struct intel_iommu *iommu = g_iommus[idx];
1572-
u16 did = dmar_domain->iommu_did[iommu->seq_id];
1567+
xa_for_each(&dmar_domain->iommu_array, idx, info) {
1568+
struct intel_iommu *iommu = info->iommu;
1569+
u16 did = domain_id_iommu(dmar_domain, iommu);
15731570

15741571
if (domain_use_first_level(dmar_domain))
15751572
qi_flush_piotlb(iommu, did, PASID_RID2PASID, 0, -1, 0);
@@ -1745,52 +1742,74 @@ static struct dmar_domain *alloc_domain(unsigned int type)
17451742
domain->has_iotlb_device = false;
17461743
INIT_LIST_HEAD(&domain->devices);
17471744
spin_lock_init(&domain->lock);
1745+
xa_init(&domain->iommu_array);
17481746

17491747
return domain;
17501748
}
17511749

17521750
static int domain_attach_iommu(struct dmar_domain *domain,
17531751
struct intel_iommu *iommu)
17541752
{
1753+
struct iommu_domain_info *info, *curr;
17551754
unsigned long ndomains;
1756-
int num, ret = 0;
1755+
int num, ret = -ENOSPC;
1756+
1757+
info = kzalloc(sizeof(*info), GFP_KERNEL);
1758+
if (!info)
1759+
return -ENOMEM;
17571760

17581761
spin_lock(&iommu->lock);
1759-
domain->iommu_refcnt[iommu->seq_id] += 1;
1760-
if (domain->iommu_refcnt[iommu->seq_id] == 1) {
1761-
ndomains = cap_ndoms(iommu->cap);
1762-
num = find_first_zero_bit(iommu->domain_ids, ndomains);
1763-
1764-
if (num >= ndomains) {
1765-
pr_err("%s: No free domain ids\n", iommu->name);
1766-
domain->iommu_refcnt[iommu->seq_id] -= 1;
1767-
ret = -ENOSPC;
1768-
goto out_unlock;
1769-
}
1762+
curr = xa_load(&domain->iommu_array, iommu->seq_id);
1763+
if (curr) {
1764+
curr->refcnt++;
1765+
spin_unlock(&iommu->lock);
1766+
kfree(info);
1767+
return 0;
1768+
}
17701769

1771-
set_bit(num, iommu->domain_ids);
1772-
domain->iommu_did[iommu->seq_id] = num;
1773-
domain->nid = iommu->node;
1774-
domain_update_iommu_cap(domain);
1770+
ndomains = cap_ndoms(iommu->cap);
1771+
num = find_first_zero_bit(iommu->domain_ids, ndomains);
1772+
if (num >= ndomains) {
1773+
pr_err("%s: No free domain ids\n", iommu->name);
1774+
goto err_unlock;
17751775
}
17761776

1777-
out_unlock:
1777+
set_bit(num, iommu->domain_ids);
1778+
info->refcnt = 1;
1779+
info->did = num;
1780+
info->iommu = iommu;
1781+
curr = xa_cmpxchg(&domain->iommu_array, iommu->seq_id,
1782+
NULL, info, GFP_ATOMIC);
1783+
if (curr) {
1784+
ret = xa_err(curr) ? : -EBUSY;
1785+
goto err_clear;
1786+
}
1787+
domain_update_iommu_cap(domain);
1788+
17781789
spin_unlock(&iommu->lock);
1790+
return 0;
1791+
1792+
err_clear:
1793+
clear_bit(info->did, iommu->domain_ids);
1794+
err_unlock:
1795+
spin_unlock(&iommu->lock);
1796+
kfree(info);
17791797
return ret;
17801798
}
17811799

17821800
static void domain_detach_iommu(struct dmar_domain *domain,
17831801
struct intel_iommu *iommu)
17841802
{
1785-
int num;
1803+
struct iommu_domain_info *info;
17861804

17871805
spin_lock(&iommu->lock);
1788-
domain->iommu_refcnt[iommu->seq_id] -= 1;
1789-
if (domain->iommu_refcnt[iommu->seq_id] == 0) {
1790-
num = domain->iommu_did[iommu->seq_id];
1791-
clear_bit(num, iommu->domain_ids);
1806+
info = xa_load(&domain->iommu_array, iommu->seq_id);
1807+
if (--info->refcnt == 0) {
1808+
clear_bit(info->did, iommu->domain_ids);
1809+
xa_erase(&domain->iommu_array, iommu->seq_id);
1810+
domain->nid = NUMA_NO_NODE;
17921811
domain_update_iommu_cap(domain);
1793-
domain->iommu_did[iommu->seq_id] = 0;
1812+
kfree(info);
17941813
}
17951814
spin_unlock(&iommu->lock);
17961815
}
@@ -1880,7 +1899,7 @@ static int domain_context_mapping_one(struct dmar_domain *domain,
18801899
{
18811900
struct device_domain_info *info =
18821901
iommu_support_dev_iotlb(domain, iommu, bus, devfn);
1883-
u16 did = domain->iommu_did[iommu->seq_id];
1902+
u16 did = domain_id_iommu(domain, iommu);
18841903
int translation = CONTEXT_TT_MULTI_LEVEL;
18851904
struct context_entry *context;
18861905
int ret;
@@ -2129,8 +2148,9 @@ static void switch_to_super_page(struct dmar_domain *domain,
21292148
unsigned long end_pfn, int level)
21302149
{
21312150
unsigned long lvl_pages = lvl_to_nr_pages(level);
2151+
struct iommu_domain_info *info;
21322152
struct dma_pte *pte = NULL;
2133-
int i;
2153+
unsigned long i;
21342154

21352155
while (start_pfn <= end_pfn) {
21362156
if (!pte)
@@ -2141,8 +2161,8 @@ static void switch_to_super_page(struct dmar_domain *domain,
21412161
start_pfn + lvl_pages - 1,
21422162
level + 1);
21432163

2144-
for_each_domain_iommu(i, domain)
2145-
iommu_flush_iotlb_psi(g_iommus[i], domain,
2164+
xa_for_each(&domain->iommu_array, i, info)
2165+
iommu_flush_iotlb_psi(info->iommu, domain,
21462166
start_pfn, lvl_pages,
21472167
0, 0);
21482168
}
@@ -2272,7 +2292,7 @@ static void domain_context_clear_one(struct device_domain_info *info, u8 bus, u8
22722292
if (hw_pass_through && domain_type_is_si(info->domain))
22732293
did_old = FLPT_DEFAULT_DID;
22742294
else
2275-
did_old = info->domain->iommu_did[iommu->seq_id];
2295+
did_old = domain_id_iommu(info->domain, iommu);
22762296
} else {
22772297
did_old = context_domain_id(context);
22782298
}
@@ -2330,7 +2350,7 @@ static int domain_setup_first_level(struct intel_iommu *iommu,
23302350
flags |= PASID_FLAG_PAGE_SNOOP;
23312351

23322352
return intel_pasid_setup_first_level(iommu, dev, (pgd_t *)pgd, pasid,
2333-
domain->iommu_did[iommu->seq_id],
2353+
domain_id_iommu(domain, iommu),
23342354
flags);
23352355
}
23362356

@@ -4368,15 +4388,16 @@ static void intel_iommu_tlb_sync(struct iommu_domain *domain,
43684388
struct dmar_domain *dmar_domain = to_dmar_domain(domain);
43694389
unsigned long iova_pfn = IOVA_PFN(gather->start);
43704390
size_t size = gather->end - gather->start;
4391+
struct iommu_domain_info *info;
43714392
unsigned long start_pfn;
43724393
unsigned long nrpages;
4373-
int iommu_id;
4394+
unsigned long i;
43744395

43754396
nrpages = aligned_nrpages(gather->start, size);
43764397
start_pfn = mm_to_dma_pfn(iova_pfn);
43774398

4378-
for_each_domain_iommu(iommu_id, dmar_domain)
4379-
iommu_flush_iotlb_psi(g_iommus[iommu_id], dmar_domain,
4399+
xa_for_each(&dmar_domain->iommu_array, i, info)
4400+
iommu_flush_iotlb_psi(info->iommu, dmar_domain,
43804401
start_pfn, nrpages,
43814402
list_empty(&gather->freelist), 0);
43824403

@@ -4620,7 +4641,7 @@ int intel_iommu_enable_pasid(struct intel_iommu *iommu, struct device *dev)
46204641
context[0].lo = ctx_lo;
46214642
wmb();
46224643
iommu->flush.flush_context(iommu,
4623-
domain->iommu_did[iommu->seq_id],
4644+
domain_id_iommu(domain, iommu),
46244645
PCI_DEVID(info->bus, info->devfn),
46254646
DMA_CCMD_MASK_NOBIT,
46264647
DMA_CCMD_DEVICE_INVL);
@@ -4757,13 +4778,11 @@ static void intel_iommu_iotlb_sync_map(struct iommu_domain *domain,
47574778
struct dmar_domain *dmar_domain = to_dmar_domain(domain);
47584779
unsigned long pages = aligned_nrpages(iova, size);
47594780
unsigned long pfn = iova >> VTD_PAGE_SHIFT;
4760-
struct intel_iommu *iommu;
4761-
int iommu_id;
4781+
struct iommu_domain_info *info;
4782+
unsigned long i;
47624783

4763-
for_each_domain_iommu(iommu_id, dmar_domain) {
4764-
iommu = g_iommus[iommu_id];
4765-
__mapping_notify_one(iommu, dmar_domain, pfn, pages);
4766-
}
4784+
xa_for_each(&dmar_domain->iommu_array, i, info)
4785+
__mapping_notify_one(info->iommu, dmar_domain, pfn, pages);
47674786
}
47684787

47694788
const struct iommu_ops intel_iommu_ops = {

drivers/iommu/intel/iommu.h

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <linux/dmar.h>
2222
#include <linux/ioasid.h>
2323
#include <linux/bitfield.h>
24+
#include <linux/xarray.h>
2425

2526
#include <asm/cacheflush.h>
2627
#include <asm/iommu.h>
@@ -524,17 +525,17 @@ struct context_entry {
524525
*/
525526
#define DOMAIN_FLAG_USE_FIRST_LEVEL BIT(1)
526527

527-
struct dmar_domain {
528-
int nid; /* node id */
529-
530-
unsigned int iommu_refcnt[DMAR_UNITS_SUPPORTED];
531-
/* Refcount of devices per iommu */
532-
533-
534-
u16 iommu_did[DMAR_UNITS_SUPPORTED];
535-
/* Domain ids per IOMMU. Use u16 since
528+
struct iommu_domain_info {
529+
struct intel_iommu *iommu;
530+
unsigned int refcnt; /* Refcount of devices per iommu */
531+
u16 did; /* Domain ids per IOMMU. Use u16 since
536532
* domain ids are 16 bit wide according
537533
* to VT-d spec, section 9.3 */
534+
};
535+
536+
struct dmar_domain {
537+
int nid; /* node id */
538+
struct xarray iommu_array; /* Attached IOMMU array */
538539

539540
u8 has_iotlb_device: 1;
540541
u8 iommu_coherency: 1; /* indicate coherency of iommu access */
@@ -640,6 +641,16 @@ static inline struct dmar_domain *to_dmar_domain(struct iommu_domain *dom)
640641
return container_of(dom, struct dmar_domain, domain);
641642
}
642643

644+
/* Retrieve the domain ID which has allocated to the domain */
645+
static inline u16
646+
domain_id_iommu(struct dmar_domain *domain, struct intel_iommu *iommu)
647+
{
648+
struct iommu_domain_info *info =
649+
xa_load(&domain->iommu_array, iommu->seq_id);
650+
651+
return info->did;
652+
}
653+
643654
/*
644655
* 0: readable
645656
* 1: writable

drivers/iommu/intel/pasid.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -626,7 +626,7 @@ int intel_pasid_setup_second_level(struct intel_iommu *iommu,
626626
}
627627

628628
pgd_val = virt_to_phys(pgd);
629-
did = domain->iommu_did[iommu->seq_id];
629+
did = domain_id_iommu(domain, iommu);
630630

631631
spin_lock(&iommu->lock);
632632
pte = intel_pasid_get_entry(dev, pasid);

drivers/iommu/intel/svm.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -541,7 +541,7 @@ static void intel_svm_drain_prq(struct device *dev, u32 pasid)
541541
domain = info->domain;
542542
pdev = to_pci_dev(dev);
543543
sid = PCI_DEVID(info->bus, info->devfn);
544-
did = domain->iommu_did[iommu->seq_id];
544+
did = domain_id_iommu(domain, iommu);
545545
qdep = pci_ats_queue_depth(pdev);
546546

547547
/*

0 commit comments

Comments
 (0)