1*f14f75b8SJes Sorensen /* 2*f14f75b8SJes Sorensen * Copyright (C) 2001-2005 Silicon Graphics, Inc. All rights reserved. 3*f14f75b8SJes Sorensen * 4*f14f75b8SJes Sorensen * This program is free software; you can redistribute it and/or modify it 5*f14f75b8SJes Sorensen * under the terms of version 2 of the GNU General Public License 6*f14f75b8SJes Sorensen * as published by the Free Software Foundation. 7*f14f75b8SJes Sorensen * 8*f14f75b8SJes Sorensen * A simple uncached page allocator using the generic allocator. This 9*f14f75b8SJes Sorensen * allocator first utilizes the spare (spill) pages found in the EFI 10*f14f75b8SJes Sorensen * memmap and will then start converting cached pages to uncached ones 11*f14f75b8SJes Sorensen * at a granule at a time. Node awareness is implemented by having a 12*f14f75b8SJes Sorensen * pool of pages per node. 13*f14f75b8SJes Sorensen */ 14*f14f75b8SJes Sorensen 15*f14f75b8SJes Sorensen #include <linux/types.h> 16*f14f75b8SJes Sorensen #include <linux/kernel.h> 17*f14f75b8SJes Sorensen #include <linux/module.h> 18*f14f75b8SJes Sorensen #include <linux/init.h> 19*f14f75b8SJes Sorensen #include <linux/errno.h> 20*f14f75b8SJes Sorensen #include <linux/string.h> 21*f14f75b8SJes Sorensen #include <linux/slab.h> 22*f14f75b8SJes Sorensen #include <linux/efi.h> 23*f14f75b8SJes Sorensen #include <linux/genalloc.h> 24*f14f75b8SJes Sorensen #include <asm/page.h> 25*f14f75b8SJes Sorensen #include <asm/pal.h> 26*f14f75b8SJes Sorensen #include <asm/system.h> 27*f14f75b8SJes Sorensen #include <asm/pgtable.h> 28*f14f75b8SJes Sorensen #include <asm/atomic.h> 29*f14f75b8SJes Sorensen #include <asm/tlbflush.h> 30*f14f75b8SJes Sorensen #include <asm/sn/arch.h> 31*f14f75b8SJes Sorensen 32*f14f75b8SJes Sorensen #define DEBUG 0 33*f14f75b8SJes Sorensen 34*f14f75b8SJes Sorensen #if DEBUG 35*f14f75b8SJes Sorensen #define dprintk printk 36*f14f75b8SJes Sorensen #else 37*f14f75b8SJes Sorensen #define dprintk(x...) do { } while (0) 38*f14f75b8SJes Sorensen #endif 39*f14f75b8SJes Sorensen 40*f14f75b8SJes Sorensen void __init efi_memmap_walk_uc (efi_freemem_callback_t callback); 41*f14f75b8SJes Sorensen 42*f14f75b8SJes Sorensen #define MAX_UNCACHED_GRANULES 5 43*f14f75b8SJes Sorensen static int allocated_granules; 44*f14f75b8SJes Sorensen 45*f14f75b8SJes Sorensen struct gen_pool *uncached_pool[MAX_NUMNODES]; 46*f14f75b8SJes Sorensen 47*f14f75b8SJes Sorensen 48*f14f75b8SJes Sorensen static void uncached_ipi_visibility(void *data) 49*f14f75b8SJes Sorensen { 50*f14f75b8SJes Sorensen int status; 51*f14f75b8SJes Sorensen 52*f14f75b8SJes Sorensen status = ia64_pal_prefetch_visibility(PAL_VISIBILITY_PHYSICAL); 53*f14f75b8SJes Sorensen if ((status != PAL_VISIBILITY_OK) && 54*f14f75b8SJes Sorensen (status != PAL_VISIBILITY_OK_REMOTE_NEEDED)) 55*f14f75b8SJes Sorensen printk(KERN_DEBUG "pal_prefetch_visibility() returns %i on " 56*f14f75b8SJes Sorensen "CPU %i\n", status, get_cpu()); 57*f14f75b8SJes Sorensen } 58*f14f75b8SJes Sorensen 59*f14f75b8SJes Sorensen 60*f14f75b8SJes Sorensen static void uncached_ipi_mc_drain(void *data) 61*f14f75b8SJes Sorensen { 62*f14f75b8SJes Sorensen int status; 63*f14f75b8SJes Sorensen status = ia64_pal_mc_drain(); 64*f14f75b8SJes Sorensen if (status) 65*f14f75b8SJes Sorensen printk(KERN_WARNING "ia64_pal_mc_drain() failed with %i on " 66*f14f75b8SJes Sorensen "CPU %i\n", status, get_cpu()); 67*f14f75b8SJes Sorensen } 68*f14f75b8SJes Sorensen 69*f14f75b8SJes Sorensen 70*f14f75b8SJes Sorensen static unsigned long 71*f14f75b8SJes Sorensen uncached_get_new_chunk(struct gen_pool *poolp) 72*f14f75b8SJes Sorensen { 73*f14f75b8SJes Sorensen struct page *page; 74*f14f75b8SJes Sorensen void *tmp; 75*f14f75b8SJes Sorensen int status, i; 76*f14f75b8SJes Sorensen unsigned long addr, node; 77*f14f75b8SJes Sorensen 78*f14f75b8SJes Sorensen if (allocated_granules >= MAX_UNCACHED_GRANULES) 79*f14f75b8SJes Sorensen return 0; 80*f14f75b8SJes Sorensen 81*f14f75b8SJes Sorensen node = poolp->private; 82*f14f75b8SJes Sorensen page = alloc_pages_node(node, GFP_KERNEL | __GFP_ZERO, 83*f14f75b8SJes Sorensen IA64_GRANULE_SHIFT-PAGE_SHIFT); 84*f14f75b8SJes Sorensen 85*f14f75b8SJes Sorensen dprintk(KERN_INFO "get_new_chunk page %p, addr %lx\n", 86*f14f75b8SJes Sorensen page, (unsigned long)(page-vmem_map) << PAGE_SHIFT); 87*f14f75b8SJes Sorensen 88*f14f75b8SJes Sorensen /* 89*f14f75b8SJes Sorensen * Do magic if no mem on local node! XXX 90*f14f75b8SJes Sorensen */ 91*f14f75b8SJes Sorensen if (!page) 92*f14f75b8SJes Sorensen return 0; 93*f14f75b8SJes Sorensen tmp = page_address(page); 94*f14f75b8SJes Sorensen 95*f14f75b8SJes Sorensen /* 96*f14f75b8SJes Sorensen * There's a small race here where it's possible for someone to 97*f14f75b8SJes Sorensen * access the page through /dev/mem halfway through the conversion 98*f14f75b8SJes Sorensen * to uncached - not sure it's really worth bothering about 99*f14f75b8SJes Sorensen */ 100*f14f75b8SJes Sorensen for (i = 0; i < (IA64_GRANULE_SIZE / PAGE_SIZE); i++) 101*f14f75b8SJes Sorensen SetPageUncached(&page[i]); 102*f14f75b8SJes Sorensen 103*f14f75b8SJes Sorensen flush_tlb_kernel_range(tmp, tmp + IA64_GRANULE_SIZE); 104*f14f75b8SJes Sorensen 105*f14f75b8SJes Sorensen status = ia64_pal_prefetch_visibility(PAL_VISIBILITY_PHYSICAL); 106*f14f75b8SJes Sorensen 107*f14f75b8SJes Sorensen dprintk(KERN_INFO "pal_prefetch_visibility() returns %i on cpu %i\n", 108*f14f75b8SJes Sorensen status, get_cpu()); 109*f14f75b8SJes Sorensen 110*f14f75b8SJes Sorensen if (!status) { 111*f14f75b8SJes Sorensen status = smp_call_function(uncached_ipi_visibility, NULL, 0, 1); 112*f14f75b8SJes Sorensen if (status) 113*f14f75b8SJes Sorensen printk(KERN_WARNING "smp_call_function failed for " 114*f14f75b8SJes Sorensen "uncached_ipi_visibility! (%i)\n", status); 115*f14f75b8SJes Sorensen } 116*f14f75b8SJes Sorensen 117*f14f75b8SJes Sorensen if (ia64_platform_is("sn2")) 118*f14f75b8SJes Sorensen sn_flush_all_caches((unsigned long)tmp, IA64_GRANULE_SIZE); 119*f14f75b8SJes Sorensen else 120*f14f75b8SJes Sorensen flush_icache_range((unsigned long)tmp, 121*f14f75b8SJes Sorensen (unsigned long)tmp+IA64_GRANULE_SIZE); 122*f14f75b8SJes Sorensen 123*f14f75b8SJes Sorensen ia64_pal_mc_drain(); 124*f14f75b8SJes Sorensen status = smp_call_function(uncached_ipi_mc_drain, NULL, 0, 1); 125*f14f75b8SJes Sorensen if (status) 126*f14f75b8SJes Sorensen printk(KERN_WARNING "smp_call_function failed for " 127*f14f75b8SJes Sorensen "uncached_ipi_mc_drain! (%i)\n", status); 128*f14f75b8SJes Sorensen 129*f14f75b8SJes Sorensen addr = (unsigned long)tmp - PAGE_OFFSET + __IA64_UNCACHED_OFFSET; 130*f14f75b8SJes Sorensen 131*f14f75b8SJes Sorensen allocated_granules++; 132*f14f75b8SJes Sorensen return addr; 133*f14f75b8SJes Sorensen } 134*f14f75b8SJes Sorensen 135*f14f75b8SJes Sorensen 136*f14f75b8SJes Sorensen /* 137*f14f75b8SJes Sorensen * uncached_alloc_page 138*f14f75b8SJes Sorensen * 139*f14f75b8SJes Sorensen * Allocate 1 uncached page. Allocates on the requested node. If no 140*f14f75b8SJes Sorensen * uncached pages are available on the requested node, roundrobin starting 141*f14f75b8SJes Sorensen * with higher nodes. 142*f14f75b8SJes Sorensen */ 143*f14f75b8SJes Sorensen unsigned long 144*f14f75b8SJes Sorensen uncached_alloc_page(int nid) 145*f14f75b8SJes Sorensen { 146*f14f75b8SJes Sorensen unsigned long maddr; 147*f14f75b8SJes Sorensen 148*f14f75b8SJes Sorensen maddr = gen_pool_alloc(uncached_pool[nid], PAGE_SIZE); 149*f14f75b8SJes Sorensen 150*f14f75b8SJes Sorensen dprintk(KERN_DEBUG "uncached_alloc_page returns %lx on node %i\n", 151*f14f75b8SJes Sorensen maddr, nid); 152*f14f75b8SJes Sorensen 153*f14f75b8SJes Sorensen /* 154*f14f75b8SJes Sorensen * If no memory is availble on our local node, try the 155*f14f75b8SJes Sorensen * remaining nodes in the system. 156*f14f75b8SJes Sorensen */ 157*f14f75b8SJes Sorensen if (!maddr) { 158*f14f75b8SJes Sorensen int i; 159*f14f75b8SJes Sorensen 160*f14f75b8SJes Sorensen for (i = MAX_NUMNODES - 1; i >= 0; i--) { 161*f14f75b8SJes Sorensen if (i == nid || !node_online(i)) 162*f14f75b8SJes Sorensen continue; 163*f14f75b8SJes Sorensen maddr = gen_pool_alloc(uncached_pool[i], PAGE_SIZE); 164*f14f75b8SJes Sorensen dprintk(KERN_DEBUG "uncached_alloc_page alternate search " 165*f14f75b8SJes Sorensen "returns %lx on node %i\n", maddr, i); 166*f14f75b8SJes Sorensen if (maddr) { 167*f14f75b8SJes Sorensen break; 168*f14f75b8SJes Sorensen } 169*f14f75b8SJes Sorensen } 170*f14f75b8SJes Sorensen } 171*f14f75b8SJes Sorensen 172*f14f75b8SJes Sorensen return maddr; 173*f14f75b8SJes Sorensen } 174*f14f75b8SJes Sorensen EXPORT_SYMBOL(uncached_alloc_page); 175*f14f75b8SJes Sorensen 176*f14f75b8SJes Sorensen 177*f14f75b8SJes Sorensen /* 178*f14f75b8SJes Sorensen * uncached_free_page 179*f14f75b8SJes Sorensen * 180*f14f75b8SJes Sorensen * Free a single uncached page. 181*f14f75b8SJes Sorensen */ 182*f14f75b8SJes Sorensen void 183*f14f75b8SJes Sorensen uncached_free_page(unsigned long maddr) 184*f14f75b8SJes Sorensen { 185*f14f75b8SJes Sorensen int node; 186*f14f75b8SJes Sorensen 187*f14f75b8SJes Sorensen node = nasid_to_cnodeid(NASID_GET(maddr)); 188*f14f75b8SJes Sorensen 189*f14f75b8SJes Sorensen dprintk(KERN_DEBUG "uncached_free_page(%lx) on node %i\n", maddr, node); 190*f14f75b8SJes Sorensen 191*f14f75b8SJes Sorensen if ((maddr & (0XFUL << 60)) != __IA64_UNCACHED_OFFSET) 192*f14f75b8SJes Sorensen panic("uncached_free_page invalid address %lx\n", maddr); 193*f14f75b8SJes Sorensen 194*f14f75b8SJes Sorensen gen_pool_free(uncached_pool[node], maddr, PAGE_SIZE); 195*f14f75b8SJes Sorensen } 196*f14f75b8SJes Sorensen EXPORT_SYMBOL(uncached_free_page); 197*f14f75b8SJes Sorensen 198*f14f75b8SJes Sorensen 199*f14f75b8SJes Sorensen /* 200*f14f75b8SJes Sorensen * uncached_build_memmap, 201*f14f75b8SJes Sorensen * 202*f14f75b8SJes Sorensen * Called at boot time to build a map of pages that can be used for 203*f14f75b8SJes Sorensen * memory special operations. 204*f14f75b8SJes Sorensen */ 205*f14f75b8SJes Sorensen static int __init 206*f14f75b8SJes Sorensen uncached_build_memmap(unsigned long start, unsigned long end, void *arg) 207*f14f75b8SJes Sorensen { 208*f14f75b8SJes Sorensen long length; 209*f14f75b8SJes Sorensen unsigned long vstart, vend; 210*f14f75b8SJes Sorensen int node; 211*f14f75b8SJes Sorensen 212*f14f75b8SJes Sorensen length = end - start; 213*f14f75b8SJes Sorensen vstart = start + __IA64_UNCACHED_OFFSET; 214*f14f75b8SJes Sorensen vend = end + __IA64_UNCACHED_OFFSET; 215*f14f75b8SJes Sorensen 216*f14f75b8SJes Sorensen dprintk(KERN_ERR "uncached_build_memmap(%lx %lx)\n", start, end); 217*f14f75b8SJes Sorensen 218*f14f75b8SJes Sorensen memset((char *)vstart, 0, length); 219*f14f75b8SJes Sorensen 220*f14f75b8SJes Sorensen node = nasid_to_cnodeid(NASID_GET(start)); 221*f14f75b8SJes Sorensen 222*f14f75b8SJes Sorensen for (; vstart < vend ; vstart += PAGE_SIZE) { 223*f14f75b8SJes Sorensen dprintk(KERN_INFO "sticking %lx into the pool!\n", vstart); 224*f14f75b8SJes Sorensen gen_pool_free(uncached_pool[node], vstart, PAGE_SIZE); 225*f14f75b8SJes Sorensen } 226*f14f75b8SJes Sorensen 227*f14f75b8SJes Sorensen return 0; 228*f14f75b8SJes Sorensen } 229*f14f75b8SJes Sorensen 230*f14f75b8SJes Sorensen 231*f14f75b8SJes Sorensen static int __init uncached_init(void) { 232*f14f75b8SJes Sorensen int i; 233*f14f75b8SJes Sorensen 234*f14f75b8SJes Sorensen for (i = 0; i < MAX_NUMNODES; i++) { 235*f14f75b8SJes Sorensen if (!node_online(i)) 236*f14f75b8SJes Sorensen continue; 237*f14f75b8SJes Sorensen uncached_pool[i] = gen_pool_create(0, IA64_GRANULE_SHIFT, 238*f14f75b8SJes Sorensen &uncached_get_new_chunk, i); 239*f14f75b8SJes Sorensen } 240*f14f75b8SJes Sorensen 241*f14f75b8SJes Sorensen efi_memmap_walk_uc(uncached_build_memmap); 242*f14f75b8SJes Sorensen 243*f14f75b8SJes Sorensen return 0; 244*f14f75b8SJes Sorensen } 245*f14f75b8SJes Sorensen 246*f14f75b8SJes Sorensen __initcall(uncached_init); 247