]>
Commit | Line | Data |
---|---|---|
056a1eb7 SF |
1 | /* $Id: semmutex-r0drv-linux.c $ */ |
2 | /** @file | |
3 | * IPRT - Mutex Semaphores, Ring-0 Driver, Linux. | |
4 | */ | |
5 | ||
6 | /* | |
6d209b23 | 7 | * Copyright (C) 2006-2017 Oracle Corporation |
056a1eb7 SF |
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 RTSEMMUTEX_WITHOUT_REMAPPING | |
32 | #include "the-linux-kernel.h" | |
33 | #include "internal/iprt.h" | |
34 | #include <iprt/semaphore.h> | |
35 | ||
36 | #include <iprt/assert.h> | |
37 | #include <iprt/asm.h> | |
38 | #include <iprt/mem.h> | |
39 | #include <iprt/err.h> | |
40 | #include <iprt/list.h> | |
41 | ||
42 | #include "internal/magics.h" | |
43 | ||
44 | ||
45 | /********************************************************************************************************************************* | |
46 | * Structures and Typedefs * | |
47 | *********************************************************************************************************************************/ | |
48 | typedef struct RTSEMMUTEXLNXWAITER | |
49 | { | |
50 | /** The list entry. */ | |
51 | RTLISTNODE ListEntry; | |
52 | /** The waiting task. */ | |
53 | struct task_struct *pTask; | |
54 | /** Why did we wake up? */ | |
55 | enum | |
56 | { | |
57 | /** Wakeup to take the semaphore. */ | |
58 | RTSEMMUTEXLNXWAITER_WAKEUP, | |
59 | /** Mutex is being destroyed. */ | |
60 | RTSEMMUTEXLNXWAITER_DESTROYED, | |
61 | /** Some other reason. */ | |
62 | RTSEMMUTEXLNXWAITER_OTHER | |
63 | } volatile enmReason; | |
64 | } RTSEMMUTEXLNXWAITER, *PRTSEMMUTEXLNXWAITER; | |
65 | ||
66 | /** | |
67 | * Wrapper for the linux semaphore structure. | |
68 | */ | |
69 | typedef struct RTSEMMUTEXINTERNAL | |
70 | { | |
71 | /** Magic value (RTSEMMUTEX_MAGIC). */ | |
72 | uint32_t u32Magic; | |
73 | /** The number of recursions. */ | |
74 | uint32_t cRecursions; | |
75 | /** The list of waiting threads. */ | |
76 | RTLISTANCHOR WaiterList; | |
77 | /** The current owner, NULL if none. */ | |
78 | struct task_struct *pOwnerTask; | |
79 | /** The number of references to this piece of memory. This is used to | |
80 | * prevent it from being kicked from underneath us while waiting. */ | |
81 | uint32_t volatile cRefs; | |
82 | /** The spinlock protecting the members and falling asleep. */ | |
83 | spinlock_t Spinlock; | |
84 | } RTSEMMUTEXINTERNAL, *PRTSEMMUTEXINTERNAL; | |
85 | ||
86 | ||
87 | RTDECL(int) RTSemMutexCreate(PRTSEMMUTEX phMtx) | |
88 | { | |
89 | int rc = VINF_SUCCESS; | |
90 | IPRT_LINUX_SAVE_EFL_AC(); | |
91 | ||
92 | /* | |
93 | * Allocate. | |
94 | */ | |
95 | PRTSEMMUTEXINTERNAL pThis; | |
96 | pThis = (PRTSEMMUTEXINTERNAL)RTMemAlloc(sizeof(*pThis)); | |
97 | if (pThis) | |
98 | { | |
99 | /* | |
100 | * Initialize. | |
101 | */ | |
102 | pThis->u32Magic = RTSEMMUTEX_MAGIC; | |
103 | pThis->cRecursions = 0; | |
104 | pThis->pOwnerTask = NULL; | |
105 | pThis->cRefs = 1; | |
106 | RTListInit(&pThis->WaiterList); | |
107 | spin_lock_init(&pThis->Spinlock); | |
108 | ||
109 | *phMtx = pThis; | |
110 | } | |
111 | else | |
112 | rc = VERR_NO_MEMORY; | |
113 | ||
114 | IPRT_LINUX_RESTORE_EFL_AC(); | |
115 | return rc; | |
116 | } | |
117 | RT_EXPORT_SYMBOL(RTSemMutexCreate); | |
118 | ||
119 | ||
120 | RTDECL(int) RTSemMutexDestroy(RTSEMMUTEX hMtx) | |
121 | { | |
122 | PRTSEMMUTEXINTERNAL pThis = hMtx; | |
123 | PRTSEMMUTEXLNXWAITER pCur; | |
124 | unsigned long fSavedIrq; | |
125 | ||
126 | /* | |
127 | * Validate. | |
128 | */ | |
129 | if (pThis == NIL_RTSEMMUTEX) | |
130 | return VINF_SUCCESS; | |
131 | AssertPtrReturn(pThis, VERR_INVALID_HANDLE); | |
132 | AssertMsgReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, ("u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), VERR_INVALID_HANDLE); | |
133 | ||
134 | /* | |
135 | * Kill it, kick waiters and release it. | |
136 | */ | |
137 | AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, RTSEMMUTEX_MAGIC_DEAD, RTSEMMUTEX_MAGIC), VERR_INVALID_HANDLE); | |
138 | ||
139 | IPRT_LINUX_SAVE_EFL_AC(); | |
140 | ||
141 | spin_lock_irqsave(&pThis->Spinlock, fSavedIrq); | |
142 | RTListForEach(&pThis->WaiterList, pCur, RTSEMMUTEXLNXWAITER, ListEntry) | |
143 | { | |
144 | pCur->enmReason = RTSEMMUTEXLNXWAITER_DESTROYED; | |
145 | wake_up_process(pCur->pTask); | |
146 | } | |
147 | ||
148 | if (ASMAtomicDecU32(&pThis->cRefs) != 0) | |
149 | spin_unlock_irqrestore(&pThis->Spinlock, fSavedIrq); | |
150 | else | |
151 | { | |
152 | spin_unlock_irqrestore(&pThis->Spinlock, fSavedIrq); | |
153 | RTMemFree(pThis); | |
154 | } | |
155 | ||
156 | IPRT_LINUX_RESTORE_EFL_AC(); | |
157 | ||
158 | return VINF_SUCCESS; | |
159 | } | |
160 | RT_EXPORT_SYMBOL(RTSemMutexDestroy); | |
161 | ||
162 | ||
163 | /** | |
164 | * Worker for rtSemMutexLinuxRequest that handles the case where we go to sleep. | |
165 | * | |
166 | * @returns VINF_SUCCESS, VERR_INTERRUPTED, VERR_TIMEOUT or VERR_SEM_DESTROYED. | |
167 | * Returns without owning the spinlock. | |
168 | * @param pThis The mutex instance. | |
169 | * @param cMillies The timeout. | |
170 | * @param fInterruptible The wait type. | |
171 | * @param fSavedIrq The saved IRQ flags. | |
172 | */ | |
173 | static int rtSemMutexLinuxRequestSleep(PRTSEMMUTEXINTERNAL pThis, RTMSINTERVAL cMillies, | |
174 | bool fInterruptible, unsigned long fSavedIrq) | |
175 | { | |
176 | struct task_struct *pSelf = current; | |
177 | int rc = VERR_TIMEOUT; | |
178 | long lTimeout = cMillies == RT_INDEFINITE_WAIT ? MAX_SCHEDULE_TIMEOUT : msecs_to_jiffies(cMillies); | |
179 | RTSEMMUTEXLNXWAITER Waiter; | |
180 | ||
181 | IPRT_DEBUG_SEMS_STATE(pThis, 'm'); | |
182 | ||
183 | /* | |
184 | * Grab a reference to the mutex and add ourselves to the waiter list. | |
185 | */ | |
186 | ASMAtomicIncU32(&pThis->cRefs); | |
187 | ||
188 | Waiter.pTask = pSelf; | |
189 | Waiter.enmReason = RTSEMMUTEXLNXWAITER_OTHER; | |
190 | RTListAppend(&pThis->WaiterList, &Waiter.ListEntry); | |
191 | ||
192 | /* | |
193 | * Do the waiting. | |
194 | */ | |
195 | for (;;) | |
196 | { | |
197 | /* Check signal and timeout conditions. */ | |
198 | if ( fInterruptible | |
199 | && signal_pending(pSelf)) | |
200 | { | |
201 | rc = VERR_INTERRUPTED; | |
202 | break; | |
203 | } | |
204 | ||
205 | if (!lTimeout) | |
206 | break; | |
207 | ||
208 | /* Go to sleep. */ | |
209 | set_current_state(fInterruptible ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE); | |
210 | spin_unlock_irq(&pThis->Spinlock); | |
211 | ||
212 | lTimeout = schedule_timeout(lTimeout); | |
213 | ||
214 | spin_lock_irq(&pThis->Spinlock); | |
215 | set_current_state(TASK_RUNNING); | |
216 | ||
217 | /* Did someone wake us up? */ | |
218 | if (Waiter.enmReason == RTSEMMUTEXLNXWAITER_WAKEUP) | |
219 | { | |
220 | Assert(pThis->cRecursions == 0); | |
221 | pThis->cRecursions = 1; | |
222 | pThis->pOwnerTask = pSelf; | |
223 | rc = VINF_SUCCESS; | |
224 | break; | |
225 | } | |
226 | ||
227 | /* Is the mutex being destroyed? */ | |
228 | if (RT_UNLIKELY( Waiter.enmReason == RTSEMMUTEXLNXWAITER_DESTROYED | |
229 | || pThis->u32Magic != RTSEMMUTEX_MAGIC)) | |
230 | { | |
231 | rc = VERR_SEM_DESTROYED; | |
232 | break; | |
233 | } | |
234 | } | |
235 | ||
236 | /* | |
237 | * Unlink ourself from the waiter list, dereference the mutex and exit the | |
238 | * lock. We might have to free the mutex if it was the destroyed. | |
239 | */ | |
240 | RTListNodeRemove(&Waiter.ListEntry); | |
241 | IPRT_DEBUG_SEMS_STATE_RC(pThis, 'M', rc); | |
242 | ||
243 | if (RT_LIKELY(ASMAtomicDecU32(&pThis->cRefs) != 0)) | |
244 | spin_unlock_irqrestore(&pThis->Spinlock, fSavedIrq); | |
245 | else | |
246 | { | |
247 | Assert(RT_FAILURE_NP(rc)); | |
248 | spin_unlock_irqrestore(&pThis->Spinlock, fSavedIrq); | |
249 | RTMemFree(pThis); | |
250 | } | |
251 | return rc; | |
252 | } | |
253 | ||
254 | ||
255 | /** | |
256 | * Internal worker. | |
257 | */ | |
258 | DECLINLINE(int) rtSemMutexLinuxRequest(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, bool fInterruptible) | |
259 | { | |
260 | PRTSEMMUTEXINTERNAL pThis = hMutexSem; | |
261 | struct task_struct *pSelf = current; | |
262 | unsigned long fSavedIrq; | |
263 | int rc; | |
264 | IPRT_LINUX_SAVE_EFL_AC(); | |
265 | ||
266 | /* | |
267 | * Validate. | |
268 | */ | |
269 | AssertPtrReturn(pThis, VERR_INVALID_HANDLE); | |
270 | AssertMsgReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, ("u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), VERR_INVALID_HANDLE); | |
271 | Assert(pThis->cRefs >= 1); | |
272 | ||
273 | /* | |
274 | * Lock it and check if it's a recursion. | |
275 | */ | |
276 | spin_lock_irqsave(&pThis->Spinlock, fSavedIrq); | |
277 | if (pThis->pOwnerTask == pSelf) | |
278 | { | |
279 | pThis->cRecursions++; | |
280 | Assert(pThis->cRecursions > 1); | |
281 | Assert(pThis->cRecursions < 256); | |
282 | rc = VINF_SUCCESS; | |
283 | } | |
284 | /* | |
285 | * Not a recursion, maybe it's not owned by anyone then? | |
286 | */ | |
287 | else if ( pThis->pOwnerTask == NULL | |
288 | && RTListIsEmpty(&pThis->WaiterList)) | |
289 | { | |
290 | Assert(pThis->cRecursions == 0); | |
291 | pThis->cRecursions = 1; | |
292 | pThis->pOwnerTask = pSelf; | |
293 | rc = VINF_SUCCESS; | |
294 | } | |
295 | /* | |
296 | * Was it a polling call? | |
297 | */ | |
298 | else if (cMillies == 0) | |
299 | rc = VERR_TIMEOUT; | |
300 | /* | |
301 | * No, so go to sleep. | |
302 | */ | |
303 | else | |
304 | { | |
305 | rc = rtSemMutexLinuxRequestSleep(pThis, cMillies, fInterruptible, fSavedIrq); | |
306 | IPRT_LINUX_RESTORE_EFL_ONLY_AC(); | |
307 | return rc; | |
308 | } | |
309 | ||
310 | IPRT_DEBUG_SEMS_STATE_RC(pThis, 'M', rc); | |
311 | spin_unlock_irqrestore(&pThis->Spinlock, fSavedIrq); | |
312 | IPRT_LINUX_RESTORE_EFL_ONLY_AC(); | |
313 | return rc; | |
314 | } | |
315 | ||
316 | ||
317 | RTDECL(int) RTSemMutexRequest(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies) | |
318 | { | |
319 | return rtSemMutexLinuxRequest(hMutexSem, cMillies, false /*fInterruptible*/); | |
320 | } | |
321 | RT_EXPORT_SYMBOL(RTSemMutexRequest); | |
322 | ||
323 | ||
324 | RTDECL(int) RTSemMutexRequestDebug(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL) | |
325 | { | |
326 | RT_NOREF_PV(uId); RT_SRC_POS_NOREF(); | |
327 | return RTSemMutexRequest(hMutexSem, cMillies); | |
328 | } | |
329 | RT_EXPORT_SYMBOL(RTSemMutexRequestDebug); | |
330 | ||
331 | ||
332 | RTDECL(int) RTSemMutexRequestNoResume(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies) | |
333 | { | |
334 | return rtSemMutexLinuxRequest(hMutexSem, cMillies, true /*fInterruptible*/); | |
335 | } | |
336 | RT_EXPORT_SYMBOL(RTSemMutexRequestNoResume); | |
337 | ||
338 | ||
339 | RTDECL(int) RTSemMutexRequestNoResumeDebug(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL) | |
340 | { | |
341 | RT_NOREF_PV(uId); RT_SRC_POS_NOREF(); | |
342 | return RTSemMutexRequestNoResume(hMutexSem, cMillies); | |
343 | } | |
344 | RT_EXPORT_SYMBOL(RTSemMutexRequestNoResumeDebug); | |
345 | ||
346 | ||
347 | RTDECL(int) RTSemMutexRelease(RTSEMMUTEX hMtx) | |
348 | { | |
349 | PRTSEMMUTEXINTERNAL pThis = hMtx; | |
350 | struct task_struct *pSelf = current; | |
351 | unsigned long fSavedIrq; | |
352 | int rc; | |
353 | IPRT_LINUX_SAVE_EFL_AC(); | |
354 | ||
355 | /* | |
356 | * Validate. | |
357 | */ | |
358 | AssertPtrReturn(pThis, VERR_INVALID_HANDLE); | |
359 | AssertMsgReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, ("u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), VERR_INVALID_HANDLE); | |
360 | Assert(pThis->cRefs >= 1); | |
361 | ||
362 | /* | |
363 | * Take the lock and release one recursion. | |
364 | */ | |
365 | spin_lock_irqsave(&pThis->Spinlock, fSavedIrq); | |
366 | if (pThis->pOwnerTask == pSelf) | |
367 | { | |
368 | Assert(pThis->cRecursions > 0); | |
369 | if (--pThis->cRecursions == 0) | |
370 | { | |
371 | pThis->pOwnerTask = NULL; | |
372 | ||
373 | /* anyone to wake up? */ | |
374 | if (!RTListIsEmpty(&pThis->WaiterList)) | |
375 | { | |
376 | PRTSEMMUTEXLNXWAITER pWaiter = RTListGetFirst(&pThis->WaiterList, RTSEMMUTEXLNXWAITER, ListEntry); | |
377 | pWaiter->enmReason = RTSEMMUTEXLNXWAITER_WAKEUP; | |
378 | wake_up_process(pWaiter->pTask); | |
379 | } | |
380 | IPRT_DEBUG_SEMS_STATE(pThis, 'u'); | |
381 | } | |
382 | rc = VINF_SUCCESS; | |
383 | } | |
384 | else | |
385 | rc = VERR_NOT_OWNER; | |
386 | spin_unlock_irqrestore(&pThis->Spinlock, fSavedIrq); | |
387 | ||
388 | AssertRC(rc); | |
389 | IPRT_LINUX_RESTORE_EFL_AC(); | |
390 | return rc; | |
391 | } | |
392 | RT_EXPORT_SYMBOL(RTSemMutexRelease); | |
393 | ||
394 | ||
395 | RTDECL(bool) RTSemMutexIsOwned(RTSEMMUTEX hMutexSem) | |
396 | { | |
397 | PRTSEMMUTEXINTERNAL pThis = hMutexSem; | |
398 | unsigned long fSavedIrq; | |
399 | bool fOwned; | |
400 | IPRT_LINUX_SAVE_EFL_AC(); | |
401 | ||
402 | /* | |
403 | * Validate. | |
404 | */ | |
405 | AssertPtrReturn(pThis, false); | |
406 | AssertMsgReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, ("u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), false); | |
407 | Assert(pThis->cRefs >= 1); | |
408 | ||
409 | /* | |
410 | * Take the lock and release one recursion. | |
411 | */ | |
412 | spin_lock_irqsave(&pThis->Spinlock, fSavedIrq); | |
413 | fOwned = pThis->pOwnerTask != NULL; | |
414 | spin_unlock_irqrestore(&pThis->Spinlock, fSavedIrq); | |
415 | ||
416 | IPRT_LINUX_RESTORE_EFL_AC(); | |
417 | return fOwned; | |
418 | ||
419 | } | |
420 | RT_EXPORT_SYMBOL(RTSemMutexIsOwned); | |
421 |