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