threadctxhooks-r0drv-linux.c revision 7d7c3dba7ea44027b3adf273ef13b638271a4f58
2N/A/* $Id$ */
2N/A/** @file
2N/A * IPRT - Thread-Context Hook, Ring-0 Driver, Linux.
2N/A */
2N/A
2N/A/*
2N/A * Copyright (C) 2013 Oracle Corporation
2N/A *
2N/A * This file is part of VirtualBox Open Source Edition (OSE), as
2N/A * available from http://www.virtualbox.org. This file is free software;
2N/A * you can redistribute it and/or modify it under the terms of the GNU
2N/A * General Public License (GPL) as published by the Free Software
2N/A * Foundation, in version 2 as it comes in the "COPYING" file of the
2N/A * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
2N/A * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
2N/A *
2N/A * The contents of this file may alternatively be used under the terms
2N/A * of the Common Development and Distribution License Version 1.0
2N/A * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
2N/A * VirtualBox OSE distribution, in which case the provisions of the
2N/A * CDDL are applicable instead of those of the GPL.
2N/A *
2N/A * You may elect to license modified versions of this file under the
2N/A * terms and conditions of either the GPL or the CDDL or both.
2N/A */
2N/A
2N/A
2N/A/*******************************************************************************
2N/A* Header Files *
2N/A*******************************************************************************/
2N/A#include "the-linux-kernel.h"
2N/A#include "internal/iprt.h"
2N/A
2N/A#include <iprt/mem.h>
2N/A#include <iprt/assert.h>
2N/A#include <iprt/thread.h>
2N/A#include <iprt/err.h>
2N/A#include <iprt/asm.h>
2N/A#include "internal/thread.h"
2N/A
2N/A#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23) && defined(CONFIG_PREEMPT_NOTIFIERS)
2N/A
2N/A/*******************************************************************************
2N/A* Structures and Typedefs *
2N/A*******************************************************************************/
2N/A/**
2N/A * The internal thread-context object.
2N/A */
2N/Atypedef struct RTTHREADCTXINT
2N/A{
2N/A /** Magic value (RTTHREADCTXINT_MAGIC). */
2N/A uint32_t volatile u32Magic;
2N/A /** The thread handle (owner) for which the context-hooks are registered. */
2N/A RTTHREAD hOwner;
2N/A /** The preemption notifier object. */
2N/A struct preempt_notifier hPreemptNotifier;
2N/A /** Whether this handle has any hooks registered or not. */
2N/A bool fRegistered;
2N/A /** Pointer to the registered thread-context hook. */
2N/A PFNRTTHREADCTXHOOK pfnThreadCtxHook;
2N/A /** User argument passed to the thread-context hook. */
2N/A void *pvUser;
2N/A /** The thread-context operations. */
2N/A struct preempt_ops hPreemptOps;
2N/A} RTTHREADCTXINT, *PRTTHREADCTXINT;
2N/A
2N/A
2N/A/**
2N/A * Hook function for the thread-preempting event.
2N/A *
2N/A * @param pPreemptNotifier Pointer to the preempt_notifier struct.
2N/A * @param pNext Pointer to the task that is preempting the
2N/A * current thread.
2N/A *
2N/A * @remarks Called with the rq (runqueue) lock held and with preemption
2N/A * disabled!
2N/A */
2N/Astatic void rtThreadCtxHooksLnxSchedOut(struct preempt_notifier *pPreemptNotifier, struct task_struct *pNext)
2N/A{
2N/A PRTTHREADCTXINT pThis = RT_FROM_MEMBER(pPreemptNotifier, RTTHREADCTXINT, hPreemptNotifier);
2N/A AssertPtr(pThis);
2N/A AssertPtr(pThis->pfnThreadCtxHook);
2N/A AssertPtr(pThis->fRegistered);
2N/A Assert(!RTThreadPreemptIsEnabled(NIL_RTTHREAD));
2N/A
2N/A pThis->pfnThreadCtxHook(RTTHREADCTXEVENT_PREEMPTING, pThis->pvUser);
2N/A}
2N/A
2N/A
2N/A/**
2N/A * Hook function for the thread-resumed event.
2N/A *
2N/A * @param pPreemptNotifier Pointer to the preempt_notifier struct.
2N/A * @param iCpu The CPU this thread is scheduled on.
2N/A *
2N/A * @remarks Called without holding the rq (runqueue) lock and with preemption
2N/A * enabled!
2N/A */
2N/Astatic void rtThreadCtxHooksLnxSchedIn(struct preempt_notifier *pPreemptNotifier, int iCpu)
2N/A{
2N/A PRTTHREADCTXINT pThis = RT_FROM_MEMBER(pPreemptNotifier, RTTHREADCTXINT, hPreemptNotifier);
2N/A AssertPtr(pThis);
2N/A AssertPtr(pThis->pfnThreadCtxHook);
2N/A AssertPtr(pThis->fRegistered);
2N/A
2N/A pThis->pfnThreadCtxHook(RTTHREADCTXEVENT_RESUMED, pThis->pvUser);
2N/A}
2N/A
2N/A
2N/A/**
2N/A * Worker function for RTThreadCtxHooks(Deregister|Destroy)().
2N/A *
2N/A * @param pThis Pointer to the internal thread-context object.
2N/A */
2N/ADECLINLINE(void) rtThreadCtxHooksDeregister(PRTTHREADCTXINT pThis)
2N/A{
2N/A preempt_notifier_unregister(&pThis->hPreemptNotifier);
2N/A pThis->hPreemptOps.sched_out = NULL;
2N/A pThis->hPreemptOps.sched_in = NULL;
2N/A pThis->fRegistered = false;
2N/A}
2N/A
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 = RTThreadSelf();
pThis->fRegistered = false;
preempt_notifier_init(&pThis->hPreemptNotifier, &pThis->hPreemptOps);
*phThreadCtx = pThis;
return VINF_SUCCESS;
}
RT_EXPORT_SYMBOL(RTThreadCtxHooksCreate);
RTDECL(void) RTThreadCtxHooksDestroy(RTTHREADCTX hThreadCtx)
{
/*
* Validate input.
*/
PRTTHREADCTXINT pThis = hThreadCtx;
if (pThis == NIL_RTTHREADCTX)
return;
AssertPtr(pThis);
AssertMsgReturnVoid(pThis->u32Magic == RTTHREADCTXINT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis));
Assert(pThis->hOwner == RTThreadSelf());
Assert(RTThreadPreemptIsEnabled(NIL_RTTHREAD));
/*
* If there's still a registered thread-context hook, deregister it now before destroying the object.
*/
if (pThis->fRegistered)
rtThreadCtxHooksDeregister(pThis);
/*
* Paranoia... but since these are ring-0 threads we can't be too careful.
*/
Assert(!pThis->fRegistered);
Assert(!pThis->hPreemptOps.sched_out);
Assert(!pThis->hPreemptOps.sched_in);
/*
* Destroy the object.
*/
ASMAtomicWriteU32(&pThis->u32Magic, ~RTTHREADCTXINT_MAGIC);
RTMemFree(pThis);
}
RT_EXPORT_SYMBOL(RTThreadCtxHooksDestroy);
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 == RTThreadSelf());
Assert(!pThis->hPreemptOps.sched_out);
Assert(!pThis->hPreemptOps.sched_in);
/*
* Register the callback.
*/
pThis->hPreemptOps.sched_out = rtThreadCtxHooksLnxSchedOut;
pThis->hPreemptOps.sched_in = rtThreadCtxHooksLnxSchedIn;
pThis->pvUser = pvUser;
pThis->pfnThreadCtxHook = pfnThreadCtxHook;
pThis->fRegistered = true;
preempt_notifier_register(&pThis->hPreemptNotifier);
return VINF_SUCCESS;
}
RT_EXPORT_SYMBOL(RTThreadCtxHooksRegister);
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 == RTThreadSelf());
Assert(pThis->fRegistered);
/*
* Deregister the callback.
*/
rtThreadCtxHooksDeregister(pThis);
return VINF_SUCCESS;
}
RT_EXPORT_SYMBOL(RTThreadCtxHooksDeregister);
#else /* Not supported / Not needed */
RTDECL(int) RTThreadCtxHooksCreate(PRTTHREADCTX phThreadCtx)
{
NOREF(phThreadCtx);
return VERR_NOT_SUPPORTED;
}
RT_EXPORT_SYMBOL(RTThreadCtxHooksCreate);
RTDECL(void) RTThreadCtxHooksDestroy(RTTHREADCTX hThreadCtx)
{
NOREF(hThreadCtx);
}
RT_EXPORT_SYMBOL(RTThreadCtxHooksDestroy);
RTDECL(int) RTThreadCtxHooksRegister(RTTHREADCTX hThreadCtx, PFNRTTHREADCTXHOOK pfnThreadCtxHook, void *pvUser)
{
NOREF(hThreadCtx);
NOREF(pfnThreadCtxHook);
NOREF(pvUser);
return VERR_NOT_SUPPORTED;
}
RT_EXPORT_SYMBOL(RTThreadCtxHooksRegister);
RTDECL(int) RTThreadCtxHooksDeregister(RTTHREADCTX hThreadCtx)
{
NOREF(hThreadCtx);
return VERR_NOT_SUPPORTED;
}
RT_EXPORT_SYMBOL(RTThreadCtxHooksDeregister);
#endif /* Not supported / Not needed */