Нужно пройтись по регистрам чипсета, поэтому необходимо заблокировать прерывания на всех процессорах. На SMP-системах это не совсем просто...
Я пока сделал через interlocked-зацикливание в DPC, на "идеальном" SMP это всегда работаеет. Но на многих SMP-системах не каждый процессор может непосредственно послать IPI (поставить DPC в очередь) к любому другому, и в результате система может зациклиться.
Кто-нибудь знает про готовые решения?
// мой код пока примерно такой:
class TSingleProcessorMode
{
KDPC DpcTraps[MAXIMUM_PROCESSORS];
volatile LONG Stall;
KIRQL SavedIrql;
CCHAR CpuCount;
KPRIORITY SavedPriority;
static void DpcRoutine(KDPC *pDpc, void *pContext, void *pArg1, void *pArg2);
public:
void Initialize();
void Enter();
void Exit();
};
void TSingleProcessorMode::Initialize()
{
RtlZeroMemory(this, sizeof(TSingleProcessorMode));
CpuCount = (CCHAR) GetNumberOfProcessors();
if(CpuCount > 1)
{
for(CCHAR i = 0; i < CpuCount; i++)
{
KeInitializeDpc(&DpcTraps[i], DpcRoutine, this);
KeSetImportanceDpc(&DpcTraps[i], LowImportance);
KeSetTargetProcessorDpc(&DpcTraps[i], i);
}
}
}
void TSingleProcessorMode::DpcRoutine(KDPC *pDpc, void *pContext, void *pArg1, void *pArg2)
{
TSingleProcessorMode *pThis = (TSingleProcessorMode *) pContext;
InterlockedDecrement(&pThis->Stall);
do
__Crt::ProcessorPause();
while(pThis->Stall > 0);
if(pThis->Stall >= 0)
{
KIRQL Irql;
KeRaiseIrql(HIGH_LEVEL, &Irql);
DbgPrint("SingleProcessorMode: cpu %d stalled\n", KeGetCurrentProcessorNumber());
do
__Crt::ProcessorPause();
while(pThis->Stall >= 0);
KeLowerIrql(Irql);
}
}
void TSingleProcessorMode::Enter()
{
DbgPrint("SingleProcessorMode: entering\n");
if(CpuCount > 1)
{
SavedPriority = KeSetPriorityThread(KeGetCurrentThread(), HIGH_PRIORITY);
KAFFINITY ActiveProcessors = KeQueryActiveProcessors();
KeRaiseIrql(DISPATCH_LEVEL, &SavedIrql);
CCHAR CurrentProcessor = (CCHAR) KeGetCurrentProcessorNumber();
Stall = 1;
for(CCHAR i = CpuCount - 1; i >= 0; i--)
if(i != CurrentProcessor && (ActiveProcessors & (1u << i)) != 0)
{
InterlockedIncrement(&Stall);
KeInsertQueueDpc(&DpcTraps[i], 0, 0);
}
KeLowerIrql(SavedIrql);
InterlockedDecrement(&Stall);
__Crt::ProcessorPause();
KeRaiseIrql(HIGH_LEVEL, &SavedIrql);
if(Stall > 0)
{
DbgPrint("SingleProcessorMode: wait for %d cpu\n", Stall);
KeLowerIrql(SavedIrql);
do
__Crt::ProcessorPause();
while(Stall > 0);
KeRaiseIrql(HIGH_LEVEL, &SavedIrql);
}
}
else
KeRaiseIrql(HIGH_LEVEL, &SavedIrql);
}
void TSingleProcessorMode::Exit()
{
if(CpuCount > 1)
{
InterlockedExchange(&Stall, -1);
KeLowerIrql(SavedIrql);
KeSetPriorityThread(KeGetCurrentThread(), SavedPriority);
}
else
KeLowerIrql(SavedIrql);
DbgPrint("SingleProcessorMode: exit\n");
}
---
|