Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.1 309 lines 8.1 kB view raw
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#include <linux/slab.h> 24 25#include <asm/mmu_context.h> 26 27#ifdef CONFIG_PPC_ICSWX 28/* 29 * The processor and its L2 cache cause the icswx instruction to 30 * generate a COP_REQ transaction on PowerBus. The transaction has 31 * no address, and the processor does not perform an MMU access 32 * to authenticate the transaction. The command portion of the 33 * PowerBus COP_REQ transaction includes the LPAR_ID (LPID) and 34 * the coprocessor Process ID (PID), which the coprocessor compares 35 * to the authorized LPID and PID held in the coprocessor, to determine 36 * if the process is authorized to generate the transaction. 37 * The data of the COP_REQ transaction is 128-byte or less and is 38 * placed in cacheable memory on a 128-byte cache line boundary. 39 * 40 * The task to use a coprocessor should use use_cop() to allocate 41 * a coprocessor PID before executing icswx instruction. use_cop() 42 * also enables the coprocessor context switching. Drop_cop() is 43 * used to free the coprocessor PID. 44 * 45 * Example: 46 * Host Fabric Interface (HFI) is a PowerPC network coprocessor. 47 * Each HFI have multiple windows. Each HFI window serves as a 48 * network device sending to and receiving from HFI network. 49 * HFI immediate send function uses icswx instruction. The immediate 50 * send function allows small (single cache-line) packets be sent 51 * without using the regular HFI send FIFO and doorbell, which are 52 * much slower than immediate send. 53 * 54 * For each task intending to use HFI immediate send, the HFI driver 55 * calls use_cop() to obtain a coprocessor PID for the task. 56 * The HFI driver then allocate a free HFI window and save the 57 * coprocessor PID to the HFI window to allow the task to use the 58 * HFI window. 59 * 60 * The HFI driver repeatedly creates immediate send packets and 61 * issues icswx instruction to send data through the HFI window. 62 * The HFI compares the coprocessor PID in the CPU PID register 63 * to the PID held in the HFI window to determine if the transaction 64 * is allowed. 65 * 66 * When the task to release the HFI window, the HFI driver calls 67 * drop_cop() to release the coprocessor PID. 68 */ 69 70#define COP_PID_NONE 0 71#define COP_PID_MIN (COP_PID_NONE + 1) 72#define COP_PID_MAX (0xFFFF) 73 74static DEFINE_SPINLOCK(mmu_context_acop_lock); 75static DEFINE_IDA(cop_ida); 76 77void switch_cop(struct mm_struct *next) 78{ 79 mtspr(SPRN_PID, next->context.cop_pid); 80 mtspr(SPRN_ACOP, next->context.acop); 81} 82 83static int new_cop_pid(struct ida *ida, int min_id, int max_id, 84 spinlock_t *lock) 85{ 86 int index; 87 int err; 88 89again: 90 if (!ida_pre_get(ida, GFP_KERNEL)) 91 return -ENOMEM; 92 93 spin_lock(lock); 94 err = ida_get_new_above(ida, min_id, &index); 95 spin_unlock(lock); 96 97 if (err == -EAGAIN) 98 goto again; 99 else if (err) 100 return err; 101 102 if (index > max_id) { 103 spin_lock(lock); 104 ida_remove(ida, index); 105 spin_unlock(lock); 106 return -ENOMEM; 107 } 108 109 return index; 110} 111 112static void sync_cop(void *arg) 113{ 114 struct mm_struct *mm = arg; 115 116 if (mm == current->active_mm) 117 switch_cop(current->active_mm); 118} 119 120/** 121 * Start using a coprocessor. 122 * @acop: mask of coprocessor to be used. 123 * @mm: The mm the coprocessor to associate with. Most likely current mm. 124 * 125 * Return a positive PID if successful. Negative errno otherwise. 126 * The returned PID will be fed to the coprocessor to determine if an 127 * icswx transaction is authenticated. 128 */ 129int use_cop(unsigned long acop, struct mm_struct *mm) 130{ 131 int ret; 132 133 if (!cpu_has_feature(CPU_FTR_ICSWX)) 134 return -ENODEV; 135 136 if (!mm || !acop) 137 return -EINVAL; 138 139 /* We need to make sure mm_users doesn't change */ 140 down_read(&mm->mmap_sem); 141 spin_lock(mm->context.cop_lockp); 142 143 if (mm->context.cop_pid == COP_PID_NONE) { 144 ret = new_cop_pid(&cop_ida, COP_PID_MIN, COP_PID_MAX, 145 &mmu_context_acop_lock); 146 if (ret < 0) 147 goto out; 148 149 mm->context.cop_pid = ret; 150 } 151 mm->context.acop |= acop; 152 153 sync_cop(mm); 154 155 /* 156 * If this is a threaded process then there might be other threads 157 * running. We need to send an IPI to force them to pick up any 158 * change in PID and ACOP. 159 */ 160 if (atomic_read(&mm->mm_users) > 1) 161 smp_call_function(sync_cop, mm, 1); 162 163 ret = mm->context.cop_pid; 164 165out: 166 spin_unlock(mm->context.cop_lockp); 167 up_read(&mm->mmap_sem); 168 169 return ret; 170} 171EXPORT_SYMBOL_GPL(use_cop); 172 173/** 174 * Stop using a coprocessor. 175 * @acop: mask of coprocessor to be stopped. 176 * @mm: The mm the coprocessor associated with. 177 */ 178void drop_cop(unsigned long acop, struct mm_struct *mm) 179{ 180 int free_pid = COP_PID_NONE; 181 182 if (!cpu_has_feature(CPU_FTR_ICSWX)) 183 return; 184 185 if (WARN_ON_ONCE(!mm)) 186 return; 187 188 /* We need to make sure mm_users doesn't change */ 189 down_read(&mm->mmap_sem); 190 spin_lock(mm->context.cop_lockp); 191 192 mm->context.acop &= ~acop; 193 194 if ((!mm->context.acop) && (mm->context.cop_pid != COP_PID_NONE)) { 195 free_pid = mm->context.cop_pid; 196 mm->context.cop_pid = COP_PID_NONE; 197 } 198 199 sync_cop(mm); 200 201 /* 202 * If this is a threaded process then there might be other threads 203 * running. We need to send an IPI to force them to pick up any 204 * change in PID and ACOP. 205 */ 206 if (atomic_read(&mm->mm_users) > 1) 207 smp_call_function(sync_cop, mm, 1); 208 209 if (free_pid != COP_PID_NONE) { 210 spin_lock(&mmu_context_acop_lock); 211 ida_remove(&cop_ida, free_pid); 212 spin_unlock(&mmu_context_acop_lock); 213 } 214 215 spin_unlock(mm->context.cop_lockp); 216 up_read(&mm->mmap_sem); 217} 218EXPORT_SYMBOL_GPL(drop_cop); 219 220#endif /* CONFIG_PPC_ICSWX */ 221 222static DEFINE_SPINLOCK(mmu_context_lock); 223static DEFINE_IDA(mmu_context_ida); 224 225/* 226 * The proto-VSID space has 2^35 - 1 segments available for user mappings. 227 * Each segment contains 2^28 bytes. Each context maps 2^44 bytes, 228 * so we can support 2^19-1 contexts (19 == 35 + 28 - 44). 229 */ 230#define MAX_CONTEXT ((1UL << 19) - 1) 231 232int __init_new_context(void) 233{ 234 int index; 235 int err; 236 237again: 238 if (!ida_pre_get(&mmu_context_ida, GFP_KERNEL)) 239 return -ENOMEM; 240 241 spin_lock(&mmu_context_lock); 242 err = ida_get_new_above(&mmu_context_ida, 1, &index); 243 spin_unlock(&mmu_context_lock); 244 245 if (err == -EAGAIN) 246 goto again; 247 else if (err) 248 return err; 249 250 if (index > MAX_CONTEXT) { 251 spin_lock(&mmu_context_lock); 252 ida_remove(&mmu_context_ida, index); 253 spin_unlock(&mmu_context_lock); 254 return -ENOMEM; 255 } 256 257 return index; 258} 259EXPORT_SYMBOL_GPL(__init_new_context); 260 261int init_new_context(struct task_struct *tsk, struct mm_struct *mm) 262{ 263 int index; 264 265 index = __init_new_context(); 266 if (index < 0) 267 return index; 268 269 /* The old code would re-promote on fork, we don't do that 270 * when using slices as it could cause problem promoting slices 271 * that have been forced down to 4K 272 */ 273 if (slice_mm_new_context(mm)) 274 slice_set_user_psize(mm, mmu_virtual_psize); 275 subpage_prot_init_new_context(mm); 276 mm->context.id = index; 277#ifdef CONFIG_PPC_ICSWX 278 mm->context.cop_lockp = kmalloc(sizeof(spinlock_t), GFP_KERNEL); 279 if (!mm->context.cop_lockp) { 280 __destroy_context(index); 281 subpage_prot_free(mm); 282 mm->context.id = MMU_NO_CONTEXT; 283 return -ENOMEM; 284 } 285 spin_lock_init(mm->context.cop_lockp); 286#endif /* CONFIG_PPC_ICSWX */ 287 288 return 0; 289} 290 291void __destroy_context(int context_id) 292{ 293 spin_lock(&mmu_context_lock); 294 ida_remove(&mmu_context_ida, context_id); 295 spin_unlock(&mmu_context_lock); 296} 297EXPORT_SYMBOL_GPL(__destroy_context); 298 299void destroy_context(struct mm_struct *mm) 300{ 301#ifdef CONFIG_PPC_ICSWX 302 drop_cop(mm->context.acop, mm); 303 kfree(mm->context.cop_lockp); 304 mm->context.cop_lockp = NULL; 305#endif /* CONFIG_PPC_ICSWX */ 306 __destroy_context(mm->context.id); 307 subpage_prot_free(mm); 308 mm->context.id = MMU_NO_CONTEXT; 309}