📄 uncached.c
字号:
/* * Copyright (C) 2001-2005 Silicon Graphics, Inc. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License * as published by the Free Software Foundation. * * A simple uncached page allocator using the generic allocator. This * allocator first utilizes the spare (spill) pages found in the EFI * memmap and will then start converting cached pages to uncached ones * at a granule at a time. Node awareness is implemented by having a * pool of pages per node. */#include <linux/types.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/init.h>#include <linux/errno.h>#include <linux/string.h>#include <linux/slab.h>#include <linux/efi.h>#include <linux/genalloc.h>#include <asm/page.h>#include <asm/pal.h>#include <asm/system.h>#include <asm/pgtable.h>#include <asm/atomic.h>#include <asm/tlbflush.h>#include <asm/sn/arch.h>#define DEBUG 0#if DEBUG#define dprintk printk#else#define dprintk(x...) do { } while (0)#endifvoid __init efi_memmap_walk_uc (efi_freemem_callback_t callback);#define MAX_UNCACHED_GRANULES 5static int allocated_granules;struct gen_pool *uncached_pool[MAX_NUMNODES];static void uncached_ipi_visibility(void *data){ int status; status = ia64_pal_prefetch_visibility(PAL_VISIBILITY_PHYSICAL); if ((status != PAL_VISIBILITY_OK) && (status != PAL_VISIBILITY_OK_REMOTE_NEEDED)) printk(KERN_DEBUG "pal_prefetch_visibility() returns %i on " "CPU %i\n", status, raw_smp_processor_id());}static void uncached_ipi_mc_drain(void *data){ int status; status = ia64_pal_mc_drain(); if (status) printk(KERN_WARNING "ia64_pal_mc_drain() failed with %i on " "CPU %i\n", status, raw_smp_processor_id());}static unsigned longuncached_get_new_chunk(struct gen_pool *poolp){ struct page *page; void *tmp; int status, i; unsigned long addr, node; if (allocated_granules >= MAX_UNCACHED_GRANULES) return 0; node = poolp->private; page = alloc_pages_node(node, GFP_KERNEL | __GFP_ZERO, IA64_GRANULE_SHIFT-PAGE_SHIFT); dprintk(KERN_INFO "get_new_chunk page %p, addr %lx\n", page, (unsigned long)(page-vmem_map) << PAGE_SHIFT); /* * Do magic if no mem on local node! XXX */ if (!page) return 0; tmp = page_address(page); /* * There's a small race here where it's possible for someone to * access the page through /dev/mem halfway through the conversion * to uncached - not sure it's really worth bothering about */ for (i = 0; i < (IA64_GRANULE_SIZE / PAGE_SIZE); i++) SetPageUncached(&page[i]); flush_tlb_kernel_range(tmp, tmp + IA64_GRANULE_SIZE); status = ia64_pal_prefetch_visibility(PAL_VISIBILITY_PHYSICAL); dprintk(KERN_INFO "pal_prefetch_visibility() returns %i on cpu %i\n", status, raw_smp_processor_id()); if (!status) { status = smp_call_function(uncached_ipi_visibility, NULL, 0, 1); if (status) printk(KERN_WARNING "smp_call_function failed for " "uncached_ipi_visibility! (%i)\n", status); } if (ia64_platform_is("sn2")) sn_flush_all_caches((unsigned long)tmp, IA64_GRANULE_SIZE); else flush_icache_range((unsigned long)tmp, (unsigned long)tmp+IA64_GRANULE_SIZE); ia64_pal_mc_drain(); status = smp_call_function(uncached_ipi_mc_drain, NULL, 0, 1); if (status) printk(KERN_WARNING "smp_call_function failed for " "uncached_ipi_mc_drain! (%i)\n", status); addr = (unsigned long)tmp - PAGE_OFFSET + __IA64_UNCACHED_OFFSET; allocated_granules++; return addr;}/* * uncached_alloc_page * * Allocate 1 uncached page. Allocates on the requested node. If no * uncached pages are available on the requested node, roundrobin starting * with higher nodes. */unsigned longuncached_alloc_page(int nid){ unsigned long maddr; maddr = gen_pool_alloc(uncached_pool[nid], PAGE_SIZE); dprintk(KERN_DEBUG "uncached_alloc_page returns %lx on node %i\n", maddr, nid); /* * If no memory is availble on our local node, try the * remaining nodes in the system. */ if (!maddr) { int i; for (i = MAX_NUMNODES - 1; i >= 0; i--) { if (i == nid || !node_online(i)) continue; maddr = gen_pool_alloc(uncached_pool[i], PAGE_SIZE); dprintk(KERN_DEBUG "uncached_alloc_page alternate search " "returns %lx on node %i\n", maddr, i); if (maddr) { break; } } } return maddr;}EXPORT_SYMBOL(uncached_alloc_page);/* * uncached_free_page * * Free a single uncached page. */voiduncached_free_page(unsigned long maddr){ int node; node = paddr_to_nid(maddr - __IA64_UNCACHED_OFFSET); dprintk(KERN_DEBUG "uncached_free_page(%lx) on node %i\n", maddr, node); if ((maddr & (0XFUL << 60)) != __IA64_UNCACHED_OFFSET) panic("uncached_free_page invalid address %lx\n", maddr); gen_pool_free(uncached_pool[node], maddr, PAGE_SIZE);}EXPORT_SYMBOL(uncached_free_page);/* * uncached_build_memmap, * * Called at boot time to build a map of pages that can be used for * memory special operations. */static int __inituncached_build_memmap(unsigned long start, unsigned long end, void *arg){ long length = end - start; int node; dprintk(KERN_ERR "uncached_build_memmap(%lx %lx)\n", start, end); memset((char *)start, 0, length); node = paddr_to_nid(start - __IA64_UNCACHED_OFFSET); for (; start < end ; start += PAGE_SIZE) { dprintk(KERN_INFO "sticking %lx into the pool!\n", start); gen_pool_free(uncached_pool[node], start, PAGE_SIZE); } return 0;}static int __init uncached_init(void) { int i; for (i = 0; i < MAX_NUMNODES; i++) { if (!node_online(i)) continue; uncached_pool[i] = gen_pool_create(0, IA64_GRANULE_SHIFT, &uncached_get_new_chunk, i); } efi_memmap_walk_uc(uncached_build_memmap); return 0;}__initcall(uncached_init);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -