VirtualBox

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

Last change on this file since 55976 was 55976, checked in by vboxsync, 10 years ago

Runtime/r0drv: Fix typo in assertion in thread context hooks.

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