smmu 学习笔记之get

    xiaoxiao2021-03-25  114

    iommu的get_sgtable 函数实现如下: static int __iommu_get_sgtable(struct device *dev, struct sg_table *sgt,                    void *cpu_addr, dma_addr_t dma_addr,                    size_t size, unsigned long attrs) {     unsigned int count = PAGE_ALIGN(size) >> PAGE_SHIFT;     struct vm_struct *area = find_vm_area(cpu_addr);     if (WARN_ON(!area || !area->pages))         return -ENXIO;     return sg_alloc_table_from_pages(sgt, area->pages, count, 0, size,                      GFP_KERNEL); } 在__iommu_get_sgtable 中首先将需要申请的size转换成page number,即总共需要申请多少个page,并按page size对其。 unsigned int count = PAGE_ALIGN(size) >> PAGE_SHIFT; 然后通过find_vm_area 找到cpu_addr 对应的vm_struct,也就是落在vmalloc区域。找到这个vm_struct *area 就可以通过area->pages得到page address  检查area和area->pages 不为null后调用sg_alloc_table_from_pages,这个函数会申请并初始化一个sg table int sg_alloc_table_from_pages(struct sg_table *sgt,     struct page **pages, unsigned int n_pages,     unsigned long offset, unsigned long size,     gfp_t gfp_mask) {     unsigned int chunks;     unsigned int i;     unsigned int cur_page;     int ret;     struct scatterlist *s;     /* compute number of contiguous chunks */     chunks = 1;     for (i = 1; i < n_pages; ++i)         if (page_to_pfn(pages[i]) != page_to_pfn(pages[i - 1]) + 1)             ++chunks;     ret = sg_alloc_table(sgt, chunks, gfp_mask);     if (unlikely(ret))         return ret;     /* merging chunks and putting them into the scatterlist */     cur_page = 0;     for_each_sg(sgt->sgl, s, sgt->orig_nents, i) {         unsigned long chunk_size;         unsigned int j;         /* look for the end of the current chunk */         for (j = cur_page + 1; j < n_pages; ++j)             if (page_to_pfn(pages[j]) !=                 page_to_pfn(pages[j - 1]) + 1)                 break;         chunk_size = ((j - cur_page) << PAGE_SHIFT) - offset;         sg_set_page(s, pages[cur_page], min(size, chunk_size), offset);         size -= chunk_size;         offset = 0;         cur_page = j;     }     return 0; } 在sg_alloc_table_from_pages 中首先通过     for (i = 1; i < n_pages; ++i)         if (page_to_pfn(pages[i]) != page_to_pfn(pages[i - 1]) + 1)             ++chunks; 计算有多少个连续的page.例如pfg=1,2,5,6的话,这个chunks就等于2.这个值记录在chunks中,然后根据chunks 来申请sg_table *sgt 然后这个for_each_sg(sgt->sgl, s, sgt->orig_nents, i) 会变量所有的chunks。这里的 sgt->orig_nents就等于chunks         /* look for the end of the current chunk */         for (j = cur_page + 1; j < n_pages; ++j)             if (page_to_pfn(pages[j]) !=                 page_to_pfn(pages[j - 1]) + 1)                 break; 这段code找到从page pfn 0开始的第一个chunks的size         chunk_size = ((j - cur_page) << PAGE_SHIFT) - offset; 上面的code计算当前chunk szize 最后通过    sg_set_page(s, pages[cur_page], min(size, chunk_size), offset);将chunks的offset和size存到到scatterlist *sg static inline void sg_set_page(struct scatterlist *sg, struct page *page,                    unsigned int len, unsigned int offset) {     sg_assign_page(sg, page);     sg->offset = offset;     sg->length = len; } for循环继续直到遍历所有的chunk
    转载请注明原文地址: https://ju.6miu.com/read-9996.html

    最新回复(0)