powerpc: Split ICSWX ACOP and PID processing
authorJimi Xenidis <jimix@pobox.com>
Thu, 29 Sep 2011 10:55:12 +0000 (10:55 +0000)
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>
Fri, 25 Nov 2011 03:11:27 +0000 (14:11 +1100)
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 <jimix@pobox.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
arch/powerpc/mm/Makefile
arch/powerpc/mm/icswx.c [new file with mode: 0644]
arch/powerpc/mm/icswx.h [new file with mode: 0644]
arch/powerpc/mm/icswx_pid.c [new file with mode: 0644]
arch/powerpc/mm/mmu_context_hash64.c
arch/powerpc/platforms/Kconfig.cputype

index 991ee813d2a8da02acae26e135ed1f770d7ee6bc..3787b61f7d2022a72cbcbb20eb115c39b86ed8d5 100644 (file)
@@ -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 (file)
index 0000000..a98850f
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ *  ICSWX and ACOP Management
+ *
+ *  Copyright (C) 2011 Anton Blanchard, IBM Corp. <anton@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 128-byte or less in size and is placed in cacheable
+ * memory on a 128-byte cache line boundary.
+ *
+ * The task to use a coprocessor should use use_cop() to mark the use
+ * of the Coprocessor Type (CT) and context switching. On a server
+ * class 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
+ * 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.
+ */
+
+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 (file)
index 0000000..07514e4
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef _ARCH_POWERPC_MM_ICSWX_H_
+#define _ARCH_POWERPC_MM_ICSWX_H_
+
+/*
+ *  ICSWX and ACOP Management
+ *
+ *  Copyright (C) 2011 Anton Blanchard, IBM Corp. <anton@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
+
+#endif /* !_ARCH_POWERPC_MM_ICSWX_H_ */
diff --git a/arch/powerpc/mm/icswx_pid.c b/arch/powerpc/mm/icswx_pid.c
new file mode 100644 (file)
index 0000000..91e30eb
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ *  ICSWX and ACOP/PID Management
+ *
+ *  Copyright (C) 2011 Anton Blanchard, IBM Corp. <anton@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);
+}
index ca988a3d5fb250e32a8f426819acf6aee0931c93..40677aa0190e53b4013ddb535a181cda13390e63 100644 (file)
 
 #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;
-
-       /* The page_table_lock ensures mm_users won't change under us */
-       spin_lock(&mm->page_table_lock);
-       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);
-       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 = COP_PID_NONE;
-
-       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;
-
-       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);
-       spin_unlock(&mm->page_table_lock);
-}
-EXPORT_SYMBOL_GPL(drop_cop);
-
-#endif /* CONFIG_PPC_ICSWX */
+#include "icswx.h"
 
 static DEFINE_SPINLOCK(mmu_context_lock);
 static DEFINE_IDA(mmu_context_ida);
index fbecae0fbb49cc35aa01eb2002f61986c1ce25b9..a67105aeeda8aeb54eeac88bcb3f3a23250aa99d 100644 (file)
@@ -252,6 +252,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)