VirtualBox

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

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

iprt/r0drv/solaris: context assertions (RT_MORE_STRICT).

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