Date: Wed, 21 Nov 2018 18:56:15 +0000 (UTC) From: Mateusz Guzik <mjg@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r340742 - in head/sys: kern sys Message-ID: <201811211856.wALIuFvk006942@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: mjg Date: Wed Nov 21 18:56:15 2018 New Revision: 340742 URL: https://svnweb.freebsd.org/changeset/base/340742 Log: proc: implement pid hash locks and an iterator forks, exits and waits are frequently stalled during poudriere -j 128 runs due to killpg and process list exports performed for each package. Both uses take the allproc lock. The latter case can be modified to iterate over the hash with finer grained locking instead. Reviewed by: kib Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D17817 Modified: head/sys/kern/kern_exit.c head/sys/kern/kern_fork.c head/sys/kern/kern_proc.c head/sys/sys/proc.h Modified: head/sys/kern/kern_exit.c ============================================================================== --- head/sys/kern/kern_exit.c Wed Nov 21 18:54:38 2018 (r340741) +++ head/sys/kern/kern_exit.c Wed Nov 21 18:56:15 2018 (r340742) @@ -435,7 +435,6 @@ exit1(struct thread *td, int rval, int signo) 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); /* @@ -876,6 +875,9 @@ proc_reap(struct thread *td, struct proc *p, int *stat 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); Modified: head/sys/kern/kern_fork.c ============================================================================== --- head/sys/kern/kern_fork.c Wed Nov 21 18:54:38 2018 (r340741) +++ head/sys/kern/kern_fork.c Wed Nov 21 18:56:15 2018 (r340742) @@ -406,7 +406,9 @@ do_fork(struct thread *td, struct fork_req *fr, struct 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); Modified: head/sys/kern/kern_proc.c ============================================================================== --- head/sys/kern/kern_proc.c Wed Nov 21 18:54:38 2018 (r340741) +++ head/sys/kern/kern_proc.c Wed Nov 21 18:56:15 2018 (r340742) @@ -118,7 +118,9 @@ static struct proc *zpfind_locked(pid_t pid); * 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; @@ -173,6 +175,7 @@ CTASSERT(sizeof(struct kinfo_proc32) == KINFO_PROC32_S void procinit(void) { + u_long i; sx_init(&allproc_lock, "allproc"); sx_init(&proctree_lock, "proctree"); @@ -180,6 +183,13 @@ procinit(void) 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, @@ -306,7 +316,7 @@ pfind_locked(pid_t pid) 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; } @@ -1421,13 +1431,134 @@ sysctl_out_proc(struct proc *p, struct sysctl_req *req return (0); } +int +proc_iterate(int (*cb)(struct proc *, void *), void *cbarg) +{ + struct proc *p; + int error, i, j; + + 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 != 0) { + sx_sunlock(&pidhashtbl_lock[i]); + return (error); + } + } + } + sx_sunlock(&pidhashtbl_lock[i]); + } + return (0); +} + +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; @@ -1479,112 +1610,11 @@ sysctl_kern_proc(SYSCTL_HANDLER_ARGS) if (error != 0) return (error); } - 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); return (error); } Modified: head/sys/sys/proc.h ============================================================================== --- head/sys/sys/proc.h Wed Nov 21 18:54:38 2018 (r340741) +++ head/sys/sys/proc.h Wed Nov 21 18:56:15 2018 (r340742) @@ -942,8 +942,11 @@ extern pid_t pid_max; #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; @@ -1046,6 +1049,7 @@ int proc_getargv(struct thread *td, struct proc *p, st 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);
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201811211856.wALIuFvk006942>