当前位置:   article > 正文

arm64下dma相关 api(4.19)_dma_pool_alloc

dma_pool_alloc

相关API 

方式1:dma内存池:dma_pool_create

  1. struct dma_pool *dma_pool_create(const char *name, struct device *dev,
  2. size_t size, size_t align, size_t boundary)
  3. {
  4. struct dma_pool *retval;
  5. size_t allocation;
  6. bool empty = false;
  7. if (align == 0)
  8. align = 1;
  9. else if (align & (align - 1))
  10. return NULL;
  11. if (size == 0)
  12. return NULL;
  13. else if (size < 4)
  14. size = 4;
  15. if ((size % align) != 0)
  16. size = ALIGN(size, align);
  17. allocation = max_t(size_t, size, PAGE_SIZE);
  18. if (!boundary)
  19. boundary = allocation;
  20. else if ((boundary < size) || (boundary & (boundary - 1)))
  21. return NULL;
  22. //slub分配一个dma_pool 结构体
  23. retval = kmalloc_node(sizeof(*retval), GFP_KERNEL, dev_to_node(dev));
  24. if (!retval)
  25. return retval;
  26. strlcpy(retval->name, name, sizeof(retval->name));
  27. retval->dev = dev;
  28. INIT_LIST_HEAD(&retval->page_list);
  29. spin_lock_init(&retval->lock);
  30. retval->size = size;
  31. retval->boundary = boundary;
  32. retval->allocation = allocation;
  33. INIT_LIST_HEAD(&retval->pools);
  34. /*
  35. * pools_lock ensures that the ->dma_pools list does not get corrupted.
  36. * pools_reg_lock ensures that there is not a race between
  37. * dma_pool_create() and dma_pool_destroy() or within dma_pool_create()
  38. * when the first invocation of dma_pool_create() failed on
  39. * device_create_file() and the second assumes that it has been done (I
  40. * know it is a short window).
  41. */
  42. mutex_lock(&pools_reg_lock);
  43. mutex_lock(&pools_lock);
  44. if (list_empty(&dev->dma_pools))
  45. empty = true;
  46. list_add(&retval->pools, &dev->dma_pools);
  47. mutex_unlock(&pools_lock);
  48. if (empty) {
  49. int err;
  50. err = device_create_file(dev, &dev_attr_pools);
  51. if (err) {
  52. mutex_lock(&pools_lock);
  53. list_del(&retval->pools);
  54. mutex_unlock(&pools_lock);
  55. mutex_unlock(&pools_reg_lock);
  56. kfree(retval);
  57. return NULL;
  58. }
  59. }
  60. mutex_unlock(&pools_reg_lock);
  61. return retval;
  62. }
  63. EXPORT_SYMBOL(dma_pool_create);

dma_pool_alloc:从内存池中分配,底层会调用dma_alloc_coherent分配。

  1. void *dma_pool_alloc(struct dma_pool *pool, gfp_t mem_flags,
  2. dma_addr_t *handle)
  3. {
  4. unsigned long flags;
  5. struct dma_page *page;
  6. size_t offset;
  7. void *retval;
  8. might_sleep_if(gfpflags_allow_blocking(mem_flags));
  9. spin_lock_irqsave(&pool->lock, flags);
  10. list_for_each_entry(page, &pool->page_list, page_list) {
  11. if (page->offset < pool->allocation)
  12. goto ready;
  13. }
  14. /* pool_alloc_page() might sleep, so temporarily drop &pool->lock */
  15. spin_unlock_irqrestore(&pool->lock, flags);
  16. //创建dma_page,pool_alloc_page里面使用 dma_alloc_coherent申请dma内存。
  17. page = pool_alloc_page(pool, mem_flags & (~__GFP_ZERO));
  18. if (!page)
  19. return NULL;
  20. spin_lock_irqsave(&pool->lock, flags);
  21. list_add(&page->page_list, &pool->page_list);
  22. ready:
  23. page->in_use++;
  24. offset = page->offset;
  25. page->offset = *(int *)(page->vaddr + offset);
  26. retval = offset + page->vaddr;
  27. *handle = offset + page->dma;
  28. #ifdef DMAPOOL_DEBUG
  29. {
  30. int i;
  31. u8 *data = retval;
  32. /* page->offset is stored in first 4 bytes */
  33. for (i = sizeof(page->offset); i < pool->size; i++) {
  34. if (data[i] == POOL_POISON_FREED)
  35. continue;
  36. if (pool->dev)
  37. dev_err(pool->dev,
  38. "dma_pool_alloc %s, %p (corrupted)\n",
  39. pool->name, retval);
  40. else
  41. pr_err("dma_pool_alloc %s, %p (corrupted)\n",
  42. pool->name, retval);
  43. /*
  44. * Dump the first 4 bytes even if they are not
  45. * POOL_POISON_FREED
  46. */
  47. print_hex_dump(KERN_ERR, "", DUMP_PREFIX_OFFSET, 16, 1,
  48. data, pool->size, 1);
  49. break;
  50. }
  51. }
  52. if (!(mem_flags & __GFP_ZERO))
  53. memset(retval, POOL_POISON_ALLOCATED, pool->size);
  54. #endif
  55. spin_unlock_irqrestore(&pool->lock, flags);
  56. if (mem_flags & __GFP_ZERO)
  57. memset(retval, 0, pool->size);
  58. return retval;
  59. }
  60. EXPORT_SYMBOL(dma_pool_alloc);

 dma_pool结构体

  1. struct dma_pool { /* the pool */
  2. struct list_head page_list;
  3. spinlock_t lock;
  4. size_t size;
  5. struct device *dev;
  6. size_t allocation;
  7. size_t boundary;
  8. char name[32];
  9. struct list_head pools;
  10. };
  11. struct dma_page { /* cacheable header for 'allocation' bytes */
  12. struct list_head page_list;
  13. void *vaddr;
  14. dma_addr_t dma;
  15. unsigned int in_use;
  16. unsigned int offset;
  17. };

pool_alloc_page:

  1. static struct dma_page *pool_alloc_page(struct dma_pool *pool, gfp_t mem_flags)
  2. {
  3. struct dma_page *page;
  4. page = kmalloc(sizeof(*page), mem_flags);
  5. if (!page)
  6. return NULL;
  7. page->vaddr = dma_alloc_coherent(pool->dev, pool->allocation,
  8. &page->dma, mem_flags);
  9. if (page->vaddr) {
  10. #ifdef DMAPOOL_DEBUG
  11. memset(page->vaddr, POOL_POISON_FREED, pool->allocation);
  12. #endif
  13. pool_initialise_page(pool, page);
  14. page->in_use = 0;
  15. page->offset = 0;
  16. } else {
  17. kfree(page);
  18. page = NULL;
  19. }
  20. return page;
  21. }

dma_alloc_coherent: 一致性内存,创建即映射,一直存在。

  1. static inline void *dma_alloc_coherent(struct device *dev, size_t size,
  2. dma_addr_t *dma_handle, gfp_t flag)
  3. {
  4. return dma_alloc_attrs(dev, size, dma_handle, flag, 0);
  5. }
  6. static inline void *dma_alloc_attrs(struct device *dev, size_t size,
  7. dma_addr_t *dma_handle, gfp_t flag,
  8. unsigned long attrs)
  9. {
  10. const struct dma_map_ops *ops = get_dma_ops(dev);
  11. void *cpu_addr;
  12. BUG_ON(!ops);
  13. WARN_ON_ONCE(dev && !dev->coherent_dma_mask);
  14. if (dma_alloc_from_dev_coherent(dev, size, dma_handle, &cpu_addr))
  15. return cpu_addr;
  16. /* let the implementation decide on the zone to allocate from: */
  17. flag &= ~(__GFP_DMA | __GFP_DMA32 | __GFP_HIGHMEM);
  18. if (!arch_dma_alloc_attrs(&dev))
  19. return NULL;
  20. if (!ops->alloc)
  21. return NULL;
  22. //通过ops->alloc来分配
  23. cpu_addr = ops->alloc(dev, size, dma_handle, flag, attrs);
  24. debug_dma_alloc_coherent(dev, size, *dma_handle, cpu_addr);
  25. return cpu_addr;
  26. }

上述使用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

  1. void arch_setup_dma_ops(struct device *dev, u64 dma_base, u64 size,
  2. const struct iommu_ops *iommu, bool coherent)
  3. {
  4. if (!dev->dma_ops)
  5. dev->dma_ops = &arm64_swiotlb_dma_ops; //使用该ops
  6. dev->archdata.dma_coherent = coherent;
  7. __iommu_setup_dma_ops(dev, dma_base, size, iommu);
  8. printk("===arch_setup_dma_ops dev %llx name %s dev->dma_ops %llx \n",dev,dev_name(dev),dev->dma_ops);
  9. #ifdef CONFIG_XEN
  10. if (xen_initial_domain()) {
  11. dev->archdata.dev_dma_ops = dev->dma_ops;
  12. dev->dma_ops = xen_dma_ops;
  13. }
  14. #endif
  15. }
  16. static void __iommu_setup_dma_ops(struct device *dev, u64 dma_base, u64 size,
  17. const struct iommu_ops *ops)
  18. {
  19. struct iommu_domain *domain;
  20. if (!ops)
  21. return;
  22. /*
  23. * The IOMMU core code allocates the default DMA domain, which the
  24. * underlying IOMMU driver needs to support via the dma-iommu layer.
  25. */
  26. domain = iommu_get_domain_for_dev(dev);
  27. if (!domain)
  28. goto out_err;
  29. //判断domain-type类型,如果硬件不支持则依然是swiotlb软件实现地址映射。否则iommu_dma_ops。
  30. if (domain->type == IOMMU_DOMAIN_DMA) {
  31. if (iommu_dma_init_domain(domain, dma_base, size, dev))
  32. goto out_err;
  33. dev->dma_ops = &iommu_dma_ops;
  34. }
  35. return;
  36. out_err:
  37. pr_warn("Failed to set up IOMMU for device %s; retaining platform DMA ops\n",
  38. dev_name(dev));
  39. }

上述打印: 

[   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

  1. static int __init iommu_set_def_domain_type(char *str)
  2. {
  3. bool pt;
  4. int ret;
  5. ret = kstrtobool(str, &pt);
  6. if (ret)
  7. return ret;
  8. #ifdef CONFIG_ARCH_PHYTIUM
  9. /*
  10. * Always set default iommu type to IOMMU_DOMAIN_IDENTITY
  11. * on Phytium FT-2000+ SoC to avoid unnecessary troubles
  12. * introduced by the SMMU workaround.
  13. */
  14. if ((read_cpuid_id() & MIDR_CPU_MODEL_MASK) == MIDR_PHYTIUM_FT2000PLUS)
  15. iommu_def_domain_type = IOMMU_DOMAIN_IDENTITY;
  16. else
  17. iommu_def_domain_type = pt ? IOMMU_DOMAIN_IDENTITY : IOMMU_DOMAIN_DMA;
  18. #else
  19. iommu_def_domain_type = pt ? IOMMU_DOMAIN_IDENTITY : IOMMU_DOMAIN_DMA;
  20. #endif
  21. return 0;
  22. }
  23. early_param("iommu.passthrough", iommu_set_def_domain_type);

分配iommu_domain: 

  1. static struct iommu_domain *__iommu_domain_alloc(struct bus_type *bus,
  2. unsigned type)
  3. {
  4. struct iommu_domain *domain;
  5. if (bus == NULL || bus->iommu_ops == NULL)
  6. return NULL;
  7. domain = bus->iommu_ops->domain_alloc(type);
  8. //调用总线中的iommu_ops->domain_alloc 。 比如pci_bus的,在arm-smmu.c 的arm_smmu_bus_init 函数。
  9. if (!domain)
  10. return NULL;
  11. domain->ops = bus->iommu_ops;
  12. domain->type = type;
  13. /* Assume all sizes by default; the driver may override this later */
  14. domain->pgsize_bitmap = bus->iommu_ops->pgsize_bitmap;
  15. return domain;
  16. }
  17. struct iommu_domain *iommu_domain_alloc(struct bus_type *bus)
  18. {
  19. return __iommu_domain_alloc(bus, IOMMU_DOMAIN_UNMANAGED);
  20. }
  21. EXPORT_SYMBOL_GPL(iommu_domain_alloc);

arm64_swiotlb_dma_ops :alloc函数

  1. static void *__dma_alloc(struct device *dev, size_t size,
  2. dma_addr_t *dma_handle, gfp_t flags,
  3. unsigned long attrs)
  4. {
  5. struct page *page;
  6. void *ptr, *coherent_ptr;
  7. bool coherent = is_device_dma_coherent(dev);
  8. pgprot_t prot = __get_dma_pgprot(attrs, PAGE_KERNEL, false);
  9. size = PAGE_ALIGN(size);
  10. if (!coherent && !gfpflags_allow_blocking(flags)) {
  11. struct page *page = NULL;
  12. void *addr = __alloc_from_pool(size, &page, flags);
  13. if (addr)
  14. *dma_handle = phys_to_dma(dev, page_to_phys(page));
  15. return addr;
  16. }
  17. //走这里 。。。。。。。。。。
  18. ptr = swiotlb_alloc(dev, size, dma_handle, flags, attrs);
  19. if (!ptr)
  20. goto no_mem;
  21. /* no need for non-cacheable mapping if coherent */
  22. if (coherent)
  23. return ptr;
  24. /* remove any dirty cache lines on the kernel alias */
  25. __dma_flush_area(ptr, size);
  26. /* create a coherent mapping */
  27. page = virt_to_page(ptr);
  28. coherent_ptr = dma_common_contiguous_remap(page, size, VM_USERMAP,
  29. prot, __builtin_return_address(0));
  30. if (!coherent_ptr)
  31. goto no_map;
  32. return coherent_ptr;
  33. no_map:
  34. swiotlb_free(dev, size, ptr, *dma_handle, attrs);
  35. no_mem:
  36. return NULL;
  37. }

 swiotlb_alloc函数: 

  1. void *swiotlb_alloc(struct device *dev, size_t size, dma_addr_t *dma_handle,
  2. gfp_t gfp, unsigned long attrs)
  3. {
  4. void *vaddr;
  5. /* temporary workaround: */
  6. if (gfp & __GFP_NOWARN)
  7. attrs |= DMA_ATTR_NO_WARN;
  8. /*
  9. * Don't print a warning when the first allocation attempt fails.
  10. * swiotlb_alloc_coherent() will print a warning when the DMA memory
  11. * allocation ultimately failed.
  12. */
  13. gfp |= __GFP_NOWARN;
  14. //debug发现使用dma_direct_alloc
  15. vaddr = dma_direct_alloc(dev, size, dma_handle, gfp, attrs);
  16. if (!vaddr)
  17. vaddr = swiotlb_alloc_buffer(dev, size, dma_handle, attrs);
  18. return vaddr;
  19. }
  20. //kernel/dma/direct.c
  21. void *dma_direct_alloc(struct device *dev, size_t size, dma_addr_t *dma_handle,
  22. gfp_t gfp, unsigned long attrs)
  23. {
  24. unsigned int count = PAGE_ALIGN(size) >> PAGE_SHIFT;
  25. int page_order = get_order(size);
  26. struct page *page = NULL;
  27. void *ret;
  28. /* we always manually zero the memory once we are done: */
  29. gfp &= ~__GFP_ZERO;
  30. /* GFP_DMA32 and GFP_DMA are no ops without the corresponding zones: */
  31. if (dev->coherent_dma_mask <= DMA_BIT_MASK(ARCH_ZONE_DMA_BITS))
  32. gfp |= GFP_DMA;
  33. if (dev->coherent_dma_mask <= DMA_BIT_MASK(32) && !(gfp & GFP_DMA))
  34. gfp |= GFP_DMA32;
  35. again:
  36. /* CMA can be used only in the context which permits sleeping */
  37. if (gfpflags_allow_blocking(gfp)) {
  38. page = dma_alloc_from_contiguous(dev, count, page_order,
  39. gfp & __GFP_NOWARN);
  40. if (page && !dma_coherent_ok(dev, page_to_phys(page), size)) {
  41. dma_release_from_contiguous(dev, page, count);
  42. page = NULL;
  43. }
  44. }
  45. if (!page)
  46. page = alloc_pages_node(dev_to_node(dev), gfp, page_order);
  47. if (page && !dma_coherent_ok(dev, page_to_phys(page), size)) {
  48. __free_pages(page, page_order);
  49. page = NULL;
  50. if (IS_ENABLED(CONFIG_ZONE_DMA32) &&
  51. dev->coherent_dma_mask < DMA_BIT_MASK(64) &&
  52. !(gfp & (GFP_DMA32 | GFP_DMA))) {
  53. gfp |= GFP_DMA32;
  54. goto again;
  55. }
  56. if (IS_ENABLED(CONFIG_ZONE_DMA) &&
  57. dev->coherent_dma_mask < DMA_BIT_MASK(32) &&
  58. !(gfp & GFP_DMA)) {
  59. gfp = (gfp & ~GFP_DMA32) | GFP_DMA;
  60. goto again;
  61. }
  62. }
  63. if (!page)
  64. return NULL;
  65. ret = page_address(page);
  66. if (force_dma_unencrypted()) {
  67. set_memory_decrypted((unsigned long)ret, 1 << page_order);
  68. *dma_handle = __phys_to_dma(dev, page_to_phys(page));
  69. } else {
  70. *dma_handle = phys_to_dma(dev, page_to_phys(page));
  71. }
  72. memset(ret, 0, size);
  73. return ret;
  74. }

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

  1. struct iommu_group *pci_device_group(struct device *dev)
  2. {
  3. struct pci_dev *pdev = to_pci_dev(dev);
  4. /*
  5. * Find the upstream DMA alias for the device. A device must not
  6. * be aliased due to topology in order to have its own IOMMU group.
  7. * If we find an alias along the way that already belongs to a
  8. * group, use it.
  9. */
  10. if (pci_for_each_dma_alias(pdev, get_pci_alias_or_group, &data))
  11. return data.group;
  12. /*
  13. * Continue upstream from the point of minimum IOMMU granularity
  14. * due to aliases to the point where devices are protected from
  15. * peer-to-peer DMA by PCI ACS. Again, if we find an existing
  16. * group, use it.
  17. */
  18. for (bus = pdev->bus; !pci_is_root_bus(bus); bus = bus->parent) {
  19. if (!bus->self)
  20. continue;
  21. if (pci_acs_path_enabled(bus->self, NULL, REQ_ACS_FLAGS))
  22. break;
  23. pdev = bus->self;
  24. group = iommu_group_get(&pdev->dev);
  25. if (group)
  26. return group;
  27. }
  28. /*
  29. * Look for existing groups on device aliases. If we alias another
  30. * device or another device aliases us, use the same group.
  31. */
  32. group = get_pci_alias_group(pdev, (unsigned long *)devfns);
  33. if (group)
  34. return group;
  35. /*
  36. * Look for existing groups on non-isolated functions on the same
  37. * slot and aliases of those funcions, if any. No need to clear
  38. * the search bitmap, the tested devfns are still valid.
  39. */
  40. group = get_pci_function_alias_group(pdev, (unsigned long *)devfns);
  41. if (group)
  42. return group;
  43. return iommu_group_alloc();
  44. }
  45. 这个函数的核心逻辑在于pci_acs_path_enabled,简单来说如果是pcie的设备则检查该设备到root complex的路径上如果都开启了ACS则这个设备就单独成一个iommu_group,如果不是则找到它的alias group就行了比如如果这个是传统的pci bus(没有pcie这些ACS的特性)则这个pci bus下面的所有设备就组合成一个iommu_group

开启smmu后dmam_alloc_coherent:

使用

  1. static const struct dma_map_ops iommu_dma_ops = {
  2. .alloc = __iommu_alloc_attrs,
  3. .free = __iommu_free_attrs,
  4. .mmap = __iommu_mmap_attrs,
  5. .get_sgtable = __iommu_get_sgtable,
  6. .map_page = __iommu_map_page,
  7. .unmap_page = __iommu_unmap_page,
  8. .map_sg = __iommu_map_sg_attrs,
  9. .unmap_sg = __iommu_unmap_sg_attrs,
  10. .sync_single_for_cpu = __iommu_sync_single_for_cpu,
  11. .sync_single_for_device = __iommu_sync_single_for_device,
  12. .sync_sg_for_cpu = __iommu_sync_sg_for_cpu,
  13. .sync_sg_for_device = __iommu_sync_sg_for_device,
  14. .map_resource = iommu_dma_map_resource,
  15. .unmap_resource = iommu_dma_unmap_resource,
  16. .mapping_error = iommu_dma_mapping_error,
  17. };

其中__iommu_alloc_attrs调用iommu_dma_alloc 申请page页:

  1. struct page **iommu_dma_alloc(struct device *dev, size_t size, gfp_t gfp,
  2. unsigned long attrs, int prot, dma_addr_t *handle,
  3. void (*flush_page)(struct device *, const void *, phys_addr_t))
  4. {
  5. //这里申请任意物理地址页。
  6. count = PAGE_ALIGN(size) >> PAGE_SHIFT;
  7. pages = __iommu_dma_alloc_pages(count, alloc_sizes >> PAGE_SHIFT, gfp);
  8. if (!pages)
  9. return NULL;
  10. //这里申请iova是dma能访问的4G以下的虚拟地址。
  11. size = iova_align(iovad, size);
  12. iova = iommu_dma_alloc_iova(domain, size, dev->coherent_dma_mask, dev);
  13. if (!iova)
  14. goto out_free_pages;
  15. if (sg_alloc_table_from_pages(&sgt, pages, count, 0, size, GFP_KERNEL))
  16. goto out_free_iova;
  17. if (!(prot & IOMMU_CACHE)) {
  18. struct sg_mapping_iter miter;
  19. /*
  20. * The CPU-centric flushing implied by SG_MITER_TO_SG isn't
  21. * sufficient here, so skip it by using the "wrong" direction.
  22. */
  23. sg_miter_start(&miter, sgt.sgl, sgt.orig_nents, SG_MITER_FROM_SG);
  24. while (sg_miter_next(&miter))
  25. flush_page(dev, miter.addr, page_to_phys(miter.page));
  26. sg_miter_stop(&miter);
  27. }
  28. //建立映射。
  29. if (iommu_map_sg(domain, iova, sgt.sgl, sgt.orig_nents, prot)
  30. < size)
  31. goto out_free_sg;
  32. *handle = iova;
  33. sg_free_table(&sgt);
  34. return pages;
  35. }

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/292388
推荐阅读
相关标签
  

闽ICP备14008679号