VirtualBox

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

Last change on this file since 41627 was 40971, checked in by vboxsync, 13 years ago

Runtime/r0drv/solaris: remove temporary debug prints.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.7 KB
Line 
1/* $Id: timer-r0drv-solaris.c 40971 2012-04-17 17:57:29Z vboxsync $ */
2/** @file
3 * IPRT - Timer, Ring-0 Driver, Solaris.
4 */
5
6/*
7 * Copyright (C) 2006-2007 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/*******************************************************************************
29* Header Files *
30*******************************************************************************/
31#include "the-solaris-kernel.h"
32#include "internal/iprt.h"
33#include <iprt/timer.h>
34
35#include <iprt/asm.h>
36#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
37# include <iprt/asm-amd64-x86.h>
38#endif
39#include <iprt/assert.h>
40#include <iprt/err.h>
41#include <iprt/mem.h>
42#include <iprt/mp.h>
43#include <iprt/spinlock.h>
44#include <iprt/time.h>
45#include <iprt/thread.h>
46#include "internal/magics.h"
47
48#define SOL_TIMER_ANY_CPU (-1)
49
50/*******************************************************************************
51* Structures and Typedefs *
52*******************************************************************************/
53/**
54 * Single-CPU timer handle.
55 */
56typedef struct RTR0SINGLETIMERSOL
57{
58 /** Cyclic handler. */
59 cyc_handler_t hHandler;
60 /** Cyclic time and interval representation. */
61 cyc_time_t hFireTime;
62 /** Timer ticks. */
63 uint64_t u64Tick;
64} RTR0SINGLETIMERSOL;
65typedef RTR0SINGLETIMERSOL *PRTR0SINGLETIMERSOL;
66
67/**
68 * Omni-CPU timer handle.
69 */
70typedef struct RTR0OMNITIMERSOL
71{
72 /** Absolute timestamp of when the timer should fire next. */
73 uint64_t u64When;
74 /** Array of timer ticks per CPU. Reinitialized when a CPU is online'd. */
75 uint64_t *au64Ticks;
76} RTR0OMNITIMERSOL;
77typedef RTR0OMNITIMERSOL *PRTR0OMNITIMERSOL;
78
79/**
80 * The internal representation of a Solaris timer handle.
81 */
82typedef struct RTTIMER
83{
84 /** Magic.
85 * This is RTTIMER_MAGIC, but changes to something else before the timer
86 * is destroyed to indicate clearly that thread should exit. */
87 uint32_t volatile u32Magic;
88 /** Flag indicating that the timer is suspended. */
89 uint8_t volatile fSuspended;
90 /** Whether the timer must run on all CPUs or not. */
91 uint8_t fAllCpu;
92 /** Whether the timer must run on a specific CPU or not. */
93 uint8_t fSpecificCpu;
94 /** The CPU it must run on if fSpecificCpu is set. */
95 uint8_t iCpu;
96 /** The nano second interval for repeating timers. */
97 uint64_t interval;
98 /** Cyclic timer Id. */
99 cyclic_id_t hCyclicId;
100 /** @todo Make this a union unless we intend to support omni<=>single timers
101 * conversions. */
102 /** Single-CPU timer handle. */
103 PRTR0SINGLETIMERSOL pSingleTimer;
104 /** Omni-CPU timer handle. */
105 PRTR0OMNITIMERSOL pOmniTimer;
106 /** The user callback. */
107 PFNRTTIMER pfnTimer;
108 /** The argument for the user callback. */
109 void *pvUser;
110} RTTIMER;
111
112
113/*******************************************************************************
114* Defined Constants And Macros *
115*******************************************************************************/
116/** Validates that the timer is valid. */
117#define RTTIMER_ASSERT_VALID_RET(pTimer) \
118 do \
119 { \
120 AssertPtrReturn(pTimer, VERR_INVALID_HANDLE); \
121 AssertMsgReturn((pTimer)->u32Magic == RTTIMER_MAGIC, ("pTimer=%p u32Magic=%x expected %x\n", (pTimer), (pTimer)->u32Magic, RTTIMER_MAGIC), \
122 VERR_INVALID_HANDLE); \
123 } while (0)
124
125
126/**
127 * Callback wrapper for Omni-CPU and single-CPU timers.
128 *
129 * @param pvArg Opaque pointer to the timer.
130 *
131 * @remarks This will be executed in interrupt context but only at the specified
132 * level i.e. CY_LOCK_LEVEL in our case. We -CANNOT- call into the
133 * cyclic subsystem here, neither should pfnTimer().
134 */
135static void rtTimerSolCallbackWrapper(void *pvArg)
136{
137 PRTTIMER pTimer = (PRTTIMER)pvArg;
138 AssertPtrReturnVoid(pTimer);
139
140 if (pTimer->pSingleTimer)
141 {
142 uint64_t u64Tick = ++pTimer->pSingleTimer->u64Tick;
143 pTimer->pfnTimer(pTimer, pTimer->pvUser, u64Tick);
144 }
145 else if (pTimer->pOmniTimer)
146 {
147 uint64_t u64Tick = ++pTimer->pOmniTimer->au64Ticks[CPU->cpu_id];
148 pTimer->pfnTimer(pTimer, pTimer->pvUser, u64Tick);
149 }
150}
151
152
153/**
154 * Omni-CPU cyclic online event. This is called before the omni cycle begins to
155 * fire on the specified CPU.
156 *
157 * @param pvArg Opaque pointer to the timer.
158 * @param pCpu Pointer to the CPU on which it will fire.
159 * @param pCyclicHandler Pointer to a cyclic handler to add to the CPU
160 * specified in @a pCpu.
161 * @param pCyclicTime Pointer to the cyclic time and interval object.
162 *
163 * @remarks We -CANNOT- call back into the cyclic subsystem here, we can however
164 * block (sleep).
165 */
166static void rtTimerSolOmniCpuOnline(void *pvArg, cpu_t *pCpu, cyc_handler_t *pCyclicHandler, cyc_time_t *pCyclicTime)
167{
168 PRTTIMER pTimer = (PRTTIMER)pvArg;
169 AssertPtrReturnVoid(pTimer);
170 AssertPtrReturnVoid(pCpu);
171 AssertPtrReturnVoid(pCyclicHandler);
172 AssertPtrReturnVoid(pCyclicTime);
173
174 pTimer->pOmniTimer->au64Ticks[pCpu->cpu_id] = 0;
175 pCyclicHandler->cyh_func = rtTimerSolCallbackWrapper;
176 pCyclicHandler->cyh_arg = pTimer;
177 pCyclicHandler->cyh_level = CY_LOCK_LEVEL;
178
179 uint64_t u64Now = RTTimeNanoTS();
180 if (pTimer->pOmniTimer->u64When < u64Now)
181 pCyclicTime->cyt_when = u64Now + pTimer->interval / 2;
182 else
183 pCyclicTime->cyt_when = pTimer->pOmniTimer->u64When;
184
185 pCyclicTime->cyt_interval = pTimer->interval;
186}
187
188
189RTDECL(int) RTTimerCreateEx(PRTTIMER *ppTimer, uint64_t u64NanoInterval, uint32_t fFlags, PFNRTTIMER pfnTimer, void *pvUser)
190{
191 RT_ASSERT_PREEMPTIBLE();
192 *ppTimer = NULL;
193
194 /*
195 * Validate flags.
196 */
197 if (!RTTIMER_FLAGS_ARE_VALID(fFlags))
198 return VERR_INVALID_PARAMETER;
199
200 if ( (fFlags & RTTIMER_FLAGS_CPU_SPECIFIC)
201 && (fFlags & RTTIMER_FLAGS_CPU_ALL) != RTTIMER_FLAGS_CPU_ALL
202 && !RTMpIsCpuPossible(RTMpCpuIdFromSetIndex(fFlags & RTTIMER_FLAGS_CPU_MASK)))
203 return VERR_CPU_NOT_FOUND;
204
205 if ((fFlags & RTTIMER_FLAGS_CPU_ALL) == RTTIMER_FLAGS_CPU_ALL && u64NanoInterval == 0)
206 return VERR_NOT_SUPPORTED;
207
208 /*
209 * Allocate and initialize the timer handle.
210 */
211 PRTTIMER pTimer = (PRTTIMER)RTMemAlloc(sizeof(*pTimer));
212 if (!pTimer)
213 return VERR_NO_MEMORY;
214
215 pTimer->u32Magic = RTTIMER_MAGIC;
216 pTimer->fSuspended = true;
217 if ((fFlags & RTTIMER_FLAGS_CPU_ALL) == RTTIMER_FLAGS_CPU_ALL)
218 {
219 pTimer->fAllCpu = true;
220 pTimer->fSpecificCpu = false;
221 pTimer->iCpu = 255;
222 }
223 else if (fFlags & RTTIMER_FLAGS_CPU_SPECIFIC)
224 {
225 pTimer->fAllCpu = false;
226 pTimer->fSpecificCpu = true;
227 pTimer->iCpu = fFlags & RTTIMER_FLAGS_CPU_MASK; /* ASSUMES: index == cpuid */
228 }
229 else
230 {
231 pTimer->fAllCpu = false;
232 pTimer->fSpecificCpu = false;
233 pTimer->iCpu = 255;
234 }
235 pTimer->interval = u64NanoInterval;
236 pTimer->pfnTimer = pfnTimer;
237 pTimer->pvUser = pvUser;
238 pTimer->pSingleTimer = NULL;
239 pTimer->pOmniTimer = NULL;
240 pTimer->hCyclicId = CYCLIC_NONE;
241
242 *ppTimer = pTimer;
243 return VINF_SUCCESS;
244}
245
246
247RTDECL(int) RTTimerDestroy(PRTTIMER pTimer)
248{
249 if (pTimer == NULL)
250 return VINF_SUCCESS;
251 RTTIMER_ASSERT_VALID_RET(pTimer);
252 RT_ASSERT_INTS_ON();
253
254 /*
255 * Free the associated resources.
256 */
257 RTTimerStop(pTimer);
258 ASMAtomicWriteU32(&pTimer->u32Magic, ~RTTIMER_MAGIC);
259 RTMemFree(pTimer);
260 return VINF_SUCCESS;
261}
262
263
264RTDECL(int) RTTimerStart(PRTTIMER pTimer, uint64_t u64First)
265{
266 RTTIMER_ASSERT_VALID_RET(pTimer);
267 RT_ASSERT_INTS_ON();
268
269 if (!pTimer->fSuspended)
270 return VERR_TIMER_ACTIVE;
271
272 /* One-shot timers are not supported by the cyclic system. */
273 if (pTimer->interval == 0)
274 return VERR_NOT_SUPPORTED;
275
276 pTimer->fSuspended = false;
277 if (pTimer->fAllCpu)
278 {
279 PRTR0OMNITIMERSOL pOmniTimer = RTMemAllocZ(sizeof(RTR0OMNITIMERSOL));
280 if (RT_UNLIKELY(!pOmniTimer))
281 return VERR_NO_MEMORY;
282
283 pOmniTimer->au64Ticks = RTMemAllocZ(RTMpGetCount() * sizeof(uint64_t));
284 if (RT_UNLIKELY(!pOmniTimer->au64Ticks))
285 {
286 RTMemFree(pOmniTimer);
287 return VERR_NO_MEMORY;
288 }
289
290 /*
291 * Setup omni (all CPU) timer. The Omni-CPU online event will fire
292 * and from there we setup periodic timers per CPU.
293 */
294 pTimer->pOmniTimer = pOmniTimer;
295 pOmniTimer->u64When = pTimer->interval + RTTimeNanoTS();
296
297 cyc_omni_handler_t hOmni;
298 hOmni.cyo_online = rtTimerSolOmniCpuOnline;
299 hOmni.cyo_offline = NULL;
300 hOmni.cyo_arg = pTimer;
301
302 mutex_enter(&cpu_lock);
303 pTimer->hCyclicId = cyclic_add_omni(&hOmni);
304 mutex_exit(&cpu_lock);
305 }
306 else
307 {
308 int iCpu = SOL_TIMER_ANY_CPU;
309 if (pTimer->fSpecificCpu)
310 {
311 iCpu = pTimer->iCpu;
312 if (!RTMpIsCpuOnline(iCpu)) /* ASSUMES: index == cpuid */
313 return VERR_CPU_OFFLINE;
314 }
315
316 PRTR0SINGLETIMERSOL pSingleTimer = RTMemAllocZ(sizeof(RTR0SINGLETIMERSOL));
317 if (RT_UNLIKELY(!pSingleTimer))
318 return VERR_NO_MEMORY;
319
320 pTimer->pSingleTimer = pSingleTimer;
321 pSingleTimer->hHandler.cyh_func = rtTimerSolCallbackWrapper;
322 pSingleTimer->hHandler.cyh_arg = pTimer;
323 pSingleTimer->hHandler.cyh_level = CY_LOCK_LEVEL;
324
325 mutex_enter(&cpu_lock);
326 if (iCpu != SOL_TIMER_ANY_CPU && !cpu_is_online(cpu[iCpu]))
327 {
328 mutex_exit(&cpu_lock);
329 RTMemFree(pSingleTimer);
330 pTimer->pSingleTimer = NULL;
331 return VERR_CPU_OFFLINE;
332 }
333
334 pSingleTimer->hFireTime.cyt_when = u64First + RTTimeNanoTS();
335 if (pTimer->interval == 0)
336 {
337 /* @todo use gethrtime_max instead of LLONG_MAX? */
338 AssertCompileSize(pSingleTimer->hFireTime.cyt_interval, sizeof(long long));
339 pSingleTimer->hFireTime.cyt_interval = LLONG_MAX - pSingleTimer->hFireTime.cyt_when;
340 }
341 else
342 pSingleTimer->hFireTime.cyt_interval = pTimer->interval;
343
344 pTimer->hCyclicId = cyclic_add(&pSingleTimer->hHandler, &pSingleTimer->hFireTime);
345 if (iCpu != SOL_TIMER_ANY_CPU)
346 cyclic_bind(pTimer->hCyclicId, cpu[iCpu], NULL /* cpupart */);
347
348 mutex_exit(&cpu_lock);
349 }
350
351 return VINF_SUCCESS;
352}
353
354
355RTDECL(int) RTTimerStop(PRTTIMER pTimer)
356{
357 RTTIMER_ASSERT_VALID_RET(pTimer);
358 RT_ASSERT_INTS_ON();
359
360 if (pTimer->fSuspended)
361 return VERR_TIMER_SUSPENDED;
362
363 pTimer->fSuspended = true;
364 if (pTimer->pSingleTimer)
365 {
366 mutex_enter(&cpu_lock);
367 cyclic_remove(pTimer->hCyclicId);
368 mutex_exit(&cpu_lock);
369 RTMemFree(pTimer->pSingleTimer);
370 }
371 else if (pTimer->pOmniTimer)
372 {
373 mutex_enter(&cpu_lock);
374 cyclic_remove(pTimer->hCyclicId);
375 mutex_exit(&cpu_lock);
376 RTMemFree(pTimer->pOmniTimer->au64Ticks);
377 RTMemFree(pTimer->pOmniTimer);
378 }
379
380 return VINF_SUCCESS;
381}
382
383
384RTDECL(int) RTTimerChangeInterval(PRTTIMER pTimer, uint64_t u64NanoInterval)
385{
386 RTTIMER_ASSERT_VALID_RET(pTimer);
387
388 /** @todo implement me! */
389
390 return VERR_NOT_SUPPORTED;
391}
392
393
394RTDECL(uint32_t) RTTimerGetSystemGranularity(void)
395{
396 return nsec_per_tick;
397}
398
399
400RTDECL(int) RTTimerRequestSystemGranularity(uint32_t u32Request, uint32_t *pu32Granted)
401{
402 return VERR_NOT_SUPPORTED;
403}
404
405
406RTDECL(int) RTTimerReleaseSystemGranularity(uint32_t u32Granted)
407{
408 return VERR_NOT_SUPPORTED;
409}
410
411
412RTDECL(bool) RTTimerCanDoHighResolution(void)
413{
414 /** @todo return true; - when missing bits have been implemented and tested*/
415 return false;
416}
417
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