]> git.ipfire.org Git - thirdparty/open-vm-tools.git/commitdiff
Always register VMCI Socket control message handlers on Linux
authorVMware, Inc <>
Thu, 24 Feb 2011 21:49:12 +0000 (13:49 -0800)
committerMarcelo Vanzin <mvanzin@vmware.com>
Thu, 24 Feb 2011 21:49:12 +0000 (13:49 -0800)
The VMCI Socket code on Linux only registers an address family
with the kernel as long as there are active sockets. As part
of registering/unregistrering the address family, the VMCI
Socket module also registers/unregisters the stream protocol
control message handlers. This results in no control message
processing being done, when there are no active sockets. For
peers attempting to connect this results in control messages
being silently dropped, and in the case of a connect, the
connect will just hang. This change moves the registration of
the control messages to the vsock module initialization, such
that control messages will always be handled even if there are
no active sockets.

Note that this change also makes the VMCI Socket module
loading fail, if the VMCI device isn't available. This isn't
strictly necessary, but the vsock module isn't all that useful
without the VMCI device.

Signed-off-by: Marcelo Vanzin <mvanzin@vmware.com>
open-vm-tools/modules/linux/vsock/linux/af_vsock.c

index 3ee995464c363cd2a40b1f31f074ef11ba5791b6..093af080da16a0ccd937edab1cf2571695a85fda 100644 (file)
@@ -197,6 +197,8 @@ static struct sock *__VSockVmciCreate(struct net *net,
                                       gfp_t priority, unsigned short type);
 #endif
 static void VSockVmciTestUnregister(void);
+static int VSockVmciRegisterWithVmci(void);
+static void VSockVmciUnregisterWithVmci(void);
 static int VSockVmciRegisterAddressFamily(void);
 static void VSockVmciUnregisterAddressFamily(void);
 
@@ -3054,17 +3056,99 @@ VSockVmciRegisterAddressFamily(void)
 {
    int err = 0;
    int i;
+
+   /*
+    * Linux will not allocate an address family to code that is not part of the
+    * kernel proper, so until that time comes we need a workaround.  Here we
+    * loop through the allowed values and claim the first one that's not
+    * currently used.  Users will then make an ioctl(2) into our module to
+    * retrieve this value before calling socket(2).
+    *
+    * This is undesirable, but it's better than having users' programs break
+    * when a hard-coded, currently-available value gets assigned to someone
+    * else in the future.
+    */
+   for (i = NPROTO - 1; i >= 0; i--) {
+      vsockVmciFamilyOps.family = i;
+      err = sock_register(&vsockVmciFamilyOps);
+      if (err) {
+         Warning("Could not register address family %d.\n", i);
+         vsockVmciFamilyOps.family = VSOCK_INVALID_FAMILY;
+      } else {
+         vsockVmciDgramOps.family = i;
+         vsockVmciStreamOps.family = i;
+         err = i;
+         break;
+      }
+   }
+
+   return err;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * VSockVmciUnregisterAddressFamily --
+ *
+ *      Unregisters the address family with the kernel.
+ *
+ *      Note that this assumes the registration lock is held.
+ *
+ * Results:
+ *      None.
+ *
+ * Side effects:
+ *      Our socket implementation is no longer accessible.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+static void
+VSockVmciUnregisterAddressFamily(void)
+{
+   if (vsockVmciFamilyOps.family != VSOCK_INVALID_FAMILY) {
+      sock_unregister(vsockVmciFamilyOps.family);
+   }
+
+   vsockVmciDgramOps.family = vsockVmciFamilyOps.family = VSOCK_INVALID_FAMILY;
+   vsockVmciStreamOps.family = vsockVmciFamilyOps.family;
+}
+
+
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * VSockVmciRegisterWithVmci --
+ *
+ *      Registers with the VMCI device, and creates control message
+ *      and event handlers.
+ *
+ * Results:
+ *      Zero on success, error code on failure.
+ *
+ * Side effects:
+ *      None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+static int
+VSockVmciRegisterWithVmci(void)
+{
+   int err = 0;
    uint32 apiVersion;
 
    /*
-    * We don't call into the vmci module or register our socket family if the
-    * vmci device isn't present.
+    * We don't call into the vmci module if the vmci device isn't
+    * present.
     */
    apiVersion = VMCI_KERNEL_API_VERSION_1;
    vmciDevicePresent = VMCI_DeviceGet(&apiVersion);
    if (!vmciDevicePresent) {
-      Log("Could not register VMCI Sockets because VMCI device is not present "
-          "or API version is unsupported.\n");
+      Warning("VMCI device not present.\n");
       return -1;
    }
 
@@ -3079,7 +3163,8 @@ VSockVmciRegisterAddressFamily(void)
                                      TRUE);
     if (err < VMCI_SUCCESS) {
       Warning("Unable to create datagram handle. (%d)\n", err);
-      goto error;
+      err = VSockVmci_ErrorToVSockError(err);
+      goto out;
    }
 
    err = VMCIEvent_Subscribe(VMCI_EVENT_QP_RESUMED,
@@ -3091,49 +3176,14 @@ VSockVmciRegisterAddressFamily(void)
       Warning("Unable to subscribe to QP resumed event. (%d)\n", err);
       err = VSockVmci_ErrorToVSockError(err);
       qpResumedSubId = VMCI_INVALID_ID;
-      goto error;
-   }
-
-   /*
-    * Linux will not allocate an address family to code that is not part of the
-    * kernel proper, so until that time comes we need a workaround.  Here we
-    * loop through the allowed values and claim the first one that's not
-    * currently used.  Users will then make an ioctl(2) into our module to
-    * retrieve this value before calling socket(2).
-    *
-    * This is undesirable, but it's better than having users' programs break
-    * when a hard-coded, currently-available value gets assigned to someone
-    * else in the future.
-    */
-   for (i = NPROTO - 1; i >= 0; i--) {
-      vsockVmciFamilyOps.family = i;
-      err = sock_register(&vsockVmciFamilyOps);
-      if (err) {
-         Warning("Could not register address family %d.\n", i);
-         vsockVmciFamilyOps.family = VSOCK_INVALID_FAMILY;
-      } else {
-         vsockVmciDgramOps.family = i;
-         vsockVmciStreamOps.family = i;
-         break;
-      }
-   }
-
-   if (err) {
-      goto error;
+      goto out;
    }
 
-   return vsockVmciFamilyOps.family;
-
-error:
-   if (qpResumedSubId != VMCI_INVALID_ID) {
-      VMCIEvent_Unsubscribe(qpResumedSubId);
-      qpResumedSubId = VMCI_INVALID_ID;
+out:
+   if (err != 0) {
+      VSockVmciUnregisterWithVmci();
    }
 
-   if (!VMCI_HANDLE_INVALID(vmciStreamHandle)) {
-      VMCIDatagram_DestroyHnd(vmciStreamHandle);
-      vmciStreamHandle = VMCI_INVALID_HANDLE;
-   }
    return err;
 }
 
@@ -3141,11 +3191,10 @@ error:
 /*
  *----------------------------------------------------------------------------
  *
- * VSockVmciUnregisterAddressFamily --
+ * VSockVmciUnregisterWithVmci --
  *
- *      Unregisters the address family with the kernel.
- *
- *      Note that this assumes the registration lock is held.
+ *      Destroys control message and event handlers, and unregisters
+ *      with the VMCI device
  *
  * Results:
  *      None.
@@ -3157,7 +3206,7 @@ error:
  */
 
 static void
-VSockVmciUnregisterAddressFamily(void)
+VSockVmciUnregisterWithVmci(void)
 {
    if (!vmciDevicePresent) {
       /* Nothing was registered. */
@@ -3176,14 +3225,8 @@ VSockVmciUnregisterAddressFamily(void)
       qpResumedSubId = VMCI_INVALID_ID;
    }
 
-   if (vsockVmciFamilyOps.family != VSOCK_INVALID_FAMILY) {
-      sock_unregister(vsockVmciFamilyOps.family);
-   }
-
-   vsockVmciDgramOps.family = vsockVmciFamilyOps.family = VSOCK_INVALID_FAMILY;
-   vsockVmciStreamOps.family = vsockVmciFamilyOps.family;
-
    VMCI_DeviceRelease();
+   vmciDevicePresent = FALSE;
 }
 
 
@@ -5099,9 +5142,18 @@ VSockVmciInit(void)
       return err;
    }
 
+   err = VSockVmciRegisterWithVmci();
+   if (err) {
+      Warning("Cannot register with VMCI device.\n");
+      unregister_ioctl32_handlers();
+      misc_deregister(&vsockVmciDevice);
+      return err;
+   }
+
    err = VSockVmciRegisterProto();
    if (err) {
       Warning("Cannot register vsock protocol.\n");
+      VSockVmciUnregisterWithVmci();
       unregister_ioctl32_handlers();
       misc_deregister(&vsockVmciDevice);
       return err;
@@ -5138,6 +5190,7 @@ VSockVmciExit(void)
    compat_mutex_unlock(&registrationMutex);
 
    VSockVmciUnregisterProto();
+   VSockVmciUnregisterWithVmci();
 }