Index: stable/5/sys/compat/freebsd32/freebsd32_misc.c =================================================================== --- stable/5/sys/compat/freebsd32/freebsd32_misc.c (revision 145377) +++ stable/5/sys/compat/freebsd32/freebsd32_misc.c (revision 145378) @@ -1,1328 +1,1324 @@ /*- * Copyright (c) 2002 Doug Rabson * 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. */ #include __FBSDID("$FreeBSD$"); #include "opt_compat.h" #include #include #include #include #include #include #include #include #include #include #include #include #include /* Must come after sys/malloc.h */ #include #include #include #include #include #include #include #include #include #include #include #include /* Must come after sys/selinfo.h */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include CTASSERT(sizeof(struct timeval32) == 8); CTASSERT(sizeof(struct timespec32) == 8); CTASSERT(sizeof(struct statfs32) == 256); CTASSERT(sizeof(struct rusage32) == 72); int freebsd32_wait4(struct thread *td, struct freebsd32_wait4_args *uap) { int error, status; struct rusage32 ru32; struct rusage ru; error = kern_wait(td, uap->pid, &status, uap->options, &ru); if (error) return (error); if (uap->status != NULL) error = copyout(&status, uap->status, sizeof(status)); if (uap->rusage != NULL && error == 0) { TV_CP(ru, ru32, ru_utime); TV_CP(ru, ru32, ru_stime); CP(ru, ru32, ru_maxrss); CP(ru, ru32, ru_ixrss); CP(ru, ru32, ru_idrss); CP(ru, ru32, ru_isrss); CP(ru, ru32, ru_minflt); CP(ru, ru32, ru_majflt); CP(ru, ru32, ru_nswap); CP(ru, ru32, ru_inblock); CP(ru, ru32, ru_oublock); CP(ru, ru32, ru_msgsnd); CP(ru, ru32, ru_msgrcv); CP(ru, ru32, ru_nsignals); CP(ru, ru32, ru_nvcsw); CP(ru, ru32, ru_nivcsw); error = copyout(&ru32, uap->rusage, sizeof(ru32)); } return (error); } #ifdef COMPAT_FREEBSD4 static void copy_statfs(struct statfs *in, struct statfs32 *out) { CP(*in, *out, f_bsize); CP(*in, *out, f_iosize); CP(*in, *out, f_blocks); CP(*in, *out, f_bfree); CP(*in, *out, f_bavail); CP(*in, *out, f_files); CP(*in, *out, f_ffree); CP(*in, *out, f_fsid); CP(*in, *out, f_owner); CP(*in, *out, f_type); CP(*in, *out, f_flags); CP(*in, *out, f_flags); CP(*in, *out, f_syncwrites); CP(*in, *out, f_asyncwrites); bcopy(in->f_fstypename, out->f_fstypename, MFSNAMELEN); bcopy(in->f_mntonname, out->f_mntonname, min(MNAMELEN, FREEBSD4_MNAMELEN)); CP(*in, *out, f_syncreads); CP(*in, *out, f_asyncreads); bcopy(in->f_mntfromname, out->f_mntfromname, min(MNAMELEN, FREEBSD4_MNAMELEN)); } #endif #ifdef COMPAT_FREEBSD4 int freebsd4_freebsd32_getfsstat(struct thread *td, struct freebsd4_freebsd32_getfsstat_args *uap) { int error; caddr_t sg; struct statfs32 *sp32, stat32; struct statfs *sp = NULL, stat; int maxcount, count, i; sp32 = uap->buf; maxcount = uap->bufsize / sizeof(struct statfs32); if (sp32) { sg = stackgap_init(); sp = stackgap_alloc(&sg, sizeof(struct statfs) * maxcount); uap->buf = (struct statfs32 *)sp; } error = getfsstat(td, (struct getfsstat_args *) uap); if (sp32 && !error) { count = td->td_retval[0]; for (i = 0; i < count; i++) { error = copyin(&sp[i], &stat, sizeof(stat)); if (error) return (error); copy_statfs(&stat, &stat32); error = copyout(&stat32, &sp32[i], sizeof(stat32)); if (error) return (error); } } return (error); } #endif struct sigaltstack32 { u_int32_t ss_sp; u_int32_t ss_size; int ss_flags; }; CTASSERT(sizeof(struct sigaltstack32) == 12); int freebsd32_sigaltstack(struct thread *td, struct freebsd32_sigaltstack_args *uap) { struct sigaltstack32 s32; struct sigaltstack ss, oss, *ssp; int error; if (uap->ss != NULL) { error = copyin(uap->ss, &s32, sizeof(s32)); if (error) return (error); PTRIN_CP(s32, ss, ss_sp); CP(s32, ss, ss_size); CP(s32, ss, ss_flags); ssp = &ss; } else ssp = NULL; error = kern_sigaltstack(td, ssp, &oss); if (error == 0 && uap->oss != NULL) { PTROUT_CP(oss, s32, ss_sp); CP(oss, s32, ss_size); CP(oss, s32, ss_flags); error = copyout(&s32, uap->oss, sizeof(s32)); } return (error); } int freebsd32_execve(struct thread *td, struct freebsd32_execve_args *uap) { int error; caddr_t sg; struct execve_args ap; u_int32_t *p32, arg; char **p, *p64; int count; sg = stackgap_init(); ap.fname = uap->fname; if (uap->argv) { count = 0; p32 = uap->argv; do { error = copyin(p32++, &arg, sizeof(arg)); if (error) return error; count++; } while (arg != 0); p = stackgap_alloc(&sg, count * sizeof(char *)); ap.argv = p; p32 = uap->argv; do { error = copyin(p32++, &arg, sizeof(arg)); if (error) return error; p64 = PTRIN(arg); error = copyout(&p64, p++, sizeof(p64)); if (error) return error; } while (arg != 0); } if (uap->envv) { count = 0; p32 = uap->envv; do { error = copyin(p32++, &arg, sizeof(arg)); if (error) return error; count++; } while (arg != 0); p = stackgap_alloc(&sg, count * sizeof(char *)); ap.envv = p; p32 = uap->envv; do { error = copyin(p32++, &arg, sizeof(arg)); if (error) return error; p64 = PTRIN(arg); error = copyout(&p64, p++, sizeof(p64)); if (error) return error; } while (arg != 0); } return execve(td, &ap); } #ifdef __ia64__ static int freebsd32_mmap_partial(struct thread *td, vm_offset_t start, vm_offset_t end, int prot, int fd, off_t pos) { vm_map_t map; vm_map_entry_t entry; int rv; map = &td->td_proc->p_vmspace->vm_map; if (fd != -1) prot |= VM_PROT_WRITE; if (vm_map_lookup_entry(map, start, &entry)) { if ((entry->protection & prot) != prot) { rv = vm_map_protect(map, trunc_page(start), round_page(end), entry->protection | prot, FALSE); if (rv != KERN_SUCCESS) return (EINVAL); } } else { vm_offset_t addr = trunc_page(start); rv = vm_map_find(map, 0, 0, &addr, PAGE_SIZE, FALSE, prot, VM_PROT_ALL, 0); if (rv != KERN_SUCCESS) return (EINVAL); } if (fd != -1) { struct pread_args r; r.fd = fd; r.buf = (void *) start; r.nbyte = end - start; r.offset = pos; return (pread(td, &r)); } else { while (start < end) { subyte((void *) start, 0); start++; } return (0); } } #endif int freebsd32_mmap(struct thread *td, struct freebsd32_mmap_args *uap) { struct mmap_args ap; vm_offset_t addr = (vm_offset_t) uap->addr; vm_size_t len = uap->len; int prot = uap->prot; int flags = uap->flags; int fd = uap->fd; off_t pos = (uap->poslo | ((off_t)uap->poshi << 32)); #ifdef __ia64__ vm_size_t pageoff; int error; /* * Attempt to handle page size hassles. */ pageoff = (pos & PAGE_MASK); if (flags & MAP_FIXED) { vm_offset_t start, end; start = addr; end = addr + len; if (start != trunc_page(start)) { error = freebsd32_mmap_partial(td, start, round_page(start), prot, fd, pos); if (fd != -1) pos += round_page(start) - start; start = round_page(start); } if (end != round_page(end)) { vm_offset_t t = trunc_page(end); error = freebsd32_mmap_partial(td, t, end, prot, fd, pos + t - start); end = trunc_page(end); } if (end > start && fd != -1 && (pos & PAGE_MASK)) { /* * We can't map this region at all. The specified * address doesn't have the same alignment as the file * position. Fake the mapping by simply reading the * entire region into memory. First we need to make * sure the region exists. */ vm_map_t map; struct pread_args r; int rv; prot |= VM_PROT_WRITE; map = &td->td_proc->p_vmspace->vm_map; rv = vm_map_remove(map, start, end); if (rv != KERN_SUCCESS) return (EINVAL); rv = vm_map_find(map, 0, 0, &start, end - start, FALSE, prot, VM_PROT_ALL, 0); if (rv != KERN_SUCCESS) return (EINVAL); r.fd = fd; r.buf = (void *) start; r.nbyte = end - start; r.offset = pos; error = pread(td, &r); if (error) return (error); td->td_retval[0] = addr; return (0); } if (end == start) { /* * After dealing with the ragged ends, there * might be none left. */ td->td_retval[0] = addr; return (0); } addr = start; len = end - start; } #endif ap.addr = (void *) addr; ap.len = len; ap.prot = prot; ap.flags = flags; ap.fd = fd; ap.pos = pos; return (mmap(td, &ap)); } struct itimerval32 { struct timeval32 it_interval; struct timeval32 it_value; }; CTASSERT(sizeof(struct itimerval32) == 16); int freebsd32_setitimer(struct thread *td, struct freebsd32_setitimer_args *uap) { int error; caddr_t sg; struct itimerval32 *p32, *op32, s32; struct itimerval *p = NULL, *op = NULL, s; p32 = uap->itv; if (p32) { sg = stackgap_init(); p = stackgap_alloc(&sg, sizeof(struct itimerval)); uap->itv = (struct itimerval32 *)p; error = copyin(p32, &s32, sizeof(s32)); if (error) return (error); TV_CP(s32, s, it_interval); TV_CP(s32, s, it_value); error = copyout(&s, p, sizeof(s)); if (error) return (error); } op32 = uap->oitv; if (op32) { sg = stackgap_init(); op = stackgap_alloc(&sg, sizeof(struct itimerval)); uap->oitv = (struct itimerval32 *)op; } error = setitimer(td, (struct setitimer_args *) uap); if (error) return (error); if (op32) { error = copyin(op, &s, sizeof(s)); if (error) return (error); TV_CP(s, s32, it_interval); TV_CP(s, s32, it_value); error = copyout(&s32, op32, sizeof(s32)); } return (error); } int freebsd32_getitimer(struct thread *td, struct freebsd32_getitimer_args *uap) { int error; caddr_t sg; struct itimerval32 *p32, s32; struct itimerval *p = NULL, s; p32 = uap->itv; if (p32) { sg = stackgap_init(); p = stackgap_alloc(&sg, sizeof(struct itimerval)); uap->itv = (struct itimerval32 *)p; } error = getitimer(td, (struct getitimer_args *) uap); if (error) return (error); if (p32) { error = copyin(p, &s, sizeof(s)); if (error) return (error); TV_CP(s, s32, it_interval); TV_CP(s, s32, it_value); error = copyout(&s32, p32, sizeof(s32)); } return (error); } int freebsd32_select(struct thread *td, struct freebsd32_select_args *uap) { int error; caddr_t sg; struct timeval32 *p32, s32; struct timeval *p = NULL, s; p32 = uap->tv; if (p32) { sg = stackgap_init(); p = stackgap_alloc(&sg, sizeof(struct timeval)); uap->tv = (struct timeval32 *)p; error = copyin(p32, &s32, sizeof(s32)); if (error) return (error); CP(s32, s, tv_sec); CP(s32, s, tv_usec); error = copyout(&s, p, sizeof(s)); if (error) return (error); } /* * XXX big-endian needs to convert the fd_sets too. */ return (select(td, (struct select_args *) uap)); } struct kevent32 { u_int32_t ident; /* identifier for this event */ short filter; /* filter for event */ u_short flags; u_int fflags; int32_t data; u_int32_t udata; /* opaque user data identifier */ }; CTASSERT(sizeof(struct kevent32) == 20); int freebsd32_kevent(struct thread *td, struct freebsd32_kevent_args *uap) { - int error; - caddr_t sg; struct timespec32 ts32; - struct timespec ts; - struct kevent32 ks32; + struct timespec ts, *tsp; struct kevent *ks; - struct kevent_args a; - int i; + struct kevent32 ks32; + struct kevent *changes, *events; + int error, i; - sg = stackgap_init(); - a.fd = uap->fd; - a.changelist = uap->changelist; - a.nchanges = uap->nchanges; - a.eventlist = uap->eventlist; - a.nevents = uap->nevents; - a.timeout = NULL; - if (uap->timeout) { - a.timeout = stackgap_alloc(&sg, sizeof(struct timespec)); error = copyin(uap->timeout, &ts32, sizeof(ts32)); if (error) return (error); CP(ts32, ts, tv_sec); CP(ts32, ts, tv_nsec); - error = copyout(&ts, (void *)(uintptr_t)a.timeout, sizeof(ts)); - if (error) - return (error); - } - if (uap->changelist) { - a.changelist = (struct kevent *)stackgap_alloc(&sg, - uap->nchanges * sizeof(struct kevent)); + tsp = &ts; + } else + tsp = NULL; + if (uap->changelist && uap->nchanges > 0) { + changes = malloc(sizeof(struct kevent) * uap->nchanges, M_TEMP, + M_WAITOK); for (i = 0; i < uap->nchanges; i++) { error = copyin(&uap->changelist[i], &ks32, sizeof(ks32)); - if (error) + if (error) { + free(changes, M_TEMP); return (error); - ks = (struct kevent *)(uintptr_t)&a.changelist[i]; + } + ks = &changes[i]; CP(ks32, *ks, ident); CP(ks32, *ks, filter); CP(ks32, *ks, flags); CP(ks32, *ks, fflags); CP(ks32, *ks, data); PTRIN_CP(ks32, *ks, udata); } - } - if (uap->eventlist) { - a.eventlist = stackgap_alloc(&sg, - uap->nevents * sizeof(struct kevent)); - } - error = kevent(td, &a); - if (uap->eventlist && error > 0) { - for (i = 0; i < error; i++) { - ks = &a.eventlist[i]; + } else + changes = NULL; + if (uap->eventlist && uap->nevents > 0) + events = malloc(sizeof(struct kevent) * uap->nevents, M_TEMP, + M_WAITOK); + else + events = NULL; + error = kern_kevent(td, uap->fd, changes, uap->nchanges, UIO_SYSSPACE, + events, uap->nevents, UIO_SYSSPACE, tsp); + free(changes, M_TEMP); + if (uap->eventlist && events && td->td_retval[0] > 0) { + for (i = 0; i < td->td_retval[0]; i++) { + ks = &events[i]; CP(*ks, ks32, ident); CP(*ks, ks32, filter); CP(*ks, ks32, flags); CP(*ks, ks32, fflags); CP(*ks, ks32, data); PTROUT_CP(*ks, ks32, udata); error = copyout(&ks32, &uap->eventlist[i], sizeof(ks32)); if (error) - return (error); + break; } } - return error; + if (events) + free(events, M_TEMP); + return (error); } int freebsd32_gettimeofday(struct thread *td, struct freebsd32_gettimeofday_args *uap) { struct timeval atv; struct timeval32 atv32; struct timezone rtz; int error = 0; if (uap->tp) { microtime(&atv); CP(atv, atv32, tv_sec); CP(atv, atv32, tv_usec); error = copyout(&atv32, uap->tp, sizeof (atv32)); } if (error == 0 && uap->tzp != NULL) { rtz.tz_minuteswest = tz_minuteswest; rtz.tz_dsttime = tz_dsttime; error = copyout(&rtz, uap->tzp, sizeof (rtz)); } return (error); } int freebsd32_getrusage(struct thread *td, struct freebsd32_getrusage_args *uap) { int error; caddr_t sg; struct rusage32 *p32, s32; struct rusage *p = NULL, s; p32 = uap->rusage; if (p32) { sg = stackgap_init(); p = stackgap_alloc(&sg, sizeof(struct rusage)); uap->rusage = (struct rusage32 *)p; } error = getrusage(td, (struct getrusage_args *) uap); if (error) return (error); if (p32) { error = copyin(p, &s, sizeof(s)); if (error) return (error); TV_CP(s, s32, ru_utime); TV_CP(s, s32, ru_stime); CP(s, s32, ru_maxrss); CP(s, s32, ru_ixrss); CP(s, s32, ru_idrss); CP(s, s32, ru_isrss); CP(s, s32, ru_minflt); CP(s, s32, ru_majflt); CP(s, s32, ru_nswap); CP(s, s32, ru_inblock); CP(s, s32, ru_oublock); CP(s, s32, ru_msgsnd); CP(s, s32, ru_msgrcv); CP(s, s32, ru_nsignals); CP(s, s32, ru_nvcsw); CP(s, s32, ru_nivcsw); error = copyout(&s32, p32, sizeof(s32)); } return (error); } struct iovec32 { u_int32_t iov_base; int iov_len; }; CTASSERT(sizeof(struct iovec32) == 8); static int freebsd32_copyinuio(struct iovec32 *iovp, u_int iovcnt, struct uio **uiop) { struct iovec32 iov32; struct iovec *iov; struct uio *uio; u_int iovlen; int error, i; *uiop = NULL; if (iovcnt > UIO_MAXIOV) return (EINVAL); iovlen = iovcnt * sizeof(struct iovec); uio = malloc(iovlen + sizeof *uio, M_IOV, M_WAITOK); iov = (struct iovec *)(uio + 1); for (i = 0; i < iovcnt; i++) { error = copyin(&iovp[i], &iov32, sizeof(struct iovec32)); if (error) { free(uio, M_IOV); return (error); } iov[i].iov_base = PTRIN(iov32.iov_base); iov[i].iov_len = iov32.iov_len; } uio->uio_iov = iov; uio->uio_iovcnt = iovcnt; uio->uio_segflg = UIO_USERSPACE; uio->uio_offset = -1; uio->uio_resid = 0; for (i = 0; i < iovcnt; i++) { if (iov->iov_len > INT_MAX - uio->uio_resid) { free(uio, M_IOV); return (EINVAL); } uio->uio_resid += iov->iov_len; iov++; } *uiop = uio; return (0); } int freebsd32_readv(struct thread *td, struct freebsd32_readv_args *uap) { struct uio *auio; int error; error = freebsd32_copyinuio(uap->iovp, uap->iovcnt, &auio); if (error) return (error); error = kern_readv(td, uap->fd, auio); free(auio, M_IOV); return (error); } int freebsd32_writev(struct thread *td, struct freebsd32_writev_args *uap) { struct uio *auio; int error; error = freebsd32_copyinuio(uap->iovp, uap->iovcnt, &auio); if (error) return (error); error = kern_writev(td, uap->fd, auio); free(auio, M_IOV); return (error); } int freebsd32_settimeofday(struct thread *td, struct freebsd32_settimeofday_args *uap) { struct timeval32 tv32; struct timeval tv, *tvp; struct timezone tz, *tzp; int error; if (uap->tv) { error = copyin(uap->tv, &tv32, sizeof(tv32)); if (error) return (error); CP(tv32, tv, tv_sec); CP(tv32, tv, tv_usec); tvp = &tv; } else tvp = NULL; if (uap->tzp) { error = copyin(uap->tzp, &tz, sizeof(tz)); if (error) return (error); tzp = &tz; } else tzp = NULL; return (kern_settimeofday(td, tvp, tzp)); } int freebsd32_utimes(struct thread *td, struct freebsd32_utimes_args *uap) { int error; caddr_t sg; struct timeval32 *p32, s32[2]; struct timeval *p = NULL, s[2]; p32 = uap->tptr; if (p32) { sg = stackgap_init(); p = stackgap_alloc(&sg, 2*sizeof(struct timeval)); uap->tptr = (struct timeval32 *)p; error = copyin(p32, s32, sizeof(s32)); if (error) return (error); CP(s32[0], s[0], tv_sec); CP(s32[0], s[0], tv_usec); CP(s32[1], s[1], tv_sec); CP(s32[1], s[1], tv_usec); error = copyout(s, p, sizeof(s)); if (error) return (error); } return (utimes(td, (struct utimes_args *) uap)); } int freebsd32_adjtime(struct thread *td, struct freebsd32_adjtime_args *uap) { struct timeval32 tv32; struct timeval delta, olddelta, *deltap; int error; if (uap->delta) { error = copyin(uap->delta, &tv32, sizeof(tv32)); if (error) return (error); CP(tv32, delta, tv_sec); CP(tv32, delta, tv_usec); deltap = δ } else deltap = NULL; error = kern_adjtime(td, deltap, &olddelta); if (uap->olddelta && error == 0) { CP(olddelta, tv32, tv_sec); CP(olddelta, tv32, tv_usec); error = copyout(&tv32, uap->olddelta, sizeof(tv32)); } return (error); } #ifdef COMPAT_FREEBSD4 int freebsd4_freebsd32_statfs(struct thread *td, struct freebsd4_freebsd32_statfs_args *uap) { int error; caddr_t sg; struct statfs32 *p32, s32; struct statfs *p = NULL, s; p32 = uap->buf; if (p32) { sg = stackgap_init(); p = stackgap_alloc(&sg, sizeof(struct statfs)); uap->buf = (struct statfs32 *)p; } error = statfs(td, (struct statfs_args *) uap); if (error) return (error); if (p32) { error = copyin(p, &s, sizeof(s)); if (error) return (error); copy_statfs(&s, &s32); error = copyout(&s32, p32, sizeof(s32)); } return (error); } #endif #ifdef COMPAT_FREEBSD4 int freebsd4_freebsd32_fstatfs(struct thread *td, struct freebsd4_freebsd32_fstatfs_args *uap) { int error; caddr_t sg; struct statfs32 *p32, s32; struct statfs *p = NULL, s; p32 = uap->buf; if (p32) { sg = stackgap_init(); p = stackgap_alloc(&sg, sizeof(struct statfs)); uap->buf = (struct statfs32 *)p; } error = fstatfs(td, (struct fstatfs_args *) uap); if (error) return (error); if (p32) { error = copyin(p, &s, sizeof(s)); if (error) return (error); copy_statfs(&s, &s32); error = copyout(&s32, p32, sizeof(s32)); } return (error); } #endif #ifdef COMPAT_FREEBSD4 int freebsd4_freebsd32_fhstatfs(struct thread *td, struct freebsd4_freebsd32_fhstatfs_args *uap) { int error; caddr_t sg; struct statfs32 *p32, s32; struct statfs *p = NULL, s; p32 = uap->buf; if (p32) { sg = stackgap_init(); p = stackgap_alloc(&sg, sizeof(struct statfs)); uap->buf = (struct statfs32 *)p; } error = fhstatfs(td, (struct fhstatfs_args *) uap); if (error) return (error); if (p32) { error = copyin(p, &s, sizeof(s)); if (error) return (error); copy_statfs(&s, &s32); error = copyout(&s32, p32, sizeof(s32)); } return (error); } #endif int freebsd32_semsys(struct thread *td, struct freebsd32_semsys_args *uap) { /* * Vector through to semsys if it is loaded. */ return sysent[169].sy_call(td, uap); } int freebsd32_msgsys(struct thread *td, struct freebsd32_msgsys_args *uap) { /* * Vector through to msgsys if it is loaded. */ return sysent[170].sy_call(td, uap); } int freebsd32_shmsys(struct thread *td, struct freebsd32_shmsys_args *uap) { /* * Vector through to shmsys if it is loaded. */ return sysent[171].sy_call(td, uap); } int freebsd32_pread(struct thread *td, struct freebsd32_pread_args *uap) { struct pread_args ap; ap.fd = uap->fd; ap.buf = uap->buf; ap.nbyte = uap->nbyte; ap.offset = (uap->offsetlo | ((off_t)uap->offsethi << 32)); return (pread(td, &ap)); } int freebsd32_pwrite(struct thread *td, struct freebsd32_pwrite_args *uap) { struct pwrite_args ap; ap.fd = uap->fd; ap.buf = uap->buf; ap.nbyte = uap->nbyte; ap.offset = (uap->offsetlo | ((off_t)uap->offsethi << 32)); return (pwrite(td, &ap)); } int freebsd32_lseek(struct thread *td, struct freebsd32_lseek_args *uap) { int error; struct lseek_args ap; off_t pos; ap.fd = uap->fd; ap.offset = (uap->offsetlo | ((off_t)uap->offsethi << 32)); ap.whence = uap->whence; error = lseek(td, &ap); /* Expand the quad return into two parts for eax and edx */ pos = *(off_t *)(td->td_retval); td->td_retval[0] = pos & 0xffffffff; /* %eax */ td->td_retval[1] = pos >> 32; /* %edx */ return error; } int freebsd32_truncate(struct thread *td, struct freebsd32_truncate_args *uap) { struct truncate_args ap; ap.path = uap->path; ap.length = (uap->lengthlo | ((off_t)uap->lengthhi << 32)); return (truncate(td, &ap)); } int freebsd32_ftruncate(struct thread *td, struct freebsd32_ftruncate_args *uap) { struct ftruncate_args ap; ap.fd = uap->fd; ap.length = (uap->lengthlo | ((off_t)uap->lengthhi << 32)); return (ftruncate(td, &ap)); } #ifdef COMPAT_FREEBSD4 int freebsd4_freebsd32_sendfile(struct thread *td, struct freebsd4_freebsd32_sendfile_args *uap) { struct freebsd4_sendfile_args ap; ap.fd = uap->fd; ap.s = uap->s; ap.offset = (uap->offsetlo | ((off_t)uap->offsethi << 32)); ap.nbytes = uap->nbytes; /* XXX check */ ap.hdtr = uap->hdtr; /* XXX check */ ap.sbytes = uap->sbytes; /* XXX FIXME!! */ ap.flags = uap->flags; return (freebsd4_sendfile(td, &ap)); } #endif int freebsd32_sendfile(struct thread *td, struct freebsd32_sendfile_args *uap) { struct sendfile_args ap; ap.fd = uap->fd; ap.s = uap->s; ap.offset = (uap->offsetlo | ((off_t)uap->offsethi << 32)); ap.nbytes = uap->nbytes; /* XXX check */ ap.hdtr = uap->hdtr; /* XXX check */ ap.sbytes = uap->sbytes; /* XXX FIXME!! */ ap.flags = uap->flags; return (sendfile(td, &ap)); } struct stat32 { dev_t st_dev; ino_t st_ino; mode_t st_mode; nlink_t st_nlink; uid_t st_uid; gid_t st_gid; dev_t st_rdev; struct timespec32 st_atimespec; struct timespec32 st_mtimespec; struct timespec32 st_ctimespec; off_t st_size; int64_t st_blocks; u_int32_t st_blksize; u_int32_t st_flags; u_int32_t st_gen; struct timespec32 st_birthtimespec; unsigned int :(8 / 2) * (16 - (int)sizeof(struct timespec32)); unsigned int :(8 / 2) * (16 - (int)sizeof(struct timespec32)); }; CTASSERT(sizeof(struct stat32) == 96); static void copy_stat( struct stat *in, struct stat32 *out) { CP(*in, *out, st_dev); CP(*in, *out, st_ino); CP(*in, *out, st_mode); CP(*in, *out, st_nlink); CP(*in, *out, st_uid); CP(*in, *out, st_gid); CP(*in, *out, st_rdev); TS_CP(*in, *out, st_atimespec); TS_CP(*in, *out, st_mtimespec); TS_CP(*in, *out, st_ctimespec); CP(*in, *out, st_size); CP(*in, *out, st_blocks); CP(*in, *out, st_blksize); CP(*in, *out, st_flags); CP(*in, *out, st_gen); } int freebsd32_stat(struct thread *td, struct freebsd32_stat_args *uap) { struct stat sb; struct stat32 sb32; int error; struct nameidata nd; #ifdef LOOKUP_SHARED NDINIT(&nd, LOOKUP, FOLLOW | LOCKSHARED | LOCKLEAF | NOOBJ, UIO_USERSPACE, uap->path, td); #else NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, uap->path, td); #endif if ((error = namei(&nd)) != 0) return (error); error = vn_stat(nd.ni_vp, &sb, td->td_ucred, NOCRED, td); NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_vp); if (error) return (error); copy_stat(&sb, &sb32); error = copyout(&sb32, uap->ub, sizeof (sb32)); return (error); } int freebsd32_fstat(struct thread *td, struct freebsd32_fstat_args *uap) { struct file *fp; struct stat ub; struct stat32 ub32; int error; if ((error = fget(td, uap->fd, &fp)) != 0) return (error); mtx_lock(&Giant); error = fo_stat(fp, &ub, td->td_ucred, td); mtx_unlock(&Giant); fdrop(fp, td); if (error) return (error); copy_stat(&ub, &ub32); error = copyout(&ub32, uap->ub, sizeof(ub32)); return (error); } int freebsd32_lstat(struct thread *td, struct freebsd32_lstat_args *uap) { int error; struct vnode *vp; struct stat sb; struct stat32 sb32; struct nameidata nd; NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, uap->path, td); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; error = vn_stat(vp, &sb, td->td_ucred, NOCRED, td); NDFREE(&nd, NDF_ONLY_PNBUF); vput(vp); if (error) return (error); copy_stat(&sb, &sb32); error = copyout(&sb32, uap->ub, sizeof (sb32)); return (error); } /* * MPSAFE */ int freebsd32_sysctl(struct thread *td, struct freebsd32_sysctl_args *uap) { int error, name[CTL_MAXNAME]; size_t j, oldlen; if (uap->namelen > CTL_MAXNAME || uap->namelen < 2) return (EINVAL); error = copyin(uap->name, &name, uap->namelen * sizeof(int)); if (error) return (error); mtx_lock(&Giant); if (uap->oldlenp) oldlen = fuword32(uap->oldlenp); else oldlen = 0; error = userland_sysctl(td, name, uap->namelen, uap->old, &oldlen, 1, uap->new, uap->newlen, &j); if (error && error != ENOMEM) goto done2; if (uap->oldlenp) { suword32(uap->oldlenp, j); } done2: mtx_unlock(&Giant); return (error); } struct sigaction32 { u_int32_t sa_u; int sa_flags; sigset_t sa_mask; }; CTASSERT(sizeof(struct sigaction32) == 24); int freebsd32_sigaction(struct thread *td, struct freebsd32_sigaction_args *uap) { struct sigaction32 s32; struct sigaction sa, osa, *sap; int error; if (uap->act) { error = copyin(uap->act, &s32, sizeof(s32)); if (error) return (error); sa.sa_handler = PTRIN(s32.sa_u); CP(s32, sa, sa_flags); CP(s32, sa, sa_mask); sap = &sa; } else sap = NULL; error = kern_sigaction(td, uap->sig, sap, &osa, 0); if (error != 0 && uap->oact != NULL) { s32.sa_u = PTROUT(osa.sa_handler); CP(osa, s32, sa_flags); CP(osa, s32, sa_mask); error = copyout(&s32, uap->oact, sizeof(s32)); } return (error); } #ifdef COMPAT_FREEBSD4 int freebsd4_freebsd32_sigaction(struct thread *td, struct freebsd4_freebsd32_sigaction_args *uap) { struct sigaction32 s32; struct sigaction sa, osa, *sap; int error; if (uap->act) { error = copyin(uap->act, &s32, sizeof(s32)); if (error) return (error); sa.sa_handler = PTRIN(s32.sa_u); CP(s32, sa, sa_flags); CP(s32, sa, sa_mask); sap = &sa; } else sap = NULL; error = kern_sigaction(td, uap->sig, sap, &osa, KSA_FREEBSD4); if (error != 0 && uap->oact != NULL) { s32.sa_u = PTROUT(osa.sa_handler); CP(osa, s32, sa_flags); CP(osa, s32, sa_mask); error = copyout(&s32, uap->oact, sizeof(s32)); } return (error); } #endif int freebsd32_nanosleep(struct thread *td, struct freebsd32_nanosleep_args *uap) { struct timespec32 rmt32, rqt32; struct timespec rmt, rqt; int error; error = copyin(uap->rqtp, &rqt32, sizeof(rqt)); if (error) return (error); CP(rqt32, rqt, tv_sec); CP(rqt32, rqt, tv_nsec); if (uap->rmtp && !useracc((caddr_t)uap->rmtp, sizeof(rmt), VM_PROT_WRITE)) return (EFAULT); error = kern_nanosleep(td, &rqt, &rmt); if (error && uap->rmtp) { int error2; CP(rmt, rmt32, tv_sec); CP(rmt, rmt32, tv_nsec); error2 = copyout(&rmt32, uap->rmtp, sizeof(rmt)); if (error2) error = error2; } return (error); } #if 0 int freebsd32_xxx(struct thread *td, struct freebsd32_xxx_args *uap) { int error; caddr_t sg; struct yyy32 *p32, s32; struct yyy *p = NULL, s; p32 = uap->zzz; if (p32) { sg = stackgap_init(); p = stackgap_alloc(&sg, sizeof(struct yyy)); uap->zzz = (struct yyy32 *)p; error = copyin(p32, &s32, sizeof(s32)); if (error) return (error); /* translate in */ error = copyout(&s, p, sizeof(s)); if (error) return (error); } error = xxx(td, (struct xxx_args *) uap); if (error) return (error); if (p32) { error = copyin(p, &s, sizeof(s)); if (error) return (error); /* translate out */ error = copyout(&s32, p32, sizeof(s32)); } return (error); } #endif Index: stable/5/sys/compat/freebsd32/syscalls.master =================================================================== --- stable/5/sys/compat/freebsd32/syscalls.master (revision 145377) +++ stable/5/sys/compat/freebsd32/syscalls.master (revision 145378) @@ -1,612 +1,612 @@ $FreeBSD$ ; from: @(#)syscalls.master 8.2 (Berkeley) 1/13/94 ; from: src/sys/kern/syscalls.master 1.107 ; ; System call name/number master file. ; Processed to created init_sysent.c, syscalls.c and syscall.h. ; Columns: number [M]type nargs name alt{name,tag,rtyp}/comments ; number system call number, must be in order ; type one of [M]STD, [M]OBSOL, [M]UNIMPL, [M]COMPAT, [M]CPT_NOA, ; [M]LIBCOMPAT, [M]NODEF, [M]NOARGS, [M]NOPROTO, [M]NOIMPL, ; [M]NOSTD ; name psuedo-prototype of syscall routine ; If one of the following alts is different, then all appear: ; altname name of system call if different ; alttag name of args struct tag if different from [o]`name'"_args" ; altrtyp return type if not int (bogus - syscalls always return int) ; for UNIMPL/OBSOL, name continues with comments ; types: ; [M] e.g. like MSTD -- means the system call is MP-safe. If no ; M prefix is used, the syscall wrapper will obtain the Giant ; lock for the syscall. ; STD always included ; COMPAT included on COMPAT #ifdef ; LIBCOMPAT included on COMPAT #ifdef, and placed in syscall.h ; OBSOL obsolete, not included in system, only specifies name ; UNIMPL not implemented, placeholder only ; NOSTD implemented but as a lkm that can be statically ; compiled in sysent entry will be filled with lkmsys ; so the SYSCALL_MODULE macro works ; #ifdef's, etc. may be included, and are copied to the output files. #include #include #include #include #include #include ; Reserved/unimplemented system calls in the range 0-150 inclusive ; are reserved for use in future Berkeley releases. ; Additional system calls implemented in vendor and other ; redistributions should be placed in the reserved range at the end ; of the current calls. 0 MNOPROTO { int nosys(void); } syscall nosys_args int 1 MNOPROTO { void sys_exit(int rval); } exit sys_exit_args void 2 MNOPROTO { int fork(void); } 3 MNOPROTO { ssize_t read(int fd, void *buf, size_t nbyte); } 4 MNOPROTO { ssize_t write(int fd, const void *buf, size_t nbyte); } 5 NOPROTO { int open(char *path, int flags, int mode); } 6 MNOPROTO { int close(int fd); } 7 MSTD { int freebsd32_wait4(int pid, int *status, int options, \ struct rusage32 *rusage); } 8 OBSOL old creat 9 NOPROTO { int link(char *path, char *link); } 10 NOPROTO { int unlink(char *path); } 11 OBSOL execv 12 NOPROTO { int chdir(char *path); } 13 NOPROTO { int fchdir(int fd); } 14 NOPROTO { int mknod(char *path, int mode, int dev); } 15 NOPROTO { int chmod(char *path, int mode); } 16 NOPROTO { int chown(char *path, int uid, int gid); } 17 MNOPROTO { int obreak(char *nsize); } break obreak_args int 18 COMPAT4 { int freebsd32_getfsstat(struct statfs32 *buf, \ long bufsize, int flags); } 19 OBSOL olseek 20 MNOPROTO { pid_t getpid(void); } 21 NOPROTO { int mount(char *type, char *path, int flags, \ caddr_t data); } 22 NOPROTO { int unmount(char *path, int flags); } 23 MNOPROTO { int setuid(uid_t uid); } 24 MNOPROTO { uid_t getuid(void); } 25 MNOPROTO { uid_t geteuid(void); } 26 MNOPROTO { int ptrace(int req, pid_t pid, caddr_t addr, int data); } ; XXX implement 27 UNIMPL recvmsg 28 MNOPROTO { int sendmsg(int s, caddr_t msg, int flags); } 29 MNOPROTO { int recvfrom(int s, caddr_t buf, size_t len, int flags, \ caddr_t from, int *fromlenaddr); } 30 MNOPROTO { int accept(int s, caddr_t name, int *anamelen); } 31 MNOPROTO { int getpeername(int fdes, caddr_t asa, int *alen); } 32 MNOPROTO { int getsockname(int fdes, caddr_t asa, int *alen); } 33 NOPROTO { int access(char *path, int flags); } 34 NOPROTO { int chflags(char *path, int flags); } 35 NOPROTO { int fchflags(int fd, int flags); } 36 NOPROTO { int sync(void); } 37 MNOPROTO { int kill(int pid, int signum); } 38 UNIMPL ostat 39 MNOPROTO { pid_t getppid(void); } 40 UNIMPL olstat 41 MNOPROTO { int dup(u_int fd); } 42 MNOPROTO { int pipe(void); } 43 MNOPROTO { gid_t getegid(void); } 44 MNOPROTO { int profil(caddr_t samples, size_t size, size_t offset, \ u_int scale); } 45 MNOPROTO { int ktrace(const char *fname, int ops, int facs, int pid); } 46 UNIMPL osigaction 47 MNOPROTO { gid_t getgid(void); } 48 UNIMPL osigprocmask 49 MNOPROTO { int getlogin(char *namebuf, u_int namelen); } 50 MNOPROTO { int setlogin(char *namebuf); } 51 MNOPROTO { int acct(char *path); } 52 OBSOL osigpending 53 MSTD { int freebsd32_sigaltstack(struct sigaltstack32 *ss, \ struct sigaltstack32 *oss); } 54 MNOPROTO { int ioctl(int fd, u_long com, caddr_t data); } 55 MNOPROTO { int reboot(int opt); } 56 NOPROTO { int revoke(char *path); } 57 NOPROTO { int symlink(char *path, char *link); } 58 NOPROTO { int readlink(char *path, char *buf, int count); } 59 STD { int freebsd32_execve(char *fname, u_int32_t *argv, \ u_int32_t *envv); } 60 MNOPROTO { int umask(int newmask); } umask umask_args int 61 NOPROTO { int chroot(char *path); } 62 OBSOL ofstat 63 OBSOL ogetkerninfo 64 OBSOL ogetpagesize ; XXX implement (not OBSOL at all) 65 MNOPROTO { int msync(void *addr, size_t len, int flags); } 66 MNOPROTO { int vfork(void); } 67 OBSOL vread 68 OBSOL vwrite 69 MNOPROTO { int sbrk(int incr); } 70 MNOPROTO { int sstk(int incr); } 71 OBSOL ommap 72 MNOPROTO { int ovadvise(int anom); } vadvise ovadvise_args int 73 MNOPROTO { int munmap(void *addr, size_t len); } 74 MNOPROTO { int mprotect(const void *addr, size_t len, int prot); } 75 MNOPROTO { int madvise(void *addr, size_t len, int behav); } 76 OBSOL vhangup 77 OBSOL vlimit 78 MNOPROTO { int mincore(const void *addr, size_t len, char *vec); } 79 MNOPROTO { int getgroups(u_int gidsetsize, gid_t *gidset); } 80 MNOPROTO { int setgroups(u_int gidsetsize, gid_t *gidset); } 81 MNOPROTO { int getpgrp(void); } 82 MNOPROTO { int setpgid(int pid, int pgid); } 83 STD { int freebsd32_setitimer(u_int which, \ struct itimerval32 *itv, struct itimerval32 *oitv); } 84 OBSOL owait ; XXX implement 85 OBSOL oswapon 86 STD { int freebsd32_getitimer(u_int which, \ struct itimerval32 *itv); } 87 OBSOL ogethostname 88 OBSOL osethostname 89 MNOPROTO { int getdtablesize(void); } 90 MNOPROTO { int dup2(u_int from, u_int to); } 91 UNIMPL getdopt 92 MNOPROTO { int fcntl(int fd, int cmd, long arg); } 93 STD { int freebsd32_select(int nd, fd_set *in, fd_set *ou, \ fd_set *ex, struct timeval32 *tv); } ; XXX need to override for big-endian - little-endian should work fine. 94 UNIMPL setdopt 95 NOPROTO { int fsync(int fd); } 96 MNOPROTO { int setpriority(int which, int who, int prio); } 97 MNOPROTO { int socket(int domain, int type, int protocol); } 98 MNOPROTO { int connect(int s, caddr_t name, int namelen); } 99 OBSOL oaccept 100 MNOPROTO { int getpriority(int which, int who); } 101 OBSOL osend 102 OBSOL orecv 103 OBSOL osigreturn 104 MNOPROTO { int bind(int s, caddr_t name, int namelen); } 105 MNOPROTO { int setsockopt(int s, int level, int name, caddr_t val, \ int valsize); } 106 MNOPROTO { int listen(int s, int backlog); } 107 OBSOL vtimes 108 OBSOL osigvec 109 OBSOL osigblock 110 OBSOL osigsetmask 111 OBSOL osigsuspend 112 OBSOL osigstack 113 OBSOL orecvmsg 114 OBSOL osendmsg 115 OBSOL vtrace 116 MSTD { int freebsd32_gettimeofday(struct timeval32 *tp, \ struct timezone *tzp); } 117 STD { int freebsd32_getrusage(int who, struct rusage32 *rusage); } 118 MNOPROTO { int getsockopt(int s, int level, int name, caddr_t val, \ int *avalsize); } 119 UNIMPL resuba (BSD/OS 2.x) 120 STD { int freebsd32_readv(int fd, struct iovec32 *iovp, \ u_int iovcnt); } 121 STD { int freebsd32_writev(int fd, struct iovec32 *iovp, \ u_int iovcnt); } 122 STD { int freebsd32_settimeofday(struct timeval32 *tv, \ struct timezone *tzp); } 123 NOPROTO { int fchown(int fd, int uid, int gid); } 124 NOPROTO { int fchmod(int fd, int mode); } 125 OBSOL orecvfrom 126 MNOPROTO { int setreuid(int ruid, int euid); } 127 MNOPROTO { int setregid(int rgid, int egid); } 128 NOPROTO { int rename(char *from, char *to); } 129 OBSOL otruncate 130 OBSOL ftruncate 131 MNOPROTO { int flock(int fd, int how); } 132 NOPROTO { int mkfifo(char *path, int mode); } 133 MNOPROTO { int sendto(int s, caddr_t buf, size_t len, int flags, \ caddr_t to, int tolen); } 134 MNOPROTO { int shutdown(int s, int how); } 135 MNOPROTO { int socketpair(int domain, int type, int protocol, \ int *rsv); } 136 NOPROTO { int mkdir(char *path, int mode); } 137 NOPROTO { int rmdir(char *path); } 138 STD { int freebsd32_utimes(char *path, struct timeval32 *tptr); } 139 OBSOL 4.2 sigreturn 140 STD { int freebsd32_adjtime(struct timeval32 *delta, \ struct timeval32 *olddelta); } 141 OBSOL ogetpeername 142 OBSOL ogethostid 143 OBSOL sethostid 144 OBSOL getrlimit 145 OBSOL setrlimit 146 OBSOL killpg 147 MNOPROTO { int setsid(void); } 148 NOPROTO { int quotactl(char *path, int cmd, int uid, caddr_t arg); } 149 OBSOL oquota 150 OBSOL ogetsockname ; Syscalls 151-180 inclusive are reserved for vendor-specific ; system calls. (This includes various calls added for compatibity ; with other Unix variants.) ; Some of these calls are now supported by BSD... 151 UNIMPL sem_lock (BSD/OS 2.x) 152 UNIMPL sem_wakeup (BSD/OS 2.x) 153 UNIMPL asyncdaemon (BSD/OS 2.x) 154 UNIMPL nosys ; 155 is initialized by the NFS code, if present. ; XXX this is a problem!!! 155 UNIMPL nfssvc 156 OBSOL ogetdirentries 157 COMPAT4 { int freebsd32_statfs(char *path, \ struct statfs32 *buf); } 158 COMPAT4 { int freebsd32_fstatfs(int fd, struct statfs32 *buf);} 159 UNIMPL nosys 160 UNIMPL nosys 161 NOPROTO { int getfh(char *fname, struct fhandle *fhp); } 162 MNOPROTO { int getdomainname(char *domainname, int len); } 163 MNOPROTO { int setdomainname(char *domainname, int len); } 164 MNOPROTO { int uname(struct utsname *name); } 165 MNOPROTO { int sysarch(int op, char *parms); } 166 MNOPROTO { int rtprio(int function, pid_t pid, \ struct rtprio *rtp); } 167 UNIMPL nosys 168 UNIMPL nosys 169 STD { int freebsd32_semsys(int which, int a2, int a3, int a4, \ int a5); } 170 STD { int freebsd32_msgsys(int which, int a2, int a3, int a4, \ int a5, int a6); } 171 STD { int freebsd32_shmsys(int which, int a2, int a3, int a4); } 172 UNIMPL nosys 173 STD { ssize_t freebsd32_pread(int fd, void *buf, size_t nbyte, \ int pad, u_int32_t offsetlo, u_int32_t offsethi); } ; XXX note - bigendian is different 174 STD { ssize_t freebsd32_pwrite(int fd, const void *buf, \ size_t nbyte, int pad, u_int32_t offsetlo, \ u_int32_t offsethi); } ; XXX note - bigendian is different 175 UNIMPL nosys 176 MNOPROTO { int ntp_adjtime(struct timex *tp); } 177 UNIMPL sfork (BSD/OS 2.x) 178 UNIMPL getdescriptor (BSD/OS 2.x) 179 UNIMPL setdescriptor (BSD/OS 2.x) 180 UNIMPL nosys ; Syscalls 181-199 are used by/reserved for BSD 181 MNOPROTO { int setgid(gid_t gid); } 182 MNOPROTO { int setegid(gid_t egid); } 183 MNOPROTO { int seteuid(uid_t euid); } 184 UNIMPL lfs_bmapv 185 UNIMPL lfs_markv 186 UNIMPL lfs_segclean 187 UNIMPL lfs_segwait 188 STD { int freebsd32_stat(char *path, struct stat32 *ub); } 189 MSTD { int freebsd32_fstat(int fd, struct stat32 *ub); } 190 STD { int freebsd32_lstat(char *path, struct stat32 *ub); } 191 NOPROTO { int pathconf(char *path, int name); } 192 MNOPROTO { int fpathconf(int fd, int name); } 193 UNIMPL nosys 194 MNOPROTO { int getrlimit(u_int which, struct rlimit *rlp); } \ getrlimit __getrlimit_args int 195 MNOPROTO { int setrlimit(u_int which, struct rlimit *rlp); } \ setrlimit __setrlimit_args int 196 NOPROTO { int getdirentries(int fd, char *buf, u_int count, \ long *basep); } 197 STD { caddr_t freebsd32_mmap(caddr_t addr, size_t len, int prot, \ int flags, int fd, int pad, u_int32_t poslo, \ u_int32_t poshi); } 198 NOPROTO { int nosys(void); } __syscall __syscall_args int ; XXX note - bigendian is different 199 STD { off_t freebsd32_lseek(int fd, int pad, u_int32_t offsetlo, \ u_int32_t offsethi, int whence); } ; XXX note - bigendian is different 200 STD { int freebsd32_truncate(char *path, int pad, \ u_int32_t lengthlo, u_int32_t lengthhi); } ; XXX note - bigendian is different 201 STD { int freebsd32_ftruncate(int fd, int pad, \ u_int32_t lengthlo, u_int32_t lengthhi); } 202 MSTD { int freebsd32_sysctl(int *name, u_int namelen, void *old, \ u_int32_t *oldlenp, void *new, u_int32_t newlen); } 203 MNOPROTO { int mlock(const void *addr, size_t len); } 204 MNOPROTO { int munlock(const void *addr, size_t len); } 205 NOPROTO { int undelete(char *path); } 206 NOPROTO { int futimes(int fd, struct timeval *tptr); } 207 MNOPROTO { int getpgid(pid_t pid); } 208 UNIMPL newreboot (NetBSD) 209 MNOPROTO { int poll(struct pollfd *fds, u_int nfds, int timeout); } ; ; The following are reserved for loadable syscalls ; 210 UNIMPL 211 UNIMPL 212 UNIMPL 213 UNIMPL 214 UNIMPL 215 UNIMPL 216 UNIMPL 217 UNIMPL 218 UNIMPL 219 UNIMPL ; ; The following were introduced with NetBSD/4.4Lite-2 ; They are initialized by thier respective modules/sysinits ; XXX PROBLEM!! 220 MNOPROTO { int __semctl(int semid, int semnum, int cmd, \ union semun *arg); } 221 MNOPROTO { int semget(key_t key, int nsems, int semflg); } 222 MNOPROTO { int semop(int semid, struct sembuf *sops, u_int nsops); } 223 UNIMPL semconfig 224 MNOPROTO { int msgctl(int msqid, int cmd, struct msqid_ds *buf); } 225 MNOPROTO { int msgget(key_t key, int msgflg); } 226 MNOPROTO { int msgsnd(int msqid, void *msgp, size_t msgsz, \ int msgflg); } 227 MNOPROTO { int msgrcv(int msqid, void *msgp, size_t msgsz, \ long msgtyp, int msgflg); } 228 MNOPROTO { int shmat(int shmid, void *shmaddr, int shmflg); } 229 MNOPROTO { int shmctl(int shmid, int cmd, struct shmid_ds *buf); } 230 MNOPROTO { int shmdt(void *shmaddr); } 231 MNOPROTO { int shmget(key_t key, int size, int shmflg); } ; 232 MNOPROTO { int clock_gettime(clockid_t clock_id, struct timespec *tp); } 233 MNOPROTO { int clock_settime(clockid_t clock_id, \ const struct timespec *tp); } 234 MNOPROTO { int clock_getres(clockid_t clock_id, struct timespec *tp); } 235 UNIMPL timer_create 236 UNIMPL timer_delete 237 UNIMPL timer_settime 238 UNIMPL timer_gettime 239 UNIMPL timer_getoverrun 240 MSTD { int freebsd32_nanosleep(const struct timespec *rqtp, \ struct timespec *rmtp); } 241 UNIMPL nosys 242 UNIMPL nosys 243 UNIMPL nosys 244 UNIMPL nosys 245 UNIMPL nosys 246 UNIMPL nosys 247 UNIMPL nosys 248 UNIMPL nosys 249 UNIMPL nosys ; syscall numbers initially used in OpenBSD 250 MNOPROTO { int minherit(void *addr, size_t len, int inherit); } 251 MNOPROTO { int rfork(int flags); } 252 MNOPROTO { int openbsd_poll(struct pollfd *fds, u_int nfds, \ int timeout); } 253 MNOPROTO { int issetugid(void); } 254 NOPROTO { int lchown(char *path, int uid, int gid); } 255 UNIMPL nosys 256 UNIMPL nosys 257 UNIMPL nosys 258 UNIMPL nosys 259 UNIMPL nosys 260 UNIMPL nosys 261 UNIMPL nosys 262 UNIMPL nosys 263 UNIMPL nosys 264 UNIMPL nosys 265 UNIMPL nosys 266 UNIMPL nosys 267 UNIMPL nosys 268 UNIMPL nosys 269 UNIMPL nosys 270 UNIMPL nosys 271 UNIMPL nosys 272 NOPROTO { int getdents(int fd, char *buf, size_t count); } 273 UNIMPL nosys 274 NOPROTO { int lchmod(char *path, mode_t mode); } 275 NOPROTO { int lchown(char *path, uid_t uid, gid_t gid); } \ netbsd_lchown lchown_args int 276 NOPROTO { int lutimes(char *path, struct timeval *tptr); } 277 MNOPROTO { int msync(void *addr, size_t len, int flags); } \ netbsd_msync msync_args int 278 NOPROTO { int nstat(char *path, struct nstat *ub); } 279 MNOPROTO { int nfstat(int fd, struct nstat *sb); } 280 NOPROTO { int nlstat(char *path, struct nstat *ub); } 281 UNIMPL nosys 282 UNIMPL nosys 283 UNIMPL nosys 284 UNIMPL nosys 285 UNIMPL nosys 286 UNIMPL nosys 287 UNIMPL nosys 288 UNIMPL nosys 289 UNIMPL nosys 290 UNIMPL nosys 291 UNIMPL nosys 292 UNIMPL nosys 293 UNIMPL nosys 294 UNIMPL nosys 295 UNIMPL nosys 296 UNIMPL nosys ; XXX 297 is 300 in NetBSD 297 COMPAT4 { int freebsd32_fhstatfs(const struct fhandle *u_fhp, \ struct statfs32 *buf); } 298 NOPROTO { int fhopen(const struct fhandle *u_fhp, int flags); } 299 NOPROTO { int fhstat(const struct fhandle *u_fhp, struct stat *sb); } ; syscall numbers for FreeBSD 300 MNOPROTO { int modnext(int modid); } 301 MSTD { int freebsd32_modstat(int modid, struct module_stat32* stat); } 302 MNOPROTO { int modfnext(int modid); } 303 MNOPROTO { int modfind(const char *name); } 304 MNOPROTO { int kldload(const char *file); } 305 MNOPROTO { int kldunload(int fileid); } 306 MNOPROTO { int kldfind(const char *file); } 307 MNOPROTO { int kldnext(int fileid); } 308 MNOPROTO { int kldstat(int fileid, struct kld_file_stat* stat); } 309 MNOPROTO { int kldfirstmod(int fileid); } 310 MNOPROTO { int getsid(pid_t pid); } 311 MNOPROTO { int setresuid(uid_t ruid, uid_t euid, uid_t suid); } 312 MNOPROTO { int setresgid(gid_t rgid, gid_t egid, gid_t sgid); } 313 OBSOL signanosleep 314 UNIMPL aio_return 315 UNIMPL aio_suspend 316 UNIMPL aio_cancel 317 UNIMPL aio_error 318 UNIMPL aio_read 319 UNIMPL aio_write 320 UNIMPL lio_listio 321 MNOPROTO { int yield(void); } 322 OBSOL thr_sleep 323 OBSOL thr_wakeup 324 MNOPROTO { int mlockall(int how); } 325 MNOPROTO { int munlockall(void); } 326 NOPROTO { int __getcwd(u_char *buf, u_int buflen); } 327 MNOPROTO { int sched_setparam (pid_t pid, \ const struct sched_param *param); } 328 MNOPROTO { int sched_getparam (pid_t pid, struct sched_param *param); } 329 MNOPROTO { int sched_setscheduler (pid_t pid, int policy, \ const struct sched_param *param); } 330 MNOPROTO { int sched_getscheduler (pid_t pid); } 331 MNOPROTO { int sched_yield (void); } 332 MNOPROTO { int sched_get_priority_max (int policy); } 333 MNOPROTO { int sched_get_priority_min (int policy); } 334 MNOPROTO { int sched_rr_get_interval (pid_t pid, \ struct timespec *interval); } 335 MNOPROTO { int utrace(const void *addr, size_t len); } ; XXX note - bigendian is different 336 MCOMPAT4 { int freebsd32_sendfile(int fd, int s, u_int32_t offsetlo, \ u_int32_t offsethi, size_t nbytes, struct sf_hdtr *hdtr, \ off_t *sbytes, int flags); } 337 NOPROTO { int kldsym(int fileid, int cmd, void *data); } 338 MNOPROTO { int jail(struct jail *jail); } 339 UNIMPL pioctl 340 MNOPROTO { int sigprocmask(int how, const sigset_t *set, \ sigset_t *oset); } 341 MNOPROTO { int sigsuspend(const sigset_t *sigmask); } 342 MCOMPAT4 { int freebsd32_sigaction(int sig, struct sigaction32 *act, \ struct sigaction32 *oact); } 343 MNOPROTO { int sigpending(sigset_t *set); } 344 MCOMPAT4 { int freebsd32_sigreturn( \ const struct freebsd4_freebsd32_ucontext *sigcntxp); } ; XXX implement 345 UNIMPL sigtimedwait ; XXX implement 346 UNIMPL sigwaitinfo 347 MNOPROTO { int __acl_get_file(const char *path, acl_type_t type, \ struct acl *aclp); } 348 MNOPROTO { int __acl_set_file(const char *path, acl_type_t type, \ struct acl *aclp); } 349 MNOPROTO { int __acl_get_fd(int filedes, acl_type_t type, \ struct acl *aclp); } 350 MNOPROTO { int __acl_set_fd(int filedes, acl_type_t type, \ struct acl *aclp); } 351 MNOPROTO { int __acl_delete_file(const char *path, acl_type_t type); } 352 MNOPROTO { int __acl_delete_fd(int filedes, acl_type_t type); } 353 MNOPROTO { int __acl_aclcheck_file(const char *path, acl_type_t type, \ struct acl *aclp); } 354 MNOPROTO { int __acl_aclcheck_fd(int filedes, acl_type_t type, \ struct acl *aclp); } 355 NOPROTO { int extattrctl(const char *path, int cmd, \ const char *filename, int attrnamespace, \ const char *attrname); } 356 NOPROTO { int extattr_set_file(const char *path, \ int attrnamespace, const char *attrname, \ void *data, size_t nbytes); } 357 NOPROTO { ssize_t extattr_get_file(const char *path, \ int attrnamespace, const char *attrname, \ void *data, size_t nbytes); } 358 NOPROTO { int extattr_delete_file(const char *path, \ int attrnamespace, const char *attrname); } 359 UNIMPL aio_waitcomplete 360 MNOPROTO { int getresuid(uid_t *ruid, uid_t *euid, uid_t *suid); } 361 MNOPROTO { int getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid); } 362 MNOPROTO { int kqueue(void); } 363 MSTD { int freebsd32_kevent(int fd, \ - const struct kevent *changelist, int nchanges, \ - struct kevent *eventlist, int nevents, \ - const struct timespec *timeout); } + const struct kevent32 *changelist, int nchanges, \ + struct kevent32 *eventlist, int nevents, \ + const struct timespec32 *timeout); } 364 UNIMPL __cap_get_proc 365 UNIMPL __cap_set_proc 366 UNIMPL __cap_get_fd 367 UNIMPL __cap_get_file 368 UNIMPL __cap_set_fd 369 UNIMPL __cap_set_file 370 UNIMPL lkmressys 371 NOPROTO { int extattr_set_fd(int fd, int attrnamespace, \ const char *attrname, void *data, size_t nbytes); } 372 NOPROTO { ssize_t extattr_get_fd(int fd, int attrnamespace, \ const char *attrname, void *data, size_t nbytes); } 373 NOPROTO { int extattr_delete_fd(int fd, int attrnamespace, \ const char *attrname); } 374 MNOPROTO { int __setugid(int flag); } 375 UNIMPL nfsclnt 376 NOPROTO { int eaccess(char *path, int flags); } 377 UNIMPL afs_syscall 378 NOPROTO { int nmount(struct iovec *iovp, unsigned int iovcnt, \ int flags); } 379 NOPROTO { int kse_exit(void); } 380 NOPROTO { int kse_wakeup(struct kse_mailbox *mbx); } 381 NOPROTO { int kse_create(struct kse_mailbox *mbx, int newgroup); } 382 NOPROTO { int kse_thr_interrupt(struct kse_thr_mailbox *tmbx); } 383 NOPROTO { int kse_release(void); } 384 UNIMPL __mac_get_proc 385 UNIMPL __mac_set_proc 386 UNIMPL __mac_get_fd 387 UNIMPL __mac_get_file 388 UNIMPL __mac_set_fd 389 UNIMPL __mac_set_file 390 NOPROTO { int kenv(int what, const char *name, char *value, int len); } 391 NOPROTO { int lchflags(const char *path, int flags); } 392 NOPROTO { int uuidgen(struct uuid *store, int count); } 393 MSTD { int freebsd32_sendfile(int fd, int s, u_int32_t offsetlo, \ u_int32_t offsethi, size_t nbytes, struct sf_hdtr *hdtr, \ off_t *sbytes, int flags); } 394 UNIMPL mac_syscall 395 NOPROTO { int getfsstat(struct statfs *buf, long bufsize, \ int flags); } 396 NOPROTO { int statfs(char *path, struct statfs *buf); } 397 NOPROTO { int fstatfs(int fd, struct statfs *buf); } 398 NOPROTO { int fhstatfs(const struct fhandle *u_fhp, \ struct statfs *buf); } 399 UNIMPL nosys ; XXX implement these? 400 UNIMPL ksem_close 401 UNIMPL ksem_post 402 UNIMPL ksem_wait 403 UNIMPL ksem_trywait 404 UNIMPL ksem_init 405 UNIMPL ksem_open 406 UNIMPL ksem_unlink 407 UNIMPL ksem_getvalue 408 UNIMPL ksem_destroy 409 UNIMPL __mac_get_pid 410 UNIMPL __mac_get_link 411 UNIMPL __mac_set_link 412 UNIMPL extattr_set_link 413 UNIMPL extattr_get_link 414 UNIMPL extattr_delete_link 415 UNIMPL __mac_execve 416 STD { int freebsd32_sigaction(int sig, struct sigaction32 *act, \ struct sigaction32 *oact); } 417 MSTD { int freebsd32_sigreturn( \ const struct freebsd32_ucontext *sigcntxp); } 418 UNIMPL __xstat 419 UNIMPL __xfstat 420 UNIMPL __xlstat ; XXX implement 421 UNIMPL getcontext ; XXX implement 422 UNIMPL setcontext ; XXX implement 423 UNIMPL swapcontext 424 UNIMPL swapoff 425 UNIMPL __acl_get_link 426 UNIMPL __acl_set_link 427 UNIMPL __acl_delete_link 428 UNIMPL __acl_aclcheck_link ; XXX implement 429 UNIMPL sigwait 430 MNOPROTO { int thr_create(ucontext_t *ctx, long *id, int flag s); } 431 MNOPROTO { void thr_exit(long *state); } 432 MNOPROTO { int thr_self(long *id); } 433 MNOPROTO { int thr_kill(long id, int sig); } 434 MNOPROTO { int _umtx_lock(struct umtx *umtx); } 435 MNOPROTO { int _umtx_unlock(struct umtx *umtx); } 436 MNOPROTO { int jail_attach(int jid); } 437 UNIMPL extattr_list_fd 438 UNIMPL extattr_list_file 439 UNIMPL extattr_list_link 440 UNIMPL kse_switchin 441 UNIMPL ksem_timedwait 442 MNOPROTO { int thr_suspend(const struct timespec *timeout); } 443 MNOPROTO { int thr_wake(long id); } 444 MNOPROTO { int kldunloadf(int fileid, int flags); } Index: stable/5/sys/kern/kern_event.c =================================================================== --- stable/5/sys/kern/kern_event.c (revision 145377) +++ stable/5/sys/kern/kern_event.c (revision 145378) @@ -1,1799 +1,1841 @@ /*- * Copyright (c) 1999,2000,2001 Jonathan Lemon * Copyright 2004 John-Mark Gurney * 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #include #include #include MALLOC_DEFINE(M_KQUEUE, "kqueue", "memory for kqueue system"); /* * This lock is used if multiple kq locks are required. This possibly * should be made into a per proc lock. */ static struct mtx kq_global; MTX_SYSINIT(kq_global, &kq_global, "kqueue order", MTX_DEF); #define KQ_GLOBAL_LOCK(lck, haslck) do { \ if (!haslck) \ mtx_lock(lck); \ haslck = 1; \ } while (0) #define KQ_GLOBAL_UNLOCK(lck, haslck) do { \ if (haslck) \ mtx_unlock(lck); \ haslck = 0; \ } while (0) TASKQUEUE_DEFINE_THREAD(kqueue); +static int kevent_copyout(struct kevent **eventlist, enum uio_seg eventseg, + struct kevent *kevp, int count); static int kqueue_aquire(struct file *fp, struct kqueue **kqp); static void kqueue_release(struct kqueue *kq, int locked); static int kqueue_expand(struct kqueue *kq, struct filterops *fops, uintptr_t ident, int waitok); static void kqueue_task(void *arg, int pending); static int kqueue_scan(struct kqueue *kq, int maxevents, - struct kevent *ulistp, const struct timespec *timeout, - struct kevent *keva, struct thread *td); + struct kevent *eventlist, enum uio_seg eventseg, + const struct timespec *timeout, struct kevent *keva, + struct thread *td); static void kqueue_wakeup(struct kqueue *kq); static struct filterops *kqueue_fo_find(int filt); static void kqueue_fo_release(int filt); static fo_rdwr_t kqueue_read; static fo_rdwr_t kqueue_write; static fo_ioctl_t kqueue_ioctl; static fo_poll_t kqueue_poll; static fo_kqfilter_t kqueue_kqfilter; static fo_stat_t kqueue_stat; static fo_close_t kqueue_close; static struct fileops kqueueops = { .fo_read = kqueue_read, .fo_write = kqueue_write, .fo_ioctl = kqueue_ioctl, .fo_poll = kqueue_poll, .fo_kqfilter = kqueue_kqfilter, .fo_stat = kqueue_stat, .fo_close = kqueue_close, }; static int knote_attach(struct knote *kn, struct kqueue *kq); static void knote_drop(struct knote *kn, struct thread *td); static void knote_enqueue(struct knote *kn); static void knote_dequeue(struct knote *kn); static void knote_init(void); static struct knote *knote_alloc(int waitok); static void knote_free(struct knote *kn); static void filt_kqdetach(struct knote *kn); static int filt_kqueue(struct knote *kn, long hint); static int filt_procattach(struct knote *kn); static void filt_procdetach(struct knote *kn); static int filt_proc(struct knote *kn, long hint); static int filt_fileattach(struct knote *kn); static void filt_timerexpire(void *knx); static int filt_timerattach(struct knote *kn); static void filt_timerdetach(struct knote *kn); static int filt_timer(struct knote *kn, long hint); static struct filterops file_filtops = { 1, filt_fileattach, NULL, NULL }; static struct filterops kqread_filtops = { 1, NULL, filt_kqdetach, filt_kqueue }; /* XXX - move to kern_proc.c? */ static struct filterops proc_filtops = { 0, filt_procattach, filt_procdetach, filt_proc }; static struct filterops timer_filtops = { 0, filt_timerattach, filt_timerdetach, filt_timer }; static uma_zone_t knote_zone; static int kq_ncallouts = 0; static int kq_calloutmax = (4 * 1024); SYSCTL_INT(_kern, OID_AUTO, kq_calloutmax, CTLFLAG_RW, &kq_calloutmax, 0, "Maximum number of callouts allocated for kqueue"); /* XXX - ensure not KN_INFLUX?? */ #define KNOTE_ACTIVATE(kn, islock) do { \ if ((islock)) \ mtx_assert(&(kn)->kn_kq->kq_lock, MA_OWNED); \ else \ KQ_LOCK((kn)->kn_kq); \ (kn)->kn_status |= KN_ACTIVE; \ if (((kn)->kn_status & (KN_QUEUED | KN_DISABLED)) == 0) \ knote_enqueue((kn)); \ if (!(islock)) \ KQ_UNLOCK((kn)->kn_kq); \ } while(0) #define KQ_LOCK(kq) do { \ mtx_lock(&(kq)->kq_lock); \ } while (0) #define KQ_FLUX_WAKEUP(kq) do { \ if (((kq)->kq_state & KQ_FLUXWAIT) == KQ_FLUXWAIT) { \ (kq)->kq_state &= ~KQ_FLUXWAIT; \ wakeup((kq)); \ } \ } while (0) #define KQ_UNLOCK_FLUX(kq) do { \ KQ_FLUX_WAKEUP(kq); \ mtx_unlock(&(kq)->kq_lock); \ } while (0) #define KQ_UNLOCK(kq) do { \ mtx_unlock(&(kq)->kq_lock); \ } while (0) #define KQ_OWNED(kq) do { \ mtx_assert(&(kq)->kq_lock, MA_OWNED); \ } while (0) #define KQ_NOTOWNED(kq) do { \ mtx_assert(&(kq)->kq_lock, MA_NOTOWNED); \ } while (0) #define KN_LIST_LOCK(kn) do { \ if (kn->kn_knlist != NULL) \ mtx_lock(kn->kn_knlist->kl_lock); \ } while (0) #define KN_LIST_UNLOCK(kn) do { \ if (kn->kn_knlist != NULL) \ mtx_unlock(kn->kn_knlist->kl_lock); \ } while (0) #define KN_HASHSIZE 64 /* XXX should be tunable */ #define KN_HASH(val, mask) (((val) ^ (val >> 8)) & (mask)) static int filt_nullattach(struct knote *kn) { return (ENXIO); }; struct filterops null_filtops = { 0, filt_nullattach, NULL, NULL }; /* XXX - make SYSINIT to add these, and move into respective modules. */ extern struct filterops sig_filtops; extern struct filterops fs_filtops; /* * Table for for all system-defined filters. */ static struct mtx filterops_lock; MTX_SYSINIT(kqueue_filterops, &filterops_lock, "protect sysfilt_ops", MTX_DEF); static struct { struct filterops *for_fop; int for_refcnt; } sysfilt_ops[EVFILT_SYSCOUNT] = { { &file_filtops }, /* EVFILT_READ */ { &file_filtops }, /* EVFILT_WRITE */ { &null_filtops }, /* EVFILT_AIO */ { &file_filtops }, /* EVFILT_VNODE */ { &proc_filtops }, /* EVFILT_PROC */ { &sig_filtops }, /* EVFILT_SIGNAL */ { &timer_filtops }, /* EVFILT_TIMER */ { &file_filtops }, /* EVFILT_NETDEV */ { &fs_filtops }, /* EVFILT_FS */ }; /* * Simple redirection for all cdevsw style objects to call their fo_kqfilter * method. */ static int filt_fileattach(struct knote *kn) { return (fo_kqfilter(kn->kn_fp, kn)); } /*ARGSUSED*/ static int kqueue_kqfilter(struct file *fp, struct knote *kn) { struct kqueue *kq = kn->kn_fp->f_data; if (kn->kn_filter != EVFILT_READ) return (EINVAL); kn->kn_status |= KN_KQUEUE; kn->kn_fop = &kqread_filtops; knlist_add(&kq->kq_sel.si_note, kn, 0); return (0); } static void filt_kqdetach(struct knote *kn) { struct kqueue *kq = kn->kn_fp->f_data; knlist_remove(&kq->kq_sel.si_note, kn, 0); } /*ARGSUSED*/ static int filt_kqueue(struct knote *kn, long hint) { struct kqueue *kq = kn->kn_fp->f_data; kn->kn_data = kq->kq_count; return (kn->kn_data > 0); } /* XXX - move to kern_proc.c? */ static int filt_procattach(struct knote *kn) { struct proc *p; int immediate; int error; immediate = 0; p = pfind(kn->kn_id); if (p == NULL && (kn->kn_sfflags & NOTE_EXIT)) { p = zpfind(kn->kn_id); immediate = 1; } else if (p != NULL && (p->p_flag & P_WEXIT)) { immediate = 1; } if (p == NULL) return (ESRCH); if ((error = p_cansee(curthread, p))) return (error); kn->kn_ptr.p_proc = p; kn->kn_flags |= EV_CLEAR; /* automatically set */ /* * internal flag indicating registration done by kernel */ if (kn->kn_flags & EV_FLAG1) { kn->kn_data = kn->kn_sdata; /* ppid */ kn->kn_fflags = NOTE_CHILD; kn->kn_flags &= ~EV_FLAG1; } if (immediate == 0) knlist_add(&p->p_klist, kn, 1); /* * Immediately activate any exit notes if the target process is a * zombie. This is necessary to handle the case where the target * process, e.g. a child, dies before the kevent is registered. */ if (immediate && filt_proc(kn, NOTE_EXIT)) KNOTE_ACTIVATE(kn, 0); PROC_UNLOCK(p); return (0); } /* * The knote may be attached to a different process, which may exit, * leaving nothing for the knote to be attached to. So when the process * exits, the knote is marked as DETACHED and also flagged as ONESHOT so * it will be deleted when read out. However, as part of the knote deletion, * this routine is called, so a check is needed to avoid actually performing * a detach, because the original process does not exist any more. */ /* XXX - move to kern_proc.c? */ static void filt_procdetach(struct knote *kn) { struct proc *p; p = kn->kn_ptr.p_proc; knlist_remove(&p->p_klist, kn, 0); kn->kn_ptr.p_proc = NULL; } /* XXX - move to kern_proc.c? */ static int filt_proc(struct knote *kn, long hint) { struct proc *p = kn->kn_ptr.p_proc; u_int event; /* * mask off extra data */ event = (u_int)hint & NOTE_PCTRLMASK; /* * if the user is interested in this event, record it. */ if (kn->kn_sfflags & event) kn->kn_fflags |= event; /* * process is gone, so flag the event as finished. */ if (event == NOTE_EXIT) { if (!(kn->kn_status & KN_DETACHED)) knlist_remove_inevent(&p->p_klist, kn); kn->kn_flags |= (EV_EOF | EV_ONESHOT); kn->kn_ptr.p_proc = NULL; return (1); } /* * process forked, and user wants to track the new process, * so attach a new knote to it, and immediately report an * event with the parent's pid. */ if ((event == NOTE_FORK) && (kn->kn_sfflags & NOTE_TRACK)) { struct kevent kev; int error; /* * register knote with new process. */ kev.ident = hint & NOTE_PDATAMASK; /* pid */ kev.filter = kn->kn_filter; kev.flags = kn->kn_flags | EV_ADD | EV_ENABLE | EV_FLAG1; kev.fflags = kn->kn_sfflags; kev.data = kn->kn_id; /* parent */ kev.udata = kn->kn_kevent.udata; /* preserve udata */ error = kqueue_register(kn->kn_kq, &kev, NULL, 0); if (error) kn->kn_fflags |= NOTE_TRACKERR; } return (kn->kn_fflags != 0); } static int timertoticks(intptr_t data) { struct timeval tv; int tticks; tv.tv_sec = data / 1000; tv.tv_usec = (data % 1000) * 1000; tticks = tvtohz(&tv); return tticks; } /* XXX - move to kern_timeout.c? */ static void filt_timerexpire(void *knx) { struct knote *kn = knx; struct callout *calloutp; kn->kn_data++; KNOTE_ACTIVATE(kn, 0); /* XXX - handle locking */ if ((kn->kn_flags & EV_ONESHOT) != EV_ONESHOT) { calloutp = (struct callout *)kn->kn_hook; callout_reset(calloutp, timertoticks(kn->kn_sdata), filt_timerexpire, kn); } } /* * data contains amount of time to sleep, in milliseconds */ /* XXX - move to kern_timeout.c? */ static int filt_timerattach(struct knote *kn) { struct callout *calloutp; atomic_add_int(&kq_ncallouts, 1); if (kq_ncallouts >= kq_calloutmax) { atomic_add_int(&kq_ncallouts, -1); return (ENOMEM); } kn->kn_flags |= EV_CLEAR; /* automatically set */ kn->kn_status &= ~KN_DETACHED; /* knlist_add usually sets it */ MALLOC(calloutp, struct callout *, sizeof(*calloutp), M_KQUEUE, M_WAITOK); callout_init(calloutp, CALLOUT_MPSAFE); kn->kn_hook = calloutp; callout_reset(calloutp, timertoticks(kn->kn_sdata), filt_timerexpire, kn); return (0); } /* XXX - move to kern_timeout.c? */ static void filt_timerdetach(struct knote *kn) { struct callout *calloutp; calloutp = (struct callout *)kn->kn_hook; callout_drain(calloutp); FREE(calloutp, M_KQUEUE); atomic_add_int(&kq_ncallouts, -1); kn->kn_status |= KN_DETACHED; /* knlist_remove usually clears it */ } /* XXX - move to kern_timeout.c? */ static int filt_timer(struct knote *kn, long hint) { return (kn->kn_data != 0); } /* * MPSAFE */ int kqueue(struct thread *td, struct kqueue_args *uap) { struct filedesc *fdp; struct kqueue *kq; struct file *fp; int fd, error; fdp = td->td_proc->p_fd; error = falloc(td, &fp, &fd); if (error) goto done2; /* An extra reference on `nfp' has been held for us by falloc(). */ kq = malloc(sizeof *kq, M_KQUEUE, M_WAITOK | M_ZERO); mtx_init(&kq->kq_lock, "kqueue", NULL, MTX_DEF|MTX_DUPOK); TAILQ_INIT(&kq->kq_head); kq->kq_fdp = fdp; knlist_init(&kq->kq_sel.si_note, &kq->kq_lock); TASK_INIT(&kq->kq_task, 0, kqueue_task, kq); FILEDESC_LOCK_FAST(fdp); SLIST_INSERT_HEAD(&fdp->fd_kqlist, kq, kq_list); FILEDESC_UNLOCK_FAST(fdp); FILE_LOCK(fp); fp->f_flag = FREAD | FWRITE; fp->f_type = DTYPE_KQUEUE; fp->f_ops = &kqueueops; fp->f_data = kq; FILE_UNLOCK(fp); fdrop(fp, td); td->td_retval[0] = fd; done2: return (error); } #ifndef _SYS_SYSPROTO_H_ struct kevent_args { int fd; const struct kevent *changelist; int nchanges; struct kevent *eventlist; int nevents; const struct timespec *timeout; }; #endif /* * MPSAFE */ int kevent(struct thread *td, struct kevent_args *uap) { + struct timespec ts, *tsp; + int error; + + if (uap->timeout != NULL) { + error = copyin(uap->timeout, &ts, sizeof(ts)); + if (error) + return (error); + tsp = &ts; + } else + tsp = NULL; + + return (kern_kevent(td, uap->fd, uap->changelist, uap->nchanges, + UIO_USERSPACE, uap->eventlist, uap->nevents, UIO_USERSPACE, tsp)); +} + +/* + * Copy 'count' items into the destination list pointd to by *eventlist. The + * eventlist and nevents values are updated to point after the copied out + * item(s) upon return. + */ +static int +kevent_copyout(struct kevent **eventlist, enum uio_seg eventseg, + struct kevent *kevp, int count) +{ + int error; + + if (eventseg == UIO_USERSPACE) + error = copyout(kevp, *eventlist, + sizeof(struct kevent) * count); + else { + bcopy(kevp, *eventlist, sizeof(struct kevent) * count); + error = 0; + } + *eventlist += count; + return (error); +} + +int +kern_kevent(struct thread *td, int fd, struct kevent *changelist, int nchanges, + enum uio_seg changeseg, struct kevent *eventlist, int nevents, + enum uio_seg eventseg, const struct timespec *timeout) +{ struct kevent keva[KQ_NEVENTS]; - struct kevent *kevp; + struct kevent *kevp, *changes; struct kqueue *kq; struct file *fp; - struct timespec ts; int i, n, nerrors, error; - if ((error = fget(td, uap->fd, &fp)) != 0) + if ((error = fget(td, fd, &fp)) != 0) return (error); if ((error = kqueue_aquire(fp, &kq)) != 0) goto done_norel; - if (uap->timeout != NULL) { - error = copyin(uap->timeout, &ts, sizeof(ts)); - if (error) - goto done; - uap->timeout = &ts; - } - nerrors = 0; - while (uap->nchanges > 0) { - n = uap->nchanges > KQ_NEVENTS ? KQ_NEVENTS : uap->nchanges; - error = copyin(uap->changelist, keva, - n * sizeof *keva); - if (error) - goto done; + while (nchanges > 0) { + if (changeseg == UIO_USERSPACE) { + n = nchanges > KQ_NEVENTS ? KQ_NEVENTS : nchanges; + error = copyin(changelist, keva, n * sizeof *keva); + if (error) + goto done; + changes = keva; + } else { + changes = changelist; + n = nchanges; + } for (i = 0; i < n; i++) { - kevp = &keva[i]; + kevp = &changes[i]; kevp->flags &= ~EV_SYSFLAGS; error = kqueue_register(kq, kevp, td, 1); if (error) { - if (uap->nevents != 0) { + if (nevents != 0) { kevp->flags = EV_ERROR; kevp->data = error; - (void) copyout(kevp, - uap->eventlist, - sizeof(*kevp)); - uap->eventlist++; - uap->nevents--; + (void) kevent_copyout(&eventlist, + eventseg, kevp, 1); + nevents--; nerrors++; } else { goto done; } } } - uap->nchanges -= n; - uap->changelist += n; + nchanges -= n; + changelist += n; } if (nerrors) { td->td_retval[0] = nerrors; error = 0; goto done; } - error = kqueue_scan(kq, uap->nevents, uap->eventlist, uap->timeout, + error = kqueue_scan(kq, nevents, eventlist, eventseg, timeout, keva, td); done: kqueue_release(kq, 0); done_norel: if (fp != NULL) fdrop(fp, td); return (error); } int kqueue_add_filteropts(int filt, struct filterops *filtops) { int error; if (filt > 0 || filt + EVFILT_SYSCOUNT < 0) { printf( "trying to add a filterop that is out of range: %d is beyond %d\n", ~filt, EVFILT_SYSCOUNT); return EINVAL; } mtx_lock(&filterops_lock); if (sysfilt_ops[~filt].for_fop != &null_filtops && sysfilt_ops[~filt].for_fop != NULL) error = EEXIST; else { sysfilt_ops[~filt].for_fop = filtops; sysfilt_ops[~filt].for_refcnt = 0; } mtx_unlock(&filterops_lock); return (0); } int kqueue_del_filteropts(int filt) { int error; error = 0; if (filt > 0 || filt + EVFILT_SYSCOUNT < 0) return EINVAL; mtx_lock(&filterops_lock); if (sysfilt_ops[~filt].for_fop == &null_filtops || sysfilt_ops[~filt].for_fop == NULL) error = EINVAL; else if (sysfilt_ops[~filt].for_refcnt != 0) error = EBUSY; else { sysfilt_ops[~filt].for_fop = &null_filtops; sysfilt_ops[~filt].for_refcnt = 0; } mtx_unlock(&filterops_lock); return error; } static struct filterops * kqueue_fo_find(int filt) { if (filt > 0 || filt + EVFILT_SYSCOUNT < 0) return NULL; mtx_lock(&filterops_lock); sysfilt_ops[~filt].for_refcnt++; if (sysfilt_ops[~filt].for_fop == NULL) sysfilt_ops[~filt].for_fop = &null_filtops; mtx_unlock(&filterops_lock); return sysfilt_ops[~filt].for_fop; } static void kqueue_fo_release(int filt) { if (filt > 0 || filt + EVFILT_SYSCOUNT < 0) return; mtx_lock(&filterops_lock); KASSERT(sysfilt_ops[~filt].for_refcnt > 0, ("filter object refcount not valid on release")); sysfilt_ops[~filt].for_refcnt--; mtx_unlock(&filterops_lock); } /* * A ref to kq (obtained via kqueue_aquire) should be held. waitok will * influence if memory allocation should wait. Make sure it is 0 if you * hold any mutexes. */ int kqueue_register(struct kqueue *kq, struct kevent *kev, struct thread *td, int waitok) { struct filedesc *fdp; struct filterops *fops; struct file *fp; struct knote *kn, *tkn; int error, filt, event; int haskqglobal; int fd; fdp = NULL; fp = NULL; kn = NULL; error = 0; haskqglobal = 0; filt = kev->filter; fops = kqueue_fo_find(filt); if (fops == NULL) return EINVAL; tkn = knote_alloc(waitok); /* prevent waiting with locks */ findkn: if (fops->f_isfd) { KASSERT(td != NULL, ("td is NULL")); fdp = td->td_proc->p_fd; FILEDESC_LOCK(fdp); /* validate descriptor */ fd = kev->ident; if (fd < 0 || fd >= fdp->fd_nfiles || (fp = fdp->fd_ofiles[fd]) == NULL) { FILEDESC_UNLOCK(fdp); error = EBADF; goto done; } fhold(fp); if ((kev->flags & EV_ADD) == EV_ADD && kqueue_expand(kq, fops, kev->ident, 0) != 0) { /* unlock and try again */ FILEDESC_UNLOCK(fdp); fdrop(fp, td); fp = NULL; error = kqueue_expand(kq, fops, kev->ident, waitok); if (error) goto done; goto findkn; } if (fp->f_type == DTYPE_KQUEUE) { /* * if we add some inteligence about what we are doing, * we should be able to support events on ourselves. * We need to know when we are doing this to prevent * getting both the knlist lock and the kq lock since * they are the same thing. */ if (fp->f_data == kq) { FILEDESC_UNLOCK(fdp); error = EINVAL; goto done_noglobal; } KQ_GLOBAL_LOCK(&kq_global, haskqglobal); } FILEDESC_UNLOCK(fdp); KQ_LOCK(kq); if (kev->ident < kq->kq_knlistsize) { SLIST_FOREACH(kn, &kq->kq_knlist[kev->ident], kn_link) if (kev->filter == kn->kn_filter) break; } } else { if ((kev->flags & EV_ADD) == EV_ADD) kqueue_expand(kq, fops, kev->ident, waitok); KQ_LOCK(kq); if (kq->kq_knhashmask != 0) { struct klist *list; list = &kq->kq_knhash[ KN_HASH((u_long)kev->ident, kq->kq_knhashmask)]; SLIST_FOREACH(kn, list, kn_link) if (kev->ident == kn->kn_id && kev->filter == kn->kn_filter) break; } } /* knote is in the process of changing, wait for it to stablize. */ if (kn != NULL && (kn->kn_status & KN_INFLUX) == KN_INFLUX) { if (fp != NULL) { fdrop(fp, td); fp = NULL; } KQ_GLOBAL_UNLOCK(&kq_global, haskqglobal); kq->kq_state |= KQ_FLUXWAIT; msleep(kq, &kq->kq_lock, PSOCK | PDROP, "kqflxwt", 0); goto findkn; } if (kn == NULL && ((kev->flags & EV_ADD) == 0)) { KQ_UNLOCK(kq); error = ENOENT; goto done; } /* * kn now contains the matching knote, or NULL if no match */ if (kev->flags & EV_ADD) { if (kn == NULL) { kn = tkn; tkn = NULL; if (kn == NULL) { error = ENOMEM; goto done; } kn->kn_fp = fp; kn->kn_kq = kq; kn->kn_fop = fops; /* * apply reference counts to knote structure, and * do not release it at the end of this routine. */ fops = NULL; fp = NULL; kn->kn_sfflags = kev->fflags; kn->kn_sdata = kev->data; kev->fflags = 0; kev->data = 0; kn->kn_kevent = *kev; kn->kn_status = KN_INFLUX|KN_DETACHED; error = knote_attach(kn, kq); KQ_UNLOCK(kq); if (error != 0) { tkn = kn; goto done; } if ((error = kn->kn_fop->f_attach(kn)) != 0) { knote_drop(kn, td); goto done; } KN_LIST_LOCK(kn); } else { /* * The user may change some filter values after the * initial EV_ADD, but doing so will not reset any * filter which has already been triggered. */ kn->kn_status |= KN_INFLUX; KQ_UNLOCK(kq); KN_LIST_LOCK(kn); kn->kn_sfflags = kev->fflags; kn->kn_sdata = kev->data; kn->kn_kevent.udata = kev->udata; } /* * We can get here with kn->kn_knlist == NULL. * This can happen when the initial attach event decides that * the event is "completed" already. i.e. filt_procattach * is called on a zombie process. It will call filt_proc * which will remove it from the list, and NULL kn_knlist. */ event = kn->kn_fop->f_event(kn, 0); KN_LIST_UNLOCK(kn); KQ_LOCK(kq); if (event) KNOTE_ACTIVATE(kn, 1); kn->kn_status &= ~KN_INFLUX; } else if (kev->flags & EV_DELETE) { kn->kn_status |= KN_INFLUX; KQ_UNLOCK(kq); if (!(kn->kn_status & KN_DETACHED)) kn->kn_fop->f_detach(kn); knote_drop(kn, td); goto done; } if ((kev->flags & EV_DISABLE) && ((kn->kn_status & KN_DISABLED) == 0)) { kn->kn_status |= KN_DISABLED; } if ((kev->flags & EV_ENABLE) && (kn->kn_status & KN_DISABLED)) { kn->kn_status &= ~KN_DISABLED; if ((kn->kn_status & KN_ACTIVE) && ((kn->kn_status & KN_QUEUED) == 0)) knote_enqueue(kn); } KQ_UNLOCK_FLUX(kq); done: KQ_GLOBAL_UNLOCK(&kq_global, haskqglobal); done_noglobal: if (fp != NULL) fdrop(fp, td); if (tkn != NULL) knote_free(tkn); if (fops != NULL) kqueue_fo_release(filt); return (error); } static int kqueue_aquire(struct file *fp, struct kqueue **kqp) { int error; struct kqueue *kq; error = 0; FILE_LOCK(fp); do { kq = fp->f_data; if (fp->f_type != DTYPE_KQUEUE || kq == NULL) { error = EBADF; break; } *kqp = kq; KQ_LOCK(kq); if ((kq->kq_state & KQ_CLOSING) == KQ_CLOSING) { KQ_UNLOCK(kq); error = EBADF; break; } kq->kq_refcnt++; KQ_UNLOCK(kq); } while (0); FILE_UNLOCK(fp); return error; } static void kqueue_release(struct kqueue *kq, int locked) { if (locked) KQ_OWNED(kq); else KQ_LOCK(kq); kq->kq_refcnt--; if (kq->kq_refcnt == 1) wakeup(&kq->kq_refcnt); if (!locked) KQ_UNLOCK(kq); } static void kqueue_schedtask(struct kqueue *kq) { KQ_OWNED(kq); KASSERT(((kq->kq_state & KQ_TASKDRAIN) != KQ_TASKDRAIN), ("scheduling kqueue task while draining")); if ((kq->kq_state & KQ_TASKSCHED) != KQ_TASKSCHED) { taskqueue_enqueue(taskqueue_kqueue, &kq->kq_task); kq->kq_state |= KQ_TASKSCHED; } } /* * Expand the kq to make sure we have storage for fops/ident pair. * * Return 0 on success (or no work necessary), return errno on failure. * * Not calling hashinit w/ waitok (proper malloc flag) should be safe. * If kqueue_register is called from a non-fd context, there usually/should * be no locks held. */ static int kqueue_expand(struct kqueue *kq, struct filterops *fops, uintptr_t ident, int waitok) { struct klist *list, *tmp_knhash; u_long tmp_knhashmask; int size; int fd; int mflag = waitok ? M_WAITOK : M_NOWAIT; KQ_NOTOWNED(kq); if (fops->f_isfd) { fd = ident; if (kq->kq_knlistsize <= fd) { size = kq->kq_knlistsize; while (size <= fd) size += KQEXTENT; MALLOC(list, struct klist *, size * sizeof list, M_KQUEUE, mflag); if (list == NULL) return ENOMEM; KQ_LOCK(kq); if (kq->kq_knlistsize > fd) { FREE(list, M_KQUEUE); list = NULL; } else { if (kq->kq_knlist != NULL) { bcopy(kq->kq_knlist, list, kq->kq_knlistsize * sizeof list); FREE(kq->kq_knlist, M_KQUEUE); kq->kq_knlist = NULL; } bzero((caddr_t)list + kq->kq_knlistsize * sizeof list, (size - kq->kq_knlistsize) * sizeof list); kq->kq_knlistsize = size; kq->kq_knlist = list; } KQ_UNLOCK(kq); } } else { if (kq->kq_knhashmask == 0) { tmp_knhash = hashinit(KN_HASHSIZE, M_KQUEUE, &tmp_knhashmask); if (tmp_knhash == NULL) return ENOMEM; KQ_LOCK(kq); if (kq->kq_knhashmask == 0) { kq->kq_knhash = tmp_knhash; kq->kq_knhashmask = tmp_knhashmask; } else { free(tmp_knhash, M_KQUEUE); } KQ_UNLOCK(kq); } } KQ_NOTOWNED(kq); return 0; } static void kqueue_task(void *arg, int pending) { struct kqueue *kq; int haskqglobal; haskqglobal = 0; kq = arg; KQ_GLOBAL_LOCK(&kq_global, haskqglobal); KQ_LOCK(kq); KNOTE_LOCKED(&kq->kq_sel.si_note, 0); kq->kq_state &= ~KQ_TASKSCHED; if ((kq->kq_state & KQ_TASKDRAIN) == KQ_TASKDRAIN) { wakeup(&kq->kq_state); } KQ_UNLOCK(kq); KQ_GLOBAL_UNLOCK(&kq_global, haskqglobal); } /* * Scan, update kn_data (if not ONESHOT), and copyout triggered events. * We treat KN_MARKER knotes as if they are INFLUX. */ static int -kqueue_scan(struct kqueue *kq, int maxevents, struct kevent *ulistp, - const struct timespec *tsp, struct kevent *keva, struct thread *td) +kqueue_scan(struct kqueue *kq, int maxevents, struct kevent *eventlist, + enum uio_seg eventseg, const struct timespec *tsp, struct kevent *keva, + struct thread *td) { struct kevent *kevp; struct timeval atv, rtv, ttv; struct knote *kn, *marker; int count, timeout, nkev, error; int haskqglobal; count = maxevents; nkev = 0; error = 0; haskqglobal = 0; if (maxevents == 0) goto done_nl; if (tsp != NULL) { TIMESPEC_TO_TIMEVAL(&atv, tsp); if (itimerfix(&atv)) { error = EINVAL; goto done_nl; } if (tsp->tv_sec == 0 && tsp->tv_nsec == 0) timeout = -1; else timeout = atv.tv_sec > 24 * 60 * 60 ? 24 * 60 * 60 * hz : tvtohz(&atv); getmicrouptime(&rtv); timevaladd(&atv, &rtv); } else { atv.tv_sec = 0; atv.tv_usec = 0; timeout = 0; } marker = knote_alloc(1); if (marker == NULL) { error = ENOMEM; goto done_nl; } marker->kn_status = KN_MARKER; KQ_LOCK(kq); goto start; retry: if (atv.tv_sec || atv.tv_usec) { getmicrouptime(&rtv); if (timevalcmp(&rtv, &atv, >=)) goto done; ttv = atv; timevalsub(&ttv, &rtv); timeout = ttv.tv_sec > 24 * 60 * 60 ? 24 * 60 * 60 * hz : tvtohz(&ttv); } start: kevp = keva; if (kq->kq_count == 0) { if (timeout < 0) { error = EWOULDBLOCK; } else { KQ_GLOBAL_UNLOCK(&kq_global, haskqglobal); kq->kq_state |= KQ_SLEEP; error = msleep(kq, &kq->kq_lock, PSOCK | PCATCH, "kqread", timeout); } if (error == 0) goto retry; /* don't restart after signals... */ if (error == ERESTART) error = EINTR; else if (error == EWOULDBLOCK) error = 0; goto done; } TAILQ_INSERT_TAIL(&kq->kq_head, marker, kn_tqe); while (count) { KQ_OWNED(kq); kn = TAILQ_FIRST(&kq->kq_head); if ((kn->kn_status == KN_MARKER && kn != marker) || (kn->kn_status & KN_INFLUX) == KN_INFLUX) { KQ_GLOBAL_UNLOCK(&kq_global, haskqglobal); kq->kq_state |= KQ_FLUXWAIT; error = msleep(kq, &kq->kq_lock, PSOCK, "kqflxwt", 0); continue; } TAILQ_REMOVE(&kq->kq_head, kn, kn_tqe); if ((kn->kn_status & KN_DISABLED) == KN_DISABLED) { kn->kn_status &= ~KN_QUEUED; kq->kq_count--; continue; } if (kn == marker) { KQ_FLUX_WAKEUP(kq); if (count == maxevents) goto retry; goto done; } KASSERT((kn->kn_status & KN_INFLUX) == 0, ("KN_INFLUX set when not suppose to be")); if ((kn->kn_flags & EV_ONESHOT) == EV_ONESHOT) { kn->kn_status &= ~KN_QUEUED; kn->kn_status |= KN_INFLUX; kq->kq_count--; KQ_UNLOCK(kq); /* * We don't need to lock the list since we've marked * it _INFLUX. */ *kevp = kn->kn_kevent; if (!(kn->kn_status & KN_DETACHED)) kn->kn_fop->f_detach(kn); knote_drop(kn, td); KQ_LOCK(kq); kn = NULL; } else { kn->kn_status |= KN_INFLUX; KQ_UNLOCK(kq); if ((kn->kn_status & KN_KQUEUE) == KN_KQUEUE) KQ_GLOBAL_LOCK(&kq_global, haskqglobal); KN_LIST_LOCK(kn); if (kn->kn_fop->f_event(kn, 0) == 0) { KN_LIST_UNLOCK(kn); KQ_LOCK(kq); kn->kn_status &= ~(KN_QUEUED | KN_ACTIVE | KN_INFLUX); kq->kq_count--; continue; } *kevp = kn->kn_kevent; KQ_LOCK(kq); if (kn->kn_flags & EV_CLEAR) { kn->kn_data = 0; kn->kn_fflags = 0; kn->kn_status &= ~(KN_QUEUED | KN_ACTIVE); kq->kq_count--; } else TAILQ_INSERT_TAIL(&kq->kq_head, kn, kn_tqe); KN_LIST_UNLOCK(kn); kn->kn_status &= ~(KN_INFLUX); } /* we are returning a copy to the user */ kevp++; nkev++; count--; if (nkev == KQ_NEVENTS) { KQ_UNLOCK_FLUX(kq); - error = copyout(keva, ulistp, sizeof *keva * nkev); - ulistp += nkev; + error = kevent_copyout(&eventlist, eventseg, keva, + nkev); nkev = 0; kevp = keva; KQ_LOCK(kq); if (error) break; } } TAILQ_REMOVE(&kq->kq_head, marker, kn_tqe); done: KQ_OWNED(kq); KQ_UNLOCK_FLUX(kq); KQ_GLOBAL_UNLOCK(&kq_global, haskqglobal); knote_free(marker); done_nl: KQ_NOTOWNED(kq); if (nkev != 0) - error = copyout(keva, ulistp, sizeof *keva * nkev); + error = kevent_copyout(&eventlist, eventseg, keva, nkev); td->td_retval[0] = maxevents - count; return (error); } /* * XXX * This could be expanded to call kqueue_scan, if desired. */ /*ARGSUSED*/ static int kqueue_read(struct file *fp, struct uio *uio, struct ucred *active_cred, int flags, struct thread *td) { return (ENXIO); } /*ARGSUSED*/ static int kqueue_write(struct file *fp, struct uio *uio, struct ucred *active_cred, int flags, struct thread *td) { return (ENXIO); } /*ARGSUSED*/ static int kqueue_ioctl(struct file *fp, u_long cmd, void *data, struct ucred *active_cred, struct thread *td) { /* * Enabling sigio causes two major problems: * 1) infinite recursion: * Synopsys: kevent is being used to track signals and have FIOASYNC * set. On receipt of a signal this will cause a kqueue to recurse * into itself over and over. Sending the sigio causes the kqueue * to become ready, which in turn posts sigio again, forever. * Solution: this can be solved by setting a flag in the kqueue that * we have a SIGIO in progress. * 2) locking problems: * Synopsys: Kqueue is a leaf subsystem, but adding signalling puts * us above the proc and pgrp locks. * Solution: Post a signal using an async mechanism, being sure to * record a generation count in the delivery so that we do not deliver * a signal to the wrong process. * * Note, these two mechanisms are somewhat mutually exclusive! */ #if 0 struct kqueue *kq; kq = fp->f_data; switch (cmd) { case FIOASYNC: if (*(int *)data) { kq->kq_state |= KQ_ASYNC; } else { kq->kq_state &= ~KQ_ASYNC; } return (0); case FIOSETOWN: return (fsetown(*(int *)data, &kq->kq_sigio)); case FIOGETOWN: *(int *)data = fgetown(&kq->kq_sigio); return (0); } #endif return (ENOTTY); } /*ARGSUSED*/ static int kqueue_poll(struct file *fp, int events, struct ucred *active_cred, struct thread *td) { struct kqueue *kq; int revents = 0; int error; if ((error = kqueue_aquire(fp, &kq))) return POLLERR; KQ_LOCK(kq); if (events & (POLLIN | POLLRDNORM)) { if (kq->kq_count) { revents |= events & (POLLIN | POLLRDNORM); } else { selrecord(td, &kq->kq_sel); kq->kq_state |= KQ_SEL; } } kqueue_release(kq, 1); KQ_UNLOCK(kq); return (revents); } /*ARGSUSED*/ static int kqueue_stat(struct file *fp, struct stat *st, struct ucred *active_cred, struct thread *td) { return (ENXIO); } /*ARGSUSED*/ static int kqueue_close(struct file *fp, struct thread *td) { struct kqueue *kq = fp->f_data; struct filedesc *fdp; struct knote *kn; int i; int error; if ((error = kqueue_aquire(fp, &kq))) return error; KQ_LOCK(kq); KASSERT((kq->kq_state & KQ_CLOSING) != KQ_CLOSING, ("kqueue already closing")); kq->kq_state |= KQ_CLOSING; if (kq->kq_refcnt > 1) msleep(&kq->kq_refcnt, &kq->kq_lock, PSOCK, "kqclose", 0); KASSERT(kq->kq_refcnt == 1, ("other refs are out there!")); fdp = kq->kq_fdp; KASSERT(knlist_empty(&kq->kq_sel.si_note), ("kqueue's knlist not empty")); for (i = 0; i < kq->kq_knlistsize; i++) { while ((kn = SLIST_FIRST(&kq->kq_knlist[i])) != NULL) { KASSERT((kn->kn_status & KN_INFLUX) == 0, ("KN_INFLUX set when not suppose to be")); kn->kn_status |= KN_INFLUX; KQ_UNLOCK(kq); if (!(kn->kn_status & KN_DETACHED)) kn->kn_fop->f_detach(kn); knote_drop(kn, td); KQ_LOCK(kq); } } if (kq->kq_knhashmask != 0) { for (i = 0; i <= kq->kq_knhashmask; i++) { while ((kn = SLIST_FIRST(&kq->kq_knhash[i])) != NULL) { KASSERT((kn->kn_status & KN_INFLUX) == 0, ("KN_INFLUX set when not suppose to be")); kn->kn_status |= KN_INFLUX; KQ_UNLOCK(kq); if (!(kn->kn_status & KN_DETACHED)) kn->kn_fop->f_detach(kn); knote_drop(kn, td); KQ_LOCK(kq); } } } if ((kq->kq_state & KQ_TASKSCHED) == KQ_TASKSCHED) { kq->kq_state |= KQ_TASKDRAIN; msleep(&kq->kq_state, &kq->kq_lock, PSOCK, "kqtqdr", 0); } if ((kq->kq_state & KQ_SEL) == KQ_SEL) { kq->kq_state &= ~KQ_SEL; selwakeuppri(&kq->kq_sel, PSOCK); } KQ_UNLOCK(kq); FILEDESC_LOCK_FAST(fdp); SLIST_REMOVE(&fdp->fd_kqlist, kq, kqueue, kq_list); FILEDESC_UNLOCK_FAST(fdp); knlist_destroy(&kq->kq_sel.si_note); mtx_destroy(&kq->kq_lock); kq->kq_fdp = NULL; if (kq->kq_knhash != NULL) free(kq->kq_knhash, M_KQUEUE); if (kq->kq_knlist != NULL) free(kq->kq_knlist, M_KQUEUE); funsetown(&kq->kq_sigio); free(kq, M_KQUEUE); fp->f_data = NULL; return (0); } static void kqueue_wakeup(struct kqueue *kq) { KQ_OWNED(kq); if ((kq->kq_state & KQ_SLEEP) == KQ_SLEEP) { kq->kq_state &= ~KQ_SLEEP; wakeup(kq); } if ((kq->kq_state & KQ_SEL) == KQ_SEL) { kq->kq_state &= ~KQ_SEL; selwakeuppri(&kq->kq_sel, PSOCK); } if (!knlist_empty(&kq->kq_sel.si_note)) kqueue_schedtask(kq); if ((kq->kq_state & KQ_ASYNC) == KQ_ASYNC) { pgsigio(&kq->kq_sigio, SIGIO, 0); } } /* * Walk down a list of knotes, activating them if their event has triggered. * * There is a possibility to optimize in the case of one kq watching another. * Instead of scheduling a task to wake it up, you could pass enough state * down the chain to make up the parent kqueue. Make this code functional * first. */ void knote(struct knlist *list, long hint, int islocked) { struct kqueue *kq; struct knote *kn; if (list == NULL) return; mtx_assert(list->kl_lock, islocked ? MA_OWNED : MA_NOTOWNED); if (!islocked) mtx_lock(list->kl_lock); /* * If we unlock the list lock (and set KN_INFLUX), we can eliminate * the kqueue scheduling, but this will introduce four * lock/unlock's for each knote to test. If we do, continue to use * SLIST_FOREACH, SLIST_FOREACH_SAFE is not safe in our case, it is * only safe if you want to remove the current item, which we are * not doing. */ SLIST_FOREACH(kn, &list->kl_list, kn_selnext) { kq = kn->kn_kq; if ((kn->kn_status & KN_INFLUX) != KN_INFLUX) { KQ_LOCK(kq); if ((kn->kn_status & KN_INFLUX) != KN_INFLUX) { kn->kn_status |= KN_HASKQLOCK; if (kn->kn_fop->f_event(kn, hint)) KNOTE_ACTIVATE(kn, 1); kn->kn_status &= ~KN_HASKQLOCK; } KQ_UNLOCK(kq); } kq = NULL; } if (!islocked) mtx_unlock(list->kl_lock); } /* * add a knote to a knlist */ void knlist_add(struct knlist *knl, struct knote *kn, int islocked) { mtx_assert(knl->kl_lock, islocked ? MA_OWNED : MA_NOTOWNED); KQ_NOTOWNED(kn->kn_kq); KASSERT((kn->kn_status & (KN_INFLUX|KN_DETACHED)) == (KN_INFLUX|KN_DETACHED), ("knote not KN_INFLUX and KN_DETACHED")); if (!islocked) mtx_lock(knl->kl_lock); SLIST_INSERT_HEAD(&knl->kl_list, kn, kn_selnext); if (!islocked) mtx_unlock(knl->kl_lock); KQ_LOCK(kn->kn_kq); kn->kn_knlist = knl; kn->kn_status &= ~KN_DETACHED; KQ_UNLOCK(kn->kn_kq); } static void knlist_remove_kq(struct knlist *knl, struct knote *kn, int knlislocked, int kqislocked) { KASSERT(!(!!kqislocked && !knlislocked), ("kq locked w/o knl locked")); mtx_assert(knl->kl_lock, knlislocked ? MA_OWNED : MA_NOTOWNED); mtx_assert(&kn->kn_kq->kq_lock, kqislocked ? MA_OWNED : MA_NOTOWNED); if (!kqislocked) KASSERT((kn->kn_status & (KN_INFLUX|KN_DETACHED)) == KN_INFLUX, ("knlist_remove called w/o knote being KN_INFLUX or already removed")); if (!knlislocked) mtx_lock(knl->kl_lock); SLIST_REMOVE(&knl->kl_list, kn, knote, kn_selnext); kn->kn_knlist = NULL; if (!knlislocked) mtx_unlock(knl->kl_lock); if (!kqislocked) KQ_LOCK(kn->kn_kq); kn->kn_status |= KN_DETACHED; if (!kqislocked) KQ_UNLOCK(kn->kn_kq); } /* * remove all knotes from a specified klist */ void knlist_remove(struct knlist *knl, struct knote *kn, int islocked) { knlist_remove_kq(knl, kn, islocked, 0); } /* * remove knote from a specified klist while in f_event handler. */ void knlist_remove_inevent(struct knlist *knl, struct knote *kn) { knlist_remove_kq(knl, kn, 1, (kn->kn_status & KN_HASKQLOCK) == KN_HASKQLOCK); } int knlist_empty(struct knlist *knl) { mtx_assert(knl->kl_lock, MA_OWNED); return SLIST_EMPTY(&knl->kl_list); } static struct mtx knlist_lock; MTX_SYSINIT(knlist_lock, &knlist_lock, "knlist lock for lockless objects", MTX_DEF); void knlist_init(struct knlist *knl, struct mtx *mtx) { if (mtx == NULL) knl->kl_lock = &knlist_lock; else knl->kl_lock = mtx; SLIST_INIT(&knl->kl_list); } void knlist_destroy(struct knlist *knl) { #ifdef INVARIANTS /* * if we run across this error, we need to find the offending * driver and have it call knlist_clear. */ if (!SLIST_EMPTY(&knl->kl_list)) printf("WARNING: destroying knlist w/ knotes on it!\n"); #endif knl->kl_lock = NULL; SLIST_INIT(&knl->kl_list); } /* * Even if we are locked, we may need to drop the lock to allow any influx * knotes time to "settle". */ void knlist_cleardel(struct knlist *knl, struct thread *td, int islocked, int killkn) { struct knote *kn; struct kqueue *kq; if (islocked) mtx_assert(knl->kl_lock, MA_OWNED); else { mtx_assert(knl->kl_lock, MA_NOTOWNED); again: /* need to reaquire lock since we have dropped it */ mtx_lock(knl->kl_lock); } SLIST_FOREACH(kn, &knl->kl_list, kn_selnext) { kq = kn->kn_kq; KQ_LOCK(kq); if ((kn->kn_status & KN_INFLUX)) { KQ_UNLOCK(kq); continue; } knlist_remove_kq(knl, kn, 1, 1); if (killkn) { kn->kn_status |= KN_INFLUX | KN_DETACHED; KQ_UNLOCK(kq); knote_drop(kn, td); } else { /* Make sure cleared knotes disappear soon */ kn->kn_flags |= (EV_EOF | EV_ONESHOT); KQ_UNLOCK(kq); } kq = NULL; } if (!SLIST_EMPTY(&knl->kl_list)) { /* there are still KN_INFLUX remaining */ kn = SLIST_FIRST(&knl->kl_list); kq = kn->kn_kq; KQ_LOCK(kq); KASSERT(kn->kn_status & KN_INFLUX, ("knote removed w/o list lock")); mtx_unlock(knl->kl_lock); kq->kq_state |= KQ_FLUXWAIT; msleep(kq, &kq->kq_lock, PSOCK | PDROP, "kqkclr", 0); kq = NULL; goto again; } if (islocked) mtx_assert(knl->kl_lock, MA_OWNED); else { mtx_unlock(knl->kl_lock); mtx_assert(knl->kl_lock, MA_NOTOWNED); } } /* * remove all knotes referencing a specified fd * must be called with FILEDESC lock. This prevents a race where a new fd * comes along and occupies the entry and we attach a knote to the fd. */ void knote_fdclose(struct thread *td, int fd) { struct filedesc *fdp = td->td_proc->p_fd; struct kqueue *kq; struct knote *kn; int influx; FILEDESC_LOCK_ASSERT(fdp, MA_OWNED); /* * We shouldn't have to worry about new kevents appearing on fd * since filedesc is locked. */ SLIST_FOREACH(kq, &fdp->fd_kqlist, kq_list) { KQ_LOCK(kq); again: influx = 0; while (kq->kq_knlistsize > fd && (kn = SLIST_FIRST(&kq->kq_knlist[fd])) != NULL) { if (kn->kn_status & KN_INFLUX) { /* someone else might be waiting on our knote */ if (influx) wakeup(kq); kq->kq_state |= KQ_FLUXWAIT; msleep(kq, &kq->kq_lock, PSOCK, "kqflxwt", 0); goto again; } kn->kn_status |= KN_INFLUX; KQ_UNLOCK(kq); if (!(kn->kn_status & KN_DETACHED)) kn->kn_fop->f_detach(kn); knote_drop(kn, td); influx = 1; KQ_LOCK(kq); } KQ_UNLOCK_FLUX(kq); } } static int knote_attach(struct knote *kn, struct kqueue *kq) { struct klist *list; KASSERT(kn->kn_status & KN_INFLUX, ("knote not marked INFLUX")); KQ_OWNED(kq); if (kn->kn_fop->f_isfd) { if (kn->kn_id >= kq->kq_knlistsize) return ENOMEM; list = &kq->kq_knlist[kn->kn_id]; } else { if (kq->kq_knhash == NULL) return ENOMEM; list = &kq->kq_knhash[KN_HASH(kn->kn_id, kq->kq_knhashmask)]; } SLIST_INSERT_HEAD(list, kn, kn_link); return 0; } /* * knote must already have been detatched using the f_detach method. * no lock need to be held, it is assumed that the KN_INFLUX flag is set * to prevent other removal. */ static void knote_drop(struct knote *kn, struct thread *td) { struct kqueue *kq; struct klist *list; kq = kn->kn_kq; KQ_NOTOWNED(kq); KASSERT((kn->kn_status & KN_INFLUX) == KN_INFLUX, ("knote_drop called without KN_INFLUX set in kn_status")); KQ_LOCK(kq); if (kn->kn_fop->f_isfd) list = &kq->kq_knlist[kn->kn_id]; else list = &kq->kq_knhash[KN_HASH(kn->kn_id, kq->kq_knhashmask)]; SLIST_REMOVE(list, kn, knote, kn_link); if (kn->kn_status & KN_QUEUED) knote_dequeue(kn); KQ_UNLOCK_FLUX(kq); if (kn->kn_fop->f_isfd) { fdrop(kn->kn_fp, td); kn->kn_fp = NULL; } kqueue_fo_release(kn->kn_kevent.filter); kn->kn_fop = NULL; knote_free(kn); } static void knote_enqueue(struct knote *kn) { struct kqueue *kq = kn->kn_kq; KQ_OWNED(kn->kn_kq); KASSERT((kn->kn_status & KN_QUEUED) == 0, ("knote already queued")); TAILQ_INSERT_TAIL(&kq->kq_head, kn, kn_tqe); kn->kn_status |= KN_QUEUED; kq->kq_count++; kqueue_wakeup(kq); } static void knote_dequeue(struct knote *kn) { struct kqueue *kq = kn->kn_kq; KQ_OWNED(kn->kn_kq); KASSERT(kn->kn_status & KN_QUEUED, ("knote not queued")); TAILQ_REMOVE(&kq->kq_head, kn, kn_tqe); kn->kn_status &= ~KN_QUEUED; kq->kq_count--; } static void knote_init(void) { knote_zone = uma_zcreate("KNOTE", sizeof(struct knote), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); } SYSINIT(knote, SI_SUB_PSEUDO, SI_ORDER_ANY, knote_init, NULL) static struct knote * knote_alloc(int waitok) { return ((struct knote *)uma_zalloc(knote_zone, (waitok ? M_WAITOK : M_NOWAIT)|M_ZERO)); } static void knote_free(struct knote *kn) { if (kn != NULL) uma_zfree(knote_zone, kn); } Index: stable/5/sys/sys/event.h =================================================================== --- stable/5/sys/sys/event.h (revision 145377) +++ stable/5/sys/sys/event.h (revision 145378) @@ -1,233 +1,233 @@ /*- * Copyright (c) 1999,2000,2001 Jonathan Lemon * 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$ */ #ifndef _SYS_EVENT_H_ #define _SYS_EVENT_H_ #define EVFILT_READ (-1) #define EVFILT_WRITE (-2) #define EVFILT_AIO (-3) /* attached to aio requests */ #define EVFILT_VNODE (-4) /* attached to vnodes */ #define EVFILT_PROC (-5) /* attached to struct proc */ #define EVFILT_SIGNAL (-6) /* attached to struct proc */ #define EVFILT_TIMER (-7) /* timers */ #define EVFILT_NETDEV (-8) /* network devices */ #define EVFILT_FS (-9) /* filesystem events */ #define EVFILT_SYSCOUNT 9 #define EV_SET(kevp_, a, b, c, d, e, f) do { \ struct kevent *kevp = (kevp_); \ (kevp)->ident = (a); \ (kevp)->filter = (b); \ (kevp)->flags = (c); \ (kevp)->fflags = (d); \ (kevp)->data = (e); \ (kevp)->udata = (f); \ } while(0) struct kevent { uintptr_t ident; /* identifier for this event */ short filter; /* filter for event */ u_short flags; u_int fflags; intptr_t data; void *udata; /* opaque user data identifier */ }; /* actions */ #define EV_ADD 0x0001 /* add event to kq (implies enable) */ #define EV_DELETE 0x0002 /* delete event from kq */ #define EV_ENABLE 0x0004 /* enable event */ #define EV_DISABLE 0x0008 /* disable event (not reported) */ /* flags */ #define EV_ONESHOT 0x0010 /* only report one occurrence */ #define EV_CLEAR 0x0020 /* clear event state after reporting */ #define EV_SYSFLAGS 0xF000 /* reserved by system */ #define EV_FLAG1 0x2000 /* filter-specific flag */ /* returned values */ #define EV_EOF 0x8000 /* EOF detected */ #define EV_ERROR 0x4000 /* error, data contains errno */ /* * data/hint flags for EVFILT_{READ|WRITE}, shared with userspace */ #define NOTE_LOWAT 0x0001 /* low water mark */ /* * data/hint flags for EVFILT_VNODE, shared with userspace */ #define NOTE_DELETE 0x0001 /* vnode was removed */ #define NOTE_WRITE 0x0002 /* data contents changed */ #define NOTE_EXTEND 0x0004 /* size increased */ #define NOTE_ATTRIB 0x0008 /* attributes changed */ #define NOTE_LINK 0x0010 /* link count changed */ #define NOTE_RENAME 0x0020 /* vnode was renamed */ #define NOTE_REVOKE 0x0040 /* vnode access was revoked */ /* * data/hint flags for EVFILT_PROC, shared with userspace */ #define NOTE_EXIT 0x80000000 /* process exited */ #define NOTE_FORK 0x40000000 /* process forked */ #define NOTE_EXEC 0x20000000 /* process exec'd */ #define NOTE_PCTRLMASK 0xf0000000 /* mask for hint bits */ #define NOTE_PDATAMASK 0x000fffff /* mask for pid */ /* additional flags for EVFILT_PROC */ #define NOTE_TRACK 0x00000001 /* follow across forks */ #define NOTE_TRACKERR 0x00000002 /* could not track child */ #define NOTE_CHILD 0x00000004 /* am a child process */ /* * data/hint flags for EVFILT_NETDEV, shared with userspace */ #define NOTE_LINKUP 0x0001 /* link is up */ #define NOTE_LINKDOWN 0x0002 /* link is down */ #define NOTE_LINKINV 0x0004 /* link state is invalid */ /* * This is currently visible to userland to work around broken * programs which pull in . */ #include #include #include struct knote; SLIST_HEAD(klist, knote); struct kqueue; SLIST_HEAD(kqlist, kqueue); struct knlist { struct mtx *kl_lock; /* lock to protect kll_list */ struct klist kl_list; }; #ifdef _KERNEL #ifdef MALLOC_DECLARE MALLOC_DECLARE(M_KQUEUE); #endif #define KNOTE(list, hist, lock) knote(list, hist, lock) #define KNOTE_LOCKED(list, hint) knote(list, hint, 1) #define KNOTE_UNLOCKED(list, hint) knote(list, hint, 0) /* * Flag indicating hint is a signal. Used by EVFILT_SIGNAL, and also * shared by EVFILT_PROC (all knotes attached to p->p_klist) */ #define NOTE_SIGNAL 0x08000000 struct filterops { int f_isfd; /* true if ident == filedescriptor */ int (*f_attach)(struct knote *kn); void (*f_detach)(struct knote *kn); int (*f_event)(struct knote *kn, long hint); }; /* * Setting the KN_INFLUX flag enables you to unlock the kq that this knote * is on, and modify kn_status as if you had the KQ lock. * * kn_sfflags, kn_sdata, and kn_kevent are protected by the knlist lock. */ struct knote { SLIST_ENTRY(knote) kn_link; /* for kq */ SLIST_ENTRY(knote) kn_selnext; /* for struct selinfo */ struct knlist *kn_knlist; /* f_attach populated */ TAILQ_ENTRY(knote) kn_tqe; struct kqueue *kn_kq; /* which queue we are on */ struct kevent kn_kevent; int kn_status; /* protected by kq lock */ #define KN_ACTIVE 0x01 /* event has been triggered */ #define KN_QUEUED 0x02 /* event is on queue */ #define KN_DISABLED 0x04 /* event is disabled */ #define KN_DETACHED 0x08 /* knote is detached */ #define KN_INFLUX 0x10 /* knote is in flux */ #define KN_MARKER 0x20 /* ignore this knote */ #define KN_KQUEUE 0x40 /* this knote belongs to a kq */ #define KN_HASKQLOCK 0x80 /* for _inevent */ int kn_sfflags; /* saved filter flags */ intptr_t kn_sdata; /* saved data field */ union { struct file *p_fp; /* file data pointer */ struct proc *p_proc; /* proc pointer */ } kn_ptr; struct filterops *kn_fop; void *kn_hook; #define kn_id kn_kevent.ident #define kn_filter kn_kevent.filter #define kn_flags kn_kevent.flags #define kn_fflags kn_kevent.fflags #define kn_data kn_kevent.data #define kn_fp kn_ptr.p_fp }; struct thread; struct proc; struct knlist; extern void knote(struct knlist *list, long hint, int islocked); extern void knlist_add(struct knlist *knl, struct knote *kn, int islocked); extern void knlist_remove(struct knlist *knl, struct knote *kn, int islocked); extern void knlist_remove_inevent(struct knlist *knl, struct knote *kn); extern int knlist_empty(struct knlist *knl); extern void knlist_init(struct knlist *knl, struct mtx *mtx); extern void knlist_destroy(struct knlist *knl); extern void knlist_cleardel(struct knlist *knl, struct thread *td, int islocked, int killkn); #define knlist_clear(knl, islocked) \ knlist_cleardel((knl), NULL, (islocked), 0) #define knlist_delete(knl, td, islocked) \ knlist_cleardel((knl), (td), (islocked), 1) extern void knote_fdclose(struct thread *p, int fd); extern int kqueue_register(struct kqueue *kq, struct kevent *kev, struct thread *p, int waitok); extern int kqueue_add_filteropts(int filt, struct filterops *filtops); extern int kqueue_del_filteropts(int filt); #else /* !_KERNEL */ #include struct timespec; __BEGIN_DECLS int kqueue(void); -int kevent(int kq, const struct kevent *changelist, int nchanges, +int kevent(int kq, struct kevent *changelist, int nchanges, struct kevent *eventlist, int nevents, const struct timespec *timeout); __END_DECLS #endif /* !_KERNEL */ #endif /* !_SYS_EVENT_H_ */