VirtualBox

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

Last change on this file since 85624 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

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