//初始化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_alloc从DMA池中分配一块一致内存,其参数pool是将产生块的DMA池,参数mem_flags是GFP_*位掩码,参数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表示调用 kmalloc(GFP_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;
} |