Skip to content

Commit

Permalink
aio: implement io_pgetevents
Browse files Browse the repository at this point in the history
This is the io_getevents equivalent of ppoll/pselect and allows to
properly mix signals and aio completions (especially with IOCB_CMD_POLL)
and atomically executes the following sequence:

	sigset_t origmask;

	pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
	ret = io_getevents(ctx, min_nr, nr, events, timeout);
	pthread_sigmask(SIG_SETMASK, &origmask, NULL);

Note that unlike many other signal related calls we do not pass a sigmask
size, as that would get us to 7 arguments, which aren't easily supported
by the syscall infrastructure.  It seems a lot less painful to just add a
new syscall variant in the unlikely case we're going to increase the
sigset size.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
  • Loading branch information
Christoph Hellwig committed May 2, 2018
1 parent a3c0d43 commit 7a074e9
Show file tree
Hide file tree
Showing 8 changed files with 130 additions and 11 deletions.
1 change: 1 addition & 0 deletions arch/x86/entry/syscalls/syscall_32.tbl
Expand Up @@ -396,3 +396,4 @@
382 i386 pkey_free sys_pkey_free __ia32_sys_pkey_free
383 i386 statx sys_statx __ia32_sys_statx
384 i386 arch_prctl sys_arch_prctl __ia32_compat_sys_arch_prctl
385 i386 io_pgetevents sys_io_pgetevents __ia32_compat_sys_io_pgetevents
1 change: 1 addition & 0 deletions arch/x86/entry/syscalls/syscall_64.tbl
Expand Up @@ -341,6 +341,7 @@
330 common pkey_alloc __x64_sys_pkey_alloc
331 common pkey_free __x64_sys_pkey_free
332 common statx __x64_sys_statx
333 common io_pgetevents __x64_sys_io_pgetevents

#
# x32-specific system call numbers start at 512 to avoid cache impact
Expand Down
114 changes: 104 additions & 10 deletions fs/aio.c
Expand Up @@ -1303,10 +1303,6 @@ static long read_events(struct kioctx *ctx, long min_nr, long nr,
wait_event_interruptible_hrtimeout(ctx->wait,
aio_read_events(ctx, min_nr, nr, event, &ret),
until);

if (!ret && signal_pending(current))
ret = -EINTR;

return ret;
}

Expand Down Expand Up @@ -1921,13 +1917,60 @@ SYSCALL_DEFINE5(io_getevents, aio_context_t, ctx_id,
struct timespec __user *, timeout)
{
struct timespec64 ts;
int ret;

if (timeout && unlikely(get_timespec64(&ts, timeout)))
return -EFAULT;

ret = do_io_getevents(ctx_id, min_nr, nr, events, timeout ? &ts : NULL);
if (!ret && signal_pending(current))
ret = -EINTR;
return ret;
}

if (timeout) {
if (unlikely(get_timespec64(&ts, timeout)))
SYSCALL_DEFINE6(io_pgetevents,
aio_context_t, ctx_id,
long, min_nr,
long, nr,
struct io_event __user *, events,
struct timespec __user *, timeout,
const struct __aio_sigset __user *, usig)
{
struct __aio_sigset ksig = { NULL, };
sigset_t ksigmask, sigsaved;
struct timespec64 ts;
int ret;

if (timeout && unlikely(get_timespec64(&ts, timeout)))
return -EFAULT;

if (usig && copy_from_user(&ksig, usig, sizeof(ksig)))
return -EFAULT;

if (ksig.sigmask) {
if (ksig.sigsetsize != sizeof(sigset_t))
return -EINVAL;
if (copy_from_user(&ksigmask, ksig.sigmask, sizeof(ksigmask)))
return -EFAULT;
sigdelsetmask(&ksigmask, sigmask(SIGKILL) | sigmask(SIGSTOP));
sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved);
}

ret = do_io_getevents(ctx_id, min_nr, nr, events, timeout ? &ts : NULL);
if (signal_pending(current)) {
if (ksig.sigmask) {
current->saved_sigmask = sigsaved;
set_restore_sigmask();
}

if (!ret)
ret = -ERESTARTNOHAND;
} else {
if (ksig.sigmask)
sigprocmask(SIG_SETMASK, &sigsaved, NULL);
}

return do_io_getevents(ctx_id, min_nr, nr, events, timeout ? &ts : NULL);
return ret;
}

#ifdef CONFIG_COMPAT
Expand All @@ -1938,13 +1981,64 @@ COMPAT_SYSCALL_DEFINE5(io_getevents, compat_aio_context_t, ctx_id,
struct compat_timespec __user *, timeout)
{
struct timespec64 t;
int ret;

if (timeout && compat_get_timespec64(&t, timeout))
return -EFAULT;

ret = do_io_getevents(ctx_id, min_nr, nr, events, timeout ? &t : NULL);
if (!ret && signal_pending(current))
ret = -EINTR;
return ret;
}


if (timeout) {
if (compat_get_timespec64(&t, timeout))
struct __compat_aio_sigset {
compat_sigset_t __user *sigmask;
compat_size_t sigsetsize;
};

COMPAT_SYSCALL_DEFINE6(io_pgetevents,
compat_aio_context_t, ctx_id,
compat_long_t, min_nr,
compat_long_t, nr,
struct io_event __user *, events,
struct compat_timespec __user *, timeout,
const struct __compat_aio_sigset __user *, usig)
{
struct __compat_aio_sigset ksig = { NULL, };
sigset_t ksigmask, sigsaved;
struct timespec64 t;
int ret;

if (timeout && compat_get_timespec64(&t, timeout))
return -EFAULT;

if (usig && copy_from_user(&ksig, usig, sizeof(ksig)))
return -EFAULT;

if (ksig.sigmask) {
if (ksig.sigsetsize != sizeof(compat_sigset_t))
return -EINVAL;
if (get_compat_sigset(&ksigmask, ksig.sigmask))
return -EFAULT;
sigdelsetmask(&ksigmask, sigmask(SIGKILL) | sigmask(SIGSTOP));
sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved);
}

ret = do_io_getevents(ctx_id, min_nr, nr, events, timeout ? &t : NULL);
if (signal_pending(current)) {
if (ksig.sigmask) {
current->saved_sigmask = sigsaved;
set_restore_sigmask();
}
if (!ret)
ret = -ERESTARTNOHAND;
} else {
if (ksig.sigmask)
sigprocmask(SIG_SETMASK, &sigsaved, NULL);
}

return do_io_getevents(ctx_id, min_nr, nr, events, timeout ? &t : NULL);
return ret;
}
#endif
7 changes: 7 additions & 0 deletions include/linux/compat.h
Expand Up @@ -330,6 +330,7 @@ extern int put_compat_rusage(const struct rusage *,
struct compat_rusage __user *);

struct compat_siginfo;
struct __compat_aio_sigset;

struct compat_dirent {
u32 d_ino;
Expand Down Expand Up @@ -553,6 +554,12 @@ asmlinkage long compat_sys_io_getevents(compat_aio_context_t ctx_id,
compat_long_t nr,
struct io_event __user *events,
struct compat_timespec __user *timeout);
asmlinkage long compat_sys_io_pgetevents(compat_aio_context_t ctx_id,
compat_long_t min_nr,
compat_long_t nr,
struct io_event __user *events,
struct compat_timespec __user *timeout,
const struct __compat_aio_sigset __user *usig);

/* fs/cookies.c */
asmlinkage long compat_sys_lookup_dcookie(u32, u32, char __user *, compat_size_t);
Expand Down
6 changes: 6 additions & 0 deletions include/linux/syscalls.h
Expand Up @@ -290,6 +290,12 @@ asmlinkage long sys_io_getevents(aio_context_t ctx_id,
long nr,
struct io_event __user *events,
struct timespec __user *timeout);
asmlinkage long sys_io_pgetevents(aio_context_t ctx_id,
long min_nr,
long nr,
struct io_event __user *events,
struct timespec __user *timeout,
const struct __aio_sigset *sig);

/* fs/xattr.c */
asmlinkage long sys_setxattr(const char __user *path, const char __user *name,
Expand Down
4 changes: 3 additions & 1 deletion include/uapi/asm-generic/unistd.h
Expand Up @@ -732,9 +732,11 @@ __SYSCALL(__NR_pkey_alloc, sys_pkey_alloc)
__SYSCALL(__NR_pkey_free, sys_pkey_free)
#define __NR_statx 291
__SYSCALL(__NR_statx, sys_statx)
#define __NR_io_pgetevents 292
__SC_COMP(__NR_io_pgetevents, sys_io_pgetevents, compat_sys_io_pgetevents)

#undef __NR_syscalls
#define __NR_syscalls 292
#define __NR_syscalls 293

/*
* 32 bit systems traditionally used different
Expand Down
6 changes: 6 additions & 0 deletions include/uapi/linux/aio_abi.h
Expand Up @@ -29,6 +29,7 @@

#include <linux/types.h>
#include <linux/fs.h>
#include <linux/signal.h>
#include <asm/byteorder.h>

typedef __kernel_ulong_t aio_context_t;
Expand Down Expand Up @@ -108,5 +109,10 @@ struct iocb {
#undef IFBIG
#undef IFLITTLE

struct __aio_sigset {
sigset_t __user *sigmask;
size_t sigsetsize;
};

#endif /* __LINUX__AIO_ABI_H */

2 changes: 2 additions & 0 deletions kernel/sys_ni.c
Expand Up @@ -43,7 +43,9 @@ COND_SYSCALL(io_submit);
COND_SYSCALL_COMPAT(io_submit);
COND_SYSCALL(io_cancel);
COND_SYSCALL(io_getevents);
COND_SYSCALL(io_pgetevents);
COND_SYSCALL_COMPAT(io_getevents);
COND_SYSCALL_COMPAT(io_pgetevents);

/* fs/xattr.c */

Expand Down

1 comment on commit 7a074e9

@MilhouseVH
Copy link

@MilhouseVH MilhouseVH commented on 7a074e9 Jun 21, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hope you don't mind this ping.

Building gcc-8.1.0 for an x86_64 toolchain fails to build against the 4.18-rc1 kernel due to the following error:

In file included from /home/neil/projects/scratch/alternates/LibreELEC.tv/build.LibreELEC-Generic.x86_64-9.0-devel-next/toolchain/x86_64-libreelec-linux-gnu/sysroot/usr/include/asm/signal.h:7,
                 from /home/neil/projects/scratch/alternates/LibreELEC.tv/build.LibreELEC-Generic.x86_64-9.0-devel-next/toolchain/x86_64-libreelec-linux-gnu/sysroot/usr/include/linux/signal.h:5,
                 from /home/neil/projects/scratch/alternates/LibreELEC.tv/build.LibreELEC-Generic.x86_64-9.0-devel-next/toolchain/x86_64-libreelec-linux-gnu/sysroot/usr/include/linux/aio_abi.h:32,
                 from /home/neil/projects/scratch/alternates/LibreELEC.tv/build.LibreELEC-Generic.x86_64-9.0-devel-next/gcc-8.1.0/libsanitizer/include/system/linux/aio_abi.h:2,
                 from /home/neil/projects/scratch/alternates/LibreELEC.tv/build.LibreELEC-Generic.x86_64-9.0-devel-next/gcc-8.1.0/libsanitizer/sanitizer_common/sanitizer_platform_limits_linux.cc:50:
/home/neil/projects/scratch/alternates/LibreELEC.tv/build.LibreELEC-Generic.x86_64-9.0-devel-next/toolchain/x86_64-libreelec-linux-gnu/sysroot/usr/include/linux/time.h:10:8: error: redefinition of 'struct timespec'
 struct timespec {
        ^~~~~~~~
In file included from /home/neil/projects/scratch/alternates/LibreELEC.tv/build.LibreELEC-Generic.x86_64-9.0-devel-next/toolchain/x86_64-libreelec-linux-gnu/sysroot/usr/include/sys/stat.h:30,
                 from /home/neil/projects/scratch/alternates/LibreELEC.tv/build.LibreELEC-Generic.x86_64-9.0-devel-next/gcc-8.1.0/libsanitizer/sanitizer_common/sanitizer_platform_limits_linux.cc:31:
/home/neil/projects/scratch/alternates/LibreELEC.tv/build.LibreELEC-Generic.x86_64-9.0-devel-next/toolchain/x86_64-libreelec-linux-gnu/sysroot/usr/include/bits/types/struct_timespec.h:8:8: note: previous definition of 'struct timespec'
 struct timespec
        ^~~~~~~~
make[5]: *** [Makefile:539: sanitizer_platform_limits_linux.lo] Error 1
make[5]: *** Waiting for unfinished jobs....

End of build log: https://pastebin.com/raw/jbbA7CrE

I bisected the 4.18-rc1 kernel (v4.17 being "good") and this commit is identified as the bad commit - presumably this is a kernel issue rather than a gcc issue? Just looking for pointers on who to bother at this stage... :)

Please sign in to comment.