summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeff Moyer <jmoyer@redhat.com>2018-10-22 12:34:58 -0400
committerJeff Moyer <jmoyer@redhat.com>2018-10-22 12:34:58 -0400
commitcaed0bc756738ce7ae03118466b950a5af4ff510 (patch)
tree2d855ebaa5feb3fe874d9a852d1446f0c094c33f
parentf66be22ab0a59a39858900ab72a8c6a6e8b0b7ec (diff)
parent9c6935e81854d1585bbfa48c35b185849d746864 (diff)
downloadlibaio-caed0bc756738ce7ae03118466b950a5af4ff510.tar.gz
Merge branch 'aio-poll'
-rw-r--r--harness/cases/22.t149
-rw-r--r--man/io_getevents.361
-rw-r--r--src/Makefile2
-rw-r--r--src/aio_ring.h49
-rw-r--r--src/io_getevents.c28
-rw-r--r--src/io_pgetevents.c56
-rw-r--r--src/libaio.h8
-rw-r--r--src/libaio.map5
-rw-r--r--src/syscall-i386.h1
-rw-r--r--src/syscall-x86_64.h1
-rw-r--r--src/syscall.h7
11 files changed, 335 insertions, 32 deletions
diff --git a/harness/cases/22.t b/harness/cases/22.t
new file mode 100644
index 0000000..c7e7c0e
--- /dev/null
+++ b/harness/cases/22.t
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2006-2018 Free Software Foundation, Inc.
+ * Copyright (C) 2018 Christoph Hellwig.
+ * License: LGPLv2.1 or later.
+ *
+ * Description: test aio poll and io_pgetevents signal handling.
+ *
+ * Very roughly based on glibc tst-pselect.c.
+ */
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/poll.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+
+static volatile int handler_called;
+
+static void
+handler(int sig)
+{
+ handler_called = 1;
+}
+
+int test_main(void)
+{
+ struct timespec to = { .tv_sec = 0, .tv_nsec = 500000000 };
+ pid_t parent = getpid(), p;
+ int pipe1[2], pipe2[2];
+ struct sigaction sa = { .sa_flags = 0 };
+ sigset_t sigmask;
+ struct io_context *ctx = NULL;
+ struct io_event ev;
+ struct iocb iocb;
+ struct iocb *iocbs[] = { &iocb };
+ int ret;
+
+ sigemptyset(&sa.sa_mask);
+
+ sa.sa_handler = handler;
+ if (sigaction(SIGUSR1, &sa, NULL) != 0) {
+ printf("sigaction(1) failed\n");
+ return 1;
+ }
+
+ sa.sa_handler = SIG_IGN;
+ if (sigaction(SIGCHLD, &sa, NULL) != 0) {
+ printf("sigaction(2) failed\n");
+ return 1;
+ }
+
+ sigemptyset(&sigmask);
+ sigaddset(&sigmask, SIGUSR1);
+ if (sigprocmask(SIG_BLOCK, &sigmask, NULL) != 0) {
+ printf("sigprocmask failed\n");
+ return 1;
+ }
+
+ if (pipe(pipe1) != 0 || pipe(pipe2) != 0) {
+ printf("pipe failed\n");
+ return 1;
+ }
+
+ sigprocmask(SIG_SETMASK, NULL, &sigmask);
+ sigdelset(&sigmask, SIGUSR1);
+
+ p = fork();
+ switch (p) {
+ case -1:
+ printf("fork failed\n");
+ exit(2);
+ case 0:
+ close(pipe1[1]);
+ close(pipe2[0]);
+
+ ret = io_setup(1, &ctx);
+ if (ret) {
+ printf("child: io_setup failed\n");
+ return 1;
+ }
+
+ io_prep_poll(&iocb, pipe1[0], POLLIN);
+ ret = io_submit(ctx, 1, iocbs);
+ if (ret != 1) {
+ printf("child: io_submit failed\n");
+ return 1;
+ }
+
+ do {
+ if (getppid() != parent) {
+ printf("parent died\n");
+ exit(2);
+ }
+ ret = io_pgetevents(ctx, 1, 1, &ev, &to, &sigmask);
+ } while (ret == 0);
+
+ if (ret != -EINTR) {
+ printf("child: io_pgetevents did not set errno to EINTR\n");
+ return 1;
+ }
+
+ do {
+ errno = 0;
+ ret = write(pipe2[1], "foo", 3);
+ } while (ret == -1 && errno == EINTR);
+
+ exit(0);
+ default:
+ close(pipe1[0]);
+ close(pipe2[1]);
+
+ io_prep_poll(&iocb, pipe2[0], POLLIN);
+
+ ret = io_setup(1, &ctx);
+ if (ret) {
+ printf("parent: io_setup failed\n");
+ return 1;
+ }
+
+ ret = io_submit(ctx, 1, iocbs);
+ if (ret != 1) {
+ printf("parent: io_submit failed\n");
+ return 1;
+ }
+
+ kill(p, SIGUSR1);
+
+ ret = io_pgetevents(ctx, 1, 1, &ev, NULL, &sigmask);
+ if (ret < 0) {
+ printf("parent: io_pgetevents failed\n");
+ return 1;
+ }
+ if (ret != 1) {
+ printf("parent: io_pgetevents did not report event\n");
+ return 1;
+ }
+ if (ev.obj != &iocb) {
+ printf("parent: io_pgetevents reports wrong fd\n");
+ return 1;
+ }
+ if (ev.res != POLLIN) {
+ printf("parent: io_pgetevents did not report readable fd\n");
+ return 1;
+ }
+
+ return 0;
+ }
+}
diff --git a/man/io_getevents.3 b/man/io_getevents.3
index 8e9ddc8..5062daa 100644
--- a/man/io_getevents.3
+++ b/man/io_getevents.3
@@ -18,7 +18,7 @@
./"
.TH io_getevents 2 2002-09-03 "Linux 2.4" "Linux AIO"
.SH NAME
-io_getevents \- Read resulting events from io requests
+io_getevents, aio_pgetevents \- Read resulting events from io requests
.SH SYNOPSIS
.nf
.B #include <errno.h>
@@ -43,7 +43,7 @@ struct io_event {
};
.sp
.BI "int io_getevents(io_context_t " ctx ", long " nr ", struct io_event *" events "[], struct timespec *" timeout ");"
-
+.BI "int io_pgetevents(io_context_t " ctx ", long " nr ", struct io_event *" events "[], struct timespec *" timeout ", sigset_t *" sigmask ");"
.fi
.SH DESCRIPTION
Attempts to read up to nr events from
@@ -55,6 +55,60 @@ by when has elapsed, where when == NULL specifies an infinite
timeout. Note that the timeout pointed to by when is relative and
will be updated if not NULL and the operation blocks. Will fail
with ENOSYS if not implemented.
+.SS io_pgetevents()
+The relationship between
+.BR io_getevents ()
+and
+.BR io_pgetevents ()
+is analogous to the relationship between
+.BR select (2)
+and
+.BR pselect (2):
+similar
+.BR pselect (2),
+.BR pgetevents ()
+allows an application to safely wait until either an aio completion
+events happens or until a signal is caught.
+.PP
+The following
+.BR io_pgetevents ()
+call:
+call:
+.PP
+.in +4n
+.EX
+ret = io_pgetevents(ctx, min_nr, nr, events, timeout, sigmask);
+.EE
+.in
+.PP
+is equivalent to
+.I atomically
+executing the following calls:
+.PP
+.in +4n
+.EX
+sigset_t origmask;
+
+pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
+ret = io_getevents(ctx, min_nr, nr, events, timeout);
+pthread_sigmask(SIG_SETMASK, &origmask, NULL);
+.EE
+.in
+.PP
+See the description of
+.BR pselect (2)
+for an explanation of why
+.BR io_pgetevents ()
+is necessary.
+.PP
+If the
+.I sigmask
+argument is specified as NULL, then no signal mask manipulation is
+performed (and thus
+.BR io_pgetevents ()
+behaves the same as
+.BR io_getevents()
+) .
.SH ERRORS
.TP
.B EINVAL
@@ -76,4 +130,5 @@ if any of the memory specified to is invalid.
.BR io_queue_wait(3),
.BR io_set_callback(3),
.BR io_submit(3),
-.BR errno(3)
+.BR errno(3),
+.BR pselect(2)
diff --git a/src/Makefile b/src/Makefile
index eadb336..f5a57d3 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -23,7 +23,7 @@ libaio_srcs += io_queue_wait.c io_queue_run.c
# real syscalls
libaio_srcs += io_getevents.c io_submit.c io_cancel.c
-libaio_srcs += io_setup.c io_destroy.c
+libaio_srcs += io_setup.c io_destroy.c io_pgetevents.c
# internal functions
libaio_srcs += raw_syscall.c
diff --git a/src/aio_ring.h b/src/aio_ring.h
new file mode 100644
index 0000000..3842c4b
--- /dev/null
+++ b/src/aio_ring.h
@@ -0,0 +1,49 @@
+/*
+ libaio Linux async I/O interface
+ Copyright 2002 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _AIO_RING_H
+#define _AIO_RING_H
+
+#define AIO_RING_MAGIC 0xa10a10a1
+
+struct aio_ring {
+ unsigned id; /* kernel internal index number */
+ unsigned nr; /* number of io_events */
+ unsigned head;
+ unsigned tail;
+
+ unsigned magic;
+ unsigned compat_features;
+ unsigned incompat_features;
+ unsigned header_length; /* size of aio_ring */
+};
+
+static inline int aio_ring_is_empty(io_context_t ctx, struct timespec *timeout)
+{
+ struct aio_ring *ring = (struct aio_ring *)ctx;
+
+ if (!ring || ring->magic != AIO_RING_MAGIC)
+ return 0;
+ if (!timeout || timeout->tv_sec || timeout->tv_nsec)
+ return 0;
+ if (ring->head != ring->tail)
+ return 0;
+ return 1;
+}
+
+#endif /* _AIO_RING_H */
diff --git a/src/io_getevents.c b/src/io_getevents.c
index 5a05174..90d6081 100644
--- a/src/io_getevents.c
+++ b/src/io_getevents.c
@@ -21,36 +21,14 @@
#include <stdlib.h>
#include <time.h>
#include "syscall.h"
+#include "aio_ring.h"
io_syscall5(int, __io_getevents_0_4, io_getevents, io_context_t, ctx, long, min_nr, long, nr, struct io_event *, events, struct timespec *, timeout)
-#define AIO_RING_MAGIC 0xa10a10a1
-
-/* Ben will hate me for this */
-struct aio_ring {
- unsigned id; /* kernel internal index number */
- unsigned nr; /* number of io_events */
- unsigned head;
- unsigned tail;
-
- unsigned magic;
- unsigned compat_features;
- unsigned incompat_features;
- unsigned header_length; /* size of aio_ring */
-};
-
int io_getevents_0_4(io_context_t ctx, long min_nr, long nr, struct io_event * events, struct timespec * timeout)
{
- struct aio_ring *ring;
- ring = (struct aio_ring*)ctx;
- if (ring==NULL || ring->magic != AIO_RING_MAGIC)
- goto do_syscall;
- if (timeout!=NULL && timeout->tv_sec == 0 && timeout->tv_nsec == 0) {
- if (ring->head == ring->tail)
- return 0;
- }
-
-do_syscall:
+ if (aio_ring_is_empty(ctx, timeout))
+ return 0;
return __io_getevents_0_4(ctx, min_nr, nr, events, timeout);
}
diff --git a/src/io_pgetevents.c b/src/io_pgetevents.c
new file mode 100644
index 0000000..e6b0614
--- /dev/null
+++ b/src/io_pgetevents.c
@@ -0,0 +1,56 @@
+/*
+ libaio Linux async I/O interface
+ Copyright 2018 Christoph Hellwig.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <libaio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <time.h>
+#include <signal.h>
+#include "syscall.h"
+#include "aio_ring.h"
+
+#ifdef __NR_io_pgetevents
+io_syscall6(int, __io_pgetevents, io_pgetevents, io_context_t, ctx, long,
+ min_nr, long, nr, struct io_event *, events,
+ struct timespec *, timeout, void *, sigmask);
+
+int io_pgetevents(io_context_t ctx, long min_nr, long nr,
+ struct io_event *events, struct timespec *timeout,
+ sigset_t *sigmask)
+{
+ struct {
+ unsigned long ss;
+ unsigned long ss_len;
+ } data;
+
+ if (aio_ring_is_empty(ctx, timeout))
+ return 0;
+
+ data.ss = (unsigned long)sigmask;
+ data.ss_len = _NSIG / 8;
+ return __io_pgetevents(ctx, min_nr, nr, events, timeout, &data);
+}
+#else
+int io_pgetevents(io_context_t ctx, long min_nr, long nr,
+ struct io_event *events, struct timespec *timeout,
+ sigset_t *sigmask)
+
+{
+ return -ENOSYS;
+}
+#endif /* __NR_io_pgetevents */
diff --git a/src/libaio.h b/src/libaio.h
index 564e680..2bc24e0 100644
--- a/src/libaio.h
+++ b/src/libaio.h
@@ -29,6 +29,7 @@ extern "C" {
#include <sys/types.h>
#include <string.h>
+#include <signal.h>
struct timespec;
struct sockaddr;
@@ -43,7 +44,7 @@ typedef enum io_iocb_cmd {
IO_CMD_FSYNC = 2,
IO_CMD_FDSYNC = 3,
- IO_CMD_POLL = 5, /* Never implemented in mainline, see io_prep_poll */
+ IO_CMD_POLL = 5,
IO_CMD_NOOP = 6,
IO_CMD_PREADV = 7,
IO_CMD_PWRITEV = 8,
@@ -162,6 +163,9 @@ extern int io_destroy(io_context_t ctx);
extern int io_submit(io_context_t ctx, long nr, struct iocb *ios[]);
extern int io_cancel(io_context_t ctx, struct iocb *iocb, struct io_event *evt);
extern int io_getevents(io_context_t ctx_id, long min_nr, long nr, struct io_event *events, struct timespec *timeout);
+extern int io_pgetevents(io_context_t ctx_id, long min_nr, long nr,
+ struct io_event *events, struct timespec *timeout,
+ sigset_t *sigmask);
static inline void io_set_callback(struct iocb *iocb, io_callback_t cb)
@@ -237,8 +241,6 @@ static inline void io_prep_pwritev2(struct iocb *iocb, int fd, const struct iove
iocb->u.c.offset = offset;
}
-/* Jeff Moyer says this was implemented in Red Hat AS2.1 and RHEL3.
- * AFAICT, it was never in mainline, and should not be used. --RR */
static inline void io_prep_poll(struct iocb *iocb, int fd, int events)
{
memset(iocb, 0, sizeof(*iocb));
diff --git a/src/libaio.map b/src/libaio.map
index dc37725..ec9d13b 100644
--- a/src/libaio.map
+++ b/src/libaio.map
@@ -20,3 +20,8 @@ LIBAIO_0.4 {
io_getevents;
io_queue_wait;
} LIBAIO_0.1;
+
+LIBAIO_0.5 {
+ global:
+ io_pgetevents;
+} LIBAIO_0.4;
diff --git a/src/syscall-i386.h b/src/syscall-i386.h
index 266ed93..bc66bb1 100644
--- a/src/syscall-i386.h
+++ b/src/syscall-i386.h
@@ -3,3 +3,4 @@
#define __NR_io_getevents 247
#define __NR_io_submit 248
#define __NR_io_cancel 249
+#define __NR_io_pgetevents 385
diff --git a/src/syscall-x86_64.h b/src/syscall-x86_64.h
index 84b2639..0eccef3 100644
--- a/src/syscall-x86_64.h
+++ b/src/syscall-x86_64.h
@@ -3,3 +3,4 @@
#define __NR_io_getevents 208
#define __NR_io_submit 209
#define __NR_io_cancel 210
+#define __NR_io_pgetevents 333
diff --git a/src/syscall.h b/src/syscall.h
index 9b9e9c1..b53da4c 100644
--- a/src/syscall.h
+++ b/src/syscall.h
@@ -64,3 +64,10 @@ _body_io_syscall(sname, (long)arg1, (long)arg2, (long)arg3, (long)arg4)
#define io_syscall5(type,fname,sname,type1,arg1,type2,arg2,type3,arg3,type4,arg4, type5,arg5) \
type fname (type1 arg1,type2 arg2,type3 arg3,type4 arg4,type5 arg5) \
_body_io_syscall(sname, (long)arg1, (long)arg2, (long)arg3, (long)arg4, (long)arg5)
+
+#define io_syscall6(type,fname,sname,type1,arg1,type2,arg2,type3,arg3, \
+ type4,arg4,type5,arg5,type6,arg6) \
+type fname (type1 arg1,type2 arg2,type3 arg3,type4 arg4,type5 arg5, \
+ type6 arg6) \
+_body_io_syscall(sname, (long)arg1, (long)arg2, (long)arg3, (long)arg4, \
+ (long)arg5, (long)arg6)