VirtualBox

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

Last change on this file was 98103, checked in by vboxsync, 17 months ago

Copyright year updates by scm.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use