📄 viopath.c
字号:
/* -*- linux-c -*- * arch/ppc64/viopath.c * * iSeries Virtual I/O Message Path code * * Authors: Dave Boutcher <boutcher@us.ibm.com> * Ryan Arnold <ryanarn@us.ibm.com> * Colin Devilbiss <devilbis@us.ibm.com> * * (C) Copyright 2000 IBM Corporation * * This code is used by the iSeries virtual disk, cd, * tape, and console to communicate with OS/400 in another * partition. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) anyu later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */#include <linux/config.h>#include <asm/uaccess.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/errno.h>#include <linux/vmalloc.h>#include <linux/string.h>#include <linux/proc_fs.h>#include <linux/pci.h>#include <linux/wait.h>#include <asm/iSeries/LparData.h>#include <asm/iSeries/HvLpEvent.h>#include <asm/iSeries/HvLpConfig.h>#include <asm/iSeries/HvCallCfg.h>#include <asm/iSeries/mf.h>#include <asm/iSeries/iSeries_proc.h>#include <asm/iSeries/vio.h>EXPORT_SYMBOL(viopath_hostLp);EXPORT_SYMBOL(viopath_ourLp);EXPORT_SYMBOL(vio_set_hostlp);EXPORT_SYMBOL(vio_lookup_rc);EXPORT_SYMBOL(viopath_open);EXPORT_SYMBOL(viopath_close);EXPORT_SYMBOL(viopath_isactive);EXPORT_SYMBOL(viopath_sourceinst);EXPORT_SYMBOL(viopath_targetinst);EXPORT_SYMBOL(vio_setHandler);EXPORT_SYMBOL(vio_clearHandler);EXPORT_SYMBOL(vio_get_event_buffer);EXPORT_SYMBOL(vio_free_event_buffer);extern struct pci_dev *iSeries_vio_dev;/* Status of the path to each other partition in the system. * This is overkill, since we will only ever establish connections * to our hosting partition and the primary partition on the system. * But this allows for other support in the future. */static struct viopathStatus { int isOpen:1; /* Did we open the path? */ int isActive:1; /* Do we have a mon msg outstanding */ int users[VIO_MAX_SUBTYPES]; HvLpInstanceId mSourceInst; HvLpInstanceId mTargetInst; int numberAllocated;} viopathStatus[HVMAXARCHITECTEDLPS];static spinlock_t statuslock = SPIN_LOCK_UNLOCKED;/* * For each kind of event we allocate a buffer that is * guaranteed not to cross a page boundary */static void *event_buffer[VIO_MAX_SUBTYPES] = { };static atomic_t event_buffer_available[VIO_MAX_SUBTYPES] = { };static void handleMonitorEvent(struct HvLpEvent *event);/* We use this structure to handle asynchronous responses. The caller * blocks on the semaphore and the handler posts the semaphore. */struct doneAllocParms_t { struct semaphore *sem; int number;};/* Put a sequence number in each mon msg. The value is not * important. Start at something other than 0 just for * readability. wrapping this is ok. */static u8 viomonseq = 22;/* Our hosting logical partition. We get this at startup * time, and different modules access this variable directly. */HvLpIndex viopath_hostLp = 0xff; /* HvLpIndexInvalid */HvLpIndex viopath_ourLp = 0xff;/* For each kind of incoming event we set a pointer to a * routine to call. */static vio_event_handler_t *vio_handler[VIO_MAX_SUBTYPES];static unsigned char e2a(unsigned char x){ switch (x) { case 0xF0: return '0'; case 0xF1: return '1'; case 0xF2: return '2'; case 0xF3: return '3'; case 0xF4: return '4'; case 0xF5: return '5'; case 0xF6: return '6'; case 0xF7: return '7'; case 0xF8: return '8'; case 0xF9: return '9'; case 0xC1: return 'A'; case 0xC2: return 'B'; case 0xC3: return 'C'; case 0xC4: return 'D'; case 0xC5: return 'E'; case 0xC6: return 'F'; case 0xC7: return 'G'; case 0xC8: return 'H'; case 0xC9: return 'I'; case 0xD1: return 'J'; case 0xD2: return 'K'; case 0xD3: return 'L'; case 0xD4: return 'M'; case 0xD5: return 'N'; case 0xD6: return 'O'; case 0xD7: return 'P'; case 0xD8: return 'Q'; case 0xD9: return 'R'; case 0xE2: return 'S'; case 0xE3: return 'T'; case 0xE4: return 'U'; case 0xE5: return 'V'; case 0xE6: return 'W'; case 0xE7: return 'X'; case 0xE8: return 'Y'; case 0xE9: return 'Z'; } return ' ';}/* Handle reads from the proc file system */static int proc_read(char *buf, char **start, off_t offset, int blen, int *eof, void *data){ HvLpEvent_Rc hvrc; DECLARE_MUTEX_LOCKED(Semaphore); dma_addr_t dmaa = pci_map_single(iSeries_vio_dev, buf, PAGE_SIZE, PCI_DMA_FROMDEVICE); int len = PAGE_SIZE; if (len > blen) len = blen; memset(buf, 0x00, len); hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, HvLpEvent_Type_VirtualIo, viomajorsubtype_config | vioconfigget, HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, viopath_sourceinst (viopath_hostLp), viopath_targetinst (viopath_hostLp), (u64) (unsigned long) &Semaphore, VIOVERSION << 16, ((u64) dmaa) << 32, len, 0, 0); if (hvrc != HvLpEvent_Rc_Good) { printk("viopath hv error on op %d\n", (int) hvrc); } down(&Semaphore); pci_unmap_single(iSeries_vio_dev, dmaa, PAGE_SIZE, PCI_DMA_FROMDEVICE); sprintf(buf + strlen(buf), "SRLNBR="); buf[strlen(buf)] = e2a(xItExtVpdPanel.mfgID[2]); buf[strlen(buf)] = e2a(xItExtVpdPanel.mfgID[3]); buf[strlen(buf)] = e2a(xItExtVpdPanel.systemSerial[1]); buf[strlen(buf)] = e2a(xItExtVpdPanel.systemSerial[2]); buf[strlen(buf)] = e2a(xItExtVpdPanel.systemSerial[3]); buf[strlen(buf)] = e2a(xItExtVpdPanel.systemSerial[4]); buf[strlen(buf)] = e2a(xItExtVpdPanel.systemSerial[5]); buf[strlen(buf)] = '\n'; *eof = 1; return strlen(buf);}/* Handle writes to our proc file system */static int proc_write(struct file *file, const char *buffer, unsigned long count, void *data){ /* Doesn't do anything today!!! */ return count;}/* setup our proc file system entries */static void vio_proc_init(struct proc_dir_entry *iSeries_proc){ struct proc_dir_entry *ent; ent = create_proc_entry("config", S_IFREG | S_IRUSR, iSeries_proc); if (!ent) return; ent->nlink = 1; ent->data = NULL; ent->read_proc = proc_read; ent->write_proc = proc_write;}/* See if a given LP is active. Allow for invalid lps to be passed in * and just return invalid */int viopath_isactive(HvLpIndex lp){ if (lp == HvLpIndexInvalid) return 0; if (lp < HVMAXARCHITECTEDLPS) return viopathStatus[lp].isActive; else return 0;}/* We cache the source and target instance ids for each * partition. */HvLpInstanceId viopath_sourceinst(HvLpIndex lp){ return viopathStatus[lp].mSourceInst;}HvLpInstanceId viopath_targetinst(HvLpIndex lp){ return viopathStatus[lp].mTargetInst;}/* Send a monitor message. This is a message with the acknowledge * bit on that the other side will NOT explicitly acknowledge. When * the other side goes down, the hypervisor will acknowledge any * outstanding messages....so we will know when the other side dies. */static void sendMonMsg(HvLpIndex remoteLp){ HvLpEvent_Rc hvrc; viopathStatus[remoteLp].mSourceInst = HvCallEvent_getSourceLpInstanceId(remoteLp, HvLpEvent_Type_VirtualIo); viopathStatus[remoteLp].mTargetInst = HvCallEvent_getTargetLpInstanceId(remoteLp, HvLpEvent_Type_VirtualIo); /* Deliberately ignore the return code here. if we call this * more than once, we don't care. */ vio_setHandler(viomajorsubtype_monitor, handleMonitorEvent); hvrc = HvCallEvent_signalLpEventFast(remoteLp, HvLpEvent_Type_VirtualIo, viomajorsubtype_monitor, HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_DeferredAck, viopathStatus[remoteLp]. mSourceInst, viopathStatus[remoteLp]. mTargetInst, viomonseq++, 0, 0, 0, 0, 0); if (hvrc == HvLpEvent_Rc_Good) { viopathStatus[remoteLp].isActive = 1; } else { printk(KERN_WARNING_VIO "could not connect to partition %d\n", remoteLp); viopathStatus[remoteLp].isActive = 0; }}static void handleMonitorEvent(struct HvLpEvent *event){ HvLpIndex remoteLp; int i; /* This handler is _also_ called as part of the loop * at the end of this routine, so it must be able to * ignore NULL events... */ if (!event) return; /* First see if this is just a normal monitor message from the * other partition */ if (event->xFlags.xFunction == HvLpEvent_Function_Int) { remoteLp = event->xSourceLp; if (!viopathStatus[remoteLp].isActive) sendMonMsg(remoteLp); return; } /* This path is for an acknowledgement; the other partition * died */ remoteLp = event->xTargetLp; if ((event->xSourceInstanceId != viopathStatus[remoteLp].mSourceInst) || (event->xTargetInstanceId != viopathStatus[remoteLp].mTargetInst)) { printk(KERN_WARNING_VIO "ignoring ack....mismatched instances\n"); return; } printk(KERN_WARNING_VIO "partition %d ended\n", remoteLp); viopathStatus[remoteLp].isActive = 0; /* For each active handler, pass them a NULL * message to indicate that the other partition * died */ for (i = 0; i < VIO_MAX_SUBTYPES; i++) { if (vio_handler[i] != NULL) (*vio_handler[i]) (NULL); }}int vio_setHandler(int subtype, vio_event_handler_t * beh){ subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT; if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES)) return -EINVAL; if (vio_handler[subtype] != NULL) return -EBUSY; vio_handler[subtype] = beh; return 0;}int vio_clearHandler(int subtype){ subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT; if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES)) return -EINVAL;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -