Received: by 2002:ad5:474a:0:0:0:0:0 with SMTP id i10csp3488004imu; Mon, 28 Jan 2019 05:51:14 -0800 (PST) X-Google-Smtp-Source: ALg8bN56/Gs2h5IganneLRrPH25lNbpI9nSe4Vd7uprqnALOpMnSPvCGo7vyToM3RDusl8QPF9qi X-Received: by 2002:a17:902:4601:: with SMTP id o1mr21846715pld.243.1548683474067; Mon, 28 Jan 2019 05:51:14 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1548683474; cv=none; d=google.com; s=arc-20160816; b=qVI0IZoubmLtr5Has3Gbdlu/iqNnBmJEMFTlkUjfproDRhN2d8KPXtCEvx/+RssLg2 uptx2Wooj/Sv/KCoYHMKRrthJAUlWdY0Wczd3vBZBkwIySRLPQZbvE+jM7x95Eui3QxC yM7gEUMYoGMAwibfUCBmO/ZP1zqza5eSw8yQI6kCrEEDd8xqKfS8CZT34zphQ13yE0/X BwFzi8/w+FHDfiSNuo2trn35fOaJO6ggugC8WNThbcBPRRhvK6bYD22KZ17X0s4BcTA8 zkxWVo+0oNNKQ6p25MdVpWbyPWok0Vzyhar5FLLUiPbk5vdvGT8btZP4XSRa3tDeM0MK Am0A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :message-id:date:subject:cc:to:from; bh=Vakv3OTUghxnZMPagsTlDiD7LEklNX9zLIFkQ4RBn7s=; b=SLowcewaTNYJ3ihDfqaVikoviLga/EJqXWAGMgCYTftfzav9S4SdG+x1oCgnCrFnZs frS1lv49ogamV5k+MM9Po/5n50tZgCpEIw4C/kEUP58ot+Dz9C1hY7zufND2qzJNIHs9 uhLnQSJwLcRy3UNoHcgCZ50xwGCbox/9WSSjz5AEVjKM6Mui1TXFkljBOn//0YKFqiMC zksxP6tkqx2UbPk3OPPZpwcTU6Or7j2l67PF2jf75M9z/fOeFEZMA7DqfXvnhuL5AzMa +qNwkRboIhr1nxrL2ErUsE3SEEdykOF7+ZyR6mA5jq46zOPQE4FEF2O3ZaD4G3KbmeIW PQFw== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id q64si30603373pga.280.2019.01.28.05.50.58; Mon, 28 Jan 2019 05:51:14 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726887AbfA1NuH (ORCPT + 99 others); Mon, 28 Jan 2019 08:50:07 -0500 Received: from mx2.suse.de ([195.135.220.15]:46594 "EHLO mx1.suse.de" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1726668AbfA1NuG (ORCPT ); Mon, 28 Jan 2019 08:50:06 -0500 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx1.suse.de (Postfix) with ESMTP id 851EDACBF; Mon, 28 Jan 2019 13:50:03 +0000 (UTC) From: Cyril Hrubis To: ltp@lists.linux.it Cc: Cyril Hrubis , Jiri Kosina , Linux-MM , kernel list , Linux API Subject: [PATCH] syscalls/preadv203: Add basic RWF_NOWAIT test Date: Mon, 28 Jan 2019 14:46:56 +0100 Message-Id: <20190128134656.27979-1-metan@ucw.cz> X-Mailer: git-send-email 2.19.2 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Cyril Hrubis We are attempting to trigger the EAGAIN path for the RWF_NOWAIT flag. In order to do so the test runs three threads: * nowait_reader: reads from a random offset from a random file with RWF_NOWAIT flag and expects to get EAGAIN and short read sooner or later * writer_thread: rewrites random file in order to keep the underlying device bussy so that pages evicted from cache cannot be faulted immediatelly * cache_dropper: attempts to evict pages from a cache in order for reader to hit evicted page sooner or later Signed-off-by: Cyril Hrubis CC: Jiri Kosina CC: Linux-MM CC: kernel list CC: Linux API --- I was wondering if we can do a better job at flushing the caches. Is there an interface for flusing caches just for the device we are using for the test? Also the RWF_NOWAIT should probably be benchmarked as well but that is completely out of scope for LTP. runtest/syscalls | 2 + testcases/kernel/syscalls/preadv2/.gitignore | 2 + testcases/kernel/syscalls/preadv2/Makefile | 4 + testcases/kernel/syscalls/preadv2/preadv203.c | 266 ++++++++++++++++++ 4 files changed, 274 insertions(+) create mode 100644 testcases/kernel/syscalls/preadv2/preadv203.c diff --git a/runtest/syscalls b/runtest/syscalls index 34b47f36b..a69c431f1 100644 --- a/runtest/syscalls +++ b/runtest/syscalls @@ -853,6 +853,8 @@ preadv201 preadv201 preadv201_64 preadv201_64 preadv202 preadv202 preadv202_64 preadv202_64 +preadv203 preadv203 +preadv203_64 preadv203_64 profil01 profil01 diff --git a/testcases/kernel/syscalls/preadv2/.gitignore b/testcases/kernel/syscalls/preadv2/.gitignore index 759d9ef5b..98b81abea 100644 --- a/testcases/kernel/syscalls/preadv2/.gitignore +++ b/testcases/kernel/syscalls/preadv2/.gitignore @@ -2,3 +2,5 @@ /preadv201_64 /preadv202 /preadv202_64 +/preadv203 +/preadv203_64 diff --git a/testcases/kernel/syscalls/preadv2/Makefile b/testcases/kernel/syscalls/preadv2/Makefile index fc1fbf3c7..fbedd0287 100644 --- a/testcases/kernel/syscalls/preadv2/Makefile +++ b/testcases/kernel/syscalls/preadv2/Makefile @@ -11,4 +11,8 @@ include $(abs_srcdir)/../utils/newer_64.mk %_64: CPPFLAGS += -D_FILE_OFFSET_BITS=64 +preadv203: CFLAGS += -pthread +preadv203_64: CFLAGS += -pthread +preadv203_64: LDFLAGS += -pthread + include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/syscalls/preadv2/preadv203.c b/testcases/kernel/syscalls/preadv2/preadv203.c new file mode 100644 index 000000000..a6d5300f9 --- /dev/null +++ b/testcases/kernel/syscalls/preadv2/preadv203.c @@ -0,0 +1,266 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2019 Cyril Hrubis + */ + +/* + * This is a basic functional test for RWF_NOWAIT flag, we are attempting to + * force preadv2() either to return a short read or EAGAIN with three + * concurelntly running threads: + * + * nowait_reader: reads from a random offset from a random file with + * RWF_NOWAIT flag and expects to get EAGAIN and short + * read sooner or later + * + * writer_thread: rewrites random file in order to keep the underlying device + * bussy so that pages evicted from cache cannot be faulted + * immediatelly + * + * cache_dropper: attempts to evict pages from a cache in order for reader to + * hit evicted page sooner or later + */ + +/* + * If test fails with EOPNOTSUPP you have likely hit a glibc bug: + * + * https://sourceware.org/bugzilla/show_bug.cgi?id=23579 + * + * Which can be worked around by calling preadv2() directly by syscall() such as: + * + * static ssize_t sys_preadv2(int fd, const struct iovec *iov, int iovcnt, + * off_t offset, int flags) + * { + * return syscall(SYS_preadv2, fd, iov, iovcnt, offset, offset>>32, flags); + * } + * + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include + +#include "tst_test.h" +#include "tst_safe_pthread.h" +#include "lapi/preadv2.h" + +#define CHUNK_SZ 4123 +#define CHUNKS 60 +#define MNTPOINT "mntpoint" +#define FILES 1000 + +static int fds[FILES]; + +static volatile int stop; + +static void drop_caches(void) +{ + SAFE_FILE_PRINTF("/proc/sys/vm/drop_caches", "3"); +} + +/* + * All files are divided in chunks each filled with the same bytes starting with + * '0' at offset 0 and with increasing value on each next chunk. + * + * 000....000111....111.......AAA......AAA... + * | chunk0 || chunk1 | ... | chunk17 | + */ +static int verify_short_read(struct iovec *iov, size_t iov_cnt, + off_t off, size_t size) +{ + unsigned int i; + size_t j, checked = 0; + + for (i = 0; i < iov_cnt; i++) { + char *buf = iov[i].iov_base; + for (j = 0; j < iov[i].iov_len; j++) { + char exp_val = '0' + (off + checked)/CHUNK_SZ; + + if (exp_val != buf[j]) { + tst_res(TFAIL, + "Wrong value read pos %zu size %zu %c (%i) %c (%i)!", + checked, size, exp_val, exp_val, + isprint(buf[j]) ? buf[j] : ' ', buf[j]); + return 1; + } + + if (++checked >= size) + return 0; + } + } + + return 0; +} + +static void *nowait_reader(void *unused LTP_ATTRIBUTE_UNUSED) +{ + char buf1[CHUNK_SZ/2]; + char buf2[CHUNK_SZ]; + unsigned int full_read_cnt = 0, eagain_cnt = 0; + unsigned int short_read_cnt = 0, zero_read_cnt = 0; + + struct iovec rd_iovec[] = { + {buf1, sizeof(buf1)}, + {buf2, sizeof(buf2)}, + }; + + while (!stop) { + if (eagain_cnt >= 100 && short_read_cnt >= 10) + stop = 1; + + /* Ensure short reads doesn't happen because of tripping on EOF */ + off_t off = random() % ((CHUNKS - 2) * CHUNK_SZ); + int fd = fds[random() % FILES]; + + TEST(preadv2(fd, rd_iovec, 2, off, RWF_NOWAIT)); + + if (TST_RET < 0) { + if (TST_ERR != EAGAIN) + tst_brk(TBROK | TTERRNO, "preadv2() failed"); + + eagain_cnt++; + continue; + } + + + if (TST_RET == 0) { + zero_read_cnt++; + continue; + } + + if (TST_RET != CHUNK_SZ + CHUNK_SZ/2) { + verify_short_read(rd_iovec, 2, off, TST_RET); + short_read_cnt++; + continue; + } + + full_read_cnt++; + } + + tst_res(TINFO, + "Number of full_reads %u, short reads %u, zero len reads %u, EAGAIN(s) %u", + full_read_cnt, short_read_cnt, zero_read_cnt, eagain_cnt); + + return (void*)(long)eagain_cnt; +} + +static void *writer_thread(void *unused) +{ + char buf[CHUNK_SZ]; + unsigned int j, write_cnt = 0; + + struct iovec wr_iovec[] = { + {buf, sizeof(buf)}, + }; + + while (!stop) { + int fd = fds[random() % FILES]; + + for (j = 0; j < CHUNKS; j++) { + memset(buf, '0' + j, sizeof(buf)); + + off_t off = CHUNK_SZ * j; + + if (pwritev(fd, wr_iovec, 1, off) < 0) { + if (errno == EBADF) { + tst_res(TBROK | TERRNO, "FDs closed?"); + return unused; + } + + tst_brk(TBROK | TERRNO, "pwritev()"); + } + + write_cnt++; + } + } + + tst_res(TINFO, "Number of writes %u", write_cnt); + + return unused; +} + +static void *cache_dropper(void *unused) +{ + unsigned int drop_cnt = 0; + + while (!stop) { + drop_caches(); + drop_cnt++; + } + + tst_res(TINFO, "Cache dropped %u times", drop_cnt); + + return unused; +} + +static void verify_preadv2(void) +{ + pthread_t reader, dropper, writer; + unsigned int max_runtime = 600; + void *eagains; + + stop = 0; + + drop_caches(); + + SAFE_PTHREAD_CREATE(&dropper, NULL, cache_dropper, NULL); + SAFE_PTHREAD_CREATE(&reader, NULL, nowait_reader, NULL); + SAFE_PTHREAD_CREATE(&writer, NULL, writer_thread, NULL); + + while (!stop && max_runtime-- > 0) + usleep(100000); + + stop = 1; + + SAFE_PTHREAD_JOIN(reader, &eagains); + SAFE_PTHREAD_JOIN(dropper, NULL); + SAFE_PTHREAD_JOIN(writer, NULL); + + if (eagains) + tst_res(TPASS, "Got some EAGAIN"); + else + tst_res(TFAIL, "Haven't got EAGAIN"); +} + +static void setup(void) +{ + char path[1024]; + char buf[CHUNK_SZ]; + unsigned int i; + char j; + + for (i = 0; i < FILES; i++) { + snprintf(path, sizeof(path), MNTPOINT"/file_%i", i); + + fds[i] = SAFE_OPEN(path, O_RDWR | O_CREAT, 0644); + + for (j = 0; j < CHUNKS; j++) { + memset(buf, '0' + j, sizeof(buf)); + SAFE_WRITE(1, fds[i], buf, sizeof(buf)); + } + } +} + +static void do_cleanup(void) +{ + unsigned int i; + + for (i = 0; i < FILES; i++) { + if (fds[i] > 0) + SAFE_CLOSE(fds[i]); + } +} + +TST_DECLARE_ONCE_FN(cleanup, do_cleanup); + +static struct tst_test test = { + .setup = setup, + .cleanup = cleanup, + .test_all = verify_preadv2, + .mntpoint = MNTPOINT, + .all_filesystems = 1, + .needs_tmpdir = 1, +}; -- 2.19.2