Hi Jimi, > Some processors, like embedded, that already have a PID register that > is managed by the system. This patch separates the ACOP and PID > processing into separate files so that the ACOP code can be shared. > > Signed-off-by: Jimi Xenidis <ji...@pobox.com>
Looks good. Acked-by: Anton Blanchard <an...@samba.org> Anton > --- > Re: ga...@kernel.crashing.org > Fix typo in arch/powerpc/mm/Makefile > > Re: an...@samba.org > merge in: powerpc: Fix deadlock in icswx code > --- > arch/powerpc/mm/Makefile | 2 + > arch/powerpc/mm/icswx.c | 162 > ++++++++++++++++++++++++++ arch/powerpc/mm/icswx.h | > 34 ++++++ arch/powerpc/mm/icswx_pid.c | 87 ++++++++++++++ > arch/powerpc/mm/mmu_context_hash64.c | 195 > -------------------------------- > arch/powerpc/platforms/Kconfig.cputype | 10 ++- 6 files changed, > 294 insertions(+), 196 deletions(-) create mode 100644 > arch/powerpc/mm/icswx.c create mode 100644 arch/powerpc/mm/icswx.h > create mode 100644 arch/powerpc/mm/icswx_pid.c > > diff --git a/arch/powerpc/mm/Makefile b/arch/powerpc/mm/Makefile > index bdca46e..fb7976f 100644 > --- a/arch/powerpc/mm/Makefile > +++ b/arch/powerpc/mm/Makefile > @@ -21,6 +21,8 @@ obj-$(CONFIG_PPC_STD_MMU_32) += ppc_mmu_32.o > obj-$(CONFIG_PPC_STD_MMU) += hash_low_$(CONFIG_WORD_SIZE).o \ > tlb_hash$(CONFIG_WORD_SIZE).o \ > mmu_context_hash$(CONFIG_WORD_SIZE).o > +obj-$(CONFIG_PPC_ICSWX) += icswx.o > +obj-$(CONFIG_PPC_ICSWX_PID) += icswx_pid.o > obj-$(CONFIG_40x) += 40x_mmu.o > obj-$(CONFIG_44x) += 44x_mmu.o > obj-$(CONFIG_PPC_FSL_BOOK3E) += fsl_booke_mmu.o > diff --git a/arch/powerpc/mm/icswx.c b/arch/powerpc/mm/icswx.c > new file mode 100644 > index 0000000..2f1dd29 > --- /dev/null > +++ b/arch/powerpc/mm/icswx.c > @@ -0,0 +1,162 @@ > +/* > + * ICSWX and ACOP Management > + * > + * Copyright (C) 2011 Anton Blanchard, IBM Corp. <an...@samba.org> > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public License > + * as published by the Free Software Foundation; either version > + * 2 of the License, or (at your option) any later version. > + * > + */ > + > +#include <linux/sched.h> > +#include <linux/kernel.h> > +#include <linux/errno.h> > +#include <linux/types.h> > +#include <linux/mm.h> > +#include <linux/spinlock.h> > +#include <linux/module.h> > +#include "icswx.h" > + > + > +/* > + * The processor and its L2 cache cause the icswx instruction to > + * generate a COP_REQ transaction on PowerBus. The transaction has no > + * address, and the processor does not perform an MMU access to > + * authenticate the transaction. The command portion of the PowerBus > + * COP_REQ transaction includes the LPAR_ID (LPID) and the > coprocessor > + * Process ID (PID), which the coprocessor compares to the authorized > + * LPID and PID held in the coprocessor, to determine if the process > + * is authorized to generate the transaction. The data of the > COP_REQ > + * transaction is cache block or less, typically 64 or 128 bytes in > + * size, and is placed in cacheable memory on a 128-byte boundary > + * _always_. > + * > + * The task to use a coprocessor should use use_cop() mark the use of > + * the coprocessor type (CT) and context swithing. On a server > + * processor the PID register is used only for coprocessor management > + * and so a coprocessor PID is allocated before executing icswx > + * instruction. Drop_cop() is used to free the resources created by > + * use_cop(). > + * > + * Example: > + * Host Fabric Interface (HFI) is a PowerPC network coprocessor. > + * Each HFI have multiple windows. Each HFI window serves as a > + * network device sending to and receiving from HFI network. > + * HFI immediate send function uses icswx instruction. The immediate > + * send function allows small (single cache-line) packets be sent > + * without using the regular HFI send FIFO and doorbell, which are > + * much slower than immediate send. > + * > + * For each task intending to use HFI immediate send, the HFI driver > + * calls use_cop() to obtain a coprocessor PID for the task. > + * The HFI driver then allocate a free HFI window and save the > + * coprocessor PID to the HFI window to allow the task to use the > + * HFI window. > + * > + * The HFI driver repeatedly creates immediate send packets and > + * issues icswx instruction to send data through the HFI window. > + * The HFI compares the coprocessor PID in the CPU PID register > + * to the PID held in the HFI window to determine if the transaction > + * is allowed. > + * > + * When the task to release the HFI window, the HFI driver calls > + * drop_cop() to release the coprocessor PID. > + */ > + > +void switch_cop(struct mm_struct *next) > +{ > +#ifdef CONFIG_ICSWX_PID > + mtspr(SPRN_PID, next->context.cop_pid); > +#endif > + mtspr(SPRN_ACOP, next->context.acop); > +} > + > +/** > + * Start using a coprocessor. > + * @acop: mask of coprocessor to be used. > + * @mm: The mm the coprocessor to associate with. Most likely > current mm. > + * > + * Return a positive PID if successful. Negative errno otherwise. > + * The returned PID will be fed to the coprocessor to determine if an > + * icswx transaction is authenticated. > + */ > +int use_cop(unsigned long acop, struct mm_struct *mm) > +{ > + int ret; > + > + if (!cpu_has_feature(CPU_FTR_ICSWX)) > + return -ENODEV; > + > + if (!mm || !acop) > + return -EINVAL; > + > + /* The page_table_lock ensures mm_users won't change under > us */ > + spin_lock(&mm->page_table_lock); > + spin_lock(mm->context.cop_lockp); > + > + ret = get_cop_pid(mm); > + if (ret < 0) > + goto out; > + > + /* update acop */ > + mm->context.acop |= acop; > + > + sync_cop(mm); > + > + /* > + * If this is a threaded process then there might be other > threads > + * running. We need to send an IPI to force them to pick up > any > + * change in PID and ACOP. > + */ > + if (atomic_read(&mm->mm_users) > 1) > + smp_call_function(sync_cop, mm, 1); > + > +out: > + spin_unlock(mm->context.cop_lockp); > + spin_unlock(&mm->page_table_lock); > + > + return ret; > +} > +EXPORT_SYMBOL_GPL(use_cop); > + > +/** > + * Stop using a coprocessor. > + * @acop: mask of coprocessor to be stopped. > + * @mm: The mm the coprocessor associated with. > + */ > +void drop_cop(unsigned long acop, struct mm_struct *mm) > +{ > + int free_pid; > + > + if (!cpu_has_feature(CPU_FTR_ICSWX)) > + return; > + > + if (WARN_ON_ONCE(!mm)) > + return; > + > + /* The page_table_lock ensures mm_users won't change under > us */ > + spin_lock(&mm->page_table_lock); > + spin_lock(mm->context.cop_lockp); > + > + mm->context.acop &= ~acop; > + > + free_pid = disable_cop_pid(mm); > + sync_cop(mm); > + > + /* > + * If this is a threaded process then there might be other > threads > + * running. We need to send an IPI to force them to pick up > any > + * change in PID and ACOP. > + */ > + if (atomic_read(&mm->mm_users) > 1) > + smp_call_function(sync_cop, mm, 1); > + > + if (free_pid != COP_PID_NONE) > + free_cop_pid(free_pid); > + > + spin_unlock(mm->context.cop_lockp); > + spin_unlock(&mm->page_table_lock); > +} > +EXPORT_SYMBOL_GPL(drop_cop); > diff --git a/arch/powerpc/mm/icswx.h b/arch/powerpc/mm/icswx.h > new file mode 100644 > index 0000000..5121ddd > --- /dev/null > +++ b/arch/powerpc/mm/icswx.h > @@ -0,0 +1,34 @@ > +/* > + * ICSWX and ACOP Management > + * > + * Copyright (C) 2011 Anton Blanchard, IBM Corp. <an...@samba.org> > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public License > + * as published by the Free Software Foundation; either version > + * 2 of the License, or (at your option) any later version. > + * > + */ > + > +#include <asm/mmu_context.h> > + > +/* also used to denote that PIDs are not used */ > +#define COP_PID_NONE 0 > + > +static inline void sync_cop(void *arg) > +{ > + struct mm_struct *mm = arg; > + > + if (mm == current->active_mm) > + switch_cop(current->active_mm); > +} > + > +#ifdef CONFIG_PPC_ICSWX_PID > +extern int get_cop_pid(struct mm_struct *mm); > +extern int disable_cop_pid(struct mm_struct *mm); > +extern void free_cop_pid(int free_pid); > +#else > +#define get_cop_pid(m) (COP_PID_NONE) > +#define disable_cop_pid(m) (COP_PID_NONE) > +#define free_cop_pid(p) > +#endif > diff --git a/arch/powerpc/mm/icswx_pid.c b/arch/powerpc/mm/icswx_pid.c > new file mode 100644 > index 0000000..91e30eb > --- /dev/null > +++ b/arch/powerpc/mm/icswx_pid.c > @@ -0,0 +1,87 @@ > +/* > + * ICSWX and ACOP/PID Management > + * > + * Copyright (C) 2011 Anton Blanchard, IBM Corp. <an...@samba.org> > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public License > + * as published by the Free Software Foundation; either version > + * 2 of the License, or (at your option) any later version. > + * > + */ > + > +#include <linux/sched.h> > +#include <linux/kernel.h> > +#include <linux/errno.h> > +#include <linux/types.h> > +#include <linux/mm.h> > +#include <linux/spinlock.h> > +#include <linux/idr.h> > +#include <linux/module.h> > +#include "icswx.h" > + > +#define COP_PID_MIN (COP_PID_NONE + 1) > +#define COP_PID_MAX (0xFFFF) > + > +static DEFINE_SPINLOCK(mmu_context_acop_lock); > +static DEFINE_IDA(cop_ida); > + > +static int new_cop_pid(struct ida *ida, int min_id, int max_id, > + spinlock_t *lock) > +{ > + int index; > + int err; > + > +again: > + if (!ida_pre_get(ida, GFP_KERNEL)) > + return -ENOMEM; > + > + spin_lock(lock); > + err = ida_get_new_above(ida, min_id, &index); > + spin_unlock(lock); > + > + if (err == -EAGAIN) > + goto again; > + else if (err) > + return err; > + > + if (index > max_id) { > + spin_lock(lock); > + ida_remove(ida, index); > + spin_unlock(lock); > + return -ENOMEM; > + } > + > + return index; > +} > + > +int get_cop_pid(struct mm_struct *mm) > +{ > + int pid; > + > + if (mm->context.cop_pid == COP_PID_NONE) { > + pid = new_cop_pid(&cop_ida, COP_PID_MIN, COP_PID_MAX, > + &mmu_context_acop_lock); > + if (pid >= 0) > + mm->context.cop_pid = pid; > + } > + return mm->context.cop_pid; > +} > + > +int disable_cop_pid(struct mm_struct *mm) > +{ > + int free_pid = COP_PID_NONE; > + > + if ((!mm->context.acop) && (mm->context.cop_pid != > COP_PID_NONE)) { > + free_pid = mm->context.cop_pid; > + mm->context.cop_pid = COP_PID_NONE; > + } > + return free_pid; > +} > + > +void free_cop_pid(int free_pid) > +{ > + spin_lock(&mmu_context_acop_lock); > + ida_remove(&cop_ida, free_pid); > + spin_unlock(&mmu_context_acop_lock); > +} > diff --git a/arch/powerpc/mm/mmu_context_hash64.c > b/arch/powerpc/mm/mmu_context_hash64.c index 3bafc3d..a75832c 100644 > --- a/arch/powerpc/mm/mmu_context_hash64.c > +++ b/arch/powerpc/mm/mmu_context_hash64.c > @@ -24,201 +24,6 @@ > > #include <asm/mmu_context.h> > > -#ifdef CONFIG_PPC_ICSWX > -/* > - * The processor and its L2 cache cause the icswx instruction to > - * generate a COP_REQ transaction on PowerBus. The transaction has > - * no address, and the processor does not perform an MMU access > - * to authenticate the transaction. The command portion of the > - * PowerBus COP_REQ transaction includes the LPAR_ID (LPID) and > - * the coprocessor Process ID (PID), which the coprocessor compares > - * to the authorized LPID and PID held in the coprocessor, to > determine > - * if the process is authorized to generate the transaction. > - * The data of the COP_REQ transaction is 128-byte or less and is > - * placed in cacheable memory on a 128-byte cache line boundary. > - * > - * The task to use a coprocessor should use use_cop() to allocate > - * a coprocessor PID before executing icswx instruction. use_cop() > - * also enables the coprocessor context switching. Drop_cop() is > - * used to free the coprocessor PID. > - * > - * Example: > - * Host Fabric Interface (HFI) is a PowerPC network coprocessor. > - * Each HFI have multiple windows. Each HFI window serves as a > - * network device sending to and receiving from HFI network. > - * HFI immediate send function uses icswx instruction. The immediate > - * send function allows small (single cache-line) packets be sent > - * without using the regular HFI send FIFO and doorbell, which are > - * much slower than immediate send. > - * > - * For each task intending to use HFI immediate send, the HFI driver > - * calls use_cop() to obtain a coprocessor PID for the task. > - * The HFI driver then allocate a free HFI window and save the > - * coprocessor PID to the HFI window to allow the task to use the > - * HFI window. > - * > - * The HFI driver repeatedly creates immediate send packets and > - * issues icswx instruction to send data through the HFI window. > - * The HFI compares the coprocessor PID in the CPU PID register > - * to the PID held in the HFI window to determine if the transaction > - * is allowed. > - * > - * When the task to release the HFI window, the HFI driver calls > - * drop_cop() to release the coprocessor PID. > - */ > - > -#define COP_PID_NONE 0 > -#define COP_PID_MIN (COP_PID_NONE + 1) > -#define COP_PID_MAX (0xFFFF) > - > -static DEFINE_SPINLOCK(mmu_context_acop_lock); > -static DEFINE_IDA(cop_ida); > - > -void switch_cop(struct mm_struct *next) > -{ > - mtspr(SPRN_PID, next->context.cop_pid); > - mtspr(SPRN_ACOP, next->context.acop); > -} > - > -static int new_cop_pid(struct ida *ida, int min_id, int max_id, > - spinlock_t *lock) > -{ > - int index; > - int err; > - > -again: > - if (!ida_pre_get(ida, GFP_KERNEL)) > - return -ENOMEM; > - > - spin_lock(lock); > - err = ida_get_new_above(ida, min_id, &index); > - spin_unlock(lock); > - > - if (err == -EAGAIN) > - goto again; > - else if (err) > - return err; > - > - if (index > max_id) { > - spin_lock(lock); > - ida_remove(ida, index); > - spin_unlock(lock); > - return -ENOMEM; > - } > - > - return index; > -} > - > -static void sync_cop(void *arg) > -{ > - struct mm_struct *mm = arg; > - > - if (mm == current->active_mm) > - switch_cop(current->active_mm); > -} > - > -/** > - * Start using a coprocessor. > - * @acop: mask of coprocessor to be used. > - * @mm: The mm the coprocessor to associate with. Most likely > current mm. > - * > - * Return a positive PID if successful. Negative errno otherwise. > - * The returned PID will be fed to the coprocessor to determine if an > - * icswx transaction is authenticated. > - */ > -int use_cop(unsigned long acop, struct mm_struct *mm) > -{ > - int ret; > - > - if (!cpu_has_feature(CPU_FTR_ICSWX)) > - return -ENODEV; > - > - if (!mm || !acop) > - return -EINVAL; > - > - /* We need to make sure mm_users doesn't change */ > - down_read(&mm->mmap_sem); > - spin_lock(mm->context.cop_lockp); > - > - if (mm->context.cop_pid == COP_PID_NONE) { > - ret = new_cop_pid(&cop_ida, COP_PID_MIN, COP_PID_MAX, > - &mmu_context_acop_lock); > - if (ret < 0) > - goto out; > - > - mm->context.cop_pid = ret; > - } > - mm->context.acop |= acop; > - > - sync_cop(mm); > - > - /* > - * If this is a threaded process then there might be other > threads > - * running. We need to send an IPI to force them to pick up > any > - * change in PID and ACOP. > - */ > - if (atomic_read(&mm->mm_users) > 1) > - smp_call_function(sync_cop, mm, 1); > - > - ret = mm->context.cop_pid; > - > -out: > - spin_unlock(mm->context.cop_lockp); > - up_read(&mm->mmap_sem); > - > - return ret; > -} > -EXPORT_SYMBOL_GPL(use_cop); > - > -/** > - * Stop using a coprocessor. > - * @acop: mask of coprocessor to be stopped. > - * @mm: The mm the coprocessor associated with. > - */ > -void drop_cop(unsigned long acop, struct mm_struct *mm) > -{ > - int free_pid = COP_PID_NONE; > - > - if (!cpu_has_feature(CPU_FTR_ICSWX)) > - return; > - > - if (WARN_ON_ONCE(!mm)) > - return; > - > - /* We need to make sure mm_users doesn't change */ > - down_read(&mm->mmap_sem); > - spin_lock(mm->context.cop_lockp); > - > - mm->context.acop &= ~acop; > - > - if ((!mm->context.acop) && (mm->context.cop_pid != > COP_PID_NONE)) { > - free_pid = mm->context.cop_pid; > - mm->context.cop_pid = COP_PID_NONE; > - } > - > - sync_cop(mm); > - > - /* > - * If this is a threaded process then there might be other > threads > - * running. We need to send an IPI to force them to pick up > any > - * change in PID and ACOP. > - */ > - if (atomic_read(&mm->mm_users) > 1) > - smp_call_function(sync_cop, mm, 1); > - > - if (free_pid != COP_PID_NONE) { > - spin_lock(&mmu_context_acop_lock); > - ida_remove(&cop_ida, free_pid); > - spin_unlock(&mmu_context_acop_lock); > - } > - > - spin_unlock(mm->context.cop_lockp); > - up_read(&mm->mmap_sem); > -} > -EXPORT_SYMBOL_GPL(drop_cop); > - > -#endif /* CONFIG_PPC_ICSWX */ > - > static DEFINE_SPINLOCK(mmu_context_lock); > static DEFINE_IDA(mmu_context_ida); > > diff --git a/arch/powerpc/platforms/Kconfig.cputype > b/arch/powerpc/platforms/Kconfig.cputype index e06e395..3cd22e5 100644 > --- a/arch/powerpc/platforms/Kconfig.cputype > +++ b/arch/powerpc/platforms/Kconfig.cputype > @@ -234,7 +234,7 @@ config VSX > > config PPC_ICSWX > bool "Support for PowerPC icswx coprocessor instruction" > - depends on POWER4 > + depends on POWER4 || PPC_A2 > default n > ---help--- > > @@ -250,6 +250,14 @@ config PPC_ICSWX > > If in doubt, say N here. > > +config PPC_ICSWX_PID > + bool "icswx requires direct PID management" > + depends on PPC_ICSWX && POWER4 > + default y > + ---help--- > + PID register in server is used explicitly for ICSWX. In > + embedded systems PID managment is done by the system. > + > config SPE > bool "SPE Support" > depends on E200 || (E500 && !PPC_E500MC) _______________________________________________ Linuxppc-dev mailing list Linuxppc-dev@lists.ozlabs.org https://lists.ozlabs.org/listinfo/linuxppc-dev