From patchwork Fri Jan 30 19:03:22 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Thompson X-Patchwork-Id: 44063 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-lb0-f199.google.com (mail-lb0-f199.google.com [209.85.217.199]) by ip-10-151-82-157.ec2.internal (Postfix) with ESMTPS id 64EF920CA8 for ; Fri, 30 Jan 2015 19:03:37 +0000 (UTC) Received: by mail-lb0-f199.google.com with SMTP id f15sf4255624lbj.2 for ; Fri, 30 Jan 2015 11:03:36 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:delivered-to:from:to:cc:subject :date:message-id:in-reply-to:references:x-original-sender :x-original-authentication-results:precedence:mailing-list:list-id :list-post:list-help:list-archive:list-unsubscribe; bh=vsjNooOaxphyKUQ0PfKleFe2feij+kpMTJuqczC6seA=; b=iPDbPCCBX8hokjJxcedPcYDXXFkaSWP4BJ44gkR4qiFVih7l5SMF9W4T75UCL/yry6 4eA8MtOeIZiN8k5w6YKTWgcVh7oFYw6V3phaKmE4Yh5combYGMs4q347qo/CjelAJypM SFjzF8dzQjfaOfHOnDCUA1u/1Z90yJiBbz8x4bqoP33WKnSojIcdkc+XP35YUQ9i8D8e cQOrV+1aZSQSwNs49zJXfFTXc7kVpsczlVKbcpcbgXrBZ5qmuvaQRPwE8uev5U6ARrkA bVEZmlkGWz5DbY8GvwZbZLWvUI+YsMGc2LKk10BMbXN8cth65xyMABRL9LYv6sJXnWS6 UjCA== X-Gm-Message-State: ALoCoQkLjpO07kw2AB1Cm9n3K59p/djAxWa1TgQsWrDHeTel6aK9UHlHtaGb248xpcIYXgpVWTFu X-Received: by 10.152.26.74 with SMTP id j10mr1040098lag.10.1422644616423; Fri, 30 Jan 2015 11:03:36 -0800 (PST) MIME-Version: 1.0 X-BeenThere: patchwork-forward@linaro.org Received: by 10.152.4.97 with SMTP id j1ls411432laj.28.gmail; Fri, 30 Jan 2015 11:03:36 -0800 (PST) X-Received: by 10.112.160.193 with SMTP id xm1mr8091477lbb.5.1422644616273; Fri, 30 Jan 2015 11:03:36 -0800 (PST) Received: from mail-lb0-f172.google.com (mail-lb0-f172.google.com. [209.85.217.172]) by mx.google.com with ESMTPS id my5si8363302lbb.93.2015.01.30.11.03.36 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Fri, 30 Jan 2015 11:03:36 -0800 (PST) Received-SPF: pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.217.172 as permitted sender) client-ip=209.85.217.172; Received: by mail-lb0-f172.google.com with SMTP id l4so37930047lbv.3 for ; Fri, 30 Jan 2015 11:03:36 -0800 (PST) X-Received: by 10.112.181.41 with SMTP id dt9mr7974403lbc.56.1422644616167; Fri, 30 Jan 2015 11:03:36 -0800 (PST) X-Forwarded-To: patchwork-forward@linaro.org X-Forwarded-For: patch@linaro.org patchwork-forward@linaro.org Delivered-To: patches@linaro.org Received: by 10.112.35.133 with SMTP id h5csp380599lbj; Fri, 30 Jan 2015 11:03:35 -0800 (PST) X-Received: by 10.180.228.72 with SMTP id sg8mr409068wic.48.1422644615238; Fri, 30 Jan 2015 11:03:35 -0800 (PST) Received: from mail-wi0-f179.google.com (mail-wi0-f179.google.com. [209.85.212.179]) by mx.google.com with ESMTPS id ia1si22293435wjb.155.2015.01.30.11.03.35 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Fri, 30 Jan 2015 11:03:35 -0800 (PST) Received-SPF: pass (google.com: domain of daniel.thompson@linaro.org designates 209.85.212.179 as permitted sender) client-ip=209.85.212.179; Received: by mail-wi0-f179.google.com with SMTP id l15so4489540wiw.0 for ; Fri, 30 Jan 2015 11:03:35 -0800 (PST) X-Received: by 10.180.8.169 with SMTP id s9mr375105wia.72.1422644614958; Fri, 30 Jan 2015 11:03:34 -0800 (PST) Received: from sundance.lan (cpc4-aztw19-0-0-cust157.18-1.cable.virginm.net. [82.33.25.158]) by mx.google.com with ESMTPSA id d6sm8166187wic.1.2015.01.30.11.03.32 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 30 Jan 2015 11:03:33 -0800 (PST) From: Daniel Thompson To: Thomas Gleixner , John Stultz Cc: Daniel Thompson , linux-kernel@vger.kernel.org, patches@linaro.org, linaro-kernel@lists.linaro.org, Sumit Semwal , Stephen Boyd , Steven Rostedt , Russell King , Will Deacon , Catalin Marinas Subject: [PATCH v3 4/4] sched_clock: Avoid deadlock during read from NMI Date: Fri, 30 Jan 2015 19:03:22 +0000 Message-Id: <1422644602-11953-5-git-send-email-daniel.thompson@linaro.org> X-Mailer: git-send-email 1.9.3 In-Reply-To: <1422644602-11953-1-git-send-email-daniel.thompson@linaro.org> References: <1421859236-19782-1-git-send-email-daniel.thompson@linaro.org> <1422644602-11953-1-git-send-email-daniel.thompson@linaro.org> X-Removed-Original-Auth: Dkim didn't pass. X-Original-Sender: daniel.thompson@linaro.org X-Original-Authentication-Results: mx.google.com; spf=pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.217.172 as permitted sender) smtp.mail=patch+caf_=patchwork-forward=linaro.org@linaro.org Precedence: list Mailing-list: list patchwork-forward@linaro.org; contact patchwork-forward+owners@linaro.org List-ID: X-Google-Group-Id: 836684582541 List-Post: , List-Help: , List-Archive: List-Unsubscribe: , Currently it is possible for an NMI (or FIQ on ARM) to come in and read sched_clock() whilst update_sched_clock() has locked the seqcount for writing. This results in the NMI handler locking up when it calls raw_read_seqcount_begin(). This patch fixes the NMI safety issues by providing banked clock data. This is a similar approach to the one used in Thomas Gleixner's 4396e058c52e("timekeeping: Provide fast and NMI safe access to CLOCK_MONOTONIC"). Suggested-by: Stephen Boyd Signed-off-by: Daniel Thompson Cc: Russell King Cc: Will Deacon Cc: Catalin Marinas --- kernel/time/sched_clock.c | 91 ++++++++++++++++++++++++++++++----------------- 1 file changed, 58 insertions(+), 33 deletions(-) diff --git a/kernel/time/sched_clock.c b/kernel/time/sched_clock.c index 638c765131fa..2b1a465f1c00 100644 --- a/kernel/time/sched_clock.c +++ b/kernel/time/sched_clock.c @@ -40,12 +40,12 @@ struct clock_read_data { * registration of a new clock source) * * The ordering of this structure has been chosen to optimize cache - * performance. In particular seq and read_data (combined) should fit + * performance. In particular seq and read_data[0] (combined) should fit * into a single 64 byte cache line. */ struct clock_data { seqcount_t seq; - struct clock_read_data read_data; + struct clock_read_data read_data[2]; ktime_t wrap_kt; unsigned long rate; u64 (*actual_read_sched_clock)(void); @@ -66,10 +66,9 @@ static u64 notrace jiffy_sched_clock_read(void) } static struct clock_data cd ____cacheline_aligned = { - .read_data = { .mult = NSEC_PER_SEC / HZ, - .read_sched_clock = jiffy_sched_clock_read, }, + .read_data[0] = { .mult = NSEC_PER_SEC / HZ, + .read_sched_clock = jiffy_sched_clock_read, }, .actual_read_sched_clock = jiffy_sched_clock_read, - }; static inline u64 notrace cyc_to_ns(u64 cyc, u32 mult, u32 shift) @@ -81,10 +80,11 @@ unsigned long long notrace sched_clock(void) { u64 cyc, res; unsigned long seq; - struct clock_read_data *rd = &cd.read_data; + struct clock_read_data *rd; do { - seq = raw_read_seqcount_begin(&cd.seq); + seq = raw_read_seqcount(&cd.seq); + rd = cd.read_data + (seq & 1); res = rd->epoch_ns; if (rd->read_sched_clock) { @@ -98,26 +98,50 @@ unsigned long long notrace sched_clock(void) } /* + * Updating the data required to read the clock. + * + * sched_clock will never observe mis-matched data even if called from + * an NMI. We do this by maintaining an odd/even copy of the data and + * steering sched_clock to one or the other using a sequence counter. + * In order to preserve the data cache profile of sched_clock as much + * as possible the system reverts back to the even copy when the update + * completes; the odd copy is used *only* during an update. + */ +static void update_clock_read_data(struct clock_read_data *rd) +{ + /* update the backup (odd) copy with the new data */ + cd.read_data[1] = *rd; + + /* steer readers towards the new data */ + raw_write_seqcount_latch(&cd.seq); + + /* now its safe for us to update the normal (even) copy */ + cd.read_data[0] = *rd; + + /* switch readers back to the even copy */ + raw_write_seqcount_latch(&cd.seq); +} + +/* * Atomically update the sched_clock epoch. */ static void notrace update_sched_clock(void) { - unsigned long flags; u64 cyc; u64 ns; - struct clock_read_data *rd = &cd.read_data; + struct clock_read_data rd; + + rd = cd.read_data[0]; cyc = cd.actual_read_sched_clock(); - ns = rd->epoch_ns + - cyc_to_ns((cyc - rd->epoch_cyc) & rd->sched_clock_mask, - rd->mult, rd->shift); - - raw_local_irq_save(flags); - raw_write_seqcount_begin(&cd.seq); - rd->epoch_ns = ns; - rd->epoch_cyc = cyc; - raw_write_seqcount_end(&cd.seq); - raw_local_irq_restore(flags); + ns = rd.epoch_ns + + cyc_to_ns((cyc - rd.epoch_cyc) & rd.sched_clock_mask, + rd.mult, rd.shift); + + rd.epoch_ns = ns; + rd.epoch_cyc = cyc; + + update_clock_read_data(&rd); } static enum hrtimer_restart sched_clock_poll(struct hrtimer *hrt) @@ -134,7 +158,7 @@ void __init sched_clock_register(u64 (*read)(void), int bits, u32 new_mult, new_shift; unsigned long r; char r_unit; - struct clock_read_data *rd = &cd.read_data; + struct clock_read_data rd; if (cd.rate > rate) return; @@ -151,22 +175,23 @@ void __init sched_clock_register(u64 (*read)(void), int bits, wrap = clocks_calc_max_nsecs(new_mult, new_shift, 0, new_mask); cd.wrap_kt = ns_to_ktime(wrap - (wrap >> 3)); + rd = cd.read_data[0]; + /* update epoch for new counter and update epoch_ns from old counter*/ new_epoch = read(); cyc = cd.actual_read_sched_clock(); - ns = rd->epoch_ns + - cyc_to_ns((cyc - rd->epoch_cyc) & rd->sched_clock_mask, - rd->mult, rd->shift); + ns = rd.epoch_ns + + cyc_to_ns((cyc - rd.epoch_cyc) & rd.sched_clock_mask, + rd.mult, rd.shift); cd.actual_read_sched_clock = read; - raw_write_seqcount_begin(&cd.seq); - rd->read_sched_clock = read; - rd->sched_clock_mask = new_mask; - rd->mult = new_mult; - rd->shift = new_shift; - rd->epoch_cyc = new_epoch; - rd->epoch_ns = ns; - raw_write_seqcount_end(&cd.seq); + rd.read_sched_clock = read; + rd.sched_clock_mask = new_mask; + rd.mult = new_mult; + rd.shift = new_shift; + rd.epoch_cyc = new_epoch; + rd.epoch_ns = ns; + update_clock_read_data(&rd); r = rate; if (r >= 4000000) { @@ -213,7 +238,7 @@ void __init sched_clock_postinit(void) static int sched_clock_suspend(void) { - struct clock_read_data *rd = &cd.read_data; + struct clock_read_data *rd = &cd.read_data[0]; update_sched_clock(); hrtimer_cancel(&sched_clock_timer); @@ -223,7 +248,7 @@ static int sched_clock_suspend(void) static void sched_clock_resume(void) { - struct clock_read_data *rd = &cd.read_data; + struct clock_read_data *rd = &cd.read_data[0]; rd->epoch_cyc = cd.actual_read_sched_clock(); hrtimer_start(&sched_clock_timer, cd.wrap_kt, HRTIMER_MODE_REL);