Compiler les modules vmware sous linux

Afin d’améliorer le fonctionnement et les performances des machines virtuelles, VMware fournit les VMware tools.
Ces  »tools » se décomposent en :

  • une application graphique (wait whaaat ? did you say UI ??)
  • des kernel modules pré-compilés, ainsi que les sources pour les autres noyaux.

J’aurais pu me contenter de fournir les patches, lesquels auraient été suffisant pour compiler les modules, mais en plus d’avoir des codes sources obsolètes, l’installeur s’est mis à dysfonctionner. Impossible donc de compter sur lui pour faire le boulot.

En décortiquant l’installeur, je suis arrivé à un binaire, qui est responsable de la localisation des linux headers sur ma machine, c’est lui qui pour une raison qui m’échappe, ne trouve plus les linux-headers, mais de par sa forme binaire, c’est une voie sans issue (je ne suis pas expert en reverse engineering, et encore moins sous Linux)…

Aucune importance : j’ai trouvé mieux. En décompressant les sources qui se trouvent dans ~/lib/modules/sources , il m’a suffit de lancer un make  et la magie s’est opérée : j’avais mes modules (enfin… après quelques petites modifications) !!

Ce script fonctionne précisément pour les kernels de la branche 3.8 patchés avec grsec. En enlevant les appels à pax_open_kernel  et pax_close_kernel  ou en mettant la bonne pré-condition, on pourrait le rendre compatible avec n’importe quel kernel  de la branche 3.8 je pense.

J’aurais pu fournir les fichiers patch à part, mais j’ai préféré ne garder qu’un seul fichier, afin de conserver une certaine simplicité de gestion pour la maintenance du script.

# sources:
# https://bugs.gentoo.org/attachment.cgi?id=289757&action=edit
# http://pastebin.com/dVPszctW
# https://bugs.gentoo.org/show_bug.cgi?id=461872
mount /mnt/cdrom
tar -xvzf /mnt/cdrom/vmware-tools-distrib.tar.gz /root/
cd /root/vmware-tools-distrib/lib/modules/source

echo compiling vmmemctl...
tar -xvf vmmemctl.tar
cd vmmemctl-only
make
cd ..

echo compiling vmsync
tar -xvf vmsync.tar
cd vmsync-only
echo '
--- driver/sync.c 2013-04-16 21:52:23.277342798 +0200
+++ driver/sync.c 2013-04-16 21:53:43.094056640 +0200
@@ -162,7 +162,7 @@
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 @@
* 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;
' | patch sync.c
make
cd ..

echo compiling vmci
tar -xvf vmci.tar
cd vmci-only
echo '
--- linux/driver.c 2013-04-17 20:19:38.778514246 +0200
+++ linux/driver.c 2013-04-17 20:43:31.109809776 +0200
@@ -124,7 +124,7 @@
.name = "vmci",
.id_table = vmci_ids,
.probe = vmci_probe_device,
- .remove = __devexit_p(vmci_remove_device),
+ .remove = vmci_remove_device,
};

#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
@@ -252,7 +252,21 @@
#define LinuxDriverUnlockIoctlPerFD(mutex) do {} while (0)
#endif

-static struct file_operations vmuser_fops;
+/* Initialize the file_operations structure */
+static struct file_operations vmuser_fops = {
+ .owner = THIS_MODULE,
+ .poll = LinuxDriverPoll,
+#ifdef HAVE_UNLOCKED_IOCTL
+ .unlocked_ioctl = LinuxDriver_UnlockedIoctl,
+#else
+ .ioctl = LinuxDriver_Ioctl,
+#endif
+#ifdef HAVE_COMPAT_IOCTL
+ .compat_ioctl = LinuxDriver_UnlockedIoctl,
+#endif
+ .open = LinuxDriver_Open,
+ .release = LinuxDriver_Close
+};


/*
@@ -389,26 +403,6 @@
return -ENOMEM;
}

- /*
- * Initialize the file_operations structure. Because this code is always
- * compiled as a module, this is fine to do it here and not in a static
- * initializer.
- */
-
- memset(&vmuser_fops, 0, sizeof vmuser_fops);
- vmuser_fops.owner = THIS_MODULE;
- vmuser_fops.poll = LinuxDriverPoll;
-#ifdef HAVE_UNLOCKED_IOCTL
- vmuser_fops.unlocked_ioctl = LinuxDriver_UnlockedIoctl;
-#else
- vmuser_fops.ioctl = LinuxDriver_Ioctl;
-#endif
-#ifdef HAVE_COMPAT_IOCTL
- vmuser_fops.compat_ioctl = LinuxDriver_UnlockedIoctl;
-#endif
- vmuser_fops.open = LinuxDriver_Open;
- vmuser_fops.release = LinuxDriver_Close;
-
sprintf(linuxState.deviceName, "vmci");
linuxState.major = 10;
linuxState.misc.minor = MISC_DYNAMIC_MINOR;
@@ -1750,7 +1744,7 @@
*-----------------------------------------------------------------------------
*/

-static int __devinit
+static int
vmci_probe_device(struct pci_dev *pdev, // IN: vmci PCI device
const struct pci_device_id *id) // IN: matching device ID
{
@@ -1978,7 +1972,7 @@
*-----------------------------------------------------------------------------
*/

-static void __devexit
+static void
vmci_remove_device(struct pci_dev* pdev)
{
struct vmci_device *dev = pci_get_drvdata(pdev);
' | patch linux/driver.c
make
cd ..

echo compiling vsock
tar -xvf vsock.tar
cd vsock-only
cat ../vmci-only/Module.symvers >> Module.symvers
cp linux/af_vsock.c linux/af_vsock.c.bak
echo '
--- linux/af_vsock.c 2013-04-17 20:25:44.093536289 +0200
+++ linux/af_vsock.c 2013-04-17 20:27:51.518312015 +0200
@@ -3160,11 +3160,15 @@
* else in the future.
*/
for (i = NPROTO - 1; i >= 0; i--) {
- vsockVmciFamilyOps.family = i;
+ pax_open_kernel();
+ *(int *)&vsockVmciFamilyOps.family = i;
+ pax_close_kernel();
err = sock_register(&vsockVmciFamilyOps);
if (err) {
Warning("Could not register address family %d.\n", i);
- vsockVmciFamilyOps.family = VSOCK_INVALID_FAMILY;
+ pax_open_kernel();
+ *(int *)&vsockVmciFamilyOps.family = VSOCK_INVALID_FAMILY;
+ pax_close_kernel();
} else {
vsockVmciDgramOps.family = i;
vsockVmciStreamOps.family = i;
@@ -3202,7 +3206,9 @@
sock_unregister(vsockVmciFamilyOps.family);
}

- vsockVmciDgramOps.family = vsockVmciFamilyOps.family = VSOCK_INVALID_FAMILY;
+ pax_open_kernel();
+ vsockVmciDgramOps.family = *(int*)&vsockVmciFamilyOps.family = VSOCK_INVALID_FAMILY;
+ pax_close_kernel();
vsockVmciStreamOps.family = vsockVmciFamilyOps.family;
}' | patch -l linux/af_sock.c
make
cd ..

if [ -f pvscsi.tar ]
tar -xvf pvscsi.tar
cd pvscsi-only
echo '
--- pvscsi.c 2013-05-14 23:24:04.801773269 +0200
+++ pvscsi.c 2013-05-14 23:25:34.701065554 +0200
@@ -455,7 +455,7 @@
SCSI_SENSE_BUFFERSIZE, PCI_DMA_FROMDEVICE);
}

-static int __devinit pvscsi_allocate_rings(struct pvscsi_adapter *adapter)
+static int pvscsi_allocate_rings(struct pvscsi_adapter *adapter)
{
adapter->rings_state = pci_alloc_consistent(adapter->dev, PAGE_SIZE,
&adapter->ringStatePA);
@@ -1234,7 +1234,7 @@
* just use a statically allocated scatter list.
*
*/
-static int __devinit pvscsi_allocate_sg(struct pvscsi_adapter *adapter)
+static int pvscsi_allocate_sg(struct pvscsi_adapter *adapter)
{
struct pvscsi_ctx *ctx;
int i;
@@ -1317,7 +1317,7 @@
return numPhys;
}

-static int __devinit pvscsi_probe(struct pci_dev *pdev,
+static int pvscsi_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
struct pvscsi_adapter *adapter;
@@ -1535,7 +1535,7 @@
.name = "pvscsi",
.id_table = pvscsi_pci_tbl,
.probe = pvscsi_probe,
- .remove = __devexit_p(pvscsi_remove),
+ .remove = pvscsi_remove,
COMPAT_PCI_SHUTDOWN(pvscsi_shutdown)
};' | patch pvscsi.c
make
cd ..
fi

echo installing
LIBDIR=/lib/modules/`uname -r`/misc
[ -d "$LIBDIR" ] || mkdir "$LIBDIR"
cp vmmemctl.o "$LIBDIR/vmmemctl.ko"
cp vmsync.o "$LIBDIR/vmsync.ko"
cp vmci.o "$LIBDIR/vmci.ko"
cp vsock.o "$LIBDIR/vsock.ko"
[ -f pvscsi.o ] && cp pvscsi.o "$LIBDIR/pvscsi.o"

// clean up
rm -R vmci-only vmsync-only vsock-only vmmemctl-only
[ -d pvscsi-only ] && rm -R pvscsi-only

// finished !
# refresh modules.dep.bin for modprobe
depmod

echo "You just have to add this line on /etc/conf.d/modules to auto-load the vmware modules"
echo "module_$(uname -r | sed -e 's/[-.]/_/g')=\"vmmemctl vmsync vmci vsock pvscsi\""

Le petit plus du script : il évite d’installer les VMware tools. Sans interface graphique, ils ne servent à rien, et sous Gentoo, ça pollue la sortie de revdep-rebuild .

Ce script ne compile pas les modules vmxnet  ni vmxnet3 , le premier parce qu’il est déprécié et parce que je ne l’utilise pas, le second parce qu’il est déjà intégré au noyau linux.
Dans des versions ultérieures de linux, il se pourrait que je n’ai même plus à gérer la compilation de vmci  et vsock  : en effet ces derniers pourraient faire une entrée dans le noyau linux.

Notes

J’ai finis de rédiger le script hier soir en compilant mes commandes balancées dans le terminal. Je n’ai jamais testé le script, donc à vos risques et périls !
En revanche, je peux prendre en compte vos remarques.

Impact de la virtualisation sur les performances brutes

Sur les serveurs dédiés sous ma responsabilité, il m’arrive d’y installer un client BOINC. C’est un logiciel qui rapatrie, pour divers projets scientifiques, du travail à effectuer par la machine, laquelle fait alors partie d’une grille de calcul. Bien entendu, BOINC ne travaille que lorsque le serveur ne fait rien, ou plutôt utilise les cœurs non utilisés des CPUs des serveurs. Il n’est pas question que les services en production souffrent de la présence de BOINC. Sous ces conditions, pourquoi pas, ça permet d’utiliser les ressources non utilisées pour une bonne cause, on évite le gâchis.
Ce programme m’a permis de comparer la puissance de calcul brute de plusieurs configuration, sachant que tous les clients BOINC sont configurés pour travailler sur le même projet. En particulier, il y avait deux machines identiques en RAM (non saturées) et en CPU, un Core i5.

L’une de ces machines était sous VMware ESXi avec une seule VM sous Windows Server 2003, l’autre était directement sous Windows Server 2008 R2.
La configuration CPU/RAM identique m’a permis de comparer quantitativement la perte de performance induite par la couche ESXi simplement en regardant le score de BOINC par client.
J’avais entendu parler au cours d’une conférence d’évangélisation que la perte induite par des outils de virtualisation comme VMware Workstation ou Hyper-V R1 était de 15%, tandis que celle de ESXi était seulement de 5%, grâce à son noyau super léger, mais qu’en est-il réellement ?
Pour ce qui est de Workstation, j’en ai aucune idée, je m’en sers que pour le développement donc pas en continue. En revanche on voit une différence de… 15% de entre l’utilisation ou non de VMware ESXi. Wow ! ce n’est pas rien !

On m’aurait menti ? J’ai attendu plusieurs jours pour que le score quotidien des clients BOINC se stabilise et on note toujours cette nette différence de 15%, quelque soit le travail effectué (mais toujours sur le même projet). Il est à noter également que concernant ces deux serveurs, ils ne faisaient quasiment que de l’idle, donc normalement pas perturbés par d’autres processus. C’est un test approximatif et il vaut ce qu’il vaut, mais a l’avantage de donner une assez bonne précision de ce qu’on perd en CPU brut avec cette couche de virtualisation.

Pour autant, la virtualisation n’est pas à jeter, ça dépend de l’utilisation de la machine. Pour mon cas où le CPU est loin d’être le goulet d’étranglement sur ces machines, surtout un Core i5, cette perte est significative, mais acceptable.