Path: blob/master/arch/powerpc/platforms/iseries/viopath.c
10820 views
/* -*- linux-c -*-1*2* iSeries Virtual I/O Message Path code3*4* Authors: Dave Boutcher <[email protected]>5* Ryan Arnold <[email protected]>6* Colin Devilbiss <[email protected]>7*8* (C) Copyright 2000-2005 IBM Corporation9*10* This code is used by the iSeries virtual disk, cd,11* tape, and console to communicate with OS/400 in another12* partition.13*14* This program is free software; you can redistribute it and/or15* modify it under the terms of the GNU General Public License as16* published by the Free Software Foundation; either version 2 of the17* License, or (at your option) anyu later version.18*19* This program is distributed in the hope that it will be useful, but20* WITHOUT ANY WARRANTY; without even the implied warranty of21* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU22* General Public License for more details.23*24* You should have received a copy of the GNU General Public License25* along with this program; if not, write to the Free Software Foundation,26* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA27*28*/29#include <linux/module.h>30#include <linux/kernel.h>31#include <linux/slab.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/interrupt.h>40#include <linux/completion.h>4142#include <asm/system.h>43#include <asm/uaccess.h>44#include <asm/prom.h>45#include <asm/firmware.h>46#include <asm/iseries/hv_types.h>47#include <asm/iseries/hv_lp_event.h>48#include <asm/iseries/hv_lp_config.h>49#include <asm/iseries/mf.h>50#include <asm/iseries/vio.h>5152/* Status of the path to each other partition in the system.53* This is overkill, since we will only ever establish connections54* to our hosting partition and the primary partition on the system.55* But this allows for other support in the future.56*/57static struct viopathStatus {58int isOpen; /* Did we open the path? */59int isActive; /* Do we have a mon msg outstanding */60int users[VIO_MAX_SUBTYPES];61HvLpInstanceId mSourceInst;62HvLpInstanceId mTargetInst;63int numberAllocated;64} viopathStatus[HVMAXARCHITECTEDLPS];6566static DEFINE_SPINLOCK(statuslock);6768/*69* For each kind of event we allocate a buffer that is70* guaranteed not to cross a page boundary71*/72static unsigned char event_buffer[VIO_MAX_SUBTYPES * 256]73__attribute__((__aligned__(4096)));74static atomic_t event_buffer_available[VIO_MAX_SUBTYPES];75static int event_buffer_initialised;7677static void handleMonitorEvent(struct HvLpEvent *event);7879/*80* We use this structure to handle asynchronous responses. The caller81* blocks on the semaphore and the handler posts the semaphore. However,82* if system_state is not SYSTEM_RUNNING, then wait_atomic is used ...83*/84struct alloc_parms {85struct completion done;86int number;87atomic_t wait_atomic;88int used_wait_atomic;89};9091/* Put a sequence number in each mon msg. The value is not92* important. Start at something other than 0 just for93* readability. wrapping this is ok.94*/95static u8 viomonseq = 22;9697/* Our hosting logical partition. We get this at startup98* time, and different modules access this variable directly.99*/100HvLpIndex viopath_hostLp = HvLpIndexInvalid;101EXPORT_SYMBOL(viopath_hostLp);102HvLpIndex viopath_ourLp = HvLpIndexInvalid;103EXPORT_SYMBOL(viopath_ourLp);104105/* For each kind of incoming event we set a pointer to a106* routine to call.107*/108static vio_event_handler_t *vio_handler[VIO_MAX_SUBTYPES];109110#define VIOPATH_KERN_WARN KERN_WARNING "viopath: "111#define VIOPATH_KERN_INFO KERN_INFO "viopath: "112113static int proc_viopath_show(struct seq_file *m, void *v)114{115char *buf;116u16 vlanMap;117dma_addr_t handle;118HvLpEvent_Rc hvrc;119DECLARE_COMPLETION_ONSTACK(done);120struct device_node *node;121const char *sysid;122123buf = kzalloc(HW_PAGE_SIZE, GFP_KERNEL);124if (!buf)125return 0;126127handle = iseries_hv_map(buf, HW_PAGE_SIZE, DMA_FROM_DEVICE);128129hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,130HvLpEvent_Type_VirtualIo,131viomajorsubtype_config | vioconfigget,132HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck,133viopath_sourceinst(viopath_hostLp),134viopath_targetinst(viopath_hostLp),135(u64)(unsigned long)&done, VIOVERSION << 16,136((u64)handle) << 32, HW_PAGE_SIZE, 0, 0);137138if (hvrc != HvLpEvent_Rc_Good)139printk(VIOPATH_KERN_WARN "hv error on op %d\n", (int)hvrc);140141wait_for_completion(&done);142143vlanMap = HvLpConfig_getVirtualLanIndexMap();144145buf[HW_PAGE_SIZE-1] = '\0';146seq_printf(m, "%s", buf);147148iseries_hv_unmap(handle, HW_PAGE_SIZE, DMA_FROM_DEVICE);149kfree(buf);150151seq_printf(m, "AVAILABLE_VETH=%x\n", vlanMap);152153node = of_find_node_by_path("/");154sysid = NULL;155if (node != NULL)156sysid = of_get_property(node, "system-id", NULL);157158if (sysid == NULL)159seq_printf(m, "SRLNBR=<UNKNOWN>\n");160else161/* Skip "IBM," on front of serial number, see dt.c */162seq_printf(m, "SRLNBR=%s\n", sysid + 4);163164of_node_put(node);165166return 0;167}168169static int proc_viopath_open(struct inode *inode, struct file *file)170{171return single_open(file, proc_viopath_show, NULL);172}173174static const struct file_operations proc_viopath_operations = {175.open = proc_viopath_open,176.read = seq_read,177.llseek = seq_lseek,178.release = single_release,179};180181static int __init vio_proc_init(void)182{183if (!firmware_has_feature(FW_FEATURE_ISERIES))184return 0;185186proc_create("iSeries/config", 0, NULL, &proc_viopath_operations);187return 0;188}189__initcall(vio_proc_init);190191/* See if a given LP is active. Allow for invalid lps to be passed in192* and just return invalid193*/194int viopath_isactive(HvLpIndex lp)195{196if (lp == HvLpIndexInvalid)197return 0;198if (lp < HVMAXARCHITECTEDLPS)199return viopathStatus[lp].isActive;200else201return 0;202}203EXPORT_SYMBOL(viopath_isactive);204205/*206* We cache the source and target instance ids for each207* partition.208*/209HvLpInstanceId viopath_sourceinst(HvLpIndex lp)210{211return viopathStatus[lp].mSourceInst;212}213EXPORT_SYMBOL(viopath_sourceinst);214215HvLpInstanceId viopath_targetinst(HvLpIndex lp)216{217return viopathStatus[lp].mTargetInst;218}219EXPORT_SYMBOL(viopath_targetinst);220221/*222* Send a monitor message. This is a message with the acknowledge223* bit on that the other side will NOT explicitly acknowledge. When224* the other side goes down, the hypervisor will acknowledge any225* outstanding messages....so we will know when the other side dies.226*/227static void sendMonMsg(HvLpIndex remoteLp)228{229HvLpEvent_Rc hvrc;230231viopathStatus[remoteLp].mSourceInst =232HvCallEvent_getSourceLpInstanceId(remoteLp,233HvLpEvent_Type_VirtualIo);234viopathStatus[remoteLp].mTargetInst =235HvCallEvent_getTargetLpInstanceId(remoteLp,236HvLpEvent_Type_VirtualIo);237238/*239* Deliberately ignore the return code here. if we call this240* more than once, we don't care.241*/242vio_setHandler(viomajorsubtype_monitor, handleMonitorEvent);243244hvrc = HvCallEvent_signalLpEventFast(remoteLp, HvLpEvent_Type_VirtualIo,245viomajorsubtype_monitor, HvLpEvent_AckInd_DoAck,246HvLpEvent_AckType_DeferredAck,247viopathStatus[remoteLp].mSourceInst,248viopathStatus[remoteLp].mTargetInst,249viomonseq++, 0, 0, 0, 0, 0);250251if (hvrc == HvLpEvent_Rc_Good)252viopathStatus[remoteLp].isActive = 1;253else {254printk(VIOPATH_KERN_WARN "could not connect to partition %d\n",255remoteLp);256viopathStatus[remoteLp].isActive = 0;257}258}259260static void handleMonitorEvent(struct HvLpEvent *event)261{262HvLpIndex remoteLp;263int i;264265/*266* This handler is _also_ called as part of the loop267* at the end of this routine, so it must be able to268* ignore NULL events...269*/270if (!event)271return;272273/*274* First see if this is just a normal monitor message from the275* other partition276*/277if (hvlpevent_is_int(event)) {278remoteLp = event->xSourceLp;279if (!viopathStatus[remoteLp].isActive)280sendMonMsg(remoteLp);281return;282}283284/*285* This path is for an acknowledgement; the other partition286* died287*/288remoteLp = event->xTargetLp;289if ((event->xSourceInstanceId != viopathStatus[remoteLp].mSourceInst) ||290(event->xTargetInstanceId != viopathStatus[remoteLp].mTargetInst)) {291printk(VIOPATH_KERN_WARN "ignoring ack....mismatched instances\n");292return;293}294295printk(VIOPATH_KERN_WARN "partition %d ended\n", remoteLp);296297viopathStatus[remoteLp].isActive = 0;298299/*300* For each active handler, pass them a NULL301* message to indicate that the other partition302* died303*/304for (i = 0; i < VIO_MAX_SUBTYPES; i++) {305if (vio_handler[i] != NULL)306(*vio_handler[i])(NULL);307}308}309310int vio_setHandler(int subtype, vio_event_handler_t *beh)311{312subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT;313if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES))314return -EINVAL;315if (vio_handler[subtype] != NULL)316return -EBUSY;317vio_handler[subtype] = beh;318return 0;319}320EXPORT_SYMBOL(vio_setHandler);321322int vio_clearHandler(int subtype)323{324subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT;325if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES))326return -EINVAL;327if (vio_handler[subtype] == NULL)328return -EAGAIN;329vio_handler[subtype] = NULL;330return 0;331}332EXPORT_SYMBOL(vio_clearHandler);333334static void handleConfig(struct HvLpEvent *event)335{336if (!event)337return;338if (hvlpevent_is_int(event)) {339printk(VIOPATH_KERN_WARN340"unexpected config request from partition %d",341event->xSourceLp);342343if (hvlpevent_need_ack(event)) {344event->xRc = HvLpEvent_Rc_InvalidSubtype;345HvCallEvent_ackLpEvent(event);346}347return;348}349350complete((struct completion *)event->xCorrelationToken);351}352353/*354* Initialization of the hosting partition355*/356void vio_set_hostlp(void)357{358/*359* If this has already been set then we DON'T want to either change360* it or re-register the proc file system361*/362if (viopath_hostLp != HvLpIndexInvalid)363return;364365/*366* Figure out our hosting partition. This isn't allowed to change367* while we're active368*/369viopath_ourLp = HvLpConfig_getLpIndex();370viopath_hostLp = HvLpConfig_getHostingLpIndex(viopath_ourLp);371372if (viopath_hostLp != HvLpIndexInvalid)373vio_setHandler(viomajorsubtype_config, handleConfig);374}375EXPORT_SYMBOL(vio_set_hostlp);376377static void vio_handleEvent(struct HvLpEvent *event)378{379HvLpIndex remoteLp;380int subtype = (event->xSubtype & VIOMAJOR_SUBTYPE_MASK)381>> VIOMAJOR_SUBTYPE_SHIFT;382383if (hvlpevent_is_int(event)) {384remoteLp = event->xSourceLp;385/*386* The isActive is checked because if the hosting partition387* went down and came back up it would not be active but it388* would have different source and target instances, in which389* case we'd want to reset them. This case really protects390* against an unauthorized active partition sending interrupts391* or acks to this linux partition.392*/393if (viopathStatus[remoteLp].isActive394&& (event->xSourceInstanceId !=395viopathStatus[remoteLp].mTargetInst)) {396printk(VIOPATH_KERN_WARN397"message from invalid partition. "398"int msg rcvd, source inst (%d) doesn't match (%d)\n",399viopathStatus[remoteLp].mTargetInst,400event->xSourceInstanceId);401return;402}403404if (viopathStatus[remoteLp].isActive405&& (event->xTargetInstanceId !=406viopathStatus[remoteLp].mSourceInst)) {407printk(VIOPATH_KERN_WARN408"message from invalid partition. "409"int msg rcvd, target inst (%d) doesn't match (%d)\n",410viopathStatus[remoteLp].mSourceInst,411event->xTargetInstanceId);412return;413}414} else {415remoteLp = event->xTargetLp;416if (event->xSourceInstanceId !=417viopathStatus[remoteLp].mSourceInst) {418printk(VIOPATH_KERN_WARN419"message from invalid partition. "420"ack msg rcvd, source inst (%d) doesn't match (%d)\n",421viopathStatus[remoteLp].mSourceInst,422event->xSourceInstanceId);423return;424}425426if (event->xTargetInstanceId !=427viopathStatus[remoteLp].mTargetInst) {428printk(VIOPATH_KERN_WARN429"message from invalid partition. "430"viopath: ack msg rcvd, target inst (%d) doesn't match (%d)\n",431viopathStatus[remoteLp].mTargetInst,432event->xTargetInstanceId);433return;434}435}436437if (vio_handler[subtype] == NULL) {438printk(VIOPATH_KERN_WARN439"unexpected virtual io event subtype %d from partition %d\n",440event->xSubtype, remoteLp);441/* No handler. Ack if necessary */442if (hvlpevent_is_int(event) && hvlpevent_need_ack(event)) {443event->xRc = HvLpEvent_Rc_InvalidSubtype;444HvCallEvent_ackLpEvent(event);445}446return;447}448449/* This innocuous little line is where all the real work happens */450(*vio_handler[subtype])(event);451}452453static void viopath_donealloc(void *parm, int number)454{455struct alloc_parms *parmsp = parm;456457parmsp->number = number;458if (parmsp->used_wait_atomic)459atomic_set(&parmsp->wait_atomic, 0);460else461complete(&parmsp->done);462}463464static int allocateEvents(HvLpIndex remoteLp, int numEvents)465{466struct alloc_parms parms;467468if (system_state != SYSTEM_RUNNING) {469parms.used_wait_atomic = 1;470atomic_set(&parms.wait_atomic, 1);471} else {472parms.used_wait_atomic = 0;473init_completion(&parms.done);474}475mf_allocate_lp_events(remoteLp, HvLpEvent_Type_VirtualIo, 250, /* It would be nice to put a real number here! */476numEvents, &viopath_donealloc, &parms);477if (system_state != SYSTEM_RUNNING) {478while (atomic_read(&parms.wait_atomic))479mb();480} else481wait_for_completion(&parms.done);482return parms.number;483}484485int viopath_open(HvLpIndex remoteLp, int subtype, int numReq)486{487int i;488unsigned long flags;489int tempNumAllocated;490491if ((remoteLp >= HVMAXARCHITECTEDLPS) || (remoteLp == HvLpIndexInvalid))492return -EINVAL;493494subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT;495if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES))496return -EINVAL;497498spin_lock_irqsave(&statuslock, flags);499500if (!event_buffer_initialised) {501for (i = 0; i < VIO_MAX_SUBTYPES; i++)502atomic_set(&event_buffer_available[i], 1);503event_buffer_initialised = 1;504}505506viopathStatus[remoteLp].users[subtype]++;507508if (!viopathStatus[remoteLp].isOpen) {509viopathStatus[remoteLp].isOpen = 1;510HvCallEvent_openLpEventPath(remoteLp, HvLpEvent_Type_VirtualIo);511512/*513* Don't hold the spinlock during an operation that514* can sleep.515*/516spin_unlock_irqrestore(&statuslock, flags);517tempNumAllocated = allocateEvents(remoteLp, 1);518spin_lock_irqsave(&statuslock, flags);519520viopathStatus[remoteLp].numberAllocated += tempNumAllocated;521522if (viopathStatus[remoteLp].numberAllocated == 0) {523HvCallEvent_closeLpEventPath(remoteLp,524HvLpEvent_Type_VirtualIo);525526spin_unlock_irqrestore(&statuslock, flags);527return -ENOMEM;528}529530viopathStatus[remoteLp].mSourceInst =531HvCallEvent_getSourceLpInstanceId(remoteLp,532HvLpEvent_Type_VirtualIo);533viopathStatus[remoteLp].mTargetInst =534HvCallEvent_getTargetLpInstanceId(remoteLp,535HvLpEvent_Type_VirtualIo);536HvLpEvent_registerHandler(HvLpEvent_Type_VirtualIo,537&vio_handleEvent);538sendMonMsg(remoteLp);539printk(VIOPATH_KERN_INFO "opening connection to partition %d, "540"setting sinst %d, tinst %d\n",541remoteLp, viopathStatus[remoteLp].mSourceInst,542viopathStatus[remoteLp].mTargetInst);543}544545spin_unlock_irqrestore(&statuslock, flags);546tempNumAllocated = allocateEvents(remoteLp, numReq);547spin_lock_irqsave(&statuslock, flags);548viopathStatus[remoteLp].numberAllocated += tempNumAllocated;549spin_unlock_irqrestore(&statuslock, flags);550551return 0;552}553EXPORT_SYMBOL(viopath_open);554555int viopath_close(HvLpIndex remoteLp, int subtype, int numReq)556{557unsigned long flags;558int i;559int numOpen;560struct alloc_parms parms;561562if ((remoteLp >= HVMAXARCHITECTEDLPS) || (remoteLp == HvLpIndexInvalid))563return -EINVAL;564565subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT;566if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES))567return -EINVAL;568569spin_lock_irqsave(&statuslock, flags);570/*571* If the viopath_close somehow gets called before a572* viopath_open it could decrement to -1 which is a non573* recoverable state so we'll prevent this from574* happening.575*/576if (viopathStatus[remoteLp].users[subtype] > 0)577viopathStatus[remoteLp].users[subtype]--;578579spin_unlock_irqrestore(&statuslock, flags);580581parms.used_wait_atomic = 0;582init_completion(&parms.done);583mf_deallocate_lp_events(remoteLp, HvLpEvent_Type_VirtualIo,584numReq, &viopath_donealloc, &parms);585wait_for_completion(&parms.done);586587spin_lock_irqsave(&statuslock, flags);588for (i = 0, numOpen = 0; i < VIO_MAX_SUBTYPES; i++)589numOpen += viopathStatus[remoteLp].users[i];590591if ((viopathStatus[remoteLp].isOpen) && (numOpen == 0)) {592printk(VIOPATH_KERN_INFO "closing connection to partition %d\n",593remoteLp);594595HvCallEvent_closeLpEventPath(remoteLp,596HvLpEvent_Type_VirtualIo);597viopathStatus[remoteLp].isOpen = 0;598viopathStatus[remoteLp].isActive = 0;599600for (i = 0; i < VIO_MAX_SUBTYPES; i++)601atomic_set(&event_buffer_available[i], 0);602event_buffer_initialised = 0;603}604spin_unlock_irqrestore(&statuslock, flags);605return 0;606}607EXPORT_SYMBOL(viopath_close);608609void *vio_get_event_buffer(int subtype)610{611subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT;612if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES))613return NULL;614615if (atomic_dec_if_positive(&event_buffer_available[subtype]) == 0)616return &event_buffer[subtype * 256];617else618return NULL;619}620EXPORT_SYMBOL(vio_get_event_buffer);621622void vio_free_event_buffer(int subtype, void *buffer)623{624subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT;625if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES)) {626printk(VIOPATH_KERN_WARN627"unexpected subtype %d freeing event buffer\n", subtype);628return;629}630631if (atomic_read(&event_buffer_available[subtype]) != 0) {632printk(VIOPATH_KERN_WARN633"freeing unallocated event buffer, subtype %d\n",634subtype);635return;636}637638if (buffer != &event_buffer[subtype * 256]) {639printk(VIOPATH_KERN_WARN640"freeing invalid event buffer, subtype %d\n", subtype);641}642643atomic_set(&event_buffer_available[subtype], 1);644}645EXPORT_SYMBOL(vio_free_event_buffer);646647static const struct vio_error_entry vio_no_error =648{ 0, 0, "Non-VIO Error" };649static const struct vio_error_entry vio_unknown_error =650{ 0, EIO, "Unknown Error" };651652static const struct vio_error_entry vio_default_errors[] = {653{0x0001, EIO, "No Connection"},654{0x0002, EIO, "No Receiver"},655{0x0003, EIO, "No Buffer Available"},656{0x0004, EBADRQC, "Invalid Message Type"},657{0x0000, 0, NULL},658};659660const struct vio_error_entry *vio_lookup_rc(661const struct vio_error_entry *local_table, u16 rc)662{663const struct vio_error_entry *cur;664665if (!rc)666return &vio_no_error;667if (local_table)668for (cur = local_table; cur->rc; ++cur)669if (cur->rc == rc)670return cur;671for (cur = vio_default_errors; cur->rc; ++cur)672if (cur->rc == rc)673return cur;674return &vio_unknown_error;675}676EXPORT_SYMBOL(vio_lookup_rc);677678679