]>
Commit | Line | Data |
---|---|---|
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 | */ | |
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 |