mp_fget.c
上传用户:romrleung
上传日期:2022-05-23
资源大小:18897k
文件大小:19k
源码类别:

MySQL数据库

开发平台:

Visual C++

  1. /*-
  2.  * See the file LICENSE for redistribution information.
  3.  *
  4.  * Copyright (c) 1996-2002
  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.68 2002/08/06 04:58:09 bostic Exp $";
  10. #endif /* not lint */
  11. #ifndef NO_SYSTEM_INCLUDES
  12. #include <sys/types.h>
  13. #include <string.h>
  14. #endif
  15. #include "db_int.h"
  16. #include "dbinc/db_shash.h"
  17. #include "dbinc/mp.h"
  18. #ifdef HAVE_FILESYSTEM_NOTZERO
  19. static int __memp_fs_notzero
  20.     __P((DB_ENV *, DB_MPOOLFILE *, MPOOLFILE *, db_pgno_t *));
  21. #endif
  22. /*
  23.  * __memp_fget --
  24.  * Get a page from the file.
  25.  *
  26.  * PUBLIC: int __memp_fget
  27.  * PUBLIC:     __P((DB_MPOOLFILE *, db_pgno_t *, u_int32_t, void *));
  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. enum { FIRST_FOUND, FIRST_MISS, SECOND_FOUND, SECOND_MISS } state;
  37. BH *alloc_bhp, *bhp;
  38. DB_ENV *dbenv;
  39. DB_MPOOL *dbmp;
  40. DB_MPOOL_HASH *hp;
  41. MPOOL *c_mp, *mp;
  42. MPOOLFILE *mfp;
  43. roff_t mf_offset;
  44. u_int32_t n_cache, st_hsearch;
  45. int b_incr, extending, first, ret;
  46. *(void **)addrp = NULL;
  47. dbmp = dbmfp->dbmp;
  48. dbenv = dbmp->dbenv;
  49. PANIC_CHECK(dbenv);
  50. mp = dbmp->reginfo[0].primary;
  51. mfp = dbmfp->mfp;
  52. mf_offset = R_OFFSET(dbmp->reginfo, mfp);
  53. alloc_bhp = bhp = NULL;
  54. hp = NULL;
  55. b_incr = extending = ret = 0;
  56. /*
  57.  * Validate arguments.
  58.  *
  59.  * !!!
  60.  * Don't test for DB_MPOOL_CREATE and DB_MPOOL_NEW flags for readonly
  61.  * files here, and create non-existent pages in readonly files if the
  62.  * flags are set, later.  The reason is that the hash access method
  63.  * wants to get empty pages that don't really exist in readonly files.
  64.  * The only alternative is for hash to write the last "bucket" all the
  65.  * time, which we don't want to do because one of our big goals in life
  66.  * is to keep database files small.  It's sleazy as hell, but we catch
  67.  * any attempt to actually write the file in memp_fput().
  68.  */
  69. #define OKFLAGS (DB_MPOOL_CREATE | DB_MPOOL_LAST | DB_MPOOL_NEW)
  70. if (flags != 0) {
  71. if ((ret = __db_fchk(dbenv, "memp_fget", flags, OKFLAGS)) != 0)
  72. return (ret);
  73. switch (flags) {
  74. case DB_MPOOL_CREATE:
  75. break;
  76. case DB_MPOOL_LAST:
  77. /* Get the last page number in the file. */
  78. if (flags == DB_MPOOL_LAST) {
  79. R_LOCK(dbenv, dbmp->reginfo);
  80. *pgnoaddr = mfp->last_pgno;
  81. R_UNLOCK(dbenv, dbmp->reginfo);
  82. }
  83. break;
  84. case DB_MPOOL_NEW:
  85. /*
  86.  * If always creating a page, skip the first search
  87.  * of the hash bucket.
  88.  */
  89. if (flags == DB_MPOOL_NEW)
  90. goto alloc;
  91. break;
  92. default:
  93. return (__db_ferr(dbenv, "memp_fget", 1));
  94. }
  95. }
  96. /*
  97.  * If mmap'ing the file and the page is not past the end of the file,
  98.  * just return a pointer.
  99.  *
  100.  * The page may be past the end of the file, so check the page number
  101.  * argument against the original length of the file.  If we previously
  102.  * returned pages past the original end of the file, last_pgno will
  103.  * have been updated to match the "new" end of the file, and checking
  104.  * against it would return pointers past the end of the mmap'd region.
  105.  *
  106.  * If another process has opened the file for writing since we mmap'd
  107.  * it, we will start playing the game by their rules, i.e. everything
  108.  * goes through the cache.  All pages previously returned will be safe,
  109.  * as long as the correct locking protocol was observed.
  110.  *
  111.  * We don't discard the map because we don't know when all of the
  112.  * pages will have been discarded from the process' address space.
  113.  * It would be possible to do so by reference counting the open
  114.  * pages from the mmap, but it's unclear to me that it's worth it.
  115.  */
  116. if (dbmfp->addr != NULL &&
  117.     F_ISSET(mfp, MP_CAN_MMAP) && *pgnoaddr <= mfp->orig_last_pgno) {
  118. *(void **)addrp =
  119.     R_ADDR(dbmfp, *pgnoaddr * mfp->stat.st_pagesize);
  120. ++mfp->stat.st_map;
  121. return (0);
  122. }
  123. hb_search:
  124. /*
  125.  * Determine the cache and hash bucket where this page lives and get
  126.  * local pointers to them.  Reset on each pass through this code, the
  127.  * page number can change.
  128.  */
  129. n_cache = NCACHE(mp, mf_offset, *pgnoaddr);
  130. c_mp = dbmp->reginfo[n_cache].primary;
  131. hp = R_ADDR(&dbmp->reginfo[n_cache], c_mp->htab);
  132. hp = &hp[NBUCKET(c_mp, mf_offset, *pgnoaddr)];
  133. /* Search the hash chain for the page. */
  134. retry: st_hsearch = 0;
  135. MUTEX_LOCK(dbenv, &hp->hash_mutex);
  136. for (bhp = SH_TAILQ_FIRST(&hp->hash_bucket, __bh);
  137.     bhp != NULL; bhp = SH_TAILQ_NEXT(bhp, hq, __bh)) {
  138. ++st_hsearch;
  139. if (bhp->pgno != *pgnoaddr || bhp->mf_offset != mf_offset)
  140. continue;
  141. /*
  142.  * Increment the reference count.  We may discard the hash
  143.  * bucket lock as we evaluate and/or read the buffer, so we
  144.  * need to ensure it doesn't move and its contents remain
  145.  * unchanged.
  146.  */
  147. if (bhp->ref == UINT16_T_MAX) {
  148. __db_err(dbenv,
  149.     "%s: page %lu: reference count overflow",
  150.     __memp_fn(dbmfp), (u_long)bhp->pgno);
  151. ret = EINVAL;
  152. MUTEX_UNLOCK(dbenv, &hp->hash_mutex);
  153. goto err;
  154. }
  155. ++bhp->ref;
  156. b_incr = 1;
  157. /*
  158.  * BH_LOCKED --
  159.  * I/O is in progress or sync is waiting on the buffer to write
  160.  * it.  Because we've incremented the buffer reference count,
  161.  * we know the buffer can't move.  Unlock the bucket lock, wait
  162.  * for the buffer to become available, reacquire the bucket.
  163.  */
  164. for (first = 1; F_ISSET(bhp, BH_LOCKED) &&
  165.     !F_ISSET(dbenv, DB_ENV_NOLOCKING); first = 0) {
  166. /*
  167.  * If someone is trying to sync this buffer and the
  168.  * buffer is hot, they may never get in.  Give up
  169.  * and try again.
  170.  */
  171. if (!first && bhp->ref_sync != 0) {
  172. --bhp->ref;
  173. b_incr = 0;
  174. MUTEX_UNLOCK(dbenv, &hp->hash_mutex);
  175. __os_yield(dbenv, 1);
  176. goto retry;
  177. }
  178. MUTEX_UNLOCK(dbenv, &hp->hash_mutex);
  179. /*
  180.  * Explicitly yield the processor if not the first pass
  181.  * through this loop -- if we don't, we might run to the
  182.  * end of our CPU quantum as we will simply be swapping
  183.  * between the two locks.
  184.  */
  185. if (!first)
  186. __os_yield(dbenv, 1);
  187. MUTEX_LOCK(dbenv, &bhp->mutex);
  188. /* Wait for I/O to finish... */
  189. MUTEX_UNLOCK(dbenv, &bhp->mutex);
  190. MUTEX_LOCK(dbenv, &hp->hash_mutex);
  191. }
  192. ++mfp->stat.st_cache_hit;
  193. break;
  194. }
  195. /*
  196.  * Update the hash bucket search statistics -- do now because our next
  197.  * search may be for a different bucket.
  198.  */
  199. ++c_mp->stat.st_hash_searches;
  200. if (st_hsearch > c_mp->stat.st_hash_longest)
  201. c_mp->stat.st_hash_longest = st_hsearch;
  202. c_mp->stat.st_hash_examined += st_hsearch;
  203. /*
  204.  * There are 4 possible paths to this location:
  205.  *
  206.  * FIRST_MISS:
  207.  * Didn't find the page in the hash bucket on our first pass:
  208.  * bhp == NULL, alloc_bhp == NULL
  209.  *
  210.  * FIRST_FOUND:
  211.  * Found the page in the hash bucket on our first pass:
  212.  * bhp != NULL, alloc_bhp == NULL
  213.  *
  214.  * SECOND_FOUND:
  215.  * Didn't find the page in the hash bucket on the first pass,
  216.  * allocated space, and found the page in the hash bucket on
  217.  * our second pass:
  218.  * bhp != NULL, alloc_bhp != NULL
  219.  *
  220.  * SECOND_MISS:
  221.  * Didn't find the page in the hash bucket on the first pass,
  222.  * allocated space, and didn't find the page in the hash bucket
  223.  * on our second pass:
  224.  * bhp == NULL, alloc_bhp != NULL
  225.  */
  226. state = bhp == NULL ?
  227.     (alloc_bhp == NULL ? FIRST_MISS : SECOND_MISS) :
  228.     (alloc_bhp == NULL ? FIRST_FOUND : SECOND_FOUND);
  229. switch (state) {
  230. case FIRST_FOUND:
  231. /* We found the buffer in our first check -- we're done. */
  232. break;
  233. case FIRST_MISS:
  234. /*
  235.  * We didn't find the buffer in our first check.  Figure out
  236.  * if the page exists, and allocate structures so we can add
  237.  * the page to the buffer pool.
  238.  */
  239. MUTEX_UNLOCK(dbenv, &hp->hash_mutex);
  240. alloc: /*
  241.  * If DB_MPOOL_NEW is set, we have to allocate a page number.
  242.  * If neither DB_MPOOL_CREATE or DB_MPOOL_CREATE is set, then
  243.  * it's an error to try and get a page past the end of file.
  244.  */
  245. COMPQUIET(n_cache, 0);
  246. extending = ret = 0;
  247. R_LOCK(dbenv, dbmp->reginfo);
  248. switch (flags) {
  249. case DB_MPOOL_NEW:
  250. extending = 1;
  251. *pgnoaddr = mfp->last_pgno + 1;
  252. break;
  253. case DB_MPOOL_CREATE:
  254. extending = *pgnoaddr > mfp->last_pgno;
  255. break;
  256. default:
  257. ret = *pgnoaddr > mfp->last_pgno ? DB_PAGE_NOTFOUND : 0;
  258. break;
  259. }
  260. R_UNLOCK(dbenv, dbmp->reginfo);
  261. if (ret != 0)
  262. goto err;
  263. /*
  264.  * !!!
  265.  * In the DB_MPOOL_NEW code path, mf_offset and n_cache have
  266.  * not yet been initialized.
  267.  */
  268. mf_offset = R_OFFSET(dbmp->reginfo, mfp);
  269. n_cache = NCACHE(mp, mf_offset, *pgnoaddr);
  270. /* Allocate a new buffer header and data space. */
  271. if ((ret = __memp_alloc(dbmp,
  272.     &dbmp->reginfo[n_cache], mfp, 0, NULL, &alloc_bhp)) != 0)
  273. goto err;
  274. #ifdef DIAGNOSTIC
  275. if ((db_alignp_t)alloc_bhp->buf & (sizeof(size_t) - 1)) {
  276. __db_err(dbenv,
  277.     "Error: buffer data is NOT size_t aligned");
  278. ret = EINVAL;
  279. goto err;
  280. }
  281. #endif
  282. /*
  283.  * If we are extending the file, we'll need the region lock
  284.  * again.
  285.  */
  286. if (extending)
  287. R_LOCK(dbenv, dbmp->reginfo);
  288. /*
  289.  * DB_MPOOL_NEW does not guarantee you a page unreferenced by
  290.  * any other thread of control.  (That guarantee is interesting
  291.  * for DB_MPOOL_NEW, unlike DB_MPOOL_CREATE, because the caller
  292.  * did not specify the page number, and so, may reasonably not
  293.  * have any way to lock the page outside of mpool.) Regardless,
  294.  * if we allocate the page, and some other thread of control
  295.  * requests the page by number, we will not detect that and the
  296.  * thread of control that allocated using DB_MPOOL_NEW may not
  297.  * have a chance to initialize the page.  (Note: we *could*
  298.  * detect this case if we set a flag in the buffer header which
  299.  * guaranteed that no gets of the page would succeed until the
  300.  * reference count went to 0, that is, until the creating page
  301.  * put the page.)  What we do guarantee is that if two threads
  302.  * of control are both doing DB_MPOOL_NEW calls, they won't
  303.  * collide, that is, they won't both get the same page.
  304.  *
  305.  * There's a possibility that another thread allocated the page
  306.  * we were planning to allocate while we were off doing buffer
  307.  * allocation.  We can do that by making sure the page number
  308.  * we were going to use is still available.  If it's not, then
  309.  * we check to see if the next available page number hashes to
  310.  * the same mpool region as the old one -- if it does, we can
  311.  * continue, otherwise, we have to start over.
  312.  */
  313. if (flags == DB_MPOOL_NEW && *pgnoaddr != mfp->last_pgno + 1) {
  314. *pgnoaddr = mfp->last_pgno + 1;
  315. if (n_cache != NCACHE(mp, mf_offset, *pgnoaddr)) {
  316. __db_shalloc_free(
  317.     dbmp->reginfo[n_cache].addr, alloc_bhp);
  318. /*
  319.  * flags == DB_MPOOL_NEW, so extending is set
  320.  * and we're holding the region locked.
  321.  */
  322. R_UNLOCK(dbenv, dbmp->reginfo);
  323. alloc_bhp = NULL;
  324. goto alloc;
  325. }
  326. }
  327. /*
  328.  * We released the region lock, so another thread might have
  329.  * extended the file.  Update the last_pgno and initialize
  330.  * the file, as necessary, if we extended the file.
  331.  */
  332. if (extending) {
  333. #ifdef HAVE_FILESYSTEM_NOTZERO
  334. if (*pgnoaddr > mfp->last_pgno &&
  335.     __os_fs_notzero() &&
  336.     F_ISSET(dbmfp->fhp, DB_FH_VALID))
  337. ret = __memp_fs_notzero(
  338.     dbenv, dbmfp, mfp, pgnoaddr);
  339. else
  340. ret = 0;
  341. #endif
  342. if (ret == 0 && *pgnoaddr > mfp->last_pgno)
  343. mfp->last_pgno = *pgnoaddr;
  344. R_UNLOCK(dbenv, dbmp->reginfo);
  345. if (ret != 0)
  346. goto err;
  347. }
  348. goto hb_search;
  349. case SECOND_FOUND:
  350. /*
  351.  * We allocated buffer space for the requested page, but then
  352.  * found the page in the buffer cache on our second check.
  353.  * That's OK -- we can use the page we found in the pool,
  354.  * unless DB_MPOOL_NEW is set.
  355.  *
  356.  * Free the allocated memory, we no longer need it.  Since we
  357.  * can't acquire the region lock while holding the hash bucket
  358.  * lock, we have to release the hash bucket and re-acquire it.
  359.  * That's OK, because we have the buffer pinned down.
  360.  */
  361. MUTEX_UNLOCK(dbenv, &hp->hash_mutex);
  362. R_LOCK(dbenv, &dbmp->reginfo[n_cache]);
  363. __db_shalloc_free(dbmp->reginfo[n_cache].addr, alloc_bhp);
  364. alloc_bhp = NULL;
  365. R_UNLOCK(dbenv, &dbmp->reginfo[n_cache]);
  366. MUTEX_LOCK(dbenv, &hp->hash_mutex);
  367. /*
  368.  * We can't use the page we found in the pool if DB_MPOOL_NEW
  369.  * was set.  (For details, see the above comment beginning
  370.  * "DB_MPOOL_NEW does not guarantee you a page unreferenced by
  371.  * any other thread of control".)  If DB_MPOOL_NEW is set, we
  372.  * release our pin on this particular buffer, and try to get
  373.  * another one.
  374.  */
  375. if (flags == DB_MPOOL_NEW) {
  376. --bhp->ref;
  377. b_incr = 0;
  378. goto alloc;
  379. }
  380. break;
  381. case SECOND_MISS:
  382. /*
  383.  * We allocated buffer space for the requested page, and found
  384.  * the page still missing on our second pass through the buffer
  385.  * cache.  Instantiate the page.
  386.  */
  387. bhp = alloc_bhp;
  388. alloc_bhp = NULL;
  389. /*
  390.  * Initialize all the BH and hash bucket fields so we can call
  391.  * __memp_bhfree if an error occurs.
  392.  *
  393.  * Append the buffer to the tail of the bucket list and update
  394.  * the hash bucket's priority.
  395.  */
  396. b_incr = 1;
  397. memset(bhp, 0, sizeof(BH));
  398. bhp->ref = 1;
  399. bhp->priority = UINT32_T_MAX;
  400. bhp->pgno = *pgnoaddr;
  401. bhp->mf_offset = mf_offset;
  402. SH_TAILQ_INSERT_TAIL(&hp->hash_bucket, bhp, hq);
  403. hp->hash_priority =
  404.     SH_TAILQ_FIRST(&hp->hash_bucket, __bh)->priority;
  405. /* If we extended the file, make sure the page is never lost. */
  406. if (extending) {
  407. ++hp->hash_page_dirty;
  408. F_SET(bhp, BH_DIRTY | BH_DIRTY_CREATE);
  409. }
  410. /*
  411.  * If we created the page, zero it out.  If we didn't create
  412.  * the page, read from the backing file.
  413.  *
  414.  * !!!
  415.  * DB_MPOOL_NEW doesn't call the pgin function.
  416.  *
  417.  * If DB_MPOOL_CREATE is used, then the application's pgin
  418.  * function has to be able to handle pages of 0's -- if it
  419.  * uses DB_MPOOL_NEW, it can detect all of its page creates,
  420.  * and not bother.
  421.  *
  422.  * If we're running in diagnostic mode, smash any bytes on the
  423.  * page that are unknown quantities for the caller.
  424.  *
  425.  * Otherwise, read the page into memory, optionally creating it
  426.  * if DB_MPOOL_CREATE is set.
  427.  */
  428. if (extending) {
  429. if (mfp->clear_len == 0)
  430. memset(bhp->buf, 0, mfp->stat.st_pagesize);
  431. else {
  432. memset(bhp->buf, 0, mfp->clear_len);
  433. #if defined(DIAGNOSTIC) || defined(UMRW)
  434. memset(bhp->buf + mfp->clear_len, CLEAR_BYTE,
  435.     mfp->stat.st_pagesize - mfp->clear_len);
  436. #endif
  437. }
  438. if (flags == DB_MPOOL_CREATE && mfp->ftype != 0)
  439. F_SET(bhp, BH_CALLPGIN);
  440. ++mfp->stat.st_page_create;
  441. } else {
  442. F_SET(bhp, BH_TRASH);
  443. ++mfp->stat.st_cache_miss;
  444. }
  445. /* Increment buffer count referenced by MPOOLFILE. */
  446. MUTEX_LOCK(dbenv, &mfp->mutex);
  447. ++mfp->block_cnt;
  448. MUTEX_UNLOCK(dbenv, &mfp->mutex);
  449. /*
  450.  * Initialize the mutex.  This is the last initialization step,
  451.  * because it's the only one that can fail, and everything else
  452.  * must be set up or we can't jump to the err label because it
  453.  * will call __memp_bhfree.
  454.  */
  455. if ((ret = __db_mutex_setup(dbenv,
  456.     &dbmp->reginfo[n_cache], &bhp->mutex, 0)) != 0)
  457. goto err;
  458. }
  459. DB_ASSERT(bhp->ref != 0);
  460. /*
  461.  * If we're the only reference, update buffer and bucket priorities.
  462.  * We may be about to release the hash bucket lock, and everything
  463.  * should be correct, first.  (We've already done this if we created
  464.  * the buffer, so there is no need to do it again.)
  465.  */
  466. if (state != SECOND_MISS && bhp->ref == 1) {
  467. bhp->priority = UINT32_T_MAX;
  468. SH_TAILQ_REMOVE(&hp->hash_bucket, bhp, hq, __bh);
  469. SH_TAILQ_INSERT_TAIL(&hp->hash_bucket, bhp, hq);
  470. hp->hash_priority =
  471.     SH_TAILQ_FIRST(&hp->hash_bucket, __bh)->priority;
  472. }
  473. /*
  474.  * BH_TRASH --
  475.  * The buffer we found may need to be filled from the disk.
  476.  *
  477.  * It's possible for the read function to fail, which means we fail as
  478.  * well.  Note, the __memp_pgread() function discards and reacquires
  479.  * the hash lock, so the buffer must be pinned down so that it cannot
  480.  * move and its contents are unchanged.  Discard the buffer on failure
  481.  * unless another thread is waiting on our I/O to complete.  It's OK to
  482.  * leave the buffer around, as the waiting thread will see the BH_TRASH
  483.  * flag set, and will also attempt to discard it.  If there's a waiter,
  484.  * we need to decrement our reference count.
  485.  */
  486. if (F_ISSET(bhp, BH_TRASH) &&
  487.     (ret = __memp_pgread(dbmfp,
  488.     &hp->hash_mutex, bhp, LF_ISSET(DB_MPOOL_CREATE) ? 1 : 0)) != 0)
  489. goto err;
  490. /*
  491.  * BH_CALLPGIN --
  492.  * The buffer was processed for being written to disk, and now has
  493.  * to be re-converted for use.
  494.  */
  495. if (F_ISSET(bhp, BH_CALLPGIN)) {
  496. if ((ret = __memp_pg(dbmfp, bhp, 1)) != 0)
  497. goto err;
  498. F_CLR(bhp, BH_CALLPGIN);
  499. }
  500. MUTEX_UNLOCK(dbenv, &hp->hash_mutex);
  501. #ifdef DIAGNOSTIC
  502. /* Update the file's pinned reference count. */
  503. R_LOCK(dbenv, dbmp->reginfo);
  504. ++dbmfp->pinref;
  505. R_UNLOCK(dbenv, dbmp->reginfo);
  506. /*
  507.  * We want to switch threads as often as possible, and at awkward
  508.  * times.  Yield every time we get a new page to ensure contention.
  509.  */
  510. if (F_ISSET(dbenv, DB_ENV_YIELDCPU))
  511. __os_yield(dbenv, 1);
  512. #endif
  513. *(void **)addrp = bhp->buf;
  514. return (0);
  515. err: /*
  516.  * Discard our reference.  If we're the only reference, discard the
  517.  * the buffer entirely.  If we held a reference to a buffer, we are
  518.  * also still holding the hash bucket mutex.
  519.  */
  520. if (b_incr) {
  521. if (bhp->ref == 1)
  522. (void)__memp_bhfree(dbmp, hp, bhp, 1);
  523. else {
  524. --bhp->ref;
  525. MUTEX_UNLOCK(dbenv, &hp->hash_mutex);
  526. }
  527. }
  528. /* If alloc_bhp is set, free the memory. */
  529. if (alloc_bhp != NULL)
  530. __db_shalloc_free(dbmp->reginfo[n_cache].addr, alloc_bhp);
  531. return (ret);
  532. }
  533. #ifdef HAVE_FILESYSTEM_NOTZERO
  534. /*
  535.  * __memp_fs_notzero --
  536.  * Initialize the underlying allocated pages in the file.
  537.  */
  538. static int
  539. __memp_fs_notzero(dbenv, dbmfp, mfp, pgnoaddr)
  540. DB_ENV *dbenv;
  541. DB_MPOOLFILE *dbmfp;
  542. MPOOLFILE *mfp;
  543. db_pgno_t *pgnoaddr;
  544. {
  545. DB_IO db_io;
  546. u_int32_t i, npages;
  547. size_t nw;
  548. int ret;
  549. u_int8_t *page;
  550. char *fail;
  551. /*
  552.  * Pages allocated by writing pages past end-of-file are not zeroed,
  553.  * on some systems.  Recovery could theoretically be fooled by a page
  554.  * showing up that contained garbage.  In order to avoid this, we
  555.  * have to write the pages out to disk, and flush them.  The reason
  556.  * for the flush is because if we don't sync, the allocation of another
  557.  * page subsequent to this one might reach the disk first, and if we
  558.  * crashed at the right moment, leave us with this page as the one
  559.  * allocated by writing a page past it in the file.
  560.  *
  561.  * Hash is the only access method that allocates groups of pages.  We
  562.  * know that it will use the existence of the last page in a group to
  563.  * signify that the entire group is OK; so, write all the pages but
  564.  * the last one in the group, flush them to disk, and then write the
  565.  * last one to disk and flush it.
  566.  */
  567. if ((ret = __os_calloc(dbenv, 1, mfp->stat.st_pagesize, &page)) != 0)
  568. return (ret);
  569. db_io.fhp = dbmfp->fhp;
  570. db_io.mutexp = dbmfp->mutexp;
  571. db_io.pagesize = db_io.bytes = mfp->stat.st_pagesize;
  572. db_io.buf = page;
  573. npages = *pgnoaddr - mfp->last_pgno;
  574. for (i = 1; i < npages; ++i) {
  575. db_io.pgno = mfp->last_pgno + i;
  576. if ((ret = __os_io(dbenv, &db_io, DB_IO_WRITE, &nw)) != 0) {
  577. fail = "write";
  578. goto err;
  579. }
  580. }
  581. if (i != 1 && (ret = __os_fsync(dbenv, dbmfp->fhp)) != 0) {
  582. fail = "sync";
  583. goto err;
  584. }
  585. db_io.pgno = mfp->last_pgno + npages;
  586. if ((ret = __os_io(dbenv, &db_io, DB_IO_WRITE, &nw)) != 0) {
  587. fail = "write";
  588. goto err;
  589. }
  590. if ((ret = __os_fsync(dbenv, dbmfp->fhp)) != 0) {
  591. fail = "sync";
  592. err: __db_err(dbenv, "%s: %s failed for page %lu",
  593.     __memp_fn(dbmfp), fail, (u_long)db_io.pgno);
  594. }
  595. __os_free(dbenv, page);
  596. return (ret);
  597. }
  598. #endif