VirtualBox

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

Last change on this file since 97896 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

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