VirtualBox

source: vbox/trunk/src/VBox/Runtime/r0drv/linux/waitqueue-r0drv-linux.h@ 62477

Last change on this file since 62477 was 62477, checked in by vboxsync, 8 years ago

(C) 2016

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 8.2 KB
Line 
1/* $Id: waitqueue-r0drv-linux.h 62477 2016-07-22 18:27:37Z vboxsync $ */
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 */
46typedef struct RTR0SEMLNXWAIT
47{
48 /** The wait queue entry. */
49 wait_queue_t WaitQE;
50 /** The absolute timeout given as nano seconds since the start of the
51 * monotonic clock. */
52 uint64_t uNsAbsTimeout;
53 /** The timeout in nano seconds relative to the start of the wait. */
54 uint64_t cNsRelTimeout;
55 /** The native timeout value. */
56 union
57 {
58#ifdef IPRT_LINUX_HAS_HRTIMER
59 /** The timeout when fHighRes is true. Absolute, so no updating. */
60 ktime_t KtTimeout;
61#endif
62 /** The timeout when fHighRes is false. Updated after waiting. */
63 long lTimeout;
64 } u;
65 /** Set if we use high resolution timeouts. */
66 bool fHighRes;
67 /** Set if it's an indefinite wait. */
68 bool fIndefinite;
69 /** Set if we've already timed out.
70 * Set by rtR0SemLnxWaitDoIt and read by rtR0SemLnxWaitHasTimedOut. */
71 bool fTimedOut;
72 /** TASK_INTERRUPTIBLE or TASK_UNINTERRUPTIBLE. */
73 int iWaitState;
74 /** The wait queue. */
75 wait_queue_head_t *pWaitQueue;
76} RTR0SEMLNXWAIT;
77/** Pointer to a linux wait state. */
78typedef RTR0SEMLNXWAIT *PRTR0SEMLNXWAIT;
79
80
81/**
82 * Initializes a wait.
83 *
84 * The caller MUST check the wait condition BEFORE calling this function or the
85 * timeout logic will be flawed.
86 *
87 * @returns VINF_SUCCESS or VERR_TIMEOUT.
88 * @param pWait The wait structure.
89 * @param fFlags The wait flags.
90 * @param uTimeout The timeout.
91 * @param pWaitQueue The wait queue head.
92 */
93DECLINLINE(int) rtR0SemLnxWaitInit(PRTR0SEMLNXWAIT pWait, uint32_t fFlags, uint64_t uTimeout,
94 wait_queue_head_t *pWaitQueue)
95{
96 /*
97 * Process the flags and timeout.
98 */
99 if (!(fFlags & RTSEMWAIT_FLAGS_INDEFINITE))
100 {
101/** @todo optimize: millisecs -> nanosecs -> millisec -> jiffies */
102 if (fFlags & RTSEMWAIT_FLAGS_MILLISECS)
103 uTimeout = uTimeout < UINT64_MAX / RT_US_1SEC * RT_US_1SEC
104 ? uTimeout * RT_US_1SEC
105 : UINT64_MAX;
106 if (uTimeout == UINT64_MAX)
107 fFlags |= RTSEMWAIT_FLAGS_INDEFINITE;
108 else
109 {
110 uint64_t u64Now;
111 if (fFlags & RTSEMWAIT_FLAGS_RELATIVE)
112 {
113 if (uTimeout == 0)
114 return VERR_TIMEOUT;
115
116 u64Now = RTTimeSystemNanoTS();
117 pWait->cNsRelTimeout = uTimeout;
118 pWait->uNsAbsTimeout = u64Now + uTimeout;
119 if (pWait->uNsAbsTimeout < u64Now) /* overflow */
120 fFlags |= RTSEMWAIT_FLAGS_INDEFINITE;
121 }
122 else
123 {
124 u64Now = RTTimeSystemNanoTS();
125 if (u64Now >= uTimeout)
126 return VERR_TIMEOUT;
127
128 pWait->cNsRelTimeout = uTimeout - u64Now;
129 pWait->uNsAbsTimeout = uTimeout;
130 }
131 }
132 }
133
134 if (!(fFlags & RTSEMWAIT_FLAGS_INDEFINITE))
135 {
136 pWait->fIndefinite = false;
137#ifdef IPRT_LINUX_HAS_HRTIMER
138 if ( (fFlags & (RTSEMWAIT_FLAGS_NANOSECS | RTSEMWAIT_FLAGS_ABSOLUTE))
139 || pWait->cNsRelTimeout < RT_NS_1SEC / HZ * 4)
140 {
141 pWait->fHighRes = true;
142# if BITS_PER_LONG < 64
143 if ( KTIME_SEC_MAX <= LONG_MAX
144 && pWait->uNsAbsTimeout >= KTIME_SEC_MAX * RT_NS_1SEC_64 + (RT_NS_1SEC - 1))
145 fFlags |= RTSEMWAIT_FLAGS_INDEFINITE;
146 else
147# endif
148 pWait->u.KtTimeout = ns_to_ktime(pWait->uNsAbsTimeout);
149 }
150 else
151#endif
152 {
153 uint64_t cJiffies = ASMMultU64ByU32DivByU32(pWait->cNsRelTimeout, HZ, RT_NS_1SEC);
154 if (cJiffies >= MAX_JIFFY_OFFSET)
155 fFlags |= RTSEMWAIT_FLAGS_INDEFINITE;
156 else
157 {
158 pWait->u.lTimeout = (long)cJiffies;
159 pWait->fHighRes = false;
160 }
161 }
162 }
163
164 if (fFlags & RTSEMWAIT_FLAGS_INDEFINITE)
165 {
166 pWait->fIndefinite = true;
167 pWait->fHighRes = false;
168 pWait->uNsAbsTimeout = UINT64_MAX;
169 pWait->cNsRelTimeout = UINT64_MAX;
170 pWait->u.lTimeout = LONG_MAX;
171 }
172
173 pWait->fTimedOut = false;
174
175 /*
176 * Initialize the wait queue related bits.
177 */
178#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 39)
179 init_wait((&pWait->WaitQE));
180#else
181 RT_ZERO(pWait->WaitQE);
182 init_waitqueue_entry((&pWait->WaitQE), current);
183#endif
184 pWait->pWaitQueue = pWaitQueue;
185 pWait->iWaitState = fFlags & RTSEMWAIT_FLAGS_INTERRUPTIBLE
186 ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE;
187
188 return VINF_SUCCESS;
189}
190
191
192/**
193 * Prepares the next wait.
194 *
195 * This must be called before rtR0SemLnxWaitDoIt, and the caller should check
196 * the exit conditions in-between the two calls.
197 *
198 * @param pWait The wait structure.
199 */
200DECLINLINE(void) rtR0SemLnxWaitPrepare(PRTR0SEMLNXWAIT pWait)
201{
202 /* Make everything thru schedule*() atomic scheduling wise. (Is this correct?) */
203 prepare_to_wait(pWait->pWaitQueue, &pWait->WaitQE, pWait->iWaitState);
204}
205
206
207/**
208 * Do the actual wait.
209 *
210 * @param pWait The wait structure.
211 */
212DECLINLINE(void) rtR0SemLnxWaitDoIt(PRTR0SEMLNXWAIT pWait)
213{
214 if (pWait->fIndefinite)
215 schedule();
216#ifdef IPRT_LINUX_HAS_HRTIMER
217 else if (pWait->fHighRes)
218 {
219 int rc = schedule_hrtimeout_range(&pWait->u.KtTimeout, HRTIMER_MODE_ABS, RTR0SEMLNXWAIT_RESOLUTION);
220 if (!rc)
221 pWait->fTimedOut = true;
222 }
223#endif
224 else
225 {
226 pWait->u.lTimeout = schedule_timeout(pWait->u.lTimeout);
227 if (pWait->u.lTimeout <= 0)
228 pWait->fTimedOut = true;
229 }
230 after_wait((&pWait->WaitQE));
231}
232
233
234/**
235 * Checks if a linux wait was interrupted.
236 *
237 * @returns true / false
238 * @param pWait The wait structure.
239 * @remarks This shall be called before the first rtR0SemLnxWaitDoIt().
240 */
241DECLINLINE(bool) rtR0SemLnxWaitWasInterrupted(PRTR0SEMLNXWAIT pWait)
242{
243 return pWait->iWaitState == TASK_INTERRUPTIBLE
244 && signal_pending(current);
245}
246
247
248/**
249 * Checks if a linux wait has timed out.
250 *
251 * @returns true / false
252 * @param pWait The wait structure.
253 */
254DECLINLINE(bool) rtR0SemLnxWaitHasTimedOut(PRTR0SEMLNXWAIT pWait)
255{
256 return pWait->fTimedOut;
257}
258
259
260/**
261 * Deletes a linux wait.
262 *
263 * @param pWait The wait structure.
264 */
265DECLINLINE(void) rtR0SemLnxWaitDelete(PRTR0SEMLNXWAIT pWait)
266{
267 finish_wait(pWait->pWaitQueue, &pWait->WaitQE);
268}
269
270
271/**
272 * Gets the max resolution of the timeout machinery.
273 *
274 * @returns Resolution specified in nanoseconds.
275 */
276DECLINLINE(uint32_t) rtR0SemLnxWaitGetResolution(void)
277{
278#ifdef IPRT_LINUX_HAS_HRTIMER
279 return RTR0SEMLNXWAIT_RESOLUTION;
280#else
281 return RT_NS_1SEC / HZ; /* ns */
282#endif
283}
284
285#endif
286
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette