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