125763b3cSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 217a3b050SJes Sorensen /* 317a3b050SJes Sorensen * Copyright (C) 2001-2006 Silicon Graphics, Inc. All rights 417a3b050SJes Sorensen * reserved. 517a3b050SJes Sorensen */ 617a3b050SJes Sorensen 717a3b050SJes Sorensen /* 817a3b050SJes Sorensen * SN Platform Special Memory (mspec) Support 917a3b050SJes Sorensen * 1017a3b050SJes Sorensen * This driver exports the SN special memory (mspec) facility to user 1117a3b050SJes Sorensen * processes. 1217a3b050SJes Sorensen * There are three types of memory made available thru this driver: 1317a3b050SJes Sorensen * fetchops, uncached and cached. 1417a3b050SJes Sorensen * 1517a3b050SJes Sorensen * Fetchops are atomic memory operations that are implemented in the 1617a3b050SJes Sorensen * memory controller on SGI SN hardware. 1717a3b050SJes Sorensen * 1817a3b050SJes Sorensen * Uncached are used for memory write combining feature of the ia64 1917a3b050SJes Sorensen * cpu. 2017a3b050SJes Sorensen * 2117a3b050SJes Sorensen * Cached are used for areas of memory that are used as cached addresses 2217a3b050SJes Sorensen * on our partition and used as uncached addresses from other partitions. 2317a3b050SJes Sorensen * Due to a design constraint of the SN2 Shub, you can not have processors 2417a3b050SJes Sorensen * on the same FSB perform both a cached and uncached reference to the 2517a3b050SJes Sorensen * same cache line. These special memory cached regions prevent the 2617a3b050SJes Sorensen * kernel from ever dropping in a TLB entry and therefore prevent the 2717a3b050SJes Sorensen * processor from ever speculating a cache line from this page. 2817a3b050SJes Sorensen */ 2917a3b050SJes Sorensen 3017a3b050SJes Sorensen #include <linux/types.h> 3117a3b050SJes Sorensen #include <linux/kernel.h> 3217a3b050SJes Sorensen #include <linux/module.h> 3317a3b050SJes Sorensen #include <linux/init.h> 3417a3b050SJes Sorensen #include <linux/errno.h> 3517a3b050SJes Sorensen #include <linux/miscdevice.h> 3617a3b050SJes Sorensen #include <linux/spinlock.h> 3717a3b050SJes Sorensen #include <linux/mm.h> 384e950f6fSAlexey Dobriyan #include <linux/fs.h> 3917a3b050SJes Sorensen #include <linux/vmalloc.h> 4017a3b050SJes Sorensen #include <linux/string.h> 4117a3b050SJes Sorensen #include <linux/slab.h> 4217a3b050SJes Sorensen #include <linux/numa.h> 43f7d88d24SElena Reshetova #include <linux/refcount.h> 4417a3b050SJes Sorensen #include <asm/page.h> 4517a3b050SJes Sorensen #include <asm/pgtable.h> 4660063497SArun Sharma #include <linux/atomic.h> 4717a3b050SJes Sorensen #include <asm/tlbflush.h> 4817a3b050SJes Sorensen #include <asm/uncached.h> 4917a3b050SJes Sorensen #include <asm/sn/addrs.h> 5017a3b050SJes Sorensen #include <asm/sn/arch.h> 5117a3b050SJes Sorensen #include <asm/sn/mspec.h> 5217a3b050SJes Sorensen #include <asm/sn/sn_cpuid.h> 5317a3b050SJes Sorensen #include <asm/sn/io.h> 5417a3b050SJes Sorensen #include <asm/sn/bte.h> 5517a3b050SJes Sorensen #include <asm/sn/shubio.h> 5617a3b050SJes Sorensen 5717a3b050SJes Sorensen 5817a3b050SJes Sorensen #define FETCHOP_ID "SGI Fetchop," 5917a3b050SJes Sorensen #define CACHED_ID "Cached," 6017a3b050SJes Sorensen #define UNCACHED_ID "Uncached" 6117a3b050SJes Sorensen #define REVISION "4.0" 6217a3b050SJes Sorensen #define MSPEC_BASENAME "mspec" 6317a3b050SJes Sorensen 6417a3b050SJes Sorensen /* 6517a3b050SJes Sorensen * Page types allocated by the device. 6617a3b050SJes Sorensen */ 674191ba26SCliff Wickman enum mspec_page_type { 6817a3b050SJes Sorensen MSPEC_FETCHOP = 1, 6917a3b050SJes Sorensen MSPEC_CACHED, 7017a3b050SJes Sorensen MSPEC_UNCACHED 7117a3b050SJes Sorensen }; 7217a3b050SJes Sorensen 731a4b0fc5SJes Sorensen #ifdef CONFIG_SGI_SN 7417a3b050SJes Sorensen static int is_sn2; 751a4b0fc5SJes Sorensen #else 761a4b0fc5SJes Sorensen #define is_sn2 0 771a4b0fc5SJes Sorensen #endif 7817a3b050SJes Sorensen 7917a3b050SJes Sorensen /* 8017a3b050SJes Sorensen * One of these structures is allocated when an mspec region is mmaped. The 8117a3b050SJes Sorensen * structure is pointed to by the vma->vm_private_data field in the vma struct. 8217a3b050SJes Sorensen * This structure is used to record the addresses of the mspec pages. 834191ba26SCliff Wickman * This structure is shared by all vma's that are split off from the 844191ba26SCliff Wickman * original vma when split_vma()'s are done. 854191ba26SCliff Wickman * 864191ba26SCliff Wickman * The refcnt is incremented atomically because mm->mmap_sem does not 874191ba26SCliff Wickman * protect in fork case where multiple tasks share the vma_data. 8817a3b050SJes Sorensen */ 8917a3b050SJes Sorensen struct vma_data { 90f7d88d24SElena Reshetova refcount_t refcnt; /* Number of vmas sharing the data. */ 914191ba26SCliff Wickman spinlock_t lock; /* Serialize access to this structure. */ 9217a3b050SJes Sorensen int count; /* Number of pages allocated. */ 934191ba26SCliff Wickman enum mspec_page_type type; /* Type of pages allocated. */ 944191ba26SCliff Wickman unsigned long vm_start; /* Original (unsplit) base. */ 954191ba26SCliff Wickman unsigned long vm_end; /* Original (unsplit) end. */ 9617a3b050SJes Sorensen unsigned long maddr[0]; /* Array of MSPEC addresses. */ 9717a3b050SJes Sorensen }; 9817a3b050SJes Sorensen 9917a3b050SJes Sorensen /* used on shub2 to clear FOP cache in the HUB */ 10017a3b050SJes Sorensen static unsigned long scratch_page[MAX_NUMNODES]; 10117a3b050SJes Sorensen #define SH2_AMO_CACHE_ENTRIES 4 10217a3b050SJes Sorensen 10317a3b050SJes Sorensen static inline int 10417a3b050SJes Sorensen mspec_zero_block(unsigned long addr, int len) 10517a3b050SJes Sorensen { 10617a3b050SJes Sorensen int status; 10717a3b050SJes Sorensen 10817a3b050SJes Sorensen if (is_sn2) { 10917a3b050SJes Sorensen if (is_shub2()) { 11017a3b050SJes Sorensen int nid; 11117a3b050SJes Sorensen void *p; 11217a3b050SJes Sorensen int i; 11317a3b050SJes Sorensen 11417a3b050SJes Sorensen nid = nasid_to_cnodeid(get_node_number(__pa(addr))); 11517a3b050SJes Sorensen p = (void *)TO_AMO(scratch_page[nid]); 11617a3b050SJes Sorensen 11717a3b050SJes Sorensen for (i=0; i < SH2_AMO_CACHE_ENTRIES; i++) { 11817a3b050SJes Sorensen FETCHOP_LOAD_OP(p, FETCHOP_LOAD); 11917a3b050SJes Sorensen p += FETCHOP_VAR_SIZE; 12017a3b050SJes Sorensen } 12117a3b050SJes Sorensen } 12217a3b050SJes Sorensen 12317a3b050SJes Sorensen status = bte_copy(0, addr & ~__IA64_UNCACHED_OFFSET, len, 12417a3b050SJes Sorensen BTE_WACQUIRE | BTE_ZERO_FILL, NULL); 12517a3b050SJes Sorensen } else { 12617a3b050SJes Sorensen memset((char *) addr, 0, len); 12717a3b050SJes Sorensen status = 0; 12817a3b050SJes Sorensen } 12917a3b050SJes Sorensen return status; 13017a3b050SJes Sorensen } 13117a3b050SJes Sorensen 13217a3b050SJes Sorensen /* 13317a3b050SJes Sorensen * mspec_open 13417a3b050SJes Sorensen * 13517a3b050SJes Sorensen * Called when a device mapping is created by a means other than mmap 1364191ba26SCliff Wickman * (via fork, munmap, etc.). Increments the reference count on the 1374191ba26SCliff Wickman * underlying mspec data so it is not freed prematurely. 13817a3b050SJes Sorensen */ 13917a3b050SJes Sorensen static void 14017a3b050SJes Sorensen mspec_open(struct vm_area_struct *vma) 14117a3b050SJes Sorensen { 14217a3b050SJes Sorensen struct vma_data *vdata; 14317a3b050SJes Sorensen 14417a3b050SJes Sorensen vdata = vma->vm_private_data; 145f7d88d24SElena Reshetova refcount_inc(&vdata->refcnt); 14617a3b050SJes Sorensen } 14717a3b050SJes Sorensen 14817a3b050SJes Sorensen /* 14917a3b050SJes Sorensen * mspec_close 15017a3b050SJes Sorensen * 15117a3b050SJes Sorensen * Called when unmapping a device mapping. Frees all mspec pages 152afa684f6SCliff Wickman * belonging to all the vma's sharing this vma_data structure. 15317a3b050SJes Sorensen */ 15417a3b050SJes Sorensen static void 15517a3b050SJes Sorensen mspec_close(struct vm_area_struct *vma) 15617a3b050SJes Sorensen { 15717a3b050SJes Sorensen struct vma_data *vdata; 158afa684f6SCliff Wickman int index, last_index; 1594191ba26SCliff Wickman unsigned long my_page; 16017a3b050SJes Sorensen 16117a3b050SJes Sorensen vdata = vma->vm_private_data; 16217a3b050SJes Sorensen 163f7d88d24SElena Reshetova if (!refcount_dec_and_test(&vdata->refcnt)) 164afa684f6SCliff Wickman return; 1654191ba26SCliff Wickman 166afa684f6SCliff Wickman last_index = (vdata->vm_end - vdata->vm_start) >> PAGE_SHIFT; 167afa684f6SCliff Wickman for (index = 0; index < last_index; index++) { 1684191ba26SCliff Wickman if (vdata->maddr[index] == 0) 16917a3b050SJes Sorensen continue; 17017a3b050SJes Sorensen /* 17117a3b050SJes Sorensen * Clear the page before sticking it back 17217a3b050SJes Sorensen * into the pool. 17317a3b050SJes Sorensen */ 1744191ba26SCliff Wickman my_page = vdata->maddr[index]; 1754191ba26SCliff Wickman vdata->maddr[index] = 0; 176afa684f6SCliff Wickman if (!mspec_zero_block(my_page, PAGE_SIZE)) 177e4a064dfSDean Nelson uncached_free_page(my_page, 1); 17817a3b050SJes Sorensen else 17917a3b050SJes Sorensen printk(KERN_WARNING "mspec_close(): " 180afa684f6SCliff Wickman "failed to zero page %ld\n", my_page); 18117a3b050SJes Sorensen } 1824191ba26SCliff Wickman 1831d5cfdb0STetsuo Handa kvfree(vdata); 18417a3b050SJes Sorensen } 18517a3b050SJes Sorensen 18617a3b050SJes Sorensen /* 187efe9e779SNick Piggin * mspec_fault 18817a3b050SJes Sorensen * 18917a3b050SJes Sorensen * Creates a mspec page and maps it to user space. 19017a3b050SJes Sorensen */ 1913eb87d4eSSouptick Joarder static vm_fault_t 19211bac800SDave Jiang mspec_fault(struct vm_fault *vmf) 19317a3b050SJes Sorensen { 19417a3b050SJes Sorensen unsigned long paddr, maddr; 19517a3b050SJes Sorensen unsigned long pfn; 196efe9e779SNick Piggin pgoff_t index = vmf->pgoff; 19711bac800SDave Jiang struct vma_data *vdata = vmf->vma->vm_private_data; 19817a3b050SJes Sorensen 19917a3b050SJes Sorensen maddr = (volatile unsigned long) vdata->maddr[index]; 20017a3b050SJes Sorensen if (maddr == 0) { 201e4a064dfSDean Nelson maddr = uncached_alloc_page(numa_node_id(), 1); 20217a3b050SJes Sorensen if (maddr == 0) 203efe9e779SNick Piggin return VM_FAULT_OOM; 20417a3b050SJes Sorensen 20517a3b050SJes Sorensen spin_lock(&vdata->lock); 20617a3b050SJes Sorensen if (vdata->maddr[index] == 0) { 20717a3b050SJes Sorensen vdata->count++; 20817a3b050SJes Sorensen vdata->maddr[index] = maddr; 20917a3b050SJes Sorensen } else { 210e4a064dfSDean Nelson uncached_free_page(maddr, 1); 21117a3b050SJes Sorensen maddr = vdata->maddr[index]; 21217a3b050SJes Sorensen } 21317a3b050SJes Sorensen spin_unlock(&vdata->lock); 21417a3b050SJes Sorensen } 21517a3b050SJes Sorensen 21617a3b050SJes Sorensen if (vdata->type == MSPEC_FETCHOP) 21717a3b050SJes Sorensen paddr = TO_AMO(maddr); 21817a3b050SJes Sorensen else 2191a4b0fc5SJes Sorensen paddr = maddr & ~__IA64_UNCACHED_OFFSET; 22017a3b050SJes Sorensen 22117a3b050SJes Sorensen pfn = paddr >> PAGE_SHIFT; 22217a3b050SJes Sorensen 2233eb87d4eSSouptick Joarder return vmf_insert_pfn(vmf->vma, vmf->address, pfn); 22417a3b050SJes Sorensen } 22517a3b050SJes Sorensen 226f0f37e2fSAlexey Dobriyan static const struct vm_operations_struct mspec_vm_ops = { 22717a3b050SJes Sorensen .open = mspec_open, 22817a3b050SJes Sorensen .close = mspec_close, 229efe9e779SNick Piggin .fault = mspec_fault, 23017a3b050SJes Sorensen }; 23117a3b050SJes Sorensen 23217a3b050SJes Sorensen /* 23317a3b050SJes Sorensen * mspec_mmap 23417a3b050SJes Sorensen * 235af901ca1SAndré Goddard Rosa * Called when mmapping the device. Initializes the vma with a fault handler 23617a3b050SJes Sorensen * and private data structure necessary to allocate, track, and free the 23717a3b050SJes Sorensen * underlying pages. 23817a3b050SJes Sorensen */ 23917a3b050SJes Sorensen static int 2404191ba26SCliff Wickman mspec_mmap(struct file *file, struct vm_area_struct *vma, 2414191ba26SCliff Wickman enum mspec_page_type type) 24217a3b050SJes Sorensen { 24317a3b050SJes Sorensen struct vma_data *vdata; 2441d5cfdb0STetsuo Handa int pages, vdata_size; 24517a3b050SJes Sorensen 24617a3b050SJes Sorensen if (vma->vm_pgoff != 0) 24717a3b050SJes Sorensen return -EINVAL; 24817a3b050SJes Sorensen 24917a3b050SJes Sorensen if ((vma->vm_flags & VM_SHARED) == 0) 25017a3b050SJes Sorensen return -EINVAL; 25117a3b050SJes Sorensen 25217a3b050SJes Sorensen if ((vma->vm_flags & VM_WRITE) == 0) 25317a3b050SJes Sorensen return -EPERM; 25417a3b050SJes Sorensen 255a0ea59d5SLibin pages = vma_pages(vma); 25617a3b050SJes Sorensen vdata_size = sizeof(struct vma_data) + pages * sizeof(long); 25717a3b050SJes Sorensen if (vdata_size <= PAGE_SIZE) 258658c74cfSRakib Mullick vdata = kzalloc(vdata_size, GFP_KERNEL); 2591d5cfdb0STetsuo Handa else 260658c74cfSRakib Mullick vdata = vzalloc(vdata_size); 26117a3b050SJes Sorensen if (!vdata) 26217a3b050SJes Sorensen return -ENOMEM; 26317a3b050SJes Sorensen 2644191ba26SCliff Wickman vdata->vm_start = vma->vm_start; 2654191ba26SCliff Wickman vdata->vm_end = vma->vm_end; 26617a3b050SJes Sorensen vdata->type = type; 26717a3b050SJes Sorensen spin_lock_init(&vdata->lock); 268f7d88d24SElena Reshetova refcount_set(&vdata->refcnt, 1); 26917a3b050SJes Sorensen vma->vm_private_data = vdata; 27017a3b050SJes Sorensen 271314e51b9SKonstantin Khlebnikov vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP; 27217a3b050SJes Sorensen if (vdata->type == MSPEC_FETCHOP || vdata->type == MSPEC_UNCACHED) 27317a3b050SJes Sorensen vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 27417a3b050SJes Sorensen vma->vm_ops = &mspec_vm_ops; 27517a3b050SJes Sorensen 27617a3b050SJes Sorensen return 0; 27717a3b050SJes Sorensen } 27817a3b050SJes Sorensen 27917a3b050SJes Sorensen static int 28017a3b050SJes Sorensen fetchop_mmap(struct file *file, struct vm_area_struct *vma) 28117a3b050SJes Sorensen { 28217a3b050SJes Sorensen return mspec_mmap(file, vma, MSPEC_FETCHOP); 28317a3b050SJes Sorensen } 28417a3b050SJes Sorensen 28517a3b050SJes Sorensen static int 28617a3b050SJes Sorensen cached_mmap(struct file *file, struct vm_area_struct *vma) 28717a3b050SJes Sorensen { 28817a3b050SJes Sorensen return mspec_mmap(file, vma, MSPEC_CACHED); 28917a3b050SJes Sorensen } 29017a3b050SJes Sorensen 29117a3b050SJes Sorensen static int 29217a3b050SJes Sorensen uncached_mmap(struct file *file, struct vm_area_struct *vma) 29317a3b050SJes Sorensen { 29417a3b050SJes Sorensen return mspec_mmap(file, vma, MSPEC_UNCACHED); 29517a3b050SJes Sorensen } 29617a3b050SJes Sorensen 2972b8693c0SArjan van de Ven static const struct file_operations fetchop_fops = { 29817a3b050SJes Sorensen .owner = THIS_MODULE, 2996038f373SArnd Bergmann .mmap = fetchop_mmap, 3006038f373SArnd Bergmann .llseek = noop_llseek, 30117a3b050SJes Sorensen }; 30217a3b050SJes Sorensen 30317a3b050SJes Sorensen static struct miscdevice fetchop_miscdev = { 30417a3b050SJes Sorensen .minor = MISC_DYNAMIC_MINOR, 30517a3b050SJes Sorensen .name = "sgi_fetchop", 30617a3b050SJes Sorensen .fops = &fetchop_fops 30717a3b050SJes Sorensen }; 30817a3b050SJes Sorensen 3092b8693c0SArjan van de Ven static const struct file_operations cached_fops = { 31017a3b050SJes Sorensen .owner = THIS_MODULE, 3116038f373SArnd Bergmann .mmap = cached_mmap, 3126038f373SArnd Bergmann .llseek = noop_llseek, 31317a3b050SJes Sorensen }; 31417a3b050SJes Sorensen 31517a3b050SJes Sorensen static struct miscdevice cached_miscdev = { 31617a3b050SJes Sorensen .minor = MISC_DYNAMIC_MINOR, 31717a3b050SJes Sorensen .name = "mspec_cached", 31817a3b050SJes Sorensen .fops = &cached_fops 31917a3b050SJes Sorensen }; 32017a3b050SJes Sorensen 3212b8693c0SArjan van de Ven static const struct file_operations uncached_fops = { 32217a3b050SJes Sorensen .owner = THIS_MODULE, 3236038f373SArnd Bergmann .mmap = uncached_mmap, 3246038f373SArnd Bergmann .llseek = noop_llseek, 32517a3b050SJes Sorensen }; 32617a3b050SJes Sorensen 32717a3b050SJes Sorensen static struct miscdevice uncached_miscdev = { 32817a3b050SJes Sorensen .minor = MISC_DYNAMIC_MINOR, 32917a3b050SJes Sorensen .name = "mspec_uncached", 33017a3b050SJes Sorensen .fops = &uncached_fops 33117a3b050SJes Sorensen }; 33217a3b050SJes Sorensen 33317a3b050SJes Sorensen /* 33417a3b050SJes Sorensen * mspec_init 33517a3b050SJes Sorensen * 33617a3b050SJes Sorensen * Called at boot time to initialize the mspec facility. 33717a3b050SJes Sorensen */ 33817a3b050SJes Sorensen static int __init 33917a3b050SJes Sorensen mspec_init(void) 34017a3b050SJes Sorensen { 34117a3b050SJes Sorensen int ret; 34217a3b050SJes Sorensen int nid; 34317a3b050SJes Sorensen 34417a3b050SJes Sorensen /* 34517a3b050SJes Sorensen * The fetchop device only works on SN2 hardware, uncached and cached 34617a3b050SJes Sorensen * memory drivers should both be valid on all ia64 hardware 34717a3b050SJes Sorensen */ 3481a4b0fc5SJes Sorensen #ifdef CONFIG_SGI_SN 34917a3b050SJes Sorensen if (ia64_platform_is("sn2")) { 35017a3b050SJes Sorensen is_sn2 = 1; 35117a3b050SJes Sorensen if (is_shub2()) { 35217a3b050SJes Sorensen ret = -ENOMEM; 3532dca53a9SChristoph Lameter for_each_node_state(nid, N_ONLINE) { 35417a3b050SJes Sorensen int actual_nid; 35517a3b050SJes Sorensen int nasid; 35617a3b050SJes Sorensen unsigned long phys; 35717a3b050SJes Sorensen 358e4a064dfSDean Nelson scratch_page[nid] = uncached_alloc_page(nid, 1); 35917a3b050SJes Sorensen if (scratch_page[nid] == 0) 36017a3b050SJes Sorensen goto free_scratch_pages; 36117a3b050SJes Sorensen phys = __pa(scratch_page[nid]); 36217a3b050SJes Sorensen nasid = get_node_number(phys); 36317a3b050SJes Sorensen actual_nid = nasid_to_cnodeid(nasid); 36417a3b050SJes Sorensen if (actual_nid != nid) 36517a3b050SJes Sorensen goto free_scratch_pages; 36617a3b050SJes Sorensen } 36717a3b050SJes Sorensen } 36817a3b050SJes Sorensen 36917a3b050SJes Sorensen ret = misc_register(&fetchop_miscdev); 37017a3b050SJes Sorensen if (ret) { 37117a3b050SJes Sorensen printk(KERN_ERR 37217a3b050SJes Sorensen "%s: failed to register device %i\n", 37317a3b050SJes Sorensen FETCHOP_ID, ret); 37417a3b050SJes Sorensen goto free_scratch_pages; 37517a3b050SJes Sorensen } 37617a3b050SJes Sorensen } 3771a4b0fc5SJes Sorensen #endif 37817a3b050SJes Sorensen ret = misc_register(&cached_miscdev); 37917a3b050SJes Sorensen if (ret) { 38017a3b050SJes Sorensen printk(KERN_ERR "%s: failed to register device %i\n", 38117a3b050SJes Sorensen CACHED_ID, ret); 38217a3b050SJes Sorensen if (is_sn2) 38317a3b050SJes Sorensen misc_deregister(&fetchop_miscdev); 38417a3b050SJes Sorensen goto free_scratch_pages; 38517a3b050SJes Sorensen } 38617a3b050SJes Sorensen ret = misc_register(&uncached_miscdev); 38717a3b050SJes Sorensen if (ret) { 38817a3b050SJes Sorensen printk(KERN_ERR "%s: failed to register device %i\n", 38917a3b050SJes Sorensen UNCACHED_ID, ret); 39017a3b050SJes Sorensen misc_deregister(&cached_miscdev); 39117a3b050SJes Sorensen if (is_sn2) 39217a3b050SJes Sorensen misc_deregister(&fetchop_miscdev); 39317a3b050SJes Sorensen goto free_scratch_pages; 39417a3b050SJes Sorensen } 39517a3b050SJes Sorensen 39617a3b050SJes Sorensen printk(KERN_INFO "%s %s initialized devices: %s %s %s\n", 39717a3b050SJes Sorensen MSPEC_BASENAME, REVISION, is_sn2 ? FETCHOP_ID : "", 39817a3b050SJes Sorensen CACHED_ID, UNCACHED_ID); 39917a3b050SJes Sorensen 40017a3b050SJes Sorensen return 0; 40117a3b050SJes Sorensen 40217a3b050SJes Sorensen free_scratch_pages: 40317a3b050SJes Sorensen for_each_node(nid) { 40417a3b050SJes Sorensen if (scratch_page[nid] != 0) 405e4a064dfSDean Nelson uncached_free_page(scratch_page[nid], 1); 40617a3b050SJes Sorensen } 40717a3b050SJes Sorensen return ret; 40817a3b050SJes Sorensen } 40917a3b050SJes Sorensen 41017a3b050SJes Sorensen static void __exit 41117a3b050SJes Sorensen mspec_exit(void) 41217a3b050SJes Sorensen { 41317a3b050SJes Sorensen int nid; 41417a3b050SJes Sorensen 41517a3b050SJes Sorensen misc_deregister(&uncached_miscdev); 41617a3b050SJes Sorensen misc_deregister(&cached_miscdev); 41717a3b050SJes Sorensen if (is_sn2) { 41817a3b050SJes Sorensen misc_deregister(&fetchop_miscdev); 41917a3b050SJes Sorensen 42017a3b050SJes Sorensen for_each_node(nid) { 42117a3b050SJes Sorensen if (scratch_page[nid] != 0) 422e4a064dfSDean Nelson uncached_free_page(scratch_page[nid], 1); 42317a3b050SJes Sorensen } 42417a3b050SJes Sorensen } 42517a3b050SJes Sorensen } 42617a3b050SJes Sorensen 42717a3b050SJes Sorensen module_init(mspec_init); 42817a3b050SJes Sorensen module_exit(mspec_exit); 42917a3b050SJes Sorensen 43017a3b050SJes Sorensen MODULE_AUTHOR("Silicon Graphics, Inc. <linux-altix@sgi.com>"); 43117a3b050SJes Sorensen MODULE_DESCRIPTION("Driver for SGI SN special memory operations"); 43217a3b050SJes Sorensen MODULE_LICENSE("GPL"); 433