mp_fget.c
上传用户:tsgydb
上传日期:2007-04-14
资源大小:10674k
文件大小:12k
源码类别:

MySQL数据库

开发平台:

Visual C++

  1. /*-
  2.  * See the file LICENSE for redistribution information.
  3.  *
  4.  * Copyright (c) 1996, 1997, 1998, 1999, 2000
  5.  * Sleepycat Software.  All rights reserved.
  6.  */
  7. #include "db_config.h"
  8. #ifndef lint
  9. static const char revid[] = "$Id: mp_fget.c,v 11.28 2001/01/10 04:50:53 ubell Exp $";
  10. #endif /* not lint */
  11. #ifndef NO_SYSTEM_INCLUDES
  12. #include <sys/types.h>
  13. #include <string.h>
  14. #endif
  15. #ifdef  HAVE_RPC
  16. #include "db_server.h"
  17. #endif
  18. #include "db_int.h"
  19. #include "db_shash.h"
  20. #include "mp.h"
  21. #ifdef HAVE_RPC
  22. #include "gen_client_ext.h"
  23. #include "rpc_client_ext.h"
  24. #endif
  25. /*
  26.  * memp_fget --
  27.  * Get a page from the file.
  28.  */
  29. int
  30. memp_fget(dbmfp, pgnoaddr, flags, addrp)
  31. DB_MPOOLFILE *dbmfp;
  32. db_pgno_t *pgnoaddr;
  33. u_int32_t flags;
  34. void *addrp;
  35. {
  36. BH *bhp;
  37. DB_ENV *dbenv;
  38. DB_MPOOL *dbmp;
  39. DB_HASHTAB *dbht;
  40. MPOOL *c_mp, *mp;
  41. MPOOLFILE *mfp;
  42. size_t n_bucket, n_cache, mf_offset;
  43. u_int32_t st_hsearch;
  44. int b_incr, first, ret;
  45. dbmp = dbmfp->dbmp;
  46. dbenv = dbmp->dbenv;
  47. mp = dbmp->reginfo[0].primary;
  48. mfp = dbmfp->mfp;
  49. #ifdef HAVE_RPC
  50. if (F_ISSET(dbenv, DB_ENV_RPCCLIENT))
  51. return (__dbcl_memp_fget(dbmfp, pgnoaddr, flags, addrp));
  52. #endif
  53. PANIC_CHECK(dbenv);
  54. /*
  55.  * Validate arguments.
  56.  *
  57.  * !!!
  58.  * Don't test for DB_MPOOL_CREATE and DB_MPOOL_NEW flags for readonly
  59.  * files here, and create non-existent pages in readonly files if the
  60.  * flags are set, later.  The reason is that the hash access method
  61.  * wants to get empty pages that don't really exist in readonly files.
  62.  * The only alternative is for hash to write the last "bucket" all the
  63.  * time, which we don't want to do because one of our big goals in life
  64.  * is to keep database files small.  It's sleazy as hell, but we catch
  65.  * any attempt to actually write the file in memp_fput().
  66.  */
  67. #define OKFLAGS
  68.     (DB_MPOOL_CREATE | DB_MPOOL_LAST | 
  69.     DB_MPOOL_NEW | DB_MPOOL_NEW_GROUP | DB_MPOOL_EXTENT)
  70. if (flags != 0) {
  71. if ((ret = __db_fchk(dbenv, "memp_fget", flags, OKFLAGS)) != 0)
  72. return (ret);
  73. switch (flags & ~DB_MPOOL_EXTENT) {
  74. case DB_MPOOL_CREATE:
  75. case DB_MPOOL_LAST:
  76. case DB_MPOOL_NEW:
  77. case DB_MPOOL_NEW_GROUP:
  78. case 0:
  79. break;
  80. default:
  81. return (__db_ferr(dbenv, "memp_fget", 1));
  82. }
  83. }
  84. #ifdef DIAGNOSTIC
  85. /*
  86.  * XXX
  87.  * We want to switch threads as often as possible.  Yield every time
  88.  * we get a new page to ensure contention.
  89.  */
  90. if (DB_GLOBAL(db_pageyield))
  91. __os_yield(dbenv, 1);
  92. #endif
  93. /* Initialize remaining local variables. */
  94. mf_offset = R_OFFSET(dbmp->reginfo, mfp);
  95. bhp = NULL;
  96. st_hsearch = 0;
  97. b_incr = ret = 0;
  98. R_LOCK(dbenv, dbmp->reginfo);
  99. /*
  100.  * Check for the new, last or last + 1 page requests.
  101.  *
  102.  * Examine and update the file's last_pgno value.  We don't care if
  103.  * the last_pgno value immediately changes due to another thread --
  104.  * at this instant in time, the value is correct.  We do increment the
  105.  * current last_pgno value if the thread is asking for a new page,
  106.  * however, to ensure that two threads creating pages don't get the
  107.  * same one.
  108.  *
  109.  * If we create a page, there is the potential that a page after it
  110.  * in the file will be written before it will be written.  Recovery
  111.  * depends on pages that are "created" in the file by subsequent pages
  112.  * being written be zeroed out, not have random garbage.  Ensure that
  113.  * the OS agrees.
  114.  *
  115.  * !!!
  116.  * DB_MPOOL_NEW_GROUP is undocumented -- the hash access method needs
  117.  * to allocate contiguous groups of pages in order to do subdatabases.
  118.  * We return the first page in the group, but the caller must put an
  119.  * LSN on the *last* page and write it, otherwise after a crash we may
  120.  * not create all of the pages we need to create.
  121.  */
  122. if (LF_ISSET(DB_MPOOL_LAST | DB_MPOOL_NEW | DB_MPOOL_NEW_GROUP)) {
  123. if (LF_ISSET(DB_MPOOL_NEW)) {
  124. if (F_ISSET(&dbmfp->fh, DB_FH_VALID) && (ret =
  125.     __os_fpinit(dbenv, &dbmfp->fh, mfp->last_pgno + 1,
  126.     1, mfp->stat.st_pagesize)) != 0) {
  127. R_UNLOCK(dbenv, dbmp->reginfo);
  128. return (ret);
  129. }
  130. ++mfp->last_pgno;
  131. }
  132. if (LF_ISSET(DB_MPOOL_NEW_GROUP)) {
  133. if (F_ISSET(&dbmfp->fh, DB_FH_VALID) && (ret =
  134.     __os_fpinit(dbenv, &dbmfp->fh, mfp->last_pgno + 1,
  135.     (int)*pgnoaddr, mfp->stat.st_pagesize)) != 0) {
  136. R_UNLOCK(dbenv, dbmp->reginfo);
  137. return (ret);
  138. }
  139. mfp->last_pgno += *pgnoaddr;
  140. }
  141. *pgnoaddr = mfp->last_pgno;
  142. }
  143. /*
  144.  * Determine the hash bucket where this page will live, and get local
  145.  * pointers to the cache and its hash table.
  146.  */
  147. n_cache = NCACHE(mp, *pgnoaddr);
  148. c_mp = dbmp->reginfo[n_cache].primary;
  149. n_bucket = NBUCKET(c_mp, mf_offset, *pgnoaddr);
  150. dbht = R_ADDR(&dbmp->reginfo[n_cache], c_mp->htab);
  151. if (LF_ISSET(DB_MPOOL_NEW | DB_MPOOL_NEW_GROUP))
  152. goto alloc;
  153. /*
  154.  * If mmap'ing the file and the page is not past the end of the file,
  155.  * just return a pointer.
  156.  *
  157.  * The page may be past the end of the file, so check the page number
  158.  * argument against the original length of the file.  If we previously
  159.  * returned pages past the original end of the file, last_pgno will
  160.  * have been updated to match the "new" end of the file, and checking
  161.  * against it would return pointers past the end of the mmap'd region.
  162.  *
  163.  * If another process has opened the file for writing since we mmap'd
  164.  * it, we will start playing the game by their rules, i.e. everything
  165.  * goes through the cache.  All pages previously returned will be safe,
  166.  * as long as the correct locking protocol was observed.
  167.  *
  168.  * XXX
  169.  * We don't discard the map because we don't know when all of the
  170.  * pages will have been discarded from the process' address space.
  171.  * It would be possible to do so by reference counting the open
  172.  * pages from the mmap, but it's unclear to me that it's worth it.
  173.  */
  174. if (dbmfp->addr != NULL && F_ISSET(mfp, MP_CAN_MMAP)) {
  175. if (*pgnoaddr > mfp->orig_last_pgno) {
  176. /*
  177.  * !!!
  178.  * See the comment above about non-existent pages and
  179.  * the hash access method.
  180.  */
  181. if (!LF_ISSET(DB_MPOOL_CREATE)) {
  182. if (!LF_ISSET(DB_MPOOL_EXTENT))
  183. __db_err(dbenv,
  184.     "%s: page %lu doesn't exist",
  185.     __memp_fn(dbmfp), (u_long)*pgnoaddr);
  186. ret = EINVAL;
  187. goto err;
  188. }
  189. } else {
  190. *(void **)addrp =
  191.     R_ADDR(dbmfp, *pgnoaddr * mfp->stat.st_pagesize);
  192. ++mfp->stat.st_map;
  193. goto done;
  194. }
  195. }
  196. /* Search the hash chain for the page. */
  197. for (bhp = SH_TAILQ_FIRST(&dbht[n_bucket], __bh);
  198.     bhp != NULL; bhp = SH_TAILQ_NEXT(bhp, hq, __bh)) {
  199. ++st_hsearch;
  200. if (bhp->pgno != *pgnoaddr || bhp->mf_offset != mf_offset)
  201. continue;
  202. /* Increment the reference count. */
  203. if (bhp->ref == UINT16_T_MAX) {
  204. __db_err(dbenv,
  205.     "%s: page %lu: reference count overflow",
  206.     __memp_fn(dbmfp), (u_long)bhp->pgno);
  207. ret = EINVAL;
  208. goto err;
  209. }
  210. /*
  211.  * Increment the reference count.  We may discard the region
  212.  * lock as we evaluate and/or read the buffer, so we need to
  213.  * ensure that it doesn't move and that its contents remain
  214.  * unchanged.
  215.  */
  216. ++bhp->ref;
  217. b_incr = 1;
  218. /*
  219.  * Any buffer we find might be trouble.
  220.  *
  221.  * BH_LOCKED --
  222.  * I/O is in progress.  Because we've incremented the buffer
  223.  * reference count, we know the buffer can't move.  Unlock
  224.  * the region lock, wait for the I/O to complete, and reacquire
  225.  * the region.
  226.  */
  227. for (first = 1; F_ISSET(bhp, BH_LOCKED); first = 0) {
  228. R_UNLOCK(dbenv, dbmp->reginfo);
  229. /*
  230.  * Explicitly yield the processor if it's not the first
  231.  * pass through this loop -- if we don't, we might end
  232.  * up running to the end of our CPU quantum as we will
  233.  * simply be swapping between the two locks.
  234.  */
  235. if (!first)
  236. __os_yield(dbenv, 1);
  237. MUTEX_LOCK(dbenv, &bhp->mutex, dbenv->lockfhp);
  238. /* Wait for I/O to finish... */
  239. MUTEX_UNLOCK(dbenv, &bhp->mutex);
  240. R_LOCK(dbenv, dbmp->reginfo);
  241. }
  242. /*
  243.  * BH_TRASH --
  244.  * The contents of the buffer are garbage.  Shouldn't happen,
  245.  * and this read is likely to fail, but might as well try.
  246.  */
  247. if (F_ISSET(bhp, BH_TRASH))
  248. goto reread;
  249. /*
  250.  * BH_CALLPGIN --
  251.  * The buffer was converted so it could be written, and the
  252.  * contents need to be converted again.
  253.  */
  254. if (F_ISSET(bhp, BH_CALLPGIN)) {
  255. if ((ret = __memp_pg(dbmfp, bhp, 1)) != 0)
  256. goto err;
  257. F_CLR(bhp, BH_CALLPGIN);
  258. }
  259. ++mfp->stat.st_cache_hit;
  260. *(void **)addrp = bhp->buf;
  261. goto done;
  262. }
  263. alloc: /* Allocate new buffer header and data space. */
  264. if ((ret = __memp_alloc(dbmp,
  265.     &dbmp->reginfo[n_cache], mfp, 0, NULL, &bhp)) != 0)
  266. goto err;
  267. ++c_mp->stat.st_page_clean;
  268. /*
  269.  * Initialize the BH fields so that we can call the __memp_bhfree
  270.  * routine if an error occurs.
  271.  */
  272. memset(bhp, 0, sizeof(BH));
  273. bhp->ref = 1;
  274. bhp->pgno = *pgnoaddr;
  275. bhp->mf_offset = mf_offset;
  276. /* Increment the count of buffers referenced by this MPOOLFILE. */
  277. ++mfp->block_cnt;
  278. /*
  279.  * Prepend the bucket header to the head of the appropriate MPOOL
  280.  * bucket hash list.  Append the bucket header to the tail of the
  281.  * MPOOL LRU chain.
  282.  */
  283. SH_TAILQ_INSERT_HEAD(&dbht[n_bucket], bhp, hq, __bh);
  284. SH_TAILQ_INSERT_TAIL(&c_mp->bhq, bhp, q);
  285. #ifdef DIAGNOSTIC
  286. if ((db_alignp_t)bhp->buf & (sizeof(size_t) - 1)) {
  287. __db_err(dbenv, "Internal error: BH data NOT size_t aligned.");
  288. ret = EINVAL;
  289. __memp_bhfree(dbmp, bhp, 1);
  290. goto err;
  291. }
  292. #endif
  293. if ((ret = __db_shmutex_init(dbenv, &bhp->mutex,
  294.     R_OFFSET(dbmp->reginfo, &bhp->mutex) + DB_FCNTL_OFF_MPOOL,
  295.     0, &dbmp->reginfo[n_cache],
  296.     (REGMAINT *)R_ADDR(&dbmp->reginfo[n_cache], c_mp->maint_off)))
  297.     != 0) {
  298. __memp_bhfree(dbmp, bhp, 1);
  299. goto err;
  300. }
  301. /*
  302.  * If we created the page, zero it out and continue.
  303.  *
  304.  * !!!
  305.  * Note: DB_MPOOL_NEW specifically doesn't call the pgin function.
  306.  * If DB_MPOOL_CREATE is used, then the application's pgin function
  307.  * has to be able to handle pages of 0's -- if it uses DB_MPOOL_NEW,
  308.  * it can detect all of its page creates, and not bother.
  309.  *
  310.  * If we're running in diagnostic mode, smash any bytes on the
  311.  * page that are unknown quantities for the caller.
  312.  *
  313.  * Otherwise, read the page into memory, optionally creating it if
  314.  * DB_MPOOL_CREATE is set.
  315.  */
  316. if (LF_ISSET(DB_MPOOL_NEW | DB_MPOOL_NEW_GROUP)) {
  317. if (mfp->clear_len == 0)
  318. memset(bhp->buf, 0, mfp->stat.st_pagesize);
  319. else {
  320. memset(bhp->buf, 0, mfp->clear_len);
  321. #ifdef DIAGNOSTIC
  322. memset(bhp->buf + mfp->clear_len, CLEAR_BYTE,
  323.     mfp->stat.st_pagesize - mfp->clear_len);
  324. #endif
  325. }
  326. ++mfp->stat.st_page_create;
  327. } else {
  328. /*
  329.  * It's possible for the read function to fail, which means
  330.  * that we fail as well.  Note, the __memp_pgread() function
  331.  * discards the region lock, so the buffer must be pinned
  332.  * down so that it cannot move and its contents are unchanged.
  333.  */
  334. reread: if ((ret = __memp_pgread(dbmfp,
  335.     bhp, LF_ISSET(DB_MPOOL_CREATE|DB_MPOOL_EXTENT))) != 0) {
  336. /*
  337.  * !!!
  338.  * Discard the buffer unless another thread is waiting
  339.  * on our I/O to complete.  Regardless, the header has
  340.  * the BH_TRASH flag set.
  341.  */
  342. if (bhp->ref == 1)
  343. __memp_bhfree(dbmp, bhp, 1);
  344. goto err;
  345. }
  346. ++mfp->stat.st_cache_miss;
  347. }
  348. /*
  349.  * If we're returning a page after our current notion of the last-page,
  350.  * update our information.  Note, there's no way to un-instantiate this
  351.  * page, it's going to exist whether it's returned to us dirty or not.
  352.  */
  353. if (bhp->pgno > mfp->last_pgno)
  354. mfp->last_pgno = bhp->pgno;
  355. *(void **)addrp = bhp->buf;
  356. done: /* Update the chain search statistics. */
  357. if (st_hsearch) {
  358. ++c_mp->stat.st_hash_searches;
  359. if (st_hsearch > c_mp->stat.st_hash_longest)
  360. c_mp->stat.st_hash_longest = st_hsearch;
  361. c_mp->stat.st_hash_examined += st_hsearch;
  362. }
  363. ++dbmfp->pinref;
  364. R_UNLOCK(dbenv, dbmp->reginfo);
  365. return (0);
  366. err: /* Discard our reference. */
  367. if (b_incr)
  368. --bhp->ref;
  369. R_UNLOCK(dbenv, dbmp->reginfo);
  370. *(void **)addrp = NULL;
  371. return (ret);
  372. }