VirtualBox

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

Last change on this file since 68198 was 62477, checked in by vboxsync, 8 years ago

(C) 2016

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