diff options
Diffstat (limited to 'arch/arm/mvp/commkm')
33 files changed, 6790 insertions, 0 deletions
diff --git a/arch/arm/mvp/commkm/COPYING b/arch/arm/mvp/commkm/COPYING new file mode 100644 index 0000000..10828e0 --- /dev/null +++ b/arch/arm/mvp/commkm/COPYING @@ -0,0 +1,341 @@ + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + 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) any 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/arch/arm/mvp/commkm/Kbuild b/arch/arm/mvp/commkm/Kbuild new file mode 100644 index 0000000..de43a5c --- /dev/null +++ b/arch/arm/mvp/commkm/Kbuild @@ -0,0 +1,9 @@ +# Warning: autogenerated +obj-m := commkm.o +commkm-objs := check_kconfig.o comm_ev_kernel.o comm.o comm_os_linux.o comm_os_mod_linux.o comm_svc.o comm_transp_mvp.o + +ccflags-y += -fno-pic -fno-dwarf2-cfi-asm -march=armv7-a -D__linux__ +ccflags-y += -DCOMM_BUILDING_SERVER +ccflags-y += -mfpu=neon -DIN_MODULE -DGPLED_CODE +ccflags-y += --std=gnu89 -O2 -g2 -ggdb -mapcs -fno-optimize-sibling-calls -mno-sched-prolog +ccflags-$(CONFIG_VMWARE_MVP_DEBUG) += -DMVP_DEBUG diff --git a/arch/arm/mvp/commkm/Makefile b/arch/arm/mvp/commkm/Makefile new file mode 100644 index 0000000..16eb389 --- /dev/null +++ b/arch/arm/mvp/commkm/Makefile @@ -0,0 +1 @@ +# Warning: autogenerated diff --git a/arch/arm/mvp/commkm/check_kconfig.c b/arch/arm/mvp/commkm/check_kconfig.c new file mode 100644 index 0000000..0867d74 --- /dev/null +++ b/arch/arm/mvp/commkm/check_kconfig.c @@ -0,0 +1,91 @@ +/* + * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications + * + * Copyright (C) 2010-2012 VMware, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#line 5 + +/** + * @file + * @brief Check for required kernel configuration + * + * Check to make sure that the kernel options that the MVP hypervisor requires + * have been enabled in the kernel that this kernel module is being built + * against. + */ +#include <linux/version.h> + +/* + * Minimum kernel version + * - network namespace support is only really functional starting in 2.6.29 + * - Android Gingerbread requires 2.6.35 + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) +#error "MVP requires a host kernel newer than 2.6.35" +#endif + +/* module loading ability */ +#ifndef CONFIG_MODULES +#error "MVP requires kernel loadable module support be enabled (CONFIG_MODULES)" +#endif +#ifndef CONFIG_MODULE_UNLOAD +#error "MVP requires kernel module unload support be enabled (CONFIG_MODULE_UNLOAD)" +#endif + +/* sysfs */ +#ifndef CONFIG_SYSFS +#error "MVP requires sysfs support (CONFIG_SYSFS)" +#endif + +/* network traffic isolation */ +#ifndef CONFIG_NAMESPACES +#error "MVP networking support requires namespace support (CONFIG_NAMESPACES)" +#endif +#ifndef CONFIG_NET_NS +#error "MVP networking support requires Network Namespace support to be enabled (CONFIG_NET_NS)" +#endif + +/* TCP/IP networking */ +#ifndef CONFIG_INET +#error "MVP networking requires IPv4 support (CONFIG_INET)" +#endif +#ifndef CONFIG_IPV6 +#error "MVP networking requires IPv6 support (CONFIG_IPV6)" +#endif + +/* VPN support */ +#if !defined(CONFIG_TUN) && !defined(CONFIG_TUN_MODULE) +#error "MVP VPN support requires TUN device support (CONFIG_TUN)" +#endif + +#if !defined(CONFIG_NETFILTER) && !defined(PVTCP_DISABLE_NETFILTER) +#error "MVP networking support requires netfilter support (CONFIG_NETFILTER)" +#endif + +/* Force /proc/config.gz support for eng/userdebug builds */ +#ifdef MVP_DEBUG +#if !defined(CONFIG_IKCONFIG) || !defined(CONFIG_IKCONFIG_PROC) +#error "MVP kernel /proc/config.gz support required for debuggability (CONFIG_IKCONFIG_PROC)" +#endif +#endif + +/* Sanity check we're only dealing with the memory hotplug + migrate and/or + * compaction combo */ +#ifdef CONFIG_MIGRATION +#if defined(CONFIG_NUMA) || defined(CONFIG_CPUSETS) || defined(CONFIG_MEMORY_FAILURE) +#error "MVP not tested with migration features other than CONFIG_MEMORY_HOTPLUG and CONFIG_COMPACTION" +#endif +#endif diff --git a/arch/arm/mvp/commkm/comm.c b/arch/arm/mvp/commkm/comm.c new file mode 100644 index 0000000..8fd591c --- /dev/null +++ b/arch/arm/mvp/commkm/comm.c @@ -0,0 +1,1457 @@ +/* + * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications + * + * Copyright (C) 2010-2012 VMware, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#line 5 + +/** + * @file + * + * @brief Communication functions based on transport functionality. + */ + +#include "comm.h" +#include "comm_transp_impl.h" + + +/* Constant and macro definitions */ + +#if defined(COMM_INSTRUMENT) +static CommOSAtomic commMaxCoalesceSize; +static CommOSAtomic commPacketsReceived; +static CommOSAtomic commCommittedPacketsReceived; +static CommOSAtomic commOpCalls; +#endif + +#define COMM_DISPATCH_EXTRA_WRITER_WAKEUP 1 + +#define COMM_CHANNEL_MAX_CAPACITY 2048 +#define COMM_CHANNEL_FREE 0x0 +#define COMM_CHANNEL_INITIALIZED 0x1 +#define COMM_CHANNEL_OPENED 0x2 +#define COMM_CHANNEL_ACTIVE 0x4 +#define COMM_CHANNEL_ZOMBIE 0x8 + +#define CommIsFree(chan) \ + ((chan)->lifecycleState == COMM_CHANNEL_FREE) +#define CommIsInitialized(chan) \ + ((chan)->lifecycleState == COMM_CHANNEL_INITIALIZED) +#define CommIsOpened(chan) \ + ((chan)->lifecycleState == COMM_CHANNEL_OPENED) +#define CommIsActive(chan) \ + ((chan)->lifecycleState == COMM_CHANNEL_ACTIVE) +#define CommIsZombie(chan) \ + ((chan)->lifecycleState == COMM_CHANNEL_ZOMBIE) + +#define CommSetFree(chan) \ + SetLifecycleState(chan, COMM_CHANNEL_FREE) +#define CommSetInitialized(chan) \ + SetLifecycleState(chan, COMM_CHANNEL_INITIALIZED) +#define CommSetOpened(chan) \ + SetLifecycleState(chan, COMM_CHANNEL_OPENED) +#define CommSetActive(chan) \ + SetLifecycleState(chan, COMM_CHANNEL_ACTIVE) +#define CommSetZombie(chan) \ + SetLifecycleState(chan, COMM_CHANNEL_ZOMBIE) + +#define CommGlobalLock() CommOS_SpinLock(&commGlobalLock) +#define CommGlobalUnlock() CommOS_SpinUnlock(&commGlobalLock) +#define CommGlobalLockBH() CommOS_SpinLockBH(&commGlobalLock) +#define CommGlobalUnlockBH() CommOS_SpinUnlockBH(&commGlobalLock) + +#define DispatchTrylock(chan) CommOS_MutexTrylock(&(chan)->dispatchMutex) +#define DispatchUnlock(chan) CommOS_MutexUnlock(&(chan)->dispatchMutex) + +#define WriteLock(chan) CommOS_MutexLock(&(chan)->writeMutex) +#define WriteTrylock(chan) CommOS_MutexTrylock(&(chan)->writeMutex) +#define WriteUnlock(chan) CommOS_MutexUnlock(&(chan)->writeMutex) + +#define StateLock(chan) CommOS_MutexLock(&(chan)->stateMutex) +#define StateTrylock(chan) CommOS_MutexTrylock(&(chan)->stateMutex) +#define StateUnlock(chan) CommOS_MutexUnlock(&(chan)->stateMutex) + +#define CommHoldInit(chan) CommOS_WriteAtomic(&(chan)->holds, 0) +#define CommHold(chan) CommOS_AddReturnAtomic(&(chan)->holds, 1) +#define CommRelease(chan) CommOS_SubReturnAtomic(&(chan)->holds, 1) +#define CommIsHeld(chan) (CommOS_ReadAtomic(&(chan)->holds) > 0) + +#define PacketLenOverLimit(chan, len) \ + (((len) - sizeof (CommPacket)) > ((chan)->transpArgs.capacity / 4)) + + +/* + * Data structure describing the offload <-> paravirtualized module + * communication channel. + */ + +struct CommChannelPriv { + CommOSAtomic holds; // Active readers and writers + CommTranspInitArgs transpArgs; // Transport initialization arguments + CommTransp transp; // Transport handle + CommOSMutex dispatchMutex; // Dispatch mutex + CommOSMutex writeMutex; // Non-BH write mutex + CommOSMutex stateMutex; // Upper-layer state mutex + CommOSWaitQueue availableWaitQ; // Available write space wait data + unsigned int desiredWriteSpace; // Size of write space needed + const CommImpl *impl; // Implementation + unsigned int implNmbOps; // Number of implementation operations + unsigned int lifecycleState; // Lifecycle state + void *state; // Upper layer-specific state +}; + + +static volatile int running; // Initialized and running. +static CommOSWaitQueue exitWaitQ; // Exit wait queue. +static CommOSSpinlock commGlobalLock; // Global lock. + + +/* Communication channel slots. */ + +static unsigned int commChannelCapacity; // Maximum number of channels. +static unsigned int commChannelSize; // Current size of channel array. +static unsigned int commChannelAllocated; // Nmb. entries currently in use. +static struct CommChannelPriv *commChannels; // Allocated channel array. + + +/** + * @brief Callback function called when the other side created a transport + * handle to which we need to potentially attach. + * @param[in,out] transpArgs arguments used when shared memory area was created. + * @param probeData our callback data, an implementation block. + * @return 0 if successful, -1 otherwise. + * @sideeffects May allocate a channel. + */ + +static int +DefaultTranspListener(CommTranspInitArgs *transpArgs, + void *probeData) +{ + int rc = -1; + const int inBH = 1; + const CommImpl *impl; + + if (!transpArgs || !probeData) { + CommOS_Debug(("%s: NULL args [0x%p, 0x%p].\n", + __FUNCTION__, transpArgs, probeData)); + goto out; + } + + impl = probeData; + CommOS_Debug(("%s: Received attach info [%u,%u,%u:%u].\n", + __FUNCTION__, + transpArgs->capacity, transpArgs->type, + transpArgs->id.d32[0], transpArgs->id.d32[1])); + + if (impl->checkArgs(transpArgs)) { + goto out; + } + transpArgs->mode = COMM_TRANSP_INIT_ATTACH; /* Ensure we attach. */ + + /* We recognized it, so don't let others waste any time. Even if we fail. */ + + rc = 0; + if (Comm_Alloc(transpArgs, impl, inBH, NULL)) { + impl->closeNtf(impl->closeNtfData, transpArgs, inBH); + CommOS_Log(("%s: Can't allocate new channel!\n", __FUNCTION__)); + } + +out: + return rc; +} + + +/** + * @brief Sets the lifecycle state of a channel entry + * @param channel channel to update + * @param newState state to update to + */ + +static inline void +SetLifecycleState(CommChannel channel, + unsigned int newState) +{ + + channel->lifecycleState = newState; +} + + +/* Wait conditions: functions returning 1: true, 0: false, < 0: error. */ + +/** + * @brief Wait condition function to check whether module can be unloaded. + * @param arg1 dummy + * @param arg2 dummy + * @return 1 if no channels are currently allocated, 0 if there are + */ + +static int +ExitCondition(void *arg1, + void *arg2) +{ + unsigned int i; + int rc; + + (void)arg1; + (void)arg2; + CommOS_Debug(("%s: running [%d] " + "commChannelAllocated [%u] commChannelSize [%u].\n", + __FUNCTION__, running, commChannelAllocated, commChannelSize)); + rc = !running && (commChannelAllocated == 0); + if (!rc) { + for (i = 0; i < commChannelCapacity; i++) { + CommOS_Debug(("%s: channel[%u] state [0x%x].\n", + __FUNCTION__, i, commChannels[i].lifecycleState)); + } + } + return rc; +} + + +/** + * @brief Wait condition function to check available write space. + * @param arg1 pointer to CommChannel struct + * @param arg2 size argument + * @return 1 if there is enough write space, 0 if not, -ENOMEM if comm down. + */ + +static int +WriteSpaceCondition(void *arg1, + void *arg2) +{ + CommChannel channel = arg1; + + if (!CommIsActive(channel)) { + return -ENOMEM; + } + return channel->desiredWriteSpace < CommTransp_EnqueueSpace(channel->transp); +} + + +/** + * @brief Registers an implementation block used when attaching to channels + * in response to transport attach events. + * @param impl implementation block. + * @return 0 if successful, non-zero otherwise. + */ + +int +Comm_RegisterImpl(const CommImpl *impl) +{ + CommTranspListener listener = { + .probe = DefaultTranspListener, + .probeData = (void *)impl + }; + + return CommTransp_Register(&listener); +} + + +/** + * @brief Unregisters an implementation block used when attaching to channels + * in response to transport attach events. + * @param impl implementation block. + */ + +void +Comm_UnregisterImpl(const CommImpl *impl) +{ + CommTranspListener listener = { + .probe = DefaultTranspListener, + .probeData = (void *)impl + }; + + CommTransp_Unregister(&listener); +} + + +/** + * @brief Allocates and initializes comm global state. Single-threaded use. + * @param maxChannels maximum number of channels. + * @return zero if successful, non-zero otherwise. + */ + +int +Comm_Init(unsigned int maxChannels) +{ + int rc = -1; + unsigned int i; + + if (running || commChannels || + (maxChannels == 0) || (maxChannels > COMM_CHANNEL_MAX_CAPACITY)) { + goto out; + } + +#if defined(COMM_INSTRUMENT) + CommOS_WriteAtomic(&commMaxCoalesceSize, 0); + CommOS_WriteAtomic(&commPacketsReceived, 0); + CommOS_WriteAtomic(&commCommittedPacketsReceived, 0); + CommOS_WriteAtomic(&commOpCalls, 0); +#endif + + CommOS_WaitQueueInit(&exitWaitQ); + CommOS_SpinlockInit(&commGlobalLock); + commChannelCapacity = maxChannels; + commChannelAllocated = 0; + commChannels = CommOS_Kmalloc((sizeof *commChannels) * commChannelCapacity); + if (!commChannels) { + goto out; + } + + memset(commChannels, 0, (sizeof *commChannels) * commChannelCapacity); + for (i = 0; i < commChannelCapacity; i++ ) { + CommChannel channel; + + channel = &commChannels[i]; + CommHoldInit(channel); + channel->transp = NULL; + CommOS_MutexInit(&channel->dispatchMutex); + CommOS_MutexInit(&channel->writeMutex); + CommOS_MutexInit(&channel->stateMutex); + CommOS_WaitQueueInit(&channel->availableWaitQ); + channel->desiredWriteSpace = -1U; + channel->state = NULL; + CommSetFree(channel); + } + + rc = CommTransp_Init(); + if (!rc) { + commChannelSize = 0; + running = 1; + rc = 0; + } else { + CommOS_Kfree(commChannels); + } + +out: + return rc; +} + + +/** + * @brief Initiates and finishes, comm global state deallocations. + * @param timeoutMillis initialization timeout in milliseconds + * @return zero if deallocations done, non-zero if more calls are needed. + */ + +int +Comm_Finish(unsigned long long *timeoutMillis) +{ + int rc; + unsigned int i; + unsigned long long timeout; + + for (i = 0; i < commChannelSize; i++) { + Comm_Zombify(&commChannels[i], 0); + } + + running = 0; + timeout = timeoutMillis ? *timeoutMillis : 0; + /* coverity[var_deref_model] */ + rc = CommOS_Wait(&exitWaitQ, ExitCondition, NULL, NULL, &timeout); + if (rc == 1) { + /* + * Didn't time out, task wasn't interrupted, we can wrap it up.. + */ + + CommTransp_Exit(); + CommOS_Kfree(commChannels); + commChannels = NULL; + commChannelSize = 0; +#if defined(COMM_INSTRUMENT) + CommOS_Log(("%s: commMaxCoalesceSize = %lu.\n", + __FUNCTION__, + CommOS_ReadAtomic(&commMaxCoalesceSize))); + CommOS_Log(("%s: commPacketsReceived = %lu.\n", + __FUNCTION__, + CommOS_ReadAtomic(&commPacketsReceived))); + CommOS_Log(("%s: commCommittedPacketsReceived = %lu.\n", + __FUNCTION__, + CommOS_ReadAtomic(&commCommittedPacketsReceived))); + CommOS_Log(("%s: commOpCalls = %lu.\n", + __FUNCTION__, + CommOS_ReadAtomic)(&commOpCalls))); +#endif + rc = 0; + } else { + rc = -1; + } + return rc; +} + + +/** + * @brief Finds a free entry and initializes it with the information provided. + * May be called from BH. It doesn't call potentially blocking functions. + * + * @note Depending on the choice of shared memory transport (VMCI or MVP QP), + * the 'inBH' distinction is important. VMCI datagrams are received under + * some circumstances in bottom-half context, so 'inBH' should be set. This + * is not a restriction on MVP. + * + * @param transpArgs transport initialization arguments. + * @param impl implementation block. + * @param inBH non-zero if called in bottom half. + * @param[out] newChannel newly allocated channel. + * @return zero if successful, non-zero otherwise. + * @sideeffects Initializes the communications channel with given parameters + */ + +int +Comm_Alloc(const CommTranspInitArgs *transpArgs, + const CommImpl *impl, + int inBH, + CommChannel *newChannel) +{ + unsigned int i; + CommChannel channel = NULL; + int restoreSize = 0; + int modHeld = 0; + int rc = -1; + + if (inBH) { + CommGlobalLock(); + } else { + CommGlobalLockBH(); + } + + if (!running || !transpArgs || !impl) { + goto out; + } + + if (CommOS_ModuleGet(impl->owner)) { + goto out; + } + modHeld = 1; + + for (i = 0; i < commChannelSize; i++) { + /* + * Check if this channel is already allocated. We don't match against + * ANY because those channels are in the process of being opened; after + * that happens, they'll get proper IDs. + */ + + if (!CommIsFree(&commChannels[i]) && + (transpArgs->id.d64 != COMM_TRANSP_ID_64_ANY) && + (transpArgs->id.d64 == commChannels[i].transpArgs.id.d64)) { + goto out; + } + if (!channel && CommIsFree(&commChannels[i])) { + channel = &commChannels[i]; + } + } + if (!channel) { + if (commChannelSize == commChannelCapacity) { + goto out; + } + channel = &commChannels[commChannelSize]; + commChannelSize++; + restoreSize = 1; + } + + if (channel->transp) { /* Inconsistency! */ + if (restoreSize) { + commChannelSize--; + } + goto out; + } + + channel->transpArgs = *transpArgs; + channel->impl = impl; + for (i = 0; impl->operations[i]; i++) { + ; + } + channel->implNmbOps = i; + channel->desiredWriteSpace = -1U; + commChannelAllocated++; + CommSetInitialized(channel); + if (newChannel) { + *newChannel = channel; + } + rc = 0; + CommOS_ScheduleDisp(); + +out: + if (inBH) { + CommGlobalUnlock(); + } else { + CommGlobalUnlockBH(); + } + if (rc && modHeld) { + CommOS_ModulePut(impl->owner); + } + return rc; +} + + +/** + * @brief Zombifies a channel. May fail if channel isn't active. + * @param[in,out] channel channel to zombify. + * @param inBH non-zero if called in bottom half. + * @return zero if channel zombified, non-zero otherwise. + */ + +int +Comm_Zombify(CommChannel channel, + int inBH) +{ + int rc = -1; + + if (!running) { + goto out; + } + if (inBH) { + CommGlobalLock(); + } else { + CommGlobalLockBH(); + } + if (CommIsActive(channel) || CommIsOpened(channel)) { + CommSetZombie(channel); + rc = 0; + } + if (inBH) { + CommGlobalUnlock(); + } else { + CommGlobalUnlockBH(); + } + +out: + if (!rc) { + CommOS_ScheduleDisp(); + } + return rc; +} + + +/** + * @brief Reports whether a channel is active. + * @param channel channel to report on. + * @return non-zero if channel active, zero otherwise. + */ + +int +Comm_IsActive(CommChannel channel) +{ + return channel ? CommIsActive(channel) : 0; +} + + +/** + * @brief Wakes up potential writer on the channel. This function must be + * called on an active channel, with either the dispatch lock taken, or + * the channel ref count incremented. + * @param channel CommChannel structure on which potential writer waits. + */ + +static inline void +WakeUpWriter(CommChannel channel) +{ + if (WriteSpaceCondition(channel, NULL)) { + CommOS_WakeUp(&channel->availableWaitQ); + } +} + + +/** + * @brief Transport event handler for comm channels. + * @param transp transport handle. + * @param event type of event. + * @param data callback data. + * @sideeffects may put the channel into zombie state, or schedule it for I/O. + */ + +static void +TranspEventHandler(CommTransp transp, + CommTranspIOEvent event, + void *data) +{ + CommChannel channel = (CommChannel)data; + + switch (event) { + case COMM_TRANSP_IO_DETACH: + CommOS_Debug(("%s: Detach event. Zombifying channel.\n", __FUNCTION__)); + Comm_Zombify(channel, 1); + break; + + case COMM_TRANSP_IO_IN: + case COMM_TRANSP_IO_INOUT: + /* + * The dispatch threads may not have been started because either: + * a) we're not running in the CommSvc service, or + * b) the Comm client didn't create them explicitly (CommOS_StartIO()). + * + * If so, the CommOS_ScheduleDisp() call is ineffective. This is + * the intended behavior: the client obviously wants to call the Comm + * dispatch function(s) directly. + */ + + CommOS_ScheduleDisp(); + break; + + case COMM_TRANSP_IO_OUT: + CommHold(channel); + if (CommIsActive(channel)) { + WakeUpWriter(channel); + } + CommRelease(channel); + if (CommIsZombie(channel)) { + /* + * After releasing the hold on the channel, we must check if it was + * set to zombie and the dispatcher was supposed to nuke it. If the + * dispatcher had made its run while we were holding the channel, it + * gave up. So schedule it. + */ + + CommOS_ScheduleDisp(); + } + break; + + default: + CommOS_Debug(("%s: Unhandled event [%u, %p, %p].\n", + __FUNCTION__, event, transp, data)); + } +} + + +/** + * @brief Destroys upper layer state, unregisters event handlers and + * detaches from or deletes shared memory. + * @param[in,out] channel CommChannel structure to close. + */ + +static void +CommClose(CommChannel channel) +{ + const CommImpl *impl = channel->impl; + + StateLock(channel); + if (impl->stateDtor && channel->state) { + impl->stateDtor(channel->state); + } + channel->state = NULL; + StateUnlock(channel); + + CommOS_ModulePut(impl->owner); + + if (channel->transp) { + CommTransp_Close(channel->transp); + channel->transp = NULL; + } + + CommGlobalLockBH(); + CommSetFree(channel); + commChannelAllocated--; + if (channel == &commChannels[commChannelSize - 1]) { + commChannelSize--; + } + CommGlobalUnlockBH(); + if (!running && (commChannelAllocated == 0)) { + CommOS_WakeUp(&exitWaitQ); + } +} + + +/** + * @brief Allocates upper layer state, registers transport event handler + * and creates or attaches to shared memory. + * @param[in,out] channel CommChannel structure to open. + * @return zero if successful, -1 otherwise + * @sideeffects Memory may be allocated, event handlers registered and + * QP allocated or attached to. + */ + +static int +CommOpen(CommChannel channel) +{ + int rc = -1; + CommTranspEvent transpEvent = { + .ioEvent = TranspEventHandler, + .ioEventData = channel + }; + const CommImpl *impl; + + if (!channel || !CommIsInitialized(channel)) { + return rc; + } + + if (!running) { /* Ok, toggle it back to FREE. */ + goto out; + } + + impl = channel->impl; + if (impl->stateCtor) { + channel->state = impl->stateCtor(channel); + if (!channel->state) { + goto out; + } + } + + if (!CommTransp_Open(&channel->transp, &channel->transpArgs, &transpEvent)) { + rc = 0; + } else { + channel->transp = NULL; + } + +out: + if (!rc) { + CommSetOpened(channel); + } else { + CommClose(channel); + } + return rc; +} + + +/** + * @brief Retrieves a channel's transport initialization arguments. + * It doesn't lock, the caller must ensure the channel may be accessed. + * @param channel CommChannel structure to get initialization arguments from. + * @return initialization arguments used to allocate/attach to channel. + */ + +CommTranspInitArgs +Comm_GetTranspInitArgs(CommChannel channel) +{ + if (!channel) { + CommTranspInitArgs res = { .capacity = 0 }; + + return res; + } + return channel->transpArgs; +} + + +/** + * @brief Retrieves upper layer state (pointer). It doesn't lock, the caller + * must ensure the channel may be accessed. + * @param channel CommChannel structure to get state from. + * @return pointer to upper layer state. + */ + +void * +Comm_GetState(CommChannel channel) +{ + if (!channel) { + return NULL; + } + return channel->state; +} + + +/** + * @brief Main input processing function operating on a given channel. + * @param channel CommChannel structure to process. + * @return number of processed channels (0 or 1), or -1 if channel closed. + * @sideeffects Lifecycle states are transitioned to and from. Channel may + * be opened or destroyed, waiting writers may be woken up, and input + * may be handed off to operation callbacks. + */ + +int +Comm_Dispatch(CommChannel channel) +{ + int rc = 0; + int zombify = 0; + CommPacket packet; + CommPacket firstPacket; + unsigned int dataLen; +#define VEC_SIZE 32 + struct kvec vec[VEC_SIZE]; + unsigned int vecLen; + + /* + * Taking the reader mutex is safe in all cases: entries, including + * free ones, are guaranteed to have initialized mutexes and locks. + * Locking empty entries may seem wasteful, but those entries are rare. + */ + + if (DispatchTrylock(channel)) { + return 0; + } + + /* Process input and writer wake-up. */ + + if (CommIsActive(channel)) { + /* + * The entry may have transitioned to ZOMBIE, somehow. That's OK + * since it can't be freed just yet (it's currently locked). + */ + + /* Wake up any waiting writers, if necessary. */ + + WakeUpWriter(channel); + + /* Read packets, payloads. */ + CommTransp_DequeueReset(channel->transp); + + for (vecLen = 0; vecLen < VEC_SIZE; vecLen++) { + if (!running) { + break; + } + + /* Read header. */ + + rc = CommTransp_DequeueSegment(channel->transp, + &packet, sizeof packet); + if (rc <= 0) { + /* No packet (header). */ + + rc = vecLen == 0 ? 0 : 1; + break; + } +#if defined(COMM_INSTRUMENT) + CommOS_AddReturnAtomic(commPacketsReceived, 1); +#endif + if ((rc != sizeof packet) || (packet.len < sizeof packet)) { + rc = -1; /* Fatal protocol error, close down comm. */ + break; + } + rc = 1; + + /* Read payload, if any. */ + + dataLen = packet.len - sizeof packet; + if (vecLen == 0) { + /* Save header of first packet. */ + + firstPacket = packet; + if (dataLen == 0) { + /* Commit no-payload packet read and we're done. */ + + CommTransp_DequeueCommit(channel->transp); +#if defined(COMM_INSTRUMENT) + CommOS_AddReturnAtomic(&commCommittedPacketsReceived, 1); +#endif + break; + } + } else { + /* + * Check if non-equivalent packet or above coalescing limit. + * If so, don't commit the read. + */ + + if (memcmp(&packet.opCode, &firstPacket.opCode, + sizeof packet - offsetof(CommPacket, opCode)) || + PacketLenOverLimit(channel, firstPacket.len + dataLen)) { + break; + } + } + + if (dataLen == 0) { + /* + * Received equivalent packet with zero-sized payload. This may + * happen in certain cases, such as pvtcp forwarding zero-sized + * datagrams. So don't break the loop, but keep going for as + * along as we can. + */ + + vec[vecLen].iov_base = NULL; + goto dequeueCommit; + } + + /* The packet has a payload (dataLen > 0). */ + + if (!(vec[vecLen].iov_base = channel->impl->dataAlloc(dataLen))) { + /* + * We treat out-of-(net?-)memory errors as "nothing to read". + * Memory pressure may either subside, in which case a future + * read may be successful, or be severe enough for the kernel + * to oops, anyway. Leave packet uncommitted. + */ + + CommOS_Debug(("%s: COULD NOT ALLOC PAYLOAD BYTES!\n", + __FUNCTION__)); + rc = vecLen == 0 ? 0 : 1; + break; + } + + /* Read payload and commit (packet and payload). */ + + rc = CommTransp_DequeueSegment(channel->transp, + vec[vecLen].iov_base, dataLen); + if (rc != dataLen) { + channel->impl->dataFree(vec[vecLen].iov_base); + CommOS_Log(("%s: BOOG -- COULD NOT DEQUEUE PAYLOAD! [%d != %u]", + __FUNCTION__, rc, dataLen)); + rc = -1; /* Fatal protocol error, close down comm. */ + break; + } + rc = 1; + +dequeueCommit: + CommTransp_DequeueCommit(channel->transp); +#if defined(COMM_INSTRUMENT) + CommOS_AddReturnAtomic(&commCommittedPacketsReceived, 1); +#endif + vec[vecLen].iov_len = dataLen; + if (vecLen > 0) { + firstPacket.len += dataLen; + if (packet.flags) { + /* Update to latest flags _iff_ latter non-zero. */ + + firstPacket.flags = packet.flags; + } + } +#if defined(COMM_INSTRUMENT) + if (firstPacket.len > + CommOS_ReadAtomic(&commMaxCoalesceSize)) { + CommOS_WriteAtomic(&commMaxCoalesceSize, firstPacket.len); + } +#endif + if (COMM_OPF_TEST_ERR(packet.flags)) { + /* If error bit is set, we're done (no more coalescing). */ + + vecLen++; + break; + } + } + + if (rc <= 0) { + if (rc < 0) { + zombify = 1; + rc = 1; + } + goto outUnlockAndFreeIovec; + } + +#if defined(COMM_DISPATCH_EXTRA_WRITER_WAKEUP) + /* Check again if we need to wake up any writers. */ + + WakeUpWriter(channel); +#endif + + if (firstPacket.opCode >= channel->implNmbOps) { + CommOS_Debug(("%s: Ignoring illegal opCode [%u]!\n", + __FUNCTION__, (unsigned int)firstPacket.opCode)); + CommOS_Debug(("%s: Max opCode: %u\n", + __FUNCTION__, channel->implNmbOps)); + goto outUnlockAndFreeIovec; + } + + /* + * NOTE: + * DispatchUnlock() _must_ be called from the operation callback. + * The reason for doing so is that, for better scalability, we want + * it released as soon as possible, BUT: + * - releasing it here, before calling into the operation, doesn't + * let the latter coordinate its own lock acquisition, such as + * potential socket or state locks. + * - alternatively, always releasing the dispatch lock after the + * operation completes, ties up the channel and imposes too much + * serialization between sockets. + * - to prevent the channel from being torn down while an operation + * is in flight (and potentially having released the dispatch lock), + * we increment the ref count on the channel and then release it + * after the function returns. + */ + +#if defined(COMM_INSTRUMENT) + CommOS_AddReturnAtomic(&commOpCalls, 1); +#endif + + CommHold(channel); + channel->impl->operations[firstPacket.opCode](channel, channel->state, + &firstPacket, vec, vecLen); + CommRelease(channel); + goto out; /* No unlocking, see comment above. */ + } + + /* Process state changes. */ + + if (CommIsZombie(channel) && !CommIsHeld(channel)) { + CommTranspInitArgs transpArgs = channel->transpArgs; + void (*closeNtf)(void *, + const CommTranspInitArgs *, + int inBH) = channel->impl->closeNtf; + void *closeNtfData = channel->impl->closeNtfData; + + while (WriteTrylock(channel)) { + /* Take the write lock; kick writers out if necessary. */ + + CommOS_Debug(("%s: Kicking writers out...\n", __FUNCTION__)); + CommOS_WakeUp(&channel->availableWaitQ); + } + WriteUnlock(channel); + + CommOS_Debug(("%s: Nuking zombie channel.\n", __FUNCTION__)); + CommClose(channel); + if (closeNtf) { + closeNtf(closeNtfData, &transpArgs, 0); + } + rc = -1; + } else if (CommIsInitialized(channel) && + (channel->impl->openAtMillis <= + CommOS_GetCurrentMillis())) { + if (!CommOpen(channel)) { + if (channel->transpArgs.mode == COMM_TRANSP_INIT_CREATE) { + /* + * If the attach side doesn't get notified, the entry will + * time out in OPENED and will be collected. + * Note that during the CommOpen(Transp_Open) call, the IDs + * in the transpArgs may have changed. Use those. + */ + + CommTransp_Notify(&channel->impl->ntfCenterID, + &channel->transpArgs); + } else { /* Attach mode */ + packet.len = sizeof packet; + packet.opCode = 0xff; + packet.flags = 0x00; + + /* + * Send out control packet, attach ack, and transition straight + * to ACTIVE. + */ + + rc = CommTransp_EnqueueAtomic(channel->transp, + &packet, sizeof packet); + if (rc == sizeof packet) { + /* Guard against potentially concurrent zombify. */ + + CommGlobalLockBH(); + if (CommIsOpened(channel)) { + CommOS_Debug(("%s: Sent attach ack. Activating channel.\n", + __FUNCTION__)); + CommSetActive(channel); + } + CommGlobalUnlockBH(); + } + } + rc = 1; + } + } else if (CommIsOpened(channel) && + (channel->transpArgs.mode == COMM_TRANSP_INIT_CREATE)) { + /* + * Get control packet (opCode == 0xff), attach ack (flags == 0x0), + * or check whether the channel timed out in OPENED. + */ + + rc = CommTransp_DequeueAtomic(channel->transp, + &packet, sizeof packet); + if (rc == sizeof packet) { + void (*activateNtf)(void *activateNtfData, CommChannel) = NULL; + void *activateNtfData = NULL; + + /* Guard against potentially concurrent zombify. */ + + CommGlobalLockBH(); + if (CommIsOpened(channel) && + (packet.opCode == 0xff) && (packet.flags == 0x0)) { + activateNtf = channel->impl->activateNtf; + activateNtfData = channel->impl->activateNtfData; + + CommSetActive(channel); + CommOS_Debug(("%s: Received attach ack. Activating channel.\n", + __FUNCTION__)); + } + CommHold(channel); + CommGlobalUnlockBH(); + + if (activateNtf) { + /* The callback must be short and 'put' the channel when done. */ + + activateNtf(activateNtfData, channel); + } else { + /* Don't forget to put back the channel if no activate callback. */ + + CommRelease(channel); + } + } else if ((channel->impl->openTimeoutAtMillis <= + CommOS_GetCurrentMillis()) || + !running) { + zombify = 1; + CommOS_Debug(("%s: Zombifying expired opened channel.\n", + __FUNCTION__)); + } + rc = 1; + } + DispatchUnlock(channel); + +out: + if (zombify) { + Comm_Zombify(channel, 0); + } + return rc; + +outUnlockAndFreeIovec: + DispatchUnlock(channel); + for ( ; vecLen; ) { + if (vec[--vecLen].iov_base) { + channel->impl->dataFree(vec[vecLen].iov_base); + vec[vecLen].iov_base = NULL; + } + vec[vecLen].iov_len = 0; + } + goto out; +#undef VEC_SIZE +} + + +/** + * @brief Main input processing function operating on all channels. + * @return number of processed channels. + * @sideeffects Lifecycle states are transitioned to and from. Channels may + * be opened and destroyed, waiting writers may be woken up, and input + * may be handed off to operation callbacks. + */ + +unsigned int +Comm_DispatchAll(void) +{ + unsigned int i; + unsigned int hits; + + for (hits = 0, i = 0; running && (i < commChannelSize); i++) { + hits += !!Comm_Dispatch(&commChannels[i]); + } + return hits; +} + + +/** + * @brief Writes a fully formatted packet (containing payload data, if + * applicable) to the specified channel. + * + * The operation may block until enough write space is available, but no + * more than the specified interval. The operation either writes the full + * amount of bytes, or it fails. Warning: callers must _not_ use the + * _Lock/_Unlock functions to bracket calls to this function. + * @param[in,out] channel channel to write to. + * @param packet packet to write. + * @param[in,out] timeoutMillis interval in milliseconds to wait. + * @return number of bytes written, 0 if it times out, -1 error. + * @sideeffects Data may be written to the channel. + */ + +int +Comm_Write(CommChannel channel, + const CommPacket *packet, + unsigned long long *timeoutMillis) +{ + int rc = -1; + int zombify; + + if (!channel || !timeoutMillis || + !packet || (packet->len < sizeof *packet)) { + return rc; + } + + zombify = (*timeoutMillis >= COMM_MAX_TO); + + WriteLock(channel); + if (!CommIsActive(channel)) { + goto out; + } + + CommTransp_EnqueueReset(channel->transp); + channel->desiredWriteSpace = packet->len; + rc = CommOS_DoWait(&channel->availableWaitQ, WriteSpaceCondition, + channel, NULL, timeoutMillis, + (*timeoutMillis != COMM_MAX_TO_UNINT)); + channel->desiredWriteSpace = -1U; + + if (rc) { /* Don't zombify, if it didn't time out. */ + zombify = 0; + } + if (rc == 1) { /* Enough write space, enqueue the packet. */ + rc = CommTransp_EnqueueAtomic(channel->transp, packet, packet->len); + if (rc != packet->len) { + zombify = 1; + rc = -1; /* Fatal protocol error. */ + } + } + +out: + WriteUnlock(channel); + if (zombify) { + Comm_Zombify(channel, 0); + } + return rc; +} + + +/** + * @brief Writes a packet and associated payload data to the specified channel. + * The operation may block until enough write space is available, but + * not more than the specified interval. + * The operation either writes the full amount of bytes, or it fails. + * If there is not enough data in the vector, padding will be added to + * reach the specified packet length, if the flags parameter requires it. + * Users may call this function successively to write several packets + * from large {io|k}vecs, when the flags parameter indicates it. If this + * is the case, the packet header needs to be updated accordingly in + * between calls, for the different (total) lengths. + * Warning: callers must _not_ use the _Lock/_Unlock functions to bracket + * calls to this function. + * @param[in,out] channel the specified channel. + * @param packet packet to write. + * @param[in,out] vec kvec to write from. + * @param[in,out] vecLen length of kvec. + * @param[in,out] timeoutMillis interval in milliseconds to wait. + * @param[in,out] iovOffset must be set to 0 before first call (internal cookie) + * @return number of bytes written, 0 if it timed out, -1 error. + * @sideeffects data may be written to the channel. + */ + +int +Comm_WriteVec(CommChannel channel, + const CommPacket *packet, + struct kvec **vec, + unsigned int *vecLen, + unsigned long long *timeoutMillis, + unsigned int *iovOffset) +{ + int rc; + int zombify; + unsigned int dataLen; + unsigned int vecDataLen; + unsigned int vecNdx; + unsigned int iovLen; + void *iovBase; + + if (!channel || !timeoutMillis || !iovOffset || + !packet || (packet->len < sizeof *packet) || + (((dataLen = packet->len - sizeof *packet) > 0) && + (!*vec || !*vecLen))) { + return -1; + } + + zombify = (*timeoutMillis >= COMM_MAX_TO); + + WriteLock(channel); + if (!CommIsActive(channel)) { + rc = -1; + goto out; + } + + CommTransp_EnqueueReset(channel->transp); + channel->desiredWriteSpace = packet->len; + rc = CommOS_DoWait(&channel->availableWaitQ, WriteSpaceCondition, + channel, NULL, timeoutMillis, + (*timeoutMillis != COMM_MAX_TO_UNINT)); + channel->desiredWriteSpace = -1U; + + if (rc) { /* Don't zombify, if it didn't time out. */ + zombify = 0; + } + if (rc == 1) { /* Enough write space, enqueue the packet. */ + iovLen = 0; + rc = CommTransp_EnqueueSegment(channel->transp, packet, sizeof *packet); + if (rc != sizeof *packet) { + zombify = 1; + rc = -1; /* Fatal protocol error. */ + goto out; + } + + if (dataLen > 0) { + int done = 0; + + for (vecDataLen = 0, vecNdx = 0; vecNdx < *vecLen; vecNdx++) { + if (vecNdx) { + *iovOffset = 0; + } + iovLen = (*vec)[vecNdx].iov_len - *iovOffset; + iovBase = (*vec)[vecNdx].iov_base + *iovOffset; + + if (!iovLen) { + continue; + } + + vecDataLen += iovLen; + if (vecDataLen >= dataLen) { + iovLen -= (vecDataLen - dataLen); + done = 1; + } + + rc = CommTransp_EnqueueSegment(channel->transp, iovBase, iovLen); + if (rc != iovLen) { + zombify = 1; + rc = -1; /* Fatal protocol error, close down comm. */ + goto out; + } + + if (done) { + CommTransp_EnqueueCommit(channel->transp); + if (vecDataLen == dataLen) { + vecNdx++; + *iovOffset = 0; + } else { + *iovOffset += iovLen; + } + *vecLen -= vecNdx; + *vec += vecNdx; + break; + } + } + + if (!done) { + /* + * We exhausted all the bytes in the given vector, but total length + * in the packet header is more than we sent (was available). + * If so, we pad by sending zero bytes to reach length required. + */ + + static char pad[1024]; + unsigned int delta; + unsigned int toSend; + + while (vecDataLen < dataLen) { + delta = dataLen - vecDataLen; + toSend = delta <= sizeof pad ? delta : sizeof pad; + if (toSend == delta) { + done = 1; + } + vecDataLen += toSend; + + rc = CommTransp_EnqueueSegment(channel->transp, pad, toSend); + if (rc != toSend) { + zombify = 1; + rc = -1; /* Fatal protocol error, close down comm. */ + goto out; + } + + if (done) { + CommTransp_EnqueueCommit(channel->transp); + *vec = NULL; + *vecLen = 0; + *iovOffset = 0; + break; + } + } + } + } else { + CommTransp_EnqueueCommit(channel->transp); + } + rc = (int)packet->len; + } else { + CommOS_Debug(("%s: timed out...\n", __FUNCTION__)); + } + +out: + WriteUnlock(channel); + if (zombify) { + Comm_Zombify(channel, 0); + } + return rc; +} + + +/** + * @brief Releases channel ref count. This function is exported for the upper + * layer's 'activateNtf' callback which may be run asynchronously. The + * callback is protected from concurrent channel releases until it calls + * this function. + * @param[in,out] channel CommChannel structure to release. + */ + +void +Comm_Put(CommChannel channel) +{ + if (channel) { + CommRelease(channel); + } +} + + +/** + * @brief Uses the read lock. This function is exported for the upper layer + * such that it can order acquisition of a different lock (socket) with + * the release of the dispatch lock. + * @param[in,out] channel CommChannel structure to unlock. + */ + +void +Comm_DispatchUnlock(CommChannel channel) +{ + if (channel) { + DispatchUnlock(channel); + } +} + + +/** + * @brief Lock the channel for upper layer state. + * This function is exported for the upper layer to ensure that channel + * isn't closed while updating the layer state. Operations using this + * function are expected to be short, since unlike the _Write functions, + * these callers cannot be signaled. + * @param[in,out] channel CommChannel structure to lock. + * @return zero if successful, -1 otherwise. + */ + +int +Comm_Lock(CommChannel channel) +{ + if (!channel) { + return -1; + } + StateLock(channel); + if (!CommIsActive(channel) && !CommIsZombie(channel)) { + StateUnlock(channel); + return -1; + } + return 0; +} + + +/** + * @brief Uses the writer lock. This function is exported for the upper layer + * to ensure that channel isn't closed while updating the layer state. + * See Comm_Lock for details). + * @param[in,out] channel CommChannel structure to unlock. + */ + +void +Comm_Unlock(CommChannel channel) +{ + if (channel) { + StateUnlock(channel); + } +} + + +/** + * @brief Requests events be posted in-line after the function completes. + * @param channel channel object. + * @return current number of requests for inline event posting, or -1 on error. + */ + +unsigned int +Comm_RequestInlineEvents(CommChannel channel) +{ + if (channel->transp) { + return CommTransp_RequestInlineEvents(channel->transp); + } else { + return (unsigned int)-1; + } +} + + +/** + * @brief Requests events be posted out-of-band after the function completes. + * @param channel channel object. + * @return current number of requests for inline event posting, or -1 on error. + */ + +unsigned int +Comm_ReleaseInlineEvents(CommChannel channel) +{ + if (channel->transp) { + return CommTransp_ReleaseInlineEvents(channel->transp); + } else { + return (unsigned int)-1; + } +} diff --git a/arch/arm/mvp/commkm/comm.h b/arch/arm/mvp/commkm/comm.h new file mode 100644 index 0000000..8291ae4 --- /dev/null +++ b/arch/arm/mvp/commkm/comm.h @@ -0,0 +1,171 @@ +/* + * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications + * + * Copyright (C) 2010-2012 VMware, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#line 5 + +/** + * @file + * + * @brief Communication functions based on queue pair transport APIs. + * + * Comm is a shared memory-based mechanism that facilitates the implementation + * of kernel components that require host-to-guest, or guest-to-guest + * communication. + * This facility assumes the availability of a minimal shared memory queue pair + * implementation, such as MVP queue pairs or VMCI queue pairs. The latter must + * provide primitives for queue pair creation and destruction, and reading and + * writing from/to queue pairs. + * Comm assumes that the queue pair (transport) layer is not concerned with + * multi-threading, locking or flow control, and does not require such features. + */ + +#ifndef _COMM_H_ +#define _COMM_H_ + +#define INCLUDE_ALLOW_MODULE +#define INCLUDE_ALLOW_PV +#define INCLUDE_ALLOW_GPL +#include "include_check.h" + +#include "comm_os.h" +#include "comm_transp.h" + + +/* Default/maximum Comm timeouts (in milliseconds). */ +#define COMM_MAX_TO 60000ULL +#define COMM_MAX_TO_UNINT (COMM_MAX_TO + 1) + +#define COMM_OPF_SET_ERR(flags) ((flags) |= 128) +#define COMM_OPF_CLEAR_ERR(flags) ((flags) &= 127) +#define COMM_OPF_TEST_ERR(flags) ((flags) & 128) + +#define COMM_OPF_SET_VAL(flags, val) ((flags) |= ((val) & 127)) +#define COMM_OPF_GET_VAL(flags) ((flags) & 127) + +/** + * Packet (header) structure. + * NB: Do not change this structure, especially the first three fields; there + * will be consequences. It may be extended, but it's not recommended: all + * operations carry this header, so it's better kept in its minimal form. + */ + +typedef struct CommPacket { + unsigned int len; // Total length + unsigned char flags; // Operation flags + unsigned char opCode; // Operation to call + unsigned short data16; // Auxiliary data + unsigned long long data64; + unsigned long long data64ex; + union { + struct { + unsigned int data32; + unsigned int data32ex; + }; + unsigned long long data64ex2; + }; +} CommPacket; + + +/* Opaque structure representing a communication channel. */ + +struct CommChannelPriv; +typedef struct CommChannelPriv *CommChannel; + + +/* Input operations associated with a comm channel. */ + +typedef void (*CommOperationFunc)(CommChannel channel, + void *state, + CommPacket *packet, + struct kvec *vec, + unsigned int vecLen); + + +/* Helper macros */ + +#define COMM_DEFINE_OP(funcName) \ +void \ +funcName(CommChannel channel, \ + void *state, \ + CommPacket *packet, \ + struct kvec *vec, \ + unsigned int vecLen) + + +/* Comm-based implementations. */ + +typedef struct CommImpl { + struct module *owner; + int (*checkArgs)(CommTranspInitArgs *transpArgs); + void *(*stateCtor)(CommChannel channel); + void (*stateDtor)(void *state); + void *(*dataAlloc)(unsigned int dataLen); + void (*dataFree)(void *data); + const CommOperationFunc *operations; + void (*closeNtf)(void *closeNtfData, + const CommTranspInitArgs *transpArgs, + int inBH); + void *closeNtfData; + void (*activateNtf)(void *activateNtfData, + CommChannel channel); + void *activateNtfData; + unsigned long long openAtMillis; + unsigned long long openTimeoutAtMillis; + CommTranspID ntfCenterID; +} CommImpl; + + +int Comm_Init(unsigned int maxChannels); +int Comm_Finish(unsigned long long *timeoutMillis); +int Comm_RegisterImpl(const CommImpl *impl); +void Comm_UnregisterImpl(const CommImpl *impl); +int Comm_IsActive(CommChannel channel); +CommTranspInitArgs Comm_GetTranspInitArgs(CommChannel channel); +void *Comm_GetState(CommChannel channel); +int Comm_Dispatch(CommChannel channel); +unsigned int Comm_DispatchAll(void); +void Comm_Put(CommChannel channel); +void Comm_DispatchUnlock(CommChannel channel); +int Comm_Lock(CommChannel channel); +void Comm_Unlock(CommChannel channel); +int Comm_Zombify(CommChannel channel, int inBH); + +int +Comm_Alloc(const CommTranspInitArgs *transpArgs, + const CommImpl *impl, + int inBH, + CommChannel *newChannel); + + +int +Comm_Write(CommChannel channel, + const CommPacket *packet, + unsigned long long *timeoutMillis); + +int +Comm_WriteVec(CommChannel channel, + const CommPacket *packet, + struct kvec **vec, + unsigned int *vecLen, + unsigned long long *timeoutMillis, + unsigned int *iovOffset); + +unsigned int Comm_RequestInlineEvents(CommChannel channel); +unsigned int Comm_ReleaseInlineEvents(CommChannel channel); + +#endif // _COMM_H_ diff --git a/arch/arm/mvp/commkm/comm_ev.h b/arch/arm/mvp/commkm/comm_ev.h new file mode 100644 index 0000000..bf629c3 --- /dev/null +++ b/arch/arm/mvp/commkm/comm_ev.h @@ -0,0 +1,51 @@ +/* + * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications + * + * Copyright (C) 2010-2012 VMware, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#line 5 + +/** + * @file + * + * @brief various comm event signaling types and signatures + */ + +#ifndef _COMM_EV_H +#define _COMM_EV_H + +#define INCLUDE_ALLOW_PV +#define INCLUDE_ALLOW_GPL +#define INCLUDE_ALLOW_MONITOR +#define INCLUDE_ALLOW_MODULE +#include "include_check.h" + +/** + * @name Identifiers of comm event signaling class methods + * @{ + */ +#define MVP_COMM_EV_SIGNATURE 0x4d4d4f43 ///< 'COMM' +#define MVP_COMM_EV_SIGNAL (MVP_OBJECT_CUSTOM_BASE + 0) ///< Signal host +#define MVP_COMM_EV_READ_EVENT_DATA (MVP_OBJECT_CUSTOM_BASE + 1) ///< read event data +#define MVP_COMM_EV_LAST (MVP_OBJECT_CUSTOM_BASE + 2) ///< Number of methods +/**@}*/ + +typedef struct CommEvent { + CommTranspID id; + CommTranspIOEvent event; +} CommEvent; + +#endif diff --git a/arch/arm/mvp/commkm/comm_ev_kernel.c b/arch/arm/mvp/commkm/comm_ev_kernel.c new file mode 100644 index 0000000..0701945 --- /dev/null +++ b/arch/arm/mvp/commkm/comm_ev_kernel.c @@ -0,0 +1,136 @@ +/* + * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications + * + * Copyright (C) 2010-2012 VMware, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#line 5 + +/** + * @file + * + * @brief Comm event signaling, host kernel side. + */ + +#include <linux/net.h> + +#include "mvp_types.h" +#include "comm_os.h" +#include "comm_transp_impl.h" +#include "mksck_sockaddr.h" +#include "comm_ev.h" +#include "mvpkm_comm_ev.h" + +static struct socket *sock; + +/** + * @brief Raises a transport event on the provided event ID (address). This + * function is called from a comm_transp provider, such as comm_transp_mvp, + * when it needs to signal an event on a given channel. + * @param targetEvID opaque event channel ID (interpreted by implementation). + * @param transpID ID of transport to signal. + * @param eventType event type to raise. + * @return 0 if successful, -1 otherwise. + */ + +int +CommTranspEvent_Raise(unsigned int targetEvID, // unused + CommTranspID *transpID, + CommTranspIOEvent eventType) +{ + struct sockaddr_mk guestAddr; + struct msghdr msg; + struct kvec vec[1]; + int rc; + CommEvent event; + + if (!transpID) { + return -1; + } + + guestAddr.mk_family = AF_MKSCK; + guestAddr.mk_addr.addr = Mksck_AddrInit(transpID->d32[0], MKSCK_PORT_COMM_EV); + + memset(&msg, 0, sizeof (struct msghdr)); + msg.msg_name = &guestAddr; + msg.msg_namelen = sizeof (guestAddr); + + event.id = *transpID; + event.event = eventType; + + vec[0].iov_base = &event; + vec[0].iov_len = sizeof (CommEvent); + + rc = kernel_sendmsg(sock, + &msg, + vec, + 1, + sizeof (CommEvent)); + rc = (rc < 0) ? -1 : 0; + return rc; +} + + +/** + * @brief Performs one-time, global initialization of event provider. + * @return 0 if successful, -1 otherwise. + */ +int +CommTranspEvent_Init(void) +{ + struct sockaddr_mk addr = { AF_MKSCK, { .addr = MKSCK_ADDR_UNDEF } }; + int rc; + + rc = sock_create_kern(AF_MKSCK, SOCK_DGRAM, 0, &sock); + if (rc < 0) { + goto out; + } + + rc = kernel_bind(sock, (struct sockaddr *) &addr, sizeof addr); + if (rc < 0) { + sock_release(sock); + sock = NULL; + goto out; + } + + Mvpkm_CommEvRegisterProcessCB(CommTranspEvent_Process); + +out: + if (rc) { + CommOS_Log(("%s: Failed to initialize transport event signaling\n", + __FUNCTION__)); + } else { + CommOS_Log(("%s: Transport event signaling initialization successful\n", + __FUNCTION__)); + } + return rc; +} + + +/** + * @brief Performs global clean-up of event provider. + */ + +void +CommTranspEvent_Exit(void) +{ + Mvpkm_CommEvUnregisterProcessCB(); + if (sock) { + sock_release(sock); + sock = NULL; + } + + CommOS_Debug(("%s: done.\n", __FUNCTION__)); +} diff --git a/arch/arm/mvp/commkm/comm_os.h b/arch/arm/mvp/commkm/comm_os.h new file mode 100644 index 0000000..f98c8d4 --- /dev/null +++ b/arch/arm/mvp/commkm/comm_os.h @@ -0,0 +1,150 @@ +/* + * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications + * + * Copyright (C) 2010-2012 VMware, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#line 5 + +/** + * @file + * + * @brief Cross-platform base type definitions and function declarations. + * Includes OS-specific base type definitions and function declarations. + */ + +#ifndef _COMM_OS_H_ +#define _COMM_OS_H_ + +/* For-ever timeout constant (in milliseconds). */ +#define COMM_OS_4EVER_TO ((unsigned long long)(~0UL >> 1)) + +/* Condition function prototype. Returns 1: true, 0: false, < 0: error code. */ +typedef int (*CommOSWaitConditionFunc)(void *arg1, void *arg2); + +/* Dispatch function prototype. Called by input (dispatch) kernel threads. */ +typedef unsigned int (*CommOSDispatchFunc)(void); + +/* Module initialization and exit callback functions. */ +extern int (*commOSModInit)(void *args); +extern void (*commOSModExit)(void); + +/* Macro to assign Init and Exit callbacks. */ +#define COMM_OS_MOD_INIT(init, exit) \ + int (*commOSModInit)(void *args) = init; \ + void (*commOSModExit)(void) = exit + + +/* + * OS-specific implementations must provide the following: + * 1. Types: + * CommOSAtomic + * CommOSSpinlock + * CommOSMutex + * CommOSWaitQueue + * CommOSWork + * CommOSWorkFunc + * CommOSList + * CommOSModule + * struct kvec + * + * 2. Definition, initializers: + * CommOSSpinlock_Define() + * + * 3. Functions: + * void CommOS_Debug(const char *format, ...); + * void CommOS_Log(const char *format, ...); + * void CommOS_WriteAtomic(CommOSAtomic *atomic, int val); + * int CommOS_ReadAtomic(CommOSAtomic *atomic); + * int CommOS_AddReturnAtomic(CommOSAtomic *atomic, int val); + * int CommOS_SubReturnAtomic(CommOSAtomic *atomic, int val); + * void CommOS_SpinlockInit(CommOSSpinlock *lock); + * void CommOS_SpinLockBH(CommOSSpinlock *lock); + * int CommOS_SpinTrylockBH(CommOSSpinlock *lock); + * void CommOS_SpinUnlockBH(CommOSSpinlock *lock); + * void CommOS_SpinLock(CommOSSpinlock *lock); + * int CommOS_SpinTrylock(CommOSSpinlock *lock); + * void CommOS_SpinUnlock(CommOSSpinlock *lock); + * void CommOS_MutexInit(CommOSMutex *mutex); + * void CommOS_MutexLock(CommOSMutex *mutex); + * int CommOS_MutexLockUninterruptible(CommOSMutex *mutex); + * int CommOS_MutexTrylock(CommOSMutex *mutex); + * void CommOS_MutexUnlock(CommOSMutex *mutex); + * void CommOS_WaitQueueInit(CommOSWaitQueue *wq); + * CommOS_DoWait(CommOSWaitQueue *wq, + * CommOSWaitConditionFunc cond, + * void *condArg1, + * void *condArg2, + * unsigned long long *timeoutMillis, + * int interruptible); + * int CommOS_Wait(CommOSWaitQueue *wq, + * CommOSWaitConditionFunc func, + * void *funcArg1, + * void *funcArg2, + * unsigned long long *timeoutMillis); + * int CommOS_WaitUninterruptible(CommOSWaitQueue *wq, + * CommOSWaitConditionFunc func, + * void *funcArg1, + * void *funcArg2, + * unsigned long long *timeoutMillis); + * void CommOS_WakeUp(CommOSWaitQueue *wq); + * void *CommOS_KmallocNoSleep(unsigned int size); + * void *CommOS_Kmalloc(unsigned int size); + * void CommOS_Kfree(void *arg); + * void CommOS_Yield(void); + * unsigned long long CommOS_GetCurrentMillis(void); + * void CommOS_ListInit(CommOSList *list); + * int CommOS_ListEmpty(CommOSList *list); + * void CommOS_ListAdd(CommOSList *list, CommOSList *listElem); + * void CommOS_ListAddTail(CommOSList *list, CommOSList *listElem); + * void int CommOS_ListDel(CommOSList *listElem); + * Macros: + * CommOS_ListForEach(*list, *item, itemListFieldName); + * CommOS_ListForEachSafe(*list, *item, *tmp, itemListFieldName); + * void CommOS_ListSplice(CommOSList *list, CommOSList *listToAdd); + * void CommOS_ListSpliceTail(CommOSList *list, CommOSList *listToAdd); + * CommOSModule CommOS_ModuleSelf(void); + * int CommOS_ModuleGet(CommOSModule module); + * void CommOS_ModulePut(CommOSModule module); + * void CommOS_MemBarrier(void); + * + * These cannot be defined here: a) non-pointer type definitions need size + * information, and b) functions may or may not be inlined, or macros may + * be used instead. + */ + + +#ifdef __linux__ +#include "comm_os_linux.h" +#else +#error "Unsupported OS" +#endif + +/* Functions to start and stop the dispatch and aio kernel threads. */ +void CommOS_StopIO(void); +void CommOS_ScheduleDisp(void); +void CommOS_InitWork(CommOSWork *work, CommOSWorkFunc func); +int CommOS_ScheduleAIOWork(CommOSWork *work); +void CommOS_FlushAIOWork(CommOSWork *work); + +int +CommOS_StartIO(const char *dispatchTaskName, + CommOSDispatchFunc dispatchHandler, + unsigned int interval, + unsigned int maxCycles, + const char *aioTaskName); + + +#endif /* _COMM_OS_H_ */ diff --git a/arch/arm/mvp/commkm/comm_os_linux.c b/arch/arm/mvp/commkm/comm_os_linux.c new file mode 100644 index 0000000..74f99f5 --- /dev/null +++ b/arch/arm/mvp/commkm/comm_os_linux.c @@ -0,0 +1,371 @@ +/* + * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications + * + * Copyright (C) 2010-2012 VMware, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#line 5 + +/** + * @file + * + * @brief Linux-specific functions/types. + */ + +#include "comm_os.h" + +#define DISPATCH_MAX_CYCLES 8192 + +/* Type definitions */ + +typedef struct workqueue_struct CommOSWorkQueue; + + +/* Static data */ + +static volatile int running; +static int numCpus; +static CommOSWorkQueue *dispatchWQ; +static CommOSDispatchFunc dispatch; +static CommOSWork dispatchWorksNow[NR_CPUS]; +static CommOSWork dispatchWorks[NR_CPUS]; +static unsigned int dispatchInterval = 1; +static unsigned int dispatchMaxCycles = 2048; +static CommOSWorkQueue *aioWQ; + + +/** + * @brief Initializes a workqueue consisting of per-cpu kernel threads. + * @param name workqueue name + * @return workqueue handle if successful, NULL otherwise + */ + +static inline CommOSWorkQueue * +CreateWorkqueue(const char *name) +{ + return create_workqueue(name); +} + + +/** + * @brief Destroys a workqueue and stops its threads. + * @param[in,out] wq workqueue to destroy. + * @return workqueue handle is successful, NULL otherwise. + */ + +static inline void +DestroyWorkqueue(CommOSWorkQueue *wq) +{ + destroy_workqueue(wq); +} + + +/** + * @brief Force execution of a work item. + * @param[in,out] work work item to dequeue. + */ + +static inline void +FlushDelayedWork(CommOSWork *work) +{ + flush_delayed_work(work); +} + + +/** + * @brief Enqueue a work item to a workqueue for execution on a given cpu + * and after the specified interval. + * @param cpu cpu number. If negative, work item is enqueued on current cpu. + * @param[in,out] wq target work queue. + * @param[in,out] work work item to enqueue. + * @param jif delay interval. + * @return zero if successful, non-zero otherwise. + */ + +static inline int +QueueDelayedWorkOn(int cpu, + CommOSWorkQueue *wq, + CommOSWork *work, + unsigned long jif) +{ + if (cpu < 0) { + return !queue_delayed_work(wq, work, jif) ? -1 : 0; + } else { + return !queue_delayed_work_on(cpu, wq, work, jif) ? -1 : 0; + } +} + + +/** + * @brief Enqueues a work item to a workqueue for execution on the current cpu + * and after the specified interval. + * @param[in,out] wq target work queue. + * @param[in,out] work work item to enqueue. + * @param jif delay interval. + * @return zero if successful, non-zero otherwise. + */ + +static inline int +QueueDelayedWork(CommOSWorkQueue *wq, + CommOSWork *work, + unsigned long jif) +{ + return QueueDelayedWorkOn(-1, wq, work, jif); +} + + +/** + * @brief Cancels a queued delayed work item and synchronizes with its + * completion. + * @param[in,out] work work item to cancel + */ + +static inline void +WaitForDelayedWork(CommOSWork *work) +{ + cancel_delayed_work_sync(work); +} + + +/** + * @brief Discards work items queued to the specified workqueue. + * @param[in,out] wq work queue to flush. + */ + +static inline void +FlushWorkqueue(CommOSWorkQueue *wq) +{ + flush_workqueue(wq); +} + + +/** + * @brief Schedules dispatcher threads for immediate execution. + */ + +void +CommOS_ScheduleDisp(void) +{ + CommOSWork *work = &dispatchWorksNow[get_cpu()]; + + put_cpu(); + if (running) { + QueueDelayedWork(dispatchWQ, work, 0); + } +} + + +/** + * @brief Default delayed work callback function implementation. + * Calls the input function specified at initialization. + * @param[in,out] work work item. + */ + +static void +DispatchWrapper(CommOSWork *work) +{ + unsigned int misses; + + for (misses = 0; running && (misses < dispatchMaxCycles); ) { + /* We run for at most dispatchMaxCycles worth of channel no-ops. */ + + if (!dispatch()) { + /* No useful work was done, on any of the channels. */ + + misses++; + if ((misses % 32) == 0) { + CommOS_Yield(); + } + } else { + misses = 0; + } + } + + if (running && + (work >= &dispatchWorks[0]) && + (work <= &dispatchWorks[NR_CPUS - 1])) { + /* + * If still running _and_ this was a regular, time-based run, then + * re-arm the timer. + */ + + QueueDelayedWork(dispatchWQ, work, dispatchInterval); + } +} + + +/** + * @brief Initializes work item with specified callback function. + * @param[in,out] work work queue to initialize. + * @param func work item to initialize the queue with. + */ + +void +CommOS_InitWork(CommOSWork *work, + CommOSWorkFunc func) +{ + INIT_DELAYED_WORK(work, (work_func_t)func); +} + + +/** + * @brief Flush execution of a work item + * @param{in,out] work work item to dequeue + */ +void +CommOS_FlushAIOWork(CommOSWork *work) +{ + if (aioWQ && work) { + FlushDelayedWork(work); + } +} + + +/** + * @brief Queue a work item to the AIO workqueue. + * @param[in,out] work work item to enqueue. + * @return zero if work enqueued, non-zero otherwise. + */ + +int +CommOS_ScheduleAIOWork(CommOSWork *work) +{ + if (running && aioWQ && work) { + return QueueDelayedWork(aioWQ, work, 0); + } + return -1; +} + + +/** + * @brief Initializes the base IO system. + * @param dispatchTaskName dispatch thread(s) name. + * @param dispatchFunc dispatch function. + * @param intervalMillis periodic interval in milliseconds to call dispatch. + * The floor is 1 jiffy, regardless of how small intervalMillis is + * @param maxCycles number of cycles to do adaptive polling before scheduling. + * The maximum number of cycles is DISPATCH_MAX_CYCLES. + * @param aioTaskName AIO thread(s) name. If NULL, AIO threads aren't started. + * @return zero is successful, -1 otherwise. + * @sideeffects Dispatch threads, and if applicable, AIO threads are started. + */ + +int +CommOS_StartIO(const char *dispatchTaskName, // IN + CommOSDispatchFunc dispatchFunc, // IN + unsigned int intervalMillis, // IN + unsigned int maxCycles, // IN + const char *aioTaskName) // IN +{ + int rc; + int cpu; + + if (running) { + CommOS_Debug(("%s: I/O tasks already running.\n", __FUNCTION__)); + return 0; + } + + /* + * OK, let's test the handler against NULL. Though, the whole concept + * of checking for NULL pointers, outside cases where NULL is meaningful + * to the implementation, is relatively useless: garbage, random pointers + * rarely happen to be all-zeros. + */ + + if (!dispatchFunc) { + CommOS_Log(("%s: a NULL Dispatch handler was passed.\n", __FUNCTION__)); + return -1; + } + dispatch = dispatchFunc; + + if (intervalMillis == 0) { + intervalMillis = 4; + } + if ((dispatchInterval = msecs_to_jiffies(intervalMillis)) < 1) { + dispatchInterval = 1; + } + if (maxCycles > DISPATCH_MAX_CYCLES) { + dispatchMaxCycles = DISPATCH_MAX_CYCLES; + } else if (maxCycles > 0) { + dispatchMaxCycles = maxCycles; + } + CommOS_Debug(("%s: Interval millis %u (jif:%u).\n", __FUNCTION__, + intervalMillis, dispatchInterval)); + CommOS_Debug(("%s: Max cycles %u.\n", __FUNCTION__, dispatchMaxCycles)); + + numCpus = num_present_cpus(); + dispatchWQ = CreateWorkqueue(dispatchTaskName); + if (!dispatchWQ) { + CommOS_Log(("%s: Couldn't create %s task(s).\n", __FUNCTION__, + dispatchTaskName)); + return -1; + } + + if (aioTaskName) { + aioWQ = CreateWorkqueue(aioTaskName); + if (!aioWQ) { + CommOS_Log(("%s: Couldn't create %s task(s).\n", __FUNCTION__, + aioTaskName)); + DestroyWorkqueue(dispatchWQ); + return -1; + } + } else { + aioWQ = NULL; + } + + running = 1; + for (cpu = 0; cpu < numCpus; cpu++) { + CommOS_InitWork(&dispatchWorksNow[cpu], DispatchWrapper); + CommOS_InitWork(&dispatchWorks[cpu], DispatchWrapper); + rc = QueueDelayedWorkOn(cpu, dispatchWQ, + &dispatchWorks[cpu], + dispatchInterval); + if (rc != 0) { + CommOS_StopIO(); + return -1; + } + } + CommOS_Log(("%s: Created I/O task(s) successfully.\n", __FUNCTION__)); + return 0; +} + + +/** + * @brief Stops the base IO system. + * @sideeffects Dispatch threads, and if applicable, AIO threads are stopped. + */ + +void +CommOS_StopIO(void) +{ + int cpu; + + if (running) { + running = 0; + if (aioWQ) { + FlushWorkqueue(aioWQ); + DestroyWorkqueue(aioWQ); + aioWQ = NULL; + } + FlushWorkqueue(dispatchWQ); + for (cpu = 0; cpu < numCpus; cpu++) { + WaitForDelayedWork(&dispatchWorksNow[cpu]); + WaitForDelayedWork(&dispatchWorks[cpu]); + } + DestroyWorkqueue(dispatchWQ); + dispatchWQ = NULL; + CommOS_Log(("%s: I/O tasks stopped.\n", __FUNCTION__)); + } +} diff --git a/arch/arm/mvp/commkm/comm_os_linux.h b/arch/arm/mvp/commkm/comm_os_linux.h new file mode 100644 index 0000000..f92c8bd --- /dev/null +++ b/arch/arm/mvp/commkm/comm_os_linux.h @@ -0,0 +1,699 @@ +/* + * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications + * + * Copyright (C) 2010-2012 VMware, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#line 5 + +/** + * @file + * + * @brief Contains linux-specific type definitions and function declarations + */ + +#ifndef _COMM_OS_LINUX_H_ +#define _COMM_OS_LINUX_H_ + +#include <linux/types.h> +#include <linux/version.h> + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) +#error "Kernel versions lower than 2.6.20 are not supported" +#endif + +#include <linux/kernel.h> +#include <linux/workqueue.h> +#include <linux/sched.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/slab.h> + + +/* + * Type definitions. + */ + +typedef atomic_t CommOSAtomic; +typedef spinlock_t CommOSSpinlock; +typedef struct mutex CommOSMutex; +typedef wait_queue_head_t CommOSWaitQueue; +typedef struct delayed_work CommOSWork; +typedef void (*CommOSWorkFunc)(CommOSWork *work); +typedef struct list_head CommOSList; +typedef struct module *CommOSModule; + + +/* + * Initializers. + */ + +#define CommOSSpinlock_Define DEFINE_SPINLOCK + + +#define COMM_OS_DOLOG(...) printk(KERN_INFO __VA_ARGS__) + + +/** + * @brief Logs given arguments in debug builds. + */ + +#if defined(COMM_OS_DEBUG) + #define CommOS_Debug(args) COMM_OS_DOLOG args +#else + #define CommOS_Debug(args) +#endif + + +/** + * @brief Logs given arguments. + */ + +#define CommOS_Log(args) COMM_OS_DOLOG args + + +/** + * @brief Logs function name and location. + */ + +#if defined(COMM_OS_TRACE) +#define TRACE(ptr) \ + do { \ + CommOS_Debug(("%p:%s: at [%s:%d] with arg ptr [0x%p].\n", current, \ + __FUNCTION__, __FILE__, __LINE__, (ptr))); \ + } while (0) +#else +#define TRACE(ptr) +#endif + + +/** + * @brief Write atomic variable + * @param[in,out] atomic variable to write + * @param val new value + */ + +static inline void +CommOS_WriteAtomic(CommOSAtomic *atomic, + int val) +{ + atomic_set(atomic, val); +} + + +/** + * @brief Reads atomic variable + * @param atomic variable to read + * @return value + */ + +static inline int +CommOS_ReadAtomic(CommOSAtomic *atomic) +{ + return atomic_read(atomic); +} + + +/** + * @brief Atomically add value to atomic variable, return new value. + * @param[in,out] atomic variable + * @param val value to add + * @return new value + */ + +static inline int +CommOS_AddReturnAtomic(CommOSAtomic *atomic, + int val) +{ + return atomic_add_return(val, atomic); +} + + +/** + * @brief Atomically substract value from atomic variable, return new value. + * @param[in,out] atomic variable + * @param val value to substract + * @return new value + */ + +static inline int +CommOS_SubReturnAtomic(CommOSAtomic *atomic, + int val) +{ + return atomic_sub_return(val, atomic); +} + + +/** + * @brief Initializes a given lock. + * @param[in,out] lock lock to initialize + */ + +static inline void +CommOS_SpinlockInit(CommOSSpinlock *lock) +{ + spin_lock_init(lock); +} + + +/** + * @brief Locks given lock and disables bottom half processing. + * @param[in,out] lock lock to lock + */ + +static inline void +CommOS_SpinLockBH(CommOSSpinlock *lock) +{ + spin_lock_bh(lock); +} + + +/** + * @brief Attempts to lock the given lock and disable BH processing. + * @param[in,out] lock lock to lock + * @return zero if successful, non-zero otherwise + */ + +static inline int +CommOS_SpinTrylockBH(CommOSSpinlock *lock) +{ + return !spin_trylock_bh(lock); +} + + +/** + * @brief Unlocks given lock and re-enables BH processing. + * @param[in,out] lock lock to unlock + */ + +static inline void +CommOS_SpinUnlockBH(CommOSSpinlock *lock) +{ + spin_unlock_bh(lock); +} + + +/** + * @brief Locks the given lock. + * @param[in,out] lock lock to lock + */ + +static inline void +CommOS_SpinLock(CommOSSpinlock *lock) +{ + spin_lock(lock); +} + + +/** + * @brief Attempts to lock the given lock. + * @param[in,out] lock lock to try-lock + * @return zero if successful, non-zero otherwise + */ + +static inline int +CommOS_SpinTrylock(CommOSSpinlock *lock) +{ + return !spin_trylock(lock); +} + + +/** + * @brief Unlocks given lock. + * @param[in,out] lock lock to unlock + */ + +static inline void +CommOS_SpinUnlock(CommOSSpinlock *lock) +{ + spin_unlock(lock); +} + + +/** + * @brief Initializes given mutex. + * @param[in,out] mutex mutex to initialize + */ + +static inline void +CommOS_MutexInit(CommOSMutex *mutex) +{ + mutex_init(mutex); +} + + +/** + * @brief Acquires mutex. + * @param[in,out] mutex mutex to lock + * @return zero if successful, non-zero otherwise (interrupted) + */ + +static inline int +CommOS_MutexLock(CommOSMutex *mutex) +{ + return mutex_lock_interruptible(mutex); +} + + +/** + * @brief Acquires mutex in uninterruptible mode. + * @param[in,out] mutex mutex to lock + */ + +static inline void +CommOS_MutexLockUninterruptible(CommOSMutex *mutex) +{ + mutex_lock(mutex); +} + + +/** + * @brief Attempts to acquire given mutex. + * @param[in,out] mutex mutex to try-lock + * @return zero if successful, non-zero otherwise + */ + +static inline int +CommOS_MutexTrylock(CommOSMutex *mutex) +{ + return !mutex_trylock(mutex); +} + + +/** + * @brief Releases a given mutex. + * @param[in,out] mutex mutex to unlock + */ + +static inline void +CommOS_MutexUnlock(CommOSMutex *mutex) +{ + mutex_unlock(mutex); +} + + +/** + * @brief Initializes a wait queue. + * @param[in,out] wq workqueue to initialize + */ + +static inline void +CommOS_WaitQueueInit(CommOSWaitQueue *wq) +{ + init_waitqueue_head(wq); +} + + +/** + * @brief Puts the caller on a wait queue until either of the following occurs: + * - the condition function (predicate) evaluates to TRUE + * - the specified timeout interval elapsed + * - a signal is pending + * @param[in,out] wq wait queue to put item on + * @param cond predicate to test + * @param condArg1 argument 1 for cond + * @param condArg2 argument 2 for cond + * @param[in,out] timeoutMillis timeout interval in milliseconds + * @param interruptible enable/disable signal pending check + * @return 1 if condition was met + * 0 if the timeout interval elapsed + * <0, if a signal is pending or other error set by condition + * @sideeffect timeoutMillis is updated to time remaining + */ + +static inline int +CommOS_DoWait(CommOSWaitQueue *wq, + CommOSWaitConditionFunc cond, + void *condArg1, + void *condArg2, + unsigned long long *timeoutMillis, + int interruptible) +{ + int rc; + DEFINE_WAIT(wait); + long timeout; +#if defined(COMM_OS_LINUX_WAIT_WORKAROUND) + long tmpTimeout; + long retTimeout; + const unsigned int interval = 50; +#endif + + if (!timeoutMillis) { + return -1; + } + if ((rc = cond(condArg1, condArg2)) != 0) { + return rc; + } + +#if defined(COMM_OS_LINUX_WAIT_WORKAROUND) + timeout = msecs_to_jiffies(interval < *timeoutMillis ? + interval : (unsigned int)*timeoutMillis); + retTimeout = msecs_to_jiffies((unsigned int)(*timeoutMillis)); + + for (; retTimeout >= 0; ) { + prepare_to_wait(wq, &wait, + (interruptible?TASK_INTERRUPTIBLE:TASK_UNINTERRUPTIBLE)); + if ((rc = cond(condArg1, condArg2))) { + break; + } + if (interruptible && signal_pending(current)) { + rc = -EINTR; + break; + } + if ((tmpTimeout = schedule_timeout(timeout))) { + retTimeout -= (timeout - tmpTimeout); + } else { + retTimeout -= timeout; + } + if (retTimeout < 0) { + retTimeout = 0; + } + } + finish_wait(wq, &wait); + if (rc == 0) { + rc = cond(condArg1, condArg2); + if (rc && (retTimeout == 0)) { + retTimeout = 1; + } + } + *timeoutMillis = (unsigned long long)jiffies_to_msecs(retTimeout); +#else // !defined(COMM_OS_LINUX_WAIT_WORKAROUND) + timeout = msecs_to_jiffies((unsigned int)(*timeoutMillis)); + + for (;;) { + prepare_to_wait(wq, &wait, + (interruptible?TASK_INTERRUPTIBLE:TASK_UNINTERRUPTIBLE)); + if ((rc = cond(condArg1, condArg2)) != 0) { + break; + } + if (interruptible && signal_pending(current)) { + rc = -EINTR; + break; + } + if ((timeout = schedule_timeout(timeout)) == 0) { + rc = 0; + break; + } + } + finish_wait(wq, &wait); + if (rc == 0) { + rc = cond(condArg1, condArg2); + if (rc && (timeout == 0)) { + timeout = 1; + } + } + *timeoutMillis = (unsigned long long)jiffies_to_msecs(timeout); +#endif + + return rc; +} + + +/** + * @brief Puts the caller on a wait queue until either of the following occurs: + * - the condition function (predicate) evaluates to TRUE + * - the specified timeout interval elapsed + * - a signal is pending + * @param[in,out] wq wait queue to put item on + * @param cond predicate to test + * @param condArg1 argument 1 for cond + * @param condArg2 argument 2 for cond + * @param[in,out] timeoutMillis timeout interval in milliseconds + * @return 1 if condition was met + * 0 if the timeout interval elapsed + * <0, if a signal is pending or other error set by condition + * @sideeffect timeoutMillis is updated to time remaining + */ + +static inline int +CommOS_Wait(CommOSWaitQueue *wq, + CommOSWaitConditionFunc cond, + void *condArg1, + void *condArg2, + unsigned long long *timeoutMillis) +{ + return CommOS_DoWait(wq, cond, condArg1, condArg2, timeoutMillis, 1); +} + + +/** + * @brief Puts the caller on a wait queue until either of the following occurs: + * - the condition function (predicate) evaluates to TRUE + * - the specified timeout interval elapsed + * @param[in,out] wq wait queue to put item on + * @param cond predicate to test + * @param condArg1 argument 1 for cond + * @param condArg2 argument 2 for cond + * @param[in,out] timeoutMillis timeout interval in milliseconds + * @return 1 if condition was met + * 0 if the timeout interval elapsed + * <0, error set by condition + * @sideeffect timeoutMillis is updated to time remaining + */ + +static inline int +CommOS_WaitUninterruptible(CommOSWaitQueue *wq, + CommOSWaitConditionFunc cond, + void *condArg1, + void *condArg2, + unsigned long long *timeoutMillis) +{ + return CommOS_DoWait(wq, cond, condArg1, condArg2, timeoutMillis, 0); +} + + +/** + * @brief Wakes up task(s) waiting on the given wait queue. + * @param[in,out] wq wait queue. + */ + +static inline void +CommOS_WakeUp(CommOSWaitQueue *wq) +{ + wake_up(wq); +} + + +/** + * @brief Allocates kernel memory of specified size; does not sleep. + * @param size size to allocate. + * @return Address of allocated memory or NULL if the allocation fails. + */ + +static inline void * +CommOS_KmallocNoSleep(unsigned int size) +{ + return kmalloc(size, GFP_ATOMIC); +} + + +/** + * @brief Allocates kernel memory of specified size; may sleep. + * @param size size to allocate. + * @return Address of allocated memory or NULL if the allocation fails. + */ + +static inline void * +CommOS_Kmalloc(unsigned int size) +{ + return kmalloc(size, GFP_KERNEL); +} + + +/** + * @brief Frees previously allocated kernel memory. + * @param obj object to free. + */ + +static inline void +CommOS_Kfree(void *obj) +{ + if (obj) { + kfree(obj); + } +} + + +/** + * @brief Yields the current cpu to other runnable tasks. + */ + +static inline void +CommOS_Yield(void) +{ + cond_resched(); +} + + +/** + * @brief Gets the current time in milliseconds. + * @return Current time in milliseconds, with precision of at most one tick. + */ + +static inline unsigned long long +CommOS_GetCurrentMillis(void) +{ + return (unsigned long long)jiffies_to_msecs(jiffies); +} + + +/** + * @brief Initializes given list. + * @param list list to initialize. + */ + +static inline void +CommOS_ListInit(CommOSList *list) +{ + INIT_LIST_HEAD(list); +} + + +/** + * @brief Tests if list is empty. + * @param list list to test. + * @return non-zero if empty, zero otherwise. + */ + +#define CommOS_ListEmpty(list) list_empty((list)) + + +/** + * @brief Adds given element to beginning of list. + * @param list list to add to. + * @param elem element to add. + */ + +#define CommOS_ListAdd(list, elem) list_add((elem), (list)) + + +/** + * @brief Adds given element to end of list. + * @param list list to add to. + * @param elem element to add. + */ + +#define CommOS_ListAddTail(list, elem) list_add_tail((elem), (list)) + + +/** + * @brief Deletes given element from its list. + * @param elem element to delete. + */ + +#define CommOS_ListDel(elem) \ + do { \ + list_del((elem)); \ + INIT_LIST_HEAD((elem)); \ + } while (0) + + +/** + * @brief Iterates over a list. + * @param list list to iterate over. + * @param[out] item stores next element. + * @param itemListFieldName name in the item structure storing the list head. + */ + +#define CommOS_ListForEach(list, item, itemListFieldName) \ + list_for_each_entry((item), (list), itemListFieldName) + + +/** + * @brief Iterates safely over a list. + * @param list list to iterate over. + * @param[out] item stores next element. May be deleted in the loop. + * @param[out] tmpItem saves iteration element. + * @param itemListFieldName name in the item structure storing the list head. + */ + +#define CommOS_ListForEachSafe(list, item, tmpItem, itemListFieldName) \ + list_for_each_entry_safe((item), (tmpItem), (list), itemListFieldName) + + +/** + * @brief Combines two lists, adds second list to beginning of first one. + * @param list list to add to. + * @param list2 list to add. + */ + +#define CommOS_ListSplice(list, list2) list_splice((list2), (list)) + + +/** + * @brief Combines two lists, adds second list to end of first one. + * @param list list to add to. + * @param list2 list to add. + */ + +#define CommOS_ListSpliceTail(list, list2) list_splice_tail((list2), (list)) + + +/** + * @brief Gets current module handle. + * @return module handle. + */ + +static inline CommOSModule +CommOS_ModuleSelf(void) +{ + return THIS_MODULE; +} + + +/** + * @brief Retains module. + * @param[in,out] module to retain. + * @return zero if successful, non-zero otherwise. + */ + +static inline int +CommOS_ModuleGet(CommOSModule module) +{ + int rc = 0; + + if (!module) { + goto out; + } + if (!try_module_get(module)) { + rc = -1; + } + +out: + return rc; +} + + +/** + * @brief Releases module. + * @param[in,out] module to release. + */ + +static inline void +CommOS_ModulePut(CommOSModule module) +{ + if (module) { + module_put(module); + } +} + + +/** + * @brief Inserts r/w memory barrier. + */ + +#define CommOS_MemBarrier smp_mb + +#endif /* _COMM_OS_LINUX_H_ */ diff --git a/arch/arm/mvp/commkm/comm_os_mod_linux.c b/arch/arm/mvp/commkm/comm_os_mod_linux.c new file mode 100644 index 0000000..8470de6 --- /dev/null +++ b/arch/arm/mvp/commkm/comm_os_mod_linux.c @@ -0,0 +1,105 @@ +/* + * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications + * + * Copyright (C) 2010-2012 VMware, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#line 5 + +/** + * @file + * + * @brief Linux-specific module loading, unloading functions. + */ + +#include "comm_os.h" +#include "comm_os_mod_ver.h" + +#include <linux/moduleparam.h> + + +/* Module parameters -- passed as one 'name=value'-list string. */ + +static char modParams[256]; +module_param_string(COMM_OS_MOD_SHORT_NAME, modParams, sizeof modParams, 0644); + + +/** + * @brief Module initialization entry point. Calls the commOSModInit + * function pointer to perform upper layer initialization. + * @return zero if successful, non-zero otherwise. + */ + +static int __init +ModInit(void) +{ + int rc; + + if (!commOSModInit) { + CommOS_Log(("%s: Can't find \'init\' function for module \'" \ + COMM_OS_MOD_SHORT_NAME_STRING "\'.\n", __FUNCTION__)); + return -1; + } + + CommOS_Debug(("%s: Module parameters: [%s].\n", __FUNCTION__, modParams)); + + rc = (*commOSModInit)(modParams); + if (rc == 0) { + CommOS_Log(("%s: Module \'" COMM_OS_MOD_SHORT_NAME_STRING \ + "\' has been successfully initialized.\n", __FUNCTION__)); + } else { + CommOS_Log(("%s: Module \'" COMM_OS_MOD_SHORT_NAME_STRING \ + "\' could not be initialized [%d].\n", __FUNCTION__, rc)); + } + + return rc > 0 ? -rc : rc; +} + + +/** + * @brief Module exit function. Calls the commOSModExit function pointer + * to perform upper layer cleanup. + */ + +static void __exit +ModExit(void) +{ + if (!commOSModExit) { + CommOS_Log(("%s: Can't find \'fini\' function for module \'" \ + COMM_OS_MOD_SHORT_NAME_STRING "\'.\n", __FUNCTION__)); + return; + } + + (*commOSModExit)(); + CommOS_Log(("%s: Module \'" COMM_OS_MOD_SHORT_NAME_STRING \ + "\' has been stopped.\n", __FUNCTION__)); +} + + +module_init(ModInit); +module_exit(ModExit); + +/* Module information. */ +MODULE_AUTHOR("VMware, Inc."); +MODULE_DESCRIPTION(COMM_OS_MOD_NAME_STRING); +MODULE_VERSION(COMM_OS_MOD_VERSION_STRING); +MODULE_LICENSE("GPL v2"); +/* + * Starting with SLE10sp2, Novell requires that IHVs sign a support agreement + * with them and mark their kernel modules as externally supported via a + * change to the module header. If this isn't done, the module will not load + * by default (i.e., neither mkinitrd nor modprobe will accept it). + */ +MODULE_INFO(supported, "external"); diff --git a/arch/arm/mvp/commkm/comm_os_mod_ver.h b/arch/arm/mvp/commkm/comm_os_mod_ver.h new file mode 100644 index 0000000..059854c --- /dev/null +++ b/arch/arm/mvp/commkm/comm_os_mod_ver.h @@ -0,0 +1,38 @@ +/* + * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications + * + * Copyright (C) 2010-2012 VMware, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#line 5 + +/** + * @file + * + * @brief Version definitions for the Comm module. + */ + +#ifndef _COMM_OS_MOD_VER_H_ +#define _COMM_OS_MOD_VER_H_ + +#define COMM_OS_MOD_NAME_STRING "VMware communication module" +#define COMM_OS_MOD_SHORT_NAME comm +#define COMM_OS_MOD_SHORT_NAME_STRING "comm" + +#define COMM_OS_MOD_VERSION 1.0.0.0 +#define COMM_OS_MOD_VERSION_COMMAS 1,0,0,0 +#define COMM_OS_MOD_VERSION_STRING "1.0.0.0" + +#endif /* _COM_OS_MOD_VER_H_ */ diff --git a/arch/arm/mvp/commkm/comm_svc.c b/arch/arm/mvp/commkm/comm_svc.c new file mode 100644 index 0000000..18f62bd --- /dev/null +++ b/arch/arm/mvp/commkm/comm_svc.c @@ -0,0 +1,421 @@ +/* + * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications + * + * Copyright (C) 2010-2012 VMware, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#line 5 + +/** + * @file + * + * @brief Communication functions based on transport functionality. + */ + +#include "comm_os.h" +#include "comm_os_mod_ver.h" +#include "comm_svc.h" + + +/* + * Initialization of module entry and exit callbacks expected by module + * loading/unloading functions in comm_os. + */ + +static int Init(void *args); +static void Exit(void); + +COMM_OS_MOD_INIT(Init, Exit); + +static volatile int running; // Initialized and running. + + +/** + * @brief Allocates and initializes comm global state. + * Starts input dispatch and aio threads. + * @param argsIn arguments + * @return zero if successful, non-zero otherwise. + */ + +static int +Init(void *argsIn) +{ + int rc = -1; + unsigned int maxChannels = 8; + /* + * Infinite timeout, 1 polling cycle + * see kernel/time.c: msecs_to_jiffies() + */ + unsigned int pollingMillis = (unsigned int)-1; + unsigned int pollingCycles = 1; + const char *args = argsIn; + + if (args && *args) { + /* coverity[secure_coding] */ + sscanf(args, + "max_channels:%u,poll_millis:%u,poll_cycles:%u", + &maxChannels, &pollingMillis, &pollingCycles); + CommOS_Debug(("%s: arguments [%s].\n", __FUNCTION__, args)); + } + + rc = Comm_Init(maxChannels); + if (rc) { + goto out; + } + + rc = CommOS_StartIO(COMM_OS_MOD_SHORT_NAME_STRING "-disp", + Comm_DispatchAll, pollingMillis, pollingCycles, + COMM_OS_MOD_SHORT_NAME_STRING "-aio"); + if (rc) { + unsigned long long timeout = 0; + + Comm_Finish(&timeout); /* Nothing started, guaranteed to succeed. */ + goto out; + } + running = 1; + rc = 0; + +out: + return rc; +} + + +/** + * @brief Attempts to close all channels. + * @return zero if successful, non-zero otherwise. + */ + +static int +Halt(void) +{ + unsigned int maxTries = 10; + int rc = -1; + + if (!running) { + rc = 0; + goto out; + } + + for ( ; maxTries; maxTries--) { + unsigned long long timeout = 2000ULL; + + CommOS_Debug(("%s: Attempting to halt...\n", __FUNCTION__)); + if (!Comm_Finish(&timeout)) { + running = 0; + rc = 0; + break; + } + } + +out: + return rc; +} + + +/** + * @brief Stops the comm_rt module. + * If Halt() call successful, stops input dispatch and aio threads. + */ + +static void +Exit(void) +{ + if (!Halt()) { + CommOS_StopIO(); + } +} + + +/** + * @brief Registers an implementation block used when attaching to channels + * in response to transport attach events. + * @param impl implementation block. + * @return 0 if successful, non-zero otherwise. + */ + +int +CommSvc_RegisterImpl(const CommImpl *impl) +{ + return Comm_RegisterImpl(impl); +} + + +/** + * @brief Unregisters an implementation block used when attaching to channels + * in response to transport attach events. + * @param impl implementation block. + */ + +void +CommSvc_UnregisterImpl(const CommImpl *impl) +{ + Comm_UnregisterImpl(impl); +} + + +/** + * @brief Finds a free entry and initializes it with the information provided. + * May be called from BH. It doesn't call potentially blocking functions. + * @param transpArgs transport initialization arguments. + * @param impl implementation block. + * @param inBH non-zero if called in bottom half. + * @param[out] newChannel newly allocated channel. + * @return zero if successful, non-zero otherwise. + * @sideeffects Initializes the communications channel with given parameters + */ + +int +CommSvc_Alloc(const CommTranspInitArgs *transpArgs, + const CommImpl *impl, + int inBH, + CommChannel *newChannel) +{ + return Comm_Alloc(transpArgs, impl, inBH, newChannel); +} + + +/** + * @brief Zombifies a channel. May fail if channel isn't active. + * @param channel channel to zombify. + * @param inBH non-zero if called in bottom half. + * @return zero if channel zombified, non-zero otherwise. + */ + +int +CommSvc_Zombify(CommChannel channel, + int inBH) +{ + return Comm_Zombify(channel, inBH); +} + + +/** + * @brief Reports whether a channel is active. + * @param channel channel to report on. + * @return non-zero if channel active, zero otherwise. + */ + +int +CommSvc_IsActive(CommChannel channel) +{ + return Comm_IsActive(channel); +} + + +/** + * @brief Retrieves a channel's transport initialization arguments. + * It doesn't lock, the caller must ensure the channel may be accessed. + * @param channel CommChannel structure to get initialization arguments from. + * @return initialization arguments used to allocate/attach to channel. + */ + +CommTranspInitArgs +CommSvc_GetTranspInitArgs(CommChannel channel) +{ + return Comm_GetTranspInitArgs(channel); +} + + +/** + * @brief Retrieves upper layer state (pointer). It doesn't lock, the caller + * must ensure the channel may be accessed. + * @param channel CommChannel structure to get state from. + * @return pointer to upper layer state. + */ + +void * +CommSvc_GetState(CommChannel channel) +{ + return Comm_GetState(channel); +} + + +/** + * @brief Writes a fully formatted packet (containing payload data, if + * applicable) to the specified channel. + * + * The operation may block until enough write space is available, but no + * more than the specified interval. The operation either writes the full + * amount of bytes, or it fails. Warning: callers must _not_ use the + * _Lock/_Unlock functions to bracket calls to this function. + * @param[in,out] channel channel to write to. + * @param packet packet to write. + * @param[in,out] timeoutMillis interval in milliseconds to wait. + * @return number of bytes written, 0 if it times out, -1 error. + * @sideeffects Data may be written to the channel. + */ + +int +CommSvc_Write(CommChannel channel, + const CommPacket *packet, + unsigned long long *timeoutMillis) +{ + return Comm_Write(channel, packet, timeoutMillis); +} + + +/** + * @brief Writes a packet and associated payload data to the specified channel. + * + * The operation may block until enough write space is available, but not + * more than the specified interval. The operation either writes the full + * amount of bytes, or it fails. Users may call this function successively + * to write several packets from large {io|k}vecs. If that's the case, the + * packet header needs to be updated in between calls, for the different + * (total) lengths. Warning: callers must _not_ use the _Lock/_Unlock + * functions to bracket calls to this function. + * @param[in,out] channel the specified channel + * @param packet packet to write + * @param[in,out] vec kvec to write from + * @param[in,out] vecLen length of kvec + * @param[in,out] timeoutMillis interval in milliseconds to wait + * @param[in,out] iovOffset must be set to 0 before first call (internal cookie) + * @return number of bytes written, 0 if it timed out, -1 error + * @sideeffects data may be written to the channel + */ + +int +CommSvc_WriteVec(CommChannel channel, + const CommPacket *packet, + struct kvec **vec, + unsigned int *vecLen, + unsigned long long *timeoutMillis, + unsigned int *iovOffset) +{ + return Comm_WriteVec(channel, packet, vec, vecLen, timeoutMillis, iovOffset); +} + + +/** + * @brief Releases channel ref count. This function is exported for the upper + * layer's 'activateNtf' callback which may be run asynchronously. The + * callback is protected from concurrent channel releases until it calls + * this function. + * @param[in,out] channel CommChannel structure to release. + */ + +void +CommSvc_Put(CommChannel channel) +{ + Comm_Put(channel); +} + + +/** + * @brief Uses the read lock. This function is exported for the upper layer + * such that it can order acquisition of a different lock (socket) with + * the release of the dispatch lock. + * @param[in,out] channel CommChannel structure to unlock. + */ + +void +CommSvc_DispatchUnlock(CommChannel channel) +{ + Comm_DispatchUnlock(channel); +} + + +/** + * @brief Lock the channel. + * + * Uses the writer lock. This function is exported for the upper layer + * to ensure that channel isn't closed while updating the layer state. + * It also guarantees that if the lock is taken, the entry is either ACTIVE + * or ZOMBIE. Operations using this function are expected to be short, + * since unlike the _Write functions, these callers cannot be signaled. + * @param[in,out] channel CommChannel structure to lock. + * @return zero if successful, -1 otherwise. + */ + +int +CommSvc_Lock(CommChannel channel) +{ + return Comm_Lock(channel); +} + + +/** + * @brief Unlock the channel. + * + * Uses the writer lock. This function is exported for the upper layer + * to ensure that channel isn't closed while updating the layer state. + * See Comm_WriteLock for details). + * @param[in,out] channel CommChannel structure to unlock. + */ + +void +CommSvc_Unlock(CommChannel channel) +{ + Comm_Unlock(channel); +} + + +/** + * @brief Schedules a work item on the AIO thread(s). + * @param[in,out] work work item to be scheduled. + * @return zero if successful, -1 otherwise. + */ + +int +CommSvc_ScheduleAIOWork(CommOSWork *work) +{ + return CommOS_ScheduleAIOWork(work); +} + + +/** + * @brief Requests events be posted in-line after the function completes. + * @param channel channel object. + * @return current number of requests for inline event posting, or -1 on error. + */ + +unsigned int +CommSvc_RequestInlineEvents(CommChannel channel) +{ + return Comm_RequestInlineEvents(channel); +} + + +/** + * @brief Requests events be posted out-of-band after the function completes. + * @param channel channel object. + * @return current number of requests for inline event posting, or -1 on error. + */ + +unsigned int +CommSvc_ReleaseInlineEvents(CommChannel channel) +{ + return Comm_ReleaseInlineEvents(channel); +} + + +#if defined(__linux__) +EXPORT_SYMBOL(CommSvc_RegisterImpl); +EXPORT_SYMBOL(CommSvc_UnregisterImpl); +EXPORT_SYMBOL(CommSvc_Alloc); +EXPORT_SYMBOL(CommSvc_Zombify); +EXPORT_SYMBOL(CommSvc_IsActive); +EXPORT_SYMBOL(CommSvc_GetTranspInitArgs); +EXPORT_SYMBOL(CommSvc_GetState); +EXPORT_SYMBOL(CommSvc_Write); +EXPORT_SYMBOL(CommSvc_WriteVec); +EXPORT_SYMBOL(CommSvc_Put); +EXPORT_SYMBOL(CommSvc_DispatchUnlock); +EXPORT_SYMBOL(CommSvc_Lock); +EXPORT_SYMBOL(CommSvc_Unlock); +EXPORT_SYMBOL(CommSvc_ScheduleAIOWork); +EXPORT_SYMBOL(CommSvc_RequestInlineEvents); +EXPORT_SYMBOL(CommSvc_ReleaseInlineEvents); +#endif // defined(__linux__) diff --git a/arch/arm/mvp/commkm/comm_svc.h b/arch/arm/mvp/commkm/comm_svc.h new file mode 100644 index 0000000..c4f3292 --- /dev/null +++ b/arch/arm/mvp/commkm/comm_svc.h @@ -0,0 +1,71 @@ +/* + * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications + * + * Copyright (C) 2010-2012 VMware, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#line 5 + +/** + * @file + * + * @brief Communication functions exported by the comm_rt module. + */ + +#ifndef _COMM_SVC_H_ +#define _COMM_SVC_H_ + +#define INCLUDE_ALLOW_MODULE +#define INCLUDE_ALLOW_PV +#define INCLUDE_ALLOW_GPL +#include "include_check.h" + +#include "comm.h" + +int CommSvc_RegisterImpl(const CommImpl *impl); +void CommSvc_UnregisterImpl(const CommImpl *impl); +int CommSvc_Zombify(CommChannel channel, int inBH); +int CommSvc_IsActive(CommChannel channel); +CommTranspInitArgs CommSvc_GetTranspInitArgs(CommChannel channel); +void *CommSvc_GetState(CommChannel channel); +void CommSvc_Put(CommChannel channel); +void CommSvc_DispatchUnlock(CommChannel channel); +int CommSvc_Lock(CommChannel channel); +void CommSvc_Unlock(CommChannel channel); +int CommSvc_ScheduleAIOWork(CommOSWork *work); + +int +CommSvc_Alloc(const CommTranspInitArgs *transpArgs, + const CommImpl *impl, + int inBH, + CommChannel *newChannel); + +int +CommSvc_Write(CommChannel channel, + const CommPacket *packet, + unsigned long long *timeoutMillis); + +int +CommSvc_WriteVec(CommChannel channel, + const CommPacket *packet, + struct kvec **vec, + unsigned int *vecLen, + unsigned long long *timeoutMillis, + unsigned int *iovOffset); + +unsigned int CommSvc_RequestInlineEvents(CommChannel channel); +unsigned int CommSvc_ReleaseInlineEvents(CommChannel channel); + +#endif // _COMM_SVC_H_ diff --git a/arch/arm/mvp/commkm/comm_transp.h b/arch/arm/mvp/commkm/comm_transp.h new file mode 100644 index 0000000..6cc58ae --- /dev/null +++ b/arch/arm/mvp/commkm/comm_transp.h @@ -0,0 +1,90 @@ +/* + * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications + * + * Copyright (C) 2010-2012 VMware, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#line 5 + +/** + * @file + * + * @brief Generic shared memory transport API. + */ + +#ifndef _COMM_TRANSP_H_ +#define _COMM_TRANSP_H_ + +#define INCLUDE_ALLOW_PV +#define INCLUDE_ALLOW_MODULE +#define INCLUDE_ALLOW_MONITOR +#define INCLUDE_ALLOW_GPL +#include "include_check.h" + +/* + * Common shared memory identifier. + * External handle that makes sense to both hypervisor and guest. + */ + +#define COMM_TRANSP_ID_8_ANY ((unsigned char)-1) +#define COMM_TRANSP_ID_32_ANY ((unsigned int)-1) +#define COMM_TRANSP_ID_64_ANY ((unsigned long long)-1) + + +typedef struct CommTranspID { + union { + unsigned char d8[8]; + unsigned int d32[2]; + unsigned long long d64; + }; +} CommTranspID; + + +/* Basic initialization arguments. */ + +typedef enum CommTranspInitMode { + COMM_TRANSP_INIT_CREATE = 0x0, + COMM_TRANSP_INIT_ATTACH = 0x1 +} CommTranspInitMode; + +typedef struct CommTranspInitArgs { + unsigned int capacity; // Shared memory capacity. + unsigned int type; // Type / implementation using this area. + CommTranspID id; // ID (name) of shared memory area. + CommTranspInitMode mode; // Init mode (above). +} CommTranspInitArgs; + + +/** + * @brief Generate a type id from description (protocol) string. This function + * uses djb2, a string hashing algorithm by Dan Bernstein. + * (see http://www.cse.yorku.ca/~oz/hash.html) + * @param str string to hash + * @return 32-bit hash value + */ + +static inline unsigned int +CommTransp_GetType(const char *str) +{ + unsigned int hash = 5381; + int c; + + while ((c = *str++)) { + hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ + } + return hash; +} + +#endif // _COMM_TRANSP_H_ diff --git a/arch/arm/mvp/commkm/comm_transp_impl.h b/arch/arm/mvp/commkm/comm_transp_impl.h new file mode 100644 index 0000000..113cd21 --- /dev/null +++ b/arch/arm/mvp/commkm/comm_transp_impl.h @@ -0,0 +1,165 @@ +/* + * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications + * + * Copyright (C) 2010-2012 VMware, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#line 5 + +/** + * @file + * + * @brief Generic shared memory transport private API. + */ + +#ifndef _COMM_TRANSP_IMPL_H_ +#define _COMM_TRANSP_IMPL_H_ + +#define INCLUDE_ALLOW_PV +#define INCLUDE_ALLOW_MODULE +#define INCLUDE_ALLOW_MONITOR +#define INCLUDE_ALLOW_GPL +#include "include_check.h" + +#include "comm_transp.h" + + +/* Shared memory opaque descriptor/handle. Only meaningful locally. */ + +typedef struct CommTranspPriv *CommTransp; + + +/* Asynchronous signaling initialization arguments. */ + +typedef enum CommTranspIOEvent { + COMM_TRANSP_IO_DETACH = 0x0, + COMM_TRANSP_IO_IN = 0x1, + COMM_TRANSP_IO_OUT = 0x2, + COMM_TRANSP_IO_INOUT = 0x3 +} CommTranspIOEvent; + +typedef struct CommTranspEvent { + void (*ioEvent)(CommTransp transp, CommTranspIOEvent event, void *data); + void *ioEventData; +} CommTranspEvent; + + +/* + * Mechanism to detect and optionally attach to, created shared memory regions. + */ + +typedef struct CommTranspListener { + int (*probe)(CommTranspInitArgs *transpArgs, void *probeData); + void *probeData; +} CommTranspListener; + + + +/* + * Function prototypes. + */ + +int CommTranspEvent_Init(void); +void CommTranspEvent_Exit(void); +int CommTranspEvent_Process(CommTranspID *transpID, CommTranspIOEvent event); +int +CommTranspEvent_Raise(unsigned int peerEvID, + CommTranspID *transpID, + CommTranspIOEvent event); + +int CommTransp_Init(void); +void CommTransp_Exit(void); + +int CommTransp_Register(const CommTranspListener *listener); +void CommTransp_Unregister(const CommTranspListener *listener); +int +CommTransp_Notify(const CommTranspID *notificationCenterID, + CommTranspInitArgs *transpArgs); + +int +CommTransp_Open(CommTransp *transp, + CommTranspInitArgs *transpArgs, + CommTranspEvent *transpEvent); +void CommTransp_Close(CommTransp transp); + +int CommTransp_EnqueueSpace(CommTransp transp); +int CommTransp_EnqueueReset(CommTransp transp); +int CommTransp_EnqueueCommit(CommTransp transp); +int +CommTransp_EnqueueSegment(CommTransp transp, + const void *buf, + unsigned int bufLen); + +int CommTransp_DequeueSpace(CommTransp transp); +int CommTransp_DequeueReset(CommTransp transp); +int CommTransp_DequeueCommit(CommTransp transp); +int +CommTransp_DequeueSegment(CommTransp transp, + void *buf, + unsigned int bufLen); + +unsigned int CommTransp_RequestInlineEvents(CommTransp transp); +unsigned int CommTransp_ReleaseInlineEvents(CommTransp transp); + + +/** + * @brief Enqueues data into the transport object, data is available for + * reading immediately. + * @param transp handle to the transport object. + * @param buf bytes to enqueue. + * @param bufLen number of bytes to enqueue. + * @return number of bytes enqueued on success, < 0 otherwise. + */ + +static inline int +CommTransp_EnqueueAtomic(CommTransp transp, + const void *buf, + unsigned int bufLen) +{ + int rc; + + CommTransp_EnqueueReset(transp); + rc = CommTransp_EnqueueSegment(transp, buf, bufLen); + if (CommTransp_EnqueueCommit(transp)) { + rc = -1; + } + return rc; +} + + +/** + * @brief Dequeues data from the transport object into a buffer. + * @param transp handle to the transport object. + * @param[out] buf buffer to copy to. + * @param bufLen number of bytes to dequeue. + * @return number of bytes dequeued on success, < 0 otherwise, + */ + +static inline int +CommTransp_DequeueAtomic(CommTransp transp, + void *buf, + unsigned int bufLen) +{ + int rc; + + CommTransp_DequeueReset(transp); + rc = CommTransp_DequeueSegment(transp, buf, bufLen); + if (CommTransp_DequeueCommit(transp)) { + rc = -1; + } + return rc; +} + +#endif // _COMM_TRANSP_IMPL_H_ diff --git a/arch/arm/mvp/commkm/comm_transp_mvp.c b/arch/arm/mvp/commkm/comm_transp_mvp.c new file mode 100644 index 0000000..f755de9 --- /dev/null +++ b/arch/arm/mvp/commkm/comm_transp_mvp.c @@ -0,0 +1,944 @@ +/* + * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications + * + * Copyright (C) 2010-2012 VMware, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#line 5 + +/** + * @file + * + * @brief Generic shared memory transport API. + */ +#include <linux/wait.h> + +#include "comm_os.h" +#include "comm_transp_impl.h" + +#include "mvp_types.h" +#include "qp.h" + + +/* + * Opaque CommTransp structure. See comm_transp.h + */ + +struct CommTranspPriv { + QPHandle *qp; + CommTranspEvent event; + unsigned int peerEvID; + unsigned int writeSize; + unsigned int readSize; + uint32 backRef; + CommOSWork work; + CommOSAtomic raiseInline; +}; + +/* + * Transport table object accounting + */ + +typedef struct TranspTableEntry { + CommOSAtomic holds; + CommTransp transp; + CommOSWaitQueue wq; +} TranspTableEntry; + +TranspTableEntry transpTable[QP_MAX_QUEUE_PAIRS]; +static CommOSSpinlock_Define(transpTableLock); + +/** + * @brief Destroy the transport object + * @param transp transport object to destroy + * @sideeffects detaches from queue pair + */ + +static void +DestroyTransp(CommTransp transp) +{ + CommTranspID transpID; + int32 rc; + + if (!transp) { + CommOS_Debug(("Failed to close channel: Bad handle\n")); + return; + } + + CommOS_Log(("%s: Detaching channel [%u:%u]\n", + __FUNCTION__, + transp->qp->id.context, + transp->qp->id.resource)); + + transpID.d32[0] = transp->qp->id.context; + transpID.d32[1] = transp->qp->id.resource; + +#if !defined(COMM_BUILDING_SERVER) + /* + * Tell the host to detach, will block in the host + * until the host has unmapped memory. Once the + * host has unmapped, it is safe to free. + */ + CommTranspEvent_Raise(transp->peerEvID, + &transpID, + COMM_TRANSP_IO_DETACH); +#endif + + rc = QP_Detach(transp->qp); + +#if defined(COMM_BUILDING_SERVER) + /* + * Wake up waiters now that unmapping is complete + */ + CommOS_WakeUp(&transpTable[transp->backRef].wq); +#endif + + CommOS_Kfree(transp); + if (rc != QP_SUCCESS) { + CommOS_Log(("%s: Failed to detach. rc: %d\n", __FUNCTION__, rc)); + } else { + CommOS_Log(("%s: Channel detached.\n", __FUNCTION__)); + } +} + + +/** + * @brief Initialize the transport object table + */ + +static void +TranspTableInit(void) +{ + uint32 i; + CommOS_SpinLock(&transpTableLock); + for (i = 0; i < QP_MAX_QUEUE_PAIRS; i++) { + CommOS_WriteAtomic(&transpTable[i].holds, -1); + transpTable[i].transp = NULL; + } + CommOS_SpinUnlock(&transpTableLock); +} + + +/** + * @brief Add a transport object into the table + * @param transp handle to the transport object + * @return 0 on success, -1 otherwise + * @sideeffects increments entry refcount + */ + +static inline int32 +TranspTableAdd(CommTransp transp) +{ + uint32 i; + + if (!transp) { + return -1; + } + + CommOS_SpinLock(&transpTableLock); + for (i = 0; i < QP_MAX_QUEUE_PAIRS; i++) { + if ((transpTable[i].transp) == NULL) { + transpTable[i].transp = transp; + CommOS_WriteAtomic(&transpTable[i].holds, 1); + CommOS_WaitQueueInit(&transpTable[i].wq); + transp->backRef = i; + break; + } + } + CommOS_SpinUnlock(&transpTableLock); + + return 0; +} + +/** + * @brief retrieve a transport object and increment its ref count + * @param id transport id to retrieve + * @return transport object, or NULL if not found + * @sideeffects increments entry ref count + */ + +static inline CommTransp +TranspTableGet(CommTranspID *id) +{ + CommTransp transp; + uint32 i; + + if (!id) { + return NULL; + } + + for (i = 0; i < QP_MAX_QUEUE_PAIRS; i++) { + transp = transpTable[i].transp; + if (transp && + (transp->qp->id.context == id->d32[0]) && + (transp->qp->id.resource == id->d32[1])) { + CommOS_AddReturnAtomic(&transpTable[i].holds, 1); + return transp; + } + } + CommOS_Debug(("%s: couldn't find transport object\n", __FUNCTION__)); + + return NULL; +} + +/** + * @brief Puts back a previously TranspGet-ed transport object. + * @param transp the transport object. + * @sideeffects decrements the transport reference count. + * frees object if refcount now zero + */ + +static inline void +TranspTablePut(CommTransp transp) +{ + int32 holds; + int32 backRef; + if (!transp) { + return; + } + + backRef = transp->backRef; + BUG_ON(backRef >= QP_MAX_QUEUE_PAIRS); + + holds = CommOS_SubReturnAtomic(&transpTable[backRef].holds, 1); + if (holds > 0) { + return; + } + BUG_ON(holds < 0); + + CommOS_SpinLock(&transpTableLock); + CommOS_WriteAtomic(&transpTable[backRef].holds, -1); + transpTable[backRef].transp = NULL; + CommOS_SpinUnlock(&transpTableLock); + DestroyTransp(transp); +} + + +/** + * @brief Puts back a previously TranspGet-ed transport object. + * @param transp the transport object. + * @sideeffects decrements the transport reference count. + * asserts that remaining count > 0 + */ + +static inline void +TranspTablePutNF(CommTransp transp) +{ + int32 holds; + int32 backRef; + if (!transp) { + return; + } + + backRef = transp->backRef; + BUG_ON(backRef >= QP_MAX_QUEUE_PAIRS); + + holds = CommOS_SubReturnAtomic(&transpTable[backRef].holds, 1); + BUG_ON(holds <= 0); +} + + +/** + * @brief Raises INOUT event in-line or out-of-band. Note that this function + * expects the transport object to be held prior to being called. + * @param arg work item of transport object. + */ + +static void +RaiseEvent(CommOSWork *arg) +{ +#if !defined(__linux__) +#error "RaiseEvent() is only supported on linux. Port 'container_of'!" +#endif + CommTransp transp = container_of(arg, struct CommTranspPriv, work); + CommTranspID transpID = {{ + .d32 = { + [0] = transp->qp->id.context, + [1] = transp->qp->id.resource + } + }}; + + CommTranspEvent_Raise(transp->peerEvID, + &transpID, + COMM_TRANSP_IO_INOUT); + TranspTablePut(transp); +} + + +/** + * @brief Requests events be posted in-line after the function completes. + * @param transp transport object. + * @return current number of requests for inline event posting. + * @sideeffects posts an event on the first transition to in-line processing. + */ + +unsigned int +CommTransp_RequestInlineEvents(CommTransp transp) +{ + unsigned int res = CommOS_AddReturnAtomic(&transp->raiseInline, 1); + if (res == 1) { + /* On the first (effective) transition, make sure an event is raised. */ + + CommOS_AddReturnAtomic(&transpTable[transp->backRef].holds, 1); + RaiseEvent(&transp->work); + } + return res; +} + + +/** + * @brief Requests events be posted out-of-band after the function completes. + * @param transp transport object. + * @return current number of requests for inline event posting. + */ + +unsigned int +CommTransp_ReleaseInlineEvents(CommTransp transp) +{ + return CommOS_SubReturnAtomic(&transp->raiseInline, 1); +} + + +/* + * Comm Offload server callbacks. + */ + +#if defined(COMM_BUILDING_SERVER) + +#define COMM_MAX_LISTENERS QP_MAX_LISTENERS + +static int32 NotifyCB(const QPInitArgs *args); +static void DetachCB(void *data); + +static CommOSSpinlock_Define(listenersLock); +static CommTranspListener listeners[COMM_MAX_LISTENERS]; +static uint32 numListeners = 0; + + +/** + * @brief Notify callback when guests attach to queue pairs. Notifies any + * registered listeners (e.g. Comm layer). + * @param args Initialization arguments used by the guest to initialize + * its queue pair + * @return 0 on success, <0 otherwise. see qp.h for error codes. + */ + +static int32 +NotifyCB(const QPInitArgs* args) +{ + CommTranspInitArgs transpArgs; + uint32 i; + int32 rc = -1; + + if (!args) { + return QP_ERROR_INVALID_ARGS; + } + + transpArgs.id.d32[0] = args->id.context; + transpArgs.id.d32[1] = args->id.resource; + transpArgs.capacity = args->capacity; + transpArgs.type = args->type; + + CommOS_SpinLock(&listenersLock); + for (i = 0; i < COMM_MAX_LISTENERS; i++) { + if (listeners[i].probe && + (listeners[i].probe(&transpArgs, listeners[i].probeData) == 0)) { + CommOS_Debug(("%s: Delivered notify event to listener %u\n", + __FUNCTION__, + i)); + rc = 0; + break; + } + } + CommOS_SpinUnlock(&listenersLock); + return rc; +} + + +/** + * @brief Detach callback when guests detach from queue pairs. Notifies + * any registered listeners (e.g. CommComm layer). + * @param data Transport object passed when the callback was registered + */ + +static void +DetachCB(void *data) +{ + CommTransp transp = data; + if (!transp || !(transp->event.ioEvent)) { + return; + } + CommOS_Debug(("%s: Guest detached from [%u:%u]\n", + __FUNCTION__, + transp->qp->id.context, + transp->qp->id.resource)); + transp->event.ioEvent(transp, COMM_TRANSP_IO_DETACH, transp->event.ioEventData); +} +#endif + + +/** + * @brief Performs one-time initialization of mvp transport provider. + * @return 0 on success, < 0 otherwise. + */ + +int +CommTransp_Init(void) +{ + int32 rc; + TranspTableInit(); + + rc = CommTranspEvent_Init(); + +#if defined(COMM_BUILDING_SERVER) + if (!rc) { + QP_RegisterListener(NotifyCB); + } +#endif + return rc; +} + + +/** + * @brief Performs clean-up of mvp transport provider. + */ + +void +CommTransp_Exit(void) +{ + CommTranspEvent_Exit(); +#if defined(COMM_BUILDING_SERVER) + QP_UnregisterListener(NotifyCB); +#endif +} + +#if defined(COMM_BUILDING_SERVER) + +/** + * @brief Checks for a successful detach from Comm + * @param arg1 back reference index for channel in transport table + * @param arg2 ignored + * @return 1 if detach completed, 0 otherwise + */ + +static int +DetachCondition(void *arg1, void *arg2) +{ + uint32 backRef = (uint32)arg1; + + return (CommOS_ReadAtomic(&transpTable[backRef].holds) == -1); +} +#endif + + +/** + * @brief Processes a raised signal event. This is a callback function called + * from a comm_transp_ev plugin when a signal is received. Delivers an event + * to one or more channels. If id->d32[1] == COMM_TRANSP_ID_32_ANY, the event + * will be delivered to all registered channels associated with vmID + * id->d32[0]. + * @param id identifies a transport object to signal. + * @param event type of event. + * @return 0 if delivered to at least one channel, -1 on failure. + */ + +int +CommTranspEvent_Process(CommTranspID *id, + CommTranspIOEvent event) +{ + int rc = 0; + unsigned int delivered = 0; + unsigned int backRef; + int i = 0; + + CommTransp transp; + uint32 raiseOnAllChannels = (id->d32[1] == COMM_TRANSP_ID_32_ANY); + uint32 channels = raiseOnAllChannels ? QP_MAX_QUEUE_PAIRS : 1; + + while (channels--) { + if (raiseOnAllChannels) { + id->d32[1] = i++; + } + transp = TranspTableGet(id); + if (transp) { + if (transp->event.ioEvent) { + transp->event.ioEvent(transp, event, transp->event.ioEventData); + } + backRef = transp->backRef; + TranspTablePut(transp); + +#if defined(COMM_BUILDING_SERVER) + /* + * Wait for unmap on IO_DETACH, return to monitor. + */ + if (event == COMM_TRANSP_IO_DETACH) { + unsigned long long timeout = 30000; + + rc = CommOS_Wait(&transpTable[backRef].wq, + DetachCondition, + (void*)backRef, + NULL, + &timeout); + switch (rc) { + case 1: // Memory successfully unmapped + rc = 0; + break; + default: // Timed out or other error. + return -1; + } + } +#endif + delivered++; + } + } + + rc = (delivered > 0) ? 0 : -1; + return rc; +} + + +/** + * @brief Register a listener to be notified when guests attach to the Comm + * offload server + * @param listener the listener to be notified + * @return 0 on success, -1 on failure + */ + +int +CommTransp_Register(const CommTranspListener *listener) +{ + int32 rc = -1; +#if defined(COMM_BUILDING_SERVER) + uint32 i; + + if (!listener) { + return -1; + } + + CommOS_SpinLock(&listenersLock); + for (i = 0; i < COMM_MAX_LISTENERS; i++) { + if ((listeners[i].probe == NULL) && + (listeners[i].probeData == NULL)) { + listeners[i] = *listener; + numListeners++; + rc = 0; + CommOS_Debug(("%s: Registered listener %u\n", __FUNCTION__, i)); + break; + } + } + CommOS_SpinUnlock(&listenersLock); +#endif + return rc; +} + + +/** + * @brief Unregisters a listener from the transport event notification system + * @param listener listener to unregister + * @return 0 on success + */ + +void +CommTransp_Unregister(const CommTranspListener *listener) +{ +#if defined(COMM_BUILDING_SERVER) + uint32 i; + + if (!listener || !listener->probe) { + return; + } + + + CommOS_SpinLock(&listenersLock); + for (i = 0; i < COMM_MAX_LISTENERS; i++) { + if ((listeners[i].probe == listener->probe) && + (listeners[i].probeData == listener->probeData)) { + listeners[i].probe = NULL; + listeners[i].probeData = NULL; + numListeners--; + CommOS_Debug(("%s: Unregistered listener %u\n", __FUNCTION__, i)); + } + } + CommOS_SpinUnlock(&listenersLock); +#endif +} + + +/** + * @brief Allocates and initializes a transport object + * @param[in,out] transp handle to the transport to allocate and initialize + * @param transpArgs initialization arguments (see pvtcpTransp.h) + * @param transpEvent event callback to be delivered when events occur (e.g. + * detach events) + * @return 0 on success, <0 otherwise. See qp.h for error codes. + * @sideeffects Allocates memory + */ + +int +CommTransp_Open(CommTransp *transp, + CommTranspInitArgs *transpArgs, + CommTranspEvent *transpEvent) +{ + int32 rc = -1; + QPHandle *qp = NULL; + CommTransp transpOut = NULL; + QPInitArgs qpInitArgs; + + if (!transp || !transpArgs) { + return -1; + } + + CommOS_Log(("%s: Attaching to [%u:%u]. Capacity: %u\n", + __FUNCTION__, + transpArgs->id.d32[1], + transpArgs->id.d32[0], + transpArgs->capacity)); + + qpInitArgs.id.context = transpArgs->id.d32[0]; + qpInitArgs.id.resource = transpArgs->id.d32[1]; + qpInitArgs.capacity = transpArgs->capacity; + qpInitArgs.type = transpArgs->type; + + if (!(transpOut = CommOS_Kmalloc(sizeof *transpOut))) { + rc = -1; + goto out; + } + + /* + * Attach to the queue pair + */ + rc = QP_Attach(&qpInitArgs, &qp); + if (rc < 0) { + rc = -1; + goto out; + } + + transpOut->qp = qp; + + /* + * Reassign ID so Comm knows what ID was actually given + */ + transpArgs->id.d32[0] = qp->id.context; + transpArgs->id.d32[1] = qp->id.resource; + + if (transpEvent) { + transpOut->event = *transpEvent; + } else { + transpOut->event.ioEvent = NULL; + transpOut->event.ioEventData = NULL; + } + +#if defined(COMM_BUILDING_SERVER) + CommOS_Debug(("%s: Registering detach CB on id %u...\n", + __FUNCTION__, transpArgs->id.d32[1])); + QP_RegisterDetachCB(transpOut->qp, DetachCB, transpOut); +#endif + + transpOut->peerEvID = COMM_TRANSP_ID_32_ANY; + transpOut->writeSize = 0; + transpOut->readSize = 0; + CommOS_InitWork(&transpOut->work, RaiseEvent); + CommOS_WriteAtomic(&transpOut->raiseInline, 0); + + if (TranspTableAdd(transpOut)) { + CommOS_Log(("%s: Exceeded max limit of transport objects!\n", + __FUNCTION__)); + DestroyTransp(transpOut); + rc = -1; + goto out; + } + + *transp = transpOut; + rc = 0; + + CommOS_Log(("%s: Channel attached.\n", __FUNCTION__)); + +out: + if (rc && transpOut) { + CommOS_Log(("%s: Failed to attach: %d\n", __FUNCTION__, rc)); + CommOS_Kfree(transpOut); + } + + return rc; +} + + +/** + * @brief Tear down the transport channel, destroy the object if the refcount + * drops to zero + * @param transp handle to the transport channel + * @sideeffects decrements the entry's refcount + */ + +void +CommTransp_Close(CommTransp transp) { + if (!transp) { + return; + } + CommOS_FlushAIOWork(&transp->work); + TranspTablePut(transp); +} + + +/** + * @brief Returns available space for enqueue, in bytes + * @param transp handle to the transport object + * @return available space in the queue for enqueue operations, <0 + * on error conditions. see qp.h for error codes. + */ + +int +CommTransp_EnqueueSpace(CommTransp transp) +{ + if (!transp) { + return -1; + } + return QP_EnqueueSpace(transp->qp); +} + + +/** + * @brief Discards any pending enqueues + * @param transp handle to the transport object + * @return 0 on success, <0 otherwise. see qp.h for error codes + */ + +int +CommTransp_EnqueueReset(CommTransp transp) +{ + if (!transp) { + return -1; + } + transp->writeSize = 0; + return QP_EnqueueReset(transp->qp); +} + + +/** + * @brief Enqueues a segment of data into the transport object + * @param transp handle to the transport object + * @param buf data to enqueue + * @param bufLen number of bytes to enqueue + * @return number of bytes enqueued on success, <0 otherwise. see qp.h + * for error codes + */ + +int +CommTransp_EnqueueSegment(CommTransp transp, + const void *buf, + unsigned int bufLen) +{ + int rc; + + if (!transp) { + return -1; + } + rc = QP_EnqueueSegment(transp->qp, (void*)buf, bufLen); + if (rc >= 0) { + transp->writeSize += (unsigned int)rc; + } else { + transp->writeSize = 0; + } + return rc; +} + + +/** + * @brief Commits any previous EnqueueSegment operations to the transport + * object. + * @param transp handle to the transport object. + * @return 0 on success, < 0 otherwise. + */ + +int +CommTransp_EnqueueCommit(CommTransp transp) +{ + int rc; + + if (!transp) { + return -1; + } + + rc = QP_EnqueueCommit(transp->qp); + if (rc >= 0) { + const unsigned int fudge = 4; + int writable = CommTransp_EnqueueSpace(transp); + + if ((writable >= 0) && + ((transp->writeSize + (unsigned int)writable + fudge) >= + transp->qp->queueSize)) { + /* + * If bytes written since last commit + writable space 'almost' + * equal write queue size, then signal. The 'almost' fudge factor + * accounts for a possibly inaccurate CommTransp_EnqueueSpace() + * return value. Most of the time, this is inconsequential. In + * rare, borderline occasions, it results in a few extra signals. + * The scheme essentially means this: if this is the first packet + * to be write-committed, we signal. Otherwise, the remote end is + * supposed to keep going for as long as it can read. + * + */ + + BUG_ON(transp->backRef >= QP_MAX_QUEUE_PAIRS); + CommOS_AddReturnAtomic(&transpTable[transp->backRef].holds, 1); + if (CommOS_ReadAtomic(&transp->raiseInline)) { + RaiseEvent(&transp->work); + } else if (CommOS_ScheduleAIOWork(&transp->work)) { + TranspTablePutNF(transp); + } + } + } else { + rc = -1; + } + transp->writeSize = 0; + return rc; +} + + +/** + * @brief Returns any available bytes for dequeue + * @param transp handle to the transport object + * @return available bytes for dequeue, <0 otherwise. see qp.h for error codes + */ + +int +CommTransp_DequeueSpace(CommTransp transp) +{ + if (!transp) { + return -1; + } + return QP_DequeueSpace(transp->qp); +} + + +/** + * @brief Discards any pending dequeues + * @param transp handle to the transport object + * @return 0 on success, <0 otherwise, see qp.h for error codes + */ + +int +CommTransp_DequeueReset(CommTransp transp) +{ + if (!transp) { + return -1; + } + transp->readSize = 0; + return QP_DequeueReset(transp->qp); +} + + +/** + * @brief Dequeues a segment of data from the consumer queue into + * a buffer + * @param transp handle to the transport object + * @param[out] buf buffer to copy to + * @param bufLen number of bytes to dequeue + * @return number of bytes dequeued on success, <0 otherwise, + * see qp.h for error codes + */ + +int +CommTransp_DequeueSegment(CommTransp transp, + void *buf, + unsigned bufLen) +{ + int rc; + + if (!transp) { + return -1; + } + rc = QP_DequeueSegment(transp->qp, buf, bufLen); + if (rc >= 0) { + transp->readSize += (unsigned int)rc; + } else { + transp->readSize = 0; + } + return rc; +} + + +/** + * @brief Commits any previous DequeueSegment operations to the + * transport object. + * @param transp handle to the transport object. + * @return 0 on success, < 0 otherwise. + */ + +int +CommTransp_DequeueCommit(CommTransp transp) +{ + int rc; + + if (!transp) { + return -1; + } + rc = QP_DequeueCommit(transp->qp); + if (rc >= 0) { + int readable = CommTransp_DequeueSpace(transp); + const unsigned int limit = transp->qp->queueSize / 2; + + if ((readable >= 0) && + (transp->readSize + (unsigned int)readable >= limit) && + ((unsigned int)readable < limit)) { + /* + * Minimize the number of likely 'peer write OK' signalling: + * only do it, if reading crossed half-way down. + * + */ + + BUG_ON(transp->backRef >= QP_MAX_QUEUE_PAIRS); + CommOS_AddReturnAtomic(&transpTable[transp->backRef].holds, 1); + if (CommOS_ReadAtomic(&transp->raiseInline)) { + RaiseEvent(&transp->work); + } else if (CommOS_ScheduleAIOWork(&transp->work)) { + TranspTablePut(transp); + } + } + } else { + rc = -1; + } + /* coverity[deref_after_free] */ + transp->readSize = 0; + return rc; +} + + +/** + * @brief Notify any registered listeners for the given queue pair + * @param notificationCenterID noop, unused on MVP + * @param transpArgs initialization arguments used by the guest for this + * channel + * @sideeffects the host may attach to the queue pair + */ + +int +CommTransp_Notify(const CommTranspID *notificationCenterID, + CommTranspInitArgs *transpArgs) +{ + QPInitArgs args; + + args.id.context = transpArgs->id.d32[0]; + args.id.resource = transpArgs->id.d32[1]; + args.capacity = transpArgs->capacity; + args.type = transpArgs->type; + + CommOS_Debug(("%s: d32[0]: %u d32[1]: %u\n", + __FUNCTION__, + transpArgs->id.d32[0], + transpArgs->id.d32[1])); + QP_Notify(&args); + return 0; +} diff --git a/arch/arm/mvp/commkm/fatalerror.h b/arch/arm/mvp/commkm/fatalerror.h new file mode 100644 index 0000000..9676ff3 --- /dev/null +++ b/arch/arm/mvp/commkm/fatalerror.h @@ -0,0 +1,126 @@ +/* + * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications + * + * Copyright (C) 2010-2012 VMware, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#line 5 + +/** + * @file + * + * @brief fatal error handlers. They all post fatal errors regardless of build + * type. + */ + +#ifndef _FATALERROR_H +#define _FATALERROR_H + +#define INCLUDE_ALLOW_MVPD +#define INCLUDE_ALLOW_VMX +#define INCLUDE_ALLOW_MODULE +#define INCLUDE_ALLOW_MONITOR +#define INCLUDE_ALLOW_PV +#define INCLUDE_ALLOW_HOSTUSER +#define INCLUDE_ALLOW_GUESTUSER +#define INCLUDE_ALLOW_WORKSTATION +#define INCLUDE_ALLOW_GPL +#include "include_check.h" + +#include "mvp_compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum FECode { + FECodeMisc, ///< generic FATAL() call of sorts + FECodeOOM, ///< FATAL_OOM() call of sorts + FECodeAssert, ///< ASSERT() call of sorts + FECodeNR, ///< NOT_REACHED() call of sorts + FECodeNI, ///< NOT_IMPLEMENTED() call of sorts + FECodeNT, ///< NOT_TESTED() call of sorts + FECodeCF ///< COMPILE_FAIL() call of sorts +}; +typedef enum FECode FECode; + +#define FATAL() FatalError(__FILE__, __LINE__, FECodeMisc, 0, NULL) +#define FATAL_IF(x) do { if (UNLIKELY(x)) FATAL(); } while (0) +#define FATAL_OOM() FatalError(__FILE__, __LINE__, FECodeOOM, 0, NULL) +#define FATAL_OOM_IF(x) do { if (UNLIKELY(x)) FATAL_OOM(); } while (0) + +extern _Bool FatalError_hit; + +void NORETURN FatalError(char const *file, + int line, + FECode feCode, + int bugno, + char const *fmt, + ...) FORMAT(printf,5,6); + +#define FATALERROR_COMMON(printFunc, \ + printFuncV, \ + file, \ + line, \ + feCode, \ + bugno, \ + fmt) { \ + va_list ap; \ + \ + printFunc("FatalError: %s:%d, code %d, bugno %d\n", \ + file, line, feCode, bugno); \ + if (fmt != NULL) { \ + va_start(ap, fmt); \ + printFuncV(fmt, ap); \ + va_end(ap); \ + } \ + } + +#if defined IN_HOSTUSER || defined IN_GUESTUSER || defined IN_WORKSTATION + +#define FATALERROR_POSIX_USER \ +void \ +FatalError_VErrPrintf(const char *fmt, va_list ap) \ +{ \ + vfprintf(stderr, fmt, ap); \ +} \ +\ +void \ +FatalError_ErrPrintf(const char *fmt, ...) \ +{ \ + va_list ap; \ + va_start(ap, fmt); \ + FatalError_VErrPrintf(fmt, ap); \ + va_end(ap); \ +} \ +\ +void NORETURN \ +FatalError(char const *file, \ + int line, \ + FECode feCode, \ + int bugno, \ + const char *fmt, \ + ...) \ +{ \ + FATALERROR_COMMON(FatalError_ErrPrintf, FatalError_VErrPrintf, file, line, feCode, bugno, fmt); \ + exit(EXIT_FAILURE); \ +} +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/arch/arm/mvp/commkm/include_check.h b/arch/arm/mvp/commkm/include_check.h new file mode 100644 index 0000000..2eeafe7 --- /dev/null +++ b/arch/arm/mvp/commkm/include_check.h @@ -0,0 +1,18 @@ +/* + * Linux 2.6.32 and later Kernel module for Empty File Placeholder + * + * Copyright (C) 2010-2012 VMware, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ diff --git a/arch/arm/mvp/commkm/mksck.h b/arch/arm/mvp/commkm/mksck.h new file mode 100644 index 0000000..e9e10bc --- /dev/null +++ b/arch/arm/mvp/commkm/mksck.h @@ -0,0 +1,153 @@ +/* + * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications + * + * Copyright (C) 2010-2012 VMware, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#line 5 + +#ifndef _MKSCK_H +#define _MKSCK_H + +/** + * @file + * + * @brief The monitor-kernel socket interface definitions. + * + * The monitor kernel socket interface was created for (what the name + * says) communications between the monitor and host processes. On the + * monitor side a special API is introduced, see mksck_vmm.h. On the + * host side the API is the standard Berkeley socket interface. Host + * process to host process or monitor to monitor communication is not + * supported. + * + * A generic address consists of two 16 bit fields: the vm id and the + * port id. Both hosts (vmx) and monitors (vmm) get their vm id + * automatically. The host vm id is assigned at the time the host + * process opens the mvpkm file descriptor, while the monitor vm id is + * assigned when the vmx.c:SetupWorldSwitchPage() calls + * Mvpkm_SetupIds(). As a vmx may create multiple monitors to service + * an MP guest, a vmx vm id may be associated with multiple monitor vm + * ids. A monitor id, however, has a single associated vmx host id, + * the id of its canonical vmx. + * + * Sockets on the host get their addresses either by explicit user + * call (the bind command) or implicitly by (issuing a send command + * first). At an explicit bind the user may omit one or both fields by + * providing MKSCK_VMID_UNDEF/MKSCK_PORT_UNDEF respectively. An + * implicit bind behaves as if both fields were omitted in an explicit + * bind. The default value of the vmid field is the vmid computed from + * the thread group id while that of a port is a new number. It is not + * invalid to bind a host process socket with a vm id different from + * the vmid computed from the tgid. + * + * Sockets of the monitor are automatically assigned a vmid, that of their + * monitor, at the time of their creation. The port id can be assigned by the + * user or left to the implementation to assign an unused one (by specifying + * MKSCK_PORT_UNDEF at @ref Mksck_Open). + * + * Host unconnected sockets may receive from any monitor sender, may send to any + * monitor socket. A socket can be connected to a peer address, that enables the + * use of the send command. + * + * One of many special predefined port (both host and monitor) is + * MKSCK_PORT_MASTER. It is used for initialization. + * + * Monitor sockets have to send their peer address explicitly (by + * Mksck_SetPeer()) or implicitly by receiving first. After the peer + * is set, monitor sockets may send or receive only to/from their + * peer. + */ + + +#define INCLUDE_ALLOW_MVPD +#define INCLUDE_ALLOW_VMX +#define INCLUDE_ALLOW_MODULE +#define INCLUDE_ALLOW_MONITOR +#define INCLUDE_ALLOW_HOSTUSER +#define INCLUDE_ALLOW_GUESTUSER +#define INCLUDE_ALLOW_GPL +#include "include_check.h" + +#include "vmid.h" + +/* + * The interface limits the size of transferable packets. + */ +#define MKSCK_XFER_MAX 1024 + +#define MKSCK_ADDR_UNDEF (uint32)0xffffffff + +#define MKSCK_PORT_UNDEF (uint16)0xffff +#define MKSCK_PORT_MASTER (MKSCK_PORT_UNDEF-1) +#define MKSCK_PORT_HOST_FB (MKSCK_PORT_UNDEF-2) +#define MKSCK_PORT_BALLOON (MKSCK_PORT_UNDEF-3) +#define MKSCK_PORT_HOST_HID (MKSCK_PORT_UNDEF-4) +#define MKSCK_PORT_CHECKPOINT (MKSCK_PORT_UNDEF-5) +#define MKSCK_PORT_COMM_EV (MKSCK_PORT_UNDEF-6) +#define MKSCK_PORT_HIGH (MKSCK_PORT_UNDEF-7) + +#define MKSCK_VMID_UNDEF VMID_UNDEF +#define MKSCK_VMID_HIGH (MKSCK_VMID_UNDEF-1) + +#define MKSCK_DETACH 3 + +typedef uint16 Mksck_Port; +typedef VmId Mksck_VmId; + +/** + * @brief Page descriptor for typed messages. Each page describes a region of + * the machine address space with base mpn and size 2^(12 + order) bytes. + */ +typedef struct { + uint32 mpn : 20; ///< Base MPN of region described by page + uint32 order : 12; ///< Region is 2^(12 + order) bytes. +} Mksck_PageDesc; + +/** + * @brief Typed message template macro. Allows us to avoid having two message + * types, one with page descriptor vector (for VMM), one without (for + * VMX). + * + * @param type C type of uninterpreted component of the message (following the + * page descriptor vector). + * @param pages number of page descriptors in vector. + */ +#define MKSCK_DESC_TYPE(type,pages) \ + struct { \ + type umsg; \ + Mksck_PageDesc page[pages]; \ + } + +/** + * @brief The monitor kernel socket interface address format + */ +typedef union { + uint32 addr; ///< the address + struct { /* The address is decomposed to two shorts */ + Mksck_Port port; ///< port unique within a vmid + Mksck_VmId vmId; ///< unique vmid + }; +} Mksck_Address; + +static inline uint32 +Mksck_AddrInit(Mksck_VmId vmId, Mksck_Port port) +{ + Mksck_Address aa; + aa.vmId = vmId; + aa.port = port; + return aa.addr; +} +#endif diff --git a/arch/arm/mvp/commkm/mksck_sockaddr.h b/arch/arm/mvp/commkm/mksck_sockaddr.h new file mode 100644 index 0000000..82df240 --- /dev/null +++ b/arch/arm/mvp/commkm/mksck_sockaddr.h @@ -0,0 +1,50 @@ +/* + * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications + * + * Copyright (C) 2010-2012 VMware, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#line 5 + +/** + * @file + * + * @brief Host user space definitions for mksck sockets. + */ + +#ifndef _MKSCK_SOCKADDR_H_ +#define _MKSCK_SOCKADDR_H_ + +#define INCLUDE_ALLOW_MVPD +#define INCLUDE_ALLOW_VMX +#define INCLUDE_ALLOW_MODULE +#define INCLUDE_ALLOW_HOSTUSER +#define INCLUDE_ALLOW_GUESTUSER +#define INCLUDE_ALLOW_GPL +#include "include_check.h" + +#include "mksck.h" + +/* no one ever uses DECnet anymore? */ +#define AF_MKSCK AF_DECnet +#define PF_MKSCK PF_DECnet + +/* Address structure used by the host user socket interface. */ +struct sockaddr_mk { + sa_family_t mk_family; + Mksck_Address mk_addr; +}; + +#endif diff --git a/arch/arm/mvp/commkm/mvp.h b/arch/arm/mvp/commkm/mvp.h new file mode 100644 index 0000000..a57f8cc --- /dev/null +++ b/arch/arm/mvp/commkm/mvp.h @@ -0,0 +1,48 @@ +/* + * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications + * + * Copyright (C) 2010-2012 VMware, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#line 5 + +/** + * @file + * + * @brief top-level include for all basic includes. + * This file should not define anything of its own. + */ + +#ifndef _MVP_H +#define _MVP_H + +#define INCLUDE_ALLOW_MVPD +#define INCLUDE_ALLOW_VMX +#define INCLUDE_ALLOW_MODULE +#define INCLUDE_ALLOW_MONITOR +#define INCLUDE_ALLOW_PV +#define INCLUDE_ALLOW_HOSTUSER +#define INCLUDE_ALLOW_GUESTUSER +#define INCLUDE_ALLOW_WORKSTATION +#define INCLUDE_ALLOW_GPL +#include "include_check.h" + +#include "mvp_compiler.h" +#include "utils.h" +#include "mvp_assert.h" +#include "mvp_types.h" +#include "platdefx.h" + +#endif diff --git a/arch/arm/mvp/commkm/mvp_assert.h b/arch/arm/mvp/commkm/mvp_assert.h new file mode 100644 index 0000000..cbc5ed8 --- /dev/null +++ b/arch/arm/mvp/commkm/mvp_assert.h @@ -0,0 +1,125 @@ +/* + * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications + * + * Copyright (C) 2010-2012 VMware, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#line 5 + +/** + * @file + * + * @brief ASSERT() and related macros. + */ + +#ifndef _MVP_ASSERT_H +#define _MVP_ASSERT_H + +#define INCLUDE_ALLOW_MVPD +#define INCLUDE_ALLOW_VMX +#define INCLUDE_ALLOW_MODULE +#define INCLUDE_ALLOW_MONITOR +#define INCLUDE_ALLOW_PV +#define INCLUDE_ALLOW_HOSTUSER +#define INCLUDE_ALLOW_GUESTUSER +#define INCLUDE_ALLOW_WORKSTATION +#define INCLUDE_ALLOW_GPL +#include "include_check.h" + +#define ASSERT(_x) ASSERT_BUG((_x),0) + +#ifndef NDEBUG +#define ASSERT_BUG(_x,_tkt) do { \ + if (UNLIKELY(!(_x))) { \ + FatalError(__FILE__, __LINE__, FECodeAssert, _tkt, NULL); \ + } \ +} while (0) + +#define ASSERTF(_x, ...) do { \ + if (UNLIKELY(!(_x))) { \ + FatalError(__FILE__, \ + __LINE__, \ + FECodeAssert, \ + 0, \ + __VA_ARGS__); \ + } \ +} while (0) +#else + +#define ASSERT_BUG(_x,_tkt) (void)sizeof((int)(_x)) +#define ASSERTF(_x, ...) ASSERT_BUG(_x, 0) + +#endif + +/* + * Compile-time assertions. + * + * ASSERT_ON_COMPILE does not use the common + * switch (0) { case 0: case (e): ; } trick because some compilers (e.g. MSVC) + * generate code for it. + * + * The implementation uses both enum and typedef because the typedef alone is + * insufficient; gcc allows arrays to be declared with non-constant expressions + * (even in typedefs, where it makes no sense). + */ +#ifdef __COVERITY__ +#define ASSERT_ON_COMPILE(e) ASSERT(e) +#else +#define ASSERT_ON_COMPILE(e) \ + do { \ + enum { AssertOnCompileMisused = ((e) ? 1 : -1) }; \ + typedef char AssertOnCompileFailed[AssertOnCompileMisused]; \ + } while (0) +#endif + +/* + * To put an ASSERT_ON_COMPILE() outside a function, wrap it + * in MY_ASSERTS(). The first parameter must be unique in + * each .c file where it appears. For example, + * + * MY_ASSERTS(FS3_INT, + * ASSERT_ON_COMPILE(sizeof(FS3_DiskLock) == 128); + * ASSERT_ON_COMPILE(sizeof(FS3_DiskLockReserved) == DISK_BLOCK_SIZE); + * ASSERT_ON_COMPILE(sizeof(FS3_DiskBlock) == DISK_BLOCK_SIZE); + * ASSERT_ON_COMPILE(sizeof(Hardware_DMIUUID) == 16); + * ) + * + * Caution: ASSERT() within MY_ASSERTS() is silently ignored. + * The same goes for anything else not evaluated at compile time. + */ + +#define MY_ASSERTS(name, assertions) \ + static inline void name(void) { \ + assertions \ + } + +#define KNOWN_BUG(_tkt) + +#define NOT_IMPLEMENTED() NOT_IMPLEMENTED_JIRA(0) +#define NOT_IMPLEMENTED_JIRA(_tkt,...) FatalError(__FILE__, __LINE__, FECodeNI, _tkt, NULL) + +#define NOT_IMPLEMENTED_IF(_x) NOT_IMPLEMENTED_IF_JIRA((_x),0) +#define NOT_IMPLEMENTED_IF_JIRA(_x,_tkt,...) do { if (UNLIKELY(_x)) NOT_IMPLEMENTED_JIRA(_tkt); } while (0) +/* + * All sites tagged with this are @knownjira{MVP-1855}. + */ +#define NOT_IMPLEMENTEDF(...) FatalError(__FILE__, __LINE__, FECodeNI, 0, __VA_ARGS__) + +#define NOT_REACHED() FatalError(__FILE__, __LINE__, FECodeNR, 0, NULL) + +#include "fatalerror.h" +#include "nottested.h" + +#endif diff --git a/arch/arm/mvp/commkm/mvp_compiler.h b/arch/arm/mvp/commkm/mvp_compiler.h new file mode 100644 index 0000000..21af455 --- /dev/null +++ b/arch/arm/mvp/commkm/mvp_compiler.h @@ -0,0 +1,56 @@ +/* + * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications + * + * Copyright (C) 2010-2012 VMware, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#line 5 + +/** + * @file + * + * @brief Compiler-related definitions and directives. + */ + +#ifndef _MVP_COMPILER_H_ +#define _MVP_COMPILER_H_ + +#define INCLUDE_ALLOW_MVPD +#define INCLUDE_ALLOW_VMX +#define INCLUDE_ALLOW_MODULE +#define INCLUDE_ALLOW_MONITOR +#define INCLUDE_ALLOW_PV +#define INCLUDE_ALLOW_HOSTUSER +#define INCLUDE_ALLOW_GUESTUSER +#define INCLUDE_ALLOW_WORKSTATION +#define INCLUDE_ALLOW_GPL +#include "include_check.h" + +#ifdef __GNUC__ +#include "mvp_compiler_gcc.h" +#else /* __GNUC__ */ +#include "mvp_compiler_other.h" +#endif /* __GNUC__ */ + +/** + * @brief Find last set bit. + * + * @param n unsigned 32-bit integer. + * + * @return 0 if n == 0 otherwise 32 - the number of leading zeroes in n. + */ +#define FLS(n) (32 - CLZ(n)) + +#endif /// ifndef _MVP_COMPILER_H_ diff --git a/arch/arm/mvp/commkm/mvp_compiler_gcc.h b/arch/arm/mvp/commkm/mvp_compiler_gcc.h new file mode 100644 index 0000000..fbc96e3 --- /dev/null +++ b/arch/arm/mvp/commkm/mvp_compiler_gcc.h @@ -0,0 +1,87 @@ +/* + * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications + * + * Copyright (C) 2010-2012 VMware, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#line 5 + +/** + * @file + * + * @brief common definitions for GCC + */ + +#ifndef _MVP_COMPILER_GCC_H +#define _MVP_COMPILER_GCC_H + +#define INCLUDE_ALLOW_MVPD +#define INCLUDE_ALLOW_VMX +#define INCLUDE_ALLOW_MODULE +#define INCLUDE_ALLOW_MONITOR +#define INCLUDE_ALLOW_PV +#define INCLUDE_ALLOW_HOSTUSER +#define INCLUDE_ALLOW_GUESTUSER +#define INCLUDE_ALLOW_WORKSTATION +#define INCLUDE_ALLOW_GPL +#include "include_check.h" + +/** + * @brief Count leading zeroes. + * + * @param n unsigned 32-bit integer. + * + * @return 32 if n == 0 otherwise 31 - the bit position of the most significant 1 + * in n. + */ +#ifdef __COVERITY__ +static inline int +CLZ(unsigned int n) +{ + unsigned int r = 0; + + while (n) { + r++; + n >>= 1; + } + + return 32 - r; +} +#else +#define CLZ(n) __builtin_clz(n) +#endif + +#define PACKED __attribute__ ((packed)) +#define ALLOC __attribute__ ((malloc, warn_unused_result)) +#define UNUSED __attribute__ ((unused)) +#define PURE __attribute__ ((pure)) +#define WARN_UNUSED_RESULT __attribute__ ((warn_unused_result)) +#define FORMAT(x,y,z) __attribute__ ((format(x,y,z))) +#define LIKELY(x) __builtin_expect(!!(x), 1) +#define UNLIKELY(x) __builtin_expect((x), 0) + +/* + * For debug builds, we want to omit __attribute__((noreturn)) so that gcc will + * keep stack linkages and then we will have useful core dumps. For non-debug + * builds, we don't care about the stack frames and want the little bit of + * optimization that noreturn gives us. + */ +#if defined(__COVERITY__) || !defined(MVP_DEBUG) +#define NORETURN __attribute__((noreturn)) +#else +#define NORETURN +#endif + +#endif diff --git a/arch/arm/mvp/commkm/mvp_types.h b/arch/arm/mvp/commkm/mvp_types.h new file mode 100644 index 0000000..ba5c04c --- /dev/null +++ b/arch/arm/mvp/commkm/mvp_types.h @@ -0,0 +1,94 @@ +/* + * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications + * + * Copyright (C) 2010-2012 VMware, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#line 5 + +/** + * @file + * + * @brief basic type definitions. + * These may need to be conditionalized for different compilers/platforms. + */ + +#ifndef _MVPTYPES_H +#define _MVPTYPES_H + +#define INCLUDE_ALLOW_MVPD +#define INCLUDE_ALLOW_VMX +#define INCLUDE_ALLOW_MODULE +#define INCLUDE_ALLOW_MONITOR +#define INCLUDE_ALLOW_PV +#define INCLUDE_ALLOW_HOSTUSER +#define INCLUDE_ALLOW_GUESTUSER +#define INCLUDE_ALLOW_WORKSTATION +#define INCLUDE_ALLOW_GPL +#include "include_check.h" + +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint32; +typedef unsigned long long uint64; + +typedef signed char int8; +typedef short int16; +typedef int int32; +typedef long long int64; + +typedef uint32 CVA; // whatever we are compiling the code as +typedef uint32 GVA; // guest virtual addresses +typedef uint32 MVA; // monitor virtual addresses +typedef uint32 HKVA; // host kernel virtual addresses +typedef uint32 HUVA; // host user virtual addresses +typedef uint64 PA; // (guest) physical addresses (40-bit) +typedef uint32 MA; // (host) machine addresses + +typedef uint32 PPN; // PA/PAGE_SIZE +typedef uint32 MPN; // MA/PAGE_SIZE + +typedef uint64 cycle_t; + +/** + * @brief Page segment. + * + * Specifies a segment within a single page. + */ +typedef struct { + uint16 off; + uint16 len; +} PageSeg; + +/* + * GCC's argument checking for printf-like functions + * + * fmtPos is the position of the format string argument, beginning at 1 + * varPos is the position of the variable argument, beginning at 1 + */ + +#if defined(__GNUC__) +# define PRINTF_DECL(fmtPos, varPos) __attribute__((__format__(__printf__, fmtPos, varPos))) +#else +# define PRINTF_DECL(fmtPos, varPos) +#endif + +#if defined(__GNUC__) +# define SCANF_DECL(fmtPos, varPos) __attribute__((__format__(__scanf__, fmtPos, varPos))) +#else +# define SCANF_DECL(fmtPos, varPos) +#endif + +#endif /* _MVPTYPES_H */ diff --git a/arch/arm/mvp/commkm/mvpkm_comm_ev.h b/arch/arm/mvp/commkm/mvpkm_comm_ev.h new file mode 100644 index 0000000..b220a9b --- /dev/null +++ b/arch/arm/mvp/commkm/mvpkm_comm_ev.h @@ -0,0 +1,53 @@ +/* + * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications + * + * Copyright (C) 2010-2012 VMware, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#line 5 + +/** + * @file + * + * @brief mvpkm kernel hooks for comm event signaling + */ + +#ifndef _MVPKM_COMM_EV_H +#define _MVPKM_COMM_EV_H + +extern int (*CommTranspEvProcess)(CommTranspID* id, CommTranspIOEvent event); + +/** + * @brief Forward any guest signal requests to the commkm module + * @param id transport channel id + * @param event comm event type + */ + +static inline void +Mvpkm_CommEvSignal(CommTranspID *id, CommTranspIOEvent event) +{ + if (CommTranspEvProcess) { + CommTranspEvProcess(id, event); + } +} + +void +Mvpkm_CommEvRegisterProcessCB(int (*commProcessFunc)(CommTranspID*, + CommTranspIOEvent)); +void Mvpkm_CommEvUnregisterProcessCB(void); + + + +#endif diff --git a/arch/arm/mvp/commkm/nottested.h b/arch/arm/mvp/commkm/nottested.h new file mode 100644 index 0000000..c5c1e26 --- /dev/null +++ b/arch/arm/mvp/commkm/nottested.h @@ -0,0 +1,54 @@ +/* + * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications + * + * Copyright (C) 2010-2012 VMware, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#line 5 + +/** + * @file + * + * @brief NOT_TESTED() and related. + */ + +#ifndef _NOTTESTED_H +#define _NOTTESTED_H + +#define INCLUDE_ALLOW_MVPD +#define INCLUDE_ALLOW_VMX +#define INCLUDE_ALLOW_MODULE +#define INCLUDE_ALLOW_MONITOR +#define INCLUDE_ALLOW_PV +#define INCLUDE_ALLOW_HOSTUSER +#define INCLUDE_ALLOW_GUESTUSER +#define INCLUDE_ALLOW_WORKSTATION +#define INCLUDE_ALLOW_GPL +#include "include_check.h" + +#include <stdbool.h> + +#ifdef NOT_TESTED_ENABLED +#define NotTestedEnabled true +#else +#define NotTestedEnabled false +#endif + +#define NOT_TESTED() NOT_TESTED_JIRA(0) +#define NOT_TESTED_JIRA(_tkt,...) NotTested(_tkt, __FILE__, __LINE__) + +void NotTested(int tkt, char const *file, int line); + +#endif diff --git a/arch/arm/mvp/commkm/platdefx.h b/arch/arm/mvp/commkm/platdefx.h new file mode 100644 index 0000000..42953e6 --- /dev/null +++ b/arch/arm/mvp/commkm/platdefx.h @@ -0,0 +1,67 @@ +/* + * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications + * + * Copyright (C) 2010-2012 VMware, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#line 5 + +/** + * @file + * + * @brief Basic platform definitions needed various places. + */ + +#ifndef _PLATDEFX_H +#define _PLATDEFX_H + +#define INCLUDE_ALLOW_MVPD +#define INCLUDE_ALLOW_VMX +#define INCLUDE_ALLOW_MODULE +#define INCLUDE_ALLOW_MONITOR +#define INCLUDE_ALLOW_PV +#define INCLUDE_ALLOW_HOSTUSER +#define INCLUDE_ALLOW_GUESTUSER +#define INCLUDE_ALLOW_WORKSTATION +#define INCLUDE_ALLOW_GPL +#include "include_check.h" + +#define PAGE_ORDER 12 + +#ifndef PAGE_SIZE +#define PAGE_SIZE (1UL << PAGE_ORDER) +#endif +#if PAGE_SIZE != 4096 +#error bad page size PAGE_SIZE +#endif + +#define PA_2_PPN(_pa) ((_pa) / PAGE_SIZE) +#define PPN_2_PA(_ppn) ((_ppn) * PAGE_SIZE) + +#define VMM_DOMAIN 0x0 +#define VMM_DOMAIN_NO_ACCESS 0x3 +#define VMM_DOMAIN_CLIENT 0x1 +#define VMM_DOMAIN_MANAGER 0x4 + +#define INVALID_CVA (-(CVA)1) +#define INVALID_GVA (-(GVA)1) +#define INVALID_MVA (-(MVA)1) +#define INVALID_HKVA (-(HKVA)1) +#define INVALID_HUVA (-(HUVA)1) + +#define INVALID_MPN (((MPN)-1) >> ARM_L2D_SMALL_ORDER) +#define INVALID_PPN (((PPN)-1) >> ARM_L2D_SMALL_ORDER) + +#endif diff --git a/arch/arm/mvp/commkm/qp.h b/arch/arm/mvp/commkm/qp.h new file mode 100644 index 0000000..d4a50ec --- /dev/null +++ b/arch/arm/mvp/commkm/qp.h @@ -0,0 +1,332 @@ +/* + * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications + * + * Copyright (C) 2010-2012 VMware, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#line 5 + +/** + * @file + * + * @brief MVP Queue Pairs function and structure declarations + * + * MVP Queue Pairs: + * + * Queue pairs are intended to be a generic bulk data transport mechanism + * between the guest and host kernels. The queue pair abstraction is based + * on two ring buffers (queues) placed on a shared memory region mapped + * into both guest and host kernel address spaces. + * + * NOTE: Queue pairs are SINGLE-READER, SINGLE-WRITER. Any caller is + * responsible for multi-reader/writer serialization!!! + * + * There are a maximum of QP_MAX_QUEUE_PAIRS in the system, with a maximum + * size of QP_MAX_CAPACITY per pair. Each queue pair is identified by + * an ID. + * + * Each peer follows a producer-consumer model in which one side is the + * producer on one queue, and the other side is the consumer on that queue + * (and vice-versa for its pair). + * + * Data is enqueued and dequeued into the pair in transactional stages, + * meaning each enqueue/dequeue can be followed by zero or more + * enqueue/dequeues, but the enqueue/dequeue is not visible to the peer + * until it has been committed with the *Commit() function. + * In PVTCP, for example, this is used to enqueue a short header, then + * followed by 'segments' of iovecs, then followed by a commit. This + * model prevents a peer from reading the header, expecting a payload, + * but not being able to read the payload because it hasn't been + * enqueued yet. + * + * Queue Pair setup: + * + * Before data can be passed, the guest and host kernel must perform + * the following connection handshake: + * + * 1). A host kernel service registers a listener with the queue pair + * subsystem with a callback to be called when guests create + * and attach to a shared memory region. + * + * 2). Guest initiates an QP_Attach() operation to a shared memory region + * keyed by ID. This step allocates memory, maps it into the host + * address space, and optionally notifies any host services who are + * listening for attach requests from the guest (see previous step). + * Host listeners are provided with a copy of the initialization + * arguments used by the guest (id, size, service type). All registered + * listeners are iterated over until one of them handles the attach + * request and acknowledges with QP_SUCCESS. + * + * 3). The registered host callback is called, notifying the host that + * the guest has attached. + * + * 4). The host can now QP_Attach() to the shared memory region with the same + * arguments as the guest. The queue pair is now well formed and enqueues + * and dequeues can proceed on either side. + * + * Queue Pair teardown: + * + * 1). As before, teardowns are initiated by the guest. Hosts can register + * a callback to be called upon detach. Guests initiate a teardown + * through a call to QP_Detach(). + * + * 2). Registered hosts are notified through the aforementioned callback. + * 3). The host service can call QP_Detach() at its own leisure. Memory + * is freed, the queue pair is destroyed. + * + * If at any point the guest unexpectedly shuts down, the host will be + * notified at monitor shutdown time. Memory is freed, and the queue + * pair is destroyed. + * + */ + +#ifndef _QP_H +#define _QP_H + +#define INCLUDE_ALLOW_MODULE +#define INCLUDE_ALLOW_MONITOR +#define INCLUDE_ALLOW_PV +#define INCLUDE_ALLOW_GPL +#include "include_check.h" + +//#define QP_DEBUG 1 + +typedef enum QPState { + QP_STATE_FREE = 0x1, ///< No peers, not memory-backed + QP_STATE_CONNECTED, ///< Both peers attached , memory backed + QP_STATE_GUEST_ATTACHED, ///< Guest allocated memory, host not yet attached + QP_STATE_MAX // leave this at the end! +} QPState; + +typedef struct QPId { + uint32 context; + uint32 resource; +} QPId; + +/* + * Initialization arguments for each queue pair + */ +typedef struct QPInitArgs { + QPId id; ///< Shared memory region ID + uint32 capacity; ///< Total size of shared region in bytes + uint32 type; ///< Type of queue pair (PVTCP, other)... +} QPInitArgs; + +/* + * Placed on the shared region, two per region + */ +typedef struct QHandle { + volatile uint32 head; ///< queue head offset + volatile uint32 tail; ///< queue tail offset + volatile uint32 phantom_head; ///< queue shadow head offset + volatile uint32 phantom_tail; ///< queue shadow tail offset + uint8 data[0]; ///< start of data, runs off + // the struct +} QHandle; + +/* + * Local to each peer + */ +typedef struct QPHandle { + QPId id; ///< shared memory region ID + uint32 capacity; ///< size of region in bytes + QHandle *produceQ; ///< producer queue + QHandle *consumeQ; ///< consumer queue + uint32 queueSize; ///< size of each queue in bytes + uint32 type; ///< type of queue pair + + /* + * Following fields unused by guest + */ + QPState state; + void (*peerDetachCB)(void* data); ///< detach notification callback + void *detachData; ///< data for the detach cb + struct page **pages; ///< page pointers for shared region +} QPHandle; + +/* + * QP Error codes + */ +#define QP_SUCCESS 0 +#define QP_ERROR_NO_MEM (-1) +#define QP_ERROR_INVALID_HANDLE (-2) +#define QP_ERROR_INVALID_ARGS (-3) +#define QP_ERROR_ALREADY_ATTACHED (-4) + +/* + * Hard-coded limits + */ +#define QP_MIN_CAPACITY (PAGE_SIZE * 2) +#define QP_MAX_CAPACITY (1024*1024) // 1M +#define QP_MAX_QUEUE_PAIRS 32 +#define QP_MAX_ID QP_MAX_QUEUE_PAIRS +#define QP_MAX_LISTENERS QP_MAX_QUEUE_PAIRS +#define QP_MAX_PAGES (QP_MAX_CAPACITY/PAGE_SIZE) // 256 pages + +#define QP_INVALID_ID 0xFFFFFFFF +#define QP_INVALID_SIZE 0xFFFFFFFF +#define QP_INVALID_REGION 0xFFFFFFFF +#define QP_INVALID_TYPE 0xFFFFFFFF + +#ifdef __KERNEL__ +/** + * @brief Utility function to sanity check arguments + * @param args argument structure to check + * @return true if arguments are sane, false otherwise + */ +static inline +_Bool QP_CheckArgs(QPInitArgs *args) +{ + if (!args || + !is_power_of_2(args->capacity) || + (args->capacity < QP_MIN_CAPACITY) || + (args->capacity > QP_MAX_CAPACITY) || + !(args->id.resource < QP_MAX_ID || args->id.resource == QP_INVALID_ID) || + (args->type == QP_INVALID_TYPE)) { + return false; + } else { + return true; + } +} +#endif + + +/** + * @brief Utility function to sanity check a queue pair handle + * @param qp handle to the queue pair + * @return true if the handle is sane, false otherwise + */ +static inline +_Bool QP_CheckHandle(QPHandle *qp) +{ +#ifdef MVP_DEBUG + if (!(qp) || + !(qp->produceQ) || + !(qp->consumeQ) || + (qp->state >= (uint32)QP_STATE_MAX) || + !(qp->queueSize < (QP_MAX_CAPACITY/2))) { + return false; + } else { + return true; + } +#else + return true; +#endif +} + + +/** + * @brief Initializes an invalid handle + * @param[in, out] qp handle to the queue pair + */ +static inline void +QP_MakeInvalidQPHandle(QPHandle *qp) +{ + if (!qp) { + return; + } + + qp->id.context = QP_INVALID_ID; + qp->id.resource = QP_INVALID_ID; + qp->capacity = QP_INVALID_SIZE; + qp->produceQ = NULL; + qp->consumeQ = NULL; + qp->queueSize = QP_INVALID_SIZE; + qp->type = QP_INVALID_TYPE; + qp->state = QP_STATE_FREE; + qp->peerDetachCB = NULL; + qp->detachData = NULL; +} + +/* + * Host only + */ +typedef int32 (*QPListener)(const QPInitArgs*); +int32 QP_RegisterListener(const QPListener); +int32 QP_UnregisterListener(const QPListener); +int32 QP_RegisterDetachCB(QPHandle *qp, void (*callback)(void*), void *data); + + +/* + * Host and guest specific implementations, see qp_host.c and qp_guest.c + */ +int32 QP_Attach(QPInitArgs *args, QPHandle** qp); +int32 QP_Detach(QPHandle* qp); +int32 QP_Notify(QPInitArgs *args); + +/* + * Common implementation, see qp_common.c + */ +int32 QP_EnqueueSpace(QPHandle *qp); +int32 QP_EnqueueSegment(QPHandle *qp, const void *buf, size_t length); +int32 QP_EnqueueCommit(QPHandle *qp); +int32 QP_EnqueueReset(QPHandle *qp); + +static inline int32 +QP_EnqueueAtomic(QPHandle *qp, const void *buf, size_t length) +{ + int32 rc; + QP_EnqueueReset(qp); + rc = QP_EnqueueSegment(qp, buf, length); + if (rc < 0) { + return rc; + } else { + QP_EnqueueCommit(qp); + } + return rc; +} + +int32 QP_DequeueSpace(QPHandle *qp); +int32 QP_DequeueSegment(QPHandle *qp, const void *buf, size_t length); +int32 QP_DequeueReset(QPHandle *qp); +int32 QP_DequeueCommit(QPHandle *qp); + +static inline int32 +QP_DequeueAtomic(QPHandle *qp, const void *buf, size_t length) +{ + int32 rc; + QP_DequeueReset(qp); + rc = QP_DequeueSegment(qp, buf, length); + if (rc < 0) { + return rc; + } else { + QP_DequeueCommit(qp); + } + return rc; +} + +/* + * HVC methods and signatures + */ +#define MVP_QP_SIGNATURE 0x53525051 ///< 'QPRS' +#define MVP_QP_ATTACH (MVP_OBJECT_CUSTOM_BASE + 0) ///< attach to a queue pair +#define MVP_QP_DETACH (MVP_OBJECT_CUSTOM_BASE + 1) ///< detach from a queue pair +#define MVP_QP_NOTIFY (MVP_OBJECT_CUSTOM_BASE + 2) ///< notify host of attach +#define MVP_QP_LAST (MVP_OBJECT_CUSTOM_BASE + 3) ///< Number of methods + +/* + * Debug macros + */ +#ifdef QP_DEBUG + #ifdef IN_MONITOR + #define QP_DBG(...) Log(__VA_ARGS__) + #else + #define QP_DBG(...) printk(KERN_INFO __VA_ARGS__) + #endif +#else + #define QP_DBG(...) +#endif + +#endif diff --git a/arch/arm/mvp/commkm/utils.h b/arch/arm/mvp/commkm/utils.h new file mode 100644 index 0000000..b5f1e18 --- /dev/null +++ b/arch/arm/mvp/commkm/utils.h @@ -0,0 +1,172 @@ +/* + * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications + * + * Copyright (C) 2010-2012 VMware, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#line 5 + +/** + * @file + * + * @brief General architecture-independent definitions, typedefs, and macros. + */ + +#ifndef _UTILS_H +#define _UTILS_H + +#define INCLUDE_ALLOW_MVPD +#define INCLUDE_ALLOW_VMX +#define INCLUDE_ALLOW_MODULE +#define INCLUDE_ALLOW_MONITOR +#define INCLUDE_ALLOW_PV +#define INCLUDE_ALLOW_HOSTUSER +#define INCLUDE_ALLOW_GUESTUSER +#define INCLUDE_ALLOW_WORKSTATION +#define INCLUDE_ALLOW_GPL +#include "include_check.h" + +#define MAX_FILENAME 128 + +// Round address up to given size boundary +// Note: ALIGN() conflicts with Linux + +#define MVP_ALIGN(_v, _n) (((_v) + (_n) - 1) & -(_n)) + +#define ALIGNVA(_addr, _size) MVP_ALIGN(_addr, _size) + +#define alignof(t) offsetof(struct { char c; typeof(t) x; }, x) + +#define MIN(x,y) ((x) < (y) ? (x) : (y)) +#define MAX(x,y) ((x) > (y) ? (x) : (y)) + +#ifndef NULL +#define NULL ((void *)0) +#endif + +#define KB(_X_) ((_X_)*1024U) +#define MB(_X_) (KB(_X_)*1024) +#define GB(_X_) (MB(_X_)*1024) + +#define NELEM(x) (sizeof(x)/sizeof((x)[0])) + +/* + * x in [low,high) + * args evaluated once + */ +#define RANGE(x,low,high) \ + ({ \ + typeof(x) _x = (x); \ + typeof(x) _low = (typeof(x))(low); \ + typeof(x) _high =(typeof(x))(high); \ + (_Bool)( (_low <= _x) && (_x < _high)); \ + }) + +#define OBJECTS_PER_PAGE(_type) (PAGE_SIZE / sizeof(_type)) + +#define MA_2_MPN(_ma) ((MPN)((_ma) / PAGE_SIZE)) +#define MPN_2_MA(_mpn) ((MA)((_mpn) * PAGE_SIZE)) + +#define VA_2_VPN(_va) ((_va) / PAGE_SIZE) +#define VPN_2_vA(_vpn) ((_vpn) * PAGE_SIZE) + +/* + * The following convenience macro can be used in a following situation + * + * send(..., &foo, sizeof(foo)) --> send(..., PTR_N_SIZE(foo)) + */ + +#define PTR_N_SIZE(_var) &(_var), sizeof(_var) + + +/* + * + * BIT-PULLING macros + * + */ +#define MVP_BIT(val,n) ( ((val)>>(n))&1) +#define MVP_BITS(val,m,n) (((val)<<(31-(n))) >> ((31-(n))+(m)) ) +#define MVP_EXTRACT_FIELD(w, m, n) MVP_BITS((w), (m), ((m) + (n) - 1)) +#define MVP_MASK(m, n) (MVP_EXTRACT_FIELD(~(uint32)0U, (m), (n)) << (m)) +#define MVP_UPDATE_FIELD(old_val, field_val, m, n) \ + (((old_val) & ~MVP_MASK((m), (n))) | (MVP_EXTRACT_FIELD((field_val), 0, (n)) << (m))) + +/* + * + * 64BIT-PULLING macros + * + */ +#define MVP_BITS64(val,m,n) (((val)<<(63-(n))) >> ((63-(n))+(m)) ) +#define MVP_EXTRACT_FIELD64(w, m, n) MVP_BITS64((w), (m), ((m) + (n) - 1)) +#define MVP_MASK64(m, n) (MVP_EXTRACT_FIELD64(~(uint64)0ULL, (m), (n)) << (m)) +#define MVP_UPDATE_FIELD64(old_val, field_val, m, n) \ + (((old_val) & ~MVP_MASK64((m), (n))) | (MVP_EXTRACT_FIELD64(((uint64)(field_val)), 0ULL, (n)) << (m))) + +/* + * + * BIT-CHANGING macros + * + */ +#define MVP_SETBIT(val,n) ((val)|=(1<<(n))) +#define MVP_CLRBIT(val,n) ((val)&=(~(1<<(n)))) + +/* + * Fixed bit-width sign extension. + */ +#define MVP_SIGN_EXTEND(val,width) \ + (((val) ^ (1 << ((width) - 1))) - (1 << ((width) - 1))) + + +/* + * Assembler helpers. + */ +#define _MVP_HASH # +#define MVP_HASH() _MVP_HASH + +#define _MVP_STRINGIFY(...) #__VA_ARGS__ +#define MVP_STRINGIFY(...) _MVP_STRINGIFY(__VA_ARGS__) + +#ifndef __ASSEMBLER__ + +#include <stddef.h> +#include <stdbool.h> + +/* + * Constant equivalents of build-flags. + * + * Test these when possible instead of using #ifdef so that your code + * gets parsed. + */ +#ifdef MVP_DEBUG +static const _Bool mvpDebug = true; +#else +static const _Bool mvpDebug = false; +#endif + +#ifdef MVP_STATS +static const _Bool mvpStats = true; +#else +static const _Bool mvpStats = false; +#endif + +#ifdef MVP_DEVEL +static const _Bool mvpDevel = true; +#else +static const _Bool mvpDevel = false; +#endif + +#endif + +#endif diff --git a/arch/arm/mvp/commkm/vmid.h b/arch/arm/mvp/commkm/vmid.h new file mode 100644 index 0000000..f24a650 --- /dev/null +++ b/arch/arm/mvp/commkm/vmid.h @@ -0,0 +1,44 @@ +/* + * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications + * + * Copyright (C) 2010-2012 VMware, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#line 5 + +#ifndef _VMID_H +#define _VMID_H + +/** + * @file + * + * @brief The vmid definition + */ + + + +#define INCLUDE_ALLOW_MVPD +#define INCLUDE_ALLOW_VMX +#define INCLUDE_ALLOW_MODULE +#define INCLUDE_ALLOW_MONITOR +#define INCLUDE_ALLOW_HOSTUSER +#define INCLUDE_ALLOW_GUESTUSER +#define INCLUDE_ALLOW_GPL +#include "include_check.h" + +#define VMID_UNDEF (uint16)0xffff +typedef uint16 VmId; + +#endif |