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

MySQL数据库

开发平台:

Visual C++

  1. /******************************************************
  2. The database buffer buf_pool flush algorithm
  3. (c) 1995-2001 Innobase Oy
  4. Created 11/11/1995 Heikki Tuuri
  5. *******************************************************/
  6. #include "buf0flu.h"
  7. #ifdef UNIV_NONINL
  8. #include "buf0flu.ic"
  9. #include "trx0sys.h"
  10. #endif
  11. #include "ut0byte.h"
  12. #include "ut0lst.h"
  13. #include "page0page.h"
  14. #include "fil0fil.h"
  15. #include "buf0buf.h"
  16. #include "buf0lru.h"
  17. #include "buf0rea.h"
  18. #include "ibuf0ibuf.h"
  19. #include "log0log.h"
  20. #include "os0file.h"
  21. #include "trx0sys.h"
  22. #include "srv0srv.h"
  23. /* When flushed, dirty blocks are searched in neigborhoods of this size, and
  24. flushed along with the original page. */
  25. #define BUF_FLUSH_AREA ut_min(BUF_READ_AHEAD_AREA,
  26.        buf_pool->curr_size / 16)
  27. /**********************************************************************
  28. Validates the flush list. */
  29. static
  30. ibool
  31. buf_flush_validate_low(void);
  32. /*========================*/
  33. /* out: TRUE if ok */
  34. /************************************************************************
  35. Inserts a modified block into the flush list. */
  36. void
  37. buf_flush_insert_into_flush_list(
  38. /*=============================*/
  39. buf_block_t* block) /* in: block which is modified */
  40. {
  41. #ifdef UNIV_SYNC_DEBUG
  42. ut_ad(mutex_own(&(buf_pool->mutex)));
  43. #endif /* UNIV_SYNC_DEBUG */
  44. ut_a(block->state == BUF_BLOCK_FILE_PAGE);
  45. ut_ad((UT_LIST_GET_FIRST(buf_pool->flush_list) == NULL)
  46.       || (ut_dulint_cmp(
  47. (UT_LIST_GET_FIRST(buf_pool->flush_list))
  48. ->oldest_modification,
  49. block->oldest_modification) <= 0));
  50. UT_LIST_ADD_FIRST(flush_list, buf_pool->flush_list, block);
  51. ut_ad(buf_flush_validate_low());
  52. }
  53. /************************************************************************
  54. Inserts a modified block into the flush list in the right sorted position.
  55. This function is used by recovery, because there the modifications do not
  56. necessarily come in the order of lsn's. */
  57. void
  58. buf_flush_insert_sorted_into_flush_list(
  59. /*====================================*/
  60. buf_block_t* block) /* in: block which is modified */
  61. {
  62. buf_block_t* prev_b;
  63. buf_block_t* b;
  64. #ifdef UNIV_SYNC_DEBUG
  65. ut_ad(mutex_own(&(buf_pool->mutex)));
  66. #endif /* UNIV_SYNC_DEBUG */
  67. prev_b = NULL;
  68. b = UT_LIST_GET_FIRST(buf_pool->flush_list);
  69. while (b && (ut_dulint_cmp(b->oldest_modification,
  70. block->oldest_modification) > 0)) {
  71. prev_b = b;
  72. b = UT_LIST_GET_NEXT(flush_list, b);
  73. }
  74. if (prev_b == NULL) {
  75. UT_LIST_ADD_FIRST(flush_list, buf_pool->flush_list, block);
  76. } else {
  77. UT_LIST_INSERT_AFTER(flush_list, buf_pool->flush_list, prev_b,
  78. block);
  79. }
  80. ut_ad(buf_flush_validate_low());
  81. }
  82. /************************************************************************
  83. Returns TRUE if the file page block is immediately suitable for replacement,
  84. i.e., the transition FILE_PAGE => NOT_USED allowed. */
  85. ibool
  86. buf_flush_ready_for_replace(
  87. /*========================*/
  88. /* out: TRUE if can replace immediately */
  89. buf_block_t* block) /* in: buffer control block, must be in state
  90. BUF_BLOCK_FILE_PAGE and in the LRU list */
  91. {
  92. #ifdef UNIV_SYNC_DEBUG
  93. ut_ad(mutex_own(&(buf_pool->mutex)));
  94. #endif /* UNIV_SYNC_DEBUG */
  95. if (block->state != BUF_BLOCK_FILE_PAGE) {
  96. ut_print_timestamp(stderr);
  97. fprintf(stderr,
  98. "  InnoDB: Error: buffer block state %lu in the LRU list!n",
  99. (ulong)block->state);
  100. ut_print_buf(stderr, (byte*)block, sizeof(buf_block_t));
  101. return(FALSE);
  102. }
  103. if ((ut_dulint_cmp(block->oldest_modification, ut_dulint_zero) > 0)
  104.     || (block->buf_fix_count != 0)
  105.     || (block->io_fix != 0)) {
  106. return(FALSE);
  107. }
  108. return(TRUE);
  109. }
  110. /************************************************************************
  111. Returns TRUE if the block is modified and ready for flushing. */
  112. UNIV_INLINE
  113. ibool
  114. buf_flush_ready_for_flush(
  115. /*======================*/
  116. /* out: TRUE if can flush immediately */
  117. buf_block_t* block, /* in: buffer control block, must be in state
  118. BUF_BLOCK_FILE_PAGE */
  119. ulint flush_type)/* in: BUF_FLUSH_LRU or BUF_FLUSH_LIST */
  120. {
  121. #ifdef UNIV_SYNC_DEBUG
  122. ut_ad(mutex_own(&(buf_pool->mutex)));
  123. #endif /* UNIV_SYNC_DEBUG */
  124. ut_a(block->state == BUF_BLOCK_FILE_PAGE);
  125. if ((ut_dulint_cmp(block->oldest_modification, ut_dulint_zero) > 0)
  126.      && (block->io_fix == 0)) {
  127.      if (flush_type != BUF_FLUSH_LRU) {
  128. return(TRUE);
  129. } else if (block->buf_fix_count == 0) {
  130.  
  131. /* If we are flushing the LRU list, to avoid deadlocks
  132. we require the block not to be bufferfixed, and hence
  133. not latched. */
  134. return(TRUE);
  135. }
  136. }
  137. return(FALSE);
  138. }
  139. /************************************************************************
  140. Updates the flush system data structures when a write is completed. */
  141. void
  142. buf_flush_write_complete(
  143. /*=====================*/
  144. buf_block_t* block) /* in: pointer to the block in question */
  145. {
  146. ut_ad(block);
  147. #ifdef UNIV_SYNC_DEBUG
  148. ut_ad(mutex_own(&(buf_pool->mutex)));
  149. #endif /* UNIV_SYNC_DEBUG */
  150. ut_a(block->state == BUF_BLOCK_FILE_PAGE);
  151. block->oldest_modification = ut_dulint_zero;
  152. UT_LIST_REMOVE(flush_list, buf_pool->flush_list, block);
  153. ut_d(UT_LIST_VALIDATE(flush_list, buf_block_t, buf_pool->flush_list));
  154. (buf_pool->n_flush[block->flush_type])--;
  155. if (block->flush_type == BUF_FLUSH_LRU) {
  156. /* Put the block to the end of the LRU list to wait to be
  157. moved to the free list */
  158. buf_LRU_make_block_old(block);
  159. buf_pool->LRU_flush_ended++;
  160. }
  161. /* fprintf(stderr, "n pending flush %lun",
  162. buf_pool->n_flush[block->flush_type]); */
  163. if ((buf_pool->n_flush[block->flush_type] == 0)
  164.     && (buf_pool->init_flush[block->flush_type] == FALSE)) {
  165. /* The running flush batch has ended */
  166. os_event_set(buf_pool->no_flush[block->flush_type]);
  167. }
  168. }
  169. /************************************************************************
  170. Flushes possible buffered writes from the doublewrite memory buffer to disk,
  171. and also wakes up the aio thread if simulated aio is used. It is very
  172. important to call this function after a batch of writes has been posted,
  173. and also when we may have to wait for a page latch! Otherwise a deadlock
  174. of threads can occur. */
  175. static
  176. void
  177. buf_flush_buffered_writes(void)
  178. /*===========================*/
  179. {
  180. buf_block_t* block;
  181. byte* write_buf;
  182. ulint len;
  183. ulint len2;
  184. ulint i;
  185. if (trx_doublewrite == NULL) {
  186. os_aio_simulated_wake_handler_threads();
  187. return;
  188. }
  189. mutex_enter(&(trx_doublewrite->mutex));
  190. /* Write first to doublewrite buffer blocks. We use synchronous
  191. aio and thus know that file write has been completed when the
  192. control returns. */
  193. if (trx_doublewrite->first_free == 0) {
  194. mutex_exit(&(trx_doublewrite->mutex));
  195. return;
  196. }
  197. for (i = 0; i < trx_doublewrite->first_free; i++) {
  198. block = trx_doublewrite->buf_block_arr[i];
  199.         ut_a(block->state == BUF_BLOCK_FILE_PAGE);
  200. if (mach_read_from_4(block->frame + FIL_PAGE_LSN + 4)
  201.                             != mach_read_from_4(block->frame + UNIV_PAGE_SIZE
  202.                                         - FIL_PAGE_END_LSN_OLD_CHKSUM + 4)) {
  203.                             ut_print_timestamp(stderr);
  204.                             fprintf(stderr,
  205. "  InnoDB: ERROR: The page to be written seems corrupt!n"
  206. "InnoDB: The lsn fields do not match! Noticed in the buffer pooln"
  207. "InnoDB: before posting to the doublewrite buffer.n");
  208.                 }
  209. if (block->check_index_page_at_flush
  210. && !page_simple_validate(block->frame)) {
  211. buf_page_print(block->frame);
  212. ut_print_timestamp(stderr);
  213. fprintf(stderr,
  214. "  InnoDB: Apparent corruption of an index page n:o %lu in space %lun"
  215. "InnoDB: to be written to data file. We intentionally crash servern"
  216. "InnoDB: to prevent corrupt data from ending up in datan"
  217. "InnoDB: files.n",
  218. (ulong) block->offset, (ulong) block->space);
  219. ut_error;
  220. }
  221. }
  222. if (trx_doublewrite->first_free > TRX_SYS_DOUBLEWRITE_BLOCK_SIZE) {
  223. len = TRX_SYS_DOUBLEWRITE_BLOCK_SIZE * UNIV_PAGE_SIZE;
  224. } else {
  225. len = trx_doublewrite->first_free * UNIV_PAGE_SIZE;
  226. }
  227. fil_io(OS_FILE_WRITE,
  228. TRUE, TRX_SYS_SPACE,
  229. trx_doublewrite->block1, 0, len,
  230.   (void*)trx_doublewrite->write_buf, NULL);
  231. write_buf = trx_doublewrite->write_buf;
  232.         for (len2 = 0; len2 + UNIV_PAGE_SIZE <= len; len2 += UNIV_PAGE_SIZE) {
  233.          if (mach_read_from_4(write_buf + len2 + FIL_PAGE_LSN + 4)
  234.                     != mach_read_from_4(write_buf + len2 + UNIV_PAGE_SIZE
  235.                                         - FIL_PAGE_END_LSN_OLD_CHKSUM + 4)) {
  236. ut_print_timestamp(stderr);
  237. fprintf(stderr,
  238. "  InnoDB: ERROR: The page to be written seems corrupt!n"
  239. "InnoDB: The lsn fields do not match! Noticed in the doublewrite block1.n");
  240. }
  241. }
  242. if (trx_doublewrite->first_free > TRX_SYS_DOUBLEWRITE_BLOCK_SIZE) {
  243. len = (trx_doublewrite->first_free
  244. - TRX_SYS_DOUBLEWRITE_BLOCK_SIZE) * UNIV_PAGE_SIZE;
  245. fil_io(OS_FILE_WRITE,
  246. TRUE, TRX_SYS_SPACE,
  247. trx_doublewrite->block2, 0, len,
  248.   (void*)(trx_doublewrite->write_buf
  249.   + TRX_SYS_DOUBLEWRITE_BLOCK_SIZE * UNIV_PAGE_SIZE),
  250. NULL);
  251. write_buf = trx_doublewrite->write_buf
  252.    + TRX_SYS_DOUBLEWRITE_BLOCK_SIZE * UNIV_PAGE_SIZE;
  253. for (len2 = 0; len2 + UNIV_PAGE_SIZE <= len;
  254. len2 += UNIV_PAGE_SIZE) {
  255.          if (mach_read_from_4(write_buf + len2
  256. + FIL_PAGE_LSN + 4)
  257.                          != mach_read_from_4(write_buf + len2
  258. + UNIV_PAGE_SIZE
  259.                                         - FIL_PAGE_END_LSN_OLD_CHKSUM + 4)) {
  260. ut_print_timestamp(stderr);
  261. fprintf(stderr,
  262. "  InnoDB: ERROR: The page to be written seems corrupt!n"
  263. "InnoDB: The lsn fields do not match! Noticed in the doublewrite block2.n");
  264. }
  265. }
  266. }
  267. /* Now flush the doublewrite buffer data to disk */
  268. fil_flush(TRX_SYS_SPACE);
  269. /* We know that the writes have been flushed to disk now
  270. and in recovery we will find them in the doublewrite buffer
  271. blocks. Next do the writes to the intended positions. */
  272. for (i = 0; i < trx_doublewrite->first_free; i++) {
  273. block = trx_doublewrite->buf_block_arr[i];
  274. if (mach_read_from_4(block->frame + FIL_PAGE_LSN + 4)
  275.                             != mach_read_from_4(block->frame + UNIV_PAGE_SIZE
  276.                                         - FIL_PAGE_END_LSN_OLD_CHKSUM + 4)) {
  277.                             ut_print_timestamp(stderr);
  278.                             fprintf(stderr,
  279. "  InnoDB: ERROR: The page to be written seems corrupt!n"
  280. "InnoDB: The lsn fields do not match! Noticed in the buffer pooln"
  281. "InnoDB: after posting and flushing the doublewrite buffer.n"
  282. "InnoDB: Page buf fix count %lu, io fix %lu, state %lun",
  283. (ulong)block->buf_fix_count, (ulong)block->io_fix,
  284. (ulong)block->state);
  285.                 }
  286. ut_a(block->state == BUF_BLOCK_FILE_PAGE);
  287. fil_io(OS_FILE_WRITE | OS_AIO_SIMULATED_WAKE_LATER,
  288. FALSE, block->space, block->offset, 0, UNIV_PAGE_SIZE,
  289.   (void*)block->frame, (void*)block);
  290. }
  291. /* Wake possible simulated aio thread to actually post the
  292. writes to the operating system */
  293. os_aio_simulated_wake_handler_threads();
  294. /* Wait that all async writes to tablespaces have been posted to
  295. the OS */
  296. os_aio_wait_until_no_pending_writes();
  297. /* Now we flush the data to disk (for example, with fsync) */
  298. fil_flush_file_spaces(FIL_TABLESPACE);
  299. /* We can now reuse the doublewrite memory buffer: */
  300. trx_doublewrite->first_free = 0;
  301. mutex_exit(&(trx_doublewrite->mutex));
  302. }
  303. /************************************************************************
  304. Posts a buffer page for writing. If the doublewrite memory buffer is
  305. full, calls buf_flush_buffered_writes and waits for for free space to
  306. appear. */
  307. static
  308. void
  309. buf_flush_post_to_doublewrite_buf(
  310. /*==============================*/
  311. buf_block_t* block) /* in: buffer block to write */
  312. {
  313. try_again:
  314. mutex_enter(&(trx_doublewrite->mutex));
  315. ut_a(block->state == BUF_BLOCK_FILE_PAGE);
  316. if (trx_doublewrite->first_free
  317. >= 2 * TRX_SYS_DOUBLEWRITE_BLOCK_SIZE) {
  318. mutex_exit(&(trx_doublewrite->mutex));
  319. buf_flush_buffered_writes();
  320. goto try_again;
  321. }
  322. ut_memcpy(trx_doublewrite->write_buf
  323. + UNIV_PAGE_SIZE * trx_doublewrite->first_free,
  324. block->frame, UNIV_PAGE_SIZE);
  325. trx_doublewrite->buf_block_arr[trx_doublewrite->first_free] = block;
  326. trx_doublewrite->first_free++;
  327. if (trx_doublewrite->first_free
  328. >= 2 * TRX_SYS_DOUBLEWRITE_BLOCK_SIZE) {
  329. mutex_exit(&(trx_doublewrite->mutex));
  330. buf_flush_buffered_writes();
  331. return;
  332. }
  333. mutex_exit(&(trx_doublewrite->mutex));
  334. }
  335. /************************************************************************
  336. Initializes a page for writing to the tablespace. */
  337. void
  338. buf_flush_init_for_writing(
  339. /*=======================*/
  340. byte* page, /* in: page */
  341. dulint newest_lsn, /* in: newest modification lsn to the page */
  342. ulint space, /* in: space id */
  343. ulint page_no) /* in: page number */
  344. {
  345. /* Write the newest modification lsn to the page header and trailer */
  346. mach_write_to_8(page + FIL_PAGE_LSN, newest_lsn);
  347. mach_write_to_8(page + UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM,
  348. newest_lsn);
  349. /* Write the page number and the space id */
  350. mach_write_to_4(page + FIL_PAGE_OFFSET, page_no);
  351.         mach_write_to_4(page + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID, space);
  352. /* Store the new formula checksum */
  353. mach_write_to_4(page + FIL_PAGE_SPACE_OR_CHKSUM,
  354. buf_calc_page_new_checksum(page));
  355. /* We overwrite the first 4 bytes of the end lsn field to store
  356. the old formula checksum. Since it depends also on the field
  357. FIL_PAGE_SPACE_OR_CHKSUM, it has to be calculated after storing the
  358. new formula checksum. */
  359. mach_write_to_4(page + UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM,
  360. buf_calc_page_old_checksum(page));
  361. }
  362. /************************************************************************
  363. Does an asynchronous write of a buffer page. NOTE: in simulated aio and
  364. also when the doublewrite buffer is used, we must call
  365. buf_flush_buffered_writes after we have posted a batch of writes! */
  366. static
  367. void
  368. buf_flush_write_block_low(
  369. /*======================*/
  370. buf_block_t* block) /* in: buffer block to write */
  371. {
  372. #ifdef UNIV_LOG_DEBUG
  373. static ibool univ_log_debug_warned;
  374. #endif /* UNIV_LOG_DEBUG */
  375. ut_a(block->state == BUF_BLOCK_FILE_PAGE);
  376. #ifdef UNIV_IBUF_DEBUG
  377. ut_a(ibuf_count_get(block->space, block->offset) == 0);
  378. #endif
  379. ut_ad(!ut_dulint_is_zero(block->newest_modification));
  380. #ifdef UNIV_LOG_DEBUG
  381. if (!univ_log_debug_warned) {
  382. univ_log_debug_warned = TRUE;
  383. fputs(
  384. "Warning: cannot force log to disk if UNIV_LOG_DEBUG is defined!n"
  385. "Crash recovery will not work!n",
  386. stderr);
  387. }
  388. #else
  389. /* Force the log to the disk before writing the modified block */
  390. log_write_up_to(block->newest_modification, LOG_WAIT_ALL_GROUPS, TRUE);
  391. #endif
  392. buf_flush_init_for_writing(block->frame, block->newest_modification,
  393. block->space, block->offset);
  394. if (!trx_doublewrite) {
  395. fil_io(OS_FILE_WRITE | OS_AIO_SIMULATED_WAKE_LATER,
  396. FALSE, block->space, block->offset, 0, UNIV_PAGE_SIZE,
  397.   (void*)block->frame, (void*)block);
  398. } else {
  399. buf_flush_post_to_doublewrite_buf(block);
  400. }
  401. }
  402. /************************************************************************
  403. Writes a page asynchronously from the buffer buf_pool to a file, if it can be
  404. found in the buf_pool and it is in a flushable state. NOTE: in simulated aio
  405. we must call os_aio_simulated_wake_handler_threads after we have posted a batch
  406. of writes! */
  407. static
  408. ulint
  409. buf_flush_try_page(
  410. /*===============*/
  411. /* out: 1 if a page was flushed, 0 otherwise */
  412. ulint space, /* in: space id */
  413. ulint offset, /* in: page offset */
  414. ulint flush_type) /* in: BUF_FLUSH_LRU, BUF_FLUSH_LIST, or
  415. BUF_FLUSH_SINGLE_PAGE */
  416. {
  417. buf_block_t* block;
  418. ibool locked;
  419. ut_ad(flush_type == BUF_FLUSH_LRU || flush_type == BUF_FLUSH_LIST
  420. || flush_type == BUF_FLUSH_SINGLE_PAGE);
  421. mutex_enter(&(buf_pool->mutex));
  422. block = buf_page_hash_get(space, offset);
  423. ut_a(!block || block->state == BUF_BLOCK_FILE_PAGE);
  424. if (flush_type == BUF_FLUSH_LIST
  425.     && block && buf_flush_ready_for_flush(block, flush_type)) {
  426. block->io_fix = BUF_IO_WRITE;
  427. /* If AWE is enabled and the page is not mapped to a frame,
  428. then map it */
  429. if (block->frame == NULL) {
  430. ut_a(srv_use_awe);
  431. /* We set second parameter TRUE because the block is
  432. in the LRU list and we must put it to
  433. awe_LRU_free_mapped list once mapped to a frame */
  434. buf_awe_map_page_to_frame(block, TRUE);
  435. }
  436. block->flush_type = flush_type;
  437. if (buf_pool->n_flush[flush_type] == 0) {
  438. os_event_reset(buf_pool->no_flush[flush_type]);
  439. }
  440. (buf_pool->n_flush[flush_type])++;
  441. locked = FALSE;
  442. /* If the simulated aio thread is not running, we must
  443. not wait for any latch, as we may end up in a deadlock:
  444. if buf_fix_count == 0, then we know we need not wait */
  445. if (block->buf_fix_count == 0) {
  446. rw_lock_s_lock_gen(&(block->lock), BUF_IO_WRITE);
  447. locked = TRUE;
  448. }
  449. mutex_exit(&(buf_pool->mutex));
  450. if (!locked) {
  451. buf_flush_buffered_writes();
  452. rw_lock_s_lock_gen(&(block->lock), BUF_IO_WRITE);
  453. }
  454. if (buf_debug_prints) {
  455. fprintf(stderr,
  456. "Flushing page space %lu, page no %lu n",
  457. (ulong) block->space, (ulong) block->offset);
  458. }
  459. buf_flush_write_block_low(block);
  460. return(1);
  461. } else if (flush_type == BUF_FLUSH_LRU && block
  462. && buf_flush_ready_for_flush(block, flush_type)) {
  463. /* VERY IMPORTANT:
  464. Because any thread may call the LRU flush, even when owning
  465. locks on pages, to avoid deadlocks, we must make sure that the
  466. s-lock is acquired on the page without waiting: this is
  467. accomplished because in the if-condition above we require
  468. the page not to be bufferfixed (in function
  469. ..._ready_for_flush). */
  470. block->io_fix = BUF_IO_WRITE;
  471. /* If AWE is enabled and the page is not mapped to a frame,
  472. then map it */
  473. if (block->frame == NULL) {
  474. ut_a(srv_use_awe);
  475. /* We set second parameter TRUE because the block is
  476. in the LRU list and we must put it to
  477. awe_LRU_free_mapped list once mapped to a frame */
  478. buf_awe_map_page_to_frame(block, TRUE);
  479. }
  480. block->flush_type = flush_type;
  481. if (buf_pool->n_flush[flush_type] == 0) {
  482. os_event_reset(buf_pool->no_flush[flush_type]);
  483. }
  484. (buf_pool->n_flush[flush_type])++;
  485. rw_lock_s_lock_gen(&(block->lock), BUF_IO_WRITE);
  486. /* Note that the s-latch is acquired before releasing the
  487. buf_pool mutex: this ensures that the latch is acquired
  488. immediately. */
  489. mutex_exit(&(buf_pool->mutex));
  490. buf_flush_write_block_low(block);
  491. return(1);
  492. } else if (flush_type == BUF_FLUSH_SINGLE_PAGE && block
  493. && buf_flush_ready_for_flush(block, flush_type)) {
  494. block->io_fix = BUF_IO_WRITE;
  495. /* If AWE is enabled and the page is not mapped to a frame,
  496. then map it */
  497. if (block->frame == NULL) {
  498. ut_a(srv_use_awe);
  499. /* We set second parameter TRUE because the block is
  500. in the LRU list and we must put it to
  501. awe_LRU_free_mapped list once mapped to a frame */
  502. buf_awe_map_page_to_frame(block, TRUE);
  503. }
  504. block->flush_type = flush_type;
  505. if (buf_pool->n_flush[block->flush_type] == 0) {
  506. os_event_reset(buf_pool->no_flush[block->flush_type]);
  507. }
  508. (buf_pool->n_flush[flush_type])++;
  509. mutex_exit(&(buf_pool->mutex));
  510. rw_lock_s_lock_gen(&(block->lock), BUF_IO_WRITE);
  511. if (buf_debug_prints) {
  512. fprintf(stderr,
  513. "Flushing single page space %lu, page no %lu n",
  514. (ulong) block->space,
  515.                         (ulong) block->offset);
  516. }
  517. buf_flush_write_block_low(block);
  518. return(1);
  519. } else {
  520. mutex_exit(&(buf_pool->mutex));
  521. return(0);
  522. }
  523. }
  524. /***************************************************************
  525. Flushes to disk all flushable pages within the flush area. */
  526. static
  527. ulint
  528. buf_flush_try_neighbors(
  529. /*====================*/
  530. /* out: number of pages flushed */
  531. ulint space, /* in: space id */
  532. ulint offset, /* in: page offset */
  533. ulint flush_type) /* in: BUF_FLUSH_LRU or BUF_FLUSH_LIST */
  534. {
  535. buf_block_t* block;
  536. ulint low, high;
  537. ulint count = 0;
  538. ulint i;
  539. ut_ad(flush_type == BUF_FLUSH_LRU || flush_type == BUF_FLUSH_LIST);
  540. low = (offset / BUF_FLUSH_AREA) * BUF_FLUSH_AREA;
  541. high = (offset / BUF_FLUSH_AREA + 1) * BUF_FLUSH_AREA;
  542. if (UT_LIST_GET_LEN(buf_pool->LRU) < BUF_LRU_OLD_MIN_LEN) {
  543. /* If there is little space, it is better not to flush any
  544. block except from the end of the LRU list */
  545. low = offset;
  546. high = offset + 1;
  547. }
  548. /* fprintf(stderr, "Flush area: low %lu high %lun", low, high); */
  549. if (high > fil_space_get_size(space)) {
  550. high = fil_space_get_size(space);
  551. }
  552. mutex_enter(&(buf_pool->mutex));
  553. for (i = low; i < high; i++) {
  554. block = buf_page_hash_get(space, i);
  555. ut_a(!block || block->state == BUF_BLOCK_FILE_PAGE);
  556. if (block && flush_type == BUF_FLUSH_LRU && i != offset
  557.     && !block->old) {
  558.         /* We avoid flushing 'non-old' blocks in an LRU flush,
  559.         because the flushed blocks are soon freed */
  560.         continue;
  561. }
  562. if (block && buf_flush_ready_for_flush(block, flush_type)
  563.    && (i == offset || block->buf_fix_count == 0)) {
  564. /* We only try to flush those neighbors != offset
  565. where the buf fix count is zero, as we then know that
  566. we probably can latch the page without a semaphore
  567. wait. Semaphore waits are expensive because we must
  568. flush the doublewrite buffer before we start
  569. waiting. */
  570. mutex_exit(&(buf_pool->mutex));
  571. /* Note: as we release the buf_pool mutex above, in
  572. buf_flush_try_page we cannot be sure the page is still
  573. in a flushable state: therefore we check it again
  574. inside that function. */
  575. count += buf_flush_try_page(space, i, flush_type);
  576. mutex_enter(&(buf_pool->mutex));
  577. }
  578. }
  579. mutex_exit(&(buf_pool->mutex));
  580. return(count);
  581. }
  582. /***********************************************************************
  583. This utility flushes dirty blocks from the end of the LRU list or flush_list.
  584. NOTE 1: in the case of an LRU flush the calling thread may own latches to
  585. pages: to avoid deadlocks, this function must be written so that it cannot
  586. end up waiting for these latches! NOTE 2: in the case of a flush list flush,
  587. the calling thread is not allowed to own any latches on pages! */
  588. ulint
  589. buf_flush_batch(
  590. /*============*/
  591. /* out: number of blocks for which the write
  592. request was queued; ULINT_UNDEFINED if there
  593. was a flush of the same type already running */
  594. ulint flush_type, /* in: BUF_FLUSH_LRU or BUF_FLUSH_LIST; if
  595. BUF_FLUSH_LIST, then the caller must not own
  596. any latches on pages */
  597. ulint min_n, /* in: wished minimum mumber of blocks flushed
  598. (it is not guaranteed that the actual number
  599. is that big, though) */
  600. dulint lsn_limit) /* in the case BUF_FLUSH_LIST all blocks whose
  601. oldest_modification is smaller than this
  602. should be flushed (if their number does not
  603. exceed min_n), otherwise ignored */
  604. {
  605. buf_block_t* block;
  606. ulint page_count  = 0;
  607. ulint old_page_count;
  608. ulint space;
  609. ulint offset;
  610. ibool found;
  611. ut_ad((flush_type == BUF_FLUSH_LRU)
  612. || (flush_type == BUF_FLUSH_LIST)); 
  613. ut_ad((flush_type != BUF_FLUSH_LIST)
  614. || sync_thread_levels_empty_gen(TRUE));
  615. mutex_enter(&(buf_pool->mutex));
  616. if ((buf_pool->n_flush[flush_type] > 0)
  617.     || (buf_pool->init_flush[flush_type] == TRUE)) {
  618. /* There is already a flush batch of the same type running */
  619. mutex_exit(&(buf_pool->mutex));
  620. return(ULINT_UNDEFINED);
  621. }
  622. (buf_pool->init_flush)[flush_type] = TRUE;
  623. for (;;) {
  624. /* If we have flushed enough, leave the loop */
  625. if (page_count >= min_n) {
  626. break;
  627. }
  628. /* Start from the end of the list looking for a suitable
  629. block to be flushed. */
  630.      if (flush_type == BUF_FLUSH_LRU) {
  631. block = UT_LIST_GET_LAST(buf_pool->LRU);
  632.      } else {
  633. ut_ad(flush_type == BUF_FLUSH_LIST);
  634. block = UT_LIST_GET_LAST(buf_pool->flush_list);
  635. if (!block
  636.     || (ut_dulint_cmp(block->oldest_modification,
  637.      lsn_limit) >= 0)) {
  638. /* We have flushed enough */
  639. break;
  640. }
  641.      }
  642.     
  643.      found = FALSE;
  644. /* Note that after finding a single flushable page, we try to
  645. flush also all its neighbors, and after that start from the
  646. END of the LRU list or flush list again: the list may change
  647. during the flushing and we cannot safely preserve within this
  648. function a pointer to a block in the list! */
  649.      while ((block != NULL) && !found) {
  650. ut_a(block->state == BUF_BLOCK_FILE_PAGE);
  651. if (buf_flush_ready_for_flush(block, flush_type)) {
  652. found = TRUE;
  653. space = block->space;
  654. offset = block->offset;
  655.     
  656. mutex_exit(&(buf_pool->mutex));
  657. old_page_count = page_count;
  658. /* Try to flush also all the neighbors */
  659. page_count +=
  660. buf_flush_try_neighbors(space, offset,
  661. flush_type);
  662. /* fprintf(stderr,
  663. "Flush type %lu, page no %lu, neighb %lun",
  664. flush_type, offset,
  665. page_count - old_page_count); */
  666. mutex_enter(&(buf_pool->mutex));
  667. } else if (flush_type == BUF_FLUSH_LRU) {
  668. block = UT_LIST_GET_PREV(LRU, block);
  669. } else {
  670. ut_ad(flush_type == BUF_FLUSH_LIST);
  671. block = UT_LIST_GET_PREV(flush_list, block);
  672. }
  673.      }
  674.      /* If we could not find anything to flush, leave the loop */
  675.      if (!found) {
  676.      break;
  677.      }
  678. }
  679. (buf_pool->init_flush)[flush_type] = FALSE;
  680. if ((buf_pool->n_flush[flush_type] == 0)
  681.     && (buf_pool->init_flush[flush_type] == FALSE)) {
  682. /* The running flush batch has ended */
  683. os_event_set(buf_pool->no_flush[flush_type]);
  684. }
  685. mutex_exit(&(buf_pool->mutex));
  686. buf_flush_buffered_writes();
  687. if (buf_debug_prints && page_count > 0) {
  688. ut_a(flush_type == BUF_FLUSH_LRU
  689. || flush_type == BUF_FLUSH_LIST);
  690. fprintf(stderr, flush_type == BUF_FLUSH_LRU
  691. ? "Flushed %lu pages in LRU flushn"
  692. : "Flushed %lu pages in flush list flushn",
  693. (ulong) page_count);
  694. }
  695. return(page_count);
  696. }
  697. /**********************************************************************
  698. Waits until a flush batch of the given type ends */
  699. void
  700. buf_flush_wait_batch_end(
  701. /*=====================*/
  702. ulint type) /* in: BUF_FLUSH_LRU or BUF_FLUSH_LIST */
  703. {
  704. ut_ad((type == BUF_FLUSH_LRU) || (type == BUF_FLUSH_LIST));
  705. os_event_wait(buf_pool->no_flush[type]);
  706. }
  707. /**********************************************************************
  708. Gives a recommendation of how many blocks should be flushed to establish
  709. a big enough margin of replaceable blocks near the end of the LRU list
  710. and in the free list. */
  711. static
  712. ulint
  713. buf_flush_LRU_recommendation(void)
  714. /*==============================*/
  715. /* out: number of blocks which should be flushed
  716. from the end of the LRU list */
  717. {
  718. buf_block_t* block;
  719. ulint n_replaceable;
  720. ulint distance = 0;
  721. mutex_enter(&(buf_pool->mutex));
  722. n_replaceable = UT_LIST_GET_LEN(buf_pool->free);
  723. block = UT_LIST_GET_LAST(buf_pool->LRU);
  724. while ((block != NULL)
  725.        && (n_replaceable < BUF_FLUSH_FREE_BLOCK_MARGIN
  726.         + BUF_FLUSH_EXTRA_MARGIN)
  727.        && (distance < BUF_LRU_FREE_SEARCH_LEN)) {
  728. if (buf_flush_ready_for_replace(block)) {
  729. n_replaceable++;
  730. }
  731. distance++;
  732. block = UT_LIST_GET_PREV(LRU, block);
  733. }
  734. mutex_exit(&(buf_pool->mutex));
  735. if (n_replaceable >= BUF_FLUSH_FREE_BLOCK_MARGIN) {
  736. return(0);
  737. }
  738. return(BUF_FLUSH_FREE_BLOCK_MARGIN + BUF_FLUSH_EXTRA_MARGIN
  739. - n_replaceable);
  740. }
  741. /*************************************************************************
  742. Flushes pages from the end of the LRU list if there is too small a margin
  743. of replaceable pages there or in the free list. VERY IMPORTANT: this function
  744. is called also by threads which have locks on pages. To avoid deadlocks, we
  745. flush only pages such that the s-lock required for flushing can be acquired
  746. immediately, without waiting. */ 
  747. void
  748. buf_flush_free_margin(void)
  749. /*=======================*/
  750. {
  751. ulint n_to_flush;
  752. ulint n_flushed;
  753. n_to_flush = buf_flush_LRU_recommendation();
  754. if (n_to_flush > 0) {
  755. n_flushed = buf_flush_batch(BUF_FLUSH_LRU, n_to_flush,
  756. ut_dulint_zero);
  757. if (n_flushed == ULINT_UNDEFINED) {
  758. /* There was an LRU type flush batch already running;
  759. let us wait for it to end */
  760.    
  761.         buf_flush_wait_batch_end(BUF_FLUSH_LRU);
  762. }
  763. }
  764. }
  765. /**********************************************************************
  766. Validates the flush list. */
  767. static
  768. ibool
  769. buf_flush_validate_low(void)
  770. /*========================*/
  771. /* out: TRUE if ok */
  772. {
  773. buf_block_t* block;
  774. dulint om;
  775. UT_LIST_VALIDATE(flush_list, buf_block_t, buf_pool->flush_list);
  776. block = UT_LIST_GET_FIRST(buf_pool->flush_list);
  777. while (block != NULL) {
  778. om = block->oldest_modification;
  779. ut_a(block->state == BUF_BLOCK_FILE_PAGE);
  780. ut_a(ut_dulint_cmp(om, ut_dulint_zero) > 0);
  781. block = UT_LIST_GET_NEXT(flush_list, block);
  782. if (block) {
  783. ut_a(ut_dulint_cmp(om, block->oldest_modification)
  784. >= 0);
  785. }
  786. }
  787. return(TRUE);
  788. }
  789. /**********************************************************************
  790. Validates the flush list. */
  791. ibool
  792. buf_flush_validate(void)
  793. /*====================*/
  794. /* out: TRUE if ok */
  795. {
  796. ibool ret;
  797. mutex_enter(&(buf_pool->mutex));
  798. ret = buf_flush_validate_low();
  799. mutex_exit(&(buf_pool->mutex));
  800. return(ret);
  801. }