aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/mm
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 /arch/powerpc/mm
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>
Diffstat (limited to 'arch/powerpc/mm')
-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
5 files changed, 289 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);