赞
踩
- 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);
- 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;
- };
- 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;
- }
- 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;
- }
打开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。
使用
- 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类似。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。