From: Brad Peters <bpeters@redhat.com> Date: Fri, 1 Aug 2008 16:18:37 -0400 Subject: [ppc] oprofile: wrong cpu_type returned Message-id: 20080801201837.19423.72393.sendpatchset@squad5-lp1.lab.bos.redhat.com O-Subject: [PATCH RHEL5.3] wrong cpu_type returned to oprofile Bugzilla: 441539 RH-Acked-by: David Howells <dhowells@redhat.com> RHBZ#: ====== https://bugzilla.redhat.com/show_bug.cgi?id=441539 Description: =========== Bug fix / Power arch only Some IBM machines supply a "logical" PVR (processor version register) value in the device tree in the cpu nodes rather than the real PVR. This is used for instance to indicate that the processors in a POWER6 partition have been configured by the hypervisor to run in POWER5+ mode rather than POWER6 mode. To cope with this, we call identify_cpu a second time with the logical PVR value (the first call is with the real PVR value in the very early setup code). However, POWER5+ machines can also supply a logical PVR value, and use the same value (the value that indicates a v2.04 architecture compliant processor). This causes problems for code that uses the performance monitor (such as oprofile), because the PMU registers are different in POWER6 (even in POWER5+ mode) from the real POWER5+. This change works around this problem by taking out the PMU information from the cputable entries for the logical PVR values, and changing identify_cpu so that the second call to it won't overwrite the PMU information that was established by the first call (the one with the real PVR), but does update the other fields. Specifically, if the cputable entry for the logical PVR value has num_pmcs == 0, none of the PMU-related fields get used. So that we can create a mixed cputable entry, we now make cur_cpu_spec point to a single static struct cpu_spec, and copy stuff from cpu_specs[i] into it. This has the side-effect that we can now make cpu_specs[] be initdata. Ultimately it would be good to move the PMU-related fields out to a separate structure, pointed to by the cputable entries, and change identify_cpu so that it saves the PMU info pointer, copies the whole structure, and restores the PMU info pointer, rather than identify_cpu having to list all the fields that are *not* PMU-related. RHEL Version Found: ================ RHEL 5.2 kABI Status: ============ No symbols were harmed. Brew: ===== Built on all platforms. http://brewweb.devel.redhat.com/brew/taskinfo?taskID=1371599 Upstream Status: ================ Upstream at git commit#87a72f9e171e558a0288aa83ef1dc6ae4af32224 Test Status: ============ Confirmed bootability and confirmed that /dev/oprofile/cpu_type returned proper CPU value Brad Peters 8-1-08 =============================================================== Brad Peters 1-978-392-1000 x 23183 IBM on-site partner. Proposed Patch: =============== This patch is based on 2.6.18-94.el5 diff --git a/arch/powerpc/kernel/cputable.c b/arch/powerpc/kernel/cputable.c index 0af93f4..88481ee 100644 --- a/arch/powerpc/kernel/cputable.c +++ b/arch/powerpc/kernel/cputable.c @@ -275,13 +275,6 @@ static struct cpu_spec cpu_specs[] = { .cpu_user_features = COMMON_USER_POWER5_PLUS, .icache_bsize = 128, .dcache_bsize = 128, - .num_pmcs = 6, - .oprofile_cpu_type = "ppc64/power6", - .oprofile_type = PPC_OPROFILE_POWER4, - .oprofile_mmcra_sihv = POWER6_MMCRA_SIHV, - .oprofile_mmcra_sipr = POWER6_MMCRA_SIPR, - .oprofile_mmcra_clear = POWER6_MMCRA_THRM | - POWER6_MMCRA_OTHER, .platform = "power5+", }, { /* Power5++ */ @@ -325,13 +318,6 @@ static struct cpu_spec cpu_specs[] = { .cpu_user_features = COMMON_USER_POWER6, .icache_bsize = 128, .dcache_bsize = 128, - .num_pmcs = 6, - .oprofile_cpu_type = "ppc64/power6", - .oprofile_type = PPC_OPROFILE_POWER4, - .oprofile_mmcra_sihv = POWER6_MMCRA_SIHV, - .oprofile_mmcra_sipr = POWER6_MMCRA_SIPR, - .oprofile_mmcra_clear = POWER6_MMCRA_THRM | - POWER6_MMCRA_OTHER, .platform = "power6", }, { /* Cell Broadband Engine */ @@ -1190,18 +1176,36 @@ static struct cpu_spec cpu_specs[] = { #endif /* CONFIG_PPC32 */ }; +static struct cpu_spec the_cpu_spec; + struct cpu_spec *identify_cpu(unsigned long offset, unsigned int pvr) { struct cpu_spec *s = cpu_specs; - struct cpu_spec **cur = &cur_cpu_spec; + struct cpu_spec *t = &the_cpu_spec; int i; s = PTRRELOC(s); - cur = PTRRELOC(cur); + t = PTRRELOC(t); for (i = 0; i < ARRAY_SIZE(cpu_specs); i++,s++) if ((pvr & s->pvr_mask) == s->pvr_value) { - *cur = cpu_specs + i; + /* + * If we are overriding a previous value derived + * from the real PVR with a new value obtained + * using a logical PVR value, don't modify the + * performance monitor fields. + */ + if (t->num_pmcs && !s->num_pmcs) { + t->cpu_name = s->cpu_name; + t->cpu_features = s->cpu_features; + t->cpu_user_features = s->cpu_user_features; + t->icache_bsize = s->icache_bsize; + t->dcache_bsize = s->dcache_bsize; + t->cpu_setup = s->cpu_setup; + t->platform = s->platform; + } else + *t = *s; + *PTRRELOC(&cur_cpu_spec) = &the_cpu_spec; #ifdef CONFIG_PPC64 /* ppc64 expects identify_cpu to also call setup_cpu * for that processor. I will consolidate that at a diff --git a/include/asm-powerpc/cputable.h b/include/asm-powerpc/cputable.h index 5448082..ba105f6 100644 --- a/include/asm-powerpc/cputable.h +++ b/include/asm-powerpc/cputable.h @@ -50,6 +50,7 @@ enum powerpc_oprofile_type { #endif }; +/* NOTE WELL: Update identify_cpu() if fields are added or removed! */ struct cpu_spec { /* CPU is matched via (PVR & pvr_mask) == pvr_value */ unsigned int pvr_mask;