diff options
author | Jimi Xenidis <jimix@pobox.com> | 2011-09-29 06:55:12 -0400 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2011-11-24 22:11:27 -0500 |
commit | 9d670280908013004f173b2b86414d9b6918511b (patch) | |
tree | bd7a7483ae7455cb481c72df907a3b4362e121c2 /arch/powerpc/mm/icswx.c | |
parent | 5182a131ddf988fe4f5cef9964dbfb64a188b0c3 (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/icswx.c')
-rw-r--r-- | arch/powerpc/mm/icswx.c | 160 |
1 files changed, 160 insertions, 0 deletions
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 | |||
66 | void 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 | */ | ||
83 | int 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 | |||
114 | out: | ||
115 | spin_unlock(mm->context.cop_lockp); | ||
116 | spin_unlock(&mm->page_table_lock); | ||
117 | |||
118 | return ret; | ||
119 | } | ||
120 | EXPORT_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 | */ | ||
127 | void 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 | } | ||
160 | EXPORT_SYMBOL_GPL(drop_cop); | ||