diff options
Diffstat (limited to 'usr/src')
-rw-r--r-- | usr/src/uts/common/fs/proc/prdata.h | 57 | ||||
-rw-r--r-- | usr/src/uts/common/fs/proc/prioctl.c | 159 | ||||
-rw-r--r-- | usr/src/uts/common/fs/proc/prsubr.c | 247 | ||||
-rw-r--r-- | usr/src/uts/common/fs/proc/prvnops.c | 124 |
4 files changed, 307 insertions, 280 deletions
diff --git a/usr/src/uts/common/fs/proc/prdata.h b/usr/src/uts/common/fs/proc/prdata.h index 10d7b6e3a4..1294421f9f 100644 --- a/usr/src/uts/common/fs/proc/prdata.h +++ b/usr/src/uts/common/fs/proc/prdata.h @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -39,6 +38,7 @@ #include <sys/prsystm.h> #include <sys/model.h> #include <sys/poll.h> +#include <sys/list.h> #ifdef __cplusplus extern "C" { @@ -268,6 +268,47 @@ extern int nproc_highbit; /* highbit(v.v_nproc) */ extern struct vnodeops *prvnodeops; +/* + * Generic chained copyout buffers for procfs use. + * In order to prevent procfs from making huge oversize kmem_alloc calls, + * a list of smaller buffers can be concatenated and copied to userspace in + * sequence. + * + * The implementation is opaque. + * + * A user of this will perform the following steps: + * + * list_t listhead; + * struct my *mp; + * + * pr_iol_initlist(&listhead, sizeof (*mp), n); + * while (whatever) { + * mp = pr_iol_newbuf(&listhead, sizeof (*mp); + * ... + * error = ... + * } + * + * When done, depending on whether copyout() or uiomove() is supposed to + * be used for transferring the buffered data to userspace, call either: + * + * error = pr_iol_copyout_and_free(&listhead, &cmaddr, error); + * + * or else: + * + * error = pr_iol_uiomove_and_free(&listhead, uiop, error); + * + * These two functions will in any case kmem_free() all list items, but + * if an error occurred before they will not perform the copyout/uiomove. + * If copyout/uiomove are done, the passed target address / uio_t + * are updated. The error returned will either be the one passed in, or + * the error that occurred during copyout/uiomove. + */ + +extern void pr_iol_initlist(list_t *head, size_t itemsize, int nitems); +extern void * pr_iol_newbuf(list_t *head, size_t itemsize); +extern int pr_iol_copyout_and_free(list_t *head, caddr_t *tgt, int errin); +extern int pr_iol_uiomove_and_free(list_t *head, uio_t *uiop, int errin); + #if defined(_SYSCALL32_IMPL) extern int prwritectl32(vnode_t *, struct uio *, cred_t *); @@ -278,11 +319,11 @@ extern void prcvtusage32(struct prhusage *, prusage32_t *); /* kludge to support old /proc interface */ #if !defined(_SYS_OLD_PROCFS_H) -extern int prgetmap(proc_t *, int, prmap_t **, size_t *); -extern int prgetxmap(proc_t *, prxmap_t **, size_t *); +extern int prgetmap(proc_t *, int, list_t *); +extern int prgetxmap(proc_t *, list_t *); #if defined(_SYSCALL32_IMPL) -extern int prgetmap32(proc_t *, int, prmap32_t **, size_t *); -extern int prgetxmap32(proc_t *, prxmap32_t **, size_t *); +extern int prgetmap32(proc_t *, int, list_t *); +extern int prgetxmap32(proc_t *, list_t *); #endif /* _SYSCALL32_IMPL */ #endif /* !_SYS_OLD_PROCFS_H */ diff --git a/usr/src/uts/common/fs/proc/prioctl.c b/usr/src/uts/common/fs/proc/prioctl.c index 844a3b7bb1..1eb2861b0f 100644 --- a/usr/src/uts/common/fs/proc/prioctl.c +++ b/usr/src/uts/common/fs/proc/prioctl.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -85,7 +84,7 @@ static void prsetrun(kthread_t *, prrun_t *); static int propenm(prnode_t *, caddr_t, caddr_t, int *, cred_t *); extern void oprgetstatus(kthread_t *, prstatus_t *, zone_t *); extern void oprgetpsinfo(proc_t *, prpsinfo_t *, kthread_t *); -static int oprgetmap(proc_t *, prmap_t **, size_t *); +static int oprgetmap(proc_t *, list_t *); /*ARGSUSED*/ static int @@ -957,29 +956,32 @@ startover: case PIOCMAP: /* get memory map information */ { - int n; - prmap_t *prmapp; - size_t size; + list_t iolhead; struct as *as = p->p_as; - int issys = ((p->p_flag & SSYS) || as == &kas); - if (issys) { - n = 1; - prmapp = &un.prmap; - bzero(prmapp, sizeof (*prmapp)); + if ((p->p_flag & SSYS) || as == &kas) { + error = 0; + prunlock(pnp); } else { mutex_exit(&p->p_lock); AS_LOCK_ENTER(as, &as->a_lock, RW_WRITER); - n = oprgetmap(p, &prmapp, &size); + error = oprgetmap(p, &iolhead); AS_LOCK_EXIT(as, &as->a_lock); mutex_enter(&p->p_lock); - } - prunlock(pnp); + prunlock(pnp); - if (copyout(prmapp, cmaddr, n * sizeof (prmap_t))) + error = pr_iol_copyout_and_free(&iolhead, + &cmaddr, error); + } + /* + * The procfs PIOCMAP ioctl returns an all-zero buffer + * to indicate the end of the prmap[] array. + * Append it to whatever has already been copied out. + */ + bzero(&un.prmap, sizeof (un.prmap)); + if (!error && copyout(&un.prmap, cmaddr, sizeof (un.prmap))) error = EFAULT; - if (!issys) - kmem_free(prmapp, size); + break; } @@ -1311,7 +1313,7 @@ startover: #ifdef _SYSCALL32_IMPL -static int oprgetmap32(proc_t *, ioc_prmap32_t **, size_t *); +static int oprgetmap32(proc_t *, list_t *); void oprgetstatus32(kthread_t *t, prstatus32_t *sp, zone_t *zp) @@ -2567,33 +2569,35 @@ startover: case PIOCMAP: /* get memory map information */ { - int n; - ioc_prmap32_t *prmapp; - size_t size; + list_t iolhead; struct as *as = p->p_as; - int issys = ((p->p_flag & SSYS) || as == &kas); - if (issys) { - n = 1; - prmapp = &un32.prmap; - bzero(prmapp, sizeof (*prmapp)); + if ((p->p_flag & SSYS) || as == &kas) { + error = 0; + prunlock(pnp); } else if (PROCESS_NOT_32BIT(p)) { error = EOVERFLOW; prunlock(pnp); - break; } else { mutex_exit(&p->p_lock); AS_LOCK_ENTER(as, &as->a_lock, RW_WRITER); - n = oprgetmap32(p, &prmapp, &size); + error = oprgetmap32(p, &iolhead); AS_LOCK_EXIT(as, &as->a_lock); mutex_enter(&p->p_lock); - } - prunlock(pnp); + prunlock(pnp); - if (copyout(prmapp, cmaddr, n * sizeof (ioc_prmap32_t))) - error = EFAULT; - if (!issys) - kmem_free(prmapp, size); + error = pr_iol_copyout_and_free(&iolhead, + &cmaddr, error); + } + /* + * The procfs PIOCMAP ioctl returns an all-zero buffer + * to indicate the end of the prmap[] array. + * Append it to whatever has already been copied out. + */ + bzero(&un32.prmap, sizeof (un32.prmap)); + if (!error && + copyout(&un32.prmap, cmaddr, sizeof (un32.prmap))) + error = EFAULT; break; } @@ -3468,32 +3472,29 @@ oprgetpsinfo(proc_t *p, prpsinfo_t *psp, kthread_t *tp) /* * Return an array of structures with memory map information. - * In addition to the "real" segments we need space for the - * zero-filled entry that terminates the list. * We allocate here; the caller must deallocate. + * The caller is also responsible to append the zero-filled entry + * that terminates the PIOCMAP output buffer. */ -#define MAPSIZE 8192 static int -oprgetmap(proc_t *p, prmap_t **prmapp, size_t *sizep) +oprgetmap(proc_t *p, list_t *iolhead) { struct as *as = p->p_as; - int nmaps = 0; prmap_t *mp; - size_t size; struct seg *seg; struct seg *brkseg, *stkseg; uint_t prot; ASSERT(as != &kas && AS_WRITE_HELD(as, &as->a_lock)); - /* initial allocation */ - *sizep = size = MAPSIZE; - *prmapp = mp = kmem_alloc(MAPSIZE, KM_SLEEP); + /* + * Request an initial buffer size that doesn't waste memory + * if the address space has only a small number of segments. + */ + pr_iol_initlist(iolhead, sizeof (*mp), avl_numnodes(&as->a_segtree)); - if ((seg = AS_SEGFIRST(as)) == NULL) { - bzero(mp, sizeof (*mp)); - return (1); - } + if ((seg = AS_SEGFIRST(as)) == NULL) + return (0); brkseg = break_seg(p); stkseg = as_segat(as, prgetstackbase(p)); @@ -3507,18 +3508,9 @@ oprgetmap(proc_t *p, prmap_t **prmapp, size_t *sizep) prot = pr_getprot(seg, 0, &tmp, &saddr, &naddr, eaddr); if (saddr == naddr) continue; - /* reallocate if necessary */ - if ((nmaps + 2) * sizeof (prmap_t) > size) { - size_t newsize = size + MAPSIZE; - prmap_t *newmp = kmem_alloc(newsize, KM_SLEEP); - - bcopy(*prmapp, newmp, nmaps * sizeof (prmap_t)); - kmem_free(*prmapp, size); - *sizep = size = newsize; - *prmapp = newmp; - mp = newmp + nmaps; - } - bzero(mp, sizeof (*mp)); + + mp = pr_iol_newbuf(iolhead, sizeof (*mp)); + mp->pr_vaddr = saddr; mp->pr_size = naddr - saddr; mp->pr_off = SEGOP_GETOFFSET(seg, saddr); @@ -3536,38 +3528,33 @@ oprgetmap(proc_t *p, prmap_t **prmapp, size_t *sizep) else if (seg == stkseg) mp->pr_mflags |= MA_STACK; mp->pr_pagesize = PAGESIZE; - mp++; - nmaps++; } ASSERT(tmp == NULL); } while ((seg = AS_SEGNEXT(as, seg)) != NULL); - bzero(mp, sizeof (*mp)); - return (nmaps + 1); + return (0); } #ifdef _SYSCALL32_IMPL static int -oprgetmap32(proc_t *p, ioc_prmap32_t **prmapp, size_t *sizep) +oprgetmap32(proc_t *p, list_t *iolhead) { struct as *as = p->p_as; - int nmaps = 0; ioc_prmap32_t *mp; - size_t size; struct seg *seg; struct seg *brkseg, *stkseg; uint_t prot; ASSERT(as != &kas && AS_WRITE_HELD(as, &as->a_lock)); - /* initial allocation */ - *sizep = size = MAPSIZE; - *prmapp = mp = kmem_alloc(MAPSIZE, KM_SLEEP); + /* + * Request an initial buffer size that doesn't waste memory + * if the address space has only a small number of segments. + */ + pr_iol_initlist(iolhead, sizeof (*mp), avl_numnodes(&as->a_segtree)); - if ((seg = AS_SEGFIRST(as)) == NULL) { - bzero(mp, sizeof (*mp)); - return (1); - } + if ((seg = AS_SEGFIRST(as)) == NULL) + return (0); brkseg = break_seg(p); stkseg = as_segat(as, prgetstackbase(p)); @@ -3581,20 +3568,9 @@ oprgetmap32(proc_t *p, ioc_prmap32_t **prmapp, size_t *sizep) prot = pr_getprot(seg, 0, &tmp, &saddr, &naddr, eaddr); if (saddr == naddr) continue; - /* reallocate if necessary */ - if ((nmaps + 2) * sizeof (ioc_prmap32_t) > size) { - size_t newsize = size + MAPSIZE; - ioc_prmap32_t *newmp = - kmem_alloc(newsize, KM_SLEEP); - - bcopy(*prmapp, newmp, - nmaps * sizeof (ioc_prmap32_t)); - kmem_free(*prmapp, size); - *sizep = size = newsize; - *prmapp = newmp; - mp = newmp + nmaps; - } - bzero(mp, sizeof (*mp)); + + mp = pr_iol_newbuf(iolhead, sizeof (*mp)); + mp->pr_vaddr = (caddr32_t)(uintptr_t)saddr; mp->pr_size = (size32_t)(naddr - saddr); mp->pr_off = (off32_t)SEGOP_GETOFFSET(seg, saddr); @@ -3612,14 +3588,11 @@ oprgetmap32(proc_t *p, ioc_prmap32_t **prmapp, size_t *sizep) else if (seg == stkseg) mp->pr_mflags |= MA_STACK; mp->pr_pagesize = PAGESIZE; - mp++; - nmaps++; } ASSERT(tmp == NULL); } while ((seg = AS_SEGNEXT(as, seg)) != NULL); - bzero(mp, sizeof (*mp)); - return (nmaps + 1); + return (0); } #endif /* _SYSCALL32_IMPL */ diff --git a/usr/src/uts/common/fs/proc/prsubr.c b/usr/src/uts/common/fs/proc/prsubr.c index e1931414ec..619666e5a8 100644 --- a/usr/src/uts/common/fs/proc/prsubr.c +++ b/usr/src/uts/common/fs/proc/prsubr.c @@ -86,6 +86,7 @@ #include <sys/pool.h> #include <sys/zone.h> #include <sys/atomic.h> +#include <sys/sdt.h> #define MAX_ITERS_SPIN 5 @@ -1475,18 +1476,130 @@ break_seg(proc_t *p) } /* + * Implementation of service functions to handle procfs generic chained + * copyout buffers. + */ +typedef struct pr_iobuf_list { + list_node_t piol_link; /* buffer linkage */ + size_t piol_size; /* total size (header + data) */ + size_t piol_usedsize; /* amount to copy out from this buf */ +} piol_t; + +#define MAPSIZE (64 * 1024) +#define PIOL_DATABUF(iol) ((void *)(&(iol)[1])) + +void +pr_iol_initlist(list_t *iolhead, size_t itemsize, int n) +{ + piol_t *iol; + size_t initial_size = MIN(1, n) * itemsize; + + list_create(iolhead, sizeof (piol_t), offsetof(piol_t, piol_link)); + + ASSERT(list_head(iolhead) == NULL); + ASSERT(itemsize < MAPSIZE - sizeof (*iol)); + ASSERT(initial_size > 0); + + /* + * Someone creating chained copyout buffers may ask for less than + * MAPSIZE if the amount of data to be buffered is known to be + * smaller than that. + * But in order to prevent involuntary self-denial of service, + * the requested input size is clamped at MAPSIZE. + */ + initial_size = MIN(MAPSIZE, initial_size + sizeof (*iol)); + iol = kmem_alloc(initial_size, KM_SLEEP); + list_insert_head(iolhead, iol); + iol->piol_usedsize = 0; + iol->piol_size = initial_size; +} + +void * +pr_iol_newbuf(list_t *iolhead, size_t itemsize) +{ + piol_t *iol; + char *new; + + ASSERT(itemsize < MAPSIZE - sizeof (*iol)); + ASSERT(list_head(iolhead) != NULL); + + iol = (piol_t *)list_tail(iolhead); + + if (iol->piol_size < + iol->piol_usedsize + sizeof (*iol) + itemsize) { + /* + * Out of space in the current buffer. Allocate more. + */ + piol_t *newiol; + + newiol = kmem_alloc(MAPSIZE, KM_SLEEP); + newiol->piol_size = MAPSIZE; + newiol->piol_usedsize = 0; + + list_insert_after(iolhead, iol, newiol); + iol = list_next(iolhead, iol); + ASSERT(iol == newiol); + } + new = (char *)PIOL_DATABUF(iol) + iol->piol_usedsize; + iol->piol_usedsize += itemsize; + bzero(new, itemsize); + return (new); +} + +int +pr_iol_copyout_and_free(list_t *iolhead, caddr_t *tgt, int errin) +{ + int error = errin; + piol_t *iol; + + while ((iol = list_head(iolhead)) != NULL) { + list_remove(iolhead, iol); + if (!error) { + if (copyout(PIOL_DATABUF(iol), *tgt, + iol->piol_usedsize)) + error = EFAULT; + *tgt += iol->piol_usedsize; + } + kmem_free(iol, iol->piol_size); + } + list_destroy(iolhead); + + return (error); +} + +int +pr_iol_uiomove_and_free(list_t *iolhead, uio_t *uiop, int errin) +{ + offset_t off = uiop->uio_offset; + char *base; + size_t size; + piol_t *iol; + int error = errin; + + while ((iol = list_head(iolhead)) != NULL) { + list_remove(iolhead, iol); + base = PIOL_DATABUF(iol); + size = iol->piol_usedsize; + if (off <= size && error == 0 && uiop->uio_resid > 0) + error = uiomove(base + off, size - off, + UIO_READ, uiop); + off = MAX(0, off - (offset_t)size); + kmem_free(iol, iol->piol_size); + } + list_destroy(iolhead); + + return (error); +} + +/* * Return an array of structures with memory map information. * We allocate here; the caller must deallocate. */ -#define INITIAL_MAPSIZE 65536 -#define MAPSIZE 8192 int -prgetmap(proc_t *p, int reserved, prmap_t **prmapp, size_t *sizep) +prgetmap(proc_t *p, int reserved, list_t *iolhead) { struct as *as = p->p_as; - int nmaps = 0; prmap_t *mp; - size_t size; struct seg *seg; struct seg *brkseg, *stkseg; struct vnode *vp; @@ -1495,9 +1608,11 @@ prgetmap(proc_t *p, int reserved, prmap_t **prmapp, size_t *sizep) ASSERT(as != &kas && AS_WRITE_HELD(as, &as->a_lock)); - /* initial allocation */ - *sizep = size = INITIAL_MAPSIZE; - *prmapp = mp = kmem_alloc(size, KM_SLEEP); + /* + * Request an initial buffer size that doesn't waste memory + * if the address space has only a small number of segments. + */ + pr_iol_initlist(iolhead, sizeof (*mp), avl_numnodes(&as->a_segtree)); if ((seg = AS_SEGFIRST(as)) == NULL) return (0); @@ -1515,18 +1630,9 @@ prgetmap(proc_t *p, int reserved, prmap_t **prmapp, size_t *sizep) &saddr, &naddr, eaddr); if (saddr == naddr) continue; - /* reallocate if necessary */ - if ((nmaps + 1) * sizeof (prmap_t) > size) { - size_t newsize = size + 3 * size / 16; - prmap_t *newmp = kmem_alloc(newsize, KM_SLEEP); - - bcopy(*prmapp, newmp, nmaps * sizeof (prmap_t)); - kmem_free(*prmapp, size); - *sizep = size = newsize; - *prmapp = newmp; - mp = newmp + nmaps; - } - bzero(mp, sizeof (*mp)); + + mp = pr_iol_newbuf(iolhead, sizeof (*mp)); + mp->pr_vaddr = (uintptr_t)saddr; mp->pr_size = naddr - saddr; mp->pr_offset = SEGOP_GETOFFSET(seg, saddr); @@ -1592,24 +1698,19 @@ prgetmap(proc_t *p, int reserved, prmap_t **prmapp, size_t *sizep) } else { mp->pr_shmid = -1; } - - mp++; - nmaps++; } ASSERT(tmp == NULL); } while ((seg = AS_SEGNEXT(as, seg)) != NULL); - return (nmaps); + return (0); } #ifdef _SYSCALL32_IMPL int -prgetmap32(proc_t *p, int reserved, prmap32_t **prmapp, size_t *sizep) +prgetmap32(proc_t *p, int reserved, list_t *iolhead) { struct as *as = p->p_as; - int nmaps = 0; prmap32_t *mp; - size_t size; struct seg *seg; struct seg *brkseg, *stkseg; struct vnode *vp; @@ -1618,9 +1719,11 @@ prgetmap32(proc_t *p, int reserved, prmap32_t **prmapp, size_t *sizep) ASSERT(as != &kas && AS_WRITE_HELD(as, &as->a_lock)); - /* initial allocation */ - *sizep = size = INITIAL_MAPSIZE; - *prmapp = mp = kmem_alloc(size, KM_SLEEP); + /* + * Request an initial buffer size that doesn't waste memory + * if the address space has only a small number of segments. + */ + pr_iol_initlist(iolhead, sizeof (*mp), avl_numnodes(&as->a_segtree)); if ((seg = AS_SEGFIRST(as)) == NULL) return (0); @@ -1638,20 +1741,9 @@ prgetmap32(proc_t *p, int reserved, prmap32_t **prmapp, size_t *sizep) &saddr, &naddr, eaddr); if (saddr == naddr) continue; - /* reallocate if necessary */ - if ((nmaps + 1) * sizeof (prmap32_t) > size) { - size_t newsize = size + 3 * size / 16; - prmap32_t *newmp = - kmem_alloc(newsize, KM_SLEEP); - - bcopy(*prmapp, newmp, - nmaps * sizeof (prmap32_t)); - kmem_free(*prmapp, size); - *sizep = size = newsize; - *prmapp = newmp; - mp = newmp + nmaps; - } - bzero(mp, sizeof (*mp)); + + mp = pr_iol_newbuf(iolhead, sizeof (*mp)); + mp->pr_vaddr = (caddr32_t)(uintptr_t)saddr; mp->pr_size = (size32_t)(naddr - saddr); mp->pr_offset = SEGOP_GETOFFSET(seg, saddr); @@ -1718,14 +1810,11 @@ prgetmap32(proc_t *p, int reserved, prmap32_t **prmapp, size_t *sizep) } else { mp->pr_shmid = -1; } - - mp++; - nmaps++; } ASSERT(tmp == NULL); } while ((seg = AS_SEGNEXT(as, seg)) != NULL); - return (nmaps); + return (0); } #endif /* _SYSCALL32_IMPL */ @@ -3791,12 +3880,10 @@ pr_getpagesize(struct seg *seg, caddr_t saddr, caddr_t *naddrp, caddr_t eaddr) * We allocate here; the caller must deallocate. */ int -prgetxmap(proc_t *p, prxmap_t **prxmapp, size_t *sizep) +prgetxmap(proc_t *p, list_t *iolhead) { struct as *as = p->p_as; - int nmaps = 0; prxmap_t *mp; - size_t size; struct seg *seg; struct seg *brkseg, *stkseg; struct vnode *vp; @@ -3805,9 +3892,11 @@ prgetxmap(proc_t *p, prxmap_t **prxmapp, size_t *sizep) ASSERT(as != &kas && AS_WRITE_HELD(as, &as->a_lock)); - /* initial allocation */ - *sizep = size = INITIAL_MAPSIZE; - *prxmapp = mp = kmem_alloc(size, KM_SLEEP); + /* + * Request an initial buffer size that doesn't waste memory + * if the address space has only a small number of segments. + */ + pr_iol_initlist(iolhead, sizeof (*mp), avl_numnodes(&as->a_segtree)); if ((seg = AS_SEGFIRST(as)) == NULL) return (0); @@ -3843,21 +3932,8 @@ prgetxmap(proc_t *p, prxmap_t **prxmapp, size_t *sizep) psz = pr_getpagesize(seg, saddr, &naddr, baddr); ASSERT(naddr >= saddr && naddr <= baddr); - /* reallocate if necessary */ - if ((nmaps + 1) * sizeof (prxmap_t) > size) { - size_t newsize = size + 3 * size / 16; - prxmap_t *newmp = - kmem_alloc(newsize, KM_SLEEP); - - bcopy(*prxmapp, newmp, - nmaps * sizeof (prxmap_t)); - kmem_free(*prxmapp, size); - *sizep = size = newsize; - *prxmapp = newmp; - mp = newmp + nmaps; - } + mp = pr_iol_newbuf(iolhead, sizeof (*mp)); - bzero(mp, sizeof (*mp)); mp->pr_vaddr = (uintptr_t)saddr; mp->pr_size = naddr - saddr; mp->pr_offset = SEGOP_GETOFFSET(seg, saddr); @@ -3939,14 +4015,12 @@ prgetxmap(proc_t *p, prxmap_t **prxmapp, size_t *sizep) mp->pr_locked++; } kmem_free(parr, npages); - mp++; - nmaps++; } } ASSERT(tmp == NULL); } while ((seg = AS_SEGNEXT(as, seg)) != NULL); - return (nmaps); + return (0); } /* @@ -3989,12 +4063,10 @@ prgetpriv(proc_t *p, prpriv_t *pprp) * We allocate here; the caller must deallocate. */ int -prgetxmap32(proc_t *p, prxmap32_t **prxmapp, size_t *sizep) +prgetxmap32(proc_t *p, list_t *iolhead) { struct as *as = p->p_as; - int nmaps = 0; prxmap32_t *mp; - size_t size; struct seg *seg; struct seg *brkseg, *stkseg; struct vnode *vp; @@ -4003,9 +4075,11 @@ prgetxmap32(proc_t *p, prxmap32_t **prxmapp, size_t *sizep) ASSERT(as != &kas && AS_WRITE_HELD(as, &as->a_lock)); - /* initial allocation */ - *sizep = size = INITIAL_MAPSIZE; - *prxmapp = mp = kmem_alloc(size, KM_SLEEP); + /* + * Request an initial buffer size that doesn't waste memory + * if the address space has only a small number of segments. + */ + pr_iol_initlist(iolhead, sizeof (*mp), avl_numnodes(&as->a_segtree)); if ((seg = AS_SEGFIRST(as)) == NULL) return (0); @@ -4041,21 +4115,8 @@ prgetxmap32(proc_t *p, prxmap32_t **prxmapp, size_t *sizep) psz = pr_getpagesize(seg, saddr, &naddr, baddr); ASSERT(naddr >= saddr && naddr <= baddr); - /* reallocate if necessary */ - if ((nmaps + 1) * sizeof (prxmap32_t) > size) { - size_t newsize = size + 3 * size / 16; - prxmap32_t *newmp = - kmem_alloc(newsize, KM_SLEEP); - - bcopy(*prxmapp, newmp, - nmaps * sizeof (prxmap32_t)); - kmem_free(*prxmapp, size); - *sizep = size = newsize; - *prxmapp = newmp; - mp = newmp + nmaps; - } + mp = pr_iol_newbuf(iolhead, sizeof (*mp)); - bzero(mp, sizeof (*mp)); mp->pr_vaddr = (caddr32_t)(uintptr_t)saddr; mp->pr_size = (size32_t)(naddr - saddr); mp->pr_offset = SEGOP_GETOFFSET(seg, saddr); @@ -4138,13 +4199,11 @@ prgetxmap32(proc_t *p, prxmap32_t **prxmapp, size_t *sizep) mp->pr_locked++; } kmem_free(parr, npages); - mp++; - nmaps++; } } ASSERT(tmp == NULL); } while ((seg = AS_SEGNEXT(as, seg)) != NULL); - return (nmaps); + return (0); } #endif /* _SYSCALL32_IMPL */ diff --git a/usr/src/uts/common/fs/proc/prvnops.c b/usr/src/uts/common/fs/proc/prvnops.c index d12ee64e8c..3f15bb084f 100644 --- a/usr/src/uts/common/fs/proc/prvnops.c +++ b/usr/src/uts/common/fs/proc/prvnops.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -857,13 +856,11 @@ pr_read_lpsinfo(prnode_t *pnp, uio_t *uiop) } static int -pr_read_map_common(prnode_t *pnp, uio_t *uiop, int reserved) +pr_read_map_common(prnode_t *pnp, uio_t *uiop, prnodetype_t type) { proc_t *p; struct as *as; - int nmaps; - prmap_t *prmapp; - size_t size; + list_t iolhead; int error; if ((error = prlock(pnp, ZNO)) != 0) @@ -879,13 +876,23 @@ pr_read_map_common(prnode_t *pnp, uio_t *uiop, int reserved) mutex_exit(&p->p_lock); AS_LOCK_ENTER(as, &as->a_lock, RW_WRITER); - nmaps = prgetmap(p, reserved, &prmapp, &size); + switch (type) { + case PR_XMAP: + error = prgetxmap(p, &iolhead); + break; + case PR_RMAP: + error = prgetmap(p, 1, &iolhead); + break; + case PR_MAP: + error = prgetmap(p, 0, &iolhead); + break; + } AS_LOCK_EXIT(as, &as->a_lock); mutex_enter(&p->p_lock); prunlock(pnp); - error = pr_uioread(prmapp, nmaps * sizeof (prmap_t), uiop); - kmem_free(prmapp, size); + error = pr_iol_uiomove_and_free(&iolhead, uiop, error); + return (error); } @@ -893,49 +900,21 @@ static int pr_read_map(prnode_t *pnp, uio_t *uiop) { ASSERT(pnp->pr_type == PR_MAP); - return (pr_read_map_common(pnp, uiop, 0)); + return (pr_read_map_common(pnp, uiop, pnp->pr_type)); } static int pr_read_rmap(prnode_t *pnp, uio_t *uiop) { ASSERT(pnp->pr_type == PR_RMAP); - return (pr_read_map_common(pnp, uiop, 1)); + return (pr_read_map_common(pnp, uiop, pnp->pr_type)); } static int pr_read_xmap(prnode_t *pnp, uio_t *uiop) { - proc_t *p; - struct as *as; - int nmems; - prxmap_t *prxmapp; - size_t size; - int error; - ASSERT(pnp->pr_type == PR_XMAP); - - if ((error = prlock(pnp, ZNO)) != 0) - return (error); - - p = pnp->pr_common->prc_proc; - as = p->p_as; - - if ((p->p_flag & SSYS) || as == &kas) { - prunlock(pnp); - return (0); - } - - mutex_exit(&p->p_lock); - AS_LOCK_ENTER(as, &as->a_lock, RW_WRITER); - nmems = prgetxmap(p, &prxmapp, &size); - AS_LOCK_EXIT(as, &as->a_lock); - mutex_enter(&p->p_lock); - prunlock(pnp); - - error = pr_uioread(prxmapp, nmems * sizeof (prxmap_t), uiop); - kmem_free(prxmapp, size); - return (error); + return (pr_read_map_common(pnp, uiop, pnp->pr_type)); } static int @@ -1954,13 +1933,11 @@ pr_read_lpsinfo_32(prnode_t *pnp, uio_t *uiop) } static int -pr_read_map_common_32(prnode_t *pnp, uio_t *uiop, int reserved) +pr_read_map_common_32(prnode_t *pnp, uio_t *uiop, prnodetype_t type) { proc_t *p; struct as *as; - int nmaps; - prmap32_t *prmapp; - size_t size; + list_t iolhead; int error; if ((error = prlock(pnp, ZNO)) != 0) @@ -1981,13 +1958,23 @@ pr_read_map_common_32(prnode_t *pnp, uio_t *uiop, int reserved) mutex_exit(&p->p_lock); AS_LOCK_ENTER(as, &as->a_lock, RW_WRITER); - nmaps = prgetmap32(p, reserved, &prmapp, &size); + switch (type) { + case PR_XMAP: + error = prgetxmap32(p, &iolhead); + break; + case PR_RMAP: + error = prgetmap32(p, 1, &iolhead); + break; + case PR_MAP: + error = prgetmap32(p, 0, &iolhead); + break; + } AS_LOCK_EXIT(as, &as->a_lock); mutex_enter(&p->p_lock); prunlock(pnp); - error = pr_uioread(prmapp, nmaps * sizeof (prmap32_t), uiop); - kmem_free(prmapp, size); + error = pr_iol_uiomove_and_free(&iolhead, uiop, error); + return (error); } @@ -1995,54 +1982,21 @@ static int pr_read_map_32(prnode_t *pnp, uio_t *uiop) { ASSERT(pnp->pr_type == PR_MAP); - return (pr_read_map_common_32(pnp, uiop, 0)); + return (pr_read_map_common_32(pnp, uiop, pnp->pr_type)); } static int pr_read_rmap_32(prnode_t *pnp, uio_t *uiop) { ASSERT(pnp->pr_type == PR_RMAP); - return (pr_read_map_common_32(pnp, uiop, 1)); + return (pr_read_map_common_32(pnp, uiop, pnp->pr_type)); } static int pr_read_xmap_32(prnode_t *pnp, uio_t *uiop) { - proc_t *p; - struct as *as; - int nmems; - prxmap32_t *prxmapp; - size_t size; - int error; - ASSERT(pnp->pr_type == PR_XMAP); - - if ((error = prlock(pnp, ZNO)) != 0) - return (error); - - p = pnp->pr_common->prc_proc; - as = p->p_as; - - if ((p->p_flag & SSYS) || as == &kas) { - prunlock(pnp); - return (0); - } - - if (PROCESS_NOT_32BIT(p)) { - prunlock(pnp); - return (EOVERFLOW); - } - - mutex_exit(&p->p_lock); - AS_LOCK_ENTER(as, &as->a_lock, RW_WRITER); - nmems = prgetxmap32(p, &prxmapp, &size); - AS_LOCK_EXIT(as, &as->a_lock); - mutex_enter(&p->p_lock); - prunlock(pnp); - - error = pr_uioread(prxmapp, nmems * sizeof (prxmap32_t), uiop); - kmem_free(prxmapp, size); - return (error); + return (pr_read_map_common_32(pnp, uiop, pnp->pr_type)); } static int |