VirtualBox

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

Last change on this file since 99743 was 98103, checked in by vboxsync, 2 years ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.0 KB
Line 
1/* $Id: threadctxhooks-r0drv-linux.c 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * IPRT - Thread Context Switching Hook, Ring-0 Driver, Linux.
4 */
5
6/*
7 * Copyright (C) 2013-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#include "the-linux-kernel.h"
42#include "internal/iprt.h"
43
44#include <iprt/mem.h>
45#include <iprt/assert.h>
46#include <iprt/thread.h>
47#include <iprt/errcore.h>
48#include <iprt/asm.h>
49#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
50# include <iprt/asm-amd64-x86.h>
51#endif
52#include "internal/thread.h"
53
54
55/*
56 * Linux kernel 2.6.23 introduced preemption notifiers but RedHat 2.6.18 kernels
57 * got it backported.
58 */
59#if RTLNX_VER_MIN(2,6,18) && defined(CONFIG_PREEMPT_NOTIFIERS)
60
61
62/*********************************************************************************************************************************
63* Structures and Typedefs *
64*********************************************************************************************************************************/
65/**
66 * The internal hook object for linux.
67 */
68typedef struct RTTHREADCTXHOOKINT
69{
70 /** Magic value (RTTHREADCTXHOOKINT_MAGIC). */
71 uint32_t volatile u32Magic;
72 /** The thread handle (owner) for which the hook is registered. */
73 RTNATIVETHREAD hOwner;
74 /** The preemption notifier object. */
75 struct preempt_notifier LnxPreemptNotifier;
76 /** Whether the hook is enabled or not. If enabled, the LnxPreemptNotifier
77 * is linked into the owning thread's list of preemption callouts. */
78 bool fEnabled;
79 /** Pointer to the user callback. */
80 PFNRTTHREADCTXHOOK pfnCallback;
81 /** User argument passed to the callback. */
82 void *pvUser;
83 /** The linux callbacks. */
84 struct preempt_ops PreemptOps;
85#if RTLNX_VER_MIN(3,1,19) && defined(RT_ARCH_AMD64)
86 /** Starting with 3.1.19, the linux kernel doesn't restore kernel RFLAGS during
87 * task switch, so we have to do that ourselves. (x86 code is not affected.) */
88 RTCCUINTREG fSavedRFlags;
89#endif
90} RTTHREADCTXHOOKINT;
91typedef RTTHREADCTXHOOKINT *PRTTHREADCTXHOOKINT;
92
93
94/**
95 * Hook function for the thread schedule out event.
96 *
97 * @param pPreemptNotifier Pointer to the preempt_notifier struct.
98 * @param pNext Pointer to the task that is being scheduled
99 * instead of the current thread.
100 *
101 * @remarks Called with the rq (runqueue) lock held and with preemption and
102 * interrupts disabled!
103 */
104static void rtThreadCtxHooksLnxSchedOut(struct preempt_notifier *pPreemptNotifier, struct task_struct *pNext)
105{
106 PRTTHREADCTXHOOKINT pThis = RT_FROM_MEMBER(pPreemptNotifier, RTTHREADCTXHOOKINT, LnxPreemptNotifier);
107#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
108 RTCCUINTREG fSavedEFlags = ASMGetFlags();
109 stac();
110#endif
111 RT_NOREF_PV(pNext);
112
113 AssertPtr(pThis);
114 AssertPtr(pThis->pfnCallback);
115 Assert(pThis->fEnabled);
116 Assert(!RTThreadPreemptIsEnabled(NIL_RTTHREAD));
117
118 pThis->pfnCallback(RTTHREADCTXEVENT_OUT, pThis->pvUser);
119
120#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
121 ASMSetFlags(fSavedEFlags);
122# if RTLNX_VER_MIN(3,1,19) && defined(RT_ARCH_AMD64)
123 pThis->fSavedRFlags = fSavedEFlags;
124# endif
125#endif
126}
127
128
129/**
130 * Hook function for the thread schedule in event.
131 *
132 * @param pPreemptNotifier Pointer to the preempt_notifier struct.
133 * @param iCpu The CPU this thread is being scheduled on.
134 *
135 * @remarks Called without holding the rq (runqueue) lock and with preemption
136 * enabled!
137 * @todo r=bird: Preemption is of course disabled when it is called.
138 */
139static void rtThreadCtxHooksLnxSchedIn(struct preempt_notifier *pPreemptNotifier, int iCpu)
140{
141 PRTTHREADCTXHOOKINT pThis = RT_FROM_MEMBER(pPreemptNotifier, RTTHREADCTXHOOKINT, LnxPreemptNotifier);
142#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
143 RTCCUINTREG fSavedEFlags = ASMGetFlags();
144 stac();
145#endif
146 RT_NOREF_PV(iCpu);
147
148 AssertPtr(pThis);
149 AssertPtr(pThis->pfnCallback);
150 Assert(pThis->fEnabled);
151
152 pThis->pfnCallback(RTTHREADCTXEVENT_IN, pThis->pvUser);
153
154#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
155# if RTLNX_VER_MIN(3,1,19) && defined(RT_ARCH_AMD64)
156 fSavedEFlags &= ~RT_BIT_64(18) /*X86_EFL_AC*/;
157 fSavedEFlags |= pThis->fSavedRFlags & RT_BIT_64(18) /*X86_EFL_AC*/;
158# endif
159 ASMSetFlags(fSavedEFlags);
160#endif
161}
162
163
164/**
165 * Worker function for RTThreadCtxHooks(Deregister|Release)().
166 *
167 * @param pThis Pointer to the internal thread-context object.
168 */
169DECLINLINE(void) rtThreadCtxHookDisable(PRTTHREADCTXHOOKINT pThis)
170{
171 Assert(pThis->PreemptOps.sched_out == rtThreadCtxHooksLnxSchedOut);
172 Assert(pThis->PreemptOps.sched_in == rtThreadCtxHooksLnxSchedIn);
173 preempt_disable();
174 preempt_notifier_unregister(&pThis->LnxPreemptNotifier);
175 pThis->fEnabled = false;
176 preempt_enable();
177}
178
179
180RTDECL(int) RTThreadCtxHookCreate(PRTTHREADCTXHOOK phCtxHook, uint32_t fFlags, PFNRTTHREADCTXHOOK pfnCallback, void *pvUser)
181{
182 IPRT_LINUX_SAVE_EFL_AC();
183
184 /*
185 * Validate input.
186 */
187 PRTTHREADCTXHOOKINT pThis;
188 Assert(RTThreadPreemptIsEnabled(NIL_RTTHREAD));
189 AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
190 AssertReturn(fFlags == 0, VERR_INVALID_FLAGS);
191
192 /*
193 * Allocate and initialize a new hook. We don't register it yet, just
194 * create it.
195 */
196 pThis = (PRTTHREADCTXHOOKINT)RTMemAllocZ(sizeof(*pThis));
197 if (RT_UNLIKELY(!pThis))
198 {
199 IPRT_LINUX_RESTORE_EFL_AC();
200 return VERR_NO_MEMORY;
201 }
202 pThis->u32Magic = RTTHREADCTXHOOKINT_MAGIC;
203 pThis->hOwner = RTThreadNativeSelf();
204 pThis->fEnabled = false;
205 pThis->pfnCallback = pfnCallback;
206 pThis->pvUser = pvUser;
207 preempt_notifier_init(&pThis->LnxPreemptNotifier, &pThis->PreemptOps);
208 pThis->PreemptOps.sched_out = rtThreadCtxHooksLnxSchedOut;
209 pThis->PreemptOps.sched_in = rtThreadCtxHooksLnxSchedIn;
210
211#if RTLNX_VER_MIN(4,2,0)
212 preempt_notifier_inc();
213#endif
214
215 *phCtxHook = pThis;
216 IPRT_LINUX_RESTORE_EFL_AC();
217 return VINF_SUCCESS;
218}
219RT_EXPORT_SYMBOL(RTThreadCtxHookCreate);
220
221
222RTDECL(int ) RTThreadCtxHookDestroy(RTTHREADCTXHOOK hCtxHook)
223{
224 IPRT_LINUX_SAVE_EFL_AC();
225
226 /*
227 * Validate input.
228 */
229 PRTTHREADCTXHOOKINT pThis = hCtxHook;
230 if (pThis == NIL_RTTHREADCTXHOOK)
231 return VINF_SUCCESS;
232 AssertPtr(pThis);
233 AssertMsgReturn(pThis->u32Magic == RTTHREADCTXHOOKINT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis),
234 VERR_INVALID_HANDLE);
235 Assert(RTThreadPreemptIsEnabled(NIL_RTTHREAD));
236 Assert(!pThis->fEnabled || pThis->hOwner == RTThreadNativeSelf());
237
238 /*
239 * If there's still a registered thread-context hook, deregister it now before destroying the object.
240 */
241 if (pThis->fEnabled)
242 {
243 Assert(pThis->hOwner == RTThreadNativeSelf());
244 rtThreadCtxHookDisable(pThis);
245 Assert(!pThis->fEnabled); /* paranoia */
246 }
247
248#if RTLNX_VER_MIN(4,2,0)
249 preempt_notifier_dec();
250#endif
251
252 ASMAtomicWriteU32(&pThis->u32Magic, ~RTTHREADCTXHOOKINT_MAGIC);
253 RTMemFree(pThis);
254
255 IPRT_LINUX_RESTORE_EFL_AC();
256 return VINF_SUCCESS;
257}
258RT_EXPORT_SYMBOL(RTThreadCtxHookDestroy);
259
260
261RTDECL(int) RTThreadCtxHookEnable(RTTHREADCTXHOOK hCtxHook)
262{
263 /*
264 * Validate input.
265 */
266 PRTTHREADCTXHOOKINT pThis = hCtxHook;
267 AssertPtr(pThis);
268 AssertMsgReturn(pThis->u32Magic == RTTHREADCTXHOOKINT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis),
269 VERR_INVALID_HANDLE);
270 Assert(pThis->hOwner == RTThreadNativeSelf());
271 Assert(!pThis->fEnabled);
272 if (!pThis->fEnabled)
273 {
274 IPRT_LINUX_SAVE_EFL_AC();
275 Assert(pThis->PreemptOps.sched_out == rtThreadCtxHooksLnxSchedOut);
276 Assert(pThis->PreemptOps.sched_in == rtThreadCtxHooksLnxSchedIn);
277
278 /*
279 * Register the callback.
280 */
281 preempt_disable();
282 pThis->fEnabled = true;
283 preempt_notifier_register(&pThis->LnxPreemptNotifier);
284 preempt_enable();
285
286 IPRT_LINUX_RESTORE_EFL_AC();
287 }
288
289 return VINF_SUCCESS;
290}
291RT_EXPORT_SYMBOL(RTThreadCtxHookEnable);
292
293
294RTDECL(int) RTThreadCtxHookDisable(RTTHREADCTXHOOK hCtxHook)
295{
296 /*
297 * Validate input.
298 */
299 PRTTHREADCTXHOOKINT pThis = hCtxHook;
300 if (pThis != NIL_RTTHREADCTXHOOK)
301 {
302 AssertPtr(pThis);
303 AssertMsgReturn(pThis->u32Magic == RTTHREADCTXHOOKINT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis),
304 VERR_INVALID_HANDLE);
305 Assert(pThis->hOwner == RTThreadNativeSelf());
306
307 /*
308 * Deregister the callback.
309 */
310 if (pThis->fEnabled)
311 {
312 IPRT_LINUX_SAVE_EFL_AC();
313 rtThreadCtxHookDisable(pThis);
314 IPRT_LINUX_RESTORE_EFL_AC();
315 }
316 }
317 return VINF_SUCCESS;
318}
319RT_EXPORT_SYMBOL(RTThreadCtxHookDisable);
320
321
322RTDECL(bool) RTThreadCtxHookIsEnabled(RTTHREADCTXHOOK hCtxHook)
323{
324 /*
325 * Validate input.
326 */
327 PRTTHREADCTXHOOKINT pThis = hCtxHook;
328 if (pThis == NIL_RTTHREADCTXHOOK)
329 return false;
330 AssertPtr(pThis);
331 AssertMsgReturn(pThis->u32Magic == RTTHREADCTXHOOKINT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis),
332 false);
333
334 return pThis->fEnabled;
335}
336RT_EXPORT_SYMBOL(RTThreadCtxHookIsEnabled);
337
338#else /* Not supported / Not needed */
339# include "../generic/threadctxhooks-r0drv-generic.cpp"
340#endif /* Not supported / Not needed */
341
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