From 406882169e00272f14067d948324d690893e6fe3 Mon Sep 17 00:00:00 2001 From: Sudheer A Date: Thu, 23 Jul 2009 15:32:40 -0700 Subject: [PATCH] 6801244 ps takes too long when many AS segments because of rm_assize Contributed by Chad Mynhier --- usr/src/uts/common/brand/lx/procfs/lx_prvnops.c | 8 +-- usr/src/uts/common/fs/proc/prioctl.c | 4 +- usr/src/uts/common/fs/proc/prsubr.c | 7 +- usr/src/uts/common/fs/proc/prvnops.c | 9 +-- usr/src/uts/common/vm/as.h | 5 +- usr/src/uts/common/vm/rm.h | 10 +-- usr/src/uts/common/vm/seg_dev.h | 10 ++- usr/src/uts/common/vm/seg_vn.c | 16 ++++- usr/src/uts/common/vm/seg_vn.h | 11 +++- usr/src/uts/common/vm/vm_as.c | 86 +++++++++++++++---------- usr/src/uts/common/vm/vm_rm.c | 84 +----------------------- 11 files changed, 102 insertions(+), 148 deletions(-) diff --git a/usr/src/uts/common/brand/lx/procfs/lx_prvnops.c b/usr/src/uts/common/brand/lx/procfs/lx_prvnops.c index f37e460f7c..a2365bd352 100644 --- a/usr/src/uts/common/brand/lx/procfs/lx_prvnops.c +++ b/usr/src/uts/common/brand/lx/procfs/lx_prvnops.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -762,7 +762,7 @@ lxpr_read_pid_statm(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf) mutex_exit(&p->p_lock); AS_LOCK_ENTER(as, &as->a_lock, RW_READER); - vsize = btopr(rm_assize(as)); + vsize = btopr(as->a_resvsize); rss = rm_asrss(as); AS_LOCK_EXIT(as, &as->a_lock); @@ -896,7 +896,7 @@ lxpr_read_pid_status(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf) if ((p->p_stat != SZOMB) && !(p->p_flag & SSYS) && (as != &kas)) { mutex_exit(&p->p_lock); AS_LOCK_ENTER(as, &as->a_lock, RW_READER); - vsize = rm_assize(as); + vsize = as->a_resvsize; rss = rm_asrss(as); AS_LOCK_EXIT(as, &as->a_lock); mutex_enter(&p->p_lock); @@ -1062,7 +1062,7 @@ lxpr_read_pid_stat(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf) as = p->p_as; mutex_exit(&p->p_lock); AS_LOCK_ENTER(as, &as->a_lock, RW_READER); - vsize = rm_assize(as); + vsize = as->a_resvsize; rss = rm_asrss(as); AS_LOCK_EXIT(as, &as->a_lock); mutex_enter(&p->p_lock); diff --git a/usr/src/uts/common/fs/proc/prioctl.c b/usr/src/uts/common/fs/proc/prioctl.c index 5630219ef3..721e8e20a6 100644 --- a/usr/src/uts/common/fs/proc/prioctl.c +++ b/usr/src/uts/common/fs/proc/prioctl.c @@ -1656,7 +1656,7 @@ oprgetpsinfo32(proc_t *p, prpsinfo32_t *psp, kthread_t *tp) } else { mutex_exit(&p->p_lock); AS_LOCK_ENTER(as, &as->a_lock, RW_READER); - psp->pr_size = (size32_t)btopr(rm_assize(as)); + psp->pr_size = (size32_t)btopr(as->a_resvsize); psp->pr_rssize = (size32_t)rm_asrss(as); psp->pr_pctmem = rm_pctmemory(as); AS_LOCK_EXIT(as, &as->a_lock); @@ -3495,7 +3495,7 @@ oprgetpsinfo(proc_t *p, prpsinfo_t *psp, kthread_t *tp) } else { mutex_exit(&p->p_lock); AS_LOCK_ENTER(as, &as->a_lock, RW_READER); - psp->pr_size = btopr(rm_assize(as)); + psp->pr_size = btopr(as->a_resvsize); psp->pr_rssize = rm_asrss(as); psp->pr_pctmem = rm_pctmemory(as); AS_LOCK_EXIT(as, &as->a_lock); diff --git a/usr/src/uts/common/fs/proc/prsubr.c b/usr/src/uts/common/fs/proc/prsubr.c index 50ea0196ff..d84cb0a137 100644 --- a/usr/src/uts/common/fs/proc/prsubr.c +++ b/usr/src/uts/common/fs/proc/prsubr.c @@ -20,7 +20,7 @@ */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -2332,7 +2332,8 @@ prgetpsinfo(proc_t *p, psinfo_t *psp) } else { mutex_exit(&p->p_lock); AS_LOCK_ENTER(as, &as->a_lock, RW_READER); - psp->pr_size = btopr(rm_assize(as)) * (PAGESIZE / 1024); + psp->pr_size = btopr(as->a_resvsize) * + (PAGESIZE / 1024); psp->pr_rssize = rm_asrss(as) * (PAGESIZE / 1024); psp->pr_pctmem = rm_pctmemory(as); AS_LOCK_EXIT(as, &as->a_lock); @@ -2465,7 +2466,7 @@ prgetpsinfo32(proc_t *p, psinfo32_t *psp) mutex_exit(&p->p_lock); AS_LOCK_ENTER(as, &as->a_lock, RW_READER); psp->pr_size = (size32_t) - (btopr(rm_assize(as)) * (PAGESIZE / 1024)); + (btopr(as->a_resvsize) * (PAGESIZE / 1024)); psp->pr_rssize = (size32_t) (rm_asrss(as) * (PAGESIZE / 1024)); psp->pr_pctmem = rm_pctmemory(as); diff --git a/usr/src/uts/common/fs/proc/prvnops.c b/usr/src/uts/common/fs/proc/prvnops.c index a78a84fa1a..f7dadc0424 100644 --- a/usr/src/uts/common/fs/proc/prvnops.c +++ b/usr/src/uts/common/fs/proc/prvnops.c @@ -2909,13 +2909,8 @@ prgetattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr, case PR_LWPIDFILE: if ((p->p_flag & SSYS) || (as = p->p_as) == &kas) vap->va_size = 0; - else { - mutex_exit(&p->p_lock); - AS_LOCK_ENTER(as, &as->a_lock, RW_READER); - vap->va_size = rm_assize(as); - AS_LOCK_EXIT(as, &as->a_lock); - mutex_enter(&p->p_lock); - } + else + vap->va_size = as->a_resvsize; break; case PR_STATUS: vap->va_size = PR_OBJSIZE(pstatus32_t, pstatus_t); diff --git a/usr/src/uts/common/vm/as.h b/usr/src/uts/common/vm/as.h index 8ee8cb5276..98e496ef36 100644 --- a/usr/src/uts/common/vm/as.h +++ b/usr/src/uts/common/vm/as.h @@ -110,7 +110,7 @@ struct as { caddr_t a_userlimit; /* highest allowable address in this as */ struct seg *a_seglast; /* last segment hit on the addr space */ krwlock_t a_lock; /* protects segment related fields */ - size_t a_size; /* size of address space */ + size_t a_size; /* total size of address space */ struct seg *a_lastgap; /* last seg found by as_gap() w/ AS_HI (mmap) */ struct seg *a_lastgaphl; /* last seg saved in as_gap() either for */ /* AS_HI or AS_LO used in as_addseg() */ @@ -123,6 +123,7 @@ struct as { struct as_callback *a_callbacks; /* callback list */ void *a_xhat; /* list of xhat providers */ proc_t *a_proc; /* back pointer to proc */ + size_t a_resvsize; /* size of reserved part of address space */ }; #define AS_PAGLCK 0x80 @@ -276,8 +277,6 @@ int as_incore(struct as *as, caddr_t addr, size_t size, char *vec, size_t *sizep); int as_ctl(struct as *as, caddr_t addr, size_t size, int func, int attr, uintptr_t arg, ulong_t *lock_map, size_t pos); -int as_exec(struct as *oas, caddr_t ostka, size_t stksz, - struct as *nas, caddr_t nstka, uint_t hatflag); int as_pagelock(struct as *as, struct page ***ppp, caddr_t addr, size_t size, enum seg_rw rw); void as_pageunlock(struct as *as, struct page **pp, caddr_t addr, diff --git a/usr/src/uts/common/vm/rm.h b/usr/src/uts/common/vm/rm.h index 9789283993..768ee745b9 100644 --- a/usr/src/uts/common/vm/rm.h +++ b/usr/src/uts/common/vm/rm.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 2001 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -40,8 +39,6 @@ #ifndef _VM_RM_H #define _VM_RM_H -#pragma ident "%Z%%M% %I% %E% SMI" - #ifdef __cplusplus extern "C" { #endif @@ -49,7 +46,6 @@ extern "C" { #ifdef _KERNEL extern size_t rm_asrss(struct as *); -extern size_t rm_assize(struct as *); extern ushort_t rm_pctmemory(struct as *); #endif /* _KERNEL */ diff --git a/usr/src/uts/common/vm/seg_dev.h b/usr/src/uts/common/vm/seg_dev.h index 38e37eb6ce..d7fbbfe6a3 100644 --- a/usr/src/uts/common/vm/seg_dev.h +++ b/usr/src/uts/common/vm/seg_dev.h @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -112,6 +112,14 @@ struct devmap_pmem_cookie { #ifdef _KERNEL +/* + * Mappings of /dev/null come from segdev and have no mapping type. + */ + +#define SEG_IS_DEVNULL_MAPPING(seg) \ + ((seg)->s_ops == &segdev_ops && \ + ((SEGOP_GETTYPE(seg, (seg)->s_base) & (MAP_SHARED | MAP_PRIVATE)) == 0)) + extern void segdev_init(void); extern int segdev_create(struct seg *, void *); diff --git a/usr/src/uts/common/vm/seg_vn.c b/usr/src/uts/common/vm/seg_vn.c index 56da962297..9ba274a2eb 100644 --- a/usr/src/uts/common/vm/seg_vn.c +++ b/usr/src/uts/common/vm/seg_vn.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -2029,6 +2029,9 @@ retry: svd->anon_index, npages)); anon_unresv_zone(oswresv - svd->swresv, seg->s_as->a_proc->p_zone); + if (SEG_IS_PARTIAL_RESV(seg)) + seg->s_as->a_resvsize -= oswresv - + svd->swresv; } else { size_t unlen; @@ -2130,6 +2133,9 @@ retry: svd->anon_index, npages)); anon_unresv_zone(oswresv - svd->swresv, seg->s_as->a_proc->p_zone); + if (SEG_IS_PARTIAL_RESV(seg)) + seg->s_as->a_resvsize -= oswresv - + svd->swresv; } else { size_t unlen; @@ -2302,6 +2308,9 @@ retry: ASSERT(oswresv >= (svd->swresv + nsvd->swresv)); anon_unresv_zone(oswresv - (svd->swresv + nsvd->swresv), seg->s_as->a_proc->p_zone); + if (SEG_IS_PARTIAL_RESV(seg)) + seg->s_as->a_resvsize -= oswresv - + (svd->swresv + nsvd->swresv); } else { size_t unlen; @@ -2443,6 +2452,8 @@ segvn_free(struct seg *seg) seg->s_as->a_proc->p_zone); TRACE_3(TR_FAC_VM, TR_ANON_PROC, "anon proc:%p %lu %u", seg, len, 0); + if (SEG_IS_PARTIAL_RESV(seg)) + seg->s_as->a_resvsize -= svd->swresv; svd->swresv = 0; } /* @@ -2710,6 +2721,8 @@ segvn_faultpage( if (anon_resv_zone(ptob(1), seg->s_as->a_proc->p_zone)) { atomic_add_long(&svd->swresv, ptob(1)); + atomic_add_long(&seg->s_as->a_resvsize, + ptob(1)); } else { err = ENOMEM; goto out; @@ -2986,6 +2999,7 @@ segvn_faultpage( if ((svd->flags & MAP_NORESERVE) && (ap == NULL)) { if (anon_resv_zone(ptob(1), seg->s_as->a_proc->p_zone)) { atomic_add_long(&svd->swresv, ptob(1)); + atomic_add_long(&seg->s_as->a_resvsize, ptob(1)); } else { page_unlock(opp); err = ENOMEM; diff --git a/usr/src/uts/common/vm/seg_vn.h b/usr/src/uts/common/vm/seg_vn.h index 66acf8cb61..f94e0cb873 100644 --- a/usr/src/uts/common/vm/seg_vn.h +++ b/usr/src/uts/common/vm/seg_vn.h @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -39,8 +39,6 @@ #ifndef _VM_SEG_VN_H #define _VM_SEG_VN_H -#pragma ident "%Z%%M% %I% %E% SMI" - #include #include @@ -175,6 +173,13 @@ typedef struct segvn_data { #define SEGVN_TR_ADDSTAT(stat) \ segvn_textrepl_stats[CPU->cpu_id].tr_stat_##stat++ +#define SEGVN_DATA(seg) ((struct segvn_data *)(seg)->s_data) +#define SEG_IS_PARTIAL_RESV(seg) \ + ((seg)->s_ops == &segvn_ops && SEGVN_DATA(seg) != NULL && \ + (SEGVN_DATA(seg)->vp == NULL || \ + SEGVN_DATA(seg)->vp->v_type != VREG) && \ + (SEGVN_DATA(seg)->flags & MAP_NORESERVE)) + /* * A hash table entry looked up by vnode, off/eoff and szc to find anon map to * use for text replication based on main thread's (t_tid = 1) lgrp. diff --git a/usr/src/uts/common/vm/vm_as.c b/usr/src/uts/common/vm/vm_as.c index 3e8df474df..e8ccade6e8 100644 --- a/usr/src/uts/common/vm/vm_as.c +++ b/usr/src/uts/common/vm/vm_as.c @@ -656,6 +656,7 @@ as_alloc(void) as->a_hrm = NULL; as->a_seglast = NULL; as->a_size = 0; + as->a_resvsize = 0; as->a_updatedir = 0; gethrestime(&as->a_updatetime); as->a_objectdir = NULL; @@ -781,6 +782,7 @@ as_dup(struct as *as, struct proc *forkedproc) { struct as *newas; struct seg *seg, *newseg; + size_t purgesize = 0; int error; AS_LOCK_ENTER(as, &as->a_lock, RW_WRITER); @@ -803,8 +805,10 @@ as_dup(struct as *as, struct proc *forkedproc) for (seg = AS_SEGFIRST(as); seg != NULL; seg = AS_SEGNEXT(as, seg)) { - if (seg->s_flags & S_PURGE) + if (seg->s_flags & S_PURGE) { + purgesize += seg->s_size; continue; + } newseg = seg_alloc(newas, seg->s_base, seg->s_size); if (newseg == NULL) { @@ -835,6 +839,7 @@ as_dup(struct as *as, struct proc *forkedproc) } newas->a_size += seg->s_size; } + newas->a_resvsize = as->a_resvsize - purgesize; error = hat_dup(as->a_hat, newas->a_hat, NULL, 0, HAT_DUP_ALL); if (as->a_xhat != NULL) @@ -1375,7 +1380,7 @@ as_unmap(struct as *as, caddr_t addr, size_t size) struct seg *seg, *seg_next; struct as_callback *cb; caddr_t raddr, eaddr; - size_t ssize; + size_t ssize, rsize = 0; int err; top: @@ -1415,6 +1420,15 @@ top: */ seg_next = AS_SEGNEXT(as, seg); + /* + * We didn't count /dev/null mappings, so ignore them here. + * We'll handle MAP_NORESERVE cases in segvn_unmap(). (Again, + * we have to do this check here while we have seg.) + */ + if (!SEG_IS_DEVNULL_MAPPING(seg) && + !SEG_IS_PARTIAL_RESV(seg)) + rsize = ssize; + retry: err = SEGOP_UNMAP(seg, raddr, ssize); if (err == EAGAIN) { @@ -1490,6 +1504,7 @@ retry: } as->a_size -= ssize; + as->a_resvsize -= rsize; raddr += ssize; } AS_LOCK_EXIT(as, &as->a_lock); @@ -1530,6 +1545,12 @@ as_map_segvn_segs(struct as *as, caddr_t addr, size_t size, uint_t szcvec, seg_free(seg); } else { as->a_size += size; + /* + * We'll count MAP_NORESERVE mappings as we fault + * pages in. + */ + if (!SEG_IS_PARTIAL_RESV(seg)) + as->a_resvsize += size; } return (error); } @@ -1562,6 +1583,14 @@ as_map_segvn_segs(struct as *as, caddr_t addr, size_t size, uint_t szcvec, return (error); } as->a_size += segsize; + /* + * We'll count MAP_NORESERVE mappings as we fault + * pages in. We don't count /dev/null mappings at all. + */ + if (!SEG_IS_DEVNULL_MAPPING(seg) && + !SEG_IS_PARTIAL_RESV(seg)) + as->a_resvsize += segsize; + *segcreated = 1; if (do_off) { vn_a->offset += segsize; @@ -1590,6 +1619,14 @@ as_map_segvn_segs(struct as *as, caddr_t addr, size_t size, uint_t szcvec, return (error); } as->a_size += segsize; + /* + * We'll count MAP_NORESERVE mappings as we fault + * pages in. We don't count /dev/null mappings at all. + */ + if (!SEG_IS_DEVNULL_MAPPING(seg) && + !SEG_IS_PARTIAL_RESV(seg)) + as->a_resvsize += segsize; + *segcreated = 1; if (do_off) { vn_a->offset += segsize; @@ -1640,6 +1677,12 @@ again: seg_free(seg); } else { as->a_size += size; + /* + * We'll count MAP_NORESERVE mappings as we fault + * pages in. + */ + if (!SEG_IS_PARTIAL_RESV(seg)) + as->a_resvsize += size; } return (error); } @@ -1799,6 +1842,13 @@ as_map_locked(struct as *as, caddr_t addr, size_t size, int (*crfp)(), * Add size now so as_unmap will work if as_ctl fails. */ as->a_size += rsize; + /* + * We'll count MAP_NORESERVE mappings as we fault + * pages in. We don't count /dev/null mappings at all. + */ + if (!SEG_IS_DEVNULL_MAPPING(seg) && + !SEG_IS_PARTIAL_RESV(seg)) + as->a_resvsize += rsize; } as_setwatch(as); @@ -2559,38 +2609,6 @@ lockerr: return (error); } -/* - * Special code for exec to move the stack segment from its interim - * place in the old address to the right place in the new address space. - */ -/*ARGSUSED*/ -int -as_exec(struct as *oas, caddr_t ostka, size_t stksz, - struct as *nas, caddr_t nstka, uint_t hatflag) -{ - struct seg *stkseg; - - AS_LOCK_ENTER(oas, &oas->a_lock, RW_WRITER); - stkseg = as_segat(oas, ostka); - stkseg = as_removeseg(oas, stkseg); - ASSERT(stkseg != NULL); - ASSERT(stkseg->s_base == ostka && stkseg->s_size == stksz); - stkseg->s_as = nas; - stkseg->s_base = nstka; - - /* - * It's ok to lock the address space we are about to exec to. - */ - AS_LOCK_ENTER(nas, &nas->a_lock, RW_WRITER); - ASSERT(avl_numnodes(&nas->a_wpage) == 0); - nas->a_size += stkseg->s_size; - oas->a_size -= stkseg->s_size; - (void) as_addseg(nas, stkseg); - AS_LOCK_EXIT(nas, &nas->a_lock); - AS_LOCK_EXIT(oas, &oas->a_lock); - return (0); -} - int fc_decode(faultcode_t fault_err) { diff --git a/usr/src/uts/common/vm/vm_rm.c b/usr/src/uts/common/vm/vm_rm.c index eb920214f7..e0500874b6 100644 --- a/usr/src/uts/common/vm/vm_rm.c +++ b/usr/src/uts/common/vm/vm_rm.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -36,8 +36,6 @@ * contributors. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include #include #include @@ -59,86 +57,6 @@ #include /* - * Yield the size of an address space. - * - * The size can only be used as a hint since we cannot guarantee it - * will stay the same size unless the as->a_lock is held by the caller. - */ -size_t -rm_assize(struct as *as) -{ - size_t size = 0; - struct seg *seg; - struct segvn_data *svd; - extern struct seg_ops segdev_ops; /* needs a header file */ - - ASSERT(as != NULL && AS_READ_HELD(as, &as->a_lock)); - - if (as == &kas) - return (0); - - for (seg = AS_SEGFIRST(as); seg != NULL; seg = AS_SEGNEXT(as, seg)) { - if (seg->s_ops == &segdev_ops && - ((SEGOP_GETTYPE(seg, seg->s_base) & - (MAP_SHARED | MAP_PRIVATE)) == 0)) { - /* - * Don't include mappings of /dev/null. These just - * reserve address space ranges and have no memory. - * We cheat by knowing that these segments come - * from segdev and have no mapping type. - */ - /* EMPTY */; - } else if (seg->s_ops == &segvn_ops && - (svd = (struct segvn_data *)seg->s_data) != NULL && - (svd->vp == NULL || svd->vp->v_type != VREG) && - (svd->flags & MAP_NORESERVE)) { - /* - * Don't include MAP_NORESERVE pages in the - * address range unless their mappings have - * actually materialized. We cheat by knowing - * that segvn is the only segment driver that - * supports MAP_NORESERVE and that the actual - * number of bytes reserved is in the segment's - * private data structure. - */ - size += svd->swresv; - } else { - caddr_t addr = seg->s_base; - size_t segsize = seg->s_size; - vnode_t *vp; - vattr_t vattr; - - /* - * If the segment is mapped beyond the end of the - * underlying mapped file, if any, then limit the - * segment's size contribution to the file size. - */ - vattr.va_mask = AT_SIZE; - if (seg->s_ops == &segvn_ops && - SEGOP_GETVP(seg, addr, &vp) == 0 && - vp != NULL && vp->v_type == VREG && - VOP_GETATTR(vp, &vattr, ATTR_HINT, - CRED(), NULL) == 0) { - u_offset_t filesize = vattr.va_size; - u_offset_t offset = SEGOP_GETOFFSET(seg, addr); - - if (filesize < offset) - filesize = 0; - else - filesize -= offset; - filesize = P2ROUNDUP_TYPED(filesize, PAGESIZE, - u_offset_t); - if ((u_offset_t)segsize > filesize) - segsize = filesize; - } - size += segsize; - } - } - - return (size); -} - -/* * Yield the memory claim requirement for an address space. * * This is currently implemented as the number of active hardware -- 2.11.4.GIT