From patchwork Fri Apr 15 18:12:30 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric B Munson X-Patchwork-Id: 91427 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from ozlabs.org (localhost [IPv6:::1]) by ozlabs.org (Postfix) with ESMTP id E0FA0100AF2 for ; Sat, 16 Apr 2011 04:12:55 +1000 (EST) Received: from mail-qw0-f51.google.com (mail-qw0-f51.google.com [209.85.216.51]) (using TLSv1 with cipher RC4-SHA (128/128 bits)) (Client CN "smtp.gmail.com", Issuer "Google Internet Authority" (verified OK)) by ozlabs.org (Postfix) with ESMTPS id CADE71007D7 for ; Sat, 16 Apr 2011 04:12:46 +1000 (EST) Received: by qwf7 with SMTP id 7so1821896qwf.38 for ; Fri, 15 Apr 2011 11:12:42 -0700 (PDT) Received: by 10.229.68.106 with SMTP id u42mr1617028qci.284.1302891162678; Fri, 15 Apr 2011 11:12:42 -0700 (PDT) Received: from bert.mgebm.net (EASTGATE-SEVEN-THIRTY-ONE.MIT.EDU [18.97.7.220]) by mx.google.com with ESMTPS id f5sm2095146qck.32.2011.04.15.11.12.39 (version=TLSv1/SSLv3 cipher=OTHER); Fri, 15 Apr 2011 11:12:40 -0700 (PDT) Received: by bert.mgebm.net (Postfix, from userid 1000) id 7173C15C1925; Fri, 15 Apr 2011 14:12:36 -0400 (EDT) From: Eric B Munson To: benh@kernel.crashing.org Subject: [PATCH V4] POWER: perf_event: Skip updating kernel counters if register value shrinks Date: Fri, 15 Apr 2011 14:12:30 -0400 Message-Id: <1302891150-8121-1-git-send-email-emunson@mgebm.net> X-Mailer: git-send-email 1.7.1 Cc: a.p.zijlstra@chello.nl, linux-kernel@vger.kernel.org, David.Laight@ACULAB.COM, paulus@samba.org, anton@samba.org, acme@ghostprotocols.net, mingo@elte.hu, linuxppc-dev@lists.ozlabs.org, stable@kernel.org, Eric B Munson X-BeenThere: linuxppc-dev@lists.ozlabs.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: Linux on PowerPC Developers Mail List List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: linuxppc-dev-bounces+patchwork-incoming=ozlabs.org@lists.ozlabs.org Sender: linuxppc-dev-bounces+patchwork-incoming=ozlabs.org@lists.ozlabs.org Because of speculative event roll back, it is possible for some event coutners to decrease between reads on POWER7. This causes a problem with the way that counters are updated. Delta calues are calculated in a 64 bit value and the top 32 bits are masked. If the register value has decreased, this leaves us with a very large positive value added to the kernel counters. This patch protects against this by skipping the update if the delta would be negative. This can lead to a lack of precision in the coutner values, but from my testing the value is typcially fewer than 10 samples at a time. Signed-off-by: Eric B Munson Cc: stable@kernel.org --- Changes from V3: Fix delta checking so that only roll backs are discarded Changes from V2: Create a helper that should handle counter roll back as well as registers that might be allowed to roll over Changes from V1: Updated patch leader Added stable CC Use an s32 to hold delta values and discard any values that are less than 0 arch/powerpc/kernel/perf_event.c | 37 ++++++++++++++++++++++++++++++------- 1 files changed, 30 insertions(+), 7 deletions(-) diff --git a/arch/powerpc/kernel/perf_event.c b/arch/powerpc/kernel/perf_event.c index c4063b7..822f630 100644 --- a/arch/powerpc/kernel/perf_event.c +++ b/arch/powerpc/kernel/perf_event.c @@ -398,6 +398,25 @@ static int check_excludes(struct perf_event **ctrs, unsigned int cflags[], return 0; } +static u64 check_and_compute_delta(u64 prev, u64 val) +{ + u64 delta = (val - prev) & 0xfffffffful; + + /* + * POWER7 can roll back counter values, if the new value is smaller + * than the previous value it will cause the delta and the counter to + * have bogus values unless we rolled a counter over. If a coutner is + * rolled back, it will be smaller, but within 256, which is the maximum + * number of events to rollback at once. If we dectect a rollback + * return 0. This can lead to a small lack of precision in the + * counters. + */ + if (prev > val && (prev - val) < 256) + delta = 0; + + return delta; +} + static void power_pmu_read(struct perf_event *event) { s64 val, delta, prev; @@ -416,10 +435,11 @@ static void power_pmu_read(struct perf_event *event) prev = local64_read(&event->hw.prev_count); barrier(); val = read_pmc(event->hw.idx); + delta = check_and_compute_delta(prev, val); + if (!delta) + return; } while (local64_cmpxchg(&event->hw.prev_count, prev, val) != prev); - /* The counters are only 32 bits wide */ - delta = (val - prev) & 0xfffffffful; local64_add(delta, &event->count); local64_sub(delta, &event->hw.period_left); } @@ -449,8 +469,9 @@ static void freeze_limited_counters(struct cpu_hw_events *cpuhw, val = (event->hw.idx == 5) ? pmc5 : pmc6; prev = local64_read(&event->hw.prev_count); event->hw.idx = 0; - delta = (val - prev) & 0xfffffffful; - local64_add(delta, &event->count); + delta = check_and_compute_delta(prev, val); + if (delta) + local64_add(delta, &event->count); } } @@ -458,14 +479,16 @@ static void thaw_limited_counters(struct cpu_hw_events *cpuhw, unsigned long pmc5, unsigned long pmc6) { struct perf_event *event; - u64 val; + u64 val, prev; int i; for (i = 0; i < cpuhw->n_limited; ++i) { event = cpuhw->limited_counter[i]; event->hw.idx = cpuhw->limited_hwidx[i]; val = (event->hw.idx == 5) ? pmc5 : pmc6; - local64_set(&event->hw.prev_count, val); + prev = local64_read(&event->hw.prev_count); + if (check_and_compute_delta(prev, val)) + local64_set(&event->hw.prev_count, val); perf_event_update_userpage(event); } } @@ -1197,7 +1220,7 @@ static void record_and_restart(struct perf_event *event, unsigned long val, /* we don't have to worry about interrupts here */ prev = local64_read(&event->hw.prev_count); - delta = (val - prev) & 0xfffffffful; + delta = check_and_compute_delta(prev, val); local64_add(delta, &event->count); /*