VirtualBox

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

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

iprt/r0drv/linux: Preserve EFLAGS/AC where ever it may possibly be thought to change when calling kernel code.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette