Commit | Line | Data |
---|---|---|
9d670280 JX |
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); |