1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
|
/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 1996, 1997, 1998, 1999
* Sleepycat Software. All rights reserved.
*/
#include "db_config.h"
#ifndef lint
static const char sccsid[] = "@(#)mp_alloc.c 11.3 (Sleepycat) 9/29/99";
#endif /* not lint */
#ifndef NO_SYSTEM_INCLUDES
#include <sys/types.h>
#endif
#include "db_int.h"
#include "db_shash.h"
#include "mp.h"
/*
* CDB___memp_alloc --
* Allocate some space in the mpool region.
*
* PUBLIC: int CDB___memp_alloc __P((DB_MPOOL *,
* PUBLIC: REGINFO *, MPOOLFILE *, size_t, roff_t *, void *));
*/
int
CDB___memp_alloc(dbmp, memreg, mfp, len, offsetp, retp)
DB_MPOOL *dbmp;
REGINFO *memreg;
MPOOLFILE *mfp;
size_t len;
roff_t *offsetp;
void *retp;
{
BH *bhp, *nbhp;
MCACHE *mc;
MPOOL *mp;
MPOOLFILE *bh_mfp;
size_t total;
int nomore, restart, ret, wrote;
void *p;
mp = dbmp->reginfo.primary;
mc = memreg->primary;
/*
* If we're allocating a buffer, and the one we're discarding is the
* same size, we don't want to waste the time to re-integrate it into
* the shared memory free list. If the DB_MPOOLFILE argument isn't
* NULL, we'll compare the underlying page sizes of the two buffers
* before free-ing and re-allocating buffers.
*/
if (mfp != NULL)
len = (sizeof(BH) - sizeof(u_int8_t)) + mfp->stat.st_pagesize;
nomore = 0;
alloc: if ((ret = CDB___db_shalloc(memreg->addr, len, MUTEX_ALIGN, &p)) == 0) {
if (offsetp != NULL)
*offsetp = R_OFFSET(memreg, p);
*(void **)retp = p;
return (0);
}
if (nomore) {
CDB___db_err(dbmp->dbenv,
"Unable to allocate %lu bytes from mpool shared region: %s\n",
(u_long)len, CDB_db_strerror(ret));
return (ret);
}
retry: /* Find a buffer we can flush; pure LRU. */
restart = total = 0;
for (bhp =
SH_TAILQ_FIRST(&mc->bhq, __bh); bhp != NULL; bhp = nbhp) {
nbhp = SH_TAILQ_NEXT(bhp, q, __bh);
/* Ignore pinned or locked (I/O in progress) buffers. */
if (bhp->ref != 0 || F_ISSET(bhp, BH_LOCKED))
continue;
/* Find the associated MPOOLFILE. */
bh_mfp = R_ADDR(&dbmp->reginfo, bhp->mf_offset);
/* Write the page if it's dirty. */
if (F_ISSET(bhp, BH_DIRTY)) {
++bhp->ref;
if ((ret = CDB___memp_bhwrite(dbmp,
bh_mfp, bhp, &restart, &wrote)) != 0)
return (ret);
--bhp->ref;
/*
* Another process may have acquired this buffer and
* incremented the ref count after we wrote it.
*/
if (bhp->ref != 0)
goto retry;
/*
* If we wrote the page, continue and free the buffer.
* We don't have to rewalk the list to acquire the
* buffer because it was never available for any other
* process to modify it.
*
* If we didn't write the page, but we discarded and
* reacquired the region lock, restart the list walk.
*
* If we neither wrote the buffer nor discarded the
* region lock, continue down the buffer list.
*/
if (wrote)
++mc->stat.st_rw_evict;
else {
if (restart)
goto retry;
continue;
}
} else
++mc->stat.st_ro_evict;
/*
* Check to see if the buffer is the size we're looking for.
* If it is, simply reuse it.
*/
if (mfp != NULL &&
mfp->stat.st_pagesize == bh_mfp->stat.st_pagesize) {
CDB___memp_bhfree(dbmp, bhp, 0);
if (offsetp != NULL)
*offsetp = R_OFFSET(memreg, bhp);
*(void **)retp = bhp;
return (0);
}
/* Note how much space we've freed, and free the buffer. */
total += CDB___db_shsizeof(bhp);
CDB___memp_bhfree(dbmp, bhp, 1);
/*
* Retry as soon as we've freed up sufficient space. If we
* have to coalesce of memory to satisfy the request, don't
* try until it's likely (possible?) that we'll succeed.
*/
if (total >= 3 * len)
goto alloc;
/* Restart the walk if we discarded the region lock. */
if (restart)
goto retry;
}
nomore = 1;
goto alloc;
}
|