https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-system_cpu_set_information
MSDN says nothing about it, but I'm wondering what does SchedulingClass mean?
I'm looking at AMD Threadripper PRO 5975WX (32 core / 64 logical processors) that has the same cores (from Windows point of view) with EfficiencyClass=0 except SchedulingClass. The SchedulingClass is different for the first 32 logical cores, but the last 32 cores have SchedulingClass=0. What's the difference? Does this somehow affect the performance of cores?
I also compared the frequencies and PROCESSOR_POWER_INFORMATION of each core and they are all the same.
(Note: a bit heavy on assembly and kernel stuff).
Since the structure is a bit hard to parse (even visually), so the offset of the SchedulingClass is 20 (0x14):
size_t offScheduling = offsetof(SYSTEM_CPU_SET_INFORMATION, CpuSet.SchedulingClass);
printf("off: %llu", offScheduling); // prints 20
The structure itself is used by GetSystemCpuSetInformation (exported by kernel32.dll).
In fact it is located in kernelbase.dll through export forwarding. The function is a wrapper around nt!NtQuerySystemInformationEx (thus a kernel function) with case 0xAF:
00007FFC8C22C5D0 <kernelbase.GetSystemCpuSetInformation>
; ...
00007FFC8C22C60E mov ecx,AF
00007FFC8C22C613 call qword ptr ds:[<&NtQuerySystemInformationEx>]
For case 0xAF, nt!NtQuerySystemInformationEx calls nt!ExpQuerySystemInformation (with the same case number), and we land here:
PAGE:000000014069215C loc_14069215C:
PAGE:000000014069215C mov rcx, [rsp+338h+Handle] ; jumptable 000000014068F773 case 175
PAGE:0000000140692164 test rcx, rcx
PAGE:0000000140692167 jz short loc_1406921A9
PAGE:0000000140692169 mov r8, cs:PsProcessType ; ObjectType
PAGE:0000000140692170 mov [rsp+338h+var_238], r14
PAGE:0000000140692178 mov [rsp+338h+HandleInformation], r14 ; HandleInformation
PAGE:000000014069217D lea rax, [rsp+338h+var_238]
PAGE:0000000140692185 mov [rsp+338h+Object], rax ; Object
PAGE:000000014069218A movzx r9d, r12b ; AccessMode
PAGE:000000014069218E mov edx, 1000h ; DesiredAccess
PAGE:0000000140692193 call ObReferenceObjectByHandle
PAGE:0000000140692198 mov r14, [rsp+338h+var_238]
PAGE:00000001406921A0 test eax, eax
PAGE:00000001406921A2 jns short loc_1406921B1
PAGE:00000001406921A4 jmp loc_1406925ED
PAGE:00000001406921A9 ; ---------------------------------------------------------------------------
PAGE:00000001406921A9
PAGE:00000001406921A9 loc_1406921A9: ; CODE XREF: ExpQuerySystemInformation+2D87↑j
PAGE:00000001406921A9 mov r14, [rsp+338h+Process]
PAGE:00000001406921B1
PAGE:00000001406921B1 loc_1406921B1: ; CODE XREF: ExpQuerySystemInformation+2DC2↑j
PAGE:00000001406921B1 mov r9, r14
PAGE:00000001406921B4 lea r8, [rsp+338h+Size]
PAGE:00000001406921B9 mov edx, edi
PAGE:00000001406921BB mov rcx, rbx ; void *
PAGE:00000001406921BE call KeQueryCpuSetInformation
What happens above: if you passed a process handle to GetSystemCpuSetInformation, the handle is first dereferenced (that is, handle -> _EPROCESS) and then nt!KeQueryCpuSetInformation is called.
In nt!KeQueryCpuSetInformation we have this code:
PAGE:00000001407B4D02 call KiGetCpuSetData
PAGE:00000001407B4D07 mov r13, rax
; ...
PAGE:00000001407B4D56 mov al, [r13+6]
PAGE:00000001407B4D5A mov [r11+14h], al ; set SchedulingClass!
So, nt!KiGetCpuSetData returns a structure; from this structure the value at offset +6 is used to set the SchedulingClass field.
(Note that r11 is the output structure).
Now let's see in nt!KeQueryCpuSetInformation:
.text:0000000140360D94 KiGetCpuSetData proc near
.text:0000000140360D94
.text:0000000140360D94 shl ecx, 6
.text:0000000140360D97 lea eax, [rdx+rcx]
.text:0000000140360D9A shl rax, 4
.text:0000000140360D9E add rax, cs:KiCpuSetData
.text:0000000140360DA5 retn
.text:0000000140360DA5 KiGetCpuSetData endp
So everything comes from a global variable named nt!KiCpuSetData. Looking at the reference to this structure we just have a few hits.
One is interesting at nt!KiConfigureCpuSetSchedulingInformation:
.text:00000001403B284C KiConfigureCpuSetSchedulingInformation proc near
; ...
.text:00000001403B2865 mov rsi, rcx
...
.text:00000001403B28C3 add r8, cs:KiCpuSetData
; ...
.text:00000001403B293B mov al, [rsi+81B9h]
.text:00000001403B2941 mov [r8+6], al ; set offset +6.
So, RSI is a structure, the code uses offset 0x81B9 to set the offset +6 in the current structure in KiCpuSetData.
rsi actually points to the _KPRCB (Kernel Processor Control Block) structure which is a structure that (basically) represents a CPU for the kernel (alongside its host structure, the _KPCR; Kernel Processor Control Region).
PAGE:00000001408B7295 lea rax, KiProcessorBlock ; global system KPRCB
PAGE:00000001408B729C mov ecx, edi
PAGE:00000001408B729E mov rcx, [rax+rcx*8] ; pick the one for the current processor.
PAGE:00000001408B72A2 call KiConfigureCpuSetSchedulingInformation
Let's see offset 0x81B9:
typedef struct _KPRCB // 351 elements, 0xAF00 bytes (sizeof)
{
// ...
/*0x7FF8*/ VOID* MmInternal;
/*0x8000*/ struct _PROCESSOR_POWER_STATE PowerState; // 46 elements, 0x200 bytes (sizeof)
/*0x8200*/ VOID* HyperPte;
// ...
}KPRCB, *PKPRCB;
We have a structure at 0x8000, so it's inside this structure at offset 0x1B9 (0x8000 + 0x1B9 = 0x81B9):
typedef struct _PROCESSOR_POWER_STATE // 46 elements, 0x200 bytes (sizeof)
{
// ...
/*0x1B8*/ UINT8 ArchitecturalEfficiencyClass;
/*0x1B9*/ UINT8 PerformanceSchedulingClass;
/*0x1BA*/ UINT8 EfficiencySchedulingClass;
// ...
}PROCESSOR_POWER_STATE, *PPROCESSOR_POWER_STATE;
So the name of the field is PerformanceSchedulingClass. I didn't push further my investigations (where this field is actually set up)
but there's a nice explanation in this slide deck from AMD, page 19:
Some AMD products have cores which are faster than other cores. The system BIOS describes the CPPC Highest Performance ranking for each logical processor. The Windows Kernel creates a PerformanceSchedulingClass ranking based on this information and uses it during scheduling. Logical processor 0 and CCD0 may not be the fastest.
As far as I understand the above and from the code we've seen in the kernel, this SchedulingClass is used by the kernel to sort processors in groups based on how much they perform from a ground base? (I'm not a CPU specialist).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With