1 /*****************************************************************************
2 
3 Copyright (c) 2011, 2021, Oracle and/or its affiliates.
4 
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License, version 2.0,
7 as published by the Free Software Foundation.
8 
9 This program is also distributed with certain software (including
10 but not limited to OpenSSL) that is licensed under separate terms,
11 as designated in a particular file or component or in included license
12 documentation.  The authors of MySQL hereby grant you an additional
13 permission to link the program and your derivative works with the
14 separately licensed software that they have included with MySQL.
15 
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 GNU General Public License, version 2.0, for more details.
20 
21 You should have received a copy of the GNU General Public License along with
22 this program; if not, write to the Free Software Foundation, Inc.,
23 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
24 
25 *****************************************************************************/
26 
27 /**************************************************//**
28 @file buf/buf0dump.cc
29 Implements a buffer pool dump/load.
30 
31 Created April 08, 2011 Vasil Dimov
32 *******************************************************/
33 
34 #include "my_global.h"
35 #include "my_sys.h"
36 #include "my_thread.h"
37 
38 #include "mysql/psi/mysql_stage.h"
39 #include "mysql/psi/psi.h"
40 
41 #include "univ.i"
42 
43 #include "buf0buf.h"
44 #include "buf0dump.h"
45 #include "dict0dict.h"
46 #include "os0file.h"
47 #include "os0thread.h"
48 #include "srv0srv.h"
49 #include "srv0start.h"
50 #include "sync0rw.h"
51 #include "ut0byte.h"
52 
53 #include <algorithm>
54 
55 enum status_severity {
56 	STATUS_VERBOSE,
57 	STATUS_INFO,
58 	STATUS_ERR
59 };
60 
61 #define SHUTTING_DOWN()	(srv_shutdown_state != SRV_SHUTDOWN_NONE)
62 
63 /* Flags that tell the buffer pool dump/load thread which action should it
64 take after being waked up. */
65 static ibool	buf_dump_should_start = FALSE;
66 static ibool	buf_load_should_start = FALSE;
67 
68 static ibool	buf_load_abort_flag = FALSE;
69 
70 /* Used to temporary store dump info in order to avoid IO while holding
71 buffer pool LRU list mutex during dump and also to sort the contents of the
72 dump before reading the pages from disk during load.
73 We store the space id in the high 32 bits and page no in low 32 bits. */
74 typedef ib_uint64_t	buf_dump_t;
75 
76 /* Aux macros to create buf_dump_t and to extract space and page from it */
77 #define BUF_DUMP_CREATE(space, page)	ut_ull_create(space, page)
78 #define BUF_DUMP_SPACE(a)		((ulint) ((a) >> 32))
79 #define BUF_DUMP_PAGE(a)		((ulint) ((a) & 0xFFFFFFFFUL))
80 
81 /*****************************************************************//**
82 Wakes up the buffer pool dump/load thread and instructs it to start
83 a dump. This function is called by MySQL code via buffer_pool_dump_now()
84 and it should return immediately because the whole MySQL is frozen during
85 its execution. */
86 void
buf_dump_start()87 buf_dump_start()
88 /*============*/
89 {
90 	buf_dump_should_start = TRUE;
91 	os_event_set(srv_buf_dump_event);
92 }
93 
94 /*****************************************************************//**
95 Wakes up the buffer pool dump/load thread and instructs it to start
96 a load. This function is called by MySQL code via buffer_pool_load_now()
97 and it should return immediately because the whole MySQL is frozen during
98 its execution. */
99 void
buf_load_start()100 buf_load_start()
101 /*============*/
102 {
103 	buf_load_should_start = TRUE;
104 	os_event_set(srv_buf_dump_event);
105 }
106 
107 /*****************************************************************//**
108 Sets the global variable that feeds MySQL's innodb_buffer_pool_dump_status
109 to the specified string. The format and the following parameters are the
110 same as the ones used for printf(3). The value of this variable can be
111 retrieved by:
112 SELECT variable_value FROM information_schema.global_status WHERE
113 variable_name = 'INNODB_BUFFER_POOL_DUMP_STATUS';
114 or by:
115 SHOW STATUS LIKE 'innodb_buffer_pool_dump_status'; */
116 static MY_ATTRIBUTE((nonnull, format(printf, 2, 3)))
117 void
buf_dump_status(enum status_severity severity,const char * fmt,...)118 buf_dump_status(
119 /*============*/
120 	enum status_severity	severity,/*!< in: status severity */
121 	const char*		fmt,	/*!< in: format */
122 	...)				/*!< in: extra parameters according
123 					to fmt */
124 {
125 	va_list	ap;
126 
127 	va_start(ap, fmt);
128 
129 	ut_vsnprintf(
130 		export_vars.innodb_buffer_pool_dump_status,
131 		sizeof(export_vars.innodb_buffer_pool_dump_status),
132 		fmt, ap);
133 
134 	switch (severity) {
135 	case STATUS_INFO:
136 		ib::info() << export_vars.innodb_buffer_pool_dump_status;
137 		break;
138 
139 	case STATUS_ERR:
140 		ib::error() << export_vars.innodb_buffer_pool_dump_status;
141 		break;
142 
143 	case STATUS_VERBOSE:
144 		break;
145 	}
146 
147 	va_end(ap);
148 }
149 
150 /*****************************************************************//**
151 Sets the global variable that feeds MySQL's innodb_buffer_pool_load_status
152 to the specified string. The format and the following parameters are the
153 same as the ones used for printf(3). The value of this variable can be
154 retrieved by:
155 SELECT variable_value FROM information_schema.global_status WHERE
156 variable_name = 'INNODB_BUFFER_POOL_LOAD_STATUS';
157 or by:
158 SHOW STATUS LIKE 'innodb_buffer_pool_load_status'; */
159 static MY_ATTRIBUTE((nonnull, format(printf, 2, 3)))
160 void
buf_load_status(enum status_severity severity,const char * fmt,...)161 buf_load_status(
162 /*============*/
163 	enum status_severity	severity,/*!< in: status severity */
164 	const char*	fmt,	/*!< in: format */
165 	...)			/*!< in: extra parameters according to fmt */
166 {
167 	va_list	ap;
168 
169 	va_start(ap, fmt);
170 
171 	ut_vsnprintf(
172 		export_vars.innodb_buffer_pool_load_status,
173 		sizeof(export_vars.innodb_buffer_pool_load_status),
174 		fmt, ap);
175 
176 	switch (severity) {
177 	case STATUS_INFO:
178 		ib::info() << export_vars.innodb_buffer_pool_load_status;
179 		break;
180 
181 	case STATUS_ERR:
182 		ib::error() << export_vars.innodb_buffer_pool_load_status;
183 		break;
184 
185 	case STATUS_VERBOSE:
186 		break;
187 	}
188 
189 	va_end(ap);
190 }
191 
192 /** Returns the directory path where the buffer pool dump file will be created.
193 @return directory path */
194 static
195 const char*
get_buf_dump_dir()196 get_buf_dump_dir()
197 {
198 	const char*	dump_dir;
199 
200 	/* The dump file should be created in the default data directory if
201 	innodb_data_home_dir is set as an empty string. */
202 	if (strcmp(srv_data_home, "") == 0) {
203 		dump_dir = fil_path_to_mysql_datadir;
204 	} else {
205 		dump_dir = srv_data_home;
206 	}
207 
208 	return(dump_dir);
209 }
210 
211 /** Generate the path to the buffer pool dump/load file.
212 @param[out]	path		generated path
213 @param[in]	path_size	size of 'path', used as in snprintf(3). */
214 static
215 void
buf_dump_generate_path(char * path,size_t path_size)216 buf_dump_generate_path(
217 	char*	path,
218 	size_t	path_size)
219 {
220 	char	buf[FN_REFLEN];
221 
222 	ut_snprintf(buf, sizeof(buf), "%s%c%s", get_buf_dump_dir(),
223 		    OS_PATH_SEPARATOR, srv_buf_dump_filename);
224 
225 	os_file_type_t	type;
226 	bool		exists = false;
227 	bool		ret;
228 
229 	ret = os_file_status(buf, &exists, &type);
230 
231 	/* For realpath() to succeed the file must exist. */
232 
233 	if (ret && exists) {
234 		/* my_realpath() assumes the destination buffer is big enough
235 		to hold FN_REFLEN bytes. */
236 		ut_a(path_size >= FN_REFLEN);
237 
238 		my_realpath(path, buf, 0);
239 	} else {
240 		/* If it does not exist, then resolve only srv_data_home
241 		and append srv_buf_dump_filename to it. */
242 		char	srv_data_home_full[FN_REFLEN];
243 
244 		my_realpath(srv_data_home_full, get_buf_dump_dir(), 0);
245 
246 		if (srv_data_home_full[strlen(srv_data_home_full) - 1]
247 		    == OS_PATH_SEPARATOR) {
248 
249 			ut_snprintf(path, path_size, "%s%s",
250 				    srv_data_home_full,
251 				    srv_buf_dump_filename);
252 		} else {
253 			ut_snprintf(path, path_size, "%s%c%s",
254 				    srv_data_home_full,
255 				    OS_PATH_SEPARATOR,
256 				    srv_buf_dump_filename);
257 		}
258 	}
259 }
260 
261 /*****************************************************************//**
262 Perform a buffer pool dump into the file specified by
263 innodb_buffer_pool_filename. If any errors occur then the value of
264 innodb_buffer_pool_dump_status will be set accordingly, see buf_dump_status().
265 The dump filename can be specified by (relative to srv_data_home):
266 SET GLOBAL innodb_buffer_pool_filename='filename'; */
267 static
268 void
buf_dump(ibool obey_shutdown)269 buf_dump(
270 /*=====*/
271 	ibool	obey_shutdown)	/*!< in: quit if we are in a shutting down
272 				state */
273 {
274 #define SHOULD_QUIT()	(SHUTTING_DOWN() && obey_shutdown)
275 
276 	char	full_filename[OS_FILE_MAX_PATH];
277 	char	tmp_filename[OS_FILE_MAX_PATH + 11];
278 	char	now[32];
279 	FILE*	f;
280 	ulint	i;
281 	int	ret;
282 
283 	buf_dump_generate_path(full_filename, sizeof(full_filename));
284 
285 	ut_snprintf(tmp_filename, sizeof(tmp_filename),
286 		    "%s.incomplete", full_filename);
287 
288 	buf_dump_status(STATUS_INFO, "Dumping buffer pool(s) to %s",
289 			full_filename);
290 
291 	f = fopen(tmp_filename, "w");
292 	if (f == NULL) {
293 		buf_dump_status(STATUS_ERR,
294 				"Cannot open '%s' for writing: %s",
295 				tmp_filename, strerror(errno));
296 		return;
297 	}
298 	/* else */
299 
300 	/* walk through each buffer pool */
301 	for (i = 0; i < srv_buf_pool_instances && !SHOULD_QUIT(); i++) {
302 		buf_pool_t*		buf_pool;
303 		const buf_page_t*	bpage;
304 		buf_dump_t*		dump;
305 		ulint			n_pages;
306 		ulint			j;
307 
308 		buf_pool = buf_pool_from_array(i);
309 
310 		/* obtain buf_pool LRU list mutex before allocate, since
311 		UT_LIST_GET_LEN(buf_pool->LRU) could change */
312 		mutex_enter(&buf_pool->LRU_list_mutex);
313 
314 		n_pages = UT_LIST_GET_LEN(buf_pool->LRU);
315 
316 		/* skip empty buffer pools */
317 		if (n_pages == 0) {
318 			mutex_exit(&buf_pool->LRU_list_mutex);
319 			continue;
320 		}
321 
322 		if (srv_buf_pool_dump_pct != 100) {
323 			ut_ad(srv_buf_pool_dump_pct < 100);
324 
325 			n_pages = n_pages * srv_buf_pool_dump_pct / 100;
326 
327 			if (n_pages == 0) {
328 				n_pages = 1;
329 			}
330 		}
331 
332 		dump = static_cast<buf_dump_t*>(ut_malloc_nokey(
333 				n_pages * sizeof(*dump)));
334 
335 		if (dump == NULL) {
336 			mutex_exit(&buf_pool->LRU_list_mutex);
337 			fclose(f);
338 			buf_dump_status(STATUS_ERR,
339 					"Cannot allocate " ULINTPF " bytes: %s",
340 					(ulint) (n_pages * sizeof(*dump)),
341 					strerror(errno));
342 			/* leave tmp_filename to exist */
343 			return;
344 		}
345 
346 		for (bpage = UT_LIST_GET_FIRST(buf_pool->LRU), j = 0;
347 		     bpage != NULL && j < n_pages;
348 		     bpage = UT_LIST_GET_NEXT(LRU, bpage), j++) {
349 
350 			ut_a(buf_page_in_file(bpage));
351 
352 			dump[j] = BUF_DUMP_CREATE(bpage->id.space(),
353 						  bpage->id.page_no());
354 		}
355 
356 		ut_a(j == n_pages);
357 
358 		mutex_exit(&buf_pool->LRU_list_mutex);
359 
360 		for (j = 0; j < n_pages && !SHOULD_QUIT(); j++) {
361 			ret = fprintf(f, ULINTPF "," ULINTPF "\n",
362 				      BUF_DUMP_SPACE(dump[j]),
363 				      BUF_DUMP_PAGE(dump[j]));
364 			if (ret < 0) {
365 				ut_free(dump);
366 				fclose(f);
367 				buf_dump_status(STATUS_ERR,
368 						"Cannot write to '%s': %s",
369 						tmp_filename, strerror(errno));
370 				/* leave tmp_filename to exist */
371 				return;
372 			}
373 
374 			if (j % 128 == 0) {
375 				buf_dump_status(
376 					STATUS_VERBOSE,
377 					"Dumping buffer pool"
378 					" " ULINTPF "/" ULINTPF ","
379 					" page " ULINTPF "/" ULINTPF,
380 					i + 1, srv_buf_pool_instances,
381 					j + 1, n_pages);
382 			}
383 		}
384 
385 		ut_free(dump);
386 	}
387 
388 	ret = fclose(f);
389 	if (ret != 0) {
390 		buf_dump_status(STATUS_ERR,
391 				"Cannot close '%s': %s",
392 				tmp_filename, strerror(errno));
393 		return;
394 	}
395 	/* else */
396 
397 	ret = unlink(full_filename);
398 	if (ret != 0 && errno != ENOENT) {
399 		buf_dump_status(STATUS_ERR,
400 				"Cannot delete '%s': %s",
401 				full_filename, strerror(errno));
402 		/* leave tmp_filename to exist */
403 		return;
404 	}
405 	/* else */
406 
407 	ret = rename(tmp_filename, full_filename);
408 	if (ret != 0) {
409 		buf_dump_status(STATUS_ERR,
410 				"Cannot rename '%s' to '%s': %s",
411 				tmp_filename, full_filename,
412 				strerror(errno));
413 		/* leave tmp_filename to exist */
414 		return;
415 	}
416 	/* else */
417 
418 	/* success */
419 
420 	ut_sprintf_timestamp(now);
421 
422 	buf_dump_status(STATUS_INFO,
423 			"Buffer pool(s) dump completed at %s", now);
424 }
425 
426 /** Artificially delay the buffer pool loading if necessary. The idea of this
427 function is to prevent hogging the server with IO and slowing down too much
428 normal client queries.
429 @param[in,out]	last_check_time		milliseconds since epoch of the last
430                                         time we did check if throttling is
431                                         needed, we do the check every
432                                         srv_io_capacity IO ops.
433 @param[in]	last_activity_count	activity count
434 @param[in]	n_io			number of IO ops done since buffer
435                                         pool load has started */
436 UNIV_INLINE
437 void
buf_load_throttle_if_needed(ib_time_monotonic_ms_t * last_check_time,ulint * last_activity_count,ulint n_io)438 buf_load_throttle_if_needed(
439 /*========================*/
440 	ib_time_monotonic_ms_t*	last_check_time,
441 	ulint*			last_activity_count,
442 	ulint 			n_io)
443 {
444 	if (n_io % srv_io_capacity < srv_io_capacity - 1) {
445 		return;
446 	}
447 
448 	if (*last_check_time == 0 || *last_activity_count == 0) {
449 		*last_check_time = ut_time_monotonic_ms();
450 		*last_activity_count = srv_get_activity_count();
451 		return;
452 	}
453 
454 	/* srv_io_capacity IO operations have been performed by buffer pool
455 	load since the last time we were here. */
456 
457 	/* If no other activity, then keep going without any delay. */
458 	if (srv_get_activity_count() == *last_activity_count) {
459 		return;
460 	}
461 
462 	/* There has been other activity, throttle. */
463 
464 	ib_time_monotonic_ms_t	now = ut_time_monotonic_ms();
465 	ulint	elapsed_time = now - *last_check_time;
466 
467 	/* Notice that elapsed_time is not the time for the last
468 	srv_io_capacity IO operations performed by BP load. It is the
469 	time elapsed since the last time we detected that there has been
470 	other activity. This has a small and acceptable deficiency, e.g.:
471 	1. BP load runs and there is no other activity.
472 	2. Other activity occurs, we run N IO operations after that and
473 	   enter here (where 0 <= N < srv_io_capacity).
474 	3. last_check_time is very old and we do not sleep at this time, but
475 	   only update last_check_time and last_activity_count.
476 	4. We run srv_io_capacity more IO operations and call this function
477 	   again.
478 	5. There has been more other activity and thus we enter here.
479 	6. Now last_check_time is recent and we sleep if necessary to prevent
480 	   more than srv_io_capacity IO operations per second.
481 	The deficiency is that we could have slept at 3., but for this we
482 	would have to update last_check_time before the
483 	"cur_activity_count == *last_activity_count" check and calling
484 	ut_time_monotonic_ms() that often may turn out to be too expensive. */
485 
486 	if (elapsed_time < 1000 /* 1 sec (1000 milli secs) */) {
487 		os_thread_sleep((1000 - elapsed_time) * 1000 /* micro secs */);
488 	}
489 
490 	*last_check_time = ut_time_monotonic_ms();
491 	*last_activity_count = srv_get_activity_count();
492 }
493 
494 /*****************************************************************//**
495 Perform a buffer pool load from the file specified by
496 innodb_buffer_pool_filename. If any errors occur then the value of
497 innodb_buffer_pool_load_status will be set accordingly, see buf_load_status().
498 The dump filename can be specified by (relative to srv_data_home):
499 SET GLOBAL innodb_buffer_pool_filename='filename'; */
500 static
501 void
buf_load()502 buf_load()
503 /*======*/
504 {
505 	char		full_filename[OS_FILE_MAX_PATH];
506 	char		now[32];
507 	FILE*		f;
508 	buf_dump_t*	dump;
509 	ulint		dump_n;
510 	ulint		total_buffer_pools_pages;
511 	ulint		i;
512 	ulint		space_id;
513 	ulint		page_no;
514 	int		fscanf_ret;
515 
516 	/* Ignore any leftovers from before */
517 	buf_load_abort_flag = FALSE;
518 
519 	buf_dump_generate_path(full_filename, sizeof(full_filename));
520 
521 	buf_load_status(STATUS_INFO,
522 			"Loading buffer pool(s) from %s", full_filename);
523 
524 	f = fopen(full_filename, "r");
525 	if (f == NULL) {
526 		buf_load_status(STATUS_ERR,
527 				"Cannot open '%s' for reading: %s",
528 				full_filename, strerror(errno));
529 		return;
530 	}
531 	/* else */
532 
533 	/* First scan the file to estimate how many entries are in it.
534 	This file is tiny (approx 500KB per 1GB buffer pool), reading it
535 	two times is fine. */
536 	dump_n = 0;
537 	while (fscanf(f, ULINTPF "," ULINTPF, &space_id, &page_no) == 2
538 	       && !SHUTTING_DOWN()) {
539 		dump_n++;
540 	}
541 
542 	if (!SHUTTING_DOWN() && !feof(f)) {
543 		/* fscanf() returned != 2 */
544 		const char*	what;
545 		if (ferror(f)) {
546 			what = "reading";
547 		} else {
548 			what = "parsing";
549 		}
550 		fclose(f);
551 		buf_load_status(STATUS_ERR, "Error %s '%s',"
552 				" unable to load buffer pool (stage 1)",
553 				what, full_filename);
554 		return;
555 	}
556 
557 	/* If dump is larger than the buffer pool(s), then we ignore the
558 	extra trailing. This could happen if a dump is made, then buffer
559 	pool is shrunk and then load is attempted. */
560 	total_buffer_pools_pages = buf_pool_get_n_pages()
561 		* srv_buf_pool_instances;
562 	if (dump_n > total_buffer_pools_pages) {
563 		dump_n = total_buffer_pools_pages;
564 	}
565 
566 	if(dump_n != 0) {
567 		dump = static_cast<buf_dump_t*>(ut_malloc_nokey(
568 				dump_n * sizeof(*dump)));
569 	} else {
570 		fclose(f);
571 		ut_sprintf_timestamp(now);
572 		buf_load_status(STATUS_INFO,
573 				"Buffer pool(s) load completed at %s"
574 				" (%s was empty)", now, full_filename);
575 		return;
576 	}
577 
578 	if (dump == NULL) {
579 		fclose(f);
580 		buf_load_status(STATUS_ERR,
581 				"Cannot allocate " ULINTPF " bytes: %s",
582 				(ulint) (dump_n * sizeof(*dump)),
583 				strerror(errno));
584 		return;
585 	}
586 
587 	rewind(f);
588 
589 	for (i = 0; i < dump_n && !SHUTTING_DOWN(); i++) {
590 		fscanf_ret = fscanf(f, ULINTPF "," ULINTPF,
591 				    &space_id, &page_no);
592 
593 		if (fscanf_ret != 2) {
594 			if (feof(f)) {
595 				break;
596 			}
597 			/* else */
598 
599 			ut_free(dump);
600 			fclose(f);
601 			buf_load_status(STATUS_ERR,
602 					"Error parsing '%s', unable"
603 					" to load buffer pool (stage 2)",
604 					full_filename);
605 			return;
606 		}
607 
608 		if (space_id > ULINT32_MASK || page_no > ULINT32_MASK) {
609 			ut_free(dump);
610 			fclose(f);
611 			buf_load_status(STATUS_ERR,
612 					"Error parsing '%s': bogus"
613 					" space,page " ULINTPF "," ULINTPF
614 					" at line " ULINTPF ","
615 					" unable to load buffer pool",
616 					full_filename,
617 					space_id, page_no,
618 					i);
619 			return;
620 		}
621 
622 		dump[i] = BUF_DUMP_CREATE(space_id, page_no);
623 	}
624 
625 	/* Set dump_n to the actual number of initialized elements,
626 	i could be smaller than dump_n here if the file got truncated after
627 	we read it the first time. */
628 	dump_n = i;
629 
630 	fclose(f);
631 
632 	if (dump_n == 0) {
633 		ut_free(dump);
634 		ut_sprintf_timestamp(now);
635 		buf_load_status(STATUS_INFO,
636 				"Buffer pool(s) load completed at %s"
637 				" (%s was empty)", now, full_filename);
638 		return;
639 	}
640 
641 	if (!SHUTTING_DOWN()) {
642 		std::sort(dump, dump + dump_n);
643 	}
644 
645 	ib_time_monotonic_ms_t		last_check_time = 0;
646 	ulint		last_activity_cnt = 0;
647 
648 	/* Avoid calling the expensive fil_space_acquire_silent() for each
649 	page within the same tablespace. dump[] is sorted by (space, page),
650 	so all pages from a given tablespace are consecutive. */
651 	ulint		cur_space_id = BUF_DUMP_SPACE(dump[0]);
652 	fil_space_t*	space = fil_space_acquire_silent(cur_space_id);
653 	page_size_t	page_size(space ? space->flags : 0);
654 
655 #ifdef HAVE_PSI_STAGE_INTERFACE
656 	PSI_stage_progress*	pfs_stage_progress
657 		= mysql_set_stage(srv_stage_buffer_pool_load.m_key);
658 #endif /* HAVE_PSI_STAGE_INTERFACE */
659 
660 	mysql_stage_set_work_estimated(pfs_stage_progress, dump_n);
661 	mysql_stage_set_work_completed(pfs_stage_progress, 0);
662 
663 	for (i = 0; i < dump_n && !SHUTTING_DOWN(); i++) {
664 
665 		/* space_id for this iteration of the loop */
666 		const ulint	this_space_id = BUF_DUMP_SPACE(dump[i]);
667 
668 		if (this_space_id != cur_space_id) {
669 			if (space != NULL) {
670 				fil_space_release(space);
671 			}
672 
673 			cur_space_id = this_space_id;
674 			space = fil_space_acquire_silent(cur_space_id);
675 
676 			if (space != NULL) {
677 				const page_size_t	cur_page_size(
678 					space->flags);
679 				page_size.copy_from(cur_page_size);
680 			}
681 		}
682 
683 		if (space == NULL) {
684 			continue;
685 		}
686 
687 		buf_read_page_background(
688 			page_id_t(this_space_id, BUF_DUMP_PAGE(dump[i])),
689 			page_size, true);
690 
691 		if (i % 64 == 63) {
692 			os_aio_simulated_wake_handler_threads();
693 		}
694 
695 		/* Update the progress every 32 MiB, which is every Nth page,
696 		where N = 32*1024^2 / page_size. */
697 		static const ulint	update_status_every_n_mb = 32;
698 		static const ulint	update_status_every_n_pages
699 			= update_status_every_n_mb * 1024 * 1024
700 			/ page_size.physical();
701 
702 		if (i % update_status_every_n_pages == 0) {
703 			buf_load_status(STATUS_VERBOSE,
704 					"Loaded " ULINTPF "/" ULINTPF " pages",
705 					i + 1, dump_n);
706 			mysql_stage_set_work_completed(pfs_stage_progress, i);
707 		}
708 
709 		if (buf_load_abort_flag) {
710 			if (space != NULL) {
711 				fil_space_release(space);
712 			}
713 			buf_load_abort_flag = FALSE;
714 			ut_free(dump);
715 			buf_load_status(
716 				STATUS_INFO,
717 				"Buffer pool(s) load aborted on request");
718 			/* Premature end, set estimated = completed = i and
719 			end the current stage event. */
720 			mysql_stage_set_work_estimated(pfs_stage_progress, i);
721 			mysql_stage_set_work_completed(pfs_stage_progress, i);
722 #ifdef HAVE_PSI_STAGE_INTERFACE
723 			mysql_end_stage();
724 #endif /* HAVE_PSI_STAGE_INTERFACE */
725 			return;
726 		}
727 
728 		buf_load_throttle_if_needed(
729 			&last_check_time, &last_activity_cnt, i);
730 	}
731 
732 	if (space != NULL) {
733 		fil_space_release(space);
734 	}
735 
736 	ut_free(dump);
737 
738 	ut_sprintf_timestamp(now);
739 
740 	buf_load_status(STATUS_INFO,
741 			"Buffer pool(s) load completed at %s", now);
742 
743 	/* Make sure that estimated = completed when we end. */
744 	mysql_stage_set_work_completed(pfs_stage_progress, dump_n);
745 	/* End the stage progress event. */
746 #ifdef HAVE_PSI_STAGE_INTERFACE
747 	mysql_end_stage();
748 #endif /* HAVE_PSI_STAGE_INTERFACE */
749 }
750 
751 /*****************************************************************//**
752 Aborts a currently running buffer pool load. This function is called by
753 MySQL code via buffer_pool_load_abort() and it should return immediately
754 because the whole MySQL is frozen during its execution. */
755 void
buf_load_abort()756 buf_load_abort()
757 /*============*/
758 {
759 	buf_load_abort_flag = TRUE;
760 }
761 
762 /*****************************************************************//**
763 This is the main thread for buffer pool dump/load. It waits for an
764 event and when waked up either performs a dump or load and sleeps
765 again.
766 @return this function does not return, it calls os_thread_exit() */
767 extern "C"
768 os_thread_ret_t
DECLARE_THREAD(buf_dump_thread)769 DECLARE_THREAD(buf_dump_thread)(
770 /*============================*/
771 	void*	arg MY_ATTRIBUTE((unused)))	/*!< in: a dummy parameter
772 						required by os_thread_create */
773 {
774 	my_thread_init();
775 	ut_ad(!srv_read_only_mode);
776 
777 #ifdef UNIV_PFS_THREAD
778 	pfs_register_thread(buf_dump_thread_key);
779 #endif /* UNIV_PFS_THREAD */
780 
781 	srv_buf_dump_thread_active = TRUE;
782 
783 	buf_dump_status(STATUS_VERBOSE, "Dumping of buffer pool not started");
784 	buf_load_status(STATUS_VERBOSE, "Loading of buffer pool not started");
785 
786 	if (srv_buffer_pool_load_at_startup) {
787 		buf_load();
788 	}
789 
790 	while (!SHUTTING_DOWN()) {
791 
792 		os_event_wait(srv_buf_dump_event);
793 
794 		if (buf_dump_should_start) {
795 			buf_dump_should_start = FALSE;
796 			buf_dump(TRUE /* quit on shutdown */);
797 		}
798 
799 		if (buf_load_should_start) {
800 			buf_load_should_start = FALSE;
801 			buf_load();
802 		}
803 
804 		os_event_reset(srv_buf_dump_event);
805 	}
806 
807 	if (srv_buffer_pool_dump_at_shutdown && srv_fast_shutdown != 2) {
808 		buf_dump(FALSE /* ignore shutdown down flag,
809 		keep going even if we are in a shutdown state */);
810 	}
811 
812 	srv_buf_dump_thread_active = FALSE;
813 
814 	my_thread_end();
815 	/* We count the number of threads in os_thread_exit(). A created
816 	thread should always use that to exit and not use return() to exit. */
817 	os_thread_exit();
818 
819 	OS_THREAD_DUMMY_RETURN;
820 }
821