Commit | Line | Data |
---|---|---|
b0d82bd5 DN |
1 | /* |
2 | * This file is subject to the terms and conditions of the GNU General Public | |
3 | * License. See the file "COPYING" in the main directory of this archive | |
4 | * for more details. | |
5 | * | |
6 | * Copyright (c) 2004-2005 Silicon Graphics, Inc. All Rights Reserved. | |
7 | */ | |
8 | ||
9 | ||
10 | /* | |
11 | * Cross Partition (XP) base. | |
12 | * | |
13 | * XP provides a base from which its users can interact | |
14 | * with XPC, yet not be dependent on XPC. | |
15 | * | |
16 | */ | |
17 | ||
18 | ||
19 | #include <linux/kernel.h> | |
20 | #include <linux/interrupt.h> | |
21 | #include <linux/module.h> | |
f9e505a9 | 22 | #include <linux/mutex.h> |
b0d82bd5 DN |
23 | #include <asm/sn/intr.h> |
24 | #include <asm/sn/sn_sal.h> | |
25 | #include <asm/sn/xp.h> | |
26 | ||
27 | ||
28 | /* | |
29 | * Target of nofault PIO read. | |
30 | */ | |
31 | u64 xp_nofault_PIOR_target; | |
32 | ||
33 | ||
34 | /* | |
35 | * xpc_registrations[] keeps track of xpc_connect()'s done by the kernel-level | |
36 | * users of XPC. | |
37 | */ | |
38 | struct xpc_registration xpc_registrations[XPC_NCHANNELS]; | |
39 | ||
40 | ||
41 | /* | |
42 | * Initialize the XPC interface to indicate that XPC isn't loaded. | |
43 | */ | |
44 | static enum xpc_retval xpc_notloaded(void) { return xpcNotLoaded; } | |
45 | ||
46 | struct xpc_interface xpc_interface = { | |
47 | (void (*)(int)) xpc_notloaded, | |
48 | (void (*)(int)) xpc_notloaded, | |
49 | (enum xpc_retval (*)(partid_t, int, u32, void **)) xpc_notloaded, | |
50 | (enum xpc_retval (*)(partid_t, int, void *)) xpc_notloaded, | |
51 | (enum xpc_retval (*)(partid_t, int, void *, xpc_notify_func, void *)) | |
52 | xpc_notloaded, | |
53 | (void (*)(partid_t, int, void *)) xpc_notloaded, | |
54 | (enum xpc_retval (*)(partid_t, void *)) xpc_notloaded | |
55 | }; | |
56 | ||
57 | ||
58 | /* | |
59 | * XPC calls this when it (the XPC module) has been loaded. | |
60 | */ | |
61 | void | |
62 | xpc_set_interface(void (*connect)(int), | |
63 | void (*disconnect)(int), | |
64 | enum xpc_retval (*allocate)(partid_t, int, u32, void **), | |
65 | enum xpc_retval (*send)(partid_t, int, void *), | |
66 | enum xpc_retval (*send_notify)(partid_t, int, void *, | |
67 | xpc_notify_func, void *), | |
68 | void (*received)(partid_t, int, void *), | |
69 | enum xpc_retval (*partid_to_nasids)(partid_t, void *)) | |
70 | { | |
71 | xpc_interface.connect = connect; | |
72 | xpc_interface.disconnect = disconnect; | |
73 | xpc_interface.allocate = allocate; | |
74 | xpc_interface.send = send; | |
75 | xpc_interface.send_notify = send_notify; | |
76 | xpc_interface.received = received; | |
77 | xpc_interface.partid_to_nasids = partid_to_nasids; | |
78 | } | |
79 | ||
80 | ||
81 | /* | |
82 | * XPC calls this when it (the XPC module) is being unloaded. | |
83 | */ | |
84 | void | |
85 | xpc_clear_interface(void) | |
86 | { | |
87 | xpc_interface.connect = (void (*)(int)) xpc_notloaded; | |
88 | xpc_interface.disconnect = (void (*)(int)) xpc_notloaded; | |
89 | xpc_interface.allocate = (enum xpc_retval (*)(partid_t, int, u32, | |
90 | void **)) xpc_notloaded; | |
91 | xpc_interface.send = (enum xpc_retval (*)(partid_t, int, void *)) | |
92 | xpc_notloaded; | |
93 | xpc_interface.send_notify = (enum xpc_retval (*)(partid_t, int, void *, | |
94 | xpc_notify_func, void *)) xpc_notloaded; | |
95 | xpc_interface.received = (void (*)(partid_t, int, void *)) | |
96 | xpc_notloaded; | |
97 | xpc_interface.partid_to_nasids = (enum xpc_retval (*)(partid_t, void *)) | |
98 | xpc_notloaded; | |
99 | } | |
100 | ||
101 | ||
102 | /* | |
103 | * Register for automatic establishment of a channel connection whenever | |
104 | * a partition comes up. | |
105 | * | |
106 | * Arguments: | |
107 | * | |
108 | * ch_number - channel # to register for connection. | |
109 | * func - function to call for asynchronous notification of channel | |
110 | * state changes (i.e., connection, disconnection, error) and | |
111 | * the arrival of incoming messages. | |
112 | * key - pointer to optional user-defined value that gets passed back | |
113 | * to the user on any callouts made to func. | |
114 | * payload_size - size in bytes of the XPC message's payload area which | |
115 | * contains a user-defined message. The user should make | |
116 | * this large enough to hold their largest message. | |
117 | * nentries - max #of XPC message entries a message queue can contain. | |
118 | * The actual number, which is determined when a connection | |
119 | * is established and may be less then requested, will be | |
120 | * passed to the user via the xpcConnected callout. | |
121 | * assigned_limit - max number of kthreads allowed to be processing | |
122 | * messages (per connection) at any given instant. | |
123 | * idle_limit - max number of kthreads allowed to be idle at any given | |
124 | * instant. | |
125 | */ | |
126 | enum xpc_retval | |
127 | xpc_connect(int ch_number, xpc_channel_func func, void *key, u16 payload_size, | |
128 | u16 nentries, u32 assigned_limit, u32 idle_limit) | |
129 | { | |
130 | struct xpc_registration *registration; | |
131 | ||
132 | ||
133 | DBUG_ON(ch_number < 0 || ch_number >= XPC_NCHANNELS); | |
134 | DBUG_ON(payload_size == 0 || nentries == 0); | |
135 | DBUG_ON(func == NULL); | |
136 | DBUG_ON(assigned_limit == 0 || idle_limit > assigned_limit); | |
137 | ||
138 | registration = &xpc_registrations[ch_number]; | |
139 | ||
f9e505a9 | 140 | if (mutex_lock_interruptible(®istration->mutex) != 0) { |
b0d82bd5 DN |
141 | return xpcInterrupted; |
142 | } | |
143 | ||
144 | /* if XPC_CHANNEL_REGISTERED(ch_number) */ | |
145 | if (registration->func != NULL) { | |
f9e505a9 | 146 | mutex_unlock(®istration->mutex); |
b0d82bd5 DN |
147 | return xpcAlreadyRegistered; |
148 | } | |
149 | ||
150 | /* register the channel for connection */ | |
151 | registration->msg_size = XPC_MSG_SIZE(payload_size); | |
152 | registration->nentries = nentries; | |
153 | registration->assigned_limit = assigned_limit; | |
154 | registration->idle_limit = idle_limit; | |
155 | registration->key = key; | |
156 | registration->func = func; | |
157 | ||
f9e505a9 | 158 | mutex_unlock(®istration->mutex); |
b0d82bd5 DN |
159 | |
160 | xpc_interface.connect(ch_number); | |
161 | ||
162 | return xpcSuccess; | |
163 | } | |
164 | ||
165 | ||
166 | /* | |
167 | * Remove the registration for automatic connection of the specified channel | |
168 | * when a partition comes up. | |
169 | * | |
170 | * Before returning this xpc_disconnect() will wait for all connections on the | |
171 | * specified channel have been closed/torndown. So the caller can be assured | |
172 | * that they will not be receiving any more callouts from XPC to their | |
173 | * function registered via xpc_connect(). | |
174 | * | |
175 | * Arguments: | |
176 | * | |
177 | * ch_number - channel # to unregister. | |
178 | */ | |
179 | void | |
180 | xpc_disconnect(int ch_number) | |
181 | { | |
182 | struct xpc_registration *registration; | |
183 | ||
184 | ||
185 | DBUG_ON(ch_number < 0 || ch_number >= XPC_NCHANNELS); | |
186 | ||
187 | registration = &xpc_registrations[ch_number]; | |
188 | ||
189 | /* | |
190 | * We've decided not to make this a down_interruptible(), since we | |
191 | * figured XPC's users will just turn around and call xpc_disconnect() | |
192 | * again anyways, so we might as well wait, if need be. | |
193 | */ | |
f9e505a9 | 194 | mutex_lock(®istration->mutex); |
b0d82bd5 DN |
195 | |
196 | /* if !XPC_CHANNEL_REGISTERED(ch_number) */ | |
197 | if (registration->func == NULL) { | |
f9e505a9 | 198 | mutex_unlock(®istration->mutex); |
b0d82bd5 DN |
199 | return; |
200 | } | |
201 | ||
202 | /* remove the connection registration for the specified channel */ | |
203 | registration->func = NULL; | |
204 | registration->key = NULL; | |
205 | registration->nentries = 0; | |
206 | registration->msg_size = 0; | |
207 | registration->assigned_limit = 0; | |
208 | registration->idle_limit = 0; | |
209 | ||
210 | xpc_interface.disconnect(ch_number); | |
211 | ||
f9e505a9 | 212 | mutex_unlock(®istration->mutex); |
b0d82bd5 DN |
213 | |
214 | return; | |
215 | } | |
216 | ||
217 | ||
218 | int __init | |
219 | xp_init(void) | |
220 | { | |
221 | int ret, ch_number; | |
222 | u64 func_addr = *(u64 *) xp_nofault_PIOR; | |
223 | u64 err_func_addr = *(u64 *) xp_error_PIOR; | |
224 | ||
225 | ||
226 | if (!ia64_platform_is("sn2")) { | |
227 | return -ENODEV; | |
228 | } | |
229 | ||
230 | /* | |
231 | * Register a nofault code region which performs a cross-partition | |
232 | * PIO read. If the PIO read times out, the MCA handler will consume | |
233 | * the error and return to a kernel-provided instruction to indicate | |
234 | * an error. This PIO read exists because it is guaranteed to timeout | |
235 | * if the destination is down (AMO operations do not timeout on at | |
236 | * least some CPUs on Shubs <= v1.2, which unfortunately we have to | |
237 | * work around). | |
238 | */ | |
239 | if ((ret = sn_register_nofault_code(func_addr, err_func_addr, | |
240 | err_func_addr, 1, 1)) != 0) { | |
241 | printk(KERN_ERR "XP: can't register nofault code, error=%d\n", | |
242 | ret); | |
243 | } | |
244 | /* | |
245 | * Setup the nofault PIO read target. (There is no special reason why | |
246 | * SH_IPI_ACCESS was selected.) | |
247 | */ | |
248 | if (is_shub2()) { | |
249 | xp_nofault_PIOR_target = SH2_IPI_ACCESS0; | |
250 | } else { | |
251 | xp_nofault_PIOR_target = SH1_IPI_ACCESS; | |
252 | } | |
253 | ||
f9e505a9 | 254 | /* initialize the connection registration mutex */ |
b0d82bd5 | 255 | for (ch_number = 0; ch_number < XPC_NCHANNELS; ch_number++) { |
f9e505a9 | 256 | mutex_init(&xpc_registrations[ch_number].mutex); |
b0d82bd5 DN |
257 | } |
258 | ||
259 | return 0; | |
260 | } | |
261 | module_init(xp_init); | |
262 | ||
263 | ||
264 | void __exit | |
265 | xp_exit(void) | |
266 | { | |
267 | u64 func_addr = *(u64 *) xp_nofault_PIOR; | |
268 | u64 err_func_addr = *(u64 *) xp_error_PIOR; | |
269 | ||
270 | ||
271 | /* unregister the PIO read nofault code region */ | |
272 | (void) sn_register_nofault_code(func_addr, err_func_addr, | |
273 | err_func_addr, 1, 0); | |
274 | } | |
275 | module_exit(xp_exit); | |
276 | ||
277 | ||
278 | MODULE_AUTHOR("Silicon Graphics, Inc."); | |
279 | MODULE_DESCRIPTION("Cross Partition (XP) base"); | |
280 | MODULE_LICENSE("GPL"); | |
281 | ||
282 | EXPORT_SYMBOL(xp_nofault_PIOR); | |
283 | EXPORT_SYMBOL(xp_nofault_PIOR_target); | |
284 | EXPORT_SYMBOL(xpc_registrations); | |
285 | EXPORT_SYMBOL(xpc_interface); | |
286 | EXPORT_SYMBOL(xpc_clear_interface); | |
287 | EXPORT_SYMBOL(xpc_set_interface); | |
288 | EXPORT_SYMBOL(xpc_connect); | |
289 | EXPORT_SYMBOL(xpc_disconnect); | |
290 |