diff --git a/lib/libc/sys/Symbol.map b/lib/libc/sys/Symbol.map index 93fbc947a7e1..764d712958be 100644 --- a/lib/libc/sys/Symbol.map +++ b/lib/libc/sys/Symbol.map @@ -1,1047 +1,1049 @@ /* * $FreeBSD$ */ /* * It'd be nice to automatically generate the syscall symbols, but we * don't know to what version they will eventually belong to, so for now * it has to be manual. */ FBSD_1.0 { __acl_aclcheck_fd; __acl_aclcheck_file; __acl_aclcheck_link; __acl_delete_fd; __acl_delete_file; __acl_delete_link; __acl_get_fd; __acl_get_file; __acl_get_link; __acl_set_fd; __acl_set_file; __acl_set_link; __getcwd; __mac_execve; __mac_get_fd; __mac_get_file; __mac_get_link; __mac_get_pid; __mac_get_proc; __mac_set_fd; __mac_set_file; __mac_set_link; __mac_set_proc; __setugid; __syscall; __sysctl; _umtx_op; abort2; accept; access; acct; adjtime; aio_cancel; aio_error; aio_fsync; aio_read; aio_return; aio_suspend; aio_waitcomplete; aio_write; audit; auditctl; auditon; bind; chdir; chflags; chmod; chown; chroot; clock_getres; clock_gettime; clock_settime; close; connect; dup; dup2; eaccess; execve; extattr_delete_fd; extattr_delete_file; extattr_delete_link; extattr_get_fd; extattr_get_file; extattr_get_link; extattr_list_fd; extattr_list_file; extattr_list_link; extattr_set_fd; extattr_set_file; extattr_set_link; extattrctl; fchdir; fchflags; fchmod; fchown; fcntl; fhopen; flock; fork; fpathconf; fsync; futimes; getaudit; getaudit_addr; getauid; getcontext; getdtablesize; getegid; geteuid; getfh; getgid; getgroups; getitimer; getpeername; getpgid; getpgrp; getpid; getppid; getpriority; getresgid; getresuid; getrlimit; getrusage; getsid; getsockname; getsockopt; gettimeofday; getuid; ioctl; issetugid; jail; jail_attach; kenv; kill; kldfind; kldfirstmod; kldload; kldnext; kldstat; kldsym; kldunload; kldunloadf; kqueue; kmq_notify; /* Do we want these to be public interfaces? */ kmq_open; /* librt uses them to provide mq_xxx. */ kmq_setattr; kmq_timedreceive; kmq_timedsend; kmq_unlink; ksem_close; ksem_destroy; ksem_getvalue; ksem_init; ksem_open; ksem_post; ksem_timedwait; ksem_trywait; ksem_unlink; ksem_wait; ktrace; lchflags; lchmod; lchown; lgetfh; link; lio_listio; listen; lutimes; mac_syscall; madvise; mincore; minherit; mkdir; mkfifo; mlock; mlockall; modfind; modfnext; modnext; modstat; mount; mprotect; msgget; msgrcv; msgsnd; msgsys; msync; munlock; munlockall; munmap; nanosleep; nfssvc; nmount; ntp_adjtime; ntp_gettime; open; pathconf; pipe; poll; posix_openpt; preadv; profil; pselect; ptrace; pwritev; quotactl; read; readlink; readv; reboot; recvfrom; recvmsg; rename; revoke; rfork; rmdir; rtprio; rtprio_thread; sched_get_priority_max; sched_get_priority_min; sched_getparam; sched_getscheduler; sched_rr_get_interval; sched_setparam; sched_setscheduler; sched_yield; select; semget; semop; semsys; sendfile; sendmsg; sendto; setaudit; setaudit_addr; setauid; setegid; seteuid; setgid; setgroups; setitimer; setlogin; setpgid; setpriority; setregid; setresgid; setresuid; setreuid; setrlimit; setsid; setsockopt; settimeofday; setuid; shm_open; shm_unlink; shmat; shmdt; shmget; shmsys; shutdown; sigaction; sigaltstack; sigpending; sigprocmask; sigqueue; sigreturn; sigsuspend; sigtimedwait; sigwait; sigwaitinfo; socket; socketpair; swapoff; swapon; symlink; sync; sysarch; syscall; thr_create; thr_exit; thr_kill; thr_kill2; thr_new; thr_self; thr_set_name; thr_suspend; thr_wake; ktimer_create; /* Do we want these to be public interfaces? */ ktimer_delete; /* librt uses them to provide timer_xxx. */ ktimer_getoverrun; ktimer_gettime; ktimer_settime; umask; undelete; unlink; unmount; utimes; utrace; uuidgen; vadvise; wait4; write; writev; __error; ftruncate; lseek; mmap; pread; pwrite; truncate; }; FBSD_1.1 { __semctl; closefrom; cpuset; cpuset_getid; cpuset_setid; cpuset_getaffinity; cpuset_setaffinity; faccessat; fchmodat; fchownat; fexecve; futimesat; jail_get; jail_set; jail_remove; linkat; lpathconf; mkdirat; mkfifoat; msgctl; readlinkat; renameat; setfib; shmctl; symlinkat; unlinkat; }; FBSD_1.2 { cap_enter; cap_getmode; getloginclass; pdfork; pdgetpid; pdkill; posix_fallocate; rctl_get_racct; rctl_get_rules; rctl_get_limits; rctl_add_rule; rctl_remove_rule; setloginclass; }; FBSD_1.3 { accept4; aio_mlock; bindat; cap_fcntls_get; cap_fcntls_limit; cap_ioctls_get; cap_ioctls_limit; __cap_rights_get; cap_rights_limit; cap_sandboxed; chflagsat; clock_getcpuclockid2; connectat; ffclock_getcounter; ffclock_getestimate; ffclock_setestimate; pipe2; posix_fadvise; procctl; wait6; }; FBSD_1.4 { futimens; ppoll; utimensat; numa_setaffinity; numa_getaffinity; sendmmsg; recvmmsg; }; FBSD_1.5 { clock_nanosleep; fdatasync; fhstat; fhstatfs; fstat; fstatat; fstatfs; getdents; getdirentries; getfsstat; getrandom; kevent; lstat; mknod; mknodat; stat; statfs; cpuset_getdomain; cpuset_setdomain; }; FBSD_1.6 { __sysctlbyname; aio_readv; aio_writev; close_range; copy_file_range; fhlink; fhlinkat; fhreadlink; getfhat; funlinkat; memfd_create; shm_create_largepage; shm_rename; }; FBSD_1.7 { _Fork; fspacectl; }; FBSDprivate_1.0 { ___acl_aclcheck_fd; __sys___acl_aclcheck_fd; ___acl_aclcheck_file; __sys___acl_aclcheck_file; ___acl_aclcheck_link; __sys___acl_aclcheck_link; ___acl_delete_fd; __sys___acl_delete_fd; ___acl_delete_file; __sys___acl_delete_file; ___acl_delete_link; __sys___acl_delete_link; ___acl_get_fd; __sys___acl_get_fd; ___acl_get_file; __sys___acl_get_file; ___acl_get_link; __sys___acl_get_link; ___acl_set_fd; __sys___acl_set_fd; ___acl_set_file; __sys___acl_set_file; ___acl_set_link; __sys___acl_set_link; ___getcwd; __sys___getcwd; ___mac_execve; __sys___mac_execve; ___mac_get_fd; __sys___mac_get_fd; ___mac_get_file; __sys___mac_get_file; ___mac_get_link; __sys___mac_get_link; ___mac_get_pid; __sys___mac_get_pid; ___mac_get_proc; __sys___mac_get_proc; ___mac_set_fd; __sys___mac_set_fd; ___mac_set_file; __sys___mac_set_file; ___mac_set_link; __sys___mac_set_link; ___mac_set_proc; __sys___mac_set_proc; ___semctl; __sys___semctl; ___setugid; __sys___setugid; ___syscall; __sys___syscall; ___sysctl; __sys___sysctl; __umtx_op; __sys__umtx_op; _abort2; __sys_abort2; _accept; __sys_accept; _accept4; __sys_accept4; _access; __sys_access; _acct; __sys_acct; _adjtime; __sys_adjtime; __sys_aio_cancel; __sys_aio_error; __sys_aio_fsync; __sys_aio_read; + __sys_aio_readv; __sys_aio_return; __sys_aio_suspend; __sys_aio_waitcomplete; __sys_aio_write; + __sys_aio_writev; _audit; __sys_audit; _auditctl; __sys_auditctl; _auditon; __sys_auditon; _bind; __sys_bind; _chdir; __sys_chdir; _chflags; __sys_chflags; _chmod; __sys_chmod; _chown; __sys_chown; _chroot; __sys_chroot; _clock_getcpuclockid2; __sys_clock_getcpuclockid2; _clock_getres; __sys_clock_getres; _clock_gettime; __sys_clock_gettime; __sys_clock_nanosleep; _clock_settime; __sys_clock_settime; _close; __sys_close; _connect; __sys_connect; _cpuset; __sys_cpuset; _cpuset_getid; __sys_cpuset_getid; _cpuset_setid; __sys_cpuset_setid; _cpuset_getaffinity; __sys_cpuset_getaffinity; _cpuset_setaffinity; __sys_cpuset_setaffinity; _dup; __sys_dup; _dup2; __sys_dup2; _eaccess; __sys_eaccess; _execve; __sys_execve; _extattr_delete_fd; __sys_extattr_delete_fd; _extattr_delete_file; __sys_extattr_delete_file; _extattr_delete_link; __sys_extattr_delete_link; _extattr_get_fd; __sys_extattr_get_fd; _extattr_get_file; __sys_extattr_get_file; _extattr_get_link; __sys_extattr_get_link; _extattr_list_fd; __sys_extattr_list_fd; _extattr_list_file; __sys_extattr_list_file; _extattr_list_link; __sys_extattr_list_link; _extattr_set_fd; __sys_extattr_set_fd; _extattr_set_file; __sys_extattr_set_file; _extattr_set_link; __sys_extattr_set_link; _extattrctl; __sys_extattrctl; __sys_sigfastblock; _fchdir; __sys_fchdir; _fchflags; __sys_fchflags; _fchmod; __sys_fchmod; _fchown; __sys_fchown; _fcntl; __sys_fcntl; __fcntl_compat; _fhopen; __sys_fhopen; _fhstat; __sys_fhstat; _fhstatfs; __sys_fhstatfs; _flock; __sys_flock; _fork; __sys_fork; _fpathconf; __sys_fpathconf; _fstat; __sys_fstat; _fstatfs; __sys_fstatfs; _fsync; __sys_fsync; _fdatasync; __sys_fdatasync; _futimes; __sys_futimes; _getaudit; __sys_getaudit; _getaudit_addr; __sys_getaudit_addr; _getauid; __sys_getauid; _getcontext; __sys_getcontext; _getdirentries; __sys_getdirentries; _getdtablesize; __sys_getdtablesize; _getegid; __sys_getegid; _geteuid; __sys_geteuid; _getfh; __sys_getfh; _getfsstat; __sys_getfsstat; _getgid; __sys_getgid; _getgroups; __sys_getgroups; _getitimer; __sys_getitimer; _getpeername; __sys_getpeername; _getpgid; __sys_getpgid; _getpgrp; __sys_getpgrp; _getpid; __sys_getpid; _getppid; __sys_getppid; _getpriority; __sys_getpriority; _getresgid; __sys_getresgid; _getresuid; __sys_getresuid; _getrlimit; __sys_getrlimit; _getrusage; __sys_getrusage; _getsid; __sys_getsid; _getsockname; __sys_getsockname; _getsockopt; __sys_getsockopt; _gettimeofday; __sys_gettimeofday; _getuid; __sys_getuid; _ioctl; __sys_ioctl; _issetugid; __sys_issetugid; _jail; __sys_jail; _jail_attach; __sys_jail_attach; _kenv; __sys_kenv; _kevent; __sys_kevent; _kill; __sys_kill; _kldfind; __sys_kldfind; _kldfirstmod; __sys_kldfirstmod; _kldload; __sys_kldload; _kldnext; __sys_kldnext; _kldstat; __sys_kldstat; _kldsym; __sys_kldsym; _kldunload; __sys_kldunload; _kldunloadf; __sys_kldunloadf; _kmq_notify; __sys_kmq_notify; _kmq_open; __sys_kmq_open; _kmq_setattr; __sys_kmq_setattr; _kmq_timedreceive; __sys_kmq_timedreceive; _kmq_timedsend; __sys_kmq_timedsend; _kmq_unlink; __sys_kmq_unlink; _kqueue; __sys_kqueue; _ksem_close; __sys_ksem_close; _ksem_destroy; __sys_ksem_destroy; _ksem_getvalue; __sys_ksem_getvalue; _ksem_init; __sys_ksem_init; _ksem_open; __sys_ksem_open; _ksem_post; __sys_ksem_post; _ksem_timedwait; __sys_ksem_timedwait; _ksem_trywait; __sys_ksem_trywait; _ksem_unlink; __sys_ksem_unlink; _ksem_wait; __sys_ksem_wait; _ktrace; __sys_ktrace; _lchflags; __sys_lchflags; _lchmod; __sys_lchmod; _lchown; __sys_lchown; _lgetfh; __sys_lgetfh; _link; __sys_link; __sys_lio_listio; _listen; __sys_listen; _lutimes; __sys_lutimes; _mac_syscall; __sys_mac_syscall; _madvise; __sys_madvise; _mincore; __sys_mincore; _minherit; __sys_minherit; _mkdir; __sys_mkdir; _mkfifo; __sys_mkfifo; _mknod; __sys_mknod; _mlock; __sys_mlock; _mlockall; __sys_mlockall; _modfind; __sys_modfind; _modfnext; __sys_modfnext; _modnext; __sys_modnext; _modstat; __sys_modstat; _mount; __sys_mount; _mprotect; __sys_mprotect; _msgctl; __sys_msgctl; _msgget; __sys_msgget; _msgrcv; __sys_msgrcv; _msgsnd; __sys_msgsnd; _msgsys; __sys_msgsys; _msync; __sys_msync; _munlock; __sys_munlock; _munlockall; __sys_munlockall; _munmap; __sys_munmap; _nanosleep; __sys_nanosleep; _nfssvc; __sys_nfssvc; _nmount; __sys_nmount; _ntp_adjtime; __sys_ntp_adjtime; _ntp_gettime; __sys_ntp_gettime; _open; __sys_open; _openat; __sys_openat; _pathconf; __sys_pathconf; __sys_pdfork; _pipe; __sys_pipe; _poll; __sys_poll; _ppoll; __sys_ppoll; _preadv; __sys_preadv; _procctl; __sys_procctl; _profil; __sys_profil; _pselect; __sys_pselect; _ptrace; __sys_ptrace; _pwritev; __sys_pwritev; _quotactl; __sys_quotactl; _read; __sys_read; _readlink; __sys_readlink; _readv; __sys_readv; _reboot; __sys_reboot; _recvfrom; __sys_recvfrom; _recvmsg; __sys_recvmsg; _rename; __sys_rename; _revoke; __sys_revoke; _rfork; __sys_rfork; _rmdir; __sys_rmdir; _rtprio; __sys_rtprio; _rtprio_thread; __sys_rtprio_thread; _sched_get_priority_max; __sys_sched_get_priority_max; _sched_get_priority_min; __sys_sched_get_priority_min; _sched_getparam; __sys_sched_getparam; _sched_getscheduler; __sys_sched_getscheduler; _sched_rr_get_interval; __sys_sched_rr_get_interval; _sched_setparam; __sys_sched_setparam; _sched_setscheduler; __sys_sched_setscheduler; _sched_yield; __sys_sched_yield; _select; __sys_select; _semget; __sys_semget; _semop; __sys_semop; _semsys; __sys_semsys; _sendfile; __sys_sendfile; _sendmsg; __sys_sendmsg; _sendto; __sys_sendto; _setaudit; __sys_setaudit; _setaudit_addr; __sys_setaudit_addr; _setauid; __sys_setauid; _setcontext; __sys_setcontext; _setegid; __sys_setegid; _seteuid; __sys_seteuid; _setgid; __sys_setgid; _setgroups; __sys_setgroups; _setitimer; __sys_setitimer; _setlogin; __sys_setlogin; _setpgid; __sys_setpgid; _setpriority; __sys_setpriority; _setregid; __sys_setregid; _setresgid; __sys_setresgid; _setresuid; __sys_setresuid; _setreuid; __sys_setreuid; _setrlimit; __sys_setrlimit; _setsid; __sys_setsid; _setsockopt; __sys_setsockopt; _settimeofday; __sys_settimeofday; _setuid; __sys_setuid; _shm_open; __sys_shm_open; __sys_shm_open2; _shm_unlink; __sys_shm_unlink; _shmat; __sys_shmat; _shmctl; __sys_shmctl; _shmdt; __sys_shmdt; _shmget; __sys_shmget; _shmsys; __sys_shmsys; _shutdown; __sys_shutdown; _sigaction; __sys_sigaction; _sigaltstack; __sys_sigaltstack; _sigpending; __sys_sigpending; _sigprocmask; __sys_sigprocmask; _sigqueue; __sys_sigqueue; _sigreturn; __sys_sigreturn; _sigsuspend; __sys_sigsuspend; _sigtimedwait; __sys_sigtimedwait; _sigwait; __sigwait; __sys_sigwait; _sigwaitinfo; __sys_sigwaitinfo; _socket; __sys_socket; _socketpair; __sys_socketpair; _statfs; __sys_statfs; _swapcontext; __sys_swapcontext; _swapoff; __sys_swapoff; _swapon; __sys_swapon; _symlink; __sys_symlink; _sync; __sys_sync; _sysarch; __sys_sysarch; _syscall; __sys_syscall; _thr_create; __sys_thr_create; _thr_exit; __sys_thr_exit; _thr_kill; __sys_thr_kill; _thr_kill2; __sys_thr_kill2; _thr_new; __sys_thr_new; _thr_self; __sys_thr_self; _thr_set_name; __sys_thr_set_name; _thr_suspend; __sys_thr_suspend; _thr_wake; __sys_thr_wake; _ktimer_create; __sys_ktimer_create; _ktimer_delete; __sys_ktimer_delete; _ktimer_getoverrun; __sys_ktimer_getoverrun; _ktimer_gettime; __sys_ktimer_gettime; _ktimer_settime; __sys_ktimer_settime; _umask; __sys_umask; _undelete; __sys_undelete; _unlink; __sys_unlink; _unmount; __sys_unmount; _utimes; __sys_utimes; _utrace; __sys_utrace; _uuidgen; __sys_uuidgen; _wait4; __sys_wait4; _wait6; __sys_wait6; _write; __sys_write; _writev; __sys_writev; __set_error_selector; nlm_syscall; gssd_syscall; __libc_interposing_slot; __libc_sigwait; _cpuset_getdomain; __sys_cpuset_getdomain; _cpuset_setdomain; __sys_cpuset_setdomain; rpctls_syscall; }; diff --git a/lib/librt/Symbol.map b/lib/librt/Symbol.map index c11b88397afd..b8fde3dd33b8 100644 --- a/lib/librt/Symbol.map +++ b/lib/librt/Symbol.map @@ -1,65 +1,70 @@ /* * $FreeBSD$ */ FBSD_1.0 { aio_read; aio_write; aio_return; aio_waitcomplete; aio_fsync; mq_open; mq_close; mq_notify; mq_getattr; mq_setattr; mq_timedreceive; mq_timedsend; mq_unlink; mq_send; mq_receive; timer_create; timer_delete; timer_gettime; timer_settime; timer_getoverrun; }; FBSD_1.5 { lio_listio; mq_getfd_np; timer_oshandle_np; }; +FBSD_1.6 { + aio_readv; + aio_writev; +}; + FBSDprivate_1.0 { _mq_open; _mq_close; _mq_notify; _mq_getattr; _mq_setattr; _mq_timedreceive; _mq_timedsend; _mq_unlink; _mq_send; _mq_receive; __mq_open; __mq_close; __mq_notify; __mq_getattr; __mq_setattr; __mq_timedreceive; __mq_timedsend; __mq_unlink; __mq_send; __mq_receive; _timer_create; _timer_delete; _timer_gettime; _timer_settime; _timer_getoverrun; __timer_create; __timer_delete; __timer_gettime; __timer_settime; __timer_getoverrun; }; diff --git a/lib/librt/aio.c b/lib/librt/aio.c index 9c35644ecf3d..8e819a002613 100644 --- a/lib/librt/aio.c +++ b/lib/librt/aio.c @@ -1,232 +1,250 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2005 David Xu * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ * */ #include #include #include #include #include "namespace.h" #include #include #include #include "sigev_thread.h" #include "un-namespace.h" __weak_reference(__aio_read, aio_read); +__weak_reference(__aio_readv, aio_readv); __weak_reference(__aio_write, aio_write); +__weak_reference(__aio_writev, aio_writev); __weak_reference(__aio_return, aio_return); __weak_reference(__aio_waitcomplete, aio_waitcomplete); __weak_reference(__aio_fsync, aio_fsync); __weak_reference(__lio_listio, lio_listio); typedef void (*aio_func)(union sigval val, struct aiocb *iocb); extern int __sys_aio_read(struct aiocb *iocb); +extern int __sys_aio_readv(struct aiocb *iocb); extern int __sys_aio_write(struct aiocb *iocb); +extern int __sys_aio_writev(struct aiocb *iocb); extern ssize_t __sys_aio_waitcomplete(struct aiocb **iocbp, struct timespec *timeout); extern ssize_t __sys_aio_return(struct aiocb *iocb); extern int __sys_aio_error(struct aiocb *iocb); extern int __sys_aio_fsync(int op, struct aiocb *iocb); extern int __sys_lio_listio(int mode, struct aiocb * const list[], int nent, struct sigevent *sig); static void aio_dispatch(struct sigev_node *sn) { aio_func f = sn->sn_func; f(sn->sn_value, (struct aiocb *)sn->sn_id); } static int aio_sigev_alloc(sigev_id_t id, struct sigevent *sigevent, struct sigev_node **sn, struct sigevent *saved_ev) { if (__sigev_check_init()) { /* This might be that thread library is not enabled. */ errno = EINVAL; return (-1); } *sn = __sigev_alloc(SI_ASYNCIO, sigevent, NULL, 1); if (*sn == NULL) { errno = EAGAIN; return (-1); } *saved_ev = *sigevent; (*sn)->sn_id = id; __sigev_get_sigevent(*sn, sigevent, (*sn)->sn_id); (*sn)->sn_dispatch = aio_dispatch; __sigev_list_lock(); __sigev_register(*sn); __sigev_list_unlock(); return (0); } static int aio_io(struct aiocb *iocb, int (*sysfunc)(struct aiocb *iocb)) { struct sigev_node *sn; struct sigevent saved_ev; int ret, err; if (iocb->aio_sigevent.sigev_notify != SIGEV_THREAD) { ret = sysfunc(iocb); return (ret); } ret = aio_sigev_alloc((sigev_id_t)iocb, &iocb->aio_sigevent, &sn, &saved_ev); if (ret) return (ret); ret = sysfunc(iocb); iocb->aio_sigevent = saved_ev; if (ret != 0) { err = errno; __sigev_list_lock(); __sigev_delete_node(sn); __sigev_list_unlock(); errno = err; } return (ret); } int __aio_read(struct aiocb *iocb) { return aio_io(iocb, &__sys_aio_read); } +int +__aio_readv(struct aiocb *iocb) +{ + + return aio_io(iocb, &__sys_aio_readv); +} + int __aio_write(struct aiocb *iocb) { return aio_io(iocb, &__sys_aio_write); } +int +__aio_writev(struct aiocb *iocb) +{ + + return aio_io(iocb, &__sys_aio_writev); +} + ssize_t __aio_waitcomplete(struct aiocb **iocbp, struct timespec *timeout) { ssize_t ret; int err; ret = __sys_aio_waitcomplete(iocbp, timeout); if (*iocbp) { if ((*iocbp)->aio_sigevent.sigev_notify == SIGEV_THREAD) { err = errno; __sigev_list_lock(); __sigev_delete(SI_ASYNCIO, (sigev_id_t)(*iocbp)); __sigev_list_unlock(); errno = err; } } return (ret); } ssize_t __aio_return(struct aiocb *iocb) { if (iocb->aio_sigevent.sigev_notify == SIGEV_THREAD) { if (__sys_aio_error(iocb) == EINPROGRESS) { /* * Fail with EINVAL to match the semantics of * __sys_aio_return() for an in-progress * request. */ errno = EINVAL; return (-1); } __sigev_list_lock(); __sigev_delete(SI_ASYNCIO, (sigev_id_t)iocb); __sigev_list_unlock(); } return __sys_aio_return(iocb); } int __aio_fsync(int op, struct aiocb *iocb) { struct sigev_node *sn; struct sigevent saved_ev; int ret, err; if (iocb->aio_sigevent.sigev_notify != SIGEV_THREAD) return __sys_aio_fsync(op, iocb); ret = aio_sigev_alloc((sigev_id_t)iocb, &iocb->aio_sigevent, &sn, &saved_ev); if (ret) return (ret); ret = __sys_aio_fsync(op, iocb); iocb->aio_sigevent = saved_ev; if (ret != 0) { err = errno; __sigev_list_lock(); __sigev_delete_node(sn); __sigev_list_unlock(); errno = err; } return (ret); } int __lio_listio(int mode, struct aiocb * const list[], int nent, struct sigevent *sig) { struct sigev_node *sn; struct sigevent saved_ev; int ret, err; if (sig == NULL || sig->sigev_notify != SIGEV_THREAD) return (__sys_lio_listio(mode, list, nent, sig)); ret = aio_sigev_alloc((sigev_id_t)list, sig, &sn, &saved_ev); if (ret) return (ret); ret = __sys_lio_listio(mode, list, nent, sig); *sig = saved_ev; if (ret != 0) { err = errno; __sigev_list_lock(); __sigev_delete_node(sn); __sigev_list_unlock(); errno = err; } return (ret); } diff --git a/tests/sys/aio/aio_test.c b/tests/sys/aio/aio_test.c index 35bd5dc1264b..1c694ad0c18b 100644 --- a/tests/sys/aio/aio_test.c +++ b/tests/sys/aio/aio_test.c @@ -1,1819 +1,1826 @@ /*- * Copyright (c) 2004 Robert N. M. Watson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Regression test to do some very basic AIO exercising on several types of * file descriptors. Currently, the tests consist of initializing a fixed * size buffer with pseudo-random data, writing it to one fd using AIO, then * reading it from a second descriptor using AIO. For some targets, the same * fd is used for write and read (i.e., file, md device), but for others the * operation is performed on a peer (pty, socket, fifo, etc). For each file * descriptor type, several completion methods are tested. This test program * does not attempt to exercise error cases or more subtle asynchronous * behavior, just make sure that the basic operations work on some basic object * types. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "freebsd_test_suite/macros.h" #include "local.h" /* * GLOBAL_MAX sets the largest usable buffer size to be read and written, as * it sizes ac_buffer in the aio_context structure. It is also the default * size for file I/O. For other types, we use smaller blocks or we risk * blocking (and we run in a single process/thread so that would be bad). */ #define GLOBAL_MAX 16384 #define BUFFER_MAX GLOBAL_MAX /* * A completion function will block until the aio has completed, then return * the result of the aio. errno will be set appropriately. */ typedef ssize_t (*completion)(struct aiocb*); struct aio_context { int ac_read_fd, ac_write_fd; long ac_seed; char ac_buffer[GLOBAL_MAX]; int ac_buflen; int ac_seconds; }; static sem_t completions; /* * Fill a buffer given a seed that can be fed into srandom() to initialize * the PRNG in a repeatable manner. */ static void aio_fill_buffer(char *buffer, int len, long seed) { char ch; int i; srandom(seed); for (i = 0; i < len; i++) { ch = random() & 0xff; buffer[i] = ch; } } /* * Test that a buffer matches a given seed. See aio_fill_buffer(). Return * (1) on a match, (0) on a mismatch. */ static int aio_test_buffer(char *buffer, int len, long seed) { char ch; int i; srandom(seed); for (i = 0; i < len; i++) { ch = random() & 0xff; if (buffer[i] != ch) return (0); } return (1); } /* * Initialize a testing context given the file descriptors provided by the * test setup. */ static void aio_context_init(struct aio_context *ac, int read_fd, int write_fd, int buflen) { ATF_REQUIRE_MSG(buflen <= BUFFER_MAX, "aio_context_init: buffer too large (%d > %d)", buflen, BUFFER_MAX); bzero(ac, sizeof(*ac)); ac->ac_read_fd = read_fd; ac->ac_write_fd = write_fd; ac->ac_buflen = buflen; srandomdev(); ac->ac_seed = random(); aio_fill_buffer(ac->ac_buffer, buflen, ac->ac_seed); ATF_REQUIRE_MSG(aio_test_buffer(ac->ac_buffer, buflen, ac->ac_seed) != 0, "aio_test_buffer: internal error"); } static ssize_t poll(struct aiocb *aio) { int error; while ((error = aio_error(aio)) == EINPROGRESS) usleep(25000); if (error) return (error); else return (aio_return(aio)); } static void sigusr1_handler(int sig __unused) { ATF_REQUIRE_EQ(0, sem_post(&completions)); } static void thr_handler(union sigval sv __unused) { ATF_REQUIRE_EQ(0, sem_post(&completions)); } static ssize_t poll_signaled(struct aiocb *aio) { int error; ATF_REQUIRE_EQ(0, sem_wait(&completions)); error = aio_error(aio); switch (error) { case EINPROGRESS: errno = EINTR; return (-1); case 0: return (aio_return(aio)); default: return (error); } } /* * Setup a signal handler for signal delivery tests * This isn't thread safe, but it's ok since ATF runs each testcase in a * separate process */ static struct sigevent* setup_signal(void) { static struct sigevent sev; ATF_REQUIRE_EQ(0, sem_init(&completions, false, 0)); sev.sigev_notify = SIGEV_SIGNAL; sev.sigev_signo = SIGUSR1; ATF_REQUIRE(SIG_ERR != signal(SIGUSR1, sigusr1_handler)); return (&sev); } /* * Setup a thread for thread delivery tests * This isn't thread safe, but it's ok since ATF runs each testcase in a * separate process */ static struct sigevent* setup_thread(void) { static struct sigevent sev; ATF_REQUIRE_EQ(0, sem_init(&completions, false, 0)); sev.sigev_notify = SIGEV_THREAD; sev.sigev_notify_function = thr_handler; sev.sigev_notify_attributes = NULL; return (&sev); } static ssize_t suspend(struct aiocb *aio) { const struct aiocb *const iocbs[] = {aio}; int error; error = aio_suspend(iocbs, 1, NULL); if (error == 0) return (aio_return(aio)); else return (error); } static ssize_t waitcomplete(struct aiocb *aio) { struct aiocb *aiop; ssize_t ret; ret = aio_waitcomplete(&aiop, NULL); ATF_REQUIRE_EQ(aio, aiop); return (ret); } /* * Perform a simple write test of our initialized data buffer to the provided * file descriptor. */ static void aio_write_test(struct aio_context *ac, completion comp, struct sigevent *sev) { struct aiocb aio; ssize_t len; bzero(&aio, sizeof(aio)); aio.aio_buf = ac->ac_buffer; aio.aio_nbytes = ac->ac_buflen; aio.aio_fildes = ac->ac_write_fd; aio.aio_offset = 0; if (sev) aio.aio_sigevent = *sev; if (aio_write(&aio) < 0) atf_tc_fail("aio_write failed: %s", strerror(errno)); len = comp(&aio); if (len < 0) atf_tc_fail("aio failed: %s", strerror(errno)); if (len != ac->ac_buflen) atf_tc_fail("aio short write (%jd)", (intmax_t)len); } /* * Perform a vectored I/O test of our initialized data buffer to the provided * file descriptor. * * To vectorize the linear buffer, chop it up into two pieces of dissimilar * size, and swap their offsets. */ static void aio_writev_test(struct aio_context *ac, completion comp, struct sigevent *sev) { struct aiocb aio; struct iovec iov[2]; size_t len0, len1; ssize_t len; bzero(&aio, sizeof(aio)); aio.aio_fildes = ac->ac_write_fd; aio.aio_offset = 0; len0 = ac->ac_buflen * 3 / 4; len1 = ac->ac_buflen / 4; iov[0].iov_base = ac->ac_buffer + len1; iov[0].iov_len = len0; iov[1].iov_base = ac->ac_buffer; iov[1].iov_len = len1; aio.aio_iov = iov; aio.aio_iovcnt = 2; if (sev) aio.aio_sigevent = *sev; if (aio_writev(&aio) < 0) atf_tc_fail("aio_writev failed: %s", strerror(errno)); len = comp(&aio); if (len < 0) atf_tc_fail("aio failed: %s", strerror(errno)); if (len != ac->ac_buflen) atf_tc_fail("aio short write (%jd)", (intmax_t)len); } /* * Perform a simple read test of our initialized data buffer from the * provided file descriptor. */ static void aio_read_test(struct aio_context *ac, completion comp, struct sigevent *sev) { struct aiocb aio; ssize_t len; bzero(ac->ac_buffer, ac->ac_buflen); bzero(&aio, sizeof(aio)); aio.aio_buf = ac->ac_buffer; aio.aio_nbytes = ac->ac_buflen; aio.aio_fildes = ac->ac_read_fd; aio.aio_offset = 0; if (sev) aio.aio_sigevent = *sev; if (aio_read(&aio) < 0) atf_tc_fail("aio_read failed: %s", strerror(errno)); len = comp(&aio); if (len < 0) atf_tc_fail("aio failed: %s", strerror(errno)); ATF_REQUIRE_EQ_MSG(len, ac->ac_buflen, "aio short read (%jd)", (intmax_t)len); if (aio_test_buffer(ac->ac_buffer, ac->ac_buflen, ac->ac_seed) == 0) atf_tc_fail("buffer mismatched"); } static void aio_readv_test(struct aio_context *ac, completion comp, struct sigevent *sev) { struct aiocb aio; struct iovec iov[2]; size_t len0, len1; ssize_t len; bzero(ac->ac_buffer, ac->ac_buflen); bzero(&aio, sizeof(aio)); aio.aio_fildes = ac->ac_read_fd; aio.aio_offset = 0; len0 = ac->ac_buflen * 3 / 4; len1 = ac->ac_buflen / 4; iov[0].iov_base = ac->ac_buffer + len1; iov[0].iov_len = len0; iov[1].iov_base = ac->ac_buffer; iov[1].iov_len = len1; aio.aio_iov = iov; aio.aio_iovcnt = 2; if (sev) aio.aio_sigevent = *sev; if (aio_readv(&aio) < 0) atf_tc_fail("aio_read failed: %s", strerror(errno)); len = comp(&aio); if (len < 0) atf_tc_fail("aio failed: %s", strerror(errno)); ATF_REQUIRE_EQ_MSG(len, ac->ac_buflen, "aio short read (%jd)", (intmax_t)len); if (aio_test_buffer(ac->ac_buffer, ac->ac_buflen, ac->ac_seed) == 0) atf_tc_fail("buffer mismatched"); } /* * Series of type-specific tests for AIO. For now, we just make sure we can * issue a write and then a read to each type. We assume that once a write * is issued, a read can follow. */ /* * Test with a classic file. Assumes we can create a moderate size temporary * file. */ #define FILE_LEN GLOBAL_MAX #define FILE_PATHNAME "testfile" static void aio_file_test(completion comp, struct sigevent *sev, bool vectored) { struct aio_context ac; int fd; ATF_REQUIRE_KERNEL_MODULE("aio"); ATF_REQUIRE_UNSAFE_AIO(); fd = open(FILE_PATHNAME, O_RDWR | O_CREAT, 0600); ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno)); aio_context_init(&ac, fd, fd, FILE_LEN); if (vectored) { aio_writev_test(&ac, comp, sev); aio_readv_test(&ac, comp, sev); } else { aio_write_test(&ac, comp, sev); aio_read_test(&ac, comp, sev); } close(fd); } ATF_TC_WITHOUT_HEAD(file_poll); ATF_TC_BODY(file_poll, tc) { aio_file_test(poll, NULL, false); } ATF_TC_WITHOUT_HEAD(file_signal); ATF_TC_BODY(file_signal, tc) { aio_file_test(poll_signaled, setup_signal(), false); } ATF_TC_WITHOUT_HEAD(file_suspend); ATF_TC_BODY(file_suspend, tc) { aio_file_test(suspend, NULL, false); } ATF_TC_WITHOUT_HEAD(file_thread); ATF_TC_BODY(file_thread, tc) { aio_file_test(poll_signaled, setup_thread(), false); } ATF_TC_WITHOUT_HEAD(file_waitcomplete); ATF_TC_BODY(file_waitcomplete, tc) { aio_file_test(waitcomplete, NULL, false); } #define FIFO_LEN 256 #define FIFO_PATHNAME "testfifo" static void aio_fifo_test(completion comp, struct sigevent *sev) { int error, read_fd = -1, write_fd = -1; struct aio_context ac; ATF_REQUIRE_KERNEL_MODULE("aio"); ATF_REQUIRE_UNSAFE_AIO(); ATF_REQUIRE_MSG(mkfifo(FIFO_PATHNAME, 0600) != -1, "mkfifo failed: %s", strerror(errno)); read_fd = open(FIFO_PATHNAME, O_RDONLY | O_NONBLOCK); if (read_fd == -1) { error = errno; errno = error; atf_tc_fail("read_fd open failed: %s", strerror(errno)); } write_fd = open(FIFO_PATHNAME, O_WRONLY); if (write_fd == -1) { error = errno; errno = error; atf_tc_fail("write_fd open failed: %s", strerror(errno)); } aio_context_init(&ac, read_fd, write_fd, FIFO_LEN); aio_write_test(&ac, comp, sev); aio_read_test(&ac, comp, sev); close(read_fd); close(write_fd); } ATF_TC_WITHOUT_HEAD(fifo_poll); ATF_TC_BODY(fifo_poll, tc) { aio_fifo_test(poll, NULL); } ATF_TC_WITHOUT_HEAD(fifo_signal); ATF_TC_BODY(fifo_signal, tc) { aio_fifo_test(poll_signaled, setup_signal()); } ATF_TC_WITHOUT_HEAD(fifo_suspend); ATF_TC_BODY(fifo_suspend, tc) { aio_fifo_test(suspend, NULL); } ATF_TC_WITHOUT_HEAD(fifo_thread); ATF_TC_BODY(fifo_thread, tc) { aio_fifo_test(poll_signaled, setup_thread()); } ATF_TC_WITHOUT_HEAD(fifo_waitcomplete); ATF_TC_BODY(fifo_waitcomplete, tc) { aio_fifo_test(waitcomplete, NULL); } #define UNIX_SOCKETPAIR_LEN 256 static void aio_unix_socketpair_test(completion comp, struct sigevent *sev, bool vectored) { struct aio_context ac; struct rusage ru_before, ru_after; int sockets[2]; ATF_REQUIRE_KERNEL_MODULE("aio"); ATF_REQUIRE_MSG(socketpair(PF_UNIX, SOCK_STREAM, 0, sockets) != -1, "socketpair failed: %s", strerror(errno)); aio_context_init(&ac, sockets[0], sockets[1], UNIX_SOCKETPAIR_LEN); ATF_REQUIRE_MSG(getrusage(RUSAGE_SELF, &ru_before) != -1, "getrusage failed: %s", strerror(errno)); if (vectored) { aio_writev_test(&ac, comp, sev); aio_readv_test(&ac, comp, sev); } else { aio_write_test(&ac, comp, sev); aio_read_test(&ac, comp, sev); } ATF_REQUIRE_MSG(getrusage(RUSAGE_SELF, &ru_after) != -1, "getrusage failed: %s", strerror(errno)); ATF_REQUIRE(ru_after.ru_msgsnd == ru_before.ru_msgsnd + 1); ATF_REQUIRE(ru_after.ru_msgrcv == ru_before.ru_msgrcv + 1); close(sockets[0]); close(sockets[1]); } ATF_TC_WITHOUT_HEAD(socket_poll); ATF_TC_BODY(socket_poll, tc) { aio_unix_socketpair_test(poll, NULL, false); } ATF_TC_WITHOUT_HEAD(socket_signal); ATF_TC_BODY(socket_signal, tc) { aio_unix_socketpair_test(poll_signaled, setup_signal(), false); } ATF_TC_WITHOUT_HEAD(socket_suspend); ATF_TC_BODY(socket_suspend, tc) { aio_unix_socketpair_test(suspend, NULL, false); } ATF_TC_WITHOUT_HEAD(socket_thread); ATF_TC_BODY(socket_thread, tc) { aio_unix_socketpair_test(poll_signaled, setup_thread(), false); } ATF_TC_WITHOUT_HEAD(socket_waitcomplete); ATF_TC_BODY(socket_waitcomplete, tc) { aio_unix_socketpair_test(waitcomplete, NULL, false); } struct aio_pty_arg { int apa_read_fd; int apa_write_fd; }; #define PTY_LEN 256 static void aio_pty_test(completion comp, struct sigevent *sev) { struct aio_context ac; int read_fd, write_fd; struct termios ts; int error; ATF_REQUIRE_KERNEL_MODULE("aio"); ATF_REQUIRE_UNSAFE_AIO(); ATF_REQUIRE_MSG(openpty(&read_fd, &write_fd, NULL, NULL, NULL) == 0, "openpty failed: %s", strerror(errno)); if (tcgetattr(write_fd, &ts) < 0) { error = errno; errno = error; atf_tc_fail("tcgetattr failed: %s", strerror(errno)); } cfmakeraw(&ts); if (tcsetattr(write_fd, TCSANOW, &ts) < 0) { error = errno; errno = error; atf_tc_fail("tcsetattr failed: %s", strerror(errno)); } aio_context_init(&ac, read_fd, write_fd, PTY_LEN); aio_write_test(&ac, comp, sev); aio_read_test(&ac, comp, sev); close(read_fd); close(write_fd); } ATF_TC_WITHOUT_HEAD(pty_poll); ATF_TC_BODY(pty_poll, tc) { aio_pty_test(poll, NULL); } ATF_TC_WITHOUT_HEAD(pty_signal); ATF_TC_BODY(pty_signal, tc) { aio_pty_test(poll_signaled, setup_signal()); } ATF_TC_WITHOUT_HEAD(pty_suspend); ATF_TC_BODY(pty_suspend, tc) { aio_pty_test(suspend, NULL); } ATF_TC_WITHOUT_HEAD(pty_thread); ATF_TC_BODY(pty_thread, tc) { aio_pty_test(poll_signaled, setup_thread()); } ATF_TC_WITHOUT_HEAD(pty_waitcomplete); ATF_TC_BODY(pty_waitcomplete, tc) { aio_pty_test(waitcomplete, NULL); } #define PIPE_LEN 256 static void aio_pipe_test(completion comp, struct sigevent *sev) { struct aio_context ac; int pipes[2]; ATF_REQUIRE_KERNEL_MODULE("aio"); ATF_REQUIRE_UNSAFE_AIO(); ATF_REQUIRE_MSG(pipe(pipes) != -1, "pipe failed: %s", strerror(errno)); aio_context_init(&ac, pipes[0], pipes[1], PIPE_LEN); aio_write_test(&ac, comp, sev); aio_read_test(&ac, comp, sev); close(pipes[0]); close(pipes[1]); } ATF_TC_WITHOUT_HEAD(pipe_poll); ATF_TC_BODY(pipe_poll, tc) { aio_pipe_test(poll, NULL); } ATF_TC_WITHOUT_HEAD(pipe_signal); ATF_TC_BODY(pipe_signal, tc) { aio_pipe_test(poll_signaled, setup_signal()); } ATF_TC_WITHOUT_HEAD(pipe_suspend); ATF_TC_BODY(pipe_suspend, tc) { aio_pipe_test(suspend, NULL); } ATF_TC_WITHOUT_HEAD(pipe_thread); ATF_TC_BODY(pipe_thread, tc) { aio_pipe_test(poll_signaled, setup_thread()); } ATF_TC_WITHOUT_HEAD(pipe_waitcomplete); ATF_TC_BODY(pipe_waitcomplete, tc) { aio_pipe_test(waitcomplete, NULL); } #define MD_LEN GLOBAL_MAX #define MDUNIT_LINK "mdunit_link" static int aio_md_setup(void) { int error, fd, mdctl_fd, unit; char pathname[PATH_MAX]; struct md_ioctl mdio; char buf[80]; ATF_REQUIRE_KERNEL_MODULE("aio"); mdctl_fd = open("/dev/" MDCTL_NAME, O_RDWR, 0); ATF_REQUIRE_MSG(mdctl_fd != -1, "opening /dev/%s failed: %s", MDCTL_NAME, strerror(errno)); bzero(&mdio, sizeof(mdio)); mdio.md_version = MDIOVERSION; mdio.md_type = MD_MALLOC; mdio.md_options = MD_AUTOUNIT | MD_COMPRESS; mdio.md_mediasize = GLOBAL_MAX; mdio.md_sectorsize = 512; strlcpy(buf, __func__, sizeof(buf)); mdio.md_label = buf; if (ioctl(mdctl_fd, MDIOCATTACH, &mdio) < 0) { error = errno; errno = error; atf_tc_fail("ioctl MDIOCATTACH failed: %s", strerror(errno)); } close(mdctl_fd); /* Store the md unit number in a symlink for future cleanup */ unit = mdio.md_unit; snprintf(buf, sizeof(buf), "%d", unit); ATF_REQUIRE_EQ(0, symlink(buf, MDUNIT_LINK)); snprintf(pathname, PATH_MAX, "/dev/md%d", unit); fd = open(pathname, O_RDWR); ATF_REQUIRE_MSG(fd != -1, "opening %s failed: %s", pathname, strerror(errno)); return (fd); } static void aio_md_cleanup(void) { struct md_ioctl mdio; int mdctl_fd, n, unit; char buf[80]; mdctl_fd = open("/dev/" MDCTL_NAME, O_RDWR, 0); if (mdctl_fd < 0) { fprintf(stderr, "opening /dev/%s failed: %s\n", MDCTL_NAME, strerror(errno)); return; } n = readlink(MDUNIT_LINK, buf, sizeof(buf) - 1); if (n > 0) { buf[n] = '\0'; if (sscanf(buf, "%d", &unit) == 1 && unit >= 0) { bzero(&mdio, sizeof(mdio)); mdio.md_version = MDIOVERSION; mdio.md_unit = unit; if (ioctl(mdctl_fd, MDIOCDETACH, &mdio) == -1) { fprintf(stderr, "ioctl MDIOCDETACH unit %d failed: %s\n", unit, strerror(errno)); } } } close(mdctl_fd); } static void aio_md_test(completion comp, struct sigevent *sev, bool vectored) { struct aio_context ac; int fd; fd = aio_md_setup(); aio_context_init(&ac, fd, fd, MD_LEN); if (vectored) { aio_writev_test(&ac, comp, sev); aio_readv_test(&ac, comp, sev); } else { aio_write_test(&ac, comp, sev); aio_read_test(&ac, comp, sev); } close(fd); } ATF_TC_WITH_CLEANUP(md_poll); ATF_TC_HEAD(md_poll, tc) { atf_tc_set_md_var(tc, "require.user", "root"); } ATF_TC_BODY(md_poll, tc) { aio_md_test(poll, NULL, false); } ATF_TC_CLEANUP(md_poll, tc) { aio_md_cleanup(); } ATF_TC_WITH_CLEANUP(md_signal); ATF_TC_HEAD(md_signal, tc) { atf_tc_set_md_var(tc, "require.user", "root"); } ATF_TC_BODY(md_signal, tc) { aio_md_test(poll_signaled, setup_signal(), false); } ATF_TC_CLEANUP(md_signal, tc) { aio_md_cleanup(); } ATF_TC_WITH_CLEANUP(md_suspend); ATF_TC_HEAD(md_suspend, tc) { atf_tc_set_md_var(tc, "require.user", "root"); } ATF_TC_BODY(md_suspend, tc) { aio_md_test(suspend, NULL, false); } ATF_TC_CLEANUP(md_suspend, tc) { aio_md_cleanup(); } ATF_TC_WITH_CLEANUP(md_thread); ATF_TC_HEAD(md_thread, tc) { atf_tc_set_md_var(tc, "require.user", "root"); } ATF_TC_BODY(md_thread, tc) { aio_md_test(poll_signaled, setup_thread(), false); } ATF_TC_CLEANUP(md_thread, tc) { aio_md_cleanup(); } ATF_TC_WITH_CLEANUP(md_waitcomplete); ATF_TC_HEAD(md_waitcomplete, tc) { atf_tc_set_md_var(tc, "require.user", "root"); } ATF_TC_BODY(md_waitcomplete, tc) { aio_md_test(waitcomplete, NULL, false); } ATF_TC_CLEANUP(md_waitcomplete, tc) { aio_md_cleanup(); } #define ZVOL_VDEV_PATHNAME "test_vdev" #define POOL_SIZE (1 << 28) /* 256 MB */ #define ZVOL_SIZE "64m" #define POOL_NAME "aio_testpool" #define ZVOL_NAME "aio_testvol" static int aio_zvol_setup(void) { FILE *pidfile; int fd; pid_t pid; char pool_name[80]; char cmd[160]; char zvol_name[160]; char devname[160]; ATF_REQUIRE_KERNEL_MODULE("aio"); ATF_REQUIRE_KERNEL_MODULE("zfs"); fd = open(ZVOL_VDEV_PATHNAME, O_RDWR | O_CREAT, 0600); ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno)); ATF_REQUIRE_EQ_MSG(0, ftruncate(fd, POOL_SIZE), "ftruncate failed: %s", strerror(errno)); close(fd); pid = getpid(); pidfile = fopen("pidfile", "w"); ATF_REQUIRE_MSG(NULL != pidfile, "fopen: %s", strerror(errno)); fprintf(pidfile, "%d", pid); fclose(pidfile); snprintf(pool_name, sizeof(pool_name), POOL_NAME ".%d", pid); snprintf(zvol_name, sizeof(zvol_name), "%s/" ZVOL_NAME, pool_name); snprintf(cmd, sizeof(cmd), "zpool create %s $PWD/" ZVOL_VDEV_PATHNAME, pool_name); ATF_REQUIRE_EQ_MSG(0, system(cmd), "zpool create failed: %s", strerror(errno)); snprintf(cmd, sizeof(cmd), "zfs create -o volblocksize=8192 -o volmode=dev -V " ZVOL_SIZE " %s", zvol_name); ATF_REQUIRE_EQ_MSG(0, system(cmd), "zfs create failed: %s", strerror(errno)); snprintf(devname, sizeof(devname), "/dev/zvol/%s", zvol_name); do { fd = open(devname, O_RDWR); } while (fd == -1 && errno == EINTR) ; ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno)); return (fd); } static void aio_zvol_cleanup(void) { FILE *pidfile; pid_t testpid; char cmd[160]; pidfile = fopen("pidfile", "r"); if (pidfile == NULL && errno == ENOENT) { /* Setup probably failed */ return; } ATF_REQUIRE_MSG(NULL != pidfile, "fopen: %s", strerror(errno)); ATF_REQUIRE_EQ(1, fscanf(pidfile, "%d", &testpid)); fclose(pidfile); snprintf(cmd, sizeof(cmd), "zpool destroy " POOL_NAME ".%d", testpid); system(cmd); } ATF_TC_WITHOUT_HEAD(aio_large_read_test); ATF_TC_BODY(aio_large_read_test, tc) { struct aiocb cb, *cbp; ssize_t nread; size_t len; int fd; #ifdef __LP64__ int clamped; #endif ATF_REQUIRE_KERNEL_MODULE("aio"); ATF_REQUIRE_UNSAFE_AIO(); #ifdef __LP64__ len = sizeof(clamped); if (sysctlbyname("debug.iosize_max_clamp", &clamped, &len, NULL, 0) == -1) atf_libc_error(errno, "Failed to read debug.iosize_max_clamp"); #endif /* Determine the maximum supported read(2) size. */ len = SSIZE_MAX; #ifdef __LP64__ if (clamped) len = INT_MAX; #endif fd = open(FILE_PATHNAME, O_RDWR | O_CREAT, 0600); ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno)); unlink(FILE_PATHNAME); memset(&cb, 0, sizeof(cb)); cb.aio_nbytes = len; cb.aio_fildes = fd; cb.aio_buf = NULL; if (aio_read(&cb) == -1) atf_tc_fail("aio_read() of maximum read size failed: %s", strerror(errno)); nread = aio_waitcomplete(&cbp, NULL); if (nread == -1) atf_tc_fail("aio_waitcomplete() failed: %s", strerror(errno)); if (nread != 0) atf_tc_fail("aio_read() from empty file returned data: %zd", nread); memset(&cb, 0, sizeof(cb)); cb.aio_nbytes = len + 1; cb.aio_fildes = fd; cb.aio_buf = NULL; if (aio_read(&cb) == -1) { if (errno == EINVAL) goto finished; atf_tc_fail("aio_read() of too large read size failed: %s", strerror(errno)); } nread = aio_waitcomplete(&cbp, NULL); if (nread == -1) { if (errno == EINVAL) goto finished; atf_tc_fail("aio_waitcomplete() failed: %s", strerror(errno)); } atf_tc_fail("aio_read() of too large read size returned: %zd", nread); finished: close(fd); } /* * This tests for a bug where arriving socket data can wakeup multiple * AIO read requests resulting in an uncancellable request. */ ATF_TC_WITHOUT_HEAD(aio_socket_two_reads); ATF_TC_BODY(aio_socket_two_reads, tc) { struct ioreq { struct aiocb iocb; char buffer[1024]; } ioreq[2]; struct aiocb *iocb; unsigned i; int s[2]; char c; ATF_REQUIRE_KERNEL_MODULE("aio"); #if __FreeBSD_version < 1100101 aft_tc_skip("kernel version %d is too old (%d required)", __FreeBSD_version, 1100101); #endif ATF_REQUIRE(socketpair(PF_UNIX, SOCK_STREAM, 0, s) != -1); /* Queue two read requests. */ memset(&ioreq, 0, sizeof(ioreq)); for (i = 0; i < nitems(ioreq); i++) { ioreq[i].iocb.aio_nbytes = sizeof(ioreq[i].buffer); ioreq[i].iocb.aio_fildes = s[0]; ioreq[i].iocb.aio_buf = ioreq[i].buffer; ATF_REQUIRE(aio_read(&ioreq[i].iocb) == 0); } /* Send a single byte. This should complete one request. */ c = 0xc3; ATF_REQUIRE(write(s[1], &c, sizeof(c)) == 1); ATF_REQUIRE(aio_waitcomplete(&iocb, NULL) == 1); /* Determine which request completed and verify the data was read. */ if (iocb == &ioreq[0].iocb) i = 0; else i = 1; ATF_REQUIRE(ioreq[i].buffer[0] == c); i ^= 1; /* * Try to cancel the other request. On broken systems this * will fail and the process will hang on exit. */ ATF_REQUIRE(aio_error(&ioreq[i].iocb) == EINPROGRESS); ATF_REQUIRE(aio_cancel(s[0], &ioreq[i].iocb) == AIO_CANCELED); close(s[1]); close(s[0]); } static void aio_socket_blocking_short_write_test(bool vectored) { struct aiocb iocb, *iocbp; struct iovec iov[2]; char *buffer[2]; ssize_t done, r; int buffer_size, sb_size; socklen_t len; int s[2]; ATF_REQUIRE_KERNEL_MODULE("aio"); ATF_REQUIRE(socketpair(PF_UNIX, SOCK_STREAM, 0, s) != -1); len = sizeof(sb_size); ATF_REQUIRE(getsockopt(s[0], SOL_SOCKET, SO_RCVBUF, &sb_size, &len) != -1); ATF_REQUIRE(len == sizeof(sb_size)); buffer_size = sb_size; ATF_REQUIRE(getsockopt(s[1], SOL_SOCKET, SO_SNDBUF, &sb_size, &len) != -1); ATF_REQUIRE(len == sizeof(sb_size)); if (sb_size > buffer_size) buffer_size = sb_size; /* * Use twice the size of the MAX(receive buffer, send buffer) * to ensure that the write is split up into multiple writes * internally. */ buffer_size *= 2; buffer[0] = malloc(buffer_size); ATF_REQUIRE(buffer[0] != NULL); buffer[1] = malloc(buffer_size); ATF_REQUIRE(buffer[1] != NULL); srandomdev(); aio_fill_buffer(buffer[1], buffer_size, random()); memset(&iocb, 0, sizeof(iocb)); iocb.aio_fildes = s[1]; if (vectored) { iov[0].iov_base = buffer[1]; iov[0].iov_len = buffer_size / 2 + 1; iov[1].iov_base = buffer[1] + buffer_size / 2 + 1; iov[1].iov_len = buffer_size / 2 - 1; iocb.aio_iov = iov; iocb.aio_iovcnt = 2; r = aio_writev(&iocb); ATF_CHECK_EQ_MSG(0, r, "aio_writev returned %zd", r); } else { iocb.aio_buf = buffer[1]; iocb.aio_nbytes = buffer_size; r = aio_write(&iocb); ATF_CHECK_EQ_MSG(0, r, "aio_writev returned %zd", r); } done = recv(s[0], buffer[0], buffer_size, MSG_WAITALL); ATF_REQUIRE(done == buffer_size); done = aio_waitcomplete(&iocbp, NULL); ATF_REQUIRE(iocbp == &iocb); ATF_REQUIRE(done == buffer_size); ATF_REQUIRE(memcmp(buffer[0], buffer[1], buffer_size) == 0); close(s[1]); close(s[0]); } /* * This test ensures that aio_write() on a blocking socket of a "large" * buffer does not return a short completion. */ ATF_TC_WITHOUT_HEAD(aio_socket_blocking_short_write); ATF_TC_BODY(aio_socket_blocking_short_write, tc) { aio_socket_blocking_short_write_test(false); } /* * Like aio_socket_blocking_short_write, but also tests that partially * completed vectored sends can be retried correctly. */ ATF_TC_WITHOUT_HEAD(aio_socket_blocking_short_write_vectored); ATF_TC_BODY(aio_socket_blocking_short_write_vectored, tc) { aio_socket_blocking_short_write_test(true); } /* * This test verifies that cancelling a partially completed socket write * returns a short write rather than ECANCELED. */ ATF_TC_WITHOUT_HEAD(aio_socket_short_write_cancel); ATF_TC_BODY(aio_socket_short_write_cancel, tc) { struct aiocb iocb, *iocbp; char *buffer[2]; ssize_t done; int buffer_size, sb_size; socklen_t len; int s[2]; ATF_REQUIRE_KERNEL_MODULE("aio"); ATF_REQUIRE(socketpair(PF_UNIX, SOCK_STREAM, 0, s) != -1); len = sizeof(sb_size); ATF_REQUIRE(getsockopt(s[0], SOL_SOCKET, SO_RCVBUF, &sb_size, &len) != -1); ATF_REQUIRE(len == sizeof(sb_size)); buffer_size = sb_size; ATF_REQUIRE(getsockopt(s[1], SOL_SOCKET, SO_SNDBUF, &sb_size, &len) != -1); ATF_REQUIRE(len == sizeof(sb_size)); if (sb_size > buffer_size) buffer_size = sb_size; /* * Use three times the size of the MAX(receive buffer, send * buffer) for the write to ensure that the write is split up * into multiple writes internally. The recv() ensures that * the write has partially completed, but a remaining size of * two buffers should ensure that the write has not completed * fully when it is cancelled. */ buffer[0] = malloc(buffer_size); ATF_REQUIRE(buffer[0] != NULL); buffer[1] = malloc(buffer_size * 3); ATF_REQUIRE(buffer[1] != NULL); srandomdev(); aio_fill_buffer(buffer[1], buffer_size * 3, random()); memset(&iocb, 0, sizeof(iocb)); iocb.aio_fildes = s[1]; iocb.aio_buf = buffer[1]; iocb.aio_nbytes = buffer_size * 3; ATF_REQUIRE(aio_write(&iocb) == 0); done = recv(s[0], buffer[0], buffer_size, MSG_WAITALL); ATF_REQUIRE(done == buffer_size); ATF_REQUIRE(aio_error(&iocb) == EINPROGRESS); ATF_REQUIRE(aio_cancel(s[1], &iocb) == AIO_NOTCANCELED); done = aio_waitcomplete(&iocbp, NULL); ATF_REQUIRE(iocbp == &iocb); ATF_REQUIRE(done >= buffer_size && done <= buffer_size * 2); ATF_REQUIRE(memcmp(buffer[0], buffer[1], buffer_size) == 0); close(s[1]); close(s[0]); } /* * test aio_fsync's behavior with bad inputs */ ATF_TC_WITHOUT_HEAD(aio_fsync_errors); ATF_TC_BODY(aio_fsync_errors, tc) { int fd; struct aiocb iocb; ATF_REQUIRE_KERNEL_MODULE("aio"); ATF_REQUIRE_UNSAFE_AIO(); fd = open(FILE_PATHNAME, O_RDWR | O_CREAT, 0600); ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno)); unlink(FILE_PATHNAME); /* aio_fsync should return EINVAL unless op is O_SYNC or O_DSYNC */ memset(&iocb, 0, sizeof(iocb)); iocb.aio_fildes = fd; ATF_CHECK_EQ(-1, aio_fsync(666, &iocb)); ATF_CHECK_EQ(EINVAL, errno); /* aio_fsync should return EBADF if fd is not a valid descriptor */ memset(&iocb, 0, sizeof(iocb)); iocb.aio_fildes = 666; ATF_CHECK_EQ(-1, aio_fsync(O_SYNC, &iocb)); ATF_CHECK_EQ(EBADF, errno); /* aio_fsync should return EINVAL if sigev_notify is invalid */ memset(&iocb, 0, sizeof(iocb)); iocb.aio_fildes = fd; iocb.aio_sigevent.sigev_notify = 666; ATF_CHECK_EQ(-1, aio_fsync(666, &iocb)); ATF_CHECK_EQ(EINVAL, errno); } /* * This test just performs a basic test of aio_fsync(). */ static void aio_fsync_test(int op) { struct aiocb synccb, *iocbp; struct { struct aiocb iocb; bool done; char *buffer; } buffers[16]; struct stat sb; ssize_t rval; unsigned i; int fd; ATF_REQUIRE_KERNEL_MODULE("aio"); ATF_REQUIRE_UNSAFE_AIO(); fd = open(FILE_PATHNAME, O_RDWR | O_CREAT, 0600); ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno)); unlink(FILE_PATHNAME); ATF_REQUIRE(fstat(fd, &sb) == 0); ATF_REQUIRE(sb.st_blksize != 0); ATF_REQUIRE(ftruncate(fd, sb.st_blksize * nitems(buffers)) == 0); /* * Queue several asynchronous write requests. Hopefully this * forces the aio_fsync() request to be deferred. There is no * reliable way to guarantee that however. */ srandomdev(); for (i = 0; i < nitems(buffers); i++) { buffers[i].done = false; memset(&buffers[i].iocb, 0, sizeof(buffers[i].iocb)); buffers[i].buffer = malloc(sb.st_blksize); aio_fill_buffer(buffers[i].buffer, sb.st_blksize, random()); buffers[i].iocb.aio_fildes = fd; buffers[i].iocb.aio_buf = buffers[i].buffer; buffers[i].iocb.aio_nbytes = sb.st_blksize; buffers[i].iocb.aio_offset = sb.st_blksize * i; ATF_REQUIRE(aio_write(&buffers[i].iocb) == 0); } /* Queue the aio_fsync request. */ memset(&synccb, 0, sizeof(synccb)); synccb.aio_fildes = fd; ATF_REQUIRE(aio_fsync(op, &synccb) == 0); /* Wait for requests to complete. */ for (;;) { next: rval = aio_waitcomplete(&iocbp, NULL); ATF_REQUIRE(iocbp != NULL); if (iocbp == &synccb) { ATF_REQUIRE(rval == 0); break; } for (i = 0; i < nitems(buffers); i++) { if (iocbp == &buffers[i].iocb) { ATF_REQUIRE(buffers[i].done == false); ATF_REQUIRE(rval == sb.st_blksize); buffers[i].done = true; goto next; } } ATF_REQUIRE_MSG(false, "unmatched AIO request"); } for (i = 0; i < nitems(buffers); i++) ATF_REQUIRE_MSG(buffers[i].done, "AIO request %u did not complete", i); close(fd); } ATF_TC_WITHOUT_HEAD(aio_fsync_sync_test); ATF_TC_BODY(aio_fsync_sync_test, tc) { aio_fsync_test(O_SYNC); } ATF_TC_WITHOUT_HEAD(aio_fsync_dsync_test); ATF_TC_BODY(aio_fsync_dsync_test, tc) { aio_fsync_test(O_DSYNC); } /* * We shouldn't be able to DoS the system by setting iov_len to an insane * value */ ATF_TC_WITHOUT_HEAD(aio_writev_dos_iov_len); ATF_TC_BODY(aio_writev_dos_iov_len, tc) { struct aiocb aio; const struct aiocb *const iocbs[] = {&aio}; const char *wbuf = "Hello, world!"; struct iovec iov[1]; ssize_t len, r; int fd; ATF_REQUIRE_KERNEL_MODULE("aio"); ATF_REQUIRE_UNSAFE_AIO(); fd = open("testfile", O_RDWR | O_CREAT, 0600); ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno)); len = strlen(wbuf); iov[0].iov_base = __DECONST(void*, wbuf); iov[0].iov_len = 1 << 30; bzero(&aio, sizeof(aio)); aio.aio_fildes = fd; aio.aio_offset = 0; aio.aio_iov = iov; aio.aio_iovcnt = 1; r = aio_writev(&aio); ATF_CHECK_EQ_MSG(0, r, "aio_writev returned %zd", r); ATF_REQUIRE_EQ(0, aio_suspend(iocbs, 1, NULL)); r = aio_return(&aio); ATF_CHECK_EQ_MSG(-1, r, "aio_return returned %zd", r); ATF_CHECK_MSG(errno == EFAULT || errno == EINVAL, "aio_writev: %s", strerror(errno)); close(fd); } /* * We shouldn't be able to DoS the system by setting aio_iovcnt to an insane * value */ ATF_TC_WITHOUT_HEAD(aio_writev_dos_iovcnt); ATF_TC_BODY(aio_writev_dos_iovcnt, tc) { struct aiocb aio; const char *wbuf = "Hello, world!"; struct iovec iov[1]; ssize_t len; int fd; ATF_REQUIRE_KERNEL_MODULE("aio"); ATF_REQUIRE_UNSAFE_AIO(); fd = open("testfile", O_RDWR | O_CREAT, 0600); ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno)); len = strlen(wbuf); iov[0].iov_base = __DECONST(void*, wbuf); iov[0].iov_len = len; bzero(&aio, sizeof(aio)); aio.aio_fildes = fd; aio.aio_offset = 0; aio.aio_iov = iov; aio.aio_iovcnt = 1 << 30; ATF_REQUIRE_EQ(-1, aio_writev(&aio)); ATF_CHECK_EQ(EINVAL, errno); close(fd); } ATF_TC_WITH_CLEANUP(aio_writev_efault); ATF_TC_HEAD(aio_writev_efault, tc) { atf_tc_set_md_var(tc, "descr", "Vectored AIO should gracefully handle invalid addresses"); atf_tc_set_md_var(tc, "require.user", "root"); } ATF_TC_BODY(aio_writev_efault, tc) { struct aiocb aio; ssize_t buflen; char *buffer; struct iovec iov[2]; long seed; int fd; ATF_REQUIRE_KERNEL_MODULE("aio"); ATF_REQUIRE_UNSAFE_AIO(); fd = aio_md_setup(); seed = random(); buflen = 4096; buffer = malloc(buflen); aio_fill_buffer(buffer, buflen, seed); iov[0].iov_base = buffer; iov[0].iov_len = buflen; iov[1].iov_base = (void*)-1; /* Invalid! */ iov[1].iov_len = buflen; bzero(&aio, sizeof(aio)); aio.aio_fildes = fd; aio.aio_offset = 0; aio.aio_iov = iov; aio.aio_iovcnt = nitems(iov); ATF_REQUIRE_EQ(-1, aio_writev(&aio)); ATF_CHECK_EQ(EFAULT, errno); close(fd); } ATF_TC_CLEANUP(aio_writev_efault, tc) { aio_md_cleanup(); } ATF_TC_WITHOUT_HEAD(aio_writev_empty_file_poll); ATF_TC_BODY(aio_writev_empty_file_poll, tc) { struct aiocb aio; int fd; ATF_REQUIRE_KERNEL_MODULE("aio"); ATF_REQUIRE_UNSAFE_AIO(); fd = open("testfile", O_RDWR | O_CREAT, 0600); ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno)); bzero(&aio, sizeof(aio)); aio.aio_fildes = fd; aio.aio_offset = 0; aio.aio_iovcnt = 0; ATF_REQUIRE_EQ(0, aio_writev(&aio)); ATF_REQUIRE_EQ(0, suspend(&aio)); close(fd); } ATF_TC_WITHOUT_HEAD(aio_writev_empty_file_signal); ATF_TC_BODY(aio_writev_empty_file_signal, tc) { struct aiocb aio; int fd; ATF_REQUIRE_KERNEL_MODULE("aio"); ATF_REQUIRE_UNSAFE_AIO(); fd = open("testfile", O_RDWR | O_CREAT, 0600); ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno)); bzero(&aio, sizeof(aio)); aio.aio_fildes = fd; aio.aio_offset = 0; aio.aio_iovcnt = 0; aio.aio_sigevent = *setup_signal(); ATF_REQUIRE_EQ(0, aio_writev(&aio)); ATF_REQUIRE_EQ(0, poll_signaled(&aio)); close(fd); } // aio_writev and aio_readv should still work even if the iovcnt is greater // than the number of buffered AIO operations permitted per process. ATF_TC_WITH_CLEANUP(vectored_big_iovcnt); ATF_TC_HEAD(vectored_big_iovcnt, tc) { atf_tc_set_md_var(tc, "descr", "Vectored AIO should still work even if the iovcnt is greater than " "the number of buffered AIO operations permitted by the process"); atf_tc_set_md_var(tc, "require.user", "root"); } ATF_TC_BODY(vectored_big_iovcnt, tc) { struct aiocb aio; struct iovec *iov; ssize_t len, buflen; char *buffer; const char *oid = "vfs.aio.max_buf_aio"; long seed; int max_buf_aio; int fd, i; ssize_t sysctl_len = sizeof(max_buf_aio); ATF_REQUIRE_KERNEL_MODULE("aio"); ATF_REQUIRE_UNSAFE_AIO(); if (sysctlbyname(oid, &max_buf_aio, &sysctl_len, NULL, 0) == -1) atf_libc_error(errno, "Failed to read %s", oid); seed = random(); buflen = 512 * (max_buf_aio + 1); buffer = malloc(buflen); aio_fill_buffer(buffer, buflen, seed); iov = calloc(max_buf_aio + 1, sizeof(struct iovec)); fd = aio_md_setup(); bzero(&aio, sizeof(aio)); aio.aio_fildes = fd; aio.aio_offset = 0; for (i = 0; i < max_buf_aio + 1; i++) { iov[i].iov_base = &buffer[i * 512]; iov[i].iov_len = 512; } aio.aio_iov = iov; aio.aio_iovcnt = max_buf_aio + 1; if (aio_writev(&aio) < 0) atf_tc_fail("aio_writev failed: %s", strerror(errno)); len = poll(&aio); if (len < 0) atf_tc_fail("aio failed: %s", strerror(errno)); if (len != buflen) atf_tc_fail("aio short write (%jd)", (intmax_t)len); bzero(&aio, sizeof(aio)); aio.aio_fildes = fd; aio.aio_offset = 0; aio.aio_iov = iov; aio.aio_iovcnt = max_buf_aio + 1; if (aio_readv(&aio) < 0) atf_tc_fail("aio_readv failed: %s", strerror(errno)); len = poll(&aio); if (len < 0) atf_tc_fail("aio failed: %s", strerror(errno)); if (len != buflen) atf_tc_fail("aio short read (%jd)", (intmax_t)len); if (aio_test_buffer(buffer, buflen, seed) == 0) atf_tc_fail("buffer mismatched"); close(fd); } ATF_TC_CLEANUP(vectored_big_iovcnt, tc) { aio_md_cleanup(); } ATF_TC_WITHOUT_HEAD(vectored_file_poll); ATF_TC_BODY(vectored_file_poll, tc) { aio_file_test(poll, NULL, true); } +ATF_TC_WITHOUT_HEAD(vectored_thread); +ATF_TC_BODY(vectored_thread, tc) +{ + aio_file_test(poll_signaled, setup_thread(), true); +} + ATF_TC_WITH_CLEANUP(vectored_md_poll); ATF_TC_HEAD(vectored_md_poll, tc) { atf_tc_set_md_var(tc, "require.user", "root"); } ATF_TC_BODY(vectored_md_poll, tc) { aio_md_test(poll, NULL, true); } ATF_TC_CLEANUP(vectored_md_poll, tc) { aio_md_cleanup(); } ATF_TC_WITHOUT_HEAD(vectored_socket_poll); ATF_TC_BODY(vectored_socket_poll, tc) { aio_unix_socketpair_test(poll, NULL, true); } // aio_writev and aio_readv should still work even if the iov contains elements // that aren't a multiple of the device's sector size, and even if the total // amount if I/O _is_ a multiple of the device's sector size. ATF_TC_WITH_CLEANUP(vectored_unaligned); ATF_TC_HEAD(vectored_unaligned, tc) { atf_tc_set_md_var(tc, "descr", "Vectored AIO should still work even if the iov contains elements " "that aren't a multiple of the sector size."); atf_tc_set_md_var(tc, "require.user", "root"); } ATF_TC_BODY(vectored_unaligned, tc) { struct aio_context ac; struct aiocb aio; struct iovec iov[3]; ssize_t len, total_len; int fd; ATF_REQUIRE_KERNEL_MODULE("aio"); ATF_REQUIRE_UNSAFE_AIO(); /* * Use a zvol with volmode=dev, so it will allow .d_write with * unaligned uio. geom devices use physio, which doesn't allow that. */ fd = aio_zvol_setup(); aio_context_init(&ac, fd, fd, FILE_LEN); /* Break the buffer into 3 parts: * * A 4kB part, aligned to 4kB * * Two other parts that add up to 4kB: * - 256B * - 4kB - 256B */ iov[0].iov_base = ac.ac_buffer; iov[0].iov_len = 4096; iov[1].iov_base = (void*)((uintptr_t)iov[0].iov_base + iov[0].iov_len); iov[1].iov_len = 256; iov[2].iov_base = (void*)((uintptr_t)iov[1].iov_base + iov[1].iov_len); iov[2].iov_len = 4096 - iov[1].iov_len; total_len = iov[0].iov_len + iov[1].iov_len + iov[2].iov_len; bzero(&aio, sizeof(aio)); aio.aio_fildes = ac.ac_write_fd; aio.aio_offset = 0; aio.aio_iov = iov; aio.aio_iovcnt = 3; if (aio_writev(&aio) < 0) atf_tc_fail("aio_writev failed: %s", strerror(errno)); len = poll(&aio); if (len < 0) atf_tc_fail("aio failed: %s", strerror(errno)); if (len != total_len) atf_tc_fail("aio short write (%jd)", (intmax_t)len); bzero(&aio, sizeof(aio)); aio.aio_fildes = ac.ac_read_fd; aio.aio_offset = 0; aio.aio_iov = iov; aio.aio_iovcnt = 3; if (aio_readv(&aio) < 0) atf_tc_fail("aio_readv failed: %s", strerror(errno)); len = poll(&aio); ATF_REQUIRE_MSG(aio_test_buffer(ac.ac_buffer, total_len, ac.ac_seed) != 0, "aio_test_buffer: internal error"); close(fd); } ATF_TC_CLEANUP(vectored_unaligned, tc) { aio_zvol_cleanup(); } static void aio_zvol_test(completion comp, struct sigevent *sev, bool vectored) { struct aio_context ac; int fd; fd = aio_zvol_setup(); aio_context_init(&ac, fd, fd, MD_LEN); if (vectored) { aio_writev_test(&ac, comp, sev); aio_readv_test(&ac, comp, sev); } else { aio_write_test(&ac, comp, sev); aio_read_test(&ac, comp, sev); } close(fd); } /* * Note that unlike md, the zvol is not a geom device, does not allow unmapped * buffers, and does not use physio. */ ATF_TC_WITH_CLEANUP(vectored_zvol_poll); ATF_TC_HEAD(vectored_zvol_poll, tc) { atf_tc_set_md_var(tc, "require.user", "root"); } ATF_TC_BODY(vectored_zvol_poll, tc) { aio_zvol_test(poll, NULL, true); } ATF_TC_CLEANUP(vectored_zvol_poll, tc) { aio_zvol_cleanup(); } ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, file_poll); ATF_TP_ADD_TC(tp, file_signal); ATF_TP_ADD_TC(tp, file_suspend); ATF_TP_ADD_TC(tp, file_thread); ATF_TP_ADD_TC(tp, file_waitcomplete); ATF_TP_ADD_TC(tp, fifo_poll); ATF_TP_ADD_TC(tp, fifo_signal); ATF_TP_ADD_TC(tp, fifo_suspend); ATF_TP_ADD_TC(tp, fifo_thread); ATF_TP_ADD_TC(tp, fifo_waitcomplete); ATF_TP_ADD_TC(tp, socket_poll); ATF_TP_ADD_TC(tp, socket_signal); ATF_TP_ADD_TC(tp, socket_suspend); ATF_TP_ADD_TC(tp, socket_thread); ATF_TP_ADD_TC(tp, socket_waitcomplete); ATF_TP_ADD_TC(tp, pty_poll); ATF_TP_ADD_TC(tp, pty_signal); ATF_TP_ADD_TC(tp, pty_suspend); ATF_TP_ADD_TC(tp, pty_thread); ATF_TP_ADD_TC(tp, pty_waitcomplete); ATF_TP_ADD_TC(tp, pipe_poll); ATF_TP_ADD_TC(tp, pipe_signal); ATF_TP_ADD_TC(tp, pipe_suspend); ATF_TP_ADD_TC(tp, pipe_thread); ATF_TP_ADD_TC(tp, pipe_waitcomplete); ATF_TP_ADD_TC(tp, md_poll); ATF_TP_ADD_TC(tp, md_signal); ATF_TP_ADD_TC(tp, md_suspend); ATF_TP_ADD_TC(tp, md_thread); ATF_TP_ADD_TC(tp, md_waitcomplete); ATF_TP_ADD_TC(tp, aio_fsync_errors); ATF_TP_ADD_TC(tp, aio_fsync_sync_test); ATF_TP_ADD_TC(tp, aio_fsync_dsync_test); ATF_TP_ADD_TC(tp, aio_large_read_test); ATF_TP_ADD_TC(tp, aio_socket_two_reads); ATF_TP_ADD_TC(tp, aio_socket_blocking_short_write); ATF_TP_ADD_TC(tp, aio_socket_blocking_short_write_vectored); ATF_TP_ADD_TC(tp, aio_socket_short_write_cancel); ATF_TP_ADD_TC(tp, aio_writev_dos_iov_len); ATF_TP_ADD_TC(tp, aio_writev_dos_iovcnt); ATF_TP_ADD_TC(tp, aio_writev_efault); ATF_TP_ADD_TC(tp, aio_writev_empty_file_poll); ATF_TP_ADD_TC(tp, aio_writev_empty_file_signal); ATF_TP_ADD_TC(tp, vectored_big_iovcnt); ATF_TP_ADD_TC(tp, vectored_file_poll); ATF_TP_ADD_TC(tp, vectored_md_poll); ATF_TP_ADD_TC(tp, vectored_zvol_poll); ATF_TP_ADD_TC(tp, vectored_unaligned); ATF_TP_ADD_TC(tp, vectored_socket_poll); + ATF_TP_ADD_TC(tp, vectored_thread); return (atf_no_error()); }