diff options
author | Stephen Rothwell <sfr@canb.auug.org.au> | 2005-09-27 13:12:35 -0400 |
---|---|---|
committer | Stephen Rothwell <sfr@canb.auug.org.au> | 2005-09-27 13:12:35 -0400 |
commit | d223e721b743787fad71f6aef5e860176214c8f9 (patch) | |
tree | e2fec87364dc1cbaa983f31c8276eb709ee491bd /arch/ppc64 | |
parent | b6b8681843f9eaee12f8c30ffac399f7b2601ab3 (diff) |
powerpc: Move viopath.c to powerpc/platforms/iseries
Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au>
Diffstat (limited to 'arch/ppc64')
-rw-r--r-- | arch/ppc64/kernel/Makefile | 1 | ||||
-rw-r--r-- | arch/ppc64/kernel/viopath.c | 673 |
2 files changed, 0 insertions, 674 deletions
diff --git a/arch/ppc64/kernel/Makefile b/arch/ppc64/kernel/Makefile index ee88fca0c886..bb5946b88b8b 100644 --- a/arch/ppc64/kernel/Makefile +++ b/arch/ppc64/kernel/Makefile | |||
@@ -38,7 +38,6 @@ obj-$(CONFIG_MODULES) += module.o ppc_ksyms.o | |||
38 | obj-$(CONFIG_PPC_RTAS) += rtas.o rtas_pci.o | 38 | obj-$(CONFIG_PPC_RTAS) += rtas.o rtas_pci.o |
39 | obj-$(CONFIG_RTAS_PROC) += rtas-proc.o | 39 | obj-$(CONFIG_RTAS_PROC) += rtas-proc.o |
40 | obj-$(CONFIG_SCANLOG) += scanlog.o | 40 | obj-$(CONFIG_SCANLOG) += scanlog.o |
41 | obj-$(CONFIG_VIOPATH) += viopath.o | ||
42 | obj-$(CONFIG_LPARCFG) += lparcfg.o | 41 | obj-$(CONFIG_LPARCFG) += lparcfg.o |
43 | obj-$(CONFIG_HVC_CONSOLE) += hvconsole.o | 42 | obj-$(CONFIG_HVC_CONSOLE) += hvconsole.o |
44 | obj-$(CONFIG_BOOTX_TEXT) += btext.o | 43 | obj-$(CONFIG_BOOTX_TEXT) += btext.o |
diff --git a/arch/ppc64/kernel/viopath.c b/arch/ppc64/kernel/viopath.c deleted file mode 100644 index 2a6c4f01c45e..000000000000 --- a/arch/ppc64/kernel/viopath.c +++ /dev/null | |||
@@ -1,673 +0,0 @@ | |||
1 | /* -*- linux-c -*- | ||
2 | * arch/ppc64/kernel/viopath.c | ||
3 | * | ||
4 | * iSeries Virtual I/O Message Path code | ||
5 | * | ||
6 | * Authors: Dave Boutcher <boutcher@us.ibm.com> | ||
7 | * Ryan Arnold <ryanarn@us.ibm.com> | ||
8 | * Colin Devilbiss <devilbis@us.ibm.com> | ||
9 | * | ||
10 | * (C) Copyright 2000-2003 IBM Corporation | ||
11 | * | ||
12 | * This code is used by the iSeries virtual disk, cd, | ||
13 | * tape, and console to communicate with OS/400 in another | ||
14 | * partition. | ||
15 | * | ||
16 | * This program is free software; you can redistribute it and/or | ||
17 | * modify it under the terms of the GNU General Public License as | ||
18 | * published by the Free Software Foundation; either version 2 of the | ||
19 | * License, or (at your option) anyu later version. | ||
20 | * | ||
21 | * This program is distributed in the hope that it will be useful, but | ||
22 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
23 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
24 | * General Public License for more details. | ||
25 | * | ||
26 | * You should have received a copy of the GNU General Public License | ||
27 | * along with this program; if not, write to the Free Software Foundation, | ||
28 | * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
29 | * | ||
30 | */ | ||
31 | #include <linux/module.h> | ||
32 | #include <linux/kernel.h> | ||
33 | #include <linux/errno.h> | ||
34 | #include <linux/vmalloc.h> | ||
35 | #include <linux/string.h> | ||
36 | #include <linux/proc_fs.h> | ||
37 | #include <linux/dma-mapping.h> | ||
38 | #include <linux/wait.h> | ||
39 | #include <linux/seq_file.h> | ||
40 | #include <linux/smp_lock.h> | ||
41 | #include <linux/interrupt.h> | ||
42 | |||
43 | #include <asm/system.h> | ||
44 | #include <asm/uaccess.h> | ||
45 | #include <asm/iSeries/HvTypes.h> | ||
46 | #include <asm/iSeries/ItExtVpdPanel.h> | ||
47 | #include <asm/iSeries/HvLpEvent.h> | ||
48 | #include <asm/iSeries/HvLpConfig.h> | ||
49 | #include <asm/iSeries/mf.h> | ||
50 | #include <asm/iSeries/vio.h> | ||
51 | |||
52 | /* Status of the path to each other partition in the system. | ||
53 | * This is overkill, since we will only ever establish connections | ||
54 | * to our hosting partition and the primary partition on the system. | ||
55 | * But this allows for other support in the future. | ||
56 | */ | ||
57 | static struct viopathStatus { | ||
58 | int isOpen; /* Did we open the path? */ | ||
59 | int isActive; /* Do we have a mon msg outstanding */ | ||
60 | int users[VIO_MAX_SUBTYPES]; | ||
61 | HvLpInstanceId mSourceInst; | ||
62 | HvLpInstanceId mTargetInst; | ||
63 | int numberAllocated; | ||
64 | } viopathStatus[HVMAXARCHITECTEDLPS]; | ||
65 | |||
66 | static DEFINE_SPINLOCK(statuslock); | ||
67 | |||
68 | /* | ||
69 | * For each kind of event we allocate a buffer that is | ||
70 | * guaranteed not to cross a page boundary | ||
71 | */ | ||
72 | static unsigned char event_buffer[VIO_MAX_SUBTYPES * 256] __page_aligned; | ||
73 | static atomic_t event_buffer_available[VIO_MAX_SUBTYPES]; | ||
74 | static int event_buffer_initialised; | ||
75 | |||
76 | static void handleMonitorEvent(struct HvLpEvent *event); | ||
77 | |||
78 | /* | ||
79 | * We use this structure to handle asynchronous responses. The caller | ||
80 | * blocks on the semaphore and the handler posts the semaphore. However, | ||
81 | * if system_state is not SYSTEM_RUNNING, then wait_atomic is used ... | ||
82 | */ | ||
83 | struct alloc_parms { | ||
84 | struct semaphore sem; | ||
85 | int number; | ||
86 | atomic_t wait_atomic; | ||
87 | int used_wait_atomic; | ||
88 | }; | ||
89 | |||
90 | /* Put a sequence number in each mon msg. The value is not | ||
91 | * important. Start at something other than 0 just for | ||
92 | * readability. wrapping this is ok. | ||
93 | */ | ||
94 | static u8 viomonseq = 22; | ||
95 | |||
96 | /* Our hosting logical partition. We get this at startup | ||
97 | * time, and different modules access this variable directly. | ||
98 | */ | ||
99 | HvLpIndex viopath_hostLp = HvLpIndexInvalid; | ||
100 | EXPORT_SYMBOL(viopath_hostLp); | ||
101 | HvLpIndex viopath_ourLp = HvLpIndexInvalid; | ||
102 | EXPORT_SYMBOL(viopath_ourLp); | ||
103 | |||
104 | /* For each kind of incoming event we set a pointer to a | ||
105 | * routine to call. | ||
106 | */ | ||
107 | static vio_event_handler_t *vio_handler[VIO_MAX_SUBTYPES]; | ||
108 | |||
109 | #define VIOPATH_KERN_WARN KERN_WARNING "viopath: " | ||
110 | #define VIOPATH_KERN_INFO KERN_INFO "viopath: " | ||
111 | |||
112 | static int proc_viopath_show(struct seq_file *m, void *v) | ||
113 | { | ||
114 | char *buf; | ||
115 | u16 vlanMap; | ||
116 | dma_addr_t handle; | ||
117 | HvLpEvent_Rc hvrc; | ||
118 | DECLARE_MUTEX_LOCKED(Semaphore); | ||
119 | |||
120 | buf = kmalloc(PAGE_SIZE, GFP_KERNEL); | ||
121 | if (!buf) | ||
122 | return 0; | ||
123 | memset(buf, 0, PAGE_SIZE); | ||
124 | |||
125 | handle = dma_map_single(iSeries_vio_dev, buf, PAGE_SIZE, | ||
126 | DMA_FROM_DEVICE); | ||
127 | |||
128 | hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, | ||
129 | HvLpEvent_Type_VirtualIo, | ||
130 | viomajorsubtype_config | vioconfigget, | ||
131 | HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, | ||
132 | viopath_sourceinst(viopath_hostLp), | ||
133 | viopath_targetinst(viopath_hostLp), | ||
134 | (u64)(unsigned long)&Semaphore, VIOVERSION << 16, | ||
135 | ((u64)handle) << 32, PAGE_SIZE, 0, 0); | ||
136 | |||
137 | if (hvrc != HvLpEvent_Rc_Good) | ||
138 | printk(VIOPATH_KERN_WARN "hv error on op %d\n", (int)hvrc); | ||
139 | |||
140 | down(&Semaphore); | ||
141 | |||
142 | vlanMap = HvLpConfig_getVirtualLanIndexMap(); | ||
143 | |||
144 | buf[PAGE_SIZE-1] = '\0'; | ||
145 | seq_printf(m, "%s", buf); | ||
146 | seq_printf(m, "AVAILABLE_VETH=%x\n", vlanMap); | ||
147 | seq_printf(m, "SRLNBR=%c%c%c%c%c%c%c\n", | ||
148 | e2a(xItExtVpdPanel.mfgID[2]), | ||
149 | e2a(xItExtVpdPanel.mfgID[3]), | ||
150 | e2a(xItExtVpdPanel.systemSerial[1]), | ||
151 | e2a(xItExtVpdPanel.systemSerial[2]), | ||
152 | e2a(xItExtVpdPanel.systemSerial[3]), | ||
153 | e2a(xItExtVpdPanel.systemSerial[4]), | ||
154 | e2a(xItExtVpdPanel.systemSerial[5])); | ||
155 | |||
156 | dma_unmap_single(iSeries_vio_dev, handle, PAGE_SIZE, DMA_FROM_DEVICE); | ||
157 | kfree(buf); | ||
158 | |||
159 | return 0; | ||
160 | } | ||
161 | |||
162 | static int proc_viopath_open(struct inode *inode, struct file *file) | ||
163 | { | ||
164 | return single_open(file, proc_viopath_show, NULL); | ||
165 | } | ||
166 | |||
167 | static struct file_operations proc_viopath_operations = { | ||
168 | .open = proc_viopath_open, | ||
169 | .read = seq_read, | ||
170 | .llseek = seq_lseek, | ||
171 | .release = single_release, | ||
172 | }; | ||
173 | |||
174 | static int __init vio_proc_init(void) | ||
175 | { | ||
176 | struct proc_dir_entry *e; | ||
177 | |||
178 | e = create_proc_entry("iSeries/config", 0, NULL); | ||
179 | if (e) | ||
180 | e->proc_fops = &proc_viopath_operations; | ||
181 | |||
182 | return 0; | ||
183 | } | ||
184 | __initcall(vio_proc_init); | ||
185 | |||
186 | /* See if a given LP is active. Allow for invalid lps to be passed in | ||
187 | * and just return invalid | ||
188 | */ | ||
189 | int viopath_isactive(HvLpIndex lp) | ||
190 | { | ||
191 | if (lp == HvLpIndexInvalid) | ||
192 | return 0; | ||
193 | if (lp < HVMAXARCHITECTEDLPS) | ||
194 | return viopathStatus[lp].isActive; | ||
195 | else | ||
196 | return 0; | ||
197 | } | ||
198 | EXPORT_SYMBOL(viopath_isactive); | ||
199 | |||
200 | /* | ||
201 | * We cache the source and target instance ids for each | ||
202 | * partition. | ||
203 | */ | ||
204 | HvLpInstanceId viopath_sourceinst(HvLpIndex lp) | ||
205 | { | ||
206 | return viopathStatus[lp].mSourceInst; | ||
207 | } | ||
208 | EXPORT_SYMBOL(viopath_sourceinst); | ||
209 | |||
210 | HvLpInstanceId viopath_targetinst(HvLpIndex lp) | ||
211 | { | ||
212 | return viopathStatus[lp].mTargetInst; | ||
213 | } | ||
214 | EXPORT_SYMBOL(viopath_targetinst); | ||
215 | |||
216 | /* | ||
217 | * Send a monitor message. This is a message with the acknowledge | ||
218 | * bit on that the other side will NOT explicitly acknowledge. When | ||
219 | * the other side goes down, the hypervisor will acknowledge any | ||
220 | * outstanding messages....so we will know when the other side dies. | ||
221 | */ | ||
222 | static void sendMonMsg(HvLpIndex remoteLp) | ||
223 | { | ||
224 | HvLpEvent_Rc hvrc; | ||
225 | |||
226 | viopathStatus[remoteLp].mSourceInst = | ||
227 | HvCallEvent_getSourceLpInstanceId(remoteLp, | ||
228 | HvLpEvent_Type_VirtualIo); | ||
229 | viopathStatus[remoteLp].mTargetInst = | ||
230 | HvCallEvent_getTargetLpInstanceId(remoteLp, | ||
231 | HvLpEvent_Type_VirtualIo); | ||
232 | |||
233 | /* | ||
234 | * Deliberately ignore the return code here. if we call this | ||
235 | * more than once, we don't care. | ||
236 | */ | ||
237 | vio_setHandler(viomajorsubtype_monitor, handleMonitorEvent); | ||
238 | |||
239 | hvrc = HvCallEvent_signalLpEventFast(remoteLp, HvLpEvent_Type_VirtualIo, | ||
240 | viomajorsubtype_monitor, HvLpEvent_AckInd_DoAck, | ||
241 | HvLpEvent_AckType_DeferredAck, | ||
242 | viopathStatus[remoteLp].mSourceInst, | ||
243 | viopathStatus[remoteLp].mTargetInst, | ||
244 | viomonseq++, 0, 0, 0, 0, 0); | ||
245 | |||
246 | if (hvrc == HvLpEvent_Rc_Good) | ||
247 | viopathStatus[remoteLp].isActive = 1; | ||
248 | else { | ||
249 | printk(VIOPATH_KERN_WARN "could not connect to partition %d\n", | ||
250 | remoteLp); | ||
251 | viopathStatus[remoteLp].isActive = 0; | ||
252 | } | ||
253 | } | ||
254 | |||
255 | static void handleMonitorEvent(struct HvLpEvent *event) | ||
256 | { | ||
257 | HvLpIndex remoteLp; | ||
258 | int i; | ||
259 | |||
260 | /* | ||
261 | * This handler is _also_ called as part of the loop | ||
262 | * at the end of this routine, so it must be able to | ||
263 | * ignore NULL events... | ||
264 | */ | ||
265 | if (!event) | ||
266 | return; | ||
267 | |||
268 | /* | ||
269 | * First see if this is just a normal monitor message from the | ||
270 | * other partition | ||
271 | */ | ||
272 | if (event->xFlags.xFunction == HvLpEvent_Function_Int) { | ||
273 | remoteLp = event->xSourceLp; | ||
274 | if (!viopathStatus[remoteLp].isActive) | ||
275 | sendMonMsg(remoteLp); | ||
276 | return; | ||
277 | } | ||
278 | |||
279 | /* | ||
280 | * This path is for an acknowledgement; the other partition | ||
281 | * died | ||
282 | */ | ||
283 | remoteLp = event->xTargetLp; | ||
284 | if ((event->xSourceInstanceId != viopathStatus[remoteLp].mSourceInst) || | ||
285 | (event->xTargetInstanceId != viopathStatus[remoteLp].mTargetInst)) { | ||
286 | printk(VIOPATH_KERN_WARN "ignoring ack....mismatched instances\n"); | ||
287 | return; | ||
288 | } | ||
289 | |||
290 | printk(VIOPATH_KERN_WARN "partition %d ended\n", remoteLp); | ||
291 | |||
292 | viopathStatus[remoteLp].isActive = 0; | ||
293 | |||
294 | /* | ||
295 | * For each active handler, pass them a NULL | ||
296 | * message to indicate that the other partition | ||
297 | * died | ||
298 | */ | ||
299 | for (i = 0; i < VIO_MAX_SUBTYPES; i++) { | ||
300 | if (vio_handler[i] != NULL) | ||
301 | (*vio_handler[i])(NULL); | ||
302 | } | ||
303 | } | ||
304 | |||
305 | int vio_setHandler(int subtype, vio_event_handler_t *beh) | ||
306 | { | ||
307 | subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT; | ||
308 | if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES)) | ||
309 | return -EINVAL; | ||
310 | if (vio_handler[subtype] != NULL) | ||
311 | return -EBUSY; | ||
312 | vio_handler[subtype] = beh; | ||
313 | return 0; | ||
314 | } | ||
315 | EXPORT_SYMBOL(vio_setHandler); | ||
316 | |||
317 | int vio_clearHandler(int subtype) | ||
318 | { | ||
319 | subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT; | ||
320 | if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES)) | ||
321 | return -EINVAL; | ||
322 | if (vio_handler[subtype] == NULL) | ||
323 | return -EAGAIN; | ||
324 | vio_handler[subtype] = NULL; | ||
325 | return 0; | ||
326 | } | ||
327 | EXPORT_SYMBOL(vio_clearHandler); | ||
328 | |||
329 | static void handleConfig(struct HvLpEvent *event) | ||
330 | { | ||
331 | if (!event) | ||
332 | return; | ||
333 | if (event->xFlags.xFunction == HvLpEvent_Function_Int) { | ||
334 | printk(VIOPATH_KERN_WARN | ||
335 | "unexpected config request from partition %d", | ||
336 | event->xSourceLp); | ||
337 | |||
338 | if ((event->xFlags.xFunction == HvLpEvent_Function_Int) && | ||
339 | (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck)) { | ||
340 | event->xRc = HvLpEvent_Rc_InvalidSubtype; | ||
341 | HvCallEvent_ackLpEvent(event); | ||
342 | } | ||
343 | return; | ||
344 | } | ||
345 | |||
346 | up((struct semaphore *)event->xCorrelationToken); | ||
347 | } | ||
348 | |||
349 | /* | ||
350 | * Initialization of the hosting partition | ||
351 | */ | ||
352 | void vio_set_hostlp(void) | ||
353 | { | ||
354 | /* | ||
355 | * If this has already been set then we DON'T want to either change | ||
356 | * it or re-register the proc file system | ||
357 | */ | ||
358 | if (viopath_hostLp != HvLpIndexInvalid) | ||
359 | return; | ||
360 | |||
361 | /* | ||
362 | * Figure out our hosting partition. This isn't allowed to change | ||
363 | * while we're active | ||
364 | */ | ||
365 | viopath_ourLp = HvLpConfig_getLpIndex(); | ||
366 | viopath_hostLp = HvLpConfig_getHostingLpIndex(viopath_ourLp); | ||
367 | |||
368 | if (viopath_hostLp != HvLpIndexInvalid) | ||
369 | vio_setHandler(viomajorsubtype_config, handleConfig); | ||
370 | } | ||
371 | EXPORT_SYMBOL(vio_set_hostlp); | ||
372 | |||
373 | static void vio_handleEvent(struct HvLpEvent *event, struct pt_regs *regs) | ||
374 | { | ||
375 | HvLpIndex remoteLp; | ||
376 | int subtype = (event->xSubtype & VIOMAJOR_SUBTYPE_MASK) | ||
377 | >> VIOMAJOR_SUBTYPE_SHIFT; | ||
378 | |||
379 | if (event->xFlags.xFunction == HvLpEvent_Function_Int) { | ||
380 | remoteLp = event->xSourceLp; | ||
381 | /* | ||
382 | * The isActive is checked because if the hosting partition | ||
383 | * went down and came back up it would not be active but it | ||
384 | * would have different source and target instances, in which | ||
385 | * case we'd want to reset them. This case really protects | ||
386 | * against an unauthorized active partition sending interrupts | ||
387 | * or acks to this linux partition. | ||
388 | */ | ||
389 | if (viopathStatus[remoteLp].isActive | ||
390 | && (event->xSourceInstanceId != | ||
391 | viopathStatus[remoteLp].mTargetInst)) { | ||
392 | printk(VIOPATH_KERN_WARN | ||
393 | "message from invalid partition. " | ||
394 | "int msg rcvd, source inst (%d) doesnt match (%d)\n", | ||
395 | viopathStatus[remoteLp].mTargetInst, | ||
396 | event->xSourceInstanceId); | ||
397 | return; | ||
398 | } | ||
399 | |||
400 | if (viopathStatus[remoteLp].isActive | ||
401 | && (event->xTargetInstanceId != | ||
402 | viopathStatus[remoteLp].mSourceInst)) { | ||
403 | printk(VIOPATH_KERN_WARN | ||
404 | "message from invalid partition. " | ||
405 | "int msg rcvd, target inst (%d) doesnt match (%d)\n", | ||
406 | viopathStatus[remoteLp].mSourceInst, | ||
407 | event->xTargetInstanceId); | ||
408 | return; | ||
409 | } | ||
410 | } else { | ||
411 | remoteLp = event->xTargetLp; | ||
412 | if (event->xSourceInstanceId != | ||
413 | viopathStatus[remoteLp].mSourceInst) { | ||
414 | printk(VIOPATH_KERN_WARN | ||
415 | "message from invalid partition. " | ||
416 | "ack msg rcvd, source inst (%d) doesnt match (%d)\n", | ||
417 | viopathStatus[remoteLp].mSourceInst, | ||
418 | event->xSourceInstanceId); | ||
419 | return; | ||
420 | } | ||
421 | |||
422 | if (event->xTargetInstanceId != | ||
423 | viopathStatus[remoteLp].mTargetInst) { | ||
424 | printk(VIOPATH_KERN_WARN | ||
425 | "message from invalid partition. " | ||
426 | "viopath: ack msg rcvd, target inst (%d) doesnt match (%d)\n", | ||
427 | viopathStatus[remoteLp].mTargetInst, | ||
428 | event->xTargetInstanceId); | ||
429 | return; | ||
430 | } | ||
431 | } | ||
432 | |||
433 | if (vio_handler[subtype] == NULL) { | ||
434 | printk(VIOPATH_KERN_WARN | ||
435 | "unexpected virtual io event subtype %d from partition %d\n", | ||
436 | event->xSubtype, remoteLp); | ||
437 | /* No handler. Ack if necessary */ | ||
438 | if ((event->xFlags.xFunction == HvLpEvent_Function_Int) && | ||
439 | (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck)) { | ||
440 | event->xRc = HvLpEvent_Rc_InvalidSubtype; | ||
441 | HvCallEvent_ackLpEvent(event); | ||
442 | } | ||
443 | return; | ||
444 | } | ||
445 | |||
446 | /* This innocuous little line is where all the real work happens */ | ||
447 | (*vio_handler[subtype])(event); | ||
448 | } | ||
449 | |||
450 | static void viopath_donealloc(void *parm, int number) | ||
451 | { | ||
452 | struct alloc_parms *parmsp = parm; | ||
453 | |||
454 | parmsp->number = number; | ||
455 | if (parmsp->used_wait_atomic) | ||
456 | atomic_set(&parmsp->wait_atomic, 0); | ||
457 | else | ||
458 | up(&parmsp->sem); | ||
459 | } | ||
460 | |||
461 | static int allocateEvents(HvLpIndex remoteLp, int numEvents) | ||
462 | { | ||
463 | struct alloc_parms parms; | ||
464 | |||
465 | if (system_state != SYSTEM_RUNNING) { | ||
466 | parms.used_wait_atomic = 1; | ||
467 | atomic_set(&parms.wait_atomic, 1); | ||
468 | } else { | ||
469 | parms.used_wait_atomic = 0; | ||
470 | init_MUTEX_LOCKED(&parms.sem); | ||
471 | } | ||
472 | mf_allocate_lp_events(remoteLp, HvLpEvent_Type_VirtualIo, 250, /* It would be nice to put a real number here! */ | ||
473 | numEvents, &viopath_donealloc, &parms); | ||
474 | if (system_state != SYSTEM_RUNNING) { | ||
475 | while (atomic_read(&parms.wait_atomic)) | ||
476 | mb(); | ||
477 | } else | ||
478 | down(&parms.sem); | ||
479 | return parms.number; | ||
480 | } | ||
481 | |||
482 | int viopath_open(HvLpIndex remoteLp, int subtype, int numReq) | ||
483 | { | ||
484 | int i; | ||
485 | unsigned long flags; | ||
486 | int tempNumAllocated; | ||
487 | |||
488 | if ((remoteLp >= HVMAXARCHITECTEDLPS) || (remoteLp == HvLpIndexInvalid)) | ||
489 | return -EINVAL; | ||
490 | |||
491 | subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT; | ||
492 | if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES)) | ||
493 | return -EINVAL; | ||
494 | |||
495 | spin_lock_irqsave(&statuslock, flags); | ||
496 | |||
497 | if (!event_buffer_initialised) { | ||
498 | for (i = 0; i < VIO_MAX_SUBTYPES; i++) | ||
499 | atomic_set(&event_buffer_available[i], 1); | ||
500 | event_buffer_initialised = 1; | ||
501 | } | ||
502 | |||
503 | viopathStatus[remoteLp].users[subtype]++; | ||
504 | |||
505 | if (!viopathStatus[remoteLp].isOpen) { | ||
506 | viopathStatus[remoteLp].isOpen = 1; | ||
507 | HvCallEvent_openLpEventPath(remoteLp, HvLpEvent_Type_VirtualIo); | ||
508 | |||
509 | /* | ||
510 | * Don't hold the spinlock during an operation that | ||
511 | * can sleep. | ||
512 | */ | ||
513 | spin_unlock_irqrestore(&statuslock, flags); | ||
514 | tempNumAllocated = allocateEvents(remoteLp, 1); | ||
515 | spin_lock_irqsave(&statuslock, flags); | ||
516 | |||
517 | viopathStatus[remoteLp].numberAllocated += tempNumAllocated; | ||
518 | |||
519 | if (viopathStatus[remoteLp].numberAllocated == 0) { | ||
520 | HvCallEvent_closeLpEventPath(remoteLp, | ||
521 | HvLpEvent_Type_VirtualIo); | ||
522 | |||
523 | spin_unlock_irqrestore(&statuslock, flags); | ||
524 | return -ENOMEM; | ||
525 | } | ||
526 | |||
527 | viopathStatus[remoteLp].mSourceInst = | ||
528 | HvCallEvent_getSourceLpInstanceId(remoteLp, | ||
529 | HvLpEvent_Type_VirtualIo); | ||
530 | viopathStatus[remoteLp].mTargetInst = | ||
531 | HvCallEvent_getTargetLpInstanceId(remoteLp, | ||
532 | HvLpEvent_Type_VirtualIo); | ||
533 | HvLpEvent_registerHandler(HvLpEvent_Type_VirtualIo, | ||
534 | &vio_handleEvent); | ||
535 | sendMonMsg(remoteLp); | ||
536 | printk(VIOPATH_KERN_INFO "opening connection to partition %d, " | ||
537 | "setting sinst %d, tinst %d\n", | ||
538 | remoteLp, viopathStatus[remoteLp].mSourceInst, | ||
539 | viopathStatus[remoteLp].mTargetInst); | ||
540 | } | ||
541 | |||
542 | spin_unlock_irqrestore(&statuslock, flags); | ||
543 | tempNumAllocated = allocateEvents(remoteLp, numReq); | ||
544 | spin_lock_irqsave(&statuslock, flags); | ||
545 | viopathStatus[remoteLp].numberAllocated += tempNumAllocated; | ||
546 | spin_unlock_irqrestore(&statuslock, flags); | ||
547 | |||
548 | return 0; | ||
549 | } | ||
550 | EXPORT_SYMBOL(viopath_open); | ||
551 | |||
552 | int viopath_close(HvLpIndex remoteLp, int subtype, int numReq) | ||
553 | { | ||
554 | unsigned long flags; | ||
555 | int i; | ||
556 | int numOpen; | ||
557 | struct alloc_parms parms; | ||
558 | |||
559 | if ((remoteLp >= HVMAXARCHITECTEDLPS) || (remoteLp == HvLpIndexInvalid)) | ||
560 | return -EINVAL; | ||
561 | |||
562 | subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT; | ||
563 | if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES)) | ||
564 | return -EINVAL; | ||
565 | |||
566 | spin_lock_irqsave(&statuslock, flags); | ||
567 | /* | ||
568 | * If the viopath_close somehow gets called before a | ||
569 | * viopath_open it could decrement to -1 which is a non | ||
570 | * recoverable state so we'll prevent this from | ||
571 | * happening. | ||
572 | */ | ||
573 | if (viopathStatus[remoteLp].users[subtype] > 0) | ||
574 | viopathStatus[remoteLp].users[subtype]--; | ||
575 | |||
576 | spin_unlock_irqrestore(&statuslock, flags); | ||
577 | |||
578 | parms.used_wait_atomic = 0; | ||
579 | init_MUTEX_LOCKED(&parms.sem); | ||
580 | mf_deallocate_lp_events(remoteLp, HvLpEvent_Type_VirtualIo, | ||
581 | numReq, &viopath_donealloc, &parms); | ||
582 | down(&parms.sem); | ||
583 | |||
584 | spin_lock_irqsave(&statuslock, flags); | ||
585 | for (i = 0, numOpen = 0; i < VIO_MAX_SUBTYPES; i++) | ||
586 | numOpen += viopathStatus[remoteLp].users[i]; | ||
587 | |||
588 | if ((viopathStatus[remoteLp].isOpen) && (numOpen == 0)) { | ||
589 | printk(VIOPATH_KERN_INFO "closing connection to partition %d", | ||
590 | remoteLp); | ||
591 | |||
592 | HvCallEvent_closeLpEventPath(remoteLp, | ||
593 | HvLpEvent_Type_VirtualIo); | ||
594 | viopathStatus[remoteLp].isOpen = 0; | ||
595 | viopathStatus[remoteLp].isActive = 0; | ||
596 | |||
597 | for (i = 0; i < VIO_MAX_SUBTYPES; i++) | ||
598 | atomic_set(&event_buffer_available[i], 0); | ||
599 | event_buffer_initialised = 0; | ||
600 | } | ||
601 | spin_unlock_irqrestore(&statuslock, flags); | ||
602 | return 0; | ||
603 | } | ||
604 | EXPORT_SYMBOL(viopath_close); | ||
605 | |||
606 | void *vio_get_event_buffer(int subtype) | ||
607 | { | ||
608 | subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT; | ||
609 | if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES)) | ||
610 | return NULL; | ||
611 | |||
612 | if (atomic_dec_if_positive(&event_buffer_available[subtype]) == 0) | ||
613 | return &event_buffer[subtype * 256]; | ||
614 | else | ||
615 | return NULL; | ||
616 | } | ||
617 | EXPORT_SYMBOL(vio_get_event_buffer); | ||
618 | |||
619 | void vio_free_event_buffer(int subtype, void *buffer) | ||
620 | { | ||
621 | subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT; | ||
622 | if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES)) { | ||
623 | printk(VIOPATH_KERN_WARN | ||
624 | "unexpected subtype %d freeing event buffer\n", subtype); | ||
625 | return; | ||
626 | } | ||
627 | |||
628 | if (atomic_read(&event_buffer_available[subtype]) != 0) { | ||
629 | printk(VIOPATH_KERN_WARN | ||
630 | "freeing unallocated event buffer, subtype %d\n", | ||
631 | subtype); | ||
632 | return; | ||
633 | } | ||
634 | |||
635 | if (buffer != &event_buffer[subtype * 256]) { | ||
636 | printk(VIOPATH_KERN_WARN | ||
637 | "freeing invalid event buffer, subtype %d\n", subtype); | ||
638 | } | ||
639 | |||
640 | atomic_set(&event_buffer_available[subtype], 1); | ||
641 | } | ||
642 | EXPORT_SYMBOL(vio_free_event_buffer); | ||
643 | |||
644 | static const struct vio_error_entry vio_no_error = | ||
645 | { 0, 0, "Non-VIO Error" }; | ||
646 | static const struct vio_error_entry vio_unknown_error = | ||
647 | { 0, EIO, "Unknown Error" }; | ||
648 | |||
649 | static const struct vio_error_entry vio_default_errors[] = { | ||
650 | {0x0001, EIO, "No Connection"}, | ||
651 | {0x0002, EIO, "No Receiver"}, | ||
652 | {0x0003, EIO, "No Buffer Available"}, | ||
653 | {0x0004, EBADRQC, "Invalid Message Type"}, | ||
654 | {0x0000, 0, NULL}, | ||
655 | }; | ||
656 | |||
657 | const struct vio_error_entry *vio_lookup_rc( | ||
658 | const struct vio_error_entry *local_table, u16 rc) | ||
659 | { | ||
660 | const struct vio_error_entry *cur; | ||
661 | |||
662 | if (!rc) | ||
663 | return &vio_no_error; | ||
664 | if (local_table) | ||
665 | for (cur = local_table; cur->rc; ++cur) | ||
666 | if (cur->rc == rc) | ||
667 | return cur; | ||
668 | for (cur = vio_default_errors; cur->rc; ++cur) | ||
669 | if (cur->rc == rc) | ||
670 | return cur; | ||
671 | return &vio_unknown_error; | ||
672 | } | ||
673 | EXPORT_SYMBOL(vio_lookup_rc); | ||