VirtualBox

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

Last change on this file since 53403 was 53403, checked in by vboxsync, 10 years ago

Runtime/r0drv/solaris: Revert r96951, reprogramming the cyclic doesn't quite work the way it ought it.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.7 KB
Line 
1/* $Id: timer-r0drv-solaris.c 53403 2014-11-26 15:34:33Z vboxsync $ */
2/** @file
3 * IPRT - Timer, Ring-0 Driver, Solaris.
4 */
5
6/*
7 * Copyright (C) 2006-2014 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 specific timers if they happened to have been fired on
128 * the wrong CPU. See rtTimerSolCallbackWrapper().
129 *
130 * @param idCpu The CPU this is fired on.
131 * @param pvUser1 Opaque pointer to the timer.
132 * @param pvUser2 Not used, NULL.
133 */
134static void rtTimerSolMpCallbackWrapper(RTCPUID idCpu, void *pvUser1, void *pvUser2)
135{
136 PRTTIMER pTimer = (PRTTIMER)pvUser1;
137 AssertPtrReturnVoid(pTimer);
138 Assert(!RTThreadPreemptIsEnabled(NIL_RTTHREAD));
139 Assert(pTimer->iCpu == RTMpCpuId()); /* ASSUMES: index == cpuid */
140 Assert(pTimer->pSingleTimer);
141 NOREF(pvUser2);
142
143 /* Make sure one-shots do not fire another time. */
144 Assert( !pTimer->fSuspended
145 || pTimer->interval != 0);
146
147 /* For one-shot specific timers, allow RTTimer to restart them. */
148 if (pTimer->interval == 0)
149 pTimer->fSuspended = true;
150
151 uint64_t u64Tick = ++pTimer->pSingleTimer->u64Tick;
152 pTimer->pfnTimer(pTimer, pTimer->pvUser, u64Tick);
153}
154
155
156/**
157 * Callback wrapper for Omni-CPU and single-CPU timers.
158 *
159 * @param pvArg Opaque pointer to the timer.
160 *
161 * @remarks This will be executed in interrupt context but only at the specified
162 * level i.e. CY_LOCK_LEVEL in our case. We -CANNOT- call into the
163 * cyclic subsystem here, neither should pfnTimer().
164 */
165static void rtTimerSolCallbackWrapper(void *pvArg)
166{
167 PRTTIMER pTimer = (PRTTIMER)pvArg;
168 AssertPtrReturnVoid(pTimer);
169 Assert(!RTThreadPreemptIsEnabled(NIL_RTTHREAD));
170
171 if (pTimer->pSingleTimer)
172 {
173 /* Make sure one-shots do not fire another time. */
174 Assert( !pTimer->fSuspended
175 || pTimer->interval != 0);
176
177 /* For specific timers, we might fire on the wrong CPU between cyclic_add() and cyclic_bind().
178 Redirect these shots to the right CPU as we are temporarily rebinding to the right CPU. */
179 if ( pTimer->fSpecificCpu
180 && pTimer->iCpu != RTMpCpuId()) /* ASSUMES: index == cpuid */
181 {
182 RTMpOnSpecific(pTimer->iCpu, rtTimerSolMpCallbackWrapper, pTimer, NULL);
183 return;
184 }
185
186 /* For one-shot any-cpu timers, allow RTTimer to restart them. */
187 if (pTimer->interval == 0)
188 pTimer->fSuspended = true;
189
190 uint64_t u64Tick = ++pTimer->pSingleTimer->u64Tick;
191 pTimer->pfnTimer(pTimer, pTimer->pvUser, u64Tick);
192 }
193 else if (pTimer->pOmniTimer)
194 {
195 uint64_t u64Tick = ++pTimer->pOmniTimer->au64Ticks[CPU->cpu_id];
196 pTimer->pfnTimer(pTimer, pTimer->pvUser, u64Tick);
197 }
198}
199
200
201/**
202 * Omni-CPU cyclic online event. This is called before the omni cycle begins to
203 * fire on the specified CPU.
204 *
205 * @param pvArg Opaque pointer to the timer.
206 * @param pCpu Pointer to the CPU on which it will fire.
207 * @param pCyclicHandler Pointer to a cyclic handler to add to the CPU
208 * specified in @a pCpu.
209 * @param pCyclicTime Pointer to the cyclic time and interval object.
210 *
211 * @remarks We -CANNOT- call back into the cyclic subsystem here, we can however
212 * block (sleep).
213 */
214static void rtTimerSolOmniCpuOnline(void *pvArg, cpu_t *pCpu, cyc_handler_t *pCyclicHandler, cyc_time_t *pCyclicTime)
215{
216 PRTTIMER pTimer = (PRTTIMER)pvArg;
217 AssertPtrReturnVoid(pTimer);
218 AssertPtrReturnVoid(pCpu);
219 AssertPtrReturnVoid(pCyclicHandler);
220 AssertPtrReturnVoid(pCyclicTime);
221
222 pTimer->pOmniTimer->au64Ticks[pCpu->cpu_id] = 0;
223 pCyclicHandler->cyh_func = rtTimerSolCallbackWrapper;
224 pCyclicHandler->cyh_arg = pTimer;
225 pCyclicHandler->cyh_level = CY_LOCK_LEVEL;
226
227 uint64_t u64Now = RTTimeSystemNanoTS();
228 if (pTimer->pOmniTimer->u64When < u64Now)
229 pCyclicTime->cyt_when = u64Now + pTimer->interval / 2;
230 else
231 pCyclicTime->cyt_when = pTimer->pOmniTimer->u64When;
232
233 pCyclicTime->cyt_interval = pTimer->interval;
234}
235
236
237RTDECL(int) RTTimerCreateEx(PRTTIMER *ppTimer, uint64_t u64NanoInterval, uint32_t fFlags, PFNRTTIMER pfnTimer, void *pvUser)
238{
239 RT_ASSERT_PREEMPTIBLE();
240 *ppTimer = NULL;
241
242 /*
243 * Validate flags.
244 */
245 if (!RTTIMER_FLAGS_ARE_VALID(fFlags))
246 return VERR_INVALID_PARAMETER;
247
248 if ( (fFlags & RTTIMER_FLAGS_CPU_SPECIFIC)
249 && (fFlags & RTTIMER_FLAGS_CPU_ALL) != RTTIMER_FLAGS_CPU_ALL
250 && !RTMpIsCpuPossible(RTMpCpuIdFromSetIndex(fFlags & RTTIMER_FLAGS_CPU_MASK)))
251 return VERR_CPU_NOT_FOUND;
252
253 /* One-shot omni timers are not supported by the cyclic system. */
254 if ( (fFlags & RTTIMER_FLAGS_CPU_ALL) == RTTIMER_FLAGS_CPU_ALL
255 && u64NanoInterval == 0)
256 {
257 return VERR_NOT_SUPPORTED;
258 }
259
260 /*
261 * Allocate and initialize the timer handle.
262 */
263 PRTTIMER pTimer = (PRTTIMER)RTMemAlloc(sizeof(*pTimer));
264 if (!pTimer)
265 return VERR_NO_MEMORY;
266
267 pTimer->u32Magic = RTTIMER_MAGIC;
268 pTimer->fSuspended = true;
269 if ((fFlags & RTTIMER_FLAGS_CPU_ALL) == RTTIMER_FLAGS_CPU_ALL)
270 {
271 pTimer->fAllCpu = true;
272 pTimer->fSpecificCpu = false;
273 pTimer->iCpu = 255;
274 }
275 else if (fFlags & RTTIMER_FLAGS_CPU_SPECIFIC)
276 {
277 pTimer->fAllCpu = false;
278 pTimer->fSpecificCpu = true;
279 pTimer->iCpu = fFlags & RTTIMER_FLAGS_CPU_MASK; /* ASSUMES: index == cpuid */
280 }
281 else
282 {
283 pTimer->fAllCpu = false;
284 pTimer->fSpecificCpu = false;
285 pTimer->iCpu = 255;
286 }
287 pTimer->interval = u64NanoInterval;
288 pTimer->pfnTimer = pfnTimer;
289 pTimer->pvUser = pvUser;
290 pTimer->pSingleTimer = NULL;
291 pTimer->pOmniTimer = NULL;
292 pTimer->hCyclicId = CYCLIC_NONE;
293
294 *ppTimer = pTimer;
295 return VINF_SUCCESS;
296}
297
298
299RTDECL(int) RTTimerDestroy(PRTTIMER pTimer)
300{
301 if (pTimer == NULL)
302 return VINF_SUCCESS;
303 RTTIMER_ASSERT_VALID_RET(pTimer);
304 RT_ASSERT_INTS_ON();
305
306 /*
307 * Free the associated resources.
308 */
309 RTTimerStop(pTimer);
310 ASMAtomicWriteU32(&pTimer->u32Magic, ~RTTIMER_MAGIC);
311 RTMemFree(pTimer);
312 return VINF_SUCCESS;
313}
314
315
316RTDECL(int) RTTimerStart(PRTTIMER pTimer, uint64_t u64First)
317{
318 RTTIMER_ASSERT_VALID_RET(pTimer);
319 RT_ASSERT_INTS_ON();
320
321 if (!pTimer->fSuspended)
322 return VERR_TIMER_ACTIVE;
323
324 pTimer->fSuspended = false;
325 if (pTimer->fAllCpu)
326 {
327 Assert(pTimer->interval);
328 PRTR0OMNITIMERSOL pOmniTimer = RTMemAllocZ(sizeof(RTR0OMNITIMERSOL));
329 if (RT_UNLIKELY(!pOmniTimer))
330 return VERR_NO_MEMORY;
331
332 pOmniTimer->au64Ticks = RTMemAllocZ(RTMpGetCount() * sizeof(uint64_t));
333 if (RT_UNLIKELY(!pOmniTimer->au64Ticks))
334 {
335 RTMemFree(pOmniTimer);
336 return VERR_NO_MEMORY;
337 }
338
339 /*
340 * Setup omni (all CPU) timer. The Omni-CPU online event will fire
341 * and from there we setup periodic timers per CPU.
342 */
343 pTimer->pOmniTimer = pOmniTimer;
344 pOmniTimer->u64When = pTimer->interval + RTTimeSystemNanoTS();
345
346 cyc_omni_handler_t hOmni;
347 hOmni.cyo_online = rtTimerSolOmniCpuOnline;
348 hOmni.cyo_offline = NULL;
349 hOmni.cyo_arg = pTimer;
350
351 mutex_enter(&cpu_lock);
352 pTimer->hCyclicId = cyclic_add_omni(&hOmni);
353 mutex_exit(&cpu_lock);
354 }
355 else
356 {
357 int iCpu = SOL_TIMER_ANY_CPU;
358 if (pTimer->fSpecificCpu)
359 {
360 iCpu = pTimer->iCpu;
361 if (!RTMpIsCpuOnline(iCpu)) /* ASSUMES: index == cpuid */
362 return VERR_CPU_OFFLINE;
363 }
364
365 PRTR0SINGLETIMERSOL pSingleTimer = RTMemAllocZ(sizeof(RTR0SINGLETIMERSOL));
366 if (RT_UNLIKELY(!pSingleTimer))
367 return VERR_NO_MEMORY;
368
369 pTimer->pSingleTimer = pSingleTimer;
370 pSingleTimer->hHandler.cyh_func = rtTimerSolCallbackWrapper;
371 pSingleTimer->hHandler.cyh_arg = pTimer;
372 pSingleTimer->hHandler.cyh_level = CY_LOCK_LEVEL;
373
374 mutex_enter(&cpu_lock);
375 if ( iCpu != SOL_TIMER_ANY_CPU
376 && !cpu_is_online(cpu[iCpu]))
377 {
378 mutex_exit(&cpu_lock);
379 RTMemFree(pSingleTimer);
380 pTimer->pSingleTimer = NULL;
381 return VERR_CPU_OFFLINE;
382 }
383
384 pSingleTimer->hFireTime.cyt_when = u64First + RTTimeSystemNanoTS();
385 if (pTimer->interval == 0)
386 {
387 /*
388 * cylic_add() comment: "The caller is responsible for assuring that cyt_when + cyt_interval <= INT64_MAX"
389 * but it contradicts itself because cyclic_reprogram() updates only the interval and accepts CY_INFINITY as
390 * a valid, special value. See cyclic_fire().
391 */
392 pSingleTimer->hFireTime.cyt_interval = CY_INFINITY;
393 }
394 else
395 pSingleTimer->hFireTime.cyt_interval = pTimer->interval;
396
397 pTimer->hCyclicId = cyclic_add(&pSingleTimer->hHandler, &pSingleTimer->hFireTime);
398 if (iCpu != SOL_TIMER_ANY_CPU)
399 cyclic_bind(pTimer->hCyclicId, cpu[iCpu], NULL /* cpupart */);
400
401 mutex_exit(&cpu_lock);
402 }
403
404 return VINF_SUCCESS;
405}
406
407
408RTDECL(int) RTTimerStop(PRTTIMER pTimer)
409{
410 RTTIMER_ASSERT_VALID_RET(pTimer);
411 RT_ASSERT_INTS_ON();
412
413 if (pTimer->fSuspended)
414 return VERR_TIMER_SUSPENDED;
415
416 /** @remarks Do -not- call this function from a timer callback,
417 * cyclic_remove() will deadlock the system. */
418 pTimer->fSuspended = true;
419 if (pTimer->pSingleTimer)
420 {
421 mutex_enter(&cpu_lock);
422 cyclic_remove(pTimer->hCyclicId);
423 mutex_exit(&cpu_lock);
424 RTMemFree(pTimer->pSingleTimer);
425 }
426 else if (pTimer->pOmniTimer)
427 {
428 mutex_enter(&cpu_lock);
429 cyclic_remove(pTimer->hCyclicId);
430 mutex_exit(&cpu_lock);
431 RTMemFree(pTimer->pOmniTimer->au64Ticks);
432 RTMemFree(pTimer->pOmniTimer);
433 }
434 return VINF_SUCCESS;
435}
436
437
438RTDECL(int) RTTimerChangeInterval(PRTTIMER pTimer, uint64_t u64NanoInterval)
439{
440 return VERR_NOT_SUPPORTED;
441}
442
443
444RTDECL(uint32_t) RTTimerGetSystemGranularity(void)
445{
446 return nsec_per_tick;
447}
448
449
450RTDECL(int) RTTimerRequestSystemGranularity(uint32_t u32Request, uint32_t *pu32Granted)
451{
452 return VERR_NOT_SUPPORTED;
453}
454
455
456RTDECL(int) RTTimerReleaseSystemGranularity(uint32_t u32Granted)
457{
458 return VERR_NOT_SUPPORTED;
459}
460
461
462RTDECL(bool) RTTimerCanDoHighResolution(void)
463{
464 /** @todo return true; - when missing bits have been implemented and tested*/
465 return false;
466}
467
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