diff options
Diffstat (limited to 'src/VBox/Runtime/r0drv/freebsd/sleepqueue-r0drv-freebsd.h')
-rw-r--r-- | src/VBox/Runtime/r0drv/freebsd/sleepqueue-r0drv-freebsd.h | 334 |
1 files changed, 334 insertions, 0 deletions
diff --git a/src/VBox/Runtime/r0drv/freebsd/sleepqueue-r0drv-freebsd.h b/src/VBox/Runtime/r0drv/freebsd/sleepqueue-r0drv-freebsd.h new file mode 100644 index 00000000..dc1c3307 --- /dev/null +++ b/src/VBox/Runtime/r0drv/freebsd/sleepqueue-r0drv-freebsd.h @@ -0,0 +1,334 @@ +/* $Id: sleepqueue-r0drv-freebsd.h $ */ +/** @file + * IPRT - FreeBSD Ring-0 Driver Helpers for Abstracting Sleep Queues, + */ + +/* + * Copyright (C) 2006-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + +#ifndef IPRT_INCLUDED_SRC_r0drv_freebsd_sleepqueue_r0drv_freebsd_h +#define IPRT_INCLUDED_SRC_r0drv_freebsd_sleepqueue_r0drv_freebsd_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include "the-freebsd-kernel.h" + +#include <iprt/asm-math.h> +#include <iprt/err.h> +#include <iprt/string.h> +#include <iprt/time.h> + +/** + * Kernel mode FreeBSD wait state structure. + */ +typedef struct RTR0SEMBSDSLEEP +{ + /** The absolute timeout given as nano seconds since the start of the + * monotonic clock. */ + uint64_t uNsAbsTimeout; + /** The timeout in ticks. Updated after waiting. */ + int iTimeout; + /** Set if it's an indefinite wait. */ + bool fIndefinite; + /** Set if we've already timed out. + * Set by rtR0SemBsdWaitDoIt and read by rtR0SemBsdWaitHasTimedOut. */ + bool fTimedOut; + /** Flag whether the wait was interrupted. */ + bool fInterrupted; + /** flag whether the wait is interruptible or not. */ + bool fInterruptible; + /** Opaque wait channel id. */ + void *pvWaitChan; +} RTR0SEMBSDSLEEP; +/** Pointer to a FreeBSD wait state. */ +typedef RTR0SEMBSDSLEEP *PRTR0SEMBSDSLEEP; + + +/** + * Updates the timeout of the FreeBSD wait. + * + * @returns RTSEMWAIT_FLAGS_INDEFINITE if the timeout value is too big. + * 0 otherwise + * @param pWait The wait structure. + * @param uTimeout The relative timeout in nanoseconds. + */ +DECLINLINE(uint32_t) rtR0SemBsdWaitUpdateTimeout(PRTR0SEMBSDSLEEP pWait, uint64_t uTimeout) +{ +#if 0 + struct timeval tv; + + tv.tv_sec = uTimeout / UINT64_C(1000000000); + tv.tv_usec = (uTimeout % UINT64_C(1000000000)) / UINT64_C(1000); + + pWait->iTimeout = tvtohz(&tv); +#else + uint64_t cTicks = ASMMultU64ByU32DivByU32(uTimeout, hz, UINT32_C(1000000000)); + if (cTicks >= INT_MAX) + return RTSEMWAIT_FLAGS_INDEFINITE; + else + pWait->iTimeout = (int)cTicks; +#endif + + return 0; +} + +/** + * Initializes a wait. + * + * The caller MUST check the wait condition BEFORE calling this function or the + * timeout logic will be flawed. + * + * @returns VINF_SUCCESS or VERR_TIMEOUT. + * @param pWait The wait structure. + * @param fFlags The wait flags. + * @param uTimeout The timeout. + * @param pvWaitChan The opaque wait channel. + */ +DECLINLINE(int) rtR0SemBsdWaitInit(PRTR0SEMBSDSLEEP pWait, uint32_t fFlags, uint64_t uTimeout, + void *pvWaitChan) +{ + pWait->iTimeout = 0; + pWait->uNsAbsTimeout = 0; /* shut up gcc */ + + /* + * Process the flags and timeout. + */ + if (!(fFlags & RTSEMWAIT_FLAGS_INDEFINITE)) + { +/** @todo optimize: millisecs -> nanosecs -> millisec -> jiffies */ + if (fFlags & RTSEMWAIT_FLAGS_MILLISECS) + uTimeout = uTimeout < UINT64_MAX / UINT32_C(1000000) * UINT32_C(1000000) + ? uTimeout * UINT32_C(1000000) + : UINT64_MAX; + if (uTimeout == UINT64_MAX) + fFlags |= RTSEMWAIT_FLAGS_INDEFINITE; + else + { + uint64_t u64Now; + if (fFlags & RTSEMWAIT_FLAGS_RELATIVE) + { + if (uTimeout == 0) + return VERR_TIMEOUT; + + u64Now = RTTimeSystemNanoTS(); + if (u64Now + uTimeout < u64Now) /* overflow */ + fFlags |= RTSEMWAIT_FLAGS_INDEFINITE; + else + pWait->uNsAbsTimeout = u64Now + uTimeout; + } + else + { + u64Now = RTTimeSystemNanoTS(); + if (u64Now >= uTimeout) + return VERR_TIMEOUT; + + pWait->uNsAbsTimeout = uTimeout; + uTimeout -= u64Now; /* Get a relative value. */ + } + } + } + + if (!(fFlags & RTSEMWAIT_FLAGS_INDEFINITE)) + { + pWait->fIndefinite = false; + fFlags |= rtR0SemBsdWaitUpdateTimeout(pWait, uTimeout); + } + + if (fFlags & RTSEMWAIT_FLAGS_INDEFINITE) + { + pWait->fIndefinite = true; + pWait->iTimeout = INT_MAX; + pWait->uNsAbsTimeout = UINT64_MAX; + } + + pWait->fTimedOut = false; + + /* + * Initialize the wait queue related bits. + */ + pWait->fInterruptible = fFlags & RTSEMWAIT_FLAGS_INTERRUPTIBLE + ? true : false; + pWait->pvWaitChan = pvWaitChan; + pWait->fInterrupted = false; + + return VINF_SUCCESS; +} + +/** + * Prepares the next wait. + * + * This must be called before rtR0SemBsdWaitDoIt, and the caller should check + * the exit conditions inbetween the two calls. + * + * @param pWait The wait structure. + */ +DECLINLINE(void) rtR0SemBsdWaitPrepare(PRTR0SEMBSDSLEEP pWait) +{ + /* Lock the queues. */ + sleepq_lock(pWait->pvWaitChan); +} + +/** + * Do the actual wait. + * + * @param pWait The wait structure. + */ +DECLINLINE(void) rtR0SemBsdWaitDoIt(PRTR0SEMBSDSLEEP pWait) +{ + int rcBsd; + int fSleepqFlags = SLEEPQ_CONDVAR; + + if (pWait->fInterruptible) + fSleepqFlags |= SLEEPQ_INTERRUPTIBLE; + + sleepq_add(pWait->pvWaitChan, NULL, "VBoxIS", fSleepqFlags, 0); + + if (!pWait->fIndefinite) + { + sleepq_set_timeout(pWait->pvWaitChan, pWait->iTimeout); + + if (pWait->fInterruptible) + rcBsd = SLEEPQ_TIMEDWAIT_SIG(pWait->pvWaitChan); + else + rcBsd = SLEEPQ_TIMEDWAIT(pWait->pvWaitChan); + } + else + { + if (pWait->fInterruptible) + rcBsd = SLEEPQ_WAIT_SIG(pWait->pvWaitChan); + else + { + rcBsd = 0; + SLEEPQ_WAIT(pWait->pvWaitChan); + } + } + + switch (rcBsd) + { + case 0: + break; + case ERESTART: + { + if (!pWait->fIndefinite) + { + /* Recalc timeout. */ + uint64_t u64Now = RTTimeSystemNanoTS(); + if (u64Now >= pWait->uNsAbsTimeout) + pWait->fTimedOut = true; + else + { + u64Now = pWait->uNsAbsTimeout - u64Now; + rtR0SemBsdWaitUpdateTimeout(pWait, u64Now); + } + } + break; + } + case EWOULDBLOCK: + pWait->fTimedOut = true; + break; + case EINTR: + Assert(pWait->fInterruptible); + pWait->fInterrupted = true; + break; + default: + AssertMsgFailed(("sleepq_* -> %d\n", rcBsd)); + break; + } +} + + +/** + * Checks if a FreeBSD wait was interrupted. + * + * @returns true / false + * @param pWait The wait structure. + * @remarks This shall be called before the first rtR0SemBsdWaitDoIt(). + */ +DECLINLINE(bool) rtR0SemBsdWaitWasInterrupted(PRTR0SEMBSDSLEEP pWait) +{ + return pWait->fInterrupted; +} + + +/** + * Checks if a FreeBSD wait has timed out. + * + * @returns true / false + * @param pWait The wait structure. + */ +DECLINLINE(bool) rtR0SemBsdWaitHasTimedOut(PRTR0SEMBSDSLEEP pWait) +{ + return pWait->fTimedOut; +} + + +/** + * Deletes a FreeBSD wait. + * + * @param pWait The wait structure. + */ +DECLINLINE(void) rtR0SemBsdWaitDelete(PRTR0SEMBSDSLEEP pWait) +{ + sleepq_release(pWait->pvWaitChan); +} + + +/** + * Signals the wait channel. + * + * @param pvWaitChan The opaque wait channel handle. + */ +DECLINLINE(void) rtR0SemBsdSignal(void *pvWaitChan) +{ + sleepq_lock(pvWaitChan); + int fWakeupSwapProc = sleepq_signal(pvWaitChan, SLEEPQ_CONDVAR, 0, 0); + sleepq_release(pvWaitChan); + if (fWakeupSwapProc) + kick_proc0(); +} + +/** + * Wakes up all waiters on the wait channel. + * + * @param pvWaitChan The opaque wait channel handle. + */ +DECLINLINE(void) rtR0SemBsdBroadcast(void *pvWaitChan) +{ + sleepq_lock(pvWaitChan); + sleepq_broadcast(pvWaitChan, SLEEPQ_CONDVAR, 0, 0); +#if __FreeBSD_version >= 800000 /* Broadcast releases the sleep queue lock on FreeBSD 7.x */ + sleepq_release(pvWaitChan); +#endif +} + +/** + * Gets the max resolution of the timeout machinery. + * + * @returns Resolution specified in nanoseconds. + */ +DECLINLINE(uint32_t) rtR0SemBsdWaitGetResolution(void) +{ + return 1000000000 / hz; /* ns */ +} + +#endif /* !IPRT_INCLUDED_SRC_r0drv_freebsd_sleepqueue_r0drv_freebsd_h */ + |