VirtualBox

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

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

Runtime/r0drv/solaris: Fix for PIL changes while spinning across calls to timeout_generic in RTSemEventWait/RTSemEventMultiWait.

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