blob: 84e7ee2c086f59f21bec9a99b0ce368b07b11676 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/* -*- linux-c -*-
Linus Torvalds1da177e2005-04-16 15:20:36 -07002 *
3 * iSeries Virtual I/O Message Path code
4 *
5 * Authors: Dave Boutcher <boutcher@us.ibm.com>
6 * Ryan Arnold <ryanarn@us.ibm.com>
7 * Colin Devilbiss <devilbis@us.ibm.com>
8 *
Stephen Rothwelld223e722005-09-28 03:12:35 +10009 * (C) Copyright 2000-2005 IBM Corporation
Linus Torvalds1da177e2005-04-16 15:20:36 -070010 *
11 * This code is used by the iSeries virtual disk, cd,
12 * tape, and console to communicate with OS/400 in another
13 * partition.
14 *
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License as
17 * published by the Free Software Foundation; either version 2 of the
18 * License, or (at your option) anyu later version.
19 *
20 * This program is distributed in the hope that it will be useful, but
21 * WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * General Public License for more details.
24 *
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, write to the Free Software Foundation,
27 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 *
29 */
30#include <linux/module.h>
31#include <linux/kernel.h>
32#include <linux/errno.h>
33#include <linux/vmalloc.h>
34#include <linux/string.h>
35#include <linux/proc_fs.h>
36#include <linux/dma-mapping.h>
37#include <linux/wait.h>
38#include <linux/seq_file.h>
39#include <linux/smp_lock.h>
40#include <linux/interrupt.h>
41
42#include <asm/system.h>
43#include <asm/uaccess.h>
Michael Ellermana7496902006-07-13 17:52:01 +100044#include <asm/prom.h>
Kelly Daly1ec65d72005-11-02 13:46:07 +110045#include <asm/iseries/hv_types.h>
Kelly Dalye45423e2005-11-02 12:08:31 +110046#include <asm/iseries/hv_lp_event.h>
Kelly Daly15b17182005-11-02 11:55:28 +110047#include <asm/iseries/hv_lp_config.h>
Kelly Dalybbc8b622005-11-02 15:10:38 +110048#include <asm/iseries/mf.h>
Kelly Dalyb4206772005-11-02 15:13:57 +110049#include <asm/iseries/vio.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070050
51/* Status of the path to each other partition in the system.
52 * This is overkill, since we will only ever establish connections
53 * to our hosting partition and the primary partition on the system.
54 * But this allows for other support in the future.
55 */
56static struct viopathStatus {
57 int isOpen; /* Did we open the path? */
58 int isActive; /* Do we have a mon msg outstanding */
59 int users[VIO_MAX_SUBTYPES];
60 HvLpInstanceId mSourceInst;
61 HvLpInstanceId mTargetInst;
62 int numberAllocated;
63} viopathStatus[HVMAXARCHITECTEDLPS];
64
65static DEFINE_SPINLOCK(statuslock);
66
67/*
68 * For each kind of event we allocate a buffer that is
69 * guaranteed not to cross a page boundary
70 */
Benjamin Herrenschmidt3c726f82005-11-07 11:06:55 +110071static unsigned char event_buffer[VIO_MAX_SUBTYPES * 256]
72 __attribute__((__aligned__(4096)));
Linus Torvalds1da177e2005-04-16 15:20:36 -070073static atomic_t event_buffer_available[VIO_MAX_SUBTYPES];
74static int event_buffer_initialised;
75
76static 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 */
83struct 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 */
94static u8 viomonseq = 22;
95
96/* Our hosting logical partition. We get this at startup
97 * time, and different modules access this variable directly.
98 */
99HvLpIndex viopath_hostLp = HvLpIndexInvalid;
100EXPORT_SYMBOL(viopath_hostLp);
101HvLpIndex viopath_ourLp = HvLpIndexInvalid;
102EXPORT_SYMBOL(viopath_ourLp);
103
104/* For each kind of incoming event we set a pointer to a
105 * routine to call.
106 */
107static 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
112static 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);
Michael Ellermana7496902006-07-13 17:52:01 +1000119 struct device_node *node;
Stephen Rothwella0a428e2006-08-16 15:24:28 +1000120 const char *sysid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121
Yan Burmanf8485352006-12-02 13:26:57 +0200122 buf = kzalloc(HW_PAGE_SIZE, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123 if (!buf)
124 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125
Benjamin Herrenschmidt3c726f82005-11-07 11:06:55 +1100126 handle = dma_map_single(iSeries_vio_dev, buf, HW_PAGE_SIZE,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127 DMA_FROM_DEVICE);
128
129 hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
130 HvLpEvent_Type_VirtualIo,
131 viomajorsubtype_config | vioconfigget,
132 HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck,
133 viopath_sourceinst(viopath_hostLp),
134 viopath_targetinst(viopath_hostLp),
135 (u64)(unsigned long)&Semaphore, VIOVERSION << 16,
Benjamin Herrenschmidt3c726f82005-11-07 11:06:55 +1100136 ((u64)handle) << 32, HW_PAGE_SIZE, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137
138 if (hvrc != HvLpEvent_Rc_Good)
139 printk(VIOPATH_KERN_WARN "hv error on op %d\n", (int)hvrc);
140
141 down(&Semaphore);
142
143 vlanMap = HvLpConfig_getVirtualLanIndexMap();
144
Benjamin Herrenschmidt3c726f82005-11-07 11:06:55 +1100145 buf[HW_PAGE_SIZE-1] = '\0';
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 seq_printf(m, "%s", buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147
Benjamin Herrenschmidt3c726f82005-11-07 11:06:55 +1100148 dma_unmap_single(iSeries_vio_dev, handle, HW_PAGE_SIZE,
149 DMA_FROM_DEVICE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150 kfree(buf);
151
Michael Ellermana7496902006-07-13 17:52:01 +1000152 seq_printf(m, "AVAILABLE_VETH=%x\n", vlanMap);
153
154 node = of_find_node_by_path("/");
Stephen Rothwella0a428e2006-08-16 15:24:28 +1000155 sysid = NULL;
Michael Ellermana7496902006-07-13 17:52:01 +1000156 if (node != NULL)
Stephen Rothwella0a428e2006-08-16 15:24:28 +1000157 sysid = get_property(node, "system-id", NULL);
Michael Ellermana7496902006-07-13 17:52:01 +1000158
Stephen Rothwella0a428e2006-08-16 15:24:28 +1000159 if (sysid == NULL)
Michael Ellermana7496902006-07-13 17:52:01 +1000160 seq_printf(m, "SRLNBR=<UNKNOWN>\n");
161 else
162 /* Skip "IBM," on front of serial number, see dt.c */
Stephen Rothwella0a428e2006-08-16 15:24:28 +1000163 seq_printf(m, "SRLNBR=%s\n", sysid + 4);
Michael Ellermana7496902006-07-13 17:52:01 +1000164
165 of_node_put(node);
166
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167 return 0;
168}
169
170static int proc_viopath_open(struct inode *inode, struct file *file)
171{
172 return single_open(file, proc_viopath_show, NULL);
173}
174
175static struct file_operations proc_viopath_operations = {
176 .open = proc_viopath_open,
177 .read = seq_read,
178 .llseek = seq_lseek,
179 .release = single_release,
180};
181
182static int __init vio_proc_init(void)
183{
184 struct proc_dir_entry *e;
185
186 e = create_proc_entry("iSeries/config", 0, NULL);
187 if (e)
188 e->proc_fops = &proc_viopath_operations;
189
190 return 0;
191}
192__initcall(vio_proc_init);
193
194/* See if a given LP is active. Allow for invalid lps to be passed in
195 * and just return invalid
196 */
197int viopath_isactive(HvLpIndex lp)
198{
199 if (lp == HvLpIndexInvalid)
200 return 0;
201 if (lp < HVMAXARCHITECTEDLPS)
202 return viopathStatus[lp].isActive;
203 else
204 return 0;
205}
206EXPORT_SYMBOL(viopath_isactive);
207
208/*
209 * We cache the source and target instance ids for each
210 * partition.
211 */
212HvLpInstanceId viopath_sourceinst(HvLpIndex lp)
213{
214 return viopathStatus[lp].mSourceInst;
215}
216EXPORT_SYMBOL(viopath_sourceinst);
217
218HvLpInstanceId viopath_targetinst(HvLpIndex lp)
219{
220 return viopathStatus[lp].mTargetInst;
221}
222EXPORT_SYMBOL(viopath_targetinst);
223
224/*
225 * Send a monitor message. This is a message with the acknowledge
226 * bit on that the other side will NOT explicitly acknowledge. When
227 * the other side goes down, the hypervisor will acknowledge any
228 * outstanding messages....so we will know when the other side dies.
229 */
230static void sendMonMsg(HvLpIndex remoteLp)
231{
232 HvLpEvent_Rc hvrc;
233
234 viopathStatus[remoteLp].mSourceInst =
235 HvCallEvent_getSourceLpInstanceId(remoteLp,
236 HvLpEvent_Type_VirtualIo);
237 viopathStatus[remoteLp].mTargetInst =
238 HvCallEvent_getTargetLpInstanceId(remoteLp,
239 HvLpEvent_Type_VirtualIo);
240
241 /*
242 * Deliberately ignore the return code here. if we call this
243 * more than once, we don't care.
244 */
245 vio_setHandler(viomajorsubtype_monitor, handleMonitorEvent);
246
247 hvrc = HvCallEvent_signalLpEventFast(remoteLp, HvLpEvent_Type_VirtualIo,
248 viomajorsubtype_monitor, HvLpEvent_AckInd_DoAck,
249 HvLpEvent_AckType_DeferredAck,
250 viopathStatus[remoteLp].mSourceInst,
251 viopathStatus[remoteLp].mTargetInst,
252 viomonseq++, 0, 0, 0, 0, 0);
253
254 if (hvrc == HvLpEvent_Rc_Good)
255 viopathStatus[remoteLp].isActive = 1;
256 else {
257 printk(VIOPATH_KERN_WARN "could not connect to partition %d\n",
258 remoteLp);
259 viopathStatus[remoteLp].isActive = 0;
260 }
261}
262
263static void handleMonitorEvent(struct HvLpEvent *event)
264{
265 HvLpIndex remoteLp;
266 int i;
267
268 /*
269 * This handler is _also_ called as part of the loop
270 * at the end of this routine, so it must be able to
271 * ignore NULL events...
272 */
273 if (!event)
274 return;
275
276 /*
277 * First see if this is just a normal monitor message from the
278 * other partition
279 */
Stephen Rothwell677f8c02006-01-12 13:47:43 +1100280 if (hvlpevent_is_int(event)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281 remoteLp = event->xSourceLp;
282 if (!viopathStatus[remoteLp].isActive)
283 sendMonMsg(remoteLp);
284 return;
285 }
286
287 /*
288 * This path is for an acknowledgement; the other partition
289 * died
290 */
291 remoteLp = event->xTargetLp;
292 if ((event->xSourceInstanceId != viopathStatus[remoteLp].mSourceInst) ||
293 (event->xTargetInstanceId != viopathStatus[remoteLp].mTargetInst)) {
294 printk(VIOPATH_KERN_WARN "ignoring ack....mismatched instances\n");
295 return;
296 }
297
298 printk(VIOPATH_KERN_WARN "partition %d ended\n", remoteLp);
299
300 viopathStatus[remoteLp].isActive = 0;
301
302 /*
303 * For each active handler, pass them a NULL
304 * message to indicate that the other partition
305 * died
306 */
307 for (i = 0; i < VIO_MAX_SUBTYPES; i++) {
308 if (vio_handler[i] != NULL)
309 (*vio_handler[i])(NULL);
310 }
311}
312
313int vio_setHandler(int subtype, vio_event_handler_t *beh)
314{
315 subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT;
316 if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES))
317 return -EINVAL;
318 if (vio_handler[subtype] != NULL)
319 return -EBUSY;
320 vio_handler[subtype] = beh;
321 return 0;
322}
323EXPORT_SYMBOL(vio_setHandler);
324
325int vio_clearHandler(int subtype)
326{
327 subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT;
328 if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES))
329 return -EINVAL;
330 if (vio_handler[subtype] == NULL)
331 return -EAGAIN;
332 vio_handler[subtype] = NULL;
333 return 0;
334}
335EXPORT_SYMBOL(vio_clearHandler);
336
337static void handleConfig(struct HvLpEvent *event)
338{
339 if (!event)
340 return;
Stephen Rothwell677f8c02006-01-12 13:47:43 +1100341 if (hvlpevent_is_int(event)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342 printk(VIOPATH_KERN_WARN
343 "unexpected config request from partition %d",
344 event->xSourceLp);
345
Stephen Rothwell677f8c02006-01-12 13:47:43 +1100346 if (hvlpevent_need_ack(event)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347 event->xRc = HvLpEvent_Rc_InvalidSubtype;
348 HvCallEvent_ackLpEvent(event);
349 }
350 return;
351 }
352
353 up((struct semaphore *)event->xCorrelationToken);
354}
355
356/*
357 * Initialization of the hosting partition
358 */
359void vio_set_hostlp(void)
360{
361 /*
362 * If this has already been set then we DON'T want to either change
363 * it or re-register the proc file system
364 */
365 if (viopath_hostLp != HvLpIndexInvalid)
366 return;
367
368 /*
369 * Figure out our hosting partition. This isn't allowed to change
370 * while we're active
371 */
372 viopath_ourLp = HvLpConfig_getLpIndex();
Stephen Rothwelldd61ce92005-06-21 17:15:37 -0700373 viopath_hostLp = HvLpConfig_getHostingLpIndex(viopath_ourLp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374
375 if (viopath_hostLp != HvLpIndexInvalid)
376 vio_setHandler(viomajorsubtype_config, handleConfig);
377}
378EXPORT_SYMBOL(vio_set_hostlp);
379
Olaf Hering35a84c22006-10-07 22:08:26 +1000380static void vio_handleEvent(struct HvLpEvent *event)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381{
382 HvLpIndex remoteLp;
383 int subtype = (event->xSubtype & VIOMAJOR_SUBTYPE_MASK)
384 >> VIOMAJOR_SUBTYPE_SHIFT;
385
Stephen Rothwell677f8c02006-01-12 13:47:43 +1100386 if (hvlpevent_is_int(event)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700387 remoteLp = event->xSourceLp;
388 /*
389 * The isActive is checked because if the hosting partition
390 * went down and came back up it would not be active but it
391 * would have different source and target instances, in which
392 * case we'd want to reset them. This case really protects
393 * against an unauthorized active partition sending interrupts
394 * or acks to this linux partition.
395 */
396 if (viopathStatus[remoteLp].isActive
397 && (event->xSourceInstanceId !=
398 viopathStatus[remoteLp].mTargetInst)) {
399 printk(VIOPATH_KERN_WARN
400 "message from invalid partition. "
401 "int msg rcvd, source inst (%d) doesnt match (%d)\n",
402 viopathStatus[remoteLp].mTargetInst,
403 event->xSourceInstanceId);
404 return;
405 }
406
407 if (viopathStatus[remoteLp].isActive
408 && (event->xTargetInstanceId !=
409 viopathStatus[remoteLp].mSourceInst)) {
410 printk(VIOPATH_KERN_WARN
411 "message from invalid partition. "
412 "int msg rcvd, target inst (%d) doesnt match (%d)\n",
413 viopathStatus[remoteLp].mSourceInst,
414 event->xTargetInstanceId);
415 return;
416 }
417 } else {
418 remoteLp = event->xTargetLp;
419 if (event->xSourceInstanceId !=
420 viopathStatus[remoteLp].mSourceInst) {
421 printk(VIOPATH_KERN_WARN
422 "message from invalid partition. "
423 "ack msg rcvd, source inst (%d) doesnt match (%d)\n",
424 viopathStatus[remoteLp].mSourceInst,
425 event->xSourceInstanceId);
426 return;
427 }
428
429 if (event->xTargetInstanceId !=
430 viopathStatus[remoteLp].mTargetInst) {
431 printk(VIOPATH_KERN_WARN
432 "message from invalid partition. "
433 "viopath: ack msg rcvd, target inst (%d) doesnt match (%d)\n",
434 viopathStatus[remoteLp].mTargetInst,
435 event->xTargetInstanceId);
436 return;
437 }
438 }
439
440 if (vio_handler[subtype] == NULL) {
441 printk(VIOPATH_KERN_WARN
442 "unexpected virtual io event subtype %d from partition %d\n",
443 event->xSubtype, remoteLp);
444 /* No handler. Ack if necessary */
Stephen Rothwell677f8c02006-01-12 13:47:43 +1100445 if (hvlpevent_is_int(event) && hvlpevent_need_ack(event)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446 event->xRc = HvLpEvent_Rc_InvalidSubtype;
447 HvCallEvent_ackLpEvent(event);
448 }
449 return;
450 }
451
452 /* This innocuous little line is where all the real work happens */
453 (*vio_handler[subtype])(event);
454}
455
456static void viopath_donealloc(void *parm, int number)
457{
458 struct alloc_parms *parmsp = parm;
459
460 parmsp->number = number;
461 if (parmsp->used_wait_atomic)
462 atomic_set(&parmsp->wait_atomic, 0);
463 else
464 up(&parmsp->sem);
465}
466
467static int allocateEvents(HvLpIndex remoteLp, int numEvents)
468{
469 struct alloc_parms parms;
470
471 if (system_state != SYSTEM_RUNNING) {
472 parms.used_wait_atomic = 1;
473 atomic_set(&parms.wait_atomic, 1);
474 } else {
475 parms.used_wait_atomic = 0;
476 init_MUTEX_LOCKED(&parms.sem);
477 }
478 mf_allocate_lp_events(remoteLp, HvLpEvent_Type_VirtualIo, 250, /* It would be nice to put a real number here! */
479 numEvents, &viopath_donealloc, &parms);
480 if (system_state != SYSTEM_RUNNING) {
481 while (atomic_read(&parms.wait_atomic))
482 mb();
483 } else
484 down(&parms.sem);
485 return parms.number;
486}
487
488int viopath_open(HvLpIndex remoteLp, int subtype, int numReq)
489{
490 int i;
491 unsigned long flags;
492 int tempNumAllocated;
493
Stephen Rothwellc670b1a2005-06-21 17:15:41 -0700494 if ((remoteLp >= HVMAXARCHITECTEDLPS) || (remoteLp == HvLpIndexInvalid))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495 return -EINVAL;
496
497 subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT;
498 if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES))
499 return -EINVAL;
500
501 spin_lock_irqsave(&statuslock, flags);
502
503 if (!event_buffer_initialised) {
504 for (i = 0; i < VIO_MAX_SUBTYPES; i++)
505 atomic_set(&event_buffer_available[i], 1);
506 event_buffer_initialised = 1;
507 }
508
509 viopathStatus[remoteLp].users[subtype]++;
510
511 if (!viopathStatus[remoteLp].isOpen) {
512 viopathStatus[remoteLp].isOpen = 1;
513 HvCallEvent_openLpEventPath(remoteLp, HvLpEvent_Type_VirtualIo);
514
515 /*
516 * Don't hold the spinlock during an operation that
517 * can sleep.
518 */
519 spin_unlock_irqrestore(&statuslock, flags);
520 tempNumAllocated = allocateEvents(remoteLp, 1);
521 spin_lock_irqsave(&statuslock, flags);
522
523 viopathStatus[remoteLp].numberAllocated += tempNumAllocated;
524
525 if (viopathStatus[remoteLp].numberAllocated == 0) {
526 HvCallEvent_closeLpEventPath(remoteLp,
527 HvLpEvent_Type_VirtualIo);
528
529 spin_unlock_irqrestore(&statuslock, flags);
530 return -ENOMEM;
531 }
532
533 viopathStatus[remoteLp].mSourceInst =
534 HvCallEvent_getSourceLpInstanceId(remoteLp,
535 HvLpEvent_Type_VirtualIo);
536 viopathStatus[remoteLp].mTargetInst =
537 HvCallEvent_getTargetLpInstanceId(remoteLp,
538 HvLpEvent_Type_VirtualIo);
539 HvLpEvent_registerHandler(HvLpEvent_Type_VirtualIo,
540 &vio_handleEvent);
541 sendMonMsg(remoteLp);
542 printk(VIOPATH_KERN_INFO "opening connection to partition %d, "
543 "setting sinst %d, tinst %d\n",
544 remoteLp, viopathStatus[remoteLp].mSourceInst,
545 viopathStatus[remoteLp].mTargetInst);
546 }
547
548 spin_unlock_irqrestore(&statuslock, flags);
549 tempNumAllocated = allocateEvents(remoteLp, numReq);
550 spin_lock_irqsave(&statuslock, flags);
551 viopathStatus[remoteLp].numberAllocated += tempNumAllocated;
552 spin_unlock_irqrestore(&statuslock, flags);
553
554 return 0;
555}
556EXPORT_SYMBOL(viopath_open);
557
558int viopath_close(HvLpIndex remoteLp, int subtype, int numReq)
559{
560 unsigned long flags;
561 int i;
562 int numOpen;
563 struct alloc_parms parms;
564
Stephen Rothwellc670b1a2005-06-21 17:15:41 -0700565 if ((remoteLp >= HVMAXARCHITECTEDLPS) || (remoteLp == HvLpIndexInvalid))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566 return -EINVAL;
567
568 subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT;
569 if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES))
570 return -EINVAL;
571
572 spin_lock_irqsave(&statuslock, flags);
573 /*
574 * If the viopath_close somehow gets called before a
575 * viopath_open it could decrement to -1 which is a non
576 * recoverable state so we'll prevent this from
577 * happening.
578 */
579 if (viopathStatus[remoteLp].users[subtype] > 0)
580 viopathStatus[remoteLp].users[subtype]--;
581
582 spin_unlock_irqrestore(&statuslock, flags);
583
584 parms.used_wait_atomic = 0;
585 init_MUTEX_LOCKED(&parms.sem);
586 mf_deallocate_lp_events(remoteLp, HvLpEvent_Type_VirtualIo,
587 numReq, &viopath_donealloc, &parms);
588 down(&parms.sem);
589
590 spin_lock_irqsave(&statuslock, flags);
591 for (i = 0, numOpen = 0; i < VIO_MAX_SUBTYPES; i++)
592 numOpen += viopathStatus[remoteLp].users[i];
593
594 if ((viopathStatus[remoteLp].isOpen) && (numOpen == 0)) {
595 printk(VIOPATH_KERN_INFO "closing connection to partition %d",
596 remoteLp);
597
598 HvCallEvent_closeLpEventPath(remoteLp,
599 HvLpEvent_Type_VirtualIo);
600 viopathStatus[remoteLp].isOpen = 0;
601 viopathStatus[remoteLp].isActive = 0;
602
603 for (i = 0; i < VIO_MAX_SUBTYPES; i++)
604 atomic_set(&event_buffer_available[i], 0);
605 event_buffer_initialised = 0;
606 }
607 spin_unlock_irqrestore(&statuslock, flags);
608 return 0;
609}
610EXPORT_SYMBOL(viopath_close);
611
612void *vio_get_event_buffer(int subtype)
613{
614 subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT;
615 if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES))
616 return NULL;
617
618 if (atomic_dec_if_positive(&event_buffer_available[subtype]) == 0)
619 return &event_buffer[subtype * 256];
620 else
621 return NULL;
622}
623EXPORT_SYMBOL(vio_get_event_buffer);
624
625void vio_free_event_buffer(int subtype, void *buffer)
626{
627 subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT;
628 if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES)) {
629 printk(VIOPATH_KERN_WARN
630 "unexpected subtype %d freeing event buffer\n", subtype);
631 return;
632 }
633
634 if (atomic_read(&event_buffer_available[subtype]) != 0) {
635 printk(VIOPATH_KERN_WARN
636 "freeing unallocated event buffer, subtype %d\n",
637 subtype);
638 return;
639 }
640
641 if (buffer != &event_buffer[subtype * 256]) {
642 printk(VIOPATH_KERN_WARN
643 "freeing invalid event buffer, subtype %d\n", subtype);
644 }
645
646 atomic_set(&event_buffer_available[subtype], 1);
647}
648EXPORT_SYMBOL(vio_free_event_buffer);
649
650static const struct vio_error_entry vio_no_error =
651 { 0, 0, "Non-VIO Error" };
652static const struct vio_error_entry vio_unknown_error =
653 { 0, EIO, "Unknown Error" };
654
655static const struct vio_error_entry vio_default_errors[] = {
656 {0x0001, EIO, "No Connection"},
657 {0x0002, EIO, "No Receiver"},
658 {0x0003, EIO, "No Buffer Available"},
659 {0x0004, EBADRQC, "Invalid Message Type"},
660 {0x0000, 0, NULL},
661};
662
663const struct vio_error_entry *vio_lookup_rc(
664 const struct vio_error_entry *local_table, u16 rc)
665{
666 const struct vio_error_entry *cur;
667
668 if (!rc)
669 return &vio_no_error;
670 if (local_table)
671 for (cur = local_table; cur->rc; ++cur)
672 if (cur->rc == rc)
673 return cur;
674 for (cur = vio_default_errors; cur->rc; ++cur)
675 if (cur->rc == rc)
676 return cur;
677 return &vio_unknown_error;
678}
679EXPORT_SYMBOL(vio_lookup_rc);