aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJimi Xenidis <jimix@pobox.com>2011-09-29 06:55:12 -0400
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2011-11-24 22:11:27 -0500
commit9d670280908013004f173b2b86414d9b6918511b (patch)
treebd7a7483ae7455cb481c72df907a3b4362e121c2
parent5182a131ddf988fe4f5cef9964dbfb64a188b0c3 (diff)
powerpc: Split ICSWX ACOP and PID processing
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>
-rw-r--r--arch/powerpc/mm/Makefile2
-rw-r--r--arch/powerpc/mm/icswx.c160
-rw-r--r--arch/powerpc/mm/icswx.h39
-rw-r--r--arch/powerpc/mm/icswx_pid.c87
-rw-r--r--arch/powerpc/mm/mmu_context_hash64.c195
-rw-r--r--arch/powerpc/platforms/Kconfig.cputype8
6 files changed, 297 insertions, 194 deletions
diff --git a/arch/powerpc/mm/Makefile b/arch/powerpc/mm/Makefile
index 991ee813d2a8..3787b61f7d20 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
21obj-$(CONFIG_PPC_STD_MMU) += hash_low_$(CONFIG_WORD_SIZE).o \ 21obj-$(CONFIG_PPC_STD_MMU) += hash_low_$(CONFIG_WORD_SIZE).o \
22 tlb_hash$(CONFIG_WORD_SIZE).o \ 22 tlb_hash$(CONFIG_WORD_SIZE).o \
23 mmu_context_hash$(CONFIG_WORD_SIZE).o 23 mmu_context_hash$(CONFIG_WORD_SIZE).o
24obj-$(CONFIG_PPC_ICSWX) += icswx.o
25obj-$(CONFIG_PPC_ICSWX_PID) += icswx_pid.o
24obj-$(CONFIG_40x) += 40x_mmu.o 26obj-$(CONFIG_40x) += 40x_mmu.o
25obj-$(CONFIG_44x) += 44x_mmu.o 27obj-$(CONFIG_44x) += 44x_mmu.o
26obj-$(CONFIG_PPC_FSL_BOOK3E) += fsl_booke_mmu.o 28obj-$(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 000000000000..a98850fd7777
--- /dev/null
+++ b/arch/powerpc/mm/icswx.c
@@ -0,0 +1,160 @@
1/*
2 * ICSWX and ACOP Management
3 *
4 * Copyright (C) 2011 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/types.h>
17#include <linux/mm.h>
18#include <linux/spinlock.h>
19#include <linux/module.h>
20#include "icswx.h"
21
22/*
23 * The processor and its L2 cache cause the icswx instruction to
24 * generate a COP_REQ transaction on PowerBus. The transaction has no
25 * address, and the processor does not perform an MMU access to
26 * authenticate the transaction. The command portion of the PowerBus
27 * COP_REQ transaction includes the LPAR_ID (LPID) and the coprocessor
28 * Process ID (PID), which the coprocessor compares to the authorized
29 * LPID and PID held in the coprocessor, to determine if the process
30 * is authorized to generate the transaction. The data of the COP_REQ
31 * transaction is 128-byte or less in size and is placed in cacheable
32 * memory on a 128-byte cache line boundary.
33 *
34 * The task to use a coprocessor should use use_cop() to mark the use
35 * of the Coprocessor Type (CT) and context switching. On a server
36 * class processor, the PID register is used only for coprocessor
37 * management + * and so a coprocessor PID is allocated before
38 * executing icswx + * instruction. Drop_cop() is used to free the
39 * coprocessor PID.
40 *
41 * Example:
42 * Host Fabric Interface (HFI) is a PowerPC network coprocessor.
43 * Each HFI have multiple windows. Each HFI window serves as a
44 * network device sending to and receiving from HFI network.
45 * HFI immediate send function uses icswx instruction. The immediate
46 * send function allows small (single cache-line) packets be sent
47 * without using the regular HFI send FIFO and doorbell, which are
48 * much slower than immediate send.
49 *
50 * For each task intending to use HFI immediate send, the HFI driver
51 * calls use_cop() to obtain a coprocessor PID for the task.
52 * The HFI driver then allocate a free HFI window and save the
53 * coprocessor PID to the HFI window to allow the task to use the
54 * HFI window.
55 *
56 * The HFI driver repeatedly creates immediate send packets and
57 * issues icswx instruction to send data through the HFI window.
58 * The HFI compares the coprocessor PID in the CPU PID register
59 * to the PID held in the HFI window to determine if the transaction
60 * is allowed.
61 *
62 * When the task to release the HFI window, the HFI driver calls
63 * drop_cop() to release the coprocessor PID.
64 */
65
66void switch_cop(struct mm_struct *next)
67{
68#ifdef CONFIG_ICSWX_PID
69 mtspr(SPRN_PID, next->context.cop_pid);
70#endif
71 mtspr(SPRN_ACOP, next->context.acop);
72}
73
74/**
75 * Start using a coprocessor.
76 * @acop: mask of coprocessor to be used.
77 * @mm: The mm the coprocessor to associate with. Most likely current mm.
78 *
79 * Return a positive PID if successful. Negative errno otherwise.
80 * The returned PID will be fed to the coprocessor to determine if an
81 * icswx transaction is authenticated.
82 */
83int use_cop(unsigned long acop, struct mm_struct *mm)
84{
85 int ret;
86
87 if (!cpu_has_feature(CPU_FTR_ICSWX))
88 return -ENODEV;
89
90 if (!mm || !acop)
91 return -EINVAL;
92
93 /* The page_table_lock ensures mm_users won't change under us */
94 spin_lock(&mm->page_table_lock);
95 spin_lock(mm->context.cop_lockp);
96
97 ret = get_cop_pid(mm);
98 if (ret < 0)
99 goto out;
100
101 /* update acop */
102 mm->context.acop |= acop;
103
104 sync_cop(mm);
105
106 /*
107 * If this is a threaded process then there might be other threads
108 * running. We need to send an IPI to force them to pick up any
109 * change in PID and ACOP.
110 */
111 if (atomic_read(&mm->mm_users) > 1)
112 smp_call_function(sync_cop, mm, 1);
113
114out:
115 spin_unlock(mm->context.cop_lockp);
116 spin_unlock(&mm->page_table_lock);
117
118 return ret;
119}
120EXPORT_SYMBOL_GPL(use_cop);
121
122/**
123 * Stop using a coprocessor.
124 * @acop: mask of coprocessor to be stopped.
125 * @mm: The mm the coprocessor associated with.
126 */
127void drop_cop(unsigned long acop, struct mm_struct *mm)
128{
129 int free_pid;
130
131 if (!cpu_has_feature(CPU_FTR_ICSWX))
132 return;
133
134 if (WARN_ON_ONCE(!mm))
135 return;
136
137 /* The page_table_lock ensures mm_users won't change under us */
138 spin_lock(&mm->page_table_lock);
139 spin_lock(mm->context.cop_lockp);
140
141 mm->context.acop &= ~acop;
142
143 free_pid = disable_cop_pid(mm);
144 sync_cop(mm);
145
146 /*
147 * If this is a threaded process then there might be other threads
148 * running. We need to send an IPI to force them to pick up any
149 * change in PID and ACOP.
150 */
151 if (atomic_read(&mm->mm_users) > 1)
152 smp_call_function(sync_cop, mm, 1);
153
154 if (free_pid != COP_PID_NONE)
155 free_cop_pid(free_pid);
156
157 spin_unlock(mm->context.cop_lockp);
158 spin_unlock(&mm->page_table_lock);
159}
160EXPORT_SYMBOL_GPL(drop_cop);
diff --git a/arch/powerpc/mm/icswx.h b/arch/powerpc/mm/icswx.h
new file mode 100644
index 000000000000..07514e49498e
--- /dev/null
+++ b/arch/powerpc/mm/icswx.h
@@ -0,0 +1,39 @@
1#ifndef _ARCH_POWERPC_MM_ICSWX_H_
2#define _ARCH_POWERPC_MM_ICSWX_H_
3
4/*
5 * ICSWX and ACOP Management
6 *
7 * Copyright (C) 2011 Anton Blanchard, IBM Corp. <anton@samba.org>
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version
12 * 2 of the License, or (at your option) any later version.
13 *
14 */
15
16#include <asm/mmu_context.h>
17
18/* also used to denote that PIDs are not used */
19#define COP_PID_NONE 0
20
21static inline void sync_cop(void *arg)
22{
23 struct mm_struct *mm = arg;
24
25 if (mm == current->active_mm)
26 switch_cop(current->active_mm);
27}
28
29#ifdef CONFIG_PPC_ICSWX_PID
30extern int get_cop_pid(struct mm_struct *mm);
31extern int disable_cop_pid(struct mm_struct *mm);
32extern void free_cop_pid(int free_pid);
33#else
34#define get_cop_pid(m) (COP_PID_NONE)
35#define disable_cop_pid(m) (COP_PID_NONE)
36#define free_cop_pid(p)
37#endif
38
39#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
index 000000000000..91e30eb7d054
--- /dev/null
+++ b/arch/powerpc/mm/icswx_pid.c
@@ -0,0 +1,87 @@
1/*
2 * ICSWX and ACOP/PID Management
3 *
4 * Copyright (C) 2011 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/types.h>
17#include <linux/mm.h>
18#include <linux/spinlock.h>
19#include <linux/idr.h>
20#include <linux/module.h>
21#include "icswx.h"
22
23#define COP_PID_MIN (COP_PID_NONE + 1)
24#define COP_PID_MAX (0xFFFF)
25
26static DEFINE_SPINLOCK(mmu_context_acop_lock);
27static DEFINE_IDA(cop_ida);
28
29static int new_cop_pid(struct ida *ida, int min_id, int max_id,
30 spinlock_t *lock)
31{
32 int index;
33 int err;
34
35again:
36 if (!ida_pre_get(ida, GFP_KERNEL))
37 return -ENOMEM;
38
39 spin_lock(lock);
40 err = ida_get_new_above(ida, min_id, &index);
41 spin_unlock(lock);
42
43 if (err == -EAGAIN)
44 goto again;
45 else if (err)
46 return err;
47
48 if (index > max_id) {
49 spin_lock(lock);
50 ida_remove(ida, index);
51 spin_unlock(lock);
52 return -ENOMEM;
53 }
54
55 return index;
56}
57
58int get_cop_pid(struct mm_struct *mm)
59{
60 int pid;
61
62 if (mm->context.cop_pid == COP_PID_NONE) {
63 pid = new_cop_pid(&cop_ida, COP_PID_MIN, COP_PID_MAX,
64 &mmu_context_acop_lock);
65 if (pid >= 0)
66 mm->context.cop_pid = pid;
67 }
68 return mm->context.cop_pid;
69}
70
71int disable_cop_pid(struct mm_struct *mm)
72{
73 int free_pid = COP_PID_NONE;
74
75 if ((!mm->context.acop) && (mm->context.cop_pid != COP_PID_NONE)) {
76 free_pid = mm->context.cop_pid;
77 mm->context.cop_pid = COP_PID_NONE;
78 }
79 return free_pid;
80}
81
82void free_cop_pid(int free_pid)
83{
84 spin_lock(&mmu_context_acop_lock);
85 ida_remove(&cop_ida, free_pid);
86 spin_unlock(&mmu_context_acop_lock);
87}
diff --git a/arch/powerpc/mm/mmu_context_hash64.c b/arch/powerpc/mm/mmu_context_hash64.c
index ca988a3d5fb2..40677aa0190e 100644
--- a/arch/powerpc/mm/mmu_context_hash64.c
+++ b/arch/powerpc/mm/mmu_context_hash64.c
@@ -24,200 +24,7 @@
24 24
25#include <asm/mmu_context.h> 25#include <asm/mmu_context.h>
26 26
27#ifdef CONFIG_PPC_ICSWX 27#include "icswx.h"
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 /* The page_table_lock ensures mm_users won't change under us */
140 spin_lock(&mm->page_table_lock);
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 spin_unlock(&mm->page_table_lock);
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 /* The page_table_lock ensures mm_users won't change under us */
189 spin_lock(&mm->page_table_lock);
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 spin_unlock(&mm->page_table_lock);
217}
218EXPORT_SYMBOL_GPL(drop_cop);
219
220#endif /* CONFIG_PPC_ICSWX */
221 28
222static DEFINE_SPINLOCK(mmu_context_lock); 29static DEFINE_SPINLOCK(mmu_context_lock);
223static DEFINE_IDA(mmu_context_ida); 30static DEFINE_IDA(mmu_context_ida);
diff --git a/arch/powerpc/platforms/Kconfig.cputype b/arch/powerpc/platforms/Kconfig.cputype
index fbecae0fbb49..a67105aeeda8 100644
--- a/arch/powerpc/platforms/Kconfig.cputype
+++ b/arch/powerpc/platforms/Kconfig.cputype
@@ -252,6 +252,14 @@ config PPC_ICSWX
252 252
253 If in doubt, say N here. 253 If in doubt, say N here.
254 254
255config PPC_ICSWX_PID
256 bool "icswx requires direct PID management"
257 depends on PPC_ICSWX && POWER4
258 default y
259 ---help---
260 PID register in server is used explicitly for ICSWX. In
261 embedded systems PID managment is done by the system.
262
255config SPE 263config SPE
256 bool "SPE Support" 264 bool "SPE Support"
257 depends on E200 || (E500 && !PPC_E500MC) 265 depends on E200 || (E500 && !PPC_E500MC)