1/*****************************************************************************
2
3Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved.
4Copyright (c) 2017, 2018, MariaDB Corporation.
5
6This program is free software; you can redistribute it and/or modify it under
7the terms of the GNU General Public License as published by the Free Software
8Foundation; version 2 of the License.
9
10This program is distributed in the hope that it will be useful, but WITHOUT
11ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License along with
15this program; if not, write to the Free Software Foundation, Inc.,
1651 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
17
18*****************************************************************************/
19
20/******************************************************************//**
21@file fsp/fsp0fsp.cc
22File space management
23
24Created 11/29/1995 Heikki Tuuri
25***********************************************************************/
26
27#include "ha_prototypes.h"
28
29#include "fsp0fsp.h"
30#include "buf0buf.h"
31#include "fil0fil.h"
32#include "fil0crypt.h"
33#include "mtr0log.h"
34#include "ut0byte.h"
35#include "page0page.h"
36#include "fut0fut.h"
37#include "srv0srv.h"
38#include "srv0start.h"
39#include "ibuf0ibuf.h"
40#include "btr0btr.h"
41#include "btr0sea.h"
42#include "dict0boot.h"
43#include "log0log.h"
44#include "fsp0sysspace.h"
45#include "dict0mem.h"
46#include "fsp0types.h"
47
48// JAN: MySQL 5.7 Encryption
49// #include <my_aes.h>
50
51typedef ulint page_no_t;
52
53/** Return an extent to the free list of a space.
54@param[in,out] space tablespace
55@param[in] offset page number in the extent
56@param[in] page_size page size
57@param[in,out] mtr mini-transaction */
58MY_ATTRIBUTE((nonnull))
59static
60void
61fsp_free_extent(
62 fil_space_t* space,
63 page_no_t offset,
64 const page_size_t& page_size,
65 mtr_t* mtr);
66
67/********************************************************************//**
68Marks a page used. The page must reside within the extents of the given
69segment. */
70static MY_ATTRIBUTE((nonnull))
71void
72fseg_mark_page_used(
73/*================*/
74 fseg_inode_t* seg_inode,/*!< in: segment inode */
75 page_no_t page, /*!< in: page offset */
76 xdes_t* descr, /*!< in: extent descriptor */
77 mtr_t* mtr); /*!< in/out: mini-transaction */
78
79/** Returns the first extent descriptor for a segment.
80We think of the extent lists of the segment catenated in the order
81FSEG_FULL -> FSEG_NOT_FULL -> FSEG_FREE.
82@param[in] inode segment inode
83@param[in] space tablespace
84@param[in] page_size page size
85@param[in,out] mtr mini-transaction
86@return the first extent descriptor, or NULL if none */
87MY_ATTRIBUTE((nonnull, warn_unused_result))
88static
89xdes_t*
90fseg_get_first_extent(
91 fseg_inode_t* inode,
92 const fil_space_t* space,
93 const page_size_t& page_size,
94 mtr_t* mtr);
95
96/** Put new extents to the free list if there are free extents above the free
97limit. If an extent happens to contain an extent descriptor page, the extent
98is put to the FSP_FREE_FRAG list with the page marked as used.
99@param[in] init_space true if this is a single-table tablespace
100and we are only initializing the first extent and the first bitmap pages;
101then we will not allocate more extents
102@param[in,out] space tablespace
103@param[in,out] header tablespace header
104@param[in,out] mtr mini-transaction */
105static ATTRIBUTE_COLD
106void
107fsp_fill_free_list(
108 bool init_space,
109 fil_space_t* space,
110 fsp_header_t* header,
111 mtr_t* mtr);
112
113/** Allocates a single free page from a segment.
114This function implements the intelligent allocation strategy which tries
115to minimize file space fragmentation.
116@param[in,out] space tablespace
117@param[in] page_size page size
118@param[in,out] seg_inode segment inode
119@param[in] hint hint of which page would be desirable
120@param[in] direction if the new page is needed because of
121an index page split, and records are inserted there in order, into which
122direction they go alphabetically: FSP_DOWN, FSP_UP, FSP_NO_DIR
123@param[in] rw_latch RW_SX_LATCH, RW_X_LATCH
124@param[in,out] mtr mini-transaction
125@param[in,out] init_mtr mtr or another mini-transaction in
126which the page should be initialized. If init_mtr != mtr, but the page is
127already latched in mtr, do not initialize the page
128@param[in] has_done_reservation TRUE if the space has already been
129reserved, in this case we will never return NULL
130@retval NULL if no page could be allocated
131@retval block rw_lock_x_lock_count(&block->lock) == 1 if allocation succeeded
132(init_mtr == mtr, or the page was not previously freed in mtr)
133@retval block (not allocated or initialized) otherwise */
134static
135buf_block_t*
136fseg_alloc_free_page_low(
137 fil_space_t* space,
138 const page_size_t& page_size,
139 fseg_inode_t* seg_inode,
140 ulint hint,
141 byte direction,
142 rw_lock_type_t rw_latch,
143 mtr_t* mtr,
144 mtr_t* init_mtr
145#ifdef UNIV_DEBUG
146 , ibool has_done_reservation
147#endif /* UNIV_DEBUG */
148)
149 MY_ATTRIBUTE((warn_unused_result));
150
151/** Gets a pointer to the space header and x-locks its page.
152@param[in] space tablespace
153@param[in] page_size page size
154@param[in,out] mtr mini-transaction
155@return pointer to the space header, page x-locked */
156UNIV_INLINE
157fsp_header_t*
158fsp_get_space_header(
159 const fil_space_t* space,
160 const page_size_t& page_size,
161 mtr_t* mtr)
162{
163 buf_block_t* block;
164 fsp_header_t* header;
165
166 ut_ad(space->purpose != FIL_TYPE_LOG);
167 ut_ad(!FSP_FLAGS_GET_ZIP_SSIZE(space->flags)
168 == !page_size.is_compressed());
169
170 block = buf_page_get(page_id_t(space->id, 0), page_size,
171 RW_SX_LATCH, mtr);
172 header = FSP_HEADER_OFFSET + buf_block_get_frame(block);
173 buf_block_dbg_add_level(block, SYNC_FSP_PAGE);
174
175 ut_ad(space->id == mach_read_from_4(FSP_SPACE_ID + header));
176 return(header);
177}
178
179/**********************************************************************//**
180Gets a descriptor bit of a page.
181@return TRUE if free */
182UNIV_INLINE
183ibool
184xdes_mtr_get_bit(
185/*=============*/
186 const xdes_t* descr, /*!< in: descriptor */
187 ulint bit, /*!< in: XDES_FREE_BIT or XDES_CLEAN_BIT */
188 ulint offset, /*!< in: page offset within extent:
189 0 ... FSP_EXTENT_SIZE - 1 */
190 mtr_t* mtr) /*!< in: mini-transaction */
191{
192 ut_ad(mtr->is_active());
193 ut_ad(mtr_memo_contains_page(mtr, descr, MTR_MEMO_PAGE_SX_FIX));
194
195 return(xdes_get_bit(descr, bit, offset));
196}
197
198/**********************************************************************//**
199Sets a descriptor bit of a page. */
200UNIV_INLINE
201void
202xdes_set_bit(
203/*=========*/
204 xdes_t* descr, /*!< in: descriptor */
205 ulint bit, /*!< in: XDES_FREE_BIT or XDES_CLEAN_BIT */
206 ulint offset, /*!< in: page offset within extent:
207 0 ... FSP_EXTENT_SIZE - 1 */
208 ibool val, /*!< in: bit value */
209 mtr_t* mtr) /*!< in/out: mini-transaction */
210{
211 ulint index;
212 ulint byte_index;
213 ulint bit_index;
214 ulint descr_byte;
215
216 ut_ad(mtr_memo_contains_page(mtr, descr, MTR_MEMO_PAGE_SX_FIX));
217 ut_ad((bit == XDES_FREE_BIT) || (bit == XDES_CLEAN_BIT));
218 ut_ad(offset < FSP_EXTENT_SIZE);
219
220 index = bit + XDES_BITS_PER_PAGE * offset;
221
222 byte_index = index / 8;
223 bit_index = index % 8;
224
225 descr_byte = mach_read_from_1(descr + XDES_BITMAP + byte_index);
226 descr_byte = ut_bit_set_nth(descr_byte, bit_index, val);
227
228 mlog_write_ulint(descr + XDES_BITMAP + byte_index, descr_byte,
229 MLOG_1BYTE, mtr);
230}
231
232/**********************************************************************//**
233Looks for a descriptor bit having the desired value. Starts from hint
234and scans upward; at the end of the extent the search is wrapped to
235the start of the extent.
236@return bit index of the bit, ULINT_UNDEFINED if not found */
237UNIV_INLINE
238ulint
239xdes_find_bit(
240/*==========*/
241 xdes_t* descr, /*!< in: descriptor */
242 ulint bit, /*!< in: XDES_FREE_BIT or XDES_CLEAN_BIT */
243 ibool val, /*!< in: desired bit value */
244 ulint hint, /*!< in: hint of which bit position would
245 be desirable */
246 mtr_t* mtr) /*!< in/out: mini-transaction */
247{
248 ulint i;
249
250 ut_ad(descr && mtr);
251 ut_ad(val <= TRUE);
252 ut_ad(hint < FSP_EXTENT_SIZE);
253 ut_ad(mtr_memo_contains_page(mtr, descr, MTR_MEMO_PAGE_SX_FIX));
254 for (i = hint; i < FSP_EXTENT_SIZE; i++) {
255 if (val == xdes_mtr_get_bit(descr, bit, i, mtr)) {
256
257 return(i);
258 }
259 }
260
261 for (i = 0; i < hint; i++) {
262 if (val == xdes_mtr_get_bit(descr, bit, i, mtr)) {
263
264 return(i);
265 }
266 }
267
268 return(ULINT_UNDEFINED);
269}
270
271/**********************************************************************//**
272Returns the number of used pages in a descriptor.
273@return number of pages used */
274UNIV_INLINE
275ulint
276xdes_get_n_used(
277/*============*/
278 const xdes_t* descr, /*!< in: descriptor */
279 mtr_t* mtr) /*!< in/out: mini-transaction */
280{
281 ulint count = 0;
282
283 ut_ad(descr && mtr);
284 ut_ad(mtr_memo_contains_page(mtr, descr, MTR_MEMO_PAGE_SX_FIX));
285 for (ulint i = 0; i < FSP_EXTENT_SIZE; ++i) {
286 if (FALSE == xdes_mtr_get_bit(descr, XDES_FREE_BIT, i, mtr)) {
287 count++;
288 }
289 }
290
291 return(count);
292}
293
294/**********************************************************************//**
295Returns true if extent contains no used pages.
296@return TRUE if totally free */
297UNIV_INLINE
298ibool
299xdes_is_free(
300/*=========*/
301 const xdes_t* descr, /*!< in: descriptor */
302 mtr_t* mtr) /*!< in/out: mini-transaction */
303{
304 if (0 == xdes_get_n_used(descr, mtr)) {
305
306 return(TRUE);
307 }
308
309 return(FALSE);
310}
311
312/**********************************************************************//**
313Returns true if extent contains no free pages.
314@return TRUE if full */
315UNIV_INLINE
316ibool
317xdes_is_full(
318/*=========*/
319 const xdes_t* descr, /*!< in: descriptor */
320 mtr_t* mtr) /*!< in/out: mini-transaction */
321{
322 if (FSP_EXTENT_SIZE == xdes_get_n_used(descr, mtr)) {
323
324 return(TRUE);
325 }
326
327 return(FALSE);
328}
329
330/**********************************************************************//**
331Sets the state of an xdes. */
332UNIV_INLINE
333void
334xdes_set_state(
335/*===========*/
336 xdes_t* descr, /*!< in/out: descriptor */
337 ulint state, /*!< in: state to set */
338 mtr_t* mtr) /*!< in/out: mini-transaction */
339{
340 ut_ad(descr && mtr);
341 ut_ad(state >= XDES_FREE);
342 ut_ad(state <= XDES_FSEG);
343 ut_ad(mtr_memo_contains_page(mtr, descr, MTR_MEMO_PAGE_SX_FIX));
344
345 mlog_write_ulint(descr + XDES_STATE, state, MLOG_4BYTES, mtr);
346}
347
348/**********************************************************************//**
349Gets the state of an xdes.
350@return state */
351UNIV_INLINE
352ulint
353xdes_get_state(
354/*===========*/
355 const xdes_t* descr, /*!< in: descriptor */
356 mtr_t* mtr) /*!< in/out: mini-transaction */
357{
358 ulint state;
359
360 ut_ad(descr && mtr);
361 ut_ad(mtr_memo_contains_page(mtr, descr, MTR_MEMO_PAGE_SX_FIX));
362
363 state = mach_read_from_4(descr + XDES_STATE);
364 ut_ad(state - 1 < XDES_FSEG);
365 return(state);
366}
367
368/**********************************************************************//**
369Inits an extent descriptor to the free and clean state. */
370UNIV_INLINE
371void
372xdes_init(
373/*======*/
374 xdes_t* descr, /*!< in: descriptor */
375 mtr_t* mtr) /*!< in/out: mini-transaction */
376{
377 ulint i;
378
379 ut_ad(descr && mtr);
380 ut_ad(mtr_memo_contains_page(mtr, descr, MTR_MEMO_PAGE_SX_FIX));
381 ut_ad((XDES_SIZE - XDES_BITMAP) % 4 == 0);
382
383 for (i = XDES_BITMAP; i < XDES_SIZE; i += 4) {
384 mlog_write_ulint(descr + i, 0xFFFFFFFFUL, MLOG_4BYTES, mtr);
385 }
386
387 xdes_set_state(descr, XDES_FREE, mtr);
388}
389
390/** Get pointer to a the extent descriptor of a page.
391@param[in,out] sp_header tablespace header page, x-latched
392@param[in] space tablespace
393@param[in] offset page offset
394@param[in,out] mtr mini-transaction
395@param[in] init_space whether the tablespace is being initialized
396@param[out] desc_block descriptor block, or NULL if it is
397the same as the tablespace header
398@return pointer to the extent descriptor, NULL if the page does not
399exist in the space or if the offset exceeds free limit */
400UNIV_INLINE MY_ATTRIBUTE((warn_unused_result))
401xdes_t*
402xdes_get_descriptor_with_space_hdr(
403 fsp_header_t* sp_header,
404 const fil_space_t* space,
405 page_no_t offset,
406 mtr_t* mtr,
407 bool init_space = false,
408 buf_block_t** desc_block = NULL)
409{
410 ulint limit;
411 ulint size;
412 ulint descr_page_no;
413 page_t* descr_page;
414 ut_ad(mtr_memo_contains(mtr, &space->latch, MTR_MEMO_X_LOCK));
415 ut_ad(mtr_memo_contains_page(mtr, sp_header, MTR_MEMO_PAGE_SX_FIX));
416 ut_ad(page_offset(sp_header) == FSP_HEADER_OFFSET);
417 /* Read free limit and space size */
418 limit = mach_read_from_4(sp_header + FSP_FREE_LIMIT);
419 size = mach_read_from_4(sp_header + FSP_SIZE);
420 ut_ad(limit == space->free_limit
421 || (space->free_limit == 0
422 && (init_space
423 || space->purpose == FIL_TYPE_TEMPORARY
424 || (srv_startup_is_before_trx_rollback_phase
425 && (space->id == TRX_SYS_SPACE
426 || srv_is_undo_tablespace(space->id))))));
427 ut_ad(size == space->size_in_header);
428
429 if ((offset >= size) || (offset >= limit)) {
430 return(NULL);
431 }
432
433 const page_size_t page_size(space->flags);
434
435 descr_page_no = xdes_calc_descriptor_page(page_size, offset);
436
437 buf_block_t* block;
438
439 if (descr_page_no == 0) {
440 /* It is on the space header page */
441
442 descr_page = page_align(sp_header);
443 block = NULL;
444 } else {
445 block = buf_page_get(
446 page_id_t(space->id, descr_page_no), page_size,
447 RW_SX_LATCH, mtr);
448
449 buf_block_dbg_add_level(block, SYNC_FSP_PAGE);
450
451 descr_page = buf_block_get_frame(block);
452 }
453
454 if (desc_block != NULL) {
455 *desc_block = block;
456 }
457
458 return(descr_page + XDES_ARR_OFFSET
459 + XDES_SIZE * xdes_calc_descriptor_index(page_size, offset));
460}
461
462/** Get the extent descriptor of a page.
463The page where the extent descriptor resides is x-locked. If the page
464offset is equal to the free limit of the space, we will add new
465extents from above the free limit to the space free list, if not free
466limit == space size. This adding is necessary to make the descriptor
467defined, as they are uninitialized above the free limit.
468@param[in] space tablespace
469@param[in] offset page offset; if equal to the free limit, we
470try to add new extents to the space free list
471@param[in] page_size page size
472@param[in,out] mtr mini-transaction
473@return the extent descriptor */
474MY_ATTRIBUTE((warn_unused_result))
475static
476xdes_t*
477xdes_get_descriptor(
478 const fil_space_t* space,
479 page_no_t offset,
480 const page_size_t& page_size,
481 mtr_t* mtr)
482{
483 buf_block_t* block;
484 fsp_header_t* sp_header;
485
486 block = buf_page_get(page_id_t(space->id, 0), page_size,
487 RW_SX_LATCH, mtr);
488
489 buf_block_dbg_add_level(block, SYNC_FSP_PAGE);
490
491 sp_header = FSP_HEADER_OFFSET + buf_block_get_frame(block);
492 return(xdes_get_descriptor_with_space_hdr(
493 sp_header, space, offset, mtr));
494}
495
496/** Get the extent descriptor of a page.
497The page where the extent descriptor resides is x-locked. If the page
498offset is equal to the free limit of the space, we will add new
499extents from above the free limit to the space free list, if not free
500limit == space size. This adding is necessary to make the descriptor
501defined, as they are uninitialized above the free limit.
502@param[in] space tablespace
503@param[in] page descriptor page offset
504@param[in] offset page offset
505@param[in] page_size page size
506@param[in,out] mtr mini-transaction
507@return the extent descriptor
508@retval NULL if the descriptor is not available */
509MY_ATTRIBUTE((warn_unused_result))
510static
511const xdes_t*
512xdes_get_descriptor_const(
513 const fil_space_t* space,
514 page_no_t page,
515 page_no_t offset,
516 const page_size_t& page_size,
517 mtr_t* mtr)
518{
519 ut_ad(mtr_memo_contains(mtr, &space->latch, MTR_MEMO_S_LOCK));
520 ut_ad(offset < space->free_limit);
521 ut_ad(offset < space->size_in_header);
522
523 if (buf_block_t* block = buf_page_get(page_id_t(space->id, page),
524 page_size, RW_S_LATCH, mtr)) {
525 buf_block_dbg_add_level(block, SYNC_FSP_PAGE);
526
527 ut_ad(page != 0 || space->free_limit == mach_read_from_4(
528 FSP_FREE_LIMIT + FSP_HEADER_OFFSET
529 + block->frame));
530 ut_ad(page != 0 || space->size_in_header == mach_read_from_4(
531 FSP_SIZE + FSP_HEADER_OFFSET
532 + block->frame));
533
534 return(block->frame + XDES_ARR_OFFSET + XDES_SIZE
535 * xdes_calc_descriptor_index(page_size, offset));
536 }
537
538 return(NULL);
539}
540
541/** Get a pointer to the extent descriptor. The page where the
542extent descriptor resides is x-locked.
543@param[in] space tablespace
544@param[in] page_size page size
545@param[in] lst_node file address of the list node
546 contained in the descriptor
547@param[in,out] mtr mini-transaction
548@return pointer to the extent descriptor */
549MY_ATTRIBUTE((nonnull, warn_unused_result))
550UNIV_INLINE
551xdes_t*
552xdes_lst_get_descriptor(
553 const fil_space_t* space,
554 const page_size_t& page_size,
555 fil_addr_t lst_node,
556 mtr_t* mtr)
557{
558 ut_ad(mtr_memo_contains(mtr, &space->latch, MTR_MEMO_X_LOCK));
559 ut_ad(page_size.equals_to(page_size_t(space->flags)));
560 return(fut_get_ptr(space->id, page_size, lst_node, RW_SX_LATCH, mtr)
561 - XDES_FLST_NODE);
562}
563
564/********************************************************************//**
565Returns page offset of the first page in extent described by a descriptor.
566@return offset of the first page in extent */
567UNIV_INLINE
568ulint
569xdes_get_offset(
570/*============*/
571 const xdes_t* descr) /*!< in: extent descriptor */
572{
573 ut_ad(descr);
574
575 return(page_get_page_no(page_align(descr))
576 + ((page_offset(descr) - XDES_ARR_OFFSET) / XDES_SIZE)
577 * FSP_EXTENT_SIZE);
578}
579
580/***********************************************************//**
581Inits a file page whose prior contents should be ignored. */
582static
583void
584fsp_init_file_page_low(
585/*===================*/
586 buf_block_t* block) /*!< in: pointer to a page */
587{
588 page_t* page = buf_block_get_frame(block);
589
590 memset(page, 0, srv_page_size);
591
592 mach_write_to_4(page + FIL_PAGE_OFFSET, block->page.id.page_no());
593 mach_write_to_4(page + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID,
594 block->page.id.space());
595
596 if (page_zip_des_t* page_zip= buf_block_get_page_zip(block)) {
597 memset(page_zip->data, 0, page_zip_get_size(page_zip));
598 memcpy(page_zip->data + FIL_PAGE_OFFSET,
599 page + FIL_PAGE_OFFSET, 4);
600 memcpy(page_zip->data + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID,
601 page + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID, 4);
602 }
603}
604
605#ifdef UNIV_DEBUG
606/** Assert that the mini-transaction is compatible with
607updating an allocation bitmap page.
608@param[in] id tablespace identifier
609@param[in] mtr mini-transaction */
610static
611void
612fsp_space_modify_check(
613 const fil_space_t* space,
614 const mtr_t* mtr)
615{
616 switch (mtr->get_log_mode()) {
617 case MTR_LOG_SHORT_INSERTS:
618 case MTR_LOG_NONE:
619 /* These modes are only allowed within a non-bitmap page
620 when there is a higher-level redo log record written. */
621 ut_ad(space->purpose == FIL_TYPE_TABLESPACE
622 || space->purpose == FIL_TYPE_TEMPORARY);
623 break;
624 case MTR_LOG_NO_REDO:
625 ut_ad(space->purpose == FIL_TYPE_TEMPORARY
626 || space->purpose == FIL_TYPE_IMPORT
627 || my_atomic_loadlint(&space->redo_skipped_count)
628 || space->is_being_truncated
629 || srv_is_tablespace_truncated(space->id));
630 return;
631 case MTR_LOG_ALL:
632 /* We may only write redo log for a persistent tablespace. */
633 ut_ad(space->purpose == FIL_TYPE_TABLESPACE);
634 ut_ad(mtr->is_named_space(space->id));
635 return;
636 }
637
638 ut_ad(0);
639}
640#endif /* UNIV_DEBUG */
641
642/** Initialize a file page.
643@param[in,out] block file page
644@param[in,out] mtr mini-transaction */
645static
646void
647fsp_init_file_page(buf_block_t* block, mtr_t* mtr)
648{
649 fsp_init_file_page_low(block);
650
651 mlog_write_initial_log_record(buf_block_get_frame(block),
652 MLOG_INIT_FILE_PAGE2, mtr);
653}
654
655#ifdef UNIV_DEBUG
656static
657void
658fsp_init_file_page(const fil_space_t* space, buf_block_t* block, mtr_t* mtr)
659{
660 ut_d(fsp_space_modify_check(space, mtr));
661 ut_ad(space->id == block->page.id.space());
662 fsp_init_file_page(block, mtr);
663}
664#else /* UNIV_DEBUG */
665# define fsp_init_file_page(space, block, mtr) fsp_init_file_page(block, mtr)
666#endif
667
668/***********************************************************//**
669Parses a redo log record of a file page init.
670@return end of log record or NULL */
671byte*
672fsp_parse_init_file_page(
673/*=====================*/
674 byte* ptr, /*!< in: buffer */
675 byte* end_ptr MY_ATTRIBUTE((unused)), /*!< in: buffer end */
676 buf_block_t* block) /*!< in: block or NULL */
677{
678 ut_ad(ptr != NULL);
679 ut_ad(end_ptr != NULL);
680
681 if (block) {
682 fsp_init_file_page_low(block);
683 }
684
685 return(ptr);
686}
687
688/**********************************************************************//**
689Writes the space id and flags to a tablespace header. The flags contain
690row type, physical/compressed page size, and logical/uncompressed page
691size of the tablespace. */
692void
693fsp_header_init_fields(
694/*===================*/
695 page_t* page, /*!< in/out: first page in the space */
696 ulint space_id, /*!< in: space id */
697 ulint flags) /*!< in: tablespace flags (FSP_SPACE_FLAGS) */
698{
699 flags &= ~FSP_FLAGS_MEM_MASK;
700 ut_a(fsp_flags_is_valid(flags, space_id));
701
702 mach_write_to_4(FSP_HEADER_OFFSET + FSP_SPACE_ID + page,
703 space_id);
704 mach_write_to_4(FSP_HEADER_OFFSET + FSP_SPACE_FLAGS + page,
705 flags);
706}
707
708/** Initialize a tablespace header.
709@param[in,out] space tablespace
710@param[in] size current size in blocks
711@param[in,out] mtr mini-transaction */
712void fsp_header_init(fil_space_t* space, ulint size, mtr_t* mtr)
713{
714 const page_id_t page_id(space->id, 0);
715 const page_size_t page_size(space->flags);
716
717 mtr_x_lock(&space->latch, mtr);
718 buf_block_t* block = buf_page_create(page_id, page_size, mtr);
719 buf_page_get(page_id, page_size, RW_SX_LATCH, mtr);
720 buf_block_dbg_add_level(block, SYNC_FSP_PAGE);
721
722 space->size_in_header = size;
723 space->free_len = 0;
724 space->free_limit = 0;
725
726 /* The prior contents of the file page should be ignored */
727
728 fsp_init_file_page(space, block, mtr);
729
730 mlog_write_ulint(block->frame + FIL_PAGE_TYPE, FIL_PAGE_TYPE_FSP_HDR,
731 MLOG_2BYTES, mtr);
732
733 mlog_write_ulint(FSP_HEADER_OFFSET + FSP_SPACE_ID + block->frame,
734 space->id, MLOG_4BYTES, mtr);
735 mlog_write_ulint(FSP_HEADER_OFFSET + FSP_NOT_USED + block->frame, 0,
736 MLOG_4BYTES, mtr);
737 mlog_write_ulint(FSP_HEADER_OFFSET + FSP_SIZE + block->frame, size,
738 MLOG_4BYTES, mtr);
739 mlog_write_ulint(FSP_HEADER_OFFSET + FSP_FREE_LIMIT + block->frame, 0,
740 MLOG_4BYTES, mtr);
741 mlog_write_ulint(FSP_HEADER_OFFSET + FSP_SPACE_FLAGS + block->frame,
742 space->flags & ~FSP_FLAGS_MEM_MASK,
743 MLOG_4BYTES, mtr);
744 mlog_write_ulint(FSP_HEADER_OFFSET + FSP_FRAG_N_USED + block->frame, 0,
745 MLOG_4BYTES, mtr);
746
747 flst_init(FSP_HEADER_OFFSET + FSP_FREE + block->frame, mtr);
748 flst_init(FSP_HEADER_OFFSET + FSP_FREE_FRAG + block->frame, mtr);
749 flst_init(FSP_HEADER_OFFSET + FSP_FULL_FRAG + block->frame, mtr);
750 flst_init(FSP_HEADER_OFFSET + FSP_SEG_INODES_FULL + block->frame, mtr);
751 flst_init(FSP_HEADER_OFFSET + FSP_SEG_INODES_FREE + block->frame, mtr);
752
753 mlog_write_ull(FSP_HEADER_OFFSET + FSP_SEG_ID + block->frame, 1, mtr);
754
755 fsp_fill_free_list(!is_system_tablespace(space->id),
756 space, FSP_HEADER_OFFSET + block->frame, mtr);
757
758 /* Write encryption metadata to page 0 if tablespace is
759 encrypted or encryption is disabled by table option. */
760 if (space->crypt_data &&
761 (space->crypt_data->should_encrypt() ||
762 space->crypt_data->not_encrypted())) {
763 space->crypt_data->write_page0(space, block->frame, mtr);
764 }
765}
766
767/**********************************************************************//**
768Reads the space id from the first page of a tablespace.
769@return space id, ULINT UNDEFINED if error */
770ulint
771fsp_header_get_space_id(
772/*====================*/
773 const page_t* page) /*!< in: first page of a tablespace */
774{
775 ulint fsp_id;
776 ulint id;
777
778 fsp_id = mach_read_from_4(FSP_HEADER_OFFSET + page + FSP_SPACE_ID);
779
780 id = mach_read_from_4(page + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID);
781
782 DBUG_EXECUTE_IF("fsp_header_get_space_id_failure",
783 id = ULINT_UNDEFINED;);
784
785 if (id != fsp_id) {
786 ib::error() << "Space ID in fsp header is " << fsp_id
787 << ", but in the page header it is " << id << ".";
788 return(ULINT_UNDEFINED);
789 }
790
791 return(id);
792}
793
794/** Try to extend a single-table tablespace so that a page would fit in the
795data file.
796@param[in,out] space tablespace
797@param[in] page_no page number
798@param[in,out] header tablespace header
799@param[in,out] mtr mini-transaction
800@return true if success */
801static ATTRIBUTE_COLD __attribute__((warn_unused_result))
802bool
803fsp_try_extend_data_file_with_pages(
804 fil_space_t* space,
805 ulint page_no,
806 fsp_header_t* header,
807 mtr_t* mtr)
808{
809 bool success;
810 ulint size;
811
812 ut_a(!is_system_tablespace(space->id));
813 ut_d(fsp_space_modify_check(space, mtr));
814
815 size = mach_read_from_4(header + FSP_SIZE);
816 ut_ad(size == space->size_in_header);
817
818 ut_a(page_no >= size);
819
820 success = fil_space_extend(space, page_no + 1);
821 /* The size may be less than we wanted if we ran out of disk space. */
822 mlog_write_ulint(header + FSP_SIZE, space->size, MLOG_4BYTES, mtr);
823 space->size_in_header = space->size;
824
825 return(success);
826}
827
828/** Try to extend the last data file of a tablespace if it is auto-extending.
829@param[in,out] space tablespace
830@param[in,out] header tablespace header
831@param[in,out] mtr mini-transaction
832@return number of pages added
833@retval 0 if the tablespace was not extended */
834ATTRIBUTE_COLD __attribute__((nonnull))
835static
836ulint
837fsp_try_extend_data_file(fil_space_t* space, fsp_header_t* header, mtr_t* mtr)
838{
839 ulint size; /* current number of pages in the datafile */
840 ulint size_increase; /* number of pages to extend this file */
841 const char* OUT_OF_SPACE_MSG =
842 "ran out of space. Please add another file or use"
843 " 'autoextend' for the last file in setting";
844
845 ut_d(fsp_space_modify_check(space, mtr));
846
847 if (space->id == TRX_SYS_SPACE
848 && !srv_sys_space.can_auto_extend_last_file()) {
849
850 /* We print the error message only once to avoid
851 spamming the error log. Note that we don't need
852 to reset the flag to false as dealing with this
853 error requires server restart. */
854 if (!srv_sys_space.get_tablespace_full_status()) {
855 ib::error() << "The InnoDB system tablespace "
856 << OUT_OF_SPACE_MSG
857 << " innodb_data_file_path.";
858 srv_sys_space.set_tablespace_full_status(true);
859 }
860 return(0);
861 } else if (space->id == SRV_TMP_SPACE_ID
862 && !srv_tmp_space.can_auto_extend_last_file()) {
863
864 /* We print the error message only once to avoid
865 spamming the error log. Note that we don't need
866 to reset the flag to false as dealing with this
867 error requires server restart. */
868 if (!srv_tmp_space.get_tablespace_full_status()) {
869 ib::error() << "The InnoDB temporary tablespace "
870 << OUT_OF_SPACE_MSG
871 << " innodb_temp_data_file_path.";
872 srv_tmp_space.set_tablespace_full_status(true);
873 }
874 return(0);
875 }
876
877 size = mach_read_from_4(header + FSP_SIZE);
878 ut_ad(size == space->size_in_header);
879
880 const page_size_t page_size(
881 mach_read_from_4(header + FSP_SPACE_FLAGS));
882
883 switch (space->id) {
884 case TRX_SYS_SPACE:
885 size_increase = srv_sys_space.get_increment();
886 break;
887 case SRV_TMP_SPACE_ID:
888 size_increase = srv_tmp_space.get_increment();
889 break;
890 default:
891 ulint extent_pages
892 = fsp_get_extent_size_in_pages(page_size);
893 if (size < extent_pages) {
894 /* Let us first extend the file to extent_size */
895 if (!fsp_try_extend_data_file_with_pages(
896 space, extent_pages - 1, header, mtr)) {
897 return(0);
898 }
899
900 size = extent_pages;
901 }
902
903 size_increase = fsp_get_pages_to_extend_ibd(page_size, size);
904 }
905
906 if (size_increase == 0) {
907 return(0);
908 }
909
910 if (!fil_space_extend(space, size + size_increase)) {
911 return(0);
912 }
913
914 /* We ignore any fragments of a full megabyte when storing the size
915 to the space header */
916
917 space->size_in_header = ut_calc_align_down(
918 space->size, (1024 * 1024) / page_size.physical());
919
920 mlog_write_ulint(
921 header + FSP_SIZE, space->size_in_header, MLOG_4BYTES, mtr);
922
923 return(size_increase);
924}
925
926/** Calculate the number of pages to extend a datafile.
927We extend single-table tablespaces first one extent at a time,
928but 4 at a time for bigger tablespaces. It is not enough to extend always
929by one extent, because we need to add at least one extent to FSP_FREE.
930A single extent descriptor page will track many extents. And the extent
931that uses its extent descriptor page is put onto the FSP_FREE_FRAG list.
932Extents that do not use their extent descriptor page are added to FSP_FREE.
933The physical page size is used to determine how many extents are tracked
934on one extent descriptor page. See xdes_calc_descriptor_page().
935@param[in] page_size page_size of the datafile
936@param[in] size current number of pages in the datafile
937@return number of pages to extend the file. */
938ulint
939fsp_get_pages_to_extend_ibd(
940 const page_size_t& page_size,
941 ulint size)
942{
943 ulint size_increase; /* number of pages to extend this file */
944 ulint extent_size; /* one megabyte, in pages */
945 ulint threshold; /* The size of the tablespace (in number
946 of pages) where we start allocating more
947 than one extent at a time. */
948
949 extent_size = fsp_get_extent_size_in_pages(page_size);
950
951 /* The threshold is set at 32MiB except when the physical page
952 size is small enough that it must be done sooner. */
953 threshold = ut_min(32 * extent_size, page_size.physical());
954
955 if (size < threshold) {
956 size_increase = extent_size;
957 } else {
958 /* Below in fsp_fill_free_list() we assume
959 that we add at most FSP_FREE_ADD extents at
960 a time */
961 size_increase = FSP_FREE_ADD * extent_size;
962 }
963
964 return(size_increase);
965}
966
967/** Put new extents to the free list if there are free extents above the free
968limit. If an extent happens to contain an extent descriptor page, the extent
969is put to the FSP_FREE_FRAG list with the page marked as used.
970@param[in] init_space true if this is a single-table tablespace
971and we are only initializing the first extent and the first bitmap pages;
972then we will not allocate more extents
973@param[in,out] space tablespace
974@param[in,out] header tablespace header
975@param[in,out] mtr mini-transaction */
976static
977void
978fsp_fill_free_list(
979 bool init_space,
980 fil_space_t* space,
981 fsp_header_t* header,
982 mtr_t* mtr)
983{
984 ulint limit;
985 ulint size;
986 xdes_t* descr;
987 ulint count = 0;
988 ulint frag_n_used;
989 ulint i;
990
991 ut_ad(page_offset(header) == FSP_HEADER_OFFSET);
992 ut_d(fsp_space_modify_check(space, mtr));
993
994 /* Check if we can fill free list from above the free list limit */
995 size = mach_read_from_4(header + FSP_SIZE);
996 limit = mach_read_from_4(header + FSP_FREE_LIMIT);
997
998 ut_ad(size == space->size_in_header);
999 ut_ad(limit == space->free_limit);
1000
1001 const page_size_t page_size(space->flags);
1002
1003 if (size < limit + FSP_EXTENT_SIZE * FSP_FREE_ADD) {
1004 bool skip_resize = init_space;
1005 switch (space->id) {
1006 case TRX_SYS_SPACE:
1007 skip_resize = !srv_sys_space.can_auto_extend_last_file();
1008 break;
1009 case SRV_TMP_SPACE_ID:
1010 skip_resize = !srv_tmp_space.can_auto_extend_last_file();
1011 break;
1012 }
1013
1014 if (!skip_resize) {
1015 fsp_try_extend_data_file(space, header, mtr);
1016 size = space->size_in_header;
1017 }
1018 }
1019
1020 i = limit;
1021
1022 while ((init_space && i < 1)
1023 || ((i + FSP_EXTENT_SIZE <= size) && (count < FSP_FREE_ADD))) {
1024
1025 bool init_xdes
1026 = (ut_2pow_remainder(i, page_size.physical()) == 0);
1027
1028 space->free_limit = i + FSP_EXTENT_SIZE;
1029 mlog_write_ulint(header + FSP_FREE_LIMIT, i + FSP_EXTENT_SIZE,
1030 MLOG_4BYTES, mtr);
1031
1032 if (init_xdes) {
1033
1034 buf_block_t* block;
1035
1036 /* We are going to initialize a new descriptor page
1037 and a new ibuf bitmap page: the prior contents of the
1038 pages should be ignored. */
1039
1040 if (i > 0) {
1041 const page_id_t page_id(space->id, i);
1042
1043 block = buf_page_create(
1044 page_id, page_size, mtr);
1045
1046 buf_page_get(
1047 page_id, page_size, RW_SX_LATCH, mtr);
1048
1049 buf_block_dbg_add_level(block, SYNC_FSP_PAGE);
1050
1051 fsp_init_file_page(space, block, mtr);
1052 mlog_write_ulint(buf_block_get_frame(block)
1053 + FIL_PAGE_TYPE,
1054 FIL_PAGE_TYPE_XDES,
1055 MLOG_2BYTES, mtr);
1056 }
1057
1058 /* Initialize the ibuf bitmap page in a separate
1059 mini-transaction because it is low in the latching
1060 order, and we must be able to release its latch.
1061 Note: Insert-Buffering is disabled for tables that
1062 reside in the temp-tablespace. */
1063 if (space->purpose != FIL_TYPE_TEMPORARY) {
1064 mtr_t ibuf_mtr;
1065
1066 mtr_start(&ibuf_mtr);
1067 ibuf_mtr.set_named_space(space);
1068
1069 /* Avoid logging while truncate table
1070 fix-up is active. */
1071 if (srv_is_tablespace_truncated(space->id)) {
1072 mtr_set_log_mode(
1073 &ibuf_mtr, MTR_LOG_NO_REDO);
1074 }
1075
1076 const page_id_t page_id(
1077 space->id,
1078 i + FSP_IBUF_BITMAP_OFFSET);
1079
1080 block = buf_page_create(
1081 page_id, page_size, &ibuf_mtr);
1082
1083 buf_page_get(
1084 page_id, page_size, RW_SX_LATCH,
1085 &ibuf_mtr);
1086
1087 buf_block_dbg_add_level(block, SYNC_FSP_PAGE);
1088
1089 fsp_init_file_page(space, block, &ibuf_mtr);
1090
1091 ibuf_bitmap_page_init(block, &ibuf_mtr);
1092
1093 mtr_commit(&ibuf_mtr);
1094 }
1095 }
1096
1097 buf_block_t* desc_block = NULL;
1098 descr = xdes_get_descriptor_with_space_hdr(
1099 header, space, i, mtr, init_space, &desc_block);
1100 if (desc_block != NULL) {
1101 fil_block_check_type(
1102 desc_block, FIL_PAGE_TYPE_XDES, mtr);
1103 }
1104 xdes_init(descr, mtr);
1105
1106 if (UNIV_UNLIKELY(init_xdes)) {
1107
1108 /* The first page in the extent is a descriptor page
1109 and the second is an ibuf bitmap page: mark them
1110 used */
1111
1112 xdes_set_bit(descr, XDES_FREE_BIT, 0, FALSE, mtr);
1113 xdes_set_bit(descr, XDES_FREE_BIT,
1114 FSP_IBUF_BITMAP_OFFSET, FALSE, mtr);
1115 xdes_set_state(descr, XDES_FREE_FRAG, mtr);
1116
1117 flst_add_last(header + FSP_FREE_FRAG,
1118 descr + XDES_FLST_NODE, mtr);
1119 frag_n_used = mach_read_from_4(
1120 header + FSP_FRAG_N_USED);
1121 mlog_write_ulint(header + FSP_FRAG_N_USED,
1122 frag_n_used + 2, MLOG_4BYTES, mtr);
1123 } else {
1124 flst_add_last(header + FSP_FREE,
1125 descr + XDES_FLST_NODE, mtr);
1126 count++;
1127 }
1128
1129 i += FSP_EXTENT_SIZE;
1130 }
1131
1132 space->free_len += count;
1133}
1134
1135/** Allocates a new free extent.
1136@param[in,out] space tablespace
1137@param[in] page_size page size
1138@param[in] hint hint of which extent would be desirable: any
1139page offset in the extent goes; the hint must not be > FSP_FREE_LIMIT
1140@param[in,out] mtr mini-transaction
1141@return extent descriptor, NULL if cannot be allocated */
1142static
1143xdes_t*
1144fsp_alloc_free_extent(
1145 fil_space_t* space,
1146 const page_size_t& page_size,
1147 ulint hint,
1148 mtr_t* mtr)
1149{
1150 fsp_header_t* header;
1151 fil_addr_t first;
1152 xdes_t* descr;
1153 buf_block_t* desc_block = NULL;
1154
1155 header = fsp_get_space_header(space, page_size, mtr);
1156
1157 descr = xdes_get_descriptor_with_space_hdr(
1158 header, space, hint, mtr, false, &desc_block);
1159
1160 if (desc_block != NULL) {
1161 fil_block_check_type(desc_block, FIL_PAGE_TYPE_XDES, mtr);
1162 }
1163
1164 if (descr && (xdes_get_state(descr, mtr) == XDES_FREE)) {
1165 /* Ok, we can take this extent */
1166 } else {
1167 /* Take the first extent in the free list */
1168 first = flst_get_first(header + FSP_FREE, mtr);
1169
1170 if (fil_addr_is_null(first)) {
1171 fsp_fill_free_list(false, space, header, mtr);
1172
1173 first = flst_get_first(header + FSP_FREE, mtr);
1174 }
1175
1176 if (fil_addr_is_null(first)) {
1177
1178 return(NULL); /* No free extents left */
1179 }
1180
1181 descr = xdes_lst_get_descriptor(
1182 space, page_size, first, mtr);
1183 }
1184
1185 flst_remove(header + FSP_FREE, descr + XDES_FLST_NODE, mtr);
1186 space->free_len--;
1187
1188 return(descr);
1189}
1190
1191/**********************************************************************//**
1192Allocates a single free page from a space. */
1193static MY_ATTRIBUTE((nonnull))
1194void
1195fsp_alloc_from_free_frag(
1196/*=====================*/
1197 fsp_header_t* header, /*!< in/out: tablespace header */
1198 xdes_t* descr, /*!< in/out: extent descriptor */
1199 ulint bit, /*!< in: slot to allocate in the extent */
1200 mtr_t* mtr) /*!< in/out: mini-transaction */
1201{
1202 ulint frag_n_used;
1203
1204 ut_ad(xdes_get_state(descr, mtr) == XDES_FREE_FRAG);
1205 ut_a(xdes_mtr_get_bit(descr, XDES_FREE_BIT, bit, mtr));
1206 xdes_set_bit(descr, XDES_FREE_BIT, bit, FALSE, mtr);
1207
1208 /* Update the FRAG_N_USED field */
1209 frag_n_used = mach_read_from_4(header + FSP_FRAG_N_USED);
1210 frag_n_used++;
1211 mlog_write_ulint(header + FSP_FRAG_N_USED, frag_n_used, MLOG_4BYTES,
1212 mtr);
1213 if (xdes_is_full(descr, mtr)) {
1214 /* The fragment is full: move it to another list */
1215 flst_remove(header + FSP_FREE_FRAG, descr + XDES_FLST_NODE,
1216 mtr);
1217 xdes_set_state(descr, XDES_FULL_FRAG, mtr);
1218
1219 flst_add_last(header + FSP_FULL_FRAG, descr + XDES_FLST_NODE,
1220 mtr);
1221 mlog_write_ulint(header + FSP_FRAG_N_USED,
1222 frag_n_used - FSP_EXTENT_SIZE, MLOG_4BYTES,
1223 mtr);
1224 }
1225}
1226
1227/** Gets a buffer block for an allocated page.
1228NOTE: If init_mtr != mtr, the block will only be initialized if it was
1229not previously x-latched. It is assumed that the block has been
1230x-latched only by mtr, and freed in mtr in that case.
1231@param[in,out] space tablespace
1232@param[in] offset page number of the allocated page
1233@param[in] page_size page size of the allocated page
1234@param[in] rw_latch RW_SX_LATCH, RW_X_LATCH
1235@param[in,out] mtr mini-transaction of the allocation
1236@param[in,out] init_mtr mini-transaction for initializing the page
1237@return block, initialized if init_mtr==mtr
1238or rw_lock_x_lock_count(&block->lock) == 1 */
1239static
1240buf_block_t*
1241fsp_page_create(
1242 fil_space_t* space,
1243 page_no_t offset,
1244 const page_size_t& page_size,
1245 rw_lock_type_t rw_latch,
1246 mtr_t* mtr,
1247 mtr_t* init_mtr)
1248{
1249 ut_ad(page_size.equals_to(page_size_t(space->flags)));
1250
1251 buf_block_t* block = buf_page_create(page_id_t(space->id, offset),
1252 page_size, init_mtr);
1253
1254 ut_d(bool latched = mtr_memo_contains_flagged(mtr, block,
1255 MTR_MEMO_PAGE_X_FIX
1256 | MTR_MEMO_PAGE_SX_FIX));
1257
1258 ut_ad(rw_latch == RW_X_LATCH || rw_latch == RW_SX_LATCH);
1259
1260 /* Mimic buf_page_get(), but avoid the buf_pool->page_hash lookup. */
1261 if (rw_latch == RW_X_LATCH) {
1262 rw_lock_x_lock(&block->lock);
1263 } else {
1264 rw_lock_sx_lock(&block->lock);
1265 }
1266 mutex_enter(&block->mutex);
1267
1268 buf_block_buf_fix_inc(block, __FILE__, __LINE__);
1269
1270 mutex_exit(&block->mutex);
1271 mtr_memo_push(init_mtr, block, rw_latch == RW_X_LATCH
1272 ? MTR_MEMO_PAGE_X_FIX : MTR_MEMO_PAGE_SX_FIX);
1273
1274 if (init_mtr == mtr
1275 || (rw_latch == RW_X_LATCH
1276 ? rw_lock_get_x_lock_count(&block->lock) == 1
1277 : rw_lock_get_sx_lock_count(&block->lock) == 1)) {
1278
1279 /* Initialize the page, unless it was already
1280 SX-latched in mtr. (In this case, we would want to
1281 allocate another page that has not been freed in mtr.) */
1282 ut_ad(init_mtr == mtr || !latched);
1283 fsp_init_file_page(space, block, init_mtr);
1284 }
1285
1286 return(block);
1287}
1288
1289/** Allocates a single free page from a space.
1290The page is marked as used.
1291@param[in,out] space tablespace
1292@param[in] page_size page size
1293@param[in] hint hint of which page would be desirable
1294@param[in] rw_latch RW_SX_LATCH, RW_X_LATCH
1295@param[in,out] mtr mini-transaction
1296@param[in,out] init_mtr mini-transaction in which the page should be
1297initialized (may be the same as mtr)
1298@retval NULL if no page could be allocated
1299@retval block rw_lock_x_lock_count(&block->lock) == 1 if allocation succeeded
1300(init_mtr == mtr, or the page was not previously freed in mtr)
1301@retval block (not allocated or initialized) otherwise */
1302static MY_ATTRIBUTE((warn_unused_result, nonnull))
1303buf_block_t*
1304fsp_alloc_free_page(
1305 fil_space_t* space,
1306 const page_size_t& page_size,
1307 ulint hint,
1308 rw_lock_type_t rw_latch,
1309 mtr_t* mtr,
1310 mtr_t* init_mtr)
1311{
1312 fsp_header_t* header;
1313 fil_addr_t first;
1314 xdes_t* descr;
1315 ulint free;
1316 const ulint space_id = space->id;
1317
1318 ut_d(fsp_space_modify_check(space, mtr));
1319 header = fsp_get_space_header(space, page_size, mtr);
1320
1321 /* Get the hinted descriptor */
1322 descr = xdes_get_descriptor_with_space_hdr(header, space, hint, mtr);
1323
1324 if (descr && (xdes_get_state(descr, mtr) == XDES_FREE_FRAG)) {
1325 /* Ok, we can take this extent */
1326 } else {
1327 /* Else take the first extent in free_frag list */
1328 first = flst_get_first(header + FSP_FREE_FRAG, mtr);
1329
1330 if (fil_addr_is_null(first)) {
1331 /* There are no partially full fragments: allocate
1332 a free extent and add it to the FREE_FRAG list. NOTE
1333 that the allocation may have as a side-effect that an
1334 extent containing a descriptor page is added to the
1335 FREE_FRAG list. But we will allocate our page from the
1336 the free extent anyway. */
1337
1338 descr = fsp_alloc_free_extent(space, page_size,
1339 hint, mtr);
1340
1341 if (descr == NULL) {
1342 /* No free space left */
1343
1344 return(NULL);
1345 }
1346
1347 xdes_set_state(descr, XDES_FREE_FRAG, mtr);
1348 flst_add_last(header + FSP_FREE_FRAG,
1349 descr + XDES_FLST_NODE, mtr);
1350 } else {
1351 descr = xdes_lst_get_descriptor(space, page_size,
1352 first, mtr);
1353 }
1354
1355 /* Reset the hint */
1356 hint = 0;
1357 }
1358
1359 /* Now we have in descr an extent with at least one free page. Look
1360 for a free page in the extent. */
1361
1362 free = xdes_find_bit(descr, XDES_FREE_BIT, TRUE,
1363 hint % FSP_EXTENT_SIZE, mtr);
1364 if (free == ULINT_UNDEFINED) {
1365
1366 ut_print_buf(stderr, ((byte*) descr) - 500, 1000);
1367 putc('\n', stderr);
1368
1369 ut_error;
1370 }
1371
1372 page_no_t page_no = xdes_get_offset(descr) + free;
1373
1374 page_no_t space_size = mach_read_from_4(header + FSP_SIZE);
1375 ut_ad(space_size == space->size_in_header
1376 || (space_id == TRX_SYS_SPACE
1377 && srv_startup_is_before_trx_rollback_phase));
1378
1379 if (space_size <= page_no) {
1380 /* It must be that we are extending a single-table tablespace
1381 whose size is still < 64 pages */
1382
1383 ut_a(!is_system_tablespace(space_id));
1384 if (page_no >= FSP_EXTENT_SIZE) {
1385 ib::error() << "Trying to extend a single-table"
1386 " tablespace " << space << " , by single"
1387 " page(s) though the space size " << space_size
1388 << ". Page no " << page_no << ".";
1389 return(NULL);
1390 }
1391
1392 if (!fsp_try_extend_data_file_with_pages(space, page_no,
1393 header, mtr)) {
1394 /* No disk space left */
1395 return(NULL);
1396 }
1397 }
1398
1399 fsp_alloc_from_free_frag(header, descr, free, mtr);
1400 return(fsp_page_create(space, page_no, page_size, rw_latch,
1401 mtr, init_mtr));
1402}
1403
1404/** Frees a single page of a space.
1405The page is marked as free and clean.
1406@param[in,out] space tablespace
1407@param[in] page_id page id
1408@param[in] page_size page size
1409@param[in,out] mtr mini-transaction */
1410static
1411void
1412fsp_free_page(
1413 fil_space_t* space,
1414 ulint offset,
1415 const page_size_t& page_size,
1416 mtr_t* mtr)
1417{
1418 fsp_header_t* header;
1419 xdes_t* descr;
1420 ulint state;
1421 ulint frag_n_used;
1422
1423 ut_ad(mtr);
1424 ut_d(fsp_space_modify_check(space, mtr));
1425
1426 /* fprintf(stderr, "Freeing page %lu in space %lu\n", page, space); */
1427
1428 header = fsp_get_space_header(space, page_size, mtr);
1429
1430 descr = xdes_get_descriptor_with_space_hdr(
1431 header, space, offset, mtr);
1432
1433 state = xdes_get_state(descr, mtr);
1434
1435 if (UNIV_UNLIKELY(state != XDES_FREE_FRAG
1436 && state != XDES_FULL_FRAG)) {
1437 ib::error() << "File space extent descriptor of page "
1438 << page_id_t(space->id, offset)
1439 << " has state " << state;
1440 /* Crash in debug version, so that we get a core dump
1441 of this corruption. */
1442 ut_ad(0);
1443
1444 if (state == XDES_FREE) {
1445 /* We put here some fault tolerance: if the page
1446 is already free, return without doing anything! */
1447
1448 return;
1449 }
1450
1451 ut_error;
1452 }
1453
1454 if (xdes_mtr_get_bit(descr, XDES_FREE_BIT,
1455 offset % FSP_EXTENT_SIZE, mtr)) {
1456
1457 ib::error() << "File space extent descriptor of page "
1458 << page_id_t(space->id, offset)
1459 << " says it is free.";
1460 /* Crash in debug version, so that we get a core dump
1461 of this corruption. */
1462 ut_ad(0);
1463
1464 /* We put here some fault tolerance: if the page
1465 is already free, return without doing anything! */
1466
1467 return;
1468 }
1469
1470 const ulint bit = offset % FSP_EXTENT_SIZE;
1471
1472 xdes_set_bit(descr, XDES_FREE_BIT, bit, TRUE, mtr);
1473 xdes_set_bit(descr, XDES_CLEAN_BIT, bit, TRUE, mtr);
1474
1475 frag_n_used = mtr_read_ulint(header + FSP_FRAG_N_USED, MLOG_4BYTES,
1476 mtr);
1477 if (state == XDES_FULL_FRAG) {
1478 /* The fragment was full: move it to another list */
1479 flst_remove(header + FSP_FULL_FRAG, descr + XDES_FLST_NODE,
1480 mtr);
1481 xdes_set_state(descr, XDES_FREE_FRAG, mtr);
1482 flst_add_last(header + FSP_FREE_FRAG, descr + XDES_FLST_NODE,
1483 mtr);
1484 mlog_write_ulint(header + FSP_FRAG_N_USED,
1485 frag_n_used + FSP_EXTENT_SIZE - 1,
1486 MLOG_4BYTES, mtr);
1487 } else {
1488 ut_a(frag_n_used > 0);
1489 mlog_write_ulint(header + FSP_FRAG_N_USED, frag_n_used - 1,
1490 MLOG_4BYTES, mtr);
1491 }
1492
1493 if (xdes_is_free(descr, mtr)) {
1494 /* The extent has become free: move it to another list */
1495 flst_remove(header + FSP_FREE_FRAG, descr + XDES_FLST_NODE,
1496 mtr);
1497 fsp_free_extent(space, offset, page_size, mtr);
1498 }
1499}
1500
1501/** Return an extent to the free list of a space.
1502@param[in,out] space tablespace
1503@param[in] offset page number in the extent
1504@param[in] page_size page size
1505@param[in,out] mtr mini-transaction */
1506static
1507void
1508fsp_free_extent(
1509 fil_space_t* space,
1510 page_no_t offset,
1511 const page_size_t& page_size,
1512 mtr_t* mtr)
1513{
1514 fsp_header_t* header;
1515 xdes_t* descr;
1516
1517 ut_ad(mtr_memo_contains(mtr, &space->latch, MTR_MEMO_X_LOCK));
1518
1519 header = fsp_get_space_header(space, page_size, mtr);
1520
1521 descr = xdes_get_descriptor_with_space_hdr(
1522 header, space, offset, mtr);
1523
1524 ut_a(xdes_get_state(descr, mtr) != XDES_FREE);
1525
1526 xdes_init(descr, mtr);
1527
1528 flst_add_last(header + FSP_FREE, descr + XDES_FLST_NODE, mtr);
1529 space->free_len++;
1530}
1531
1532/** Returns the nth inode slot on an inode page.
1533@param[in] page segment inode page
1534@param[in] i inode index on page
1535@param[in] page_size page size
1536@param[in,out] mtr mini-transaction
1537@return segment inode */
1538UNIV_INLINE
1539fseg_inode_t*
1540fsp_seg_inode_page_get_nth_inode(
1541 page_t* page,
1542 ulint i,
1543 const page_size_t& page_size,
1544 mtr_t* mtr)
1545{
1546 ut_ad(i < FSP_SEG_INODES_PER_PAGE(page_size));
1547 ut_ad(mtr_memo_contains_page(mtr, page, MTR_MEMO_PAGE_SX_FIX));
1548
1549 return(page + FSEG_ARR_OFFSET + FSEG_INODE_SIZE * i);
1550}
1551
1552/** Looks for a used segment inode on a segment inode page.
1553@param[in] page segment inode page
1554@param[in] page_size page size
1555@param[in,out] mtr mini-transaction
1556@return segment inode index, or ULINT_UNDEFINED if not found */
1557static
1558ulint
1559fsp_seg_inode_page_find_used(
1560 page_t* page,
1561 const page_size_t& page_size,
1562 mtr_t* mtr)
1563{
1564 ulint i;
1565 fseg_inode_t* inode;
1566
1567 for (i = 0; i < FSP_SEG_INODES_PER_PAGE(page_size); i++) {
1568
1569 inode = fsp_seg_inode_page_get_nth_inode(
1570 page, i, page_size, mtr);
1571
1572 if (mach_read_from_8(inode + FSEG_ID)) {
1573 /* This is used */
1574
1575 ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N)
1576 == FSEG_MAGIC_N_VALUE);
1577 return(i);
1578 }
1579 }
1580
1581 return(ULINT_UNDEFINED);
1582}
1583
1584/** Looks for an unused segment inode on a segment inode page.
1585@param[in] page segment inode page
1586@param[in] i search forward starting from this index
1587@param[in] page_size page size
1588@param[in,out] mtr mini-transaction
1589@return segment inode index, or ULINT_UNDEFINED if not found */
1590static
1591ulint
1592fsp_seg_inode_page_find_free(
1593 page_t* page,
1594 ulint i,
1595 const page_size_t& page_size,
1596 mtr_t* mtr)
1597{
1598 for (; i < FSP_SEG_INODES_PER_PAGE(page_size); i++) {
1599
1600 fseg_inode_t* inode;
1601
1602 inode = fsp_seg_inode_page_get_nth_inode(
1603 page, i, page_size, mtr);
1604
1605 if (!mach_read_from_8(inode + FSEG_ID)) {
1606 /* This is unused */
1607 return(i);
1608 }
1609
1610 ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N)
1611 == FSEG_MAGIC_N_VALUE);
1612 }
1613
1614 return(ULINT_UNDEFINED);
1615}
1616
1617/** Allocate a file segment inode page.
1618@param[in,out] space tablespace
1619@param[in,out] space_header tablespace header
1620@param[in,out] mtr mini-transaction
1621@return whether the allocation succeeded */
1622MY_ATTRIBUTE((nonnull, warn_unused_result))
1623static
1624bool
1625fsp_alloc_seg_inode_page(
1626 fil_space_t* space,
1627 fsp_header_t* space_header,
1628 mtr_t* mtr)
1629{
1630 fseg_inode_t* inode;
1631 buf_block_t* block;
1632 page_t* page;
1633
1634 ut_ad(page_offset(space_header) == FSP_HEADER_OFFSET);
1635 ut_ad(page_get_space_id(page_align(space_header)) == space->id);
1636
1637 const page_size_t page_size(space->flags);
1638
1639 block = fsp_alloc_free_page(
1640 space, page_size, 0, RW_SX_LATCH, mtr, mtr);
1641
1642 if (block == NULL) {
1643
1644 return(false);
1645 }
1646
1647 buf_block_dbg_add_level(block, SYNC_FSP_PAGE);
1648 ut_ad(rw_lock_get_sx_lock_count(&block->lock) == 1);
1649
1650 page = buf_block_get_frame(block);
1651
1652 mlog_write_ulint(page + FIL_PAGE_TYPE, FIL_PAGE_INODE,
1653 MLOG_2BYTES, mtr);
1654
1655 for (ulint i = 0; i < FSP_SEG_INODES_PER_PAGE(page_size); i++) {
1656
1657 inode = fsp_seg_inode_page_get_nth_inode(
1658 page, i, page_size, mtr);
1659
1660 mlog_write_ull(inode + FSEG_ID, 0, mtr);
1661 }
1662
1663 flst_add_last(
1664 space_header + FSP_SEG_INODES_FREE,
1665 page + FSEG_INODE_PAGE_NODE, mtr);
1666
1667 return(true);
1668}
1669
1670/** Allocate a file segment inode.
1671@param[in,out] space tablespace
1672@param[in,out] space_header tablespace header
1673@param[in,out] mtr mini-transaction
1674@return segment inode
1675@retval NULL if not enough space */
1676MY_ATTRIBUTE((nonnull, warn_unused_result))
1677static
1678fseg_inode_t*
1679fsp_alloc_seg_inode(
1680 fil_space_t* space,
1681 fsp_header_t* space_header,
1682 mtr_t* mtr)
1683{
1684 buf_block_t* block;
1685 page_t* page;
1686 fseg_inode_t* inode;
1687 ulint n;
1688
1689 ut_ad(page_offset(space_header) == FSP_HEADER_OFFSET);
1690
1691 /* Allocate a new segment inode page if needed. */
1692 if (flst_get_len(space_header + FSP_SEG_INODES_FREE) == 0
1693 && !fsp_alloc_seg_inode_page(space, space_header, mtr)) {
1694 return(NULL);
1695 }
1696 const page_size_t page_size(space->flags);
1697 const page_id_t page_id(
1698 space->id,
1699 flst_get_first(space_header + FSP_SEG_INODES_FREE, mtr).page);
1700
1701 block = buf_page_get(page_id, page_size, RW_SX_LATCH, mtr);
1702 buf_block_dbg_add_level(block, SYNC_FSP_PAGE);
1703 fil_block_check_type(block, FIL_PAGE_INODE, mtr);
1704
1705 page = buf_block_get_frame(block);
1706
1707 n = fsp_seg_inode_page_find_free(page, 0, page_size, mtr);
1708
1709 ut_a(n != ULINT_UNDEFINED);
1710
1711 inode = fsp_seg_inode_page_get_nth_inode(page, n, page_size, mtr);
1712
1713 if (ULINT_UNDEFINED == fsp_seg_inode_page_find_free(page, n + 1,
1714 page_size, mtr)) {
1715 /* There are no other unused headers left on the page: move it
1716 to another list */
1717
1718 flst_remove(space_header + FSP_SEG_INODES_FREE,
1719 page + FSEG_INODE_PAGE_NODE, mtr);
1720
1721 flst_add_last(space_header + FSP_SEG_INODES_FULL,
1722 page + FSEG_INODE_PAGE_NODE, mtr);
1723 }
1724
1725 ut_ad(!mach_read_from_8(inode + FSEG_ID)
1726 || mach_read_from_4(inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE);
1727 return(inode);
1728}
1729
1730/** Frees a file segment inode.
1731@param[in,out] space tablespace
1732@param[in] page_size page size
1733@param[in,out] inode segment inode
1734@param[in,out] mtr mini-transaction */
1735static
1736void
1737fsp_free_seg_inode(
1738 fil_space_t* space,
1739 const page_size_t& page_size,
1740 fseg_inode_t* inode,
1741 mtr_t* mtr)
1742{
1743 page_t* page;
1744 fsp_header_t* space_header;
1745
1746 ut_d(fsp_space_modify_check(space, mtr));
1747
1748 page = page_align(inode);
1749
1750 space_header = fsp_get_space_header(space, page_size, mtr);
1751
1752 ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE);
1753
1754 if (ULINT_UNDEFINED
1755 == fsp_seg_inode_page_find_free(page, 0, page_size, mtr)) {
1756
1757 /* Move the page to another list */
1758
1759 flst_remove(space_header + FSP_SEG_INODES_FULL,
1760 page + FSEG_INODE_PAGE_NODE, mtr);
1761
1762 flst_add_last(space_header + FSP_SEG_INODES_FREE,
1763 page + FSEG_INODE_PAGE_NODE, mtr);
1764 }
1765
1766 mlog_write_ull(inode + FSEG_ID, 0, mtr);
1767 mlog_write_ulint(inode + FSEG_MAGIC_N, 0xfa051ce3, MLOG_4BYTES, mtr);
1768
1769 if (ULINT_UNDEFINED
1770 == fsp_seg_inode_page_find_used(page, page_size, mtr)) {
1771
1772 /* There are no other used headers left on the page: free it */
1773
1774 flst_remove(space_header + FSP_SEG_INODES_FREE,
1775 page + FSEG_INODE_PAGE_NODE, mtr);
1776
1777 fsp_free_page(space, page_get_page_no(page), page_size, mtr);
1778 }
1779}
1780
1781/** Returns the file segment inode, page x-latched.
1782@param[in] header segment header
1783@param[in] space space id
1784@param[in] page_size page size
1785@param[in,out] mtr mini-transaction
1786@param[out] block inode block, or NULL to ignore
1787@return segment inode, page x-latched; NULL if the inode is free */
1788static
1789fseg_inode_t*
1790fseg_inode_try_get(
1791 fseg_header_t* header,
1792 ulint space,
1793 const page_size_t& page_size,
1794 mtr_t* mtr,
1795 buf_block_t** block)
1796{
1797 fil_addr_t inode_addr;
1798 fseg_inode_t* inode;
1799
1800 inode_addr.page = mach_read_from_4(header + FSEG_HDR_PAGE_NO);
1801 inode_addr.boffset = mach_read_from_2(header + FSEG_HDR_OFFSET);
1802 ut_ad(space == mach_read_from_4(header + FSEG_HDR_SPACE));
1803
1804 inode = fut_get_ptr(space, page_size, inode_addr, RW_SX_LATCH, mtr,
1805 block);
1806
1807 if (UNIV_UNLIKELY(!mach_read_from_8(inode + FSEG_ID))) {
1808
1809 inode = NULL;
1810 } else {
1811 ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N)
1812 == FSEG_MAGIC_N_VALUE);
1813 }
1814
1815 return(inode);
1816}
1817
1818/** Returns the file segment inode, page x-latched.
1819@param[in] header segment header
1820@param[in] space space id
1821@param[in] page_size page size
1822@param[in,out] mtr mini-transaction
1823@param[out] block inode block
1824@return segment inode, page x-latched */
1825static
1826fseg_inode_t*
1827fseg_inode_get(
1828 fseg_header_t* header,
1829 ulint space,
1830 const page_size_t& page_size,
1831 mtr_t* mtr,
1832 buf_block_t** block = NULL)
1833{
1834 fseg_inode_t* inode
1835 = fseg_inode_try_get(header, space, page_size, mtr, block);
1836 ut_a(inode);
1837 return(inode);
1838}
1839
1840/**********************************************************************//**
1841Gets the page number from the nth fragment page slot.
1842@return page number, FIL_NULL if not in use */
1843UNIV_INLINE
1844ulint
1845fseg_get_nth_frag_page_no(
1846/*======================*/
1847 fseg_inode_t* inode, /*!< in: segment inode */
1848 ulint n, /*!< in: slot index */
1849 mtr_t* mtr MY_ATTRIBUTE((unused)))
1850 /*!< in/out: mini-transaction */
1851{
1852 ut_ad(inode && mtr);
1853 ut_ad(n < FSEG_FRAG_ARR_N_SLOTS);
1854 ut_ad(mtr_memo_contains_page(mtr, inode, MTR_MEMO_PAGE_SX_FIX));
1855 ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE);
1856 return(mach_read_from_4(inode + FSEG_FRAG_ARR
1857 + n * FSEG_FRAG_SLOT_SIZE));
1858}
1859
1860/**********************************************************************//**
1861Sets the page number in the nth fragment page slot. */
1862UNIV_INLINE
1863void
1864fseg_set_nth_frag_page_no(
1865/*======================*/
1866 fseg_inode_t* inode, /*!< in: segment inode */
1867 ulint n, /*!< in: slot index */
1868 ulint page_no,/*!< in: page number to set */
1869 mtr_t* mtr) /*!< in/out: mini-transaction */
1870{
1871 ut_ad(inode && mtr);
1872 ut_ad(n < FSEG_FRAG_ARR_N_SLOTS);
1873 ut_ad(mtr_memo_contains_page(mtr, inode, MTR_MEMO_PAGE_SX_FIX));
1874 ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE);
1875
1876 mlog_write_ulint(inode + FSEG_FRAG_ARR + n * FSEG_FRAG_SLOT_SIZE,
1877 page_no, MLOG_4BYTES, mtr);
1878}
1879
1880/**********************************************************************//**
1881Finds a fragment page slot which is free.
1882@return slot index; ULINT_UNDEFINED if none found */
1883static
1884ulint
1885fseg_find_free_frag_page_slot(
1886/*==========================*/
1887 fseg_inode_t* inode, /*!< in: segment inode */
1888 mtr_t* mtr) /*!< in/out: mini-transaction */
1889{
1890 ulint i;
1891 ulint page_no;
1892
1893 ut_ad(inode && mtr);
1894
1895 for (i = 0; i < FSEG_FRAG_ARR_N_SLOTS; i++) {
1896 page_no = fseg_get_nth_frag_page_no(inode, i, mtr);
1897
1898 if (page_no == FIL_NULL) {
1899
1900 return(i);
1901 }
1902 }
1903
1904 return(ULINT_UNDEFINED);
1905}
1906
1907/**********************************************************************//**
1908Finds a fragment page slot which is used and last in the array.
1909@return slot index; ULINT_UNDEFINED if none found */
1910static
1911ulint
1912fseg_find_last_used_frag_page_slot(
1913/*===============================*/
1914 fseg_inode_t* inode, /*!< in: segment inode */
1915 mtr_t* mtr) /*!< in/out: mini-transaction */
1916{
1917 ulint i;
1918 ulint page_no;
1919
1920 ut_ad(inode && mtr);
1921
1922 for (i = 0; i < FSEG_FRAG_ARR_N_SLOTS; i++) {
1923 page_no = fseg_get_nth_frag_page_no(
1924 inode, FSEG_FRAG_ARR_N_SLOTS - i - 1, mtr);
1925
1926 if (page_no != FIL_NULL) {
1927
1928 return(FSEG_FRAG_ARR_N_SLOTS - i - 1);
1929 }
1930 }
1931
1932 return(ULINT_UNDEFINED);
1933}
1934
1935/**********************************************************************//**
1936Calculates reserved fragment page slots.
1937@return number of fragment pages */
1938static
1939ulint
1940fseg_get_n_frag_pages(
1941/*==================*/
1942 fseg_inode_t* inode, /*!< in: segment inode */
1943 mtr_t* mtr) /*!< in/out: mini-transaction */
1944{
1945 ulint i;
1946 ulint count = 0;
1947
1948 ut_ad(inode && mtr);
1949
1950 for (i = 0; i < FSEG_FRAG_ARR_N_SLOTS; i++) {
1951 if (FIL_NULL != fseg_get_nth_frag_page_no(inode, i, mtr)) {
1952 count++;
1953 }
1954 }
1955
1956 return(count);
1957}
1958
1959/**********************************************************************//**
1960Creates a new segment.
1961@return the block where the segment header is placed, x-latched, NULL
1962if could not create segment because of lack of space */
1963buf_block_t*
1964fseg_create(
1965 fil_space_t* space, /*!< in,out: tablespace */
1966 ulint page, /*!< in: page where the segment header is placed: if
1967 this is != 0, the page must belong to another segment,
1968 if this is 0, a new page will be allocated and it
1969 will belong to the created segment */
1970 ulint byte_offset, /*!< in: byte offset of the created segment header
1971 on the page */
1972 mtr_t* mtr,
1973 bool has_done_reservation) /*!< in: whether the caller
1974 has already done the reservation for the pages with
1975 fsp_reserve_free_extents (at least 2 extents: one for
1976 the inode and the other for the segment) then there is
1977 no need to do the check for this individual
1978 operation */
1979{
1980 fsp_header_t* space_header;
1981 fseg_inode_t* inode;
1982 ib_id_t seg_id;
1983 buf_block_t* block = 0; /* remove warning */
1984 fseg_header_t* header = 0; /* remove warning */
1985 ulint n_reserved;
1986 ulint i;
1987
1988 DBUG_ENTER("fseg_create");
1989
1990 ut_ad(mtr);
1991 ut_ad(byte_offset + FSEG_HEADER_SIZE
1992 <= srv_page_size - FIL_PAGE_DATA_END);
1993
1994 mtr_x_lock(&space->latch, mtr);
1995 const page_size_t page_size(space->flags);
1996 ut_d(fsp_space_modify_check(space, mtr));
1997
1998 if (page != 0) {
1999 block = buf_page_get(page_id_t(space->id, page), page_size,
2000 RW_SX_LATCH, mtr);
2001
2002 header = byte_offset + buf_block_get_frame(block);
2003
2004 const ulint type = space->id == TRX_SYS_SPACE
2005 && page == TRX_SYS_PAGE_NO
2006 ? FIL_PAGE_TYPE_TRX_SYS
2007 : FIL_PAGE_TYPE_SYS;
2008
2009 fil_block_check_type(block, type, mtr);
2010 }
2011
2012 if (!has_done_reservation
2013 && !fsp_reserve_free_extents(&n_reserved, space, 2,
2014 FSP_NORMAL, mtr)) {
2015 DBUG_RETURN(NULL);
2016 }
2017
2018 space_header = fsp_get_space_header(space, page_size, mtr);
2019
2020 inode = fsp_alloc_seg_inode(space, space_header, mtr);
2021
2022 if (inode == NULL) {
2023 goto funct_exit;
2024 }
2025
2026 /* Read the next segment id from space header and increment the
2027 value in space header */
2028
2029 seg_id = mach_read_from_8(space_header + FSP_SEG_ID);
2030
2031 mlog_write_ull(space_header + FSP_SEG_ID, seg_id + 1, mtr);
2032
2033 mlog_write_ull(inode + FSEG_ID, seg_id, mtr);
2034 mlog_write_ulint(inode + FSEG_NOT_FULL_N_USED, 0, MLOG_4BYTES, mtr);
2035
2036 flst_init(inode + FSEG_FREE, mtr);
2037 flst_init(inode + FSEG_NOT_FULL, mtr);
2038 flst_init(inode + FSEG_FULL, mtr);
2039
2040 mlog_write_ulint(inode + FSEG_MAGIC_N, FSEG_MAGIC_N_VALUE,
2041 MLOG_4BYTES, mtr);
2042 for (i = 0; i < FSEG_FRAG_ARR_N_SLOTS; i++) {
2043 fseg_set_nth_frag_page_no(inode, i, FIL_NULL, mtr);
2044 }
2045
2046 if (page == 0) {
2047 block = fseg_alloc_free_page_low(space, page_size,
2048 inode, 0, FSP_UP, RW_SX_LATCH,
2049 mtr, mtr
2050#ifdef UNIV_DEBUG
2051 , has_done_reservation
2052#endif /* UNIV_DEBUG */
2053 );
2054
2055 /* The allocation cannot fail if we have already reserved a
2056 space for the page. */
2057 ut_ad(!has_done_reservation || block != NULL);
2058
2059 if (block == NULL) {
2060
2061 fsp_free_seg_inode(space, page_size, inode, mtr);
2062
2063 goto funct_exit;
2064 }
2065
2066 ut_ad(rw_lock_get_sx_lock_count(&block->lock) == 1);
2067
2068 header = byte_offset + buf_block_get_frame(block);
2069 mlog_write_ulint(buf_block_get_frame(block) + FIL_PAGE_TYPE,
2070 FIL_PAGE_TYPE_SYS, MLOG_2BYTES, mtr);
2071 }
2072
2073 mlog_write_ulint(header + FSEG_HDR_OFFSET,
2074 page_offset(inode), MLOG_2BYTES, mtr);
2075
2076 mlog_write_ulint(header + FSEG_HDR_PAGE_NO,
2077 page_get_page_no(page_align(inode)),
2078 MLOG_4BYTES, mtr);
2079
2080 mlog_write_ulint(header + FSEG_HDR_SPACE, space->id, MLOG_4BYTES, mtr);
2081
2082funct_exit:
2083 if (!has_done_reservation) {
2084 space->release_free_extents(n_reserved);
2085 }
2086
2087 DBUG_RETURN(block);
2088}
2089
2090/**********************************************************************//**
2091Calculates the number of pages reserved by a segment, and how many pages are
2092currently used.
2093@return number of reserved pages */
2094static
2095ulint
2096fseg_n_reserved_pages_low(
2097/*======================*/
2098 fseg_inode_t* inode, /*!< in: segment inode */
2099 ulint* used, /*!< out: number of pages used (not
2100 more than reserved) */
2101 mtr_t* mtr) /*!< in/out: mini-transaction */
2102{
2103 ulint ret;
2104
2105 ut_ad(inode && used && mtr);
2106 ut_ad(mtr_memo_contains_page(mtr, inode, MTR_MEMO_PAGE_SX_FIX));
2107
2108 *used = mach_read_from_4(inode + FSEG_NOT_FULL_N_USED)
2109 + FSP_EXTENT_SIZE * flst_get_len(inode + FSEG_FULL)
2110 + fseg_get_n_frag_pages(inode, mtr);
2111
2112 ret = fseg_get_n_frag_pages(inode, mtr)
2113 + FSP_EXTENT_SIZE * flst_get_len(inode + FSEG_FREE)
2114 + FSP_EXTENT_SIZE * flst_get_len(inode + FSEG_NOT_FULL)
2115 + FSP_EXTENT_SIZE * flst_get_len(inode + FSEG_FULL);
2116
2117 return(ret);
2118}
2119
2120/**********************************************************************//**
2121Calculates the number of pages reserved by a segment, and how many pages are
2122currently used.
2123@return number of reserved pages */
2124ulint
2125fseg_n_reserved_pages(
2126/*==================*/
2127 fseg_header_t* header, /*!< in: segment header */
2128 ulint* used, /*!< out: number of pages used (<= reserved) */
2129 mtr_t* mtr) /*!< in/out: mini-transaction */
2130{
2131 ulint ret;
2132 fseg_inode_t* inode;
2133 ulint space_id;
2134 fil_space_t* space;
2135
2136 space_id = page_get_space_id(page_align(header));
2137 space = mtr_x_lock_space(space_id, mtr);
2138
2139 const page_size_t page_size(space->flags);
2140
2141 inode = fseg_inode_get(header, space_id, page_size, mtr);
2142
2143 ret = fseg_n_reserved_pages_low(inode, used, mtr);
2144
2145 return(ret);
2146}
2147
2148/** Tries to fill the free list of a segment with consecutive free extents.
2149This happens if the segment is big enough to allow extents in the free list,
2150the free list is empty, and the extents can be allocated consecutively from
2151the hint onward.
2152@param[in] inode segment inode
2153@param[in] space tablespace
2154@param[in] page_size page size
2155@param[in] hint hint which extent would be good as the first
2156extent
2157@param[in,out] mtr mini-transaction */
2158static
2159void
2160fseg_fill_free_list(
2161 fseg_inode_t* inode,
2162 fil_space_t* space,
2163 const page_size_t& page_size,
2164 ulint hint,
2165 mtr_t* mtr)
2166{
2167 xdes_t* descr;
2168 ulint i;
2169 ib_id_t seg_id;
2170 ulint reserved;
2171 ulint used;
2172
2173 ut_ad(inode && mtr);
2174 ut_ad(!((page_offset(inode) - FSEG_ARR_OFFSET) % FSEG_INODE_SIZE));
2175 ut_d(fsp_space_modify_check(space, mtr));
2176
2177 reserved = fseg_n_reserved_pages_low(inode, &used, mtr);
2178
2179 if (reserved < FSEG_FREE_LIST_LIMIT * FSP_EXTENT_SIZE) {
2180
2181 /* The segment is too small to allow extents in free list */
2182
2183 return;
2184 }
2185
2186 if (flst_get_len(inode + FSEG_FREE) > 0) {
2187 /* Free list is not empty */
2188
2189 return;
2190 }
2191
2192 for (i = 0; i < FSEG_FREE_LIST_MAX_LEN; i++) {
2193 descr = xdes_get_descriptor(space, hint, page_size, mtr);
2194
2195 if ((descr == NULL)
2196 || (XDES_FREE != xdes_get_state(descr, mtr))) {
2197
2198 /* We cannot allocate the desired extent: stop */
2199
2200 return;
2201 }
2202
2203 descr = fsp_alloc_free_extent(space, page_size, hint, mtr);
2204
2205 xdes_set_state(descr, XDES_FSEG, mtr);
2206
2207 seg_id = mach_read_from_8(inode + FSEG_ID);
2208 ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N)
2209 == FSEG_MAGIC_N_VALUE);
2210 mlog_write_ull(descr + XDES_ID, seg_id, mtr);
2211
2212 flst_add_last(inode + FSEG_FREE, descr + XDES_FLST_NODE, mtr);
2213 hint += FSP_EXTENT_SIZE;
2214 }
2215}
2216
2217/** Allocates a free extent for the segment: looks first in the free list of
2218the segment, then tries to allocate from the space free list.
2219NOTE that the extent returned still resides in the segment free list, it is
2220not yet taken off it!
2221@param[in] inode segment inode
2222@param[in,out] space tablespace
2223@param[in] page_size page size
2224@param[in,out] mtr mini-transaction
2225@retval NULL if no page could be allocated
2226@retval block rw_lock_x_lock_count(&block->lock) == 1 if allocation succeeded
2227(init_mtr == mtr, or the page was not previously freed in mtr)
2228@retval block (not allocated or initialized) otherwise */
2229static
2230xdes_t*
2231fseg_alloc_free_extent(
2232 fseg_inode_t* inode,
2233 fil_space_t* space,
2234 const page_size_t& page_size,
2235 mtr_t* mtr)
2236{
2237 xdes_t* descr;
2238 ib_id_t seg_id;
2239 fil_addr_t first;
2240
2241 ut_ad(!((page_offset(inode) - FSEG_ARR_OFFSET) % FSEG_INODE_SIZE));
2242 ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE);
2243 ut_d(fsp_space_modify_check(space, mtr));
2244
2245 if (flst_get_len(inode + FSEG_FREE) > 0) {
2246 /* Segment free list is not empty, allocate from it */
2247
2248 first = flst_get_first(inode + FSEG_FREE, mtr);
2249
2250 descr = xdes_lst_get_descriptor(space, page_size, first, mtr);
2251 } else {
2252 /* Segment free list was empty, allocate from space */
2253 descr = fsp_alloc_free_extent(space, page_size, 0, mtr);
2254
2255 if (descr == NULL) {
2256
2257 return(NULL);
2258 }
2259
2260 seg_id = mach_read_from_8(inode + FSEG_ID);
2261
2262 xdes_set_state(descr, XDES_FSEG, mtr);
2263 mlog_write_ull(descr + XDES_ID, seg_id, mtr);
2264 flst_add_last(inode + FSEG_FREE, descr + XDES_FLST_NODE, mtr);
2265
2266 /* Try to fill the segment free list */
2267 fseg_fill_free_list(inode, space, page_size,
2268 xdes_get_offset(descr) + FSP_EXTENT_SIZE,
2269 mtr);
2270 }
2271
2272 return(descr);
2273}
2274
2275/** Allocates a single free page from a segment.
2276This function implements the intelligent allocation strategy which tries to
2277minimize file space fragmentation.
2278@param[in,out] space tablespace
2279@param[in] page_size page size
2280@param[in,out] seg_inode segment inode
2281@param[in] hint hint of which page would be desirable
2282@param[in] direction if the new page is needed because of
2283an index page split, and records are inserted there in order, into which
2284direction they go alphabetically: FSP_DOWN, FSP_UP, FSP_NO_DIR
2285@param[in] rw_latch RW_SX_LATCH, RW_X_LATCH
2286@param[in,out] mtr mini-transaction
2287@param[in,out] init_mtr mtr or another mini-transaction in
2288which the page should be initialized. If init_mtr != mtr, but the page is
2289already latched in mtr, do not initialize the page
2290@param[in] has_done_reservation TRUE if the space has already been
2291reserved, in this case we will never return NULL
2292@retval NULL if no page could be allocated
2293@retval block rw_lock_x_lock_count(&block->lock) == 1 if allocation succeeded
2294(init_mtr == mtr, or the page was not previously freed in mtr)
2295@retval block (not allocated or initialized) otherwise */
2296static
2297buf_block_t*
2298fseg_alloc_free_page_low(
2299 fil_space_t* space,
2300 const page_size_t& page_size,
2301 fseg_inode_t* seg_inode,
2302 ulint hint,
2303 byte direction,
2304 rw_lock_type_t rw_latch,
2305 mtr_t* mtr,
2306 mtr_t* init_mtr
2307#ifdef UNIV_DEBUG
2308 , ibool has_done_reservation
2309#endif /* UNIV_DEBUG */
2310)
2311{
2312 fsp_header_t* space_header;
2313 ib_id_t seg_id;
2314 ulint used;
2315 ulint reserved;
2316 xdes_t* descr; /*!< extent of the hinted page */
2317 ulint ret_page; /*!< the allocated page offset, FIL_NULL
2318 if could not be allocated */
2319 xdes_t* ret_descr; /*!< the extent of the allocated page */
2320 ulint n;
2321 const ulint space_id = space->id;
2322
2323 ut_ad((direction >= FSP_UP) && (direction <= FSP_NO_DIR));
2324 ut_ad(mach_read_from_4(seg_inode + FSEG_MAGIC_N)
2325 == FSEG_MAGIC_N_VALUE);
2326 ut_ad(!((page_offset(seg_inode) - FSEG_ARR_OFFSET) % FSEG_INODE_SIZE));
2327 seg_id = mach_read_from_8(seg_inode + FSEG_ID);
2328
2329 ut_ad(seg_id);
2330 ut_d(fsp_space_modify_check(space, mtr));
2331 ut_ad(fil_page_get_type(page_align(seg_inode)) == FIL_PAGE_INODE);
2332
2333 reserved = fseg_n_reserved_pages_low(seg_inode, &used, mtr);
2334
2335 space_header = fsp_get_space_header(space, page_size, mtr);
2336
2337 descr = xdes_get_descriptor_with_space_hdr(space_header, space,
2338 hint, mtr);
2339 if (descr == NULL) {
2340 /* Hint outside space or too high above free limit: reset
2341 hint */
2342 /* The file space header page is always allocated. */
2343 hint = 0;
2344 descr = xdes_get_descriptor(space, hint, page_size, mtr);
2345 }
2346
2347 /* In the big if-else below we look for ret_page and ret_descr */
2348 /*-------------------------------------------------------------*/
2349 if ((xdes_get_state(descr, mtr) == XDES_FSEG)
2350 && mach_read_from_8(descr + XDES_ID) == seg_id
2351 && (xdes_mtr_get_bit(descr, XDES_FREE_BIT,
2352 hint % FSP_EXTENT_SIZE, mtr) == TRUE)) {
2353take_hinted_page:
2354 /* 1. We can take the hinted page
2355 =================================*/
2356 ret_descr = descr;
2357 ret_page = hint;
2358 /* Skip the check for extending the tablespace. If the
2359 page hint were not within the size of the tablespace,
2360 we would have got (descr == NULL) above and reset the hint. */
2361 goto got_hinted_page;
2362 /*-----------------------------------------------------------*/
2363 } else if (xdes_get_state(descr, mtr) == XDES_FREE
2364 && reserved - used < reserved / FSEG_FILLFACTOR
2365 && used >= FSEG_FRAG_LIMIT) {
2366
2367 /* 2. We allocate the free extent from space and can take
2368 =========================================================
2369 the hinted page
2370 ===============*/
2371 ret_descr = fsp_alloc_free_extent(space, page_size, hint, mtr);
2372
2373 ut_a(ret_descr == descr);
2374
2375 xdes_set_state(ret_descr, XDES_FSEG, mtr);
2376 mlog_write_ull(ret_descr + XDES_ID, seg_id, mtr);
2377 flst_add_last(seg_inode + FSEG_FREE,
2378 ret_descr + XDES_FLST_NODE, mtr);
2379
2380 /* Try to fill the segment free list */
2381 fseg_fill_free_list(seg_inode, space, page_size,
2382 hint + FSP_EXTENT_SIZE, mtr);
2383 goto take_hinted_page;
2384 /*-----------------------------------------------------------*/
2385 } else if ((direction != FSP_NO_DIR)
2386 && ((reserved - used) < reserved / FSEG_FILLFACTOR)
2387 && (used >= FSEG_FRAG_LIMIT)
2388 && (!!(ret_descr
2389 = fseg_alloc_free_extent(
2390 seg_inode, space, page_size, mtr)))) {
2391
2392 /* 3. We take any free extent (which was already assigned above
2393 ===============================================================
2394 in the if-condition to ret_descr) and take the lowest or
2395 ========================================================
2396 highest page in it, depending on the direction
2397 ==============================================*/
2398 ret_page = xdes_get_offset(ret_descr);
2399
2400 if (direction == FSP_DOWN) {
2401 ret_page += FSP_EXTENT_SIZE - 1;
2402 }
2403 ut_ad(!has_done_reservation || ret_page != FIL_NULL);
2404 /*-----------------------------------------------------------*/
2405 } else if ((xdes_get_state(descr, mtr) == XDES_FSEG)
2406 && mach_read_from_8(descr + XDES_ID) == seg_id
2407 && (!xdes_is_full(descr, mtr))) {
2408
2409 /* 4. We can take the page from the same extent as the
2410 ======================================================
2411 hinted page (and the extent already belongs to the
2412 ==================================================
2413 segment)
2414 ========*/
2415 ret_descr = descr;
2416 ret_page = xdes_get_offset(ret_descr)
2417 + xdes_find_bit(ret_descr, XDES_FREE_BIT, TRUE,
2418 hint % FSP_EXTENT_SIZE, mtr);
2419 ut_ad(!has_done_reservation || ret_page != FIL_NULL);
2420 /*-----------------------------------------------------------*/
2421 } else if (reserved - used > 0) {
2422 /* 5. We take any unused page from the segment
2423 ==============================================*/
2424 fil_addr_t first;
2425
2426 if (flst_get_len(seg_inode + FSEG_NOT_FULL) > 0) {
2427 first = flst_get_first(seg_inode + FSEG_NOT_FULL,
2428 mtr);
2429 } else if (flst_get_len(seg_inode + FSEG_FREE) > 0) {
2430 first = flst_get_first(seg_inode + FSEG_FREE, mtr);
2431 } else {
2432 ut_ad(!has_done_reservation);
2433 return(NULL);
2434 }
2435
2436 ret_descr = xdes_lst_get_descriptor(space, page_size,
2437 first, mtr);
2438 ret_page = xdes_get_offset(ret_descr)
2439 + xdes_find_bit(ret_descr, XDES_FREE_BIT, TRUE,
2440 0, mtr);
2441 ut_ad(!has_done_reservation || ret_page != FIL_NULL);
2442 /*-----------------------------------------------------------*/
2443 } else if (used < FSEG_FRAG_LIMIT) {
2444 /* 6. We allocate an individual page from the space
2445 ===================================================*/
2446 buf_block_t* block = fsp_alloc_free_page(
2447 space, page_size, hint, rw_latch, mtr, init_mtr);
2448
2449 ut_ad(!has_done_reservation || block != NULL);
2450
2451 if (block != NULL) {
2452 /* Put the page in the fragment page array of the
2453 segment */
2454 n = fseg_find_free_frag_page_slot(seg_inode, mtr);
2455 ut_a(n != ULINT_UNDEFINED);
2456
2457 fseg_set_nth_frag_page_no(
2458 seg_inode, n, block->page.id.page_no(),
2459 mtr);
2460 }
2461
2462 /* fsp_alloc_free_page() invoked fsp_init_file_page()
2463 already. */
2464 return(block);
2465 /*-----------------------------------------------------------*/
2466 } else {
2467 /* 7. We allocate a new extent and take its first page
2468 ======================================================*/
2469 ret_descr = fseg_alloc_free_extent(seg_inode,
2470 space, page_size, mtr);
2471
2472 if (ret_descr == NULL) {
2473 ret_page = FIL_NULL;
2474 ut_ad(!has_done_reservation);
2475 } else {
2476 ret_page = xdes_get_offset(ret_descr);
2477 ut_ad(!has_done_reservation || ret_page != FIL_NULL);
2478 }
2479 }
2480
2481 if (ret_page == FIL_NULL) {
2482 /* Page could not be allocated */
2483
2484 ut_ad(!has_done_reservation);
2485 return(NULL);
2486 }
2487
2488 if (space->size <= ret_page && !is_system_tablespace(space_id)) {
2489 /* It must be that we are extending a single-table
2490 tablespace whose size is still < 64 pages */
2491
2492 if (ret_page >= FSP_EXTENT_SIZE) {
2493 ib::error() << "Error (2): trying to extend"
2494 " a single-table tablespace " << space_id
2495 << " by single page(s) though the"
2496 << " space size " << space->size
2497 << ". Page no " << ret_page << ".";
2498 ut_ad(!has_done_reservation);
2499 return(NULL);
2500 }
2501
2502 if (!fsp_try_extend_data_file_with_pages(
2503 space, ret_page, space_header, mtr)) {
2504 /* No disk space left */
2505 ut_ad(!has_done_reservation);
2506 return(NULL);
2507 }
2508 }
2509
2510got_hinted_page:
2511 /* ret_descr == NULL if the block was allocated from free_frag
2512 (XDES_FREE_FRAG) */
2513 if (ret_descr != NULL) {
2514 /* At this point we know the extent and the page offset.
2515 The extent is still in the appropriate list (FSEG_NOT_FULL
2516 or FSEG_FREE), and the page is not yet marked as used. */
2517
2518 ut_ad(xdes_get_descriptor(space, ret_page, page_size, mtr)
2519 == ret_descr);
2520
2521 ut_ad(xdes_mtr_get_bit(
2522 ret_descr, XDES_FREE_BIT,
2523 ret_page % FSP_EXTENT_SIZE, mtr));
2524
2525 fseg_mark_page_used(seg_inode, ret_page, ret_descr, mtr);
2526 }
2527
2528 return(fsp_page_create(space, ret_page, page_size, rw_latch,
2529 mtr, init_mtr));
2530}
2531
2532/**********************************************************************//**
2533Allocates a single free page from a segment. This function implements
2534the intelligent allocation strategy which tries to minimize file space
2535fragmentation.
2536@retval NULL if no page could be allocated
2537@retval block, rw_lock_x_lock_count(&block->lock) == 1 if allocation succeeded
2538(init_mtr == mtr, or the page was not previously freed in mtr)
2539@retval block (not allocated or initialized) otherwise */
2540buf_block_t*
2541fseg_alloc_free_page_general(
2542/*=========================*/
2543 fseg_header_t* seg_header,/*!< in/out: segment header */
2544 ulint hint, /*!< in: hint of which page would be
2545 desirable */
2546 byte direction,/*!< in: if the new page is needed because
2547 of an index page split, and records are
2548 inserted there in order, into which
2549 direction they go alphabetically: FSP_DOWN,
2550 FSP_UP, FSP_NO_DIR */
2551 ibool has_done_reservation, /*!< in: TRUE if the caller has
2552 already done the reservation for the page
2553 with fsp_reserve_free_extents, then there
2554 is no need to do the check for this individual
2555 page */
2556 mtr_t* mtr, /*!< in/out: mini-transaction */
2557 mtr_t* init_mtr)/*!< in/out: mtr or another mini-transaction
2558 in which the page should be initialized.
2559 If init_mtr!=mtr, but the page is already
2560 latched in mtr, do not initialize the page. */
2561{
2562 fseg_inode_t* inode;
2563 ulint space_id;
2564 fil_space_t* space;
2565 buf_block_t* iblock;
2566 buf_block_t* block;
2567 ulint n_reserved;
2568
2569 space_id = page_get_space_id(page_align(seg_header));
2570 space = mtr_x_lock_space(space_id, mtr);
2571 const page_size_t page_size(space->flags);
2572
2573 inode = fseg_inode_get(seg_header, space_id, page_size, mtr, &iblock);
2574 fil_block_check_type(iblock, FIL_PAGE_INODE, mtr);
2575
2576 if (!has_done_reservation
2577 && !fsp_reserve_free_extents(&n_reserved, space, 2,
2578 FSP_NORMAL, mtr)) {
2579 return(NULL);
2580 }
2581
2582 block = fseg_alloc_free_page_low(space, page_size,
2583 inode, hint, direction,
2584 RW_X_LATCH, mtr, init_mtr
2585#ifdef UNIV_DEBUG
2586 , has_done_reservation
2587#endif /* UNIV_DEBUG */
2588 );
2589
2590 /* The allocation cannot fail if we have already reserved a
2591 space for the page. */
2592 ut_ad(!has_done_reservation || block != NULL);
2593
2594 if (!has_done_reservation) {
2595 space->release_free_extents(n_reserved);
2596 }
2597
2598 return(block);
2599}
2600
2601/** Check that we have at least n_pages frag pages free in the first extent
2602of a single-table tablespace, and they are also physically initialized to
2603the data file. That is we have already extended the data file so that those
2604pages are inside the data file. If not, this function extends the tablespace
2605with pages.
2606@param[in,out] space tablespace
2607@param[in,out] space_header tablespace header, x-latched
2608@param[in] size size of the tablespace in pages,
2609must be less than FSP_EXTENT_SIZE
2610@param[in,out] mtr mini-transaction
2611@param[in] n_pages number of pages to reserve
2612@return true if there were at least n_pages free pages, or we were able
2613to extend */
2614static
2615bool
2616fsp_reserve_free_pages(
2617 fil_space_t* space,
2618 fsp_header_t* space_header,
2619 ulint size,
2620 mtr_t* mtr,
2621 ulint n_pages)
2622{
2623 xdes_t* descr;
2624 ulint n_used;
2625
2626 ut_a(!is_system_tablespace(space->id));
2627 ut_a(size < FSP_EXTENT_SIZE);
2628
2629 descr = xdes_get_descriptor_with_space_hdr(
2630 space_header, space, 0, mtr);
2631 n_used = xdes_get_n_used(descr, mtr);
2632
2633 ut_a(n_used <= size);
2634
2635 return(size >= n_used + n_pages
2636 || fsp_try_extend_data_file_with_pages(
2637 space, n_used + n_pages - 1, space_header, mtr));
2638}
2639
2640/** Reserves free pages from a tablespace. All mini-transactions which may
2641use several pages from the tablespace should call this function beforehand
2642and reserve enough free extents so that they certainly will be able
2643to do their operation, like a B-tree page split, fully. Reservations
2644must be released with function fil_space_t::release_free_extents()!
2645
2646The alloc_type below has the following meaning: FSP_NORMAL means an
2647operation which will probably result in more space usage, like an
2648insert in a B-tree; FSP_UNDO means allocation to undo logs: if we are
2649deleting rows, then this allocation will in the long run result in
2650less space usage (after a purge); FSP_CLEANING means allocation done
2651in a physical record delete (like in a purge) or other cleaning operation
2652which will result in less space usage in the long run. We prefer the latter
2653two types of allocation: when space is scarce, FSP_NORMAL allocations
2654will not succeed, but the latter two allocations will succeed, if possible.
2655The purpose is to avoid dead end where the database is full but the
2656user cannot free any space because these freeing operations temporarily
2657reserve some space.
2658
2659Single-table tablespaces whose size is < FSP_EXTENT_SIZE pages are a special
2660case. In this function we would liberally reserve several extents for
2661every page split or merge in a B-tree. But we do not want to waste disk space
2662if the table only occupies < FSP_EXTENT_SIZE pages. That is why we apply
2663different rules in that special case, just ensuring that there are n_pages
2664free pages available.
2665
2666@param[out] n_reserved number of extents actually reserved; if we
2667 return true and the tablespace size is <
2668 FSP_EXTENT_SIZE pages, then this can be 0,
2669 otherwise it is n_ext
2670@param[in,out] space tablespace
2671@param[in] n_ext number of extents to reserve
2672@param[in] alloc_type page reservation type (FSP_BLOB, etc)
2673@param[in,out] mtr the mini transaction
2674@param[in] n_pages for small tablespaces (tablespace size is
2675 less than FSP_EXTENT_SIZE), number of free
2676 pages to reserve.
2677@return true if we were able to make the reservation */
2678bool
2679fsp_reserve_free_extents(
2680 ulint* n_reserved,
2681 fil_space_t* space,
2682 ulint n_ext,
2683 fsp_reserve_t alloc_type,
2684 mtr_t* mtr,
2685 ulint n_pages)
2686{
2687 fsp_header_t* space_header;
2688 ulint n_free_list_ext;
2689 ulint free_limit;
2690 ulint size;
2691 ulint n_free;
2692 ulint n_free_up;
2693 ulint reserve;
2694 size_t total_reserved = 0;
2695
2696 ut_ad(mtr);
2697 *n_reserved = n_ext;
2698
2699 mtr_x_lock(&space->latch, mtr);
2700 const page_size_t page_size(space->flags);
2701
2702 space_header = fsp_get_space_header(space, page_size, mtr);
2703try_again:
2704 size = mach_read_from_4(space_header + FSP_SIZE);
2705 ut_ad(size == space->size_in_header);
2706
2707 if (size < FSP_EXTENT_SIZE && n_pages < FSP_EXTENT_SIZE / 2) {
2708 /* Use different rules for small single-table tablespaces */
2709 *n_reserved = 0;
2710 return(fsp_reserve_free_pages(space, space_header, size,
2711 mtr, n_pages));
2712 }
2713
2714 n_free_list_ext = flst_get_len(space_header + FSP_FREE);
2715 ut_ad(space->free_len == n_free_list_ext);
2716
2717 free_limit = mtr_read_ulint(space_header + FSP_FREE_LIMIT,
2718 MLOG_4BYTES, mtr);
2719 ut_ad(space->free_limit == free_limit);
2720
2721 /* Below we play safe when counting free extents above the free limit:
2722 some of them will contain extent descriptor pages, and therefore
2723 will not be free extents */
2724
2725 if (size >= free_limit) {
2726 n_free_up = (size - free_limit) / FSP_EXTENT_SIZE;
2727 } else {
2728 ut_ad(alloc_type == FSP_BLOB);
2729 n_free_up = 0;
2730 }
2731
2732 if (n_free_up > 0) {
2733 n_free_up--;
2734 n_free_up -= n_free_up / (page_size.physical()
2735 / FSP_EXTENT_SIZE);
2736 }
2737
2738 n_free = n_free_list_ext + n_free_up;
2739
2740 switch (alloc_type) {
2741 case FSP_NORMAL:
2742 /* We reserve 1 extent + 0.5 % of the space size to undo logs
2743 and 1 extent + 0.5 % to cleaning operations; NOTE: this source
2744 code is duplicated in the function below! */
2745
2746 reserve = 2 + ((size / FSP_EXTENT_SIZE) * 2) / 200;
2747
2748 if (n_free <= reserve + n_ext) {
2749
2750 goto try_to_extend;
2751 }
2752 break;
2753 case FSP_UNDO:
2754 /* We reserve 0.5 % of the space size to cleaning operations */
2755
2756 reserve = 1 + ((size / FSP_EXTENT_SIZE) * 1) / 200;
2757
2758 if (n_free <= reserve + n_ext) {
2759
2760 goto try_to_extend;
2761 }
2762 break;
2763 case FSP_CLEANING:
2764 case FSP_BLOB:
2765 reserve = 0;
2766 break;
2767 default:
2768 ut_error;
2769 }
2770
2771 if (space->reserve_free_extents(n_free, n_ext)) {
2772 return(true);
2773 }
2774try_to_extend:
2775 if (ulint n = fsp_try_extend_data_file(space, space_header, mtr)) {
2776 total_reserved += n;
2777 goto try_again;
2778 }
2779
2780 return(false);
2781}
2782
2783/********************************************************************//**
2784Marks a page used. The page must reside within the extents of the given
2785segment. */
2786static MY_ATTRIBUTE((nonnull))
2787void
2788fseg_mark_page_used(
2789/*================*/
2790 fseg_inode_t* seg_inode,/*!< in: segment inode */
2791 ulint page, /*!< in: page offset */
2792 xdes_t* descr, /*!< in: extent descriptor */
2793 mtr_t* mtr) /*!< in/out: mini-transaction */
2794{
2795 ulint not_full_n_used;
2796
2797 ut_ad(fil_page_get_type(page_align(seg_inode)) == FIL_PAGE_INODE);
2798 ut_ad(!((page_offset(seg_inode) - FSEG_ARR_OFFSET) % FSEG_INODE_SIZE));
2799 ut_ad(mach_read_from_4(seg_inode + FSEG_MAGIC_N)
2800 == FSEG_MAGIC_N_VALUE);
2801
2802 ut_ad(mtr_read_ulint(seg_inode + FSEG_ID, MLOG_4BYTES, mtr)
2803 == mtr_read_ulint(descr + XDES_ID, MLOG_4BYTES, mtr));
2804
2805 if (xdes_is_free(descr, mtr)) {
2806 /* We move the extent from the free list to the
2807 NOT_FULL list */
2808 flst_remove(seg_inode + FSEG_FREE, descr + XDES_FLST_NODE,
2809 mtr);
2810 flst_add_last(seg_inode + FSEG_NOT_FULL,
2811 descr + XDES_FLST_NODE, mtr);
2812 }
2813
2814 ut_ad(xdes_mtr_get_bit(
2815 descr, XDES_FREE_BIT, page % FSP_EXTENT_SIZE, mtr));
2816
2817 /* We mark the page as used */
2818 xdes_set_bit(descr, XDES_FREE_BIT, page % FSP_EXTENT_SIZE, FALSE, mtr);
2819
2820 not_full_n_used = mtr_read_ulint(seg_inode + FSEG_NOT_FULL_N_USED,
2821 MLOG_4BYTES, mtr);
2822 not_full_n_used++;
2823 mlog_write_ulint(seg_inode + FSEG_NOT_FULL_N_USED, not_full_n_used,
2824 MLOG_4BYTES, mtr);
2825 if (xdes_is_full(descr, mtr)) {
2826 /* We move the extent from the NOT_FULL list to the
2827 FULL list */
2828 flst_remove(seg_inode + FSEG_NOT_FULL,
2829 descr + XDES_FLST_NODE, mtr);
2830 flst_add_last(seg_inode + FSEG_FULL,
2831 descr + XDES_FLST_NODE, mtr);
2832
2833 mlog_write_ulint(seg_inode + FSEG_NOT_FULL_N_USED,
2834 not_full_n_used - FSP_EXTENT_SIZE,
2835 MLOG_4BYTES, mtr);
2836 }
2837}
2838
2839/** Frees a single page of a segment.
2840@param[in] seg_inode segment inode
2841@param[in,out] space tablespace
2842@param[in] offset page number
2843@param[in] page_size page size
2844@param[in] ahi whether we may need to drop the adaptive
2845hash index
2846@param[in,out] mtr mini-transaction */
2847static
2848void
2849fseg_free_page_low(
2850 fseg_inode_t* seg_inode,
2851 fil_space_t* space,
2852 page_no_t offset,
2853 const page_size_t& page_size,
2854#ifdef BTR_CUR_HASH_ADAPT
2855 bool ahi,
2856#endif /* BTR_CUR_HASH_ADAPT */
2857 mtr_t* mtr)
2858{
2859 xdes_t* descr;
2860 ulint not_full_n_used;
2861 ulint state;
2862 ib_id_t descr_id;
2863 ib_id_t seg_id;
2864 ulint i;
2865
2866 ut_ad(seg_inode != NULL);
2867 ut_ad(mtr != NULL);
2868 ut_ad(mach_read_from_4(seg_inode + FSEG_MAGIC_N)
2869 == FSEG_MAGIC_N_VALUE);
2870 ut_ad(!((page_offset(seg_inode) - FSEG_ARR_OFFSET) % FSEG_INODE_SIZE));
2871 ut_d(fsp_space_modify_check(space, mtr));
2872#ifdef BTR_CUR_HASH_ADAPT
2873 /* Drop search system page hash index if the page is found in
2874 the pool and is hashed */
2875
2876 if (ahi) {
2877 btr_search_drop_page_hash_when_freed(
2878 page_id_t(space->id, offset));
2879 }
2880#endif /* BTR_CUR_HASH_ADAPT */
2881
2882 descr = xdes_get_descriptor(space, offset, page_size, mtr);
2883
2884 if (xdes_mtr_get_bit(descr, XDES_FREE_BIT,
2885 offset % FSP_EXTENT_SIZE, mtr)) {
2886 ib::fatal() << "InnoDB is trying to free page "
2887 << page_id_t(space->id, offset)
2888 << " though it is already marked as free in the"
2889 " tablespace! The tablespace free space info is"
2890 " corrupt. You may need to dump your tables and"
2891 " recreate the whole database!"
2892 << FORCE_RECOVERY_MSG;
2893 }
2894
2895 state = xdes_get_state(descr, mtr);
2896
2897 if (state != XDES_FSEG) {
2898 /* The page is in the fragment pages of the segment */
2899
2900 for (i = 0;; i++) {
2901 if (fseg_get_nth_frag_page_no(seg_inode, i, mtr)
2902 == offset) {
2903
2904 fseg_set_nth_frag_page_no(seg_inode, i,
2905 FIL_NULL, mtr);
2906 break;
2907 }
2908 }
2909
2910 fsp_free_page(space, offset, page_size, mtr);
2911
2912 return;
2913 }
2914
2915 /* If we get here, the page is in some extent of the segment */
2916
2917 descr_id = mach_read_from_8(descr + XDES_ID);
2918 seg_id = mach_read_from_8(seg_inode + FSEG_ID);
2919
2920 if (UNIV_UNLIKELY(descr_id != seg_id)) {
2921 fputs("InnoDB: Dump of the tablespace extent descriptor: ",
2922 stderr);
2923 ut_print_buf(stderr, descr, 40);
2924 fputs("\nInnoDB: Dump of the segment inode: ", stderr);
2925 ut_print_buf(stderr, seg_inode, 40);
2926 putc('\n', stderr);
2927
2928 ib::fatal() << "InnoDB is trying to free page "
2929 << page_id_t(space->id, offset)
2930 << ", which does not belong to segment " << descr_id
2931 << " but belongs to segment " << seg_id << "."
2932 << FORCE_RECOVERY_MSG;
2933 }
2934
2935 not_full_n_used = mtr_read_ulint(seg_inode + FSEG_NOT_FULL_N_USED,
2936 MLOG_4BYTES, mtr);
2937 if (xdes_is_full(descr, mtr)) {
2938 /* The fragment is full: move it to another list */
2939 flst_remove(seg_inode + FSEG_FULL,
2940 descr + XDES_FLST_NODE, mtr);
2941 flst_add_last(seg_inode + FSEG_NOT_FULL,
2942 descr + XDES_FLST_NODE, mtr);
2943 mlog_write_ulint(seg_inode + FSEG_NOT_FULL_N_USED,
2944 not_full_n_used + FSP_EXTENT_SIZE - 1,
2945 MLOG_4BYTES, mtr);
2946 } else {
2947 ut_a(not_full_n_used > 0);
2948 mlog_write_ulint(seg_inode + FSEG_NOT_FULL_N_USED,
2949 not_full_n_used - 1, MLOG_4BYTES, mtr);
2950 }
2951
2952 const ulint bit = offset % FSP_EXTENT_SIZE;
2953
2954 xdes_set_bit(descr, XDES_FREE_BIT, bit, TRUE, mtr);
2955 xdes_set_bit(descr, XDES_CLEAN_BIT, bit, TRUE, mtr);
2956
2957 if (xdes_is_free(descr, mtr)) {
2958 /* The extent has become free: free it to space */
2959 flst_remove(seg_inode + FSEG_NOT_FULL,
2960 descr + XDES_FLST_NODE, mtr);
2961 fsp_free_extent(space, offset, page_size, mtr);
2962 }
2963}
2964
2965#ifndef BTR_CUR_HASH_ADAPT
2966# define fseg_free_page_low(inode, space, offset, page_size, ahi, mtr) \
2967 fseg_free_page_low(inode, space, offset, page_size, mtr)
2968#endif /* !BTR_CUR_HASH_ADAPT */
2969
2970/**********************************************************************//**
2971Frees a single page of a segment. */
2972void
2973fseg_free_page_func(
2974 fseg_header_t* seg_header, /*!< in: segment header */
2975 ulint space_id,/*!< in: space id */
2976 ulint page, /*!< in: page offset */
2977#ifdef BTR_CUR_HASH_ADAPT
2978 bool ahi, /*!< in: whether we may need to drop
2979 the adaptive hash index */
2980#endif /* BTR_CUR_HASH_ADAPT */
2981 mtr_t* mtr) /*!< in/out: mini-transaction */
2982{
2983 DBUG_ENTER("fseg_free_page");
2984 fseg_inode_t* seg_inode;
2985 buf_block_t* iblock;
2986 fil_space_t* space = mtr_x_lock_space(space_id, mtr);
2987 const page_size_t page_size(space->flags);
2988
2989 DBUG_LOG("fseg_free_page", "space_id: " << space_id
2990 << ", page_no: " << page);
2991
2992 seg_inode = fseg_inode_get(seg_header, space_id, page_size, mtr,
2993 &iblock);
2994 fil_block_check_type(iblock, FIL_PAGE_INODE, mtr);
2995
2996 fseg_free_page_low(seg_inode, space, page, page_size, ahi, mtr);
2997
2998 ut_d(buf_page_set_file_page_was_freed(page_id_t(space_id, page)));
2999
3000 DBUG_VOID_RETURN;
3001}
3002
3003/** Determine whether a page is free.
3004@param[in,out] space tablespace
3005@param[in] page page number
3006@return whether the page is marked as free */
3007bool
3008fseg_page_is_free(fil_space_t* space, unsigned page)
3009{
3010 bool is_free;
3011 mtr_t mtr;
3012 page_size_t page_size(space->flags);
3013 page_no_t dpage = xdes_calc_descriptor_page(page_size, page);
3014
3015 mtr.start();
3016 mtr_s_lock(&space->latch, &mtr);
3017
3018 if (page >= space->free_limit || page >= space->size_in_header) {
3019 is_free = true;
3020 } else if (const xdes_t* descr = xdes_get_descriptor_const(
3021 space, dpage, page, page_size, &mtr)) {
3022 is_free = xdes_get_bit(descr, XDES_FREE_BIT,
3023 page % FSP_EXTENT_SIZE);
3024 } else {
3025 is_free = true;
3026 }
3027 mtr.commit();
3028
3029 return(is_free);
3030}
3031
3032/** Free an extent of a segment to the space free list.
3033@param[in,out] seg_inode segment inode
3034@param[in,out] space tablespace
3035@param[in] page_size page size
3036@param[in] page page number in the extent
3037@param[in] ahi whether we may need to drop
3038 the adaptive hash index
3039@param[in,out] mtr mini-transaction */
3040MY_ATTRIBUTE((nonnull))
3041static
3042void
3043fseg_free_extent(
3044 fseg_inode_t* seg_inode,
3045 fil_space_t* space,
3046 const page_size_t& page_size,
3047 ulint page,
3048#ifdef BTR_CUR_HASH_ADAPT
3049 bool ahi,
3050#endif /* BTR_CUR_HASH_ADAPT */
3051 mtr_t* mtr)
3052{
3053 ulint first_page_in_extent;
3054 xdes_t* descr;
3055 ulint not_full_n_used;
3056 ulint descr_n_used;
3057 ulint i;
3058
3059 ut_ad(mtr != NULL);
3060
3061 descr = xdes_get_descriptor(space, page, page_size, mtr);
3062
3063 ut_a(xdes_get_state(descr, mtr) == XDES_FSEG);
3064 ut_a(!memcmp(descr + XDES_ID, seg_inode + FSEG_ID, 8));
3065 ut_ad(mach_read_from_4(seg_inode + FSEG_MAGIC_N)
3066 == FSEG_MAGIC_N_VALUE);
3067 ut_d(fsp_space_modify_check(space, mtr));
3068
3069 first_page_in_extent = page - (page % FSP_EXTENT_SIZE);
3070
3071#ifdef BTR_CUR_HASH_ADAPT
3072 if (ahi) {
3073 for (i = 0; i < FSP_EXTENT_SIZE; i++) {
3074 if (!xdes_mtr_get_bit(descr, XDES_FREE_BIT, i, mtr)) {
3075
3076 /* Drop search system page hash index
3077 if the page is found in the pool and
3078 is hashed */
3079
3080 btr_search_drop_page_hash_when_freed(
3081 page_id_t(space->id,
3082 first_page_in_extent + i));
3083 }
3084 }
3085 }
3086#endif /* BTR_CUR_HASH_ADAPT */
3087
3088 if (xdes_is_full(descr, mtr)) {
3089 flst_remove(seg_inode + FSEG_FULL,
3090 descr + XDES_FLST_NODE, mtr);
3091 } else if (xdes_is_free(descr, mtr)) {
3092 flst_remove(seg_inode + FSEG_FREE,
3093 descr + XDES_FLST_NODE, mtr);
3094 } else {
3095 flst_remove(seg_inode + FSEG_NOT_FULL,
3096 descr + XDES_FLST_NODE, mtr);
3097
3098 not_full_n_used = mtr_read_ulint(
3099 seg_inode + FSEG_NOT_FULL_N_USED, MLOG_4BYTES, mtr);
3100
3101 descr_n_used = xdes_get_n_used(descr, mtr);
3102 ut_a(not_full_n_used >= descr_n_used);
3103 mlog_write_ulint(seg_inode + FSEG_NOT_FULL_N_USED,
3104 not_full_n_used - descr_n_used,
3105 MLOG_4BYTES, mtr);
3106 }
3107
3108 fsp_free_extent(space, page, page_size, mtr);
3109
3110#ifdef UNIV_DEBUG
3111 for (i = 0; i < FSP_EXTENT_SIZE; i++) {
3112
3113 buf_page_set_file_page_was_freed(
3114 page_id_t(space->id, first_page_in_extent + i));
3115 }
3116#endif /* UNIV_DEBUG */
3117}
3118
3119#ifndef BTR_CUR_HASH_ADAPT
3120# define fseg_free_extent(inode, space, page_size, page, ahi, mtr) \
3121 fseg_free_extent(inode, space, page_size, page, mtr)
3122#endif /* !BTR_CUR_HASH_ADAPT */
3123
3124/**********************************************************************//**
3125Frees part of a segment. This function can be used to free a segment by
3126repeatedly calling this function in different mini-transactions. Doing
3127the freeing in a single mini-transaction might result in too big a
3128mini-transaction.
3129@return TRUE if freeing completed */
3130ibool
3131fseg_free_step_func(
3132 fseg_header_t* header, /*!< in, own: segment header; NOTE: if the header
3133 resides on the first page of the frag list
3134 of the segment, this pointer becomes obsolete
3135 after the last freeing step */
3136#ifdef BTR_CUR_HASH_ADAPT
3137 bool ahi, /*!< in: whether we may need to drop
3138 the adaptive hash index */
3139#endif /* BTR_CUR_HASH_ADAPT */
3140 mtr_t* mtr) /*!< in/out: mini-transaction */
3141{
3142 ulint n;
3143 ulint page;
3144 xdes_t* descr;
3145 fseg_inode_t* inode;
3146 ulint space_id;
3147 ulint header_page;
3148
3149 DBUG_ENTER("fseg_free_step");
3150
3151 space_id = page_get_space_id(page_align(header));
3152 header_page = page_get_page_no(page_align(header));
3153
3154 fil_space_t* space = mtr_x_lock_space(space_id, mtr);
3155 const page_size_t page_size(space->flags);
3156
3157 descr = xdes_get_descriptor(space, header_page, page_size, mtr);
3158
3159 /* Check that the header resides on a page which has not been
3160 freed yet */
3161
3162 ut_a(xdes_mtr_get_bit(descr, XDES_FREE_BIT,
3163 header_page % FSP_EXTENT_SIZE, mtr) == FALSE);
3164 buf_block_t* iblock;
3165
3166 inode = fseg_inode_try_get(header, space_id, page_size, mtr, &iblock);
3167
3168 if (inode == NULL) {
3169 ib::info() << "Double free of inode from "
3170 << page_id_t(space_id, header_page);
3171 DBUG_RETURN(TRUE);
3172 }
3173
3174 fil_block_check_type(iblock, FIL_PAGE_INODE, mtr);
3175 descr = fseg_get_first_extent(inode, space, page_size, mtr);
3176
3177 if (descr != NULL) {
3178 /* Free the extent held by the segment */
3179 page = xdes_get_offset(descr);
3180
3181 fseg_free_extent(inode, space, page_size, page, ahi, mtr);
3182
3183 DBUG_RETURN(FALSE);
3184 }
3185
3186 /* Free a frag page */
3187 n = fseg_find_last_used_frag_page_slot(inode, mtr);
3188
3189 if (n == ULINT_UNDEFINED) {
3190 /* Freeing completed: free the segment inode */
3191 fsp_free_seg_inode(space, page_size, inode, mtr);
3192
3193 DBUG_RETURN(TRUE);
3194 }
3195
3196 fseg_free_page_low(
3197 inode, space,
3198 fseg_get_nth_frag_page_no(inode, n, mtr),
3199 page_size, ahi, mtr);
3200
3201 n = fseg_find_last_used_frag_page_slot(inode, mtr);
3202
3203 if (n == ULINT_UNDEFINED) {
3204 /* Freeing completed: free the segment inode */
3205 fsp_free_seg_inode(space, page_size, inode, mtr);
3206
3207 DBUG_RETURN(TRUE);
3208 }
3209
3210 DBUG_RETURN(FALSE);
3211}
3212
3213/**********************************************************************//**
3214Frees part of a segment. Differs from fseg_free_step because this function
3215leaves the header page unfreed.
3216@return TRUE if freeing completed, except the header page */
3217ibool
3218fseg_free_step_not_header_func(
3219 fseg_header_t* header, /*!< in: segment header which must reside on
3220 the first fragment page of the segment */
3221#ifdef BTR_CUR_HASH_ADAPT
3222 bool ahi, /*!< in: whether we may need to drop
3223 the adaptive hash index */
3224#endif /* BTR_CUR_HASH_ADAPT */
3225 mtr_t* mtr) /*!< in/out: mini-transaction */
3226{
3227 ulint n;
3228 ulint page;
3229 xdes_t* descr;
3230 fseg_inode_t* inode;
3231 ulint space_id;
3232 ulint page_no;
3233
3234 space_id = page_get_space_id(page_align(header));
3235 ut_ad(mtr->is_named_space(space_id));
3236
3237 fil_space_t* space = mtr_x_lock_space(space_id, mtr);
3238 const page_size_t page_size(space->flags);
3239 buf_block_t* iblock;
3240
3241 inode = fseg_inode_get(header, space_id, page_size, mtr, &iblock);
3242 fil_block_check_type(iblock, FIL_PAGE_INODE, mtr);
3243
3244 descr = fseg_get_first_extent(inode, space, page_size, mtr);
3245
3246 if (descr != NULL) {
3247 /* Free the extent held by the segment */
3248 page = xdes_get_offset(descr);
3249
3250 fseg_free_extent(inode, space, page_size, page, ahi, mtr);
3251
3252 return(FALSE);
3253 }
3254
3255 /* Free a frag page */
3256
3257 n = fseg_find_last_used_frag_page_slot(inode, mtr);
3258
3259 if (n == ULINT_UNDEFINED) {
3260 ut_error;
3261 }
3262
3263 page_no = fseg_get_nth_frag_page_no(inode, n, mtr);
3264
3265 if (page_no == page_get_page_no(page_align(header))) {
3266
3267 return(TRUE);
3268 }
3269
3270 fseg_free_page_low(inode, space, page_no, page_size, ahi, mtr);
3271
3272 return(FALSE);
3273}
3274
3275/** Returns the first extent descriptor for a segment.
3276We think of the extent lists of the segment catenated in the order
3277FSEG_FULL -> FSEG_NOT_FULL -> FSEG_FREE.
3278@param[in] inode segment inode
3279@param[in] space tablespace
3280@param[in] page_size page size
3281@param[in,out] mtr mini-transaction
3282@return the first extent descriptor, or NULL if none */
3283MY_ATTRIBUTE((nonnull, warn_unused_result))
3284static
3285xdes_t*
3286fseg_get_first_extent(
3287 fseg_inode_t* inode,
3288 const fil_space_t* space,
3289 const page_size_t& page_size,
3290 mtr_t* mtr)
3291{
3292 fil_addr_t first;
3293
3294 ut_ad(space->id == page_get_space_id(page_align(inode)));
3295 ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE);
3296
3297 if (flst_get_len(inode + FSEG_FULL) > 0) {
3298
3299 first = flst_get_first(inode + FSEG_FULL, mtr);
3300
3301 } else if (flst_get_len(inode + FSEG_NOT_FULL) > 0) {
3302
3303 first = flst_get_first(inode + FSEG_NOT_FULL, mtr);
3304
3305 } else if (flst_get_len(inode + FSEG_FREE) > 0) {
3306
3307 first = flst_get_first(inode + FSEG_FREE, mtr);
3308 } else {
3309 return(NULL);
3310 }
3311
3312 ut_ad(first.page != FIL_NULL);
3313
3314 return(first.page == FIL_NULL ? NULL
3315 : xdes_lst_get_descriptor(space, page_size, first, mtr));
3316}
3317
3318#ifdef UNIV_BTR_PRINT
3319/*******************************************************************//**
3320Writes info of a segment. */
3321static
3322void
3323fseg_print_low(
3324/*===========*/
3325 fseg_inode_t* inode, /*!< in: segment inode */
3326 mtr_t* mtr) /*!< in/out: mini-transaction */
3327{
3328 ulint space;
3329 ulint n_used;
3330 ulint n_frag;
3331 ulint n_free;
3332 ulint n_not_full;
3333 ulint n_full;
3334 ulint reserved;
3335 ulint used;
3336 ulint page_no;
3337 ib_id_t seg_id;
3338
3339 ut_ad(mtr_memo_contains_page(mtr, inode, MTR_MEMO_PAGE_SX_FIX));
3340 space = page_get_space_id(page_align(inode));
3341 page_no = page_get_page_no(page_align(inode));
3342
3343 reserved = fseg_n_reserved_pages_low(inode, &used, mtr);
3344
3345 seg_id = mach_read_from_8(inode + FSEG_ID);
3346
3347 n_used = mtr_read_ulint(inode + FSEG_NOT_FULL_N_USED,
3348 MLOG_4BYTES, mtr);
3349 n_frag = fseg_get_n_frag_pages(inode, mtr);
3350 n_free = flst_get_len(inode + FSEG_FREE);
3351 n_not_full = flst_get_len(inode + FSEG_NOT_FULL);
3352 n_full = flst_get_len(inode + FSEG_FULL);
3353
3354 ib::info() << "SEGMENT id " << seg_id
3355 << " space " << space << ";"
3356 << " page " << page_no << ";"
3357 << " res " << reserved << " used " << used << ";"
3358 << " full ext " << n_full << ";"
3359 << " fragm pages " << n_frag << ";"
3360 << " free extents " << n_free << ";"
3361 << " not full extents " << n_not_full << ": pages " << n_used;
3362
3363 ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE);
3364}
3365
3366/*******************************************************************//**
3367Writes info of a segment. */
3368void
3369fseg_print(
3370/*=======*/
3371 fseg_header_t* header, /*!< in: segment header */
3372 mtr_t* mtr) /*!< in/out: mini-transaction */
3373{
3374 fseg_inode_t* inode;
3375 ulint space_id;
3376
3377 space_id = page_get_space_id(page_align(header));
3378 const fil_space_t* space = mtr_x_lock_space(space_id, mtr);
3379 const page_size_t page_size(space->flags);
3380
3381 inode = fseg_inode_get(header, space_id, page_size, mtr);
3382
3383 fseg_print_low(inode, mtr);
3384}
3385#endif /* UNIV_BTR_PRINT */
3386
3387#ifdef UNIV_DEBUG
3388/** Print the file segment header to the given output stream.
3389@param[in] out the output stream into which the object is printed.
3390@retval the output stream into which the object was printed. */
3391std::ostream&
3392fseg_header::to_stream(std::ostream& out) const
3393{
3394 const ulint space = mtr_read_ulint(m_header + FSEG_HDR_SPACE,
3395 MLOG_4BYTES, m_mtr);
3396 const ulint page_no = mtr_read_ulint(m_header + FSEG_HDR_PAGE_NO,
3397 MLOG_4BYTES, m_mtr);
3398
3399 const ulint offset = mtr_read_ulint(m_header + FSEG_HDR_OFFSET,
3400 MLOG_2BYTES, m_mtr);
3401
3402 out << "[fseg_header_t: space=" << space << ", page="
3403 << page_no << ", offset=" << offset << "]";
3404
3405 return(out);
3406}
3407#endif /* UNIV_DEBUG */
3408