天天看點

ION原理分析ION初始化ION使用者空間使用ION核心空間使用參考文檔:

平台: MSM8x25Q

系統: Android4.1

從上一篇ION基本概念中,我們了解了heaptype, heap id, client, handle以及如何使用,本篇再從原理上分析下ION的運作流程。

         MSM8x25Q平台使用的是board-qrd7627.c,ION相關定義如下:

/**
 * These heaps are listed in the order they will be allocated.
 * Don't swap the order unless you know what you are doing!
 */
struct ion_platform_heap msm7627a_heaps[] = {
		{
			.id	= ION_SYSTEM_HEAP_ID,
			.type	= ION_HEAP_TYPE_SYSTEM,
			.name	= ION_VMALLOC_HEAP_NAME,
		},
#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
		/* PMEM_ADSP = CAMERA */
		{
			.id	= ION_CAMERA_HEAP_ID,
			.type	= CAMERA_HEAP_TYPE,
			.name	= ION_CAMERA_HEAP_NAME,
			.memory_type = ION_EBI_TYPE,
			.extra_data = (void *)&co_mm_ion_pdata,
			.priv	= (void *)&ion_cma_device.dev,
		},
		/* AUDIO HEAP 1*/
		{
			.id	= ION_AUDIO_HEAP_ID,
			.type	= ION_HEAP_TYPE_CARVEOUT,
			.name	= ION_AUDIO_HEAP_NAME,
			.memory_type = ION_EBI_TYPE,
			.extra_data = (void *)&co_ion_pdata,
		},
		/* PMEM_MDP = SF */
		{
			.id	= ION_SF_HEAP_ID,
			.type	= ION_HEAP_TYPE_CARVEOUT,
			.name	= ION_SF_HEAP_NAME,
			.memory_type = ION_EBI_TYPE,
			.extra_data = (void *)&co_ion_pdata,
		},
		/* AUDIO HEAP 2*/
		{
			.id    = ION_AUDIO_HEAP_BL_ID,
			.type  = ION_HEAP_TYPE_CARVEOUT,
			.name  = ION_AUDIO_BL_HEAP_NAME,
			.memory_type = ION_EBI_TYPE,
			.extra_data = (void *)&co_ion_pdata,
			.base = BOOTLOADER_BASE_ADDR,
		},

#endif
};

static struct ion_co_heap_pdata co_ion_pdata = {
	.adjacent_mem_id = INVALID_HEAP_ID,
	.align = PAGE_SIZE,
};

static struct ion_co_heap_pdata co_mm_ion_pdata = {
	.adjacent_mem_id = INVALID_HEAP_ID,
	.align = PAGE_SIZE,
};

static u64 msm_dmamask = DMA_BIT_MASK(32);

static struct platform_device ion_cma_device = {
	.name = "ion-cma-device",
	.id = -1,
	.dev = {
		.dma_mask = &msm_dmamask,
		.coherent_dma_mask = DMA_BIT_MASK(32),
	}
};
           

Qualcomm提示了不要輕易調換順序,因為後面代碼處理是将順序定死了的,一旦你調換了,代碼就無法正常運作了。

另外, 本系統中隻使用了ION_HEAP_TYPE_CARVEOUT和 ION_HEAP_TYPE_SYSTEM這兩種heap type.

對于ION_HEAP_TYPE_CARVEOUT的記憶體配置設定,後面将會發現,其實就是之前講述過的使用mem pool來配置設定的。

Platform device如下,在msm_ion.c中用到。

static struct ion_platform_data ion_pdata = {
	.nr = MSM_ION_HEAP_NUM,
	.has_outer_cache = 1,
	.heaps = msm7627a_heaps,
};

static struct platform_device ion_dev = {
	.name = "ion-msm",
	.id = 1,
	.dev = { .platform_data = &ion_pdata },
};
           

ION初始化

轉到msm_ion.c,ion.c的某些函數也被重新封裝了下.萬事都從裝置比對開始:

static struct platform_driver msm_ion_driver = {
	.probe = msm_ion_probe,
	.remove = msm_ion_remove,
	.driver = { .name = "ion-msm" }
};
static int __init msm_ion_init(void)
{
	/*調用msm_ion_probe */
	return platform_driver_register(&msm_ion_driver);
}

static int msm_ion_probe(struct platform_device *pdev)
{
	/*即board-qrd7627a.c中的ion_pdata */
	struct ion_platform_data *pdata = pdev->dev.platform_data;
	int err;
	int i;

	/*heap數量*/
	num_heaps = pdata->nr;
	/*配置設定struct ion_heap */
	heaps = kcalloc(pdata->nr, sizeof(struct ion_heap *), GFP_KERNEL);

	if (!heaps) {
		err = -ENOMEM;
		goto out;
	}
	/*建立節點,最終是/dev/ion,供使用者空間操作。*/
	idev = ion_device_create(NULL);
	if (IS_ERR_OR_NULL(idev)) {
		err = PTR_ERR(idev);
		goto freeheaps;
	}
	/*最終是根據adjacent_mem_id 是否定義了來配置設定相鄰記憶體,
我們沒用到,忽略此函數。*/
	msm_ion_heap_fixup(pdata->heaps, num_heaps);

	/* create the heaps as specified in the board file */
	for (i = 0; i < num_heaps; i++) {
		struct ion_platform_heap *heap_data = &pdata->heaps[i];
		/*配置設定ion*/
		msm_ion_allocate(heap_data);

		heap_data->has_outer_cache = pdata->has_outer_cache;
		/*建立ion heap。*/
		heaps[i] = ion_heap_create(heap_data);
		if (IS_ERR_OR_NULL(heaps[i])) {
			heaps[i] = 0;
			continue;
		} else {
			if (heap_data->size)
				pr_info("ION heap %s created at %lx "
					"with size %x\n", heap_data->name,
							  heap_data->base,
							  heap_data->size);
			else
				pr_info("ION heap %s created\n",
							  heap_data->name);
		}
		/*建立的heap添加到idev中,以便後續使用。*/
		ion_device_add_heap(idev, heaps[i]);
	}
	/*檢查heap之間是否有重疊部分*/
	check_for_heap_overlap(pdata->heaps, num_heaps);
	platform_set_drvdata(pdev, idev);
	return 0;

freeheaps:
	kfree(heaps);
out:
	return err;
}

通過ion_device_create建立/dev/ion節點:
struct ion_device *ion_device_create(long (*custom_ioctl)
				     (struct ion_client *client,
				      unsigned int cmd,
				      unsigned long arg))
{
	struct ion_device *idev;
	int ret;
	
	idev = kzalloc(sizeof(struct ion_device), GFP_KERNEL);
	if (!idev)
		return ERR_PTR(-ENOMEM);
	/*是個misc裝置*/
	idev->dev.minor = MISC_DYNAMIC_MINOR;
	/*節點名字為ion*/
	idev->dev.name = "ion";
	/*fops為ion_fops,是以對應ion的操作都會調用ion_fops的函數指針。*/
	idev->dev.fops = &ion_fops;
	idev->dev.parent = NULL;
	ret = misc_register(&idev->dev);
	if (ret) {
		pr_err("ion: failed to register misc device.\n");
		return ERR_PTR(ret);
	}
	/*建立debugfs目錄,路徑為/sys/kernel/debug/ion/*/
	idev->debug_root = debugfs_create_dir("ion", NULL);
	if (IS_ERR_OR_NULL(idev->debug_root))
		pr_err("ion: failed to create debug files.\n");

	idev->custom_ioctl = custom_ioctl;
	idev->buffers = RB_ROOT;
	mutex_init(&idev->lock);
	idev->heaps = RB_ROOT;
	idev->clients = RB_ROOT;
	/*在ion目錄下建立一個check_leaked_fds檔案,用來檢查Ion的使用是否有記憶體洩漏。如果申請了ion之後不需要使用卻沒有釋放,就會導緻memory leak.*/
	debugfs_create_file("check_leaked_fds", 0664, idev->debug_root, idev,
			    &debug_leak_fops);
	return idev;
}

msm_ion_allocate:
static void msm_ion_allocate(struct ion_platform_heap *heap)
{

	if (!heap->base && heap->extra_data) {
		unsigned int align = 0;
		switch (heap->type) {
		/*擷取align參數*/
		case ION_HEAP_TYPE_CARVEOUT:
			align =
			((struct ion_co_heap_pdata *) heap->extra_data)->align;
			break;
		/*此type我們沒使用到。*/
		case ION_HEAP_TYPE_CP:
		{
			struct ion_cp_heap_pdata *data =
				(struct ion_cp_heap_pdata *)
				heap->extra_data;
			if (data->reusable) {
				const struct fmem_data *fmem_info =
					fmem_get_info();
				heap->base = fmem_info->phys;
				data->virt_addr = fmem_info->virt;
				pr_info("ION heap %s using FMEM\n", heap->name);
			} else if (data->mem_is_fmem) {
				const struct fmem_data *fmem_info =
					fmem_get_info();
				heap->base = fmem_info->phys + fmem_info->size;
			}
			align = data->align;
			break;
		}
		default:
			break;
		}
		if (align && !heap->base) {
			/*擷取heap的base address。*/
			heap->base = msm_ion_get_base(heap->size,
						      heap->memory_type,
						      align);
			if (!heap->base)
				pr_err("%s: could not get memory for heap %s "
				   "(id %x)\n", __func__, heap->name, heap->id);
		}
	}
}

static unsigned long msm_ion_get_base(unsigned long size, int memory_type,
				    unsigned int align)
{
	switch (memory_type) {
	/*我們定義的是ebi type,看見沒,此函數在mem pool中分析過了。
原理就是使用Mempool 來管理配置設定記憶體。*/
	case ION_EBI_TYPE:
		return allocate_contiguous_ebi_nomap(size, align);
		break;
	case ION_SMI_TYPE:
		return allocate_contiguous_memory_nomap(size, MEMTYPE_SMI,
							align);
		break;
	default:
		pr_err("%s: Unknown memory type %d\n", __func__, memory_type);
		return 0;
	}
}
ion_heap_create:
struct ion_heap *ion_heap_create(struct ion_platform_heap *heap_data)
{
	struct ion_heap *heap = NULL;
	/*根據Heap type調用相應的建立函數。*/
	switch (heap_data->type) {
	case ION_HEAP_TYPE_SYSTEM_CONTIG:
		heap = ion_system_contig_heap_create(heap_data);
		break;
	case ION_HEAP_TYPE_SYSTEM:
		heap = ion_system_heap_create(heap_data);
		break;
	case ION_HEAP_TYPE_CARVEOUT:
		heap = ion_carveout_heap_create(heap_data);
		break;
	case ION_HEAP_TYPE_IOMMU:
		heap = ion_iommu_heap_create(heap_data);
		break;
	case ION_HEAP_TYPE_CP:
		heap = ion_cp_heap_create(heap_data);
		break;
#ifdef CONFIG_CMA
	case ION_HEAP_TYPE_DMA:
		heap = ion_cma_heap_create(heap_data);
		break;
#endif
	default:
		pr_err("%s: Invalid heap type %d\n", __func__,
		       heap_data->type);
		return ERR_PTR(-EINVAL);
	}

	if (IS_ERR_OR_NULL(heap)) {
		pr_err("%s: error creating heap %s type %d base %lu size %u\n",
		       __func__, heap_data->name, heap_data->type,
		       heap_data->base, heap_data->size);
		return ERR_PTR(-EINVAL);
	}
	/*儲存Heap的name,id和私有資料。*/
	heap->name = heap_data->name;
	heap->id = heap_data->id;
	heap->priv = heap_data->priv;
	return heap;
}
           

從上面的代碼可以得知,ION_HEAP_TYPE_SYSTEM_CONTIG使用kmalloc建立的,ION_HEAP_TYPE_SYSTEM使用的是vmalloc,而ion_carveout_heap_create就是系統預配置設定了一片記憶體區域供其使用。Ion在申請使用的時候,會根據目前的type來操作各自的heap->ops。分别看下三個函數:

struct ion_heap *ion_system_contig_heap_create(struct ion_platform_heap *pheap)
{
	struct ion_heap *heap;

	heap = kzalloc(sizeof(struct ion_heap), GFP_KERNEL);
	if (!heap)
		return ERR_PTR(-ENOMEM);
	/*使用的是kmalloc_ops,上篇有提到哦*/
	heap->ops = &kmalloc_ops;
	heap->type = ION_HEAP_TYPE_SYSTEM_CONTIG;
	system_heap_contig_has_outer_cache = pheap->has_outer_cache;
	return heap;
}
struct ion_heap *ion_system_heap_create(struct ion_platform_heap *pheap)
{
	struct ion_heap *heap;

	heap = kzalloc(sizeof(struct ion_heap), GFP_KERNEL);
	if (!heap)
		return ERR_PTR(-ENOMEM);
	/*和上面函數的差別僅在于ops*/
	heap->ops = &vmalloc_ops;
	heap->type = ION_HEAP_TYPE_SYSTEM;
	system_heap_has_outer_cache = pheap->has_outer_cache;
	return heap;
}
struct ion_heap *ion_carveout_heap_create(struct ion_platform_heap *heap_data)
{
	struct ion_carveout_heap *carveout_heap;
	int ret;

	carveout_heap = kzalloc(sizeof(struct ion_carveout_heap), GFP_KERNEL);
	if (!carveout_heap)
		return ERR_PTR(-ENOMEM);
	/* 重新建立一個新的pool,這裡有點想不通的是為什麼不直接使用全局的mempools呢?*/
	carveout_heap->pool = gen_pool_create(12, -1);
	if (!carveout_heap->pool) {
		kfree(carveout_heap);
		return ERR_PTR(-ENOMEM);
	}
	carveout_heap->base = heap_data->base;
	ret = gen_pool_add(carveout_heap->pool, carveout_heap->base,
			heap_data->size, -1);
	if (ret < 0) {
		gen_pool_destroy(carveout_heap->pool);
		kfree(carveout_heap);
		return ERR_PTR(-EINVAL);
	}
	carveout_heap->heap.ops = &carveout_heap_ops;
	carveout_heap->heap.type = ION_HEAP_TYPE_CARVEOUT;
	carveout_heap->allocated_bytes = 0;
	carveout_heap->total_size = heap_data->size;
	carveout_heap->has_outer_cache = heap_data->has_outer_cache;

	if (heap_data->extra_data) {
		struct ion_co_heap_pdata *extra_data =
				heap_data->extra_data;

		if (extra_data->setup_region)
			carveout_heap->bus_id = extra_data->setup_region();
		if (extra_data->request_region)
			carveout_heap->request_region =
					extra_data->request_region;
		if (extra_data->release_region)
			carveout_heap->release_region =
					extra_data->release_region;
	}
	return &carveout_heap->heap;
}

Heap建立完成,然後儲存到idev中:
void ion_device_add_heap(struct ion_device *dev, struct ion_heap *heap)
{
	struct rb_node **p = &dev->heaps.rb_node;
	struct rb_node *parent = NULL;
	struct ion_heap *entry;

	if (!heap->ops->allocate || !heap->ops->free || !heap->ops->map_dma ||
	    !heap->ops->unmap_dma)
		pr_err("%s: can not add heap with invalid ops struct.\n",
		       __func__);

	heap->dev = dev;
	mutex_lock(&dev->lock);
	while (*p) {
		parent = *p;
		entry = rb_entry(parent, struct ion_heap, node);

		if (heap->id < entry->id) {
			p = &(*p)->rb_left;
		} else if (heap->id > entry->id ) {
			p = &(*p)->rb_right;
		} else {
			pr_err("%s: can not insert multiple heaps with "
				"id %d\n", __func__, heap->id);
			goto end;
		}
	}
	/*使用紅黑樹儲存*/
	rb_link_node(&heap->node, parent, p);
	rb_insert_color(&heap->node, &dev->heaps);
	/*以heap name建立fs,位于ion目錄下。如vamlloc, camera_preview , audio 等*/
	debugfs_create_file(heap->name, 0664, dev->debug_root, heap,
			    &debug_heap_fops);
end:
	mutex_unlock(&dev->lock);
}
           

到此,ION初始化已經完成了。接下來該如何使用呢?嗯,通過前面建立的misc裝置也就是idev了!還記得裡面有個fops為ion_fops嗎?先來看下使用者空間如何使用ION。

ION使用者空間使用

Ion_fops結構如下:
static const struct file_operations ion_fops = {
	.owner          = THIS_MODULE,
	.open           = ion_open,
	.release        = ion_release,
	.unlocked_ioctl = ion_ioctl,
};

使用者空間都是通過ioctl來控制。先看ion_open.

static int ion_open(struct inode *inode, struct file *file)
{
	struct miscdevice *miscdev = file->private_data;
	struct ion_device *dev = container_of(miscdev, struct ion_device, dev);
	struct ion_client *client;
	char debug_name[64];

	pr_debug("%s: %d\n", __func__, __LINE__);
	snprintf(debug_name, 64, "%u", task_pid_nr(current->group_leader));
	/*根據idev和task pid為name建立ion client*/
	client = ion_client_create(dev, -1, debug_name);
	if (IS_ERR_OR_NULL(client))
		return PTR_ERR(client);
	file->private_data = client;

	return 0;
}
           

前一篇文章有說到,要使用ION, 必須要先建立ionclient, 是以使用者空間在open ion的時候建立了client.

struct ion_client *ion_client_create(struct ion_device *dev,
				     unsigned int heap_mask,
				     const char *name)
{
	struct ion_client *client;
	struct task_struct *task;
	struct rb_node **p;
	struct rb_node *parent = NULL;
	struct ion_client *entry;
	pid_t pid;
	unsigned int name_len;

	if (!name) {
		pr_err("%s: Name cannot be null\n", __func__);
		return ERR_PTR(-EINVAL);
	}
	name_len = strnlen(name, 64);

	get_task_struct(current->group_leader);
	task_lock(current->group_leader);
	pid = task_pid_nr(current->group_leader);
	/* don't bother to store task struct for kernel threads,
	   they can't be killed anyway */
	if (current->group_leader->flags & PF_KTHREAD) {
		put_task_struct(current->group_leader);
		task = NULL;
	} else {
		task = current->group_leader;
	}
	task_unlock(current->group_leader);
	/*配置設定ion client struct.*/
	client = kzalloc(sizeof(struct ion_client), GFP_KERNEL);
	if (!client) {
		if (task)
			put_task_struct(current->group_leader);
		return ERR_PTR(-ENOMEM);
	}
	/*下面就是儲存一系列參數了。*/
	client->dev = dev;
	client->handles = RB_ROOT;
	mutex_init(&client->lock);

	client->name = kzalloc(name_len+1, GFP_KERNEL);
	if (!client->name) {
		put_task_struct(current->group_leader);
		kfree(client);
		return ERR_PTR(-ENOMEM);
	} else {
		strlcpy(client->name, name, name_len+1);
	}

	client->heap_mask = heap_mask;
	client->task = task;
	client->pid = pid;

	mutex_lock(&dev->lock);
	p = &dev->clients.rb_node;
	while (*p) {
		parent = *p;
		entry = rb_entry(parent, struct ion_client, node);

		if (client < entry)
			p = &(*p)->rb_left;
		else if (client > entry)
			p = &(*p)->rb_right;
	}
	/*目前client添加到idev的clients根樹上去。*/
	rb_link_node(&client->node, parent, p);
	rb_insert_color(&client->node, &dev->clients);

	/*在ION先建立的檔案名字是以pid命名的。*/
	client->debug_root = debugfs_create_file(name, 0664,
						 dev->debug_root, client,
						 &debug_client_fops);
	mutex_unlock(&dev->lock);

	return client;
}
           

有了client之後,使用者程式就可以開始申請配置設定ION buffer了!通過ioctl指令實作。

ion_ioct函數有若幹個cmd,ION_IOC_ALLOC和ION_IOC_FREE相對應,表示申請和釋放buffer。使用者空間程式使用前先要調用ION_IOC_MAP才能得到buffer address,而ION_IOC_IMPORT是為了将這塊記憶體共享給使用者空間另一個程序。

static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	struct ion_client *client = filp->private_data;

	switch (cmd) {
	case ION_IOC_ALLOC:
	{
		struct ion_allocation_data data;

		if (copy_from_user(&data, (void __user *)arg, sizeof(data)))
			return -EFAULT;
		/*配置設定buffer.*/
		data.handle = ion_alloc(client, data.len, data.align,
					     data.flags);

		if (IS_ERR(data.handle))
			return PTR_ERR(data.handle);

		if (copy_to_user((void __user *)arg, &data, sizeof(data))) {
			ion_free(client, data.handle);
			return -EFAULT;
		}
		break;
	}
	case ION_IOC_FREE:
	{
		struct ion_handle_data data;
		bool valid;

		if (copy_from_user(&data, (void __user *)arg,
				   sizeof(struct ion_handle_data)))
			return -EFAULT;
		mutex_lock(&client->lock);
		valid = ion_handle_validate(client, data.handle);
		mutex_unlock(&client->lock);
		if (!valid)
			return -EINVAL;
		ion_free(client, data.handle);
		break;
	}
	case ION_IOC_MAP:
	case ION_IOC_SHARE:
	{
		struct ion_fd_data data;
		int ret;
		if (copy_from_user(&data, (void __user *)arg, sizeof(data)))
			return -EFAULT;
		/*判斷目前cmd是否被調用過了,調用過就傳回,否則設定flags.*/
		ret = ion_share_set_flags(client, data.handle, filp->f_flags);
		if (ret)
			return ret;

		data.fd = ion_share_dma_buf(client, data.handle);
		if (copy_to_user((void __user *)arg, &data, sizeof(data)))
			return -EFAULT;
		if (data.fd < 0)
			return data.fd;
		break;
	}
	case ION_IOC_IMPORT:
	{
		struct ion_fd_data data;
		int ret = 0;
		if (copy_from_user(&data, (void __user *)arg,
				   sizeof(struct ion_fd_data)))
			return -EFAULT;
		data.handle = ion_import_dma_buf(client, data.fd);
		if (IS_ERR(data.handle))
			data.handle = NULL;
		if (copy_to_user((void __user *)arg, &data,
				 sizeof(struct ion_fd_data)))
			return -EFAULT;
		if (ret < 0)
			return ret;
		break;
	}
	case ION_IOC_CUSTOM:
~~snip
	case ION_IOC_CLEAN_CACHES:
	case ION_IOC_INV_CACHES:
	case ION_IOC_CLEAN_INV_CACHES:
	~~snip
	case ION_IOC_GET_FLAGS:
~~snip
	default:
		return -ENOTTY;
	}
	return 0;
}

下面分小節說明配置設定和共享的原理。
           

ION_IOC_ALLOC

struct ion_handle *ion_alloc(struct ion_client *client, size_t len,
			     size_t align, unsigned int flags)
{
~~snip

	mutex_lock(&dev->lock);
	/*循環周遊目前Heap連結清單。*/
	for (n = rb_first(&dev->heaps); n != NULL; n = rb_next(n)) {
		struct ion_heap *heap = rb_entry(n, struct ion_heap, node);
/*隻有heap type和id都符合才去建立buffer.*/
		/* if the client doesn't support this heap type */
		if (!((1 << heap->type) & client->heap_mask))
			continue;
		/* if the caller didn't specify this heap type */
		if (!((1 << heap->id) & flags))
			continue;
		/* Do not allow un-secure heap if secure is specified */
		if (secure_allocation && (heap->type != ION_HEAP_TYPE_CP))
			continue;
		buffer = ion_buffer_create(heap, dev, len, align, flags);
~~snip
	}
	mutex_unlock(&dev->lock);

~~snip
	/*建立了buffer之後,就相應地建立handle來管理buffer.*/
	handle = ion_handle_create(client, buffer);

~~snip
}

找到Heap之後調用ion_buffer_create:
static struct ion_buffer *ion_buffer_create(struct ion_heap *heap,
				     struct ion_device *dev,
				     unsigned long len,
				     unsigned long align,
				     unsigned long flags)
{
	struct ion_buffer *buffer;
	struct sg_table *table;
	int ret;
	/*配置設定struct ion buffer,用來管理buffer.*/
	buffer = kzalloc(sizeof(struct ion_buffer), GFP_KERNEL);
	if (!buffer)
		return ERR_PTR(-ENOMEM);

	buffer->heap = heap;
	kref_init(&buffer->ref);
	/*調用相應heap type的ops allocate。還記得前面有提到過不同種類的ops嗎,
如carveout_heap_ops ,vmalloc_ops 。*/
	ret = heap->ops->allocate(heap, buffer, len, align, flags);
	if (ret) {
		kfree(buffer);
		return ERR_PTR(ret);
	}

	buffer->dev = dev;
	buffer->size = len;
	/*http://lwn.net/Articles/263343/*/
	table = buffer->heap->ops->map_dma(buffer->heap, buffer);
	if (IS_ERR_OR_NULL(table)) {
		heap->ops->free(buffer);
		kfree(buffer);
		return ERR_PTR(PTR_ERR(table));
	}
	buffer->sg_table = table;

	mutex_init(&buffer->lock);
	/*将目前ion buffer添加到idev 的buffers 樹上統一管理。*/
	ion_buffer_add(dev, buffer);
	return buffer;
}
           

先拿heap type為ION_HEAP_TYPE_CARVEOUT為例,看下它是如何配置設定buffer的。

allocate對應ion_carveout_heap_allocate。

static int ion_carveout_heap_allocate(struct ion_heap *heap,
				      struct ion_buffer *buffer,
				      unsigned long size, unsigned long align,
				      unsigned long flags)
{
	buffer->priv_phys = ion_carveout_allocate(heap, size, align);
	return buffer->priv_phys == ION_CARVEOUT_ALLOCATE_FAIL ? -ENOMEM : 0;
}
ion_phys_addr_t ion_carveout_allocate(struct ion_heap *heap,
				      unsigned long size,
				      unsigned long align)
{
	struct ion_carveout_heap *carveout_heap =
		container_of(heap, struct ion_carveout_heap, heap);
	/*通過建立的mem pool來管理buffer,由于這塊buffer在初始化的
時候就預留了,現在隻要從上面拿一塊區域就可以了。*/
	unsigned long offset = gen_pool_alloc_aligned(carveout_heap->pool,
							size, ilog2(align));
	/*配置設定不成功可能是沒有記憶體空間可供配置設定了或者是有碎片導緻的。*/
	if (!offset) {
		if ((carveout_heap->total_size -
		      carveout_heap->allocated_bytes) >= size)
			pr_debug("%s: heap %s has enough memory (%lx) but"
				" the allocation of size %lx still failed."
				" Memory is probably fragmented.",
				__func__, heap->name,
				carveout_heap->total_size -
				carveout_heap->allocated_bytes, size);
		return ION_CARVEOUT_ALLOCATE_FAIL;
	}
	/*已經配置設定掉的記憶體位元組。*/
	carveout_heap->allocated_bytes += size;
	return offset;
}
           

同樣地,對于heap type為ION_HEAP_TYPE_SYSTEM的配置設定函數是ion_system_heap_allocate。

static int ion_system_contig_heap_allocate(struct ion_heap *heap,
					   struct ion_buffer *buffer,
					   unsigned long len,
					   unsigned long align,
					   unsigned long flags)
{
	/*通過kzalloc配置設定。*/
	buffer->priv_virt = kzalloc(len, GFP_KERNEL);
	if (!buffer->priv_virt)
		return -ENOMEM;
	atomic_add(len, &system_contig_heap_allocated);
	return 0;
}
           

其他的幾種Heap type可自行研究,接着調用ion_buffer_add将buffer添加到dev的buffers樹上去。

static void ion_buffer_add(struct ion_device *dev,
			   struct ion_buffer *buffer)
{
	struct rb_node **p = &dev->buffers.rb_node;
	struct rb_node *parent = NULL;
	struct ion_buffer *entry;

	while (*p) {
		parent = *p;
		entry = rb_entry(parent, struct ion_buffer, node);

		if (buffer < entry) {
			p = &(*p)->rb_left;
		} else if (buffer > entry) {
			p = &(*p)->rb_right;
		} else {
			pr_err("%s: buffer already found.", __func__);
			BUG();
		}
	}
/*又是使用紅黑樹哦!*/
	rb_link_node(&buffer->node, parent, p);
	rb_insert_color(&buffer->node, &dev->buffers);
}
           

好了buffer建立完成,接下來就要建立Hanle來管理buffer了!

static struct ion_handle *ion_handle_create(struct ion_client *client,
				     struct ion_buffer *buffer)
{
	struct ion_handle *handle;
	/*配置設定struct ion_handle.*/
	handle = kzalloc(sizeof(struct ion_handle), GFP_KERNEL);
	if (!handle)
		return ERR_PTR(-ENOMEM);
	kref_init(&handle->ref);
	rb_init_node(&handle->node);
	handle->client = client;	//client放入handle中
	ion_buffer_get(buffer);	//引用計數加1 
	handle->buffer = buffer;	//buffer也放入handle中

	return handle;
}
           

建立handle也比較簡單,至此,已經得到client和handle,buffer配置設定完成!

ION_IOC_MAP/ ION_IOC_SHARE

int ion_share_dma_buf(struct ion_client *client, struct ion_handle *handle)
{
	struct ion_buffer *buffer;
	struct dma_buf *dmabuf;
	bool valid_handle;
	int fd;

	mutex_lock(&client->lock);
	valid_handle = ion_handle_validate(client, handle);
	mutex_unlock(&client->lock);
	if (!valid_handle) {
		WARN(1, "%s: invalid handle passed to share.\n", __func__);
		return -EINVAL;
	}

	buffer = handle->buffer;
	ion_buffer_get(buffer);
	/*生成一個新的file描述符*/
	dmabuf = dma_buf_export(buffer, &dma_buf_ops, buffer->size, O_RDWR);
	if (IS_ERR(dmabuf)) {
		ion_buffer_put(buffer);
		return PTR_ERR(dmabuf);
	}
	/*将file轉換使用者空間識别的fd描述符。*/
	fd = dma_buf_fd(dmabuf, O_CLOEXEC);
	if (fd < 0)
		dma_buf_put(dmabuf);

	return fd;
}
struct dma_buf *dma_buf_export(void *priv, const struct dma_buf_ops *ops,
				size_t size, int flags)
{
	struct dma_buf *dmabuf;
	struct file *file;
~~snip
	/*配置設定struct dma_buf.*/
	dmabuf = kzalloc(sizeof(struct dma_buf), GFP_KERNEL);
	if (dmabuf == NULL)
		return ERR_PTR(-ENOMEM);
	/*儲存資訊到dmabuf,注意ops為dma_buf_ops,後面mmap為調用到。*/
	dmabuf->priv = priv;
	dmabuf->ops = ops;
	dmabuf->size = size;
	/*産生新的file*/
	file = anon_inode_getfile("dmabuf", &dma_buf_fops, dmabuf, flags);

	dmabuf->file = file;

	mutex_init(&dmabuf->lock);
	INIT_LIST_HEAD(&dmabuf->attachments);

	return dmabuf;
}
           

通過上述過程,使用者空間就得到了新的fd,重新生成一個新的fd的目的是考慮了兩個使用者空間程序想共享這塊heap記憶體的情況。然後再對fd作mmap,相應地kernel空間就調用到了file 的dma_buf_fops中的dma_buf_mmap_internal。

static const struct file_operations dma_buf_fops = {
	.release	= dma_buf_release,
	.mmap		= dma_buf_mmap_internal,
};
static int dma_buf_mmap_internal(struct file *file, struct vm_area_struct *vma)
 {
 	struct dma_buf *dmabuf;
 
 	if (!is_dma_buf_file(file))
 		return -EINVAL;
 
 	dmabuf = file->private_data;
 	/*檢查使用者空間要映射的size是否比目前dmabuf也就是目前heap的size
還要大,如果是就傳回無效。*/
 	/* check for overflowing the buffer's size */
 	if (vma->vm_pgoff + ((vma->vm_end - vma->vm_start) >> PAGE_SHIFT) >
 	    dmabuf->size >> PAGE_SHIFT)
 		return -EINVAL;
 	/*調用的是dma_buf_ops 的mmap函數*/
 	return dmabuf->ops->mmap(dmabuf, vma);
 }

struct dma_buf_ops dma_buf_ops = {
	.map_dma_buf = ion_map_dma_buf,
	.unmap_dma_buf = ion_unmap_dma_buf,
	.mmap = ion_mmap,
	.release = ion_dma_buf_release,
	.begin_cpu_access = ion_dma_buf_begin_cpu_access,
	.end_cpu_access = ion_dma_buf_end_cpu_access,
	.kmap_atomic = ion_dma_buf_kmap,
	.kunmap_atomic = ion_dma_buf_kunmap,
	.kmap = ion_dma_buf_kmap,
	.kunmap = ion_dma_buf_kunmap,
};
static int ion_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
{
	struct ion_buffer *buffer = dmabuf->priv;
	int ret;

	if (!buffer->heap->ops->map_user) {
		pr_err("%s: this heap does not define a method for mapping "
		       "to userspace\n", __func__);
		return -EINVAL;
	}

	mutex_lock(&buffer->lock);
	/* now map it to userspace */
	/*調用的是相應heap的map_user,如carveout_heap_ops 調用的是
ion_carveout_heap_map_user ,此函數就是一般的mmap實作,不追下去了。*/
	ret = buffer->heap->ops->map_user(buffer->heap, buffer, vma);

	if (ret) {
		mutex_unlock(&buffer->lock);
		pr_err("%s: failure mapping buffer to userspace\n",
		       __func__);
	} else {
		buffer->umap_cnt++;
		mutex_unlock(&buffer->lock);

		vma->vm_ops = &ion_vm_ops;
		/*
		 * move the buffer into the vm_private_data so we can access it
		 * from vma_open/close
		 */
		vma->vm_private_data = buffer;
	}
	return ret;
}
           

至此,使用者空間就得到了bufferaddress,然後可以使用了!

ION_IOC_IMPORT

當使用者空間另一個程序需要這塊heap的時候,ION_IOC_IMPORT就派上用處了!注意,

傳進去的fd為在ION_IOC_SHARE中得到的。

struct ion_handle *ion_import_dma_buf(struct ion_client *client, int fd)
{
	
	struct dma_buf *dmabuf;
	struct ion_buffer *buffer;
	struct ion_handle *handle;

	dmabuf = dma_buf_get(fd);
	if (IS_ERR_OR_NULL(dmabuf))
		return ERR_PTR(PTR_ERR(dmabuf));
	/* if this memory came from ion */
~~snip
	buffer = dmabuf->priv;

	mutex_lock(&client->lock);
	/* if a handle exists for this buffer just take a reference to it */
/*查找是否已經存在對應的handle了,沒有則建立。因為另外一個程序隻是
調用了open 接口,對應的隻建立了client,并沒有handle。
*/
	handle = ion_handle_lookup(client, buffer);
	if (!IS_ERR_OR_NULL(handle)) {
		ion_handle_get(handle);
		goto end;
	}
	handle = ion_handle_create(client, buffer);
	if (IS_ERR_OR_NULL(handle))
		goto end;
	ion_handle_add(client, handle);
end:
	mutex_unlock(&client->lock);
	dma_buf_put(dmabuf);
	return handle;
}
           

這樣,使用者空間另一個程序也得到了對應的bufferHandle,client/buffer/handle之間連接配接起來了!然後另一個一個程序就也可以使用mmap來操作這塊heap buffer了。

和一般的程序使用ION差別就是共享的程序之間struction_buffer是共享的,而struct ion_handle是各自的。

 可見,ION的使用流程還是比較清晰的。不過要記得的是,使用好了ION,一定要釋放掉,否則會導緻記憶體洩露。

ION核心空間使用

核心空間使用ION也是大同小異,按照建立client,buffer,handle的流程,隻是它的使用對使用者空間來說是透明的罷了!

ion_client_create在kernel空間被Qualcomm給封裝了下。

struct ion_client *msm_ion_client_create(unsigned int heap_mask,
					const char *name)
{
	return ion_client_create(idev, heap_mask, name);
}
           

調用的流程也類似,不過map的時候調用的是heap對應的map_kernel()而不是map_user().

msm_ion_client_create -> ion_alloc ->ion_map_kernel

參考文檔:

http://lwn.net/Articles/480055/

《ARM體系結構與程式設計》存儲系統章節

20130227

繼續閱讀