VirtualBox

source: vbox/trunk/src/VBox/Runtime/r0drv/nt/mp-r0drv-nt.cpp@ 32736

Last change on this file since 32736 was 28800, checked in by vboxsync, 15 years ago

Automated rebranding to Oracle copyright/license strings via filemuncher

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.6 KB
Line 
1/* $Id: mp-r0drv-nt.cpp 28800 2010-04-27 08:22:32Z vboxsync $ */
2/** @file
3 * IPRT - Multiprocessor, Ring-0 Driver, NT.
4 */
5
6/*
7 * Copyright (C) 2008 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#include "the-nt-kernel.h"
32
33#include <iprt/mp.h>
34#include <iprt/cpuset.h>
35#include <iprt/err.h>
36#include <iprt/asm.h>
37#include "r0drv/mp-r0drv.h"
38#include "internal-r0drv-nt.h"
39
40
41/*******************************************************************************
42* Structures and Typedefs *
43*******************************************************************************/
44typedef enum
45{
46 RT_NT_CPUID_SPECIFIC,
47 RT_NT_CPUID_OTHERS,
48 RT_NT_CPUID_ALL
49} RT_NT_CPUID;
50
51
52/* test a couple of assumption. */
53AssertCompile(MAXIMUM_PROCESSORS <= RTCPUSET_MAX_CPUS);
54AssertCompile(NIL_RTCPUID >= MAXIMUM_PROCESSORS);
55
56/** @todo
57 * We cannot do other than assume a 1:1 relationship between the
58 * affinity mask and the process despite the vagueness/warnings in
59 * the docs. If someone knows a better way to get this done, please
60 * let bird know.
61 */
62
63
64RTDECL(RTCPUID) RTMpCpuId(void)
65{
66 /* WDK upgrade warning: PCR->Number changed from BYTE to WORD. */
67 return KeGetCurrentProcessorNumber();
68}
69
70
71RTDECL(int) RTMpCpuIdToSetIndex(RTCPUID idCpu)
72{
73 return idCpu < MAXIMUM_PROCESSORS ? (int)idCpu : -1;
74}
75
76
77RTDECL(RTCPUID) RTMpCpuIdFromSetIndex(int iCpu)
78{
79 return (unsigned)iCpu < MAXIMUM_PROCESSORS ? iCpu : NIL_RTCPUID;
80}
81
82
83RTDECL(RTCPUID) RTMpGetMaxCpuId(void)
84{
85 /** @todo use KeQueryMaximumProcessorCount on vista+ */
86 return MAXIMUM_PROCESSORS - 1;
87}
88
89
90RTDECL(bool) RTMpIsCpuOnline(RTCPUID idCpu)
91{
92 if (idCpu >= MAXIMUM_PROCESSORS)
93 return false;
94
95#if 0 /* this isn't safe at all IRQLs (great work guys) */
96 KAFFINITY Mask = KeQueryActiveProcessors();
97 return !!(Mask & RT_BIT_64(idCpu));
98#else
99 return RTCpuSetIsMember(&g_rtMpNtCpuSet, idCpu);
100#endif
101}
102
103
104RTDECL(bool) RTMpIsCpuPossible(RTCPUID idCpu)
105{
106 /* Cannot easily distinguish between online and offline cpus. */
107 /** @todo online/present cpu stuff must be corrected for proper W2K8 support
108 * (KeQueryMaximumProcessorCount). */
109 return RTMpIsCpuOnline(idCpu);
110}
111
112
113
114RTDECL(PRTCPUSET) RTMpGetSet(PRTCPUSET pSet)
115{
116 /** @todo online/present cpu stuff must be corrected for proper W2K8 support
117 * (KeQueryMaximumProcessorCount). */
118 return RTMpGetOnlineSet(pSet);
119}
120
121
122RTDECL(RTCPUID) RTMpGetCount(void)
123{
124 /** @todo online/present cpu stuff must be corrected for proper W2K8 support
125 * (KeQueryMaximumProcessorCount). */
126 return RTMpGetOnlineCount();
127}
128
129
130RTDECL(PRTCPUSET) RTMpGetOnlineSet(PRTCPUSET pSet)
131{
132#if 0 /* this isn't safe at all IRQLs (great work guys) */
133 KAFFINITY Mask = KeQueryActiveProcessors();
134 return RTCpuSetFromU64(pSet, Mask);
135#else
136 *pSet = g_rtMpNtCpuSet;
137 return pSet;
138#endif
139}
140
141
142RTDECL(RTCPUID) RTMpGetOnlineCount(void)
143{
144 RTCPUSET Set;
145 RTMpGetOnlineSet(&Set);
146 return RTCpuSetCount(&Set);
147}
148
149
150#if 0
151/* Experiment with checking the undocumented KPRCB structure
152 * 'dt nt!_kprcb 0xaddress' shows the layout
153 */
154typedef struct
155{
156 LIST_ENTRY DpcListHead;
157 ULONG_PTR DpcLock;
158 volatile ULONG DpcQueueDepth;
159 ULONG DpcQueueCount;
160} KDPC_DATA, *PKDPC_DATA;
161
162RTDECL(bool) RTMpIsCpuWorkPending(void)
163{
164 uint8_t *pkprcb;
165 PKDPC_DATA pDpcData;
166
167 _asm {
168 mov eax, fs:0x20
169 mov pkprcb, eax
170 }
171 pDpcData = (PKDPC_DATA)(pkprcb + 0x19e0);
172 if (pDpcData->DpcQueueDepth)
173 return true;
174
175 pDpcData++;
176 if (pDpcData->DpcQueueDepth)
177 return true;
178 return false;
179}
180#else
181RTDECL(bool) RTMpIsCpuWorkPending(void)
182{
183 /** @todo not implemented */
184 return false;
185}
186#endif
187
188
189/**
190 * Wrapper between the native nt per-cpu callbacks and PFNRTWORKER
191 *
192 * @param Dpc DPC object
193 * @param DeferredContext Context argument specified by KeInitializeDpc
194 * @param SystemArgument1 Argument specified by KeInsertQueueDpc
195 * @param SystemArgument2 Argument specified by KeInsertQueueDpc
196 */
197static VOID rtmpNtDPCWrapper(IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2)
198{
199 PRTMPARGS pArgs = (PRTMPARGS)DeferredContext;
200 ASMAtomicIncU32(&pArgs->cHits);
201 pArgs->pfnWorker(KeGetCurrentProcessorNumber(), pArgs->pvUser1, pArgs->pvUser2);
202}
203
204
205/**
206 * Internal worker for the RTMpOn* APIs.
207 *
208 * @returns IPRT status code.
209 * @param pfnWorker The callback.
210 * @param pvUser1 User argument 1.
211 * @param pvUser2 User argument 2.
212 * @param enmCpuid What to do / is idCpu valid.
213 * @param idCpu Used if enmCpuid RT_NT_CPUID_SPECIFIC, otherwise ignored.
214 */
215static int rtMpCall(PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2, RT_NT_CPUID enmCpuid, RTCPUID idCpu)
216{
217 PRTMPARGS pArgs;
218 KDPC *paExecCpuDpcs;
219
220#if 0
221 /* KeFlushQueuedDpcs must be run at IRQL PASSIVE_LEVEL according to MSDN, but the
222 * driver verifier doesn't complain...
223 */
224 AssertMsg(KeGetCurrentIrql() == PASSIVE_LEVEL, ("%d != %d (PASSIVE_LEVEL)\n", KeGetCurrentIrql(), PASSIVE_LEVEL));
225#endif
226
227#ifdef IPRT_TARGET_NT4
228 KAFFINITY Mask;
229 /* g_pfnrtNt* are not present on NT anyway. */
230 return VERR_NOT_SUPPORTED;
231#else
232 KAFFINITY Mask = KeQueryActiveProcessors();
233#endif
234
235 /* KeFlushQueuedDpcs is not present in Windows 2000; import it dynamically so we can just fail this call. */
236 if (!g_pfnrtNtKeFlushQueuedDpcs)
237 return VERR_NOT_SUPPORTED;
238
239 pArgs = (PRTMPARGS)ExAllocatePoolWithTag(NonPagedPool, MAXIMUM_PROCESSORS*sizeof(KDPC) + sizeof(RTMPARGS), (ULONG)'RTMp');
240 if (!pArgs)
241 return VERR_NO_MEMORY;
242
243 pArgs->pfnWorker = pfnWorker;
244 pArgs->pvUser1 = pvUser1;
245 pArgs->pvUser2 = pvUser2;
246 pArgs->idCpu = NIL_RTCPUID;
247 pArgs->cHits = 0;
248
249 paExecCpuDpcs = (KDPC *)(pArgs + 1);
250
251 if (enmCpuid == RT_NT_CPUID_SPECIFIC)
252 {
253 KeInitializeDpc(&paExecCpuDpcs[0], rtmpNtDPCWrapper, pArgs);
254 KeSetImportanceDpc(&paExecCpuDpcs[0], HighImportance);
255 KeSetTargetProcessorDpc(&paExecCpuDpcs[0], (int)idCpu);
256 }
257 else
258 {
259 for (unsigned i = 0; i < MAXIMUM_PROCESSORS; i++)
260 {
261 KeInitializeDpc(&paExecCpuDpcs[i], rtmpNtDPCWrapper, pArgs);
262 KeSetImportanceDpc(&paExecCpuDpcs[i], HighImportance);
263 KeSetTargetProcessorDpc(&paExecCpuDpcs[i], i);
264 }
265 }
266
267 /* Raise the IRQL to DISPATCH_LEVEL so we can't be rescheduled to another cpu.
268 * KeInsertQueueDpc must also be executed at IRQL >= DISPATCH_LEVEL.
269 */
270 KIRQL oldIrql;
271 KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
272
273 /*
274 * We cannot do other than assume a 1:1 relationship between the
275 * affinity mask and the process despite the warnings in the docs.
276 * If someone knows a better way to get this done, please let bird know.
277 */
278 if (enmCpuid == RT_NT_CPUID_SPECIFIC)
279 {
280 BOOLEAN ret = KeInsertQueueDpc(&paExecCpuDpcs[0], 0, 0);
281 Assert(ret);
282 }
283 else
284 {
285 unsigned iSelf = KeGetCurrentProcessorNumber();
286
287 for (unsigned i = 0; i < MAXIMUM_PROCESSORS; i++)
288 {
289 if ( (i != iSelf)
290 && (Mask & RT_BIT_64(i)))
291 {
292 BOOLEAN ret = KeInsertQueueDpc(&paExecCpuDpcs[i], 0, 0);
293 Assert(ret);
294 }
295 }
296 if (enmCpuid != RT_NT_CPUID_OTHERS)
297 pfnWorker(iSelf, pvUser1, pvUser2);
298 }
299
300 KeLowerIrql(oldIrql);
301
302 /* Flush all DPCs and wait for completion. (can take long!) */
303 /** @todo Consider changing this to an active wait using some atomic inc/dec
304 * stuff (and check for the current cpu above in the specific case). */
305 g_pfnrtNtKeFlushQueuedDpcs();
306
307 ExFreePool(pArgs);
308 return VINF_SUCCESS;
309}
310
311
312RTDECL(int) RTMpOnAll(PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2)
313{
314 return rtMpCall(pfnWorker, pvUser1, pvUser2, RT_NT_CPUID_ALL, 0);
315}
316
317
318RTDECL(int) RTMpOnOthers(PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2)
319{
320 return rtMpCall(pfnWorker, pvUser1, pvUser2, RT_NT_CPUID_OTHERS, 0);
321}
322
323
324RTDECL(int) RTMpOnSpecific(RTCPUID idCpu, PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2)
325{
326 if (!RTMpIsCpuOnline(idCpu))
327 return !RTMpIsCpuPossible(idCpu)
328 ? VERR_CPU_NOT_FOUND
329 : VERR_CPU_OFFLINE;
330
331 return rtMpCall(pfnWorker, pvUser1, pvUser2, RT_NT_CPUID_SPECIFIC, idCpu);
332}
333
334static KDPC aPokeDpcs[MAXIMUM_PROCESSORS] = {0};
335static bool fPokeDPCsInitialized = false;
336
337static VOID rtMpNtPokeCpuDummy(IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2)
338{
339 NOREF(Dpc);
340 NOREF(DeferredContext);
341 NOREF(SystemArgument1);
342 NOREF(SystemArgument2);
343}
344
345#ifndef IPRT_TARGET_NT4
346
347ULONG_PTR rtMpIpiGenericCall(ULONG_PTR Argument)
348{
349 return 0;
350}
351
352int rtMpSendIpiVista(RTCPUID idCpu)
353{
354 g_pfnrtKeIpiGenericCall(rtMpIpiGenericCall, 0);
355//// g_pfnrtNtHalRequestIpi(1 << idCpu);
356 return VINF_SUCCESS;
357}
358
359int rtMpSendIpiWin7(RTCPUID idCpu)
360{
361 g_pfnrtKeIpiGenericCall(rtMpIpiGenericCall, 0);
362//// g_pfnrtNtHalSendSoftwareInterrupt(idCpu, DISPATCH_LEVEL);
363 return VINF_SUCCESS;
364}
365#endif /* IPRT_TARGET_NT4 */
366
367int rtMpSendIpiDummy(RTCPUID idCpu)
368{
369 return VERR_NOT_IMPLEMENTED;
370}
371
372RTDECL(int) RTMpPokeCpu(RTCPUID idCpu)
373{
374 if (!RTMpIsCpuOnline(idCpu))
375 return !RTMpIsCpuPossible(idCpu)
376 ? VERR_CPU_NOT_FOUND
377 : VERR_CPU_OFFLINE;
378
379 int rc = g_pfnrtSendIpi(idCpu);
380 if (rc == VINF_SUCCESS)
381 return rc;
382
383 /* Fallback. */
384 if (!fPokeDPCsInitialized)
385 {
386 for (unsigned i = 0; i < RT_ELEMENTS(aPokeDpcs); i++)
387 {
388 KeInitializeDpc(&aPokeDpcs[i], rtMpNtPokeCpuDummy, NULL);
389 KeSetImportanceDpc(&aPokeDpcs[i], HighImportance);
390 KeSetTargetProcessorDpc(&aPokeDpcs[i], (int)i);
391 }
392 fPokeDPCsInitialized = true;
393 }
394
395 /* Raise the IRQL to DISPATCH_LEVEL so we can't be rescheduled to another cpu.
396 * KeInsertQueueDpc must also be executed at IRQL >= DISPATCH_LEVEL.
397 */
398 KIRQL oldIrql;
399 KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
400
401 KeSetImportanceDpc(&aPokeDpcs[idCpu], HighImportance);
402 KeSetTargetProcessorDpc(&aPokeDpcs[idCpu], (int)idCpu);
403
404 /* Assuming here that high importance DPCs will be delivered immediately; or at least an IPI will be sent immediately.
405 * @note: not true on at least Vista & Windows 7
406 */
407 BOOLEAN bRet = KeInsertQueueDpc(&aPokeDpcs[idCpu], 0, 0);
408
409 KeLowerIrql(oldIrql);
410 return (bRet == TRUE) ? VINF_SUCCESS : VERR_ACCESS_DENIED /* already queued */;
411}
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