]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blame - ubuntu/vbox/vboxguest/r0drv/linux/waitqueue-r0drv-linux.h
UBUNTU: ubuntu: vbox -- update to 5.2.0-dfsg-2
[mirror_ubuntu-bionic-kernel.git] / ubuntu / vbox / vboxguest / r0drv / linux / waitqueue-r0drv-linux.h
CommitLineData
056a1eb7
SF
1/* $Id: waitqueue-r0drv-linux.h $ */
2/** @file
3 * IPRT - Linux Ring-0 Driver Helpers for Abstracting Wait Queues,
4 */
5
6/*
6d209b23 7 * Copyright (C) 2006-2017 Oracle Corporation
056a1eb7
SF
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 */
46typedef 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. */
82typedef 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 */
97DECLINLINE(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 */
204DECLINLINE(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 */
216DECLINLINE(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 */
245DECLINLINE(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 */
258DECLINLINE(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 */
269DECLINLINE(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 */
280DECLINLINE(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