2019.11.15 PYNQ cma實現連續內存申請

製作過程參考:https://blog.csdn.net/zhaoxinfan/article/details/83245682
(如有錯誤之處,不吝賜教)

背景介紹:

在使用pcie進行數據傳輸時,常常需要用到dma,由於dma傳輸多爲異步傳輸方式,只需要告訴dma起始地址,數據大小,然後啓動dma,cpu就可以去做其他事情。不過Dma傳輸需要有一個前提條件,分配一段連續的物理內存,在linux下,由於存在虛實物理地址轉換,用戶訪問的都是虛地址,分配一段連續的物理內存比較困難。常見的做法是在操作系統啓動時預留一段物理內存專門用於dma,缺點是操作系統無法管理這段空間,如果沒有dma操作顯然空間就浪費了。

CMA介紹:

cma是linux中一種動態分配連續物理內存的方式,具體可以參看宋寶華的這篇博文:https://blog.csdn.net/21cnbao/article/details/7309757

這裏在PYNQ中進行了一下cma的實踐

原文鏈接:https://blog.csdn.net/zhaoxinfan/article/details/83245682
1、使用petalinux製作cma申請設備節點PYNQ linux系統
具體petalinux製作PYNQ 系統見博客:
https://blog.csdn.net/weixin_40604731/article/details/100547102
在這裏插入圖片描述
在這裏插入圖片描述
上圖中設置了cma默認大小爲256MB,同時指定了PAGE_SIZE最大值12,即cma區域的頁大小爲4MB,空間也按照4MB頁進行對齊。
2、使用/dev/cma 申請動態內存

將cma測試代碼module編入內核,詳情請參考ug1144

$ cd <plnx-proj-root>
$ petalinux-create -t modules --name <user-module-name> --enable
$ cd <plnx-proj-root>/project-spec/meta-user/recipes-modules/
mymodule
mymodule.c file can be edited or replaced with the real source code for your module. Later
if you want to modify your custom user module, you are required to edit this file.

並編寫相應的makefile


#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
 
struct cma_allocation {
	struct list_head list;
	unsigned long size;
	dma_addr_t dma;
	void *virt;
};
 
static struct device *cma_dev;
static LIST_HEAD(cma_allocations);
static DEFINE_SPINLOCK(cma_lock);
 
/*
 * any read request will free the 1st allocated coherent memory, eg.
 * cat /dev/cma_test
 */
static ssize_t
cma_test_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
	struct cma_allocation *alloc = NULL;
 
	spin_lock(&cma_lock);
	if (!list_empty(&cma_allocations)) {
		alloc = list_first_entry(&cma_allocations,
			struct cma_allocation, list);
		list_del(&alloc->list);
	}
	spin_unlock(&cma_lock);
 
	if (alloc) {
		dma_free_coherent(cma_dev, alloc->size, alloc->virt,
			alloc->dma);
 
		_dev_info(cma_dev, "free CM at virtual address: 0x%p dma address: 0x%p size:%luKiB\n",
			alloc->virt, (void *)alloc->dma, alloc->size / SZ_1K);
		kfree(alloc);
	}
 
	return 0;
}
 
/*
 * any write request will alloc a new coherent memory, eg.
 * echo 1024 > /dev/cma_test
 * will request 1024KiB by CMA
 */
static ssize_t
cma_test_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
	struct cma_allocation *alloc;
	int ret;
 
	alloc = kmalloc(sizeof *alloc, GFP_KERNEL);
	if (!alloc)
		return -ENOMEM;
 
	ret = kstrtoul_from_user(buf, count, 0, &alloc->size);
	if (ret)
		return ret;
 
	if (!alloc->size)
		return -EINVAL;
 
	if (alloc->size > (ULONG_MAX << PAGE_SHIFT))
		return -EOVERFLOW;
 
	alloc->size *= SZ_1K;
	alloc->virt = dma_alloc_coherent(cma_dev, alloc->size,
		&alloc->dma, GFP_KERNEL);
 
	if (alloc->virt) {
		_dev_info(cma_dev, "allocate CM at virtual address: 0x%p"
			"address: 0x%p size:%luKiB\n", alloc->virt,
			(void *)alloc->dma, alloc->size / SZ_1K);
 
		spin_lock(&cma_lock);
		list_add_tail(&alloc->list, &cma_allocations);
		spin_unlock(&cma_lock);
 
		return count;
	} else {
		dev_err(cma_dev, "no mem in CMA area\n");
		kfree(alloc);
		return -ENOSPC;
	}
}
 
static const struct file_operations cma_test_fops = {
	.owner =    THIS_MODULE,
	.read  =    cma_test_read,
	.write =    cma_test_write,
};
 
static struct miscdevice cma_test_misc = {
	.name = "cma_test",
	.fops = &cma_test_fops,
};
 
static int __init cma_test_init(void)
{
	int ret = 0;
 
	ret = misc_register(&cma_test_misc);
	if (unlikely(ret)) {
		pr_err("failed to register cma test misc device!\n");
		return ret;
	}
	cma_dev = cma_test_misc.this_device;
	cma_dev->coherent_dma_mask = ~0;
	_dev_info(cma_dev, "registered.\n");
 
	return ret;
}
module_init(cma_test_init);
 
static void __exit cma_test_exit(void)
{
	misc_deregister(&cma_test_misc);
}
module_exit(cma_test_exit);
 
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Barry Song <[email protected]>");
MODULE_DESCRIPTION("kernel module to help the test of CMA");
MODULE_ALIAS("CMA test");

操作系統啓動後執行echo 51200> /dev/cma_test即分配50MB連續物理內存空間,起始物理地址爲0x31000000。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章