VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMAll/PDMAllQueue.cpp@ 98193

Last change on this file since 98193 was 98103, checked in by vboxsync, 2 years ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 11.0 KB
Line 
1/* $Id: PDMAllQueue.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * PDM Queue - Transport data and tasks to EMT and R3.
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 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_PDM_QUEUE
33#include "PDMInternal.h"
34#include <VBox/vmm/pdm.h>
35#ifndef IN_RC
36# include <VBox/vmm/mm.h>
37#endif
38#include <VBox/vmm/vmcc.h>
39#include <iprt/errcore.h>
40#include <VBox/log.h>
41#include <iprt/asm.h>
42#include <iprt/assert.h>
43#include <iprt/string.h>
44
45
46/*********************************************************************************************************************************
47* Defined Constants And Macros *
48*********************************************************************************************************************************/
49/*
50 * Macros for thoroughly validating a queue handle and ownership.
51 */
52#define PDMQUEUE_HANDLE_TO_VARS_RETURN_COMMON(a_cbMax, a_cbTotalMax) \
53 AssertReturn(cbItem >= sizeof(PDMQUEUEITEMCORE), pQueue->rcOkay = VERR_INTERNAL_ERROR_4); \
54 AssertReturn(cbItem <= (a_cbMax), pQueue->rcOkay = VERR_INTERNAL_ERROR_4); \
55 \
56 /* paranoia^3: */ \
57 AssertReturn(cItems > 0, pQueue->rcOkay = VERR_INTERNAL_ERROR_4); \
58 AssertReturn(cItems <= PDMQUEUE_MAX_ITEMS, pQueue->rcOkay = VERR_INTERNAL_ERROR_4); \
59 AssertReturn(cbItem * cItems <= (a_cbTotalMax), pQueue->rcOkay = VERR_INTERNAL_ERROR_4)
60
61#ifdef IN_RING0
62# define PDMQUEUE_HANDLE_TO_VARS_RETURN(a_pVM, a_hQueue, a_pvOwner) \
63 AssertPtrReturn((a_pvOwner), VERR_INVALID_PARAMETER); \
64 \
65 AssertCompile(RT_ELEMENTS((a_pVM)->pdm.s.apRing0Queues) == RT_ELEMENTS((a_pVM)->pdmr0.s.aQueues)); \
66 AssertReturn((a_hQueue) < RT_ELEMENTS((a_pVM)->pdmr0.s.aQueues), VERR_INVALID_HANDLE); \
67 AssertReturn((a_hQueue) < (a_pVM)->pdmr0.s.cQueues, VERR_INVALID_HANDLE); \
68 AssertReturn((a_pVM)->pdmr0.s.aQueues[(a_hQueue)].pvOwner == (a_pvOwner), VERR_INVALID_HANDLE); \
69 PPDMQUEUE pQueue = (a_pVM)->pdmr0.s.aQueues[(a_hQueue)].pQueue; \
70 AssertPtrReturn(pQueue, VERR_INVALID_HANDLE); \
71 AssertReturn(pQueue->u32Magic == PDMQUEUE_MAGIC, VERR_INVALID_HANDLE); \
72 AssertReturn(pQueue->rcOkay == VINF_SUCCESS, pQueue->rcOkay); \
73 \
74 uint32_t const cbItem = (a_pVM)->pdmr0.s.aQueues[(a_hQueue)].cbItem; \
75 uint32_t const cItems = (a_pVM)->pdmr0.s.aQueues[(a_hQueue)].cItems; \
76 uint32_t const offItems = (a_pVM)->pdmr0.s.aQueues[(a_hQueue)].offItems; \
77 \
78 /* paranoia^2: */ \
79 AssertReturn(pQueue->cbItem == cbItem, pQueue->rcOkay = VERR_INTERNAL_ERROR_3); \
80 AssertReturn(pQueue->cItems == cItems, pQueue->rcOkay = VERR_INTERNAL_ERROR_3); \
81 AssertReturn(pQueue->offItems == offItems, pQueue->rcOkay = VERR_INTERNAL_ERROR_3); \
82 \
83 PDMQUEUE_HANDLE_TO_VARS_RETURN_COMMON(PDMQUEUE_MAX_ITEM_SIZE, PDMQUEUE_MAX_TOTAL_SIZE_R0)
84
85#else
86# define PDMQUEUE_HANDLE_TO_VARS_RETURN(a_pVM, a_hQueue, a_pvOwner) \
87 AssertPtrReturn((a_pvOwner), VERR_INVALID_PARAMETER); \
88 \
89 PPDMQUEUE pQueue; \
90 if ((a_hQueue) < RT_ELEMENTS((a_pVM)->pdm.s.apRing0Queues)) \
91 pQueue = (a_pVM)->pdm.s.apRing0Queues[(a_hQueue)]; \
92 else \
93 { \
94 (a_hQueue) -= RT_ELEMENTS((a_pVM)->pdm.s.apRing0Queues); \
95 AssertReturn((a_pVM)->pdm.s.cRing3Queues, VERR_INVALID_HANDLE); \
96 pQueue = (a_pVM)->pdm.s.papRing3Queues[(a_hQueue)]; \
97 } \
98 AssertPtrReturn(pQueue, VERR_INVALID_HANDLE); \
99 AssertReturn(pQueue->u32Magic == PDMQUEUE_MAGIC, VERR_INVALID_HANDLE); \
100 AssertReturn(pQueue->u.Gen.pvOwner == (a_pvOwner), VERR_INVALID_HANDLE); \
101 AssertReturn(pQueue->rcOkay == VINF_SUCCESS, pQueue->rcOkay); \
102 \
103 uint32_t const cbItem = pQueue->cbItem; \
104 uint32_t const cItems = pQueue->cItems; \
105 uint32_t const offItems = pQueue->offItems; \
106 \
107 PDMQUEUE_HANDLE_TO_VARS_RETURN_COMMON(PDMQUEUE_MAX_ITEM_SIZE, PDMQUEUE_MAX_TOTAL_SIZE_R3)
108
109#endif
110
111
112/**
113 * Commmon function for initializing the shared queue structure.
114 */
115void pdmQueueInit(PPDMQUEUE pQueue, uint32_t cbBitmap, uint32_t cbItem, uint32_t cItems,
116 const char *pszName, PDMQUEUETYPE enmType, RTR3PTR pfnCallback, RTR3PTR pvOwner)
117{
118 Assert(cbBitmap * 8 >= cItems);
119
120 pQueue->u32Magic = PDMQUEUE_MAGIC;
121 pQueue->cbItem = cbItem;
122 pQueue->cItems = cItems;
123 pQueue->offItems = RT_UOFFSETOF(PDMQUEUE, bmAlloc) + cbBitmap;
124 pQueue->rcOkay = VINF_SUCCESS;
125 pQueue->u32Padding = 0;
126 pQueue->hTimer = NIL_TMTIMERHANDLE;
127 pQueue->cMilliesInterval = 0;
128 pQueue->enmType = enmType;
129 pQueue->u.Gen.pfnCallback = pfnCallback;
130 pQueue->u.Gen.pvOwner = pvOwner;
131 RTStrCopy(pQueue->szName, sizeof(pQueue->szName), pszName);
132 pQueue->iPending = UINT32_MAX;
133 RT_BZERO(pQueue->bmAlloc, cbBitmap);
134 ASMBitSetRange(pQueue->bmAlloc, 0, cItems);
135
136 uint8_t *pbItem = (uint8_t *)&pQueue->bmAlloc[0] + cbBitmap;
137 while (cItems-- > 0)
138 {
139 ((PPDMQUEUEITEMCORE)pbItem)->u64View = UINT64_C(0xfeedfeedfeedfeed);
140
141 /* next */
142 pbItem += cbItem;
143 }
144}
145
146
147/**
148 * Allocate an item from a queue, extended version.
149 *
150 * The allocated item must be handed on to PDMR3QueueInsert() after the
151 * data have been filled in.
152 *
153 * @returns VBox status code.
154 * @param pVM Pointer to the cross context VM structure w/ ring-0.
155 * @param hQueue The queue handle.
156 * @param pvOwner The queue owner.
157 * @param ppNew Where to return the item pointer on success.
158 * @thread Any thread.
159 */
160VMMDECL(int) PDMQueueAllocEx(PVMCC pVM, PDMQUEUEHANDLE hQueue, void *pvOwner, PPDMQUEUEITEMCORE *ppNew)
161{
162 /*
163 * Validate and translate input.
164 */
165 *ppNew = NULL;
166 PDMQUEUE_HANDLE_TO_VARS_RETURN(pVM, hQueue, pvOwner);
167
168 /*
169 * Do the allocation.
170 */
171 uint32_t cEmptyScans = 0;
172 for (;;)
173 {
174 int32_t iBit = ASMBitFirstSet(pQueue->bmAlloc, cItems);
175 if (iBit >= 0)
176 {
177 if (ASMAtomicBitTestAndClear(pQueue->bmAlloc, iBit))
178 {
179 PPDMQUEUEITEMCORE pNew = (PPDMQUEUEITEMCORE)&((uint8_t *)pQueue)[offItems + iBit * cbItem];
180 pNew->u64View = UINT64_C(0xbeefbeefbeefbeef);
181 *ppNew = pNew;
182 return VINF_SUCCESS;
183 }
184 cEmptyScans = 0;
185 }
186 else if (++cEmptyScans < 16)
187 ASMNopPause();
188 else
189 {
190 STAM_REL_COUNTER_INC(&pQueue->StatAllocFailures);
191 return VERR_OUT_OF_RESOURCES;
192 }
193 }
194}
195
196
197/**
198 * Allocate an item from a queue.
199 *
200 * The allocated item must be handed on to PDMR3QueueInsert() after the
201 * data have been filled in.
202 *
203 * @returns Pointer to the new item on success, NULL on failure.
204 * @param pVM Pointer to the cross context VM structure w/ ring-0.
205 * @param hQueue The queue handle.
206 * @param pvOwner The queue owner.
207 * @thread Any thread.
208 */
209VMMDECL(PPDMQUEUEITEMCORE) PDMQueueAlloc(PVMCC pVM, PDMQUEUEHANDLE hQueue, void *pvOwner)
210{
211 PPDMQUEUEITEMCORE pNew = NULL;
212 int rc = PDMQueueAllocEx(pVM, hQueue, pvOwner, &pNew);
213 if (RT_SUCCESS(rc))
214 return pNew;
215 return NULL;
216}
217
218
219/**
220 * Sets the FFs and fQueueFlushed.
221 *
222 * @param pVM Pointer to the cross context VM structure w/ ring-0.
223 */
224static void pdmQueueSetFF(PVMCC pVM)
225{
226 Log2(("PDMQueueInsert: VM_FF_PDM_QUEUES %d -> 1\n", VM_FF_IS_SET(pVM, VM_FF_PDM_QUEUES)));
227 VM_FF_SET(pVM, VM_FF_PDM_QUEUES);
228 ASMAtomicBitSet(&pVM->pdm.s.fQueueFlushing, PDM_QUEUE_FLUSH_FLAG_PENDING_BIT);
229#ifdef IN_RING3
230 VMR3NotifyGlobalFFU(pVM->pUVM, VMNOTIFYFF_FLAGS_DONE_REM);
231#endif
232}
233
234
235/**
236 * Queue an item.
237 *
238 * The item must have been obtained using PDMQueueAlloc(). Once the item
239 * have been passed to this function it must not be touched!
240 *
241 * @returns VBox status code.
242 * @param pVM Pointer to the cross context VM structure w/ ring-0.
243 * @param hQueue The queue handle.
244 * @param pvOwner The queue owner.
245 * @param pInsert The item to insert.
246 * @thread Any thread.
247 */
248VMMDECL(int) PDMQueueInsert(PVMCC pVM, PDMQUEUEHANDLE hQueue, void *pvOwner, PPDMQUEUEITEMCORE pInsert)
249{
250 /*
251 * Validate and translate input.
252 */
253 PDMQUEUE_HANDLE_TO_VARS_RETURN(pVM, hQueue, pvOwner);
254
255 uint8_t * const pbItems = (uint8_t *)pQueue + offItems;
256 uintptr_t const offInsert = (uintptr_t)pInsert - (uintptr_t)pbItems;
257 uintptr_t const iInsert = offInsert / cbItem;
258 AssertReturn(iInsert < cItems, VERR_INVALID_PARAMETER);
259 AssertReturn(iInsert * cbItem == offInsert, VERR_INVALID_PARAMETER);
260
261 AssertReturn(ASMBitTest(pQueue->bmAlloc, iInsert) == false, VERR_INVALID_PARAMETER);
262
263 /*
264 * Append the item to the pending list.
265 */
266 for (;;)
267 {
268 uint32_t const iOldPending = ASMAtomicUoReadU32(&pQueue->iPending);
269 pInsert->iNext = iOldPending;
270 if (ASMAtomicCmpXchgU32(&pQueue->iPending, iInsert, iOldPending))
271 break;
272 ASMNopPause();
273 }
274
275 if (pQueue->hTimer == NIL_TMTIMERHANDLE)
276 pdmQueueSetFF(pVM);
277 STAM_REL_COUNTER_INC(&pQueue->StatInsert);
278 STAM_STATS({ ASMAtomicIncU32(&pQueue->cStatPending); });
279
280 return VINF_SUCCESS;
281}
282
283
284/**
285 * Schedule the queue for flushing (processing) if necessary.
286 *
287 * @returns VBox status code.
288 * @retval VINF_SUCCESS if a flush was necessary.
289 * @retval VINF_NO_CHANGE if no flushing needed.
290 *
291 * @param pVM The cross context VM structure.
292 * @param pvOwner The alleged queue owner.
293 * @param hQueue The queueu to maybe flush.
294 */
295VMMDECL(int) PDMQueueFlushIfNecessary(PVMCC pVM, PDMQUEUEHANDLE hQueue, void *pvOwner)
296{
297 /*
298 * Validate input.
299 */
300 PDMQUEUE_HANDLE_TO_VARS_RETURN(pVM, hQueue, pvOwner);
301 RT_NOREF(offItems);
302
303 /*
304 * Check and maybe flush.
305 */
306 if (ASMAtomicUoReadU32(&pQueue->iPending) != UINT32_MAX)
307 {
308 pdmQueueSetFF(pVM);
309 return VINF_SUCCESS;
310 }
311 return VINF_NO_CHANGE;
312}
313
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