From patchwork Thu Jun 21 15:17:55 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Timur Tabi X-Patchwork-Id: 10480023 X-Patchwork-Delegate: agross@codeaurora.org Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 099D260532 for ; Thu, 21 Jun 2018 15:18:05 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id EE613291D6 for ; Thu, 21 Jun 2018 15:18:04 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id E360729172; Thu, 21 Jun 2018 15:18:04 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.8 required=2.0 tests=BAYES_00,DKIM_SIGNED, MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI, T_DKIM_INVALID autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 7D30629168 for ; Thu, 21 Jun 2018 15:18:04 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S933268AbeFUPSC (ORCPT ); Thu, 21 Jun 2018 11:18:02 -0400 Received: from smtp.codeaurora.org ([198.145.29.96]:54954 "EHLO smtp.codeaurora.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932847AbeFUPSB (ORCPT ); Thu, 21 Jun 2018 11:18:01 -0400 Received: by smtp.codeaurora.org (Postfix, from userid 1000) id 1781D60AFF; Thu, 21 Jun 2018 15:18:01 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=codeaurora.org; s=default; t=1529594281; bh=sDl8k6lHWQkQscldb4BI5/lpVxCdVvu2WK31W5HYWDg=; h=From:To:Cc:Subject:Date:From; b=aWDYdxM1JjwbA75IldU1QB8YwQ/4RLcPRJ69g9DCEWuWdQIvFbCUZnIQPtdSv/lhr a2r+6MOKFCEdCM8iM1oR7lwX7hhu6qvEmguDnhyGDFejKGSA77YIINNF9YdLGi780s 9iCzadCML9TaRHN3fWjQHbTX+gbIcgA/0Yl/Bzu0= Received: from timur-ubuntu.qualcomm.com (i-global254.qualcomm.com [199.106.103.254]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-SHA256 (128/128 bits)) (No client certificate requested) (Authenticated sender: timur@smtp.codeaurora.org) by smtp.codeaurora.org (Postfix) with ESMTPSA id 8A2D660275; Thu, 21 Jun 2018 15:17:59 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=codeaurora.org; s=default; t=1529594280; bh=sDl8k6lHWQkQscldb4BI5/lpVxCdVvu2WK31W5HYWDg=; h=From:To:Cc:Subject:Date:From; b=J4aJq2WE/Zs264Ko9WPXAg/1zTuMKUPEEzLgJ6z8/njg4oZFNJuvKUAalhF4fweff BTW2j8yrEPv4c096HjzoFJAzACgMgw+5CX11BIHZb8X4O8DjW6gZiutFNI/I+dbKwy 1X9aFzcM0rTYFRTE+n9eFoeTIGWyvzfjisGBENlE= DMARC-Filter: OpenDMARC Filter v1.3.2 smtp.codeaurora.org 8A2D660275 Authentication-Results: pdx-caf-mail.web.codeaurora.org; dmarc=none (p=none dis=none) header.from=codeaurora.org Authentication-Results: pdx-caf-mail.web.codeaurora.org; spf=none smtp.mailfrom=timur@codeaurora.org From: Timur Tabi To: linux-arm-kernel@lists.infradead.org, linux-crypto@vger.kernel.org, vinod.koul@linaro.org, Matt Mackall , swboyd@chromium.org, linux-arm-msm@vger.kernel.org, timur@kernel.org Cc: timur@codeaurora.org Subject: [PATCH 1/2] hwrng: msm: add a spinlock and support for blocking reads Date: Thu, 21 Jun 2018 10:17:55 -0500 Message-Id: <1529594276-12210-1-git-send-email-timur@codeaurora.org> X-Mailer: git-send-email 1.9.1 Sender: linux-arm-msm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-arm-msm@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP The hwrng.read callback includes a boolean parameter called 'wait' which indicates whether the function should block and wait for more data. When 'wait' is true, the driver spins on the DATA_AVAIL bit or until a reasonable timeout. The timeout can occur if there is a heavy load on reading the PRNG. The same code also needs a spinlock to protect against race conditions. If multiple cores hammer on the PRNG, it's possible for a race condition to occur between reading the status register and reading the data register. Add a spinlock to protect against that. 1. Core 1 reads status register, shows data is available. 2. Core 2 also reads status register, same result 3. Core 2 reads data register, depleting all entropy 4. Core 1 reads data register, which returns 0 Signed-off-by: Timur Tabi --- drivers/char/hw_random/msm-rng.c | 57 +++++++++++++++++++++++++++++++++++----- 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/drivers/char/hw_random/msm-rng.c b/drivers/char/hw_random/msm-rng.c index 841fee845ec9..44580588b938 100644 --- a/drivers/char/hw_random/msm-rng.c +++ b/drivers/char/hw_random/msm-rng.c @@ -15,9 +15,11 @@ #include #include #include +#include #include #include #include +#include /* Device specific register offsets */ #define PRNG_DATA_OUT 0x0000 @@ -35,10 +37,22 @@ #define MAX_HW_FIFO_SIZE (MAX_HW_FIFO_DEPTH * 4) #define WORD_SZ 4 +/* + * Normally, this would be the maximum time it takes to refill the FIFO, + * after a read. Under heavy load, tests show that this delay is either + * below 50us or above 2200us. The higher value is probably what happens + * when entropy is completely depleted. + * + * Since we don't want to wait 2ms in a spinlock, set the timeout to the + * lower value. Under extreme situations, that timeout can extend to 100us. + */ +#define TIMEOUT 50 + struct msm_rng { void __iomem *base; struct clk *clk; struct hwrng hwrng; + spinlock_t lock; }; #define to_msm_rng(p) container_of(p, struct msm_rng, hwrng) @@ -96,11 +110,39 @@ static int msm_rng_read(struct hwrng *hwrng, void *data, size_t max, bool wait) /* read random data from hardware */ do { - val = readl_relaxed(rng->base + PRNG_STATUS); - if (!(val & PRNG_STATUS_DATA_AVAIL)) - break; + spin_lock(&rng->lock); + + /* + * First check the status bit. If 'wait' is true, then wait + * up to TIMEOUT microseconds for data to be available. + */ + if (wait) { + int ret; + + ret = readl_poll_timeout_atomic(rng->base + PRNG_STATUS, + val, val & PRNG_STATUS_DATA_AVAIL, 0, TIMEOUT); + if (ret) { + /* Timed out */ + spin_unlock(&rng->lock); + break; + } + } else { + val = readl_relaxed(rng->base + PRNG_STATUS); + if (!(val & PRNG_STATUS_DATA_AVAIL)) { + spin_unlock(&rng->lock); + break; + } + } val = readl_relaxed(rng->base + PRNG_DATA_OUT); + spin_unlock(&rng->lock); + + /* + * Zero is technically a valid random number, but it's also + * the value returned if the PRNG is not enabled properly. + * To avoid accidentally returning all zeros, treat it as + * invalid and just return what we've already read. + */ if (!val) break; @@ -148,10 +190,11 @@ static int msm_rng_probe(struct platform_device *pdev) if (IS_ERR(rng->clk)) return PTR_ERR(rng->clk); - rng->hwrng.name = KBUILD_MODNAME, - rng->hwrng.init = msm_rng_init, - rng->hwrng.cleanup = msm_rng_cleanup, - rng->hwrng.read = msm_rng_read, + rng->hwrng.name = KBUILD_MODNAME; + rng->hwrng.init = msm_rng_init; + rng->hwrng.cleanup = msm_rng_cleanup; + rng->hwrng.read = msm_rng_read; + spin_lock_init(&rng->lock); ret = devm_hwrng_register(&pdev->dev, &rng->hwrng); if (ret) {