1 /*****************************************************************************
2 
3 Copyright (c) 2011-2012 Percona Inc. All Rights Reserved.
4 
5 This program is free software; you can redistribute it and/or modify it under
6 the terms of the GNU General Public License as published by the Free Software
7 Foundation; version 2 of the License.
8 
9 This program is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 
13 You should have received a copy of the GNU General Public License along with
14 this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
15 Street, Fifth Floor, Boston, MA 02110-1301, USA
16 
17 *****************************************************************************/
18 
19 /**************************************************//**
20 @file log/log0online.cc
21 Online database log parsing for changed page tracking
22 
23 *******************************************************/
24 
25 #include "log0online.h"
26 
27 #include "my_dbug.h"
28 
29 #include "log0recv.h"
30 #include "mach0data.h"
31 #include "mtr0log.h"
32 #include "srv0srv.h"
33 #include "srv0start.h"
34 #include "trx0sys.h"
35 #include "ut0rbt.h"
36 
37 #ifdef __WIN__
38 /* error LNK2001: unresolved external symbol _debug_sync_C_callback_ptr */
39 # define DEBUG_SYNC_C(dummy) ((void) 0)
40 #else
41 # include "m_string.h" /* for my_sys.h */
42 # include "my_sys.h" /* DEBUG_SYNC_C */
43 #endif
44 
45 enum { FOLLOW_SCAN_SIZE = 4 * (UNIV_PAGE_SIZE_MAX) };
46 
47 #ifdef UNIV_PFS_MUTEX
48 /* Key to register log_bmp_sys->mutex with PFS */
49 UNIV_INTERN mysql_pfs_key_t	log_bmp_sys_mutex_key;
50 #endif /* UNIV_PFS_MUTEX */
51 
52 /** Log parsing and bitmap output data structure */
53 struct log_bitmap_struct {
54 	byte*		read_buf_ptr;	/*!< Unaligned log read buffer */
55 	byte*		read_buf;	/*!< log read buffer */
56 	byte		parse_buf[RECV_PARSING_BUF_SIZE];
57 					/*!< log parse buffer */
58 	byte*		parse_buf_end;  /*!< parse buffer position where the
59 					next read log data should be copied to.
60 					If the previous log records were fully
61 					parsed, it points to the start,
62 					otherwise points immediatelly past the
63 					end of the incomplete log record. */
64 	char		bmp_file_home[FN_REFLEN];
65 					/*!< directory for bitmap files */
66 	log_online_bitmap_file_t out;	/*!< The current bitmap file */
67 	ulint		out_seq_num;	/*!< the bitmap file sequence number */
68 	lsn_t		start_lsn;	/*!< the LSN of the next unparsed
69 					record and the start of the next LSN
70 					interval to be parsed.  */
71 	lsn_t		end_lsn;	/*!< the end of the LSN interval to be
72 					parsed, equal to the next checkpoint
73 					LSN at the time of parse */
74 	lsn_t		next_parse_lsn;	/*!< the LSN of the next unparsed
75 					record in the current parse */
76 	ib_rbt_t*	modified_pages; /*!< the current modified page set,
77 					organized as the RB-tree with the keys
78 					of (space, 4KB-block-start-page-id)
79 					pairs */
80 	ib_rbt_node_t*	page_free_list; /*!< Singly-linked list of freed nodes
81 					of modified_pages tree for later
82 					reuse.  Nodes are linked through
83 					ib_rbt_node_t.left as this field has
84 					both the correct type and the tree does
85 					not mind its overwrite during
86 					rbt_next() tree traversal. */
87 };
88 
89 /* The log parsing and bitmap output struct instance */
90 static struct log_bitmap_struct* log_bmp_sys;
91 
92 /* Mutex protecting log_bmp_sys */
93 static ib_mutex_t	log_bmp_sys_mutex;
94 
95 /** File name stem for bitmap files. */
96 static const char* bmp_file_name_stem = "ib_modified_log_";
97 
98 /** File name template for bitmap files.  The 1st format tag is a directory
99 name, the 2nd tag is the stem, the 3rd tag is a file sequence number, the 4th
100 tag is the start LSN for the file. */
101 static const char* bmp_file_name_template = "%s%s%lu_" LSN_PF ".xdb";
102 
103 /* On server startup with empty database srv_start_lsn == 0, in
104 which case the first LSN of actual log records will be this. */
105 #define MIN_TRACKED_LSN ((LOG_START_LSN) + (LOG_BLOCK_HDR_SIZE))
106 
107 /* Tests if num bit of bitmap is set */
108 #define IS_BIT_SET(bitmap, num) \
109 	(*((bitmap) + ((num) >> 3)) & (1UL << ((num) & 7UL)))
110 
111 /** The bitmap file block size in bytes.  All writes will be multiples of this.
112  */
113 enum {
114 	MODIFIED_PAGE_BLOCK_SIZE = 4096
115 };
116 
117 
118 /** Offsets in a file bitmap block */
119 enum {
120 	MODIFIED_PAGE_IS_LAST_BLOCK = 0,/* 1 if last block in the current
121 					write, 0 otherwise. */
122 	MODIFIED_PAGE_START_LSN = 4,	/* The starting tracked LSN of this and
123 					other blocks in the same write */
124 	MODIFIED_PAGE_END_LSN = 12,	/* The ending tracked LSN of this and
125 					other blocks in the same write */
126 	MODIFIED_PAGE_SPACE_ID = 20,	/* The space ID of tracked pages in
127 					this block */
128 	MODIFIED_PAGE_1ST_PAGE_ID = 24,	/* The page ID of the first tracked
129 					page in this block */
130 	MODIFIED_PAGE_BLOCK_UNUSED_1 = 28,/* Unused in order to align the start
131 					of bitmap at 8 byte boundary */
132 	MODIFIED_PAGE_BLOCK_BITMAP = 32,/* Start of the bitmap itself */
133 	MODIFIED_PAGE_BLOCK_UNUSED_2 = MODIFIED_PAGE_BLOCK_SIZE - 8,
134 					/* Unused in order to align the end of
135 					bitmap at 8 byte boundary */
136 	MODIFIED_PAGE_BLOCK_CHECKSUM = MODIFIED_PAGE_BLOCK_SIZE - 4
137 					/* The checksum of the current block */
138 };
139 
140 /** Length of the bitmap data in a block in bytes */
141 enum { MODIFIED_PAGE_BLOCK_BITMAP_LEN
142        = MODIFIED_PAGE_BLOCK_UNUSED_2 - MODIFIED_PAGE_BLOCK_BITMAP };
143 
144 /** Length of the bitmap data in a block in page ids */
145 enum { MODIFIED_PAGE_BLOCK_ID_COUNT = MODIFIED_PAGE_BLOCK_BITMAP_LEN * 8 };
146 
147 /****************************************************************//**
148 Provide a comparisson function for the RB-tree tree (space,
149 block_start_page) pairs.  Actual implementation does not matter as
150 long as the ordering is full.
151 @return -1 if p1 < p2, 0 if p1 == p2, 1 if p1 > p2
152 */
153 static
154 int
log_online_compare_bmp_keys(const void * p1,const void * p2)155 log_online_compare_bmp_keys(
156 /*========================*/
157 	const void* p1,	/*!<in: 1st key to compare */
158 	const void* p2)	/*!<in: 2nd key to compare */
159 {
160 	const byte *k1 = (const byte *)p1;
161 	const byte *k2 = (const byte *)p2;
162 
163 	ulint k1_space = mach_read_from_4(k1 + MODIFIED_PAGE_SPACE_ID);
164 	ulint k2_space = mach_read_from_4(k2 + MODIFIED_PAGE_SPACE_ID);
165 	if (k1_space == k2_space) {
166 		ulint k1_start_page
167 			= mach_read_from_4(k1 + MODIFIED_PAGE_1ST_PAGE_ID);
168 		ulint k2_start_page
169 			= mach_read_from_4(k2 + MODIFIED_PAGE_1ST_PAGE_ID);
170 		return k1_start_page < k2_start_page
171 			? -1 : k1_start_page > k2_start_page ? 1 : 0;
172 	}
173 	return k1_space < k2_space ? -1 : 1;
174 }
175 
176 /****************************************************************//**
177 Set a bit for tracked page in the bitmap. Expand the bitmap tree as
178 necessary. */
179 static
180 void
log_online_set_page_bit(ulint space,ulint page_no)181 log_online_set_page_bit(
182 /*====================*/
183 	ulint	space,	/*!<in: log record space id */
184 	ulint	page_no)/*!<in: log record page id */
185 {
186 	ut_ad(mutex_own(&log_bmp_sys_mutex));
187 
188 	ut_a(space != ULINT_UNDEFINED);
189 	ut_a(page_no != ULINT_UNDEFINED);
190 
191 	ulint block_start_page = page_no / MODIFIED_PAGE_BLOCK_ID_COUNT
192 		* MODIFIED_PAGE_BLOCK_ID_COUNT;
193 	ulint block_pos = block_start_page ? (page_no % block_start_page / 8)
194 		: (page_no / 8);
195 	uint bit_pos = page_no % 8;
196 
197 	byte search_page[MODIFIED_PAGE_BLOCK_SIZE];
198 	mach_write_to_4(search_page + MODIFIED_PAGE_SPACE_ID, space);
199 	mach_write_to_4(search_page + MODIFIED_PAGE_1ST_PAGE_ID,
200 			block_start_page);
201 
202 	byte	       *page_ptr;
203 	ib_rbt_bound_t  tree_search_pos;
204 	if (!rbt_search(log_bmp_sys->modified_pages, &tree_search_pos,
205 			search_page)) {
206 		page_ptr = rbt_value(byte, tree_search_pos.last);
207 	}
208 	else {
209 		ib_rbt_node_t *new_node;
210 
211 		if (log_bmp_sys->page_free_list) {
212 			new_node = log_bmp_sys->page_free_list;
213 			log_bmp_sys->page_free_list = new_node->left;
214 		}
215 		else {
216 			new_node = static_cast<ib_rbt_node_t *>
217 				(ut_malloc
218 				 (SIZEOF_NODE(log_bmp_sys->modified_pages)));
219 		}
220 		memset(new_node, 0, SIZEOF_NODE(log_bmp_sys->modified_pages));
221 
222 		page_ptr = rbt_value(byte, new_node);
223 		mach_write_to_4(page_ptr + MODIFIED_PAGE_SPACE_ID, space);
224 		mach_write_to_4(page_ptr + MODIFIED_PAGE_1ST_PAGE_ID,
225 				block_start_page);
226 
227 		rbt_add_preallocated_node(log_bmp_sys->modified_pages,
228 					  &tree_search_pos, new_node);
229 	}
230 	page_ptr[MODIFIED_PAGE_BLOCK_BITMAP + block_pos] |= (1U << bit_pos);
231 }
232 
233 /****************************************************************//**
234 Calculate a bitmap block checksum.  Algorithm borrowed from
235 log_block_calc_checksum.
236 @return checksum */
237 UNIV_INLINE
238 ulint
log_online_calc_checksum(const byte * block)239 log_online_calc_checksum(
240 /*=====================*/
241 	const byte*	block)	/*!<in: bitmap block */
242 {
243 	ulint	sum;
244 	ulint	sh;
245 	ulint	i;
246 
247 	sum = 1;
248 	sh = 0;
249 
250 	for (i = 0; i < MODIFIED_PAGE_BLOCK_CHECKSUM; i++) {
251 
252 		ulint	b = block[i];
253 		sum &= 0x7FFFFFFFUL;
254 		sum += b;
255 		sum += b << sh;
256 		sh++;
257 		if (sh > 24) {
258 			sh = 0;
259 		}
260 	}
261 
262 	return sum;
263 }
264 
265 /****************************************************************//**
266 Read one bitmap data page and check it for corruption.
267 
268 @return TRUE if page read OK, FALSE if I/O error */
269 static
270 ibool
log_online_read_bitmap_page(log_online_bitmap_file_t * bitmap_file,byte * page,ibool * checksum_ok)271 log_online_read_bitmap_page(
272 /*========================*/
273 	log_online_bitmap_file_t	*bitmap_file,	/*!<in/out: bitmap
274 							file */
275 	byte				*page,	       /*!<out: read page.
276 						       Must be at least
277 						       MODIFIED_PAGE_BLOCK_SIZE
278 						       bytes long */
279 	ibool				*checksum_ok)	/*!<out: TRUE if page
280 							checksum OK */
281 {
282 	ulint	checksum;
283 	ulint	actual_checksum;
284 	ibool	success;
285 
286 	ut_a(bitmap_file->size >= MODIFIED_PAGE_BLOCK_SIZE);
287 	ut_a(bitmap_file->offset
288 	     <= bitmap_file->size - MODIFIED_PAGE_BLOCK_SIZE);
289 	ut_a(bitmap_file->offset % MODIFIED_PAGE_BLOCK_SIZE == 0);
290 
291 	success = os_file_read(bitmap_file->file, page, bitmap_file->offset,
292 			       MODIFIED_PAGE_BLOCK_SIZE);
293 
294 	if (UNIV_UNLIKELY(!success)) {
295 
296 		/* The following call prints an error message */
297 		os_file_get_last_error(TRUE);
298 		ib_logf(IB_LOG_LEVEL_WARN,
299 			"failed reading changed page bitmap file \'%s\'",
300 			bitmap_file->name);
301 		return FALSE;
302 	}
303 
304 	bitmap_file->offset += MODIFIED_PAGE_BLOCK_SIZE;
305 	ut_ad(bitmap_file->offset <= bitmap_file->size);
306 
307 	checksum = mach_read_from_4(page + MODIFIED_PAGE_BLOCK_CHECKSUM);
308 	actual_checksum = log_online_calc_checksum(page);
309 	*checksum_ok = (checksum == actual_checksum);
310 
311 	return TRUE;
312 }
313 
314 /****************************************************************//**
315 Get the last tracked fully LSN from the bitmap file by reading
316 backwards untile a correct end page is found.  Detects incomplete
317 writes and corrupted data.  Sets the start output position for the
318 written bitmap data.
319 
320 Multiple bitmap files are handled using the following assumptions:
321 1) Only the last file might be corrupted.  In case where no good data was found
322 in the last file, assume that the next to last file is OK.  This assumption
323 does not limit crash recovery capability in any way.
324 2) If the whole of the last file was corrupted, assume that the start LSN in
325 its name is correct and use it for (re-)tracking start.
326 
327 @return the last fully tracked LSN */
328 static
329 lsn_t
log_online_read_last_tracked_lsn(void)330 log_online_read_last_tracked_lsn(void)
331 /*==================================*/
332 {
333 	byte		page[MODIFIED_PAGE_BLOCK_SIZE];
334 	ibool		is_last_page	= FALSE;
335 	ibool		checksum_ok	= FALSE;
336 	lsn_t		result;
337 	os_offset_t	read_offset	= log_bmp_sys->out.offset;
338 
339 	while ((!checksum_ok || !is_last_page) && read_offset > 0)
340 	{
341 		read_offset -= MODIFIED_PAGE_BLOCK_SIZE;
342 		log_bmp_sys->out.offset = read_offset;
343 
344 		if (!log_online_read_bitmap_page(&log_bmp_sys->out, page,
345 						 &checksum_ok)) {
346 			checksum_ok = FALSE;
347 			result = 0;
348 			break;
349 		}
350 
351 		if (checksum_ok) {
352 			is_last_page
353 				= mach_read_from_4
354 				(page + MODIFIED_PAGE_IS_LAST_BLOCK);
355 		} else {
356 
357 			ib_logf(IB_LOG_LEVEL_WARN,
358 				"corruption detected in \'%s\' at offset "
359 				UINT64PF,
360 				log_bmp_sys->out.name, read_offset);
361 		}
362 	};
363 
364 	result = (checksum_ok && is_last_page)
365 		? mach_read_from_8(page + MODIFIED_PAGE_END_LSN) : 0;
366 
367 	/* Truncate the output file to discard the corrupted bitmap data, if
368 	any */
369 	if (!os_file_set_eof_at(log_bmp_sys->out.file,
370 				log_bmp_sys->out.offset)) {
371 		ib_logf(IB_LOG_LEVEL_WARN,
372 			"failed truncating changed page bitmap file \'%s\' to "
373 			UINT64PF " bytes",
374 			log_bmp_sys->out.name, log_bmp_sys->out.offset);
375 		result = 0;
376 	}
377 	return result;
378 }
379 
380 /****************************************************************//**
381 Safely write the log_sys->tracked_lsn value.  Uses atomic operations
382 if available, otherwise this field is protected with the log system
383 mutex.  The reader counterpart function is log_get_tracked_lsn() in
384 log0log.c. */
385 UNIV_INLINE
386 void
log_set_tracked_lsn(lsn_t tracked_lsn)387 log_set_tracked_lsn(
388 /*================*/
389 	lsn_t	tracked_lsn)	/*!<in: new value */
390 {
391 	log_sys->tracked_lsn = tracked_lsn;
392 	os_wmb;
393 }
394 
395 /*********************************************************************//**
396 Check if missing, if any, LSN interval can be read and tracked using the
397 current LSN value, the LSN value where the tracking stopped, and the log group
398 capacity.
399 
400 @return TRUE if the missing interval can be tracked or if there's no missing
401 data.  */
402 static
403 ibool
log_online_can_track_missing(lsn_t last_tracked_lsn,lsn_t tracking_start_lsn)404 log_online_can_track_missing(
405 /*=========================*/
406 	lsn_t	last_tracked_lsn,	/*!<in: last tracked LSN */
407 	lsn_t	tracking_start_lsn)	/*!<in:	current LSN */
408 {
409 	/* last_tracked_lsn might be < MIN_TRACKED_LSN in the case of empty
410 	bitmap file, handle this too. */
411 	last_tracked_lsn = ut_max(last_tracked_lsn, MIN_TRACKED_LSN);
412 
413 	if (last_tracked_lsn > tracking_start_lsn) {
414 		ib_logf(IB_LOG_LEVEL_ERROR,
415 			"last tracked LSN " LSN_PF " is ahead of tracking "
416 			"start LSN " LSN_PF ".  This can be caused by "
417 			"mismatched bitmap files.",
418 			last_tracked_lsn, tracking_start_lsn);
419 		exit(1);
420 	}
421 
422 	return (last_tracked_lsn == tracking_start_lsn)
423 		|| (log_sys->lsn - last_tracked_lsn
424 		    <= log_sys->log_group_capacity);
425 }
426 
427 
428 /****************************************************************//**
429 Diagnose a gap in tracked LSN range on server startup due to crash or
430 very fast shutdown and try to close it by tracking the data
431 immediatelly, if possible. */
432 static
433 void
log_online_track_missing_on_startup(lsn_t last_tracked_lsn,lsn_t tracking_start_lsn)434 log_online_track_missing_on_startup(
435 /*================================*/
436 	lsn_t	last_tracked_lsn,	/*!<in: last tracked LSN read from the
437 					bitmap file */
438 	lsn_t	tracking_start_lsn)	/*!<in: last checkpoint LSN of the
439 					current server startup */
440 {
441 	ut_ad(last_tracked_lsn != tracking_start_lsn);
442 	ut_ad(srv_track_changed_pages);
443 
444 	ib_logf(IB_LOG_LEVEL_WARN, "last tracked LSN in \'%s\' is " LSN_PF
445 		", but the last checkpoint LSN is " LSN_PF ".  This might be "
446 		"due to a server crash or a very fast shutdown.",
447 		log_bmp_sys->out.name, last_tracked_lsn, tracking_start_lsn);
448 
449 	/* See if we can fully recover the missing interval */
450 	if (log_online_can_track_missing(last_tracked_lsn,
451 					 tracking_start_lsn)) {
452 
453 		ib_logf(IB_LOG_LEVEL_INFO,
454 			"reading the log to advance the last tracked LSN.");
455 
456 		log_bmp_sys->start_lsn = ut_max(last_tracked_lsn,
457 						MIN_TRACKED_LSN);
458 		log_set_tracked_lsn(log_bmp_sys->start_lsn);
459 		if (!log_online_follow_redo_log()) {
460 			exit(1);
461 		}
462 		ut_ad(log_bmp_sys->end_lsn >= tracking_start_lsn);
463 
464 		ib_logf(IB_LOG_LEVEL_INFO,
465 			"continuing tracking changed pages from LSN " LSN_PF,
466 			log_bmp_sys->end_lsn);
467 	}
468 	else {
469 		ib_logf(IB_LOG_LEVEL_WARN,
470 			"the age of last tracked LSN exceeds log capacity, "
471 			"tracking-based incremental backups will work only "
472 			"from the higher LSN!");
473 
474 		log_bmp_sys->end_lsn = log_bmp_sys->start_lsn
475 			= tracking_start_lsn;
476 		log_set_tracked_lsn(log_bmp_sys->start_lsn);
477 
478 		ib_logf(IB_LOG_LEVEL_INFO,
479 			"starting tracking changed pages from LSN " LSN_PF,
480 			log_bmp_sys->end_lsn);
481 	}
482 }
483 
484 /*********************************************************************//**
485 Format a bitmap output file name to log_bmp_sys->out.name.  */
486 static
487 void
log_online_make_bitmap_name(lsn_t start_lsn)488 log_online_make_bitmap_name(
489 /*=========================*/
490 	lsn_t	start_lsn)	/*!< in: the start LSN name part */
491 {
492 	ut_snprintf(log_bmp_sys->out.name, sizeof(log_bmp_sys->out.name), bmp_file_name_template,
493 		    log_bmp_sys->bmp_file_home, bmp_file_name_stem,
494 		    log_bmp_sys->out_seq_num, start_lsn);
495 }
496 
497 /*********************************************************************//**
498 Check if an old file that has the name of a new bitmap file we are about to
499 create should be overwritten.  */
500 static
501 ibool
log_online_should_overwrite(const char * path)502 log_online_should_overwrite(
503 /*========================*/
504 	const char	*path)	/*!< in: path to file */
505 {
506 	dberr_t		err;
507 	os_file_stat_t	file_info;
508 
509 	/* Currently, it's OK to overwrite 0-sized files only */
510 	err = os_file_get_status(path, &file_info, false);
511 	return err == DB_SUCCESS && file_info.type == OS_FILE_TYPE_FILE
512 		&& file_info.size == 0LL;
513 }
514 
515 /*********************************************************************//**
516 Create a new empty bitmap output file.
517 
518 @return TRUE if operation succeeded, FALSE if I/O error */
519 static
520 ibool
log_online_start_bitmap_file(void)521 log_online_start_bitmap_file(void)
522 /*==============================*/
523 {
524 	ibool	success	= TRUE;
525 
526 	/* Check for an old file that should be deleted first */
527 	if (log_online_should_overwrite(log_bmp_sys->out.name)) {
528 
529 		success = static_cast<ibool>(
530 			os_file_delete_if_exists(innodb_file_bmp_key,
531 						 log_bmp_sys->out.name));
532 	}
533 
534 	if (UNIV_LIKELY(success)) {
535 		log_bmp_sys->out.file
536 			= os_file_create_simple_no_error_handling(
537 							innodb_file_bmp_key,
538 							log_bmp_sys->out.name,
539 							OS_FILE_CREATE,
540 							OS_FILE_READ_WRITE_CACHED,
541 							&success);
542 	}
543 	if (UNIV_UNLIKELY(!success)) {
544 
545 		/* The following call prints an error message */
546 		os_file_get_last_error(TRUE);
547 		ib_logf(IB_LOG_LEVEL_ERROR,
548 			"cannot create \'%s\'", log_bmp_sys->out.name);
549 		return FALSE;
550 	}
551 
552 	log_bmp_sys->out.offset = 0;
553 	return TRUE;
554 }
555 
556 /*********************************************************************//**
557 Close the current bitmap output file and create the next one.
558 
559 @return TRUE if operation succeeded, FALSE if I/O error */
560 static
561 ibool
log_online_rotate_bitmap_file(lsn_t next_file_start_lsn)562 log_online_rotate_bitmap_file(
563 /*===========================*/
564 	lsn_t	next_file_start_lsn)	/*!<in: the start LSN name
565 					part */
566 {
567 	if (!os_file_is_invalid(log_bmp_sys->out.file)) {
568 		os_file_close(log_bmp_sys->out.file);
569 		os_file_mark_invalid(&log_bmp_sys->out.file);
570 	}
571 	log_bmp_sys->out_seq_num++;
572 	log_online_make_bitmap_name(next_file_start_lsn);
573 	return log_online_start_bitmap_file();
574 }
575 
576 /*********************************************************************//**
577 Check the name of a given file if it's a changed page bitmap file and
578 return file sequence and start LSN name components if it is.  If is not,
579 the values of output parameters are undefined.
580 
581 @return TRUE if a given file is a changed page bitmap file.  */
582 static
583 ibool
log_online_is_bitmap_file(const os_file_stat_t * file_info,ulong * bitmap_file_seq_num,lsn_t * bitmap_file_start_lsn)584 log_online_is_bitmap_file(
585 /*======================*/
586 	const os_file_stat_t*	file_info,		/*!<in: file to
587 							check */
588 	ulong*			bitmap_file_seq_num,	/*!<out: bitmap file
589 							sequence number */
590 	lsn_t*			bitmap_file_start_lsn)	/*!<out: bitmap file
591 							start LSN */
592 {
593 	char	stem[FN_REFLEN];
594 
595 	ut_ad (strlen(file_info->name) < OS_FILE_MAX_PATH);
596 
597 	return ((file_info->type == OS_FILE_TYPE_FILE
598 		 || file_info->type == OS_FILE_TYPE_LINK)
599 		&& (sscanf(file_info->name, "%[a-z_]%lu_" LSN_PF ".xdb", stem,
600 			   bitmap_file_seq_num, bitmap_file_start_lsn) == 3)
601 		&& (!strcmp(stem, bmp_file_name_stem)));
602 }
603 
604 /** Initialize the constant part of the log tracking subsystem */
605 UNIV_INTERN
606 void
log_online_init(void)607 log_online_init(void)
608 {
609 	mutex_create(log_bmp_sys_mutex_key, &log_bmp_sys_mutex,
610 		     SYNC_LOG_ONLINE);
611 }
612 
613 /** Initialize the dynamic part of the log tracking subsystem */
614 UNIV_INTERN
615 void
log_online_read_init(void)616 log_online_read_init(void)
617 {
618 	ibool	success;
619 	lsn_t	tracking_start_lsn
620 		= ut_max(log_sys->last_checkpoint_lsn, MIN_TRACKED_LSN);
621 	os_file_dir_t	bitmap_dir;
622 	os_file_stat_t	bitmap_dir_file_info;
623 	lsn_t	last_file_start_lsn	= MIN_TRACKED_LSN;
624 	size_t	srv_data_home_len;
625 
626 	/* Bitmap data start and end in a bitmap block must be 8-byte
627 	aligned. */
628 	compile_time_assert(MODIFIED_PAGE_BLOCK_BITMAP % 8 == 0);
629 	compile_time_assert(MODIFIED_PAGE_BLOCK_BITMAP_LEN % 8 == 0);
630 
631 	ut_ad(srv_track_changed_pages);
632 
633 	log_bmp_sys = static_cast<log_bitmap_struct *>
634 		(ut_malloc(sizeof(*log_bmp_sys)));
635 	log_bmp_sys->read_buf_ptr = static_cast<byte *>
636 		(ut_malloc(FOLLOW_SCAN_SIZE + OS_FILE_LOG_BLOCK_SIZE));
637 	log_bmp_sys->read_buf = static_cast<byte *>
638 		(ut_align(log_bmp_sys->read_buf_ptr, OS_FILE_LOG_BLOCK_SIZE));
639 
640 	/* Initialize bitmap file directory from srv_data_home and add a path
641 	separator if needed.  */
642 	srv_data_home_len = strlen(srv_data_home);
643 	ut_a (srv_data_home_len < FN_REFLEN);
644 	strcpy(log_bmp_sys->bmp_file_home, srv_data_home);
645 	if (srv_data_home_len
646 	    && log_bmp_sys->bmp_file_home[srv_data_home_len - 1]
647 	    != SRV_PATH_SEPARATOR) {
648 
649 		ut_a (srv_data_home_len < FN_REFLEN - 1);
650 		log_bmp_sys->bmp_file_home[srv_data_home_len]
651 			= SRV_PATH_SEPARATOR;
652 		log_bmp_sys->bmp_file_home[srv_data_home_len + 1] = '\0';
653 	}
654 
655 	/* Enumerate existing bitmap files to either open the last one to get
656 	the last tracked LSN either to find that there are none and start
657 	tracking from scratch.  */
658 	log_bmp_sys->out.name[0] = '\0';
659 	log_bmp_sys->out_seq_num = 0;
660 
661 	bitmap_dir = os_file_opendir(log_bmp_sys->bmp_file_home, TRUE);
662 	ut_a(bitmap_dir);
663 	while (!os_file_readdir_next_file(log_bmp_sys->bmp_file_home,
664 					  bitmap_dir, &bitmap_dir_file_info)) {
665 
666 		ulong	file_seq_num;
667 		lsn_t	file_start_lsn;
668 
669 		if (!log_online_is_bitmap_file(&bitmap_dir_file_info,
670 					      &file_seq_num,
671 					      &file_start_lsn)) {
672 			continue;
673 		}
674 
675 		if (file_seq_num > log_bmp_sys->out_seq_num
676 		    && bitmap_dir_file_info.size > 0) {
677 			log_bmp_sys->out_seq_num = file_seq_num;
678 			last_file_start_lsn = file_start_lsn;
679 			/* No dir component (log_bmp_sys->bmp_file_home) here,
680 			because	that's the cwd */
681 			strncpy(log_bmp_sys->out.name,
682 				bitmap_dir_file_info.name, FN_REFLEN - 1);
683 			log_bmp_sys->out.name[FN_REFLEN - 1] = '\0';
684 		}
685 	}
686 
687 	if (os_file_closedir(bitmap_dir)) {
688 		os_file_get_last_error(TRUE);
689 		ib_logf(IB_LOG_LEVEL_ERROR, "cannot close \'%s\'",
690 			log_bmp_sys->bmp_file_home);
691 		exit(1);
692 	}
693 
694 	if (!log_bmp_sys->out_seq_num) {
695 		log_bmp_sys->out_seq_num = 1;
696 		log_online_make_bitmap_name(0);
697 	}
698 
699 	log_bmp_sys->modified_pages = rbt_create(MODIFIED_PAGE_BLOCK_SIZE,
700 						 log_online_compare_bmp_keys);
701 	log_bmp_sys->page_free_list = NULL;
702 
703 	log_bmp_sys->out.file
704 		= os_file_create_simple_no_error_handling
705 		(innodb_file_bmp_key, log_bmp_sys->out.name, OS_FILE_OPEN,
706 		 OS_FILE_READ_WRITE_CACHED, &success);
707 
708 	if (!success) {
709 
710 		/* New file, tracking from scratch */
711 		if (!log_online_start_bitmap_file()) {
712 			exit(1);
713 		}
714 	}
715 	else {
716 
717 		/* Read the last tracked LSN from the last file */
718 		lsn_t	last_tracked_lsn;
719 		lsn_t	file_start_lsn;
720 
721 		log_bmp_sys->out.size
722 			= os_file_get_size(log_bmp_sys->out.file);
723 		log_bmp_sys->out.offset	= log_bmp_sys->out.size;
724 
725 		if (log_bmp_sys->out.offset % MODIFIED_PAGE_BLOCK_SIZE != 0) {
726 
727 			ib_logf(IB_LOG_LEVEL_WARN,
728 				"truncated block detected in \'%s\' at offset "
729 				UINT64PF,
730 				log_bmp_sys->out.name,
731 				log_bmp_sys->out.offset);
732 			log_bmp_sys->out.offset -=
733 				log_bmp_sys->out.offset
734 				% MODIFIED_PAGE_BLOCK_SIZE;
735 		}
736 
737 		last_tracked_lsn = log_online_read_last_tracked_lsn();
738 		/* Do not rotate if we truncated the file to zero length - we
739 		can just start writing there */
740 		const bool need_rotate = (last_tracked_lsn != 0);
741 		if (!last_tracked_lsn) {
742 
743 			last_tracked_lsn = last_file_start_lsn;
744 		}
745 
746 		/* Start a new file.  Choose the LSN value in its name based on
747 		if we can retrack any missing data. */
748 		if (log_online_can_track_missing(last_tracked_lsn,
749 						 tracking_start_lsn)) {
750 			file_start_lsn = last_tracked_lsn;
751 		} else {
752 			file_start_lsn = tracking_start_lsn;
753 		}
754 
755 		if (need_rotate
756 		    && !log_online_rotate_bitmap_file(file_start_lsn)) {
757 
758 			exit(1);
759 		}
760 
761 		if (last_tracked_lsn < tracking_start_lsn) {
762 
763 			log_online_track_missing_on_startup
764 				(last_tracked_lsn, tracking_start_lsn);
765 			return;
766 		}
767 
768 		if (last_tracked_lsn > tracking_start_lsn) {
769 
770 			ib_logf(IB_LOG_LEVEL_WARN,
771 				"last tracked LSN is " LSN_PF ", but the last "
772 				"checkpoint LSN is " LSN_PF ". The "
773 				"tracking-based incremental backups will work "
774 				"only from the latter LSN!",
775 				last_tracked_lsn, tracking_start_lsn);
776 		}
777 
778 	}
779 
780 	ib_logf(IB_LOG_LEVEL_INFO, "starting tracking changed pages from LSN "
781 		LSN_PF, tracking_start_lsn);
782 	log_bmp_sys->start_lsn = tracking_start_lsn;
783 	log_set_tracked_lsn(tracking_start_lsn);
784 }
785 
786 /** Shut down the dynamic part of the log tracking subsystem */
787 UNIV_INTERN
788 void
log_online_read_shutdown(void)789 log_online_read_shutdown(void)
790 {
791 	mutex_enter(&log_bmp_sys_mutex);
792 
793 	srv_track_changed_pages = FALSE;
794 
795 	ib_rbt_node_t *free_list_node = log_bmp_sys->page_free_list;
796 
797 	if (!os_file_is_invalid(log_bmp_sys->out.file)) {
798 		os_file_close(log_bmp_sys->out.file);
799 		os_file_mark_invalid(&log_bmp_sys->out.file);
800 	}
801 
802 	rbt_free(log_bmp_sys->modified_pages);
803 
804 	while (free_list_node) {
805 		ib_rbt_node_t *next = free_list_node->left;
806 		ut_free(free_list_node);
807 		free_list_node = next;
808 	}
809 
810 	ut_free(log_bmp_sys->read_buf_ptr);
811 	ut_free(log_bmp_sys);
812 	log_bmp_sys = NULL;
813 
814 	srv_redo_log_thread_started = false;
815 
816 	mutex_exit(&log_bmp_sys_mutex);
817 }
818 
819 /** Shut down the constant part of the log tracking subsystem */
820 UNIV_INTERN
821 void
log_online_shutdown(void)822 log_online_shutdown(void)
823 {
824 	mutex_free(&log_bmp_sys_mutex);
825 }
826 
827 /*********************************************************************//**
828 For the given minilog record type determine if the record has (space; page)
829 associated with it.
830 @return TRUE if the record has (space; page) in it */
831 static
832 ibool
log_online_rec_has_page(byte type)833 log_online_rec_has_page(
834 /*====================*/
835 	byte	type)	/*!<in: the minilog record type */
836 {
837 	return type != MLOG_MULTI_REC_END && type != MLOG_DUMMY_RECORD;
838 }
839 
840 /*********************************************************************//**
841 Check if a page field for a given log record type actually contains a page
842 id. It does not for file operations and MLOG_LSN.
843 @return TRUE if page field contains actual page id, FALSE otherwise */
844 static
845 ibool
log_online_rec_page_means_page(byte type)846 log_online_rec_page_means_page(
847 /*===========================*/
848 	byte	type)	/*!<in: log record type */
849 {
850 	return log_online_rec_has_page(type)
851 #ifdef UNIV_LOG_LSN_DEBUG
852 		&& type != MLOG_LSN
853 #endif
854 		&& type != MLOG_FILE_CREATE
855 		&& type != MLOG_FILE_RENAME
856 		&& type != MLOG_FILE_DELETE
857 		&& type != MLOG_FILE_CREATE2;
858 }
859 
860 /*********************************************************************//**
861 Parse the log data in the parse buffer for the (space, page) pairs and add
862 them to the modified page set as necessary.  Removes the fully-parsed records
863 from the buffer.  If an incomplete record is found, moves it to the end of the
864 buffer. */
865 static
866 void
log_online_parse_redo_log(void)867 log_online_parse_redo_log(void)
868 /*===========================*/
869 {
870 	ut_ad(mutex_own(&log_bmp_sys_mutex));
871 
872 	byte *ptr = log_bmp_sys->parse_buf;
873 	byte *end = log_bmp_sys->parse_buf_end;
874 	ulint len = 0;
875 
876 	while (ptr != end
877 	       && log_bmp_sys->next_parse_lsn < log_bmp_sys->end_lsn) {
878 
879 		byte	type;
880 		ulint	space;
881 		ulint	page_no;
882 		byte*	body;
883 
884 		/* recv_sys is not initialized, so on corrupt log we will
885 		SIGSEGV.  But the log of a live database should not be
886 		corrupt. */
887 		len = recv_parse_log_rec(ptr, end, &type, &space, &page_no,
888 					 &body);
889 		if (len > 0) {
890 
891 			if (log_online_rec_page_means_page(type)) {
892 
893 				ut_a(len >= 3);
894 				log_online_set_page_bit(space, page_no);
895 			}
896 
897 			ptr += len;
898 			ut_ad(ptr <= end);
899 			log_bmp_sys->next_parse_lsn
900 			    = recv_calc_lsn_on_data_add
901 				(log_bmp_sys->next_parse_lsn, len);
902 		}
903 		else {
904 
905 			/* Incomplete log record.  Shift it to the
906 			beginning of the parse buffer and leave it to be
907 			completed on the next read.  */
908 			ut_memmove(log_bmp_sys->parse_buf, ptr, end - ptr);
909 			log_bmp_sys->parse_buf_end
910 				= log_bmp_sys->parse_buf + (end - ptr);
911 			ptr = end;
912 		}
913 	}
914 
915 	if (len > 0) {
916 
917 		log_bmp_sys->parse_buf_end = log_bmp_sys->parse_buf;
918 	}
919 }
920 
921 /*********************************************************************//**
922 Check the log block checksum.
923 @return TRUE if the log block checksum is OK, FALSE otherwise.  */
924 static
925 ibool
log_online_is_valid_log_seg(const byte * log_block)926 log_online_is_valid_log_seg(
927 /*========================*/
928 	const byte* log_block)	/*!< in: read log data */
929 {
930 	ibool checksum_is_ok
931 		= log_block_checksum_is_ok_or_old_format(log_block);
932 
933 	if (!checksum_is_ok) {
934 
935 		ib_logf(IB_LOG_LEVEL_ERROR,
936 			"log block checksum mismatch: expected " ULINTPF ", "
937 			"calculated checksum " ULINTPF,
938 			log_block_get_checksum(log_block),
939 			log_block_calc_checksum(log_block));
940 	}
941 
942 	return checksum_is_ok;
943 }
944 
945 /*********************************************************************//**
946 Copy new log data to the parse buffer while skipping log block header,
947 trailer and already parsed data.  */
948 static
949 void
log_online_add_to_parse_buf(const byte * log_block,ulint data_len,ulint skip_len)950 log_online_add_to_parse_buf(
951 /*========================*/
952 	const byte*	log_block,	/*!< in: read log data */
953 	ulint		data_len,	/*!< in: length of read log data */
954 	ulint		skip_len)	/*!< in: how much of log data to
955 					skip */
956 {
957 	ut_ad(mutex_own(&log_bmp_sys_mutex));
958 
959 	ulint start_offset = skip_len ? skip_len : LOG_BLOCK_HDR_SIZE;
960 	ulint end_offset
961 		= (data_len == OS_FILE_LOG_BLOCK_SIZE)
962 		? data_len - LOG_BLOCK_TRL_SIZE
963 		: data_len;
964 	ulint actual_data_len = (end_offset >= start_offset)
965 		? end_offset - start_offset : 0;
966 
967 	ut_memcpy(log_bmp_sys->parse_buf_end, log_block + start_offset,
968 		  actual_data_len);
969 
970 	log_bmp_sys->parse_buf_end += actual_data_len;
971 
972 	ut_a(log_bmp_sys->parse_buf_end - log_bmp_sys->parse_buf
973 	     <= RECV_PARSING_BUF_SIZE);
974 }
975 
976 /*********************************************************************//**
977 Parse the log block: first copies the read log data to the parse buffer while
978 skipping log block header, trailer and already parsed data.  Then it actually
979 parses the log to add to the modified page bitmap. */
980 static
981 void
log_online_parse_redo_log_block(const byte * log_block,ulint skip_already_parsed_len)982 log_online_parse_redo_log_block(
983 /*============================*/
984 	const byte*	log_block,		  /*!< in: read log data */
985 	ulint		skip_already_parsed_len)  /*!< in: how many bytes of
986 						  log data should be skipped as
987 						  they were parsed before */
988 {
989 	ut_ad(mutex_own(&log_bmp_sys_mutex));
990 
991 	ulint block_data_len = log_block_get_data_len(log_block);
992 
993 	ut_ad(block_data_len % OS_FILE_LOG_BLOCK_SIZE == 0
994 	      || block_data_len < OS_FILE_LOG_BLOCK_SIZE);
995 
996 	log_online_add_to_parse_buf(log_block, block_data_len,
997 				    skip_already_parsed_len);
998 	log_online_parse_redo_log();
999 }
1000 
1001 /*********************************************************************//**
1002 Read and parse one redo log chunk and updates the modified page bitmap. */
1003 static
1004 void
log_online_follow_log_seg(log_group_t * group,lsn_t block_start_lsn,lsn_t block_end_lsn)1005 log_online_follow_log_seg(
1006 /*======================*/
1007 	log_group_t*	group,		       /*!< in: the log group to use */
1008 	lsn_t		block_start_lsn,       /*!< in: the LSN to read from */
1009 	lsn_t		block_end_lsn)	       /*!< in: the LSN to read to */
1010 {
1011 	ut_ad(mutex_own(&log_bmp_sys_mutex));
1012 
1013 	/* Pointer to the current OS_FILE_LOG_BLOCK-sized chunk of the read log
1014 	data to parse */
1015 	byte* log_block = log_bmp_sys->read_buf;
1016 	byte* log_block_end = log_bmp_sys->read_buf
1017 		+ (block_end_lsn - block_start_lsn);
1018 
1019 	mutex_enter(&log_sys->mutex);
1020 	log_group_read_log_seg(LOG_RECOVER, log_bmp_sys->read_buf,
1021 			       group, block_start_lsn, block_end_lsn, TRUE);
1022 	/* log_group_read_log_seg will release the log_sys->mutex for us */
1023 
1024 	while (log_block < log_block_end
1025 	       && log_bmp_sys->next_parse_lsn < log_bmp_sys->end_lsn) {
1026 
1027 		/* How many bytes of log data should we skip in the current log
1028 		block.  Skipping is necessary because we round down the next
1029 		parse LSN thus it is possible to read the already-processed log
1030 		data many times */
1031 		ulint skip_already_parsed_len = 0;
1032 
1033 		if (!log_online_is_valid_log_seg(log_block)) {
1034 			break;
1035 		}
1036 
1037 		if ((block_start_lsn <= log_bmp_sys->next_parse_lsn)
1038 		    && (block_start_lsn + OS_FILE_LOG_BLOCK_SIZE
1039 			> log_bmp_sys->next_parse_lsn)) {
1040 
1041 			/* The next parse LSN is inside the current block, skip
1042 			data preceding it. */
1043 			skip_already_parsed_len
1044 				= (ulint)(log_bmp_sys->next_parse_lsn
1045 					  - block_start_lsn);
1046 		}
1047 		else {
1048 
1049 			/* If the next parse LSN is not inside the current
1050 			block, then the only option is that we have processed
1051 			ahead already. */
1052 			ut_a(block_start_lsn > log_bmp_sys->next_parse_lsn);
1053 		}
1054 
1055 		/* TODO: merge the copying to the parse buf code with
1056 		skip_already_len calculations */
1057 		log_online_parse_redo_log_block(log_block,
1058 						skip_already_parsed_len);
1059 
1060 		log_block += OS_FILE_LOG_BLOCK_SIZE;
1061 		block_start_lsn += OS_FILE_LOG_BLOCK_SIZE;
1062 	}
1063 
1064 	return;
1065 }
1066 
1067 /*********************************************************************//**
1068 Read and parse the redo log in a given group in FOLLOW_SCAN_SIZE-sized
1069 chunks and updates the modified page bitmap. */
1070 static
1071 void
log_online_follow_log_group(log_group_t * group,lsn_t contiguous_lsn)1072 log_online_follow_log_group(
1073 /*========================*/
1074 	log_group_t*	group,		/*!< in: the log group to use */
1075 	lsn_t		contiguous_lsn)	/*!< in: the LSN of log block start
1076 					containing the log_parse_start_lsn */
1077 {
1078 	ut_ad(mutex_own(&log_bmp_sys_mutex));
1079 
1080 	lsn_t	block_start_lsn = contiguous_lsn;
1081 	lsn_t	block_end_lsn;
1082 
1083 	log_bmp_sys->next_parse_lsn = log_bmp_sys->start_lsn;
1084 	log_bmp_sys->parse_buf_end = log_bmp_sys->parse_buf;
1085 
1086 	do {
1087 		block_end_lsn = block_start_lsn + FOLLOW_SCAN_SIZE;
1088 
1089 		log_online_follow_log_seg(group, block_start_lsn,
1090 					  block_end_lsn);
1091 
1092 		/* Next parse LSN can become higher than the last read LSN
1093 		only in the case when the read LSN falls right on the block
1094 		boundary, in which case next parse lsn is bumped to the actual
1095 		data LSN on the next (not yet read) block.  This assert is
1096 		slightly conservative.  */
1097 		ut_a(log_bmp_sys->next_parse_lsn
1098 		     <= block_end_lsn + LOG_BLOCK_HDR_SIZE
1099 		     + LOG_BLOCK_TRL_SIZE);
1100 
1101 		block_start_lsn = block_end_lsn;
1102 	} while (block_end_lsn < log_bmp_sys->end_lsn);
1103 
1104 	/* Assert that the last read log record is a full one */
1105 	ut_a(log_bmp_sys->parse_buf_end == log_bmp_sys->parse_buf);
1106 }
1107 
1108 /*********************************************************************//**
1109 Write, flush one bitmap block to disk and advance the output position if
1110 successful.
1111 
1112 @return TRUE if page written OK, FALSE if I/O error */
1113 static
1114 ibool
log_online_write_bitmap_page(const byte * block)1115 log_online_write_bitmap_page(
1116 /*=========================*/
1117 	const byte *block)	/*!< in: block to write */
1118 {
1119 	ut_ad(mutex_own(&log_bmp_sys_mutex));
1120 
1121 	/* Simulate a write error */
1122 	DBUG_EXECUTE_IF("bitmap_page_write_error",
1123 			{
1124 				ulint space_id
1125 					= mach_read_from_4(block
1126 					+ MODIFIED_PAGE_SPACE_ID);
1127 				if (space_id > 0) {
1128 					ib_logf(IB_LOG_LEVEL_ERROR,
1129 						"simulating bitmap write "
1130 						"error in "
1131 						"log_online_write_bitmap_page "
1132 						"for space ID %lu",
1133 						space_id);
1134 					return FALSE;
1135 				}
1136 			});
1137 
1138 	/* A crash injection site that ensures last checkpoint LSN > last
1139 	tracked LSN, so that LSN tracking for this interval is tested. */
1140 	DBUG_EXECUTE_IF("crash_before_bitmap_write",
1141 			{
1142 				ulint space_id
1143 					= mach_read_from_4(block
1144 						+ MODIFIED_PAGE_SPACE_ID);
1145 				if (space_id > 0)
1146 					DBUG_SUICIDE();
1147 			});
1148 
1149 
1150 	ibool success = os_file_write(log_bmp_sys->out.name,
1151 				log_bmp_sys->out.file, block,
1152 				log_bmp_sys->out.offset,
1153 				MODIFIED_PAGE_BLOCK_SIZE);
1154 	if (UNIV_UNLIKELY(!success)) {
1155 
1156 		/* The following call prints an error message */
1157 		os_file_get_last_error(TRUE);
1158 		ib_logf(IB_LOG_LEVEL_ERROR, "failed writing changed page "
1159 			"bitmap file \'%s\'", log_bmp_sys->out.name);
1160 		return FALSE;
1161 	}
1162 
1163 	success = os_file_flush(log_bmp_sys->out.file);
1164 	if (UNIV_UNLIKELY(!success)) {
1165 
1166 		/* The following call prints an error message */
1167 		os_file_get_last_error(TRUE);
1168 		ib_logf(IB_LOG_LEVEL_ERROR, "failed flushing changed page "
1169 			"bitmap file \'%s\'",	log_bmp_sys->out.name);
1170 		return FALSE;
1171 	}
1172 
1173 	os_file_advise(log_bmp_sys->out.file, log_bmp_sys->out.offset,
1174 		       MODIFIED_PAGE_BLOCK_SIZE, OS_FILE_ADVISE_DONTNEED);
1175 
1176 	log_bmp_sys->out.offset += MODIFIED_PAGE_BLOCK_SIZE;
1177 	return TRUE;
1178 }
1179 
1180 /*********************************************************************//**
1181 Append the current changed page bitmap to the bitmap file.  Clears the
1182 bitmap tree and recycles its nodes to the free list.
1183 
1184 @return TRUE if bitmap written OK, FALSE if I/O error*/
1185 static
1186 ibool
log_online_write_bitmap(void)1187 log_online_write_bitmap(void)
1188 /*=========================*/
1189 {
1190 	ut_ad(mutex_own(&log_bmp_sys_mutex));
1191 
1192 	if (log_bmp_sys->out.offset >= srv_max_bitmap_file_size) {
1193 		if (!log_online_rotate_bitmap_file(log_bmp_sys->start_lsn)) {
1194 			return FALSE;
1195 		}
1196 	}
1197 
1198 	ib_rbt_node_t *bmp_tree_node
1199 		= (ib_rbt_node_t *)rbt_first(log_bmp_sys->modified_pages);
1200 	const ib_rbt_node_t * const last_bmp_tree_node
1201 		= rbt_last(log_bmp_sys->modified_pages);
1202 
1203 	ibool success = TRUE;
1204 
1205 	while (bmp_tree_node) {
1206 
1207 		byte *page = rbt_value(byte, bmp_tree_node);
1208 
1209 		/* In case of a bitmap page write error keep on looping over
1210 		the tree to reclaim its memory through the free list instead of
1211 		returning immediatelly. */
1212 		if (UNIV_LIKELY(success)) {
1213 			if (bmp_tree_node == last_bmp_tree_node) {
1214 				mach_write_to_4(page
1215 						+ MODIFIED_PAGE_IS_LAST_BLOCK,
1216 						1);
1217 			}
1218 
1219 			mach_write_to_8(page + MODIFIED_PAGE_START_LSN,
1220 				       log_bmp_sys->start_lsn);
1221 			mach_write_to_8(page + MODIFIED_PAGE_END_LSN,
1222 				       log_bmp_sys->end_lsn);
1223 			mach_write_to_4(page + MODIFIED_PAGE_BLOCK_CHECKSUM,
1224 					log_online_calc_checksum(page));
1225 
1226 			success = log_online_write_bitmap_page(page);
1227 		}
1228 
1229 		bmp_tree_node->left = log_bmp_sys->page_free_list;
1230 		log_bmp_sys->page_free_list = bmp_tree_node;
1231 
1232 		bmp_tree_node = (ib_rbt_node_t*)
1233 			rbt_next(log_bmp_sys->modified_pages, bmp_tree_node);
1234 
1235 		DBUG_EXECUTE_IF("bitmap_page_2_write_error",
1236 				if (bmp_tree_node)
1237 				{
1238 					DBUG_SET("+d,bitmap_page_write_error");
1239 					DBUG_SET("-d,bitmap_page_2_write_error");
1240 				});
1241 	}
1242 
1243 	rbt_reset(log_bmp_sys->modified_pages);
1244 	return success;
1245 }
1246 
1247 /*********************************************************************//**
1248 Read and parse the redo log up to last checkpoint LSN to build the changed
1249 page bitmap which is then written to disk.
1250 
1251 @return TRUE if log tracking succeeded, FALSE if bitmap write I/O error */
1252 UNIV_INTERN
1253 ibool
log_online_follow_redo_log(void)1254 log_online_follow_redo_log(void)
1255 /*============================*/
1256 {
1257 	lsn_t		contiguous_start_lsn;
1258 	log_group_t*	group;
1259 	ibool		result;
1260 
1261 	ut_ad(!srv_read_only_mode);
1262 
1263 	if (!srv_track_changed_pages)
1264 		return TRUE;
1265 
1266 	DEBUG_SYNC_C("log_online_follow_redo_log");
1267 
1268 	mutex_enter(&log_bmp_sys_mutex);
1269 
1270 	if (!srv_track_changed_pages) {
1271 		mutex_exit(&log_bmp_sys_mutex);
1272 		return TRUE;
1273 	}
1274 
1275 	/* Grab the LSN of the last checkpoint, we will parse up to it */
1276 	mutex_enter(&(log_sys->mutex));
1277 	log_bmp_sys->end_lsn = log_sys->last_checkpoint_lsn;
1278 	mutex_exit(&(log_sys->mutex));
1279 
1280 	if (log_bmp_sys->end_lsn == log_bmp_sys->start_lsn) {
1281 		mutex_exit(&log_bmp_sys_mutex);
1282 		return TRUE;
1283 	}
1284 
1285 	group = UT_LIST_GET_FIRST(log_sys->log_groups);
1286 	ut_a(group);
1287 
1288 	contiguous_start_lsn = ut_uint64_align_down(log_bmp_sys->start_lsn,
1289 						    OS_FILE_LOG_BLOCK_SIZE);
1290 
1291 	while (group) {
1292 		log_online_follow_log_group(group, contiguous_start_lsn);
1293 		group = UT_LIST_GET_NEXT(log_groups, group);
1294 	}
1295 
1296 	result = log_online_write_bitmap();
1297 	log_bmp_sys->start_lsn = log_bmp_sys->end_lsn;
1298 	log_set_tracked_lsn(log_bmp_sys->start_lsn);
1299 
1300 	mutex_exit(&log_bmp_sys_mutex);
1301 	return result;
1302 }
1303 
1304 /*********************************************************************//**
1305 Diagnose a bitmap file range setup failure and free the partially-initialized
1306 bitmap file range.  */
1307 UNIV_COLD
1308 static
1309 void
log_online_diagnose_inconsistent_dir(log_online_bitmap_file_range_t * bitmap_files)1310 log_online_diagnose_inconsistent_dir(
1311 /*=================================*/
1312 	log_online_bitmap_file_range_t	*bitmap_files)	/*!<in/out: bitmap file
1313 							range */
1314 {
1315 	ib_logf(IB_LOG_LEVEL_WARN,
1316 		"InnoDB: Warning: inconsistent bitmap file "
1317 		"directory for a "
1318 		"INFORMATION_SCHEMA.INNODB_CHANGED_PAGES query");
1319 	free(bitmap_files->files);
1320 }
1321 
1322 /*********************************************************************//**
1323 List the bitmap files in srv_data_home and setup their range that contains the
1324 specified LSN interval.  This range, if non-empty, will start with a file that
1325 has the greatest LSN equal to or less than the start LSN and will include all
1326 the files up to the one with the greatest LSN less than the end LSN.  Caller
1327 must free bitmap_files->files when done if bitmap_files set to non-NULL and
1328 this function returned TRUE.  Field bitmap_files->count might be set to a
1329 larger value than the actual count of the files, and space for the unused array
1330 slots will be allocated but cleared to zeroes.
1331 
1332 @return TRUE if succeeded
1333 */
1334 static
1335 ibool
log_online_setup_bitmap_file_range(log_online_bitmap_file_range_t * bitmap_files,lsn_t range_start,lsn_t range_end)1336 log_online_setup_bitmap_file_range(
1337 /*===============================*/
1338 	log_online_bitmap_file_range_t	*bitmap_files,	/*!<in/out: bitmap file
1339 							range */
1340 	lsn_t				range_start,	/*!<in: start LSN */
1341 	lsn_t				range_end)	/*!<in: end LSN */
1342 {
1343 	os_file_dir_t	bitmap_dir;
1344 	os_file_stat_t	bitmap_dir_file_info;
1345 	ulong		first_file_seq_num	= ULONG_MAX;
1346 	ulong		last_file_seq_num	= 0;
1347 	lsn_t		first_file_start_lsn	= LSN_MAX;
1348 
1349 	ut_ad(range_end >= range_start);
1350 
1351 	bitmap_files->count = 0;
1352 	bitmap_files->files = NULL;
1353 
1354 	/* 1st pass: size the info array */
1355 
1356 	bitmap_dir = os_file_opendir(srv_data_home, FALSE);
1357 	if (UNIV_UNLIKELY(!bitmap_dir)) {
1358 
1359 		ib_logf(IB_LOG_LEVEL_ERROR,
1360 			"failed to open bitmap directory \'%s\'",
1361 			srv_data_home);
1362 		return FALSE;
1363 	}
1364 
1365 	while (!os_file_readdir_next_file(srv_data_home, bitmap_dir,
1366 					  &bitmap_dir_file_info)) {
1367 
1368 		ulong	file_seq_num;
1369 		lsn_t	file_start_lsn;
1370 
1371 		if (!log_online_is_bitmap_file(&bitmap_dir_file_info,
1372 					       &file_seq_num,
1373 					       &file_start_lsn)
1374 		    || file_start_lsn >= range_end) {
1375 
1376 			continue;
1377 		}
1378 
1379 		if (file_seq_num > last_file_seq_num) {
1380 
1381 			last_file_seq_num = file_seq_num;
1382 		}
1383 
1384 		if (file_start_lsn >= range_start
1385 		    || file_start_lsn == first_file_start_lsn
1386 		    || first_file_start_lsn > range_start) {
1387 
1388 			/* A file that falls into the range */
1389 
1390 			if (file_start_lsn < first_file_start_lsn) {
1391 
1392 				first_file_start_lsn = file_start_lsn;
1393 			}
1394 			if (file_seq_num < first_file_seq_num) {
1395 
1396 				first_file_seq_num = file_seq_num;
1397 			}
1398 		} else if (file_start_lsn > first_file_start_lsn) {
1399 
1400 			/* A file that has LSN closer to the range start
1401 			but smaller than it, replacing another such file */
1402 			first_file_start_lsn = file_start_lsn;
1403 			first_file_seq_num = file_seq_num;
1404 		}
1405 	}
1406 
1407 	if (UNIV_UNLIKELY(os_file_closedir(bitmap_dir))) {
1408 
1409 		os_file_get_last_error(TRUE);
1410 		ib_logf(IB_LOG_LEVEL_ERROR, "cannot close \'%s\'",
1411 			srv_data_home);
1412 		return FALSE;
1413 	}
1414 
1415 	if (first_file_seq_num == ULONG_MAX && last_file_seq_num == 0) {
1416 
1417 		bitmap_files->count = 0;
1418 		return TRUE;
1419 	}
1420 
1421 	bitmap_files->count = last_file_seq_num - first_file_seq_num + 1;
1422 
1423 	DEBUG_SYNC_C("setup_bitmap_range_middle");
1424 
1425 	/* 2nd pass: get the file names in the file_seq_num order */
1426 
1427 	bitmap_dir = os_file_opendir(srv_data_home, FALSE);
1428 	if (UNIV_UNLIKELY(!bitmap_dir)) {
1429 
1430 		ib_logf(IB_LOG_LEVEL_ERROR,
1431 			"failed to open bitmap directory \'%s\'",
1432 			srv_data_home);
1433 		return FALSE;
1434 	}
1435 
1436 	bitmap_files->files
1437 		= static_cast<log_online_bitmap_file_range_struct::files_t *>
1438 		(ut_malloc(bitmap_files->count
1439 			   * sizeof(bitmap_files->files[0])));
1440 	memset(bitmap_files->files, 0,
1441 	       bitmap_files->count * sizeof(bitmap_files->files[0]));
1442 
1443 	while (!os_file_readdir_next_file(srv_data_home, bitmap_dir,
1444 					  &bitmap_dir_file_info)) {
1445 
1446 		ulong	file_seq_num;
1447 		lsn_t	file_start_lsn;
1448 		size_t	array_pos;
1449 
1450 		if (!log_online_is_bitmap_file(&bitmap_dir_file_info,
1451 					       &file_seq_num,
1452 					       &file_start_lsn)
1453 		    || file_start_lsn >= range_end
1454 		    || file_start_lsn < first_file_start_lsn) {
1455 
1456 			continue;
1457 		}
1458 
1459 		array_pos = file_seq_num - first_file_seq_num;
1460 		if (UNIV_UNLIKELY(array_pos >= bitmap_files->count)) {
1461 
1462 			log_online_diagnose_inconsistent_dir(bitmap_files);
1463 			return FALSE;
1464 		}
1465 
1466 
1467 		if (file_seq_num > bitmap_files->files[array_pos].seq_num) {
1468 
1469 			bitmap_files->files[array_pos].seq_num = file_seq_num;
1470 			memcpy(bitmap_files->files[array_pos].name,
1471 				bitmap_dir_file_info.name, FN_REFLEN);
1472 			bitmap_files->files[array_pos].name[FN_REFLEN - 1]
1473 				= '\0';
1474 			bitmap_files->files[array_pos].start_lsn
1475 				= file_start_lsn;
1476 		}
1477 	}
1478 
1479 	if (UNIV_UNLIKELY(os_file_closedir(bitmap_dir))) {
1480 
1481 		os_file_get_last_error(TRUE);
1482 		ib_logf(IB_LOG_LEVEL_ERROR, "cannot close \'%s\'",
1483 			srv_data_home);
1484 		free(bitmap_files->files);
1485 		return FALSE;
1486 	}
1487 
1488 	if (!bitmap_files->files[0].seq_num
1489 	    || bitmap_files->files[0].seq_num != first_file_seq_num) {
1490 
1491 		log_online_diagnose_inconsistent_dir(bitmap_files);
1492 		return FALSE;
1493 	}
1494 
1495 	{
1496 		size_t i;
1497 		for (i = 1; i < bitmap_files->count; i++) {
1498 			if (!bitmap_files->files[i].seq_num) {
1499 				break;
1500 			}
1501 			if ((bitmap_files->files[i].seq_num
1502 			      <= bitmap_files->files[i - 1].seq_num)
1503 			    || (bitmap_files->files[i].start_lsn
1504 				< bitmap_files->files[i - 1].start_lsn)) {
1505 
1506 				log_online_diagnose_inconsistent_dir(
1507 								bitmap_files);
1508 				return FALSE;
1509 			}
1510 		}
1511 	}
1512 
1513 	return TRUE;
1514 }
1515 
1516 /****************************************************************//**
1517 Open a bitmap file for reading.
1518 
1519 @return TRUE if opened successfully */
1520 static
1521 ibool
log_online_open_bitmap_file_read_only(const char * name,log_online_bitmap_file_t * bitmap_file)1522 log_online_open_bitmap_file_read_only(
1523 /*==================================*/
1524 	const char*			name,		/*!<in: bitmap file
1525 							name without directory,
1526 							which is assumed to be
1527 							srv_data_home */
1528 	log_online_bitmap_file_t*	bitmap_file)	/*!<out: opened bitmap
1529 							file */
1530 {
1531 	ibool	success	= FALSE;
1532 	size_t  srv_data_home_len;
1533 
1534 	ut_ad(name[0] != '\0');
1535 
1536 	srv_data_home_len = strlen(srv_data_home);
1537 	if (srv_data_home_len
1538 			&& srv_data_home[srv_data_home_len-1]
1539 			!= SRV_PATH_SEPARATOR) {
1540 		ut_snprintf(bitmap_file->name, sizeof(bitmap_file->name), "%s%c%s",
1541 				srv_data_home, SRV_PATH_SEPARATOR, name);
1542 	} else {
1543 		ut_snprintf(bitmap_file->name, sizeof(bitmap_file->name), "%s%s",
1544 				srv_data_home, name);
1545 	}
1546 	bitmap_file->file
1547 		= os_file_create_simple_no_error_handling(innodb_file_bmp_key,
1548 							  bitmap_file->name,
1549 							  OS_FILE_OPEN,
1550 							  OS_FILE_READ_ONLY,
1551 							  &success);
1552 	if (UNIV_UNLIKELY(!success)) {
1553 
1554 		/* Here and below assume that bitmap file names do not
1555 		contain apostrophes, thus no need for ut_print_filename(). */
1556 		ib_logf(IB_LOG_LEVEL_WARN,
1557 			"error opening the changed page bitmap \'%s\'",
1558 			bitmap_file->name);
1559 		return FALSE;
1560 	}
1561 
1562 	bitmap_file->size = os_file_get_size(bitmap_file->file);
1563 	bitmap_file->offset = 0;
1564 
1565 	os_file_advise(bitmap_file->file, 0, 0, OS_FILE_ADVISE_SEQUENTIAL);
1566 	os_file_advise(bitmap_file->file, 0, 0, OS_FILE_ADVISE_NOREUSE);
1567 
1568 	return TRUE;
1569 }
1570 
1571 /****************************************************************//**
1572 Diagnose one or both of the following situations if we read close to
1573 the end of bitmap file:
1574 1) Warn if the remainder of the file is less than one page.
1575 2) Error if we cannot read any more full pages but the last read page
1576 did not have the last-in-run flag set.
1577 
1578 @return FALSE for the error */
1579 static
1580 ibool
log_online_diagnose_bitmap_eof(const log_online_bitmap_file_t * bitmap_file,ibool last_page_in_run)1581 log_online_diagnose_bitmap_eof(
1582 /*===========================*/
1583 	const log_online_bitmap_file_t*	bitmap_file,	/*!< in: bitmap file */
1584 	ibool				last_page_in_run)/*!< in: "last page in
1585 							run" flag value in the
1586 							last read page */
1587 {
1588 	/* Check if we are too close to EOF to read a full page */
1589 	if ((bitmap_file->size < MODIFIED_PAGE_BLOCK_SIZE)
1590 	    || (bitmap_file->offset
1591 		> bitmap_file->size - MODIFIED_PAGE_BLOCK_SIZE)) {
1592 
1593 		if (UNIV_UNLIKELY(bitmap_file->offset != bitmap_file->size)) {
1594 
1595 			/* If we are not at EOF and we have less than one page
1596 			to read, it's junk.  This error is not fatal in
1597 			itself. */
1598 
1599 			ib_logf(IB_LOG_LEVEL_WARN,
1600 				"junk at the end of changed page bitmap file "
1601 				"\'%s\'.", bitmap_file->name);
1602 		}
1603 
1604 		if (UNIV_UNLIKELY(!last_page_in_run)) {
1605 
1606 			/* We are at EOF but the last read page did not finish
1607 			a run */
1608 			/* It's a "Warning" here because it's not a fatal error
1609 			for the whole server */
1610 			ib_logf(IB_LOG_LEVEL_WARN,
1611 				"changed page bitmap file \'%s\', size "
1612 				UINT64PF " bytes, does not "
1613 				"contain a complete run at the next read "
1614 				"offset " UINT64PF,
1615 				bitmap_file->name, bitmap_file->size,
1616 				bitmap_file->offset);
1617 			return FALSE;
1618 		}
1619 	}
1620 	return TRUE;
1621 }
1622 
1623 /*********************************************************************//**
1624 Initialize the log bitmap iterator for a given range.  The records are
1625 processed at a bitmap block granularity, i.e. all the records in the same block
1626 share the same start and end LSN values, the exact LSN of each record is
1627 unavailable (nor is it defined for blocks that are touched more than once in
1628 the LSN interval contained in the block).  Thus min_lsn and max_lsn should be
1629 set at block boundaries or bigger, otherwise the records at the 1st and the
1630 last blocks will not be returned.  Also note that there might be returned
1631 records with LSN < min_lsn, as min_lsn is used to select the correct starting
1632 file but not block.
1633 
1634 @return TRUE if the iterator is initialized OK, FALSE otherwise. */
1635 UNIV_INTERN
1636 ibool
log_online_bitmap_iterator_init(log_bitmap_iterator_t * i,lsn_t min_lsn,lsn_t max_lsn)1637 log_online_bitmap_iterator_init(
1638 /*============================*/
1639 	log_bitmap_iterator_t	*i,	/*!<in/out:  iterator */
1640 	lsn_t			min_lsn,/*!< in: start LSN */
1641 	lsn_t			max_lsn)/*!< in: end LSN */
1642 {
1643 	ut_a(i);
1644 
1645 	i->max_lsn = max_lsn;
1646 
1647 	if (UNIV_UNLIKELY(min_lsn > max_lsn)) {
1648 
1649 		/* Empty range */
1650 		i->in_files.count = 0;
1651 		i->in_files.files = NULL;
1652 		os_file_mark_invalid(&i->in.file);
1653 		i->page = NULL;
1654 		i->failed = FALSE;
1655 		return TRUE;
1656 	}
1657 
1658 	if (!log_online_setup_bitmap_file_range(&i->in_files, min_lsn,
1659 		max_lsn)) {
1660 
1661 		i->failed = TRUE;
1662 		return FALSE;
1663 	}
1664 
1665 	i->in_i = 0;
1666 
1667 	if (i->in_files.count == 0) {
1668 
1669 		/* Empty range */
1670 		os_file_mark_invalid(&i->in.file);
1671 		i->page = NULL;
1672 		i->failed = FALSE;
1673 		return TRUE;
1674 	}
1675 
1676 	/* Open the 1st bitmap file */
1677 	if (UNIV_UNLIKELY(!log_online_open_bitmap_file_read_only(
1678 				i->in_files.files[i->in_i].name,
1679 				&i->in))) {
1680 
1681 		i->in_i = i->in_files.count;
1682 		free(i->in_files.files);
1683 		i->failed = TRUE;
1684 		return FALSE;
1685 	}
1686 
1687 	i->page = static_cast<byte *>(ut_malloc(MODIFIED_PAGE_BLOCK_SIZE));
1688 	i->bit_offset = MODIFIED_PAGE_BLOCK_BITMAP_LEN;
1689 	i->start_lsn = i->end_lsn = 0;
1690 	i->space_id = 0;
1691 	i->first_page_id = 0;
1692 	i->last_page_in_run = TRUE;
1693 	i->changed = FALSE;
1694 	i->failed = FALSE;
1695 
1696 	return TRUE;
1697 }
1698 
1699 /*********************************************************************//**
1700 Releases log bitmap iterator. */
1701 UNIV_INTERN
1702 void
log_online_bitmap_iterator_release(log_bitmap_iterator_t * i)1703 log_online_bitmap_iterator_release(
1704 /*===============================*/
1705 	log_bitmap_iterator_t *i) /*!<in/out:  iterator */
1706 {
1707 	ut_a(i);
1708 
1709 	if (!os_file_is_invalid(i->in.file)) {
1710 
1711 		os_file_close(i->in.file);
1712 		os_file_mark_invalid(&i->in.file);
1713 	}
1714 	if (i->in_files.files) {
1715 
1716 		ut_free(i->in_files.files);
1717 	}
1718 	if (i->page) {
1719 
1720 		ut_free(i->page);
1721 	}
1722 	i->failed = TRUE;
1723 }
1724 
1725 /*********************************************************************//**
1726 Iterates through bits of saved bitmap blocks.
1727 Sequentially reads blocks from bitmap file(s) and interates through
1728 their bits. Ignores blocks with wrong checksum.
1729 @return TRUE if iteration is successful, FALSE if all bits are iterated. */
1730 UNIV_INTERN
1731 ibool
log_online_bitmap_iterator_next(log_bitmap_iterator_t * i)1732 log_online_bitmap_iterator_next(
1733 /*============================*/
1734 	log_bitmap_iterator_t *i) /*!<in/out: iterator */
1735 {
1736 	ibool	checksum_ok = FALSE;
1737 	ibool	success;
1738 
1739 	ut_a(i);
1740 
1741 	if (UNIV_UNLIKELY(i->in_files.count == 0)) {
1742 
1743 		return FALSE;
1744 	}
1745 
1746 	if (UNIV_LIKELY(i->bit_offset < MODIFIED_PAGE_BLOCK_BITMAP_LEN))
1747 	{
1748 		++i->bit_offset;
1749 		i->changed =
1750 			IS_BIT_SET(i->page + MODIFIED_PAGE_BLOCK_BITMAP,
1751 				   i->bit_offset);
1752 		return TRUE;
1753 	}
1754 
1755 	if (i->end_lsn >= i->max_lsn && i->last_page_in_run)
1756 		return FALSE;
1757 
1758 	while (!checksum_ok)
1759 	{
1760 		while (i->in.size < MODIFIED_PAGE_BLOCK_SIZE
1761 		       || (i->in.offset
1762 			   > i->in.size - MODIFIED_PAGE_BLOCK_SIZE)) {
1763 
1764 			/* Advance file */
1765 			i->in_i++;
1766 			success = os_file_close_no_error_handling(
1767 				i->in.file);
1768 			os_file_mark_invalid(&i->in.file);
1769 			if (UNIV_UNLIKELY(!success)) {
1770 
1771 				os_file_get_last_error(TRUE);
1772 				i->failed = TRUE;
1773 				return FALSE;
1774 			}
1775 
1776 			success = log_online_diagnose_bitmap_eof(
1777 					&i->in, i->last_page_in_run);
1778 			if (UNIV_UNLIKELY(!success)) {
1779 
1780 				i->failed = TRUE;
1781 				return FALSE;
1782 
1783 			}
1784 
1785 			if (i->in_i == i->in_files.count) {
1786 
1787 				return FALSE;
1788 			}
1789 
1790 			if (UNIV_UNLIKELY(i->in_files.files[i->in_i].seq_num
1791 					  == 0)) {
1792 
1793 				i->failed = TRUE;
1794 				return FALSE;
1795 			}
1796 
1797 			success = log_online_open_bitmap_file_read_only(
1798 					i->in_files.files[i->in_i].name,
1799 					&i->in);
1800 			if (UNIV_UNLIKELY(!success)) {
1801 
1802 				i->failed = TRUE;
1803 				return FALSE;
1804 			}
1805 		}
1806 
1807 		success = log_online_read_bitmap_page(&i->in, i->page,
1808 						      &checksum_ok);
1809 		if (UNIV_UNLIKELY(!success)) {
1810 
1811 			os_file_get_last_error(TRUE);
1812 			ib_logf(IB_LOG_LEVEL_WARN,
1813 				"failed reading changed page bitmap file "
1814 				"\'%s\'", i->in_files.files[i->in_i].name);
1815 			i->failed = TRUE;
1816 			return FALSE;
1817 		}
1818 	}
1819 
1820 	i->start_lsn = mach_read_from_8(i->page + MODIFIED_PAGE_START_LSN);
1821 	i->end_lsn = mach_read_from_8(i->page + MODIFIED_PAGE_END_LSN);
1822 	i->space_id = mach_read_from_4(i->page + MODIFIED_PAGE_SPACE_ID);
1823 	i->first_page_id = mach_read_from_4(i->page
1824 					    + MODIFIED_PAGE_1ST_PAGE_ID);
1825 	i->last_page_in_run = mach_read_from_4(i->page
1826 					       + MODIFIED_PAGE_IS_LAST_BLOCK);
1827 	i->bit_offset = 0;
1828 	i->changed = IS_BIT_SET(i->page + MODIFIED_PAGE_BLOCK_BITMAP,
1829 				i->bit_offset);
1830 
1831 	return TRUE;
1832 }
1833 
1834 /************************************************************//**
1835 Delete all the bitmap files for data less than the specified LSN.
1836 If called with lsn == 0 (i.e. set by RESET request) or LSN_MAX,
1837 restart the bitmap file sequence, otherwise continue it.
1838 
1839 @return FALSE to indicate success, TRUE for failure. */
1840 UNIV_INTERN
1841 ibool
log_online_purge_changed_page_bitmaps(lsn_t lsn)1842 log_online_purge_changed_page_bitmaps(
1843 /*==================================*/
1844 	lsn_t	lsn)	/*!< in: LSN to purge files up to */
1845 {
1846 	log_online_bitmap_file_range_t	bitmap_files;
1847 	size_t				i;
1848 	ibool				result = FALSE;
1849 
1850 	if (lsn == 0) {
1851 		lsn = LSN_MAX;
1852 	}
1853 
1854 	bool log_bmp_sys_inited = false;
1855 	if (srv_redo_log_thread_started) {
1856 		/* User requests might happen with both enabled and disabled
1857 		tracking */
1858 		log_bmp_sys_inited = true;
1859 		mutex_enter(&log_bmp_sys_mutex);
1860 		if (!srv_redo_log_thread_started) {
1861 			log_bmp_sys_inited = false;
1862 			mutex_exit(&log_bmp_sys_mutex);
1863 		}
1864 	}
1865 
1866 	if (!log_online_setup_bitmap_file_range(&bitmap_files, 0, LSN_MAX)) {
1867 		if (log_bmp_sys_inited) {
1868 			mutex_exit(&log_bmp_sys_mutex);
1869 		}
1870 		return TRUE;
1871 	}
1872 
1873 	if (srv_redo_log_thread_started && lsn > log_bmp_sys->end_lsn) {
1874 		/* If we have to delete the current output file, close it
1875 		first. */
1876 		os_file_close(log_bmp_sys->out.file);
1877 		os_file_mark_invalid(&log_bmp_sys->out.file);
1878 	}
1879 
1880 	for (i = 0; i < bitmap_files.count; i++) {
1881 
1882 		char	full_bmp_file_name[2 * FN_REFLEN + 2];
1883 
1884 		/* We consider the end LSN of the current bitmap, derived from
1885 		the start LSN of the subsequent bitmap file, to determine
1886 		whether to remove the current bitmap.  Note that bitmap_files
1887 		does not contain an entry for the bitmap past the given LSN so
1888 		we must check the boundary conditions as well.  For example,
1889 		consider 1_0.xdb and 2_10.xdb and querying LSN 5.  bitmap_files
1890 		will only contain 1_0.xdb and we must not delete it since it
1891 		represents LSNs 0-9. */
1892 		if ((i + 1 == bitmap_files.count
1893 		     || bitmap_files.files[i + 1].seq_num == 0
1894 		     || bitmap_files.files[i + 1].start_lsn > lsn)
1895 		    && (lsn != LSN_MAX)) {
1896 
1897 			break;
1898 		}
1899 
1900 		/* In some non-trivial cases the sequence of .xdb files may
1901 		have gaps. For instance:
1902 			ib_modified_log_1_0.xdb
1903 			ib_modified_log_2_<mmm>.xdb
1904 			ib_modified_log_4_<nnn>.xdb
1905 		Adding this check as a safety precaution. */
1906 		if (bitmap_files.files[i].name[0] == '\0')
1907 			continue;
1908 
1909 		/* If redo log tracking is enabled, reuse 'bmp_file_home'
1910 		from 'log_bmp_sys'. Otherwise, compose the full '.xdb' file
1911 		path from 'srv_data_home', adding a path separator if
1912 		necessary. */
1913 		if (log_bmp_sys != NULL) {
1914 			ut_snprintf(full_bmp_file_name,
1915 				sizeof(full_bmp_file_name),
1916 				"%s%s", log_bmp_sys->bmp_file_home,
1917 				bitmap_files.files[i].name);
1918 		}
1919 		else {
1920 			char		separator[2] = {0, 0};
1921 			const size_t	srv_data_home_len =
1922 				strlen(srv_data_home);
1923 
1924 			ut_a(srv_data_home_len < FN_REFLEN);
1925 			if (srv_data_home_len != 0 &&
1926 				srv_data_home[srv_data_home_len - 1] !=
1927 				SRV_PATH_SEPARATOR) {
1928 				separator[0] = SRV_PATH_SEPARATOR;
1929 			}
1930 			ut_snprintf(full_bmp_file_name,
1931 				sizeof(full_bmp_file_name), "%s%s%s",
1932 				srv_data_home, separator,
1933 				bitmap_files.files[i].name);
1934 		}
1935 
1936 		if (!os_file_delete_if_exists(innodb_file_bmp_key,
1937 					      full_bmp_file_name)) {
1938 
1939 			os_file_get_last_error(TRUE);
1940 			result = TRUE;
1941 			break;
1942 		}
1943 	}
1944 
1945 	if (log_bmp_sys_inited) {
1946 		if (lsn > log_bmp_sys->end_lsn) {
1947 			lsn_t	new_file_lsn;
1948 			if (lsn == LSN_MAX) {
1949 				/* RESET restarts the sequence */
1950 				log_bmp_sys->out_seq_num = 0;
1951 				new_file_lsn = 0;
1952 			} else {
1953 				new_file_lsn = log_bmp_sys->end_lsn;
1954 			}
1955 			if (!log_online_rotate_bitmap_file(new_file_lsn)) {
1956 				/* If file create failed, stop log tracking */
1957 				srv_track_changed_pages = FALSE;
1958 			}
1959 		}
1960 
1961 		mutex_exit(&log_bmp_sys_mutex);
1962 	}
1963 
1964 	free(bitmap_files.files);
1965 	return result;
1966 }
1967