diff options
author | Tseng-Hui (Frank) Lin <thlin@linux.vnet.ibm.com> | 2011-05-02 16:43:04 -0400 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2011-05-04 01:19:26 -0400 |
commit | 851d2e2fe8dbcbe3afcad6fc4569c881d8ad4ce9 (patch) | |
tree | 94082e7751a66236febed1cf18a1f01a02084ae9 /arch/powerpc | |
parent | a32e252f7cdfb3675a4e50215cfac356ed8952c4 (diff) |
powerpc: Add Initiate Coprocessor Store Word (icswx) support
Icswx is a PowerPC instruction to send data to a co-processor. On Book-S
processors the LPAR_ID and process ID (PID) of the owning process are
registered in the window context of the co-processor at initialization
time. When the icswx instruction is executed the L2 generates a cop-reg
transaction on PowerBus. The transaction has no address and the
processor does not perform an MMU access to authenticate the transaction.
The co-processor compares the LPAR_ID and the PID included in the
transaction and the LPAR_ID and PID held in the window context to
determine if the process is authorized to generate the transaction.
The OS needs to assign a 16-bit PID for the process. This cop-PID needs
to be updated during context switch. The cop-PID needs to be destroyed
when the context is destroyed.
Signed-off-by: Sonny Rao <sonnyrao@linux.vnet.ibm.com>
Signed-off-by: Tseng-Hui (Frank) Lin <thlin@linux.vnet.ibm.com>
Signed-off-by: Anton Blanchard <anton@samba.org>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'arch/powerpc')
-rw-r--r-- | arch/powerpc/include/asm/cputable.h | 4 | ||||
-rw-r--r-- | arch/powerpc/include/asm/mmu-hash64.h | 6 | ||||
-rw-r--r-- | arch/powerpc/include/asm/mmu_context.h | 10 | ||||
-rw-r--r-- | arch/powerpc/include/asm/reg.h | 1 | ||||
-rw-r--r-- | arch/powerpc/mm/mmu_context_hash64.c | 211 | ||||
-rw-r--r-- | arch/powerpc/platforms/Kconfig.cputype | 18 |
6 files changed, 249 insertions, 1 deletions
diff --git a/arch/powerpc/include/asm/cputable.h b/arch/powerpc/include/asm/cputable.h index 3db2476704d6..a3e1a9e96a7f 100644 --- a/arch/powerpc/include/asm/cputable.h +++ b/arch/powerpc/include/asm/cputable.h | |||
@@ -197,6 +197,7 @@ extern const char *powerpc_base_platform; | |||
197 | #define CPU_FTR_STCX_CHECKS_ADDRESS LONG_ASM_CONST(0x0200000000000000) | 197 | #define CPU_FTR_STCX_CHECKS_ADDRESS LONG_ASM_CONST(0x0200000000000000) |
198 | #define CPU_FTR_POPCNTB LONG_ASM_CONST(0x0400000000000000) | 198 | #define CPU_FTR_POPCNTB LONG_ASM_CONST(0x0400000000000000) |
199 | #define CPU_FTR_POPCNTD LONG_ASM_CONST(0x0800000000000000) | 199 | #define CPU_FTR_POPCNTD LONG_ASM_CONST(0x0800000000000000) |
200 | #define CPU_FTR_ICSWX LONG_ASM_CONST(0x1000000000000000) | ||
200 | 201 | ||
201 | #ifndef __ASSEMBLY__ | 202 | #ifndef __ASSEMBLY__ |
202 | 203 | ||
@@ -418,7 +419,8 @@ extern const char *powerpc_base_platform; | |||
418 | CPU_FTR_COHERENT_ICACHE | \ | 419 | CPU_FTR_COHERENT_ICACHE | \ |
419 | CPU_FTR_PURR | CPU_FTR_SPURR | CPU_FTR_REAL_LE | \ | 420 | CPU_FTR_PURR | CPU_FTR_SPURR | CPU_FTR_REAL_LE | \ |
420 | CPU_FTR_DSCR | CPU_FTR_SAO | CPU_FTR_ASYM_SMT | \ | 421 | CPU_FTR_DSCR | CPU_FTR_SAO | CPU_FTR_ASYM_SMT | \ |
421 | CPU_FTR_STCX_CHECKS_ADDRESS | CPU_FTR_POPCNTB | CPU_FTR_POPCNTD) | 422 | CPU_FTR_STCX_CHECKS_ADDRESS | CPU_FTR_POPCNTB | CPU_FTR_POPCNTD | \ |
423 | CPU_FTR_ICSWX) | ||
422 | #define CPU_FTRS_CELL (CPU_FTR_USE_TB | CPU_FTR_LWSYNC | \ | 424 | #define CPU_FTRS_CELL (CPU_FTR_USE_TB | CPU_FTR_LWSYNC | \ |
423 | CPU_FTR_PPCAS_ARCH_V2 | CPU_FTR_CTRL | \ | 425 | CPU_FTR_PPCAS_ARCH_V2 | CPU_FTR_CTRL | \ |
424 | CPU_FTR_ALTIVEC_COMP | CPU_FTR_MMCRA | CPU_FTR_SMT | \ | 426 | CPU_FTR_ALTIVEC_COMP | CPU_FTR_MMCRA | CPU_FTR_SMT | \ |
diff --git a/arch/powerpc/include/asm/mmu-hash64.h b/arch/powerpc/include/asm/mmu-hash64.h index ae7b3efec8e5..d865bd909c7d 100644 --- a/arch/powerpc/include/asm/mmu-hash64.h +++ b/arch/powerpc/include/asm/mmu-hash64.h | |||
@@ -408,6 +408,7 @@ static inline void subpage_prot_init_new_context(struct mm_struct *mm) { } | |||
408 | #endif /* CONFIG_PPC_SUBPAGE_PROT */ | 408 | #endif /* CONFIG_PPC_SUBPAGE_PROT */ |
409 | 409 | ||
410 | typedef unsigned long mm_context_id_t; | 410 | typedef unsigned long mm_context_id_t; |
411 | struct spinlock; | ||
411 | 412 | ||
412 | typedef struct { | 413 | typedef struct { |
413 | mm_context_id_t id; | 414 | mm_context_id_t id; |
@@ -423,6 +424,11 @@ typedef struct { | |||
423 | #ifdef CONFIG_PPC_SUBPAGE_PROT | 424 | #ifdef CONFIG_PPC_SUBPAGE_PROT |
424 | struct subpage_prot_table spt; | 425 | struct subpage_prot_table spt; |
425 | #endif /* CONFIG_PPC_SUBPAGE_PROT */ | 426 | #endif /* CONFIG_PPC_SUBPAGE_PROT */ |
427 | #ifdef CONFIG_PPC_ICSWX | ||
428 | struct spinlock *cop_lockp; /* guard acop and cop_pid */ | ||
429 | unsigned long acop; /* mask of enabled coprocessor types */ | ||
430 | unsigned int cop_pid; /* pid value used with coprocessors */ | ||
431 | #endif /* CONFIG_PPC_ICSWX */ | ||
426 | } mm_context_t; | 432 | } mm_context_t; |
427 | 433 | ||
428 | 434 | ||
diff --git a/arch/powerpc/include/asm/mmu_context.h b/arch/powerpc/include/asm/mmu_context.h index 8e13f65b498c..a73668a5f30d 100644 --- a/arch/powerpc/include/asm/mmu_context.h +++ b/arch/powerpc/include/asm/mmu_context.h | |||
@@ -32,6 +32,10 @@ extern void __destroy_context(unsigned long context_id); | |||
32 | extern void mmu_context_init(void); | 32 | extern void mmu_context_init(void); |
33 | #endif | 33 | #endif |
34 | 34 | ||
35 | extern void switch_cop(struct mm_struct *next); | ||
36 | extern int use_cop(unsigned long acop, struct mm_struct *mm); | ||
37 | extern void drop_cop(unsigned long acop, struct mm_struct *mm); | ||
38 | |||
35 | /* | 39 | /* |
36 | * switch_mm is the entry point called from the architecture independent | 40 | * switch_mm is the entry point called from the architecture independent |
37 | * code in kernel/sched.c | 41 | * code in kernel/sched.c |
@@ -55,6 +59,12 @@ static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, | |||
55 | if (prev == next) | 59 | if (prev == next) |
56 | return; | 60 | return; |
57 | 61 | ||
62 | #ifdef CONFIG_PPC_ICSWX | ||
63 | /* Switch coprocessor context only if prev or next uses a coprocessor */ | ||
64 | if (prev->context.acop || next->context.acop) | ||
65 | switch_cop(next); | ||
66 | #endif /* CONFIG_PPC_ICSWX */ | ||
67 | |||
58 | /* We must stop all altivec streams before changing the HW | 68 | /* We must stop all altivec streams before changing the HW |
59 | * context | 69 | * context |
60 | */ | 70 | */ |
diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h index 1f9ac12742e6..632e78e14441 100644 --- a/arch/powerpc/include/asm/reg.h +++ b/arch/powerpc/include/asm/reg.h | |||
@@ -188,6 +188,7 @@ | |||
188 | 188 | ||
189 | #define SPRN_CTR 0x009 /* Count Register */ | 189 | #define SPRN_CTR 0x009 /* Count Register */ |
190 | #define SPRN_DSCR 0x11 | 190 | #define SPRN_DSCR 0x11 |
191 | #define SPRN_ACOP 0x1F /* Available Coprocessor Register */ | ||
191 | #define SPRN_CTRLF 0x088 | 192 | #define SPRN_CTRLF 0x088 |
192 | #define SPRN_CTRLT 0x098 | 193 | #define SPRN_CTRLT 0x098 |
193 | #define CTRL_CT 0xc0000000 /* current thread */ | 194 | #define CTRL_CT 0xc0000000 /* current thread */ |
diff --git a/arch/powerpc/mm/mmu_context_hash64.c b/arch/powerpc/mm/mmu_context_hash64.c index c5859448a0ab..c517815404c4 100644 --- a/arch/powerpc/mm/mmu_context_hash64.c +++ b/arch/powerpc/mm/mmu_context_hash64.c | |||
@@ -20,9 +20,205 @@ | |||
20 | #include <linux/idr.h> | 20 | #include <linux/idr.h> |
21 | #include <linux/module.h> | 21 | #include <linux/module.h> |
22 | #include <linux/gfp.h> | 22 | #include <linux/gfp.h> |
23 | #include <linux/slab.h> | ||
23 | 24 | ||
24 | #include <asm/mmu_context.h> | 25 | #include <asm/mmu_context.h> |
25 | 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 | |||
74 | static DEFINE_SPINLOCK(mmu_context_acop_lock); | ||
75 | static DEFINE_IDA(cop_ida); | ||
76 | |||
77 | void switch_cop(struct mm_struct *next) | ||
78 | { | ||
79 | mtspr(SPRN_PID, next->context.cop_pid); | ||
80 | mtspr(SPRN_ACOP, next->context.acop); | ||
81 | } | ||
82 | |||
83 | static 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 | |||
89 | again: | ||
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 | |||
112 | static 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 | */ | ||
129 | int 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 | |||
165 | out: | ||
166 | spin_unlock(mm->context.cop_lockp); | ||
167 | up_read(&mm->mmap_sem); | ||
168 | |||
169 | return ret; | ||
170 | } | ||
171 | EXPORT_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 | */ | ||
178 | void 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 | } | ||
218 | EXPORT_SYMBOL_GPL(drop_cop); | ||
219 | |||
220 | #endif /* CONFIG_PPC_ICSWX */ | ||
221 | |||
26 | static DEFINE_SPINLOCK(mmu_context_lock); | 222 | static DEFINE_SPINLOCK(mmu_context_lock); |
27 | static DEFINE_IDA(mmu_context_ida); | 223 | static DEFINE_IDA(mmu_context_ida); |
28 | 224 | ||
@@ -78,6 +274,16 @@ int init_new_context(struct task_struct *tsk, struct mm_struct *mm) | |||
78 | slice_set_user_psize(mm, mmu_virtual_psize); | 274 | slice_set_user_psize(mm, mmu_virtual_psize); |
79 | subpage_prot_init_new_context(mm); | 275 | subpage_prot_init_new_context(mm); |
80 | mm->context.id = index; | 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 = NO_CONTEXT; | ||
283 | return -ENOMEM; | ||
284 | } | ||
285 | spin_lock_init(mm->context.cop_lockp); | ||
286 | #endif /* CONFIG_PPC_ICSWX */ | ||
81 | 287 | ||
82 | return 0; | 288 | return 0; |
83 | } | 289 | } |
@@ -92,6 +298,11 @@ EXPORT_SYMBOL_GPL(__destroy_context); | |||
92 | 298 | ||
93 | void destroy_context(struct mm_struct *mm) | 299 | void destroy_context(struct mm_struct *mm) |
94 | { | 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 */ | ||
95 | __destroy_context(mm->context.id); | 306 | __destroy_context(mm->context.id); |
96 | subpage_prot_free(mm); | 307 | subpage_prot_free(mm); |
97 | mm->context.id = MMU_NO_CONTEXT; | 308 | mm->context.id = MMU_NO_CONTEXT; |
diff --git a/arch/powerpc/platforms/Kconfig.cputype b/arch/powerpc/platforms/Kconfig.cputype index 7c1e1c64437b..a1e623822a30 100644 --- a/arch/powerpc/platforms/Kconfig.cputype +++ b/arch/powerpc/platforms/Kconfig.cputype | |||
@@ -230,6 +230,24 @@ config VSX | |||
230 | 230 | ||
231 | If in doubt, say Y here. | 231 | If in doubt, say Y here. |
232 | 232 | ||
233 | config PPC_ICSWX | ||
234 | bool "Support for PowerPC icswx coprocessor instruction" | ||
235 | depends on POWER4 | ||
236 | default n | ||
237 | ---help--- | ||
238 | |||
239 | This option enables kernel support for the PowerPC Initiate | ||
240 | Coprocessor Store Word (icswx) coprocessor instruction on POWER7 | ||
241 | or newer processors. | ||
242 | |||
243 | This option is only useful if you have a processor that supports | ||
244 | the icswx coprocessor instruction. It does not have any effect | ||
245 | on processors without the icswx coprocessor instruction. | ||
246 | |||
247 | This option slightly increases kernel memory usage. | ||
248 | |||
249 | If in doubt, say N here. | ||
250 | |||
233 | config SPE | 251 | config SPE |
234 | bool "SPE Support" | 252 | bool "SPE Support" |
235 | depends on E200 || (E500 && !PPC_E500MC) | 253 | depends on E200 || (E500 && !PPC_E500MC) |