VirtualBox

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

Last change on this file was 99674, checked in by vboxsync, 13 months ago

VMM/PDMQueue: Fix PDMQueueAllocEx() to respect the multiple of 32 cBits input parameter requirement (mainly to fix the tstPDMQueue on ARM which uses a different ASMBitFirstSet() implementation which is not as forgiving), and a small fix to the testcase itself to not crash when allocating an item failed, which is counted as an error anyway

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 11.3 KB
Line 
1/* $Id: PDMAllQueue.cpp 99674 2023-05-08 13:27:47Z 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 /*
175 * ASMBitFirstSet() mandates a cBits to be a multiple of 32 while cItems can be less (but the bitmap)
176 * is aligned to 64 bytes and correctly initialized so only the number of items allocated are valid
177 * and can be marked as free.
178 */
179 int32_t iBit = ASMBitFirstSet(pQueue->bmAlloc, RT_ALIGN(cItems, 32));
180 if (iBit >= 0)
181 {
182 if (ASMAtomicBitTestAndClear(pQueue->bmAlloc, iBit))
183 {
184 PPDMQUEUEITEMCORE pNew = (PPDMQUEUEITEMCORE)&((uint8_t *)pQueue)[offItems + iBit * cbItem];
185 pNew->u64View = UINT64_C(0xbeefbeefbeefbeef);
186 *ppNew = pNew;
187 return VINF_SUCCESS;
188 }
189 cEmptyScans = 0;
190 }
191 else if (++cEmptyScans < 16)
192 ASMNopPause();
193 else
194 {
195 STAM_REL_COUNTER_INC(&pQueue->StatAllocFailures);
196 return VERR_OUT_OF_RESOURCES;
197 }
198 }
199}
200
201
202/**
203 * Allocate an item from a queue.
204 *
205 * The allocated item must be handed on to PDMR3QueueInsert() after the
206 * data have been filled in.
207 *
208 * @returns Pointer to the new item on success, NULL on failure.
209 * @param pVM Pointer to the cross context VM structure w/ ring-0.
210 * @param hQueue The queue handle.
211 * @param pvOwner The queue owner.
212 * @thread Any thread.
213 */
214VMMDECL(PPDMQUEUEITEMCORE) PDMQueueAlloc(PVMCC pVM, PDMQUEUEHANDLE hQueue, void *pvOwner)
215{
216 PPDMQUEUEITEMCORE pNew = NULL;
217 int rc = PDMQueueAllocEx(pVM, hQueue, pvOwner, &pNew);
218 if (RT_SUCCESS(rc))
219 return pNew;
220 return NULL;
221}
222
223
224/**
225 * Sets the FFs and fQueueFlushed.
226 *
227 * @param pVM Pointer to the cross context VM structure w/ ring-0.
228 */
229static void pdmQueueSetFF(PVMCC pVM)
230{
231 Log2(("PDMQueueInsert: VM_FF_PDM_QUEUES %d -> 1\n", VM_FF_IS_SET(pVM, VM_FF_PDM_QUEUES)));
232 VM_FF_SET(pVM, VM_FF_PDM_QUEUES);
233 ASMAtomicBitSet(&pVM->pdm.s.fQueueFlushing, PDM_QUEUE_FLUSH_FLAG_PENDING_BIT);
234#ifdef IN_RING3
235 VMR3NotifyGlobalFFU(pVM->pUVM, VMNOTIFYFF_FLAGS_DONE_REM);
236#endif
237}
238
239
240/**
241 * Queue an item.
242 *
243 * The item must have been obtained using PDMQueueAlloc(). Once the item
244 * have been passed to this function it must not be touched!
245 *
246 * @returns VBox status code.
247 * @param pVM Pointer to the cross context VM structure w/ ring-0.
248 * @param hQueue The queue handle.
249 * @param pvOwner The queue owner.
250 * @param pInsert The item to insert.
251 * @thread Any thread.
252 */
253VMMDECL(int) PDMQueueInsert(PVMCC pVM, PDMQUEUEHANDLE hQueue, void *pvOwner, PPDMQUEUEITEMCORE pInsert)
254{
255 /*
256 * Validate and translate input.
257 */
258 PDMQUEUE_HANDLE_TO_VARS_RETURN(pVM, hQueue, pvOwner);
259
260 uint8_t * const pbItems = (uint8_t *)pQueue + offItems;
261 uintptr_t const offInsert = (uintptr_t)pInsert - (uintptr_t)pbItems;
262 uintptr_t const iInsert = offInsert / cbItem;
263 AssertReturn(iInsert < cItems, VERR_INVALID_PARAMETER);
264 AssertReturn(iInsert * cbItem == offInsert, VERR_INVALID_PARAMETER);
265
266 AssertReturn(ASMBitTest(pQueue->bmAlloc, iInsert) == false, VERR_INVALID_PARAMETER);
267
268 /*
269 * Append the item to the pending list.
270 */
271 for (;;)
272 {
273 uint32_t const iOldPending = ASMAtomicUoReadU32(&pQueue->iPending);
274 pInsert->iNext = iOldPending;
275 if (ASMAtomicCmpXchgU32(&pQueue->iPending, iInsert, iOldPending))
276 break;
277 ASMNopPause();
278 }
279
280 if (pQueue->hTimer == NIL_TMTIMERHANDLE)
281 pdmQueueSetFF(pVM);
282 STAM_REL_COUNTER_INC(&pQueue->StatInsert);
283 STAM_STATS({ ASMAtomicIncU32(&pQueue->cStatPending); });
284
285 return VINF_SUCCESS;
286}
287
288
289/**
290 * Schedule the queue for flushing (processing) if necessary.
291 *
292 * @returns VBox status code.
293 * @retval VINF_SUCCESS if a flush was necessary.
294 * @retval VINF_NO_CHANGE if no flushing needed.
295 *
296 * @param pVM The cross context VM structure.
297 * @param pvOwner The alleged queue owner.
298 * @param hQueue The queueu to maybe flush.
299 */
300VMMDECL(int) PDMQueueFlushIfNecessary(PVMCC pVM, PDMQUEUEHANDLE hQueue, void *pvOwner)
301{
302 /*
303 * Validate input.
304 */
305 PDMQUEUE_HANDLE_TO_VARS_RETURN(pVM, hQueue, pvOwner);
306 RT_NOREF(offItems);
307
308 /*
309 * Check and maybe flush.
310 */
311 if (ASMAtomicUoReadU32(&pQueue->iPending) != UINT32_MAX)
312 {
313 pdmQueueSetFF(pVM);
314 return VINF_SUCCESS;
315 }
316 return VINF_NO_CHANGE;
317}
318
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use