]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blob - ubuntu/vbox/r0drv/linux/waitqueue-r0drv-linux.h
UBUNTU: ubuntu: vbox -- update to 5.1.28-dfsg-1
[mirror_ubuntu-bionic-kernel.git] / ubuntu / vbox / r0drv / linux / waitqueue-r0drv-linux.h
1 /* $Id: waitqueue-r0drv-linux.h $ */
2 /** @file
3 * IPRT - Linux Ring-0 Driver Helpers for Abstracting Wait Queues,
4 */
5
6 /*
7 * Copyright (C) 2006-2016 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
28 #ifndef ___r0drv_linux_waitqueue_r0drv_linux_h
29 #define ___r0drv_linux_waitqueue_r0drv_linux_h
30
31 #include "the-linux-kernel.h"
32
33 #include <iprt/asm-math.h>
34 #include <iprt/err.h>
35 #include <iprt/string.h>
36 #include <iprt/time.h>
37
38 /** The resolution (nanoseconds) specified when using
39 * schedule_hrtimeout_range. */
40 #define RTR0SEMLNXWAIT_RESOLUTION 50000
41
42
43 /**
44 * Kernel mode Linux wait state structure.
45 */
46 typedef struct RTR0SEMLNXWAIT
47 {
48 /** The wait queue entry. */
49 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
50 wait_queue_entry_t WaitQE;
51 #else
52 wait_queue_t WaitQE;
53 #endif
54 /** The absolute timeout given as nano seconds since the start of the
55 * monotonic clock. */
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. */
60 union
61 {
62 #ifdef IPRT_LINUX_HAS_HRTIMER
63 /** The timeout when fHighRes is true. Absolute, so no updating. */
64 ktime_t KtTimeout;
65 #endif
66 /** The timeout when fHighRes is false. Updated after waiting. */
67 long lTimeout;
68 } u;
69 /** Set if we use high resolution timeouts. */
70 bool fHighRes;
71 /** Set if it's an indefinite wait. */
72 bool fIndefinite;
73 /** Set if we've already timed out.
74 * Set by rtR0SemLnxWaitDoIt and read by rtR0SemLnxWaitHasTimedOut. */
75 bool fTimedOut;
76 /** TASK_INTERRUPTIBLE or TASK_UNINTERRUPTIBLE. */
77 int iWaitState;
78 /** The wait queue. */
79 wait_queue_head_t *pWaitQueue;
80 } RTR0SEMLNXWAIT;
81 /** Pointer to a linux wait state. */
82 typedef RTR0SEMLNXWAIT *PRTR0SEMLNXWAIT;
83
84
85 /**
86 * Initializes a wait.
87 *
88 * The caller MUST check the wait condition BEFORE calling this function or the
89 * timeout logic will be flawed.
90 *
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.
96 */
97 DECLINLINE(int) rtR0SemLnxWaitInit(PRTR0SEMLNXWAIT pWait, uint32_t fFlags, uint64_t uTimeout,
98 wait_queue_head_t *pWaitQueue)
99 {
100 /*
101 * Process the flags and timeout.
102 */
103 if (!(fFlags & RTSEMWAIT_FLAGS_INDEFINITE))
104 {
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
109 : UINT64_MAX;
110 if (uTimeout == UINT64_MAX)
111 fFlags |= RTSEMWAIT_FLAGS_INDEFINITE;
112 else
113 {
114 uint64_t u64Now;
115 if (fFlags & RTSEMWAIT_FLAGS_RELATIVE)
116 {
117 if (uTimeout == 0)
118 return VERR_TIMEOUT;
119
120 u64Now = RTTimeSystemNanoTS();
121 pWait->cNsRelTimeout = uTimeout;
122 pWait->uNsAbsTimeout = u64Now + uTimeout;
123 if (pWait->uNsAbsTimeout < u64Now) /* overflow */
124 fFlags |= RTSEMWAIT_FLAGS_INDEFINITE;
125 }
126 else
127 {
128 u64Now = RTTimeSystemNanoTS();
129 if (u64Now >= uTimeout)
130 return VERR_TIMEOUT;
131
132 pWait->cNsRelTimeout = uTimeout - u64Now;
133 pWait->uNsAbsTimeout = uTimeout;
134 }
135 }
136 }
137
138 if (!(fFlags & RTSEMWAIT_FLAGS_INDEFINITE))
139 {
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)
144 {
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;
150 else
151 # endif
152 pWait->u.KtTimeout = ns_to_ktime(pWait->uNsAbsTimeout);
153 }
154 else
155 #endif
156 {
157 uint64_t cJiffies = ASMMultU64ByU32DivByU32(pWait->cNsRelTimeout, HZ, RT_NS_1SEC);
158 if (cJiffies >= MAX_JIFFY_OFFSET)
159 fFlags |= RTSEMWAIT_FLAGS_INDEFINITE;
160 else
161 {
162 pWait->u.lTimeout = (long)cJiffies;
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->fTimedOut = false;
178
179 /*
180 * Initialize the wait queue related bits.
181 */
182 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 39)
183 init_wait((&pWait->WaitQE));
184 #else
185 RT_ZERO(pWait->WaitQE);
186 init_waitqueue_entry((&pWait->WaitQE), current);
187 #endif
188 pWait->pWaitQueue = pWaitQueue;
189 pWait->iWaitState = fFlags & RTSEMWAIT_FLAGS_INTERRUPTIBLE
190 ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE;
191
192 return VINF_SUCCESS;
193 }
194
195
196 /**
197 * Prepares the next wait.
198 *
199 * This must be called before rtR0SemLnxWaitDoIt, and the caller should check
200 * the exit conditions in-between the two calls.
201 *
202 * @param pWait The wait structure.
203 */
204 DECLINLINE(void) rtR0SemLnxWaitPrepare(PRTR0SEMLNXWAIT pWait)
205 {
206 /* Make everything thru schedule*() atomic scheduling wise. (Is this correct?) */
207 prepare_to_wait(pWait->pWaitQueue, &pWait->WaitQE, pWait->iWaitState);
208 }
209
210
211 /**
212 * Do the actual wait.
213 *
214 * @param pWait The wait structure.
215 */
216 DECLINLINE(void) rtR0SemLnxWaitDoIt(PRTR0SEMLNXWAIT pWait)
217 {
218 if (pWait->fIndefinite)
219 schedule();
220 #ifdef IPRT_LINUX_HAS_HRTIMER
221 else if (pWait->fHighRes)
222 {
223 int rc = schedule_hrtimeout_range(&pWait->u.KtTimeout, HRTIMER_MODE_ABS, RTR0SEMLNXWAIT_RESOLUTION);
224 if (!rc)
225 pWait->fTimedOut = true;
226 }
227 #endif
228 else
229 {
230 pWait->u.lTimeout = schedule_timeout(pWait->u.lTimeout);
231 if (pWait->u.lTimeout <= 0)
232 pWait->fTimedOut = true;
233 }
234 after_wait((&pWait->WaitQE));
235 }
236
237
238 /**
239 * Checks if a linux wait was interrupted.
240 *
241 * @returns true / false
242 * @param pWait The wait structure.
243 * @remarks This shall be called before the first rtR0SemLnxWaitDoIt().
244 */
245 DECLINLINE(bool) rtR0SemLnxWaitWasInterrupted(PRTR0SEMLNXWAIT pWait)
246 {
247 return pWait->iWaitState == TASK_INTERRUPTIBLE
248 && signal_pending(current);
249 }
250
251
252 /**
253 * Checks if a linux wait has timed out.
254 *
255 * @returns true / false
256 * @param pWait The wait structure.
257 */
258 DECLINLINE(bool) rtR0SemLnxWaitHasTimedOut(PRTR0SEMLNXWAIT pWait)
259 {
260 return pWait->fTimedOut;
261 }
262
263
264 /**
265 * Deletes a linux wait.
266 *
267 * @param pWait The wait structure.
268 */
269 DECLINLINE(void) rtR0SemLnxWaitDelete(PRTR0SEMLNXWAIT pWait)
270 {
271 finish_wait(pWait->pWaitQueue, &pWait->WaitQE);
272 }
273
274
275 /**
276 * Gets the max resolution of the timeout machinery.
277 *
278 * @returns Resolution specified in nanoseconds.
279 */
280 DECLINLINE(uint32_t) rtR0SemLnxWaitGetResolution(void)
281 {
282 #ifdef IPRT_LINUX_HAS_HRTIMER
283 return RTR0SEMLNXWAIT_RESOLUTION;
284 #else
285 return RT_NS_1SEC / HZ; /* ns */
286 #endif
287 }
288
289 #endif
290