VirtualBox

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

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

threadctxhooks-r0drv-linux.c: Looks like 3.19 stopped restoring kernel RFLAGS during context switches, so we need to handle RFLAGS.AC our selves.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.1 KB
Line 
1/* $Id: threadctxhooks-r0drv-linux.c 54872 2015-03-20 14:16:39Z vboxsync $ */
2/** @file
3 * IPRT - Thread-Context 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 * Linux kernel 2.6.23 introduced thread-context hooks but RedHat 2.6.18 kernels
46 * got it backported.
47 */
48#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18) && defined(CONFIG_PREEMPT_NOTIFIERS)
49
50/*******************************************************************************
51* Structures and Typedefs *
52*******************************************************************************/
53/**
54 * The internal thread-context object.
55 */
56typedef struct RTTHREADCTXINT
57{
58 /** Magic value (RTTHREADCTXINT_MAGIC). */
59 uint32_t volatile u32Magic;
60 /** The thread handle (owner) for which the context-hooks are registered. */
61 RTNATIVETHREAD hOwner;
62 /** The preemption notifier object. */
63 struct preempt_notifier hPreemptNotifier;
64 /** Whether this handle has any hooks registered or not. */
65 bool fRegistered;
66 /** Pointer to the registered thread-context hook. */
67 PFNRTTHREADCTXHOOK pfnThreadCtxHook;
68 /** User argument passed to the thread-context hook. */
69 void *pvUser;
70 /** The thread-context operations. */
71 struct preempt_ops hPreemptOps;
72 /** The reference count for this object. */
73 uint32_t volatile cRefs;
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} RTTHREADCTXINT, *PRTTHREADCTXINT;
80
81
82/**
83 * Hook function for the thread-preempting event.
84 *
85 * @param pPreemptNotifier Pointer to the preempt_notifier struct.
86 * @param pNext Pointer to the task that is preempting the
87 * current thread.
88 *
89 * @remarks Called with the rq (runqueue) lock held and with preemption and
90 * interrupts disabled!
91 */
92static void rtThreadCtxHooksLnxSchedOut(struct preempt_notifier *pPreemptNotifier, struct task_struct *pNext)
93{
94 PRTTHREADCTXINT pThis = RT_FROM_MEMBER(pPreemptNotifier, RTTHREADCTXINT, hPreemptNotifier);
95#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
96 RTCCUINTREG fSavedEFlags = ASMGetFlags();
97 stac();
98#endif
99
100 AssertPtr(pThis);
101 AssertPtr(pThis->pfnThreadCtxHook);
102 Assert(pThis->fRegistered);
103 Assert(!RTThreadPreemptIsEnabled(NIL_RTTHREAD));
104
105 pThis->pfnThreadCtxHook(RTTHREADCTXEVENT_PREEMPTING, pThis->pvUser);
106
107#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
108 ASMSetFlags(fSavedEFlags);
109# if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 19) && defined(RT_ARCH_AMD64)
110 pThis->fSavedRFlags = fSavedEFlags;
111# endif
112#endif
113}
114
115
116/**
117 * Hook function for the thread-resumed event.
118 *
119 * @param pPreemptNotifier Pointer to the preempt_notifier struct.
120 * @param iCpu The CPU this thread is scheduled on.
121 *
122 * @remarks Called without holding the rq (runqueue) lock and with preemption
123 * enabled!
124 */
125static void rtThreadCtxHooksLnxSchedIn(struct preempt_notifier *pPreemptNotifier, int iCpu)
126{
127 PRTTHREADCTXINT pThis = RT_FROM_MEMBER(pPreemptNotifier, RTTHREADCTXINT, hPreemptNotifier);
128#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
129 RTCCUINTREG fSavedEFlags = ASMGetFlags();
130 stac();
131#endif
132
133 AssertPtr(pThis);
134 AssertPtr(pThis->pfnThreadCtxHook);
135 Assert(pThis->fRegistered);
136
137 pThis->pfnThreadCtxHook(RTTHREADCTXEVENT_RESUMED, pThis->pvUser);
138
139#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
140# if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 19) && defined(RT_ARCH_AMD64)
141 fSavedEFlags &= ~RT_BIT_64(18) /*X86_EFL_AC*/;
142 fSavedEFlags |= pThis->fSavedRFlags & RT_BIT_64(18) /*X86_EFL_AC*/;
143# endif
144 ASMSetFlags(fSavedEFlags);
145#endif
146}
147
148
149/**
150 * Worker function for RTThreadCtxHooks(Deregister|Release)().
151 *
152 * @param pThis Pointer to the internal thread-context object.
153 */
154DECLINLINE(void) rtThreadCtxHooksDeregister(PRTTHREADCTXINT pThis)
155{
156 preempt_notifier_unregister(&pThis->hPreemptNotifier);
157 pThis->hPreemptOps.sched_out = NULL;
158 pThis->hPreemptOps.sched_in = NULL;
159 pThis->fRegistered = false;
160}
161
162
163RTDECL(int) RTThreadCtxHooksCreate(PRTTHREADCTX phThreadCtx)
164{
165 PRTTHREADCTXINT pThis;
166 Assert(RTThreadPreemptIsEnabled(NIL_RTTHREAD));
167
168 pThis = (PRTTHREADCTXINT)RTMemAllocZ(sizeof(*pThis));
169 if (RT_UNLIKELY(!pThis))
170 return VERR_NO_MEMORY;
171 pThis->u32Magic = RTTHREADCTXINT_MAGIC;
172 pThis->hOwner = RTThreadNativeSelf();
173 pThis->fRegistered = false;
174 preempt_notifier_init(&pThis->hPreemptNotifier, &pThis->hPreemptOps);
175 pThis->cRefs = 1;
176
177 *phThreadCtx = pThis;
178 return VINF_SUCCESS;
179}
180RT_EXPORT_SYMBOL(RTThreadCtxHooksCreate);
181
182
183RTDECL(uint32_t) RTThreadCtxHooksRetain(RTTHREADCTX hThreadCtx)
184{
185 /*
186 * Validate input.
187 */
188 uint32_t cRefs;
189 PRTTHREADCTXINT pThis = hThreadCtx;
190 AssertPtr(pThis);
191 AssertMsgReturn(pThis->u32Magic == RTTHREADCTXINT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis),
192 UINT32_MAX);
193
194 cRefs = ASMAtomicIncU32(&pThis->cRefs);
195 Assert(cRefs < UINT32_MAX / 2);
196 return cRefs;
197}
198RT_EXPORT_SYMBOL(RTThreadCtxHooksRetain);
199
200
201
202RTDECL(uint32_t) RTThreadCtxHooksRelease(RTTHREADCTX hThreadCtx)
203{
204 /*
205 * Validate input.
206 */
207 uint32_t cRefs;
208 PRTTHREADCTXINT pThis = hThreadCtx;
209 if (pThis == NIL_RTTHREADCTX)
210 return 0;
211
212 AssertPtr(pThis);
213 AssertMsgReturn(pThis->u32Magic == RTTHREADCTXINT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis),
214 UINT32_MAX);
215 Assert(RTThreadPreemptIsEnabled(NIL_RTTHREAD));
216
217 cRefs = ASMAtomicDecU32(&pThis->cRefs);
218 if (!cRefs)
219 {
220 /*
221 * If there's still a registered thread-context hook, deregister it now before destroying the object.
222 */
223 if (pThis->fRegistered)
224 rtThreadCtxHooksDeregister(pThis);
225
226 /*
227 * Paranoia... but since these are ring-0 threads we can't be too careful.
228 */
229 Assert(!pThis->fRegistered);
230 Assert(!pThis->hPreemptOps.sched_out);
231 Assert(!pThis->hPreemptOps.sched_in);
232
233 ASMAtomicWriteU32(&pThis->u32Magic, ~RTTHREADCTXINT_MAGIC);
234 RTMemFree(pThis);
235 }
236 else
237 Assert(cRefs < UINT32_MAX / 2);
238
239 return cRefs;
240}
241RT_EXPORT_SYMBOL(RTThreadCtxHooksRelease);
242
243
244RTDECL(int) RTThreadCtxHooksRegister(RTTHREADCTX hThreadCtx, PFNRTTHREADCTXHOOK pfnThreadCtxHook, void *pvUser)
245{
246 /*
247 * Validate input.
248 */
249 PRTTHREADCTXINT pThis = hThreadCtx;
250 if (pThis == NIL_RTTHREADCTX)
251 return VERR_INVALID_HANDLE;
252 AssertPtr(pThis);
253 AssertMsgReturn(pThis->u32Magic == RTTHREADCTXINT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis),
254 VERR_INVALID_HANDLE);
255 Assert(pThis->hOwner == RTThreadNativeSelf());
256 Assert(!pThis->hPreemptOps.sched_out);
257 Assert(!pThis->hPreemptOps.sched_in);
258
259 /*
260 * Register the callback.
261 */
262 pThis->hPreemptOps.sched_out = rtThreadCtxHooksLnxSchedOut;
263 pThis->hPreemptOps.sched_in = rtThreadCtxHooksLnxSchedIn;
264 pThis->pvUser = pvUser;
265 pThis->pfnThreadCtxHook = pfnThreadCtxHook;
266 pThis->fRegistered = true;
267 preempt_notifier_register(&pThis->hPreemptNotifier);
268
269 return VINF_SUCCESS;
270}
271RT_EXPORT_SYMBOL(RTThreadCtxHooksRegister);
272
273
274RTDECL(int) RTThreadCtxHooksDeregister(RTTHREADCTX hThreadCtx)
275{
276 /*
277 * Validate input.
278 */
279 PRTTHREADCTXINT pThis = hThreadCtx;
280 if (pThis == NIL_RTTHREADCTX)
281 return VERR_INVALID_HANDLE;
282 AssertPtr(pThis);
283 AssertMsgReturn(pThis->u32Magic == RTTHREADCTXINT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis),
284 VERR_INVALID_HANDLE);
285 Assert(pThis->hOwner == RTThreadNativeSelf());
286 Assert(pThis->fRegistered);
287
288 /*
289 * Deregister the callback.
290 */
291 rtThreadCtxHooksDeregister(pThis);
292 return VINF_SUCCESS;
293}
294RT_EXPORT_SYMBOL(RTThreadCtxHooksDeregister);
295
296
297RTDECL(bool) RTThreadCtxHooksAreRegistered(RTTHREADCTX hThreadCtx)
298{
299 /*
300 * Validate input.
301 */
302 PRTTHREADCTXINT pThis = hThreadCtx;
303 if (pThis == NIL_RTTHREADCTX)
304 return false;
305 AssertPtr(pThis);
306 AssertMsg(pThis->u32Magic == RTTHREADCTXINT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis));
307
308 return pThis->fRegistered;
309}
310
311#else /* Not supported / Not needed */
312
313RTDECL(int) RTThreadCtxHooksCreate(PRTTHREADCTX phThreadCtx)
314{
315 NOREF(phThreadCtx);
316 return VERR_NOT_SUPPORTED;
317}
318RT_EXPORT_SYMBOL(RTThreadCtxHooksCreate);
319
320
321RTDECL(uint32_t) RTThreadCtxHooksRetain(RTTHREADCTX hThreadCtx)
322{
323 NOREF(hThreadCtx);
324 return UINT32_MAX;
325}
326RT_EXPORT_SYMBOL(RTThreadCtxHooksRetain);
327
328
329RTDECL(uint32_t) RTThreadCtxHooksRelease(RTTHREADCTX hThreadCtx)
330{
331 NOREF(hThreadCtx);
332 return UINT32_MAX;
333}
334RT_EXPORT_SYMBOL(RTThreadCtxHooksRelease);
335
336
337RTDECL(int) RTThreadCtxHooksRegister(RTTHREADCTX hThreadCtx, PFNRTTHREADCTXHOOK pfnThreadCtxHook, void *pvUser)
338{
339 NOREF(hThreadCtx);
340 NOREF(pfnThreadCtxHook);
341 NOREF(pvUser);
342 return VERR_NOT_SUPPORTED;
343}
344RT_EXPORT_SYMBOL(RTThreadCtxHooksRegister);
345
346
347RTDECL(int) RTThreadCtxHooksDeregister(RTTHREADCTX hThreadCtx)
348{
349 NOREF(hThreadCtx);
350 return VERR_NOT_SUPPORTED;
351}
352RT_EXPORT_SYMBOL(RTThreadCtxHooksDeregister);
353
354
355RTDECL(bool) RTThreadCtxHooksAreRegistered(RTTHREADCTX hThreadCtx)
356{
357 NOREF(hThreadCtx);
358 return false;
359}
360RT_EXPORT_SYMBOL(RTThreadCtxHooksAreRegistered);
361
362#endif /* Not supported / Not needed */
363
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