diff mbox series

[RFC] target/loongarch/kvm: Add pmu support

Message ID 20240514094630.988617-1-gaosong@loongson.cn (mailing list archive)
State New, archived
Headers show
Series [RFC] target/loongarch/kvm: Add pmu support | expand

Commit Message

gaosong May 14, 2024, 9:46 a.m. UTC
This patch adds PMU support, We just sets some cpucfg6 default value
to PMU config on kvm mode, and then check the PMU config with kvm ioctl
KVM_GET_DEVICE_ATTR.
  e.g
    '...  -cpu max,pmu=on,pmnum=[1-16]';
    '...  -cpu max,pmu=on' (default pmnum = 4);
    '...  -cpu max,pmu=off' (disable PMU)

Signed-off-by: Song Gao <gaosong@loongson.cn>
---

This patch adds the 'RFC' heading because it requires
the kernel to merge into patch[1] first

[1]: https://lore.kernel.org/all/20240507120140.3119714-1-gaosong@loongson.cn/


 target/loongarch/cpu.h                |  2 +
 target/loongarch/cpu.c                | 64 +++++++++++++++++++++++++++
 target/loongarch/kvm/kvm.c            | 55 ++++++++++++++++++++++-
 target/loongarch/loongarch-qmp-cmds.c |  2 +-
 4 files changed, 121 insertions(+), 2 deletions(-)

Comments

Bibo Mao May 14, 2024, 12:13 p.m. UTC | #1
On 2024/5/14 下午5:46, Song Gao wrote:
> This patch adds PMU support, We just sets some cpucfg6 default value
> to PMU config on kvm mode, and then check the PMU config with kvm ioctl
> KVM_GET_DEVICE_ATTR.
>    e.g
>      '...  -cpu max,pmu=on,pmnum=[1-16]';
>      '...  -cpu max,pmu=on' (default pmnum = 4);
Do we need expose interface pmnum to user?
The default PMU number is 4 on la464/la664, it should equal to real HW.
There is no much meaning with pmnum == 1 although it supports.

>      '...  -cpu max,pmu=off' (disable PMU)
> 
> Signed-off-by: Song Gao <gaosong@loongson.cn>
> ---
> 
> This patch adds the 'RFC' heading because it requires
> the kernel to merge into patch[1] first
> 
> [1]: https://lore.kernel.org/all/20240507120140.3119714-1-gaosong@loongson.cn/
> 
> 
>   target/loongarch/cpu.h                |  2 +
>   target/loongarch/cpu.c                | 64 +++++++++++++++++++++++++++
>   target/loongarch/kvm/kvm.c            | 55 ++++++++++++++++++++++-
>   target/loongarch/loongarch-qmp-cmds.c |  2 +-
>   4 files changed, 121 insertions(+), 2 deletions(-)
> 
> diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h
> index 6c41fafb70..d834649106 100644
> --- a/target/loongarch/cpu.h
> +++ b/target/loongarch/cpu.h
> @@ -184,6 +184,8 @@ FIELD(CPUCFG6, PMNUM, 4, 4)
>   FIELD(CPUCFG6, PMBITS, 8, 6)
>   FIELD(CPUCFG6, UPM, 14, 1)
>   
> +#define PMNUM_MAX 16
> +
>   /* cpucfg[16] bits */
>   FIELD(CPUCFG16, L1_IUPRE, 0, 1)
>   FIELD(CPUCFG16, L1_IUUNIFY, 1, 1)
> diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c
> index a0cad53676..c78ee3f0b1 100644
> --- a/target/loongarch/cpu.c
> +++ b/target/loongarch/cpu.c
> @@ -8,6 +8,7 @@
>   #include "qemu/osdep.h"
>   #include "qemu/log.h"
>   #include "qemu/qemu-print.h"
> +#include "qemu/error-report.h"
>   #include "qapi/error.h"
>   #include "qemu/module.h"
>   #include "sysemu/qtest.h"
> @@ -19,6 +20,7 @@
>   #include "internals.h"
>   #include "fpu/softfloat-helpers.h"
>   #include "cpu-csr.h"
> +#include "qapi/visitor.h"
>   #ifndef CONFIG_USER_ONLY
>   #include "sysemu/reset.h"
>   #endif
> @@ -421,6 +423,14 @@ static void loongarch_la464_initfn(Object *obj)
>       data = FIELD_DP32(data, CPUCFG5, CC_DIV, 1);
>       env->cpucfg[5] = data;
>   
> +    if (kvm_enabled()) {
> +        data = 0;
> +        data = FIELD_DP32(data, CPUCFG6, PMP, 1);
> +        data = FIELD_DP32(data, CPUCFG6, PMNUM, 3);
> +        data = FIELD_DP32(data, CPUCFG6, PMBITS, 63);
> +        env->cpucfg[6] = data;
> +    }
> +
>       data = 0;
>       data = FIELD_DP32(data, CPUCFG16, L1_IUPRE, 1);
>       data = FIELD_DP32(data, CPUCFG16, L1_DPRE, 1);
> @@ -643,6 +653,48 @@ static void loongarch_set_lasx(Object *obj, bool value, Error **errp)
>       }
>   }
>   
> +static bool loongarch_get_pmu(Object *obj, Error **errp)
> +{
> +    LoongArchCPU *cpu = LOONGARCH_CPU(obj);
> +
> +    return  !!(FIELD_EX32(cpu->env.cpucfg[6], CPUCFG6, PMP));
> +}
> +
> +static void loongarch_set_pmu(Object *obj, bool value,  Error **errp)
> +{
> +    LoongArchCPU *cpu = LOONGARCH_CPU(obj);
> +
> +    cpu->env.cpucfg[6] = FIELD_DP32(cpu->env.cpucfg[6], CPUCFG6, PMP, value);
> +}
> +
> +static void loongarch_get_pmnum(Object *obj, Visitor *v,
> +                                const char *name, void *opaque,
> +                                Error **errp)
> +{
> +    LoongArchCPU *cpu = LOONGARCH_CPU(obj);
> +    uint32_t value = FIELD_EX32(cpu->env.cpucfg[6], CPUCFG6, PMNUM);
> +
> +    visit_type_uint32(v, name, &value, errp);
> +}
> +
> +static void loongarch_set_pmnum(Object *obj, Visitor *v,
> +                                const char *name, void *opaque,
> +                                Error **errp)
> +{
> +    LoongArchCPU *cpu = LOONGARCH_CPU(obj);
> +    uint32_t *value= opaque;
> +
> +    if (!visit_type_uint32(v, name, value, errp)) {
> +        return;
> +    }
> +    if ((*value <= PMNUM_MAX) && (*value > 0)) {
> +        cpu->env.cpucfg[6] = FIELD_DP32(cpu->env.cpucfg[6], CPUCFG6, PMNUM, *value -1);
> +    } else {
> +        error_report("Performance counter number need be in [1- %d]\n", PMNUM_MAX);
> +        exit(EXIT_FAILURE);
> +    }
> +}
> +
>   void loongarch_cpu_post_init(Object *obj)
>   {
>       LoongArchCPU *cpu = LOONGARCH_CPU(obj);
> @@ -655,6 +707,18 @@ void loongarch_cpu_post_init(Object *obj)
>           object_property_add_bool(obj, "lasx", loongarch_get_lasx,
>                                    loongarch_set_lasx);
>       }
> +
> +    if (kvm_enabled()) {
> +        object_property_add_bool(obj, "pmu", loongarch_get_pmu,
> +                                 loongarch_set_pmu);
> +        if (FIELD_EX32(cpu->env.cpucfg[6], CPUCFG6, PMP)) {
> +            uint32_t value = 4;
> +            object_property_add(obj, "pmnum", "uint32",
> +                                loongarch_get_pmnum,
> +                                loongarch_set_pmnum, NULL,
> +                                (void *)&value);
> +        }
> +    }
>   }
>   
>   static void loongarch_cpu_init(Object *obj)
> diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c
> index bc75552d0f..a9f9020071 100644
> --- a/target/loongarch/kvm/kvm.c
> +++ b/target/loongarch/kvm/kvm.c
> @@ -556,6 +556,53 @@ static int kvm_check_cpucfg2(CPUState *cs)
>       return ret;
>   }
>   
> +static int kvm_check_cpucfg6(CPUState *cs)
> +{
> +    int ret;
> +    uint64_t val;
> +    struct kvm_device_attr attr = {
> +        .group = KVM_LOONGARCH_VCPU_CPUCFG,
> +        .attr = 6,
> +        .addr = (uint64_t)&val,
> +    };
> +    LoongArchCPU *cpu = LOONGARCH_CPU(cs);
> +    CPULoongArchState *env = &cpu->env;
> +
> +    ret = kvm_vcpu_ioctl(cs, KVM_HAS_DEVICE_ATTR, &attr);
> +    if (!ret) {
> +        kvm_vcpu_ioctl(cs, KVM_GET_DEVICE_ATTR, &attr);
> +
> +        if (FIELD_EX32(env->cpucfg[6], CPUCFG6, PMP)) {
> +             /* Check PMP */
> +             if (!FIELD_EX32(val, CPUCFG6, PMP)) {
> +                 error_report("'pmu' feature not supported by KVM on this host"
> +                              " Please disable 'pmu' with "
> +                              "'... -cpu XXX,pmu=off ...'\n");
> +                 exit(EXIT_FAILURE);
> +             }
> +             /* Check PMNUM */
> +             int guest_pmnum = FIELD_EX32(env->cpucfg[6], CPUCFG6, PMNUM);
> +             int host_pmnum = FIELD_EX32(val, CPUCFG6, PMNUM);
> +             if (guest_pmnum > host_pmnum){
> +                 warn_report("The guest pmnum %d larger than KVM support %d\n",
> +                              guest_pmnum, host_pmnum);
It should report error and exit.
When VM migrates to physical machine with smaller pmu number, it should 
fail to migrate.

Regards
Bibo Mao
> +                 env->cpucfg[6] = FIELD_DP32(env->cpucfg[6], CPUCFG6,
> +                                             PMNUM, host_pmnum);
> +             }
> +             /* Check PMBITS */
> +             int guest_pmbits = FIELD_EX32(env->cpucfg[6], CPUCFG6, PMBITS);
> +             int host_pmbits = FIELD_EX32(val, CPUCFG6, PMBITS);
> +             if (guest_pmbits != host_pmbits) {
> +                 warn_report("The host not support PMBITS %d\n", guest_pmbits);
> +                 env->cpucfg[6] = FIELD_DP32(env->cpucfg[6], CPUCFG6,
> +                                             PMBITS, host_pmbits);
> +             }
> +        }
> +    }
> +
> +    return ret;
> +}
> +
>   static int kvm_loongarch_put_cpucfg(CPUState *cs)
>   {
>       int i, ret = 0;
> @@ -568,7 +615,13 @@ static int kvm_loongarch_put_cpucfg(CPUState *cs)
>               if (ret) {
>                   return ret;
>               }
> -	}
> +        }
> +        if (i == 6) {
> +            ret = kvm_check_cpucfg6(cs);
> +            if (ret) {
> +                return ret;
> +            }
> +        }
>           val = env->cpucfg[i];
>           ret = kvm_set_one_reg(cs, KVM_IOC_CPUCFG(i), &val);
>           if (ret < 0) {
> diff --git a/target/loongarch/loongarch-qmp-cmds.c b/target/loongarch/loongarch-qmp-cmds.c
> index 8721a5eb13..d7f2af363b 100644
> --- a/target/loongarch/loongarch-qmp-cmds.c
> +++ b/target/loongarch/loongarch-qmp-cmds.c
> @@ -40,7 +40,7 @@ CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp)
>   }
>   
>   static const char *cpu_model_advertised_features[] = {
> -    "lsx", "lasx", NULL
> +    "lsx", "lasx", "pmu", "pmnum", NULL
>   };
>   
>   CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type,
>
diff mbox series

Patch

diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h
index 6c41fafb70..d834649106 100644
--- a/target/loongarch/cpu.h
+++ b/target/loongarch/cpu.h
@@ -184,6 +184,8 @@  FIELD(CPUCFG6, PMNUM, 4, 4)
 FIELD(CPUCFG6, PMBITS, 8, 6)
 FIELD(CPUCFG6, UPM, 14, 1)
 
+#define PMNUM_MAX 16
+
 /* cpucfg[16] bits */
 FIELD(CPUCFG16, L1_IUPRE, 0, 1)
 FIELD(CPUCFG16, L1_IUUNIFY, 1, 1)
diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c
index a0cad53676..c78ee3f0b1 100644
--- a/target/loongarch/cpu.c
+++ b/target/loongarch/cpu.c
@@ -8,6 +8,7 @@ 
 #include "qemu/osdep.h"
 #include "qemu/log.h"
 #include "qemu/qemu-print.h"
+#include "qemu/error-report.h"
 #include "qapi/error.h"
 #include "qemu/module.h"
 #include "sysemu/qtest.h"
@@ -19,6 +20,7 @@ 
 #include "internals.h"
 #include "fpu/softfloat-helpers.h"
 #include "cpu-csr.h"
+#include "qapi/visitor.h"
 #ifndef CONFIG_USER_ONLY
 #include "sysemu/reset.h"
 #endif
@@ -421,6 +423,14 @@  static void loongarch_la464_initfn(Object *obj)
     data = FIELD_DP32(data, CPUCFG5, CC_DIV, 1);
     env->cpucfg[5] = data;
 
+    if (kvm_enabled()) {
+        data = 0;
+        data = FIELD_DP32(data, CPUCFG6, PMP, 1);
+        data = FIELD_DP32(data, CPUCFG6, PMNUM, 3);
+        data = FIELD_DP32(data, CPUCFG6, PMBITS, 63);
+        env->cpucfg[6] = data;
+    }
+
     data = 0;
     data = FIELD_DP32(data, CPUCFG16, L1_IUPRE, 1);
     data = FIELD_DP32(data, CPUCFG16, L1_DPRE, 1);
@@ -643,6 +653,48 @@  static void loongarch_set_lasx(Object *obj, bool value, Error **errp)
     }
 }
 
+static bool loongarch_get_pmu(Object *obj, Error **errp)
+{
+    LoongArchCPU *cpu = LOONGARCH_CPU(obj);
+
+    return  !!(FIELD_EX32(cpu->env.cpucfg[6], CPUCFG6, PMP));
+}
+
+static void loongarch_set_pmu(Object *obj, bool value,  Error **errp)
+{
+    LoongArchCPU *cpu = LOONGARCH_CPU(obj);
+
+    cpu->env.cpucfg[6] = FIELD_DP32(cpu->env.cpucfg[6], CPUCFG6, PMP, value);
+}
+
+static void loongarch_get_pmnum(Object *obj, Visitor *v,
+                                const char *name, void *opaque,
+                                Error **errp)
+{
+    LoongArchCPU *cpu = LOONGARCH_CPU(obj);
+    uint32_t value = FIELD_EX32(cpu->env.cpucfg[6], CPUCFG6, PMNUM);
+
+    visit_type_uint32(v, name, &value, errp);
+}
+
+static void loongarch_set_pmnum(Object *obj, Visitor *v,
+                                const char *name, void *opaque,
+                                Error **errp)
+{
+    LoongArchCPU *cpu = LOONGARCH_CPU(obj);
+    uint32_t *value= opaque;
+
+    if (!visit_type_uint32(v, name, value, errp)) {
+        return;
+    }
+    if ((*value <= PMNUM_MAX) && (*value > 0)) {
+        cpu->env.cpucfg[6] = FIELD_DP32(cpu->env.cpucfg[6], CPUCFG6, PMNUM, *value -1);
+    } else {
+        error_report("Performance counter number need be in [1- %d]\n", PMNUM_MAX);
+        exit(EXIT_FAILURE);
+    }
+}
+
 void loongarch_cpu_post_init(Object *obj)
 {
     LoongArchCPU *cpu = LOONGARCH_CPU(obj);
@@ -655,6 +707,18 @@  void loongarch_cpu_post_init(Object *obj)
         object_property_add_bool(obj, "lasx", loongarch_get_lasx,
                                  loongarch_set_lasx);
     }
+
+    if (kvm_enabled()) {
+        object_property_add_bool(obj, "pmu", loongarch_get_pmu,
+                                 loongarch_set_pmu);
+        if (FIELD_EX32(cpu->env.cpucfg[6], CPUCFG6, PMP)) {
+            uint32_t value = 4;
+            object_property_add(obj, "pmnum", "uint32",
+                                loongarch_get_pmnum,
+                                loongarch_set_pmnum, NULL,
+                                (void *)&value);
+        }
+    }
 }
 
 static void loongarch_cpu_init(Object *obj)
diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c
index bc75552d0f..a9f9020071 100644
--- a/target/loongarch/kvm/kvm.c
+++ b/target/loongarch/kvm/kvm.c
@@ -556,6 +556,53 @@  static int kvm_check_cpucfg2(CPUState *cs)
     return ret;
 }
 
+static int kvm_check_cpucfg6(CPUState *cs)
+{
+    int ret;
+    uint64_t val;
+    struct kvm_device_attr attr = {
+        .group = KVM_LOONGARCH_VCPU_CPUCFG,
+        .attr = 6,
+        .addr = (uint64_t)&val,
+    };
+    LoongArchCPU *cpu = LOONGARCH_CPU(cs);
+    CPULoongArchState *env = &cpu->env;
+
+    ret = kvm_vcpu_ioctl(cs, KVM_HAS_DEVICE_ATTR, &attr);
+    if (!ret) {
+        kvm_vcpu_ioctl(cs, KVM_GET_DEVICE_ATTR, &attr);
+
+        if (FIELD_EX32(env->cpucfg[6], CPUCFG6, PMP)) {
+             /* Check PMP */
+             if (!FIELD_EX32(val, CPUCFG6, PMP)) {
+                 error_report("'pmu' feature not supported by KVM on this host"
+                              " Please disable 'pmu' with "
+                              "'... -cpu XXX,pmu=off ...'\n");
+                 exit(EXIT_FAILURE);
+             }
+             /* Check PMNUM */
+             int guest_pmnum = FIELD_EX32(env->cpucfg[6], CPUCFG6, PMNUM);
+             int host_pmnum = FIELD_EX32(val, CPUCFG6, PMNUM);
+             if (guest_pmnum > host_pmnum){
+                 warn_report("The guest pmnum %d larger than KVM support %d\n",
+                              guest_pmnum, host_pmnum);
+                 env->cpucfg[6] = FIELD_DP32(env->cpucfg[6], CPUCFG6,
+                                             PMNUM, host_pmnum);
+             }
+             /* Check PMBITS */
+             int guest_pmbits = FIELD_EX32(env->cpucfg[6], CPUCFG6, PMBITS);
+             int host_pmbits = FIELD_EX32(val, CPUCFG6, PMBITS);
+             if (guest_pmbits != host_pmbits) {
+                 warn_report("The host not support PMBITS %d\n", guest_pmbits);
+                 env->cpucfg[6] = FIELD_DP32(env->cpucfg[6], CPUCFG6,
+                                             PMBITS, host_pmbits);
+             }
+        }
+    }
+
+    return ret;
+}
+
 static int kvm_loongarch_put_cpucfg(CPUState *cs)
 {
     int i, ret = 0;
@@ -568,7 +615,13 @@  static int kvm_loongarch_put_cpucfg(CPUState *cs)
             if (ret) {
                 return ret;
             }
-	}
+        }
+        if (i == 6) {
+            ret = kvm_check_cpucfg6(cs);
+            if (ret) {
+                return ret;
+            }
+        }
         val = env->cpucfg[i];
         ret = kvm_set_one_reg(cs, KVM_IOC_CPUCFG(i), &val);
         if (ret < 0) {
diff --git a/target/loongarch/loongarch-qmp-cmds.c b/target/loongarch/loongarch-qmp-cmds.c
index 8721a5eb13..d7f2af363b 100644
--- a/target/loongarch/loongarch-qmp-cmds.c
+++ b/target/loongarch/loongarch-qmp-cmds.c
@@ -40,7 +40,7 @@  CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp)
 }
 
 static const char *cpu_model_advertised_features[] = {
-    "lsx", "lasx", NULL
+    "lsx", "lasx", "pmu", "pmnum", NULL
 };
 
 CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type,