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