使用dma_alloc_writecombine申请内存空间大小的限制

来源:互联网 发布:我的校园软件 编辑:程序博客网 时间:2024/05/21 15:03

最近在调TFT分辨率,当使用1024x768 16bpp时正常,而当调整为1024x768 24bpp时报"Failed to allocate video memory"的错误.

出错的地方是在分配显示缓冲区.代码如下:
static __inline int allocate_video_memory_map(struct fb_info *fbinfo)
{
    ...
 fbinfo->screen_base = dma_alloc_writecombine(fbi->dev, map_size, (dma_addr_t *)&fbinfo->fix.smem_start, GFP_KERNEL);

 if (!fbinfo->screen_base) {
  return -ENOMEM;
 }

 return 0;
}
...
{
    ret = allocate_video_memory_map(fbinfo);
    if (ret) {
     dev_err(fbi->dev, "Failed to allocate video memory ma (%d)\n", ret);
     return -ENOMEM;
    }
}

dma_alloc_writecombine函数
/*
 * Allocate DMA-coherent memory space and return both the kernel remapped
 * virtual and bus address for that space.
 */
void *
dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp)
{
 void *memory;

 if (dma_alloc_from_coherent(dev, size, handle, &memory))
  return memory;

 if (arch_is_coherent()) {
  void *virt;

  virt = kmalloc(size, gfp);
  if (!virt)
   return NULL;
  *handle =  virt_to_dma(dev, virt);

  return virt;
 }

 return __dma_alloc(dev, size, handle, gfp,
      pgprot_noncached(pgprot_kernel));
}
EXPORT_SYMBOL(dma_alloc_coherent);

/*
 * Allocate a writecombining region, in much the same way as
 * dma_alloc_coherent above.
 */
void *
dma_alloc_writecombine(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp)
{
 return __dma_alloc(dev, size, handle, gfp,
      pgprot_writecombine(pgprot_kernel));
}
EXPORT_SYMBOL(dma_alloc_writecombine);


由上方代码可以看出,两个函数都调用了__dma_alloc函数,差别只在于最后一个参数。
dma_alloc_coherent 在 arm 平台上会禁止页表项中的 C (Cacheable) 域以及 B (Bufferable)域。而 dma_alloc_writecombine 只禁止 C (Cacheable) 域.

 

 C 代表是否应用高速缓冲存储器, 而 B 代表是否应用写缓冲区。

 

如许,dma_alloc_writecombine 分派出来的内存不应用缓存,然则会应用写缓冲区。而 dma_alloc_coherent  则二者都不应用。

 

C B 位的具体含义
0 0 无cache,无写缓冲;任何对memory的读写都反应到总线上。对 memory 的操纵过程中CPU须要守候。
0 1 无cache,有写缓冲;读操纵直接反应到总线上;写操纵,CPU将数据写入到写缓冲后持续运行,由写缓冲进行写回操纵。
1 0 有cache,写通模式;读操纵起首推敲cache hit;写操纵时直接将数据写入写缓冲,若是同时呈现cache hit,那么也更新cache。
1 1 有cache,写回模式;读操纵起首推敲cache hit;写操纵也起首推敲cache hit。

如许,两者的差别就很清楚了。

 

 

static void *
__dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp,
     pgprot_t prot)
{
 struct page *page;
 struct arm_vm_region *c;
 unsigned long order;
 u64 mask = ISA_DMA_THRESHOLD, limit;

 if (!consistent_pte[0]) {
  printk(KERN_ERR "%s: not initialised\n", __func__);
  dump_stack();
  return NULL;
 }

 if (dev) {
  mask = dev->coherent_dma_mask;

  /*
   * Sanity check the DMA mask - it must be non-zero, and
   * must be able to be satisfied by a DMA allocation.
   */
  if (mask == 0) {
   dev_warn(dev, "coherent DMA mask is unset\n");
   goto no_page;
  }

  if ((~mask) & ISA_DMA_THRESHOLD) {
   dev_warn(dev, "coherent DMA mask %#llx is smaller "
     "than system GFP_DMA mask %#llx\n",
     mask, (unsigned long long)ISA_DMA_THRESHOLD);
   goto no_page;
  }
 }

 /*
  * Sanity check the allocation size.
  */
 size = PAGE_ALIGN(size);
 limit = (mask + 1) & ~mask;
 if ((limit && size >= limit) ||
     size >= (CONSISTENT_END - CONSISTENT_BASE)) {
  printk(KERN_WARNING "coherent allocation too big "
         "(requested %#x mask %#llx)\n", size, mask);
  goto no_page;
 }

 order = get_order(size);

 if (mask != 0xffffffff)
  gfp |= GFP_DMA;

 page = alloc_pages(gfp, order);
 if (!page)
  goto no_page;

 /*
  * Invalidate any data that might be lurking in the
  * kernel direct-mapped region for device DMA.
  */
 {
  void *ptr = page_address(page);
  memset(ptr, 0, size);
  dmac_flush_range(ptr, ptr + size);
  outer_flush_range(__pa(ptr), __pa(ptr) + size);
 }

 /*
  * Allocate a virtual address in the consistent mapping region.
  */
 c = arm_vm_region_alloc(&consistent_head, size,
       gfp & ~(__GFP_DMA | __GFP_HIGHMEM));
 if (c) {
  pte_t *pte;
  struct page *end = page + (1 << order);
  int idx = CONSISTENT_PTE_INDEX(c->vm_start);
  u32 off = CONSISTENT_OFFSET(c->vm_start) & (PTRS_PER_PTE-1);

  pte = consistent_pte[idx] + off;
  c->vm_pages = page;

  split_page(page, order);

  /*
   * Set the "dma handle"
   */
  *handle = page_to_dma(dev, page);

  do {
   BUG_ON(!pte_none(*pte));

   /*
    * x86 does not mark the pages reserved...
    */
   SetPageReserved(page);
   set_pte_ext(pte, mk_pte(page, prot), 0);
   page++;
   pte++;
   off++;
   if (off >= PTRS_PER_PTE) {
    off = 0;
    pte = consistent_pte[++idx];
   }
  } while (size -= PAGE_SIZE);

  /*
   * Free the otherwise unused pages.
   */
  while (page < end) {
   __free_page(page);
   page++;
  }

  return (void *)c->vm_start;
 }

 if (page)
  __free_pages(page, order);
 no_page:
 *handle = ~0;
 return NULL;
}

 

在文件arch/arm/include/asm/memory.h中找到有关CONSISTENT_DMA_SIZE的定义:

/* * Size of DMA-consistent memory region.  Must be multiple of 2M, * between 2MB and 14MB inclusive. */#ifndef CONSISTENT_DMA_SIZE#define CONSISTENT_DMA_SIZE SZ_2M#endif



而文件arch/arm/include/asm/memory.h定义的是Linux下整个ARM体系的公共参数,如果直接修改会引起别的平台的一些不可预料的结果.
幸运的是在memory.h文件开头有一个include文件
#include <mach/memory.h>


找到<mach/memory.h>文件arch/arm/mach-xxx/include/mach/memory.h,在其中添加CONSISTENT_DMA_SIZE的定义即可,如:

#define CONSISTENT_DMA_SIZE SZ_4M


 

 

原创粉丝点击