VirtualBox

source: vbox/trunk/src/VBox/Runtime/r0drv/solaris/timer-r0drv-solaris.c@ 11280

Last change on this file since 11280 was 9557, checked in by vboxsync, 17 years ago

New timer code for solaris.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 18.6 KB
Line 
1/** $Id: timer-r0drv-solaris.c 9557 2008-06-09 18:19:26Z vboxsync $ */
2/** @file
3 * IPRT - Timer, Ring-0 Driver, Solaris.
4 */
5
6/*
7 * Copyright (C) 2006-2008 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
27 * Clara, CA 95054 USA or visit http://www.sun.com if you need
28 * additional information or have any questions.
29 */
30
31
32/*******************************************************************************
33* Header Files *
34*******************************************************************************/
35#include "the-solaris-kernel.h"
36
37#include <iprt/timer.h>
38#include <iprt/time.h>
39#include <iprt/mp.h>
40#include <iprt/spinlock.h>
41#include <iprt/err.h>
42#include <iprt/asm.h>
43#include <iprt/assert.h>
44#include <iprt/alloc.h>
45
46#include "internal/magics.h"
47
48
49/*******************************************************************************
50* Structures and Typedefs *
51*******************************************************************************/
52/**
53 * This is used to track sub-timer data.
54 */
55typedef struct RTTIMERSOLSUBTIMER
56{
57 /** The current timer tick. */
58 uint64_t iTick;
59 /** Pointer to the parent timer. */
60 PRTTIMER pParent;
61} RTTIMERSOLSUBTIMER;
62/** Pointer to a Solaris sub-timer. */
63typedef RTTIMERSOLSUBTIMER *PRTTIMERSOLSUBTIMER;
64
65/**
66 * The internal representation of a Solaris timer handle.
67 */
68typedef struct RTTIMER
69{
70 /** Magic.
71 * This is RTTIMER_MAGIC, but changes to something else before the timer
72 * is destroyed to indicate clearly that thread should exit. */
73 uint32_t volatile u32Magic;
74 /** The cyclic timer id.
75 * This is CYCLIC_NONE if the timer hasn't been started. */
76 cyclic_id_t volatile CyclicId;
77 /** Flag used by rtTimerSolarisOmniOnlineCallback to see whether we're inside the cyclic_add_omni call or not. */
78 bool volatile fStarting;
79 /** Whether the timer must run on a specific CPU or not. */
80 bool fSpecificCpu;
81 /** Set if we're using an omni cyclic. */
82 bool fOmni;
83 /** The CPU it must run on if fSpecificCpu is set. */
84 RTCPUID idCpu;
85 /** Callback. */
86 PFNRTTIMER pfnTimer;
87 /** User argument. */
88 void *pvUser;
89 /** The timer interval. 0 for one-shot timer */
90 uint64_t u64NanoInterval;
91 /** The timer spec (for omni timers mostly). */
92 cyc_time_t TimeSpecs;
93 /** The number of sub-timers. */
94 RTCPUID cSubTimers;
95 /** Sub-timer data.
96 * When fOmni is set, this will be an array indexed by CPU id.
97 * When fOmni is clear, the array will only have one member. */
98 RTTIMERSOLSUBTIMER aSubTimers[1];
99} RTTIMER;
100
101
102/*******************************************************************************
103* Internal Functions *
104*******************************************************************************/
105static void rtTimerSolarisCallback(void *pvTimer);
106static void rtTimerSolarisOmniCallback(void *pvSubTimer);
107static void rtTimerSolarisOmniDummyCallback(void *pvIgnored);
108static void rtTimerSolarisOmniOnlineCallback(void *pvTimer, cpu_t *pCpu, cyc_handler_t *pCyclicInfo, cyc_time_t *pTimeSpecs);
109static void rtTimerSolarisOmniOfflineCallback(void *pvTimer, cpu_t *pCpu, void *pvTick);
110static bool rtTimerSolarisStop(PRTTIMER pTimer);
111
112
113AssertCompileSize(cyclic_id_t, sizeof(void *));
114
115/** Atomic read of RTTIMER::CyclicId. */
116DECLINLINE(cyclic_id_t) rtTimerSolarisGetCyclicId(PRTTIMER pTimer)
117{
118 return (cyclic_id_t)ASMAtomicUoReadPtr((void * volatile *)&pTimer->CyclicId);
119}
120
121
122/** Atomic write of RTTIMER::CyclicId. */
123DECLINLINE(cyclic_id_t) rtTimerSolarisSetCyclicId(PRTTIMER pTimer, cyclic_id_t CyclicIdNew)
124{
125 ASMAtomicWritePtr((void * volatile *)&pTimer->CyclicId, (void *)CyclicIdNew);
126}
127
128
129/** Atomic compare and exchange of RTTIMER::CyclicId. */
130DECLINLINE(bool) rtTimerSolarisCmpXchgCyclicId(PRTTIMER pTimer, cyclic_id_t CyclicIdNew, cyclic_id_t CyclicIdOld)
131{
132 return ASMAtomicCmpXchgPtr((void * volatile *)&pTimer->CyclicId, (void *)CyclicIdNew, (void *)CyclicIdOld);
133}
134
135
136RTDECL(int) RTTimerCreateEx(PRTTIMER *ppTimer, uint64_t u64NanoInterval, unsigned fFlags, PFNRTTIMER pfnTimer, void *pvUser)
137{
138 RTCPUID i;
139 *ppTimer = NULL;
140
141 /*
142 * Validate flags.
143 */
144 if (!RTTIMER_FLAGS_ARE_VALID(fFlags))
145 return VERR_INVALID_PARAMETER;
146 if ( (fFlags & RTTIMER_FLAGS_CPU_SPECIFIC)
147 && (fFlags & RTTIMER_FLAGS_CPU_ALL) != RTTIMER_FLAGS_CPU_ALL
148 && !RTMpIsCpuPossible((fFlags & RTTIMER_FLAGS_CPU_MASK)))
149 return VERR_CPU_NOT_FOUND;
150
151 /*
152 * Allocate and initialize the timer handle.
153 */
154 size_t cCpus = (fFlags & RTTIMER_FLAGS_CPU_ALL) == RTTIMER_FLAGS_CPU_ALL
155 ? RTMpGetMaxCpuId() + 1 /* ASSUMES small max value, no pointers. */
156 : 1;
157 PRTTIMER pTimer = (PRTTIMER)RTMemAllocZ(RT_OFFSETOF(RTTIMER, aSubTimers[cCpus]));
158 if (!pTimer)
159 return VERR_NO_MEMORY;
160
161 pTimer->u32Magic = RTTIMER_MAGIC;
162 pTimer->CyclicId = CYCLIC_NONE;
163 pTimer->fStarting = false;
164 if ( (fFlags & RTTIMER_FLAGS_CPU_SPECIFIC)
165 && (fFlags & RTTIMER_FLAGS_CPU_ALL) != RTTIMER_FLAGS_CPU_ALL)
166 {
167 pTimer->fSpecificCpu = true;
168 pTimer->fOmni = false;
169 pTimer->idCpu = fFlags & RTTIMER_FLAGS_CPU_MASK;
170 }
171 else
172 {
173 pTimer->fSpecificCpu = false;
174 pTimer->fOmni = (fFlags & RTTIMER_FLAGS_CPU_ALL) == RTTIMER_FLAGS_CPU_ALL;
175 pTimer->idCpu = NIL_RTCPUID;
176 }
177 pTimer->pfnTimer = pfnTimer;
178 pTimer->pvUser = pvUser;
179 pTimer->u64NanoInterval = u64NanoInterval;
180 pTimer->cSubTimers = cCpus;
181
182 for (i = 0; i < cCpus; i++)
183 {
184 pTimer->aSubTimers[i].iTick = 0;
185 pTimer->aSubTimers[i].pParent = pTimer;
186 }
187
188 *ppTimer = pTimer;
189 return VINF_SUCCESS;
190}
191
192
193RTDECL(int) RTTimerDestroy(PRTTIMER pTimer)
194{
195 /*
196 * Validate.
197 */
198 if (pTimer == NULL)
199 return VINF_SUCCESS;
200 AssertPtrReturn(pTimer, VERR_INVALID_HANDLE);
201 AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_HANDLE);
202
203 /*
204 * Invalid the timer, stop it, and free the associated resources.
205 */
206 ASMAtomicWriteU32(&pTimer->u32Magic, ~RTTIMER_MAGIC);
207 rtTimerSolarisStop(pTimer);
208 RTMemFree(pTimer);
209
210 return VINF_SUCCESS;
211}
212
213
214RTDECL(int) RTTimerStart(PRTTIMER pTimer, uint64_t u64First)
215{
216 RTCPUID i;
217 cyclic_id_t CyclicId;
218 cyc_handler_t CyclicInfo;
219 cyc_omni_handler_t CyclicOmniInfo;
220 int rc = VINF_SUCCESS;
221
222 /*
223 * Validate.
224 */
225 AssertPtrReturn(pTimer, VERR_INVALID_HANDLE);
226 AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_HANDLE);
227 if (rtTimerSolarisGetCyclicId(pTimer) != CYCLIC_NONE)
228 {
229 /*
230 * If it's a one-shot we might end up here because it didn't stop after
231 * the first firing. There are two reasons for this depending on the
232 * kind type of timer. (1) Non-omni timers are (potentially) racing our
233 * RTTimerStart in setting RTTIMER::CyclicId. (2) Omni timers are stopped
234 * on the 2nd firing because we have to make sure all cpus gets called, and
235 * we're using the 2nd round that comes 1 sec after the first because this
236 * is the easier way out.
237 */
238 if (pTimer->u64NanoInterval)
239 return VERR_TIMER_ACTIVE;
240
241 for (i = 0; i < pTimer->cSubTimers; i++)
242 if (pTimer->aSubTimers[i].iTick)
243 break; /* has fired */
244 if (i >= pTimer->cSubTimers)
245 return VERR_TIMER_ACTIVE;
246
247 rtTimerSolarisStop(pTimer);
248 }
249
250 /*
251 * Do the setup bits that doesn't need the lock.
252 * We'll setup both omni and non-omni stuff here because it shorter than if'ing it.
253 */
254 CyclicInfo.cyh_func = rtTimerSolarisCallback;
255 CyclicInfo.cyh_arg = pTimer;
256 CyclicInfo.cyh_level = CY_LOCK_LEVEL;
257
258 CyclicOmniInfo.cyo_online = rtTimerSolarisOmniOnlineCallback;
259 CyclicOmniInfo.cyo_offline = rtTimerSolarisOmniOfflineCallback;
260 CyclicOmniInfo.cyo_arg = pTimer;
261
262 for (i = 0; i > pTimer->cSubTimers; i++)
263 pTimer->aSubTimers[i].iTick = 0;
264
265 if (pTimer->fSpecificCpu && u64First < 10000)
266 u64First = RTTimeNanoTS() + 10000; /* Try make sure it doesn't fire before we re-bind it. */
267 else
268 u64First += RTTimeNanoTS(); /* ASSUMES it is implemented via gethrtime() */
269
270 pTimer->TimeSpecs.cyt_when = u64First;
271 pTimer->TimeSpecs.cyt_interval = !pTimer->u64NanoInterval
272 ? 1000000000 /* 1 sec */
273 : pTimer->u64NanoInterval;
274
275 /*
276 * Acquire the cpu lock and call cyclic_add/cyclic_add_omni.
277 */
278 mutex_enter(&cpu_lock);
279
280 ASMAtomicWriteBool(&pTimer->fStarting, true);
281 if (pTimer->fOmni)
282 CyclicId = cyclic_add_omni(&CyclicOmniInfo);
283 else if (pTimer->fSpecificCpu)
284 {
285 cpu_t *pCpu = cpu_get(pTimer->idCpu);
286 CyclicId = CYCLIC_NONE;
287 if (pCpu)
288 {
289 if (cpu_is_online(pCpu))
290 {
291 CyclicId = cyclic_add(&CyclicInfo, &pTimer->TimeSpecs);
292 if (CyclicId != CYCLIC_NONE)
293 cyclic_bind(CyclicId, pCpu, NULL);
294 }
295 else
296 rc = VERR_CPU_OFFLINE;
297 }
298 else
299 rc = VERR_CPU_NOT_FOUND;
300 }
301 else
302 CyclicId = cyclic_add(&CyclicInfo, &pTimer->TimeSpecs);
303
304 rtTimerSolarisSetCyclicId(pTimer, CyclicId);
305 ASMAtomicWriteBool(&pTimer->fStarting, false);
306
307 mutex_exit(&cpu_lock);
308
309 /*
310 * Just some sanity checks should the cylic code start returning errors.
311 */
312 Assert(RT_SUCCESS(rc) || CyclicId == CYCLIC_NONE);
313 if (CyclicId == CYCLIC_NONE && rc == VINF_SUCCESS)
314 rc = VERR_GENERAL_FAILURE;
315 return rc;
316}
317
318
319RTDECL(int) RTTimerStop(PRTTIMER pTimer)
320{
321 /*
322 * Validate.
323 */
324 AssertPtrReturn(pTimer, VERR_INVALID_HANDLE);
325 AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_HANDLE);
326
327 /*
328 * Stop the timer.
329 */
330 if (!rtTimerSolarisStop(pTimer))
331 return VERR_TIMER_SUSPENDED;
332 return VINF_SUCCESS;
333}
334
335
336/**
337 * Timer callback function for non-omni timers.
338 *
339 * @param pvTimer Pointer to the timer.
340 */
341static void rtTimerSolarisCallback(void *pvTimer)
342{
343 PRTTIMER pTimer = (PRTTIMER)pvTimer;
344
345 /* Check for destruction. */
346 if (pTimer->u32Magic != RTTIMER_MAGIC)
347 return;
348
349 /*
350 * If this is a one shot timer, suspend the timer here as Solaris
351 * does not support single-shot timers implicitly.
352 */
353 if (!pTimer->u64NanoInterval)
354 {
355 rtTimerSolarisStop(pTimer);
356 if (!pTimer->aSubTimers[0].iTick)
357 {
358 ASMAtomicWriteU64(&pTimer->aSubTimers[0].iTick, 1); /* paranoia */
359 pTimer->pfnTimer(pTimer, pTimer->pvUser, 1);
360 }
361 }
362 else
363 /* recurring */
364 pTimer->pfnTimer(pTimer, pTimer->pvUser, ++pTimer->aSubTimers[0].iTick);
365}
366
367
368/**
369 * Timer callback function for omni timers.
370 *
371 * @param pvTimer Pointer to the sub-timer.
372 */
373static void rtTimerSolarisOmniCallback(void *pvSubTimer)
374{
375 PRTTIMERSOLSUBTIMER pSubTimer = (PRTTIMERSOLSUBTIMER)pvSubTimer;
376 PRTTIMER pTimer = pSubTimer->pParent;
377
378 /* Check for destruction. */
379 if ( !VALID_PTR(pTimer)
380 || pTimer->u32Magic != RTTIMER_MAGIC)
381 return;
382
383 /*
384 * If this is a one-shot timer, suspend it here the 2nd time around.
385 * We cannot do it the first time like for the non-omni timers since
386 * we don't know if it has fired on all the cpus yet.
387 */
388 if (!pTimer->u64NanoInterval)
389 {
390 if (!pSubTimer->iTick)
391 {
392 ASMAtomicWriteU64(&pSubTimer->iTick, 1); /* paranoia */
393 pTimer->pfnTimer(pTimer, pTimer->pvUser, 1);
394 }
395 else
396 rtTimerSolarisStop(pTimer);
397 }
398 else
399 /* recurring */
400 pTimer->pfnTimer(pTimer, pTimer->pvUser, ++pSubTimer->iTick);
401}
402
403
404/**
405 * This is a dummy callback use for the cases where we get cpus which id we
406 * cannot handle because of broken RTMpGetMaxCpuId(), or if we're racing
407 * RTTimerDestroy().
408 *
409 * This shouldn't happen of course, but if it does we wish to handle
410 * gracefully instead of crashing.
411 *
412 * @param pvIgnored Ignored
413 */
414static void rtTimerSolarisOmniDummyCallback(void *pvIgnored)
415{
416 NOREF(pvIgnored);
417}
418
419
420/**
421 * Omni-timer callback that sets up the timer for a cpu during cyclic_add_omni
422 * or at later when a CPU comes online.
423 *
424 *
425 * @param pvTimer Pointer to the timer.
426 * @param pCpu The cpu that has come online.
427 * @param pCyclicInfo Where to store the cyclic handler info.
428 * @param pTimeSpecs Where to store the timer firing specs.
429 */
430static void rtTimerSolarisOmniOnlineCallback(void *pvTimer, cpu_t *pCpu, cyc_handler_t *pCyclicInfo, cyc_time_t *pTimeSpecs)
431{
432 PRTTIMER pTimer = (PRTTIMER)pvTimer;
433 RTCPUID idCpu = pCpu->cpu_id;
434 AssertMsg(idCpu < pTimer->cSubTimers, ("%d < %d\n", (int)idCpu, (int)pTimer->cSubTimers));
435 if ( idCpu < pTimer->cSubTimers
436 && pTimer->u32Magic == RTTIMER_MAGIC)
437 {
438 PRTTIMERSOLSUBTIMER pSubTimer = &pTimer->aSubTimers[idCpu];
439
440 pCyclicInfo->cyh_func = rtTimerSolarisOmniCallback;
441 pCyclicInfo->cyh_arg = pSubTimer;
442 pCyclicInfo->cyh_level = CY_LOCK_LEVEL;
443
444 if (pTimer->fStarting)
445 {
446 /*
447 * Called during cyclic_add_omni, just spread the 2nd tick
448 * for the one-shots to avoid unnecessary lock contention.
449 */
450 *pTimeSpecs = pTimer->TimeSpecs;
451 if (!pTimer->u64NanoInterval)
452 pTimeSpecs->cyt_interval += idCpu * (unsigned)nsec_per_tick * 2U;
453 }
454 else
455 {
456 /*
457 * Called at run-time, have to make sure cyt_when isn't in the past.
458 */
459 ASMAtomicWriteU64(&pSubTimer->iTick, 0); /* paranoia */
460
461 uint64_t u64Now = RTTimeNanoTS(); /* ASSUMES it's implemented using gethrtime(). */
462 if (pTimer->TimeSpecs.cyt_when > u64Now)
463 *pTimeSpecs = pTimer->TimeSpecs;
464 else
465 {
466 if (!pTimer->u64NanoInterval)
467 {
468 /* one-shot: Just schedule a 1 sec timeout and set the tick to 1. */
469 pTimeSpecs->cyt_when = u64Now + 1000000000;
470 pTimeSpecs->cyt_interval = 1000000000;
471 ASMAtomicWriteU64(&pSubTimer->iTick, 1);
472 }
473 else
474 {
475#if 1 /* This might be made into a RTTIMER_FLAGS_something later, for now ASAP is what we need. */
476 /* recurring: ASAP. */
477 pTimeSpecs->cyt_when = u64Now;
478#else
479 /* recurring: Adjust it to the next tick. */
480 uint64_t cTicks = (u64Now - pTimer->TimeSpecs.cyt_when) / pTimer->TimeSpecs.cyt_interval;
481 pTimeSpecs->cyt_when = (cTicks + 1) * pTimer->TimeSpecs.cyt_interval;
482#endif
483 pTimeSpecs->cyt_interval = pTimer->TimeSpecs.cyt_interval;
484 }
485 }
486 }
487 }
488 else
489 {
490 /*
491 * Invalid cpu id or destruction race.
492 */
493 pCyclicInfo->cyh_func = rtTimerSolarisOmniDummyCallback;
494 pCyclicInfo->cyh_arg = NULL;
495 pCyclicInfo->cyh_level = CY_LOCK_LEVEL;
496
497 pTimeSpecs->cyt_when = RTTimeNanoTS() + 1000000000;
498 pTimeSpecs->cyt_interval = 1000000000;
499 }
500}
501
502
503/**
504 * Callback for when a CPU goes offline.
505 *
506 * Currently, we don't need to perform any tasks here.
507 *
508 * @param pvTimer Pointer to the timer.
509 * @param pCpu Pointer to the cpu.
510 * @param pvSubTimer Pointer to the sub timer. This may be NULL.
511 */
512static void rtTimerSolarisOmniOfflineCallback(void *pvTimer, cpu_t *pCpu, void *pvSubTimer)
513{
514 /*PRTTIMER pTimer = (PRTTIMER)pvTimer;*/
515 NOREF(pvTimer);
516 NOREF(pCpu);
517 NOREF(pvSubTimer);
518}
519
520
521/**
522 * Worker function used to stop the timer.
523 *
524 * This is used from within the callback functions (one-shot scenarious) and
525 * from RTTimerStop, RTTimerDestroy and RTTimerStart. We use atomic cmpxchg
526 * here to avoid some unnecessary cpu_lock contention and to avoid
527 * potential (?) deadlocks between the callback and the APIs. There is a
528 * slight chance of a race between active callbacks and the APIs, but this
529 * is preferred to a
530 *
531 * @returns true if we stopped it, false if it was already stopped.
532 * @param pTimer The timer to stop.
533 */
534static bool rtTimerSolarisStop(PRTTIMER pTimer)
535{
536 /*
537 * This is a bit problematic. I'm a bit unsure whether cyclic_remove might
538 * or may not deadlock with a callback trying to aquire the cpu_lock. So,
539 * in order to avoid this issue I'm making sure that we don't take the lock
540 * unless we know we're gonna call cyclic_remove. However, the downside of
541 * this is that it's possible races between RTTimerStart/RTTimerDestroy and
542 * currently active callbacks, which may cause iTick to have a bad value or
543 * in the worst case, memory to accessed after cleanup.
544 */
545 cyclic_id_t CyclicId = rtTimerSolarisGetCyclicId(pTimer);
546 if ( CyclicId != CYCLIC_NONE
547 && rtTimerSolarisCmpXchgCyclicId(pTimer, CYCLIC_NONE, CyclicId))
548 {
549 mutex_enter(&cpu_lock);
550 cyclic_remove(CyclicId);
551 mutex_exit(&cpu_lock);
552 return true;
553 }
554 return false;
555}
556
557
558RTDECL(uint32_t) RTTimerGetSystemGranularity(void)
559{
560 return nsec_per_tick;
561}
562
563
564RTDECL(int) RTTimerRequestSystemGranularity(uint32_t u32Request, uint32_t *pu32Granted)
565{
566 return VERR_NOT_SUPPORTED;
567}
568
569
570RTDECL(int) RTTimerReleaseSystemGranularity(uint32_t u32Granted)
571{
572 return VERR_NOT_SUPPORTED;
573}
574
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