arm64下dma相关 api(4.19)

相关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类似。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

古井无波 2024

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值