VirtualBox

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

Last change on this file since 57276 was 57276, checked in by vboxsync, 9 years ago

iprt/r0drv/linux: Preserve EFLAGS/AC where ever it may possibly be thought to change when calling kernel code.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.5 KB
Line 
1/* $Id: threadctxhooks-r0drv-linux.c 57276 2015-08-11 14:39:19Z 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 IPRT_LINUX_SAVE_EFL_AC();
170
171 /*
172 * Validate input.
173 */
174 PRTTHREADCTXHOOKINT pThis;
175 Assert(RTThreadPreemptIsEnabled(NIL_RTTHREAD));
176 AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
177 AssertReturn(fFlags == 0, VERR_INVALID_FLAGS);
178
179 /*
180 * Allocate and initialize a new hook. We don't register it yet, just
181 * create it.
182 */
183 pThis = (PRTTHREADCTXHOOKINT)RTMemAllocZ(sizeof(*pThis));
184 if (RT_UNLIKELY(!pThis))
185 {
186 IPRT_LINUX_RESTORE_EFL_AC();
187 return VERR_NO_MEMORY;
188 }
189 pThis->u32Magic = RTTHREADCTXHOOKINT_MAGIC;
190 pThis->hOwner = RTThreadNativeSelf();
191 pThis->fEnabled = false;
192 pThis->pfnCallback = pfnCallback;
193 pThis->pvUser = pvUser;
194 preempt_notifier_init(&pThis->LnxPreemptNotifier, &pThis->PreemptOps);
195 pThis->PreemptOps.sched_out = rtThreadCtxHooksLnxSchedOut;
196 pThis->PreemptOps.sched_in = rtThreadCtxHooksLnxSchedIn;
197
198#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0)
199 preempt_notifier_inc();
200#endif
201
202 *phCtxHook = pThis;
203 IPRT_LINUX_RESTORE_EFL_AC();
204 return VINF_SUCCESS;
205}
206RT_EXPORT_SYMBOL(RTThreadCtxHookCreate);
207
208
209RTDECL(int ) RTThreadCtxHookDestroy(RTTHREADCTXHOOK hCtxHook)
210{
211 IPRT_LINUX_SAVE_EFL_AC();
212
213 /*
214 * Validate input.
215 */
216 PRTTHREADCTXHOOKINT pThis = hCtxHook;
217 if (pThis == NIL_RTTHREADCTXHOOK)
218 return VINF_SUCCESS;
219 AssertPtr(pThis);
220 AssertMsgReturn(pThis->u32Magic == RTTHREADCTXHOOKINT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis),
221 VERR_INVALID_HANDLE);
222 Assert(RTThreadPreemptIsEnabled(NIL_RTTHREAD));
223 Assert(!pThis->fEnabled || pThis->hOwner == RTThreadNativeSelf());
224
225 /*
226 * If there's still a registered thread-context hook, deregister it now before destroying the object.
227 */
228 if (pThis->fEnabled)
229 {
230 Assert(pThis->hOwner == RTThreadNativeSelf());
231 rtThreadCtxHookDisable(pThis);
232 Assert(!pThis->fEnabled); /* paranoia */
233 }
234
235#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0)
236 preempt_notifier_dec();
237#endif
238
239 ASMAtomicWriteU32(&pThis->u32Magic, ~RTTHREADCTXHOOKINT_MAGIC);
240 RTMemFree(pThis);
241
242 IPRT_LINUX_RESTORE_EFL_AC();
243 return VINF_SUCCESS;
244}
245RT_EXPORT_SYMBOL(RTThreadCtxHookDestroy);
246
247
248RTDECL(int) RTThreadCtxHookEnable(RTTHREADCTXHOOK hCtxHook)
249{
250 /*
251 * Validate input.
252 */
253 PRTTHREADCTXHOOKINT pThis = hCtxHook;
254 AssertPtr(pThis);
255 AssertMsgReturn(pThis->u32Magic == RTTHREADCTXHOOKINT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis),
256 VERR_INVALID_HANDLE);
257 Assert(pThis->hOwner == RTThreadNativeSelf());
258 Assert(!pThis->fEnabled);
259 if (!pThis->fEnabled)
260 {
261 IPRT_LINUX_SAVE_EFL_AC();
262 Assert(pThis->PreemptOps.sched_out == rtThreadCtxHooksLnxSchedOut);
263 Assert(pThis->PreemptOps.sched_in == rtThreadCtxHooksLnxSchedIn);
264
265 /*
266 * Register the callback.
267 */
268 preempt_disable();
269 pThis->fEnabled = true;
270 preempt_notifier_register(&pThis->LnxPreemptNotifier);
271 preempt_enable();
272
273 IPRT_LINUX_RESTORE_EFL_AC();
274 }
275
276 return VINF_SUCCESS;
277}
278RT_EXPORT_SYMBOL(RTThreadCtxHookEnable);
279
280
281RTDECL(int) RTThreadCtxHookDisable(RTTHREADCTXHOOK hCtxHook)
282{
283 /*
284 * Validate input.
285 */
286 PRTTHREADCTXHOOKINT pThis = hCtxHook;
287 if (pThis != NIL_RTTHREADCTXHOOK)
288 {
289 AssertPtr(pThis);
290 AssertMsgReturn(pThis->u32Magic == RTTHREADCTXHOOKINT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis),
291 VERR_INVALID_HANDLE);
292 Assert(pThis->hOwner == RTThreadNativeSelf());
293
294 /*
295 * Deregister the callback.
296 */
297 if (pThis->fEnabled)
298 {
299 IPRT_LINUX_SAVE_EFL_AC();
300 rtThreadCtxHookDisable(pThis);
301 IPRT_LINUX_RESTORE_EFL_AC();
302 }
303 }
304 return VINF_SUCCESS;
305}
306RT_EXPORT_SYMBOL(RTThreadCtxHookDisable);
307
308
309RTDECL(bool) RTThreadCtxHookIsEnabled(RTTHREADCTXHOOK hCtxHook)
310{
311 /*
312 * Validate input.
313 */
314 PRTTHREADCTXHOOKINT pThis = hCtxHook;
315 if (pThis == NIL_RTTHREADCTXHOOK)
316 return false;
317 AssertPtr(pThis);
318 AssertMsgReturn(pThis->u32Magic == RTTHREADCTXHOOKINT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis),
319 false);
320
321 return pThis->fEnabled;
322}
323
324#else /* Not supported / Not needed */
325# include "../generic/threadctxhooks-r0drv-generic.cpp"
326#endif /* Not supported / Not needed */
327
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