1 /*****************************************************************************
2
3 Copyright (c) 1995, 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 fsp/fsp0fsp.cc
29 File space management
30
31 Created 11/29/1995 Heikki Tuuri
32 ***********************************************************************/
33
34 #include "ha_prototypes.h"
35
36 #include "fsp0fsp.h"
37
38 #ifdef UNIV_NONINL
39 #include "fsp0fsp.ic"
40 #endif
41
42 #ifdef UNIV_HOTBACKUP
43 # include "fut0lst.h"
44 #else /* UNIV_HOTBACKUP */
45 #include "buf0buf.h"
46 #include "fil0fil.h"
47 #include "mtr0log.h"
48 #include "ut0byte.h"
49 #include "page0page.h"
50 #include "fut0fut.h"
51 #include "srv0srv.h"
52 #include "srv0start.h"
53 #include "ibuf0ibuf.h"
54 #include "btr0btr.h"
55 #include "btr0sea.h"
56 #include "dict0boot.h"
57 #include "log0log.h"
58 #include "fsp0sysspace.h"
59 #include "dict0mem.h"
60 #include "fsp0types.h"
61
62 #include <my_aes.h>
63
64 /** Returns an extent to the free list of a space.
65 @param[in] page_id page id in the extent
66 @param[in] page_size page size
67 @param[in,out] mtr mini-transaction */
68 static
69 void
70 fsp_free_extent(
71 const page_id_t& page_id,
72 const page_size_t& page_size,
73 mtr_t* mtr);
74
75 /********************************************************************//**
76 Marks a page used. The page must reside within the extents of the given
77 segment. */
78 static
79 void
80 fseg_mark_page_used(
81 /*================*/
82 fseg_inode_t* seg_inode,/*!< in: segment inode */
83 ulint page, /*!< in: page offset */
84 xdes_t* descr, /*!< in: extent descriptor */
85 mtr_t* mtr); /*!< in/out: mini-transaction */
86
87 /** Returns the first extent descriptor for a segment.
88 We think of the extent lists of the segment catenated in the order
89 FSEG_FULL -> FSEG_NOT_FULL -> FSEG_FREE.
90 @param[in] inode segment inode
91 @param[in] space_id space id
92 @param[in] page_size page size
93 @param[in,out] mtr mini-transaction
94 @return the first extent descriptor, or NULL if none */
95 static
96 xdes_t*
97 fseg_get_first_extent(
98 fseg_inode_t* inode,
99 ulint space_id,
100 const page_size_t& page_size,
101 mtr_t* mtr);
102
103 /** Put new extents to the free list if there are free extents above the free
104 limit. If an extent happens to contain an extent descriptor page, the extent
105 is put to the FSP_FREE_FRAG list with the page marked as used.
106 @param[in] init_space true if this is a single-table tablespace
107 and we are only initializing the first extent and the first bitmap pages;
108 then we will not allocate more extents
109 @param[in,out] space tablespace
110 @param[in,out] header tablespace header
111 @param[in,out] mtr mini-transaction */
112 static UNIV_COLD
113 void
114 fsp_fill_free_list(
115 bool init_space,
116 fil_space_t* space,
117 fsp_header_t* header,
118 mtr_t* mtr);
119
120 /** Allocates a single free page from a segment.
121 This function implements the intelligent allocation strategy which tries
122 to minimize file space fragmentation.
123 @param[in,out] space tablespace
124 @param[in] page_size page size
125 @param[in,out] seg_inode segment inode
126 @param[in] hint hint of which page would be desirable
127 @param[in] direction if the new page is needed because of
128 an index page split, and records are inserted there in order, into which
129 direction they go alphabetically: FSP_DOWN, FSP_UP, FSP_NO_DIR
130 @param[in] rw_latch RW_SX_LATCH, RW_X_LATCH
131 @param[in,out] mtr mini-transaction
132 @param[in,out] init_mtr mtr or another mini-transaction in
133 which the page should be initialized. If init_mtr != mtr, but the page is
134 already latched in mtr, do not initialize the page
135 @param[in] has_done_reservation TRUE if the space has already been
136 reserved, in this case we will never return NULL
137 @retval NULL if no page could be allocated
138 @retval block rw_lock_x_lock_count(&block->lock) == 1 if allocation succeeded
139 (init_mtr == mtr, or the page was not previously freed in mtr)
140 @retval block (not allocated or initialized) otherwise */
141 static
142 buf_block_t*
143 fseg_alloc_free_page_low(
144 fil_space_t* space,
145 const page_size_t& page_size,
146 fseg_inode_t* seg_inode,
147 ulint hint,
148 byte direction,
149 rw_lock_type_t rw_latch,
150 mtr_t* mtr,
151 mtr_t* init_mtr
152 #ifdef UNIV_DEBUG
153 , ibool has_done_reservation
154 #endif /* UNIV_DEBUG */
155 )
156 MY_ATTRIBUTE((warn_unused_result));
157
158 /** Gets a pointer to the space header and x-locks its page.
159 @param[in] id space id
160 @param[in] page_size page size
161 @param[in,out] mtr mini-transaction
162 @return pointer to the space header, page x-locked */
163 UNIV_INLINE
164 fsp_header_t*
fsp_get_space_header(ulint id,const page_size_t & page_size,mtr_t * mtr)165 fsp_get_space_header(
166 ulint id,
167 const page_size_t& page_size,
168 mtr_t* mtr)
169 {
170 buf_block_t* block;
171 fsp_header_t* header;
172
173 ut_ad(id != 0 || !page_size.is_compressed());
174
175 block = buf_page_get(page_id_t(id, 0), page_size, RW_SX_LATCH, mtr);
176 header = FSP_HEADER_OFFSET + buf_block_get_frame(block);
177 buf_block_dbg_add_level(block, SYNC_FSP_PAGE);
178
179 ut_ad(id == mach_read_from_4(FSP_SPACE_ID + header));
180 #ifdef UNIV_DEBUG
181 const ulint flags = mach_read_from_4(FSP_SPACE_FLAGS + header);
182 ut_ad(page_size_t(flags).equals_to(page_size));
183 #endif /* UNIV_DEBUG */
184 return(header);
185 }
186
187 /** Convert a 32 bit integer tablespace flags to the 32 bit table flags.
188 This can only be done for a tablespace that was built as a file-per-table
189 tablespace. Note that the fsp_flags cannot show the difference between a
190 Compact and Redundant table, so an extra Compact boolean must be supplied.
191 Low order bit
192 | REDUNDANT | COMPACT | COMPRESSED | DYNAMIC
193 fil_space_t::flags | 0 | 0 | 1 | 1
194 dict_table_t::flags | 0 | 1 | 1 | 1
195 @param[in] fsp_flags fil_space_t::flags
196 @param[in] compact true if not Redundant row format
197 @return tablespace flags (fil_space_t::flags) */
198 ulint
fsp_flags_to_dict_tf(ulint fsp_flags,bool compact)199 fsp_flags_to_dict_tf(
200 ulint fsp_flags,
201 bool compact)
202 {
203 /* If the table in this file-per-table tablespace is Compact
204 row format, the low order bit will not indicate Compact. */
205 bool post_antelope = FSP_FLAGS_GET_POST_ANTELOPE(fsp_flags);
206 ulint zip_ssize = FSP_FLAGS_GET_ZIP_SSIZE(fsp_flags);
207 bool atomic_blobs = FSP_FLAGS_HAS_ATOMIC_BLOBS(fsp_flags);
208 bool data_dir = FSP_FLAGS_HAS_DATA_DIR(fsp_flags);
209 bool shared_space = FSP_FLAGS_GET_SHARED(fsp_flags);
210 /* FSP_FLAGS_GET_TEMPORARY(fsp_flags) does not have an equivalent
211 flag position in the table flags. But it would go into flags2 if
212 any code is created where that is needed. */
213
214 ulint flags = dict_tf_init(post_antelope | compact, zip_ssize,
215 atomic_blobs, data_dir, shared_space);
216
217 return(flags);
218 }
219 #endif /* !UNIV_HOTBACKUP */
220
221 /** Validate the tablespace flags.
222 These flags are stored in the tablespace header at offset FSP_SPACE_FLAGS.
223 They should be 0 for ROW_FORMAT=COMPACT and ROW_FORMAT=REDUNDANT.
224 The newer row formats, COMPRESSED and DYNAMIC, use a file format > Antelope
225 so they should have a file format number plus the DICT_TF_COMPACT bit set.
226 @param[in] flags Tablespace flags
227 @return true if valid, false if not */
228 bool
fsp_flags_is_valid(ulint flags)229 fsp_flags_is_valid(
230 ulint flags)
231 {
232 bool post_antelope = FSP_FLAGS_GET_POST_ANTELOPE(flags);
233 ulint zip_ssize = FSP_FLAGS_GET_ZIP_SSIZE(flags);
234 bool atomic_blobs = FSP_FLAGS_HAS_ATOMIC_BLOBS(flags);
235 ulint page_ssize = FSP_FLAGS_GET_PAGE_SSIZE(flags);
236 bool has_data_dir = FSP_FLAGS_HAS_DATA_DIR(flags);
237 bool is_shared = FSP_FLAGS_GET_SHARED(flags);
238 bool is_temp = FSP_FLAGS_GET_TEMPORARY(flags);
239 bool is_encryption = FSP_FLAGS_GET_ENCRYPTION(flags);
240
241 ulint unused = FSP_FLAGS_GET_UNUSED(flags);
242
243 DBUG_EXECUTE_IF("fsp_flags_is_valid_failure", return(false););
244
245 /* The Antelope row formats REDUNDANT and COMPACT did
246 not use tablespace flags, so the entire 4-byte field
247 is zero for Antelope row formats. */
248 if (flags == 0) {
249 return(true);
250 }
251
252 /* Barracuda row formats COMPRESSED and DYNAMIC use a feature called
253 ATOMIC_BLOBS which builds on the page structure introduced for the
254 COMPACT row format by allowing long fields to be broken into prefix
255 and externally stored parts. So if it is Post_antelope, it uses
256 Atomic BLOBs. */
257 if (post_antelope != atomic_blobs) {
258 return(false);
259 }
260
261 /* Make sure there are no bits that we do not know about. */
262 if (unused != 0) {
263 return(false);
264 }
265
266 /* The zip ssize can be zero if it is other than compressed row format,
267 or it could be from 1 to the max. */
268 if (zip_ssize > PAGE_ZIP_SSIZE_MAX) {
269 return(false);
270 }
271
272 /* The actual page size must be within 4k and 16K (3 =< ssize =< 5). */
273 if (page_ssize != 0
274 && (page_ssize < UNIV_PAGE_SSIZE_MIN
275 || page_ssize > UNIV_PAGE_SSIZE_MAX)) {
276 return(false);
277 }
278
279 /* Only single-table tablespaces use the DATA DIRECTORY clause.
280 It is not compatible with the TABLESPACE clause. Nor is it
281 compatible with the TEMPORARY clause. */
282 if (has_data_dir && (is_shared || is_temp)) {
283 return(false);
284 }
285
286 /* Only single-table and not temp tablespaces use the encryption
287 clause. */
288 if (is_encryption && (is_shared || is_temp)) {
289 return(false);
290 }
291
292 #if UNIV_FORMAT_MAX != UNIV_FORMAT_B
293 # error UNIV_FORMAT_MAX != UNIV_FORMAT_B, Add more validations.
294 #endif
295 #if FSP_FLAGS_POS_UNUSED != 14
296 # error You have added a new FSP_FLAG without adding a validation check.
297 #endif
298
299 return(true);
300 }
301
302 /** Check if tablespace is system temporary.
303 @param[in] space_id tablespace ID
304 @return true if tablespace is system temporary. */
305 bool
fsp_is_system_temporary(ulint space_id)306 fsp_is_system_temporary(
307 ulint space_id)
308 {
309 return(space_id == srv_tmp_space.space_id());
310 }
311
312 /** Check if checksum is disabled for the given space.
313 @param[in] space_id tablespace ID
314 @return true if checksum is disabled for given space. */
315 bool
fsp_is_checksum_disabled(ulint space_id)316 fsp_is_checksum_disabled(
317 ulint space_id)
318 {
319 return(fsp_is_system_temporary(space_id));
320 }
321
322 /** Check if tablespace is file-per-table.
323 @param[in] space_id tablespace ID
324 @param[in] fsp_flags tablespace flags
325 @return true if tablespace is file-per-table. */
326 bool
fsp_is_file_per_table(ulint space_id,ulint fsp_flags)327 fsp_is_file_per_table(
328 ulint space_id,
329 ulint fsp_flags)
330 {
331 return(!is_system_tablespace(space_id)
332 && !fsp_is_shared_tablespace(fsp_flags));
333 }
334 #ifndef UNIV_HOTBACKUP
335 #ifdef UNIV_DEBUG
336
337 /** Skip some of the sanity checks that are time consuming even in debug mode
338 and can affect frequent verification runs that are done to ensure stability of
339 the product.
340 @return true if check should be skipped for given space. */
341 bool
fsp_skip_sanity_check(ulint space_id)342 fsp_skip_sanity_check(
343 ulint space_id)
344 {
345 return(srv_skip_temp_table_checks_debug
346 && fsp_is_system_temporary(space_id));
347 }
348
349 #endif /* UNIV_DEBUG */
350
351 /**********************************************************************//**
352 Gets a descriptor bit of a page.
353 @return TRUE if free */
354 UNIV_INLINE
355 ibool
xdes_mtr_get_bit(const xdes_t * descr,ulint bit,ulint offset,mtr_t * mtr)356 xdes_mtr_get_bit(
357 /*=============*/
358 const xdes_t* descr, /*!< in: descriptor */
359 ulint bit, /*!< in: XDES_FREE_BIT or XDES_CLEAN_BIT */
360 ulint offset, /*!< in: page offset within extent:
361 0 ... FSP_EXTENT_SIZE - 1 */
362 mtr_t* mtr) /*!< in: mini-transaction */
363 {
364 ut_ad(mtr->is_active());
365 ut_ad(mtr_memo_contains_page(mtr, descr, MTR_MEMO_PAGE_SX_FIX));
366
367 return(xdes_get_bit(descr, bit, offset));
368 }
369
370 /**********************************************************************//**
371 Sets a descriptor bit of a page. */
372 UNIV_INLINE
373 void
xdes_set_bit(xdes_t * descr,ulint bit,ulint offset,ibool val,mtr_t * mtr)374 xdes_set_bit(
375 /*=========*/
376 xdes_t* descr, /*!< in: descriptor */
377 ulint bit, /*!< in: XDES_FREE_BIT or XDES_CLEAN_BIT */
378 ulint offset, /*!< in: page offset within extent:
379 0 ... FSP_EXTENT_SIZE - 1 */
380 ibool val, /*!< in: bit value */
381 mtr_t* mtr) /*!< in/out: mini-transaction */
382 {
383 ulint index;
384 ulint byte_index;
385 ulint bit_index;
386 ulint descr_byte;
387
388 ut_ad(mtr_memo_contains_page(mtr, descr, MTR_MEMO_PAGE_SX_FIX));
389 ut_ad((bit == XDES_FREE_BIT) || (bit == XDES_CLEAN_BIT));
390 ut_ad(offset < FSP_EXTENT_SIZE);
391
392 index = bit + XDES_BITS_PER_PAGE * offset;
393
394 byte_index = index / 8;
395 bit_index = index % 8;
396
397 descr_byte = mach_read_from_1(descr + XDES_BITMAP + byte_index);
398 descr_byte = ut_bit_set_nth(descr_byte, bit_index, val);
399
400 mlog_write_ulint(descr + XDES_BITMAP + byte_index, descr_byte,
401 MLOG_1BYTE, mtr);
402 }
403
404 /**********************************************************************//**
405 Looks for a descriptor bit having the desired value. Starts from hint
406 and scans upward; at the end of the extent the search is wrapped to
407 the start of the extent.
408 @return bit index of the bit, ULINT_UNDEFINED if not found */
409 UNIV_INLINE
410 ulint
xdes_find_bit(xdes_t * descr,ulint bit,ibool val,ulint hint,mtr_t * mtr)411 xdes_find_bit(
412 /*==========*/
413 xdes_t* descr, /*!< in: descriptor */
414 ulint bit, /*!< in: XDES_FREE_BIT or XDES_CLEAN_BIT */
415 ibool val, /*!< in: desired bit value */
416 ulint hint, /*!< in: hint of which bit position would
417 be desirable */
418 mtr_t* mtr) /*!< in/out: mini-transaction */
419 {
420 ulint i;
421
422 ut_ad(descr && mtr);
423 ut_ad(val <= TRUE);
424 ut_ad(hint < FSP_EXTENT_SIZE);
425 ut_ad(mtr_memo_contains_page(mtr, descr, MTR_MEMO_PAGE_SX_FIX));
426 for (i = hint; i < FSP_EXTENT_SIZE; i++) {
427 if (val == xdes_mtr_get_bit(descr, bit, i, mtr)) {
428
429 return(i);
430 }
431 }
432
433 for (i = 0; i < hint; i++) {
434 if (val == xdes_mtr_get_bit(descr, bit, i, mtr)) {
435
436 return(i);
437 }
438 }
439
440 return(ULINT_UNDEFINED);
441 }
442
443 /**********************************************************************//**
444 Returns the number of used pages in a descriptor.
445 @return number of pages used */
446 UNIV_INLINE
447 ulint
xdes_get_n_used(const xdes_t * descr,mtr_t * mtr)448 xdes_get_n_used(
449 /*============*/
450 const xdes_t* descr, /*!< in: descriptor */
451 mtr_t* mtr) /*!< in/out: mini-transaction */
452 {
453 ulint count = 0;
454
455 ut_ad(descr && mtr);
456 ut_ad(mtr_memo_contains_page(mtr, descr, MTR_MEMO_PAGE_SX_FIX));
457 for (ulint i = 0; i < FSP_EXTENT_SIZE; ++i) {
458 if (FALSE == xdes_mtr_get_bit(descr, XDES_FREE_BIT, i, mtr)) {
459 count++;
460 }
461 }
462
463 return(count);
464 }
465
466 /**********************************************************************//**
467 Returns true if extent contains no used pages.
468 @return TRUE if totally free */
469 UNIV_INLINE
470 ibool
xdes_is_free(const xdes_t * descr,mtr_t * mtr)471 xdes_is_free(
472 /*=========*/
473 const xdes_t* descr, /*!< in: descriptor */
474 mtr_t* mtr) /*!< in/out: mini-transaction */
475 {
476 if (0 == xdes_get_n_used(descr, mtr)) {
477
478 return(TRUE);
479 }
480
481 return(FALSE);
482 }
483
484 /**********************************************************************//**
485 Returns true if extent contains no free pages.
486 @return TRUE if full */
487 UNIV_INLINE
488 ibool
xdes_is_full(const xdes_t * descr,mtr_t * mtr)489 xdes_is_full(
490 /*=========*/
491 const xdes_t* descr, /*!< in: descriptor */
492 mtr_t* mtr) /*!< in/out: mini-transaction */
493 {
494 if (FSP_EXTENT_SIZE == xdes_get_n_used(descr, mtr)) {
495
496 return(TRUE);
497 }
498
499 return(FALSE);
500 }
501
502 /**********************************************************************//**
503 Sets the state of an xdes. */
504 UNIV_INLINE
505 void
xdes_set_state(xdes_t * descr,ulint state,mtr_t * mtr)506 xdes_set_state(
507 /*===========*/
508 xdes_t* descr, /*!< in/out: descriptor */
509 ulint state, /*!< in: state to set */
510 mtr_t* mtr) /*!< in/out: mini-transaction */
511 {
512 ut_ad(descr && mtr);
513 ut_ad(state >= XDES_FREE);
514 ut_ad(state <= XDES_FSEG);
515 ut_ad(mtr_memo_contains_page(mtr, descr, MTR_MEMO_PAGE_SX_FIX));
516
517 mlog_write_ulint(descr + XDES_STATE, state, MLOG_4BYTES, mtr);
518 }
519
520 /**********************************************************************//**
521 Gets the state of an xdes.
522 @return state */
523 UNIV_INLINE
524 ulint
xdes_get_state(const xdes_t * descr,mtr_t * mtr)525 xdes_get_state(
526 /*===========*/
527 const xdes_t* descr, /*!< in: descriptor */
528 mtr_t* mtr) /*!< in/out: mini-transaction */
529 {
530 ulint state;
531
532 ut_ad(descr && mtr);
533 ut_ad(mtr_memo_contains_page(mtr, descr, MTR_MEMO_PAGE_SX_FIX));
534
535 state = mach_read_from_4(descr + XDES_STATE);
536 ut_ad(state - 1 < XDES_FSEG);
537 return(state);
538 }
539
540 /**********************************************************************//**
541 Inits an extent descriptor to the free and clean state. */
542 UNIV_INLINE
543 void
xdes_init(xdes_t * descr,mtr_t * mtr)544 xdes_init(
545 /*======*/
546 xdes_t* descr, /*!< in: descriptor */
547 mtr_t* mtr) /*!< in/out: mini-transaction */
548 {
549 ulint i;
550
551 ut_ad(descr && mtr);
552 ut_ad(mtr_memo_contains_page(mtr, descr, MTR_MEMO_PAGE_SX_FIX));
553 ut_ad((XDES_SIZE - XDES_BITMAP) % 4 == 0);
554
555 for (i = XDES_BITMAP; i < XDES_SIZE; i += 4) {
556 mlog_write_ulint(descr + i, 0xFFFFFFFFUL, MLOG_4BYTES, mtr);
557 }
558
559 xdes_set_state(descr, XDES_FREE, mtr);
560 }
561
562 /** Get pointer to a the extent descriptor of a page.
563 @param[in,out] sp_header tablespace header page, x-latched
564 @param[in] space tablespace identifier
565 @param[in] offset page offset
566 @param[in,out] mtr mini-transaction
567 @param[in] init_space whether the tablespace is being initialized
568 @param[out] desc_block descriptor block, or NULL if it is
569 the same as the tablespace header
570 @return pointer to the extent descriptor, NULL if the page does not
571 exist in the space or if the offset exceeds free limit */
UNIV_INLINE(warn_unused_result)572 UNIV_INLINE MY_ATTRIBUTE((warn_unused_result))
573 xdes_t*
574 xdes_get_descriptor_with_space_hdr(
575 fsp_header_t* sp_header,
576 ulint space,
577 ulint offset,
578 mtr_t* mtr,
579 bool init_space = false,
580 buf_block_t** desc_block = NULL)
581 {
582 ulint limit;
583 ulint size;
584 ulint descr_page_no;
585 ulint flags;
586 page_t* descr_page;
587 #ifdef UNIV_DEBUG
588 const fil_space_t* fspace = fil_space_get(space);
589 ut_ad(fspace != NULL);
590 #endif /* UNIV_DEBUG */
591 ut_ad(mtr_memo_contains(mtr, &fspace->latch, MTR_MEMO_X_LOCK));
592 ut_ad(mtr_memo_contains_page(mtr, sp_header, MTR_MEMO_PAGE_SX_FIX));
593 ut_ad(page_offset(sp_header) == FSP_HEADER_OFFSET);
594 /* Read free limit and space size */
595 limit = mach_read_from_4(sp_header + FSP_FREE_LIMIT);
596 size = mach_read_from_4(sp_header + FSP_SIZE);
597 flags = mach_read_from_4(sp_header + FSP_SPACE_FLAGS);
598 ut_ad(limit == fspace->free_limit
599 || (fspace->free_limit == 0
600 && (init_space
601 || fspace->purpose == FIL_TYPE_TEMPORARY
602 || (srv_startup_is_before_trx_rollback_phase
603 && fspace->id <= srv_undo_tablespaces))));
604 ut_ad(size == fspace->size_in_header);
605 ut_ad(flags == fspace->flags);
606
607 if ((offset >= size) || (offset >= limit)) {
608 return(NULL);
609 }
610
611 const page_size_t page_size(flags);
612
613 descr_page_no = xdes_calc_descriptor_page(page_size, offset);
614
615 buf_block_t* block;
616
617 if (descr_page_no == 0) {
618 /* It is on the space header page */
619
620 descr_page = page_align(sp_header);
621 block = NULL;
622 } else {
623 block = buf_page_get(
624 page_id_t(space, descr_page_no), page_size,
625 RW_SX_LATCH, mtr);
626
627 buf_block_dbg_add_level(block, SYNC_FSP_PAGE);
628
629 descr_page = buf_block_get_frame(block);
630 }
631
632 if (desc_block != NULL) {
633 *desc_block = block;
634 }
635
636 return(descr_page + XDES_ARR_OFFSET
637 + XDES_SIZE * xdes_calc_descriptor_index(page_size, offset));
638 }
639
640 /** Gets pointer to a the extent descriptor of a page.
641 The page where the extent descriptor resides is x-locked. If the page offset
642 is equal to the free limit of the space, adds new extents from above the free
643 limit to the space free list, if not free limit == space size. This adding
644 is necessary to make the descriptor defined, as they are uninitialized
645 above the free limit.
646 @param[in] space_id space id
647 @param[in] offset page offset; if equal to the free limit, we
648 try to add new extents to the space free list
649 @param[in] page_size page size
650 @param[in,out] mtr mini-transaction
651 @return pointer to the extent descriptor, NULL if the page does not
652 exist in the space or if the offset exceeds the free limit */
653 xdes_t*
xdes_get_descriptor(ulint space_id,ulint offset,const page_size_t & page_size,mtr_t * mtr)654 xdes_get_descriptor(
655 ulint space_id,
656 ulint offset,
657 const page_size_t& page_size,
658 mtr_t* mtr)
659 {
660 buf_block_t* block;
661 fsp_header_t* sp_header;
662
663 block = buf_page_get(page_id_t(space_id, 0), page_size,
664 RW_SX_LATCH, mtr);
665
666 buf_block_dbg_add_level(block, SYNC_FSP_PAGE);
667
668 sp_header = FSP_HEADER_OFFSET + buf_block_get_frame(block);
669 return(xdes_get_descriptor_with_space_hdr(sp_header, space_id, offset,
670 mtr));
671 }
672
673 /********************************************************************//**
674 Gets pointer to a the extent descriptor if the file address
675 of the descriptor list node is known. The page where the
676 extent descriptor resides is x-locked.
677 @return pointer to the extent descriptor */
678 UNIV_INLINE
679 xdes_t*
xdes_lst_get_descriptor(ulint space,const page_size_t & page_size,fil_addr_t lst_node,mtr_t * mtr)680 xdes_lst_get_descriptor(
681 /*====================*/
682 ulint space, /*!< in: space id */
683 const page_size_t& page_size,
684 fil_addr_t lst_node,/*!< in: file address of the list node
685 contained in the descriptor */
686 mtr_t* mtr) /*!< in/out: mini-transaction */
687 {
688 xdes_t* descr;
689
690 ut_ad(mtr);
691 ut_ad(mtr_memo_contains(mtr, fil_space_get_latch(space, NULL),
692 MTR_MEMO_X_LOCK));
693 descr = fut_get_ptr(space, page_size, lst_node, RW_SX_LATCH, mtr)
694 - XDES_FLST_NODE;
695
696 return(descr);
697 }
698
699 /********************************************************************//**
700 Returns page offset of the first page in extent described by a descriptor.
701 @return offset of the first page in extent */
702 UNIV_INLINE
703 ulint
xdes_get_offset(const xdes_t * descr)704 xdes_get_offset(
705 /*============*/
706 const xdes_t* descr) /*!< in: extent descriptor */
707 {
708 ut_ad(descr);
709
710 return(page_get_page_no(page_align(descr))
711 + ((page_offset(descr) - XDES_ARR_OFFSET) / XDES_SIZE)
712 * FSP_EXTENT_SIZE);
713 }
714 #endif /* !UNIV_HOTBACKUP */
715
716 /***********************************************************//**
717 Inits a file page whose prior contents should be ignored. */
718 static
719 void
fsp_init_file_page_low(buf_block_t * block)720 fsp_init_file_page_low(
721 /*===================*/
722 buf_block_t* block) /*!< in: pointer to a page */
723 {
724 page_t* page = buf_block_get_frame(block);
725 page_zip_des_t* page_zip= buf_block_get_page_zip(block);
726
727 if (!fsp_is_system_temporary(block->page.id.space())) {
728 memset(page, 0, UNIV_PAGE_SIZE);
729 }
730
731 mach_write_to_4(page + FIL_PAGE_OFFSET, block->page.id.page_no());
732 mach_write_to_4(page + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID,
733 block->page.id.space());
734
735 if (page_zip) {
736 memset(page_zip->data, 0, page_zip_get_size(page_zip));
737 memcpy(page_zip->data + FIL_PAGE_OFFSET,
738 page + FIL_PAGE_OFFSET, 4);
739 memcpy(page_zip->data + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID,
740 page + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID, 4);
741 }
742 }
743
744 #ifndef UNIV_HOTBACKUP
745 # ifdef UNIV_DEBUG
746 /** Assert that the mini-transaction is compatible with
747 updating an allocation bitmap page.
748 @param[in] id tablespace identifier
749 @param[in] mtr mini-transaction */
750 static
751 void
fsp_space_modify_check(ulint id,const mtr_t * mtr)752 fsp_space_modify_check(
753 ulint id,
754 const mtr_t* mtr)
755 {
756 switch (mtr->get_log_mode()) {
757 case MTR_LOG_SHORT_INSERTS:
758 case MTR_LOG_NONE:
759 /* These modes are only allowed within a non-bitmap page
760 when there is a higher-level redo log record written. */
761 break;
762 case MTR_LOG_NO_REDO:
763 #ifdef UNIV_DEBUG
764 {
765 const fil_type_t type = fil_space_get_type(id);
766 ut_a(id == srv_tmp_space.space_id()
767 || srv_is_tablespace_truncated(id)
768 || fil_space_is_being_truncated(id)
769 || fil_space_get_flags(id) == ULINT_UNDEFINED
770 || type == FIL_TYPE_TEMPORARY
771 || type == FIL_TYPE_IMPORT
772 || fil_space_is_redo_skipped(id));
773 }
774 #endif /* UNIV_DEBUG */
775 return;
776 case MTR_LOG_ALL:
777 /* We must not write redo log for the shared temporary
778 tablespace. */
779 ut_ad(id != srv_tmp_space.space_id());
780 /* If we write redo log, the tablespace must exist. */
781 ut_ad(fil_space_get_type(id) == FIL_TYPE_TABLESPACE);
782 ut_ad(mtr->is_named_space(id));
783 return;
784 }
785
786 ut_ad(0);
787 }
788 # endif /* UNIV_DEBUG */
789
790 /** Initialize a file page.
791 @param[in,out] block file page
792 @param[in,out] mtr mini-transaction */
793 static
794 void
fsp_init_file_page(buf_block_t * block,mtr_t * mtr)795 fsp_init_file_page(
796 buf_block_t* block,
797 mtr_t* mtr)
798 {
799 fsp_init_file_page_low(block);
800
801 ut_d(fsp_space_modify_check(block->page.id.space(), mtr));
802 mlog_write_initial_log_record(buf_block_get_frame(block),
803 MLOG_INIT_FILE_PAGE2, mtr);
804 }
805 #endif /* !UNIV_HOTBACKUP */
806
807 /***********************************************************//**
808 Parses a redo log record of a file page init.
809 @return end of log record or NULL */
810 byte*
fsp_parse_init_file_page(byte * ptr,byte * end_ptr MY_ATTRIBUTE ((unused)),buf_block_t * block)811 fsp_parse_init_file_page(
812 /*=====================*/
813 byte* ptr, /*!< in: buffer */
814 byte* end_ptr MY_ATTRIBUTE((unused)), /*!< in: buffer end */
815 buf_block_t* block) /*!< in: block or NULL */
816 {
817 ut_ad(ptr != NULL);
818 ut_ad(end_ptr != NULL);
819
820 if (block) {
821 fsp_init_file_page_low(block);
822 }
823
824 return(ptr);
825 }
826
827 /**********************************************************************//**
828 Initializes the fsp system. */
829 void
fsp_init(void)830 fsp_init(void)
831 /*==========*/
832 {
833 /* FSP_EXTENT_SIZE must be a multiple of page & zip size */
834 ut_a(0 == (UNIV_PAGE_SIZE % FSP_EXTENT_SIZE));
835 ut_a(UNIV_PAGE_SIZE);
836
837 #if UNIV_PAGE_SIZE_MAX % FSP_EXTENT_SIZE_MAX
838 # error "UNIV_PAGE_SIZE_MAX % FSP_EXTENT_SIZE_MAX != 0"
839 #endif
840 #if UNIV_ZIP_SIZE_MIN % FSP_EXTENT_SIZE_MIN
841 # error "UNIV_ZIP_SIZE_MIN % FSP_EXTENT_SIZE_MIN != 0"
842 #endif
843
844 /* Does nothing at the moment */
845 }
846
847 /**********************************************************************//**
848 Writes the space id and flags to a tablespace header. The flags contain
849 row type, physical/compressed page size, and logical/uncompressed page
850 size of the tablespace. */
851 void
fsp_header_init_fields(page_t * page,ulint space_id,ulint flags)852 fsp_header_init_fields(
853 /*===================*/
854 page_t* page, /*!< in/out: first page in the space */
855 ulint space_id, /*!< in: space id */
856 ulint flags) /*!< in: tablespace flags (FSP_SPACE_FLAGS) */
857 {
858 ut_a(fsp_flags_is_valid(flags));
859
860 mach_write_to_4(FSP_HEADER_OFFSET + FSP_SPACE_ID + page,
861 space_id);
862 mach_write_to_4(FSP_HEADER_OFFSET + FSP_SPACE_FLAGS + page,
863 flags);
864 }
865
866 #ifndef UNIV_HOTBACKUP
867 /** Get the offset of encrytion information in page 0.
868 @param[in] page_size page size.
869 @return offset on success, otherwise 0. */
870 static
871 ulint
fsp_header_get_encryption_offset(const page_size_t & page_size)872 fsp_header_get_encryption_offset(
873 const page_size_t& page_size)
874 {
875 ulint offset;
876 #ifdef UNIV_DEBUG
877 ulint left_size;
878 #endif
879
880 offset = XDES_ARR_OFFSET + XDES_SIZE * xdes_arr_size(page_size);
881 #ifdef UNIV_DEBUG
882 left_size = page_size.physical() - FSP_HEADER_OFFSET - offset
883 - FIL_PAGE_DATA_END;
884
885 ut_ad(left_size >= ENCRYPTION_INFO_SIZE_V2);
886 #endif
887
888 return offset;
889 }
890
891 /** Fill the encryption info.
892 @param[in] space tablespace
893 @param[in,out] encrypt_info buffer for encrypt key.
894 @return true if success. */
895 bool
fsp_header_fill_encryption_info(fil_space_t * space,byte * encrypt_info)896 fsp_header_fill_encryption_info(
897 fil_space_t* space,
898 byte* encrypt_info)
899 {
900 byte* ptr;
901 lint elen;
902 ulint master_key_id;
903 byte* master_key;
904 byte key_info[ENCRYPTION_KEY_LEN * 2];
905 ulint crc;
906 Encryption::Version version;
907 #ifdef UNIV_ENCRYPT_DEBUG
908 const byte* data;
909 ulint i;
910 #endif
911
912 /* Get master key from key ring */
913 Encryption::get_master_key(&master_key_id, &master_key, &version);
914 if (master_key == NULL) {
915 return(false);
916 }
917
918 memset(encrypt_info, 0, ENCRYPTION_INFO_SIZE_V2);
919 memset(key_info, 0, ENCRYPTION_KEY_LEN * 2);
920
921 /* Use the new master key to encrypt the tablespace
922 key. */
923 ut_ad(encrypt_info != NULL);
924 ptr = encrypt_info;
925
926 /* Write magic header. */
927 if (version == Encryption::ENCRYPTION_VERSION_1) {
928 memcpy(ptr, ENCRYPTION_KEY_MAGIC_V1, ENCRYPTION_MAGIC_SIZE);
929 } else {
930 memcpy(ptr, ENCRYPTION_KEY_MAGIC_V2, ENCRYPTION_MAGIC_SIZE);
931 }
932 ptr += ENCRYPTION_MAGIC_SIZE;
933
934 /* Write master key id. */
935 mach_write_to_4(ptr, master_key_id);
936 ptr += sizeof(ulint);
937
938 /* Write server uuid. */
939 if (version == Encryption::ENCRYPTION_VERSION_2) {
940 memcpy(ptr, Encryption::uuid, ENCRYPTION_SERVER_UUID_LEN);
941 ptr += ENCRYPTION_SERVER_UUID_LEN;
942 }
943
944 /* Write tablespace key to temp space. */
945 memcpy(key_info,
946 space->encryption_key,
947 ENCRYPTION_KEY_LEN);
948
949 /* Write tablespace iv to temp space. */
950 memcpy(key_info + ENCRYPTION_KEY_LEN,
951 space->encryption_iv,
952 ENCRYPTION_KEY_LEN);
953
954 #ifdef UNIV_ENCRYPT_DEBUG
955 fprintf(stderr, "Set %lu:%lu ",space->id,
956 Encryption::master_key_id);
957 for (data = (const byte*) master_key, i = 0;
958 i < ENCRYPTION_KEY_LEN; i++)
959 fprintf(stderr, "%02lx", (ulong)*data++);
960 fprintf(stderr, " ");
961 for (data = (const byte*) space->encryption_key,
962 i = 0; i < ENCRYPTION_KEY_LEN; i++)
963 fprintf(stderr, "%02lx", (ulong)*data++);
964 fprintf(stderr, " ");
965 for (data = (const byte*) space->encryption_iv,
966 i = 0; i < ENCRYPTION_KEY_LEN; i++)
967 fprintf(stderr, "%02lx", (ulong)*data++);
968 fprintf(stderr, "\n");
969 #endif
970 /* Encrypt tablespace key and iv. */
971 elen = my_aes_encrypt(
972 key_info,
973 ENCRYPTION_KEY_LEN * 2,
974 ptr,
975 master_key,
976 ENCRYPTION_KEY_LEN,
977 my_aes_256_ecb,
978 NULL, false);
979
980 if (elen == MY_AES_BAD_DATA) {
981 my_free(master_key);
982 return(false);
983 }
984
985 ptr += ENCRYPTION_KEY_LEN * 2;
986
987 /* Write checksum bytes. */
988 crc = ut_crc32(key_info, ENCRYPTION_KEY_LEN * 2);
989 mach_write_to_4(ptr, crc);
990
991 my_free(master_key);
992 return(true);
993 }
994
995 /** Rotate the encryption info in the space header.
996 @param[in] space tablespace
997 @param[in] encrypt_info buffer for re-encrypt key.
998 @param[in,out] mtr mini-transaction
999 @return true if success. */
1000 bool
fsp_header_rotate_encryption(fil_space_t * space,byte * encrypt_info,mtr_t * mtr)1001 fsp_header_rotate_encryption(
1002 fil_space_t* space,
1003 byte* encrypt_info,
1004 mtr_t* mtr)
1005 {
1006 buf_block_t* block;
1007 ulint offset;
1008 page_t* page;
1009 ulint master_key_id;
1010
1011 ut_ad(mtr);
1012 ut_ad(space->encryption_type != Encryption::NONE);
1013
1014 const page_size_t page_size(space->flags);
1015
1016 DBUG_EXECUTE_IF("fsp_header_rotate_encryption_failure",
1017 return(false););
1018
1019 /* Fill encryption info. */
1020 if (!fsp_header_fill_encryption_info(space,
1021 encrypt_info)) {
1022 return(false);
1023 }
1024
1025 /* Save the encryption info to the page 0. */
1026 block = buf_page_get(page_id_t(space->id, 0),
1027 page_size,
1028 RW_SX_LATCH, mtr);
1029 buf_block_dbg_add_level(block, SYNC_FSP_PAGE);
1030 ut_ad(space->id == page_get_space_id(buf_block_get_frame(block)));
1031
1032 offset = fsp_header_get_encryption_offset(page_size);
1033 ut_ad(offset != 0 && offset < UNIV_PAGE_SIZE);
1034
1035 page = buf_block_get_frame(block);
1036
1037 /* If is in recovering, skip all master key id is rotated
1038 tablespaces. */
1039 master_key_id = mach_read_from_4(
1040 page + offset + ENCRYPTION_MAGIC_SIZE);
1041 if (recv_recovery_is_on()
1042 && master_key_id == Encryption::master_key_id) {
1043 ut_ad(memcmp(page + offset,
1044 ENCRYPTION_KEY_MAGIC_V1,
1045 ENCRYPTION_MAGIC_SIZE) == 0
1046 || memcmp(page + offset,
1047 ENCRYPTION_KEY_MAGIC_V2,
1048 ENCRYPTION_MAGIC_SIZE) == 0);
1049 return(true);
1050 }
1051
1052 mlog_write_string(page + offset,
1053 encrypt_info,
1054 ENCRYPTION_INFO_SIZE_V2,
1055 mtr);
1056
1057 return(true);
1058 }
1059
1060 /** Initializes the space header of a new created space and creates also the
1061 insert buffer tree root if space == 0.
1062 @param[in] space_id space id
1063 @param[in] size current size in blocks
1064 @param[in,out] mtr min-transaction
1065 @return true on success, otherwise false. */
1066 bool
fsp_header_init(ulint space_id,ulint size,mtr_t * mtr)1067 fsp_header_init(
1068 ulint space_id,
1069 ulint size,
1070 mtr_t* mtr)
1071 {
1072 fsp_header_t* header;
1073 buf_block_t* block;
1074 page_t* page;
1075
1076 ut_ad(mtr);
1077
1078 fil_space_t* space = mtr_x_lock_space(space_id, mtr);
1079
1080 const page_id_t page_id(space_id, 0);
1081 const page_size_t page_size(space->flags);
1082
1083 block = buf_page_create(page_id, page_size, mtr);
1084 buf_page_get(page_id, page_size, RW_SX_LATCH, mtr);
1085 buf_block_dbg_add_level(block, SYNC_FSP_PAGE);
1086
1087 space->size_in_header = size;
1088 space->free_len = 0;
1089 space->free_limit = 0;
1090
1091 /* The prior contents of the file page should be ignored */
1092
1093 fsp_init_file_page(block, mtr);
1094 page = buf_block_get_frame(block);
1095
1096 mlog_write_ulint(page + FIL_PAGE_TYPE, FIL_PAGE_TYPE_FSP_HDR,
1097 MLOG_2BYTES, mtr);
1098
1099 header = FSP_HEADER_OFFSET + page;
1100
1101 mlog_write_ulint(header + FSP_SPACE_ID, space_id, MLOG_4BYTES, mtr);
1102 mlog_write_ulint(header + FSP_NOT_USED, 0, MLOG_4BYTES, mtr);
1103
1104 mlog_write_ulint(header + FSP_SIZE, size, MLOG_4BYTES, mtr);
1105 mlog_write_ulint(header + FSP_FREE_LIMIT, 0, MLOG_4BYTES, mtr);
1106 mlog_write_ulint(header + FSP_SPACE_FLAGS, space->flags,
1107 MLOG_4BYTES, mtr);
1108 mlog_write_ulint(header + FSP_FRAG_N_USED, 0, MLOG_4BYTES, mtr);
1109
1110 flst_init(header + FSP_FREE, mtr);
1111 flst_init(header + FSP_FREE_FRAG, mtr);
1112 flst_init(header + FSP_FULL_FRAG, mtr);
1113 flst_init(header + FSP_SEG_INODES_FULL, mtr);
1114 flst_init(header + FSP_SEG_INODES_FREE, mtr);
1115
1116 mlog_write_ull(header + FSP_SEG_ID, 1, mtr);
1117
1118 fsp_fill_free_list(!is_system_tablespace(space_id),
1119 space, header, mtr);
1120
1121 /* For encryption tablespace, we need to save the encryption
1122 info to the page 0. */
1123 if (FSP_FLAGS_GET_ENCRYPTION(space->flags)) {
1124 ulint offset = fsp_header_get_encryption_offset(page_size);
1125 byte encryption_info[ENCRYPTION_INFO_SIZE_V2];
1126
1127 if (offset == 0)
1128 return(false);
1129
1130 if (!fsp_header_fill_encryption_info(space,
1131 encryption_info)) {
1132 space->encryption_type = Encryption::NONE;
1133 memset(space->encryption_key, 0, ENCRYPTION_KEY_LEN);
1134 memset(space->encryption_iv, 0, ENCRYPTION_KEY_LEN);
1135 return(false);
1136 }
1137
1138 mlog_write_string(page + offset,
1139 encryption_info,
1140 ENCRYPTION_INFO_SIZE_V2,
1141 mtr);
1142 }
1143
1144 if (space_id == srv_sys_space.space_id()) {
1145 if (btr_create(DICT_CLUSTERED | DICT_IBUF,
1146 0, univ_page_size, DICT_IBUF_ID_MIN + space_id,
1147 dict_ind_redundant, NULL, mtr) == FIL_NULL) {
1148 return(false);
1149 }
1150 }
1151
1152 return(true);
1153 }
1154 #endif /* !UNIV_HOTBACKUP */
1155
1156 /**********************************************************************//**
1157 Reads the space id from the first page of a tablespace.
1158 @return space id, ULINT UNDEFINED if error */
1159 ulint
fsp_header_get_space_id(const page_t * page)1160 fsp_header_get_space_id(
1161 /*====================*/
1162 const page_t* page) /*!< in: first page of a tablespace */
1163 {
1164 ulint fsp_id;
1165 ulint id;
1166
1167 fsp_id = mach_read_from_4(FSP_HEADER_OFFSET + page + FSP_SPACE_ID);
1168
1169 id = mach_read_from_4(page + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID);
1170
1171 DBUG_EXECUTE_IF("fsp_header_get_space_id_failure",
1172 id = ULINT_UNDEFINED;);
1173
1174 if (id != fsp_id) {
1175 ib::error() << "Space ID in fsp header is " << fsp_id
1176 << ", but in the page header it is " << id << ".";
1177 return(ULINT_UNDEFINED);
1178 }
1179
1180 return(id);
1181 }
1182
1183 /** Reads the page size from the first page of a tablespace.
1184 @param[in] page first page of a tablespace
1185 @return page size */
1186 page_size_t
fsp_header_get_page_size(const page_t * page)1187 fsp_header_get_page_size(
1188 const page_t* page)
1189 {
1190 return(page_size_t(fsp_header_get_flags(page)));
1191 }
1192
1193 /** Decoding the encryption info
1194 from the first page of a tablespace.
1195 @param[in/out] key key
1196 @param[in/out] iv iv
1197 @param[in] encryption_info encrytion info.
1198 @return true if success */
1199 bool
fsp_header_decode_encryption_info(byte * key,byte * iv,byte * encryption_info)1200 fsp_header_decode_encryption_info(
1201 byte* key,
1202 byte* iv,
1203 byte* encryption_info)
1204 {
1205 byte* ptr;
1206 ulint master_key_id;
1207 byte* master_key = NULL;
1208 lint elen;
1209 byte key_info[ENCRYPTION_KEY_LEN * 2];
1210 ulint crc1;
1211 ulint crc2;
1212 char srv_uuid[ENCRYPTION_SERVER_UUID_LEN + 1];
1213 Encryption::Version version;
1214 #ifdef UNIV_ENCRYPT_DEBUG
1215 const byte* data;
1216 ulint i;
1217 #endif
1218
1219 ptr = encryption_info;
1220
1221 /* For compatibility with 5.7.11, we need to handle the
1222 encryption information which created in this old version. */
1223 if (memcmp(ptr, ENCRYPTION_KEY_MAGIC_V1,
1224 ENCRYPTION_MAGIC_SIZE) == 0) {
1225 version = Encryption::ENCRYPTION_VERSION_1;
1226 } else {
1227 version = Encryption::ENCRYPTION_VERSION_2;
1228 }
1229
1230 /* Check magic. */
1231 if (version == Encryption::ENCRYPTION_VERSION_2
1232 && memcmp(ptr, ENCRYPTION_KEY_MAGIC_V2, ENCRYPTION_MAGIC_SIZE) != 0) {
1233 /* We ignore report error for recovery,
1234 since the encryption info maybe hasn't writen
1235 into datafile when the table is newly created. */
1236 if (!recv_recovery_is_on()) {
1237 return(false);
1238 } else {
1239 return(true);
1240 }
1241 }
1242 ptr += ENCRYPTION_MAGIC_SIZE;
1243
1244 /* Get master key id. */
1245 master_key_id = mach_read_from_4(ptr);
1246 ptr += sizeof(ulint);
1247
1248 /* Get server uuid. */
1249 if (version == Encryption::ENCRYPTION_VERSION_2) {
1250 memset(srv_uuid, 0, ENCRYPTION_SERVER_UUID_LEN + 1);
1251 memcpy(srv_uuid, ptr, ENCRYPTION_SERVER_UUID_LEN);
1252 ptr += ENCRYPTION_SERVER_UUID_LEN;
1253 }
1254
1255 /* Get master key by key id. */
1256 memset(key_info, 0, ENCRYPTION_KEY_LEN * 2);
1257 if (version == Encryption::ENCRYPTION_VERSION_1) {
1258 Encryption::get_master_key(master_key_id, NULL, &master_key);
1259 } else {
1260 Encryption::get_master_key(master_key_id, srv_uuid, &master_key);
1261 }
1262 if (master_key == NULL) {
1263 return(false);
1264 }
1265
1266 #ifdef UNIV_ENCRYPT_DEBUG
1267 fprintf(stderr, "%lu ", master_key_id);
1268 for (data = (const byte*) master_key, i = 0;
1269 i < ENCRYPTION_KEY_LEN; i++)
1270 fprintf(stderr, "%02lx", (ulong)*data++);
1271 #endif
1272
1273 /* Decrypt tablespace key and iv. */
1274 elen = my_aes_decrypt(
1275 ptr,
1276 ENCRYPTION_KEY_LEN * 2,
1277 key_info,
1278 master_key,
1279 ENCRYPTION_KEY_LEN,
1280 my_aes_256_ecb, NULL, false);
1281
1282 if (elen == MY_AES_BAD_DATA) {
1283 my_free(master_key);
1284 return(false);
1285 }
1286
1287 /* Check checksum bytes. */
1288 ptr += ENCRYPTION_KEY_LEN * 2;
1289
1290 crc1 = mach_read_from_4(ptr);
1291 crc2 = ut_crc32(key_info, ENCRYPTION_KEY_LEN * 2);
1292 if (crc1 != crc2) {
1293 ib::error() << "Failed to decrypt encryption information,"
1294 << " please confirm the master key was not changed.";
1295 my_free(master_key);
1296 return(false);
1297 }
1298
1299 /* Get tablespace key */
1300 memcpy(key, key_info, ENCRYPTION_KEY_LEN);
1301
1302 /* Get tablespace iv */
1303 memcpy(iv, key_info + ENCRYPTION_KEY_LEN,
1304 ENCRYPTION_KEY_LEN);
1305
1306 #ifdef UNIV_ENCRYPT_DEBUG
1307 fprintf(stderr, " ");
1308 for (data = (const byte*) key,
1309 i = 0; i < ENCRYPTION_KEY_LEN; i++)
1310 fprintf(stderr, "%02lx", (ulong)*data++);
1311 fprintf(stderr, " ");
1312 for (data = (const byte*) iv,
1313 i = 0; i < ENCRYPTION_KEY_LEN; i++)
1314 fprintf(stderr, "%02lx", (ulong)*data++);
1315 fprintf(stderr, "\n");
1316 #endif
1317
1318 my_free(master_key);
1319
1320 if (Encryption::master_key_id < master_key_id) {
1321 Encryption::master_key_id = master_key_id;
1322 memcpy(Encryption::uuid, srv_uuid, ENCRYPTION_SERVER_UUID_LEN);
1323 }
1324
1325 return(true);
1326 }
1327
1328 /** Reads the encryption key from the first page of a tablespace.
1329 @param[in] fsp_flags tablespace flags
1330 @param[in/out] key tablespace key
1331 @param[in/out] iv tablespace iv
1332 @param[in] page first page of a tablespace
1333 @return true if success */
1334 bool
fsp_header_get_encryption_key(ulint fsp_flags,byte * key,byte * iv,page_t * page)1335 fsp_header_get_encryption_key(
1336 ulint fsp_flags,
1337 byte* key,
1338 byte* iv,
1339 page_t* page)
1340 {
1341 ulint offset;
1342 const page_size_t page_size(fsp_flags);
1343
1344 offset = fsp_header_get_encryption_offset(page_size);
1345 if (offset == 0) {
1346 return(false);
1347 }
1348
1349 return(fsp_header_decode_encryption_info(key, iv, page + offset));
1350 }
1351
1352 #ifndef UNIV_HOTBACKUP
1353 /**********************************************************************//**
1354 Increases the space size field of a space. */
1355 void
fsp_header_inc_size(ulint space_id,ulint size_inc,mtr_t * mtr)1356 fsp_header_inc_size(
1357 /*================*/
1358 ulint space_id, /*!< in: space id */
1359 ulint size_inc, /*!< in: size increment in pages */
1360 mtr_t* mtr) /*!< in/out: mini-transaction */
1361 {
1362 fsp_header_t* header;
1363 ulint size;
1364
1365 ut_ad(mtr);
1366
1367 fil_space_t* space = mtr_x_lock_space(space_id, mtr);
1368 ut_d(fsp_space_modify_check(space_id, mtr));
1369
1370 header = fsp_get_space_header(
1371 space_id, page_size_t(space->flags), mtr);
1372
1373 size = mach_read_from_4(header + FSP_SIZE);
1374 ut_ad(size == space->size_in_header);
1375
1376 size += size_inc;
1377
1378 mlog_write_ulint(header + FSP_SIZE, size, MLOG_4BYTES, mtr);
1379 space->size_in_header = size;
1380 }
1381
1382 /**********************************************************************//**
1383 Gets the size of the system tablespace from the tablespace header. If
1384 we do not have an auto-extending data file, this should be equal to
1385 the size of the data files. If there is an auto-extending data file,
1386 this can be smaller.
1387 @return size in pages */
1388 ulint
fsp_header_get_tablespace_size(void)1389 fsp_header_get_tablespace_size(void)
1390 /*================================*/
1391 {
1392 fsp_header_t* header;
1393 ulint size;
1394 mtr_t mtr;
1395
1396 mtr_start(&mtr);
1397
1398 #ifdef UNIV_DEBUG
1399 fil_space_t* space =
1400 #endif /* UNIV_DEBUG */
1401 mtr_x_lock_space(TRX_SYS_SPACE, &mtr);
1402
1403 header = fsp_get_space_header(TRX_SYS_SPACE, univ_page_size, &mtr);
1404
1405 size = mach_read_from_4(header + FSP_SIZE);
1406 ut_ad(space->size_in_header == size);
1407
1408 mtr_commit(&mtr);
1409
1410 return(size);
1411 }
1412
1413 /** Try to extend a single-table tablespace so that a page would fit in the
1414 data file.
1415 @param[in,out] space tablespace
1416 @param[in] page_no page number
1417 @param[in,out] header tablespace header
1418 @param[in,out] mtr mini-transaction
1419 @return true if success */
UNIV_COLD(warn_unused_result)1420 static UNIV_COLD MY_ATTRIBUTE((warn_unused_result))
1421 bool
1422 fsp_try_extend_data_file_with_pages(
1423 fil_space_t* space,
1424 ulint page_no,
1425 fsp_header_t* header,
1426 mtr_t* mtr)
1427 {
1428 bool success;
1429 ulint size;
1430
1431 ut_a(!is_system_tablespace(space->id));
1432 ut_d(fsp_space_modify_check(space->id, mtr));
1433
1434 size = mach_read_from_4(header + FSP_SIZE);
1435 ut_ad(size == space->size_in_header);
1436
1437 ut_a(page_no >= size);
1438
1439 success = fil_space_extend(space, page_no + 1);
1440 /* The size may be less than we wanted if we ran out of disk space. */
1441 mlog_write_ulint(header + FSP_SIZE, space->size, MLOG_4BYTES, mtr);
1442 space->size_in_header = space->size;
1443
1444 return(success);
1445 }
1446
1447 /** Try to extend the last data file of a tablespace if it is auto-extending.
1448 @param[in,out] space tablespace
1449 @param[in,out] header tablespace header
1450 @param[in,out] mtr mini-transaction
1451 @return whether the tablespace was extended */
1452 static UNIV_COLD
1453 ulint
fsp_try_extend_data_file(fil_space_t * space,fsp_header_t * header,mtr_t * mtr)1454 fsp_try_extend_data_file(
1455 fil_space_t* space,
1456 fsp_header_t* header,
1457 mtr_t* mtr)
1458 {
1459 ulint size; /* current number of pages in the datafile */
1460 ulint size_increase; /* number of pages to extend this file */
1461 const char* OUT_OF_SPACE_MSG =
1462 "ran out of space. Please add another file or use"
1463 " 'autoextend' for the last file in setting";
1464
1465 ut_d(fsp_space_modify_check(space->id, mtr));
1466
1467 if (space->id == srv_sys_space.space_id()
1468 && !srv_sys_space.can_auto_extend_last_file()) {
1469
1470 /* We print the error message only once to avoid
1471 spamming the error log. Note that we don't need
1472 to reset the flag to false as dealing with this
1473 error requires server restart. */
1474 if (!srv_sys_space.get_tablespace_full_status()) {
1475 ib::error() << "Tablespace " << srv_sys_space.name()
1476 << " " << OUT_OF_SPACE_MSG
1477 << " innodb_data_file_path.";
1478 srv_sys_space.set_tablespace_full_status(true);
1479 }
1480 return(false);
1481 } else if (fsp_is_system_temporary(space->id)
1482 && !srv_tmp_space.can_auto_extend_last_file()) {
1483
1484 /* We print the error message only once to avoid
1485 spamming the error log. Note that we don't need
1486 to reset the flag to false as dealing with this
1487 error requires server restart. */
1488 if (!srv_tmp_space.get_tablespace_full_status()) {
1489 ib::error() << "Tablespace " << srv_tmp_space.name()
1490 << " " << OUT_OF_SPACE_MSG
1491 << " innodb_temp_data_file_path.";
1492 srv_tmp_space.set_tablespace_full_status(true);
1493 }
1494 return(false);
1495 }
1496
1497 size = mach_read_from_4(header + FSP_SIZE);
1498 ut_ad(size == space->size_in_header);
1499
1500 const page_size_t page_size(
1501 mach_read_from_4(header + FSP_SPACE_FLAGS));
1502
1503 if (space->id == srv_sys_space.space_id()) {
1504
1505 size_increase = srv_sys_space.get_increment();
1506
1507 } else if (space->id == srv_tmp_space.space_id()) {
1508
1509 size_increase = srv_tmp_space.get_increment();
1510
1511 } else {
1512 ulint extent_pages
1513 = fsp_get_extent_size_in_pages(page_size);
1514 if (size < extent_pages) {
1515 /* Let us first extend the file to extent_size */
1516 if (!fsp_try_extend_data_file_with_pages(
1517 space, extent_pages - 1, header, mtr)) {
1518 return(false);
1519 }
1520
1521 size = extent_pages;
1522 }
1523
1524 size_increase = fsp_get_pages_to_extend_ibd(page_size, size);
1525 }
1526
1527 if (size_increase == 0) {
1528
1529 return(false);
1530 }
1531
1532 if (!fil_space_extend(space, size + size_increase)) {
1533 return(false);
1534 }
1535
1536 /* We ignore any fragments of a full megabyte when storing the size
1537 to the space header */
1538
1539 space->size_in_header = ut_calc_align_down(
1540 space->size, (1024 * 1024) / page_size.physical());
1541
1542 mlog_write_ulint(
1543 header + FSP_SIZE, space->size_in_header, MLOG_4BYTES, mtr);
1544
1545 return(true);
1546 }
1547
1548 /** Calculate the number of pages to extend a datafile.
1549 We extend single-table and general tablespaces first one extent at a time,
1550 but 4 at a time for bigger tablespaces. It is not enough to extend always
1551 by one extent, because we need to add at least one extent to FSP_FREE.
1552 A single extent descriptor page will track many extents. And the extent
1553 that uses its extent descriptor page is put onto the FSP_FREE_FRAG list.
1554 Extents that do not use their extent descriptor page are added to FSP_FREE.
1555 The physical page size is used to determine how many extents are tracked
1556 on one extent descriptor page. See xdes_calc_descriptor_page().
1557 @param[in] page_size page_size of the datafile
1558 @param[in] size current number of pages in the datafile
1559 @return number of pages to extend the file. */
1560 ulint
fsp_get_pages_to_extend_ibd(const page_size_t & page_size,ulint size)1561 fsp_get_pages_to_extend_ibd(
1562 const page_size_t& page_size,
1563 ulint size)
1564 {
1565 ulint size_increase; /* number of pages to extend this file */
1566 ulint extent_size; /* one megabyte, in pages */
1567 ulint threshold; /* The size of the tablespace (in number
1568 of pages) where we start allocating more
1569 than one extent at a time. */
1570
1571 extent_size = fsp_get_extent_size_in_pages(page_size);
1572
1573 /* The threshold is set at 32MiB except when the physical page
1574 size is small enough that it must be done sooner. */
1575 threshold = ut_min(32 * extent_size, page_size.physical());
1576
1577 if (size < threshold) {
1578 size_increase = extent_size;
1579 } else {
1580 /* Below in fsp_fill_free_list() we assume
1581 that we add at most FSP_FREE_ADD extents at
1582 a time */
1583 size_increase = FSP_FREE_ADD * extent_size;
1584 }
1585
1586 return(size_increase);
1587 }
1588
1589 /** Put new extents to the free list if there are free extents above the free
1590 limit. If an extent happens to contain an extent descriptor page, the extent
1591 is put to the FSP_FREE_FRAG list with the page marked as used.
1592 @param[in] init_space true if this is a single-table tablespace
1593 and we are only initializing the first extent and the first bitmap pages;
1594 then we will not allocate more extents
1595 @param[in,out] space tablespace
1596 @param[in,out] header tablespace header
1597 @param[in,out] mtr mini-transaction */
1598 static
1599 void
fsp_fill_free_list(bool init_space,fil_space_t * space,fsp_header_t * header,mtr_t * mtr)1600 fsp_fill_free_list(
1601 bool init_space,
1602 fil_space_t* space,
1603 fsp_header_t* header,
1604 mtr_t* mtr)
1605 {
1606 ulint limit;
1607 ulint size;
1608 ulint flags;
1609 xdes_t* descr;
1610 ulint count = 0;
1611 ulint frag_n_used;
1612 ulint i;
1613
1614 ut_ad(page_offset(header) == FSP_HEADER_OFFSET);
1615 ut_d(fsp_space_modify_check(space->id, mtr));
1616
1617 /* Check if we can fill free list from above the free list limit */
1618 size = mach_read_from_4(header + FSP_SIZE);
1619 limit = mach_read_from_4(header + FSP_FREE_LIMIT);
1620 flags = mach_read_from_4(header + FSP_SPACE_FLAGS);
1621
1622 ut_ad(size == space->size_in_header);
1623 ut_ad(limit == space->free_limit);
1624 ut_ad(flags == space->flags);
1625
1626 const page_size_t page_size(flags);
1627
1628 if (size < limit + FSP_EXTENT_SIZE * FSP_FREE_ADD) {
1629 if ((!init_space && !is_system_tablespace(space->id))
1630 || (space->id == srv_sys_space.space_id()
1631 && srv_sys_space.can_auto_extend_last_file())
1632 || (space->id == srv_tmp_space.space_id()
1633 && srv_tmp_space.can_auto_extend_last_file())) {
1634 fsp_try_extend_data_file(space, header, mtr);
1635 size = space->size_in_header;
1636 }
1637 }
1638
1639 i = limit;
1640
1641 while ((init_space && i < 1)
1642 || ((i + FSP_EXTENT_SIZE <= size) && (count < FSP_FREE_ADD))) {
1643
1644 bool init_xdes
1645 = (ut_2pow_remainder(i, page_size.physical()) == 0);
1646
1647 space->free_limit = i + FSP_EXTENT_SIZE;
1648 mlog_write_ulint(header + FSP_FREE_LIMIT, i + FSP_EXTENT_SIZE,
1649 MLOG_4BYTES, mtr);
1650
1651 if (init_xdes) {
1652
1653 buf_block_t* block;
1654
1655 /* We are going to initialize a new descriptor page
1656 and a new ibuf bitmap page: the prior contents of the
1657 pages should be ignored. */
1658
1659 if (i > 0) {
1660 const page_id_t page_id(space->id, i);
1661
1662 block = buf_page_create(
1663 page_id, page_size, mtr);
1664
1665 buf_page_get(
1666 page_id, page_size, RW_SX_LATCH, mtr);
1667
1668 buf_block_dbg_add_level(block, SYNC_FSP_PAGE);
1669
1670 fsp_init_file_page(block, mtr);
1671 mlog_write_ulint(buf_block_get_frame(block)
1672 + FIL_PAGE_TYPE,
1673 FIL_PAGE_TYPE_XDES,
1674 MLOG_2BYTES, mtr);
1675 }
1676
1677 /* Initialize the ibuf bitmap page in a separate
1678 mini-transaction because it is low in the latching
1679 order, and we must be able to release its latch.
1680 Note: Insert-Buffering is disabled for tables that
1681 reside in the temp-tablespace. */
1682 if (space->id != srv_tmp_space.space_id()) {
1683 mtr_t ibuf_mtr;
1684
1685 mtr_start(&ibuf_mtr);
1686 ibuf_mtr.set_named_space(space);
1687
1688 /* Avoid logging while truncate table
1689 fix-up is active. */
1690 if (space->purpose == FIL_TYPE_TEMPORARY
1691 || srv_is_tablespace_truncated(
1692 space->id)) {
1693 mtr_set_log_mode(
1694 &ibuf_mtr, MTR_LOG_NO_REDO);
1695 }
1696
1697 const page_id_t page_id(
1698 space->id,
1699 i + FSP_IBUF_BITMAP_OFFSET);
1700
1701 block = buf_page_create(
1702 page_id, page_size, &ibuf_mtr);
1703
1704 buf_page_get(
1705 page_id, page_size, RW_SX_LATCH,
1706 &ibuf_mtr);
1707
1708 buf_block_dbg_add_level(block, SYNC_FSP_PAGE);
1709
1710 fsp_init_file_page(block, &ibuf_mtr);
1711
1712 ibuf_bitmap_page_init(block, &ibuf_mtr);
1713
1714 mtr_commit(&ibuf_mtr);
1715 }
1716 }
1717
1718 buf_block_t* desc_block = NULL;
1719 descr = xdes_get_descriptor_with_space_hdr(
1720 header, space->id, i, mtr, init_space, &desc_block);
1721 if (desc_block != NULL) {
1722 fil_block_check_type(
1723 desc_block, FIL_PAGE_TYPE_XDES, mtr);
1724 }
1725 xdes_init(descr, mtr);
1726
1727 if (UNIV_UNLIKELY(init_xdes)) {
1728
1729 /* The first page in the extent is a descriptor page
1730 and the second is an ibuf bitmap page: mark them
1731 used */
1732
1733 xdes_set_bit(descr, XDES_FREE_BIT, 0, FALSE, mtr);
1734 xdes_set_bit(descr, XDES_FREE_BIT,
1735 FSP_IBUF_BITMAP_OFFSET, FALSE, mtr);
1736 xdes_set_state(descr, XDES_FREE_FRAG, mtr);
1737
1738 flst_add_last(header + FSP_FREE_FRAG,
1739 descr + XDES_FLST_NODE, mtr);
1740 frag_n_used = mach_read_from_4(
1741 header + FSP_FRAG_N_USED);
1742 mlog_write_ulint(header + FSP_FRAG_N_USED,
1743 frag_n_used + 2, MLOG_4BYTES, mtr);
1744 } else {
1745 flst_add_last(header + FSP_FREE,
1746 descr + XDES_FLST_NODE, mtr);
1747 count++;
1748 }
1749
1750 i += FSP_EXTENT_SIZE;
1751 }
1752
1753 space->free_len += count;
1754 }
1755
1756 /** Allocates a new free extent.
1757 @param[in] space_id tablespace identifier
1758 @param[in] page_size page size
1759 @param[in] hint hint of which extent would be desirable: any
1760 page offset in the extent goes; the hint must not be > FSP_FREE_LIMIT
1761 @param[in,out] mtr mini-transaction
1762 @return extent descriptor, NULL if cannot be allocated */
1763 static
1764 xdes_t*
fsp_alloc_free_extent(ulint space_id,const page_size_t & page_size,ulint hint,mtr_t * mtr)1765 fsp_alloc_free_extent(
1766 ulint space_id,
1767 const page_size_t& page_size,
1768 ulint hint,
1769 mtr_t* mtr)
1770 {
1771 fsp_header_t* header;
1772 fil_addr_t first;
1773 xdes_t* descr;
1774 buf_block_t* desc_block = NULL;
1775
1776 header = fsp_get_space_header(space_id, page_size, mtr);
1777
1778 descr = xdes_get_descriptor_with_space_hdr(
1779 header, space_id, hint, mtr, false, &desc_block);
1780
1781 fil_space_t* space = fil_space_get(space_id);
1782 ut_a(space != NULL);
1783
1784 if (desc_block != NULL) {
1785 fil_block_check_type(desc_block, FIL_PAGE_TYPE_XDES, mtr);
1786 }
1787
1788 if (descr && (xdes_get_state(descr, mtr) == XDES_FREE)) {
1789 /* Ok, we can take this extent */
1790 } else {
1791 /* Take the first extent in the free list */
1792 first = flst_get_first(header + FSP_FREE, mtr);
1793
1794 if (fil_addr_is_null(first)) {
1795 fsp_fill_free_list(false, space, header, mtr);
1796
1797 first = flst_get_first(header + FSP_FREE, mtr);
1798 }
1799
1800 if (fil_addr_is_null(first)) {
1801
1802 return(NULL); /* No free extents left */
1803 }
1804
1805 descr = xdes_lst_get_descriptor(
1806 space_id, page_size, first, mtr);
1807 }
1808
1809 flst_remove(header + FSP_FREE, descr + XDES_FLST_NODE, mtr);
1810 space->free_len--;
1811
1812 return(descr);
1813 }
1814
1815 /**********************************************************************//**
1816 Allocates a single free page from a space. */
1817 static
1818 void
fsp_alloc_from_free_frag(fsp_header_t * header,xdes_t * descr,ulint bit,mtr_t * mtr)1819 fsp_alloc_from_free_frag(
1820 /*=====================*/
1821 fsp_header_t* header, /*!< in/out: tablespace header */
1822 xdes_t* descr, /*!< in/out: extent descriptor */
1823 ulint bit, /*!< in: slot to allocate in the extent */
1824 mtr_t* mtr) /*!< in/out: mini-transaction */
1825 {
1826 ulint frag_n_used;
1827
1828 ut_ad(xdes_get_state(descr, mtr) == XDES_FREE_FRAG);
1829 ut_a(xdes_mtr_get_bit(descr, XDES_FREE_BIT, bit, mtr));
1830 xdes_set_bit(descr, XDES_FREE_BIT, bit, FALSE, mtr);
1831
1832 /* Update the FRAG_N_USED field */
1833 frag_n_used = mach_read_from_4(header + FSP_FRAG_N_USED);
1834 frag_n_used++;
1835 mlog_write_ulint(header + FSP_FRAG_N_USED, frag_n_used, MLOG_4BYTES,
1836 mtr);
1837 if (xdes_is_full(descr, mtr)) {
1838 /* The fragment is full: move it to another list */
1839 flst_remove(header + FSP_FREE_FRAG, descr + XDES_FLST_NODE,
1840 mtr);
1841 xdes_set_state(descr, XDES_FULL_FRAG, mtr);
1842
1843 flst_add_last(header + FSP_FULL_FRAG, descr + XDES_FLST_NODE,
1844 mtr);
1845 mlog_write_ulint(header + FSP_FRAG_N_USED,
1846 frag_n_used - FSP_EXTENT_SIZE, MLOG_4BYTES,
1847 mtr);
1848 }
1849 }
1850
1851 /** Gets a buffer block for an allocated page.
1852 NOTE: If init_mtr != mtr, the block will only be initialized if it was
1853 not previously x-latched. It is assumed that the block has been
1854 x-latched only by mtr, and freed in mtr in that case.
1855 @param[in] page_id page id of the allocated page
1856 @param[in] page_size page size of the allocated page
1857 @param[in] rw_latch RW_SX_LATCH, RW_X_LATCH
1858 @param[in,out] mtr mini-transaction of the allocation
1859 @param[in,out] init_mtr mini-transaction for initializing the page
1860 @return block, initialized if init_mtr==mtr
1861 or rw_lock_x_lock_count(&block->lock) == 1 */
1862 static
1863 buf_block_t*
fsp_page_create(const page_id_t & page_id,const page_size_t & page_size,rw_lock_type_t rw_latch,mtr_t * mtr,mtr_t * init_mtr)1864 fsp_page_create(
1865 const page_id_t& page_id,
1866 const page_size_t& page_size,
1867 rw_lock_type_t rw_latch,
1868 mtr_t* mtr,
1869 mtr_t* init_mtr)
1870 {
1871 buf_block_t* block = buf_page_create(page_id, page_size, init_mtr);
1872
1873 ut_ad(mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX)
1874 == rw_lock_own(&block->lock, RW_LOCK_X));
1875
1876 ut_ad(mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_SX_FIX)
1877 == rw_lock_own(&block->lock, RW_LOCK_SX));
1878
1879 ut_ad(rw_latch == RW_X_LATCH || rw_latch == RW_SX_LATCH);
1880
1881 /* Mimic buf_page_get(), but avoid the buf_pool->page_hash lookup. */
1882 if (rw_latch == RW_X_LATCH) {
1883 rw_lock_x_lock(&block->lock);
1884 } else {
1885 rw_lock_sx_lock(&block->lock);
1886 }
1887 mutex_enter(&block->mutex);
1888
1889 buf_block_buf_fix_inc(block, __FILE__, __LINE__);
1890
1891 mutex_exit(&block->mutex);
1892 mtr_memo_push(init_mtr, block, rw_latch == RW_X_LATCH
1893 ? MTR_MEMO_PAGE_X_FIX : MTR_MEMO_PAGE_SX_FIX);
1894
1895 if (init_mtr == mtr
1896 || (rw_latch == RW_X_LATCH
1897 ? rw_lock_get_x_lock_count(&block->lock) == 1
1898 : rw_lock_get_sx_lock_count(&block->lock) == 1)) {
1899
1900 /* Initialize the page, unless it was already
1901 SX-latched in mtr. (In this case, we would want to
1902 allocate another page that has not been freed in mtr.) */
1903 ut_ad(init_mtr == mtr
1904 || !mtr_memo_contains_flagged(mtr, block,
1905 MTR_MEMO_PAGE_X_FIX
1906 | MTR_MEMO_PAGE_SX_FIX));
1907
1908 fsp_init_file_page(block, init_mtr);
1909 }
1910
1911 return(block);
1912 }
1913
1914 /** Allocates a single free page from a space.
1915 The page is marked as used.
1916 @param[in] space space id
1917 @param[in] page_size page size
1918 @param[in] hint hint of which page would be desirable
1919 @param[in] rw_latch RW_SX_LATCH, RW_X_LATCH
1920 @param[in,out] mtr mini-transaction
1921 @param[in,out] init_mtr mini-transaction in which the page should be
1922 initialized (may be the same as mtr)
1923 @retval NULL if no page could be allocated
1924 @retval block rw_lock_x_lock_count(&block->lock) == 1 if allocation succeeded
1925 (init_mtr == mtr, or the page was not previously freed in mtr)
1926 @retval block (not allocated or initialized) otherwise */
1927 static MY_ATTRIBUTE((warn_unused_result))
1928 buf_block_t*
fsp_alloc_free_page(ulint space,const page_size_t & page_size,ulint hint,rw_lock_type_t rw_latch,mtr_t * mtr,mtr_t * init_mtr)1929 fsp_alloc_free_page(
1930 ulint space,
1931 const page_size_t& page_size,
1932 ulint hint,
1933 rw_lock_type_t rw_latch,
1934 mtr_t* mtr,
1935 mtr_t* init_mtr)
1936 {
1937 fsp_header_t* header;
1938 fil_addr_t first;
1939 xdes_t* descr;
1940 ulint free;
1941 ulint page_no;
1942 ulint space_size;
1943
1944 ut_ad(mtr);
1945 ut_ad(init_mtr);
1946
1947 ut_d(fsp_space_modify_check(space, mtr));
1948 header = fsp_get_space_header(space, page_size, mtr);
1949
1950 /* Get the hinted descriptor */
1951 descr = xdes_get_descriptor_with_space_hdr(header, space, hint, mtr);
1952
1953 if (descr && (xdes_get_state(descr, mtr) == XDES_FREE_FRAG)) {
1954 /* Ok, we can take this extent */
1955 } else {
1956 /* Else take the first extent in free_frag list */
1957 first = flst_get_first(header + FSP_FREE_FRAG, mtr);
1958
1959 if (fil_addr_is_null(first)) {
1960 /* There are no partially full fragments: allocate
1961 a free extent and add it to the FREE_FRAG list. NOTE
1962 that the allocation may have as a side-effect that an
1963 extent containing a descriptor page is added to the
1964 FREE_FRAG list. But we will allocate our page from the
1965 the free extent anyway. */
1966
1967 descr = fsp_alloc_free_extent(space, page_size,
1968 hint, mtr);
1969
1970 if (descr == NULL) {
1971 /* No free space left */
1972
1973 return(NULL);
1974 }
1975
1976 xdes_set_state(descr, XDES_FREE_FRAG, mtr);
1977 flst_add_last(header + FSP_FREE_FRAG,
1978 descr + XDES_FLST_NODE, mtr);
1979 } else {
1980 descr = xdes_lst_get_descriptor(space, page_size,
1981 first, mtr);
1982 }
1983
1984 /* Reset the hint */
1985 hint = 0;
1986 }
1987
1988 /* Now we have in descr an extent with at least one free page. Look
1989 for a free page in the extent. */
1990
1991 free = xdes_find_bit(descr, XDES_FREE_BIT, TRUE,
1992 hint % FSP_EXTENT_SIZE, mtr);
1993 if (free == ULINT_UNDEFINED) {
1994
1995 ut_print_buf(stderr, ((byte*) descr) - 500, 1000);
1996 putc('\n', stderr);
1997
1998 ut_error;
1999 }
2000
2001 page_no = xdes_get_offset(descr) + free;
2002
2003 space_size = mach_read_from_4(header + FSP_SIZE);
2004 ut_ad(space_size == fil_space_get(space)->size_in_header
2005 || (space == TRX_SYS_SPACE
2006 && srv_startup_is_before_trx_rollback_phase));
2007
2008 if (space_size <= page_no) {
2009 /* It must be that we are extending a single-table tablespace
2010 whose size is still < 64 pages */
2011
2012 ut_a(!is_system_tablespace(space));
2013 if (page_no >= FSP_EXTENT_SIZE) {
2014 ib::error() << "Trying to extend a single-table"
2015 " tablespace " << space << " , by single"
2016 " page(s) though the space size " << space_size
2017 << ". Page no " << page_no << ".";
2018 return(NULL);
2019 }
2020
2021 fil_space_t* fspace = fil_space_get(space);
2022
2023 if (!fsp_try_extend_data_file_with_pages(fspace, page_no,
2024 header, mtr)) {
2025 /* No disk space left */
2026 return(NULL);
2027 }
2028 }
2029
2030 fsp_alloc_from_free_frag(header, descr, free, mtr);
2031 return(fsp_page_create(page_id_t(space, page_no), page_size,
2032 rw_latch, mtr, init_mtr));
2033 }
2034
2035 /** Frees a single page of a space.
2036 The page is marked as free and clean.
2037 @param[in] page_id page id
2038 @param[in] page_size page size
2039 @param[in,out] mtr mini-transaction */
2040 static
2041 void
fsp_free_page(const page_id_t & page_id,const page_size_t & page_size,mtr_t * mtr)2042 fsp_free_page(
2043 const page_id_t& page_id,
2044 const page_size_t& page_size,
2045 mtr_t* mtr)
2046 {
2047 fsp_header_t* header;
2048 xdes_t* descr;
2049 ulint state;
2050 ulint frag_n_used;
2051
2052 ut_ad(mtr);
2053 ut_d(fsp_space_modify_check(page_id.space(), mtr));
2054
2055 /* fprintf(stderr, "Freeing page %lu in space %lu\n", page, space); */
2056
2057 header = fsp_get_space_header(
2058 page_id.space(), page_size, mtr);
2059
2060 descr = xdes_get_descriptor_with_space_hdr(
2061 header, page_id.space(), page_id.page_no(), mtr);
2062
2063 state = xdes_get_state(descr, mtr);
2064
2065 if (state != XDES_FREE_FRAG && state != XDES_FULL_FRAG) {
2066 ib::error() << "File space extent descriptor of page "
2067 << page_id << " has state " << state;
2068 fputs("InnoDB: Dump of descriptor: ", stderr);
2069 ut_print_buf(stderr, ((byte*) descr) - 50, 200);
2070 putc('\n', stderr);
2071 /* Crash in debug version, so that we get a core dump
2072 of this corruption. */
2073 ut_ad(0);
2074
2075 if (state == XDES_FREE) {
2076 /* We put here some fault tolerance: if the page
2077 is already free, return without doing anything! */
2078
2079 return;
2080 }
2081
2082 ut_error;
2083 }
2084
2085 if (xdes_mtr_get_bit(descr, XDES_FREE_BIT,
2086 page_id.page_no() % FSP_EXTENT_SIZE, mtr)) {
2087
2088 ib::error() << "File space extent descriptor of page "
2089 << page_id << " says it is free. Dump of descriptor: ";
2090 ut_print_buf(stderr, ((byte*) descr) - 50, 200);
2091 putc('\n', stderr);
2092 /* Crash in debug version, so that we get a core dump
2093 of this corruption. */
2094 ut_ad(0);
2095
2096 /* We put here some fault tolerance: if the page
2097 is already free, return without doing anything! */
2098
2099 return;
2100 }
2101
2102 const ulint bit = page_id.page_no() % FSP_EXTENT_SIZE;
2103
2104 xdes_set_bit(descr, XDES_FREE_BIT, bit, TRUE, mtr);
2105 xdes_set_bit(descr, XDES_CLEAN_BIT, bit, TRUE, mtr);
2106
2107 frag_n_used = mtr_read_ulint(header + FSP_FRAG_N_USED, MLOG_4BYTES,
2108 mtr);
2109 if (state == XDES_FULL_FRAG) {
2110 /* The fragment was full: move it to another list */
2111 flst_remove(header + FSP_FULL_FRAG, descr + XDES_FLST_NODE,
2112 mtr);
2113 xdes_set_state(descr, XDES_FREE_FRAG, mtr);
2114 flst_add_last(header + FSP_FREE_FRAG, descr + XDES_FLST_NODE,
2115 mtr);
2116 mlog_write_ulint(header + FSP_FRAG_N_USED,
2117 frag_n_used + FSP_EXTENT_SIZE - 1,
2118 MLOG_4BYTES, mtr);
2119 } else {
2120 ut_a(frag_n_used > 0);
2121 mlog_write_ulint(header + FSP_FRAG_N_USED, frag_n_used - 1,
2122 MLOG_4BYTES, mtr);
2123 }
2124
2125 if (xdes_is_free(descr, mtr)) {
2126 /* The extent has become free: move it to another list */
2127 flst_remove(header + FSP_FREE_FRAG, descr + XDES_FLST_NODE,
2128 mtr);
2129 fsp_free_extent(page_id, page_size, mtr);
2130 }
2131 }
2132
2133 /** Returns an extent to the free list of a space.
2134 @param[in] page_id page id in the extent
2135 @param[in] page_size page size
2136 @param[in,out] mtr mini-transaction */
2137 static
2138 void
fsp_free_extent(const page_id_t & page_id,const page_size_t & page_size,mtr_t * mtr)2139 fsp_free_extent(
2140 const page_id_t& page_id,
2141 const page_size_t& page_size,
2142 mtr_t* mtr)
2143 {
2144 fsp_header_t* header;
2145 xdes_t* descr;
2146
2147 ut_ad(mtr);
2148
2149 header = fsp_get_space_header(page_id.space(), page_size, mtr);
2150
2151 descr = xdes_get_descriptor_with_space_hdr(
2152 header, page_id.space(), page_id.page_no(), mtr);
2153
2154 ut_a(xdes_get_state(descr, mtr) != XDES_FREE);
2155
2156 xdes_init(descr, mtr);
2157
2158 flst_add_last(header + FSP_FREE, descr + XDES_FLST_NODE, mtr);
2159 fil_space_get(page_id.space())->free_len++;
2160 }
2161
2162 /** Returns the nth inode slot on an inode page.
2163 @param[in] page segment inode page
2164 @param[in] i inode index on page
2165 @param[in] page_size page size
2166 @param[in,out] mtr mini-transaction
2167 @return segment inode */
2168 UNIV_INLINE
2169 fseg_inode_t*
fsp_seg_inode_page_get_nth_inode(page_t * page,ulint i,const page_size_t & page_size,mtr_t * mtr)2170 fsp_seg_inode_page_get_nth_inode(
2171 page_t* page,
2172 ulint i,
2173 const page_size_t& page_size,
2174 mtr_t* mtr)
2175 {
2176 ut_ad(i < FSP_SEG_INODES_PER_PAGE(page_size));
2177 ut_ad(mtr_memo_contains_page(mtr, page, MTR_MEMO_PAGE_SX_FIX));
2178
2179 return(page + FSEG_ARR_OFFSET + FSEG_INODE_SIZE * i);
2180 }
2181
2182 /** Looks for a used segment inode on a segment inode page.
2183 @param[in] page segment inode page
2184 @param[in] page_size page size
2185 @param[in,out] mtr mini-transaction
2186 @return segment inode index, or ULINT_UNDEFINED if not found */
2187 static
2188 ulint
fsp_seg_inode_page_find_used(page_t * page,const page_size_t & page_size,mtr_t * mtr)2189 fsp_seg_inode_page_find_used(
2190 page_t* page,
2191 const page_size_t& page_size,
2192 mtr_t* mtr)
2193 {
2194 ulint i;
2195 fseg_inode_t* inode;
2196
2197 for (i = 0; i < FSP_SEG_INODES_PER_PAGE(page_size); i++) {
2198
2199 inode = fsp_seg_inode_page_get_nth_inode(
2200 page, i, page_size, mtr);
2201
2202 if (mach_read_from_8(inode + FSEG_ID)) {
2203 /* This is used */
2204
2205 ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N)
2206 == FSEG_MAGIC_N_VALUE);
2207 return(i);
2208 }
2209 }
2210
2211 return(ULINT_UNDEFINED);
2212 }
2213
2214 /** Looks for an unused segment inode on a segment inode page.
2215 @param[in] page segment inode page
2216 @param[in] i search forward starting from this index
2217 @param[in] page_size page size
2218 @param[in,out] mtr mini-transaction
2219 @return segment inode index, or ULINT_UNDEFINED if not found */
2220 static
2221 ulint
fsp_seg_inode_page_find_free(page_t * page,ulint i,const page_size_t & page_size,mtr_t * mtr)2222 fsp_seg_inode_page_find_free(
2223 page_t* page,
2224 ulint i,
2225 const page_size_t& page_size,
2226 mtr_t* mtr)
2227 {
2228 for (; i < FSP_SEG_INODES_PER_PAGE(page_size); i++) {
2229
2230 fseg_inode_t* inode;
2231
2232 inode = fsp_seg_inode_page_get_nth_inode(
2233 page, i, page_size, mtr);
2234
2235 if (!mach_read_from_8(inode + FSEG_ID)) {
2236 /* This is unused */
2237 return(i);
2238 }
2239
2240 ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N)
2241 == FSEG_MAGIC_N_VALUE);
2242 }
2243
2244 return(ULINT_UNDEFINED);
2245 }
2246
2247 /**********************************************************************//**
2248 Allocates a new file segment inode page.
2249 @return TRUE if could be allocated */
2250 static
2251 ibool
fsp_alloc_seg_inode_page(fsp_header_t * space_header,mtr_t * mtr)2252 fsp_alloc_seg_inode_page(
2253 /*=====================*/
2254 fsp_header_t* space_header, /*!< in: space header */
2255 mtr_t* mtr) /*!< in/out: mini-transaction */
2256 {
2257 fseg_inode_t* inode;
2258 buf_block_t* block;
2259 page_t* page;
2260 ulint space;
2261
2262 ut_ad(page_offset(space_header) == FSP_HEADER_OFFSET);
2263
2264 space = page_get_space_id(page_align(space_header));
2265
2266 const page_size_t page_size(mach_read_from_4(FSP_SPACE_FLAGS
2267 + space_header));
2268
2269 block = fsp_alloc_free_page(space, page_size, 0, RW_SX_LATCH, mtr, mtr);
2270
2271 if (block == NULL) {
2272
2273 return(FALSE);
2274 }
2275
2276 buf_block_dbg_add_level(block, SYNC_FSP_PAGE);
2277 ut_ad(rw_lock_get_sx_lock_count(&block->lock) == 1);
2278
2279 page = buf_block_get_frame(block);
2280
2281 mlog_write_ulint(page + FIL_PAGE_TYPE, FIL_PAGE_INODE,
2282 MLOG_2BYTES, mtr);
2283
2284 for (ulint i = 0; i < FSP_SEG_INODES_PER_PAGE(page_size); i++) {
2285
2286 inode = fsp_seg_inode_page_get_nth_inode(
2287 page, i, page_size, mtr);
2288
2289 mlog_write_ull(inode + FSEG_ID, 0, mtr);
2290 }
2291
2292 flst_add_last(
2293 space_header + FSP_SEG_INODES_FREE,
2294 page + FSEG_INODE_PAGE_NODE, mtr);
2295
2296 return(TRUE);
2297 }
2298
2299 /**********************************************************************//**
2300 Allocates a new file segment inode.
2301 @return segment inode, or NULL if not enough space */
2302 static
2303 fseg_inode_t*
fsp_alloc_seg_inode(fsp_header_t * space_header,mtr_t * mtr)2304 fsp_alloc_seg_inode(
2305 /*================*/
2306 fsp_header_t* space_header, /*!< in: space header */
2307 mtr_t* mtr) /*!< in/out: mini-transaction */
2308 {
2309 buf_block_t* block;
2310 page_t* page;
2311 fseg_inode_t* inode;
2312 ulint n;
2313
2314 ut_ad(page_offset(space_header) == FSP_HEADER_OFFSET);
2315
2316 /* Allocate a new segment inode page if needed. */
2317 if (flst_get_len(space_header + FSP_SEG_INODES_FREE) == 0
2318 && !fsp_alloc_seg_inode_page(space_header, mtr)) {
2319 return(NULL);
2320 }
2321
2322 const page_size_t page_size(
2323 mach_read_from_4(FSP_SPACE_FLAGS + space_header));
2324
2325 const page_id_t page_id(
2326 page_get_space_id(page_align(space_header)),
2327 flst_get_first(space_header + FSP_SEG_INODES_FREE, mtr).page);
2328
2329 block = buf_page_get(page_id, page_size, RW_SX_LATCH, mtr);
2330 buf_block_dbg_add_level(block, SYNC_FSP_PAGE);
2331 fil_block_check_type(block, FIL_PAGE_INODE, mtr);
2332
2333 page = buf_block_get_frame(block);
2334
2335 n = fsp_seg_inode_page_find_free(page, 0, page_size, mtr);
2336
2337 ut_a(n != ULINT_UNDEFINED);
2338
2339 inode = fsp_seg_inode_page_get_nth_inode(page, n, page_size, mtr);
2340
2341 if (ULINT_UNDEFINED == fsp_seg_inode_page_find_free(page, n + 1,
2342 page_size, mtr)) {
2343 /* There are no other unused headers left on the page: move it
2344 to another list */
2345
2346 flst_remove(space_header + FSP_SEG_INODES_FREE,
2347 page + FSEG_INODE_PAGE_NODE, mtr);
2348
2349 flst_add_last(space_header + FSP_SEG_INODES_FULL,
2350 page + FSEG_INODE_PAGE_NODE, mtr);
2351 }
2352
2353 ut_ad(!mach_read_from_8(inode + FSEG_ID)
2354 || mach_read_from_4(inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE);
2355 return(inode);
2356 }
2357
2358 /** Frees a file segment inode.
2359 @param[in] space space id
2360 @param[in] page_size page size
2361 @param[in,out] inode segment inode
2362 @param[in,out] mtr mini-transaction */
2363 static
2364 void
fsp_free_seg_inode(ulint space,const page_size_t & page_size,fseg_inode_t * inode,mtr_t * mtr)2365 fsp_free_seg_inode(
2366 ulint space,
2367 const page_size_t& page_size,
2368 fseg_inode_t* inode,
2369 mtr_t* mtr)
2370 {
2371 page_t* page;
2372 fsp_header_t* space_header;
2373
2374 ut_d(fsp_space_modify_check(space, mtr));
2375
2376 page = page_align(inode);
2377
2378 space_header = fsp_get_space_header(space, page_size, mtr);
2379
2380 ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE);
2381
2382 if (ULINT_UNDEFINED
2383 == fsp_seg_inode_page_find_free(page, 0, page_size, mtr)) {
2384
2385 /* Move the page to another list */
2386
2387 flst_remove(space_header + FSP_SEG_INODES_FULL,
2388 page + FSEG_INODE_PAGE_NODE, mtr);
2389
2390 flst_add_last(space_header + FSP_SEG_INODES_FREE,
2391 page + FSEG_INODE_PAGE_NODE, mtr);
2392 }
2393
2394 mlog_write_ull(inode + FSEG_ID, 0, mtr);
2395 mlog_write_ulint(inode + FSEG_MAGIC_N, 0xfa051ce3, MLOG_4BYTES, mtr);
2396
2397 if (ULINT_UNDEFINED
2398 == fsp_seg_inode_page_find_used(page, page_size, mtr)) {
2399
2400 /* There are no other used headers left on the page: free it */
2401
2402 flst_remove(space_header + FSP_SEG_INODES_FREE,
2403 page + FSEG_INODE_PAGE_NODE, mtr);
2404
2405 fsp_free_page(page_id_t(space, page_get_page_no(page)),
2406 page_size, mtr);
2407 }
2408 }
2409
2410 /** Returns the file segment inode, page x-latched.
2411 @param[in] header segment header
2412 @param[in] space space id
2413 @param[in] page_size page size
2414 @param[in,out] mtr mini-transaction
2415 @param[out] block inode block, or NULL to ignore
2416 @return segment inode, page x-latched; NULL if the inode is free */
2417 static
2418 fseg_inode_t*
fseg_inode_try_get(fseg_header_t * header,ulint space,const page_size_t & page_size,mtr_t * mtr,buf_block_t ** block)2419 fseg_inode_try_get(
2420 fseg_header_t* header,
2421 ulint space,
2422 const page_size_t& page_size,
2423 mtr_t* mtr,
2424 buf_block_t** block)
2425 {
2426 fil_addr_t inode_addr;
2427 fseg_inode_t* inode;
2428
2429 inode_addr.page = mach_read_from_4(header + FSEG_HDR_PAGE_NO);
2430 inode_addr.boffset = mach_read_from_2(header + FSEG_HDR_OFFSET);
2431 ut_ad(space == mach_read_from_4(header + FSEG_HDR_SPACE));
2432
2433 inode = fut_get_ptr(space, page_size, inode_addr, RW_SX_LATCH, mtr,
2434 block);
2435
2436 if (UNIV_UNLIKELY(!mach_read_from_8(inode + FSEG_ID))) {
2437
2438 inode = NULL;
2439 } else {
2440 ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N)
2441 == FSEG_MAGIC_N_VALUE);
2442 }
2443
2444 return(inode);
2445 }
2446
2447 /** Returns the file segment inode, page x-latched.
2448 @param[in] header segment header
2449 @param[in] space space id
2450 @param[in] page_size page size
2451 @param[in,out] mtr mini-transaction
2452 @param[out] block inode block
2453 @return segment inode, page x-latched */
2454 static
2455 fseg_inode_t*
fseg_inode_get(fseg_header_t * header,ulint space,const page_size_t & page_size,mtr_t * mtr,buf_block_t ** block=NULL)2456 fseg_inode_get(
2457 fseg_header_t* header,
2458 ulint space,
2459 const page_size_t& page_size,
2460 mtr_t* mtr,
2461 buf_block_t** block = NULL)
2462 {
2463 fseg_inode_t* inode
2464 = fseg_inode_try_get(header, space, page_size, mtr, block);
2465 ut_a(inode);
2466 return(inode);
2467 }
2468
2469 /**********************************************************************//**
2470 Gets the page number from the nth fragment page slot.
2471 @return page number, FIL_NULL if not in use */
2472 UNIV_INLINE
2473 ulint
fseg_get_nth_frag_page_no(fseg_inode_t * inode,ulint n,mtr_t * mtr MY_ATTRIBUTE ((unused)))2474 fseg_get_nth_frag_page_no(
2475 /*======================*/
2476 fseg_inode_t* inode, /*!< in: segment inode */
2477 ulint n, /*!< in: slot index */
2478 mtr_t* mtr MY_ATTRIBUTE((unused)))
2479 /*!< in/out: mini-transaction */
2480 {
2481 ut_ad(inode && mtr);
2482 ut_ad(n < FSEG_FRAG_ARR_N_SLOTS);
2483 ut_ad(mtr_memo_contains_page(mtr, inode, MTR_MEMO_PAGE_SX_FIX));
2484 ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE);
2485 return(mach_read_from_4(inode + FSEG_FRAG_ARR
2486 + n * FSEG_FRAG_SLOT_SIZE));
2487 }
2488
2489 /**********************************************************************//**
2490 Sets the page number in the nth fragment page slot. */
2491 UNIV_INLINE
2492 void
fseg_set_nth_frag_page_no(fseg_inode_t * inode,ulint n,ulint page_no,mtr_t * mtr)2493 fseg_set_nth_frag_page_no(
2494 /*======================*/
2495 fseg_inode_t* inode, /*!< in: segment inode */
2496 ulint n, /*!< in: slot index */
2497 ulint page_no,/*!< in: page number to set */
2498 mtr_t* mtr) /*!< in/out: mini-transaction */
2499 {
2500 ut_ad(inode && mtr);
2501 ut_ad(n < FSEG_FRAG_ARR_N_SLOTS);
2502 ut_ad(mtr_memo_contains_page(mtr, inode, MTR_MEMO_PAGE_SX_FIX));
2503 ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE);
2504
2505 mlog_write_ulint(inode + FSEG_FRAG_ARR + n * FSEG_FRAG_SLOT_SIZE,
2506 page_no, MLOG_4BYTES, mtr);
2507 }
2508
2509 /**********************************************************************//**
2510 Finds a fragment page slot which is free.
2511 @return slot index; ULINT_UNDEFINED if none found */
2512 static
2513 ulint
fseg_find_free_frag_page_slot(fseg_inode_t * inode,mtr_t * mtr)2514 fseg_find_free_frag_page_slot(
2515 /*==========================*/
2516 fseg_inode_t* inode, /*!< in: segment inode */
2517 mtr_t* mtr) /*!< in/out: mini-transaction */
2518 {
2519 ulint i;
2520 ulint page_no;
2521
2522 ut_ad(inode && mtr);
2523
2524 for (i = 0; i < FSEG_FRAG_ARR_N_SLOTS; i++) {
2525 page_no = fseg_get_nth_frag_page_no(inode, i, mtr);
2526
2527 if (page_no == FIL_NULL) {
2528
2529 return(i);
2530 }
2531 }
2532
2533 return(ULINT_UNDEFINED);
2534 }
2535
2536 /**********************************************************************//**
2537 Finds a fragment page slot which is used and last in the array.
2538 @return slot index; ULINT_UNDEFINED if none found */
2539 static
2540 ulint
fseg_find_last_used_frag_page_slot(fseg_inode_t * inode,mtr_t * mtr)2541 fseg_find_last_used_frag_page_slot(
2542 /*===============================*/
2543 fseg_inode_t* inode, /*!< in: segment inode */
2544 mtr_t* mtr) /*!< in/out: mini-transaction */
2545 {
2546 ulint i;
2547 ulint page_no;
2548
2549 ut_ad(inode && mtr);
2550
2551 for (i = 0; i < FSEG_FRAG_ARR_N_SLOTS; i++) {
2552 page_no = fseg_get_nth_frag_page_no(
2553 inode, FSEG_FRAG_ARR_N_SLOTS - i - 1, mtr);
2554
2555 if (page_no != FIL_NULL) {
2556
2557 return(FSEG_FRAG_ARR_N_SLOTS - i - 1);
2558 }
2559 }
2560
2561 return(ULINT_UNDEFINED);
2562 }
2563
2564 /**********************************************************************//**
2565 Calculates reserved fragment page slots.
2566 @return number of fragment pages */
2567 static
2568 ulint
fseg_get_n_frag_pages(fseg_inode_t * inode,mtr_t * mtr)2569 fseg_get_n_frag_pages(
2570 /*==================*/
2571 fseg_inode_t* inode, /*!< in: segment inode */
2572 mtr_t* mtr) /*!< in/out: mini-transaction */
2573 {
2574 ulint i;
2575 ulint count = 0;
2576
2577 ut_ad(inode && mtr);
2578
2579 for (i = 0; i < FSEG_FRAG_ARR_N_SLOTS; i++) {
2580 if (FIL_NULL != fseg_get_nth_frag_page_no(inode, i, mtr)) {
2581 count++;
2582 }
2583 }
2584
2585 return(count);
2586 }
2587
2588 /**********************************************************************//**
2589 Creates a new segment.
2590 @return the block where the segment header is placed, x-latched, NULL
2591 if could not create segment because of lack of space */
2592 buf_block_t*
fseg_create_general(ulint space_id,ulint page,ulint byte_offset,ibool has_done_reservation,mtr_t * mtr)2593 fseg_create_general(
2594 /*================*/
2595 ulint space_id,/*!< in: space id */
2596 ulint page, /*!< in: page where the segment header is placed: if
2597 this is != 0, the page must belong to another segment,
2598 if this is 0, a new page will be allocated and it
2599 will belong to the created segment */
2600 ulint byte_offset, /*!< in: byte offset of the created segment header
2601 on the page */
2602 ibool has_done_reservation, /*!< in: TRUE if the caller has already
2603 done the reservation for the pages with
2604 fsp_reserve_free_extents (at least 2 extents: one for
2605 the inode and the other for the segment) then there is
2606 no need to do the check for this individual
2607 operation */
2608 mtr_t* mtr) /*!< in/out: mini-transaction */
2609 {
2610 fsp_header_t* space_header;
2611 fseg_inode_t* inode;
2612 ib_id_t seg_id;
2613 buf_block_t* block = 0; /* remove warning */
2614 fseg_header_t* header = 0; /* remove warning */
2615 ulint n_reserved;
2616 ulint i;
2617
2618 DBUG_ENTER("fseg_create_general");
2619
2620 ut_ad(mtr);
2621 ut_ad(byte_offset + FSEG_HEADER_SIZE
2622 <= UNIV_PAGE_SIZE - FIL_PAGE_DATA_END);
2623 ut_d(fsp_space_modify_check(space_id, mtr));
2624
2625 fil_space_t* space = mtr_x_lock_space(space_id, mtr);
2626 const page_size_t page_size(space->flags);
2627
2628 if (page != 0) {
2629 block = buf_page_get(page_id_t(space_id, page), page_size,
2630 RW_SX_LATCH, mtr);
2631
2632 header = byte_offset + buf_block_get_frame(block);
2633
2634 const ulint type = space_id == TRX_SYS_SPACE
2635 && page == TRX_SYS_PAGE_NO
2636 ? FIL_PAGE_TYPE_TRX_SYS
2637 : FIL_PAGE_TYPE_SYS;
2638
2639 fil_block_check_type(block, type, mtr);
2640 }
2641
2642 if (rw_lock_get_x_lock_count(&space->latch) == 1) {
2643 /* This thread did not own the latch before this call: free
2644 excess pages from the insert buffer free list */
2645
2646 if (space_id == IBUF_SPACE_ID) {
2647 ibuf_free_excess_pages();
2648 }
2649 }
2650
2651 if (!has_done_reservation
2652 && !fsp_reserve_free_extents(&n_reserved, space_id, 2,
2653 FSP_NORMAL, mtr)) {
2654 DBUG_RETURN(NULL);
2655 }
2656
2657 space_header = fsp_get_space_header(space_id, page_size, mtr);
2658
2659 inode = fsp_alloc_seg_inode(space_header, mtr);
2660
2661 if (inode == NULL) {
2662
2663 goto funct_exit;
2664 }
2665
2666 /* Read the next segment id from space header and increment the
2667 value in space header */
2668
2669 seg_id = mach_read_from_8(space_header + FSP_SEG_ID);
2670
2671 mlog_write_ull(space_header + FSP_SEG_ID, seg_id + 1, mtr);
2672
2673 mlog_write_ull(inode + FSEG_ID, seg_id, mtr);
2674 mlog_write_ulint(inode + FSEG_NOT_FULL_N_USED, 0, MLOG_4BYTES, mtr);
2675
2676 flst_init(inode + FSEG_FREE, mtr);
2677 flst_init(inode + FSEG_NOT_FULL, mtr);
2678 flst_init(inode + FSEG_FULL, mtr);
2679
2680 mlog_write_ulint(inode + FSEG_MAGIC_N, FSEG_MAGIC_N_VALUE,
2681 MLOG_4BYTES, mtr);
2682 for (i = 0; i < FSEG_FRAG_ARR_N_SLOTS; i++) {
2683 fseg_set_nth_frag_page_no(inode, i, FIL_NULL, mtr);
2684 }
2685
2686 if (page == 0) {
2687 block = fseg_alloc_free_page_low(space, page_size,
2688 inode, 0, FSP_UP, RW_SX_LATCH,
2689 mtr, mtr
2690 #ifdef UNIV_DEBUG
2691 , has_done_reservation
2692 #endif /* UNIV_DEBUG */
2693 );
2694
2695 /* The allocation cannot fail if we have already reserved a
2696 space for the page. */
2697 ut_ad(!has_done_reservation || block != NULL);
2698
2699 if (block == NULL) {
2700
2701 fsp_free_seg_inode(space_id, page_size, inode, mtr);
2702
2703 goto funct_exit;
2704 }
2705
2706 ut_ad(rw_lock_get_sx_lock_count(&block->lock) == 1);
2707
2708 header = byte_offset + buf_block_get_frame(block);
2709 mlog_write_ulint(buf_block_get_frame(block) + FIL_PAGE_TYPE,
2710 FIL_PAGE_TYPE_SYS, MLOG_2BYTES, mtr);
2711 }
2712
2713 mlog_write_ulint(header + FSEG_HDR_OFFSET,
2714 page_offset(inode), MLOG_2BYTES, mtr);
2715
2716 mlog_write_ulint(header + FSEG_HDR_PAGE_NO,
2717 page_get_page_no(page_align(inode)),
2718 MLOG_4BYTES, mtr);
2719
2720 mlog_write_ulint(header + FSEG_HDR_SPACE, space_id, MLOG_4BYTES, mtr);
2721
2722 funct_exit:
2723 if (!has_done_reservation) {
2724
2725 fil_space_release_free_extents(space_id, n_reserved);
2726 }
2727
2728 DBUG_RETURN(block);
2729 }
2730
2731 /**********************************************************************//**
2732 Creates a new segment.
2733 @return the block where the segment header is placed, x-latched, NULL
2734 if could not create segment because of lack of space */
2735 buf_block_t*
fseg_create(ulint space,ulint page,ulint byte_offset,mtr_t * mtr)2736 fseg_create(
2737 /*========*/
2738 ulint space, /*!< in: space id */
2739 ulint page, /*!< in: page where the segment header is placed: if
2740 this is != 0, the page must belong to another segment,
2741 if this is 0, a new page will be allocated and it
2742 will belong to the created segment */
2743 ulint byte_offset, /*!< in: byte offset of the created segment header
2744 on the page */
2745 mtr_t* mtr) /*!< in/out: mini-transaction */
2746 {
2747 return(fseg_create_general(space, page, byte_offset, FALSE, mtr));
2748 }
2749
2750 /**********************************************************************//**
2751 Calculates the number of pages reserved by a segment, and how many pages are
2752 currently used.
2753 @return number of reserved pages */
2754 static
2755 ulint
fseg_n_reserved_pages_low(fseg_inode_t * inode,ulint * used,mtr_t * mtr)2756 fseg_n_reserved_pages_low(
2757 /*======================*/
2758 fseg_inode_t* inode, /*!< in: segment inode */
2759 ulint* used, /*!< out: number of pages used (not
2760 more than reserved) */
2761 mtr_t* mtr) /*!< in/out: mini-transaction */
2762 {
2763 ulint ret;
2764
2765 ut_ad(inode && used && mtr);
2766 ut_ad(mtr_memo_contains_page(mtr, inode, MTR_MEMO_PAGE_SX_FIX));
2767
2768 *used = mach_read_from_4(inode + FSEG_NOT_FULL_N_USED)
2769 + FSP_EXTENT_SIZE * flst_get_len(inode + FSEG_FULL)
2770 + fseg_get_n_frag_pages(inode, mtr);
2771
2772 ret = fseg_get_n_frag_pages(inode, mtr)
2773 + FSP_EXTENT_SIZE * flst_get_len(inode + FSEG_FREE)
2774 + FSP_EXTENT_SIZE * flst_get_len(inode + FSEG_NOT_FULL)
2775 + FSP_EXTENT_SIZE * flst_get_len(inode + FSEG_FULL);
2776
2777 return(ret);
2778 }
2779
2780 /**********************************************************************//**
2781 Calculates the number of pages reserved by a segment, and how many pages are
2782 currently used.
2783 @return number of reserved pages */
2784 ulint
fseg_n_reserved_pages(fseg_header_t * header,ulint * used,mtr_t * mtr)2785 fseg_n_reserved_pages(
2786 /*==================*/
2787 fseg_header_t* header, /*!< in: segment header */
2788 ulint* used, /*!< out: number of pages used (<= reserved) */
2789 mtr_t* mtr) /*!< in/out: mini-transaction */
2790 {
2791 ulint ret;
2792 fseg_inode_t* inode;
2793 ulint space_id;
2794 fil_space_t* space;
2795
2796 space_id = page_get_space_id(page_align(header));
2797 space = mtr_x_lock_space(space_id, mtr);
2798
2799 const page_size_t page_size(space->flags);
2800
2801 inode = fseg_inode_get(header, space_id, page_size, mtr);
2802
2803 ret = fseg_n_reserved_pages_low(inode, used, mtr);
2804
2805 return(ret);
2806 }
2807
2808 /** Tries to fill the free list of a segment with consecutive free extents.
2809 This happens if the segment is big enough to allow extents in the free list,
2810 the free list is empty, and the extents can be allocated consecutively from
2811 the hint onward.
2812 @param[in] inode segment inode
2813 @param[in] space space id
2814 @param[in] page_size page size
2815 @param[in] hint hint which extent would be good as the first
2816 extent
2817 @param[in,out] mtr mini-transaction */
2818 static
2819 void
fseg_fill_free_list(fseg_inode_t * inode,ulint space,const page_size_t & page_size,ulint hint,mtr_t * mtr)2820 fseg_fill_free_list(
2821 fseg_inode_t* inode,
2822 ulint space,
2823 const page_size_t& page_size,
2824 ulint hint,
2825 mtr_t* mtr)
2826 {
2827 xdes_t* descr;
2828 ulint i;
2829 ib_id_t seg_id;
2830 ulint reserved;
2831 ulint used;
2832
2833 ut_ad(inode && mtr);
2834 ut_ad(!((page_offset(inode) - FSEG_ARR_OFFSET) % FSEG_INODE_SIZE));
2835 ut_d(fsp_space_modify_check(space, mtr));
2836
2837 reserved = fseg_n_reserved_pages_low(inode, &used, mtr);
2838
2839 if (reserved < FSEG_FREE_LIST_LIMIT * FSP_EXTENT_SIZE) {
2840
2841 /* The segment is too small to allow extents in free list */
2842
2843 return;
2844 }
2845
2846 if (flst_get_len(inode + FSEG_FREE) > 0) {
2847 /* Free list is not empty */
2848
2849 return;
2850 }
2851
2852 for (i = 0; i < FSEG_FREE_LIST_MAX_LEN; i++) {
2853 descr = xdes_get_descriptor(space, hint, page_size, mtr);
2854
2855 if ((descr == NULL)
2856 || (XDES_FREE != xdes_get_state(descr, mtr))) {
2857
2858 /* We cannot allocate the desired extent: stop */
2859
2860 return;
2861 }
2862
2863 descr = fsp_alloc_free_extent(space, page_size, hint, mtr);
2864
2865 xdes_set_state(descr, XDES_FSEG, mtr);
2866
2867 seg_id = mach_read_from_8(inode + FSEG_ID);
2868 ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N)
2869 == FSEG_MAGIC_N_VALUE);
2870 mlog_write_ull(descr + XDES_ID, seg_id, mtr);
2871
2872 flst_add_last(inode + FSEG_FREE, descr + XDES_FLST_NODE, mtr);
2873 hint += FSP_EXTENT_SIZE;
2874 }
2875 }
2876
2877 /** Allocates a free extent for the segment: looks first in the free list of
2878 the segment, then tries to allocate from the space free list.
2879 NOTE that the extent returned still resides in the segment free list, it is
2880 not yet taken off it!
2881 @param[in] inode segment inode
2882 @param[in] space space id
2883 @param[in] page_size page size
2884 @param[in,out] mtr mini-transaction
2885 @retval NULL if no page could be allocated
2886 @retval block rw_lock_x_lock_count(&block->lock) == 1 if allocation succeeded
2887 (init_mtr == mtr, or the page was not previously freed in mtr)
2888 @retval block (not allocated or initialized) otherwise */
2889 static
2890 xdes_t*
fseg_alloc_free_extent(fseg_inode_t * inode,ulint space,const page_size_t & page_size,mtr_t * mtr)2891 fseg_alloc_free_extent(
2892 fseg_inode_t* inode,
2893 ulint space,
2894 const page_size_t& page_size,
2895 mtr_t* mtr)
2896 {
2897 xdes_t* descr;
2898 ib_id_t seg_id;
2899 fil_addr_t first;
2900
2901 ut_ad(!((page_offset(inode) - FSEG_ARR_OFFSET) % FSEG_INODE_SIZE));
2902 ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE);
2903 ut_d(fsp_space_modify_check(space, mtr));
2904
2905 if (flst_get_len(inode + FSEG_FREE) > 0) {
2906 /* Segment free list is not empty, allocate from it */
2907
2908 first = flst_get_first(inode + FSEG_FREE, mtr);
2909
2910 descr = xdes_lst_get_descriptor(space, page_size, first, mtr);
2911 } else {
2912 /* Segment free list was empty, allocate from space */
2913 descr = fsp_alloc_free_extent(space, page_size, 0, mtr);
2914
2915 if (descr == NULL) {
2916
2917 return(NULL);
2918 }
2919
2920 seg_id = mach_read_from_8(inode + FSEG_ID);
2921
2922 xdes_set_state(descr, XDES_FSEG, mtr);
2923 mlog_write_ull(descr + XDES_ID, seg_id, mtr);
2924 flst_add_last(inode + FSEG_FREE, descr + XDES_FLST_NODE, mtr);
2925
2926 /* Try to fill the segment free list */
2927 fseg_fill_free_list(inode, space, page_size,
2928 xdes_get_offset(descr) + FSP_EXTENT_SIZE,
2929 mtr);
2930 }
2931
2932 return(descr);
2933 }
2934
2935 /** Allocates a single free page from a segment.
2936 This function implements the intelligent allocation strategy which tries to
2937 minimize file space fragmentation.
2938 @param[in,out] space tablespace
2939 @param[in] page_size page size
2940 @param[in,out] seg_inode segment inode
2941 @param[in] hint hint of which page would be desirable
2942 @param[in] direction if the new page is needed because of
2943 an index page split, and records are inserted there in order, into which
2944 direction they go alphabetically: FSP_DOWN, FSP_UP, FSP_NO_DIR
2945 @param[in] rw_latch RW_SX_LATCH, RW_X_LATCH
2946 @param[in,out] mtr mini-transaction
2947 @param[in,out] init_mtr mtr or another mini-transaction in
2948 which the page should be initialized. If init_mtr != mtr, but the page is
2949 already latched in mtr, do not initialize the page
2950 @param[in] has_done_reservation TRUE if the space has already been
2951 reserved, in this case we will never return NULL
2952 @retval NULL if no page could be allocated
2953 @retval block rw_lock_x_lock_count(&block->lock) == 1 if allocation succeeded
2954 (init_mtr == mtr, or the page was not previously freed in mtr)
2955 @retval block (not allocated or initialized) otherwise */
2956 static
2957 buf_block_t*
fseg_alloc_free_page_low(fil_space_t * space,const page_size_t & page_size,fseg_inode_t * seg_inode,ulint hint,byte direction,rw_lock_type_t rw_latch,mtr_t * mtr,mtr_t * init_mtr,ibool has_done_reservation)2958 fseg_alloc_free_page_low(
2959 fil_space_t* space,
2960 const page_size_t& page_size,
2961 fseg_inode_t* seg_inode,
2962 ulint hint,
2963 byte direction,
2964 rw_lock_type_t rw_latch,
2965 mtr_t* mtr,
2966 mtr_t* init_mtr
2967 #ifdef UNIV_DEBUG
2968 , ibool has_done_reservation
2969 #endif /* UNIV_DEBUG */
2970 )
2971 {
2972 fsp_header_t* space_header;
2973 ib_id_t seg_id;
2974 ulint used;
2975 ulint reserved;
2976 xdes_t* descr; /*!< extent of the hinted page */
2977 ulint ret_page; /*!< the allocated page offset, FIL_NULL
2978 if could not be allocated */
2979 xdes_t* ret_descr; /*!< the extent of the allocated page */
2980 ulint n;
2981 const ulint space_id = space->id;
2982
2983 ut_ad(mtr);
2984 ut_ad((direction >= FSP_UP) && (direction <= FSP_NO_DIR));
2985 ut_ad(mach_read_from_4(seg_inode + FSEG_MAGIC_N)
2986 == FSEG_MAGIC_N_VALUE);
2987 ut_ad(!((page_offset(seg_inode) - FSEG_ARR_OFFSET) % FSEG_INODE_SIZE));
2988 ut_ad(space->purpose == FIL_TYPE_TEMPORARY
2989 || space->purpose == FIL_TYPE_TABLESPACE);
2990 seg_id = mach_read_from_8(seg_inode + FSEG_ID);
2991
2992 ut_ad(seg_id);
2993 ut_d(fsp_space_modify_check(space_id, mtr));
2994 ut_ad(fil_page_get_type(page_align(seg_inode)) == FIL_PAGE_INODE);
2995
2996 reserved = fseg_n_reserved_pages_low(seg_inode, &used, mtr);
2997
2998 space_header = fsp_get_space_header(space_id, page_size, mtr);
2999
3000 descr = xdes_get_descriptor_with_space_hdr(space_header, space_id,
3001 hint, mtr);
3002 if (descr == NULL) {
3003 /* Hint outside space or too high above free limit: reset
3004 hint */
3005 /* The file space header page is always allocated. */
3006 hint = 0;
3007 descr = xdes_get_descriptor(space_id, hint, page_size, mtr);
3008 }
3009
3010 /* In the big if-else below we look for ret_page and ret_descr */
3011 /*-------------------------------------------------------------*/
3012 if ((xdes_get_state(descr, mtr) == XDES_FSEG)
3013 && mach_read_from_8(descr + XDES_ID) == seg_id
3014 && (xdes_mtr_get_bit(descr, XDES_FREE_BIT,
3015 hint % FSP_EXTENT_SIZE, mtr) == TRUE)) {
3016 take_hinted_page:
3017 /* 1. We can take the hinted page
3018 =================================*/
3019 ret_descr = descr;
3020 ret_page = hint;
3021 /* Skip the check for extending the tablespace. If the
3022 page hint were not within the size of the tablespace,
3023 we would have got (descr == NULL) above and reset the hint. */
3024 goto got_hinted_page;
3025 /*-----------------------------------------------------------*/
3026 } else if (xdes_get_state(descr, mtr) == XDES_FREE
3027 && reserved - used < reserved / FSEG_FILLFACTOR
3028 && used >= FSEG_FRAG_LIMIT) {
3029
3030 /* 2. We allocate the free extent from space and can take
3031 =========================================================
3032 the hinted page
3033 ===============*/
3034 ret_descr = fsp_alloc_free_extent(
3035 space_id, page_size, hint, mtr);
3036
3037 ut_a(ret_descr == descr);
3038
3039 xdes_set_state(ret_descr, XDES_FSEG, mtr);
3040 mlog_write_ull(ret_descr + XDES_ID, seg_id, mtr);
3041 flst_add_last(seg_inode + FSEG_FREE,
3042 ret_descr + XDES_FLST_NODE, mtr);
3043
3044 /* Try to fill the segment free list */
3045 fseg_fill_free_list(seg_inode, space_id, page_size,
3046 hint + FSP_EXTENT_SIZE, mtr);
3047 goto take_hinted_page;
3048 /*-----------------------------------------------------------*/
3049 } else if ((direction != FSP_NO_DIR)
3050 && ((reserved - used) < reserved / FSEG_FILLFACTOR)
3051 && (used >= FSEG_FRAG_LIMIT)
3052 && (!!(ret_descr
3053 = fseg_alloc_free_extent(
3054 seg_inode, space_id, page_size, mtr)))) {
3055
3056 /* 3. We take any free extent (which was already assigned above
3057 ===============================================================
3058 in the if-condition to ret_descr) and take the lowest or
3059 ========================================================
3060 highest page in it, depending on the direction
3061 ==============================================*/
3062 ret_page = xdes_get_offset(ret_descr);
3063
3064 if (direction == FSP_DOWN) {
3065 ret_page += FSP_EXTENT_SIZE - 1;
3066 }
3067 ut_ad(!has_done_reservation || ret_page != FIL_NULL);
3068 /*-----------------------------------------------------------*/
3069 } else if ((xdes_get_state(descr, mtr) == XDES_FSEG)
3070 && mach_read_from_8(descr + XDES_ID) == seg_id
3071 && (!xdes_is_full(descr, mtr))) {
3072
3073 /* 4. We can take the page from the same extent as the
3074 ======================================================
3075 hinted page (and the extent already belongs to the
3076 ==================================================
3077 segment)
3078 ========*/
3079 ret_descr = descr;
3080 ret_page = xdes_get_offset(ret_descr)
3081 + xdes_find_bit(ret_descr, XDES_FREE_BIT, TRUE,
3082 hint % FSP_EXTENT_SIZE, mtr);
3083 ut_ad(!has_done_reservation || ret_page != FIL_NULL);
3084 /*-----------------------------------------------------------*/
3085 } else if (reserved - used > 0) {
3086 /* 5. We take any unused page from the segment
3087 ==============================================*/
3088 fil_addr_t first;
3089
3090 if (flst_get_len(seg_inode + FSEG_NOT_FULL) > 0) {
3091 first = flst_get_first(seg_inode + FSEG_NOT_FULL,
3092 mtr);
3093 } else if (flst_get_len(seg_inode + FSEG_FREE) > 0) {
3094 first = flst_get_first(seg_inode + FSEG_FREE, mtr);
3095 } else {
3096 ut_ad(!has_done_reservation);
3097 return(NULL);
3098 }
3099
3100 ret_descr = xdes_lst_get_descriptor(space_id, page_size,
3101 first, mtr);
3102 ret_page = xdes_get_offset(ret_descr)
3103 + xdes_find_bit(ret_descr, XDES_FREE_BIT, TRUE,
3104 0, mtr);
3105 ut_ad(!has_done_reservation || ret_page != FIL_NULL);
3106 /*-----------------------------------------------------------*/
3107 } else if (used < FSEG_FRAG_LIMIT) {
3108 /* 6. We allocate an individual page from the space
3109 ===================================================*/
3110 buf_block_t* block = fsp_alloc_free_page(
3111 space_id, page_size, hint, rw_latch, mtr, init_mtr);
3112
3113 ut_ad(!has_done_reservation || block != NULL);
3114
3115 if (block != NULL) {
3116 /* Put the page in the fragment page array of the
3117 segment */
3118 n = fseg_find_free_frag_page_slot(seg_inode, mtr);
3119 ut_a(n != ULINT_UNDEFINED);
3120
3121 fseg_set_nth_frag_page_no(
3122 seg_inode, n, block->page.id.page_no(),
3123 mtr);
3124 }
3125
3126 /* fsp_alloc_free_page() invoked fsp_init_file_page()
3127 already. */
3128 return(block);
3129 /*-----------------------------------------------------------*/
3130 } else {
3131 /* 7. We allocate a new extent and take its first page
3132 ======================================================*/
3133 ret_descr = fseg_alloc_free_extent(seg_inode,
3134 space_id, page_size, mtr);
3135
3136 if (ret_descr == NULL) {
3137 ret_page = FIL_NULL;
3138 ut_ad(!has_done_reservation);
3139 } else {
3140 ret_page = xdes_get_offset(ret_descr);
3141 ut_ad(!has_done_reservation || ret_page != FIL_NULL);
3142 }
3143 }
3144
3145 if (ret_page == FIL_NULL) {
3146 /* Page could not be allocated */
3147
3148 ut_ad(!has_done_reservation);
3149 return(NULL);
3150 }
3151
3152 if (space->size <= ret_page && !is_system_tablespace(space_id)) {
3153 /* It must be that we are extending a single-table
3154 tablespace whose size is still < 64 pages */
3155
3156 if (ret_page >= FSP_EXTENT_SIZE) {
3157 ib::error() << "Error (2): trying to extend"
3158 " a single-table tablespace " << space_id
3159 << " by single page(s) though the"
3160 << " space size " << space->size
3161 << ". Page no " << ret_page << ".";
3162 ut_ad(!has_done_reservation);
3163 return(NULL);
3164 }
3165
3166 if (!fsp_try_extend_data_file_with_pages(
3167 space, ret_page, space_header, mtr)) {
3168 /* No disk space left */
3169 ut_ad(!has_done_reservation);
3170 return(NULL);
3171 }
3172 }
3173
3174 got_hinted_page:
3175 /* ret_descr == NULL if the block was allocated from free_frag
3176 (XDES_FREE_FRAG) */
3177 if (ret_descr != NULL) {
3178 /* At this point we know the extent and the page offset.
3179 The extent is still in the appropriate list (FSEG_NOT_FULL
3180 or FSEG_FREE), and the page is not yet marked as used. */
3181
3182 ut_ad(xdes_get_descriptor(space_id, ret_page, page_size, mtr)
3183 == ret_descr);
3184
3185 ut_ad(xdes_mtr_get_bit(
3186 ret_descr, XDES_FREE_BIT,
3187 ret_page % FSP_EXTENT_SIZE, mtr));
3188
3189 fseg_mark_page_used(seg_inode, ret_page, ret_descr, mtr);
3190 }
3191
3192 ut_ad(space->flags
3193 == mach_read_from_4(FSP_SPACE_FLAGS + space_header));
3194 return(fsp_page_create(page_id_t(space_id, ret_page), page_size,
3195 rw_latch, mtr, init_mtr));
3196 }
3197
3198 /**********************************************************************//**
3199 Allocates a single free page from a segment. This function implements
3200 the intelligent allocation strategy which tries to minimize file space
3201 fragmentation.
3202 @retval NULL if no page could be allocated
3203 @retval block, rw_lock_x_lock_count(&block->lock) == 1 if allocation succeeded
3204 (init_mtr == mtr, or the page was not previously freed in mtr)
3205 @retval block (not allocated or initialized) otherwise */
3206 buf_block_t*
fseg_alloc_free_page_general(fseg_header_t * seg_header,ulint hint,byte direction,ibool has_done_reservation,mtr_t * mtr,mtr_t * init_mtr)3207 fseg_alloc_free_page_general(
3208 /*=========================*/
3209 fseg_header_t* seg_header,/*!< in/out: segment header */
3210 ulint hint, /*!< in: hint of which page would be
3211 desirable */
3212 byte direction,/*!< in: if the new page is needed because
3213 of an index page split, and records are
3214 inserted there in order, into which
3215 direction they go alphabetically: FSP_DOWN,
3216 FSP_UP, FSP_NO_DIR */
3217 ibool has_done_reservation, /*!< in: TRUE if the caller has
3218 already done the reservation for the page
3219 with fsp_reserve_free_extents, then there
3220 is no need to do the check for this individual
3221 page */
3222 mtr_t* mtr, /*!< in/out: mini-transaction */
3223 mtr_t* init_mtr)/*!< in/out: mtr or another mini-transaction
3224 in which the page should be initialized.
3225 If init_mtr!=mtr, but the page is already
3226 latched in mtr, do not initialize the page. */
3227 {
3228 fseg_inode_t* inode;
3229 ulint space_id;
3230 fil_space_t* space;
3231 buf_block_t* iblock;
3232 buf_block_t* block;
3233 ulint n_reserved;
3234
3235 space_id = page_get_space_id(page_align(seg_header));
3236 space = mtr_x_lock_space(space_id, mtr);
3237 const page_size_t page_size(space->flags);
3238
3239 if (rw_lock_get_x_lock_count(&space->latch) == 1) {
3240 /* This thread did not own the latch before this call: free
3241 excess pages from the insert buffer free list */
3242
3243 if (space_id == IBUF_SPACE_ID) {
3244 ibuf_free_excess_pages();
3245 }
3246 }
3247
3248 inode = fseg_inode_get(seg_header, space_id, page_size, mtr, &iblock);
3249 fil_block_check_type(iblock, FIL_PAGE_INODE, mtr);
3250
3251 if (!has_done_reservation
3252 && !fsp_reserve_free_extents(&n_reserved, space_id, 2,
3253 FSP_NORMAL, mtr)) {
3254 return(NULL);
3255 }
3256
3257 block = fseg_alloc_free_page_low(space, page_size,
3258 inode, hint, direction,
3259 RW_X_LATCH, mtr, init_mtr
3260 #ifdef UNIV_DEBUG
3261 , has_done_reservation
3262 #endif /* UNIV_DEBUG */
3263 );
3264
3265 /* The allocation cannot fail if we have already reserved a
3266 space for the page. */
3267 ut_ad(!has_done_reservation || block != NULL);
3268
3269 if (!has_done_reservation) {
3270 fil_space_release_free_extents(space_id, n_reserved);
3271 }
3272
3273 return(block);
3274 }
3275
3276 /** Check that we have at least n_pages frag pages free in the first extent
3277 of a single-table tablespace, and they are also physically initialized to
3278 the data file. That is we have already extended the data file so that those
3279 pages are inside the data file. If not, this function extends the tablespace
3280 with pages.
3281 @param[in,out] space tablespace
3282 @param[in,out] space_header tablespace header, x-latched
3283 @param[in] size size of the tablespace in pages,
3284 must be less than FSP_EXTENT_SIZE
3285 @param[in,out] mtr mini-transaction
3286 @param[in] n_pages number of pages to reserve
3287 @return true if there were at least n_pages free pages, or we were able
3288 to extend */
3289 static
3290 bool
fsp_reserve_free_pages(fil_space_t * space,fsp_header_t * space_header,ulint size,mtr_t * mtr,ulint n_pages)3291 fsp_reserve_free_pages(
3292 fil_space_t* space,
3293 fsp_header_t* space_header,
3294 ulint size,
3295 mtr_t* mtr,
3296 ulint n_pages)
3297 {
3298 xdes_t* descr;
3299 ulint n_used;
3300
3301 ut_a(!is_system_tablespace(space->id));
3302 ut_a(size < FSP_EXTENT_SIZE);
3303
3304 descr = xdes_get_descriptor_with_space_hdr(
3305 space_header, space->id, 0, mtr);
3306 n_used = xdes_get_n_used(descr, mtr);
3307
3308 ut_a(n_used <= size);
3309
3310 return(size >= n_used + n_pages
3311 || fsp_try_extend_data_file_with_pages(
3312 space, n_used + n_pages - 1, space_header, mtr));
3313 }
3314
3315 /** Reserves free pages from a tablespace. All mini-transactions which may
3316 use several pages from the tablespace should call this function beforehand
3317 and reserve enough free extents so that they certainly will be able
3318 to do their operation, like a B-tree page split, fully. Reservations
3319 must be released with function fil_space_release_free_extents!
3320
3321 The alloc_type below has the following meaning: FSP_NORMAL means an
3322 operation which will probably result in more space usage, like an
3323 insert in a B-tree; FSP_UNDO means allocation to undo logs: if we are
3324 deleting rows, then this allocation will in the long run result in
3325 less space usage (after a purge); FSP_CLEANING means allocation done
3326 in a physical record delete (like in a purge) or other cleaning operation
3327 which will result in less space usage in the long run. We prefer the latter
3328 two types of allocation: when space is scarce, FSP_NORMAL allocations
3329 will not succeed, but the latter two allocations will succeed, if possible.
3330 The purpose is to avoid dead end where the database is full but the
3331 user cannot free any space because these freeing operations temporarily
3332 reserve some space.
3333
3334 Single-table tablespaces whose size is < FSP_EXTENT_SIZE pages are a special
3335 case. In this function we would liberally reserve several extents for
3336 every page split or merge in a B-tree. But we do not want to waste disk space
3337 if the table only occupies < FSP_EXTENT_SIZE pages. That is why we apply
3338 different rules in that special case, just ensuring that there are n_pages
3339 free pages available.
3340
3341 @param[out] n_reserved number of extents actually reserved; if we
3342 return true and the tablespace size is <
3343 FSP_EXTENT_SIZE pages, then this can be 0,
3344 otherwise it is n_ext
3345 @param[in] space_id tablespace identifier
3346 @param[in] n_ext number of extents to reserve
3347 @param[in] alloc_type page reservation type (FSP_BLOB, etc)
3348 @param[in,out] mtr the mini transaction
3349 @param[in] n_pages for small tablespaces (tablespace size is
3350 less than FSP_EXTENT_SIZE), number of free
3351 pages to reserve.
3352 @return true if we were able to make the reservation */
3353 bool
fsp_reserve_free_extents(ulint * n_reserved,ulint space_id,ulint n_ext,fsp_reserve_t alloc_type,mtr_t * mtr,ulint n_pages)3354 fsp_reserve_free_extents(
3355 ulint* n_reserved,
3356 ulint space_id,
3357 ulint n_ext,
3358 fsp_reserve_t alloc_type,
3359 mtr_t* mtr,
3360 ulint n_pages)
3361 {
3362 fsp_header_t* space_header;
3363 ulint n_free_list_ext;
3364 ulint free_limit;
3365 ulint size;
3366 ulint n_free;
3367 ulint n_free_up;
3368 ulint reserve;
3369
3370 ut_ad(mtr);
3371 *n_reserved = n_ext;
3372
3373 fil_space_t* space = mtr_x_lock_space(space_id, mtr);
3374 const page_size_t page_size(space->flags);
3375
3376 space_header = fsp_get_space_header(space_id, page_size, mtr);
3377 try_again:
3378 size = mach_read_from_4(space_header + FSP_SIZE);
3379 ut_ad(size == space->size_in_header);
3380
3381 if (size < FSP_EXTENT_SIZE && n_pages < FSP_EXTENT_SIZE / 2) {
3382 /* Use different rules for small single-table tablespaces */
3383 *n_reserved = 0;
3384 return(fsp_reserve_free_pages(space, space_header, size,
3385 mtr, n_pages));
3386 }
3387
3388 n_free_list_ext = flst_get_len(space_header + FSP_FREE);
3389 ut_ad(space->free_len == n_free_list_ext);
3390
3391 free_limit = mtr_read_ulint(space_header + FSP_FREE_LIMIT,
3392 MLOG_4BYTES, mtr);
3393 ut_ad(space->free_limit == free_limit);
3394
3395 /* Below we play safe when counting free extents above the free limit:
3396 some of them will contain extent descriptor pages, and therefore
3397 will not be free extents */
3398
3399 if (size >= free_limit) {
3400 n_free_up = (size - free_limit) / FSP_EXTENT_SIZE;
3401 } else {
3402 ut_ad(alloc_type == FSP_BLOB);
3403 n_free_up = 0;
3404 }
3405
3406 if (n_free_up > 0) {
3407 n_free_up--;
3408 n_free_up -= n_free_up / (page_size.physical()
3409 / FSP_EXTENT_SIZE);
3410 }
3411
3412 n_free = n_free_list_ext + n_free_up;
3413
3414 switch (alloc_type) {
3415 case FSP_NORMAL:
3416 /* We reserve 1 extent + 0.5 % of the space size to undo logs
3417 and 1 extent + 0.5 % to cleaning operations; NOTE: this source
3418 code is duplicated in the function below! */
3419
3420 reserve = 2 + ((size / FSP_EXTENT_SIZE) * 2) / 200;
3421
3422 if (n_free <= reserve + n_ext) {
3423
3424 goto try_to_extend;
3425 }
3426 break;
3427 case FSP_UNDO:
3428 /* We reserve 0.5 % of the space size to cleaning operations */
3429
3430 reserve = 1 + ((size / FSP_EXTENT_SIZE) * 1) / 200;
3431
3432 if (n_free <= reserve + n_ext) {
3433
3434 goto try_to_extend;
3435 }
3436 break;
3437 case FSP_CLEANING:
3438 case FSP_BLOB:
3439 break;
3440 default:
3441 ut_error;
3442 }
3443
3444 if (fil_space_reserve_free_extents(space_id, n_free, n_ext)) {
3445 return(true);
3446 }
3447 try_to_extend:
3448 if (fsp_try_extend_data_file(space, space_header, mtr)) {
3449 goto try_again;
3450 }
3451
3452 return(false);
3453 }
3454
3455 /** Calculate how many KiB of new data we will be able to insert to the
3456 tablespace without running out of space.
3457 @param[in] space_id tablespace ID
3458 @return available space in KiB
3459 @retval UINTMAX_MAX if unknown */
3460 uintmax_t
fsp_get_available_space_in_free_extents(ulint space_id)3461 fsp_get_available_space_in_free_extents(
3462 ulint space_id)
3463 {
3464 FilSpace space(space_id);
3465 if (space() == NULL) {
3466 return(UINTMAX_MAX);
3467 }
3468
3469 return(fsp_get_available_space_in_free_extents(space));
3470 }
3471
3472 /** Calculate how many KiB of new data we will be able to insert to the
3473 tablespace without running out of space. Start with a space object that has
3474 been acquired by the caller who holds it for the calculation,
3475 @param[in] space tablespace object from fil_space_acquire()
3476 @return available space in KiB */
3477 uintmax_t
fsp_get_available_space_in_free_extents(const fil_space_t * space)3478 fsp_get_available_space_in_free_extents(
3479 const fil_space_t* space)
3480 {
3481 ut_ad(space->n_pending_ops > 0);
3482
3483 ulint size_in_header = space->size_in_header;
3484 if (size_in_header < FSP_EXTENT_SIZE) {
3485 return(0); /* TODO: count free frag pages and
3486 return a value based on that */
3487 }
3488
3489 /* Below we play safe when counting free extents above the free limit:
3490 some of them will contain extent descriptor pages, and therefore
3491 will not be free extents */
3492 ut_ad(size_in_header >= space->free_limit);
3493 ulint n_free_up =
3494 (size_in_header - space->free_limit) / FSP_EXTENT_SIZE;
3495
3496 page_size_t page_size(space->flags);
3497 if (n_free_up > 0) {
3498 n_free_up--;
3499 n_free_up -= n_free_up / (page_size.physical()
3500 / FSP_EXTENT_SIZE);
3501 }
3502
3503 /* We reserve 1 extent + 0.5 % of the space size to undo logs
3504 and 1 extent + 0.5 % to cleaning operations; NOTE: this source
3505 code is duplicated in the function above! */
3506
3507 ulint reserve = 2 + ((size_in_header / FSP_EXTENT_SIZE) * 2) / 200;
3508 ulint n_free = space->free_len + n_free_up;
3509
3510 if (reserve > n_free) {
3511 return(0);
3512 }
3513
3514 return(static_cast<uintmax_t>(n_free - reserve)
3515 * FSP_EXTENT_SIZE * (page_size.physical() / 1024));
3516 }
3517
3518 /********************************************************************//**
3519 Marks a page used. The page must reside within the extents of the given
3520 segment. */
3521 static
3522 void
fseg_mark_page_used(fseg_inode_t * seg_inode,ulint page,xdes_t * descr,mtr_t * mtr)3523 fseg_mark_page_used(
3524 /*================*/
3525 fseg_inode_t* seg_inode,/*!< in: segment inode */
3526 ulint page, /*!< in: page offset */
3527 xdes_t* descr, /*!< in: extent descriptor */
3528 mtr_t* mtr) /*!< in/out: mini-transaction */
3529 {
3530 ulint not_full_n_used;
3531
3532 ut_ad(fil_page_get_type(page_align(seg_inode)) == FIL_PAGE_INODE);
3533 ut_ad(!((page_offset(seg_inode) - FSEG_ARR_OFFSET) % FSEG_INODE_SIZE));
3534 ut_ad(mach_read_from_4(seg_inode + FSEG_MAGIC_N)
3535 == FSEG_MAGIC_N_VALUE);
3536
3537 ut_ad(mtr_read_ulint(seg_inode + FSEG_ID, MLOG_4BYTES, mtr)
3538 == mtr_read_ulint(descr + XDES_ID, MLOG_4BYTES, mtr));
3539
3540 if (xdes_is_free(descr, mtr)) {
3541 /* We move the extent from the free list to the
3542 NOT_FULL list */
3543 flst_remove(seg_inode + FSEG_FREE, descr + XDES_FLST_NODE,
3544 mtr);
3545 flst_add_last(seg_inode + FSEG_NOT_FULL,
3546 descr + XDES_FLST_NODE, mtr);
3547 }
3548
3549 ut_ad(xdes_mtr_get_bit(
3550 descr, XDES_FREE_BIT, page % FSP_EXTENT_SIZE, mtr));
3551
3552 /* We mark the page as used */
3553 xdes_set_bit(descr, XDES_FREE_BIT, page % FSP_EXTENT_SIZE, FALSE, mtr);
3554
3555 not_full_n_used = mtr_read_ulint(seg_inode + FSEG_NOT_FULL_N_USED,
3556 MLOG_4BYTES, mtr);
3557 not_full_n_used++;
3558 mlog_write_ulint(seg_inode + FSEG_NOT_FULL_N_USED, not_full_n_used,
3559 MLOG_4BYTES, mtr);
3560 if (xdes_is_full(descr, mtr)) {
3561 /* We move the extent from the NOT_FULL list to the
3562 FULL list */
3563 flst_remove(seg_inode + FSEG_NOT_FULL,
3564 descr + XDES_FLST_NODE, mtr);
3565 flst_add_last(seg_inode + FSEG_FULL,
3566 descr + XDES_FLST_NODE, mtr);
3567
3568 mlog_write_ulint(seg_inode + FSEG_NOT_FULL_N_USED,
3569 not_full_n_used - FSP_EXTENT_SIZE,
3570 MLOG_4BYTES, mtr);
3571 }
3572 }
3573
3574 /** Frees a single page of a segment.
3575 @param[in] seg_inode segment inode
3576 @param[in] page_id page id
3577 @param[in] page_size page size
3578 @param[in] ahi whether we may need to drop the adaptive
3579 hash index
3580 @param[in,out] mtr mini-transaction */
3581 static
3582 void
fseg_free_page_low(fseg_inode_t * seg_inode,const page_id_t & page_id,const page_size_t & page_size,bool ahi,mtr_t * mtr)3583 fseg_free_page_low(
3584 fseg_inode_t* seg_inode,
3585 const page_id_t& page_id,
3586 const page_size_t& page_size,
3587 bool ahi,
3588 mtr_t* mtr)
3589 {
3590 xdes_t* descr;
3591 ulint not_full_n_used;
3592 ulint state;
3593 ib_id_t descr_id;
3594 ib_id_t seg_id;
3595 ulint i;
3596
3597 ut_ad(seg_inode != NULL);
3598 ut_ad(mtr != NULL);
3599 ut_ad(mach_read_from_4(seg_inode + FSEG_MAGIC_N)
3600 == FSEG_MAGIC_N_VALUE);
3601 ut_ad(!((page_offset(seg_inode) - FSEG_ARR_OFFSET) % FSEG_INODE_SIZE));
3602 ut_d(fsp_space_modify_check(page_id.space(), mtr));
3603
3604 /* Drop search system page hash index if the page is found in
3605 the pool and is hashed */
3606
3607 if (ahi) {
3608 btr_search_drop_page_hash_when_freed(page_id, page_size);
3609 }
3610
3611 descr = xdes_get_descriptor(page_id.space(), page_id.page_no(),
3612 page_size, mtr);
3613
3614 if (xdes_mtr_get_bit(descr, XDES_FREE_BIT,
3615 page_id.page_no() % FSP_EXTENT_SIZE, mtr)) {
3616 fputs("InnoDB: Dump of the tablespace extent descriptor: ",
3617 stderr);
3618 ut_print_buf(stderr, descr, 40);
3619 ib::error() << "InnoDB is trying to free page " << page_id
3620 << " though it is already marked as free in the"
3621 " tablespace! The tablespace free space info is"
3622 " corrupt. You may need to dump your tables and"
3623 " recreate the whole database!";
3624 crash:
3625 ib::fatal() << FORCE_RECOVERY_MSG;
3626 }
3627
3628 state = xdes_get_state(descr, mtr);
3629
3630 if (state != XDES_FSEG) {
3631 /* The page is in the fragment pages of the segment */
3632
3633 for (i = 0;; i++) {
3634 if (fseg_get_nth_frag_page_no(seg_inode, i, mtr)
3635 == page_id.page_no()) {
3636
3637 fseg_set_nth_frag_page_no(seg_inode, i,
3638 FIL_NULL, mtr);
3639 break;
3640 }
3641 }
3642
3643 fsp_free_page(page_id, page_size, mtr);
3644
3645 return;
3646 }
3647
3648 /* If we get here, the page is in some extent of the segment */
3649
3650 descr_id = mach_read_from_8(descr + XDES_ID);
3651 seg_id = mach_read_from_8(seg_inode + FSEG_ID);
3652
3653 if (UNIV_UNLIKELY(descr_id != seg_id)) {
3654 fputs("InnoDB: Dump of the tablespace extent descriptor: ",
3655 stderr);
3656 ut_print_buf(stderr, descr, 40);
3657 fputs("\nInnoDB: Dump of the segment inode: ", stderr);
3658 ut_print_buf(stderr, seg_inode, 40);
3659 putc('\n', stderr);
3660
3661 ib::error() << "InnoDB is trying to free page " << page_id
3662 << ", which does not belong to segment " << descr_id
3663 << " but belongs to segment " << seg_id << ".";
3664 goto crash;
3665 }
3666
3667 not_full_n_used = mtr_read_ulint(seg_inode + FSEG_NOT_FULL_N_USED,
3668 MLOG_4BYTES, mtr);
3669 if (xdes_is_full(descr, mtr)) {
3670 /* The fragment is full: move it to another list */
3671 flst_remove(seg_inode + FSEG_FULL,
3672 descr + XDES_FLST_NODE, mtr);
3673 flst_add_last(seg_inode + FSEG_NOT_FULL,
3674 descr + XDES_FLST_NODE, mtr);
3675 mlog_write_ulint(seg_inode + FSEG_NOT_FULL_N_USED,
3676 not_full_n_used + FSP_EXTENT_SIZE - 1,
3677 MLOG_4BYTES, mtr);
3678 } else {
3679 ut_a(not_full_n_used > 0);
3680 mlog_write_ulint(seg_inode + FSEG_NOT_FULL_N_USED,
3681 not_full_n_used - 1, MLOG_4BYTES, mtr);
3682 }
3683
3684 const ulint bit = page_id.page_no() % FSP_EXTENT_SIZE;
3685
3686 xdes_set_bit(descr, XDES_FREE_BIT, bit, TRUE, mtr);
3687 xdes_set_bit(descr, XDES_CLEAN_BIT, bit, TRUE, mtr);
3688
3689 if (xdes_is_free(descr, mtr)) {
3690 /* The extent has become free: free it to space */
3691 flst_remove(seg_inode + FSEG_NOT_FULL,
3692 descr + XDES_FLST_NODE, mtr);
3693 fsp_free_extent(page_id, page_size, mtr);
3694 }
3695 }
3696
3697 /**********************************************************************//**
3698 Frees a single page of a segment. */
3699 void
fseg_free_page(fseg_header_t * seg_header,ulint space_id,ulint page,bool ahi,mtr_t * mtr)3700 fseg_free_page(
3701 /*===========*/
3702 fseg_header_t* seg_header, /*!< in: segment header */
3703 ulint space_id,/*!< in: space id */
3704 ulint page, /*!< in: page offset */
3705 bool ahi, /*!< in: whether we may need to drop
3706 the adaptive hash index */
3707 mtr_t* mtr) /*!< in/out: mini-transaction */
3708 {
3709 fseg_inode_t* seg_inode;
3710 buf_block_t* iblock;
3711 const fil_space_t* space = mtr_x_lock_space(space_id, mtr);
3712 const page_size_t page_size(space->flags);
3713
3714 seg_inode = fseg_inode_get(seg_header, space_id, page_size, mtr,
3715 &iblock);
3716 fil_block_check_type(iblock, FIL_PAGE_INODE, mtr);
3717
3718 const page_id_t page_id(space_id, page);
3719
3720 fseg_free_page_low(seg_inode, page_id, page_size, ahi, mtr);
3721
3722 ut_d(buf_page_set_file_page_was_freed(page_id));
3723 }
3724
3725 /**********************************************************************//**
3726 Checks if a single page of a segment is free.
3727 @return true if free */
3728 bool
fseg_page_is_free(fseg_header_t * seg_header,ulint space_id,ulint page)3729 fseg_page_is_free(
3730 /*==============*/
3731 fseg_header_t* seg_header, /*!< in: segment header */
3732 ulint space_id, /*!< in: space id */
3733 ulint page) /*!< in: page offset */
3734 {
3735 mtr_t mtr;
3736 ibool is_free;
3737 xdes_t* descr;
3738 fseg_inode_t* seg_inode;
3739
3740 mtr_start(&mtr);
3741 const fil_space_t* space = mtr_x_lock_space(space_id, &mtr);
3742 const page_size_t page_size(space->flags);
3743
3744 seg_inode = fseg_inode_get(seg_header, space_id, page_size, &mtr);
3745
3746 ut_a(seg_inode);
3747 ut_ad(mach_read_from_4(seg_inode + FSEG_MAGIC_N)
3748 == FSEG_MAGIC_N_VALUE);
3749 ut_ad(!((page_offset(seg_inode) - FSEG_ARR_OFFSET) % FSEG_INODE_SIZE));
3750
3751 descr = xdes_get_descriptor(space_id, page, page_size, &mtr);
3752 ut_a(descr);
3753
3754 is_free = xdes_mtr_get_bit(
3755 descr, XDES_FREE_BIT, page % FSP_EXTENT_SIZE, &mtr);
3756
3757 mtr_commit(&mtr);
3758
3759 return(is_free);
3760 }
3761
3762 /**********************************************************************//**
3763 Frees an extent of a segment to the space free list. */
3764 static
3765 void
fseg_free_extent(fseg_inode_t * seg_inode,ulint space,const page_size_t & page_size,ulint page,bool ahi,mtr_t * mtr)3766 fseg_free_extent(
3767 /*=============*/
3768 fseg_inode_t* seg_inode, /*!< in: segment inode */
3769 ulint space, /*!< in: space id */
3770 const page_size_t& page_size,
3771 ulint page, /*!< in: a page in the extent */
3772 bool ahi, /*!< in: whether we may need to drop
3773 the adaptive hash index */
3774 mtr_t* mtr) /*!< in/out: mini-transaction */
3775 {
3776 ulint first_page_in_extent;
3777 xdes_t* descr;
3778 ulint not_full_n_used;
3779 ulint descr_n_used;
3780 ulint i;
3781
3782 ut_ad(seg_inode != NULL);
3783 ut_ad(mtr != NULL);
3784
3785 descr = xdes_get_descriptor(space, page, page_size, mtr);
3786
3787 ut_a(xdes_get_state(descr, mtr) == XDES_FSEG);
3788 ut_a(!memcmp(descr + XDES_ID, seg_inode + FSEG_ID, 8));
3789 ut_ad(mach_read_from_4(seg_inode + FSEG_MAGIC_N)
3790 == FSEG_MAGIC_N_VALUE);
3791 ut_d(fsp_space_modify_check(space, mtr));
3792
3793 first_page_in_extent = page - (page % FSP_EXTENT_SIZE);
3794
3795 if (ahi) {
3796 for (i = 0; i < FSP_EXTENT_SIZE; i++) {
3797 if (!xdes_mtr_get_bit(descr, XDES_FREE_BIT, i, mtr)) {
3798
3799 /* Drop search system page hash index
3800 if the page is found in the pool and
3801 is hashed */
3802
3803 btr_search_drop_page_hash_when_freed(
3804 page_id_t(space,
3805 first_page_in_extent + i),
3806 page_size);
3807 }
3808 }
3809 }
3810
3811 if (xdes_is_full(descr, mtr)) {
3812 flst_remove(seg_inode + FSEG_FULL,
3813 descr + XDES_FLST_NODE, mtr);
3814 } else if (xdes_is_free(descr, mtr)) {
3815 flst_remove(seg_inode + FSEG_FREE,
3816 descr + XDES_FLST_NODE, mtr);
3817 } else {
3818 flst_remove(seg_inode + FSEG_NOT_FULL,
3819 descr + XDES_FLST_NODE, mtr);
3820
3821 not_full_n_used = mtr_read_ulint(
3822 seg_inode + FSEG_NOT_FULL_N_USED, MLOG_4BYTES, mtr);
3823
3824 descr_n_used = xdes_get_n_used(descr, mtr);
3825 ut_a(not_full_n_used >= descr_n_used);
3826 mlog_write_ulint(seg_inode + FSEG_NOT_FULL_N_USED,
3827 not_full_n_used - descr_n_used,
3828 MLOG_4BYTES, mtr);
3829 }
3830
3831 fsp_free_extent(page_id_t(space, page), page_size, mtr);
3832
3833 #ifdef UNIV_DEBUG
3834 for (i = 0; i < FSP_EXTENT_SIZE; i++) {
3835
3836 buf_page_set_file_page_was_freed(
3837 page_id_t(space, first_page_in_extent + i));
3838 }
3839 #endif /* UNIV_DEBUG */
3840 }
3841
3842 /**********************************************************************//**
3843 Frees part of a segment. This function can be used to free a segment by
3844 repeatedly calling this function in different mini-transactions. Doing
3845 the freeing in a single mini-transaction might result in too big a
3846 mini-transaction.
3847 @return TRUE if freeing completed */
3848 ibool
fseg_free_step(fseg_header_t * header,bool ahi,mtr_t * mtr)3849 fseg_free_step(
3850 /*===========*/
3851 fseg_header_t* header, /*!< in, own: segment header; NOTE: if the header
3852 resides on the first page of the frag list
3853 of the segment, this pointer becomes obsolete
3854 after the last freeing step */
3855 bool ahi, /*!< in: whether we may need to drop
3856 the adaptive hash index */
3857 mtr_t* mtr) /*!< in/out: mini-transaction */
3858 {
3859 ulint n;
3860 ulint page;
3861 xdes_t* descr;
3862 fseg_inode_t* inode;
3863 ulint space_id;
3864 ulint header_page;
3865
3866 DBUG_ENTER("fseg_free_step");
3867
3868 space_id = page_get_space_id(page_align(header));
3869 header_page = page_get_page_no(page_align(header));
3870
3871 const fil_space_t* space = mtr_x_lock_space(space_id, mtr);
3872 const page_size_t page_size(space->flags);
3873
3874 descr = xdes_get_descriptor(space_id, header_page, page_size, mtr);
3875
3876 /* Check that the header resides on a page which has not been
3877 freed yet */
3878
3879 ut_a(xdes_mtr_get_bit(descr, XDES_FREE_BIT,
3880 header_page % FSP_EXTENT_SIZE, mtr) == FALSE);
3881 buf_block_t* iblock;
3882
3883 inode = fseg_inode_try_get(header, space_id, page_size, mtr, &iblock);
3884
3885 if (inode == NULL) {
3886 ib::info() << "Double free of inode from "
3887 << page_id_t(space_id, header_page);
3888 DBUG_RETURN(TRUE);
3889 }
3890
3891 fil_block_check_type(iblock, FIL_PAGE_INODE, mtr);
3892 descr = fseg_get_first_extent(inode, space_id, page_size, mtr);
3893
3894 if (descr != NULL) {
3895 /* Free the extent held by the segment */
3896 page = xdes_get_offset(descr);
3897
3898 fseg_free_extent(inode, space_id, page_size, page, ahi, mtr);
3899
3900 DBUG_RETURN(FALSE);
3901 }
3902
3903 /* Free a frag page */
3904 n = fseg_find_last_used_frag_page_slot(inode, mtr);
3905
3906 if (n == ULINT_UNDEFINED) {
3907 /* Freeing completed: free the segment inode */
3908 fsp_free_seg_inode(space_id, page_size, inode, mtr);
3909
3910 DBUG_RETURN(TRUE);
3911 }
3912
3913 fseg_free_page_low(
3914 inode,
3915 page_id_t(space_id, fseg_get_nth_frag_page_no(inode, n, mtr)),
3916 page_size, ahi, mtr);
3917
3918 n = fseg_find_last_used_frag_page_slot(inode, mtr);
3919
3920 if (n == ULINT_UNDEFINED) {
3921 /* Freeing completed: free the segment inode */
3922 fsp_free_seg_inode(space_id, page_size, inode, mtr);
3923
3924 DBUG_RETURN(TRUE);
3925 }
3926
3927 DBUG_RETURN(FALSE);
3928 }
3929
3930 /**********************************************************************//**
3931 Frees part of a segment. Differs from fseg_free_step because this function
3932 leaves the header page unfreed.
3933 @return TRUE if freeing completed, except the header page */
3934 ibool
fseg_free_step_not_header(fseg_header_t * header,bool ahi,mtr_t * mtr)3935 fseg_free_step_not_header(
3936 /*======================*/
3937 fseg_header_t* header, /*!< in: segment header which must reside on
3938 the first fragment page of the segment */
3939 bool ahi, /*!< in: whether we may need to drop
3940 the adaptive hash index */
3941 mtr_t* mtr) /*!< in/out: mini-transaction */
3942 {
3943 ulint n;
3944 ulint page;
3945 xdes_t* descr;
3946 fseg_inode_t* inode;
3947 ulint space_id;
3948 ulint page_no;
3949
3950 space_id = page_get_space_id(page_align(header));
3951 ut_ad(mtr->is_named_space(space_id));
3952
3953 const fil_space_t* space = mtr_x_lock_space(space_id, mtr);
3954 const page_size_t page_size(space->flags);
3955 buf_block_t* iblock;
3956
3957 inode = fseg_inode_get(header, space_id, page_size, mtr, &iblock);
3958 fil_block_check_type(iblock, FIL_PAGE_INODE, mtr);
3959
3960 descr = fseg_get_first_extent(inode, space_id, page_size, mtr);
3961
3962 if (descr != NULL) {
3963 /* Free the extent held by the segment */
3964 page = xdes_get_offset(descr);
3965
3966 fseg_free_extent(inode, space_id, page_size, page, ahi, mtr);
3967
3968 return(FALSE);
3969 }
3970
3971 /* Free a frag page */
3972
3973 n = fseg_find_last_used_frag_page_slot(inode, mtr);
3974
3975 if (n == ULINT_UNDEFINED) {
3976 ut_error;
3977 }
3978
3979 page_no = fseg_get_nth_frag_page_no(inode, n, mtr);
3980
3981 if (page_no == page_get_page_no(page_align(header))) {
3982
3983 return(TRUE);
3984 }
3985
3986 fseg_free_page_low(inode, page_id_t(space_id, page_no), page_size, ahi,
3987 mtr);
3988
3989 return(FALSE);
3990 }
3991
3992 /** Returns the first extent descriptor for a segment.
3993 We think of the extent lists of the segment catenated in the order
3994 FSEG_FULL -> FSEG_NOT_FULL -> FSEG_FREE.
3995 @param[in] inode segment inode
3996 @param[in] space_id space id
3997 @param[in] page_size page size
3998 @param[in,out] mtr mini-transaction
3999 @return the first extent descriptor, or NULL if none */
4000 static
4001 xdes_t*
fseg_get_first_extent(fseg_inode_t * inode,ulint space_id,const page_size_t & page_size,mtr_t * mtr)4002 fseg_get_first_extent(
4003 fseg_inode_t* inode,
4004 ulint space_id,
4005 const page_size_t& page_size,
4006 mtr_t* mtr)
4007 {
4008 fil_addr_t first;
4009 xdes_t* descr;
4010
4011 ut_ad(inode && mtr);
4012
4013 ut_ad(space_id == page_get_space_id(page_align(inode)));
4014 ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE);
4015
4016 first = fil_addr_null;
4017
4018 if (flst_get_len(inode + FSEG_FULL) > 0) {
4019
4020 first = flst_get_first(inode + FSEG_FULL, mtr);
4021
4022 } else if (flst_get_len(inode + FSEG_NOT_FULL) > 0) {
4023
4024 first = flst_get_first(inode + FSEG_NOT_FULL, mtr);
4025
4026 } else if (flst_get_len(inode + FSEG_FREE) > 0) {
4027
4028 first = flst_get_first(inode + FSEG_FREE, mtr);
4029 }
4030
4031 if (first.page == FIL_NULL) {
4032
4033 return(NULL);
4034 }
4035 descr = xdes_lst_get_descriptor(space_id, page_size, first, mtr);
4036
4037 return(descr);
4038 }
4039
4040 #ifdef UNIV_DEBUG
4041 /*******************************************************************//**
4042 Validates a segment.
4043 @return TRUE if ok */
4044 static
4045 ibool
fseg_validate_low(fseg_inode_t * inode,mtr_t * mtr2)4046 fseg_validate_low(
4047 /*==============*/
4048 fseg_inode_t* inode, /*!< in: segment inode */
4049 mtr_t* mtr2) /*!< in/out: mini-transaction */
4050 {
4051 ulint space_id;
4052 ib_id_t seg_id;
4053 mtr_t mtr;
4054 xdes_t* descr;
4055 fil_addr_t node_addr;
4056 ulint n_used = 0;
4057 ulint n_used2 = 0;
4058
4059 ut_ad(mtr_memo_contains_page(mtr2, inode, MTR_MEMO_PAGE_SX_FIX));
4060 ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE);
4061
4062 space_id = page_get_space_id(page_align(inode));
4063
4064 seg_id = mach_read_from_8(inode + FSEG_ID);
4065 n_used = mtr_read_ulint(inode + FSEG_NOT_FULL_N_USED,
4066 MLOG_4BYTES, mtr2);
4067 flst_validate(inode + FSEG_FREE, mtr2);
4068 flst_validate(inode + FSEG_NOT_FULL, mtr2);
4069 flst_validate(inode + FSEG_FULL, mtr2);
4070
4071 /* Validate FSEG_FREE list */
4072 node_addr = flst_get_first(inode + FSEG_FREE, mtr2);
4073
4074 while (!fil_addr_is_null(node_addr)) {
4075 mtr_start(&mtr);
4076 const fil_space_t* space = mtr_x_lock_space(
4077 space_id, &mtr);
4078
4079 const page_size_t page_size(space->flags);
4080
4081 descr = xdes_lst_get_descriptor(space_id, page_size,
4082 node_addr, &mtr);
4083
4084 ut_a(xdes_get_n_used(descr, &mtr) == 0);
4085 ut_a(xdes_get_state(descr, &mtr) == XDES_FSEG);
4086 ut_a(mach_read_from_8(descr + XDES_ID) == seg_id);
4087
4088 node_addr = flst_get_next_addr(descr + XDES_FLST_NODE, &mtr);
4089 mtr_commit(&mtr);
4090 }
4091
4092 /* Validate FSEG_NOT_FULL list */
4093
4094 node_addr = flst_get_first(inode + FSEG_NOT_FULL, mtr2);
4095
4096 while (!fil_addr_is_null(node_addr)) {
4097 mtr_start(&mtr);
4098 const fil_space_t* space = mtr_x_lock_space(
4099 space_id, &mtr);
4100 const page_size_t page_size(space->flags);
4101
4102 descr = xdes_lst_get_descriptor(space_id, page_size,
4103 node_addr, &mtr);
4104
4105 ut_a(xdes_get_n_used(descr, &mtr) > 0);
4106 ut_a(xdes_get_n_used(descr, &mtr) < FSP_EXTENT_SIZE);
4107 ut_a(xdes_get_state(descr, &mtr) == XDES_FSEG);
4108 ut_a(mach_read_from_8(descr + XDES_ID) == seg_id);
4109
4110 n_used2 += xdes_get_n_used(descr, &mtr);
4111
4112 node_addr = flst_get_next_addr(descr + XDES_FLST_NODE, &mtr);
4113 mtr_commit(&mtr);
4114 }
4115
4116 /* Validate FSEG_FULL list */
4117
4118 node_addr = flst_get_first(inode + FSEG_FULL, mtr2);
4119
4120 while (!fil_addr_is_null(node_addr)) {
4121 mtr_start(&mtr);
4122 const fil_space_t* space = mtr_x_lock_space(
4123 space_id, &mtr);
4124 const page_size_t page_size(space->flags);
4125
4126 descr = xdes_lst_get_descriptor(space_id, page_size,
4127 node_addr, &mtr);
4128
4129 ut_a(xdes_get_n_used(descr, &mtr) == FSP_EXTENT_SIZE);
4130 ut_a(xdes_get_state(descr, &mtr) == XDES_FSEG);
4131 ut_a(mach_read_from_8(descr + XDES_ID) == seg_id);
4132
4133 node_addr = flst_get_next_addr(descr + XDES_FLST_NODE, &mtr);
4134 mtr_commit(&mtr);
4135 }
4136
4137 ut_a(n_used == n_used2);
4138
4139 return(TRUE);
4140 }
4141
4142 /*******************************************************************//**
4143 Validates a segment.
4144 @return TRUE if ok */
4145 ibool
fseg_validate(fseg_header_t * header,mtr_t * mtr)4146 fseg_validate(
4147 /*==========*/
4148 fseg_header_t* header, /*!< in: segment header */
4149 mtr_t* mtr) /*!< in/out: mini-transaction */
4150 {
4151 fseg_inode_t* inode;
4152 ibool ret;
4153 ulint space_id;
4154
4155 space_id = page_get_space_id(page_align(header));
4156
4157 const fil_space_t* space = mtr_x_lock_space(space_id, mtr);
4158 const page_size_t page_size(space->flags);
4159
4160 inode = fseg_inode_get(header, space_id, page_size, mtr);
4161
4162 ret = fseg_validate_low(inode, mtr);
4163
4164 return(ret);
4165 }
4166 #endif /* UNIV_DEBUG */
4167
4168 #ifdef UNIV_BTR_PRINT
4169 /*******************************************************************//**
4170 Writes info of a segment. */
4171 static
4172 void
fseg_print_low(fseg_inode_t * inode,mtr_t * mtr)4173 fseg_print_low(
4174 /*===========*/
4175 fseg_inode_t* inode, /*!< in: segment inode */
4176 mtr_t* mtr) /*!< in/out: mini-transaction */
4177 {
4178 ulint space;
4179 ulint n_used;
4180 ulint n_frag;
4181 ulint n_free;
4182 ulint n_not_full;
4183 ulint n_full;
4184 ulint reserved;
4185 ulint used;
4186 ulint page_no;
4187 ib_id_t seg_id;
4188
4189 ut_ad(mtr_memo_contains_page(mtr, inode, MTR_MEMO_PAGE_SX_FIX));
4190 space = page_get_space_id(page_align(inode));
4191 page_no = page_get_page_no(page_align(inode));
4192
4193 reserved = fseg_n_reserved_pages_low(inode, &used, mtr);
4194
4195 seg_id = mach_read_from_8(inode + FSEG_ID);
4196
4197 n_used = mtr_read_ulint(inode + FSEG_NOT_FULL_N_USED,
4198 MLOG_4BYTES, mtr);
4199 n_frag = fseg_get_n_frag_pages(inode, mtr);
4200 n_free = flst_get_len(inode + FSEG_FREE);
4201 n_not_full = flst_get_len(inode + FSEG_NOT_FULL);
4202 n_full = flst_get_len(inode + FSEG_FULL);
4203
4204 ib::info() << "SEGMENT id " << seg_id
4205 << " space " << space << ";"
4206 << " page " << page_no << ";"
4207 << " res " << reserved << " used " << used << ";"
4208 << " full ext " << n_full << ";"
4209 << " fragm pages " << n_frag << ";"
4210 << " free extents " << n_free << ";"
4211 << " not full extents " << n_not_full << ": pages " << n_used;
4212
4213 ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE);
4214 }
4215
4216 /*******************************************************************//**
4217 Writes info of a segment. */
4218 void
fseg_print(fseg_header_t * header,mtr_t * mtr)4219 fseg_print(
4220 /*=======*/
4221 fseg_header_t* header, /*!< in: segment header */
4222 mtr_t* mtr) /*!< in/out: mini-transaction */
4223 {
4224 fseg_inode_t* inode;
4225 ulint space_id;
4226
4227 space_id = page_get_space_id(page_align(header));
4228 const fil_space_t* space = mtr_x_lock_space(space_id, mtr);
4229 const page_size_t page_size(space->flags);
4230
4231 inode = fseg_inode_get(header, space_id, page_size, mtr);
4232
4233 fseg_print_low(inode, mtr);
4234 }
4235 #endif /* UNIV_BTR_PRINT */
4236 #endif /* !UNIV_HOTBACKUP */
4237
4238 #ifdef UNIV_DEBUG
4239 /** Print the file segment header to the given output stream.
4240 @param[in] out the output stream into which the object is printed.
4241 @retval the output stream into which the object was printed. */
4242 std::ostream&
to_stream(std::ostream & out) const4243 fseg_header::to_stream(std::ostream& out) const
4244 {
4245 const ulint space = mtr_read_ulint(m_header + FSEG_HDR_SPACE,
4246 MLOG_4BYTES, m_mtr);
4247
4248 const ulint page_no = mtr_read_ulint(m_header + FSEG_HDR_PAGE_NO,
4249 MLOG_4BYTES, m_mtr);
4250
4251 const ulint offset = mtr_read_ulint(m_header + FSEG_HDR_OFFSET,
4252 MLOG_2BYTES, m_mtr);
4253
4254 out << "[fseg_header_t: space=" << space << ", page="
4255 << page_no << ", offset=" << offset << "]";
4256
4257 return(out);
4258 }
4259 #endif /* UNIV_DEBUG */
4260