VirtualBox

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

Last change on this file since 32292 was 28800, checked in by vboxsync, 15 years ago

Automated rebranding to Oracle copyright/license strings via filemuncher

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