1/* 2 * MMU context allocation for 64-bit kernels. 3 * 4 * Copyright (C) 2004 Anton Blanchard, IBM Corp. <anton@samba.org> 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License 8 * as published by the Free Software Foundation; either version 9 * 2 of the License, or (at your option) any later version. 10 * 11 */ 12 13#include <linux/sched.h> 14#include <linux/kernel.h> 15#include <linux/errno.h> 16#include <linux/string.h> 17#include <linux/types.h> 18#include <linux/mm.h> 19#include <linux/spinlock.h> 20#include <linux/idr.h> 21#include <linux/module.h> 22#include <linux/gfp.h> 23 24#include <asm/mmu_context.h> 25 26static DEFINE_SPINLOCK(mmu_context_lock); 27static DEFINE_IDA(mmu_context_ida); 28 29/* 30 * The proto-VSID space has 2^35 - 1 segments available for user mappings. 31 * Each segment contains 2^28 bytes. Each context maps 2^44 bytes, 32 * so we can support 2^19-1 contexts (19 == 35 + 28 - 44). 33 */ 34#define NO_CONTEXT 0 35#define MAX_CONTEXT ((1UL << 19) - 1) 36 37int __init_new_context(void) 38{ 39 int index; 40 int err; 41 42again: 43 if (!ida_pre_get(&mmu_context_ida, GFP_KERNEL)) 44 return -ENOMEM; 45 46 spin_lock(&mmu_context_lock); 47 err = ida_get_new_above(&mmu_context_ida, 1, &index); 48 spin_unlock(&mmu_context_lock); 49 50 if (err == -EAGAIN) 51 goto again; 52 else if (err) 53 return err; 54 55 if (index > MAX_CONTEXT) { 56 spin_lock(&mmu_context_lock); 57 ida_remove(&mmu_context_ida, index); 58 spin_unlock(&mmu_context_lock); 59 return -ENOMEM; 60 } 61 62 return index; 63} 64EXPORT_SYMBOL_GPL(__init_new_context); 65 66int init_new_context(struct task_struct *tsk, struct mm_struct *mm) 67{ 68 int index; 69 70 index = __init_new_context(); 71 if (index < 0) 72 return index; 73 74 /* The old code would re-promote on fork, we don't do that 75 * when using slices as it could cause problem promoting slices 76 * that have been forced down to 4K 77 */ 78 if (slice_mm_new_context(mm)) 79 slice_set_user_psize(mm, mmu_virtual_psize); 80 subpage_prot_init_new_context(mm); 81 mm->context.id = index; 82 83 return 0; 84} 85 86void __destroy_context(int context_id) 87{ 88 spin_lock(&mmu_context_lock); 89 ida_remove(&mmu_context_ida, context_id); 90 spin_unlock(&mmu_context_lock); 91} 92EXPORT_SYMBOL_GPL(__destroy_context); 93 94void destroy_context(struct mm_struct *mm) 95{ 96 __destroy_context(mm->context.id); 97 subpage_prot_free(mm); 98 mm->context.id = NO_CONTEXT; 99} 100