diff --git a/sys/kern/kern_event.c b/sys/kern/kern_event.c index aa3d4a8..0d536fc 100644 --- a/sys/kern/kern_event.c +++ b/sys/kern/kern_event.c @@ -403,30 +403,63 @@ filt_proc(struct knote *kn, long hint) return (1); } - /* - * process forked, and user wants to track the new process, - * so attach a new knote to it, and immediately report an - * event with the parent's pid. - */ - if ((event == NOTE_FORK) && (kn->kn_sfflags & NOTE_TRACK)) { - struct kevent kev; - int error; + return (kn->kn_fflags != 0); +} - /* - * register knote with new process. - */ - kev.ident = hint & NOTE_PDATAMASK; /* pid */ - kev.filter = kn->kn_filter; - kev.flags = kn->kn_flags | EV_ADD | EV_ENABLE | EV_FLAG1; - kev.fflags = kn->kn_sfflags; - kev.data = kn->kn_id; /* parent */ - kev.udata = kn->kn_kevent.udata; /* preserve udata */ - error = kqueue_register(kn->kn_kq, &kev, NULL, 0); - if (error) - kn->kn_fflags |= NOTE_TRACKERR; - } +/* + * Called when the process forked. For each knote attached to the + * parent, check whether user wants to track the new process. If so + * attach a new knote to it, and immediately report an event with the + * parent's pid. + */ - return (kn->kn_fflags != 0); +void +knote_fork(struct knlist *list, int pid) +{ + struct kqueue *kq; + struct knote *kn; + struct kevent kev; + int error; + + if (list == NULL) + return; + list->kl_lock(list->kl_lockarg); + + SLIST_FOREACH(kn, &list->kl_list, kn_selnext) { + kq = kn->kn_kq; + if ((kn->kn_status & KN_INFLUX) != KN_INFLUX) { + KQ_LOCK(kq); + if ((kn->kn_status & KN_INFLUX) != KN_INFLUX && + (kn->kn_sfflags & NOTE_TRACK)) { + kn->kn_status |= KN_INFLUX; + KQ_UNLOCK(kq); + list->kl_unlock(list->kl_lockarg); + + /* + * register knote with new process. + */ + kev.ident = pid; + kev.filter = kn->kn_filter; + kev.flags = kn->kn_flags | EV_ADD | EV_ENABLE | + EV_FLAG1; + kev.fflags = kn->kn_sfflags; + kev.data = kn->kn_id; /* parent */ + kev.udata = kn->kn_kevent.udata;/* preserve udata */ + error = kqueue_register(kq, &kev, NULL, 0); + if (kn->kn_fop->f_event(kn, NOTE_FORK | pid)) + KNOTE_ACTIVATE(kn, 1); + if (error) + kn->kn_fflags |= NOTE_TRACKERR; + KQ_LOCK(kq); + kn->kn_status &= ~KN_INFLUX; + KQ_UNLOCK_FLUX(kq); + list->kl_lock(list->kl_lockarg); + } else + KQ_UNLOCK(kq); + } + kq = NULL; + } + list->kl_unlock(list->kl_lockarg); } static int @@ -1259,6 +1292,7 @@ start: ~(KN_QUEUED | KN_ACTIVE | KN_INFLUX); kq->kq_count--; KN_LIST_UNLOCK(kn); + KQ_FLUX_WAKEUP(kq); continue; } *kevp = kn->kn_kevent; @@ -1274,6 +1308,7 @@ start: kn->kn_status &= ~(KN_INFLUX); KN_LIST_UNLOCK(kn); + KQ_FLUX_WAKEUP(kq); } /* we are returning a copy to the user */ @@ -1452,11 +1487,13 @@ kqueue_close(struct file *fp, struct thread *td) KASSERT(knlist_empty(&kq->kq_sel.si_note), ("kqueue's knlist not empty")); - for (i = 0; i < kq->kq_knlistsize; i++) { while ((kn = SLIST_FIRST(&kq->kq_knlist[i])) != NULL) { - KASSERT((kn->kn_status & KN_INFLUX) == 0, - ("KN_INFLUX set when not suppose to be")); + if (kn->kn_status & KN_INFLUX) { + kq->kq_state |= KQ_FLUXWAIT; + msleep(kq, &kq->kq_lock, PSOCK, "kqclo1", 0); + continue; + } kn->kn_status |= KN_INFLUX; KQ_UNLOCK(kq); if (!(kn->kn_status & KN_DETACHED)) @@ -1468,8 +1505,12 @@ kqueue_close(struct file *fp, struct thread *td) if (kq->kq_knhashmask != 0) { for (i = 0; i <= kq->kq_knhashmask; i++) { while ((kn = SLIST_FIRST(&kq->kq_knhash[i])) != NULL) { - KASSERT((kn->kn_status & KN_INFLUX) == 0, - ("KN_INFLUX set when not suppose to be")); + if (kn->kn_status & KN_INFLUX) { + kq->kq_state |= KQ_FLUXWAIT; + msleep(kq, &kq->kq_lock, PSOCK, + "kqclo2", 0); + continue; + } kn->kn_status |= KN_INFLUX; KQ_UNLOCK(kq); if (!(kn->kn_status & KN_DETACHED)) diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c index 223f78a..38c4e91 100644 --- a/sys/kern/kern_fork.c +++ b/sys/kern/kern_fork.c @@ -715,6 +715,8 @@ again: PROC_UNLOCK(p1); + knote_fork(&p1->p_klist, p2->p_pid); + /* * Preserve synchronization semantics of vfork. If waiting for * child to exec or exit, set P_PPWAIT on child, and sleep on our diff --git a/sys/kern/sys_pipe.c b/sys/kern/sys_pipe.c index c711b80..8375128 100644 --- a/sys/kern/sys_pipe.c +++ b/sys/kern/sys_pipe.c @@ -265,8 +265,8 @@ pipe_zone_ctor(void *mem, int size, void *arg, int flags) * one at a time. When both are free'd, then the whole pair * is released. */ - rpipe->pipe_present = 1; - wpipe->pipe_present = 1; + rpipe->pipe_present = PIPE_ACTIVE; + wpipe->pipe_present = PIPE_ACTIVE; /* * Eventually, the MAC Framework may initialize the label @@ -975,7 +975,8 @@ pipe_write(fp, uio, active_cred, flags, td) /* * detect loss of pipe read side, issue SIGPIPE if lost. */ - if ((!wpipe->pipe_present) || (wpipe->pipe_state & PIPE_EOF)) { + if (wpipe->pipe_present != PIPE_ACTIVE || + (wpipe->pipe_state & PIPE_EOF)) { pipeunlock(wpipe); PIPE_UNLOCK(rpipe); return (EPIPE); @@ -1346,13 +1347,14 @@ pipe_poll(fp, events, active_cred, td) revents |= events & (POLLIN | POLLRDNORM); if (events & (POLLOUT | POLLWRNORM)) - if (!wpipe->pipe_present || (wpipe->pipe_state & PIPE_EOF) || + if (wpipe->pipe_present != PIPE_ACTIVE || + (wpipe->pipe_state & PIPE_EOF) || (((wpipe->pipe_state & PIPE_DIRECTW) == 0) && (wpipe->pipe_buffer.size - wpipe->pipe_buffer.cnt) >= PIPE_BUF)) revents |= events & (POLLOUT | POLLWRNORM); if ((rpipe->pipe_state & PIPE_EOF) || - (!wpipe->pipe_present) || + wpipe->pipe_present != PIPE_ACTIVE || (wpipe->pipe_state & PIPE_EOF)) revents |= POLLHUP; @@ -1493,7 +1495,7 @@ pipeclose(cpipe) * Disconnect from peer, if any. */ ppipe = cpipe->pipe_peer; - if (ppipe->pipe_present != 0) { + if (ppipe->pipe_present == PIPE_ACTIVE) { pipeselwakeup(ppipe); ppipe->pipe_state |= PIPE_EOF; @@ -1510,16 +1512,23 @@ pipeclose(cpipe) PIPE_UNLOCK(cpipe); pipe_free_kmem(cpipe); PIPE_LOCK(cpipe); - cpipe->pipe_present = 0; + cpipe->pipe_present = PIPE_CLOSING; pipeunlock(cpipe); + + /* + * knlist_clear() may sleep dropping the PIPE_MTX. Set the + * PIPE_FINALIZED, that allows other end to free the + * pipe_pair, only after the knotes are completely dismantled. + */ knlist_clear(&cpipe->pipe_sel.si_note, 1); + cpipe->pipe_present = PIPE_FINALIZED; knlist_destroy(&cpipe->pipe_sel.si_note); /* * If both endpoints are now closed, release the memory for the * pipe pair. If not, unlock. */ - if (ppipe->pipe_present == 0) { + if (ppipe->pipe_present == PIPE_FINALIZED) { PIPE_UNLOCK(cpipe); #ifdef MAC mac_pipe_destroy(pp); @@ -1543,7 +1552,7 @@ pipe_kqfilter(struct file *fp, struct knote *kn) break; case EVFILT_WRITE: kn->kn_fop = &pipe_wfiltops; - if (!cpipe->pipe_peer->pipe_present) { + if (cpipe->pipe_peer->pipe_present != PIPE_ACTIVE) { /* other end of pipe has been closed */ PIPE_UNLOCK(cpipe); return (EPIPE); @@ -1566,13 +1575,8 @@ filt_pipedetach(struct knote *kn) struct pipe *cpipe = (struct pipe *)kn->kn_fp->f_data; PIPE_LOCK(cpipe); - if (kn->kn_filter == EVFILT_WRITE) { - if (!cpipe->pipe_peer->pipe_present) { - PIPE_UNLOCK(cpipe); - return; - } + if (kn->kn_filter == EVFILT_WRITE) cpipe = cpipe->pipe_peer; - } knlist_remove(&cpipe->pipe_sel.si_note, kn, 1); PIPE_UNLOCK(cpipe); } @@ -1591,7 +1595,8 @@ filt_piperead(struct knote *kn, long hint) kn->kn_data = rpipe->pipe_map.cnt; if ((rpipe->pipe_state & PIPE_EOF) || - (!wpipe->pipe_present) || (wpipe->pipe_state & PIPE_EOF)) { + wpipe->pipe_present != PIPE_ACTIVE || + (wpipe->pipe_state & PIPE_EOF)) { kn->kn_flags |= EV_EOF; PIPE_UNLOCK(rpipe); return (1); @@ -1609,7 +1614,8 @@ filt_pipewrite(struct knote *kn, long hint) struct pipe *wpipe = rpipe->pipe_peer; PIPE_LOCK(rpipe); - if ((!wpipe->pipe_present) || (wpipe->pipe_state & PIPE_EOF)) { + if (wpipe->pipe_present != PIPE_ACTIVE || + (wpipe->pipe_state & PIPE_EOF)) { kn->kn_data = 0; kn->kn_flags |= EV_EOF; PIPE_UNLOCK(rpipe); diff --git a/sys/sys/event.h b/sys/sys/event.h index 167056c..99f6bca 100644 --- a/sys/sys/event.h +++ b/sys/sys/event.h @@ -205,6 +205,7 @@ struct proc; struct knlist; extern void knote(struct knlist *list, long hint, int islocked); +extern void knote_fork(struct knlist *list, int pid); extern void knlist_add(struct knlist *knl, struct knote *kn, int islocked); extern void knlist_remove(struct knlist *knl, struct knote *kn, int islocked); extern void knlist_remove_inevent(struct knlist *knl, struct knote *kn); diff --git a/sys/sys/pipe.h b/sys/sys/pipe.h index 7f599f5..6659e6e 100644 --- a/sys/sys/pipe.h +++ b/sys/sys/pipe.h @@ -115,6 +115,13 @@ struct pipe { }; /* + * Values for the pipe_present. + */ +#define PIPE_ACTIVE 1 +#define PIPE_CLOSING 2 +#define PIPE_FINALIZED 3 + +/* * Container structure to hold the two pipe endpoints, mutex, and label * pointer. */