1 /* $Id: waitqueue-r0drv-linux.h $ */
3 * IPRT - Linux Ring-0 Driver Helpers for Abstracting Wait Queues,
7 * Copyright (C) 2006-2017 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_linux_waitqueue_r0drv_linux_h
29 #define ___r0drv_linux_waitqueue_r0drv_linux_h
31 #include "the-linux-kernel.h"
33 #include <iprt/asm-math.h>
35 #include <iprt/string.h>
36 #include <iprt/time.h>
38 /** The resolution (nanoseconds) specified when using
39 * schedule_hrtimeout_range. */
40 #define RTR0SEMLNXWAIT_RESOLUTION 50000
44 * Kernel mode Linux wait state structure.
46 typedef struct RTR0SEMLNXWAIT
48 /** The wait queue entry. */
49 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
50 wait_queue_entry_t WaitQE
;
54 /** The absolute timeout given as nano seconds since the start of the
56 uint64_t uNsAbsTimeout
;
57 /** The timeout in nano seconds relative to the start of the wait. */
58 uint64_t cNsRelTimeout
;
59 /** The native timeout value. */
62 #ifdef IPRT_LINUX_HAS_HRTIMER
63 /** The timeout when fHighRes is true. Absolute, so no updating. */
66 /** The timeout when fHighRes is false. Updated after waiting. */
69 /** Set if we use high resolution timeouts. */
71 /** Set if it's an indefinite wait. */
73 /** Set if we've already timed out.
74 * Set by rtR0SemLnxWaitDoIt and read by rtR0SemLnxWaitHasTimedOut. */
76 /** TASK_INTERRUPTIBLE or TASK_UNINTERRUPTIBLE. */
78 /** The wait queue. */
79 wait_queue_head_t
*pWaitQueue
;
81 /** Pointer to a linux wait state. */
82 typedef RTR0SEMLNXWAIT
*PRTR0SEMLNXWAIT
;
88 * The caller MUST check the wait condition BEFORE calling this function or the
89 * timeout logic will be flawed.
91 * @returns VINF_SUCCESS or VERR_TIMEOUT.
92 * @param pWait The wait structure.
93 * @param fFlags The wait flags.
94 * @param uTimeout The timeout.
95 * @param pWaitQueue The wait queue head.
97 DECLINLINE(int) rtR0SemLnxWaitInit(PRTR0SEMLNXWAIT pWait
, uint32_t fFlags
, uint64_t uTimeout
,
98 wait_queue_head_t
*pWaitQueue
)
101 * Process the flags and timeout.
103 if (!(fFlags
& RTSEMWAIT_FLAGS_INDEFINITE
))
105 /** @todo optimize: millisecs -> nanosecs -> millisec -> jiffies */
106 if (fFlags
& RTSEMWAIT_FLAGS_MILLISECS
)
107 uTimeout
= uTimeout
< UINT64_MAX
/ RT_US_1SEC
* RT_US_1SEC
108 ? uTimeout
* RT_US_1SEC
110 if (uTimeout
== UINT64_MAX
)
111 fFlags
|= RTSEMWAIT_FLAGS_INDEFINITE
;
115 if (fFlags
& RTSEMWAIT_FLAGS_RELATIVE
)
120 u64Now
= RTTimeSystemNanoTS();
121 pWait
->cNsRelTimeout
= uTimeout
;
122 pWait
->uNsAbsTimeout
= u64Now
+ uTimeout
;
123 if (pWait
->uNsAbsTimeout
< u64Now
) /* overflow */
124 fFlags
|= RTSEMWAIT_FLAGS_INDEFINITE
;
128 u64Now
= RTTimeSystemNanoTS();
129 if (u64Now
>= uTimeout
)
132 pWait
->cNsRelTimeout
= uTimeout
- u64Now
;
133 pWait
->uNsAbsTimeout
= uTimeout
;
138 if (!(fFlags
& RTSEMWAIT_FLAGS_INDEFINITE
))
140 pWait
->fIndefinite
= false;
141 #ifdef IPRT_LINUX_HAS_HRTIMER
142 if ( (fFlags
& (RTSEMWAIT_FLAGS_NANOSECS
| RTSEMWAIT_FLAGS_ABSOLUTE
))
143 || pWait
->cNsRelTimeout
< RT_NS_1SEC
/ HZ
* 4)
145 pWait
->fHighRes
= true;
146 # if BITS_PER_LONG < 64
147 if ( KTIME_SEC_MAX
<= LONG_MAX
148 && pWait
->uNsAbsTimeout
>= KTIME_SEC_MAX
* RT_NS_1SEC_64
+ (RT_NS_1SEC
- 1))
149 fFlags
|= RTSEMWAIT_FLAGS_INDEFINITE
;
152 pWait
->u
.KtTimeout
= ns_to_ktime(pWait
->uNsAbsTimeout
);
157 uint64_t cJiffies
= ASMMultU64ByU32DivByU32(pWait
->cNsRelTimeout
, HZ
, RT_NS_1SEC
);
158 if (cJiffies
>= MAX_JIFFY_OFFSET
)
159 fFlags
|= RTSEMWAIT_FLAGS_INDEFINITE
;
162 pWait
->u
.lTimeout
= (long)cJiffies
;
163 pWait
->fHighRes
= false;
168 if (fFlags
& RTSEMWAIT_FLAGS_INDEFINITE
)
170 pWait
->fIndefinite
= true;
171 pWait
->fHighRes
= false;
172 pWait
->uNsAbsTimeout
= UINT64_MAX
;
173 pWait
->cNsRelTimeout
= UINT64_MAX
;
174 pWait
->u
.lTimeout
= LONG_MAX
;
177 pWait
->fTimedOut
= false;
180 * Initialize the wait queue related bits.
182 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 39)
183 init_wait((&pWait
->WaitQE
));
185 RT_ZERO(pWait
->WaitQE
);
186 init_waitqueue_entry((&pWait
->WaitQE
), current
);
188 pWait
->pWaitQueue
= pWaitQueue
;
189 pWait
->iWaitState
= fFlags
& RTSEMWAIT_FLAGS_INTERRUPTIBLE
190 ? TASK_INTERRUPTIBLE
: TASK_UNINTERRUPTIBLE
;
197 * Prepares the next wait.
199 * This must be called before rtR0SemLnxWaitDoIt, and the caller should check
200 * the exit conditions in-between the two calls.
202 * @param pWait The wait structure.
204 DECLINLINE(void) rtR0SemLnxWaitPrepare(PRTR0SEMLNXWAIT pWait
)
206 /* Make everything thru schedule*() atomic scheduling wise. (Is this correct?) */
207 prepare_to_wait(pWait
->pWaitQueue
, &pWait
->WaitQE
, pWait
->iWaitState
);
212 * Do the actual wait.
214 * @param pWait The wait structure.
216 DECLINLINE(void) rtR0SemLnxWaitDoIt(PRTR0SEMLNXWAIT pWait
)
218 if (pWait
->fIndefinite
)
220 #ifdef IPRT_LINUX_HAS_HRTIMER
221 else if (pWait
->fHighRes
)
223 int rc
= schedule_hrtimeout_range(&pWait
->u
.KtTimeout
, HRTIMER_MODE_ABS
, RTR0SEMLNXWAIT_RESOLUTION
);
225 pWait
->fTimedOut
= true;
230 pWait
->u
.lTimeout
= schedule_timeout(pWait
->u
.lTimeout
);
231 if (pWait
->u
.lTimeout
<= 0)
232 pWait
->fTimedOut
= true;
234 after_wait((&pWait
->WaitQE
));
239 * Checks if a linux wait was interrupted.
241 * @returns true / false
242 * @param pWait The wait structure.
243 * @remarks This shall be called before the first rtR0SemLnxWaitDoIt().
245 DECLINLINE(bool) rtR0SemLnxWaitWasInterrupted(PRTR0SEMLNXWAIT pWait
)
247 return pWait
->iWaitState
== TASK_INTERRUPTIBLE
248 && signal_pending(current
);
253 * Checks if a linux wait has timed out.
255 * @returns true / false
256 * @param pWait The wait structure.
258 DECLINLINE(bool) rtR0SemLnxWaitHasTimedOut(PRTR0SEMLNXWAIT pWait
)
260 return pWait
->fTimedOut
;
265 * Deletes a linux wait.
267 * @param pWait The wait structure.
269 DECLINLINE(void) rtR0SemLnxWaitDelete(PRTR0SEMLNXWAIT pWait
)
271 finish_wait(pWait
->pWaitQueue
, &pWait
->WaitQE
);
276 * Gets the max resolution of the timeout machinery.
278 * @returns Resolution specified in nanoseconds.
280 DECLINLINE(uint32_t) rtR0SemLnxWaitGetResolution(void)
282 #ifdef IPRT_LINUX_HAS_HRTIMER
283 return RTR0SEMLNXWAIT_RESOLUTION
;
285 return RT_NS_1SEC
/ HZ
; /* ns */