]>
Commit | Line | Data |
---|---|---|
056a1eb7 SF |
1 | /* $Id: semevent-r0drv-linux.c $ */ |
2 | /** @file | |
3 | * IPRT - Single Release Event Semaphores, Ring-0 Driver, Linux. | |
4 | */ | |
5 | ||
6 | /* | |
7 | * Copyright (C) 2006-2016 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 | #define RTSEMEVENT_WITHOUT_REMAPPING | |
32 | #include "the-linux-kernel.h" | |
33 | #include "internal/iprt.h" | |
34 | #include <iprt/semaphore.h> | |
35 | ||
36 | #include <iprt/asm.h> | |
37 | #include <iprt/assert.h> | |
38 | #include <iprt/err.h> | |
39 | #include <iprt/lockvalidator.h> | |
40 | #include <iprt/mem.h> | |
41 | ||
42 | #include "waitqueue-r0drv-linux.h" | |
43 | #include "internal/magics.h" | |
44 | ||
45 | ||
46 | /********************************************************************************************************************************* | |
47 | * Structures and Typedefs * | |
48 | *********************************************************************************************************************************/ | |
49 | /** | |
50 | * Linux event semaphore. | |
51 | */ | |
52 | typedef struct RTSEMEVENTINTERNAL | |
53 | { | |
54 | /** Magic value (RTSEMEVENT_MAGIC). */ | |
55 | uint32_t volatile u32Magic; | |
56 | /** The object status - !0 when signaled and 0 when reset. */ | |
57 | uint32_t volatile fState; | |
58 | /** Reference counter. */ | |
59 | uint32_t volatile cRefs; | |
60 | /** The wait queue. */ | |
61 | wait_queue_head_t Head; | |
62 | } RTSEMEVENTINTERNAL, *PRTSEMEVENTINTERNAL; | |
63 | ||
64 | ||
65 | ||
66 | RTDECL(int) RTSemEventCreate(PRTSEMEVENT phEventSem) | |
67 | { | |
68 | return RTSemEventCreateEx(phEventSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, NULL); | |
69 | } | |
70 | ||
71 | ||
72 | RTDECL(int) RTSemEventCreateEx(PRTSEMEVENT phEventSem, uint32_t fFlags, RTLOCKVALCLASS hClass, const char *pszNameFmt, ...) | |
73 | { | |
74 | PRTSEMEVENTINTERNAL pThis; | |
75 | IPRT_LINUX_SAVE_EFL_AC(); | |
76 | RT_NOREF_PV(hClass); RT_NOREF_PV(pszNameFmt); | |
77 | ||
78 | AssertReturn(!(fFlags & ~(RTSEMEVENT_FLAGS_NO_LOCK_VAL | RTSEMEVENT_FLAGS_BOOTSTRAP_HACK)), VERR_INVALID_PARAMETER); | |
79 | Assert(!(fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK) || (fFlags & RTSEMEVENT_FLAGS_NO_LOCK_VAL)); | |
80 | ||
81 | pThis = (PRTSEMEVENTINTERNAL)RTMemAlloc(sizeof(*pThis)); | |
82 | if (!pThis) | |
83 | return VERR_NO_MEMORY; | |
84 | ||
85 | pThis->u32Magic = RTSEMEVENT_MAGIC; | |
86 | pThis->fState = 0; | |
87 | pThis->cRefs = 1; | |
88 | init_waitqueue_head(&pThis->Head); | |
89 | ||
90 | *phEventSem = pThis; | |
91 | IPRT_LINUX_RESTORE_EFL_AC(); | |
92 | return VINF_SUCCESS; | |
93 | } | |
94 | RT_EXPORT_SYMBOL(RTSemEventCreate); | |
95 | ||
96 | ||
97 | /** | |
98 | * Retains a reference to the event semaphore. | |
99 | * | |
100 | * @param pThis The event semaphore. | |
101 | */ | |
102 | DECLINLINE(void) rtR0SemEventLnxRetain(PRTSEMEVENTINTERNAL pThis) | |
103 | { | |
104 | uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs); | |
105 | Assert(cRefs < 100000); NOREF(cRefs); | |
106 | } | |
107 | ||
108 | ||
109 | /** | |
110 | * Releases a reference to the event semaphore. | |
111 | * | |
112 | * @param pThis The event semaphore. | |
113 | */ | |
114 | DECLINLINE(void) rtR0SemEventLnxRelease(PRTSEMEVENTINTERNAL pThis) | |
115 | { | |
116 | if (RT_UNLIKELY(ASMAtomicDecU32(&pThis->cRefs) == 0)) | |
117 | RTMemFree(pThis); | |
118 | } | |
119 | ||
120 | ||
121 | RTDECL(int) RTSemEventDestroy(RTSEMEVENT hEventSem) | |
122 | { | |
123 | IPRT_LINUX_SAVE_EFL_AC(); | |
124 | ||
125 | /* | |
126 | * Validate input. | |
127 | */ | |
128 | PRTSEMEVENTINTERNAL pThis = hEventSem; | |
129 | if (pThis == NIL_RTSEMEVENT) | |
130 | return VINF_SUCCESS; | |
131 | AssertMsgReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), VERR_INVALID_HANDLE); | |
132 | Assert(pThis->cRefs > 0); | |
133 | ||
134 | /* | |
135 | * Invalidate it and signal the object just in case. | |
136 | */ | |
137 | ASMAtomicWriteU32(&pThis->u32Magic, ~RTSEMEVENT_MAGIC); | |
138 | ASMAtomicWriteU32(&pThis->fState, 0); | |
139 | Assert(!waitqueue_active(&pThis->Head)); | |
140 | wake_up_all(&pThis->Head); | |
141 | rtR0SemEventLnxRelease(pThis); | |
142 | ||
143 | IPRT_LINUX_RESTORE_EFL_AC(); | |
144 | return VINF_SUCCESS; | |
145 | } | |
146 | RT_EXPORT_SYMBOL(RTSemEventDestroy); | |
147 | ||
148 | ||
149 | RTDECL(int) RTSemEventSignal(RTSEMEVENT hEventSem) | |
150 | { | |
151 | IPRT_LINUX_SAVE_EFL_AC(); | |
152 | ||
153 | /* | |
154 | * Validate input. | |
155 | */ | |
156 | PRTSEMEVENTINTERNAL pThis = (PRTSEMEVENTINTERNAL)hEventSem; | |
157 | AssertPtrReturn(pThis, VERR_INVALID_HANDLE); | |
158 | AssertMsgReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), VERR_INVALID_HANDLE); | |
159 | rtR0SemEventLnxRetain(pThis); | |
160 | ||
161 | /* | |
162 | * Signal the event object. | |
163 | */ | |
164 | ASMAtomicWriteU32(&pThis->fState, 1); | |
165 | wake_up(&pThis->Head); | |
166 | ||
167 | rtR0SemEventLnxRelease(pThis); | |
168 | IPRT_LINUX_RESTORE_EFL_AC(); | |
169 | return VINF_SUCCESS; | |
170 | } | |
171 | RT_EXPORT_SYMBOL(RTSemEventSignal); | |
172 | ||
173 | ||
174 | /** | |
175 | * Worker for RTSemEventWaitEx and RTSemEventWaitExDebug. | |
176 | * | |
177 | * @returns VBox status code. | |
178 | * @param pThis The event semaphore. | |
179 | * @param fFlags See RTSemEventWaitEx. | |
180 | * @param uTimeout See RTSemEventWaitEx. | |
181 | * @param pSrcPos The source code position of the wait. | |
182 | */ | |
183 | static int rtR0SemEventLnxWait(PRTSEMEVENTINTERNAL pThis, uint32_t fFlags, uint64_t uTimeout, | |
184 | PCRTLOCKVALSRCPOS pSrcPos) | |
185 | { | |
186 | int rc; | |
187 | RT_NOREF_PV(pSrcPos); | |
188 | ||
189 | /* | |
190 | * Validate the input. | |
191 | */ | |
192 | AssertPtrReturn(pThis, VERR_INVALID_PARAMETER); | |
193 | AssertMsgReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, ("%p u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_PARAMETER); | |
194 | AssertReturn(RTSEMWAIT_FLAGS_ARE_VALID(fFlags), VERR_INVALID_PARAMETER); | |
195 | rtR0SemEventLnxRetain(pThis); | |
196 | ||
197 | /* | |
198 | * Try grab the event without setting up the wait. | |
199 | */ | |
200 | if ( 1 /** @todo check if there are someone waiting already - waitqueue_active, but then what do we do below? */ | |
201 | && ASMAtomicCmpXchgU32(&pThis->fState, 0, 1)) | |
202 | rc = VINF_SUCCESS; | |
203 | else | |
204 | { | |
205 | /* | |
206 | * We have to wait. | |
207 | */ | |
208 | IPRT_LINUX_SAVE_EFL_AC(); | |
209 | RTR0SEMLNXWAIT Wait; | |
210 | rc = rtR0SemLnxWaitInit(&Wait, fFlags, uTimeout, &pThis->Head); | |
211 | if (RT_SUCCESS(rc)) | |
212 | { | |
213 | IPRT_DEBUG_SEMS_STATE(pThis, 'E'); | |
214 | for (;;) | |
215 | { | |
216 | /* The destruction test. */ | |
217 | if (RT_UNLIKELY(pThis->u32Magic != RTSEMEVENT_MAGIC)) | |
218 | rc = VERR_SEM_DESTROYED; | |
219 | else | |
220 | { | |
221 | rtR0SemLnxWaitPrepare(&Wait); | |
222 | ||
223 | /* Check the exit conditions. */ | |
224 | if (RT_UNLIKELY(pThis->u32Magic != RTSEMEVENT_MAGIC)) | |
225 | rc = VERR_SEM_DESTROYED; | |
226 | else if (ASMAtomicCmpXchgU32(&pThis->fState, 0, 1)) | |
227 | rc = VINF_SUCCESS; | |
228 | else if (rtR0SemLnxWaitHasTimedOut(&Wait)) | |
229 | rc = VERR_TIMEOUT; | |
230 | else if (rtR0SemLnxWaitWasInterrupted(&Wait)) | |
231 | rc = VERR_INTERRUPTED; | |
232 | else | |
233 | { | |
234 | /* Do the wait and then recheck the conditions. */ | |
235 | rtR0SemLnxWaitDoIt(&Wait); | |
236 | continue; | |
237 | } | |
238 | } | |
239 | break; | |
240 | } | |
241 | ||
242 | rtR0SemLnxWaitDelete(&Wait); | |
243 | IPRT_DEBUG_SEMS_STATE_RC(pThis, 'E', rc); | |
244 | } | |
245 | IPRT_LINUX_RESTORE_EFL_AC(); | |
246 | } | |
247 | ||
248 | rtR0SemEventLnxRelease(pThis); | |
249 | return rc; | |
250 | } | |
251 | ||
252 | ||
253 | RTDECL(int) RTSemEventWaitEx(RTSEMEVENT hEventSem, uint32_t fFlags, uint64_t uTimeout) | |
254 | { | |
255 | #ifndef RTSEMEVENT_STRICT | |
256 | return rtR0SemEventLnxWait(hEventSem, fFlags, uTimeout, NULL); | |
257 | #else | |
258 | RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API(); | |
259 | return rtR0SemEventLnxWait(hEventSem, fFlags, uTimeout, &SrcPos); | |
260 | #endif | |
261 | } | |
262 | RT_EXPORT_SYMBOL(RTSemEventWaitEx); | |
263 | ||
264 | ||
265 | RTDECL(int) RTSemEventWaitExDebug(RTSEMEVENT hEventSem, uint32_t fFlags, uint64_t uTimeout, | |
266 | RTHCUINTPTR uId, RT_SRC_POS_DECL) | |
267 | { | |
268 | RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API(); | |
269 | return rtR0SemEventLnxWait(hEventSem, fFlags, uTimeout, &SrcPos); | |
270 | } | |
271 | RT_EXPORT_SYMBOL(RTSemEventWaitExDebug); | |
272 | ||
273 | ||
274 | RTDECL(uint32_t) RTSemEventGetResolution(void) | |
275 | { | |
276 | return rtR0SemLnxWaitGetResolution(); | |
277 | } | |
278 | RT_EXPORT_SYMBOL(RTSemEventGetResolution); | |
279 |