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