Genericise the IOVA allocator
Subsystems (2)
Robin Murphy
maintainercover Genericise the IOVA allocator
Hi all,
Here's an update of my previous RFC[1] in preparation for hooking the IOVA allocator up to the arm64 DMA mapping API, rebased onto 3.19-rc3.
I tried rebasing patches 3 and 4 onto Sakari's RFC series[2] (the merge conflict is pretty trivial), however I found that series applied to rc3 causes a build error in intel-iommu.c. Thus for now I've left in my simpler patches 1 and 2 for breaking out the library. Hopefully we can reach some consensus on that.
Tested on arm64 (DMA mapping series coming soon), and compile-tested for x86_64_defconfig.
Changes since RFC:
Patch 1: Use a proper Kconfig symbol rather than a hack Patch 4: sanity check for powers of two also, and clarify the comment
[1]:http://thread.gmane.org/gmane.linux.kernel.iommu/7480 [2]:http://thread.gmane.org/gmane.linux.kernel.iommu/7436
Robin Murphy (4):
iommu: allow building iova.c independently
iommu: consolidate IOVA allocator code
iommu: make IOVA domain low limit flexible
iommu: make IOVA domain page size explicit
drivers/iommu/Kconfig | 4 ++++ drivers/iommu/Makefile | 3 ++- drivers/iommu/intel-iommu.c | 45 ++++++++++---------------------------- drivers/iommu/iova.c | 53 +++++++++++++++++++++++++++++++++++++++++---- include/linux/iova.h | 41 +++++++++++++++++++++++++++++++---- 5 files changed, 103 insertions(+), 43 deletions(-)
Signature · 1 line
1.9.1
Robin Murphy
maintainerPATCH 1/4 iommu: allow building iova.c independently
In preparation for sharing the IOVA allocator, split it out under its own Kconfig symbol.
--- drivers/iommu/Kconfig | 4 ++++ drivers/iommu/Makefile | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-)
drivers/iommu/Kconfig +4 -0
@@ -13,6 +13,9 @@ menuconfig IOMMU_SUPPORT if IOMMU_SUPPORT +config IOMMU_IOVA + bool + config OF_IOMMU def_bool y depends on OF && IOMMU_API
@@ -91,6 +94,7 @@ config INTEL_IOMMU bool "Support for Intel IOMMU using DMA Remapping Devices" depends on PCI_MSI && ACPI && (X86 || IA64_GENERIC) select IOMMU_API + select IOMMU_IOVA select DMAR_TABLE help DMA remapping (DMAR) devices support enables independent address
drivers/iommu/Makefile +2 -1
@@ -1,13 +1,14 @@ obj-$(CONFIG_IOMMU_API) += iommu.o obj-$(CONFIG_IOMMU_API) += iommu-traces.o obj-$(CONFIG_IOMMU_API) += iommu-sysfs.o +obj-$(CONFIG_IOMMU_IOVA) += iova.o obj-$(CONFIG_OF_IOMMU) += of_iommu.o obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o msm_iommu_dev.o obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o obj-$(CONFIG_AMD_IOMMU_V2) += amd_iommu_v2.o obj-$(CONFIG_ARM_SMMU) += arm-smmu.o obj-$(CONFIG_DMAR_TABLE) += dmar.o -obj-$(CONFIG_INTEL_IOMMU) += iova.o intel-iommu.o +obj-$(CONFIG_INTEL_IOMMU) += intel-iommu.o obj-$(CONFIG_IPMMU_VMSA) += ipmmu-vmsa.o obj-$(CONFIG_IRQ_REMAP) += intel_irq_remapping.o irq_remapping.o obj-$(CONFIG_OMAP_IOMMU) += omap-iommu.o
Signature · 1 line
1.9.1
Robin Murphy
maintainerPATCH 2/4 iommu: consolidate IOVA allocator code
In reply to Robin MurphyIn order to share the IOVA allocator with other architectures, break the unnecssary dependency on the Intel IOMMU driver and move the remaining IOVA internals to iova.c
--- drivers/iommu/intel-iommu.c | 33 ++------------------------------- drivers/iommu/iova.c | 35 +++++++++++++++++++++++++++++++++++ include/linux/iova.h | 3 +++ 3 files changed, 40 insertions(+), 31 deletions(-)
drivers/iommu/intel-iommu.c +2 -31
@@ -485,7 +485,6 @@ __setup("intel_iommu=", intel_iommu_setup);static struct kmem_cache *iommu_domain_cache;static struct kmem_cache *iommu_devinfo_cache; -static struct kmem_cache *iommu_iova_cache;static inline void *alloc_pgtable_page (int node) {
@@ -523,16 +522,6 @@ static inline void free_devinfo_mem(void *vaddr)kmem_cache_free (iommu_devinfo_cache, vaddr); } -struct iova *alloc_iova_mem (void ) -{ -return kmem_cache_alloc (iommu_iova_cache,GFP_ATOMIC ); -} - -void free_iova_mem (struct iova *iova) -{ -kmem_cache_free (iommu_iova_cache, iova); -} -static inline int domain_type_is_vm (struct dmar_domain *domain) {return domain->flags &DOMAIN_FLAG_VIRTUAL_MACHINE ;
@@ -3427,23 +3416,6 @@ static inline int iommu_devinfo_cache_init(void)return ret; } -static inline int iommu_iova_cache_init (void ) -{ -int ret =0 ; - - iommu_iova_cache =kmem_cache_create ("iommu_iova" , -sizeof (struct iova ), -0 , -SLAB_HWCACHE_ALIGN , -NULL ); -if (!iommu_iova_cache) { -printk (KERN_ERR "Couldn't create iova cache\n" ); - ret = -ENOMEM ; - } - -return ret; -} -static int __init iommu_init_mempool (void ) {int ret;
@@ -3461,7 +3433,7 @@ static int __init iommu_init_mempool(void)kmem_cache_destroy (iommu_domain_cache); domain_error: -kmem_cache_destroy (iommu_iova_cache); +iommu_iova_cache_destroy ();return -ENOMEM ; }
@@ -3470,8 +3442,7 @@ static void __init iommu_exit_mempool(void) {kmem_cache_destroy (iommu_devinfo_cache);kmem_cache_destroy (iommu_domain_cache); -kmem_cache_destroy (iommu_iova_cache); - +iommu_iova_cache_destroy (); }static void quirk_ioat_snb_local_iommu (struct pci_dev *pdev)
drivers/iommu/iova.c +35 -0
@@ -18,6 +18,41 @@*/ #include <linux/iova.h> +#include <linux/slab.h> + +static struct kmem_cache *iommu_iova_cache; + +int iommu_iova_cache_init (void ) +{ +int ret =0 ; + + iommu_iova_cache =kmem_cache_create ("iommu_iova" , +sizeof (struct iova ), +0 , +SLAB_HWCACHE_ALIGN , +NULL ); +if (!iommu_iova_cache) { +pr_err ("Couldn't create iova cache\n" ); + ret = -ENOMEM ; + } + +return ret; +} + +void iommu_iova_cache_destroy (void ) +{ +kmem_cache_destroy (iommu_iova_cache); +} + +struct iova *alloc_iova_mem (void ) +{ +return kmem_cache_alloc (iommu_iova_cache,GFP_ATOMIC ); +} + +void free_iova_mem (struct iova *iova) +{ +kmem_cache_free (iommu_iova_cache, iova); +}void init_iova_domain (struct iova_domain *iovad,unsigned long pfn_32bit)
include/linux/iova.h +3 -0
@@ -39,6 +39,9 @@ static inline unsigned long iova_size(struct iova *iova)return iova->pfn_hi - iova->pfn_lo +1 ; } +int iommu_iova_cache_init (void ); +void iommu_iova_cache_destroy (void ); +struct iova *alloc_iova_mem (void );void free_iova_mem (struct iova *iova);void free_iova (struct iova_domain *iovad,unsigned long pfn);
Signature · 1 line
1.9.1
Robin Murphy
maintainerPATCH 3/4 iommu: make IOVA domain low limit flexible
In reply to Robin MurphyTo share the IOVA allocator with other architectures, it needs to accommodate more general aperture restrictions; move the lower limit from a compile-time constant to a runtime domain property to allow IOVA domains with different requirements to co-exist.
Also reword the slightly unclear description of alloc_iova since we're touching it anyway.
--- drivers/iommu/intel-iommu.c | 9 ++++++--- drivers/iommu/iova.c | 10 ++++++---- include/linux/iova.h | 7 +++---- 3 files changed, 15 insertions(+), 11 deletions(-)
drivers/iommu/intel-iommu.c +6 -3
@@ -71,6 +71,9 @@ __DOMAIN_MAX_PFN(gaw), (unsigned long)-1))#define DOMAIN_MAX_ADDR (gaw) (((uint64_t)__DOMAIN_MAX_PFN(gaw)) << VTD_PAGE_SHIFT) +/* IO virtual address start page frame number */ +#define IOVA_START_PFN (1) +#define IOVA_PFN (addr) ((addr) >> PAGE_SHIFT)#define DMA_32BIT_PFN IOVA_PFN(DMA_BIT_MASK(32))#define DMA_64BIT_PFN IOVA_PFN(DMA_BIT_MASK(64))
@@ -1632,7 +1635,7 @@ static int dmar_init_reserved_ranges(void)struct iova *iova;int i; -init_iova_domain (&reserved_iova_list,DMA_32BIT_PFN ); +init_iova_domain (&reserved_iova_list,IOVA_START_PFN ,DMA_32BIT_PFN );lockdep_set_class (&reserved_iova_list.iova_rbtree_lock, &reserved_rbtree_key);
@@ -1690,7 +1693,7 @@ static int domain_init(struct dmar_domain *domain, int guest_width)int adjust_width, agaw;unsigned long sagaw; -init_iova_domain (&domain->iovad,DMA_32BIT_PFN ); +init_iova_domain (&domain->iovad,IOVA_START_PFN ,DMA_32BIT_PFN );domain_reserve_special_ranges (domain);/* calculate AGAW */
@@ -4321,7 +4324,7 @@ static int md_domain_init(struct dmar_domain *domain, int guest_width) {int adjust_width; -init_iova_domain (&domain->iovad,DMA_32BIT_PFN ); +init_iova_domain (&domain->iovad,IOVA_START_PFN ,DMA_32BIT_PFN );domain_reserve_special_ranges (domain);/* calculate AGAW */
drivers/iommu/iova.c +6 -4
@@ -55,11 +55,13 @@ void free_iova_mem(struct iova *iova) }void -init_iova_domain (struct iova_domain *iovad,unsigned long pfn_32bit) +init_iova_domain (struct iova_domain *iovad,unsigned long start_pfn, +unsigned long pfn_32bit) {spin_lock_init (&iovad->iova_rbtree_lock); iovad->rbroot =RB_ROOT ; iovad->cached32_node =NULL ; + iovad->start_pfn = start_pfn; iovad->dma_32bit_pfn = pfn_32bit; }
@@ -162,7 +164,7 @@ move_left:if (!curr) {if (size_aligned) pad_size =iova_get_pad_size (size, limit_pfn); -if ((IOVA_START_PFN + size + pad_size) > limit_pfn) { +if ((iovad->start_pfn + size + pad_size) > limit_pfn) {spin_unlock_irqrestore (&iovad->iova_rbtree_lock, flags);return -ENOMEM ; }
@@ -237,8 +239,8 @@ iova_insert_rbtree(struct rb_root *root, struct iova *iova)* @size: - size of page frames to allocate * @limit_pfn: - max limit address * @size_aligned: - set if size_aligned address range is required -* This function allocates an iova in the range limit_pfn to IOVA_START_PFN -* looking from limit_pfn instead from IOVA_START_PFN. If the size_aligned +* This function allocates an iova in the range iovad->start_pfn to limit_pfn, +* searching top-down from limit_pfn to iovad->start_pfn. If the size_aligned * flag is set then the allocated address iova->pfn_lo will be naturally * aligned on roundup_power_of_two(size). */
include/linux/iova.h +3 -4
@@ -16,9 +16,6 @@#include <linux/rbtree.h> #include <linux/dma-mapping.h> -/* IO virtual address start page frame number */ -#define IOVA_START_PFN (1) -/* iova structure */ struct iova {struct rb_node node;
@@ -31,6 +28,7 @@ struct iova_domain {spinlock_t iova_rbtree_lock;/* Lock to protect update of rbtree */ struct rb_root rbroot;/* iova domain rbtree root */ struct rb_node *cached32_node;/* Save last alloced node */ +unsigned long start_pfn;/* Lower limit for this domain */ unsigned long dma_32bit_pfn; };
@@ -52,7 +50,8 @@ struct iova *alloc_iova(struct iova_domain *iovad, unsigned long size,struct iova *reserve_iova (struct iova_domain *iovad,unsigned long pfn_lo,unsigned long pfn_hi);void copy_reserved_iova (struct iova_domain *from,struct iova_domain *to); -void init_iova_domain (struct iova_domain *iovad,unsigned long pfn_32bit); +void init_iova_domain (struct iova_domain *iovad,unsigned long start_pfn, +unsigned long pfn_32bit);struct iova *find_iova (struct iova_domain *iovad,unsigned long pfn);void put_iova_domain (struct iova_domain *iovad); struct iova *split_and_remove_iova(struct iova_domain *iovad,
Signature · 1 line
1.9.1
Robin Murphy
maintainerPATCH 4/4 iommu: make IOVA domain page size explicit
In reply to Robin MurphySystems may contain heterogeneous IOMMUs supporting differing minimum page sizes, which may also not be common with the CPU page size. Thus it is practical to have an explicit notion of IOVA granularity to simplify handling of mapping and allocation constraints.
As an initial step, move the IOVA page granularity from an implicit compile-time constant to a per-domain property so we can make use of it in IOVA domain context at runtime. To keep the abstraction tidy, extend the little API of inline iova_* helpers to parallel some of the equivalent PAGE_* macros.
--- drivers/iommu/intel-iommu.c | 9 ++++++--- drivers/iommu/iova.c | 12 ++++++++++-- include/linux/iova.h | 35 +++++++++++++++++++++++++++++++++-- 3 files changed, 49 insertions(+), 7 deletions(-)
drivers/iommu/intel-iommu.c +6 -3
@@ -1635,7 +1635,8 @@ static int dmar_init_reserved_ranges(void)struct iova *iova;int i; -init_iova_domain (&reserved_iova_list,IOVA_START_PFN ,DMA_32BIT_PFN ); +init_iova_domain (&reserved_iova_list,VTD_PAGE_SIZE ,IOVA_START_PFN , +DMA_32BIT_PFN );lockdep_set_class (&reserved_iova_list.iova_rbtree_lock, &reserved_rbtree_key);
@@ -1693,7 +1694,8 @@ static int domain_init(struct dmar_domain *domain, int guest_width)int adjust_width, agaw;unsigned long sagaw; -init_iova_domain (&domain->iovad,IOVA_START_PFN ,DMA_32BIT_PFN ); +init_iova_domain (&domain->iovad,VTD_PAGE_SIZE ,IOVA_START_PFN , +DMA_32BIT_PFN );domain_reserve_special_ranges (domain);/* calculate AGAW */
@@ -4324,7 +4326,8 @@ static int md_domain_init(struct dmar_domain *domain, int guest_width) {int adjust_width; -init_iova_domain (&domain->iovad,IOVA_START_PFN ,DMA_32BIT_PFN ); +init_iova_domain (&domain->iovad,VTD_PAGE_SIZE ,IOVA_START_PFN , +DMA_32BIT_PFN );domain_reserve_special_ranges (domain);/* calculate AGAW */
drivers/iommu/iova.c +10 -2
@@ -55,12 +55,20 @@ void free_iova_mem(struct iova *iova) }void -init_iova_domain (struct iova_domain *iovad,unsigned long start_pfn, -unsigned long pfn_32bit) +init_iova_domain (struct iova_domain *iovad,unsigned long granule, +unsigned long start_pfn,unsigned long pfn_32bit) { +/* +* IOVA granularity will normally be equal to the smallest +* supported IOMMU page size; both *must* be capable of +* representing individual CPU pages exactly. +*/ +BUG_ON ((granule >PAGE_SIZE ) || !is_power_of_2 (granule)); +spin_lock_init (&iovad->iova_rbtree_lock); iovad->rbroot =RB_ROOT ; iovad->cached32_node =NULL ; + iovad->granule = granule; iovad->start_pfn = start_pfn; iovad->dma_32bit_pfn = pfn_32bit; }
include/linux/iova.h +33 -2
@@ -28,6 +28,7 @@ struct iova_domain {spinlock_t iova_rbtree_lock;/* Lock to protect update of rbtree */ struct rb_root rbroot;/* iova domain rbtree root */ struct rb_node *cached32_node;/* Save last alloced node */ +unsigned long granule;/* pfn granularity for this domain */ unsigned long start_pfn;/* Lower limit for this domain */ unsigned long dma_32bit_pfn; };
@@ -37,6 +38,36 @@ static inline unsigned long iova_size(struct iova *iova)return iova->pfn_hi - iova->pfn_lo +1 ; } +static inline unsigned long iova_shift (struct iova_domain *iovad) +{ +return __ffs (iovad->granule); +} + +static inline unsigned long iova_mask (struct iova_domain *iovad) +{ +return iovad->granule -1 ; +} + +static inline size_t iova_offset (struct iova_domain *iovad,dma_addr_t iova) +{ +return iova &iova_mask (iovad); +} + +static inline size_t iova_align (struct iova_domain *iovad,size_t size) +{ +return ALIGN (size, iovad->granule); +} + +static inline dma_addr_t iova_dma_addr (struct iova_domain *iovad,struct iova *iova) +{ +return (dma_addr_t )iova->pfn_lo <<iova_shift (iovad); +} + +static inline unsigned long iova_pfn (struct iova_domain *iovad,dma_addr_t iova) +{ +return iova >>iova_shift (iovad); +} +int iommu_iova_cache_init (void );void iommu_iova_cache_destroy (void );
@@ -50,8 +81,8 @@ struct iova *alloc_iova(struct iova_domain *iovad, unsigned long size,struct iova *reserve_iova (struct iova_domain *iovad,unsigned long pfn_lo,unsigned long pfn_hi);void copy_reserved_iova (struct iova_domain *from,struct iova_domain *to); -void init_iova_domain (struct iova_domain *iovad,unsigned long start_pfn, -unsigned long pfn_32bit); +void init_iova_domain (struct iova_domain *iovad,unsigned long granule, +unsigned long start_pfn,unsigned long pfn_32bit);struct iova *find_iova (struct iova_domain *iovad,unsigned long pfn);void put_iova_domain (struct iova_domain *iovad); struct iova *split_and_remove_iova(struct iova_domain *iovad,
Signature · 1 line
1.9.1
cover Genericise the IOVA allocator
In reply to Robin MurphyQuoted 5 lines · Robin Murphy
Robin Murphy (4):
iommu: allow buildingiova.cindependently
iommu: consolidate IOVA allocator code
iommu: make IOVA domain low limit flexible
iommu: make IOVA domain page size explicit
Applied to core, thanks Robin.