在内核中有时需要申请一段大内存,方法之一是可以采取如下方法:

示例: 如何将1M的物理内存作为私人使用(假设物理内存大小为256M):

1. 在内核启动时,通过mem=255M参数,让内核只能使用255M的空间。

2. 然后通过如下调用来使用这个1M的私人空间:

   dmabuf= ioremap (0xFF00000 /* 255M */, 0x100000 /* 1M */);

不过,这种方法不宜使用在开启了高内存的系统上。


DMA

默认情况下,Linux内核都假定设备都能在32位地址上进行DMA操作,如果不是这样,那么需要通过如下调用来告知内核:

int dma_set_mask(struct device *dev, u64mask);

下面是一个只支持24位地址DMA操作的示例:

if (dma_set_mask (dev, 0xffffff))
card->use_dma = 1;
else {
card->use_dma = 0; /* We'll have to live without DMA */
printk (KERN_WARN, "mydev: DMA not supported\n");
}

当然,如果设备本身支持32位DMA操作,则没有必要调用dma_set_mask。

 

DMA映射(大块数据分配)

建立DMA映射包含两个步骤:

1.分配一个DMA缓冲空间。

2. 为该缓冲空间生成一个设备可访问的地址。

 

DMA映射中需要处理cache一致性的问题。

表示总线地址的数据类型:dma_addr_t。

 

根据DMA缓冲区存在时间的长短,有两种类型的DMA映射:

1. 一致性DMA映射(Coherent DMA mappings)

   这种映射在驱动的生命同期中一直存在。一致缓冲区必须同时对CPU和外设可用,所以一致映射必须存在于cache-cohrent内存中。这种映射使用和建立的代价比较高。

2. 流式DMA映射(Streaming DMA mappings)

   流式映射是一种短期的映射,它支持一个或多个DMA操作。根据体系结构的要求,可能会涉及到创建bounce buffer, IOMMU寄存器编程,冲刷处理Caches等。当然,这种方式下,对缓冲区的访问要受制于一些苛刻的规则,特别是对缓冲区的所有权,当映射建立后,缓冲区的所有权属于设备,处理器不能访问或修改它。一般应尽可能地使用这种DMA映射方式,理由如下:1. 一致性DMA映射会独占映射中使用到的寄存器,导致其他模块无法访问。2. 流式DMA映射有更多的优化方式。

 

通过如下函数可以建立一致映射:

/**
 * dma_alloc_coherent - allocate consistent memory for DMA
 * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices
 * @size: required memory size
 * @handle: bus-specific DMA address
 *
 * Allocate some uncached, unbuffered memory for a device for
 * performing DMA.  This function allocates pages, and will
 * return the CPU-viewed address, and sets @handle to be the
 * device-viewed address.
 */
void *dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, int flag);

上述函数分配的BUFFER大小至少是一个页的大小, 属于大内存分配的情形。

通过如下函数可以将DMA Buffer映射到请求的VMA中:

/**
 * dma_mmap_coherent - map a coherent DMA allocation into user space
 * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices
 * @vma: vm_area_struct describing requested user mapping
 * @cpu_addr: kernel CPU-view address returned from dma_alloc_coherent
 * @handle: device-view address returned from dma_alloc_coherent
 * @size: size of memory originally requested in dma_alloc_coherent
 *
 * Map a coherent DMA buffer previously allocated by dma_alloc_coherent
 * into user space.  The coherent DMA buffer must not be freed by the
 * driver until the user space mapping has been released.
 */
int dma_mmap_coherent(struct device *dev, struct vm_area_struct *vma,
		void *cpu_addr, dma_addr_t handle, size_t size);

DMA池

DMA池是针对小的,一致性DMA映射的分配机制。相关函数:

struct dma_pool *dma_pool_create(const char *name, struct device *dev,
size_t size, size_t align,
size_t allocation);

void dma_pool_destroy(struct dma_pool *pool);

void *dma_pool_alloc(struct dma_pool *pool, int mem_flags,
dma_addr_t *handle);

void dma_pool_free(struct dma_pool *pool, void *vaddr, dma_addr_t addr);

对于流式DMA映射,接口要复杂些。建立映射时,需要指定数据移动的方向,根据不同的目的,有如下一些选项:

DMA_TO_DEVICE

data is being sent to the device (in response, perhaps, to a write system call), DMA_TO_DEVICE should be used;

DMA_FROM_DEVICE

data going to the CPU, is marked with DMA_FROM_DEVICE.

DMA_BIDIRECTIONAL

If data can move in either direction, use DMA_BIDIRECTIONAL

DMA_NONE

only for debug purpose, should never use

传输单个缓冲区:

dma_addr_t dma_map_single(struct device *dev, void *buffer, size_t size,
enum dma_data_direction direction);
void dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size,
enum dma_data_direction direction);

一旦缓冲区被映射后,它只属于设备,驱动不能访问。如果一定要在映射期间访问缓冲区的内容,必须调用如下相关的接口:

void dma_sync_single_for_cpu(struct device *dev, dma_handle_t bus_addr,
size_t size, enum dma_data_direction direction);
…
void dma_sync_single_for_device(struct device *dev, dma_handle_t bus_addr,
size_t size, enum dma_data_direction direction);

单页流式映射

有时,要映射包含struct page指针的缓冲区,可使用如下接口:

dma_addr_t dma_map_page(struct device *dev, struct page *page,
unsigned long offset, size_t size,
enum dma_data_direction direction);
void dma_unmap_page(struct device *dev, dma_addr_t dma_address,
size_t size, enum dma_data_direction direction);

代码示例:

static u32 _kernel_page_allocate(void)
{
	struct page *new_page;
	u32 linux_phys_addr;
	
	new_page = alloc_page(GFP_HIGHUSER | __GFP_ZERO | __GFP_REPEAT | __GFP_NOWARN | __GFP_COLD);
	
	if ( NULL == new_page )
	{
		return 0;
	}

	/* Ensure page is flushed from CPU caches. */
	linux_phys_addr = dma_map_page(NULL, new_page, 0, PAGE_SIZE, DMA_BIDIRECTIONAL);

	return linux_phys_addr;
}

static void _kernel_page_release(u32 physical_address)
{
	struct page *unmap_page;

	#if 1
	dma_unmap_page(NULL, physical_address, PAGE_SIZE, DMA_BIDIRECTIONAL);
	#endif
	
	unmap_page = pfn_to_page( physical_address >> PAGE_SHIFT );
	MALI_DEBUG_ASSERT_POINTER( unmap_page );
	__free_page( unmap_page );
}


scatter/gather I/O

它是一种特殊的流式DMA映射,将多个BUFFER,通过一个DMA操作,从或往设备传输数据。



更多推荐

大块数据申请及DMA