VirtualBox

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

Last change on this file since 76553 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 12.5 KB
Line 
1/* $Id: semmutex-r0drv-linux.c 76553 2019-01-01 01:45:53Z vboxsync $ */
2/** @file
3 * IPRT - Mutex Semaphores, Ring-0 Driver, Linux.
4 */
5
6/*
7 * Copyright (C) 2006-2019 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 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}
117RT_EXPORT_SYMBOL(RTSemMutexCreate);
118
119
120RTDECL(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}
160RT_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 */
173static 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 */
258DECLINLINE(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
317RTDECL(int) RTSemMutexRequest(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies)
318{
319 return rtSemMutexLinuxRequest(hMutexSem, cMillies, false /*fInterruptible*/);
320}
321RT_EXPORT_SYMBOL(RTSemMutexRequest);
322
323
324RTDECL(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}
329RT_EXPORT_SYMBOL(RTSemMutexRequestDebug);
330
331
332RTDECL(int) RTSemMutexRequestNoResume(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies)
333{
334 return rtSemMutexLinuxRequest(hMutexSem, cMillies, true /*fInterruptible*/);
335}
336RT_EXPORT_SYMBOL(RTSemMutexRequestNoResume);
337
338
339RTDECL(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}
344RT_EXPORT_SYMBOL(RTSemMutexRequestNoResumeDebug);
345
346
347RTDECL(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}
392RT_EXPORT_SYMBOL(RTSemMutexRelease);
393
394
395RTDECL(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}
420RT_EXPORT_SYMBOL(RTSemMutexIsOwned);
421
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