Last active
June 18, 2023 19:25
-
-
Save kikairoya/6218671 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/modules/linux/dkms.conf b/modules/linux/dkms.conf | |
index 3ed77f9..263921f 100644 | |
--- a/modules/linux/dkms.conf | |
+++ b/modules/linux/dkms.conf | |
@@ -1,39 +1,35 @@ | |
PACKAGE_NAME=open-vm-tools | |
PACKAGE_VERSION=2013.04.16 | |
MAKE_CMD_TMPL="make VM_UNAME=\$kernelver \ | |
- MODULEBUILDDIR=$dkms_tree/$PACKAGE_NAME/$PACKAGE_VERSION/build" | |
+ MODULEBUILDDIR=$dkms_tree/$PACKAGE_NAME/$PACKAGE_VERSION/build \ | |
+ CC_WARNINGS=-Wno-unused-local-typedefs" | |
# The vsock module depends on symbols exported by the vmci module, so it | |
# needs to be built afterwards; the MODULEBUILDDIR variable tells the makefiles | |
# where to store / retrive those symbol files. | |
MAKE[0]="$MAKE_CMD_TMPL -C vmblock; \ | |
- $MAKE_CMD_TMPL -C vmci; \ | |
$MAKE_CMD_TMPL -C vmhgfs; \ | |
$MAKE_CMD_TMPL -C vmsync; \ | |
$MAKE_CMD_TMPL -C vmxnet; \ | |
$MAKE_CMD_TMPL -C vsock" | |
CLEAN[0]="$MAKE_CMD_TMPL -C vmblock clean; \ | |
- $MAKE_CMD_TMPL -C vmci clean; \ | |
$MAKE_CMD_TMPL -C vmhgfs clean; \ | |
- $MAKE_CMD_TMPL -C vmsync clean"; \ | |
+ $MAKE_CMD_TMPL -C vmsync clean; \ | |
$MAKE_CMD_TMPL -C vmxnet clean; \ | |
- $MAKE_CMD_TMPL -C vsock clean | |
+ $MAKE_CMD_TMPL -C vsock clean" | |
BUILT_MODULE_NAME[0]="vmblock" | |
-BUILT_MODULE_NAME[1]="vmci" | |
-BUILT_MODULE_NAME[2]="vmhgfs" | |
-BUILT_MODULE_NAME[3]="vmsync" | |
-BUILT_MODULE_NAME[4]="vmxnet" | |
-BUILT_MODULE_NAME[5]="vsock" | |
+BUILT_MODULE_NAME[1]="vmhgfs" | |
+BUILT_MODULE_NAME[2]="vmsync" | |
+BUILT_MODULE_NAME[3]="vmxnet" | |
+BUILT_MODULE_NAME[4]="vsock" | |
BUILT_MODULE_LOCATION[0]="vmblock/" | |
-BUILT_MODULE_LOCATION[1]="vmci/" | |
-BUILT_MODULE_LOCATION[2]="vmhgfs/" | |
-BUILT_MODULE_LOCATION[3]="vmsync/" | |
-BUILT_MODULE_LOCATION[4]="vmxnet/" | |
-BUILT_MODULE_LOCATION[5]="vsock/" | |
+BUILT_MODULE_LOCATION[1]="vmhgfs/" | |
+BUILT_MODULE_LOCATION[2]="vmsync/" | |
+BUILT_MODULE_LOCATION[3]="vmxnet/" | |
+BUILT_MODULE_LOCATION[4]="vsock/" | |
DEST_MODULE_LOCATION[0]="/kernel/fs/vmblock" | |
-DEST_MODULE_LOCATION[1]="/kernel/drivers/misc" | |
-DEST_MODULE_LOCATION[2]="/kernel/fs/vmhgfs" | |
-DEST_MODULE_LOCATION[3]="/kernel/drivers/misc" | |
-DEST_MODULE_LOCATION[4]="/kernel/drivers/net" | |
-DEST_MODULE_LOCATION[5]="/kernel/net/vsock" | |
+DEST_MODULE_LOCATION[1]="/kernel/fs/vmhgfs" | |
+DEST_MODULE_LOCATION[2]="/kernel/drivers/misc" | |
+DEST_MODULE_LOCATION[3]="/kernel/drivers/net" | |
+DEST_MODULE_LOCATION[4]="/kernel/net/vsock" | |
AUTOINSTALL="YES" | |
diff --git a/modules/linux/vmblock/linux/control.c b/modules/linux/vmblock/linux/control.c | |
index 79716bd..ee64cdc 100644 | |
--- a/modules/linux/vmblock/linux/control.c | |
+++ b/modules/linux/vmblock/linux/control.c | |
@@ -208,9 +208,10 @@ SetupProcDevice(void) | |
VMBlockSetProcEntryOwner(controlProcMountpoint); | |
/* Create /proc/fs/vmblock/dev */ | |
- controlProcEntry = create_proc_entry(VMBLOCK_CONTROL_DEVNAME, | |
- VMBLOCK_CONTROL_MODE, | |
- controlProcDirEntry); | |
+ controlProcEntry = proc_create(VMBLOCK_CONTROL_DEVNAME, | |
+ VMBLOCK_CONTROL_MODE, | |
+ controlProcDirEntry, | |
+ &ControlFileOps); | |
if (!controlProcEntry) { | |
Warning("SetupProcDevice: could not create " VMBLOCK_DEVICE "\n"); | |
remove_proc_entry(VMBLOCK_CONTROL_MOUNTPOINT, controlProcDirEntry); | |
@@ -218,7 +219,6 @@ SetupProcDevice(void) | |
return -EINVAL; | |
} | |
- controlProcEntry->proc_fops = &ControlFileOps; | |
return 0; | |
} | |
@@ -278,7 +278,7 @@ ExecuteBlockOp(const char __user *buf, // IN: buffer with name | |
int (*blockOp)(const char *filename, // IN: block operation | |
const os_blocker_id_t blocker)) | |
{ | |
- char *name; | |
+ struct filename *name; | |
int i; | |
int retval; | |
@@ -287,13 +287,13 @@ ExecuteBlockOp(const char __user *buf, // IN: buffer with name | |
return PTR_ERR(name); | |
} | |
- for (i = strlen(name) - 1; i >= 0 && name[i] == '/'; i--) { | |
- name[i] = '\0'; | |
+ for (i = strlen(name->name) - 1; i >= 0 && name->name[i] == '/'; i--) { | |
+ ((char *)name->name)[i] = '\0'; | |
} | |
- retval = i < 0 ? -EINVAL : blockOp(name, blocker); | |
+ retval = i < 0 ? -EINVAL : blockOp(name->name, blocker); | |
- putname(name); | |
+ __putname(name); | |
return retval; | |
} | |
diff --git a/modules/linux/vmblock/linux/dentry.c b/modules/linux/vmblock/linux/dentry.c | |
index 05ea95a..d93b2f0 100644 | |
--- a/modules/linux/vmblock/linux/dentry.c | |
+++ b/modules/linux/vmblock/linux/dentry.c | |
@@ -31,8 +31,7 @@ | |
#include "filesystem.h" | |
#include "block.h" | |
- | |
-static int DentryOpRevalidate(struct dentry *dentry, struct nameidata *nd); | |
+static int DentryOpRevalidate(struct dentry *dentry, unsigned int flags); | |
struct dentry_operations LinkDentryOps = { | |
.d_revalidate = DentryOpRevalidate, | |
@@ -60,7 +59,7 @@ struct dentry_operations LinkDentryOps = { | |
static int | |
DentryOpRevalidate(struct dentry *dentry, // IN: dentry revalidating | |
- struct nameidata *nd) // IN: lookup flags & intent | |
+ unsigned int flags) // IN: lookup flags | |
{ | |
VMBlockInodeInfo *iinfo; | |
struct nameidata actualNd; | |
@@ -101,7 +100,7 @@ DentryOpRevalidate(struct dentry *dentry, // IN: dentry revalidating | |
if (actualDentry && | |
actualDentry->d_op && | |
actualDentry->d_op->d_revalidate) { | |
- return actualDentry->d_op->d_revalidate(actualDentry, nd); | |
+ return actualDentry->d_op->d_revalidate(actualDentry, flags); | |
} | |
if (compat_path_lookup(iinfo->name, 0, &actualNd)) { | |
diff --git a/modules/linux/vmblock/linux/inode.c b/modules/linux/vmblock/linux/inode.c | |
index 098c94c..ddd37f3 100644 | |
--- a/modules/linux/vmblock/linux/inode.c | |
+++ b/modules/linux/vmblock/linux/inode.c | |
@@ -36,7 +36,7 @@ | |
/* Inode operations */ | |
static struct dentry *InodeOpLookup(struct inode *dir, | |
- struct dentry *dentry, struct nameidata *nd); | |
+ struct dentry *dentry, unsigned int flags); | |
static int InodeOpReadlink(struct dentry *dentry, char __user *buffer, int buflen); | |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 13) | |
static void *InodeOpFollowlink(struct dentry *dentry, struct nameidata *nd); | |
@@ -75,7 +75,7 @@ static struct inode_operations LinkInodeOps = { | |
static struct dentry * | |
InodeOpLookup(struct inode *dir, // IN: parent directory's inode | |
struct dentry *dentry, // IN: dentry to lookup | |
- struct nameidata *nd) // IN: lookup intent and information | |
+ unsigned int flags) // IN: lookup flags | |
{ | |
char *filename; | |
struct inode *inode; | |
diff --git a/modules/linux/vmci/COPYING b/modules/linux/vmci/COPYING | |
deleted file mode 100644 | |
index d511905..0000000 | |
--- a/modules/linux/vmci/COPYING | |
+++ /dev/null | |
@@ -1,339 +0,0 @@ | |
- GNU GENERAL PUBLIC LICENSE | |
- Version 2, June 1991 | |
- | |
- Copyright (C) 1989, 1991 Free Software Foundation, Inc., | |
- 51 Franklin Street, 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 Lesser 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 Street, 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 Lesser General | |
-Public License instead of this License. | |
diff --git a/modules/linux/vmci/Makefile b/modules/linux/vmci/Makefile | |
deleted file mode 100644 | |
index a8cc0b7..0000000 | |
--- a/modules/linux/vmci/Makefile | |
+++ /dev/null | |
@@ -1,171 +0,0 @@ | |
-#!/usr/bin/make -f | |
-########################################################## | |
-# Copyright (C) 1998 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 as published by the | |
-# Free Software Foundation version 2 and no 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 | |
-# | |
-########################################################## | |
- | |
-#### | |
-#### VMware kernel module Makefile to be distributed externally | |
-#### | |
- | |
-#### | |
-#### SRCROOT _must_ be a relative path. | |
-#### | |
-SRCROOT = . | |
- | |
-# | |
-# open-vm-tools doesn't replicate shared source files for different modules; | |
-# instead, files are kept in shared locations. So define a few useful macros | |
-# to be able to handle both cases cleanly. | |
-# | |
-INCLUDE := | |
-ifdef OVT_SOURCE_DIR | |
-AUTOCONF_DIR := $(OVT_SOURCE_DIR)/modules/linux/shared/autoconf | |
-VMLIB_PATH = $(OVT_SOURCE_DIR)/lib/$(1) | |
-INCLUDE += -I$(OVT_SOURCE_DIR)/modules/linux/shared | |
-INCLUDE += -I$(OVT_SOURCE_DIR)/lib/include | |
-else | |
-AUTOCONF_DIR := $(SRCROOT)/shared/autoconf | |
-INCLUDE += -I$(SRCROOT)/shared | |
-endif | |
- | |
- | |
-VM_UNAME = $(shell uname -r) | |
- | |
-# Header directory for the running kernel | |
-ifdef LINUXINCLUDE | |
-HEADER_DIR = $(LINUXINCLUDE) | |
-else | |
-HEADER_DIR = /lib/modules/$(VM_UNAME)/build/include | |
-endif | |
- | |
-BUILD_DIR = $(HEADER_DIR)/.. | |
- | |
-DRIVER := vmci | |
-PRODUCT := tools-source | |
- | |
-# Grep program | |
-GREP = /bin/grep | |
- | |
-vm_check_gcc = $(shell if $(CC) $(1) -S -o /dev/null -xc /dev/null \ | |
- > /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi) | |
-vm_check_file = $(shell if test -f $(1); then echo "yes"; else echo "no"; fi) | |
- | |
-ifndef VM_KBUILD | |
-VM_KBUILD := no | |
-ifeq ($(call vm_check_file,$(BUILD_DIR)/Makefile), yes) | |
-ifneq ($(call vm_check_file,$(BUILD_DIR)/Rules.make), yes) | |
-VM_KBUILD := 26 | |
-endif | |
-endif | |
-export VM_KBUILD | |
-endif | |
- | |
-ifndef VM_KBUILD_SHOWN | |
-ifeq ($(VM_KBUILD), no) | |
-VM_DUMMY := $(shell echo >&2 "Using standalone build system.") | |
-else | |
-ifeq ($(VM_KBUILD), 24) | |
-VM_DUMMY := $(shell echo >&2 "Using 2.4.x kernel build system.") | |
-else | |
-VM_DUMMY := $(shell echo >&2 "Using 2.6.x kernel build system.") | |
-endif | |
-endif | |
-VM_KBUILD_SHOWN := yes | |
-export VM_KBUILD_SHOWN | |
-endif | |
- | |
-ifneq ($(VM_KBUILD), no) | |
- | |
-VMCCVER := $(shell $(CC) -dumpversion) | |
- | |
-# If there is no version defined, we are in toplevel pass, not yet in kernel makefiles... | |
-ifeq ($(VERSION),) | |
- | |
-ifeq ($(VM_KBUILD), 24) | |
-DRIVER_KO := $(DRIVER).o | |
-else | |
-DRIVER_KO := $(DRIVER).ko | |
-endif | |
- | |
-.PHONY: $(DRIVER_KO) | |
- | |
-auto-build: $(DRIVER_KO) | |
- cp -f $< $(SRCROOT)/../$(DRIVER).o | |
- | |
-# $(DRIVER_KO) is a phony target, so compare file times explicitly | |
-$(DRIVER): $(DRIVER_KO) | |
- if [ $< -nt $@ ] || [ ! -e $@ ] ; then cp -f $< $@; fi | |
- | |
-# Pass gcc version down the chain, so we can detect if kernel attempts to use unapproved compiler | |
-VM_CCVER := $(VMCCVER) | |
-export VM_CCVER | |
-VM_CC := $(CC) | |
-export VM_CC | |
- | |
-MAKEOVERRIDES := $(filter-out CC=%,$(MAKEOVERRIDES)) | |
- | |
-# | |
-# Define a setup target that gets built before the actual driver. | |
-# This target may not be used at all, but if it is then it will be defined | |
-# in Makefile.kernel | |
-# | |
-prebuild:: ; | |
-postbuild:: ; | |
- | |
-$(DRIVER_KO): prebuild | |
- $(MAKE) -C $(BUILD_DIR) SUBDIRS=$$PWD SRCROOT=$$PWD/$(SRCROOT) \ | |
- MODULEBUILDDIR=$(MODULEBUILDDIR) modules | |
- $(MAKE) -C $$PWD SRCROOT=$$PWD/$(SRCROOT) \ | |
- MODULEBUILDDIR=$(MODULEBUILDDIR) postbuild | |
-endif | |
- | |
-vm_check_build = $(shell if $(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) \ | |
- $(CPPFLAGS) $(CFLAGS) $(CFLAGS_KERNEL) $(LINUXINCLUDE) \ | |
- $(EXTRA_CFLAGS) -Iinclude2/asm/mach-default \ | |
- -DKBUILD_BASENAME=\"$(DRIVER)\" \ | |
- -Werror -S -o /dev/null -xc $(1) \ | |
- > /dev/null 2>&1; then echo "$(2)"; else echo "$(3)"; fi) | |
- | |
-CC_WARNINGS := -Wall -Wstrict-prototypes | |
-CC_OPTS := $(GLOBAL_DEFS) $(CC_WARNINGS) -DVMW_USING_KBUILD | |
-ifdef VMX86_DEVEL | |
-CC_OPTS += -DVMX86_DEVEL | |
-endif | |
-ifdef VMX86_DEBUG | |
-CC_OPTS += -DVMX86_DEBUG | |
-endif | |
- | |
-include $(SRCROOT)/Makefile.kernel | |
- | |
-ifdef TOPDIR | |
-ifeq ($(VM_KBUILD), 24) | |
- | |
-O_TARGET := $(DRIVER).o | |
- | |
-obj-y := $($(DRIVER)-y) | |
- | |
-include $(TOPDIR)/Rules.make | |
-endif | |
-endif | |
- | |
-else | |
- | |
-include $(SRCROOT)/Makefile.normal | |
- | |
-endif | |
- | |
-#.SILENT: | |
diff --git a/modules/linux/vmci/Makefile.kernel b/modules/linux/vmci/Makefile.kernel | |
deleted file mode 100644 | |
index ba343ee..0000000 | |
--- a/modules/linux/vmci/Makefile.kernel | |
+++ /dev/null | |
@@ -1,65 +0,0 @@ | |
-#!/usr/bin/make -f | |
-########################################################## | |
-# Copyright (C) 2007 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 as published by the | |
-# Free Software Foundation version 2 and no 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 | |
-# | |
-########################################################## | |
- | |
-CC_OPTS += -DVMCI | |
- | |
-INCLUDE += -I$(SRCROOT)/shared -I$(SRCROOT)/common -I$(SRCROOT)/linux | |
- | |
-EXTRA_CFLAGS := $(CC_OPTS) $(INCLUDE) | |
- | |
-obj-m += $(DRIVER).o | |
- | |
-$(DRIVER)-y := $(subst $(SRCROOT)/, , $(patsubst %.c, %.o, \ | |
- $(wildcard $(SRCROOT)/linux/*.c $(SRCROOT)/common/*.c))) | |
- | |
-# | |
-# In open-vm-tools, need to compile the common sources from the shared directory. | |
-# | |
-DRIVERLOG := driverLog.o | |
-$(DRIVER)-y += $(DRIVERLOG) | |
- | |
-VMCI_PATH := $(shell cd $(SRCROOT) && pwd) | |
-ifdef OVT_SOURCE_DIR | |
-DRIVERLOG_PATH := $(OVT_SOURCE_DIR)/modules/linux/shared | |
-else | |
-DRIVERLOG_PATH := $(VMCI_PATH)/shared | |
-endif | |
- | |
-$(addprefix $(VMCI_PATH)/,$(DRIVERLOG)): $(VMCI_PATH)/%.o: $(DRIVERLOG_PATH)/%.c | |
- $(Q)$(rule_cc_o_c) | |
- | |
-clean: | |
- rm -rf $(wildcard $(DRIVER).mod.c $(DRIVER).ko .tmp_versions \ | |
- Module.symvers Modules.symvers Module.markers modules.order \ | |
- $(foreach dir,linux/ common/ \ | |
- ./,$(addprefix $(dir),.*.cmd .*.o.flags *.o))) | |
-ifneq ($(MODULEBUILDDIR),) | |
- rm -f $(MODULEBUILDDIR)/VMwareVMCIModule.symvers | |
-endif | |
- | |
-# | |
-# If this build generated a Module.symvers, copy it to a public place where | |
-# the VMCI Sockets build will be able to find it. | |
-# | |
-postbuild:: | |
-ifeq ($(call vm_check_file,$(SRCROOT)/Module.symvers), yes) | |
-ifneq ($(MODULEBUILDDIR),) | |
- cp -f $(SRCROOT)/Module.symvers $(MODULEBUILDDIR)/VMwareVMCIModule.symvers | |
-endif | |
-endif | |
diff --git a/modules/linux/vmci/Makefile.normal b/modules/linux/vmci/Makefile.normal | |
deleted file mode 100644 | |
index dda1844..0000000 | |
--- a/modules/linux/vmci/Makefile.normal | |
+++ /dev/null | |
@@ -1,146 +0,0 @@ | |
-#!/usr/bin/make -f | |
-########################################################## | |
-# Copyright (C) 2007 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 as published by the | |
-# Free Software Foundation version 2 and no 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 | |
-# | |
-########################################################## | |
- | |
-vm_check_build = $(shell if $(CC) $(CC_OPTS) $(INCLUDE) -Werror -S -o /dev/null -xc $(1) \ | |
- > /dev/null 2>&1; then echo "$(2)"; else echo "$(3)"; fi) | |
- | |
-#### | |
-#### DESTDIR is where the module, object files, and dependencies are built | |
-#### | |
-DESTDIR := driver-$(VM_UNAME) | |
- | |
-#### | |
-#### DRIVERNAME should be untouched unless you have a good reason to change | |
-#### it. The form below is how the scripts expect it. | |
-#### | |
-DRIVERNAME := $(DRIVER)-xxx-$(VM_UNAME) | |
- | |
-ifneq (,$(filter x86_64%, $(shell $(CC) -dumpmachine))) | |
-MACHINE := x86_64 | |
-else | |
-MACHINE := x386 | |
-endif | |
- | |
-ifdef QUIET | |
-ECHO := @true | |
-else | |
-ECHO := @echo | |
-endif | |
- | |
-#### | |
-#### You must compile with at least -O level of optimization | |
-#### or the module won't load. | |
-#### If desparate, I think that bringing in <linux/bitops.h> might | |
-#### suffice. | |
-#### | |
-CC_WARNINGS := -Wall -Wstrict-prototypes | |
-# Don't use -pipe or egcs-2.91.66 (shipped with RedHat) will die | |
-CC_KFLAGS := -D__KERNEL__ -fno-strength-reduce -fno-omit-frame-pointer \ | |
- -fno-common -DKBUILD_MODNAME=\"$(DRIVER)\" | |
-CC_KFLAGS += $(call vm_check_gcc,-falign-loops=2 -falign-jumps=2 -falign-functions=2, \ | |
- -malign-loops=2 -malign-jumps=2 -malign-functions=2) | |
-CC_KFLAGS += $(call vm_check_gcc,-fno-strict-aliasing,) | |
-ifeq ($(MACHINE),x86_64) | |
-CC_KFLAGS += -mno-red-zone -mcmodel=kernel | |
-else | |
-# Gcc 3.0 deprecates -m486 --hpreg | |
-CC_KFLAGS += -DCPU=586 $(call check_gcc,-march=i586,-m486) | |
-endif | |
- | |
-CC_OPTS := -O2 -DMODULE -DVMCI $(GLOBAL_DEFS) $(CC_KFLAGS) $(CC_WARNINGS) | |
- | |
-INCLUDE := -I$(SRCROOT)/shared -I$(SRCROOT)/common -I$(SRCROOT)/linux \ | |
- -I$(HEADER_DIR) | |
- | |
-INCLUDE += $(shell $(CC) $(INCLUDE) -E $(SRCROOT)/shared/autoconf/geninclude.c \ | |
- | sed -n -e 's!^APATH!-I$(HEADER_DIR)/asm!p') | |
- | |
-ifdef OVT_SOURCE_PATH | |
-DRIVERLOGPATH := $(OVT_SOURCE_DIR)/modules/linux/shared | |
-else | |
-DRIVERLOGPATH := $(SRCROOT)/shared | |
-endif | |
- | |
-C_TARGETS_LINUX := driver.o vmciKernelIf.o | |
-C_TARGETS_COMMON := vmciContext.o vmciDatagram.o vmciDriver.o \ | |
- vmciHashtable.o vmciResource.o \ | |
- vmciQueuePair.o vmciEvent.o vmciRoute.o | |
- | |
-C_TARGETS_LINUX_D := ${C_TARGETS_LINUX:.o=.d} | |
-C_TARGETS_COMMON_D := ${C_TARGETS_COMMON:.o=.d} | |
-C_TARGETS := $(C_TARGETS_LINUX) $(C_TARGETS_COMMON) driverLog.o | |
- | |
-#### | |
-#### Make Targets are beneath here. | |
-#### | |
- | |
-driver: setup deps | |
- $(MAKE) -C $(DESTDIR) -f ../Makefile SRCROOT=../$(SRCROOT) $(DRIVER).o \ | |
- INCLUDE_DEPS=1 | |
- | |
-setup: | |
- @if [ -d $(DESTDIR) ] ; then true ; else mkdir $(DESTDIR); chmod 755 $(DESTDIR) ; fi | |
- | |
-$(DRIVER) $(DRIVER).o: $(DRIVERNAME) | |
- cp -f $< $@ | |
- | |
-$(DRIVERNAME): $(C_TARGETS) | |
- $(ECHO) "Building $(DRIVERNAME)" | |
- ld -r -o $(DRIVERNAME) $(C_TARGETS) | |
- | |
-auto-build: | |
- $(MAKE) driver QUIET=1 | |
- cp -f $(DESTDIR)/$(DRIVERNAME) $(SRCROOT)/../$(DRIVER).o | |
- | |
-$(C_TARGETS_LINUX): %.o: $(SRCROOT)/linux/%.c | |
- $(ECHO) "Compiling linux/$(<F)" | |
- $(CC) $(CC_OPTS) $(INCLUDE) -c $< | |
- | |
-$(C_TARGETS_COMMON): %.o: $(SRCROOT)/common/%.c | |
- $(ECHO) "Compiling common/$(<F)" | |
- $(CC) $(CC_OPTS) $(INCLUDE) -c $< | |
- | |
-driverLog.o: $(DRIVERLOGPATH)/driverLog.c | |
- $(CC) $(CC_OPTS) $(INCLUDE) -c $< | |
- | |
-clean: | |
- rm -rf $(DESTDIR)/ | |
- | |
-$(C_TARGETS_COMMON_D): %.d: $(SRCROOT)/common/%.c | |
- $(ECHO) "Dependencies for $(<F)" | |
- $(CC) -MM $(CC_OPTS) $(INCLUDE) $< > $@ | |
- | |
-$(C_TARGETS_LINUX_D): %.d: $(SRCROOT)/linux/%.c | |
- $(ECHO) "Dependencies for $(<F)" | |
- $(CC) -MM $(CC_OPTS) $(INCLUDE) $< > $@ | |
- | |
-driverLog.d: $(DRIVERLOGPATH)/driverLog.c | |
- $(ECHO) "Dependencies for $(<F)" | |
- $(CC) -MM $(CC_OPTS) $(INCLUDE) $< > $@ | |
- | |
-deps: setup | |
- $(MAKE) -C $(DESTDIR) -f ../Makefile SRCROOT=../$(SRCROOT) driver_deps | |
- | |
-driver_deps: ${C_TARGETS:.o=.d} | |
- | |
-ifdef INCLUDE_DEPS | |
-include ${C_TARGETS:.o=.d} | |
-endif | |
- | |
-.SILENT: | |
diff --git a/modules/linux/vmci/common/vmciCommonInt.h b/modules/linux/vmci/common/vmciCommonInt.h | |
deleted file mode 100644 | |
index 8af33b4..0000000 | |
--- a/modules/linux/vmci/common/vmciCommonInt.h | |
+++ /dev/null | |
@@ -1,150 +0,0 @@ | |
-/********************************************************* | |
- * Copyright (C) 2006-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 as published by the | |
- * Free Software Foundation version 2 and no 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 | |
- * | |
- *********************************************************/ | |
- | |
-/* | |
- * vmciCommonInt.h -- | |
- * | |
- * Struct definitions for VMCI internal common code. | |
- */ | |
- | |
-#ifndef _VMCI_COMMONINT_H_ | |
-#define _VMCI_COMMONINT_H_ | |
- | |
-#define INCLUDE_ALLOW_MODULE | |
-#define INCLUDE_ALLOW_VMCORE | |
-#define INCLUDE_ALLOW_VMKERNEL | |
-#include "includeCheck.h" | |
- | |
-#include "vm_atomic.h" | |
-#include "vmci_defs.h" | |
-#include "vmci_call_defs.h" | |
-#include "vmci_infrastructure.h" | |
-#include "vmci_handle_array.h" | |
-#include "vmci_kernel_if.h" | |
- | |
-/* | |
- * The DatagramQueueEntry is a queue header for the in-kernel VMCI | |
- * datagram queues. It is allocated in non-paged memory, as the | |
- * content is accessed while holding a spinlock. The pending datagram | |
- * itself may be allocated from paged memory. We shadow the size of | |
- * the datagram in the non-paged queue entry as this size is used | |
- * while holding the same spinlock as above. | |
- */ | |
- | |
-typedef struct DatagramQueueEntry { | |
- VMCIListItem listItem; /* For queuing. */ | |
- size_t dgSize; /* Size of datagram. */ | |
- VMCIDatagram *dg; /* Pending datagram. */ | |
-} DatagramQueueEntry; | |
- | |
- | |
-/* | |
- * The VMCIFilterState captures the state of all VMCI filters in one | |
- * direction. The ranges array contains all filter list in a single | |
- * memory chunk, and the filter list pointers in the VMCIProtoFilters | |
- * point into the ranges array. | |
- */ | |
- | |
-typedef struct VMCIFilterState { | |
- VMCIProtoFilters filters; | |
- VMCIIdRange *ranges; | |
- size_t rangesSize; | |
-} VMCIFilterState; | |
- | |
- | |
-struct VMCIContext { | |
- VMCIListItem listItem; /* For global VMCI list. */ | |
- VMCIId cid; | |
- Atomic_uint32 refCount; | |
- VMCIList datagramQueue; /* Head of per VM queue. */ | |
- uint32 pendingDatagrams; | |
- size_t datagramQueueSize;/* Size of datagram queue in bytes. */ | |
- int userVersion; /* | |
- * Version of the code that created | |
- * this context; e.g., VMX. | |
- */ | |
- VMCILock lock; /* | |
- * Locks datagramQueue, inFilters, | |
- * doorbellArray, pendingDoorbellArray | |
- * and notifierArray. | |
- */ | |
- VMCIHandleArray *queuePairArray; /* | |
- * QueuePairs attached to. The array of | |
- * handles for queue pairs is accessed | |
- * from the code for QP API, and there | |
- * it is protected by the QP lock. It | |
- * is also accessed from the context | |
- * clean up path, which does not | |
- * require a lock. VMCILock is not | |
- * used to protect the QP array. | |
- */ | |
- VMCIHandleArray *doorbellArray; /* Doorbells created by context. */ | |
- VMCIHandleArray *pendingDoorbellArray; /* Doorbells pending for context. */ | |
- VMCIHandleArray *notifierArray; /* Contexts current context is subscribing to. */ | |
- VMCIHost hostContext; | |
- VMCIPrivilegeFlags privFlags; | |
- VMCIHostUser user; | |
- Bool validUser; | |
-#ifdef VMKERNEL | |
- Bool isQuiesced; /* Whether current VM is quiesced */ | |
- VMCIId migrateCid; /* The migrate cid if it is migrating */ | |
- VMCIMutex guestMemMutex; /* | |
- * Coordinates guest memory | |
- * registration/release during FSR. | |
- */ | |
- VMCIGuestMemID curGuestMemID; /* ID of current registered guest mem */ | |
- VMCIFilterState *inFilters; /* Ingoing filters for VMCI traffic. */ | |
-#endif | |
-#ifndef VMX86_SERVER | |
- Bool *notify; /* Notify flag pointer - hosted only. */ | |
-# ifdef __linux__ | |
- struct page *notifyPage; /* Page backing the notify UVA. */ | |
-# endif | |
-#endif | |
-}; | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCIDenyInteraction -- | |
- * | |
- * Utilility function that checks whether two entities are allowed | |
- * to interact. If one of them is restricted, the other one must | |
- * be trusted. | |
- * | |
- * Result: | |
- * TRUE if the two entities are not allowed to interact. FALSE otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-static INLINE Bool | |
-VMCIDenyInteraction(VMCIPrivilegeFlags partOne, // IN | |
- VMCIPrivilegeFlags partTwo) // IN | |
-{ | |
- return (((partOne & VMCI_PRIVILEGE_FLAG_RESTRICTED) && | |
- !(partTwo & VMCI_PRIVILEGE_FLAG_TRUSTED)) || | |
- ((partTwo & VMCI_PRIVILEGE_FLAG_RESTRICTED) && | |
- !(partOne & VMCI_PRIVILEGE_FLAG_TRUSTED))); | |
-} | |
- | |
-#endif /* _VMCI_COMMONINT_H_ */ | |
diff --git a/modules/linux/vmci/common/vmciContext.c b/modules/linux/vmci/common/vmciContext.c | |
deleted file mode 100644 | |
index 5a698e3..0000000 | |
--- a/modules/linux/vmci/common/vmciContext.c | |
+++ /dev/null | |
@@ -1,2716 +0,0 @@ | |
-/********************************************************* | |
- * Copyright (C) 2006-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 as published by the | |
- * Free Software Foundation version 2 and no 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 | |
- * | |
- *********************************************************/ | |
- | |
-/* | |
- * vmciContext.c -- | |
- * | |
- * Platform independent routines for VMCI calls. | |
- */ | |
- | |
-#include "vmci_kernel_if.h" | |
-#include "vm_assert.h" | |
-#include "vmci_defs.h" | |
-#include "vmci_infrastructure.h" | |
-#include "vmciCommonInt.h" | |
-#include "vmciContext.h" | |
-#include "vmciDatagram.h" | |
-#include "vmciDoorbell.h" | |
-#include "vmciDriver.h" | |
-#include "vmciEvent.h" | |
-#include "vmciKernelAPI.h" | |
-#include "vmciQueuePair.h" | |
-#if defined(VMKERNEL) | |
-# include "vmciVmkInt.h" | |
-# include "vm_libc.h" | |
-# include "helper_ext.h" | |
-#endif | |
- | |
-#define LGPFX "VMCIContext: " | |
- | |
-static void VMCIContextFreeContext(VMCIContext *context); | |
-static Bool VMCIContextExists(VMCIId cid); | |
-static int VMCIContextFireNotification(VMCIId contextID, | |
- VMCIPrivilegeFlags privFlags); | |
-#if defined(VMKERNEL) | |
-static void VMCIContextReleaseGuestMemLocked(VMCIContext *context, | |
- VMCIGuestMemID gid); | |
-static void VMCIContextInFilterCleanup(VMCIContext *context); | |
-#endif | |
- | |
-/* | |
- * List of current VMCI contexts. | |
- */ | |
- | |
-static struct { | |
- VMCIList head; | |
- VMCILock lock; | |
- VMCILock firingLock; | |
-} contextList; | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIContextSignalNotify -- | |
- * | |
- * Sets the notify flag to TRUE. Assumes that the context lock is | |
- * held. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-static INLINE void | |
-VMCIContextSignalNotify(VMCIContext *context) // IN: | |
-{ | |
-#ifndef VMX86_SERVER | |
- if (context->notify) { | |
- *context->notify = TRUE; | |
- } | |
-#endif | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIContextClearNotify -- | |
- * | |
- * Sets the notify flag to FALSE. Assumes that the context lock is | |
- * held. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-static INLINE void | |
-VMCIContextClearNotify(VMCIContext *context) // IN: | |
-{ | |
-#ifndef VMX86_SERVER | |
- if (context->notify) { | |
- *context->notify = FALSE; | |
- } | |
-#endif | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIContextClearNotifyAndCall -- | |
- * | |
- * If nothing requires the attention of the guest, clears both | |
- * notify flag and call. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-static INLINE void | |
-VMCIContextClearNotifyAndCall(VMCIContext *context) // IN: | |
-{ | |
- if (context->pendingDatagrams == 0 && | |
- VMCIHandleArray_GetSize(context->pendingDoorbellArray) == 0) { | |
- VMCIHost_ClearCall(&context->hostContext); | |
- VMCIContextClearNotify(context); | |
- } | |
-} | |
- | |
- | |
-#ifndef VMX86_SERVER | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIContext_CheckAndSignalNotify -- | |
- * | |
- * Sets the context's notify flag iff datagrams are pending for this | |
- * context. Called from VMCISetupNotify(). | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCIContext_CheckAndSignalNotify(VMCIContext *context) // IN: | |
-{ | |
- VMCILockFlags flags; | |
- | |
- ASSERT(context); | |
- VMCI_GrabLock(&context->lock, &flags); | |
- if (context->pendingDatagrams) { | |
- VMCIContextSignalNotify(context); | |
- } | |
- VMCI_ReleaseLock(&context->lock, flags); | |
-} | |
-#endif | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIContext_Init -- | |
- * | |
- * Initializes the VMCI context module. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCIContext_Init(void) | |
-{ | |
- int err; | |
- | |
- VMCIList_Init(&contextList.head); | |
- | |
- err = VMCI_InitLock(&contextList.lock, "VMCIContextListLock", | |
- VMCI_LOCK_RANK_CONTEXTLIST); | |
- if (err < VMCI_SUCCESS) { | |
- return err; | |
- } | |
- | |
- err = VMCI_InitLock(&contextList.firingLock, "VMCIContextFiringLock", | |
- VMCI_LOCK_RANK_CONTEXTFIRE); | |
- if (err < VMCI_SUCCESS) { | |
- VMCI_CleanupLock(&contextList.lock); | |
- } | |
- | |
- return err; | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIContext_Exit -- | |
- * | |
- * Cleans up the contexts module. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCIContext_Exit(void) | |
-{ | |
- VMCI_CleanupLock(&contextList.firingLock); | |
- VMCI_CleanupLock(&contextList.lock); | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIContext_InitContext -- | |
- * | |
- * Allocates and initializes a VMCI context. | |
- * | |
- * Results: | |
- * Returns 0 on success, appropriate error code otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCIContext_InitContext(VMCIId cid, // IN | |
- VMCIPrivilegeFlags privFlags, // IN | |
- uintptr_t eventHnd, // IN | |
- int userVersion, // IN: User's vers no. | |
- VMCIHostUser *user, // IN | |
- VMCIContext **outContext) // OUT | |
-{ | |
- VMCILockFlags flags; | |
- VMCIContext *context; | |
- int result; | |
- | |
- if (privFlags & ~VMCI_PRIVILEGE_ALL_FLAGS) { | |
- VMCI_DEBUG_LOG(4, (LGPFX"Invalid flag (flags=0x%x) for VMCI context.\n", | |
- privFlags)); | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- if (userVersion == 0) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- context = VMCI_AllocKernelMem(sizeof *context, VMCI_MEMORY_NONPAGED); | |
- if (context == NULL) { | |
- VMCI_WARNING((LGPFX"Failed to allocate memory for VMCI context.\n")); | |
- return VMCI_ERROR_NO_MEM; | |
- } | |
- memset(context, 0, sizeof *context); | |
- | |
- VMCIList_InitEntry(&context->listItem); | |
- VMCIList_Init(&context->datagramQueue); | |
- | |
- context->userVersion = userVersion; | |
- | |
- context->queuePairArray = VMCIHandleArray_Create(0); | |
- if (!context->queuePairArray) { | |
- result = VMCI_ERROR_NO_MEM; | |
- goto error; | |
- } | |
- | |
- context->doorbellArray = VMCIHandleArray_Create(0); | |
- if (!context->doorbellArray) { | |
- result = VMCI_ERROR_NO_MEM; | |
- goto error; | |
- } | |
- | |
- context->pendingDoorbellArray = VMCIHandleArray_Create(0); | |
- if (!context->pendingDoorbellArray) { | |
- result = VMCI_ERROR_NO_MEM; | |
- goto error; | |
- } | |
- | |
- context->notifierArray = VMCIHandleArray_Create(0); | |
- if (context->notifierArray == NULL) { | |
- result = VMCI_ERROR_NO_MEM; | |
- goto error; | |
- } | |
- | |
- result = VMCI_InitLock(&context->lock, "VMCIContextLock", | |
- VMCI_LOCK_RANK_CONTEXT); | |
- if (result < VMCI_SUCCESS) { | |
- goto error; | |
- } | |
- Atomic_Write(&context->refCount, 1); | |
- | |
-#if defined(VMKERNEL) | |
- result = VMCIMutex_Init(&context->guestMemMutex, "VMCIGuestMem", | |
- VMCI_SEMA_RANK_GUESTMEM); | |
- if (result < VMCI_SUCCESS) { | |
- VMCI_CleanupLock(&context->lock); | |
- goto error; | |
- } | |
- context->curGuestMemID = INVALID_VMCI_GUEST_MEM_ID; | |
- | |
- context->inFilters = NULL; | |
-#endif | |
- | |
- /* Inititialize host-specific VMCI context. */ | |
- VMCIHost_InitContext(&context->hostContext, eventHnd); | |
- | |
- context->privFlags = privFlags; | |
- | |
- /* | |
- * If we collide with an existing context we generate a new and use it | |
- * instead. The VMX will determine if regeneration is okay. Since there | |
- * isn't 4B - 16 VMs running on a given host, the below loop will terminate. | |
- */ | |
- VMCI_GrabLock(&contextList.lock, &flags); | |
- ASSERT(cid != VMCI_INVALID_ID); | |
- while (VMCIContextExists(cid)) { | |
- | |
- /* | |
- * If the cid is below our limit and we collide we are creating duplicate | |
- * contexts internally so we want to assert fail in that case. | |
- */ | |
- ASSERT(cid >= VMCI_RESERVED_CID_LIMIT); | |
- | |
- /* We reserve the lowest 16 ids for fixed contexts. */ | |
- cid = MAX(cid, VMCI_RESERVED_CID_LIMIT-1) + 1; | |
- if (cid == VMCI_INVALID_ID) { | |
- cid = VMCI_RESERVED_CID_LIMIT; | |
- } | |
- } | |
- ASSERT(!VMCIContextExists(cid)); | |
- context->cid = cid; | |
- context->validUser = user != NULL; | |
- if (context->validUser) { | |
- context->user = *user; | |
- } | |
- VMCIList_Insert(&context->listItem, &contextList.head); | |
- VMCI_ReleaseLock(&contextList.lock, flags); | |
- | |
-#ifdef VMKERNEL | |
- VMCIContext_SetFSRState(context, FALSE, VMCI_INVALID_ID, eventHnd, FALSE); | |
-#endif | |
- | |
-#ifndef VMX86_SERVER | |
- context->notify = NULL; | |
-# ifdef __linux__ | |
- context->notifyPage = NULL; | |
-# endif | |
-#endif | |
- | |
- *outContext = context; | |
- return VMCI_SUCCESS; | |
- | |
-error: | |
- if (context->notifierArray) { | |
- VMCIHandleArray_Destroy(context->notifierArray); | |
- } | |
- if (context->queuePairArray) { | |
- VMCIHandleArray_Destroy(context->queuePairArray); | |
- } | |
- if (context->doorbellArray) { | |
- VMCIHandleArray_Destroy(context->doorbellArray); | |
- } | |
- if (context->pendingDoorbellArray) { | |
- VMCIHandleArray_Destroy(context->pendingDoorbellArray); | |
- } | |
- VMCI_FreeKernelMem(context, sizeof *context); | |
- return result; | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIContext_ReleaseContext -- | |
- * | |
- * Cleans up a VMCI context. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCIContext_ReleaseContext(VMCIContext *context) // IN | |
-{ | |
- VMCILockFlags flags; | |
- | |
- /* Dequeue VMCI context. */ | |
- | |
- VMCI_GrabLock(&contextList.lock, &flags); | |
- VMCIList_Remove(&context->listItem); | |
- VMCI_ReleaseLock(&contextList.lock, flags); | |
- | |
- VMCIContext_Release(context); | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIContextFreeContext -- | |
- * | |
- * Deallocates all parts of a context datastructure. This | |
- * functions doesn't lock the context, because it assumes that | |
- * the caller is holding the last reference to context. As paged | |
- * memory may be freed as part of the call, the function must be | |
- * called without holding any spinlocks as this is not allowed on | |
- * Windows. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * Paged memory is freed. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static void | |
-VMCIContextFreeContext(VMCIContext *context) // IN | |
-{ | |
- VMCIListItem *curr; | |
- VMCIListItem *next; | |
- DatagramQueueEntry *dqEntry; | |
- VMCIHandle tempHandle; | |
- | |
- /* Fire event to all contexts interested in knowing this context is dying. */ | |
- VMCIContextFireNotification(context->cid, context->privFlags); | |
- | |
- /* | |
- * Cleanup all queue pair resources attached to context. If the VM dies | |
- * without cleaning up, this code will make sure that no resources are | |
- * leaked. | |
- */ | |
- | |
- tempHandle = VMCIHandleArray_GetEntry(context->queuePairArray, 0); | |
- while (!VMCI_HANDLE_EQUAL(tempHandle, VMCI_INVALID_HANDLE)) { | |
- if (VMCIQPBroker_Detach(tempHandle, context) < VMCI_SUCCESS) { | |
- /* | |
- * When VMCIQPBroker_Detach() succeeds it removes the handle from the | |
- * array. If detach fails, we must remove the handle ourselves. | |
- */ | |
- VMCIHandleArray_RemoveEntry(context->queuePairArray, tempHandle); | |
- } | |
- tempHandle = VMCIHandleArray_GetEntry(context->queuePairArray, 0); | |
- } | |
- | |
- /* | |
- * It is fine to destroy this without locking the datagramQueue, as | |
- * this is the only thread having a reference to the context. | |
- */ | |
- | |
- VMCIList_ScanSafe(curr, next, &context->datagramQueue) { | |
- dqEntry = VMCIList_Entry(curr, DatagramQueueEntry, listItem); | |
- VMCIList_Remove(curr); | |
- ASSERT(dqEntry && dqEntry->dg); | |
- ASSERT(dqEntry->dgSize == VMCI_DG_SIZE(dqEntry->dg)); | |
- VMCI_FreeKernelMem(dqEntry->dg, dqEntry->dgSize); | |
- VMCI_FreeKernelMem(dqEntry, sizeof *dqEntry); | |
- } | |
- | |
- VMCIHandleArray_Destroy(context->notifierArray); | |
- VMCIHandleArray_Destroy(context->queuePairArray); | |
- VMCIHandleArray_Destroy(context->doorbellArray); | |
- VMCIHandleArray_Destroy(context->pendingDoorbellArray); | |
- VMCI_CleanupLock(&context->lock); | |
-#if defined(VMKERNEL) | |
- VMCIContextInFilterCleanup(context); | |
- VMCIMutex_Destroy(&context->guestMemMutex); | |
-#endif | |
- VMCIHost_ReleaseContext(&context->hostContext); | |
-#ifndef VMX86_SERVER | |
-# ifdef __linux__ | |
- /* TODO Windows and Mac OS. */ | |
- VMCIUnsetNotify(context); | |
-# endif | |
-#endif | |
- VMCI_FreeKernelMem(context, sizeof *context); | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIContext_PendingDatagrams -- | |
- * | |
- * Returns the current number of pending datagrams. The call may | |
- * also serve as a synchronization point for the datagram queue, | |
- * as no enqueue operations can occur concurrently. | |
- * | |
- * Results: | |
- * Length of datagram queue for the given context. | |
- * | |
- * Side effects: | |
- * Locks datagram queue. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCIContext_PendingDatagrams(VMCIId cid, // IN | |
- uint32 *pending) // OUT | |
-{ | |
- VMCIContext *context; | |
- VMCILockFlags flags; | |
- | |
- context = VMCIContext_Get(cid); | |
- if (context == NULL) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- VMCI_GrabLock(&context->lock, &flags); | |
- if (pending) { | |
- *pending = context->pendingDatagrams; | |
- } | |
- VMCI_ReleaseLock(&context->lock, flags); | |
- VMCIContext_Release(context); | |
- | |
- return VMCI_SUCCESS; | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIContext_EnqueueDatagram -- | |
- * | |
- * Queues a VMCI datagram for the appropriate target VM | |
- * context. | |
- * | |
- * Results: | |
- * Size of enqueued data on success, appropriate error code otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCIContext_EnqueueDatagram(VMCIId cid, // IN: Target VM | |
- VMCIDatagram *dg) // IN: | |
-{ | |
- DatagramQueueEntry *dqEntry; | |
- VMCIContext *context; | |
- VMCILockFlags flags; | |
- VMCIHandle dgSrc; | |
- size_t vmciDgSize; | |
- | |
- ASSERT(dg); | |
- vmciDgSize = VMCI_DG_SIZE(dg); | |
- ASSERT(vmciDgSize <= VMCI_MAX_DG_SIZE); | |
- | |
- /* Get the target VM's VMCI context. */ | |
- context = VMCIContext_Get(cid); | |
- if (context == NULL) { | |
- VMCI_DEBUG_LOG(4, (LGPFX"Invalid context (ID=0x%x).\n", cid)); | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- /* Allocate guest call entry and add it to the target VM's queue. */ | |
- dqEntry = VMCI_AllocKernelMem(sizeof *dqEntry, VMCI_MEMORY_NONPAGED); | |
- if (dqEntry == NULL) { | |
- VMCI_WARNING((LGPFX"Failed to allocate memory for datagram.\n")); | |
- VMCIContext_Release(context); | |
- return VMCI_ERROR_NO_MEM; | |
- } | |
- dqEntry->dg = dg; | |
- dqEntry->dgSize = vmciDgSize; | |
- dgSrc = dg->src; | |
- VMCIList_InitEntry(&dqEntry->listItem); | |
- | |
- VMCI_GrabLock(&context->lock, &flags); | |
- | |
-#if defined(VMKERNEL) | |
- if (context->inFilters != NULL) { | |
- if (VMCIFilterDenyDgIn(context->inFilters->filters, dg)) { | |
- VMCI_ReleaseLock(&context->lock, flags); | |
- VMCIContext_Release(context); | |
- VMCI_FreeKernelMem(dqEntry, sizeof *dqEntry); | |
- return VMCI_ERROR_NO_ACCESS; | |
- } | |
- } | |
-#endif | |
- | |
- /* | |
- * We put a higher limit on datagrams from the hypervisor. If the pending | |
- * datagram is not from hypervisor, then we check if enqueueing it would | |
- * exceed the VMCI_MAX_DATAGRAM_QUEUE_SIZE limit on the destination. If the | |
- * pending datagram is from hypervisor, we allow it to be queued at the | |
- * destination side provided we don't reach the | |
- * VMCI_MAX_DATAGRAM_AND_EVENT_QUEUE_SIZE limit. | |
- */ | |
- if (context->datagramQueueSize + vmciDgSize >= | |
- VMCI_MAX_DATAGRAM_QUEUE_SIZE && | |
- (!VMCI_HANDLE_EQUAL(dgSrc, | |
- VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID, | |
- VMCI_CONTEXT_RESOURCE_ID)) || | |
- context->datagramQueueSize + vmciDgSize >= | |
- VMCI_MAX_DATAGRAM_AND_EVENT_QUEUE_SIZE)) { | |
- VMCI_ReleaseLock(&context->lock, flags); | |
- VMCIContext_Release(context); | |
- VMCI_FreeKernelMem(dqEntry, sizeof *dqEntry); | |
- VMCI_DEBUG_LOG(10, (LGPFX"Context (ID=0x%x) receive queue is full.\n", | |
- cid)); | |
- return VMCI_ERROR_NO_RESOURCES; | |
- } | |
- | |
- VMCIList_Insert(&dqEntry->listItem, &context->datagramQueue); | |
- context->pendingDatagrams++; | |
- context->datagramQueueSize += vmciDgSize; | |
- VMCIContextSignalNotify(context); | |
- VMCIHost_SignalCall(&context->hostContext); | |
- VMCI_ReleaseLock(&context->lock, flags); | |
- VMCIContext_Release(context); | |
- | |
- return vmciDgSize; | |
-} | |
- | |
-#undef VMCI_MAX_DATAGRAM_AND_EVENT_QUEUE_SIZE | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIContextExists -- | |
- * | |
- * Internal helper to check if a context with the specified context | |
- * ID exists. Assumes the contextList.lock is held. | |
- * | |
- * Results: | |
- * TRUE if a context exists with the given cid. | |
- * FALSE otherwise | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-static Bool | |
-VMCIContextExists(VMCIId cid) // IN | |
-{ | |
- VMCIContext *context; | |
- VMCIListItem *next; | |
- Bool rv = FALSE; | |
- | |
- VMCIList_Scan(next, &contextList.head) { | |
- context = VMCIList_Entry(next, VMCIContext, listItem); | |
- if (context->cid == cid) { | |
- rv = TRUE; | |
- break; | |
- } | |
- } | |
- return rv; | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIContext_Exists -- | |
- * | |
- * Verifies whether a context with the specified context ID exists. | |
- * | |
- * Results: | |
- * TRUE if a context exists with the given cid. | |
- * FALSE otherwise | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-Bool | |
-VMCIContext_Exists(VMCIId cid) // IN | |
-{ | |
- VMCILockFlags flags; | |
- Bool rv; | |
- | |
- VMCI_GrabLock(&contextList.lock, &flags); | |
- rv = VMCIContextExists(cid); | |
- VMCI_ReleaseLock(&contextList.lock, flags); | |
- return rv; | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIContext_Get -- | |
- * | |
- * Retrieves VMCI context corresponding to the given cid. | |
- * | |
- * Results: | |
- * VMCI context on success, NULL otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-VMCIContext * | |
-VMCIContext_Get(VMCIId cid) // IN | |
-{ | |
- VMCIContext *context = NULL; | |
- VMCIListItem *next; | |
- VMCILockFlags flags; | |
- | |
- if (cid == VMCI_INVALID_ID) { | |
- return NULL; | |
- } | |
- | |
- VMCI_GrabLock(&contextList.lock, &flags); | |
- if (VMCIList_Empty(&contextList.head)) { | |
- goto out; | |
- } | |
- | |
- VMCIList_Scan(next, &contextList.head) { | |
- context = VMCIList_Entry(next, VMCIContext, listItem); | |
- if (context->cid == cid) { | |
- /* | |
- * At this point, we are sure that the reference count is | |
- * larger already than zero. When starting the destruction of | |
- * a context, we always remove it from the context list | |
- * before decreasing the reference count. As we found the | |
- * context here, it hasn't been destroyed yet. This means | |
- * that we are not about to increase the reference count of | |
- * something that is in the process of being destroyed. | |
- */ | |
- | |
- Atomic_Inc(&context->refCount); | |
- break; | |
- } | |
- } | |
- | |
-out: | |
- VMCI_ReleaseLock(&contextList.lock, flags); | |
- return (context && context->cid == cid) ? context : NULL; | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIContext_Release -- | |
- * | |
- * Releases the VMCI context. If this is the last reference to | |
- * the context it will be deallocated. A context is created with | |
- * a reference count of one, and on destroy, it is removed from | |
- * the context list before its reference count is | |
- * decremented. Thus, if we reach zero, we are sure that nobody | |
- * else are about to increment it (they need the entry in the | |
- * context list for that). This function musn't be called with a | |
- * lock held. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * Paged memory may be deallocated. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCIContext_Release(VMCIContext *context) // IN | |
-{ | |
- uint32 refCount; | |
- ASSERT(context); | |
- refCount = Atomic_FetchAndDec(&context->refCount); | |
- if (refCount == 1) { | |
- VMCIContextFreeContext(context); | |
- } | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIContext_DequeueDatagram -- | |
- * | |
- * Dequeues the next datagram and returns it to caller. | |
- * The caller passes in a pointer to the max size datagram | |
- * it can handle and the datagram is only unqueued if the | |
- * size is less than maxSize. If larger maxSize is set to | |
- * the size of the datagram to give the caller a chance to | |
- * set up a larger buffer for the guestcall. | |
- * | |
- * Results: | |
- * On success: 0 if no more pending datagrams, otherwise the size of | |
- * the next pending datagram. | |
- * On failure: appropriate error code. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCIContext_DequeueDatagram(VMCIContext *context, // IN | |
- size_t *maxSize, // IN/OUT: max size of datagram caller can handle. | |
- VMCIDatagram **dg) // OUT: | |
-{ | |
- DatagramQueueEntry *dqEntry; | |
- VMCIListItem *listItem; | |
- VMCILockFlags flags; | |
- int rv; | |
- | |
- ASSERT(context && dg); | |
- | |
- /* Dequeue the next datagram entry. */ | |
- VMCI_GrabLock(&context->lock, &flags); | |
- if (context->pendingDatagrams == 0) { | |
- VMCIContextClearNotifyAndCall(context); | |
- VMCI_ReleaseLock(&context->lock, flags); | |
- VMCI_DEBUG_LOG(4, (LGPFX"No datagrams pending.\n")); | |
- return VMCI_ERROR_NO_MORE_DATAGRAMS; | |
- } | |
- | |
- listItem = VMCIList_First(&context->datagramQueue); | |
- ASSERT (listItem != NULL); | |
- | |
- dqEntry = VMCIList_Entry(listItem, DatagramQueueEntry, listItem); | |
- ASSERT(dqEntry->dg); | |
- | |
- /* Check size of caller's buffer. */ | |
- if (*maxSize < dqEntry->dgSize) { | |
- *maxSize = dqEntry->dgSize; | |
- VMCI_ReleaseLock(&context->lock, flags); | |
- VMCI_DEBUG_LOG(4, (LGPFX"Caller's buffer should be at least " | |
- "(size=%u bytes).\n", (uint32)*maxSize)); | |
- return VMCI_ERROR_NO_MEM; | |
- } | |
- | |
- VMCIList_Remove(listItem); | |
- context->pendingDatagrams--; | |
- context->datagramQueueSize -= dqEntry->dgSize; | |
- if (context->pendingDatagrams == 0) { | |
- VMCIContextClearNotifyAndCall(context); | |
- rv = VMCI_SUCCESS; | |
- } else { | |
- /* | |
- * Return the size of the next datagram. | |
- */ | |
- DatagramQueueEntry *nextEntry; | |
- | |
- listItem = VMCIList_First(&context->datagramQueue); | |
- ASSERT(listItem); | |
- nextEntry = VMCIList_Entry(listItem, DatagramQueueEntry, listItem); | |
- ASSERT(nextEntry && nextEntry->dg); | |
- /* | |
- * The following size_t -> int truncation is fine as the maximum size of | |
- * a (routable) datagram is 68KB. | |
- */ | |
- rv = (int)nextEntry->dgSize; | |
- } | |
- VMCI_ReleaseLock(&context->lock, flags); | |
- | |
- /* Caller must free datagram. */ | |
- ASSERT(dqEntry->dgSize == VMCI_DG_SIZE(dqEntry->dg)); | |
- *dg = dqEntry->dg; | |
- dqEntry->dg = NULL; | |
- VMCI_FreeKernelMem(dqEntry, sizeof *dqEntry); | |
- | |
- return rv; | |
-} | |
- | |
- | |
-#ifdef VMKERNEL | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIContext_SetFSRState -- | |
- * | |
- * Set the states related to FSR (quiesced state, migrateCid, | |
- * active event handle). | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCIContext_SetFSRState(VMCIContext *context, // IN | |
- Bool isQuiesced, // IN | |
- VMCIId migrateCid, // IN | |
- uintptr_t eventHnd, // IN | |
- Bool isLocked) // IN | |
-{ | |
- VMCILockFlags flags; | |
- if (!context) { | |
- return; | |
- } | |
- if (!isLocked) { | |
- VMCI_GrabLock(&context->lock, &flags); | |
- } | |
- context->isQuiesced = isQuiesced; | |
- context->migrateCid = migrateCid; | |
- VMCIHost_SetActiveHnd(&context->hostContext, eventHnd); | |
- if (!isLocked) { | |
- VMCI_ReleaseLock(&context->lock, flags); | |
- } | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIContext_FindAndUpdateSrcFSR -- | |
- * | |
- * Find the source context for fast-suspend-resume. If found, the | |
- * source context's FSR state is changed to reflect the new active | |
- * event handle. | |
- * | |
- * Results: | |
- * If found, source context for fast-suspend-resume, NULL otherwise. | |
- * | |
- * Side effects: | |
- * The source context reference count increased and the caller is | |
- * supposed to release the context once it is done using it. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-VMCIContext * | |
-VMCIContext_FindAndUpdateSrcFSR(VMCIId migrateCid, // IN | |
- uintptr_t eventHnd, // IN | |
- uintptr_t *srcEventHnd) // IN/OUT | |
-{ | |
- VMCIContext *contextSrc = VMCIContext_Get(migrateCid); | |
- | |
- if (contextSrc) { | |
- VMCILockFlags flags; | |
- | |
- VMCI_GrabLock(&contextSrc->lock, &flags); | |
- if (contextSrc->isQuiesced && contextSrc->migrateCid == migrateCid) { | |
- if (srcEventHnd) { | |
- *srcEventHnd = VMCIHost_GetActiveHnd(&contextSrc->hostContext); | |
- ASSERT(*srcEventHnd != VMCI_INVALID_ID); | |
- } | |
- VMCIContext_SetFSRState(contextSrc, FALSE, VMCI_INVALID_ID, | |
- eventHnd, TRUE); | |
- VMCI_ReleaseLock(&contextSrc->lock, flags); | |
- return contextSrc; | |
- } | |
- VMCI_ReleaseLock(&contextSrc->lock, flags); | |
- VMCIContext_Release(contextSrc); | |
- } | |
- return NULL; | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIContext_IsActiveHnd -- | |
- * | |
- * Whether the curent event handle is the active handle. | |
- * | |
- * Results: | |
- * TRUE if the event handle is active, FALSE otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-Bool | |
-VMCIContext_IsActiveHnd(VMCIContext *context, // IN | |
- uintptr_t eventHnd) // IN | |
-{ | |
- VMCILockFlags flags; | |
- Bool isActive; | |
- | |
- ASSERT(context); | |
- VMCI_GrabLock(&context->lock, &flags); | |
- isActive = VMCIHost_IsActiveHnd(&context->hostContext, eventHnd); | |
- VMCI_ReleaseLock(&context->lock, flags); | |
- return isActive; | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIContext_GetActiveHnd -- | |
- * | |
- * Returns the curent event handle. | |
- * | |
- * Results: | |
- * The current active handle. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-uintptr_t | |
-VMCIContext_GetActiveHnd(VMCIContext *context) // IN | |
-{ | |
- VMCILockFlags flags; | |
- uintptr_t activeHnd; | |
- | |
- ASSERT(context); | |
- VMCI_GrabLock(&context->lock, &flags); | |
- activeHnd = VMCIHost_GetActiveHnd(&context->hostContext); | |
- VMCI_ReleaseLock(&context->lock, flags); | |
- return activeHnd; | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIContext_SetInactiveHnd -- | |
- * | |
- * Set the handle to be the inactive one. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCIContext_SetInactiveHnd(VMCIContext *context, // IN | |
- uintptr_t eventHnd) // IN | |
-{ | |
- VMCILockFlags flags; | |
- | |
- ASSERT(context); | |
- VMCI_GrabLock(&context->lock, &flags); | |
- VMCIHost_SetInactiveHnd(&context->hostContext, eventHnd); | |
- VMCI_ReleaseLock(&context->lock, flags); | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIContext_RemoveHnd -- | |
- * | |
- * Remove the event handle from host context. | |
- * | |
- * Results: | |
- * Whether the handle exists and removed, also number of handles | |
- * before removal and number of handles after removal. | |
- * | |
- * Side effects: | |
- * If this is active handle, the inactive handle becomes active. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-Bool | |
-VMCIContext_RemoveHnd(VMCIContext *context, // IN | |
- uintptr_t eventHnd, // IN | |
- uint32 *numOld, // OUT | |
- uint32 *numNew) // OUT | |
-{ | |
- VMCILockFlags flags; | |
- uint32 numHandleOld, numHandleNew; | |
- Bool ret; | |
- | |
- ASSERT(context); | |
- VMCI_GrabLock(&context->lock, &flags); | |
- numHandleOld = VMCIHost_NumHnds(&context->hostContext); | |
- ret = VMCIHost_RemoveHnd(&context->hostContext, eventHnd); | |
- numHandleNew = VMCIHost_NumHnds(&context->hostContext); | |
- /* | |
- * This is needed to prevent FSR to share this | |
- * context while this context is being destroyed. | |
- */ | |
- if (ret && numHandleOld == 1 && numHandleNew == 1) { | |
- context->migrateCid = VMCI_INVALID_ID; | |
- } | |
- VMCI_ReleaseLock(&context->lock, flags); | |
- | |
- if (numOld) { | |
- *numOld = numHandleOld; | |
- } | |
- if (numNew) { | |
- *numNew = numHandleNew; | |
- } | |
- return ret; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIContext_ClearDatagrams -- | |
- * | |
- * Clear pending datagrams. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCIContext_ClearDatagrams(VMCIContext *context) // IN | |
-{ | |
- int retval; | |
- VMCIDatagram *dg = NULL; | |
- size_t size = VMCI_MAX_DG_SIZE; | |
- uint32 pending; | |
- | |
- /* Drop all datagrams that are currently pending for given context. */ | |
- if (context == NULL) { | |
- return; | |
- } | |
- retval = VMCIContext_PendingDatagrams(context->cid, &pending); | |
- if (retval != VMCI_SUCCESS) { | |
- /* | |
- * This shouldn't happen as we already verified that the context | |
- * exists. | |
- */ | |
- | |
- ASSERT(FALSE); | |
- return; | |
- } | |
- | |
- /* | |
- * We drain the queue for any datagrams pending at the beginning of | |
- * the loop. As datagrams may arrive at any point in time, we | |
- * cannot guarantee that the queue is empty after this point. Only | |
- * removing a fixed number of pending datagrams prevents us from | |
- * looping forever. | |
- */ | |
- | |
- while (pending > 0 && | |
- VMCIContext_DequeueDatagram(context, &size, &dg) >= 0) { | |
- ASSERT(dg); | |
- VMCI_FreeKernelMem(dg, VMCI_DG_SIZE(dg)); | |
- --pending; | |
- } | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIContext_SetId -- | |
- * | |
- * Set the cid of given VMCI context. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCIContext_SetId(VMCIContext *context, // IN | |
- VMCIId cid) // IN: | |
-{ | |
- VMCILockFlags flags; | |
- | |
- if (!context) { | |
- return; | |
- } | |
- VMCI_GrabLock(&context->lock, &flags); | |
- context->cid = cid; | |
- VMCI_ReleaseLock(&context->lock, flags); | |
-} | |
-#endif | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIContext_GetId -- | |
- * | |
- * Retrieves cid of given VMCI context. | |
- * | |
- * Results: | |
- * VMCIId of context on success, VMCI_INVALID_ID otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-VMCIId | |
-VMCIContext_GetId(VMCIContext *context) // IN: | |
-{ | |
- if (!context) { | |
- return VMCI_INVALID_ID; | |
- } | |
- ASSERT(context->cid != VMCI_INVALID_ID); | |
- return context->cid; | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * vmci_context_get_priv_flags -- | |
- * | |
- * Retrieves the privilege flags of the given VMCI context ID. | |
- * | |
- * Results: | |
- * Context's privilege flags. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-VMCI_EXPORT_SYMBOL(vmci_context_get_priv_flags) | |
-VMCIPrivilegeFlags | |
-vmci_context_get_priv_flags(VMCIId contextID) // IN | |
-{ | |
- if (VMCI_HostPersonalityActive()) { | |
- VMCIPrivilegeFlags flags; | |
- VMCIContext *context; | |
- | |
- context = VMCIContext_Get(contextID); | |
- if (!context) { | |
- return VMCI_LEAST_PRIVILEGE_FLAGS; | |
- } | |
- flags = context->privFlags; | |
- VMCIContext_Release(context); | |
- return flags; | |
- } | |
- return VMCI_NO_PRIVILEGE_FLAGS; | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIContext_AddNotification -- | |
- * | |
- * Add remoteCID to list of contexts current contexts wants | |
- * notifications from/about. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS on success, error code otherwise. | |
- * | |
- * Side effects: | |
- * As in VMCIHandleArray_AppendEntry(). | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCIContext_AddNotification(VMCIId contextID, // IN: | |
- VMCIId remoteCID) // IN: | |
-{ | |
- int result = VMCI_ERROR_ALREADY_EXISTS; | |
- VMCILockFlags flags; | |
- VMCILockFlags firingFlags; | |
- VMCIHandle notifierHandle; | |
- VMCIContext *context = VMCIContext_Get(contextID); | |
- if (context == NULL) { | |
- return VMCI_ERROR_NOT_FOUND; | |
- } | |
- | |
- if (VMCI_CONTEXT_IS_VM(contextID) && VMCI_CONTEXT_IS_VM(remoteCID)) { | |
- VMCI_DEBUG_LOG(4, (LGPFX"Context removed notifications for other VMs not " | |
- "supported (src=0x%x, remote=0x%x).\n", | |
- contextID, remoteCID)); | |
- result = VMCI_ERROR_DST_UNREACHABLE; | |
- goto out; | |
- } | |
- | |
- if (context->privFlags & VMCI_PRIVILEGE_FLAG_RESTRICTED) { | |
- result = VMCI_ERROR_NO_ACCESS; | |
- goto out; | |
- } | |
- | |
- notifierHandle = VMCI_MAKE_HANDLE(remoteCID, VMCI_EVENT_HANDLER); | |
- VMCI_GrabLock(&contextList.firingLock, &firingFlags); | |
- VMCI_GrabLock(&context->lock, &flags); | |
- if (!VMCIHandleArray_HasEntry(context->notifierArray, notifierHandle)) { | |
- VMCIHandleArray_AppendEntry(&context->notifierArray, notifierHandle); | |
- result = VMCI_SUCCESS; | |
- } | |
- VMCI_ReleaseLock(&context->lock, flags); | |
- VMCI_ReleaseLock(&contextList.firingLock, firingFlags); | |
-out: | |
- VMCIContext_Release(context); | |
- return result; | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIContext_RemoveNotification -- | |
- * | |
- * Remove remoteCID from current context's list of contexts it is | |
- * interested in getting notifications from/about. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS on success, error code otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCIContext_RemoveNotification(VMCIId contextID, // IN: | |
- VMCIId remoteCID) // IN: | |
-{ | |
- VMCILockFlags flags; | |
- VMCILockFlags firingFlags; | |
- VMCIContext *context = VMCIContext_Get(contextID); | |
- VMCIHandle tmpHandle; | |
- if (context == NULL) { | |
- return VMCI_ERROR_NOT_FOUND; | |
- } | |
- VMCI_GrabLock(&contextList.firingLock, &firingFlags); | |
- VMCI_GrabLock(&context->lock, &flags); | |
- tmpHandle = | |
- VMCIHandleArray_RemoveEntry(context->notifierArray, | |
- VMCI_MAKE_HANDLE(remoteCID, | |
- VMCI_EVENT_HANDLER)); | |
- VMCI_ReleaseLock(&context->lock, flags); | |
- VMCI_ReleaseLock(&contextList.firingLock, firingFlags); | |
- VMCIContext_Release(context); | |
- | |
- if (VMCI_HANDLE_EQUAL(tmpHandle, VMCI_INVALID_HANDLE)) { | |
- return VMCI_ERROR_NOT_FOUND; | |
- } | |
- return VMCI_SUCCESS; | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIContextFireNotification -- | |
- * | |
- * Fire notification for all contexts interested in given cid. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS on success, error code otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-static int | |
-VMCIContextFireNotification(VMCIId contextID, // IN | |
- VMCIPrivilegeFlags privFlags) // IN | |
-{ | |
- uint32 i, arraySize; | |
- VMCIListItem *next; | |
- VMCILockFlags flags; | |
- VMCILockFlags firingFlags; | |
- VMCIHandleArray *subscriberArray; | |
- VMCIHandle contextHandle = VMCI_MAKE_HANDLE(contextID, VMCI_EVENT_HANDLER); | |
- | |
- /* | |
- * We create an array to hold the subscribers we find when scanning through | |
- * all contexts. | |
- */ | |
- subscriberArray = VMCIHandleArray_Create(0); | |
- if (subscriberArray == NULL) { | |
- return VMCI_ERROR_NO_MEM; | |
- } | |
- | |
- /* | |
- * Scan all contexts to find who is interested in being notified about | |
- * given contextID. We have a special firingLock that we use to synchronize | |
- * across all notification operations. This avoids us having to take the | |
- * context lock for each HasEntry call and it solves a lock ranking issue. | |
- */ | |
- VMCI_GrabLock(&contextList.firingLock, &firingFlags); | |
- VMCI_GrabLock(&contextList.lock, &flags); | |
- VMCIList_Scan(next, &contextList.head) { | |
- VMCIContext *subCtx = VMCIList_Entry(next, VMCIContext, listItem); | |
- | |
- /* | |
- * We only deliver notifications of the removal of contexts, if | |
- * the two contexts are allowed to interact. | |
- */ | |
- | |
- if (VMCIHandleArray_HasEntry(subCtx->notifierArray, contextHandle) && | |
- !VMCIDenyInteraction(privFlags, subCtx->privFlags)) { | |
- VMCIHandleArray_AppendEntry(&subscriberArray, | |
- VMCI_MAKE_HANDLE(subCtx->cid, | |
- VMCI_EVENT_HANDLER)); | |
- } | |
- } | |
- VMCI_ReleaseLock(&contextList.lock, flags); | |
- VMCI_ReleaseLock(&contextList.firingLock, firingFlags); | |
- | |
- /* Fire event to all subscribers. */ | |
- arraySize = VMCIHandleArray_GetSize(subscriberArray); | |
- for (i = 0; i < arraySize; i++) { | |
- int result; | |
- VMCIEventMsg *eMsg; | |
- VMCIEventPayload_Context *evPayload; | |
- char buf[sizeof *eMsg + sizeof *evPayload]; | |
- | |
- eMsg = (VMCIEventMsg *)buf; | |
- | |
- /* Clear out any garbage. */ | |
- memset(eMsg, 0, sizeof *eMsg + sizeof *evPayload); | |
- eMsg->hdr.dst = VMCIHandleArray_GetEntry(subscriberArray, i); | |
- eMsg->hdr.src = VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID, | |
- VMCI_CONTEXT_RESOURCE_ID); | |
- eMsg->hdr.payloadSize = sizeof *eMsg + sizeof *evPayload - | |
- sizeof eMsg->hdr; | |
- eMsg->eventData.event = VMCI_EVENT_CTX_REMOVED; | |
- evPayload = VMCIEventMsgPayload(eMsg); | |
- evPayload->contextID = contextID; | |
- | |
- result = VMCIDatagram_Dispatch(VMCI_HYPERVISOR_CONTEXT_ID, | |
- (VMCIDatagram *)eMsg, FALSE); | |
- if (result < VMCI_SUCCESS) { | |
- VMCI_DEBUG_LOG(4, (LGPFX"Failed to enqueue event datagram " | |
- "(type=%d) for context (ID=0x%x).\n", | |
- eMsg->eventData.event, eMsg->hdr.dst.context)); | |
- /* We continue to enqueue on next subscriber. */ | |
- } | |
- } | |
- VMCIHandleArray_Destroy(subscriberArray); | |
- | |
- return VMCI_SUCCESS; | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIContext_GetCheckpointState -- | |
- * | |
- * Get current context's checkpoint state of given type. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS on success, error code otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCIContext_GetCheckpointState(VMCIId contextID, // IN: | |
- uint32 cptType, // IN: | |
- uint32 *bufSize, // IN/OUT: | |
- char **cptBufPtr) // OUT: | |
-{ | |
- int i, result; | |
- VMCILockFlags flags; | |
- uint32 arraySize, cptDataSize; | |
- VMCIHandleArray *array; | |
- VMCIContext *context; | |
- char *cptBuf; | |
- Bool getContextID; | |
- | |
- ASSERT(bufSize && cptBufPtr); | |
- | |
- context = VMCIContext_Get(contextID); | |
- if (context == NULL) { | |
- return VMCI_ERROR_NOT_FOUND; | |
- } | |
- | |
- VMCI_GrabLock(&context->lock, &flags); | |
- if (cptType == VMCI_NOTIFICATION_CPT_STATE) { | |
- ASSERT(context->notifierArray); | |
- array = context->notifierArray; | |
- getContextID = TRUE; | |
- } else if (cptType == VMCI_WELLKNOWN_CPT_STATE) { | |
- /* | |
- * For compatibility with VMX'en with VM to VM communication, we | |
- * always return zero wellknown handles. | |
- */ | |
- | |
- *bufSize = 0; | |
- *cptBufPtr = NULL; | |
- result = VMCI_SUCCESS; | |
- goto release; | |
- } else if (cptType == VMCI_DOORBELL_CPT_STATE) { | |
- ASSERT(context->doorbellArray); | |
- array = context->doorbellArray; | |
- getContextID = FALSE; | |
- } else { | |
- VMCI_DEBUG_LOG(4, (LGPFX"Invalid cpt state (type=%d).\n", cptType)); | |
- result = VMCI_ERROR_INVALID_ARGS; | |
- goto release; | |
- } | |
- | |
- arraySize = VMCIHandleArray_GetSize(array); | |
- if (arraySize > 0) { | |
- if (cptType == VMCI_DOORBELL_CPT_STATE) { | |
- cptDataSize = arraySize * sizeof(VMCIDoorbellCptState); | |
- } else { | |
- cptDataSize = arraySize * sizeof(VMCIId); | |
- } | |
- if (*bufSize < cptDataSize) { | |
- *bufSize = cptDataSize; | |
- result = VMCI_ERROR_MORE_DATA; | |
- goto release; | |
- } | |
- | |
- cptBuf = VMCI_AllocKernelMem(cptDataSize, | |
- VMCI_MEMORY_NONPAGED | VMCI_MEMORY_ATOMIC); | |
- if (cptBuf == NULL) { | |
- result = VMCI_ERROR_NO_MEM; | |
- goto release; | |
- } | |
- | |
- for (i = 0; i < arraySize; i++) { | |
- VMCIHandle tmpHandle = VMCIHandleArray_GetEntry(array, i); | |
- if (cptType == VMCI_DOORBELL_CPT_STATE) { | |
- ((VMCIDoorbellCptState *)cptBuf)[i].handle = tmpHandle; | |
- } else { | |
- ((VMCIId *)cptBuf)[i] = | |
- getContextID ? tmpHandle.context : tmpHandle.resource; | |
- } | |
- } | |
- *bufSize = cptDataSize; | |
- *cptBufPtr = cptBuf; | |
- } else { | |
- *bufSize = 0; | |
- *cptBufPtr = NULL; | |
- } | |
- result = VMCI_SUCCESS; | |
- | |
- release: | |
- VMCI_ReleaseLock(&context->lock, flags); | |
- VMCIContext_Release(context); | |
- | |
- return result; | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIContext_SetCheckpointState -- | |
- * | |
- * Set current context's checkpoint state of given type. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS on success, error code otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCIContext_SetCheckpointState(VMCIId contextID, // IN: | |
- uint32 cptType, // IN: | |
- uint32 bufSize, // IN: | |
- char *cptBuf) // IN: | |
-{ | |
- uint32 i; | |
- VMCIId currentID; | |
- int result = VMCI_SUCCESS; | |
- uint32 numIDs = bufSize / sizeof(VMCIId); | |
- ASSERT(cptBuf); | |
- | |
- if (cptType == VMCI_WELLKNOWN_CPT_STATE && numIDs > 0) { | |
- /* | |
- * We would end up here if VMX with VM to VM communication | |
- * attempts to restore a checkpoint with wellknown handles. | |
- */ | |
- | |
- VMCI_WARNING((LGPFX"Attempt to restore checkpoint with obsolete " | |
- "wellknown handles.\n")); | |
- return VMCI_ERROR_OBSOLETE; | |
- } | |
- | |
- if (cptType != VMCI_NOTIFICATION_CPT_STATE) { | |
- VMCI_DEBUG_LOG(4, (LGPFX"Invalid cpt state (type=%d).\n", cptType)); | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- for (i = 0; i < numIDs && result == VMCI_SUCCESS; i++) { | |
- currentID = ((VMCIId *)cptBuf)[i]; | |
- result = VMCIContext_AddNotification(contextID, currentID); | |
- if (result != VMCI_SUCCESS) { | |
- break; | |
- } | |
- } | |
- if (result != VMCI_SUCCESS) { | |
- VMCI_DEBUG_LOG(4, (LGPFX"Failed to set cpt state (type=%d) (error=%d).\n", | |
- cptType, result)); | |
- } | |
- return result; | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIContext_ReceiveNotificationsGet -- | |
- * | |
- * Retrieves the specified context's pending notifications in the | |
- * form of a handle array. The handle arrays returned are the | |
- * actual data - not a copy and should not be modified by the | |
- * caller. They must be released using | |
- * VMCIContext_ReceiveNotificationsRelease. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS on success, error code otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCIContext_ReceiveNotificationsGet(VMCIId contextID, // IN | |
- VMCIHandleArray **dbHandleArray, // OUT | |
- VMCIHandleArray **qpHandleArray) // OUT | |
-{ | |
- VMCIContext *context; | |
- VMCILockFlags flags; | |
- int result = VMCI_SUCCESS; | |
- | |
- ASSERT(dbHandleArray && qpHandleArray); | |
- | |
- context = VMCIContext_Get(contextID); | |
- if (context == NULL) { | |
- return VMCI_ERROR_NOT_FOUND; | |
- } | |
- VMCI_GrabLock(&context->lock, &flags); | |
- | |
- *dbHandleArray = context->pendingDoorbellArray; | |
- context->pendingDoorbellArray = VMCIHandleArray_Create(0); | |
- if (!context->pendingDoorbellArray) { | |
- context->pendingDoorbellArray = *dbHandleArray; | |
- *dbHandleArray = NULL; | |
- result = VMCI_ERROR_NO_MEM; | |
- } | |
- *qpHandleArray = NULL; | |
- | |
- VMCI_ReleaseLock(&context->lock, flags); | |
- VMCIContext_Release(context); | |
- | |
- return result; | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIContext_ReceiveNotificationsRelease -- | |
- * | |
- * Releases handle arrays with pending notifications previously | |
- * retrieved using VMCIContext_ReceiveNotificationsGet. If the | |
- * notifications were not successfully handed over to the guest, | |
- * success must be false. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCIContext_ReceiveNotificationsRelease(VMCIId contextID, // IN | |
- VMCIHandleArray *dbHandleArray, // IN | |
- VMCIHandleArray *qpHandleArray, // IN | |
- Bool success) // IN | |
-{ | |
- VMCIContext *context = VMCIContext_Get(contextID); | |
- | |
- if (context) { | |
- VMCILockFlags flags; | |
- | |
- VMCI_GrabLock(&context->lock, &flags); | |
- if (!success) { | |
- VMCIHandle handle; | |
- | |
- /* | |
- * New notifications may have been added while we were not | |
- * holding the context lock, so we transfer any new pending | |
- * doorbell notifications to the old array, and reinstate the | |
- * old array. | |
- */ | |
- | |
- handle = VMCIHandleArray_RemoveTail(context->pendingDoorbellArray); | |
- while (!VMCI_HANDLE_INVALID(handle)) { | |
- ASSERT(VMCIHandleArray_HasEntry(context->doorbellArray, handle)); | |
- if (!VMCIHandleArray_HasEntry(dbHandleArray, handle)) { | |
- VMCIHandleArray_AppendEntry(&dbHandleArray, handle); | |
- } | |
- handle = VMCIHandleArray_RemoveTail(context->pendingDoorbellArray); | |
- } | |
- VMCIHandleArray_Destroy(context->pendingDoorbellArray); | |
- context->pendingDoorbellArray = dbHandleArray; | |
- dbHandleArray = NULL; | |
- } else { | |
- VMCIContextClearNotifyAndCall(context); | |
- } | |
- VMCI_ReleaseLock(&context->lock, flags); | |
- VMCIContext_Release(context); | |
- } else { | |
- /* | |
- * The OS driver part is holding on to the context for the | |
- * duration of the receive notification ioctl, so it should | |
- * still be here. | |
- */ | |
- | |
- ASSERT(FALSE); | |
- } | |
- | |
- if (dbHandleArray) { | |
- VMCIHandleArray_Destroy(dbHandleArray); | |
- } | |
- if (qpHandleArray) { | |
- VMCIHandleArray_Destroy(qpHandleArray); | |
- } | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIContext_DoorbellCreate -- | |
- * | |
- * Registers that a new doorbell handle has been allocated by the | |
- * context. Only doorbell handles registered can be notified. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS on success, appropriate error code otherewise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCIContext_DoorbellCreate(VMCIId contextID, // IN | |
- VMCIHandle handle) // IN | |
-{ | |
- VMCIContext *context; | |
- VMCILockFlags flags; | |
- int result; | |
- | |
- if (contextID == VMCI_INVALID_ID || VMCI_HANDLE_INVALID(handle)) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- context = VMCIContext_Get(contextID); | |
- if (context == NULL) { | |
- return VMCI_ERROR_NOT_FOUND; | |
- } | |
- | |
- VMCI_GrabLock(&context->lock, &flags); | |
- if (!VMCIHandleArray_HasEntry(context->doorbellArray, handle)) { | |
- VMCIHandleArray_AppendEntry(&context->doorbellArray, handle); | |
- result = VMCI_SUCCESS; | |
- } else { | |
- result = VMCI_ERROR_DUPLICATE_ENTRY; | |
- } | |
- VMCI_ReleaseLock(&context->lock, flags); | |
- | |
- VMCIContext_Release(context); | |
- | |
- return result; | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIContext_DoorbellDestroy -- | |
- * | |
- * Unregisters a doorbell handle that was previously registered | |
- * with VMCIContext_DoorbellCreate. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS on success, appropriate error code otherewise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCIContext_DoorbellDestroy(VMCIId contextID, // IN | |
- VMCIHandle handle) // IN | |
-{ | |
- VMCIContext *context; | |
- VMCILockFlags flags; | |
- VMCIHandle removedHandle; | |
- | |
- if (contextID == VMCI_INVALID_ID || VMCI_HANDLE_INVALID(handle)) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- context = VMCIContext_Get(contextID); | |
- if (context == NULL) { | |
- return VMCI_ERROR_NOT_FOUND; | |
- } | |
- | |
- VMCI_GrabLock(&context->lock, &flags); | |
- removedHandle = VMCIHandleArray_RemoveEntry(context->doorbellArray, handle); | |
- VMCIHandleArray_RemoveEntry(context->pendingDoorbellArray, handle); | |
- VMCI_ReleaseLock(&context->lock, flags); | |
- | |
- VMCIContext_Release(context); | |
- | |
- if (VMCI_HANDLE_INVALID(removedHandle)) { | |
- return VMCI_ERROR_NOT_FOUND; | |
- } else { | |
- return VMCI_SUCCESS; | |
- } | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIContext_DoorbellDestroyAll -- | |
- * | |
- * Unregisters all doorbell handles that were previously | |
- * registered with VMCIContext_DoorbellCreate. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS on success, appropriate error code otherewise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCIContext_DoorbellDestroyAll(VMCIId contextID) // IN | |
-{ | |
- VMCIContext *context; | |
- VMCILockFlags flags; | |
- VMCIHandle removedHandle; | |
- | |
- if (contextID == VMCI_INVALID_ID) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- context = VMCIContext_Get(contextID); | |
- if (context == NULL) { | |
- return VMCI_ERROR_NOT_FOUND; | |
- } | |
- | |
- VMCI_GrabLock(&context->lock, &flags); | |
- do { | |
- removedHandle = VMCIHandleArray_RemoveTail(context->doorbellArray); | |
- } while(!VMCI_HANDLE_INVALID(removedHandle)); | |
- do { | |
- removedHandle = VMCIHandleArray_RemoveTail(context->pendingDoorbellArray); | |
- } while(!VMCI_HANDLE_INVALID(removedHandle)); | |
- VMCI_ReleaseLock(&context->lock, flags); | |
- | |
- VMCIContext_Release(context); | |
- | |
- return VMCI_SUCCESS; | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIContext_NotifyDoorbell -- | |
- * | |
- * Registers a notification of a doorbell handle initiated by the | |
- * specified source context. The notification of doorbells are | |
- * subject to the same isolation rules as datagram delivery. To | |
- * allow host side senders of notifications a finer granularity | |
- * of sender rights than those assigned to the sending context | |
- * itself, the host context is required to specify a different | |
- * set of privilege flags that will override the privileges of | |
- * the source context. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS on success, appropriate error code otherewise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCIContext_NotifyDoorbell(VMCIId srcCID, // IN | |
- VMCIHandle handle, // IN | |
- VMCIPrivilegeFlags srcPrivFlags) // IN | |
-{ | |
- VMCIContext *dstContext; | |
- VMCILockFlags flags; | |
- int result; | |
- | |
- if (VMCI_HANDLE_INVALID(handle)) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- /* Get the target VM's VMCI context. */ | |
- dstContext = VMCIContext_Get(handle.context); | |
- if (dstContext == NULL) { | |
- VMCI_DEBUG_LOG(4, (LGPFX"Invalid context (ID=0x%x).\n", handle.context)); | |
- return VMCI_ERROR_NOT_FOUND; | |
- } | |
- | |
- if (srcCID != handle.context) { | |
- VMCIPrivilegeFlags dstPrivFlags; | |
- | |
- if (!vmkernel && VMCI_CONTEXT_IS_VM(srcCID) && | |
- VMCI_CONTEXT_IS_VM(handle.context)) { | |
- VMCI_DEBUG_LOG(4, (LGPFX"Doorbell notification from VM to VM not " | |
- "supported (src=0x%x, dst=0x%x).\n", | |
- srcCID, handle.context)); | |
- result = VMCI_ERROR_DST_UNREACHABLE; | |
- goto out; | |
- } | |
- | |
- result = VMCIDoorbellGetPrivFlags(handle, &dstPrivFlags); | |
- if (result < VMCI_SUCCESS) { | |
- VMCI_WARNING((LGPFX"Failed to get privilege flags for destination " | |
- "(handle=0x%x:0x%x).\n", handle.context, | |
- handle.resource)); | |
- goto out; | |
- } | |
- | |
- if (srcCID != VMCI_HOST_CONTEXT_ID || | |
- srcPrivFlags == VMCI_NO_PRIVILEGE_FLAGS) { | |
- srcPrivFlags = vmci_context_get_priv_flags(srcCID); | |
- } | |
- | |
- if (VMCIDenyInteraction(srcPrivFlags, dstPrivFlags)) { | |
- result = VMCI_ERROR_NO_ACCESS; | |
- goto out; | |
- } | |
- } | |
- | |
- if (handle.context == VMCI_HOST_CONTEXT_ID) { | |
- result = VMCIDoorbellHostContextNotify(srcCID, handle); | |
- } else { | |
- VMCI_GrabLock(&dstContext->lock, &flags); | |
- | |
-#if defined(VMKERNEL) | |
- if (dstContext->inFilters != NULL && | |
- VMCIFilterProtoDeny(dstContext->inFilters->filters, handle.resource, | |
- VMCI_FP_DOORBELL)) { | |
- result = VMCI_ERROR_NO_ACCESS; | |
- } else | |
-#endif // VMKERNEL | |
- if (!VMCIHandleArray_HasEntry(dstContext->doorbellArray, handle)) { | |
- result = VMCI_ERROR_NOT_FOUND; | |
- } else { | |
- if (!VMCIHandleArray_HasEntry(dstContext->pendingDoorbellArray, handle)) { | |
- VMCIHandleArray_AppendEntry(&dstContext->pendingDoorbellArray, handle); | |
- | |
- VMCIContextSignalNotify(dstContext); | |
-#if defined(VMKERNEL) | |
- VMCIHost_SignalBitmap(&dstContext->hostContext); | |
-#else | |
- VMCIHost_SignalCall(&dstContext->hostContext); | |
-#endif | |
- } | |
- result = VMCI_SUCCESS; | |
- } | |
- VMCI_ReleaseLock(&dstContext->lock, flags); | |
- } | |
- | |
-out: | |
- VMCIContext_Release(dstContext); | |
- | |
- return result; | |
-} | |
- | |
- | |
-#ifdef VMKERNEL | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIContext_SignalPendingDoorbells -- | |
- * | |
- * Signals the guest if any doorbell notifications are | |
- * pending. This is used after the VMCI device is unquiesced to | |
- * ensure that no pending notifications go unnoticed, since | |
- * signals may not be fully processed while the device is | |
- * quiesced. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCIContext_SignalPendingDoorbells(VMCIId contextID) | |
-{ | |
- VMCIContext *context; | |
- VMCILockFlags flags; | |
- Bool pending; | |
- | |
- context = VMCIContext_Get(contextID); | |
- if (!context) { | |
- ASSERT(FALSE); | |
- return; | |
- } | |
- | |
- VMCI_GrabLock(&context->lock, &flags); | |
- pending = VMCIHandleArray_GetSize(context->pendingDoorbellArray) > 0; | |
- VMCI_ReleaseLock(&context->lock, flags); | |
- | |
- if (pending) { | |
- VMCIHost_SignalBitmap(&context->hostContext); | |
- } | |
- | |
- VMCIContext_Release(context); | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIContext_SignalPendingDatagrams -- | |
- * | |
- * Signals the guest if any datagrams are pending. This is used | |
- * after the VMCI device is unquiesced to ensure that no pending | |
- * datagrams go unnoticed, since signals may not be fully | |
- * processed while the device is quiesced. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCIContext_SignalPendingDatagrams(VMCIId contextID) | |
-{ | |
- Bool pending; | |
- VMCIContext *context; | |
- VMCILockFlags flags; | |
- | |
- context = VMCIContext_Get(contextID); | |
- if (!context) { | |
- ASSERT(FALSE); | |
- return; | |
- } | |
- | |
- VMCI_GrabLock(&context->lock, &flags); | |
- pending = context->pendingDatagrams; | |
- VMCI_ReleaseLock(&context->lock, flags); | |
- | |
- if (pending) { | |
- VMCIHost_SignalCall(&context->hostContext); | |
- } | |
- | |
- VMCIContext_Release(context); | |
-} | |
- | |
-#endif // defined(VMKERNEL) | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * vmci_cid_2_host_vm_id -- | |
- * | |
- * Maps a context ID to the host specific (process/world) ID | |
- * of the VM/VMX. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS on success, error code otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-VMCI_EXPORT_SYMBOL(vmci_cid_2_host_vm_id) | |
-int | |
-vmci_cid_2_host_vm_id(VMCIId contextID, // IN | |
- void *hostVmID, // OUT | |
- size_t hostVmIDLen) // IN | |
-{ | |
-#if defined(VMKERNEL) | |
- VMCIContext *context; | |
- VMCIHostVmID vmID; | |
- int result; | |
- | |
- context = VMCIContext_Get(contextID); | |
- if (!context) { | |
- return VMCI_ERROR_NOT_FOUND; | |
- } | |
- | |
- result = VMCIHost_ContextToHostVmID(&context->hostContext, &vmID); | |
- if (result == VMCI_SUCCESS) { | |
- if (sizeof vmID == hostVmIDLen) { | |
- memcpy(hostVmID, &vmID, hostVmIDLen); | |
- } else { | |
- result = VMCI_ERROR_INVALID_ARGS; | |
- } | |
- } | |
- | |
- VMCIContext_Release(context); | |
- | |
- return result; | |
-#else // !defined(VMKERNEL) | |
- return VMCI_ERROR_UNAVAILABLE; | |
-#endif | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * vmci_is_context_owner -- | |
- * | |
- * Determines whether a given host OS specific representation of | |
- * user is the owner of the VM/VMX. | |
- * | |
- * Results: | |
- * Linux: 1 (true) if hostUser is owner, 0 (false) otherwise. | |
- * Other: VMCI_SUCCESS if the hostUser is owner, error code otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-VMCI_EXPORT_SYMBOL(vmci_is_context_owner) | |
-#if defined(linux) && !defined(VMKERNEL) | |
-int | |
-vmci_is_context_owner(VMCIId contextID, // IN | |
- uid_t uid) // IN | |
-{ | |
- int isOwner = 0; | |
- | |
- if (VMCI_HostPersonalityActive()) { | |
- VMCIContext *context = VMCIContext_Get(contextID); | |
- if (context) { | |
- if (context->validUser) { | |
- if (VMCIHost_CompareUser((VMCIHostUser *)&uid, | |
- &context->user) == VMCI_SUCCESS) { | |
- isOwner = 1; | |
- } | |
- } | |
- VMCIContext_Release(context); | |
- } | |
- } | |
- | |
- return isOwner; | |
-} | |
-#else // !linux || VMKERNEL | |
-int | |
-vmci_is_context_owner(VMCIId contextID, // IN | |
- void *hostUser) // IN | |
-{ | |
- if (VMCI_HostPersonalityActive()) { | |
- VMCIContext *context; | |
- VMCIHostUser *user = (VMCIHostUser *)hostUser; | |
- int retval; | |
- | |
- if (vmkernel) { | |
- return VMCI_ERROR_UNAVAILABLE; | |
- } | |
- | |
- if (!hostUser) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- context = VMCIContext_Get(contextID); | |
- if (!context) { | |
- return VMCI_ERROR_NOT_FOUND; | |
- } | |
- | |
- if (context->validUser) { | |
- retval = VMCIHost_CompareUser(user, &context->user); | |
- } else { | |
- retval = VMCI_ERROR_UNAVAILABLE; | |
- } | |
- VMCIContext_Release(context); | |
- | |
- return retval; | |
- } | |
- return VMCI_ERROR_UNAVAILABLE; | |
-} | |
-#endif // !linux || VMKERNEL | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIContext_SupportsHostQP -- | |
- * | |
- * Can host QPs be connected to this user process. The answer is | |
- * FALSE unless a sufficient version number has previously been set | |
- * by this caller. | |
- * | |
- * Results: | |
- * TRUE if context supports host queue pairs, FALSE otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-Bool | |
-VMCIContext_SupportsHostQP(VMCIContext *context) // IN: Context structure | |
-{ | |
-#ifdef VMKERNEL | |
- return TRUE; | |
-#else | |
- if (!context || context->userVersion < VMCI_VERSION_HOSTQP) { | |
- return FALSE; | |
- } | |
- return TRUE; | |
-#endif | |
-} | |
- | |
- | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIContext_QueuePairCreate -- | |
- * | |
- * Registers that a new queue pair handle has been allocated by | |
- * the context. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS on success, appropriate error code otherewise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCIContext_QueuePairCreate(VMCIContext *context, // IN: Context structure | |
- VMCIHandle handle) // IN | |
-{ | |
- int result; | |
- | |
- if (context == NULL || VMCI_HANDLE_INVALID(handle)) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- if (!VMCIHandleArray_HasEntry(context->queuePairArray, handle)) { | |
- VMCIHandleArray_AppendEntry(&context->queuePairArray, handle); | |
- result = VMCI_SUCCESS; | |
- } else { | |
- result = VMCI_ERROR_DUPLICATE_ENTRY; | |
- } | |
- | |
- return result; | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIContext_QueuePairDestroy -- | |
- * | |
- * Unregisters a queue pair handle that was previously registered | |
- * with VMCIContext_QueuePairCreate. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS on success, appropriate error code otherewise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCIContext_QueuePairDestroy(VMCIContext *context, // IN: Context structure | |
- VMCIHandle handle) // IN | |
-{ | |
- VMCIHandle removedHandle; | |
- | |
- if (context == NULL || VMCI_HANDLE_INVALID(handle)) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- removedHandle = VMCIHandleArray_RemoveEntry(context->queuePairArray, handle); | |
- | |
- if (VMCI_HANDLE_INVALID(removedHandle)) { | |
- return VMCI_ERROR_NOT_FOUND; | |
- } else { | |
- return VMCI_SUCCESS; | |
- } | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIContext_QueuePairExists -- | |
- * | |
- * Determines whether a given queue pair handle is registered | |
- * with the given context. | |
- * | |
- * Results: | |
- * TRUE, if queue pair is registered with context. FALSE, otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-Bool | |
-VMCIContext_QueuePairExists(VMCIContext *context, // IN: Context structure | |
- VMCIHandle handle) // IN | |
-{ | |
- Bool result; | |
- | |
- if (context == NULL || VMCI_HANDLE_INVALID(handle)) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- result = VMCIHandleArray_HasEntry(context->queuePairArray, handle); | |
- | |
- return result; | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIContext_RegisterGuestMem -- | |
- * | |
- * Tells the context that guest memory is available for | |
- * access. This should only be used when unquiescing the VMCI | |
- * device of a guest. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * Notifies host side endpoints of queue pairs that the queue pairs | |
- * can be accessed. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCIContext_RegisterGuestMem(VMCIContext *context, // IN: Context structure | |
- VMCIGuestMemID gid) // IN: Reference to guest | |
-{ | |
-#ifdef VMKERNEL | |
- uint32 numQueuePairs; | |
- uint32 cur; | |
- | |
- VMCIMutex_Acquire(&context->guestMemMutex); | |
- | |
- if (context->curGuestMemID != INVALID_VMCI_GUEST_MEM_ID) { | |
- if (context->curGuestMemID != gid) { | |
- /* | |
- * The guest memory has been registered with a different guest | |
- * memory ID. This is possible if we attempt to continue the | |
- * execution of the source VMX following a failed FSR. | |
- */ | |
- | |
- VMCIContextReleaseGuestMemLocked(context, context->curGuestMemID); | |
- } else { | |
- /* | |
- * When unquiescing the device during a restore sync not part | |
- * of an FSR, we will already have registered the guest | |
- * memory when creating the device, so we don't need to do it | |
- * again. Also, there are no active queue pairs at this | |
- * point, so nothing to do. | |
- */ | |
- | |
- ASSERT(VMCIHandleArray_GetSize(context->queuePairArray) == 0); | |
- goto out; | |
- } | |
- } | |
- context->curGuestMemID = gid; | |
- | |
- /* | |
- * It is safe to access the queue pair array here, since no changes | |
- * to the queuePairArray can take place until after the unquiescing | |
- * is complete. | |
- */ | |
- | |
- numQueuePairs = VMCIHandleArray_GetSize(context->queuePairArray); | |
- for (cur = 0; cur < numQueuePairs; cur++) { | |
- VMCIHandle handle; | |
- handle = VMCIHandleArray_GetEntry(context->queuePairArray, cur); | |
- if (!VMCI_HANDLE_EQUAL(handle, VMCI_INVALID_HANDLE)) { | |
- int res; | |
- | |
- res = VMCIQPBroker_Map(handle, context, NULL); | |
- if (res < VMCI_SUCCESS) { | |
- VMCI_WARNING(("Failed to map guest memory for queue pair " | |
- "(handle=0x%x:0x%x, res=%d).\n", | |
- handle.context, handle.resource, res)); | |
- } | |
- } | |
- } | |
- | |
-out: | |
- VMCIMutex_Release(&context->guestMemMutex); | |
-#endif | |
-} | |
- | |
- | |
-#ifdef VMKERNEL | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIContextReleaseGuestMemLocked -- | |
- * | |
- * A version of VMCIContext_ReleaseGuestMem that assumes that the | |
- * guest mem lock is already held. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-static void | |
-VMCIContextReleaseGuestMemLocked(VMCIContext *context, // IN: Context structure | |
- VMCIGuestMemID gid) // IN: Reference to guest | |
-{ | |
- uint32 numQueuePairs; | |
- uint32 cur; | |
- | |
- /* | |
- * It is safe to access the queue pair array here, since no changes | |
- * to the queuePairArray can take place when the the quiescing | |
- * has been initiated. | |
- */ | |
- | |
- numQueuePairs = VMCIHandleArray_GetSize(context->queuePairArray); | |
- for (cur = 0; cur < numQueuePairs; cur++) { | |
- VMCIHandle handle; | |
- handle = VMCIHandleArray_GetEntry(context->queuePairArray, cur); | |
- if (!VMCI_HANDLE_EQUAL(handle, VMCI_INVALID_HANDLE)) { | |
- int res; | |
- | |
- res = VMCIQPBroker_Unmap(handle, context, gid); | |
- if (res < VMCI_SUCCESS) { | |
- VMCI_WARNING(("Failed to unmap guest memory for queue pair " | |
- "(handle=0x%x:0x%x, res=%d).\n", | |
- handle.context, handle.resource, res)); | |
- } | |
- } | |
- } | |
-} | |
-#endif | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIContext_ReleaseGuestMem -- | |
- * | |
- * Releases all the contexts references to guest memory, if the | |
- * caller identified by the gid was the last one to register the | |
- * guest memory. This should only be used when quiescing or | |
- * cleaning up the VMCI device of a guest. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCIContext_ReleaseGuestMem(VMCIContext *context, // IN: Context structure | |
- VMCIGuestMemID gid) // IN: Reference to guest | |
-{ | |
-#ifdef VMKERNEL | |
- VMCIMutex_Acquire(&context->guestMemMutex); | |
- | |
- if (context->curGuestMemID == gid) { | |
- /* | |
- * In the case of an FSR, we may have multiple VMX'en | |
- * registering and releasing guest memory concurrently. The | |
- * common case is that the source will clean up its device state | |
- * after a successful FSR, where the destination may already | |
- * have registered guest memory. So we only release guest | |
- * memory, if this is the same gid, that registered the memory. | |
- */ | |
- | |
- VMCIContextReleaseGuestMemLocked(context, gid); | |
- context->curGuestMemID = INVALID_VMCI_GUEST_MEM_ID; | |
- } | |
- | |
- VMCIMutex_Release(&context->guestMemMutex); | |
-#endif | |
-} | |
- | |
-#if defined(VMKERNEL) | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIContext_FilterSet -- | |
- * | |
- * Sets an ingoing (host to guest) filter for the VMCI firewall of the | |
- * given context. If a filter list already exists for the given filter | |
- * entry, the old entry will be deleted. It is assumed that the list | |
- * can be used as is, and that the memory backing it will be freed by the | |
- * VMCI Context module once the filter is deleted. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS on success, | |
- * VMCI_ERROR_NOT_FOUND if there is no active context linked to the cid, | |
- * VMCI_ERROR_INVALID_ARGS if a non-VM cid is specified. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCIContext_FilterSet(VMCIId cid, // IN | |
- VMCIFilterState *filters) // IN | |
-{ | |
- VMCIContext *context; | |
- VMCILockFlags flags; | |
- VMCIFilterState *oldState; | |
- | |
- if (!VMCI_CONTEXT_IS_VM(cid)) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- context = VMCIContext_Get(cid); | |
- if (!context) { | |
- return VMCI_ERROR_NOT_FOUND; | |
- } | |
- | |
- VMCI_GrabLock(&context->lock, &flags); | |
- | |
- oldState = context->inFilters; | |
- context->inFilters = filters; | |
- | |
- VMCI_ReleaseLock(&context->lock, flags); | |
- if (oldState) { | |
- VMCIVMKDevFreeFilterState(oldState); | |
- } | |
- VMCIContext_Release(context); | |
- | |
- return VMCI_SUCCESS; | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIContextInFilterCleanup -- | |
- * | |
- * When a context is destroyed, all filters will be deleted. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-static void | |
-VMCIContextInFilterCleanup(VMCIContext *context) | |
-{ | |
- if (context->inFilters != NULL) { | |
- VMCIVMKDevFreeFilterState(context->inFilters); | |
- context->inFilters = NULL; | |
- } | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCI_Uuid2ContextId -- | |
- * | |
- * Given a running VM's UUID, retrieve the VM's VMCI context ID. | |
- * The given UUID is local to the host; it is _not_ the UUID | |
- * handed out by VC. It comes from the "bios.uuid" field in the | |
- * VMX file. We walk the context list and try to match the given | |
- * UUID against each context. If we get a match, we return the | |
- * contexts's VMCI ID. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS if found and *contextID contains the CID. | |
- * VMCI_ERROR_INVALID_ARGS for bad parameters. | |
- * VMCI_ERROR_NOT_FOUND if no match. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCI_Uuid2ContextId(const char *uuidString, // IN | |
- VMCIId *contextID) // OUT | |
-{ | |
- int err; | |
- VMCIListItem *next; | |
- VMCILockFlags flags; | |
- | |
- if (!uuidString || *uuidString == '\0' || !contextID) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- err = VMCI_ERROR_NOT_FOUND; | |
- | |
- VMCI_GrabLock(&contextList.lock, &flags); | |
- VMCIList_Scan(next, &contextList.head) { | |
- VMCIContext *context = VMCIList_Entry(next, VMCIContext, listItem); | |
- if (VMCIHost_ContextHasUuid(&context->hostContext, uuidString) == | |
- VMCI_SUCCESS) { | |
- *contextID = context->cid; | |
- err = VMCI_SUCCESS; | |
- break; | |
- } | |
- } | |
- VMCI_ReleaseLock(&contextList.lock, flags); | |
- | |
- return err; | |
-} | |
-#endif // VMKERNEL | |
diff --git a/modules/linux/vmci/common/vmciContext.h b/modules/linux/vmci/common/vmciContext.h | |
deleted file mode 100644 | |
index 93e37a8..0000000 | |
--- a/modules/linux/vmci/common/vmciContext.h | |
+++ /dev/null | |
@@ -1,120 +0,0 @@ | |
-/********************************************************* | |
- * Copyright (C) 2006-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 as published by the | |
- * Free Software Foundation version 2 and no 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 | |
- * | |
- *********************************************************/ | |
- | |
-/* | |
- * vmciContext.h -- | |
- * | |
- * VMCI state to enable sending calls between VMs. | |
- */ | |
- | |
-#ifndef _VMCI_CONTEXT_H_ | |
-#define _VMCI_CONTEXT_H_ | |
- | |
-#define INCLUDE_ALLOW_MODULE | |
-#define INCLUDE_ALLOW_VMMON | |
-#define INCLUDE_ALLOW_VMCORE | |
-#define INCLUDE_ALLOW_VMKERNEL | |
-#include "includeCheck.h" | |
- | |
-#include "vmci_defs.h" | |
-#include "vmci_call_defs.h" | |
-#include "vmci_handle_array.h" | |
-#include "vmci_infrastructure.h" | |
-#include "vmci_kernel_if.h" | |
-#include "vmciCommonInt.h" | |
- | |
-#define MAX_QUEUED_GUESTCALLS_PER_VM 100 | |
- | |
-typedef struct VMCIContext VMCIContext; | |
- | |
-int VMCIContext_Init(void); | |
-void VMCIContext_Exit(void); | |
-int VMCIContext_InitContext(VMCIId cid, VMCIPrivilegeFlags flags, | |
- uintptr_t eventHnd, int version, | |
- VMCIHostUser *user, VMCIContext **context); | |
-#ifdef VMKERNEL | |
-void VMCIContext_SetFSRState(VMCIContext *context, | |
- Bool isQuiesced, | |
- VMCIId migrateCid, | |
- uintptr_t eventHnd, | |
- Bool isLocked); | |
-VMCIContext *VMCIContext_FindAndUpdateSrcFSR(VMCIId migrateCid, | |
- uintptr_t eventHnd, | |
- uintptr_t *srcEventHnd); | |
-Bool VMCIContext_IsActiveHnd(VMCIContext *context, uintptr_t eventHnd); | |
-uintptr_t VMCIContext_GetActiveHnd(VMCIContext *context); | |
-void VMCIContext_SetInactiveHnd(VMCIContext *context, uintptr_t eventHnd); | |
-Bool VMCIContext_RemoveHnd(VMCIContext *context, | |
- uintptr_t eventHnd, | |
- uint32 *numOld, | |
- uint32 *numNew); | |
-void VMCIContext_ClearDatagrams(VMCIContext *context); | |
-void VMCIContext_SetId(VMCIContext *context, VMCIId cid); | |
-#endif | |
-Bool VMCIContext_SupportsHostQP(VMCIContext *context); | |
-void VMCIContext_ReleaseContext(VMCIContext *context); | |
-int VMCIContext_EnqueueDatagram(VMCIId cid, VMCIDatagram *dg); | |
-int VMCIContext_DequeueDatagram(VMCIContext *context, size_t *maxSize, | |
- VMCIDatagram **dg); | |
-int VMCIContext_PendingDatagrams(VMCIId cid, uint32 *pending); | |
-VMCIContext *VMCIContext_Get(VMCIId cid); | |
-void VMCIContext_Release(VMCIContext *context); | |
-Bool VMCIContext_Exists(VMCIId cid); | |
- | |
-VMCIId VMCIContext_GetId(VMCIContext *context); | |
-int VMCIContext_AddNotification(VMCIId contextID, VMCIId remoteCID); | |
-int VMCIContext_RemoveNotification(VMCIId contextID, VMCIId remoteCID); | |
-int VMCIContext_GetCheckpointState(VMCIId contextID, uint32 cptType, | |
- uint32 *numCIDs, char **cptBufPtr); | |
-int VMCIContext_SetCheckpointState(VMCIId contextID, uint32 cptType, | |
- uint32 numCIDs, char *cptBuf); | |
-void VMCIContext_RegisterGuestMem(VMCIContext *context, VMCIGuestMemID gid); | |
-void VMCIContext_ReleaseGuestMem(VMCIContext *context, VMCIGuestMemID gid); | |
- | |
-int VMCIContext_QueuePairCreate(VMCIContext *context, VMCIHandle handle); | |
-int VMCIContext_QueuePairDestroy(VMCIContext *context, VMCIHandle handle); | |
-Bool VMCIContext_QueuePairExists(VMCIContext *context, VMCIHandle handle); | |
- | |
-#ifndef VMX86_SERVER | |
-void VMCIContext_CheckAndSignalNotify(VMCIContext *context); | |
-# ifdef __linux__ | |
-/* TODO Windows and Mac OS. */ | |
-void VMCIUnsetNotify(VMCIContext *context); | |
-# endif | |
-#endif | |
- | |
-int VMCIContext_DoorbellCreate(VMCIId contextID, VMCIHandle handle); | |
-int VMCIContext_DoorbellDestroy(VMCIId contextID, VMCIHandle handle); | |
-int VMCIContext_DoorbellDestroyAll(VMCIId contextID); | |
-int VMCIContext_NotifyDoorbell(VMCIId cid, VMCIHandle handle, | |
- VMCIPrivilegeFlags srcPrivFlags); | |
- | |
-int VMCIContext_ReceiveNotificationsGet(VMCIId contextID, | |
- VMCIHandleArray **dbHandleArray, | |
- VMCIHandleArray **qpHandleArray); | |
-void VMCIContext_ReceiveNotificationsRelease(VMCIId contextID, | |
- VMCIHandleArray *dbHandleArray, | |
- VMCIHandleArray *qpHandleArray, | |
- Bool success); | |
-#if defined(VMKERNEL) | |
-void VMCIContext_SignalPendingDoorbells(VMCIId contextID); | |
-void VMCIContext_SignalPendingDatagrams(VMCIId contextID); | |
-int VMCIContext_FilterSet(VMCIId cid, VMCIFilterState *filterState); | |
-int VMCI_Uuid2ContextId(const char *uuidString, VMCIId *contextID); | |
-#endif // VMKERNEL | |
-#endif // _VMCI_CONTEXT_H_ | |
diff --git a/modules/linux/vmci/common/vmciDatagram.c b/modules/linux/vmci/common/vmciDatagram.c | |
deleted file mode 100644 | |
index 0dbac65..0000000 | |
--- a/modules/linux/vmci/common/vmciDatagram.c | |
+++ /dev/null | |
@@ -1,966 +0,0 @@ | |
-/********************************************************* | |
- * Copyright (C) 2006-2011 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 as published by the | |
- * Free Software Foundation version 2 and no 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 | |
- * | |
- *********************************************************/ | |
- | |
-/* | |
- * vmciDatagram.c -- | |
- * | |
- * This file implements the VMCI Simple Datagram API on the host. | |
- */ | |
- | |
-#include "vmci_kernel_if.h" | |
-#include "vm_assert.h" | |
-#include "vmci_defs.h" | |
-#include "vmci_infrastructure.h" | |
-#include "vmciCommonInt.h" | |
-#include "vmciContext.h" | |
-#include "vmciDatagram.h" | |
-#include "vmciDriver.h" | |
-#include "vmciEvent.h" | |
-#include "vmciHashtable.h" | |
-#include "vmciKernelAPI.h" | |
-#include "vmciResource.h" | |
-#include "vmciRoute.h" | |
-#if defined(VMKERNEL) | |
-# include "vmciVmkInt.h" | |
-# include "vm_libc.h" | |
-# include "helper_ext.h" | |
-#endif | |
- | |
-#define LGPFX "VMCIDatagram: " | |
- | |
- | |
-/* | |
- * DatagramEntry describes the datagram entity. It is used for datagram | |
- * entities created only on the host. | |
- */ | |
-typedef struct DatagramEntry { | |
- VMCIResource resource; | |
- uint32 flags; | |
- Bool runDelayed; | |
- VMCIDatagramRecvCB recvCB; | |
- void *clientData; | |
- VMCIEvent destroyEvent; | |
- VMCIPrivilegeFlags privFlags; | |
-} DatagramEntry; | |
- | |
-typedef struct VMCIDelayedDatagramInfo { | |
- Bool inDGHostQueue; | |
- DatagramEntry *entry; | |
- VMCIDatagram msg; | |
-} VMCIDelayedDatagramInfo; | |
- | |
- | |
-static Atomic_uint32 delayedDGHostQueueSize; | |
- | |
-static int VMCIDatagramGetPrivFlagsInt(VMCIId contextID, VMCIHandle handle, | |
- VMCIPrivilegeFlags *privFlags); | |
-static void DatagramFreeCB(void *resource); | |
-static int DatagramReleaseCB(void *clientData); | |
- | |
- | |
-/*------------------------------ Helper functions ----------------------------*/ | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * DatagramFreeCB -- | |
- * Callback to free datagram structure when resource is no longer used, | |
- * ie. the reference count reached 0. | |
- * | |
- * Result: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-static void | |
-DatagramFreeCB(void *clientData) | |
-{ | |
- DatagramEntry *entry = (DatagramEntry *)clientData; | |
- ASSERT(entry); | |
- VMCI_SignalEvent(&entry->destroyEvent); | |
- | |
- /* | |
- * The entry is freed in VMCIDatagram_DestroyHnd, who is waiting for the | |
- * above signal. | |
- */ | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * DatagramReleaseCB -- | |
- * | |
- * Callback to release the resource reference. It is called by the | |
- * VMCI_WaitOnEvent function before it blocks. | |
- * | |
- * Result: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-static int | |
-DatagramReleaseCB(void *clientData) | |
-{ | |
- DatagramEntry *entry = (DatagramEntry *)clientData; | |
- ASSERT(entry); | |
- VMCIResource_Release(&entry->resource); | |
- return 0; | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * DatagramCreateHnd -- | |
- * | |
- * Internal function to create a datagram entry given a handle. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS if created, negative errno value otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-static int | |
-DatagramCreateHnd(VMCIId resourceID, // IN: | |
- uint32 flags, // IN: | |
- VMCIPrivilegeFlags privFlags, // IN: | |
- VMCIDatagramRecvCB recvCB, // IN: | |
- void *clientData, // IN: | |
- VMCIHandle *outHandle) // OUT: | |
- | |
-{ | |
- int result; | |
- VMCIId contextID; | |
- VMCIHandle handle; | |
- DatagramEntry *entry; | |
- | |
- ASSERT(recvCB != NULL); | |
- ASSERT(outHandle != NULL); | |
- ASSERT(!(privFlags & ~VMCI_PRIVILEGE_ALL_FLAGS)); | |
- | |
- if ((flags & VMCI_FLAG_WELLKNOWN_DG_HND) != 0) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } else { | |
- if ((flags & VMCI_FLAG_ANYCID_DG_HND) != 0) { | |
- contextID = VMCI_INVALID_ID; | |
- } else { | |
- contextID = vmci_get_context_id(); | |
- if (contextID == VMCI_INVALID_ID) { | |
- return VMCI_ERROR_NO_RESOURCES; | |
- } | |
- } | |
- | |
- if (resourceID == VMCI_INVALID_ID) { | |
- resourceID = VMCIResource_GetID(contextID); | |
- if (resourceID == VMCI_INVALID_ID) { | |
- return VMCI_ERROR_NO_HANDLE; | |
- } | |
- } | |
- | |
- handle = VMCI_MAKE_HANDLE(contextID, resourceID); | |
- } | |
- | |
- entry = VMCI_AllocKernelMem(sizeof *entry, VMCI_MEMORY_NONPAGED); | |
- if (entry == NULL) { | |
- VMCI_WARNING((LGPFX"Failed allocating memory for datagram entry.\n")); | |
- return VMCI_ERROR_NO_MEM; | |
- } | |
- | |
- if (!VMCI_CanScheduleDelayedWork()) { | |
- if (flags & VMCI_FLAG_DG_DELAYED_CB) { | |
- VMCI_FreeKernelMem(entry, sizeof *entry); | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- entry->runDelayed = FALSE; | |
- } else { | |
- entry->runDelayed = (flags & VMCI_FLAG_DG_DELAYED_CB) ? TRUE : FALSE; | |
- } | |
- | |
- entry->flags = flags; | |
- entry->recvCB = recvCB; | |
- entry->clientData = clientData; | |
- VMCI_CreateEvent(&entry->destroyEvent); | |
- entry->privFlags = privFlags; | |
- | |
- /* Make datagram resource live. */ | |
- result = VMCIResource_Add(&entry->resource, VMCI_RESOURCE_TYPE_DATAGRAM, | |
- handle, DatagramFreeCB, entry); | |
- if (result != VMCI_SUCCESS) { | |
- VMCI_WARNING((LGPFX"Failed to add new resource (handle=0x%x:0x%x).\n", | |
- handle.context, handle.resource)); | |
- VMCI_DestroyEvent(&entry->destroyEvent); | |
- VMCI_FreeKernelMem(entry, sizeof *entry); | |
- return result; | |
- } | |
- *outHandle = handle; | |
- | |
- return VMCI_SUCCESS; | |
-} | |
- | |
- | |
-/*------------------------------ Init functions ----------------------------*/ | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCIDatagram_Init -- | |
- * | |
- * Initialize Datagram API, ie. register the API functions with their | |
- * corresponding vectors. | |
- * | |
- * Result: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-int | |
-VMCIDatagram_Init(void) | |
-{ | |
- Atomic_Write(&delayedDGHostQueueSize, 0); | |
- return VMCI_SUCCESS; | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCIDatagram_Exit -- | |
- * | |
- * Cleanup Datagram API. | |
- * | |
- * Result: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-void | |
-VMCIDatagram_Exit(void) | |
-{ | |
-} | |
- | |
- | |
-/*------------------------------ Public API functions ------------------------*/ | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * vmci_datagram_create_handle -- | |
- * | |
- * Creates a host context datagram endpoint and returns a handle to it. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS if created, negative errno value otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-VMCI_EXPORT_SYMBOL(vmci_datagram_create_handle) | |
-int | |
-vmci_datagram_create_handle( | |
- VMCIId resourceID, // IN: Optional, generated if VMCI_INVALID_ID | |
- uint32 flags, // IN: | |
- VMCIDatagramRecvCB recvCB, // IN: | |
- void *clientData, // IN: | |
- VMCIHandle *outHandle) // OUT: newly created handle | |
-{ | |
- if (outHandle == NULL) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- if (recvCB == NULL) { | |
- VMCI_DEBUG_LOG(4, | |
- (LGPFX"Client callback needed when creating datagram.\n")); | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- return DatagramCreateHnd(resourceID, flags, VMCI_DEFAULT_PROC_PRIVILEGE_FLAGS, | |
- recvCB, clientData, outHandle); | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * vmci_datagram_create_handle_priv -- | |
- * | |
- * Creates a host context datagram endpoint and returns a handle to it. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS if created, negative errno value otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-VMCI_EXPORT_SYMBOL(vmci_datagram_create_handle_priv) | |
-int | |
-vmci_datagram_create_handle_priv( | |
- VMCIId resourceID, // IN: Optional, generated if VMCI_INVALID_ID | |
- uint32 flags, // IN: | |
- VMCIPrivilegeFlags privFlags, // IN: | |
- VMCIDatagramRecvCB recvCB, // IN: | |
- void *clientData, // IN: | |
- VMCIHandle *outHandle) // OUT: newly created handle | |
-{ | |
- if (outHandle == NULL) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- if (recvCB == NULL) { | |
- VMCI_DEBUG_LOG(4, | |
- (LGPFX"Client callback needed when creating datagram.\n")); | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- if (privFlags & ~VMCI_PRIVILEGE_ALL_FLAGS) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- return DatagramCreateHnd(resourceID, flags, privFlags, recvCB, clientData, | |
- outHandle); | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * vmci_datagram_destroy_handle -- | |
- * | |
- * Destroys a handle. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-VMCI_EXPORT_SYMBOL(vmci_datagram_destroy_handle) | |
-int | |
-vmci_datagram_destroy_handle(VMCIHandle handle) // IN | |
-{ | |
- DatagramEntry *entry; | |
- VMCIResource *resource = VMCIResource_Get(handle, | |
- VMCI_RESOURCE_TYPE_DATAGRAM); | |
- if (resource == NULL) { | |
- VMCI_DEBUG_LOG(4, (LGPFX"Failed to destroy datagram (handle=0x%x:0x%x).\n", | |
- handle.context, handle.resource)); | |
- return VMCI_ERROR_NOT_FOUND; | |
- } | |
- entry = RESOURCE_CONTAINER(resource, DatagramEntry, resource); | |
- | |
- VMCIResource_Remove(handle, VMCI_RESOURCE_TYPE_DATAGRAM); | |
- | |
- /* | |
- * We now wait on the destroyEvent and release the reference we got | |
- * above. | |
- */ | |
- VMCI_WaitOnEvent(&entry->destroyEvent, DatagramReleaseCB, entry); | |
- | |
- /* | |
- * We know that we are now the only reference to the above entry so | |
- * can safely free it. | |
- */ | |
- VMCI_DestroyEvent(&entry->destroyEvent); | |
- VMCI_FreeKernelMem(entry, sizeof *entry); | |
- | |
- return VMCI_SUCCESS; | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCIDatagramGetPrivFlagsInt -- | |
- * | |
- * Internal utilility function with the same purpose as | |
- * VMCIDatagram_GetPrivFlags that also takes a contextID. | |
- * | |
- * Result: | |
- * VMCI_SUCCESS on success, VMCI_ERROR_INVALID_ARGS if handle is invalid. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-static int | |
-VMCIDatagramGetPrivFlagsInt(VMCIId contextID, // IN | |
- VMCIHandle handle, // IN | |
- VMCIPrivilegeFlags *privFlags) // OUT | |
-{ | |
- ASSERT(privFlags); | |
- ASSERT(contextID != VMCI_INVALID_ID); | |
- | |
- if (contextID == VMCI_HOST_CONTEXT_ID) { | |
- DatagramEntry *srcEntry; | |
- VMCIResource *resource; | |
- | |
- resource = VMCIResource_Get(handle, VMCI_RESOURCE_TYPE_DATAGRAM); | |
- if (resource == NULL) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- srcEntry = RESOURCE_CONTAINER(resource, DatagramEntry, resource); | |
- *privFlags = srcEntry->privFlags; | |
- VMCIResource_Release(resource); | |
- } else if (contextID == VMCI_HYPERVISOR_CONTEXT_ID) { | |
- *privFlags = VMCI_MAX_PRIVILEGE_FLAGS; | |
- } else { | |
- *privFlags = vmci_context_get_priv_flags(contextID); | |
- } | |
- | |
- return VMCI_SUCCESS; | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCIDatagram_GetPrivFlags -- | |
- * | |
- * Utilility function that retrieves the privilege flags | |
- * associated with a given datagram handle. For hypervisor and | |
- * guest endpoints, the privileges are determined by the context | |
- * ID, but for host endpoints privileges are associated with the | |
- * complete handle. | |
- * | |
- * Result: | |
- * VMCI_SUCCESS on success, VMCI_ERROR_INVALID_ARGS if handle is invalid. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-int | |
-VMCIDatagram_GetPrivFlags(VMCIHandle handle, // IN | |
- VMCIPrivilegeFlags *privFlags) // OUT | |
-{ | |
- if (privFlags == NULL || handle.context == VMCI_INVALID_ID) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- return VMCIDatagramGetPrivFlagsInt(handle.context, handle, privFlags); | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIDatagramDelayedDispatchCB -- | |
- * | |
- * Calls the specified callback in a delayed context. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static void | |
-VMCIDatagramDelayedDispatchCB(void *data) // IN | |
-{ | |
- Bool inDGHostQueue; | |
- VMCIDelayedDatagramInfo *dgInfo = (VMCIDelayedDatagramInfo *)data; | |
- | |
- ASSERT(data); | |
- | |
- dgInfo->entry->recvCB(dgInfo->entry->clientData, &dgInfo->msg); | |
- | |
- VMCIResource_Release(&dgInfo->entry->resource); | |
- | |
- inDGHostQueue = dgInfo->inDGHostQueue; | |
- VMCI_FreeKernelMem(dgInfo, sizeof *dgInfo + (size_t)dgInfo->msg.payloadSize); | |
- | |
- if (inDGHostQueue) { | |
- Atomic_Dec(&delayedDGHostQueueSize); | |
- } | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCIDatagramDispatchAsHost -- | |
- * | |
- * Dispatch datagram as a host, to the host or other vm context. This | |
- * function cannot dispatch to hypervisor context handlers. This should | |
- * have been handled before we get here by VMCIDatagramDispatch. | |
- * | |
- * Result: | |
- * Number of bytes sent on success, appropriate error code otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-static int | |
-VMCIDatagramDispatchAsHost(VMCIId contextID, // IN: | |
- VMCIDatagram *dg) // IN: | |
-{ | |
- int retval; | |
- size_t dgSize; | |
- VMCIPrivilegeFlags srcPrivFlags; | |
- | |
- ASSERT(dg); | |
- ASSERT(VMCI_HostPersonalityActive()); | |
- | |
- dgSize = VMCI_DG_SIZE(dg); | |
- | |
- if (contextID == VMCI_HOST_CONTEXT_ID && | |
- dg->dst.context == VMCI_HYPERVISOR_CONTEXT_ID) { | |
- VMCI_DEBUG_LOG(4, (LGPFX"Host cannot talk to hypervisor\n")); | |
- return VMCI_ERROR_DST_UNREACHABLE; | |
- } | |
- | |
- ASSERT(dg->dst.context != VMCI_HYPERVISOR_CONTEXT_ID); | |
- | |
- /* Chatty. */ | |
- // VMCI_DEBUG_LOG(10, (LGPFX"Sending from (handle=0x%x:0x%x) to " | |
- // "(handle=0x%x:0x%x) (size=%u bytes).\n", | |
- // dg->src.context, dg->src.resource, | |
- // dg->dst.context, dg->dst.resource, (uint32)dgSize)); | |
- | |
- /* | |
- * Check that source handle matches sending context. | |
- */ | |
- if (dg->src.context != contextID) { | |
- VMCI_DEBUG_LOG(4, (LGPFX"Sender context (ID=0x%x) is not owner of src " | |
- "datagram entry (handle=0x%x:0x%x).\n", | |
- contextID, dg->src.context, dg->src.resource)); | |
- return VMCI_ERROR_NO_ACCESS; | |
- } | |
- | |
- /* | |
- * Get hold of privileges of sending endpoint. | |
- */ | |
- | |
- retval = VMCIDatagramGetPrivFlagsInt(contextID, dg->src, &srcPrivFlags); | |
- if (retval != VMCI_SUCCESS) { | |
- VMCI_WARNING((LGPFX"Couldn't get privileges (handle=0x%x:0x%x).\n", | |
- dg->src.context, dg->src.resource)); | |
- return retval; | |
- } | |
- | |
- /* Determine if we should route to host or guest destination. */ | |
- if (dg->dst.context == VMCI_HOST_CONTEXT_ID) { | |
- /* Route to host datagram entry. */ | |
- DatagramEntry *dstEntry; | |
- VMCIResource *resource; | |
- | |
- if (dg->src.context == VMCI_HYPERVISOR_CONTEXT_ID && | |
- dg->dst.resource == VMCI_EVENT_HANDLER) { | |
- return VMCIEvent_Dispatch(dg); | |
- } | |
- | |
- resource = VMCIResource_Get(dg->dst, VMCI_RESOURCE_TYPE_DATAGRAM); | |
- if (resource == NULL) { | |
- VMCI_DEBUG_LOG(4, (LGPFX"Sending to invalid destination " | |
- "(handle=0x%x:0x%x).\n", | |
- dg->dst.context, dg->dst.resource)); | |
- return VMCI_ERROR_INVALID_RESOURCE; | |
- } | |
- dstEntry = RESOURCE_CONTAINER(resource, DatagramEntry, resource); | |
- if (VMCIDenyInteraction(srcPrivFlags, dstEntry->privFlags)) { | |
- VMCIResource_Release(resource); | |
- return VMCI_ERROR_NO_ACCESS; | |
- } | |
- ASSERT(dstEntry->recvCB); | |
- | |
- /* | |
- * If a VMCI datagram destined for the host is also sent by the | |
- * host, we always run it delayed. This ensures that no locks | |
- * are held when the datagram callback runs. | |
- */ | |
- | |
- if (dstEntry->runDelayed || | |
- (dg->src.context == VMCI_HOST_CONTEXT_ID && | |
- VMCI_CanScheduleDelayedWork())) { | |
- VMCIDelayedDatagramInfo *dgInfo; | |
- | |
- if (Atomic_FetchAndAdd(&delayedDGHostQueueSize, 1) == | |
- VMCI_MAX_DELAYED_DG_HOST_QUEUE_SIZE) { | |
- Atomic_Dec(&delayedDGHostQueueSize); | |
- VMCIResource_Release(resource); | |
- return VMCI_ERROR_NO_MEM; | |
- } | |
- | |
- dgInfo = VMCI_AllocKernelMem(sizeof *dgInfo + (size_t)dg->payloadSize, | |
- (VMCI_MEMORY_ATOMIC | | |
- VMCI_MEMORY_NONPAGED)); | |
- if (NULL == dgInfo) { | |
- Atomic_Dec(&delayedDGHostQueueSize); | |
- VMCIResource_Release(resource); | |
- return VMCI_ERROR_NO_MEM; | |
- } | |
- | |
- dgInfo->inDGHostQueue = TRUE; | |
- dgInfo->entry = dstEntry; | |
- memcpy(&dgInfo->msg, dg, dgSize); | |
- | |
- retval = VMCI_ScheduleDelayedWork(VMCIDatagramDelayedDispatchCB, dgInfo); | |
- if (retval < VMCI_SUCCESS) { | |
- VMCI_WARNING((LGPFX"Failed to schedule delayed work for datagram " | |
- "(result=%d).\n", retval)); | |
- VMCI_FreeKernelMem(dgInfo, sizeof *dgInfo + (size_t)dg->payloadSize); | |
- VMCIResource_Release(resource); | |
- Atomic_Dec(&delayedDGHostQueueSize); | |
- return retval; | |
- } | |
- } else { | |
- retval = dstEntry->recvCB(dstEntry->clientData, dg); | |
- VMCIResource_Release(resource); | |
- if (retval < VMCI_SUCCESS) { | |
- return retval; | |
- } | |
- } | |
- } else { | |
- /* | |
- * Route to destination VM context. | |
- */ | |
- | |
- VMCIDatagram *newDG; | |
- | |
- if (contextID != dg->dst.context) { | |
- if (VMCIDenyInteraction(srcPrivFlags, | |
- vmci_context_get_priv_flags(dg->dst.context))) { | |
- VMCI_DEBUG_LOG(4, (LGPFX"Interaction denied (%X/%X - %X/%X)\n", | |
- contextID, srcPrivFlags, | |
- dg->dst.context, | |
- vmci_context_get_priv_flags(dg->dst.context))); | |
- return VMCI_ERROR_NO_ACCESS; | |
- } else if (VMCI_CONTEXT_IS_VM(contextID)) { | |
- /* | |
- * If the sending context is a VM, it cannot reach another VM. | |
- */ | |
- | |
- if (!vmkernel) { | |
- VMCI_DEBUG_LOG(4, (LGPFX"Datagram communication between VMs not " | |
- "supported (src=0x%x, dst=0x%x).\n", | |
- contextID, dg->dst.context)); | |
- return VMCI_ERROR_DST_UNREACHABLE; | |
- } | |
- } | |
- } | |
- | |
- /* We make a copy to enqueue. */ | |
- newDG = VMCI_AllocKernelMem(dgSize, VMCI_MEMORY_NORMAL); | |
- if (newDG == NULL) { | |
- VMCI_DEBUG_LOG(4, (LGPFX"No memory for datagram\n")); | |
- return VMCI_ERROR_NO_MEM; | |
- } | |
- memcpy(newDG, dg, dgSize); | |
- retval = VMCIContext_EnqueueDatagram(dg->dst.context, newDG); | |
- if (retval < VMCI_SUCCESS) { | |
- VMCI_FreeKernelMem(newDG, dgSize); | |
- VMCI_DEBUG_LOG(4, (LGPFX"Enqueue failed\n")); | |
- return retval; | |
- } | |
- } | |
- | |
- /* The datagram is freed when the context reads it. */ | |
- | |
- /* Chatty. */ | |
- // VMCI_DEBUG_LOG(10, (LGPFX"Sent datagram (size=%u bytes).\n", | |
- // (uint32)dgSize)); | |
- | |
- /* | |
- * We currently truncate the size to signed 32 bits. This doesn't | |
- * matter for this handler as it only support 4Kb messages. | |
- */ | |
- | |
- return (int)dgSize; | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCIDatagramDispatchAsGuest -- | |
- * | |
- * Dispatch datagram as a guest, down through the VMX and potentially to | |
- * the host. | |
- * | |
- * Result: | |
- * Number of bytes sent on success, appropriate error code otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-static int | |
-VMCIDatagramDispatchAsGuest(VMCIDatagram *dg) | |
-{ | |
-#if defined(VMKERNEL) | |
- VMCI_WARNING((LGPFX"Cannot send down to host from VMKERNEL.\n")); | |
- return VMCI_ERROR_DST_UNREACHABLE; | |
-#else // VMKERNEL | |
- int retval; | |
- VMCIResource *resource; | |
- | |
- resource = VMCIResource_Get(dg->src, VMCI_RESOURCE_TYPE_DATAGRAM); | |
- if (NULL == resource) { | |
- return VMCI_ERROR_NO_HANDLE; | |
- } | |
- | |
- retval = VMCI_SendDatagram(dg); | |
- VMCIResource_Release(resource); | |
- return retval; | |
-#endif // VMKERNEL | |
-} | |
- | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCIDatagram_Dispatch -- | |
- * | |
- * Dispatch datagram. This will determine the routing for the datagram | |
- * and dispatch it accordingly. | |
- * | |
- * Result: | |
- * Number of bytes sent on success, appropriate error code otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-int | |
-VMCIDatagram_Dispatch(VMCIId contextID, | |
- VMCIDatagram *dg, | |
- Bool fromGuest) | |
-{ | |
- int retval; | |
- VMCIRoute route; | |
- | |
- ASSERT(dg); | |
- ASSERT_ON_COMPILE(sizeof(VMCIDatagram) == 24); | |
- | |
- if (VMCI_DG_SIZE(dg) > VMCI_MAX_DG_SIZE) { | |
- VMCI_DEBUG_LOG(4, (LGPFX"Payload (size=%"FMT64"u bytes) too big to " | |
- "send.\n", dg->payloadSize)); | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- retval = VMCI_Route(&dg->src, &dg->dst, fromGuest, &route); | |
- if (retval < VMCI_SUCCESS) { | |
- VMCI_DEBUG_LOG(4, (LGPFX"Failed to route datagram (src=0x%x, dst=0x%x, " | |
- "err=%d)\n.", dg->src.context, dg->dst.context, | |
- retval)); | |
- return retval; | |
- } | |
- | |
- if (VMCI_ROUTE_AS_HOST == route) { | |
- if (VMCI_INVALID_ID == contextID) { | |
- contextID = VMCI_HOST_CONTEXT_ID; | |
- } | |
- return VMCIDatagramDispatchAsHost(contextID, dg); | |
- } | |
- | |
- if (VMCI_ROUTE_AS_GUEST == route) { | |
- return VMCIDatagramDispatchAsGuest(dg); | |
- } | |
- | |
- VMCI_WARNING((LGPFX"Unknown route (%d) for datagram.\n", route)); | |
- return VMCI_ERROR_DST_UNREACHABLE; | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCIDatagram_InvokeGuestHandler -- | |
- * | |
- * Invoke the handler for the given datagram. This is intended to be | |
- * called only when acting as a guest and receiving a datagram from the | |
- * virtual device. | |
- * | |
- * Result: | |
- * VMCI_SUCCESS on success, other error values on failure. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-int | |
-VMCIDatagram_InvokeGuestHandler(VMCIDatagram *dg) // IN | |
-{ | |
-#if defined(VMKERNEL) | |
- VMCI_WARNING((LGPFX"Cannot dispatch within guest in VMKERNEL.\n")); | |
- return VMCI_ERROR_DST_UNREACHABLE; | |
-#else // VMKERNEL | |
- int retval; | |
- VMCIResource *resource; | |
- DatagramEntry *dstEntry; | |
- | |
- ASSERT(dg); | |
- | |
- resource = VMCIResource_Get(dg->dst, VMCI_RESOURCE_TYPE_DATAGRAM); | |
- if (NULL == resource) { | |
- VMCI_DEBUG_LOG(4, (LGPFX"destination (handle=0x%x:0x%x) doesn't exist.\n", | |
- dg->dst.context, dg->dst.resource)); | |
- return VMCI_ERROR_NO_HANDLE; | |
- } | |
- | |
- dstEntry = RESOURCE_CONTAINER(resource, DatagramEntry, resource); | |
- if (dstEntry->runDelayed) { | |
- VMCIDelayedDatagramInfo *dgInfo; | |
- | |
- dgInfo = VMCI_AllocKernelMem(sizeof *dgInfo + (size_t)dg->payloadSize, | |
- (VMCI_MEMORY_ATOMIC | VMCI_MEMORY_NONPAGED)); | |
- if (NULL == dgInfo) { | |
- VMCIResource_Release(resource); | |
- retval = VMCI_ERROR_NO_MEM; | |
- goto exit; | |
- } | |
- | |
- dgInfo->inDGHostQueue = FALSE; | |
- dgInfo->entry = dstEntry; | |
- memcpy(&dgInfo->msg, dg, VMCI_DG_SIZE(dg)); | |
- | |
- retval = VMCI_ScheduleDelayedWork(VMCIDatagramDelayedDispatchCB, dgInfo); | |
- if (retval < VMCI_SUCCESS) { | |
- VMCI_WARNING((LGPFX"Failed to schedule delayed work for datagram " | |
- "(result=%d).\n", retval)); | |
- VMCI_FreeKernelMem(dgInfo, sizeof *dgInfo + (size_t)dg->payloadSize); | |
- VMCIResource_Release(resource); | |
- dgInfo = NULL; | |
- goto exit; | |
- } | |
- } else { | |
- dstEntry->recvCB(dstEntry->clientData, dg); | |
- VMCIResource_Release(resource); | |
- retval = VMCI_SUCCESS; | |
- } | |
- | |
-exit: | |
- return retval; | |
-#endif // VMKERNEL | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * vmci_datagram_send -- | |
- * | |
- * Sends the payload to the destination datagram handle. | |
- * | |
- * Results: | |
- * Returns number of bytes sent if success, or error code if failure. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-VMCI_EXPORT_SYMBOL(vmci_datagram_send) | |
-int | |
-vmci_datagram_send(VMCIDatagram *msg) // IN | |
-{ | |
- if (msg == NULL) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- return VMCIDatagram_Dispatch(VMCI_INVALID_ID, msg, FALSE); | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIDatagram_Sync -- | |
- * | |
- * Use this as a synchronization point when setting globals, for example, | |
- * during device shutdown. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCIDatagram_Sync(void) | |
-{ | |
- VMCIResource_Sync(); | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIDatagram_CheckHostCapabilities -- | |
- * | |
- * Verify that the host supports the resources we need. | |
- * None are required for datagrams since they are implicitly supported. | |
- * | |
- * Results: | |
- * TRUE. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-Bool | |
-VMCIDatagram_CheckHostCapabilities(void) | |
-{ | |
- return TRUE; | |
-} | |
diff --git a/modules/linux/vmci/common/vmciDatagram.h b/modules/linux/vmci/common/vmciDatagram.h | |
deleted file mode 100644 | |
index ad7ff45..0000000 | |
--- a/modules/linux/vmci/common/vmciDatagram.h | |
+++ /dev/null | |
@@ -1,57 +0,0 @@ | |
-/********************************************************* | |
- * Copyright (C) 2006 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 as published by the | |
- * Free Software Foundation version 2 and no 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 | |
- * | |
- *********************************************************/ | |
- | |
-/* | |
- * vmciDatagram.h -- | |
- * | |
- * Internal functions in the VMCI Simple Datagram API. | |
- */ | |
- | |
-#ifndef _VMCI_DATAGRAM_H_ | |
-#define _VMCI_DATAGRAM_H_ | |
- | |
-#define INCLUDE_ALLOW_MODULE | |
-#define INCLUDE_ALLOW_VMMON | |
-#define INCLUDE_ALLOW_VMCORE | |
-#define INCLUDE_ALLOW_VMKERNEL | |
-#include "includeCheck.h" | |
- | |
-#include "vmciContext.h" | |
-#include "vmci_call_defs.h" | |
-#ifndef VMX86_SERVER | |
-#include "vmci_iocontrols.h" | |
-#endif // !VMX86_SERVER | |
- | |
-#define VMCI_MAX_DELAYED_DG_HOST_QUEUE_SIZE 256 | |
- | |
-/* Init functions. */ | |
-int VMCIDatagram_Init(void); | |
-void VMCIDatagram_Exit(void); | |
- | |
-/* Datagram API for non-public use. */ | |
-int VMCIDatagram_Dispatch(VMCIId contextID, VMCIDatagram *dg, Bool fromGuest); | |
-int VMCIDatagram_InvokeGuestHandler(VMCIDatagram *dg); | |
-int VMCIDatagram_GetPrivFlags(VMCIHandle handle, VMCIPrivilegeFlags *privFlags); | |
- | |
-/* Misc. */ | |
-void VMCIDatagram_Sync(void); | |
-Bool VMCIDatagram_CheckHostCapabilities(void); | |
- | |
-#endif // _VMCI_DATAGRAM_H_ | |
- | |
- | |
diff --git a/modules/linux/vmci/common/vmciDoorbell.c b/modules/linux/vmci/common/vmciDoorbell.c | |
deleted file mode 100644 | |
index 74053e1..0000000 | |
--- a/modules/linux/vmci/common/vmciDoorbell.c | |
+++ /dev/null | |
@@ -1,1241 +0,0 @@ | |
-/********************************************************* | |
- * Copyright (C) 2010 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 as published by the | |
- * Free Software Foundation version 2 and no 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 | |
- * | |
- *********************************************************/ | |
- | |
-/* | |
- * vmciDoorbell.c -- | |
- * | |
- * This file implements the VMCI doorbell API on the host. | |
- */ | |
- | |
-#include "vmci_kernel_if.h" | |
-#include "vm_assert.h" | |
-#include "vmci_defs.h" | |
-#include "vmci_infrastructure.h" | |
-#include "vmciCommonInt.h" | |
-#include "vmciDatagram.h" | |
-#include "vmciDoorbell.h" | |
-#include "vmciDriver.h" | |
-#include "vmciKernelAPI.h" | |
-#include "vmciResource.h" | |
-#include "vmciRoute.h" | |
-#if defined(VMKERNEL) | |
-# include "vmciVmkInt.h" | |
-# include "vm_libc.h" | |
-# include "helper_ext.h" | |
-#endif | |
- | |
-#define LGPFX "VMCIDoorbell: " | |
- | |
-#if !defined(SOLARIS) && !defined(__APPLE__) | |
- | |
-#define VMCI_DOORBELL_INDEX_TABLE_SIZE 64 | |
-#define VMCI_DOORBELL_HASH(_idx) \ | |
- VMCI_HashId((_idx), VMCI_DOORBELL_INDEX_TABLE_SIZE) | |
- | |
- | |
-/* | |
- * DoorbellEntry describes the a doorbell notification handle allocated by the | |
- * host. | |
- */ | |
- | |
-typedef struct VMCIDoorbellEntry { | |
- VMCIResource resource; | |
- uint32 idx; | |
- VMCIListItem idxListItem; | |
- VMCIPrivilegeFlags privFlags; | |
- Bool isDoorbell; | |
- Bool runDelayed; | |
- VMCICallback notifyCB; | |
- void *clientData; | |
- VMCIEvent destroyEvent; | |
- Atomic_uint32 active; // Only used by guest personality | |
-} VMCIDoorbellEntry; | |
- | |
-typedef struct VMCIDoorbellIndexTable { | |
- VMCILock lock; | |
- VMCIList entries[VMCI_DOORBELL_INDEX_TABLE_SIZE]; | |
-} VMCIDoorbellIndexTable; | |
- | |
- | |
-/* The VMCI index table keeps track of currently registered doorbells. */ | |
-static VMCIDoorbellIndexTable vmciDoorbellIT; | |
- | |
- | |
-/* | |
- * The maxNotifyIdx is one larger than the currently known bitmap index in | |
- * use, and is used to determine how much of the bitmap needs to be scanned. | |
- */ | |
- | |
-static uint32 maxNotifyIdx; | |
- | |
-/* | |
- * The notifyIdxCount is used for determining whether there are free entries | |
- * within the bitmap (if notifyIdxCount + 1 < maxNotifyIdx). | |
- */ | |
- | |
-static uint32 notifyIdxCount; | |
- | |
-/* | |
- * The lastNotifyIdxReserved is used to track the last index handed out - in | |
- * the case where multiple handles share a notification index, we hand out | |
- * indexes round robin based on lastNotifyIdxReserved. | |
- */ | |
- | |
-static uint32 lastNotifyIdxReserved; | |
- | |
-/* This is a one entry cache used to by the index allocation. */ | |
-static uint32 lastNotifyIdxReleased = PAGE_SIZE; | |
- | |
- | |
-static void VMCIDoorbellFreeCB(void *clientData); | |
-static int VMCIDoorbellReleaseCB(void *clientData); | |
-static void VMCIDoorbellDelayedDispatchCB(void *data); | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCIDoorbell_Init -- | |
- * | |
- * General init code. | |
- * | |
- * Result: | |
- * VMCI_SUCCESS on success, lock allocation error otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-int | |
-VMCIDoorbell_Init(void) | |
-{ | |
- uint32 bucket; | |
- | |
- for (bucket = 0; bucket < ARRAYSIZE(vmciDoorbellIT.entries); ++bucket) { | |
- VMCIList_Init(&vmciDoorbellIT.entries[bucket]); | |
- } | |
- | |
- return VMCI_InitLock(&vmciDoorbellIT.lock, "VMCIDoorbellIndexTableLock", | |
- VMCI_LOCK_RANK_DOORBELL); | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCIDoorbell_Exit -- | |
- * | |
- * General init code. | |
- * | |
- * Result: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-void | |
-VMCIDoorbell_Exit(void) | |
-{ | |
- VMCI_CleanupLock(&vmciDoorbellIT.lock); | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCIDoorbellFreeCB -- | |
- * | |
- * Callback to free doorbell entry structure when resource is no longer used, | |
- * ie. the reference count reached 0. The entry is freed in | |
- * VMCIDoorbell_Destroy(), which is waiting on the signal that gets fired | |
- * here. | |
- * | |
- * Result: | |
- * None. | |
- * | |
- * Side effects: | |
- * Signals VMCI event. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-static void | |
-VMCIDoorbellFreeCB(void *clientData) // IN | |
-{ | |
- VMCIDoorbellEntry *entry = (VMCIDoorbellEntry *)clientData; | |
- ASSERT(entry); | |
- VMCI_SignalEvent(&entry->destroyEvent); | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCIDoorbellReleaseCB -- | |
- * | |
- * Callback to release the resource reference. It is called by the | |
- * VMCI_WaitOnEvent function before it blocks. | |
- * | |
- * Result: | |
- * Always 0. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-static int | |
-VMCIDoorbellReleaseCB(void *clientData) // IN: doorbell entry | |
-{ | |
- VMCIDoorbellEntry *entry = (VMCIDoorbellEntry *)clientData; | |
- ASSERT(entry); | |
- VMCIResource_Release(&entry->resource); | |
- return 0; | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCIDoorbellGetPrivFlags -- | |
- * | |
- * Utility function that retrieves the privilege flags associated | |
- * with a given doorbell handle. For guest endpoints, the | |
- * privileges are determined by the context ID, but for host | |
- * endpoints privileges are associated with the complete | |
- * handle. Hypervisor endpoints are not yet supported. | |
- * | |
- * Result: | |
- * VMCI_SUCCESS on success, | |
- * VMCI_ERROR_NOT_FOUND if handle isn't found, | |
- * VMCI_ERROR_INVALID_ARGS if handle is invalid. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-int | |
-VMCIDoorbellGetPrivFlags(VMCIHandle handle, // IN | |
- VMCIPrivilegeFlags *privFlags) // OUT | |
-{ | |
- if (privFlags == NULL || handle.context == VMCI_INVALID_ID) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- if (handle.context == VMCI_HOST_CONTEXT_ID) { | |
- VMCIDoorbellEntry *entry; | |
- VMCIResource *resource; | |
- | |
- resource = VMCIResource_Get(handle, VMCI_RESOURCE_TYPE_DOORBELL); | |
- if (resource == NULL) { | |
- return VMCI_ERROR_NOT_FOUND; | |
- } | |
- entry = RESOURCE_CONTAINER(resource, VMCIDoorbellEntry, resource); | |
- *privFlags = entry->privFlags; | |
- VMCIResource_Release(resource); | |
- } else if (handle.context == VMCI_HYPERVISOR_CONTEXT_ID) { | |
- /* Hypervisor endpoints for notifications are not supported (yet). */ | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } else { | |
- *privFlags = vmci_context_get_priv_flags(handle.context); | |
- } | |
- | |
- return VMCI_SUCCESS; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIDoorbellIndexTableFind -- | |
- * | |
- * Find doorbell entry by bitmap index. | |
- * | |
- * Results: | |
- * Entry if found, NULL if not. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static VMCIDoorbellEntry * | |
-VMCIDoorbellIndexTableFind(uint32 idx) // IN | |
-{ | |
- uint32 bucket = VMCI_DOORBELL_HASH(idx); | |
- VMCIListItem *iter; | |
- | |
- ASSERT(VMCI_GuestPersonalityActive()); | |
- | |
- VMCIList_Scan(iter, &vmciDoorbellIT.entries[bucket]) { | |
- VMCIDoorbellEntry *cur = | |
- VMCIList_Entry(iter, VMCIDoorbellEntry, idxListItem); | |
- | |
- ASSERT(cur); | |
- | |
- if (idx == cur->idx) { | |
- return cur; | |
- } | |
- } | |
- | |
- return NULL; | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCIDoorbellIndexTableAdd -- | |
- * | |
- * Add the given entry to the index table. This will hold() the entry's | |
- * resource so that the entry is not deleted before it is removed from the | |
- * table. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-static void | |
-VMCIDoorbellIndexTableAdd(VMCIDoorbellEntry *entry) // IN/OUT | |
-{ | |
- uint32 bucket; | |
- uint32 newNotifyIdx; | |
- VMCILockFlags flags; | |
- | |
- ASSERT(entry); | |
- ASSERT(VMCI_GuestPersonalityActive()); | |
- | |
- VMCIResource_Hold(&entry->resource); | |
- | |
- VMCI_GrabLock_BH(&vmciDoorbellIT.lock, &flags); | |
- | |
- /* | |
- * Below we try to allocate an index in the notification bitmap with "not | |
- * too much" sharing between resources. If we use less that the full bitmap, | |
- * we either add to the end if there are no unused flags within the | |
- * currently used area, or we search for unused ones. If we use the full | |
- * bitmap, we allocate the index round robin. | |
- */ | |
- | |
- if (maxNotifyIdx < PAGE_SIZE || notifyIdxCount < PAGE_SIZE) { | |
- if (lastNotifyIdxReleased < maxNotifyIdx && | |
- !VMCIDoorbellIndexTableFind(lastNotifyIdxReleased)) { | |
- newNotifyIdx = lastNotifyIdxReleased; | |
- lastNotifyIdxReleased = PAGE_SIZE; | |
- } else { | |
- Bool reused = FALSE; | |
- newNotifyIdx = lastNotifyIdxReserved; | |
- if (notifyIdxCount + 1 < maxNotifyIdx) { | |
- do { | |
- if (!VMCIDoorbellIndexTableFind(newNotifyIdx)) { | |
- reused = TRUE; | |
- break; | |
- } | |
- newNotifyIdx = (newNotifyIdx + 1) % maxNotifyIdx; | |
- } while(newNotifyIdx != lastNotifyIdxReleased); | |
- } | |
- if (!reused) { | |
- newNotifyIdx = maxNotifyIdx; | |
- maxNotifyIdx++; | |
- } | |
- } | |
- } else { | |
- newNotifyIdx = (lastNotifyIdxReserved + 1) % PAGE_SIZE; | |
- } | |
- lastNotifyIdxReserved = newNotifyIdx; | |
- notifyIdxCount++; | |
- | |
- entry->idx = newNotifyIdx; | |
- bucket = VMCI_DOORBELL_HASH(entry->idx); | |
- VMCIList_Insert(&entry->idxListItem, &vmciDoorbellIT.entries[bucket]); | |
- | |
- VMCI_ReleaseLock_BH(&vmciDoorbellIT.lock, flags); | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCIDoorbellIndexTableRemove -- | |
- * | |
- * Remove the given entry from the index table. This will release() the | |
- * entry's resource. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-static void | |
-VMCIDoorbellIndexTableRemove(VMCIDoorbellEntry *entry) // IN/OUT | |
-{ | |
- VMCILockFlags flags; | |
- | |
- ASSERT(entry); | |
- ASSERT(VMCI_GuestPersonalityActive()); | |
- | |
- VMCI_GrabLock_BH(&vmciDoorbellIT.lock, &flags); | |
- | |
- VMCIList_Remove(&entry->idxListItem); | |
- | |
- notifyIdxCount--; | |
- if (entry->idx == maxNotifyIdx - 1) { | |
- /* | |
- * If we delete an entry with the maximum known notification index, we | |
- * take the opportunity to prune the current max. As there might be other | |
- * unused indices immediately below, we lower the maximum until we hit an | |
- * index in use. | |
- */ | |
- | |
- while (maxNotifyIdx > 0 && | |
- !VMCIDoorbellIndexTableFind(maxNotifyIdx - 1)) { | |
- maxNotifyIdx--; | |
- } | |
- } | |
- lastNotifyIdxReleased = entry->idx; | |
- | |
- VMCI_ReleaseLock_BH(&vmciDoorbellIT.lock, flags); | |
- | |
- VMCIResource_Release(&entry->resource); | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCIDoorbellLink -- | |
- * | |
- * Creates a link between the given doorbell handle and the given | |
- * index in the bitmap in the device backend. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS if success, appropriate error code otherwise. | |
- * | |
- * Side effects: | |
- * Notification state is created in hypervisor. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-static int | |
-VMCIDoorbellLink(VMCIHandle handle, // IN | |
- Bool isDoorbell, // IN | |
- uint32 notifyIdx) // IN | |
-{ | |
-#if defined(VMKERNEL) | |
- VMCI_WARNING((LGPFX"Cannot send down to host from VMKERNEL.\n")); | |
- return VMCI_ERROR_DST_UNREACHABLE; | |
-#else // VMKERNEL | |
- VMCIId resourceID; | |
- VMCIDoorbellLinkMsg linkMsg; | |
- | |
- ASSERT(!VMCI_HANDLE_INVALID(handle)); | |
- ASSERT(VMCI_GuestPersonalityActive()); | |
- | |
- if (isDoorbell) { | |
- resourceID = VMCI_DOORBELL_LINK; | |
- } else { | |
- ASSERT(FALSE); | |
- return VMCI_ERROR_UNAVAILABLE; | |
- } | |
- | |
- linkMsg.hdr.dst = VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID, resourceID); | |
- linkMsg.hdr.src = VMCI_ANON_SRC_HANDLE; | |
- linkMsg.hdr.payloadSize = sizeof linkMsg - VMCI_DG_HEADERSIZE; | |
- linkMsg.handle = handle; | |
- linkMsg.notifyIdx = notifyIdx; | |
- | |
- return VMCI_SendDatagram((VMCIDatagram *)&linkMsg); | |
-#endif // VMKERNEL | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCIDoorbellUnlink -- | |
- * | |
- * Unlinks the given doorbell handle from an index in the bitmap in | |
- * the device backend. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS if success, appropriate error code otherwise. | |
- * | |
- * Side effects: | |
- * Notification state is destroyed in hypervisor. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-static int | |
-VMCIDoorbellUnlink(VMCIHandle handle, // IN | |
- Bool isDoorbell) // IN | |
-{ | |
-#if defined(VMKERNEL) | |
- VMCI_WARNING((LGPFX"Cannot send down to host from VMKERNEL.\n")); | |
- return VMCI_ERROR_DST_UNREACHABLE; | |
-#else // VMKERNEL | |
- VMCIId resourceID; | |
- VMCIDoorbellUnlinkMsg unlinkMsg; | |
- | |
- ASSERT(!VMCI_HANDLE_INVALID(handle)); | |
- ASSERT(VMCI_GuestPersonalityActive()); | |
- | |
- if (isDoorbell) { | |
- resourceID = VMCI_DOORBELL_UNLINK; | |
- } else { | |
- ASSERT(FALSE); | |
- return VMCI_ERROR_UNAVAILABLE; | |
- } | |
- | |
- unlinkMsg.hdr.dst = VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID, resourceID); | |
- unlinkMsg.hdr.src = VMCI_ANON_SRC_HANDLE; | |
- unlinkMsg.hdr.payloadSize = sizeof unlinkMsg - VMCI_DG_HEADERSIZE; | |
- unlinkMsg.handle = handle; | |
- | |
- return VMCI_SendDatagram((VMCIDatagram *)&unlinkMsg); | |
-#endif // VMKERNEL | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * vmci_doorbell_create -- | |
- * | |
- * Creates a doorbell with the given callback. If the handle is | |
- * VMCI_INVALID_HANDLE, a free handle will be assigned, if | |
- * possible. The callback can be run immediately (potentially with | |
- * locks held - the default) or delayed (in a kernel thread) by | |
- * specifying the flag VMCI_FLAG_DELAYED_CB. If delayed execution | |
- * is selected, a given callback may not be run if the kernel is | |
- * unable to allocate memory for the delayed execution (highly | |
- * unlikely). | |
- * | |
- * Results: | |
- * VMCI_SUCCESS on success, appropriate error code otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-VMCI_EXPORT_SYMBOL(vmci_doorbell_create) | |
-int | |
-vmci_doorbell_create(VMCIHandle *handle, // IN/OUT | |
- uint32 flags, // IN | |
- VMCIPrivilegeFlags privFlags, // IN | |
- VMCICallback notifyCB, // IN | |
- void *clientData) // IN | |
-{ | |
- VMCIDoorbellEntry *entry; | |
- VMCIHandle newHandle; | |
- int result; | |
- | |
- if (!handle || !notifyCB || flags & ~VMCI_FLAG_DELAYED_CB || | |
- privFlags & ~VMCI_PRIVILEGE_ALL_FLAGS) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- entry = VMCI_AllocKernelMem(sizeof *entry, VMCI_MEMORY_NONPAGED); | |
- if (entry == NULL) { | |
- VMCI_WARNING((LGPFX"Failed allocating memory for datagram entry.\n")); | |
- return VMCI_ERROR_NO_MEM; | |
- } | |
- | |
- if (!VMCI_CanScheduleDelayedWork() && (flags & VMCI_FLAG_DELAYED_CB)) { | |
- result = VMCI_ERROR_INVALID_ARGS; | |
- goto freeMem; | |
- } | |
- | |
- if (VMCI_HANDLE_INVALID(*handle)) { | |
- VMCIId contextID = vmci_get_context_id(); | |
- VMCIId resourceID = VMCIResource_GetID(contextID); | |
- if (resourceID == VMCI_INVALID_ID) { | |
- result = VMCI_ERROR_NO_HANDLE; | |
- goto freeMem; | |
- } | |
- newHandle = VMCI_MAKE_HANDLE(contextID, resourceID); | |
- } else { | |
- Bool validContext; | |
- | |
- /* | |
- * Validate the handle. We must do both of the checks below | |
- * because we can be acting as both a host and a guest at the | |
- * same time. We always allow the host context ID, since the | |
- * host functionality is in practice always there with the | |
- * unified driver. | |
- */ | |
- | |
- validContext = FALSE; | |
- if (VMCI_HOST_CONTEXT_ID == handle->context) { | |
- validContext = TRUE; | |
- } | |
- if (VMCI_GuestPersonalityActive() && | |
- vmci_get_context_id() == handle->context) { | |
- validContext = TRUE; | |
- } | |
- | |
- if (!validContext || VMCI_INVALID_ID == handle->resource) { | |
- VMCI_DEBUG_LOG(4, (LGPFX"Invalid argument (handle=0x%x:0x%x).\n", | |
- handle->context, handle->resource)); | |
- result = VMCI_ERROR_INVALID_ARGS; | |
- goto freeMem; | |
- } | |
- | |
- newHandle = *handle; | |
- } | |
- | |
- entry->idx = 0; | |
- VMCIList_Init(&entry->idxListItem); | |
- entry->privFlags = privFlags; | |
- entry->isDoorbell = TRUE; | |
- entry->runDelayed = (flags & VMCI_FLAG_DELAYED_CB) ? TRUE : FALSE; | |
- entry->notifyCB = notifyCB; | |
- entry->clientData = clientData; | |
- Atomic_Write(&entry->active, 0); | |
- VMCI_CreateEvent(&entry->destroyEvent); | |
- | |
- result = VMCIResource_Add(&entry->resource, VMCI_RESOURCE_TYPE_DOORBELL, | |
- newHandle, VMCIDoorbellFreeCB, entry); | |
- if (result != VMCI_SUCCESS) { | |
- VMCI_WARNING((LGPFX"Failed to add new resource (handle=0x%x:0x%x).\n", | |
- newHandle.context, newHandle.resource)); | |
- if (result == VMCI_ERROR_DUPLICATE_ENTRY) { | |
- result = VMCI_ERROR_ALREADY_EXISTS; | |
- } | |
- goto destroy; | |
- } | |
- | |
- if (VMCI_GuestPersonalityActive()) { | |
- VMCIDoorbellIndexTableAdd(entry); | |
- result = VMCIDoorbellLink(newHandle, entry->isDoorbell, entry->idx); | |
- if (VMCI_SUCCESS != result) { | |
- goto destroyResource; | |
- } | |
- Atomic_Write(&entry->active, 1); | |
- } | |
- | |
- if (VMCI_HANDLE_INVALID(*handle)) { | |
- *handle = newHandle; | |
- } | |
- | |
- return result; | |
- | |
-destroyResource: | |
- VMCIDoorbellIndexTableRemove(entry); | |
- VMCIResource_Remove(newHandle, VMCI_RESOURCE_TYPE_DOORBELL); | |
-destroy: | |
- VMCI_DestroyEvent(&entry->destroyEvent); | |
-freeMem: | |
- VMCI_FreeKernelMem(entry, sizeof *entry); | |
- return result; | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * vmci_doorbell_destroy -- | |
- * | |
- * Destroys a doorbell previously created with | |
- * vmci_doorbell_create. This operation may block waiting for a | |
- * callback to finish. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS on success, appropriate error code otherwise. | |
- * | |
- * Side effects: | |
- * May block. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-VMCI_EXPORT_SYMBOL(vmci_doorbell_destroy) | |
-int | |
-vmci_doorbell_destroy(VMCIHandle handle) // IN | |
-{ | |
- VMCIDoorbellEntry *entry; | |
- VMCIResource *resource; | |
- | |
- if (VMCI_HANDLE_INVALID(handle)) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- resource = VMCIResource_Get(handle, VMCI_RESOURCE_TYPE_DOORBELL); | |
- if (resource == NULL) { | |
- VMCI_DEBUG_LOG(4, (LGPFX"Failed to destroy doorbell (handle=0x%x:0x%x).\n", | |
- handle.context, handle.resource)); | |
- return VMCI_ERROR_NOT_FOUND; | |
- } | |
- entry = RESOURCE_CONTAINER(resource, VMCIDoorbellEntry, resource); | |
- | |
- if (VMCI_GuestPersonalityActive()) { | |
- int result; | |
- | |
- VMCIDoorbellIndexTableRemove(entry); | |
- | |
- result = VMCIDoorbellUnlink(handle, entry->isDoorbell); | |
- if (VMCI_SUCCESS != result) { | |
- | |
- /* | |
- * The only reason this should fail would be an inconsistency between | |
- * guest and hypervisor state, where the guest believes it has an | |
- * active registration whereas the hypervisor doesn't. One case where | |
- * this may happen is if a doorbell is unregistered following a | |
- * hibernation at a time where the doorbell state hasn't been restored | |
- * on the hypervisor side yet. Since the handle has now been removed | |
- * in the guest, we just print a warning and return success. | |
- */ | |
- | |
- VMCI_DEBUG_LOG(4, (LGPFX"Unlink of %s (handle=0x%x:0x%x) unknown by " | |
- "hypervisor (error=%d).\n", | |
- entry->isDoorbell ? "doorbell" : "queuepair", | |
- handle.context, handle.resource, result)); | |
- } | |
- } | |
- | |
- /* | |
- * Now remove the resource from the table. It might still be in use | |
- * after this, in a callback or still on the delayed work queue. | |
- */ | |
- | |
- VMCIResource_Remove(handle, VMCI_RESOURCE_TYPE_DOORBELL); | |
- | |
- /* | |
- * We now wait on the destroyEvent and release the reference we got | |
- * above. | |
- */ | |
- | |
- VMCI_WaitOnEvent(&entry->destroyEvent, VMCIDoorbellReleaseCB, entry); | |
- | |
- /* | |
- * We know that we are now the only reference to the above entry so | |
- * can safely free it. | |
- */ | |
- | |
- VMCI_DestroyEvent(&entry->destroyEvent); | |
- VMCI_FreeKernelMem(entry, sizeof *entry); | |
- | |
- return VMCI_SUCCESS; | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCIDoorbellNotifyAsGuest -- | |
- * | |
- * Notify another guest or the host. We send a datagram down to the | |
- * host via the hypervisor with the notification info. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS on success, appropriate error code otherwise. | |
- * | |
- * Side effects: | |
- * May do a hypercall. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-static int | |
-VMCIDoorbellNotifyAsGuest(VMCIHandle handle, // IN | |
- VMCIPrivilegeFlags privFlags) // IN | |
-{ | |
-#if defined(VMKERNEL) | |
- VMCI_WARNING((LGPFX"Cannot send down to host from VMKERNEL.\n")); | |
- return VMCI_ERROR_DST_UNREACHABLE; | |
-#else // VMKERNEL | |
- VMCIDoorbellNotifyMsg notifyMsg; | |
- | |
- ASSERT(VMCI_GuestPersonalityActive()); | |
- | |
- notifyMsg.hdr.dst = VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID, | |
- VMCI_DOORBELL_NOTIFY); | |
- notifyMsg.hdr.src = VMCI_ANON_SRC_HANDLE; | |
- notifyMsg.hdr.payloadSize = sizeof notifyMsg - VMCI_DG_HEADERSIZE; | |
- notifyMsg.handle = handle; | |
- | |
- return VMCI_SendDatagram((VMCIDatagram *)¬ifyMsg); | |
-#endif // VMKERNEL | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * vmci_doorbell_notify -- | |
- * | |
- * Generates a notification on the doorbell identified by the | |
- * handle. For host side generation of notifications, the caller | |
- * can specify what the privilege of the calling side is. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS on success, appropriate error code otherwise. | |
- * | |
- * Side effects: | |
- * May do a hypercall. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-VMCI_EXPORT_SYMBOL(vmci_doorbell_notify) | |
-int | |
-vmci_doorbell_notify(VMCIHandle dst, // IN | |
- VMCIPrivilegeFlags privFlags) // IN | |
-{ | |
- int retval; | |
- VMCIRoute route; | |
- VMCIHandle src; | |
- | |
- if (VMCI_HANDLE_INVALID(dst) || (privFlags & ~VMCI_PRIVILEGE_ALL_FLAGS)) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- src = VMCI_INVALID_HANDLE; | |
- retval = VMCI_Route(&src, &dst, FALSE, &route); | |
- if (retval < VMCI_SUCCESS) { | |
- return retval; | |
- } | |
- | |
- if (VMCI_ROUTE_AS_HOST == route) { | |
- return VMCIContext_NotifyDoorbell(VMCI_HOST_CONTEXT_ID, dst, privFlags); | |
- } | |
- | |
- if (VMCI_ROUTE_AS_GUEST == route) { | |
- return VMCIDoorbellNotifyAsGuest(dst, privFlags); | |
- } | |
- | |
- VMCI_WARNING((LGPFX"Unknown route (%d) for doorbell.\n", route)); | |
- return VMCI_ERROR_DST_UNREACHABLE; | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCIDoorbellDelayedDispatchCB -- | |
- * | |
- * Calls the specified callback in a delayed context. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-static void | |
-VMCIDoorbellDelayedDispatchCB(void *data) // IN | |
-{ | |
- VMCIDoorbellEntry *entry = (VMCIDoorbellEntry *)data; | |
- | |
- ASSERT(data); | |
- | |
- entry->notifyCB(entry->clientData); | |
- | |
- VMCIResource_Release(&entry->resource); | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCIDoorbellHostContextNotify -- | |
- * | |
- * Dispatches a doorbell notification to the host context. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS on success. Appropriate error code otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-int | |
-VMCIDoorbellHostContextNotify(VMCIId srcCID, // IN | |
- VMCIHandle handle) // IN | |
-{ | |
- VMCIDoorbellEntry *entry; | |
- VMCIResource *resource; | |
- int result; | |
- | |
- ASSERT(VMCI_HostPersonalityActive()); | |
- | |
- if (VMCI_HANDLE_INVALID(handle)) { | |
- VMCI_DEBUG_LOG(4, | |
- (LGPFX"Notifying an invalid doorbell (handle=0x%x:0x%x).\n", | |
- handle.context, handle.resource)); | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- resource = VMCIResource_Get(handle, VMCI_RESOURCE_TYPE_DOORBELL); | |
- if (resource == NULL) { | |
- VMCI_DEBUG_LOG(4, | |
- (LGPFX"Notifying an unknown doorbell (handle=0x%x:0x%x).\n", | |
- handle.context, handle.resource)); | |
- return VMCI_ERROR_NOT_FOUND; | |
- } | |
- entry = RESOURCE_CONTAINER(resource, VMCIDoorbellEntry, resource); | |
- | |
- if (entry->runDelayed) { | |
- result = VMCI_ScheduleDelayedWork(VMCIDoorbellDelayedDispatchCB, entry); | |
- if (result < VMCI_SUCCESS) { | |
- /* | |
- * If we failed to schedule the delayed work, we need to | |
- * release the resource immediately. Otherwise, the resource | |
- * will be released once the delayed callback has been | |
- * completed. | |
- */ | |
- | |
- VMCI_DEBUG_LOG(10, (LGPFX"Failed to schedule delayed doorbell " | |
- "notification (result=%d).\n", result)); | |
- VMCIResource_Release(resource); | |
- } | |
- } else { | |
- entry->notifyCB(entry->clientData); | |
- VMCIResource_Release(resource); | |
- result = VMCI_SUCCESS; | |
- } | |
- return result; | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCIDoorbell_Hibernate -- | |
- * | |
- * When a guest leaves hibernation, the device driver state is out of sync | |
- * with the device state, since the driver state has doorbells registered | |
- * that aren't known to the device. This function takes care of | |
- * reregistering any doorbells. In case an error occurs during | |
- * reregistration (this is highly unlikely since 1) it succeeded the first | |
- * time 2) the device driver is the only source of doorbell registrations), | |
- * we simply log the error. The doorbell can still be destroyed using | |
- * VMCIDoorbell_Destroy. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-void | |
-VMCIDoorbell_Hibernate(Bool enterHibernate) | |
-{ | |
- uint32 bucket; | |
- VMCIListItem *iter; | |
- VMCILockFlags flags; | |
- | |
- if (!VMCI_GuestPersonalityActive() || enterHibernate) { | |
- return; | |
- } | |
- | |
- VMCI_GrabLock_BH(&vmciDoorbellIT.lock, &flags); | |
- | |
- for (bucket = 0; bucket < ARRAYSIZE(vmciDoorbellIT.entries); bucket++) { | |
- VMCIList_Scan(iter, &vmciDoorbellIT.entries[bucket]) { | |
- int result; | |
- VMCIHandle h; | |
- VMCIDoorbellEntry *cur; | |
- | |
- cur = VMCIList_Entry(iter, VMCIDoorbellEntry, idxListItem); | |
- h = VMCIResource_Handle(&cur->resource); | |
- result = VMCIDoorbellLink(h, cur->isDoorbell, cur->idx); | |
- if (result != VMCI_SUCCESS && result != VMCI_ERROR_DUPLICATE_ENTRY) { | |
- VMCI_WARNING((LGPFX"Failed to reregister doorbell " | |
- "(handle=0x%x:0x%x) of resource %s to index " | |
- "(error=%d).\n", | |
- h.context, h.resource, | |
- cur->isDoorbell ? "doorbell" : "queue pair", result)); | |
- } | |
- } | |
- } | |
- | |
- VMCI_ReleaseLock_BH(&vmciDoorbellIT.lock, flags); | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCIDoorbell_Sync -- | |
- * | |
- * Use this as a synchronization point when setting globals, for example, | |
- * during device shutdown. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-void | |
-VMCIDoorbell_Sync(void) | |
-{ | |
- VMCILockFlags flags; | |
- VMCI_GrabLock_BH(&vmciDoorbellIT.lock, &flags); | |
- VMCI_ReleaseLock_BH(&vmciDoorbellIT.lock, flags); | |
- VMCIResource_Sync(); | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCI_RegisterNotificationBitmap -- | |
- * | |
- * Register the notification bitmap with the host. | |
- * | |
- * Results: | |
- * TRUE if the bitmap is registered successfully with the device, FALSE | |
- * otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-Bool | |
-VMCI_RegisterNotificationBitmap(PPN bitmapPPN) | |
-{ | |
- int result; | |
- VMCINotifyBitmapSetMsg bitmapSetMsg; | |
- | |
- /* | |
- * Do not ASSERT() on the guest device here. This function can get called | |
- * during device initialization, so the ASSERT() will fail even though | |
- * the device is (almost) up. | |
- */ | |
- | |
- bitmapSetMsg.hdr.dst = VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID, | |
- VMCI_SET_NOTIFY_BITMAP); | |
- bitmapSetMsg.hdr.src = VMCI_ANON_SRC_HANDLE; | |
- bitmapSetMsg.hdr.payloadSize = sizeof bitmapSetMsg - VMCI_DG_HEADERSIZE; | |
- bitmapSetMsg.bitmapPPN = bitmapPPN; | |
- | |
- result = VMCI_SendDatagram((VMCIDatagram *)&bitmapSetMsg); | |
- if (result != VMCI_SUCCESS) { | |
- VMCI_DEBUG_LOG(4, (LGPFX"Failed to register (PPN=%u) as " | |
- "notification bitmap (error=%d).\n", | |
- bitmapPPN, result)); | |
- return FALSE; | |
- } | |
- return TRUE; | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------- | |
- * | |
- * VMCIDoorbellFireEntries -- | |
- * | |
- * Executes or schedules the handlers for a given notify index. | |
- * | |
- * Result: | |
- * Notification hash entry if found. NULL otherwise. | |
- * | |
- * Side effects: | |
- * Whatever the side effects of the handlers are. | |
- * | |
- *------------------------------------------------------------------------- | |
- */ | |
- | |
-static void | |
-VMCIDoorbellFireEntries(uint32 notifyIdx) // IN | |
-{ | |
- uint32 bucket = VMCI_DOORBELL_HASH(notifyIdx); | |
- VMCIListItem *iter; | |
- VMCILockFlags flags; | |
- | |
- ASSERT(VMCI_GuestPersonalityActive()); | |
- | |
- VMCI_GrabLock_BH(&vmciDoorbellIT.lock, &flags); | |
- | |
- VMCIList_Scan(iter, &vmciDoorbellIT.entries[bucket]) { | |
- VMCIDoorbellEntry *cur = | |
- VMCIList_Entry(iter, VMCIDoorbellEntry, idxListItem); | |
- | |
- ASSERT(cur); | |
- | |
- if (cur->idx == notifyIdx && Atomic_Read(&cur->active) == 1) { | |
- ASSERT(cur->notifyCB); | |
- if (cur->runDelayed) { | |
- int err; | |
- | |
- VMCIResource_Hold(&cur->resource); | |
- err = VMCI_ScheduleDelayedWork(VMCIDoorbellDelayedDispatchCB, cur); | |
- if (err != VMCI_SUCCESS) { | |
- VMCIResource_Release(&cur->resource); | |
- goto out; | |
- } | |
- } else { | |
- cur->notifyCB(cur->clientData); | |
- } | |
- } | |
- } | |
- | |
-out: | |
- VMCI_ReleaseLock_BH(&vmciDoorbellIT.lock, flags); | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCI_ScanNotificationBitmap -- | |
- * | |
- * Scans the notification bitmap, collects pending notifications, | |
- * resets the bitmap and invokes appropriate callbacks. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * May schedule tasks, allocate memory and run callbacks. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-void | |
-VMCI_ScanNotificationBitmap(uint8 *bitmap) | |
-{ | |
- uint32 idx; | |
- | |
- ASSERT(bitmap); | |
- ASSERT(VMCI_GuestPersonalityActive()); | |
- | |
- for (idx = 0; idx < maxNotifyIdx; idx++) { | |
- if (bitmap[idx] & 0x1) { | |
- bitmap[idx] &= ~1; | |
- VMCIDoorbellFireEntries(idx); | |
- } | |
- } | |
-} | |
- | |
- | |
-#else // SOLARIS) || __APPLE__ | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIDoorbell_Create/VMCIDoorbell_Destroy/VMCIDoorbell_Notify/ | |
- * VMCIDoorbellHostContextNotify/VMCIDoorbellGetPrivFlags/ | |
- * VMCIDoorbell_Init/VMCIDoorbell_Exit -- | |
- * | |
- * The doorbell functions have yet to be implemented for Solaris | |
- * and Mac OS X guest drivers. | |
- * | |
- * Results: | |
- * VMCI_ERROR_UNAVAILABLE. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-VMCI_EXPORT_SYMBOL(VMCIDoorbell_Create) | |
-int | |
-VMCIDoorbell_Create(VMCIHandle *handle, // IN | |
- uint32 flags, // IN | |
- VMCIPrivilegeFlags privFlags, // IN | |
- VMCICallback notifyCB, // IN | |
- void *clientData) // IN | |
-{ | |
- return VMCI_ERROR_UNAVAILABLE; | |
-} | |
- | |
- | |
-VMCI_EXPORT_SYMBOL(VMCIDoorbell_Destroy) | |
-int | |
-VMCIDoorbell_Destroy(VMCIHandle handle) // IN | |
-{ | |
- return VMCI_ERROR_UNAVAILABLE; | |
-} | |
- | |
- | |
-VMCI_EXPORT_SYMBOL(VMCIDoorbell_Notify) | |
-int | |
-VMCIDoorbell_Notify(VMCIHandle handle, // IN | |
- VMCIPrivilegeFlags privFlags) // IN | |
-{ | |
- return VMCI_ERROR_UNAVAILABLE; | |
-} | |
- | |
- | |
-int | |
-VMCIDoorbellHostContextNotify(VMCIId srcCID, // IN | |
- VMCIHandle handle) // IN | |
-{ | |
- return VMCI_ERROR_UNAVAILABLE; | |
-} | |
- | |
- | |
-int | |
-VMCIDoorbellGetPrivFlags(VMCIHandle handle, // IN | |
- VMCIPrivilegeFlags *privFlags) // OUT | |
-{ | |
- return VMCI_ERROR_UNAVAILABLE; | |
-} | |
- | |
- | |
-int | |
-VMCIDoorbell_Init(void) | |
-{ | |
- return VMCI_SUCCESS; | |
-} | |
- | |
- | |
-void | |
-VMCIDoorbell_Exit(void) | |
-{ | |
-} | |
- | |
-#endif // SOLARIS) || __APPLE__ | |
diff --git a/modules/linux/vmci/common/vmciDoorbell.h b/modules/linux/vmci/common/vmciDoorbell.h | |
deleted file mode 100644 | |
index 8cb3f72..0000000 | |
--- a/modules/linux/vmci/common/vmciDoorbell.h | |
+++ /dev/null | |
@@ -1,47 +0,0 @@ | |
-/********************************************************* | |
- * Copyright (C) 2010 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 as published by the | |
- * Free Software Foundation version 2 and no 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 | |
- * | |
- *********************************************************/ | |
- | |
-/* | |
- * vmciDoorbell.h -- | |
- * | |
- * Internal functions in the VMCI Doorbell API. | |
- */ | |
- | |
-#ifndef VMCI_DOORBELL_H | |
-#define VMCI_DOORBELL_H | |
- | |
-#define INCLUDE_ALLOW_MODULE | |
-#define INCLUDE_ALLOW_VMCORE | |
-#define INCLUDE_ALLOW_VMKERNEL | |
-#include "includeCheck.h" | |
- | |
-#include "vmci_kernel_if.h" | |
-#include "vmci_defs.h" | |
- | |
-int VMCIDoorbell_Init(void); | |
-void VMCIDoorbell_Exit(void); | |
-void VMCIDoorbell_Hibernate(Bool enterHibernation); | |
-void VMCIDoorbell_Sync(void); | |
- | |
-int VMCIDoorbellHostContextNotify(VMCIId srcCID, VMCIHandle handle); | |
-int VMCIDoorbellGetPrivFlags(VMCIHandle handle, VMCIPrivilegeFlags *privFlags); | |
- | |
-Bool VMCI_RegisterNotificationBitmap(PPN bitmapPPN); | |
-void VMCI_ScanNotificationBitmap(uint8 *bitmap); | |
- | |
-#endif // VMCI_DOORBELL_H | |
diff --git a/modules/linux/vmci/common/vmciDriver.c b/modules/linux/vmci/common/vmciDriver.c | |
deleted file mode 100644 | |
index bd0b496..0000000 | |
--- a/modules/linux/vmci/common/vmciDriver.c | |
+++ /dev/null | |
@@ -1,688 +0,0 @@ | |
-/********************************************************* | |
- * Copyright (C) 2006 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 as published by the | |
- * Free Software Foundation version 2 and no 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 | |
- * | |
- *********************************************************/ | |
- | |
-/* | |
- * vmciDriver.c -- | |
- * | |
- * VMCI initialization and ioctl handling. | |
- */ | |
- | |
-#include "vmci_kernel_if.h" | |
-#include "vm_assert.h" | |
-#include "vmci_defs.h" | |
-#include "vmci_infrastructure.h" | |
-#include "vmciCommonInt.h" | |
-#include "vmciContext.h" | |
-#include "vmciDatagram.h" | |
-#include "vmciDoorbell.h" | |
-#include "vmciDriver.h" | |
-#include "vmciEvent.h" | |
-#include "vmciHashtable.h" | |
-#include "vmciKernelAPI.h" | |
-#include "vmciQueuePair.h" | |
-#include "vmciResource.h" | |
-#if defined(VMKERNEL) | |
-# include "vmciVmkInt.h" | |
-# include "vm_libc.h" | |
-# include "helper_ext.h" | |
-#endif | |
- | |
-#define LGPFX "VMCI: " | |
- | |
-static VMCIId ctxUpdateSubID = VMCI_INVALID_ID; | |
-static VMCIContext *hostContext; | |
-static Atomic_uint32 vmContextID = { VMCI_INVALID_ID }; | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCI_HostInit -- | |
- * | |
- * Initializes the host driver specific components of VMCI. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS if successful, appropriate error code otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCI_HostInit(void) | |
-{ | |
- int result; | |
- | |
- /* | |
- * In theory, it is unsafe to pass an eventHnd of -1 to platforms which use | |
- * it (VMKernel/Windows/Mac OS at the time of this writing). In practice we | |
- * are fine though, because the event is never used in the case of the host | |
- * context. | |
- */ | |
- result = VMCIContext_InitContext(VMCI_HOST_CONTEXT_ID, | |
- VMCI_DEFAULT_PROC_PRIVILEGE_FLAGS, | |
- -1, VMCI_VERSION, NULL, &hostContext); | |
- if (result < VMCI_SUCCESS) { | |
- VMCI_WARNING((LGPFX"Failed to initialize VMCIContext (result=%d).\n", | |
- result)); | |
- goto errorExit; | |
- } | |
- | |
- result = VMCIQPBroker_Init(); | |
- if (result < VMCI_SUCCESS) { | |
- goto hostContextExit; | |
- } | |
- | |
- VMCI_DEBUG_LOG(0, (LGPFX"host components initialized.\n")); | |
- return VMCI_SUCCESS; | |
- | |
-hostContextExit: | |
- VMCIContext_ReleaseContext(hostContext); | |
-errorExit: | |
- return result; | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCI_HostCleanup -- | |
- * | |
- * Cleans up the host specific components of the VMCI module. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCI_HostCleanup(void) | |
-{ | |
- VMCIContext_ReleaseContext(hostContext); | |
- VMCIQPBroker_Exit(); | |
-} | |
- | |
- | |
-#if defined(__APPLE__) || defined(SOLARIS) || defined(VMKERNEL) | |
-/* Windows has its own implementation of this, and Linux doesn't need one. */ | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * vmci_device_get -- | |
- * | |
- * Verifies that a valid VMCI device is present, and indicates | |
- * the callers intention to use the device until it calls | |
- * VMCI_DeviceRelease(). | |
- * | |
- * Results: | |
- * TRUE if a valid VMCI device is present, FALSE otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-VMCI_EXPORT_SYMBOL(vmci_device_get) | |
-Bool | |
-vmci_device_get(uint32 *apiVersion, // IN/OUT | |
- VMCI_DeviceShutdownFn *deviceShutdownCB, // UNUSED | |
- void *userData, // UNUSED | |
- void **deviceRegistration) // OUT | |
-{ | |
- if (NULL != deviceRegistration) { | |
- *deviceRegistration = NULL; | |
- } | |
- | |
- if (*apiVersion > VMCI_KERNEL_API_VERSION) { | |
- *apiVersion = VMCI_KERNEL_API_VERSION; | |
- return FALSE; | |
- } | |
- | |
- if (!VMCI_DeviceEnabled()) { | |
- return FALSE; | |
- } | |
- | |
- return TRUE; | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * vmci_device_release -- | |
- * | |
- * Indicates that the caller is done using the VMCI device. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-VMCI_EXPORT_SYMBOL(vmci_device_release) | |
-void | |
-vmci_device_release(void *deviceRegistration) // UNUSED | |
-{ | |
-} | |
-#endif // __APPLE__ || SOLARIS || VMKERNEL | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIUtilCidUpdate -- | |
- * | |
- * Gets called with the new context id if updated or resumed. | |
- * | |
- * Results: | |
- * Context id. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static void | |
-VMCIUtilCidUpdate(VMCIId subID, // IN: | |
- VMCI_EventData *eventData, // IN: | |
- void *clientData) // IN: | |
-{ | |
- VMCIEventPayload_Context *evPayload = VMCIEventDataPayload(eventData); | |
- | |
- if (subID != ctxUpdateSubID) { | |
- VMCI_DEBUG_LOG(4, (LGPFX"Invalid subscriber (ID=0x%x).\n", subID)); | |
- return; | |
- } | |
- if (eventData == NULL || evPayload->contextID == VMCI_INVALID_ID) { | |
- VMCI_DEBUG_LOG(4, (LGPFX"Invalid event data.\n")); | |
- return; | |
- } | |
- VMCI_LOG((LGPFX"Updating context from (ID=0x%x) to (ID=0x%x) on event " | |
- "(type=%d).\n", Atomic_Read(&vmContextID), evPayload->contextID, | |
- eventData->event)); | |
- Atomic_Write(&vmContextID, evPayload->contextID); | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIUtil_Init -- | |
- * | |
- * Subscribe to context id update event. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCIUtil_Init(void) | |
-{ | |
- /* | |
- * We subscribe to the VMCI_EVENT_CTX_ID_UPDATE here so we can update the | |
- * internal context id when needed. | |
- */ | |
- if (vmci_event_subscribe(VMCI_EVENT_CTX_ID_UPDATE, | |
-#if !defined(linux) || defined(VMKERNEL) | |
- VMCI_FLAG_EVENT_NONE, | |
-#endif // !linux || VMKERNEL | |
- VMCIUtilCidUpdate, NULL, | |
- &ctxUpdateSubID) < VMCI_SUCCESS) { | |
- VMCI_WARNING((LGPFX"Failed to subscribe to event (type=%d).\n", | |
- VMCI_EVENT_CTX_ID_UPDATE)); | |
- } | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIUtil_Exit -- | |
- * | |
- * Cleanup | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCIUtil_Exit(void) | |
-{ | |
- if (vmci_event_unsubscribe(ctxUpdateSubID) < VMCI_SUCCESS) { | |
- VMCI_WARNING((LGPFX"Failed to unsubscribe to event (type=%d) with " | |
- "subscriber (ID=0x%x).\n", VMCI_EVENT_CTX_ID_UPDATE, | |
- ctxUpdateSubID)); | |
- } | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIUtil_CheckHostCapabilities -- | |
- * | |
- * Verify that the host supports the hypercalls we need. If it does not, | |
- * try to find fallback hypercalls and use those instead. | |
- * | |
- * Results: | |
- * TRUE if required hypercalls (or fallback hypercalls) are | |
- * supported by the host, FALSE otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-#define VMCI_UTIL_NUM_RESOURCES 1 | |
- | |
-static Bool | |
-VMCIUtilCheckHostCapabilities(void) | |
-{ | |
- int result; | |
- VMCIResourcesQueryMsg *msg; | |
- uint32 msgSize = sizeof(VMCIResourcesQueryHdr) + | |
- VMCI_UTIL_NUM_RESOURCES * sizeof(VMCI_Resource); | |
- VMCIDatagram *checkMsg = VMCI_AllocKernelMem(msgSize, VMCI_MEMORY_NONPAGED); | |
- | |
- if (checkMsg == NULL) { | |
- VMCI_WARNING((LGPFX"Check host: Insufficient memory.\n")); | |
- return FALSE; | |
- } | |
- | |
- checkMsg->dst = VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID, | |
- VMCI_RESOURCES_QUERY); | |
- checkMsg->src = VMCI_ANON_SRC_HANDLE; | |
- checkMsg->payloadSize = msgSize - VMCI_DG_HEADERSIZE; | |
- msg = (VMCIResourcesQueryMsg *)VMCI_DG_PAYLOAD(checkMsg); | |
- | |
- msg->numResources = VMCI_UTIL_NUM_RESOURCES; | |
- msg->resources[0] = VMCI_GET_CONTEXT_ID; | |
- | |
- result = VMCI_SendDatagram(checkMsg); | |
- VMCI_FreeKernelMem(checkMsg, msgSize); | |
- | |
- /* We need the vector. There are no fallbacks. */ | |
- return (result == 0x1); | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCI_CheckHostCapabilities -- | |
- * | |
- * Tell host which guestcalls we support and let each API check | |
- * that the host supports the hypercalls it needs. If a hypercall | |
- * is not supported, the API can check for a fallback hypercall, | |
- * or fail the check. | |
- * | |
- * Results: | |
- * TRUE if successful, FALSE otherwise. | |
- * | |
- * Side effects: | |
- * Fallback mechanisms may be enabled in the API and vmmon. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-Bool | |
-VMCI_CheckHostCapabilities(void) | |
-{ | |
- Bool result = VMCIEvent_CheckHostCapabilities(); | |
- result &= VMCIDatagram_CheckHostCapabilities(); | |
- result &= VMCIUtilCheckHostCapabilities(); | |
- | |
- if (!result) { | |
- /* If it failed, then make sure this goes to the system event log. */ | |
- VMCI_WARNING((LGPFX"Host capability checked failed.\n")); | |
- } else { | |
- VMCI_DEBUG_LOG(0, (LGPFX"Host capability check passed.\n")); | |
- } | |
- | |
- return result; | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCI_ReadDatagramsFromPort -- | |
- * | |
- * Reads datagrams from the data in port and dispatches them. We | |
- * always start reading datagrams into only the first page of the | |
- * datagram buffer. If the datagrams don't fit into one page, we | |
- * use the maximum datagram buffer size for the remainder of the | |
- * invocation. This is a simple heuristic for not penalizing | |
- * small datagrams. | |
- * | |
- * This function assumes that it has exclusive access to the data | |
- * in port for the duration of the call. | |
- * | |
- * Results: | |
- * No result. | |
- * | |
- * Side effects: | |
- * Datagram handlers may be invoked. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCI_ReadDatagramsFromPort(VMCIIoHandle ioHandle, // IN | |
- VMCIIoPort dgInPort, // IN | |
- uint8 *dgInBuffer, // IN | |
- size_t dgInBufferSize) // IN | |
-{ | |
- VMCIDatagram *dg; | |
- size_t currentDgInBufferSize = PAGE_SIZE; | |
- size_t remainingBytes; | |
- | |
- ASSERT(dgInBufferSize >= PAGE_SIZE); | |
- | |
- VMCI_ReadPortBytes(ioHandle, dgInPort, dgInBuffer, currentDgInBufferSize); | |
- dg = (VMCIDatagram *)dgInBuffer; | |
- remainingBytes = currentDgInBufferSize; | |
- | |
- while (dg->dst.resource != VMCI_INVALID_ID || remainingBytes > PAGE_SIZE) { | |
- unsigned dgInSize; | |
- | |
- /* | |
- * When the input buffer spans multiple pages, a datagram can | |
- * start on any page boundary in the buffer. | |
- */ | |
- | |
- if (dg->dst.resource == VMCI_INVALID_ID) { | |
- ASSERT(remainingBytes > PAGE_SIZE); | |
- dg = (VMCIDatagram *)ROUNDUP((uintptr_t)dg + 1, PAGE_SIZE); | |
- ASSERT((uint8 *)dg < dgInBuffer + currentDgInBufferSize); | |
- remainingBytes = (size_t)(dgInBuffer + currentDgInBufferSize - (uint8 *)dg); | |
- continue; | |
- } | |
- | |
- dgInSize = VMCI_DG_SIZE_ALIGNED(dg); | |
- | |
- if (dgInSize <= dgInBufferSize) { | |
- int result; | |
- | |
- /* | |
- * If the remaining bytes in the datagram buffer doesn't | |
- * contain the complete datagram, we first make sure we have | |
- * enough room for it and then we read the reminder of the | |
- * datagram and possibly any following datagrams. | |
- */ | |
- | |
- if (dgInSize > remainingBytes) { | |
- | |
- if (remainingBytes != currentDgInBufferSize) { | |
- | |
- /* | |
- * We move the partial datagram to the front and read | |
- * the reminder of the datagram and possibly following | |
- * calls into the following bytes. | |
- */ | |
- | |
- memmove(dgInBuffer, dgInBuffer + currentDgInBufferSize - remainingBytes, | |
- remainingBytes); | |
- | |
- dg = (VMCIDatagram *)dgInBuffer; | |
- } | |
- | |
- if (currentDgInBufferSize != dgInBufferSize) { | |
- currentDgInBufferSize = dgInBufferSize; | |
- } | |
- | |
- VMCI_ReadPortBytes(ioHandle, dgInPort, dgInBuffer + remainingBytes, | |
- currentDgInBufferSize - remainingBytes); | |
- } | |
- | |
- /* We special case event datagrams from the hypervisor. */ | |
- if (dg->src.context == VMCI_HYPERVISOR_CONTEXT_ID && | |
- dg->dst.resource == VMCI_EVENT_HANDLER) { | |
- result = VMCIEvent_Dispatch(dg); | |
- } else { | |
- result = VMCIDatagram_InvokeGuestHandler(dg); | |
- } | |
- if (result < VMCI_SUCCESS) { | |
- VMCI_DEBUG_LOG(4, (LGPFX"Datagram with resource (ID=0x%x) failed " | |
- "(err=%d).\n", dg->dst.resource, result)); | |
- } | |
- | |
- /* On to the next datagram. */ | |
- dg = (VMCIDatagram *)((uint8 *)dg + dgInSize); | |
- } else { | |
- size_t bytesToSkip; | |
- | |
- /* | |
- * Datagram doesn't fit in datagram buffer of maximal size. We drop it. | |
- */ | |
- | |
- VMCI_DEBUG_LOG(4, (LGPFX"Failed to receive datagram (size=%u bytes).\n", | |
- dgInSize)); | |
- | |
- bytesToSkip = dgInSize - remainingBytes; | |
- if (currentDgInBufferSize != dgInBufferSize) { | |
- currentDgInBufferSize = dgInBufferSize; | |
- } | |
- for (;;) { | |
- VMCI_ReadPortBytes(ioHandle, dgInPort, dgInBuffer, currentDgInBufferSize); | |
- if (bytesToSkip <= currentDgInBufferSize) { | |
- break; | |
- } | |
- bytesToSkip -= currentDgInBufferSize; | |
- } | |
- dg = (VMCIDatagram *)(dgInBuffer + bytesToSkip); | |
- } | |
- | |
- remainingBytes = (size_t) (dgInBuffer + currentDgInBufferSize - (uint8 *)dg); | |
- | |
- if (remainingBytes < VMCI_DG_HEADERSIZE) { | |
- /* Get the next batch of datagrams. */ | |
- | |
- VMCI_ReadPortBytes(ioHandle, dgInPort, dgInBuffer, currentDgInBufferSize); | |
- dg = (VMCIDatagram *)dgInBuffer; | |
- remainingBytes = currentDgInBufferSize; | |
- } | |
- } | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------------- | |
- * | |
- * vmci_get_context_id -- | |
- * | |
- * Returns the current context ID. Note that since this is accessed only | |
- * from code running in the host, this always returns the host context ID. | |
- * | |
- * Results: | |
- * Context ID. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------------- | |
- */ | |
- | |
-VMCI_EXPORT_SYMBOL(vmci_get_context_id) | |
-VMCIId | |
-vmci_get_context_id(void) | |
-{ | |
- if (VMCI_GuestPersonalityActive()) { | |
- if (Atomic_Read(&vmContextID) == VMCI_INVALID_ID) { | |
- uint32 result; | |
- VMCIDatagram getCidMsg; | |
- getCidMsg.dst = VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID, | |
- VMCI_GET_CONTEXT_ID); | |
- getCidMsg.src = VMCI_ANON_SRC_HANDLE; | |
- getCidMsg.payloadSize = 0; | |
- result = VMCI_SendDatagram(&getCidMsg); | |
- Atomic_Write(&vmContextID, result); | |
- } | |
- return Atomic_Read(&vmContextID); | |
- } else if (VMCI_HostPersonalityActive()) { | |
- return VMCI_HOST_CONTEXT_ID; | |
- } | |
- return VMCI_INVALID_ID; | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * vmci_version -- | |
- * | |
- * Returns the version of the VMCI driver. | |
- * | |
- * Results: | |
- * Returns a version number. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-VMCI_EXPORT_SYMBOL(vmci_version) | |
-uint32 | |
-vmci_version(void) | |
-{ | |
- return VMCI_VERSION; | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCI_SharedInit -- | |
- * | |
- * Initializes VMCI components shared between guest and host | |
- * driver. This registers core hypercalls. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS if successful, appropriate error code otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCI_SharedInit(void) | |
-{ | |
- int result; | |
- | |
- result = VMCIResource_Init(); | |
- if (result < VMCI_SUCCESS) { | |
- VMCI_WARNING((LGPFX"Failed to initialize VMCIResource (result=%d).\n", | |
- result)); | |
- goto errorExit; | |
- } | |
- | |
- result = VMCIContext_Init(); | |
- if (result < VMCI_SUCCESS) { | |
- VMCI_WARNING((LGPFX"Failed to initialize VMCIContext (result=%d).\n", | |
- result)); | |
- goto resourceExit; | |
- } | |
- | |
- result = VMCIDatagram_Init(); | |
- if (result < VMCI_SUCCESS) { | |
- VMCI_WARNING((LGPFX"Failed to initialize VMCIDatagram (result=%d).\n", | |
- result)); | |
- goto contextExit; | |
- } | |
- | |
- result = VMCIEvent_Init(); | |
- if (result < VMCI_SUCCESS) { | |
- VMCI_WARNING((LGPFX"Failed to initialize VMCIEvent (result=%d).\n", | |
- result)); | |
- goto datagramExit; | |
- } | |
- | |
- result = VMCIDoorbell_Init(); | |
- if (result < VMCI_SUCCESS) { | |
- VMCI_WARNING((LGPFX"Failed to initialize VMCIDoorbell (result=%d).\n", | |
- result)); | |
- goto eventExit; | |
- } | |
- | |
- VMCI_DEBUG_LOG(0, (LGPFX"shared components initialized.\n")); | |
- return VMCI_SUCCESS; | |
- | |
-eventExit: | |
- VMCIEvent_Exit(); | |
-datagramExit: | |
- VMCIDatagram_Exit(); | |
-contextExit: | |
- VMCIContext_Exit(); | |
-resourceExit: | |
- VMCIResource_Exit(); | |
-errorExit: | |
- return result; | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCI_SharedCleanup -- | |
- * | |
- * Cleans up VMCI components shared between guest and host | |
- * driver. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCI_SharedCleanup(void) | |
-{ | |
- VMCIDoorbell_Exit(); | |
- VMCIEvent_Exit(); | |
- VMCIDatagram_Exit(); | |
- VMCIContext_Exit(); | |
- VMCIResource_Exit(); | |
-} | |
diff --git a/modules/linux/vmci/common/vmciDriver.h b/modules/linux/vmci/common/vmciDriver.h | |
deleted file mode 100644 | |
index 9ce7995..0000000 | |
--- a/modules/linux/vmci/common/vmciDriver.h | |
+++ /dev/null | |
@@ -1,98 +0,0 @@ | |
-/********************************************************* | |
- * Copyright (C) 2006 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 as published by the | |
- * Free Software Foundation version 2 and no 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 | |
- * | |
- *********************************************************/ | |
- | |
-/* | |
- * vmciDriver.h -- | |
- * | |
- * VMCI host driver interface. | |
- */ | |
- | |
-#ifndef _VMCI_DRIVER_H_ | |
-#define _VMCI_DRIVER_H_ | |
- | |
-#define INCLUDE_ALLOW_MODULE | |
-#define INCLUDE_ALLOW_VMMON | |
-#define INCLUDE_ALLOW_VMCORE | |
-#define INCLUDE_ALLOW_VMKERNEL | |
-#include "includeCheck.h" | |
- | |
-#include "vmci_defs.h" | |
-#include "vmci_infrastructure.h" | |
-#include "vmciContext.h" | |
- | |
-/* | |
- * A few macros to encapsulate logging in common code. The macros | |
- * result in LOG/LOGThrottled on vmkernel and Log on hosted. | |
- */ | |
- | |
-#ifdef _WIN32 | |
-# include "vmciLog.h" | |
-#else // _WIN32 | |
-#if defined(VMKERNEL) && !defined(VMKERNEL_OFFSET_CHECKER) | |
- /* | |
- * LOGLEVEL_MODULE is defined in another header that gets included into the | |
- * dummy file used by the offsetchecker, which causes it to barf on the | |
- * redefinition. | |
- */ | |
-# define LOGLEVEL_MODULE_LEN 0 | |
-# define LOGLEVEL_MODULE VMCIVMK | |
-# include "log.h" | |
-# ifdef VMX86_LOG | |
-# define _VMCILOG(_args...) LOG(i, _args) | |
-# define VMCI_DEBUG_LOG(_level, _args) \ | |
- do { \ | |
- int i = _level; \ | |
- _VMCILOG _args ; \ | |
- } while(FALSE) | |
-# else // VMX86_LOG | |
-# define VMCI_DEBUG_LOG(_level, _args) | |
-# endif // VMX86_LOG | |
-#else // VMKERNEL | |
-# define VMCI_DEBUG_LEVEL 4 | |
-# define VMCI_DEBUG_LOG(_level, _args) \ | |
- do { \ | |
- if (_level < VMCI_DEBUG_LEVEL) { \ | |
- Log _args ; \ | |
- } \ | |
- } while(FALSE) | |
-#endif // VMKERNEL | |
-#define VMCI_LOG(_args) Log _args | |
-#define VMCI_WARNING(_args) Warning _args | |
-#endif // _WIN32 | |
- | |
- | |
-int VMCI_SharedInit(void); | |
-void VMCI_SharedCleanup(void); | |
-int VMCI_HostInit(void); | |
-void VMCI_HostCleanup(void); | |
-VMCIId VMCI_GetContextID(void); | |
-int VMCI_SendDatagram(VMCIDatagram *dg); | |
- | |
-void VMCIUtil_Init(void); | |
-void VMCIUtil_Exit(void); | |
-Bool VMCI_CheckHostCapabilities(void); | |
-void VMCI_ReadDatagramsFromPort(VMCIIoHandle ioHandle, VMCIIoPort dgInPort, | |
- uint8 *dgInBuffer, size_t dgInBufferSize); | |
-Bool VMCI_DeviceEnabled(void); | |
-#if defined(_WIN64) | |
-int VMCIDoSendDatagram(unsigned int dgSize, ULONG *dataPort, ULONG *resultPort, | |
- VMCIDatagram *dg); | |
-#endif // _WIN64 | |
- | |
- | |
-#endif // _VMCI_DRIVER_H_ | |
diff --git a/modules/linux/vmci/common/vmciEvent.c b/modules/linux/vmci/common/vmciEvent.c | |
deleted file mode 100644 | |
index 0d6a569..0000000 | |
--- a/modules/linux/vmci/common/vmciEvent.c | |
+++ /dev/null | |
@@ -1,788 +0,0 @@ | |
-/********************************************************* | |
- * Copyright (C) 2007 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 as published by the | |
- * Free Software Foundation version 2 and no 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 | |
- * | |
- *********************************************************/ | |
- | |
-/* | |
- * vmciEvent.c -- | |
- * | |
- * VMCI Event code for host and guests. | |
- */ | |
- | |
-#include "vmci_kernel_if.h" | |
-#include "vmci_defs.h" | |
-#include "vmci_infrastructure.h" | |
-#include "vmciEvent.h" | |
-#include "vmciKernelAPI.h" | |
-#if defined(VMKERNEL) | |
-# include "vmciVmkInt.h" | |
-# include "vm_libc.h" | |
-# include "helper_ext.h" | |
-# include "vmciDriver.h" | |
-#else | |
-# include "vmciDriver.h" | |
-#endif | |
- | |
-#define LGPFX "VMCIEvent: " | |
- | |
-#define EVENT_MAGIC 0xEABE0000 | |
- | |
-typedef struct VMCISubscription { | |
- VMCIId id; | |
- int refCount; | |
- Bool runDelayed; | |
- VMCIEvent destroyEvent; | |
- VMCI_Event event; | |
- VMCI_EventCB callback; | |
- void *callbackData; | |
- VMCIListItem subscriberListItem; | |
-} VMCISubscription; | |
- | |
- | |
-static VMCISubscription *VMCIEventFind(VMCIId subID); | |
-static int VMCIEventDeliver(VMCIEventMsg *eventMsg); | |
-static int VMCIEventRegisterSubscription(VMCISubscription *sub, | |
- VMCI_Event event, | |
- uint32 flags, | |
- VMCI_EventCB callback, | |
- void *callbackData); | |
-static VMCISubscription *VMCIEventUnregisterSubscription(VMCIId subID); | |
- | |
-static VMCIList subscriberArray[VMCI_EVENT_MAX]; | |
-static VMCILock subscriberLock; | |
- | |
-typedef struct VMCIDelayedEventInfo { | |
- VMCISubscription *sub; | |
- uint8 eventPayload[sizeof(VMCIEventData_Max)]; | |
-} VMCIDelayedEventInfo; | |
- | |
-typedef struct VMCIEventRef { | |
- VMCISubscription *sub; | |
- VMCIListItem listItem; | |
-} VMCIEventRef; | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIEvent_Init -- | |
- * | |
- * General init code. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS on success, appropriate error code otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCIEvent_Init(void) | |
-{ | |
- int i; | |
- | |
- for (i = 0; i < VMCI_EVENT_MAX; i++) { | |
- VMCIList_Init(&subscriberArray[i]); | |
- } | |
- | |
- return VMCI_InitLock(&subscriberLock, "VMCIEventSubscriberLock", | |
- VMCI_LOCK_RANK_EVENT); | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIEvent_Exit -- | |
- * | |
- * General exit code. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCIEvent_Exit(void) | |
-{ | |
- VMCIListItem *iter, *iter2; | |
- VMCI_Event e; | |
- | |
- /* We free all memory at exit. */ | |
- for (e = 0; e < VMCI_EVENT_MAX; e++) { | |
- VMCIList_ScanSafe(iter, iter2, &subscriberArray[e]) { | |
- VMCISubscription *cur; | |
- | |
- /* | |
- * We should never get here because all events should have been | |
- * unregistered before we try to unload the driver module. | |
- * Also, delayed callbacks could still be firing so this cleanup | |
- * would not be safe. | |
- * Still it is better to free the memory than not ... so we | |
- * leave this code in just in case.... | |
- * | |
- */ | |
- ASSERT(FALSE); | |
- | |
- cur = VMCIList_Entry(iter, VMCISubscription, subscriberListItem); | |
- VMCI_FreeKernelMem(cur, sizeof *cur); | |
- } | |
- } | |
- VMCI_CleanupLock(&subscriberLock); | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIEvent_Sync -- | |
- * | |
- * Use this as a synchronization point when setting globals, for example, | |
- * during device shutdown. | |
- * | |
- * Results: | |
- * TRUE. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCIEvent_Sync(void) | |
-{ | |
- VMCILockFlags lockFlags; | |
- VMCI_GrabLock_BH(&subscriberLock, &lockFlags); | |
- VMCI_ReleaseLock_BH(&subscriberLock, lockFlags); | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIEvent_CheckHostCapabilities -- | |
- * | |
- * Verify that the host supports the hypercalls we need. If it does not, | |
- * try to find fallback hypercalls and use those instead. | |
- * | |
- * Results: | |
- * TRUE if required hypercalls (or fallback hypercalls) are | |
- * supported by the host, FALSE otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-Bool | |
-VMCIEvent_CheckHostCapabilities(void) | |
-{ | |
- /* VMCIEvent does not require any hypercalls. */ | |
- return TRUE; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIEventGet -- | |
- * | |
- * Gets a reference to the given VMCISubscription. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static void | |
-VMCIEventGet(VMCISubscription *entry) // IN | |
-{ | |
- ASSERT(entry); | |
- | |
- entry->refCount++; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIEventRelease -- | |
- * | |
- * Releases the given VMCISubscription. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * Fires the destroy event if the reference count has gone to zero. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static void | |
-VMCIEventRelease(VMCISubscription *entry) // IN | |
-{ | |
- ASSERT(entry); | |
- ASSERT(entry->refCount > 0); | |
- | |
- entry->refCount--; | |
- if (entry->refCount == 0) { | |
- VMCI_SignalEvent(&entry->destroyEvent); | |
- } | |
-} | |
- | |
- | |
- /* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * EventReleaseCB -- | |
- * | |
- * Callback to release the event entry reference. It is called by the | |
- * VMCI_WaitOnEvent function before it blocks. | |
- * | |
- * Result: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-static int | |
-EventReleaseCB(void *clientData) // IN | |
-{ | |
- VMCILockFlags flags; | |
- VMCISubscription *sub = (VMCISubscription *)clientData; | |
- | |
- ASSERT(sub); | |
- | |
- VMCI_GrabLock_BH(&subscriberLock, &flags); | |
- VMCIEventRelease(sub); | |
- VMCI_ReleaseLock_BH(&subscriberLock, flags); | |
- | |
- return 0; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIEventFind -- | |
- * | |
- * Find entry. Assumes lock is held. | |
- * | |
- * Results: | |
- * Entry if found, NULL if not. | |
- * | |
- * Side effects: | |
- * Increments the VMCISubscription refcount if an entry is found. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static VMCISubscription * | |
-VMCIEventFind(VMCIId subID) // IN | |
-{ | |
- VMCIListItem *iter; | |
- VMCI_Event e; | |
- | |
- for (e = 0; e < VMCI_EVENT_MAX; e++) { | |
- VMCIList_Scan(iter, &subscriberArray[e]) { | |
- VMCISubscription *cur = | |
- VMCIList_Entry(iter, VMCISubscription, subscriberListItem); | |
- if (cur->id == subID) { | |
- VMCIEventGet(cur); | |
- return cur; | |
- } | |
- } | |
- } | |
- return NULL; | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIEventDelayedDispatchCB -- | |
- * | |
- * Calls the specified callback in a delayed context. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-static void | |
-VMCIEventDelayedDispatchCB(void *data) // IN | |
-{ | |
- VMCIDelayedEventInfo *eventInfo; | |
- VMCISubscription *sub; | |
- VMCI_EventData *ed; | |
- VMCILockFlags flags; | |
- | |
- eventInfo = (VMCIDelayedEventInfo *)data; | |
- | |
- ASSERT(eventInfo); | |
- ASSERT(eventInfo->sub); | |
- | |
- sub = eventInfo->sub; | |
- ed = (VMCI_EventData *)eventInfo->eventPayload; | |
- | |
- sub->callback(sub->id, ed, sub->callbackData); | |
- | |
- VMCI_GrabLock_BH(&subscriberLock, &flags); | |
- VMCIEventRelease(sub); | |
- VMCI_ReleaseLock_BH(&subscriberLock, flags); | |
- | |
- VMCI_FreeKernelMem(eventInfo, sizeof *eventInfo); | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------------- | |
- * | |
- * VMCIEventDeliver -- | |
- * | |
- * Actually delivers the events to the subscribers. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * The callback function for each subscriber is invoked. | |
- * | |
- *---------------------------------------------------------------------------- | |
- */ | |
- | |
-static int | |
-VMCIEventDeliver(VMCIEventMsg *eventMsg) // IN | |
-{ | |
- int err = VMCI_SUCCESS; | |
- VMCIListItem *iter; | |
- VMCILockFlags flags; | |
- | |
- VMCIList noDelayList; | |
- VMCIList_Init(&noDelayList); | |
- | |
- ASSERT(eventMsg); | |
- | |
- VMCI_GrabLock_BH(&subscriberLock, &flags); | |
- VMCIList_Scan(iter, &subscriberArray[eventMsg->eventData.event]) { | |
- VMCISubscription *cur = VMCIList_Entry(iter, VMCISubscription, | |
- subscriberListItem); | |
- ASSERT(cur && cur->event == eventMsg->eventData.event); | |
- | |
- if (cur->runDelayed) { | |
- VMCIDelayedEventInfo *eventInfo; | |
- if ((eventInfo = VMCI_AllocKernelMem(sizeof *eventInfo, | |
- (VMCI_MEMORY_ATOMIC | | |
- VMCI_MEMORY_NONPAGED))) == NULL) { | |
- err = VMCI_ERROR_NO_MEM; | |
- goto out; | |
- } | |
- | |
- VMCIEventGet(cur); | |
- | |
- memset(eventInfo, 0, sizeof *eventInfo); | |
- memcpy(eventInfo->eventPayload, VMCI_DG_PAYLOAD(eventMsg), | |
- (size_t)eventMsg->hdr.payloadSize); | |
- eventInfo->sub = cur; | |
- err = VMCI_ScheduleDelayedWork(VMCIEventDelayedDispatchCB, | |
- eventInfo); | |
- if (err != VMCI_SUCCESS) { | |
- VMCIEventRelease(cur); | |
- VMCI_FreeKernelMem(eventInfo, sizeof *eventInfo); | |
- goto out; | |
- } | |
- | |
- } else { | |
- VMCIEventRef *eventRef; | |
- | |
- /* | |
- * To avoid possible lock rank voilation when holding | |
- * subscriberLock, we construct a local list of | |
- * subscribers and release subscriberLock before | |
- * invokes the callbacks. This is similar to delayed | |
- * callbacks, but callbacks is invoked right away here. | |
- */ | |
- if ((eventRef = VMCI_AllocKernelMem(sizeof *eventRef, | |
- (VMCI_MEMORY_ATOMIC | | |
- VMCI_MEMORY_NONPAGED))) == NULL) { | |
- err = VMCI_ERROR_NO_MEM; | |
- goto out; | |
- } | |
- | |
- VMCIEventGet(cur); | |
- eventRef->sub = cur; | |
- VMCIList_InitEntry(&eventRef->listItem); | |
- VMCIList_Insert(&eventRef->listItem, &noDelayList); | |
- } | |
- } | |
- | |
-out: | |
- VMCI_ReleaseLock_BH(&subscriberLock, flags); | |
- | |
- if (!VMCIList_Empty(&noDelayList)) { | |
- VMCI_EventData *ed; | |
- VMCIListItem *iter2; | |
- | |
- VMCIList_ScanSafe(iter, iter2, &noDelayList) { | |
- VMCIEventRef *eventRef = VMCIList_Entry(iter, VMCIEventRef, | |
- listItem); | |
- VMCISubscription *cur = eventRef->sub; | |
- uint8 eventPayload[sizeof(VMCIEventData_Max)]; | |
- | |
- /* We set event data before each callback to ensure isolation. */ | |
- memset(eventPayload, 0, sizeof eventPayload); | |
- memcpy(eventPayload, VMCI_DG_PAYLOAD(eventMsg), | |
- (size_t)eventMsg->hdr.payloadSize); | |
- ed = (VMCI_EventData *)eventPayload; | |
- cur->callback(cur->id, ed, cur->callbackData); | |
- | |
- VMCI_GrabLock_BH(&subscriberLock, &flags); | |
- VMCIEventRelease(cur); | |
- VMCI_ReleaseLock_BH(&subscriberLock, flags); | |
- VMCI_FreeKernelMem(eventRef, sizeof *eventRef); | |
- } | |
- } | |
- | |
- return err; | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIEvent_Dispatch -- | |
- * | |
- * Dispatcher for the VMCI_EVENT_RECEIVE datagrams. Calls all | |
- * subscribers for given event. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS on success, error code otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCIEvent_Dispatch(VMCIDatagram *msg) // IN | |
-{ | |
- VMCIEventMsg *eventMsg = (VMCIEventMsg *)msg; | |
- | |
- ASSERT(msg && | |
- msg->src.context == VMCI_HYPERVISOR_CONTEXT_ID && | |
- msg->dst.resource == VMCI_EVENT_HANDLER); | |
- | |
- if (msg->payloadSize < sizeof(VMCI_Event) || | |
- msg->payloadSize > sizeof(VMCIEventData_Max)) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- if (!VMCI_EVENT_VALID(eventMsg->eventData.event)) { | |
- return VMCI_ERROR_EVENT_UNKNOWN; | |
- } | |
- | |
- VMCIEventDeliver(eventMsg); | |
- | |
- return VMCI_SUCCESS; | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIEventRegisterSubscription -- | |
- * | |
- * Initialize and add subscription to subscriber list. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS on success, error code otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-static int | |
-VMCIEventRegisterSubscription(VMCISubscription *sub, // IN | |
- VMCI_Event event, // IN | |
- uint32 flags, // IN | |
- VMCI_EventCB callback, // IN | |
- void *callbackData) // IN | |
-{ | |
-# define VMCI_EVENT_MAX_ATTEMPTS 10 | |
- static VMCIId subscriptionID = 0; | |
- VMCILockFlags lockFlags; | |
- uint32 attempts = 0; | |
- int result; | |
- Bool success; | |
- | |
- ASSERT(sub); | |
- | |
- if (!VMCI_EVENT_VALID(event) || callback == NULL) { | |
- VMCI_DEBUG_LOG(4, (LGPFX"Failed to subscribe to event (type=%d) " | |
- "(callback=%p) (data=%p).\n", | |
- event, callback, callbackData)); | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- if (vmkernel) { | |
- /* | |
- * In the vmkernel we defer delivery of events to a helper world. This | |
- * makes the event delivery more consistent across hosts and guests with | |
- * regard to which locks are held. Memory access events are an exception | |
- * to this, since clients need to know immediately that the device | |
- * memory is disabled (if we delay such events, then clients may be | |
- * notified too late). | |
- */ | |
- if (VMCI_EVENT_MEM_ACCESS_ON == event || | |
- VMCI_EVENT_MEM_ACCESS_OFF == event) { | |
- /* | |
- * Client must expect to get such events synchronously, and should | |
- * perform its locking accordingly. If it can't handle this, then | |
- * fail. | |
- */ | |
- if (flags & VMCI_FLAG_EVENT_DELAYED_CB) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- sub->runDelayed = FALSE; | |
- } else { | |
- sub->runDelayed = TRUE; | |
- } | |
- } else if (!VMCI_CanScheduleDelayedWork()) { | |
- /* | |
- * If the platform doesn't support delayed work callbacks then don't | |
- * allow registration for them. | |
- */ | |
- if (flags & VMCI_FLAG_EVENT_DELAYED_CB) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- sub->runDelayed = FALSE; | |
- } else { | |
- /* | |
- * The platform supports delayed work callbacks. Honor the requested | |
- * flags | |
- */ | |
- sub->runDelayed = (flags & VMCI_FLAG_EVENT_DELAYED_CB) ? TRUE : FALSE; | |
- } | |
- | |
- sub->refCount = 1; | |
- sub->event = event; | |
- sub->callback = callback; | |
- sub->callbackData = callbackData; | |
- VMCIList_InitEntry(&sub->subscriberListItem); | |
- | |
- VMCI_GrabLock_BH(&subscriberLock, &lockFlags); | |
- | |
- /* Check if creation of a new event is allowed. */ | |
- if (!VMCI_CanCreate()) { | |
- result = VMCI_ERROR_UNAVAILABLE; | |
- goto exit; | |
- } | |
- | |
- for (success = FALSE, attempts = 0; | |
- success == FALSE && attempts < VMCI_EVENT_MAX_ATTEMPTS; | |
- attempts++) { | |
- VMCISubscription *existingSub = NULL; | |
- | |
- /* | |
- * We try to get an id a couple of time before claiming we are out of | |
- * resources. | |
- */ | |
- sub->id = ++subscriptionID; | |
- | |
- /* Test for duplicate id. */ | |
- existingSub = VMCIEventFind(sub->id); | |
- if (existingSub == NULL) { | |
- /* We succeeded if we didn't find a duplicate. */ | |
- success = TRUE; | |
- } else { | |
- VMCIEventRelease(existingSub); | |
- } | |
- } | |
- | |
- if (success) { | |
- VMCI_CreateEvent(&sub->destroyEvent); | |
- VMCIList_Insert(&sub->subscriberListItem, &subscriberArray[event]); | |
- result = VMCI_SUCCESS; | |
- } else { | |
- result = VMCI_ERROR_NO_RESOURCES; | |
- } | |
- | |
-exit: | |
- VMCI_ReleaseLock_BH(&subscriberLock, lockFlags); | |
- return result; | |
-# undef VMCI_EVENT_MAX_ATTEMPTS | |
-} | |
- | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIEventUnregisterSubscription -- | |
- * | |
- * Remove subscription from subscriber list. | |
- * | |
- * Results: | |
- * VMCISubscription when found, NULL otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-static VMCISubscription * | |
-VMCIEventUnregisterSubscription(VMCIId subID) // IN | |
-{ | |
- VMCILockFlags flags; | |
- VMCISubscription *s; | |
- | |
- VMCI_GrabLock_BH(&subscriberLock, &flags); | |
- s = VMCIEventFind(subID); | |
- if (s != NULL) { | |
- VMCIEventRelease(s); | |
- VMCIList_Remove(&s->subscriberListItem); | |
- } | |
- VMCI_ReleaseLock_BH(&subscriberLock, flags); | |
- | |
- if (s != NULL) { | |
- VMCI_WaitOnEvent(&s->destroyEvent, EventReleaseCB, s); | |
- VMCI_DestroyEvent(&s->destroyEvent); | |
- } | |
- | |
- return s; | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * vmci_event_subscribe -- | |
- * | |
- * Subscribe to given event. The callback specified can be fired | |
- * in different contexts depending on what flag is specified while | |
- * registering. If flags contains VMCI_FLAG_EVENT_NONE then the | |
- * callback is fired with the subscriber lock held (and BH context | |
- * on the guest). If flags contain VMCI_FLAG_EVENT_DELAYED_CB then | |
- * the callback is fired with no locks held in thread context. | |
- * This is useful because other VMCIEvent functions can be called, | |
- * but it also increases the chances that an event will be dropped. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS on success, error code otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-VMCI_EXPORT_SYMBOL(vmci_event_subscribe) | |
-int | |
-vmci_event_subscribe(VMCI_Event event, // IN | |
-#if !defined(linux) || defined(VMKERNEL) | |
- uint32 flags, // IN | |
-#endif // !linux || VMKERNEL | |
- VMCI_EventCB callback, // IN | |
- void *callbackData, // IN | |
- VMCIId *subscriptionID) // OUT | |
-{ | |
- int retval; | |
-#if defined(linux) && !defined(VMKERNEL) | |
- uint32 flags = VMCI_FLAG_EVENT_NONE; | |
-#endif // linux && !VMKERNEL | |
- VMCISubscription *s = NULL; | |
- | |
- if (subscriptionID == NULL) { | |
- VMCI_DEBUG_LOG(4, (LGPFX"Invalid subscription (NULL).\n")); | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- s = VMCI_AllocKernelMem(sizeof *s, VMCI_MEMORY_NONPAGED); | |
- if (s == NULL) { | |
- return VMCI_ERROR_NO_MEM; | |
- } | |
- | |
- retval = VMCIEventRegisterSubscription(s, event, flags, | |
- callback, callbackData); | |
- if (retval < VMCI_SUCCESS) { | |
- VMCI_FreeKernelMem(s, sizeof *s); | |
- return retval; | |
- } | |
- | |
- *subscriptionID = s->id; | |
- return retval; | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * vmci_event_unsubscribe -- | |
- * | |
- * Unsubscribe to given event. Removes it from list and frees it. | |
- * Will return callbackData if requested by caller. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS on success, error code otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-VMCI_EXPORT_SYMBOL(vmci_event_unsubscribe) | |
-int | |
-vmci_event_unsubscribe(VMCIId subID) // IN | |
-{ | |
- VMCISubscription *s; | |
- | |
- /* | |
- * Return subscription. At this point we know noone else is accessing | |
- * the subscription so we can free it. | |
- */ | |
- s = VMCIEventUnregisterSubscription(subID); | |
- if (s == NULL) { | |
- return VMCI_ERROR_NOT_FOUND; | |
- | |
- } | |
- VMCI_FreeKernelMem(s, sizeof *s); | |
- | |
- return VMCI_SUCCESS; | |
-} | |
diff --git a/modules/linux/vmci/common/vmciEvent.h b/modules/linux/vmci/common/vmciEvent.h | |
deleted file mode 100644 | |
index 720b50d..0000000 | |
--- a/modules/linux/vmci/common/vmciEvent.h | |
+++ /dev/null | |
@@ -1,43 +0,0 @@ | |
-/********************************************************* | |
- * Copyright (C) 2007 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 as published by the | |
- * Free Software Foundation version 2 and no 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 | |
- * | |
- *********************************************************/ | |
- | |
-/* | |
- * vmciEvent.h -- | |
- * | |
- * Event code for the vmci guest driver | |
- */ | |
- | |
-#ifndef __VMCI_EVENT_H__ | |
-#define __VMCI_EVENT_H__ | |
- | |
-#define INCLUDE_ALLOW_MODULE | |
-#define INCLUDE_ALLOW_VMMON | |
-#define INCLUDE_ALLOW_VMCORE | |
-#define INCLUDE_ALLOW_VMKERNEL | |
-#include "includeCheck.h" | |
- | |
-#include "vmci_defs.h" | |
-#include "vmci_call_defs.h" | |
- | |
-int VMCIEvent_Init(void); | |
-void VMCIEvent_Exit(void); | |
-void VMCIEvent_Sync(void); | |
-int VMCIEvent_Dispatch(VMCIDatagram *msg); | |
-Bool VMCIEvent_CheckHostCapabilities(void); | |
- | |
-#endif //__VMCI_EVENT_H__ | |
diff --git a/modules/linux/vmci/common/vmciHashtable.c b/modules/linux/vmci/common/vmciHashtable.c | |
deleted file mode 100644 | |
index b7b577d..0000000 | |
--- a/modules/linux/vmci/common/vmciHashtable.c | |
+++ /dev/null | |
@@ -1,607 +0,0 @@ | |
-/********************************************************* | |
- * Copyright (C) 2006-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 as published by the | |
- * Free Software Foundation version 2 and no 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 | |
- * | |
- *********************************************************/ | |
- | |
-/* | |
- * vmciHashtable.c -- | |
- * | |
- * Implementation of the VMCI Hashtable. | |
- * TODO: Look into what is takes to use lib/misc/hashTable.c instead of | |
- * our own implementation. | |
- */ | |
- | |
-#include "vmci_kernel_if.h" | |
-#include "vm_assert.h" | |
-#include "vmci_defs.h" | |
-#include "vmci_infrastructure.h" | |
-#include "vmciCommonInt.h" | |
-#include "vmciDriver.h" | |
-#include "vmciHashtable.h" | |
-#if defined(VMKERNEL) | |
-# include "vmciVmkInt.h" | |
-# include "vm_libc.h" | |
-# include "helper_ext.h" | |
-#endif | |
- | |
-#define LGPFX "VMCIHashTable: " | |
- | |
-#define VMCI_HASHTABLE_HASH(_h, _sz) \ | |
- VMCI_HashId(VMCI_HANDLE_TO_RESOURCE_ID(_h), (_sz)) | |
- | |
-static int HashTableUnlinkEntry(VMCIHashTable *table, VMCIHashEntry *entry); | |
-static Bool VMCIHashTableEntryExistsLocked(VMCIHashTable *table, | |
- VMCIHandle handle); | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCIHashTable_Create -- | |
- * XXX Factor out the hashtable code to be shared amongst host and guest. | |
- * | |
- * Result: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-VMCIHashTable * | |
-VMCIHashTable_Create(int size) | |
-{ | |
- VMCIHashTable *table = VMCI_AllocKernelMem(sizeof *table, | |
- VMCI_MEMORY_NONPAGED); | |
- if (table == NULL) { | |
- return NULL; | |
- } | |
- | |
- table->entries = VMCI_AllocKernelMem(sizeof *table->entries * size, | |
- VMCI_MEMORY_NONPAGED); | |
- if (table->entries == NULL) { | |
- VMCI_FreeKernelMem(table, sizeof *table); | |
- return NULL; | |
- } | |
- memset(table->entries, 0, sizeof *table->entries * size); | |
- table->size = size; | |
- if (VMCI_InitLock(&table->lock, "VMCIHashTableLock", | |
- VMCI_LOCK_RANK_HASHTABLE) < VMCI_SUCCESS) { | |
- VMCI_FreeKernelMem(table->entries, sizeof *table->entries * size); | |
- VMCI_FreeKernelMem(table, sizeof *table); | |
- return NULL; | |
- } | |
- | |
- return table; | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCIHashTable_Destroy -- | |
- * This function should be called at module exit time. | |
- * We rely on the module ref count to insure that no one is accessing any | |
- * hash table entries at this point in time. Hence we should be able to just | |
- * remove all entries from the hash table. | |
- * | |
- * Result: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-void | |
-VMCIHashTable_Destroy(VMCIHashTable *table) | |
-{ | |
- VMCILockFlags flags; | |
-#if 0 | |
- DEBUG_ONLY(int i;) | |
- DEBUG_ONLY(int leakingEntries = 0;) | |
-#endif | |
- | |
- ASSERT(table); | |
- | |
- VMCI_GrabLock_BH(&table->lock, &flags); | |
-#if 0 | |
-#ifdef VMX86_DEBUG | |
- for (i = 0; i < table->size; i++) { | |
- VMCIHashEntry *head = table->entries[i]; | |
- while (head) { | |
- leakingEntries++; | |
- head = head->next; | |
- } | |
- } | |
- if (leakingEntries) { | |
- VMCI_WARNING((LGPFX"Leaking entries (%d) for hash table (%p).\n", | |
- leakingEntries, table)); | |
- } | |
-#endif // VMX86_DEBUG | |
-#endif | |
- VMCI_FreeKernelMem(table->entries, sizeof *table->entries * table->size); | |
- table->entries = NULL; | |
- VMCI_ReleaseLock_BH(&table->lock, flags); | |
- VMCI_CleanupLock(&table->lock); | |
- VMCI_FreeKernelMem(table, sizeof *table); | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCIHashTable_InitEntry -- | |
- * Initializes a hash entry; | |
- * | |
- * Result: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
-void | |
-VMCIHashTable_InitEntry(VMCIHashEntry *entry, // IN | |
- VMCIHandle handle) // IN | |
-{ | |
- ASSERT(entry); | |
- entry->handle = handle; | |
- entry->refCount = 0; | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCIHashTable_AddEntry -- | |
- * XXX Factor out the hashtable code to be shared amongst host and guest. | |
- * | |
- * Result: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-int | |
-VMCIHashTable_AddEntry(VMCIHashTable *table, // IN | |
- VMCIHashEntry *entry) // IN | |
-{ | |
- int idx; | |
- VMCILockFlags flags; | |
- | |
- ASSERT(entry); | |
- ASSERT(table); | |
- | |
- VMCI_GrabLock_BH(&table->lock, &flags); | |
- | |
- /* Check if creation of a new hashtable entry is allowed. */ | |
- if (!VMCI_CanCreate()) { | |
- VMCI_ReleaseLock_BH(&table->lock, flags); | |
- return VMCI_ERROR_UNAVAILABLE; | |
- } | |
- | |
- if (VMCIHashTableEntryExistsLocked(table, entry->handle)) { | |
- VMCI_DEBUG_LOG(4, (LGPFX"Entry (handle=0x%x:0x%x) already exists.\n", | |
- entry->handle.context, entry->handle.resource)); | |
- VMCI_ReleaseLock_BH(&table->lock, flags); | |
- return VMCI_ERROR_DUPLICATE_ENTRY; | |
- } | |
- | |
- idx = VMCI_HASHTABLE_HASH(entry->handle, table->size); | |
- ASSERT(idx < table->size); | |
- | |
- /* New entry is added to top/front of hash bucket. */ | |
- entry->refCount++; | |
- entry->next = table->entries[idx]; | |
- table->entries[idx] = entry; | |
- VMCI_ReleaseLock_BH(&table->lock, flags); | |
- | |
- return VMCI_SUCCESS; | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCIHashTable_RemoveEntry -- | |
- * XXX Factor out the hashtable code to shared amongst API and perhaps | |
- * host and guest. | |
- * | |
- * Result: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-int | |
-VMCIHashTable_RemoveEntry(VMCIHashTable *table, // IN | |
- VMCIHashEntry *entry) // IN | |
-{ | |
- int result; | |
- VMCILockFlags flags; | |
- | |
- ASSERT(table); | |
- ASSERT(entry); | |
- | |
- VMCI_GrabLock_BH(&table->lock, &flags); | |
- | |
- /* First unlink the entry. */ | |
- result = HashTableUnlinkEntry(table, entry); | |
- if (result != VMCI_SUCCESS) { | |
- /* We failed to find the entry. */ | |
- goto done; | |
- } | |
- | |
- /* Decrement refcount and check if this is last reference. */ | |
- entry->refCount--; | |
- if (entry->refCount == 0) { | |
- result = VMCI_SUCCESS_ENTRY_DEAD; | |
- goto done; | |
- } | |
- | |
- done: | |
- VMCI_ReleaseLock_BH(&table->lock, flags); | |
- | |
- return result; | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCIHashTableGetEntryLocked -- | |
- * | |
- * Looks up an entry in the hash table, that is already locked. | |
- * | |
- * Result: | |
- * If the element is found, a pointer to the element is returned. | |
- * Otherwise NULL is returned. | |
- * | |
- * Side effects: | |
- * The reference count of the returned element is increased. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-static VMCIHashEntry * | |
-VMCIHashTableGetEntryLocked(VMCIHashTable *table, // IN | |
- VMCIHandle handle) // IN | |
-{ | |
- VMCIHashEntry *cur = NULL; | |
- int idx; | |
- | |
- ASSERT(!VMCI_HANDLE_EQUAL(handle, VMCI_INVALID_HANDLE)); | |
- ASSERT(table); | |
- | |
- idx = VMCI_HASHTABLE_HASH(handle, table->size); | |
- | |
- cur = table->entries[idx]; | |
- while (TRUE) { | |
- if (cur == NULL) { | |
- break; | |
- } | |
- | |
- if (VMCI_HANDLE_TO_RESOURCE_ID(cur->handle) == | |
- VMCI_HANDLE_TO_RESOURCE_ID(handle)) { | |
- if ((VMCI_HANDLE_TO_CONTEXT_ID(cur->handle) == | |
- VMCI_HANDLE_TO_CONTEXT_ID(handle)) || | |
- (VMCI_INVALID_ID == VMCI_HANDLE_TO_CONTEXT_ID(cur->handle))) { | |
- cur->refCount++; | |
- break; | |
- } | |
- } | |
- cur = cur->next; | |
- } | |
- | |
- return cur; | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCIHashTable_GetEntry -- | |
- * XXX Factor out the hashtable code to shared amongst API and perhaps | |
- * host and guest. | |
- * | |
- * Result: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-VMCIHashEntry * | |
-VMCIHashTable_GetEntry(VMCIHashTable *table, // IN | |
- VMCIHandle handle) // IN | |
-{ | |
- VMCIHashEntry *entry; | |
- VMCILockFlags flags; | |
- | |
- if (VMCI_HANDLE_EQUAL(handle, VMCI_INVALID_HANDLE)) { | |
- return NULL; | |
- } | |
- | |
- ASSERT(table); | |
- | |
- VMCI_GrabLock_BH(&table->lock, &flags); | |
- entry = VMCIHashTableGetEntryLocked(table, handle); | |
- VMCI_ReleaseLock_BH(&table->lock, flags); | |
- | |
- return entry; | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCIHashTable_HoldEntry -- | |
- * | |
- * Hold the given entry. This will increment the entry's reference count. | |
- * This is like a GetEntry() but without having to lookup the entry by | |
- * handle. | |
- * | |
- * Result: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-void | |
-VMCIHashTable_HoldEntry(VMCIHashTable *table, // IN | |
- VMCIHashEntry *entry) // IN/OUT | |
-{ | |
- VMCILockFlags flags; | |
- | |
- ASSERT(table); | |
- ASSERT(entry); | |
- | |
- VMCI_GrabLock_BH(&table->lock, &flags); | |
- entry->refCount++; | |
- VMCI_ReleaseLock_BH(&table->lock, flags); | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCIHashTableReleaseEntryLocked -- | |
- * | |
- * Releases an element previously obtained with | |
- * VMCIHashTableGetEntryLocked. | |
- * | |
- * Result: | |
- * If the entry is removed from the hash table, VMCI_SUCCESS_ENTRY_DEAD | |
- * is returned. Otherwise, VMCI_SUCCESS is returned. | |
- * | |
- * Side effects: | |
- * The reference count of the entry is decreased and the entry is removed | |
- * from the hash table on 0. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-static int | |
-VMCIHashTableReleaseEntryLocked(VMCIHashTable *table, // IN | |
- VMCIHashEntry *entry) // IN | |
-{ | |
- int result = VMCI_SUCCESS; | |
- | |
- ASSERT(table); | |
- ASSERT(entry); | |
- | |
- entry->refCount--; | |
- /* Check if this is last reference and report if so. */ | |
- if (entry->refCount == 0) { | |
- | |
- /* | |
- * Remove entry from hash table if not already removed. This could have | |
- * happened already because VMCIHashTable_RemoveEntry was called to unlink | |
- * it. We ignore if it is not found. Datagram handles will often have | |
- * RemoveEntry called, whereas SharedMemory regions rely on ReleaseEntry | |
- * to unlink the entry, since the creator does not call RemoveEntry when | |
- * it detaches. | |
- */ | |
- | |
- HashTableUnlinkEntry(table, entry); | |
- result = VMCI_SUCCESS_ENTRY_DEAD; | |
- } | |
- | |
- return result; | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCIHashTable_ReleaseEntry -- | |
- * XXX Factor out the hashtable code to shared amongst API and perhaps | |
- * host and guest. | |
- * | |
- * Result: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-int | |
-VMCIHashTable_ReleaseEntry(VMCIHashTable *table, // IN | |
- VMCIHashEntry *entry) // IN | |
-{ | |
- VMCILockFlags flags; | |
- int result; | |
- | |
- ASSERT(table); | |
- VMCI_GrabLock_BH(&table->lock, &flags); | |
- result = VMCIHashTableReleaseEntryLocked(table, entry); | |
- VMCI_ReleaseLock_BH(&table->lock, flags); | |
- | |
- return result; | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCIHashTable_EntryExists -- | |
- * XXX Factor out the hashtable code to shared amongst API and perhaps | |
- * host and guest. | |
- * | |
- * Result: | |
- * TRUE if handle already in hashtable. FALSE otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-Bool | |
-VMCIHashTable_EntryExists(VMCIHashTable *table, // IN | |
- VMCIHandle handle) // IN | |
-{ | |
- Bool exists; | |
- VMCILockFlags flags; | |
- | |
- ASSERT(table); | |
- | |
- VMCI_GrabLock_BH(&table->lock, &flags); | |
- exists = VMCIHashTableEntryExistsLocked(table, handle); | |
- VMCI_ReleaseLock_BH(&table->lock, flags); | |
- | |
- return exists; | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCIHashTableEntryExistsLocked -- | |
- * | |
- * Unlocked version of VMCIHashTable_EntryExists. | |
- * | |
- * Result: | |
- * TRUE if handle already in hashtable. FALSE otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-static Bool | |
-VMCIHashTableEntryExistsLocked(VMCIHashTable *table, // IN | |
- VMCIHandle handle) // IN | |
- | |
-{ | |
- VMCIHashEntry *entry; | |
- int idx; | |
- | |
- ASSERT(table); | |
- | |
- idx = VMCI_HASHTABLE_HASH(handle, table->size); | |
- | |
- entry = table->entries[idx]; | |
- while (entry) { | |
- if (VMCI_HANDLE_TO_RESOURCE_ID(entry->handle) == | |
- VMCI_HANDLE_TO_RESOURCE_ID(handle)) { | |
- if ((VMCI_HANDLE_TO_CONTEXT_ID(entry->handle) == | |
- VMCI_HANDLE_TO_CONTEXT_ID(handle)) || | |
- (VMCI_INVALID_ID == VMCI_HANDLE_TO_CONTEXT_ID(handle)) || | |
- (VMCI_INVALID_ID == VMCI_HANDLE_TO_CONTEXT_ID(entry->handle))) { | |
- return TRUE; | |
- } | |
- } | |
- entry = entry->next; | |
- } | |
- | |
- return FALSE; | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * HashTableUnlinkEntry -- | |
- * XXX Factor out the hashtable code to shared amongst API and perhaps | |
- * host and guest. | |
- * Assumes caller holds table lock. | |
- * | |
- * Result: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-static int | |
-HashTableUnlinkEntry(VMCIHashTable *table, // IN | |
- VMCIHashEntry *entry) // IN | |
-{ | |
- int result; | |
- VMCIHashEntry *prev, *cur; | |
- int idx; | |
- | |
- idx = VMCI_HASHTABLE_HASH(entry->handle, table->size); | |
- | |
- prev = NULL; | |
- cur = table->entries[idx]; | |
- while (TRUE) { | |
- if (cur == NULL) { | |
- result = VMCI_ERROR_NOT_FOUND; | |
- break; | |
- } | |
- if (VMCI_HANDLE_EQUAL(cur->handle, entry->handle)) { | |
- ASSERT(cur == entry); | |
- | |
- /* Remove entry and break. */ | |
- if (prev) { | |
- prev->next = cur->next; | |
- } else { | |
- table->entries[idx] = cur->next; | |
- } | |
- cur->next = NULL; | |
- result = VMCI_SUCCESS; | |
- break; | |
- } | |
- prev = cur; | |
- cur = cur->next; | |
- } | |
- return result; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIHashTable_Sync -- | |
- * | |
- * Use this as a synchronization point when setting globals, for example, | |
- * during device shutdown. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCIHashTable_Sync(VMCIHashTable *table) | |
-{ | |
- VMCILockFlags flags; | |
- ASSERT(table); | |
- VMCI_GrabLock_BH(&table->lock, &flags); | |
- VMCI_ReleaseLock_BH(&table->lock, flags); | |
-} | |
diff --git a/modules/linux/vmci/common/vmciHashtable.h b/modules/linux/vmci/common/vmciHashtable.h | |
deleted file mode 100644 | |
index a786818..0000000 | |
--- a/modules/linux/vmci/common/vmciHashtable.h | |
+++ /dev/null | |
@@ -1,60 +0,0 @@ | |
-/********************************************************* | |
- * Copyright (C) 2006 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 as published by the | |
- * Free Software Foundation version 2 and no 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 | |
- * | |
- *********************************************************/ | |
- | |
-/* | |
- * vmciHashtable.h -- | |
- * | |
- * Hash table for use in the APIs. | |
- */ | |
- | |
-#ifndef _VMCI_HASHTABLE_H_ | |
-#define _VMCI_HASHTABLE_H_ | |
- | |
-#define INCLUDE_ALLOW_MODULE | |
-#define INCLUDE_ALLOW_VMMON | |
-#define INCLUDE_ALLOW_VMCORE | |
-#define INCLUDE_ALLOW_VMKERNEL | |
-#include "includeCheck.h" | |
- | |
-#include "vmci_kernel_if.h" | |
-#include "vmci_defs.h" | |
- | |
-typedef struct VMCIHashEntry { | |
- VMCIHandle handle; | |
- int refCount; | |
- struct VMCIHashEntry *next; | |
-} VMCIHashEntry; | |
- | |
-typedef struct VMCIHashTable { | |
- VMCIHashEntry **entries; | |
- int size; // Number of buckets in above array. | |
- VMCILock lock; | |
-} VMCIHashTable; | |
- | |
-VMCIHashTable *VMCIHashTable_Create(int size); | |
-void VMCIHashTable_Destroy(VMCIHashTable *table); | |
-void VMCIHashTable_InitEntry(VMCIHashEntry *entry, VMCIHandle handle); | |
-int VMCIHashTable_AddEntry(VMCIHashTable *table, VMCIHashEntry *entry); | |
-int VMCIHashTable_RemoveEntry(VMCIHashTable *table, VMCIHashEntry *entry); | |
-VMCIHashEntry *VMCIHashTable_GetEntry(VMCIHashTable *table, VMCIHandle handle); | |
-void VMCIHashTable_HoldEntry(VMCIHashTable *table, VMCIHashEntry *entry); | |
-int VMCIHashTable_ReleaseEntry(VMCIHashTable *table, VMCIHashEntry *entry); | |
-Bool VMCIHashTable_EntryExists(VMCIHashTable *table, VMCIHandle handle); | |
-void VMCIHashTable_Sync(VMCIHashTable *table); | |
- | |
-#endif // _VMCI_HASHTABLE_H_ | |
diff --git a/modules/linux/vmci/common/vmciQPair.c b/modules/linux/vmci/common/vmciQPair.c | |
deleted file mode 100644 | |
index 5c5253f..0000000 | |
--- a/modules/linux/vmci/common/vmciQPair.c | |
+++ /dev/null | |
@@ -1,1424 +0,0 @@ | |
-/********************************************************* | |
- * 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 as published by the | |
- * Free Software Foundation version 2 and no 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 | |
- * | |
- *********************************************************/ | |
- | |
-/* | |
- * vmciQPair.c -- | |
- * | |
- * This file implements Queue accessor methods. | |
- * | |
- * VMCIQPair is a new interface that hides the queue pair internals. | |
- * Rather than access each queue in a pair directly, operations are now | |
- * performed on the queue as a whole. This is simpler and less | |
- * error-prone, and allows for future queue pair features to be added | |
- * under the hood with no change to the client code. | |
- * | |
- * This also helps in a particular case on Windows hosts, where the memory | |
- * allocated by the client (e.g., VMX) will disappear when the client does | |
- * (e.g., abnormal termination). The kernel can't lock user memory into | |
- * its address space indefinitely. By guarding access to the queue | |
- * contents we can correctly handle the case where the client disappears. | |
- * | |
- * On code style: | |
- * | |
- * + This entire file started its life as a cut-and-paste of the | |
- * static INLINE functions in bora/public/vmci_queue_pair.h. | |
- * From there, new copies of the routines were made named | |
- * without the prefix VMCI, without the underscore (the one | |
- * that followed VMCIQueue_). The no-underscore versions of | |
- * the routines require that the mutexes are held. | |
- * | |
- * + The code -always- uses the xyzLocked() version of any given | |
- * routine even when the wrapped function is a one-liner. The | |
- * reason for this decision was to ensure that we didn't have | |
- * copies of logic lying around that needed to be maintained. | |
- * | |
- * + Note that we still pass around 'const VMCIQueue *'s. | |
- * | |
- * + Note that mutex is a field within VMCIQueue. We skirt the | |
- * issue of passing around a const VMCIQueue, even though the | |
- * mutex field (__mutex, specifically) will get modified by not | |
- * ever referring to the mutex -itself- except during | |
- * initialization. Beyond that, the code only passes the | |
- * pointer to the mutex, which is also a member of VMCIQueue, | |
- * obviously, and which doesn't change after initialization. | |
- * This eliminates having to redefine all the functions that | |
- * are currently taking const VMCIQueue's so that these | |
- * functions are compatible with those definitions. | |
- */ | |
- | |
-#include "vmci_kernel_if.h" | |
-#include "vm_assert.h" | |
-#include "vmci_handle_array.h" | |
-#include "vmci_defs.h" | |
-#include "vmciKernelAPI.h" | |
-#include "vmciQueue.h" | |
-#include "vmciQueuePair.h" | |
-#include "vmciRoute.h" | |
- | |
- | |
-/* | |
- * VMCIQPair | |
- * | |
- * This structure is opaque to the clients. | |
- */ | |
- | |
-struct VMCIQPair { | |
- VMCIHandle handle; | |
- VMCIQueue *produceQ; | |
- VMCIQueue *consumeQ; | |
- uint64 produceQSize; | |
- uint64 consumeQSize; | |
- VMCIId peer; | |
- uint32 flags; | |
- VMCIPrivilegeFlags privFlags; | |
- Bool guestEndpoint; | |
- uint32 blocked; | |
- VMCIEvent event; | |
-}; | |
- | |
-static int VMCIQPairMapQueueHeaders(VMCIQueue *produceQ, VMCIQueue *consumeQ, | |
- Bool canBlock); | |
-static int VMCIQPairGetQueueHeaders(const VMCIQPair *qpair, | |
- VMCIQueueHeader **produceQHeader, | |
- VMCIQueueHeader **consumeQHeader); | |
-static int VMCIQPairWakeupCB(void *clientData); | |
-static int VMCIQPairReleaseMutexCB(void *clientData); | |
-static Bool VMCIQPairWaitForReadyQueue(VMCIQPair *qpair); | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIQPairLock -- | |
- * | |
- * Helper routine that will lock the QPair before subsequent operations. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS if lock acquired. VMCI_ERROR_WOULD_BLOCK if queue mutex | |
- * couldn't be acquired and qpair isn't allowed to block. | |
- * | |
- * Side effects: | |
- * May block. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static INLINE int | |
-VMCIQPairLock(const VMCIQPair *qpair) // IN | |
-{ | |
-#if !defined VMX86_VMX | |
- if (qpair->flags & VMCI_QPFLAG_PINNED) { | |
- VMCI_LockQueueHeader(qpair->produceQ); | |
- return VMCI_SUCCESS; | |
- } | |
- return VMCI_AcquireQueueMutex(qpair->produceQ, | |
- !(qpair->flags & VMCI_QPFLAG_NONBLOCK)); | |
-#else | |
- return VMCI_SUCCESS | |
-#endif | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIQPairUnlock -- | |
- * | |
- * Helper routine that will unlock the QPair after various operations. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static INLINE void | |
-VMCIQPairUnlock(const VMCIQPair *qpair) // IN | |
-{ | |
-#if !defined VMX86_VMX | |
- if (qpair->flags & VMCI_QPFLAG_PINNED) { | |
- VMCI_UnlockQueueHeader(qpair->produceQ); | |
- } else { | |
- VMCI_ReleaseQueueMutex(qpair->produceQ); | |
- } | |
-#endif | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIQPairHeaderLock -- | |
- * | |
- * Helper routine that will lock the queue pair header before subsequent | |
- * operations. If the queue pair is non blocking, a spinlock will be used. | |
- * Otherwise, a regular mutex locking the complete queue pair will be used. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * May block. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static INLINE void | |
-VMCIQPairLockHeader(const VMCIQPair *qpair) // IN | |
-{ | |
-#if !defined VMX86_VMX | |
- if (qpair->flags & VMCI_QPFLAG_NONBLOCK) { | |
- VMCI_LockQueueHeader(qpair->produceQ); | |
- } else { | |
- (void)VMCI_AcquireQueueMutex(qpair->produceQ, | |
- !(qpair->flags & VMCI_QPFLAG_NONBLOCK)); | |
- } | |
-#endif | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIQPairUnlockHeader -- | |
- * | |
- * Helper routine that unlocks the queue pair header after calling | |
- * VMCIQPairHeaderLock. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static INLINE void | |
-VMCIQPairUnlockHeader(const VMCIQPair *qpair) // IN | |
-{ | |
-#if !defined VMX86_VMX | |
- if (qpair->flags & VMCI_QPFLAG_NONBLOCK) { | |
- VMCI_UnlockQueueHeader(qpair->produceQ); | |
- } else { | |
- VMCI_ReleaseQueueMutex(qpair->produceQ); | |
- } | |
-#endif | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIQPairGetQueueHeaders -- | |
- * | |
- * Helper routine that will retrieve the produce and consume | |
- * headers of a given queue pair. If the guest memory of the | |
- * queue pair is currently not available, the saved queue headers | |
- * will be returned, if these are available. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS if either current or saved queue headers are found. | |
- * Appropriate error code otherwise. | |
- * | |
- * Side effects: | |
- * May block. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static int | |
-VMCIQPairGetQueueHeaders(const VMCIQPair *qpair, // IN | |
- VMCIQueueHeader **produceQHeader, // OUT | |
- VMCIQueueHeader **consumeQHeader) // OUT | |
-{ | |
- int result; | |
- | |
- result = VMCIQPairMapQueueHeaders(qpair->produceQ, qpair->consumeQ, | |
- !(qpair->flags & VMCI_QPFLAG_NONBLOCK)); | |
- if (result == VMCI_SUCCESS) { | |
- *produceQHeader = qpair->produceQ->qHeader; | |
- *consumeQHeader = qpair->consumeQ->qHeader; | |
- } else if (qpair->produceQ->savedHeader && qpair->consumeQ->savedHeader) { | |
- ASSERT(!qpair->guestEndpoint); | |
- *produceQHeader = qpair->produceQ->savedHeader; | |
- *consumeQHeader = qpair->consumeQ->savedHeader; | |
- result = VMCI_SUCCESS; | |
- } | |
- | |
- return result; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIQPairMapQueueHeaders -- | |
- * | |
- * The queue headers may not be mapped at all times. If a queue is | |
- * currently not mapped, it will be attempted to do so. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS if queues were validated, appropriate error code otherwise. | |
- * | |
- * Side effects: | |
- * May attempt to map in guest memory. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static int | |
-VMCIQPairMapQueueHeaders(VMCIQueue *produceQ, // IN | |
- VMCIQueue *consumeQ, // IN | |
- Bool canBlock) // IN | |
-{ | |
- int result; | |
- | |
- if (NULL == produceQ->qHeader || NULL == consumeQ->qHeader) { | |
- if (canBlock) { | |
- /* | |
- * We return data from creator of the queue in VM2VM case. | |
- * That should be OK, as if they do not match then there | |
- * is somebody else in progress of making them match, and | |
- * you should not be looking at somebody else's queue if | |
- * queue is active. | |
- */ | |
- result = VMCIHost_MapQueues(0, produceQ, consumeQ, 0); | |
- } else { | |
- result = VMCI_ERROR_QUEUEPAIR_NOT_READY; | |
- } | |
- if (result < VMCI_SUCCESS) { | |
- if (produceQ->savedHeader && consumeQ->savedHeader) { | |
- return VMCI_ERROR_QUEUEPAIR_NOT_READY; | |
- } else { | |
- return VMCI_ERROR_QUEUEPAIR_NOTATTACHED; | |
- } | |
- } | |
- } | |
- | |
- return VMCI_SUCCESS; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIQPairWakeupCB -- | |
- * | |
- * Callback from VMCI queue pair broker indicating that a queue | |
- * pair that was previously not ready, now either is ready or | |
- * gone forever. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS always. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static int | |
-VMCIQPairWakeupCB(void *clientData) | |
-{ | |
- VMCIQPair *qpair = (VMCIQPair *)clientData; | |
- ASSERT(qpair); | |
- | |
- VMCIQPairLock(qpair); | |
- while (qpair->blocked > 0) { | |
- qpair->blocked--; | |
- VMCI_SignalEvent(&qpair->event); | |
- } | |
- VMCIQPairUnlock(qpair); | |
- | |
- return VMCI_SUCCESS; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIQPairReleaseMutexCB -- | |
- * | |
- * Callback from VMCI_WaitOnEvent releasing the queue pair mutex | |
- * protecting the queue pair header state. | |
- * | |
- * Results: | |
- * 0 always. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static int | |
-VMCIQPairReleaseMutexCB(void *clientData) | |
-{ | |
- VMCIQPair *qpair = (VMCIQPair *)clientData; | |
- ASSERT(qpair); | |
- VMCIQPairUnlock(qpair); | |
- return 0; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIQPairWaitForReadyQueue -- | |
- * | |
- * Makes the calling thread wait for the queue pair to become | |
- * ready for host side access. | |
- * | |
- * Results: | |
- * TRUE when thread is woken up after queue pair state change. | |
- * FALSE otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static Bool | |
-VMCIQPairWaitForReadyQueue(VMCIQPair *qpair) | |
-{ | |
- if (UNLIKELY(qpair->guestEndpoint)) { | |
- ASSERT(FALSE); | |
- return FALSE; | |
- } | |
- if (qpair->flags & VMCI_QPFLAG_NONBLOCK) { | |
- return FALSE; | |
- } | |
- qpair->blocked++; | |
- VMCI_WaitOnEvent(&qpair->event, VMCIQPairReleaseMutexCB, qpair); | |
- VMCIQPairLock(qpair); | |
- return TRUE; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * vmci_qpair_alloc -- | |
- * | |
- * This is the client interface for allocating the memory for a | |
- * VMCIQPair structure and then attaching to the underlying | |
- * queue. If an error occurs allocating the memory for the | |
- * VMCIQPair structure, no attempt is made to attach. If an | |
- * error occurs attaching, then there's the VMCIQPair structure | |
- * is freed. | |
- * | |
- * Results: | |
- * An err, if < 0. | |
- * | |
- * Side effects: | |
- * Windows blocking call. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-VMCI_EXPORT_SYMBOL(vmci_qpair_alloc) | |
-int | |
-vmci_qpair_alloc(VMCIQPair **qpair, // OUT | |
- VMCIHandle *handle, // OUT | |
- uint64 produceQSize, // IN | |
- uint64 consumeQSize, // IN | |
- VMCIId peer, // IN | |
- uint32 flags, // IN | |
- VMCIPrivilegeFlags privFlags) // IN | |
-{ | |
- VMCIQPair *myQPair; | |
- int retval; | |
- VMCIHandle src = VMCI_INVALID_HANDLE; | |
- VMCIHandle dst = VMCI_MAKE_HANDLE(peer, VMCI_INVALID_ID); | |
- VMCIRoute route; | |
- VMCIEventReleaseCB wakeupCB; | |
- void *clientData; | |
- | |
- /* | |
- * Restrict the size of a queuepair. The device already enforces a limit | |
- * on the total amount of memory that can be allocated to queuepairs for a | |
- * guest. However, we try to allocate this memory before we make the | |
- * queuepair allocation hypercall. On Windows and Mac OS, we request a | |
- * single, continguous block, and it will fail if the OS cannot satisfy the | |
- * request. On Linux, we allocate each page separately, which means rather | |
- * than fail, the guest will thrash while it tries to allocate, and will | |
- * become increasingly unresponsive to the point where it appears to be hung. | |
- * So we place a limit on the size of an individual queuepair here, and | |
- * leave the device to enforce the restriction on total queuepair memory. | |
- * (Note that this doesn't prevent all cases; a user with only this much | |
- * physical memory could still get into trouble.) The error used by the | |
- * device is NO_RESOURCES, so use that here too. | |
- */ | |
- | |
- if (produceQSize + consumeQSize < MAX(produceQSize, consumeQSize) || | |
- produceQSize + consumeQSize > VMCI_MAX_GUEST_QP_MEMORY) { | |
- return VMCI_ERROR_NO_RESOURCES; | |
- } | |
- | |
- retval = VMCI_Route(&src, &dst, FALSE, &route); | |
- if (retval < VMCI_SUCCESS) { | |
- if (VMCI_GuestPersonalityActive()) { | |
- route = VMCI_ROUTE_AS_GUEST; | |
- } else { | |
- route = VMCI_ROUTE_AS_HOST; | |
- } | |
- } | |
- | |
- if ((flags & (VMCI_QPFLAG_NONBLOCK | VMCI_QPFLAG_PINNED)) && !vmkernel) { | |
-#if defined(linux) | |
- if (VMCI_ROUTE_AS_GUEST != route) | |
-#endif // linux | |
- { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- } | |
- | |
- if (flags & VMCI_QPFLAG_PINNED) { | |
- /* | |
- * Pinned pages implies non-blocking mode. Technically it doesn't | |
- * have to, but there doesn't seem much point in pinning the pages if you | |
- * can block since the queues will be small, so there's no performance | |
- * gain to be had. | |
- */ | |
- | |
- if (!(flags & VMCI_QPFLAG_NONBLOCK)) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- /* Limit the amount of memory that can be pinned. */ | |
- | |
- if (produceQSize + consumeQSize > VMCI_MAX_PINNED_QP_MEMORY) { | |
- return VMCI_ERROR_NO_RESOURCES; | |
- } | |
- } | |
- | |
- myQPair = VMCI_AllocKernelMem(sizeof *myQPair, VMCI_MEMORY_NONPAGED); | |
- if (!myQPair) { | |
- return VMCI_ERROR_NO_MEM; | |
- } | |
- | |
- myQPair->produceQSize = produceQSize; | |
- myQPair->consumeQSize = consumeQSize; | |
- myQPair->peer = peer; | |
- myQPair->flags = flags; | |
- myQPair->privFlags = privFlags; | |
- | |
- wakeupCB = clientData = NULL; | |
- if (VMCI_ROUTE_AS_HOST == route) { | |
- myQPair->guestEndpoint = FALSE; | |
- if (!(flags & VMCI_QPFLAG_LOCAL)) { | |
- myQPair->blocked = 0; | |
- VMCI_CreateEvent(&myQPair->event); | |
- wakeupCB = VMCIQPairWakeupCB; | |
- clientData = (void *)myQPair; | |
- } | |
- } else { | |
- myQPair->guestEndpoint = TRUE; | |
- } | |
- | |
- retval = VMCIQueuePair_Alloc(handle, | |
- &myQPair->produceQ, | |
- myQPair->produceQSize, | |
- &myQPair->consumeQ, | |
- myQPair->consumeQSize, | |
- myQPair->peer, | |
- myQPair->flags, | |
- myQPair->privFlags, | |
- myQPair->guestEndpoint, | |
- wakeupCB, | |
- clientData); | |
- | |
- if (retval < VMCI_SUCCESS) { | |
- if (VMCI_ROUTE_AS_HOST == route && !(flags & VMCI_QPFLAG_LOCAL)) { | |
- VMCI_DestroyEvent(&myQPair->event); | |
- } | |
- VMCI_FreeKernelMem(myQPair, sizeof *myQPair); | |
- return retval; | |
- } | |
- | |
- *qpair = myQPair; | |
- myQPair->handle = *handle; | |
- | |
- return retval; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * vmci_qpair_detach -- | |
- * | |
- * This is the client interface for detaching from a VMCIQPair. | |
- * Note that this routine will free the memory allocated for the | |
- * VMCIQPair structure, too. | |
- * | |
- * Results: | |
- * An error, if < 0. | |
- * | |
- * Side effects: | |
- * Will clear the caller's pointer to the VMCIQPair structure. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-VMCI_EXPORT_SYMBOL(vmci_qpair_detach) | |
-int | |
-vmci_qpair_detach(VMCIQPair **qpair) // IN/OUT | |
-{ | |
- int result; | |
- VMCIQPair *oldQPair; | |
- | |
- if (!qpair || !(*qpair)) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- oldQPair = *qpair; | |
- result = VMCIQueuePair_Detach(oldQPair->handle, oldQPair->guestEndpoint); | |
- | |
- /* | |
- * The guest can fail to detach for a number of reasons, and if it does so, | |
- * it will cleanup the entry (if there is one). The host can fail too, but | |
- * it won't cleanup the entry immediately, it will do that later when the | |
- * context is freed. Either way, we need to release the qpair struct here; | |
- * there isn't much the caller can do, and we don't want to leak. | |
- */ | |
- | |
- if (!(oldQPair->guestEndpoint || (oldQPair->flags & VMCI_QPFLAG_LOCAL))) { | |
- VMCI_DestroyEvent(&oldQPair->event); | |
- } | |
- VMCI_FreeKernelMem(oldQPair, sizeof *oldQPair); | |
- *qpair = NULL; | |
- | |
- return result; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * vmci_qpair_get_produce_indexes -- | |
- * | |
- * This is the client interface for getting the current indexes of the | |
- * QPair from the point of the view of the caller as the producer. | |
- * | |
- * Results: | |
- * err, if < 0 | |
- * Success otherwise. | |
- * | |
- * Side effects: | |
- * Windows blocking call. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-VMCI_EXPORT_SYMBOL(vmci_qpair_get_produce_indexes) | |
-int | |
-vmci_qpair_get_produce_indexes(const VMCIQPair *qpair, // IN | |
- uint64 *producerTail, // OUT | |
- uint64 *consumerHead) // OUT | |
-{ | |
- VMCIQueueHeader *produceQHeader; | |
- VMCIQueueHeader *consumeQHeader; | |
- int result; | |
- | |
- if (!qpair) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- VMCIQPairLockHeader(qpair); | |
- result = VMCIQPairGetQueueHeaders(qpair, &produceQHeader, &consumeQHeader); | |
- if (result == VMCI_SUCCESS) { | |
- VMCIQueueHeader_GetPointers(produceQHeader, consumeQHeader, | |
- producerTail, consumerHead); | |
- } | |
- VMCIQPairUnlockHeader(qpair); | |
- | |
- if (result == VMCI_SUCCESS && | |
- ((producerTail && *producerTail >= qpair->produceQSize) || | |
- (consumerHead && *consumerHead >= qpair->produceQSize))) { | |
- return VMCI_ERROR_INVALID_SIZE; | |
- } | |
- | |
- return result; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * vmci_qpair_get_consume_indexes -- | |
- * | |
- * This is the client interface for getting the current indexes of the | |
- * QPair from the point of the view of the caller as the consumer. | |
- * | |
- * Results: | |
- * err, if < 0 | |
- * Success otherwise. | |
- * | |
- * Side effects: | |
- * Windows blocking call. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-VMCI_EXPORT_SYMBOL(vmci_qpair_get_consume_indexes) | |
-int | |
-vmci_qpair_get_consume_indexes(const VMCIQPair *qpair, // IN | |
- uint64 *consumerTail, // OUT | |
- uint64 *producerHead) // OUT | |
-{ | |
- VMCIQueueHeader *produceQHeader; | |
- VMCIQueueHeader *consumeQHeader; | |
- int result; | |
- | |
- if (!qpair) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- VMCIQPairLockHeader(qpair); | |
- result = VMCIQPairGetQueueHeaders(qpair, &produceQHeader, &consumeQHeader); | |
- if (result == VMCI_SUCCESS) { | |
- VMCIQueueHeader_GetPointers(consumeQHeader, produceQHeader, | |
- consumerTail, producerHead); | |
- } | |
- VMCIQPairUnlockHeader(qpair); | |
- | |
- if (result == VMCI_SUCCESS && | |
- ((consumerTail && *consumerTail >= qpair->consumeQSize) || | |
- (producerHead && *producerHead >= qpair->consumeQSize))) { | |
- return VMCI_ERROR_INVALID_SIZE; | |
- } | |
- | |
- return result; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * vmci_qpair_produce_free_space -- | |
- * | |
- * This is the client interface for getting the amount of free | |
- * space in the QPair from the point of the view of the caller as | |
- * the producer which is the common case. | |
- * | |
- * Results: | |
- * Err, if < 0. | |
- * Full queue if = 0. | |
- * Number of available bytes into which data can be enqueued if > 0. | |
- * | |
- * Side effects: | |
- * Windows blocking call. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-VMCI_EXPORT_SYMBOL(vmci_qpair_produce_free_space) | |
-int64 | |
-vmci_qpair_produce_free_space(const VMCIQPair *qpair) // IN | |
-{ | |
- VMCIQueueHeader *produceQHeader; | |
- VMCIQueueHeader *consumeQHeader; | |
- int64 result; | |
- | |
- if (!qpair) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- VMCIQPairLockHeader(qpair); | |
- result = VMCIQPairGetQueueHeaders(qpair, &produceQHeader, &consumeQHeader); | |
- if (result == VMCI_SUCCESS) { | |
- result = VMCIQueueHeader_FreeSpace(produceQHeader, consumeQHeader, | |
- qpair->produceQSize); | |
- } else { | |
- result = 0; | |
- } | |
- VMCIQPairUnlockHeader(qpair); | |
- | |
- return result; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * vmci_qpair_consume_free_space -- | |
- * | |
- * This is the client interface for getting the amount of free | |
- * space in the QPair from the point of the view of the caller as | |
- * the consumer which is not the common case (see | |
- * VMCIQPair_ProduceFreeSpace(), above). | |
- * | |
- * Results: | |
- * Err, if < 0. | |
- * Full queue if = 0. | |
- * Number of available bytes into which data can be enqueued if > 0. | |
- * | |
- * Side effects: | |
- * Windows blocking call. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-VMCI_EXPORT_SYMBOL(vmci_qpair_consume_free_space) | |
-int64 | |
-vmci_qpair_consume_free_space(const VMCIQPair *qpair) // IN | |
-{ | |
- VMCIQueueHeader *produceQHeader; | |
- VMCIQueueHeader *consumeQHeader; | |
- int64 result; | |
- | |
- if (!qpair) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- VMCIQPairLockHeader(qpair); | |
- result = VMCIQPairGetQueueHeaders(qpair, &produceQHeader, &consumeQHeader); | |
- if (result == VMCI_SUCCESS) { | |
- result = VMCIQueueHeader_FreeSpace(consumeQHeader, produceQHeader, | |
- qpair->consumeQSize); | |
- } else { | |
- result = 0; | |
- } | |
- VMCIQPairUnlockHeader(qpair); | |
- | |
- return result; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * vmci_qpair_produce_buf_ready -- | |
- * | |
- * This is the client interface for getting the amount of | |
- * enqueued data in the QPair from the point of the view of the | |
- * caller as the producer which is not the common case (see | |
- * VMCIQPair_ConsumeBufReady(), above). | |
- * | |
- * Results: | |
- * Err, if < 0. | |
- * Empty queue if = 0. | |
- * Number of bytes ready to be dequeued if > 0. | |
- * | |
- * Side effects: | |
- * Windows blocking call. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-VMCI_EXPORT_SYMBOL(vmci_qpair_produce_buf_ready) | |
-int64 | |
-vmci_qpair_produce_buf_ready(const VMCIQPair *qpair) // IN | |
-{ | |
- VMCIQueueHeader *produceQHeader; | |
- VMCIQueueHeader *consumeQHeader; | |
- int64 result; | |
- | |
- if (!qpair) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- VMCIQPairLockHeader(qpair); | |
- result = VMCIQPairGetQueueHeaders(qpair, &produceQHeader, &consumeQHeader); | |
- if (result == VMCI_SUCCESS) { | |
- result = VMCIQueueHeader_BufReady(produceQHeader, consumeQHeader, | |
- qpair->produceQSize); | |
- } else { | |
- result = 0; | |
- } | |
- VMCIQPairUnlockHeader(qpair); | |
- | |
- return result; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * vmci_qpair_consume_buf_ready -- | |
- * | |
- * This is the client interface for getting the amount of | |
- * enqueued data in the QPair from the point of the view of the | |
- * caller as the consumer which is the normal case. | |
- * | |
- * Results: | |
- * Err, if < 0. | |
- * Empty queue if = 0. | |
- * Number of bytes ready to be dequeued if > 0. | |
- * | |
- * Side effects: | |
- * Windows blocking call. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-VMCI_EXPORT_SYMBOL(vmci_qpair_consume_buf_ready) | |
-int64 | |
-vmci_qpair_consume_buf_ready(const VMCIQPair *qpair) // IN | |
-{ | |
- VMCIQueueHeader *produceQHeader; | |
- VMCIQueueHeader *consumeQHeader; | |
- int64 result; | |
- | |
- if (!qpair) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- VMCIQPairLockHeader(qpair); | |
- result = VMCIQPairGetQueueHeaders(qpair, &produceQHeader, &consumeQHeader); | |
- if (result == VMCI_SUCCESS) { | |
- result = VMCIQueueHeader_BufReady(consumeQHeader, produceQHeader, | |
- qpair->consumeQSize); | |
- } else { | |
- result = 0; | |
- } | |
- VMCIQPairUnlockHeader(qpair); | |
- | |
- return result; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * EnqueueLocked -- | |
- * | |
- * Enqueues a given buffer to the produce queue using the provided | |
- * function. As many bytes as possible (space available in the queue) | |
- * are enqueued. | |
- * | |
- * Assumes the queue->mutex has been acquired. | |
- * | |
- * Results: | |
- * VMCI_ERROR_QUEUEPAIR_NOSPACE if no space was available to enqueue data. | |
- * VMCI_ERROR_INVALID_SIZE, if any queue pointer is outside the queue | |
- * (as defined by the queue size). | |
- * VMCI_ERROR_INVALID_ARGS, if an error occured when accessing the buffer. | |
- * VMCI_ERROR_QUEUEPAIR_NOTATTACHED, if the queue pair pages aren't | |
- * available. | |
- * Otherwise, the number of bytes written to the queue is returned. | |
- * | |
- * Side effects: | |
- * Updates the tail pointer of the produce queue. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static ssize_t | |
-EnqueueLocked(VMCIQueue *produceQ, // IN | |
- VMCIQueue *consumeQ, // IN | |
- const uint64 produceQSize, // IN | |
- const void *buf, // IN | |
- size_t bufSize, // IN | |
- int bufType, // IN | |
- VMCIMemcpyToQueueFunc memcpyToQueue, // IN | |
- Bool canBlock) // IN | |
-{ | |
- int64 freeSpace; | |
- uint64 tail; | |
- size_t written; | |
- ssize_t result; | |
- | |
-#if !defined VMX86_VMX | |
- if (UNLIKELY(VMCI_EnqueueToDevNull(produceQ))) { | |
- return (ssize_t) bufSize; | |
- } | |
- | |
- result = VMCIQPairMapQueueHeaders(produceQ, consumeQ, canBlock); | |
- if (UNLIKELY(result != VMCI_SUCCESS)) { | |
- return result; | |
- } | |
-#endif | |
- | |
- freeSpace = VMCIQueueHeader_FreeSpace(produceQ->qHeader, | |
- consumeQ->qHeader, | |
- produceQSize); | |
- if (freeSpace == 0) { | |
- return VMCI_ERROR_QUEUEPAIR_NOSPACE; | |
- } | |
- | |
- if (freeSpace < VMCI_SUCCESS) { | |
- return (ssize_t)freeSpace; | |
- } | |
- | |
- written = (size_t)(freeSpace > bufSize ? bufSize : freeSpace); | |
- tail = VMCIQueueHeader_ProducerTail(produceQ->qHeader); | |
- if (LIKELY(tail + written < produceQSize)) { | |
- result = memcpyToQueue(produceQ, tail, buf, 0, written, bufType, | |
- canBlock); | |
- } else { | |
- /* Tail pointer wraps around. */ | |
- | |
- const size_t tmp = (size_t)(produceQSize - tail); | |
- | |
- result = memcpyToQueue(produceQ, tail, buf, 0, tmp, bufType, canBlock); | |
- if (result >= VMCI_SUCCESS) { | |
- result = memcpyToQueue(produceQ, 0, buf, tmp, written - tmp, bufType, | |
- canBlock); | |
- } | |
- } | |
- | |
- if (result < VMCI_SUCCESS) { | |
- return result; | |
- } | |
- | |
- VMCIQueueHeader_AddProducerTail(produceQ->qHeader, written, produceQSize); | |
- return written; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * DequeueLocked -- | |
- * | |
- * Dequeues data (if available) from the given consume queue. Writes data | |
- * to the user provided buffer using the provided function. | |
- * | |
- * Assumes the queue->mutex has been acquired. | |
- * | |
- * Results: | |
- * VMCI_ERROR_QUEUEPAIR_NODATA if no data was available to dequeue. | |
- * VMCI_ERROR_INVALID_SIZE, if any queue pointer is outside the queue | |
- * (as defined by the queue size). | |
- * VMCI_ERROR_INVALID_ARGS, if an error occured when accessing the buffer. | |
- * Otherwise the number of bytes dequeued is returned. | |
- * | |
- * Side effects: | |
- * Updates the head pointer of the consume queue. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static ssize_t | |
-DequeueLocked(VMCIQueue *produceQ, // IN | |
- VMCIQueue *consumeQ, // IN | |
- const uint64 consumeQSize, // IN | |
- void *buf, // IN | |
- size_t bufSize, // IN | |
- int bufType, // IN | |
- VMCIMemcpyFromQueueFunc memcpyFromQueue, // IN | |
- Bool updateConsumer, // IN | |
- Bool canBlock) // IN | |
-{ | |
- int64 bufReady; | |
- uint64 head; | |
- size_t read; | |
- ssize_t result; | |
- | |
-#if !defined VMX86_VMX | |
- result = VMCIQPairMapQueueHeaders(produceQ, consumeQ, canBlock); | |
- if (UNLIKELY(result != VMCI_SUCCESS)) { | |
- return result; | |
- } | |
-#endif | |
- | |
- bufReady = VMCIQueueHeader_BufReady(consumeQ->qHeader, | |
- produceQ->qHeader, | |
- consumeQSize); | |
- if (bufReady == 0) { | |
- return VMCI_ERROR_QUEUEPAIR_NODATA; | |
- } | |
- if (bufReady < VMCI_SUCCESS) { | |
- return (ssize_t)bufReady; | |
- } | |
- | |
- read = (size_t)(bufReady > bufSize ? bufSize : bufReady); | |
- head = VMCIQueueHeader_ConsumerHead(produceQ->qHeader); | |
- if (LIKELY(head + read < consumeQSize)) { | |
- result = memcpyFromQueue(buf, 0, consumeQ, head, read, bufType, canBlock); | |
- } else { | |
- /* Head pointer wraps around. */ | |
- | |
- const size_t tmp = (size_t)(consumeQSize - head); | |
- | |
- result = memcpyFromQueue(buf, 0, consumeQ, head, tmp, bufType, canBlock); | |
- if (result >= VMCI_SUCCESS) { | |
- result = memcpyFromQueue(buf, tmp, consumeQ, 0, read - tmp, bufType, | |
- canBlock); | |
- } | |
- } | |
- | |
- if (result < VMCI_SUCCESS) { | |
- return result; | |
- } | |
- | |
- if (updateConsumer) { | |
- VMCIQueueHeader_AddConsumerHead(produceQ->qHeader, | |
- read, | |
- consumeQSize); | |
- } | |
- | |
- return read; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * vmci_qpair_enqueue -- | |
- * | |
- * This is the client interface for enqueueing data into the queue. | |
- * | |
- * Results: | |
- * Err, if < 0. | |
- * Number of bytes enqueued if >= 0. | |
- * | |
- * Side effects: | |
- * Windows blocking call. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-VMCI_EXPORT_SYMBOL(vmci_qpair_enqueue) | |
-ssize_t | |
-vmci_qpair_enqueue(VMCIQPair *qpair, // IN | |
- const void *buf, // IN | |
- size_t bufSize, // IN | |
- int bufType) // IN | |
-{ | |
- ssize_t result; | |
- | |
- if (!qpair || !buf) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- result = VMCIQPairLock(qpair); | |
- if (result != VMCI_SUCCESS) { | |
- return result; | |
- } | |
- | |
- do { | |
- result = EnqueueLocked(qpair->produceQ, | |
- qpair->consumeQ, | |
- qpair->produceQSize, | |
- buf, bufSize, bufType, | |
- qpair->flags & VMCI_QPFLAG_LOCAL? | |
- VMCIMemcpyToQueueLocal: | |
- VMCIMemcpyToQueue, | |
- !(qpair->flags & VMCI_QPFLAG_NONBLOCK)); | |
- if (result == VMCI_ERROR_QUEUEPAIR_NOT_READY) { | |
- if (!VMCIQPairWaitForReadyQueue(qpair)) { | |
- result = VMCI_ERROR_WOULD_BLOCK; | |
- } | |
- } | |
- } while (result == VMCI_ERROR_QUEUEPAIR_NOT_READY); | |
- | |
- VMCIQPairUnlock(qpair); | |
- | |
- return result; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * vmci_qpair_dequeue -- | |
- * | |
- * This is the client interface for dequeueing data from the queue. | |
- * | |
- * Results: | |
- * Err, if < 0. | |
- * Number of bytes dequeued if >= 0. | |
- * | |
- * Side effects: | |
- * Windows blocking call. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-VMCI_EXPORT_SYMBOL(vmci_qpair_dequeue) | |
-ssize_t | |
-vmci_qpair_dequeue(VMCIQPair *qpair, // IN | |
- void *buf, // IN | |
- size_t bufSize, // IN | |
- int bufType) // IN | |
-{ | |
- ssize_t result; | |
- | |
- if (!qpair || !buf) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- result = VMCIQPairLock(qpair); | |
- if (result != VMCI_SUCCESS) { | |
- return result; | |
- } | |
- | |
- do { | |
- result = DequeueLocked(qpair->produceQ, | |
- qpair->consumeQ, | |
- qpair->consumeQSize, | |
- buf, bufSize, bufType, | |
- qpair->flags & VMCI_QPFLAG_LOCAL? | |
- VMCIMemcpyFromQueueLocal: | |
- VMCIMemcpyFromQueue, | |
- TRUE, !(qpair->flags & VMCI_QPFLAG_NONBLOCK)); | |
- if (result == VMCI_ERROR_QUEUEPAIR_NOT_READY) { | |
- if (!VMCIQPairWaitForReadyQueue(qpair)) { | |
- result = VMCI_ERROR_WOULD_BLOCK; | |
- } | |
- } | |
- } while (result == VMCI_ERROR_QUEUEPAIR_NOT_READY); | |
- | |
- VMCIQPairUnlock(qpair); | |
- | |
- return result; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * vmci_qpair_peek -- | |
- * | |
- * This is the client interface for peeking into a queue. (I.e., | |
- * copy data from the queue without updating the head pointer.) | |
- * | |
- * Results: | |
- * Err, if < 0. | |
- * Number of bytes peeked, if >= 0. | |
- * | |
- * Side effects: | |
- * Windows blocking call. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-VMCI_EXPORT_SYMBOL(vmci_qpair_peek) | |
-ssize_t | |
-vmci_qpair_peek(VMCIQPair *qpair, // IN | |
- void *buf, // IN | |
- size_t bufSize, // IN | |
- int bufType) // IN | |
-{ | |
- ssize_t result; | |
- | |
- if (!qpair || !buf) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- result = VMCIQPairLock(qpair); | |
- if (result != VMCI_SUCCESS) { | |
- return result; | |
- } | |
- | |
- do { | |
- result = DequeueLocked(qpair->produceQ, | |
- qpair->consumeQ, | |
- qpair->consumeQSize, | |
- buf, bufSize, bufType, | |
- qpair->flags & VMCI_QPFLAG_LOCAL? | |
- VMCIMemcpyFromQueueLocal: | |
- VMCIMemcpyFromQueue, | |
- FALSE, !(qpair->flags & VMCI_QPFLAG_NONBLOCK)); | |
- if (result == VMCI_ERROR_QUEUEPAIR_NOT_READY) { | |
- if (!VMCIQPairWaitForReadyQueue(qpair)) { | |
- result = VMCI_ERROR_WOULD_BLOCK; | |
- } | |
- } | |
- } while (result == VMCI_ERROR_QUEUEPAIR_NOT_READY); | |
- | |
- VMCIQPairUnlock(qpair); | |
- | |
- return result; | |
-} | |
- | |
- | |
-#if defined (SOLARIS) || (defined(__APPLE__) && !defined (VMX86_TOOLS)) || \ | |
- (defined(__linux__) && defined(__KERNEL__)) || \ | |
- (defined(_WIN32) && defined(WINNT_DDK)) | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * vmci_qpair_enquev -- | |
- * | |
- * This is the client interface for enqueueing data into the queue. | |
- * | |
- * Results: | |
- * Err, if < 0. | |
- * Number of bytes enqueued if >= 0. | |
- * | |
- * Side effects: | |
- * Windows blocking call. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-VMCI_EXPORT_SYMBOL(vmci_qpair_enquev) | |
-ssize_t | |
-vmci_qpair_enquev(VMCIQPair *qpair, // IN | |
- void *iov, // IN | |
- size_t iovSize, // IN | |
- int bufType) // IN | |
-{ | |
- ssize_t result; | |
- | |
- if (!qpair || !iov) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- result = VMCIQPairLock(qpair); | |
- if (result != VMCI_SUCCESS) { | |
- return result; | |
- } | |
- | |
- do { | |
- result = EnqueueLocked(qpair->produceQ, | |
- qpair->consumeQ, | |
- qpair->produceQSize, | |
- iov, iovSize, bufType, | |
- qpair->flags & VMCI_QPFLAG_LOCAL? | |
- VMCIMemcpyToQueueVLocal: | |
- VMCIMemcpyToQueueV, | |
- !(qpair->flags & VMCI_QPFLAG_NONBLOCK)); | |
- if (result == VMCI_ERROR_QUEUEPAIR_NOT_READY) { | |
- if (!VMCIQPairWaitForReadyQueue(qpair)) { | |
- result = VMCI_ERROR_WOULD_BLOCK; | |
- } | |
- } | |
- } while (result == VMCI_ERROR_QUEUEPAIR_NOT_READY); | |
- | |
- VMCIQPairUnlock(qpair); | |
- | |
- return result; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * vmci_qpair_dequev -- | |
- * | |
- * This is the client interface for dequeueing data from the queue. | |
- * | |
- * Results: | |
- * Err, if < 0. | |
- * Number of bytes dequeued if >= 0. | |
- * | |
- * Side effects: | |
- * Windows blocking call. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-VMCI_EXPORT_SYMBOL(vmci_qpair_dequev) | |
-ssize_t | |
-vmci_qpair_dequev(VMCIQPair *qpair, // IN | |
- void *iov, // IN | |
- size_t iovSize, // IN | |
- int bufType) // IN | |
-{ | |
- ssize_t result; | |
- | |
- if (!qpair || !iov) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- result = VMCIQPairLock(qpair); | |
- if (result != VMCI_SUCCESS) { | |
- return result; | |
- } | |
- | |
- do { | |
- result = DequeueLocked(qpair->produceQ, | |
- qpair->consumeQ, | |
- qpair->consumeQSize, | |
- iov, iovSize, bufType, | |
- qpair->flags & VMCI_QPFLAG_LOCAL? | |
- VMCIMemcpyFromQueueVLocal: | |
- VMCIMemcpyFromQueueV, | |
- TRUE, !(qpair->flags & VMCI_QPFLAG_NONBLOCK)); | |
- if (result == VMCI_ERROR_QUEUEPAIR_NOT_READY) { | |
- if (!VMCIQPairWaitForReadyQueue(qpair)) { | |
- result = VMCI_ERROR_WOULD_BLOCK; | |
- } | |
- } | |
- } while (result == VMCI_ERROR_QUEUEPAIR_NOT_READY); | |
- | |
- VMCIQPairUnlock(qpair); | |
- | |
- return result; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * vmci_qpair_peekv -- | |
- * | |
- * This is the client interface for peeking into a queue. (I.e., | |
- * copy data from the queue without updating the head pointer.) | |
- * | |
- * Results: | |
- * Err, if < 0. | |
- * Number of bytes peeked, if >= 0. | |
- * | |
- * Side effects: | |
- * Windows blocking call. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-VMCI_EXPORT_SYMBOL(vmci_qpair_peekv) | |
-ssize_t | |
-vmci_qpair_peekv(VMCIQPair *qpair, // IN | |
- void *iov, // IN | |
- size_t iovSize, // IN | |
- int bufType) // IN | |
-{ | |
- ssize_t result; | |
- | |
- if (!qpair || !iov) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- result = VMCIQPairLock(qpair); | |
- if (result != VMCI_SUCCESS) { | |
- return result; | |
- } | |
- | |
- do { | |
- result = DequeueLocked(qpair->produceQ, | |
- qpair->consumeQ, | |
- qpair->consumeQSize, | |
- iov, iovSize, bufType, | |
- qpair->flags & VMCI_QPFLAG_LOCAL? | |
- VMCIMemcpyFromQueueVLocal: | |
- VMCIMemcpyFromQueueV, | |
- FALSE, !(qpair->flags & VMCI_QPFLAG_NONBLOCK)); | |
- if (result == VMCI_ERROR_QUEUEPAIR_NOT_READY) { | |
- if (!VMCIQPairWaitForReadyQueue(qpair)) { | |
- result = VMCI_ERROR_WOULD_BLOCK; | |
- } | |
- } | |
- } while (result == VMCI_ERROR_QUEUEPAIR_NOT_READY); | |
- | |
- VMCIQPairUnlock(qpair); | |
- | |
- return result; | |
-} | |
- | |
-#endif /* Systems that support struct iovec */ | |
diff --git a/modules/linux/vmci/common/vmciQueuePair.c b/modules/linux/vmci/common/vmciQueuePair.c | |
deleted file mode 100644 | |
index 921105a..0000000 | |
--- a/modules/linux/vmci/common/vmciQueuePair.c | |
+++ /dev/null | |
@@ -1,3041 +0,0 @@ | |
-/********************************************************* | |
- * Copyright (C) 2007-2011 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 as published by the | |
- * Free Software Foundation version 2 and no 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 | |
- * | |
- *********************************************************/ | |
- | |
-/* | |
- * vmciQueuePair.c -- | |
- * | |
- * VMCI QueuePair API implementation in the host driver. | |
- */ | |
- | |
-#include "vmci_kernel_if.h" | |
-#include "vm_assert.h" | |
-#include "vmci_defs.h" | |
-#include "vmci_handle_array.h" | |
-#include "vmci_infrastructure.h" | |
-#include "vmciCommonInt.h" | |
-#include "vmciContext.h" | |
-#include "vmciDatagram.h" | |
-#include "vmciDriver.h" | |
-#include "vmciEvent.h" | |
-#include "vmciHashtable.h" | |
-#include "vmciKernelAPI.h" | |
-#include "vmciQueuePair.h" | |
-#include "vmciResource.h" | |
-#include "vmciRoute.h" | |
-#if defined(VMKERNEL) | |
-# include "vmciVmkInt.h" | |
-# include "vm_libc.h" | |
-#endif | |
- | |
-#define LGPFX "VMCIQueuePair: " | |
- | |
- | |
-/* | |
- * In the following, we will distinguish between two kinds of VMX processes - | |
- * the ones with versions lower than VMCI_VERSION_NOVMVM that use specialized | |
- * VMCI page files in the VMX and supporting VM to VM communication) and the | |
- * newer ones that use the guest memory directly. We will in the following refer | |
- * to the older VMX versions as old-style VMX'en, and the newer ones as new-style | |
- * VMX'en. | |
- * | |
- * The state transition datagram is as follows (the VMCIQPB_ prefix has been | |
- * removed for readability) - see below for more details on the transtions: | |
- * | |
- * -------------- NEW ------------- | |
- * | | | |
- * \_/ \_/ | |
- * CREATED (nomem) <-----------------> CREATED | |
- * | | | | |
- * | o-----------------------o | | |
- * | | | | |
- * \_/ \_/ \_/ | |
- * ATTACHED (nomem) <----------------> ATTACHED | |
- * | | | | |
- * | o----------------------o | | |
- * | | | | |
- * \_/ \_/ \_/ | |
- * SHUTDOWN (nomem) <----------------> SHUTDOWN | |
- * | | | |
- * | | | |
- * -------------> gone <------------- | |
- * | |
- * In more detail. When a VMCI queue pair is first created, it will be in the | |
- * VMCIQPB_NEW state. It will then move into one of the following states: | |
- * - VMCIQPB_CREATED with hasMem[0] FALSE: this state indicates that either: | |
- * - the created was performed by a host endpoint, in which case there is no | |
- * backing memory yet. | |
- * - the create was initiated by an old-style VMX, that uses | |
- * VMCIQPBroker_SetPageStore to specify the UVAs of the queue pair at a | |
- * later point in time. This state can be distinguished from the one above | |
- * by the context ID of the creator. A host side is not allowed to attach | |
- * until the page store has been set. | |
- * - VMCIQPB_CREATED with hasMem[0] TRUE: this state is the result when the | |
- * queue pair is created by a VMX using the queue pair device backend that | |
- * sets the UVAs of the queue pair immediately and stores the information | |
- * for later attachers. At this point, it is ready for the host side to attach | |
- * to it. | |
- * Once the queue pair is in one of the created states (with the exception of the | |
- * case mentioned for older VMX'en above), it is possible to attach to the queue | |
- * pair. Again we have two new states possible: | |
- * - VMCIQPB_ATTACHED with hasMem[0] TRUE: this state can be reached through | |
- * the following paths: | |
- * - from VMCIQPB_CREATED without memory when a new-style VMX allocates a queue pair, | |
- * and attaches to a queue pair previously created by the host side. | |
- * - from VMCIQPB_CREATED with memory when the host side attaches to a queue pair | |
- * already created by a guest. | |
- * - from VMCIQPB_ATTACHED without memory, when an old-style VMX calls | |
- * VMCIQPBroker_SetPageStore (see below). | |
- * - VMCIQPB_ATTACHED with hasMem[0] FALSE: If the queue pair already was in the | |
- * VMCIQPB_CREATED without memory due to a host side create, an old-style VMX | |
- * will bring the queue pair into this state. Once VMCIQPBroker_SetPageStore is | |
- * called to register the user memory, the VMCIQPB_ATTACH with memory state will be | |
- * entered. | |
- * From the attached queue pair, the queue pair can enter the shutdown states | |
- * when either side of the queue pair detaches. If the guest side detaches first, | |
- * the queue pair will enter the VMCIQPB_SHUTDOWN without memory state, where the content | |
- * of the queue pair will no longer be available. If the host side detaches first, | |
- * the queue pair will either enter the VMCIQPB_SHUTDOWN with memory, if the guest memory | |
- * is currently mapped, or VMCIQPB_SHUTDOWN without memory, if the guest memory is not | |
- * mapped (e.g., the host detaches while a guest is stunned). | |
- * | |
- * New-style VMX'en will also unmap guest memory, if the guest is quiesced, e.g., | |
- * during a snapshot operation. In that case, the guest memory will no longer be | |
- * available, and the queue pair will transition from memory to memory-less state. | |
- * The VMX may later map the memory once more, in which case the queue | |
- * pair will transition from the memory-less state at that point back to the memory | |
- * state. | |
- */ | |
- | |
-typedef enum { | |
- VMCIQPB_NEW, | |
- VMCIQPB_CREATED, | |
- VMCIQPB_ATTACHED, | |
- VMCIQPB_SHUTDOWN, | |
- VMCIQPB_GONE | |
-} QPBrokerState; | |
- | |
- | |
-/* | |
- * In the queue pair broker, we always use the guest point of view for | |
- * the produce and consume queue values and references, e.g., the | |
- * produce queue size stored is the guests produce queue size. The | |
- * host endpoint will need to swap these around. The only exception is | |
- * the local queue pairs on the host, in which case the host endpoint | |
- * that creates the queue pair will have the right orientation, and | |
- * the attaching host endpoint will need to swap. | |
- */ | |
- | |
-typedef struct QueuePairEntry { | |
- VMCIListItem listItem; | |
- VMCIHandle handle; | |
- VMCIId peer; | |
- uint32 flags; | |
- uint64 produceSize; | |
- uint64 consumeSize; | |
- uint32 refCount; | |
-} QueuePairEntry; | |
- | |
-typedef struct QPBrokerEntry { | |
- QueuePairEntry qp; | |
- VMCIId createId; | |
- VMCIId attachId; | |
- QPBrokerState stateQP; | |
- Bool hasMem[2]; | |
- Bool requireTrustedAttach; | |
- Bool createdByTrusted; | |
- Bool vmciPageFiles; // Created by VMX using VMCI page files | |
- VMCIQueue *produceQ; | |
- VMCIQueue *consumeQ; | |
- VMCIQueueHeader savedProduceQ; | |
- VMCIQueueHeader savedConsumeQ; | |
- VMCIEventReleaseCB wakeupCB; | |
- void *clientData; | |
- void *localMem; // Kernel memory for local queue pair | |
- Bool isVM2VM; | |
-} QPBrokerEntry; | |
- | |
-#if !defined(VMKERNEL) | |
-typedef struct QPGuestEndpoint { | |
- QueuePairEntry qp; | |
- uint64 numPPNs; | |
- void *produceQ; | |
- void *consumeQ; | |
- Bool hibernateFailure; | |
- PPNSet ppnSet; | |
-} QPGuestEndpoint; | |
-#endif | |
- | |
-typedef struct QueuePairList { | |
- VMCIList head; | |
- Atomic_uint32 hibernate; | |
- VMCIMutex mutex; | |
-} QueuePairList; | |
- | |
-static QueuePairList qpBrokerList; | |
- | |
-#define QPE_NUM_PAGES(_QPE) ((uint32)(CEILING(_QPE.produceSize, PAGE_SIZE) + \ | |
- CEILING(_QPE.consumeSize, PAGE_SIZE) + 2)) | |
- | |
-#if !defined(VMKERNEL) | |
- static QueuePairList qpGuestEndpoints; | |
- static VMCIHandleArray *hibernateFailedList; | |
- static VMCILock hibernateFailedListLock; | |
-#endif | |
- | |
-static void VMCIQPBrokerLock(void); | |
-static void VMCIQPBrokerUnlock(void); | |
- | |
-static QueuePairEntry *QueuePairList_FindEntry(QueuePairList *qpList, | |
- VMCIHandle handle); | |
-static void QueuePairList_AddEntry(QueuePairList *qpList, | |
- QueuePairEntry *entry); | |
-static void QueuePairList_RemoveEntry(QueuePairList *qpList, | |
- QueuePairEntry *entry); | |
-static QueuePairEntry *QueuePairList_GetHead(QueuePairList *qpList); | |
- | |
-static int QueuePairNotifyPeer(Bool attach, VMCIHandle handle, VMCIId myId, | |
- VMCIId peerId); | |
- | |
-static int VMCIQPBrokerAllocInt(VMCIHandle handle, VMCIId peer, | |
- uint32 flags, VMCIPrivilegeFlags privFlags, | |
- uint64 produceSize, | |
- uint64 consumeSize, | |
- QueuePairPageStore *pageStore, | |
- VMCIContext *context, | |
- VMCIEventReleaseCB wakeupCB, | |
- void *clientData, | |
- QPBrokerEntry **ent, | |
- Bool *swap); | |
-static int VMCIQPBrokerAttach(QPBrokerEntry *entry, | |
- VMCIId peer, | |
- uint32 flags, | |
- VMCIPrivilegeFlags privFlags, | |
- uint64 produceSize, | |
- uint64 consumeSize, | |
- QueuePairPageStore *pageStore, | |
- VMCIContext *context, | |
- VMCIEventReleaseCB wakeupCB, | |
- void *clientData, | |
- QPBrokerEntry **ent); | |
-static int VMCIQPBrokerCreate(VMCIHandle handle, | |
- VMCIId peer, | |
- uint32 flags, | |
- VMCIPrivilegeFlags privFlags, | |
- uint64 produceSize, | |
- uint64 consumeSize, | |
- QueuePairPageStore *pageStore, | |
- VMCIContext *context, | |
- VMCIEventReleaseCB wakeupCB, | |
- void *clientData, | |
- QPBrokerEntry **ent); | |
-static int VMCIQueuePairAllocHostWork(VMCIHandle *handle, VMCIQueue **produceQ, | |
- uint64 produceSize, VMCIQueue **consumeQ, | |
- uint64 consumeSize, | |
- VMCIId peer, uint32 flags, | |
- VMCIPrivilegeFlags privFlags, | |
- VMCIEventReleaseCB wakeupCB, | |
- void *clientData); | |
-static int VMCIQueuePairDetachHostWork(VMCIHandle handle); | |
- | |
-static int QueuePairSaveHeaders(QPBrokerEntry *entry, VMCIId contextId); | |
-static void QueuePairResetSavedHeaders(QPBrokerEntry *entry); | |
- | |
-#if !defined(VMKERNEL) | |
- | |
-static int QueuePairNotifyPeerLocal(Bool attach, VMCIHandle handle); | |
- | |
-static QPGuestEndpoint *QPGuestEndpointCreate(VMCIHandle handle, | |
- VMCIId peer, uint32 flags, | |
- uint64 produceSize, | |
- uint64 consumeSize, | |
- void *produceQ, void *consumeQ); | |
-static void QPGuestEndpointDestroy(QPGuestEndpoint *entry); | |
-static int VMCIQueuePairAllocHypercall(const QPGuestEndpoint *entry); | |
-static int VMCIQueuePairAllocGuestWork(VMCIHandle *handle, VMCIQueue **produceQ, | |
- uint64 produceSize, VMCIQueue **consumeQ, | |
- uint64 consumeSize, | |
- VMCIId peer, uint32 flags, | |
- VMCIPrivilegeFlags privFlags); | |
-static int VMCIQueuePairDetachGuestWork(VMCIHandle handle); | |
-static int VMCIQueuePairDetachHypercall(VMCIHandle handle); | |
-static void VMCIQPMarkHibernateFailed(QPGuestEndpoint *entry); | |
-static void VMCIQPUnmarkHibernateFailed(QPGuestEndpoint *entry); | |
- | |
-extern int VMCI_SendDatagram(VMCIDatagram *); | |
- | |
-#endif | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIQPBEGetIndex -- | |
- * | |
- * Retrieve index into host's queue structures for queue entry. | |
- * | |
- * Results: | |
- * 0 or 1. Or crash. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static unsigned int | |
-VMCIQPBEGetIndex(const QPBrokerEntry *entry, // IN | |
- VMCIId contextId) // IN | |
-{ | |
- ASSERT(entry->createId == contextId || entry->attachId == contextId); | |
- | |
- return (entry->isVM2VM && entry->createId != contextId) ? 1 : 0; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIQueuePair_Alloc -- | |
- * | |
- * Allocates a VMCI QueuePair. Only checks validity of input | |
- * arguments. The real work is done in the host or guest | |
- * specific function. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS on success, appropriate error code otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCIQueuePair_Alloc(VMCIHandle *handle, // IN/OUT | |
- VMCIQueue **produceQ, // OUT | |
- uint64 produceSize, // IN | |
- VMCIQueue **consumeQ, // OUT | |
- uint64 consumeSize, // IN | |
- VMCIId peer, // IN | |
- uint32 flags, // IN | |
- VMCIPrivilegeFlags privFlags, // IN | |
- Bool guestEndpoint, // IN | |
- VMCIEventReleaseCB wakeupCB, // IN | |
- void *clientData) // IN | |
-{ | |
- if (!handle || !produceQ || !consumeQ || (!produceSize && !consumeSize) || | |
- (flags & ~VMCI_QP_ALL_FLAGS)) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- if (guestEndpoint) { | |
-#if !defined(VMKERNEL) | |
- return VMCIQueuePairAllocGuestWork(handle, produceQ, produceSize, consumeQ, | |
- consumeSize, peer, flags, privFlags); | |
-#else | |
- return VMCI_ERROR_INVALID_ARGS; | |
-#endif | |
- } else { | |
- return VMCIQueuePairAllocHostWork(handle, produceQ, produceSize, consumeQ, | |
- consumeSize, peer, flags, privFlags, | |
- wakeupCB, clientData); | |
- } | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIQueuePair_Detach -- | |
- * | |
- * Detaches from a VMCI QueuePair. Only checks validity of input argument. | |
- * Real work is done in the host or guest specific function. | |
- * | |
- * Results: | |
- * Success or failure. | |
- * | |
- * Side effects: | |
- * Memory is freed. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCIQueuePair_Detach(VMCIHandle handle, // IN | |
- Bool guestEndpoint) // IN | |
-{ | |
- if (VMCI_HANDLE_INVALID(handle)) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- if (guestEndpoint) { | |
-#if !defined(VMKERNEL) | |
- return VMCIQueuePairDetachGuestWork(handle); | |
-#else | |
- return VMCI_ERROR_INVALID_ARGS; | |
-#endif | |
- } else { | |
- return VMCIQueuePairDetachHostWork(handle); | |
- } | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * QueuePairList_Init -- | |
- * | |
- * Initializes the list of QueuePairs. | |
- * | |
- * Results: | |
- * Success or failure. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static INLINE int | |
-QueuePairList_Init(QueuePairList *qpList) // IN | |
-{ | |
- int ret; | |
- | |
- VMCIList_Init(&qpList->head); | |
- Atomic_Write(&qpList->hibernate, 0); | |
- ret = VMCIMutex_Init(&qpList->mutex, "VMCIQPListLock", | |
- VMCI_SEMA_RANK_QUEUEPAIRLIST); | |
- return ret; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * QueuePairList_Destroy -- | |
- * | |
- * Destroy the list's mutex. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static INLINE void | |
-QueuePairList_Destroy(QueuePairList *qpList) | |
-{ | |
- VMCIMutex_Destroy(&qpList->mutex); | |
- VMCIList_Init(&qpList->head); | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIQPBrokerLock -- | |
- * | |
- * Acquires the mutex protecting a VMCI queue pair broker transaction. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static void | |
-VMCIQPBrokerLock(void) | |
-{ | |
- VMCIMutex_Acquire(&qpBrokerList.mutex); | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIQPBrokerUnlock -- | |
- * | |
- * Releases the mutex protecting a VMCI queue pair broker transaction. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static void | |
-VMCIQPBrokerUnlock(void) | |
-{ | |
- VMCIMutex_Release(&qpBrokerList.mutex); | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * QueuePairList_FindEntry -- | |
- * | |
- * Finds the entry in the list corresponding to a given handle. Assumes | |
- * that the list is locked. | |
- * | |
- * Results: | |
- * Pointer to entry. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static QueuePairEntry * | |
-QueuePairList_FindEntry(QueuePairList *qpList, // IN | |
- VMCIHandle handle) // IN | |
-{ | |
- VMCIListItem *next; | |
- | |
- if (VMCI_HANDLE_INVALID(handle)) { | |
- return NULL; | |
- } | |
- | |
- VMCIList_Scan(next, &qpList->head) { | |
- QueuePairEntry *entry = VMCIList_Entry(next, QueuePairEntry, listItem); | |
- | |
- if (VMCI_HANDLE_EQUAL(entry->handle, handle)) { | |
- return entry; | |
- } | |
- } | |
- | |
- return NULL; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * QueuePairList_AddEntry -- | |
- * | |
- * Adds the given entry to the list. Assumes that the list is locked. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static void | |
-QueuePairList_AddEntry(QueuePairList *qpList, // IN | |
- QueuePairEntry *entry) // IN | |
-{ | |
- if (entry) { | |
- VMCIList_Insert(&entry->listItem, &qpList->head); | |
- } | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * QueuePairList_RemoveEntry -- | |
- * | |
- * Removes the given entry from the list. Assumes that the list is locked. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static void | |
-QueuePairList_RemoveEntry(QueuePairList *qpList, // IN | |
- QueuePairEntry *entry) // IN | |
-{ | |
- if (entry) { | |
- VMCIList_Remove(&entry->listItem); | |
- } | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * QueuePairList_GetHead -- | |
- * | |
- * Returns the entry from the head of the list. Assumes that the list is | |
- * locked. | |
- * | |
- * Results: | |
- * Pointer to entry. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static QueuePairEntry * | |
-QueuePairList_GetHead(QueuePairList *qpList) | |
-{ | |
- VMCIListItem *first = VMCIList_First(&qpList->head); | |
- | |
- if (first) { | |
- QueuePairEntry *entry = VMCIList_Entry(first, QueuePairEntry, listItem); | |
- return entry; | |
- } | |
- | |
- return NULL; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIQPBroker_Init -- | |
- * | |
- * Initalizes queue pair broker state. | |
- * | |
- * Results: | |
- * Success or failure. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCIQPBroker_Init(void) | |
-{ | |
- return QueuePairList_Init(&qpBrokerList); | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIQPBroker_Exit -- | |
- * | |
- * Destroys the queue pair broker state. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCIQPBroker_Exit(void) | |
-{ | |
- QPBrokerEntry *entry; | |
- | |
- VMCIQPBrokerLock(); | |
- | |
- while ((entry = (QPBrokerEntry *)QueuePairList_GetHead(&qpBrokerList))) { | |
- QueuePairList_RemoveEntry(&qpBrokerList, &entry->qp); | |
- VMCI_FreeKernelMem(entry, sizeof *entry); | |
- } | |
- | |
- VMCIQPBrokerUnlock(); | |
- QueuePairList_Destroy(&qpBrokerList); | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIQPBroker_Alloc -- | |
- * | |
- * Requests that a queue pair be allocated with the VMCI queue | |
- * pair broker. Allocates a queue pair entry if one does not | |
- * exist. Attaches to one if it exists, and retrieves the page | |
- * files backing that QueuePair. Assumes that the queue pair | |
- * broker lock is held. | |
- * | |
- * Results: | |
- * Success or failure. | |
- * | |
- * Side effects: | |
- * Memory may be allocated. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCIQPBroker_Alloc(VMCIHandle handle, // IN | |
- VMCIId peer, // IN | |
- uint32 flags, // IN | |
- VMCIPrivilegeFlags privFlags, // IN | |
- uint64 produceSize, // IN | |
- uint64 consumeSize, // IN | |
- QueuePairPageStore *pageStore, // IN/OUT | |
- VMCIContext *context) // IN: Caller | |
-{ | |
- return VMCIQPBrokerAllocInt(handle, peer, flags, privFlags, | |
- produceSize, consumeSize, | |
- pageStore, context, NULL, NULL, | |
- NULL, NULL); | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * QueuePairNotifyPeer -- | |
- * | |
- * Enqueues an event datagram to notify the peer VM attached to | |
- * the given queue pair handle about attach/detach event by the | |
- * given VM. | |
- * | |
- * Results: | |
- * Payload size of datagram enqueued on success, error code otherwise. | |
- * | |
- * Side effects: | |
- * Memory is allocated. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-QueuePairNotifyPeer(Bool attach, // IN: attach or detach? | |
- VMCIHandle handle, // IN | |
- VMCIId myId, // IN | |
- VMCIId peerId) // IN: CID of VM to notify | |
-{ | |
- int rv; | |
- VMCIEventMsg *eMsg; | |
- VMCIEventPayload_QP *evPayload; | |
- char buf[sizeof *eMsg + sizeof *evPayload]; | |
- | |
- if (VMCI_HANDLE_INVALID(handle) || myId == VMCI_INVALID_ID || | |
- peerId == VMCI_INVALID_ID) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- /* | |
- * Notification message contains: queue pair handle and | |
- * attaching/detaching VM's context id. | |
- */ | |
- | |
- eMsg = (VMCIEventMsg *)buf; | |
- | |
- /* | |
- * In VMCIContext_EnqueueDatagram() we enforce the upper limit on number of | |
- * pending events from the hypervisor to a given VM otherwise a rogue VM | |
- * could do an arbitrary number of attach and detach operations causing memory | |
- * pressure in the host kernel. | |
- */ | |
- | |
- /* Clear out any garbage. */ | |
- memset(eMsg, 0, sizeof buf); | |
- | |
- eMsg->hdr.dst = VMCI_MAKE_HANDLE(peerId, VMCI_EVENT_HANDLER); | |
- eMsg->hdr.src = VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID, | |
- VMCI_CONTEXT_RESOURCE_ID); | |
- eMsg->hdr.payloadSize = sizeof *eMsg + sizeof *evPayload - sizeof eMsg->hdr; | |
- eMsg->eventData.event = attach ? VMCI_EVENT_QP_PEER_ATTACH : | |
- VMCI_EVENT_QP_PEER_DETACH; | |
- evPayload = VMCIEventMsgPayload(eMsg); | |
- evPayload->handle = handle; | |
- evPayload->peerId = myId; | |
- | |
- rv = VMCIDatagram_Dispatch(VMCI_HYPERVISOR_CONTEXT_ID, (VMCIDatagram *)eMsg, | |
- FALSE); | |
- if (rv < VMCI_SUCCESS) { | |
- VMCI_WARNING((LGPFX"Failed to enqueue QueuePair %s event datagram for " | |
- "context (ID=0x%x).\n", attach ? "ATTACH" : "DETACH", | |
- peerId)); | |
- } | |
- | |
- return rv; | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIQueuePairAllocHostWork -- | |
- * | |
- * This function implements the kernel API for allocating a queue | |
- * pair. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS on succes and appropriate failure code otherwise. | |
- * | |
- * Side effects: | |
- * May allocate memory. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-static int | |
-VMCIQueuePairAllocHostWork(VMCIHandle *handle, // IN/OUT | |
- VMCIQueue **produceQ, // OUT | |
- uint64 produceSize, // IN | |
- VMCIQueue **consumeQ, // OUT | |
- uint64 consumeSize, // IN | |
- VMCIId peer, // IN | |
- uint32 flags, // IN | |
- VMCIPrivilegeFlags privFlags, // IN | |
- VMCIEventReleaseCB wakeupCB, // IN | |
- void *clientData) // IN | |
-{ | |
- VMCIContext *context; | |
- QPBrokerEntry *entry; | |
- int result; | |
- Bool swap; | |
- | |
- if (VMCI_HANDLE_INVALID(*handle)) { | |
- VMCIId resourceID = VMCIResource_GetID(VMCI_HOST_CONTEXT_ID); | |
- if (resourceID == VMCI_INVALID_ID) { | |
- return VMCI_ERROR_NO_HANDLE; | |
- } | |
- *handle = VMCI_MAKE_HANDLE(VMCI_HOST_CONTEXT_ID, resourceID); | |
- } | |
- | |
- context = VMCIContext_Get(VMCI_HOST_CONTEXT_ID); | |
- ASSERT(context); | |
- | |
- entry = NULL; | |
- result = VMCIQPBrokerAllocInt(*handle, peer, flags, privFlags, produceSize, | |
- consumeSize, NULL, context, wakeupCB, clientData, | |
- &entry, &swap); | |
- if (result == VMCI_SUCCESS) { | |
- if (swap) { | |
- /* | |
- * If this is a local queue pair, the attacher will swap around produce | |
- * and consume queues. | |
- */ | |
- | |
- *produceQ = entry->consumeQ; | |
- *consumeQ = entry->produceQ; | |
- } else { | |
- *produceQ = entry->produceQ; | |
- *consumeQ = entry->consumeQ; | |
- } | |
- } else { | |
- *handle = VMCI_INVALID_HANDLE; | |
- VMCI_DEBUG_LOG(4, (LGPFX"queue pair broker failed to alloc (result=%d).\n", | |
- result)); | |
- } | |
- VMCIContext_Release(context); | |
- return result; | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIQueuePairDetachHostWork -- | |
- * | |
- * This function implements the host kernel API for detaching from | |
- * a queue pair. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS on success and appropriate failure code otherwise. | |
- * | |
- * Side effects: | |
- * May deallocate memory. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-static int | |
-VMCIQueuePairDetachHostWork(VMCIHandle handle) // IN | |
-{ | |
- int result; | |
- VMCIContext *context; | |
- | |
- context = VMCIContext_Get(VMCI_HOST_CONTEXT_ID); | |
- | |
- result = VMCIQPBroker_Detach(handle, context); | |
- | |
- VMCIContext_Release(context); | |
- return result; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIQPBrokerAllocInt -- | |
- * | |
- * QueuePair_Alloc for use when setting up queue pair endpoints | |
- * on the host. Like QueuePair_Alloc, but returns a pointer to | |
- * the QPBrokerEntry on success. | |
- * | |
- * Results: | |
- * Success or failure. | |
- * | |
- * Side effects: | |
- * Memory may be allocated. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static int | |
-VMCIQPBrokerAllocInt(VMCIHandle handle, // IN | |
- VMCIId peer, // IN | |
- uint32 flags, // IN | |
- VMCIPrivilegeFlags privFlags, // IN | |
- uint64 produceSize, // IN | |
- uint64 consumeSize, // IN | |
- QueuePairPageStore *pageStore, // IN/OUT | |
- VMCIContext *context, // IN: Caller | |
- VMCIEventReleaseCB wakeupCB, // IN | |
- void *clientData, // IN | |
- QPBrokerEntry **ent, // OUT | |
- Bool *swap) // OUT: swap queues? | |
-{ | |
- const VMCIId contextId = VMCIContext_GetId(context); | |
- Bool create; | |
- QPBrokerEntry *entry; | |
- Bool isLocal = flags & VMCI_QPFLAG_LOCAL; | |
- int result; | |
- | |
- if (VMCI_HANDLE_INVALID(handle) || | |
- (flags & ~VMCI_QP_ALL_FLAGS) || | |
- (isLocal && (!vmkernel || contextId != VMCI_HOST_CONTEXT_ID || | |
- handle.context != contextId)) || | |
- !(produceSize || consumeSize) || | |
- !context || contextId == VMCI_INVALID_ID || | |
- handle.context == VMCI_INVALID_ID) { | |
- VMCI_DEBUG_LOG(5, ("Invalid argument - handle, flags, whatever\n")); | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- if (pageStore && !VMCI_QP_PAGESTORE_IS_WELLFORMED(pageStore)) { | |
- VMCI_DEBUG_LOG(5, ("Invalid argument - page store\n")); | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- /* | |
- * In the initial argument check, we ensure that non-vmkernel hosts | |
- * are not allowed to create local queue pairs. | |
- */ | |
- | |
- ASSERT(vmkernel || !isLocal); | |
- | |
- VMCIQPBrokerLock(); | |
- | |
- if (!isLocal && VMCIContext_QueuePairExists(context, handle)) { | |
- VMCI_DEBUG_LOG(4, (LGPFX"Context (ID=0x%x) already attached to queue pair " | |
- "(handle=0x%x:0x%x).\n", | |
- contextId, handle.context, handle.resource)); | |
- VMCIQPBrokerUnlock(); | |
- return VMCI_ERROR_ALREADY_EXISTS; | |
- } | |
- | |
- entry = (QPBrokerEntry *)QueuePairList_FindEntry(&qpBrokerList, handle); | |
- if (!entry) { | |
- create = TRUE; | |
- result = VMCIQPBrokerCreate(handle, peer, flags, privFlags, produceSize, | |
- consumeSize, pageStore, context, wakeupCB, | |
- clientData, ent); | |
- } else { | |
- create = FALSE; | |
- result = VMCIQPBrokerAttach(entry, peer, flags, privFlags, produceSize, | |
- consumeSize, pageStore, context, wakeupCB, | |
- clientData, ent); | |
- } | |
- | |
- VMCIQPBrokerUnlock(); | |
- | |
- if (swap) { | |
- *swap = (contextId == VMCI_HOST_CONTEXT_ID) && !(create && isLocal); | |
- } | |
- | |
- return result; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIQPBrokerCreate -- | |
- * | |
- * The first endpoint issuing a queue pair allocation will create the state | |
- * of the queue pair in the queue pair broker. | |
- * | |
- * If the creator is a guest, it will associate a VMX virtual address range | |
- * with the queue pair as specified by the pageStore. For compatibility with | |
- * older VMX'en, that would use a separate step to set the VMX virtual | |
- * address range, the virtual address range can be registered later using | |
- * VMCIQPBroker_SetPageStore. In that case, a pageStore of NULL should be | |
- * used. | |
- * | |
- * If the creator is the host, a pageStore of NULL should be used as well, | |
- * since the host is not able to supply a page store for the queue pair. | |
- * | |
- * For older VMX and host callers, the queue pair will be created in the | |
- * VMCIQPB_CREATED_NO_MEM state, and for current VMX callers, it will be | |
- * created in VMCOQPB_CREATED_MEM state. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS on success, appropriate error code otherwise. | |
- * | |
- * Side effects: | |
- * Memory will be allocated, and pages may be pinned. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static int | |
-VMCIQPBrokerCreate(VMCIHandle handle, // IN | |
- VMCIId peer, // IN | |
- uint32 flags, // IN | |
- VMCIPrivilegeFlags privFlags, // IN | |
- uint64 produceSize, // IN | |
- uint64 consumeSize, // IN | |
- QueuePairPageStore *pageStore, // IN | |
- VMCIContext *context, // IN: Caller | |
- VMCIEventReleaseCB wakeupCB, // IN | |
- void *clientData, // IN | |
- QPBrokerEntry **ent) // OUT | |
-{ | |
- QPBrokerEntry *entry = NULL; | |
- const VMCIId contextId = VMCIContext_GetId(context); | |
- Bool isLocal = flags & VMCI_QPFLAG_LOCAL; | |
- int result; | |
- uint64 guestProduceSize; | |
- uint64 guestConsumeSize; | |
- Bool isVM2VM; | |
- | |
- /* | |
- * Do not create if the caller asked not to. | |
- */ | |
- | |
- if (flags & VMCI_QPFLAG_ATTACH_ONLY) { | |
- VMCI_DEBUG_LOG(5, ("QP Create - attach only\n")); | |
- return VMCI_ERROR_NOT_FOUND; | |
- } | |
- | |
- /* | |
- * Creator's context ID should match handle's context ID or the creator | |
- * must allow the context in handle's context ID as the "peer". | |
- */ | |
- | |
- if (handle.context != contextId && handle.context != peer) { | |
- VMCI_DEBUG_LOG(5, ("QP Create - contextId fail, %x != %x, %x\n", | |
- handle.context, contextId, peer)); | |
- return VMCI_ERROR_NO_ACCESS; | |
- } | |
- | |
- isVM2VM = VMCI_CONTEXT_IS_VM(contextId) && VMCI_CONTEXT_IS_VM(peer); | |
- if (!vmkernel && isVM2VM) { | |
- VMCI_DEBUG_LOG(5, ("QP Create - VM2VM\n")); | |
- return VMCI_ERROR_DST_UNREACHABLE; | |
- } | |
- | |
- /* | |
- * Creator's context ID for local queue pairs should match the | |
- * peer, if a peer is specified. | |
- */ | |
- | |
- if (isLocal && peer != VMCI_INVALID_ID && contextId != peer) { | |
- VMCI_DEBUG_LOG(5, ("QP Create - peer %x, context %x\n", | |
- peer, contextId)); | |
- return VMCI_ERROR_NO_ACCESS; | |
- } | |
- | |
- entry = VMCI_AllocKernelMem(sizeof *entry, VMCI_MEMORY_ATOMIC); | |
- if (!entry) { | |
- VMCI_DEBUG_LOG(5, ("QP Create - no memory\n")); | |
- return VMCI_ERROR_NO_MEM; | |
- } | |
- | |
- if (VMCIContext_GetId(context) == VMCI_HOST_CONTEXT_ID && !isLocal) { | |
- /* | |
- * The queue pair broker entry stores values from the guest | |
- * point of view, so a creating host side endpoint should swap | |
- * produce and consume values -- unless it is a local queue | |
- * pair, in which case no swapping is necessary, since the local | |
- * attacher will swap queues. | |
- */ | |
- | |
- guestProduceSize = consumeSize; | |
- guestConsumeSize = produceSize; | |
- } else { | |
- guestProduceSize = produceSize; | |
- guestConsumeSize = consumeSize; | |
- } | |
- | |
- memset(entry, 0, sizeof *entry); | |
- entry->qp.handle = handle; | |
- entry->qp.peer = peer; | |
- entry->qp.flags = flags; | |
- entry->qp.produceSize = guestProduceSize; | |
- entry->qp.consumeSize = guestConsumeSize; | |
- entry->qp.refCount = 1; | |
- entry->createId = contextId; | |
- entry->attachId = VMCI_INVALID_ID; | |
- entry->stateQP = VMCIQPB_NEW; | |
- entry->requireTrustedAttach = | |
- (context->privFlags & VMCI_PRIVILEGE_FLAG_RESTRICTED) ? TRUE : FALSE; | |
- entry->createdByTrusted = | |
- (privFlags & VMCI_PRIVILEGE_FLAG_TRUSTED) ? TRUE : FALSE; | |
- entry->vmciPageFiles = FALSE; | |
- entry->wakeupCB = wakeupCB; | |
- entry->clientData = clientData; | |
- entry->isVM2VM = FALSE; /* It is not VM2VM until VM attaches... */ | |
- entry->produceQ = VMCIHost_AllocQueue(guestProduceSize); | |
- if (entry->produceQ == NULL) { | |
- result = VMCI_ERROR_NO_MEM; | |
- VMCI_DEBUG_LOG(5, ("QP Create - no memory PQ\n")); | |
- goto error; | |
- } | |
- entry->consumeQ = VMCIHost_AllocQueue(guestConsumeSize); | |
- if (entry->consumeQ == NULL) { | |
- result = VMCI_ERROR_NO_MEM; | |
- VMCI_DEBUG_LOG(5, ("QP Create - no memory CQ\n")); | |
- goto error; | |
- } | |
- | |
- VMCI_InitQueueMutex(entry->produceQ, entry->consumeQ); | |
- | |
- VMCIList_InitEntry(&entry->qp.listItem); | |
- | |
- if (isLocal) { | |
- ASSERT(pageStore == NULL); | |
- | |
- entry->localMem = VMCI_AllocKernelMem(QPE_NUM_PAGES(entry->qp) * PAGE_SIZE, | |
- VMCI_MEMORY_NONPAGED); | |
- if (entry->localMem == NULL) { | |
- result = VMCI_ERROR_NO_MEM; | |
- VMCI_DEBUG_LOG(5, ("QP Create - no memory LM\n")); | |
- goto error; | |
- } | |
- entry->stateQP = VMCIQPB_CREATED; | |
- entry->hasMem[0] = TRUE; | |
- entry->produceQ->qHeader = entry->localMem; | |
- entry->consumeQ->qHeader = | |
- (VMCIQueueHeader *)((uint8 *)entry->localMem + | |
- (CEILING(entry->qp.produceSize, PAGE_SIZE) + 1) * PAGE_SIZE); | |
- VMCIQueueHeader_Init(entry->produceQ->qHeader, handle); | |
- VMCIQueueHeader_Init(entry->consumeQ->qHeader, handle); | |
- } else if (pageStore) { | |
- ASSERT(entry->createId != VMCI_HOST_CONTEXT_ID); | |
- | |
- /* | |
- * The VMX already initialized the queue pair headers, so no | |
- * need for the kernel side to do that. | |
- */ | |
- | |
- result = VMCIHost_RegisterUserMemory(0, pageStore, | |
- entry->produceQ, | |
- entry->consumeQ); | |
- if (result < VMCI_SUCCESS) { | |
- VMCI_DEBUG_LOG(5, ("QP Create - cannot register user memory\n")); | |
- goto error; | |
- } | |
- VMCIHost_MarkQueuesAvailable(0, entry->produceQ, entry->consumeQ); | |
- entry->stateQP = VMCIQPB_CREATED; | |
- entry->hasMem[0] = TRUE; | |
- } else { | |
- /* | |
- * A create without a pageStore may be either a host side create (in which | |
- * case we are waiting for the guest side to supply the memory) or an old | |
- * style queue pair create (in which case we will expect a set page store | |
- * call as the next step). | |
- */ | |
- | |
- entry->stateQP = VMCIQPB_CREATED; | |
- } | |
- | |
- QueuePairList_AddEntry(&qpBrokerList, &entry->qp); | |
- if (ent != NULL) { | |
- *ent = entry; | |
- } | |
- | |
- VMCIContext_QueuePairCreate(context, handle); | |
- | |
- return VMCI_SUCCESS; | |
- | |
-error: | |
- if (entry != NULL) { | |
- if (entry->produceQ != NULL) { | |
- VMCIHost_FreeQueue(entry->produceQ, guestProduceSize); | |
- } | |
- if (entry->consumeQ != NULL) { | |
- VMCIHost_FreeQueue(entry->consumeQ, guestConsumeSize); | |
- } | |
- VMCI_FreeKernelMem(entry, sizeof *entry); | |
- } | |
- return result; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIQPBrokerAttach -- | |
- * | |
- * The second endpoint issuing a queue pair allocation will attach to the | |
- * queue pair registered with the queue pair broker. | |
- * | |
- * If the attacher is a guest, it will associate a VMX virtual address range | |
- * with the queue pair as specified by the pageStore. At this point, the | |
- * already attach host endpoint may start using the queue pair, and an | |
- * attach event is sent to it. For compatibility with older VMX'en, that | |
- * used a separate step to set the VMX virtual address range, the virtual | |
- * address range can be registered later using VMCIQPBroker_SetPageStore. In | |
- * that case, a pageStore of NULL should be used, and the attach event will | |
- * be generated once the actual page store has been set. | |
- * | |
- * If the attacher is the host, a pageStore of NULL should be used as well, | |
- * since the page store information is already set by the guest. | |
- * | |
- * For new VMX and host callers, the queue pair will be moved to the | |
- * VMCIQPB_ATTACHED_MEM state, and for older VMX callers, it will be | |
- * moved to the VMCOQPB_ATTACHED_NO_MEM state. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS on success, appropriate error code otherwise. | |
- * | |
- * Side effects: | |
- * Memory will be allocated, and pages may be pinned. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static int | |
-VMCIQPBrokerAttach(QPBrokerEntry *entry, // IN | |
- VMCIId peer, // IN | |
- uint32 flags, // IN | |
- VMCIPrivilegeFlags privFlags, // IN | |
- uint64 produceSize, // IN | |
- uint64 consumeSize, // IN | |
- QueuePairPageStore *pageStore, // IN/OUT | |
- VMCIContext *context, // IN: Caller | |
- VMCIEventReleaseCB wakeupCB, // IN | |
- void *clientData, // IN | |
- QPBrokerEntry **ent) // OUT | |
-{ | |
- const VMCIId contextId = VMCIContext_GetId(context); | |
- Bool isLocal = flags & VMCI_QPFLAG_LOCAL; | |
- int result; | |
- Bool isVM2VM; | |
- | |
- if (entry->stateQP != VMCIQPB_CREATED) { | |
- VMCI_DEBUG_LOG(5, ("QP Attach - state is %x\n", entry->stateQP)); | |
- return VMCI_ERROR_UNAVAILABLE; | |
- } | |
- | |
- if (isLocal) { | |
- if (!(entry->qp.flags & VMCI_QPFLAG_LOCAL) || | |
- contextId != entry->createId) { | |
- VMCI_DEBUG_LOG(5, ("QP Attach - invalid args, ctx=%x, createId=%x\n", | |
- contextId, entry->createId)); | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- } else if (contextId == entry->createId || contextId == entry->attachId) { | |
- VMCI_DEBUG_LOG(5, ("QP Attach - already, ctx=%x, create=%x, attach=%x\n", | |
- contextId, entry->createId, entry->attachId)); | |
- return VMCI_ERROR_ALREADY_EXISTS; | |
- } | |
- | |
- ASSERT(entry->qp.refCount < 2); | |
- ASSERT(entry->attachId == VMCI_INVALID_ID); | |
- | |
- isVM2VM = VMCI_CONTEXT_IS_VM(contextId) && VMCI_CONTEXT_IS_VM(entry->createId); | |
- if (!vmkernel && isVM2VM) { | |
- VMCI_DEBUG_LOG(5, ("QP Attach - VM2VM\n")); | |
- return VMCI_ERROR_DST_UNREACHABLE; | |
- } | |
- | |
- /* | |
- * If we are attaching from a restricted context then the queuepair | |
- * must have been created by a trusted endpoint. | |
- */ | |
- | |
- if (context->privFlags & VMCI_PRIVILEGE_FLAG_RESTRICTED) { | |
- if (!entry->createdByTrusted) { | |
- VMCI_DEBUG_LOG(5, ("QP Attach - restricted vs trusted\n")); | |
- return VMCI_ERROR_NO_ACCESS; | |
- } | |
- } | |
- | |
- /* | |
- * If we are attaching to a queuepair that was created by a restricted | |
- * context then we must be trusted. | |
- */ | |
- | |
- if (entry->requireTrustedAttach) { | |
- if (!(privFlags & VMCI_PRIVILEGE_FLAG_TRUSTED)) { | |
- VMCI_DEBUG_LOG(5, ("QP Attach - trusted attach required\n")); | |
- return VMCI_ERROR_NO_ACCESS; | |
- } | |
- } | |
- | |
- /* | |
- * If the creator specifies VMCI_INVALID_ID in "peer" field, access | |
- * control check is not performed. | |
- */ | |
- | |
- if (entry->qp.peer != VMCI_INVALID_ID && entry->qp.peer != contextId) { | |
- VMCI_DEBUG_LOG(5, ("QP Attach - bad peer id %x != %x\n", | |
- contextId, entry->qp.peer)); | |
- return VMCI_ERROR_NO_ACCESS; | |
- } | |
- | |
- if (entry->createId == VMCI_HOST_CONTEXT_ID) { | |
- /* | |
- * Do not attach if the caller doesn't support Host Queue Pairs | |
- * and a host created this queue pair. | |
- */ | |
- | |
- if (!VMCIContext_SupportsHostQP(context)) { | |
- VMCI_DEBUG_LOG(5, ("QP Attach - no attach to host qp\n")); | |
- return VMCI_ERROR_INVALID_RESOURCE; | |
- } | |
- } else if (contextId == VMCI_HOST_CONTEXT_ID) { | |
- VMCIContext *createContext; | |
- Bool supportsHostQP; | |
- | |
- /* | |
- * Do not attach a host to a user created queue pair if that | |
- * user doesn't support host queue pair end points. | |
- */ | |
- | |
- createContext = VMCIContext_Get(entry->createId); | |
- supportsHostQP = VMCIContext_SupportsHostQP(createContext); | |
- VMCIContext_Release(createContext); | |
- | |
- if (!supportsHostQP) { | |
- VMCI_DEBUG_LOG(5, ("QP Attach - no host attach to qp\n")); | |
- return VMCI_ERROR_INVALID_RESOURCE; | |
- } | |
- } | |
- | |
- if ((entry->qp.flags & ~VMCI_QP_ASYMM) != (flags & ~VMCI_QP_ASYMM_PEER)) { | |
- VMCI_DEBUG_LOG(5, ("QP Attach - flags mismatch\n")); | |
- return VMCI_ERROR_QUEUEPAIR_MISMATCH; | |
- } | |
- | |
- if (contextId != VMCI_HOST_CONTEXT_ID && !isVM2VM) { | |
- /* | |
- * The queue pair broker entry stores values from the guest | |
- * point of view, so an attaching guest should match the values | |
- * stored in the entry. | |
- */ | |
- | |
- if (entry->qp.produceSize != produceSize || | |
- entry->qp.consumeSize != consumeSize) { | |
- VMCI_DEBUG_LOG(5, ("QP Attach - queue size mismatch\n")); | |
- return VMCI_ERROR_QUEUEPAIR_MISMATCH; | |
- } | |
- } else if (entry->qp.produceSize != consumeSize || | |
- entry->qp.consumeSize != produceSize) { | |
- VMCI_DEBUG_LOG(5, ("QP Attach - host queue size mismatch\n")); | |
- return VMCI_ERROR_QUEUEPAIR_MISMATCH; | |
- } | |
- | |
- if (contextId != VMCI_HOST_CONTEXT_ID) { | |
- /* | |
- * If a guest attached to a queue pair, it will supply the backing memory. | |
- * If this is a pre NOVMVM vmx, the backing memory will be supplied by | |
- * calling VMCIQPBroker_SetPageStore() following the return of the | |
- * VMCIQPBroker_Alloc() call. If it is a vmx of version NOVMVM or later, | |
- * the page store must be supplied as part of the VMCIQPBroker_Alloc call. | |
- * Under all circumstances must the initially created queue pair not have | |
- * any memory associated with it already. | |
- */ | |
- | |
- if (isVM2VM) { | |
- if (!pageStore || entry->stateQP != VMCIQPB_CREATED) { | |
- VMCI_DEBUG_LOG(5, ("QP Attach - bad QP state for VM2VM, %x, %p\n", entry->stateQP, pageStore)); | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- } else { | |
- if (entry->stateQP != VMCIQPB_CREATED || entry->hasMem[0]) { | |
- VMCI_DEBUG_LOG(5, ("QP Attach - bad QP state, %x\n", entry->stateQP)); | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- } | |
- | |
- if (pageStore != NULL) { | |
- unsigned int index; | |
- | |
- ASSERT(entry->isVM2VM == FALSE); | |
- entry->isVM2VM = isVM2VM; | |
- | |
- /* | |
- * Patch up host state to point to guest supplied memory. The VMX | |
- * already initialized the queue pair headers, so no need for the | |
- * kernel side to do that. | |
- */ | |
- | |
- index = isVM2VM ? 1 : 0; | |
- result = VMCIHost_RegisterUserMemory(index, | |
- pageStore, | |
- entry->produceQ, | |
- entry->consumeQ); | |
- if (result < VMCI_SUCCESS) { | |
- VMCI_DEBUG_LOG(5, ("QP Attach - cannot register memory\n")); | |
- entry->isVM2VM = FALSE; | |
- return result; | |
- } | |
- VMCIHost_MarkQueuesAvailable(index, entry->produceQ, entry->consumeQ); | |
- if (entry->qp.flags & VMCI_QPFLAG_NONBLOCK) { | |
- result = VMCIHost_MapQueues(index, entry->produceQ, entry->consumeQ, | |
- entry->qp.flags); | |
- if (result < VMCI_SUCCESS) { | |
- VMCIHost_ReleaseUserMemory(index, entry->produceQ, entry->consumeQ); | |
- entry->isVM2VM = FALSE; | |
- VMCI_DEBUG_LOG(5, ("QP Attach - cannot map queues\n")); | |
- return result; | |
- } | |
- } | |
- entry->stateQP = VMCIQPB_ATTACHED; | |
- entry->hasMem[index] = TRUE; | |
- } else { | |
- entry->stateQP = VMCIQPB_ATTACHED; | |
- } | |
- } else if (entry->stateQP == VMCIQPB_CREATED && !entry->hasMem[0]) { | |
- /* | |
- * The host side is attempting to attach to a queue pair that doesn't have | |
- * any memory associated with it. This must be a pre NOVMVM vmx that hasn't | |
- * set the page store information yet, or a quiesced VM. | |
- */ | |
- | |
- VMCI_DEBUG_LOG(5, ("QP Attach - QP without memory\n")); | |
- return VMCI_ERROR_UNAVAILABLE; | |
- } else { | |
- /* | |
- * For non-blocking queue pairs, we cannot rely on enqueue/dequeue to map | |
- * in the pages on the host-side, since it may block, so we make an attempt | |
- * here. | |
- */ | |
- | |
- if (flags & VMCI_QPFLAG_NONBLOCK) { | |
- /* | |
- * We only have to do work here if this is a host-to-VM queuepair. | |
- * Otherwise there's nothing to map, since the pages backing the | |
- * queues are allocated directly out of host memory. | |
- */ | |
- | |
- if (!isLocal) { | |
- VMCIHost_MarkQueuesAvailable(0, entry->produceQ, entry->consumeQ); | |
- result = VMCIHost_MapQueues(0, entry->produceQ, entry->consumeQ, flags); | |
- if (result < VMCI_SUCCESS) { | |
- VMCI_DEBUG_LOG(5, ("QP Attach - cannot map queues for host\n")); | |
- return result; | |
- } | |
- } | |
- entry->qp.flags |= flags & (VMCI_QPFLAG_NONBLOCK | VMCI_QPFLAG_PINNED); | |
- } | |
- | |
- /* | |
- * The host side has successfully attached to a queue pair. | |
- */ | |
- | |
- entry->stateQP = VMCIQPB_ATTACHED; | |
- } | |
- | |
- if (entry->stateQP == VMCIQPB_ATTACHED && entry->hasMem[0] && (!entry->isVM2VM || entry->hasMem[1])) { | |
- result = QueuePairNotifyPeer(TRUE, entry->qp.handle, contextId, | |
- entry->createId); | |
- if (result < VMCI_SUCCESS) { | |
- VMCI_WARNING((LGPFX"Failed to notify peer (ID=0x%x) of attach to queue " | |
- "pair (handle=0x%x:0x%x).\n", entry->createId, | |
- entry->qp.handle.context, entry->qp.handle.resource)); | |
- } | |
- } | |
- | |
- entry->attachId = contextId; | |
- entry->qp.refCount++; | |
- if (wakeupCB) { | |
- ASSERT(!entry->wakeupCB); | |
- entry->wakeupCB = wakeupCB; | |
- entry->clientData = clientData; | |
- } | |
- | |
- /* | |
- * When attaching to local queue pairs, the context already has | |
- * an entry tracking the queue pair, so don't add another one. | |
- */ | |
- | |
- if (!isLocal) { | |
- VMCIContext_QueuePairCreate(context, entry->qp.handle); | |
- } else { | |
- ASSERT(VMCIContext_QueuePairExists(context, entry->qp.handle)); | |
- } | |
- if (ent != NULL) { | |
- *ent = entry; | |
- } | |
- | |
- return VMCI_SUCCESS; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIQPBroker_SetPageStore -- | |
- * | |
- * VMX'en with versions lower than VMCI_VERSION_NOVMVM use a separate | |
- * step to add the UVAs of the VMX mapping of the queue pair. This function | |
- * provides backwards compatibility with such VMX'en, and takes care of | |
- * registering the page store for a queue pair previously allocated by the | |
- * VMX during create or attach. This function will move the queue pair state | |
- * to either from VMCIQBP_CREATED_NO_MEM to VMCIQBP_CREATED_MEM or | |
- * VMCIQBP_ATTACHED_NO_MEM to VMCIQBP_ATTACHED_MEM. If moving to the | |
- * attached state with memory, the queue pair is ready to be used by the | |
- * host peer, and an attached event will be generated. | |
- * | |
- * Assumes that the queue pair broker lock is held. | |
- * | |
- * This function is only used by the hosted platform, since there is no | |
- * issue with backwards compatibility for vmkernel. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS on success, appropriate error code otherwise. | |
- * | |
- * Side effects: | |
- * Pages may get pinned. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCIQPBroker_SetPageStore(VMCIHandle handle, // IN | |
- VA64 produceUVA, // IN | |
- VA64 consumeUVA, // IN | |
- VMCIContext *context) // IN: Caller | |
-{ | |
- QPBrokerEntry *entry; | |
- int result; | |
- const VMCIId contextId = VMCIContext_GetId(context); | |
- unsigned int index; | |
- | |
- if (VMCI_HANDLE_INVALID(handle) || !context || contextId == VMCI_INVALID_ID) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- /* | |
- * We only support guest to host queue pairs, so the VMX must | |
- * supply UVAs for the mapped page files. | |
- */ | |
- | |
- if (produceUVA == 0 || consumeUVA == 0) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- VMCIQPBrokerLock(); | |
- | |
- if (!VMCIContext_QueuePairExists(context, handle)) { | |
- VMCI_WARNING((LGPFX"Context (ID=0x%x) not attached to queue pair " | |
- "(handle=0x%x:0x%x).\n", contextId, handle.context, | |
- handle.resource)); | |
- result = VMCI_ERROR_NOT_FOUND; | |
- goto out; | |
- } | |
- | |
- entry = (QPBrokerEntry *)QueuePairList_FindEntry(&qpBrokerList, handle); | |
- if (!entry) { | |
- result = VMCI_ERROR_NOT_FOUND; | |
- goto out; | |
- } | |
- | |
- /* | |
- * If I'm the owner then I can set the page store. | |
- * | |
- * Or, if a host created the QueuePair and I'm the attached peer | |
- * then I can set the page store. | |
- */ | |
- | |
- if (entry->createId != contextId && | |
- (entry->createId != VMCI_HOST_CONTEXT_ID || | |
- entry->attachId != contextId)) { | |
- result = VMCI_ERROR_QUEUEPAIR_NOTOWNER; | |
- goto out; | |
- } | |
- | |
- index = VMCIQPBEGetIndex(entry, contextId); | |
- | |
- if (entry->hasMem[index] || | |
- (entry->stateQP != VMCIQPB_CREATED && entry->stateQP == VMCIQPB_ATTACHED)) { | |
- result = VMCI_ERROR_UNAVAILABLE; | |
- goto out; | |
- } | |
- | |
- result = VMCIHost_GetUserMemory(index, produceUVA, consumeUVA, | |
- entry->produceQ, entry->consumeQ); | |
- if (result < VMCI_SUCCESS) { | |
- goto out; | |
- } | |
- VMCIHost_MarkQueuesAvailable(index, entry->produceQ, entry->consumeQ); | |
- result = VMCIHost_MapQueues(index, entry->produceQ, entry->consumeQ, 0); | |
- if (result < VMCI_SUCCESS) { | |
- VMCIHost_ReleaseUserMemory(index, entry->produceQ, entry->consumeQ); | |
- goto out; | |
- } | |
- | |
- entry->hasMem[index] = TRUE; | |
- entry->vmciPageFiles = TRUE; | |
- | |
- if (entry->stateQP == VMCIQPB_ATTACHED && entry->hasMem[0] && (!entry->isVM2VM || entry->hasMem[1])) { | |
- result = QueuePairNotifyPeer(TRUE, handle, contextId, entry->createId); | |
- if (result < VMCI_SUCCESS) { | |
- VMCI_WARNING((LGPFX"Failed to notify peer (ID=0x%x) of attach to queue " | |
- "pair (handle=0x%x:0x%x).\n", entry->createId, | |
- entry->qp.handle.context, entry->qp.handle.resource)); | |
- } | |
- } | |
- | |
- result = VMCI_SUCCESS; | |
-out: | |
- VMCIQPBrokerUnlock(); | |
- return result; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIQPBroker_Detach -- | |
- * | |
- * The main entry point for detaching from a queue pair registered with the | |
- * queue pair broker. If more than one endpoint is attached to the queue | |
- * pair, the first endpoint will mainly decrement a reference count and | |
- * generate a notification to its peer. The last endpoint will clean up | |
- * the queue pair state registered with the broker. | |
- * | |
- * When a guest endpoint detaches, it will unmap and unregister the guest | |
- * memory backing the queue pair. If the host is still attached, it will | |
- * no longer be able to access the queue pair content. | |
- * | |
- * If the queue pair is already in a state where there is no memory | |
- * registered for the queue pair (any *_NO_MEM state), it will transition to | |
- * the VMCIQPB_SHUTDOWN_NO_MEM state. This will also happen, if a guest | |
- * endpoint is the first of two endpoints to detach. If the host endpoint is | |
- * the first out of two to detach, the queue pair will move to the | |
- * VMCIQPB_SHUTDOWN_MEM state. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS on success, appropriate error code otherwise. | |
- * | |
- * Side effects: | |
- * Memory may be freed, and pages may be unpinned. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCIQPBroker_Detach(VMCIHandle handle, // IN | |
- VMCIContext *context) // IN | |
-{ | |
- QPBrokerEntry *entry; | |
- const VMCIId contextId = VMCIContext_GetId(context); | |
- VMCIId peerId; | |
- Bool isLocal = FALSE; | |
- int result; | |
- unsigned int index; | |
- | |
- if (VMCI_HANDLE_INVALID(handle) || !context || contextId == VMCI_INVALID_ID) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- VMCIQPBrokerLock(); | |
- | |
- if (!VMCIContext_QueuePairExists(context, handle)) { | |
- VMCI_DEBUG_LOG(4, (LGPFX"Context (ID=0x%x) not attached to queue pair " | |
- "(handle=0x%x:0x%x).\n", | |
- contextId, handle.context, handle.resource)); | |
- result = VMCI_ERROR_NOT_FOUND; | |
- goto out; | |
- } | |
- | |
- entry = (QPBrokerEntry *)QueuePairList_FindEntry(&qpBrokerList, handle); | |
- if (!entry) { | |
- VMCI_DEBUG_LOG(4, (LGPFX"Context (ID=0x%x) reports being attached to queue pair " | |
- "(handle=0x%x:0x%x) that isn't present in broker.\n", | |
- contextId, handle.context, handle.resource)); | |
- result = VMCI_ERROR_NOT_FOUND; | |
- goto out; | |
- } | |
- | |
- if (contextId != entry->createId && contextId != entry->attachId) { | |
- result = VMCI_ERROR_QUEUEPAIR_NOTATTACHED; | |
- goto out; | |
- } | |
- | |
- index = VMCIQPBEGetIndex(entry, contextId); | |
- | |
- if (contextId == entry->createId) { | |
- peerId = entry->attachId; | |
- entry->createId = VMCI_INVALID_ID; | |
- } else { | |
- peerId = entry->createId; | |
- entry->attachId = VMCI_INVALID_ID; | |
- } | |
- entry->qp.refCount--; | |
- | |
- isLocal = entry->qp.flags & VMCI_QPFLAG_LOCAL; | |
- | |
- if (contextId != VMCI_HOST_CONTEXT_ID) { | |
- int result; | |
- Bool headersMapped; | |
- | |
- ASSERT(!isLocal); | |
- | |
- /* | |
- * Pre NOVMVM vmx'en may detach from a queue pair before setting the page | |
- * store, and in that case there is no user memory to detach from. Also, | |
- * more recent VMX'en may detach from a queue pair in the quiesced state. | |
- */ | |
- | |
- VMCI_AcquireQueueMutex(entry->produceQ, TRUE); | |
- headersMapped = entry->produceQ->qHeader || entry->consumeQ->qHeader; | |
- if (entry->hasMem[index]) { | |
- VMCIHost_MarkQueuesUnavailable(index, entry->produceQ, entry->consumeQ); | |
- result = VMCIHost_UnmapQueues(index, INVALID_VMCI_GUEST_MEM_ID, | |
- entry->produceQ, | |
- entry->consumeQ); | |
- if (result < VMCI_SUCCESS) { | |
- VMCI_WARNING((LGPFX"Failed to unmap queue headers for queue pair " | |
- "(handle=0x%x:0x%x,result=%d).\n", handle.context, | |
- handle.resource, result)); | |
- } | |
- if (entry->vmciPageFiles) { | |
- VMCIHost_ReleaseUserMemory(index, entry->produceQ, entry->consumeQ); | |
- } else { | |
- VMCIHost_UnregisterUserMemory(index, entry->produceQ, entry->consumeQ); | |
- } | |
- entry->hasMem[index] = FALSE; | |
- } | |
- if (!headersMapped) { | |
- QueuePairResetSavedHeaders(entry); | |
- } | |
- if (index == 1) { | |
- entry->isVM2VM = FALSE; | |
- } | |
- VMCI_ReleaseQueueMutex(entry->produceQ); | |
- if (!headersMapped && entry->wakeupCB) { | |
- entry->wakeupCB(entry->clientData); | |
- } | |
- } else { | |
- if (entry->wakeupCB) { | |
- entry->wakeupCB = NULL; | |
- entry->clientData = NULL; | |
- } | |
- } | |
- | |
- if (entry->qp.refCount == 0) { | |
- QueuePairList_RemoveEntry(&qpBrokerList, &entry->qp); | |
- | |
- if (isLocal) { | |
- VMCI_FreeKernelMem(entry->localMem, QPE_NUM_PAGES(entry->qp) * PAGE_SIZE); | |
- entry->hasMem[0] = FALSE; | |
- } | |
- ASSERT(entry->hasMem[0] == FALSE); | |
- ASSERT(entry->hasMem[1] == FALSE); | |
- VMCI_CleanupQueueMutex(entry->produceQ, entry->consumeQ); | |
- VMCIHost_FreeQueue(entry->produceQ, entry->qp.produceSize); | |
- VMCIHost_FreeQueue(entry->consumeQ, entry->qp.consumeSize); | |
- VMCI_FreeKernelMem(entry, sizeof *entry); | |
- | |
- VMCIContext_QueuePairDestroy(context, handle); | |
- } else { | |
- ASSERT(peerId != VMCI_INVALID_ID); | |
- QueuePairNotifyPeer(FALSE, handle, contextId, peerId); | |
- entry->stateQP = VMCIQPB_SHUTDOWN; | |
- if (!isLocal) { | |
- VMCIContext_QueuePairDestroy(context, handle); | |
- } | |
- } | |
- result = VMCI_SUCCESS; | |
-out: | |
- VMCIQPBrokerUnlock(); | |
- return result; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIQPBroker_Map -- | |
- * | |
- * Establishes the necessary mappings for a queue pair given a | |
- * reference to the queue pair guest memory. This is usually | |
- * called when a guest is unquiesced and the VMX is allowed to | |
- * map guest memory once again. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS on success, appropriate error code otherwise. | |
- * | |
- * Side effects: | |
- * Memory may be allocated, and pages may be pinned. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCIQPBroker_Map(VMCIHandle handle, // IN | |
- VMCIContext *context, // IN | |
- VMCIQPGuestMem guestMem) // IN | |
-{ | |
- QPBrokerEntry *entry; | |
- const VMCIId contextId = VMCIContext_GetId(context); | |
- Bool isLocal = FALSE; | |
- int result; | |
- unsigned int endpoint; | |
- unsigned int index; | |
- | |
- if (VMCI_HANDLE_INVALID(handle) || !context || contextId == VMCI_INVALID_ID) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- VMCIQPBrokerLock(); | |
- | |
- if (!VMCIContext_QueuePairExists(context, handle)) { | |
- VMCI_DEBUG_LOG(4, (LGPFX"Context (ID=0x%x) not attached to queue pair " | |
- "(handle=0x%x:0x%x).\n", | |
- contextId, handle.context, handle.resource)); | |
- result = VMCI_ERROR_NOT_FOUND; | |
- goto out; | |
- } | |
- | |
- entry = (QPBrokerEntry *)QueuePairList_FindEntry(&qpBrokerList, handle); | |
- if (!entry) { | |
- VMCI_DEBUG_LOG(4, (LGPFX"Context (ID=0x%x) reports being attached to queue pair " | |
- "(handle=0x%x:0x%x) that isn't present in broker.\n", | |
- contextId, handle.context, handle.resource)); | |
- result = VMCI_ERROR_NOT_FOUND; | |
- goto out; | |
- } | |
- | |
- if (contextId != entry->createId && contextId != entry->attachId) { | |
- result = VMCI_ERROR_QUEUEPAIR_NOTATTACHED; | |
- goto out; | |
- } | |
- index = VMCIQPBEGetIndex(entry, contextId); | |
- endpoint = contextId == entry->createId ? 0 : 1; | |
- | |
- isLocal = entry->qp.flags & VMCI_QPFLAG_LOCAL; | |
- | |
- if (vmkernel) { | |
- /* | |
- * On vmkernel, the readiness of the queue pair can be signalled | |
- * immediately since the guest memory is already registered. | |
- */ | |
- | |
- VMCI_AcquireQueueMutex(entry->produceQ, TRUE); | |
- VMCIHost_MarkQueuesAvailable(index, entry->produceQ, entry->consumeQ); | |
- if (entry->qp.flags & VMCI_QPFLAG_NONBLOCK) { | |
- result = VMCIHost_MapQueues(index, entry->produceQ, entry->consumeQ, | |
- entry->qp.flags); | |
- } else { | |
- result = VMCI_SUCCESS; | |
- } | |
- if (result == VMCI_SUCCESS) { | |
- QueuePairResetSavedHeaders(entry); | |
- } | |
- VMCI_ReleaseQueueMutex(entry->produceQ); | |
- if (result == VMCI_SUCCESS) { | |
- if (entry->wakeupCB) { | |
- entry->wakeupCB(entry->clientData); | |
- } | |
- } | |
- } else if (contextId != VMCI_HOST_CONTEXT_ID) { | |
- QueuePairPageStore pageStore; | |
- | |
- ASSERT((entry->stateQP == VMCIQPB_CREATED || | |
- entry->stateQP == VMCIQPB_SHUTDOWN || | |
- entry->stateQP == VMCIQPB_ATTACHED) && !entry->hasMem[0]); | |
- ASSERT(!isLocal); | |
- | |
- pageStore.pages = guestMem; | |
- pageStore.len = QPE_NUM_PAGES(entry->qp); | |
- | |
- VMCI_AcquireQueueMutex(entry->produceQ, TRUE); | |
- QueuePairResetSavedHeaders(entry); | |
- result = VMCIHost_RegisterUserMemory(index, &pageStore, entry->produceQ, entry->consumeQ); | |
- VMCIHost_MarkQueuesAvailable(index, entry->produceQ, entry->consumeQ); | |
- VMCI_ReleaseQueueMutex(entry->produceQ); | |
- if (result == VMCI_SUCCESS) { | |
- entry->hasMem[index] = TRUE; | |
- if (entry->wakeupCB) { | |
- entry->wakeupCB(entry->clientData); | |
- } | |
- } | |
- } else { | |
- result = VMCI_SUCCESS; | |
- } | |
- | |
-out: | |
- VMCIQPBrokerUnlock(); | |
- return result; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * QueuePairSaveHeaders -- | |
- * | |
- * Saves a snapshot of the queue headers for the given QP broker | |
- * entry. Should be used when guest memory is unmapped. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS on success, appropriate error code if guest memory | |
- * can't be accessed.. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static int | |
-QueuePairSaveHeaders(QPBrokerEntry *entry, // IN | |
- VMCIId contextId) // IN | |
-{ | |
- int result; | |
- | |
- if (entry->produceQ->savedHeader != NULL && | |
- entry->consumeQ->savedHeader != NULL) { | |
- /* | |
- * If the headers have already been saved, we don't need to do | |
- * it again, and we don't want to map in the headers | |
- * unnecessarily. | |
- */ | |
- return VMCI_SUCCESS; | |
- } | |
- if (NULL == entry->produceQ->qHeader || NULL == entry->consumeQ->qHeader) { | |
- unsigned int index; | |
- | |
- /* | |
- * If this is second VM, do not save headers: qHeader is for VM which | |
- * created queue pair only. And no API which uses savedHeader should | |
- * be used together with VM2VM queue pair anyway. | |
- */ | |
- index = VMCIQPBEGetIndex(entry, contextId); | |
- if (index != 0) { | |
- return VMCI_SUCCESS; | |
- } | |
- result = VMCIHost_MapQueues(index, entry->produceQ, entry->consumeQ, 0); | |
- if (result < VMCI_SUCCESS) { | |
- return result; | |
- } | |
- } | |
- memcpy(&entry->savedProduceQ, entry->produceQ->qHeader, | |
- sizeof entry->savedProduceQ); | |
- entry->produceQ->savedHeader = &entry->savedProduceQ; | |
- memcpy(&entry->savedConsumeQ, entry->consumeQ->qHeader, | |
- sizeof entry->savedConsumeQ); | |
- entry->consumeQ->savedHeader = &entry->savedConsumeQ; | |
- | |
- return VMCI_SUCCESS; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * QueuePairResetSavedHeaders -- | |
- * | |
- * Resets saved queue headers for the given QP broker | |
- * entry. Should be used when guest memory becomes available | |
- * again, or the guest detaches. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static void | |
-QueuePairResetSavedHeaders(QPBrokerEntry *entry) // IN | |
-{ | |
- if (vmkernel) { | |
- VMCI_LockQueueHeader(entry->produceQ); | |
- } | |
- entry->produceQ->savedHeader = NULL; | |
- entry->consumeQ->savedHeader = NULL; | |
- if (vmkernel) { | |
- VMCI_UnlockQueueHeader(entry->produceQ); | |
- } | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIQPBroker_Unmap -- | |
- * | |
- * Removes all references to the guest memory of a given queue pair, and | |
- * will move the queue pair from state *_MEM to *_NO_MEM. It is usually | |
- * called when a VM is being quiesced where access to guest memory should | |
- * avoided. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS on success, appropriate error code otherwise. | |
- * | |
- * Side effects: | |
- * Memory may be freed, and pages may be unpinned. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCIQPBroker_Unmap(VMCIHandle handle, // IN | |
- VMCIContext *context, // IN | |
- VMCIGuestMemID gid) // IN | |
-{ | |
- QPBrokerEntry *entry; | |
- const VMCIId contextId = VMCIContext_GetId(context); | |
- Bool isLocal = FALSE; | |
- int result; | |
- unsigned int index; | |
- | |
- if (VMCI_HANDLE_INVALID(handle) || !context || contextId == VMCI_INVALID_ID) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- VMCIQPBrokerLock(); | |
- if (!VMCIContext_QueuePairExists(context, handle)) { | |
- VMCI_DEBUG_LOG(4, (LGPFX"Context (ID=0x%x) not attached to queue pair " | |
- "(handle=0x%x:0x%x).\n", | |
- contextId, handle.context, handle.resource)); | |
- result = VMCI_ERROR_NOT_FOUND; | |
- goto out; | |
- } | |
- | |
- entry = (QPBrokerEntry *)QueuePairList_FindEntry(&qpBrokerList, handle); | |
- if (!entry) { | |
- VMCI_DEBUG_LOG(4, (LGPFX"Context (ID=0x%x) reports being attached to queue pair " | |
- "(handle=0x%x:0x%x) that isn't present in broker.\n", | |
- contextId, handle.context, handle.resource)); | |
- result = VMCI_ERROR_NOT_FOUND; | |
- goto out; | |
- } | |
- | |
- if (contextId != entry->createId && contextId != entry->attachId) { | |
- result = VMCI_ERROR_QUEUEPAIR_NOTATTACHED; | |
- goto out; | |
- } | |
- index = VMCIQPBEGetIndex(entry, contextId); | |
- isLocal = entry->qp.flags & VMCI_QPFLAG_LOCAL; | |
- | |
- if (contextId != VMCI_HOST_CONTEXT_ID) { | |
- ASSERT(entry->hasMem[index]); | |
- ASSERT(!isLocal); | |
- | |
- VMCI_AcquireQueueMutex(entry->produceQ, TRUE); | |
- result = QueuePairSaveHeaders(entry, contextId); | |
- if (result < VMCI_SUCCESS) { | |
- VMCI_WARNING((LGPFX"Failed to save queue headers for queue pair " | |
- "(handle=0x%x:0x%x,result=%d).\n", handle.context, | |
- handle.resource, result)); | |
- } | |
- VMCIHost_MarkQueuesUnavailable(index, entry->produceQ, entry->consumeQ); | |
- VMCIHost_UnmapQueues(index, gid, entry->produceQ, entry->consumeQ); | |
- if (!vmkernel) { | |
- /* | |
- * On hosted, when we unmap queue pairs, the VMX will also | |
- * unmap the guest memory, so we invalidate the previously | |
- * registered memory. If the queue pair is mapped again at a | |
- * later point in time, we will need to reregister the user | |
- * memory with a possibly new user VA. | |
- */ | |
- | |
- VMCIHost_UnregisterUserMemory(index, entry->produceQ, entry->consumeQ); | |
- entry->hasMem[index] = FALSE; | |
- } | |
- | |
- VMCI_ReleaseQueueMutex(entry->produceQ); | |
- } | |
- | |
- result = VMCI_SUCCESS; | |
-out: | |
- VMCIQPBrokerUnlock(); | |
- return result; | |
-} | |
- | |
-#if !defined(VMKERNEL) | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIQPGuestEndpoints_Init -- | |
- * | |
- * Initalizes data structure state keeping track of queue pair | |
- * guest endpoints. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS on success and appropriate failure code otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCIQPGuestEndpoints_Init(void) | |
-{ | |
- int err; | |
- | |
- err = QueuePairList_Init(&qpGuestEndpoints); | |
- if (err < VMCI_SUCCESS) { | |
- return err; | |
- } | |
- | |
- hibernateFailedList = VMCIHandleArray_Create(0); | |
- if (NULL == hibernateFailedList) { | |
- QueuePairList_Destroy(&qpGuestEndpoints); | |
- return VMCI_ERROR_NO_MEM; | |
- } | |
- | |
- /* | |
- * The lock rank must be lower than subscriberLock in vmciEvent, | |
- * since we hold the hibernateFailedListLock while generating | |
- * detach events. | |
- */ | |
- | |
- err = VMCI_InitLock(&hibernateFailedListLock, "VMCIQPHibernateFailed", | |
- VMCI_LOCK_RANK_QPHIBERNATE); | |
- if (err < VMCI_SUCCESS) { | |
- VMCIHandleArray_Destroy(hibernateFailedList); | |
- hibernateFailedList = NULL; | |
- QueuePairList_Destroy(&qpGuestEndpoints); | |
- } | |
- | |
- return err; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIQPGuestEndpoints_Exit -- | |
- * | |
- * Destroys all guest queue pair endpoints. If active guest queue | |
- * pairs still exist, hypercalls to attempt detach from these | |
- * queue pairs will be made. Any failure to detach is silently | |
- * ignored. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCIQPGuestEndpoints_Exit(void) | |
-{ | |
- QPGuestEndpoint *entry; | |
- | |
- VMCIMutex_Acquire(&qpGuestEndpoints.mutex); | |
- | |
- while ((entry = (QPGuestEndpoint *)QueuePairList_GetHead(&qpGuestEndpoints))) { | |
- /* | |
- * Don't make a hypercall for local QueuePairs. | |
- */ | |
- if (!(entry->qp.flags & VMCI_QPFLAG_LOCAL)) { | |
- VMCIQueuePairDetachHypercall(entry->qp.handle); | |
- } | |
- /* | |
- * We cannot fail the exit, so let's reset refCount. | |
- */ | |
- entry->qp.refCount = 0; | |
- QueuePairList_RemoveEntry(&qpGuestEndpoints, &entry->qp); | |
- QPGuestEndpointDestroy(entry); | |
- } | |
- | |
- Atomic_Write(&qpGuestEndpoints.hibernate, 0); | |
- VMCIMutex_Release(&qpGuestEndpoints.mutex); | |
- QueuePairList_Destroy(&qpGuestEndpoints); | |
- VMCI_CleanupLock(&hibernateFailedListLock); | |
- VMCIHandleArray_Destroy(hibernateFailedList); | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIQPGuestEndpoints_Sync -- | |
- * | |
- * Use this as a synchronization point when setting globals, for example, | |
- * during device shutdown. | |
- * | |
- * Results: | |
- * TRUE. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCIQPGuestEndpoints_Sync(void) | |
-{ | |
- VMCIMutex_Acquire(&qpGuestEndpoints.mutex); | |
- VMCIMutex_Release(&qpGuestEndpoints.mutex); | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * QPGuestEndpointCreate -- | |
- * | |
- * Allocates and initializes a QPGuestEndpoint structure. | |
- * Allocates a QueuePair rid (and handle) iff the given entry has | |
- * an invalid handle. 0 through VMCI_RESERVED_RESOURCE_ID_MAX | |
- * are reserved handles. Assumes that the QP list mutex is held | |
- * by the caller. | |
- * | |
- * Results: | |
- * Pointer to structure intialized. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-QPGuestEndpoint * | |
-QPGuestEndpointCreate(VMCIHandle handle, // IN | |
- VMCIId peer, // IN | |
- uint32 flags, // IN | |
- uint64 produceSize, // IN | |
- uint64 consumeSize, // IN | |
- void *produceQ, // IN | |
- void *consumeQ) // IN | |
-{ | |
- static VMCIId queuePairRID = VMCI_RESERVED_RESOURCE_ID_MAX + 1; | |
- QPGuestEndpoint *entry; | |
- const uint64 numPPNs = CEILING(produceSize, PAGE_SIZE) + | |
- CEILING(consumeSize, PAGE_SIZE) + | |
- 2; /* One page each for the queue headers. */ | |
- | |
- ASSERT((produceSize || consumeSize) && produceQ && consumeQ); | |
- | |
- if (VMCI_HANDLE_INVALID(handle)) { | |
- VMCIId contextID = vmci_get_context_id(); | |
- VMCIId oldRID = queuePairRID; | |
- | |
- /* | |
- * Generate a unique QueuePair rid. Keep on trying until we wrap around | |
- * in the RID space. | |
- */ | |
- ASSERT(oldRID > VMCI_RESERVED_RESOURCE_ID_MAX); | |
- do { | |
- handle = VMCI_MAKE_HANDLE(contextID, queuePairRID); | |
- entry = (QPGuestEndpoint *)QueuePairList_FindEntry(&qpGuestEndpoints, | |
- handle); | |
- queuePairRID++; | |
- if (UNLIKELY(!queuePairRID)) { | |
- /* | |
- * Skip the reserved rids. | |
- */ | |
- queuePairRID = VMCI_RESERVED_RESOURCE_ID_MAX + 1; | |
- } | |
- } while (entry && queuePairRID != oldRID); | |
- | |
- if (UNLIKELY(entry != NULL)) { | |
- ASSERT(queuePairRID == oldRID); | |
- /* | |
- * We wrapped around --- no rids were free. | |
- */ | |
- return NULL; | |
- } | |
- } | |
- | |
- ASSERT(!VMCI_HANDLE_INVALID(handle) && | |
- QueuePairList_FindEntry(&qpGuestEndpoints, handle) == NULL); | |
- entry = VMCI_AllocKernelMem(sizeof *entry, VMCI_MEMORY_NORMAL); | |
- if (entry) { | |
- entry->qp.handle = handle; | |
- entry->qp.peer = peer; | |
- entry->qp.flags = flags; | |
- entry->qp.produceSize = produceSize; | |
- entry->qp.consumeSize = consumeSize; | |
- entry->qp.refCount = 0; | |
- entry->numPPNs = numPPNs; | |
- memset(&entry->ppnSet, 0, sizeof entry->ppnSet); | |
- entry->produceQ = produceQ; | |
- entry->consumeQ = consumeQ; | |
- VMCIList_InitEntry(&entry->qp.listItem); | |
- } | |
- return entry; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * QPGuestEndpointDestroy -- | |
- * | |
- * Frees a QPGuestEndpoint structure. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-QPGuestEndpointDestroy(QPGuestEndpoint *entry) // IN | |
-{ | |
- ASSERT(entry); | |
- ASSERT(entry->qp.refCount == 0); | |
- | |
- VMCI_FreePPNSet(&entry->ppnSet); | |
- VMCI_CleanupQueueMutex(entry->produceQ, entry->consumeQ); | |
- VMCI_FreeQueue(entry->produceQ, entry->qp.produceSize); | |
- VMCI_FreeQueue(entry->consumeQ, entry->qp.consumeSize); | |
- VMCI_FreeKernelMem(entry, sizeof *entry); | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIQueuePairAllocHypercall -- | |
- * | |
- * Helper to make a QueuePairAlloc hypercall when the driver is | |
- * supporting a guest device. | |
- * | |
- * Results: | |
- * Result of the hypercall. | |
- * | |
- * Side effects: | |
- * Memory is allocated & freed. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static int | |
-VMCIQueuePairAllocHypercall(const QPGuestEndpoint *entry) // IN | |
-{ | |
- VMCIQueuePairAllocMsg *allocMsg; | |
- size_t msgSize; | |
- int result; | |
- | |
- if (!entry || entry->numPPNs <= 2) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- ASSERT(!(entry->qp.flags & VMCI_QPFLAG_LOCAL)); | |
- | |
- msgSize = sizeof *allocMsg + (size_t)entry->numPPNs * sizeof(PPN); | |
- allocMsg = VMCI_AllocKernelMem(msgSize, VMCI_MEMORY_NONPAGED); | |
- if (!allocMsg) { | |
- return VMCI_ERROR_NO_MEM; | |
- } | |
- | |
- allocMsg->hdr.dst = VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID, | |
- VMCI_QUEUEPAIR_ALLOC); | |
- allocMsg->hdr.src = VMCI_ANON_SRC_HANDLE; | |
- allocMsg->hdr.payloadSize = msgSize - VMCI_DG_HEADERSIZE; | |
- allocMsg->handle = entry->qp.handle; | |
- allocMsg->peer = entry->qp.peer; | |
- allocMsg->flags = entry->qp.flags; | |
- allocMsg->produceSize = entry->qp.produceSize; | |
- allocMsg->consumeSize = entry->qp.consumeSize; | |
- allocMsg->numPPNs = entry->numPPNs; | |
- result = VMCI_PopulatePPNList((uint8 *)allocMsg + sizeof *allocMsg, &entry->ppnSet); | |
- if (result == VMCI_SUCCESS) { | |
- result = VMCI_SendDatagram((VMCIDatagram *)allocMsg); | |
- } | |
- VMCI_FreeKernelMem(allocMsg, msgSize); | |
- | |
- return result; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIQueuePairAllocGuestWork -- | |
- * | |
- * This functions handles the actual allocation of a VMCI queue | |
- * pair guest endpoint. Allocates physical pages for the queue | |
- * pair. It makes OS dependent calls through generic wrappers. | |
- * | |
- * Results: | |
- * Success or failure. | |
- * | |
- * Side effects: | |
- * Memory is allocated. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static int | |
-VMCIQueuePairAllocGuestWork(VMCIHandle *handle, // IN/OUT | |
- VMCIQueue **produceQ, // OUT | |
- uint64 produceSize, // IN | |
- VMCIQueue **consumeQ, // OUT | |
- uint64 consumeSize, // IN | |
- VMCIId peer, // IN | |
- uint32 flags, // IN | |
- VMCIPrivilegeFlags privFlags) // IN | |
- | |
-{ | |
- const uint64 numProducePages = CEILING(produceSize, PAGE_SIZE) + 1; | |
- const uint64 numConsumePages = CEILING(consumeSize, PAGE_SIZE) + 1; | |
- void *myProduceQ = NULL; | |
- void *myConsumeQ = NULL; | |
- int result; | |
- QPGuestEndpoint *queuePairEntry = NULL; | |
- | |
- /* | |
- * XXX Check for possible overflow of 'size' arguments when passed to | |
- * compat_get_order (after some arithmetic ops). | |
- */ | |
- | |
- ASSERT(handle && produceQ && consumeQ && (produceSize || consumeSize)); | |
- | |
- if (privFlags != VMCI_NO_PRIVILEGE_FLAGS) { | |
- return VMCI_ERROR_NO_ACCESS; | |
- } | |
- | |
- VMCIMutex_Acquire(&qpGuestEndpoints.mutex); | |
- | |
- /* Check if creation/attachment of a queuepair is allowed. */ | |
- if (!VMCI_CanCreate()) { | |
- result = VMCI_ERROR_UNAVAILABLE; | |
- goto error; | |
- } | |
- | |
- if ((Atomic_Read(&qpGuestEndpoints.hibernate) == 1) && | |
- !(flags & VMCI_QPFLAG_LOCAL)) { | |
- /* | |
- * While guest OS is in hibernate state, creating non-local | |
- * queue pairs is not allowed after the point where the VMCI | |
- * guest driver converted the existing queue pairs to local | |
- * ones. | |
- */ | |
- | |
- result = VMCI_ERROR_UNAVAILABLE; | |
- goto error; | |
- } | |
- | |
- if ((queuePairEntry = (QPGuestEndpoint *) | |
- QueuePairList_FindEntry(&qpGuestEndpoints, *handle))) { | |
- if (queuePairEntry->qp.flags & VMCI_QPFLAG_LOCAL) { | |
- /* Local attach case. */ | |
- if (queuePairEntry->qp.refCount > 1) { | |
- VMCI_DEBUG_LOG(4, (LGPFX"Error attempting to attach more than " | |
- "once.\n")); | |
- result = VMCI_ERROR_UNAVAILABLE; | |
- goto errorKeepEntry; | |
- } | |
- | |
- if (queuePairEntry->qp.produceSize != consumeSize || | |
- queuePairEntry->qp.consumeSize != produceSize || | |
- queuePairEntry->qp.flags != (flags & ~VMCI_QPFLAG_ATTACH_ONLY)) { | |
- VMCI_DEBUG_LOG(4, (LGPFX"Error mismatched queue pair in local " | |
- "attach.\n")); | |
- result = VMCI_ERROR_QUEUEPAIR_MISMATCH; | |
- goto errorKeepEntry; | |
- } | |
- | |
- /* | |
- * Do a local attach. We swap the consume and produce queues for the | |
- * attacher and deliver an attach event. | |
- */ | |
- result = QueuePairNotifyPeerLocal(TRUE, *handle); | |
- if (result < VMCI_SUCCESS) { | |
- goto errorKeepEntry; | |
- } | |
- myProduceQ = queuePairEntry->consumeQ; | |
- myConsumeQ = queuePairEntry->produceQ; | |
- goto out; | |
- } | |
- result = VMCI_ERROR_ALREADY_EXISTS; | |
- goto errorKeepEntry; | |
- } | |
- | |
- myProduceQ = VMCI_AllocQueue(produceSize, flags); | |
- if (!myProduceQ) { | |
- VMCI_WARNING((LGPFX"Error allocating pages for produce queue.\n")); | |
- result = VMCI_ERROR_NO_MEM; | |
- goto error; | |
- } | |
- | |
- myConsumeQ = VMCI_AllocQueue(consumeSize, flags); | |
- if (!myConsumeQ) { | |
- VMCI_WARNING((LGPFX"Error allocating pages for consume queue.\n")); | |
- result = VMCI_ERROR_NO_MEM; | |
- goto error; | |
- } | |
- | |
- queuePairEntry = QPGuestEndpointCreate(*handle, peer, flags, | |
- produceSize, consumeSize, | |
- myProduceQ, myConsumeQ); | |
- if (!queuePairEntry) { | |
- VMCI_WARNING((LGPFX"Error allocating memory in %s.\n", __FUNCTION__)); | |
- result = VMCI_ERROR_NO_MEM; | |
- goto error; | |
- } | |
- | |
- result = VMCI_AllocPPNSet(myProduceQ, numProducePages, myConsumeQ, | |
- numConsumePages, &queuePairEntry->ppnSet); | |
- if (result < VMCI_SUCCESS) { | |
- VMCI_WARNING((LGPFX"VMCI_AllocPPNSet failed.\n")); | |
- goto error; | |
- } | |
- | |
- /* | |
- * It's only necessary to notify the host if this queue pair will be | |
- * attached to from another context. | |
- */ | |
- if (queuePairEntry->qp.flags & VMCI_QPFLAG_LOCAL) { | |
- /* Local create case. */ | |
- VMCIId contextId = vmci_get_context_id(); | |
- | |
- /* | |
- * Enforce similar checks on local queue pairs as we do for regular ones. | |
- * The handle's context must match the creator or attacher context id | |
- * (here they are both the current context id) and the attach-only flag | |
- * cannot exist during create. We also ensure specified peer is this | |
- * context or an invalid one. | |
- */ | |
- if (queuePairEntry->qp.handle.context != contextId || | |
- (queuePairEntry->qp.peer != VMCI_INVALID_ID && | |
- queuePairEntry->qp.peer != contextId)) { | |
- result = VMCI_ERROR_NO_ACCESS; | |
- goto error; | |
- } | |
- | |
- if (queuePairEntry->qp.flags & VMCI_QPFLAG_ATTACH_ONLY) { | |
- result = VMCI_ERROR_NOT_FOUND; | |
- goto error; | |
- } | |
- } else { | |
- result = VMCIQueuePairAllocHypercall(queuePairEntry); | |
- if (result < VMCI_SUCCESS) { | |
- VMCI_WARNING((LGPFX"VMCIQueuePairAllocHypercall result = %d.\n", | |
- result)); | |
- goto error; | |
- } | |
- } | |
- | |
- VMCI_InitQueueMutex((VMCIQueue *)myProduceQ, (VMCIQueue *)myConsumeQ); | |
- | |
- QueuePairList_AddEntry(&qpGuestEndpoints, &queuePairEntry->qp); | |
- | |
-out: | |
- queuePairEntry->qp.refCount++; | |
- *handle = queuePairEntry->qp.handle; | |
- *produceQ = (VMCIQueue *)myProduceQ; | |
- *consumeQ = (VMCIQueue *)myConsumeQ; | |
- | |
- /* | |
- * We should initialize the queue pair header pages on a local queue pair | |
- * create. For non-local queue pairs, the hypervisor initializes the header | |
- * pages in the create step. | |
- */ | |
- if ((queuePairEntry->qp.flags & VMCI_QPFLAG_LOCAL) && | |
- queuePairEntry->qp.refCount == 1) { | |
- VMCIQueueHeader_Init((*produceQ)->qHeader, *handle); | |
- VMCIQueueHeader_Init((*consumeQ)->qHeader, *handle); | |
- } | |
- | |
- VMCIMutex_Release(&qpGuestEndpoints.mutex); | |
- | |
- return VMCI_SUCCESS; | |
- | |
-error: | |
- VMCIMutex_Release(&qpGuestEndpoints.mutex); | |
- if (queuePairEntry) { | |
- /* The queues will be freed inside the destroy routine. */ | |
- QPGuestEndpointDestroy(queuePairEntry); | |
- } else { | |
- if (myProduceQ) { | |
- VMCI_FreeQueue(myProduceQ, produceSize); | |
- } | |
- if (myConsumeQ) { | |
- VMCI_FreeQueue(myConsumeQ, consumeSize); | |
- } | |
- } | |
- return result; | |
- | |
-errorKeepEntry: | |
- /* This path should only be used when an existing entry was found. */ | |
- ASSERT(queuePairEntry->qp.refCount > 0); | |
- VMCIMutex_Release(&qpGuestEndpoints.mutex); | |
- return result; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIQueuePairDetachHypercall -- | |
- * | |
- * Helper to make a QueuePairDetach hypercall when the driver is | |
- * supporting a guest device. | |
- * | |
- * Results: | |
- * Result of the hypercall. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCIQueuePairDetachHypercall(VMCIHandle handle) // IN | |
-{ | |
- VMCIQueuePairDetachMsg detachMsg; | |
- | |
- detachMsg.hdr.dst = VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID, | |
- VMCI_QUEUEPAIR_DETACH); | |
- detachMsg.hdr.src = VMCI_ANON_SRC_HANDLE; | |
- detachMsg.hdr.payloadSize = sizeof handle; | |
- detachMsg.handle = handle; | |
- | |
- return VMCI_SendDatagram((VMCIDatagram *)&detachMsg); | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIQueuePairDetachGuestWork -- | |
- * | |
- * Helper for VMCI QueuePair detach interface. Frees the physical | |
- * pages for the queue pair. | |
- * | |
- * Results: | |
- * Success or failure. | |
- * | |
- * Side effects: | |
- * Memory may be freed. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static int | |
-VMCIQueuePairDetachGuestWork(VMCIHandle handle) // IN | |
-{ | |
- int result; | |
- QPGuestEndpoint *entry; | |
- uint32 refCount; | |
- | |
- ASSERT(!VMCI_HANDLE_INVALID(handle)); | |
- | |
- VMCIMutex_Acquire(&qpGuestEndpoints.mutex); | |
- | |
- entry = (QPGuestEndpoint *)QueuePairList_FindEntry(&qpGuestEndpoints, handle); | |
- if (!entry) { | |
- VMCIMutex_Release(&qpGuestEndpoints.mutex); | |
- return VMCI_ERROR_NOT_FOUND; | |
- } | |
- | |
- ASSERT(entry->qp.refCount >= 1); | |
- | |
- if (entry->qp.flags & VMCI_QPFLAG_LOCAL) { | |
- result = VMCI_SUCCESS; | |
- | |
- if (entry->qp.refCount > 1) { | |
- result = QueuePairNotifyPeerLocal(FALSE, handle); | |
- /* | |
- * We can fail to notify a local queuepair because we can't allocate. | |
- * We still want to release the entry if that happens, so don't bail | |
- * out yet. | |
- */ | |
- } | |
- } else { | |
- result = VMCIQueuePairDetachHypercall(handle); | |
- if (entry->hibernateFailure) { | |
- if (result == VMCI_ERROR_NOT_FOUND) { | |
- /* | |
- * If a queue pair detach failed when entering | |
- * hibernation, the guest driver and the device may | |
- * disagree on its existence when coming out of | |
- * hibernation. The guest driver will regard it as a | |
- * non-local queue pair, but the device state is gone, | |
- * since the device has been powered off. In this case, we | |
- * treat the queue pair as a local queue pair with no | |
- * peer. | |
- */ | |
- | |
- ASSERT(entry->qp.refCount == 1); | |
- result = VMCI_SUCCESS; | |
- } | |
- if (result == VMCI_SUCCESS) { | |
- VMCIQPUnmarkHibernateFailed(entry); | |
- } | |
- } | |
- if (result < VMCI_SUCCESS) { | |
- /* | |
- * We failed to notify a non-local queuepair. That other queuepair | |
- * might still be accessing the shared memory, so don't release the | |
- * entry yet. It will get cleaned up by VMCIQueuePair_Exit() | |
- * if necessary (assuming we are going away, otherwise why did this | |
- * fail?). | |
- */ | |
- | |
- VMCIMutex_Release(&qpGuestEndpoints.mutex); | |
- return result; | |
- } | |
- } | |
- | |
- /* | |
- * If we get here then we either failed to notify a local queuepair, or | |
- * we succeeded in all cases. Release the entry if required. | |
- */ | |
- | |
- entry->qp.refCount--; | |
- if (entry->qp.refCount == 0) { | |
- QueuePairList_RemoveEntry(&qpGuestEndpoints, &entry->qp); | |
- } | |
- | |
- /* If we didn't remove the entry, this could change once we unlock. */ | |
- refCount = entry ? entry->qp.refCount : | |
- 0xffffffff; /* | |
- * Value does not matter, silence the | |
- * compiler. | |
- */ | |
- | |
- VMCIMutex_Release(&qpGuestEndpoints.mutex); | |
- | |
- if (refCount == 0) { | |
- QPGuestEndpointDestroy(entry); | |
- } | |
- return result; | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------------- | |
- * | |
- * QueuePairNotifyPeerLocal -- | |
- * | |
- * Dispatches a queue pair event message directly into the local event | |
- * queue. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS on success, error code otherwise | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------------- | |
- */ | |
- | |
-static int | |
-QueuePairNotifyPeerLocal(Bool attach, // IN: attach or detach? | |
- VMCIHandle handle) // IN: queue pair handle | |
-{ | |
- VMCIEventMsg *eMsg; | |
- VMCIEventPayload_QP *ePayload; | |
- /* buf is only 48 bytes. */ | |
- char buf[sizeof *eMsg + sizeof *ePayload]; | |
- VMCIId contextId = vmci_get_context_id(); | |
- | |
- eMsg = (VMCIEventMsg *)buf; | |
- ePayload = VMCIEventMsgPayload(eMsg); | |
- | |
- eMsg->hdr.dst = VMCI_MAKE_HANDLE(contextId, VMCI_EVENT_HANDLER); | |
- eMsg->hdr.src = VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID, | |
- VMCI_CONTEXT_RESOURCE_ID); | |
- eMsg->hdr.payloadSize = sizeof *eMsg + sizeof *ePayload - sizeof eMsg->hdr; | |
- eMsg->eventData.event = attach ? VMCI_EVENT_QP_PEER_ATTACH : | |
- VMCI_EVENT_QP_PEER_DETACH; | |
- ePayload->peerId = contextId; | |
- ePayload->handle = handle; | |
- | |
- return VMCIEvent_Dispatch((VMCIDatagram *)eMsg); | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIQPMarkHibernateFailed -- | |
- * | |
- * Helper function that marks a queue pair entry as not being | |
- * converted to a local version during hibernation. Must be | |
- * called with the queue pair list mutex held. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static void | |
-VMCIQPMarkHibernateFailed(QPGuestEndpoint *entry) // IN | |
-{ | |
- VMCILockFlags flags; | |
- VMCIHandle handle; | |
- | |
- /* | |
- * entry->handle is located in paged memory, so it can't be | |
- * accessed while holding a spinlock. | |
- */ | |
- | |
- handle = entry->qp.handle; | |
- entry->hibernateFailure = TRUE; | |
- VMCI_GrabLock_BH(&hibernateFailedListLock, &flags); | |
- VMCIHandleArray_AppendEntry(&hibernateFailedList, handle); | |
- VMCI_ReleaseLock_BH(&hibernateFailedListLock, flags); | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIQPUnmarkHibernateFailed -- | |
- * | |
- * Helper function that removes a queue pair entry from the group | |
- * of handles marked as having failed hibernation. Must be called | |
- * with the queue pair list lock held. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static void | |
-VMCIQPUnmarkHibernateFailed(QPGuestEndpoint *entry) // IN | |
-{ | |
- VMCILockFlags flags; | |
- VMCIHandle handle; | |
- | |
- /* | |
- * entry->handle is located in paged memory, so it can't be | |
- * accessed while holding a spinlock. | |
- */ | |
- | |
- handle = entry->qp.handle; | |
- entry->hibernateFailure = FALSE; | |
- VMCI_GrabLock_BH(&hibernateFailedListLock, &flags); | |
- VMCIHandleArray_RemoveEntry(hibernateFailedList, handle); | |
- VMCI_ReleaseLock_BH(&hibernateFailedListLock, flags); | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------------- | |
- * | |
- * VMCIQPGuestEndpoints_Convert -- | |
- * | |
- * Guest queue pair endpoints may be converted to local ones in | |
- * two cases: when entering hibernation or when the device is | |
- * powered off before entering a sleep mode. Below we first | |
- * discuss the case of hibernation and then the case of entering | |
- * sleep state. | |
- * | |
- * When the guest enters hibernation, any non-local queue pairs | |
- * will disconnect no later than at the time the VMCI device | |
- * powers off. To preserve the content of the non-local queue | |
- * pairs for this guest, we make a local copy of the content and | |
- * disconnect from the queue pairs. This will ensure that the | |
- * peer doesn't continue to update the queue pair state while the | |
- * guest OS is checkpointing the memory (otherwise we might end | |
- * up with a inconsistent snapshot where the pointers of the | |
- * consume queue are checkpointed later than the data pages they | |
- * point to, possibly indicating that non-valid data is | |
- * valid). While we are in hibernation mode, we block the | |
- * allocation of new non-local queue pairs. Note that while we | |
- * are doing the conversion to local queue pairs, we are holding | |
- * the queue pair list lock, which will prevent concurrent | |
- * creation of additional non-local queue pairs. | |
- * | |
- * The hibernation cannot fail, so if we are unable to either | |
- * save the queue pair state or detach from a queue pair, we deal | |
- * with it by keeping the queue pair around, and converting it to | |
- * a local queue pair when going out of hibernation. Since | |
- * failing a detach is highly unlikely (it would require a queue | |
- * pair being actively used as part of a DMA operation), this is | |
- * an acceptable fall back. Once we come back from hibernation, | |
- * these queue pairs will no longer be external, so we simply | |
- * mark them as local at that point. | |
- * | |
- * For the sleep state, the VMCI device will also be put into the | |
- * D3 power state, which may make the device inaccessible to the | |
- * guest driver (Windows unmaps the I/O space). When entering | |
- * sleep state, the hypervisor is likely to suspend the guest as | |
- * well, which will again convert all queue pairs to local ones. | |
- * However, VMCI device clients, e.g., VMCI Sockets, may attempt | |
- * to use queue pairs after the device has been put into the D3 | |
- * power state, so we convert the queue pairs to local ones in | |
- * that case as well. When exiting the sleep states, the device | |
- * has not been reset, so all device state is still in sync with | |
- * the device driver, so no further processing is necessary at | |
- * that point. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * Queue pairs are detached. | |
- * | |
- *---------------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCIQPGuestEndpoints_Convert(Bool toLocal, // IN | |
- Bool deviceReset) // IN | |
-{ | |
- if (toLocal) { | |
- VMCIListItem *next; | |
- | |
- VMCIMutex_Acquire(&qpGuestEndpoints.mutex); | |
- | |
- VMCIList_Scan(next, &qpGuestEndpoints.head) { | |
- QPGuestEndpoint *entry = (QPGuestEndpoint *)VMCIList_Entry( | |
- next, | |
- QueuePairEntry, | |
- listItem); | |
- | |
- if (!(entry->qp.flags & VMCI_QPFLAG_LOCAL)) { | |
- UNUSED_PARAM(VMCIQueue *prodQ); // Only used on Win32 | |
- UNUSED_PARAM(VMCIQueue *consQ); // Only used on Win32 | |
- void *oldProdQ; | |
- UNUSED_PARAM(void *oldConsQ); // Only used on Win32 | |
- int result; | |
- | |
- prodQ = (VMCIQueue *)entry->produceQ; | |
- consQ = (VMCIQueue *)entry->consumeQ; | |
- oldConsQ = oldProdQ = NULL; | |
- | |
- VMCI_AcquireQueueMutex(prodQ, TRUE); | |
- | |
- result = VMCI_ConvertToLocalQueue(consQ, prodQ, | |
- entry->qp.consumeSize, | |
- TRUE, &oldConsQ); | |
- if (result != VMCI_SUCCESS) { | |
- VMCI_WARNING((LGPFX"Hibernate failed to create local consume " | |
- "queue from handle %x:%x (error: %d)\n", | |
- entry->qp.handle.context, entry->qp.handle.resource, | |
- result)); | |
- VMCI_ReleaseQueueMutex(prodQ); | |
- VMCIQPMarkHibernateFailed(entry); | |
- continue; | |
- } | |
- result = VMCI_ConvertToLocalQueue(prodQ, consQ, | |
- entry->qp.produceSize, | |
- FALSE, &oldProdQ); | |
- if (result != VMCI_SUCCESS) { | |
- VMCI_WARNING((LGPFX"Hibernate failed to create local produce " | |
- "queue from handle %x:%x (error: %d)\n", | |
- entry->qp.handle.context, entry->qp.handle.resource, | |
- result)); | |
- VMCI_RevertToNonLocalQueue(consQ, oldConsQ, | |
- entry->qp.consumeSize); | |
- VMCI_ReleaseQueueMutex(prodQ); | |
- VMCIQPMarkHibernateFailed(entry); | |
- continue; | |
- } | |
- | |
- /* | |
- * Now that the contents of the queue pair has been saved, | |
- * we can detach from the non-local queue pair. This will | |
- * discard the content of the non-local queues. | |
- */ | |
- | |
- result = VMCIQueuePairDetachHypercall(entry->qp.handle); | |
- if (result < VMCI_SUCCESS) { | |
- VMCI_WARNING((LGPFX"Hibernate failed to detach from handle " | |
- "%x:%x\n", | |
- entry->qp.handle.context, | |
- entry->qp.handle.resource)); | |
- VMCI_RevertToNonLocalQueue(consQ, oldConsQ, | |
- entry->qp.consumeSize); | |
- VMCI_RevertToNonLocalQueue(prodQ, oldProdQ, | |
- entry->qp.produceSize); | |
- VMCI_ReleaseQueueMutex(prodQ); | |
- VMCIQPMarkHibernateFailed(entry); | |
- continue; | |
- } | |
- | |
- entry->qp.flags |= VMCI_QPFLAG_LOCAL; | |
- | |
- VMCI_ReleaseQueueMutex(prodQ); | |
- | |
- VMCI_FreeQueueBuffer(oldProdQ, entry->qp.produceSize); | |
- VMCI_FreeQueueBuffer(oldConsQ, entry->qp.consumeSize); | |
- | |
- QueuePairNotifyPeerLocal(FALSE, entry->qp.handle); | |
- } | |
- } | |
- Atomic_Write(&qpGuestEndpoints.hibernate, 1); | |
- | |
- VMCIMutex_Release(&qpGuestEndpoints.mutex); | |
- } else { | |
- VMCILockFlags flags; | |
- VMCIHandle handle; | |
- | |
- /* | |
- * When a guest enters hibernation, there may be queue pairs | |
- * around, that couldn't be converted to local queue | |
- * pairs. When coming out of hibernation, these queue pairs | |
- * will be restored as part of the guest main mem by the OS | |
- * hibernation code and they can now be regarded as local | |
- * versions. Since they are no longer connected, detach | |
- * notifications are sent to the local endpoint. | |
- */ | |
- | |
- VMCI_GrabLock_BH(&hibernateFailedListLock, &flags); | |
- while (VMCIHandleArray_GetSize(hibernateFailedList) > 0) { | |
- handle = VMCIHandleArray_RemoveTail(hibernateFailedList); | |
- if (deviceReset) { | |
- QueuePairNotifyPeerLocal(FALSE, handle); | |
- } | |
- } | |
- VMCI_ReleaseLock_BH(&hibernateFailedListLock, flags); | |
- | |
- Atomic_Write(&qpGuestEndpoints.hibernate, 0); | |
- } | |
-} | |
- | |
-#endif /* !VMKERNEL */ | |
diff --git a/modules/linux/vmci/common/vmciQueuePair.h b/modules/linux/vmci/common/vmciQueuePair.h | |
deleted file mode 100644 | |
index 9c0be14..0000000 | |
--- a/modules/linux/vmci/common/vmciQueuePair.h | |
+++ /dev/null | |
@@ -1,104 +0,0 @@ | |
-/********************************************************* | |
- * Copyright (C) 2007 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 as published by the | |
- * Free Software Foundation version 2 and no 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 | |
- * | |
- *********************************************************/ | |
- | |
-/* | |
- * vmciQueuePair.h -- | |
- * | |
- * VMCI QueuePair API implementation in the host driver. | |
- */ | |
- | |
-#ifndef _VMCI_QUEUE_PAIR_H_ | |
-#define _VMCI_QUEUE_PAIR_H_ | |
- | |
-#define INCLUDE_ALLOW_MODULE | |
-#define INCLUDE_ALLOW_VMMON | |
-#define INCLUDE_ALLOW_VMCORE | |
-#define INCLUDE_ALLOW_VMKERNEL | |
-#include "includeCheck.h" | |
- | |
-#include "vmci_defs.h" | |
-#include "vmci_iocontrols.h" | |
-#include "vmci_kernel_if.h" | |
-#include "vmciContext.h" | |
-#include "vmciQueue.h" | |
- | |
-/* | |
- * QueuePairPageStore describes how the memory of a given queue pair | |
- * is backed. When the queue pair is between the host and a guest, the | |
- * page store consists of references to the guest pages. On vmkernel, | |
- * this is a list of PPNs, and on hosted, it is a user VA where the | |
- * queue pair is mapped into the VMX address space. | |
- */ | |
- | |
-typedef struct QueuePairPageStore { | |
- VMCIQPGuestMem pages; // Reference to pages backing the queue pair. | |
- uint32 len; // Length of pageList/virtual addres range (in pages). | |
-} QueuePairPageStore; | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCI_QP_PAGESTORE_IS_WELLFORMED -- | |
- * | |
- * Utility function that checks whether the fields of the page | |
- * store contain valid values. | |
- * | |
- * Result: | |
- * TRUE if the page store is wellformed. FALSE otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-static INLINE Bool | |
-VMCI_QP_PAGESTORE_IS_WELLFORMED(QueuePairPageStore *pageStore) // IN | |
-{ | |
- return pageStore->len >= 2; | |
-} | |
- | |
-int VMCIQPBroker_Init(void); | |
-void VMCIQPBroker_Exit(void); | |
-int VMCIQPBroker_Alloc(VMCIHandle handle, VMCIId peer, uint32 flags, | |
- VMCIPrivilegeFlags privFlags, | |
- uint64 produceSize, uint64 consumeSize, | |
- QueuePairPageStore *pageStore, | |
- VMCIContext *context); | |
-int VMCIQPBroker_SetPageStore(VMCIHandle handle, VA64 produceUVA, VA64 consumeUVA, | |
- VMCIContext *context); | |
-int VMCIQPBroker_Detach(VMCIHandle handle, VMCIContext *context); | |
- | |
-int VMCIQPGuestEndpoints_Init(void); | |
-void VMCIQPGuestEndpoints_Exit(void); | |
-void VMCIQPGuestEndpoints_Sync(void); | |
-void VMCIQPGuestEndpoints_Convert(Bool toLocal, Bool deviceReset); | |
- | |
-int VMCIQueuePair_Alloc(VMCIHandle *handle, VMCIQueue **produceQ, | |
- uint64 produceSize, VMCIQueue **consumeQ, | |
- uint64 consumeSize, VMCIId peer, uint32 flags, | |
- VMCIPrivilegeFlags privFlags, Bool guestEndpoint, | |
- VMCIEventReleaseCB wakeupCB, void *clientData); | |
-int VMCIQueuePair_Detach(VMCIHandle handle, Bool guestEndpoint); | |
-int VMCIQPBroker_Map(VMCIHandle handle, VMCIContext *context, VMCIQPGuestMem guestMem); | |
-int VMCIQPBroker_Unmap(VMCIHandle handle, VMCIContext *context, VMCIGuestMemID gid); | |
- | |
- | |
-#endif /* !_VMCI_QUEUE_PAIR_H_ */ | |
- | |
diff --git a/modules/linux/vmci/common/vmciResource.c b/modules/linux/vmci/common/vmciResource.c | |
deleted file mode 100644 | |
index 720924b..0000000 | |
--- a/modules/linux/vmci/common/vmciResource.c | |
+++ /dev/null | |
@@ -1,424 +0,0 @@ | |
-/********************************************************* | |
- * Copyright (C) 2006 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 as published by the | |
- * Free Software Foundation version 2 and no 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 | |
- * | |
- *********************************************************/ | |
- | |
-/* | |
- * vmciResource.c -- | |
- * | |
- * Implementation of the VMCI Resource Access Control API. | |
- */ | |
- | |
-#include "vmci_kernel_if.h" | |
-#include "vm_assert.h" | |
-#include "vmci_defs.h" | |
-#include "vmci_infrastructure.h" | |
-#include "vmciCommonInt.h" | |
-#include "vmciHashtable.h" | |
-#include "vmciResource.h" | |
-#if defined(VMKERNEL) | |
-# include "vmciVmkInt.h" | |
-# include "vm_libc.h" | |
-# include "helper_ext.h" | |
-# include "vmciDriver.h" | |
-#else | |
-# include "vmciDriver.h" | |
-#endif | |
- | |
-#define LGPFX "VMCIResource: " | |
- | |
-/* 0 through VMCI_RESERVED_RESOURCE_ID_MAX are reserved. */ | |
-static uint32 resourceID = VMCI_RESERVED_RESOURCE_ID_MAX + 1; | |
-static VMCILock resourceIdLock; | |
- | |
-static void VMCIResourceDoRemove(VMCIResource *resource); | |
- | |
-static VMCIHashTable *resourceTable = NULL; | |
- | |
- | |
-/* Public Resource Access Control API. */ | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCIResource_Init -- | |
- * | |
- * Initializes the VMCI Resource Access Control API. Creates a hashtable | |
- * to hold all resources, and registers vectors and callbacks for | |
- * hypercalls. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-int | |
-VMCIResource_Init(void) | |
-{ | |
- int err = VMCI_InitLock(&resourceIdLock, "VMCIRIDLock", | |
- VMCI_LOCK_RANK_RESOURCE); | |
- if (err < VMCI_SUCCESS) { | |
- return err; | |
- } | |
- | |
- resourceTable = VMCIHashTable_Create(128); | |
- if (resourceTable == NULL) { | |
- VMCI_WARNING((LGPFX"Failed creating a resource hash table for VMCI.\n")); | |
- VMCI_CleanupLock(&resourceIdLock); | |
- return VMCI_ERROR_NO_MEM; | |
- } | |
- | |
- return VMCI_SUCCESS; | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCIResource_Exit -- | |
- * | |
- * Cleans up resources. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-void | |
-VMCIResource_Exit(void) | |
-{ | |
- /* Cleanup resources.*/ | |
- VMCI_CleanupLock(&resourceIdLock); | |
- | |
- if (resourceTable) { | |
- VMCIHashTable_Destroy(resourceTable); | |
- } | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCIResource_GetID -- | |
- * | |
- * Return resource ID. The first VMCI_RESERVED_RESOURCE_ID_MAX are | |
- * reserved so we start from its value + 1. | |
- * | |
- * Result: | |
- * VMCI resource id on success, VMCI_INVALID_ID on failure. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-VMCIId | |
-VMCIResource_GetID(VMCIId contextID) | |
-{ | |
- VMCIId oldRID = resourceID; | |
- VMCIId currentRID; | |
- Bool foundRID = FALSE; | |
- | |
- /* | |
- * Generate a unique resource ID. Keep on trying until we wrap around | |
- * in the RID space. | |
- */ | |
- ASSERT(oldRID > VMCI_RESERVED_RESOURCE_ID_MAX); | |
- | |
- do { | |
- VMCILockFlags flags; | |
- VMCIHandle handle; | |
- | |
- VMCI_GrabLock(&resourceIdLock, &flags); | |
- currentRID = resourceID; | |
- handle = VMCI_MAKE_HANDLE(contextID, currentRID); | |
- resourceID++; | |
- if (UNLIKELY(resourceID == VMCI_INVALID_ID)) { | |
- /* | |
- * Skip the reserved rids. | |
- */ | |
- | |
- resourceID = VMCI_RESERVED_RESOURCE_ID_MAX + 1; | |
- } | |
- VMCI_ReleaseLock(&resourceIdLock, flags); | |
- foundRID = !VMCIHashTable_EntryExists(resourceTable, handle); | |
- } while (!foundRID && resourceID != oldRID); | |
- | |
- if (UNLIKELY(!foundRID)) { | |
- return VMCI_INVALID_ID; | |
- } else { | |
- return currentRID; | |
- } | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCIResource_Add -- | |
- * | |
- * Results: | |
- * VMCI_SUCCESS if successful, error code if not. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-int | |
-VMCIResource_Add(VMCIResource *resource, // IN | |
- VMCIResourceType resourceType, // IN | |
- VMCIHandle resourceHandle, // IN | |
- VMCIResourceFreeCB containerFreeCB, // IN | |
- void *containerObject) // IN | |
-{ | |
- int result; | |
- | |
- ASSERT(resource); | |
- | |
- if (VMCI_HANDLE_EQUAL(resourceHandle, VMCI_INVALID_HANDLE)) { | |
- VMCI_DEBUG_LOG(4, (LGPFX"Invalid argument resource (handle=0x%x:0x%x).\n", | |
- resourceHandle.context, resourceHandle.resource)); | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- VMCIHashTable_InitEntry(&resource->hashEntry, resourceHandle); | |
- resource->type = resourceType; | |
- resource->containerFreeCB = containerFreeCB; | |
- resource->containerObject = containerObject; | |
- | |
- /* Add resource to hashtable. */ | |
- result = VMCIHashTable_AddEntry(resourceTable, &resource->hashEntry); | |
- if (result != VMCI_SUCCESS) { | |
- VMCI_DEBUG_LOG(4, (LGPFX"Failed to add entry to hash table " | |
- "(result=%d).\n", result)); | |
- return result; | |
- } | |
- | |
- return result; | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCIResource_Remove -- | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-void | |
-VMCIResource_Remove(VMCIHandle resourceHandle, // IN: | |
- VMCIResourceType resourceType) // IN: | |
-{ | |
- VMCIResource *resource = VMCIResource_Get(resourceHandle, resourceType); | |
- if (resource == NULL) { | |
- return; | |
- } | |
- | |
- /* Remove resource from hashtable. */ | |
- VMCIHashTable_RemoveEntry(resourceTable, &resource->hashEntry); | |
- | |
- VMCIResource_Release(resource); | |
- /* resource could be freed by now. */ | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCIResource_Get -- | |
- * | |
- * Results: | |
- * Resource is successful. Otherwise NULL. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-VMCIResource * | |
-VMCIResource_Get(VMCIHandle resourceHandle, // IN | |
- VMCIResourceType resourceType) // IN | |
-{ | |
- VMCIResource *resource; | |
- VMCIHashEntry *entry = VMCIHashTable_GetEntry(resourceTable, resourceHandle); | |
- if (entry == NULL) { | |
- return NULL; | |
- } | |
- resource = RESOURCE_CONTAINER(entry, VMCIResource, hashEntry); | |
- if (resourceType == VMCI_RESOURCE_TYPE_ANY || | |
- resource->type == resourceType) { | |
- return resource; | |
- } | |
- VMCIHashTable_ReleaseEntry(resourceTable, entry); | |
- return NULL; | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCIResource_Hold -- | |
- * | |
- * Hold the given resource. This will hold the hashtable entry. This | |
- * is like doing a Get() but without having to lookup the resource by | |
- * handle. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-void | |
-VMCIResource_Hold(VMCIResource *resource) | |
-{ | |
- ASSERT(resource); | |
- VMCIHashTable_HoldEntry(resourceTable, &resource->hashEntry); | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCIResourceDoRemove -- | |
- * | |
- * Deallocates data structures associated with the given resource | |
- * and invoke any call back registered for the resource. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * May deallocate memory and invoke a callback for the removed resource. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-static void INLINE | |
-VMCIResourceDoRemove(VMCIResource *resource) | |
-{ | |
- ASSERT(resource); | |
- | |
- if (resource->containerFreeCB) { | |
- resource->containerFreeCB(resource->containerObject); | |
- /* Resource has been freed don't dereference it. */ | |
- } | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCIResource_Release -- | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * resource's containerFreeCB will get called if last reference. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-int | |
-VMCIResource_Release(VMCIResource *resource) | |
-{ | |
- int result; | |
- | |
- ASSERT(resource); | |
- | |
- result = VMCIHashTable_ReleaseEntry(resourceTable, &resource->hashEntry); | |
- if (result == VMCI_SUCCESS_ENTRY_DEAD) { | |
- VMCIResourceDoRemove(resource); | |
- } | |
- | |
- /* | |
- * We propagate the information back to caller in case it wants to know | |
- * whether entry was freed. | |
- */ | |
- return result; | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCIResource_Handle -- | |
- * | |
- * Get the handle for the given resource. | |
- * | |
- * Results: | |
- * The resource's associated handle. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-VMCIHandle | |
-VMCIResource_Handle(VMCIResource *resource) | |
-{ | |
- ASSERT(resource); | |
- return resource->hashEntry.handle; | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCIResource_Sync -- | |
- * | |
- * Use this as a synchronization point when setting globals, for example, | |
- * during device shutdown. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-void | |
-VMCIResource_Sync(void) | |
-{ | |
- VMCIHashTable_Sync(resourceTable); | |
-} | |
diff --git a/modules/linux/vmci/common/vmciResource.h b/modules/linux/vmci/common/vmciResource.h | |
deleted file mode 100644 | |
index da056d1..0000000 | |
--- a/modules/linux/vmci/common/vmciResource.h | |
+++ /dev/null | |
@@ -1,79 +0,0 @@ | |
-/********************************************************* | |
- * Copyright (C) 2006 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 as published by the | |
- * Free Software Foundation version 2 and no 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 | |
- * | |
- *********************************************************/ | |
- | |
-/* | |
- * vmciResource.h -- | |
- * | |
- * VMCI Resource Access Control API. | |
- */ | |
- | |
-#ifndef _VMCI_RESOURCE_H_ | |
-#define _VMCI_RESOURCE_H_ | |
- | |
-#define INCLUDE_ALLOW_VMMON | |
-#define INCLUDE_ALLOW_VMCORE | |
-#define INCLUDE_ALLOW_MODULE | |
-#define INCLUDE_ALLOW_VMKERNEL | |
-#include "includeCheck.h" | |
- | |
-#include "vmci_defs.h" | |
-#include "vmci_kernel_if.h" | |
-#include "vmciHashtable.h" | |
-#include "vmciContext.h" | |
- | |
-#define RESOURCE_CONTAINER(ptr, type, member) \ | |
- ((type *)((char *)(ptr) - offsetof(type, member))) | |
- | |
-typedef void(*VMCIResourceFreeCB)(void *resource); | |
- | |
-typedef enum { | |
- VMCI_RESOURCE_TYPE_ANY, | |
- VMCI_RESOURCE_TYPE_API, | |
- VMCI_RESOURCE_TYPE_GROUP, | |
- VMCI_RESOURCE_TYPE_DATAGRAM, | |
- VMCI_RESOURCE_TYPE_DOORBELL, | |
-} VMCIResourceType; | |
- | |
-typedef struct VMCIResource { | |
- VMCIHashEntry hashEntry; | |
- VMCIResourceType type; | |
- VMCIResourceFreeCB containerFreeCB; // Callback to free container | |
- // object when refCount is 0. | |
- void *containerObject; // Container object reference. | |
-} VMCIResource; | |
- | |
- | |
-int VMCIResource_Init(void); | |
-void VMCIResource_Exit(void); | |
-void VMCIResource_Sync(void); | |
- | |
-VMCIId VMCIResource_GetID(VMCIId contextID); | |
- | |
-int VMCIResource_Add(VMCIResource *resource, VMCIResourceType resourceType, | |
- VMCIHandle resourceHandle, | |
- VMCIResourceFreeCB containerFreeCB, void *containerObject); | |
-void VMCIResource_Remove(VMCIHandle resourceHandle, | |
- VMCIResourceType resourceType); | |
-VMCIResource *VMCIResource_Get(VMCIHandle resourceHandle, | |
- VMCIResourceType resourceType); | |
-void VMCIResource_Hold(VMCIResource *resource); | |
-int VMCIResource_Release(VMCIResource *resource); | |
-VMCIHandle VMCIResource_Handle(VMCIResource *resource); | |
- | |
- | |
-#endif // _VMCI_RESOURCE_H_ | |
diff --git a/modules/linux/vmci/common/vmciRoute.c b/modules/linux/vmci/common/vmciRoute.c | |
deleted file mode 100644 | |
index 6dd1eb9..0000000 | |
--- a/modules/linux/vmci/common/vmciRoute.c | |
+++ /dev/null | |
@@ -1,303 +0,0 @@ | |
-/********************************************************* | |
- * Copyright (C) 2011-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 as published by the | |
- * Free Software Foundation version 2 and no 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 | |
- * | |
- *********************************************************/ | |
- | |
-/* | |
- * vmciRoute.c -- | |
- * | |
- * Implementation of VMCI routing rules. | |
- */ | |
- | |
-#include "vmci_kernel_if.h" | |
-#include "vm_assert.h" | |
-#include "vmci_defs.h" | |
-#include "vmci_infrastructure.h" | |
-#include "vmciCommonInt.h" | |
-#include "vmciContext.h" | |
-#include "vmciDriver.h" | |
-#include "vmciKernelAPI.h" | |
-#include "vmciRoute.h" | |
-#if defined(VMKERNEL) | |
-# include "vmciVmkInt.h" | |
-# include "vm_libc.h" | |
-# include "helper_ext.h" | |
-#endif | |
- | |
-#define LGPFX "VMCIRoute: " | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCI_Route -- | |
- * | |
- * Make a routing decision for the given source and destination handles. | |
- * This will try to determine the route using the handles and the available | |
- * devices. | |
- * | |
- * Result: | |
- * A VMCIRoute value. | |
- * | |
- * Side effects: | |
- * Sets the source context if it is invalid. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-int | |
-VMCI_Route(VMCIHandle *src, // IN/OUT | |
- const VMCIHandle *dst, // IN | |
- Bool fromGuest, // IN | |
- VMCIRoute *route) // OUT | |
-{ | |
- Bool hasHostDevice; | |
- Bool hasGuestDevice; | |
- | |
- ASSERT(src); | |
- ASSERT(dst); | |
- ASSERT(route); | |
- | |
- *route = VMCI_ROUTE_NONE; | |
- | |
- /* | |
- * "fromGuest" is only ever set to TRUE by IOCTL_VMCI_DATAGRAM_SEND (or by | |
- * the vmkernel equivalent), which comes from the VMX, so we know it is | |
- * coming from a guest. | |
- */ | |
- | |
- /* | |
- * To avoid inconsistencies, test these once. We will test them again | |
- * when we do the actual send to ensure that we do not touch a non-existent | |
- * device. | |
- */ | |
- | |
- hasHostDevice = VMCI_HostPersonalityActive(); | |
- hasGuestDevice = VMCI_GuestPersonalityActive(); | |
- | |
- /* Must have a valid destination context. */ | |
- if (VMCI_INVALID_ID == dst->context) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- /* Anywhere to hypervisor. */ | |
- if (VMCI_HYPERVISOR_CONTEXT_ID == dst->context) { | |
- /* | |
- * If this message already came from a guest then we cannot send it | |
- * to the hypervisor. It must come from a local client. | |
- */ | |
- | |
- if (fromGuest) { | |
- return VMCI_ERROR_DST_UNREACHABLE; | |
- } | |
- | |
- /* We must be acting as a guest in order to send to the hypervisor. */ | |
- if (!hasGuestDevice) { | |
- return VMCI_ERROR_DEVICE_NOT_FOUND; | |
- } | |
- | |
- /* And we cannot send if the source is the host context. */ | |
- if (VMCI_HOST_CONTEXT_ID == src->context) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- /* | |
- * If the client passed the ANON source handle then respect it (both | |
- * context and resource are invalid). However, if they passed only | |
- * an invalid context, then they probably mean ANY, in which case we | |
- * should set the real context here before passing it down. | |
- */ | |
- | |
- if (VMCI_INVALID_ID == src->context && VMCI_INVALID_ID != src->resource) { | |
- src->context = vmci_get_context_id(); | |
- } | |
- | |
- /* Send from local client down to the hypervisor. */ | |
- *route = VMCI_ROUTE_AS_GUEST; | |
- return VMCI_SUCCESS; | |
- } | |
- | |
- /* Anywhere to local client on host. */ | |
- if (VMCI_HOST_CONTEXT_ID == dst->context) { | |
- /* | |
- * If it is not from a guest but we are acting as a guest, then we need | |
- * to send it down to the host. Note that if we are also acting as a | |
- * host then this will prevent us from sending from local client to | |
- * local client, but we accept that restriction as a way to remove | |
- * any ambiguity from the host context. | |
- */ | |
- | |
- if (src->context == VMCI_HYPERVISOR_CONTEXT_ID) { | |
- /* | |
- * If the hypervisor is the source, this is host local | |
- * communication. The hypervisor may send vmci event | |
- * datagrams to the host itself, but it will never send | |
- * datagrams to an "outer host" through the guest device. | |
- */ | |
- | |
- if (hasHostDevice) { | |
- *route = VMCI_ROUTE_AS_HOST; | |
- return VMCI_SUCCESS; | |
- } else { | |
- return VMCI_ERROR_DEVICE_NOT_FOUND; | |
- } | |
- } | |
- | |
- if (!fromGuest && hasGuestDevice) { | |
- /* If no source context then use the current. */ | |
- if (VMCI_INVALID_ID == src->context) { | |
- src->context = vmci_get_context_id(); | |
- } | |
- | |
- /* Send it from local client down to the host. */ | |
- *route = VMCI_ROUTE_AS_GUEST; | |
- return VMCI_SUCCESS; | |
- } | |
- | |
- /* | |
- * Otherwise we already received it from a guest and it is destined | |
- * for a local client on this host, or it is from another local client | |
- * on this host. We must be acting as a host to service it. | |
- */ | |
- | |
- if (!hasHostDevice) { | |
- return VMCI_ERROR_DEVICE_NOT_FOUND; | |
- } | |
- | |
- if (VMCI_INVALID_ID == src->context) { | |
- /* | |
- * If it came from a guest then it must have a valid context. | |
- * Otherwise we can use the host context. | |
- */ | |
- | |
- if (fromGuest) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- src->context = VMCI_HOST_CONTEXT_ID; | |
- } | |
- | |
- /* Route to local client. */ | |
- *route = VMCI_ROUTE_AS_HOST; | |
- return VMCI_SUCCESS; | |
- } | |
- | |
- /* If we are acting as a host then this might be destined for a guest. */ | |
- if (hasHostDevice) { | |
- /* It will have a context if it is meant for a guest. */ | |
- if (VMCIContext_Exists(dst->context)) { | |
- if (VMCI_INVALID_ID == src->context) { | |
- /* | |
- * If it came from a guest then it must have a valid context. | |
- * Otherwise we can use the host context. | |
- */ | |
- | |
- if (fromGuest) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- src->context = VMCI_HOST_CONTEXT_ID; | |
- } else if (VMCI_CONTEXT_IS_VM(src->context) && | |
- src->context != dst->context) { | |
- /* | |
- * VM to VM communication is not allowed. Since we catch | |
- * all communication destined for the host above, this | |
- * must be destined for a VM since there is a valid | |
- * context. | |
- */ | |
- | |
- ASSERT(VMCI_CONTEXT_IS_VM(dst->context)); | |
- | |
- if (!vmkernel) { | |
- return VMCI_ERROR_DST_UNREACHABLE; | |
- } | |
- } | |
- | |
- /* Pass it up to the guest. */ | |
- *route = VMCI_ROUTE_AS_HOST; | |
- return VMCI_SUCCESS; | |
- } else if (!hasGuestDevice) { | |
- /* | |
- * The host is attempting to reach a CID without an active context, and | |
- * we can't send it down, since we have no guest device. | |
- */ | |
- | |
- return VMCI_ERROR_DST_UNREACHABLE; | |
- } | |
- } | |
- | |
- /* | |
- * We must be a guest trying to send to another guest, which means | |
- * we need to send it down to the host. We do not filter out VM to | |
- * VM communication here, since we want to be able to use the guest | |
- * driver on older versions that do support VM to VM communication. | |
- */ | |
- | |
- if (!hasGuestDevice) { | |
- /* | |
- * Ending up here means we have neither guest nor host device. That | |
- * shouldn't happen, since any VMCI client in the kernel should have done | |
- * a successful VMCI_DeviceGet. | |
- */ | |
- | |
- ASSERT(FALSE); | |
- | |
- return VMCI_ERROR_DEVICE_NOT_FOUND; | |
- } | |
- | |
- /* If no source context then use the current context. */ | |
- if (VMCI_INVALID_ID == src->context) { | |
- src->context = vmci_get_context_id(); | |
- } | |
- | |
- /* | |
- * Send it from local client down to the host, which will route it to | |
- * the other guest for us. | |
- */ | |
- | |
- *route = VMCI_ROUTE_AS_GUEST; | |
- return VMCI_SUCCESS; | |
-} | |
- | |
- | |
-/* | |
- *------------------------------------------------------------------------------ | |
- * | |
- * VMCI_RouteString -- | |
- * | |
- * Get a string for the given route. | |
- * | |
- * Result: | |
- * A string representing the route, if the route is valid, otherwise an | |
- * empty string. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *------------------------------------------------------------------------------ | |
- */ | |
- | |
-const char * | |
-VMCI_RouteString(VMCIRoute route) // IN | |
-{ | |
- const char *vmciRouteStrings[] = { | |
- "none", | |
- "as host", | |
- "as guest", | |
- }; | |
- if (route >= VMCI_ROUTE_NONE && route <= VMCI_ROUTE_AS_GUEST) { | |
- return vmciRouteStrings[route]; | |
- } | |
- return ""; | |
-} | |
diff --git a/modules/linux/vmci/common/vmciRoute.h b/modules/linux/vmci/common/vmciRoute.h | |
deleted file mode 100644 | |
index a595862..0000000 | |
--- a/modules/linux/vmci/common/vmciRoute.h | |
+++ /dev/null | |
@@ -1,49 +0,0 @@ | |
-/********************************************************* | |
- * Copyright (C) 2011 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 as published by the | |
- * Free Software Foundation version 2 and no 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 | |
- * | |
- *********************************************************/ | |
- | |
-/* | |
- * vmciRoute.h -- | |
- * | |
- * VMCI Routing. | |
- */ | |
- | |
-#ifndef _VMCI_ROUTE_H_ | |
-#define _VMCI_ROUTE_H_ | |
- | |
-#define INCLUDE_ALLOW_VMMON | |
-#define INCLUDE_ALLOW_VMCORE | |
-#define INCLUDE_ALLOW_MODULE | |
-#define INCLUDE_ALLOW_VMKERNEL | |
-#include "includeCheck.h" | |
- | |
-#include "vmci_defs.h" | |
- | |
- | |
-typedef enum { | |
- VMCI_ROUTE_NONE, | |
- VMCI_ROUTE_AS_HOST, | |
- VMCI_ROUTE_AS_GUEST, | |
-} VMCIRoute; | |
- | |
- | |
-int VMCI_Route(VMCIHandle *src, const VMCIHandle *dst, Bool fromGuest, | |
- VMCIRoute *route); | |
-const char *VMCI_RouteString(VMCIRoute route); | |
- | |
- | |
-#endif // _VMCI_ROUTE_H_ | |
diff --git a/modules/linux/vmci/linux/driver.c b/modules/linux/vmci/linux/driver.c | |
deleted file mode 100644 | |
index 82738e4..0000000 | |
--- a/modules/linux/vmci/linux/driver.c | |
+++ /dev/null | |
@@ -1,2509 +0,0 @@ | |
-/********************************************************* | |
- * Copyright (C) 2011 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 as published by the | |
- * Free Software Foundation version 2 and no 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 | |
- * | |
- *********************************************************/ | |
- | |
-/* Must come before any kernel header file */ | |
-#include "driver-config.h" | |
- | |
-#define EXPORT_SYMTAB | |
- | |
-#include <asm/atomic.h> | |
-#include <asm/io.h> | |
- | |
-#include <linux/file.h> | |
-#include <linux/fs.h> | |
-#include <linux/init.h> | |
-#if defined(__x86_64__) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 12) | |
-# include <linux/ioctl32.h> | |
-/* Use weak: not all kernels export sys_ioctl for use by modules */ | |
-asmlinkage __attribute__((weak)) long | |
-sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg); | |
-#endif | |
-#include <linux/miscdevice.h> | |
-#include <linux/moduleparam.h> | |
-#include <linux/poll.h> | |
-#include <linux/smp.h> | |
- | |
-#include "compat_highmem.h" | |
-#include "compat_interrupt.h" | |
-#include "compat_ioport.h" | |
-#include "compat_kernel.h" | |
-#include "compat_mm.h" | |
-#include "compat_module.h" | |
-#include "compat_mutex.h" | |
-#include "compat_page.h" | |
-#include "compat_pci.h" | |
-#include "compat_sched.h" | |
-#include "compat_slab.h" | |
-#include "compat_uaccess.h" | |
-#include "compat_version.h" | |
- | |
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 9) | |
-# error "Linux kernels before 2.6.9 are not supported." | |
-#endif | |
- | |
-#include "vm_basic_types.h" | |
-#include "vm_device_version.h" | |
- | |
-#include "vmware.h" | |
-#include "driverLog.h" | |
-#include "pgtbl.h" | |
-#include "vmci_defs.h" | |
-#include "vmci_handle_array.h" | |
-#include "vmci_infrastructure.h" | |
-#include "vmci_iocontrols.h" | |
-#include "vmci_version.h" | |
-#include "vmci_kernel_if.h" | |
-#include "vmciCommonInt.h" | |
-#include "vmciContext.h" | |
-#include "vmciDatagram.h" | |
-#include "vmciDoorbell.h" | |
-#include "vmciDriver.h" | |
-#include "vmciEvent.h" | |
-#include "vmciKernelAPI.h" | |
-#include "vmciQueuePair.h" | |
-#include "vmciResource.h" | |
- | |
-#define LGPFX "VMCI: " | |
- | |
-#define VMCI_DEVICE_NAME "vmci" | |
-#define VMCI_MODULE_NAME "vmci" | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * PCI Device interface -- | |
- * | |
- * Declarations of types and functions related to the VMCI PCI | |
- * device personality. | |
- * | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-/* | |
- * VMCI PCI driver state | |
- */ | |
- | |
-typedef struct vmci_device { | |
- compat_mutex_t lock; | |
- | |
- unsigned int ioaddr; | |
- unsigned int ioaddr_size; | |
- unsigned int irq; | |
- unsigned int intr_type; | |
- Bool exclusive_vectors; | |
- struct msix_entry msix_entries[VMCI_MAX_INTRS]; | |
- | |
- Bool enabled; | |
- spinlock_t dev_spinlock; | |
- atomic_t datagrams_allowed; | |
-} vmci_device; | |
- | |
-static const struct pci_device_id vmci_ids[] = { | |
- { PCI_DEVICE(PCI_VENDOR_ID_VMWARE, PCI_DEVICE_ID_VMWARE_VMCI), }, | |
- { 0 }, | |
-}; | |
- | |
-static int vmci_probe_device(struct pci_dev *pdev, | |
- const struct pci_device_id *id); | |
-static void vmci_remove_device(struct pci_dev* pdev); | |
- | |
-static struct pci_driver vmci_driver = { | |
- .name = VMCI_DEVICE_NAME, | |
- .id_table = vmci_ids, | |
- .probe = vmci_probe_device, | |
- .remove = vmci_remove_device, | |
-}; | |
- | |
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) | |
-static compat_irqreturn_t vmci_interrupt(int irq, void *dev_id, | |
- struct pt_regs * regs); | |
-static compat_irqreturn_t vmci_interrupt_bm(int irq, void *dev_id, | |
- struct pt_regs * regs); | |
-#else | |
-static compat_irqreturn_t vmci_interrupt(int irq, void *dev_id); | |
-static compat_irqreturn_t vmci_interrupt_bm(int irq, void *dev_id); | |
-#endif | |
-static void dispatch_datagrams(unsigned long data); | |
-static void process_bitmap(unsigned long data); | |
- | |
-/* MSI-X has performance problems in < 2.6.19 */ | |
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) | |
-# define VMCI_DISABLE_MSIX 0 | |
-#else | |
-# define VMCI_DISABLE_MSIX 1 | |
-#endif | |
- | |
-static vmci_device vmci_dev; | |
-static compat_mod_param_bool vmci_disable_host = 0; | |
-static compat_mod_param_bool vmci_disable_guest = 0; | |
-static compat_mod_param_bool vmci_disable_msi; | |
-static compat_mod_param_bool vmci_disable_msix = VMCI_DISABLE_MSIX; | |
- | |
-DECLARE_TASKLET(vmci_dg_tasklet, dispatch_datagrams, | |
- (unsigned long)&vmci_dev); | |
- | |
-DECLARE_TASKLET(vmci_bm_tasklet, process_bitmap, | |
- (unsigned long)&vmci_dev); | |
- | |
-/* | |
- * Allocate a buffer for incoming datagrams globally to avoid repeated | |
- * allocation in the interrupt handler's atomic context. | |
- */ | |
- | |
-static uint8 *data_buffer = NULL; | |
-static uint32 data_buffer_size = VMCI_MAX_DG_SIZE; | |
- | |
-/* | |
- * If the VMCI hardware supports the notification bitmap, we allocate | |
- * and register a page with the device. | |
- */ | |
- | |
-static uint8 *notification_bitmap = NULL; | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * Host device node interface -- | |
- * | |
- * Implements VMCI by implementing open/close/ioctl functions | |
- * | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-/* | |
- * Per-instance host state | |
- */ | |
- | |
-typedef struct VMCILinux { | |
- VMCIContext *context; | |
- int userVersion; | |
- VMCIObjType ctType; | |
-#if defined(HAVE_COMPAT_IOCTL) || defined(HAVE_UNLOCKED_IOCTL) | |
- compat_mutex_t lock; | |
-#endif | |
-} VMCILinux; | |
- | |
-/* | |
- * Static driver state. | |
- */ | |
- | |
-#define VM_DEVICE_NAME_SIZE 32 | |
-#define LINUXLOG_BUFFER_SIZE 1024 | |
- | |
-typedef struct VMCILinuxState { | |
- struct miscdevice misc; | |
- char buf[LINUXLOG_BUFFER_SIZE]; | |
- atomic_t activeContexts; | |
-} VMCILinuxState; | |
- | |
-static int VMCISetupNotify(VMCIContext *context, VA notifyUVA); | |
-static void VMCIUnsetNotifyInt(VMCIContext *context, Bool useLock); | |
- | |
-static int LinuxDriver_Open(struct inode *inode, struct file *filp); | |
- | |
-static int LinuxDriver_Ioctl(struct inode *inode, struct file *filp, | |
- u_int iocmd, unsigned long ioarg); | |
- | |
-#if defined(HAVE_COMPAT_IOCTL) || defined(HAVE_UNLOCKED_IOCTL) | |
-static long LinuxDriver_UnlockedIoctl(struct file *filp, | |
- u_int iocmd, unsigned long ioarg); | |
-#endif | |
- | |
-static int LinuxDriver_Close(struct inode *inode, struct file *filp); | |
-static unsigned int LinuxDriverPoll(struct file *file, poll_table *wait); | |
- | |
- | |
-#if defined(HAVE_COMPAT_IOCTL) || defined(HAVE_UNLOCKED_IOCTL) | |
-#define LinuxDriverLockIoctlPerFD(mutex) compat_mutex_lock(mutex) | |
-#define LinuxDriverUnlockIoctlPerFD(mutex) compat_mutex_unlock(mutex) | |
-#else | |
-#define LinuxDriverLockIoctlPerFD(mutex) do {} while (0) | |
-#define LinuxDriverUnlockIoctlPerFD(mutex) do {} while (0) | |
-#endif | |
- | |
-/* should be const if not for older kernels support */ | |
-static struct file_operations vmuser_fops = { | |
- .owner = THIS_MODULE, | |
- .open = LinuxDriver_Open, | |
- .release = LinuxDriver_Close, | |
- .poll = LinuxDriverPoll, | |
-#ifdef HAVE_UNLOCKED_IOCTL | |
- .unlocked_ioctl = LinuxDriver_UnlockedIoctl, | |
-#else | |
- .ioctl = LinuxDriver_Ioctl, | |
-#endif | |
-#ifdef HAVE_COMPAT_IOCTL | |
- .compat_ioctl = LinuxDriver_UnlockedIoctl, | |
-#endif | |
-}; | |
- | |
-static struct VMCILinuxState linuxState = { | |
- .misc = { | |
- .name = VMCI_DEVICE_NAME, | |
- .minor = MISC_DYNAMIC_MINOR, | |
- .fops = &vmuser_fops, | |
- }, | |
- .activeContexts = ATOMIC_INIT(0), | |
-}; | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * Shared VMCI device definitions -- | |
- * | |
- * Types and variables shared by both host and guest personality | |
- * | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-static Bool guestDeviceInit; | |
-static atomic_t guestDeviceActive; | |
-static Bool hostDeviceInit; | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * Host device support -- | |
- * | |
- * The following functions implement the support for the VMCI | |
- * host driver. | |
- * | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
- | |
-#ifdef VM_X86_64 | |
-#ifndef HAVE_COMPAT_IOCTL | |
-static int | |
-LinuxDriver_Ioctl32_Handler(unsigned int fd, unsigned int iocmd, | |
- unsigned long ioarg, struct file * filp) | |
-{ | |
- int ret; | |
- ret = -ENOTTY; | |
- if (filp && filp->f_op && filp->f_op->ioctl == LinuxDriver_Ioctl) { | |
- ret = LinuxDriver_Ioctl(filp->f_dentry->d_inode, filp, iocmd, ioarg); | |
- } | |
- return ret; | |
-} | |
-#endif /* !HAVE_COMPAT_IOCTL */ | |
- | |
-static int | |
-register_ioctl32_handlers(void) | |
-{ | |
-#ifndef HAVE_COMPAT_IOCTL | |
- { | |
- int i; | |
- | |
- for (i = IOCTL_VMCI_FIRST; i < IOCTL_VMCI_LAST; i++) { | |
- int retval = register_ioctl32_conversion(i, | |
- LinuxDriver_Ioctl32_Handler); | |
- | |
- if (retval) { | |
- Warning(LGPFX"Failed to register ioctl32 conversion " | |
- "(cmd=%d,err=%d).\n", i, retval); | |
- return retval; | |
- } | |
- } | |
- | |
- for (i = IOCTL_VMCI_FIRST2; i < IOCTL_VMCI_LAST2; i++) { | |
- int retval = register_ioctl32_conversion(i, | |
- LinuxDriver_Ioctl32_Handler); | |
- | |
- if (retval) { | |
- Warning(LGPFX"Failed to register ioctl32 conversion " | |
- "(cmd=%d,err=%d).\n", i, retval); | |
- return retval; | |
- } | |
- } | |
- } | |
-#endif /* !HAVE_COMPAT_IOCTL */ | |
- return 0; | |
-} | |
- | |
-static void | |
-unregister_ioctl32_handlers(void) | |
-{ | |
-#ifndef HAVE_COMPAT_IOCTL | |
- { | |
- int i; | |
- | |
- for (i = IOCTL_VMCI_FIRST; i < IOCTL_VMCI_LAST; i++) { | |
- int retval = unregister_ioctl32_conversion(i); | |
- | |
- if (retval) { | |
- Warning(LGPFX"Failed to unregister ioctl32 conversion " | |
- "(cmd=%d,err=%d).\n", i, retval); | |
- } | |
- } | |
- | |
- for (i = IOCTL_VMCI_FIRST2; i < IOCTL_VMCI_LAST2; i++) { | |
- int retval = unregister_ioctl32_conversion(i); | |
- | |
- if (retval) { | |
- Warning(LGPFX"Failed to unregister ioctl32 conversion " | |
- "(cmd=%d,err=%d).\n", i, retval); | |
- } | |
- } | |
- } | |
-#endif /* !HAVE_COMPAT_IOCTL */ | |
-} | |
-#else /* VM_X86_64 */ | |
-#define register_ioctl32_handlers() (0) | |
-#define unregister_ioctl32_handlers() do { } while (0) | |
-#endif /* VM_X86_64 */ | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * vmci_host_init -- | |
- * | |
- * Initializes the VMCI host device driver. | |
- * | |
- * Results: | |
- * 0 on success, other error codes on failure. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static int | |
-vmci_host_init(void) | |
-{ | |
- int error; | |
- | |
- if (VMCI_HostInit() < VMCI_SUCCESS) { | |
- return -ENOMEM; | |
- } | |
- | |
- error = misc_register(&linuxState.misc); | |
- if (error) { | |
- Warning(LGPFX "Module registration error " | |
- "(name=%s, major=%d, minor=%d, err=%d).\n", | |
- linuxState.misc.name, MISC_MAJOR, linuxState.misc.minor, | |
- error); | |
- goto err_host_cleanup; | |
- } | |
- | |
- error = register_ioctl32_handlers(); | |
- if (error) { | |
- Warning(LGPFX "Failed to register ioctl32 handlers, err: %d\n", error); | |
- goto err_misc_unregister; | |
- } | |
- | |
- Log(LGPFX "Module registered (name=%s, major=%d, minor=%d).\n", | |
- linuxState.misc.name, MISC_MAJOR, linuxState.misc.minor); | |
- | |
- return 0; | |
- | |
-err_misc_unregister: | |
- misc_deregister(&linuxState.misc); | |
-err_host_cleanup: | |
- VMCI_HostCleanup(); | |
- return error; | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * LinuxDriver_Open -- | |
- * | |
- * Called on open of /dev/vmci. | |
- * | |
- * Side effects: | |
- * Increment use count used to determine eventual deallocation of | |
- * the module | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-static int | |
-LinuxDriver_Open(struct inode *inode, // IN | |
- struct file *filp) // IN | |
-{ | |
- VMCILinux *vmciLinux; | |
- | |
- vmciLinux = kmalloc(sizeof *vmciLinux, GFP_KERNEL); | |
- if (vmciLinux == NULL) { | |
- return -ENOMEM; | |
- } | |
- memset(vmciLinux, 0, sizeof *vmciLinux); | |
- vmciLinux->ctType = VMCIOBJ_NOT_SET; | |
- vmciLinux->userVersion = 0; | |
-#if defined(HAVE_COMPAT_IOCTL) || defined(HAVE_UNLOCKED_IOCTL) | |
- compat_mutex_init(&vmciLinux->lock); | |
-#endif | |
- | |
- filp->private_data = vmciLinux; | |
- | |
- return 0; | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * LinuxDriver_Close -- | |
- * | |
- * Called on close of /dev/vmci, most often when the process | |
- * exits. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-static int | |
-LinuxDriver_Close(struct inode *inode, // IN | |
- struct file *filp) // IN | |
-{ | |
- VMCILinux *vmciLinux; | |
- | |
- vmciLinux = (VMCILinux *)filp->private_data; | |
- ASSERT(vmciLinux); | |
- | |
- if (vmciLinux->ctType == VMCIOBJ_CONTEXT) { | |
- ASSERT(vmciLinux->context); | |
- | |
- VMCIContext_ReleaseContext(vmciLinux->context); | |
- vmciLinux->context = NULL; | |
- | |
- /* | |
- * The number of active contexts is used to track whether any | |
- * VMX'en are using the host personality. It is incremented when | |
- * a context is created through the IOCTL_VMCI_INIT_CONTEXT | |
- * ioctl. | |
- */ | |
- | |
- atomic_dec(&linuxState.activeContexts); | |
- } | |
- vmciLinux->ctType = VMCIOBJ_NOT_SET; | |
- | |
- kfree(vmciLinux); | |
- filp->private_data = NULL; | |
- return 0; | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * LinuxDriverPoll -- | |
- * | |
- * This is used to wake up the VMX when a VMCI call arrives, or | |
- * to wake up select() or poll() at the next clock tick. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-static unsigned int | |
-LinuxDriverPoll(struct file *filp, | |
- poll_table *wait) | |
-{ | |
- VMCILockFlags flags; | |
- VMCILinux *vmciLinux = (VMCILinux *) filp->private_data; | |
- unsigned int mask = 0; | |
- | |
- if (vmciLinux->ctType == VMCIOBJ_CONTEXT) { | |
- ASSERT(vmciLinux->context != NULL); | |
- /* | |
- * Check for VMCI calls to this VM context. | |
- */ | |
- | |
- if (wait != NULL) { | |
- poll_wait(filp, &vmciLinux->context->hostContext.waitQueue, wait); | |
- } | |
- | |
- VMCI_GrabLock(&vmciLinux->context->lock, &flags); | |
- if (vmciLinux->context->pendingDatagrams > 0 || | |
- VMCIHandleArray_GetSize(vmciLinux->context->pendingDoorbellArray) > 0) { | |
- mask = POLLIN; | |
- } | |
- VMCI_ReleaseLock(&vmciLinux->context->lock, flags); | |
- } | |
- return mask; | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCICopyHandleArrayToUser -- | |
- * | |
- * Copies the handles of a handle array into a user buffer, and | |
- * returns the new length in userBufferSize. If the copy to the | |
- * user buffer fails, the functions still returns VMCI_SUCCESS, | |
- * but retval != 0. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-static int | |
-VMCICopyHandleArrayToUser(void *userBufUVA, // IN | |
- uint64 *userBufSize, // IN/OUT | |
- VMCIHandleArray *handleArray, // IN | |
- int *retval) // IN | |
-{ | |
- uint32 arraySize; | |
- VMCIHandle *handles; | |
- | |
- if (handleArray) { | |
- arraySize = VMCIHandleArray_GetSize(handleArray); | |
- } else { | |
- arraySize = 0; | |
- } | |
- | |
- if (arraySize * sizeof *handles > *userBufSize) { | |
- return VMCI_ERROR_MORE_DATA; | |
- } | |
- | |
- *userBufSize = arraySize * sizeof *handles; | |
- if (*userBufSize) { | |
- *retval = copy_to_user(userBufUVA, | |
- VMCIHandleArray_GetHandles(handleArray), | |
- *userBufSize); | |
- } | |
- | |
- return VMCI_SUCCESS; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIDoQPBrokerAlloc -- | |
- * | |
- * Helper function for creating queue pair and copying the result | |
- * to user memory. | |
- * | |
- * Results: | |
- * 0 if result value was copied to user memory, -EFAULT otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static int | |
-VMCIDoQPBrokerAlloc(VMCIHandle handle, | |
- VMCIId peer, | |
- uint32 flags, | |
- uint64 produceSize, | |
- uint64 consumeSize, | |
- QueuePairPageStore *pageStore, | |
- VMCIContext *context, | |
- Bool vmToVm, | |
- void *resultUVA) | |
-{ | |
- VMCIId cid; | |
- int result; | |
- int retval; | |
- | |
- cid = VMCIContext_GetId(context); | |
- | |
- result = VMCIQPBroker_Alloc(handle, peer, flags, VMCI_NO_PRIVILEGE_FLAGS, | |
- produceSize, consumeSize, pageStore, context); | |
- if (result == VMCI_SUCCESS && vmToVm) { | |
- result = VMCI_SUCCESS_QUEUEPAIR_CREATE; | |
- } | |
- retval = copy_to_user(resultUVA, &result, sizeof result); | |
- if (retval) { | |
- retval = -EFAULT; | |
- if (result >= VMCI_SUCCESS) { | |
- result = VMCIQPBroker_Detach(handle, context); | |
- ASSERT(result >= VMCI_SUCCESS); | |
- } | |
- } | |
- | |
- return retval; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * LinuxDriver_Ioctl -- | |
- * | |
- * Main path for UserRPC | |
- * | |
- * Results: | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static int | |
-LinuxDriver_Ioctl(struct inode *inode, | |
- struct file *filp, | |
- u_int iocmd, | |
- unsigned long ioarg) | |
-{ | |
- VMCILinux *vmciLinux = (VMCILinux *) filp->private_data; | |
- int retval = 0; | |
- | |
- switch (iocmd) { | |
- case IOCTL_VMCI_VERSION2: { | |
- int verFromUser; | |
- | |
- if (copy_from_user(&verFromUser, (void *)ioarg, sizeof verFromUser)) { | |
- retval = -EFAULT; | |
- break; | |
- } | |
- | |
- vmciLinux->userVersion = verFromUser; | |
- } | |
- /* Fall through. */ | |
- case IOCTL_VMCI_VERSION: | |
- /* | |
- * The basic logic here is: | |
- * | |
- * If the user sends in a version of 0 tell it our version. | |
- * If the user didn't send in a version, tell it our version. | |
- * If the user sent in an old version, tell it -its- version. | |
- * If the user sent in an newer version, tell it our version. | |
- * | |
- * The rationale behind telling the caller its version is that | |
- * Workstation 6.5 required that VMX and VMCI kernel module were | |
- * version sync'd. All new VMX users will be programmed to | |
- * handle the VMCI kernel module version. | |
- */ | |
- | |
- if (vmciLinux->userVersion > 0 && | |
- vmciLinux->userVersion < VMCI_VERSION_HOSTQP) { | |
- retval = vmciLinux->userVersion; | |
- } else { | |
- retval = VMCI_VERSION; | |
- } | |
- break; | |
- | |
- case IOCTL_VMCI_INIT_CONTEXT: { | |
- VMCIInitBlock initBlock; | |
- VMCIHostUser user; | |
- | |
- retval = copy_from_user(&initBlock, (void *)ioarg, sizeof initBlock); | |
- if (retval != 0) { | |
- Log(LGPFX"Error reading init block.\n"); | |
- retval = -EFAULT; | |
- break; | |
- } | |
- | |
- LinuxDriverLockIoctlPerFD(&vmciLinux->lock); | |
- if (vmciLinux->ctType != VMCIOBJ_NOT_SET) { | |
- Log(LGPFX"Received VMCI init on initialized handle.\n"); | |
- retval = -EINVAL; | |
- goto init_release; | |
- } | |
- | |
- if (initBlock.flags & ~VMCI_PRIVILEGE_FLAG_RESTRICTED) { | |
- Log(LGPFX"Unsupported VMCI restriction flag.\n"); | |
- retval = -EINVAL; | |
- goto init_release; | |
- } | |
- | |
- user = current_uid(); | |
- retval = VMCIContext_InitContext(initBlock.cid, initBlock.flags, | |
- 0 /* Unused */, vmciLinux->userVersion, | |
- &user, &vmciLinux->context); | |
- if (retval < VMCI_SUCCESS) { | |
- Log(LGPFX"Error initializing context.\n"); | |
- retval = retval == VMCI_ERROR_DUPLICATE_ENTRY ? -EEXIST : -EINVAL; | |
- goto init_release; | |
- } | |
- | |
- /* | |
- * Copy cid to userlevel, we do this to allow the VMX to enforce its | |
- * policy on cid generation. | |
- */ | |
- initBlock.cid = VMCIContext_GetId(vmciLinux->context); | |
- retval = copy_to_user((void *)ioarg, &initBlock, sizeof initBlock); | |
- if (retval != 0) { | |
- VMCIContext_ReleaseContext(vmciLinux->context); | |
- vmciLinux->context = NULL; | |
- Log(LGPFX"Error writing init block.\n"); | |
- retval = -EFAULT; | |
- goto init_release; | |
- } | |
- ASSERT(initBlock.cid != VMCI_INVALID_ID); | |
- | |
- vmciLinux->ctType = VMCIOBJ_CONTEXT; | |
- | |
- atomic_inc(&linuxState.activeContexts); | |
- | |
- init_release: | |
- LinuxDriverUnlockIoctlPerFD(&vmciLinux->lock); | |
- break; | |
- } | |
- | |
- case IOCTL_VMCI_DATAGRAM_SEND: { | |
- VMCIDatagramSendRecvInfo sendInfo; | |
- VMCIDatagram *dg = NULL; | |
- VMCIId cid; | |
- | |
- if (vmciLinux->ctType != VMCIOBJ_CONTEXT) { | |
- Warning(LGPFX"Ioctl only valid for context handle (iocmd=%d).\n", iocmd); | |
- retval = -EINVAL; | |
- break; | |
- } | |
- | |
- retval = copy_from_user(&sendInfo, (void *) ioarg, sizeof sendInfo); | |
- if (retval) { | |
- Warning(LGPFX"copy_from_user failed.\n"); | |
- retval = -EFAULT; | |
- break; | |
- } | |
- | |
- if (sendInfo.len > VMCI_MAX_DG_SIZE) { | |
- Warning(LGPFX"Datagram too big (size=%d).\n", sendInfo.len); | |
- retval = -EINVAL; | |
- break; | |
- } | |
- | |
- if (sendInfo.len < sizeof *dg) { | |
- Warning(LGPFX"Datagram too small (size=%d).\n", sendInfo.len); | |
- retval = -EINVAL; | |
- break; | |
- } | |
- | |
- dg = VMCI_AllocKernelMem(sendInfo.len, VMCI_MEMORY_NORMAL); | |
- if (dg == NULL) { | |
- Log(LGPFX"Cannot allocate memory to dispatch datagram.\n"); | |
- retval = -ENOMEM; | |
- break; | |
- } | |
- | |
- retval = copy_from_user(dg, (char *)(VA)sendInfo.addr, sendInfo.len); | |
- if (retval != 0) { | |
- Log(LGPFX"Error getting datagram (err=%d).\n", retval); | |
- VMCI_FreeKernelMem(dg, sendInfo.len); | |
- retval = -EFAULT; | |
- break; | |
- } | |
- | |
- VMCI_DEBUG_LOG(10, (LGPFX"Datagram dst (handle=0x%x:0x%x) src " | |
- "(handle=0x%x:0x%x), payload (size=%"FMT64"u " | |
- "bytes).\n", dg->dst.context, dg->dst.resource, | |
- dg->src.context, dg->src.resource, | |
- dg->payloadSize)); | |
- | |
- /* Get source context id. */ | |
- ASSERT(vmciLinux->context); | |
- cid = VMCIContext_GetId(vmciLinux->context); | |
- ASSERT(cid != VMCI_INVALID_ID); | |
- sendInfo.result = VMCIDatagram_Dispatch(cid, dg, TRUE); | |
- VMCI_FreeKernelMem(dg, sendInfo.len); | |
- retval = copy_to_user((void *)ioarg, &sendInfo, sizeof sendInfo); | |
- break; | |
- } | |
- | |
- case IOCTL_VMCI_DATAGRAM_RECEIVE: { | |
- VMCIDatagramSendRecvInfo recvInfo; | |
- VMCIDatagram *dg = NULL; | |
- size_t size; | |
- | |
- if (vmciLinux->ctType != VMCIOBJ_CONTEXT) { | |
- Warning(LGPFX"Ioctl only valid for context handle (iocmd=%d).\n", | |
- iocmd); | |
- retval = -EINVAL; | |
- break; | |
- } | |
- | |
- retval = copy_from_user(&recvInfo, (void *) ioarg, sizeof recvInfo); | |
- if (retval) { | |
- Warning(LGPFX"copy_from_user failed.\n"); | |
- retval = -EFAULT; | |
- break; | |
- } | |
- | |
- ASSERT(vmciLinux->ctType == VMCIOBJ_CONTEXT); | |
- | |
- size = recvInfo.len; | |
- ASSERT(vmciLinux->context); | |
- recvInfo.result = VMCIContext_DequeueDatagram(vmciLinux->context, | |
- &size, &dg); | |
- | |
- if (recvInfo.result >= VMCI_SUCCESS) { | |
- ASSERT(dg); | |
- retval = copy_to_user((void *) ((uintptr_t) recvInfo.addr), dg, | |
- VMCI_DG_SIZE(dg)); | |
- VMCI_FreeKernelMem(dg, VMCI_DG_SIZE(dg)); | |
- if (retval != 0) { | |
- break; | |
- } | |
- } | |
- retval = copy_to_user((void *)ioarg, &recvInfo, sizeof recvInfo); | |
- break; | |
- } | |
- | |
- case IOCTL_VMCI_QUEUEPAIR_ALLOC: { | |
- if (vmciLinux->ctType != VMCIOBJ_CONTEXT) { | |
- Log(LGPFX"IOCTL_VMCI_QUEUEPAIR_ALLOC only valid for contexts.\n"); | |
- retval = -EINVAL; | |
- break; | |
- } | |
- | |
- if (vmciLinux->userVersion < VMCI_VERSION_NOVMVM) { | |
- VMCIQueuePairAllocInfo_VMToVM queuePairAllocInfo; | |
- VMCIQueuePairAllocInfo_VMToVM *info = (VMCIQueuePairAllocInfo_VMToVM *)ioarg; | |
- | |
- retval = copy_from_user(&queuePairAllocInfo, (void *)ioarg, | |
- sizeof queuePairAllocInfo); | |
- if (retval) { | |
- retval = -EFAULT; | |
- break; | |
- } | |
- | |
- retval = VMCIDoQPBrokerAlloc(queuePairAllocInfo.handle, | |
- queuePairAllocInfo.peer, | |
- queuePairAllocInfo.flags, | |
- queuePairAllocInfo.produceSize, | |
- queuePairAllocInfo.consumeSize, | |
- NULL, | |
- vmciLinux->context, | |
- TRUE, // VM to VM style create | |
- &info->result); | |
- } else { | |
- VMCIQueuePairAllocInfo queuePairAllocInfo; | |
- VMCIQueuePairAllocInfo *info = (VMCIQueuePairAllocInfo *)ioarg; | |
- QueuePairPageStore pageStore; | |
- | |
- retval = copy_from_user(&queuePairAllocInfo, (void *)ioarg, | |
- sizeof queuePairAllocInfo); | |
- if (retval) { | |
- retval = -EFAULT; | |
- break; | |
- } | |
- | |
- pageStore.pages = queuePairAllocInfo.ppnVA; | |
- pageStore.len = queuePairAllocInfo.numPPNs; | |
- | |
- retval = VMCIDoQPBrokerAlloc(queuePairAllocInfo.handle, | |
- queuePairAllocInfo.peer, | |
- queuePairAllocInfo.flags, | |
- queuePairAllocInfo.produceSize, | |
- queuePairAllocInfo.consumeSize, | |
- &pageStore, | |
- vmciLinux->context, | |
- FALSE, // Not VM to VM style create | |
- &info->result); | |
- } | |
- break; | |
- } | |
- | |
- case IOCTL_VMCI_QUEUEPAIR_SETVA: { | |
- VMCIQueuePairSetVAInfo setVAInfo; | |
- VMCIQueuePairSetVAInfo *info = (VMCIQueuePairSetVAInfo *)ioarg; | |
- int32 result; | |
- | |
- if (vmciLinux->ctType != VMCIOBJ_CONTEXT) { | |
- Log(LGPFX"IOCTL_VMCI_QUEUEPAIR_SETVA only valid for contexts.\n"); | |
- retval = -EINVAL; | |
- break; | |
- } | |
- | |
- if (vmciLinux->userVersion < VMCI_VERSION_NOVMVM) { | |
- Log(LGPFX"IOCTL_VMCI_QUEUEPAIR_SETVA not supported for this VMX version.\n"); | |
- retval = -EINVAL; | |
- break; | |
- } | |
- | |
- retval = copy_from_user(&setVAInfo, (void *)ioarg, sizeof setVAInfo); | |
- if (retval) { | |
- retval = -EFAULT; | |
- break; | |
- } | |
- | |
- if (setVAInfo.va) { | |
- /* | |
- * VMX is passing down a new VA for the queue pair mapping. | |
- */ | |
- | |
- result = VMCIQPBroker_Map(setVAInfo.handle, vmciLinux->context, setVAInfo.va); | |
- } else { | |
- /* | |
- * The queue pair is about to be unmapped by the VMX. | |
- */ | |
- | |
- result = VMCIQPBroker_Unmap(setVAInfo.handle, vmciLinux->context, 0); | |
- } | |
- | |
- retval = copy_to_user(&info->result, &result, sizeof result); | |
- if (retval) { | |
- retval = -EFAULT; | |
- } | |
- | |
- break; | |
- } | |
- | |
- case IOCTL_VMCI_QUEUEPAIR_SETPAGEFILE: { | |
- VMCIQueuePairPageFileInfo pageFileInfo; | |
- VMCIQueuePairPageFileInfo *info = (VMCIQueuePairPageFileInfo *)ioarg; | |
- int32 result; | |
- | |
- if (vmciLinux->userVersion < VMCI_VERSION_HOSTQP || | |
- vmciLinux->userVersion >= VMCI_VERSION_NOVMVM) { | |
- Log(LGPFX"IOCTL_VMCI_QUEUEPAIR_SETPAGEFILE not supported this VMX " | |
- "(version=%d).\n", vmciLinux->userVersion); | |
- retval = -EINVAL; | |
- break; | |
- } | |
- | |
- if (vmciLinux->ctType != VMCIOBJ_CONTEXT) { | |
- Log(LGPFX"IOCTL_VMCI_QUEUEPAIR_SETPAGEFILE only valid for contexts.\n"); | |
- retval = -EINVAL; | |
- break; | |
- } | |
- | |
- retval = copy_from_user(&pageFileInfo, (void *)ioarg, sizeof *info); | |
- if (retval) { | |
- retval = -EFAULT; | |
- break; | |
- } | |
- | |
- /* | |
- * Communicate success pre-emptively to the caller. Note that | |
- * the basic premise is that it is incumbent upon the caller not | |
- * to look at the info.result field until after the ioctl() | |
- * returns. And then, only if the ioctl() result indicates no | |
- * error. We send up the SUCCESS status before calling | |
- * SetPageStore() store because failing to copy up the result | |
- * code means unwinding the SetPageStore(). | |
- * | |
- * It turns out the logic to unwind a SetPageStore() opens a can | |
- * of worms. For example, if a host had created the QueuePair | |
- * and a guest attaches and SetPageStore() is successful but | |
- * writing success fails, then ... the host has to be stopped | |
- * from writing (anymore) data into the QueuePair. That means | |
- * an additional test in the VMCI_Enqueue() code path. Ugh. | |
- */ | |
- | |
- result = VMCI_SUCCESS; | |
- retval = copy_to_user(&info->result, &result, sizeof result); | |
- if (retval == 0) { | |
- result = VMCIQPBroker_SetPageStore(pageFileInfo.handle, | |
- pageFileInfo.produceVA, | |
- pageFileInfo.consumeVA, | |
- vmciLinux->context); | |
- if (result < VMCI_SUCCESS) { | |
- | |
- retval = copy_to_user(&info->result, &result, sizeof result); | |
- if (retval != 0) { | |
- /* | |
- * Note that in this case the SetPageStore() call | |
- * failed but we were unable to communicate that to the | |
- * caller (because the copy_to_user() call failed). | |
- * So, if we simply return an error (in this case | |
- * -EFAULT) then the caller will know that the | |
- * SetPageStore failed even though we couldn't put the | |
- * result code in the result field and indicate exactly | |
- * why it failed. | |
- * | |
- * That says nothing about the issue where we were once | |
- * able to write to the caller's info memory and now | |
- * can't. Something more serious is probably going on | |
- * than the fact that SetPageStore() didn't work. | |
- */ | |
- retval = -EFAULT; | |
- } | |
- } | |
- | |
- } else { | |
- /* | |
- * In this case, we can't write a result field of the | |
- * caller's info block. So, we don't even try to | |
- * SetPageStore(). | |
- */ | |
- retval = -EFAULT; | |
- } | |
- | |
- break; | |
- } | |
- | |
- case IOCTL_VMCI_QUEUEPAIR_DETACH: { | |
- VMCIQueuePairDetachInfo detachInfo; | |
- VMCIQueuePairDetachInfo *info = (VMCIQueuePairDetachInfo *)ioarg; | |
- int32 result; | |
- | |
- if (vmciLinux->ctType != VMCIOBJ_CONTEXT) { | |
- Log(LGPFX"IOCTL_VMCI_QUEUEPAIR_DETACH only valid for contexts.\n"); | |
- retval = -EINVAL; | |
- break; | |
- } | |
- | |
- retval = copy_from_user(&detachInfo, (void *)ioarg, sizeof detachInfo); | |
- if (retval) { | |
- retval = -EFAULT; | |
- break; | |
- } | |
- | |
- result = VMCIQPBroker_Detach(detachInfo.handle, vmciLinux->context); | |
- if (result == VMCI_SUCCESS && | |
- vmciLinux->userVersion < VMCI_VERSION_NOVMVM) { | |
- result = VMCI_SUCCESS_LAST_DETACH; | |
- } | |
- | |
- retval = copy_to_user(&info->result, &result, sizeof result); | |
- if (retval) { | |
- retval = -EFAULT; | |
- } | |
- | |
- break; | |
- } | |
- | |
- case IOCTL_VMCI_CTX_ADD_NOTIFICATION: { | |
- VMCINotifyAddRemoveInfo arInfo; | |
- VMCINotifyAddRemoveInfo *info = (VMCINotifyAddRemoveInfo *)ioarg; | |
- int32 result; | |
- VMCIId cid; | |
- | |
- if (vmciLinux->ctType != VMCIOBJ_CONTEXT) { | |
- Log(LGPFX"IOCTL_VMCI_CTX_ADD_NOTIFICATION only valid for contexts.\n"); | |
- retval = -EINVAL; | |
- break; | |
- } | |
- | |
- retval = copy_from_user(&arInfo, (void *)ioarg, sizeof arInfo); | |
- if (retval) { | |
- retval = -EFAULT; | |
- break; | |
- } | |
- | |
- cid = VMCIContext_GetId(vmciLinux->context); | |
- result = VMCIContext_AddNotification(cid, arInfo.remoteCID); | |
- retval = copy_to_user(&info->result, &result, sizeof result); | |
- if (retval) { | |
- retval = -EFAULT; | |
- break; | |
- } | |
- break; | |
- } | |
- | |
- case IOCTL_VMCI_CTX_REMOVE_NOTIFICATION: { | |
- VMCINotifyAddRemoveInfo arInfo; | |
- VMCINotifyAddRemoveInfo *info = (VMCINotifyAddRemoveInfo *)ioarg; | |
- int32 result; | |
- VMCIId cid; | |
- | |
- if (vmciLinux->ctType != VMCIOBJ_CONTEXT) { | |
- Log(LGPFX"IOCTL_VMCI_CTX_REMOVE_NOTIFICATION only valid for " | |
- "contexts.\n"); | |
- retval = -EINVAL; | |
- break; | |
- } | |
- | |
- retval = copy_from_user(&arInfo, (void *)ioarg, sizeof arInfo); | |
- if (retval) { | |
- retval = -EFAULT; | |
- break; | |
- } | |
- | |
- cid = VMCIContext_GetId(vmciLinux->context); | |
- result = VMCIContext_RemoveNotification(cid, arInfo.remoteCID); | |
- retval = copy_to_user(&info->result, &result, sizeof result); | |
- if (retval) { | |
- retval = -EFAULT; | |
- break; | |
- } | |
- break; | |
- } | |
- | |
- case IOCTL_VMCI_CTX_GET_CPT_STATE: { | |
- VMCICptBufInfo getInfo; | |
- VMCIId cid; | |
- char *cptBuf; | |
- | |
- if (vmciLinux->ctType != VMCIOBJ_CONTEXT) { | |
- Log(LGPFX"IOCTL_VMCI_CTX_GET_CPT_STATE only valid for contexts.\n"); | |
- retval = -EINVAL; | |
- break; | |
- } | |
- | |
- retval = copy_from_user(&getInfo, (void *)ioarg, sizeof getInfo); | |
- if (retval) { | |
- retval = -EFAULT; | |
- break; | |
- } | |
- | |
- cid = VMCIContext_GetId(vmciLinux->context); | |
- getInfo.result = VMCIContext_GetCheckpointState(cid, getInfo.cptType, | |
- &getInfo.bufSize, | |
- &cptBuf); | |
- if (getInfo.result == VMCI_SUCCESS && getInfo.bufSize) { | |
- retval = copy_to_user((void *)(VA)getInfo.cptBuf, cptBuf, | |
- getInfo.bufSize); | |
- VMCI_FreeKernelMem(cptBuf, getInfo.bufSize); | |
- if (retval) { | |
- retval = -EFAULT; | |
- break; | |
- } | |
- } | |
- retval = copy_to_user((void *)ioarg, &getInfo, sizeof getInfo); | |
- if (retval) { | |
- retval = -EFAULT; | |
- break; | |
- } | |
- break; | |
- } | |
- | |
- case IOCTL_VMCI_CTX_SET_CPT_STATE: { | |
- VMCICptBufInfo setInfo; | |
- VMCIId cid; | |
- char *cptBuf; | |
- | |
- if (vmciLinux->ctType != VMCIOBJ_CONTEXT) { | |
- Log(LGPFX"IOCTL_VMCI_CTX_SET_CPT_STATE only valid for contexts.\n"); | |
- retval = -EINVAL; | |
- break; | |
- } | |
- | |
- retval = copy_from_user(&setInfo, (void *)ioarg, sizeof setInfo); | |
- if (retval) { | |
- retval = -EFAULT; | |
- break; | |
- } | |
- | |
- cptBuf = VMCI_AllocKernelMem(setInfo.bufSize, VMCI_MEMORY_NORMAL); | |
- if (cptBuf == NULL) { | |
- Log(LGPFX"Cannot allocate memory to set cpt state (type=%d).\n", | |
- setInfo.cptType); | |
- retval = -ENOMEM; | |
- break; | |
- } | |
- retval = copy_from_user(cptBuf, (void *)(VA)setInfo.cptBuf, | |
- setInfo.bufSize); | |
- if (retval) { | |
- VMCI_FreeKernelMem(cptBuf, setInfo.bufSize); | |
- retval = -EFAULT; | |
- break; | |
- } | |
- | |
- cid = VMCIContext_GetId(vmciLinux->context); | |
- setInfo.result = VMCIContext_SetCheckpointState(cid, setInfo.cptType, | |
- setInfo.bufSize, cptBuf); | |
- VMCI_FreeKernelMem(cptBuf, setInfo.bufSize); | |
- retval = copy_to_user((void *)ioarg, &setInfo, sizeof setInfo); | |
- if (retval) { | |
- retval = -EFAULT; | |
- break; | |
- } | |
- break; | |
- } | |
- | |
- case IOCTL_VMCI_GET_CONTEXT_ID: { | |
- VMCIId cid = VMCI_HOST_CONTEXT_ID; | |
- | |
- retval = copy_to_user((void *)ioarg, &cid, sizeof cid); | |
- break; | |
- } | |
- | |
- case IOCTL_VMCI_SET_NOTIFY: { | |
- VMCISetNotifyInfo notifyInfo; | |
- | |
- if (vmciLinux->ctType != VMCIOBJ_CONTEXT) { | |
- Log(LGPFX"IOCTL_VMCI_SET_NOTIFY only valid for contexts.\n"); | |
- retval = -EINVAL; | |
- break; | |
- } | |
- | |
- retval = copy_from_user(¬ifyInfo, (void *)ioarg, sizeof notifyInfo); | |
- if (retval) { | |
- retval = -EFAULT; | |
- break; | |
- } | |
- | |
- if ((VA)notifyInfo.notifyUVA != (VA)NULL) { | |
- notifyInfo.result = VMCISetupNotify(vmciLinux->context, | |
- (VA)notifyInfo.notifyUVA); | |
- } else { | |
- VMCIUnsetNotifyInt(vmciLinux->context, TRUE); | |
- notifyInfo.result = VMCI_SUCCESS; | |
- } | |
- | |
- retval = copy_to_user((void *)ioarg, ¬ifyInfo, sizeof notifyInfo); | |
- if (retval) { | |
- retval = -EFAULT; | |
- break; | |
- } | |
- | |
- break; | |
- } | |
- | |
- case IOCTL_VMCI_NOTIFY_RESOURCE: { | |
- VMCINotifyResourceInfo info; | |
- VMCIId cid; | |
- | |
- if (vmciLinux->userVersion < VMCI_VERSION_NOTIFY) { | |
- Log(LGPFX"IOCTL_VMCI_NOTIFY_RESOURCE is invalid for current" | |
- " VMX versions.\n"); | |
- retval = -EINVAL; | |
- break; | |
- } | |
- | |
- if (vmciLinux->ctType != VMCIOBJ_CONTEXT) { | |
- Log(LGPFX"IOCTL_VMCI_NOTIFY_RESOURCE is only valid for contexts.\n"); | |
- retval = -EINVAL; | |
- break; | |
- } | |
- | |
- retval = copy_from_user(&info, (void *)ioarg, sizeof info); | |
- if (retval) { | |
- retval = -EFAULT; | |
- break; | |
- } | |
- | |
- cid = VMCIContext_GetId(vmciLinux->context); | |
- switch (info.action) { | |
- case VMCI_NOTIFY_RESOURCE_ACTION_NOTIFY: | |
- if (info.resource == VMCI_NOTIFY_RESOURCE_DOOR_BELL) { | |
- info.result = VMCIContext_NotifyDoorbell(cid, info.handle, | |
- VMCI_NO_PRIVILEGE_FLAGS); | |
- } else { | |
- info.result = VMCI_ERROR_UNAVAILABLE; | |
- } | |
- break; | |
- case VMCI_NOTIFY_RESOURCE_ACTION_CREATE: | |
- info.result = VMCIContext_DoorbellCreate(cid, info.handle); | |
- break; | |
- case VMCI_NOTIFY_RESOURCE_ACTION_DESTROY: | |
- info.result = VMCIContext_DoorbellDestroy(cid, info.handle); | |
- break; | |
- default: | |
- Log(LGPFX"IOCTL_VMCI_NOTIFY_RESOURCE got unknown action (action=%d).\n", | |
- info.action); | |
- info.result = VMCI_ERROR_INVALID_ARGS; | |
- } | |
- retval = copy_to_user((void *)ioarg, &info, | |
- sizeof info); | |
- if (retval) { | |
- retval = -EFAULT; | |
- break; | |
- } | |
- | |
- break; | |
- } | |
- | |
- case IOCTL_VMCI_NOTIFICATIONS_RECEIVE: { | |
- VMCINotificationReceiveInfo info; | |
- VMCIHandleArray *dbHandleArray; | |
- VMCIHandleArray *qpHandleArray; | |
- VMCIId cid; | |
- | |
- if (vmciLinux->ctType != VMCIOBJ_CONTEXT) { | |
- Log(LGPFX"IOCTL_VMCI_NOTIFICATIONS_RECEIVE is only valid for contexts.\n"); | |
- retval = -EINVAL; | |
- break; | |
- } | |
- | |
- if (vmciLinux->userVersion < VMCI_VERSION_NOTIFY) { | |
- Log(LGPFX"IOCTL_VMCI_NOTIFICATIONS_RECEIVE is not supported for the" | |
- " current vmx version.\n"); | |
- retval = -EINVAL; | |
- break; | |
- } | |
- | |
- retval = copy_from_user(&info, (void *)ioarg, sizeof info); | |
- if (retval) { | |
- retval = -EFAULT; | |
- break; | |
- } | |
- | |
- if ((info.dbHandleBufSize && !info.dbHandleBufUVA) || | |
- (info.qpHandleBufSize && !info.qpHandleBufUVA)) { | |
- retval = -EINVAL; | |
- break; | |
- } | |
- | |
- cid = VMCIContext_GetId(vmciLinux->context); | |
- info.result = VMCIContext_ReceiveNotificationsGet(cid, | |
- &dbHandleArray, | |
- &qpHandleArray); | |
- if (info.result == VMCI_SUCCESS) { | |
- info.result = | |
- VMCICopyHandleArrayToUser((void *)(VA)info.dbHandleBufUVA, | |
- &info.dbHandleBufSize, | |
- dbHandleArray, | |
- &retval); | |
- if (info.result == VMCI_SUCCESS && !retval) { | |
- info.result = | |
- VMCICopyHandleArrayToUser((void *)(VA)info.qpHandleBufUVA, | |
- &info.qpHandleBufSize, | |
- qpHandleArray, | |
- &retval); | |
- } | |
- if (!retval) { | |
- retval = copy_to_user((void *)ioarg, &info, sizeof info); | |
- } | |
- VMCIContext_ReceiveNotificationsRelease(cid, dbHandleArray, qpHandleArray, | |
- info.result == VMCI_SUCCESS && !retval); | |
- } else { | |
- retval = copy_to_user((void *)ioarg, &info, sizeof info); | |
- } | |
- break; | |
- } | |
- | |
- default: | |
- Warning(LGPFX"Unknown ioctl (iocmd=%d).\n", iocmd); | |
- retval = -EINVAL; | |
- } | |
- | |
- return retval; | |
-} | |
- | |
- | |
-#if defined(HAVE_COMPAT_IOCTL) || defined(HAVE_UNLOCKED_IOCTL) | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * LinuxDriver_UnlockedIoctl -- | |
- * | |
- * Wrapper for LinuxDriver_Ioctl supporting the compat_ioctl and | |
- * unlocked_ioctl methods that have signatures different from the | |
- * old ioctl. Used as compat_ioctl method for 32bit apps running | |
- * on 64bit kernel and for unlocked_ioctl on systems supporting | |
- * those. LinuxDriver_Ioctl may safely be called without holding | |
- * the BKL. | |
- * | |
- * Results: | |
- * Same as LinuxDriver_Ioctl. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static long | |
-LinuxDriver_UnlockedIoctl(struct file *filp, | |
- u_int iocmd, | |
- unsigned long ioarg) | |
-{ | |
- return LinuxDriver_Ioctl(NULL, filp, iocmd, ioarg); | |
-} | |
-#endif | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIUserVAInvalidPointer -- | |
- * | |
- * Checks if a given user VA is valid or not. Copied from | |
- * bora/modules/vmnet/linux/hostif.c:VNetUserIfInvalidPointer(). TODO | |
- * libify the common code. | |
- * | |
- * Results: | |
- * TRUE iff invalid. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static INLINE Bool | |
-VMCIUserVAInvalidPointer(VA uva, // IN: | |
- size_t size) // IN: | |
-{ | |
- return !access_ok(VERIFY_WRITE, (void *)uva, size); | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIUserVALockPage -- | |
- * | |
- * Lock physical page backing a given user VA. Copied from | |
- * bora/modules/vmnet/linux/userif.c:UserIfLockPage(). TODO libify the | |
- * common code. | |
- * | |
- * Results: | |
- * Pointer to struct page on success, NULL otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static INLINE struct page * | |
-VMCIUserVALockPage(VA addr) // IN: | |
-{ | |
- struct page *page = NULL; | |
- int retval; | |
- | |
- down_read(¤t->mm->mmap_sem); | |
- retval = get_user_pages(current, current->mm, addr, | |
- 1, 1, 0, &page, NULL); | |
- up_read(¤t->mm->mmap_sem); | |
- | |
- if (retval != 1) { | |
- return NULL; | |
- } | |
- | |
- return page; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIMapBoolPtr -- | |
- * | |
- * Lock physical page backing a given user VA and maps it to kernel | |
- * address space. The range of the mapped memory should be within a | |
- * single page otherwise an error is returned. Copied from | |
- * bora/modules/vmnet/linux/userif.c:VNetUserIfMapUint32Ptr(). TODO | |
- * libify the common code. | |
- * | |
- * Results: | |
- * 0 on success, negative error code otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static INLINE int | |
-VMCIMapBoolPtr(VA notifyUVA, // IN: | |
- struct page **p, // OUT: | |
- Bool **notifyPtr) // OUT: | |
-{ | |
- if (VMCIUserVAInvalidPointer(notifyUVA, sizeof **notifyPtr) || | |
- (((notifyUVA + sizeof **notifyPtr - 1) & ~(PAGE_SIZE - 1)) != | |
- (notifyUVA & ~(PAGE_SIZE - 1)))) { | |
- return -EINVAL; | |
- } | |
- | |
- *p = VMCIUserVALockPage(notifyUVA); | |
- if (*p == NULL) { | |
- return -EAGAIN; | |
- } | |
- | |
- *notifyPtr = (Bool *)((uint8 *)kmap(*p) + (notifyUVA & (PAGE_SIZE - 1))); | |
- return 0; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCISetupNotify -- | |
- * | |
- * Sets up a given context for notify to work. Calls VMCIMapBoolPtr() | |
- * which maps the notify boolean in user VA in kernel space. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS on success, error code otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static int | |
-VMCISetupNotify(VMCIContext *context, // IN: | |
- VA notifyUVA) // IN: | |
-{ | |
- int retval; | |
- | |
- if (context->notify) { | |
- Warning(LGPFX"Notify mechanism is already set up.\n"); | |
- return VMCI_ERROR_DUPLICATE_ENTRY; | |
- } | |
- | |
- retval = | |
- VMCIMapBoolPtr(notifyUVA, &context->notifyPage, &context->notify) == 0 ? | |
- VMCI_SUCCESS : VMCI_ERROR_GENERIC; | |
- if (retval == VMCI_SUCCESS) { | |
- VMCIContext_CheckAndSignalNotify(context); | |
- } | |
- | |
- return retval; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIUnsetNotifyInt -- | |
- * | |
- * Internal version of VMCIUnsetNotify, that allows for locking | |
- * the context before unsetting the notify pointer. If useLock is | |
- * TRUE, the context lock is grabbed. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static void | |
-VMCIUnsetNotifyInt(VMCIContext *context, // IN | |
- Bool useLock) // IN | |
-{ | |
- VMCILockFlags flags; | |
- | |
- if (useLock) { | |
- VMCI_GrabLock(&context->lock, &flags); | |
- } | |
- | |
- if (context->notifyPage) { | |
- struct page *notifyPage = context->notifyPage; | |
- | |
- context->notify = NULL; | |
- context->notifyPage = NULL; | |
- | |
- if (useLock) { | |
- VMCI_ReleaseLock(&context->lock, flags); | |
- } | |
- | |
- kunmap(notifyPage); | |
- put_page(notifyPage); | |
- } else { | |
- if (useLock) { | |
- VMCI_ReleaseLock(&context->lock, flags); | |
- } | |
- } | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIUnsetNotify -- | |
- * | |
- * Reverts actions set up by VMCISetupNotify(). Unmaps and unlocks the | |
- * page mapped/locked by VMCISetupNotify(). | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCIUnsetNotify(VMCIContext *context) // IN: | |
-{ | |
- VMCIUnsetNotifyInt(context, FALSE); | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * PCI device support -- | |
- * | |
- * The following functions implement the support for the VMCI | |
- * guest device. This includes initializing the device and | |
- * interrupt handling. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * vmci_guest_init -- | |
- * | |
- * Initializes the VMCI PCI device. The initialization might fail | |
- * if there is no VMCI PCI device. | |
- * | |
- * Results: | |
- * 0 on success, other error codes on failure. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static int | |
-vmci_guest_init(void) | |
-{ | |
- int retval; | |
- | |
- /* Initialize guest device data. */ | |
- compat_mutex_init(&vmci_dev.lock); | |
- vmci_dev.intr_type = VMCI_INTR_TYPE_INTX; | |
- vmci_dev.exclusive_vectors = FALSE; | |
- spin_lock_init(&vmci_dev.dev_spinlock); | |
- vmci_dev.enabled = FALSE; | |
- atomic_set(&vmci_dev.datagrams_allowed, 0); | |
- atomic_set(&guestDeviceActive, 0); | |
- | |
- data_buffer = vmalloc(data_buffer_size); | |
- if (!data_buffer) { | |
- return -ENOMEM; | |
- } | |
- | |
- /* This should be last to make sure we are done initializing. */ | |
- retval = pci_register_driver(&vmci_driver); | |
- if (retval < 0) { | |
- vfree(data_buffer); | |
- data_buffer = NULL; | |
- return retval; | |
- } | |
- | |
- return 0; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * vmci_enable_msix -- | |
- * | |
- * Enable MSI-X. Try exclusive vectors first, then shared vectors. | |
- * | |
- * Results: | |
- * 0 on success, other error codes on failure. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static int | |
-vmci_enable_msix(struct pci_dev *pdev) // IN | |
-{ | |
- int i; | |
- int result; | |
- | |
- for (i = 0; i < VMCI_MAX_INTRS; ++i) { | |
- vmci_dev.msix_entries[i].entry = i; | |
- vmci_dev.msix_entries[i].vector = i; | |
- } | |
- | |
- result = pci_enable_msix(pdev, vmci_dev.msix_entries, VMCI_MAX_INTRS); | |
- if (!result) { | |
- vmci_dev.exclusive_vectors = TRUE; | |
- } else if (result > 0) { | |
- result = pci_enable_msix(pdev, vmci_dev.msix_entries, 1); | |
- } | |
- return result; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * vmci_probe_device -- | |
- * | |
- * Most of the initialization at module load time is done here. | |
- * | |
- * Results: | |
- * Returns 0 for success, an error otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static int | |
-vmci_probe_device(struct pci_dev *pdev, // IN: vmci PCI device | |
- const struct pci_device_id *id) // IN: matching device ID | |
-{ | |
- unsigned int ioaddr; | |
- unsigned int ioaddr_size; | |
- unsigned int capabilities; | |
- int result; | |
- | |
- printk(KERN_INFO "Probing for vmci/PCI.\n"); | |
- | |
- result = pci_enable_device(pdev); | |
- if (result) { | |
- printk(KERN_ERR "Cannot VMCI device %s: error %d\n", | |
- pci_name(pdev), result); | |
- return result; | |
- } | |
- pci_set_master(pdev); /* To enable QueuePair functionality. */ | |
- ioaddr = pci_resource_start(pdev, 0); | |
- ioaddr_size = pci_resource_len(pdev, 0); | |
- | |
- /* | |
- * Request I/O region with adjusted base address and size. The adjusted | |
- * values are needed and used if we release the region in case of failure. | |
- */ | |
- | |
- if (!compat_request_region(ioaddr, ioaddr_size, "vmci")) { | |
- printk(KERN_INFO "vmci: Another driver already loaded " | |
- "for device in slot %s.\n", pci_name(pdev)); | |
- goto pci_disable; | |
- } | |
- | |
- printk(KERN_INFO "Found vmci/PCI at %#x, irq %u.\n", ioaddr, pdev->irq); | |
- | |
- /* | |
- * Verify that the VMCI Device supports the capabilities that | |
- * we need. If the device is missing capabilities that we would | |
- * like to use, check for fallback capabilities and use those | |
- * instead (so we can run a new VM on old hosts). Fail the load if | |
- * a required capability is missing and there is no fallback. | |
- * | |
- * Right now, we need datagrams. There are no fallbacks. | |
- */ | |
- capabilities = inl(ioaddr + VMCI_CAPS_ADDR); | |
- | |
- if ((capabilities & VMCI_CAPS_DATAGRAM) == 0) { | |
- printk(KERN_ERR "VMCI device does not support datagrams.\n"); | |
- goto release; | |
- } | |
- | |
- /* | |
- * If the hardware supports notifications, we will use that as | |
- * well. | |
- */ | |
- if (capabilities & VMCI_CAPS_NOTIFICATIONS) { | |
- capabilities = VMCI_CAPS_DATAGRAM; | |
- notification_bitmap = vmalloc(PAGE_SIZE); | |
- if (notification_bitmap == NULL) { | |
- printk(KERN_ERR "VMCI device unable to allocate notification bitmap.\n"); | |
- } else { | |
- memset(notification_bitmap, 0, PAGE_SIZE); | |
- capabilities |= VMCI_CAPS_NOTIFICATIONS; | |
- } | |
- } else { | |
- capabilities = VMCI_CAPS_DATAGRAM; | |
- } | |
- printk(KERN_INFO "VMCI: using capabilities 0x%x.\n", capabilities); | |
- | |
- /* Let the host know which capabilities we intend to use. */ | |
- outl(capabilities, ioaddr + VMCI_CAPS_ADDR); | |
- | |
- /* Device struct initialization. */ | |
- compat_mutex_lock(&vmci_dev.lock); | |
- if (vmci_dev.enabled) { | |
- printk(KERN_ERR "VMCI device already enabled.\n"); | |
- goto unlock; | |
- } | |
- | |
- vmci_dev.ioaddr = ioaddr; | |
- vmci_dev.ioaddr_size = ioaddr_size; | |
- atomic_set(&vmci_dev.datagrams_allowed, 1); | |
- | |
- /* | |
- * Register notification bitmap with device if that capability is | |
- * used | |
- */ | |
- if (capabilities & VMCI_CAPS_NOTIFICATIONS) { | |
- unsigned long bitmapPPN; | |
- bitmapPPN = page_to_pfn(vmalloc_to_page(notification_bitmap)); | |
- if (!VMCI_RegisterNotificationBitmap(bitmapPPN)) { | |
- printk(KERN_ERR "VMCI device unable to register notification bitmap " | |
- "with PPN 0x%x.\n", (uint32)bitmapPPN); | |
- goto datagram_disallow; | |
- } | |
- } | |
- | |
- /* Check host capabilities. */ | |
- if (!VMCI_CheckHostCapabilities()) { | |
- goto remove_bitmap; | |
- } | |
- | |
- /* Enable device. */ | |
- vmci_dev.enabled = TRUE; | |
- pci_set_drvdata(pdev, &vmci_dev); | |
- | |
- /* | |
- * We do global initialization here because we need datagrams | |
- * during VMCIUtil_Init, since it registers for VMCI events. If we | |
- * ever support more than one VMCI device we will have to create | |
- * seperate LateInit/EarlyExit functions that can be used to do | |
- * initialization/cleanup that depends on the device being | |
- * accessible. We need to initialize VMCI components before | |
- * requesting an irq - the VMCI interrupt handler uses these | |
- * components, and it may be invoked once request_irq() has | |
- * registered the handler (as the irq line may be shared). | |
- */ | |
- VMCIUtil_Init(); | |
- | |
- if (VMCIQPGuestEndpoints_Init() < VMCI_SUCCESS) { | |
- goto util_exit; | |
- } | |
- | |
- /* | |
- * Enable interrupts. Try MSI-X first, then MSI, and then fallback on | |
- * legacy interrupts. | |
- */ | |
- if (!vmci_disable_msix && !vmci_enable_msix(pdev)) { | |
- vmci_dev.intr_type = VMCI_INTR_TYPE_MSIX; | |
- vmci_dev.irq = vmci_dev.msix_entries[0].vector; | |
- } else if (!vmci_disable_msi && !pci_enable_msi(pdev)) { | |
- vmci_dev.intr_type = VMCI_INTR_TYPE_MSI; | |
- vmci_dev.irq = pdev->irq; | |
- } else { | |
- vmci_dev.intr_type = VMCI_INTR_TYPE_INTX; | |
- vmci_dev.irq = pdev->irq; | |
- } | |
- | |
- /* Request IRQ for legacy or MSI interrupts, or for first MSI-X vector. */ | |
- result = request_irq(vmci_dev.irq, vmci_interrupt, COMPAT_IRQF_SHARED, | |
- "vmci", &vmci_dev); | |
- if (result) { | |
- printk(KERN_ERR "vmci: irq %u in use: %d\n", vmci_dev.irq, result); | |
- goto components_exit; | |
- } | |
- | |
- /* | |
- * For MSI-X with exclusive vectors we need to request an interrupt for each | |
- * vector so that we get a separate interrupt handler routine. This allows | |
- * us to distinguish between the vectors. | |
- */ | |
- | |
- if (vmci_dev.exclusive_vectors) { | |
- ASSERT(vmci_dev.intr_type == VMCI_INTR_TYPE_MSIX); | |
- result = request_irq(vmci_dev.msix_entries[1].vector, vmci_interrupt_bm, | |
- 0, "vmci", &vmci_dev); | |
- if (result) { | |
- printk(KERN_ERR "vmci: irq %u in use: %d\n", | |
- vmci_dev.msix_entries[1].vector, result); | |
- free_irq(vmci_dev.irq, &vmci_dev); | |
- goto components_exit; | |
- } | |
- } | |
- | |
- printk(KERN_INFO "Registered vmci device.\n"); | |
- | |
- atomic_inc(&guestDeviceActive); | |
- | |
- compat_mutex_unlock(&vmci_dev.lock); | |
- | |
- /* Enable specific interrupt bits. */ | |
- if (capabilities & VMCI_CAPS_NOTIFICATIONS) { | |
- outl(VMCI_IMR_DATAGRAM | VMCI_IMR_NOTIFICATION, | |
- vmci_dev.ioaddr + VMCI_IMR_ADDR); | |
- } else { | |
- outl(VMCI_IMR_DATAGRAM, vmci_dev.ioaddr + VMCI_IMR_ADDR); | |
- } | |
- | |
- /* Enable interrupts. */ | |
- outl(VMCI_CONTROL_INT_ENABLE, vmci_dev.ioaddr + VMCI_CONTROL_ADDR); | |
- | |
- return 0; | |
- | |
- components_exit: | |
- VMCIQPGuestEndpoints_Exit(); | |
- util_exit: | |
- VMCIUtil_Exit(); | |
- vmci_dev.enabled = FALSE; | |
- if (vmci_dev.intr_type == VMCI_INTR_TYPE_MSIX) { | |
- pci_disable_msix(pdev); | |
- } else if (vmci_dev.intr_type == VMCI_INTR_TYPE_MSI) { | |
- pci_disable_msi(pdev); | |
- } | |
- remove_bitmap: | |
- if (notification_bitmap) { | |
- outl(VMCI_CONTROL_RESET, vmci_dev.ioaddr + VMCI_CONTROL_ADDR); | |
- } | |
- datagram_disallow: | |
- atomic_set(&vmci_dev.datagrams_allowed, 0); | |
- unlock: | |
- compat_mutex_unlock(&vmci_dev.lock); | |
- release: | |
- if (notification_bitmap) { | |
- vfree(notification_bitmap); | |
- notification_bitmap = NULL; | |
- } | |
- release_region(ioaddr, ioaddr_size); | |
- pci_disable: | |
- pci_disable_device(pdev); | |
- return -EBUSY; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * vmci_remove_device -- | |
- * | |
- * Cleanup, called for each device on unload. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static void | |
-vmci_remove_device(struct pci_dev* pdev) | |
-{ | |
- struct vmci_device *dev = pci_get_drvdata(pdev); | |
- | |
- printk(KERN_INFO "Removing vmci device\n"); | |
- | |
- atomic_dec(&guestDeviceActive); | |
- | |
- VMCIQPGuestEndpoints_Exit(); | |
- VMCIUtil_Exit(); | |
- | |
- compat_mutex_lock(&dev->lock); | |
- | |
- atomic_set(&vmci_dev.datagrams_allowed, 0); | |
- | |
- printk(KERN_INFO "Resetting vmci device\n"); | |
- outl(VMCI_CONTROL_RESET, vmci_dev.ioaddr + VMCI_CONTROL_ADDR); | |
- | |
- /* | |
- * Free IRQ and then disable MSI/MSI-X as appropriate. For MSI-X, we might | |
- * have multiple vectors, each with their own IRQ, which we must free too. | |
- */ | |
- | |
- free_irq(dev->irq, dev); | |
- if (dev->intr_type == VMCI_INTR_TYPE_MSIX) { | |
- if (dev->exclusive_vectors) { | |
- free_irq(dev->msix_entries[1].vector, dev); | |
- } | |
- pci_disable_msix(pdev); | |
- } else if (dev->intr_type == VMCI_INTR_TYPE_MSI) { | |
- pci_disable_msi(pdev); | |
- } | |
- dev->exclusive_vectors = FALSE; | |
- dev->intr_type = VMCI_INTR_TYPE_INTX; | |
- | |
- release_region(dev->ioaddr, dev->ioaddr_size); | |
- dev->enabled = FALSE; | |
- if (notification_bitmap) { | |
- /* | |
- * The device reset above cleared the bitmap state of the | |
- * device, so we can safely free it here. | |
- */ | |
- | |
- vfree(notification_bitmap); | |
- notification_bitmap = NULL; | |
- } | |
- | |
- printk(KERN_INFO "Unregistered vmci device.\n"); | |
- compat_mutex_unlock(&dev->lock); | |
- | |
- pci_disable_device(pdev); | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * vmci_interrupt -- | |
- * | |
- * Interrupt handler for legacy or MSI interrupt, or for first MSI-X | |
- * interrupt (vector VMCI_INTR_DATAGRAM). | |
- * | |
- * Results: | |
- * COMPAT_IRQ_HANDLED if the interrupt is handled, COMPAT_IRQ_NONE if | |
- * not an interrupt. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) | |
-static compat_irqreturn_t | |
-vmci_interrupt(int irq, // IN | |
- void *clientdata, // IN | |
- struct pt_regs *regs) // IN | |
-#else | |
-static compat_irqreturn_t | |
-vmci_interrupt(int irq, // IN | |
- void *clientdata) // IN | |
-#endif | |
-{ | |
- vmci_device *dev = clientdata; | |
- | |
- if (dev == NULL) { | |
- printk(KERN_DEBUG "vmci_interrupt(): irq %d for unknown device.\n", irq); | |
- return COMPAT_IRQ_NONE; | |
- } | |
- | |
- /* | |
- * If we are using MSI-X with exclusive vectors then we simply schedule | |
- * the datagram tasklet, since we know the interrupt was meant for us. | |
- * Otherwise we must read the ICR to determine what to do. | |
- */ | |
- | |
- if (dev->intr_type == VMCI_INTR_TYPE_MSIX && dev->exclusive_vectors) { | |
- tasklet_schedule(&vmci_dg_tasklet); | |
- } else { | |
- unsigned int icr; | |
- | |
- ASSERT(dev->intr_type == VMCI_INTR_TYPE_INTX || | |
- dev->intr_type == VMCI_INTR_TYPE_MSI); | |
- | |
- /* Acknowledge interrupt and determine what needs doing. */ | |
- icr = inl(dev->ioaddr + VMCI_ICR_ADDR); | |
- if (icr == 0 || icr == 0xffffffff) { | |
- return COMPAT_IRQ_NONE; | |
- } | |
- | |
- if (icr & VMCI_ICR_DATAGRAM) { | |
- tasklet_schedule(&vmci_dg_tasklet); | |
- icr &= ~VMCI_ICR_DATAGRAM; | |
- } | |
- if (icr & VMCI_ICR_NOTIFICATION) { | |
- tasklet_schedule(&vmci_bm_tasklet); | |
- icr &= ~VMCI_ICR_NOTIFICATION; | |
- } | |
- if (icr != 0) { | |
- printk(KERN_INFO LGPFX"Ignoring unknown interrupt cause (%d).\n", icr); | |
- } | |
- } | |
- | |
- return COMPAT_IRQ_HANDLED; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * vmci_interrupt_bm -- | |
- * | |
- * Interrupt handler for MSI-X interrupt vector VMCI_INTR_NOTIFICATION, | |
- * which is for the notification bitmap. Will only get called if we are | |
- * using MSI-X with exclusive vectors. | |
- * | |
- * Results: | |
- * COMPAT_IRQ_HANDLED if the interrupt is handled, COMPAT_IRQ_NONE if | |
- * not an interrupt. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) | |
-static compat_irqreturn_t | |
-vmci_interrupt_bm(int irq, // IN | |
- void *clientdata, // IN | |
- struct pt_regs *regs) // IN | |
-#else | |
-static compat_irqreturn_t | |
-vmci_interrupt_bm(int irq, // IN | |
- void *clientdata) // IN | |
-#endif | |
-{ | |
- vmci_device *dev = clientdata; | |
- | |
- if (dev == NULL) { | |
- printk(KERN_DEBUG "vmci_interrupt_bm(): irq %d for unknown device.\n", irq); | |
- return COMPAT_IRQ_NONE; | |
- } | |
- | |
- /* For MSI-X we can just assume it was meant for us. */ | |
- ASSERT(dev->intr_type == VMCI_INTR_TYPE_MSIX && dev->exclusive_vectors); | |
- tasklet_schedule(&vmci_bm_tasklet); | |
- | |
- return COMPAT_IRQ_HANDLED; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCI_DeviceEnabled -- | |
- * | |
- * Checks whether the VMCI device is enabled. | |
- * | |
- * Results: | |
- * TRUE if device is enabled, FALSE otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-Bool | |
-VMCI_DeviceEnabled(void) | |
-{ | |
- return VMCI_GuestPersonalityActive() || VMCI_HostPersonalityActive(); | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCI_SendDatagram -- | |
- * | |
- * VM to hypervisor call mechanism. We use the standard VMware naming | |
- * convention since shared code is calling this function as well. | |
- * | |
- * Results: | |
- * The result of the hypercall. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCI_SendDatagram(VMCIDatagram *dg) | |
-{ | |
- unsigned long flags; | |
- int result; | |
- | |
- /* Check args. */ | |
- if (dg == NULL) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- if (atomic_read(&vmci_dev.datagrams_allowed) == 0) { | |
- return VMCI_ERROR_UNAVAILABLE; | |
- } | |
- | |
- /* | |
- * Need to acquire spinlock on the device because | |
- * the datagram data may be spread over multiple pages and the monitor may | |
- * interleave device user rpc calls from multiple VCPUs. Acquiring the | |
- * spinlock precludes that possibility. Disabling interrupts to avoid | |
- * incoming datagrams during a "rep out" and possibly landing up in this | |
- * function. | |
- */ | |
- spin_lock_irqsave(&vmci_dev.dev_spinlock, flags); | |
- | |
- /* | |
- * Send the datagram and retrieve the return value from the result register. | |
- */ | |
- __asm__ __volatile__( | |
- "cld\n\t" | |
- "rep outsb\n\t" | |
- : /* No output. */ | |
- : "d"(vmci_dev.ioaddr + VMCI_DATA_OUT_ADDR), | |
- "c"(VMCI_DG_SIZE(dg)), "S"(dg) | |
- ); | |
- | |
- /* | |
- * XXX Should read result high port as well when updating handlers to | |
- * return 64bit. | |
- */ | |
- result = inl(vmci_dev.ioaddr + VMCI_RESULT_LOW_ADDR); | |
- spin_unlock_irqrestore(&vmci_dev.dev_spinlock, flags); | |
- | |
- return result; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * dispatch_datagrams -- | |
- * | |
- * Reads and dispatches incoming datagrams. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * Reads data from the device. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-dispatch_datagrams(unsigned long data) | |
-{ | |
- vmci_device *dev = (vmci_device *)data; | |
- | |
- if (dev == NULL) { | |
- printk(KERN_DEBUG "vmci: dispatch_datagrams(): no vmci device" | |
- "present.\n"); | |
- return; | |
- } | |
- | |
- if (data_buffer == NULL) { | |
- printk(KERN_DEBUG "vmci: dispatch_datagrams(): no buffer present.\n"); | |
- return; | |
- } | |
- | |
- | |
- VMCI_ReadDatagramsFromPort((VMCIIoHandle) 0, dev->ioaddr + VMCI_DATA_IN_ADDR, | |
- data_buffer, data_buffer_size); | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * process_bitmap -- | |
- * | |
- * Scans the notification bitmap for raised flags, clears them | |
- * and handles the notifications. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-process_bitmap(unsigned long data) | |
-{ | |
- vmci_device *dev = (vmci_device *)data; | |
- | |
- if (dev == NULL) { | |
- printk(KERN_DEBUG "vmci: process_bitmaps(): no vmci device" | |
- "present.\n"); | |
- return; | |
- } | |
- | |
- if (notification_bitmap == NULL) { | |
- printk(KERN_DEBUG "vmci: process_bitmaps(): no bitmap present.\n"); | |
- return; | |
- } | |
- | |
- | |
- VMCI_ScanNotificationBitmap(notification_bitmap); | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * Shared functions -- | |
- * | |
- * Functions shared between host and guest personality. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCI_GuestPersonalityActive -- | |
- * | |
- * Determines whether the VMCI PCI device has been successfully | |
- * initialized. | |
- * | |
- * Results: | |
- * TRUE, if VMCI guest device is operational, FALSE otherwise. | |
- * | |
- * Side effects: | |
- * Reads data from the device. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-Bool | |
-VMCI_GuestPersonalityActive(void) | |
-{ | |
- return guestDeviceInit && atomic_read(&guestDeviceActive) > 0; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCI_HostPersonalityActive -- | |
- * | |
- * Determines whether the VMCI host personality is | |
- * available. Since the core functionality of the host driver is | |
- * always present, all guests could possibly use the host | |
- * personality. However, to minimize the deviation from the | |
- * pre-unified driver state of affairs, we only consider the host | |
- * device active, if there is no active guest device, or if there | |
- * are VMX'en with active VMCI contexts using the host device. | |
- * | |
- * Results: | |
- * TRUE, if VMCI host driver is operational, FALSE otherwise. | |
- * | |
- * Side effects: | |
- * Reads data from the device. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-Bool | |
-VMCI_HostPersonalityActive(void) | |
-{ | |
- return hostDeviceInit && | |
- (!VMCI_GuestPersonalityActive() || | |
- atomic_read(&linuxState.activeContexts) > 0); | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * Module definitions -- | |
- * | |
- * Implements support for module load/unload. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * vmci_init -- | |
- * | |
- * linux module entry point. Called by /sbin/insmod command | |
- * | |
- * Results: | |
- * registers a device driver for a major # that depends | |
- * on the uid. Add yourself to that list. List is now in | |
- * private/driver-private.c. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-static int __init | |
-vmci_init(void) | |
-{ | |
- int retval; | |
- | |
- retval = VMCI_SharedInit(); | |
- if (retval != VMCI_SUCCESS) { | |
- Warning(LGPFX"Failed to initialize VMCI common components (err=%d).\n", | |
- retval); | |
- return -ENOMEM; | |
- } | |
- | |
- if (!vmci_disable_guest) { | |
- retval = vmci_guest_init(); | |
- if (retval != 0) { | |
- Warning(LGPFX"VMCI PCI device not initialized (err=%d).\n", retval); | |
- } else { | |
- guestDeviceInit = TRUE; | |
- if (VMCI_GuestPersonalityActive()) { | |
- Log(LGPFX"Using guest personality\n"); | |
- } | |
- } | |
- } | |
- | |
- if (!vmci_disable_host) { | |
- retval = vmci_host_init(); | |
- if (retval != 0) { | |
- Warning(LGPFX"Unable to initialize host personality (err=%d).\n", | |
- retval); | |
- } else { | |
- hostDeviceInit = TRUE; | |
- Log(LGPFX"Using host personality\n"); | |
- } | |
- } | |
- | |
- if (!guestDeviceInit && !hostDeviceInit) { | |
- VMCI_SharedCleanup(); | |
- return -ENODEV; | |
- } | |
- | |
- Log(LGPFX"Module (name=%s) is initialized\n", VMCI_MODULE_NAME); | |
- | |
- return 0; | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * vmci_exit -- | |
- * | |
- * Called by /sbin/rmmod | |
- * | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-static void __exit | |
-vmci_exit(void) | |
-{ | |
- int retval; | |
- | |
- if (guestDeviceInit) { | |
- pci_unregister_driver(&vmci_driver); | |
- vfree(data_buffer); | |
- guestDeviceInit = FALSE; | |
- } | |
- | |
- if (hostDeviceInit) { | |
- unregister_ioctl32_handlers(); | |
- | |
- VMCI_HostCleanup(); | |
- | |
- retval = misc_deregister(&linuxState.misc); | |
- if (retval) { | |
- Warning(LGPFX "Module %s: error unregistering\n", VMCI_MODULE_NAME); | |
- } else { | |
- Log(LGPFX"Module %s: unloaded\n", VMCI_MODULE_NAME); | |
- } | |
- | |
- hostDeviceInit = FALSE; | |
- } | |
- | |
- VMCI_SharedCleanup(); | |
-} | |
- | |
- | |
-module_init(vmci_init); | |
-module_exit(vmci_exit); | |
-MODULE_DEVICE_TABLE(pci, vmci_ids); | |
- | |
-module_param_named(disable_host, vmci_disable_host, bool, 0); | |
-MODULE_PARM_DESC(disable_host, "Disable driver host personality - (default=0)"); | |
- | |
-module_param_named(disable_guest, vmci_disable_guest, bool, 0); | |
-MODULE_PARM_DESC(disable_guest, "Disable driver guest personality - (default=0)"); | |
- | |
-module_param_named(disable_msi, vmci_disable_msi, bool, 0); | |
-MODULE_PARM_DESC(disable_msi, "Disable MSI use in driver - (default=0)"); | |
- | |
-module_param_named(disable_msix, vmci_disable_msix, bool, 0); | |
-MODULE_PARM_DESC(disable_msix, "Disable MSI-X use in driver - (default=" | |
- __stringify(VMCI_DISABLE_MSIX) ")"); | |
- | |
-MODULE_AUTHOR("VMware, Inc."); | |
-MODULE_DESCRIPTION("VMware Virtual Machine Communication Interface (VMCI)."); | |
-MODULE_VERSION(VMCI_DRIVER_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/modules/linux/vmci/linux/vmciKernelIf.c b/modules/linux/vmci/linux/vmciKernelIf.c | |
deleted file mode 100644 | |
index a51bef8..0000000 | |
--- a/modules/linux/vmci/linux/vmciKernelIf.c | |
+++ /dev/null | |
@@ -1,2170 +0,0 @@ | |
-/********************************************************* | |
- * Copyright (C) 2007 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 as published by the | |
- * Free Software Foundation version 2 and no 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 | |
- * | |
- *********************************************************/ | |
- | |
-/* | |
- * vmciKernelIf.c -- | |
- * | |
- * This file implements defines and helper functions for VMCI | |
- * host _and_ guest kernel code. This is the linux specific | |
- * implementation. | |
- */ | |
- | |
-/* Must come before any kernel header file */ | |
-#include "driver-config.h" | |
- | |
-#if !defined(linux) || defined(VMKERNEL) | |
-#error "Wrong platform." | |
-#endif | |
- | |
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 9) | |
-# error "Linux kernels before 2.6.9 are not supported." | |
-#endif | |
- | |
-#include <linux/mm.h> /* For vmalloc_to_page() and get_user_pages()*/ | |
-#include <linux/pagemap.h> /* For page_cache_release() */ | |
-#include <linux/socket.h> /* For memcpy_{to,from}iovec(). */ | |
-#include <linux/vmalloc.h> | |
-#include <linux/wait.h> | |
- | |
-#include "compat_highmem.h" | |
-#include "compat_interrupt.h" | |
-#include "compat_mm.h" | |
-#include "compat_module.h" | |
-#include "compat_page.h" | |
-#include "compat_pci.h" | |
-#include "compat_sched.h" | |
-#include "compat_semaphore.h" | |
-#include "compat_slab.h" | |
-#include "compat_spinlock.h" | |
-#include "compat_version.h" | |
-#include "compat_workqueue.h" | |
- | |
-#include "vm_assert.h" | |
-#include "vm_basic_types.h" | |
-#include "vmci_iocontrols.h" | |
-#include "vmci_kernel_if.h" | |
-#include "vmciQueue.h" | |
-#include "vmciQueuePair.h" | |
- | |
- | |
-/* | |
- * The Kernel specific component of the VMCIQueue structure. | |
- */ | |
- | |
-struct VMCIQueueKernelIf { | |
- struct page **page; | |
- struct page **headerPage; | |
- void *va; | |
- VMCIMutex __mutex; | |
- VMCIMutex *mutex; | |
- Bool host; | |
- Bool isDataMapped; | |
- size_t numPages; | |
-}; | |
- | |
-typedef struct VMCIDelayedWorkInfo { | |
- compat_work work; | |
- VMCIWorkFn *workFn; | |
- void *data; | |
-} VMCIDelayedWorkInfo; | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCI_InitLock | |
- * | |
- * Initializes the lock. Must be called before use. | |
- * | |
- * Results: | |
- * Always VMCI_SUCCESS. | |
- * | |
- * Side effects: | |
- * Thread can block. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCI_InitLock(VMCILock *lock, // IN: | |
- char *name, // IN: Unused on Linux | |
- VMCILockRank rank) // IN: Unused on Linux | |
-{ | |
- spin_lock_init(lock); | |
- return VMCI_SUCCESS; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCI_CleanupLock | |
- * | |
- * Cleanup the lock. Must be called before deallocating lock. | |
- * | |
- * Results: | |
- * None | |
- * | |
- * Side effects: | |
- * Deletes kernel lock state | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCI_CleanupLock(VMCILock *lock) | |
-{ | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCI_GrabLock | |
- * | |
- * Grabs the given lock. XXX Fill in specific lock requirements. XXX Move | |
- * locking code into hostif if VMCI stays in vmmon. | |
- * | |
- * Results: | |
- * None | |
- * | |
- * Side effects: | |
- * Thread can block. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCI_GrabLock(VMCILock *lock, // IN | |
- VMCILockFlags *flags) // OUT: used to restore irql on windows | |
-{ | |
- spin_lock(lock); | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCI_ReleaseLock | |
- * | |
- * Releases the given lock. XXX Move locking code into hostif if VMCI | |
- * stays in vmmon. | |
- * | |
- * Results: | |
- * None | |
- * | |
- * Side effects: | |
- * A thread blocked on this lock may wake up. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCI_ReleaseLock(VMCILock *lock, // IN | |
- VMCILockFlags flags) // IN | |
-{ | |
- spin_unlock(lock); | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCI_GrabLock_BH | |
- * | |
- * Grabs the given lock and for linux kernels disables bottom half execution. | |
- * This should be used with locks accessed both from bottom half/tasklet | |
- * contexts, ie. guestcall handlers, and from process contexts to avoid | |
- * deadlocks where the process has the lock and gets descheduled due to a | |
- * bh/tasklet coming in. | |
- * | |
- * Results: | |
- * None | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCI_GrabLock_BH(VMCILock *lock, // IN | |
- VMCILockFlags *flags) // OUT: used to restore | |
-{ | |
- spin_lock_bh(lock); | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCI_ReleaseLock_BH | |
- * | |
- * Releases the given lock and for linux kernels reenables bottom half | |
- * execution. | |
- * This should be used with locks accessed both from bottom half/tasklet | |
- * contexts, ie. guestcall handlers, and from process contexts to avoid | |
- * deadlocks where the process has the lock and get descheduled due to a | |
- * bh/tasklet coming in. | |
- * | |
- * Results: | |
- * None | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCI_ReleaseLock_BH(VMCILock *lock, // IN | |
- VMCILockFlags flags) // IN | |
-{ | |
- spin_unlock_bh(lock); | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIHost_InitContext -- | |
- * | |
- * Host-specific initialization of VMCI context state. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCIHost_InitContext(VMCIHost *hostContext, // IN | |
- uintptr_t eventHnd) // IN: Unused | |
-{ | |
- init_waitqueue_head(&hostContext->waitQueue); | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIHost_ReleaseContext -- | |
- * | |
- * Host-specific release of state allocated by | |
- * VMCIHost_InitContext. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCIHost_ReleaseContext(VMCIHost *hostContext) // IN | |
-{ | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIHost_SignalCall -- | |
- * | |
- * Signal to userlevel that a VMCI call is waiting. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCIHost_SignalCall(VMCIHost *hostContext) // IN | |
-{ | |
- wake_up(&hostContext->waitQueue); | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIHost_WaitForCallLocked -- | |
- * | |
- * Wait until a VMCI call is pending or the waiting thread is | |
- * interrupted. It is assumed that a lock is held prior to | |
- * calling this function. The lock will be released during the | |
- * wait. The correctnes of this funtion depends on that the same | |
- * lock is held when the call is signalled. | |
- * | |
- * Results: | |
- * TRUE on success | |
- * FALSE if the wait was interrupted. | |
- * | |
- * Side effects: | |
- * The call may block. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-Bool | |
-VMCIHost_WaitForCallLocked(VMCIHost *hostContext, // IN | |
- VMCILock *lock, // IN | |
- VMCILockFlags *flags, // IN | |
- Bool useBH) // IN | |
- | |
-{ | |
- DECLARE_WAITQUEUE(wait, current); | |
- | |
- /* | |
- * The thread must be added to the wait queue and have its state | |
- * changed while holding the lock - otherwise a signal may change | |
- * the state in between and have it overwritten causing a loss of | |
- * the event. | |
- */ | |
- | |
- add_wait_queue(&hostContext->waitQueue, &wait); | |
- current->state = TASK_INTERRUPTIBLE; | |
- | |
- if (useBH) { | |
- VMCI_ReleaseLock_BH(lock, *flags); | |
- } else { | |
- VMCI_ReleaseLock(lock, *flags); | |
- } | |
- | |
- schedule(); | |
- | |
- if (useBH) { | |
- VMCI_GrabLock_BH(lock, flags); | |
- } else { | |
- VMCI_GrabLock(lock, flags); | |
- } | |
- | |
- current->state = TASK_RUNNING; | |
- | |
- remove_wait_queue(&hostContext->waitQueue, &wait); | |
- | |
- if (signal_pending(current)) { | |
- return FALSE; | |
- } | |
- | |
- return TRUE; | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCIHost_ClearCall -- | |
- * | |
- * Clear the pending call signal. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCIHost_ClearCall(VMCIHost *hostContext) // IN | |
-{ | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIHost_CompareUser -- | |
- * | |
- * Determines whether the two users are the same. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS if equal, error code otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-int VMCIHost_CompareUser(VMCIHostUser *user1, | |
- VMCIHostUser *user2) | |
-{ | |
- if (!user1 || !user2) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- if (*user1 == *user2) { | |
- return VMCI_SUCCESS; | |
- } else { | |
- return VMCI_ERROR_GENERIC; | |
- } | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCI_AllocKernelMem | |
- * | |
- * Allocate some kernel memory for the VMCI driver. | |
- * | |
- * Results: | |
- * The address allocated or NULL on error. | |
- * | |
- * | |
- * Side effects: | |
- * memory is malloced | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-void * | |
-VMCI_AllocKernelMem(size_t size, int flags) | |
-{ | |
- void *ptr; | |
- | |
- if ((flags & VMCI_MEMORY_ATOMIC) != 0) { | |
- ptr = kmalloc(size, GFP_ATOMIC); | |
- } else { | |
- ptr = kmalloc(size, GFP_KERNEL); | |
- } | |
- | |
- return ptr; | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------- | |
- * | |
- * VMCI_FreeKernelMem | |
- * | |
- * Free kernel memory allocated for the VMCI driver. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * memory is freed. | |
- *---------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCI_FreeKernelMem(void *ptr, // IN: | |
- size_t size) // IN: Unused on Linux | |
-{ | |
- kfree(ptr); | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCI_CopyToUser -- | |
- * | |
- * Copy memory to the user application from a kernel buffer. This | |
- * function may block, so don't call it while holding any kind of | |
- * lock. | |
- * | |
- * Results: | |
- * 0 on success. | |
- * Nonzero on failure. | |
- * | |
- * Side effects: | |
- * None | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCI_CopyToUser(VA64 dst, // OUT: Destination user VA. | |
- const void *src, // IN: Source kernel VA. | |
- size_t len) // IN: Number of bytes to copy. | |
-{ | |
- return copy_to_user(VMCIVA64ToPtr(dst), src, len) ? -EFAULT : 0; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCI_CopyFromUser -- | |
- * | |
- * Copy memory from the user application to a kernel buffer. This | |
- * function may block, so don't call it while holding any kind of | |
- * lock. | |
- * | |
- * Results: | |
- * 0 on success. | |
- * Nonzero on failure. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCI_CopyFromUser(void *dst, // OUT: Kernel VA | |
- VA64 src, // IN: User VA | |
- size_t len) // IN | |
-{ | |
- return copy_from_user(dst, VMCIVA64ToPtr(src), len); | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------------- | |
- * | |
- * VMCIDelayedWorkCB | |
- * | |
- * Called in a worker thread context. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------------- | |
- */ | |
- | |
-static void | |
-VMCIDelayedWorkCB(compat_work_arg work) // IN | |
-{ | |
- VMCIDelayedWorkInfo *delayedWorkInfo; | |
- | |
- delayedWorkInfo = COMPAT_WORK_GET_DATA(work, VMCIDelayedWorkInfo, work); | |
- ASSERT(delayedWorkInfo); | |
- ASSERT(delayedWorkInfo->workFn); | |
- | |
- delayedWorkInfo->workFn(delayedWorkInfo->data); | |
- | |
- VMCI_FreeKernelMem(delayedWorkInfo, sizeof *delayedWorkInfo); | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------------- | |
- * | |
- * VMCI_CanScheduleDelayedWork -- | |
- * | |
- * Checks to see if the given platform supports delayed work callbacks. | |
- * | |
- * Results: | |
- * TRUE if it does. FALSE otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------------- | |
- */ | |
- | |
-Bool | |
-VMCI_CanScheduleDelayedWork(void) | |
-{ | |
- return TRUE; | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------------- | |
- * | |
- * VMCI_ScheduleDelayedWork -- | |
- * | |
- * Schedule the specified callback. | |
- * | |
- * Results: | |
- * Zero on success, error code otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCI_ScheduleDelayedWork(VMCIWorkFn *workFn, // IN | |
- void *data) // IN | |
-{ | |
- VMCIDelayedWorkInfo *delayedWorkInfo; | |
- | |
- ASSERT(workFn); | |
- | |
- delayedWorkInfo = VMCI_AllocKernelMem(sizeof *delayedWorkInfo, | |
- VMCI_MEMORY_ATOMIC); | |
- if (!delayedWorkInfo) { | |
- return VMCI_ERROR_NO_MEM; | |
- } | |
- | |
- delayedWorkInfo->workFn = workFn; | |
- delayedWorkInfo->data = data; | |
- | |
- COMPAT_INIT_WORK(&delayedWorkInfo->work, VMCIDelayedWorkCB, | |
- delayedWorkInfo); | |
- | |
- compat_schedule_work(&delayedWorkInfo->work); | |
- | |
- return VMCI_SUCCESS; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCI_CreateEvent -- | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCI_CreateEvent(VMCIEvent *event) // IN: | |
-{ | |
- init_waitqueue_head(event); | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCI_DestroyEvent -- | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCI_DestroyEvent(VMCIEvent *event) // IN: | |
-{ | |
- /* Nothing to do. */ | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCI_SignalEvent -- | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCI_SignalEvent(VMCIEvent *event) // IN: | |
-{ | |
- wake_up(event); | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCI_WaitOnEvent -- | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCI_WaitOnEvent(VMCIEvent *event, // IN: | |
- VMCIEventReleaseCB releaseCB, // IN: | |
- void *clientData) // IN: | |
-{ | |
- /* | |
- * XXX Should this be a TASK_UNINTERRUPTIBLE wait? I'm leaving it | |
- * as it was for now. | |
- */ | |
- VMCI_WaitOnEventInterruptible(event, releaseCB, clientData); | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCI_WaitOnEventInterruptible -- | |
- * | |
- * Results: | |
- * True if the wait was interrupted by a signal, false otherwise. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-Bool | |
-VMCI_WaitOnEventInterruptible(VMCIEvent *event, // IN: | |
- VMCIEventReleaseCB releaseCB, // IN: | |
- void *clientData) // IN: | |
-{ | |
- DECLARE_WAITQUEUE(wait, current); | |
- | |
- if (event == NULL || releaseCB == NULL) { | |
- return FALSE; | |
- } | |
- | |
- add_wait_queue(event, &wait); | |
- current->state = TASK_INTERRUPTIBLE; | |
- | |
- /* | |
- * Release the lock or other primitive that makes it possible for us to | |
- * put the current thread on the wait queue without missing the signal. | |
- * Ie. on Linux we need to put ourselves on the wait queue and set our | |
- * stateto TASK_INTERRUPTIBLE without another thread signalling us. | |
- * The releaseCB is used to synchronize this. | |
- */ | |
- releaseCB(clientData); | |
- | |
- schedule(); | |
- current->state = TASK_RUNNING; | |
- remove_wait_queue(event, &wait); | |
- | |
- return signal_pending(current); | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIMutex_Init -- | |
- * | |
- * Initializes the mutex. Must be called before use. | |
- * | |
- * Results: | |
- * Success. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCIMutex_Init(VMCIMutex *mutex, // IN | |
- char *name, // IN: Unused | |
- VMCILockRank rank) // IN: Unused | |
-{ | |
- sema_init(mutex, 1); | |
- return VMCI_SUCCESS; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIMutex_Destroy -- | |
- * | |
- * Destroys the mutex. Does nothing on Linux. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCIMutex_Destroy(VMCIMutex *mutex) // IN: Unused | |
-{ | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIMutex_Acquire -- | |
- * | |
- * Acquires the mutex. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * Thread may block. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCIMutex_Acquire(VMCIMutex *mutex) // IN: | |
-{ | |
- down(mutex); | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIMutex_Release -- | |
- * | |
- * Releases the mutex. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * May wake up the thread blocking on this mutex. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCIMutex_Release(VMCIMutex *mutex) // IN: | |
-{ | |
- up(mutex); | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCI_AllocQueue -- | |
- * | |
- * Allocates kernel VA space of specified size, plus space for the | |
- * queue structure/kernel interface and the queue header. Allocates | |
- * physical pages for the queue data pages. | |
- * | |
- * PAGE m: VMCIQueueHeader (VMCIQueue->qHeader) | |
- * PAGE m+1: VMCIQueue | |
- * PAGE m+1+q: VMCIQueueKernelIf (VMCIQueue->kernelIf) | |
- * PAGE n-size: Data pages (VMCIQueue->kernelIf->page[]) | |
- * | |
- * Results: | |
- * Pointer to the queue on success, NULL otherwise. | |
- * | |
- * Side effects: | |
- * Memory is allocated. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-void * | |
-VMCI_AllocQueue(uint64 size, // IN: size of queue (not including header) | |
- uint32 flags) // IN: queuepair flags | |
-{ | |
- uint64 i; | |
- VMCIQueue *queue; | |
- VMCIQueueHeader *qHeader; | |
- const uint64 numDataPages = CEILING(size, PAGE_SIZE); | |
- const uint queueSize = | |
- PAGE_SIZE + | |
- sizeof *queue + sizeof *(queue->kernelIf) + | |
- numDataPages * sizeof *(queue->kernelIf->page); | |
- | |
- /* | |
- * Size should be enforced by VMCIQPair_Alloc(), double-check here. | |
- * Allocating too much on Linux can cause the system to become | |
- * unresponsive, because we allocate page-by-page, and we allow the | |
- * system to wait for pages rather than fail. | |
- */ | |
- | |
- if (size > VMCI_MAX_GUEST_QP_MEMORY) { | |
- ASSERT(FALSE); | |
- return NULL; | |
- } | |
- | |
- /* | |
- * If pinning is requested then double-check the size of the queue. | |
- * VMCIQPair_Alloc() will do this for the total queuepair size. | |
- */ | |
- | |
- if ((flags & VMCI_QPFLAG_PINNED) && size > VMCI_MAX_PINNED_QP_MEMORY) { | |
- return NULL; | |
- } | |
- | |
- qHeader = (VMCIQueueHeader *)vmalloc(queueSize); | |
- if (!qHeader) { | |
- return NULL; | |
- } | |
- | |
- queue = (VMCIQueue *)((uint8 *)qHeader + PAGE_SIZE); | |
- queue->qHeader = qHeader; | |
- queue->savedHeader = NULL; | |
- queue->kernelIf = (VMCIQueueKernelIf *)((uint8 *)queue + sizeof *queue); | |
- queue->kernelIf->headerPage = NULL; // Unused in guest. | |
- queue->kernelIf->page = (struct page **)((uint8 *)queue->kernelIf + | |
- sizeof *(queue->kernelIf)); | |
- queue->kernelIf->va = NULL; | |
- queue->kernelIf->host = FALSE; | |
- queue->kernelIf->isDataMapped = FALSE; | |
- | |
- for (i = 0; i < numDataPages; i++) { | |
- queue->kernelIf->page[i] = alloc_pages(GFP_KERNEL, 0); | |
- if (!queue->kernelIf->page[i]) { | |
- VMCI_FreeQueue(queue, i * PAGE_SIZE); | |
- return NULL; | |
- } | |
- } | |
- | |
- /* | |
- * alloc_pages() returns pinned PAs, but we need a permanent mapping to VA | |
- * if the caller has requested pinned queuepairs. Map all of them into | |
- * kernel VA now, for the lifetime of the queue. The page VAs will be | |
- * contiguous. | |
- */ | |
- | |
- if (flags & VMCI_QPFLAG_PINNED) { | |
- queue->kernelIf->va = vmap(queue->kernelIf->page, numDataPages, VM_MAP, | |
- PAGE_KERNEL); | |
- if (NULL == queue->kernelIf->va) { | |
- VMCI_FreeQueue(queue, numDataPages * PAGE_SIZE); | |
- return NULL; | |
- } | |
- queue->kernelIf->isDataMapped = TRUE; | |
- } | |
- | |
- return (void *)queue; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCI_FreeQueue -- | |
- * | |
- * Frees kernel VA space for a given queue and its queue header, and | |
- * frees physical data pages. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * Memory is freed. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCI_FreeQueue(void *q, // IN: | |
- uint64 size) // IN: size of queue (not including header) | |
-{ | |
- VMCIQueue *queue = q; | |
- | |
- if (queue) { | |
- uint64 i; | |
- | |
- if (queue->kernelIf->isDataMapped) { | |
- ASSERT(queue->kernelIf->va); | |
- vunmap(queue->kernelIf->va); | |
- queue->kernelIf->va = NULL; | |
- } | |
- | |
- ASSERT(NULL == queue->kernelIf->va); | |
- | |
- for (i = 0; i < CEILING(size, PAGE_SIZE); i++) { | |
- __free_page(queue->kernelIf->page[i]); | |
- } | |
- vfree(queue->qHeader); | |
- } | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCI_AllocPPNSet -- | |
- * | |
- * Allocates two list of PPNs --- one for the pages in the produce queue, | |
- * and the other for the pages in the consume queue. Intializes the list | |
- * of PPNs with the page frame numbers of the KVA for the two queues (and | |
- * the queue headers). | |
- * | |
- * Results: | |
- * Success or failure. | |
- * | |
- * Side effects: | |
- * Memory may be allocated. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCI_AllocPPNSet(void *prodQ, // IN: | |
- uint64 numProducePages, // IN: for queue plus header | |
- void *consQ, // IN: | |
- uint64 numConsumePages, // IN: for queue plus header | |
- PPNSet *ppnSet) // OUT: | |
-{ | |
- VMCIPpnList producePPNs; | |
- VMCIPpnList consumePPNs; | |
- VMCIQueue *produceQ = prodQ; | |
- VMCIQueue *consumeQ = consQ; | |
- uint64 i; | |
- | |
- if (!produceQ || !numProducePages || !consumeQ || !numConsumePages || | |
- !ppnSet) { | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- | |
- if (ppnSet->initialized) { | |
- return VMCI_ERROR_ALREADY_EXISTS; | |
- } | |
- | |
- producePPNs = | |
- VMCI_AllocKernelMem(numProducePages * sizeof *producePPNs, | |
- VMCI_MEMORY_NORMAL); | |
- if (!producePPNs) { | |
- return VMCI_ERROR_NO_MEM; | |
- } | |
- | |
- consumePPNs = | |
- VMCI_AllocKernelMem(numConsumePages * sizeof *consumePPNs, | |
- VMCI_MEMORY_NORMAL); | |
- if (!consumePPNs) { | |
- VMCI_FreeKernelMem(producePPNs, numProducePages * sizeof *producePPNs); | |
- return VMCI_ERROR_NO_MEM; | |
- } | |
- | |
- producePPNs[0] = page_to_pfn(vmalloc_to_page(produceQ->qHeader)); | |
- for (i = 1; i < numProducePages; i++) { | |
- unsigned long pfn; | |
- | |
- producePPNs[i] = pfn = page_to_pfn(produceQ->kernelIf->page[i - 1]); | |
- | |
- /* | |
- * Fail allocation if PFN isn't supported by hypervisor. | |
- */ | |
- | |
- if (sizeof pfn > sizeof *producePPNs && | |
- pfn != producePPNs[i]) { | |
- goto ppnError; | |
- } | |
- } | |
- consumePPNs[0] = page_to_pfn(vmalloc_to_page(consumeQ->qHeader)); | |
- for (i = 1; i < numConsumePages; i++) { | |
- unsigned long pfn; | |
- | |
- consumePPNs[i] = pfn = page_to_pfn(consumeQ->kernelIf->page[i - 1]); | |
- | |
- /* | |
- * Fail allocation if PFN isn't supported by hypervisor. | |
- */ | |
- | |
- if (sizeof pfn > sizeof *consumePPNs && | |
- pfn != consumePPNs[i]) { | |
- goto ppnError; | |
- } | |
- } | |
- | |
- ppnSet->numProducePages = numProducePages; | |
- ppnSet->numConsumePages = numConsumePages; | |
- ppnSet->producePPNs = producePPNs; | |
- ppnSet->consumePPNs = consumePPNs; | |
- ppnSet->initialized = TRUE; | |
- return VMCI_SUCCESS; | |
- | |
-ppnError: | |
- VMCI_FreeKernelMem(producePPNs, numProducePages * sizeof *producePPNs); | |
- VMCI_FreeKernelMem(consumePPNs, numConsumePages * sizeof *consumePPNs); | |
- return VMCI_ERROR_INVALID_ARGS; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCI_FreePPNSet -- | |
- * | |
- * Frees the two list of PPNs for a queue pair. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCI_FreePPNSet(PPNSet *ppnSet) // IN: | |
-{ | |
- ASSERT(ppnSet); | |
- if (ppnSet->initialized) { | |
- /* Do not call these functions on NULL inputs. */ | |
- ASSERT(ppnSet->producePPNs && ppnSet->consumePPNs); | |
- VMCI_FreeKernelMem(ppnSet->producePPNs, | |
- ppnSet->numProducePages * sizeof *ppnSet->producePPNs); | |
- VMCI_FreeKernelMem(ppnSet->consumePPNs, | |
- ppnSet->numConsumePages * sizeof *ppnSet->consumePPNs); | |
- } | |
- memset(ppnSet, 0, sizeof *ppnSet); | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCI_PopulatePPNList -- | |
- * | |
- * Populates the list of PPNs in the hypercall structure with the PPNS | |
- * of the produce queue and the consume queue. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCI_PopulatePPNList(uint8 *callBuf, // OUT: | |
- const PPNSet *ppnSet) // IN: | |
-{ | |
- ASSERT(callBuf && ppnSet && ppnSet->initialized); | |
- memcpy(callBuf, ppnSet->producePPNs, | |
- ppnSet->numProducePages * sizeof *ppnSet->producePPNs); | |
- memcpy(callBuf + ppnSet->numProducePages * sizeof *ppnSet->producePPNs, | |
- ppnSet->consumePPNs, | |
- ppnSet->numConsumePages * sizeof *ppnSet->consumePPNs); | |
- | |
- return VMCI_SUCCESS; | |
-} | |
- | |
- | |
-#ifdef __KERNEL__ | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * __VMCIMemcpyToQueue -- | |
- * | |
- * Copies from a given buffer or iovector to a VMCI Queue. Uses | |
- * kmap()/kunmap() to dynamically map/unmap required portions of the queue | |
- * by traversing the offset -> page translation structure for the queue. | |
- * Assumes that offset + size does not wrap around in the queue. | |
- * | |
- * Results: | |
- * Zero on success, negative error code on failure. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-__VMCIMemcpyToQueue(VMCIQueue *queue, // OUT: | |
- uint64 queueOffset, // IN: | |
- const void *src, // IN: | |
- size_t size, // IN: | |
- Bool isIovec) // IN: if src is a struct iovec * | |
-{ | |
- VMCIQueueKernelIf *kernelIf = queue->kernelIf; | |
- size_t bytesCopied = 0; | |
- | |
- while (bytesCopied < size) { | |
- uint64 pageIndex = (queueOffset + bytesCopied) / PAGE_SIZE; | |
- size_t pageOffset = (queueOffset + bytesCopied) & (PAGE_SIZE - 1); | |
- void *va; | |
- size_t toCopy; | |
- | |
- if (kernelIf->isDataMapped) { | |
- va = (void *)((uint8 *)kernelIf->va + (pageIndex * PAGE_SIZE)); | |
- } else { | |
- va = kmap(kernelIf->page[pageIndex]); | |
- } | |
- | |
- ASSERT(va); | |
- if (size - bytesCopied > PAGE_SIZE - pageOffset) { | |
- /* Enough payload to fill up from this page. */ | |
- toCopy = PAGE_SIZE - pageOffset; | |
- } else { | |
- toCopy = size - bytesCopied; | |
- } | |
- | |
- if (isIovec) { | |
- struct iovec *iov = (struct iovec *)src; | |
- int err; | |
- | |
- /* The iovec will track bytesCopied internally. */ | |
- err = memcpy_fromiovec((uint8 *)va + pageOffset, iov, toCopy); | |
- if (err != 0) { | |
- if (!kernelIf->isDataMapped) { | |
- kunmap(kernelIf->page[pageIndex]); | |
- } | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- } else { | |
- memcpy((uint8 *)va + pageOffset, (uint8 *)src + bytesCopied, toCopy); | |
- } | |
- | |
- bytesCopied += toCopy; | |
- if (!kernelIf->isDataMapped) { | |
- kunmap(kernelIf->page[pageIndex]); | |
- } | |
- } | |
- | |
- return VMCI_SUCCESS; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * __VMCIMemcpyFromQueue -- | |
- * | |
- * Copies to a given buffer or iovector from a VMCI Queue. Uses | |
- * kmap()/kunmap() to dynamically map/unmap required portions of the queue | |
- * by traversing the offset -> page translation structure for the queue. | |
- * Assumes that offset + size does not wrap around in the queue. | |
- * | |
- * Results: | |
- * Zero on success, negative error code on failure. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-__VMCIMemcpyFromQueue(void *dest, // OUT: | |
- const VMCIQueue *queue, // IN: | |
- uint64 queueOffset, // IN: | |
- size_t size, // IN: | |
- Bool isIovec) // IN: if dest is a struct iovec * | |
-{ | |
- VMCIQueueKernelIf *kernelIf = queue->kernelIf; | |
- size_t bytesCopied = 0; | |
- | |
- while (bytesCopied < size) { | |
- uint64 pageIndex = (queueOffset + bytesCopied) / PAGE_SIZE; | |
- size_t pageOffset = (queueOffset + bytesCopied) & (PAGE_SIZE - 1); | |
- void *va; | |
- size_t toCopy; | |
- | |
- if (kernelIf->isDataMapped) { | |
- va = (void *)((uint8 *)kernelIf->va + (pageIndex * PAGE_SIZE)); | |
- } else { | |
- va = kmap(kernelIf->page[pageIndex]); | |
- } | |
- | |
- ASSERT(va); | |
- if (size - bytesCopied > PAGE_SIZE - pageOffset) { | |
- /* Enough payload to fill up this page. */ | |
- toCopy = PAGE_SIZE - pageOffset; | |
- } else { | |
- toCopy = size - bytesCopied; | |
- } | |
- | |
- if (isIovec) { | |
- struct iovec *iov = (struct iovec *)dest; | |
- int err; | |
- | |
- /* The iovec will track bytesCopied internally. */ | |
- err = memcpy_toiovec(iov, (uint8 *)va + pageOffset, toCopy); | |
- if (err != 0) { | |
- if (!kernelIf->isDataMapped) { | |
- kunmap(kernelIf->page[pageIndex]); | |
- } | |
- return VMCI_ERROR_INVALID_ARGS; | |
- } | |
- } else { | |
- memcpy((uint8 *)dest + bytesCopied, (uint8 *)va + pageOffset, toCopy); | |
- } | |
- | |
- bytesCopied += toCopy; | |
- if (!kernelIf->isDataMapped) { | |
- kunmap(kernelIf->page[pageIndex]); | |
- } | |
- } | |
- | |
- return VMCI_SUCCESS; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIMemcpyToQueue -- | |
- * | |
- * Copies from a given buffer to a VMCI Queue. | |
- * | |
- * Results: | |
- * Zero on success, negative error code on failure. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCIMemcpyToQueue(VMCIQueue *queue, // OUT: | |
- uint64 queueOffset, // IN: | |
- const void *src, // IN: | |
- size_t srcOffset, // IN: | |
- size_t size, // IN: | |
- int bufType, // IN: Unused | |
- Bool canBlock) // IN: Unused | |
-{ | |
- ASSERT(canBlock || !queue->kernelIf->host); | |
- | |
- return __VMCIMemcpyToQueue(queue, queueOffset, | |
- (uint8 *)src + srcOffset, size, FALSE); | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIMemcpyFromQueue -- | |
- * | |
- * Copies to a given buffer from a VMCI Queue. | |
- * | |
- * Results: | |
- * Zero on success, negative error code on failure. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCIMemcpyFromQueue(void *dest, // OUT: | |
- size_t destOffset, // IN: | |
- const VMCIQueue *queue, // IN: | |
- uint64 queueOffset, // IN: | |
- size_t size, // IN: | |
- int bufType, // IN: Unused | |
- Bool canBlock) // IN: Unused | |
-{ | |
- ASSERT(canBlock || !queue->kernelIf->host); | |
- | |
- return __VMCIMemcpyFromQueue((uint8 *)dest + destOffset, | |
- queue, queueOffset, size, FALSE); | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIMemcpyToQueueLocal -- | |
- * | |
- * Copies from a given buffer to a local VMCI queue. On Linux, this is the | |
- * same as a regular copy. | |
- * | |
- * Results: | |
- * Zero on success, negative error code on failure. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCIMemcpyToQueueLocal(VMCIQueue *queue, // OUT | |
- uint64 queueOffset, // IN | |
- const void *src, // IN | |
- size_t srcOffset, // IN | |
- size_t size, // IN | |
- int bufType, // IN: Unused | |
- Bool canBlock) // IN: Unused | |
-{ | |
- ASSERT(canBlock || !queue->kernelIf->host); | |
- | |
- return __VMCIMemcpyToQueue(queue, queueOffset, | |
- (uint8 *)src + srcOffset, size, FALSE);; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIMemcpyFromQueueLocal -- | |
- * | |
- * Copies to a given buffer from a VMCI Queue. | |
- * | |
- * Results: | |
- * Zero on success, negative error code on failure. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCIMemcpyFromQueueLocal(void *dest, // OUT: | |
- size_t destOffset, // IN: | |
- const VMCIQueue *queue, // IN: | |
- uint64 queueOffset, // IN: | |
- size_t size, // IN: | |
- int bufType, // IN: Unused | |
- Bool canBlock) // IN: Unused | |
-{ | |
- ASSERT(canBlock || !queue->kernelIf->host); | |
- | |
- return __VMCIMemcpyFromQueue((uint8 *)dest + destOffset, | |
- queue, queueOffset, size, FALSE); | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------------- | |
- * | |
- * VMCIMemcpyToQueueV -- | |
- * | |
- * Copies from a given iovec from a VMCI Queue. | |
- * | |
- * Results: | |
- * Zero on success, negative error code on failure. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCIMemcpyToQueueV(VMCIQueue *queue, // OUT: | |
- uint64 queueOffset, // IN: | |
- const void *src, // IN: iovec | |
- size_t srcOffset, // IN: ignored | |
- size_t size, // IN: | |
- int bufType, // IN: Unused | |
- Bool canBlock) // IN: Unused | |
-{ | |
- ASSERT(canBlock || !queue->kernelIf->host); | |
- | |
- /* | |
- * We ignore srcOffset because src is really a struct iovec * and will | |
- * maintain offset internally. | |
- */ | |
- return __VMCIMemcpyToQueue(queue, queueOffset, src, size, TRUE); | |
-} | |
- | |
- | |
-/* | |
- *---------------------------------------------------------------------------- | |
- * | |
- * VMCIMemcpyFromQueueV -- | |
- * | |
- * Copies to a given iovec from a VMCI Queue. | |
- * | |
- * Results: | |
- * Zero on success, negative error code on failure. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCIMemcpyFromQueueV(void *dest, // OUT: iovec | |
- size_t destOffset, // IN: ignored | |
- const VMCIQueue *queue, // IN: | |
- uint64 queueOffset, // IN: | |
- size_t size, // IN: | |
- int bufType, // IN: Unused | |
- Bool canBlock) // IN: Unused | |
-{ | |
- ASSERT(canBlock || !queue->kernelIf->host); | |
- | |
- /* | |
- * We ignore destOffset because dest is really a struct iovec * and will | |
- * maintain offset internally. | |
- */ | |
- return __VMCIMemcpyFromQueue(dest, queue, queueOffset, size, TRUE); | |
-} | |
- | |
-#endif | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIWellKnownID_AllowMap -- | |
- * | |
- * Checks whether the calling context is allowed to register for the given | |
- * well known service ID. Currently returns FALSE if the service ID is | |
- * within the reserved range and VMCI_PRIVILEGE_FLAG_TRUSTED is not | |
- * provided as the input privilege flags. Otherwise returns TRUE. | |
- * XXX TODO access control based on host configuration information; this | |
- * will be platform specific implementation. | |
- * | |
- * Results: | |
- * Boolean value indicating access granted or denied. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-Bool | |
-VMCIWellKnownID_AllowMap(VMCIId wellKnownID, // IN: | |
- VMCIPrivilegeFlags privFlags) // IN: | |
-{ | |
- if (wellKnownID < VMCI_RESERVED_RESOURCE_ID_MAX && | |
- !(privFlags & VMCI_PRIVILEGE_FLAG_TRUSTED)) { | |
- return FALSE; | |
- } | |
- return TRUE; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIHost_AllocQueue -- | |
- * | |
- * Allocates kernel VA space of specified size plus space for the queue | |
- * and kernel interface. This is different from the guest queue allocator, | |
- * because we do not allocate our own queue header/data pages here but | |
- * share those of the guest. | |
- * | |
- * Results: | |
- * A pointer to an allocated and initialized VMCIQueue structure or NULL. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-VMCIQueue * | |
-VMCIHost_AllocQueue(uint64 size) // IN: | |
-{ | |
- VMCIQueue *queue; | |
- const size_t numPages = CEILING(size, PAGE_SIZE) + 1; | |
- const size_t queueSize = sizeof *queue + sizeof *(queue->kernelIf); | |
- const size_t queuePageSize = numPages * sizeof *queue->kernelIf->page; | |
- | |
- queue = VMCI_AllocKernelMem(queueSize + queuePageSize, VMCI_MEMORY_NORMAL); | |
- if (queue) { | |
- queue->qHeader = NULL; | |
- queue->savedHeader = NULL; | |
- queue->kernelIf = (VMCIQueueKernelIf *)((uint8 *)queue + sizeof *queue); | |
- queue->kernelIf->host = TRUE; | |
- queue->kernelIf->mutex = NULL; | |
- queue->kernelIf->numPages = numPages; | |
- queue->kernelIf->headerPage = (struct page **)((uint8*)queue + queueSize); | |
- queue->kernelIf->page = &queue->kernelIf->headerPage[1]; | |
- memset(queue->kernelIf->headerPage, 0, | |
- sizeof *queue->kernelIf->headerPage * queue->kernelIf->numPages); | |
- queue->kernelIf->va = NULL; | |
- queue->kernelIf->isDataMapped = FALSE; | |
- } | |
- | |
- return queue; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIHost_FreeQueue -- | |
- * | |
- * Frees kernel memory for a given queue (header plus translation | |
- * structure). | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * Memory is freed. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCIHost_FreeQueue(VMCIQueue *queue, // IN: | |
- uint64 queueSize) // IN: | |
-{ | |
- if (queue) { | |
- const uint queueSize = sizeof *queue + sizeof *(queue->kernelIf); | |
- VMCI_FreeKernelMem(queue, queueSize); | |
- } | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCI_InitQueueMutex() | |
- * | |
- * Initialize the mutex for the pair of queues. This mutex is used to | |
- * protect the qHeader and the buffer from changing out from under any | |
- * users of either queue. Of course, it's only any good if the mutexes | |
- * are actually acquired. Queue structure must lie on non-paged memory | |
- * or we cannot guarantee access to the mutex. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side Effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCI_InitQueueMutex(VMCIQueue *produceQ, // IN/OUT | |
- VMCIQueue *consumeQ) // IN/OUT | |
-{ | |
- ASSERT(produceQ); | |
- ASSERT(consumeQ); | |
- ASSERT(produceQ->kernelIf); | |
- ASSERT(consumeQ->kernelIf); | |
- | |
- /* | |
- * Only the host queue has shared state - the guest queues do not | |
- * need to synchronize access using a queue mutex. | |
- */ | |
- | |
- if (produceQ->kernelIf->host) { | |
- produceQ->kernelIf->mutex = &produceQ->kernelIf->__mutex; | |
- consumeQ->kernelIf->mutex = &produceQ->kernelIf->__mutex; | |
- sema_init(produceQ->kernelIf->mutex, 1); | |
- } | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCI_CleanupQueueMutex() | |
- * | |
- * Cleans up the mutex for the pair of queues. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side Effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCI_CleanupQueueMutex(VMCIQueue *produceQ, // IN/OUT | |
- VMCIQueue *consumeQ) // IN/OUT | |
-{ | |
- ASSERT(produceQ); | |
- ASSERT(consumeQ); | |
- ASSERT(produceQ->kernelIf); | |
- ASSERT(consumeQ->kernelIf); | |
- | |
- if (produceQ->kernelIf->host) { | |
- produceQ->kernelIf->mutex = NULL; | |
- consumeQ->kernelIf->mutex = NULL; | |
- } | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCI_AcquireQueueMutex() | |
- * | |
- * Acquire the mutex for the queue. Note that the produceQ and | |
- * the consumeQ share a mutex. So, only one of the two need to | |
- * be passed in to this routine. Either will work just fine. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS always. | |
- * | |
- * Side Effects: | |
- * May block the caller. | |
- * | |
- *---------------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCI_AcquireQueueMutex(VMCIQueue *queue, // IN | |
- Bool canBlock) // IN: Unused | |
-{ | |
- ASSERT(queue); | |
- ASSERT(queue->kernelIf); | |
- | |
- if (queue->kernelIf->host) { | |
- ASSERT(canBlock); | |
- ASSERT(queue->kernelIf->mutex); | |
- down(queue->kernelIf->mutex); | |
- } | |
- | |
- return VMCI_SUCCESS; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCI_ReleaseQueueMutex() | |
- * | |
- * Release the mutex for the queue. Note that the produceQ and | |
- * the consumeQ share a mutex. So, only one of the two need to | |
- * be passed in to this routine. Either will work just fine. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side Effects: | |
- * May block the caller. | |
- * | |
- *---------------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCI_ReleaseQueueMutex(VMCIQueue *queue) // IN | |
-{ | |
- ASSERT(queue); | |
- ASSERT(queue->kernelIf); | |
- | |
- if (queue->kernelIf->host) { | |
- ASSERT(queue->kernelIf->mutex); | |
- up(queue->kernelIf->mutex); | |
- } | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCI_LockQueueHeader() | |
- * | |
- * Acquire a spinlock guarding the queue header. Note that the produceQ | |
- * and the consumeQ share the lock mutex. So, only one of the two need to | |
- * be passed in to this routine. Either will work just fine. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side Effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCI_LockQueueHeader(VMCIQueue *queue) // IN | |
-{ | |
- ASSERT(queue); | |
- ASSERT(queue->kernelIf); | |
- ASSERT(!queue->kernelIf->host); | |
- | |
- /* | |
- * We don't support non-blocking on the host right now, so we won't get | |
- * here for a host queue. And there's no lock required on the guest. So | |
- * this is a NOP. | |
- */ | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCI_UnlockQueueHeader() | |
- * | |
- * Release the spinlock guarding the queue header. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side Effects: | |
- * None. | |
- * | |
- *---------------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCI_UnlockQueueHeader(VMCIQueue *queue) // IN | |
-{ | |
- ASSERT(queue); | |
- ASSERT(queue->kernelIf); | |
- ASSERT(!queue->kernelIf->host); | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIReleasePageStorePages -- | |
- * | |
- * Helper function to release pages in the PageStoreAttachInfo | |
- * previously obtained using get_user_pages. | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side Effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static void | |
-VMCIReleasePages(struct page **pages, // IN | |
- uint64 numPages, // IN | |
- Bool dirty) // IN | |
-{ | |
- int i; | |
- | |
- for (i = 0; i < numPages; i++) { | |
- ASSERT(pages[i]); | |
- if (dirty) { | |
- set_page_dirty(pages[i]); | |
- } | |
- page_cache_release(pages[i]); | |
- pages[i] = NULL; | |
- } | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIHost_RegisterUserMemory -- | |
- * | |
- * Registers the specification of the user pages used for backing a queue | |
- * pair. Enough information to map in pages is stored in the OS specific | |
- * part of the VMCIQueue structure. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS on sucess, negative error code on failure. | |
- * | |
- * Side Effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCIHost_RegisterUserMemory(unsigned int index, // IN | |
- QueuePairPageStore *pageStore, // IN | |
- VMCIQueue *produceQ, // OUT | |
- VMCIQueue *consumeQ) // OUT | |
-{ | |
- VA64 produceUVA; | |
- VA64 consumeUVA; | |
- | |
- ASSERT(index == 0); | |
- ASSERT(produceQ->kernelIf->headerPage && consumeQ->kernelIf->headerPage); | |
- | |
- /* | |
- * The new style and the old style mapping only differs in that we either | |
- * get a single or two UVAs, so we split the single UVA range at the | |
- * appropriate spot. | |
- */ | |
- | |
- produceUVA = pageStore->pages; | |
- consumeUVA = pageStore->pages + produceQ->kernelIf->numPages * PAGE_SIZE; | |
- return VMCIHost_GetUserMemory(index, produceUVA, consumeUVA, produceQ, consumeQ); | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIHost_UnregisterUserMemory -- | |
- * | |
- * Releases and removes the references to user pages stored in the attach | |
- * struct. | |
- * | |
- * Results: | |
- * None | |
- * | |
- * Side Effects: | |
- * Pages are released from the page cache and may become | |
- * swappable again. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCIHost_UnregisterUserMemory(unsigned int index, // IN | |
- VMCIQueue *produceQ, // IN/OUT | |
- VMCIQueue *consumeQ) // IN/OUT | |
-{ | |
- ASSERT(index == 0); | |
- ASSERT(produceQ->kernelIf); | |
- ASSERT(consumeQ->kernelIf); | |
- ASSERT(!produceQ->qHeader && !consumeQ->qHeader); | |
- | |
- VMCIReleasePages(produceQ->kernelIf->headerPage, produceQ->kernelIf->numPages, TRUE); | |
- memset(produceQ->kernelIf->headerPage, 0, | |
- sizeof *produceQ->kernelIf->headerPage * produceQ->kernelIf->numPages); | |
- VMCIReleasePages(consumeQ->kernelIf->headerPage, consumeQ->kernelIf->numPages, TRUE); | |
- memset(consumeQ->kernelIf->headerPage, 0, | |
- sizeof *consumeQ->kernelIf->headerPage * consumeQ->kernelIf->numPages); | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIHost_MapQueues -- | |
- * | |
- * Once VMCIHost_RegisterUserMemory has been performed on a | |
- * queue, the queue pair headers can be mapped into the | |
- * kernel. Once mapped, they must be unmapped with | |
- * VMCIHost_UnmapQueues prior to calling | |
- * VMCIHost_UnregisterUserMemory. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS if pages are mapped, appropriate error code otherwise. | |
- * | |
- * Side Effects: | |
- * Pages are pinned. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCIHost_MapQueues(unsigned int index, // IN | |
- VMCIQueue *produceQ, // IN/OUT | |
- VMCIQueue *consumeQ, // IN/OUT | |
- uint32 flags) // UNUSED | |
-{ | |
- int result; | |
- | |
- ASSERT(index == 0); | |
- if (!produceQ->qHeader || !consumeQ->qHeader) { | |
- struct page *headers[2]; | |
- | |
- if (produceQ->qHeader != consumeQ->qHeader) { | |
- return VMCI_ERROR_QUEUEPAIR_MISMATCH; | |
- } | |
- | |
- if (produceQ->kernelIf->headerPage == NULL || | |
- *produceQ->kernelIf->headerPage == NULL) { | |
- return VMCI_ERROR_UNAVAILABLE; | |
- } | |
- | |
- ASSERT(*produceQ->kernelIf->headerPage && *consumeQ->kernelIf->headerPage); | |
- | |
- headers[0] = *produceQ->kernelIf->headerPage; | |
- headers[1] = *consumeQ->kernelIf->headerPage; | |
- | |
- produceQ->qHeader = vmap(headers, 2, VM_MAP, PAGE_KERNEL); | |
- if (produceQ->qHeader != NULL) { | |
- consumeQ->qHeader = | |
- (VMCIQueueHeader *)((uint8 *)produceQ->qHeader + PAGE_SIZE); | |
- result = VMCI_SUCCESS; | |
- } else { | |
- Log("vmap failed\n"); | |
- result = VMCI_ERROR_NO_MEM; | |
- } | |
- } else { | |
- result = VMCI_SUCCESS; | |
- } | |
- | |
- return result; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIHost_UnmapQueues -- | |
- * | |
- * Unmaps previously mapped queue pair headers from the kernel. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS always. | |
- * | |
- * Side Effects: | |
- * Pages are unpinned. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCIHost_UnmapQueues(unsigned int index, // IN | |
- VMCIGuestMemID gid, // IN | |
- VMCIQueue *produceQ, // IN/OUT | |
- VMCIQueue *consumeQ) // IN/OUT | |
-{ | |
- ASSERT(index == 0); | |
- if (produceQ->qHeader) { | |
- ASSERT(consumeQ->qHeader); | |
- | |
- if (produceQ->qHeader < consumeQ->qHeader) { | |
- vunmap(produceQ->qHeader); | |
- } else { | |
- vunmap(consumeQ->qHeader); | |
- } | |
- produceQ->qHeader = NULL; | |
- consumeQ->qHeader = NULL; | |
- } | |
- | |
- return VMCI_SUCCESS; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIHost_GetUserMemory -- | |
- * | |
- * | |
- * Lock the user pages referenced by the {produce,consume}Buffer | |
- * struct into memory and populate the {produce,consume}Pages | |
- * arrays in the attach structure with them. | |
- * | |
- * Results: | |
- * VMCI_SUCCESS on sucess, negative error code on failure. | |
- * | |
- * Side Effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-int | |
-VMCIHost_GetUserMemory(unsigned int index, // IN | |
- VA64 produceUVA, // IN | |
- VA64 consumeUVA, // IN | |
- VMCIQueue *produceQ, // OUT | |
- VMCIQueue *consumeQ) // OUT | |
-{ | |
- int retval; | |
- int err = VMCI_SUCCESS; | |
- | |
- ASSERT(index == 0); | |
- down_write(¤t->mm->mmap_sem); | |
- retval = get_user_pages(current, | |
- current->mm, | |
- (VA)produceUVA, | |
- produceQ->kernelIf->numPages, | |
- 1, 0, | |
- produceQ->kernelIf->headerPage, | |
- NULL); | |
- if (retval < produceQ->kernelIf->numPages) { | |
- Log("get_user_pages(produce) failed (retval=%d)\n", retval); | |
- VMCIReleasePages(produceQ->kernelIf->headerPage, retval, FALSE); | |
- err = VMCI_ERROR_NO_MEM; | |
- goto out; | |
- } | |
- | |
- retval = get_user_pages(current, | |
- current->mm, | |
- (VA)consumeUVA, | |
- consumeQ->kernelIf->numPages, | |
- 1, 0, | |
- consumeQ->kernelIf->headerPage, | |
- NULL); | |
- if (retval < consumeQ->kernelIf->numPages) { | |
- Log("get_user_pages(consume) failed (retval=%d)\n", retval); | |
- VMCIReleasePages(consumeQ->kernelIf->headerPage, retval, FALSE); | |
- VMCIReleasePages(produceQ->kernelIf->headerPage, | |
- produceQ->kernelIf->numPages, FALSE); | |
- err = VMCI_ERROR_NO_MEM; | |
- } | |
- | |
-out: | |
- up_write(¤t->mm->mmap_sem); | |
- | |
- return err; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIHost_ReleaseUserMemory -- | |
- * Release the reference to user pages stored in the attach | |
- * struct | |
- * | |
- * Results: | |
- * None | |
- * | |
- * Side Effects: | |
- * Pages are released from the page cache and may become | |
- * swappable again. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCIHost_ReleaseUserMemory(unsigned int index, // IN | |
- VMCIQueue *produceQ, // IN/OUT | |
- VMCIQueue *consumeQ) // IN/OUT | |
-{ | |
- ASSERT(index == 0); | |
- ASSERT(produceQ->kernelIf->headerPage); | |
- | |
- VMCIHost_UnregisterUserMemory(index, produceQ, consumeQ); | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCI_ReadPortBytes -- | |
- * | |
- * Copy memory from an I/O port to kernel memory. | |
- * | |
- * Results: | |
- * No results. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-void | |
-VMCI_ReadPortBytes(VMCIIoHandle handle, // IN: Unused | |
- VMCIIoPort port, // IN | |
- uint8 *buffer, // OUT | |
- size_t bufferLength) // IN | |
-{ | |
- insb(port, buffer, bufferLength); | |
-} | |
diff --git a/modules/linux/vmci/linux/vmci_version.h b/modules/linux/vmci/linux/vmci_version.h | |
deleted file mode 100644 | |
index 9bce229..0000000 | |
--- a/modules/linux/vmci/linux/vmci_version.h | |
+++ /dev/null | |
@@ -1,32 +0,0 @@ | |
-/********************************************************* | |
- * Copyright (C) 2007-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 as published by the | |
- * Free Software Foundation version 2 and no 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 | |
- * | |
- *********************************************************/ | |
- | |
-/* | |
- * vmci_version.h -- | |
- * | |
- * Version definitions for the Linux vmci driver. | |
- */ | |
- | |
-#ifndef _VMCI_VERSION_H_ | |
-#define _VMCI_VERSION_H_ | |
- | |
-#define VMCI_DRIVER_VERSION 9.5.17.0 | |
-#define VMCI_DRIVER_VERSION_COMMAS 9,5,17,0 | |
-#define VMCI_DRIVER_VERSION_STRING "9.5.17.0" | |
- | |
-#endif /* _VMCI_VERSION_H_ */ | |
diff --git a/modules/linux/vmci/shared/pgtbl.h b/modules/linux/vmci/shared/pgtbl.h | |
deleted file mode 100644 | |
index 0f0faee..0000000 | |
--- a/modules/linux/vmci/shared/pgtbl.h | |
+++ /dev/null | |
@@ -1,377 +0,0 @@ | |
-/********************************************************* | |
- * Copyright (C) 2002 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 as published by the | |
- * Free Software Foundation version 2 and no 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 | |
- * | |
- *********************************************************/ | |
- | |
-#ifndef __PGTBL_H__ | |
-# define __PGTBL_H__ | |
- | |
- | |
-#include <linux/highmem.h> | |
- | |
-#include "compat_pgtable.h" | |
-#include "compat_spinlock.h" | |
-#include "compat_page.h" | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * PgtblPte2MPN -- | |
- * | |
- * Returns the page structure associated to a Page Table Entry. | |
- * | |
- * This function is not allowed to schedule() because it can be called while | |
- * holding a spinlock --hpreg | |
- * | |
- * Results: | |
- * INVALID_MPN on failure | |
- * mpn on success | |
- * | |
- * Side effects: | |
- * None | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static INLINE MPN | |
-PgtblPte2MPN(pte_t *pte) // IN | |
-{ | |
- if (pte_present(*pte) == 0) { | |
- return INVALID_MPN; | |
- } | |
- return pte_pfn(*pte); | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * PgtblPte2Page -- | |
- * | |
- * Returns the page structure associated to a Page Table Entry. | |
- * | |
- * This function is not allowed to schedule() because it can be called while | |
- * holding a spinlock --hpreg | |
- * | |
- * Results: | |
- * The page structure if the page table entry points to a physical page | |
- * NULL if the page table entry does not point to a physical page | |
- * | |
- * Side effects: | |
- * None | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static INLINE struct page * | |
-PgtblPte2Page(pte_t *pte) // IN | |
-{ | |
- if (pte_present(*pte) == 0) { | |
- return NULL; | |
- } | |
- | |
- return compat_pte_page(*pte); | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * PgtblPGD2PTELocked -- | |
- * | |
- * Walks through the hardware page tables to try to find the pte | |
- * associated to a virtual address. | |
- * | |
- * Results: | |
- * pte. Caller must call pte_unmap if valid pte returned. | |
- * | |
- * Side effects: | |
- * None | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static INLINE pte_t * | |
-PgtblPGD2PTELocked(compat_pgd_t *pgd, // IN: PGD to start with | |
- VA addr) // IN: Address in the virtual address | |
- // space of that process | |
-{ | |
- compat_pud_t *pud; | |
- pmd_t *pmd; | |
- pte_t *pte; | |
- | |
- if (compat_pgd_present(*pgd) == 0) { | |
- return NULL; | |
- } | |
- | |
- pud = compat_pud_offset(pgd, addr); | |
- if (compat_pud_present(*pud) == 0) { | |
- return NULL; | |
- } | |
- | |
- pmd = pmd_offset_map(pud, addr); | |
- if (pmd_present(*pmd) == 0) { | |
- pmd_unmap(pmd); | |
- return NULL; | |
- } | |
- | |
- pte = pte_offset_map(pmd, addr); | |
- pmd_unmap(pmd); | |
- return pte; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * PgtblVa2PTELocked -- | |
- * | |
- * Walks through the hardware page tables to try to find the pte | |
- * associated to a virtual address. | |
- * | |
- * Results: | |
- * pte. Caller must call pte_unmap if valid pte returned. | |
- * | |
- * Side effects: | |
- * None | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static INLINE pte_t * | |
-PgtblVa2PTELocked(struct mm_struct *mm, // IN: Mm structure of a process | |
- VA addr) // IN: Address in the virtual address | |
- // space of that process | |
-{ | |
- return PgtblPGD2PTELocked(compat_pgd_offset(mm, addr), addr); | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * PgtblVa2MPNLocked -- | |
- * | |
- * Retrieve MPN for a given va. | |
- * | |
- * Caller must call pte_unmap if valid pte returned. The mm->page_table_lock | |
- * must be held, so this function is not allowed to schedule() --hpreg | |
- * | |
- * Results: | |
- * INVALID_MPN on failure | |
- * mpn on success | |
- * | |
- * Side effects: | |
- * None | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static INLINE MPN | |
-PgtblVa2MPNLocked(struct mm_struct *mm, // IN: Mm structure of a process | |
- VA addr) // IN: Address in the virtual address | |
-{ | |
- pte_t *pte; | |
- | |
- pte = PgtblVa2PTELocked(mm, addr); | |
- if (pte != NULL) { | |
- MPN mpn = PgtblPte2MPN(pte); | |
- pte_unmap(pte); | |
- return mpn; | |
- } | |
- return INVALID_MPN; | |
-} | |
- | |
- | |
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0) | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * PgtblKVa2MPNLocked -- | |
- * | |
- * Retrieve MPN for a given kernel va. | |
- * | |
- * Caller must call pte_unmap if valid pte returned. The mm->page_table_lock | |
- * must be held, so this function is not allowed to schedule() --hpreg | |
- * | |
- * Results: | |
- * INVALID_MPN on failure | |
- * mpn on success | |
- * | |
- * Side effects: | |
- * None | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static INLINE MPN | |
-PgtblKVa2MPNLocked(struct mm_struct *mm, // IN: Mm structure of a caller | |
- VA addr) // IN: Address in the virtual address | |
-{ | |
- pte_t *pte; | |
- | |
- pte = PgtblPGD2PTELocked(compat_pgd_offset_k(mm, addr), addr); | |
- if (pte != NULL) { | |
- MPN mpn = PgtblPte2MPN(pte); | |
- pte_unmap(pte); | |
- return mpn; | |
- } | |
- return INVALID_MPN; | |
-} | |
-#endif | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * PgtblVa2PageLocked -- | |
- * | |
- * Return the "page" struct for a given va. | |
- * | |
- * Results: | |
- * struct page or NULL. The mm->page_table_lock must be held, so this | |
- * function is not allowed to schedule() --hpreg | |
- * | |
- * Side effects: | |
- * None | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static INLINE struct page * | |
-PgtblVa2PageLocked(struct mm_struct *mm, // IN: Mm structure of a process | |
- VA addr) // IN: Address in the virtual address | |
-{ | |
- pte_t *pte; | |
- | |
- pte = PgtblVa2PTELocked(mm, addr); | |
- if (pte != NULL) { | |
- struct page *page = PgtblPte2Page(pte); | |
- pte_unmap(pte); | |
- return page; | |
- } else { | |
- return NULL; | |
- } | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * PgtblVa2MPN -- | |
- * | |
- * Walks through the hardware page tables of the current process to try to | |
- * find the page structure associated to a virtual address. | |
- * | |
- * Results: | |
- * Same as PgtblVa2MPNLocked() | |
- * | |
- * Side effects: | |
- * None | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static INLINE int | |
-PgtblVa2MPN(VA addr) // IN | |
-{ | |
- struct mm_struct *mm; | |
- MPN mpn; | |
- | |
- /* current->mm is NULL for kernel threads, so use active_mm. */ | |
- mm = current->active_mm; | |
- if (compat_get_page_table_lock(mm)) { | |
- spin_lock(compat_get_page_table_lock(mm)); | |
- } | |
- mpn = PgtblVa2MPNLocked(mm, addr); | |
- if (compat_get_page_table_lock(mm)) { | |
- spin_unlock(compat_get_page_table_lock(mm)); | |
- } | |
- return mpn; | |
-} | |
- | |
- | |
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0) | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * PgtblKVa2MPN -- | |
- * | |
- * Walks through the hardware page tables of the current process to try to | |
- * find the page structure associated to a virtual address. | |
- * | |
- * Results: | |
- * Same as PgtblVa2MPNLocked() | |
- * | |
- * Side effects: | |
- * None | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static INLINE int | |
-PgtblKVa2MPN(VA addr) // IN | |
-{ | |
- struct mm_struct *mm = current->active_mm; | |
- MPN mpn; | |
- | |
- if (compat_get_page_table_lock(mm)) { | |
- spin_lock(compat_get_page_table_lock(mm)); | |
- } | |
- mpn = PgtblKVa2MPNLocked(mm, addr); | |
- if (compat_get_page_table_lock(mm)) { | |
- spin_unlock(compat_get_page_table_lock(mm)); | |
- } | |
- return mpn; | |
-} | |
-#endif | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * PgtblVa2Page -- | |
- * | |
- * Walks through the hardware page tables of the current process to try to | |
- * find the page structure associated to a virtual address. | |
- * | |
- * Results: | |
- * Same as PgtblVa2PageLocked() | |
- * | |
- * Side effects: | |
- * None | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-static INLINE struct page * | |
-PgtblVa2Page(VA addr) // IN | |
-{ | |
- struct mm_struct *mm = current->active_mm; | |
- struct page *page; | |
- | |
- if (compat_get_page_table_lock(mm)) { | |
- spin_lock(compat_get_page_table_lock(mm)); | |
- } | |
- page = PgtblVa2PageLocked(mm, addr); | |
- if (compat_get_page_table_lock(mm)) { | |
- spin_unlock(compat_get_page_table_lock(mm)); | |
- } | |
- return page; | |
-} | |
- | |
- | |
-#endif /* __PGTBL_H__ */ | |
diff --git a/modules/linux/vmci/shared/vmciQueue.h b/modules/linux/vmci/shared/vmciQueue.h | |
deleted file mode 100644 | |
index 11225e7..0000000 | |
--- a/modules/linux/vmci/shared/vmciQueue.h | |
+++ /dev/null | |
@@ -1,206 +0,0 @@ | |
-/********************************************************* | |
- * Copyright (C) 2010 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 as published by the | |
- * Free Software Foundation version 2 and no 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 | |
- * | |
- *********************************************************/ | |
- | |
-#ifndef _VMCI_QUEUE_H_ | |
-#define _VMCI_QUEUE_H_ | |
- | |
-/* | |
- * | |
- * vmciQueue.h -- | |
- * | |
- * Defines the queue structure, and helper functions to enqueue and dequeue | |
- * items. XXX needs checksumming? | |
- */ | |
- | |
-#define INCLUDE_ALLOW_MODULE | |
-#define INCLUDE_ALLOW_VMX | |
-#define INCLUDE_ALLOW_VMK_MODULE | |
-#define INCLUDE_ALLOW_VMKERNEL | |
-#include "includeCheck.h" | |
- | |
-#if defined(SOLARIS) || defined(__APPLE__) | |
-# include <sys/uio.h> | |
-#endif | |
- | |
-#if defined VMKERNEL | |
-# include "vm_atomic.h" | |
-# include "return_status.h" | |
-# include "util_copy_dist.h" | |
-#endif | |
- | |
- | |
-/* | |
- * VMCIQueue | |
- * | |
- * This data type contains the information about a queue. | |
- * | |
- * There are two queues (hence, queue pairs) per transaction model between a | |
- * pair of end points, A & B. One queue is used by end point A to transmit | |
- * commands and responses to B. The other queue is used by B to transmit | |
- * commands and responses. | |
- * | |
- * VMCIQueueKernelIf is a per-OS defined Queue structure. It contains either a | |
- * direct pointer to the linear address of the buffer contents or a pointer to | |
- * structures which help the OS locate those data pages. See vmciKernelIf.c | |
- * for each platform for its definition. | |
- */ | |
- | |
-typedef struct VMCIQueueKernelIf VMCIQueueKernelIf; | |
- | |
-typedef struct VMCIQueue { | |
- VMCIQueueHeader *qHeader; | |
- VMCIQueueHeader *savedHeader; | |
- VMCIQueueKernelIf *kernelIf; | |
-} VMCIQueue; | |
- | |
- | |
-/* | |
- * ESX uses a buffer type for the memcpy functions. Currently, none | |
- * of the hosted products use such a field. And, to keep the function | |
- * definitions simple, we use a define to declare the type parameter. | |
- */ | |
- | |
-#ifdef VMKERNEL | |
-#define BUF_TYPE Util_BufferType | |
-#else | |
-#define BUF_TYPE int | |
-#endif | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIMemcpy{To,From}QueueFunc() prototypes. Functions of these | |
- * types are passed around to enqueue and dequeue routines. Note that | |
- * often the functions passed are simply wrappers around memcpy | |
- * itself. | |
- * | |
- * Note: In order for the memcpy typedefs to be compatible with the VMKernel, | |
- * there's an unused last parameter for the hosted side. In | |
- * ESX, that parameter holds a buffer type. | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
-typedef int VMCIMemcpyToQueueFunc(VMCIQueue *queue, uint64 queueOffset, | |
- const void *src, size_t srcOffset, | |
- size_t size, BUF_TYPE bufType, | |
- Bool canBlock); | |
-typedef int VMCIMemcpyFromQueueFunc(void *dest, size_t destOffset, | |
- const VMCIQueue *queue, uint64 queueOffset, | |
- size_t size, BUF_TYPE bufType, | |
- Bool canBlock); | |
- | |
- | |
-#if defined(_WIN32) && defined(WINNT_DDK) | |
-/* | |
- * Windows needs iovec for the V functions. We use an MDL for the actual | |
- * buffers, but we also have an offset that comes from WSK_BUF. | |
- */ | |
-typedef struct iovec { | |
- PMDL mdl; // List of memory descriptors. | |
- ULONG offset; // Base offset. | |
-}; | |
-#endif // _WIN32 && WINNT_DDK | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------- | |
- * | |
- * VMCIMemcpy{To,From}Queue[V][Local]() prototypes | |
- * | |
- * Note that these routines are NOT SAFE to call on a host end-point | |
- * until the guest end of the queue pair has attached -AND- | |
- * SetPageStore(). The VMX crosstalk device will issue the | |
- * SetPageStore() on behalf of the guest when the guest creates a | |
- * QueuePair or attaches to one created by the host. So, if the guest | |
- * notifies the host that it's attached then the queue is safe to use. | |
- * Also, if the host registers notification of the connection of the | |
- * guest, then it will only receive that notification when the guest | |
- * has issued the SetPageStore() call and not before (when the guest | |
- * had attached). | |
- * | |
- *----------------------------------------------------------------------------- | |
- */ | |
- | |
-int VMCIMemcpyToQueue(VMCIQueue *queue, uint64 queueOffset, const void *src, | |
- size_t srcOffset, size_t size, BUF_TYPE bufType, | |
- Bool canBlock); | |
-int VMCIMemcpyFromQueue(void *dest, size_t destOffset, const VMCIQueue *queue, | |
- uint64 queueOffset, size_t size, BUF_TYPE bufType, | |
- Bool canBlock); | |
- | |
-int VMCIMemcpyToQueueLocal(VMCIQueue *queue, uint64 queueOffset, const void *src, | |
- size_t srcOffset, size_t size, BUF_TYPE bufType, | |
- Bool canBlock); | |
-int VMCIMemcpyFromQueueLocal(void *dest, size_t destOffset, const VMCIQueue *queue, | |
- uint64 queueOffset, size_t size, BUF_TYPE bufType, | |
- Bool canBlock); | |
- | |
-#if defined VMKERNEL || defined (SOLARIS) || \ | |
- (defined(__APPLE__) && !defined (VMX86_TOOLS)) || \ | |
- (defined(__linux__) && defined(__KERNEL__)) || \ | |
- (defined(_WIN32) && defined(WINNT_DDK)) | |
-int VMCIMemcpyToQueueV(VMCIQueue *queue, uint64 queueOffset, const void *src, | |
- size_t srcOffset, size_t size, BUF_TYPE bufType, | |
- Bool canBlock); | |
-int VMCIMemcpyFromQueueV(void *dest, size_t destOffset, const VMCIQueue *queue, | |
- uint64 queueOffset, size_t size, BUF_TYPE bufType, | |
- Bool canBlock); | |
-# if defined(VMKERNEL) | |
-int VMCIMemcpyToQueueVLocal(VMCIQueue *queue, uint64 queueOffset, | |
- const void *src, size_t srcOffset, size_t size, | |
- BUF_TYPE bufType, Bool canBlock); | |
-int VMCIMemcpyFromQueueVLocal(void *dest, size_t destOffset, | |
- const VMCIQueue *queue, uint64 queueOffset, | |
- size_t size, BUF_TYPE bufType, Bool canBlock); | |
-# else | |
-/* | |
- * For non-vmkernel platforms, the local versions are identical to the | |
- * non-local ones. | |
- */ | |
- | |
-static INLINE int | |
-VMCIMemcpyToQueueVLocal(VMCIQueue *queue, // IN/OUT | |
- uint64 queueOffset, // IN | |
- const void *src, // IN: iovec | |
- size_t srcOffset, // IN | |
- size_t size, // IN | |
- BUF_TYPE bufType, // IN | |
- Bool canBlock) // IN | |
-{ | |
- return VMCIMemcpyToQueueV(queue, queueOffset, src, srcOffset, size, bufType, | |
- canBlock); | |
-} | |
- | |
-static INLINE int | |
-VMCIMemcpyFromQueueVLocal(void *dest, // IN/OUT: iovec | |
- size_t destOffset, // IN | |
- const VMCIQueue *queue, // IN | |
- uint64 queueOffset, // IN | |
- size_t size, // IN | |
- BUF_TYPE bufType, // IN | |
- Bool canBlock) // IN | |
-{ | |
- return VMCIMemcpyFromQueueV(dest, destOffset, queue, queueOffset, size, bufType, | |
- canBlock); | |
-} | |
-# endif /* !VMKERNEL */ | |
-#endif /* Does the O/S support iovec? */ | |
- | |
- | |
-#endif /* !_VMCI_QUEUE_H_ */ | |
- | |
diff --git a/modules/linux/vmci/shared/vmci_handle_array.h b/modules/linux/vmci/shared/vmci_handle_array.h | |
deleted file mode 100644 | |
index b31ab82..0000000 | |
--- a/modules/linux/vmci/shared/vmci_handle_array.h | |
+++ /dev/null | |
@@ -1,374 +0,0 @@ | |
-/********************************************************* | |
- * Copyright (C) 2006 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 as published by the | |
- * Free Software Foundation version 2 and no 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 | |
- * | |
- *********************************************************/ | |
- | |
-/* | |
- * vmci_handle_array.h -- | |
- * | |
- * Simple dynamic array. | |
- */ | |
- | |
-#ifndef _VMCI_HANDLE_ARRAY_H_ | |
-#define _VMCI_HANDLE_ARRAY_H_ | |
- | |
-#define INCLUDE_ALLOW_VMMON | |
-#define INCLUDE_ALLOW_MODULE | |
-#define INCLUDE_ALLOW_VMCORE | |
-#define INCLUDE_ALLOW_VMKERNEL | |
-#include "includeCheck.h" | |
- | |
-#include "vmci_kernel_if.h" | |
-#include "vmware.h" | |
- | |
-#include "vmci_defs.h" | |
-#include "vm_assert.h" | |
-#ifdef VMKERNEL | |
-#include "vm_libc.h" | |
-#endif // VMKERNEL | |
- | |
-#ifdef SOLARIS | |
-#include <sys/ddi.h> | |
-#include <sys/kmem.h> | |
-#include <sys/types.h> | |
-#include <sys/systm.h> | |
-#endif | |
- | |
-#define VMCI_HANDLE_ARRAY_DEFAULT_SIZE 4 | |
- | |
-typedef struct VMCIHandleArray { | |
- uint32 capacity; | |
- uint32 size; | |
- VMCIHandle entries[1]; | |
-} VMCIHandleArray; | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------------- | |
- * | |
- * VMCIHandleArray_Create -- | |
- * | |
- * Results: | |
- * Array if successful, NULL if not. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------------- | |
- */ | |
- | |
-static INLINE VMCIHandleArray * | |
-VMCIHandleArray_Create(uint32 capacity) | |
-{ | |
- VMCIHandleArray *array; | |
- | |
- if (capacity == 0) { | |
- capacity = VMCI_HANDLE_ARRAY_DEFAULT_SIZE; | |
- } | |
- | |
- array = (VMCIHandleArray *)VMCI_AllocKernelMem(sizeof array->capacity + | |
- sizeof array->size + | |
- capacity * sizeof(VMCIHandle), | |
- VMCI_MEMORY_NONPAGED | | |
- VMCI_MEMORY_ATOMIC); | |
- if (array == NULL) { | |
- return NULL; | |
- } | |
- array->capacity = capacity; | |
- array->size = 0; | |
- | |
- return array; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------------- | |
- * | |
- * VMCIHandleArray_Destroy -- | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------------- | |
- */ | |
- | |
-static INLINE void | |
-VMCIHandleArray_Destroy(VMCIHandleArray *array) | |
-{ | |
- VMCI_FreeKernelMem(array, | |
- sizeof array->capacity + sizeof array->size + | |
- array->capacity * sizeof(VMCIHandle)); | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------------- | |
- * | |
- * VMCIHandleArray_AppendEntry -- | |
- * | |
- * Results: | |
- * None. | |
- * | |
- * Side effects: | |
- * Array may be reallocated. | |
- * | |
- *----------------------------------------------------------------------------------- | |
- */ | |
- | |
-static INLINE void | |
-VMCIHandleArray_AppendEntry(VMCIHandleArray **arrayPtr, | |
- VMCIHandle handle) | |
-{ | |
- VMCIHandleArray *array; | |
- | |
- ASSERT(arrayPtr && *arrayPtr); | |
- array = *arrayPtr; | |
- | |
- if (UNLIKELY(array->size >= array->capacity)) { | |
- /* reallocate. */ | |
- uint32 arraySize = sizeof array->capacity + sizeof array->size + | |
- array->capacity * sizeof(VMCIHandle); | |
- VMCIHandleArray *newArray = (VMCIHandleArray *) | |
- VMCI_AllocKernelMem(arraySize + array->capacity * sizeof(VMCIHandle), | |
- VMCI_MEMORY_NONPAGED | VMCI_MEMORY_ATOMIC); | |
- if (newArray == NULL) { | |
- return; | |
- } | |
- memcpy(newArray, array, arraySize); | |
- newArray->capacity *= 2; | |
- VMCI_FreeKernelMem(array, arraySize); | |
- *arrayPtr = newArray; | |
- array = newArray; | |
- } | |
- array->entries[array->size] = handle; | |
- array->size++; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------------- | |
- * | |
- * VMCIHandleArray_RemoveEntry -- | |
- * | |
- * Results: | |
- * Handle that was removed, VMCI_INVALID_HANDLE if entry not found. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------------- | |
- */ | |
- | |
-static INLINE VMCIHandle | |
-VMCIHandleArray_RemoveEntry(VMCIHandleArray *array, | |
- VMCIHandle entryHandle) | |
-{ | |
- uint32 i; | |
- VMCIHandle handle = VMCI_INVALID_HANDLE; | |
- | |
- ASSERT(array); | |
- for (i = 0; i < array->size; i++) { | |
- if (VMCI_HANDLE_EQUAL(array->entries[i], entryHandle)) { | |
- handle = array->entries[i]; | |
- array->entries[i] = array->entries[array->size-1]; | |
- array->entries[array->size-1] = VMCI_INVALID_HANDLE; | |
- array->size--; | |
- break; | |
- } | |
- } | |
- | |
- return handle; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------------- | |
- * | |
- * VMCIHandleArray_RemoveTail -- | |
- * | |
- * Results: | |
- * Handle that was removed, VMCI_INVALID_HANDLE if array was empty. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------------- | |
- */ | |
- | |
-static INLINE VMCIHandle | |
-VMCIHandleArray_RemoveTail(VMCIHandleArray *array) | |
-{ | |
- VMCIHandle handle; | |
- | |
- if (array->size == 0) { | |
- return VMCI_INVALID_HANDLE; | |
- } | |
- handle = array->entries[array->size-1]; | |
- array->entries[array->size-1] = VMCI_INVALID_HANDLE; | |
- array->size--; | |
- | |
- return handle; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------------- | |
- * | |
- * VMCIHandleArray_GetEntry -- | |
- * | |
- * Results: | |
- * Handle at given index, VMCI_INVALID_HANDLE if invalid index. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------------- | |
- */ | |
- | |
-static INLINE VMCIHandle | |
-VMCIHandleArray_GetEntry(const VMCIHandleArray *array, | |
- uint32 index) | |
-{ | |
- ASSERT(array); | |
- if (UNLIKELY(index >= array->size)) { | |
- return VMCI_INVALID_HANDLE; | |
- } | |
- | |
- return array->entries[index]; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------------- | |
- * | |
- * VMCIHandleArray_GetSize -- | |
- * | |
- * Results: | |
- * Number of entries in array. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------------- | |
- */ | |
- | |
-static INLINE uint32 | |
-VMCIHandleArray_GetSize(const VMCIHandleArray *array) | |
-{ | |
- ASSERT(array); | |
- return array->size; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------------- | |
- * | |
- * VMCIHandleArray_HasEntry -- | |
- * | |
- * Results: | |
- * TRUE is entry exists in array, FALSE if not. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------------- | |
- */ | |
- | |
-static INLINE Bool | |
-VMCIHandleArray_HasEntry(const VMCIHandleArray *array, | |
- VMCIHandle entryHandle) | |
-{ | |
- uint32 i; | |
- | |
- ASSERT(array); | |
- for (i = 0; i < array->size; i++) { | |
- if (VMCI_HANDLE_EQUAL(array->entries[i], entryHandle)) { | |
- return TRUE; | |
- } | |
- } | |
- | |
- return FALSE; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------------- | |
- * | |
- * VMCIHandleArray_GetCopy -- | |
- * | |
- * Results: | |
- * Returns pointer to copy of array on success or NULL, if memory allocation | |
- * fails. | |
- * | |
- * Side effects: | |
- * Allocates nonpaged memory. | |
- * | |
- *----------------------------------------------------------------------------------- | |
- */ | |
- | |
-static INLINE VMCIHandleArray * | |
-VMCIHandleArray_GetCopy(const VMCIHandleArray *array) | |
-{ | |
- VMCIHandleArray *arrayCopy; | |
- | |
- ASSERT(array); | |
- | |
- arrayCopy = (VMCIHandleArray *)VMCI_AllocKernelMem(sizeof array->capacity + | |
- sizeof array->size + | |
- array->size * sizeof(VMCIHandle), | |
- VMCI_MEMORY_NONPAGED | | |
- VMCI_MEMORY_ATOMIC); | |
- if (arrayCopy != NULL) { | |
- memcpy(&arrayCopy->size, &array->size, | |
- sizeof array->size + array->size * sizeof(VMCIHandle)); | |
- arrayCopy->capacity = array->size; | |
- } | |
- | |
- return arrayCopy; | |
-} | |
- | |
- | |
-/* | |
- *----------------------------------------------------------------------------------- | |
- * | |
- * VMCIHandleArray_GetHandles -- | |
- * | |
- * Results: | |
- * NULL if the array is empty. Otherwise, a pointer to the array | |
- * of VMCI handles in the handle array. | |
- * | |
- * Side effects: | |
- * None. | |
- * | |
- *----------------------------------------------------------------------------------- | |
- */ | |
- | |
-static INLINE VMCIHandle * | |
-VMCIHandleArray_GetHandles(VMCIHandleArray *array) // IN | |
-{ | |
- ASSERT(array); | |
- | |
- if (array->size) { | |
- return array->entries; | |
- } else { | |
- return NULL; | |
- } | |
-} | |
- | |
-#endif // _VMCI_HANDLE_ARRAY_H_ | |
diff --git a/modules/linux/vmci/shared/vmci_page_channel.h b/modules/linux/vmci/shared/vmci_page_channel.h | |
deleted file mode 100644 | |
index 22e9406..0000000 | |
--- a/modules/linux/vmci/shared/vmci_page_channel.h | |
+++ /dev/null | |
@@ -1,733 +0,0 @@ | |
-/********************************************************* | |
- * Copyright (C) 2011-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 as published by the | |
- * Free Software Foundation version 2 and no 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 | |
- * | |
- *********************************************************/ | |
- | |
-/* | |
- * vmci_page_channel.h | |
- * | |
- * vPageChannel structure and functions. | |
- */ | |
- | |
-#ifndef _VMCI_PAGE_CHANNEL_H_ | |
-#define _VMCI_PAGE_CHANNEL_H_ | |
- | |
-#define INCLUDE_ALLOW_MODULE | |
-#define INCLUDE_ALLOW_VMK_MODULE | |
-#define INCLUDE_ALLOW_VMKERNEL | |
-#include "includeCheck.h" | |
- | |
-#include "vmci_defs.h" | |
-#include "vmci_call_defs.h" | |
- | |
-/** \cond PRIVATE */ | |
-#define VPAGECHANNEL_MAX_TX_BUF_SIZE (1 << 14) | |
-#define VPAGECHANNEL_MAX_PAGES_PER_TX_BUFFER \ | |
- (VPAGECHANNEL_MAX_TX_BUF_SIZE / PAGE_SIZE + 1) | |
-/** \endcond */ | |
- | |
-/** | |
- * \brief Get a pointer to the elements in a packet. | |
- * | |
- * Returns a pointer to the elements at the end of a page channel packet. | |
- * | |
- * \see VPageChannelElem | |
- * \see VPageChannelPacket | |
- */ | |
- | |
-#define VPAGECHANNEL_PACKET_ELEMS(packet) \ | |
- (VPageChannelElem *)((char *)(packet) + \ | |
- sizeof(VPageChannelPacket) + \ | |
- packet->msgLen) | |
- | |
-/** | |
- * \brief Get a pointer to the message in a packet. | |
- * | |
- * Returns a pointer to the message embedded in a page channel packet. | |
- * | |
- * \see VPageChannelPacket | |
- */ | |
- | |
-#define VPAGECHANNEL_PACKET_MESSAGE(packet) \ | |
- (char *)((char *)(packet) + sizeof(VPageChannelPacket)) | |
- | |
-/** | |
- * \brief Notify client directly, and do not read packets. | |
- * | |
- * This flag indicates that the channel should invoke the client's receive | |
- * callback directly when any packets are available. If not specified, then | |
- * when a notification is received, packets are read from the channel and the | |
- * callback invoked for each one separately. | |
- * | |
- * \note Not applicable to VMKernel. | |
- * | |
- * \see VPageChannel_CreateInVM() | |
- */ | |
- | |
-#define VPAGECHANNEL_FLAGS_NOTIFY_ONLY 0x1 | |
- | |
-/** | |
- * \brief Invoke client's receive callback in delayed context. | |
- * | |
- * This flag indicates that all callbacks run in a delayed context, and the | |
- * caller and callback are allowed to block. If not specified, then callbacks | |
- * run in interrupt context and the channel will not block, and the caller | |
- * is not allowed to block. | |
- * | |
- * \note Not applicable to VMKernel. | |
- * | |
- * \see VPageChannel_CreateInVM() | |
- */ | |
- | |
-#define VPAGECHANNEL_FLAGS_RECV_DELAYED 0x2 | |
- | |
-/** | |
- * \brief Send from an atomic context. | |
- * | |
- * This flag indicates that the client wishes to call Send() from an atomic | |
- * context and that the channel should not block. If the channel is not | |
- * allowed to block, then the channel's pages are permanently mapped and | |
- * pinned. Note that this will limit the total size of the channel to | |
- * VMCI_MAX_PINNED_QP_MEMORY. | |
- * | |
- * \note Not applicable to VMKernel. | |
- * | |
- * \see VPageChannel_CreateInVM() | |
- */ | |
- | |
-#define VPAGECHANNEL_FLAGS_SEND_WHILE_ATOMIC 0x4 | |
- | |
-/** | |
- * \brief An element describing a data range. | |
- * | |
- * Describes a data range, starting at a base address and for a given | |
- * length, i.e., an element of a scatter-gather list. Indicates physical | |
- * address for the guest and machine address for hypervisor. Can be passed | |
- * via packets or buffers. | |
- * | |
- * \note Structure is packed. | |
- * | |
- * \see VPageChannelPacket | |
- * \see VPageChanelBuffer | |
- */ | |
- | |
-typedef | |
-#include "vmware_pack_begin.h" | |
-struct VPageChannelElem { | |
- union { | |
- /** \brief Physical address for guest. */ | |
- uint64 pa; | |
- | |
- /** \brief Machine address for hypervisor. */ | |
- uint64 ma; | |
- }; | |
- | |
- /** \brief Length of range. */ | |
- uint32 le; | |
-} | |
-#include "vmware_pack_end.h" | |
-VPageChannelElem; | |
- | |
-/** | |
- * \brief Page channel page types. | |
- * | |
- * The various types of page channel packets. | |
- * | |
- * \see VPageChannelPacket | |
- */ | |
-typedef enum { | |
- /** \brief Data packet. */ | |
- VPCPacket_Data = 1, | |
- | |
- /** \brief Completion notification, from hypervisor to guest. */ | |
- VPCPacket_Completion_Notify, | |
- | |
- /** \cond PRIVATE */ | |
- /** \brief Connect to hypervisor, internal. */ | |
- VPCPacket_GuestConnect, | |
- | |
- /** \brief Complete connection handshake, internal. */ | |
- VPCPacket_HyperConnect, | |
- | |
- /** \brief Request buffers, internal. */ | |
- VPCPacket_RequestBuffer, | |
- | |
- /** \brief Set buffers, internal. */ | |
- VPCPacket_SetRecvBuffer, | |
- | |
- /** \brief Hypervisor channel disconnect, internal. */ | |
- VPCPacket_HyperDisconnect, | |
- | |
- /** \brief Guest channel ACK hypervisor disconnect, internal. */ | |
- VPCPacket_GuestDisconnect, | |
- /** \endcond */ | |
-} VPageChannelPacketType; | |
- | |
-/** | |
- * \brief Page channel packet structure. | |
- * | |
- * A packet structure for passing control/data between guest and hypervisor. | |
- * Can optionally contain a message and also a number of elements. | |
- * | |
- * \note Structure is packed. | |
- * | |
- * \see VPageChannelPacketType | |
- */ | |
-typedef | |
-#include "vmware_pack_begin.h" | |
-struct VPageChannelPacket { | |
- /** \brief Type of packet. */ | |
- VPageChannelPacketType type; | |
- | |
- /** \brief Length of optional message. */ | |
- uint32 msgLen; | |
- | |
- /** \brief Number of optional elements in packet. */ | |
- uint32 numElems; | |
- | |
- /** \brief Followed by msgLen of message and numElems VPageChannelElem. */ | |
-} | |
-#include "vmware_pack_end.h" | |
-VPageChannelPacket; | |
- | |
-/** | |
- * \brief Page channel buffer structure. | |
- * | |
- * A buffer of elements (a scatter-gather list). | |
- * | |
- * \note Structure is packed. | |
- * | |
- * \see VPageChannelElem | |
- */ | |
- | |
-typedef | |
-#include "vmware_pack_begin.h" | |
-struct VPageChannelBuffer { | |
- /** \brief Number of elements. */ | |
- uint32 numElems; | |
- | |
- /** \brief First element. */ | |
- VPageChannelElem elems[1]; | |
- | |
- /** \brief Followed by numElems - 1 of VPageChannelElem. */ | |
-} | |
-#include "vmware_pack_end.h" | |
-VPageChannelBuffer; | |
- | |
-/** \cond PRIVATE */ | |
-typedef | |
-#include "vmware_pack_begin.h" | |
-struct VPageChannelGuestConnectMessage { | |
- | |
- /** \brief Guest channel's datagram handle for control channel. */ | |
- VMCIHandle dgHandle; | |
- | |
- /** \brief Guest channel's queuepair handle. */ | |
- VMCIHandle qpHandle; | |
- | |
- /** \brief Size of producer queue in queuepair in bytes. */ | |
- uint64 produceQSize; | |
- | |
- /** \brief Size of consumer queue in queuepair in bytes. */ | |
- uint64 consumeQSize; | |
- | |
- /** \brief Guest channel's doorbell handle. */ | |
- VMCIHandle doorbellHandle; | |
-} | |
-#include "vmware_pack_end.h" | |
-VPageChannelGuestConnectMessage; | |
- | |
-typedef | |
-#include "vmware_pack_begin.h" | |
-struct VPageChannelHyperConnectMessage { | |
- /** \brief Hypervisor's doorbell handle. */ | |
- VMCIHandle doorbellHandle; | |
-} | |
-#include "vmware_pack_end.h" | |
-VPageChannelHyperConnectMessage; | |
-/** \endcond PRIVATE */ | |
- | |
-/** \cond PRIVATE */ | |
-typedef enum VPageChannelState { | |
- VPCState_Free = 0, | |
- VPCState_Unconnected, | |
- VPCState_Connecting, | |
- VPCState_Connected, | |
- VPCState_Disconnecting, | |
- VPCState_Disconnected, | |
-} VPageChannelState; | |
-/** \endcond PRIVATE */ | |
- | |
-/** | |
- * \brief Opaque page channel type. | |
- */ | |
- | |
-struct VPageChannel; | |
-typedef struct VPageChannel VPageChannel; | |
- | |
-/** | |
- * \brief Client receive callback type. | |
- * | |
- * Type of receive callback, invoked when there are data packets in the | |
- * channel. The client provides a callback with this type to | |
- * VPageChannel_CreateInVM(). If VPAGECHANNEL_FLAGS_NOTIFY_ONLY is specified | |
- * in the channel creation flags, then \c packet is \c NULL; otherwise, | |
- * \c packet points to a channel packet. | |
- * | |
- * \see VPageChannel_CreateInVM() | |
- * \see VPageChannelPacket | |
- */ | |
- | |
-typedef void (*VPageChannelRecvCB)(void *clientData, | |
- VPageChannelPacket *packet); | |
- | |
- | |
-#if !defined(VMKERNEL) | |
- | |
-/** | |
- * \brief Client element allocation callback type. | |
- * | |
- * Type of element allocation callback, invoked when the channel needs | |
- * elements. The client provides a callback of this type to | |
- * VPageChannel_CreateInVM(). | |
- * | |
- * \see VPageChannel_CreateInVM() | |
- * \see VPageChannelElem | |
- * \see VPageChannelFreeElemFn | |
- */ | |
- | |
-typedef int (*VPageChannelAllocElemFn)(void *clientData, | |
- VPageChannelElem *elems, | |
- int numElems); | |
- | |
-/** | |
- * \brief Client element release callback type. | |
- * | |
- * Type of element release callback, invoked when the channel releases | |
- * elements. The client provides a callback of this type to | |
- * VPageChannel_CreateInVM(). | |
- * | |
- * \see VPageChannel_CreateInVM() | |
- * \see VPageChannelElem | |
- * \see VPageChannelAllocElemFn | |
- */ | |
- | |
-typedef void (*VPageChannelFreeElemFn)(void *clientData, | |
- VPageChannelElem *elems, | |
- int numElems); | |
- | |
-/* | |
- ************************************************************************ | |
- * VPageChannel_CreateInVM */ /** | |
- * | |
- * \brief Create guest page channel. | |
- * | |
- * Creates a page channel in the guest. The channel should be released | |
- * with VPageChannel_Destroy(). | |
- * | |
- * \note Only applicable in the guest. | |
- * | |
- * \see VPageChannel_CreateInVMK() | |
- * \see VPageChannel_Destroy() | |
- * | |
- * \param[out] channel Pointer to a newly constructed page | |
- * channel if successful. | |
- * \param[in] resourceId Resource ID on which the channel should | |
- * register its control channel. | |
- * \param[in] peerResourceId Resource ID of peer's control channel. | |
- * \param[in] produceQSize Size of producer queue in queuepair in | |
- * bytes. | |
- * \param[in] consumeQSize Size of consumer queue in queuepair in | |
- * bytes. | |
- * \param[in] flags Channel flags. | |
- * \param[in] recvCB Client's receive callback. | |
- * \param[in] clientRecvData Client data for client's receive | |
- * callback. | |
- * \param[in] elemAlloc Element allocation callback for | |
- * allocating page ranges (scatter-gather | |
- * elements). | |
- * \param[in] allocClientData Client data for element allocation | |
- * callback. | |
- * \param[in] elemFree Element release callback for elements. | |
- * \param[in] freeClientData Client data for element release | |
- * callback. | |
- * \param[in] defRecvBufs Default number of elements sent to | |
- * hypervisor channel. | |
- * \param[in] maxRecvBufs Maximum number of elements that can be | |
- * sent to the hypervisor channel. | |
- * | |
- * \retval VMCI_SUCCESS Creation succeeded, \c *channel contains | |
- * a pointer to a valid channel. | |
- * \retval other Failure. | |
- * | |
- ************************************************************************ | |
- */ | |
- | |
-int VPageChannel_CreateInVM(VPageChannel **channel, | |
- VMCIId resourceId, | |
- VMCIId peerResourceId, | |
- uint64 produceQSize, | |
- uint64 consumeQSize, | |
- uint32 flags, | |
- VPageChannelRecvCB recvCB, | |
- void *clientRecvData, | |
- VPageChannelAllocElemFn elemAlloc, | |
- void *allocClientData, | |
- VPageChannelFreeElemFn elemFree, | |
- void *freeClientData, | |
- int defRecvBufs, | |
- int maxRecvBufs); | |
- | |
-#else // VMKERNEL | |
- | |
-/** | |
- * \brief Type of VM memory access off callback. | |
- * | |
- * This callback is invoked when the memory of the VM containing the peer | |
- * endpoint becomes inaccessible, for example due to a crash. When this | |
- * occurs, the client should unmap any guest pages and cleanup any state. | |
- * This callback runs in a blockable context. The client is not allowed to | |
- * call VPageChannel_Destroy() from inside the callback, or it will deadlock, | |
- * since that function will wait for the callback to complete. | |
- * | |
- * \note Only applicable on VMKernel. | |
- * | |
- * \see VPageChannel_CreateInVMK() | |
- */ | |
- | |
-typedef void (*VPageChannelMemAccessOffCB)(void *clientData); | |
- | |
-/* | |
- ************************************************************************ | |
- * VPageChannel_CreateInVMK */ /** | |
- * | |
- * \brief Create a page channel in VMKernel. | |
- * | |
- * Creates a page channel. The channel should be released with | |
- * VPageChannel_Destroy(). | |
- * | |
- * \note Only applicable on VMKernel. | |
- * | |
- * \see VPageChannel_CreateInVM() | |
- * \see VPageChannel_Destroy() | |
- * | |
- * \param[out] channel Pointer to a newly constructed page | |
- * channel if successful. | |
- * \param[in] resourceId Resource ID on which to register | |
- * control channel. | |
- * \param[in] recvCB Client's receive callback. | |
- * \param[in] clientRecvData Client data for receive callback. | |
- * \param[in] memAccessOffCB Client's mem access off callback. | |
- * \param[in] memAccessOffData Client data for mem access off. | |
- * | |
- * \retval VMCI_SUCCESS Creation succeeded, \c *channel | |
- * contains a pointer to a valid channel. | |
- * \retval other Failure. | |
- * | |
- *********************************************************************** | |
- */ | |
- | |
-int VPageChannel_CreateInVMK(VPageChannel **channel, | |
- VMCIId resourceId, | |
- VPageChannelRecvCB recvCB, | |
- void *clientRecvData, | |
- VPageChannelMemAccessOffCB memAccessOffCB, | |
- void *memAccessOffData); | |
- | |
-/* | |
- ************************************************************************ | |
- * VPageChannel_ReserveBuffers */ /** | |
- * | |
- * \brief Reserve guest elements. | |
- * | |
- * Reserve sufficient guest elements to cover the given length. The | |
- * buffers can then be posted to the guest. This allocates both the | |
- * buffer and the elements within the buffer. | |
- * | |
- * \note Only applicable on VMKernel. | |
- * | |
- * \see VPageChannel_ReleaseBuffers() | |
- * | |
- * \param[in] channel Page channel. | |
- * \param[in] dataLen Length to reserve in bytes. | |
- * \param[out] buffer Pointer to a buffer containing elements to cover | |
- * the given length if successful. | |
- * | |
- * \retval VMCI_SUCCESS Reservation succeeded, \c *buffer contains | |
- * a pointer to a valid buffer. | |
- * \retval other Failure. | |
- * | |
- ************************************************************************ | |
- */ | |
- | |
-int VPageChannel_ReserveBuffers(VPageChannel *channel, | |
- size_t dataLen, | |
- VPageChannelBuffer **buffer); | |
- | |
-/* | |
- ************************************************************************ | |
- * VPageChannel_ReleaseBuffers */ /** | |
- * | |
- * \brief Release guest elements. | |
- * | |
- * \note Only applicable on VMKernel. | |
- * | |
- * \see VPageChannel_ReserveBuffers() | |
- * | |
- * Release guest elements previous reserved with | |
- * VPageChannel_ReserveBuffers(). If the buffers were sent to the guest, | |
- * then only the buffer itself should be released, i.e., | |
- * \c returnToFreePool should be \c FALSE; the guest will release the | |
- * buffers on completion. Otherwise, if for some reason they are not | |
- * sent after reserving them, then \c returnToFreePool should be set to | |
- * \c TRUE. | |
- * | |
- * \param[in] channel Page channel. | |
- * \param[in] buffer Buffer to be released. | |
- * \param[in] returnToFreePool If \c TRUE, then release the elements | |
- * of the buffer along with the buffer | |
- * itself. If \c FALSE, then release only | |
- * the buffer pointer itself. | |
- * | |
- ************************************************************************ | |
- */ | |
- | |
-void VPageChannel_ReleaseBuffers(VPageChannel *channel, | |
- VPageChannelBuffer *buffer, | |
- Bool returnToFreePool); | |
- | |
-/* | |
- ************************************************************************ | |
- * VPageChannel_CompletionNotify */ /** | |
- * | |
- * \brief Notify channel of completion. | |
- * | |
- * This function is called when the client is finished using the elements | |
- * (scatter-gather list) of a packet. This will generated a notification | |
- * to the guest to pass ownership of the buffers back to the guest. This | |
- * can also be used to read back the data from the hypervisor and send | |
- * it to the guest. | |
- * | |
- * \note Only applicable on VMKernel. | |
- * | |
- * \see VPageChannel_ReserveBuffers | |
- * | |
- * \param[in] channel Channel on which I/O is complete. | |
- * \param[in] message Optional message to send to guest. | |
- * \param[in] len Length of optional message. | |
- * \param[in] buffer Buffer used for I/O. | |
- * | |
- ************************************************************************ | |
- */ | |
- | |
-int VPageChannel_CompletionNotify(VPageChannel *channel, | |
- char *message, | |
- int len, | |
- VPageChannelBuffer *buffer); | |
- | |
-/* | |
- ************************************************************************ | |
- * VPageChannel_MapToMa */ /** | |
- * | |
- * \brief Map guest PA in an element to a list of MAs. | |
- * | |
- * Map a guest physical address to a list of hypervisor machine | |
- * addresses. | |
- * | |
- * \note Only applicable on VMKernel. | |
- * | |
- * \param[in] channel Channel on which to map. | |
- * \param[in] paElem Guest's physical address. | |
- * \param[out] maElems Hypervisor machine addresses. | |
- * \param[in] numElems Max number of hypervisor elements. | |
- * | |
- * \retval elems Number of mapped elements. | |
- * | |
- ************************************************************************ | |
- */ | |
- | |
-int VPageChannel_MapToMa(VPageChannel *channel, | |
- VPageChannelElem paElem, | |
- VPageChannelElem *maElems, | |
- uint32 numElems); | |
- | |
-/* | |
- ************************************************************************ | |
- * VPageChannel_UnmapMa */ /** | |
- * | |
- * \brief Unmap MA for a buffer. | |
- * | |
- * Unmap hypervisor machine addresses referring to a guest physical | |
- * addresses. | |
- * | |
- * \note Only applicable on VMKernel. | |
- * | |
- * \see VPageChannel_MapToMa | |
- * | |
- * \param[in] channel Channel for which to unmap. | |
- * \param[in] buffer Buffer containing elements to unmap. | |
- * \param[in] numElems Number of elements to unmap. | |
- * | |
- * \retval 0 Unmap successful. | |
- * \retval -1 World not found for channel. | |
- * | |
- ************************************************************************ | |
- */ | |
- | |
-int VPageChannel_UnmapMa(VPageChannel *channel, | |
- VPageChannelBuffer *buffer, | |
- int numElems); | |
- | |
-#endif // VMKERNEL | |
- | |
-/* | |
- ************************************************************************ | |
- * VPageChannel_Destroy */ /** | |
- * | |
- * \brief Destroy the given channel. | |
- * | |
- * Destroy the given channel. This will disconnect from the peer | |
- * channel (if connected) and release all resources. | |
- * | |
- * \see VPageChannel_CreateInVMK | |
- * \see VPageChannel_CreateInVM | |
- * | |
- * \param[in] channel The channel to be destroyed. | |
- * | |
- ************************************************************************ | |
- */ | |
- | |
-void VPageChannel_Destroy(VPageChannel *channel); | |
- | |
-/* | |
- ************************************************************************ | |
- * VPageChannel_Send */ /** | |
- * | |
- * \brief Send a packet to the channel's peer. | |
- * | |
- * Send a packet to the channel's peer. A message and a number of | |
- * elements may optionally be sent. If the send is successful, the | |
- * elements are owned by the peer and only the buffer itself should | |
- * be released, but not the elements within. If the send fails, the | |
- * client should release the buffer and the elements. | |
- * | |
- * \see VPageChannel_SendPacket | |
- * | |
- * \param[in] channel Channel on which to send. | |
- * \param[in] type Type of packet to send. | |
- * \param[in] message Optional message to send. | |
- * \param[in] len Length of optional message. | |
- * \param[in] buffer Buffer (of elements) to send. | |
- * | |
- * \retval VMCI_SUCCESS Packet successfully sent, buffer elements | |
- * owned by peer. | |
- * \retval other Failure to send, client should release | |
- * elements. | |
- * | |
- ************************************************************************ | |
- */ | |
- | |
-int VPageChannel_Send(VPageChannel *channel, | |
- VPageChannelPacketType type, | |
- char *message, | |
- int len, | |
- VPageChannelBuffer *buffer); | |
- | |
-/* | |
- ************************************************************************ | |
- * VPageChannel_SendPacket */ /** | |
- * | |
- * \brief Send the given packet to the channel's peer. | |
- * | |
- * Send a client-constructed packet to the channel's peer. If the | |
- * send is successful, any elements in the packet are owned by the | |
- * peer. Otherwise, the client retains ownership. | |
- * | |
- * \see VPageChannel_Send | |
- * | |
- * \param[in] channel Channel on which to send. | |
- * \param[in] packet Packet to be sent. | |
- * | |
- * \retval VMCI_SUCCESS Packet successfully sent, buffer elements | |
- * owned by peer. | |
- * \retval other Failure to send, client should release | |
- * elements. | |
- * | |
- ************************************************************************ | |
- */ | |
- | |
-int VPageChannel_SendPacket(VPageChannel *channel, | |
- VPageChannelPacket *packet); | |
- | |
-/* | |
- ************************************************************************ | |
- * VPageChannel_PollRecvQ */ /** | |
- * | |
- * \brief Poll the channel's receive queue for packets. | |
- * | |
- * Poll the channel's receive queue for packets from the peer. If any | |
- * packets are available, the channel's receive callback will be invoked. | |
- * | |
- * \param[in] channel Channel to poll. | |
- * | |
- ************************************************************************ | |
- */ | |
- | |
-void VPageChannel_PollRecvQ(VPageChannel *channel); | |
- | |
-/* | |
- ************************************************************************ | |
- * VPageChannel_BufferLen */ /** | |
- * | |
- * \brief Determine the length of a packet. | |
- * | |
- * Determine the length of the given packet in bytes. | |
- * | |
- * \param[in] packet Packet for which length is to be determined. | |
- * | |
- * \retval bytes Size of the packet in bytes. | |
- * | |
- ************************************************************************ | |
- */ | |
- | |
-static INLINE size_t | |
-VPageChannelPacket_BufferLen(VPageChannelPacket *packet) // IN | |
-{ | |
- size_t len, i; | |
- VPageChannelElem *elems; | |
- | |
- ASSERT(packet); | |
- | |
- len = 0; | |
- elems = VPAGECHANNEL_PACKET_ELEMS(packet); | |
- for (i = 0; i < packet->numElems; i++) { | |
- len += elems[i].le; | |
- } | |
- | |
- return len; | |
-} | |
- | |
-/** \cond PRIVATE */ | |
-#if defined(linux) && !defined(VMKERNEL) | |
-#include "compat_pci.h" | |
-#define vmci_pci_map_page(_pg, _off, _sz, _dir) \ | |
- pci_map_page(NULL, (_pg), (_off), (_sz), (_dir)) | |
-#define vmci_pci_unmap_page(_dma, _sz, _dir) \ | |
- pci_unmap_page(NULL, (_dma), (_sz), (_dir)) | |
-#endif // linux && !VMKERNEL | |
-/** \endcond PRIVATE */ | |
- | |
-#endif // _VMCI_PACKET_H_ | |
diff --git a/modules/linux/vmhgfs/file.c b/modules/linux/vmhgfs/file.c | |
index 1033984..db62070 100644 | |
--- a/modules/linux/vmhgfs/file.c | |
+++ b/modules/linux/vmhgfs/file.c | |
@@ -25,6 +25,7 @@ | |
/* Must come before any kernel header file. */ | |
#include "driver-config.h" | |
+#include <linux/aio.h> | |
#include <linux/errno.h> | |
#include <linux/module.h> | |
#include <linux/signal.h> | |
diff --git a/modules/linux/vmsync/sync.c b/modules/linux/vmsync/sync.c | |
index d05ccad..73baf8b 100644 | |
--- a/modules/linux/vmsync/sync.c | |
+++ b/modules/linux/vmsync/sync.c | |
@@ -162,7 +162,7 @@ VmSyncThawDevices(void *_state) // IN | |
cancel_delayed_work(&state->thawTask); | |
list_for_each_safe(cur, tmp, &state->devices) { | |
dev = list_entry(cur, VmSyncBlockDevice, list); | |
- if (dev->sb != NULL && dev->sb->s_frozen != SB_UNFROZEN) { | |
+ if (dev->sb != NULL && dev->sb->s_writers.frozen != SB_UNFROZEN) { | |
thaw_bdev(dev->bdev, dev->sb); | |
atomic_dec(&gFreezeCount); | |
} | |
@@ -237,7 +237,7 @@ VmSyncAddPath(const VmSyncState *state, // IN | |
* the superblock is already frozen. | |
*/ | |
if (inode->i_sb->s_bdev == NULL || | |
- inode->i_sb->s_frozen != SB_UNFROZEN) { | |
+ inode->i_sb->s_writers.frozen != SB_UNFROZEN) { | |
result = (inode->i_sb->s_bdev == NULL) ? -EINVAL : -EALREADY; | |
compat_path_release(&nd); | |
goto exit; | |
@@ -303,7 +303,7 @@ VmSyncFreezeDevices(VmSyncState *state, // IN | |
const char __user *userPaths) // IN | |
{ | |
int result = 0; | |
- char *paths; | |
+ struct filename *paths; | |
char *currPath; | |
char *nextSep; | |
struct list_head *cur, *tmp; | |
@@ -328,7 +328,8 @@ VmSyncFreezeDevices(VmSyncState *state, // IN | |
/* | |
* First, try to add all paths to the list of paths to be frozen. | |
*/ | |
- currPath = paths; | |
+ currPath = __getname(); | |
+ strcpy(currPath, paths->name); | |
do { | |
nextSep = strchr(currPath, ':'); | |
if (nextSep != NULL) { | |
@@ -347,6 +348,7 @@ VmSyncFreezeDevices(VmSyncState *state, // IN | |
} | |
currPath = nextSep + 1; | |
} while (nextSep != NULL); | |
+ __putname(currPath); | |
/* | |
* If adding all the requested paths worked, then freeze them. | |
@@ -371,6 +373,8 @@ VmSyncFreezeDevices(VmSyncState *state, // IN | |
compat_mutex_unlock(&state->lock); | |
compat_mutex_unlock(&gFreezeLock); | |
+ __putname(paths); | |
+ | |
if (result == 0) { | |
compat_schedule_delayed_work(&state->thawTask, VMSYNC_THAW_TASK_DELAY); | |
} | |
@@ -670,9 +674,10 @@ init_module(void) | |
} | |
/* Create /proc/driver/vmware-sync */ | |
- controlProcEntry = create_proc_entry("driver/vmware-sync", | |
- S_IFREG | S_IRUSR | S_IRGRP | S_IROTH, | |
- NULL); | |
+ controlProcEntry = proc_create("driver/vmware-sync", | |
+ S_IFREG | S_IRUSR | S_IRGRP | S_IROTH, | |
+ NULL, | |
+ &VmSyncFileOps); | |
if (!controlProcEntry) { | |
printk(KERN_ERR "vmsync: could not create /proc/driver/vmware-sync\n"); | |
kmem_cache_destroy(gSyncStateCache); | |
@@ -680,7 +685,6 @@ init_module(void) | |
return -EINVAL; | |
} | |
- controlProcEntry->proc_fops = &VmSyncFileOps; | |
return 0; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment