From b9b1579c765256b4c224f93628b9803c181ef1fe Mon Sep 17 00:00:00 2001 From: mjg Date: Sun, 25 Feb 2018 15:16:58 +0000 Subject: [PATCH] Don't generate data in sysctl_out_proc unless we intend to copy out. The first call is used to gauge how much spaces is needed. Just computing the size instead of generating the output allows to not take the proctree lock. --- sys/kern/kern_proc.c | 58 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 12 deletions(-) diff --git a/sys/kern/kern_proc.c b/sys/kern/kern_proc.c index 11e09e3687ec..f124767756e4 100644 --- a/sys/kern/kern_proc.c +++ b/sys/kern/kern_proc.c @@ -1347,6 +1347,32 @@ freebsd32_kinfo_proc_out(const struct kinfo_proc *ki, struct kinfo_proc32 *ki32) } #endif +static ssize_t +kern_proc_out_size(struct proc *p, int flags) +{ + ssize_t size = 0; + + PROC_LOCK_ASSERT(p, MA_OWNED); + + if ((flags & KERN_PROC_NOTHREADS) != 0) { +#ifdef COMPAT_FREEBSD32 + if ((flags & KERN_PROC_MASK32) != 0) { + size += sizeof(struct kinfo_proc32); + } else +#endif + size += sizeof(struct kinfo_proc); + } else { +#ifdef COMPAT_FREEBSD32 + if ((flags & KERN_PROC_MASK32) != 0) + size += sizeof(struct kinfo_proc32) * p->p_numthreads; + else +#endif + size += sizeof(struct kinfo_proc) * p->p_numthreads; + } + PROC_UNLOCK(p); + return (size); +} + int kern_proc_out(struct proc *p, struct sbuf *sb, int flags) { @@ -1399,6 +1425,9 @@ sysctl_out_proc(struct proc *p, struct sysctl_req *req, int flags) struct kinfo_proc ki; int error, error2; + if (req->oldptr == NULL) + return (SYSCTL_OUT(req, 0, kern_proc_out_size(p, flags))); + sbuf_new_for_sysctl(&sb, (char *)&ki, sizeof(ki), req); sbuf_clear_flags(&sb, SBUF_INCLUDENUL); error = kern_proc_out(p, &sb, flags); @@ -1461,16 +1490,22 @@ sysctl_kern_proc(SYSCTL_HANDLER_ARGS) break; } - if (!req->oldptr) { + if (req->oldptr == NULL) { /* overestimate by 5 procs */ error = SYSCTL_OUT(req, 0, sizeof (struct kinfo_proc) * 5); if (error) return (error); + } else { + error = sysctl_wire_old_buffer(req, 0); + if (error != 0) + return (error); + /* + * This lock is only needed to safely grab the parent of a + * traced process. Only grab it if we are producing any + * data to begin with. + */ + sx_slock(&proctree_lock); } - error = sysctl_wire_old_buffer(req, 0); - if (error != 0) - return (error); - sx_slock(&proctree_lock); sx_slock(&allproc_lock); for (doingzomb=0 ; doingzomb < 2 ; doingzomb++) { if (!doingzomb) @@ -1571,16 +1606,15 @@ sysctl_kern_proc(SYSCTL_HANDLER_ARGS) } error = sysctl_out_proc(p, req, flags); - if (error) { - sx_sunlock(&allproc_lock); - sx_sunlock(&proctree_lock); - return (error); - } + if (error) + goto out; } } +out: sx_sunlock(&allproc_lock); - sx_sunlock(&proctree_lock); - return (0); + if (req->oldptr != NULL) + sx_sunlock(&proctree_lock); + return (error); } struct pargs *