VirtualBox

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

Last change on this file since 25536 was 24034, checked in by vboxsync, 15 years ago

Backed out r53864; will cause too many problems unfortunately.

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