VirtualBox

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

Last change on this file since 93115 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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