Index: sys/kern/kern_exit.c =================================================================== --- sys/kern/kern_exit.c +++ sys/kern/kern_exit.c @@ -440,7 +440,6 @@ sx_xlock(&allproc_lock); LIST_REMOVE(p, p_list); LIST_INSERT_HEAD(&zombproc, p, p_list); - LIST_REMOVE(p, p_hash); sx_xunlock(&allproc_lock); /* @@ -883,6 +882,9 @@ sx_xlock(&allproc_lock); LIST_REMOVE(p, p_list); /* off zombproc */ sx_xunlock(&allproc_lock); + sx_xlock(PIDHASHLOCK(p->p_pid)); + LIST_REMOVE(p, p_hash); + sx_xunlock(PIDHASHLOCK(p->p_pid)); LIST_REMOVE(p, p_sibling); reaper_abandon_children(p, true); LIST_REMOVE(p, p_reapsibling); Index: sys/kern/kern_fork.c =================================================================== --- sys/kern/kern_fork.c +++ sys/kern/kern_fork.c @@ -406,7 +406,9 @@ AUDIT_ARG_PID(p2->p_pid); LIST_INSERT_HEAD(&allproc, p2, p_list); allproc_gen++; + sx_xlock(PIDHASHLOCK(p2->p_pid)); LIST_INSERT_HEAD(PIDHASH(p2->p_pid), p2, p_hash); + sx_xunlock(PIDHASHLOCK(p2->p_pid)); PROC_LOCK(p2); PROC_LOCK(p1); Index: sys/kern/kern_proc.c =================================================================== --- sys/kern/kern_proc.c +++ sys/kern/kern_proc.c @@ -127,7 +127,9 @@ * Other process lists */ struct pidhashhead *pidhashtbl; +struct sx *pidhashtbl_lock; u_long pidhash; +u_long pidhashlock; struct pgrphashhead *pgrphashtbl; u_long pgrphash; struct proclist allproc; @@ -182,6 +184,7 @@ void procinit(void) { + u_long i; sx_init(&allproc_lock, "allproc"); sx_init(&proctree_lock, "proctree"); @@ -189,6 +192,14 @@ LIST_INIT(&allproc); LIST_INIT(&zombproc); pidhashtbl = hashinit(maxproc / 4, M_PROC, &pidhash); + pidhashlock = (pidhash + 1) / 64; + if (pidhashlock > 0) + pidhashlock--; + pidhashtbl_lock = malloc(sizeof(*pidhashtbl_lock) * (pidhashlock + 1), + M_PROC, M_WAITOK | M_ZERO); + for (i = 0; i < pidhashlock + 1; i++) + sx_init(&pidhashtbl_lock[i], "pidhash"); + pgrphashtbl = hashinit(maxproc / 4, M_PROC, &pgrphash); proc_zone = uma_zcreate("PROC", sched_sizeof_proc(), proc_ctor, proc_dtor, proc_init, proc_fini, @@ -321,7 +332,7 @@ LIST_FOREACH(p, PIDHASH(pid), p_hash) { if (p->p_pid == pid) { PROC_LOCK(p); - if (p->p_state == PRS_NEW) { + if (p->p_state == PRS_NEW || p->p_state == PRS_ZOMBIE) { PROC_UNLOCK(p); p = NULL; } @@ -1438,13 +1449,145 @@ return (0); } +int +proc_iterate(int (*cb)(struct proc *, void *), void *cbarg) +{ + struct proc *p; + int i, j, error; + + error = 0; + for (i = 0; i < pidhashlock + 1; i++) { + sx_slock(&pidhashtbl_lock[i]); + for (j = i; j <= pidhash; j += pidhashlock + 1) { + LIST_FOREACH(p, &pidhashtbl[j], p_hash) { + if (p->p_state == PRS_NEW) + continue; + error = cb(p, cbarg); + PROC_LOCK_ASSERT(p, MA_NOTOWNED); + if (error) + goto out_err; + } + } + sx_sunlock(&pidhashtbl_lock[i]); + } + return (0); +out_err: + sx_sunlock(&pidhashtbl_lock[i]); + return (error); +} + +struct kern_proc_out_args { + struct sysctl_req *req; + int flags; + int oid_number; + int *name; +}; + +static int +sysctl_kern_proc_iterate(struct proc *p, void *origarg) +{ + struct kern_proc_out_args *arg = origarg; + int *name = arg->name; + int oid_number = arg->oid_number; + int flags = arg->flags; + struct sysctl_req *req = arg->req; + int error = 0; + + PROC_LOCK(p); + + KASSERT(p->p_ucred != NULL, + ("process credential is NULL for non-NEW proc")); + /* + * Show a user only appropriate processes. + */ + if (p_cansee(curthread, p)) { + goto skip; + } + /* + * TODO - make more efficient (see notes below). + * do by session. + */ + switch (oid_number) { + + case KERN_PROC_GID: + if (p->p_ucred->cr_gid != (gid_t)name[0]) { + goto skip; + } + break; + + case KERN_PROC_PGRP: + /* could do this by traversing pgrp */ + if (p->p_pgrp == NULL || + p->p_pgrp->pg_id != (pid_t)name[0]) { + goto skip; + } + break; + + case KERN_PROC_RGID: + if (p->p_ucred->cr_rgid != (gid_t)name[0]) { + goto skip; + } + break; + + case KERN_PROC_SESSION: + if (p->p_session == NULL || + p->p_session->s_sid != (pid_t)name[0]) { + goto skip; + } + break; + + case KERN_PROC_TTY: + if ((p->p_flag & P_CONTROLT) == 0 || + p->p_session == NULL) { + goto skip; + } + /* XXX proctree_lock */ + SESS_LOCK(p->p_session); + if (p->p_session->s_ttyp == NULL || + tty_udev(p->p_session->s_ttyp) != + (dev_t)name[0]) { + SESS_UNLOCK(p->p_session); + goto skip; + } + SESS_UNLOCK(p->p_session); + break; + + case KERN_PROC_UID: + if (p->p_ucred->cr_uid != (uid_t)name[0]) { + goto skip; + } + break; + + case KERN_PROC_RUID: + if (p->p_ucred->cr_ruid != (uid_t)name[0]) { + goto skip; + } + break; + + case KERN_PROC_PROC: + break; + + default: + break; + + } + + error = sysctl_out_proc(p, req, flags); + PROC_LOCK_ASSERT(p, MA_NOTOWNED); + return (error); +skip: + PROC_UNLOCK(p); + return (0); +} + static int sysctl_kern_proc(SYSCTL_HANDLER_ARGS) { + struct kern_proc_out_args iterarg; int *name = (int *)arg1; u_int namelen = arg2; struct proc *p; - int flags, doingzomb, oid_number; + int flags, oid_number; int error = 0; oid_number = oidp->oid_number; @@ -1504,112 +1647,11 @@ */ sx_slock(&proctree_lock); } - sx_slock(&allproc_lock); - for (doingzomb=0 ; doingzomb < 2 ; doingzomb++) { - if (!doingzomb) - p = LIST_FIRST(&allproc); - else - p = LIST_FIRST(&zombproc); - for (; p != NULL; p = LIST_NEXT(p, p_list)) { - /* - * Skip embryonic processes. - */ - if (p->p_state == PRS_NEW) - continue; - PROC_LOCK(p); - KASSERT(p->p_ucred != NULL, - ("process credential is NULL for non-NEW proc")); - /* - * Show a user only appropriate processes. - */ - if (p_cansee(curthread, p)) { - PROC_UNLOCK(p); - continue; - } - /* - * TODO - make more efficient (see notes below). - * do by session. - */ - switch (oid_number) { - - case KERN_PROC_GID: - if (p->p_ucred->cr_gid != (gid_t)name[0]) { - PROC_UNLOCK(p); - continue; - } - break; - - case KERN_PROC_PGRP: - /* could do this by traversing pgrp */ - if (p->p_pgrp == NULL || - p->p_pgrp->pg_id != (pid_t)name[0]) { - PROC_UNLOCK(p); - continue; - } - break; - - case KERN_PROC_RGID: - if (p->p_ucred->cr_rgid != (gid_t)name[0]) { - PROC_UNLOCK(p); - continue; - } - break; - - case KERN_PROC_SESSION: - if (p->p_session == NULL || - p->p_session->s_sid != (pid_t)name[0]) { - PROC_UNLOCK(p); - continue; - } - break; - - case KERN_PROC_TTY: - if ((p->p_flag & P_CONTROLT) == 0 || - p->p_session == NULL) { - PROC_UNLOCK(p); - continue; - } - /* XXX proctree_lock */ - SESS_LOCK(p->p_session); - if (p->p_session->s_ttyp == NULL || - tty_udev(p->p_session->s_ttyp) != - (dev_t)name[0]) { - SESS_UNLOCK(p->p_session); - PROC_UNLOCK(p); - continue; - } - SESS_UNLOCK(p->p_session); - break; - - case KERN_PROC_UID: - if (p->p_ucred->cr_uid != (uid_t)name[0]) { - PROC_UNLOCK(p); - continue; - } - break; - - case KERN_PROC_RUID: - if (p->p_ucred->cr_ruid != (uid_t)name[0]) { - PROC_UNLOCK(p); - continue; - } - break; - - case KERN_PROC_PROC: - break; - - default: - break; - - } - - error = sysctl_out_proc(p, req, flags); - if (error) - goto out; - } - } -out: - sx_sunlock(&allproc_lock); + iterarg.flags = flags; + iterarg.oid_number = oid_number; + iterarg.req = req; + iterarg.name = name; + error = proc_iterate(sysctl_kern_proc_iterate, &iterarg); if (req->oldptr != NULL) sx_sunlock(&proctree_lock); return (error); Index: sys/kern/kern_sig.c =================================================================== --- sys/kern/kern_sig.c +++ sys/kern/kern_sig.c @@ -1656,9 +1656,43 @@ * Common code for kill process group/broadcast kill. * cp is calling process. */ + +struct killpg_iter_arg { + int sig; + int *ret; + ksiginfo_t *ksi; + struct thread *td; +}; + +static int +killpg_iter(struct proc *p, void *origarg) +{ + struct killpg_iter_arg *arg = origarg; + int err; + + if (p->p_pid <= 1 || p->p_flag & P_SYSTEM || + p == arg->td->td_proc) + return (0); + PROC_LOCK(p); + if (p->p_state == PRS_ZOMBIE) + goto out; + err = p_cansignal(arg->td, p, arg->sig); + if (err == 0) { + if (arg->sig) + pksignal(p, arg->sig, arg->ksi); + *arg->ret = err; + } + else if (*arg->ret == ESRCH) + *arg->ret = err; +out: + PROC_UNLOCK(p); + return (0); +} + static int killpg1(struct thread *td, int sig, int pgid, int all, ksiginfo_t *ksi) { + struct killpg_iter_arg iterarg; struct proc *p; struct pgrp *pgrp; int err; @@ -1669,24 +1703,11 @@ /* * broadcast */ - sx_slock(&allproc_lock); - FOREACH_PROC_IN_SYSTEM(p) { - if (p->p_pid <= 1 || p->p_flag & P_SYSTEM || - p == td->td_proc || p->p_state == PRS_NEW) { - continue; - } - PROC_LOCK(p); - err = p_cansignal(td, p, sig); - if (err == 0) { - if (sig) - pksignal(p, sig, ksi); - ret = err; - } - else if (ret == ESRCH) - ret = err; - PROC_UNLOCK(p); - } - sx_sunlock(&allproc_lock); + iterarg.sig = sig; + iterarg.ret = &ret; + iterarg.ksi = ksi; + iterarg.td = td; + proc_iterate(killpg_iter, &iterarg); } else { sx_slock(&proctree_lock); if (pgid == 0) { Index: sys/sys/proc.h =================================================================== --- sys/sys/proc.h +++ sys/sys/proc.h @@ -940,8 +940,11 @@ #define THREAD_CAN_SLEEP() ((curthread)->td_no_sleeping == 0) #define PIDHASH(pid) (&pidhashtbl[(pid) & pidhash]) +#define PIDHASHLOCK(pid) (&pidhashtbl_lock[((pid) & pidhashlock)]) extern LIST_HEAD(pidhashhead, proc) *pidhashtbl; +extern struct sx *pidhashtbl_lock; extern u_long pidhash; +extern u_long pidhashlock; #define TIDHASH(tid) (&tidhashtbl[(tid) & tidhash]) extern LIST_HEAD(tidhashhead, thread) *tidhashtbl; extern u_long tidhash; @@ -1044,6 +1047,7 @@ int proc_getauxv(struct thread *td, struct proc *p, struct sbuf *sb); int proc_getenvv(struct thread *td, struct proc *p, struct sbuf *sb); void procinit(void); +int proc_iterate(int (*cb)(struct proc *, void *), void *cbarg); void proc_linkup0(struct proc *p, struct thread *td); void proc_linkup(struct proc *p, struct thread *td); struct proc *proc_realparent(struct proc *child);