VirtualBox

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

Last change on this file since 79025 was 79025, checked in by vboxsync, 6 years ago

Linux/host and guest drivers: support more openSUSE 15.0 and 15.1 kernels.

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