Path: blob/master/arch/powerpc/mm/mmu_context_hash64.c
10818 views
/*1* MMU context allocation for 64-bit kernels.2*3* Copyright (C) 2004 Anton Blanchard, IBM Corp. <[email protected]>4*5* This program is free software; you can redistribute it and/or6* modify it under the terms of the GNU General Public License7* as published by the Free Software Foundation; either version8* 2 of the License, or (at your option) any later version.9*10*/1112#include <linux/sched.h>13#include <linux/kernel.h>14#include <linux/errno.h>15#include <linux/string.h>16#include <linux/types.h>17#include <linux/mm.h>18#include <linux/spinlock.h>19#include <linux/idr.h>20#include <linux/module.h>21#include <linux/gfp.h>22#include <linux/slab.h>2324#include <asm/mmu_context.h>2526#ifdef CONFIG_PPC_ICSWX27/*28* The processor and its L2 cache cause the icswx instruction to29* generate a COP_REQ transaction on PowerBus. The transaction has30* no address, and the processor does not perform an MMU access31* to authenticate the transaction. The command portion of the32* PowerBus COP_REQ transaction includes the LPAR_ID (LPID) and33* the coprocessor Process ID (PID), which the coprocessor compares34* to the authorized LPID and PID held in the coprocessor, to determine35* if the process is authorized to generate the transaction.36* The data of the COP_REQ transaction is 128-byte or less and is37* placed in cacheable memory on a 128-byte cache line boundary.38*39* The task to use a coprocessor should use use_cop() to allocate40* a coprocessor PID before executing icswx instruction. use_cop()41* also enables the coprocessor context switching. Drop_cop() is42* used to free the coprocessor PID.43*44* Example:45* Host Fabric Interface (HFI) is a PowerPC network coprocessor.46* Each HFI have multiple windows. Each HFI window serves as a47* network device sending to and receiving from HFI network.48* HFI immediate send function uses icswx instruction. The immediate49* send function allows small (single cache-line) packets be sent50* without using the regular HFI send FIFO and doorbell, which are51* much slower than immediate send.52*53* For each task intending to use HFI immediate send, the HFI driver54* calls use_cop() to obtain a coprocessor PID for the task.55* The HFI driver then allocate a free HFI window and save the56* coprocessor PID to the HFI window to allow the task to use the57* HFI window.58*59* The HFI driver repeatedly creates immediate send packets and60* issues icswx instruction to send data through the HFI window.61* The HFI compares the coprocessor PID in the CPU PID register62* to the PID held in the HFI window to determine if the transaction63* is allowed.64*65* When the task to release the HFI window, the HFI driver calls66* drop_cop() to release the coprocessor PID.67*/6869#define COP_PID_NONE 070#define COP_PID_MIN (COP_PID_NONE + 1)71#define COP_PID_MAX (0xFFFF)7273static DEFINE_SPINLOCK(mmu_context_acop_lock);74static DEFINE_IDA(cop_ida);7576void switch_cop(struct mm_struct *next)77{78mtspr(SPRN_PID, next->context.cop_pid);79mtspr(SPRN_ACOP, next->context.acop);80}8182static int new_cop_pid(struct ida *ida, int min_id, int max_id,83spinlock_t *lock)84{85int index;86int err;8788again:89if (!ida_pre_get(ida, GFP_KERNEL))90return -ENOMEM;9192spin_lock(lock);93err = ida_get_new_above(ida, min_id, &index);94spin_unlock(lock);9596if (err == -EAGAIN)97goto again;98else if (err)99return err;100101if (index > max_id) {102spin_lock(lock);103ida_remove(ida, index);104spin_unlock(lock);105return -ENOMEM;106}107108return index;109}110111static void sync_cop(void *arg)112{113struct mm_struct *mm = arg;114115if (mm == current->active_mm)116switch_cop(current->active_mm);117}118119/**120* Start using a coprocessor.121* @acop: mask of coprocessor to be used.122* @mm: The mm the coprocessor to associate with. Most likely current mm.123*124* Return a positive PID if successful. Negative errno otherwise.125* The returned PID will be fed to the coprocessor to determine if an126* icswx transaction is authenticated.127*/128int use_cop(unsigned long acop, struct mm_struct *mm)129{130int ret;131132if (!cpu_has_feature(CPU_FTR_ICSWX))133return -ENODEV;134135if (!mm || !acop)136return -EINVAL;137138/* We need to make sure mm_users doesn't change */139down_read(&mm->mmap_sem);140spin_lock(mm->context.cop_lockp);141142if (mm->context.cop_pid == COP_PID_NONE) {143ret = new_cop_pid(&cop_ida, COP_PID_MIN, COP_PID_MAX,144&mmu_context_acop_lock);145if (ret < 0)146goto out;147148mm->context.cop_pid = ret;149}150mm->context.acop |= acop;151152sync_cop(mm);153154/*155* If this is a threaded process then there might be other threads156* running. We need to send an IPI to force them to pick up any157* change in PID and ACOP.158*/159if (atomic_read(&mm->mm_users) > 1)160smp_call_function(sync_cop, mm, 1);161162ret = mm->context.cop_pid;163164out:165spin_unlock(mm->context.cop_lockp);166up_read(&mm->mmap_sem);167168return ret;169}170EXPORT_SYMBOL_GPL(use_cop);171172/**173* Stop using a coprocessor.174* @acop: mask of coprocessor to be stopped.175* @mm: The mm the coprocessor associated with.176*/177void drop_cop(unsigned long acop, struct mm_struct *mm)178{179int free_pid = COP_PID_NONE;180181if (!cpu_has_feature(CPU_FTR_ICSWX))182return;183184if (WARN_ON_ONCE(!mm))185return;186187/* We need to make sure mm_users doesn't change */188down_read(&mm->mmap_sem);189spin_lock(mm->context.cop_lockp);190191mm->context.acop &= ~acop;192193if ((!mm->context.acop) && (mm->context.cop_pid != COP_PID_NONE)) {194free_pid = mm->context.cop_pid;195mm->context.cop_pid = COP_PID_NONE;196}197198sync_cop(mm);199200/*201* If this is a threaded process then there might be other threads202* running. We need to send an IPI to force them to pick up any203* change in PID and ACOP.204*/205if (atomic_read(&mm->mm_users) > 1)206smp_call_function(sync_cop, mm, 1);207208if (free_pid != COP_PID_NONE) {209spin_lock(&mmu_context_acop_lock);210ida_remove(&cop_ida, free_pid);211spin_unlock(&mmu_context_acop_lock);212}213214spin_unlock(mm->context.cop_lockp);215up_read(&mm->mmap_sem);216}217EXPORT_SYMBOL_GPL(drop_cop);218219#endif /* CONFIG_PPC_ICSWX */220221static DEFINE_SPINLOCK(mmu_context_lock);222static DEFINE_IDA(mmu_context_ida);223224/*225* The proto-VSID space has 2^35 - 1 segments available for user mappings.226* Each segment contains 2^28 bytes. Each context maps 2^44 bytes,227* so we can support 2^19-1 contexts (19 == 35 + 28 - 44).228*/229#define MAX_CONTEXT ((1UL << 19) - 1)230231int __init_new_context(void)232{233int index;234int err;235236again:237if (!ida_pre_get(&mmu_context_ida, GFP_KERNEL))238return -ENOMEM;239240spin_lock(&mmu_context_lock);241err = ida_get_new_above(&mmu_context_ida, 1, &index);242spin_unlock(&mmu_context_lock);243244if (err == -EAGAIN)245goto again;246else if (err)247return err;248249if (index > MAX_CONTEXT) {250spin_lock(&mmu_context_lock);251ida_remove(&mmu_context_ida, index);252spin_unlock(&mmu_context_lock);253return -ENOMEM;254}255256return index;257}258EXPORT_SYMBOL_GPL(__init_new_context);259260int init_new_context(struct task_struct *tsk, struct mm_struct *mm)261{262int index;263264index = __init_new_context();265if (index < 0)266return index;267268/* The old code would re-promote on fork, we don't do that269* when using slices as it could cause problem promoting slices270* that have been forced down to 4K271*/272if (slice_mm_new_context(mm))273slice_set_user_psize(mm, mmu_virtual_psize);274subpage_prot_init_new_context(mm);275mm->context.id = index;276#ifdef CONFIG_PPC_ICSWX277mm->context.cop_lockp = kmalloc(sizeof(spinlock_t), GFP_KERNEL);278if (!mm->context.cop_lockp) {279__destroy_context(index);280subpage_prot_free(mm);281mm->context.id = MMU_NO_CONTEXT;282return -ENOMEM;283}284spin_lock_init(mm->context.cop_lockp);285#endif /* CONFIG_PPC_ICSWX */286287return 0;288}289290void __destroy_context(int context_id)291{292spin_lock(&mmu_context_lock);293ida_remove(&mmu_context_ida, context_id);294spin_unlock(&mmu_context_lock);295}296EXPORT_SYMBOL_GPL(__destroy_context);297298void destroy_context(struct mm_struct *mm)299{300#ifdef CONFIG_PPC_ICSWX301drop_cop(mm->context.acop, mm);302kfree(mm->context.cop_lockp);303mm->context.cop_lockp = NULL;304#endif /* CONFIG_PPC_ICSWX */305__destroy_context(mm->context.id);306subpage_prot_free(mm);307mm->context.id = MMU_NO_CONTEXT;308}309310311