From 78d98c3245a12433d2016ffc1e00a34fae79d822 Mon Sep 17 00:00:00 2001 From: Gary King Date: Tue, 12 Oct 2010 17:40:18 -0700 Subject: [PATCH 1/2] video: tegra: nvmap: eliminate arm_attrib_allocator dependency remove the dependency that nvmap has on the arm_attrib_allocator and the lowmem in PTEs change by adding a private page allocator utility function and calling vm_map_ram unconditionally for all sysmem handles. also, add Kconfig variables to allow platforms to disallow the SYSMEM heap, and to optionally restrict the SYSMEM and IOVMM heaps to just HIGHMEM. Change-Id: I3dab1c7323f54a8ab3994dc672b27fd79a9057d7 Signed-off-by: Gary King --- drivers/video/tegra/Kconfig | 26 +++++++++--- drivers/video/tegra/nvmap/nvmap.c | 10 ++--- drivers/video/tegra/nvmap/nvmap_handle.c | 51 +++++++++++++++++++++--- 3 files changed, 70 insertions(+), 17 deletions(-) diff --git a/drivers/video/tegra/Kconfig b/drivers/video/tegra/Kconfig index f9192c6d68b8..2b8160877688 100644 --- a/drivers/video/tegra/Kconfig +++ b/drivers/video/tegra/Kconfig @@ -28,22 +28,38 @@ config FB_TEGRA Framebuffer device support for the Tegra display controller. config TEGRA_NVMAP - bool "Tegra GPU memory management driver" - select ARM_ATTRIB_ALLOCATOR + bool "Tegra GPU memory management driver (nvmap)" default y help Say Y here to include the memory management driver for the Tegra GPU, multimedia and display subsystems config NVMAP_RECLAIM_UNPINNED_VM - bool "Allow /dev/nvmap to reclaim unpinned I/O virtual memory" + bool "Virtualize IOVMM memory in nvmap" depends on TEGRA_NVMAP && TEGRA_IOVMM default y help - Say Y here to enable /dev/nvmap to reclaim I/O virtual memory after - it has been unpinned, and re-use it for other objects. This can + Say Y here to enable nvmap to reclaim I/O virtual memory after + it has been unpinned, and re-use it for other handles. This can allow a larger virtual I/O VM space than would normally be supported by the hardware, at a slight cost in performance. +config NVMAP_ALLOW_SYSMEM + bool "Allow physical system memory to be used by nvmap" + depends on TEGRA_NVMAP + default y + help + Say Y here to allow nvmap to use physical system memory (i.e., + shared with the operating system but not translated through + an IOVMM device) for allocations. + +config NVMAP_HIGHMEM_ONLY + bool "Use only HIGHMEM for nvmap" + depends on TEGRA_NVMAP && (NVMAP_ALLOW_SYSMEM || TEGRA_IOVMM) && HIGHMEM + default n + help + Say Y here to restrict nvmap system memory allocations (both + physical system memory and IOVMM) to just HIGHMEM pages. + endif diff --git a/drivers/video/tegra/nvmap/nvmap.c b/drivers/video/tegra/nvmap/nvmap.c index 506aef8408a9..865681f2e221 100644 --- a/drivers/video/tegra/nvmap/nvmap.c +++ b/drivers/video/tegra/nvmap/nvmap.c @@ -623,10 +623,7 @@ void *nvmap_mmap(struct nvmap_handle_ref *ref) prot = nvmap_pgprot(h, pgprot_kernel); - if (h->heap_pgalloc && h->pgalloc.contig && - !PageHighMem(h->pgalloc.pages[0])) - return page_address(h->pgalloc.pages[0]); - else if (h->heap_pgalloc) + if (h->heap_pgalloc) return vm_map_ram(h->pgalloc.pages, h->size >> PAGE_SHIFT, -1, prot); @@ -687,10 +684,9 @@ void nvmap_munmap(struct nvmap_handle_ref *ref, void *addr) h = ref->handle; - if (h->heap_pgalloc && (!h->pgalloc.contig || - PageHighMem(h->pgalloc.pages[0]))) { + if (h->heap_pgalloc) { vm_unmap_ram(addr, h->size >> PAGE_SHIFT); - } else if (!h->heap_pgalloc) { + } else { struct vm_struct *vm; addr -= (h->carveout->base & ~PAGE_MASK); vm = remove_vm_area(addr); diff --git a/drivers/video/tegra/nvmap/nvmap_handle.c b/drivers/video/tegra/nvmap/nvmap_handle.c index 21cbf9c4d85d..725a132ff6fd 100644 --- a/drivers/video/tegra/nvmap/nvmap_handle.c +++ b/drivers/video/tegra/nvmap/nvmap_handle.c @@ -28,7 +28,8 @@ #include #include -#include +#include +#include #include #include @@ -38,7 +39,11 @@ #include "nvmap_mru.h" #define NVMAP_SECURE_HEAPS (NVMAP_HEAP_CARVEOUT_IRAM | NVMAP_HEAP_IOVMM) +#ifdef CONFIG_NVMAP_HIGHMEM_ONLY #define GFP_NVMAP (__GFP_HIGHMEM | __GFP_NOWARN) +#else +#define GFP_NVMAP (GFP_KERNEL | __GFP_HIGHMEM | __GFP_NOWARN) +#endif /* handles may be arbitrarily large (16+MiB), and any handle allocated from * the kernel (i.e., not a carveout handle) includes its array of pages. to * preserve kmalloc space, if the array of pages exceeds PAGELIST_VMALLOC_MIN, @@ -90,7 +95,7 @@ void _nvmap_handle_free(struct nvmap_handle *h) tegra_iovmm_free_vm(h->pgalloc.area); for (i = 0; i < nr_page; i++) - arm_attrib_free_page(h->pgalloc.pages[i]); + __free_page(h->pgalloc.pages[i]); altfree(h->pgalloc.pages, nr_page * sizeof(struct page *)); @@ -99,6 +104,36 @@ void _nvmap_handle_free(struct nvmap_handle *h) nvmap_client_put(client); } +extern void __flush_dcache_page(struct address_space *, struct page *); + +static struct page *nvmap_alloc_pages_exact(gfp_t gfp, size_t size) +{ + struct page *page, *p, *e; + unsigned int order; + unsigned long base; + + size = PAGE_ALIGN(size); + order = get_order(size); + page = alloc_pages(gfp, order); + + if (!page) + return NULL; + + split_page(page, order); + + e = page + (1 << order); + for (p = page + (size >> PAGE_SHIFT); p < e; p++) + __free_page(p); + + e = page + (size >> PAGE_SHIFT); + for (p = page; p < e; p++) + __flush_dcache_page(page_mapping(p), p); + + base = page_to_phys(page); + outer_flush_range(base, base + size); + return page; +} + static int handle_page_alloc(struct nvmap_client *client, struct nvmap_handle *h, bool contiguous) { @@ -120,14 +155,16 @@ static int handle_page_alloc(struct nvmap_client *client, h->pgalloc.area = NULL; if (contiguous) { struct page *page; - page = arm_attrib_alloc_pages_exact(GFP_NVMAP, size, prot); + page = nvmap_alloc_pages_exact(GFP_NVMAP, size); + if (!page) + goto fail; for (i = 0; i < nr_page; i++) pages[i] = nth_page(page, i); } else { for (i = 0; i < nr_page; i++) { - pages[i] = arm_attrib_alloc_page(GFP_NVMAP, prot); + pages[i] = nvmap_alloc_pages_exact(GFP_NVMAP, PAGE_SIZE); if (!pages[i]) goto fail; } @@ -151,7 +188,7 @@ static int handle_page_alloc(struct nvmap_client *client, fail: while (i--) - arm_attrib_free_page(pages[i]); + __free_page(pages[i]); altfree(pages, nr_page * sizeof(*pages)); return -ENOMEM; } @@ -210,7 +247,9 @@ static void alloc_handle(struct nvmap_client *client, size_t align, * sub-page splinters */ static const unsigned int heap_policy_small[] = { NVMAP_HEAP_CARVEOUT_IRAM, +#ifdef CONFIG_NVMAP_ALLOW_SYSMEM NVMAP_HEAP_SYSMEM, +#endif NVMAP_HEAP_CARVEOUT_MASK, NVMAP_HEAP_IOVMM, 0, @@ -220,7 +259,9 @@ static const unsigned int heap_policy_large[] = { NVMAP_HEAP_CARVEOUT_IRAM, NVMAP_HEAP_IOVMM, NVMAP_HEAP_CARVEOUT_MASK, +#ifdef CONFIG_NVMAP_ALLOW_SYSMEM NVMAP_HEAP_SYSMEM, +#endif 0, }; From 7f91ddc13588a66d2268391cbbd73d8268f92ef1 Mon Sep 17 00:00:00 2001 From: Gary King Date: Tue, 12 Oct 2010 18:10:31 -0700 Subject: [PATCH 2/2] Revert "[ARM] mm: add page allocator for modifying cache attributes" This reverts commit 54d414570432ce07fa1a14b657f53bed752e3d7e. Change-Id: I8e5cf6ef3555129da9741ef52a1e6a3a772ad588 Signed-off-by: Gary King --- arch/arm/include/asm/attrib_alloc.h | 47 ----------- arch/arm/mm/Kconfig | 23 ----- arch/arm/mm/Makefile | 2 - arch/arm/mm/attrib_alloc.c | 126 ---------------------------- 4 files changed, 198 deletions(-) delete mode 100644 arch/arm/include/asm/attrib_alloc.h delete mode 100644 arch/arm/mm/attrib_alloc.c diff --git a/arch/arm/include/asm/attrib_alloc.h b/arch/arm/include/asm/attrib_alloc.h deleted file mode 100644 index bd21761810f5..000000000000 --- a/arch/arm/include/asm/attrib_alloc.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * arch/arm/include/asm/attrib_alloc.h - * - * Page allocator with custom cache attributes - * - * Copyright (c) 2010, NVIDIA Corporation. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#ifndef __ARCH_ARM_ATTRIB_ALLOC_H -#define __ARCH_ARM_ATTRIB_ALLOC_H - -#include -#include - -struct page *arm_attrib_alloc_pages_exact_node(int nid, gfp_t gfp, - size_t size, pgprot_t prot); - -void arm_attrib_free_pages_exact(struct page *page, size_t size); - -static inline -struct page *arm_attrib_alloc_pages_exact(gfp_t gfp, size_t size, - pgprot_t prot) -{ - return arm_attrib_alloc_pages_exact_node(-1, gfp, size, prot); -} - -#define arm_attrib_alloc_page(gfp, prot) \ - arm_attrib_alloc_pages_exact((gfp), PAGE_SIZE, (prot)) - -#define arm_attrib_free_page(page) \ - arm_attrib_free_pages_exact((page), PAGE_SIZE) - -#endif /* __ARCH_ARM_ATTRIB_ALLOC_H */ diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig index da057fcd0491..cc6f9d6193dd 100644 --- a/arch/arm/mm/Kconfig +++ b/arch/arm/mm/Kconfig @@ -808,29 +808,6 @@ config ARM_L1_CACHE_SHIFT default 6 if ARM_L1_CACHE_SHIFT_6 default 5 -config ARM_ATTRIB_ALLOCATOR - bool "Support custom cache attribute allocations in low memory" - select ARCH_LOWMEM_IN_PTES if (CPU_V7) - depends on MMU && !CPU_CACHE_VIVT - help - Historically, the kernel has only reserved a small region - of physical memory for uncached access, and relied on - explicit cache maintenance for ensuring coherency between - the CPU and DMA. - - However, many recent systems support mapping discontiguous - physical pages into contiguous DMA addresses (so-called - system MMUs). For some DMA clients (notably graphics and - multimedia engines), performing explict cache maintenance - between CPU and DMA mappings can be prohibitively expensive, - and since ARMv7, mapping the same physical page with different - cache attributes is disallowed and has unpredictable behavior. - - Say 'Y' here to include page allocation support with explicit - cache attributes; on ARMv7 systems this will also force the - kernel's page tables to be mapped using page tables rather - than sections. - config ARM_DMA_MEM_BUFFERABLE bool "Use non-cacheable memory for DMA" if CPU_V6 && !CPU_V7 depends on !(MACH_REALVIEW_PB1176 || REALVIEW_EB_ARM11MP || \ diff --git a/arch/arm/mm/Makefile b/arch/arm/mm/Makefile index 16e8164b63bc..d63b6c413758 100644 --- a/arch/arm/mm/Makefile +++ b/arch/arm/mm/Makefile @@ -12,8 +12,6 @@ ifneq ($(CONFIG_MMU),y) obj-y += nommu.o endif -obj-$(CONFIG_ARM_ATTRIB_ALLOCATOR) += attrib_alloc.o - obj-$(CONFIG_MODULES) += proc-syms.o obj-$(CONFIG_ALIGNMENT_TRAP) += alignment.o diff --git a/arch/arm/mm/attrib_alloc.c b/arch/arm/mm/attrib_alloc.c deleted file mode 100644 index 73be2d664669..000000000000 --- a/arch/arm/mm/attrib_alloc.c +++ /dev/null @@ -1,126 +0,0 @@ -/* - * arch/arm/mm/attrib_alloc.c - * - * Page allocator with custom cache attributes - * - * Copyright (c) 2010, NVIDIA Corporation. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "mm.h" - -static void update_kmap_pte(struct page *page, pgprot_t prot) -{ -#ifdef CONFIG_HIGHMEM - unsigned long addr; - pte_t *pte; - - addr = (unsigned long)kmap_high_get(page); - BUG_ON(!PageHighMem(page) || addr >= FIXADDR_START); - if (!addr) - return; - - pte = &pkmap_page_table[PKMAP_NR(addr)]; - set_pte_at(&init_mm, addr, pte, mk_pte(page, __pgprot(prot))); - flush_tlb_kernel_range(addr, addr + PAGE_SIZE); - kunmap_high(page); -#endif -} - -static void update_pte(struct page *page, pgprot_t prot) -{ -#ifdef CONFIG_ARCH_LOWMEM_IN_PTES - unsigned long addr = (unsigned long)page_address(page); - pgd_t *pgd = pgd_offset_k(addr); - pmd_t *pmd = pmd_offset(pgd, addr); - pte_t *pte; - - BUG_ON(pmd_none(*pmd)); - pte = pte_offset_kernel(pmd, addr); - set_pte_at(&init_mm, addr, pte, mk_pte(page, __pgprot(prot))); - flush_tlb_kernel_range(addr, addr + PAGE_SIZE); -#endif -} - -void arm_attrib_free_pages_exact(struct page *page, size_t size) -{ - struct page *p, *e; - - size = PAGE_ALIGN(size); - e = page + (size >> PAGE_SHIFT); - - /* reset the page's mappings back to the standard kernel mappings - * before returning it to the page allocator */ - for (p = page; p < e; p++) { - if (PageHighMem(p)) - update_kmap_pte(p, pgprot_kernel); - else - update_pte(p, pgprot_kernel); - - __free_page(p); - } -} - -struct page *arm_attrib_alloc_pages_exact_node(int nid, gfp_t gfp, - size_t size, pgprot_t prot) -{ - struct page *page, *p, *e; - unsigned int order; - unsigned long base; - - size = PAGE_ALIGN(size); - order = get_order(size); - page = alloc_pages_node(nid, gfp, order); - - if (!page) - return NULL; - - split_page(page, order); - - e = page + (1 << order); - for (p = page + (size >> PAGE_SHIFT); p < e; p++) - __free_page(p); - - e = page + (size >> PAGE_SHIFT); - - for (p = page; p < e; p++) { - __flush_dcache_page(page_mapping(p), p); - - /* even though a freshly-allocated highmem page shouldn't - * be mapped, because the kmaps are flushed lazily, it - * is possible that a mapping from an old kmap_high call - * is still present, and its cache attributes need to - * be updated to match the new expectations */ - if (PageHighMem(p)) - update_kmap_pte(p, prot); - else - update_pte(p, prot); - } - base = page_to_phys(page); - outer_flush_range(base, base + size); - return page; -}