Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mm: add alloc_pages_exact() and free_pages_exact()

alloc_pages_exact() is similar to alloc_pages(), except that it allocates
the minimum number of pages to fulfill the request. This is useful if you
want to allocate a very large buffer that is slightly larger than an even
power-of-two number of pages. In that case, alloc_pages() will waste a
lot of memory.

I have a video driver that wants to allocate a 5MB buffer. alloc_pages()
wiill waste 3MB of physically-contiguous memory.

Signed-off-by: Timur Tabi <timur@freescale.com>
Cc: Andi Kleen <andi@firstfloor.org>
Acked-by: Mel Gorman <mel@csn.ul.ie>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Timur Tabi and committed by
Linus Torvalds
2be0ffe2 3560e249

+56
+3
include/linux/gfp.h
··· 228 228 extern unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order); 229 229 extern unsigned long get_zeroed_page(gfp_t gfp_mask); 230 230 231 + void *alloc_pages_exact(size_t size, gfp_t gfp_mask); 232 + void free_pages_exact(void *virt, size_t size); 233 + 231 234 #define __get_free_page(gfp_mask) \ 232 235 __get_free_pages((gfp_mask),0) 233 236
+53
mm/page_alloc.c
··· 1697 1697 1698 1698 EXPORT_SYMBOL(free_pages); 1699 1699 1700 + /** 1701 + * alloc_pages_exact - allocate an exact number physically-contiguous pages. 1702 + * @size: the number of bytes to allocate 1703 + * @gfp_mask: GFP flags for the allocation 1704 + * 1705 + * This function is similar to alloc_pages(), except that it allocates the 1706 + * minimum number of pages to satisfy the request. alloc_pages() can only 1707 + * allocate memory in power-of-two pages. 1708 + * 1709 + * This function is also limited by MAX_ORDER. 1710 + * 1711 + * Memory allocated by this function must be released by free_pages_exact(). 1712 + */ 1713 + void *alloc_pages_exact(size_t size, gfp_t gfp_mask) 1714 + { 1715 + unsigned int order = get_order(size); 1716 + unsigned long addr; 1717 + 1718 + addr = __get_free_pages(gfp_mask, order); 1719 + if (addr) { 1720 + unsigned long alloc_end = addr + (PAGE_SIZE << order); 1721 + unsigned long used = addr + PAGE_ALIGN(size); 1722 + 1723 + split_page(virt_to_page(addr), order); 1724 + while (used < alloc_end) { 1725 + free_page(used); 1726 + used += PAGE_SIZE; 1727 + } 1728 + } 1729 + 1730 + return (void *)addr; 1731 + } 1732 + EXPORT_SYMBOL(alloc_pages_exact); 1733 + 1734 + /** 1735 + * free_pages_exact - release memory allocated via alloc_pages_exact() 1736 + * @virt: the value returned by alloc_pages_exact. 1737 + * @size: size of allocation, same value as passed to alloc_pages_exact(). 1738 + * 1739 + * Release the memory allocated by a previous call to alloc_pages_exact. 1740 + */ 1741 + void free_pages_exact(void *virt, size_t size) 1742 + { 1743 + unsigned long addr = (unsigned long)virt; 1744 + unsigned long end = addr + PAGE_ALIGN(size); 1745 + 1746 + while (addr < end) { 1747 + free_page(addr); 1748 + addr += PAGE_SIZE; 1749 + } 1750 + } 1751 + EXPORT_SYMBOL(free_pages_exact); 1752 + 1700 1753 static unsigned int nr_free_zone_pages(int offset) 1701 1754 { 1702 1755 struct zoneref *z;