VirtualBox

source: vbox/trunk/src/VBox/Runtime/r0drv/solaris/threadctxhooks-r0drv-solaris.c@ 78253

Last change on this file since 78253 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

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