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 }