summaryrefslogtreecommitdiff
path: root/harness
diff options
context:
space:
mode:
authorJeff Moyer <jmoyer@redhat.com>2014-09-09 11:05:07 -0400
committerJeff Moyer <jmoyer@redhat.com>2014-09-09 11:05:07 -0400
commit76c4046d32205c413e62005b46524b0589d7aa2f (patch)
treee1f8eaf52e693d4c99ac95361e202bb313614d08 /harness
parent08f50baec0e7731116d3f665c6155d7829edf5d7 (diff)
downloadlibaio-76c4046d32205c413e62005b46524b0589d7aa2f.tar.gz
Add two new tests to the test harness
- Add a test for ring buffer overflows from Dan Aloni. - Add a test that ensures io_getevents waits for I/O completions from Anatol Pomozov. Signed-off-by: Jeff Moyer <jmoyer@redhat.com>
Diffstat (limited to 'harness')
-rw-r--r--harness/Makefile2
-rw-r--r--harness/cases/17.t265
-rw-r--r--harness/cases/18.t116
3 files changed, 382 insertions, 1 deletions
diff --git a/harness/Makefile b/harness/Makefile
index 2a88e71..3db5573 100644
--- a/harness/Makefile
+++ b/harness/Makefile
@@ -6,7 +6,7 @@ PROGS:=$(PARTPROGS) $(EXTRAPROGS)
HARNESS_SRCS:=main.c
# io_queue.c
-CFLAGS+=-Wall -Werror -I../src -g -O
+CFLAGS+=-Wall -Werror -I../src -g -O -lpthread
#-lpthread -lrt
all: $(PROGS)
diff --git a/harness/cases/17.t b/harness/cases/17.t
new file mode 100644
index 0000000..38ada4d
--- /dev/null
+++ b/harness/cases/17.t
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2014, Dan Aloni, Kernelim Ltd.
+ * Copyright (C) 2014, Benjamin LaHaise <bcrl@kvack.org>.
+ * Copyright (C) 2014, Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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
+ * General Public License for more details.
+ */
+/*
+ * Description:
+ * This regression test ensures that submitting more events than can
+ * fit in the completion ring will not result in a hung task.
+ */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE 1
+#endif
+#include <assert.h>
+#include <errno.h>
+#include <libaio.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+
+const int max_events = 32;
+const int io_size = 0x1000;
+struct iocb *io;
+struct iocb **iops;
+struct iovec *iovecs;
+struct io_event *events;
+char *data;
+
+long submitted = 0;
+long completed = 0;
+long pending = 0;
+
+#define SYS_IO_GETEVENTS 0
+#define USER_GETEVENTS 1
+
+static volatile sig_atomic_t done = 0;
+
+struct aio_ring {
+ unsigned id; /* kernel internal index number */
+ unsigned nr; /* number of io_events */
+ volatile unsigned head;
+ volatile unsigned tail;
+
+ unsigned magic;
+ unsigned compat_features;
+ unsigned incompat_features;
+ unsigned header_length; /* size of aio_ring */
+
+ struct io_event io_events[0];
+};
+
+int get_ring_size(int nr_events)
+{
+ io_context_t ctx;
+ int ret, ring_size;
+ struct aio_ring *ring;
+
+ memset(&ctx, 0, sizeof(ctx));
+ ret = io_setup(nr_events, &ctx);
+ assert(!ret);
+
+ ring = (void *)ctx;
+ ring_size = ring->nr;
+
+ ret = io_destroy(ctx);
+ assert(!ret);
+
+ return ring_size;
+}
+
+int user_getevents(io_context_t ctx, int nr_events, struct io_event *event)
+{
+ struct aio_ring *ring = (void *)ctx;
+ int completed = 0;
+ while ((completed < nr_events) && (ring->head != ring->tail)) {
+ unsigned new_head = ring->head;
+ *event = ring->io_events[new_head];
+ new_head += 1;
+ new_head %= ring->nr;
+ ring->head = new_head;
+ completed++;
+ }
+ return completed;
+}
+
+void prune(io_context_t io_ctx, int max_ios, int getevents_type)
+{
+ int ret;
+
+ if (getevents_type == USER_GETEVENTS)
+ ret = user_getevents(io_ctx, max_ios, events);
+ else
+ ret = io_getevents(io_ctx, pending, max_ios, events, NULL);
+ if (ret > 0) {
+ printf("Completed: %d\n", ret);
+ completed += ret;
+ pending -= ret;
+ }
+}
+
+void run_test(int max_ios, int getevents_type)
+{
+ int fd, ret;
+ long i, to_submit;
+ struct iocb **iocb_sub;
+ io_context_t io_ctx;
+ const char *filename = "testfile";
+
+ printf("MAX_IOS: %d, %s\n", max_ios, getevents_type == USER_GETEVENTS ?
+ "USER_GETEVENTS" : "IO_GETEVENTS");
+ memset(&io_ctx, 0, sizeof(io_ctx));
+ ret = io_setup(max_events, &io_ctx);
+ assert(!ret);
+
+ io = calloc(max_ios, sizeof(*io));
+ iops = calloc(max_ios, sizeof(*iops));
+ iovecs = calloc(max_ios, sizeof(*iovecs));
+ events = calloc(max_ios, sizeof(*events));
+
+ unlink(filename);
+ fd = open(filename, O_CREAT | O_RDWR | O_DIRECT, 0644);
+ assert(fd >= 0);
+
+ ret = ftruncate(fd, max_ios * io_size);
+ assert(!ret);
+
+ data = mmap(NULL, io_size * max_ios, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ assert(data != MAP_FAILED);
+
+ for (i = 0; i < max_ios; i++) {
+ iops[i] = &io[i];
+ io[i].data = io;
+ iovecs[i].iov_base = &data[io_size * i];
+ iovecs[i].iov_len = io_size;
+ io_prep_preadv(&io[i], fd, &iovecs[i], 1, 0);
+ }
+
+ submitted = completed = pending = 0;
+
+ to_submit = max_ios;
+ iocb_sub = iops;
+
+ while (submitted < max_ios) {
+ printf("Submitting: %ld\n", to_submit);
+
+ ret = io_submit(io_ctx, to_submit, iocb_sub);
+ if (ret >= 0) {
+ printf("Submitted: %d\n", ret);
+ submitted += ret;
+ iocb_sub += ret;
+ pending += ret;
+ to_submit -= ret;
+ } else {
+ if (ret == -EAGAIN) {
+ printf("Submitted too much, that's okay\n");
+ prune(io_ctx, max_ios, getevents_type);
+ }
+ }
+ }
+
+ prune(io_ctx, max_ios, getevents_type);
+ io_destroy(io_ctx);
+ close(fd);
+ ret = munmap(data, io_size * max_ios);
+ assert(!ret);
+
+ printf("Verifying...\n");
+
+ assert(completed == submitted);
+
+ printf("OK\n");
+}
+
+void run_child(void)
+{
+ int ring_size;
+
+ ring_size = get_ring_size(max_events);
+
+ printf("aio ring size: %d\n", ring_size);
+
+ run_test(ring_size-1, SYS_IO_GETEVENTS);
+ run_test(ring_size, SYS_IO_GETEVENTS);
+ run_test(ring_size+1, SYS_IO_GETEVENTS);
+ run_test(ring_size*2, SYS_IO_GETEVENTS);
+ run_test(ring_size*4, SYS_IO_GETEVENTS);
+
+ run_test(ring_size-1, USER_GETEVENTS);
+ run_test(ring_size, USER_GETEVENTS);
+ run_test(ring_size+1, USER_GETEVENTS);
+ run_test(ring_size*2, USER_GETEVENTS);
+ run_test(ring_size*4, USER_GETEVENTS);
+
+ exit(0);
+}
+
+void sighandler(int signo)
+{
+ assert(signo == SIGCHLD);
+ done = 1;
+}
+
+int test_main(void)
+{
+ unsigned int ret;
+ sighandler_t oldhandler;
+ pid_t child;
+
+ switch (child = fork()) {
+ case 0: /* child */
+ run_child();
+ break;
+ case -1:
+ perror("fork");
+ exit(1);
+ default:
+ oldhandler = signal(SIGCHLD, sighandler);
+ assert(oldhandler != SIG_ERR);
+ break;
+ }
+
+ ret = sleep(10);
+ if (ret != 0) {
+ pid_t pid;
+ int status;
+
+ assert(done);
+
+ pid = wait(&status);
+ if (pid != child) {
+ perror("wait");
+ exit(1);
+ }
+
+ return WEXITSTATUS(status);
+ }
+
+ return 1; /* failed */
+}
+/*
+ * Local Variables:
+ * mode: c
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/harness/cases/18.t b/harness/cases/18.t
new file mode 100644
index 0000000..1fdcf91
--- /dev/null
+++ b/harness/cases/18.t
@@ -0,0 +1,116 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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
+ * General Public License for more details.
+ */
+
+/*
+ * Author: Anatol Pomozov <anatol.pomozov@gmail.com>
+ *
+ * Description: This code tests to make sure that when io_destroy
+ * returns, all outstanding I/Os have been completed. It does this by
+ * issuing one I/O and then calling io_destroy (without calling
+ * io_getevents). After the call to io_destroy, the buffer is checked
+ * to ensure that the data was retrieved. This is done simultaneously
+ * from 100 threads.
+ */
+
+#define _GNU_SOURCE
+
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <unistd.h>
+#include <string.h>
+#include <libaio.h>
+#include <stdbool.h>
+
+#define PAGE_SIZE 4096
+
+#define FILENAME "tempfile"
+#define FILEPATTERN '1'
+#define DESTROY_PATTERN '2'
+
+#define THREADS_NUM 100
+
+void
+aio_worker(void *ptr)
+{
+ int i, j, fd;
+ char buffer[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));
+
+ fd = open(FILENAME, O_DIRECT|O_RDONLY);
+ assert(fd >= 0);
+
+ for (i = 0; i < 1000; i++) {
+ io_context_t ctx;
+ struct iocb cb;
+ struct iocb *cbs[1];
+
+ assert(!io_queue_init(1, &ctx));
+ io_prep_pread(&cb, fd, buffer, PAGE_SIZE, 0);
+ cbs[0] = &cb;
+
+ memset(buffer, '0', PAGE_SIZE);
+ assert(io_submit(ctx, 1, &cbs[0]) == 1);
+ // wait random time (0-500ms) ?
+
+ io_destroy(ctx);
+ memset(buffer, DESTROY_PATTERN, PAGE_SIZE);
+ // wait random for (0-500ms) ?
+
+ // check it is still DESTROY_PATTERN
+ for (j = 0; j < PAGE_SIZE; j++) {
+ if (buffer[j] != DESTROY_PATTERN) {
+ fprintf(stderr,
+ "Buffer has unexpected character: %c\n",
+ buffer[j]);
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+
+ close(fd);
+}
+
+int
+test_main(void)
+{
+ int i, fd, ret;
+ char buffer[PAGE_SIZE];
+ pthread_t threads[THREADS_NUM];
+
+ fd = open(FILENAME, O_CREAT|O_TRUNC|O_APPEND|O_RDWR, S_IRUSR|S_IWUSR);
+ assert(fd != -1);
+
+ memset(buffer, FILEPATTERN, PAGE_SIZE);
+ write(fd, buffer, PAGE_SIZE);
+ close(fd);
+
+ for (i = 0; i < THREADS_NUM; i++) {
+ ret = pthread_create(&threads[i], NULL,
+ (void *)&aio_worker, NULL);
+ assert(ret == 0);
+ }
+ for (i = 0; i < THREADS_NUM; i++) {
+ ret = pthread_join(threads[i], NULL);
+ assert(ret == 0);
+ }
+
+ return EXIT_SUCCESS;
+}
+/*
+ * Local Variables:
+ * mode: c
+ * c-basic-offset: 8
+ * End:
+ */