Repository navigation
Expand file tree
/
Copy pathbuf0buf.cc
More file actions
7119 lines (5703 loc) · 231 KB
/
Copy pathbuf0buf.cc
File metadata and controls
7119 lines (5703 loc) · 231 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*****************************************************************************
Copyright (c) 1995, 2025, Oracle and/or its affiliates.
Copyright (c) 2008, Google Inc.
Portions of this file contain modifications contributed and copyrighted by
Google, Inc. Those modifications are gratefully acknowledged and are described
briefly in the InnoDB documentation. The contributions by Google are
incorporated with their permission, and subject to the conditions contained in
the file COPYING.Google.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License, version 2.0, as published by the
Free Software Foundation.
This program is designed to work with certain software (including
but not limited to OpenSSL) that is licensed under separate terms,
as designated in a particular file or component or in included license
documentation. The authors of MySQL hereby grant you an additional
permission to link the program and your derivative works with the
separately licensed software that they have either included with
the program or referenced in the documentation.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0,
for more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*****************************************************************************/
/** @file buf/buf0buf.cc
The database buffer buf_pool
Created 11/5/1995 Heikki Tuuri
*******************************************************/
#include "my_config.h"
#include "btr0btr.h"
#include "buf0buf.h"
#include "fil0fil.h"
#include "fsp0sysspace.h"
#include "ha_prototypes.h"
#include "mem0mem.h"
#include "my_dbug.h"
#include "page0size.h"
#ifndef UNIV_HOTBACKUP
#include "btr0sea.h"
#include "buf0buddy.h"
#include "buf0stats.h"
#include "dict0stats_bg.h"
#include "ibuf0ibuf.h"
#include "lock0lock.h"
#include "log0buf.h"
#include "log0chkp.h"
#include "page0page.h"
#include "sync0rw.h"
#include "trx0purge.h"
#include "trx0undo.h"
#include <errno.h>
#include <stdarg.h>
#include <sys/types.h>
#include <time.h>
#include <map>
#include <new>
#include <sstream>
#include <string_view>
#include <unordered_map>
#include "buf0checksum.h"
#include "buf0dump.h"
#include "dict0dict.h"
#include "log0recv.h"
#include "os0thread-create.h"
#include "page0zip.h"
#include "srv0mon.h"
#include "srv0srv.h"
#include "srv0start.h"
#include "sync0sync.h"
#include "ut0new.h"
#include "scope_guard.h"
#endif /* !UNIV_HOTBACKUP */
#ifdef UNIV_DEBUG
#include "ut0stateful_latching_rules.h"
#endif /* UNIV_DEBUG */
#ifdef HAVE_LIBNUMA
#include <numa.h>
#include <numaif.h>
struct set_numa_interleave_t {
set_numa_interleave_t() {
if (srv_numa_interleave) {
ib::info(ER_IB_MSG_47) << "Setting NUMA memory policy to"
" MPOL_INTERLEAVE";
struct bitmask *numa_nodes = numa_get_mems_allowed();
if (set_mempolicy(MPOL_INTERLEAVE, numa_nodes->maskp, numa_nodes->size) !=
0) {
ib::warn(ER_IB_MSG_48) << "Failed to set NUMA memory"
" policy to MPOL_INTERLEAVE: "
<< strerror(errno);
}
numa_bitmask_free(numa_nodes);
}
}
~set_numa_interleave_t() {
if (srv_numa_interleave) {
ib::info(ER_IB_MSG_49) << "Setting NUMA memory policy to"
" MPOL_DEFAULT";
if (set_mempolicy(MPOL_DEFAULT, nullptr, 0) != 0) {
ib::warn(ER_IB_MSG_50) << "Failed to set NUMA memory"
" policy to MPOL_DEFAULT: "
<< strerror(errno);
}
}
}
};
#define NUMA_MEMPOLICY_INTERLEAVE_IN_SCOPE set_numa_interleave_t scoped_numa
#else
#define NUMA_MEMPOLICY_INTERLEAVE_IN_SCOPE
#endif /* HAVE_LIBNUMA */
/*
IMPLEMENTATION OF THE BUFFER POOL
=================================
Buffer frames and blocks
------------------------
Following the terminology of Gray and Reuter, we call the memory
blocks where file pages are loaded buffer frames. For each buffer
frame there is a control block, or shortly, a block, in the buffer
control array. The control info which does not need to be stored
in the file along with the file page, resides in the control block.
Buffer pool struct
------------------
The buffer buf_pool contains several mutexes which protect all the
control data structures of the buf_pool. The content of a buffer frame is
protected by a separate read-write lock in its control block, though.
buf_pool->chunks_mutex protects the chunks, n_chunks during resize;
it also protects buf_pool_should_madvise:
- readers of buf_pool_should_madvise hold any buf_pool's chunks_mutex
- writers hold all buf_pools' chunk_mutex-es;
it is useful to think that it also protects the status of madvice() flags set
for chunks in this pool, even though these flags are handled by OS, as we only
modify them why holding this latch;
buf_pool->LRU_list_mutex protects the LRU_list;
buf_pool->free_list_mutex protects the free_list and withdraw list;
buf_pool->flush_state_mutex protects the flush state related data structures;
buf_pool->zip_free mutex protects the zip_free arrays;
buf_pool->zip_hash mutex protects the zip_hash hash and in_zip_hash flag.
Control blocks
--------------
The control block contains, for instance, the bufferfix count
which is incremented when a thread wants a file page to be fixed
in a buffer frame. The bufferfix operation does not lock the
contents of the frame, however. For this purpose, the control
block contains a read-write lock.
The buffer frames have to be aligned so that the start memory
address of a frame is divisible by the universal page size, which
is a power of two.
The control blocks containing file pages are put to a hash table
according to the file address of the page.
We could speed up the access to an individual page by using
"pointer swizzling": we could replace the page references on
non-leaf index pages by direct pointers to the page, if it exists
in the buf_pool. We could make a separate hash table where we could
chain all the page references in non-leaf pages residing in the buf_pool,
using the page reference as the hash key,
and at the time of reading of a page update the pointers accordingly.
Drawbacks of this solution are added complexity and,
possibly, extra space required on non-leaf pages for memory pointers.
A simpler solution is just to speed up the hash table mechanism
in the database, using tables whose size is a power of 2.
Lists of blocks
---------------
There are several lists of control blocks.
The free list (buf_pool->free) contains blocks which are currently not
used.
The common LRU list contains all the blocks holding a file page
except those for which the bufferfix count is non-zero.
The pages are in the LRU list roughly in the order of the last
access to the page, so that the oldest pages are at the end of the
list. We also keep a pointer to near the end of the LRU list,
which we can use when we want to artificially age a page in the
buf_pool. This is used if we know that some page is not needed
again for some time: we insert the block right after the pointer,
causing it to be replaced sooner than would normally be the case.
Currently this aging mechanism is used for read-ahead mechanism
of pages, and it can also be used when there is a scan of a full
table which cannot fit in the memory. Putting the pages near the
end of the LRU list, we make sure that most of the buf_pool stays
in the main memory, undisturbed.
The unzip_LRU list contains a subset of the common LRU list. The
blocks on the unzip_LRU list hold a compressed file page and the
corresponding uncompressed page frame. A block is in unzip_LRU if and
only if the predicate buf_page_belongs_to_unzip_LRU(&block->page)
holds. The blocks in unzip_LRU will be in same order as they are in
the common LRU list. That is, each manipulation of the common LRU
list will result in the same manipulation of the unzip_LRU list.
The chain of modified blocks (buf_pool->flush_list) contains the blocks
holding file pages that have been modified in the memory
but not written to disk yet. The block with the oldest modification
which has not yet been written to disk is at the end of the chain.
The access to this list is protected by buf_pool->flush_list_mutex.
The chain of unmodified compressed blocks (buf_pool->zip_clean)
contains the control blocks (buf_page_t) of those compressed pages
that are not in buf_pool->flush_list and for which no uncompressed
page has been allocated in the buffer pool. The control blocks for
uncompressed pages are accessible via buf_block_t objects that are
reachable via buf_pool->chunks[].
The chains of free memory blocks (buf_pool->zip_free[]) are used by
the buddy allocator (buf0buddy.cc) to keep track of currently unused
memory blocks of size sizeof(buf_page_t)..UNIV_PAGE_SIZE / 2. These
blocks are inside the UNIV_PAGE_SIZE-sized memory blocks of type
BUF_BLOCK_MEMORY that the buddy allocator requests from the buffer
pool. The buddy allocator is solely used for allocating control
blocks for compressed pages (buf_page_t) and compressed page frames.
Loading a file page
-------------------
First, a victim block for replacement has to be found in the
buf_pool. It is taken from the free list or searched for from the
end of the LRU-list. An exclusive lock is reserved for the frame,
the io_fix field is set in the block fixing the block in buf_pool,
and the io-operation for loading the page is queued. The io-handler thread
releases the X-lock on the frame and resets the io_fix field
when the io operation completes.
A thread may request the above operation using the function
buf_page_get(). It may then continue to request a lock on the frame.
The lock is granted when the io-handler releases the x-lock.
Read-ahead
----------
The read-ahead mechanism is intended to be intelligent and
isolated from the semantically higher levels of the database
index management. From the higher level we only need the
information if a file page has a natural successor or
predecessor page. On the leaf level of a B-tree index,
these are the next and previous pages in the natural
order of the pages.
Let us first explain the read-ahead mechanism when the leafs
of a B-tree are scanned in an ascending or descending order.
When a read page is the first time referenced in the buf_pool,
the buffer manager checks if it is at the border of a so-called
linear read-ahead area. The tablespace is divided into these
areas of size 64 blocks, for example. So if the page is at the
border of such an area, the read-ahead mechanism checks if
all the other blocks in the area have been accessed in an
ascending or descending order. If this is the case, the system
looks at the natural successor or predecessor of the page,
checks if that is at the border of another area, and in this case
issues read-requests for all the pages in that area. Maybe
we could relax the condition that all the pages in the area
have to be accessed: if data is deleted from a table, there may
appear holes of unused pages in the area.
A different read-ahead mechanism is used when there appears
to be a random access pattern to a file.
If a new page is referenced in the buf_pool, and several pages
of its random access area (for instance, 32 consecutive pages
in a tablespace) have recently been referenced, we may predict
that the whole area may be needed in the near future, and issue
the read requests for the whole area.
*/
#ifndef UNIV_HOTBACKUP
constexpr std::chrono::microseconds WAIT_FOR_READ{100};
constexpr std::chrono::microseconds WAIT_FOR_WRITE{100};
/** Number of attempts made to read in a page in the buffer pool */
static const ulint BUF_PAGE_READ_MAX_RETRIES = 100;
/** Number of pages to read ahead */
static const ulint BUF_READ_AHEAD_PAGES = 64;
/** The maximum portion of the buffer pool that can be used for the
read-ahead buffer. (Divide buf_pool size by this amount) */
static const ulint BUF_READ_AHEAD_PORTION = 32;
/** The buffer pools of the database */
buf_pool_t *buf_pool_ptr;
/** true when resizing buffer pool is in the critical path. */
volatile bool buf_pool_resizing;
/** Atomic variables to track resize status code and progress */
std::atomic_uint32_t buf_pool_resize_status_code = {0};
std::atomic_uint32_t buf_pool_resize_status_progress = {0};
/** Map of buffer pool chunks by its first frame address
This is newly made by initialization of buffer pool and buf_resize_thread.
Note: mutex protection is required when creating multiple buffer pools
in parallel. We don't use a mutex during resize because that is still single
threaded. */
typedef std::map<const byte *, buf_chunk_t *, std::less<const byte *>,
ut::allocator<std::pair<const byte *const, buf_chunk_t *>>>
buf_pool_chunk_map_t;
static buf_pool_chunk_map_t *buf_chunk_map_reg;
/** Container for how many pages from each index are contained in the buffer
pool(s). */
buf_stat_per_index_t *buf_stat_per_index;
#if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG
/** This is used to insert validation operations in execution
in the debug version */
static ulint buf_dbg_counter = 0;
#endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */
#ifdef UNIV_DEBUG
/** This is used to enable multiple buffer pool instances
with small buffer pool size. */
bool srv_buf_pool_debug;
#endif /* UNIV_DEBUG */
namespace {
#ifndef UNIV_HOTBACKUP
const std::unordered_map<buf_io_fix, std::string_view> buf_io_fix_str{
{BUF_IO_NONE, "BUF_IO_NONE"},
{BUF_IO_READ, "BUF_IO_READ"},
{BUF_IO_WRITE, "BUF_IO_WRITE"},
{BUF_IO_PIN, "BUF_IO_PIN"},
};
const std::unordered_map<buf_flush_t, std::string_view> buf_flush_str{
{BUF_FLUSH_LRU, "BUF_FLUSH_LRU"},
{BUF_FLUSH_LIST, "BUF_FLUSH_LIST"},
{BUF_FLUSH_SINGLE_PAGE, "BUF_FLUSH_SINGLE_PAGE"},
{BUF_FLUSH_N_TYPES, "BUF_FLUSH_N_TYPES"}};
/** Helper iostream operator presenting the io_fix value as human-readable
name of the enum. Used in error messages of Buf_io_fix_latching_rules.
@param[in,out] outs the output stream to which to print
@param[in] io_fix the value to be printed
@return always equals the stream passed as the outs argument
*/
static std::ostream &operator<<(std::ostream &outs, const buf_io_fix io_fix) {
ut_a(buf_page_t::is_correct_io_fix_value(io_fix));
return outs << buf_io_fix_str.at(io_fix);
}
/** Helper ostream operator to print buf_page_state in human-readable name of
the enum.
@param[in,out] outs the output stream
@param[in] state the page state to be printed
@return same output stream passed as input */
std::ostream &operator<<(std::ostream &outs, const buf_page_state &state) {
const std::string_view state_str = buf_page_state_str.at(state);
if (state_str.length() < 2) {
/* Compression pages states: invalid for buffer block pages */
if (state == BUF_BLOCK_POOL_WATCH) {
return outs << "POOL_WATCH";
} else if (state == BUF_BLOCK_ZIP_PAGE) {
return outs << "ZIP_PAGE";
} else if (state == BUF_BLOCK_ZIP_DIRTY) {
return outs << "ZIP_DIRTY";
} else {
ut_error;
}
}
return outs << state_str;
}
/** Helper ostream operator to print buf_flush_t in human-readable name of the
enum.
@param[in,out] outs the output stream
@param[in] flush_type the flush_type to be printed
@return same output stream passed as input */
std::ostream &operator<<(std::ostream &outs, const buf_flush_t &flush_type) {
return outs << buf_flush_str.at(flush_type);
}
#endif /* !UNIV_HOTBACKUP */
} // namespace
#if defined UNIV_PFS_MUTEX || defined UNIV_PFS_RWLOCK
#ifndef PFS_SKIP_BUFFER_MUTEX_RWLOCK
/* Buffer block mutexes and rwlocks can be registered
in one group rather than individually. If PFS_GROUP_BUFFER_SYNC
is defined, register buffer block mutex and rwlock
in one group after their initialization. */
#define PFS_GROUP_BUFFER_SYNC
/* This define caps the number of mutexes/rwlocks can
be registered with performance schema. Developers can
modify this define if necessary. Please note, this would
be effective only if PFS_GROUP_BUFFER_SYNC is defined. */
#define PFS_MAX_BUFFER_MUTEX_LOCK_REGISTER ULINT_MAX
#endif /* !PFS_SKIP_BUFFER_MUTEX_RWLOCK */
#endif /* UNIV_PFS_MUTEX || UNIV_PFS_RWLOCK */
/** Macro to determine whether the read of write counter is used depending
on the io_type */
#define MONITOR_RW_COUNTER(io_type, counter) \
((io_type == BUF_IO_READ) ? (counter##_READ) : (counter##_WRITTEN))
/** Registers a chunk to buf_pool_chunk_map
@param[in] chunk chunk of buffers */
static void buf_pool_register_chunk(buf_chunk_t *chunk) {
buf_chunk_map_reg->insert(
buf_pool_chunk_map_t::value_type(chunk->blocks->frame, chunk));
}
lsn_t buf_pool_get_oldest_modification_approx(void) {
lsn_t lsn = 0;
lsn_t oldest_lsn = 0;
/* When we traverse all the flush lists we don't care if previous
flush lists changed. We do not require consistent result. */
for (ulint i = 0; i < srv_buf_pool_instances; i++) {
buf_pool_t *buf_pool;
buf_pool = buf_pool_from_array(i);
buf_flush_list_mutex_enter(buf_pool);
buf_page_t *bpage;
/* We don't let log-checkpoint halt because pages from system
temporary are not yet flushed to the disk. Anyway, object
residing in system temporary doesn't generate REDO logging. */
bpage = buf_pool->oldest_hp.get();
if (bpage != nullptr) {
ut_ad(bpage->in_flush_list);
} else {
bpage = UT_LIST_GET_LAST(buf_pool->flush_list);
}
for (; bpage != nullptr && fsp_is_system_temporary(bpage->id.space());
bpage = UT_LIST_GET_PREV(list, bpage)) {
/* Do nothing. */
}
if (bpage != nullptr) {
ut_ad(bpage->in_flush_list);
lsn = bpage->get_oldest_lsn();
buf_pool->oldest_hp.set(bpage);
} else {
/* The last scanned page as entry point, or nullptr. */
buf_pool->oldest_hp.set(UT_LIST_GET_FIRST(buf_pool->flush_list));
}
buf_flush_list_mutex_exit(buf_pool);
if (!oldest_lsn || oldest_lsn > lsn) {
oldest_lsn = lsn;
}
}
/* The returned answer may be out of date: the flush_list can
change after the mutex has been released. */
return (oldest_lsn);
}
lsn_t buf_pool_get_oldest_modification_lwm(void) {
const lsn_t lsn = buf_pool_get_oldest_modification_approx();
if (lsn == 0) {
return (0);
}
ut_a(lsn % OS_FILE_LOG_BLOCK_SIZE >= LOG_BLOCK_HDR_SIZE);
const log_t &log = *log_sys;
const lsn_t lag = log_buffer_flush_order_lag(log);
ut_a(lag % OS_FILE_LOG_BLOCK_SIZE == 0);
const lsn_t checkpoint_lsn = log_get_checkpoint_lsn(log);
ut_a(checkpoint_lsn != 0);
if (lsn > lag) {
return (std::max(checkpoint_lsn, lsn - lag));
} else {
return (checkpoint_lsn);
}
}
/** Get total buffer pool statistics.
@param[out] LRU_len Length of all lru lists
@param[out] free_len Length of all free lists
@param[out] flush_list_len Length of all flush lists */
void buf_get_total_list_len(ulint *LRU_len, ulint *free_len,
ulint *flush_list_len) {
ulint i;
*LRU_len = 0;
*free_len = 0;
*flush_list_len = 0;
for (i = 0; i < srv_buf_pool_instances; i++) {
buf_pool_t *buf_pool;
buf_pool = buf_pool_from_array(i);
*LRU_len += UT_LIST_GET_LEN(buf_pool->LRU);
*free_len += UT_LIST_GET_LEN(buf_pool->free);
*flush_list_len += UT_LIST_GET_LEN(buf_pool->flush_list);
}
}
/** Get total list size in bytes from all buffer pools. */
void buf_get_total_list_size_in_bytes(
buf_pools_list_size_t *buf_pools_list_size) /*!< out: list sizes
in all buffer pools */
{
ut_ad(buf_pools_list_size);
memset(buf_pools_list_size, 0, sizeof(*buf_pools_list_size));
for (ulint i = 0; i < srv_buf_pool_instances; i++) {
buf_pool_t *buf_pool;
buf_pool = buf_pool_from_array(i);
/* We don't need mutex protection since this is
for statistics purpose */
buf_pools_list_size->LRU_bytes += buf_pool->stat.LRU_bytes;
buf_pools_list_size->unzip_LRU_bytes +=
UT_LIST_GET_LEN(buf_pool->unzip_LRU) * UNIV_PAGE_SIZE;
buf_pools_list_size->flush_list_bytes += buf_pool->stat.flush_list_bytes;
}
}
/** Get total buffer pool statistics. */
void buf_get_total_stat(
buf_pool_stat_t *tot_stat) /*!< out: buffer pool stats */
{
ulint i;
tot_stat->reset();
for (i = 0; i < srv_buf_pool_instances; i++) {
buf_pool_t *buf_pool = buf_pool_from_array(i);
buf_pool_stat_t *buf_stat = &buf_pool->stat;
Counter::add(tot_stat->m_n_page_gets, buf_stat->m_n_page_gets);
tot_stat->n_pages_read += buf_stat->n_pages_read;
tot_stat->n_pages_written += buf_stat->n_pages_written;
tot_stat->n_pages_created += buf_stat->n_pages_created;
tot_stat->n_ra_pages_read_rnd += buf_stat->n_ra_pages_read_rnd;
tot_stat->n_ra_pages_read += buf_stat->n_ra_pages_read;
tot_stat->n_ra_pages_evicted += buf_stat->n_ra_pages_evicted;
tot_stat->n_pages_made_young += buf_stat->n_pages_made_young;
tot_stat->n_pages_not_made_young += buf_stat->n_pages_not_made_young;
}
}
/** Allocates a buffer block.
@return own: the allocated block, in state BUF_BLOCK_MEMORY */
buf_block_t *buf_block_alloc(
buf_pool_t *buf_pool) /*!< in/out: buffer pool instance,
or NULL for round-robin selection
of the buffer pool */
{
buf_block_t *block;
ulint index;
static ulint buf_pool_index;
if (buf_pool == nullptr) {
/* We are allocating memory from any buffer pool, ensure
we spread the grace on all buffer pool instances. */
index = buf_pool_index++ % srv_buf_pool_instances;
buf_pool = buf_pool_from_array(index);
}
block = buf_LRU_get_free_block(buf_pool);
buf_block_set_state(block, BUF_BLOCK_MEMORY);
return (block);
}
#endif /* !UNIV_HOTBACKUP */
/** Prints a page to stderr.
@param[in] read_buf a database page
@param[in] page_size page size
@param[in] flags 0 or BUF_PAGE_PRINT_NO_CRASH or
BUF_PAGE_PRINT_NO_FULL */
void buf_page_print(const byte *read_buf, const page_size_t &page_size,
ulint flags) {
if (!(flags & BUF_PAGE_PRINT_NO_FULL)) {
ib::info(ER_IB_MSG_51) << "Page dump in ascii and hex ("
<< page_size.physical() << " bytes):";
ut_print_buf(stderr, read_buf, page_size.physical());
fputs("\nInnoDB: End of page dump\n", stderr);
}
if (page_size.is_compressed()) {
BlockReporter compressed = BlockReporter(false, read_buf, page_size, false);
/* Print compressed page. */
ib::info(ER_IB_MSG_52)
<< "Compressed page type (" << fil_page_get_type(read_buf)
<< "); stored checksum in field1 "
<< mach_read_from_4(read_buf + FIL_PAGE_SPACE_OR_CHKSUM)
<< "; calculated checksums for field1: "
<< buf_checksum_algorithm_name(SRV_CHECKSUM_ALGORITHM_CRC32) << " "
<< compressed.calc_zip_checksum(SRV_CHECKSUM_ALGORITHM_CRC32) << ", "
<< buf_checksum_algorithm_name(SRV_CHECKSUM_ALGORITHM_INNODB) << " "
<< compressed.calc_zip_checksum(SRV_CHECKSUM_ALGORITHM_INNODB) << ", "
<< buf_checksum_algorithm_name(SRV_CHECKSUM_ALGORITHM_NONE) << " "
<< compressed.calc_zip_checksum(SRV_CHECKSUM_ALGORITHM_NONE)
<< "; page LSN " << mach_read_from_8(read_buf + FIL_PAGE_LSN)
<< "; page number (if stored to page"
<< " already) " << mach_read_from_4(read_buf + FIL_PAGE_OFFSET)
<< "; space id (if stored to page already) "
<< mach_read_from_4(read_buf + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID);
} else {
const uint32_t crc32 = buf_calc_page_crc32(read_buf);
const uint32_t crc32_legacy = buf_calc_page_crc32(read_buf, true);
ib::info(ER_IB_MSG_53)
<< "Uncompressed page, stored checksum in field1 "
<< mach_read_from_4(read_buf + FIL_PAGE_SPACE_OR_CHKSUM)
<< ", calculated checksums for field1: "
<< buf_checksum_algorithm_name(SRV_CHECKSUM_ALGORITHM_CRC32) << " "
<< crc32 << "/" << crc32_legacy << ", "
<< buf_checksum_algorithm_name(SRV_CHECKSUM_ALGORITHM_INNODB) << " "
<< buf_calc_page_new_checksum(read_buf) << ", "
<< buf_checksum_algorithm_name(SRV_CHECKSUM_ALGORITHM_NONE) << " "
<< BUF_NO_CHECKSUM_MAGIC << ", stored checksum in field2 "
<< mach_read_from_4(read_buf + page_size.logical() -
FIL_PAGE_END_LSN_OLD_CHKSUM)
<< ", calculated checksums for field2: "
<< buf_checksum_algorithm_name(SRV_CHECKSUM_ALGORITHM_CRC32) << " "
<< crc32 << "/" << crc32_legacy << ", "
<< buf_checksum_algorithm_name(SRV_CHECKSUM_ALGORITHM_INNODB) << " "
<< buf_calc_page_old_checksum(read_buf) << ", "
<< buf_checksum_algorithm_name(SRV_CHECKSUM_ALGORITHM_NONE) << " "
<< BUF_NO_CHECKSUM_MAGIC << ", page LSN "
<< mach_read_from_4(read_buf + FIL_PAGE_LSN) << " "
<< mach_read_from_4(read_buf + FIL_PAGE_LSN + 4)
<< ", low 4 bytes of LSN at page end "
<< mach_read_from_4(read_buf + page_size.logical() -
FIL_PAGE_END_LSN_OLD_CHKSUM + 4)
<< ", page number (if stored to page already) "
<< mach_read_from_4(read_buf + FIL_PAGE_OFFSET)
<< ", space id (if created with >= MySQL-4.1.1"
" and stored already) "
<< mach_read_from_4(read_buf + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID);
}
#ifndef UNIV_HOTBACKUP
if (mach_read_from_2(read_buf + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_TYPE) ==
TRX_UNDO_INSERT) {
fprintf(stderr, "InnoDB: Page may be an insert undo log page\n");
} else if (mach_read_from_2(read_buf + TRX_UNDO_PAGE_HDR +
TRX_UNDO_PAGE_TYPE) == TRX_UNDO_UPDATE) {
fprintf(stderr, "InnoDB: Page may be an update undo log page\n");
}
#endif /* !UNIV_HOTBACKUP */
switch (fil_page_get_type(read_buf)) {
space_index_t index_id;
case FIL_PAGE_INDEX:
case FIL_PAGE_RTREE:
index_id = btr_page_get_index_id(read_buf);
fprintf(stderr,
"InnoDB: Page may be an index page where"
" index id is " IB_ID_FMT "\n",
index_id);
break;
case FIL_PAGE_INODE:
fputs("InnoDB: Page may be an 'inode' page\n", stderr);
break;
case FIL_PAGE_IBUF_FREE_LIST:
fputs("InnoDB: Page may be an insert buffer free list page\n", stderr);
break;
case FIL_PAGE_TYPE_ALLOCATED:
fputs("InnoDB: Page may be a freshly allocated page\n", stderr);
break;
case FIL_PAGE_IBUF_BITMAP:
fputs("InnoDB: Page may be an insert buffer bitmap page\n", stderr);
break;
case FIL_PAGE_TYPE_SYS:
fputs("InnoDB: Page may be a system page\n", stderr);
break;
case FIL_PAGE_TYPE_TRX_SYS:
fputs("InnoDB: Page may be a transaction system page\n", stderr);
break;
case FIL_PAGE_TYPE_FSP_HDR:
fputs("InnoDB: Page may be a file space header page\n", stderr);
break;
case FIL_PAGE_TYPE_XDES:
fputs("InnoDB: Page may be an extent descriptor page\n", stderr);
break;
case FIL_PAGE_TYPE_BLOB:
fputs("InnoDB: Page may be a BLOB page\n", stderr);
break;
case FIL_PAGE_SDI_BLOB:
fputs("InnoDB: Page may be a SDI BLOB page\n", stderr);
break;
case FIL_PAGE_TYPE_ZBLOB:
case FIL_PAGE_TYPE_ZBLOB2:
fputs("InnoDB: Page may be a compressed BLOB page\n", stderr);
break;
case FIL_PAGE_SDI_ZBLOB:
fputs("InnoDB: Page may be a compressed SDI BLOB page\n", stderr);
break;
case FIL_PAGE_TYPE_RSEG_ARRAY:
fputs("InnoDB: Page may be a Rollback Segment Array page\n", stderr);
break;
}
ut_ad(flags & BUF_PAGE_PRINT_NO_CRASH);
}
#ifndef UNIV_HOTBACKUP
#ifdef PFS_GROUP_BUFFER_SYNC
#ifndef PFS_SKIP_BUFFER_MUTEX_RWLOCK
extern mysql_pfs_key_t buffer_block_mutex_key;
#endif /* !PFS_SKIP_BUFFER_MUTEX_RWLOCK */
/** This function registers mutexes and rwlocks in buffer blocks with
performance schema. If PFS_MAX_BUFFER_MUTEX_LOCK_REGISTER is
defined to be a value less than chunk->size, then only mutexes
and rwlocks in the first PFS_MAX_BUFFER_MUTEX_LOCK_REGISTER
blocks are registered. */
static void pfs_register_buffer_block(
buf_chunk_t *chunk) /*!< in/out: chunk of buffers */
{
buf_block_t *block;
ulint num_to_register;
block = chunk->blocks;
num_to_register = std::min(chunk->size, PFS_MAX_BUFFER_MUTEX_LOCK_REGISTER);
for (ulint i = 0; i < num_to_register; i++) {
#ifdef UNIV_PFS_MUTEX
BPageMutex *mutex;
mutex = &block->mutex;
#ifndef PFS_SKIP_BUFFER_MUTEX_RWLOCK
mutex->pfs_add(buffer_block_mutex_key);
#endif /* !PFS_SKIP_BUFFER_MUTEX_RWLOCK */
#endif /* UNIV_PFS_MUTEX */
rw_lock_t *rwlock;
#ifdef UNIV_PFS_RWLOCK
rwlock = &block->lock;
ut_a(!rwlock->pfs_psi);
#ifndef PFS_SKIP_BUFFER_MUTEX_RWLOCK
rwlock->pfs_psi = (PSI_server)
? PSI_server->init_rwlock(buf_block_lock_key, rwlock)
: NULL;
#else
rwlock->pfs_psi =
(PSI_server) ? PSI_server->init_rwlock(PFS_NOT_INSTRUMENTED, rwlock)
: NULL;
#endif /* !PFS_SKIP_BUFFER_MUTEX_RWLOCK */
#ifdef UNIV_DEBUG
rwlock = &block->debug_latch;
ut_a(!rwlock->pfs_psi);
rwlock->pfs_psi = (PSI_server) ? PSI_server->init_rwlock(
buf_block_debug_latch_key, rwlock)
: NULL;
#endif /* UNIV_DEBUG */
#endif /* UNIV_PFS_RWLOCK */
block++;
}
}
#endif /* PFS_GROUP_BUFFER_SYNC */
/** Initializes a buffer control block when the buf_pool is created. */
static void buf_block_init(
buf_pool_t *buf_pool, /*!< in: buffer pool instance */
buf_block_t *block, /*!< in: pointer to control block */
byte *frame) /*!< in: pointer to buffer frame */
{
UNIV_MEM_DESC(frame, UNIV_PAGE_SIZE);
/* This function should only be executed at database startup or by
buf_pool_resize(). Either way, adaptive hash index must not exist. */
block->ahi.assert_empty_on_init();
block->frame = frame;
block->page.buf_pool_index = buf_pool_index(buf_pool);
block->page.state = BUF_BLOCK_NOT_USED;
block->page.buf_fix_count.store(0);
block->page.init_io_fix();
block->page.reset_flush_observer();
block->page.m_space = nullptr;
block->page.m_version = 0;
block->modify_clock = 0;
ut_d(block->page.file_page_was_freed = false);
block->ahi.index = nullptr;
block->made_dirty_with_no_latch = false;
ut_d(block->page.in_page_hash = false);
ut_d(block->page.in_zip_hash = false);
ut_d(block->page.in_flush_list = false);
ut_d(block->page.in_free_list = false);
ut_d(block->page.in_LRU_list = false);
ut_d(block->in_unzip_LRU_list = false);
ut_d(block->in_withdraw_list = false);
page_zip_des_init(&block->page.zip);
mutex_create(LATCH_ID_BUF_BLOCK_MUTEX, &block->mutex);
#if defined PFS_SKIP_BUFFER_MUTEX_RWLOCK || defined PFS_GROUP_BUFFER_SYNC
/* If PFS_SKIP_BUFFER_MUTEX_RWLOCK is defined, skip registration
of buffer block rwlock with performance schema.
If PFS_GROUP_BUFFER_SYNC is defined, skip the registration
since buffer block rwlock will be registered later in
pfs_register_buffer_block(). */
rw_lock_create(PFS_NOT_INSTRUMENTED, &block->lock, LATCH_ID_BUF_BLOCK_LOCK);
ut_d(rw_lock_create(PFS_NOT_INSTRUMENTED, &block->debug_latch,
LATCH_ID_BUF_BLOCK_DEBUG));
#else /* PFS_SKIP_BUFFER_MUTEX_RWLOCK || PFS_GROUP_BUFFER_SYNC */
rw_lock_create(buf_block_lock_key, &block->lock, LATCH_ID_BUF_BLOCK_LOCK);
ut_d(rw_lock_create(buf_block_debug_latch_key, &block->debug_latch,
LATCH_ID_BUF_BLOCK_DEBUG));
#endif /* PFS_SKIP_BUFFER_MUTEX_RWLOCK || PFS_GROUP_BUFFER_SYNC */
block->lock.is_block_lock = true;
ut_ad(rw_lock_validate(&(block->lock)));
}
/* We maintain our private view of innobase_should_madvise_buf_pool() which we
initialize at the beginning of buf_pool_init() and then update when the
@@global.innodb_buffer_pool_in_core_file changes.
Changes to buf_pool_should_madvise are protected by holding chunk_mutex for all
buf_pool_t instances.
This way, even if @@global.innodb_buffer_pool_in_core_file changes during
execution of buf_pool_init() (unlikely) or during buf_pool_resize(), we will use
a single consistent value for all (de)allocated chunks.
The function buf_pool_update_madvise() handles updating buf_pool_should_madvise
in reaction to changes to @@global.innodb_buffer_pool_in_core_file and makes
sure before releasing chunk_mutex-es that all chunks are properly madvised
according to new value.
It is important that initial value of this variable is `false` and not `true`,
as on some platforms which do not support madvise() or MADV_DONT_DUMP we need to
avoid taking any actions which might trigger a warning or disabling @@core_file.
*/
static bool buf_pool_should_madvise = false;
// Doxygen gets confused by buf_chunk_t somehow.
//! @cond
/* Implementation of buf_chunk_t's methods */
/** Advices the OS that this chunk should be dumped to a core file.
Emits a warning to the log if could not succeed.
@return true iff succeeded, false if no OS support or failed */
bool buf_chunk_t::madvise_dump() {
#ifdef HAVE_MADV_DONTDUMP
const auto low_level_info =
ut::large_page_low_level_info(this->mem, ut::fallback_to_normal_page_t{});
if (madvise(low_level_info.base_ptr, low_level_info.allocation_size,
MADV_DODUMP)) {
ib::warn(ER_IB_MSG_MADVISE_FAILED, low_level_info.base_ptr,
low_level_info.allocation_size, "MADV_DODUMP", strerror(errno));
return false;
}
return true;
#else /* HAVE_MADV_DONTDUMP */
ib::warn(ER_IB_MSG_MADV_DONTDUMP_UNSUPPORTED);
return false;
#endif /* HAVE_MADV_DONTDUMP */
}
/** Advices the OS that this chunk should not be dumped to a core file.
Emits a warning to the log if could not succeed.
@return true iff succeeded, false if no OS support or failed */
bool buf_chunk_t::madvise_dont_dump() {
#ifdef HAVE_MADV_DONTDUMP
const auto low_level_info =
ut::large_page_low_level_info(this->mem, ut::fallback_to_normal_page_t{});
if (madvise(low_level_info.base_ptr, low_level_info.allocation_size,
MADV_DONTDUMP)) {
ib::warn(ER_IB_MSG_MADVISE_FAILED, low_level_info.base_ptr,
low_level_info.allocation_size, "MADV_DONTDUMP", strerror(errno));
return false;
}
return true;
#else /* HAVE_MADV_DONTDUMP */
ib::warn(ER_IB_MSG_MADV_DONTDUMP_UNSUPPORTED);
return false;
#endif /* HAVE_MADV_DONTDUMP */
}
/* End of implementation of buf_chunk_t's methods */
//! @endcond
/* Implementation of buf_pool_t's methods */
bool buf_pool_t::allocate_chunk(ulonglong mem_size, buf_chunk_t *chunk) {
ut_ad(mutex_own(&chunks_mutex));
chunk->mem = static_cast<uint8_t *>(ut::malloc_large_page_withkey(
ut::make_psi_memory_key(mem_key_buf_buf_pool), mem_size,
ut::fallback_to_normal_page_t{}));
if (chunk->mem == nullptr) {
return false;
}
/* Dump core without large memory buffers */
if (buf_pool_should_madvise) {
if (!chunk->madvise_dont_dump()) {
innobase_disable_core_dump();
}
}
#ifdef HAVE_LIBNUMA
if (srv_numa_interleave) {
const auto low_level_info = ut::large_page_low_level_info(
chunk->mem, ut::fallback_to_normal_page_t{});
struct bitmask *numa_nodes = numa_get_mems_allowed();
int st = mbind(low_level_info.base_ptr, low_level_info.allocation_size,
MPOL_INTERLEAVE, numa_nodes->maskp, numa_nodes->size,
MPOL_MF_MOVE);
if (st != 0) {
ib::warn(ER_IB_MSG_54, low_level_info.base_ptr,
low_level_info.allocation_size, "MPOL_INTERLEAVE",
"MPOL_MF_MOVE", strerror(errno));
}
numa_bitmask_free(numa_nodes);
}
#endif /* HAVE_LIBNUMA */
return true;
}
void buf_pool_t::deallocate_chunk(buf_chunk_t *chunk) {
ut_ad(mutex_own(&chunks_mutex));
/* Undo the effect of the earlier MADV_DONTDUMP */
if (buf_pool_should_madvise) {
if (!chunk->madvise_dump()) {
innobase_disable_core_dump();
}
}
ut::free_large_page(chunk->mem, ut::fallback_to_normal_page_t{});
}
bool buf_pool_t::madvise_dump() {
ut_ad(mutex_own(&chunks_mutex));
for (buf_chunk_t *chunk = chunks; chunk < chunks + n_chunks; chunk++) {
if (!chunk->madvise_dump()) {
return false;
}
}
return true;
}
bool buf_pool_t::madvise_dont_dump() {