VirtualBox

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

Last change on this file since 32736 was 32572, checked in by vboxsync, 14 years ago

VMM,SUPDrv,IPRT: More changes for related to the priodic preemption timer. (still disabled)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.7 KB
Line 
1/* $Id: timer-r0drv-nt.cpp 32572 2010-09-16 16:18:12Z 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 if ( pTimer->fSpecificCpu
221 && !RTMpIsCpuOnline(pTimer->idCpu))
222 return VERR_CPU_OFFLINE;
223
224 /*
225 * Start the timer.
226 */
227 PKDPC pMasterDpc = pTimer->fOmniTimer
228 ? &pTimer->aSubTimers[RTMpCpuIdToSetIndex(pTimer->idCpu)].NtDpc
229 : &pTimer->aSubTimers[0].NtDpc;
230
231 uint64_t u64Interval = pTimer->u64NanoInterval / 1000000; /* This is ms, believe it or not. */
232 ULONG ulInterval = (ULONG)u64Interval;
233 if (ulInterval != u64Interval)
234 ulInterval = MAXLONG;
235 else if (!ulInterval && pTimer->u64NanoInterval)
236 ulInterval = 1;
237
238 LARGE_INTEGER DueTime;
239 DueTime.QuadPart = -(int64_t)(u64First / 100); /* Relative, NT time. */
240 if (DueTime.QuadPart)
241 DueTime.QuadPart = -1;
242
243 ASMAtomicWriteBool(&pTimer->fSuspended, false);
244 KeSetTimerEx(&pTimer->NtTimer, DueTime, ulInterval, pMasterDpc);
245 return VINF_SUCCESS;
246}
247
248
249/**
250 * Worker function that stops an active timer.
251 *
252 * Shared by RTTimerStop and RTTimerDestroy.
253 *
254 * @param pTimer The active timer.
255 */
256static void rtTimerNtStopWorker(PRTTIMER pTimer)
257{
258 /*
259 * Just cancel the timer, dequeue the DPCs and flush them (if this is supported).
260 */
261 ASMAtomicWriteBool(&pTimer->fSuspended, true);
262 KeCancelTimer(&pTimer->NtTimer);
263
264 for (RTCPUID iCpu = 0; iCpu < pTimer->cSubTimers; iCpu++)
265 KeRemoveQueueDpc(&pTimer->aSubTimers[iCpu].NtDpc);
266
267 /*
268 * I'm a bit uncertain whether this should be done during RTTimerStop
269 * or only in RTTimerDestroy()... Linux and Solaris will wait AFAIK,
270 * which is why I'm keeping this here for now.
271 */
272 if (g_pfnrtNtKeFlushQueuedDpcs)
273 g_pfnrtNtKeFlushQueuedDpcs();
274}
275
276
277RTDECL(int) RTTimerStop(PRTTIMER pTimer)
278{
279 /*
280 * Validate.
281 */
282 AssertPtrReturn(pTimer, VERR_INVALID_HANDLE);
283 AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_HANDLE);
284
285 if (ASMAtomicUoReadBool(&pTimer->fSuspended))
286 return VERR_TIMER_SUSPENDED;
287
288 /*
289 * Call the worker we share with RTTimerDestroy.
290 */
291 rtTimerNtStopWorker(pTimer);
292 return VINF_SUCCESS;
293}
294
295
296RTDECL(int) RTTimerChangeInterval(PRTTIMER pTimer, uint64_t u64NanoInterval)
297{
298 AssertPtrReturn(pTimer, VERR_INVALID_HANDLE);
299 AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_HANDLE);
300
301 return VERR_NOT_SUPPORTED;
302}
303
304
305RTDECL(int) RTTimerDestroy(PRTTIMER pTimer)
306{
307 /* It's ok to pass NULL pointer. */
308 if (pTimer == /*NIL_RTTIMER*/ NULL)
309 return VINF_SUCCESS;
310 AssertPtrReturn(pTimer, VERR_INVALID_HANDLE);
311 AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_HANDLE);
312
313 /*
314 * Invalidate the timer, stop it if it's running and finally
315 * free up the memory.
316 */
317 ASMAtomicWriteU32(&pTimer->u32Magic, ~RTTIMER_MAGIC);
318 if (!ASMAtomicUoReadBool(&pTimer->fSuspended))
319 rtTimerNtStopWorker(pTimer);
320 RTMemFree(pTimer);
321
322 return VINF_SUCCESS;
323}
324
325
326RTDECL(int) RTTimerCreateEx(PRTTIMER *ppTimer, uint64_t u64NanoInterval, uint32_t fFlags, PFNRTTIMER pfnTimer, void *pvUser)
327{
328 *ppTimer = NULL;
329
330 /*
331 * Validate flags.
332 */
333 if (!RTTIMER_FLAGS_ARE_VALID(fFlags))
334 return VERR_INVALID_PARAMETER;
335 if ( (fFlags & RTTIMER_FLAGS_CPU_SPECIFIC)
336 && (fFlags & RTTIMER_FLAGS_CPU_ALL) != RTTIMER_FLAGS_CPU_ALL
337 && !RTMpIsCpuPossible(RTMpCpuIdFromSetIndex(fFlags & RTTIMER_FLAGS_CPU_MASK)))
338 return VERR_CPU_NOT_FOUND;
339
340 /*
341 * Allocate the timer handler.
342 */
343 RTCPUID cSubTimers = 1;
344 if ((fFlags & RTTIMER_FLAGS_CPU_ALL) == RTTIMER_FLAGS_CPU_ALL)
345 {
346 cSubTimers = RTMpGetMaxCpuId() + 1;
347 Assert(cSubTimers <= RTCPUSET_MAX_CPUS); /* On Windows we have a 1:1 relationship between cpuid and set index. */
348 }
349
350 PRTTIMER pTimer = (PRTTIMER)RTMemAllocZ(RT_OFFSETOF(RTTIMER, aSubTimers[cSubTimers]));
351 if (!pTimer)
352 return VERR_NO_MEMORY;
353
354 /*
355 * Initialize it.
356 */
357 pTimer->u32Magic = RTTIMER_MAGIC;
358 pTimer->fSuspended = true;
359 pTimer->fSpecificCpu = (fFlags & RTTIMER_FLAGS_CPU_SPECIFIC) && (fFlags & RTTIMER_FLAGS_CPU_ALL) != RTTIMER_FLAGS_CPU_ALL;
360 pTimer->fOmniTimer = (fFlags & RTTIMER_FLAGS_CPU_ALL) == RTTIMER_FLAGS_CPU_ALL;
361 pTimer->idCpu = pTimer->fSpecificCpu ? RTMpCpuIdFromSetIndex(fFlags & RTTIMER_FLAGS_CPU_MASK) : NIL_RTCPUID;
362 pTimer->cSubTimers = cSubTimers;
363 pTimer->pfnTimer = pfnTimer;
364 pTimer->pvUser = pvUser;
365 pTimer->u64NanoInterval = u64NanoInterval;
366 KeInitializeTimerEx(&pTimer->NtTimer, SynchronizationTimer);
367 if (pTimer->fOmniTimer)
368 {
369 /*
370 * Initialize the per-cpu "sub-timers", select the first online cpu
371 * to be the master.
372 * ASSUMES that no cpus will ever go offline.
373 */
374 pTimer->idCpu = NIL_RTCPUID;
375 for (unsigned iCpu = 0; iCpu < cSubTimers; iCpu++)
376 {
377 pTimer->aSubTimers[iCpu].iTick = 0;
378 pTimer->aSubTimers[iCpu].pParent = pTimer;
379
380 if ( pTimer->idCpu == NIL_RTCPUID
381 && RTMpIsCpuOnline(RTMpCpuIdFromSetIndex(iCpu)))
382 {
383 pTimer->idCpu = RTMpCpuIdFromSetIndex(iCpu);
384 KeInitializeDpc(&pTimer->aSubTimers[iCpu].NtDpc, rtTimerNtOmniMasterCallback, &pTimer->aSubTimers[iCpu]);
385 }
386 else
387 KeInitializeDpc(&pTimer->aSubTimers[iCpu].NtDpc, rtTimerNtOmniSlaveCallback, &pTimer->aSubTimers[iCpu]);
388 KeSetImportanceDpc(&pTimer->aSubTimers[iCpu].NtDpc, HighImportance);
389 KeSetTargetProcessorDpc(&pTimer->aSubTimers[iCpu].NtDpc, (int)RTMpCpuIdFromSetIndex(iCpu));
390 }
391 Assert(pTimer->idCpu != NIL_RTCPUID);
392 }
393 else
394 {
395 /*
396 * Initialize the first "sub-timer", target the DPC on a specific processor
397 * if requested to do so.
398 */
399 pTimer->aSubTimers[0].iTick = 0;
400 pTimer->aSubTimers[0].pParent = pTimer;
401
402 KeInitializeDpc(&pTimer->aSubTimers[0].NtDpc, rtTimerNtSimpleCallback, pTimer);
403 KeSetImportanceDpc(&pTimer->aSubTimers[0].NtDpc, HighImportance);
404 if (pTimer->fSpecificCpu)
405 KeSetTargetProcessorDpc(&pTimer->aSubTimers[0].NtDpc, (int)pTimer->idCpu);
406 }
407
408 *ppTimer = pTimer;
409 return VINF_SUCCESS;
410}
411
412
413RTDECL(uint32_t) RTTimerGetSystemGranularity(void)
414{
415 /*
416 * Get the default/max timer increment value, return it if ExSetTimerResolution
417 * isn't available. Accoring to the sysinternals guys NtQueryTimerResolution
418 * is only available in userland and they find it equally annoying.
419 */
420 ULONG ulTimeInc = KeQueryTimeIncrement();
421 if (!g_pfnrtNtExSetTimerResolution)
422 return ulTimeInc * 100; /* The value is in 100ns, the funny NT unit. */
423
424 /*
425 * Use the value returned by ExSetTimerResolution. Since the kernel is keeping
426 * count of these calls, we have to do two calls that cancel each other out.
427 */
428 ULONG ulResolution1 = g_pfnrtNtExSetTimerResolution(ulTimeInc, TRUE);
429 ULONG ulResolution2 = g_pfnrtNtExSetTimerResolution(0 /*ignored*/, FALSE);
430 AssertMsg(ulResolution1 == ulResolution2, ("%ld, %ld\n", ulResolution1, ulResolution2)); /* not supposed to change it! */
431 return ulResolution2 * 100; /* NT -> ns */
432}
433
434
435RTDECL(int) RTTimerRequestSystemGranularity(uint32_t u32Request, uint32_t *pu32Granted)
436{
437 if (!g_pfnrtNtExSetTimerResolution)
438 return VERR_NOT_SUPPORTED;
439
440 ULONG ulGranted = g_pfnrtNtExSetTimerResolution(u32Request / 100, TRUE);
441 if (pu32Granted)
442 *pu32Granted = ulGranted * 100; /* NT -> ns */
443 return VINF_SUCCESS;
444}
445
446
447RTDECL(int) RTTimerReleaseSystemGranularity(uint32_t u32Granted)
448{
449 if (!g_pfnrtNtExSetTimerResolution)
450 return VERR_NOT_SUPPORTED;
451
452 g_pfnrtNtExSetTimerResolution(0 /* ignored */, FALSE);
453 NOREF(u32Granted);
454 return VINF_SUCCESS;
455}
456
457
458RTDECL(bool) RTTimerCanDoHighResolution(void)
459{
460 return false;
461}
462
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