1
/* $Id: sleepqueue-r0drv-freebsd.h 33527 2010-10-27 17:09:25Z vboxsync $ */
3
* IPRT - FreeBSD Ring-0 Driver Helpers for Abstracting Sleep Queues,
7
* Copyright (C) 2006-2010 Oracle Corporation
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.
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.
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.
28
#ifndef ___r0drv_freebsd_sleepqueue_r0drv_freebsd_h
29
#define ___r0drv_freebsd_sleepqueue_r0drv_freebsd_h
31
#include "the-freebsd-kernel.h"
33
#include <iprt/asm-math.h>
35
#include <iprt/string.h>
36
#include <iprt/time.h>
39
* Kernel mode Linux wait state structure.
41
typedef struct RTR0SEMBSDSLEEP
43
/** The absolute timeout given as nano seconds since the start of the
45
uint64_t uNsAbsTimeout;
46
/** The timeout in ticks. Updated after waiting. */
48
/** Set if it's an indefinite wait. */
50
/** Set if we've already timed out.
51
* Set by rtR0SemBsdWaitDoIt and read by rtR0SemBsdWaitHasTimedOut. */
53
/** Flag whether the wait was interrupted. */
55
/** flag whether the wait is interruptible or not. */
57
/** Opaque wait channel id. */
60
/** Pointer to a FreeBSD wait state. */
61
typedef RTR0SEMBSDSLEEP *PRTR0SEMBSDSLEEP;
65
* Updates the timeout of the FreeBSD wait.
67
* @returns RTSEMWAIT_FLAGS_INDEFINITE if the timeout value is too big.
69
* @param pWait The wait structure.
70
* @param uTimeout The relative timeout in nanoseconds.
72
DECLINLINE(uint32_t) rtR0SemBsdWaitUpdateTimeout(PRTR0SEMBSDSLEEP pWait, uint64_t uTimeout)
77
tv.tv_sec = uTimeout / UINT64_C(1000000000);
78
tv.tv_usec = (uTimeout % UINT64_C(1000000000)) / UINT64_C(1000);
80
pWait->iTimeout = tvtohz(&tv);
82
uint64_t cTicks = ASMMultU64ByU32DivByU32(uTimeout, hz, UINT32_C(1000000000));
83
if (cTicks >= INT_MAX)
84
return RTSEMWAIT_FLAGS_INDEFINITE;
86
pWait->iTimeout = (int)cTicks;
95
* The caller MUST check the wait condition BEFORE calling this function or the
96
* timeout logic will be flawed.
98
* @returns VINF_SUCCESS or VERR_TIMEOUT.
99
* @param pWait The wait structure.
100
* @param fFlags The wait flags.
101
* @param uTimeout The timeout.
102
* @param pvWaitChan The opaque wait channel.
104
DECLINLINE(int) rtR0SemBsdWaitInit(PRTR0SEMBSDSLEEP pWait, uint32_t fFlags, uint64_t uTimeout,
108
pWait->uNsAbsTimeout = 0; /* shut up gcc */
111
* Process the flags and timeout.
113
if (!(fFlags & RTSEMWAIT_FLAGS_INDEFINITE))
115
/** @todo optimize: millisecs -> nanosecs -> millisec -> jiffies */
116
if (fFlags & RTSEMWAIT_FLAGS_MILLISECS)
117
uTimeout = uTimeout < UINT64_MAX / UINT32_C(1000000) * UINT32_C(1000000)
118
? uTimeout * UINT32_C(1000000)
120
if (uTimeout == UINT64_MAX)
121
fFlags |= RTSEMWAIT_FLAGS_INDEFINITE;
125
if (fFlags & RTSEMWAIT_FLAGS_RELATIVE)
130
u64Now = RTTimeSystemNanoTS();
131
if (u64Now + uTimeout < u64Now) /* overflow */
132
fFlags |= RTSEMWAIT_FLAGS_INDEFINITE;
134
pWait->uNsAbsTimeout = u64Now + uTimeout;
138
u64Now = RTTimeSystemNanoTS();
139
if (u64Now >= uTimeout)
142
pWait->uNsAbsTimeout = uTimeout;
143
uTimeout -= u64Now; /* Get a relative value. */
148
if (!(fFlags & RTSEMWAIT_FLAGS_INDEFINITE))
150
pWait->fIndefinite = false;
151
fFlags |= rtR0SemBsdWaitUpdateTimeout(pWait, uTimeout);
154
if (fFlags & RTSEMWAIT_FLAGS_INDEFINITE)
156
pWait->fIndefinite = true;
157
pWait->iTimeout = INT_MAX;
158
pWait->uNsAbsTimeout = UINT64_MAX;
161
pWait->fTimedOut = false;
164
* Initialize the wait queue related bits.
166
pWait->fInterruptible = fFlags & RTSEMWAIT_FLAGS_INTERRUPTIBLE
168
pWait->pvWaitChan = pvWaitChan;
169
pWait->fInterrupted = false;
175
* Prepares the next wait.
177
* This must be called before rtR0SemBsdWaitDoIt, and the caller should check
178
* the exit conditions inbetween the two calls.
180
* @param pWait The wait structure.
182
DECLINLINE(void) rtR0SemBsdWaitPrepare(PRTR0SEMBSDSLEEP pWait)
184
/* Lock the queues. */
185
sleepq_lock(pWait->pvWaitChan);
189
* Do the actual wait.
191
* @param pWait The wait structure.
193
DECLINLINE(void) rtR0SemBsdWaitDoIt(PRTR0SEMBSDSLEEP pWait)
196
int fSleepqFlags = SLEEPQ_CONDVAR;
198
if (pWait->fInterruptible)
199
fSleepqFlags |= SLEEPQ_INTERRUPTIBLE;
201
sleepq_add(pWait->pvWaitChan, NULL, "IPRT Semaphore", fSleepqFlags, 0);
203
if (!pWait->fIndefinite)
205
sleepq_set_timeout(pWait->pvWaitChan, pWait->iTimeout);
207
if (pWait->fInterruptible)
208
rcBsd = SLEEPQ_TIMEDWAIT_SIG(pWait->pvWaitChan);
210
rcBsd = SLEEPQ_TIMEDWAIT(pWait->pvWaitChan);
214
if (pWait->fInterruptible)
215
rcBsd = SLEEPQ_WAIT_SIG(pWait->pvWaitChan);
219
SLEEPQ_WAIT(pWait->pvWaitChan);
229
if (!pWait->fIndefinite)
231
/* Recalc timeout. */
232
uint64_t u64Now = RTTimeSystemNanoTS();
233
if (u64Now >= pWait->uNsAbsTimeout)
234
pWait->fTimedOut = true;
237
u64Now = pWait->uNsAbsTimeout - u64Now;
238
rtR0SemBsdWaitUpdateTimeout(pWait, u64Now);
244
pWait->fTimedOut = true;
247
Assert(pWait->fInterruptible);
248
pWait->fInterrupted = true;
251
AssertMsgFailed(("sleepq_* -> %d\n", rcBsd));
258
* Checks if a FreeBSD wait was interrupted.
260
* @returns true / false
261
* @param pWait The wait structure.
262
* @remarks This shall be called before the first rtR0SemLnxWaitDoIt().
264
DECLINLINE(bool) rtR0SemBsdWaitWasInterrupted(PRTR0SEMBSDSLEEP pWait)
266
return pWait->fInterrupted;
271
* Checks if a FreeBSD wait has timed out.
273
* @returns true / false
274
* @param pWait The wait structure.
276
DECLINLINE(bool) rtR0SemBsdWaitHasTimedOut(PRTR0SEMBSDSLEEP pWait)
278
return pWait->fTimedOut;
283
* Deletes a FreeBSD wait.
285
* @param pWait The wait structure.
287
DECLINLINE(void) rtR0SemBsdWaitDelete(PRTR0SEMBSDSLEEP pWait)
289
sleepq_release(pWait->pvWaitChan);
294
* Signals the wait channel.
296
* @param pvWaitChan The opaque wait channel handle.
298
DECLINLINE(void) rtR0SemBsdSignal(void *pvWaitChan)
300
sleepq_lock(pvWaitChan);
301
int fWakeupSwapProc = sleepq_signal(pvWaitChan, SLEEPQ_CONDVAR, 0, 0);
302
sleepq_release(pvWaitChan);
308
* Wakes up all waiters on the wait channel.
310
* @param pvWaitChan The opaque wait channel handle.
312
DECLINLINE(void) rtR0SemBsdBroadcast(void *pvWaitChan)
314
sleepq_lock(pvWaitChan);
315
sleepq_broadcast(pvWaitChan, SLEEPQ_CONDVAR, 0, 0);
316
sleepq_release(pvWaitChan);
320
* Gets the max resolution of the timeout machinery.
322
* @returns Resolution specified in nanoseconds.
324
DECLINLINE(uint32_t) rtR0SemBsdWaitGetResolution(void)
326
return 1000000000 / hz; /* ns */