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