From nobody Thu May 16 06:00:17 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org ARC-Seal: i=1; a=rsa-sha256; t=1594820033; cv=none; d=zohomail.com; s=zohoarc; b=f+5GPmwTdJWOXQTe8p0A4UjGhUI9PSzc2cc7PF4p+Z/vKZsffB8hQ1lHVyS1sVIa3c/XFJmqVDO4H2TOh3l59UkLk6ux7CcMfxllAQvuGj3YwlPWJs5JZe9D7XhvCYmGiV+QYNd74QVWvQFsWwBCNeZ0N/GrHGV8TUPp/NXiG/w= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1594820033; h=Content-Transfer-Encoding:Cc:Date:From:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Sender:Subject:To; bh=8v9QBxUVqait/fzMGc/0HD5fR6pGAqJlExMk1AxiP8s=; b=L+nTDExfVGK5TiczuEULAXDqbhWdK3IpPT4tAPAIFZGg4ZqSGTEcxapchA0BXrWmzB5nhGtom+3ujkFc4UuC6IlYwd8Mpm6PlzP8Vh+ECvp81sSbBWSMA5ZsMrBpMZksr1C4oB5FTnQL5WiD9IV0Q2WfHc83XWiy9C1uKkXBbes= ARC-Authentication-Results: i=1; mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1594820033483666.905601896981; Wed, 15 Jul 2020 06:33:53 -0700 (PDT) Received: from localhost ([::1]:36904 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jvhXj-0002a8-I8 for importer@patchew.org; Wed, 15 Jul 2020 09:33:51 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:33780) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1jvhX7-0002Ah-5F for qemu-devel@nongnu.org; Wed, 15 Jul 2020 09:33:13 -0400 Received: from [195.135.220.15] (port=39854 helo=mx2.suse.de) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1jvhX3-0001fY-St for qemu-devel@nongnu.org; Wed, 15 Jul 2020 09:33:12 -0400 Received: from relay2.suse.de (unknown [195.135.221.27]) by mx2.suse.de (Postfix) with ESMTP id 6696CAD89; Wed, 15 Jul 2020 13:33:11 +0000 (UTC) X-Virus-Scanned: by amavisd-new at test-mx.suse.de From: mwilck@suse.com To: "Michael S. Tsirkin" , Jason Wang Subject: [PATCH v2] virtio-rng: return available data with O_NONBLOCK Date: Wed, 15 Jul 2020 15:32:55 +0200 Message-Id: <20200715133255.10526-1-mwilck@suse.com> X-Mailer: git-send-email 2.26.2 MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Host-Lookup-Failed: Reverse DNS lookup failed for 195.135.220.15 (failed) Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=195.135.220.15; envelope-from=mwilck@suse.com; helo=mx2.suse.de X-detected-operating-system: by eggs.gnu.org: First seen = 2020/07/14 23:03:40 X-ACL-Warn: Detected OS = Linux 2.2.x-3.x (no timestamps) [generic] X-Spam_score_int: -33 X-Spam_score: -3.4 X-Spam_bar: --- X-Spam_report: (-3.4 / 5.0 requ) BAYES_00=-1.9, RCVD_IN_DNSWL_MED=-2.3, RCVD_IN_MSPIKE_H3=-0.01, RCVD_IN_MSPIKE_WL=-0.01, RDNS_NONE=0.793, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Martin Wilck , qemu-devel@nongnu.org, virtualization@lists.linux-foundation.org Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Type: text/plain; charset="utf-8" From: Martin Wilck If a program opens /dev/hwrng with O_NONBLOCK and uses poll() and non-blocking read() to retrieve random data, it ends up in a tight loop with poll() always returning POLLIN and read() returning EAGAIN. This repeats forever until some process makes a blocking read() call. The reason is that virtio_read() always returns 0 in non-blocking mode, even if data is available. Worse, it fetches random data from the hypervisor after every non-blocking call, without ever using this data. The following test program illustrates the behavior and can be used for testing and experiments. The problem will only be seen if all tasks use non-blocking access; otherwise the blocking reads will "recharge" the random pool and cause other, non-blocking reads to succeed at least sometimes. /* Whether to use non-blocking mode in a task, problem occurs if CONDITION = is 1 */ //#define CONDITION (getpid() % 2 !=3D 0) static volatile sig_atomic_t stop; static void handler(int sig __attribute__((unused))) { stop =3D 1; } static void loop(int fd, int sec) { struct pollfd pfd =3D { .fd =3D fd, .events =3D POLLIN, }; unsigned long errors =3D 0, eagains =3D 0, bytes =3D 0, succ =3D 0; int size, rc, rd; srandom(getpid()); if (CONDITION && fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK) =3D= =3D -1) perror("fcntl"); size =3D MINBUFSIZ + random() % (MAXBUFSIZ - MINBUFSIZ + 1); for(;;) { char buf[size]; if (stop) break; rc =3D poll(&pfd, 1, sec); if (rc > 0) { rd =3D read(fd, buf, sizeof(buf)); if (rd =3D=3D -1 && errno =3D=3D EAGAIN) eagains++; else if (rd =3D=3D -1) errors++; else { succ++; bytes +=3D rd; write(1, buf, sizeof(buf)); } } else if (rc =3D=3D -1) { if (errno !=3D EINTR) perror("poll"); break; } else fprintf(stderr, "poll: timeout\n"); } fprintf(stderr, "pid %d %sblocking, bufsize %d, %d seconds, %lu bytes read, %lu success, = %lu eagain, %lu errors\n", getpid(), CONDITION ? "non-" : "", size, sec, bytes, succ, eagains, error= s); } int main(void) { int fd; fork(); fork(); fd =3D open("/dev/hwrng", O_RDONLY); if (fd =3D=3D -1) { perror("open"); return 1; }; signal(SIGALRM, handler); alarm(SECONDS); loop(fd, SECONDS); close(fd); wait(NULL); return 0; } void loop(int fd) { struct pollfd pfd0 =3D { .fd =3D fd, .events =3D POLLIN, }; int rc; unsigned int n; for (n =3D LOOPS; n > 0; n--) { struct pollfd pfd =3D pfd0; char buf[SIZE]; rc =3D poll(&pfd, 1, 1); if (rc > 0) { int rd =3D read(fd, buf, sizeof(buf)); if (rd =3D=3D -1) perror("read"); else printf("read %d bytes\n", rd); } else if (rc =3D=3D -1) perror("poll"); else fprintf(stderr, "timeout\n"); } } int main(void) { int fd; fd =3D open("/dev/hwrng", O_RDONLY|O_NONBLOCK); if (fd =3D=3D -1) { perror("open"); return 1; }; loop(fd); close(fd); return 0; } This can be observed in the real word e.g. with nested qemu/KVM virtual machines, if both the "outer" and "inner" VMs have a virtio-rng device. If the "inner" VM requests random data, qemu running in the "outer" VM uses this device in a non-blocking manner like the test program above. Fix it by returning available data if a previous hypervisor call has completed in the meantime. I tested the patch with the program above, and with rng-tools. Signed-off-by: Martin Wilck --- drivers/char/hw_random/virtio-rng.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/v= irtio-rng.c index 79a6e47b5fbc..984713b35892 100644 --- a/drivers/char/hw_random/virtio-rng.c +++ b/drivers/char/hw_random/virtio-rng.c @@ -59,6 +59,20 @@ static int virtio_read(struct hwrng *rng, void *buf, siz= e_t size, bool wait) if (vi->hwrng_removed) return -ENODEV; =20 + /* + * If the previous call was non-blocking, we may have got some + * randomness already. + */ + if (vi->busy && completion_done(&vi->have_data)) { + unsigned int len; + + vi->busy =3D false; + len =3D vi->data_avail > size ? size : vi->data_avail; + vi->data_avail -=3D len; + if (len) + return len; + } + if (!vi->busy) { vi->busy =3D true; reinit_completion(&vi->have_data); --=20 2.26.2