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