Message ID | 20231016123320.9865-2-chrubis@suse.cz (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | Add tst_fd iterator API | expand |
Hello, Good stuff! Reviewed-by: Richard Palethorpe <rpalethorpe@suse.com> Cyril Hrubis <chrubis@suse.cz> writes: > Which allows tests to loop over different types of file descriptors > > Signed-off-by: Cyril Hrubis <chrubis@suse.cz> > --- > include/tst_fd.h | 61 +++++++++ > include/tst_test.h | 1 + > lib/tst_fd.c | 331 +++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 393 insertions(+) > create mode 100644 include/tst_fd.h > create mode 100644 lib/tst_fd.c > > diff --git a/include/tst_fd.h b/include/tst_fd.h > new file mode 100644 > index 000000000..2f15a06c8 > --- /dev/null > +++ b/include/tst_fd.h > @@ -0,0 +1,61 @@ > +// SPDX-License-Identifier: GPL-2.0-or-later > + > +/* > + * Copyright (C) 2023 Cyril Hrubis <chrubis@suse.cz> > + */ > + > +#ifndef TST_FD_H__ > +#define TST_FD_H__ > + > +enum tst_fd_type { > + TST_FD_FILE, > + TST_FD_PATH, > + TST_FD_DIR, > + TST_FD_DEV_ZERO, > + TST_FD_PROC_MAPS, > + TST_FD_PIPE_READ, > + TST_FD_PIPE_WRITE, > + TST_FD_UNIX_SOCK, > + TST_FD_INET_SOCK, > + TST_FD_EPOLL, > + TST_FD_EVENTFD, > + TST_FD_SIGNALFD, > + TST_FD_TIMERFD, > + TST_FD_PIDFD, > + TST_FD_FANOTIFY, > + TST_FD_INOTIFY, > + TST_FD_USERFAULTFD, > + TST_FD_PERF_EVENT, > + TST_FD_IO_URING, > + TST_FD_BPF_MAP, > + TST_FD_FSOPEN, > + TST_FD_FSPICK, > + TST_FD_OPEN_TREE, > + TST_FD_MEMFD, > + TST_FD_MEMFD_SECRET, > + TST_FD_MAX, > +}; > + > +struct tst_fd { > + enum tst_fd_type type; > + int fd; > + /* used by the library, do not touch! */ > + long priv; > +}; > + > +#define TST_FD_INIT {.type = TST_FD_FILE, .fd = -1} > + > +/* > + * Advances the iterator to the next fd type, returns zero at the end. > + */ > +int tst_fd_next(struct tst_fd *fd); > + > +#define TST_FD_FOREACH(fd) \ > + for (struct tst_fd fd = TST_FD_INIT; tst_fd_next(&fd); ) > + > +/* > + * Returns human readable name for the file descriptor type. > + */ > +const char *tst_fd_desc(struct tst_fd *fd); > + > +#endif /* TST_FD_H__ */ > diff --git a/include/tst_test.h b/include/tst_test.h > index 75c2109b9..5eee36bac 100644 > --- a/include/tst_test.h > +++ b/include/tst_test.h > @@ -44,6 +44,7 @@ > #include "tst_taint.h" > #include "tst_memutils.h" > #include "tst_arch.h" > +#include "tst_fd.h" > > /* > * Reports testcase result. > diff --git a/lib/tst_fd.c b/lib/tst_fd.c > new file mode 100644 > index 000000000..3e0a0fe20 > --- /dev/null > +++ b/lib/tst_fd.c > @@ -0,0 +1,331 @@ > +// SPDX-License-Identifier: GPL-2.0-or-later > + > +/* > + * Copyright (C) 2023 Cyril Hrubis <chrubis@suse.cz> > + */ > + > +#define TST_NO_DEFAULT_MAIN > + > +#include <sys/epoll.h> > +#include <sys/eventfd.h> > +#include <sys/signalfd.h> > +#include <sys/timerfd.h> > +#include <sys/fanotify.h> > +#include <sys/inotify.h> > +#include <linux/perf_event.h> > + > +#include "tst_test.h" > +#include "tst_safe_macros.h" > + > +#include "lapi/pidfd.h" > +#include "lapi/io_uring.h" > +#include "lapi/bpf.h" > +#include "lapi/fsmount.h" > + > +#include "tst_fd.h" > + > +struct tst_fd_desc { > + void (*open_fd)(struct tst_fd *fd); > + void (*destroy)(struct tst_fd *fd); > + const char *desc; > +}; > + > +static void open_file(struct tst_fd *fd) > +{ > + fd->fd = SAFE_OPEN("fd_file", O_RDWR | O_CREAT, 0666); > + SAFE_UNLINK("fd_file"); > +} > + > +static void open_path(struct tst_fd *fd) > +{ > + int tfd; > + > + tfd = SAFE_CREAT("fd_file", 0666); > + SAFE_CLOSE(tfd); > + > + fd->fd = SAFE_OPEN("fd_file", O_PATH); > + > + SAFE_UNLINK("fd_file"); > +} > + > +static void open_dir(struct tst_fd *fd) > +{ > + SAFE_MKDIR("fd_dir", 0700); > + fd->fd = SAFE_OPEN("fd_dir", O_DIRECTORY); > + SAFE_RMDIR("fd_dir"); > +} > + > +static void open_dev_zero(struct tst_fd *fd) > +{ > + fd->fd = SAFE_OPEN("/dev/zero", O_RDONLY); > +} > + > +static void open_proc_self_maps(struct tst_fd *fd) > +{ > + fd->fd = SAFE_OPEN("/proc/self/maps", O_RDONLY); > +} > + > +static void open_pipe_read(struct tst_fd *fd) > +{ > + int pipe[2]; > + > + SAFE_PIPE(pipe); > + fd->fd = pipe[0]; > + fd->priv = pipe[1]; > +} > + > +static void open_pipe_write(struct tst_fd *fd) > +{ > + int pipe[2]; > + > + SAFE_PIPE(pipe); > + fd->fd = pipe[1]; > + fd->priv = pipe[0]; > +} > + > +static void destroy_pipe(struct tst_fd *fd) > +{ > + SAFE_CLOSE(fd->priv); > +} > + > +static void open_unix_sock(struct tst_fd *fd) > +{ > + fd->fd = SAFE_SOCKET(AF_UNIX, SOCK_STREAM, 0); > +} > + > +static void open_inet_sock(struct tst_fd *fd) > +{ > + fd->fd = SAFE_SOCKET(AF_INET, SOCK_STREAM, 0); > +} > + > +static void open_epoll(struct tst_fd *fd) > +{ > + fd->fd = epoll_create(1); > + > + if (fd->fd < 0) > + tst_brk(TBROK | TERRNO, "epoll_create()"); > +} > + > +static void open_eventfd(struct tst_fd *fd) > +{ > + fd->fd = eventfd(0, 0); > + > + if (fd->fd < 0) { > + tst_res(TCONF | TERRNO, > + "Skipping %s", tst_fd_desc(fd)); > + } > +} > + > +static void open_signalfd(struct tst_fd *fd) > +{ > + sigset_t sfd_mask; > + sigemptyset(&sfd_mask); > + > + fd->fd = signalfd(-1, &sfd_mask, 0); > + if (fd->fd < 0) { > + tst_res(TCONF | TERRNO, > + "Skipping %s", tst_fd_desc(fd)); > + } > +} > + > +static void open_timerfd(struct tst_fd *fd) > +{ > + fd->fd = timerfd_create(CLOCK_REALTIME, 0); > + if (fd->fd < 0) { > + tst_res(TCONF | TERRNO, > + "Skipping %s", tst_fd_desc(fd)); > + } > +} > + > +static void open_pidfd(struct tst_fd *fd) > +{ > + fd->fd = pidfd_open(getpid(), 0); > + if (fd->fd < 0) > + tst_brk(TBROK | TERRNO, "pidfd_open()"); > +} > + > +static void open_fanotify(struct tst_fd *fd) > +{ > + fd->fd = fanotify_init(FAN_CLASS_NOTIF, O_RDONLY); > + if (fd->fd < 0) { > + tst_res(TCONF | TERRNO, > + "Skipping %s", tst_fd_desc(fd)); > + } > +} > + > +static void open_inotify(struct tst_fd *fd) > +{ > + fd->fd = inotify_init(); > + if (fd->fd < 0) { > + tst_res(TCONF | TERRNO, > + "Skipping %s", tst_fd_desc(fd)); > + } > +} > + > +static void open_userfaultfd(struct tst_fd *fd) > +{ > + fd->fd = syscall(__NR_userfaultfd, 0); > + > + if (fd->fd < 0) { > + tst_res(TCONF | TERRNO, > + "Skipping %s", tst_fd_desc(fd)); > + } > +} > + > +static void open_perf_event(struct tst_fd *fd) > +{ > + struct perf_event_attr pe_attr = { > + .type = PERF_TYPE_SOFTWARE, > + .size = sizeof(struct perf_event_attr), > + .config = PERF_COUNT_SW_CPU_CLOCK, > + .disabled = 1, > + .exclude_kernel = 1, > + .exclude_hv = 1, > + }; > + > + fd->fd = syscall(__NR_perf_event_open, &pe_attr, 0, -1, -1, 0); > + if (fd->fd < 0) { > + tst_res(TCONF | TERRNO, > + "Skipping %s", tst_fd_desc(fd)); > + } > +} > + > +static void open_io_uring(struct tst_fd *fd) > +{ > + struct io_uring_params uring_params = {}; > + > + fd->fd = io_uring_setup(1, &uring_params); > + if (fd->fd < 0) { > + tst_res(TCONF | TERRNO, > + "Skipping %s", tst_fd_desc(fd)); > + } > +} > + > +static void open_bpf_map(struct tst_fd *fd) > +{ > + union bpf_attr array_attr = { > + .map_type = BPF_MAP_TYPE_ARRAY, > + .key_size = 4, > + .value_size = 8, > + .max_entries = 1, > + }; > + > + fd->fd = bpf(BPF_MAP_CREATE, &array_attr, sizeof(array_attr)); > + if (fd->fd < 0) { > + tst_res(TCONF | TERRNO, > + "Skipping %s", tst_fd_desc(fd)); > + } > +} > + > +static void open_fsopen(struct tst_fd *fd) > +{ > + fd->fd = fsopen("ext2", 0); > + if (fd->fd < 0) { > + tst_res(TCONF | TERRNO, > + "Skipping %s", tst_fd_desc(fd)); > + } > +} > + > +static void open_fspick(struct tst_fd *fd) > +{ > + fd->fd = fspick(AT_FDCWD, "/", 0); > + if (fd->fd < 0) { > + tst_res(TCONF | TERRNO, > + "Skipping %s", tst_fd_desc(fd)); > + } > +} > + > +static void open_open_tree(struct tst_fd *fd) > +{ > + fd->fd = open_tree(AT_FDCWD, "/", 0); > + if (fd->fd < 0) { > + tst_res(TCONF | TERRNO, > + "Skipping %s", tst_fd_desc(fd)); > + } > +} > + > +static void open_memfd(struct tst_fd *fd) > +{ > + fd->fd = syscall(__NR_memfd_create, "ltp_memfd", 0); > + if (fd->fd < 0) { > + tst_res(TCONF | TERRNO, > + "Skipping %s", tst_fd_desc(fd)); > + } > +} > + > +static void open_memfd_secret(struct tst_fd *fd) > +{ > + fd->fd = syscall(__NR_memfd_secret, 0); > + if (fd->fd < 0) { > + tst_res(TCONF | TERRNO, > + "Skipping %s", tst_fd_desc(fd)); > + } > +} > + > +static struct tst_fd_desc fd_desc[] = { > + [TST_FD_FILE] = {.open_fd = open_file, .desc = "file"}, > + [TST_FD_PATH] = {.open_fd = open_path, .desc = "O_PATH file"}, > + [TST_FD_DIR] = {.open_fd = open_dir, .desc = "directory"}, > + [TST_FD_DEV_ZERO] = {.open_fd = open_dev_zero, .desc = "/dev/zero"}, > + [TST_FD_PROC_MAPS] = {.open_fd = open_proc_self_maps, .desc = "/proc/self/maps"}, > + [TST_FD_PIPE_READ] = {.open_fd = open_pipe_read, .desc = "pipe read end", .destroy = destroy_pipe}, > + [TST_FD_PIPE_WRITE] = {.open_fd = open_pipe_write, .desc = "pipe write end", .destroy = destroy_pipe}, > + [TST_FD_UNIX_SOCK] = {.open_fd = open_unix_sock, .desc = "unix socket"}, > + [TST_FD_INET_SOCK] = {.open_fd = open_inet_sock, .desc = "inet socket"}, > + [TST_FD_EPOLL] = {.open_fd = open_epoll, .desc = "epoll"}, > + [TST_FD_EVENTFD] = {.open_fd = open_eventfd, .desc = "eventfd"}, > + [TST_FD_SIGNALFD] = {.open_fd = open_signalfd, .desc = "signalfd"}, > + [TST_FD_TIMERFD] = {.open_fd = open_timerfd, .desc = "timerfd"}, > + [TST_FD_PIDFD] = {.open_fd = open_pidfd, .desc = "pidfd"}, > + [TST_FD_FANOTIFY] = {.open_fd = open_fanotify, .desc = "fanotify"}, > + [TST_FD_INOTIFY] = {.open_fd = open_inotify, .desc = "inotify"}, > + [TST_FD_USERFAULTFD] = {.open_fd = open_userfaultfd, .desc = "userfaultfd"}, > + [TST_FD_PERF_EVENT] = {.open_fd = open_perf_event, .desc = "perf event"}, > + [TST_FD_IO_URING] = {.open_fd = open_io_uring, .desc = "io uring"}, > + [TST_FD_BPF_MAP] = {.open_fd = open_bpf_map, .desc = "bpf map"}, > + [TST_FD_FSOPEN] = {.open_fd = open_fsopen, .desc = "fsopen"}, > + [TST_FD_FSPICK] = {.open_fd = open_fspick, .desc = "fspick"}, > + [TST_FD_OPEN_TREE] = {.open_fd = open_open_tree, .desc = "open_tree"}, > + [TST_FD_MEMFD] = {.open_fd = open_memfd, .desc = "memfd"}, > + [TST_FD_MEMFD_SECRET] = {.open_fd = open_memfd_secret, .desc = "memfd secret"}, > +}; > + > +const char *tst_fd_desc(struct tst_fd *fd) > +{ > + if (fd->type >= ARRAY_SIZE(fd_desc)) > + return "invalid"; > + > + return fd_desc[fd->type].desc; > +} > + > +void tst_fd_init(struct tst_fd *fd) > +{ > + fd->type = TST_FD_FILE; > + fd->fd = -1; > +} > + > +int tst_fd_next(struct tst_fd *fd) > +{ > + size_t len = ARRAY_SIZE(fd_desc); > + > + if (fd->fd >= 0) { > + SAFE_CLOSE(fd->fd); > + > + if (fd_desc[fd->type].destroy) > + fd_desc[fd->type].destroy(fd); > + > + fd->type++; > + } > + > + for (;;) { > + if (fd->type >= len) > + return 0; > + > + fd_desc[fd->type].open_fd(fd); > + > + if (fd->fd >= 0) > + return 1; > + > + fd->type++; > + } > +} > -- > 2.41.0
Hi Cyril, > Which allows tests to loop over different types of file descriptors Nice API, thanks! > Signed-off-by: Cyril Hrubis <chrubis@suse.cz> > --- > include/tst_fd.h | 61 +++++++++ > include/tst_test.h | 1 + > lib/tst_fd.c | 331 +++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 393 insertions(+) > create mode 100644 include/tst_fd.h > create mode 100644 lib/tst_fd.c > diff --git a/include/tst_fd.h b/include/tst_fd.h > new file mode 100644 > index 000000000..2f15a06c8 > --- /dev/null > +++ b/include/tst_fd.h > @@ -0,0 +1,61 @@ > +// SPDX-License-Identifier: GPL-2.0-or-later > + > +/* > + * Copyright (C) 2023 Cyril Hrubis <chrubis@suse.cz> > + */ > + > +#ifndef TST_FD_H__ > +#define TST_FD_H__ > + > +enum tst_fd_type { > + TST_FD_FILE, > + TST_FD_PATH, > + TST_FD_DIR, > + TST_FD_DEV_ZERO, > + TST_FD_PROC_MAPS, > + TST_FD_PIPE_READ, > + TST_FD_PIPE_WRITE, > + TST_FD_UNIX_SOCK, > + TST_FD_INET_SOCK, > + TST_FD_EPOLL, > + TST_FD_EVENTFD, > + TST_FD_SIGNALFD, > + TST_FD_TIMERFD, > + TST_FD_PIDFD, > + TST_FD_FANOTIFY, > + TST_FD_INOTIFY, > + TST_FD_USERFAULTFD, > + TST_FD_PERF_EVENT, > + TST_FD_IO_URING, > + TST_FD_BPF_MAP, > + TST_FD_FSOPEN, > + TST_FD_FSPICK, > + TST_FD_OPEN_TREE, > + TST_FD_MEMFD, > + TST_FD_MEMFD_SECRET, > + TST_FD_MAX, > +}; > + > +struct tst_fd { > + enum tst_fd_type type; > + int fd; > + /* used by the library, do not touch! */ > + long priv; > +}; > + > +#define TST_FD_INIT {.type = TST_FD_FILE, .fd = -1} > + > +/* > + * Advances the iterator to the next fd type, returns zero at the end. > + */ > +int tst_fd_next(struct tst_fd *fd); > + > +#define TST_FD_FOREACH(fd) \ > + for (struct tst_fd fd = TST_FD_INIT; tst_fd_next(&fd); ) > + > +/* > + * Returns human readable name for the file descriptor type. > + */ > +const char *tst_fd_desc(struct tst_fd *fd); > + > +#endif /* TST_FD_H__ */ > diff --git a/include/tst_test.h b/include/tst_test.h > index 75c2109b9..5eee36bac 100644 > --- a/include/tst_test.h > +++ b/include/tst_test.h > @@ -44,6 +44,7 @@ > #include "tst_taint.h" > #include "tst_memutils.h" > #include "tst_arch.h" > +#include "tst_fd.h" > /* > * Reports testcase result. > diff --git a/lib/tst_fd.c b/lib/tst_fd.c > new file mode 100644 > index 000000000..3e0a0fe20 > --- /dev/null > +++ b/lib/tst_fd.c > @@ -0,0 +1,331 @@ > +// SPDX-License-Identifier: GPL-2.0-or-later > + > +/* > + * Copyright (C) 2023 Cyril Hrubis <chrubis@suse.cz> > + */ > + > +#define TST_NO_DEFAULT_MAIN > + > +#include <sys/epoll.h> > +#include <sys/eventfd.h> > +#include <sys/signalfd.h> > +#include <sys/timerfd.h> > +#include <sys/fanotify.h> > +#include <sys/inotify.h> > +#include <linux/perf_event.h> > + > +#include "tst_test.h" > +#include "tst_safe_macros.h" > + > +#include "lapi/pidfd.h" > +#include "lapi/io_uring.h" centos stream 9 (glibc 2.34) https://github.com/pevik/ltp/actions/runs/7415994730/job/20180154319 In file included from /usr/include/linux/fs.h:19, from /__w/ltp/ltp/include/lapi/io_uring.h:17, from /__w/ltp/ltp/lib/tst_fd.c:21: /usr/include/x86_64-linux-gnu/sys/mount.h:35:3: error: expected identifier before numeric constant 35 | MS_RDONLY = 1, /* Mount read-only. */ | ^~~~~~~~~ CC lib/tst_fill_file.o make[1]: *** [/__w/ltp/ltp/include/mk/rules.mk:15: tst_fd.o] Error 1 make[1]: *** Waiting for unfinished jobs.... https://sourceware.org/glibc/wiki/Synchronizing_Headers does mention conflict between <linux/mount.h> and <sys/mount.h>, and that's what happen - <linux/fs.h> includes <linux/mount.h>. I send a fix for this which should be applied before the release: https://patchwork.ozlabs.org/project/ltp/patch/20240105002914.1463989-1-pvorel@suse.cz/ It fixes most of the distros: https://github.com/pevik/ltp/actions/runs/7416413061/job/20181348475 But unfortunately it fails on one distro: Ubuntu Bionic (glibc 2.27): https://github.com/pevik/ltp/actions/runs/7416413061/job/20181348475 In file included from ../include/lapi/io_uring.h:17:0, from tst_fd.c:21: /usr/include/x86_64-linux-gnu/sys/mount.h:35:3: error: expected identifier before numeric constant MS_RDONLY = 1, /* Mount read-only. */ ^ ../include/mk/rules.mk:15: recipe for target 'tst_fd.o' failed I'm not sure if we can fix it. Somebody tried to fix it for QEMU: https://lore.kernel.org/qemu-devel/20220802164134.1851910-1-berrange@redhat.com/ which got later deleted due accepted glibc fix: https://lore.kernel.org/qemu-devel/20231109135933.1462615-46-mjt@tls.msk.ru/ Maybe it's time to drop Ubuntu Bionic? We have Leap 42.2, which is the oldest distro we care and it works on it (probably it does not have HAVE_FSOPEN defined). There is yet another error for very old distros ie. old Leap 42.2 (glibc 2.22), probably missing fallback definitions? https://github.com/pevik/ltp/actions/runs/7415994730/job/20180153354 In file included from ../include/lapi/io_uring.h:17:0, from tst_fd.c:21: /usr/include/sys/mount.h:35:3: error: expected identifier before numeric constant MS_RDONLY = 1, /* Mount read-only. */ ^ tst_fd.c: In function 'open_io_uring': tst_fd.c:195:9: warning: missing initializer for field 'sq_entries' of 'struct io_uring_params' [-Wmissing-field-initializers] struct io_uring_params uring_params = {}; ^ In file included from tst_fd.c:21:0: ../include/lapi/io_uring.h:198:11: note: 'sq_entries' declared here uint32_t sq_entries; ^ tst_fd.c: In function 'open_bpf_map': tst_fd.c:208:3: warning: missing initializer for field 'key_size' of 'struct <anonymous>' [-Wmissing-field-initializers] .key_size = 4, ^ In file included from tst_fd.c:22:0: ../include/lapi/bpf.h:185:12: note: 'key_size' declared here uint32_t key_size; /* size of key in bytes */ ^ tst_fd.c:209:3: warning: missing initializer for field 'value_size' of 'struct <anonymous>' [-Wmissing-field-initializers] .value_size = 8, ^ In file included from tst_fd.c:22:0: ../include/lapi/bpf.h:186:12: note: 'value_size' declared here uint32_t value_size; /* size of value in bytes */ ^ tst_fd.c:210:3: warning: missing initializer for field 'max_entries' of 'struct <anonymous>' [-Wmissing-field-initializers] .max_entries = 1, ^ In file included from tst_fd.c:22:0: ../include/lapi/bpf.h:187:12: note: 'max_entries' declared here uint32_t max_entries; /* max number of entries in a map */ ^ tst_fd.c:211:2: warning: missing initializer for field 'map_flags' of 'struct <anonymous>' [-Wmissing-field-initializers] }; ^ In file included from tst_fd.c:22:0: ../include/lapi/bpf.h:188:12: note: 'map_flags' declared here uint32_t map_flags; /* BPF_MAP_CREATE related ^ make[1]: *** [tst_fd.o] Error 1 ../include/mk/rules.mk:15: recipe for target 'tst_fd.o' failed > +#include "lapi/bpf.h" > +#include "lapi/fsmount.h" > + > +#include "tst_fd.h" ... > +static void destroy_pipe(struct tst_fd *fd) > +{ > + SAFE_CLOSE(fd->priv); > +} > + > +static void open_unix_sock(struct tst_fd *fd) > +{ > + fd->fd = SAFE_SOCKET(AF_UNIX, SOCK_STREAM, 0); > +} > + > +static void open_inet_sock(struct tst_fd *fd) > +{ > + fd->fd = SAFE_SOCKET(AF_INET, SOCK_STREAM, 0); > +} > + > +static void open_epoll(struct tst_fd *fd) > +{ > + fd->fd = epoll_create(1); > + > + if (fd->fd < 0) > + tst_brk(TBROK | TERRNO, "epoll_create()"); > +} > + > +static void open_eventfd(struct tst_fd *fd) > +{ > + fd->fd = eventfd(0, 0); > + > + if (fd->fd < 0) { > + tst_res(TCONF | TERRNO, > + "Skipping %s", tst_fd_desc(fd)); Why there is sometimes TCONF? Permissions? I would expect some check which would determine whether TCONF or TBROK. Again, I suppose you'll be able to check, when TST_EXP_FAIL() merged, right? https://lore.kernel.org/ltp/20240103115700.14585-1-chrubis@suse.cz/ If not, some local macro which would wrap error handling would be useful. > + } > +} > + > +static void open_signalfd(struct tst_fd *fd) > +{ > + sigset_t sfd_mask; > + sigemptyset(&sfd_mask); > + > + fd->fd = signalfd(-1, &sfd_mask, 0); > + if (fd->fd < 0) { > + tst_res(TCONF | TERRNO, > + "Skipping %s", tst_fd_desc(fd)); > + } > +} > + > +static void open_timerfd(struct tst_fd *fd) > +{ > + fd->fd = timerfd_create(CLOCK_REALTIME, 0); > + if (fd->fd < 0) { > + tst_res(TCONF | TERRNO, > + "Skipping %s", tst_fd_desc(fd)); > + } > +} > + > +static void open_pidfd(struct tst_fd *fd) > +{ > + fd->fd = pidfd_open(getpid(), 0); > + if (fd->fd < 0) > + tst_brk(TBROK | TERRNO, "pidfd_open()"); > +} > + > +static void open_fanotify(struct tst_fd *fd) > +{ > + fd->fd = fanotify_init(FAN_CLASS_NOTIF, O_RDONLY); FYI we have safe_fanotify_init(), which checks for ENOSYS. > + if (fd->fd < 0) { > + tst_res(TCONF | TERRNO, > + "Skipping %s", tst_fd_desc(fd)); > + } > +} > + > +static void open_inotify(struct tst_fd *fd) > +{ > + fd->fd = inotify_init(); > + if (fd->fd < 0) { > + tst_res(TCONF | TERRNO, > + "Skipping %s", tst_fd_desc(fd)); > + } > +} > + > +static void open_userfaultfd(struct tst_fd *fd) > +{ > + fd->fd = syscall(__NR_userfaultfd, 0); Wouldn't be safe to use tst_syscall() ? > + > + if (fd->fd < 0) { > + tst_res(TCONF | TERRNO, > + "Skipping %s", tst_fd_desc(fd)); > + } > +} ... > + [TST_FD_FSPICK] = {.open_fd = open_fspick, .desc = "fspick"}, > + [TST_FD_OPEN_TREE] = {.open_fd = open_open_tree, .desc = "open_tree"}, > + [TST_FD_MEMFD] = {.open_fd = open_memfd, .desc = "memfd"}, > + [TST_FD_MEMFD_SECRET] = {.open_fd = open_memfd_secret, .desc = "memfd secret"}, > +}; > + > +const char *tst_fd_desc(struct tst_fd *fd) > +{ > + if (fd->type >= ARRAY_SIZE(fd_desc)) > + return "invalid"; Maybe use assert() instead? > + > + return fd_desc[fd->type].desc; > +} > + > +void tst_fd_init(struct tst_fd *fd) This is not in tst_fd.h, thus check complains about not static. > +{ > + fd->type = TST_FD_FILE; > + fd->fd = -1; > +} ... Kind regards, Petr
Hi! > centos stream 9 (glibc 2.34) > https://github.com/pevik/ltp/actions/runs/7415994730/job/20180154319 > In file included from /usr/include/linux/fs.h:19, > from /__w/ltp/ltp/include/lapi/io_uring.h:17, > from /__w/ltp/ltp/lib/tst_fd.c:21: > /usr/include/x86_64-linux-gnu/sys/mount.h:35:3: error: expected identifier before numeric constant > 35 | MS_RDONLY = 1, /* Mount read-only. */ > | ^~~~~~~~~ > CC lib/tst_fill_file.o > make[1]: *** [/__w/ltp/ltp/include/mk/rules.mk:15: tst_fd.o] Error 1 > make[1]: *** Waiting for unfinished jobs.... > > https://sourceware.org/glibc/wiki/Synchronizing_Headers > does mention conflict between <linux/mount.h> and <sys/mount.h>, > and that's what happen - <linux/fs.h> includes <linux/mount.h>. > > I send a fix for this which should be applied before the release: > https://patchwork.ozlabs.org/project/ltp/patch/20240105002914.1463989-1-pvorel@suse.cz/ > > It fixes most of the distros: > https://github.com/pevik/ltp/actions/runs/7416413061/job/20181348475 > > But unfortunately it fails on one distro: Ubuntu Bionic (glibc 2.27): > https://github.com/pevik/ltp/actions/runs/7416413061/job/20181348475 > > In file included from ../include/lapi/io_uring.h:17:0, > from tst_fd.c:21: > /usr/include/x86_64-linux-gnu/sys/mount.h:35:3: error: expected identifier before numeric constant > MS_RDONLY = 1, /* Mount read-only. */ > ^ > ../include/mk/rules.mk:15: recipe for target 'tst_fd.o' failed > > I'm not sure if we can fix it. Somebody tried to fix it for QEMU: > https://lore.kernel.org/qemu-devel/20220802164134.1851910-1-berrange@redhat.com/ > > which got later deleted due accepted glibc fix: > https://lore.kernel.org/qemu-devel/20231109135933.1462615-46-mjt@tls.msk.ru/ > > Maybe it's time to drop Ubuntu Bionic? We have Leap 42.2, which is the oldest > distro we care and it works on it (probably it does not have HAVE_FSOPEN > defined). > > There is yet another error for very old distros ie. old Leap 42.2 (glibc 2.22), > probably missing fallback definitions? > https://github.com/pevik/ltp/actions/runs/7415994730/job/20180153354 > > In file included from ../include/lapi/io_uring.h:17:0, > from tst_fd.c:21: > /usr/include/sys/mount.h:35:3: error: expected identifier before numeric constant > MS_RDONLY = 1, /* Mount read-only. */ > ^ > tst_fd.c: In function 'open_io_uring': > tst_fd.c:195:9: warning: missing initializer for field 'sq_entries' of 'struct io_uring_params' [-Wmissing-field-initializers] > struct io_uring_params uring_params = {}; > ^ > In file included from tst_fd.c:21:0: > ../include/lapi/io_uring.h:198:11: note: 'sq_entries' declared here > uint32_t sq_entries; > ^ > tst_fd.c: In function 'open_bpf_map': > tst_fd.c:208:3: warning: missing initializer for field 'key_size' of 'struct <anonymous>' [-Wmissing-field-initializers] > .key_size = 4, > ^ > In file included from tst_fd.c:22:0: > ../include/lapi/bpf.h:185:12: note: 'key_size' declared here > uint32_t key_size; /* size of key in bytes */ > ^ > tst_fd.c:209:3: warning: missing initializer for field 'value_size' of 'struct <anonymous>' [-Wmissing-field-initializers] > .value_size = 8, > ^ > In file included from tst_fd.c:22:0: > ../include/lapi/bpf.h:186:12: note: 'value_size' declared here > uint32_t value_size; /* size of value in bytes */ > ^ > tst_fd.c:210:3: warning: missing initializer for field 'max_entries' of 'struct <anonymous>' [-Wmissing-field-initializers] > .max_entries = 1, > ^ > In file included from tst_fd.c:22:0: > ../include/lapi/bpf.h:187:12: note: 'max_entries' declared here > uint32_t max_entries; /* max number of entries in a map */ > ^ > tst_fd.c:211:2: warning: missing initializer for field 'map_flags' of 'struct <anonymous>' [-Wmissing-field-initializers] > }; > ^ > In file included from tst_fd.c:22:0: > ../include/lapi/bpf.h:188:12: note: 'map_flags' declared here > uint32_t map_flags; /* BPF_MAP_CREATE related > ^ > make[1]: *** [tst_fd.o] Error 1 > ../include/mk/rules.mk:15: recipe for target 'tst_fd.o' failed Uff, do we still support distros with these header failures? I especailly used the lapi/ headers where possible in order to avoid any compilation failures, if lapi/bpf.h fails it's lapi/bpf.h that is broken though. > > +static void destroy_pipe(struct tst_fd *fd) > > +{ > > + SAFE_CLOSE(fd->priv); > > +} > > + > > +static void open_unix_sock(struct tst_fd *fd) > > +{ > > + fd->fd = SAFE_SOCKET(AF_UNIX, SOCK_STREAM, 0); > > +} > > + > > +static void open_inet_sock(struct tst_fd *fd) > > +{ > > + fd->fd = SAFE_SOCKET(AF_INET, SOCK_STREAM, 0); > > +} > > + > > +static void open_epoll(struct tst_fd *fd) > > +{ > > + fd->fd = epoll_create(1); > > + > > + if (fd->fd < 0) > > + tst_brk(TBROK | TERRNO, "epoll_create()"); > > +} > > + > > +static void open_eventfd(struct tst_fd *fd) > > +{ > > + fd->fd = eventfd(0, 0); > > + > > + if (fd->fd < 0) { > > + tst_res(TCONF | TERRNO, > > + "Skipping %s", tst_fd_desc(fd)); > Why there is sometimes TCONF? Permissions? I would expect some check which would > determine whether TCONF or TBROK. Again, I suppose you'll be able to check, when > TST_EXP_FAIL() merged, right? The TCONF branch is added to the calls that can be disabled in kernel. The CONFIG_EVENTFD can turn off the eventfd() syscall so we can't TBROK here on a failure. > > + } > > +} > > + > > +static void open_signalfd(struct tst_fd *fd) > > +{ > > + sigset_t sfd_mask; > > + sigemptyset(&sfd_mask); > > + > > + fd->fd = signalfd(-1, &sfd_mask, 0); > > + if (fd->fd < 0) { > > + tst_res(TCONF | TERRNO, > > + "Skipping %s", tst_fd_desc(fd)); > > + } > > +} > > + > > +static void open_timerfd(struct tst_fd *fd) > > +{ > > + fd->fd = timerfd_create(CLOCK_REALTIME, 0); > > + if (fd->fd < 0) { > > + tst_res(TCONF | TERRNO, > > + "Skipping %s", tst_fd_desc(fd)); > > + } > > +} > > + > > +static void open_pidfd(struct tst_fd *fd) > > +{ > > + fd->fd = pidfd_open(getpid(), 0); > > + if (fd->fd < 0) > > + tst_brk(TBROK | TERRNO, "pidfd_open()"); > > +} > > + > > +static void open_fanotify(struct tst_fd *fd) > > +{ > > + fd->fd = fanotify_init(FAN_CLASS_NOTIF, O_RDONLY); > FYI we have safe_fanotify_init(), which checks for ENOSYS. But it calls tst_brk() on ENOSYS so we can't use that here. > > + if (fd->fd < 0) { > > + tst_res(TCONF | TERRNO, > > + "Skipping %s", tst_fd_desc(fd)); > > + } > > +} > > + > > +static void open_inotify(struct tst_fd *fd) > > +{ > > + fd->fd = inotify_init(); > > + if (fd->fd < 0) { > > + tst_res(TCONF | TERRNO, > > + "Skipping %s", tst_fd_desc(fd)); > > + } > > +} > > + > > +static void open_userfaultfd(struct tst_fd *fd) > > +{ > > + fd->fd = syscall(__NR_userfaultfd, 0); > Wouldn't be safe to use tst_syscall() ? Again that one calls tst_brk() on ENOSYS, we can't call any of the tst_* or safe_* variants because of that. > > + > > + if (fd->fd < 0) { > > + tst_res(TCONF | TERRNO, > > + "Skipping %s", tst_fd_desc(fd)); > > + } > > +} > > ... > > + [TST_FD_FSPICK] = {.open_fd = open_fspick, .desc = "fspick"}, > > + [TST_FD_OPEN_TREE] = {.open_fd = open_open_tree, .desc = "open_tree"}, > > + [TST_FD_MEMFD] = {.open_fd = open_memfd, .desc = "memfd"}, > > + [TST_FD_MEMFD_SECRET] = {.open_fd = open_memfd_secret, .desc = "memfd secret"}, > > +}; > > + > > +const char *tst_fd_desc(struct tst_fd *fd) > > +{ > > + if (fd->type >= ARRAY_SIZE(fd_desc)) > > + return "invalid"; > Maybe use assert() instead? > > + > > + return fd_desc[fd->type].desc; > > +} > > + > > +void tst_fd_init(struct tst_fd *fd) > This is not in tst_fd.h, thus check complains about not static. Ah, right, this is a leftover that should be removed, will do.
Hi Cyril, > > In file included from tst_fd.c:22:0: > > ../include/lapi/bpf.h:188:12: note: 'map_flags' declared here > > uint32_t map_flags; /* BPF_MAP_CREATE related > > ^ > > make[1]: *** [tst_fd.o] Error 1 > > ../include/mk/rules.mk:15: recipe for target 'tst_fd.o' failed > Uff, do we still support distros with these header failures? Unfortunately yes (SLES 12-SP2, somehow covered in CI by openSUSE Leap 42.2). > I especailly used the lapi/ headers where possible in order to avoid any > compilation failures, if lapi/bpf.h fails it's lapi/bpf.h that is broken > though. ... > > > +static void open_eventfd(struct tst_fd *fd) > > > +{ > > > + fd->fd = eventfd(0, 0); > > > + > > > + if (fd->fd < 0) { > > > + tst_res(TCONF | TERRNO, > > > + "Skipping %s", tst_fd_desc(fd)); > > Why there is sometimes TCONF? Permissions? I would expect some check which would > > determine whether TCONF or TBROK. Again, I suppose you'll be able to check, when > > TST_EXP_FAIL() merged, right? > The TCONF branch is added to the calls that can be disabled in kernel. > The CONFIG_EVENTFD can turn off the eventfd() syscall so we can't TBROK > here on a failure. OK, thx for info! Kind regards, Petr
diff --git a/include/tst_fd.h b/include/tst_fd.h new file mode 100644 index 000000000..2f15a06c8 --- /dev/null +++ b/include/tst_fd.h @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/* + * Copyright (C) 2023 Cyril Hrubis <chrubis@suse.cz> + */ + +#ifndef TST_FD_H__ +#define TST_FD_H__ + +enum tst_fd_type { + TST_FD_FILE, + TST_FD_PATH, + TST_FD_DIR, + TST_FD_DEV_ZERO, + TST_FD_PROC_MAPS, + TST_FD_PIPE_READ, + TST_FD_PIPE_WRITE, + TST_FD_UNIX_SOCK, + TST_FD_INET_SOCK, + TST_FD_EPOLL, + TST_FD_EVENTFD, + TST_FD_SIGNALFD, + TST_FD_TIMERFD, + TST_FD_PIDFD, + TST_FD_FANOTIFY, + TST_FD_INOTIFY, + TST_FD_USERFAULTFD, + TST_FD_PERF_EVENT, + TST_FD_IO_URING, + TST_FD_BPF_MAP, + TST_FD_FSOPEN, + TST_FD_FSPICK, + TST_FD_OPEN_TREE, + TST_FD_MEMFD, + TST_FD_MEMFD_SECRET, + TST_FD_MAX, +}; + +struct tst_fd { + enum tst_fd_type type; + int fd; + /* used by the library, do not touch! */ + long priv; +}; + +#define TST_FD_INIT {.type = TST_FD_FILE, .fd = -1} + +/* + * Advances the iterator to the next fd type, returns zero at the end. + */ +int tst_fd_next(struct tst_fd *fd); + +#define TST_FD_FOREACH(fd) \ + for (struct tst_fd fd = TST_FD_INIT; tst_fd_next(&fd); ) + +/* + * Returns human readable name for the file descriptor type. + */ +const char *tst_fd_desc(struct tst_fd *fd); + +#endif /* TST_FD_H__ */ diff --git a/include/tst_test.h b/include/tst_test.h index 75c2109b9..5eee36bac 100644 --- a/include/tst_test.h +++ b/include/tst_test.h @@ -44,6 +44,7 @@ #include "tst_taint.h" #include "tst_memutils.h" #include "tst_arch.h" +#include "tst_fd.h" /* * Reports testcase result. diff --git a/lib/tst_fd.c b/lib/tst_fd.c new file mode 100644 index 000000000..3e0a0fe20 --- /dev/null +++ b/lib/tst_fd.c @@ -0,0 +1,331 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/* + * Copyright (C) 2023 Cyril Hrubis <chrubis@suse.cz> + */ + +#define TST_NO_DEFAULT_MAIN + +#include <sys/epoll.h> +#include <sys/eventfd.h> +#include <sys/signalfd.h> +#include <sys/timerfd.h> +#include <sys/fanotify.h> +#include <sys/inotify.h> +#include <linux/perf_event.h> + +#include "tst_test.h" +#include "tst_safe_macros.h" + +#include "lapi/pidfd.h" +#include "lapi/io_uring.h" +#include "lapi/bpf.h" +#include "lapi/fsmount.h" + +#include "tst_fd.h" + +struct tst_fd_desc { + void (*open_fd)(struct tst_fd *fd); + void (*destroy)(struct tst_fd *fd); + const char *desc; +}; + +static void open_file(struct tst_fd *fd) +{ + fd->fd = SAFE_OPEN("fd_file", O_RDWR | O_CREAT, 0666); + SAFE_UNLINK("fd_file"); +} + +static void open_path(struct tst_fd *fd) +{ + int tfd; + + tfd = SAFE_CREAT("fd_file", 0666); + SAFE_CLOSE(tfd); + + fd->fd = SAFE_OPEN("fd_file", O_PATH); + + SAFE_UNLINK("fd_file"); +} + +static void open_dir(struct tst_fd *fd) +{ + SAFE_MKDIR("fd_dir", 0700); + fd->fd = SAFE_OPEN("fd_dir", O_DIRECTORY); + SAFE_RMDIR("fd_dir"); +} + +static void open_dev_zero(struct tst_fd *fd) +{ + fd->fd = SAFE_OPEN("/dev/zero", O_RDONLY); +} + +static void open_proc_self_maps(struct tst_fd *fd) +{ + fd->fd = SAFE_OPEN("/proc/self/maps", O_RDONLY); +} + +static void open_pipe_read(struct tst_fd *fd) +{ + int pipe[2]; + + SAFE_PIPE(pipe); + fd->fd = pipe[0]; + fd->priv = pipe[1]; +} + +static void open_pipe_write(struct tst_fd *fd) +{ + int pipe[2]; + + SAFE_PIPE(pipe); + fd->fd = pipe[1]; + fd->priv = pipe[0]; +} + +static void destroy_pipe(struct tst_fd *fd) +{ + SAFE_CLOSE(fd->priv); +} + +static void open_unix_sock(struct tst_fd *fd) +{ + fd->fd = SAFE_SOCKET(AF_UNIX, SOCK_STREAM, 0); +} + +static void open_inet_sock(struct tst_fd *fd) +{ + fd->fd = SAFE_SOCKET(AF_INET, SOCK_STREAM, 0); +} + +static void open_epoll(struct tst_fd *fd) +{ + fd->fd = epoll_create(1); + + if (fd->fd < 0) + tst_brk(TBROK | TERRNO, "epoll_create()"); +} + +static void open_eventfd(struct tst_fd *fd) +{ + fd->fd = eventfd(0, 0); + + if (fd->fd < 0) { + tst_res(TCONF | TERRNO, + "Skipping %s", tst_fd_desc(fd)); + } +} + +static void open_signalfd(struct tst_fd *fd) +{ + sigset_t sfd_mask; + sigemptyset(&sfd_mask); + + fd->fd = signalfd(-1, &sfd_mask, 0); + if (fd->fd < 0) { + tst_res(TCONF | TERRNO, + "Skipping %s", tst_fd_desc(fd)); + } +} + +static void open_timerfd(struct tst_fd *fd) +{ + fd->fd = timerfd_create(CLOCK_REALTIME, 0); + if (fd->fd < 0) { + tst_res(TCONF | TERRNO, + "Skipping %s", tst_fd_desc(fd)); + } +} + +static void open_pidfd(struct tst_fd *fd) +{ + fd->fd = pidfd_open(getpid(), 0); + if (fd->fd < 0) + tst_brk(TBROK | TERRNO, "pidfd_open()"); +} + +static void open_fanotify(struct tst_fd *fd) +{ + fd->fd = fanotify_init(FAN_CLASS_NOTIF, O_RDONLY); + if (fd->fd < 0) { + tst_res(TCONF | TERRNO, + "Skipping %s", tst_fd_desc(fd)); + } +} + +static void open_inotify(struct tst_fd *fd) +{ + fd->fd = inotify_init(); + if (fd->fd < 0) { + tst_res(TCONF | TERRNO, + "Skipping %s", tst_fd_desc(fd)); + } +} + +static void open_userfaultfd(struct tst_fd *fd) +{ + fd->fd = syscall(__NR_userfaultfd, 0); + + if (fd->fd < 0) { + tst_res(TCONF | TERRNO, + "Skipping %s", tst_fd_desc(fd)); + } +} + +static void open_perf_event(struct tst_fd *fd) +{ + struct perf_event_attr pe_attr = { + .type = PERF_TYPE_SOFTWARE, + .size = sizeof(struct perf_event_attr), + .config = PERF_COUNT_SW_CPU_CLOCK, + .disabled = 1, + .exclude_kernel = 1, + .exclude_hv = 1, + }; + + fd->fd = syscall(__NR_perf_event_open, &pe_attr, 0, -1, -1, 0); + if (fd->fd < 0) { + tst_res(TCONF | TERRNO, + "Skipping %s", tst_fd_desc(fd)); + } +} + +static void open_io_uring(struct tst_fd *fd) +{ + struct io_uring_params uring_params = {}; + + fd->fd = io_uring_setup(1, &uring_params); + if (fd->fd < 0) { + tst_res(TCONF | TERRNO, + "Skipping %s", tst_fd_desc(fd)); + } +} + +static void open_bpf_map(struct tst_fd *fd) +{ + union bpf_attr array_attr = { + .map_type = BPF_MAP_TYPE_ARRAY, + .key_size = 4, + .value_size = 8, + .max_entries = 1, + }; + + fd->fd = bpf(BPF_MAP_CREATE, &array_attr, sizeof(array_attr)); + if (fd->fd < 0) { + tst_res(TCONF | TERRNO, + "Skipping %s", tst_fd_desc(fd)); + } +} + +static void open_fsopen(struct tst_fd *fd) +{ + fd->fd = fsopen("ext2", 0); + if (fd->fd < 0) { + tst_res(TCONF | TERRNO, + "Skipping %s", tst_fd_desc(fd)); + } +} + +static void open_fspick(struct tst_fd *fd) +{ + fd->fd = fspick(AT_FDCWD, "/", 0); + if (fd->fd < 0) { + tst_res(TCONF | TERRNO, + "Skipping %s", tst_fd_desc(fd)); + } +} + +static void open_open_tree(struct tst_fd *fd) +{ + fd->fd = open_tree(AT_FDCWD, "/", 0); + if (fd->fd < 0) { + tst_res(TCONF | TERRNO, + "Skipping %s", tst_fd_desc(fd)); + } +} + +static void open_memfd(struct tst_fd *fd) +{ + fd->fd = syscall(__NR_memfd_create, "ltp_memfd", 0); + if (fd->fd < 0) { + tst_res(TCONF | TERRNO, + "Skipping %s", tst_fd_desc(fd)); + } +} + +static void open_memfd_secret(struct tst_fd *fd) +{ + fd->fd = syscall(__NR_memfd_secret, 0); + if (fd->fd < 0) { + tst_res(TCONF | TERRNO, + "Skipping %s", tst_fd_desc(fd)); + } +} + +static struct tst_fd_desc fd_desc[] = { + [TST_FD_FILE] = {.open_fd = open_file, .desc = "file"}, + [TST_FD_PATH] = {.open_fd = open_path, .desc = "O_PATH file"}, + [TST_FD_DIR] = {.open_fd = open_dir, .desc = "directory"}, + [TST_FD_DEV_ZERO] = {.open_fd = open_dev_zero, .desc = "/dev/zero"}, + [TST_FD_PROC_MAPS] = {.open_fd = open_proc_self_maps, .desc = "/proc/self/maps"}, + [TST_FD_PIPE_READ] = {.open_fd = open_pipe_read, .desc = "pipe read end", .destroy = destroy_pipe}, + [TST_FD_PIPE_WRITE] = {.open_fd = open_pipe_write, .desc = "pipe write end", .destroy = destroy_pipe}, + [TST_FD_UNIX_SOCK] = {.open_fd = open_unix_sock, .desc = "unix socket"}, + [TST_FD_INET_SOCK] = {.open_fd = open_inet_sock, .desc = "inet socket"}, + [TST_FD_EPOLL] = {.open_fd = open_epoll, .desc = "epoll"}, + [TST_FD_EVENTFD] = {.open_fd = open_eventfd, .desc = "eventfd"}, + [TST_FD_SIGNALFD] = {.open_fd = open_signalfd, .desc = "signalfd"}, + [TST_FD_TIMERFD] = {.open_fd = open_timerfd, .desc = "timerfd"}, + [TST_FD_PIDFD] = {.open_fd = open_pidfd, .desc = "pidfd"}, + [TST_FD_FANOTIFY] = {.open_fd = open_fanotify, .desc = "fanotify"}, + [TST_FD_INOTIFY] = {.open_fd = open_inotify, .desc = "inotify"}, + [TST_FD_USERFAULTFD] = {.open_fd = open_userfaultfd, .desc = "userfaultfd"}, + [TST_FD_PERF_EVENT] = {.open_fd = open_perf_event, .desc = "perf event"}, + [TST_FD_IO_URING] = {.open_fd = open_io_uring, .desc = "io uring"}, + [TST_FD_BPF_MAP] = {.open_fd = open_bpf_map, .desc = "bpf map"}, + [TST_FD_FSOPEN] = {.open_fd = open_fsopen, .desc = "fsopen"}, + [TST_FD_FSPICK] = {.open_fd = open_fspick, .desc = "fspick"}, + [TST_FD_OPEN_TREE] = {.open_fd = open_open_tree, .desc = "open_tree"}, + [TST_FD_MEMFD] = {.open_fd = open_memfd, .desc = "memfd"}, + [TST_FD_MEMFD_SECRET] = {.open_fd = open_memfd_secret, .desc = "memfd secret"}, +}; + +const char *tst_fd_desc(struct tst_fd *fd) +{ + if (fd->type >= ARRAY_SIZE(fd_desc)) + return "invalid"; + + return fd_desc[fd->type].desc; +} + +void tst_fd_init(struct tst_fd *fd) +{ + fd->type = TST_FD_FILE; + fd->fd = -1; +} + +int tst_fd_next(struct tst_fd *fd) +{ + size_t len = ARRAY_SIZE(fd_desc); + + if (fd->fd >= 0) { + SAFE_CLOSE(fd->fd); + + if (fd_desc[fd->type].destroy) + fd_desc[fd->type].destroy(fd); + + fd->type++; + } + + for (;;) { + if (fd->type >= len) + return 0; + + fd_desc[fd->type].open_fd(fd); + + if (fd->fd >= 0) + return 1; + + fd->type++; + } +}
Which allows tests to loop over different types of file descriptors Signed-off-by: Cyril Hrubis <chrubis@suse.cz> --- include/tst_fd.h | 61 +++++++++ include/tst_test.h | 1 + lib/tst_fd.c | 331 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 393 insertions(+) create mode 100644 include/tst_fd.h create mode 100644 lib/tst_fd.c