misc: tegra-profiler: fix pmu init failure
Igor Nabirushkin [Mon, 5 Dec 2016 10:18:07 +0000 (14:18 +0400)]
On some linux-linux systems, midr register can be zeroed for
cores which are not really present in VM and this leads to PMU
initialization failure.
Process such cores correctly.
Do not show them in capabilities.

Bug 1848139

Change-Id: Id434a8e2cf4a323d49bdffe9ac06d837b05474ed
Signed-off-by: Igor Nabirushkin <inabirushkin@nvidia.com>
Reviewed-on: http://git-master/r/1270083
(cherry picked from commit 13569332e89040fe6a5ad05587ab52005055f4e6)
Reviewed-on: http://git-master/r/1274708
(cherry picked from commit 0889663b7fd527c06c37d0b25157eefce8b260b2)

drivers/misc/tegra-profiler/armv7_pmu.c
drivers/misc/tegra-profiler/armv8_pmu.c
drivers/misc/tegra-profiler/comm.c
drivers/misc/tegra-profiler/comm.h
drivers/misc/tegra-profiler/main.c
drivers/misc/tegra-profiler/quadd.h
drivers/misc/tegra-profiler/quadd_proc.c

index 1b803c7..4385e19 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * drivers/misc/tegra-profiler/armv7_pmu.c
  *
- * Copyright (c) 2014-2015, NVIDIA CORPORATION.  All rights reserved.
+ * Copyright (c) 2014-2016, NVIDIA CORPORATION.  All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms and conditions of the GNU General Public License,
@@ -714,6 +714,9 @@ static int get_supported_events(int cpuid, int *events, int max_events)
        int i, nr_events = 0;
        struct quadd_pmu_ctx *local_pmu_ctx = &__get_cpu_var(pmu_ctx);
 
+       if (!local_pmu_ctx->current_map)
+               return 0;
+
        max_events = min_t(int, QUADD_EVENT_TYPE_MAX, max_events);
 
        for (i = 0; i < max_events; i++) {
@@ -722,6 +725,7 @@ static int get_supported_events(int cpuid, int *events, int max_events)
                if (event != QUADD_ARMV7_UNSUPPORTED_EVENT)
                        events[nr_events++] = i;
        }
+
        return nr_events;
 }
 
@@ -745,7 +749,7 @@ static struct quadd_arch_info *get_arch(int cpuid)
 {
        struct quadd_pmu_ctx *local_pmu_ctx = &__get_cpu_var(pmu_ctx);
 
-       return &local_pmu_ctx->arch;
+       return local_pmu_ctx->current_map ? &local_pmu_ctx->arch : NULL;
 }
 
 static struct quadd_event_source_interface pmu_armv7_int = {
@@ -766,23 +770,30 @@ static struct quadd_event_source_interface pmu_armv7_int = {
        .get_arch               = get_arch,
 };
 
-static int quadd_armv7_pmu_init_for_cpu(int cpuid)
+static int quadd_armv7_pmu_init_for_cpu(int cpu)
 {
        int err = 0;
-       unsigned long cpu_id, cpu_implementer, part_number;
+       unsigned long cpuid, cpu_implementer, part_number;
 
-       struct cpuinfo_arm *local_cpu_data = &per_cpu(cpu_data, cpuid);
-       struct quadd_pmu_ctx *local_pmu_ctx = &per_cpu(pmu_ctx, cpuid);
-
-       cpu_id = local_cpu_data->cpuid;
-       cpu_implementer = cpu_id >> 24;
-       part_number = cpu_id & 0xFFF0;
+       struct cpuinfo_arm *local_cpu_data = &per_cpu(cpu_data, cpu);
+       struct quadd_pmu_ctx *local_pmu_ctx = &per_cpu(pmu_ctx, cpu);
 
        local_pmu_ctx->arch.type = QUADD_ARM_CPU_TYPE_UNKNOWN;
        local_pmu_ctx->arch.ver = 0;
+       local_pmu_ctx->current_map = NULL;
        strncpy(local_pmu_ctx->arch.name, "Unknown",
                sizeof(local_pmu_ctx->arch.name));
 
+       INIT_LIST_HEAD(&local_pmu_ctx->used_events);
+
+       cpuid = local_cpu_data->cpuid;
+
+       if (!cpuid)
+               return 0;
+
+       cpu_implementer = cpuid >> 24;
+       part_number = cpuid & 0xFFF0;
+
        if (cpu_implementer == ARM_CPU_IMP_ARM) {
                switch (part_number) {
                case ARM_CPU_PART_CORTEX_A9:
@@ -817,11 +828,9 @@ static int quadd_armv7_pmu_init_for_cpu(int cpuid)
                err = 1;
        }
 
-       INIT_LIST_HEAD(&local_pmu_ctx->used_events);
-
        local_pmu_ctx->arch.name[sizeof(local_pmu_ctx->arch.name) - 1] = '\0';
-       pr_info("arch: %s, type: %d, ver: %d\n",
-               local_pmu_ctx->arch.name, local_pmu_ctx->arch.type,
+       pr_info("[%d] arch: %s, type: %d, ver: %d\n",
+               cpu, local_pmu_ctx->arch.name, local_pmu_ctx->arch.type,
                local_pmu_ctx->arch.ver);
 
        return err;
@@ -830,12 +839,10 @@ static int quadd_armv7_pmu_init_for_cpu(int cpuid)
 struct quadd_event_source_interface *quadd_armv7_pmu_init(void)
 {
        struct quadd_event_source_interface *pmu = NULL;
-       int cpuid;
-       int err;
-       int initialized = 1;
+       int cpu, err, initialized = 1;
 
-       for_each_possible_cpu(cpuid) {
-               err = quadd_armv7_pmu_init_for_cpu(cpuid);
+       for_each_possible_cpu(cpu) {
+               err = quadd_armv7_pmu_init_for_cpu(cpu);
                if (err) {
                        initialized = 0;
                        break;
@@ -852,11 +859,12 @@ struct quadd_event_source_interface *quadd_armv7_pmu_init(void)
 
 void quadd_armv7_pmu_deinit(void)
 {
-       int cpuid;
+       int cpu;
 
-       for_each_possible_cpu(cpuid) {
-               struct quadd_pmu_ctx *local_pmu_ctx = &per_cpu(pmu_ctx, cpuid);
+       for_each_possible_cpu(cpu) {
+               struct quadd_pmu_ctx *local_pmu_ctx = &per_cpu(pmu_ctx, cpu);
 
-               free_events(&local_pmu_ctx->used_events);
+               if (local_pmu_ctx->current_map)
+                       free_events(&local_pmu_ctx->used_events);
        }
 }
index 818c7dc..fa378b2 100644 (file)
@@ -769,6 +769,9 @@ static int get_supported_events(int cpuid, int *events, int max_events)
 
        struct quadd_pmu_ctx *local_pmu_ctx = &per_cpu(pmu_ctx, cpuid);
 
+       if (!local_pmu_ctx->current_map)
+               return 0;
+
        max_events = min_t(int, QUADD_EVENT_TYPE_MAX, max_events);
 
        for (i = 0; i < max_events; i++) {
@@ -777,6 +780,7 @@ static int get_supported_events(int cpuid, int *events, int max_events)
                if (event != QUADD_ARMV8_UNSUPPORTED_EVENT)
                        events[nr_events++] = i;
        }
+
        return nr_events;
 }
 
@@ -802,7 +806,7 @@ static struct quadd_arch_info *get_arch(int cpuid)
 {
        struct quadd_pmu_ctx *local_pmu_ctx = &per_cpu(pmu_ctx, cpuid);
 
-       return &local_pmu_ctx->arch;
+       return local_pmu_ctx->current_map ? &local_pmu_ctx->arch : NULL;
 }
 
 static struct quadd_event_source_interface pmu_armv8_int = {
@@ -825,17 +829,13 @@ static struct quadd_event_source_interface pmu_armv8_int = {
 
 static int quadd_armv8_pmu_init_for_cpu(int cpuid)
 {
-       u32 pmcr;
-       u32 idcode = 0;
-       int err = 0;
-       int idx;
+       int idx, err = 0;
+       u32 pmcr, ext_ver, idcode = 0;
+       u64 aa64_dfr;
+       u8 implementer;
        struct cpuinfo_arm64 *local_cpu_data = &per_cpu(cpu_data, cpuid);
        struct quadd_pmu_ctx *local_pmu_ctx = &per_cpu(pmu_ctx, cpuid);
        u32 reg_midr = local_cpu_data->reg_midr;
-       u32 ext_ver;
-       u64 aa64_dfr;
-
-       char implementer = (reg_midr >> 24) & 0xFF;
 
        strncpy(local_pmu_ctx->arch.name, "Unknown",
                        sizeof(local_pmu_ctx->arch.name));
@@ -844,6 +844,13 @@ static int quadd_armv8_pmu_init_for_cpu(int cpuid)
        local_pmu_ctx->arch.ver = 0;
        local_pmu_ctx->current_map = NULL;
 
+       INIT_LIST_HEAD(&local_pmu_ctx->used_events);
+
+       if (!reg_midr)
+               return 0;
+
+       implementer = (reg_midr >> 24) & 0xFF;
+
        aa64_dfr = read_cpuid(ID_AA64DFR0_EL1);
        aa64_dfr = (aa64_dfr >> 8) & 0x0f;
 
@@ -929,11 +936,10 @@ static int quadd_armv8_pmu_init_for_cpu(int cpuid)
        }
 
        local_pmu_ctx->arch.name[sizeof(local_pmu_ctx->arch.name) - 1] = '\0';
-       pr_info("arch: %s, type: %d, ver: %d\n",
-               local_pmu_ctx->arch.name, local_pmu_ctx->arch.type,
+       pr_info("[%d] arch: %s, type: %d, ver: %d\n",
+               cpuid, local_pmu_ctx->arch.name, local_pmu_ctx->arch.type,
                local_pmu_ctx->arch.ver);
 
-       INIT_LIST_HEAD(&local_pmu_ctx->used_events);
        return err;
 }
 
@@ -967,6 +973,7 @@ void quadd_armv8_pmu_deinit(void)
        for_each_possible_cpu(cpu_id) {
                struct quadd_pmu_ctx *local_pmu_ctx = &per_cpu(pmu_ctx, cpu_id);
 
-               free_events(&local_pmu_ctx->used_events);
+               if (local_pmu_ctx->current_map)
+                       free_events(&local_pmu_ctx->used_events);
        }
 }
index 50c3dfb..ae16e54 100644 (file)
@@ -406,16 +406,21 @@ static void rb_reset(struct quadd_ring_buffer *rb)
 static int
 ready_to_profile(void)
 {
-       int cpuid;
+       int cpuid, is_cpu_present;
+       struct comm_cpu_context *cc;
 
        if (!comm_ctx.params_ok)
                return 0;
 
        for_each_possible_cpu(cpuid) {
-               struct comm_cpu_context *cc = &per_cpu(cpu_ctx, cpuid);
+               is_cpu_present = comm_ctx.control->is_cpu_present(cpuid);
+
+               if (is_cpu_present) {
+                       cc = &per_cpu(cpu_ctx, cpuid);
 
-               if (!cc->params_ok)
-                       return 0;
+                       if (!cc->params_ok)
+                               return 0;
+               }
        }
 
        return 1;
index db9def3..f330b80 100644 (file)
@@ -63,6 +63,7 @@ struct quadd_comm_control_interface {
        int (*set_extab)(struct quadd_sections *extabs,
                         struct quadd_mmap_area *mmap);
        void (*delete_mmap)(struct quadd_mmap_area *mmap);
+       int (*is_cpu_present)(int cpuid);
 };
 
 struct quadd_comm_data_interface {
index b3572a6..2939173 100644 (file)
@@ -202,6 +202,9 @@ set_parameters_for_cpu(struct quadd_pmu_setup_for_cpu *params)
        struct source_info *pmu_info = &per_cpu(ctx_pmu_info, cpuid);
        int pmu_events_id[QUADD_MAX_COUNTERS];
 
+       if (!pmu_info->is_present)
+               return -ENODEV;
+
        for (i = 0; i < params->nr_events; i++) {
                int event = params->events[i];
 
@@ -342,9 +345,14 @@ static void
 get_capabilities_for_cpu(int cpuid, struct quadd_comm_cap_for_cpu *cap)
 {
        int i;
-       struct quadd_events_cap *events_cap = &cap->events_cap;
+       struct quadd_events_cap *events_cap;
        struct source_info *s = &per_cpu(ctx_pmu_info, cpuid);
 
+       if (!s->is_present)
+               return;
+
+       events_cap = &cap->events_cap;
+
        cap->cpuid = cpuid;
        events_cap->cpu_cycles = 0;
        events_cap->l1_dcache_read_misses = 0;
@@ -416,11 +424,31 @@ get_capabilities_for_cpu(int cpuid, struct quadd_comm_cap_for_cpu *cap)
        }
 }
 
+static u32 get_possible_cpu(void)
+{
+       int cpu;
+       u32 mask = 0;
+       struct source_info *s;
+
+       if (ctx.pmu) {
+               for_each_possible_cpu(cpu) {
+                       /* since we don't support more than 32 CPUs */
+                       if (cpu >= BITS_PER_BYTE * sizeof(mask))
+                               break;
+
+                       s = &per_cpu(ctx_pmu_info, cpu);
+                       if (s->is_present)
+                               mask |= (1U << cpu);
+               }
+       }
+
+       return mask;
+}
+
 static void
 get_capabilities(struct quadd_comm_cap *cap)
 {
        int i;
-       int cpuid;
        unsigned int extra = 0;
        struct quadd_events_cap *events_cap = &cap->events_cap;
 
@@ -490,9 +518,7 @@ get_capabilities(struct quadd_comm_cap *cap)
                extra |= QUADD_COMM_CAP_EXTRA_ARCH_TIMER;
 
        cap->reserved[QUADD_COMM_CAP_IDX_EXTRA] = extra;
-       cap->reserved[QUADD_COMM_CAP_IDX_CPU_MASK] = 0;
-       for_each_possible_cpu(cpuid)
-               cap->reserved[QUADD_COMM_CAP_IDX_CPU_MASK] |= (1 << cpuid);
+       cap->reserved[QUADD_COMM_CAP_IDX_CPU_MASK] = get_possible_cpu();
 }
 
 void quadd_get_state(struct quadd_module_state *state)
@@ -523,6 +549,14 @@ delete_mmap(struct quadd_mmap_area *mmap)
        quadd_unwind_delete_mmap(mmap);
 }
 
+static int
+is_cpu_present(int cpuid)
+{
+       struct source_info *s = &per_cpu(ctx_pmu_info, cpuid);
+
+       return s->is_present;
+}
+
 static struct quadd_comm_control_interface control = {
        .start                  = start,
        .stop                   = stop,
@@ -533,6 +567,7 @@ static struct quadd_comm_control_interface control = {
        .get_state              = quadd_get_state,
        .set_extab              = set_extab,
        .delete_mmap            = delete_mmap,
+       .is_cpu_present         = is_cpu_present,
 };
 
 static int __init quadd_module_init(void)
@@ -558,8 +593,10 @@ static int __init quadd_module_init(void)
        for_each_possible_cpu(cpuid) {
                struct source_info *pmu_info = &per_cpu(ctx_pmu_info, cpuid);
 
-               pmu_info->active = 1;
+               pmu_info->active = 0;
+               pmu_info->is_present = 0;
        }
+
        ctx.pl310_info.active = 0;
 
 #ifdef CONFIG_ARM64
@@ -573,8 +610,15 @@ static int __init quadd_module_init(void)
        }
 
        for_each_possible_cpu(cpuid) {
-               struct source_info *pmu_info = &per_cpu(ctx_pmu_info,
-                                                       cpuid);
+               struct quadd_arch_info *arch;
+               struct source_info *pmu_info;
+
+               arch = ctx.pmu->get_arch(cpuid);
+               if (!arch)
+                       continue;
+
+               pmu_info = &per_cpu(ctx_pmu_info, cpuid);
+               pmu_info->is_present = 1;
 
                events = pmu_info->supported_events;
                nr_events =
index b0d728b..1cd4cf6 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * drivers/misc/tegra-profiler/quadd.h
  *
- * Copyright (c) 2014-2015, NVIDIA CORPORATION.  All rights reserved.
+ * Copyright (c) 2014-2016, NVIDIA CORPORATION.  All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms and conditions of the GNU General Public License,
@@ -43,6 +43,7 @@ struct source_info {
        int supported_events[QUADD_MAX_COUNTERS];
        int nr_supported_events;
 
+       int is_present;
        int active;
 };
 
index 9d446a4..bf9e339 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * drivers/misc/tegra-profiler/quadd_proc.c
  *
- * Copyright (c) 2014-2015, NVIDIA CORPORATION.  All rights reserved.
+ * Copyright (c) 2014-2016, NVIDIA CORPORATION.  All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms and conditions of the GNU General Public License,
@@ -61,7 +61,6 @@ static int show_capabilities(struct seq_file *f, void *offset)
        unsigned int extra = cap->reserved[QUADD_COMM_CAP_IDX_EXTRA];
        struct quadd_arch_info *arch = NULL;
 
-
        seq_printf(f, "pmu:                                   %s\n",
                   YES_NO(cap->pmu));
        seq_printf(f, "tegra 3 LP cluster:                    %s\n",
@@ -99,17 +98,20 @@ static int show_capabilities(struct seq_file *f, void *offset)
                        struct quadd_comm_cap_for_cpu *cpu_cap;
                        struct quadd_events_cap *event;
 
+                       arch = ctx->pmu->get_arch(cpuid);
+                       if (!arch)
+                               continue;
+
                        cpu_cap = ctx->get_capabilities_for_cpu(cpuid);
                        event = &cpu_cap->events_cap;
 
-                       arch = ctx->pmu->get_arch(cpuid);
                        seq_printf(f, "\nCPU %d\n", cpuid);
-                       if (arch) {
-                               seq_printf(f, "pmu arch:                  %s\n",
-                                                  arch->name);
-                               seq_printf(f, "pmu arch version:          %d\n",
-                                                  arch->ver);
-                       }
+
+                       seq_printf(f, "pmu arch:                  %s\n",
+                                          arch->name);
+                       seq_printf(f, "pmu arch version:          %d\n",
+                                          arch->ver);
+
                        seq_printf(f, "l2 cache:                  %s\n",
                                   YES_NO(cpu_cap->l2_cache));
                        if (cap->l2_cache) {