timer-r0drv-solaris.c revision ee0e2a2680771d8020caad2948d1d1e78b26f9e8
/** $Id$ */
/** @file
* IPRT - Timer, Ring-0 Driver, Solaris.
*/
/*
* Copyright (C) 2006-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 "the-solaris-kernel.h"
#include <iprt/spinlock.h>
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/**
* This is used to track sub-timer data.
*/
typedef struct RTTIMERSOLSUBTIMER
{
/** The current timer tick. */
/** Pointer to the parent timer. */
/** Pointer to a Solaris sub-timer. */
typedef RTTIMERSOLSUBTIMER *PRTTIMERSOLSUBTIMER;
/**
* The internal representation of a Solaris timer handle.
*/
typedef struct RTTIMER
{
/** Magic.
* This is RTTIMER_MAGIC, but changes to something else before the timer
* is destroyed to indicate clearly that thread should exit. */
/** The cyclic timer id.
* This is CYCLIC_NONE if the timer hasn't been started. */
cyclic_id_t volatile CyclicId;
/** Flag used by rtTimerSolarisOmniOnlineCallback to see whether we're inside the cyclic_add_omni call or not. */
bool volatile fStarting;
/** Whether the timer must run on a specific CPU or not. */
bool fSpecificCpu;
/** Set if we're using an omni cyclic. */
bool fOmni;
/** The CPU it must run on if fSpecificCpu is set. */
/** Callback. */
/** User argument. */
void *pvUser;
/** The timer interval. 0 for one-shot timer */
/** The timer spec (for omni timers mostly). */
/** The number of sub-timers. */
/** Sub-timer data.
* When fOmni is set, this will be an array indexed by CPU id.
* When fOmni is clear, the array will only have one member. */
} RTTIMER;
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
static void rtTimerSolarisCallback(void *pvTimer);
static void rtTimerSolarisOmniCallback(void *pvSubTimer);
static void rtTimerSolarisOmniDummyCallback(void *pvIgnored);
static void rtTimerSolarisOmniOnlineCallback(void *pvTimer, cpu_t *pCpu, cyc_handler_t *pCyclicInfo, cyc_time_t *pTimeSpecs);
AssertCompileSize(cyclic_id_t, sizeof(void *));
/** Atomic read of RTTIMER::CyclicId. */
{
}
/** Atomic write of RTTIMER::CyclicId. */
{
}
/** Atomic compare and exchange of RTTIMER::CyclicId. */
DECLINLINE(bool) rtTimerSolarisCmpXchgCyclicId(PRTTIMER pTimer, cyclic_id_t CyclicIdNew, cyclic_id_t CyclicIdOld)
{
return ASMAtomicCmpXchgPtr((void * volatile *)&pTimer->CyclicId, (void *)CyclicIdNew, (void *)CyclicIdOld);
}
RTDECL(int) RTTimerCreateEx(PRTTIMER *ppTimer, uint64_t u64NanoInterval, unsigned fFlags, PFNRTTIMER pfnTimer, void *pvUser)
{
RTCPUID i;
/*
* Validate flags.
*/
if (!RTTIMER_FLAGS_ARE_VALID(fFlags))
return VERR_INVALID_PARAMETER;
if ( (fFlags & RTTIMER_FLAGS_CPU_SPECIFIC)
return VERR_CPU_NOT_FOUND;
/*
* Allocate and initialize the timer handle.
*/
: 1;
if (!pTimer)
return VERR_NO_MEMORY;
if ( (fFlags & RTTIMER_FLAGS_CPU_SPECIFIC)
{
pTimer->fSpecificCpu = true;
}
else
{
pTimer->fSpecificCpu = false;
}
for (i = 0; i < cCpus; i++)
{
}
return VINF_SUCCESS;
}
{
/*
* Validate.
*/
return VINF_SUCCESS;
/*
* Invalid the timer, stop it, and free the associated resources.
*/
return VINF_SUCCESS;
}
{
RTCPUID i;
int rc = VINF_SUCCESS;
/*
* Validate.
*/
{
/*
* If it's a one-shot we might end up here because it didn't stop after
* the first firing. There are two reasons for this depending on the
* kind type of timer. (1) Non-omni timers are (potentially) racing our
* RTTimerStart in setting RTTIMER::CyclicId. (2) Omni timers are stopped
* on the 2nd firing because we have to make sure all cpus gets called, and
* we're using the 2nd round that comes 1 sec after the first because this
* is the easier way out.
*/
if (pTimer->u64NanoInterval)
return VERR_TIMER_ACTIVE;
for (i = 0; i < pTimer->cSubTimers; i++)
break; /* has fired */
if (i >= pTimer->cSubTimers)
return VERR_TIMER_ACTIVE;
}
/*
* Do the setup bits that doesn't need the lock.
* We'll setup both omni and non-omni stuff here because it shorter than if'ing it.
*/
for (i = 0; i > pTimer->cSubTimers; i++)
else
? 1000000000 /* 1 sec */
/*
* Acquire the cpu lock and call cyclic_add/cyclic_add_omni.
*/
else if (pTimer->fSpecificCpu)
{
if (pCpu)
{
if (cpu_is_online(pCpu))
{
if (CyclicId != CYCLIC_NONE)
}
else
}
else
}
else
/*
* Just some sanity checks should the cylic code start returning errors.
*/
return rc;
}
{
/*
* Validate.
*/
/*
* Stop the timer.
*/
if (!rtTimerSolarisStop(pTimer))
return VERR_TIMER_SUSPENDED;
return VINF_SUCCESS;
}
/**
* Timer callback function for non-omni timers.
*
* @param pvTimer Pointer to the timer.
*/
static void rtTimerSolarisCallback(void *pvTimer)
{
/* Check for destruction. */
return;
/*
* If this is a one shot timer, suspend the timer here as Solaris
* does not support single-shot timers implicitly.
*/
if (!pTimer->u64NanoInterval)
{
{
}
}
else
/* recurring */
}
/**
* Timer callback function for omni timers.
*
* @param pvTimer Pointer to the sub-timer.
*/
static void rtTimerSolarisOmniCallback(void *pvSubTimer)
{
/* Check for destruction. */
return;
/*
* If this is a one-shot timer, suspend it here the 2nd time around.
* We cannot do it the first time like for the non-omni timers since
* we don't know if it has fired on all the cpus yet.
*/
if (!pTimer->u64NanoInterval)
{
{
}
else
}
else
/* recurring */
}
/**
* This is a dummy callback use for the cases where we get cpus which id we
* cannot handle because of broken RTMpGetMaxCpuId(), or if we're racing
* RTTimerDestroy().
*
* This shouldn't happen of course, but if it does we wish to handle
* gracefully instead of crashing.
*
* @param pvIgnored Ignored
*/
static void rtTimerSolarisOmniDummyCallback(void *pvIgnored)
{
}
/**
* Omni-timer callback that sets up the timer for a cpu during cyclic_add_omni
* or at later when a CPU comes online.
*
*
* @param pvTimer Pointer to the timer.
* @param pCpu The cpu that has come online.
* @param pCyclicInfo Where to store the cyclic handler info.
* @param pTimeSpecs Where to store the timer firing specs.
*/
static void rtTimerSolarisOmniOnlineCallback(void *pvTimer, cpu_t *pCpu, cyc_handler_t *pCyclicInfo, cyc_time_t *pTimeSpecs)
{
{
{
/*
* Called during cyclic_add_omni, just spread the 2nd tick
* for the one-shots to avoid unnecessary lock contention.
*/
if (!pTimer->u64NanoInterval)
}
else
{
/*
* Called at run-time, have to make sure cyt_when isn't in the past.
*/
else
{
if (!pTimer->u64NanoInterval)
{
/* one-shot: Just schedule a 1 sec timeout and set the tick to 1. */
}
else
{
#if 1 /* This might be made into a RTTIMER_FLAGS_something later, for now ASAP is what we need. */
/* recurring: ASAP. */
#else
/* recurring: Adjust it to the next tick. */
#endif
}
}
}
}
else
{
/*
* Invalid cpu id or destruction race.
*/
}
}
/**
* Callback for when a CPU goes offline.
*
* Currently, we don't need to perform any tasks here.
*
* @param pvTimer Pointer to the timer.
* @param pCpu Pointer to the cpu.
* @param pvSubTimer Pointer to the sub timer. This may be NULL.
*/
{
/*PRTTIMER pTimer = (PRTTIMER)pvTimer;*/
}
/**
* Worker function used to stop the timer.
*
* This is used from within the callback functions (one-shot scenarious) and
* from RTTimerStop, RTTimerDestroy and RTTimerStart. We use atomic cmpxchg
* here to avoid some unnecessary cpu_lock contention and to avoid
* potential (?) deadlocks between the callback and the APIs. There is a
* slight chance of a race between active callbacks and the APIs, but this
* is preferred to a
*
* @returns true if we stopped it, false if it was already stopped.
* @param pTimer The timer to stop.
*/
{
/*
* This is a bit problematic. I'm a bit unsure whether cyclic_remove might
* or may not deadlock with a callback trying to aquire the cpu_lock. So,
* in order to avoid this issue I'm making sure that we don't take the lock
* unless we know we're gonna call cyclic_remove. However, the downside of
* this is that it's possible races between RTTimerStart/RTTimerDestroy and
* currently active callbacks, which may cause iTick to have a bad value or
* in the worst case, memory to accessed after cleanup.
*/
if ( CyclicId != CYCLIC_NONE
{
return true;
}
return false;
}
{
return nsec_per_tick;
}
{
return VERR_NOT_SUPPORTED;
}
{
return VERR_NOT_SUPPORTED;
}