VirtualBox

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

最後變更 在這個檔案從56955是 56955,由 vboxsync 提交於 10 年 前

Runtime/r0drv/threadctxhooks: Call preempt_notifier_inc/dec() for linux kernels 4.2.0+.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 10.1 KB
 
1/* $Id: threadctxhooks-r0drv-linux.c 56955 2015-07-16 16:16:16Z 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.alldomusa.eu.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#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0)
194 preempt_notifier_inc();
195#endif
196
197 *phCtxHook = pThis;
198 return VINF_SUCCESS;
199}
200RT_EXPORT_SYMBOL(RTThreadCtxHookCreate);
201
202
203RTDECL(int ) RTThreadCtxHookDestroy(RTTHREADCTXHOOK hCtxHook)
204{
205 /*
206 * Validate input.
207 */
208 PRTTHREADCTXHOOKINT pThis = hCtxHook;
209 if (pThis == NIL_RTTHREADCTXHOOK)
210 return VINF_SUCCESS;
211 AssertPtr(pThis);
212 AssertMsgReturn(pThis->u32Magic == RTTHREADCTXHOOKINT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis),
213 VERR_INVALID_HANDLE);
214 Assert(RTThreadPreemptIsEnabled(NIL_RTTHREAD));
215 Assert(!pThis->fEnabled || pThis->hOwner == RTThreadNativeSelf());
216
217 /*
218 * If there's still a registered thread-context hook, deregister it now before destroying the object.
219 */
220 if (pThis->fEnabled)
221 {
222 Assert(pThis->hOwner == RTThreadNativeSelf());
223 rtThreadCtxHookDisable(pThis);
224 Assert(!pThis->fEnabled); /* paranoia */
225 }
226
227#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0)
228 preempt_notifier_dec();
229#endif
230
231 ASMAtomicWriteU32(&pThis->u32Magic, ~RTTHREADCTXHOOKINT_MAGIC);
232 RTMemFree(pThis);
233
234 return VINF_SUCCESS;
235}
236RT_EXPORT_SYMBOL(RTThreadCtxHookDestroy);
237
238
239RTDECL(int) RTThreadCtxHookEnable(RTTHREADCTXHOOK hCtxHook)
240{
241 /*
242 * Validate input.
243 */
244 PRTTHREADCTXHOOKINT pThis = hCtxHook;
245 AssertPtr(pThis);
246 AssertMsgReturn(pThis->u32Magic == RTTHREADCTXHOOKINT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis),
247 VERR_INVALID_HANDLE);
248 Assert(pThis->hOwner == RTThreadNativeSelf());
249 Assert(!pThis->fEnabled);
250 if (!pThis->fEnabled)
251 {
252 Assert(pThis->PreemptOps.sched_out == rtThreadCtxHooksLnxSchedOut);
253 Assert(pThis->PreemptOps.sched_in == rtThreadCtxHooksLnxSchedIn);
254
255 /*
256 * Register the callback.
257 */
258 preempt_disable();
259 pThis->fEnabled = true;
260 preempt_notifier_register(&pThis->LnxPreemptNotifier);
261 preempt_enable();
262 }
263
264 return VINF_SUCCESS;
265}
266RT_EXPORT_SYMBOL(RTThreadCtxHookEnable);
267
268
269RTDECL(int) RTThreadCtxHookDisable(RTTHREADCTXHOOK hCtxHook)
270{
271 /*
272 * Validate input.
273 */
274 PRTTHREADCTXHOOKINT pThis = hCtxHook;
275 if (pThis != NIL_RTTHREADCTXHOOK)
276 {
277 AssertPtr(pThis);
278 AssertMsgReturn(pThis->u32Magic == RTTHREADCTXHOOKINT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis),
279 VERR_INVALID_HANDLE);
280 Assert(pThis->hOwner == RTThreadNativeSelf());
281
282 /*
283 * Deregister the callback.
284 */
285 if (pThis->fEnabled)
286 rtThreadCtxHookDisable(pThis);
287 }
288 return VINF_SUCCESS;
289}
290RT_EXPORT_SYMBOL(RTThreadCtxHookDisable);
291
292
293RTDECL(bool) RTThreadCtxHookIsEnabled(RTTHREADCTXHOOK hCtxHook)
294{
295 /*
296 * Validate input.
297 */
298 PRTTHREADCTXHOOKINT pThis = hCtxHook;
299 if (pThis == NIL_RTTHREADCTXHOOK)
300 return false;
301 AssertPtr(pThis);
302 AssertMsgReturn(pThis->u32Magic == RTTHREADCTXHOOKINT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis),
303 false);
304
305 return pThis->fEnabled;
306}
307
308#else /* Not supported / Not needed */
309# include "../generic/threadctxhooks-r0drv-generic.cpp"
310#endif /* Not supported / Not needed */
311
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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