VirtualBox

source: vbox/trunk/src/VBox/Runtime/r0drv/solaris/threadctxhooks-r0drv-solaris.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: 13.1 KB
Line 
1/* $Id: threadctxhooks-r0drv-solaris.c 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * IPRT - Thread Context Switching Hook, Ring-0 Driver, Solaris.
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-solaris-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#include <iprt/log.h>
50#include "internal/thread.h"
51
52
53/*********************************************************************************************************************************
54* Structures and Typedefs *
55*********************************************************************************************************************************/
56/**
57 * The internal hook object for solaris.
58 */
59typedef struct RTTHREADCTXHOOKINT
60{
61 /** Magic value (RTTHREADCTXHOOKINT_MAGIC). */
62 uint32_t volatile u32Magic;
63 /** The thread handle (owner) for which the context-hooks are registered. */
64 RTNATIVETHREAD hOwner;
65 /** Pointer to the registered callback function. */
66 PFNRTTHREADCTXHOOK pfnCallback;
67 /** User argument passed to the callback function. */
68 void *pvUser;
69 /** Whether the hook is enabled or not. */
70 bool volatile fEnabled;
71 /** Number of references to this object. */
72 uint32_t volatile cRefs;
73} RTTHREADCTXHOOKINT;
74typedef RTTHREADCTXHOOKINT *PRTTHREADCTXHOOKINT;
75
76
77/*********************************************************************************************************************************
78* Defined Constants And Macros *
79*********************************************************************************************************************************/
80/** Validates a hook handle and returns rc if not valid. */
81#define RTTHREADCTX_VALID_RETURN_RC(pThis, rc) \
82 do { \
83 AssertPtrReturn((pThis), (rc)); \
84 AssertReturn((pThis)->u32Magic == RTTHREADCTXHOOKINT_MAGIC, (rc)); \
85 AssertReturn((pThis)->cRefs > 0, (rc)); \
86 } while (0)
87
88
89/**
90 * Hook function for the thread-save event.
91 *
92 * @param pvThreadCtxInt Opaque pointer to the internal hook object.
93 *
94 * @remarks Called with the with preemption disabled!
95 */
96static void rtThreadCtxHookSolOut(void *pvThreadCtxInt)
97{
98 PRTTHREADCTXHOOKINT pThis = (PRTTHREADCTXHOOKINT)pvThreadCtxInt;
99 AssertPtr(pThis);
100 Assert(!RTThreadPreemptIsEnabled(NIL_RTTHREAD));
101 Assert(pThis->cRefs > 0);
102
103 if (pThis->fEnabled)
104 {
105 Assert(pThis->pfnCallback);
106 pThis->pfnCallback(RTTHREADCTXEVENT_OUT, pThis->pvUser);
107 }
108}
109
110
111/**
112 * Hook function for the thread-restore event.
113 *
114 * @param pvThreadCtxInt Opaque pointer to the internal hook object.
115 *
116 * @remarks Called with preemption disabled!
117 */
118static void rtThreadCtxHookSolIn(void *pvThreadCtxInt)
119{
120 PRTTHREADCTXHOOKINT pThis = (PRTTHREADCTXHOOKINT)pvThreadCtxInt;
121 AssertPtr(pThis);
122 Assert(!RTThreadPreemptIsEnabled(NIL_RTTHREAD));
123 Assert(pThis->cRefs > 0);
124
125 if (pThis->fEnabled)
126 {
127 Assert(pThis->pfnCallback);
128 pThis->pfnCallback(RTTHREADCTXEVENT_IN, pThis->pvUser);
129 }
130}
131
132
133/**
134 * Hook function for the thread-free event.
135 *
136 * This is used for making sure the hook object is safely released - see
137 * RTThreadCtxHookRelease for details.
138 *
139 * @param pvThreadCtxInt Opaque pointer to the internal hook object.
140 * @param fIsExec Whether this event is triggered due to exec().
141 */
142static void rtThreadCtxHookSolFree(void *pvThreadCtxInt, int fIsExec)
143{
144 PRTTHREADCTXHOOKINT pThis = (PRTTHREADCTXHOOKINT)pvThreadCtxInt;
145 AssertPtrReturnVoid(pThis);
146 AssertMsgReturnVoid(pThis->u32Magic == RTTHREADCTXHOOKINT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis));
147
148 uint32_t cRefs = ASMAtomicReadU32(&pThis->cRefs);
149 if (cRefs > 0)
150 {
151 cRefs = ASMAtomicDecU32(&pThis->cRefs);
152 if (!cRefs)
153 {
154 Assert(!pThis->fEnabled);
155 ASMAtomicWriteU32(&pThis->u32Magic, ~RTTHREADCTXHOOKINT_MAGIC);
156 RTMemFree(pThis);
157 }
158 }
159 else
160 {
161 /* Should never happen. */
162 AssertMsgFailed(("rtThreadCtxHookSolFree with cRefs=0 pThis=%p\n", pThis));
163 }
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.
179 */
180 pThis = (PRTTHREADCTXHOOKINT)RTMemAllocZ(sizeof(*pThis));
181 if (RT_UNLIKELY(!pThis))
182 return VERR_NO_MEMORY;
183 pThis->u32Magic = RTTHREADCTXHOOKINT_MAGIC;
184 pThis->hOwner = RTThreadNativeSelf();
185 pThis->pfnCallback = pfnCallback;
186 pThis->pvUser = pvUser;
187 pThis->fEnabled = false;
188 pThis->cRefs = 2; /* One reference for the thread, one for the caller. */
189
190 /*
191 * installctx() allocates memory and thus cannot be used in RTThreadCtxHookRegister() which can be used
192 * with preemption disabled. We allocate the context-hooks here and use 'fEnabled' to determine if we can
193 * invoke the consumer's hook or not.
194 */
195 if (g_frtSolOldThreadCtx)
196 {
197 g_rtSolThreadCtx.Install.pfnSol_installctx_old(curthread,
198 pThis,
199 rtThreadCtxHookSolOut, /* save */
200 rtThreadCtxHookSolIn, /* restore */
201 NULL, /* fork */
202 NULL, /* lwp_create */
203 rtThreadCtxHookSolFree);
204 }
205 else
206 {
207 g_rtSolThreadCtx.Install.pfnSol_installctx(curthread,
208 pThis,
209 rtThreadCtxHookSolOut, /* save */
210 rtThreadCtxHookSolIn, /* restore */
211 NULL, /* fork */
212 NULL, /* lwp_create */
213 NULL, /* exit */
214 rtThreadCtxHookSolFree);
215 }
216
217 *phCtxHook = pThis;
218 return VINF_SUCCESS;
219}
220
221
222RTDECL(int) RTThreadCtxHookDestroy(RTTHREADCTXHOOK hCtxHook)
223{
224 /*
225 * Validate input, ignoring NIL.
226 */
227 PRTTHREADCTXHOOKINT pThis = hCtxHook;
228 if (pThis == NIL_RTTHREADCTXHOOK)
229 return VINF_SUCCESS;
230 RTTHREADCTX_VALID_RETURN_RC(hCtxHook, VERR_INVALID_HANDLE);
231 Assert(RTThreadPreemptIsEnabled(NIL_RTTHREAD));
232 Assert(!pThis->fEnabled || pThis->hOwner == RTThreadNativeSelf());
233
234 /*
235 * Make sure it's disabled.
236 */
237 ASMAtomicWriteBool(&pThis->fEnabled, false);
238
239 /*
240 * Decrement.
241 */
242 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
243 if ( cRefs == 1
244 && pThis->hOwner == RTThreadNativeSelf())
245 {
246 /*
247 * removectx() will invoke rtThreadCtxHookSolFree() and there is no way to bypass it and still use
248 * rtThreadCtxHookSolFree() at the same time. Hence the convulated reference counting.
249 *
250 * When this function is called from the owner thread and is the last reference, we call removectx() which
251 * will invoke rtThreadCtxHookSolFree() with cRefs = 1 and that will then free the hook object.
252 *
253 * When the function is called from a different thread, we simply decrement the reference. Whenever the
254 * ring-0 thread dies, Solaris will call rtThreadCtxHookSolFree() which will free the hook object.
255 */
256 int rc;
257 if (g_frtSolOldThreadCtx)
258 {
259 rc = g_rtSolThreadCtx.Remove.pfnSol_removectx_old(curthread,
260 pThis,
261 rtThreadCtxHookSolOut, /* save */
262 rtThreadCtxHookSolIn, /* restore */
263 NULL, /* fork */
264 NULL, /* lwp_create */
265 rtThreadCtxHookSolFree);
266 }
267 else
268 {
269 rc = g_rtSolThreadCtx.Remove.pfnSol_removectx(curthread,
270 pThis,
271 rtThreadCtxHookSolOut, /* save */
272 rtThreadCtxHookSolIn, /* restore */
273 NULL, /* fork */
274 NULL, /* lwp_create */
275 NULL, /* exit */
276 rtThreadCtxHookSolFree);
277 }
278 AssertMsg(rc, ("removectx() failed. rc=%d\n", rc));
279 NOREF(rc);
280
281#if 0 /*def RT_STRICT - access after free */
282 cRefs = ASMAtomicReadU32(&pThis->cRefs);
283 Assert(!cRefs);
284#endif
285 cRefs = 0;
286 }
287 else if (!cRefs)
288 {
289 /*
290 * The ring-0 thread for this hook object has already died. Free up the object as we have no more references.
291 */
292 Assert(pThis->hOwner != RTThreadNativeSelf());
293 ASMAtomicWriteU32(&pThis->u32Magic, ~RTTHREADCTXHOOKINT_MAGIC);
294 RTMemFree(pThis);
295 }
296
297 return cRefs;
298}
299
300
301RTDECL(int) RTThreadCtxHookEnable(RTTHREADCTXHOOK hCtxHook)
302{
303 /*
304 * Validate input.
305 */
306 PRTTHREADCTXHOOKINT pThis = hCtxHook;
307 AssertPtr(pThis);
308 AssertMsgReturn(pThis->u32Magic == RTTHREADCTXHOOKINT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis),
309 VERR_INVALID_HANDLE);
310 Assert(pThis->hOwner == RTThreadNativeSelf());
311 Assert(!pThis->fEnabled);
312
313 /*
314 * Mark it as enabled.
315 */
316 pThis->fEnabled = true;
317
318 return VINF_SUCCESS;
319}
320
321
322RTDECL(int) RTThreadCtxHookDisable(RTTHREADCTXHOOK hCtxHook)
323{
324 /*
325 * Validate input.
326 */
327 PRTTHREADCTXHOOKINT pThis = hCtxHook;
328 if (pThis != NIL_RTTHREADCTXHOOK)
329 {
330 AssertPtr(pThis);
331 AssertMsgReturn(pThis->u32Magic == RTTHREADCTXHOOKINT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis),
332 VERR_INVALID_HANDLE);
333 Assert(pThis->hOwner == RTThreadNativeSelf());
334
335 /*
336 * Mark it as disabled.
337 */
338 pThis->fEnabled = false;
339 }
340
341 return VINF_SUCCESS;
342}
343
344
345RTDECL(bool) RTThreadCtxHookIsEnabled(RTTHREADCTXHOOK hCtxHook)
346{
347 /*
348 * Validate input.
349 */
350 PRTTHREADCTXHOOKINT pThis = hCtxHook;
351 if (pThis == NIL_RTTHREADCTXHOOK)
352 return false;
353 AssertPtr(pThis);
354 AssertMsgReturn(pThis->u32Magic == RTTHREADCTXHOOKINT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis),
355 false);
356
357 return pThis->fEnabled;
358}
359
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