Received: by 2002:a05:6a10:22f:0:0:0:0 with SMTP id 15csp2952614pxk; Mon, 21 Sep 2020 01:04:08 -0700 (PDT) X-Google-Smtp-Source: ABdhPJz2TkSsk0wr4TRSsTaqhdpRVQ2xn/3ZjueNuvbsYDAjVZVGlQyEpsTaFcA2LRfTL01AOVj6 X-Received: by 2002:a17:906:838f:: with SMTP id p15mr50647892ejx.315.1600675448493; Mon, 21 Sep 2020 01:04:08 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1600675448; cv=none; d=google.com; s=arc-20160816; b=AZcy7JvN8Qae0FH/odVt5/6pzSPYBivTOC0nenHafSouMkORt59gfA6jcbBIvS4Y4m jqx3a5qK4F4VADC1LFyt7ZJOOKzGUe2c8Joa7fY0F6IjFTqU/zYtJtQHBq+sWrEWVmPE uxLJPWmH0z1ROR7hQMcNUdGNQb/EJP+NXF7xRx7EfU1aGJaf++kSvatomUGtK21FmSo8 N5MqCEblbZwawoE/jMO+V4TAuIyVp2MrymfSmuAOrFGoPA2Ud3JAJt4k+Wk/EF1Lcny5 T/0Q9iKXBXBVSyRnPNLZ12ryEGxC8aIJxc03gJmI/jggnuyrt1tomQZ4cjg00ZHgufKj mr8A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from; bh=wklogt6hkQa2bTKF7wW7IeIQd5UCgKazpwDExjH0WKo=; b=xm2LlGFAEDdRGZl2XDEwVRMUxhdviBKA5bh3NZTD6l+hJpIeADbKTIrHyszeODmbj2 ZpdMYeyD4hSf6lG2tG8gF8Uq4U0wrjHYatbheEv9VKs/Pa5AHcpZKEh4KpBfXGnRGryA 8oEKdyQSnLr35azbJes5CTpee1qiEpwngGod+vCfyUMV04kFoyFu9l1zE1xSb7rIUE8A 9CSXLcAGrh2WWWSOwGmAj9qY2GhqMbZgBxhRIapjBFTSCH/Dm9OCmqfmYXSFrdSh/CYq CQ8v9M4XkkKektY3rpmUMPSLJe0ZxL38JvLfmXehXPrpsNsOacBuuSHpcF0NSBfFbqjw 0t3Q== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id h19si8022833edq.571.2020.09.21.01.03.45; Mon, 21 Sep 2020 01:04:08 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726868AbgIUIBq (ORCPT + 99 others); Mon, 21 Sep 2020 04:01:46 -0400 Received: from mx2.suse.de ([195.135.220.15]:56892 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726211AbgIUH7V (ORCPT ); Mon, 21 Sep 2020 03:59:21 -0400 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.221.27]) by mx2.suse.de (Postfix) with ESMTP id 4774AB506; Mon, 21 Sep 2020 07:59:54 +0000 (UTC) From: Nicolai Stange To: "Theodore Y. Ts'o" Cc: linux-crypto@vger.kernel.org, LKML , Arnd Bergmann , Greg Kroah-Hartman , "Eric W. Biederman" , "Alexander E. Patrakov" , "Ahmed S. Darwish" , Willy Tarreau , Matthew Garrett , Vito Caputo , Andreas Dilger , Jan Kara , Ray Strode , William Jon McCann , zhangjs , Andy Lutomirski , Florian Weimer , Lennart Poettering , Peter Matthias , Marcelo Henrique Cerri , Roman Drahtmueller , Neil Horman , Randy Dunlap , Julia Lawall , Dan Carpenter , Andy Lavr , Eric Biggers , "Jason A. Donenfeld" , =?UTF-8?q?Stephan=20M=C3=BCller?= , Torsten Duwe , Petr Tesarik , Nicolai Stange Subject: [RFC PATCH 09/41] random: protect ->entropy_count with the pool spinlock Date: Mon, 21 Sep 2020 09:58:25 +0200 Message-Id: <20200921075857.4424-10-nstange@suse.de> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200921075857.4424-1-nstange@suse.de> References: <20200921075857.4424-1-nstange@suse.de> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Currently, all updates to ->entropy_count are synchronized by means of cmpxchg-retry loops found in credit_entropy_bits(), __credit_entropy_bits_fast() and account() respectively. However, all but one __credit_entropy_bits_fast() call sites grap the pool ->lock already and it would be nice if the potentially costly cmpxchg could be avoided in these performance critical paths. In addition to that, future patches will introduce new fields to struct entropy_store which will required some kinf of synchronization with ->entropy_count updates from said producer paths as well. Protect ->entropy_count with the pool ->lock. - Make callers of __credit_entropy_bits_fast() invoke it with the pool ->lock held. Extend existing critical sections where possible. Drop the cmpxchg-reply loop in __credit_entropy_bits_fast() in favor of a plain assignment. - Retain the retry loop in credit_entropy_bits(): the potentially expensive pool_entropy_delta() should not be called under the lock in order to not unnecessarily block contenders. In order to continue to synchronize with __credit_entropy_bits_fast() and account(), the cmpxchg gets replaced by a plain comparison + store with the ->lock being held. - Make account() grab the ->lock and drop the cmpxchg-retry loop in favor of a plain assignent. Signed-off-by: Nicolai Stange --- drivers/char/random.c | 44 +++++++++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/drivers/char/random.c b/drivers/char/random.c index d9e4dd27d45d..9f87332b158f 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -718,7 +718,7 @@ static unsigned int pool_entropy_delta(struct entropy_store *r, * Credit the entropy store with n bits of entropy. * To be used from hot paths when it is either known that nbits is * smaller than one half of the pool size or losing anything beyond that - * doesn't matter. + * doesn't matter. Must be called with r->lock being held. */ static bool __credit_entropy_bits_fast(struct entropy_store *r, int nbits) { @@ -727,13 +727,11 @@ static bool __credit_entropy_bits_fast(struct entropy_store *r, int nbits) if (!nbits) return false; -retry: - orig = READ_ONCE(r->entropy_count); + orig = r->entropy_count; entropy_count = orig + pool_entropy_delta(r, orig, nbits << ENTROPY_SHIFT, true); - if (cmpxchg(&r->entropy_count, orig, entropy_count) != orig) - goto retry; + WRITE_ONCE(r->entropy_count, entropy_count); trace_credit_entropy_bits(r->name, nbits, entropy_count >> ENTROPY_SHIFT, _RET_IP_); @@ -755,17 +753,28 @@ static bool __credit_entropy_bits_fast(struct entropy_store *r, int nbits) static void credit_entropy_bits(struct entropy_store *r, int nbits) { int entropy_count, orig; + unsigned long flags; if (!nbits) return; retry: + /* + * Don't run the potentially expensive pool_entropy_delta() + * calculations under the spinlock. Instead retry until + * ->entropy_count becomes stable. + */ orig = READ_ONCE(r->entropy_count); entropy_count = orig + pool_entropy_delta(r, orig, nbits << ENTROPY_SHIFT, false); - if (cmpxchg(&r->entropy_count, orig, entropy_count) != orig) + spin_lock_irqsave(&r->lock, flags); + if (r->entropy_count != orig) { + spin_unlock_irqrestore(&r->lock, flags); goto retry; + } + WRITE_ONCE(r->entropy_count, entropy_count); + spin_unlock_irqrestore(&r->lock, flags); trace_credit_entropy_bits(r->name, nbits, entropy_count >> ENTROPY_SHIFT, _RET_IP_); @@ -1203,12 +1212,11 @@ static void add_timer_randomness(struct timer_rand_state *state, unsigned num) } sample; long delta, delta2, delta3; bool reseed; + unsigned long flags; sample.jiffies = jiffies; sample.cycles = random_get_entropy(); sample.num = num; - r = &input_pool; - mix_pool_bytes(r, &sample, sizeof(sample)); /* * Calculate number of bits of randomness we probably added. @@ -1235,12 +1243,16 @@ static void add_timer_randomness(struct timer_rand_state *state, unsigned num) if (delta > delta3) delta = delta3; + r = &input_pool; + spin_lock_irqsave(&r->lock, flags); + __mix_pool_bytes(r, &sample, sizeof(sample)); /* * delta is now minimum absolute delta. * Round down by 1 bit on general principles, * and limit entropy estimate to 12 bits. */ reseed = __credit_entropy_bits_fast(r, min_t(int, fls(delta>>1), 11)); + spin_unlock_irqrestore(&r->lock, flags); if (reseed) crng_reseed(&primary_crng, r); } @@ -1358,12 +1370,12 @@ void add_interrupt_randomness(int irq, int irq_flags) __mix_pool_bytes(r, &seed, sizeof(seed)); credit = 1; } - spin_unlock(&r->lock); fast_pool->count = 0; /* award one bit for the contents of the fast pool */ reseed = __credit_entropy_bits_fast(r, credit + 1); + spin_unlock(&r->lock); if (reseed) crng_reseed(&primary_crng, r); } @@ -1393,14 +1405,15 @@ EXPORT_SYMBOL_GPL(add_disk_randomness); */ static size_t account(struct entropy_store *r, size_t nbytes, int min) { - int entropy_count, orig, have_bytes; + int entropy_count, have_bytes; size_t ibytes, nfrac; + unsigned long flags; BUG_ON(r->entropy_count > r->poolinfo->poolfracbits); + spin_lock_irqsave(&r->lock, flags); /* Can we pull enough? */ -retry: - entropy_count = orig = READ_ONCE(r->entropy_count); + entropy_count = r->entropy_count; ibytes = nbytes; /* never pull more than available */ have_bytes = entropy_count >> (ENTROPY_SHIFT + 3); @@ -1420,8 +1433,8 @@ static size_t account(struct entropy_store *r, size_t nbytes, int min) else entropy_count = 0; - if (cmpxchg(&r->entropy_count, orig, entropy_count) != orig) - goto retry; + WRITE_ONCE(r->entropy_count, entropy_count); + spin_unlock_irqrestore(&r->lock, flags); trace_debit_entropy(r->name, 8 * ibytes); if (ibytes && ENTROPY_BITS(r) < random_write_wakeup_bits) { @@ -1639,8 +1652,11 @@ EXPORT_SYMBOL(get_random_bytes); static void entropy_timer(struct timer_list *t) { bool reseed; + unsigned long flags; + spin_lock_irqsave(&input_pool.lock, flags); reseed = __credit_entropy_bits_fast(&input_pool, 1); + spin_unlock_irqrestore(&input_pool.lock, flags); if (reseed) crng_reseed(&primary_crng, &input_pool); } -- 2.26.2