Index: head/lib/libthr/thread/thr_timer.c =================================================================== --- head/lib/libthr/thread/thr_timer.c (revision 152028) +++ head/lib/libthr/thread/thr_timer.c (revision 152029) @@ -1,373 +1,373 @@ /* * Copyright (c) 2005 David Xu * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ * */ #include #include #include #include #include #include "thr_private.h" struct thread_node { struct pthread_attr attr; TAILQ_ENTRY(thread_node) link; pthread_t thread; int refcount; int exit; jmp_buf jbuf; struct timer *curtmr; }; struct timer { union sigval value; - void (*function)(union sigval *, int); + void (*function)(union sigval, int); int timerid; long flags; int gen; struct thread_node *tn; }; static struct timer **timer_list; static int timer_gen; static int timer_max; static umtx_t timer_list_lock; static TAILQ_HEAD(,thread_node) timer_threads; static umtx_t timer_threads_lock; static void *service_loop(void *); static int register_timer(struct timer *); static struct thread_node *create_timer_thread(pthread_attr_t); static void release_timer_thread(struct thread_node *); extern int __sys_timer_create(clockid_t, struct sigevent *, timer_t *); extern int __sys_timer_delete(timer_t); __weak_reference(__timer_create, timer_create); __weak_reference(__timer_create, _timer_create); __weak_reference(__timer_delete, timer_delete); __weak_reference(__timer_delete, _timer_delete); #define SIGTIMER SIGCANCEL /* Reuse SIGCANCEL */ #define WORKING 0x01 #define WANTED 0x02 #define TIMERS_LOCK(t) THR_UMTX_LOCK((t), &timer_list_lock) #define TIMERS_UNLOCK(t) THR_UMTX_UNLOCK((t), &timer_list_lock) #define THREADS_LOCK(t) THR_UMTX_LOCK((t), &timer_threads_lock) #define THREADS_UNLOCK(t) THR_UMTX_UNLOCK((t), &timer_threads_lock) void _thr_timer_init(void) { _thr_umtx_init(&timer_list_lock); _thr_umtx_init(&timer_threads_lock); TAILQ_INIT(&timer_threads); timer_list = NULL; timer_max = 0; } /* * Purpose of the function is to implement POSIX timer's * SEGEV_THREAD notification mechanism. */ int __timer_create(clockid_t clockid, struct sigevent *evp, timer_t *timerid) { pthread_attr_t attr; struct sigevent ev; struct timer *tmr; int ret; /* Call syscall directly if it is not SIGEV_THREAD */ if (evp == NULL || evp->sigev_notify != SIGEV_THREAD) return (__sys_timer_create(clockid, evp, timerid)); /* Otherwise, do all magical things. */ tmr = malloc(sizeof(*tmr)); if (__predict_false(tmr == NULL)) { errno = EAGAIN; return (-1); } tmr->value = evp->sigev_value; /* XXX * Here we pass second parameter an overrun count, this is * not required by POSIX. */ - tmr->function = (void (*)(union sigval *, int)) + tmr->function = (void (*)(union sigval, int)) evp->sigev_notify_function; tmr->flags = 0; tmr->timerid = -1; pthread_attr_init(&attr); if (evp->sigev_notify_attributes != NULL) { *attr = **(pthread_attr_t *)(evp->sigev_notify_attributes); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); } tmr->gen = atomic_fetchadd_int(&timer_gen, 1); tmr->tn = create_timer_thread(attr); if (__predict_false(tmr->tn == NULL)) { free(tmr); errno = EAGAIN; return (-1); } /* * Build a new sigevent, and tell kernel to deliver * SIGTIMER signal to the new thread. */ ev.sigev_notify = SIGEV_THREAD_ID; ev.sigev_signo = SIGTIMER; ev.sigev_notify_thread_id = (lwpid_t)tmr->tn->thread->tid; - ev.sigev_value.sigval_int = tmr->gen; + ev.sigev_value.sival_int = tmr->gen; ret = __sys_timer_create(clockid, &ev, &tmr->timerid); if (__predict_false(ret != 0 || register_timer(tmr) != 0)) { ret = errno; release_timer_thread(tmr->tn); free(tmr); errno = ret; return (-1); } *timerid = tmr->timerid; return (0); } int __timer_delete(timer_t timerid) { struct pthread *curthread = _get_curthread(); struct timer *tmr = NULL; long flags; TIMERS_LOCK(curthread); /* * Check if this is a SIGEV_THREAD timer by looking up * it in the registered list. */ if (timerid >= 0 && timerid < timer_max && (tmr = timer_list[timerid]) != NULL) { /* Take it from timer list */ timer_list[timerid] = NULL; /* If the timer is servicing, allow it to complete. */ while ((flags = tmr->flags) & WORKING) { tmr->flags |= WANTED; TIMERS_UNLOCK(curthread); _thr_umtx_wait(&tmr->flags, flags, NULL); TIMERS_LOCK(curthread); } TIMERS_UNLOCK(curthread); /* * Drop reference count of servicing thread, * may free the thread. */ release_timer_thread(tmr->tn); } else TIMERS_UNLOCK(curthread); if (tmr != NULL) free(tmr); return (__sys_timer_delete(timerid)); } static struct thread_node * create_timer_thread(pthread_attr_t attr) { struct pthread *curthread = _get_curthread(); struct thread_node *tn; int ret; THREADS_LOCK(curthread); /* Search a thread matching the required pthread_attr. */ TAILQ_FOREACH(tn, &timer_threads, link) { if (attr->stackaddr_attr == NULL) { if (attr->sched_policy == tn->attr.sched_policy && attr->sched_inherit == tn->attr.sched_inherit && attr->prio == tn->attr.prio && attr->stacksize_attr == tn->attr.stacksize_attr && attr->guardsize_attr == tn->attr.guardsize_attr && ((attr->flags & PTHREAD_SCOPE_SYSTEM) == (tn->attr.flags & PTHREAD_SCOPE_SYSTEM))) break; } else { /* * Reuse the thread if it has same stack address, * because two threads can not run on same stack. */ if (attr->stackaddr_attr == tn->attr.stackaddr_attr) break; } } if (tn != NULL) { tn->refcount++; THREADS_UNLOCK(curthread); return (tn); } tn = malloc(sizeof(*tn)); tn->refcount = 1; tn->exit = 0; tn->attr = *attr; tn->curtmr = NULL; _thr_signal_block(curthread); /* SIGTIMER is also blocked. */ TAILQ_INSERT_TAIL(&timer_threads, tn, link); ret = _pthread_create(&tn->thread, &attr, service_loop, tn); _thr_signal_unblock(curthread); if (ret != 0) { TAILQ_REMOVE(&timer_threads, tn, link); free(tn); tn = NULL; } THREADS_UNLOCK(curthread); return (tn); } static void release_timer_thread(struct thread_node *tn) { struct pthread *curthread = _get_curthread(); struct pthread *th; THREADS_LOCK(curthread); if (--tn->refcount == 0) { /* * If I am the last user, current implement kills the * service thread, is this allowed by POSIX ? does * this hurt performance ? */ tn->exit = 1; th = tn->thread; _thr_send_sig(th, SIGTIMER); pthread_join(th, NULL); TAILQ_REMOVE(&timer_threads, tn, link); } else tn = NULL; THREADS_UNLOCK(curthread); if (tn != NULL) free(tn); } /* Register a SIGEV_THREAD timer. */ static int register_timer(struct timer *tmr) { struct pthread *curthread = _get_curthread(); struct timer **list; int count; while ((count = timer_max) <= tmr->timerid) { if (count < 32) count = 32; while (count <= tmr->timerid) count <<= 1; list = malloc(count * sizeof(void *)); memset(list, 0, count * sizeof(void *)); if (list == NULL) return (-1); TIMERS_LOCK(curthread); if (timer_max >= count) { TIMERS_UNLOCK(curthread); free(list); continue; } memcpy(timer_list, list, timer_max * sizeof(void *)); timer_list = list; timer_max = count; TIMERS_UNLOCK(curthread); } TIMERS_LOCK(curthread); timer_list[tmr->timerid] = tmr; TIMERS_UNLOCK(curthread); return (0); } /* * This function is called if user callback calls * pthread_exit() or pthread_cancel() for the thread. */ static void cleanup_thread(void *arg) { struct pthread *curthread = _get_curthread(); struct thread_node *tn = arg; if (tn->exit == 0) { /* broken usercode is killing us. */ if (tn->curtmr) { TIMERS_LOCK(curthread); tn->curtmr->flags &= ~WORKING; if (tn->curtmr->flags & WANTED) _thr_umtx_wake(&tn->curtmr->flags, INT_MAX); TIMERS_UNLOCK(curthread); } atomic_clear_int(&curthread->cancelflags, THR_CANCEL_EXITING); longjmp(tn->jbuf, 1); } } static void * service_loop(void *arg) { struct pthread *curthread = _get_curthread(); struct thread_node *tn = arg; struct timer *tmr; siginfo_t si; sigset_t set; /* * Service thread should not be killed by callback, if user * tries to do so, the thread will be restarted. */ setjmp(tn->jbuf); pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); sigemptyset(&set); sigaddset(&set, SIGTIMER); THR_CLEANUP_PUSH(curthread, cleanup_thread, tn); while (tn->exit == 0) { if (__predict_false(__sys_sigwaitinfo(&set, &si) == -1 || si.si_code != SI_TIMER)) continue; TIMERS_LOCK(curthread); if (si.si_timerid >= 0 && si.si_timerid < timer_max && (tmr = timer_list[si.si_timerid]) != NULL && - si.si_value.sigval_int == tmr->gen) { + si.si_value.sival_int == tmr->gen) { tmr->flags |= WORKING; TIMERS_UNLOCK(curthread); tn->curtmr = tmr; - tmr->function(&tmr->value, si.si_overrun); + tmr->function(tmr->value, si.si_overrun); tn->curtmr = NULL; TIMERS_LOCK(curthread); tmr->flags &= ~WORKING; if (tmr->flags & WANTED) _thr_umtx_wake(&tmr->flags, INT_MAX); } TIMERS_UNLOCK(curthread); } THR_CLEANUP_POP(curthread, 0); return (0); } Index: head/sys/kern/kern_sig.c =================================================================== --- head/sys/kern/kern_sig.c (revision 152028) +++ head/sys/kern/kern_sig.c (revision 152029) @@ -1,3178 +1,3178 @@ /*- * Copyright (c) 1982, 1986, 1989, 1991, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * 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. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. * * @(#)kern_sig.c 8.7 (Berkeley) 4/18/94 */ #include __FBSDID("$FreeBSD$"); #include "opt_compat.h" #include "opt_ktrace.h" #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 #include #include #include #include #include #include #if defined (__alpha__) && !defined(COMPAT_43) #error "You *really* need COMPAT_43 on the alpha for longjmp(3)" #endif #define ONSIG 32 /* NSIG for osig* syscalls. XXX. */ static int coredump(struct thread *); static char *expand_name(const char *, uid_t, pid_t); static int killpg1(struct thread *td, int sig, int pgid, int all); static int issignal(struct thread *p); static int sigprop(int sig); static void tdsigwakeup(struct thread *td, int sig, sig_t action); static int filt_sigattach(struct knote *kn); static void filt_sigdetach(struct knote *kn); static int filt_signal(struct knote *kn, long hint); static struct thread *sigtd(struct proc *p, int sig, int prop); static int kern_sigtimedwait(struct thread *, sigset_t, ksiginfo_t *, struct timespec *); static int do_tdsignal(struct proc *, struct thread *, int, ksiginfo_t *); static void sigqueue_start(void); static uma_zone_t ksiginfo_zone = NULL; struct filterops sig_filtops = { 0, filt_sigattach, filt_sigdetach, filt_signal }; static int kern_logsigexit = 1; SYSCTL_INT(_kern, KERN_LOGSIGEXIT, logsigexit, CTLFLAG_RW, &kern_logsigexit, 0, "Log processes quitting on abnormal signals to syslog(3)"); SYSCTL_NODE(_kern, OID_AUTO, sigqueue, CTLFLAG_RW, 0, "POSIX real time signal"); static int max_pending_per_proc = 128; SYSCTL_INT(_kern_sigqueue, OID_AUTO, max_pending_per_proc, CTLFLAG_RW, &max_pending_per_proc, 0, "Max pending signals per proc"); static int queue_rt_signal_only = 1; SYSCTL_INT(_kern_sigqueue, OID_AUTO, queue_rt_signal_only, CTLFLAG_RW, &queue_rt_signal_only, 0, "Only rt signal is queued"); static int preallocate_siginfo = 1024; TUNABLE_INT("kern.sigqueue.preallocate", &preallocate_siginfo); SYSCTL_INT(_kern_sigqueue, OID_AUTO, preallocate, CTLFLAG_RD, &preallocate_siginfo, 0, "Preallocated signal memory size"); static int signal_overflow = 0; SYSCTL_INT(_kern_sigqueue, OID_AUTO, signal_overflow, CTLFLAG_RD, &signal_overflow, 0, "Number of signals overflew"); static int signal_alloc_fail = 0; SYSCTL_INT(_kern_sigqueue, OID_AUTO, signal_alloc_fail, CTLFLAG_RD, &signal_alloc_fail, 0, "signals failed to be allocated"); SYSINIT(signal, SI_SUB_P1003_1B, SI_ORDER_FIRST+3, sigqueue_start, NULL); /* * Policy -- Can ucred cr1 send SIGIO to process cr2? * Should use cr_cansignal() once cr_cansignal() allows SIGIO and SIGURG * in the right situations. */ #define CANSIGIO(cr1, cr2) \ ((cr1)->cr_uid == 0 || \ (cr1)->cr_ruid == (cr2)->cr_ruid || \ (cr1)->cr_uid == (cr2)->cr_ruid || \ (cr1)->cr_ruid == (cr2)->cr_uid || \ (cr1)->cr_uid == (cr2)->cr_uid) int sugid_coredump; SYSCTL_INT(_kern, OID_AUTO, sugid_coredump, CTLFLAG_RW, &sugid_coredump, 0, "Enable coredumping set user/group ID processes"); static int do_coredump = 1; SYSCTL_INT(_kern, OID_AUTO, coredump, CTLFLAG_RW, &do_coredump, 0, "Enable/Disable coredumps"); static int set_core_nodump_flag = 0; SYSCTL_INT(_kern, OID_AUTO, nodump_coredump, CTLFLAG_RW, &set_core_nodump_flag, 0, "Enable setting the NODUMP flag on coredump files"); /* * Signal properties and actions. * The array below categorizes the signals and their default actions * according to the following properties: */ #define SA_KILL 0x01 /* terminates process by default */ #define SA_CORE 0x02 /* ditto and coredumps */ #define SA_STOP 0x04 /* suspend process */ #define SA_TTYSTOP 0x08 /* ditto, from tty */ #define SA_IGNORE 0x10 /* ignore by default */ #define SA_CONT 0x20 /* continue if suspended */ #define SA_CANTMASK 0x40 /* non-maskable, catchable */ #define SA_PROC 0x80 /* deliverable to any thread */ static int sigproptbl[NSIG] = { SA_KILL|SA_PROC, /* SIGHUP */ SA_KILL|SA_PROC, /* SIGINT */ SA_KILL|SA_CORE|SA_PROC, /* SIGQUIT */ SA_KILL|SA_CORE, /* SIGILL */ SA_KILL|SA_CORE, /* SIGTRAP */ SA_KILL|SA_CORE, /* SIGABRT */ SA_KILL|SA_CORE|SA_PROC, /* SIGEMT */ SA_KILL|SA_CORE, /* SIGFPE */ SA_KILL|SA_PROC, /* SIGKILL */ SA_KILL|SA_CORE, /* SIGBUS */ SA_KILL|SA_CORE, /* SIGSEGV */ SA_KILL|SA_CORE, /* SIGSYS */ SA_KILL|SA_PROC, /* SIGPIPE */ SA_KILL|SA_PROC, /* SIGALRM */ SA_KILL|SA_PROC, /* SIGTERM */ SA_IGNORE|SA_PROC, /* SIGURG */ SA_STOP|SA_PROC, /* SIGSTOP */ SA_STOP|SA_TTYSTOP|SA_PROC, /* SIGTSTP */ SA_IGNORE|SA_CONT|SA_PROC, /* SIGCONT */ SA_IGNORE|SA_PROC, /* SIGCHLD */ SA_STOP|SA_TTYSTOP|SA_PROC, /* SIGTTIN */ SA_STOP|SA_TTYSTOP|SA_PROC, /* SIGTTOU */ SA_IGNORE|SA_PROC, /* SIGIO */ SA_KILL, /* SIGXCPU */ SA_KILL, /* SIGXFSZ */ SA_KILL|SA_PROC, /* SIGVTALRM */ SA_KILL|SA_PROC, /* SIGPROF */ SA_IGNORE|SA_PROC, /* SIGWINCH */ SA_IGNORE|SA_PROC, /* SIGINFO */ SA_KILL|SA_PROC, /* SIGUSR1 */ SA_KILL|SA_PROC, /* SIGUSR2 */ }; static void sigqueue_start(void) { ksiginfo_zone = uma_zcreate("ksiginfo", sizeof(ksiginfo_t), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); uma_prealloc(ksiginfo_zone, preallocate_siginfo); } ksiginfo_t * ksiginfo_alloc(void) { if (ksiginfo_zone != NULL) return ((ksiginfo_t *)uma_zalloc(ksiginfo_zone, M_NOWAIT | M_ZERO)); return (NULL); } void ksiginfo_free(ksiginfo_t *ksi) { uma_zfree(ksiginfo_zone, ksi); } static __inline int ksiginfo_tryfree(ksiginfo_t *ksi) { if (!(ksi->ksi_flags & KSI_EXT)) { uma_zfree(ksiginfo_zone, ksi); return (1); } return (0); } void sigqueue_init(sigqueue_t *list, struct proc *p) { SIGEMPTYSET(list->sq_signals); TAILQ_INIT(&list->sq_list); list->sq_proc = p; list->sq_flags = SQ_INIT; } /* * Get a signal's ksiginfo. * Return: * 0 - signal not found * others - signal number */ int sigqueue_get(sigqueue_t *sq, int signo, ksiginfo_t *si) { struct proc *p = sq->sq_proc; struct ksiginfo *ksi, *next; int count = 0; KASSERT(sq->sq_flags & SQ_INIT, ("sigqueue not inited")); if (!SIGISMEMBER(sq->sq_signals, signo)) return (0); for (ksi = TAILQ_FIRST(&sq->sq_list); ksi != NULL; ksi = next) { next = TAILQ_NEXT(ksi, ksi_link); if (ksi->ksi_signo == signo) { if (count == 0) { TAILQ_REMOVE(&sq->sq_list, ksi, ksi_link); ksi->ksi_sigq = NULL; ksiginfo_copy(ksi, si); if (ksiginfo_tryfree(ksi) && p != NULL) p->p_pendingcnt--; } count++; } } if (count <= 1) SIGDELSET(sq->sq_signals, signo); si->ksi_signo = signo; return (signo); } void sigqueue_take(ksiginfo_t *ksi) { struct ksiginfo *kp; struct proc *p; sigqueue_t *sq; if ((sq = ksi->ksi_sigq) == NULL) return; p = sq->sq_proc; TAILQ_REMOVE(&sq->sq_list, ksi, ksi_link); ksi->ksi_sigq = NULL; if (!(ksi->ksi_flags & KSI_EXT) && p != NULL) p->p_pendingcnt--; for (kp = TAILQ_FIRST(&sq->sq_list); kp != NULL; kp = TAILQ_NEXT(kp, ksi_link)) { if (kp->ksi_signo == ksi->ksi_signo) break; } if (kp == NULL) SIGDELSET(sq->sq_signals, ksi->ksi_signo); } int sigqueue_add(sigqueue_t *sq, int signo, ksiginfo_t *si) { struct proc *p = sq->sq_proc; struct ksiginfo *ksi; int ret = 0; KASSERT(sq->sq_flags & SQ_INIT, ("sigqueue not inited")); if (signo == SIGKILL || signo == SIGSTOP || si == NULL) goto out_set_bit; /* directly insert the ksi, don't copy it */ if (si->ksi_flags & KSI_INS) { TAILQ_INSERT_TAIL(&sq->sq_list, si, ksi_link); si->ksi_sigq = sq; goto out_set_bit; } if (__predict_false(ksiginfo_zone == NULL)) goto out_set_bit; if (p != NULL && p->p_pendingcnt > max_pending_per_proc) { signal_overflow++; ret = EAGAIN; } else if ((ksi = ksiginfo_alloc()) == NULL) { signal_alloc_fail++; ret = EAGAIN; } else { if (p != NULL) p->p_pendingcnt++; ksiginfo_copy(si, ksi); ksi->ksi_signo = signo; TAILQ_INSERT_TAIL(&sq->sq_list, ksi, ksi_link); ksi->ksi_sigq = sq; } if ((si->ksi_flags & KSI_TRAP) != 0) { ret = 0; goto out_set_bit; } if (ret != 0) return (ret); out_set_bit: SIGADDSET(sq->sq_signals, signo); return (ret); } void sigqueue_flush(sigqueue_t *sq) { struct proc *p = sq->sq_proc; ksiginfo_t *ksi; KASSERT(sq->sq_flags & SQ_INIT, ("sigqueue not inited")); if (p != NULL) PROC_LOCK_ASSERT(p, MA_OWNED); while ((ksi = TAILQ_FIRST(&sq->sq_list)) != NULL) { TAILQ_REMOVE(&sq->sq_list, ksi, ksi_link); ksi->ksi_sigq = NULL; if (ksiginfo_tryfree(ksi) && p != NULL) p->p_pendingcnt--; } SIGEMPTYSET(sq->sq_signals); } void sigqueue_collect_set(sigqueue_t *sq, sigset_t *set) { ksiginfo_t *ksi; KASSERT(sq->sq_flags & SQ_INIT, ("sigqueue not inited")); TAILQ_FOREACH(ksi, &sq->sq_list, ksi_link) SIGADDSET(*set, ksi->ksi_signo); } void sigqueue_move_set(sigqueue_t *src, sigqueue_t *dst, sigset_t *setp) { sigset_t tmp, set; struct proc *p1, *p2; ksiginfo_t *ksi, *next; KASSERT(src->sq_flags & SQ_INIT, ("src sigqueue not inited")); KASSERT(dst->sq_flags & SQ_INIT, ("dst sigqueue not inited")); /* * make a copy, this allows setp to point to src or dst * sq_signals without trouble. */ set = *setp; p1 = src->sq_proc; p2 = dst->sq_proc; /* Move siginfo to target list */ for (ksi = TAILQ_FIRST(&src->sq_list); ksi != NULL; ksi = next) { next = TAILQ_NEXT(ksi, ksi_link); if (SIGISMEMBER(set, ksi->ksi_signo)) { TAILQ_REMOVE(&src->sq_list, ksi, ksi_link); if (p1 != NULL) p1->p_pendingcnt--; TAILQ_INSERT_TAIL(&dst->sq_list, ksi, ksi_link); ksi->ksi_sigq = dst; if (p2 != NULL) p2->p_pendingcnt++; } } /* Move pending bits to target list */ tmp = src->sq_signals; SIGSETAND(tmp, set); SIGSETOR(dst->sq_signals, tmp); SIGSETNAND(src->sq_signals, tmp); /* Finally, rescan src queue and set pending bits for it */ sigqueue_collect_set(src, &src->sq_signals); } void sigqueue_move(sigqueue_t *src, sigqueue_t *dst, int signo) { sigset_t set; SIGEMPTYSET(set); SIGADDSET(set, signo); sigqueue_move_set(src, dst, &set); } void sigqueue_delete_set(sigqueue_t *sq, sigset_t *set) { struct proc *p = sq->sq_proc; ksiginfo_t *ksi, *next; KASSERT(sq->sq_flags & SQ_INIT, ("src sigqueue not inited")); /* Remove siginfo queue */ for (ksi = TAILQ_FIRST(&sq->sq_list); ksi != NULL; ksi = next) { next = TAILQ_NEXT(ksi, ksi_link); if (SIGISMEMBER(*set, ksi->ksi_signo)) { TAILQ_REMOVE(&sq->sq_list, ksi, ksi_link); ksi->ksi_sigq = NULL; if (ksiginfo_tryfree(ksi) && p != NULL) p->p_pendingcnt--; } } SIGSETNAND(sq->sq_signals, *set); /* Finally, rescan queue and set pending bits for it */ sigqueue_collect_set(sq, &sq->sq_signals); } void sigqueue_delete(sigqueue_t *sq, int signo) { sigset_t set; SIGEMPTYSET(set); SIGADDSET(set, signo); sigqueue_delete_set(sq, &set); } /* Remove a set of signals for a process */ void sigqueue_delete_set_proc(struct proc *p, sigset_t *set) { sigqueue_t worklist; struct thread *td0; PROC_LOCK_ASSERT(p, MA_OWNED); sigqueue_init(&worklist, NULL); sigqueue_move_set(&p->p_sigqueue, &worklist, set); mtx_lock_spin(&sched_lock); FOREACH_THREAD_IN_PROC(p, td0) sigqueue_move_set(&td0->td_sigqueue, &worklist, set); mtx_unlock_spin(&sched_lock); sigqueue_flush(&worklist); } void sigqueue_delete_proc(struct proc *p, int signo) { sigset_t set; SIGEMPTYSET(set); SIGADDSET(set, signo); sigqueue_delete_set_proc(p, &set); } void sigqueue_delete_stopmask_proc(struct proc *p) { sigset_t set; SIGEMPTYSET(set); SIGADDSET(set, SIGSTOP); SIGADDSET(set, SIGTSTP); SIGADDSET(set, SIGTTIN); SIGADDSET(set, SIGTTOU); sigqueue_delete_set_proc(p, &set); } /* * Determine signal that should be delivered to process p, the current * process, 0 if none. If there is a pending stop signal with default * action, the process stops in issignal(). * * MP SAFE. */ int cursig(struct thread *td) { PROC_LOCK_ASSERT(td->td_proc, MA_OWNED); mtx_assert(&td->td_proc->p_sigacts->ps_mtx, MA_OWNED); mtx_assert(&sched_lock, MA_NOTOWNED); return (SIGPENDING(td) ? issignal(td) : 0); } /* * Arrange for ast() to handle unmasked pending signals on return to user * mode. This must be called whenever a signal is added to td_sigqueue or * unmasked in td_sigmask. */ void signotify(struct thread *td) { struct proc *p; sigset_t set, saved; p = td->td_proc; PROC_LOCK_ASSERT(p, MA_OWNED); /* * If our mask changed we may have to move signal that were * previously masked by all threads to our sigqueue. */ set = p->p_sigqueue.sq_signals; if (p->p_flag & P_SA) saved = p->p_sigqueue.sq_signals; SIGSETNAND(set, td->td_sigmask); if (! SIGISEMPTY(set)) sigqueue_move_set(&p->p_sigqueue, &td->td_sigqueue, &set); if (SIGPENDING(td)) { mtx_lock_spin(&sched_lock); td->td_flags |= TDF_NEEDSIGCHK | TDF_ASTPENDING; mtx_unlock_spin(&sched_lock); } if ((p->p_flag & P_SA) && !(p->p_flag & P_SIGEVENT)) { if (!SIGSETEQ(saved, p->p_sigqueue.sq_signals)) { /* pending set changed */ p->p_flag |= P_SIGEVENT; wakeup(&p->p_siglist); } } } int sigonstack(size_t sp) { struct thread *td = curthread; return ((td->td_pflags & TDP_ALTSTACK) ? #if defined(COMPAT_43) ((td->td_sigstk.ss_size == 0) ? (td->td_sigstk.ss_flags & SS_ONSTACK) : ((sp - (size_t)td->td_sigstk.ss_sp) < td->td_sigstk.ss_size)) #else ((sp - (size_t)td->td_sigstk.ss_sp) < td->td_sigstk.ss_size) #endif : 0); } static __inline int sigprop(int sig) { if (sig > 0 && sig < NSIG) return (sigproptbl[_SIG_IDX(sig)]); return (0); } int sig_ffs(sigset_t *set) { int i; for (i = 0; i < _SIG_WORDS; i++) if (set->__bits[i]) return (ffs(set->__bits[i]) + (i * 32)); return (0); } /* * kern_sigaction * sigaction * freebsd4_sigaction * osigaction * * MPSAFE */ int kern_sigaction(td, sig, act, oact, flags) struct thread *td; register int sig; struct sigaction *act, *oact; int flags; { struct sigacts *ps; struct proc *p = td->td_proc; if (!_SIG_VALID(sig)) return (EINVAL); PROC_LOCK(p); ps = p->p_sigacts; mtx_lock(&ps->ps_mtx); if (oact) { oact->sa_handler = ps->ps_sigact[_SIG_IDX(sig)]; oact->sa_mask = ps->ps_catchmask[_SIG_IDX(sig)]; oact->sa_flags = 0; if (SIGISMEMBER(ps->ps_sigonstack, sig)) oact->sa_flags |= SA_ONSTACK; if (!SIGISMEMBER(ps->ps_sigintr, sig)) oact->sa_flags |= SA_RESTART; if (SIGISMEMBER(ps->ps_sigreset, sig)) oact->sa_flags |= SA_RESETHAND; if (SIGISMEMBER(ps->ps_signodefer, sig)) oact->sa_flags |= SA_NODEFER; if (SIGISMEMBER(ps->ps_siginfo, sig)) oact->sa_flags |= SA_SIGINFO; if (sig == SIGCHLD && ps->ps_flag & PS_NOCLDSTOP) oact->sa_flags |= SA_NOCLDSTOP; if (sig == SIGCHLD && ps->ps_flag & PS_NOCLDWAIT) oact->sa_flags |= SA_NOCLDWAIT; } if (act) { if ((sig == SIGKILL || sig == SIGSTOP) && act->sa_handler != SIG_DFL) { mtx_unlock(&ps->ps_mtx); PROC_UNLOCK(p); return (EINVAL); } /* * Change setting atomically. */ ps->ps_catchmask[_SIG_IDX(sig)] = act->sa_mask; SIG_CANTMASK(ps->ps_catchmask[_SIG_IDX(sig)]); if (act->sa_flags & SA_SIGINFO) { ps->ps_sigact[_SIG_IDX(sig)] = (__sighandler_t *)act->sa_sigaction; SIGADDSET(ps->ps_siginfo, sig); } else { ps->ps_sigact[_SIG_IDX(sig)] = act->sa_handler; SIGDELSET(ps->ps_siginfo, sig); } if (!(act->sa_flags & SA_RESTART)) SIGADDSET(ps->ps_sigintr, sig); else SIGDELSET(ps->ps_sigintr, sig); if (act->sa_flags & SA_ONSTACK) SIGADDSET(ps->ps_sigonstack, sig); else SIGDELSET(ps->ps_sigonstack, sig); if (act->sa_flags & SA_RESETHAND) SIGADDSET(ps->ps_sigreset, sig); else SIGDELSET(ps->ps_sigreset, sig); if (act->sa_flags & SA_NODEFER) SIGADDSET(ps->ps_signodefer, sig); else SIGDELSET(ps->ps_signodefer, sig); if (sig == SIGCHLD) { if (act->sa_flags & SA_NOCLDSTOP) ps->ps_flag |= PS_NOCLDSTOP; else ps->ps_flag &= ~PS_NOCLDSTOP; if (act->sa_flags & SA_NOCLDWAIT) { /* * Paranoia: since SA_NOCLDWAIT is implemented * by reparenting the dying child to PID 1 (and * trust it to reap the zombie), PID 1 itself * is forbidden to set SA_NOCLDWAIT. */ if (p->p_pid == 1) ps->ps_flag &= ~PS_NOCLDWAIT; else ps->ps_flag |= PS_NOCLDWAIT; } else ps->ps_flag &= ~PS_NOCLDWAIT; if (ps->ps_sigact[_SIG_IDX(SIGCHLD)] == SIG_IGN) ps->ps_flag |= PS_CLDSIGIGN; else ps->ps_flag &= ~PS_CLDSIGIGN; } /* * Set bit in ps_sigignore for signals that are set to SIG_IGN, * and for signals set to SIG_DFL where the default is to * ignore. However, don't put SIGCONT in ps_sigignore, as we * have to restart the process. */ if (ps->ps_sigact[_SIG_IDX(sig)] == SIG_IGN || (sigprop(sig) & SA_IGNORE && ps->ps_sigact[_SIG_IDX(sig)] == SIG_DFL)) { if ((p->p_flag & P_SA) && SIGISMEMBER(p->p_sigqueue.sq_signals, sig)) { p->p_flag |= P_SIGEVENT; wakeup(&p->p_siglist); } /* never to be seen again */ sigqueue_delete_proc(p, sig); if (sig != SIGCONT) /* easier in psignal */ SIGADDSET(ps->ps_sigignore, sig); SIGDELSET(ps->ps_sigcatch, sig); } else { SIGDELSET(ps->ps_sigignore, sig); if (ps->ps_sigact[_SIG_IDX(sig)] == SIG_DFL) SIGDELSET(ps->ps_sigcatch, sig); else SIGADDSET(ps->ps_sigcatch, sig); } #ifdef COMPAT_FREEBSD4 if (ps->ps_sigact[_SIG_IDX(sig)] == SIG_IGN || ps->ps_sigact[_SIG_IDX(sig)] == SIG_DFL || (flags & KSA_FREEBSD4) == 0) SIGDELSET(ps->ps_freebsd4, sig); else SIGADDSET(ps->ps_freebsd4, sig); #endif #ifdef COMPAT_43 if (ps->ps_sigact[_SIG_IDX(sig)] == SIG_IGN || ps->ps_sigact[_SIG_IDX(sig)] == SIG_DFL || (flags & KSA_OSIGSET) == 0) SIGDELSET(ps->ps_osigset, sig); else SIGADDSET(ps->ps_osigset, sig); #endif } mtx_unlock(&ps->ps_mtx); PROC_UNLOCK(p); return (0); } #ifndef _SYS_SYSPROTO_H_ struct sigaction_args { int sig; struct sigaction *act; struct sigaction *oact; }; #endif /* * MPSAFE */ int sigaction(td, uap) struct thread *td; register struct sigaction_args *uap; { struct sigaction act, oact; register struct sigaction *actp, *oactp; int error; actp = (uap->act != NULL) ? &act : NULL; oactp = (uap->oact != NULL) ? &oact : NULL; if (actp) { error = copyin(uap->act, actp, sizeof(act)); if (error) return (error); } error = kern_sigaction(td, uap->sig, actp, oactp, 0); if (oactp && !error) error = copyout(oactp, uap->oact, sizeof(oact)); return (error); } #ifdef COMPAT_FREEBSD4 #ifndef _SYS_SYSPROTO_H_ struct freebsd4_sigaction_args { int sig; struct sigaction *act; struct sigaction *oact; }; #endif /* * MPSAFE */ int freebsd4_sigaction(td, uap) struct thread *td; register struct freebsd4_sigaction_args *uap; { struct sigaction act, oact; register struct sigaction *actp, *oactp; int error; actp = (uap->act != NULL) ? &act : NULL; oactp = (uap->oact != NULL) ? &oact : NULL; if (actp) { error = copyin(uap->act, actp, sizeof(act)); if (error) return (error); } error = kern_sigaction(td, uap->sig, actp, oactp, KSA_FREEBSD4); if (oactp && !error) error = copyout(oactp, uap->oact, sizeof(oact)); return (error); } #endif /* COMAPT_FREEBSD4 */ #ifdef COMPAT_43 /* XXX - COMPAT_FBSD3 */ #ifndef _SYS_SYSPROTO_H_ struct osigaction_args { int signum; struct osigaction *nsa; struct osigaction *osa; }; #endif /* * MPSAFE */ int osigaction(td, uap) struct thread *td; register struct osigaction_args *uap; { struct osigaction sa; struct sigaction nsa, osa; register struct sigaction *nsap, *osap; int error; if (uap->signum <= 0 || uap->signum >= ONSIG) return (EINVAL); nsap = (uap->nsa != NULL) ? &nsa : NULL; osap = (uap->osa != NULL) ? &osa : NULL; if (nsap) { error = copyin(uap->nsa, &sa, sizeof(sa)); if (error) return (error); nsap->sa_handler = sa.sa_handler; nsap->sa_flags = sa.sa_flags; OSIG2SIG(sa.sa_mask, nsap->sa_mask); } error = kern_sigaction(td, uap->signum, nsap, osap, KSA_OSIGSET); if (osap && !error) { sa.sa_handler = osap->sa_handler; sa.sa_flags = osap->sa_flags; SIG2OSIG(osap->sa_mask, sa.sa_mask); error = copyout(&sa, uap->osa, sizeof(sa)); } return (error); } #if !defined(__i386__) && !defined(__alpha__) /* Avoid replicating the same stub everywhere */ int osigreturn(td, uap) struct thread *td; struct osigreturn_args *uap; { return (nosys(td, (struct nosys_args *)uap)); } #endif #endif /* COMPAT_43 */ /* * Initialize signal state for process 0; * set to ignore signals that are ignored by default. */ void siginit(p) struct proc *p; { register int i; struct sigacts *ps; PROC_LOCK(p); ps = p->p_sigacts; mtx_lock(&ps->ps_mtx); for (i = 1; i <= NSIG; i++) if (sigprop(i) & SA_IGNORE && i != SIGCONT) SIGADDSET(ps->ps_sigignore, i); mtx_unlock(&ps->ps_mtx); PROC_UNLOCK(p); } /* * Reset signals for an exec of the specified process. */ void execsigs(struct proc *p) { struct sigacts *ps; int sig; struct thread *td; /* * Reset caught signals. Held signals remain held * through td_sigmask (unless they were caught, * and are now ignored by default). */ PROC_LOCK_ASSERT(p, MA_OWNED); td = FIRST_THREAD_IN_PROC(p); ps = p->p_sigacts; mtx_lock(&ps->ps_mtx); while (SIGNOTEMPTY(ps->ps_sigcatch)) { sig = sig_ffs(&ps->ps_sigcatch); SIGDELSET(ps->ps_sigcatch, sig); if (sigprop(sig) & SA_IGNORE) { if (sig != SIGCONT) SIGADDSET(ps->ps_sigignore, sig); sigqueue_delete_proc(p, sig); } ps->ps_sigact[_SIG_IDX(sig)] = SIG_DFL; } /* * Reset stack state to the user stack. * Clear set of signals caught on the signal stack. */ td->td_sigstk.ss_flags = SS_DISABLE; td->td_sigstk.ss_size = 0; td->td_sigstk.ss_sp = 0; td->td_pflags &= ~TDP_ALTSTACK; /* * Reset no zombies if child dies flag as Solaris does. */ ps->ps_flag &= ~(PS_NOCLDWAIT | PS_CLDSIGIGN); if (ps->ps_sigact[_SIG_IDX(SIGCHLD)] == SIG_IGN) ps->ps_sigact[_SIG_IDX(SIGCHLD)] = SIG_DFL; mtx_unlock(&ps->ps_mtx); } /* * kern_sigprocmask() * * Manipulate signal mask. */ int kern_sigprocmask(td, how, set, oset, old) struct thread *td; int how; sigset_t *set, *oset; int old; { int error; PROC_LOCK(td->td_proc); if (oset != NULL) *oset = td->td_sigmask; error = 0; if (set != NULL) { switch (how) { case SIG_BLOCK: SIG_CANTMASK(*set); SIGSETOR(td->td_sigmask, *set); break; case SIG_UNBLOCK: SIGSETNAND(td->td_sigmask, *set); signotify(td); break; case SIG_SETMASK: SIG_CANTMASK(*set); if (old) SIGSETLO(td->td_sigmask, *set); else td->td_sigmask = *set; signotify(td); break; default: error = EINVAL; break; } } PROC_UNLOCK(td->td_proc); return (error); } /* * sigprocmask() - MP SAFE */ #ifndef _SYS_SYSPROTO_H_ struct sigprocmask_args { int how; const sigset_t *set; sigset_t *oset; }; #endif int sigprocmask(td, uap) register struct thread *td; struct sigprocmask_args *uap; { sigset_t set, oset; sigset_t *setp, *osetp; int error; setp = (uap->set != NULL) ? &set : NULL; osetp = (uap->oset != NULL) ? &oset : NULL; if (setp) { error = copyin(uap->set, setp, sizeof(set)); if (error) return (error); } error = kern_sigprocmask(td, uap->how, setp, osetp, 0); if (osetp && !error) { error = copyout(osetp, uap->oset, sizeof(oset)); } return (error); } #ifdef COMPAT_43 /* XXX - COMPAT_FBSD3 */ /* * osigprocmask() - MP SAFE */ #ifndef _SYS_SYSPROTO_H_ struct osigprocmask_args { int how; osigset_t mask; }; #endif int osigprocmask(td, uap) register struct thread *td; struct osigprocmask_args *uap; { sigset_t set, oset; int error; OSIG2SIG(uap->mask, set); error = kern_sigprocmask(td, uap->how, &set, &oset, 1); SIG2OSIG(oset, td->td_retval[0]); return (error); } #endif /* COMPAT_43 */ /* * MPSAFE */ int sigwait(struct thread *td, struct sigwait_args *uap) { ksiginfo_t ksi; sigset_t set; int error; error = copyin(uap->set, &set, sizeof(set)); if (error) { td->td_retval[0] = error; return (0); } error = kern_sigtimedwait(td, set, &ksi, NULL); if (error) { if (error == ERESTART) return (error); td->td_retval[0] = error; return (0); } error = copyout(&ksi.ksi_signo, uap->sig, sizeof(ksi.ksi_signo)); td->td_retval[0] = error; return (0); } /* * MPSAFE */ int sigtimedwait(struct thread *td, struct sigtimedwait_args *uap) { struct timespec ts; struct timespec *timeout; sigset_t set; ksiginfo_t ksi; int error; if (uap->timeout) { error = copyin(uap->timeout, &ts, sizeof(ts)); if (error) return (error); timeout = &ts; } else timeout = NULL; error = copyin(uap->set, &set, sizeof(set)); if (error) return (error); error = kern_sigtimedwait(td, set, &ksi, timeout); if (error) return (error); if (uap->info) error = copyout(&ksi.ksi_info, uap->info, sizeof(siginfo_t)); if (error == 0) td->td_retval[0] = ksi.ksi_signo; return (error); } /* * MPSAFE */ int sigwaitinfo(struct thread *td, struct sigwaitinfo_args *uap) { ksiginfo_t ksi; sigset_t set; int error; error = copyin(uap->set, &set, sizeof(set)); if (error) return (error); error = kern_sigtimedwait(td, set, &ksi, NULL); if (error) return (error); if (uap->info) error = copyout(&ksi.ksi_info, uap->info, sizeof(siginfo_t)); if (error == 0) td->td_retval[0] = ksi.ksi_signo; return (error); } static int kern_sigtimedwait(struct thread *td, sigset_t waitset, ksiginfo_t *ksi, struct timespec *timeout) { struct sigacts *ps; sigset_t savedmask; struct proc *p; int error, sig, hz, i, timevalid = 0; struct timespec rts, ets, ts; struct timeval tv; p = td->td_proc; error = 0; sig = 0; SIG_CANTMASK(waitset); PROC_LOCK(p); ps = p->p_sigacts; savedmask = td->td_sigmask; if (timeout) { if (timeout->tv_nsec >= 0 && timeout->tv_nsec < 1000000000) { timevalid = 1; getnanouptime(&rts); ets = rts; timespecadd(&ets, timeout); } } again: for (i = 1; i <= _SIG_MAXSIG; ++i) { if (!SIGISMEMBER(waitset, i)) continue; if (SIGISMEMBER(td->td_sigqueue.sq_signals, i)) { SIGFILLSET(td->td_sigmask); SIG_CANTMASK(td->td_sigmask); SIGDELSET(td->td_sigmask, i); mtx_lock(&ps->ps_mtx); sig = cursig(td); i = 0; mtx_unlock(&ps->ps_mtx); } else if (SIGISMEMBER(p->p_sigqueue.sq_signals, i)) { if (p->p_flag & P_SA) { p->p_flag |= P_SIGEVENT; wakeup(&p->p_siglist); } sigqueue_move(&p->p_sigqueue, &td->td_sigqueue, i); SIGFILLSET(td->td_sigmask); SIG_CANTMASK(td->td_sigmask); SIGDELSET(td->td_sigmask, i); mtx_lock(&ps->ps_mtx); sig = cursig(td); i = 0; mtx_unlock(&ps->ps_mtx); } if (sig) goto out; } if (error) goto out; /* * POSIX says this must be checked after looking for pending * signals. */ if (timeout) { if (!timevalid) { error = EINVAL; goto out; } getnanouptime(&rts); if (timespeccmp(&rts, &ets, >=)) { error = EAGAIN; goto out; } ts = ets; timespecsub(&ts, &rts); TIMESPEC_TO_TIMEVAL(&tv, &ts); hz = tvtohz(&tv); } else hz = 0; td->td_sigmask = savedmask; SIGSETNAND(td->td_sigmask, waitset); signotify(td); error = msleep(&ps, &p->p_mtx, PPAUSE|PCATCH, "sigwait", hz); if (timeout) { if (error == ERESTART) { /* timeout can not be restarted. */ error = EINTR; } else if (error == EAGAIN) { /* will calculate timeout by ourself. */ error = 0; } } goto again; out: if (sig) { sig_t action; ksiginfo_init(ksi); sigqueue_get(&td->td_sigqueue, sig, ksi); ksi->ksi_signo = sig; if (ksi->ksi_code == SI_TIMER) itimer_accept(p, ksi->ksi_timerid, ksi); error = 0; mtx_lock(&ps->ps_mtx); action = ps->ps_sigact[_SIG_IDX(sig)]; mtx_unlock(&ps->ps_mtx); #ifdef KTRACE if (KTRPOINT(td, KTR_PSIG)) ktrpsig(sig, action, &td->td_sigmask, 0); #endif _STOPEVENT(p, S_SIG, sig); } td->td_sigmask = savedmask; signotify(td); PROC_UNLOCK(p); return (error); } #ifndef _SYS_SYSPROTO_H_ struct sigpending_args { sigset_t *set; }; #endif /* * MPSAFE */ int sigpending(td, uap) struct thread *td; struct sigpending_args *uap; { struct proc *p = td->td_proc; sigset_t pending; PROC_LOCK(p); pending = p->p_sigqueue.sq_signals; SIGSETOR(pending, td->td_sigqueue.sq_signals); PROC_UNLOCK(p); return (copyout(&pending, uap->set, sizeof(sigset_t))); } #ifdef COMPAT_43 /* XXX - COMPAT_FBSD3 */ #ifndef _SYS_SYSPROTO_H_ struct osigpending_args { int dummy; }; #endif /* * MPSAFE */ int osigpending(td, uap) struct thread *td; struct osigpending_args *uap; { struct proc *p = td->td_proc; sigset_t pending; PROC_LOCK(p); pending = p->p_sigqueue.sq_signals; SIGSETOR(pending, td->td_sigqueue.sq_signals); PROC_UNLOCK(p); SIG2OSIG(pending, td->td_retval[0]); return (0); } #endif /* COMPAT_43 */ #if defined(COMPAT_43) /* * Generalized interface signal handler, 4.3-compatible. */ #ifndef _SYS_SYSPROTO_H_ struct osigvec_args { int signum; struct sigvec *nsv; struct sigvec *osv; }; #endif /* * MPSAFE */ /* ARGSUSED */ int osigvec(td, uap) struct thread *td; register struct osigvec_args *uap; { struct sigvec vec; struct sigaction nsa, osa; register struct sigaction *nsap, *osap; int error; if (uap->signum <= 0 || uap->signum >= ONSIG) return (EINVAL); nsap = (uap->nsv != NULL) ? &nsa : NULL; osap = (uap->osv != NULL) ? &osa : NULL; if (nsap) { error = copyin(uap->nsv, &vec, sizeof(vec)); if (error) return (error); nsap->sa_handler = vec.sv_handler; OSIG2SIG(vec.sv_mask, nsap->sa_mask); nsap->sa_flags = vec.sv_flags; nsap->sa_flags ^= SA_RESTART; /* opposite of SV_INTERRUPT */ } error = kern_sigaction(td, uap->signum, nsap, osap, KSA_OSIGSET); if (osap && !error) { vec.sv_handler = osap->sa_handler; SIG2OSIG(osap->sa_mask, vec.sv_mask); vec.sv_flags = osap->sa_flags; vec.sv_flags &= ~SA_NOCLDWAIT; vec.sv_flags ^= SA_RESTART; error = copyout(&vec, uap->osv, sizeof(vec)); } return (error); } #ifndef _SYS_SYSPROTO_H_ struct osigblock_args { int mask; }; #endif /* * MPSAFE */ int osigblock(td, uap) register struct thread *td; struct osigblock_args *uap; { struct proc *p = td->td_proc; sigset_t set; OSIG2SIG(uap->mask, set); SIG_CANTMASK(set); PROC_LOCK(p); SIG2OSIG(td->td_sigmask, td->td_retval[0]); SIGSETOR(td->td_sigmask, set); PROC_UNLOCK(p); return (0); } #ifndef _SYS_SYSPROTO_H_ struct osigsetmask_args { int mask; }; #endif /* * MPSAFE */ int osigsetmask(td, uap) struct thread *td; struct osigsetmask_args *uap; { struct proc *p = td->td_proc; sigset_t set; OSIG2SIG(uap->mask, set); SIG_CANTMASK(set); PROC_LOCK(p); SIG2OSIG(td->td_sigmask, td->td_retval[0]); SIGSETLO(td->td_sigmask, set); signotify(td); PROC_UNLOCK(p); return (0); } #endif /* COMPAT_43 */ /* * Suspend calling thread until signal, providing mask to be set * in the meantime. */ #ifndef _SYS_SYSPROTO_H_ struct sigsuspend_args { const sigset_t *sigmask; }; #endif /* * MPSAFE */ /* ARGSUSED */ int sigsuspend(td, uap) struct thread *td; struct sigsuspend_args *uap; { sigset_t mask; int error; error = copyin(uap->sigmask, &mask, sizeof(mask)); if (error) return (error); return (kern_sigsuspend(td, mask)); } int kern_sigsuspend(struct thread *td, sigset_t mask) { struct proc *p = td->td_proc; /* * When returning from sigsuspend, we want * the old mask to be restored after the * signal handler has finished. Thus, we * save it here and mark the sigacts structure * to indicate this. */ PROC_LOCK(p); td->td_oldsigmask = td->td_sigmask; td->td_pflags |= TDP_OLDMASK; SIG_CANTMASK(mask); td->td_sigmask = mask; signotify(td); while (msleep(&p->p_sigacts, &p->p_mtx, PPAUSE|PCATCH, "pause", 0) == 0) /* void */; PROC_UNLOCK(p); /* always return EINTR rather than ERESTART... */ return (EINTR); } #ifdef COMPAT_43 /* XXX - COMPAT_FBSD3 */ /* * Compatibility sigsuspend call for old binaries. Note nonstandard calling * convention: libc stub passes mask, not pointer, to save a copyin. */ #ifndef _SYS_SYSPROTO_H_ struct osigsuspend_args { osigset_t mask; }; #endif /* * MPSAFE */ /* ARGSUSED */ int osigsuspend(td, uap) struct thread *td; struct osigsuspend_args *uap; { struct proc *p = td->td_proc; sigset_t mask; PROC_LOCK(p); td->td_oldsigmask = td->td_sigmask; td->td_pflags |= TDP_OLDMASK; OSIG2SIG(uap->mask, mask); SIG_CANTMASK(mask); SIGSETLO(td->td_sigmask, mask); signotify(td); while (msleep(&p->p_sigacts, &p->p_mtx, PPAUSE|PCATCH, "opause", 0) == 0) /* void */; PROC_UNLOCK(p); /* always return EINTR rather than ERESTART... */ return (EINTR); } #endif /* COMPAT_43 */ #if defined(COMPAT_43) #ifndef _SYS_SYSPROTO_H_ struct osigstack_args { struct sigstack *nss; struct sigstack *oss; }; #endif /* * MPSAFE */ /* ARGSUSED */ int osigstack(td, uap) struct thread *td; register struct osigstack_args *uap; { struct sigstack nss, oss; int error = 0; if (uap->nss != NULL) { error = copyin(uap->nss, &nss, sizeof(nss)); if (error) return (error); } oss.ss_sp = td->td_sigstk.ss_sp; oss.ss_onstack = sigonstack(cpu_getstack(td)); if (uap->nss != NULL) { td->td_sigstk.ss_sp = nss.ss_sp; td->td_sigstk.ss_size = 0; td->td_sigstk.ss_flags |= nss.ss_onstack & SS_ONSTACK; td->td_pflags |= TDP_ALTSTACK; } if (uap->oss != NULL) error = copyout(&oss, uap->oss, sizeof(oss)); return (error); } #endif /* COMPAT_43 */ #ifndef _SYS_SYSPROTO_H_ struct sigaltstack_args { stack_t *ss; stack_t *oss; }; #endif /* * MPSAFE */ /* ARGSUSED */ int sigaltstack(td, uap) struct thread *td; register struct sigaltstack_args *uap; { stack_t ss, oss; int error; if (uap->ss != NULL) { error = copyin(uap->ss, &ss, sizeof(ss)); if (error) return (error); } error = kern_sigaltstack(td, (uap->ss != NULL) ? &ss : NULL, (uap->oss != NULL) ? &oss : NULL); if (error) return (error); if (uap->oss != NULL) error = copyout(&oss, uap->oss, sizeof(stack_t)); return (error); } int kern_sigaltstack(struct thread *td, stack_t *ss, stack_t *oss) { struct proc *p = td->td_proc; int oonstack; oonstack = sigonstack(cpu_getstack(td)); if (oss != NULL) { *oss = td->td_sigstk; oss->ss_flags = (td->td_pflags & TDP_ALTSTACK) ? ((oonstack) ? SS_ONSTACK : 0) : SS_DISABLE; } if (ss != NULL) { if (oonstack) return (EPERM); if ((ss->ss_flags & ~SS_DISABLE) != 0) return (EINVAL); if (!(ss->ss_flags & SS_DISABLE)) { if (ss->ss_size < p->p_sysent->sv_minsigstksz) return (ENOMEM); td->td_sigstk = *ss; td->td_pflags |= TDP_ALTSTACK; } else { td->td_pflags &= ~TDP_ALTSTACK; } } return (0); } /* * Common code for kill process group/broadcast kill. * cp is calling process. */ static int killpg1(td, sig, pgid, all) register struct thread *td; int sig, pgid, all; { register struct proc *p; struct pgrp *pgrp; int nfound = 0; if (all) { /* * broadcast */ sx_slock(&allproc_lock); LIST_FOREACH(p, &allproc, p_list) { PROC_LOCK(p); if (p->p_pid <= 1 || p->p_flag & P_SYSTEM || p == td->td_proc) { PROC_UNLOCK(p); continue; } if (p_cansignal(td, p, sig) == 0) { nfound++; if (sig) psignal(p, sig); } PROC_UNLOCK(p); } sx_sunlock(&allproc_lock); } else { sx_slock(&proctree_lock); if (pgid == 0) { /* * zero pgid means send to my process group. */ pgrp = td->td_proc->p_pgrp; PGRP_LOCK(pgrp); } else { pgrp = pgfind(pgid); if (pgrp == NULL) { sx_sunlock(&proctree_lock); return (ESRCH); } } sx_sunlock(&proctree_lock); LIST_FOREACH(p, &pgrp->pg_members, p_pglist) { PROC_LOCK(p); if (p->p_pid <= 1 || p->p_flag & P_SYSTEM) { PROC_UNLOCK(p); continue; } if (p_cansignal(td, p, sig) == 0) { nfound++; if (sig) psignal(p, sig); } PROC_UNLOCK(p); } PGRP_UNLOCK(pgrp); } return (nfound ? 0 : ESRCH); } #ifndef _SYS_SYSPROTO_H_ struct kill_args { int pid; int signum; }; #endif /* * MPSAFE */ /* ARGSUSED */ int kill(td, uap) register struct thread *td; register struct kill_args *uap; { register struct proc *p; int error; if ((u_int)uap->signum > _SIG_MAXSIG) return (EINVAL); if (uap->pid > 0) { /* kill single process */ if ((p = pfind(uap->pid)) == NULL) { if ((p = zpfind(uap->pid)) == NULL) return (ESRCH); } error = p_cansignal(td, p, uap->signum); if (error == 0 && uap->signum) psignal(p, uap->signum); PROC_UNLOCK(p); return (error); } switch (uap->pid) { case -1: /* broadcast signal */ return (killpg1(td, uap->signum, 0, 1)); case 0: /* signal own process group */ return (killpg1(td, uap->signum, 0, 0)); default: /* negative explicit process group */ return (killpg1(td, uap->signum, -uap->pid, 0)); } /* NOTREACHED */ } #if defined(COMPAT_43) #ifndef _SYS_SYSPROTO_H_ struct okillpg_args { int pgid; int signum; }; #endif /* * MPSAFE */ /* ARGSUSED */ int okillpg(td, uap) struct thread *td; register struct okillpg_args *uap; { if ((u_int)uap->signum > _SIG_MAXSIG) return (EINVAL); return (killpg1(td, uap->signum, uap->pgid, 0)); } #endif /* COMPAT_43 */ #ifndef _SYS_SYSPROTO_H_ struct sigqueue_args { pid_t pid; int signum; /* union sigval */ void *value; }; #endif int sigqueue(struct thread *td, struct sigqueue_args *uap) { ksiginfo_t ksi; struct proc *p; int error; if ((u_int)uap->signum > _SIG_MAXSIG) return (EINVAL); /* * Specification says sigqueue can only send signal to * single process. */ if (uap->pid <= 0) return (EINVAL); if ((p = pfind(uap->pid)) == NULL) { if ((p = zpfind(uap->pid)) == NULL) return (ESRCH); } error = p_cansignal(td, p, uap->signum); if (error == 0 && uap->signum != 0) { ksiginfo_init(&ksi); ksi.ksi_signo = uap->signum; ksi.ksi_code = SI_QUEUE; ksi.ksi_pid = td->td_proc->p_pid; ksi.ksi_uid = td->td_ucred->cr_ruid; - ksi.ksi_value.sigval_ptr = uap->value; + ksi.ksi_value.sival_ptr = uap->value; error = tdsignal(p, NULL, ksi.ksi_signo, &ksi); } PROC_UNLOCK(p); return (error); } /* * Send a signal to a process group. */ void gsignal(pgid, sig) int pgid, sig; { struct pgrp *pgrp; if (pgid != 0) { sx_slock(&proctree_lock); pgrp = pgfind(pgid); sx_sunlock(&proctree_lock); if (pgrp != NULL) { pgsignal(pgrp, sig, 0); PGRP_UNLOCK(pgrp); } } } /* * Send a signal to a process group. If checktty is 1, * limit to members which have a controlling terminal. */ void pgsignal(pgrp, sig, checkctty) struct pgrp *pgrp; int sig, checkctty; { register struct proc *p; if (pgrp) { PGRP_LOCK_ASSERT(pgrp, MA_OWNED); LIST_FOREACH(p, &pgrp->pg_members, p_pglist) { PROC_LOCK(p); if (checkctty == 0 || p->p_flag & P_CONTROLT) psignal(p, sig); PROC_UNLOCK(p); } } } /* * Send a signal caused by a trap to the current thread. * If it will be caught immediately, deliver it with correct code. * Otherwise, post it normally. * * MPSAFE */ void trapsignal(struct thread *td, ksiginfo_t *ksi) { struct sigacts *ps; struct proc *p; int error; int sig; int code; p = td->td_proc; sig = ksi->ksi_signo; code = ksi->ksi_code; KASSERT(_SIG_VALID(sig), ("invalid signal")); if (td->td_pflags & TDP_SA) { if (td->td_mailbox == NULL) thread_user_enter(td); PROC_LOCK(p); SIGDELSET(td->td_sigmask, sig); mtx_lock_spin(&sched_lock); /* * Force scheduling an upcall, so UTS has chance to * process the signal before thread runs again in * userland. */ if (td->td_upcall) td->td_upcall->ku_flags |= KUF_DOUPCALL; mtx_unlock_spin(&sched_lock); } else { PROC_LOCK(p); } ps = p->p_sigacts; mtx_lock(&ps->ps_mtx); if ((p->p_flag & P_TRACED) == 0 && SIGISMEMBER(ps->ps_sigcatch, sig) && !SIGISMEMBER(td->td_sigmask, sig)) { p->p_stats->p_ru.ru_nsignals++; #ifdef KTRACE if (KTRPOINT(curthread, KTR_PSIG)) ktrpsig(sig, ps->ps_sigact[_SIG_IDX(sig)], &td->td_sigmask, code); #endif if (!(td->td_pflags & TDP_SA)) (*p->p_sysent->sv_sendsig)(ps->ps_sigact[_SIG_IDX(sig)], ksi, &td->td_sigmask); else if (td->td_mailbox == NULL) { mtx_unlock(&ps->ps_mtx); /* UTS caused a sync signal */ p->p_code = code; /* XXX for core dump/debugger */ p->p_sig = sig; /* XXX to verify code */ sigexit(td, sig); } else { mtx_unlock(&ps->ps_mtx); SIGADDSET(td->td_sigmask, sig); PROC_UNLOCK(p); error = copyout(&ksi->ksi_info, &td->td_mailbox->tm_syncsig, sizeof(siginfo_t)); PROC_LOCK(p); /* UTS memory corrupted */ if (error) sigexit(td, SIGSEGV); mtx_lock(&ps->ps_mtx); } SIGSETOR(td->td_sigmask, ps->ps_catchmask[_SIG_IDX(sig)]); if (!SIGISMEMBER(ps->ps_signodefer, sig)) SIGADDSET(td->td_sigmask, sig); if (SIGISMEMBER(ps->ps_sigreset, sig)) { /* * See kern_sigaction() for origin of this code. */ SIGDELSET(ps->ps_sigcatch, sig); if (sig != SIGCONT && sigprop(sig) & SA_IGNORE) SIGADDSET(ps->ps_sigignore, sig); ps->ps_sigact[_SIG_IDX(sig)] = SIG_DFL; } mtx_unlock(&ps->ps_mtx); } else { mtx_unlock(&ps->ps_mtx); p->p_code = code; /* XXX for core dump/debugger */ p->p_sig = sig; /* XXX to verify code */ tdsignal(p, td, sig, ksi); } PROC_UNLOCK(p); } static struct thread * sigtd(struct proc *p, int sig, int prop) { struct thread *td, *signal_td; PROC_LOCK_ASSERT(p, MA_OWNED); /* * Check if current thread can handle the signal without * switching conetxt to another thread. */ if (curproc == p && !SIGISMEMBER(curthread->td_sigmask, sig)) return (curthread); signal_td = NULL; mtx_lock_spin(&sched_lock); FOREACH_THREAD_IN_PROC(p, td) { if (!SIGISMEMBER(td->td_sigmask, sig)) { signal_td = td; break; } } if (signal_td == NULL) signal_td = FIRST_THREAD_IN_PROC(p); mtx_unlock_spin(&sched_lock); return (signal_td); } /* * Send the signal to the process. If the signal has an action, the action * is usually performed by the target process rather than the caller; we add * the signal to the set of pending signals for the process. * * Exceptions: * o When a stop signal is sent to a sleeping process that takes the * default action, the process is stopped without awakening it. * o SIGCONT restarts stopped processes (or puts them back to sleep) * regardless of the signal action (eg, blocked or ignored). * * Other ignored signals are discarded immediately. * * MPSAFE */ void psignal(struct proc *p, int sig) { (void) tdsignal(p, NULL, sig, NULL); } int psignal_event(struct proc *p, struct sigevent *sigev, ksiginfo_t *ksi) { struct thread *td = NULL; PROC_LOCK_ASSERT(p, MA_OWNED); KASSERT(!KSI_ONQ(ksi), ("psignal_event: ksi on queue")); /* * ksi_code and other fields should be set before * calling this function. */ ksi->ksi_signo = sigev->sigev_signo; ksi->ksi_value = sigev->sigev_value; if (sigev->sigev_notify == SIGEV_THREAD_ID) { td = thread_find(p, sigev->sigev_notify_thread_id); if (td == NULL) return (ESRCH); } return (tdsignal(p, td, ksi->ksi_signo, ksi)); } /* * MPSAFE */ int tdsignal(struct proc *p, struct thread *td, int sig, ksiginfo_t *ksi) { sigset_t saved; int ret; if (p->p_flag & P_SA) saved = p->p_sigqueue.sq_signals; ret = do_tdsignal(p, td, sig, ksi); if ((p->p_flag & P_SA) && !(p->p_flag & P_SIGEVENT)) { if (!SIGSETEQ(saved, p->p_sigqueue.sq_signals)) { /* pending set changed */ p->p_flag |= P_SIGEVENT; wakeup(&p->p_siglist); } } return (ret); } static int do_tdsignal(struct proc *p, struct thread *td, int sig, ksiginfo_t *ksi) { sig_t action; sigqueue_t *sigqueue; struct thread *td0; int prop; struct sigacts *ps; int ret = 0; PROC_LOCK_ASSERT(p, MA_OWNED); if (!_SIG_VALID(sig)) panic("do_tdsignal(): invalid signal"); KASSERT(ksi == NULL || !KSI_ONQ(ksi), ("do_tdsignal: ksi on queue")); /* * IEEE Std 1003.1-2001: return success when killing a zombie. */ if (p->p_state == PRS_ZOMBIE) { if (ksi && (ksi->ksi_flags & KSI_INS)) ksiginfo_tryfree(ksi); return (ret); } ps = p->p_sigacts; KNOTE_LOCKED(&p->p_klist, NOTE_SIGNAL | sig); prop = sigprop(sig); /* * If the signal is blocked and not destined for this thread, then * assign it to the process so that we can find it later in the first * thread that unblocks it. Otherwise, assign it to this thread now. */ if (td == NULL) { td = sigtd(p, sig, prop); if (SIGISMEMBER(td->td_sigmask, sig)) sigqueue = &p->p_sigqueue; else sigqueue = &td->td_sigqueue; } else { KASSERT(td->td_proc == p, ("invalid thread")); sigqueue = &td->td_sigqueue; } /* * If the signal is being ignored, * or process is exiting or thread is exiting, * then we forget about it immediately. * (Note: we don't set SIGCONT in ps_sigignore, * and if it is set to SIG_IGN, * action will be SIG_DFL here.) */ mtx_lock(&ps->ps_mtx); if (SIGISMEMBER(ps->ps_sigignore, sig) || (p->p_flag & P_WEXIT)) { mtx_unlock(&ps->ps_mtx); if (ksi && (ksi->ksi_flags & KSI_INS)) ksiginfo_tryfree(ksi); return (ret); } if (SIGISMEMBER(td->td_sigmask, sig)) action = SIG_HOLD; else if (SIGISMEMBER(ps->ps_sigcatch, sig)) action = SIG_CATCH; else action = SIG_DFL; mtx_unlock(&ps->ps_mtx); if (prop & SA_CONT) sigqueue_delete_stopmask_proc(p); else if (prop & SA_STOP) { /* * If sending a tty stop signal to a member of an orphaned * process group, discard the signal here if the action * is default; don't stop the process below if sleeping, * and don't clear any pending SIGCONT. */ if ((prop & SA_TTYSTOP) && (p->p_pgrp->pg_jobc == 0) && (action == SIG_DFL)) { if (ksi && (ksi->ksi_flags & KSI_INS)) ksiginfo_tryfree(ksi); return (ret); } sigqueue_delete_proc(p, SIGCONT); p->p_flag &= ~P_CONTINUED; } ret = sigqueue_add(sigqueue, sig, ksi); if (ret != 0) return (ret); signotify(td); /* * Defer further processing for signals which are held, * except that stopped processes must be continued by SIGCONT. */ if (action == SIG_HOLD && !((prop & SA_CONT) && (p->p_flag & P_STOPPED_SIG))) return (ret); /* * SIGKILL: Remove procfs STOPEVENTs. */ if (sig == SIGKILL) { /* from procfs_ioctl.c: PIOCBIC */ p->p_stops = 0; /* from procfs_ioctl.c: PIOCCONT */ p->p_step = 0; wakeup(&p->p_step); } /* * Some signals have a process-wide effect and a per-thread * component. Most processing occurs when the process next * tries to cross the user boundary, however there are some * times when processing needs to be done immediatly, such as * waking up threads so that they can cross the user boundary. * We try do the per-process part here. */ if (P_SHOULDSTOP(p)) { /* * The process is in stopped mode. All the threads should be * either winding down or already on the suspended queue. */ if (p->p_flag & P_TRACED) { /* * The traced process is already stopped, * so no further action is necessary. * No signal can restart us. */ goto out; } if (sig == SIGKILL) { /* * SIGKILL sets process running. * It will die elsewhere. * All threads must be restarted. */ p->p_flag &= ~P_STOPPED_SIG; goto runfast; } if (prop & SA_CONT) { /* * If SIGCONT is default (or ignored), we continue the * process but don't leave the signal in sigqueue as * it has no further action. If SIGCONT is held, we * continue the process and leave the signal in * sigqueue. If the process catches SIGCONT, let it * handle the signal itself. If it isn't waiting on * an event, it goes back to run state. * Otherwise, process goes back to sleep state. */ p->p_flag &= ~P_STOPPED_SIG; p->p_flag |= P_CONTINUED; if (action == SIG_DFL) { sigqueue_delete(sigqueue, sig); } else if (action == SIG_CATCH) { /* * The process wants to catch it so it needs * to run at least one thread, but which one? * It would seem that the answer would be to * run an upcall in the next KSE to run, and * deliver the signal that way. In a NON KSE * process, we need to make sure that the * single thread is runnable asap. * XXXKSE for now however, make them all run. */ goto runfast; } /* * The signal is not ignored or caught. */ mtx_lock_spin(&sched_lock); thread_unsuspend(p); mtx_unlock_spin(&sched_lock); goto out; } if (prop & SA_STOP) { /* * Already stopped, don't need to stop again * (If we did the shell could get confused). * Just make sure the signal STOP bit set. */ p->p_flag |= P_STOPPED_SIG; sigqueue_delete(sigqueue, sig); goto out; } /* * All other kinds of signals: * If a thread is sleeping interruptibly, simulate a * wakeup so that when it is continued it will be made * runnable and can look at the signal. However, don't make * the PROCESS runnable, leave it stopped. * It may run a bit until it hits a thread_suspend_check(). */ mtx_lock_spin(&sched_lock); if (TD_ON_SLEEPQ(td) && (td->td_flags & TDF_SINTR)) sleepq_abort(td); mtx_unlock_spin(&sched_lock); goto out; /* * Mutexes are short lived. Threads waiting on them will * hit thread_suspend_check() soon. */ } else if (p->p_state == PRS_NORMAL) { if (p->p_flag & P_TRACED || action == SIG_CATCH) { mtx_lock_spin(&sched_lock); tdsigwakeup(td, sig, action); mtx_unlock_spin(&sched_lock); goto out; } MPASS(action == SIG_DFL); if (prop & SA_STOP) { if (p->p_flag & P_PPWAIT) goto out; p->p_flag |= P_STOPPED_SIG; p->p_xstat = sig; p->p_xthread = td; mtx_lock_spin(&sched_lock); FOREACH_THREAD_IN_PROC(p, td0) { if (TD_IS_SLEEPING(td0) && (td0->td_flags & TDF_SINTR) && !TD_IS_SUSPENDED(td0)) { thread_suspend_one(td0); } else if (td != td0) { td0->td_flags |= TDF_ASTPENDING; } } thread_stopped(p); if (p->p_numthreads == p->p_suspcount) { mtx_unlock_spin(&sched_lock); sigqueue_delete_proc(p, p->p_xstat); } else mtx_unlock_spin(&sched_lock); goto out; } else goto runfast; /* NOTREACHED */ } else { /* Not in "NORMAL" state. discard the signal. */ sigqueue_delete(sigqueue, sig); goto out; } /* * The process is not stopped so we need to apply the signal to all the * running threads. */ runfast: mtx_lock_spin(&sched_lock); tdsigwakeup(td, sig, action); thread_unsuspend(p); mtx_unlock_spin(&sched_lock); out: /* If we jump here, sched_lock should not be owned. */ mtx_assert(&sched_lock, MA_NOTOWNED); return (ret); } /* * The force of a signal has been directed against a single * thread. We need to see what we can do about knocking it * out of any sleep it may be in etc. */ static void tdsigwakeup(struct thread *td, int sig, sig_t action) { struct proc *p = td->td_proc; register int prop; PROC_LOCK_ASSERT(p, MA_OWNED); mtx_assert(&sched_lock, MA_OWNED); prop = sigprop(sig); /* * Bring the priority of a thread up if we want it to get * killed in this lifetime. */ if (action == SIG_DFL && (prop & SA_KILL)) { if (p->p_nice > 0) sched_nice(td->td_proc, 0); if (td->td_priority > PUSER) sched_prio(td, PUSER); } if (TD_ON_SLEEPQ(td)) { /* * If thread is sleeping uninterruptibly * we can't interrupt the sleep... the signal will * be noticed when the process returns through * trap() or syscall(). */ if ((td->td_flags & TDF_SINTR) == 0) return; /* * If SIGCONT is default (or ignored) and process is * asleep, we are finished; the process should not * be awakened. */ if ((prop & SA_CONT) && action == SIG_DFL) { mtx_unlock_spin(&sched_lock); sigqueue_delete(&p->p_sigqueue, sig); /* * It may be on either list in this state. * Remove from both for now. */ sigqueue_delete(&td->td_sigqueue, sig); mtx_lock_spin(&sched_lock); return; } /* * Give low priority threads a better chance to run. */ if (td->td_priority > PUSER) sched_prio(td, PUSER); sleepq_abort(td); } else { /* * Other states do nothing with the signal immediately, * other than kicking ourselves if we are running. * It will either never be noticed, or noticed very soon. */ #ifdef SMP if (TD_IS_RUNNING(td) && td != curthread) forward_signal(td); #endif } } int ptracestop(struct thread *td, int sig) { struct proc *p = td->td_proc; struct thread *td0; PROC_LOCK_ASSERT(p, MA_OWNED); WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, &p->p_mtx.mtx_object, "Stopping for traced signal"); mtx_lock_spin(&sched_lock); td->td_flags |= TDF_XSIG; mtx_unlock_spin(&sched_lock); td->td_xsig = sig; while ((p->p_flag & P_TRACED) && (td->td_flags & TDF_XSIG)) { if (p->p_flag & P_SINGLE_EXIT) { mtx_lock_spin(&sched_lock); td->td_flags &= ~TDF_XSIG; mtx_unlock_spin(&sched_lock); return (sig); } /* * Just make wait() to work, the last stopped thread * will win. */ p->p_xstat = sig; p->p_xthread = td; p->p_flag |= (P_STOPPED_SIG|P_STOPPED_TRACE); mtx_lock_spin(&sched_lock); FOREACH_THREAD_IN_PROC(p, td0) { if (TD_IS_SLEEPING(td0) && (td0->td_flags & TDF_SINTR) && !TD_IS_SUSPENDED(td0)) { thread_suspend_one(td0); } else if (td != td0) { td0->td_flags |= TDF_ASTPENDING; } } stopme: thread_stopped(p); thread_suspend_one(td); PROC_UNLOCK(p); DROP_GIANT(); mi_switch(SW_VOL, NULL); mtx_unlock_spin(&sched_lock); PICKUP_GIANT(); PROC_LOCK(p); if (!(p->p_flag & P_TRACED)) break; if (td->td_flags & TDF_DBSUSPEND) { if (p->p_flag & P_SINGLE_EXIT) break; mtx_lock_spin(&sched_lock); goto stopme; } } return (td->td_xsig); } /* * If the current process has received a signal (should be caught or cause * termination, should interrupt current syscall), return the signal number. * Stop signals with default action are processed immediately, then cleared; * they aren't returned. This is checked after each entry to the system for * a syscall or trap (though this can usually be done without calling issignal * by checking the pending signal masks in cursig.) The normal call * sequence is * * while (sig = cursig(curthread)) * postsig(sig); */ static int issignal(td) struct thread *td; { struct proc *p; struct sigacts *ps; sigset_t sigpending; int sig, prop, newsig; struct thread *td0; p = td->td_proc; ps = p->p_sigacts; mtx_assert(&ps->ps_mtx, MA_OWNED); PROC_LOCK_ASSERT(p, MA_OWNED); for (;;) { int traced = (p->p_flag & P_TRACED) || (p->p_stops & S_SIG); sigpending = td->td_sigqueue.sq_signals; SIGSETNAND(sigpending, td->td_sigmask); if (p->p_flag & P_PPWAIT) SIG_STOPSIGMASK(sigpending); if (SIGISEMPTY(sigpending)) /* no signal to send */ return (0); sig = sig_ffs(&sigpending); if (p->p_stops & S_SIG) { mtx_unlock(&ps->ps_mtx); stopevent(p, S_SIG, sig); mtx_lock(&ps->ps_mtx); } /* * We should see pending but ignored signals * only if P_TRACED was on when they were posted. */ if (SIGISMEMBER(ps->ps_sigignore, sig) && (traced == 0)) { sigqueue_delete(&td->td_sigqueue, sig); if (td->td_pflags & TDP_SA) SIGADDSET(td->td_sigmask, sig); continue; } if (p->p_flag & P_TRACED && (p->p_flag & P_PPWAIT) == 0) { /* * If traced, always stop. */ mtx_unlock(&ps->ps_mtx); newsig = ptracestop(td, sig); mtx_lock(&ps->ps_mtx); if (td->td_pflags & TDP_SA) SIGADDSET(td->td_sigmask, sig); if (sig != newsig) { /* * clear old signal. * XXX shrug off debugger, it causes siginfo to * be thrown away. */ sigqueue_delete(&td->td_sigqueue, sig); /* * If parent wants us to take the signal, * then it will leave it in p->p_xstat; * otherwise we just look for signals again. */ if (newsig == 0) continue; sig = newsig; /* * Put the new signal into td_sigqueue. If the * signal is being masked, look for other signals. */ SIGADDSET(td->td_sigqueue.sq_signals, sig); if (td->td_pflags & TDP_SA) SIGDELSET(td->td_sigmask, sig); if (SIGISMEMBER(td->td_sigmask, sig)) continue; signotify(td); } /* * If the traced bit got turned off, go back up * to the top to rescan signals. This ensures * that p_sig* and p_sigact are consistent. */ if ((p->p_flag & P_TRACED) == 0) continue; } prop = sigprop(sig); /* * Decide whether the signal should be returned. * Return the signal's number, or fall through * to clear it from the pending mask. */ switch ((intptr_t)p->p_sigacts->ps_sigact[_SIG_IDX(sig)]) { case (intptr_t)SIG_DFL: /* * Don't take default actions on system processes. */ if (p->p_pid <= 1) { #ifdef DIAGNOSTIC /* * Are you sure you want to ignore SIGSEGV * in init? XXX */ printf("Process (pid %lu) got signal %d\n", (u_long)p->p_pid, sig); #endif break; /* == ignore */ } /* * If there is a pending stop signal to process * with default action, stop here, * then clear the signal. However, * if process is member of an orphaned * process group, ignore tty stop signals. */ if (prop & SA_STOP) { if (p->p_flag & P_TRACED || (p->p_pgrp->pg_jobc == 0 && prop & SA_TTYSTOP)) break; /* == ignore */ mtx_unlock(&ps->ps_mtx); WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, &p->p_mtx.mtx_object, "Catching SIGSTOP"); p->p_flag |= P_STOPPED_SIG; p->p_xstat = sig; p->p_xthread = td; mtx_lock_spin(&sched_lock); FOREACH_THREAD_IN_PROC(p, td0) { if (TD_IS_SLEEPING(td0) && (td0->td_flags & TDF_SINTR) && !TD_IS_SUSPENDED(td0)) { thread_suspend_one(td0); } else if (td != td0) { td0->td_flags |= TDF_ASTPENDING; } } thread_stopped(p); thread_suspend_one(td); PROC_UNLOCK(p); DROP_GIANT(); mi_switch(SW_INVOL, NULL); mtx_unlock_spin(&sched_lock); PICKUP_GIANT(); PROC_LOCK(p); mtx_lock(&ps->ps_mtx); break; } else if (prop & SA_IGNORE) { /* * Except for SIGCONT, shouldn't get here. * Default action is to ignore; drop it. */ break; /* == ignore */ } else return (sig); /*NOTREACHED*/ case (intptr_t)SIG_IGN: /* * Masking above should prevent us ever trying * to take action on an ignored signal other * than SIGCONT, unless process is traced. */ if ((prop & SA_CONT) == 0 && (p->p_flag & P_TRACED) == 0) printf("issignal\n"); break; /* == ignore */ default: /* * This signal has an action, let * postsig() process it. */ return (sig); } sigqueue_delete(&td->td_sigqueue, sig); /* take the signal! */ } /* NOTREACHED */ } /* * MPSAFE */ void thread_stopped(struct proc *p) { struct proc *p1 = curthread->td_proc; struct sigacts *ps; int n; PROC_LOCK_ASSERT(p, MA_OWNED); mtx_assert(&sched_lock, MA_OWNED); n = p->p_suspcount; if (p == p1) n++; if ((p->p_flag & P_STOPPED_SIG) && (n == p->p_numthreads)) { mtx_unlock_spin(&sched_lock); p->p_flag &= ~P_WAITED; PROC_LOCK(p->p_pptr); /* * Wake up parent sleeping in kern_wait(), also send * SIGCHLD to parent, but SIGCHLD does not guarantee * that parent will awake, because parent may masked * the signal. */ p->p_pptr->p_flag |= P_STATCHILD; wakeup(p->p_pptr); ps = p->p_pptr->p_sigacts; mtx_lock(&ps->ps_mtx); if ((ps->ps_flag & PS_NOCLDSTOP) == 0) { mtx_unlock(&ps->ps_mtx); psignal(p->p_pptr, SIGCHLD); } else mtx_unlock(&ps->ps_mtx); PROC_UNLOCK(p->p_pptr); mtx_lock_spin(&sched_lock); } } /* * Take the action for the specified signal * from the current set of pending signals. */ void postsig(sig) register int sig; { struct thread *td = curthread; register struct proc *p = td->td_proc; struct sigacts *ps; sig_t action; ksiginfo_t ksi; sigset_t returnmask; int code; KASSERT(sig != 0, ("postsig")); PROC_LOCK_ASSERT(p, MA_OWNED); ps = p->p_sigacts; mtx_assert(&ps->ps_mtx, MA_OWNED); ksiginfo_init(&ksi); sigqueue_get(&td->td_sigqueue, sig, &ksi); ksi.ksi_signo = sig; if (ksi.ksi_code == SI_TIMER) itimer_accept(p, ksi.ksi_timerid, &ksi); action = ps->ps_sigact[_SIG_IDX(sig)]; #ifdef KTRACE if (KTRPOINT(td, KTR_PSIG)) ktrpsig(sig, action, td->td_pflags & TDP_OLDMASK ? &td->td_oldsigmask : &td->td_sigmask, 0); #endif if (p->p_stops & S_SIG) { mtx_unlock(&ps->ps_mtx); stopevent(p, S_SIG, sig); mtx_lock(&ps->ps_mtx); } if (!(td->td_pflags & TDP_SA) && action == SIG_DFL) { /* * Default action, where the default is to kill * the process. (Other cases were ignored above.) */ mtx_unlock(&ps->ps_mtx); sigexit(td, sig); /* NOTREACHED */ } else { if (td->td_pflags & TDP_SA) { if (sig == SIGKILL) { mtx_unlock(&ps->ps_mtx); sigexit(td, sig); } } /* * If we get here, the signal must be caught. */ KASSERT(action != SIG_IGN && !SIGISMEMBER(td->td_sigmask, sig), ("postsig action")); /* * Set the new mask value and also defer further * occurrences of this signal. * * Special case: user has done a sigsuspend. Here the * current mask is not of interest, but rather the * mask from before the sigsuspend is what we want * restored after the signal processing is completed. */ if (td->td_pflags & TDP_OLDMASK) { returnmask = td->td_oldsigmask; td->td_pflags &= ~TDP_OLDMASK; } else returnmask = td->td_sigmask; SIGSETOR(td->td_sigmask, ps->ps_catchmask[_SIG_IDX(sig)]); if (!SIGISMEMBER(ps->ps_signodefer, sig)) SIGADDSET(td->td_sigmask, sig); if (SIGISMEMBER(ps->ps_sigreset, sig)) { /* * See kern_sigaction() for origin of this code. */ SIGDELSET(ps->ps_sigcatch, sig); if (sig != SIGCONT && sigprop(sig) & SA_IGNORE) SIGADDSET(ps->ps_sigignore, sig); ps->ps_sigact[_SIG_IDX(sig)] = SIG_DFL; } p->p_stats->p_ru.ru_nsignals++; if (p->p_sig != sig) { code = 0; } else { code = p->p_code; p->p_code = 0; p->p_sig = 0; } if (td->td_pflags & TDP_SA) thread_signal_add(curthread, &ksi); else (*p->p_sysent->sv_sendsig)(action, &ksi, &returnmask); } } /* * Kill the current process for stated reason. */ void killproc(p, why) struct proc *p; char *why; { PROC_LOCK_ASSERT(p, MA_OWNED); CTR3(KTR_PROC, "killproc: proc %p (pid %d, %s)", p, p->p_pid, p->p_comm); log(LOG_ERR, "pid %d (%s), uid %d, was killed: %s\n", p->p_pid, p->p_comm, p->p_ucred ? p->p_ucred->cr_uid : -1, why); psignal(p, SIGKILL); } /* * Force the current process to exit with the specified signal, dumping core * if appropriate. We bypass the normal tests for masked and caught signals, * allowing unrecoverable failures to terminate the process without changing * signal state. Mark the accounting record with the signal termination. * If dumping core, save the signal number for the debugger. Calls exit and * does not return. * * MPSAFE */ void sigexit(td, sig) struct thread *td; int sig; { struct proc *p = td->td_proc; PROC_LOCK_ASSERT(p, MA_OWNED); p->p_acflag |= AXSIG; /* * We must be single-threading to generate a core dump. This * ensures that the registers in the core file are up-to-date. * Also, the ELF dump handler assumes that the thread list doesn't * change out from under it. * * XXX If another thread attempts to single-thread before us * (e.g. via fork()), we won't get a dump at all. */ if ((sigprop(sig) & SA_CORE) && (thread_single(SINGLE_NO_EXIT) == 0)) { p->p_sig = sig; /* * Log signals which would cause core dumps * (Log as LOG_INFO to appease those who don't want * these messages.) * XXX : Todo, as well as euid, write out ruid too * Note that coredump() drops proc lock. */ if (coredump(td) == 0) sig |= WCOREFLAG; if (kern_logsigexit) log(LOG_INFO, "pid %d (%s), uid %d: exited on signal %d%s\n", p->p_pid, p->p_comm, td->td_ucred ? td->td_ucred->cr_uid : -1, sig &~ WCOREFLAG, sig & WCOREFLAG ? " (core dumped)" : ""); } else PROC_UNLOCK(p); exit1(td, W_EXITCODE(0, sig)); /* NOTREACHED */ } static char corefilename[MAXPATHLEN] = {"%N.core"}; SYSCTL_STRING(_kern, OID_AUTO, corefile, CTLFLAG_RW, corefilename, sizeof(corefilename), "process corefile name format string"); /* * expand_name(name, uid, pid) * Expand the name described in corefilename, using name, uid, and pid. * corefilename is a printf-like string, with three format specifiers: * %N name of process ("name") * %P process id (pid) * %U user id (uid) * For example, "%N.core" is the default; they can be disabled completely * by using "/dev/null", or all core files can be stored in "/cores/%U/%N-%P". * This is controlled by the sysctl variable kern.corefile (see above). */ static char * expand_name(name, uid, pid) const char *name; uid_t uid; pid_t pid; { const char *format, *appendstr; char *temp; char buf[11]; /* Buffer for pid/uid -- max 4B */ size_t i, l, n; format = corefilename; temp = malloc(MAXPATHLEN, M_TEMP, M_NOWAIT | M_ZERO); if (temp == NULL) return (NULL); for (i = 0, n = 0; n < MAXPATHLEN && format[i]; i++) { switch (format[i]) { case '%': /* Format character */ i++; switch (format[i]) { case '%': appendstr = "%"; break; case 'N': /* process name */ appendstr = name; break; case 'P': /* process id */ sprintf(buf, "%u", pid); appendstr = buf; break; case 'U': /* user id */ sprintf(buf, "%u", uid); appendstr = buf; break; default: appendstr = ""; log(LOG_ERR, "Unknown format character %c in `%s'\n", format[i], format); } l = strlen(appendstr); if ((n + l) >= MAXPATHLEN) goto toolong; memcpy(temp + n, appendstr, l); n += l; break; default: temp[n++] = format[i]; } } if (format[i] != '\0') goto toolong; return (temp); toolong: log(LOG_ERR, "pid %ld (%s), uid (%lu): corename is too long\n", (long)pid, name, (u_long)uid); free(temp, M_TEMP); return (NULL); } /* * Dump a process' core. The main routine does some * policy checking, and creates the name of the coredump; * then it passes on a vnode and a size limit to the process-specific * coredump routine if there is one; if there _is not_ one, it returns * ENOSYS; otherwise it returns the error from the process-specific routine. */ static int coredump(struct thread *td) { struct proc *p = td->td_proc; register struct vnode *vp; register struct ucred *cred = td->td_ucred; struct flock lf; struct nameidata nd; struct vattr vattr; int error, error1, flags, locked; struct mount *mp; char *name; /* name of corefile */ off_t limit; PROC_LOCK_ASSERT(p, MA_OWNED); MPASS((p->p_flag & P_HADTHREADS) == 0 || p->p_singlethread == td); _STOPEVENT(p, S_CORE, 0); if (((sugid_coredump == 0) && p->p_flag & P_SUGID) || do_coredump == 0) { PROC_UNLOCK(p); return (EFAULT); } /* * Note that the bulk of limit checking is done after * the corefile is created. The exception is if the limit * for corefiles is 0, in which case we don't bother * creating the corefile at all. This layout means that * a corefile is truncated instead of not being created, * if it is larger than the limit. */ limit = (off_t)lim_cur(p, RLIMIT_CORE); PROC_UNLOCK(p); if (limit == 0) return (EFBIG); mtx_lock(&Giant); restart: name = expand_name(p->p_comm, td->td_ucred->cr_uid, p->p_pid); if (name == NULL) { mtx_unlock(&Giant); return (EINVAL); } NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_SYSSPACE, name, td); /* XXXKSE */ flags = O_CREAT | FWRITE | O_NOFOLLOW; error = vn_open(&nd, &flags, S_IRUSR | S_IWUSR, -1); free(name, M_TEMP); if (error) { mtx_unlock(&Giant); return (error); } NDFREE(&nd, NDF_ONLY_PNBUF); vp = nd.ni_vp; /* Don't dump to non-regular files or files with links. */ if (vp->v_type != VREG || VOP_GETATTR(vp, &vattr, cred, td) || vattr.va_nlink != 1) { VOP_UNLOCK(vp, 0, td); error = EFAULT; goto out; } VOP_UNLOCK(vp, 0, td); lf.l_whence = SEEK_SET; lf.l_start = 0; lf.l_len = 0; lf.l_type = F_WRLCK; locked = (VOP_ADVLOCK(vp, (caddr_t)p, F_SETLK, &lf, F_FLOCK) == 0); if (vn_start_write(vp, &mp, V_NOWAIT) != 0) { lf.l_type = F_UNLCK; if (locked) VOP_ADVLOCK(vp, (caddr_t)p, F_UNLCK, &lf, F_FLOCK); if ((error = vn_close(vp, FWRITE, cred, td)) != 0) return (error); if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0) return (error); goto restart; } VATTR_NULL(&vattr); vattr.va_size = 0; if (set_core_nodump_flag) vattr.va_flags = UF_NODUMP; vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); VOP_LEASE(vp, td, cred, LEASE_WRITE); VOP_SETATTR(vp, &vattr, cred, td); VOP_UNLOCK(vp, 0, td); PROC_LOCK(p); p->p_acflag |= ACORE; PROC_UNLOCK(p); error = p->p_sysent->sv_coredump ? p->p_sysent->sv_coredump(td, vp, limit) : ENOSYS; if (locked) { lf.l_type = F_UNLCK; VOP_ADVLOCK(vp, (caddr_t)p, F_UNLCK, &lf, F_FLOCK); } vn_finished_write(mp); out: error1 = vn_close(vp, FWRITE, cred, td); mtx_unlock(&Giant); if (error == 0) error = error1; return (error); } /* * Nonexistent system call-- signal process (may want to handle it). * Flag error in case process won't see signal immediately (blocked or ignored). */ #ifndef _SYS_SYSPROTO_H_ struct nosys_args { int dummy; }; #endif /* * MPSAFE */ /* ARGSUSED */ int nosys(td, args) struct thread *td; struct nosys_args *args; { struct proc *p = td->td_proc; PROC_LOCK(p); psignal(p, SIGSYS); PROC_UNLOCK(p); return (ENOSYS); } /* * Send a SIGIO or SIGURG signal to a process or process group using * stored credentials rather than those of the current process. */ void pgsigio(sigiop, sig, checkctty) struct sigio **sigiop; int sig, checkctty; { struct sigio *sigio; SIGIO_LOCK(); sigio = *sigiop; if (sigio == NULL) { SIGIO_UNLOCK(); return; } if (sigio->sio_pgid > 0) { PROC_LOCK(sigio->sio_proc); if (CANSIGIO(sigio->sio_ucred, sigio->sio_proc->p_ucred)) psignal(sigio->sio_proc, sig); PROC_UNLOCK(sigio->sio_proc); } else if (sigio->sio_pgid < 0) { struct proc *p; PGRP_LOCK(sigio->sio_pgrp); LIST_FOREACH(p, &sigio->sio_pgrp->pg_members, p_pglist) { PROC_LOCK(p); if (CANSIGIO(sigio->sio_ucred, p->p_ucred) && (checkctty == 0 || (p->p_flag & P_CONTROLT))) psignal(p, sig); PROC_UNLOCK(p); } PGRP_UNLOCK(sigio->sio_pgrp); } SIGIO_UNLOCK(); } static int filt_sigattach(struct knote *kn) { struct proc *p = curproc; kn->kn_ptr.p_proc = p; kn->kn_flags |= EV_CLEAR; /* automatically set */ knlist_add(&p->p_klist, kn, 0); return (0); } static void filt_sigdetach(struct knote *kn) { struct proc *p = kn->kn_ptr.p_proc; knlist_remove(&p->p_klist, kn, 0); } /* * signal knotes are shared with proc knotes, so we apply a mask to * the hint in order to differentiate them from process hints. This * could be avoided by using a signal-specific knote list, but probably * isn't worth the trouble. */ static int filt_signal(struct knote *kn, long hint) { if (hint & NOTE_SIGNAL) { hint &= ~NOTE_SIGNAL; if (kn->kn_id == hint) kn->kn_data++; } return (kn->kn_data != 0); } struct sigacts * sigacts_alloc(void) { struct sigacts *ps; ps = malloc(sizeof(struct sigacts), M_SUBPROC, M_WAITOK | M_ZERO); ps->ps_refcnt = 1; mtx_init(&ps->ps_mtx, "sigacts", NULL, MTX_DEF); return (ps); } void sigacts_free(struct sigacts *ps) { mtx_lock(&ps->ps_mtx); ps->ps_refcnt--; if (ps->ps_refcnt == 0) { mtx_destroy(&ps->ps_mtx); free(ps, M_SUBPROC); } else mtx_unlock(&ps->ps_mtx); } struct sigacts * sigacts_hold(struct sigacts *ps) { mtx_lock(&ps->ps_mtx); ps->ps_refcnt++; mtx_unlock(&ps->ps_mtx); return (ps); } void sigacts_copy(struct sigacts *dest, struct sigacts *src) { KASSERT(dest->ps_refcnt == 1, ("sigacts_copy to shared dest")); mtx_lock(&src->ps_mtx); bcopy(src, dest, offsetof(struct sigacts, ps_refcnt)); mtx_unlock(&src->ps_mtx); } int sigacts_shared(struct sigacts *ps) { int shared; mtx_lock(&ps->ps_mtx); shared = ps->ps_refcnt > 1; mtx_unlock(&ps->ps_mtx); return (shared); } Index: head/sys/kern/kern_time.c =================================================================== --- head/sys/kern/kern_time.c (revision 152028) +++ head/sys/kern/kern_time.c (revision 152029) @@ -1,1508 +1,1508 @@ /*- * Copyright (c) 1982, 1986, 1989, 1993 * The Regents of the University of California. 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. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. * * @(#)kern_time.c 8.1 (Berkeley) 6/10/93 */ #include __FBSDID("$FreeBSD$"); #include "opt_mac.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MAX_CLOCKS (CLOCK_MONOTONIC+1) int tz_minuteswest; int tz_dsttime; static struct kclock posix_clocks[MAX_CLOCKS]; static uma_zone_t itimer_zone = NULL; /* * Time of day and interval timer support. * * These routines provide the kernel entry points to get and set * the time-of-day and per-process interval timers. Subroutines * here provide support for adding and subtracting timeval structures * and decrementing interval timers, optionally reloading the interval * timers when they expire. */ static int settime(struct thread *, struct timeval *); static void timevalfix(struct timeval *); static void no_lease_updatetime(int); static void itimer_start(void); static int itimer_init(void *, int, int); static void itimer_fini(void *, int); static void itimer_enter(struct itimer *); static void itimer_leave(struct itimer *); static struct itimer *itimer_find(struct proc *, timer_t, int); static void itimers_alloc(struct proc *); static int realtimer_create(struct itimer *); static int realtimer_gettime(struct itimer *, struct itimerspec *); static int realtimer_settime(struct itimer *, int, struct itimerspec *, struct itimerspec *); static int realtimer_delete(struct itimer *); static void realtimer_clocktime(clockid_t, struct timespec *); static void realtimer_expire(void *); static void realtimer_event_hook(struct proc *, clockid_t, int event); static int kern_timer_create(struct thread *, clockid_t, struct sigevent *, timer_t *, timer_t); static int kern_timer_delete(struct thread *, timer_t); int register_posix_clock(int, struct kclock *); void itimer_fire(struct itimer *it); int itimespecfix(struct timespec *ts); #define CLOCK_CALL(clock, call, arglist) \ ((*posix_clocks[clock].call) arglist) SYSINIT(posix_timer, SI_SUB_P1003_1B, SI_ORDER_FIRST+4, itimer_start, NULL); static void no_lease_updatetime(deltat) int deltat; { } void (*lease_updatetime)(int) = no_lease_updatetime; static int settime(struct thread *td, struct timeval *tv) { struct timeval delta, tv1, tv2; static struct timeval maxtime, laststep; struct timespec ts; int s; s = splclock(); microtime(&tv1); delta = *tv; timevalsub(&delta, &tv1); /* * If the system is secure, we do not allow the time to be * set to a value earlier than 1 second less than the highest * time we have yet seen. The worst a miscreant can do in * this circumstance is "freeze" time. He couldn't go * back to the past. * * We similarly do not allow the clock to be stepped more * than one second, nor more than once per second. This allows * a miscreant to make the clock march double-time, but no worse. */ if (securelevel_gt(td->td_ucred, 1) != 0) { if (delta.tv_sec < 0 || delta.tv_usec < 0) { /* * Update maxtime to latest time we've seen. */ if (tv1.tv_sec > maxtime.tv_sec) maxtime = tv1; tv2 = *tv; timevalsub(&tv2, &maxtime); if (tv2.tv_sec < -1) { tv->tv_sec = maxtime.tv_sec - 1; printf("Time adjustment clamped to -1 second\n"); } } else { if (tv1.tv_sec == laststep.tv_sec) { splx(s); return (EPERM); } if (delta.tv_sec > 1) { tv->tv_sec = tv1.tv_sec + 1; printf("Time adjustment clamped to +1 second\n"); } laststep = *tv; } } ts.tv_sec = tv->tv_sec; ts.tv_nsec = tv->tv_usec * 1000; mtx_lock(&Giant); tc_setclock(&ts); (void) splsoftclock(); lease_updatetime(delta.tv_sec); splx(s); resettodr(); mtx_unlock(&Giant); return (0); } #ifndef _SYS_SYSPROTO_H_ struct clock_gettime_args { clockid_t clock_id; struct timespec *tp; }; #endif /* * MPSAFE */ /* ARGSUSED */ int clock_gettime(struct thread *td, struct clock_gettime_args *uap) { struct timespec ats; int error; error = kern_clock_gettime(td, uap->clock_id, &ats); if (error == 0) error = copyout(&ats, uap->tp, sizeof(ats)); return (error); } int kern_clock_gettime(struct thread *td, clockid_t clock_id, struct timespec *ats) { struct timeval sys, user; struct proc *p; p = td->td_proc; switch (clock_id) { case CLOCK_REALTIME: nanotime(ats); break; case CLOCK_VIRTUAL: PROC_LOCK(p); calcru(p, &user, &sys); PROC_UNLOCK(p); TIMEVAL_TO_TIMESPEC(&user, ats); break; case CLOCK_PROF: PROC_LOCK(p); calcru(p, &user, &sys); PROC_UNLOCK(p); timevaladd(&user, &sys); TIMEVAL_TO_TIMESPEC(&user, ats); break; case CLOCK_MONOTONIC: nanouptime(ats); break; default: return (EINVAL); } return (0); } #ifndef _SYS_SYSPROTO_H_ struct clock_settime_args { clockid_t clock_id; const struct timespec *tp; }; #endif /* * MPSAFE */ /* ARGSUSED */ int clock_settime(struct thread *td, struct clock_settime_args *uap) { struct timespec ats; int error; if ((error = copyin(uap->tp, &ats, sizeof(ats))) != 0) return (error); return (kern_clock_settime(td, uap->clock_id, &ats)); } int kern_clock_settime(struct thread *td, clockid_t clock_id, struct timespec *ats) { struct timeval atv; int error; #ifdef MAC error = mac_check_system_settime(td->td_ucred); if (error) return (error); #endif if ((error = suser(td)) != 0) return (error); if (clock_id != CLOCK_REALTIME) return (EINVAL); if (ats->tv_nsec < 0 || ats->tv_nsec >= 1000000000) return (EINVAL); /* XXX Don't convert nsec->usec and back */ TIMESPEC_TO_TIMEVAL(&atv, ats); error = settime(td, &atv); return (error); } #ifndef _SYS_SYSPROTO_H_ struct clock_getres_args { clockid_t clock_id; struct timespec *tp; }; #endif int clock_getres(struct thread *td, struct clock_getres_args *uap) { struct timespec ts; int error; if (uap->tp == NULL) return (0); error = kern_clock_getres(td, uap->clock_id, &ts); if (error == 0) error = copyout(&ts, uap->tp, sizeof(ts)); return (error); } int kern_clock_getres(struct thread *td, clockid_t clock_id, struct timespec *ts) { ts->tv_sec = 0; switch (clock_id) { case CLOCK_REALTIME: case CLOCK_MONOTONIC: /* * Round up the result of the division cheaply by adding 1. * Rounding up is especially important if rounding down * would give 0. Perfect rounding is unimportant. */ ts->tv_nsec = 1000000000 / tc_getfrequency() + 1; break; case CLOCK_VIRTUAL: case CLOCK_PROF: /* Accurately round up here because we can do so cheaply. */ ts->tv_nsec = (1000000000 + hz - 1) / hz; break; default: return (EINVAL); } return (0); } static int nanowait; int kern_nanosleep(struct thread *td, struct timespec *rqt, struct timespec *rmt) { struct timespec ts, ts2, ts3; struct timeval tv; int error; if (rqt->tv_nsec < 0 || rqt->tv_nsec >= 1000000000) return (EINVAL); if (rqt->tv_sec < 0 || (rqt->tv_sec == 0 && rqt->tv_nsec == 0)) return (0); getnanouptime(&ts); timespecadd(&ts, rqt); TIMESPEC_TO_TIMEVAL(&tv, rqt); for (;;) { error = tsleep(&nanowait, PWAIT | PCATCH, "nanslp", tvtohz(&tv)); getnanouptime(&ts2); if (error != EWOULDBLOCK) { if (error == ERESTART) error = EINTR; if (rmt != NULL) { timespecsub(&ts, &ts2); if (ts.tv_sec < 0) timespecclear(&ts); *rmt = ts; } return (error); } if (timespeccmp(&ts2, &ts, >=)) return (0); ts3 = ts; timespecsub(&ts3, &ts2); TIMESPEC_TO_TIMEVAL(&tv, &ts3); } } #ifndef _SYS_SYSPROTO_H_ struct nanosleep_args { struct timespec *rqtp; struct timespec *rmtp; }; #endif /* * MPSAFE */ /* ARGSUSED */ int nanosleep(struct thread *td, struct nanosleep_args *uap) { struct timespec rmt, rqt; int error; error = copyin(uap->rqtp, &rqt, sizeof(rqt)); if (error) return (error); 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; error2 = copyout(&rmt, uap->rmtp, sizeof(rmt)); if (error2) error = error2; } return (error); } #ifndef _SYS_SYSPROTO_H_ struct gettimeofday_args { struct timeval *tp; struct timezone *tzp; }; #endif /* * MPSAFE */ /* ARGSUSED */ int gettimeofday(struct thread *td, struct gettimeofday_args *uap) { struct timeval atv; struct timezone rtz; int error = 0; if (uap->tp) { microtime(&atv); error = copyout(&atv, uap->tp, sizeof (atv)); } 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); } #ifndef _SYS_SYSPROTO_H_ struct settimeofday_args { struct timeval *tv; struct timezone *tzp; }; #endif /* * MPSAFE */ /* ARGSUSED */ int settimeofday(struct thread *td, struct settimeofday_args *uap) { struct timeval atv, *tvp; struct timezone atz, *tzp; int error; if (uap->tv) { error = copyin(uap->tv, &atv, sizeof(atv)); if (error) return (error); tvp = &atv; } else tvp = NULL; if (uap->tzp) { error = copyin(uap->tzp, &atz, sizeof(atz)); if (error) return (error); tzp = &atz; } else tzp = NULL; return (kern_settimeofday(td, tvp, tzp)); } int kern_settimeofday(struct thread *td, struct timeval *tv, struct timezone *tzp) { int error; #ifdef MAC error = mac_check_system_settime(td->td_ucred); if (error) return (error); #endif error = suser(td); if (error) return (error); /* Verify all parameters before changing time. */ if (tv) { if (tv->tv_usec < 0 || tv->tv_usec >= 1000000) return (EINVAL); error = settime(td, tv); } if (tzp && error == 0) { tz_minuteswest = tzp->tz_minuteswest; tz_dsttime = tzp->tz_dsttime; } return (error); } /* * Get value of an interval timer. The process virtual and * profiling virtual time timers are kept in the p_stats area, since * they can be swapped out. These are kept internally in the * way they are specified externally: in time until they expire. * * The real time interval timer is kept in the process table slot * for the process, and its value (it_value) is kept as an * absolute time rather than as a delta, so that it is easy to keep * periodic real-time signals from drifting. * * Virtual time timers are processed in the hardclock() routine of * kern_clock.c. The real time timer is processed by a timeout * routine, called from the softclock() routine. Since a callout * may be delayed in real time due to interrupt processing in the system, * it is possible for the real time timeout routine (realitexpire, given below), * to be delayed in real time past when it is supposed to occur. It * does not suffice, therefore, to reload the real timer .it_value from the * real time timers .it_interval. Rather, we compute the next time in * absolute time the timer should go off. */ #ifndef _SYS_SYSPROTO_H_ struct getitimer_args { u_int which; struct itimerval *itv; }; #endif /* * MPSAFE */ int getitimer(struct thread *td, struct getitimer_args *uap) { struct itimerval aitv; int error; error = kern_getitimer(td, uap->which, &aitv); if (error != 0) return (error); return (copyout(&aitv, uap->itv, sizeof (struct itimerval))); } int kern_getitimer(struct thread *td, u_int which, struct itimerval *aitv) { struct proc *p = td->td_proc; struct timeval ctv; if (which > ITIMER_PROF) return (EINVAL); if (which == ITIMER_REAL) { /* * Convert from absolute to relative time in .it_value * part of real time timer. If time for real time timer * has passed return 0, else return difference between * current time and time for the timer to go off. */ PROC_LOCK(p); *aitv = p->p_realtimer; PROC_UNLOCK(p); if (timevalisset(&aitv->it_value)) { getmicrouptime(&ctv); if (timevalcmp(&aitv->it_value, &ctv, <)) timevalclear(&aitv->it_value); else timevalsub(&aitv->it_value, &ctv); } } else { mtx_lock_spin(&sched_lock); *aitv = p->p_stats->p_timer[which]; mtx_unlock_spin(&sched_lock); } return (0); } #ifndef _SYS_SYSPROTO_H_ struct setitimer_args { u_int which; struct itimerval *itv, *oitv; }; #endif /* * MPSAFE */ int setitimer(struct thread *td, struct setitimer_args *uap) { struct itimerval aitv, oitv; int error; if (uap->itv == NULL) { uap->itv = uap->oitv; return (getitimer(td, (struct getitimer_args *)uap)); } if ((error = copyin(uap->itv, &aitv, sizeof(struct itimerval)))) return (error); error = kern_setitimer(td, uap->which, &aitv, &oitv); if (error != 0 || uap->oitv == NULL) return (error); return (copyout(&oitv, uap->oitv, sizeof(struct itimerval))); } int kern_setitimer(struct thread *td, u_int which, struct itimerval *aitv, struct itimerval *oitv) { struct proc *p = td->td_proc; struct timeval ctv; if (aitv == NULL) return (kern_getitimer(td, which, oitv)); if (which > ITIMER_PROF) return (EINVAL); if (itimerfix(&aitv->it_value)) return (EINVAL); if (!timevalisset(&aitv->it_value)) timevalclear(&aitv->it_interval); else if (itimerfix(&aitv->it_interval)) return (EINVAL); if (which == ITIMER_REAL) { PROC_LOCK(p); if (timevalisset(&p->p_realtimer.it_value)) callout_stop(&p->p_itcallout); getmicrouptime(&ctv); if (timevalisset(&aitv->it_value)) { callout_reset(&p->p_itcallout, tvtohz(&aitv->it_value), realitexpire, p); timevaladd(&aitv->it_value, &ctv); } *oitv = p->p_realtimer; p->p_realtimer = *aitv; PROC_UNLOCK(p); if (timevalisset(&oitv->it_value)) { if (timevalcmp(&oitv->it_value, &ctv, <)) timevalclear(&oitv->it_value); else timevalsub(&oitv->it_value, &ctv); } } else { mtx_lock_spin(&sched_lock); *oitv = p->p_stats->p_timer[which]; p->p_stats->p_timer[which] = *aitv; mtx_unlock_spin(&sched_lock); } return (0); } /* * Real interval timer expired: * send process whose timer expired an alarm signal. * If time is not set up to reload, then just return. * Else compute next time timer should go off which is > current time. * This is where delay in processing this timeout causes multiple * SIGALRM calls to be compressed into one. * tvtohz() always adds 1 to allow for the time until the next clock * interrupt being strictly less than 1 clock tick, but we don't want * that here since we want to appear to be in sync with the clock * interrupt even when we're delayed. */ void realitexpire(void *arg) { struct proc *p; struct timeval ctv, ntv; p = (struct proc *)arg; PROC_LOCK(p); psignal(p, SIGALRM); if (!timevalisset(&p->p_realtimer.it_interval)) { timevalclear(&p->p_realtimer.it_value); if (p->p_flag & P_WEXIT) wakeup(&p->p_itcallout); PROC_UNLOCK(p); return; } for (;;) { timevaladd(&p->p_realtimer.it_value, &p->p_realtimer.it_interval); getmicrouptime(&ctv); if (timevalcmp(&p->p_realtimer.it_value, &ctv, >)) { ntv = p->p_realtimer.it_value; timevalsub(&ntv, &ctv); callout_reset(&p->p_itcallout, tvtohz(&ntv) - 1, realitexpire, p); PROC_UNLOCK(p); return; } } /*NOTREACHED*/ } /* * Check that a proposed value to load into the .it_value or * .it_interval part of an interval timer is acceptable, and * fix it to have at least minimal value (i.e. if it is less * than the resolution of the clock, round it up.) */ int itimerfix(struct timeval *tv) { if (tv->tv_sec < 0 || tv->tv_usec < 0 || tv->tv_usec >= 1000000) return (EINVAL); if (tv->tv_sec == 0 && tv->tv_usec != 0 && tv->tv_usec < tick) tv->tv_usec = tick; return (0); } /* * Decrement an interval timer by a specified number * of microseconds, which must be less than a second, * i.e. < 1000000. If the timer expires, then reload * it. In this case, carry over (usec - old value) to * reduce the value reloaded into the timer so that * the timer does not drift. This routine assumes * that it is called in a context where the timers * on which it is operating cannot change in value. */ int itimerdecr(struct itimerval *itp, int usec) { if (itp->it_value.tv_usec < usec) { if (itp->it_value.tv_sec == 0) { /* expired, and already in next interval */ usec -= itp->it_value.tv_usec; goto expire; } itp->it_value.tv_usec += 1000000; itp->it_value.tv_sec--; } itp->it_value.tv_usec -= usec; usec = 0; if (timevalisset(&itp->it_value)) return (1); /* expired, exactly at end of interval */ expire: if (timevalisset(&itp->it_interval)) { itp->it_value = itp->it_interval; itp->it_value.tv_usec -= usec; if (itp->it_value.tv_usec < 0) { itp->it_value.tv_usec += 1000000; itp->it_value.tv_sec--; } } else itp->it_value.tv_usec = 0; /* sec is already 0 */ return (0); } /* * Add and subtract routines for timevals. * N.B.: subtract routine doesn't deal with * results which are before the beginning, * it just gets very confused in this case. * Caveat emptor. */ void timevaladd(struct timeval *t1, const struct timeval *t2) { t1->tv_sec += t2->tv_sec; t1->tv_usec += t2->tv_usec; timevalfix(t1); } void timevalsub(struct timeval *t1, const struct timeval *t2) { t1->tv_sec -= t2->tv_sec; t1->tv_usec -= t2->tv_usec; timevalfix(t1); } static void timevalfix(struct timeval *t1) { if (t1->tv_usec < 0) { t1->tv_sec--; t1->tv_usec += 1000000; } if (t1->tv_usec >= 1000000) { t1->tv_sec++; t1->tv_usec -= 1000000; } } /* * ratecheck(): simple time-based rate-limit checking. */ int ratecheck(struct timeval *lasttime, const struct timeval *mininterval) { struct timeval tv, delta; int rv = 0; getmicrouptime(&tv); /* NB: 10ms precision */ delta = tv; timevalsub(&delta, lasttime); /* * check for 0,0 is so that the message will be seen at least once, * even if interval is huge. */ if (timevalcmp(&delta, mininterval, >=) || (lasttime->tv_sec == 0 && lasttime->tv_usec == 0)) { *lasttime = tv; rv = 1; } return (rv); } /* * ppsratecheck(): packets (or events) per second limitation. * * Return 0 if the limit is to be enforced (e.g. the caller * should drop a packet because of the rate limitation). * * maxpps of 0 always causes zero to be returned. maxpps of -1 * always causes 1 to be returned; this effectively defeats rate * limiting. * * Note that we maintain the struct timeval for compatibility * with other bsd systems. We reuse the storage and just monitor * clock ticks for minimal overhead. */ int ppsratecheck(struct timeval *lasttime, int *curpps, int maxpps) { int now; /* * Reset the last time and counter if this is the first call * or more than a second has passed since the last update of * lasttime. */ now = ticks; if (lasttime->tv_sec == 0 || (u_int)(now - lasttime->tv_sec) >= hz) { lasttime->tv_sec = now; *curpps = 1; return (maxpps != 0); } else { (*curpps)++; /* NB: ignore potential overflow */ return (maxpps < 0 || *curpps < maxpps); } } static void itimer_start(void) { struct kclock rt_clock = { .timer_create = realtimer_create, .timer_delete = realtimer_delete, .timer_settime = realtimer_settime, .timer_gettime = realtimer_gettime, .event_hook = realtimer_event_hook }; itimer_zone = uma_zcreate("itimer", sizeof(struct itimer), NULL, NULL, itimer_init, itimer_fini, UMA_ALIGN_PTR, 0); register_posix_clock(CLOCK_REALTIME, &rt_clock); register_posix_clock(CLOCK_MONOTONIC, &rt_clock); } int register_posix_clock(int clockid, struct kclock *clk) { if ((unsigned)clockid >= MAX_CLOCKS) { printf("%s: invalid clockid\n", __func__); return (0); } posix_clocks[clockid] = *clk; return (1); } static int itimer_init(void *mem, int size, int flags) { struct itimer *it; it = (struct itimer *)mem; mtx_init(&it->it_mtx, "itimer lock", NULL, MTX_DEF); return (0); } static void itimer_fini(void *mem, int size) { struct itimer *it; it = (struct itimer *)mem; mtx_destroy(&it->it_mtx); } static void itimer_enter(struct itimer *it) { mtx_assert(&it->it_mtx, MA_OWNED); it->it_usecount++; } static void itimer_leave(struct itimer *it) { mtx_assert(&it->it_mtx, MA_OWNED); KASSERT(it->it_usecount > 0, ("invalid it_usecount")); if (--it->it_usecount == 0 && (it->it_flags & ITF_WANTED) != 0) wakeup(it); } #ifndef _SYS_SYSPROTO_H_ struct timer_create_args { clockid_t clock_id; struct sigevent * evp; timer_t * timerid; }; #endif int timer_create(struct thread *td, struct timer_create_args *uap) { struct sigevent *evp1, ev; timer_t id; int error; if (uap->evp != NULL) { error = copyin(uap->evp, &ev, sizeof(ev)); if (error != 0) return (error); evp1 = &ev; } else evp1 = NULL; error = kern_timer_create(td, uap->clock_id, evp1, &id, -1); if (error == 0) { error = copyout(&id, uap->timerid, sizeof(timer_t)); if (error != 0) kern_timer_delete(td, id); } return (error); } static int kern_timer_create(struct thread *td, clockid_t clock_id, struct sigevent *evp, timer_t *timerid, timer_t preset_id) { struct proc *p = td->td_proc; struct itimer *it; int id; int error; if (clock_id < 0 || clock_id >= MAX_CLOCKS) return (EINVAL); if (posix_clocks[clock_id].timer_create == NULL) return (EINVAL); if (evp != NULL) { if (evp->sigev_notify != SIGEV_NONE && evp->sigev_notify != SIGEV_SIGNAL && evp->sigev_notify != SIGEV_THREAD_ID) return (EINVAL); if ((evp->sigev_notify == SIGEV_SIGNAL || evp->sigev_notify == SIGEV_THREAD_ID) && !_SIG_VALID(evp->sigev_signo)) return (EINVAL); } if (p->p_itimers == NULL) itimers_alloc(p); it = uma_zalloc(itimer_zone, M_WAITOK); it->it_flags = 0; it->it_usecount = 0; it->it_active = 0; timespecclear(&it->it_time.it_value); timespecclear(&it->it_time.it_interval); it->it_overrun = 0; it->it_overrun_last = 0; it->it_clockid = clock_id; it->it_timerid = -1; it->it_proc = p; ksiginfo_init(&it->it_ksi); it->it_ksi.ksi_flags |= KSI_INS | KSI_EXT; error = CLOCK_CALL(clock_id, timer_create, (it)); if (error != 0) goto out; PROC_LOCK(p); if (preset_id != -1) { KASSERT(preset_id >= 0 && preset_id < 3, ("invalid preset_id")); id = preset_id; if (p->p_itimers->its_timers[id] != NULL) { PROC_UNLOCK(p); error = 0; goto out; } } else { /* * Find a free timer slot, skipping those reserved * for setitimer(). */ for (id = 3; id < TIMER_MAX; id++) if (p->p_itimers->its_timers[id] == NULL) break; if (id == TIMER_MAX) { PROC_UNLOCK(p); error = EAGAIN; goto out; } } it->it_timerid = id; p->p_itimers->its_timers[id] = it; if (evp != NULL) it->it_sigev = *evp; else { it->it_sigev.sigev_notify = SIGEV_SIGNAL; switch (clock_id) { default: case CLOCK_REALTIME: it->it_sigev.sigev_signo = SIGALRM; break; case CLOCK_VIRTUAL: it->it_sigev.sigev_signo = SIGVTALRM; break; case CLOCK_PROF: it->it_sigev.sigev_signo = SIGPROF; break; } - it->it_sigev.sigev_value.sigval_int = id; + it->it_sigev.sigev_value.sival_int = id; } if (it->it_sigev.sigev_notify == SIGEV_SIGNAL || it->it_sigev.sigev_notify == SIGEV_THREAD_ID) { it->it_ksi.ksi_signo = it->it_sigev.sigev_signo; it->it_ksi.ksi_code = SI_TIMER; it->it_ksi.ksi_value = it->it_sigev.sigev_value; it->it_ksi.ksi_timerid = id; } PROC_UNLOCK(p); *timerid = id; return (0); out: ITIMER_LOCK(it); CLOCK_CALL(it->it_clockid, timer_delete, (it)); ITIMER_UNLOCK(it); uma_zfree(itimer_zone, it); return (error); } #ifndef _SYS_SYSPROTO_H_ struct timer_delete_args { timer_t timerid; }; #endif int timer_delete(struct thread *td, struct timer_delete_args *uap) { return (kern_timer_delete(td, uap->timerid)); } static struct itimer * itimer_find(struct proc *p, timer_t timerid, int include_deleting) { struct itimer *it; PROC_LOCK_ASSERT(p, MA_OWNED); if ((p->p_itimers == NULL) || (timerid >= TIMER_MAX) || (it = p->p_itimers->its_timers[timerid]) == NULL) { return (NULL); } ITIMER_LOCK(it); if (!include_deleting && (it->it_flags & ITF_DELETING) != 0) { ITIMER_UNLOCK(it); it = NULL; } return (it); } static int kern_timer_delete(struct thread *td, timer_t timerid) { struct proc *p = td->td_proc; struct itimer *it; PROC_LOCK(p); it = itimer_find(p, timerid, 0); if (it == NULL) { PROC_UNLOCK(p); return (EINVAL); } PROC_UNLOCK(p); it->it_flags |= ITF_DELETING; while (it->it_usecount > 0) { it->it_flags |= ITF_WANTED; msleep(it, &it->it_mtx, PPAUSE, "itimer", 0); } it->it_flags &= ~ITF_WANTED; CLOCK_CALL(it->it_clockid, timer_delete, (it)); ITIMER_UNLOCK(it); PROC_LOCK(p); if (KSI_ONQ(&it->it_ksi)) sigqueue_take(&it->it_ksi); p->p_itimers->its_timers[timerid] = NULL; PROC_UNLOCK(p); uma_zfree(itimer_zone, it); return (0); } #ifndef _SYS_SYSPROTO_H_ struct timer_settime_args { timer_t timerid; int flags; const struct itimerspec * value; struct itimerspec * ovalue; }; #endif int timer_settime(struct thread *td, struct timer_settime_args *uap) { struct proc *p = td->td_proc; struct itimer *it; struct itimerspec val, oval, *ovalp; int error; error = copyin(uap->value, &val, sizeof(val)); if (error != 0) return (error); if (uap->ovalue != NULL) ovalp = &oval; else ovalp = NULL; PROC_LOCK(p); if (uap->timerid < 3 || (it = itimer_find(p, uap->timerid, 0)) == NULL) { PROC_UNLOCK(p); error = EINVAL; } else { PROC_UNLOCK(p); itimer_enter(it); error = CLOCK_CALL(it->it_clockid, timer_settime, (it, uap->flags, &val, ovalp)); itimer_leave(it); ITIMER_UNLOCK(it); } if (error == 0 && uap->ovalue != NULL) error = copyout(ovalp, uap->ovalue, sizeof(*ovalp)); return (error); } #ifndef _SYS_SYSPROTO_H_ struct timer_gettime_args { timer_t timerid; struct itimerspec * value; }; #endif int timer_gettime(struct thread *td, struct timer_gettime_args *uap) { struct proc *p = td->td_proc; struct itimer *it; struct itimerspec val; int error; PROC_LOCK(p); if (uap->timerid < 3 || (it = itimer_find(p, uap->timerid, 0)) == NULL) { PROC_UNLOCK(p); error = EINVAL; } else { PROC_UNLOCK(p); itimer_enter(it); error = CLOCK_CALL(it->it_clockid, timer_gettime, (it, &val)); itimer_leave(it); ITIMER_UNLOCK(it); } if (error == 0) error = copyout(&val, uap->value, sizeof(val)); return (error); } #ifndef _SYS_SYSPROTO_H_ struct timer_getoverrun_args { timer_t timerid; }; #endif int timer_getoverrun(struct thread *td, struct timer_getoverrun_args *uap) { struct proc *p = td->td_proc; struct itimer *it; int error ; PROC_LOCK(p); if (uap->timerid < 3 || (it = itimer_find(p, uap->timerid, 0)) == NULL) { PROC_UNLOCK(p); error = EINVAL; } else { td->td_retval[0] = it->it_overrun_last; ITIMER_UNLOCK(it); PROC_UNLOCK(p); error = 0; } return (error); } static int realtimer_create(struct itimer *it) { callout_init_mtx(&it->it_callout, &it->it_mtx, 0); return (0); } static int realtimer_delete(struct itimer *it) { mtx_assert(&it->it_mtx, MA_OWNED); callout_stop(&it->it_callout); return (0); } static int realtimer_gettime(struct itimer *it, struct itimerspec *ovalue) { struct timespec cts; mtx_assert(&it->it_mtx, MA_OWNED); realtimer_clocktime(it->it_clockid, &cts); *ovalue = it->it_time; if (ovalue->it_value.tv_sec != 0 || ovalue->it_value.tv_nsec != 0) { timespecsub(&ovalue->it_value, &cts); if (ovalue->it_value.tv_sec < 0 || (ovalue->it_value.tv_sec == 0 && ovalue->it_value.tv_nsec == 0)) { ovalue->it_value.tv_sec = 0; ovalue->it_value.tv_nsec = 1; } } return (0); } static int realtimer_settime(struct itimer *it, int flags, struct itimerspec *value, struct itimerspec *ovalue) { struct timespec cts, ts; struct timeval tv; struct itimerspec val; mtx_assert(&it->it_mtx, MA_OWNED); val = *value; if (itimespecfix(&val.it_value)) return (EINVAL); if (timespecisset(&val.it_value)) { if (itimespecfix(&val.it_interval)) return (EINVAL); } else { timespecclear(&val.it_interval); } if (ovalue != NULL) realtimer_gettime(it, ovalue); it->it_time = val; if (timespecisset(&val.it_value)) { realtimer_clocktime(it->it_clockid, &cts); ts = val.it_value; if ((flags & TIMER_ABSTIME) == 0) { /* Convert to absolute time. */ timespecadd(&it->it_time.it_value, &cts); } else { timespecsub(&ts, &cts); /* * We don't care if ts is negative, tztohz will * fix it. */ } TIMESPEC_TO_TIMEVAL(&tv, &ts); callout_reset(&it->it_callout, tvtohz(&tv), realtimer_expire, it); } else { callout_stop(&it->it_callout); } return (0); } static void realtimer_clocktime(clockid_t id, struct timespec *ts) { if (id == CLOCK_REALTIME) getnanotime(ts); else /* CLOCK_MONOTONIC */ getnanouptime(ts); } int itimer_accept(struct proc *p, timer_t timerid, ksiginfo_t *ksi) { struct itimer *it; PROC_LOCK_ASSERT(p, MA_OWNED); it = itimer_find(p, timerid, 0); if (it != NULL) { ksi->ksi_overrun = it->it_overrun; it->it_overrun_last = it->it_overrun; it->it_overrun = 0; ITIMER_UNLOCK(it); return (0); } return (EINVAL); } int itimespecfix(struct timespec *ts) { if (ts->tv_sec < 0 || ts->tv_nsec < 0 || ts->tv_nsec >= 1000000000) return (EINVAL); if (ts->tv_sec == 0 && ts->tv_nsec != 0 && ts->tv_nsec < tick * 1000) ts->tv_nsec = tick * 1000; return (0); } static void realtimer_event_hook(struct proc *p, clockid_t clock_id, int event) { struct itimers *its; struct itimer *it; int i; /* * Timer 0 (ITIMER_REAL) is XSI interval timer, according to POSIX * specification, it should be inherited by new process image. */ if (event == ITIMER_EV_EXEC) i = 1; else i = 0; its = p->p_itimers; for (; i < TIMER_MAX; i++) { if ((it = its->its_timers[i]) != NULL && it->it_clockid == clock_id) { ITIMER_LOCK(it); callout_stop(&it->it_callout); ITIMER_UNLOCK(it); } } } /* Timeout callback for realtime timer */ static void realtimer_expire(void *arg) { struct timespec cts, ts; struct timeval tv; struct itimer *it; struct proc *p; it = (struct itimer *)arg; p = it->it_proc; realtimer_clocktime(it->it_clockid, &cts); /* Only fire if time is reached. */ if (timespeccmp(&cts, &it->it_time.it_value, >=)) { if (timespecisset(&it->it_time.it_interval)) { timespecadd(&it->it_time.it_value, &it->it_time.it_interval); while (timespeccmp(&cts, &it->it_time.it_value, >=)) { it->it_overrun++; timespecadd(&it->it_time.it_value, &it->it_time.it_interval); } } else { /* single shot timer ? */ timespecclear(&it->it_time.it_value); } if (timespecisset(&it->it_time.it_value)) { ts = it->it_time.it_value; timespecsub(&ts, &cts); TIMESPEC_TO_TIMEVAL(&tv, &ts); callout_reset(&it->it_callout, tvtohz(&tv), realtimer_expire, it); } ITIMER_UNLOCK(it); itimer_fire(it); ITIMER_LOCK(it); } else if (timespecisset(&it->it_time.it_value)) { ts = it->it_time.it_value; timespecsub(&ts, &cts); TIMESPEC_TO_TIMEVAL(&tv, &ts); callout_reset(&it->it_callout, tvtohz(&tv), realtimer_expire, it); } } void itimer_fire(struct itimer *it) { struct proc *p = it->it_proc; int ret; if (it->it_sigev.sigev_notify == SIGEV_SIGNAL || it->it_sigev.sigev_notify == SIGEV_THREAD_ID) { PROC_LOCK(p); if (!KSI_ONQ(&it->it_ksi)) { ret = psignal_event(p, &it->it_sigev, &it->it_ksi); if (__predict_false(ret != 0)) { it->it_overrun++; /* * Broken userland code, thread went * away, disarm the timer. */ if (ret == ESRCH) { ITIMER_LOCK(it); timespecclear(&it->it_time.it_value); timespecclear(&it->it_time.it_interval); callout_stop(&it->it_callout); ITIMER_UNLOCK(it); } } } else { it->it_overrun++; } PROC_UNLOCK(p); } } static void itimers_alloc(struct proc *p) { struct itimers *its; int i; its = malloc(sizeof (struct itimers), M_SUBPROC, M_WAITOK | M_ZERO); LIST_INIT(&its->its_virtual); LIST_INIT(&its->its_prof); TAILQ_INIT(&its->its_worklist); for (i = 0; i < TIMER_MAX; i++) its->its_timers[i] = NULL; PROC_LOCK(p); if (p->p_itimers == NULL) { p->p_itimers = its; PROC_UNLOCK(p); } else { PROC_UNLOCK(p); free(its, M_SUBPROC); } } /* Clean up timers when some process events are being triggered. */ void itimers_event_hook(struct proc *p, int event) { struct itimers *its; struct itimer *it; int i; if (p->p_itimers != NULL) { its = p->p_itimers; for (i = 0; i < MAX_CLOCKS; ++i) { if (posix_clocks[i].event_hook != NULL) CLOCK_CALL(i, event_hook, (p, i, event)); } /* * According to susv3, XSI interval timers should be inherited * by new image. */ if (event == ITIMER_EV_EXEC) i = 3; else if (event == ITIMER_EV_EXIT) i = 0; else panic("unhandled event"); for (; i < TIMER_MAX; ++i) { if ((it = its->its_timers[i]) != NULL) { PROC_LOCK(p); if (KSI_ONQ(&it->it_ksi)) sigqueue_take(&it->it_ksi); PROC_UNLOCK(p); uma_zfree(itimer_zone, its->its_timers[i]); its->its_timers[i] = NULL; } } if (its->its_timers[0] == NULL && its->its_timers[1] == NULL && its->its_timers[2] == NULL) { free(its, M_SUBPROC); p->p_itimers = NULL; } } } Index: head/sys/kern/vfs_aio.c =================================================================== --- head/sys/kern/vfs_aio.c (revision 152028) +++ head/sys/kern/vfs_aio.c (revision 152029) @@ -1,2449 +1,2449 @@ /*- * Copyright (c) 1997 John S. Dyson. 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. John S. Dyson's name may not be used to endorse or promote products * derived from this software without specific prior written permission. * * DISCLAIMER: This code isn't warranted to do anything useful. Anything * bad that happens because of using this software isn't the responsibility * of the author. This software is distributed AS-IS. */ /* * This file contains support for the POSIX 1003.1B AIO/LIO facility. */ #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 #include #include #include #include #include #include #include "opt_vfs_aio.h" NET_NEEDS_GIANT("aio"); /* * Counter for allocating reference ids to new jobs. Wrapped to 1 on * overflow. */ static long jobrefid; #define JOBST_NULL 0x0 #define JOBST_JOBQGLOBAL 0x2 #define JOBST_JOBRUNNING 0x3 #define JOBST_JOBFINISHED 0x4 #define JOBST_JOBQBUF 0x5 #define JOBST_JOBBFINISHED 0x6 #ifndef MAX_AIO_PER_PROC #define MAX_AIO_PER_PROC 32 #endif #ifndef MAX_AIO_QUEUE_PER_PROC #define MAX_AIO_QUEUE_PER_PROC 256 /* Bigger than AIO_LISTIO_MAX */ #endif #ifndef MAX_AIO_PROCS #define MAX_AIO_PROCS 32 #endif #ifndef MAX_AIO_QUEUE #define MAX_AIO_QUEUE 1024 /* Bigger than AIO_LISTIO_MAX */ #endif #ifndef TARGET_AIO_PROCS #define TARGET_AIO_PROCS 4 #endif #ifndef MAX_BUF_AIO #define MAX_BUF_AIO 16 #endif #ifndef AIOD_TIMEOUT_DEFAULT #define AIOD_TIMEOUT_DEFAULT (10 * hz) #endif #ifndef AIOD_LIFETIME_DEFAULT #define AIOD_LIFETIME_DEFAULT (30 * hz) #endif static SYSCTL_NODE(_vfs, OID_AUTO, aio, CTLFLAG_RW, 0, "Async IO management"); static int max_aio_procs = MAX_AIO_PROCS; SYSCTL_INT(_vfs_aio, OID_AUTO, max_aio_procs, CTLFLAG_RW, &max_aio_procs, 0, "Maximum number of kernel threads to use for handling async IO "); static int num_aio_procs = 0; SYSCTL_INT(_vfs_aio, OID_AUTO, num_aio_procs, CTLFLAG_RD, &num_aio_procs, 0, "Number of presently active kernel threads for async IO"); /* * The code will adjust the actual number of AIO processes towards this * number when it gets a chance. */ static int target_aio_procs = TARGET_AIO_PROCS; SYSCTL_INT(_vfs_aio, OID_AUTO, target_aio_procs, CTLFLAG_RW, &target_aio_procs, 0, "Preferred number of ready kernel threads for async IO"); static int max_queue_count = MAX_AIO_QUEUE; SYSCTL_INT(_vfs_aio, OID_AUTO, max_aio_queue, CTLFLAG_RW, &max_queue_count, 0, "Maximum number of aio requests to queue, globally"); static int num_queue_count = 0; SYSCTL_INT(_vfs_aio, OID_AUTO, num_queue_count, CTLFLAG_RD, &num_queue_count, 0, "Number of queued aio requests"); static int num_buf_aio = 0; SYSCTL_INT(_vfs_aio, OID_AUTO, num_buf_aio, CTLFLAG_RD, &num_buf_aio, 0, "Number of aio requests presently handled by the buf subsystem"); /* Number of async I/O thread in the process of being started */ /* XXX This should be local to _aio_aqueue() */ static int num_aio_resv_start = 0; static int aiod_timeout; SYSCTL_INT(_vfs_aio, OID_AUTO, aiod_timeout, CTLFLAG_RW, &aiod_timeout, 0, "Timeout value for synchronous aio operations"); static int aiod_lifetime; SYSCTL_INT(_vfs_aio, OID_AUTO, aiod_lifetime, CTLFLAG_RW, &aiod_lifetime, 0, "Maximum lifetime for idle aiod"); static int unloadable = 0; SYSCTL_INT(_vfs_aio, OID_AUTO, unloadable, CTLFLAG_RW, &unloadable, 0, "Allow unload of aio (not recommended)"); static int max_aio_per_proc = MAX_AIO_PER_PROC; SYSCTL_INT(_vfs_aio, OID_AUTO, max_aio_per_proc, CTLFLAG_RW, &max_aio_per_proc, 0, "Maximum active aio requests per process (stored in the process)"); static int max_aio_queue_per_proc = MAX_AIO_QUEUE_PER_PROC; SYSCTL_INT(_vfs_aio, OID_AUTO, max_aio_queue_per_proc, CTLFLAG_RW, &max_aio_queue_per_proc, 0, "Maximum queued aio requests per process (stored in the process)"); static int max_buf_aio = MAX_BUF_AIO; SYSCTL_INT(_vfs_aio, OID_AUTO, max_buf_aio, CTLFLAG_RW, &max_buf_aio, 0, "Maximum buf aio requests per process (stored in the process)"); typedef struct oaiocb { int aio_fildes; /* File descriptor */ off_t aio_offset; /* File offset for I/O */ volatile void *aio_buf; /* I/O buffer in process space */ size_t aio_nbytes; /* Number of bytes for I/O */ struct osigevent aio_sigevent; /* Signal to deliver */ int aio_lio_opcode; /* LIO opcode */ int aio_reqprio; /* Request priority -- ignored */ struct __aiocb_private _aiocb_private; } oaiocb_t; struct aiocblist { TAILQ_ENTRY(aiocblist) list; /* List of jobs */ TAILQ_ENTRY(aiocblist) plist; /* List of jobs for proc */ int jobflags; int jobstate; int inputcharge; int outputcharge; struct buf *bp; /* Buffer pointer */ struct proc *userproc; /* User process */ /* Not td! */ struct ucred *cred; /* Active credential when created */ struct file *fd_file; /* Pointer to file structure */ struct aio_liojob *lio; /* Optional lio job */ struct aiocb *uuaiocb; /* Pointer in userspace of aiocb */ struct knlist klist; /* list of knotes */ struct aiocb uaiocb; /* Kernel I/O control block */ ksiginfo_t ksi; /* Realtime signal info */ }; /* jobflags */ #define AIOCBLIST_RUNDOWN 0x4 #define AIOCBLIST_DONE 0x10 /* * AIO process info */ #define AIOP_FREE 0x1 /* proc on free queue */ struct aiothreadlist { int aiothreadflags; /* AIO proc flags */ TAILQ_ENTRY(aiothreadlist) list; /* List of processes */ struct thread *aiothread; /* The AIO thread */ }; /* * data-structure for lio signal management */ struct aio_liojob { int lioj_flags; int lioj_buffer_count; int lioj_buffer_finished_count; int lioj_queue_count; int lioj_queue_finished_count; int lioj_total_count; struct sigevent lioj_signal; /* signal on all I/O done */ TAILQ_ENTRY(aio_liojob) lioj_list; struct knlist klist; /* list of knotes */ ksiginfo_t lioj_ksi; /* Realtime signal info */ }; #define LIOJ_SIGNAL 0x1 /* signal on all done (lio) */ #define LIOJ_SIGNAL_POSTED 0x2 /* signal has been posted */ #define LIOJ_KEVENT_POSTED 0x4 /* kevent triggered */ /* * per process aio data structure */ struct kaioinfo { int kaio_flags; /* per process kaio flags */ int kaio_maxactive_count; /* maximum number of AIOs */ int kaio_active_count; /* number of currently used AIOs */ int kaio_qallowed_count; /* maxiumu size of AIO queue */ int kaio_queue_count; /* size of AIO queue */ int kaio_ballowed_count; /* maximum number of buffers */ int kaio_queue_finished_count; /* number of daemon jobs finished */ int kaio_buffer_count; /* number of physio buffers */ int kaio_buffer_finished_count; /* count of I/O done */ TAILQ_HEAD(,aio_liojob) kaio_liojoblist; /* list of lio jobs */ TAILQ_HEAD(,aiocblist) kaio_jobqueue; /* job queue for process */ TAILQ_HEAD(,aiocblist) kaio_jobdone; /* done queue for process */ TAILQ_HEAD(,aiocblist) kaio_bufqueue; /* buffer job queue for process */ TAILQ_HEAD(,aiocblist) kaio_bufdone; /* buffer done queue for process */ TAILQ_HEAD(,aiocblist) kaio_sockqueue; /* queue for aios waiting on sockets */ }; #define KAIO_RUNDOWN 0x1 /* process is being run down */ #define KAIO_WAKEUP 0x2 /* wakeup process when there is a significant event */ static TAILQ_HEAD(,aiothreadlist) aio_freeproc; /* Idle daemons */ static struct mtx aio_freeproc_mtx; static TAILQ_HEAD(,aiocblist) aio_jobs; /* Async job list */ static void aio_init_aioinfo(struct proc *p); static void aio_onceonly(void); static int aio_free_entry(struct aiocblist *aiocbe); static void aio_process(struct aiocblist *aiocbe); static int aio_newproc(void); static int aio_aqueue(struct thread *td, struct aiocb *job, int type, int osigev); static void aio_physwakeup(struct buf *bp); static void aio_proc_rundown(void *arg, struct proc *p); static int aio_fphysio(struct aiocblist *aiocbe); static int aio_qphysio(struct proc *p, struct aiocblist *iocb); static void aio_daemon(void *uproc); static void aio_swake_cb(struct socket *, struct sockbuf *); static int aio_unload(void); static int filt_aioattach(struct knote *kn); static void filt_aiodetach(struct knote *kn); static int filt_aio(struct knote *kn, long hint); static int filt_lioattach(struct knote *kn); static void filt_liodetach(struct knote *kn); static int filt_lio(struct knote *kn, long hint); #define DONE_BUF 1 #define DONE_QUEUE 2 static void aio_bio_done_notify( struct proc *userp, struct aiocblist *aiocbe, int type); static int do_lio_listio(struct thread *td, struct lio_listio_args *uap, int oldsigev); /* * Zones for: * kaio Per process async io info * aiop async io thread data * aiocb async io jobs * aiol list io job pointer - internal to aio_suspend XXX * aiolio list io jobs */ static uma_zone_t kaio_zone, aiop_zone, aiocb_zone, aiol_zone, aiolio_zone; /* kqueue filters for aio */ static struct filterops aio_filtops = { 0, filt_aioattach, filt_aiodetach, filt_aio }; static struct filterops lio_filtops = { 0, filt_lioattach, filt_liodetach, filt_lio }; static eventhandler_tag exit_tag, exec_tag; /* * Main operations function for use as a kernel module. */ static int aio_modload(struct module *module, int cmd, void *arg) { int error = 0; switch (cmd) { case MOD_LOAD: aio_onceonly(); break; case MOD_UNLOAD: error = aio_unload(); break; case MOD_SHUTDOWN: break; default: error = EINVAL; break; } return (error); } static moduledata_t aio_mod = { "aio", &aio_modload, NULL }; SYSCALL_MODULE_HELPER(aio_return); SYSCALL_MODULE_HELPER(aio_suspend); SYSCALL_MODULE_HELPER(aio_cancel); SYSCALL_MODULE_HELPER(aio_error); SYSCALL_MODULE_HELPER(aio_read); SYSCALL_MODULE_HELPER(aio_write); SYSCALL_MODULE_HELPER(aio_waitcomplete); SYSCALL_MODULE_HELPER(lio_listio); SYSCALL_MODULE_HELPER(oaio_read); SYSCALL_MODULE_HELPER(oaio_write); SYSCALL_MODULE_HELPER(olio_listio); DECLARE_MODULE(aio, aio_mod, SI_SUB_VFS, SI_ORDER_ANY); MODULE_VERSION(aio, 1); /* * Startup initialization */ static void aio_onceonly(void) { /* XXX: should probably just use so->callback */ aio_swake = &aio_swake_cb; exit_tag = EVENTHANDLER_REGISTER(process_exit, aio_proc_rundown, NULL, EVENTHANDLER_PRI_ANY); exec_tag = EVENTHANDLER_REGISTER(process_exec, aio_proc_rundown, NULL, EVENTHANDLER_PRI_ANY); kqueue_add_filteropts(EVFILT_AIO, &aio_filtops); kqueue_add_filteropts(EVFILT_LIO, &lio_filtops); TAILQ_INIT(&aio_freeproc); mtx_init(&aio_freeproc_mtx, "aio_freeproc", NULL, MTX_DEF); TAILQ_INIT(&aio_jobs); kaio_zone = uma_zcreate("AIO", sizeof(struct kaioinfo), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_NOFREE); aiop_zone = uma_zcreate("AIOP", sizeof(struct aiothreadlist), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_NOFREE); aiocb_zone = uma_zcreate("AIOCB", sizeof(struct aiocblist), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_NOFREE); aiol_zone = uma_zcreate("AIOL", AIO_LISTIO_MAX*sizeof(intptr_t) , NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_NOFREE); aiolio_zone = uma_zcreate("AIOLIO", sizeof(struct aio_liojob), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_NOFREE); aiod_timeout = AIOD_TIMEOUT_DEFAULT; aiod_lifetime = AIOD_LIFETIME_DEFAULT; jobrefid = 1; async_io_version = _POSIX_VERSION; p31b_setcfg(CTL_P1003_1B_AIO_LISTIO_MAX, AIO_LISTIO_MAX); p31b_setcfg(CTL_P1003_1B_AIO_MAX, MAX_AIO_QUEUE); p31b_setcfg(CTL_P1003_1B_AIO_PRIO_DELTA_MAX, 0); } /* * Callback for unload of AIO when used as a module. */ static int aio_unload(void) { int error; /* * XXX: no unloads by default, it's too dangerous. * perhaps we could do it if locked out callers and then * did an aio_proc_rundown() on each process. */ if (!unloadable) return (EOPNOTSUPP); error = kqueue_del_filteropts(EVFILT_AIO); if (error) return error; async_io_version = 0; aio_swake = NULL; EVENTHANDLER_DEREGISTER(process_exit, exit_tag); EVENTHANDLER_DEREGISTER(process_exec, exec_tag); p31b_setcfg(CTL_P1003_1B_AIO_LISTIO_MAX, -1); p31b_setcfg(CTL_P1003_1B_AIO_MAX, -1); p31b_setcfg(CTL_P1003_1B_AIO_PRIO_DELTA_MAX, -1); return (0); } /* * Init the per-process aioinfo structure. The aioinfo limits are set * per-process for user limit (resource) management. */ static void aio_init_aioinfo(struct proc *p) { struct kaioinfo *ki; ki = uma_zalloc(kaio_zone, M_WAITOK); ki->kaio_flags = 0; ki->kaio_maxactive_count = max_aio_per_proc; ki->kaio_active_count = 0; ki->kaio_qallowed_count = max_aio_queue_per_proc; ki->kaio_queue_count = 0; ki->kaio_ballowed_count = max_buf_aio; ki->kaio_buffer_count = 0; ki->kaio_buffer_finished_count = 0; TAILQ_INIT(&ki->kaio_jobdone); TAILQ_INIT(&ki->kaio_jobqueue); TAILQ_INIT(&ki->kaio_bufdone); TAILQ_INIT(&ki->kaio_bufqueue); TAILQ_INIT(&ki->kaio_liojoblist); TAILQ_INIT(&ki->kaio_sockqueue); PROC_LOCK(p); if (p->p_aioinfo == NULL) { p->p_aioinfo = ki; PROC_UNLOCK(p); } else { PROC_UNLOCK(p); uma_zfree(kaio_zone, ki); } while (num_aio_procs < target_aio_procs) aio_newproc(); } static int aio_sendsig(struct proc *p, struct sigevent *sigev, ksiginfo_t *ksi) { PROC_LOCK_ASSERT(p, MA_OWNED); if (!KSI_ONQ(ksi)) { ksi->ksi_code = SI_ASYNCIO; ksi->ksi_flags |= KSI_EXT | KSI_INS; return (psignal_event(p, sigev, ksi)); } return (0); } /* * Free a job entry. Wait for completion if it is currently active, but don't * delay forever. If we delay, we return a flag that says that we have to * restart the queue scan. */ static int aio_free_entry(struct aiocblist *aiocbe) { struct kaioinfo *ki; struct aio_liojob *lj; struct proc *p; int error; int s; if (aiocbe->jobstate == JOBST_NULL) panic("aio_free_entry: freeing already free job"); p = aiocbe->userproc; ki = p->p_aioinfo; lj = aiocbe->lio; if (ki == NULL) panic("aio_free_entry: missing p->p_aioinfo"); while (aiocbe->jobstate == JOBST_JOBRUNNING) { aiocbe->jobflags |= AIOCBLIST_RUNDOWN; tsleep(aiocbe, PRIBIO, "jobwai", 0); } if (aiocbe->bp == NULL) { if (ki->kaio_queue_count <= 0) panic("aio_free_entry: process queue size <= 0"); if (num_queue_count <= 0) panic("aio_free_entry: system wide queue size <= 0"); if (lj) { lj->lioj_queue_count--; if (aiocbe->jobflags & AIOCBLIST_DONE) lj->lioj_queue_finished_count--; } ki->kaio_queue_count--; if (aiocbe->jobflags & AIOCBLIST_DONE) ki->kaio_queue_finished_count--; num_queue_count--; } else { if (lj) { lj->lioj_buffer_count--; if (aiocbe->jobflags & AIOCBLIST_DONE) lj->lioj_buffer_finished_count--; } if (aiocbe->jobflags & AIOCBLIST_DONE) ki->kaio_buffer_finished_count--; ki->kaio_buffer_count--; num_buf_aio--; } /* aiocbe is going away, we need to destroy any knotes */ /* XXXKSE Note the thread here is used to eventually find the * owning process again, but it is also used to do a fo_close * and that requires the thread. (but does it require the * OWNING thread? (or maybe the running thread?) * There is a semantic problem here... */ if (lj) knlist_delete(&lj->klist, FIRST_THREAD_IN_PROC(p), 0); /* XXXKSE */ knlist_delete(&aiocbe->klist, FIRST_THREAD_IN_PROC(p), 0); /* XXXKSE */ if ((ki->kaio_flags & KAIO_WAKEUP) || ((ki->kaio_flags & KAIO_RUNDOWN) && ((ki->kaio_buffer_count == 0) && (ki->kaio_queue_count == 0)))) { ki->kaio_flags &= ~KAIO_WAKEUP; wakeup(p); } if (aiocbe->jobstate == JOBST_JOBQBUF) { if ((error = aio_fphysio(aiocbe)) != 0) return (error); if (aiocbe->jobstate != JOBST_JOBBFINISHED) panic("aio_free_entry: invalid physio finish-up state"); s = splbio(); TAILQ_REMOVE(&ki->kaio_bufdone, aiocbe, plist); splx(s); } else if (aiocbe->jobstate == JOBST_JOBQGLOBAL) { s = splnet(); TAILQ_REMOVE(&aio_jobs, aiocbe, list); TAILQ_REMOVE(&ki->kaio_jobqueue, aiocbe, plist); splx(s); } else if (aiocbe->jobstate == JOBST_JOBFINISHED) TAILQ_REMOVE(&ki->kaio_jobdone, aiocbe, plist); else if (aiocbe->jobstate == JOBST_JOBBFINISHED) { s = splbio(); TAILQ_REMOVE(&ki->kaio_bufdone, aiocbe, plist); splx(s); if (aiocbe->bp) { vunmapbuf(aiocbe->bp); relpbuf(aiocbe->bp, NULL); aiocbe->bp = NULL; } } if (lj && (lj->lioj_buffer_count == 0) && (lj->lioj_queue_count == 0)) { TAILQ_REMOVE(&ki->kaio_liojoblist, lj, lioj_list); PROC_LOCK(p); sigqueue_take(&lj->lioj_ksi); PROC_UNLOCK(p); uma_zfree(aiolio_zone, lj); } aiocbe->jobstate = JOBST_NULL; fdrop(aiocbe->fd_file, curthread); crfree(aiocbe->cred); PROC_LOCK(p); sigqueue_take(&aiocbe->ksi); PROC_UNLOCK(p); uma_zfree(aiocb_zone, aiocbe); return (0); } /* * Rundown the jobs for a given process. */ static void aio_proc_rundown(void *arg, struct proc *p) { int s; struct kaioinfo *ki; struct aio_liojob *lj, *ljn; struct aiocblist *aiocbe, *aiocbn; struct file *fp; struct socket *so; ki = p->p_aioinfo; if (ki == NULL) return; mtx_lock(&Giant); ki->kaio_flags |= LIOJ_SIGNAL_POSTED; while ((ki->kaio_active_count > 0) || (ki->kaio_buffer_count > ki->kaio_buffer_finished_count)) { ki->kaio_flags |= KAIO_RUNDOWN; if (tsleep(p, PRIBIO, "kaiowt", aiod_timeout)) break; } /* * Move any aio ops that are waiting on socket I/O to the normal job * queues so they are cleaned up with any others. */ s = splnet(); for (aiocbe = TAILQ_FIRST(&ki->kaio_sockqueue); aiocbe; aiocbe = aiocbn) { aiocbn = TAILQ_NEXT(aiocbe, plist); fp = aiocbe->fd_file; if (fp != NULL) { so = fp->f_data; TAILQ_REMOVE(&so->so_aiojobq, aiocbe, list); if (TAILQ_EMPTY(&so->so_aiojobq)) { SOCKBUF_LOCK(&so->so_snd); so->so_snd.sb_flags &= ~SB_AIO; SOCKBUF_UNLOCK(&so->so_snd); SOCKBUF_LOCK(&so->so_rcv); so->so_rcv.sb_flags &= ~SB_AIO; SOCKBUF_UNLOCK(&so->so_rcv); } } TAILQ_REMOVE(&ki->kaio_sockqueue, aiocbe, plist); TAILQ_INSERT_HEAD(&aio_jobs, aiocbe, list); TAILQ_INSERT_HEAD(&ki->kaio_jobqueue, aiocbe, plist); } splx(s); restart1: for (aiocbe = TAILQ_FIRST(&ki->kaio_jobdone); aiocbe; aiocbe = aiocbn) { aiocbn = TAILQ_NEXT(aiocbe, plist); if (aio_free_entry(aiocbe)) goto restart1; } restart2: for (aiocbe = TAILQ_FIRST(&ki->kaio_jobqueue); aiocbe; aiocbe = aiocbn) { aiocbn = TAILQ_NEXT(aiocbe, plist); if (aio_free_entry(aiocbe)) goto restart2; } /* * Note the use of lots of splbio here, trying to avoid splbio for long chains * of I/O. Probably unnecessary. */ restart3: s = splbio(); while (TAILQ_FIRST(&ki->kaio_bufqueue)) { ki->kaio_flags |= KAIO_WAKEUP; tsleep(p, PRIBIO, "aioprn", 0); splx(s); goto restart3; } splx(s); restart4: s = splbio(); for (aiocbe = TAILQ_FIRST(&ki->kaio_bufdone); aiocbe; aiocbe = aiocbn) { aiocbn = TAILQ_NEXT(aiocbe, plist); if (aio_free_entry(aiocbe)) { splx(s); goto restart4; } } splx(s); /* * If we've slept, jobs might have moved from one queue to another. * Retry rundown if we didn't manage to empty the queues. */ if (TAILQ_FIRST(&ki->kaio_jobdone) != NULL || TAILQ_FIRST(&ki->kaio_jobqueue) != NULL || TAILQ_FIRST(&ki->kaio_bufqueue) != NULL || TAILQ_FIRST(&ki->kaio_bufdone) != NULL) goto restart1; for (lj = TAILQ_FIRST(&ki->kaio_liojoblist); lj; lj = ljn) { ljn = TAILQ_NEXT(lj, lioj_list); if ((lj->lioj_buffer_count == 0) && (lj->lioj_queue_count == 0)) { TAILQ_REMOVE(&ki->kaio_liojoblist, lj, lioj_list); uma_zfree(aiolio_zone, lj); } else { #ifdef DIAGNOSTIC printf("LIO job not cleaned up: B:%d, BF:%d, Q:%d, " "QF:%d\n", lj->lioj_buffer_count, lj->lioj_buffer_finished_count, lj->lioj_queue_count, lj->lioj_queue_finished_count); #endif } } uma_zfree(kaio_zone, ki); p->p_aioinfo = NULL; mtx_unlock(&Giant); } /* * Select a job to run (called by an AIO daemon). */ static struct aiocblist * aio_selectjob(struct aiothreadlist *aiop) { int s; struct aiocblist *aiocbe; struct kaioinfo *ki; struct proc *userp; s = splnet(); for (aiocbe = TAILQ_FIRST(&aio_jobs); aiocbe; aiocbe = TAILQ_NEXT(aiocbe, list)) { userp = aiocbe->userproc; ki = userp->p_aioinfo; if (ki->kaio_active_count < ki->kaio_maxactive_count) { TAILQ_REMOVE(&aio_jobs, aiocbe, list); splx(s); return (aiocbe); } } splx(s); return (NULL); } /* * The AIO processing activity. This is the code that does the I/O request for * the non-physio version of the operations. The normal vn operations are used, * and this code should work in all instances for every type of file, including * pipes, sockets, fifos, and regular files. */ static void aio_process(struct aiocblist *aiocbe) { struct ucred *td_savedcred; struct thread *td; struct proc *mycp; struct aiocb *cb; struct file *fp; struct uio auio; struct iovec aiov; int cnt; int error; int oublock_st, oublock_end; int inblock_st, inblock_end; td = curthread; td_savedcred = td->td_ucred; td->td_ucred = aiocbe->cred; mycp = td->td_proc; cb = &aiocbe->uaiocb; fp = aiocbe->fd_file; aiov.iov_base = (void *)(uintptr_t)cb->aio_buf; aiov.iov_len = cb->aio_nbytes; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_offset = cb->aio_offset; auio.uio_resid = cb->aio_nbytes; cnt = cb->aio_nbytes; auio.uio_segflg = UIO_USERSPACE; auio.uio_td = td; inblock_st = mycp->p_stats->p_ru.ru_inblock; oublock_st = mycp->p_stats->p_ru.ru_oublock; /* * _aio_aqueue() acquires a reference to the file that is * released in aio_free_entry(). */ if (cb->aio_lio_opcode == LIO_READ) { auio.uio_rw = UIO_READ; error = fo_read(fp, &auio, fp->f_cred, FOF_OFFSET, td); } else { auio.uio_rw = UIO_WRITE; error = fo_write(fp, &auio, fp->f_cred, FOF_OFFSET, td); } inblock_end = mycp->p_stats->p_ru.ru_inblock; oublock_end = mycp->p_stats->p_ru.ru_oublock; aiocbe->inputcharge = inblock_end - inblock_st; aiocbe->outputcharge = oublock_end - oublock_st; if ((error) && (auio.uio_resid != cnt)) { if (error == ERESTART || error == EINTR || error == EWOULDBLOCK) error = 0; if ((error == EPIPE) && (cb->aio_lio_opcode == LIO_WRITE)) { PROC_LOCK(aiocbe->userproc); psignal(aiocbe->userproc, SIGPIPE); PROC_UNLOCK(aiocbe->userproc); } } cnt -= auio.uio_resid; cb->_aiocb_private.error = error; cb->_aiocb_private.status = cnt; td->td_ucred = td_savedcred; } static void aio_bio_done_notify( struct proc *userp, struct aiocblist *aiocbe, int type){ int lj_done; struct aio_liojob *lj; struct kaioinfo *ki; ki = userp->p_aioinfo; lj = aiocbe->lio; lj_done = 0; if (lj) { if (type == DONE_QUEUE) lj->lioj_queue_finished_count++; else lj->lioj_buffer_finished_count++; if (lj->lioj_queue_finished_count + lj->lioj_buffer_finished_count == lj->lioj_total_count) lj_done = 1; } if (ki) { if (type == DONE_QUEUE) { ki->kaio_queue_finished_count++; TAILQ_REMOVE(&ki->kaio_jobqueue, aiocbe, plist); TAILQ_INSERT_TAIL(&ki->kaio_jobdone, aiocbe, plist); } else { ki->kaio_buffer_finished_count++; TAILQ_REMOVE(&ki->kaio_bufqueue, aiocbe, plist); TAILQ_INSERT_TAIL(&ki->kaio_bufdone, aiocbe, plist); } if (lj_done) { if (!knlist_empty(&lj->klist) && lj->lioj_signal.sigev_notify == SIGEV_KEVENT) { lj->lioj_flags |= LIOJ_KEVENT_POSTED; KNOTE_UNLOCKED(&lj->klist, 0); } if ((lj->lioj_flags & (LIOJ_SIGNAL|LIOJ_SIGNAL_POSTED)) == LIOJ_SIGNAL && (lj->lioj_signal.sigev_notify == SIGEV_SIGNAL || lj->lioj_signal.sigev_notify == SIGEV_THREAD_ID)) { PROC_LOCK(userp); aio_sendsig(userp, &lj->lioj_signal, &lj->lioj_ksi); PROC_UNLOCK(userp); lj->lioj_flags |= LIOJ_SIGNAL_POSTED; } } KNOTE_UNLOCKED(&aiocbe->klist, 0); if (ki->kaio_flags & (KAIO_RUNDOWN|KAIO_WAKEUP)) { ki->kaio_flags &= ~KAIO_WAKEUP; wakeup(userp); } } if (aiocbe->uaiocb.aio_sigevent.sigev_notify == SIGEV_SIGNAL || aiocbe->uaiocb.aio_sigevent.sigev_notify == SIGEV_THREAD_ID) { PROC_LOCK(userp); aio_sendsig(userp, &aiocbe->uaiocb.aio_sigevent, &aiocbe->ksi); PROC_UNLOCK(userp); } } /* * The AIO daemon, most of the actual work is done in aio_process, * but the setup (and address space mgmt) is done in this routine. */ static void aio_daemon(void *uproc) { int s; struct aiocb *cb; struct aiocblist *aiocbe; struct aiothreadlist *aiop; struct kaioinfo *ki; struct proc *curcp, *mycp, *userp; struct vmspace *myvm, *tmpvm; struct thread *td = curthread; struct pgrp *newpgrp; struct session *newsess; /* * Local copies of curproc (cp) and vmspace (myvm) */ mycp = td->td_proc; myvm = mycp->p_vmspace; KASSERT(mycp->p_textvp == NULL, ("kthread has a textvp")); /* * Allocate and ready the aio control info. There is one aiop structure * per daemon. */ aiop = uma_zalloc(aiop_zone, M_WAITOK); aiop->aiothread = td; aiop->aiothreadflags |= AIOP_FREE; /* * Place thread (lightweight process) onto the AIO free thread list. */ mtx_lock(&aio_freeproc_mtx); TAILQ_INSERT_HEAD(&aio_freeproc, aiop, list); mtx_unlock(&aio_freeproc_mtx); /* * Get rid of our current filedescriptors. AIOD's don't need any * filedescriptors, except as temporarily inherited from the client. */ mtx_lock(&Giant); fdfree(td); mtx_unlock(&Giant); /* The daemon resides in its own pgrp. */ MALLOC(newpgrp, struct pgrp *, sizeof(struct pgrp), M_PGRP, M_WAITOK | M_ZERO); MALLOC(newsess, struct session *, sizeof(struct session), M_SESSION, M_WAITOK | M_ZERO); sx_xlock(&proctree_lock); enterpgrp(mycp, mycp->p_pid, newpgrp, newsess); sx_xunlock(&proctree_lock); mtx_lock(&Giant); /* * Wakeup parent process. (Parent sleeps to keep from blasting away * and creating too many daemons.) */ wakeup(mycp); for (;;) { /* * curcp is the current daemon process context. * userp is the current user process context. */ curcp = mycp; /* * Take daemon off of free queue */ mtx_lock(&aio_freeproc_mtx); if (aiop->aiothreadflags & AIOP_FREE) { TAILQ_REMOVE(&aio_freeproc, aiop, list); aiop->aiothreadflags &= ~AIOP_FREE; } mtx_unlock(&aio_freeproc_mtx); /* * Check for jobs. */ while ((aiocbe = aio_selectjob(aiop)) != NULL) { cb = &aiocbe->uaiocb; userp = aiocbe->userproc; aiocbe->jobstate = JOBST_JOBRUNNING; /* * Connect to process address space for user program. */ if (userp != curcp) { /* * Save the current address space that we are * connected to. */ tmpvm = mycp->p_vmspace; /* * Point to the new user address space, and * refer to it. */ mycp->p_vmspace = userp->p_vmspace; atomic_add_int(&mycp->p_vmspace->vm_refcnt, 1); /* Activate the new mapping. */ pmap_activate(FIRST_THREAD_IN_PROC(mycp)); /* * If the old address space wasn't the daemons * own address space, then we need to remove the * daemon's reference from the other process * that it was acting on behalf of. */ if (tmpvm != myvm) { vmspace_free(tmpvm); } curcp = userp; } ki = userp->p_aioinfo; /* Account for currently active jobs. */ ki->kaio_active_count++; /* Do the I/O function. */ aio_process(aiocbe); s = splbio(); /* Decrement the active job count. */ ki->kaio_active_count--; aiocbe->jobflags |= AIOCBLIST_DONE; aiocbe->jobstate = JOBST_JOBFINISHED; aio_bio_done_notify(userp, aiocbe, DONE_QUEUE); if (aiocbe->jobflags & AIOCBLIST_RUNDOWN) { wakeup(aiocbe); aiocbe->jobflags &= ~AIOCBLIST_RUNDOWN; } } /* * Disconnect from user address space. */ if (curcp != mycp) { /* Get the user address space to disconnect from. */ tmpvm = mycp->p_vmspace; /* Get original address space for daemon. */ mycp->p_vmspace = myvm; /* Activate the daemon's address space. */ pmap_activate(FIRST_THREAD_IN_PROC(mycp)); #ifdef DIAGNOSTIC if (tmpvm == myvm) { printf("AIOD: vmspace problem -- %d\n", mycp->p_pid); } #endif /* Remove our vmspace reference. */ vmspace_free(tmpvm); curcp = mycp; } mtx_lock(&aio_freeproc_mtx); TAILQ_INSERT_HEAD(&aio_freeproc, aiop, list); aiop->aiothreadflags |= AIOP_FREE; /* * If daemon is inactive for a long time, allow it to exit, * thereby freeing resources. */ if (msleep(aiop->aiothread, &aio_freeproc_mtx, PDROP | PRIBIO, "aiordy", aiod_lifetime)) { s = splnet(); if (TAILQ_EMPTY(&aio_jobs)) { mtx_lock(&aio_freeproc_mtx); if ((aiop->aiothreadflags & AIOP_FREE) && (num_aio_procs > target_aio_procs)) { TAILQ_REMOVE(&aio_freeproc, aiop, list); mtx_unlock(&aio_freeproc_mtx); splx(s); uma_zfree(aiop_zone, aiop); num_aio_procs--; #ifdef DIAGNOSTIC if (mycp->p_vmspace->vm_refcnt <= 1) { printf("AIOD: bad vm refcnt for" " exiting daemon: %d\n", mycp->p_vmspace->vm_refcnt); } #endif kthread_exit(0); } mtx_unlock(&aio_freeproc_mtx); } splx(s); } } } /* * Create a new AIO daemon. This is mostly a kernel-thread fork routine. The * AIO daemon modifies its environment itself. */ static int aio_newproc(void) { int error; struct proc *p; error = kthread_create(aio_daemon, curproc, &p, RFNOWAIT, 0, "aiod%d", num_aio_procs); if (error) return (error); /* * Wait until daemon is started, but continue on just in case to * handle error conditions. */ error = tsleep(p, PZERO, "aiosta", aiod_timeout); num_aio_procs++; return (error); } /* * Try the high-performance, low-overhead physio method for eligible * VCHR devices. This method doesn't use an aio helper thread, and * thus has very low overhead. * * Assumes that the caller, _aio_aqueue(), has incremented the file * structure's reference count, preventing its deallocation for the * duration of this call. */ static int aio_qphysio(struct proc *p, struct aiocblist *aiocbe) { int error; struct aiocb *cb; struct file *fp; struct buf *bp; struct vnode *vp; struct kaioinfo *ki; struct aio_liojob *lj; int s, lj_done = 0; int notify; cb = &aiocbe->uaiocb; fp = aiocbe->fd_file; if (fp->f_type != DTYPE_VNODE) return (-1); vp = fp->f_vnode; /* * If its not a disk, we don't want to return a positive error. * It causes the aio code to not fall through to try the thread * way when you're talking to a regular file. */ if (!vn_isdisk(vp, &error)) { if (error == ENOTBLK) return (-1); else return (error); } if (cb->aio_nbytes % vp->v_bufobj.bo_bsize) return (-1); if (cb->aio_nbytes > vp->v_rdev->si_iosize_max) return (-1); if (cb->aio_nbytes > MAXPHYS - (((vm_offset_t) cb->aio_buf) & PAGE_MASK)) return (-1); ki = p->p_aioinfo; if (ki->kaio_buffer_count >= ki->kaio_ballowed_count) return (-1); ki->kaio_buffer_count++; lj = aiocbe->lio; if (lj) lj->lioj_buffer_count++; /* Create and build a buffer header for a transfer. */ bp = (struct buf *)getpbuf(NULL); BUF_KERNPROC(bp); /* * Get a copy of the kva from the physical buffer. */ error = 0; bp->b_bcount = cb->aio_nbytes; bp->b_bufsize = cb->aio_nbytes; bp->b_iodone = aio_physwakeup; bp->b_saveaddr = bp->b_data; bp->b_data = (void *)(uintptr_t)cb->aio_buf; bp->b_offset = cb->aio_offset; bp->b_iooffset = cb->aio_offset; bp->b_blkno = btodb(cb->aio_offset); bp->b_iocmd = cb->aio_lio_opcode == LIO_WRITE ? BIO_WRITE : BIO_READ; /* * Bring buffer into kernel space. */ if (vmapbuf(bp) < 0) { error = EFAULT; goto doerror; } s = splbio(); aiocbe->bp = bp; bp->b_caller1 = (void *)aiocbe; TAILQ_INSERT_TAIL(&ki->kaio_bufqueue, aiocbe, plist); aiocbe->jobstate = JOBST_JOBQBUF; cb->_aiocb_private.status = cb->aio_nbytes; num_buf_aio++; bp->b_error = 0; splx(s); /* Perform transfer. */ dev_strategy(vp->v_rdev, bp); notify = 0; s = splbio(); /* * If we had an error invoking the request, or an error in processing * the request before we have returned, we process it as an error in * transfer. Note that such an I/O error is not indicated immediately, * but is returned using the aio_error mechanism. In this case, * aio_suspend will return immediately. */ if (bp->b_error || (bp->b_ioflags & BIO_ERROR)) { struct aiocb *job = aiocbe->uuaiocb; aiocbe->uaiocb._aiocb_private.status = 0; suword(&job->_aiocb_private.status, 0); aiocbe->uaiocb._aiocb_private.error = bp->b_error; suword(&job->_aiocb_private.error, bp->b_error); if (lj) { lj->lioj_buffer_finished_count++; if (lj->lioj_queue_finished_count + lj->lioj_buffer_finished_count == lj->lioj_total_count) lj_done = 1; } ki->kaio_buffer_finished_count++; if (aiocbe->jobstate != JOBST_JOBBFINISHED) { aiocbe->jobstate = JOBST_JOBBFINISHED; aiocbe->jobflags |= AIOCBLIST_DONE; TAILQ_REMOVE(&ki->kaio_bufqueue, aiocbe, plist); TAILQ_INSERT_TAIL(&ki->kaio_bufdone, aiocbe, plist); notify = 1; } } splx(s); if (notify) { if (lj && !knlist_empty(&lj->klist)) { lj->lioj_flags |= LIOJ_KEVENT_POSTED; KNOTE_UNLOCKED(&lj->klist, 0); } KNOTE_UNLOCKED(&aiocbe->klist, 0); } if (cb->aio_lio_opcode == LIO_WRITE) { aiocbe->outputcharge += btodb(cb->aio_nbytes); } else if (cb->aio_lio_opcode == LIO_READ) { aiocbe->inputcharge += btodb(cb->aio_nbytes); } return (0); doerror: ki->kaio_buffer_count--; if (lj) lj->lioj_buffer_count--; aiocbe->bp = NULL; relpbuf(bp, NULL); return (error); } /* * This waits/tests physio completion. */ static int aio_fphysio(struct aiocblist *iocb) { int s; struct buf *bp; int error; bp = iocb->bp; s = splbio(); while ((bp->b_flags & B_DONE) == 0) { if (tsleep(bp, PRIBIO, "physstr", aiod_timeout)) { if ((bp->b_flags & B_DONE) == 0) { splx(s); return (EINPROGRESS); } else break; } } splx(s); /* Release mapping into kernel space. */ vunmapbuf(bp); iocb->bp = 0; error = 0; /* Check for an error. */ if (bp->b_ioflags & BIO_ERROR) error = bp->b_error; relpbuf(bp, NULL); return (error); } /* * Wake up aio requests that may be serviceable now. */ static void aio_swake_cb(struct socket *so, struct sockbuf *sb) { struct aiocblist *cb,*cbn; struct proc *p; struct kaioinfo *ki = NULL; int opcode, wakecount = 0; struct aiothreadlist *aiop; if (sb == &so->so_snd) { opcode = LIO_WRITE; SOCKBUF_LOCK(&so->so_snd); so->so_snd.sb_flags &= ~SB_AIO; SOCKBUF_UNLOCK(&so->so_snd); } else { opcode = LIO_READ; SOCKBUF_LOCK(&so->so_rcv); so->so_rcv.sb_flags &= ~SB_AIO; SOCKBUF_UNLOCK(&so->so_rcv); } for (cb = TAILQ_FIRST(&so->so_aiojobq); cb; cb = cbn) { cbn = TAILQ_NEXT(cb, list); if (opcode == cb->uaiocb.aio_lio_opcode) { p = cb->userproc; ki = p->p_aioinfo; TAILQ_REMOVE(&so->so_aiojobq, cb, list); TAILQ_REMOVE(&ki->kaio_sockqueue, cb, plist); TAILQ_INSERT_TAIL(&aio_jobs, cb, list); TAILQ_INSERT_TAIL(&ki->kaio_jobqueue, cb, plist); wakecount++; if (cb->jobstate != JOBST_JOBQGLOBAL) panic("invalid queue value"); } } while (wakecount--) { mtx_lock(&aio_freeproc_mtx); if ((aiop = TAILQ_FIRST(&aio_freeproc)) != 0) { TAILQ_REMOVE(&aio_freeproc, aiop, list); aiop->aiothreadflags &= ~AIOP_FREE; wakeup(aiop->aiothread); } mtx_unlock(&aio_freeproc_mtx); } } /* * Queue a new AIO request. Choosing either the threaded or direct physio VCHR * technique is done in this code. */ static int _aio_aqueue(struct thread *td, struct aiocb *job, struct aio_liojob *lj, int type, int oldsigev) { struct proc *p = td->td_proc; struct filedesc *fdp; struct file *fp; unsigned int fd; struct socket *so; int s; int error; int opcode; struct aiocblist *aiocbe; struct aiothreadlist *aiop; struct kaioinfo *ki; struct kevent kev; struct kqueue *kq; struct file *kq_fp; struct sockbuf *sb; aiocbe = uma_zalloc(aiocb_zone, M_WAITOK); aiocbe->inputcharge = 0; aiocbe->outputcharge = 0; /* XXX - need a lock */ knlist_init(&aiocbe->klist, NULL, NULL, NULL, NULL); suword(&job->_aiocb_private.status, -1); suword(&job->_aiocb_private.error, 0); suword(&job->_aiocb_private.kernelinfo, -1); if (oldsigev) { bzero(&aiocbe->uaiocb, sizeof(struct aiocb)); error = copyin(job, &aiocbe->uaiocb, sizeof(struct oaiocb)); bcopy(&aiocbe->uaiocb.__spare__, &aiocbe->uaiocb.aio_sigevent, sizeof(struct osigevent)); } else { error = copyin(job, &aiocbe->uaiocb, sizeof(struct aiocb)); } if (error) { suword(&job->_aiocb_private.error, error); uma_zfree(aiocb_zone, aiocbe); return (error); } if ((aiocbe->uaiocb.aio_sigevent.sigev_notify == SIGEV_SIGNAL || aiocbe->uaiocb.aio_sigevent.sigev_notify == SIGEV_THREAD_ID) && !_SIG_VALID(aiocbe->uaiocb.aio_sigevent.sigev_signo)) { uma_zfree(aiocb_zone, aiocbe); return (EINVAL); } ksiginfo_init(&aiocbe->ksi); /* Save userspace address of the job info. */ aiocbe->uuaiocb = job; /* Get the opcode. */ if (type != LIO_NOP) aiocbe->uaiocb.aio_lio_opcode = type; opcode = aiocbe->uaiocb.aio_lio_opcode; /* Get the fd info for process. */ fdp = p->p_fd; /* * Range check file descriptor. */ FILEDESC_LOCK(fdp); fd = aiocbe->uaiocb.aio_fildes; if (fd >= fdp->fd_nfiles) { FILEDESC_UNLOCK(fdp); uma_zfree(aiocb_zone, aiocbe); if (type == 0) suword(&job->_aiocb_private.error, EBADF); return (EBADF); } fp = aiocbe->fd_file = fdp->fd_ofiles[fd]; if ((fp == NULL) || ((opcode == LIO_WRITE) && ((fp->f_flag & FWRITE) == 0)) || ((opcode == LIO_READ) && ((fp->f_flag & FREAD) == 0))) { FILEDESC_UNLOCK(fdp); uma_zfree(aiocb_zone, aiocbe); if (type == 0) suword(&job->_aiocb_private.error, EBADF); return (EBADF); } fhold(fp); FILEDESC_UNLOCK(fdp); if (aiocbe->uaiocb.aio_offset == -1LL) { error = EINVAL; goto aqueue_fail; } error = suword(&job->_aiocb_private.kernelinfo, jobrefid); if (error) { error = EINVAL; goto aqueue_fail; } aiocbe->uaiocb._aiocb_private.kernelinfo = (void *)(intptr_t)jobrefid; if (jobrefid == LONG_MAX) jobrefid = 1; else jobrefid++; if (opcode == LIO_NOP) { fdrop(fp, td); uma_zfree(aiocb_zone, aiocbe); if (type == 0) { suword(&job->_aiocb_private.error, 0); suword(&job->_aiocb_private.status, 0); suword(&job->_aiocb_private.kernelinfo, 0); } return (0); } if ((opcode != LIO_READ) && (opcode != LIO_WRITE)) { if (type == 0) suword(&job->_aiocb_private.status, 0); error = EINVAL; goto aqueue_fail; } if (aiocbe->uaiocb.aio_sigevent.sigev_notify == SIGEV_KEVENT) { kev.ident = aiocbe->uaiocb.aio_sigevent.sigev_notify_kqueue; - kev.udata = aiocbe->uaiocb.aio_sigevent.sigev_value.sigval_ptr; + kev.udata = aiocbe->uaiocb.aio_sigevent.sigev_value.sival_ptr; } else goto no_kqueue; if ((u_int)kev.ident >= fdp->fd_nfiles || (kq_fp = fdp->fd_ofiles[kev.ident]) == NULL || (kq_fp->f_type != DTYPE_KQUEUE)) { error = EBADF; goto aqueue_fail; } kq = kq_fp->f_data; kev.ident = (uintptr_t)aiocbe->uuaiocb; kev.filter = EVFILT_AIO; kev.flags = EV_ADD | EV_ENABLE | EV_FLAG1; kev.data = (intptr_t)aiocbe; error = kqueue_register(kq, &kev, td, 1); aqueue_fail: if (error) { fdrop(fp, td); uma_zfree(aiocb_zone, aiocbe); if (type == 0) suword(&job->_aiocb_private.error, error); goto done; } no_kqueue: suword(&job->_aiocb_private.error, EINPROGRESS); aiocbe->uaiocb._aiocb_private.error = EINPROGRESS; aiocbe->userproc = p; aiocbe->cred = crhold(td->td_ucred); aiocbe->jobflags = 0; aiocbe->lio = lj; ki = p->p_aioinfo; if (fp->f_type == DTYPE_SOCKET) { /* * Alternate queueing for socket ops: Reach down into the * descriptor to get the socket data. Then check to see if the * socket is ready to be read or written (based on the requested * operation). * * If it is not ready for io, then queue the aiocbe on the * socket, and set the flags so we get a call when sbnotify() * happens. * * Note if opcode is neither LIO_WRITE nor LIO_READ we lock * and unlock the snd sockbuf for no reason. */ so = fp->f_data; sb = (opcode == LIO_READ) ? &so->so_rcv : &so->so_snd; SOCKBUF_LOCK(sb); s = splnet(); if (((opcode == LIO_READ) && (!soreadable(so))) || ((opcode == LIO_WRITE) && (!sowriteable(so)))) { TAILQ_INSERT_TAIL(&so->so_aiojobq, aiocbe, list); TAILQ_INSERT_TAIL(&ki->kaio_sockqueue, aiocbe, plist); sb->sb_flags |= SB_AIO; aiocbe->jobstate = JOBST_JOBQGLOBAL; /* XXX */ ki->kaio_queue_count++; num_queue_count++; SOCKBUF_UNLOCK(sb); splx(s); error = 0; goto done; } SOCKBUF_UNLOCK(sb); splx(s); } if ((error = aio_qphysio(p, aiocbe)) == 0) goto done; if (error > 0) { suword(&job->_aiocb_private.status, 0); aiocbe->uaiocb._aiocb_private.error = error; suword(&job->_aiocb_private.error, error); goto done; } /* No buffer for daemon I/O. */ aiocbe->bp = NULL; ki->kaio_queue_count++; if (lj) lj->lioj_queue_count++; s = splnet(); TAILQ_INSERT_TAIL(&ki->kaio_jobqueue, aiocbe, plist); TAILQ_INSERT_TAIL(&aio_jobs, aiocbe, list); splx(s); aiocbe->jobstate = JOBST_JOBQGLOBAL; num_queue_count++; error = 0; /* * If we don't have a free AIO process, and we are below our quota, then * start one. Otherwise, depend on the subsequent I/O completions to * pick-up this job. If we don't sucessfully create the new process * (thread) due to resource issues, we return an error for now (EAGAIN), * which is likely not the correct thing to do. */ mtx_lock(&aio_freeproc_mtx); retryproc: if ((aiop = TAILQ_FIRST(&aio_freeproc)) != NULL) { TAILQ_REMOVE(&aio_freeproc, aiop, list); aiop->aiothreadflags &= ~AIOP_FREE; wakeup(aiop->aiothread); } else if (((num_aio_resv_start + num_aio_procs) < max_aio_procs) && ((ki->kaio_active_count + num_aio_resv_start) < ki->kaio_maxactive_count)) { num_aio_resv_start++; mtx_unlock(&aio_freeproc_mtx); if ((error = aio_newproc()) == 0) { mtx_lock(&aio_freeproc_mtx); num_aio_resv_start--; goto retryproc; } mtx_lock(&aio_freeproc_mtx); num_aio_resv_start--; } mtx_unlock(&aio_freeproc_mtx); done: return (error); } /* * This routine queues an AIO request, checking for quotas. */ static int aio_aqueue(struct thread *td, struct aiocb *job, int type, int oldsigev) { struct proc *p = td->td_proc; struct kaioinfo *ki; if (p->p_aioinfo == NULL) aio_init_aioinfo(p); if (num_queue_count >= max_queue_count) return (EAGAIN); ki = p->p_aioinfo; if (ki->kaio_queue_count >= ki->kaio_qallowed_count) return (EAGAIN); return _aio_aqueue(td, job, NULL, type, oldsigev); } /* * Support the aio_return system call, as a side-effect, kernel resources are * released. */ int aio_return(struct thread *td, struct aio_return_args *uap) { struct proc *p = td->td_proc; int s; long jobref; struct aiocblist *cb, *ncb; struct aiocb *ujob; struct kaioinfo *ki; ujob = uap->aiocbp; jobref = fuword(&ujob->_aiocb_private.kernelinfo); if (jobref == -1 || jobref == 0) return (EINVAL); ki = p->p_aioinfo; if (ki == NULL) return (EINVAL); PROC_LOCK(p); TAILQ_FOREACH(cb, &ki->kaio_jobdone, plist) { if (((intptr_t) cb->uaiocb._aiocb_private.kernelinfo) == jobref) goto done; } s = splbio(); /* aio_physwakeup */ for (cb = TAILQ_FIRST(&ki->kaio_bufdone); cb; cb = ncb) { ncb = TAILQ_NEXT(cb, plist); if (((intptr_t) cb->uaiocb._aiocb_private.kernelinfo) == jobref) { break; } } splx(s); done: PROC_UNLOCK(p); if (cb != NULL) { if (ujob == cb->uuaiocb) { td->td_retval[0] = cb->uaiocb._aiocb_private.status; } else td->td_retval[0] = EFAULT; if (cb->uaiocb.aio_lio_opcode == LIO_WRITE) { p->p_stats->p_ru.ru_oublock += cb->outputcharge; cb->outputcharge = 0; } else if (cb->uaiocb.aio_lio_opcode == LIO_READ) { p->p_stats->p_ru.ru_inblock += cb->inputcharge; cb->inputcharge = 0; } aio_free_entry(cb); return (0); } return (EINVAL); } /* * Allow a process to wakeup when any of the I/O requests are completed. */ int aio_suspend(struct thread *td, struct aio_suspend_args *uap) { struct proc *p = td->td_proc; struct timeval atv; struct timespec ts; struct aiocb *const *cbptr, *cbp; struct kaioinfo *ki; struct aiocblist *cb; int i; int njoblist; int error, s, timo; long *ijoblist; struct aiocb **ujoblist; if (uap->nent < 0 || uap->nent > AIO_LISTIO_MAX) return (EINVAL); timo = 0; if (uap->timeout) { /* Get timespec struct. */ if ((error = copyin(uap->timeout, &ts, sizeof(ts))) != 0) return (error); if (ts.tv_nsec < 0 || ts.tv_nsec >= 1000000000) return (EINVAL); TIMESPEC_TO_TIMEVAL(&atv, &ts); if (itimerfix(&atv)) return (EINVAL); timo = tvtohz(&atv); } ki = p->p_aioinfo; if (ki == NULL) return (EAGAIN); njoblist = 0; ijoblist = uma_zalloc(aiol_zone, M_WAITOK); ujoblist = uma_zalloc(aiol_zone, M_WAITOK); cbptr = uap->aiocbp; for (i = 0; i < uap->nent; i++) { cbp = (struct aiocb *)(intptr_t)fuword(&cbptr[i]); if (cbp == 0) continue; ujoblist[njoblist] = cbp; ijoblist[njoblist] = fuword(&cbp->_aiocb_private.kernelinfo); njoblist++; } if (njoblist == 0) { uma_zfree(aiol_zone, ijoblist); uma_zfree(aiol_zone, ujoblist); return (0); } error = 0; for (;;) { PROC_LOCK(p); TAILQ_FOREACH(cb, &ki->kaio_jobdone, plist) { for (i = 0; i < njoblist; i++) { if (((intptr_t) cb->uaiocb._aiocb_private.kernelinfo) == ijoblist[i]) { PROC_UNLOCK(p); if (ujoblist[i] != cb->uuaiocb) error = EINVAL; uma_zfree(aiol_zone, ijoblist); uma_zfree(aiol_zone, ujoblist); return (error); } } } s = splbio(); for (cb = TAILQ_FIRST(&ki->kaio_bufdone); cb; cb = TAILQ_NEXT(cb, plist)) { for (i = 0; i < njoblist; i++) { if (((intptr_t) cb->uaiocb._aiocb_private.kernelinfo) == ijoblist[i]) { PROC_UNLOCK(p); splx(s); if (ujoblist[i] != cb->uuaiocb) error = EINVAL; uma_zfree(aiol_zone, ijoblist); uma_zfree(aiol_zone, ujoblist); return (error); } } } ki->kaio_flags |= KAIO_WAKEUP; error = msleep(p, &p->p_mtx, PDROP | PRIBIO | PCATCH, "aiospn", timo); splx(s); if (error == ERESTART || error == EINTR) { uma_zfree(aiol_zone, ijoblist); uma_zfree(aiol_zone, ujoblist); return (EINTR); } else if (error == EWOULDBLOCK) { uma_zfree(aiol_zone, ijoblist); uma_zfree(aiol_zone, ujoblist); return (EAGAIN); } } /* NOTREACHED */ return (EINVAL); } /* * aio_cancel cancels any non-physio aio operations not currently in * progress. */ int aio_cancel(struct thread *td, struct aio_cancel_args *uap) { struct proc *p = td->td_proc; struct kaioinfo *ki; struct aiocblist *cbe, *cbn; struct file *fp; struct filedesc *fdp; struct socket *so; struct proc *po; int s,error; int cancelled=0; int notcancelled=0; struct vnode *vp; fdp = p->p_fd; if ((u_int)uap->fd >= fdp->fd_nfiles || (fp = fdp->fd_ofiles[uap->fd]) == NULL) return (EBADF); if (fp->f_type == DTYPE_VNODE) { vp = fp->f_vnode; if (vn_isdisk(vp,&error)) { td->td_retval[0] = AIO_NOTCANCELED; return (0); } } else if (fp->f_type == DTYPE_SOCKET) { so = fp->f_data; s = splnet(); for (cbe = TAILQ_FIRST(&so->so_aiojobq); cbe; cbe = cbn) { cbn = TAILQ_NEXT(cbe, list); if ((uap->aiocbp == NULL) || (uap->aiocbp == cbe->uuaiocb) ) { po = cbe->userproc; ki = po->p_aioinfo; TAILQ_REMOVE(&so->so_aiojobq, cbe, list); TAILQ_REMOVE(&ki->kaio_sockqueue, cbe, plist); TAILQ_INSERT_TAIL(&ki->kaio_jobdone, cbe, plist); if (ki->kaio_flags & KAIO_WAKEUP) { wakeup(po); } cbe->jobstate = JOBST_JOBFINISHED; cbe->uaiocb._aiocb_private.status=-1; cbe->uaiocb._aiocb_private.error=ECANCELED; cancelled++; /* XXX cancelled, knote? */ if (cbe->uaiocb.aio_sigevent.sigev_notify == SIGEV_SIGNAL || cbe->uaiocb.aio_sigevent.sigev_notify == SIGEV_THREAD_ID) { PROC_LOCK(cbe->userproc); aio_sendsig(cbe->userproc, &cbe->uaiocb.aio_sigevent, &cbe->ksi); PROC_UNLOCK(cbe->userproc); } if (uap->aiocbp) break; } } splx(s); if ((cancelled) && (uap->aiocbp)) { td->td_retval[0] = AIO_CANCELED; return (0); } } ki=p->p_aioinfo; if (ki == NULL) goto done; s = splnet(); for (cbe = TAILQ_FIRST(&ki->kaio_jobqueue); cbe; cbe = cbn) { cbn = TAILQ_NEXT(cbe, plist); if ((uap->fd == cbe->uaiocb.aio_fildes) && ((uap->aiocbp == NULL ) || (uap->aiocbp == cbe->uuaiocb))) { if (cbe->jobstate == JOBST_JOBQGLOBAL) { TAILQ_REMOVE(&aio_jobs, cbe, list); cbe->jobstate = JOBST_JOBFINISHED; cancelled++; cbe->uaiocb._aiocb_private.status = -1; cbe->uaiocb._aiocb_private.error = ECANCELED; aio_bio_done_notify(cbe->userproc, cbe, DONE_QUEUE); } else { notcancelled++; } } } splx(s); done: if (notcancelled) { td->td_retval[0] = AIO_NOTCANCELED; return (0); } if (cancelled) { td->td_retval[0] = AIO_CANCELED; return (0); } td->td_retval[0] = AIO_ALLDONE; return (0); } /* * aio_error is implemented in the kernel level for compatibility purposes only. * For a user mode async implementation, it would be best to do it in a userland * subroutine. */ int aio_error(struct thread *td, struct aio_error_args *uap) { struct proc *p = td->td_proc; int s; struct aiocblist *cb; struct kaioinfo *ki; long jobref; ki = p->p_aioinfo; if (ki == NULL) return (EINVAL); jobref = fuword(&uap->aiocbp->_aiocb_private.kernelinfo); if ((jobref == -1) || (jobref == 0)) return (EINVAL); PROC_LOCK(p); TAILQ_FOREACH(cb, &ki->kaio_jobdone, plist) { if (((intptr_t)cb->uaiocb._aiocb_private.kernelinfo) == jobref) { PROC_UNLOCK(p); td->td_retval[0] = cb->uaiocb._aiocb_private.error; return (0); } } s = splnet(); for (cb = TAILQ_FIRST(&ki->kaio_jobqueue); cb; cb = TAILQ_NEXT(cb, plist)) { if (((intptr_t)cb->uaiocb._aiocb_private.kernelinfo) == jobref) { PROC_UNLOCK(p); td->td_retval[0] = EINPROGRESS; splx(s); return (0); } } for (cb = TAILQ_FIRST(&ki->kaio_sockqueue); cb; cb = TAILQ_NEXT(cb, plist)) { if (((intptr_t)cb->uaiocb._aiocb_private.kernelinfo) == jobref) { PROC_UNLOCK(p); td->td_retval[0] = EINPROGRESS; splx(s); return (0); } } splx(s); s = splbio(); for (cb = TAILQ_FIRST(&ki->kaio_bufdone); cb; cb = TAILQ_NEXT(cb, plist)) { if (((intptr_t)cb->uaiocb._aiocb_private.kernelinfo) == jobref) { PROC_UNLOCK(p); td->td_retval[0] = cb->uaiocb._aiocb_private.error; splx(s); return (0); } } for (cb = TAILQ_FIRST(&ki->kaio_bufqueue); cb; cb = TAILQ_NEXT(cb, plist)) { if (((intptr_t)cb->uaiocb._aiocb_private.kernelinfo) == jobref) { PROC_UNLOCK(p); td->td_retval[0] = EINPROGRESS; splx(s); return (0); } } splx(s); PROC_UNLOCK(p); #if (0) /* * Hack for lio. */ status = fuword(&uap->aiocbp->_aiocb_private.status); if (status == -1) return fuword(&uap->aiocbp->_aiocb_private.error); #endif return (EINVAL); } /* syscall - asynchronous read from a file (REALTIME) */ int oaio_read(struct thread *td, struct oaio_read_args *uap) { return aio_aqueue(td, (struct aiocb *)uap->aiocbp, LIO_READ, 1); } int aio_read(struct thread *td, struct aio_read_args *uap) { return aio_aqueue(td, uap->aiocbp, LIO_READ, 0); } /* syscall - asynchronous write to a file (REALTIME) */ int oaio_write(struct thread *td, struct oaio_write_args *uap) { return aio_aqueue(td, (struct aiocb *)uap->aiocbp, LIO_WRITE, 1); } int aio_write(struct thread *td, struct aio_write_args *uap) { return aio_aqueue(td, uap->aiocbp, LIO_WRITE, 0); } /* syscall - list directed I/O (REALTIME) */ int olio_listio(struct thread *td, struct olio_listio_args *uap) { return do_lio_listio(td, (struct lio_listio_args *)uap, 1); } /* syscall - list directed I/O (REALTIME) */ int lio_listio(struct thread *td, struct lio_listio_args *uap) { return do_lio_listio(td, uap, 0); } static int do_lio_listio(struct thread *td, struct lio_listio_args *uap, int oldsigev) { struct proc *p = td->td_proc; int nent, nentqueued; struct aiocb *iocb, * const *cbptr; struct aiocblist *cb; struct kaioinfo *ki; struct aio_liojob *lj; struct kevent kev; struct kqueue * kq; struct file *kq_fp; int error, runningcode; int nerror; int i; if ((uap->mode != LIO_NOWAIT) && (uap->mode != LIO_WAIT)) return (EINVAL); nent = uap->nent; if (nent < 0 || nent > AIO_LISTIO_MAX) return (EINVAL); if (p->p_aioinfo == NULL) aio_init_aioinfo(p); if ((nent + num_queue_count) > max_queue_count) return (EAGAIN); ki = p->p_aioinfo; if ((nent + ki->kaio_queue_count) > ki->kaio_qallowed_count) return (EAGAIN); lj = uma_zalloc(aiolio_zone, M_WAITOK); if (!lj) return (EAGAIN); lj->lioj_flags = 0; lj->lioj_buffer_count = 0; lj->lioj_buffer_finished_count = 0; lj->lioj_queue_count = 0; lj->lioj_queue_finished_count = 0; lj->lioj_total_count = nent; knlist_init(&lj->klist, NULL, NULL, NULL, NULL); ksiginfo_init(&lj->lioj_ksi); kev.ident = 0; /* * Setup signal. */ if (uap->sig && (uap->mode == LIO_NOWAIT)) { bzero(&lj->lioj_signal, sizeof(&lj->lioj_signal)); error = copyin(uap->sig, &lj->lioj_signal, oldsigev ? sizeof(struct osigevent) : sizeof(struct sigevent)); if (error) { uma_zfree(aiolio_zone, lj); return (error); } if (lj->lioj_signal.sigev_notify == SIGEV_KEVENT) { /* Assume only new style KEVENT */ kev.ident = lj->lioj_signal.sigev_notify_kqueue; - kev.udata = lj->lioj_signal.sigev_value.sigval_ptr; + kev.udata = lj->lioj_signal.sigev_value.sival_ptr; if ((u_int)kev.ident >= p->p_fd->fd_nfiles || (kq_fp = p->p_fd->fd_ofiles[kev.ident]) == NULL || (kq_fp->f_type != DTYPE_KQUEUE)) { uma_zfree(aiolio_zone, lj); return (EBADF); } kq = (struct kqueue *)kq_fp->f_data; kev.filter = EVFILT_LIO; kev.flags = EV_ADD | EV_ENABLE | EV_FLAG1; kev.ident = (uintptr_t)lj; /* something unique */ kev.data = (intptr_t)lj; error = kqueue_register(kq, &kev, td, 1); if (error) { uma_zfree(aiolio_zone, lj); return (error); } } else if (!_SIG_VALID(lj->lioj_signal.sigev_signo)) { uma_zfree(aiolio_zone, lj); return EINVAL; } else { lj->lioj_flags |= LIOJ_SIGNAL; lj->lioj_flags &= ~LIOJ_SIGNAL_POSTED; } } else lj->lioj_flags &= ~LIOJ_SIGNAL; TAILQ_INSERT_TAIL(&ki->kaio_liojoblist, lj, lioj_list); /* * Get pointers to the list of I/O requests. */ nerror = 0; nentqueued = 0; cbptr = uap->acb_list; for (i = 0; i < uap->nent; i++) { iocb = (struct aiocb *)(intptr_t)fuword(&cbptr[i]); if (((intptr_t)iocb != -1) && ((intptr_t)iocb != 0)) { error = _aio_aqueue(td, iocb, lj, 0, oldsigev); if (error == 0) nentqueued++; else nerror++; } } /* * If we haven't queued any, then just return error. */ if (nentqueued == 0) return (0); /* * Calculate the appropriate error return. */ runningcode = 0; if (nerror) runningcode = EIO; if (uap->mode == LIO_WAIT) { int command, found; long jobref; for (;;) { found = 0; for (i = 0; i < uap->nent; i++) { /* * Fetch address of the control buf pointer in * user space. */ iocb = (struct aiocb *) (intptr_t)fuword(&cbptr[i]); if (((intptr_t)iocb == -1) || ((intptr_t)iocb == 0)) continue; /* * Fetch the associated command from user space. */ command = fuword(&iocb->aio_lio_opcode); if (command == LIO_NOP) { found++; continue; } jobref = fuword(&iocb->_aiocb_private.kernelinfo); TAILQ_FOREACH(cb, &ki->kaio_jobdone, plist) { if (((intptr_t)cb->uaiocb._aiocb_private.kernelinfo) == jobref) { if (cb->uaiocb.aio_lio_opcode == LIO_WRITE) { p->p_stats->p_ru.ru_oublock += cb->outputcharge; cb->outputcharge = 0; } else if (cb->uaiocb.aio_lio_opcode == LIO_READ) { p->p_stats->p_ru.ru_inblock += cb->inputcharge; cb->inputcharge = 0; } found++; break; } } TAILQ_FOREACH(cb, &ki->kaio_bufdone, plist) { if (((intptr_t)cb->uaiocb._aiocb_private.kernelinfo) == jobref) { found++; break; } } } /* * If all I/Os have been disposed of, then we can * return. */ if (found == nentqueued) return (runningcode); ki->kaio_flags |= KAIO_WAKEUP; error = tsleep(p, PRIBIO | PCATCH, "aiospn", 0); if (error == EINTR) return (EINTR); else if (error == EWOULDBLOCK) return (EAGAIN); } } return (runningcode); } /* * Interrupt handler for physio, performs the necessary process wakeups, and * signals. */ static void aio_physwakeup(struct buf *bp) { struct aiocblist *aiocbe; struct proc *userp; mtx_lock(&Giant); bp->b_flags |= B_DONE; wakeup(bp); aiocbe = (struct aiocblist *)bp->b_caller1; if (aiocbe) { userp = aiocbe->userproc; aiocbe->jobstate = JOBST_JOBBFINISHED; aiocbe->uaiocb._aiocb_private.status -= bp->b_resid; aiocbe->uaiocb._aiocb_private.error = 0; aiocbe->jobflags |= AIOCBLIST_DONE; if (bp->b_ioflags & BIO_ERROR) aiocbe->uaiocb._aiocb_private.error = bp->b_error; aio_bio_done_notify(userp, aiocbe, DONE_BUF); } mtx_unlock(&Giant); } /* syscall - wait for the next completion of an aio request */ int aio_waitcomplete(struct thread *td, struct aio_waitcomplete_args *uap) { struct proc *p = td->td_proc; struct timeval atv; struct timespec ts; struct kaioinfo *ki; struct aiocblist *cb = NULL; int error, s, timo; suword(uap->aiocbp, (int)NULL); timo = 0; if (uap->timeout) { /* Get timespec struct. */ error = copyin(uap->timeout, &ts, sizeof(ts)); if (error) return (error); if ((ts.tv_nsec < 0) || (ts.tv_nsec >= 1000000000)) return (EINVAL); TIMESPEC_TO_TIMEVAL(&atv, &ts); if (itimerfix(&atv)) return (EINVAL); timo = tvtohz(&atv); } ki = p->p_aioinfo; if (ki == NULL) return (EAGAIN); for (;;) { PROC_LOCK(p); if ((cb = TAILQ_FIRST(&ki->kaio_jobdone)) != 0) { PROC_UNLOCK(p); suword(uap->aiocbp, (uintptr_t)cb->uuaiocb); td->td_retval[0] = cb->uaiocb._aiocb_private.status; if (cb->uaiocb.aio_lio_opcode == LIO_WRITE) { p->p_stats->p_ru.ru_oublock += cb->outputcharge; cb->outputcharge = 0; } else if (cb->uaiocb.aio_lio_opcode == LIO_READ) { p->p_stats->p_ru.ru_inblock += cb->inputcharge; cb->inputcharge = 0; } error = cb->uaiocb._aiocb_private.error; aio_free_entry(cb); return (error); } s = splbio(); if ((cb = TAILQ_FIRST(&ki->kaio_bufdone)) != 0 ) { PROC_UNLOCK(p); splx(s); suword(uap->aiocbp, (uintptr_t)cb->uuaiocb); error = cb->uaiocb._aiocb_private.error; td->td_retval[0] = cb->uaiocb._aiocb_private.status; aio_free_entry(cb); return (error); } ki->kaio_flags |= KAIO_WAKEUP; error = msleep(p, &p->p_mtx, PDROP | PRIBIO | PCATCH, "aiowc", timo); splx(s); if (error == ERESTART) return (EINTR); else if (error < 0) return (error); else if (error == EINTR) return (EINTR); else if (error == EWOULDBLOCK) return (EAGAIN); } } /* kqueue attach function */ static int filt_aioattach(struct knote *kn) { struct aiocblist *aiocbe = (struct aiocblist *)kn->kn_sdata; /* * The aiocbe pointer must be validated before using it, so * registration is restricted to the kernel; the user cannot * set EV_FLAG1. */ if ((kn->kn_flags & EV_FLAG1) == 0) return (EPERM); kn->kn_flags &= ~EV_FLAG1; knlist_add(&aiocbe->klist, kn, 0); return (0); } /* kqueue detach function */ static void filt_aiodetach(struct knote *kn) { struct aiocblist *aiocbe = (struct aiocblist *)kn->kn_sdata; if (!knlist_empty(&aiocbe->klist)) knlist_remove(&aiocbe->klist, kn, 0); } /* kqueue filter function */ /*ARGSUSED*/ static int filt_aio(struct knote *kn, long hint) { struct aiocblist *aiocbe = (struct aiocblist *)kn->kn_sdata; kn->kn_data = aiocbe->uaiocb._aiocb_private.error; if (aiocbe->jobstate != JOBST_JOBFINISHED && aiocbe->jobstate != JOBST_JOBBFINISHED) return (0); kn->kn_flags |= EV_EOF; return (1); } /* kqueue attach function */ static int filt_lioattach(struct knote *kn) { struct aio_liojob * lj = (struct aio_liojob *)kn->kn_sdata; /* * The aio_liojob pointer must be validated before using it, so * registration is restricted to the kernel; the user cannot * set EV_FLAG1. */ if ((kn->kn_flags & EV_FLAG1) == 0) return (EPERM); kn->kn_flags &= ~EV_FLAG1; knlist_add(&lj->klist, kn, 0); return (0); } /* kqueue detach function */ static void filt_liodetach(struct knote *kn) { struct aio_liojob * lj = (struct aio_liojob *)kn->kn_sdata; if (!knlist_empty(&lj->klist)) knlist_remove(&lj->klist, kn, 0); } /* kqueue filter function */ /*ARGSUSED*/ static int filt_lio(struct knote *kn, long hint) { struct aio_liojob * lj = (struct aio_liojob *)kn->kn_sdata; return (lj->lioj_flags & LIOJ_KEVENT_POSTED); } Index: head/sys/sys/signal.h =================================================================== --- head/sys/sys/signal.h (revision 152028) +++ head/sys/sys/signal.h (revision 152029) @@ -1,423 +1,423 @@ /*- * Copyright (c) 1982, 1986, 1989, 1991, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * 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. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. * * @(#)signal.h 8.4 (Berkeley) 5/4/95 * $FreeBSD$ */ #ifndef _SYS_SIGNAL_H_ #define _SYS_SIGNAL_H_ #include #include #include #include /* __MINSIGSTKSZ */ #include /* sig_atomic_t; trap codes; sigcontext */ /* * System defined signals. */ #if __POSIX_VISIBLE || __XSI_VISIBLE #define SIGHUP 1 /* hangup */ #endif #define SIGINT 2 /* interrupt */ #if __POSIX_VISIBLE || __XSI_VISIBLE #define SIGQUIT 3 /* quit */ #endif #define SIGILL 4 /* illegal instr. (not reset when caught) */ #if __XSI_VISIBLE #define SIGTRAP 5 /* trace trap (not reset when caught) */ #endif #define SIGABRT 6 /* abort() */ #if __BSD_VISIBLE #define SIGIOT SIGABRT /* compatibility */ #define SIGEMT 7 /* EMT instruction */ #endif #define SIGFPE 8 /* floating point exception */ #if __POSIX_VISIBLE || __XSI_VISIBLE #define SIGKILL 9 /* kill (cannot be caught or ignored) */ #endif #if __POSIX_VISIBLE >= 200112 || __XSI_VISIBLE #define SIGBUS 10 /* bus error */ #endif #define SIGSEGV 11 /* segmentation violation */ #if __BSD_VISIBLE #define SIGSYS 12 /* non-existent system call invoked */ #endif #if __POSIX_VISIBLE || __XSI_VISIBLE #define SIGPIPE 13 /* write on a pipe with no one to read it */ #define SIGALRM 14 /* alarm clock */ #endif #define SIGTERM 15 /* software termination signal from kill */ #if __POSIX_VISIBLE >= 200112 || __XSI_VISIBLE #define SIGURG 16 /* urgent condition on IO channel */ #endif #if __POSIX_VISIBLE || __XSI_VISIBLE #define SIGSTOP 17 /* sendable stop signal not from tty */ #define SIGTSTP 18 /* stop signal from tty */ #define SIGCONT 19 /* continue a stopped process */ #define SIGCHLD 20 /* to parent on child stop or exit */ #define SIGTTIN 21 /* to readers pgrp upon background tty read */ #define SIGTTOU 22 /* like TTIN if (tp->t_local<OSTOP) */ #endif #if __BSD_VISIBLE #define SIGIO 23 /* input/output possible signal */ #endif #if __XSI_VISIBLE #define SIGXCPU 24 /* exceeded CPU time limit */ #define SIGXFSZ 25 /* exceeded file size limit */ #define SIGVTALRM 26 /* virtual time alarm */ #define SIGPROF 27 /* profiling time alarm */ #endif #if __BSD_VISIBLE #define SIGWINCH 28 /* window size changes */ #define SIGINFO 29 /* information request */ #endif #if __POSIX_VISIBLE || __XSI_VISIBLE #define SIGUSR1 30 /* user defined signal 1 */ #define SIGUSR2 31 /* user defined signal 2 */ #endif #if __BSD_VISIBLE #define SIGTHR 32 /* reserved by thread library. */ #endif #define SIGRTMIN 65 #define SIGRTMAX 128 #define SIG_DFL ((__sighandler_t *)0) #define SIG_IGN ((__sighandler_t *)1) #define SIG_ERR ((__sighandler_t *)-1) /* * XXX missing SIG_HOLD. */ /*- * Type of a signal handling function. * * Language spec sez signal handlers take exactly one arg, even though we * actually supply three. Ugh! * * We don't try to hide the difference by leaving out the args because * that would cause warnings about conformant programs. Nonconformant * programs can avoid the warnings by casting to (__sighandler_t *) or * sig_t before calling signal() or assigning to sa_handler or sv_handler. * * The kernel should reverse the cast before calling the function. It * has no way to do this, but on most machines 1-arg and 3-arg functions * have the same calling protocol so there is no problem in practice. * A bit in sa_flags could be used to specify the number of args. */ typedef void __sighandler_t(int); #if __POSIX_VISIBLE || __XSI_VISIBLE #ifndef _SIGSET_T_DECLARED #define _SIGSET_T_DECLARED typedef __sigset_t sigset_t; #endif #endif #if __POSIX_VISIBLE >= 199309 || __XSI_VISIBLE >= 500 union sigval { /* Members as suggested by Annex C of POSIX 1003.1b. */ - int sigval_int; - void *sigval_ptr; + int sival_int; + void *sival_ptr; }; #endif #if __POSIX_VISIBLE >= 199309 struct sigevent { int sigev_notify; /* Notification type */ int sigev_signo; /* Signal number */ union sigval sigev_value; /* Signal value */ union { __lwpid_t _threadid; struct { - void (*_function)(union sigval *); + void (*_function)(union sigval); void *_attribute; /* pthread_attr_t * */ } _sigev_thread; } _sigev_un; }; #if __BSD_VISIBLE #define sigev_notify_kqueue sigev_signo #define sigev_notify_thread_id _sigev_un._threadid #endif #define sigev_notify_function _sigev_un._sigev_thread._function #define sigev_notify_attributes _sigev_un._sigev_thread._attribute #define SIGEV_NONE 0 /* No async notification. */ #define SIGEV_SIGNAL 1 /* Generate a queued signal. */ #define SIGEV_THREAD 2 /* Call back from another pthread. */ #if __BSD_VISIBLE #define SIGEV_KEVENT 3 /* Generate a kevent. */ #define SIGEV_THREAD_ID 4 /* Send signal to a kernel thread. */ #endif #endif /* __POSIX_VISIBLE >= 199309 */ #if __POSIX_VISIBLE >= 199309 || __XSI_VISIBLE typedef struct __siginfo { int si_signo; /* signal number */ int si_errno; /* errno association */ /* * Cause of signal, one of the SI_ macros or signal-specific * values, i.e. one of the FPE_... values for SIGFPE. This * value is equivalent to the second argument to an old-style * FreeBSD signal handler. */ int si_code; /* signal code */ __pid_t si_pid; /* sending process */ __uid_t si_uid; /* sender's ruid */ int si_status; /* exit value */ void *si_addr; /* faulting instruction */ union sigval si_value; /* signal value */ long si_band; /* band event for SIGPOLL */ union { struct { int _trapno;/* machine specific trap code */ } _fault; struct { int _timerid; int _overrun; } _timer; int __spare__[7]; /* gimme some slack */ } _reason; } siginfo_t; #define si_trapno _reason._fault._trapno #define si_timerid _reason._timer._timerid #define si_overrun _reason._timer._overrun /** si_code **/ /* codes for SIGILL */ #define ILL_ILLOPC 1 /* Illegal opcode. */ #define ILL_ILLOPN 2 /* Illegal operand. */ #define ILL_ILLADR 3 /* Illegal addressing mode. */ #define ILL_ILLTRP 4 /* Illegal trap. */ #define ILL_PRVOPC 5 /* Privileged opcode. */ #define ILL_PRVREG 6 /* Privileged register. */ #define ILL_COPROC 7 /* Coprocessor error. */ #define ILL_BADSTK 8 /* Internal stack error. */ /* codes for SIGBUS */ #define BUS_ADRALN 1 /* Invalid address alignment. */ #define BUS_ADRERR 2 /* Nonexistent physical address. */ #define BUS_OBJERR 3 /* Object-specific hardware error. */ /* codes for SIGSEGV */ #define SEGV_MAPERR 1 /* Address not mapped to object. */ #define SEGV_ACCERR 2 /* Invalid permissions for mapped */ /* object. */ /* codes for SIGFPE */ #define FPE_INTOVF 1 /* Integer overflow. */ #define FPE_INTDIV 2 /* Integer divide by zero. */ #define FPE_FLTDIV 3 /* Floating point divide by zero. */ #define FPE_FLTOVF 4 /* Floating point overflow. */ #define FPE_FLTUND 5 /* Floating point underflow. */ #define FPE_FLTRES 6 /* Floating point inexact result. */ #define FPE_FLTINV 7 /* Invalid floating point operation. */ #define FPE_FLTSUB 8 /* Subscript out of range. */ /* codes for SIGTRAP */ #define TRAP_BRKPT 1 /* Process breakpoint. */ #define TRAP_TRACE 2 /* Process trace trap. */ /* codes for SIGCHLD */ #define CLD_EXITED 1 /* Child has exited */ #define CLD_KILLED 2 /* Child has terminated abnormally but */ /* did not create a core file */ #define CLD_DUMPED 3 /* Child has terminated abnormally and */ /* created a core file */ #define CLD_TRAPPED 4 /* Traced child has trapped */ #define CLD_STOPPED 5 /* Child has stopped */ #define CLD_CONTINUED 6 /* Stopped child has continued */ /* codes for SIGPOLL */ #define POLL_IN 1 /* Data input available */ #define POLL_OUT 2 /* Output buffers available */ #define POLL_MSG 3 /* Input message available */ #define POLL_ERR 4 /* I/O Error */ #define POLL_PRI 5 /* High priority input available */ #define POLL_HUP 4 /* Device disconnected */ #endif #if __POSIX_VISIBLE || __XSI_VISIBLE struct __siginfo; /* * Signal vector "template" used in sigaction call. */ struct sigaction { union { void (*__sa_handler)(int); void (*__sa_sigaction)(int, struct __siginfo *, void *); } __sigaction_u; /* signal handler */ int sa_flags; /* see signal options below */ sigset_t sa_mask; /* signal mask to apply */ }; #define sa_handler __sigaction_u.__sa_handler #endif #if __XSI_VISIBLE /* If SA_SIGINFO is set, sa_sigaction must be used instead of sa_handler. */ #define sa_sigaction __sigaction_u.__sa_sigaction #endif #if __POSIX_VISIBLE || __XSI_VISIBLE #define SA_NOCLDSTOP 0x0008 /* do not generate SIGCHLD on child stop */ #endif /* __POSIX_VISIBLE || __XSI_VISIBLE */ #if __XSI_VISIBLE #define SA_ONSTACK 0x0001 /* take signal on signal stack */ #define SA_RESTART 0x0002 /* restart system call on signal return */ #define SA_RESETHAND 0x0004 /* reset to SIG_DFL when taking signal */ #define SA_NODEFER 0x0010 /* don't mask the signal we're delivering */ #define SA_NOCLDWAIT 0x0020 /* don't keep zombies around */ #define SA_SIGINFO 0x0040 /* signal handler with SA_SIGINFO args */ #endif #if __BSD_VISIBLE #define NSIG 32 /* number of old signals (counting 0) */ #endif #if __POSIX_VISIBLE || __XSI_VISIBLE #define SI_USER 0x10001 /* Signal sent by kill(). */ #define SI_QUEUE 0x10002 /* Signal sent by the sigqueue(). */ #define SI_TIMER 0x10003 /* Signal generated by expiration of */ /* a timer set by timer_settime(). */ #define SI_ASYNCIO 0x10004 /* Signal generated by completion of */ /* an asynchronous I/O request.*/ #define SI_MESGQ 0x10005 /* Signal generated by arrival of a */ /* message on an empty message queue. */ #endif #if __BSD_VISIBLE #define SI_UNDEFINED 0 #endif #if __BSD_VISIBLE typedef __sighandler_t *sig_t; /* type of pointer to a signal function */ typedef void __siginfohandler_t(int, struct __siginfo *, void *); #endif #if __XSI_VISIBLE /* * Structure used in sigaltstack call. */ #if __BSD_VISIBLE typedef struct sigaltstack { #else typedef struct { #endif char *ss_sp; /* signal stack base */ __size_t ss_size; /* signal stack length */ int ss_flags; /* SS_DISABLE and/or SS_ONSTACK */ } stack_t; #define SS_ONSTACK 0x0001 /* take signal on alternate stack */ #define SS_DISABLE 0x0004 /* disable taking signals on alternate stack */ #define MINSIGSTKSZ __MINSIGSTKSZ /* minimum stack size */ #define SIGSTKSZ (MINSIGSTKSZ + 32768) /* recommended stack size */ #endif #if __BSD_VISIBLE /* * 4.3 compatibility: * Signal vector "template" used in sigvec call. */ struct sigvec { __sighandler_t *sv_handler; /* signal handler */ int sv_mask; /* signal mask to apply */ int sv_flags; /* see signal options below */ }; #define SV_ONSTACK SA_ONSTACK #define SV_INTERRUPT SA_RESTART /* same bit, opposite sense */ #define SV_RESETHAND SA_RESETHAND #define SV_NODEFER SA_NODEFER #define SV_NOCLDSTOP SA_NOCLDSTOP #define SV_SIGINFO SA_SIGINFO #define sv_onstack sv_flags /* isn't compatibility wonderful! */ #endif /* Keep this in one place only */ #if defined(_KERNEL) && defined(COMPAT_43) && \ !defined(__i386__) && !defined(__alpha__) struct osigcontext { int _not_used; }; #endif #if __XSI_VISIBLE /* * Structure used in sigstack call. */ struct sigstack { /* XXX ss_sp's type should be `void *'. */ char *ss_sp; /* signal stack pointer */ int ss_onstack; /* current status */ }; #endif #if __BSD_VISIBLE || __POSIX_VISIBLE > 0 && __POSIX_VISIBLE <= 200112 /* * Macro for converting signal number to a mask suitable for * sigblock(). */ #define sigmask(m) (1 << ((m)-1)) #endif #if __BSD_VISIBLE #define BADSIG SIG_ERR #endif #if __POSIX_VISIBLE || __XSI_VISIBLE /* * Flags for sigprocmask: */ #define SIG_BLOCK 1 /* block specified signal set */ #define SIG_UNBLOCK 2 /* unblock specified signal set */ #define SIG_SETMASK 3 /* set specified signal set */ #endif /* * For historical reasons; programs expect signal's return value to be * defined by . */ __BEGIN_DECLS __sighandler_t *signal(int, __sighandler_t *); __END_DECLS #endif /* !_SYS_SIGNAL_H_ */