VirtualBox

source: vbox/trunk/src/VBox/Runtime/r0drv/linux/threadctxhooks-r0drv-linux.c@ 55863

Last change on this file since 55863 was 55863, checked in by vboxsync, 10 years ago

IPRT,SUPDrv,VMM: Revised the context switching hook interface. Do less work when enabling the hook (formerly 'registration'). Drop the reference counting (kept internally for solaris) as it complicates restrictions wrt destroying enabled hooks. Bumped support driver version.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.0 KB
Line 
1/* $Id: threadctxhooks-r0drv-linux.c 55863 2015-05-14 18:29:34Z vboxsync $ */
2/** @file
3 * IPRT - Thread Context Switching Hook, Ring-0 Driver, Linux.
4 */
5
6/*
7 * Copyright (C) 2013-2015 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
28/*******************************************************************************
29* Header Files *
30*******************************************************************************/
31#include "the-linux-kernel.h"
32#include "internal/iprt.h"
33
34#include <iprt/mem.h>
35#include <iprt/assert.h>
36#include <iprt/thread.h>
37#include <iprt/err.h>
38#include <iprt/asm.h>
39#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
40# include <iprt/asm-amd64-x86.h>
41#endif
42#include "internal/thread.h"
43
44
45/*
46 * Linux kernel 2.6.23 introduced preemption notifiers but RedHat 2.6.18 kernels
47 * got it backported.
48 */
49#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18) && defined(CONFIG_PREEMPT_NOTIFIERS)
50
51/*******************************************************************************
52* Structures and Typedefs *
53*******************************************************************************/
54/**
55 * The internal hook object for linux.
56 */
57typedef struct RTTHREADCTXHOOKINT
58{
59 /** Magic value (RTTHREADCTXHOOKINT_MAGIC). */
60 uint32_t volatile u32Magic;
61 /** The thread handle (owner) for which the hook is registered. */
62 RTNATIVETHREAD hOwner;
63 /** The preemption notifier object. */
64 struct preempt_notifier LnxPreemptNotifier;
65 /** Whether the hook is enabled or not. If enabled, the LnxPreemptNotifier
66 * is linked into the owning thread's list of preemption callouts. */
67 bool fEnabled;
68 /** Pointer to the user callback. */
69 PFNRTTHREADCTXHOOK pfnCallback;
70 /** User argument passed to the callback. */
71 void *pvUser;
72 /** The linux callbacks. */
73 struct preempt_ops PreemptOps;
74#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 19) && defined(RT_ARCH_AMD64)
75 /** Starting with 3.1.19, the linux kernel doesn't restore kernel RFLAGS during
76 * task switch, so we have to do that ourselves. (x86 code is not affected.) */
77 RTCCUINTREG fSavedRFlags;
78#endif
79} RTTHREADCTXHOOKINT;
80typedef RTTHREADCTXHOOKINT *PRTTHREADCTXHOOKINT;
81
82
83/**
84 * Hook function for the thread schedule out event.
85 *
86 * @param pPreemptNotifier Pointer to the preempt_notifier struct.
87 * @param pNext Pointer to the task that is being scheduled
88 * instead of the current thread.
89 *
90 * @remarks Called with the rq (runqueue) lock held and with preemption and
91 * interrupts disabled!
92 */
93static void rtThreadCtxHooksLnxSchedOut(struct preempt_notifier *pPreemptNotifier, struct task_struct *pNext)
94{
95 PRTTHREADCTXHOOKINT pThis = RT_FROM_MEMBER(pPreemptNotifier, RTTHREADCTXHOOKINT, LnxPreemptNotifier);
96#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
97 RTCCUINTREG fSavedEFlags = ASMGetFlags();
98 stac();
99#endif
100
101 AssertPtr(pThis);
102 AssertPtr(pThis->pfnCallback);
103 Assert(pThis->fEnabled);
104 Assert(!RTThreadPreemptIsEnabled(NIL_RTTHREAD));
105
106 pThis->pfnCallback(RTTHREADCTXEVENT_OUT, pThis->pvUser);
107
108#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
109 ASMSetFlags(fSavedEFlags);
110# if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 19) && defined(RT_ARCH_AMD64)
111 pThis->fSavedRFlags = fSavedEFlags;
112# endif
113#endif
114}
115
116
117/**
118 * Hook function for the thread schedule in event.
119 *
120 * @param pPreemptNotifier Pointer to the preempt_notifier struct.
121 * @param iCpu The CPU this thread is being scheduled on.
122 *
123 * @remarks Called without holding the rq (runqueue) lock and with preemption
124 * enabled!
125 * @todo r=bird: Preemption is of course disabled when it is called.
126 */
127static void rtThreadCtxHooksLnxSchedIn(struct preempt_notifier *pPreemptNotifier, int iCpu)
128{
129 PRTTHREADCTXHOOKINT pThis = RT_FROM_MEMBER(pPreemptNotifier, RTTHREADCTXHOOKINT, LnxPreemptNotifier);
130#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
131 RTCCUINTREG fSavedEFlags = ASMGetFlags();
132 stac();
133#endif
134
135 AssertPtr(pThis);
136 AssertPtr(pThis->pfnCallback);
137 Assert(pThis->fEnabled);
138
139 pThis->pfnCallback(RTTHREADCTXEVENT_IN, pThis->pvUser);
140
141#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
142# if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 19) && defined(RT_ARCH_AMD64)
143 fSavedEFlags &= ~RT_BIT_64(18) /*X86_EFL_AC*/;
144 fSavedEFlags |= pThis->fSavedRFlags & RT_BIT_64(18) /*X86_EFL_AC*/;
145# endif
146 ASMSetFlags(fSavedEFlags);
147#endif
148}
149
150
151/**
152 * Worker function for RTThreadCtxHooks(Deregister|Release)().
153 *
154 * @param pThis Pointer to the internal thread-context object.
155 */
156DECLINLINE(void) rtThreadCtxHookDisable(PRTTHREADCTXHOOKINT pThis)
157{
158 Assert(pThis->PreemptOps.sched_out == rtThreadCtxHooksLnxSchedOut);
159 Assert(pThis->PreemptOps.sched_in == rtThreadCtxHooksLnxSchedIn);
160 preempt_disable();
161 preempt_notifier_unregister(&pThis->LnxPreemptNotifier);
162 pThis->fEnabled = false;
163 preempt_enable();
164}
165
166
167RTDECL(int) RTThreadCtxHookCreate(PRTTHREADCTXHOOK phCtxHook, uint32_t fFlags, PFNRTTHREADCTXHOOK pfnCallback, void *pvUser)
168{
169 /*
170 * Validate input.
171 */
172 PRTTHREADCTXHOOKINT pThis;
173 Assert(RTThreadPreemptIsEnabled(NIL_RTTHREAD));
174 AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
175 AssertReturn(fFlags == 0, VERR_INVALID_FLAGS);
176
177 /*
178 * Allocate and initialize a new hook. We don't register it yet, just
179 * create it.
180 */
181 pThis = (PRTTHREADCTXHOOKINT)RTMemAllocZ(sizeof(*pThis));
182 if (RT_UNLIKELY(!pThis))
183 return VERR_NO_MEMORY;
184 pThis->u32Magic = RTTHREADCTXHOOKINT_MAGIC;
185 pThis->hOwner = RTThreadNativeSelf();
186 pThis->fEnabled = false;
187 pThis->pfnCallback = pfnCallback;
188 pThis->pvUser = pvUser;
189 preempt_notifier_init(&pThis->LnxPreemptNotifier, &pThis->PreemptOps);
190 pThis->PreemptOps.sched_out = rtThreadCtxHooksLnxSchedOut;
191 pThis->PreemptOps.sched_in = rtThreadCtxHooksLnxSchedIn;
192
193 *phCtxHook = pThis;
194 return VINF_SUCCESS;
195}
196RT_EXPORT_SYMBOL(RTThreadCtxHookCreate);
197
198
199RTDECL(int ) RTThreadCtxHookDestroy(RTTHREADCTXHOOK hCtxHook)
200{
201 /*
202 * Validate input.
203 */
204 PRTTHREADCTXHOOKINT pThis = hCtxHook;
205 if (pThis == NIL_RTTHREADCTXHOOK)
206 return VINF_SUCCESS;
207 AssertPtr(pThis);
208 AssertMsgReturn(pThis->u32Magic == RTTHREADCTXHOOKINT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis),
209 VERR_INVALID_HANDLE);
210 Assert(RTThreadPreemptIsEnabled(NIL_RTTHREAD));
211 Assert(pThis->fEnabled || pThis->hOwner == RTThreadNativeSelf());
212
213 /*
214 * If there's still a registered thread-context hook, deregister it now before destroying the object.
215 */
216 if (pThis->fEnabled)
217 {
218 Assert(pThis->hOwner == RTThreadNativeSelf());
219 rtThreadCtxHookDisable(pThis);
220 Assert(!pThis->fEnabled); /* paranoia */
221 }
222
223 ASMAtomicWriteU32(&pThis->u32Magic, ~RTTHREADCTXHOOKINT_MAGIC);
224 RTMemFree(pThis);
225
226 return VINF_SUCCESS;
227}
228RT_EXPORT_SYMBOL(RTThreadCtxHookDestroy);
229
230
231RTDECL(int) RTThreadCtxHookEnable(RTTHREADCTXHOOK hCtxHook)
232{
233 /*
234 * Validate input.
235 */
236 PRTTHREADCTXHOOKINT pThis = hCtxHook;
237 AssertPtr(pThis);
238 AssertMsgReturn(pThis->u32Magic == RTTHREADCTXHOOKINT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis),
239 VERR_INVALID_HANDLE);
240 Assert(pThis->hOwner == RTThreadNativeSelf());
241 Assert(!pThis->fEnabled);
242 if (!pThis->fEnabled)
243 {
244 Assert(pThis->PreemptOps.sched_out == rtThreadCtxHooksLnxSchedOut);
245 Assert(pThis->PreemptOps.sched_in == rtThreadCtxHooksLnxSchedIn);
246
247 /*
248 * Register the callback.
249 */
250 preempt_disable();
251 pThis->fEnabled = true;
252 preempt_notifier_register(&pThis->LnxPreemptNotifier);
253 preempt_enable();
254 }
255
256 return VINF_SUCCESS;
257}
258RT_EXPORT_SYMBOL(RTThreadCtxHookEnable);
259
260
261RTDECL(int) RTThreadCtxHookDisable(RTTHREADCTXHOOK hCtxHook)
262{
263 /*
264 * Validate input.
265 */
266 PRTTHREADCTXHOOKINT pThis = hCtxHook;
267 if (pThis != NIL_RTTHREADCTXHOOK)
268 {
269 AssertPtr(pThis);
270 AssertMsgReturn(pThis->u32Magic == RTTHREADCTXHOOKINT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis),
271 VERR_INVALID_HANDLE);
272 Assert(pThis->hOwner == RTThreadNativeSelf());
273
274 /*
275 * Deregister the callback.
276 */
277 if (pThis->fEnabled)
278 rtThreadCtxHookDisable(pThis);
279 }
280 return VINF_SUCCESS;
281}
282RT_EXPORT_SYMBOL(RTThreadCtxHookDisable);
283
284
285RTDECL(bool) RTThreadCtxHookIsEnabled(RTTHREADCTXHOOK hCtxHook)
286{
287 /*
288 * Validate input.
289 */
290 PRTTHREADCTXHOOKINT pThis = hCtxHook;
291 if (pThis == NIL_RTTHREADCTXHOOK)
292 return false;
293 AssertPtr(pThis);
294 AssertMsgReturn(pThis->u32Magic == RTTHREADCTXHOOKINT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis),
295 false);
296
297 return pThis->fEnabled;
298}
299
300#else /* Not supported / Not needed */
301# include "../generic/threadctxhooks-r0drv-generic.cpp"
302#endif /* Not supported / Not needed */
303
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette