1
/*********************************************************
2
* Copyright (C) 2007 VMware, Inc. All rights reserved.
4
* This program is free software; you can redistribute it and/or modify it
5
* under the terms of the GNU General Public License as published by the
6
* Free Software Foundation version 2 and no later version.
8
* This program is distributed in the hope that it will be useful, but
9
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13
* You should have received a copy of the GNU General Public License along
14
* with this program; if not, write to the Free Software Foundation, Inc.,
15
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17
*********************************************************/
22
* VMCI Event code for host and guests.
25
#include "vmci_kernel_if.h"
26
#include "vmci_defs.h"
27
#include "vmci_infrastructure.h"
28
#include "vmciEvent.h"
29
#include "vmciKernelAPI.h"
31
# include "vmciVmkInt.h"
33
# include "helper_ext.h"
34
# include "vmciDriver.h"
36
# include "vmciDriver.h"
39
#define LGPFX "VMCIEvent: "
41
#define EVENT_MAGIC 0xEABE0000
43
typedef struct VMCISubscription {
47
VMCIEvent destroyEvent;
49
VMCI_EventCB callback;
51
VMCIListItem subscriberListItem;
55
static VMCISubscription *VMCIEventFind(VMCIId subID);
56
static int VMCIEventDeliver(VMCIEventMsg *eventMsg);
57
static int VMCIEventRegisterSubscription(VMCISubscription *sub,
60
VMCI_EventCB callback,
62
static VMCISubscription *VMCIEventUnregisterSubscription(VMCIId subID);
66
/* VMK doesn't need BH locks, so use lower ranks. */
67
# define VMCIEventInitLock(_lock, _name) \
68
VMCI_InitLock(_lock, _name, VMCI_LOCK_RANK_HIGHER)
69
# define VMCIEventGrabLock(_lock, _flags) VMCI_GrabLock(_lock, _flags)
70
# define VMCIEventReleaseLock(_lock, _flags) VMCI_ReleaseLock(_lock, _flags)
72
# define VMCIEventInitLock(_lock, _name) \
73
VMCI_InitLock(_lock, _name, VMCI_LOCK_RANK_HIGHER_BH)
74
# define VMCIEventGrabLock(_lock, _flags) VMCI_GrabLock_BH(_lock, _flags)
75
# define VMCIEventReleaseLock(_lock, _flags) VMCI_ReleaseLock_BH(_lock, _flags)
79
static VMCIList subscriberArray[VMCI_EVENT_MAX];
80
static VMCILock subscriberLock;
82
typedef struct VMCIDelayedEventInfo {
83
VMCISubscription *sub;
84
uint8 eventPayload[sizeof(VMCIEventData_Max)];
85
} VMCIDelayedEventInfo;
89
*----------------------------------------------------------------------
101
*----------------------------------------------------------------------
109
for (i = 0; i < VMCI_EVENT_MAX; i++) {
110
VMCIList_Init(&subscriberArray[i]);
112
VMCIEventInitLock(&subscriberLock, "VMCIEventSubscriberLock");
117
*----------------------------------------------------------------------
129
*----------------------------------------------------------------------
135
VMCIListItem *iter, *iter2;
138
/* We free all memory at exit. */
139
for (e = 0; e < VMCI_EVENT_MAX; e++) {
140
VMCIList_ScanSafe(iter, iter2, &subscriberArray[e]) {
141
VMCISubscription *cur;
144
* We should never get here because all events should have been
145
* unregistered before we try to unload the driver module.
146
* Also, delayed callbacks could still be firing so this cleanup
148
* Still it is better to free the memory than not ... so we
149
* leave this code in just in case....
154
cur = VMCIList_Entry(iter, VMCISubscription, subscriberListItem);
155
VMCI_FreeKernelMem(cur, sizeof *cur);
158
VMCI_CleanupLock(&subscriberLock);
163
*-----------------------------------------------------------------------------
167
* Use this as a synchronization point when setting globals, for example,
168
* during device shutdown.
176
*-----------------------------------------------------------------------------
182
VMCILockFlags lockFlags;
183
VMCIEventGrabLock(&subscriberLock, &lockFlags);
184
VMCIEventReleaseLock(&subscriberLock, lockFlags);
189
*-----------------------------------------------------------------------------
191
* VMCIEvent_CheckHostCapabilities --
193
* Verify that the host supports the hypercalls we need. If it does not,
194
* try to find fallback hypercalls and use those instead.
197
* TRUE if required hypercalls (or fallback hypercalls) are
198
* supported by the host, FALSE otherwise.
203
*-----------------------------------------------------------------------------
207
VMCIEvent_CheckHostCapabilities(void)
209
/* VMCIEvent does not require any hypercalls. */
215
*-----------------------------------------------------------------------------
219
* Gets a reference to the given VMCISubscription.
227
*-----------------------------------------------------------------------------
231
VMCIEventGet(VMCISubscription *entry) // IN
240
*-----------------------------------------------------------------------------
242
* VMCIEventRelease --
244
* Releases the given VMCISubscription.
250
* Fires the destroy event if the reference count has gone to zero.
252
*-----------------------------------------------------------------------------
256
VMCIEventRelease(VMCISubscription *entry) // IN
259
ASSERT(entry->refCount > 0);
262
if (entry->refCount == 0) {
263
VMCI_SignalEvent(&entry->destroyEvent);
269
*------------------------------------------------------------------------------
273
* Callback to release the event entry reference. It is called by the
274
* VMCI_WaitOnEvent function before it blocks.
282
*------------------------------------------------------------------------------
286
EventReleaseCB(void *clientData) // IN
289
VMCISubscription *sub = (VMCISubscription *)clientData;
293
VMCIEventGrabLock(&subscriberLock, &flags);
294
VMCIEventRelease(sub);
295
VMCIEventReleaseLock(&subscriberLock, flags);
302
*-----------------------------------------------------------------------------
306
* Find entry. Assumes lock is held.
309
* Entry if found, NULL if not.
312
* Increments the VMCISubscription refcount if an entry is found.
314
*-----------------------------------------------------------------------------
317
static VMCISubscription *
318
VMCIEventFind(VMCIId subID) // IN
323
for (e = 0; e < VMCI_EVENT_MAX; e++) {
324
VMCIList_Scan(iter, &subscriberArray[e]) {
325
VMCISubscription *cur =
326
VMCIList_Entry(iter, VMCISubscription, subscriberListItem);
327
if (cur->id == subID) {
338
*----------------------------------------------------------------------
340
* VMCIEventDelayedDispatchCB --
342
* Calls the specified callback in a delayed context.
350
*----------------------------------------------------------------------
354
VMCIEventDelayedDispatchCB(void *data) // IN
356
VMCIDelayedEventInfo *eventInfo;
357
VMCISubscription *sub;
361
eventInfo = (VMCIDelayedEventInfo *)data;
364
ASSERT(eventInfo->sub);
366
sub = eventInfo->sub;
367
ed = (VMCI_EventData *)eventInfo->eventPayload;
369
sub->callback(sub->id, ed, sub->callbackData);
371
VMCIEventGrabLock(&subscriberLock, &flags);
372
VMCIEventRelease(sub);
373
VMCIEventReleaseLock(&subscriberLock, flags);
375
VMCI_FreeKernelMem(eventInfo, sizeof *eventInfo);
380
*----------------------------------------------------------------------------
382
* VMCIEventDeliver --
384
* Actually delivers the events to the subscribers.
390
* The callback function for each subscriber is invoked.
392
*----------------------------------------------------------------------------
396
VMCIEventDeliver(VMCIEventMsg *eventMsg) // IN
398
int err = VMCI_SUCCESS;
404
VMCIEventGrabLock(&subscriberLock, &flags);
405
VMCIList_Scan(iter, &subscriberArray[eventMsg->eventData.event]) {
407
VMCISubscription *cur = VMCIList_Entry(iter, VMCISubscription,
409
ASSERT(cur && cur->event == eventMsg->eventData.event);
411
if (cur->runDelayed) {
412
VMCIDelayedEventInfo *eventInfo;
413
if ((eventInfo = VMCI_AllocKernelMem(sizeof *eventInfo,
414
(VMCI_MEMORY_ATOMIC |
415
VMCI_MEMORY_NONPAGED))) == NULL) {
416
err = VMCI_ERROR_NO_MEM;
422
memset(eventInfo, 0, sizeof *eventInfo);
423
memcpy(eventInfo->eventPayload, VMCI_DG_PAYLOAD(eventMsg),
424
(size_t)eventMsg->hdr.payloadSize);
425
eventInfo->sub = cur;
426
err = VMCI_ScheduleDelayedWork(VMCIEventDelayedDispatchCB,
428
if (err != VMCI_SUCCESS) {
429
VMCIEventRelease(cur);
430
VMCI_FreeKernelMem(eventInfo, sizeof *eventInfo);
435
uint8 eventPayload[sizeof(VMCIEventData_Max)];
437
/* We set event data before each callback to ensure isolation. */
438
memset(eventPayload, 0, sizeof eventPayload);
439
memcpy(eventPayload, VMCI_DG_PAYLOAD(eventMsg),
440
(size_t)eventMsg->hdr.payloadSize);
441
ed = (VMCI_EventData *)eventPayload;
442
cur->callback(cur->id, ed, cur->callbackData);
447
VMCIEventReleaseLock(&subscriberLock, flags);
454
*----------------------------------------------------------------------
456
* VMCIEvent_Dispatch --
458
* Dispatcher for the VMCI_EVENT_RECEIVE datagrams. Calls all
459
* subscribers for given event.
462
* VMCI_SUCCESS on success, error code otherwise.
467
*----------------------------------------------------------------------
471
VMCIEvent_Dispatch(VMCIDatagram *msg) // IN
473
VMCIEventMsg *eventMsg = (VMCIEventMsg *)msg;
476
msg->src.context == VMCI_HYPERVISOR_CONTEXT_ID &&
477
msg->dst.resource == VMCI_EVENT_HANDLER);
479
if (msg->payloadSize < sizeof(VMCI_Event) ||
480
msg->payloadSize > sizeof(VMCIEventData_Max)) {
481
return VMCI_ERROR_INVALID_ARGS;
484
if (!VMCI_EVENT_VALID(eventMsg->eventData.event)) {
485
return VMCI_ERROR_EVENT_UNKNOWN;
488
VMCIEventDeliver(eventMsg);
495
*----------------------------------------------------------------------
497
* VMCIEventRegisterSubscription --
499
* Initialize and add subscription to subscriber list.
502
* VMCI_SUCCESS on success, error code otherwise.
507
*----------------------------------------------------------------------
511
VMCIEventRegisterSubscription(VMCISubscription *sub, // IN
512
VMCI_Event event, // IN
514
VMCI_EventCB callback, // IN
515
void *callbackData) // IN
517
# define VMCI_EVENT_MAX_ATTEMPTS 10
518
static VMCIId subscriptionID = 0;
519
VMCILockFlags lockFlags;
526
if (!VMCI_EVENT_VALID(event) || callback == NULL) {
527
VMCI_DEBUG_LOG(4, (LGPFX"Failed to subscribe to event (type=%d) "
528
"(callback=%p) (data=%p).\n",
529
event, callback, callbackData));
530
return VMCI_ERROR_INVALID_ARGS;
535
* In the vmkernel we defer delivery of events to a helper world. This
536
* makes the event delivery more consistent across hosts and guests with
537
* regard to which locks are held.
539
sub->runDelayed = TRUE;
540
} else if (!VMCI_CanScheduleDelayedWork()) {
542
* If the platform doesn't support delayed work callbacks then don't
543
* allow registration for them.
545
if (flags & VMCI_FLAG_EVENT_DELAYED_CB) {
546
return VMCI_ERROR_INVALID_ARGS;
548
sub->runDelayed = FALSE;
551
* The platform supports delayed work callbacks. Honor the requested
554
sub->runDelayed = (flags & VMCI_FLAG_EVENT_DELAYED_CB) ? TRUE : FALSE;
559
sub->callback = callback;
560
sub->callbackData = callbackData;
561
VMCIList_InitEntry(&sub->subscriberListItem);
563
VMCIEventGrabLock(&subscriberLock, &lockFlags);
565
/* Check if creation of a new event is allowed. */
566
if (!VMCI_CanCreate()) {
567
result = VMCI_ERROR_UNAVAILABLE;
571
for (success = FALSE, attempts = 0;
572
success == FALSE && attempts < VMCI_EVENT_MAX_ATTEMPTS;
574
VMCISubscription *existingSub = NULL;
577
* We try to get an id a couple of time before claiming we are out of
580
sub->id = ++subscriptionID;
582
/* Test for duplicate id. */
583
existingSub = VMCIEventFind(sub->id);
584
if (existingSub == NULL) {
585
/* We succeeded if we didn't find a duplicate. */
588
VMCIEventRelease(existingSub);
593
VMCI_CreateEvent(&sub->destroyEvent);
594
VMCIList_Insert(&sub->subscriberListItem, &subscriberArray[event]);
595
result = VMCI_SUCCESS;
597
result = VMCI_ERROR_NO_RESOURCES;
601
VMCIEventReleaseLock(&subscriberLock, lockFlags);
603
# undef VMCI_EVENT_MAX_ATTEMPTS
609
*----------------------------------------------------------------------
611
* VMCIEventUnregisterSubscription --
613
* Remove subscription from subscriber list.
616
* VMCISubscription when found, NULL otherwise.
621
*----------------------------------------------------------------------
624
static VMCISubscription *
625
VMCIEventUnregisterSubscription(VMCIId subID) // IN
630
VMCIEventGrabLock(&subscriberLock, &flags);
631
s = VMCIEventFind(subID);
634
VMCIList_Remove(&s->subscriberListItem, &subscriberArray[s->event]);
636
VMCIEventReleaseLock(&subscriberLock, flags);
639
VMCI_WaitOnEvent(&s->destroyEvent, EventReleaseCB, s);
640
VMCI_DestroyEvent(&s->destroyEvent);
648
*----------------------------------------------------------------------
650
* VMCIEvent_Subscribe --
652
* Subscribe to given event. The callback specified can be fired
653
* in different contexts depending on what flag is specified while
654
* registering. If flags contains VMCI_FLAG_EVENT_NONE then the
655
* callback is fired with the subscriber lock held (and BH context
656
* on the guest). If flags contain VMCI_FLAG_EVENT_DELAYED_CB then
657
* the callback is fired with no locks held in thread context.
658
* This is useful because other VMCIEvent functions can be called,
659
* but it also increases the chances that an event will be dropped.
662
* VMCI_SUCCESS on success, error code otherwise.
667
*----------------------------------------------------------------------
670
VMCI_EXPORT_SYMBOL(VMCIEvent_Subscribe)
672
VMCIEvent_Subscribe(VMCI_Event event, // IN
674
VMCI_EventCB callback, // IN
675
void *callbackData, // IN
676
VMCIId *subscriptionID) // OUT
679
VMCISubscription *s = NULL;
681
if (subscriptionID == NULL) {
682
VMCI_DEBUG_LOG(4, (LGPFX"Invalid subscription (NULL).\n"));
683
return VMCI_ERROR_INVALID_ARGS;
686
s = VMCI_AllocKernelMem(sizeof *s, VMCI_MEMORY_NONPAGED);
688
return VMCI_ERROR_NO_MEM;
691
retval = VMCIEventRegisterSubscription(s, event, flags,
692
callback, callbackData);
693
if (retval < VMCI_SUCCESS) {
694
VMCI_FreeKernelMem(s, sizeof *s);
698
*subscriptionID = s->id;
704
*----------------------------------------------------------------------
706
* VMCIEvent_Unsubscribe --
708
* Unsubscribe to given event. Removes it from list and frees it.
709
* Will return callbackData if requested by caller.
712
* VMCI_SUCCESS on success, error code otherwise.
717
*----------------------------------------------------------------------
720
VMCI_EXPORT_SYMBOL(VMCIEvent_Unsubscribe)
722
VMCIEvent_Unsubscribe(VMCIId subID) // IN
727
* Return subscription. At this point we know noone else is accessing
728
* the subscription so we can free it.
730
s = VMCIEventUnregisterSubscription(subID);
732
return VMCI_ERROR_NOT_FOUND;
735
VMCI_FreeKernelMem(s, sizeof *s);