/*
kmalloc can apply 128KB memory only. This func support any continous memory allocate more than 2MB.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kallsyms.h>
#define KMEM_PAGES //apply more than one page
#define KMEM_DEBUG //debug message switch
/*
// Pure 2^n version of get_order
static __inline__ __attribute_const__ int get_order(unsigned long size)
{
int order;
size = (size - 1) >> (PAGE_SHIFT - 1);
order = -1;
do {
size >>= 1;
order++;
} while (size);
return order;
}
*/
/*
alloc memory for DMA using in kernel space.
*/
void *kmem_alloc(size_t size, dma_addr_t *dma_handle, unsigned long flags)
{
struct page *page; //start page address
void *cpu_addr = NULL; //cpu io address
struct page *end; //end of pages
unsigned int PageOrder=0;
size = PAGE_ALIGN(size); //must be times of 4KB
PageOrder=get_order(size);
#ifdef CONFIG_ISOLATE_HIGHMEM //allocate in high memory
page = alloc_pages(GFP_HIGHUSER, PageOrder);
#else //allocate from low memory
page = alloc_pages(GFP_KERNEL, PageOrder); //get_order(size) means how many 4KB page do you want to apply. 2^n
#endif
if(!page) {
err("alloc_pages fail! (requested %#x)", size);
goto no_page;
}
#ifdef KMEM_DEBUG
printk("kmem_alloc 0x%x size 0x%x,order=%d/n",page_to_phys(page),size,PageOrder);
#endif
*dma_handle = page_to_phys(page); //transform to physical address
cpu_addr = __ioremap(*dma_handle, size, flags, 1); //map to kernal io space
if(cpu_addr) {
#ifdef KMEM_PAGES
end = page + (1 << PageOrder); //count to get the last page
do {
set_page_count(page, 1); //only one user
SetPageReserved(page); //set page used flag
page++;
} while (size -= PAGE_SIZE);
/*
* Free the unused pages. get_order(size) means 2^n to apply, so there must be more pages to alloc than we want.
*/
while (page < end) {
set_page_count(page, 1);
if (!PageReserved(page) && put_page_testzero(page))
free_cold_page(page); //only we can use this func to free page
page++;
}
#else //just apply one page
set_page_count(page, 1);
SetPageReserved(page);
#endif
}
else {
__free_pages(page, PageOrder); //free more than one pages
err("__ioremap fail! (phy %#x)", *dma_handle);
}
no_page:
return cpu_addr;
}
EXPORT_SYMBOL(kmem_alloc);
void kmem_free(size_t size, void *cpu_addr, dma_addr_t handle)
{
struct page *page = pfn_to_page(handle >> PAGE_SHIFT); //equal to phys_to_pages()
struct page *pg;
unsigned int sz;
pg=page;
sz=PAGE_ALIGN(size);
#ifdef KMEM_DEBUG
printk("kmem_free 0x%x size 0x%x/n",page_to_phys(pg),sz);
#endif
__iounmap(cpu_addr); //un map from kernel io space
#ifdef KMEM_PAGES
do {
ClearPageReserved(page);
if (!PageReserved(page) && put_page_testzero(page))
free_cold_page(page);
page++;
} while (sz -= PAGE_SIZE);
#else
ClearPageReserved(page);
if (!PageReserved(page) && set_page_count(page, 0))
__free_pages_ok(page,get_order(size));
#endif
}
EXPORT_SYMBOL(kmem_free);
linux内核空间申请超过2MB连续空间的实现函数。
最新推荐文章于 2023-06-06 08:49:05 发布