VirtualBox

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

Last change on this file since 40670 was 36392, checked in by vboxsync, 14 years ago

r0drv/solaris/solevent*: Must re-check the wait condition before going to sleep.

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