version 1.128.2.13, 2007/11/01 21:10:14 |
version 1.138, 2007/12/05 06:52:01 |
Line 46 __KERNEL_RCSID(0, "$NetBSD$"); |
|
Line 46 __KERNEL_RCSID(0, "$NetBSD$"); |
|
|
|
#include <sys/param.h> |
#include <sys/param.h> |
#include <sys/systm.h> |
#include <sys/systm.h> |
|
#include <sys/bitops.h> |
#include <sys/proc.h> |
#include <sys/proc.h> |
#include <sys/errno.h> |
#include <sys/errno.h> |
#include <sys/kernel.h> |
#include <sys/kernel.h> |
Line 56 __KERNEL_RCSID(0, "$NetBSD$"); |
|
Line 57 __KERNEL_RCSID(0, "$NetBSD$"); |
|
#include <sys/debug.h> |
#include <sys/debug.h> |
#include <sys/lockdebug.h> |
#include <sys/lockdebug.h> |
#include <sys/xcall.h> |
#include <sys/xcall.h> |
|
#include <sys/cpu.h> |
|
|
#include <uvm/uvm.h> |
#include <uvm/uvm.h> |
|
|
Line 82 LIST_HEAD(,pool_cache) pool_cache_head = |
|
Line 84 LIST_HEAD(,pool_cache) pool_cache_head = |
|
/* Private pool for page header structures */ |
/* Private pool for page header structures */ |
#define PHPOOL_MAX 8 |
#define PHPOOL_MAX 8 |
static struct pool phpool[PHPOOL_MAX]; |
static struct pool phpool[PHPOOL_MAX]; |
#define PHPOOL_FREELIST_NELEM(idx) (((idx) == 0) ? 0 : (1 << (idx))) |
#define PHPOOL_FREELIST_NELEM(idx) \ |
|
(((idx) == 0) ? 0 : BITMAP_SIZE * (1 << (idx))) |
|
|
#ifdef POOL_SUBPAGE |
#ifdef POOL_SUBPAGE |
/* Pool of subpages for use by normal pools. */ |
/* Pool of subpages for use by normal pools. */ |
Line 111 static struct pool *drainpp; |
|
Line 114 static struct pool *drainpp; |
|
static kmutex_t pool_head_lock; |
static kmutex_t pool_head_lock; |
static kcondvar_t pool_busy; |
static kcondvar_t pool_busy; |
|
|
typedef uint8_t pool_item_freelist_t; |
typedef uint32_t pool_item_bitmap_t; |
|
#define BITMAP_SIZE (CHAR_BIT * sizeof(pool_item_bitmap_t)) |
|
#define BITMAP_MASK (BITMAP_SIZE - 1) |
|
|
struct pool_item_header { |
struct pool_item_header { |
/* Page headers */ |
/* Page headers */ |
Line 121 struct pool_item_header { |
|
Line 126 struct pool_item_header { |
|
ph_node; /* Off-page page headers */ |
ph_node; /* Off-page page headers */ |
void * ph_page; /* this page's address */ |
void * ph_page; /* this page's address */ |
struct timeval ph_time; /* last referenced */ |
struct timeval ph_time; /* last referenced */ |
|
uint16_t ph_nmissing; /* # of chunks in use */ |
union { |
union { |
/* !PR_NOTOUCH */ |
/* !PR_NOTOUCH */ |
struct { |
struct { |
Line 129 struct pool_item_header { |
|
Line 135 struct pool_item_header { |
|
} phu_normal; |
} phu_normal; |
/* PR_NOTOUCH */ |
/* PR_NOTOUCH */ |
struct { |
struct { |
uint16_t |
uint16_t phu_off; /* start offset in page */ |
phu_off; /* start offset in page */ |
pool_item_bitmap_t phu_bitmap[]; |
pool_item_freelist_t |
|
phu_firstfree; /* first free item */ |
|
/* |
|
* XXX it might be better to use |
|
* a simple bitmap and ffs(3) |
|
*/ |
|
} phu_notouch; |
} phu_notouch; |
} ph_u; |
} ph_u; |
uint16_t ph_nmissing; /* # of chunks in use */ |
|
}; |
}; |
#define ph_itemlist ph_u.phu_normal.phu_itemlist |
#define ph_itemlist ph_u.phu_normal.phu_itemlist |
#define ph_off ph_u.phu_notouch.phu_off |
#define ph_off ph_u.phu_notouch.phu_off |
#define ph_firstfree ph_u.phu_notouch.phu_firstfree |
#define ph_bitmap ph_u.phu_notouch.phu_bitmap |
|
|
struct pool_item { |
struct pool_item { |
#ifdef DIAGNOSTIC |
#ifdef DIAGNOSTIC |
Line 329 pr_enter_check(struct pool *pp, void (*p |
|
Line 328 pr_enter_check(struct pool *pp, void (*p |
|
#define pr_enter_check(pp, pr) |
#define pr_enter_check(pp, pr) |
#endif /* POOL_DIAGNOSTIC */ |
#endif /* POOL_DIAGNOSTIC */ |
|
|
static inline int |
static inline unsigned int |
pr_item_notouch_index(const struct pool *pp, const struct pool_item_header *ph, |
pr_item_notouch_index(const struct pool *pp, const struct pool_item_header *ph, |
const void *v) |
const void *v) |
{ |
{ |
const char *cp = v; |
const char *cp = v; |
int idx; |
unsigned int idx; |
|
|
KASSERT(pp->pr_roflags & PR_NOTOUCH); |
KASSERT(pp->pr_roflags & PR_NOTOUCH); |
idx = (cp - (char *)ph->ph_page - ph->ph_off) / pp->pr_size; |
idx = (cp - (char *)ph->ph_page - ph->ph_off) / pp->pr_size; |
Line 342 pr_item_notouch_index(const struct pool |
|
Line 341 pr_item_notouch_index(const struct pool |
|
return idx; |
return idx; |
} |
} |
|
|
#define PR_FREELIST_ALIGN(p) \ |
|
roundup((uintptr_t)(p), sizeof(pool_item_freelist_t)) |
|
#define PR_FREELIST(ph) ((pool_item_freelist_t *)PR_FREELIST_ALIGN((ph) + 1)) |
|
#define PR_INDEX_USED ((pool_item_freelist_t)-1) |
|
#define PR_INDEX_EOL ((pool_item_freelist_t)-2) |
|
|
|
static inline void |
static inline void |
pr_item_notouch_put(const struct pool *pp, struct pool_item_header *ph, |
pr_item_notouch_put(const struct pool *pp, struct pool_item_header *ph, |
void *obj) |
void *obj) |
{ |
{ |
int idx = pr_item_notouch_index(pp, ph, obj); |
unsigned int idx = pr_item_notouch_index(pp, ph, obj); |
pool_item_freelist_t *freelist = PR_FREELIST(ph); |
pool_item_bitmap_t *bitmap = ph->ph_bitmap + (idx / BITMAP_SIZE); |
|
pool_item_bitmap_t mask = 1 << (idx & BITMAP_MASK); |
|
|
KASSERT(freelist[idx] == PR_INDEX_USED); |
KASSERT((*bitmap & mask) == 0); |
freelist[idx] = ph->ph_firstfree; |
*bitmap |= mask; |
ph->ph_firstfree = idx; |
|
} |
} |
|
|
static inline void * |
static inline void * |
pr_item_notouch_get(const struct pool *pp, struct pool_item_header *ph) |
pr_item_notouch_get(const struct pool *pp, struct pool_item_header *ph) |
{ |
{ |
int idx = ph->ph_firstfree; |
pool_item_bitmap_t *bitmap = ph->ph_bitmap; |
pool_item_freelist_t *freelist = PR_FREELIST(ph); |
unsigned int idx; |
|
int i; |
|
|
KASSERT(freelist[idx] != PR_INDEX_USED); |
for (i = 0; ; i++) { |
ph->ph_firstfree = freelist[idx]; |
int bit; |
freelist[idx] = PR_INDEX_USED; |
|
|
|
|
KASSERT((i * BITMAP_SIZE) < pp->pr_itemsperpage); |
|
bit = ffs32(bitmap[i]); |
|
if (bit) { |
|
pool_item_bitmap_t mask; |
|
|
|
bit--; |
|
idx = (i * BITMAP_SIZE) + bit; |
|
mask = 1 << bit; |
|
KASSERT((bitmap[i] & mask) != 0); |
|
bitmap[i] &= ~mask; |
|
break; |
|
} |
|
} |
|
KASSERT(idx < pp->pr_itemsperpage); |
return (char *)ph->ph_page + ph->ph_off + idx * pp->pr_size; |
return (char *)ph->ph_page + ph->ph_off + idx * pp->pr_size; |
} |
} |
|
|
|
static inline void |
|
pr_item_notouch_init(const struct pool *pp, struct pool_item_header *ph) |
|
{ |
|
pool_item_bitmap_t *bitmap = ph->ph_bitmap; |
|
const int n = howmany(pp->pr_itemsperpage, BITMAP_SIZE); |
|
int i; |
|
|
|
for (i = 0; i < n; i++) { |
|
bitmap[i] = (pool_item_bitmap_t)-1; |
|
} |
|
} |
|
|
static inline int |
static inline int |
phtree_compare(struct pool_item_header *a, struct pool_item_header *b) |
phtree_compare(struct pool_item_header *a, struct pool_item_header *b) |
{ |
{ |
Line 602 pool_init(struct pool *pp, size_t size, |
|
Line 621 pool_init(struct pool *pp, size_t size, |
|
size_t trysize, phsize; |
size_t trysize, phsize; |
int off, slack; |
int off, slack; |
|
|
KASSERT((1UL << (CHAR_BIT * sizeof(pool_item_freelist_t))) - 2 >= |
|
PHPOOL_FREELIST_NELEM(PHPOOL_MAX - 1)); |
|
|
|
#ifdef DEBUG |
#ifdef DEBUG |
/* |
/* |
* Check that the pool hasn't already been initialised and |
* Check that the pool hasn't already been initialised and |
Line 786 pool_init(struct pool *pp, size_t size, |
|
Line 802 pool_init(struct pool *pp, size_t size, |
|
pp->pr_entered_file = NULL; |
pp->pr_entered_file = NULL; |
pp->pr_entered_line = 0; |
pp->pr_entered_line = 0; |
|
|
mutex_init(&pp->pr_lock, MUTEX_DEFAULT, ipl); |
/* |
|
* XXXAD hack to prevent IP input processing from blocking. |
|
*/ |
|
if (ipl == IPL_SOFTNET) { |
|
mutex_init(&pp->pr_lock, MUTEX_DEFAULT, IPL_VM); |
|
} else { |
|
mutex_init(&pp->pr_lock, MUTEX_DEFAULT, ipl); |
|
} |
cv_init(&pp->pr_cv, wchan); |
cv_init(&pp->pr_cv, wchan); |
pp->pr_ipl = ipl; |
pp->pr_ipl = ipl; |
|
|
Line 807 pool_init(struct pool *pp, size_t size, |
|
Line 830 pool_init(struct pool *pp, size_t size, |
|
"phpool-%d", nelem); |
"phpool-%d", nelem); |
sz = sizeof(struct pool_item_header); |
sz = sizeof(struct pool_item_header); |
if (nelem) { |
if (nelem) { |
sz = PR_FREELIST_ALIGN(sz) |
sz = offsetof(struct pool_item_header, |
+ nelem * sizeof(pool_item_freelist_t); |
ph_bitmap[howmany(nelem, BITMAP_SIZE)]); |
} |
} |
pool_init(&phpool[idx], sz, 0, 0, 0, |
pool_init(&phpool[idx], sz, 0, 0, 0, |
phpool_names[idx], &pool_allocator_meta, IPL_VM); |
phpool_names[idx], &pool_allocator_meta, IPL_VM); |
Line 1432 pool_prime_page(struct pool *pp, void *s |
|
Line 1455 pool_prime_page(struct pool *pp, void *s |
|
pp->pr_nitems += n; |
pp->pr_nitems += n; |
|
|
if (pp->pr_roflags & PR_NOTOUCH) { |
if (pp->pr_roflags & PR_NOTOUCH) { |
pool_item_freelist_t *freelist = PR_FREELIST(ph); |
pr_item_notouch_init(pp, ph); |
int i; |
|
|
|
ph->ph_off = (char *)cp - (char *)storage; |
|
ph->ph_firstfree = 0; |
|
for (i = 0; i < n - 1; i++) |
|
freelist[i] = i + 1; |
|
freelist[n - 1] = PR_INDEX_EOL; |
|
} else { |
} else { |
while (n--) { |
while (n--) { |
pi = (struct pool_item *)cp; |
pi = (struct pool_item *)cp; |
Line 1587 pool_reclaim(struct pool *pp) |
|
Line 1603 pool_reclaim(struct pool *pp) |
|
* and we are called from the pagedaemon without kernel_lock. |
* and we are called from the pagedaemon without kernel_lock. |
* Does not apply to IPL_SOFTBIO. |
* Does not apply to IPL_SOFTBIO. |
*/ |
*/ |
switch (pp->pr_ipl) { |
if (pp->pr_ipl == IPL_SOFTNET || pp->pr_ipl == IPL_SOFTCLOCK || |
case IPL_SOFTNET: |
pp->pr_ipl == IPL_SOFTSERIAL) { |
case IPL_SOFTCLOCK: |
|
case IPL_SOFTSERIAL: |
|
KERNEL_LOCK(1, NULL); |
KERNEL_LOCK(1, NULL); |
klock = true; |
klock = true; |
break; |
} else |
default: |
|
klock = false; |
klock = false; |
break; |
|
} |
|
|
|
/* Reclaim items from the pool's cache (if any). */ |
/* Reclaim items from the pool's cache (if any). */ |
if (pp->pr_cache != NULL) |
if (pp->pr_cache != NULL) |
Line 2036 pool_cache_bootstrap(pool_cache_t pc, si |
|
Line 2047 pool_cache_bootstrap(pool_cache_t pc, si |
|
palloc = &pool_allocator_nointr; |
palloc = &pool_allocator_nointr; |
pool_init(pp, size, align, align_offset, flags, wchan, palloc, ipl); |
pool_init(pp, size, align, align_offset, flags, wchan, palloc, ipl); |
|
|
mutex_init(&pc->pc_lock, MUTEX_DEFAULT, pp->pr_ipl); |
/* |
|
* XXXAD hack to prevent IP input processing from blocking. |
|
*/ |
|
if (ipl == IPL_SOFTNET) { |
|
mutex_init(&pc->pc_lock, MUTEX_DEFAULT, IPL_VM); |
|
} else { |
|
mutex_init(&pc->pc_lock, MUTEX_DEFAULT, ipl); |
|
} |
|
|
if (ctor == NULL) { |
if (ctor == NULL) { |
ctor = (int (*)(void *, void *, int))nullop; |
ctor = (int (*)(void *, void *, int))nullop; |
Line 2058 pool_cache_bootstrap(pool_cache_t pc, si |
|
Line 2076 pool_cache_bootstrap(pool_cache_t pc, si |
|
pc->pc_nfull = 0; |
pc->pc_nfull = 0; |
pc->pc_contended = 0; |
pc->pc_contended = 0; |
pc->pc_refcnt = 0; |
pc->pc_refcnt = 0; |
|
pc->pc_freecheck = NULL; |
|
|
/* Allocate per-CPU caches. */ |
/* Allocate per-CPU caches. */ |
memset(pc->pc_cpus, 0, sizeof(pc->pc_cpus)); |
memset(pc->pc_cpus, 0, sizeof(pc->pc_cpus)); |
pc->pc_ncpu = 0; |
pc->pc_ncpu = 0; |
for (CPU_INFO_FOREACH(cii, ci)) { |
if (ncpu == 0) { |
pool_cache_cpu_init1(ci, pc); |
/* XXX For sparc: boot CPU is not attached yet. */ |
|
pool_cache_cpu_init1(curcpu(), pc); |
|
} else { |
|
for (CPU_INFO_FOREACH(cii, ci)) { |
|
pool_cache_cpu_init1(ci, pc); |
|
} |
} |
} |
|
|
if (__predict_true(!cold)) { |
if (__predict_true(!cold)) { |
|
|
pool_cache_cpu_init1(struct cpu_info *ci, pool_cache_t pc) |
pool_cache_cpu_init1(struct cpu_info *ci, pool_cache_t pc) |
{ |
{ |
pool_cache_cpu_t *cc; |
pool_cache_cpu_t *cc; |
|
int index; |
|
|
|
index = ci->ci_index; |
|
|
|
KASSERT(index < MAXCPUS); |
KASSERT(((uintptr_t)pc->pc_cpus & (CACHE_LINE_SIZE - 1)) == 0); |
KASSERT(((uintptr_t)pc->pc_cpus & (CACHE_LINE_SIZE - 1)) == 0); |
|
|
if ((cc = pc->pc_cpus[ci->ci_index]) != NULL) { |
if ((cc = pc->pc_cpus[index]) != NULL) { |
KASSERT(cc->cc_cpu = ci); |
KASSERT(cc->cc_cpuindex == index); |
return; |
return; |
} |
} |
|
|
Line 2163 pool_cache_cpu_init1(struct cpu_info *ci |
|
Line 2191 pool_cache_cpu_init1(struct cpu_info *ci |
|
cc->cc_ipl = pc->pc_pool.pr_ipl; |
cc->cc_ipl = pc->pc_pool.pr_ipl; |
cc->cc_iplcookie = makeiplcookie(cc->cc_ipl); |
cc->cc_iplcookie = makeiplcookie(cc->cc_ipl); |
cc->cc_cache = pc; |
cc->cc_cache = pc; |
cc->cc_cpu = ci; |
cc->cc_cpuindex = index; |
cc->cc_hits = 0; |
cc->cc_hits = 0; |
cc->cc_misses = 0; |
cc->cc_misses = 0; |
cc->cc_current = NULL; |
cc->cc_current = NULL; |
cc->cc_previous = NULL; |
cc->cc_previous = NULL; |
|
|
pc->pc_cpus[ci->ci_index] = cc; |
pc->pc_cpus[index] = cc; |
} |
} |
|
|
/* |
/* |
Line 2208 pool_cache_reclaim(pool_cache_t pc) |
|
Line 2236 pool_cache_reclaim(pool_cache_t pc) |
|
return pool_reclaim(&pc->pc_pool); |
return pool_reclaim(&pc->pc_pool); |
} |
} |
|
|
|
static void |
|
pool_cache_destruct_object1(pool_cache_t pc, void *object) |
|
{ |
|
|
|
(*pc->pc_dtor)(pc->pc_arg, object); |
|
pool_put(&pc->pc_pool, object); |
|
} |
|
|
/* |
/* |
* pool_cache_destruct_object: |
* pool_cache_destruct_object: |
* |
* |
|
|
pool_cache_destruct_object(pool_cache_t pc, void *object) |
pool_cache_destruct_object(pool_cache_t pc, void *object) |
{ |
{ |
|
|
(*pc->pc_dtor)(pc->pc_arg, object); |
FREECHECK_IN(&pc->pc_freecheck, object); |
pool_put(&pc->pc_pool, object); |
|
|
pool_cache_destruct_object1(pc, object); |
} |
} |
|
|
/* |
/* |
Line 2239 pool_cache_invalidate_groups(pool_cache_ |
|
Line 2276 pool_cache_invalidate_groups(pool_cache_ |
|
|
|
for (i = 0; i < pcg->pcg_avail; i++) { |
for (i = 0; i < pcg->pcg_avail; i++) { |
object = pcg->pcg_objects[i].pcgo_va; |
object = pcg->pcg_objects[i].pcgo_va; |
pool_cache_destruct_object(pc, object); |
pool_cache_destruct_object1(pc, object); |
} |
} |
|
|
pool_put(&pcgpool, pcg); |
pool_put(&pcgpool, pcg); |
Line 2306 static inline pool_cache_cpu_t * |
|
Line 2343 static inline pool_cache_cpu_t * |
|
pool_cache_cpu_enter(pool_cache_t pc, int *s) |
pool_cache_cpu_enter(pool_cache_t pc, int *s) |
{ |
{ |
pool_cache_cpu_t *cc; |
pool_cache_cpu_t *cc; |
struct cpu_info *ci; |
|
|
|
/* |
/* |
* Prevent other users of the cache from accessing our |
* Prevent other users of the cache from accessing our |
* CPU-local data. To avoid touching shared state, we |
* CPU-local data. To avoid touching shared state, we |
* pull the neccessary information from CPU local data. |
* pull the neccessary information from CPU local data. |
*/ |
*/ |
ci = curcpu(); |
crit_enter(); |
KASSERT(ci->ci_data.cpu_index < MAXCPUS); |
cc = pc->pc_cpus[curcpu()->ci_index]; |
cc = pc->pc_cpus[ci->ci_data.cpu_index]; |
|
KASSERT(cc->cc_cache == pc); |
KASSERT(cc->cc_cache == pc); |
if (cc->cc_ipl == IPL_NONE) { |
if (cc->cc_ipl != IPL_NONE) { |
crit_enter(); |
|
} else { |
|
*s = splraiseipl(cc->cc_iplcookie); |
*s = splraiseipl(cc->cc_iplcookie); |
} |
} |
|
|
/* Moved to another CPU before disabling preemption? */ |
|
if (__predict_false(ci != curcpu())) { |
|
ci = curcpu(); |
|
cc = pc->pc_cpus[ci->ci_data.cpu_index]; |
|
} |
|
|
|
#ifdef DIAGNOSTIC |
|
KASSERT(cc->cc_cpu == ci); |
|
KASSERT(((uintptr_t)cc & (CACHE_LINE_SIZE - 1)) == 0); |
KASSERT(((uintptr_t)cc & (CACHE_LINE_SIZE - 1)) == 0); |
#endif |
|
|
|
return cc; |
return cc; |
} |
} |
Line 2342 pool_cache_cpu_exit(pool_cache_cpu_t *cc |
|
Line 2365 pool_cache_cpu_exit(pool_cache_cpu_t *cc |
|
{ |
{ |
|
|
/* No longer need exclusive access to the per-CPU data. */ |
/* No longer need exclusive access to the per-CPU data. */ |
if (cc->cc_ipl == IPL_NONE) { |
if (cc->cc_ipl != IPL_NONE) { |
crit_exit(); |
|
} else { |
|
splx(*s); |
splx(*s); |
} |
} |
|
crit_exit(); |
} |
} |
|
|
#if __GNUC_PREREQ__(3, 0) |
#if __GNUC_PREREQ__(3, 0) |