VirtualBox

source: vbox/trunk/src/VBox/Runtime/r0drv/nt/timer-r0drv-nt.cpp@ 28800

Last change on this file since 28800 was 28800, checked in by vboxsync, 15 years ago

Automated rebranding to Oracle copyright/license strings via filemuncher

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.3 KB
Line 
1/* $Id: timer-r0drv-nt.cpp 28800 2010-04-27 08:22:32Z vboxsync $ */
2/** @file
3 * IPRT - Timers, Ring-0 Driver, NT.
4 */
5
6/*
7 * Copyright (C) 2006-2008 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27/*******************************************************************************
28* Header Files *
29*******************************************************************************/
30#include "the-nt-kernel.h"
31
32#include <iprt/timer.h>
33#include <iprt/mp.h>
34#include <iprt/cpuset.h>
35#include <iprt/err.h>
36#include <iprt/asm.h>
37#include <iprt/assert.h>
38#include <iprt/alloc.h>
39
40#include "internal-r0drv-nt.h"
41#include "internal/magics.h"
42
43
44/*******************************************************************************
45* Structures and Typedefs *
46*******************************************************************************/
47/**
48 * A sub timer structure.
49 *
50 * This is used for keeping the per-cpu tick and DPC object.
51 */
52typedef struct RTTIMERNTSUBTIMER
53{
54 /** The tick counter. */
55 uint64_t iTick;
56 /** Pointer to the parent timer. */
57 PRTTIMER pParent;
58 /** The NT DPC object. */
59 KDPC NtDpc;
60} RTTIMERNTSUBTIMER;
61/** Pointer to a NT sub-timer structure. */
62typedef RTTIMERNTSUBTIMER *PRTTIMERNTSUBTIMER;
63
64/**
65 * The internal representation of an Linux timer handle.
66 */
67typedef struct RTTIMER
68{
69 /** Magic.
70 * This is RTTIMER_MAGIC, but changes to something else before the timer
71 * is destroyed to indicate clearly that thread should exit. */
72 uint32_t volatile u32Magic;
73 /** Flag indicating the timer is suspended. */
74 bool volatile fSuspended;
75 /** Whether the timer must run on one specific CPU or not. */
76 bool fSpecificCpu;
77 /** Whether the timer must run on all CPUs or not. */
78 bool fOmniTimer;
79 /** The CPU it must run on if fSpecificCpu is set.
80 * The master CPU for an omni-timer. */
81 RTCPUID idCpu;
82 /** Callback. */
83 PFNRTTIMER pfnTimer;
84 /** User argument. */
85 void *pvUser;
86 /** The timer interval. 0 if one-shot. */
87 uint64_t u64NanoInterval;
88 /** The Nt timer object. */
89 KTIMER NtTimer;
90 /** The number of sub-timers. */
91 RTCPUID cSubTimers;
92 /** Sub-timers.
93 * Normally there is just one, but for RTTIMER_FLAGS_CPU_ALL this will contain
94 * an entry for all possible cpus. In that case the index will be the same as
95 * for the RTCpuSet. */
96 RTTIMERNTSUBTIMER aSubTimers[1];
97} RTTIMER;
98
99
100
101/**
102 * Timer callback function for the non-omni timers.
103 *
104 * @returns HRTIMER_NORESTART or HRTIMER_RESTART depending on whether it's a one-shot or interval timer.
105 * @param pDpc Pointer to the the DPC.
106 * @param pvUser Pointer to our internal timer structure.
107 * @param SystemArgument1 Some system argument.
108 * @param SystemArgument2 Some system argument.
109 */
110static void _stdcall rtTimerNtSimpleCallback(IN PKDPC pDpc, IN PVOID pvUser, IN PVOID SystemArgument1, IN PVOID SystemArgument2)
111{
112 PRTTIMER pTimer = (PRTTIMER)pvUser;
113 AssertPtr(pTimer);
114#ifdef RT_STRICT
115 if (KeGetCurrentIrql() < DISPATCH_LEVEL)
116 RTAssertMsg2Weak("rtTimerNtSimpleCallback: Irql=%d expected >=%d\n", KeGetCurrentIrql(), DISPATCH_LEVEL);
117#endif
118
119 /*
120 * Check that we haven't been suspended before doing the callout.
121 */
122 if ( !ASMAtomicUoReadBool(&pTimer->fSuspended)
123 && pTimer->u32Magic == RTTIMER_MAGIC)
124 pTimer->pfnTimer(pTimer, pTimer->pvUser, ++pTimer->aSubTimers[0].iTick);
125
126 NOREF(pDpc); NOREF(SystemArgument1); NOREF(SystemArgument2);
127}
128
129
130/**
131 * The slave DPC callback for an omni timer.
132 *
133 * @param pDpc The DPC object.
134 * @param pvUser Pointer to the sub-timer.
135 * @param SystemArgument1 Some system stuff.
136 * @param SystemArgument2 Some system stuff.
137 */
138static void _stdcall rtTimerNtOmniSlaveCallback(IN PKDPC pDpc, IN PVOID pvUser, IN PVOID SystemArgument1, IN PVOID SystemArgument2)
139{
140 PRTTIMERNTSUBTIMER pSubTimer = (PRTTIMERNTSUBTIMER)pvUser;
141 PRTTIMER pTimer = pSubTimer->pParent;
142
143 AssertPtr(pTimer);
144#ifdef RT_STRICT
145 if (KeGetCurrentIrql() < DISPATCH_LEVEL)
146 RTAssertMsg2Weak("rtTimerNtOmniSlaveCallback: Irql=%d expected >=%d\n", KeGetCurrentIrql(), DISPATCH_LEVEL);
147 int iCpuSelf = RTMpCpuIdToSetIndex(RTMpCpuId());
148 if (pSubTimer - &pTimer->aSubTimers[0] != iCpuSelf)
149 RTAssertMsg2Weak("rtTimerNtOmniSlaveCallback: iCpuSelf=%d pSubTimer=%p / %d\n", iCpuSelf, pSubTimer, pSubTimer - &pTimer->aSubTimers[0]);
150#endif
151
152 /*
153 * Check that we haven't been suspended before doing the callout.
154 */
155 if ( !ASMAtomicUoReadBool(&pTimer->fSuspended)
156 && pTimer->u32Magic == RTTIMER_MAGIC)
157 pTimer->pfnTimer(pTimer, pTimer->pvUser, ++pSubTimer->iTick);
158
159 NOREF(pDpc); NOREF(SystemArgument1); NOREF(SystemArgument2);
160}
161
162
163/**
164 * The timer callback for an omni-timer.
165 *
166 * This is responsible for queueing the DPCs for the other CPUs and
167 * perform the callback on the CPU on which it is called.
168 *
169 * @param pDpc The DPC object.
170 * @param pvUser Pointer to the sub-timer.
171 * @param SystemArgument1 Some system stuff.
172 * @param SystemArgument2 Some system stuff.
173 */
174static void _stdcall rtTimerNtOmniMasterCallback(IN PKDPC pDpc, IN PVOID pvUser, IN PVOID SystemArgument1, IN PVOID SystemArgument2)
175{
176 PRTTIMERNTSUBTIMER pSubTimer = (PRTTIMERNTSUBTIMER)pvUser;
177 PRTTIMER pTimer = pSubTimer->pParent;
178 int iCpuSelf = RTMpCpuIdToSetIndex(RTMpCpuId());
179
180 AssertPtr(pTimer);
181#ifdef RT_STRICT
182 if (KeGetCurrentIrql() < DISPATCH_LEVEL)
183 RTAssertMsg2Weak("rtTimerNtOmniMasterCallback: Irql=%d expected >=%d\n", KeGetCurrentIrql(), DISPATCH_LEVEL);
184 if (pSubTimer - &pTimer->aSubTimers[0] != iCpuSelf)
185 RTAssertMsg2Weak("rtTimerNtOmniMasterCallback: iCpuSelf=%d pSubTimer=%p / %d\n", iCpuSelf, pSubTimer, pSubTimer - &pTimer->aSubTimers[0]);
186#endif
187
188 /*
189 * Check that we haven't been suspended before scheduling the other DPCs
190 * and doing the callout.
191 */
192 if ( !ASMAtomicUoReadBool(&pTimer->fSuspended)
193 && pTimer->u32Magic == RTTIMER_MAGIC)
194 {
195 RTCPUSET OnlineSet;
196 RTMpGetOnlineSet(&OnlineSet);
197 for (int iCpu = 0; iCpu < RTCPUSET_MAX_CPUS; iCpu++)
198 if ( RTCpuSetIsMemberByIndex(&OnlineSet, iCpu)
199 && iCpuSelf != iCpu)
200 KeInsertQueueDpc(&pTimer->aSubTimers[iCpu].NtDpc, 0, 0);
201
202 pTimer->pfnTimer(pTimer, pTimer->pvUser, ++pSubTimer->iTick);
203 }
204
205 NOREF(pDpc); NOREF(SystemArgument1); NOREF(SystemArgument2);
206}
207
208
209
210RTDECL(int) RTTimerStart(PRTTIMER pTimer, uint64_t u64First)
211{
212 /*
213 * Validate.
214 */
215 AssertPtrReturn(pTimer, VERR_INVALID_HANDLE);
216 AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_HANDLE);
217
218 if (!ASMAtomicUoReadBool(&pTimer->fSuspended))
219 return VERR_TIMER_ACTIVE;
220
221 /*
222 * Start the timer.
223 */
224 PKDPC pMasterDpc = pTimer->fOmniTimer
225 ? &pTimer->aSubTimers[RTMpCpuIdToSetIndex(pTimer->idCpu)].NtDpc
226 : &pTimer->aSubTimers[0].NtDpc;
227
228 uint64_t u64Interval = pTimer->u64NanoInterval / 1000000; /* This is ms, believe it or not. */
229 ULONG ulInterval = (ULONG)u64Interval;
230 if (ulInterval != u64Interval)
231 ulInterval = MAXLONG;
232 else if (!ulInterval && pTimer->u64NanoInterval)
233 ulInterval = 1;
234
235 LARGE_INTEGER DueTime;
236 DueTime.QuadPart = -(int64_t)(u64First / 100); /* Relative, NT time. */
237 if (DueTime.QuadPart)
238 DueTime.QuadPart = -1;
239
240 ASMAtomicWriteBool(&pTimer->fSuspended, false);
241 KeSetTimerEx(&pTimer->NtTimer, DueTime, ulInterval, pMasterDpc);
242 return VINF_SUCCESS;
243}
244
245
246/**
247 * Worker function that stops an active timer.
248 *
249 * Shared by RTTimerStop and RTTimerDestroy.
250 *
251 * @param pTimer The active timer.
252 */
253static void rtTimerNtStopWorker(PRTTIMER pTimer)
254{
255 /*
256 * Just cancel the timer, dequeue the DPCs and flush them (if this is supported).
257 */
258 ASMAtomicWriteBool(&pTimer->fSuspended, true);
259 KeCancelTimer(&pTimer->NtTimer);
260
261 for (RTCPUID iCpu = 0; iCpu < pTimer->cSubTimers; iCpu++)
262 KeRemoveQueueDpc(&pTimer->aSubTimers[iCpu].NtDpc);
263
264 /*
265 * I'm a bit uncertain whether this should be done during RTTimerStop
266 * or only in RTTimerDestroy()... Linux and Solaris will wait AFAIK,
267 * which is why I'm keeping this here for now.
268 */
269 if (g_pfnrtNtKeFlushQueuedDpcs)
270 g_pfnrtNtKeFlushQueuedDpcs();
271}
272
273
274RTDECL(int) RTTimerStop(PRTTIMER pTimer)
275{
276 /*
277 * Validate.
278 */
279 AssertPtrReturn(pTimer, VERR_INVALID_HANDLE);
280 AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_HANDLE);
281
282 if (ASMAtomicUoReadBool(&pTimer->fSuspended))
283 return VERR_TIMER_SUSPENDED;
284
285 /*
286 * Call the worker we share with RTTimerDestroy.
287 */
288 rtTimerNtStopWorker(pTimer);
289 return VINF_SUCCESS;
290}
291
292
293RTDECL(int) RTTimerDestroy(PRTTIMER pTimer)
294{
295 /* It's ok to pass NULL pointer. */
296 if (pTimer == /*NIL_RTTIMER*/ NULL)
297 return VINF_SUCCESS;
298 AssertPtrReturn(pTimer, VERR_INVALID_HANDLE);
299 AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_HANDLE);
300
301 /*
302 * Invalidate the timer, stop it if it's running and finally .
303 * free up the memory.
304 */
305 ASMAtomicWriteU32(&pTimer->u32Magic, ~RTTIMER_MAGIC);
306 if (!ASMAtomicUoReadBool(&pTimer->fSuspended))
307 rtTimerNtStopWorker(pTimer);
308 RTMemFree(pTimer);
309
310 return VINF_SUCCESS;
311}
312
313
314RTDECL(int) RTTimerCreateEx(PRTTIMER *ppTimer, uint64_t u64NanoInterval, unsigned fFlags, PFNRTTIMER pfnTimer, void *pvUser)
315{
316 *ppTimer = NULL;
317
318 /*
319 * Validate flags.
320 */
321 if (!RTTIMER_FLAGS_ARE_VALID(fFlags))
322 return VERR_INVALID_PARAMETER;
323 if ( (fFlags & RTTIMER_FLAGS_CPU_SPECIFIC)
324 && (fFlags & RTTIMER_FLAGS_CPU_ALL) != RTTIMER_FLAGS_CPU_ALL
325 && !RTMpIsCpuOnline(fFlags & RTTIMER_FLAGS_CPU_MASK))
326 return (fFlags & RTTIMER_FLAGS_CPU_MASK) > RTMpGetMaxCpuId()
327 ? VERR_CPU_NOT_FOUND
328 : VERR_CPU_OFFLINE;
329
330 /*
331 * Allocate the timer handler.
332 */
333 RTCPUID cSubTimers = 1;
334 if ((fFlags & RTTIMER_FLAGS_CPU_ALL) == RTTIMER_FLAGS_CPU_ALL)
335 {
336 cSubTimers = RTMpGetMaxCpuId() + 1;
337 Assert(cSubTimers <= RTCPUSET_MAX_CPUS); /* On Windows we have a 1:1 relationship between cpuid and set index. */
338 }
339
340 PRTTIMER pTimer = (PRTTIMER)RTMemAllocZ(RT_OFFSETOF(RTTIMER, aSubTimers[cSubTimers]));
341 if (!pTimer)
342 return VERR_NO_MEMORY;
343
344 /*
345 * Initialize it.
346 */
347 pTimer->u32Magic = RTTIMER_MAGIC;
348 pTimer->fSuspended = true;
349 pTimer->fSpecificCpu = (fFlags & RTTIMER_FLAGS_CPU_SPECIFIC) && (fFlags & RTTIMER_FLAGS_CPU_ALL) != RTTIMER_FLAGS_CPU_ALL;
350 pTimer->fOmniTimer = (fFlags & RTTIMER_FLAGS_CPU_ALL) == RTTIMER_FLAGS_CPU_ALL;
351 pTimer->idCpu = fFlags & RTTIMER_FLAGS_CPU_MASK;
352 pTimer->cSubTimers = cSubTimers;
353 pTimer->pfnTimer = pfnTimer;
354 pTimer->pvUser = pvUser;
355 pTimer->u64NanoInterval = u64NanoInterval;
356 KeInitializeTimerEx(&pTimer->NtTimer, SynchronizationTimer);
357 if (pTimer->fOmniTimer)
358 {
359 /*
360 * Initialize the per-cpu "sub-timers", select the first online cpu
361 * to be the master.
362 * ASSUMES that no cpus will ever go offline.
363 */
364 pTimer->idCpu = NIL_RTCPUID; /* */
365 for (unsigned iCpu = 0; iCpu < cSubTimers; iCpu++)
366 {
367 pTimer->aSubTimers[iCpu].iTick = 0;
368 pTimer->aSubTimers[iCpu].pParent = pTimer;
369
370 if ( pTimer->idCpu == NIL_RTCPUID
371 && RTMpIsCpuOnline(RTMpCpuIdFromSetIndex(iCpu)))
372 {
373 pTimer->idCpu = RTMpCpuIdFromSetIndex(iCpu);
374 KeInitializeDpc(&pTimer->aSubTimers[iCpu].NtDpc, rtTimerNtOmniMasterCallback, &pTimer->aSubTimers[iCpu]);
375 }
376 else
377 KeInitializeDpc(&pTimer->aSubTimers[iCpu].NtDpc, rtTimerNtOmniSlaveCallback, &pTimer->aSubTimers[iCpu]);
378 KeSetImportanceDpc(&pTimer->aSubTimers[iCpu].NtDpc, HighImportance);
379 KeSetTargetProcessorDpc(&pTimer->aSubTimers[iCpu].NtDpc, (int)RTMpCpuIdFromSetIndex(iCpu));
380 }
381 Assert(pTimer->idCpu != NIL_RTCPUID);
382 }
383 else
384 {
385 /*
386 * Initialize the first "sub-timer", target the DPC on a specific processor
387 * if requested to do so.
388 */
389 pTimer->aSubTimers[0].iTick = 0;
390 pTimer->aSubTimers[0].pParent = pTimer;
391
392 KeInitializeDpc(&pTimer->aSubTimers[0].NtDpc, rtTimerNtSimpleCallback, pTimer);
393 KeSetImportanceDpc(&pTimer->aSubTimers[0].NtDpc, HighImportance);
394 if (pTimer->fSpecificCpu)
395 KeSetTargetProcessorDpc(&pTimer->aSubTimers[0].NtDpc, (int)pTimer->idCpu);
396 }
397
398 *ppTimer = pTimer;
399 return VINF_SUCCESS;
400}
401
402
403RTDECL(uint32_t) RTTimerGetSystemGranularity(void)
404{
405 /*
406 * Get the default/max timer increment value, return it if ExSetTimerResolution
407 * isn't available. Accoring to the sysinternals guys NtQueryTimerResolution
408 * is only available in userland and they find it equally annoying.
409 */
410 ULONG ulTimeInc = KeQueryTimeIncrement();
411 if (!g_pfnrtNtExSetTimerResolution)
412 return ulTimeInc * 100; /* The value is in 100ns, the funny NT unit. */
413
414 /*
415 * Use the value returned by ExSetTimerResolution. Since the kernel is keeping
416 * count of these calls, we have to do two calls that cancel each other out.
417 */
418 ULONG ulResolution1 = g_pfnrtNtExSetTimerResolution(ulTimeInc, TRUE);
419 ULONG ulResolution2 = g_pfnrtNtExSetTimerResolution(0 /*ignored*/, FALSE);
420 AssertMsg(ulResolution1 == ulResolution2, ("%ld, %ld\n", ulResolution1, ulResolution2)); /* not supposed to change it! */
421 return ulResolution2 * 100; /* NT -> ns */
422}
423
424
425RTDECL(int) RTTimerRequestSystemGranularity(uint32_t u32Request, uint32_t *pu32Granted)
426{
427 if (!g_pfnrtNtExSetTimerResolution)
428 return VERR_NOT_SUPPORTED;
429
430 ULONG ulGranted = g_pfnrtNtExSetTimerResolution(u32Request / 100, TRUE);
431 if (pu32Granted)
432 *pu32Granted = ulGranted * 100; /* NT -> ns */
433 return VINF_SUCCESS;
434}
435
436
437RTDECL(int) RTTimerReleaseSystemGranularity(uint32_t u32Granted)
438{
439 if (!g_pfnrtNtExSetTimerResolution)
440 return VERR_NOT_SUPPORTED;
441
442 g_pfnrtNtExSetTimerResolution(0 /* ignored */, FALSE);
443 NOREF(u32Granted);
444 return VINF_SUCCESS;
445}
446
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette