VirtualBox

source: vbox/trunk/src/VBox/Runtime/r0drv/solaris/semeventwait-r0drv-solaris.h@ 54695

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

Runtime/r0drv/solaris: temporary try.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.9 KB
Line 
1/* $Id: semeventwait-r0drv-solaris.h 54695 2015-03-09 12:54:07Z vboxsync $ */
2/** @file
3 * IPRT - Solaris Ring-0 Driver Helpers for Event Semaphore Waits.
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#ifndef ___r0drv_solaris_semeventwait_r0drv_solaris_h
29#define ___r0drv_solaris_semeventwait_r0drv_solaris_h
30
31#include "the-solaris-kernel.h"
32
33#include <iprt/err.h>
34#include <iprt/string.h>
35#include <iprt/time.h>
36
37
38/** The resolution (nanoseconds) specified when using timeout_generic. */
39#define RTR0SEMSOLWAIT_RESOLUTION 50000
40
41/** Disables the cyclic fallback code for old S10 installs - see @bugref{5342}.
42 * @todo Fixed by @bugref{5595}, can be reenabled after checking out
43 * CY_HIGH_LEVEL. */
44#define RTR0SEMSOLWAIT_NO_OLD_S10_FALLBACK
45
46#define SOL_THREAD_TINTR_PTR ((kthread_t **)((char *)curthread + g_offrtSolThreadIntrThread))
47
48
49/**
50 * Solaris semaphore wait structure.
51 */
52typedef struct RTR0SEMSOLWAIT
53{
54 /** The absolute timeout given as nanoseconds since the start of the
55 * monotonic clock. */
56 uint64_t uNsAbsTimeout;
57 /** The timeout in nanoseconds relative to the start of the wait. */
58 uint64_t cNsRelTimeout;
59 /** The native timeout value. */
60 union
61 {
62 /** The timeout (in ticks) when fHighRes is false. */
63 clock_t lTimeout;
64 } u;
65 /** Set if we use high resolution timeouts. */
66 bool fHighRes;
67 /** Set if it's an indefinite wait. */
68 bool fIndefinite;
69 /** Set if the waiting thread is ready to be woken up.
70 * Avoids false setrun() calls due to temporary mutex exits. */
71 bool volatile fWantWakeup;
72 /** Set if we've already timed out.
73 * Set by rtR0SemSolWaitDoIt or rtR0SemSolWaitHighResTimeout, read by
74 * rtR0SemSolWaitHasTimedOut. */
75 bool volatile fTimedOut;
76 /** Whether the wait was interrupted. */
77 bool fInterrupted;
78 /** Interruptible or uninterruptible wait. */
79 bool fInterruptible;
80 /** The thread to wake up. */
81 kthread_t *pThread;
82#ifndef RTR0SEMSOLWAIT_NO_OLD_S10_FALLBACK
83 /** Cylic timer ID (used by the timeout callback). */
84 cyclic_id_t idCy;
85#endif
86 /** The mutex associated with the condition variable wait. */
87 void volatile *pvMtx;
88} RTR0SEMSOLWAIT;
89/** Pointer to a solaris semaphore wait structure. */
90typedef RTR0SEMSOLWAIT *PRTR0SEMSOLWAIT;
91
92
93/**
94 * Initializes a wait.
95 *
96 * The caller MUST check the wait condition BEFORE calling this function or the
97 * timeout logic will be flawed.
98 *
99 * @returns VINF_SUCCESS or VERR_TIMEOUT.
100 * @param pWait The wait structure.
101 * @param fFlags The wait flags.
102 * @param uTimeout The timeout.
103 * @param pWaitQueue The wait queue head.
104 */
105DECLINLINE(int) rtR0SemSolWaitInit(PRTR0SEMSOLWAIT pWait, uint32_t fFlags, uint64_t uTimeout)
106{
107 /*
108 * Process the flags and timeout.
109 */
110 if (!(fFlags & RTSEMWAIT_FLAGS_INDEFINITE))
111 {
112 if (fFlags & RTSEMWAIT_FLAGS_MILLISECS)
113 uTimeout = uTimeout < UINT64_MAX / RT_NS_1MS
114 ? uTimeout * RT_NS_1MS
115 : UINT64_MAX;
116 if (uTimeout == UINT64_MAX)
117 fFlags |= RTSEMWAIT_FLAGS_INDEFINITE;
118 else
119 {
120 uint64_t u64Now;
121 if (fFlags & RTSEMWAIT_FLAGS_RELATIVE)
122 {
123 if (uTimeout == 0)
124 return VERR_TIMEOUT;
125
126 u64Now = RTTimeSystemNanoTS();
127 pWait->cNsRelTimeout = uTimeout;
128 pWait->uNsAbsTimeout = u64Now + uTimeout;
129 if (pWait->uNsAbsTimeout < u64Now) /* overflow */
130 fFlags |= RTSEMWAIT_FLAGS_INDEFINITE;
131 }
132 else
133 {
134 u64Now = RTTimeSystemNanoTS();
135 if (u64Now >= uTimeout)
136 return VERR_TIMEOUT;
137
138 pWait->cNsRelTimeout = uTimeout - u64Now;
139 pWait->uNsAbsTimeout = uTimeout;
140 }
141 }
142 }
143
144 if (!(fFlags & RTSEMWAIT_FLAGS_INDEFINITE))
145 {
146 pWait->fIndefinite = false;
147 if ( ( (fFlags & (RTSEMWAIT_FLAGS_NANOSECS | RTSEMWAIT_FLAGS_ABSOLUTE))
148 || pWait->cNsRelTimeout < UINT32_C(1000000000) / 100 /*Hz*/ * 4)
149#ifdef RTR0SEMSOLWAIT_NO_OLD_S10_FALLBACK
150 && g_pfnrtR0Sol_timeout_generic != NULL
151#endif
152 )
153 pWait->fHighRes = true;
154 else
155 {
156 uint64_t cTicks = NSEC_TO_TICK_ROUNDUP(uTimeout);
157 if (cTicks >= LONG_MAX)
158 fFlags |= RTSEMWAIT_FLAGS_INDEFINITE;
159 else
160 {
161 pWait->u.lTimeout = cTicks;
162 pWait->fHighRes = false;
163 }
164 }
165 }
166
167 if (fFlags & RTSEMWAIT_FLAGS_INDEFINITE)
168 {
169 pWait->fIndefinite = true;
170 pWait->fHighRes = false;
171 pWait->uNsAbsTimeout = UINT64_MAX;
172 pWait->cNsRelTimeout = UINT64_MAX;
173 pWait->u.lTimeout = LONG_MAX;
174 }
175
176 pWait->fWantWakeup = false;
177 pWait->fTimedOut = false;
178 pWait->fInterrupted = false;
179 pWait->fInterruptible = !!(fFlags & RTSEMWAIT_FLAGS_INTERRUPTIBLE);
180 pWait->pThread = curthread;
181 pWait->pvMtx = NULL;
182#ifndef RTR0SEMSOLWAIT_NO_OLD_S10_FALLBACK
183 pWait->idCy = CYCLIC_NONE;
184#endif
185
186 return VINF_SUCCESS;
187}
188
189
190#ifndef RTR0SEMSOLWAIT_NO_OLD_S10_FALLBACK
191/**
192 * Cyclic timeout callback that sets the timeout indicator and wakes up the
193 * waiting thread.
194 *
195 * @param pvUser The wait structure.
196 */
197static void rtR0SemSolWaitHighResTimeout(void *pvUser)
198{
199 PRTR0SEMSOLWAIT pWait = (PRTR0SEMSOLWAIT)pvUser;
200 kthread_t *pThread = pWait->pThread;
201 kmutex_t *pMtx = (kmutex_t *)ASMAtomicReadPtr(&pWait->pvMtx);
202 if (VALID_PTR(pMtx))
203 {
204 /* Enter the mutex here to make sure the thread has gone to sleep
205 before we wake it up.
206 Note: Trying to take the cpu_lock here doesn't work. */
207 mutex_enter(pMtx);
208 if (mutex_owner(&cpu_lock) == curthread)
209 {
210 cyclic_remove(pWait->idCy);
211 pWait->idCy = CYCLIC_NONE;
212 }
213 bool const fWantWakeup = pWait->fWantWakeup;
214 ASMAtomicWriteBool(&pWait->fTimedOut, true);
215 mutex_exit(pMtx);
216
217 if (fWantWakeup)
218 setrun(pThread);
219 }
220}
221#endif
222
223
224/**
225 * Timeout callback that sets the timeout indicator and wakes up the waiting
226 * thread.
227 *
228 * @param pvUser The wait structure.
229 */
230static void rtR0SemSolWaitTimeout(void *pvUser)
231{
232 PRTR0SEMSOLWAIT pWait = (PRTR0SEMSOLWAIT)pvUser;
233 kthread_t *pThread = pWait->pThread;
234 kmutex_t *pMtx = (kmutex_t *)ASMAtomicReadPtr((void * volatile *)&pWait->pvMtx);
235 if (VALID_PTR(pMtx))
236 {
237 /* Enter the mutex here to make sure the thread has gone to sleep
238 before we wake it up. */
239 mutex_enter(pMtx);
240 bool const fWantWakeup = pWait->fWantWakeup;
241 ASMAtomicWriteBool(&pWait->fTimedOut, true);
242 mutex_exit(pMtx);
243
244 if (fWantWakeup)
245 setrun(pThread);
246 }
247}
248
249
250/**
251 * Do the actual wait.
252 *
253 * @param pWait The wait structure.
254 * @param pCnd The condition variable to wait on.
255 * @param pMtx The mutex related to the condition variable.
256 * The caller has entered this.
257 * @param pfState The state variable to check if have changed
258 * after leaving the mutex (spinlock).
259 * @param fCurState The current value of @a pfState. We'll return
260 * without sleeping if @a pfState doesn't hold
261 * this value after reacquiring the mutex.
262 *
263 * @remarks This must be call with the object mutex (spinlock) held.
264 */
265DECLINLINE(void) rtR0SemSolWaitDoIt(PRTR0SEMSOLWAIT pWait, kcondvar_t *pCnd, kmutex_t *pMtx,
266 uint32_t volatile *pfState, uint32_t const fCurState)
267{
268 union
269 {
270 callout_id_t idCo;
271 timeout_id_t idTom;
272 } u;
273
274 /*
275 * Arm the timeout callback.
276 *
277 * We will have to leave the mutex (spinlock) when doing this because S10
278 * (didn't check S11) will not correctly preserve PIL across calls to
279 * timeout_generic() - @bugref{5595}. We do it for all timeout methods to
280 * be on the safe side, the nice sideeffect of which is that it solves the
281 * lock inversion problem found in @bugref{5342}.
282 */
283 bool const fHasTimeout = !pWait->fIndefinite;
284 bool fGoToSleep = !fHasTimeout;
285 if (fHasTimeout)
286 {
287 pWait->fWantWakeup = false; /* only want fTimedOut */
288 ASMAtomicWritePtr(&pWait->pvMtx, pMtx); /* atomic is paranoia */
289 mutex_exit(pMtx);
290
291 if (pWait->fHighRes)
292 {
293#ifndef RTR0SEMSOLWAIT_NO_OLD_S10_FALLBACK
294 if (g_pfnrtR0Sol_timeout_generic != NULL)
295#endif
296 {
297 /*
298 * High resolution timeout - arm a high resolution timeout callback
299 * for waking up the thread at the desired time.
300 */
301 u.idCo = g_pfnrtR0Sol_timeout_generic(CALLOUT_REALTIME, rtR0SemSolWaitTimeout, pWait,
302 pWait->uNsAbsTimeout, RTR0SEMSOLWAIT_RESOLUTION,
303 CALLOUT_FLAG_ABSOLUTE);
304 }
305#ifndef RTR0SEMSOLWAIT_NO_OLD_S10_FALLBACK
306 else
307 {
308 /*
309 * High resolution timeout - arm a one-shot cyclic for waking up
310 * the thread at the desired time.
311 */
312 cyc_handler_t Cyh;
313 Cyh.cyh_arg = pWait;
314 Cyh.cyh_func = rtR0SemSolWaitHighResTimeout;
315 Cyh.cyh_level = CY_LOW_LEVEL; /// @todo try CY_LOCK_LEVEL and CY_HIGH_LEVEL?
316
317 cyc_time_t Cyt;
318 Cyt.cyt_when = pWait->uNsAbsTimeout;
319 Cyt.cyt_interval = UINT64_C(1000000000) * 60;
320
321 mutex_enter(&cpu_lock);
322 pWait->idCy = cyclic_add(&Cyh, &Cyt);
323 mutex_exit(&cpu_lock);
324 }
325#endif
326 }
327 else
328 {
329 /*
330 * Normal timeout.
331 * We're better off with our own callback like on the timeout man page,
332 * than calling cv_timedwait[_sig]().
333 */
334 u.idTom = realtime_timeout(rtR0SemSolWaitTimeout, pWait, pWait->u.lTimeout);
335 }
336
337 /*
338 * Reacquire the mutex and check if the sleep condition still holds and
339 * that we didn't already time out.
340 */
341 mutex_enter(pMtx);
342 pWait->fWantWakeup = true;
343 fGoToSleep = !ASMAtomicUoReadBool(&pWait->fTimedOut)
344 && ASMAtomicReadU32(pfState) == fCurState;
345 }
346
347 /*
348 * Do the waiting if that's still desirable.
349 * (rc > 0 - normal wake-up; rc == 0 - interruption; rc == -1 - timeout)
350 */
351 if (fGoToSleep)
352 {
353 if (pWait->fInterruptible)
354 {
355 int rc = cv_wait_sig(pCnd, pMtx);
356 if (RT_UNLIKELY(rc <= 0))
357 {
358 if (RT_LIKELY(rc == 0))
359 pWait->fInterrupted = true;
360 else
361 AssertMsgFailed(("rc=%d\n", rc)); /* no timeouts, see above! */
362 }
363 }
364 else
365 cv_wait(pCnd, pMtx);
366 }
367
368 /*
369 * Remove the timeout callback. Drop the lock while we're doing that
370 * to reduce lock contention / deadlocks. Before dropping the lock,
371 * indicate that the callback shouldn't do anything.
372 *
373 * (Too bad we are stuck with the cv_* API here, it's doing a little
374 * bit too much.)
375 */
376 if (fHasTimeout)
377 {
378 pWait->fWantWakeup = false;
379 ASMAtomicWritePtr(&pWait->pvMtx, NULL);
380 mutex_exit(pMtx);
381
382 if (pWait->fHighRes)
383 {
384#ifndef RTR0SEMSOLWAIT_NO_OLD_S10_FALLBACK
385 if (g_pfnrtR0Sol_timeout_generic != NULL)
386#endif
387 g_pfnrtR0Sol_untimeout_generic(u.idCo, 0 /*nowait*/);
388#ifndef RTR0SEMSOLWAIT_NO_OLD_S10_FALLBACK
389 else
390 {
391 mutex_enter(&cpu_lock);
392 if (pWait->idCy != CYCLIC_NONE)
393 {
394 cyclic_remove(pWait->idCy);
395 pWait->idCy = CYCLIC_NONE;
396 }
397 mutex_exit(&cpu_lock);
398 }
399#endif
400 }
401 else
402 untimeout(u.idTom);
403
404 mutex_enter(pMtx);
405 }
406}
407
408
409/**
410 * Checks if a solaris wait was interrupted.
411 *
412 * @returns true / false
413 * @param pWait The wait structure.
414 * @remarks This shall be called before the first rtR0SemSolWaitDoIt().
415 */
416DECLINLINE(bool) rtR0SemSolWaitWasInterrupted(PRTR0SEMSOLWAIT pWait)
417{
418 return pWait->fInterrupted;
419}
420
421
422/**
423 * Checks if a solaris wait has timed out.
424 *
425 * @returns true / false
426 * @param pWait The wait structure.
427 */
428DECLINLINE(bool) rtR0SemSolWaitHasTimedOut(PRTR0SEMSOLWAIT pWait)
429{
430 return pWait->fTimedOut;
431}
432
433
434/**
435 * Deletes a solaris wait.
436 *
437 * @param pWait The wait structure.
438 */
439DECLINLINE(void) rtR0SemSolWaitDelete(PRTR0SEMSOLWAIT pWait)
440{
441 pWait->pThread = NULL;
442}
443
444
445/**
446 * Enters the mutex, unpinning the underlying current thread if contended and
447 * we're on an interrupt thread.
448 *
449 * The unpinning is done to prevent a deadlock, see s this could lead to a
450 * deadlock (see @bugref{4259} for the full explanation)
451 *
452 * @param pMtx The mutex to enter.
453 */
454DECLINLINE(void) rtR0SemSolWaitEnterMutexWithUnpinningHack(kmutex_t *pMtx)
455{
456 int fAcquired = mutex_tryenter(pMtx);
457 if (!fAcquired)
458 {
459 /*
460 * Note! This assumes nobody is using the RTThreadPreemptDisable() in an
461 * interrupt context and expects it to work right. The swtch will
462 * result in a voluntary preemption. To fix this, we would have to
463 * do our own counting in RTThreadPreemptDisable/Restore() like we do
464 * on systems which doesn't do preemption (OS/2, linux, ...) and
465 * check whether preemption was disabled via RTThreadPreemptDisable()
466 * or not and only call swtch if RTThreadPreemptDisable() wasn't called.
467 */
468#if 0 /* Temporarily disabled, for @bugref{7726}. */
469 kthread_t **ppIntrThread = SOL_THREAD_TINTR_PTR;
470 if ( *ppIntrThread
471 && getpil() < DISP_LEVEL)
472#else
473 if ( curthread->t_intr
474 && getpil() < DISP_LEVEL)
475#endif
476 {
477 RTTHREADPREEMPTSTATE PreemptState = RTTHREADPREEMPTSTATE_INITIALIZER;
478 RTThreadPreemptDisable(&PreemptState);
479 preempt();
480 RTThreadPreemptRestore(&PreemptState);
481 }
482 mutex_enter(pMtx);
483 }
484}
485
486
487/**
488 * Gets the max resolution of the timeout machinery.
489 *
490 * @returns Resolution specified in nanoseconds.
491 */
492DECLINLINE(uint32_t) rtR0SemSolWaitGetResolution(void)
493{
494 return g_pfnrtR0Sol_timeout_generic != NULL
495 ? RTR0SEMSOLWAIT_RESOLUTION
496 : cyclic_getres();
497}
498
499#endif /* ___r0drv_solaris_semeventwait_r0drv_solaris_h */
500
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