db

mp_fget.c

Go to the documentation of this file.
00001 /*-
00002  * See the file LICENSE for redistribution information.
00003  *
00004  * Copyright (c) 1996, 1997, 1998, 1999, 2000
00005  * Sleepycat Software. All rights reserved.
00006  */
00007 #include "config.h"
00008 
00009 #ifndef lint
00010 static const char revid[] = "$Id: mp__fget_8c-source.html,v 1.1 2008年06月08日 10:20:45 sebdiaz Exp $";
00011 #endif /* not lint */
00012 
00013 #ifndef NO_SYSTEM_INCLUDES
00014 #include <sys/types.h>
00015 
00016 #include <errno.h>
00017 #include <string.h>
00018 #endif
00019 
00020 #ifdef HAVE_RPC
00021 #include "db_server.h"
00022 #endif
00023 
00024 #include "db_int.h"
00025 #include "db_shash.h"
00026 #include "db_page.h"
00027 #include "mp.h"
00028 
00029 #ifdef DEBUG
00030 #include "WordMonitor.h"
00031 #endif /* DEBUG */
00032 
00033 #ifdef HAVE_RPC
00034 #include "gen_client_ext.h"
00035 #include "rpc_client_ext.h"
00036 #endif
00037 
00038 /*
00039  * CDB_memp_fget --
00040  * Get a page from the file.
00041  */
00042 int
00043 CDB_memp_fget(dbmfp, pgnoaddr, flags, addrp)
00044 DB_MPOOLFILE *dbmfp;
00045 db_pgno_t *pgnoaddr;
00046 u_int32_t flags;
00047 void *addrp;
00048 {
00049 BH *bhp;
00050 DB_ENV *dbenv;
00051 DB_MPOOL *dbmp;
00052 DB_HASHTAB *dbht;
00053 MPOOL *c_mp, *mp;
00054 MPOOLFILE *mfp;
00055 size_t n_bucket, n_cache, mf_offset;
00056 u_int32_t st_hsearch;
00057 int b_incr, first, ret;
00058 
00059 dbmp = dbmfp->dbmp;
00060 dbenv = dbmp->dbenv;
00061 mp = dbmp->reginfo[0].primary;
00062 mfp = dbmfp->mfp;
00063 #ifdef HAVE_RPC
00064 if (F_ISSET(dbenv, DB_ENV_RPCCLIENT))
00065 return (__dbcl_memp_fget(dbmfp, pgnoaddr, flags, addrp));
00066 #endif
00067 
00068 PANIC_CHECK(dbenv);
00069 
00070 /*
00071  * Validate arguments.
00072  *
00073  * !!!
00074  * Don't test for DB_MPOOL_CREATE and DB_MPOOL_NEW flags for readonly
00075  * files here, and create non-existent pages in readonly files if the
00076  * flags are set, later. The reason is that the hash access method
00077  * wants to get empty pages that don't really exist in readonly files.
00078  * The only alternative is for hash to write the last "bucket" all the
00079  * time, which we don't want to do because one of our big goals in life
00080  * is to keep database files small. It's sleazy as hell, but we catch
00081  * any attempt to actually write the file in CDB_memp_fput().
00082  */
00083 #define OKFLAGS \
00084  (DB_MPOOL_CREATE | DB_MPOOL_LAST | DB_MPOOL_NEW | DB_MPOOL_NEW_GROUP)
00085 if (flags != 0) {
00086 if ((ret = CDB___db_fchk(dbenv, "CDB_memp_fget", flags, OKFLAGS)) != 0)
00087 return (ret);
00088 
00089 switch (flags) {
00090 case DB_MPOOL_CREATE:
00091 case DB_MPOOL_LAST:
00092 case DB_MPOOL_NEW:
00093 case DB_MPOOL_NEW_GROUP:
00094 case 0:
00095 break;
00096 default:
00097 return (CDB___db_ferr(dbenv, "CDB_memp_fget", 1));
00098 }
00099 }
00100 
00101 #ifdef DIAGNOSTIC
00102 /*
00103  * XXX
00104  * We want to switch threads as often as possible. Yield every time
00105  * we get a new page to ensure contention.
00106  */
00107 if (DB_GLOBAL(db_pageyield))
00108 CDB___os_yield(dbenv, 1);
00109 #endif
00110 
00111 /* Initialize remaining local variables. */
00112 mf_offset = R_OFFSET(dbmp->reginfo, mfp);
00113 bhp = NULL;
00114 st_hsearch = 0;
00115 b_incr = ret = 0;
00116 
00117 R_LOCK(dbenv, dbmp->reginfo);
00118 
00119 /*
00120  * Check for the new, last or last + 1 page requests.
00121  *
00122  * Examine and update the file's last_pgno value. We don't care if
00123  * the last_pgno value immediately changes due to another thread --
00124  * at this instant in time, the value is correct. We do increment the
00125  * current last_pgno value if the thread is asking for a new page,
00126  * however, to ensure that two threads creating pages don't get the
00127  * same one.
00128  *
00129  * If we create a page, there is the potential that a page after it
00130  * in the file will be written before it will be written. Recovery
00131  * depends on pages that are "created" in the file by subsequent pages
00132  * being written be zeroed out, not have random garbage. Ensure that
00133  * the OS agrees.
00134  *
00135  * !!!
00136  * DB_MPOOL_NEW_GROUP is undocumented -- the hash access method needs
00137  * to allocate contiguous groups of pages in order to do subdatabases.
00138  * We return the first page in the group, but the caller must put an
00139  * LSN on the *last* page and write it, otherwise after a crash we may
00140  * not create all of the pages we need to create.
00141  */
00142 if (LF_ISSET(DB_MPOOL_LAST | DB_MPOOL_NEW | DB_MPOOL_NEW_GROUP)) {
00143 if (LF_ISSET(DB_MPOOL_NEW)) {
00144 if ((ret = CDB___os_fpinit(dbenv,
00145 &dbmfp->fh, mfp->last_pgno + 1,
00146 1, mfp->stat.st_pagesize)) != 0) {
00147 R_UNLOCK(dbenv, dbmp->reginfo);
00148 return (ret);
00149 }
00150 ++mfp->last_pgno;
00151 #ifdef DEBUG
00152 word_monitor_set(DB_MONITOR(dbenv), WORD_MONITOR_PGNO, mfp->last_pgno);
00153 #endif /* DEBUG */
00154 }
00155 if (LF_ISSET(DB_MPOOL_NEW_GROUP)) {
00156 if ((ret = CDB___os_fpinit(dbenv,
00157 &dbmfp->fh, mfp->last_pgno + 1,
00158 (int)*pgnoaddr, mfp->stat.st_pagesize)) != 0) {
00159 R_UNLOCK(dbenv, dbmp->reginfo);
00160 return (ret);
00161 }
00162 mfp->last_pgno += *pgnoaddr;
00163 }
00164 *pgnoaddr = mfp->last_pgno;
00165 }
00166 
00167 /*
00168  * Determine the hash bucket where this page will live, and get local
00169  * pointers to the cache and its hash table.
00170  */
00171 n_cache = NCACHE(mp, *pgnoaddr);
00172 c_mp = dbmp->reginfo[n_cache].primary;
00173 n_bucket = NBUCKET(c_mp, mf_offset, *pgnoaddr);
00174 dbht = R_ADDR(&dbmp->reginfo[n_cache], c_mp->htab);
00175 
00176 if (LF_ISSET(DB_MPOOL_NEW | DB_MPOOL_NEW_GROUP))
00177 goto alloc;
00178 
00179 /*
00180  * If mmap'ing the file and the page is not past the end of the file,
00181  * just return a pointer.
00182  *
00183  * The page may be past the end of the file, so check the page number
00184  * argument against the original length of the file. If we previously
00185  * returned pages past the original end of the file, last_pgno will
00186  * have been updated to match the "new" end of the file, and checking
00187  * against it would return pointers past the end of the mmap'd region.
00188  *
00189  * If another process has opened the file for writing since we mmap'd
00190  * it, we will start playing the game by their rules, i.e. everything
00191  * goes through the cache. All pages previously returned will be safe,
00192  * as long as the correct locking protocol was observed.
00193  *
00194  * XXX
00195  * We don't discard the map because we don't know when all of the
00196  * pages will have been discarded from the process' address space.
00197  * It would be possible to do so by reference counting the open
00198  * pages from the mmap, but it's unclear to me that it's worth it.
00199  */
00200 if (dbmfp->addr != NULL && F_ISSET(mfp, MP_CAN_MMAP)) {
00201 if (*pgnoaddr > mfp->orig_last_pgno) {
00202 /*
00203  * !!!
00204  * See the comment above about non-existent pages and
00205  * the hash access method.
00206  */
00207 if (!LF_ISSET(DB_MPOOL_CREATE)) {
00208 CDB___db_err(dbenv, "%s: page %lu doesn't exist",
00209 CDB___memp_fn(dbmfp), (u_long)*pgnoaddr);
00210 ret = EINVAL;
00211 goto err;
00212 }
00213 } else {
00214 *(void **)addrp =
00215 R_ADDR(dbmfp, *pgnoaddr * mfp->stat.st_pagesize);
00216 ++mfp->stat.st_map;
00217 goto done;
00218 }
00219 }
00220 
00221 /* Search the hash chain for the page. */
00222 for (bhp = SH_TAILQ_FIRST(&dbht[n_bucket], __bh);
00223 bhp != NULL; bhp = SH_TAILQ_NEXT(bhp, hq, __bh)) {
00224 ++st_hsearch;
00225 if (bhp->pgno != *pgnoaddr || bhp->mf_offset != mf_offset)
00226 continue;
00227 
00228 /* Increment the reference count. */
00229 if (bhp->ref == UINT16_T_MAX) {
00230 CDB___db_err(dbenv,
00231 "%s: page %lu: reference count overflow",
00232 CDB___memp_fn(dbmfp), (u_long)bhp->pgno);
00233 ret = EINVAL;
00234 goto err;
00235 }
00236 
00237 /*
00238  * Increment the reference count. We may discard the region
00239  * lock as we evaluate and/or read the buffer, so we need to
00240  * ensure that it doesn't move and that its contents remain
00241  * unchanged.
00242  */
00243 ++bhp->ref;
00244 b_incr = 1;
00245 
00246 /*
00247  * Any buffer we find might be trouble.
00248  *
00249  * BH_LOCKED --
00250  * I/O is in progress. Because we've incremented the buffer
00251  * reference count, we know the buffer can't move. Unlock
00252  * the region lock, wait for the I/O to complete, and reacquire
00253  * the region.
00254  */
00255 for (first = 1; F_ISSET(bhp, BH_LOCKED); first = 0) {
00256 R_UNLOCK(dbenv, dbmp->reginfo);
00257 
00258 /*
00259  * Explicitly yield the processor if it's not the first
00260  * pass through this loop -- if we don't, we might end
00261  * up running to the end of our CPU quantum as we will
00262  * simply be swapping between the two locks.
00263  */
00264 if (!first)
00265 CDB___os_yield(dbenv, 1);
00266 
00267 MUTEX_LOCK(&bhp->mutex, dbenv->lockfhp);
00268 /* Wait for I/O to finish... */
00269 MUTEX_UNLOCK(&bhp->mutex);
00270 R_LOCK(dbenv, dbmp->reginfo);
00271 }
00272 
00273 /*
00274  * BH_TRASH --
00275  * The contents of the buffer are garbage. Shouldn't happen,
00276  * and this read is likely to fail, but might as well try.
00277  */
00278 if (F_ISSET(bhp, BH_TRASH))
00279 goto reread;
00280 
00281 /*
00282  * BH_CALLPGIN --
00283  * The buffer was converted so it could be written, and the
00284  * contents need to be converted again.
00285  */
00286 if (F_ISSET(bhp, BH_CALLPGIN)) {
00287 if ((ret = CDB___memp_pg(dbmfp, bhp, 1)) != 0)
00288 goto err;
00289 F_CLR(bhp, BH_CALLPGIN);
00290 }
00291 
00292 ++mfp->stat.st_cache_hit;
00293 *(void **)addrp = bhp->buf;
00294 goto done;
00295 }
00296 
00297 alloc: /* Allocate new buffer header and data space. */
00298 if ((ret = CDB___memp_alloc(dbmp,
00299 &dbmp->reginfo[n_cache], mfp, 0, NULL, &bhp)) != 0)
00300 goto err;
00301 
00302 ++c_mp->stat.st_page_clean;
00303 
00304 /*
00305  * Initialize the BH fields so that we can call the CDB___memp_bhfree
00306  * routine if an error occurs.
00307  */
00308 memset(bhp, 0, sizeof(BH));
00309 bhp->ref = 1;
00310 bhp->pgno = *pgnoaddr;
00311 bhp->mf_offset = mf_offset;
00312 
00313 /* Increment the count of buffers referenced by this MPOOLFILE. */
00314 ++mfp->ref_cnt;
00315 
00316 /*
00317  * Prepend the bucket header to the head of the appropriate MPOOL
00318  * bucket hash list. Append the bucket header to the tail of the
00319  * MPOOL LRU chain.
00320  */
00321 SH_TAILQ_INSERT_HEAD(&dbht[n_bucket], bhp, hq, __bh);
00322 SH_TAILQ_INSERT_TAIL(&c_mp->bhq, bhp, q);
00323 
00324 #ifdef DIAGNOSTIC
00325 if ((db_alignp_t)bhp->buf & (sizeof(size_t) - 1)) {
00326 CDB___db_err(dbenv, "Internal error: BH data NOT size_t aligned.");
00327 ret = EINVAL;
00328 CDB___memp_bhfree(dbmp, bhp, 1);
00329 goto err;
00330 }
00331 #endif
00332 
00333 if ((ret = __db_mutex_init(dbenv, &bhp->mutex, R_OFFSET(
00334 dbmp->reginfo, &bhp->mutex) + DB_FCNTL_OFF_MPOOL, 0)) != 0) {
00335 CDB___memp_bhfree(dbmp, bhp, 1);
00336 goto err;
00337 }
00338 
00339 /*
00340  * If we created the page, zero it out and continue.
00341  *
00342  * !!!
00343  * Note: DB_MPOOL_NEW specifically doesn't call the pgin function.
00344  * If DB_MPOOL_CREATE is used, then the application's pgin function
00345  * has to be able to handle pages of 0's -- if it uses DB_MPOOL_NEW,
00346  * it can detect all of its page creates, and not bother.
00347  *
00348  * Otherwise, read the page into memory, optionally creating it if
00349  * DB_MPOOL_CREATE is set.
00350  */
00351 if (LF_ISSET(DB_MPOOL_NEW | DB_MPOOL_NEW_GROUP)) {
00352 if (mfp->clear_len == 0)
00353 memset(bhp->buf, 0, mfp->stat.st_pagesize);
00354 else {
00355 memset(bhp->buf, 0, mfp->clear_len);
00356 #ifdef DIAGNOSTIC
00357 memset(bhp->buf + mfp->clear_len, CLEAR_BYTE,
00358 mfp->stat.st_pagesize - mfp->clear_len);
00359 #endif
00360 }
00361 
00362 ++mfp->stat.st_page_create;
00363 } else {
00364 /*
00365  * It's possible for the read function to fail, which means
00366  * that we fail as well. Note, the CDB___memp_pgread() function
00367  * discards the region lock, so the buffer must be pinned
00368  * down so that it cannot move and its contents are unchanged.
00369  */
00370 reread: if ((ret = CDB___memp_pgread(
00371 dbmfp, bhp, LF_ISSET(DB_MPOOL_CREATE))) != 0) {
00372 /*
00373  * !!!
00374  * Discard the buffer unless another thread is waiting
00375  * on our I/O to complete. Regardless, the header has
00376  * the BH_TRASH flag set.
00377  */
00378 if (bhp->ref == 1)
00379 CDB___memp_bhfree(dbmp, bhp, 1);
00380 goto err;
00381 }
00382 
00383 ++mfp->stat.st_cache_miss;
00384 }
00385 
00386 /*
00387  * If we're returning a page after our current notion of the last-page,
00388  * update our information. Note, there's no way to un-instantiate this
00389  * page, it's going to exist whether it's returned to us dirty or not.
00390  */
00391 if (bhp->pgno > mfp->last_pgno)
00392 mfp->last_pgno = bhp->pgno;
00393 
00394 *(void **)addrp = bhp->buf;
00395 
00396 done: /* Update the chain search statistics. */
00397 if (st_hsearch) {
00398 ++c_mp->stat.st_hash_searches;
00399 if (st_hsearch > c_mp->stat.st_hash_longest)
00400 c_mp->stat.st_hash_longest = st_hsearch;
00401 c_mp->stat.st_hash_examined += st_hsearch;
00402 }
00403 
00404 ++dbmfp->pinref;
00405 
00406 R_UNLOCK(dbenv, dbmp->reginfo);
00407 
00408 return (0);
00409 
00410 err: /* Discard our reference. */
00411 if (b_incr)
00412 --bhp->ref;
00413 R_UNLOCK(dbenv, dbmp->reginfo);
00414 
00415 *(void **)addrp = NULL;
00416 return (ret);
00417 }

Generated on Sun Jun 8 10:56:38 2008 for GNUmifluz by doxygen 1.5.5

AltStyle によって変換されたページ (->オリジナル) /