From patchwork Tue Jun 27 16:57:19 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?0L3QsNCx?= X-Patchwork-Id: 13294818 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 894E8EB64D9 for ; Tue, 27 Jun 2023 16:57:25 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230040AbjF0Q5Y (ORCPT ); Tue, 27 Jun 2023 12:57:24 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:38242 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229501AbjF0Q5X (ORCPT ); Tue, 27 Jun 2023 12:57:23 -0400 Received: from tarta.nabijaczleweli.xyz (unknown [139.28.40.42]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 55B2B10CF; Tue, 27 Jun 2023 09:57:21 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=nabijaczleweli.xyz; s=202305; t=1687885040; bh=Az1W0LBoRrz7YW1u9PmFCtcve2J0qXsV3SeB7GMF0ps=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=bto/E5jTK1/420ZGrPZSSsY7qNHL2Z4JofFpTcdv8BdbJ+gJiiCCEUR5qmewifHGR EXfbSHI4Sim9KaLIvMpOjl4sYnvq60jlPUBMwHFJMVipYt+9Jl8UddMJP/76rptfsG yKNsWalfXH9ybZdnEUjHKUaCB/kYmzuxCT8GrMbOJbrX8O32o6UeA0qNjeh7ERVIE3 KHP6YQTUxQ4dQ2orb8/ui+zqpwhuRqNxCku+ubFMzgUy1krQC7mcuOhClDz5olyt1f 6qdKCJc8RA+NHF0zdsqWDJ/lBJYlZtnfhs7o+mplYOXuo7nUCtlL4zhD+ZZbpHllfN 2/G4JOL0ApGnA== Received: from tarta.nabijaczleweli.xyz (unknown [192.168.1.250]) by tarta.nabijaczleweli.xyz (Postfix) with ESMTPSA id 9AE391A92; Tue, 27 Jun 2023 18:57:20 +0200 (CEST) Date: Tue, 27 Jun 2023 18:57:19 +0200 From: Ahelenia =?utf-8?q?Ziemia=C5=84ska?= To: Amir Goldstein Cc: Alexander Viro , Christian Brauner , linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Jan Kara , Chung-Chiang Cheng , ltp@vger.kernel.org Subject: [LTP PATCH] inotify13: new test for fs/splice.c functions vs pipes vs inotify Message-ID: <44neh3sog5jaskc4zy6lwnld7hussp5sslx4fun47fr45mxe3a@q2jgkjwlq74f> References: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: User-Agent: NeoMutt/20230517 Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org The only one that passes on 6.1.27-1 is sendfile_file_to_pipe. Link: https://lore.kernel.org/linux-fsdevel/jbyihkyk5dtaohdwjyivambb2gffyjs3dodpofafnkkunxq7bu@jngkdxx65pux/t/#u Signed-off-by: Ahelenia Ziemiańska --- Formatted to clang-format defaults. Put the original Fixes:ed SHA in the metadata, that's probably fine, right? testcases/kernel/syscalls/inotify/.gitignore | 1 + testcases/kernel/syscalls/inotify/inotify13.c | 246 ++++++++++++++++++ 2 files changed, 247 insertions(+) create mode 100644 testcases/kernel/syscalls/inotify/inotify13.c diff --git a/testcases/kernel/syscalls/inotify/.gitignore b/testcases/kernel/syscalls/inotify/.gitignore index f6e5c546a..b597ea63f 100644 --- a/testcases/kernel/syscalls/inotify/.gitignore +++ b/testcases/kernel/syscalls/inotify/.gitignore @@ -10,3 +10,4 @@ /inotify10 /inotify11 /inotify12 +/inotify13 diff --git a/testcases/kernel/syscalls/inotify/inotify13.c b/testcases/kernel/syscalls/inotify/inotify13.c new file mode 100644 index 000000000..c34f1dc9f --- /dev/null +++ b/testcases/kernel/syscalls/inotify/inotify13.c @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/*\ + * Verify splice-family functions (and sendfile) generate IN_ACCESS + * for what they read and IN_MODIFY for what they write. + * + * Regression test for 983652c69199 and + * https://lore.kernel.org/linux-fsdevel/jbyihkyk5dtaohdwjyivambb2gffyjs3dodpofafnkkunxq7bu@jngkdxx65pux/t/#u + */ + +#define _GNU_SOURCE +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tst_test.h" +#include "tst_safe_macros.h" +#include "inotify.h" + +#if defined(HAVE_SYS_INOTIFY_H) +#include + + +static int pipes[2] = {-1, -1}; +static int inotify = -1; +static int memfd = -1; +static int data_pipes[2] = {-1, -1}; + +static void watch_rw(int fd) { + char buf[64]; + sprintf(buf, "/proc/self/fd/%d", fd); + SAFE_MYINOTIFY_ADD_WATCH(inotify, buf, IN_ACCESS | IN_MODIFY); +} + +static int compar(const void *l, const void *r) { + const struct inotify_event *lie = l; + const struct inotify_event *rie = r; + return lie->wd - rie->wd; +} + +static void get_events(size_t evcnt, struct inotify_event evs[static evcnt]) { + struct inotify_event tail, *itr = evs; + for (size_t left = evcnt; left; --left) + SAFE_READ(true, inotify, itr++, sizeof(struct inotify_event)); + + TEST(read(inotify, &tail, sizeof(struct inotify_event))); + if (TST_RET != -1) + tst_brk(TFAIL, "expect %zu events", evcnt); + if (TST_ERR != EAGAIN) + tst_brk(TFAIL | TTERRNO, "expected EAGAIN"); + + qsort(evs, evcnt, sizeof(struct inotify_event), compar); +} + +static void expect_event(struct inotify_event *ev, int wd, uint32_t mask) { + if (ev->wd != wd) + tst_brk(TFAIL, "expect event for wd %d got %d", wd, ev->wd); + if (ev->mask != mask) + tst_brk(TFAIL, "expect event with mask %" PRIu32 " got %" PRIu32 "", mask, + ev->mask); +} + +#define F2P(splice) \ + SAFE_WRITE(SAFE_WRITE_RETRY, memfd, __func__, sizeof(__func__)); \ + SAFE_LSEEK(memfd, 0, SEEK_SET); \ + watch_rw(memfd); \ + watch_rw(pipes[0]); \ + TEST(splice); \ + if (TST_RET == -1) \ + tst_brk(TBROK | TERRNO, #splice); \ + if (TST_RET != sizeof(__func__)) \ + tst_brk(TBROK, #splice ": %" PRId64 "", TST_RET); \ + \ + /*expecting: IN_ACCESS memfd, IN_MODIFY pipes[0]*/ \ + struct inotify_event events[2]; \ + get_events(ARRAY_SIZE(events), events); \ + expect_event(events + 0, 1, IN_ACCESS); \ + expect_event(events + 1, 2, IN_MODIFY); \ + \ + char buf[sizeof(__func__)]; \ + SAFE_READ(true, pipes[0], buf, sizeof(__func__)); \ + if (memcmp(buf, __func__, sizeof(__func__))) \ + tst_brk(TFAIL, "buf contents bad"); +static void splice_file_to_pipe(void) { + F2P(splice(memfd, NULL, pipes[1], NULL, 128 * 1024 * 1024, 0)); +} +static void sendfile_file_to_pipe(void) { + F2P(sendfile(pipes[1], memfd, NULL, 128 * 1024 * 1024)); +} + +static void splice_pipe_to_file(void) { + SAFE_WRITE(SAFE_WRITE_RETRY, pipes[1], __func__, sizeof(__func__)); + watch_rw(pipes[0]); + watch_rw(memfd); + TEST(splice(pipes[0], NULL, memfd, NULL, 128 * 1024 * 1024, 0)); + if(TST_RET == -1) + tst_brk(TBROK | TERRNO, "splice"); + if(TST_RET != sizeof(__func__)) + tst_brk(TBROK, "splice: %" PRId64 "", TST_RET); + + // expecting: IN_ACCESS pipes[0], IN_MODIFY memfd + struct inotify_event events[2]; + get_events(ARRAY_SIZE(events), events); + expect_event(events + 0, 1, IN_ACCESS); + expect_event(events + 1, 2, IN_MODIFY); + + char buf[sizeof(__func__)]; + SAFE_LSEEK(memfd, 0, SEEK_SET); + SAFE_READ(true, memfd, buf, sizeof(__func__)); + if (memcmp(buf, __func__, sizeof(__func__))) + tst_brk(TFAIL, "buf contents bad"); +} + +#define P2P(splice) \ + SAFE_WRITE(SAFE_WRITE_RETRY, data_pipes[1], __func__, sizeof(__func__)); \ + watch_rw(data_pipes[0]); \ + watch_rw(pipes[1]); \ + TEST(splice); \ + if (TST_RET == -1) \ + tst_brk(TBROK | TERRNO, #splice); \ + if (TST_RET != sizeof(__func__)) \ + tst_brk(TBROK, #splice ": %" PRId64 "", TST_RET); \ + \ + /* expecting: IN_ACCESS data_pipes[0], IN_MODIFY pipes[1] */ \ + struct inotify_event events[2]; \ + get_events(ARRAY_SIZE(events), events); \ + expect_event(events + 0, 1, IN_ACCESS); \ + expect_event(events + 1, 2, IN_MODIFY); \ + \ + char buf[sizeof(__func__)]; \ + SAFE_READ(true, pipes[0], buf, sizeof(__func__)); \ + if (memcmp(buf, __func__, sizeof(__func__))) \ + tst_brk(TFAIL, "buf contents bad"); +static void splice_pipe_to_pipe(void) { + P2P(splice(data_pipes[0], NULL, pipes[1], NULL, 128 * 1024 * 1024, 0)); +} +static void tee_pipe_to_pipe(void) { + P2P(tee(data_pipes[0], pipes[1], 128 * 1024 * 1024, 0)); +} + +static char vmsplice_pipe_to_mem_dt[32 * 1024]; +static void vmsplice_pipe_to_mem(void) { + memcpy(vmsplice_pipe_to_mem_dt, __func__, sizeof(__func__)); + watch_rw(pipes[0]); + TEST(vmsplice(pipes[1], + &(struct iovec){.iov_base = vmsplice_pipe_to_mem_dt, + .iov_len = sizeof(vmsplice_pipe_to_mem_dt)}, + 1, SPLICE_F_GIFT)); + if (TST_RET == -1) + tst_brk(TBROK | TERRNO, "vmsplice"); + if (TST_RET != sizeof(vmsplice_pipe_to_mem_dt)) + tst_brk(TBROK, "vmsplice: %" PRId64 "", TST_RET); + + // expecting: IN_MODIFY pipes[0] + struct inotify_event event; + get_events(1, &event); + expect_event(&event, 1, IN_MODIFY); + + char buf[sizeof(__func__)]; + SAFE_READ(true, pipes[0], buf, sizeof(__func__)); + if (memcmp(buf, __func__, sizeof(__func__))) + tst_brk(TFAIL, "buf contents bad"); +} + +static void vmsplice_mem_to_pipe(void) { + char buf[sizeof(__func__)]; + SAFE_WRITE(SAFE_WRITE_RETRY, pipes[1], __func__, sizeof(__func__)); + watch_rw(pipes[1]); + TEST(vmsplice(pipes[0], + &(struct iovec){.iov_base = buf, .iov_len = sizeof(buf)}, 1, + 0)); + if (TST_RET == -1) + tst_brk(TBROK | TERRNO, "vmsplice"); + if (TST_RET != sizeof(buf)) + tst_brk(TBROK, "vmsplice: %" PRId64 "", TST_RET); + + // expecting: IN_ACCESS pipes[1] + struct inotify_event event; + get_events(1, &event); + expect_event(&event, 1, IN_ACCESS); + if (memcmp(buf, __func__, sizeof(__func__))) + tst_brk(TFAIL, "buf contents bad"); +} + +typedef void (*tests_f)(void); +#define TEST_F(f) { f, #f } +static const struct { + tests_f f; + const char *n; +} tests[] = { + TEST_F(splice_file_to_pipe), TEST_F(sendfile_file_to_pipe), + TEST_F(splice_pipe_to_file), TEST_F(splice_pipe_to_pipe), + TEST_F(tee_pipe_to_pipe), TEST_F(vmsplice_pipe_to_mem), + TEST_F(vmsplice_mem_to_pipe), +}; + +static void run_test(unsigned int n) +{ + tst_res(TINFO, "%s", tests[n].n); + + SAFE_PIPE2(pipes, O_CLOEXEC); + SAFE_PIPE2(data_pipes, O_CLOEXEC); + inotify = SAFE_MYINOTIFY_INIT1(IN_NONBLOCK | IN_CLOEXEC); + if((memfd = memfd_create(__func__, MFD_CLOEXEC)) == -1) + tst_brk(TCONF | TERRNO, "memfd"); + tests[n].f(); + tst_res(TPASS, "ок"); +} + +static void cleanup(void) +{ + if (memfd != -1) + SAFE_CLOSE(memfd); + if (inotify != -1) + SAFE_CLOSE(inotify); + if (pipes[0] != -1) + SAFE_CLOSE(pipes[0]); + if (pipes[1] != -1) + SAFE_CLOSE(pipes[1]); + if (data_pipes[0] != -1) + SAFE_CLOSE(data_pipes[0]); + if (data_pipes[1] != -1) + SAFE_CLOSE(data_pipes[1]); +} + +static struct tst_test test = { + .max_runtime = 10, + .cleanup = cleanup, + .test = run_test, + .tcnt = ARRAY_SIZE(tests), + .tags = (const struct tst_tag[]) { + {"linux-git", "983652c69199"}, + {} + }, +}; + +#else + TST_TEST_TCONF("system doesn't have required inotify support"); +#endif