VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 6 weeks ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.2 KB
Line 
1/* $Id: semeventwait-r0drv-solaris.h 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * IPRT - Solaris Ring-0 Driver Helpers for Event Semaphore Waits.
4 */
5
6/*
7 * Copyright (C) 2006-2024 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 u.idCo = 0; /* Silence a spurious gcc [-Wmaybe-uninitialized] warning. */
286
287 /*
288 * Arm the timeout callback.
289 *
290 * We will have to leave the mutex (spinlock) when doing this because S10
291 * (didn't check S11) will not correctly preserve PIL across calls to
292 * timeout_generic() - @bugref{5595}. We do it for all timeout methods to
293 * be on the safe side, the nice sideeffect of which is that it solves the
294 * lock inversion problem found in @bugref{5342}.
295 */
296 bool const fHasTimeout = !pWait->fIndefinite;
297 bool fGoToSleep = !fHasTimeout;
298 if (fHasTimeout)
299 {
300 pWait->fWantWakeup = false; /* only want fTimedOut */
301 ASMAtomicWritePtr(&pWait->pvMtx, pMtx); /* atomic is paranoia */
302 mutex_exit(pMtx);
303
304 if (pWait->fHighRes)
305 {
306#ifndef RTR0SEMSOLWAIT_NO_OLD_S10_FALLBACK
307 if (g_pfnrtR0Sol_timeout_generic != NULL)
308#endif
309 {
310 /*
311 * High resolution timeout - arm a high resolution timeout callback
312 * for waking up the thread at the desired time.
313 */
314 u.idCo = g_pfnrtR0Sol_timeout_generic(CALLOUT_REALTIME, rtR0SemSolWaitTimeout, pWait,
315 pWait->uNsAbsTimeout, RTR0SEMSOLWAIT_RESOLUTION,
316 CALLOUT_FLAG_ABSOLUTE);
317 }
318#ifndef RTR0SEMSOLWAIT_NO_OLD_S10_FALLBACK
319 else
320 {
321 /*
322 * High resolution timeout - arm a one-shot cyclic for waking up
323 * the thread at the desired time.
324 */
325 cyc_handler_t Cyh;
326 Cyh.cyh_arg = pWait;
327 Cyh.cyh_func = rtR0SemSolWaitHighResTimeout;
328 Cyh.cyh_level = CY_LOW_LEVEL; /// @todo try CY_LOCK_LEVEL and CY_HIGH_LEVEL?
329
330 cyc_time_t Cyt;
331 Cyt.cyt_when = pWait->uNsAbsTimeout;
332 Cyt.cyt_interval = UINT64_C(1000000000) * 60;
333
334 mutex_enter(&cpu_lock);
335 pWait->idCy = cyclic_add(&Cyh, &Cyt);
336 mutex_exit(&cpu_lock);
337 }
338#endif
339 }
340 else
341 {
342 /*
343 * Normal timeout.
344 * We're better off with our own callback like on the timeout man page,
345 * than calling cv_timedwait[_sig]().
346 */
347 u.idTom = realtime_timeout(rtR0SemSolWaitTimeout, pWait, pWait->u.lTimeout);
348 }
349
350 /*
351 * Reacquire the mutex and check if the sleep condition still holds and
352 * that we didn't already time out.
353 */
354 mutex_enter(pMtx);
355 pWait->fWantWakeup = true;
356 fGoToSleep = !ASMAtomicUoReadBool(&pWait->fTimedOut)
357 && ASMAtomicReadU32(pfState) == fCurState;
358 }
359
360 /*
361 * Do the waiting if that's still desirable.
362 * (rc > 0 - normal wake-up; rc == 0 - interruption; rc == -1 - timeout)
363 */
364 if (fGoToSleep)
365 {
366 if (pWait->fInterruptible)
367 {
368 int rc = cv_wait_sig(pCnd, pMtx);
369 if (RT_UNLIKELY(rc <= 0))
370 {
371 if (RT_LIKELY(rc == 0))
372 pWait->fInterrupted = true;
373 else
374 AssertMsgFailed(("rc=%d\n", rc)); /* no timeouts, see above! */
375 }
376 }
377 else
378 cv_wait(pCnd, pMtx);
379 }
380
381 /*
382 * Remove the timeout callback. Drop the lock while we're doing that
383 * to reduce lock contention / deadlocks. Before dropping the lock,
384 * indicate that the callback shouldn't do anything.
385 *
386 * (Too bad we are stuck with the cv_* API here, it's doing a little
387 * bit too much.)
388 */
389 if (fHasTimeout)
390 {
391 pWait->fWantWakeup = false;
392 ASMAtomicWritePtr(&pWait->pvMtx, NULL);
393 mutex_exit(pMtx);
394
395 if (pWait->fHighRes)
396 {
397#ifndef RTR0SEMSOLWAIT_NO_OLD_S10_FALLBACK
398 if (g_pfnrtR0Sol_timeout_generic != NULL)
399#endif
400 g_pfnrtR0Sol_untimeout_generic(u.idCo, 0 /*nowait*/);
401#ifndef RTR0SEMSOLWAIT_NO_OLD_S10_FALLBACK
402 else
403 {
404 mutex_enter(&cpu_lock);
405 if (pWait->idCy != CYCLIC_NONE)
406 {
407 cyclic_remove(pWait->idCy);
408 pWait->idCy = CYCLIC_NONE;
409 }
410 mutex_exit(&cpu_lock);
411 }
412#endif
413 }
414 else
415 untimeout(u.idTom);
416
417 mutex_enter(pMtx);
418 }
419}
420
421
422/**
423 * Checks if a solaris wait was interrupted.
424 *
425 * @returns true / false
426 * @param pWait The wait structure.
427 * @remarks This shall be called before the first rtR0SemSolWaitDoIt().
428 */
429DECLINLINE(bool) rtR0SemSolWaitWasInterrupted(PRTR0SEMSOLWAIT pWait)
430{
431 return pWait->fInterrupted;
432}
433
434
435/**
436 * Checks if a solaris wait has timed out.
437 *
438 * @returns true / false
439 * @param pWait The wait structure.
440 */
441DECLINLINE(bool) rtR0SemSolWaitHasTimedOut(PRTR0SEMSOLWAIT pWait)
442{
443 return pWait->fTimedOut;
444}
445
446
447/**
448 * Deletes a solaris wait.
449 *
450 * @param pWait The wait structure.
451 */
452DECLINLINE(void) rtR0SemSolWaitDelete(PRTR0SEMSOLWAIT pWait)
453{
454 pWait->pThread = NULL;
455}
456
457
458/**
459 * Enters the mutex, unpinning the underlying current thread if contended and
460 * we're on an interrupt thread.
461 *
462 * The unpinning is done to prevent a deadlock, see s this could lead to a
463 * deadlock (see @bugref{4259} for the full explanation)
464 *
465 * @param pMtx The mutex to enter.
466 */
467DECLINLINE(void) rtR0SemSolWaitEnterMutexWithUnpinningHack(kmutex_t *pMtx)
468{
469 int fAcquired = mutex_tryenter(pMtx);
470 if (!fAcquired)
471 {
472 /*
473 * Note! This assumes nobody is using the RTThreadPreemptDisable() in an
474 * interrupt context and expects it to work right. The swtch will
475 * result in a voluntary preemption. To fix this, we would have to
476 * do our own counting in RTThreadPreemptDisable/Restore() like we do
477 * on systems which doesn't do preemption (OS/2, linux, ...) and
478 * check whether preemption was disabled via RTThreadPreemptDisable()
479 * or not and only call swtch if RTThreadPreemptDisable() wasn't called.
480 */
481 kthread_t **ppIntrThread = SOL_THREAD_TINTR_PTR;
482 if ( *ppIntrThread
483 && getpil() < DISP_LEVEL)
484 {
485 RTTHREADPREEMPTSTATE PreemptState = RTTHREADPREEMPTSTATE_INITIALIZER;
486 RTThreadPreemptDisable(&PreemptState);
487 preempt();
488 RTThreadPreemptRestore(&PreemptState);
489 }
490 mutex_enter(pMtx);
491 }
492}
493
494
495/**
496 * Gets the max resolution of the timeout machinery.
497 *
498 * @returns Resolution specified in nanoseconds.
499 */
500DECLINLINE(uint32_t) rtR0SemSolWaitGetResolution(void)
501{
502 return g_pfnrtR0Sol_timeout_generic != NULL
503 ? RTR0SEMSOLWAIT_RESOLUTION
504 : cyclic_getres();
505}
506
507#endif /* !IPRT_INCLUDED_SRC_r0drv_solaris_semeventwait_r0drv_solaris_h */
508
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