第16章:IOMMU 输入输出内存管理单元
基于 Linux 6.12.38 源码
16.1 IOMMU 概述
16.1.1 什么是 IOMMU
IOMMU (Input/Output Memory Management Unit) 是一种硬件组件,用于管理设备对系统内存的访问。它类似于 CPU 的 MMU,但服务于 I/O 设备而非 CPU。
IOMMU 核心功能:
| 功能 |
描述 |
| DMA 地址转换 |
将设备看到的 IOVA (I/O Virtual Address) 转换为物理地址 |
| 内存隔离 |
防止恶意设备访问任意内存区域 |
| scatter-gather 支持 |
支持不连续物理内存的 DMA 操作 |
| 中断重映射 |
提供 MSI/MSI-X 中断重映射功能 |
16.1.2 IOMMU 与 CPU MMU 对比
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| ┌─────────────────────────────────────────────────────────────────┐ │ CPU MMU (内存管理单元) │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ CPU ──→ 虚拟地址 (VA) ──→ MMU ──→ 物理地址 (PA) ──→ 系统内存 │ │ │ │ - 进程地址空间隔离 │ │ - 页表管理 (PGD → PUD → PMD → PTE) │ │ - TLB 缓存 │ │ │ ├─────────────────────────────────────────────────────────────────┤ │ IOMMU (IO 内存管理单元) │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 设备 ──→ IOVA ──→ IOMMU ──→ 物理地址 (PA) ──→ 系统内存 │ │ │ │ - 设备 DMA 地址转换 │ │ - 设备隔离 (防止恶意 DMA) │ │ - IOTLB 缓存 │ │ - PASID 支持 (多个地址空间) │ │ │ └─────────────────────────────────────────────────────────────────┘
|
16.1.3 主要 IOMMU 硬件实现
| 硬件 |
描述 |
Linux 驱动位置 |
| Intel VT-d |
Intel x86 平台的 IOMMU (VT-x for I/O) |
drivers/iommu/intel/ |
| AMD-Vi |
AMD x86 平台的 IOMMU |
drivers/iommu/amd/ |
| ARM SMMU |
ARM 平台 SMMU v2/v3 |
drivers/iommu/arm/arm-smmu-v3/ |
| virtio-iommu |
虚拟化环境 IOMMU |
drivers/iommu/virtio-iommu.c |
16.2 IOMMU 核心数据结构
16.2.1 iommu_device 结构
位置: include/linux/iommu.h:682
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
struct iommu_device { struct list_head list; const struct iommu_ops *ops; struct fwnode_handle *fwnode; struct device *dev; struct iommu_group *singleton_group; u32 max_pasids; };
|
16.2.2 iommu_domain 结构
位置: include/linux/iommu.h:208
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
|
struct iommu_domain { unsigned type; const struct iommu_domain_ops *ops; const struct iommu_dirty_ops *dirty_ops; const struct iommu_ops *owner; unsigned long pgsize_bitmap; struct iommu_domain_geometry geometry; struct iommu_dma_cookie *iova_cookie; int (*iopf_handler)(struct iopf_group *); void *fault_data; union { struct { iommu_fault_handler_t handler; void *handler_token; }; struct { struct mm_struct *mm; int users; struct list_head next; }; }; };
|
16.2.3 iommu_domain_geometry
1 2 3 4 5 6 7 8 9 10 11
|
struct iommu_domain_geometry { dma_addr_t aperture_start; dma_addr_t aperture_end; bool force_aperture; };
|
16.2.4 iommu_group 结构
位置: drivers/iommu/iommu.c:47
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
struct iommu_group { struct kobject kobj; struct kobject *devices_kobj; struct list_head devices; struct xarray pasid_array; struct mutex mutex; void *iommu_data; void (*iommu_data_release)(void *iommu_data); char *name; int id; struct iommu_domain *default_domain; struct iommu_domain *blocking_domain; struct iommu_domain *domain; struct list_head entry; unsigned int owner_cnt; void *owner; };
|
16.2.5 dev_iommu 结构
位置: include/linux/iommu.h:732
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
struct dev_iommu { struct mutex lock; struct iommu_fault_param __rcu *fault_param; struct iommu_fwspec *fwspec; struct iommu_device *iommu_dev; void *priv; u32 max_pasids; u32 attach_deferred:1; u32 pci_32bit_workaround:1; u32 require_direct:1; u32 shadow_on_flush:1; };
|
16.2.6 iommu_mm_data 结构
位置: include/linux/iommu.h:1017
1 2 3 4 5 6 7 8 9
|
struct iommu_mm_data { u32 pasid; struct list_head sva_domains; };
|
16.3 IOMMU 域类型
16.3.1 域类型定义
位置: include/linux/iommu.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| #define __IOMMU_DOMAIN_PAGING (1U << 0) #define __IOMMU_DOMAIN_DMA_API (1U << 1) #define __IOMMU_DOMAIN_PT (1U << 2) #define __IOMMU_DOMAIN_DMA_FQ (1U << 3) #define __IOMMU_DOMAIN_SVA (1U << 4) #define __IOMMU_DOMAIN_PLATFORM (1U << 5) #define __IOMMU_DOMAIN_NESTED (1U << 6)
#define IOMMU_DOMAIN_BLOCKED (0U) #define IOMMU_DOMAIN_IDENTITY (__IOMMU_DOMAIN_PT) #define IOMMU_DOMAIN_UNMANAGED (__IOMMU_DOMAIN_PAGING) #define IOMMU_DOMAIN_DMA (__IOMMU_DOMAIN_PAGING | __IOMMU_DOMAIN_DMA_API) #define IOMMU_DOMAIN_DMA_FQ (__IOMMU_DOMAIN_PAGING | __IOMMU_DOMAIN_DMA_API | __IOMMU_DOMAIN_DMA_FQ) #define IOMMU_DOMAIN_SVA (__IOMMU_DOMAIN_SVA) #define IOMMU_DOMAIN_PLATFORM (__IOMMU_DOMAIN_PLATFORM) #define IOMMU_DOMAIN_NESTED (__IOMMU_DOMAIN_NESTED)
|
16.3.2 域类型特性对比
| 域类型 |
描述 |
地址转换 |
用途 |
| BLOCKED |
所有 DMA 被阻塞 |
N/A |
设备隔离 |
| IDENTITY |
1:1 恒等映射 |
IOVA = PA |
直通模式 |
| UNMANAGED |
用户管理映射 |
IOVA → PA |
虚拟化 |
| DMA |
内核 DMA API |
IOVA → PA |
通用设备 |
| DMA_FQ |
DMA + Flush Queue |
IOVA → PA |
高性能设备 |
| SVA |
共享进程地址 |
VA = IOVA → PA |
设备直接访问进程内存 |
| NESTED |
嵌套页表 |
GVA → GPA → PA |
虚拟化嵌套 |
16.4 IOMMU 域操作
16.4.1 iommu_ops 结构
位置: include/linux/iommu.h:559
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
|
struct iommu_ops { bool (*capable)(struct device *dev, enum iommu_cap); void *(*hw_info)(struct device *dev, u32 *length, u32 *type);
struct iommu_domain *(*domain_alloc)(unsigned iommu_domain_type); struct iommu_domain *(*domain_alloc_user)( struct device *dev, u32 flags, struct iommu_domain *parent, const struct iommu_user_data *user_data); struct iommu_domain *(*domain_alloc_paging)(struct device *dev); struct iommu_domain *(*domain_alloc_sva)(struct device *dev, struct mm_struct *mm);
struct iommu_device *(*probe_device)(struct device *dev); void (*release_device)(struct device *dev); void (*probe_finalize)(struct device *dev); struct iommu_group *(*device_group)(struct device *dev);
void (*get_resv_regions)(struct device *dev, struct list_head *list);
int (*dev_enable_feat)(struct device *dev, enum iommu_dev_features f); int (*dev_disable_feat)(struct device *dev, enum iommu_dev_features f);
void (*page_response)(struct device *dev, struct iopf_fault *evt, struct iommu_page_response *msg);
int (*def_domain_type)(struct device *dev);
void (*remove_dev_pasid)(struct device *dev, ioasid_t pasid, struct iommu_domain *domain);
const struct iommu_domain_ops *default_domain_ops; unsigned long pgsize_bitmap; struct module *owner; struct iommu_domain *identity_domain; struct iommu_domain *blocked_domain; struct iommu_domain *release_domain; struct iommu_domain *default_domain; u8 user_pasid_table:1; };
|
16.4.2 iommu_domain_ops 结构
位置: include/linux/iommu.h:642
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
|
struct iommu_domain_ops { int (*attach_dev)(struct iommu_domain *domain, struct device *dev); int (*set_dev_pasid)(struct iommu_domain *domain, struct device *dev, ioasid_t pasid);
int (*map_pages)(struct iommu_domain *domain, unsigned long iova, phys_addr_t paddr, size_t pgsize, size_t pgcount, int prot, gfp_t gfp, size_t *mapped); size_t (*unmap_pages)(struct iommu_domain *domain, unsigned long iova, size_t pgsize, size_t pgcount, struct iommu_iotlb_gather *iotlb_gather);
void (*flush_iotlb_all)(struct iommu_domain *domain); int (*iotlb_sync_map)(struct iommu_domain *domain, unsigned long iova, size_t size); void (*iotlb_sync)(struct iommu_domain *domain, struct iommu_iotlb_gather *iotlb_gather); int (*cache_invalidate_user)(struct iommu_domain *domain, struct iommu_user_data_array *array);
phys_addr_t (*iova_to_phys)(struct iommu_domain *domain, dma_addr_t iova);
bool (*enforce_cache_coherency)(struct iommu_domain *domain); int (*enable_nesting)(struct iommu_domain *domain); int (*set_pgtable_quirks)(struct iommu_domain *domain, unsigned long quirks);
void (*free)(struct iommu_domain *domain); };
|
16.4.3 IOMMU 核心操作函数
位置: include/linux/iommu.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| extern struct iommu_domain *iommu_domain_alloc(const struct bus_type *bus); struct iommu_domain *iommu_paging_domain_alloc(struct device *dev); extern void iommu_domain_free(struct iommu_domain *domain);
extern int iommu_attach_device(struct iommu_domain *domain, struct device *dev); extern void iommu_detach_device(struct iommu_domain *domain, struct device *dev); extern struct iommu_domain *iommu_get_domain_for_dev(struct device *dev); extern struct iommu_domain *iommu_get_dma_domain(struct device *dev);
extern int iommu_map(struct iommu_domain *domain, unsigned long iova, phys_addr_t paddr, size_t size, int prot, gfp_t gfp); extern size_t iommu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size); extern size_t iommu_unmap_fast(struct iommu_domain *domain, unsigned long iova, size_t size, struct iommu_iotlb_gather *iotlb_gather); extern ssize_t iommu_map_sg(struct iommu_domain *domain, unsigned long iova, struct scatterlist *sg, unsigned int nents, int prot, gfp_t gfp);
extern phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova);
extern void iommu_set_fault_handler(struct iommu_domain *domain, iommu_fault_handler_t handler, void *token); extern int report_iommu_fault(struct iommu_domain *domain, struct device *dev, unsigned long iova, int flags);
extern int iommu_attach_group(struct iommu_domain *domain, struct iommu_group *group); extern void iommu_detach_group(struct iommu_domain *domain, struct iommu_group *group); extern struct iommu_group *iommu_group_alloc(void); extern struct iommu_group *iommu_group_get(struct device *dev); extern void iommu_group_put(struct iommu_group *group); extern int iommu_group_id(struct iommu_group *group);
extern void iommu_get_resv_regions(struct device *dev, struct list_head *list); extern void iommu_put_resv_regions(struct device *dev, struct list_head *list);
|
16.4.4 IOMMU 保护标志
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #define IOMMU_READ (1 << 0) #define IOMMU_WRITE (1 << 1) #define IOMMU_CACHE (1 << 2) #define IOMMU_NOEXEC (1 << 3) #define IOMMU_MMIO (1 << 4) #define IOMMU_PRIV (1 << 5)
|
16.5 IOMMU IOTLB 管理
16.5.1 iommu_iotlb_gather 结构
位置: include/linux/iommu.h:345
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
struct iommu_iotlb_gather { unsigned long start; unsigned long end; size_t pgsize; struct list_head freelist; bool queued; };
|
16.5.2 IOTLB 刷新辅助函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| static inline void iommu_iotlb_gather_init(struct iommu_iotlb_gather *gather) { *gather = (struct iommu_iotlb_gather) { .start = ULONG_MAX, .freelist = LIST_HEAD_INIT(gather->freelist), }; }
static inline bool iommu_iotlb_gather_is_disjoint( struct iommu_iotlb_gather *gather, unsigned long iova, size_t size) { unsigned long start = iova, end = start + size - 1; return gather->end != 0 && (end + 1 < gather->start || start > gather->end + 1); }
static inline void iommu_iotlb_gather_add_range( struct iommu_iotlb_gather *gather, unsigned long iova, size_t size) { unsigned long end = iova + size - 1; if (gather->start > iova) gather->start = iova; if (gather->end < end) gather->end = end; }
static inline void iommu_iotlb_gather_add_page( struct iommu_domain *domain, struct iommu_iotlb_gather *gather, unsigned long iova, size_t size) { if ((gather->pgsize && gather->pgsize != size) || iommu_iotlb_gather_is_disjoint(gather, iova, size)) iommu_iotlb_sync(domain, gather); gather->pgsize = size; iommu_iotlb_gather_add_range(gather, iova, size); }
static inline bool iommu_iotlb_gather_queued(struct iommu_iotlb_gather *gather) { return gather && gather->queued; }
|
16.5.3 TLB 刷新函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| static inline void iommu_flush_iotlb_all(struct iommu_domain *domain) { if (domain->ops->flush_iotlb_all) domain->ops->flush_iotlb_all(domain); }
static inline void iommu_iotlb_sync(struct iommu_domain *domain, struct iommu_iotlb_gather *iotlb_gather) { if (domain->ops->iotlb_sync) domain->ops->iotlb_sync(domain, iotlb_gather); iommu_iotlb_gather_init(iotlb_gather); }
|
16.6 SVA (共享虚拟地址)
16.6.1 SVA 概述
SVA (Shared Virtual Addressing) 允许设备使用与 CPU 相同的虚拟地址空间。
SVA 核心概念:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| ┌─────────────────────────────────────────────────────────────────┐ │ 传统 DMA 模型 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 进程 VA ──→ CPU MMU ──→ PA │ │ │ │ 设备 ──→ IOVA ──→ IOMMU ──→ PA ──→ (可能通过 DMA 映射) │ │ │ │ 问题: 需要维护两套地址空间,需要显式 DMA 映射 │ │ │ ├─────────────────────────────────────────────────────────────────┤ │ SVA 模式 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 进程 VA ──→ CPU MMU ──→ PA │ │ │ │ │ └── 设备 ──→ IOVA (=VA) ──→ IOMMU ──→ PA │ │ │ │ 优势: 设备直接使用进程虚拟地址,无需额外映射 │ │ 通过 PASID 区分不同进程的地址空间 │ │ │ └─────────────────────────────────────────────────────────────────┘
|
16.6.2 PASID (Process Address Space ID)
PASID 定义:
1 2 3 4
| #define IOMMU_NO_PASID (0U) #define IOMMU_FIRST_GLOBAL_PASID (1U) #define IOMMU_PASID_INVALID (-1U) typedef unsigned int ioasid_t;
|
PASID 作用:
- 地址空间隔离: 允许单个设备同时访问多个进程的地址空间
- 并发访问: 设备可以同时为多个进程执行 DMA 操作
- PASID 表: IOMMU 维护 PASID 表,每个 PASID 指向不同的页表
16.6.3 iommu_sva 结构
位置: include/linux/iommu.h:1009
1 2 3 4 5 6 7 8
|
struct iommu_sva { struct iommu_attach_handle handle; struct device *dev; refcount_t users; };
|
16.6.4 SVA 操作函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| struct iommu_sva *iommu_sva_bind_device(struct device *dev, struct mm_struct *mm); void iommu_sva_unbind_device(struct iommu_sva *handle);
u32 iommu_sva_get_pasid(struct iommu_sva *handle);
int iommu_attach_device_pasid(struct iommu_domain *domain, struct device *dev, ioasid_t pasid, struct iommu_attach_handle *handle); void iommu_detach_device_pasid(struct iommu_domain *domain, struct device *dev, ioasid_t pasid);
ioasid_t iommu_alloc_global_pasid(struct device *dev); void iommu_free_global_pasid(ioasid_t pasid);
|
16.6.5 mm_struct PASID 操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| static inline void mm_pasid_init(struct mm_struct *mm) {
mm->iommu_mm = NULL; }
static inline bool mm_valid_pasid(struct mm_struct *mm) { return READ_ONCE(mm->iommu_mm); }
static inline u32 mm_get_enqcmd_pasid(struct mm_struct *mm) { struct iommu_mm_data *iommu_mm = READ_ONCE(mm->iommu_mm); if (!iommu_mm) return IOMMU_PASID_INVALID; return iommu_mm->pasid; }
void mm_pasid_drop(struct mm_struct *mm);
|
16.7 IOMMU Fault 处理
16.7.1 IOMMU Fault 类型
1 2 3 4 5 6 7 8 9 10
| enum iommu_fault_type { IOMMU_FAULT_PAGE_REQ = 1, };
#define IOMMU_FAULT_PERM_READ (1 << 0) #define IOMMU_FAULT_PERM_WRITE (1 << 1) #define IOMMU_FAULT_PERM_EXEC (1 << 2) #define IOMMU_FAULT_PERM_PRIV (1 << 3)
|
16.7.2 IOMMU Fault 结构
位置: include/linux/iommu.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
struct iommu_fault_page_request { #define IOMMU_FAULT_PAGE_REQUEST_PASID_VALID (1 << 0) #define IOMMU_FAULT_PAGE_REQUEST_LAST_PAGE (1 << 1) #define IOMMU_FAULT_PAGE_RESPONSE_NEEDS_PASID (1 << 2) u32 flags; u32 pasid; u32 grpid; u32 perm; u64 addr; u64 private_data[2]; };
struct iommu_fault { u32 type; struct iommu_fault_page_request prm; };
|
16.7.3 页响应代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
enum iommu_page_response_code { IOMMU_PAGE_RESP_SUCCESS = 0, IOMMU_PAGE_RESP_INVALID, IOMMU_PAGE_RESP_FAILURE, };
struct iommu_page_response { u32 pasid; u32 grpid; u32 code; };
|
16.7.4 IOPF (I/O Page Fault) 队列
位置: include/linux/iommu.h:139
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
|
struct iopf_group { struct iopf_fault last_fault; struct list_head faults; size_t fault_count; struct list_head pending_node; struct work_struct work; struct iommu_attach_handle *attach_handle; struct iommu_fault_param *fault_param; struct list_head node; u32 cookie; };
struct iopf_fault { struct iommu_fault fault; struct list_head list; };
struct iopf_queue { struct workqueue_struct *wq; struct list_head devices; struct mutex lock; };
|
16.7.5 IOPF 队列操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| struct iopf_queue *iopf_queue_alloc(const char *name); void iopf_queue_free(struct iopf_queue *queue);
int iopf_queue_add_device(struct iopf_queue *queue, struct device *dev); void iopf_queue_remove_device(struct iopf_queue *queue, struct device *dev); int iopf_queue_flush_dev(struct device *dev);
int iommu_report_device_fault(struct device *dev, struct iopf_fault *evt); void iopf_group_response(struct iopf_group *group, enum iommu_page_response_code status); void iopf_free_group(struct iopf_group *group);
int iopf_queue_discard_partial(struct iopf_queue *queue);
|
16.8 DMA-IOMMU 实现
16.8.1 DMA-IOMMU 概述
DMA-IOMMU 层为通用 DMA API 提供后端支持,自动管理设备的 IOMMU 域和 IOVA 地址空间。
位置: drivers/iommu/dma-iommu.c
架构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| ┌─────────────────────────────────────────────────────────────────┐ │ 设备驱动 │ │ dma_map_single() / dma_map_sg() / dma_alloc_coherent() │ └─────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ DMA 映射层 │ │ (drivers/iommu/dma-iommu.c) │ └─────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ IOMMU 核心 │ │ (drivers/iommu/iommu.c) │ └─────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ IOMMU 硬件驱动 │ │ Intel VT-d / AMD-Vi / ARM SMMU / virtio-iommu │ └─────────────────────────────────────────────────────────────────┘
|
16.8.2 DMA-IOMMU 核心函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| struct iommu_domain *iommu_dma_domain_alloc(struct bus_type *bus); int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base, u64 size, struct device *dev);
int iommu_get_msi_cookie(struct iommu_domain *domain, dma_addr_t base);
int iommu_dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, enum dma_data_direction dir, unsigned long attrs); void iommu_dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nents, enum dma_data_direction dir, unsigned long attrs);
dma_addr_t iommu_dma_map_page(struct device *dev, struct page *page, unsigned long offset, size_t size, enum dma_data_direction dir, unsigned long attrs); void iommu_dma_unmap_page(struct device *dev, dma_addr_t dma_handle, size_t size, enum dma_data_direction dir, unsigned long attrs);
void *iommu_dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t gfp, unsigned long attrs); void iommu_dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr_t dma_handle, unsigned long attrs);
|
16.8.3 IOVA 管理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| struct iova_domain { struct rbroot rbroot; spinlock_t iova_rbtree_lock; unsigned long granule; unsigned long start_pfn; unsigned long dma_32bit_pfn; struct iova_rcache *rcaches; };
struct iova *alloc_iova(struct iova_domain *iovad, unsigned long size, unsigned long limit_pfn, bool size_aligned); void free_iova(struct iova_domain *iovad, struct iova *iova);
void init_iova_rcaches(struct iova_domain *iovad); void free_iova_rcaches(struct iova_domain *iovad); void put_iova_domain(struct iova_domain *iovad);
|
16.9 IOMMU 保留区域
16.9.1 保留区域类型
位置: include/linux/iommu.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| enum iommu_resv_type { IOMMU_RESV_DIRECT,
IOMMU_RESV_DIRECT_RELAXABLE, IOMMU_RESV_RESERVED, IOMMU_RESV_MSI, IOMMU_RESV_SW_MSI, };
|
16.9.2 iommu_resv_region 结构
位置: include/linux/iommu.h:285
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
struct iommu_resv_region { struct list_head list; phys_addr_t start; size_t length; int prot; enum iommu_resv_type type; void (*free)(struct device *dev, struct iommu_resv_region *region); };
|
16.9.3 保留区域操作
1 2 3 4 5 6 7 8 9 10
| extern void iommu_get_resv_regions(struct device *dev, struct list_head *list); extern void iommu_put_resv_regions(struct device *dev, struct list_head *list); extern int iommu_get_group_resv_regions(struct iommu_group *group, struct list_head *head);
extern struct iommu_resv_region * iommu_alloc_resv_region(phys_addr_t start, size_t length, int prot, enum iommu_resv_type type, gfp_t gfp);
|
16.10 IOMMU 设备特性
16.10.1 设备特性枚举
位置: include/linux/iommu.h
1 2 3 4 5 6 7 8 9 10 11
|
enum iommu_dev_features { IOMMU_DEV_FEAT_SVA, IOMMU_DEV_FEAT_IOPF, };
|
16.10.2 设备特性操作
1 2 3 4 5 6
| int iommu_dev_enable_feature(struct device *dev, enum iommu_dev_features f); int iommu_dev_disable_feature(struct device *dev, enum iommu_dev_features f);
bool device_iommu_capable(struct device *dev, enum iommu_cap cap);
|
16.10.3 IOMMU 能力
1 2 3 4 5 6 7 8
| enum iommu_cap { IOMMU_CAP_CACHE_COHERENCY, IOMMU_CAP_NOEXEC, IOMMU_CAP_PRE_BOOT_PROTECTION, IOMMU_CAP_ENFORCE_CACHE_COHERENCY, IOMMU_CAP_DEFERRED_FLUSH, IOMMU_CAP_DIRTY_TRACKING, };
|
16.11 IOMMU 脏页跟踪
16.11.1 脏页跟踪概述
IOMMU 脏页跟踪用于虚拟化场景,记录设备通过 IOMMU 修改的页面,用于迁移或增量备份。
16.11.2 iommu_dirty_ops
位置: include/linux/iommu.h:371
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
struct iommu_dirty_ops { int (*set_dirty_tracking)(struct iommu_domain *domain, bool enabled); int (*read_and_clear_dirty)(struct iommu_domain *domain, unsigned long iova, size_t size, unsigned long flags, struct iommu_dirty_bitmap *dirty); };
|
16.11.3 iommu_dirty_bitmap 结构
位置: include/linux/iommu.h:358
1 2 3 4 5 6 7 8 9
|
struct iommu_dirty_bitmap { struct iova_bitmap *bitmap; struct iommu_iotlb_gather *gather; };
|
16.11.4 脏页跟踪操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| static inline void iommu_dirty_bitmap_init(struct iommu_dirty_bitmap *dirty, struct iova_bitmap *bitmap, struct iommu_iotlb_gather *gather) { if (gather) iommu_iotlb_gather_init(gather); dirty->bitmap = bitmap; dirty->gather = gather; }
static inline void iommu_dirty_bitmap_record(struct iommu_dirty_bitmap *dirty, unsigned long iova, unsigned long length) { if (dirty->bitmap) iova_bitmap_set(dirty->bitmap, iova, length); if (dirty->gather) iommu_iotlb_gather_add_range(dirty->gather, iova, length); }
|
16.12 嵌套 IOMMU (虚拟化)
16.12.1 嵌套 IOMMU 概述
嵌套 IOMMU 用于虚拟化场景,允许客户机 (Guest) 管理自己的 IOMMU 页表,形成两级地址转换:
1 2 3 4 5
| GVA (Guest Virtual Address) ↓ (Guest Page Table) GPA (Guest Physical Address) ↓ (Nested IOMMU + Host Stage-2) HPA (Host Physical Address)
|
16.12.2 嵌套域操作
1 2 3 4 5 6 7 8 9 10 11
| int iommu_enable_nesting(struct iommu_domain *domain);
struct iommu_domain *(*domain_alloc_user)( struct device *dev, u32 flags, struct iommu_domain *parent, const struct iommu_user_data *user_data);
int (*cache_invalidate_user)(struct iommu_domain *domain, struct iommu_user_data_array *array);
|
16.13 IOMMU 调试和监控
16.13.1 IOMMU DebugFS
1 2 3 4
| #ifdef CONFIG_IOMMU_DEBUGFS extern struct dentry *iommu_debugfs_dir; void iommu_debugfs_setup(void); #endif
|
16.13.2 IOMMU 跟踪事件
IOMMU 提供了多个跟踪点用于调试:
1 2 3 4 5 6 7 8
| trace-cmd list | grep iommu
|
16.14 架构特定实现
16.14.1 Intel VT-d
驱动位置: drivers/iommu/intel/
核心文件:
iommu.c - 核心 IOMMU 实现
dmar.c - DMAR (DMA Remapping) 表解析
pasid.c - PASID 管理
svm.c - Shared Virtual Memory (SVM) 支持
关键特性:
- 支持 5 级页表
- Scalable Mode (Scalable Mode Translation)
- PASID 支持 (最多 2^20 - 1 个 PASID)
- PRS (Page Request Services) for IOPF
16.14.2 AMD-Vi
驱动位置: drivers/iommu/amd/
核心文件:
iommu.c - 核心 IOMMU 实现
init.c - 初始化
pasid.c - PASID 管理
io_pgtable.c - IO 页表实现
关键特性:
- 支持 IOMMU v1 和 v2
- GDTR (Guest DTE Root) for nested
- PPR (Peripheral Page Request) for IOPF
16.14.3 ARM SMMU v3
驱动位置: drivers/iommu/arm/arm-smmu-v3/
核心文件:
arm-smmu-v3.c - SMMUv3 驱动
arm-smmu-v3-sva.c - SVA 实现
关键特性:
- 支持两阶段转换 (Stage-1 + Stage-2)
- SVA (SMMUv3.1+)
- 支持 PCIe 和平台设备
- 最多 2^20 - 1 PASID
16.15 本章小结
本章介绍了 Linux 6.12 的 IOMMU 子系统:
核心概念
- IOMMU 基础: DMA 地址转换、内存隔离、scatter-gather 支持
- 核心结构: iommu_device、iommu_domain、iommu_group、dev_iommu
- 域类型: BLOCKED、IDENTITY、UNMANAGED、DMA、DMA_FQ、SVA、NESTED
主要功能
- 域操作: 分配/释放域、设备附加/分离、映射/解除映射
- IOTLB 管理: TLB 刷新、批量失效
- SVA: 共享虚拟地址、PASID 支持、设备-mm 绑定
- Fault 处理: IOPF 队列、fault 响应
高级特性
- DMA-IOMMU: DMA API 的 IOMMU 后端实现、IOVA 管理
- 脏页跟踪: 虚拟化场景的页面修改跟踪
- 嵌套 IOMMU: 虚拟化两级地址转换
架构支持
- Intel VT-d: DMAR、PASID、SVM
- AMD-Vi: IOMMU v1/v2、PPR
- ARM SMMU v3: 两阶段转换、SVA
相关章节:
- 第4章: 虚拟地址空间 - mm_struct 结构
- 第5章: 页表管理 - CPU MMU 页表
- 第6章: 虚拟内存区域 (VMA) - VMA 管理
- 第9章: 缺页异常处理 - CPU 页面 fault