相关API
方式1:dma内存池:dma_pool_create
struct dma_pool *dma_pool_create(const char *name, struct device *dev,
size_t size, size_t align, size_t boundary)
{
struct dma_pool *retval;
size_t allocation;
bool empty = false;
if (align == 0)
align = 1;
else if (align & (align - 1))
return NULL;
if (size == 0)
return NULL;
else if (size < 4)
size = 4;
if ((size % align) != 0)
size = ALIGN(size, align);
allocation = max_t(size_t, size, PAGE_SIZE);
if (!boundary)
boundary = allocation;
else if ((boundary < size) || (boundary & (boundary - 1)))
return NULL;
//slub分配一个dma_pool 结构体
retval = kmalloc_node(sizeof(*retval), GFP_KERNEL, dev_to_node(dev));
if (!retval)
return retval;
strlcpy(retval->name, name, sizeof(retval->name));
retval->dev = dev;
INIT_LIST_HEAD(&retval->page_list);
spin_lock_init(&retval->lock);
retval->size = size;
retval->boundary = boundary;
retval->allocation = allocation;
INIT_LIST_HEAD(&retval->pools);
/*
* pools_lock ensures that the ->dma_pools list does not get corrupted.
* pools_reg_lock ensures that there is not a race between
* dma_pool_create() and dma_pool_destroy() or within dma_pool_create()
* when the first invocation of dma_pool_create() failed on
* device_create_file() and the second assumes that it has been done (I
* know it is a short window).
*/
mutex_lock(&pools_reg_lock);
mutex_lock(&pools_lock);
if (list_empty(&dev->dma_pools))
empty = true;
list_add(&retval->pools, &dev->dma_pools);
mutex_unlock(&pools_lock);
if (empty) {
int err;
err = device_create_file(dev, &dev_attr_pools);
if (err) {
mutex_lock(&pools_lock);
list_del(&retval->pools);
mutex_unlock(&pools_lock);
mutex_unlock(&pools_reg_lock);
kfree(retval);
return NULL;
}
}
mutex_unlock(&pools_reg_lock);
return retval;
}
EXPORT_SYMBOL(dma_pool_create);
dma_pool_alloc:从内存池中分配,底层会调用dma_alloc_coherent分配。
void *dma_pool_alloc(struct dma_pool *pool, gfp_t mem_flags,
dma_addr_t *handle)
{
unsigned long flags;
struct dma_page *page;
size_t offset;
void *retval;
might_sleep_if(gfpflags_allow_blocking(mem_flags));
spin_lock_irqsave(&pool->lock, flags);
list_for_each_entry(page, &pool->page_list, page_list) {
if (page->offset < pool->allocation)
goto ready;
}
/* pool_alloc_page() might sleep, so temporarily drop &pool->lock */
spin_unlock_irqrestore(&pool->lock, flags);
//创建dma_page,pool_alloc_page里面使用 dma_alloc_coherent申请dma内存。
page = pool_alloc_page(pool, mem_flags & (~__GFP_ZERO));
if (!page)
return NULL;
spin_lock_irqsave(&pool->lock, flags);
list_add(&page->page_list, &pool->page_list);
ready:
page->in_use++;
offset = page->offset;
page->offset = *(int *)(page->vaddr + offset);
retval = offset + page->vaddr;
*handle = offset + page->dma;
#ifdef DMAPOOL_DEBUG
{
int i;
u8 *data = retval;
/* page->offset is stored in first 4 bytes */
for (i = sizeof(page->offset); i < pool->size; i++) {
if (data[i] == POOL_POISON_FREED)
continue;
if (pool->dev)
dev_err(pool->dev,
"dma_pool_alloc %s, %p (corrupted)\n",
pool->name, retval);
else
pr_err("dma_pool_alloc %s, %p (corrupted)\n",
pool->name, retval);
/*
* Dump the first 4 bytes even if they are not
* POOL_POISON_FREED
*/
print_hex_dump(KERN_ERR, "", DUMP_PREFIX_OFFSET, 16, 1,
data, pool->size, 1);
break;
}
}
if (!(mem_flags & __GFP_ZERO))
memset(retval, POOL_POISON_ALLOCATED, pool->size);
#endif
spin_unlock_irqrestore(&pool->lock, flags);
if (mem_flags & __GFP_ZERO)
memset(retval, 0, pool->size);
return retval;
}
EXPORT_SYMBOL(dma_pool_alloc);
dma_pool结构体:
struct dma_pool { /* the pool */
struct list_head page_list;
spinlock_t lock;
size_t size;
struct device *dev;
size_t allocation;
size_t boundary;
char name[32];
struct list_head pools;
};
struct dma_page { /* cacheable header for 'allocation' bytes */
struct list_head page_list;
void *vaddr;
dma_addr_t dma;
unsigned int in_use;
unsigned int offset;
};
pool_alloc_page:
static struct dma_page *pool_alloc_page(struct dma_pool *pool, gfp_t mem_flags)
{
struct dma_page *page;
page = kmalloc(sizeof(*page), mem_flags);
if (!page)
return NULL;
page->vaddr = dma_alloc_coherent(pool->dev, pool->allocation,
&page->dma, mem_flags);
if (page->vaddr) {
#ifdef DMAPOOL_DEBUG
memset(page->vaddr, POOL_POISON_FREED, pool->allocation);
#endif
pool_initialise_page(pool, page);
page->in_use = 0;
page->offset = 0;
} else {
kfree(page);
page = NULL;
}
return page;
}
dma_alloc_coherent: 一致性内存,创建即映射,一直存在。
static inline void *dma_alloc_coherent(struct device *dev, size_t size,
dma_addr_t *dma_handle, gfp_t flag)
{
return dma_alloc_attrs(dev, size, dma_handle, flag, 0);
}
static inline void *dma_alloc_attrs(struct device *dev, size_t size,
dma_addr_t *dma_handle, gfp_t flag,
unsigned long attrs)
{
const struct dma_map_ops *ops = get_dma_ops(dev);
void *cpu_addr;
BUG_ON(!ops);
WARN_ON_ONCE(dev && !dev->coherent_dma_mask);
if (dma_alloc_from_dev_coherent(dev, size, dma_handle, &cpu_addr))
return cpu_addr;
/* let the implementation decide on the zone to allocate from: */
flag &= ~(__GFP_DMA | __GFP_DMA32 | __GFP_HIGHMEM);
if (!arch_dma_alloc_attrs(&dev))
return NULL;
if (!ops->alloc)
return NULL;
//通过ops->alloc来分配
cpu_addr = ops->alloc(dev, size, dma_handle, flag, attrs);
debug_dma_alloc_coherent(dev, size, *dma_handle, cpu_addr);
return cpu_addr;
}
上述使用ops->alloc来分配;在arm64下pci设备在probe的过程中会通过arch_setup_dma_ops设置:
[ 38.254846] ===arch_setup_dma_ops dev ffff834b41814098 dev->dma_ops ffff000008a50000
[ 38.262741] CPU: 26 PID: 1 Comm: swapper/0 Not tainted 4.19.0l.1118+ #15
[ 38.269409] Hardware name: PHYTIUM LTD Phytium S2500 Development Platform/Phytium S2500 Development Platform, BIOS EDK II Jan 20 2021
[ 38.281348] Call trace:
[ 38.283789] dump_backtrace+0x0/0x190
[ 38.287435] show_stack+0x28/0x38
[ 38.290735] dump_stack+0x90/0xb4
[ 38.294035] arch_setup_dma_ops+0x9c/0xe0
[ 38.298029] acpi_dma_configure+0x7c/0xb0
[ 38.302020] pci_dma_configure+0xc8/0xd8
[ 38.305925] dma_configure+0x34/0x40
[ 38.309485] really_probe+0x90/0x3a8
[ 38.313043] driver_probe_device+0x70/0x140
[ 38.317206] __driver_attach+0x11c/0x158
[ 38.321109] bus_for_each_dev+0x88/0xd8
[ 38.324926] driver_attach+0x34/0x40
[ 38.328485] bus_add_driver+0x214/0x258
[ 38.332301] driver_register+0x68/0x118
[ 38.336118] __pci_register_driver+0x5c/0x70
[ 38.340369] nvme_init+0x2c/0x34
[ 38.343581] do_one_initcall+0x6c/0x1ec
[ 38.347400] kernel_init_freeable+0x2d4/0x390
[ 38.351737] kernel_init+0x1c/0x110
[ 38.355209] ret_from_fork+0x10/0x18
arch_setup_dma_ops函数: arch/arn64/mm/dma-mapping.c
void arch_setup_dma_ops(struct device *dev, u64 dma_base, u64 size,
const struct iommu_ops *iommu, bool coherent)
{
if (!dev->dma_ops)
dev->dma_ops = &arm64_swiotlb_dma_ops; //使用该ops
dev->archdata.dma_coherent = coherent;
__iommu_setup_dma_ops(dev, dma_base, size, iommu);
printk("===arch_setup_dma_ops dev %llx name %s dev->dma_ops %llx \n",dev,dev_name(dev),dev->dma_ops);
#ifdef CONFIG_XEN
if (xen_initial_domain()) {
dev->archdata.dev_dma_ops = dev->dma_ops;
dev->dma_ops = xen_dma_ops;
}
#endif
}
static void __iommu_setup_dma_ops(struct device *dev, u64 dma_base, u64 size,
const struct iommu_ops *ops)
{
struct iommu_domain *domain;
if (!ops)
return;
/*
* The IOMMU core code allocates the default DMA domain, which the
* underlying IOMMU driver needs to support via the dma-iommu layer.
*/
domain = iommu_get_domain_for_dev(dev);
if (!domain)
goto out_err;
//判断domain-type类型,如果硬件不支持则依然是swiotlb软件实现地址映射。否则iommu_dma_ops。
if (domain->type == IOMMU_DOMAIN_DMA) {
if (iommu_dma_init_domain(domain, dma_base, size, dev))
goto out_err;
dev->dma_ops = &iommu_dma_ops;
}
return;
out_err:
pr_warn("Failed to set up IOMMU for device %s; retaining platform DMA ops\n",
dev_name(dev));
}
上述打印:
[ 38.094271] ===arch_setup_dma_ops dev ffff835b41594098 name 0000:03:00.0 dev->dma_ops ffff000008a50000
[ 38.606332] ===arch_setup_dma_ops dev ffff835b41595098 name 0000:04:00.0 dev->dma_ops ffff000008a50000
[ 39.698185] ===arch_setup_dma_ops dev ffff835b41596098 name 0000:05:00.0 dev->dma_ops ffff000008a50000
[ 41.540046] ===arch_setup_dma_ops dev ffff835b415a1098 name 0001:03:00.0 dev->dma_ops ffff000008a50000
[ 45.113132] ===arch_setup_dma_ops dev ffff835b41599098 name 0000:13:00.0 dev->dma_ops ffff000008a50000
[ 92.951271] ===arch_setup_dma_ops dev ffff835b41597098 name 0000:12:00.0 dev->dma_ops ffff000008a50000
[ 93.234855] ===arch_setup_dma_ops dev ffff835b4159b098 name 0000:15:00.0 dev->dma_ops ffff000008a50000
[ 93.260169] ===arch_setup_dma_ops dev ffff835b41598098 name 0000:12:00.1 dev->dma_ops ffff000008a50000
[root@localhost linux-4.19]# cat /proc/kallsyms |grep 8a50000
ffff000008a50000 r arm64_swiotlb_dma_ops
ffff000008a50000 R vdso_end
例:驱动使用ops的map_page函数:
[ 421.258031] dump_backtrace+0x0/0x1b8
[ 421.265029] show_stack+0x24/0x30
[ 421.271647] dump_stack+0x90/0xb4
[ 421.278219] __swiotlb_map_page+0x60/0xf0
[ 421.285523] e1000_alloc_rx_buffers+0xe4/0x2a8 [e1000e]
[ 421.294189] e1000_clean_rx_irq+0x2f0/0x3c8 [e1000e]
[ 421.294200] e1000e_poll+0xc4/0x2b8 [e1000e]
[ 421.310160] net_rx_action+0x180/0x410
[ 421.317226] __do_softirq+0x11c/0x30c
[ 421.324211] irq_exit+0x108/0x120
[ 421.330850] __handle_domain_irq+0x6c/0xc0
[ 421.338213] gic_handle_irq+0x80/0x18c
[ 421.345212] el1_irq+0xb0/0x140
[ 421.351620] arch_cpu_idle+0x30/0x1b8
[ 421.358532] do_idle+0x1dc/0x2a8
[ 421.365010] cpu_startup_entry+0x2c/0x30
[ 421.372205] secondary_start_kernel+0x180/0x1c8
关于domain_type:IOMMU_DOMAIN_DMA
static int __init iommu_set_def_domain_type(char *str)
{
bool pt;
int ret;
ret = kstrtobool(str, &pt);
if (ret)
return ret;
#ifdef CONFIG_ARCH_PHYTIUM
/*
* Always set default iommu type to IOMMU_DOMAIN_IDENTITY
* on Phytium FT-2000+ SoC to avoid unnecessary troubles
* introduced by the SMMU workaround.
*/
if ((read_cpuid_id() & MIDR_CPU_MODEL_MASK) == MIDR_PHYTIUM_FT2000PLUS)
iommu_def_domain_type = IOMMU_DOMAIN_IDENTITY;
else
iommu_def_domain_type = pt ? IOMMU_DOMAIN_IDENTITY : IOMMU_DOMAIN_DMA;
#else
iommu_def_domain_type = pt ? IOMMU_DOMAIN_IDENTITY : IOMMU_DOMAIN_DMA;
#endif
return 0;
}
early_param("iommu.passthrough", iommu_set_def_domain_type);
分配iommu_domain:
static struct iommu_domain *__iommu_domain_alloc(struct bus_type *bus,
unsigned type)
{
struct iommu_domain *domain;
if (bus == NULL || bus->iommu_ops == NULL)
return NULL;
domain = bus->iommu_ops->domain_alloc(type);
//调用总线中的iommu_ops->domain_alloc 。 比如pci_bus的,在arm-smmu.c 的arm_smmu_bus_init 函数。
if (!domain)
return NULL;
domain->ops = bus->iommu_ops;
domain->type = type;
/* Assume all sizes by default; the driver may override this later */
domain->pgsize_bitmap = bus->iommu_ops->pgsize_bitmap;
return domain;
}
struct iommu_domain *iommu_domain_alloc(struct bus_type *bus)
{
return __iommu_domain_alloc(bus, IOMMU_DOMAIN_UNMANAGED);
}
EXPORT_SYMBOL_GPL(iommu_domain_alloc);
arm64_swiotlb_dma_ops :alloc函数
static void *__dma_alloc(struct device *dev, size_t size,
dma_addr_t *dma_handle, gfp_t flags,
unsigned long attrs)
{
struct page *page;
void *ptr, *coherent_ptr;
bool coherent = is_device_dma_coherent(dev);
pgprot_t prot = __get_dma_pgprot(attrs, PAGE_KERNEL, false);
size = PAGE_ALIGN(size);
if (!coherent && !gfpflags_allow_blocking(flags)) {
struct page *page = NULL;
void *addr = __alloc_from_pool(size, &page, flags);
if (addr)
*dma_handle = phys_to_dma(dev, page_to_phys(page));
return addr;
}
//走这里 。。。。。。。。。。
ptr = swiotlb_alloc(dev, size, dma_handle, flags, attrs);
if (!ptr)
goto no_mem;
/* no need for non-cacheable mapping if coherent */
if (coherent)
return ptr;
/* remove any dirty cache lines on the kernel alias */
__dma_flush_area(ptr, size);
/* create a coherent mapping */
page = virt_to_page(ptr);
coherent_ptr = dma_common_contiguous_remap(page, size, VM_USERMAP,
prot, __builtin_return_address(0));
if (!coherent_ptr)
goto no_map;
return coherent_ptr;
no_map:
swiotlb_free(dev, size, ptr, *dma_handle, attrs);
no_mem:
return NULL;
}
swiotlb_alloc函数:
void *swiotlb_alloc(struct device *dev, size_t size, dma_addr_t *dma_handle,
gfp_t gfp, unsigned long attrs)
{
void *vaddr;
/* temporary workaround: */
if (gfp & __GFP_NOWARN)
attrs |= DMA_ATTR_NO_WARN;
/*
* Don't print a warning when the first allocation attempt fails.
* swiotlb_alloc_coherent() will print a warning when the DMA memory
* allocation ultimately failed.
*/
gfp |= __GFP_NOWARN;
//debug发现使用dma_direct_alloc
vaddr = dma_direct_alloc(dev, size, dma_handle, gfp, attrs);
if (!vaddr)
vaddr = swiotlb_alloc_buffer(dev, size, dma_handle, attrs);
return vaddr;
}
//kernel/dma/direct.c
void *dma_direct_alloc(struct device *dev, size_t size, dma_addr_t *dma_handle,
gfp_t gfp, unsigned long attrs)
{
unsigned int count = PAGE_ALIGN(size) >> PAGE_SHIFT;
int page_order = get_order(size);
struct page *page = NULL;
void *ret;
/* we always manually zero the memory once we are done: */
gfp &= ~__GFP_ZERO;
/* GFP_DMA32 and GFP_DMA are no ops without the corresponding zones: */
if (dev->coherent_dma_mask <= DMA_BIT_MASK(ARCH_ZONE_DMA_BITS))
gfp |= GFP_DMA;
if (dev->coherent_dma_mask <= DMA_BIT_MASK(32) && !(gfp & GFP_DMA))
gfp |= GFP_DMA32;
again:
/* CMA can be used only in the context which permits sleeping */
if (gfpflags_allow_blocking(gfp)) {
page = dma_alloc_from_contiguous(dev, count, page_order,
gfp & __GFP_NOWARN);
if (page && !dma_coherent_ok(dev, page_to_phys(page), size)) {
dma_release_from_contiguous(dev, page, count);
page = NULL;
}
}
if (!page)
page = alloc_pages_node(dev_to_node(dev), gfp, page_order);
if (page && !dma_coherent_ok(dev, page_to_phys(page), size)) {
__free_pages(page, page_order);
page = NULL;
if (IS_ENABLED(CONFIG_ZONE_DMA32) &&
dev->coherent_dma_mask < DMA_BIT_MASK(64) &&
!(gfp & (GFP_DMA32 | GFP_DMA))) {
gfp |= GFP_DMA32;
goto again;
}
if (IS_ENABLED(CONFIG_ZONE_DMA) &&
dev->coherent_dma_mask < DMA_BIT_MASK(32) &&
!(gfp & GFP_DMA)) {
gfp = (gfp & ~GFP_DMA32) | GFP_DMA;
goto again;
}
}
if (!page)
return NULL;
ret = page_address(page);
if (force_dma_unencrypted()) {
set_memory_decrypted((unsigned long)ret, 1 << page_order);
*dma_handle = __phys_to_dma(dev, page_to_phys(page));
} else {
*dma_handle = phys_to_dma(dev, page_to_phys(page));
}
memset(ret, 0, size);
return ret;
}
IOMMU/SMMU:
打开smmu的情况下
分配iommu_group 堆栈
arm_smmu add device
arm_smmu_master_alloc_smes->iommu_group_get_for_dev 创建iommu_group->iommu_domain
分配iommu_group
[ 51.916741] iommu_group_alloc+0x1b0/0x1f0
[ 51.920817] pci_device_group+0x108/0x128
[ 51.924808] arm_smmu_device_group+0xc4/0xd0
[ 51.929056] iommu_group_get_for_dev+0x54/0x180
[ 51.933565] arm_smmu_add_device+0x19c/0x618
[ 51.937815] iort_iommu_configure+0x110/0x1c8 //通过iort_add_device_replay调用iommu_ops->add_device(dev)函数。
[ 51.942152] acpi_dma_configure+0x54/0xb0
[ 51.946143] pci_dma_configure+0xc8/0xd8
[ 51.950047] dma_configure+0x34/0x40 //调用bus_type下的dma_configure回调,pci_bus_type是pci_dma_configure
[ 51.953606] really_probe+0x90/0x3a8 //注意:pci_bridge并没有驱动,不会probe。
[ 51.957164] driver_probe_device+0x70/0x140
[ 51.961327] __driver_attach+0x11c/0x158
[ 51.965231] bus_for_each_dev+0x88/0xd8
[ 51.969048] driver_attach+0x34/0x40
[ 51.972606] bus_add_driver+0x214/0x258
[ 51.976423] driver_register+0x68/0x118
[ 51.980240] __pci_register_driver+0x5c/0x70
[ 51.984491] nvme_init+0x2c/0x34
[ 51.987703] do_one_initcall+0x6c/0x1ec
[ 51.991522] kernel_init_freeable+0x2d4/0x390
[ 51.995858] kernel_init+0x1c/0x110
[ 51.999329] ret_from_fork+0x10/0x18
struct iommu_group *pci_device_group(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
/*
* Find the upstream DMA alias for the device. A device must not
* be aliased due to topology in order to have its own IOMMU group.
* If we find an alias along the way that already belongs to a
* group, use it.
*/
if (pci_for_each_dma_alias(pdev, get_pci_alias_or_group, &data))
return data.group;
/*
* Continue upstream from the point of minimum IOMMU granularity
* due to aliases to the point where devices are protected from
* peer-to-peer DMA by PCI ACS. Again, if we find an existing
* group, use it.
*/
for (bus = pdev->bus; !pci_is_root_bus(bus); bus = bus->parent) {
if (!bus->self)
continue;
if (pci_acs_path_enabled(bus->self, NULL, REQ_ACS_FLAGS))
break;
pdev = bus->self;
group = iommu_group_get(&pdev->dev);
if (group)
return group;
}
/*
* Look for existing groups on device aliases. If we alias another
* device or another device aliases us, use the same group.
*/
group = get_pci_alias_group(pdev, (unsigned long *)devfns);
if (group)
return group;
/*
* Look for existing groups on non-isolated functions on the same
* slot and aliases of those funcions, if any. No need to clear
* the search bitmap, the tested devfns are still valid.
*/
group = get_pci_function_alias_group(pdev, (unsigned long *)devfns);
if (group)
return group;
return iommu_group_alloc();
}
这个函数的核心逻辑在于pci_acs_path_enabled,简单来说如果是pcie的设备则检查该设备到root complex的路径上如果都开启了ACS则这个设备就单独成一个iommu_group,如果不是则找到它的alias group就行了比如如果这个是传统的pci bus(没有pcie这些ACS的特性)则这个pci bus下面的所有设备就组合成一个iommu_group。
开启smmu后dmam_alloc_coherent:
使用
static const struct dma_map_ops iommu_dma_ops = {
.alloc = __iommu_alloc_attrs,
.free = __iommu_free_attrs,
.mmap = __iommu_mmap_attrs,
.get_sgtable = __iommu_get_sgtable,
.map_page = __iommu_map_page,
.unmap_page = __iommu_unmap_page,
.map_sg = __iommu_map_sg_attrs,
.unmap_sg = __iommu_unmap_sg_attrs,
.sync_single_for_cpu = __iommu_sync_single_for_cpu,
.sync_single_for_device = __iommu_sync_single_for_device,
.sync_sg_for_cpu = __iommu_sync_sg_for_cpu,
.sync_sg_for_device = __iommu_sync_sg_for_device,
.map_resource = iommu_dma_map_resource,
.unmap_resource = iommu_dma_unmap_resource,
.mapping_error = iommu_dma_mapping_error,
};
其中__iommu_alloc_attrs调用iommu_dma_alloc 申请page页:
struct page **iommu_dma_alloc(struct device *dev, size_t size, gfp_t gfp,
unsigned long attrs, int prot, dma_addr_t *handle,
void (*flush_page)(struct device *, const void *, phys_addr_t))
{
//这里申请任意物理地址页。
count = PAGE_ALIGN(size) >> PAGE_SHIFT;
pages = __iommu_dma_alloc_pages(count, alloc_sizes >> PAGE_SHIFT, gfp);
if (!pages)
return NULL;
//这里申请iova是dma能访问的4G以下的虚拟地址。
size = iova_align(iovad, size);
iova = iommu_dma_alloc_iova(domain, size, dev->coherent_dma_mask, dev);
if (!iova)
goto out_free_pages;
if (sg_alloc_table_from_pages(&sgt, pages, count, 0, size, GFP_KERNEL))
goto out_free_iova;
if (!(prot & IOMMU_CACHE)) {
struct sg_mapping_iter miter;
/*
* The CPU-centric flushing implied by SG_MITER_TO_SG isn't
* sufficient here, so skip it by using the "wrong" direction.
*/
sg_miter_start(&miter, sgt.sgl, sgt.orig_nents, SG_MITER_FROM_SG);
while (sg_miter_next(&miter))
flush_page(dev, miter.addr, page_to_phys(miter.page));
sg_miter_stop(&miter);
}
//建立映射。
if (iommu_map_sg(domain, iova, sgt.sgl, sgt.orig_nents, prot)
< size)
goto out_free_sg;
*handle = iova;
sg_free_table(&sgt);
return pages;
}
drivers/iommu/dma-iommu.c中
[ 20.258409] ===iommu_dma_alloc page count 2 poage0 va ffff810789a20000 pa 10809a20000 iova fffe0000
[ 20.267503] CPU: 0 PID: 748 Comm: kworker/0:2 Not tainted 4.19.0l+ #12
[ 20.273999] Hardware name: PHYTIUM LTD Phytium S2500/64/Phytium S2500/64, BIOS V2.2 Feb 9 2021
[ 20.282662] Workqueue: events work_for_cpu_fn
[ 20.286999] Call trace:
[ 20.289436] dump_backtrace+0x0/0x1c0
[ 20.293080] show_stack+0x24/0x30
[ 20.296379] dump_stack+0x9c/0xbc
[ 20.299678] iommu_dma_alloc+0x234/0x460
[ 20.303582] __iommu_alloc_attrs+0xe4/0x2b0
[ 20.307746] dmam_alloc_coherent+0xb0/0x160
[ 20.311910] ahci_port_start+0x11c/0x220
[ 20.315814] ata_host_start.part.6+0xe4/0x1e8
[ 20.320150] ata_host_activate+0x70/0x150
[ 20.324139] ahci_host_activate+0x164/0x1b8
[ 20.328302] ahci_init_one+0x970/0xde4
[ 20.332034] local_pci_probe+0x44/0xa8
[ 20.335764] work_for_cpu_fn+0x20/0x30
[ 20.339494] process_one_work+0x1c4/0x408
[ 20.343483] worker_thread+0x228/0x488
[ 20.347214] kthread+0x130/0x138
[ 20.350427] ret_from_fork+0x10/0x1c
上述证明iova地址是4G内,而实际的物理地址可以实大于4G的范围,由SMMU硬件负责映射。和mmu类似。
更多推荐
arm64下dma相关 api
发布评论