I’ve written some code that aims to stall all processors except the currently executing one using DPC routines. I’d appreciate it if someone could review and let me know if this is correct or if there are potential improvements/issues. Here’s the code:
ULONG GetNumberOfProcessors()
{
SYSTEM_BASIC_INFORMATION sysInfo;
ZwQuerySystemInformation(SystemBasicInformation, &sysInfo, sizeof(sysInfo), NULL);
return sysInfo.NumberOfProcessors;
}
volatile LONG stallCount;
void DpcRoutine(KDPC* pDpc, PVOID pContext, PVOID pArg1, PVOID pArg2)
{
InterlockedDecrement(&stallCount);
while (InterlockedCompareExchange(&stallCount, 0, 0) > 0)
{
_mm_pause(); // Busy-wait
}
}
void StallOtherProcessors()
{
CCHAR currentProcessor = (CCHAR)KeGetCurrentProcessorNumber();
CCHAR cpuCount = (CCHAR)GetNumberOfProcessors();
KDPC dpcTraps[MAXIMUM_PROCESSORS];
// Initialize DPCs for all processors
for (CCHAR i = 0; i < cpuCount; i++)
{
KeInitializeDpc(&dpcTraps[i], DpcRoutine, NULL);
KeSetImportanceDpc(&dpcTraps[i], HighImportance);
KeSetTargetProcessorDpc(&dpcTraps[i], i);
}
// Stall all processors except the current one
stallCount = cpuCount - 1; // Exclude the current processor
for (CCHAR i = 0; i < cpuCount; i++)
{
if (i != currentProcessor)
{
KeInsertQueueDpc(&dpcTraps[i], NULL, NULL);
}
}
// Wait for all DPCs to execute, stalling the other processors
while (InterlockedCompareExchange(&stallCount, 0, 0) > 0)
{
_mm_pause(); // Busy-wait
}
}
void ResumeOtherProcessors()
{
InterlockedExchange(&stallCount, 0); // Allow the other processors to exit the DPC routine
}
This is not immune to interrupts.
@Tim_Roberts said:
This is not immune to interrupts.
yes that’s why i do
void myfunction(){
KIRQL QL;
KeRaiseIrql(DISPATCH_LEVEL,&QL);
StallOtherProcessors();
func();
ResumeOtherProcessors();
KeLowerIrql(QL);
}
That doesn’t help. DISPATCH_LEVEL is way below the IRQL of hardware interrupts.
@Tim_Roberts said:
That doesn’t help. DISPATCH_LEVEL is way below the IRQL of hardware interrupts.
i see so my only option is do it like this
KIRQL QL;
KeRaiseIrql(HIGH_LEVEL,&QL);
_disable();
StallOtherProcessors();
func();
ResumeOtherProcessors();
_enable();
KeLowerIrql(QL);
change KeGetCurrentProcessorNumber(IRQL >= DISPATCH_LEVEL.) and ZwQuerySystemInformation(PASSIVE_LEVEL)
Hmmm… I don’t see thepoint of both raising to IRQL HIGH_LEVEL and calling __disable()… what are you trying to accomplish by this?
In order to have the OTHER processors not handle interrupts, the DPC routine that they execute is going to have to raise to IRQL HIGH_LEVEL.
Also, you can replace your GetNumberOfProcessors() function with a call to KeQueryActiveProcessorCount.
Finally, just so that SOMEbody in this thread has said it once: What you’re doing looks like it’s probably a bad idea. I’m not sure what your overall goal is, but the sort of “big hammer” that you’re using here is rarely necessary.
Peter
1 Like
You might want to look at KeIpiGenericCall as this does exactly what you are trying to do, only without the defects you are coding up :-). You still have to implement the corral in your callback routine, but that is fairly trivial.
I forgot to add that if you read the docs carefully, the callback guarantees that all the processor have responded to the IPI when the callback is invoked, so your corral only needs to nominate one of the callers as the active thread, generally the first one in using an interlocked expression, and then release all the waiters when it is done.
thanks i found a code that do all that
i was wondering how i can skip core 0 in this code ?
_IRQL_raises_(DISPATCH_LEVEL)
static PSTOP_PROCESSORS_DATA StopProcessors()
{
PSTOP_PROCESSORS_DATA Data = (PSTOP_PROCESSORS_DATA)ExAllocatePoolWithTag(NonPagedPool, sizeof(*Data), POOL_TAG);
if (!Data) return NULL;
RtlZeroMemory(Data, sizeof(*Data));
KeRaiseIrql(DISPATCH_LEVEL, &Data->PreviousIrql);
KAFFINITY ActiveProcessors = 0;
ULONG CurrentProcessor = KeGetCurrentProcessorNumber();
ULONG ProcessorsCount = KeQueryActiveProcessorCount(&ActiveProcessors);
Data->NeedToBeStopped = ProcessorsCount;
for (unsigned i = 0; i < ProcessorsCount; ++i) {
if (i == CurrentProcessor) continue;
KeInitializeDpc(&Data->Dpcs[i], (PKDEFERRED_ROUTINE)StallDpcRoutine, Data);
KeSetTargetProcessorDpc(&Data->Dpcs[i], (CCHAR)i);
KeSetImportanceDpc(&Data->Dpcs[i], HighImportance);
KeInsertQueueDpc(&Data->Dpcs[i], NULL, NULL);
}
InterlockedIncrement(&Data->ProcessorsStopped);
WaitForEquality(&Data->ProcessorsStopped, ProcessorsCount);
return Data;
}
is this approch correct ?
Data->NeedToBeStopped = ProcessorsCount - 1; // Reducing for core 0
if (CurrentProcessor != 0) {
Data->NeedToBeStopped -= 1; // Further reduce if the current core isn't core 0
}
for (unsigned i = 0; i < ProcessorsCount; ++i) {
if (i == CurrentProcessor || i == 0) continue; // skip core 0
KeInitializeDpc(&Data->Dpcs[i], (PKDEFERRED_ROUTINE)StallDpcRoutine, Data);
KeSetTargetProcessorDpc(&Data->Dpcs[i], (CCHAR)i);
KeSetImportanceDpc(&Data->Dpcs[i], HighImportance);
KeInsertQueueDpc(&Data->Dpcs[i], NULL, NULL);
}
InterlockedIncrement(&Data->ProcessorsStopped);
WaitForEquality(&Data->ProcessorsStopped, Data->NeedToBeStopped);