timer-r0drv-linux.c revision ad27e1d5e48ca41245120c331cc88b50464813ce
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * IPRT - Timers, Ring-0 Driver, Linux.
b72d3233df38e3122eda39b39a27b35c27209615vboxsync * Copyright (C) 2006-2010 Oracle Corporation
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * available from http://www.virtualbox.org. This file is free software;
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * you can redistribute it and/or modify it under the terms of the GNU
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * General Public License (GPL) as published by the Free Software
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * The contents of this file may alternatively be used under the terms
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * of the Common Development and Distribution License Version 1.0
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * VirtualBox OSE distribution, in which case the provisions of the
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * CDDL are applicable instead of those of the GPL.
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * You may elect to license modified versions of this file under the
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * terms and conditions of either the GPL or the CDDL or both.
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync/*******************************************************************************
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync* Header Files *
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync*******************************************************************************/
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync/** @def RTTIMER_LINUX_WITH_HRTIMER
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync * Whether to use high resolution timers. */
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync/*******************************************************************************
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync* Structures and Typedefs *
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync*******************************************************************************/
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync * Timer state machine.
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync * This is used to try handle the issues with MP events and
0612e2adbcc146b9eb7748983c720e35e38d0dc9vboxsync * timers that runs on all CPUs. It's relatively nasty :-/
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync /** Stopped. */
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync /** Transient state; next ACTIVE. */
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync /** Transient state; next ACTIVE. (not really necessary) */
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync /** Active. */
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync /** Active and in callback; next ACTIVE, STOPPED or CALLBACK_DESTROYING. */
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync /** Stopped while in the callback; next STOPPED. */
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync /** Restarted while in the callback; next ACTIVE, STOPPED, DESTROYING. */
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync /** The callback shall destroy the timer; next STOPPED. */
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync /** Transient state; next STOPPED. */
39c2eccedfdb7455c52225543c355e33a65f0c81vboxsync /** Transient state; next STOPPED. */
39c2eccedfdb7455c52225543c355e33a65f0c81vboxsync /** The usual 32-bit hack. */
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * A Linux sub-timer.
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync /** Timer specific data. */
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync /** High resolution timer. */
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync /** The linux timer structure. */
0368e9c310393e82ef37c480b6acbd0f107cf0edvboxsync /** Standard timer. */
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync /** The linux timer structure. */
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync /** The start of the current run (ns).
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * This is used to calculate when the timer ought to fire the next time. */
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync /** The u64NextTS in jiffies. */
d1c36fd86d36726777e3d6f9d040573e0aaf30devboxsync unsigned long ulNextJiffies;
d1c36fd86d36726777e3d6f9d040573e0aaf30devboxsync /** Set when starting or changing the timer so that u64StartTs
d1c36fd86d36726777e3d6f9d040573e0aaf30devboxsync * and u64NextTS gets reinitialized (eliminating some jitter). */
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync bool volatile fFirstAfterChg;
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync /** The current tick number. */
d1c36fd86d36726777e3d6f9d040573e0aaf30devboxsync /** Restart the single shot timer at this specific time.
d1c36fd86d36726777e3d6f9d040573e0aaf30devboxsync * Used when a single shot timer is restarted from the callback. */
d1c36fd86d36726777e3d6f9d040573e0aaf30devboxsync /** Pointer to the parent timer. */
d1c36fd86d36726777e3d6f9d040573e0aaf30devboxsync /** The current sub-timer state. */
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync/** Pointer to a linux sub-timer. */
d1c36fd86d36726777e3d6f9d040573e0aaf30devboxsync * The internal representation of an Linux timer handle.
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsynctypedef struct RTTIMER
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * This is RTTIMER_MAGIC, but changes to something else before the timer
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * is destroyed to indicate clearly that thread should exit. */
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync /** Spinlock synchronizing the fSuspended and MP event handling.
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * This is NIL_RTSPINLOCK if cCpus == 1. */
d1c36fd86d36726777e3d6f9d040573e0aaf30devboxsync /** Flag indicating that the timer is suspended. */
d1c36fd86d36726777e3d6f9d040573e0aaf30devboxsync bool volatile fSuspended;
d1c36fd86d36726777e3d6f9d040573e0aaf30devboxsync /** Whether the timer must run on one specific CPU or not. */
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync /** Whether the timer must run on all CPUs or not. */
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync#endif /* else: All -> specific on non-SMP kernels */
d1c36fd86d36726777e3d6f9d040573e0aaf30devboxsync /** Whether it is a high resolution timer or a standard one. */
d1c36fd86d36726777e3d6f9d040573e0aaf30devboxsync /** The id of the CPU it must run on if fSpecificCpu is set. */
d1c36fd86d36726777e3d6f9d040573e0aaf30devboxsync /** The number of CPUs this timer should run on. */
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync /** Callback. */
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync /** User argument. */
d1c36fd86d36726777e3d6f9d040573e0aaf30devboxsync /** The timer interval. 0 if one-shot. */
d1c36fd86d36726777e3d6f9d040573e0aaf30devboxsync /** This is set to the number of jiffies between ticks if the interval is
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * an exact number of jiffies. (Standard timers only.) */
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync unsigned long volatile cJiffies;
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync /** The change interval spinlock for standard timers only. */
d1c36fd86d36726777e3d6f9d040573e0aaf30devboxsync /** Sub-timers.
d1c36fd86d36726777e3d6f9d040573e0aaf30devboxsync * Normally there is just one, but for RTTIMER_FLAGS_CPU_ALL this will contain
d1c36fd86d36726777e3d6f9d040573e0aaf30devboxsync * an entry for all possible cpus. In that case the index will be the same as
d1c36fd86d36726777e3d6f9d040573e0aaf30devboxsync * for the RTCpuSet. */
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync * A rtTimerLinuxStartOnCpu and rtTimerLinuxStartOnCpu argument package.
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync /** The current time (RTTimeSystemNanoTS). */
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync /** When to start firing (delta). */
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync/** Pointer to a rtTimerLinuxStartOnCpu argument package. */
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsynctypedef RTTIMERLINUXSTARTONCPUARGS *PRTTIMERLINUXSTARTONCPUARGS;
0612e2adbcc146b9eb7748983c720e35e38d0dc9vboxsync/*******************************************************************************
0612e2adbcc146b9eb7748983c720e35e38d0dc9vboxsync* Internal Functions *
0612e2adbcc146b9eb7748983c720e35e38d0dc9vboxsync*******************************************************************************/
0612e2adbcc146b9eb7748983c720e35e38d0dc9vboxsyncstatic DECLCALLBACK(void) rtTimerLinuxMpEvent(RTMPEVENT enmEvent, RTCPUID idCpu, void *pvUser);
0612e2adbcc146b9eb7748983c720e35e38d0dc9vboxsyncstatic void myLogBackdoorPrintf(const char *pszFormat, ...)
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync cb = RTStrPrintf(szTmp, sizeof(szTmp) - 10, "%d: ", RTMpCpuId());
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync cb += RTStrPrintfV(&szTmp[cb], sizeof(szTmp) - cb, pszFormat, args);
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync# define RTAssertMsg1Weak(pszExpr, uLine, pszFile, pszFunction) \
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync myLogBackdoorPrintf("\n!!Guest Assertion failed!!\n%s(%d) %s\n%s\n", uLine, pszFile, pszFunction, (pszExpr))
0612e2adbcc146b9eb7748983c720e35e38d0dc9vboxsync# define RTTIMERLNX_LOG(a) do { } while (0)
0612e2adbcc146b9eb7748983c720e35e38d0dc9vboxsync * Sets the state.
0612e2adbcc146b9eb7748983c720e35e38d0dc9vboxsyncDECLINLINE(void) rtTimerLnxSetState(RTTIMERLNXSTATE volatile *penmState, RTTIMERLNXSTATE enmNewState)
39c2eccedfdb7455c52225543c355e33a65f0c81vboxsync RTTIMERLNX_LOG(("set %d -> %d\n", *penmState, enmNewState));
b72d3233df38e3122eda39b39a27b35c27209615vboxsync ASMAtomicWriteU32((uint32_t volatile *)penmState, enmNewState);
39c2eccedfdb7455c52225543c355e33a65f0c81vboxsync * Sets the state if it has a certain value.
39c2eccedfdb7455c52225543c355e33a65f0c81vboxsync * @return true if xchg was done.
39c2eccedfdb7455c52225543c355e33a65f0c81vboxsync * @return false if xchg wasn't done.
d8dee9a7ef33d4c705d5fd087d5af9c7cb071f85vboxsync#define rtTimerLnxCmpXchgState(penmState, enmNewState, enmCurState) rtTimerLnxCmpXchgStateDebug(penmState, enmNewState, enmCurState, __LINE__)
39c2eccedfdb7455c52225543c355e33a65f0c81vboxsyncstatic bool rtTimerLnxCmpXchgStateDebug(RTTIMERLNXSTATE volatile *penmState, RTTIMERLNXSTATE enmNewState,
39c2eccedfdb7455c52225543c355e33a65f0c81vboxsync bool fRc = ASMAtomicCmpXchgExU32((uint32_t volatile *)penmState, enmNewState, enmCurState, (uint32_t *)&enmOldState);
39c2eccedfdb7455c52225543c355e33a65f0c81vboxsync RTTIMERLNX_LOG(("cxg %d -> %d - %d at %u\n", enmOldState, enmNewState, fRc, uLine));
39c2eccedfdb7455c52225543c355e33a65f0c81vboxsyncDECLINLINE(bool) rtTimerLnxCmpXchgState(RTTIMERLNXSTATE volatile *penmState, RTTIMERLNXSTATE enmNewState,
d8dee9a7ef33d4c705d5fd087d5af9c7cb071f85vboxsync return ASMAtomicCmpXchgU32((uint32_t volatile *)penmState, enmNewState, enmCurState);
d8dee9a7ef33d4c705d5fd087d5af9c7cb071f85vboxsync * Gets the state.
d8dee9a7ef33d4c705d5fd087d5af9c7cb071f85vboxsyncDECLINLINE(RTTIMERLNXSTATE) rtTimerLnxGetState(RTTIMERLNXSTATE volatile *penmState)
d8dee9a7ef33d4c705d5fd087d5af9c7cb071f85vboxsync return (RTTIMERLNXSTATE)ASMAtomicUoReadU32((uint32_t volatile *)penmState);
d8dee9a7ef33d4c705d5fd087d5af9c7cb071f85vboxsync * Converts a nano second time stamp to ktime_t.
d8dee9a7ef33d4c705d5fd087d5af9c7cb071f85vboxsync * ASSUMES RTTimeSystemNanoTS() is implemented using ktime_get_ts().
d8dee9a7ef33d4c705d5fd087d5af9c7cb071f85vboxsync * @returns ktime_t.
d8dee9a7ef33d4c705d5fd087d5af9c7cb071f85vboxsync * @param cNanoSecs Nanoseconds.
d8dee9a7ef33d4c705d5fd087d5af9c7cb071f85vboxsyncDECLINLINE(ktime_t) rtTimerLnxNanoToKt(uint64_t cNanoSecs)
d8dee9a7ef33d4c705d5fd087d5af9c7cb071f85vboxsync /* With some luck the compiler optimizes the division out of this... (Bet it doesn't.) */
0612e2adbcc146b9eb7748983c720e35e38d0dc9vboxsync return ktime_set(cNanoSecs / 1000000000, cNanoSecs % 1000000000);
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * Converts ktime_t to a nano second time stamp.
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * ASSUMES RTTimeSystemNanoTS() is implemented using ktime_get_ts().
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * @returns nano second time stamp.
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * @param Kt ktime_t.
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync#endif /* RTTIMER_LINUX_WITH_HRTIMER */
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * Converts a nano second interval to jiffies.
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * @returns Jiffies.
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * @param cNanoSecs Nanoseconds.
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsyncDECLINLINE(unsigned long) rtTimerLnxNanoToJiffies(uint64_t cNanoSecs)
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync /* this can be made even better... */
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync if (cNanoSecs > (uint64_t)TICK_NSEC * MAX_JIFFY_OFFSET)
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync return ((uint32_t)cNanoSecs + (TICK_NSEC-1)) / TICK_NSEC;
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * Starts a sub-timer (RTTimerStart).
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * @param pSubTimer The sub-timer to start.
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * @param u64Now The current timestamp (RTTimeSystemNanoTS()).
683eff3070b1b86fe71b71af7fda82766ea19d17vboxsync * @param u64First The interval from u64Now to the first time the timer should fire.
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * @param fPinned true = timer pinned to a specific CPU,
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * false = timer can migrate between CPUs
683eff3070b1b86fe71b71af7fda82766ea19d17vboxsync * @param fHighRes Whether the user requested a high resolution timer or not.
683eff3070b1b86fe71b71af7fda82766ea19d17vboxsync * @param enmOldState The old timer state.
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsyncstatic void rtTimerLnxStartSubTimer(PRTTIMERLNXSUBTIMER pSubTimer, uint64_t u64Now, uint64_t u64First,
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * Calc when it should start firing.
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync RTTIMERLNX_LOG(("startsubtimer %p\n", pSubTimer->pParent));
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync hrtimer_start(&pSubTimer->u.Hr.LnxTimer, rtTimerLnxNanoToKt(u64NextTS),
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync fPinned ? HRTIMER_MODE_ABS_PINNED : HRTIMER_MODE_ABS);
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync unsigned long cJiffies = !u64First ? 0 : rtTimerLnxNanoToJiffies(u64First);
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync pSubTimer->u.Std.ulNextJiffies = jiffies + cJiffies;
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync mod_timer_pinned(&pSubTimer->u.Std.LnxTimer, pSubTimer->u.Std.ulNextJiffies);
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync mod_timer(&pSubTimer->u.Std.LnxTimer, pSubTimer->u.Std.ulNextJiffies);
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync /* Be a bit careful here since we could be racing the callback. */
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync if (!rtTimerLnxCmpXchgState(&pSubTimer->enmState, RTTIMERLNXSTATE_ACTIVE, RTTIMERLNXSTATE_STARTING))
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync rtTimerLnxCmpXchgState(&pSubTimer->enmState, RTTIMERLNXSTATE_ACTIVE, RTTIMERLNXSTATE_MP_STARTING);
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * Stops a sub-timer (RTTimerStart and rtTimerLinuxMpEvent()).
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * The caller has already changed the state, so we will not be in a callback
683eff3070b1b86fe71b71af7fda82766ea19d17vboxsync * situation wrt to the calling thread.
b72d3233df38e3122eda39b39a27b35c27209615vboxsync * @param pSubTimer The sub-timer.
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * @param fHighRes Whether the user requested a high resolution timer or not.
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsyncstatic void rtTimerLnxStopSubTimer(PRTTIMERLNXSUBTIMER pSubTimer, bool fHighRes)
b72d3233df38e3122eda39b39a27b35c27209615vboxsync RTTIMERLNX_LOG(("stopsubtimer %p %d\n", pSubTimer->pParent, fHighRes));
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync rtTimerLnxSetState(&pSubTimer->enmState, RTTIMERLNXSTATE_STOPPED);
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * Used by RTTimerDestroy and rtTimerLnxCallbackDestroy to do the actual work.
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * @param pTimer The timer in question.
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * Remove the MP notifications first because it'll reduce the risk of
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * us overtaking any MP event that might theoretically be racing us here.
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync int rc = RTMpNotificationDeregister(rtTimerLinuxMpEvent, pTimer);
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync#endif /* CONFIG_SMP */
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * Uninitialize the structure and free the associated resources.
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * The spinlock goes last.
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync ASMAtomicWriteU32(&pTimer->u32Magic, ~RTTIMER_MAGIC);
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync RTMemFreeEx(pTimer, RT_OFFSETOF(RTTIMER, aSubTimers[pTimer->cCpus]));
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * Called when the timer was destroyed by the callback function.
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * @param pTimer The timer.
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * @param pSubTimer The sub-timer which we're handling, the state of this
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * will be RTTIMERLNXSTATE_CALLBACK_DESTROYING.
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsyncstatic void rtTimerLnxCallbackDestroy(PRTTIMER pTimer, PRTTIMERLNXSUBTIMER pSubTimer)
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * If it's an omni timer, the last dude does the destroying.
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync Assert(pSubTimer->enmState == RTTIMERLNXSTATE_CB_DESTROYING);
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync rtTimerLnxSetState(&pSubTimer->enmState, RTTIMERLNXSTATE_STOPPED);
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync while (iCpu-- > 0)
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync if (rtTimerLnxGetState(&pTimer->aSubTimers[iCpu].enmState) != RTTIMERLNXSTATE_STOPPED)
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * Deal with a sub-timer that has migrated.
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * @param pTimer The timer.
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * @param pSubTimer The sub-timer.
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsyncstatic void rtTimerLnxCallbackHandleMigration(PRTTIMER pTimer, PRTTIMERLNXSUBTIMER pSubTimer)
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync enmState = rtTimerLnxGetState(&pSubTimer->enmState);
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync if (rtTimerLnxCmpXchgState(&pSubTimer->enmState, RTTIMERLNXSTATE_STOPPED, enmState))
b72d3233df38e3122eda39b39a27b35c27209615vboxsync#endif /* CONFIG_SMP */
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * The slow path of rtTimerLnxChangeToCallbackState.
b72d3233df38e3122eda39b39a27b35c27209615vboxsync * @returns true if changed successfully, false if not.
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * @param pSubTimer The sub-timer.
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsyncstatic bool rtTimerLnxChangeToCallbackStateSlow(PRTTIMERLNXSUBTIMER pSubTimer)
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync RTTIMERLNXSTATE enmState = rtTimerLnxGetState(&pSubTimer->enmState);
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync if (rtTimerLnxCmpXchgState(&pSubTimer->enmState, RTTIMERLNXSTATE_CALLBACK, enmState))
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync return true;
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync return false;
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * Tries to change the sub-timer state to 'callback'.
b72d3233df38e3122eda39b39a27b35c27209615vboxsync * @returns true if changed successfully, false if not.
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * @param pSubTimer The sub-timer.
b72d3233df38e3122eda39b39a27b35c27209615vboxsyncDECLINLINE(bool) rtTimerLnxChangeToCallbackState(PRTTIMERLNXSUBTIMER pSubTimer)
b72d3233df38e3122eda39b39a27b35c27209615vboxsync if (RT_LIKELY(rtTimerLnxCmpXchgState(&pSubTimer->enmState, RTTIMERLNXSTATE_CALLBACK, RTTIMERLNXSTATE_ACTIVE)))
b72d3233df38e3122eda39b39a27b35c27209615vboxsync return true;
b72d3233df38e3122eda39b39a27b35c27209615vboxsync return rtTimerLnxChangeToCallbackStateSlow(pSubTimer);
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * Timer callback function for high resolution timers.
return HRTIMER_NORESTART;
#ifdef CONFIG_SMP
return HRTIMER_NORESTART;
if (RT_LIKELY(rtTimerLnxCmpXchgState(&pSubTimer->enmState, RTTIMERLNXSTATE_ACTIVE, RTTIMERLNXSTATE_CALLBACK)))
return HRTIMER_RESTART;
if (RT_LIKELY(rtTimerLnxCmpXchgState(&pSubTimer->enmState, RTTIMERLNXSTATE_STOPPED, RTTIMERLNXSTATE_CALLBACK)))
return HRTIMER_NORESTART;
switch (enmState)
return HRTIMER_NORESTART;
if (rtTimerLnxCmpXchgState(&pSubTimer->enmState, RTTIMERLNXSTATE_STOPPED, RTTIMERLNXSTATE_CB_STOPPING))
return HRTIMER_NORESTART;
if (rtTimerLnxCmpXchgState(&pSubTimer->enmState, RTTIMERLNXSTATE_ACTIVE, RTTIMERLNXSTATE_CB_RESTARTING))
return HRTIMER_RESTART;
return HRTIMER_NORESTART;
ASMNopPause();
#ifdef CONFIG_SMP
unsigned long cJiffies;
unsigned long flFlags;
if (cJiffies)
pSubTimer->u.Std.ulNextJiffies = jiffies + rtTimerLnxNanoToJiffies(pSubTimer->u.Std.u64NextTS - u64NanoTS);
if (RT_LIKELY(rtTimerLnxCmpXchgState(&pSubTimer->enmState, RTTIMERLNXSTATE_ACTIVE, RTTIMERLNXSTATE_CALLBACK)))
#ifdef CONFIG_SMP
if (RT_LIKELY(rtTimerLnxCmpXchgState(&pSubTimer->enmState, RTTIMERLNXSTATE_STOPPED, RTTIMERLNXSTATE_CALLBACK)))
switch (enmState)
if (rtTimerLnxCmpXchgState(&pSubTimer->enmState, RTTIMERLNXSTATE_STOPPED, RTTIMERLNXSTATE_CB_STOPPING))
if (rtTimerLnxCmpXchgState(&pSubTimer->enmState, RTTIMERLNXSTATE_ACTIVE, RTTIMERLNXSTATE_CB_RESTARTING))
unsigned long flFlags;
: jiffies;
#ifdef CONFIG_SMP
ASMNopPause();
#ifdef CONFIG_SMP
* Per-cpu callback function (RTMpOnAll/RTMpOnSpecific).
rtTimerLnxStartSubTimer(&pTimer->aSubTimers[idCpu], pArgs->u64Now, pArgs->u64First, true /*fPinned*/, pTimer->fHighRes);
int rc2;
return VERR_TIMER_BUSY;
if (rtTimerLnxCmpXchgState(&pTimer->aSubTimers[iCpu].enmState, RTTIMERLNXSTATE_STOPPED, RTTIMERLNXSTATE_STARTING))
return VINF_SUCCESS;
bool fActiveCallbacks = false;
enmState))
fActiveCallbacks = true;
ASMNopPause();
return fActiveCallbacks;
if (rtTimerLnxCmpXchgState(&pSubTimer->enmState, RTTIMERLNXSTATE_MP_STARTING, RTTIMERLNXSTATE_STOPPED))
rtTimerLnxStartSubTimer(pSubTimer, pArgs->u64Now, pArgs->u64First, true /*fPinned*/, pTimer->fHighRes);
switch (enmEvent)
case RTMPEVENT_ONLINE:
if (rtTimerLnxCmpXchgState(&pSubTimer->enmState, RTTIMERLNXSTATE_MP_STARTING, RTTIMERLNXSTATE_STOPPED))
case RTMPEVENT_OFFLINE:
if (rtTimerLnxCmpXchgState(&pSubTimer->enmState, RTTIMERLNXSTATE_MP_STOPPING, RTTIMERLNXSTATE_ACTIVE))
ASMNopPause();
rtTimerLnxStartSubTimer(&pTimer->aSubTimers[0], pArgs->u64Now, pArgs->u64First, true /*fPinned*/, pTimer->fHighRes);
int rc2;
return VERR_TIMER_ACTIVE;
#ifdef CONFIG_SMP
switch (enmState)
case RTTIMERLNXSTATE_STOPPED:
if (rtTimerLnxCmpXchgState(&pTimer->aSubTimers[0].enmState, RTTIMERLNXSTATE_STARTING, RTTIMERLNXSTATE_STOPPED))
return rc2;
return VINF_SUCCESS;
case RTTIMERLNXSTATE_CALLBACK:
if (rtTimerLnxCmpXchgState(&pTimer->aSubTimers[0].enmState, RTTIMERLNXSTATE_CB_RESTARTING, enmState))
return VINF_SUCCESS;
return VERR_INTERNAL_ERROR_4;
ASMNopPause();
#ifdef CONFIG_SMP
switch (enmState)
case RTTIMERLNXSTATE_ACTIVE:
if (rtTimerLnxCmpXchgState(&pTimer->aSubTimers[0].enmState, RTTIMERLNXSTATE_STOPPING, RTTIMERLNXSTATE_ACTIVE))
case RTTIMERLNXSTATE_CALLBACK:
enmState))
case RTTIMERLNXSTATE_STOPPED:
return VINF_SUCCESS;
case RTTIMERLNXSTATE_STARTING:
case RTTIMERLNXSTATE_STOPPING:
ASMNopPause();
return VERR_TIMER_SUSPENDED;
return VINF_SUCCESS;
unsigned long cJiffies;
unsigned long flFlags;
#ifdef RTTIMER_LINUX_WITH_HRTIMER
return VINF_SUCCESS;
return VERR_NOT_SUPPORTED;
cJiffies = 0;
return VINF_SUCCESS;
bool fCanDestroy;
return VINF_SUCCESS;
fCanDestroy = true;
while (iCpu-- > 0)
switch (enmState)
case RTTIMERLNXSTATE_CALLBACK:
if (!rtTimerLnxCmpXchgState(&pTimer->aSubTimers[iCpu].enmState, RTTIMERLNXSTATE_CB_DESTROYING, enmState))
fCanDestroy = false;
fCanDestroy = false;
if (fCanDestroy)
return VINF_SUCCESS;
RTDECL(int) RTTimerCreateEx(PRTTIMER *ppTimer, uint64_t u64NanoInterval, uint32_t fFlags, PFNRTTIMER pfnTimer, void *pvUser)
unsigned cCpus;
int rc;
return VERR_INVALID_PARAMETER;
return VERR_CPU_NOT_FOUND;
#ifdef CONFIG_SMP
Assert(cCpus <= RTCPUSET_MAX_CPUS); /* On linux we have a 1:1 relationship between cpuid and set index. */
AssertReturn(u64NanoInterval, VERR_NOT_IMPLEMENTED); /* We don't implement single shot on all cpus, sorry. */
return rc;
#ifdef CONFIG_SMP
pTimer->fSpecificCpu = (fFlags & RTTIMER_FLAGS_CPU_SPECIFIC) && (fFlags & RTTIMER_FLAGS_CPU_ALL) != RTTIMER_FLAGS_CPU_ALL;
: NIL_RTCPUID;
#ifdef RTTIMER_LINUX_WITH_HRTIMER
#ifdef CONFIG_SMP
return rc;
RTTIMERLNX_LOG(("create %p hires=%d fFlags=%#x cCpus=%u\n", pTimer, pTimer->fHighRes, fFlags, cCpus));
return VINF_SUCCESS;
if (!rc)
return VERR_NOT_SUPPORTED;
return VERR_NOT_SUPPORTED;
#ifdef RTTIMER_LINUX_WITH_HRTIMER