threadctxhooks-r0drv-solaris.c revision 6b83e14a3fd9389cfed4aa0790485adbca556e1a
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync/* $Id$ */
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync/** @file
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync * IPRT - Thread-Context Hook, Ring-0 Driver, Solaris.
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync */
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync/*
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync * Copyright (C) 2013 Oracle Corporation
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync *
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync * available from http://www.virtualbox.org. This file is free software;
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync * you can redistribute it and/or modify it under the terms of the GNU
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync * General Public License (GPL) as published by the Free Software
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync *
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync * The contents of this file may alternatively be used under the terms
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync * of the Common Development and Distribution License Version 1.0
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync * VirtualBox OSE distribution, in which case the provisions of the
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync * CDDL are applicable instead of those of the GPL.
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync *
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync * You may elect to license modified versions of this file under the
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync * terms and conditions of either the GPL or the CDDL or both.
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync */
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync/*******************************************************************************
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync* Header Files *
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync*******************************************************************************/
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync#include "the-solaris-kernel.h"
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync#include "internal/iprt.h"
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync#include <iprt/mem.h>
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync#include <iprt/assert.h>
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync#include <iprt/thread.h>
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync#include <iprt/err.h>
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync#include <iprt/asm.h>
4a4140b851e27d435a9c56675db3598b8490fea2vboxsync#include "internal/thread.h"
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync/*******************************************************************************
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync* Structures and Typedefs *
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync*******************************************************************************/
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync/**
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync * The internal thread-context object.
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync */
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsynctypedef struct RTTHREADCTXINT
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync{
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync /** Magic value (RTTHREADCTXINT_MAGIC). */
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync uint32_t volatile u32Magic;
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync /** The thread handle (owner) for which the context-hooks are registered. */
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync RTNATIVETHREAD hOwner;
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync /** Pointer to the registered thread-context hook. */
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync PFNRTTHREADCTXHOOK pfnThreadCtxHook;
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync /** User argument passed to the thread-context hook. */
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync void *pvUser;
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync /** Whether this handle has any hooks registered or not. */
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync bool fRegistered;
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync /** Number of references to this object. */
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync uint32_t volatile cRefs;
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync} RTTHREADCTXINT, *PRTTHREADCTXINT;
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync/*******************************************************************************
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync* Defined Constants And Macros *
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync*******************************************************************************/
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync/** Validates a thread-context hook handle and returns rc if not valid. */
4a4140b851e27d435a9c56675db3598b8490fea2vboxsync#define RTTHREADCTX_VALID_RETURN_RC(pThis, rc) \
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync do { \
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync AssertPtrReturn((pThis), (rc)); \
4a4140b851e27d435a9c56675db3598b8490fea2vboxsync AssertReturn((pThis)->u32Magic == RTTHREADCTXINT_MAGIC, (rc)); \
4a4140b851e27d435a9c56675db3598b8490fea2vboxsync AssertReturn((pThis)->cRefs > 0, (rc)); \
4a4140b851e27d435a9c56675db3598b8490fea2vboxsync } while (0)
4a4140b851e27d435a9c56675db3598b8490fea2vboxsync
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync
4a4140b851e27d435a9c56675db3598b8490fea2vboxsync/**
4a4140b851e27d435a9c56675db3598b8490fea2vboxsync * Hook function for the thread-preempting event.
4a4140b851e27d435a9c56675db3598b8490fea2vboxsync *
4a4140b851e27d435a9c56675db3598b8490fea2vboxsync * @param pvThreadCtxInt Opaque pointer to the internal thread-context
4a4140b851e27d435a9c56675db3598b8490fea2vboxsync * object.
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync *
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync * @remarks Called with the with preemption disabled!
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync */
4a4140b851e27d435a9c56675db3598b8490fea2vboxsyncstatic void rtThreadCtxHooksSolPreempting(void *pvThreadCtxInt)
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync{
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync PRTTHREADCTXINT pThis = (PRTTHREADCTXINT)pvThreadCtxInt;
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync AssertPtr(pThis);
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync Assert(!RTThreadPreemptIsEnabled(NIL_RTTHREAD));
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync
4a4140b851e27d435a9c56675db3598b8490fea2vboxsync if (pThis->fRegistered)
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync {
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync Assert(pThis->pfnThreadCtxHook);
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync pThis->pfnThreadCtxHook(RTTHREADCTXEVENT_PREEMPTING, pThis->pvUser);
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync }
4a4140b851e27d435a9c56675db3598b8490fea2vboxsync}
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync/**
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync * Hook function for the thread-resumed event.
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync *
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync * @param pvThreadCtxInt Opaque pointer to the internal thread-context
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync * object.
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync */
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsyncstatic void rtThreadCtxHooksSolResumed(void *pvThreadCtxInt)
3cc33d1e7558a0f4a04b59bfe914a24d0b9a2329vboxsync{
PRTTHREADCTXINT pThis = (PRTTHREADCTXINT)pvThreadCtxInt;
AssertPtr(pThis);
Assert(!RTThreadPreemptIsEnabled(NIL_RTTHREAD));
if (pThis->fRegistered)
{
Assert(pThis->pfnThreadCtxHook);
pThis->pfnThreadCtxHook(RTTHREADCTXEVENT_RESUMED, pThis->pvUser);
}
}
/**
* Hook function for the thread-free event.
*
* @param pvThreadCtxInt Opaque pointer to the internal thread-context
* object.
* @param fIsExec Whether this event is triggered due to exec().
*/
static void rtThreadCtxHooksSolFree(void *pvThreadCtxInt, int fIsExec)
{
PRTTHREADCTXINT pThis = (PRTTHREADCTXINT)pvThreadCtxInt;
AssertPtrReturnVoid(pThis);
AssertMsgReturnVoid(pThis->u32Magic == RTTHREADCTXINT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis));
uint32_t cRefs = ASMAtomicReadU32(&pThis->cRefs);
if (RT_UNLIKELY(!cRefs))
{
/* Should never happen. */
AssertMsgFailed(("rtThreadCtxHooksSolFree with cRefs=0 pThis=%p\n", pThis));
return;
}
cRefs = ASMAtomicDecU32(&pThis->cRefs)
if (!cRefs)
{
Assert(!pThis->fRegistered);
ASMAtomicWriteU32(&pThis->u32Magic, ~RTTHREADCTXINT_MAGIC);
RTMemFree(pThis);
}
}
RTDECL(int) RTThreadCtxHooksCreate(PRTTHREADCTX phThreadCtx)
{
PRTTHREADCTXINT pThis;
Assert(RTThreadPreemptIsEnabled(NIL_RTTHREAD));
pThis = (PRTTHREADCTXINT)RTMemAllocZ(sizeof(*pThis));
if (RT_UNLIKELY(!pThis))
return VERR_NO_MEMORY;
pThis->u32Magic = RTTHREADCTXINT_MAGIC;
pThis->hOwner = RTThreadNativeSelf();
pThis->fRegistered = false;
pThis->cRefs = 2; /* One reference for the thread, one for the hook object. */
/*
* installctx() allocates memory and thus cannot be used in RTThreadCtxHooksRegister() which can be used
* with preemption disabled. We allocate the context-hooks here and use 'fRegistered' to determine if we can
* invoke the consumer's hook or not.
*/
if (g_frtSolOldThreadCtx)
{
g_rtSolThreadCtx.Install.pfnSol_installctx_old(curthread,
pThis,
rtThreadCtxHooksSolPreempting,
rtThreadCtxHooksSolResumed,
NULL, /* fork */
NULL, /* lwp_create */
rtThreadCtxHooksSolFree);
}
else
{
g_rtSolThreadCtx.Install.pfnSol_installctx(curthread,
pThis,
rtThreadCtxHooksSolPreempting,
rtThreadCtxHooksSolResumed,
NULL, /* fork */
NULL, /* lwp_create */
NULL, /* exit */
rtThreadCtxHooksSolFree);
}
*phThreadCtx = pThis;
return VINF_SUCCESS;
}
RTDECL(uint32_t) RTThreadCtxHooksRetain(RTTHREADCTX hThreadCtx)
{
PRTTHREADCTXINT pThis = hThreadCtx;
RTTHREADCTX_VALID_RETURN_RC(hThreadCtx, UINT32_MAX);
uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
Assert(cRefs < UINT32_MAX / 2);
return cRefs;
}
RTDECL(uint32_t) RTThreadCtxHooksRelease(RTTHREADCTX hThreadCtx)
{
PRTTHREADCTXINT pThis = hThreadCtx;
if (pThis == NIL_RTTHREADCTX)
return 0;
RTTHREADCTX_VALID_RETURN_RC(hThreadCtx, UINT32_MAX);
Assert(RTThreadPreemptIsEnabled(NIL_RTTHREAD));
pThis->fRegistered = false;
uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
if ( pThis->hOwner == RTThreadNativeSelf()
&& cRefs == 1)
{
/*
* removectx() will invoke rtThreadCtxHooksSolFree() and there is no way to bypass it and still use
* rtThreadCtxHooksSolFree() at the same time. Hence the convulated reference counting.
*
* When this function is called from the owner thread and is the last reference, we call removectx() which
* will invoke rtThreadCtxHooksSolFree() with cRefs = 1 and that will then free the hook object.
*
* When the function is called from a different thread, we simply decrement the reference. Whenever the
* ring-0 thread dies, Solaris will call rtThreadCtxHooksSolFree() which will free the hook object.
*/
int rc;
if (g_frtSolOldThreadCtx)
{
rc = g_rtSolThreadCtx.Remove.pfnSol_removectx_old(curthread,
pThis,
rtThreadCtxHooksSolPreempting,
rtThreadCtxHooksSolResumed,
NULL, /* fork */
NULL, /* lwp_create */
rtThreadCtxHooksSolFree);
}
else
{
rc = g_rtSolThreadCtx.Remove.pfnSol_removectx(curthread,
pThis,
rtThreadCtxHooksSolPreempting,
rtThreadCtxHooksSolResumed,
NULL, /* fork */
NULL, /* lwp_create */
NULL, /* exit */
rtThreadCtxHooksSolFree);
}
AssertMsg(rc, ("removectx failed. rc=%d\n", rc));
NOREF(rc);
#ifdef VBOX_STRICT
cRefs = ASMAtomicReadU32(&pThis->cRefs);
Assert(!cRefs);
#endif
cRefs = 0;
}
else if (!cRefs)
{
/*
* The ring-0 thread for this hook object has already died. Free up the object as we have no more references.
*/
Assert(pThis->hOwner != RTThreadNativeSelf());
ASMAtomicWriteU32(&pThis->u32Magic, ~RTTHREADCTXINT_MAGIC);
RTMemFree(pThis);
}
return cRefs;
}
RTDECL(int) RTThreadCtxHooksRegister(RTTHREADCTX hThreadCtx, PFNRTTHREADCTXHOOK pfnThreadCtxHook, void *pvUser)
{
/*
* Validate input.
*/
PRTTHREADCTXINT pThis = hThreadCtx;
if (pThis == NIL_RTTHREADCTX)
return VINF_SUCCESS;
AssertPtr(pThis);
AssertMsgReturn(pThis->u32Magic == RTTHREADCTXINT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis),
VERR_INVALID_HANDLE);
Assert(pThis->hOwner == RTThreadNativeSelf());
/*
* Register the callback.
*/
pThis->pvUser = pvUser;
pThis->pfnThreadCtxHook = pfnThreadCtxHook;
pThis->fRegistered = true;
return VINF_SUCCESS;
}
RTDECL(int) RTThreadCtxHooksDeregister(RTTHREADCTX hThreadCtx)
{
/*
* Validate input.
*/
PRTTHREADCTXINT pThis = hThreadCtx;
if (pThis == NIL_RTTHREADCTX)
return VINF_SUCCESS;
AssertPtr(pThis);
AssertMsgReturn(pThis->u32Magic == RTTHREADCTXINT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis),
VERR_INVALID_HANDLE);
Assert(pThis->hOwner == RTThreadNativeSelf());
Assert(pThis->fRegistered);
/*
* Deregister the callback.
*/
pThis->fRegistered = false;
return VINF_SUCCESS;
}