mpnotification-r0drv.c revision 8c99dcd207cf5b7bee01f95fbe19728a94076f94
/* $Id$ */
/** @file
* IPRT - Multiprocessor, Ring-0 Driver, Event Notifications.
*/
/*
* Copyright (C) 2008 Sun Microsystems, Inc.
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* The contents of this file may alternatively be used under the terms
* of the Common Development and Distribution License Version 1.0
* (CDDL) only, as it comes in the "COPYING.CDDL" file of the
* VirtualBox OSE distribution, in which case the provisions of the
* CDDL are applicable instead of those of the GPL.
*
* You may elect to license modified versions of this file under the
* terms and conditions of either the GPL or the CDDL or both.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 USA or visit http://www.sun.com if you need
* additional information or have any questions.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#include <iprt/spinlock.h>
#include "r0drv/mp-r0drv.h"
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/**
* Notification registration record tracking
* RTMpRegisterNotification() calls.
*/
typedef struct RTMPNOTIFYREG
{
/** Pointer to the next record. */
struct RTMPNOTIFYREG * volatile pNext;
/** The callback. */
/** The user argument. */
void *pvUser;
/** Bit mask indicating whether we've done this callback or not. */
/** Pointer to a registration record. */
typedef RTMPNOTIFYREG *PRTMPNOTIFYREG;
/*******************************************************************************
* Global Variables *
*******************************************************************************/
/** The spinlock protecting the list. */
/** List of callbacks, in registration order. */
/** The current done bit. */
static uint32_t volatile g_iRTMpDoneBit;
/** The list generation.
* This is increased whenever the list has been modified. The callback routine
* make use of this to avoid having restart at the list head after each callback. */
static uint32_t volatile g_iRTMpGeneration;
/** The number of RTMpNotification users.
* This is incremented on init and decremented on termination. */
static uint32_t volatile g_cRTMpUsers = 0;
/**
* This is called by the native code.
*
* @param idCpu The CPU id the event applies to.
* @param enmEvent The event.
*/
{
/*
* This is a little bit tricky as we cannot be holding the spinlock
* while calling the callback. This means that the list might change
* while we're walking it, and that multiple events might be running
* concurrently (depending on the OS).
*
* So, the first measure is to employ a 32-bitmask for each
* record where we'll use a bit that rotates for each call to
* this function to indicate which records that has been
* processed. This will take care of both changes to the list
* and a reasonable amount of concurrent events.
*
* In order to avoid having to restart the list walks for every
* callback we make, we'll make use a list generation number that is
* incremented everytime the list is changed. So, if it remains
* unchanged over a callback we can safely continue the iteration.
*/
if (hSpinlock == NIL_RTSPINLOCK)
return;
/* Clear the bit. */
/* Iterate the records and perform the callbacks. */
do
{
while (pCur)
{
{
/* carefully require the lock here, see RTR0MpNotificationTerm(). */
if (hSpinlock == NIL_RTSPINLOCK)
return;
break;
}
else
}
} while (pCur);
}
{
/*
* Validation.
*/
break;
AssertMsgReturn(!pCur, ("pCur=%p pfnCallback=%p pvUser=%p\n", pCur, pfnCallback, pvUser), VERR_ALREADY_EXISTS);
/*
* Allocate a new record and attempt to insert it.
*/
if (!pNew)
return VERR_NO_MEMORY;
if (!pCur)
else
{
break;
{
break;
}
}
/* duplicate? */
if (pCur)
{
AssertMsgFailedReturn(("pCur=%p pfnCallback=%p pvUser=%p\n", pCur, pfnCallback, pvUser), VERR_ALREADY_EXISTS);
}
return VINF_SUCCESS;
}
{
/*
* Validation.
*/
/*
* Find and unlink the record from the list.
*/
{
break;
}
if (pCur)
{
if (pPrev)
else
}
if (!pCur)
return VERR_NOT_FOUND;
/*
* Invalidate and free the record.
*/
return VINF_SUCCESS;
}
int rtR0MpNotificationInit(void)
{
int rc = VINF_SUCCESS;
{
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
return rc;
}
}
return rc;
}
void rtR0MpNotificationTerm(void)
{
if (hSpinlock != NIL_RTSPINLOCK)
{
if (ASMAtomicDecS32(&g_cRTMpUsers) == 0)
{
/* pick up the list and the spinlock. */
/* free the list. */
while (pHead)
{
}
}
}
}