diff mbox series

[RFC,V3,01/29] arm/virt, target/arm: Add new ARMCPU {socket, cluster, core, thread}-id property

Message ID 20240613233639.202896-2-salil.mehta@huawei.com (mailing list archive)
State New, archived
Headers show
Series Support of Virtual CPU Hotplug for ARMv8 Arch | expand

Commit Message

Salil Mehta June 13, 2024, 11:36 p.m. UTC
This shall be used to store user specified topology{socket,cluster,core,thread}
and shall be converted to a unique 'vcpu-id' which is used as slot-index during
hot(un)plug of vCPU.

Co-developed-by: Keqian Zhu <zhukeqian1@huawei.com>
Signed-off-by: Keqian Zhu <zhukeqian1@huawei.com>
Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
---
 hw/arm/virt.c         | 10 ++++++++++
 include/hw/arm/virt.h | 28 ++++++++++++++++++++++++++++
 target/arm/cpu.c      |  4 ++++
 target/arm/cpu.h      |  4 ++++
 4 files changed, 46 insertions(+)

Comments

Gavin Shan Aug. 12, 2024, 4:35 a.m. UTC | #1
On 6/14/24 9:36 AM, Salil Mehta wrote:
> This shall be used to store user specified topology{socket,cluster,core,thread}
> and shall be converted to a unique 'vcpu-id' which is used as slot-index during
> hot(un)plug of vCPU.
> 
> Co-developed-by: Keqian Zhu <zhukeqian1@huawei.com>
> Signed-off-by: Keqian Zhu <zhukeqian1@huawei.com>
> Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
> ---
>   hw/arm/virt.c         | 10 ++++++++++
>   include/hw/arm/virt.h | 28 ++++++++++++++++++++++++++++
>   target/arm/cpu.c      |  4 ++++
>   target/arm/cpu.h      |  4 ++++
>   4 files changed, 46 insertions(+)
> 

Those 4 properties are introduced to determine the vCPU's slot, which is the index
to MachineState::possible_cpus::cpus[]. From there, the CPU object or instance is
referenced and then the CPU's state can be further determined. It sounds reasonable
to use the CPU's topology to determine the index. However, I'm wandering if this can
be simplified to use 'cpu-index' or 'index' for a couple of facts: (1) 'cpu-index'
or 'index' is simplified. Users have to provide 4 parameters in order to determine
its index in the extreme case, for example "device_add host-arm-cpu, id=cpu7,socket-id=1,
cluster-id=1,core-id=1,thread-id=1". With 'cpu-index' or 'index', it can be simplified
to 'index=7'. (2) The cold-booted and hotpluggable CPUs are determined by their index
instead of their topology. For example, CPU0/1/2/3 are cold-booted CPUs while CPU4/5/6/7
are hotpluggable CPUs with command lines '-smp maxcpus=8,cpus=4'. So 'index' makes
more sense to identify a vCPU's slot.

> diff --git a/hw/arm/virt.c b/hw/arm/virt.c
> index 3c93c0c0a6..11fc7fc318 100644
> --- a/hw/arm/virt.c
> +++ b/hw/arm/virt.c
> @@ -2215,6 +2215,14 @@ static void machvirt_init(MachineState *machine)
>                             &error_fatal);
>   
>           aarch64 &= object_property_get_bool(cpuobj, "aarch64", NULL);
> +        object_property_set_int(cpuobj, "socket-id",
> +                                virt_get_socket_id(machine, n), NULL);
> +        object_property_set_int(cpuobj, "cluster-id",
> +                                virt_get_cluster_id(machine, n), NULL);
> +        object_property_set_int(cpuobj, "core-id",
> +                                virt_get_core_id(machine, n), NULL);
> +        object_property_set_int(cpuobj, "thread-id",
> +                                virt_get_thread_id(machine, n), NULL);
>   
>           if (!vms->secure) {
>               object_property_set_bool(cpuobj, "has_el3", false, NULL);
> @@ -2708,6 +2716,7 @@ static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms)
>   {
>       int n;
>       unsigned int max_cpus = ms->smp.max_cpus;
> +    unsigned int smp_threads = ms->smp.threads;
>       VirtMachineState *vms = VIRT_MACHINE(ms);
>       MachineClass *mc = MACHINE_GET_CLASS(vms);
>   
> @@ -2721,6 +2730,7 @@ static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms)
>       ms->possible_cpus->len = max_cpus;
>       for (n = 0; n < ms->possible_cpus->len; n++) {
>           ms->possible_cpus->cpus[n].type = ms->cpu_type;
> +        ms->possible_cpus->cpus[n].vcpus_count = smp_threads;
>           ms->possible_cpus->cpus[n].arch_id =
>               virt_cpu_mp_affinity(vms, n);
>   

Why @vcpus_count is initialized to @smp_threads? it needs to be documented in
the commit log.

> diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h
> index bb486d36b1..6f9a7bb60b 100644
> --- a/include/hw/arm/virt.h
> +++ b/include/hw/arm/virt.h
> @@ -209,4 +209,32 @@ static inline int virt_gicv3_redist_region_count(VirtMachineState *vms)
>               vms->highmem_redists) ? 2 : 1;
>   }
>   
> +static inline int virt_get_socket_id(const MachineState *ms, int cpu_index)
> +{
> +    assert(cpu_index >= 0 && cpu_index < ms->possible_cpus->len);
> +
> +    return ms->possible_cpus->cpus[cpu_index].props.socket_id;
> +}
> +
> +static inline int virt_get_cluster_id(const MachineState *ms, int cpu_index)
> +{
> +    assert(cpu_index >= 0 && cpu_index < ms->possible_cpus->len);
> +
> +    return ms->possible_cpus->cpus[cpu_index].props.cluster_id;
> +}
> +
> +static inline int virt_get_core_id(const MachineState *ms, int cpu_index)
> +{
> +    assert(cpu_index >= 0 && cpu_index < ms->possible_cpus->len);
> +
> +    return ms->possible_cpus->cpus[cpu_index].props.core_id;
> +}
> +
> +static inline int virt_get_thread_id(const MachineState *ms, int cpu_index)
> +{
> +    assert(cpu_index >= 0 && cpu_index < ms->possible_cpus->len);
> +
> +    return ms->possible_cpus->cpus[cpu_index].props.thread_id;
> +}
> +
>   #endif /* QEMU_ARM_VIRT_H */
> diff --git a/target/arm/cpu.c b/target/arm/cpu.c
> index 77f8c9c748..abc4ed0842 100644
> --- a/target/arm/cpu.c
> +++ b/target/arm/cpu.c
> @@ -2582,6 +2582,10 @@ static Property arm_cpu_properties[] = {
>       DEFINE_PROP_UINT64("mp-affinity", ARMCPU,
>                           mp_affinity, ARM64_AFFINITY_INVALID),
>       DEFINE_PROP_INT32("node-id", ARMCPU, node_id, CPU_UNSET_NUMA_NODE_ID),
> +    DEFINE_PROP_INT32("socket-id", ARMCPU, socket_id, 0),
> +    DEFINE_PROP_INT32("cluster-id", ARMCPU, cluster_id, 0),
> +    DEFINE_PROP_INT32("core-id", ARMCPU, core_id, 0),
> +    DEFINE_PROP_INT32("thread-id", ARMCPU, thread_id, 0),
>       DEFINE_PROP_INT32("core-count", ARMCPU, core_count, -1),
>       /* True to default to the backward-compat old CNTFRQ rather than 1Ghz */
>       DEFINE_PROP_BOOL("backcompat-cntfrq", ARMCPU, backcompat_cntfrq, false),
> diff --git a/target/arm/cpu.h b/target/arm/cpu.h
> index c17264c239..208c719db3 100644
> --- a/target/arm/cpu.h
> +++ b/target/arm/cpu.h
> @@ -1076,6 +1076,10 @@ struct ArchCPU {
>       QLIST_HEAD(, ARMELChangeHook) el_change_hooks;
>   
>       int32_t node_id; /* NUMA node this CPU belongs to */
> +    int32_t socket_id;
> +    int32_t cluster_id;
> +    int32_t core_id;
> +    int32_t thread_id;
>   
>       /* Used to synchronize KVM and QEMU in-kernel device levels */
>       uint8_t device_irq_level;

Thanks,
Gavin
Igor Mammedov Aug. 12, 2024, 8:15 a.m. UTC | #2
On Mon, 12 Aug 2024 14:35:56 +1000
Gavin Shan <gshan@redhat.com> wrote:

> On 6/14/24 9:36 AM, Salil Mehta wrote:
> > This shall be used to store user specified topology{socket,cluster,core,thread}
> > and shall be converted to a unique 'vcpu-id' which is used as slot-index during
> > hot(un)plug of vCPU.
> > 
> > Co-developed-by: Keqian Zhu <zhukeqian1@huawei.com>
> > Signed-off-by: Keqian Zhu <zhukeqian1@huawei.com>
> > Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
> > ---
> >   hw/arm/virt.c         | 10 ++++++++++
> >   include/hw/arm/virt.h | 28 ++++++++++++++++++++++++++++
> >   target/arm/cpu.c      |  4 ++++
> >   target/arm/cpu.h      |  4 ++++
> >   4 files changed, 46 insertions(+)
> >   
> 
> Those 4 properties are introduced to determine the vCPU's slot, which is the index
> to MachineState::possible_cpus::cpus[]. From there, the CPU object or instance is
> referenced and then the CPU's state can be further determined. It sounds reasonable
> to use the CPU's topology to determine the index. However, I'm wandering if this can
> be simplified to use 'cpu-index' or 'index' for a couple of facts: (1) 'cpu-index'

Please, don't. We've spent a bunch of time to get rid of cpu-index in user
visible interface (well, old NUMA CLI is still there along with 'new' topology
based one, but that's the last one).

> or 'index' is simplified. Users have to provide 4 parameters in order to determine
> its index in the extreme case, for example "device_add host-arm-cpu, id=cpu7,socket-id=1,
> cluster-id=1,core-id=1,thread-id=1". With 'cpu-index' or 'index', it can be simplified
> to 'index=7'. (2) The cold-booted and hotpluggable CPUs are determined by their index
> instead of their topology. For example, CPU0/1/2/3 are cold-booted CPUs while CPU4/5/6/7
> are hotpluggable CPUs with command lines '-smp maxcpus=8,cpus=4'. So 'index' makes
> more sense to identify a vCPU's slot.
cpu-index have been used for hotplug with x86 machines as a starting point
to implement hotplug as it was easy to hack and it has already existed in QEMU.

But that didn't scale as was desired and had its own issues.
Hence the current interface that majority agreed upon.
I don't remember exact arguments anymore (they could be found qemu-devel if needed)
Here is a link to the talk that tried to explain why topo based was introduced.
  http://events17.linuxfoundation.org/sites/events/files/slides/CPU%20Hot-plug%20support%20in%20QEMU.pdf


> > diff --git a/hw/arm/virt.c b/hw/arm/virt.c
> > index 3c93c0c0a6..11fc7fc318 100644
> > --- a/hw/arm/virt.c
> > +++ b/hw/arm/virt.c
> > @@ -2215,6 +2215,14 @@ static void machvirt_init(MachineState *machine)
> >                             &error_fatal);
> >   
> >           aarch64 &= object_property_get_bool(cpuobj, "aarch64", NULL);
> > +        object_property_set_int(cpuobj, "socket-id",
> > +                                virt_get_socket_id(machine, n), NULL);
> > +        object_property_set_int(cpuobj, "cluster-id",
> > +                                virt_get_cluster_id(machine, n), NULL);
> > +        object_property_set_int(cpuobj, "core-id",
> > +                                virt_get_core_id(machine, n), NULL);
> > +        object_property_set_int(cpuobj, "thread-id",
> > +                                virt_get_thread_id(machine, n), NULL);
> >   
> >           if (!vms->secure) {
> >               object_property_set_bool(cpuobj, "has_el3", false, NULL);
> > @@ -2708,6 +2716,7 @@ static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms)
> >   {
> >       int n;
> >       unsigned int max_cpus = ms->smp.max_cpus;
> > +    unsigned int smp_threads = ms->smp.threads;
> >       VirtMachineState *vms = VIRT_MACHINE(ms);
> >       MachineClass *mc = MACHINE_GET_CLASS(vms);
> >   
> > @@ -2721,6 +2730,7 @@ static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms)
> >       ms->possible_cpus->len = max_cpus;
> >       for (n = 0; n < ms->possible_cpus->len; n++) {
> >           ms->possible_cpus->cpus[n].type = ms->cpu_type;
> > +        ms->possible_cpus->cpus[n].vcpus_count = smp_threads;
> >           ms->possible_cpus->cpus[n].arch_id =
> >               virt_cpu_mp_affinity(vms, n);
> >     
> 
> Why @vcpus_count is initialized to @smp_threads? it needs to be documented in
> the commit log.
> 
> > diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h
> > index bb486d36b1..6f9a7bb60b 100644
> > --- a/include/hw/arm/virt.h
> > +++ b/include/hw/arm/virt.h
> > @@ -209,4 +209,32 @@ static inline int virt_gicv3_redist_region_count(VirtMachineState *vms)
> >               vms->highmem_redists) ? 2 : 1;
> >   }
> >   
> > +static inline int virt_get_socket_id(const MachineState *ms, int cpu_index)
> > +{
> > +    assert(cpu_index >= 0 && cpu_index < ms->possible_cpus->len);
> > +
> > +    return ms->possible_cpus->cpus[cpu_index].props.socket_id;
> > +}
> > +
> > +static inline int virt_get_cluster_id(const MachineState *ms, int cpu_index)
> > +{
> > +    assert(cpu_index >= 0 && cpu_index < ms->possible_cpus->len);
> > +
> > +    return ms->possible_cpus->cpus[cpu_index].props.cluster_id;
> > +}
> > +
> > +static inline int virt_get_core_id(const MachineState *ms, int cpu_index)
> > +{
> > +    assert(cpu_index >= 0 && cpu_index < ms->possible_cpus->len);
> > +
> > +    return ms->possible_cpus->cpus[cpu_index].props.core_id;
> > +}
> > +
> > +static inline int virt_get_thread_id(const MachineState *ms, int cpu_index)
> > +{
> > +    assert(cpu_index >= 0 && cpu_index < ms->possible_cpus->len);
> > +
> > +    return ms->possible_cpus->cpus[cpu_index].props.thread_id;
> > +}
> > +
> >   #endif /* QEMU_ARM_VIRT_H */
> > diff --git a/target/arm/cpu.c b/target/arm/cpu.c
> > index 77f8c9c748..abc4ed0842 100644
> > --- a/target/arm/cpu.c
> > +++ b/target/arm/cpu.c
> > @@ -2582,6 +2582,10 @@ static Property arm_cpu_properties[] = {
> >       DEFINE_PROP_UINT64("mp-affinity", ARMCPU,
> >                           mp_affinity, ARM64_AFFINITY_INVALID),
> >       DEFINE_PROP_INT32("node-id", ARMCPU, node_id, CPU_UNSET_NUMA_NODE_ID),
> > +    DEFINE_PROP_INT32("socket-id", ARMCPU, socket_id, 0),
> > +    DEFINE_PROP_INT32("cluster-id", ARMCPU, cluster_id, 0),
> > +    DEFINE_PROP_INT32("core-id", ARMCPU, core_id, 0),
> > +    DEFINE_PROP_INT32("thread-id", ARMCPU, thread_id, 0),
> >       DEFINE_PROP_INT32("core-count", ARMCPU, core_count, -1),
> >       /* True to default to the backward-compat old CNTFRQ rather than 1Ghz */
> >       DEFINE_PROP_BOOL("backcompat-cntfrq", ARMCPU, backcompat_cntfrq, false),
> > diff --git a/target/arm/cpu.h b/target/arm/cpu.h
> > index c17264c239..208c719db3 100644
> > --- a/target/arm/cpu.h
> > +++ b/target/arm/cpu.h
> > @@ -1076,6 +1076,10 @@ struct ArchCPU {
> >       QLIST_HEAD(, ARMELChangeHook) el_change_hooks;
> >   
> >       int32_t node_id; /* NUMA node this CPU belongs to */
> > +    int32_t socket_id;
> > +    int32_t cluster_id;
> > +    int32_t core_id;
> > +    int32_t thread_id;
> >   
> >       /* Used to synchronize KVM and QEMU in-kernel device levels */
> >       uint8_t device_irq_level;  
> 
> Thanks,
> Gavin
>
Gavin Shan Aug. 13, 2024, 12:31 a.m. UTC | #3
On 8/12/24 6:15 PM, Igor Mammedov wrote:
> On Mon, 12 Aug 2024 14:35:56 +1000
> Gavin Shan <gshan@redhat.com> wrote:
> 
>> On 6/14/24 9:36 AM, Salil Mehta wrote:
>>> This shall be used to store user specified topology{socket,cluster,core,thread}
>>> and shall be converted to a unique 'vcpu-id' which is used as slot-index during
>>> hot(un)plug of vCPU.
>>>
>>> Co-developed-by: Keqian Zhu <zhukeqian1@huawei.com>
>>> Signed-off-by: Keqian Zhu <zhukeqian1@huawei.com>
>>> Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
>>> ---
>>>    hw/arm/virt.c         | 10 ++++++++++
>>>    include/hw/arm/virt.h | 28 ++++++++++++++++++++++++++++
>>>    target/arm/cpu.c      |  4 ++++
>>>    target/arm/cpu.h      |  4 ++++
>>>    4 files changed, 46 insertions(+)
>>>    
>>
>> Those 4 properties are introduced to determine the vCPU's slot, which is the index
>> to MachineState::possible_cpus::cpus[]. From there, the CPU object or instance is
>> referenced and then the CPU's state can be further determined. It sounds reasonable
>> to use the CPU's topology to determine the index. However, I'm wandering if this can
>> be simplified to use 'cpu-index' or 'index' for a couple of facts: (1) 'cpu-index'
> 
> Please, don't. We've spent a bunch of time to get rid of cpu-index in user
> visible interface (well, old NUMA CLI is still there along with 'new' topology
> based one, but that's the last one).
> 

Ok, thanks for the hints. It's a question I had from the beginning. I didn't dig into
the historic background. From the vCPU hotplug document (cpu-hotplug.rst), the CPU
topology is used to identify hot-added vCPU on x86 and it's reasonable for ARM to
follow this mechanism.

>> or 'index' is simplified. Users have to provide 4 parameters in order to determine
>> its index in the extreme case, for example "device_add host-arm-cpu, id=cpu7,socket-id=1,
>> cluster-id=1,core-id=1,thread-id=1". With 'cpu-index' or 'index', it can be simplified
>> to 'index=7'. (2) The cold-booted and hotpluggable CPUs are determined by their index
>> instead of their topology. For example, CPU0/1/2/3 are cold-booted CPUs while CPU4/5/6/7
>> are hotpluggable CPUs with command lines '-smp maxcpus=8,cpus=4'. So 'index' makes
>> more sense to identify a vCPU's slot.
> cpu-index have been used for hotplug with x86 machines as a starting point
> to implement hotplug as it was easy to hack and it has already existed in QEMU.
> 
> But that didn't scale as was desired and had its own issues.
> Hence the current interface that majority agreed upon.
> I don't remember exact arguments anymore (they could be found qemu-devel if needed)
> Here is a link to the talk that tried to explain why topo based was introduced.
>    http://events17.linuxfoundation.org/sites/events/files/slides/CPU%20Hot-plug%20support%20in%20QEMU.pdf
> 

Right, I overlooked the migration case where the source and destination vCPU have
to be strictly correlation. This strict correlation can become broken with 'index'
or 'cpu-index', but it's ensured with the CPU topology as stated on page-19.

Thanks,
Gavin
Salil Mehta Aug. 19, 2024, 11:53 a.m. UTC | #4
HI Gavin,

Sorry, I was away for almost entire last week. Joined back today.
Thanks for taking out time to review. 

>  From: Gavin Shan <gshan@redhat.com>
>  Sent: Monday, August 12, 2024 5:36 AM
>  To: Salil Mehta <salil.mehta@huawei.com>; qemu-devel@nongnu.org;
>  qemu-arm@nongnu.org; mst@redhat.com
>  
>  On 6/14/24 9:36 AM, Salil Mehta wrote:
>  > This shall be used to store user specified
>  > topology{socket,cluster,core,thread}
>  > and shall be converted to a unique 'vcpu-id' which is used as
>  > slot-index during hot(un)plug of vCPU.
>  >
>  > Co-developed-by: Keqian Zhu <zhukeqian1@huawei.com>
>  > Signed-off-by: Keqian Zhu <zhukeqian1@huawei.com>
>  > Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
>  > ---
>  >   hw/arm/virt.c         | 10 ++++++++++
>  >   include/hw/arm/virt.h | 28 ++++++++++++++++++++++++++++
>  >   target/arm/cpu.c      |  4 ++++
>  >   target/arm/cpu.h      |  4 ++++
>  >   4 files changed, 46 insertions(+)
>  >
>  
>  Those 4 properties are introduced to determine the vCPU's slot, which is the
>  index to MachineState::possible_cpus::cpus[]. 

Correct.

From there, the CPU object
>  or instance is referenced and then the CPU's state can be further
>  determined. It sounds reasonable to use the CPU's topology to determine
>  the index. However, I'm wandering if this can be simplified to use 'cpu-
>  index' or 'index' 

Are you suggesting to use CPU index while specifying vCPUs through
command line and I'm not even sure how will it simply CPU naming?

CPU index is internal index to QOM. The closest thing which you can
have is the 'slot-id'  and later can have mapping to the CPU index
internally but I'm not sure how much useful it is to introduce this 
slot abstraction. I did raise this in the original RFC I posted in 2020.


for a couple of facts: (1) 'cpu-index'
>  or 'index' is simplified. Users have to provide 4 parameters in order to
>  determine its index in the extreme case, for example "device_add host-
>  arm-cpu, id=cpu7,socket-id=1, cluster-id=1,core-id=1,thread-id=1". With
>  'cpu-index' or 'index', it can be simplified to 'index=7'. (2) The cold-booted
>  and hotpluggable CPUs are determined by their index instead of their
>  topology. For example, CPU0/1/2/3 are cold-booted CPUs while CPU4/5/6/7
>  are hotpluggable CPUs with command lines '-smp maxcpus=8,cpus=4'. So
>  'index' makes more sense to identify a vCPU's slot.


I'm not sure if anybody wants to use it this way. People want to specify topology
i.e. where the vCPU fits. Internally it's up to QOM to translate that topology to
some index.


>  
>  > diff --git a/hw/arm/virt.c b/hw/arm/virt.c index
>  > 3c93c0c0a6..11fc7fc318 100644
>  > --- a/hw/arm/virt.c
>  > +++ b/hw/arm/virt.c
>  > @@ -2215,6 +2215,14 @@ static void machvirt_init(MachineState
>  *machine)
>  >                             &error_fatal);
>  >
>  >           aarch64 &= object_property_get_bool(cpuobj, "aarch64",
>  > NULL);
>  > +        object_property_set_int(cpuobj, "socket-id",
>  > +                                virt_get_socket_id(machine, n), NULL);
>  > +        object_property_set_int(cpuobj, "cluster-id",
>  > +                                virt_get_cluster_id(machine, n), NULL);
>  > +        object_property_set_int(cpuobj, "core-id",
>  > +                                virt_get_core_id(machine, n), NULL);
>  > +        object_property_set_int(cpuobj, "thread-id",
>  > +                                virt_get_thread_id(machine, n),
>  > + NULL);
>  >
>  >           if (!vms->secure) {
>  >               object_property_set_bool(cpuobj, "has_el3", false,
>  > NULL); @@ -2708,6 +2716,7 @@ static const CPUArchIdList
>  *virt_possible_cpu_arch_ids(MachineState *ms)
>  >   {
>  >       int n;
>  >       unsigned int max_cpus = ms->smp.max_cpus;
>  > +    unsigned int smp_threads = ms->smp.threads;
>  >       VirtMachineState *vms = VIRT_MACHINE(ms);
>  >       MachineClass *mc = MACHINE_GET_CLASS(vms);
>  >
>  > @@ -2721,6 +2730,7 @@ static const CPUArchIdList
>  *virt_possible_cpu_arch_ids(MachineState *ms)
>  >       ms->possible_cpus->len = max_cpus;
>  >       for (n = 0; n < ms->possible_cpus->len; n++) {
>  >           ms->possible_cpus->cpus[n].type = ms->cpu_type;
>  > +        ms->possible_cpus->cpus[n].vcpus_count = smp_threads;
>  >           ms->possible_cpus->cpus[n].arch_id =
>  >               virt_cpu_mp_affinity(vms, n);
>  >
>  
>  Why @vcpus_count is initialized to @smp_threads? it needs to be
>  documented in the commit log.


Because every thread internally amounts to a vCPU in QOM and which
is in 1:1 relationship with KVM vCPU. AFAIK, QOM does not strictly follows
any architecture. Once you start to get into details of threads there
are many aspects of shared resources one will have to consider and
these can vary across different implementations of architecture.

It is a bigger problem than you think, which I've touched at very nascent
stages while doing POC of vCPU hotplug but tried to avoid till now. 


But I would like to hear other community members views on this.

Hi Igor/Peter,

What is your take on this?

Thanks
Salil.



>  > diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index
>  > bb486d36b1..6f9a7bb60b 100644
>  > --- a/include/hw/arm/virt.h
>  > +++ b/include/hw/arm/virt.h
>  > @@ -209,4 +209,32 @@ static inline int
>  virt_gicv3_redist_region_count(VirtMachineState *vms)
>  >               vms->highmem_redists) ? 2 : 1;
>  >   }
>  >
>  > +static inline int virt_get_socket_id(const MachineState *ms, int
>  > +cpu_index) {
>  > +    assert(cpu_index >= 0 && cpu_index < ms->possible_cpus->len);
>  > +
>  > +    return ms->possible_cpus->cpus[cpu_index].props.socket_id;
>  > +}
>  > +
>  > +static inline int virt_get_cluster_id(const MachineState *ms, int
>  > +cpu_index) {
>  > +    assert(cpu_index >= 0 && cpu_index < ms->possible_cpus->len);
>  > +
>  > +    return ms->possible_cpus->cpus[cpu_index].props.cluster_id;
>  > +}
>  > +
>  > +static inline int virt_get_core_id(const MachineState *ms, int
>  > +cpu_index) {
>  > +    assert(cpu_index >= 0 && cpu_index < ms->possible_cpus->len);
>  > +
>  > +    return ms->possible_cpus->cpus[cpu_index].props.core_id;
>  > +}
>  > +
>  > +static inline int virt_get_thread_id(const MachineState *ms, int
>  > +cpu_index) {
>  > +    assert(cpu_index >= 0 && cpu_index < ms->possible_cpus->len);
>  > +
>  > +    return ms->possible_cpus->cpus[cpu_index].props.thread_id;
>  > +}
>  > +
>  >   #endif /* QEMU_ARM_VIRT_H */
>  > diff --git a/target/arm/cpu.c b/target/arm/cpu.c index
>  > 77f8c9c748..abc4ed0842 100644
>  > --- a/target/arm/cpu.c
>  > +++ b/target/arm/cpu.c
>  > @@ -2582,6 +2582,10 @@ static Property arm_cpu_properties[] = {
>  >       DEFINE_PROP_UINT64("mp-affinity", ARMCPU,
>  >                           mp_affinity, ARM64_AFFINITY_INVALID),
>  >       DEFINE_PROP_INT32("node-id", ARMCPU, node_id,
>  > CPU_UNSET_NUMA_NODE_ID),
>  > +    DEFINE_PROP_INT32("socket-id", ARMCPU, socket_id, 0),
>  > +    DEFINE_PROP_INT32("cluster-id", ARMCPU, cluster_id, 0),
>  > +    DEFINE_PROP_INT32("core-id", ARMCPU, core_id, 0),
>  > +    DEFINE_PROP_INT32("thread-id", ARMCPU, thread_id, 0),
>  >       DEFINE_PROP_INT32("core-count", ARMCPU, core_count, -1),
>  >       /* True to default to the backward-compat old CNTFRQ rather than
>  1Ghz */
>  >       DEFINE_PROP_BOOL("backcompat-cntfrq", ARMCPU,
>  backcompat_cntfrq,
>  > false), diff --git a/target/arm/cpu.h b/target/arm/cpu.h index
>  > c17264c239..208c719db3 100644
>  > --- a/target/arm/cpu.h
>  > +++ b/target/arm/cpu.h
>  > @@ -1076,6 +1076,10 @@ struct ArchCPU {
>  >       QLIST_HEAD(, ARMELChangeHook) el_change_hooks;
>  >
>  >       int32_t node_id; /* NUMA node this CPU belongs to */
>  > +    int32_t socket_id;
>  > +    int32_t cluster_id;
>  > +    int32_t core_id;
>  > +    int32_t thread_id;
>  >
>  >       /* Used to synchronize KVM and QEMU in-kernel device levels */
>  >       uint8_t device_irq_level;
>  
>  Thanks,
>  Gavin
>
Salil Mehta Aug. 19, 2024, 12:07 p.m. UTC | #5
>  From: Igor Mammedov <imammedo@redhat.com>
>  Sent: Monday, August 12, 2024 9:16 AM
>  To: Gavin Shan <gshan@redhat.com>
>  
>  On Mon, 12 Aug 2024 14:35:56 +1000
>  Gavin Shan <gshan@redhat.com> wrote:
>  
>  > On 6/14/24 9:36 AM, Salil Mehta wrote:
>  > > This shall be used to store user specified
>  > > topology{socket,cluster,core,thread}
>  > > and shall be converted to a unique 'vcpu-id' which is used as
>  > > slot-index during hot(un)plug of vCPU.
>  > >
>  > > Co-developed-by: Keqian Zhu <zhukeqian1@huawei.com>
>  > > Signed-off-by: Keqian Zhu <zhukeqian1@huawei.com>
>  > > Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
>  > > ---
>  > >   hw/arm/virt.c         | 10 ++++++++++
>  > >   include/hw/arm/virt.h | 28 ++++++++++++++++++++++++++++
>  > >   target/arm/cpu.c      |  4 ++++
>  > >   target/arm/cpu.h      |  4 ++++
>  > >   4 files changed, 46 insertions(+)
>  > >
>  >
>  > Those 4 properties are introduced to determine the vCPU's slot, which
>  > is the index to MachineState::possible_cpus::cpus[]. From there, the
>  > CPU object or instance is referenced and then the CPU's state can be
>  > further determined. It sounds reasonable to use the CPU's topology to
>  > determine the index. However, I'm wandering if this can be simplified to
>  use 'cpu-index' or 'index' for a couple of facts: (1) 'cpu-index'
>  
>  Please, don't. We've spent a bunch of time to get rid of cpu-index in user
>  visible interface (well, old NUMA CLI is still there along with 'new' topology
>  based one, but that's the last one).


Agreed. We shouldn't expose CPU index to user.

>  
>  > or 'index' is simplified. Users have to provide 4 parameters in order
>  > to determine its index in the extreme case, for example "device_add
>  > host-arm-cpu, id=cpu7,socket-id=1,
>  > cluster-id=1,core-id=1,thread-id=1". With 'cpu-index' or 'index', it
>  > can be simplified to 'index=7'. (2) The cold-booted and hotpluggable
>  > CPUs are determined by their index instead of their topology. For
>  > example, CPU0/1/2/3 are cold-booted CPUs while CPU4/5/6/7 are
>  hotpluggable CPUs with command lines '-smp maxcpus=8,cpus=4'. So 'index'
>  makes more sense to identify a vCPU's slot.
>  cpu-index have been used for hotplug with x86 machines as a starting point
>  to implement hotplug as it was easy to hack and it has already existed in
>  QEMU.
>  
>  But that didn't scale as was desired and had its own issues.
>  Hence the current interface that majority agreed upon.
>  I don't remember exact arguments anymore (they could be found qemu-
>  devel if needed) Here is a link to the talk that tried to explain why topo
>  based was introduced.
>  
>  http://events17.linuxfoundation.org/sites/events/files/slides/CPU%20Hot-
>  plug%20support%20in%20QEMU.pdf


I think you are referring to slide-19 of above presentation?

Thanks
Salil.
Zhao Liu Sept. 4, 2024, 2:42 p.m. UTC | #6
Hi Salil,

On Mon, Aug 19, 2024 at 11:53:52AM +0000, Salil Mehta wrote:
> Date: Mon, 19 Aug 2024 11:53:52 +0000
> From: Salil Mehta <salil.mehta@huawei.com>
> Subject: RE: [PATCH RFC V3 01/29] arm/virt,target/arm: Add new ARMCPU
>  {socket,cluster,core,thread}-id property

[snip]

> >  > NULL); @@ -2708,6 +2716,7 @@ static const CPUArchIdList
> >  *virt_possible_cpu_arch_ids(MachineState *ms)
> >  >   {
> >  >       int n;
> >  >       unsigned int max_cpus = ms->smp.max_cpus;
> >  > +    unsigned int smp_threads = ms->smp.threads;
> >  >       VirtMachineState *vms = VIRT_MACHINE(ms);
> >  >       MachineClass *mc = MACHINE_GET_CLASS(vms);
> >  >
> >  > @@ -2721,6 +2730,7 @@ static const CPUArchIdList
> >  *virt_possible_cpu_arch_ids(MachineState *ms)
> >  >       ms->possible_cpus->len = max_cpus;
> >  >       for (n = 0; n < ms->possible_cpus->len; n++) {
> >  >           ms->possible_cpus->cpus[n].type = ms->cpu_type;
> >  > +        ms->possible_cpus->cpus[n].vcpus_count = smp_threads;
> >  >           ms->possible_cpus->cpus[n].arch_id =
> >  >               virt_cpu_mp_affinity(vms, n);
> >  >
> >  
> >  Why @vcpus_count is initialized to @smp_threads? it needs to be
> >  documented in the commit log.
> 
> 
> Because every thread internally amounts to a vCPU in QOM and which
> is in 1:1 relationship with KVM vCPU. AFAIK, QOM does not strictly follows
> any architecture. Once you start to get into details of threads there
> are many aspects of shared resources one will have to consider and
> these can vary across different implementations of architecture.

For SPAPR CPU, the granularity of >possible_cpus->cpus[] is "core", and
for x86, it's "thread" granularity.

And smp.threads means how many threads in one core, so for x86, the
vcpus_count of a "thread" is 1, and for spapr, the vcpus_count of a
"core" equals to smp.threads.

IIUC, your granularity is still "thread", so that this filed should be 1.

-Zhao

> It is a bigger problem than you think, which I've touched at very nascent
> stages while doing POC of vCPU hotplug but tried to avoid till now. 
> 
> 
> But I would like to hear other community members views on this.
> 
> Hi Igor/Peter,
> 
> What is your take on this?
> 
> Thanks
> Salil.
Salil Mehta Sept. 4, 2024, 5:37 p.m. UTC | #7
Hi Zhao,

>  From: zhao1.liu@intel.com <zhao1.liu@intel.com>
>  Sent: Wednesday, September 4, 2024 3:43 PM
>  To: Salil Mehta <salil.mehta@huawei.com>
>  
>  Hi Salil,
>  
>  On Mon, Aug 19, 2024 at 11:53:52AM +0000, Salil Mehta wrote:
>  > Date: Mon, 19 Aug 2024 11:53:52 +0000
>  > From: Salil Mehta <salil.mehta@huawei.com>
>  > Subject: RE: [PATCH RFC V3 01/29] arm/virt,target/arm: Add new ARMCPU
>  > {socket,cluster,core,thread}-id property
>  
>  [snip]
>  
>  > >  > NULL); @@ -2708,6 +2716,7 @@ static const CPUArchIdList
>  > > *virt_possible_cpu_arch_ids(MachineState *ms)
>  > >  >   {
>  > >  >       int n;
>  > >  >       unsigned int max_cpus = ms->smp.max_cpus;
>  > >  > +    unsigned int smp_threads = ms->smp.threads;
>  > >  >       VirtMachineState *vms = VIRT_MACHINE(ms);
>  > >  >       MachineClass *mc = MACHINE_GET_CLASS(vms);
>  > >  >
>  > >  > @@ -2721,6 +2730,7 @@ static const CPUArchIdList
>  > > *virt_possible_cpu_arch_ids(MachineState *ms)
>  > >  >       ms->possible_cpus->len = max_cpus;
>  > >  >       for (n = 0; n < ms->possible_cpus->len; n++) {
>  > >  >           ms->possible_cpus->cpus[n].type = ms->cpu_type;
>  > >  > +        ms->possible_cpus->cpus[n].vcpus_count = smp_threads;
>  > >  >           ms->possible_cpus->cpus[n].arch_id =
>  > >  >               virt_cpu_mp_affinity(vms, n);
>  > >  >
>  > >
>  > >  Why @vcpus_count is initialized to @smp_threads? it needs to be
>  > > documented in the commit log.
>  >
>  >
>  > Because every thread internally amounts to a vCPU in QOM and which is
>  > in 1:1 relationship with KVM vCPU. AFAIK, QOM does not strictly
>  > follows any architecture. Once you start to get into details of
>  > threads there are many aspects of shared resources one will have to
>  > consider and these can vary across different implementations of
>  architecture.
>  
>  For SPAPR CPU, the granularity of >possible_cpus->cpus[] is "core", and for
>  x86, it's "thread" granularity.


We have threads per-core at microarchitecture level in ARM as well. But each
thread appears like a vCPU to OS and AFAICS there are no special attributes
attached to it. SMT can be enabled/disabled at firmware and should get
reflected in the configuration accordingly i.e. value of *threads-per-core* 
changes between 1 and 'N'.  This means 'vcpus_count' has to reflect the
correct configuration. But I think threads lack proper representation
in Qemu QOM.

In Qemu, each vCPU reflects an execution context (which gets uniquely mapped
to KVM vCPU). AFAICS, we only have *CPUState* (Struct ArchCPU) as a placeholder
for this execution context and there is no *ThreadState* (derived out of
Struct CPUState). Hence, we've  to map all the threads as QOM vCPUs. This means
the array of present or possible CPUs represented by 'struct CPUArchIdList' contains
all execution contexts which actually might be vCPU or a thread. Hence, usage of
*vcpus_count* seems quite superficial to me frankly.

Also, AFAICS, KVM does not have the concept of the threads and only has
KVM vCPUs, but you are still allowed to specify the topology with sockets, dies,
clusters, cores, threads in most architectures.  


>  
>  And smp.threads means how many threads in one core, so for x86, the
>  vcpus_count of a "thread" is 1, and for spapr, the vcpus_count of a "core"
>  equals to smp.threads.


Sure, but does the KVM specifies this? and how does these threads map to the QOM
vCPU objects or execution context? AFAICS there is nothing but 'CPUState'
which will be made part of the  possible vCPU list 'struct CPUArchIdList'.



>  
>  IIUC, your granularity is still "thread", so that this filed should be 1.


Well, again we need more discussion on this. I've stated my concerns against
doing this. User should be allowed to create virtual topology which will
include 'threads' as one of the parameter.


>  
>  -Zhao
>  
>  > It is a bigger problem than you think, which I've touched at very
>  > nascent stages while doing POC of vCPU hotplug but tried to avoid till now.
>  >
>  >
>  > But I would like to hear other community members views on this.
>  >
>  > Hi Igor/Peter,
>  >
>  > What is your take on this?
>  >
>  > Thanks
>  > Salil.
Zhao Liu Sept. 9, 2024, 3:28 p.m. UTC | #8
On Wed, Sep 04, 2024 at 05:37:21PM +0000, Salil Mehta wrote:
> Date: Wed, 4 Sep 2024 17:37:21 +0000
> From: Salil Mehta <salil.mehta@huawei.com>
> Subject: RE: [PATCH RFC V3 01/29] arm/virt,target/arm: Add new ARMCPU
>  {socket,cluster,core,thread}-id property
> 
> Hi Zhao,
> 
> >  From: zhao1.liu@intel.com <zhao1.liu@intel.com>
> >  Sent: Wednesday, September 4, 2024 3:43 PM
> >  To: Salil Mehta <salil.mehta@huawei.com>
> >  
> >  Hi Salil,
> >  
> >  On Mon, Aug 19, 2024 at 11:53:52AM +0000, Salil Mehta wrote:
> >  > Date: Mon, 19 Aug 2024 11:53:52 +0000
> >  > From: Salil Mehta <salil.mehta@huawei.com>
> >  > Subject: RE: [PATCH RFC V3 01/29] arm/virt,target/arm: Add new ARMCPU
> >  > {socket,cluster,core,thread}-id property
> >  
> >  [snip]
> >  
> >  > >  > NULL); @@ -2708,6 +2716,7 @@ static const CPUArchIdList
> >  > > *virt_possible_cpu_arch_ids(MachineState *ms)
> >  > >  >   {
> >  > >  >       int n;
> >  > >  >       unsigned int max_cpus = ms->smp.max_cpus;
> >  > >  > +    unsigned int smp_threads = ms->smp.threads;
> >  > >  >       VirtMachineState *vms = VIRT_MACHINE(ms);
> >  > >  >       MachineClass *mc = MACHINE_GET_CLASS(vms);
> >  > >  >
> >  > >  > @@ -2721,6 +2730,7 @@ static const CPUArchIdList
> >  > > *virt_possible_cpu_arch_ids(MachineState *ms)
> >  > >  >       ms->possible_cpus->len = max_cpus;
> >  > >  >       for (n = 0; n < ms->possible_cpus->len; n++) {
> >  > >  >           ms->possible_cpus->cpus[n].type = ms->cpu_type;
> >  > >  > +        ms->possible_cpus->cpus[n].vcpus_count = smp_threads;
> >  > >  >           ms->possible_cpus->cpus[n].arch_id =
> >  > >  >               virt_cpu_mp_affinity(vms, n);
> >  > >  >
> >  > >
> >  > >  Why @vcpus_count is initialized to @smp_threads? it needs to be
> >  > > documented in the commit log.
> >  >
> >  >
> >  > Because every thread internally amounts to a vCPU in QOM and which is
> >  > in 1:1 relationship with KVM vCPU. AFAIK, QOM does not strictly
> >  > follows any architecture. Once you start to get into details of
> >  > threads there are many aspects of shared resources one will have to
> >  > consider and these can vary across different implementations of
> >  architecture.
> >  
> >  For SPAPR CPU, the granularity of >possible_cpus->cpus[] is "core", and for
> >  x86, it's "thread" granularity.
> 
> 
> We have threads per-core at microarchitecture level in ARM as well. But each
> thread appears like a vCPU to OS and AFAICS there are no special attributes
> attached to it. SMT can be enabled/disabled at firmware and should get
> reflected in the configuration accordingly i.e. value of *threads-per-core* 
> changes between 1 and 'N'.  This means 'vcpus_count' has to reflect the
> correct configuration. But I think threads lack proper representation
> in Qemu QOM.

In topology related part, SMT (of x86) usually represents the logical
processor level. And thread has the same meaning. To change these
meanings is also possible, but I think it should be based on the actual
use case. we can consider the complexity of the implementation when
there is a need.

> In Qemu, each vCPU reflects an execution context (which gets uniquely mapped
> to KVM vCPU). AFAICS, we only have *CPUState* (Struct ArchCPU) as a placeholder
> for this execution context and there is no *ThreadState* (derived out of
> Struct CPUState). Hence, we've  to map all the threads as QOM vCPUs. This means
> the array of present or possible CPUs represented by 'struct CPUArchIdList' contains
> all execution contexts which actually might be vCPU or a thread. Hence, usage of
> *vcpus_count* seems quite superficial to me frankly.
>
> Also, AFAICS, KVM does not have the concept of the threads and only has
> KVM vCPUs, but you are still allowed to specify the topology with sockets, dies,
> clusters, cores, threads in most architectures.  
 
There are some uses for topology, such as it affects scheduling behavior,
and it affects feature emulation, etc.
  
> >  And smp.threads means how many threads in one core, so for x86, the
> >  vcpus_count of a "thread" is 1, and for spapr, the vcpus_count of a "core"
> >  equals to smp.threads.
> 
> 
> Sure, but does the KVM specifies this? 

At least as you said, KVM (for x86) doesn't consider higher-level topologies
at the moment, but that's not to say that it won't in the future, as certain
registers do have topology dependencies.

> and how does these threads map to the QOM vCPU objects or execution context?

Each CPU object will create a (software) thread, you can refer the
function "kvm_start_vcpu_thread(CPUState *cpu)", which will be called
when CPU object realizes.

> AFAICS there is nothing but 'CPUState'
> which will be made part of the  possible vCPU list 'struct CPUArchIdList'.
 
As I said, an example is spapr ("spapr_possible_cpu_arch_ids()"), which
maps possible_cpu to core object. However, this is a very specific
example, and like Igor's slides said, I understand it's an architectural
requirement.

> >  
> >  IIUC, your granularity is still "thread", so that this filed should be 1.
> 
> 
> Well, again we need more discussion on this. I've stated my concerns against
> doing this. User should be allowed to create virtual topology which will
> include 'threads' as one of the parameter.
> 

I don't seem to understand...There is a “threads” parameter in -smp, does
this not satisfy your use case?

Regards,
Zhao
Salil Mehta Sept. 10, 2024, 11:01 a.m. UTC | #9
HI Zhao,

>  From: Zhao Liu <zhao1.liu@intel.com>
>  Sent: Monday, September 9, 2024 4:28 PM
>  To: Salil Mehta <salil.mehta@huawei.com>
>  
>  On Wed, Sep 04, 2024 at 05:37:21PM +0000, Salil Mehta wrote:
>  > Date: Wed, 4 Sep 2024 17:37:21 +0000
>  > From: Salil Mehta <salil.mehta@huawei.com>
>  > Subject: RE: [PATCH RFC V3 01/29] arm/virt,target/arm: Add new ARMCPU
>  > {socket,cluster,core,thread}-id property
>  >
>  > Hi Zhao,
>  >
>  > >  From: zhao1.liu@intel.com <zhao1.liu@intel.com>
>  > >  Sent: Wednesday, September 4, 2024 3:43 PM
>  > >  To: Salil Mehta <salil.mehta@huawei.com>
>  > >
>  > >  Hi Salil,
>  > >
>  > >  On Mon, Aug 19, 2024 at 11:53:52AM +0000, Salil Mehta wrote:
>  > >  > Date: Mon, 19 Aug 2024 11:53:52 +0000  > From: Salil Mehta
>  > > <salil.mehta@huawei.com>  > Subject: RE: [PATCH RFC V3 01/29]
>  > > arm/virt,target/arm: Add new ARMCPU  >
>  > > {socket,cluster,core,thread}-id property
>  > >
>  > >  [snip]
>  > >
>  > >  > >  > NULL); @@ -2708,6 +2716,7 @@ static const CPUArchIdList  > >
>  > > *virt_possible_cpu_arch_ids(MachineState *ms)
>  > >  > >  >   {
>  > >  > >  >       int n;
>  > >  > >  >       unsigned int max_cpus = ms->smp.max_cpus;
>  > >  > >  > +    unsigned int smp_threads = ms->smp.threads;
>  > >  > >  >       VirtMachineState *vms = VIRT_MACHINE(ms);
>  > >  > >  >       MachineClass *mc = MACHINE_GET_CLASS(vms);
>  > >  > >  >
>  > >  > >  > @@ -2721,6 +2730,7 @@ static const CPUArchIdList  > >
>  > > *virt_possible_cpu_arch_ids(MachineState *ms)
>  > >  > >  >       ms->possible_cpus->len = max_cpus;
>  > >  > >  >       for (n = 0; n < ms->possible_cpus->len; n++) {
>  > >  > >  >           ms->possible_cpus->cpus[n].type = ms->cpu_type;
>  > >  > >  > +        ms->possible_cpus->cpus[n].vcpus_count = smp_threads;
>  > >  > >  >           ms->possible_cpus->cpus[n].arch_id =
>  > >  > >  >               virt_cpu_mp_affinity(vms, n);
>  > >  > >  >
>  > >  > >
>  > >  > >  Why @vcpus_count is initialized to @smp_threads? it needs to
>  > > be  > > documented in the commit log.
>  > >  >
>  > >  >
>  > >  > Because every thread internally amounts to a vCPU in QOM and
>  > > which is  > in 1:1 relationship with KVM vCPU. AFAIK, QOM does not
>  > > strictly  > follows any architecture. Once you start to get into
>  > > details of  > threads there are many aspects of shared resources one
>  > > will have to  > consider and these can vary across different
>  > > implementations of  architecture.
>  > >
>  > >  For SPAPR CPU, the granularity of >possible_cpus->cpus[] is "core",
>  > > and for  x86, it's "thread" granularity.
>  >
>  >
>  > We have threads per-core at microarchitecture level in ARM as well.
>  > But each thread appears like a vCPU to OS and AFAICS there are no
>  > special attributes attached to it. SMT can be enabled/disabled at
>  > firmware and should get reflected in the configuration accordingly
>  > i.e. value of *threads-per-core* changes between 1 and 'N'.  This
>  > means 'vcpus_count' has to reflect the correct configuration. But I
>  > think threads lack proper representation in Qemu QOM.
>  
>  In topology related part, SMT (of x86) usually represents the logical
>  processor level. And thread has the same meaning.


Agreed. It is same in ARM as well. The difference could be in how hardware
threads are implemented at microarchitecture level.  Nevertheless, we do
have such virtual configurations, and the meaning of *threads* as-in QOM
topology (socket,cluster,core,thread) is virtualized similar to the hardware
threads in host. And One should be able to configure threads support in the
virtual environment,  regardless whether or not underlying hardware
supports threads. That's my take.

Other aspect is how we then expose these threads to the guest. The guest
kernel (just like host kernel) should gather topology information using
ACPI PPTT Table (This is ARM specific?). Later is populated by the Qemu
(just like by firmware for the host kernel) by making use of the virtual
topology. ARM guest kernel, in absence of PPTT support can detect
presence of hardware threads by reading MT Bit within the MPIDR_EL1
register.

Every property in 'ms->possible_cpus->cpus[n].props should be exactly
same as finalized and part of the MachineState::CpuTopology.
Hence, number of threads-per-core 'vcpus_count'  should not be treated
differently. 

But there is  a catch! (I explained that earlier)


 To change these
>  meanings is also possible, but I think it should be based on the actual use
>  case. we can consider the complexity of the implementation when there is a
>  need.


Agreed. There is no ambiguity in the meaning of hardware threads or the 
virtualized MachineState::CpuTopology. Properties of all the possible vCPUs
should exactly be same as part of MachineState. This includes the number
of threads-per-core.

You mentioned 'vcpus_count' should be 1 but does that mean user can never
specify threads > 1 in virtual configuration for x86?


>  
>  > In Qemu, each vCPU reflects an execution context (which gets uniquely
>  > mapped to KVM vCPU). AFAICS, we only have *CPUState* (Struct
>  ArchCPU)
>  > as a placeholder for this execution context and there is no
>  > *ThreadState* (derived out of Struct CPUState). Hence, we've  to map
>  > all the threads as QOM vCPUs. This means the array of present or
>  > possible CPUs represented by 'struct CPUArchIdList' contains all
>  > execution contexts which actually might be vCPU or a thread. Hence,
>  > usage of
>  > *vcpus_count* seems quite superficial to me frankly.
>  >
>  > Also, AFAICS, KVM does not have the concept of the threads and only
>  > has KVM vCPUs, but you are still allowed to specify the topology with
>  > sockets, dies, clusters, cores, threads in most architectures.
>  
>  There are some uses for topology, such as it affects scheduling behavior,
>  and it affects feature emulation, etc.


True. And we should be flexible at the VMM level. We should let Admin of
the VMM control how he creates the virtual topology which best fits
on the underlying hardware features of the host. This includes, NUMA,
sub-NUMA, cores, hardware, threads, cache topology etc. 


>  
>  > >  And smp.threads means how many threads in one core, so for x86, the
>  > > vcpus_count of a "thread" is 1, and for spapr, the vcpus_count of a
>  "core" equals to smp.threads.
>  >
>  >
>  > Sure, but does the KVM specifies this?
>  
>  At least as you said, KVM (for x86) doesn't consider higher-level topologies
>  at the moment, but that's not to say that it won't in the future, as certain
>  registers do have topology dependencies.


sure. so you mean for x86 virtual topology, smp.threads = 1 always?


>  
>  > and how does these threads map to the QOM vCPU objects or execution
>  context?
>  
>  Each CPU object will create a (software) thread, you can refer the function
>  "kvm_start_vcpu_thread(CPUState *cpu)", which will be called when CPU
>  object realizes.


Yes, sure, and each such QOM vCPU thread and 'struct CPUState' is mapped to
the lowest granularity of execution specified within the QOM virtual topology.
It could be a 'thread' or a 'core'. And all these will run as a KVM vCPU scheduled
on some hardware core and maybe hardware thread (if enabled).

So there is no difference across architectures regarding this part. I was trying
to point that in QOM, even the threads will have their own 'struct CPUState'
and each one will be part of the "CPUArchIdList *possible_cpus" maintained
at the MachineState. At this level we loose the relationship information of
the cores and their corresponding threads (given by 'vcpus_count').


>  > AFAICS there is nothing but 'CPUState'
>  > which will be made part of the  possible vCPU list 'struct CPUArchIdList'.
>  
>  As I said, an example is spapr ("spapr_possible_cpu_arch_ids()"), which
>  maps possible_cpu to core object. However, this is a very specific example,
>  and like Igor's slides said, I understand it's an architectural requirement.


I'm sure there must have been some. I'm trying to understand it. Can you
share the slides?


>  
>  > >
>  > >  IIUC, your granularity is still "thread", so that this filed should be 1.
>  >
>  >
>  > Well, again we need more discussion on this. I've stated my concerns
>  > against doing this. User should be allowed to create virtual topology
>  > which will include 'threads' as one of the parameter.
>  >
>  
>  I don't seem to understand...There is a "threads" parameter in -smp, does
>  this not satisfy your use case?

It certainly does. But this is what should get reflected in the 'vcpus_count' as well? 


Best regards
Salil

>  
>  Regards,
>  Zhao
>
Jonathan Cameron Sept. 11, 2024, 11:35 a.m. UTC | #10
On Tue, 10 Sep 2024 12:01:05 +0100
Salil Mehta <salil.mehta@huawei.com> wrote:

> HI Zhao,

A few trivial comments inline.

> 
> >  From: Zhao Liu <zhao1.liu@intel.com>
> >  Sent: Monday, September 9, 2024 4:28 PM
> >  To: Salil Mehta <salil.mehta@huawei.com>
> >  
> >  On Wed, Sep 04, 2024 at 05:37:21PM +0000, Salil Mehta wrote:  
> >  > Date: Wed, 4 Sep 2024 17:37:21 +0000
> >  > From: Salil Mehta <salil.mehta@huawei.com>
> >  > Subject: RE: [PATCH RFC V3 01/29] arm/virt,target/arm: Add new ARMCPU
> >  > {socket,cluster,core,thread}-id property
> >  >
> >  > Hi Zhao,
> >  >  
> >  > >  From: zhao1.liu@intel.com <zhao1.liu@intel.com>
> >  > >  Sent: Wednesday, September 4, 2024 3:43 PM
> >  > >  To: Salil Mehta <salil.mehta@huawei.com>
> >  > >
> >  > >  Hi Salil,
> >  > >
> >  > >  On Mon, Aug 19, 2024 at 11:53:52AM +0000, Salil Mehta wrote:  
> >  > >  > Date: Mon, 19 Aug 2024 11:53:52 +0000  > From: Salil Mehta  
> >  > > <salil.mehta@huawei.com>  > Subject: RE: [PATCH RFC V3 01/29]
> >  > > arm/virt,target/arm: Add new ARMCPU  >
> >  > > {socket,cluster,core,thread}-id property
> >  > >
> >  > >  [snip]
> >  > >  
> >  > >  > >  > NULL); @@ -2708,6 +2716,7 @@ static const CPUArchIdList  > >  
> >  > > *virt_possible_cpu_arch_ids(MachineState *ms)  
> >  > >  > >  >   {
> >  > >  > >  >       int n;
> >  > >  > >  >       unsigned int max_cpus = ms->smp.max_cpus;
> >  > >  > >  > +    unsigned int smp_threads = ms->smp.threads;
> >  > >  > >  >       VirtMachineState *vms = VIRT_MACHINE(ms);
> >  > >  > >  >       MachineClass *mc = MACHINE_GET_CLASS(vms);
> >  > >  > >  >
> >  > >  > >  > @@ -2721,6 +2730,7 @@ static const CPUArchIdList  > >  
> >  > > *virt_possible_cpu_arch_ids(MachineState *ms)  
> >  > >  > >  >       ms->possible_cpus->len = max_cpus;
> >  > >  > >  >       for (n = 0; n < ms->possible_cpus->len; n++) {
> >  > >  > >  >           ms->possible_cpus->cpus[n].type = ms->cpu_type;
> >  > >  > >  > +        ms->possible_cpus->cpus[n].vcpus_count = smp_threads;
> >  > >  > >  >           ms->possible_cpus->cpus[n].arch_id =
> >  > >  > >  >               virt_cpu_mp_affinity(vms, n);
> >  > >  > >  >  
> >  > >  > >
> >  > >  > >  Why @vcpus_count is initialized to @smp_threads? it needs to  
> >  > > be  > > documented in the commit log.  
> >  > >  >
> >  > >  >
> >  > >  > Because every thread internally amounts to a vCPU in QOM and  
> >  > > which is  > in 1:1 relationship with KVM vCPU. AFAIK, QOM does not
> >  > > strictly  > follows any architecture. Once you start to get into
> >  > > details of  > threads there are many aspects of shared resources one
> >  > > will have to  > consider and these can vary across different
> >  > > implementations of  architecture.
> >  > >
> >  > >  For SPAPR CPU, the granularity of >possible_cpus->cpus[] is "core",
> >  > > and for  x86, it's "thread" granularity.  
> >  >
> >  >
> >  > We have threads per-core at microarchitecture level in ARM as well.
> >  > But each thread appears like a vCPU to OS and AFAICS there are no
> >  > special attributes attached to it. SMT can be enabled/disabled at
> >  > firmware and should get reflected in the configuration accordingly
> >  > i.e. value of *threads-per-core* changes between 1 and 'N'.  This
> >  > means 'vcpus_count' has to reflect the correct configuration. But I
> >  > think threads lack proper representation in Qemu QOM.  
> >  
> >  In topology related part, SMT (of x86) usually represents the logical
> >  processor level. And thread has the same meaning.  
> 
> 
> Agreed. It is same in ARM as well. The difference could be in how hardware
> threads are implemented at microarchitecture level.  Nevertheless, we do
> have such virtual configurations, and the meaning of *threads* as-in QOM
> topology (socket,cluster,core,thread) is virtualized similar to the hardware
> threads in host. And One should be able to configure threads support in the
> virtual environment,  regardless whether or not underlying hardware
> supports threads. That's my take.
> 
> Other aspect is how we then expose these threads to the guest. The guest
> kernel (just like host kernel) should gather topology information using
> ACPI PPTT Table (This is ARM specific?). 

Not ARM specific, but not used by x86 in practice (I believe some risc-v boards
use it).
https://lore.kernel.org/linux-riscv/20240617131425.7526-3-cuiyunhui@bytedance.com/

> Later is populated by the Qemu
> (just like by firmware for the host kernel) by making use of the virtual
> topology. ARM guest kernel, in absence of PPTT support can detect
> presence of hardware threads by reading MT Bit within the MPIDR_EL1
> register.

Sadly no it can't.  Lots of CPUs cores that are single thread set that
bit anyway (so it's garbage and PPTT / DT is the only source of truth)
https://lore.kernel.org/all/CAFfO_h7vUEhqV15epf+_zVrbDhc3JrejkkOVsHzHgCXNk+nDdg@mail.gmail.com/T/

Jonathan
Salil Mehta Sept. 11, 2024, 12:25 p.m. UTC | #11
On Wed, Sep 11, 2024 at 11:35 AM Jonathan Cameron <
Jonathan.Cameron@huawei.com> wrote:

> On Tue, 10 Sep 2024 12:01:05 +0100
> Salil Mehta <salil.mehta@huawei.com> wrote:
>
> > HI Zhao,
>
> A few trivial comments inline.
>
> >
> > >  From: Zhao Liu <zhao1.liu@intel.com>
> > >  Sent: Monday, September 9, 2024 4:28 PM
> > >  To: Salil Mehta <salil.mehta@huawei.com>
> > >
> > >  On Wed, Sep 04, 2024 at 05:37:21PM +0000, Salil Mehta wrote:
> > >  > Date: Wed, 4 Sep 2024 17:37:21 +0000
> > >  > From: Salil Mehta <salil.mehta@huawei.com>
> > >  > Subject: RE: [PATCH RFC V3 01/29] arm/virt,target/arm: Add new
> ARMCPU
> > >  > {socket,cluster,core,thread}-id property
> > >  >
> > >  > Hi Zhao,
> > >  >
> > >  > >  From: zhao1.liu@intel.com <zhao1.liu@intel.com>
> > >  > >  Sent: Wednesday, September 4, 2024 3:43 PM
> > >  > >  To: Salil Mehta <salil.mehta@huawei.com>
> > >  > >
> > >  > >  Hi Salil,
> > >  > >
> > >  > >  On Mon, Aug 19, 2024 at 11:53:52AM +0000, Salil Mehta wrote:
> > >  > >  > Date: Mon, 19 Aug 2024 11:53:52 +0000  > From: Salil Mehta
> > >  > > <salil.mehta@huawei.com>  > Subject: RE: [PATCH RFC V3 01/29]
> > >  > > arm/virt,target/arm: Add new ARMCPU  >
> > >  > > {socket,cluster,core,thread}-id property
> > >  > >
> > >  > >  [snip]
> > >  > >
> > >  > >  > >  > NULL); @@ -2708,6 +2716,7 @@ static const CPUArchIdList
> > >
> > >  > > *virt_possible_cpu_arch_ids(MachineState *ms)
> > >  > >  > >  >   {
> > >  > >  > >  >       int n;
> > >  > >  > >  >       unsigned int max_cpus = ms->smp.max_cpus;
> > >  > >  > >  > +    unsigned int smp_threads = ms->smp.threads;
> > >  > >  > >  >       VirtMachineState *vms = VIRT_MACHINE(ms);
> > >  > >  > >  >       MachineClass *mc = MACHINE_GET_CLASS(vms);
> > >  > >  > >  >
> > >  > >  > >  > @@ -2721,6 +2730,7 @@ static const CPUArchIdList  > >
> > >  > > *virt_possible_cpu_arch_ids(MachineState *ms)
> > >  > >  > >  >       ms->possible_cpus->len = max_cpus;
> > >  > >  > >  >       for (n = 0; n < ms->possible_cpus->len; n++) {
> > >  > >  > >  >           ms->possible_cpus->cpus[n].type = ms->cpu_type;
> > >  > >  > >  > +        ms->possible_cpus->cpus[n].vcpus_count =
> smp_threads;
> > >  > >  > >  >           ms->possible_cpus->cpus[n].arch_id =
> > >  > >  > >  >               virt_cpu_mp_affinity(vms, n);
> > >  > >  > >  >
> > >  > >  > >
> > >  > >  > >  Why @vcpus_count is initialized to @smp_threads? it needs
> to
> > >  > > be  > > documented in the commit log.
> > >  > >  >
> > >  > >  >
> > >  > >  > Because every thread internally amounts to a vCPU in QOM and
> > >  > > which is  > in 1:1 relationship with KVM vCPU. AFAIK, QOM does not
> > >  > > strictly  > follows any architecture. Once you start to get into
> > >  > > details of  > threads there are many aspects of shared resources
> one
> > >  > > will have to  > consider and these can vary across different
> > >  > > implementations of  architecture.
> > >  > >
> > >  > >  For SPAPR CPU, the granularity of >possible_cpus->cpus[] is
> "core",
> > >  > > and for  x86, it's "thread" granularity.
> > >  >
> > >  >
> > >  > We have threads per-core at microarchitecture level in ARM as well.
> > >  > But each thread appears like a vCPU to OS and AFAICS there are no
> > >  > special attributes attached to it. SMT can be enabled/disabled at
> > >  > firmware and should get reflected in the configuration accordingly
> > >  > i.e. value of *threads-per-core* changes between 1 and 'N'.  This
> > >  > means 'vcpus_count' has to reflect the correct configuration. But I
> > >  > think threads lack proper representation in Qemu QOM.
> > >
> > >  In topology related part, SMT (of x86) usually represents the logical
> > >  processor level. And thread has the same meaning.
> >
> >
> > Agreed. It is same in ARM as well. The difference could be in how
> hardware
> > threads are implemented at microarchitecture level.  Nevertheless, we do
> > have such virtual configurations, and the meaning of *threads* as-in QOM
> > topology (socket,cluster,core,thread) is virtualized similar to the
> hardware
> > threads in host. And One should be able to configure threads support in
> the
> > virtual environment,  regardless whether or not underlying hardware
> > supports threads. That's my take.
> >
> > Other aspect is how we then expose these threads to the guest. The guest
> > kernel (just like host kernel) should gather topology information using
> > ACPI PPTT Table (This is ARM specific?).
>
> Not ARM specific, but not used by x86 in practice (I believe some risc-v
> boards
> use it).
>
> https://lore.kernel.org/linux-riscv/20240617131425.7526-3-cuiyunhui@bytedance.com/
>
> > Later is populated by the Qemu
> > (just like by firmware for the host kernel) by making use of the virtual
> > topology. ARM guest kernel, in absence of PPTT support can detect
> > presence of hardware threads by reading MT Bit within the MPIDR_EL1
> > register.
>
> Sadly no it can't.  Lots of CPUs cores that are single thread set that
> bit anyway (so it's garbage and PPTT / DT is the only source of truth)
>
> https://lore.kernel.org/all/CAFfO_h7vUEhqV15epf+_zVrbDhc3JrejkkOVsHzHgCXNk+nDdg@mail.gmail.com/T/



Yes, agreed, this last explanation was not completely correct.
IICRC, Marc did point out in RFC V1 of June 2020 that value  MT=0 is set by
KVM to tell the guest kernel that vCPUs at the same affinity-1 fields are
independent.
Problem was with the interpretation of non-zero MT Bit  and it was not
consistent.
The key thing is we should not rely on the value of the MT Bit in MPIDR to
know
if multithreading exists. So yes, to know the exact status of the
multithreading
on ARM systems parsing PPTT Table is the only way.

I mentioned that because handling still exists in the kernel code but I
think it exists
for handling those other cases. Maybe a comment is required here (?):

https://elixir.bootlin.com/linux/v6.11-rc7/source/arch/arm64/kernel/topology.c#L34

Thanks for pointing this out.


Thanks
Salil.




>
>
> Jonathan
>
>
diff mbox series

Patch

diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 3c93c0c0a6..11fc7fc318 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -2215,6 +2215,14 @@  static void machvirt_init(MachineState *machine)
                           &error_fatal);
 
         aarch64 &= object_property_get_bool(cpuobj, "aarch64", NULL);
+        object_property_set_int(cpuobj, "socket-id",
+                                virt_get_socket_id(machine, n), NULL);
+        object_property_set_int(cpuobj, "cluster-id",
+                                virt_get_cluster_id(machine, n), NULL);
+        object_property_set_int(cpuobj, "core-id",
+                                virt_get_core_id(machine, n), NULL);
+        object_property_set_int(cpuobj, "thread-id",
+                                virt_get_thread_id(machine, n), NULL);
 
         if (!vms->secure) {
             object_property_set_bool(cpuobj, "has_el3", false, NULL);
@@ -2708,6 +2716,7 @@  static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms)
 {
     int n;
     unsigned int max_cpus = ms->smp.max_cpus;
+    unsigned int smp_threads = ms->smp.threads;
     VirtMachineState *vms = VIRT_MACHINE(ms);
     MachineClass *mc = MACHINE_GET_CLASS(vms);
 
@@ -2721,6 +2730,7 @@  static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms)
     ms->possible_cpus->len = max_cpus;
     for (n = 0; n < ms->possible_cpus->len; n++) {
         ms->possible_cpus->cpus[n].type = ms->cpu_type;
+        ms->possible_cpus->cpus[n].vcpus_count = smp_threads;
         ms->possible_cpus->cpus[n].arch_id =
             virt_cpu_mp_affinity(vms, n);
 
diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h
index bb486d36b1..6f9a7bb60b 100644
--- a/include/hw/arm/virt.h
+++ b/include/hw/arm/virt.h
@@ -209,4 +209,32 @@  static inline int virt_gicv3_redist_region_count(VirtMachineState *vms)
             vms->highmem_redists) ? 2 : 1;
 }
 
+static inline int virt_get_socket_id(const MachineState *ms, int cpu_index)
+{
+    assert(cpu_index >= 0 && cpu_index < ms->possible_cpus->len);
+
+    return ms->possible_cpus->cpus[cpu_index].props.socket_id;
+}
+
+static inline int virt_get_cluster_id(const MachineState *ms, int cpu_index)
+{
+    assert(cpu_index >= 0 && cpu_index < ms->possible_cpus->len);
+
+    return ms->possible_cpus->cpus[cpu_index].props.cluster_id;
+}
+
+static inline int virt_get_core_id(const MachineState *ms, int cpu_index)
+{
+    assert(cpu_index >= 0 && cpu_index < ms->possible_cpus->len);
+
+    return ms->possible_cpus->cpus[cpu_index].props.core_id;
+}
+
+static inline int virt_get_thread_id(const MachineState *ms, int cpu_index)
+{
+    assert(cpu_index >= 0 && cpu_index < ms->possible_cpus->len);
+
+    return ms->possible_cpus->cpus[cpu_index].props.thread_id;
+}
+
 #endif /* QEMU_ARM_VIRT_H */
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index 77f8c9c748..abc4ed0842 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -2582,6 +2582,10 @@  static Property arm_cpu_properties[] = {
     DEFINE_PROP_UINT64("mp-affinity", ARMCPU,
                         mp_affinity, ARM64_AFFINITY_INVALID),
     DEFINE_PROP_INT32("node-id", ARMCPU, node_id, CPU_UNSET_NUMA_NODE_ID),
+    DEFINE_PROP_INT32("socket-id", ARMCPU, socket_id, 0),
+    DEFINE_PROP_INT32("cluster-id", ARMCPU, cluster_id, 0),
+    DEFINE_PROP_INT32("core-id", ARMCPU, core_id, 0),
+    DEFINE_PROP_INT32("thread-id", ARMCPU, thread_id, 0),
     DEFINE_PROP_INT32("core-count", ARMCPU, core_count, -1),
     /* True to default to the backward-compat old CNTFRQ rather than 1Ghz */
     DEFINE_PROP_BOOL("backcompat-cntfrq", ARMCPU, backcompat_cntfrq, false),
diff --git a/target/arm/cpu.h b/target/arm/cpu.h
index c17264c239..208c719db3 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -1076,6 +1076,10 @@  struct ArchCPU {
     QLIST_HEAD(, ARMELChangeHook) el_change_hooks;
 
     int32_t node_id; /* NUMA node this CPU belongs to */
+    int32_t socket_id;
+    int32_t cluster_id;
+    int32_t core_id;
+    int32_t thread_id;
 
     /* Used to synchronize KVM and QEMU in-kernel device levels */
     uint8_t device_irq_level;