On Thu, 7 Nov 2024 10:13:38 +0800
maobibo <maob...@loongson.cn> wrote:

> On 2024/11/6 下午6:41, Igor Mammedov wrote:
> > On Tue, 29 Oct 2024 21:19:15 +0800
> > Zhao Liu <zhao1....@intel.com> wrote:
> >   
> >> Hi Bibo,
> >>
> >> [snip]
> >>  
> >>> +In the CPU topology relationship, When we know the ``socket_id`` 
> >>> ``core_id``
> >>> +and ``thread_id`` of the CPU, we can calculate its ``arch_id``:
> >>> +
> >>> +``arch_id = (socket_id * S) + (core_id * C) + (thread_id * T)``  
> >>
> >> What's the difference between arch_id and CPU index (CPUState.cpu_index)?  
> > 
> > They might be the same but not necessarily.
> > arch_id is unique cpu identifier from architecture point of view
> > (which easily could be sparse and without specific order).
> > (ex: for x86 it's apic_id, for spapr it's core_id)
> > 
> > while cpu_index is internal QEMU, that existed before possible_cpus[]
> > and non-sparse range and depends on order of cpus are created.
> > For machines that support possible_cpus[], we override default
> > cpu_index assignment to fit possible_cpus[].
> > 
> > It might be nice to get rid of cpu_index in favor of possible_cpus[],
> > but that would be a lot work probably with no huge benefit
> > when it comes majority of machines that don't need variable
> > cpu count.
> >   
> >>      
> >>>   static void virt_init(MachineState *machine)
> >>>   {
> >>> -    LoongArchCPU *lacpu;
> >>>       const char *cpu_model = machine->cpu_type;
> >>>       MemoryRegion *address_space_mem = get_system_memory();
> >>>       LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine);
> >>> @@ -1145,7 +1144,7 @@ static void virt_init(MachineState *machine)
> >>>       hwaddr base, size, ram_size = machine->ram_size;
> >>>       const CPUArchIdList *possible_cpus;
> >>>       MachineClass *mc = MACHINE_GET_CLASS(machine);
> >>> -    CPUState *cpu;
> >>> +    Object *cpuobj;
> >>>   
> >>>       if (!cpu_model) {
> >>>           cpu_model = LOONGARCH_CPU_TYPE_NAME("la464");
> >>> @@ -1164,12 +1163,25 @@ static void virt_init(MachineState *machine)
> >>>   
> >>>       /* Init CPUs */
> >>>       possible_cpus = mc->possible_cpu_arch_ids(machine);
> >>> -    for (i = 0; i < possible_cpus->len; i++) {
> >>> -        cpu = cpu_create(machine->cpu_type);
> >>> -        cpu->cpu_index = i;
> >>> -        machine->possible_cpus->cpus[i].cpu = cpu;
> >>> -        lacpu = LOONGARCH_CPU(cpu);
> >>> -        lacpu->phy_id = machine->possible_cpus->cpus[i].arch_id;
> >>> +    for (i = 0; i < machine->smp.cpus; i++) {
> >>> +        cpuobj = object_new(machine->cpu_type);
> >>> +        object_property_set_uint(cpuobj, "phy-id",
> >>> +                                possible_cpus->cpus[i].arch_id, NULL);
> >>> +        /*
> >>> +         * The CPU in place at the time of machine startup will also 
> >>> enter
> >>> +         * the CPU hot-plug process when it is created, but at this time,
> >>> +         * the GED device has not been created, resulting in exit in the 
> >>> CPU
> >>> +         * hot-plug process, which can avoid the incumbent CPU repeatedly
> >>> +         * applying for resources.
> >>> +         *
> >>> +         * The interrupt resource of the in-place CPU will be requested 
> >>> at
> >>> +         * the current function call virt_irq_init().
> >>> +         *
> >>> +         * The interrupt resource of the subsequently inserted CPU will 
> >>> be
> >>> +         * requested in the CPU hot-plug process.
> >>> +         */
> >>> +        qdev_realize(DEVICE(cpuobj), NULL, &error_fatal);
> >>> +        object_unref(cpuobj);  
> >>
> >> You can use qdev_realize_and_unref().
> >>  
> >>>       }
> >>>       fdt_add_cpu_nodes(lvms);
> >>>       fdt_add_memory_nodes(machine);
> >>> @@ -1286,6 +1298,35 @@ static void virt_initfn(Object *obj)
> >>>       virt_flash_create(lvms);
> >>>   }
> >>>   
> >>> +static int virt_get_arch_id_from_topo(MachineState *ms, LoongArchCPUTopo 
> >>> *topo)
> >>> +{
> >>> +    int arch_id, sock_vcpu_num, core_vcpu_num;
> >>> +
> >>> +    /*
> >>> +     * calculate total logical cpus across socket/core/thread.
> >>> +     * For more information on how to calculate the arch_id,
> >>> +     * you can refer to the CPU Topology chapter of the
> >>> +     * docs/system/loongarch/virt.rst document.
> >>> +     */
> >>> +    sock_vcpu_num = topo->socket_id * (ms->smp.threads * ms->smp.cores);
> >>> +    core_vcpu_num = topo->core_id * ms->smp.threads;
> >>> +
> >>> +    /* get vcpu-id(logical cpu index) for this vcpu from this topology */
> >>> +    arch_id = (sock_vcpu_num + core_vcpu_num) + topo->thread_id;  
> >>
> >> Looking at the calculations, arch_id looks the same as cpu_index, right?
> >>  
> >>> +    assert(arch_id >= 0 && arch_id < ms->possible_cpus->len);
> >>> +
> >>> +    return arch_id;
> >>> +}
> >>> +
> >>> +static void virt_get_topo_from_index(MachineState *ms,
> >>> +                                     LoongArchCPUTopo *topo, int index)
> >>> +{
> >>> +    topo->socket_id = index / (ms->smp.cores * ms->smp.threads);
> >>> +    topo->core_id = index / ms->smp.threads % ms->smp.cores;
> >>> +    topo->thread_id = index % ms->smp.threads;
> >>> +}
> >>> +
> >>>   static bool memhp_type_supported(DeviceState *dev)
> >>>   {
> >>>       /* we only support pc dimm now */
> >>> @@ -1385,8 +1426,9 @@ static HotplugHandler 
> >>> *virt_get_hotplug_handler(MachineState *machine,
> >>>   
> >>>   static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms)
> >>>   {
> >>> -    int n;
> >>> +    int n, arch_id;
> >>>       unsigned int max_cpus = ms->smp.max_cpus;
> >>> +    LoongArchCPUTopo topo;
> >>>   
> >>>       if (ms->possible_cpus) {
> >>>           assert(ms->possible_cpus->len == max_cpus);
> >>> @@ -1397,17 +1439,17 @@ static const CPUArchIdList 
> >>> *virt_possible_cpu_arch_ids(MachineState *ms)
> >>>                                     sizeof(CPUArchId) * max_cpus);
> >>>       ms->possible_cpus->len = max_cpus;
> >>>       for (n = 0; n < ms->possible_cpus->len; n++) {
> >>> +        virt_get_topo_from_index(ms, &topo, n);
> >>> +        arch_id = virt_get_arch_id_from_topo(ms, &topo);
> >>> +        ms->possible_cpus->cpus[n].vcpus_count = ms->smp.threads;  
> >>
> >> In include/hw/boards.h, the doc of CPUArchId said:
> >>
> >> vcpus_count - number of threads provided by @cpu object
> >>
> >> And I undersatnd each element in possible_cpus->cpus[] is mapped to a
> >> CPU object, so that here vcpus_count should be 1.  
> > 
> > it's arch specific, CPU object in possible_cpus was meant to point to
> > thread/core/..higher layers in future../
> > 
> > For example spapr put's there CPUCore, where vcpus_count can be > 1
> > 
> > That is a bit broken though, since CPUCore is not CPUState by any means,
> > spapr_core_plug() gets away with it only because
> >    core_slot->cpu = CPU(dev)
> > CPU() is dumb pointer cast.
> > 
> > Ideally CPUArchId::cpu should be (Object*) to accommodate various
> > levels of granularity correctly (with dumb cast above it's just
> > cosmetics though as long as we treat it as Object in non arch
> > specific code).
> >   
> >>>           ms->possible_cpus->cpus[n].type = ms->cpu_type;
> >>> -        ms->possible_cpus->cpus[n].arch_id = n;
> >>> -
> >>> +        ms->possible_cpus->cpus[n].arch_id = arch_id;
> >>>           ms->possible_cpus->cpus[n].props.has_socket_id = true;
> >>> -        ms->possible_cpus->cpus[n].props.socket_id  =
> >>> -                                   n / (ms->smp.cores * ms->smp.threads);
> >>> +        ms->possible_cpus->cpus[n].props.socket_id = topo.socket_id;
> >>>           ms->possible_cpus->cpus[n].props.has_core_id = true;
> >>> -        ms->possible_cpus->cpus[n].props.core_id =
> >>> -                                   n / ms->smp.threads % ms->smp.cores;
> >>> +        ms->possible_cpus->cpus[n].props.core_id = topo.core_id;
> >>>           ms->possible_cpus->cpus[n].props.has_thread_id = true;
> >>> -        ms->possible_cpus->cpus[n].props.thread_id = n % ms->smp.threads;
> >>> +        ms->possible_cpus->cpus[n].props.thread_id = topo.thread_id;
> >>>       }
> >>>       return ms->possible_cpus;
> >>>   }
> >>> diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c
> >>> index 7212fb5f8f..5dfc0d5c43 100644
> >>> --- a/target/loongarch/cpu.c
> >>> +++ b/target/loongarch/cpu.c
> >>> @@ -16,6 +16,7 @@
> >>>   #include "kvm/kvm_loongarch.h"
> >>>   #include "exec/exec-all.h"
> >>>   #include "cpu.h"
> >>> +#include "hw/qdev-properties.h"
> >>>   #include "internals.h"
> >>>   #include "fpu/softfloat-helpers.h"
> >>>   #include "cpu-csr.h"
> >>> @@ -780,6 +781,15 @@ static int64_t loongarch_cpu_get_arch_id(CPUState 
> >>> *cs)
> >>>   }
> >>>   #endif
> >>>   
> >>> +static Property loongarch_cpu_properties[] = {
> >>> +    DEFINE_PROP_UINT32("phy-id", LoongArchCPU, phy_id, UNSET_PHY_ID),  
> >>
> >> IIUC, the phy_id is kind of like the x86 apic_id, but the field is 
> >> completely
> >> derived from topology, so why do you need to define it as a property and 
> >> then
> >> expose it to the user?  
> > 
> > for x86 we do expose apic_id as a property as well, partly from historical 
> > pov
> > but also it's better to access cpu fields via properties from outside of
> > CPU object than directly poke inside of CPU structure from outside of CPU
> > (especially if it comes to generic code)  
> yes, accessing fields via properties is better than directly poking 
> internal structure elements. Is there internal property for cpu object? 
> so that internal property is invisible for users.

not that I'm aware of. usually we add prefix 'x-' to a property that is
'experimental/not stable/shouldn't be used by endusers' 

extending properties API to mark/create internal properties,
would be nice to have but I won't ask you to do that as it's very
much off topic.

> There will be problem if user adds CPU object with apic-id property, for 
> example:

apic-id is there for historical reasons. that's why x86 has a bunch of checks
to make sure input is correct.

> qemu-system-x86_64 -display none -no-user-config -m 2048 -nodefaults 
> -monitor stdio -machine pc,accel=kvm,usb=off -smp 1,maxcpus=2 -cpu 
> IvyBridge-IBRS -qmp unix:/tmp/qmp-sock,server=on,wait=off
> 
> (qemu) device_add 
> id=cpu-2,driver=IvyBridge-IBRS-x86_64-cpu,socket-id=0,core-id=1,thread-id=0,apic-id=100
> Error: Invalid CPU [socket: 50, die: 0, module: 0, core: 0, thread: 0] 
> with APIC ID 100, valid index range 0:1
> 
> (qemu) device_add 
> id=cpu-2,driver=IvyBridge-IBRS-x86_64-cpu,socket-id=0,core-id=1,thread-id=0,apic-id=0
> Error: CPU[0] with APIC ID 0 exists
> 
> 
> Regards
> Bibo Mao
> >   
> >>
> >> Thanks,
> >> Zhao
> >>
> >>  
> 


Reply via email to