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