| /* |
| * Copyright (c) 2004, Bull SA. All rights reserved. |
| * Created by: Laurent.Vivier@bull.net |
| * Copyright (c) 2013 Cyril Hrubis <chrubis@suse.cz> |
| * |
| * This file is licensed under the GPL license. For the full content |
| * of this license, see the COPYING file at the top level of this |
| * source tree. |
| */ |
| |
| /* |
| * assertion: |
| * |
| * aio_suspend() shall fail if: |
| * [EAGAIN] No AIO indicated in the list completed before timeout |
| * |
| * method: |
| * |
| * - write to a file |
| * - submit a list of read requests |
| * - check that the selected request has not completed |
| * - suspend on selected request |
| * - check that the suspend timed out and returned EAGAIN |
| * |
| */ |
| |
| #define _XOPEN_SOURCE 600 |
| #include <sys/stat.h> |
| #include <aio.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <signal.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| |
| #include "posixtest.h" |
| |
| #define WAIT_FOR_AIOCB 6 |
| |
| static sig_atomic_t received_all; |
| |
| static void sigrt1_handler() |
| { |
| received_all = 1; |
| } |
| |
| static int do_test(int num_aiocbs, size_t buf_size) |
| { |
| char tmpfname[256]; |
| int fd; |
| struct aiocb *aiocbs[num_aiocbs]; |
| struct aiocb *plist[2]; |
| char *bufs; |
| struct sigaction action; |
| struct sigevent event; |
| struct timespec ts = {0, 1000000}; /* 1 ms */ |
| int ret, ret2; |
| int err = PTS_UNRESOLVED; |
| int i; |
| |
| snprintf(tmpfname, sizeof(tmpfname), "/tmp/pts_aio_suspend_9_1_%d", |
| getpid()); |
| unlink(tmpfname); |
| |
| fd = open(tmpfname, O_CREAT | O_RDWR | O_EXCL, S_IRUSR | S_IWUSR); |
| |
| if (fd == -1) { |
| printf("Error at open(): %s\n", strerror(errno)); |
| goto err0; |
| } |
| |
| unlink(tmpfname); |
| |
| int file_size = num_aiocbs * buf_size; |
| |
| bufs = malloc(file_size); |
| |
| if (bufs == NULL) { |
| printf("Error at malloc(): %s\n", strerror(errno)); |
| goto err1; |
| } |
| |
| ret = write(fd, bufs, file_size); |
| if (ret != file_size) { |
| |
| if (ret < 0) |
| printf("Error at write(): %s\n", strerror(errno)); |
| else |
| printf("Error at write(): %i of %i written\n", |
| ret, file_size); |
| |
| goto err2; |
| } |
| |
| /* Queue up a bunch of aio reads */ |
| for (i = 0; i < num_aiocbs; i++) { |
| aiocbs[i] = malloc(sizeof(struct aiocb)); |
| memset(aiocbs[i], 0, sizeof(struct aiocb)); |
| |
| aiocbs[i]->aio_fildes = fd; |
| aiocbs[i]->aio_offset = i * buf_size; |
| aiocbs[i]->aio_buf = &bufs[i * buf_size]; |
| aiocbs[i]->aio_nbytes = buf_size; |
| aiocbs[i]->aio_lio_opcode = LIO_READ; |
| } |
| |
| /* reset the completion flag */ |
| received_all = 0; |
| |
| /* Use SIGRTMIN+1 for list completion */ |
| event.sigev_notify = SIGEV_SIGNAL; |
| event.sigev_signo = SIGRTMIN + 1; |
| event.sigev_value.sival_ptr = NULL; |
| |
| action.sa_sigaction = sigrt1_handler; |
| sigemptyset(&action.sa_mask); |
| action.sa_flags = SA_SIGINFO | SA_RESTART; |
| sigaction(SIGRTMIN + 1, &action, NULL); |
| |
| /* Setup suspend list */ |
| plist[0] = NULL; |
| plist[1] = aiocbs[WAIT_FOR_AIOCB]; |
| |
| /* Submit request list */ |
| ret = lio_listio(LIO_NOWAIT, aiocbs, num_aiocbs, &event); |
| |
| if (ret) { |
| printf(" Error at lio_listio() %d: %s\n", |
| errno, strerror(errno)); |
| goto err3; |
| } |
| |
| /* Suspend on selected request */ |
| ret = aio_suspend((const struct aiocb **)plist, 2, &ts); |
| |
| /* Check selected request has not completed yet */ |
| ret2 = aio_error(aiocbs[WAIT_FOR_AIOCB]); |
| if (ret2 != EINPROGRESS) { |
| /* |
| * The operation was too fast, wait for completion |
| * and redo it with larger buffers. |
| */ |
| err = -1; |
| goto err4; |
| } |
| |
| /* timed out aio_suspend should return -1 and set errno to EAGAIN */ |
| if (ret != -1) { |
| printf("aio_suspend() should return -1\n"); |
| err = PTS_FAIL; |
| goto err4; |
| } |
| if (errno != EAGAIN) { |
| printf("aio_suspend() should set errno to EAGAIN: %d (%s)\n", |
| errno, strerror(errno)); |
| err = PTS_FAIL; |
| goto err4; |
| } |
| |
| /* Wait for list processing completion */ |
| while (!received_all) |
| usleep(10000); |
| |
| /* Check return values and errors */ |
| err = PTS_PASS; |
| |
| for (i = 0; i < num_aiocbs; i++) { |
| err = aio_error(aiocbs[i]); |
| ret = aio_return(aiocbs[i]); |
| |
| if ((err != 0) && ((size_t)ret != buf_size)) { |
| printf("req %d: error = %d - return = %d\n", |
| i, err, ret); |
| err = PTS_FAIL; |
| } |
| } |
| |
| err4: |
| while (!received_all) |
| usleep(10000); |
| err3: |
| for (i = 0; i < num_aiocbs; i++) |
| free(aiocbs[i]); |
| err2: |
| free(bufs); |
| err1: |
| close(fd); |
| err0: |
| return err; |
| } |
| |
| int main(void) |
| { |
| int aio_cbs = 10; |
| int buf_size = 1024 * 64; |
| int ret; |
| |
| if (sysconf(_SC_ASYNCHRONOUS_IO) < 200112L) |
| return PTS_UNSUPPORTED; |
| |
| /* Repeat the test with increasing buffer size */ |
| do { |
| ret = do_test(aio_cbs, buf_size); |
| buf_size += buf_size / 4; |
| } while (ret == -1); |
| |
| if (ret != 0) |
| return ret; |
| |
| printf("(buf_size = %i)\nTest PASSED\n", buf_size); |
| return PTS_PASS; |
| } |