Merge master.kernel.org:/pub/scm/linux/kernel/git/wim/linux-2.6-watchdog
[linux-2.6] / arch / ppc64 / kernel / viopath.c
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);