首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

Linux DMA详解 (7)

Linux DMA详解 (7)

//初始化dma_pool结构对象retval
        INIT_LIST_HEAD (&retval->page_list);//
初始化页链表
        spin_lock_init (&retval->lock);
        retval->size = size;
        retval->allocation = allocation;
        retval->blocks_per_page = allocation / size;
        init_waitqueue_head (&retval->waitq);//
初始化等待队列

        if (dev) {//
设备存在时
                down (&pools_lock);
                if (list_empty (&dev->dma_pools))
//
给设备创建sysfs文件系统属性文件
                        device_create_file (dev, &dev_attr_pools);
                /* note:  not currently insisting "name" be unique */
                list_add (&retval->pools, &dev->dma_pools);//
DMA池加到dev
                up (&pools_lock);
        } else
                INIT_LIST_HEAD (&retval->pools);

        return retval;
}

  

函数dma_pool_allocDMA池中分配一块一致内存,其参数pool是将产生块的DMA池,参数mem_flagsGFP_*位掩码,参数handle是指向块的DMA地址,函数dma_pool_alloc返回当前没用的块的内核虚拟地址,并通过handle给出它的DMA地址,如果内存块不能被分配,返回null

  

函数dma_pool_alloc包裹了dma_alloc_coherent页分配器,这样小块更容易被总线的主控制器使用。这可能共享slab分配器的内容。

  

函数dma_pool_alloc分析如下(在drivers/base/dmapool.c中):

  

void *dma_pool_alloc (struct dma_pool *pool, int mem_flags, dma_addr_t *handle)
{
        unsigned long                flags;
        struct dma_page                *page;
        int                        map, block;
        size_t                        offset;
        void                        *retval;

restart:
        spin_lock_irqsave (&pool->lock, flags);
        list_for_each_entry(page, &pool->page_list, page_list) {
                int                i;
                /* only cachable accesses here ... */
        //
遍历一页的每块,而每块又以32字节递增
                for (map = 0, i = 0;
                                i < pool->blocks_per_page;//
每页的块数
                                i += BITS_PER_LONG, map++) {// BITS_PER_LONG
定义为32
                        if (page->bitmap [map] == 0)
                                continue;
                        block = ffz (~ page->bitmap [map]);//
找出第一个0
                        if ((i + block) < pool->blocks_per_page) {
                                clear_bit (block, &page->bitmap [map]);
//
得到相对于页边界的偏移
                                offset = (BITS_PER_LONG * map) + block;
                                offset *= pool->size;
                                goto ready;
                        }
                }
        }
//
DMA池分配dma_page结构空间,加入到pool->page_list链表,
//
并作DMA一致映射,它包括分配给DMA池一页。
// SLAB_ATOMIC
表示调用 kmallocGFP_ATOMIC)直到失败为止,
//
然后它等待内核释放若干页面,接下来再一次进行分配。
        if (!(page = pool_alloc_page (pool, SLAB_ATOMIC))) {
                if (mem_flags & __GFP_WAIT) {
                        DECLARE_WAITQUEUE (wait, current);

                        current->state = TASK_INTERRUPTIBLE;
                        add_wait_queue (&pool->waitq, &wait);
                        spin_unlock_irqrestore (&pool->lock, flags);

                        schedule_timeout (POOL_TIMEOUT_JIFFIES);

                        remove_wait_queue (&pool->waitq, &wait);
                        goto restart;
                }
                retval = NULL;
                goto done;
        }

        clear_bit (0, &page->bitmap [0]);
        offset = 0;
ready:
        page->in_use++;
        retval = offset + page->vaddr;//
返回虚拟地址
        *handle = offset + page->dma;//
相对DMA地址
#ifdef        CONFIG_DEBUG_SLAB
        memset (retval, POOL_POISON_ALLOCATED, pool->size);
#endif
done:
        spin_unlock_irqrestore (&pool->lock, flags);
        return retval;
}

继承事业,薪火相传
返回列表