DMA pools的使用

前面我们讲申请大于一个page size的时候用dma_alloc_coherent,如果小于一个page size,则可以使用DMA-coherent buffers.
在使用DMA-coherent buffers 之前首先要调用来create buffer.
    struct dma_pool *
    dma_pool_create(const char *name, struct device *dev,
            size_t size, size_t align, size_t alloc);
然后就可以调用dma_pool_alloc 从前面已经create的pool中申请buffer
    void *dma_pool_alloc(struct dma_pool *pool, gfp_t gfp_flags,
            dma_addr_t *dma_handle);

void *dma_pool_zalloc(struct dma_pool *pool, gfp_t mem_flags,
                  dma_addr_t *handle)
和dma_pool_alloc 类似,会把申请的memory清零.
buffer 使用完后调用dma_pool_free 来释放buffer
最后调用dma_pool_destroy 来销毁之前申请的pool。
在dma_pool_create 中 可以指定align ,如果没有指定align,最小size是1个byte.
    if (align == 0)
        align = 1;
    else if (align & (align - 1))
        return NULL;

申请的size 最小是4个byte
    if (size == 0)
        return NULL;
    else if (size < 4)
        size = 4;
申请的size 要align,也可以看出调用最大只能申请一个page的size.
    if ((size % align) != 0)
        size = ALIGN(size, align);

    allocation = max_t(size_t, size, PAGE_SIZE);
边界检查,边界必须小于size,且必须是2的指数.
    if (!boundary)
        boundary = allocation;
    else if ((boundary < size) || (boundary & (boundary - 1)))
        return NULL;
原来也是通过kmalloc 申请,然后给retval 赋值.
    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);
如果dev->dma_pools 为null,则说明是第一次申请
    if (list_empty(&dev->dma_pools))
        empty = true;
    list_add(&retval->pools, &dev->dma_pools);

如果是第一次申请,则创建sysfs attribute

    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;
        }
    }
创建sysfs attribute,主要是调用下面的函数.
show_pools(struct device *dev, struct device_attribute *attr, char *buf)
{
    unsigned temp;
    unsigned size;
    char *next;
    struct dma_page *page;
    struct dma_pool *pool;

    next = buf;
    size = PAGE_SIZE;

    temp = scnprintf(next, size, "poolinfo - 0.1\n");
    size -= temp;
    next += temp;

    mutex_lock(&pools_lock);
    list_for_each_entry(pool, &dev->dma_pools, pools) {
        unsigned pages = 0;
        unsigned blocks = 0;

        spin_lock_irq(&pool->lock);
        list_for_each_entry(page, &pool->page_list, page_list) {
            pages++;
            blocks += page->in_use;
        }
        spin_unlock_irq(&pool->lock);

        /* per-pool info, no real statistics yet */
        temp = scnprintf(next, size, "%-16s %4u %4Zu %4Zu %2u\n",
                 pool->name, blocks,
                 pages * (pool->allocation / pool->size),
                 pool->size, pages);
        size -= temp;
        next += temp;
    }
    mutex_unlock(&pools_lock);

    return PAGE_SIZE - size;
}

static DEVICE_ATTR(pools, S_IRUGO, show_pools, NULL);


调用dma_pool_alloc 从已经申请好的pool中查找memroy。
首先查找释放有足够的memory 来申请.
    list_for_each_entry(page, &pool->page_list, page_list) {
        if (page->offset < pool->allocation)
            goto ready;
    }
如果没有的话,调用pool_alloc_page 申请buffer,然后添加到原来的pool中
    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);
所以调用dma_pool_alloc 肯定会找到pool来申请,如果没有就申请memory再添加到原来的pool中,也算是一种lazy allocation。
    page->in_use++;
    offset = page->offset;
    page->offset = *(int *)(page->vaddr + offset);
    retval = offset + page->vaddr;
    *handle = offset + page->dma;
计算后,最后返回retval,这个就是物理地址。
需要注意的是pool_alloc_page 函数中首先通过kmalloc 来申请,如果failed 就调用dma_alloc_coherent 来申请。
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 memory没人踩坏可以打开DMAPOOL_DEBUG,复制到现象是就会打印下面的log
WARNING: at /data2/repos/linux-2.6-iommu/lib/dma-debug.c:448
    check_unmap+0x203/0x490()
Hardware name:
forcedeth 0000:00:08.0: DMA-API: device driver frees DMA memory with wrong
    function [device address=0x00000000640444be] [size=66 bytes] [mapped as
single] [unmapped as page]
Modules linked in: nfsd exportfs bridge stp llc r8169
Pid: 0, comm: swapper Tainted: G        W  2.6.28-dmatest-09289-g8bb99c0 #1
Call Trace:
 <IRQ>  [<ffffffff80240b22>] warn_slowpath+0xf2/0x130
 [<ffffffff80647b70>] _spin_unlock+0x10/0x30
 [<ffffffff80537e75>] usb_hcd_link_urb_to_ep+0x75/0xc0
 [<ffffffff80647c22>] _spin_unlock_irqrestore+0x12/0x40
 [<ffffffff8055347f>] ohci_urb_enqueue+0x19f/0x7c0
 [<ffffffff80252f96>] queue_work+0x56/0x60
 [<ffffffff80237e10>] enqueue_task_fair+0x20/0x50
 [<ffffffff80539279>] usb_hcd_submit_urb+0x379/0xbc0
 [<ffffffff803b78c3>] cpumask_next_and+0x23/0x40
 [<ffffffff80235177>] find_busiest_group+0x207/0x8a0
 [<ffffffff8064784f>] _spin_lock_irqsave+0x1f/0x50
 [<ffffffff803c7ea3>] check_unmap+0x203/0x490
 [<ffffffff803c8259>] debug_dma_unmap_page+0x49/0x50
 [<ffffffff80485f26>] nv_tx_done_optimized+0xc6/0x2c0
 [<ffffffff80486c13>] nv_nic_irq_optimized+0x73/0x2b0
 [<ffffffff8026df84>] handle_IRQ_event+0x34/0x70
 [<ffffffff8026ffe9>] handle_edge_irq+0xc9/0x150
 [<ffffffff8020e3ab>] do_IRQ+0xcb/0x1c0
 [<ffffffff8020c093>] ret_from_intr+0x0/0xa
 <EOI> <4>---[ end trace f6435a98e2a38c0e ]---
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Nginx server_pools是Nginx的一个功能模块,用于管理和负载均衡服务器池。服务器池是一组具有相似功能、性能和内容的服务器的集合,它们可以共同处理和提供网络服务。 使用Nginx的server_pools功能,我们可以将多个服务器组成一个逻辑组,并将请求分发给这些服务器。这样可以提高服务器的性能和可扩展性,同时也增加了系统的容错能力,避免单点故障。 在配置server_pools时,我们需要指定每个服务器的地址和端口,以及负载均衡策略。Nginx提供了多种负载均衡算法,如轮询、加权轮询、IP哈希等,可以根据具体需求选择适合的算法。 当用户发送请求时,Nginx会根据负载均衡算法选择一个服务器来处理请求。如果其中某个服务器故障或者超过了预设的负载阈值,Nginx会自动将请求转发给其他服务器。这样可以实现请求的高效处理和服务器资源的合理利用。 另外,Nginx server_pools还支持动态添加和移除服务器。这意味着我们可以在运行时动态调整服务器池的大小,根据实时的负载情况和服务器性能来灵活配置。这对于应对突发的请求量和实现应用的动态伸缩非常有用。 综上所述,Nginx server_pools是Nginx提供的一个有用的功能模块,可用于管理和负载均衡服务器池,帮助我们提高服务器性能和可用性,实现请求的高效分发和服务器资源的优化利用。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值