VirtualBox

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

最後變更 在這個檔案從93609是 93609,由 vboxsync 提交於 3 年 前

VMM/PDMQueue: Rewrote the queue code to not use the hyper heap and be a bit safer. Added a testcase (driverless). bugref:10093

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

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