]> git.ipfire.org Git - thirdparty/open-vm-tools.git/commitdiff
This change was reviewed as multiple pieces but is being checked in as one big chunk
authorVMware, Inc <>
Thu, 17 Dec 2009 21:31:13 +0000 (13:31 -0800)
committerMarcelo Vanzin <mvanzin@vmware.com>
Thu, 17 Dec 2009 21:31:13 +0000 (13:31 -0800)
to prevent build breaks since parts depend on other pieces.

Add VMCILOG to the guest

The guest and the host crosstalk drivers have different notions of logging macros. Really we should unify them.
To keep this change from getting any larger I have instead added a temporary VMCILOG macro to the guest.
====
Add VMCI_CanScheduleDelayedWork and VMCI_ScheduleDelayedWork to all VMCI platforms

This change is a precursor to supporting VMCI delayed event callbacks. It provides basic
infrastructure to ask that a callback be scheduled and run in a delayed work context.
Right now this is only implemented on linux and vmkernel. Other platforms currently provide
dummy implementations and return VMCI_ScheduleDelayedWork() as FALSE.
====
Add support for delayed linux VMCI events.

This is the intial change to add delayed event callbacks to vmciEvent.c.
In particular this change:
1. Defines a flags field and requires that all callers pass in delayed or none flags when calling
VMCIEvent_RegisterSubscription.
2. Changes the lock rank of the event callbacks to be above queuepairs.
3. Change the VMCIEvent cleanup routine to require all VMCI events to be unregsitered before the module is unloaded.
4. Reference count the individual vmci event subscriptions so they don't go away while callbacks can be firing.
5. Wait for all callbacks to fire before allowing unsubscription to continue.
6. Modify VMCIEvent_Dispatch to allow callbacks to be fired in a delayed work context with no locks held.
====
Pull in vmciEvent.c changes from the host into vmciEvent.c

This change is a setup change to unify vmciEvent.c on the guest and the host module. This change just pulls in
host side vmciEvent.c code into the guest. Followup changes will make this code actually work.
====
Remove HelperQueue VMKERNEL call from vmciEvent.c.

Previous to this change the VMkernel went through the vmci event subscriber array in a helper world because it
wanted to take the subscriber array lock which was lower rank than the queuepair lock. Now that the VMK always uses
the delayed work mechanism ... this is no longer needed.
====
Make vmkernel work with the new vmciEvent code.

This modifies the core vmkernel vmci interface code to use the new vmci event flag field. This lets
vmkernel continue to compile and lets external (to the module) vmkernel clients use vmci if they desire.
====
Merge bora-vmsoft vmciEvent.c into bora vmciEvent.c

Now that the vmciEvent code is in a state where it can work on both the guest and the host, merge the guest
vmciEvent.c code to be 100% the same as the host code. In the future we can consider just using the same file
for the host and guest.
====
Add extra VMCIEvent_Subscribe param to vsock linux build.

This change just modifies the linux vsock code to pass in the new required param for Event subscribe to existing
calls. This should not change anything about how linux vsock actually works.
====
Register for VMCI_EVENT_CTX_ID_UPDATE

This change makes linux vsock register for VMCI_EVENT_CTX_ID_UPDATE and use the new vmciEvent delayed callback code.
Right now stream sockets break if a vm's cid ever changes and vsock is not reloaded (new stream sockets will not
succeed in being connected). This change fixes that by registering for the VMCI_EVENT_CTX_ID_UPDATE call and
reregistering the datagram handler for the control port.
====
Make common vsock code pass in the new argument to VMCIEvent_Subscribe

This change is required to have the common vsock code continue building. There should be no functional changes
here.
====

Signed-off-by: Marcelo Vanzin <mvanzin@vmware.com>
open-vm-tools/modules/linux/shared/vmciGuestKernelAPI.h
open-vm-tools/modules/linux/shared/vmci_call_defs.h
open-vm-tools/modules/linux/shared/vmci_kernel_if.h
open-vm-tools/modules/linux/vmci/vmciEvent.c
open-vm-tools/modules/linux/vmci/vmciEvent.h
open-vm-tools/modules/linux/vmci/vmciInt.h
open-vm-tools/modules/linux/vmci/vmciKernelIf.c
open-vm-tools/modules/linux/vmci/vmciUtil.c
open-vm-tools/modules/linux/vsock/linux/af_vsock.c
open-vm-tools/modules/linux/vsock/linux/vmciHostKernelAPI.h

index 27a61bffed3ceaebc7ba626cfe2a83fd5b8618ef..87c97508c2e2923af9381fc3c16fd6a6994172ca 100644 (file)
@@ -70,7 +70,7 @@ uint32 VMCI_Version(void);
 typedef void (*VMCI_EventCB)(VMCIId subID, VMCI_EventData *ed,
                             void *clientData);
 
-int VMCIEvent_Subscribe(VMCI_Event event, VMCI_EventCB callback,
+int VMCIEvent_Subscribe(VMCI_Event event, uint32 flags, VMCI_EventCB callback,
                         void *callbackData, VMCIId *subID);
 int VMCIEvent_Unsubscribe(VMCIId subID);
 
index 89542d1f8d2a2607d0ccb495c9b93e5d1fe24ad3..96fc95839a7e5b298778bdea273ba4caa7ea52b6 100644 (file)
@@ -55,6 +55,10 @@ typedef struct VMCIDatagram {
 /* Flag for creating a wellknown handle instead of a per context handle. */
 #define VMCI_FLAG_WELLKNOWN_DG_HND 0x1
 
+/* Event callback should fire in a delayed context (not interrupt context.) */
+#define VMCI_FLAG_EVENT_NONE       0
+#define VMCI_FLAG_EVENT_DELAYED_CB 0x1
+
 /* 
  * Maximum supported size of a VMCI datagram for routable datagrams.
  * Datagrams going to the hypervisor are allowed to be larger.
index 198f4daf185859039cf84194a2c5737aa739deb7..26e9292efe248aef2078e4538740b476260f3d1a 100644 (file)
@@ -151,6 +151,7 @@ typedef int (*VMCIEventReleaseCB)(void *clientData);
 #else
   typedef unsigned long VMCILockRank;
 
+  #define VMCI_LOCK_RANK_HIGHER_BH      0x8000
   #define VMCI_LOCK_RANK_HIGH_BH        0x4000
   #define VMCI_LOCK_RANK_MIDDLE_BH      0x2000
   #define VMCI_LOCK_RANK_LOW_BH         0x1000
@@ -258,6 +259,11 @@ Bool VMCI_WaitOnEventInterruptible(VMCIEvent *event,
 int VMCI_CopyFromUser(void *dst, VA64 src, size_t len);
 #endif
 
+typedef void (VMCIWorkFn)(void *data);
+Bool VMCI_CanScheduleDelayedWork(void);
+int VMCI_ScheduleDelayedWork(VMCIWorkFn  *workFn,
+                             void *data);
+
 int VMCIMutex_Init(VMCIMutex *mutex);
 void VMCIMutex_Destroy(VMCIMutex *mutex);
 void VMCIMutex_Acquire(VMCIMutex *mutex);
index beee76b0d1fd8daad75c44376ccda79ddfc38df0..7ef7ca018b3ab8c043e5d12a6366b100f448f14d 100644 (file)
 
 #if defined(__linux__) && !defined(VMKERNEL)
 #  include "driver-config.h"
-
-#  define EXPORT_SYMTAB
-
-#  include <linux/module.h>
 #  include "compat_kernel.h"
+#  include "compat_module.h"
 #endif // __linux__
 #include "vmci_defs.h"
 #include "vmci_kernel_if.h"
 #include "vmci_infrastructure.h"
 #include "vmciEvent.h"
-#ifdef VMX86_TOOLS 
+#ifdef VMX86_TOOLS
 #  include "vmciInt.h"
 #  include "vmciGuestKernelAPI.h"
 #  include "vmciUtil.h"
+#elif defined(VMKERNEL)
+#  include "vmciVmkInt.h"
+#  include "vm_libc.h"
+#  include "helper_ext.h"
+#  include "vmciDriver.h"
 #else
 #  include "vmciDriver.h"
+#  include "vmciHostKernelAPI.h"
 #endif
-#include "circList.h"
-#ifdef VMKERNEL
-#  include "vm_libc.h"
-#endif
+#include "circList.h"  /* Must come after vmciVmkInt.h. */
 
 #define EVENT_MAGIC 0xEABE0000
 
 
 typedef struct VMCISubscription {
    VMCIId         id;
+   int            refCount;
+   Bool           runDelayed;
+   VMCIEvent      destroyEvent;
    VMCI_Event     event;
    VMCI_EventCB   callback;
    void           *callbackData;
@@ -64,8 +67,11 @@ typedef struct VMCISubscriptionItem {
 
 
 static VMCISubscription *VMCIEventFind(VMCIId subID);
-static int VMCIEventRegisterSubscription(VMCISubscription *sub, VMCI_Event event,
-                                         VMCI_EventCB callback, 
+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);
 
@@ -75,12 +81,12 @@ static VMCISubscription *VMCIEventUnregisterSubscription(VMCIId subID);
  * isn't so, and regular locks are used instead.
  */
 
-#ifdef VMX86_TOOLS 
-#define VMCIEventInitLock(_lock, _name) VMCI_InitLock(_lock, _name, VMCI_LOCK_RANK_MIDDLE_BH)
+#ifdef VMX86_TOOLS
+#define VMCIEventInitLock(_lock, _name) VMCI_InitLock(_lock, _name, VMCI_LOCK_RANK_HIGHER_BH)
 #define VMCIEventGrabLock(_lock, _flags) VMCI_GrabLock_BH(_lock, _flags)
 #define VMCIEventReleaseLock(_lock, _flags) VMCI_ReleaseLock_BH(_lock, _flags)
 #else
-#define VMCIEventInitLock(_lock, _name) VMCI_InitLock(_lock, _name, VMCI_LOCK_RANK_HIGH)
+#define VMCIEventInitLock(_lock, _name) VMCI_InitLock(_lock, _name, VMCI_LOCK_RANK_HIGHER)
 #define VMCIEventGrabLock(_lock, _flags) VMCI_GrabLock(_lock, _flags)
 #define VMCIEventReleaseLock(_lock, _flags) VMCI_ReleaseLock(_lock, _flags)
 #endif
@@ -89,6 +95,11 @@ static VMCISubscription *VMCIEventUnregisterSubscription(VMCIId subID);
 static ListItem *subscriberArray[VMCI_EVENT_MAX] = {NULL};
 static VMCILock subscriberLock;
 
+typedef struct VMCIDelayedEventInfo {
+   VMCISubscription *sub;
+   uint8 eventPayload[sizeof(VMCIEventData_Max)];
+} VMCIDelayedEventInfo;
+
 
 /*
  *----------------------------------------------------------------------
@@ -132,21 +143,29 @@ VMCIEvent_Init(void)
 void
 VMCIEvent_Exit(void)
 {
-   VMCILockFlags flags;
    ListItem *iter, *iter2;
    VMCI_Event e;
 
    /* We free all memory at exit. */
-   VMCIEventGrabLock(&subscriberLock, &flags);
    for (e = 0; e < VMCI_EVENT_MAX; e++) {
       LIST_SCAN_SAFE(iter, iter2, subscriberArray[e]) {
-         VMCISubscription *cur = 
-            LIST_CONTAINER(iter, VMCISubscription, subscriberListItem);
+         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 = LIST_CONTAINER(iter, VMCISubscription, subscriberListItem);
          VMCI_FreeKernelMem(cur, sizeof *cur);
       }
-      subscriberArray[e] = NULL;
    }
-   VMCIEventReleaseLock(&subscriberLock, flags);
    VMCI_CleanupLock(&subscriberLock);
 }
 
@@ -177,6 +196,94 @@ VMCIEvent_CheckHostCapabilities(void)
 }
 #endif
 
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * 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);
+
+   VMCIEventGrabLock(&subscriberLock, &flags);
+   VMCIEventRelease(sub);
+   VMCIEventReleaseLock(&subscriberLock, flags);
+
+   return 0;
+}
+
+
 /*
  *-----------------------------------------------------------------------------
  *
@@ -188,7 +295,7 @@ VMCIEvent_CheckHostCapabilities(void)
  *      Entry if found, NULL if not.
  *
  * Side effects:
- *      None.
+ *      Increments the VMCISubscription refcount if an entry is found.
  *
  *-----------------------------------------------------------------------------
  */
@@ -201,11 +308,12 @@ VMCIEventFind(VMCIId subID)  // IN
 
    for (e = 0; e < VMCI_EVENT_MAX; e++) {
       LIST_SCAN(iter, subscriberArray[e]) {
-         VMCISubscription *cur = 
+         VMCISubscription *cur =
             LIST_CONTAINER(iter, VMCISubscription, subscriberListItem);
-        if (cur->id == subID) {
-           return cur;
-        }
+         if (cur->id == subID) {
+            VMCIEventGet(cur);
+            return cur;
+         }
       }
    }
    return NULL;
@@ -215,9 +323,124 @@ VMCIEventFind(VMCIId subID)  // IN
 /*
  *----------------------------------------------------------------------
  *
- * VMCIEvent_Dispatch -- 
+ * VMCIEventDelayedDispatchCB --
+ *
+ *      Calls the specified callback in a delayed context.
  *
- *      Dispatcher for the VMCI_EVENT_RECEIVE datagrams. Calls all 
+ * 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);
+
+   VMCIEventGrabLock(&subscriberLock, &flags);
+   VMCIEventRelease(sub);
+   VMCIEventReleaseLock(&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;
+   ListItem *iter;
+   VMCILockFlags flags;
+
+   ASSERT(eventMsg);
+
+   VMCIEventGrabLock(&subscriberLock, &flags);
+   LIST_SCAN(iter, subscriberArray[eventMsg->eventData.event]) {
+      VMCI_EventData *ed;
+      VMCISubscription *cur = LIST_CONTAINER(iter, VMCISubscription,
+                                             subscriberListItem);
+      ASSERT(cur && cur->event == eventMsg->eventData.event);
+
+      if (cur->runDelayed) {
+         VMCIDelayedEventInfo *eventInfo;
+         if ((eventInfo = VMCI_AllocKernelMem(sizeof *eventInfo,
+                                 VMCI_MEMORY_ATOMIC)) == 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 {
+         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);
+      }
+   }
+
+out:
+   VMCIEventReleaseLock(&subscriberLock, flags);
+
+   return err;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VMCIEvent_Dispatch --
+ *
+ *      Dispatcher for the VMCI_EVENT_RECEIVE datagrams. Calls all
  *      subscribers for given event.
  *
  * Results:
@@ -232,11 +455,9 @@ VMCIEventFind(VMCIId subID)  // IN
 int
 VMCIEvent_Dispatch(VMCIDatagram *msg)  // IN
 {
-   ListItem *iter;
-   VMCILockFlags flags;
    VMCIEventMsg *eventMsg = (VMCIEventMsg *)msg;
 
-   ASSERT(msg && 
+   ASSERT(msg &&
           msg->src.context == VMCI_HYPERVISOR_CONTEXT_ID &&
           msg->dst.resource == VMCI_EVENT_HANDLER);
 
@@ -249,22 +470,7 @@ VMCIEvent_Dispatch(VMCIDatagram *msg)  // IN
       return VMCI_ERROR_EVENT_UNKNOWN;
    }
 
-   VMCIEventGrabLock(&subscriberLock, &flags);
-   LIST_SCAN(iter, subscriberArray[eventMsg->eventData.event]) {
-      uint8 eventPayload[sizeof(VMCIEventData_Max)];
-      VMCI_EventData *ed;
-      VMCISubscription *cur = LIST_CONTAINER(iter, VMCISubscription,
-                                             subscriberListItem);
-      ASSERT(cur && cur->event == eventMsg->eventData.event);
-
-      /* 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);
-   }
-   VMCIEventReleaseLock(&subscriberLock, flags);
+   VMCIEventDeliver(eventMsg);
 
    return VMCI_SUCCESS;
 }
@@ -289,53 +495,84 @@ VMCIEvent_Dispatch(VMCIDatagram *msg)  // IN
 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 flags;
+   VMCILockFlags lockFlags;
    uint32 attempts = 0;
    int result;
    Bool success;
 
    ASSERT(sub);
-   
+
    if (event >= VMCI_EVENT_MAX || callback == NULL) {
-      VMCI_LOG(("VMCIEvent: Failed to subscribe to event %d cb %p data %p.\n",
-                event, callback, callbackData));
+      VMCILOG(("VMCIEvent: Failed to subscribe to event %d cb %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.
+       */
+      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;
-   
-   VMCIEventGrabLock(&subscriberLock, &flags);
+
+   VMCIEventGrabLock(&subscriberLock, &lockFlags);
    for (success = FALSE, attempts = 0;
-       success == FALSE && attempts < VMCI_EVENT_MAX_ATTEMPTS;
-       attempts++) {
+        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. */
-      if (VMCIEventFind(sub->id) == NULL) {
-        /* We succeeded if we didn't find a duplicate. */
-        success = TRUE;
+      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);
       LIST_QUEUE(&sub->subscriberListItem, &subscriberArray[event]);
       result = VMCI_SUCCESS;
    } else {
       result = VMCI_ERROR_NO_RESOURCES;
    }
-   VMCIEventReleaseLock(&subscriberLock, flags);
+   VMCIEventReleaseLock(&subscriberLock, lockFlags);
 
    return result;
 #  undef VMCI_EVENT_MAX_ATTEMPTS
@@ -364,14 +601,20 @@ VMCIEventUnregisterSubscription(VMCIId subID)    // IN
 {
    VMCILockFlags flags;
    VMCISubscription *s;
-   
+
    VMCIEventGrabLock(&subscriberLock, &flags);
    s = VMCIEventFind(subID);
    if (s != NULL) {
+      VMCIEventRelease(s);
       LIST_DEL(&s->subscriberListItem, &subscriberArray[s->event]);
    }
    VMCIEventReleaseLock(&subscriberLock, flags);
-   
+
+   if (s != NULL) {
+      VMCI_WaitOnEvent(&s->destroyEvent, EventReleaseCB, s);
+      VMCI_DestroyEvent(&s->destroyEvent);
+   }
+
    return s;
 }
 
@@ -381,7 +624,14 @@ VMCIEventUnregisterSubscription(VMCIId subID)    // IN
  *
  * VMCIEventSubscribe --
  *
- *      Subscribe to given event.
+ *      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.
@@ -394,6 +644,7 @@ VMCIEventUnregisterSubscription(VMCIId subID)    // IN
 
 int
 VMCIEventSubscribe(VMCI_Event event,        // IN
+                   uint32 flags,            // IN
                    VMCI_EventCB callback,   // IN
                    void *callbackData,      // IN
                    VMCIId *subscriptionID)  // OUT
@@ -402,7 +653,7 @@ VMCIEventSubscribe(VMCI_Event event,        // IN
    VMCISubscription *s = NULL;
 
    if (subscriptionID == NULL) {
-      VMCI_LOG(("VMCIEvent: Invalid arguments.\n"));
+      VMCILOG(("VMCIEvent: Invalid arguments.\n"));
       return VMCI_ERROR_INVALID_ARGS;
    }
 
@@ -411,7 +662,8 @@ VMCIEventSubscribe(VMCI_Event event,        // IN
       return VMCI_ERROR_NO_MEM;
    }
 
-   retval = VMCIEventRegisterSubscription(s, event, callback, callbackData);
+   retval = VMCIEventRegisterSubscription(s, event, flags,
+                                          callback, callbackData);
    if (retval < VMCI_SUCCESS) {
       VMCI_FreeKernelMem(s, sizeof *s);
       return retval;
@@ -422,6 +674,7 @@ VMCIEventSubscribe(VMCI_Event event,        // IN
 }
 
 
+#ifndef VMKERNEL
 /*
  *----------------------------------------------------------------------
  *
@@ -438,18 +691,21 @@ VMCIEventSubscribe(VMCI_Event event,        // IN
  *----------------------------------------------------------------------
  */
 
-#if defined(__linux__) && !defined(VMKERNEL)
+#if defined(__linux__)
 EXPORT_SYMBOL(VMCIEvent_Subscribe);
 #endif
 
 int
 VMCIEvent_Subscribe(VMCI_Event event,        // IN
+                    uint32 flags,            // IN
                     VMCI_EventCB callback,   // IN
                     void *callbackData,      // IN
                     VMCIId *subscriptionID)  // OUT
 {
-   return VMCIEventSubscribe(event, callback, callbackData, subscriptionID);
+   return VMCIEventSubscribe(event, flags, callback, callbackData,
+                             subscriptionID);
 }
+#endif /* !VMKERNEL  */
 
 
 /*
@@ -457,7 +713,7 @@ VMCIEvent_Subscribe(VMCI_Event event,        // IN
  *
  * VMCIEventUnsubscribe --
  *
- *      Unsubscribe to given event. Removes it from list and frees it. 
+ *      Unsubscribe to given event. Removes it from list and frees it.
  *      Will return callbackData if requested by caller.
  *
  * Results:
@@ -489,6 +745,7 @@ VMCIEventUnsubscribe(VMCIId subID)   // IN
 }
 
 
+#ifndef VMKERNEL
 /*
  *----------------------------------------------------------------------
  *
@@ -506,7 +763,7 @@ VMCIEventUnsubscribe(VMCIId subID)   // IN
  *----------------------------------------------------------------------
  */
 
-#if defined(__linux__) && !defined(VMKERNEL)
+#if defined(__linux__)
 EXPORT_SYMBOL(VMCIEvent_Unsubscribe);
 #endif
 
@@ -515,3 +772,5 @@ VMCIEvent_Unsubscribe(VMCIId subID)   // IN
 {
    return VMCIEventUnsubscribe(subID);
 }
+
+#endif /* !VMKERNEL  */
index 908e27583cd0c0f25166c0529d512405ad219de3..31f40f8a33e51911b590438f2ff46964b2422d90 100644 (file)
@@ -16,7 +16,7 @@
  *
  *********************************************************/
 
-/* 
+/*
  * vmciEvent.h --
  *
  *      Event code for the vmci guest driver
 
 #include "vmci_defs.h"
 #include "vmci_call_defs.h"
+#ifdef VMX86_TOOLS
 #include "vmciGuestKernelAPI.h"
+#else
+#include "vmciHostKernelAPI.h"
+#endif
 
 void VMCIEvent_Init(void);
 void VMCIEvent_Exit(void);
@@ -42,14 +46,12 @@ int  VMCIEvent_Dispatch(VMCIDatagram *msg);
 Bool VMCIEvent_CheckHostCapabilities(void);
 #endif
 
-
 /*
- * Non-public VMCI Event API for guest kernel.
+ * Non-public VMCI Event API for the kernel.
  */
 
-int VMCIEventSubscribe(VMCI_Event event, VMCI_EventCB callback,
+int VMCIEventSubscribe(VMCI_Event event, uint32 flags, VMCI_EventCB callback,
                         void *callbackData, VMCIId *subID);
 int VMCIEventUnsubscribe(VMCIId subID);
 
-
 #endif //__VMCI_EVENT_H__
index 25911d5659632bb9bfda299686fe0ec59a4a0785..f03218014ffc1ffe3b3cee07295b4e7613717f47 100644 (file)
@@ -28,6 +28,8 @@
 
 #define DOLOG(...) printk(KERN_INFO __VA_ARGS__)
 #define VMCI_LOG(_args) DOLOG _args
+/* XXX We need to make this consistant between the guest and the host. */
+#define VMCILOG(_args) DOLOG _args
 
 /* 
  * Called by common code, hence the different naming convention. 
index 1bd87968f8e0e9905ddd503c09fabba19dfce40f..d508c8a4a0e0caac873b8f609132f543b453f2a0 100644 (file)
@@ -41,6 +41,7 @@
 
 #include "compat_version.h"
 #include "compat_wait.h"
+#include "compat_workqueue.h"
 #include "compat_interrupt.h"
 #include "compat_spinlock.h"
 #include "compat_slab.h"
 #  define VMCIKVaToMPN(_ptr) PgtblKVa2MPN((VA)_ptr)
 #endif
 
+typedef struct VMCIDelayedWorkInfo {
+   compat_work work;
+   VMCIWorkFn *workFn;
+   void *data;
+} VMCIDelayedWorkInfo;
+
+
 /*
  *-----------------------------------------------------------------------------
  *
@@ -624,6 +632,102 @@ VMCI_CopyFromUser(void *dst,  // OUT: Kernel VA
 }
 
 
+/*
+ *----------------------------------------------------------------------------
+ *
+ * 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;
+}
+
+
 /*
  *-----------------------------------------------------------------------------
  *
index 46148c6c589831e047b0da7633936e65df3f6e87..e482bd253fbdb1141772f9efbcb20056eceef8da 100644 (file)
@@ -92,7 +92,8 @@ VMCIUtil_Init(void)
     * We subscribe to the VMCI_EVENT_CTX_ID_UPDATE here so we can update the
     * internal context id when needed.
     */
-   if (VMCIEvent_Subscribe(VMCI_EVENT_CTX_ID_UPDATE, VMCIUtilCidUpdate, NULL,
+   if (VMCIEvent_Subscribe(VMCI_EVENT_CTX_ID_UPDATE, VMCI_FLAG_EVENT_NONE,
+                           VMCIUtilCidUpdate, NULL,
                            &ctxUpdateSubID) < VMCI_SUCCESS) {
       VMCI_LOG(("VMCIUtil: Failed to subscribe to event %d.\n", 
                 VMCI_EVENT_CTX_ID_UPDATE));
index 09c356ceefecb5f5ea34786146ef1bc0d7622856..d140b9a8f396f7495d428465efa5d669376c9d19 100644 (file)
@@ -353,6 +353,7 @@ static Bool vmciDevicePresent = FALSE;
 #endif
 static VMCIHandle vmciStreamHandle = { VMCI_INVALID_ID, VMCI_INVALID_ID };
 static VMCIId qpResumedSubId = VMCI_INVALID_ID;
+static VMCIId ctxChangedSubId = VMCI_INVALID_ID;
 
 static int PROTOCOL_OVERRIDE = -1;
 
@@ -1269,6 +1270,52 @@ VSockVmciQPResumedCB(VMCIId subId,             // IN
 }
 
 
+/*
+ *----------------------------------------------------------------------------
+ *
+ * VSockVmciCidChangedCB --
+ *
+ *    Invoked when the context id of the VM may have changed. In this case
+ *    we need to reregister the stream control channel handler.
+ *
+ *    XXX: Open stream sockets will be closed by the detached callback for the
+ *    QP. However, this doesn't fix up bound stream sockets. We should figure
+ *    out what the right thing to do is in that case.
+ *
+ * Results:
+ *    None.
+ *
+ * Side effects:
+ *    None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+static void
+VSockVmciCidChangedCB(VMCIId subId,             // IN
+                      VMCI_EventData *eData,    // IN
+                      void *clientData)         // IN
+{
+   int err;
+   down(&registrationMutex);
+
+   if (!VMCI_HANDLE_INVALID(vmciStreamHandle)) {
+      VMCIDatagram_DestroyHnd(vmciStreamHandle);
+      vmciStreamHandle = VMCI_INVALID_HANDLE;
+   }
+
+   err = VSockVmciDatagramCreateHnd(VSOCK_PACKET_RID, 0,
+                                    VSockVmciRecvStreamCB, NULL,
+                                    &vmciStreamHandle,
+                                    TRUE);
+   if (err < 0) {
+      Warning("Unable to create datagram handle. (%d)\n", err);
+   }
+
+  up(&registrationMutex);
+}
+
+
 /*
  *----------------------------------------------------------------------------
  *
@@ -1718,6 +1765,7 @@ VSockVmciRecvConnectingServer(struct sock *listener, // IN: the listening socket
     * specifying the ATTACH_ONLY flag below.
     */
    err = VMCIEvent_Subscribe(VMCI_EVENT_QP_PEER_DETACH,
+                             VMCI_FLAG_EVENT_NONE,
                              VSockVmciPeerDetachCB,
                              pending,
                              &detachSubId);
@@ -2038,6 +2086,7 @@ VSockVmciRecvConnectingClientNegotiate(struct sock *sk,   // IN: socket
     * once and add a way to lookup sockets by queue pair handle.
     */
    err = VMCIEvent_Subscribe(VMCI_EVENT_QP_PEER_ATTACH,
+                             VMCI_FLAG_EVENT_NONE,
                              VSockVmciPeerAttachCB,
                              sk,
                              &attachSubId);
@@ -2047,6 +2096,7 @@ VSockVmciRecvConnectingClientNegotiate(struct sock *sk,   // IN: socket
    }
 
    err = VMCIEvent_Subscribe(VMCI_EVENT_QP_PEER_DETACH,
+                             VMCI_FLAG_EVENT_NONE,
                              VSockVmciPeerDetachCB,
                              sk,
                              &detachSubId);
@@ -3011,6 +3061,22 @@ VSockVmciRegisterAddressFamily(void)
    }
 #endif
 
+   /*
+    * Register the context id changed callback before creating our datagram
+    * handler to make sure we don't miss a context id change.
+    */
+   err = VMCIEvent_Subscribe(VMCI_EVENT_CTX_ID_UPDATE,
+                             VMCI_FLAG_EVENT_DELAYED_CB,
+                             VSockVmciCidChangedCB,
+                             NULL,
+                             &ctxChangedSubId);
+   if (err < VMCI_SUCCESS) {
+      Warning("Unable to subscribe to Ctx Id update event. (%d)\n", err);
+      err = VSockVmci_ErrorToVSockError(err);
+      ctxChangedSubId = VMCI_INVALID_ID;
+      return VSockVmci_ErrorToVSockError(err);
+   }
+
    /*
     * Create the datagram handle that we will use to send and receive all
     * VSocket control messages for this context.
@@ -3023,10 +3089,11 @@ VSockVmciRegisterAddressFamily(void)
        vmciStreamHandle.context == VMCI_INVALID_ID ||
        vmciStreamHandle.resource == VMCI_INVALID_ID) {
       Warning("Unable to create datagram handle. (%d)\n", err);
-      return VSockVmci_ErrorToVSockError(err);
+      goto error;
    }
 
    err = VMCIEvent_Subscribe(VMCI_EVENT_QP_RESUMED,
+                             VMCI_FLAG_EVENT_NONE,
                              VSockVmciQPResumedCB,
                              NULL,
                              &qpResumedSubId);
@@ -3068,11 +3135,18 @@ VSockVmciRegisterAddressFamily(void)
    return vsockVmciFamilyOps.family;
 
 error:
+   if (ctxChangedSubId != VMCI_INVALID_ID) {
+      VMCIEvent_Unsubscribe(ctxChangedSubId);
+      ctxChangedSubId = VMCI_INVALID_ID;
+   }
    if (qpResumedSubId != VMCI_INVALID_ID) {
       VMCIEvent_Unsubscribe(qpResumedSubId);
       qpResumedSubId = VMCI_INVALID_ID;
    }
-   VMCIDatagram_DestroyHnd(vmciStreamHandle);
+
+   if (!VMCI_HANDLE_INVALID(vmciStreamHandle)) {
+      VMCIDatagram_DestroyHnd(vmciStreamHandle);
+   }
    return err;
 }
 
@@ -3105,6 +3179,11 @@ VSockVmciUnregisterAddressFamily(void)
    }
 #endif
 
+   if (ctxChangedSubId != VMCI_INVALID_ID) {
+      VMCIEvent_Unsubscribe(ctxChangedSubId);
+      ctxChangedSubId = VMCI_INVALID_ID;
+   }
+
    if (!VMCI_HANDLE_INVALID(vmciStreamHandle)) {
       if (VMCIDatagram_DestroyHnd(vmciStreamHandle) != VMCI_SUCCESS) {
          Warning("Could not destroy VMCI datagram handle.\n");
index 4697591531b22d338152b59be850042d26a2b85e..655c8a77de2ab55baca83fc7694c0a453b053233 100644 (file)
@@ -81,7 +81,7 @@ int VMCI_ContextID2HostVmID(VMCIId contextID, void *hostVmID,
 typedef void (*VMCI_EventCB)(VMCIId subID, VMCI_EventData *ed,
                             void *clientData);
 
-int VMCIEvent_Subscribe(VMCI_Event event, VMCI_EventCB callback,
+int VMCIEvent_Subscribe(VMCI_Event event, uint32 flags, VMCI_EventCB callback,
                         void *callbackData, VMCIId *subID);
 int VMCIEvent_Unsubscribe(VMCIId subID);