VirtualBox

source: vbox/trunk/src/VBox/Runtime/r0drv/linux/semmutex-r0drv-linux.c@ 52502

Last change on this file since 52502 was 48935, checked in by vboxsync, 11 years ago

Runtime: Whitespace and svn:keyword cleanups by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 11.7 KB
Line 
1/* $Id: semmutex-r0drv-linux.c 48935 2013-10-07 21:19:37Z vboxsync $ */
2/** @file
3 * IPRT - Mutex Semaphores, Ring-0 Driver, Linux.
4 */
5
6/*
7 * Copyright (C) 2006-2011 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 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*******************************************************************************/
48typedef 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 */
69typedef 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
87RTDECL(int) RTSemMutexCreate(PRTSEMMUTEX phMtx)
88{
89 /*
90 * Allocate.
91 */
92 PRTSEMMUTEXINTERNAL pThis;
93 pThis = (PRTSEMMUTEXINTERNAL)RTMemAlloc(sizeof(*pThis));
94 if (!pThis)
95 return VERR_NO_MEMORY;
96
97 /*
98 * Initialize.
99 */
100 pThis->u32Magic = RTSEMMUTEX_MAGIC;
101 pThis->cRecursions = 0;
102 pThis->pOwnerTask = NULL;
103 pThis->cRefs = 1;
104 RTListInit(&pThis->WaiterList);
105 spin_lock_init(&pThis->Spinlock);
106
107 *phMtx = pThis;
108 return VINF_SUCCESS;
109}
110RT_EXPORT_SYMBOL(RTSemMutexCreate);
111
112
113RTDECL(int) RTSemMutexDestroy(RTSEMMUTEX hMtx)
114{
115 PRTSEMMUTEXINTERNAL pThis = hMtx;
116 PRTSEMMUTEXLNXWAITER pCur;
117 unsigned long fSavedIrq;
118
119 /*
120 * Validate.
121 */
122 if (pThis == NIL_RTSEMMUTEX)
123 return VINF_SUCCESS;
124 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
125 AssertMsgReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, ("u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), VERR_INVALID_HANDLE);
126
127 /*
128 * Kill it, kick waiters and release it.
129 */
130 AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, RTSEMMUTEX_MAGIC_DEAD, RTSEMMUTEX_MAGIC), VERR_INVALID_HANDLE);
131
132 spin_lock_irqsave(&pThis->Spinlock, fSavedIrq);
133 RTListForEach(&pThis->WaiterList, pCur, RTSEMMUTEXLNXWAITER, ListEntry)
134 {
135 pCur->enmReason = RTSEMMUTEXLNXWAITER_DESTROYED;
136 wake_up_process(pCur->pTask);
137 }
138
139 if (ASMAtomicDecU32(&pThis->cRefs) != 0)
140 spin_unlock_irqrestore(&pThis->Spinlock, fSavedIrq);
141 else
142 {
143 spin_unlock_irqrestore(&pThis->Spinlock, fSavedIrq);
144 RTMemFree(pThis);
145 }
146
147 return VINF_SUCCESS;
148}
149RT_EXPORT_SYMBOL(RTSemMutexDestroy);
150
151
152/**
153 * Worker for rtSemMutexLinuxRequest that handles the case where we go to sleep.
154 *
155 * @returns VINF_SUCCESS, VERR_INTERRUPTED, VERR_TIMEOUT or VERR_SEM_DESTROYED.
156 * Returns without owning the spinlock.
157 * @param pThis The mutex instance.
158 * @param cMillies The timeout.
159 * @param fInterruptible The wait type.
160 * @param fSavedIrq The saved IRQ flags.
161 */
162static int rtSemMutexLinuxRequestSleep(PRTSEMMUTEXINTERNAL pThis, RTMSINTERVAL cMillies,
163 bool fInterruptible, unsigned long fSavedIrq)
164{
165 struct task_struct *pSelf = current;
166 int rc = VERR_TIMEOUT;
167 long lTimeout = cMillies == RT_INDEFINITE_WAIT ? MAX_SCHEDULE_TIMEOUT : msecs_to_jiffies(cMillies);
168 RTSEMMUTEXLNXWAITER Waiter;
169
170 IPRT_DEBUG_SEMS_STATE(pThis, 'm');
171
172 /*
173 * Grab a reference to the mutex and add ourselves to the waiter list.
174 */
175 ASMAtomicIncU32(&pThis->cRefs);
176
177 Waiter.pTask = pSelf;
178 Waiter.enmReason = RTSEMMUTEXLNXWAITER_OTHER;
179 RTListAppend(&pThis->WaiterList, &Waiter.ListEntry);
180
181 /*
182 * Do the waiting.
183 */
184 for (;;)
185 {
186 /* Check signal and timeout conditions. */
187 if ( fInterruptible
188 && signal_pending(pSelf))
189 {
190 rc = VERR_INTERRUPTED;
191 break;
192 }
193
194 if (!lTimeout)
195 break;
196
197 /* Go to sleep. */
198 set_task_state(pSelf, fInterruptible ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
199 spin_unlock_irq(&pThis->Spinlock);
200
201 lTimeout = schedule_timeout(lTimeout);
202
203 spin_lock_irq(&pThis->Spinlock);
204 set_current_state(TASK_RUNNING);
205
206 /* Did someone wake us up? */
207 if (Waiter.enmReason == RTSEMMUTEXLNXWAITER_WAKEUP)
208 {
209 Assert(pThis->cRecursions == 0);
210 pThis->cRecursions = 1;
211 pThis->pOwnerTask = pSelf;
212 rc = VINF_SUCCESS;
213 break;
214 }
215
216 /* Is the mutex being destroyed? */
217 if (RT_UNLIKELY( Waiter.enmReason == RTSEMMUTEXLNXWAITER_DESTROYED
218 || pThis->u32Magic != RTSEMMUTEX_MAGIC))
219 {
220 rc = VERR_SEM_DESTROYED;
221 break;
222 }
223 }
224
225 /*
226 * Unlink ourself from the waiter list, dereference the mutex and exit the
227 * lock. We might have to free the mutex if it was the destroyed.
228 */
229 RTListNodeRemove(&Waiter.ListEntry);
230 IPRT_DEBUG_SEMS_STATE_RC(pThis, 'M', rc);
231
232 if (RT_LIKELY(ASMAtomicDecU32(&pThis->cRefs) != 0))
233 spin_unlock_irqrestore(&pThis->Spinlock, fSavedIrq);
234 else
235 {
236 Assert(RT_FAILURE_NP(rc));
237 spin_unlock_irqrestore(&pThis->Spinlock, fSavedIrq);
238 RTMemFree(pThis);
239 }
240 return rc;
241}
242
243
244/**
245 * Internal worker.
246 */
247DECLINLINE(int) rtSemMutexLinuxRequest(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, bool fInterruptible)
248{
249 PRTSEMMUTEXINTERNAL pThis = hMutexSem;
250 struct task_struct *pSelf = current;
251 unsigned long fSavedIrq;
252 int rc;
253
254 /*
255 * Validate.
256 */
257 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
258 AssertMsgReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, ("u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), VERR_INVALID_HANDLE);
259 Assert(pThis->cRefs >= 1);
260
261 /*
262 * Lock it and check if it's a recursion.
263 */
264 spin_lock_irqsave(&pThis->Spinlock, fSavedIrq);
265 if (pThis->pOwnerTask == pSelf)
266 {
267 pThis->cRecursions++;
268 Assert(pThis->cRecursions > 1);
269 Assert(pThis->cRecursions < 256);
270 rc = VINF_SUCCESS;
271 }
272 /*
273 * Not a recursion, maybe it's not owned by anyone then?
274 */
275 else if ( pThis->pOwnerTask == NULL
276 && RTListIsEmpty(&pThis->WaiterList))
277 {
278 Assert(pThis->cRecursions == 0);
279 pThis->cRecursions = 1;
280 pThis->pOwnerTask = pSelf;
281 rc = VINF_SUCCESS;
282 }
283 /*
284 * Was it a polling call?
285 */
286 else if (cMillies == 0)
287 rc = VERR_TIMEOUT;
288 /*
289 * No, so go to sleep.
290 */
291 else
292 return rtSemMutexLinuxRequestSleep(pThis, cMillies, fInterruptible, fSavedIrq);
293
294 IPRT_DEBUG_SEMS_STATE_RC(pThis, 'M', rc);
295 spin_unlock_irqrestore(&pThis->Spinlock, fSavedIrq);
296 return rc;
297}
298
299
300RTDECL(int) RTSemMutexRequest(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies)
301{
302 return rtSemMutexLinuxRequest(hMutexSem, cMillies, false /*fInterruptible*/);
303}
304RT_EXPORT_SYMBOL(RTSemMutexRequest);
305
306
307RTDECL(int) RTSemMutexRequestDebug(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL)
308{
309 return RTSemMutexRequest(hMutexSem, cMillies);
310}
311RT_EXPORT_SYMBOL(RTSemMutexRequestDebug);
312
313
314RTDECL(int) RTSemMutexRequestNoResume(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies)
315{
316 return rtSemMutexLinuxRequest(hMutexSem, cMillies, true /*fInterruptible*/);
317}
318RT_EXPORT_SYMBOL(RTSemMutexRequestNoResume);
319
320
321RTDECL(int) RTSemMutexRequestNoResumeDebug(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL)
322{
323 return RTSemMutexRequestNoResume(hMutexSem, cMillies);
324}
325RT_EXPORT_SYMBOL(RTSemMutexRequestNoResumeDebug);
326
327
328RTDECL(int) RTSemMutexRelease(RTSEMMUTEX hMtx)
329{
330 PRTSEMMUTEXINTERNAL pThis = hMtx;
331 struct task_struct *pSelf = current;
332 unsigned long fSavedIrq;
333 int rc;
334
335 /*
336 * Validate.
337 */
338 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
339 AssertMsgReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, ("u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), VERR_INVALID_HANDLE);
340 Assert(pThis->cRefs >= 1);
341
342 /*
343 * Take the lock and release one recursion.
344 */
345 spin_lock_irqsave(&pThis->Spinlock, fSavedIrq);
346 if (pThis->pOwnerTask == pSelf)
347 {
348 Assert(pThis->cRecursions > 0);
349 if (--pThis->cRecursions == 0)
350 {
351 pThis->pOwnerTask = NULL;
352
353 /* anyone to wake up? */
354 if (!RTListIsEmpty(&pThis->WaiterList))
355 {
356 PRTSEMMUTEXLNXWAITER pWaiter = RTListGetFirst(&pThis->WaiterList, RTSEMMUTEXLNXWAITER, ListEntry);
357 pWaiter->enmReason = RTSEMMUTEXLNXWAITER_WAKEUP;
358 wake_up_process(pWaiter->pTask);
359 }
360 IPRT_DEBUG_SEMS_STATE(pThis, 'u');
361 }
362 rc = VINF_SUCCESS;
363 }
364 else
365 rc = VERR_NOT_OWNER;
366 spin_unlock_irqrestore(&pThis->Spinlock, fSavedIrq);
367
368 AssertRC(rc);
369 return rc;
370}
371RT_EXPORT_SYMBOL(RTSemMutexRelease);
372
373
374RTDECL(bool) RTSemMutexIsOwned(RTSEMMUTEX hMutexSem)
375{
376 PRTSEMMUTEXINTERNAL pThis = hMutexSem;
377 unsigned long fSavedIrq;
378 bool fOwned;
379
380 /*
381 * Validate.
382 */
383 AssertPtrReturn(pThis, false);
384 AssertMsgReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, ("u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), false);
385 Assert(pThis->cRefs >= 1);
386
387 /*
388 * Take the lock and release one recursion.
389 */
390 spin_lock_irqsave(&pThis->Spinlock, fSavedIrq);
391 fOwned = pThis->pOwnerTask != NULL;
392 spin_unlock_irqrestore(&pThis->Spinlock, fSavedIrq);
393
394 return fOwned;
395
396}
397RT_EXPORT_SYMBOL(RTSemMutexIsOwned);
398
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