summaryrefslogtreecommitdiff
path: root/innobase/fsp
diff options
context:
space:
mode:
Diffstat (limited to 'innobase/fsp')
-rw-r--r--innobase/fsp/Makefile.am25
-rw-r--r--innobase/fsp/fsp0fsp.c3365
-rw-r--r--innobase/fsp/makefilewin9
-rw-r--r--innobase/fsp/trash/FSP0FSP.C3100
-rw-r--r--innobase/fsp/ts/del.c891
-rw-r--r--innobase/fsp/ts/makefile16
-rw-r--r--innobase/fsp/ts/tsfsp.c1234
7 files changed, 8640 insertions, 0 deletions
diff --git a/innobase/fsp/Makefile.am b/innobase/fsp/Makefile.am
new file mode 100644
index 00000000000..b3e9ab44d9b
--- /dev/null
+++ b/innobase/fsp/Makefile.am
@@ -0,0 +1,25 @@
+# Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
+# & Innobase Oy
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+include ../include/Makefile.i
+
+libs_LIBRARIES = libfsp.a
+
+libfsp_a_SOURCES = fsp0fsp.c
+
+EXTRA_PROGRAMS =
diff --git a/innobase/fsp/fsp0fsp.c b/innobase/fsp/fsp0fsp.c
new file mode 100644
index 00000000000..27b5798cbcc
--- /dev/null
+++ b/innobase/fsp/fsp0fsp.c
@@ -0,0 +1,3365 @@
+/**********************************************************************
+File space management
+
+(c) 1995 Innobase Oy
+
+Created 11/29/1995 Heikki Tuuri
+***********************************************************************/
+
+#include "fsp0fsp.h"
+
+#ifdef UNIV_NONINL
+#include "fsp0fsp.ic"
+#endif
+
+#include "buf0buf.h"
+#include "fil0fil.h"
+#include "sync0sync.h"
+#include "mtr0log.h"
+#include "fut0fut.h"
+#include "ut0byte.h"
+#include "srv0srv.h"
+#include "page0types.h"
+#include "ibuf0ibuf.h"
+#include "btr0btr.h"
+#include "btr0sea.h"
+#include "dict0boot.h"
+#include "dict0mem.h"
+#include "log0log.h"
+
+/* The data structures in files are defined just as byte strings in C */
+typedef byte fsp_header_t;
+typedef byte xdes_t;
+
+/* SPACE HEADER
+ ============
+
+File space header data structure: this data structure is contained in the
+first page of a space. The space for this header is reserved in every extent
+descriptor page, but used only in the first. */
+
+#define FSP_HEADER_OFFSET FIL_PAGE_DATA /* Offset of the space header
+ within a file page */
+/*-------------------------------------*/
+#define FSP_NOT_USED 0 /* this field contained a value up to
+ which we know that the modifications
+ in the database have been flushed to
+ the file space; not used now */
+#define FSP_SIZE 8 /* Current size of the space in
+ pages */
+#define FSP_FREE_LIMIT 12 /* Minimum page number for which the
+ free list has not been initialized:
+ the pages >= this limit are, by
+ definition free */
+#define FSP_LOWEST_NO_WRITE 16 /* The lowest page offset for which
+ the page has not been written to disk
+ (if it has been written, we know that
+ the OS has really reserved the
+ physical space for the page) */
+#define FSP_FRAG_N_USED 20 /* number of used pages in the
+ FSP_FREE_FRAG list */
+#define FSP_FREE 24 /* list of free extents */
+#define FSP_FREE_FRAG (24 + FLST_BASE_NODE_SIZE)
+ /* list of partially free extents not
+ belonging to any segment */
+#define FSP_FULL_FRAG (24 + 2 * FLST_BASE_NODE_SIZE)
+ /* list of full extents not belonging
+ to any segment */
+#define FSP_SEG_ID (24 + 3 * FLST_BASE_NODE_SIZE)
+ /* 8 bytes which give the first unused
+ segment id */
+#define FSP_SEG_INODES_FULL (32 + 3 * FLST_BASE_NODE_SIZE)
+ /* list of pages containing segment
+ headers, where all the segment inode
+ slots are reserved */
+#define FSP_SEG_INODES_FREE (32 + 4 * FLST_BASE_NODE_SIZE)
+ /* list of pages containing segment
+ headers, where not all the segment
+ header slots are reserved */
+/*-------------------------------------*/
+/* File space header size */
+#define FSP_HEADER_SIZE (32 + 5 * FLST_BASE_NODE_SIZE)
+
+#define FSP_FREE_ADD 4 /* this many free extents are added
+ to the free list from above
+ FSP_FREE_LIMIT at a time */
+
+
+/* FILE SEGMENT INODE
+ ==================
+
+Segment inode which is created for each segment in a tablespace. NOTE: in
+purge we assume that a segment having only one currently used page can be
+freed in a few steps, so that the freeing cannot fill the file buffer with
+bufferfixed file pages. */
+
+typedef byte fseg_inode_t;
+
+#define FSEG_INODE_PAGE_NODE FSEG_PAGE_DATA
+ /* the list node for linking
+ segment inode pages */
+
+#define FSEG_ARR_OFFSET (FSEG_PAGE_DATA + FLST_NODE_SIZE)
+/*-------------------------------------*/
+#define FSEG_ID 0 /* 8 bytes of segment id: if this is
+ ut_dulint_zero, it means that the
+ header is unused */
+#define FSEG_NOT_FULL_N_USED 8
+ /* number of used segment pages in
+ the FSEG_NOT_FULL list */
+#define FSEG_FREE 12
+ /* list of free extents of this
+ segment */
+#define FSEG_NOT_FULL (12 + FLST_BASE_NODE_SIZE)
+ /* list of partially free extents */
+#define FSEG_FULL (12 + 2 * FLST_BASE_NODE_SIZE)
+ /* list of full extents */
+#define FSEG_MAGIC_N (12 + 3 * FLST_BASE_NODE_SIZE)
+ /* magic number used in debugging */
+#define FSEG_FRAG_ARR (16 + 3 * FLST_BASE_NODE_SIZE)
+ /* array of individual pages
+ belonging to this segment in fsp
+ fragment extent lists */
+#define FSEG_FRAG_ARR_N_SLOTS (FSP_EXTENT_SIZE / 2)
+ /* number of slots in the array for
+ the fragment pages */
+#define FSEG_FRAG_SLOT_SIZE 4 /* a fragment page slot contains its
+ page number within space, FIL_NULL
+ means that the slot is not in use */
+/*-------------------------------------*/
+#define FSEG_INODE_SIZE (16 + 3 * FLST_BASE_NODE_SIZE +\
+ FSEG_FRAG_ARR_N_SLOTS * FSEG_FRAG_SLOT_SIZE)
+
+#define FSP_SEG_INODES_PER_PAGE ((UNIV_PAGE_SIZE - FSEG_ARR_OFFSET - 10)\
+ / FSEG_INODE_SIZE)
+ /* Number of segment inodes which fit on a
+ single page */
+
+#define FSEG_MAGIC_N_VALUE 97937874
+
+#define FSEG_FILLFACTOR 8 /* If this value is x, then if
+ the number of unused but reserved
+ pages in a segment is less than
+ reserved pages * 1/x, and there are
+ at least FSEG_FRAG_LIMIT used pages,
+ then we allow a new empty extent to
+ be added to the segment in
+ fseg_alloc_free_page. Otherwise, we
+ use unused pages of the segment. */
+
+#define FSEG_FRAG_LIMIT FSEG_FRAG_ARR_N_SLOTS
+ /* If the segment has >= this many
+ used pages, it may be expanded by
+ allocating extents to the segment;
+ until that only individual fragment
+ pages are allocated from the space */
+
+#define FSEG_FREE_LIST_LIMIT 40 /* If the reserved size of a segment
+ is at least this many extents, we
+ allow extents to be put to the free
+ list of the extent: at most
+ FSEG_FREE_LIST_MAX_LEN many */
+#define FSEG_FREE_LIST_MAX_LEN 4
+
+
+/* EXTENT DESCRIPTOR
+ =================
+
+File extent descriptor data structure: contains bits to tell which pages in
+the extent are free and which contain old tuple version to clean. */
+
+/*-------------------------------------*/
+#define XDES_ID 0 /* The identifier of the segment
+ to which this extent belongs */
+#define XDES_FLST_NODE 8 /* The list node data structure
+ for the descriptors */
+#define XDES_STATE (FLST_NODE_SIZE + 8)
+ /* contains state information
+ of the extent */
+#define XDES_BITMAP (FLST_NODE_SIZE + 12)
+ /* Descriptor bitmap of the pages
+ in the extent */
+/*-------------------------------------*/
+
+#define XDES_BITS_PER_PAGE 2 /* How many bits are there per page */
+#define XDES_FREE_BIT 0 /* Index of the bit which tells if
+ the page is free */
+#define XDES_CLEAN_BIT 1 /* NOTE: currently not used!
+ Index of the bit which tells if
+ there are old versions of tuples
+ on the page */
+/* States of a descriptor */
+#define XDES_FREE 1 /* extent is in free list of space */
+#define XDES_FREE_FRAG 2 /* extent is in free fragment list of
+ space */
+#define XDES_FULL_FRAG 3 /* extent is in full fragment list of
+ space */
+#define XDES_FSEG 4 /* extent belongs to a segment */
+
+/* File extent data structure size in bytes. The "+ 7 ) / 8" part in the
+definition rounds the number of bytes upward. */
+#define XDES_SIZE (XDES_BITMAP +\
+ (FSP_EXTENT_SIZE * XDES_BITS_PER_PAGE + 7) / 8)
+
+/* Offset of the descriptor array on a descriptor page */
+#define XDES_ARR_OFFSET (FSP_HEADER_OFFSET + FSP_HEADER_SIZE)
+
+/**************************************************************************
+Returns an extent to the free list of a space. */
+static
+void
+fsp_free_extent(
+/*============*/
+ ulint space, /* in: space id */
+ ulint page, /* in: page offset in the extent */
+ mtr_t* mtr); /* in: mtr */
+/**************************************************************************
+Frees an extent of a segment to the space free list. */
+static
+void
+fseg_free_extent(
+/*=============*/
+ fseg_inode_t* seg_inode, /* in: segment inode */
+ ulint space, /* in: space id */
+ ulint page, /* in: page offset in the extent */
+ mtr_t* mtr); /* in: mtr handle */
+/**************************************************************************
+Calculates the number of pages reserved by a segment, and how
+many pages are currently used. */
+static
+ulint
+fseg_n_reserved_pages_low(
+/*======================*/
+ /* out: number of reserved pages */
+ fseg_inode_t* header, /* in: segment inode */
+ ulint* used, /* out: number of pages used (<= reserved) */
+ mtr_t* mtr); /* in: mtr handle */
+/************************************************************************
+Marks a page used. The page must reside within the extents of the given
+segment. */
+static
+void
+fseg_mark_page_used(
+/*================*/
+ fseg_inode_t* seg_inode,/* in: segment inode */
+ ulint space, /* in: space id */
+ ulint page, /* in: page offset */
+ mtr_t* mtr); /* in: mtr */
+/**************************************************************************
+Returns the first extent descriptor for a segment. We think of the extent
+lists of the segment catenated in the order FSEG_FULL -> FSEG_NOT_FULL
+-> FSEG_FREE. */
+static
+xdes_t*
+fseg_get_first_extent(
+/*==================*/
+ /* out: the first extent descriptor, or NULL if
+ none */
+ fseg_inode_t* inode, /* in: segment inode */
+ mtr_t* mtr); /* in: mtr */
+/**************************************************************************
+Puts new extents to the free list if
+there are free extents above the free limit. If an extent happens
+to contain an extent descriptor page, the extent is put to
+the FSP_FREE_FRAG list with the page marked as used. */
+static
+void
+fsp_fill_free_list(
+/*===============*/
+ ulint space, /* in: space */
+ fsp_header_t* header, /* in: space header */
+ mtr_t* mtr); /* in: mtr */
+/**************************************************************************
+Allocates a single free page from a segment. This function implements
+the intelligent allocation strategy which tries to minimize file space
+fragmentation. */
+static
+ulint
+fseg_alloc_free_page_low(
+/*=====================*/
+ /* out: the allocated page number, FIL_NULL
+ if no page could be allocated */
+ ulint space, /* in: space */
+ fseg_inode_t* seg_inode, /* in: segment inode */
+ ulint hint, /* in: hint of which page would be desirable */
+ byte direction, /* in: if the new page is needed because
+ of an index page split, and records are
+ inserted there in order, into which
+ direction they go alphabetically: FSP_DOWN,
+ FSP_UP, FSP_NO_DIR */
+ mtr_t* mtr); /* in: mtr handle */
+
+/**************************************************************************
+Gets a pointer to the space header and x-locks its page. */
+UNIV_INLINE
+fsp_header_t*
+fsp_get_space_header(
+/*=================*/
+ /* out: pointer to the space header, page x-locked */
+ ulint id, /* in: space id */
+ mtr_t* mtr) /* in: mtr */
+{
+ fsp_header_t* header;
+
+ ut_ad(mtr);
+
+ header = FSP_HEADER_OFFSET + buf_page_get(id, 0, RW_X_LATCH, mtr);
+
+ buf_page_dbg_add_level(header, SYNC_FSP_PAGE);
+
+ return(header);
+}
+
+/**************************************************************************
+Gets a descriptor bit of a page. */
+UNIV_INLINE
+ibool
+xdes_get_bit(
+/*=========*/
+ /* out: TRUE if free */
+ xdes_t* descr, /* in: descriptor */
+ ulint bit, /* in: XDES_FREE_BIT or XDES_CLEAN_BIT */
+ ulint offset, /* in: page offset within extent:
+ 0 ... FSP_EXTENT_SIZE - 1 */
+ mtr_t* mtr) /* in: mtr */
+{
+ ulint index;
+ ulint byte_index;
+ ulint bit_index;
+
+ ut_ad(mtr_memo_contains(mtr, buf_block_align(descr),
+ MTR_MEMO_PAGE_X_FIX));
+ ut_ad((bit == XDES_FREE_BIT) || (bit == XDES_CLEAN_BIT));
+ ut_ad(offset < FSP_EXTENT_SIZE);
+
+ index = bit + XDES_BITS_PER_PAGE * offset;
+
+ byte_index = index / 8;
+ bit_index = index % 8;
+
+ return(ut_bit_get_nth(
+ mtr_read_ulint(descr + XDES_BITMAP + byte_index,
+ MLOG_1BYTE, mtr),
+ bit_index));
+}
+
+/**************************************************************************
+Sets a descriptor bit of a page. */
+UNIV_INLINE
+void
+xdes_set_bit(
+/*=========*/
+ xdes_t* descr, /* in: descriptor */
+ ulint bit, /* in: XDES_FREE_BIT or XDES_CLEAN_BIT */
+ ulint offset, /* in: page offset within extent:
+ 0 ... FSP_EXTENT_SIZE - 1 */
+ ibool val, /* in: bit value */
+ mtr_t* mtr) /* in: mtr */
+{
+ ulint index;
+ ulint byte_index;
+ ulint bit_index;
+ ulint descr_byte;
+
+ ut_ad(mtr_memo_contains(mtr, buf_block_align(descr),
+ MTR_MEMO_PAGE_X_FIX));
+ ut_ad((bit == XDES_FREE_BIT) || (bit == XDES_CLEAN_BIT));
+ ut_ad(offset < FSP_EXTENT_SIZE);
+
+ index = bit + XDES_BITS_PER_PAGE * offset;
+
+ byte_index = index / 8;
+ bit_index = index % 8;
+
+ descr_byte = mtr_read_ulint(descr + XDES_BITMAP + byte_index,
+ MLOG_1BYTE, mtr);
+ descr_byte = ut_bit_set_nth(descr_byte, bit_index, val);
+
+ mlog_write_ulint(descr + XDES_BITMAP + byte_index, descr_byte,
+ MLOG_1BYTE, mtr);
+}
+
+/**************************************************************************
+Looks for a descriptor bit having the desired value. Starts from hint
+and scans upward; at the end of the extent the search is wrapped to
+the start of the extent. */
+UNIV_INLINE
+ulint
+xdes_find_bit(
+/*==========*/
+ /* out: bit index of the bit, ULINT_UNDEFINED if not
+ found */
+ xdes_t* descr, /* in: descriptor */
+ ulint bit, /* in: XDES_FREE_BIT or XDES_CLEAN_BIT */
+ ibool val, /* in: desired bit value */
+ ulint hint, /* in: hint of which bit position would be desirable */
+ mtr_t* mtr) /* in: mtr */
+{
+ ulint i;
+
+ ut_ad(descr && mtr);
+ ut_ad(val <= TRUE);
+ ut_ad(hint < FSP_EXTENT_SIZE);
+ ut_ad(mtr_memo_contains(mtr, buf_block_align(descr),
+ MTR_MEMO_PAGE_X_FIX));
+ for (i = hint; i < FSP_EXTENT_SIZE; i++) {
+ if (val == xdes_get_bit(descr, bit, i, mtr)) {
+
+ return(i);
+ }
+ }
+
+ for (i = 0; i < hint; i++) {
+ if (val == xdes_get_bit(descr, bit, i, mtr)) {
+
+ return(i);
+ }
+ }
+
+ return(ULINT_UNDEFINED);
+}
+
+/**************************************************************************
+Looks for a descriptor bit having the desired value. Scans the extent in
+a direction opposite to xdes_find_bit. */
+UNIV_INLINE
+ulint
+xdes_find_bit_downward(
+/*===================*/
+ /* out: bit index of the bit, ULINT_UNDEFINED if not
+ found */
+ xdes_t* descr, /* in: descriptor */
+ ulint bit, /* in: XDES_FREE_BIT or XDES_CLEAN_BIT */
+ ibool val, /* in: desired bit value */
+ ulint hint, /* in: hint of which bit position would be desirable */
+ mtr_t* mtr) /* in: mtr */
+{
+ ulint i;
+
+ ut_ad(descr && mtr);
+ ut_ad(val <= TRUE);
+ ut_ad(hint < FSP_EXTENT_SIZE);
+ ut_ad(mtr_memo_contains(mtr, buf_block_align(descr),
+ MTR_MEMO_PAGE_X_FIX));
+ for (i = hint + 1; i > 0; i--) {
+ if (val == xdes_get_bit(descr, bit, i - 1, mtr)) {
+
+ return(i - 1);
+ }
+ }
+
+ for (i = FSP_EXTENT_SIZE - 1; i > hint; i--) {
+ if (val == xdes_get_bit(descr, bit, i, mtr)) {
+
+ return(i);
+ }
+ }
+
+ return(ULINT_UNDEFINED);
+}
+
+/**************************************************************************
+Returns the number of used pages in a descriptor. */
+UNIV_INLINE
+ulint
+xdes_get_n_used(
+/*============*/
+ /* out: number of pages used */
+ xdes_t* descr, /* in: descriptor */
+ mtr_t* mtr) /* in: mtr */
+{
+ ulint i;
+ ulint count = 0;
+
+ ut_ad(descr && mtr);
+ ut_ad(mtr_memo_contains(mtr, buf_block_align(descr),
+ MTR_MEMO_PAGE_X_FIX));
+ for (i = 0; i < FSP_EXTENT_SIZE; i++) {
+ if (FALSE == xdes_get_bit(descr, XDES_FREE_BIT, i, mtr)) {
+ count++;
+ }
+ }
+
+ return(count);
+}
+
+/**************************************************************************
+Returns true if extent contains no used pages. */
+UNIV_INLINE
+ibool
+xdes_is_free(
+/*=========*/
+ /* out: TRUE if totally free */
+ xdes_t* descr, /* in: descriptor */
+ mtr_t* mtr) /* in: mtr */
+{
+ if (0 == xdes_get_n_used(descr, mtr)) {
+
+ return(TRUE);
+ }
+
+ return(FALSE);
+}
+
+/**************************************************************************
+Returns true if extent contains no free pages. */
+UNIV_INLINE
+ibool
+xdes_is_full(
+/*=========*/
+ /* out: TRUE if full */
+ xdes_t* descr, /* in: descriptor */
+ mtr_t* mtr) /* in: mtr */
+{
+ if (FSP_EXTENT_SIZE == xdes_get_n_used(descr, mtr)) {
+
+ return(TRUE);
+ }
+
+ return(FALSE);
+}
+
+/**************************************************************************
+Sets the state of an xdes. */
+UNIV_INLINE
+void
+xdes_set_state(
+/*===========*/
+ xdes_t* descr, /* in: descriptor */
+ ulint state, /* in: state to set */
+ mtr_t* mtr) /* in: mtr handle */
+{
+ ut_ad(descr && mtr);
+ ut_ad(state >= XDES_FREE);
+ ut_ad(state <= XDES_FSEG);
+ ut_ad(mtr_memo_contains(mtr, buf_block_align(descr),
+ MTR_MEMO_PAGE_X_FIX));
+
+ mlog_write_ulint(descr + XDES_STATE, state, MLOG_4BYTES, mtr);
+}
+
+/**************************************************************************
+Gets the state of an xdes. */
+UNIV_INLINE
+ulint
+xdes_get_state(
+/*===========*/
+ /* out: state */
+ xdes_t* descr, /* in: descriptor */
+ mtr_t* mtr) /* in: mtr handle */
+{
+ ut_ad(descr && mtr);
+ ut_ad(mtr_memo_contains(mtr, buf_block_align(descr),
+ MTR_MEMO_PAGE_X_FIX));
+
+ return(mtr_read_ulint(descr + XDES_STATE, MLOG_4BYTES, mtr));
+}
+
+/**************************************************************************
+Inits an extent descriptor to the free and clean state. */
+UNIV_INLINE
+void
+xdes_init(
+/*======*/
+ xdes_t* descr, /* in: descriptor */
+ mtr_t* mtr) /* in: mtr */
+{
+ ulint i;
+
+ ut_ad(descr && mtr);
+ ut_ad(mtr_memo_contains(mtr, buf_block_align(descr),
+ MTR_MEMO_PAGE_X_FIX));
+ ut_ad((XDES_SIZE - XDES_BITMAP) % 4 == 0);
+
+ for (i = XDES_BITMAP; i < XDES_SIZE; i += 4) {
+ mlog_write_ulint(descr + i, 0xFFFFFFFF, MLOG_4BYTES, mtr);
+ }
+
+ xdes_set_state(descr, XDES_FREE, mtr);
+}
+
+/************************************************************************
+Calculates the page where the descriptor of a page resides. */
+UNIV_INLINE
+ulint
+xdes_calc_descriptor_page(
+/*======================*/
+ /* out: descriptor page offset */
+ ulint offset) /* in: page offset */
+{
+ ut_ad(UNIV_PAGE_SIZE > XDES_ARR_OFFSET
+ + (XDES_DESCRIBED_PER_PAGE / FSP_EXTENT_SIZE) * XDES_SIZE);
+
+ return(ut_2pow_round(offset, XDES_DESCRIBED_PER_PAGE));
+}
+
+/************************************************************************
+Calculates the descriptor index within a descriptor page. */
+UNIV_INLINE
+ulint
+xdes_calc_descriptor_index(
+/*=======================*/
+ /* out: descriptor index */
+ ulint offset) /* in: page offset */
+{
+ return(ut_2pow_remainder(offset, XDES_DESCRIBED_PER_PAGE) /
+ FSP_EXTENT_SIZE);
+}
+
+/************************************************************************
+Gets pointer to a the extent descriptor of a page. The page where the extent
+descriptor resides is x-locked. If the page offset is equal to the free limit
+of the space, adds new extents from above the free limit to the space free
+list, if not free limit == space size. This adding is necessary to make the
+descriptor defined, as they are uninitialized above the free limit. */
+UNIV_INLINE
+xdes_t*
+xdes_get_descriptor_with_space_hdr(
+/*===============================*/
+ /* out: pointer to the extent descriptor,
+ NULL if the page does not exist in the
+ space or if offset > free limit */
+ fsp_header_t* sp_header,/* in: space header, x-latched */
+ ulint space, /* in: space id */
+ ulint offset, /* in: page offset;
+ if equal to the free limit,
+ we try to add new extents to
+ the space free list */
+ mtr_t* mtr) /* in: mtr handle */
+{
+ ulint limit;
+ ulint size;
+ ulint descr_page_no;
+ page_t* descr_page;
+
+ ut_ad(mtr);
+ ut_ad(mtr_memo_contains(mtr, fil_space_get_latch(space), MTR_MEMO_X_LOCK));
+
+ /* Read free limit and space size */
+ limit = mtr_read_ulint(sp_header + FSP_FREE_LIMIT, MLOG_4BYTES, mtr);
+ size = mtr_read_ulint(sp_header + FSP_SIZE, MLOG_4BYTES, mtr);
+
+ /* If offset is >= size or > limit, return NULL */
+
+ if ((offset >= size) || (offset > limit)) {
+
+ return(NULL);
+ }
+
+ /* If offset is == limit, fill free list of the space. */
+
+ if (offset == limit) {
+ fsp_fill_free_list(space, sp_header, mtr);
+ }
+
+ descr_page_no = xdes_calc_descriptor_page(offset);
+
+ if (descr_page_no == 0) {
+ /* It is on the space header page */
+
+ descr_page = buf_frame_align(sp_header);
+ } else {
+ descr_page = buf_page_get(space, descr_page_no, RW_X_LATCH,
+ mtr);
+ buf_page_dbg_add_level(descr_page, SYNC_FSP_PAGE);
+ }
+
+ return(descr_page + XDES_ARR_OFFSET
+ + XDES_SIZE * xdes_calc_descriptor_index(offset));
+}
+
+/************************************************************************
+Gets pointer to a the extent descriptor of a page. The page where the
+extent descriptor resides is x-locked. If the page offset is equal to
+the free limit of the space, adds new extents from above the free limit
+to the space free list, if not free limit == space size. This adding
+is necessary to make the descriptor defined, as they are uninitialized
+above the free limit. */
+static
+xdes_t*
+xdes_get_descriptor(
+/*================*/
+ /* out: pointer to the extent descriptor, NULL if the
+ page does not exist in the space or if offset > free
+ limit */
+ ulint space, /* in: space id */
+ ulint offset, /* in: page offset; if equal to the free limit,
+ we try to add new extents to the space free list */
+ mtr_t* mtr) /* in: mtr handle */
+{
+ fsp_header_t* sp_header;
+
+ sp_header = FSP_HEADER_OFFSET
+ + buf_page_get(space, 0, RW_X_LATCH, mtr);
+ buf_page_dbg_add_level(sp_header, SYNC_FSP_PAGE);
+
+ return(xdes_get_descriptor_with_space_hdr(sp_header, space, offset,
+ mtr));
+}
+
+/************************************************************************
+Gets pointer to a the extent descriptor if the file address
+of the descriptor list node is known. The page where the
+extent descriptor resides is x-locked. */
+UNIV_INLINE
+xdes_t*
+xdes_lst_get_descriptor(
+/*====================*/
+ /* out: pointer to the extent descriptor */
+ ulint space, /* in: space id */
+ fil_addr_t lst_node,/* in: file address of the list node
+ contained in the descriptor */
+ mtr_t* mtr) /* in: mtr handle */
+{
+ xdes_t* descr;
+
+ ut_ad(mtr);
+ ut_ad(mtr_memo_contains(mtr, fil_space_get_latch(space), MTR_MEMO_X_LOCK));
+
+ descr = fut_get_ptr(space, lst_node, RW_X_LATCH, mtr) - XDES_FLST_NODE;
+
+ return(descr);
+}
+
+/************************************************************************
+Gets pointer to the next descriptor in a descriptor list and x-locks its
+page. */
+UNIV_INLINE
+xdes_t*
+xdes_lst_get_next(
+/*==============*/
+ xdes_t* descr, /* in: pointer to a descriptor */
+ mtr_t* mtr) /* in: mtr handle */
+{
+ ulint space;
+
+ ut_ad(mtr && descr);
+
+ space = buf_frame_get_space_id(descr);
+
+ return(xdes_lst_get_descriptor(space,
+ flst_get_next_addr(descr + XDES_FLST_NODE, mtr), mtr));
+}
+
+/************************************************************************
+Returns page offset of the first page in extent described by a descriptor. */
+UNIV_INLINE
+ulint
+xdes_get_offset(
+/*============*/
+ /* out: offset of the first page in extent */
+ xdes_t* descr) /* in: extent descriptor */
+{
+ ut_ad(descr);
+
+ return(buf_frame_get_page_no(descr)
+ + ((descr - buf_frame_align(descr) - XDES_ARR_OFFSET)
+ / XDES_SIZE)
+ * FSP_EXTENT_SIZE);
+}
+
+/***************************************************************
+Inits a file page whose prior contents should be ignored. */
+static
+void
+fsp_init_file_page_low(
+/*=====================*/
+ byte* ptr) /* in: pointer to a page */
+{
+ page_t* page;
+#ifdef UNIV_BASIC_LOG_DEBUG
+ ulint i;
+#endif
+ page = buf_frame_align(ptr);
+
+#ifdef UNIV_BASIC_LOG_DEBUG
+/* printf("In log debug version: Erase the contents of the file page\n");
+*/
+ for (i = 0; i < UNIV_PAGE_SIZE; i++) {
+ page[i] = 0xFF;
+ }
+#endif
+ mach_write_to_8(page + UNIV_PAGE_SIZE - FIL_PAGE_END_LSN,
+ ut_dulint_zero);
+ mach_write_to_8(page + FIL_PAGE_LSN, ut_dulint_zero);
+}
+
+/***************************************************************
+Inits a file page whose prior contents should be ignored. */
+
+void
+fsp_init_file_page(
+/*===============*/
+ page_t* page, /* in: page */
+ mtr_t* mtr) /* in: mtr */
+{
+ fsp_init_file_page_low(page);
+
+ mlog_write_initial_log_record(page, MLOG_INIT_FILE_PAGE, mtr);
+}
+
+/***************************************************************
+Parses a redo log record of a file page init. */
+
+byte*
+fsp_parse_init_file_page(
+/*=====================*/
+ /* out: end of log record or NULL */
+ byte* ptr, /* in: buffer */
+ byte* end_ptr,/* in: buffer end */
+ page_t* page) /* in: page or NULL */
+{
+ ut_ad(ptr && end_ptr);
+
+ if (page) {
+ fsp_init_file_page_low(page);
+ }
+
+ return(ptr);
+}
+
+/**************************************************************************
+Initializes the fsp system. */
+
+void
+fsp_init(void)
+/*==========*/
+{
+ /* Does nothing at the moment */
+}
+
+/**************************************************************************
+Initializes the space header of a new created space and creates also the
+insert buffer tree root. */
+
+void
+fsp_header_init(
+/*============*/
+ ulint space, /* in: space id */
+ ulint size, /* in: current size in blocks */
+ mtr_t* mtr) /* in: mini-transaction handle */
+{
+ fsp_header_t* header;
+ page_t* page;
+
+ ut_ad(mtr);
+
+ mtr_x_lock(fil_space_get_latch(space), mtr);
+
+ page = buf_page_create(space, 0, mtr);
+ buf_page_dbg_add_level(page, SYNC_FSP_PAGE);
+
+ buf_page_get(space, 0, RW_X_LATCH, mtr);
+ buf_page_dbg_add_level(page, SYNC_FSP_PAGE);
+
+ /* The prior contents of the file page should be ignored */
+
+ fsp_init_file_page(page, mtr);
+
+ header = FSP_HEADER_OFFSET + page;
+
+ mlog_write_ulint(header + FSP_SIZE, size, MLOG_4BYTES, mtr);
+ mlog_write_ulint(header + FSP_FREE_LIMIT, 0, MLOG_4BYTES, mtr);
+ mlog_write_ulint(header + FSP_LOWEST_NO_WRITE, 0, MLOG_4BYTES, mtr);
+ mlog_write_ulint(header + FSP_FRAG_N_USED, 0, MLOG_4BYTES, mtr);
+
+ flst_init(header + FSP_FREE, mtr);
+ flst_init(header + FSP_FREE_FRAG, mtr);
+ flst_init(header + FSP_FULL_FRAG, mtr);
+ flst_init(header + FSP_SEG_INODES_FULL, mtr);
+ flst_init(header + FSP_SEG_INODES_FREE, mtr);
+
+ mlog_write_dulint(header + FSP_SEG_ID, ut_dulint_create(0, 1),
+ MLOG_8BYTES, mtr);
+ fsp_fill_free_list(space, header, mtr);
+
+ btr_create(DICT_CLUSTERED | DICT_UNIVERSAL | DICT_IBUF, space,
+ ut_dulint_add(DICT_IBUF_ID_MIN, space), mtr);
+}
+
+/**************************************************************************
+Increases the space size field of a space. */
+
+void
+fsp_header_inc_size(
+/*================*/
+ ulint space, /* in: space id */
+ ulint size_inc,/* in: size increment in pages */
+ mtr_t* mtr) /* in: mini-transaction handle */
+{
+ fsp_header_t* header;
+ ulint size;
+
+ ut_ad(mtr);
+
+ mtr_x_lock(fil_space_get_latch(space), mtr);
+
+ header = fsp_get_space_header(space, mtr);
+
+ size = mtr_read_ulint(header + FSP_SIZE, MLOG_4BYTES, mtr);
+
+ mlog_write_ulint(header + FSP_SIZE, size + size_inc, MLOG_4BYTES, mtr);
+}
+
+/**************************************************************************
+Puts new extents to the free list if there are free extents above the free
+limit. If an extent happens to contain an extent descriptor page, the extent
+is put to the FSP_FREE_FRAG list with the page marked as used. */
+static
+void
+fsp_fill_free_list(
+/*===============*/
+ ulint space, /* in: space */
+ fsp_header_t* header, /* in: space header */
+ mtr_t* mtr) /* in: mtr */
+{
+ ulint limit;
+ ulint size;
+ xdes_t* descr;
+ ulint count = 0;
+ ulint frag_n_used;
+ page_t* descr_page;
+ page_t* ibuf_page;
+ mtr_t ibuf_mtr;
+ ulint i;
+
+ ut_ad(header && mtr);
+
+ /* Check if we can fill free list from above the free list limit */
+ size = mtr_read_ulint(header + FSP_SIZE, MLOG_4BYTES, mtr);
+ limit = mtr_read_ulint(header + FSP_FREE_LIMIT, MLOG_4BYTES, mtr);
+
+ i = limit;
+
+ while ((i + FSP_EXTENT_SIZE <= size) && (count < FSP_FREE_ADD)) {
+
+ mlog_write_ulint(header + FSP_FREE_LIMIT, i + FSP_EXTENT_SIZE,
+ MLOG_4BYTES, mtr);
+ if (0 == i % XDES_DESCRIBED_PER_PAGE) {
+
+ /* We are going to initialize a new descriptor page
+ and a new ibuf bitmap page: the prior contents of the
+ pages should be ignored. */
+
+ if (i > 0) {
+ descr_page = buf_page_create(space, i, mtr);
+ buf_page_dbg_add_level(descr_page,
+ SYNC_FSP_PAGE);
+ buf_page_get(space, i, RW_X_LATCH, mtr);
+ buf_page_dbg_add_level(descr_page,
+ SYNC_FSP_PAGE);
+ fsp_init_file_page(descr_page, mtr);
+ }
+
+ /* Initialize the ibuf page in a separate
+ mini-transaction because it is low in the latching
+ order, and we must be able to release the its latch
+ before returning from the fsp routine */
+
+ mtr_start(&ibuf_mtr);
+
+ ibuf_page = buf_page_create(space,
+ i + FSP_IBUF_BITMAP_OFFSET, &ibuf_mtr);
+ buf_page_dbg_add_level(ibuf_page, SYNC_IBUF_BITMAP);
+
+ buf_page_get(space, i + FSP_IBUF_BITMAP_OFFSET,
+ RW_X_LATCH, &ibuf_mtr);
+ buf_page_dbg_add_level(ibuf_page, SYNC_FSP_PAGE);
+
+ fsp_init_file_page(ibuf_page, &ibuf_mtr);
+
+ ibuf_bitmap_page_init(ibuf_page, &ibuf_mtr);
+
+ mtr_commit(&ibuf_mtr);
+ }
+
+ descr = xdes_get_descriptor_with_space_hdr(header, space, i,
+ mtr);
+ xdes_init(descr, mtr);
+
+ ut_ad(XDES_DESCRIBED_PER_PAGE % FSP_EXTENT_SIZE == 0);
+
+ if (0 == i % XDES_DESCRIBED_PER_PAGE) {
+
+ /* The first page in the extent is a descriptor page
+ and the second is an ibuf bitmap page: mark them
+ used */
+
+ xdes_set_bit(descr, XDES_FREE_BIT, 0, FALSE, mtr);
+ xdes_set_bit(descr, XDES_FREE_BIT,
+ FSP_IBUF_BITMAP_OFFSET, FALSE, mtr);
+ xdes_set_state(descr, XDES_FREE_FRAG, mtr);
+
+ flst_add_last(header + FSP_FREE_FRAG,
+ descr + XDES_FLST_NODE, mtr);
+ frag_n_used = mtr_read_ulint(header + FSP_FRAG_N_USED,
+ MLOG_4BYTES, mtr);
+ mlog_write_ulint(header + FSP_FRAG_N_USED,
+ frag_n_used + 2, MLOG_4BYTES, mtr);
+ } else {
+ flst_add_last(header + FSP_FREE,
+ descr + XDES_FLST_NODE, mtr);
+ count++;
+ }
+
+ i += FSP_EXTENT_SIZE;
+ }
+}
+
+/**************************************************************************
+Allocates a new free extent. */
+static
+xdes_t*
+fsp_alloc_free_extent(
+/*==================*/
+ /* out: extent descriptor, NULL if cannot be
+ allocated */
+ ulint space, /* in: space id */
+ ulint hint, /* in: hint of which extent would be desirable: any
+ page offset in the extent goes; the hint must not
+ be > FSP_FREE_LIMIT */
+ mtr_t* mtr) /* in: mtr */
+{
+ fsp_header_t* header;
+ fil_addr_t first;
+ xdes_t* descr;
+
+ ut_ad(mtr);
+
+ header = fsp_get_space_header(space, mtr);
+
+ descr = xdes_get_descriptor_with_space_hdr(header, space, hint, mtr);
+
+ if (descr && (xdes_get_state(descr, mtr) == XDES_FREE)) {
+ /* Ok, we can take this extent */
+ } else {
+ /* Take the first extent in the free list */
+ first = flst_get_first(header + FSP_FREE, mtr);
+
+ if (fil_addr_is_null(first)) {
+ fsp_fill_free_list(space, header, mtr);
+
+ first = flst_get_first(header + FSP_FREE, mtr);
+ }
+
+ if (fil_addr_is_null(first)) {
+
+ return(NULL); /* No free extents left */
+ }
+
+ descr = xdes_lst_get_descriptor(space, first, mtr);
+ }
+
+ flst_remove(header + FSP_FREE, descr + XDES_FLST_NODE, mtr);
+
+ return(descr);
+}
+
+/**************************************************************************
+Allocates a single free page from a space. The page is marked as used. */
+static
+ulint
+fsp_alloc_free_page(
+/*================*/
+ /* out: the page offset, FIL_NULL if no page could
+ be allocated */
+ ulint space, /* in: space id */
+ ulint hint, /* in: hint of which page would be desirable */
+ mtr_t* mtr) /* in: mtr handle */
+{
+ fsp_header_t* header;
+ fil_addr_t first;
+ xdes_t* descr;
+ page_t* page;
+ ulint free;
+ ulint frag_n_used;
+ ulint page_no;
+
+ ut_ad(mtr);
+
+ header = fsp_get_space_header(space, mtr);
+
+ /* Get the hinted descriptor */
+ descr = xdes_get_descriptor_with_space_hdr(header, space, hint, mtr);
+
+ if (descr && (xdes_get_state(descr, mtr) == XDES_FREE_FRAG)) {
+ /* Ok, we can take this extent */
+ } else {
+ /* Else take the first extent in free_frag list */
+ first = flst_get_first(header + FSP_FREE_FRAG, mtr);
+
+ if (fil_addr_is_null(first)) {
+ /* There are no partially full fragments: allocate
+ a free extent and add it to the FREE_FRAG list. NOTE
+ that the allocation may have as a side-effect that an
+ extent containing a descriptor page is added to the
+ FREE_FRAG list. But we will allocate our page from the
+ the free extent anyway. */
+
+ descr = fsp_alloc_free_extent(space, hint, mtr);
+
+ if (descr == NULL) {
+ /* No free space left */
+
+ return(FIL_NULL);
+ }
+
+ xdes_set_state(descr, XDES_FREE_FRAG, mtr);
+ flst_add_last(header + FSP_FREE_FRAG,
+ descr + XDES_FLST_NODE, mtr);
+ } else {
+ descr = xdes_lst_get_descriptor(space, first, mtr);
+ }
+
+ /* Reset the hint */
+ hint = 0;
+ }
+
+ /* Now we have in descr an extent with at least one free page. Look
+ for a free page in the extent. */
+
+ free = xdes_find_bit(descr, XDES_FREE_BIT, TRUE,
+ hint % FSP_EXTENT_SIZE, mtr);
+ ut_a(free != ULINT_UNDEFINED);
+
+ xdes_set_bit(descr, XDES_FREE_BIT, free, FALSE, mtr);
+
+ /* Update the FRAG_N_USED field */
+ frag_n_used = mtr_read_ulint(header + FSP_FRAG_N_USED, MLOG_4BYTES,
+ mtr);
+ frag_n_used++;
+ mlog_write_ulint(header + FSP_FRAG_N_USED, frag_n_used, MLOG_4BYTES,
+ mtr);
+ if (xdes_is_full(descr, mtr)) {
+ /* The fragment is full: move it to another list */
+ flst_remove(header + FSP_FREE_FRAG, descr + XDES_FLST_NODE,
+ mtr);
+ xdes_set_state(descr, XDES_FULL_FRAG, mtr);
+
+ flst_add_last(header + FSP_FULL_FRAG, descr + XDES_FLST_NODE,
+ mtr);
+ mlog_write_ulint(header + FSP_FRAG_N_USED,
+ frag_n_used - FSP_EXTENT_SIZE, MLOG_4BYTES,
+ mtr);
+ }
+
+ page_no = xdes_get_offset(descr) + free;
+
+ /* Initialize the allocated page to the buffer pool, so that it can
+ be obtained immediately with buf_page_get without need for a disk
+ read. */
+
+ buf_page_create(space, page_no, mtr);
+
+ page = buf_page_get(space, page_no, RW_X_LATCH, mtr);
+
+ buf_page_dbg_add_level(page, SYNC_FSP_PAGE);
+
+ /* Prior contents of the page should be ignored */
+ fsp_init_file_page(page, mtr);
+
+ return(page_no);
+}
+
+/**************************************************************************
+Frees a single page of a space. The page is marked as free and clean. */
+static
+void
+fsp_free_page(
+/*==========*/
+ ulint space, /* in: space id */
+ ulint page, /* in: page offset */
+ mtr_t* mtr) /* in: mtr handle */
+{
+ fsp_header_t* header;
+ xdes_t* descr;
+ ulint state;
+ ulint frag_n_used;
+
+ ut_ad(mtr);
+
+/* printf("Freeing page %lu in space %lu\n", page, space); */
+
+ header = fsp_get_space_header(space, mtr);
+
+ descr = xdes_get_descriptor_with_space_hdr(header, space, page, mtr);
+
+ state = xdes_get_state(descr, mtr);
+
+ ut_a((state == XDES_FREE_FRAG) || (state == XDES_FULL_FRAG));
+
+ ut_a(xdes_get_bit(descr, XDES_FREE_BIT, page % FSP_EXTENT_SIZE, mtr)
+ == FALSE);
+
+ xdes_set_bit(descr, XDES_FREE_BIT, page % FSP_EXTENT_SIZE, TRUE, mtr);
+ xdes_set_bit(descr, XDES_CLEAN_BIT, page % FSP_EXTENT_SIZE, TRUE, mtr);
+
+ frag_n_used = mtr_read_ulint(header + FSP_FRAG_N_USED, MLOG_4BYTES,
+ mtr);
+ if (state == XDES_FULL_FRAG) {
+ /* The fragment was full: move it to another list */
+ flst_remove(header + FSP_FULL_FRAG, descr + XDES_FLST_NODE,
+ mtr);
+ xdes_set_state(descr, XDES_FREE_FRAG, mtr);
+ flst_add_last(header + FSP_FREE_FRAG, descr + XDES_FLST_NODE,
+ mtr);
+ mlog_write_ulint(header + FSP_FRAG_N_USED,
+ frag_n_used + FSP_EXTENT_SIZE - 1,
+ MLOG_4BYTES, mtr);
+ } else {
+ ut_a(frag_n_used > 0);
+ mlog_write_ulint(header + FSP_FRAG_N_USED, frag_n_used - 1,
+ MLOG_4BYTES, mtr);
+ }
+
+ if (xdes_is_free(descr, mtr)) {
+ /* The extent has become free: move it to another list */
+ flst_remove(header + FSP_FREE_FRAG, descr + XDES_FLST_NODE,
+ mtr);
+ fsp_free_extent(space, page, mtr);
+ }
+}
+
+/**************************************************************************
+Returns an extent to the free list of a space. */
+static
+void
+fsp_free_extent(
+/*============*/
+ ulint space, /* in: space id */
+ ulint page, /* in: page offset in the extent */
+ mtr_t* mtr) /* in: mtr */
+{
+ fsp_header_t* header;
+ xdes_t* descr;
+
+ ut_ad(mtr);
+
+ header = fsp_get_space_header(space, mtr);
+
+ descr = xdes_get_descriptor_with_space_hdr(header, space, page, mtr);
+
+ ut_a(xdes_get_state(descr, mtr) != XDES_FREE);
+
+ xdes_init(descr, mtr);
+
+ flst_add_last(header + FSP_FREE, descr + XDES_FLST_NODE, mtr);
+}
+
+/**************************************************************************
+Returns the nth inode slot on an inode page. */
+UNIV_INLINE
+fseg_inode_t*
+fsp_seg_inode_page_get_nth_inode(
+/*=============================*/
+ /* out: segment inode */
+ page_t* page, /* in: segment inode page */
+ ulint i, /* in: inode index on page */
+ mtr_t* mtr) /* in: mini-transaction handle */
+{
+ ut_ad(i < FSP_SEG_INODES_PER_PAGE);
+ ut_ad(mtr_memo_contains(mtr, buf_block_align(page),
+ MTR_MEMO_PAGE_X_FIX));
+
+ return(page + FSEG_ARR_OFFSET + FSEG_INODE_SIZE * i);
+}
+
+/**************************************************************************
+Looks for a used segment inode on a segment inode page. */
+static
+ulint
+fsp_seg_inode_page_find_used(
+/*=========================*/
+ /* out: segment inode index, or ULINT_UNDEFINED
+ if not found */
+ page_t* page, /* in: segment inode page */
+ mtr_t* mtr) /* in: mini-transaction handle */
+{
+ ulint i;
+ fseg_inode_t* inode;
+
+ for (i = 0; i < FSP_SEG_INODES_PER_PAGE; i++) {
+
+ inode = fsp_seg_inode_page_get_nth_inode(page, i, mtr);
+
+ if (ut_dulint_cmp(mach_read_from_8(inode + FSEG_ID),
+ ut_dulint_zero) != 0) {
+ /* This is used */
+
+ return(i);
+ }
+ }
+
+ return(ULINT_UNDEFINED);
+}
+
+/**************************************************************************
+Looks for an unused segment inode on a segment inode page. */
+static
+ulint
+fsp_seg_inode_page_find_free(
+/*=========================*/
+ /* out: segment inode index, or ULINT_UNDEFINED
+ if not found */
+ page_t* page, /* in: segment inode page */
+ ulint j, /* in: search forward starting from this index */
+ mtr_t* mtr) /* in: mini-transaction handle */
+{
+ ulint i;
+ fseg_inode_t* inode;
+
+ for (i = j; i < FSP_SEG_INODES_PER_PAGE; i++) {
+
+ inode = fsp_seg_inode_page_get_nth_inode(page, i, mtr);
+
+ if (ut_dulint_cmp(mach_read_from_8(inode + FSEG_ID),
+ ut_dulint_zero) == 0) {
+ /* This is unused */
+
+ return(i);
+ }
+ }
+
+ return(ULINT_UNDEFINED);
+}
+
+/**************************************************************************
+Allocates a new file segment inode page. */
+static
+ibool
+fsp_alloc_seg_inode_page(
+/*=====================*/
+ /* out: TRUE if could be allocated */
+ fsp_header_t* space_header, /* in: space header */
+ mtr_t* mtr) /* in: mini-transaction handle */
+{
+ fseg_inode_t* inode;
+ page_t* page;
+ ulint page_no;
+ ulint space;
+ ulint i;
+
+ space = buf_frame_get_space_id(space_header);
+
+ page_no = fsp_alloc_free_page(space, 0, mtr);
+
+ if (page_no == FIL_NULL) {
+
+ return(FALSE);
+ }
+
+ page = buf_page_get(space, page_no, RW_X_LATCH, mtr);
+
+ buf_page_dbg_add_level(page, SYNC_FSP_PAGE);
+
+ for (i = 0; i < FSP_SEG_INODES_PER_PAGE; i++) {
+
+ inode = fsp_seg_inode_page_get_nth_inode(page, i, mtr);
+
+ mlog_write_dulint(inode + FSEG_ID, ut_dulint_zero,
+ MLOG_8BYTES, mtr);
+ }
+
+ flst_add_last(space_header + FSP_SEG_INODES_FREE,
+ page + FSEG_INODE_PAGE_NODE, mtr);
+ return(TRUE);
+}
+
+/**************************************************************************
+Allocates a new file segment inode. */
+static
+fseg_inode_t*
+fsp_alloc_seg_inode(
+/*================*/
+ /* out: segment inode, or NULL if
+ not enough space */
+ fsp_header_t* space_header, /* in: space header */
+ mtr_t* mtr) /* in: mini-transaction handle */
+{
+ ulint page_no;
+ page_t* page;
+ fseg_inode_t* inode;
+ ibool success;
+ ulint n;
+
+ if (flst_get_len(space_header + FSP_SEG_INODES_FREE, mtr) == 0) {
+ /* Allocate a new segment inode page */
+
+ success = fsp_alloc_seg_inode_page(space_header, mtr);
+
+ if (!success) {
+
+ return(NULL);
+ }
+ }
+
+ page_no = flst_get_first(space_header + FSP_SEG_INODES_FREE, mtr).page;
+
+ page = buf_page_get(buf_frame_get_space_id(space_header), page_no,
+ RW_X_LATCH, mtr);
+ buf_page_dbg_add_level(page, SYNC_FSP_PAGE);
+
+ n = fsp_seg_inode_page_find_free(page, 0, mtr);
+
+ ut_a(n != ULINT_UNDEFINED);
+
+ inode = fsp_seg_inode_page_get_nth_inode(page, n, mtr);
+
+ if (ULINT_UNDEFINED == fsp_seg_inode_page_find_free(page, n + 1, mtr)) {
+
+ /* There are no other unused headers left on the page: move it
+ to another list */
+
+ flst_remove(space_header + FSP_SEG_INODES_FREE,
+ page + FSEG_INODE_PAGE_NODE, mtr);
+
+ flst_add_last(space_header + FSP_SEG_INODES_FULL,
+ page + FSEG_INODE_PAGE_NODE, mtr);
+ }
+
+ return(inode);
+}
+
+/**************************************************************************
+Frees a file segment inode. */
+static
+void
+fsp_free_seg_inode(
+/*===============*/
+ ulint space, /* in: space id */
+ fseg_inode_t* inode, /* in: segment inode */
+ mtr_t* mtr) /* in: mini-transaction handle */
+{
+ page_t* page;
+ fsp_header_t* space_header;
+
+ page = buf_frame_align(inode);
+
+ space_header = fsp_get_space_header(space, mtr);
+
+ ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE);
+
+ if (ULINT_UNDEFINED == fsp_seg_inode_page_find_free(page, 0, mtr)) {
+
+ /* Move the page to another list */
+
+ flst_remove(space_header + FSP_SEG_INODES_FULL,
+ page + FSEG_INODE_PAGE_NODE, mtr);
+
+ flst_add_last(space_header + FSP_SEG_INODES_FREE,
+ page + FSEG_INODE_PAGE_NODE, mtr);
+ }
+
+ mlog_write_dulint(inode + FSEG_ID, ut_dulint_zero, MLOG_8BYTES, mtr);
+ mlog_write_ulint(inode + FSEG_MAGIC_N, 0, MLOG_4BYTES, mtr);
+
+ if (ULINT_UNDEFINED == fsp_seg_inode_page_find_used(page, mtr)) {
+
+ /* There are no other used headers left on the page: free it */
+
+ flst_remove(space_header + FSP_SEG_INODES_FREE,
+ page + FSEG_INODE_PAGE_NODE, mtr);
+
+ fsp_free_page(space, buf_frame_get_page_no(page), mtr);
+ }
+}
+
+/**************************************************************************
+Returns the file segment inode, page x-latched. */
+static
+fseg_inode_t*
+fseg_inode_get(
+/*===========*/
+ /* out: segment inode, page x-latched */
+ fseg_header_t* header, /* in: segment header */
+ mtr_t* mtr) /* in: mtr handle */
+{
+ fil_addr_t inode_addr;
+ fseg_inode_t* inode;
+
+ inode_addr.page = mach_read_from_4(header + FSEG_HDR_PAGE_NO);
+ inode_addr.boffset = mach_read_from_2(header + FSEG_HDR_OFFSET);
+
+ inode = fut_get_ptr(mach_read_from_4(header + FSEG_HDR_SPACE),
+ inode_addr, RW_X_LATCH, mtr);
+
+ ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE);
+
+ return(inode);
+}
+
+/**************************************************************************
+Gets the page number from the nth fragment page slot. */
+UNIV_INLINE
+ulint
+fseg_get_nth_frag_page_no(
+/*======================*/
+ /* out: page number, FIL_NULL if not in use */
+ fseg_inode_t* inode, /* in: segment inode */
+ ulint n, /* in: slot index */
+ mtr_t* mtr) /* in: mtr handle */
+{
+ ut_ad(inode && mtr);
+ ut_ad(n < FSEG_FRAG_ARR_N_SLOTS);
+ ut_ad(mtr_memo_contains(mtr, buf_block_align(inode),
+ MTR_MEMO_PAGE_X_FIX));
+ return(mach_read_from_4(inode + FSEG_FRAG_ARR
+ + n * FSEG_FRAG_SLOT_SIZE));
+}
+
+/**************************************************************************
+Sets the page number in the nth fragment page slot. */
+UNIV_INLINE
+void
+fseg_set_nth_frag_page_no(
+/*======================*/
+ fseg_inode_t* inode, /* in: segment inode */
+ ulint n, /* in: slot index */
+ ulint page_no,/* in: page number to set */
+ mtr_t* mtr) /* in: mtr handle */
+{
+ ut_ad(inode && mtr);
+ ut_ad(n < FSEG_FRAG_ARR_N_SLOTS);
+ ut_ad(mtr_memo_contains(mtr, buf_block_align(inode),
+ MTR_MEMO_PAGE_X_FIX));
+
+ mlog_write_ulint(inode + FSEG_FRAG_ARR + n * FSEG_FRAG_SLOT_SIZE,
+ page_no, MLOG_4BYTES, mtr);
+}
+
+/**************************************************************************
+Finds a fragment page slot which is free. */
+static
+ulint
+fseg_find_free_frag_page_slot(
+/*==========================*/
+ /* out: slot index; ULINT_UNDEFINED if none
+ found */
+ fseg_inode_t* inode, /* in: segment inode */
+ mtr_t* mtr) /* in: mtr handle */
+{
+ ulint i;
+ ulint page_no;
+
+ ut_ad(inode && mtr);
+
+ for (i = 0; i < FSEG_FRAG_ARR_N_SLOTS; i++) {
+ page_no = fseg_get_nth_frag_page_no(inode, i, mtr);
+
+ if (page_no == FIL_NULL) {
+
+ return(i);
+ }
+ }
+
+ return(ULINT_UNDEFINED);
+}
+
+/**************************************************************************
+Finds a fragment page slot which is used and last in the array. */
+static
+ulint
+fseg_find_last_used_frag_page_slot(
+/*===============================*/
+ /* out: slot index; ULINT_UNDEFINED if none
+ found */
+ fseg_inode_t* inode, /* in: segment inode */
+ mtr_t* mtr) /* in: mtr handle */
+{
+ ulint i;
+ ulint page_no;
+
+ ut_ad(inode && mtr);
+
+ for (i = 0; i < FSEG_FRAG_ARR_N_SLOTS; i++) {
+ page_no = fseg_get_nth_frag_page_no(inode,
+ FSEG_FRAG_ARR_N_SLOTS - i - 1, mtr);
+
+ if (page_no != FIL_NULL) {
+
+ return(FSEG_FRAG_ARR_N_SLOTS - i - 1);
+ }
+ }
+
+ return(ULINT_UNDEFINED);
+}
+
+/**************************************************************************
+Calculates reserved fragment page slots. */
+static
+ulint
+fseg_get_n_frag_pages(
+/*==================*/
+ /* out: number of fragment pages */
+ fseg_inode_t* inode, /* in: segment inode */
+ mtr_t* mtr) /* in: mtr handle */
+{
+ ulint i;
+ ulint count = 0;
+
+ ut_ad(inode && mtr);
+
+ for (i = 0; i < FSEG_FRAG_ARR_N_SLOTS; i++) {
+ if (FIL_NULL != fseg_get_nth_frag_page_no(inode, i, mtr)) {
+ count++;
+ }
+ }
+
+ return(count);
+}
+
+/**************************************************************************
+Creates a new segment. */
+
+page_t*
+fseg_create_general(
+/*================*/
+ /* out: the page where the segment header is placed,
+ x-latched, NULL if could not create segment
+ because of lack of space */
+ ulint space, /* in: space id */
+ ulint page, /* in: page where the segment header is placed: if
+ this is != 0, the page must belong to another segment,
+ if this is 0, a new page will be allocated and it
+ will belong to the created segment */
+ ulint byte_offset, /* in: byte offset of the created segment header
+ on the page */
+ ibool has_done_reservation, /* in: TRUE if the caller has
+ already done the reservation for the pages
+ with fsp_reserve_free_extents (at least 2 extents:
+ one for the inode and, then there other for the
+ segment) is no need to do the check for this
+ individual operation */
+ mtr_t* mtr) /* in: mtr */
+{
+ fsp_header_t* space_header;
+ fseg_inode_t* inode;
+ dulint seg_id;
+ fseg_header_t* header;
+ rw_lock_t* latch;
+ ibool success;
+ page_t* ret = NULL;
+ ulint i;
+
+ ut_ad(mtr);
+
+ if (page != 0) {
+ header = byte_offset + buf_page_get(space, page, RW_X_LATCH,
+ mtr);
+ }
+
+ ut_ad(!mutex_own(&kernel_mutex)
+ || mtr_memo_contains(mtr, fil_space_get_latch(space),
+ MTR_MEMO_X_LOCK));
+ latch = fil_space_get_latch(space);
+
+ mtr_x_lock(latch, mtr);
+
+ if (rw_lock_get_x_lock_count(latch) == 1) {
+ /* This thread did not own the latch before this call: free
+ excess pages from the insert buffer free list */
+
+ ibuf_free_excess_pages(space);
+ }
+
+ if (!has_done_reservation) {
+ success = fsp_reserve_free_extents(space, 2, FSP_NORMAL, mtr);
+
+ if (!success) {
+ return(NULL);
+ }
+ }
+
+ space_header = fsp_get_space_header(space, mtr);
+
+ inode = fsp_alloc_seg_inode(space_header, mtr);
+
+ if (inode == NULL) {
+
+ goto funct_exit;
+ }
+
+ /* Read the next segment id from space header and increment the
+ value in space header */
+
+ seg_id = mtr_read_dulint(space_header + FSP_SEG_ID, MLOG_8BYTES, mtr);
+
+ mlog_write_dulint(space_header + FSP_SEG_ID, ut_dulint_add(seg_id, 1),
+ MLOG_8BYTES, mtr);
+
+ mlog_write_dulint(inode + FSEG_ID, seg_id, MLOG_8BYTES, mtr);
+ mlog_write_ulint(inode + FSEG_NOT_FULL_N_USED, 0, MLOG_4BYTES, mtr);
+
+ flst_init(inode + FSEG_FREE, mtr);
+ flst_init(inode + FSEG_NOT_FULL, mtr);
+ flst_init(inode + FSEG_FULL, mtr);
+
+ mlog_write_ulint(inode + FSEG_MAGIC_N, FSEG_MAGIC_N_VALUE,
+ MLOG_4BYTES, mtr);
+ for (i = 0; i < FSEG_FRAG_ARR_N_SLOTS; i++) {
+ fseg_set_nth_frag_page_no(inode, i, FIL_NULL, mtr);
+ }
+
+ if (page == 0) {
+ page = fseg_alloc_free_page_low(space, inode, 0, FSP_UP, mtr);
+
+ if (page == FIL_NULL) {
+
+ fsp_free_seg_inode(space, inode, mtr);
+
+ goto funct_exit;
+ }
+
+ header = byte_offset
+ + buf_page_get(space, page, RW_X_LATCH, mtr);
+ }
+
+ mlog_write_ulint(header + FSEG_HDR_OFFSET,
+ inode - buf_frame_align(inode), MLOG_2BYTES, mtr);
+
+ mlog_write_ulint(header + FSEG_HDR_PAGE_NO,
+ buf_frame_get_page_no(inode), MLOG_4BYTES, mtr);
+
+ mlog_write_ulint(header + FSEG_HDR_SPACE, space, MLOG_4BYTES, mtr);
+
+ ret = buf_frame_align(header);
+
+funct_exit:
+ if (!has_done_reservation) {
+
+ fil_space_release_free_extents(space, 2);
+ }
+
+ return(ret);
+}
+
+/**************************************************************************
+Creates a new segment. */
+
+page_t*
+fseg_create(
+/*========*/
+ /* out: the page where the segment header is placed,
+ x-latched, NULL if could not create segment
+ because of lack of space */
+ ulint space, /* in: space id */
+ ulint page, /* in: page where the segment header is placed: if
+ this is != 0, the page must belong to another segment,
+ if this is 0, a new page will be allocated and it
+ will belong to the created segment */
+ ulint byte_offset, /* in: byte offset of the created segment header
+ on the page */
+ mtr_t* mtr) /* in: mtr */
+{
+ return(fseg_create_general(space, page, byte_offset, FALSE, mtr));
+}
+
+/**************************************************************************
+Calculates the number of pages reserved by a segment, and how many pages are
+currently used. */
+static
+ulint
+fseg_n_reserved_pages_low(
+/*======================*/
+ /* out: number of reserved pages */
+ fseg_inode_t* inode, /* in: segment inode */
+ ulint* used, /* out: number of pages used (<= reserved) */
+ mtr_t* mtr) /* in: mtr handle */
+{
+ ulint ret;
+
+ ut_ad(inode && used && mtr);
+ ut_ad(mtr_memo_contains(mtr, buf_block_align(inode),
+ MTR_MEMO_PAGE_X_FIX));
+
+ *used = mtr_read_ulint(inode + FSEG_NOT_FULL_N_USED, MLOG_4BYTES, mtr)
+ + FSP_EXTENT_SIZE * flst_get_len(inode + FSEG_FULL, mtr)
+ + fseg_get_n_frag_pages(inode, mtr);
+
+ ret = fseg_get_n_frag_pages(inode, mtr)
+ + FSP_EXTENT_SIZE * flst_get_len(inode + FSEG_FREE, mtr)
+ + FSP_EXTENT_SIZE * flst_get_len(inode + FSEG_NOT_FULL, mtr)
+ + FSP_EXTENT_SIZE * flst_get_len(inode + FSEG_FULL, mtr);
+
+ return(ret);
+}
+
+/**************************************************************************
+Calculates the number of pages reserved by a segment, and how many pages are
+currently used. */
+
+ulint
+fseg_n_reserved_pages(
+/*==================*/
+ /* out: number of reserved pages */
+ fseg_header_t* header, /* in: segment header */
+ ulint* used, /* out: number of pages used (<= reserved) */
+ mtr_t* mtr) /* in: mtr handle */
+{
+ ulint ret;
+ fseg_inode_t* inode;
+ ulint space;
+
+ space = buf_frame_get_space_id(header);
+
+ ut_ad(!mutex_own(&kernel_mutex)
+ || mtr_memo_contains(mtr, fil_space_get_latch(space),
+ MTR_MEMO_X_LOCK));
+ mtr_x_lock(fil_space_get_latch(space), mtr);
+
+ inode = fseg_inode_get(header, mtr);
+
+ ret = fseg_n_reserved_pages_low(inode, used, mtr);
+
+ return(ret);
+}
+
+/*************************************************************************
+Tries to fill the free list of a segment with consecutive free extents.
+This happens if the segment is big enough to allow extents in the free list,
+the free list is empty, and the extents can be allocated consecutively from
+the hint onward. */
+static
+void
+fseg_fill_free_list(
+/*================*/
+ fseg_inode_t* inode, /* in: segment inode */
+ ulint space, /* in: space id */
+ ulint hint, /* in: hint which extent would be good as
+ the first extent */
+ mtr_t* mtr) /* in: mtr */
+{
+ xdes_t* descr;
+ ulint i;
+ dulint seg_id;
+ ulint reserved;
+ ulint used;
+
+ ut_ad(inode && mtr);
+
+ reserved = fseg_n_reserved_pages_low(inode, &used, mtr);
+
+ if (reserved < FSEG_FREE_LIST_LIMIT * FSP_EXTENT_SIZE) {
+
+ /* The segment is too small to allow extents in free list */
+
+ return;
+ }
+
+ if (flst_get_len(inode + FSEG_FREE, mtr) > 0) {
+ /* Free list is not empty */
+
+ return;
+ }
+
+ for (i = 0; i < FSEG_FREE_LIST_MAX_LEN; i++) {
+ descr = xdes_get_descriptor(space, hint, mtr);
+
+ if ((descr == NULL) ||
+ (XDES_FREE != xdes_get_state(descr, mtr))) {
+
+ /* We cannot allocate the desired extent: stop */
+
+ return;
+ }
+
+ descr = fsp_alloc_free_extent(space, hint, mtr);
+
+ xdes_set_state(descr, XDES_FSEG, mtr);
+
+ seg_id = mtr_read_dulint(inode + FSEG_ID, MLOG_8BYTES, mtr);
+ mlog_write_dulint(descr + XDES_ID, seg_id, MLOG_8BYTES, mtr);
+
+ flst_add_last(inode + FSEG_FREE, descr + XDES_FLST_NODE, mtr);
+ hint += FSP_EXTENT_SIZE;
+ }
+}
+
+/*************************************************************************
+Allocates a free extent for the segment: looks first in the free list of the
+segment, then tries to allocate from the space free list. NOTE that the extent
+returned still resides in the segment free list, it is not yet taken off it! */
+static
+xdes_t*
+fseg_alloc_free_extent(
+/*===================*/
+ /* out: allocated extent, still placed in the
+ segment free list, NULL if could
+ not be allocated */
+ fseg_inode_t* inode, /* in: segment inode */
+ ulint space, /* in: space id */
+ mtr_t* mtr) /* in: mtr */
+{
+ xdes_t* descr;
+ dulint seg_id;
+ fil_addr_t first;
+
+ if (flst_get_len(inode + FSEG_FREE, mtr) > 0) {
+ /* Segment free list is not empty, allocate from it */
+
+ first = flst_get_first(inode + FSEG_FREE, mtr);
+
+ descr = xdes_lst_get_descriptor(space, first, mtr);
+ } else {
+ /* Segment free list was empty, allocate from space */
+ descr = fsp_alloc_free_extent(space, 0, mtr);
+
+ if (descr == NULL) {
+
+ return(NULL);
+ }
+
+ seg_id = mtr_read_dulint(inode + FSEG_ID, MLOG_8BYTES, mtr);
+
+ xdes_set_state(descr, XDES_FSEG, mtr);
+ mlog_write_dulint(descr + XDES_ID, seg_id, MLOG_8BYTES, mtr);
+ flst_add_last(inode + FSEG_FREE, descr + XDES_FLST_NODE, mtr);
+
+ /* Try to fill the segment free list */
+ fseg_fill_free_list(inode, space,
+ xdes_get_offset(descr) + FSP_EXTENT_SIZE, mtr);
+ }
+
+ return(descr);
+}
+
+/**************************************************************************
+Allocates a single free page from a segment. This function implements
+the intelligent allocation strategy which tries to minimize file space
+fragmentation. */
+static
+ulint
+fseg_alloc_free_page_low(
+/*=====================*/
+ /* out: the allocated page number, FIL_NULL
+ if no page could be allocated */
+ ulint space, /* in: space */
+ fseg_inode_t* seg_inode, /* in: segment inode */
+ ulint hint, /* in: hint of which page would be desirable */
+ byte direction, /* in: if the new page is needed because
+ of an index page split, and records are
+ inserted there in order, into which
+ direction they go alphabetically: FSP_DOWN,
+ FSP_UP, FSP_NO_DIR */
+ mtr_t* mtr) /* in: mtr handle */
+{
+ dulint seg_id;
+ ulint used;
+ ulint reserved;
+ fil_addr_t first;
+ xdes_t* descr; /* extent of the hinted page */
+ ulint ret_page; /* the allocated page offset, FIL_NULL
+ if could not be allocated */
+ xdes_t* ret_descr; /* the extent of the allocated page */
+ page_t* page;
+ ibool frag_page_allocated = FALSE;
+ ulint n;
+
+ ut_ad(mtr);
+ ut_ad((direction >= FSP_UP) && (direction <= FSP_NO_DIR));
+ ut_ad(mach_read_from_4(seg_inode + FSEG_MAGIC_N) ==
+ FSEG_MAGIC_N_VALUE);
+ seg_id = mtr_read_dulint(seg_inode + FSEG_ID, MLOG_8BYTES, mtr);
+
+ ut_ad(ut_dulint_cmp(seg_id, ut_dulint_zero) > 0);
+
+ reserved = fseg_n_reserved_pages_low(seg_inode, &used, mtr);
+
+ descr = xdes_get_descriptor(space, hint, mtr);
+
+ if (descr == NULL) {
+ /* Hint outside space or too high above free limit: reset
+ hint */
+ hint = 0;
+ descr = xdes_get_descriptor(space, hint, mtr);
+ }
+
+ /* In the big if-else below we look for ret_page and ret_descr */
+ /*-------------------------------------------------------------*/
+ if ((xdes_get_state(descr, mtr) == XDES_FSEG)
+ && (0 == ut_dulint_cmp(mtr_read_dulint(descr + XDES_ID,
+ MLOG_8BYTES, mtr),
+ seg_id))
+ && (xdes_get_bit(descr, XDES_FREE_BIT,
+ hint % FSP_EXTENT_SIZE, mtr) == TRUE)) {
+
+ /* 1. We can take the hinted page
+ =================================*/
+ ret_descr = descr;
+ ret_page = hint;
+ /*-------------------------------------------------------------*/
+ } else if ((xdes_get_state(descr, mtr) == XDES_FREE)
+ && ((reserved - used) < reserved / FSEG_FILLFACTOR)
+ && (used >= FSEG_FRAG_LIMIT)) {
+
+ /* 2. We allocate the free extent from space and can take
+ =========================================================
+ the hinted page
+ ===============*/
+ ret_descr = fsp_alloc_free_extent(space, hint, mtr);
+
+ ut_a(ret_descr == descr);
+
+ xdes_set_state(ret_descr, XDES_FSEG, mtr);
+ mlog_write_dulint(ret_descr + XDES_ID, seg_id, MLOG_8BYTES,
+ mtr);
+ flst_add_last(seg_inode + FSEG_FREE,
+ ret_descr + XDES_FLST_NODE, mtr);
+
+ /* Try to fill the segment free list */
+ fseg_fill_free_list(seg_inode, space,
+ hint + FSP_EXTENT_SIZE, mtr);
+ ret_page = hint;
+ /*-------------------------------------------------------------*/
+ } else if ((direction != FSP_NO_DIR)
+ && ((reserved - used) < reserved / FSEG_FILLFACTOR)
+ && (used >= FSEG_FRAG_LIMIT)
+ && (NULL != (ret_descr =
+ fseg_alloc_free_extent(seg_inode, space, mtr)))) {
+
+ /* 3. We take any free extent (which was already assigned above
+ ===============================================================
+ in the if-condition to ret_descr) and take the lowest or
+ ========================================================
+ highest page in it, depending on the direction
+ ==============================================*/
+ ret_page = xdes_get_offset(ret_descr);
+
+ if (direction == FSP_DOWN) {
+ ret_page += FSP_EXTENT_SIZE - 1;
+ }
+ /*-------------------------------------------------------------*/
+ } else if ((xdes_get_state(descr, mtr) == XDES_FSEG)
+ && (0 == ut_dulint_cmp(mtr_read_dulint(descr + XDES_ID,
+ MLOG_8BYTES, mtr),
+ seg_id))
+ && (!xdes_is_full(descr, mtr))) {
+
+ /* 4. We can take the page from the same extent as the
+ ======================================================
+ hinted page (and the extent already belongs to the
+ ==================================================
+ segment)
+ ========*/
+ ret_descr = descr;
+ ret_page = xdes_get_offset(ret_descr) +
+ xdes_find_bit(ret_descr, XDES_FREE_BIT, TRUE,
+ hint % FSP_EXTENT_SIZE, mtr);
+ /*-------------------------------------------------------------*/
+ } else if (reserved - used > 0) {
+ /* 5. We take any unused page from the segment
+ ==============================================*/
+ if (flst_get_len(seg_inode + FSEG_NOT_FULL, mtr) > 0) {
+ first = flst_get_first(seg_inode + FSEG_NOT_FULL,
+ mtr);
+ } else if (flst_get_len(seg_inode + FSEG_FREE, mtr) > 0) {
+ first = flst_get_first(seg_inode + FSEG_FREE, mtr);
+ } else {
+ ut_error;
+ }
+
+ ret_descr = xdes_lst_get_descriptor(space, first, mtr);
+ ret_page = xdes_get_offset(ret_descr) +
+ xdes_find_bit(ret_descr, XDES_FREE_BIT, TRUE,
+ 0, mtr);
+ /*-------------------------------------------------------------*/
+ } else if (used < FSEG_FRAG_LIMIT) {
+ /* 6. We allocate an individual page from the space
+ ===================================================*/
+ ret_page = fsp_alloc_free_page(space, hint, mtr);
+ ret_descr = NULL;
+
+ frag_page_allocated = TRUE;
+
+ if (ret_page != FIL_NULL) {
+ /* Put the page in the fragment page array of the
+ segment */
+ n = fseg_find_free_frag_page_slot(seg_inode, mtr);
+ ut_a(n != FIL_NULL);
+
+ fseg_set_nth_frag_page_no(seg_inode, n, ret_page,
+ mtr);
+ }
+ /*-------------------------------------------------------------*/
+ } else {
+ /* 7. We allocate a new extent and take its first page
+ ======================================================*/
+ ret_descr = fseg_alloc_free_extent(seg_inode, space, mtr);
+
+ if (ret_descr == NULL) {
+ ret_page = FIL_NULL;
+ } else {
+ ret_page = xdes_get_offset(ret_descr);
+ }
+ }
+
+ if (ret_page == FIL_NULL) {
+ /* Page could not be allocated */
+
+ return(FIL_NULL);
+ }
+
+ if (!frag_page_allocated) {
+
+ /* Initialize the allocated page to buffer pool, so that it
+ can be obtained immediately with buf_page_get without need
+ for a disk read */
+
+ page = buf_page_create(space, ret_page, mtr);
+
+ ut_a(page == buf_page_get(space, ret_page, RW_X_LATCH, mtr));
+
+ buf_page_dbg_add_level(page, SYNC_FSP_PAGE);
+
+ /* The prior contents of the page should be ignored */
+ fsp_init_file_page(page, mtr);
+
+ /* At this point we know the extent and the page offset.
+ The extent is still in the appropriate list (FSEG_NOT_FULL
+ or FSEG_FREE), and the page is not yet marked as used. */
+
+ ut_ad(xdes_get_descriptor(space, ret_page, mtr) == ret_descr);
+ ut_ad(xdes_get_bit(ret_descr, XDES_FREE_BIT,
+ ret_page % FSP_EXTENT_SIZE, mtr) == TRUE);
+
+ fseg_mark_page_used(seg_inode, space, ret_page, mtr);
+ }
+
+ return(ret_page);
+}
+
+/**************************************************************************
+Allocates a single free page from a segment. This function implements
+the intelligent allocation strategy which tries to minimize file space
+fragmentation. */
+
+ulint
+fseg_alloc_free_page_general(
+/*=========================*/
+ /* out: allocated page offset, FIL_NULL if no
+ page could be allocated */
+ fseg_header_t* seg_header,/* in: segment header */
+ ulint hint, /* in: hint of which page would be desirable */
+ byte direction,/* in: if the new page is needed because
+ of an index page split, and records are
+ inserted there in order, into which
+ direction they go alphabetically: FSP_DOWN,
+ FSP_UP, FSP_NO_DIR */
+ ibool has_done_reservation, /* in: TRUE if the caller has
+ already done the reservation for the page
+ with fsp_reserve_free_extents, then there
+ is no need to do the check for this individual
+ page */
+ mtr_t* mtr) /* in: mtr handle */
+{
+ fseg_inode_t* inode;
+ ulint space;
+ rw_lock_t* latch;
+ ibool success;
+ ulint page_no;
+
+ space = buf_frame_get_space_id(seg_header);
+
+ ut_ad(!mutex_own(&kernel_mutex)
+ || mtr_memo_contains(mtr, fil_space_get_latch(space),
+ MTR_MEMO_X_LOCK));
+ latch = fil_space_get_latch(space);
+
+ mtr_x_lock(latch, mtr);
+
+ if (rw_lock_get_x_lock_count(latch) == 1) {
+ /* This thread did not own the latch before this call: free
+ excess pages from the insert buffer free list */
+
+ ibuf_free_excess_pages(space);
+ }
+
+ inode = fseg_inode_get(seg_header, mtr);
+
+ if (!has_done_reservation) {
+ success = fsp_reserve_free_extents(space, 2, FSP_NORMAL, mtr);
+
+ if (!success) {
+ return(FIL_NULL);
+ }
+ }
+
+ page_no = fseg_alloc_free_page_low(buf_frame_get_space_id(inode),
+ inode, hint, direction, mtr);
+ if (!has_done_reservation) {
+ fil_space_release_free_extents(space, 2);
+ }
+
+ return(page_no);
+}
+
+/**************************************************************************
+Allocates a single free page from a segment. This function implements
+the intelligent allocation strategy which tries to minimize file space
+fragmentation. */
+
+ulint
+fseg_alloc_free_page(
+/*=================*/
+ /* out: allocated page offset, FIL_NULL if no
+ page could be allocated */
+ fseg_header_t* seg_header,/* in: segment header */
+ ulint hint, /* in: hint of which page would be desirable */
+ byte direction,/* in: if the new page is needed because
+ of an index page split, and records are
+ inserted there in order, into which
+ direction they go alphabetically: FSP_DOWN,
+ FSP_UP, FSP_NO_DIR */
+ mtr_t* mtr) /* in: mtr handle */
+{
+ return(fseg_alloc_free_page_general(seg_header, hint, direction,
+ FALSE, mtr));
+}
+
+/**************************************************************************
+Reserves free pages from a tablespace. All mini-transactions which may
+use several pages from the tablespace should call this function beforehand
+and reserve enough free extents so that they certainly will be able
+to do their operation, like a B-tree page split, fully. Reservations
+must be released with function fil_space_release_free_extents!
+
+The alloc_type below has the following meaning: FSP_NORMAL means an
+operation which will probably result in more space usage, like an
+insert in a B-tree; FSP_UNDO means allocation to undo logs: if we are
+deleting rows, then this allocation will in the long run result in
+less space usage (after a purge); FSP_CLEANING means allocation done
+in a physical record delete (like in a purge) or other cleaning operation
+which will result in less space usage in the long run. We prefer the latter
+two types of allocation: when space is scarce, FSP_NORMAL allocations
+will not succeed, but the latter two allocations will succeed, if possible.
+The purpose is to avoid dead end where the database is full but the
+user cannot free any space because these freeing operations temporarily
+reserve some space. */
+
+ibool
+fsp_reserve_free_extents(
+/*=====================*/
+ /* out: TRUE if we were able to make the reservation */
+ ulint space, /* in: space id */
+ ulint n_ext, /* in: number of extents to reserve */
+ ulint alloc_type,/* in: FSP_NORMAL, FSP_UNDO, or FSP_CLEANING */
+ mtr_t* mtr) /* in: mtr */
+{
+ fsp_header_t* space_header;
+ ulint n_free_list_ext;
+ ulint free_limit;
+ ulint size;
+ ulint n_free;
+ ulint n_free_up;
+ ulint reserve;
+ rw_lock_t* latch;
+
+ ut_ad(mtr);
+ ut_ad(!mutex_own(&kernel_mutex)
+ || mtr_memo_contains(mtr, fil_space_get_latch(space),
+ MTR_MEMO_X_LOCK));
+ latch = fil_space_get_latch(space);
+
+ mtr_x_lock(latch, mtr);
+
+ space_header = fsp_get_space_header(space, mtr);
+
+ size = mtr_read_ulint(space_header + FSP_SIZE, MLOG_4BYTES, mtr);
+
+ n_free_list_ext = flst_get_len(space_header + FSP_FREE, mtr);
+
+ free_limit = mtr_read_ulint(space_header + FSP_FREE_LIMIT,
+ MLOG_4BYTES, mtr);
+
+ /* Below we play safe when counting free extents above the free limit:
+ some of them will contain extent descriptor pages, and therefore
+ will not be free extents */
+
+ n_free_up = (size - free_limit) / FSP_EXTENT_SIZE;
+
+ if (n_free_up > 0) {
+ n_free_up--;
+ n_free_up = n_free_up - n_free_up
+ / (XDES_DESCRIBED_PER_PAGE / FSP_EXTENT_SIZE);
+ }
+
+ n_free = n_free_list_ext + n_free_up;
+
+ if (alloc_type == FSP_NORMAL) {
+ /* We reserve 1 extent + 4 % of the space size to undo logs
+ and 1 extent + 1 % to cleaning operations; NOTE: this source
+ code is duplicated in the function below! */
+
+ reserve = 2 + ((size / FSP_EXTENT_SIZE) * 5) / 100;
+
+ if (n_free <= reserve + n_ext) {
+
+ return(FALSE);
+ }
+ } else if (alloc_type == FSP_UNDO) {
+ /* We reserve 1 % of the space size to cleaning operations */
+
+ reserve = 1 + ((size / FSP_EXTENT_SIZE) * 1) / 100;
+
+ if (n_free <= reserve + n_ext) {
+
+ return(FALSE);
+ }
+ } else {
+ ut_a(alloc_type == FSP_CLEANING);
+ }
+
+ return(fil_space_reserve_free_extents(space, n_free, n_ext));
+}
+
+/**************************************************************************
+This function should be used to get information on how much we still
+will be able to insert new data to the database without running out the
+tablespace. Only free extents are taken into account and we also subtract
+the safety margin required by the above function fsp_reserve_free_extents. */
+
+ulint
+fsp_get_available_space_in_free_extents(
+/*====================================*/
+ /* out: available space in kB */
+ ulint space) /* in: space id */
+{
+ fsp_header_t* space_header;
+ ulint n_free_list_ext;
+ ulint free_limit;
+ ulint size;
+ ulint n_free;
+ ulint n_free_up;
+ ulint reserve;
+ rw_lock_t* latch;
+ mtr_t mtr;
+
+ ut_ad(!mutex_own(&kernel_mutex));
+
+ mtr_start(&mtr);
+
+ latch = fil_space_get_latch(space);
+
+ mtr_x_lock(latch, &mtr);
+
+ space_header = fsp_get_space_header(space, &mtr);
+
+ size = mtr_read_ulint(space_header + FSP_SIZE, MLOG_4BYTES, &mtr);
+
+ n_free_list_ext = flst_get_len(space_header + FSP_FREE, &mtr);
+
+ free_limit = mtr_read_ulint(space_header + FSP_FREE_LIMIT,
+ MLOG_4BYTES, &mtr);
+ mtr_commit(&mtr);
+
+ /* Below we play safe when counting free extents above the free limit:
+ some of them will contain extent descriptor pages, and therefore
+ will not be free extents */
+
+ n_free_up = (size - free_limit) / FSP_EXTENT_SIZE;
+
+ if (n_free_up > 0) {
+ n_free_up--;
+ n_free_up = n_free_up - n_free_up
+ / (XDES_DESCRIBED_PER_PAGE / FSP_EXTENT_SIZE);
+ }
+
+ n_free = n_free_list_ext + n_free_up;
+
+ /* We reserve 1 extent + 4 % of the space size to undo logs
+ and 1 extent + 1 % to cleaning operations; NOTE: this source
+ code is duplicated in the function above! */
+
+ reserve = 2 + ((size / FSP_EXTENT_SIZE) * 5) / 100;
+
+ if (reserve > n_free) {
+ return(0);
+ }
+
+ return(((n_free - reserve) * FSP_EXTENT_SIZE)
+ * (UNIV_PAGE_SIZE / 1024));
+}
+
+/************************************************************************
+Marks a page used. The page must reside within the extents of the given
+segment. */
+static
+void
+fseg_mark_page_used(
+/*================*/
+ fseg_inode_t* seg_inode,/* in: segment inode */
+ ulint space, /* in: space id */
+ ulint page, /* in: page offset */
+ mtr_t* mtr) /* in: mtr */
+{
+ xdes_t* descr;
+ ulint not_full_n_used;
+
+ ut_ad(seg_inode && mtr);
+
+ descr = xdes_get_descriptor(space, page, mtr);
+
+ ut_ad(mtr_read_ulint(seg_inode + FSEG_ID, MLOG_4BYTES, mtr) ==
+ mtr_read_ulint(descr + XDES_ID, MLOG_4BYTES, mtr));
+
+ if (xdes_is_free(descr, mtr)) {
+ /* We move the extent from the free list to the
+ NOT_FULL list */
+ flst_remove(seg_inode + FSEG_FREE, descr + XDES_FLST_NODE,
+ mtr);
+ flst_add_last(seg_inode + FSEG_NOT_FULL,
+ descr + XDES_FLST_NODE, mtr);
+ }
+
+ ut_ad(xdes_get_bit(descr, XDES_FREE_BIT, page % FSP_EXTENT_SIZE, mtr)
+ == TRUE);
+ /* We mark the page as used */
+ xdes_set_bit(descr, XDES_FREE_BIT, page % FSP_EXTENT_SIZE, FALSE, mtr);
+
+ not_full_n_used = mtr_read_ulint(seg_inode + FSEG_NOT_FULL_N_USED,
+ MLOG_4BYTES, mtr);
+ not_full_n_used++;
+ mlog_write_ulint(seg_inode + FSEG_NOT_FULL_N_USED, not_full_n_used,
+ MLOG_4BYTES, mtr);
+ if (xdes_is_full(descr, mtr)) {
+ /* We move the extent from the NOT_FULL list to the
+ FULL list */
+ flst_remove(seg_inode + FSEG_NOT_FULL,
+ descr + XDES_FLST_NODE, mtr);
+ flst_add_last(seg_inode + FSEG_FULL,
+ descr + XDES_FLST_NODE, mtr);
+
+ mlog_write_ulint(seg_inode + FSEG_NOT_FULL_N_USED,
+ not_full_n_used - FSP_EXTENT_SIZE,
+ MLOG_4BYTES, mtr);
+ }
+}
+
+/**************************************************************************
+Frees a single page of a segment. */
+static
+void
+fseg_free_page_low(
+/*===============*/
+ fseg_inode_t* seg_inode, /* in: segment inode */
+ ulint space, /* in: space id */
+ ulint page, /* in: page offset */
+ mtr_t* mtr) /* in: mtr handle */
+{
+ xdes_t* descr;
+ ulint not_full_n_used;
+ ulint state;
+ ulint i;
+
+ ut_ad(seg_inode && mtr);
+ ut_ad(mach_read_from_4(seg_inode + FSEG_MAGIC_N) ==
+ FSEG_MAGIC_N_VALUE);
+
+ /* Drop search system page hash index if the page is found in
+ the pool and is hashed */
+
+ btr_search_drop_page_hash_when_freed(space, page);
+
+ descr = xdes_get_descriptor(space, page, mtr);
+
+ ut_a(descr);
+ ut_a(xdes_get_bit(descr, XDES_FREE_BIT, page % FSP_EXTENT_SIZE, mtr)
+ == FALSE);
+ state = xdes_get_state(descr, mtr);
+
+ if (state != XDES_FSEG) {
+ /* The page is in the fragment pages of the segment */
+
+ for (i = 0;; i++) {
+ if (fseg_get_nth_frag_page_no(seg_inode, i, mtr)
+ == page) {
+
+ fseg_set_nth_frag_page_no(seg_inode, i,
+ FIL_NULL, mtr);
+ break;
+ }
+ }
+
+ fsp_free_page(space, page, mtr);
+
+ return;
+ }
+
+ /* If we get here, the page is in some extent of the segment */
+ ut_a(0 == ut_dulint_cmp(
+ mtr_read_dulint(descr + XDES_ID, MLOG_8BYTES, mtr),
+ mtr_read_dulint(seg_inode + FSEG_ID, MLOG_8BYTES, mtr)));
+
+ not_full_n_used = mtr_read_ulint(seg_inode + FSEG_NOT_FULL_N_USED,
+ MLOG_4BYTES, mtr);
+ if (xdes_is_full(descr, mtr)) {
+ /* The fragment is full: move it to another list */
+ flst_remove(seg_inode + FSEG_FULL,
+ descr + XDES_FLST_NODE, mtr);
+ flst_add_last(seg_inode + FSEG_NOT_FULL,
+ descr + XDES_FLST_NODE, mtr);
+ mlog_write_ulint(seg_inode + FSEG_NOT_FULL_N_USED,
+ not_full_n_used + FSP_EXTENT_SIZE - 1,
+ MLOG_4BYTES, mtr);
+ } else {
+ ut_a(not_full_n_used > 0);
+ mlog_write_ulint(seg_inode + FSEG_NOT_FULL_N_USED,
+ not_full_n_used - 1, MLOG_4BYTES, mtr);
+ }
+
+ xdes_set_bit(descr, XDES_FREE_BIT, page % FSP_EXTENT_SIZE, TRUE, mtr);
+ xdes_set_bit(descr, XDES_CLEAN_BIT, page % FSP_EXTENT_SIZE, TRUE, mtr);
+
+ if (xdes_is_free(descr, mtr)) {
+ /* The extent has become free: free it to space */
+ flst_remove(seg_inode + FSEG_NOT_FULL,
+ descr + XDES_FLST_NODE, mtr);
+ fsp_free_extent(space, page, mtr);
+ }
+}
+
+/**************************************************************************
+Frees a single page of a segment. */
+
+void
+fseg_free_page(
+/*===========*/
+ fseg_header_t* seg_header, /* in: segment header */
+ ulint space, /* in: space id */
+ ulint page, /* in: page offset */
+ mtr_t* mtr) /* in: mtr handle */
+{
+ fseg_inode_t* seg_inode;
+
+ ut_ad(!mutex_own(&kernel_mutex)
+ || mtr_memo_contains(mtr, fil_space_get_latch(space),
+ MTR_MEMO_X_LOCK));
+ mtr_x_lock(fil_space_get_latch(space), mtr);
+
+ seg_inode = fseg_inode_get(seg_header, mtr);
+
+ fseg_free_page_low(seg_inode, space, page, mtr);
+}
+
+/**************************************************************************
+Frees an extent of a segment to the space free list. */
+static
+void
+fseg_free_extent(
+/*=============*/
+ fseg_inode_t* seg_inode, /* in: segment inode */
+ ulint space, /* in: space id */
+ ulint page, /* in: a page in the extent */
+ mtr_t* mtr) /* in: mtr handle */
+{
+ ulint first_page_in_extent;
+ xdes_t* descr;
+ ulint not_full_n_used;
+ ulint descr_n_used;
+ ulint i;
+
+ ut_ad(seg_inode && mtr);
+
+ descr = xdes_get_descriptor(space, page, mtr);
+
+ ut_a(xdes_get_state(descr, mtr) == XDES_FSEG);
+ ut_a(0 == ut_dulint_cmp(
+ mtr_read_dulint(descr + XDES_ID, MLOG_8BYTES, mtr),
+ mtr_read_dulint(seg_inode + FSEG_ID, MLOG_8BYTES, mtr)));
+
+ first_page_in_extent = page - (page % FSP_EXTENT_SIZE);
+
+ for (i = 0; i < FSP_EXTENT_SIZE; i++) {
+ if (FALSE == xdes_get_bit(descr, XDES_FREE_BIT, i, mtr)) {
+
+ /* Drop search system page hash index if the page is
+ found in the pool and is hashed */
+
+ btr_search_drop_page_hash_when_freed(space,
+ first_page_in_extent + i);
+ }
+ }
+
+ if (xdes_is_full(descr, mtr)) {
+ flst_remove(seg_inode + FSEG_FULL,
+ descr + XDES_FLST_NODE, mtr);
+ } else if (xdes_is_free(descr, mtr)) {
+ flst_remove(seg_inode + FSEG_FREE,
+ descr + XDES_FLST_NODE, mtr);
+ } else {
+ flst_remove(seg_inode + FSEG_NOT_FULL,
+ descr + XDES_FLST_NODE, mtr);
+
+ not_full_n_used = mtr_read_ulint(
+ seg_inode + FSEG_NOT_FULL_N_USED,
+ MLOG_4BYTES, mtr);
+
+ descr_n_used = xdes_get_n_used(descr, mtr);
+ ut_a(not_full_n_used >= descr_n_used);
+ mlog_write_ulint(seg_inode + FSEG_NOT_FULL_N_USED,
+ not_full_n_used - descr_n_used,
+ MLOG_4BYTES, mtr);
+ }
+
+ fsp_free_extent(space, page, mtr);
+}
+
+/**************************************************************************
+Frees part of a segment. This function can be used to free a segment by
+repeatedly calling this function in different mini-transactions. Doing
+the freeing in a single mini-transaction might result in too big a
+mini-transaction. */
+
+ibool
+fseg_free_step(
+/*===========*/
+ /* out: TRUE if freeing completed */
+ fseg_header_t* header, /* in, own: segment header; NOTE: if the header
+ resides on the first page of the frag list
+ of the segment, this pointer becomes obsolete
+ after the last freeing step */
+ mtr_t* mtr) /* in: mtr */
+{
+ ulint n;
+ ulint page;
+ xdes_t* descr;
+ fseg_inode_t* inode;
+ ulint space;
+
+ ut_ad(!mutex_own(&kernel_mutex)
+ || mtr_memo_contains(mtr, fil_space_get_latch(space),
+ MTR_MEMO_X_LOCK));
+ space = buf_frame_get_space_id(header);
+
+ mtr_x_lock(fil_space_get_latch(space), mtr);
+
+ inode = fseg_inode_get(header, mtr);
+
+ descr = fseg_get_first_extent(inode, mtr);
+
+ if (descr != NULL) {
+ /* Free the extent held by the segment */
+ page = xdes_get_offset(descr);
+
+ fseg_free_extent(inode, space, page, mtr);
+
+ return(FALSE);
+ }
+
+ /* Free a frag page */
+
+ n = fseg_find_last_used_frag_page_slot(inode, mtr);
+
+ if (n == ULINT_UNDEFINED) {
+ /* Freeing completed: free the segment inode */
+ fsp_free_seg_inode(space, inode, mtr);
+
+ return(TRUE);
+ }
+
+ fseg_free_page_low(inode, space,
+ fseg_get_nth_frag_page_no(inode, n, mtr), mtr);
+ return(FALSE);
+}
+
+/**************************************************************************
+Frees part of a segment. Differs from fseg_free_step because this function
+leaves the header page unfreed. */
+
+ibool
+fseg_free_step_not_header(
+/*======================*/
+ /* out: TRUE if freeing completed, except the
+ header page */
+ fseg_header_t* header, /* in: segment header which must reside on
+ the first fragment page of the segment */
+ mtr_t* mtr) /* in: mtr */
+{
+ ulint n;
+ ulint page;
+ xdes_t* descr;
+ fseg_inode_t* inode;
+ ulint space;
+ ulint page_no;
+
+ ut_ad(!mutex_own(&kernel_mutex)
+ || mtr_memo_contains(mtr, fil_space_get_latch(space), MTR_MEMO_X_LOCK));
+
+ space = buf_frame_get_space_id(header);
+
+ mtr_x_lock(fil_space_get_latch(space), mtr);
+
+ inode = fseg_inode_get(header, mtr);
+
+ descr = fseg_get_first_extent(inode, mtr);
+
+ if (descr != NULL) {
+ /* Free the extent held by the segment */
+ page = xdes_get_offset(descr);
+
+ fseg_free_extent(inode, space, page, mtr);
+
+ return(FALSE);
+ }
+
+ /* Free a frag page */
+
+ n = fseg_find_last_used_frag_page_slot(inode, mtr);
+
+ if (n == ULINT_UNDEFINED) {
+ ut_error;
+ }
+
+ page_no = fseg_get_nth_frag_page_no(inode, n, mtr);
+
+ if (page_no == buf_frame_get_page_no(header)) {
+
+ return(TRUE);
+ }
+
+ fseg_free_page_low(inode, space, page_no, mtr);
+
+ return(FALSE);
+}
+
+/***********************************************************************
+Frees a segment. The freeing is performed in several mini-transactions,
+so that there is no danger of bufferfixing too many buffer pages. */
+
+void
+fseg_free(
+/*======*/
+ ulint space, /* in: space id */
+ ulint page_no,/* in: page number where the segment header is
+ placed */
+ ulint offset) /* in: byte offset of the segment header on that
+ page */
+{
+ mtr_t mtr;
+ ibool finished;
+ fseg_header_t* header;
+ fil_addr_t addr;
+
+ addr.page = page_no;
+ addr.boffset = offset;
+
+ for (;;) {
+ mtr_start(&mtr);
+
+ header = fut_get_ptr(space, addr, RW_X_LATCH, &mtr);
+
+ finished = fseg_free_step(header, &mtr);
+
+ mtr_commit(&mtr);
+
+ if (finished) {
+
+ return;
+ }
+ }
+}
+
+/**************************************************************************
+Returns the first extent descriptor for a segment. We think of the extent
+lists of the segment catenated in the order FSEG_FULL -> FSEG_NOT_FULL
+-> FSEG_FREE. */
+static
+xdes_t*
+fseg_get_first_extent(
+/*==================*/
+ /* out: the first extent descriptor, or NULL if
+ none */
+ fseg_inode_t* inode, /* in: segment inode */
+ mtr_t* mtr) /* in: mtr */
+{
+ fil_addr_t first;
+ ulint space;
+ xdes_t* descr;
+
+ ut_ad(inode && mtr);
+
+ space = buf_frame_get_space_id(inode);
+
+ first = fil_addr_null;
+
+ if (flst_get_len(inode + FSEG_FULL, mtr) > 0) {
+
+ first = flst_get_first(inode + FSEG_FULL, mtr);
+
+ } else if (flst_get_len(inode + FSEG_NOT_FULL, mtr) > 0) {
+
+ first = flst_get_first(inode + FSEG_NOT_FULL, mtr);
+
+ } else if (flst_get_len(inode + FSEG_FREE, mtr) > 0) {
+
+ first = flst_get_first(inode + FSEG_FREE, mtr);
+ }
+
+ if (first.page == FIL_NULL) {
+
+ return(NULL);
+ }
+ descr = xdes_lst_get_descriptor(space, first, mtr);
+
+ return(descr);
+}
+
+/***********************************************************************
+Validates a segment. */
+static
+ibool
+fseg_validate_low(
+/*==============*/
+ /* out: TRUE if ok */
+ fseg_inode_t* inode, /* in: segment inode */
+ mtr_t* mtr2) /* in: mtr */
+{
+ ulint space;
+ dulint seg_id;
+ mtr_t mtr;
+ xdes_t* descr;
+ fil_addr_t node_addr;
+ ulint n_used = 0;
+ ulint n_used2 = 0;
+
+ ut_ad(mtr_memo_contains(mtr2, buf_block_align(inode),
+ MTR_MEMO_PAGE_X_FIX));
+ ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE);
+
+ space = buf_frame_get_space_id(inode);
+
+ seg_id = mtr_read_dulint(inode + FSEG_ID, MLOG_8BYTES, mtr2);
+ n_used = mtr_read_ulint(inode + FSEG_NOT_FULL_N_USED,
+ MLOG_4BYTES, mtr2);
+ flst_validate(inode + FSEG_FREE, mtr2);
+ flst_validate(inode + FSEG_NOT_FULL, mtr2);
+ flst_validate(inode + FSEG_FULL, mtr2);
+
+ /* Validate FSEG_FREE list */
+ node_addr = flst_get_first(inode + FSEG_FREE, mtr2);
+
+ while (!fil_addr_is_null(node_addr)) {
+ mtr_start(&mtr);
+ mtr_x_lock(fil_space_get_latch(space), &mtr);
+
+ descr = xdes_lst_get_descriptor(space, node_addr, &mtr);
+
+ ut_a(xdes_get_n_used(descr, &mtr) == 0);
+ ut_a(xdes_get_state(descr, &mtr) == XDES_FSEG);
+ ut_a(0 == ut_dulint_cmp(
+ mtr_read_dulint(descr + XDES_ID, MLOG_8BYTES,
+ &mtr), seg_id));
+
+ node_addr = flst_get_next_addr(descr + XDES_FLST_NODE, &mtr);
+ mtr_commit(&mtr);
+ }
+
+ /* Validate FSEG_NOT_FULL list */
+
+ node_addr = flst_get_first(inode + FSEG_NOT_FULL, mtr2);
+
+ while (!fil_addr_is_null(node_addr)) {
+ mtr_start(&mtr);
+ mtr_x_lock(fil_space_get_latch(space), &mtr);
+
+ descr = xdes_lst_get_descriptor(space, node_addr, &mtr);
+
+ ut_a(xdes_get_n_used(descr, &mtr) > 0);
+ ut_a(xdes_get_n_used(descr, &mtr) < FSP_EXTENT_SIZE);
+ ut_a(xdes_get_state(descr, &mtr) == XDES_FSEG);
+ ut_a(0 == ut_dulint_cmp(
+ mtr_read_dulint(descr + XDES_ID, MLOG_8BYTES,
+ &mtr), seg_id));
+
+ n_used2 += xdes_get_n_used(descr, &mtr);
+
+ node_addr = flst_get_next_addr(descr + XDES_FLST_NODE, &mtr);
+ mtr_commit(&mtr);
+ }
+
+ /* Validate FSEG_FULL list */
+
+ node_addr = flst_get_first(inode + FSEG_FULL, mtr2);
+
+ while (!fil_addr_is_null(node_addr)) {
+ mtr_start(&mtr);
+ mtr_x_lock(fil_space_get_latch(space), &mtr);
+
+ descr = xdes_lst_get_descriptor(space, node_addr, &mtr);
+
+ ut_a(xdes_get_n_used(descr, &mtr) == FSP_EXTENT_SIZE);
+ ut_a(xdes_get_state(descr, &mtr) == XDES_FSEG);
+ ut_a(0 == ut_dulint_cmp(
+ mtr_read_dulint(descr + XDES_ID, MLOG_8BYTES,
+ &mtr), seg_id));
+
+ node_addr = flst_get_next_addr(descr + XDES_FLST_NODE, &mtr);
+ mtr_commit(&mtr);
+ }
+
+ ut_a(n_used == n_used2);
+
+ return(TRUE);
+}
+
+/***********************************************************************
+Validates a segment. */
+
+ibool
+fseg_validate(
+/*==========*/
+ /* out: TRUE if ok */
+ fseg_header_t* header, /* in: segment header */
+ mtr_t* mtr2) /* in: mtr */
+{
+ fseg_inode_t* inode;
+ ibool ret;
+ ulint space;
+
+ space = buf_frame_get_space_id(header);
+
+ mtr_x_lock(fil_space_get_latch(space), mtr2);
+
+ inode = fseg_inode_get(header, mtr2);
+
+ ret = fseg_validate_low(inode, mtr2);
+
+ return(ret);
+}
+
+/***********************************************************************
+Writes info of a segment. */
+static
+void
+fseg_print_low(
+/*===========*/
+ fseg_inode_t* inode, /* in: segment inode */
+ mtr_t* mtr) /* in: mtr */
+{
+ ulint space;
+ ulint seg_id_low;
+ ulint seg_id_high;
+ ulint n_used;
+ ulint n_frag;
+ ulint n_free;
+ ulint n_not_full;
+ ulint n_full;
+ ulint reserved;
+ ulint used;
+ ulint page_no;
+
+ ut_ad(mtr_memo_contains(mtr, buf_block_align(inode),
+ MTR_MEMO_PAGE_X_FIX));
+ space = buf_frame_get_space_id(inode);
+ page_no = buf_frame_get_page_no(inode);
+
+ reserved = fseg_n_reserved_pages_low(inode, &used, mtr);
+
+ seg_id_low = ut_dulint_get_low(mtr_read_dulint(inode + FSEG_ID,
+ MLOG_8BYTES, mtr));
+ seg_id_high = ut_dulint_get_high(mtr_read_dulint(inode + FSEG_ID,
+ MLOG_8BYTES, mtr));
+
+ n_used = mtr_read_ulint(inode + FSEG_NOT_FULL_N_USED,
+ MLOG_4BYTES, mtr);
+ n_frag = fseg_get_n_frag_pages(inode, mtr);
+ n_free = flst_get_len(inode + FSEG_FREE, mtr);
+ n_not_full = flst_get_len(inode + FSEG_NOT_FULL, mtr);
+ n_full = flst_get_len(inode + FSEG_FULL, mtr);
+
+ printf(
+ "SEGMENT id %lu %lu space %lu; page %lu; res %lu used %lu; full ext %lu\n",
+ seg_id_high, seg_id_low, space, page_no, reserved, used,
+ n_full);
+ printf(
+ "fragm pages %lu; free extents %lu; not full extents %lu: pages %lu\n",
+ n_frag, n_free, n_not_full, n_used);
+}
+
+/***********************************************************************
+Writes info of a segment. */
+
+void
+fseg_print(
+/*=======*/
+ fseg_header_t* header, /* in: segment header */
+ mtr_t* mtr) /* in: mtr */
+{
+ fseg_inode_t* inode;
+ ulint space;
+
+ space = buf_frame_get_space_id(header);
+
+ mtr_x_lock(fil_space_get_latch(space), mtr);
+
+ inode = fseg_inode_get(header, mtr);
+
+ fseg_print_low(inode, mtr);
+}
+
+/***********************************************************************
+Validates the file space system and its segments. */
+
+ibool
+fsp_validate(
+/*=========*/
+ /* out: TRUE if ok */
+ ulint space) /* in: space id */
+{
+ fsp_header_t* header;
+ fseg_inode_t* seg_inode;
+ page_t* seg_inode_page;
+ ulint size;
+ ulint free_limit;
+ ulint frag_n_used;
+ mtr_t mtr;
+ mtr_t mtr2;
+ xdes_t* descr;
+ fil_addr_t node_addr;
+ fil_addr_t next_node_addr;
+ ulint descr_count = 0;
+ ulint n_used = 0;
+ ulint n_used2 = 0;
+ ulint n_full_frag_pages;
+ ulint n;
+ ulint seg_inode_len_free;
+ ulint seg_inode_len_full;
+
+ /* Start first a mini-transaction mtr2 to lock out all other threads
+ from the fsp system */
+ mtr_start(&mtr2);
+ mtr_x_lock(fil_space_get_latch(space), &mtr2);
+
+ mtr_start(&mtr);
+ mtr_x_lock(fil_space_get_latch(space), &mtr);
+
+ header = fsp_get_space_header(space, &mtr);
+
+ size = mtr_read_ulint(header + FSP_SIZE, MLOG_4BYTES, &mtr);
+ free_limit = mtr_read_ulint(header + FSP_FREE_LIMIT,
+ MLOG_4BYTES, &mtr);
+ frag_n_used = mtr_read_ulint(header + FSP_FRAG_N_USED,
+ MLOG_4BYTES, &mtr);
+
+ n_full_frag_pages = FSP_EXTENT_SIZE *
+ flst_get_len(header + FSP_FULL_FRAG, &mtr);
+
+ ut_a(free_limit <= size);
+
+ flst_validate(header + FSP_FREE, &mtr);
+ flst_validate(header + FSP_FREE_FRAG, &mtr);
+ flst_validate(header + FSP_FULL_FRAG, &mtr);
+
+ mtr_commit(&mtr);
+
+ /* Validate FSP_FREE list */
+ mtr_start(&mtr);
+ mtr_x_lock(fil_space_get_latch(space), &mtr);
+
+ header = fsp_get_space_header(space, &mtr);
+ node_addr = flst_get_first(header + FSP_FREE, &mtr);
+
+ mtr_commit(&mtr);
+
+ while (!fil_addr_is_null(node_addr)) {
+ mtr_start(&mtr);
+ mtr_x_lock(fil_space_get_latch(space), &mtr);
+
+ descr_count++;
+ descr = xdes_lst_get_descriptor(space, node_addr, &mtr);
+
+ ut_a(xdes_get_n_used(descr, &mtr) == 0);
+ ut_a(xdes_get_state(descr, &mtr) == XDES_FREE);
+
+ node_addr = flst_get_next_addr(descr + XDES_FLST_NODE, &mtr);
+ mtr_commit(&mtr);
+ }
+
+ /* Validate FSP_FREE_FRAG list */
+ mtr_start(&mtr);
+ mtr_x_lock(fil_space_get_latch(space), &mtr);
+
+ header = fsp_get_space_header(space, &mtr);
+ node_addr = flst_get_first(header + FSP_FREE_FRAG, &mtr);
+
+ mtr_commit(&mtr);
+
+ while (!fil_addr_is_null(node_addr)) {
+ mtr_start(&mtr);
+ mtr_x_lock(fil_space_get_latch(space), &mtr);
+
+ descr_count++;
+ descr = xdes_lst_get_descriptor(space, node_addr, &mtr);
+
+ ut_a(xdes_get_n_used(descr, &mtr) > 0);
+ ut_a(xdes_get_n_used(descr, &mtr) < FSP_EXTENT_SIZE);
+ ut_a(xdes_get_state(descr, &mtr) == XDES_FREE_FRAG);
+
+ n_used += xdes_get_n_used(descr, &mtr);
+ node_addr = flst_get_next_addr(descr + XDES_FLST_NODE, &mtr);
+
+ mtr_commit(&mtr);
+ }
+
+ /* Validate FSP_FULL_FRAG list */
+ mtr_start(&mtr);
+ mtr_x_lock(fil_space_get_latch(space), &mtr);
+
+ header = fsp_get_space_header(space, &mtr);
+ node_addr = flst_get_first(header + FSP_FULL_FRAG, &mtr);
+
+ mtr_commit(&mtr);
+
+ while (!fil_addr_is_null(node_addr)) {
+ mtr_start(&mtr);
+ mtr_x_lock(fil_space_get_latch(space), &mtr);
+
+ descr_count++;
+ descr = xdes_lst_get_descriptor(space, node_addr, &mtr);
+
+ ut_a(xdes_get_n_used(descr, &mtr) == FSP_EXTENT_SIZE);
+ ut_a(xdes_get_state(descr, &mtr) == XDES_FULL_FRAG);
+
+ node_addr = flst_get_next_addr(descr + XDES_FLST_NODE, &mtr);
+ mtr_commit(&mtr);
+ }
+
+ /* Validate segments */
+ mtr_start(&mtr);
+ mtr_x_lock(fil_space_get_latch(space), &mtr);
+
+ header = fsp_get_space_header(space, &mtr);
+
+ node_addr = flst_get_first(header + FSP_SEG_INODES_FULL, &mtr);
+
+ seg_inode_len_full = flst_get_len(header + FSP_SEG_INODES_FULL, &mtr);
+
+ mtr_commit(&mtr);
+
+ while (!fil_addr_is_null(node_addr)) {
+
+ for (n = 0; n < FSP_SEG_INODES_PER_PAGE; n++) {
+
+ mtr_start(&mtr);
+ mtr_x_lock(fil_space_get_latch(space), &mtr);
+
+ seg_inode_page = fut_get_ptr(space, node_addr, RW_X_LATCH,
+ &mtr) - FSEG_INODE_PAGE_NODE;
+
+ seg_inode = fsp_seg_inode_page_get_nth_inode(seg_inode_page,
+ n, &mtr);
+ ut_a(ut_dulint_cmp(mach_read_from_8(seg_inode + FSEG_ID),
+ ut_dulint_zero) != 0);
+ fseg_validate_low(seg_inode, &mtr);
+
+ descr_count += flst_get_len(seg_inode + FSEG_FREE, &mtr);
+ descr_count += flst_get_len(seg_inode + FSEG_FULL, &mtr);
+ descr_count += flst_get_len(seg_inode + FSEG_NOT_FULL, &mtr);
+
+ n_used2 += fseg_get_n_frag_pages(seg_inode, &mtr);
+
+ next_node_addr = flst_get_next_addr(seg_inode_page
+ + FSEG_INODE_PAGE_NODE, &mtr);
+ mtr_commit(&mtr);
+ }
+
+ node_addr = next_node_addr;
+ }
+
+ mtr_start(&mtr);
+ mtr_x_lock(fil_space_get_latch(space), &mtr);
+
+ header = fsp_get_space_header(space, &mtr);
+
+ node_addr = flst_get_first(header + FSP_SEG_INODES_FREE, &mtr);
+
+ seg_inode_len_free = flst_get_len(header + FSP_SEG_INODES_FREE, &mtr);
+
+ mtr_commit(&mtr);
+
+ while (!fil_addr_is_null(node_addr)) {
+
+ for (n = 0; n < FSP_SEG_INODES_PER_PAGE; n++) {
+
+ mtr_start(&mtr);
+ mtr_x_lock(fil_space_get_latch(space), &mtr);
+
+ seg_inode_page = fut_get_ptr(space, node_addr, RW_X_LATCH,
+ &mtr) - FSEG_INODE_PAGE_NODE;
+
+ seg_inode = fsp_seg_inode_page_get_nth_inode(seg_inode_page,
+ n, &mtr);
+ if (ut_dulint_cmp(mach_read_from_8(seg_inode + FSEG_ID),
+ ut_dulint_zero) != 0) {
+ fseg_validate_low(seg_inode, &mtr);
+
+ descr_count += flst_get_len(seg_inode + FSEG_FREE,
+ &mtr);
+ descr_count += flst_get_len(seg_inode + FSEG_FULL,
+ &mtr);
+ descr_count += flst_get_len(seg_inode + FSEG_NOT_FULL,
+ &mtr);
+ n_used2 += fseg_get_n_frag_pages(seg_inode, &mtr);
+ }
+
+ next_node_addr = flst_get_next_addr(seg_inode_page
+ + FSEG_INODE_PAGE_NODE, &mtr);
+ mtr_commit(&mtr);
+ }
+
+ node_addr = next_node_addr;
+ }
+
+ ut_a(descr_count * FSP_EXTENT_SIZE == free_limit);
+ ut_a(n_used + n_full_frag_pages
+ == n_used2 + (free_limit + XDES_DESCRIBED_PER_PAGE - 1)
+ / XDES_DESCRIBED_PER_PAGE
+ + seg_inode_len_full + seg_inode_len_free);
+ ut_a(frag_n_used == n_used);
+
+ mtr_commit(&mtr2);
+ return(TRUE);
+}
+
+/***********************************************************************
+Prints info of a file space. */
+
+void
+fsp_print(
+/*======*/
+ ulint space) /* in: space id */
+{
+ fsp_header_t* header;
+ fseg_inode_t* seg_inode;
+ page_t* seg_inode_page;
+ ulint size;
+ ulint free_limit;
+ ulint frag_n_used;
+ fil_addr_t node_addr;
+ fil_addr_t next_node_addr;
+ ulint n_free;
+ ulint n_free_frag;
+ ulint n_full_frag;
+ ulint seg_id_low;
+ ulint seg_id_high;
+ ulint n;
+ ulint n_segs = 0;
+ mtr_t mtr;
+ mtr_t mtr2;
+
+ /* Start first a mini-transaction mtr2 to lock out all other threads
+ from the fsp system */
+
+ mtr_start(&mtr2);
+
+ mtr_x_lock(fil_space_get_latch(space), &mtr2);
+
+ mtr_start(&mtr);
+
+ mtr_x_lock(fil_space_get_latch(space), &mtr);
+
+ header = fsp_get_space_header(space, &mtr);
+
+ size = mtr_read_ulint(header + FSP_SIZE, MLOG_4BYTES, &mtr);
+
+ free_limit = mtr_read_ulint(header + FSP_FREE_LIMIT, MLOG_4BYTES,
+ &mtr);
+ frag_n_used = mtr_read_ulint(header + FSP_FRAG_N_USED, MLOG_4BYTES,
+ &mtr);
+ n_free = flst_get_len(header + FSP_FREE, &mtr);
+ n_free_frag = flst_get_len(header + FSP_FREE_FRAG, &mtr);
+ n_full_frag = flst_get_len(header + FSP_FULL_FRAG, &mtr);
+
+ seg_id_low = ut_dulint_get_low(mtr_read_dulint(header + FSP_SEG_ID,
+ MLOG_8BYTES, &mtr));
+ seg_id_high = ut_dulint_get_high(mtr_read_dulint(header + FSP_SEG_ID,
+ MLOG_8BYTES, &mtr));
+ printf("FILE SPACE INFO: id %lu\n", space);
+
+ printf("size %lu, free limit %lu, free extents %lu\n",
+ size, free_limit, n_free);
+ printf(
+ "not full frag extents %lu: used pages %lu, full frag extents %lu\n",
+ n_free_frag, frag_n_used, n_full_frag);
+
+ printf("first seg id not used %lu %lu\n", seg_id_high, seg_id_low);
+
+ mtr_commit(&mtr);
+
+ /* Print segments */
+
+ mtr_start(&mtr);
+ mtr_x_lock(fil_space_get_latch(space), &mtr);
+
+ header = fsp_get_space_header(space, &mtr);
+
+ node_addr = flst_get_first(header + FSP_SEG_INODES_FULL, &mtr);
+
+ mtr_commit(&mtr);
+
+ while (!fil_addr_is_null(node_addr)) {
+
+ for (n = 0; n < FSP_SEG_INODES_PER_PAGE; n++) {
+
+ mtr_start(&mtr);
+ mtr_x_lock(fil_space_get_latch(space), &mtr);
+
+ seg_inode_page = fut_get_ptr(space, node_addr, RW_X_LATCH,
+ &mtr) - FSEG_INODE_PAGE_NODE;
+
+ seg_inode = fsp_seg_inode_page_get_nth_inode(seg_inode_page,
+ n, &mtr);
+ ut_a(ut_dulint_cmp(mach_read_from_8(seg_inode + FSEG_ID),
+ ut_dulint_zero) != 0);
+ fseg_print_low(seg_inode, &mtr);
+
+ n_segs++;
+
+ next_node_addr = flst_get_next_addr(seg_inode_page
+ + FSEG_INODE_PAGE_NODE, &mtr);
+ mtr_commit(&mtr);
+ }
+
+ node_addr = next_node_addr;
+ }
+
+ mtr_start(&mtr);
+ mtr_x_lock(fil_space_get_latch(space), &mtr);
+
+ header = fsp_get_space_header(space, &mtr);
+
+ node_addr = flst_get_first(header + FSP_SEG_INODES_FREE, &mtr);
+
+ mtr_commit(&mtr);
+
+ while (!fil_addr_is_null(node_addr)) {
+
+ for (n = 0; n < FSP_SEG_INODES_PER_PAGE; n++) {
+
+ mtr_start(&mtr);
+ mtr_x_lock(fil_space_get_latch(space), &mtr);
+
+ seg_inode_page = fut_get_ptr(space, node_addr, RW_X_LATCH,
+ &mtr) - FSEG_INODE_PAGE_NODE;
+
+ seg_inode = fsp_seg_inode_page_get_nth_inode(seg_inode_page,
+ n, &mtr);
+ if (ut_dulint_cmp(mach_read_from_8(seg_inode + FSEG_ID),
+ ut_dulint_zero) != 0) {
+
+ fseg_print_low(seg_inode, &mtr);
+ n_segs++;
+ }
+
+ next_node_addr = flst_get_next_addr(seg_inode_page
+ + FSEG_INODE_PAGE_NODE, &mtr);
+ mtr_commit(&mtr);
+ }
+
+ node_addr = next_node_addr;
+ }
+
+ mtr_commit(&mtr2);
+
+ printf("NUMBER of file segments: %lu\n", n_segs);
+}
diff --git a/innobase/fsp/makefilewin b/innobase/fsp/makefilewin
new file mode 100644
index 00000000000..503cf27f490
--- /dev/null
+++ b/innobase/fsp/makefilewin
@@ -0,0 +1,9 @@
+include ..\include\makefile.i
+
+fsp.lib: fsp0fsp.obj
+ lib -out:..\libs\fsp.lib fsp0fsp.obj
+
+fsp0fsp.obj: fsp0fsp.c
+ $(CCOM) $(CFL) -c fsp0fsp.c
+
+
diff --git a/innobase/fsp/trash/FSP0FSP.C b/innobase/fsp/trash/FSP0FSP.C
new file mode 100644
index 00000000000..b0add437bff
--- /dev/null
+++ b/innobase/fsp/trash/FSP0FSP.C
@@ -0,0 +1,3100 @@
+/**********************************************************************
+File-space management
+
+(c) 1995 Innobase Oy
+
+Created 11/29/1995 Heikki Tuuri
+***********************************************************************/
+
+#include "fsp0fsp.h"
+
+#include "buf0buf.h"
+#include "fil0fil.h"
+#include "sync0sync.h"
+#include "mtr0log.h"
+#include "fut0fut.h"
+#include "ut0byte.h"
+
+/* The data structures in files are defined just as byte strings in C */
+typedef byte fsp_header_t;
+typedef byte xdes_t;
+typedef byte fseg_page_header_t;
+
+/* Rw-latch protecting the whole file space system */
+rw_lock_t fsp_latch;
+
+
+/* SPACE HEADER
+ ============
+
+File space header data structure: this data structure
+is contained in the first page of a space. The space for this header
+is reserved in every extent descriptor page, but used only in the first. */
+#define FSP_HEADER_OFFSET FIL_PAGE_DATA /* Offset of the space header
+ within a file page */
+/*-------------------------------------*/
+#define FSP_SIZE 0 /* Current
+ size of the space in pages */
+#define FSP_FREE_LIMIT 4 /* Minimum page number for which
+ the free list has not been initialized:
+ the pages >= this limit are, by
+ definition, free */
+#define FSP_LOWEST_NO_WRITE 8 /* The lowest page offset for which
+ the page has not been written to disk
+ (if it has been written, we know
+ that the OS has really reserved
+ the physical space for the page) */
+#define FSP_FRAG_N_USED 12 /* number of used pages in
+ the FSP_FREE_FRAG list */
+#define FSP_FREE 16 /* list of free extents */
+#define FSP_FREE_FRAG (16 + FLST_BASE_NODE_SIZE)
+ /* list of partially free extents not
+ belonging to any segment */
+#define FSP_FULL_FRAG (16 + 2 * FLST_BASE_NODE_SIZE)
+ /* list of full extents not belonging
+ to any segment */
+#define FSP_SEG_ID (16 + 3 * FLST_BASE_NODE_SIZE)
+ /* 8 bytes which give the first
+#define FSP_SEG_HDRS_FULL (24 + 3 * FLST_BASE_NODE_SIZE)
+ /* list of pages containing segment
+ headers, where all the segment header
+ slots are reserved */
+#define FSP_SEG_HDRS_FREE (24 + 4 * FLST_BASE_NODE_SIZE)
+ /* list of pages containing segment
+ headers, where not all the segment
+ header slots are reserved */
+/*-------------------------------------*/
+/* File space header size */
+#define FSP_HEADER_SIZE (24 + 4 * FLST_BASE_NODE_SIZE)
+
+#define FSP_FREE_ADD 4 /* this many free extents are added
+ to the free list from above
+ FSP_FREE_LIMIT at a time */
+
+
+/* SEGMENT HEADER
+ ==============
+
+Segment header which is created for each segment in a tablespace, on a
+page of its own. NOTE: in purge we assume that a segment having only one
+currently used page can be freed in a few steps, so that the freeing cannot
+fill the file buffer with bufferfixed file pages. */
+
+#define FSEG_HDR_PAGE_NODE FSEG_PAGE_DATA
+ /* the list node for linking
+ segment header pages */
+
+#define FSEG_ARR_OFFSET (FSEG_PAGE_DATA + FLST_NODE_SIZE)
+/*-------------------------------------*/
+#define FSEG_ID 0 /* 8 bytes of segment id: if this is
+ ut_dulint_zero, it means that the
+ header is unused */
+#define FSEG_NOT_FULL_N_USED 8
+ /* number of used segment pages in
+ the FSEG_NOT_FULL list */
+#define FSEG_FREE 12
+ /* list of free extents of this
+ segment */
+#define FSEG_NOT_FULL (12 + FLST_BASE_NODE_SIZE)
+ /* list of partially free extents */
+#define FSEG_FULL (12 + 2 * FLST_BASE_NODE_SIZE)
+ /* list of full extents */
+#define FSEG_MAGIC_N (12 + 3 * FLST_BASE_NODE_SIZE)
+ /* magic number used in debugging */
+#define FSEG_FRAG_ARR (16 + 3 * FLST_BASE_NODE_SIZE)
+ /* array of individual pages
+ belonging to this segment in fsp
+ fragment extent lists */
+#define FSEG_FRAG_ARR_N_SLOTS (FSP_EXTENT_SIZE / 2)
+ /* number of slots in the array for
+ the fragment pages */
+#define FSEG_FRAG_SLOT_SIZE 4 /* a fragment page slot contains its
+ page number within space, FIL_NULL
+ means that the slot is not in use */
+/*-------------------------------------*/
+#define FSEG_HEADER_SIZE (16 + 3 * FLST_BASE_NODE_SIZE +\
+ FSEG_FRAG_ARR_N_SLOTS * FSEG_FRAG_SLOT_SIZE)
+
+#define FSP_SEG_HDRS_PER_PAGE ((UNIV_PAGE_SIZE - FSEG_ARR_OFFSET - 10)\
+ / FSEG_HEADER_SIZE)
+ /* Number of segment headers which fit on a
+ single page */
+
+#define FSEG_MAGIC_N_VALUE 97937874
+
+#define FSEG_FILLFACTOR 8 /* If this value is x, then if
+ the number of unused but reserved
+ pages in a segment is less than
+ reserved pages * 1/x, and there are
+ at least FSEG_FRAG_LIMIT used pages,
+ then we allow a new empty extent to
+ be added to the segment in
+ fseg_alloc_free_page. Otherwise, we
+ use unused pages of the segment. */
+
+#define FSEG_FRAG_LIMIT FSEG_FRAG_N_ARR_SLOTS
+ /* If the segment has >= this many
+ used pages, it may be expanded by
+ allocating extents to the segment;
+ until that only individual fragment
+ pages are allocated from the space */
+
+#define FSEG_FREE_LIST_LIMIT 40 /* If the reserved size of a segment
+ is at least this many extents, we
+ allow extents to be put to the free
+ list of the extent: at most
+ FSEG_FREE_LIST_MAX_LEN many */
+#define FSEG_FREE_LIST_MAX_LEN 4
+
+
+/* EXTENT DESCRIPTOR
+ =================
+
+File extent descriptor data structure: contains bits to tell
+which pages in the extent are free and which contain old tuple
+version to clean. */
+
+/*-------------------------------------*/
+#define XDES_ID 0 /* The identifier of the segment
+ to which this extent belongs */
+#define XDES_FLST_NODE 8 /* The list node data structure
+ for the descriptors */
+#define XDES_STATE (FLST_NODE_SIZE + 8)
+ /* contains state information
+ of the extent */
+#define XDES_BITMAP (FLST_NODE_SIZE + 12)
+ /* Descriptor bitmap of the pages
+ in the extent*/
+/*-------------------------------------*/
+
+#define XDES_BITS_PER_PAGE 2 /* How many bits are there per page */
+#define XDES_FREE_BIT 0 /* Index of the bit which tells if
+ the page is free */
+#define XDES_CLEAN_BIT 1 /* Index of the bit which tells if
+ there are old versions of tuples
+ on the page */
+/* States of a descriptor */
+#define XDES_FREE 1 /* extent is in free list of space */
+#define XDES_FREE_FRAG 2 /* extent is in free fragment list of
+ space */
+#define XDES_FULL_FRAG 3 /* extent is in full fragment list of
+ space */
+#define XDES_FSEG 4 /* extent belongs to a segment*/
+
+/* Number of pages described in a single descriptor page:
+currently each page description takes less than
+1 byte. */
+#define XDES_DESCRIBED_PER_PAGE UNIV_PAGE_SIZE
+
+/* File extent data structure size in bytes. The "+ 7 ) / 8"
+part in the definition rounds the number of bytes upward. */
+#define XDES_SIZE (XDES_BITMAP +\
+ (FSP_EXTENT_SIZE * XDES_BITS_PER_PAGE + 7) / 8)
+
+/* Offset of the descriptor array on a descriptor page */
+#define XDES_ARR_OFFSET (FSP_HEADER_OFFSET + FSP_HEADER_SIZE)
+
+/**************************************************************************
+Returns an extent to the free list of a space. */
+static
+void
+fsp_free_extent(
+/*============*/
+ ulint space, /* in: space id */
+ ulint page, /* in: page offset in the extent */
+ mtr_t* mtr); /* in: mtr */
+/**************************************************************************
+Frees an extent of a segment to the space free list. */
+static
+void
+fseg_free_extent(
+/*=============*/
+ fseg_header_t* seg_header, /* in: segment header */
+ ulint space, /* in: space id */
+ ulint page, /* in: page offset in the extent */
+ mtr_t* mtr); /* in: mtr handle */
+/**************************************************************************
+Calculates the number of pages reserved by a segment, and how
+many pages are currently used. */
+static
+ulint
+fseg_n_reserved_pages_low(
+/*======================*/
+ /* out: number of reserved pages */
+ fseg_header_t* header, /* in: segment header */
+ ulint* used, /* out: number of pages used (<= reserved) */
+ mtr_t* mtr); /* in: mtr handle */
+/************************************************************************
+Marks a page used. The page must reside within the extents of the given
+segment. */
+static
+void
+fseg_mark_page_used(
+/*================*/
+ fseg_header_t* seg_header,/* in: segment header */
+ ulint space, /* in: space id */
+ ulint page, /* in: page offset */
+ mtr_t* mtr); /* in: mtr */
+/**************************************************************************
+Frees a single page of a segment. */
+static
+void
+fseg_free_page_low(
+/*===============*/
+ fseg_header_t* seg_header, /* in: segment header */
+ ulint space, /* in: space id */
+ ulint page, /* in: page offset */
+ mtr_t* mtr); /* in: mtr handle */
+/**************************************************************************
+Returns the first extent descriptor for a segment. We think of the extent
+lists of the segment catenated in the order FSEG_FULL -> FSEG_NOT_FULL
+-> FSEG_FREE. */
+static
+xdes_t*
+fseg_get_first_extent(
+/*==================*/
+ /* out: the first extent descriptor, or NULL if
+ none */
+ fseg_header_t* header, /* in: segment header */
+ mtr_t* mtr); /* in: mtr */
+/**************************************************************************
+Puts new extents to the free list if
+there are free extents above the free limit. If an extent happens
+to contain an extent descriptor page, the extent is put to
+the FSP_FREE_FRAG list with the page marked as used. */
+static
+void
+fsp_fill_free_list(
+/*===============*/
+ ulint space, /* in: space */
+ fsp_header_t* header, /* in: space header */
+ mtr_t* mtr); /* in: mtr */
+
+/**************************************************************************
+Gets a descriptor bit of a page. */
+UNIV_INLINE
+bool
+xdes_get_bit(
+/*=========*/
+ /* out: TRUE if free */
+ xdes_t* descr, /* in: descriptor */
+ ulint bit, /* in: XDES_FREE_BIT or XDES_CLEAN_BIT */
+ ulint offset, /* in: page offset within extent:
+ 0 ... FSP_EXTENT_SIZE - 1 */
+ mtr_t* mtr) /* in: mtr */
+{
+ ulint index;
+ ulint byte_index;
+ ulint bit_index;
+
+ ut_ad(mtr_memo_contains(mtr, buf_block_align(descr),
+ MTR_MEMO_PAGE_X_LOCK));
+ ut_ad((bit == XDES_FREE_BIT) || (bit == XDES_CLEAN_BIT));
+ ut_ad(offset < FSP_EXTENT_SIZE);
+
+ index = bit + XDES_BITS_PER_PAGE * offset;
+
+ byte_index = index / 8;
+ bit_index = index % 8;
+
+ return(ut_bit_get_nth(
+ mtr_read_ulint(descr + XDES_BITMAP + byte_index,
+ MLOG_1BYTE, mtr),
+ bit_index));
+}
+
+/**************************************************************************
+Sets a descriptor bit of a page. */
+UNIV_INLINE
+void
+xdes_set_bit(
+/*=========*/
+ xdes_t* descr, /* in: descriptor */
+ ulint bit, /* in: XDES_FREE_BIT or XDES_CLEAN_BIT */
+ ulint offset, /* in: page offset within extent:
+ 0 ... FSP_EXTENT_SIZE - 1 */
+ bool val, /* in: bit value */
+ mtr_t* mtr) /* in: mtr */
+{
+ ulint index;
+ ulint byte_index;
+ ulint bit_index;
+ ulint descr_byte;
+
+ ut_ad(mtr_memo_contains(mtr, buf_block_align(descr),
+ MTR_MEMO_PAGE_X_LOCK));
+ ut_ad((bit == XDES_FREE_BIT) || (bit == XDES_CLEAN_BIT));
+ ut_ad(offset < FSP_EXTENT_SIZE);
+
+ index = bit + XDES_BITS_PER_PAGE * offset;
+
+ byte_index = index / 8;
+ bit_index = index % 8;
+
+ descr_byte = mtr_read_ulint(descr + XDES_BITMAP + byte_index,
+ MLOG_1BYTE, mtr);
+
+ descr_byte = ut_bit_set_nth(descr_byte, bit_index, val);
+
+ mlog_write_ulint(descr + XDES_BITMAP + byte_index,
+ descr_byte, MLOG_1BYTE, mtr);
+}
+
+/**************************************************************************
+Looks for a descriptor bit having the desired value. Starts from hint
+and scans upward; at the end of the extent the search is wrapped to
+the start of the extent. */
+UNIV_INLINE
+ulint
+xdes_find_bit(
+/*==========*/
+ /* out: bit index of the bit,
+ ULINT_UNDEFINED if not found */
+ xdes_t* descr, /* in: descriptor */
+ ulint bit, /* in: XDES_FREE_BIT or XDES_CLEAN_BIT */
+ bool val, /* in: desired bit value */
+ ulint hint, /* in: hint of which bit position would be
+ desirable */
+ mtr_t* mtr) /* in: mtr */
+{
+ ulint i;
+
+ ut_ad(descr && mtr);
+ ut_ad(val <= TRUE);
+ ut_ad(hint < FSP_EXTENT_SIZE);
+ ut_ad(mtr_memo_contains(mtr, buf_block_align(descr),
+ MTR_MEMO_PAGE_X_LOCK));
+
+ for (i = hint; i < FSP_EXTENT_SIZE; i++) {
+ if (val == xdes_get_bit(descr, bit, i, mtr)) {
+ return(i);
+ }
+ }
+
+ for (i = 0; i < hint; i++) {
+ if (val == xdes_get_bit(descr, bit, i, mtr)) {
+ return(i);
+ }
+ }
+
+ return(ULINT_UNDEFINED);
+}
+
+/**************************************************************************
+Looks for a descriptor bit having the desired value. Scans the extent in
+a direction opposite to xdes_find_bit. */
+UNIV_INLINE
+ulint
+xdes_find_bit_downward(
+/*===================*/
+ /* out: bit index of the bit,
+ ULINT_UNDEFINED if not found */
+ xdes_t* descr, /* in: descriptor */
+ ulint bit, /* in: XDES_FREE_BIT or XDES_CLEAN_BIT */
+ bool val, /* in: desired bit value */
+ ulint hint, /* in: hint of which bit position would be
+ desirable */
+ mtr_t* mtr) /* in: mtr */
+{
+ ulint i;
+
+ ut_ad(descr && mtr);
+ ut_ad(val <= TRUE);
+ ut_ad(hint < FSP_EXTENT_SIZE);
+ ut_ad(mtr_memo_contains(mtr, buf_block_align(descr),
+ MTR_MEMO_PAGE_X_LOCK));
+
+ for (i = hint + 1; i > 0; i--) {
+ if (val == xdes_get_bit(descr, bit, i - 1, mtr)) {
+ return(i - 1);
+ }
+ }
+
+ for (i = FSP_EXTENT_SIZE - 1; i > hint; i--) {
+ if (val == xdes_get_bit(descr, bit, i, mtr)) {
+ return(i);
+ }
+ }
+
+ return(ULINT_UNDEFINED);
+}
+
+/**************************************************************************
+Returns the number of used pages in a descriptor. */
+UNIV_INLINE
+ulint
+xdes_get_n_used(
+/*============*/
+ /* out: number of pages used */
+ xdes_t* descr, /* in: descriptor */
+ mtr_t* mtr) /* in: mtr */
+{
+ ulint i;
+ ulint count = 0;
+
+ ut_ad(descr && mtr);
+ ut_ad(mtr_memo_contains(mtr, buf_block_align(descr),
+ MTR_MEMO_PAGE_X_LOCK));
+
+ for (i = 0; i < FSP_EXTENT_SIZE; i++) {
+ if (FALSE == xdes_get_bit(descr, XDES_FREE_BIT, i, mtr)) {
+ count++;
+ }
+ }
+
+ return(count);
+}
+
+/**************************************************************************
+Returns true if extent contains no used pages. */
+UNIV_INLINE
+bool
+xdes_is_free(
+/*=========*/
+ /* out: TRUE if totally free */
+ xdes_t* descr, /* in: descriptor */
+ mtr_t* mtr) /* in: mtr */
+{
+ if (0 == xdes_get_n_used(descr, mtr)) {
+ return(TRUE);
+ } else {
+ return(FALSE);
+ }
+}
+
+/**************************************************************************
+Returns true if extent contains no free pages. */
+UNIV_INLINE
+bool
+xdes_is_full(
+/*=========*/
+ /* out: TRUE if full */
+ xdes_t* descr, /* in: descriptor */
+ mtr_t* mtr) /* in: mtr */
+{
+ if (FSP_EXTENT_SIZE == xdes_get_n_used(descr, mtr)) {
+ return(TRUE);
+ } else {
+ return(FALSE);
+ }
+}
+
+/**************************************************************************
+Sets the state of an xdes. */
+UNIV_INLINE
+void
+xdes_set_state(
+/*===========*/
+ xdes_t* descr, /* in: descriptor */
+ ulint state, /* in: state to set */
+ mtr_t* mtr) /* in: mtr handle */
+{
+ ut_ad(descr && mtr);
+ ut_ad(state >= XDES_FREE);
+ ut_ad(state <= XDES_FSEG);
+ ut_ad(mtr_memo_contains(mtr, buf_block_align(descr),
+ MTR_MEMO_PAGE_X_LOCK));
+
+ mlog_write_ulint(descr + XDES_STATE, state, MLOG_4BYTES, mtr);
+}
+
+/**************************************************************************
+Gets the state of an xdes. */
+UNIV_INLINE
+ulint
+xdes_get_state(
+/*===========*/
+ /* out: state */
+ xdes_t* descr, /* in: descriptor */
+ mtr_t* mtr) /* in: mtr handle */
+{
+ ut_ad(descr && mtr);
+ ut_ad(mtr_memo_contains(mtr, buf_block_align(descr),
+ MTR_MEMO_PAGE_X_LOCK));
+
+ return(mtr_read_ulint(descr + XDES_STATE, MLOG_4BYTES, mtr));
+}
+
+/**************************************************************************
+Inits an extent descriptor to free and clean state. */
+UNIV_INLINE
+void
+xdes_init(
+/*======*/
+ xdes_t* descr, /* in: descriptor */
+ mtr_t* mtr) /* in: mtr */
+{
+ ulint i;
+
+ ut_ad(descr && mtr);
+ ut_ad(mtr_memo_contains(mtr, buf_block_align(descr),
+ MTR_MEMO_PAGE_X_LOCK));
+
+ for (i = 0; i < FSP_EXTENT_SIZE; i++) {
+ xdes_set_bit(descr, XDES_FREE_BIT, i, TRUE, mtr);
+ xdes_set_bit(descr, XDES_CLEAN_BIT, i, TRUE, mtr);
+ }
+
+ xdes_set_state(descr, XDES_FREE, mtr);
+}
+
+/************************************************************************
+Calculates the page where the descriptor of a page resides. */
+UNIV_INLINE
+ulint
+xdes_calc_descriptor_page(
+/*======================*/
+ /* out: descriptor page offset */
+ ulint offset) /* in: page offset */
+{
+ ut_ad(UNIV_PAGE_SIZE > XDES_ARR_OFFSET
+ + (XDES_DESCRIBED_PER_PAGE / FSP_EXTENT_SIZE) * XDES_SIZE);
+
+ return(ut_2pow_round(offset, XDES_DESCRIBED_PER_PAGE));
+}
+
+/************************************************************************
+Calculates the descriptor index within a descriptor page. */
+UNIV_INLINE
+ulint
+xdes_calc_descriptor_index(
+/*=======================*/
+ /* out: descriptor index */
+ ulint offset) /* in: page offset */
+{
+ return(ut_2pow_remainder(offset, XDES_DESCRIBED_PER_PAGE) /
+ FSP_EXTENT_SIZE);
+}
+
+/************************************************************************
+Gets pointer to a the extent descriptor of a page. The page where the
+extent descriptor resides is x-locked. If the page offset is equal to the free
+limit of the space, adds new extents from above the free limit
+to the space free list, if not free limit == space size. This adding
+is necessary to make the descriptor defined, as they are uninitialized
+above the free limit. */
+UNIV_INLINE
+xdes_t*
+xdes_get_descriptor_with_space_hdr(
+/*===============================*/
+ /* out: pointer to the extent descriptor,
+ NULL if the page does not exist in the
+ space or if offset > free limit */
+ fsp_header_t* sp_header,/* in: space header, x-latched */
+ ulint space, /* in: space id */
+ ulint offset, /* in: page offset;
+ if equal to the free limit,
+ we try to add new extents to
+ the space free list */
+ mtr_t* mtr) /* in: mtr handle */
+{
+ ulint limit;
+ ulint size;
+ buf_block_t* buf_page;
+ ulint descr_page_no;
+ page_t* descr_page;
+
+ ut_ad(mtr);
+ ut_ad(mtr_memo_contains(mtr, &fsp_latch, MTR_MEMO_X_LOCK));
+
+ /* Read free limit and space size */
+ limit = mtr_read_ulint(sp_header + FSP_FREE_LIMIT, MLOG_4BYTES, mtr);
+ size = mtr_read_ulint(sp_header + FSP_SIZE, MLOG_4BYTES, mtr);
+
+ /* If offset is >= size or > limit, return NULL */
+ if ((offset >= size) || (offset > limit)) {
+ return(NULL);
+ }
+
+ /* If offset is == limit, fill free list of the space. */
+ if (offset == limit) {
+ fsp_fill_free_list(space, sp_header, mtr);
+ }
+
+ descr_page_no = xdes_calc_descriptor_page(offset);
+
+ if (descr_page_no == 0) {
+ /* It is on the space header page */
+
+ descr_page = buf_frame_align(sp_header);
+ } else {
+
+ buf_page = buf_page_get(space, descr_page_no, mtr);
+ buf_page_x_lock(buf_page, mtr);
+ descr_page = buf_block_get_frame(buf_page);
+ }
+
+ return(descr_page + XDES_ARR_OFFSET
+ + XDES_SIZE * xdes_calc_descriptor_index(offset));
+}
+
+/************************************************************************
+Gets pointer to a the extent descriptor of a page. The page where the
+extent descriptor resides is x-locked. If the page offset is equal to the free
+limit of the space, adds new extents from above the free limit
+to the space free list, if not free limit == space size. This adding
+is necessary to make the descriptor defined, as they are uninitialized
+above the free limit. */
+static
+xdes_t*
+xdes_get_descriptor(
+/*================*/
+ /* out: pointer to the extent descriptor,
+ NULL if the page does not exist in the
+ space or if offset > free limit */
+ ulint space, /* in: space id */
+ ulint offset, /* in: page offset;
+ if equal to the free limit,
+ we try to add new extents to
+ the space free list */
+ mtr_t* mtr) /* in: mtr handle */
+{
+ fsp_header_t* sp_header;
+ buf_block_t* block;
+
+ block = buf_page_get(space, 0, mtr); /* get space header */
+ sp_header = FSP_HEADER_OFFSET + buf_block_get_frame(block);
+ buf_page_x_lock(block, mtr);
+
+ return(xdes_get_descriptor_with_space_hdr(sp_header, space, offset,
+ mtr));
+}
+
+/************************************************************************
+Gets pointer to a the extent descriptor if the file address
+of the descriptor list node is known. The page where the
+extent descriptor resides is x-locked. */
+UNIV_INLINE
+xdes_t*
+xdes_lst_get_descriptor(
+/*====================*/
+ /* out: pointer to the extent descriptor */
+ ulint space, /* in: space id */
+ fil_addr_t lst_node,/* in: file address of the list node
+ contained in the descriptor */
+ mtr_t* mtr) /* in: mtr handle */
+{
+ xdes_t* descr;
+
+ ut_ad(mtr);
+ ut_ad(mtr_memo_contains(mtr, &fsp_latch, MTR_MEMO_X_LOCK));
+
+ descr = fut_get_ptr_x_lock(space, lst_node, mtr) - XDES_FLST_NODE;
+
+ return(descr);
+}
+
+/************************************************************************
+Gets pointer to the next descriptor in a descriptor list and x-locks
+its page. */
+UNIV_INLINE
+xdes_t*
+xdes_lst_get_next(
+/*==============*/
+ xdes_t* descr, /* in: pointer to a descriptor */
+ mtr_t* mtr) /* in: mtr handle */
+{
+ ulint space;
+
+ ut_ad(mtr && descr);
+
+ space = buf_page_get_space(buf_block_align(descr));
+
+ return(xdes_lst_get_descriptor(space,
+ flst_get_next_addr(descr + XDES_FLST_NODE, mtr), mtr));
+}
+
+/************************************************************************
+Returns page offset of the first page in extent described by a descriptor.
+*/
+UNIV_INLINE
+ulint
+xdes_get_offset(
+/*============*/
+ /* out: offset of the first page in extent */
+ xdes_t* descr) /* in: extent descriptor */
+{
+ buf_block_t* buf_page;
+
+ ut_ad(descr);
+
+ buf_page = buf_block_align(descr);
+
+ return(buf_page_get_offset(buf_page)
+ + ((descr - buf_frame_align(descr) - XDES_ARR_OFFSET)
+ / XDES_SIZE)
+ * FSP_EXTENT_SIZE);
+}
+
+/**************************************************************************
+Gets a pointer to the space header and x-locks its page. */
+UNIV_INLINE
+fsp_header_t*
+fsp_get_space_header(
+/*=================*/
+ /* out: pointer to the space header, page x-locked */
+ ulint id, /* in: space id */
+ mtr_t* mtr) /* in: mtr */
+{
+ buf_block_t* block;
+
+ ut_ad(mtr);
+
+ block = buf_page_get(id, 0, mtr);
+
+ buf_page_x_lock(block, mtr);
+
+ return(FSP_HEADER_OFFSET + buf_block_get_frame(block));
+}
+
+/**************************************************************************
+Initializes the file space system mutex. */
+
+void
+fsp_init(void)
+/*==========*/
+{
+ rw_lock_create(&fsp_latch);
+}
+
+/**************************************************************************
+Initializes the space header of a new created space. */
+
+void
+fsp_header_init(
+/*============*/
+ ulint space, /* in: space id */
+ ulint size, /* in: current size in blocks */
+ mtr_t* mtr) /* in: mini-transaction handle */
+{
+ fsp_header_t* header;
+
+ ut_ad(mtr);
+
+ mtr_x_lock(&fsp_latch, mtr);
+
+ header = fsp_get_space_header(space, mtr);
+
+ mlog_write_ulint(header + FSP_SIZE, size, MLOG_4BYTES, mtr);
+ mlog_write_ulint(header + FSP_FREE_LIMIT, 0, MLOG_4BYTES, mtr);
+ mlog_write_ulint(header + FSP_LOWEST_NO_WRITE, 0, MLOG_4BYTES, mtr);
+ mlog_write_ulint(header + FSP_FRAG_N_USED, 0, MLOG_4BYTES, mtr);
+
+ flst_init(header + FSP_FREE, mtr);
+ flst_init(header + FSP_FREE_FRAG, mtr);
+ flst_init(header + FSP_FULL_FRAG, mtr);
+ flst_init(header + FSP_SEG_HDRS_FULL, mtr);
+ flst_init(header + FSP_SEG_HDRS_FREE, mtr);
+
+ mlog_write_dulint(header + FSP_SEG_ID, ut_dulint_create(0, 1),
+ MLOG_8BYTES, mtr);
+}
+
+/**************************************************************************
+Increases the space size field of a space. */
+
+void
+fsp_header_inc_size(
+/*================*/
+ ulint space, /* in: space id */
+ ulint size_inc,/* in: size increment in pages */
+ mtr_t* mtr) /* in: mini-transaction handle */
+{
+ fsp_header_t* header;
+ ulint size;
+
+ ut_ad(mtr);
+
+ mtr_x_lock(&fsp_latch, mtr);
+
+ header = fsp_get_space_header(space, mtr);
+
+ size = mtr_read_ulint(header + FSP_SIZE, MLOG_4BYTES, mtr);
+ mlog_write_ulint(header + FSP_SIZE, size + size_inc, MLOG_4BYTES, mtr);
+}
+
+/**************************************************************************
+Puts new extents to the free list if there are free extents above the free
+limit. If an extent happens to contain an extent descriptor page, the extent
+is put to the FSP_FREE_FRAG list with the page marked as used. */
+static
+void
+fsp_fill_free_list(
+/*===============*/
+ ulint space, /* in: space */
+ fsp_header_t* header, /* in: space header */
+ mtr_t* mtr) /* in: mtr */
+{
+ ulint limit;
+ ulint size;
+ ulint i;
+ xdes_t* descr;
+ ulint count = 0;
+ ulint frag_n_used;
+
+ ut_ad(header && mtr);
+
+ /* Check if we can fill free list from above the free list limit */
+ size = mtr_read_ulint(header + FSP_SIZE, MLOG_4BYTES, mtr);
+ limit = mtr_read_ulint(header + FSP_FREE_LIMIT, MLOG_4BYTES, mtr);
+
+ i = limit;
+ while ((i + FSP_EXTENT_SIZE <= size) && (count < FSP_FREE_ADD)) {
+ mlog_write_ulint(header + FSP_FREE_LIMIT,
+ i + FSP_EXTENT_SIZE, MLOG_4BYTES, mtr);
+
+ descr = xdes_get_descriptor_with_space_hdr(header, space, i,
+ mtr);
+ xdes_init(descr, mtr);
+
+ ut_ad(XDES_DESCRIBED_PER_PAGE % FSP_EXTENT_SIZE == 0);
+
+ if (0 == i % XDES_DESCRIBED_PER_PAGE) {
+ /* The first page in the extent is a descriptor page:
+ mark it used */
+ xdes_set_bit(descr, XDES_FREE_BIT, 0, FALSE, mtr);
+ xdes_set_state(descr, XDES_FREE_FRAG, mtr);
+ flst_add_last(header + FSP_FREE_FRAG,
+ descr + XDES_FLST_NODE, mtr);
+ frag_n_used = mtr_read_ulint(header + FSP_FRAG_N_USED,
+ MLOG_4BYTES, mtr);
+ mlog_write_ulint(header + FSP_FRAG_N_USED,
+ frag_n_used + 1,
+ MLOG_4BYTES, mtr);
+ } else {
+ flst_add_last(header + FSP_FREE,
+ descr + XDES_FLST_NODE, mtr);
+ count++;
+ }
+ i += FSP_EXTENT_SIZE;
+ }
+}
+
+/**************************************************************************
+Allocates a new free extent. */
+static
+xdes_t*
+fsp_alloc_free_extent(
+/*==================*/
+ /* out: extent descriptor, NULL if cannot
+ be allocated */
+ ulint space, /* in: space id */
+ ulint hint, /* in: hint of which extent would be
+ desirable: any page offset in the extent
+ goes; the hint must not be > FSP_FREE_LIMIT */
+ mtr_t* mtr) /* in: mtr */
+{
+ fsp_header_t* header;
+ fil_addr_t first;
+ xdes_t* descr;
+
+ ut_ad(mtr);
+
+ header = fsp_get_space_header(space, mtr);
+
+ descr = xdes_get_descriptor_with_space_hdr(header, space, hint, mtr);
+
+ if (descr && (xdes_get_state(descr, mtr) == XDES_FREE)) {
+ /* Ok, we can take this extent */
+ } else {
+ /* Take the first extent in the free list */
+ first = flst_get_first(header + FSP_FREE, mtr);
+
+ if (fil_addr_is_null(first)) {
+ fsp_fill_free_list(space, header, mtr);
+ first = flst_get_first(header + FSP_FREE, mtr);
+ }
+
+ if (fil_addr_is_null(first)) {
+ return(NULL); /* No free extents left */
+ }
+
+ descr = xdes_lst_get_descriptor(space, first, mtr);
+ }
+
+ flst_remove(header + FSP_FREE, descr + XDES_FLST_NODE, mtr);
+
+ return(descr);
+}
+
+/**************************************************************************
+Allocates a single free page from a space. The page is marked as used. */
+static
+ulint
+fsp_alloc_free_page(
+/*================*/
+ /* out: the page offset, FIL_NULL
+ if no page could be allocated */
+ ulint space, /* in: space id */
+ ulint hint, /* in: hint of which page would be desirable */
+ mtr_t* mtr) /* in: mtr handle */
+{
+ fsp_header_t* header;
+ fil_addr_t first;
+ xdes_t* descr;
+ ulint free;
+ ulint frag_n_used;
+
+ ut_ad(mtr);
+
+ header = fsp_get_space_header(space, mtr);
+
+ /* Get the hinted descriptor */
+ descr = xdes_get_descriptor_with_space_hdr(header, space, hint, mtr);
+
+ if (descr && (xdes_get_state(descr, mtr) == XDES_FREE_FRAG)) {
+ /* Ok, we can take this extent */
+ } else {
+ /* Else take the first extent in free_frag list */
+ first = flst_get_first(header + FSP_FREE_FRAG, mtr);
+
+ if (fil_addr_is_null(first)) {
+ /* There are no partially full fragments: allocate
+ a free extent and add it to the FREE_FRAG
+ list. NOTE that the allocation may have as a
+ side-effect that an extent containing a descriptor
+ page is added to the FREE_FRAG list. But we will
+ allocate our page from the allocated free extent. */
+
+ descr = fsp_alloc_free_extent(space, hint, mtr);
+
+ if (descr == NULL) {
+ /* No free space left */
+ return(FIL_NULL);
+ }
+
+ xdes_set_state(descr, XDES_FREE_FRAG, mtr);
+ flst_add_last(header + FSP_FREE_FRAG,
+ descr + XDES_FLST_NODE, mtr);
+ } else {
+ descr = xdes_lst_get_descriptor(space, first, mtr);
+ }
+
+ /* Reset the hint */
+ hint = 0;
+ }
+
+ /* Now we have in descr an extent with at least one free page.
+ Look for a free page in the extent. */
+ free = xdes_find_bit(descr, XDES_FREE_BIT, TRUE,
+ hint % FSP_EXTENT_SIZE, mtr);
+ ut_a(free != ULINT_UNDEFINED);
+
+ xdes_set_bit(descr, XDES_FREE_BIT, free, FALSE, mtr);
+
+ /* Update the FRAG_N_USED field */
+ frag_n_used = mtr_read_ulint(header + FSP_FRAG_N_USED,
+ MLOG_4BYTES, mtr);
+ frag_n_used++;
+ mlog_write_ulint(header + FSP_FRAG_N_USED, frag_n_used,
+ MLOG_4BYTES, mtr);
+
+ if (xdes_is_full(descr, mtr)) {
+ /* The fragment is full: move it to another list */
+ flst_remove(header + FSP_FREE_FRAG,
+ descr + XDES_FLST_NODE, mtr);
+ xdes_set_state(descr, XDES_FULL_FRAG, mtr);
+ flst_add_last(header + FSP_FULL_FRAG,
+ descr + XDES_FLST_NODE, mtr);
+ mlog_write_ulint(header + FSP_FRAG_N_USED,
+ frag_n_used - FSP_EXTENT_SIZE,
+ MLOG_4BYTES, mtr);
+ }
+ return(xdes_get_offset(descr) + free);
+}
+
+/**************************************************************************
+Frees a single page of a space. The page is marked as free and clean. */
+static
+void
+fsp_free_page(
+/*==========*/
+ ulint space, /* in: space id */
+ ulint page, /* in: page offset */
+ mtr_t* mtr) /* in: mtr handle */
+{
+ fsp_header_t* header;
+ xdes_t* descr;
+ ulint state;
+ ulint frag_n_used;
+
+ ut_ad(mtr);
+
+ header = fsp_get_space_header(space, mtr);
+
+ descr = xdes_get_descriptor_with_space_hdr(header, space, page, mtr);
+
+ state = xdes_get_state(descr, mtr);
+
+ ut_a((state == XDES_FREE_FRAG) || (state == XDES_FULL_FRAG));
+
+ ut_a(xdes_get_bit(descr, XDES_FREE_BIT, page % FSP_EXTENT_SIZE, mtr)
+ == FALSE);
+
+ xdes_set_bit(descr, XDES_FREE_BIT, page % FSP_EXTENT_SIZE, TRUE, mtr);
+ xdes_set_bit(descr, XDES_CLEAN_BIT, page % FSP_EXTENT_SIZE, TRUE, mtr);
+
+ frag_n_used = mtr_read_ulint(header + FSP_FRAG_N_USED,
+ MLOG_4BYTES, mtr);
+
+ if (state == XDES_FULL_FRAG) {
+ /* The fragment was full: move it to another list */
+ flst_remove(header + FSP_FULL_FRAG,
+ descr + XDES_FLST_NODE, mtr);
+ xdes_set_state(descr, XDES_FREE_FRAG, mtr);
+ flst_add_last(header + FSP_FREE_FRAG,
+ descr + XDES_FLST_NODE, mtr);
+ mlog_write_ulint(header + FSP_FRAG_N_USED,
+ frag_n_used + FSP_EXTENT_SIZE - 1,
+ MLOG_4BYTES, mtr);
+ } else {
+ ut_a(frag_n_used > 0);
+ mlog_write_ulint(header + FSP_FRAG_N_USED, frag_n_used - 1,
+ MLOG_4BYTES, mtr);
+ }
+
+ if (xdes_is_free(descr, mtr)) {
+ /* The extent has become free: move it to another list */
+ flst_remove(header + FSP_FREE_FRAG,
+ descr + XDES_FLST_NODE, mtr);
+ fsp_free_extent(space, page, mtr);
+ }
+}
+
+/**************************************************************************
+Returns an extent to the free list of a space. */
+static
+void
+fsp_free_extent(
+/*============*/
+ ulint space, /* in: space id */
+ ulint page, /* in: page offset in the extent */
+ mtr_t* mtr) /* in: mtr */
+{
+ fsp_header_t* header;
+ xdes_t* descr;
+
+ ut_ad(mtr);
+
+ header = fsp_get_space_header(space, mtr);
+
+ descr = xdes_get_descriptor_with_space_hdr(header, space, page, mtr);
+
+ ut_a(xdes_get_state(descr, mtr) != XDES_FREE);
+
+ xdes_init(descr, mtr);
+
+ flst_add_last(header + FSP_FREE, descr + XDES_FLST_NODE, mtr);
+}
+
+/**************************************************************************
+Looks for an unused segment header on a segment header page. */
+UNIV_INLINE
+fseg_header_t*
+fsp_seg_hdr_page_get_nth_hdr(
+/*=========================*/
+ /* out: segment header */
+ page_t* page, /* in: segment header page */
+ ulint i, /* in: search forward starting from this index */
+ mtr_t* mtr) /* in: mini-transaction handle */
+{
+ ut_ad(i < FSP_SEG_HDRS_PER_PAGE);
+ ut_ad(mtr_memo_contains(mtr, page, MTR_MEMO_PAGE_X_LOCK));
+
+ return(page + FSEG_ARR_OFFSET + FSEG_HEADER_SIZE * i);
+}
+
+/**************************************************************************
+Looks for a used segment header on a segment header page. */
+static
+ulint
+fsp_seg_hdr_page_find_used(
+/*=======================*/
+ /* out: segment header index, or ULINT_UNDEFINED
+ if not found */
+ page_t* page, /* in: segment header page */
+ mtr_t* mtr) /* in: mini-transaction handle */
+{
+ ulint i;
+ fseg_header_t* header;
+
+ for (i = 0; i < FSP_SEG_HDRS_PER_PAGE; i++) {
+
+ header = fsp_seg_hdr_page_get_nth_hdr(page, i, mtr);
+
+ if (ut_dulint_cmp(mach_read_from_8(header + FSEG_ID),
+ ut_dulint_zero) != 0) {
+ /* This is used */
+
+ return(i);
+ }
+ }
+
+ return(ULINT_UNDEFINED);
+}
+
+/**************************************************************************
+Looks for an unused segment header on a segment header page. */
+static
+ulint
+fsp_seg_hdr_page_find_free(
+/*=======================*/
+ /* out: segment header index, or ULINT_UNDEFINED
+ if not found */
+ page_t* page, /* in: segment header page */
+ ulint j, /* in: search forward starting from this index */
+ mtr_t* mtr) /* in: mini-transaction handle */
+{
+ ulint i;
+ fseg_header_t* header;
+
+ for (i = j; i < FSP_SEG_HDRS_PER_PAGE; i++) {
+
+ header = fsp_seg_hdr_page_get_nth_hdr(page, i, mtr);
+
+ if (ut_dulint_cmp(mach_read_from_8(header + FSEG_ID),
+ ut_dulint_zero) == 0) {
+ /* This is unused */
+
+ return(i);
+ }
+ }
+
+ return(ULINT_UNDEFINED);
+}
+
+/**************************************************************************
+Allocates a new file segment header page. */
+static
+bool
+fsp_alloc_seg_hdr_page(
+/*===================*/
+ /* out: TRUE if could be allocated */
+ fsp_header_t* space_header, /* in: space header */
+ mtr_t* mtr) /* in: mini-transaction handle */
+{
+ buf_block_t* block;
+ ulint page_no;
+ page_t* page;
+ fseg_header_t* header;
+ ulint i;
+
+ page_no = fsp_alloc_free_page(buf_frame_get_space(space_header),
+ 0, mtr);
+ if (page_no == FIL_NULL) {
+
+ return(FALSE);
+ }
+
+ block = buf_page_get(buf_frame_get_space(space_header), page_no, mtr);
+
+ buf_page_x_lock(block, mtr);
+
+ page = buf_block_get_frame(block);
+
+ for (i = 0; i < FSP_SEG_HDRS_PER_PAGE; i++) {
+
+ header = fsp_seg_hdr_page_get_nth_hdr(page, i, mtr);
+
+ mlog_write_dulint(header + FSEG_ID, ut_dulint_zero,
+ MLOG_8BYTES, mtr);
+ }
+
+ flst_add_last(space_header + FSP_SEG_HDRS_FREE,
+ page + FSEG_HDR_PAGE_NODE, mtr);
+ return(TRUE);
+}
+
+/**************************************************************************
+Allocates a new file segment header. */
+static
+fseg_header_t*
+fsp_alloc_seg_header(
+/*=================*/
+ /* out: segment header, or NULL if
+ not enough space */
+ fsp_header_t* space_header, /* in: space header */
+ mtr_t* mtr) /* in: mini-transaction handle */
+{
+ buf_block_t* block;
+ ulint page_no;
+ page_t* page;
+ fseg_header_t* header;
+ ulint n;
+ bool success;
+
+ if (flst_get_len(space_header + FSP_SEG_HDRS_FREE, mtr) == 0) {
+ /* Allocate a new segment header page */
+
+ success = fsp_alloc_seg_hdr_page(space_header, mtr);
+
+ if (!success) {
+
+ return(NULL);
+ }
+ }
+
+ page_no = flst_get_first(space_header + FSP_SEG_HDRS_FREE, mtr).page;
+
+ block = buf_page_get(buf_frame_get_space(space_header), page_no, mtr);
+
+ buf_page_x_lock(block, mtr);
+
+ page = buf_block_get_frame(block);
+
+ n = fsp_seg_hdr_page_find_free(page, 0, mtr);
+
+ ut_a(n != ULINT_UNDEFINED);
+
+ header = fsp_seg_hdr_page_get_nth_hdr(page, n, mtr);
+
+ if (ULINT_UNDEFINED == fsp_seg_hdr_page_find_free(page, n + 1, mtr)) {
+
+ /* There are no other unused headers left on the page: move it
+ to another list */
+
+ flst_remove(space_header + FSP_SEG_HDRS_FREE,
+ page + FSEG_HDR_PAGE_NODE, mtr);
+
+ flst_add_last(space_header + FSP_SEG_HDRS_FULL,
+ page + FSEG_HDR_PAGE_NODE, mtr);
+ }
+
+ return(header);
+}
+
+/**************************************************************************
+Frees a file segment header. */
+static
+void
+fsp_free_seg_header(
+/*================*/
+ ulint space, /* in: space id */
+ fseg_header_t* header, /* in: segment header */
+ mtr_t* mtr) /* in: mini-transaction handle */
+{
+ page_t* page;
+ fsp_header_t* space_header;
+
+ page = buf_frame_align(header);
+
+ space_header = fsp_get_space_header(space, mtr);
+
+ ut_ad(mach_read_from_4(header + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE);
+
+ if (ULINT_UNDEFINED == fsp_seg_hdr_page_find_free(page, mtr)) {
+
+ /* Move the page to another list */
+
+ flst_remove(space_header + FSP_SEG_HDRS_FULL,
+ page + FSEG_HDR_PAGE_NODE, mtr);
+
+ flst_add_last(space_header + FSP_SEG_HDRS_FREE,
+ page + FSEG_HDR_PAGE_NODE, mtr);
+ }
+
+ mlog_write_dulint(header + FSEG_ID, ut_dulint_zero, MLOG_8BYTES, mtr);
+ mlog_write_ulint(header + FSEG_MAGIC_N, 0, MLOG_4BYTES, mtr);
+
+ if (ULINT_UNDEFINED == fsp_seg_hdr_page_find_used(page, mtr)) {
+
+ /* There are no other used headers left on the page: free it */
+
+ flst_remove(space_header + FSP_SEG_HDRS_FREE,
+ page + FSEG_HDR_PAGE_NODE, mtr);
+
+ fsp_free_page(space, page_no, mtr);
+ }
+}
+
+/**************************************************************************
+Gets the page number from the nth fragment page slot. */
+UNIV_INLINE
+ulint
+fseg_get_nth_frag_page_no(
+/*======================*/
+ /* out: page number, FIL_NULL if not in use */
+ fseg_header_t* header, /* in: segment header */
+ ulint n, /* in: slot index */
+ mtr_t* mtr) /* in: mtr handle */
+{
+ ut_ad(header && mtr);
+ ut_ad(n < FSEG_FRAG_ARR_N_SLOTS);
+ ut_ad(mtr_memo_contains(mtr, buf_block_align(header),
+ MTR_MEMO_PAGE_X_LOCK));
+
+ return(mach_read_from_4(header + FSEG_FRAG_ARR
+ + n * FSEG_FRAG_SLOT_SIZE));
+}
+
+/**************************************************************************
+Sets the page number in the nth fragment page slot. */
+UNIV_INLINE
+void
+fseg_set_nth_frag_page_no(
+/*======================*/
+ fseg_header_t* header, /* in: segment header */
+ ulint n, /* in: slot index */
+ ulint page_no,/* in: page number to set */
+ mtr_t* mtr) /* in: mtr handle */
+{
+ ut_ad(header && mtr);
+ ut_ad(n < FSEG_FRAG_ARR_N_SLOTS);
+ ut_ad(mtr_memo_contains(mtr, buf_block_align(header),
+ MTR_MEMO_PAGE_X_LOCK));
+
+ mlog_write_ulint(header + FSEG_FRAG_ARR + n * FSEG_FRAG_SLOT_SIZE,
+ page_no, MLOG_4BYTES, mtr);
+}
+
+/**************************************************************************
+Finds a fragment page slot which is free. */
+static
+ulint
+fseg_find_free_frag_page_slot(
+/*==========================*/
+ /* out: slot index; ULINT_UNDEFINED if none
+ found */
+ fseg_header_t* header, /* in: segment header */
+ mtr_t* mtr) /* in: mtr handle */
+{
+ ulint i;
+ ulint page_no;
+
+ ut_ad(header && mtr);
+
+ for (i = 0; i < FSEG_FRAG_ARR_N_SLOTS; i++) {
+ page_no = fseg_get_nth_frag_page_no(header, i, mtr);
+
+ if (page_no == FIL_NULL) {
+
+ return(i);
+ }
+ }
+ return(ULINT_UNDEFINED);
+}
+
+/**************************************************************************
+Finds a fragment page slot which is used and last in the array. */
+static
+ulint
+fseg_find_last_used_frag_page_slot(
+/*===============================*/
+ /* out: slot index; ULINT_UNDEFINED if none
+ found */
+ fseg_header_t* header, /* in: segment header */
+ mtr_t* mtr) /* in: mtr handle */
+{
+ ulint i;
+ ulint page_no;
+
+ ut_ad(header && mtr);
+
+ for (i = 0; i < FSEG_FRAG_ARR_N_SLOTS; i++) {
+ page_no = fseg_get_nth_frag_page_no(header,
+ FSEG_ARR_N_SLOTS - i - 1, mtr);
+
+ if (page_no != FIL_NULL) {
+
+ return(i);
+ }
+ }
+ return(ULINT_UNDEFINED);
+}
+
+/**************************************************************************
+Calculates reserved fragment page slots. */
+static
+ulint
+fseg_get_n_frag_pages(
+/*==================*/
+ /* out: number of fragment pages */
+ fseg_header_t* header, /* in: segment header */
+ mtr_t* mtr) /* in: mtr handle */
+{
+ ulint i;
+ ulint count = 0;
+
+ ut_ad(header && mtr);
+
+ for (i = 0; i < FSEG_FRAG_ARR_N_SLOTS; i++) {
+ if (FIL_NULL != fseg_get_nth_frag_page_no(header, i, mtr)) {
+ count++;
+ }
+ }
+ return(count);
+}
+
+/**************************************************************************
+Creates a new segment. */
+
+ulint
+fseg_create(
+/*========*/
+ /* out: the page number where the segment header is
+ placed, FIL_NULL if could not create segment because
+ lack of space */
+ ulint space, /* in: space id */
+ ulint* offset, /* out: byte offset of the segment header on its
+ page */
+ mtr_t* mtr) /* in: mtr */
+{
+ buf_block_t* block;
+ buf_frame_t* frame;
+ fsp_header_t* space_header;
+ fseg_header_t* header;
+ dulint seg_id;
+ ulint i;
+
+ ut_ad(mtr);
+
+ mtr_x_lock(&fsp_latch, mtr);
+
+ space_header = fsp_get_space_header(space, mtr);
+
+ header = fsp_alloc_seg_header(space_header, mtr);
+
+ if (header == NULL) {
+
+ return(FIL_NULL);
+ }
+
+ /* Read the next segment id from space header and increment the
+ value in space header */
+
+ seg_id = mtr_read_dulint(space_header + FSP_SEG_ID, MLOG_8BYTES, mtr);
+
+ mlog_write_dulint(space_header + FSP_SEG_ID, ut_dulint_add(seg_id, 1),
+ MLOG_8BYTES, mtr);
+
+ mlog_write_dulint(header + FSEG_ID, seg_id, MLOG_8BYTES, mtr);
+ mlog_write_ulint(header + FSEG_NOT_FULL_N_USED, 0, MLOG_4BYTES, mtr);
+
+ flst_init(header + FSEG_FREE, mtr);
+ flst_init(header + FSEG_NOT_FULL, mtr);
+ flst_init(header + FSEG_FULL, mtr);
+
+ mlog_write_ulint(header + FSEG_MAGIC_N, FSEG_MAGIC_N_VALUE,
+ MLOG_4BYTES, mtr);
+ for (i = 0; i < FSEG_FRAG_ARR_N_SLOTS; i++) {
+ fseg_set_nth_frag_page_no(header, i, FIL_NULL, mtr);
+ }
+
+ *offset = header - buf_frame_align(header);
+ return(buf_frame_get_page(buf_frame_align(header)));
+}
+
+/**************************************************************************
+Calculates the number of pages reserved by a segment, and how
+many pages are currently used. */
+
+ulint
+fseg_n_reserved_pages(
+/*==================*/
+ /* out: number of reserved pages */
+ fseg_header_t* header, /* in: segment header */
+ ulint* used, /* out: number of pages used (<= reserved) */
+ mtr_t* mtr) /* in: mtr handle */
+{
+ ulint ret;
+
+ mtr_x_lock(&fsp_latch, mtr);
+
+ ret = fseg_n_reserved_pages_low(header, used, mtr);
+
+ return(ret);
+}
+
+/**************************************************************************
+Calculates the number of pages reserved by a segment, and how
+many pages are currently used. */
+static
+ulint
+fseg_n_reserved_pages_low(
+/*======================*/
+ /* out: number of reserved pages */
+ fseg_header_t* header, /* in: segment header */
+ ulint* used, /* out: number of pages used (<= reserved) */
+ mtr_t* mtr) /* in: mtr handle */
+{
+ ulint ret;
+
+ ut_ad(header && used && mtr);
+ ut_ad(mtr_memo_contains(mtr, buf_block_align(header),
+ MTR_MEMO_BUF_FIX));
+
+ buf_page_x_lock(buf_block_align(header), mtr);
+
+ *used = mtr_read_ulint(header + FSEG_NOT_FULL_N_USED, MLOG_4BYTES, mtr)
+ + FSP_EXTENT_SIZE * flst_get_len(header + FSEG_FULL, mtr)
+ + fseg_get_n_frag_pages(header, mtr);
+
+ ret = fseg_get_n_frag_pages(header, mtr)
+ + FSP_EXTENT_SIZE * flst_get_len(header + FSEG_FREE, mtr)
+ + FSP_EXTENT_SIZE * flst_get_len(header + FSEG_NOT_FULL, mtr)
+ + FSP_EXTENT_SIZE * flst_get_len(header + FSEG_FULL, mtr);
+
+ return(ret);
+}
+
+/*************************************************************************
+Tries to fill the free list of a segment with consecutive free extents.
+This happens if the segment is big enough to allowextents in the free list,
+the free list is empty, and the extents can be allocated consecutively from
+the hint onward. */
+static
+void
+fseg_fill_free_list(
+/*================*/
+ fseg_header_t* header, /* in: segment header */
+ ulint space, /* in: space id */
+ ulint hint, /* in: hint which extent would be good as
+ the first extent */
+ mtr_t* mtr) /* in: mtr */
+{
+ xdes_t* descr;
+ ulint i;
+ dulint seg_id;
+ ulint reserved;
+ ulint used;
+
+ ut_ad(header && mtr);
+
+ buf_page_x_lock(buf_block_align(header), mtr);
+
+ reserved = fseg_n_reserved_pages_low(header, &used, mtr);
+
+ if (reserved < FSEG_FREE_LIST_LIMIT * FSP_EXTENT_SIZE) {
+ /* The segment is too small to allow extents in free list */
+
+ return;
+ }
+
+ if (flst_get_len(header + FSEG_FREE, mtr) > 0) {
+ /* Free list is not empty */
+
+ return;
+ }
+
+ for (i = 0; i < FSEG_FREE_LIST_MAX_LEN; i++) {
+ descr = xdes_get_descriptor(space, hint, mtr);
+
+ if ((descr == NULL) ||
+ (XDES_FREE != xdes_get_state(descr, mtr))) {
+ /* We cannot allocate the desired extent: stop */
+
+ return;
+ }
+
+ descr = fsp_alloc_free_extent(space, hint, mtr);
+
+ xdes_set_state(descr, XDES_FSEG, mtr);
+
+ seg_id = mtr_read_dulint(header + FSEG_ID, MLOG_8BYTES, mtr);
+ mlog_write_dulint(descr + XDES_ID, seg_id, MLOG_8BYTES, mtr);
+
+ flst_add_last(header + FSEG_FREE, descr + XDES_FLST_NODE, mtr);
+ hint += FSP_EXTENT_SIZE;
+ }
+}
+
+/*************************************************************************
+Allocates a free extent for the segment: looks first in the
+free list of the segment, then tries to allocate from the space free
+list. NOTE that the extent returned is still placed in the segment free
+list, not taken off it! */
+static
+xdes_t*
+fseg_alloc_free_extent(
+/*===================*/
+ /* out: allocated extent, still placed in the
+ segment free list, NULL if could
+ not be allocated */
+ fseg_header_t* header, /* in: segment header */
+ ulint space, /* in: space id */
+ mtr_t* mtr) /* in: mtr */
+{
+ xdes_t* descr;
+ dulint seg_id;
+ fil_addr_t first;
+
+ buf_page_x_lock(buf_block_align(header), mtr);
+
+ if (flst_get_len(header + FSEG_FREE, mtr) > 0) {
+ /* Segment free list is not empty, allocate from it */
+
+ first = flst_get_first(header + FSEG_FREE, mtr);
+
+ descr = xdes_lst_get_descriptor(space, first, mtr);
+ } else {
+ /* Segment free list was empty, allocate from space */
+ descr = fsp_alloc_free_extent(space, 0, mtr);
+
+ if (descr == NULL) {
+ return(NULL);
+ }
+
+ seg_id = mtr_read_dulint(header + FSEG_ID, MLOG_8BYTES, mtr);
+
+ xdes_set_state(descr, XDES_FSEG, mtr);
+ mlog_write_dulint(descr + XDES_ID, seg_id, MLOG_8BYTES, mtr);
+ flst_add_last(header + FSEG_FREE,
+ descr + XDES_FLST_NODE, mtr);
+
+ /* Try to fill the segment free list */
+ fseg_fill_free_list(header, space,
+ xdes_get_offset(descr) + FSP_EXTENT_SIZE, mtr);
+ }
+
+ return(descr);
+}
+
+/**************************************************************************
+Allocates a single free page from a segment. This function implements
+the intelligent allocation strategy which tries to minimize file space
+fragmentation. */
+
+ulint
+fseg_alloc_free_page(
+/*=================*/
+ /* out: the allocated page offset
+ FIL_NULL if no page could be allocated */
+ fseg_header_t* seg_header, /* in: segment header */
+ ulint hint, /* in: hint of which page would be desirable */
+ byte direction, /* in: if the new page is needed because
+ of an index page split, and records are
+ inserted there in order, into which
+ direction they go alphabetically: FSP_DOWN,
+ FSP_UP, FSP_NO_DIR */
+ mtr_t* mtr) /* in: mtr handle */
+{
+ buf_block_t* block;
+ dulint seg_id;
+ fseg_page_header_t* page_header;
+ ulint space;
+ ulint used;
+ ulint reserved;
+ fil_addr_t first;
+ xdes_t* descr; /* extent of the hinted page */
+ ulint ret_page; /* the allocated page offset, FIL_NULL
+ if could not be allocated */
+ buf_block_t* ret_buf_page;
+ buf_frame_t* ret_frame;
+ xdes_t* ret_descr; /* the extent of the allocated page */
+ ulint n;
+ bool frag_page_allocated = FALSE;
+
+ ut_ad(seg_header && mtr);
+ ut_ad((direction >= FSP_UP) && (direction <= FSP_NO_DIR));
+
+ mtr_x_lock(&fsp_latch, mtr);
+
+ block = buf_block_align(seg_header);
+ buf_page_x_lock(block, mtr);
+
+ space = buf_page_get_space(block);
+
+ seg_id = mtr_read_dulint(seg_header + FSEG_ID, MLOG_8BYTES, mtr);
+
+ ut_ad(ut_dulint_cmp(seg_id, ut_dulint_zero) > 0);
+
+ reserved = fseg_n_reserved_pages_low(seg_header, &used, mtr);
+
+ descr = xdes_get_descriptor(space, hint, mtr);
+
+ if (descr == NULL) {
+ /* Hint outside space or too high above free limit:
+ reset hint */
+ hint = 0;
+ descr = xdes_get_descriptor(space, hint, mtr);
+ }
+
+ /* In the big if-else below we look for ret_page and ret_descr */
+ /*-------------------------------------------------------------*/
+ if ((xdes_get_state(descr, mtr) == XDES_FSEG)
+ && (0 == ut_dulint_cmp(mtr_read_dulint(descr + XDES_ID,
+ MLOG_8BYTES, mtr),
+ seg_id))
+ && (xdes_get_bit(descr, XDES_FREE_BIT,
+ hint % FSP_EXTENT_SIZE, mtr) == TRUE)) {
+
+ /* 1. We can take the hinted page
+ =================================*/
+ ret_descr = descr;
+ ret_page = hint;
+ /*-------------------------------------------------------------*/
+ } else if ((xdes_get_state(descr, mtr) == XDES_FREE)
+ && ((reserved - used) < reserved / FSEG_FILLFACTOR)
+ && (used >= FSEG_FRAG_LIMIT)) {
+
+ /* 2. We allocate the free extent from space and can take
+ =========================================================
+ the hinted page
+ ===============*/
+ ret_descr = fsp_alloc_free_extent(space, hint, mtr);
+
+ ut_a(ret_descr == descr);
+
+ xdes_set_state(ret_descr, XDES_FSEG, mtr);
+ mlog_write_dulint(ret_descr + XDES_ID, seg_id, MLOG_8BYTES,
+ mtr);
+ flst_add_last(seg_header + FSEG_FREE,
+ ret_descr + XDES_FLST_NODE, mtr);
+
+ /* Try to fill the segment free list */
+ fseg_fill_free_list(seg_header, space,
+ hint + FSP_EXTENT_SIZE, mtr);
+ ret_page = hint;
+ /*-------------------------------------------------------------*/
+ } else if ((direction != FSP_NO_DIR)
+ && ((reserved - used) < reserved / FSEG_FILLFACTOR)
+ && (used >= FSEG_FRAG_LIMIT)
+ && (NULL != (ret_descr =
+ fseg_alloc_free_extent(seg_header, space, mtr)))) {
+
+ /* 3. We take any free extent (which was already assigned above
+ ===============================================================
+ in the if-condition to ret_descr) and take the lowest or
+ ========================================================
+ highest page in it, depending on the direction
+ ==============================================*/
+ ret_page = xdes_get_offset(ret_descr);
+ if (direction == FSP_DOWN) {
+ ret_page += FSP_EXTENT_SIZE - 1;
+ }
+ /*-------------------------------------------------------------*/
+ } else if ((xdes_get_state(descr, mtr) == XDES_FSEG)
+ && (0 == ut_dulint_cmp(mtr_read_dulint(descr + XDES_ID,
+ MLOG_8BYTES, mtr),
+ seg_id))
+ && (!xdes_is_full(descr, mtr))) {
+
+ /* 4. We can take the page from the same extent as the
+ ======================================================
+ hinted page (and the extent already belongs to the
+ ==================================================
+ segment)
+ ========*/
+ ret_descr = descr;
+ ret_page = xdes_get_offset(ret_descr) +
+ xdes_find_bit(ret_descr, XDES_FREE_BIT, TRUE,
+ hint % FSP_EXTENT_SIZE, mtr);
+ /*-------------------------------------------------------------*/
+ } else if (reserved - used > 0) {
+ /* 5. We take any unused page from the segment
+ ==============================================*/
+ if (flst_get_len(seg_header + FSEG_NOT_FULL, mtr) > 0) {
+ first = flst_get_first(seg_header + FSEG_NOT_FULL,
+ mtr);
+ } else if (flst_get_len(seg_header + FSEG_FREE, mtr) > 0) {
+ first = flst_get_first(seg_header + FSEG_FREE, mtr);
+ } else {
+ ut_error;
+ }
+
+ ret_descr = xdes_lst_get_descriptor(space, first, mtr);
+ ret_page = xdes_get_offset(ret_descr) +
+ xdes_find_bit(ret_descr, XDES_FREE_BIT, TRUE,
+ 0, mtr);
+ /*-------------------------------------------------------------*/
+ } else if (used < FSEG_FRAG_LIMIT) {
+ /* 6. We allocate an individual page from the space
+ ===================================================*/
+ ret_page = fsp_alloc_free_page(space, hint, mtr);
+ ret_descr = NULL;
+
+ frag_page_allocated = TRUE;
+
+ if (ret_page != FIL_NULL) {
+ /* Put the page in the fragment page array of the
+ segment */
+ n = fseg_find_free_frag_page_slot(seg_header, mtr);
+ ut_a(n != FIL_NULL);
+
+ fseg_set_nth_frag_page_no(seg_header, n, ret_page,
+ mtr);
+ }
+ /*-------------------------------------------------------------*/
+ } else {
+ /* 7. We allocate a new extent and take its first page
+ ======================================================*/
+ ret_descr = fseg_alloc_free_extent(seg_header, space, mtr);
+
+ if (ret_descr == NULL) {
+ ret_page = FIL_NULL;
+ } else {
+ ret_page = xdes_get_offset(ret_descr);
+ }
+ }
+
+ if (ret_page == FIL_NULL) {
+ /* Page could not be allocated */
+
+ return(FIL_NULL);
+ }
+
+ /* Initialize the allocated page to buffer pool, so that it can be
+ obtained immediately with buf_page_get without need for disk read */
+
+ ret_buf_page = buf_page_create(space, ret_page, mtr);
+
+ if (!frag_page_allocated) {
+ /* At this point we know the extent and the page offset.
+ The extent is still in the appropriate list (FSEG_NOT_FULL or
+ FSEG_FREE), and the page is not yet marked as used. */
+
+ ut_ad(xdes_get_descriptor(space, ret_page, mtr) == ret_descr);
+ ut_ad(xdes_get_bit(ret_descr, XDES_FREE_BIT,
+ ret_page % FSP_EXTENT_SIZE, mtr) == TRUE);
+
+ fseg_mark_page_used(seg_header, space, ret_page, mtr);
+ }
+
+ return(ret_page);
+}
+
+/************************************************************************
+Marks a page used. The page must reside within the extents of the given
+segment. */
+static
+void
+fseg_mark_page_used(
+/*================*/
+ fseg_header_t* seg_header,/* in: segment header */
+ ulint space, /* in: space id */
+ ulint page, /* in: page offset */
+ mtr_t* mtr) /* in: mtr */
+{
+ xdes_t* descr;
+ ulint not_full_n_used;
+
+ ut_ad(seg_header && mtr);
+
+ descr = xdes_get_descriptor(space, page, mtr);
+
+ ut_ad(mtr_read_ulint(seg_header + FSEG_ID, MLOG_4BYTES, mtr) ==
+ mtr_read_ulint(descr + XDES_ID, MLOG_4BYTES, mtr));
+
+ if (xdes_is_free(descr, mtr)) {
+ /* We move the extent from the free list to the
+ NOT_FULL list */
+ flst_remove(seg_header + FSEG_FREE,
+ descr + XDES_FLST_NODE, mtr);
+ flst_add_last(seg_header + FSEG_NOT_FULL,
+ descr + XDES_FLST_NODE, mtr);
+ }
+
+ ut_ad(xdes_get_bit(descr, XDES_FREE_BIT, page % FSP_EXTENT_SIZE, mtr)
+ == TRUE);
+
+ /* We mark the page as used */
+ xdes_set_bit(descr, XDES_FREE_BIT, page % FSP_EXTENT_SIZE, FALSE, mtr);
+
+ not_full_n_used = mtr_read_ulint(seg_header + FSEG_NOT_FULL_N_USED,
+ MLOG_4BYTES, mtr);
+
+ not_full_n_used++;
+ mlog_write_ulint(seg_header + FSEG_NOT_FULL_N_USED,
+ not_full_n_used, MLOG_4BYTES, mtr);
+
+ if (xdes_is_full(descr, mtr)) {
+ /* We move the extent from the NOT_FULL list to the
+ FULL list */
+ flst_remove(seg_header + FSEG_NOT_FULL,
+ descr + XDES_FLST_NODE, mtr);
+ flst_add_last(seg_header + FSEG_FULL,
+ descr + XDES_FLST_NODE, mtr);
+
+ mlog_write_ulint(seg_header + FSEG_NOT_FULL_N_USED,
+ not_full_n_used - FSP_EXTENT_SIZE,
+ MLOG_4BYTES, mtr);
+ }
+}
+
+/**************************************************************************
+Frees a single page of a segment. */
+
+void
+fseg_free_page(
+/*===========*/
+ fseg_header_t* seg_header, /* in: segment header */
+ ulint space, /* in: space id */
+ ulint page, /* in: page offset */
+ mtr_t* mtr) /* in: mtr handle */
+{
+ mtr_x_lock(&fsp_latch, mtr);
+
+ fseg_free_page_low(seg_header, space, page, mtr);
+}
+
+/**************************************************************************
+Frees a single page of a segment. */
+static
+void
+fseg_free_page_low(
+/*===============*/
+ fseg_header_t* seg_header, /* in: segment header */
+ ulint space, /* in: space id */
+ ulint page, /* in: page offset */
+ mtr_t* mtr) /* in: mtr handle */
+{
+ buf_block_t* block;
+ xdes_t* descr;
+ ulint used;
+ ulint not_full_n_used;
+ ulint state;
+ buf_block_t* buf_page;
+ buf_frame_t* buf_frame;
+ ulint i;
+
+ ut_ad(seg_header && mtr);
+
+ block = buf_block_align(seg_header);
+ buf_page_x_lock(block, mtr);
+
+ descr = xdes_get_descriptor(space, page, mtr);
+
+ ut_a(descr);
+ ut_a(xdes_get_bit(descr, XDES_FREE_BIT, page % FSP_EXTENT_SIZE, mtr)
+ == FALSE);
+
+ state = xdes_get_state(descr, mtr);
+
+ if (state != XDES_FSEG) {
+ /* The page is in the fragment pages of the segment */
+
+ for (i = 0;; i++) {
+ if (fseg_get_nth_frag_page_no(seg_header, i, mtr)
+ == page) {
+
+ fseg_set_nth_frag_page_no(seg_header, i,
+ FIL_NULL, mtr);
+ break;
+ }
+ }
+
+ fsp_free_page(space, page, mtr);
+
+ return;
+ }
+
+ /* If we get here, the page is in some extent of the segment */
+ ut_a(0 == ut_dulint_cmp(
+ mtr_read_dulint(descr + XDES_ID, MLOG_8BYTES, mtr),
+ mtr_read_dulint(seg_header + FSEG_ID, MLOG_8BYTES, mtr)));
+
+ not_full_n_used = mtr_read_ulint(seg_header + FSEG_NOT_FULL_N_USED,
+ MLOG_4BYTES, mtr);
+ if (xdes_is_full(descr, mtr)) {
+ /* The fragment is full: move it to another list */
+ flst_remove(seg_header + FSEG_FULL,
+ descr + XDES_FLST_NODE, mtr);
+ flst_add_last(seg_header + FSEG_NOT_FULL,
+ descr + XDES_FLST_NODE, mtr);
+ mlog_write_ulint(seg_header + FSEG_NOT_FULL_N_USED,
+ not_full_n_used + FSP_EXTENT_SIZE - 1,
+ MLOG_4BYTES, mtr);
+ } else {
+ ut_a(not_full_n_used > 0);
+ mlog_write_ulint(seg_header + FSEG_NOT_FULL_N_USED,
+ not_full_n_used - 1,
+ MLOG_4BYTES, mtr);
+ }
+
+ xdes_set_bit(descr, XDES_FREE_BIT, page % FSP_EXTENT_SIZE, TRUE, mtr);
+ xdes_set_bit(descr, XDES_CLEAN_BIT, page % FSP_EXTENT_SIZE, TRUE, mtr);
+
+ if (xdes_is_free(descr, mtr)) {
+ /* The extent has become free: free it to space */
+ flst_remove(seg_header + FSEG_NOT_FULL,
+ descr + XDES_FLST_NODE, mtr);
+ fsp_free_extent(space, page, mtr);
+ }
+}
+
+/**************************************************************************
+Frees an extent of a segment to the space free list. */
+static
+void
+fseg_free_extent(
+/*=============*/
+ fseg_header_t* seg_header, /* in: segment header */
+ ulint space, /* in: space id */
+ ulint page, /* in: page offset in the extent */
+ mtr_t* mtr) /* in: mtr handle */
+{
+ buf_block_t* block;
+ xdes_t* descr;
+ ulint not_full_n_used;
+ ulint descr_n_used;
+
+ ut_ad(seg_header && mtr);
+
+ block = buf_block_align(seg_header);
+ buf_page_x_lock(block, mtr);
+
+ descr = xdes_get_descriptor(space, page, mtr);
+
+ ut_a(xdes_get_state(descr, mtr) == XDES_FSEG);
+ ut_a(0 == ut_dulint_cmp(
+ mtr_read_dulint(descr + XDES_ID, MLOG_8BYTES, mtr),
+ mtr_read_dulint(seg_header + FSEG_ID, MLOG_8BYTES, mtr)));
+
+ if (xdes_is_full(descr, mtr)) {
+ flst_remove(seg_header + FSEG_FULL,
+ descr + XDES_FLST_NODE, mtr);
+ } else if (xdes_is_free(descr, mtr)) {
+ flst_remove(seg_header + FSEG_FREE,
+ descr + XDES_FLST_NODE, mtr);
+ } else {
+ flst_remove(seg_header + FSEG_NOT_FULL,
+ descr + XDES_FLST_NODE, mtr);
+
+ not_full_n_used = mtr_read_ulint(
+ seg_header + FSEG_NOT_FULL_N_USED,
+ MLOG_4BYTES, mtr);
+
+ descr_n_used = xdes_get_n_used(descr, mtr);
+ ut_a(not_full_n_used >= descr_n_used);
+ mlog_write_ulint(seg_header + FSEG_NOT_FULL_N_USED,
+ not_full_n_used - descr_n_used,
+ MLOG_4BYTES, mtr);
+ }
+ fsp_free_extent(space, page, mtr);
+}
+
+/**************************************************************************
+Frees part of a segment. This function can be used to free a segment
+by repeatedly calling this function in different mini-transactions.
+Doing the freeing in a single mini-transaction might result in too big
+a mini-transaction. */
+
+bool
+fseg_free_step(
+/*===========*/
+ /* out: TRUE if freeing completed */
+ ulint space, /* in: segment space id */
+ ulint page_no,/* in: segment header page number */
+ ulint offset, /* in: segment header byte offset on page */
+ mtr_t* mtr) /* in: mtr */
+{
+ buf_block_t* block;
+ ulint n;
+ ulint page;
+ xdes_t* descr;
+ fseg_header_t* header;
+ fil_addr_t header_addr;
+
+ header_addr.page = page_no;
+ header_addr.boffset = offset;
+
+ mtr_x_lock(&fsp_latch, mtr);
+
+ header = fut_get_ptr_x_lock(space, header_addr, mtr);
+
+ descr = fseg_get_first_extent(header, mtr);
+
+ if (descr != NULL) {
+ /* Free the extent held by the segment */
+ page = xdes_get_offset(descr);
+
+ fseg_free_extent(header, space, page, mtr);
+
+ return(FALSE);
+ }
+
+ /* Free a frag page */
+
+ n = fseg_get_last_used_frag_page_slot(header, mtr);
+
+ if (n == ULINT_UNDEFINED) {
+ /* Freeing completed: free the segment header */
+ fsp_free_seg_header(space, header, mtr);
+
+ return(TRUE);
+ }
+
+ fseg_free_page_low(header, space,
+ fseg_get_nth_frag_page_no(header, n, mtr), mtr);
+
+ return(FALSE);
+}
+
+/***********************************************************************
+Frees a segment. The freeing is performed in several mini-transactions,
+so that there is no danger of bufferfixing too many buffer pages. */
+
+void
+fseg_free(
+/*======*/
+ ulint space, /* in: space id */
+ ulint page_no,/* in: page number where the segment header is
+ placed */
+ ulint offset) /* in: byte offset of the segment header on that
+ page */
+{
+ mtr_t mtr;
+ buf_block_t* block;
+ bool finished;
+
+ for (;;) {
+ mtr_start(&mtr);
+
+ block = buf_page_get(space, page_no, &mtr);
+
+ finished = fseg_free_step(space, page_no, offset, &mtr);
+
+ mtr_commit(&mtr);
+
+ if (finished) {
+ break;
+ }
+ }
+}
+
+/**************************************************************************
+Returns the first extent descriptor for a segment. We think of the extent
+lists of the segment catenated in the order FSEG_FULL -> FSEG_NOT_FULL
+-> FSEG_FREE. */
+static
+xdes_t*
+fseg_get_first_extent(
+/*==================*/
+ /* out: the first extent descriptor, or NULL if
+ none */
+ fseg_header_t* header, /* in: segment header */
+ mtr_t* mtr) /* in: mtr */
+{
+ buf_block_t* block;
+ fil_addr_t first;
+ ulint space;
+ xdes_t* descr;
+
+ ut_ad(header && mtr);
+
+ block = buf_block_align(header);
+ buf_page_x_lock(block, mtr);
+
+ space = buf_page_get_space(block);
+
+ first = fil_addr_null;
+
+ if (flst_get_len(header + FSEG_FULL, mtr) > 0) {
+ first = flst_get_first(header + FSEG_FULL, mtr);
+ } else if (flst_get_len(header + FSEG_NOT_FULL, mtr) > 0) {
+ first = flst_get_first(header + FSEG_NOT_FULL, mtr);
+ } else if (flst_get_len(header + FSEG_FREE, mtr) > 0) {
+ first = flst_get_first(header + FSEG_FREE, mtr);
+ }
+
+ if (first.page == FIL_NULL) {
+ return(NULL);
+ } else {
+ descr = xdes_lst_get_descriptor(space, first, mtr);
+ return(descr);
+ }
+}
+
+#ifdef notdefined
+
+/**************************************************************************
+Returns the last non-free extent descriptor for a segment. We think of
+the extent lists of the segment catenated in the order FSEG_FULL ->
+FSEG_NOT_FULL -> FSEG_FREE. */
+static
+xdes_t*
+fseg_get_last_non_free_extent(
+/*==========================*/
+ /* out: the last extent descriptor, or NULL if
+ none */
+ fseg_header_t* header, /* in: segment header */
+ mtr_t* mtr) /* in: mtr */
+{
+ buf_block_t* block;
+ fil_addr_t last;
+ ulint space;
+ xdes_t* descr;
+
+ ut_ad(header && mtr);
+
+ block = buf_block_align(header);
+ buf_page_x_lock(block, mtr);
+
+ space = buf_page_get_space(block);
+
+ last = fil_addr_null;
+
+ if (flst_get_len(header + FSEG_NOT_FULL, mtr) > 0) {
+ last = flst_get_last(header + FSEG_NOT_FULL, mtr);
+ } else if (flst_get_len(header + FSEG_FULL, mtr) > 0) {
+ last = flst_get_last(header + FSEG_FULL, mtr);
+ }
+
+ if (last.page == FIL_NULL) {
+ return(NULL);
+ } else {
+ descr = xdes_lst_get_descriptor(space, last, mtr);
+ return(descr);
+ }
+}
+
+/**************************************************************************
+Returns the next extent descriptor for a segment. We think of the extent
+lists of the segment catenated in the order FSEG_FULL -> FSEG_NOT_FULL
+-> FSEG_FREE. */
+static
+xdes_t*
+fseg_get_next_extent(
+/*=================*/
+ /* out: next extent descriptor, or NULL if
+ none */
+ fseg_header_t* header, /* in: segment header */
+ xdes_t* descr, /* in: previous extent descriptor */
+ mtr_t* mtr) /* in: mtr */
+{
+ fil_addr_t next_addr;
+ buf_block_t* block;
+ ulint space;
+
+ ut_ad(header && descr && mtr);
+
+ block = buf_block_align(header);
+ buf_page_x_lock(block, mtr);
+
+ space = buf_page_get_space(block);
+
+ next_addr = flst_get_next_addr(descr + XDES_FLST_NODE, mtr);
+
+ if (next_addr.page == FIL_NULL) {
+ /* This is the last extent in the list. */
+ if (xdes_is_full(descr, mtr)) {
+ /* descr is in FSEG_FULL list */
+ if (flst_get_len(header + FSEG_NOT_FULL, mtr) > 0) {
+ next_addr = flst_get_first(header
+ + FSEG_NOT_FULL, mtr);
+ } else if (flst_get_len(header + FSEG_FREE, mtr) > 0) {
+ next_addr = flst_get_first(header
+ + FSEG_FREE, mtr);
+ }
+ } else if (!xdes_is_full(descr, mtr)
+ && !xdes_is_free(descr, mtr)) {
+ /* descr is in FSEG_NOT_FULL list */
+ if (flst_get_len(header + FSEG_FREE, mtr) > 0) {
+ next_addr = flst_get_first(header
+ + FSEG_FREE, mtr);
+ }
+ }
+ }
+
+ if (next_addr.page != FIL_NULL) {
+ descr = xdes_lst_get_descriptor(space, next_addr, mtr);
+ ut_ad(descr);
+ return(descr);
+ } else {
+ return(NULL);
+ }
+}
+
+/**************************************************************************
+Returns the previous extent descriptor for a segment. We think of the extent
+lists of the segment catenated in the order FSEG_FULL -> FSEG_NOT_FULL
+-> FSEG_FREE. */
+static
+xdes_t*
+fseg_get_prev_extent(
+/*=================*/
+ /* out: previous extent descriptor, or NULL if
+ none */
+ fseg_header_t* header, /* in: segment header */
+ xdes_t* descr, /* in: extent descriptor */
+ mtr_t* mtr) /* in: mtr */
+{
+ fil_addr_t prev_addr;
+ buf_block_t* block;
+ ulint space;
+
+ ut_ad(header && descr && mtr);
+
+ block = buf_block_align(header);
+ buf_page_x_lock(block, mtr);
+
+ space = buf_page_get_space(block);
+
+ prev_addr = flst_get_prev_addr(descr + XDES_FLST_NODE, mtr);
+
+ if (prev_addr.page == FIL_NULL) {
+ /* This is the first extent in the list. */
+ if (xdes_is_free(descr, mtr)) {
+ /* descr is in FSEG_FREE list */
+ if (flst_get_len(header + FSEG_NOT_FULL, mtr) > 0) {
+ prev_addr = flst_get_last(header
+ + FSEG_NOT_FULL, mtr);
+ } else if (flst_get_len(header + FSEG_FULL, mtr) > 0) {
+ prev_addr = flst_get_last(header
+ + FSEG_FULL, mtr);
+ }
+ } else if (!xdes_is_full(descr, mtr)
+ && !xdes_is_free(descr, mtr)) {
+ /* descr is in FSEG_NOT_FULL list */
+ if (flst_get_len(header + FSEG_FULL, mtr) > 0) {
+ prev_addr = flst_get_last(header
+ + FSEG_FULL, mtr);
+ }
+ }
+ }
+
+ if (prev_addr.page != FIL_NULL) {
+ descr = xdes_lst_get_descriptor(space, prev_addr, mtr);
+ ut_ad(descr);
+ return(descr);
+ } else {
+ return(NULL);
+ }
+}
+
+/*************************************************************************
+Gets the first used page number in the given extent assigned to a
+specific segment, or its successors, in the order defined in
+fsp_get_next_extent. */
+static
+ulint
+fseg_extent_get_next_page_no(
+/*=========================*/
+ /* next used page number in the given extent
+ or a successor of it, FIL_NULL if no page
+ found */
+ fseg_header_t* header, /* in: segment header */
+ xdes_t* descr, /* in: extent descriptor, if this is NULL, the
+ function returns FIL_NULL */
+ mtr_t* mtr) /* in: mtr */
+{
+ ulint bit;
+
+ UT_NOT_USED(header);
+ ut_ad((descr == NULL) || (xdes_get_state(descr, mtr) == XDES_FSEG));
+
+ for (;;) {
+ if (descr == NULL) {
+ return(FIL_NULL);
+ }
+
+ bit = xdes_find_bit(descr, XDES_FREE_BIT, FALSE, 0, mtr);
+
+ if (bit == ULINT_UNDEFINED) {
+ /* No page found in this extent: the extent is in
+ FSEG_FREE list, thus, no used page can be found
+ in successors */
+ return(FIL_NULL);
+ } else {
+ return(xdes_get_offset(descr) + bit);
+ }
+ }
+}
+
+/*************************************************************************
+Gets the last used page number in the given extent assigned to a
+specific segment, or its predecessor extents, in the order defined in
+fsp_get_next_extent. If the page cannot be found from the extents,
+the last page of the fragment list is returned, or FIL_NULL if it is
+empty.*/
+static
+ulint
+fseg_extent_get_prev_page_no(
+/*=========================*/
+ /* previous used page number in the given
+ extent or a predecessor, FIL_NULL
+ if no page found */
+ fseg_header_t* header, /* in: segment header */
+ xdes_t* descr, /* in: extent descriptor, if this is NULL, the
+ function returns the last page of the fragment
+ list, if any */
+ mtr_t* mtr) /* in: mtr */
+{
+ ulint prev_page_no;
+ ulint bit;
+ fil_addr_t last_frag_page_addr;
+
+ ut_ad((descr == NULL) || (xdes_get_state(descr, mtr) == XDES_FSEG));
+
+ for (;;) {
+ if (descr == NULL) {
+ prev_page_no = FIL_NULL;
+ break;
+ }
+
+ bit = xdes_find_bit_downward(descr, XDES_FREE_BIT, FALSE,
+ FSP_EXTENT_SIZE - 1, mtr);
+
+ if (bit == ULINT_UNDEFINED) {
+ descr = fseg_get_prev_extent(header, descr, mtr);
+ } else {
+ prev_page_no = xdes_get_offset(descr) + bit;
+ break;
+ }
+ }
+
+ if (prev_page_no == FIL_NULL) {
+ last_frag_page_addr = flst_get_last(header + FSEG_FRAG, mtr);
+ prev_page_no = last_frag_page_addr.page;
+ }
+
+ return(prev_page_no);
+}
+
+/**************************************************************************
+Returns the page number of the first segment page. If no pages have been
+freed from the segment, and the pages were allocated with the hint page
+number always one greater than previous page, then it is guaranteed that
+this function returns the first allocated page. */
+
+ulint
+fseg_get_first_page_no(
+/*===================*/
+ /* out: page number, FIL_NULL if no
+ page found */
+ fseg_header_t* header, /* in: segment header */
+ mtr_t* mtr) /* in: mtr */
+{
+ buf_block_t* block;
+ ulint first_page_no;
+ xdes_t* descr;
+ fil_addr_t first_frag_page_addr;
+
+ ut_ad(header);
+
+ mtr_x_lock(&fsp_latch, mtr);
+
+ block = buf_block_align(header);
+ buf_page_x_lock(block, mtr);
+
+ /* Find first page */
+ first_frag_page_addr = flst_get_first(header + FSEG_FRAG, mtr);
+ first_page_no = first_frag_page_addr.page;
+
+ if (first_page_no == FIL_NULL) {
+ descr = fseg_get_first_extent(header, mtr);
+ first_page_no = fseg_extent_get_next_page_no(header, descr,
+ mtr);
+ }
+
+ return(first_page_no);
+}
+
+/**************************************************************************
+Returns the page number of the last segment page. If no pages have been
+freed from the segment, and the pages were allocated with the hint page
+number always one greater than previous page, then it is guaranteed that
+this function returns the last allocated page. */
+
+ulint
+fseg_get_last_page_no(
+/*==================*/
+ /* out: page number, FIL_NULL if no
+ page found */
+ fseg_header_t* header, /* in: segment header */
+ mtr_t* mtr) /* in: mtr */
+{
+ buf_block_t* block;
+ ulint last_page_no;
+ xdes_t* descr;
+
+ ut_ad(header);
+
+ mtr_x_lock(&fsp_latch, mtr);
+
+ block = buf_block_align(header);
+ buf_page_x_lock(block, mtr);
+
+ descr = fseg_get_last_non_free_extent(header, mtr);
+ last_page_no = fseg_extent_get_prev_page_no(header, descr, mtr);
+
+ return(last_page_no);
+}
+
+/**************************************************************************
+Returns the page number of the next segment page. If no pages have been
+freed from the segment, and the pages were allocated with the hint page
+number always one greater than previous page, then it is guaranteed that
+this function steps the pages through in the order they were allocated
+to the segment. */
+
+ulint
+fseg_get_next_page_no(
+/*==================*/
+ /* out: page number, FIL_NULL if no
+ page left */
+ fseg_header_t* header, /* in: segment header */
+ ulint page_no,/* in: previous page number */
+ mtr_t* mtr) /* in: mtr */
+{
+ buf_block_t* block;
+ buf_frame_t* frame;
+ ulint space;
+ ulint next_page_no;
+ xdes_t* descr;
+ ulint bit;
+ fil_addr_t next_frag_page_addr;
+ fseg_page_header_t* page_header;
+
+ ut_ad(header);
+
+ mtr_x_lock(&fsp_latch, mtr);
+
+ block = buf_block_align(header);
+ buf_page_x_lock(block, mtr);
+
+ space = buf_page_get_space(block);
+
+ descr = xdes_get_descriptor(space, page_no, mtr);
+ ut_ad(xdes_get_bit(descr, XDES_FREE_BIT,
+ page_no % FSP_EXTENT_SIZE, mtr) == FALSE);
+
+ if (xdes_get_state(descr, mtr) == XDES_FSEG) {
+ /* The extent of the current page belongs to the segment */
+ bit = xdes_find_bit(descr, XDES_FREE_BIT, FALSE,
+ (page_no + 1) % FSP_EXTENT_SIZE,
+ mtr);
+ if ((bit == ULINT_UNDEFINED)
+ || (bit <= (page_no % FSP_EXTENT_SIZE))) {
+ /* No higher address pages in this extent */
+ descr = fseg_get_next_extent(header, descr, mtr);
+ next_page_no = fseg_extent_get_next_page_no(
+ header, descr, mtr);
+ } else {
+ next_page_no = xdes_get_offset(descr) + bit;
+ }
+ } else {
+ /* Current page is a fragment page */
+ block = buf_page_get(space, page_no, mtr);
+ buf_page_x_lock(block, mtr);
+ frame = buf_block_get_frame(block);
+ page_header = frame + FSEG_PAGE_HEADER_OFFSET;
+ next_frag_page_addr = flst_get_next_addr(
+ page_header + FSEG_PAGE_FRAG_NODE,
+ mtr);
+
+ next_page_no = next_frag_page_addr.page;
+ if (next_page_no == FIL_NULL) {
+ descr = fseg_get_first_extent(header, mtr);
+ next_page_no = fseg_extent_get_next_page_no(
+ header, descr, mtr);
+ }
+ }
+ return(next_page_no);
+}
+
+/**************************************************************************
+Returns the page number of the previous segment page. If no pages have been
+freed from the segment, and the pages were allocated with the hint page
+number always one greater than the previous page, then it is guaranteed that
+this function steps through the pages in the order opposite to the allocation
+order of the pages. */
+
+ulint
+fseg_get_prev_page_no(
+/*==================*/
+ /* out: page number, FIL_NULL if no page
+ left */
+ fseg_header_t* header, /* in: segment header */
+ ulint page_no,/* in: page number */
+ mtr_t* mtr) /* in: mtr */
+{
+ buf_block_t* block;
+ buf_frame_t* frame;
+ ulint space;
+ ulint prev_page_no;
+ xdes_t* descr;
+ ulint bit;
+ fil_addr_t prev_frag_page_addr;
+ fseg_page_header_t* page_header;
+
+ ut_ad(header);
+
+ mtr_x_lock(&fsp_latch, mtr);
+
+ block = buf_block_align(header);
+ buf_page_x_lock(block, mtr);
+
+ space = buf_page_get_space(block);
+
+ descr = xdes_get_descriptor(space, page_no, mtr);
+ ut_ad(xdes_get_bit(descr, XDES_FREE_BIT,
+ page_no % FSP_EXTENT_SIZE, mtr) == FALSE);
+
+ if (xdes_get_state(descr, mtr) == XDES_FSEG) {
+ /* The extent of the current page belongs to the segment */
+ bit = xdes_find_bit_downward(descr, XDES_FREE_BIT, FALSE,
+ (page_no - 1) % FSP_EXTENT_SIZE,
+ mtr);
+ if ((bit == ULINT_UNDEFINED)
+ || (bit >= (page_no % FSP_EXTENT_SIZE))) {
+ /* No lower address pages in this extent */
+ descr = fseg_get_prev_extent(header, descr, mtr);
+ prev_page_no = fseg_extent_get_prev_page_no(
+ header, descr, mtr);
+ } else {
+ prev_page_no = xdes_get_offset(descr) + bit;
+ }
+ } else {
+ /* Current page is a fragment page */
+ block = buf_page_get(space, page_no, mtr);
+ buf_page_x_lock(block, mtr);
+ frame = buf_block_get_frame(block);
+ page_header = frame + FSEG_PAGE_HEADER_OFFSET;
+ prev_frag_page_addr = flst_get_prev_addr(
+ page_header + FSEG_PAGE_FRAG_NODE,
+ mtr);
+
+ prev_page_no = prev_frag_page_addr.page;
+ }
+ return(prev_page_no);
+}
+
+#endif
+
+/***********************************************************************
+Validates a segment. */
+static
+bool
+fseg_validate_low(
+/*==============*/
+ /* out: TRUE if ok */
+ fseg_header_t* header, /* in: segment header */
+ mtr_t* mtr2) /* in: mtr */
+{
+ ulint space;
+ dulint seg_id;
+ mtr_t mtr;
+ xdes_t* descr;
+ fil_addr_t node_addr;
+ ulint n_used = 0;
+ ulint n_used2 = 0;
+ flst_node_t* node;
+ buf_frame_t* frame;
+ fseg_page_header_t* page_header;
+
+ ut_ad(mtr_memo_contains(mtr2, buf_block_align(header),
+ MTR_MEMO_BUF_FIX));
+ buf_page_x_lock(buf_block_align(header), mtr2);
+
+ space = buf_page_get_space(buf_block_align(header));
+
+ seg_id = mtr_read_dulint(header + FSEG_ID, MLOG_8BYTES, mtr2);
+ n_used = mtr_read_ulint(header + FSEG_NOT_FULL_N_USED,
+ MLOG_4BYTES, mtr2);
+
+ flst_validate(header + FSEG_FRAG, mtr2);
+ flst_validate(header + FSEG_FREE, mtr2);
+ flst_validate(header + FSEG_NOT_FULL, mtr2);
+ flst_validate(header + FSEG_FULL, mtr2);
+
+ /* Validate FSEG_FREE list */
+ node_addr = flst_get_first(header + FSEG_FREE, mtr2);
+
+ while (!fil_addr_is_null(node_addr)) {
+ mtr_start(&mtr);
+ mtr_x_lock(&fsp_latch, &mtr);
+
+ descr = xdes_lst_get_descriptor(space, node_addr, &mtr);
+
+ ut_a(xdes_get_n_used(descr, &mtr) == 0);
+ ut_a(xdes_get_state(descr, &mtr) == XDES_FSEG);
+ ut_a(0 == ut_dulint_cmp(
+ mtr_read_dulint(descr + XDES_ID, MLOG_8BYTES,
+ &mtr), seg_id));
+
+ node_addr = flst_get_next_addr(descr + XDES_FLST_NODE, &mtr);
+ mtr_commit(&mtr);
+ }
+
+ /* Validate FSEG_NOT_FULL list */
+
+ node_addr = flst_get_first(header + FSEG_NOT_FULL, mtr2);
+
+ while (!fil_addr_is_null(node_addr)) {
+ mtr_start(&mtr);
+ mtr_x_lock(&fsp_latch, &mtr);
+
+ descr = xdes_lst_get_descriptor(space, node_addr, &mtr);
+
+ ut_a(xdes_get_n_used(descr, &mtr) > 0);
+ ut_a(xdes_get_n_used(descr, &mtr) < FSP_EXTENT_SIZE);
+ ut_a(xdes_get_state(descr, &mtr) == XDES_FSEG);
+ ut_a(0 == ut_dulint_cmp(
+ mtr_read_dulint(descr + XDES_ID, MLOG_8BYTES,
+ &mtr), seg_id));
+
+ n_used2 += xdes_get_n_used(descr, &mtr);
+
+ node_addr = flst_get_next_addr(descr + XDES_FLST_NODE, &mtr);
+ mtr_commit(&mtr);
+ }
+
+ /* Validate FSEG_FULL list */
+
+ node_addr = flst_get_first(header + FSEG_FULL, mtr2);
+
+ while (!fil_addr_is_null(node_addr)) {
+ mtr_start(&mtr);
+ mtr_x_lock(&fsp_latch, &mtr);
+
+ descr = xdes_lst_get_descriptor(space, node_addr, &mtr);
+
+ ut_a(xdes_get_n_used(descr, &mtr) == FSP_EXTENT_SIZE);
+ ut_a(xdes_get_state(descr, &mtr) == XDES_FSEG);
+ ut_a(0 == ut_dulint_cmp(
+ mtr_read_dulint(descr + XDES_ID, MLOG_8BYTES,
+ &mtr), seg_id));
+
+ node_addr = flst_get_next_addr(descr + XDES_FLST_NODE, &mtr);
+ mtr_commit(&mtr);
+ }
+
+ /* Validate FSEG_FRAG list */
+ node_addr = flst_get_first(header + FSEG_FRAG, mtr2);
+
+ while (!fil_addr_is_null(node_addr)) {
+ mtr_start(&mtr);
+ mtr_x_lock(&fsp_latch, &mtr);
+
+ node = fut_get_ptr_x_lock(space, node_addr, &mtr);
+ frame = buf_frame_align(node);
+ page_header = frame + FSEG_PAGE_HEADER_OFFSET;
+ ut_a(0 == ut_dulint_cmp(
+ mtr_read_dulint(page_header + FSEG_PAGE_SEG_ID,
+ MLOG_8BYTES, &mtr), seg_id));
+
+ node_addr = flst_get_next_addr(node, &mtr);
+ mtr_commit(&mtr);
+ }
+
+ ut_a(n_used == n_used2);
+
+ return(TRUE);
+}
+
+/***********************************************************************
+Validates a segment. */
+
+bool
+fseg_validate(
+/*==========*/
+ /* out: TRUE if ok */
+ fseg_header_t* header, /* in: segment header */
+ mtr_t* mtr2) /* in: mtr */
+{
+ bool ret;
+
+ mtr_x_lock(&fsp_latch, mtr2);
+ ret = fseg_validate_low(header, mtr2);
+
+ return(ret);
+}
+
+/***********************************************************************
+Writes info of a segment. */
+static
+void
+fseg_print_low(
+/*===========*/
+ fseg_header_t* header, /* in: segment header */
+ mtr_t* mtr) /* in: mtr */
+{
+ ulint space;
+ ulint seg_id_low;
+ ulint seg_id_high;
+ ulint n_used;
+ ulint n_frag;
+ ulint n_free;
+ ulint n_not_full;
+ ulint n_full;
+ ulint reserved;
+ ulint used;
+ ulint page_no;
+
+ ut_ad(mtr_memo_contains(mtr, buf_block_align(header),
+ MTR_MEMO_BUF_FIX));
+ buf_page_x_lock(buf_block_align(header), mtr);
+
+ space = buf_page_get_space(buf_block_align(header));
+ page_no = buf_page_get_offset(buf_block_align(header));
+
+ reserved = fseg_n_reserved_pages_low(header, &used, mtr);
+
+ seg_id_low = ut_dulint_get_low(mtr_read_dulint(header + FSEG_ID,
+ MLOG_8BYTES, mtr));
+ seg_id_high = ut_dulint_get_high(mtr_read_dulint(header + FSEG_ID,
+ MLOG_8BYTES, mtr));
+
+ n_used = mtr_read_ulint(header + FSEG_NOT_FULL_N_USED,
+ MLOG_4BYTES, mtr);
+
+ n_frag = flst_get_len(header + FSEG_FRAG, mtr);
+ n_free = flst_get_len(header + FSEG_FREE, mtr);
+ n_not_full = flst_get_len(header + FSEG_NOT_FULL, mtr);
+ n_full = flst_get_len(header + FSEG_FULL, mtr);
+
+ printf(
+ "SEGMENT id %lu %lu space %lu; page %lu; res %lu used %lu; full ext %lu\n",
+ seg_id_high, seg_id_low, space, page_no, reserved, used,
+ n_full);
+ printf(
+ "fragm pages %lu; free extents %lu; not full extents %lu: pages %lu\n",
+ n_frag, n_free, n_not_full, n_used);
+}
+
+/***********************************************************************
+Writes info of a segment. */
+
+void
+fseg_print(
+/*=======*/
+ fseg_header_t* header, /* in: segment header */
+ mtr_t* mtr) /* in: mtr */
+{
+ mtr_x_lock(&fsp_latch, mtr);
+
+ fseg_print_low(header, mtr);
+}
+
+/***********************************************************************
+Validates the file space system and its segments. */
+
+bool
+fsp_validate(
+/*=========*/
+ /* out: TRUE if ok */
+ ulint space) /* in: space id */
+{
+ fsp_header_t* header;
+ fseg_header_t* seg_header;
+ ulint size;
+ ulint free_limit;
+ ulint frag_n_used;
+ mtr_t mtr;
+ mtr_t mtr2;
+ xdes_t* descr;
+ fil_addr_t node_addr;
+ ulint descr_count = 0;
+ ulint n_used = 0;
+ ulint n_used2 = 0;
+ ulint n_full_frag_pages;
+
+ /* Start first a mini-transaction mtr2 to lock out all other threads
+ from the fsp system */
+ mtr_start(&mtr2);
+ mtr_x_lock(&fsp_latch, &mtr2);
+
+ mtr_start(&mtr);
+ mtr_x_lock(&fsp_latch, &mtr);
+
+ header = fsp_get_space_header(space, &mtr);
+
+ size = mtr_read_ulint(header + FSP_SIZE, MLOG_4BYTES, &mtr);
+ free_limit = mtr_read_ulint(header + FSP_FREE_LIMIT,
+ MLOG_4BYTES, &mtr);
+ frag_n_used = mtr_read_ulint(header + FSP_FRAG_N_USED,
+ MLOG_4BYTES, &mtr);
+
+ n_full_frag_pages = FSP_EXTENT_SIZE *
+ flst_get_len(header + FSP_FULL_FRAG, &mtr);
+
+ ut_a(free_limit <= size);
+
+ flst_validate(header + FSP_FREE, &mtr);
+ flst_validate(header + FSP_FREE_FRAG, &mtr);
+ flst_validate(header + FSP_FULL_FRAG, &mtr);
+ flst_validate(header + FSP_SEGS, &mtr);
+
+ mtr_commit(&mtr);
+
+ /* Validate FSP_FREE list */
+ mtr_start(&mtr);
+ mtr_x_lock(&fsp_latch, &mtr);
+
+ header = fsp_get_space_header(space, &mtr);
+ node_addr = flst_get_first(header + FSP_FREE, &mtr);
+
+ mtr_commit(&mtr);
+
+ while (!fil_addr_is_null(node_addr)) {
+ mtr_start(&mtr);
+ mtr_x_lock(&fsp_latch, &mtr);
+
+ descr_count++;
+ descr = xdes_lst_get_descriptor(space, node_addr, &mtr);
+
+ ut_a(xdes_get_n_used(descr, &mtr) == 0);
+ ut_a(xdes_get_state(descr, &mtr) == XDES_FREE);
+
+ node_addr = flst_get_next_addr(descr + XDES_FLST_NODE, &mtr);
+ mtr_commit(&mtr);
+ }
+
+ /* Validate FSP_FREE_FRAG list */
+ mtr_start(&mtr);
+ mtr_x_lock(&fsp_latch, &mtr);
+
+ header = fsp_get_space_header(space, &mtr);
+ node_addr = flst_get_first(header + FSP_FREE_FRAG, &mtr);
+
+ mtr_commit(&mtr);
+
+ while (!fil_addr_is_null(node_addr)) {
+ mtr_start(&mtr);
+ mtr_x_lock(&fsp_latch, &mtr);
+
+ descr_count++;
+ descr = xdes_lst_get_descriptor(space, node_addr, &mtr);
+
+ ut_a(xdes_get_n_used(descr, &mtr) > 0);
+ ut_a(xdes_get_n_used(descr, &mtr) < FSP_EXTENT_SIZE);
+ ut_a(xdes_get_state(descr, &mtr) == XDES_FREE_FRAG);
+
+ n_used += xdes_get_n_used(descr, &mtr);
+ node_addr = flst_get_next_addr(descr + XDES_FLST_NODE, &mtr);
+
+ mtr_commit(&mtr);
+ }
+
+ /* Validate FSP_FULL_FRAG list */
+ mtr_start(&mtr);
+ mtr_x_lock(&fsp_latch, &mtr);
+
+ header = fsp_get_space_header(space, &mtr);
+ node_addr = flst_get_first(header + FSP_FULL_FRAG, &mtr);
+
+ mtr_commit(&mtr);
+
+ while (!fil_addr_is_null(node_addr)) {
+ mtr_start(&mtr);
+ mtr_x_lock(&fsp_latch, &mtr);
+
+ descr_count++;
+ descr = xdes_lst_get_descriptor(space, node_addr, &mtr);
+
+ ut_a(xdes_get_n_used(descr, &mtr) == FSP_EXTENT_SIZE);
+ ut_a(xdes_get_state(descr, &mtr) == XDES_FULL_FRAG);
+
+ node_addr = flst_get_next_addr(descr + XDES_FLST_NODE, &mtr);
+ mtr_commit(&mtr);
+ }
+
+ /* Validate segments */
+ mtr_start(&mtr);
+ mtr_x_lock(&fsp_latch, &mtr);
+
+ header = fsp_get_space_header(space, &mtr);
+ node_addr = flst_get_first(header + FSP_SEGS, &mtr);
+
+ mtr_commit(&mtr);
+
+ while (!fil_addr_is_null(node_addr)) {
+ mtr_start(&mtr);
+ mtr_x_lock(&fsp_latch, &mtr);
+
+ seg_header = fut_get_ptr_x_lock(space, node_addr,
+ &mtr) - FSEG_FLST_NODE;
+ fseg_validate_low(seg_header, &mtr);
+
+ descr_count += flst_get_len(seg_header + FSEG_FREE, &mtr);
+ descr_count += flst_get_len(seg_header + FSEG_FULL, &mtr);
+ descr_count += flst_get_len(seg_header + FSEG_NOT_FULL, &mtr);
+
+ n_used2 += flst_get_len(seg_header + FSEG_FRAG, &mtr);
+
+ node_addr = flst_get_next_addr(seg_header + FSEG_FLST_NODE,
+ &mtr);
+ mtr_commit(&mtr);
+ }
+
+ ut_a(descr_count * FSP_EXTENT_SIZE == free_limit);
+ ut_a(n_used + n_full_frag_pages
+ == n_used2 + (free_limit + XDES_DESCRIBED_PER_PAGE - 1)
+ / XDES_DESCRIBED_PER_PAGE);
+ ut_a(frag_n_used == n_used);
+
+ mtr_commit(&mtr2);
+ return(TRUE);
+}
+
+/***********************************************************************
+Prints info of a file space. */
+
+void
+fsp_print(
+/*======*/
+ ulint space) /* in: space id */
+{
+ fsp_header_t* header;
+ fseg_header_t* seg_header;
+ ulint size;
+ ulint free_limit;
+ ulint frag_n_used;
+ mtr_t mtr;
+ mtr_t mtr2;
+ fil_addr_t node_addr;
+ ulint n_free;
+ ulint n_free_frag;
+ ulint n_full_frag;
+ ulint n_segs;
+ ulint seg_id_low;
+ ulint seg_id_high;
+
+ /* Start first a mini-transaction mtr2 to lock out all other threads
+ from the fsp system */
+ mtr_start(&mtr2);
+ mtr_x_lock(&fsp_latch, &mtr2);
+
+ mtr_start(&mtr);
+ mtr_x_lock(&fsp_latch, &mtr);
+
+ header = fsp_get_space_header(space, &mtr);
+
+ size = mtr_read_ulint(header + FSP_SIZE, MLOG_4BYTES, &mtr);
+ free_limit = mtr_read_ulint(header + FSP_FREE_LIMIT,
+ MLOG_4BYTES, &mtr);
+ frag_n_used = mtr_read_ulint(header + FSP_FRAG_N_USED,
+ MLOG_4BYTES, &mtr);
+
+ n_free = flst_get_len(header + FSP_FREE, &mtr);
+ n_free_frag = flst_get_len(header + FSP_FREE_FRAG, &mtr);
+ n_full_frag = flst_get_len(header + FSP_FULL_FRAG, &mtr);
+ n_segs = flst_get_len(header + FSP_SEGS, &mtr);
+ seg_id_low = ut_dulint_get_low(mtr_read_dulint(header + FSP_SEG_ID,
+ MLOG_8BYTES, &mtr));
+ seg_id_high = ut_dulint_get_high(mtr_read_dulint(header + FSP_SEG_ID,
+ MLOG_8BYTES, &mtr));
+
+ printf("FILE SPACE INFO: id %lu\n", space);
+
+ printf("size %lu, free limit %lu, free extents %lu\n",
+ size, free_limit, n_free);
+ printf(
+ "not full frag extents %lu: used pages %lu, full frag extents %lu\n",
+ n_free_frag, frag_n_used, n_full_frag);
+
+ printf("number of segments %lu, first seg id not used %lu %lu\n",
+ n_segs, seg_id_high, seg_id_low);
+
+ /* Print segments */
+ node_addr = flst_get_first(header + FSP_SEGS, &mtr);
+
+ mtr_commit(&mtr);
+
+ while (!fil_addr_is_null(node_addr)) {
+ mtr_start(&mtr);
+ mtr_x_lock(&fsp_latch, &mtr);
+
+ seg_header = fut_get_ptr_x_lock(space, node_addr,
+ &mtr) - FSEG_FLST_NODE;
+ fseg_print_low(seg_header, &mtr);
+
+ node_addr = flst_get_next_addr(seg_header + FSEG_FLST_NODE,
+ &mtr);
+ mtr_commit(&mtr);
+ }
+
+ mtr_commit(&mtr2);
+}
+
diff --git a/innobase/fsp/ts/del.c b/innobase/fsp/ts/del.c
new file mode 100644
index 00000000000..885797bdc76
--- /dev/null
+++ b/innobase/fsp/ts/del.c
@@ -0,0 +1,891 @@
+/************************************************************************
+The test module for the file system and buffer manager
+
+(c) 1995 Innobase Oy
+
+Created 11/16/1995 Heikki Tuuri
+*************************************************************************/
+
+#include "string.h"
+
+#include "os0thread.h"
+#include "os0file.h"
+#include "ut0ut.h"
+#include "ut0byte.h"
+#include "sync0sync.h"
+#include "mem0mem.h"
+#include "fil0fil.h"
+#include "..\buf0buf.h"
+#include "..\buf0buf.h1"
+#include "..\buf0buf.h2"
+#include "..\buf0flu.h"
+#include "..\buf0lru.h"
+#include "mtr0buf.h"
+#include "mtr0log.h"
+#include "fsp0fsp.h"
+#include "log0log.h"
+
+os_file_t files[1000];
+
+mutex_t ios_mutex;
+ulint ios;
+ulint n[5];
+
+
+/************************************************************************
+Io-handler thread function. */
+
+ulint
+handler_thread(
+/*===========*/
+ void* arg)
+{
+ ulint segment;
+ void* mess;
+ ulint i;
+ bool ret;
+
+ segment = *((ulint*)arg);
+
+ printf("Thread %lu starts\n", segment);
+
+ for (i = 0;; i++) {
+ ret = fil_aio_wait(segment, &mess);
+ ut_a(ret);
+
+ buf_page_io_complete((buf_block_t*)mess);
+
+ mutex_enter(&ios_mutex);
+ ios++;
+ mutex_exit(&ios_mutex);
+
+ }
+
+ return(0);
+}
+
+/************************************************************************
+Creates the test database files. */
+
+void
+create_db(void)
+/*===========*/
+{
+ ulint i;
+ buf_block_t* block;
+ byte* frame;
+ ulint j;
+ ulint tm, oldtm;
+ mtr_t mtr;
+
+ oldtm = ut_clock();
+
+ for (i = 0; i < 1; i++) {
+ for (j = 0; j < 4096; j++) {
+ mtr_start(&mtr);
+ if (j == 0) {
+ fsp_header_init(i, 4096, &mtr);
+
+ block = mtr_page_get(i, j, NULL, &mtr);
+ } else {
+ block = mtr_page_create(i, j, &mtr);
+ }
+
+ frame = buf_block_get_frame(block);
+
+ mtr_page_x_lock(block, &mtr);
+
+ mlog_write_ulint(frame + FIL_PAGE_PREV,
+ j - 1, MLOG_4BYTES, &mtr);
+
+ mlog_write_ulint(frame + FIL_PAGE_NEXT,
+ j + 1, MLOG_4BYTES, &mtr);
+
+ mlog_write_ulint(frame + FIL_PAGE_OFFSET,
+ j, MLOG_4BYTES, &mtr);
+
+ mtr_commit(&mtr);
+ }
+ }
+
+ tm = ut_clock();
+ printf("Wall clock time for test %lu milliseconds\n", tm - oldtm);
+
+ /* Flush the pool of dirty pages by reading low-offset pages */
+ for (i = 0; i < 1000; i++) {
+
+ mtr_start(&mtr);
+ block = mtr_page_get(0, i, NULL, &mtr);
+
+ frame = buf_block_get_frame(block);
+
+ mtr_page_s_lock(block, &mtr);
+
+ ut_a(mtr_read_ulint(frame + FIL_PAGE_OFFSET, MLOG_4BYTES,
+ &mtr) == i);
+
+ mtr_commit(&mtr);
+ }
+
+ os_thread_sleep(1000000);
+
+ ut_a(buf_all_freed());
+}
+
+/*************************************************************************
+Creates the files for the file system test and inserts them to
+the file system. */
+
+void
+create_files(void)
+/*==============*/
+{
+ bool ret;
+ ulint i, k;
+ char name[20];
+ os_thread_t thr[5];
+ os_thread_id_t id[5];
+
+ strcpy(name, "j:\\tsfile1");
+
+ for (k = 0; k < 1; k++) {
+ for (i = 0; i < 4; i++) {
+
+ name[9] = (char)((ulint)'0' + i);
+
+ files[i] = os_file_create(name, OS_FILE_CREATE,
+ OS_FILE_TABLESPACE, &ret);
+
+ if (ret == FALSE) {
+ ut_a(os_file_get_last_error() ==
+ OS_FILE_ALREADY_EXISTS);
+
+ files[i] = os_file_create(
+ name, OS_FILE_OPEN,
+ OS_FILE_TABLESPACE, &ret);
+
+ ut_a(ret);
+ }
+
+ ret = os_file_set_size(files[i], 4096 * 8192, 0);
+ ut_a(ret);
+
+ ret = os_file_close(files[i]);
+ ut_a(ret);
+
+ if (i == 0) {
+ fil_space_create("noname", k, OS_FILE_TABLESPACE);
+ }
+
+ ut_a(fil_validate());
+
+ fil_node_create(name, 4096, k);
+ }
+ }
+
+ ios = 0;
+
+ mutex_create(&ios_mutex);
+
+ for (i = 0; i < 5; i++) {
+ n[i] = i;
+
+ thr[i] = os_thread_create(handler_thread, n + i, id + i);
+ }
+}
+
+/************************************************************************
+Reads the test database files. */
+
+void
+test1(void)
+/*=======*/
+{
+ ulint i, j, k;
+ buf_block_t* block;
+ byte* frame;
+ ulint tm, oldtm;
+
+ buf_flush_batch(BUF_FLUSH_LIST, 1000);
+
+ os_thread_sleep(1000000);
+
+ buf_all_freed();
+
+ oldtm = ut_clock();
+
+ for (k = 0; k < 1; k++) {
+ for (i = 0; i < 1; i++) {
+ for (j = 0; j < 409; j++) {
+ block = buf_page_get(i, j, NULL);
+
+ frame = buf_block_get_frame(block);
+
+ buf_page_s_lock(block);
+
+ ut_a(*((ulint*)(frame + 16)) == j);
+
+ buf_page_s_unlock(block);
+
+ buf_page_release(block);
+ }
+ }
+ }
+
+ tm = ut_clock();
+ printf("Wall clock time for test %lu milliseconds\n", tm - oldtm);
+
+}
+
+/************************************************************************
+Reads the test database files. */
+
+void
+test2(void)
+/*=======*/
+{
+ ulint i, j, k, rnd;
+ buf_block_t* block;
+ byte* frame;
+ ulint tm, oldtm;
+
+ oldtm = ut_clock();
+
+ rnd = 123;
+
+ for (k = 0; k < 100; k++) {
+ rnd += 23651;
+ rnd = rnd % 4096;
+
+ i = rnd / 4096;
+ j = rnd % 2048;
+
+ block = buf_page_get(i, j, NULL);
+
+ frame = buf_block_get_frame(block);
+
+ buf_page_s_lock(block);
+
+ ut_a(*((ulint*)(frame + 16)) == j);
+
+ buf_page_s_unlock(block);
+
+ buf_page_release(block);
+ }
+
+ tm = ut_clock();
+ printf("Wall clock time for random read %lu milliseconds\n",
+ tm - oldtm);
+}
+
+/************************************************************************
+Reads the test database files. */
+
+void
+test4(void)
+/*=======*/
+{
+ ulint i, j, k, rnd;
+ buf_block_t* block;
+ byte* frame;
+ ulint tm, oldtm;
+
+ /* Flush the pool of high-offset pages */
+ for (i = 0; i < 1000; i++) {
+
+ block = buf_page_get(0, i, NULL);
+
+ frame = buf_block_get_frame(block);
+
+ buf_page_s_lock(block);
+
+ ut_a(*((ulint*)(frame + 16)) == i);
+
+ buf_page_s_unlock(block);
+
+ buf_page_release(block);
+ }
+
+ printf("Test starts\n");
+
+ oldtm = ut_clock();
+
+ rnd = 123;
+
+ for (k = 0; k < 400; k++) {
+
+ rnd += 4357;
+
+ i = 0;
+ j = 1001 + rnd % 3000;
+
+ block = buf_page_get(i, j, NULL);
+
+ frame = buf_block_get_frame(block);
+
+ buf_page_s_lock(block);
+
+ ut_a(*((ulint*)(frame + 16)) == j);
+
+ buf_page_s_unlock(block);
+
+ buf_page_release(block);
+ }
+
+ tm = ut_clock();
+ printf(
+ "Wall clock time for %lu random no read-ahead %lu milliseconds\n",
+ k, tm - oldtm);
+
+ /* Flush the pool of high-offset pages */
+ for (i = 0; i < 1000; i++) {
+
+ block = buf_page_get(0, i, NULL);
+
+ frame = buf_block_get_frame(block);
+
+ buf_page_s_lock(block);
+
+ ut_a(*((ulint*)(frame + 16)) == i);
+
+ buf_page_s_unlock(block);
+
+ buf_page_release(block);
+ }
+
+ printf("Test starts\n");
+
+ oldtm = ut_clock();
+
+ rnd = 123;
+
+ for (k = 0; k < 400; k++) {
+
+ rnd += 4357;
+
+ i = 0;
+ j = 1001 + rnd % 400;
+
+ block = buf_page_get(i, j, NULL);
+
+ frame = buf_block_get_frame(block);
+
+ buf_page_s_lock(block);
+
+ ut_a(*((ulint*)(frame + 16)) == j);
+
+ buf_page_s_unlock(block);
+
+ buf_page_release(block);
+ }
+
+ tm = ut_clock();
+ printf(
+ "Wall clock time for %lu random read-ahead %lu milliseconds\n",
+ k, tm - oldtm);
+
+}
+
+/************************************************************************
+Tests speed of CPU algorithms. */
+
+void
+test3(void)
+/*=======*/
+{
+ ulint i, j;
+ buf_block_t* block;
+ ulint tm, oldtm;
+
+ for (i = 0; i < 400; i++) {
+
+ block = buf_page_get(0, i, NULL);
+
+ buf_page_release(block);
+ }
+
+ os_thread_sleep(2000000);
+
+ oldtm = ut_clock();
+
+ for (j = 0; j < 500; j++) {
+ for (i = 0; i < 200; i++) {
+
+ block = buf_page_get(0, i, NULL);
+
+/*
+ buf_page_s_lock(block);
+
+ buf_page_s_unlock(block);
+*/
+
+ buf_page_release(block);
+
+ }
+ }
+
+ tm = ut_clock();
+ printf("Wall clock time for %lu page get-release %lu milliseconds\n",
+ i * j, tm - oldtm);
+
+ oldtm = ut_clock();
+
+ for (j = 0; j < 500; j++) {
+ for (i = 0; i < 200; i++) {
+
+ buf_page_get(0, i, NULL);
+/*
+ buf_page_s_lock(block);
+
+ buf_page_s_unlock(block);
+*/
+ buf_page_release(block);
+ }
+ }
+
+ tm = ut_clock();
+ printf("Wall clock time for %lu block get-release %lu milliseconds\n",
+ i * j, tm - oldtm);
+
+
+ oldtm = ut_clock();
+
+ for (i = 0; i < 100000; i++) {
+ block = buf_block_alloc();
+ buf_block_free(block);
+ }
+
+ tm = ut_clock();
+ printf("Wall clock time for %lu block alloc-free %lu milliseconds\n",
+ i, tm - oldtm);
+
+ ha_print_info(buf_pool->page_hash);
+}
+
+/************************************************************************
+Frees the spaces in the file system. */
+
+void
+free_system(void)
+/*=============*/
+{
+ ulint i;
+
+ for (i = 0; i < 1; i++) {
+ fil_space_free(i);
+ }
+}
+
+/************************************************************************
+Test for file space management. */
+
+void
+test5(void)
+/*=======*/
+{
+ mtr_t mtr;
+ ulint seg_page;
+ ulint new_page;
+ ulint seg_page2;
+ ulint new_page2;
+ buf_block_t* block;
+ bool finished;
+ ulint i;
+ ulint reserved;
+ ulint used;
+ ulint tm, oldtm;
+
+ os_thread_sleep(1000000);
+
+ buf_validate();
+
+ buf_print();
+
+ mtr_start(&mtr);
+
+ seg_page = fseg_create(0, 0, 1000, 555, &mtr);
+
+ mtr_commit(&mtr);
+
+ os_thread_sleep(1000000);
+ buf_validate();
+ printf("Segment created: header page %lu\n", seg_page);
+
+ mtr_start(&mtr);
+
+ block = mtr_page_get(0, seg_page, NULL, &mtr);
+
+ new_page = fseg_alloc_free_page(buf_block_get_frame(block) + 1000,
+ 2, FSP_UP, &mtr);
+
+ mtr_commit(&mtr);
+
+ buf_validate();
+ buf_print();
+ printf("Segment page allocated %lu\n", new_page);
+
+ finished = FALSE;
+
+ while (!finished) {
+
+ mtr_start(&mtr);
+
+ block = mtr_page_get(0, seg_page, NULL, &mtr);
+
+ finished = fseg_free_step(
+ buf_block_get_frame(block) + 1000, &mtr);
+
+ mtr_commit(&mtr);
+ }
+
+ /***********************************************/
+ os_thread_sleep(1000000);
+ buf_validate();
+ buf_print();
+ mtr_start(&mtr);
+
+ seg_page = fseg_create(0, 0, 1000, 557, &mtr);
+
+ mtr_commit(&mtr);
+
+ ut_a(seg_page == 1);
+
+ printf("Segment created: header page %lu\n", seg_page);
+
+ new_page = seg_page;
+ for (i = 0; i < 1023; i++) {
+
+ mtr_start(&mtr);
+
+ block = mtr_page_get(0, seg_page, NULL, &mtr);
+
+ new_page = fseg_alloc_free_page(
+ buf_block_get_frame(block) + 1000,
+ new_page + 1, FSP_UP, &mtr);
+ if (i < FSP_EXTENT_SIZE - 1) {
+ ut_a(new_page == 2 + i);
+ } else {
+ ut_a(new_page == i + FSP_EXTENT_SIZE + 1);
+ }
+
+ printf("%lu %lu; ", i, new_page);
+ if (i % 10 == 0) {
+ printf("\n");
+ }
+
+ mtr_commit(&mtr);
+ }
+
+ buf_print();
+ buf_validate();
+
+ mtr_start(&mtr);
+
+ block = mtr_page_get(0, seg_page, NULL, &mtr);
+
+ mtr_page_s_lock(block, &mtr);
+
+ reserved = fseg_n_reserved_pages(buf_block_get_frame(block) + 1000,
+ &used, &mtr);
+
+ ut_a(used == 1024);
+ ut_a(reserved >= 1024);
+
+ printf("Pages used in segment %lu reserved by segment %lu \n",
+ used, reserved);
+
+ mtr_commit(&mtr);
+
+ finished = FALSE;
+
+ while (!finished) {
+
+ mtr_start(&mtr);
+
+ block = mtr_page_get(0, seg_page, NULL, &mtr);
+
+ finished = fseg_free_step(
+ buf_block_get_frame(block) + 1000, &mtr);
+
+ mtr_commit(&mtr);
+ }
+
+ buf_print();
+ buf_validate();
+
+ /***********************************************/
+
+ mtr_start(&mtr);
+
+ seg_page = fseg_create(0, 0, 1000, 557, &mtr);
+
+ mtr_commit(&mtr);
+
+ ut_a(seg_page == 1);
+
+ mtr_start(&mtr);
+
+ seg_page2 = fseg_create(0, 0, 1000, 558, &mtr);
+
+ mtr_commit(&mtr);
+
+ ut_a(seg_page2 == 2);
+
+ new_page = seg_page;
+ new_page2 = seg_page2;
+
+ for (;;) {
+
+ mtr_start(&mtr);
+
+ block = mtr_page_get(0, seg_page, NULL, &mtr);
+
+ new_page = fseg_alloc_free_page(
+ buf_block_get_frame(block) + 1000,
+ new_page + 1, FSP_UP, &mtr);
+
+ printf("1:%lu %lu; ", i, new_page);
+ if (i % 10 == 0) {
+ printf("\n");
+ }
+
+ new_page = fseg_alloc_free_page(
+ buf_block_get_frame(block) + 1000,
+ new_page + 1, FSP_UP, &mtr);
+
+ printf("1:%lu %lu; ", i, new_page);
+ if (i % 10 == 0) {
+ printf("\n");
+ }
+
+ mtr_commit(&mtr);
+
+ mtr_start(&mtr);
+
+ block = mtr_page_get(0, seg_page2, NULL, &mtr);
+
+ new_page2 = fseg_alloc_free_page(
+ buf_block_get_frame(block) + 1000,
+ new_page2 + 1, FSP_UP, &mtr);
+
+ printf("2:%lu %lu; ", i, new_page2);
+ if (i % 10 == 0) {
+ printf("\n");
+ }
+
+ mtr_commit(&mtr);
+
+ if (new_page2 == FIL_NULL) {
+ break;
+ }
+ }
+
+ mtr_start(&mtr);
+
+ block = mtr_page_get(0, seg_page, NULL, &mtr);
+
+ mtr_page_s_lock(block, &mtr);
+
+ reserved = fseg_n_reserved_pages(buf_block_get_frame(block) + 1000,
+ &used, &mtr);
+
+ printf("Pages used in segment 1 %lu, reserved by segment %lu \n",
+ used, reserved);
+
+ mtr_commit(&mtr);
+
+ mtr_start(&mtr);
+
+ block = mtr_page_get(0, seg_page2, NULL, &mtr);
+
+ mtr_page_s_lock(block, &mtr);
+
+ reserved = fseg_n_reserved_pages(buf_block_get_frame(block) + 1000,
+ &used, &mtr);
+
+ printf("Pages used in segment 2 %lu, reserved by segment %lu \n",
+ used, reserved);
+
+ mtr_commit(&mtr);
+
+ for (;;) {
+
+ mtr_start(&mtr);
+
+ block = mtr_page_get(0, seg_page, NULL, &mtr);
+
+ fseg_free_step(
+ buf_block_get_frame(block) + 1000, &mtr);
+
+ block = mtr_page_get(0, seg_page2, NULL, &mtr);
+
+ finished = fseg_free_step(
+ buf_block_get_frame(block) + 1000, &mtr);
+
+ mtr_commit(&mtr);
+
+ if (finished) {
+ break;
+ }
+ }
+
+ mtr_start(&mtr);
+
+ seg_page2 = fseg_create(0, 0, 1000, 558, &mtr);
+
+ mtr_commit(&mtr);
+
+ i = 0;
+ for (;;) {
+ mtr_start(&mtr);
+
+ block = mtr_page_get(0, seg_page2, NULL, &mtr);
+
+ new_page2 = fseg_alloc_free_page(
+ buf_block_get_frame(block) + 1000,
+ 557, FSP_DOWN, &mtr);
+
+ printf("%lu %lu; ", i, new_page2);
+ mtr_commit(&mtr);
+
+ if (new_page2 == FIL_NULL) {
+ break;
+ }
+ i++;
+ }
+
+ for (;;) {
+
+ mtr_start(&mtr);
+
+ block = mtr_page_get(0, seg_page, NULL, &mtr);
+
+ finished = fseg_free_step(
+ buf_block_get_frame(block) + 1000, &mtr);
+
+ mtr_commit(&mtr);
+
+ if (finished) {
+ break;
+ }
+ }
+
+ for (;;) {
+
+ mtr_start(&mtr);
+
+ block = mtr_page_get(0, seg_page2, NULL, &mtr);
+
+ finished = fseg_free_step(
+ buf_block_get_frame(block) + 1000, &mtr);
+
+ mtr_commit(&mtr);
+
+ if (finished) {
+ break;
+ }
+ }
+
+
+ /***************************************/
+
+ oldtm = ut_clock();
+
+ for (i = 0; i < 1000; i++) {
+ mtr_start(&mtr);
+
+ seg_page = fseg_create(0, 0, 1000, 555, &mtr);
+
+ mtr_commit(&mtr);
+
+ mtr_start(&mtr);
+
+ block = mtr_page_get(0, seg_page, NULL, &mtr);
+
+ new_page = fseg_alloc_free_page(buf_block_get_frame(block) + 1000,
+ 2, FSP_UP, &mtr);
+
+ mtr_commit(&mtr);
+
+ finished = FALSE;
+
+ while (!finished) {
+
+ mtr_start(&mtr);
+
+ block = mtr_page_get(0, seg_page, NULL, &mtr);
+
+ finished = fseg_free_step(
+ buf_block_get_frame(block) + 1000, &mtr);
+
+ mtr_commit(&mtr);
+ }
+ }
+
+ tm = ut_clock();
+ printf("Wall clock time for %lu seg crea+free %lu millisecs\n",
+ i, tm - oldtm);
+
+ buf_validate();
+
+ buf_flush_batch(BUF_FLUSH_LIST, 500);
+
+ os_thread_sleep(1000000);
+
+ buf_all_freed();
+}
+
+
+/************************************************************************
+Main test function. */
+
+void
+main(void)
+/*======*/
+{
+ ulint tm, oldtm;
+ ulint n;
+
+ oldtm = ut_clock();
+
+ os_aio_init(160, 5);
+ sync_init();
+ mem_init();
+ fil_init(26); /* Allow 25 open files at a time */
+ buf_pool_init(1000, 1000);
+ log_init();
+
+ buf_validate();
+
+ ut_a(fil_validate());
+
+ create_files();
+
+ create_db();
+
+ buf_validate();
+
+ test5();
+/*
+ test1();
+
+ test3();
+
+ test4();
+
+ test2();
+*/
+ buf_validate();
+
+ n = buf_flush_batch(BUF_FLUSH_LIST, 500);
+
+ os_thread_sleep(1000000);
+
+ buf_all_freed();
+
+ free_system();
+
+ tm = ut_clock();
+ printf("Wall clock time for test %lu milliseconds\n", tm - oldtm);
+ printf("TESTS COMPLETED SUCCESSFULLY!\n");
+}
diff --git a/innobase/fsp/ts/makefile b/innobase/fsp/ts/makefile
new file mode 100644
index 00000000000..cd56e791b31
--- /dev/null
+++ b/innobase/fsp/ts/makefile
@@ -0,0 +1,16 @@
+
+
+
+include ..\..\makefile.i
+
+tsfsp: ..\fsp.lib tsfsp.c
+ $(CCOM) $(CFL) -I.. -I..\.. ..\..\btr.lib ..\..\trx.lib ..\..\pars.lib ..\..\que.lib ..\..\lock.lib ..\..\row.lib ..\..\read.lib ..\..\srv.lib ..\..\com.lib ..\..\usr.lib ..\..\thr.lib ..\..\fut.lib ..\fsp.lib ..\..\page.lib ..\..\dyn.lib ..\..\mtr.lib ..\..\log.lib ..\..\rem.lib ..\..\fil.lib ..\..\buf.lib ..\..\dict.lib ..\..\data.lib ..\..\mach.lib ..\..\ha.lib ..\..\ut.lib ..\..\sync.lib ..\..\mem.lib ..\..\os.lib tsfsp.c $(LFL)
+
+
+
+
+
+
+
+
+
diff --git a/innobase/fsp/ts/tsfsp.c b/innobase/fsp/ts/tsfsp.c
new file mode 100644
index 00000000000..ba6a35ebc69
--- /dev/null
+++ b/innobase/fsp/ts/tsfsp.c
@@ -0,0 +1,1234 @@
+/************************************************************************
+The test module for file space management
+
+(c) 1996 Innobase Oy
+
+Created 1/4/1996 Heikki Tuuri
+*************************************************************************/
+
+#include "string.h"
+
+#include "os0thread.h"
+#include "os0file.h"
+#include "ut0ut.h"
+#include "ut0byte.h"
+#include "sync0sync.h"
+#include "mem0mem.h"
+#include "fil0fil.h"
+#include "mach0data.h"
+#include "buf0buf.h"
+#include "buf0flu.h"
+#include "log0log.h"
+#include "fut0lst.h"
+#include "fut0fut.h"
+#include "mtr0mtr.h"
+#include "mtr0log.h"
+#include "..\fsp0fsp.h"
+
+os_file_t files[1000];
+
+mutex_t ios_mutex;
+ulint ios;
+ulint n[10];
+
+mutex_t incs_mutex;
+ulint incs;
+ulint page_nos[10000];
+
+#define N_SPACES 1
+#define N_FILES 2
+#define FILE_SIZE 1000 /* must be > 512 */
+#define POOL_SIZE 1000
+#define COUNTER_OFFSET 1500
+
+#define LOOP_SIZE 150
+#define N_THREADS 5
+
+
+ulint zero = 0;
+
+buf_block_t* bl_arr[POOL_SIZE];
+
+/************************************************************************
+Io-handler thread function. */
+
+ulint
+handler_thread(
+/*===========*/
+ void* arg)
+{
+ ulint segment;
+ void* mess;
+ ulint i;
+ bool ret;
+
+ segment = *((ulint*)arg);
+
+ printf("Io handler thread %lu starts\n", segment);
+
+ for (i = 0;; i++) {
+ ret = fil_aio_wait(segment, &mess);
+ ut_a(ret);
+
+ buf_page_io_complete((buf_block_t*)mess);
+
+ mutex_enter(&ios_mutex);
+ ios++;
+ mutex_exit(&ios_mutex);
+
+ }
+
+ return(0);
+}
+
+/*************************************************************************
+Creates the files for the file system test and inserts them to
+the file system. */
+
+void
+create_files(void)
+/*==============*/
+{
+ bool ret;
+ ulint i, k;
+ char name[20];
+ os_thread_t thr[5];
+ os_thread_id_t id[5];
+
+ printf("--------------------------------------------------------\n");
+ printf("Create or open database files\n");
+
+ strcpy(name, "tsfile00");
+
+ for (k = 0; k < N_SPACES; k++) {
+ for (i = 0; i < N_FILES; i++) {
+
+ name[6] = (char)((ulint)'0' + k);
+ name[7] = (char)((ulint)'0' + i);
+
+ files[i] = os_file_create(name, OS_FILE_CREATE,
+ OS_FILE_TABLESPACE, &ret);
+
+ if (ret == FALSE) {
+ ut_a(os_file_get_last_error() ==
+ OS_FILE_ALREADY_EXISTS);
+
+ files[i] = os_file_create(
+ name, OS_FILE_OPEN,
+ OS_FILE_TABLESPACE, &ret);
+
+ ut_a(ret);
+ }
+
+ ret = os_file_close(files[i]);
+ ut_a(ret);
+
+ if (i == 0) {
+ fil_space_create(name, k, OS_FILE_TABLESPACE);
+ }
+
+ ut_a(fil_validate());
+
+ fil_node_create(name, FILE_SIZE, k);
+ }
+ }
+
+ ios = 0;
+
+ mutex_create(&ios_mutex);
+
+ for (i = 0; i < 5; i++) {
+ n[i] = i;
+
+ thr[i] = os_thread_create(handler_thread, n + i, id + i);
+ }
+}
+
+/************************************************************************
+Creates the test database files. */
+
+void
+create_db(void)
+/*===========*/
+{
+ ulint i;
+ buf_block_t* block;
+ byte* frame;
+ ulint j;
+ ulint tm, oldtm;
+ mtr_t mtr;
+
+ printf("--------------------------------------------------------\n");
+ printf("Write database pages\n");
+
+ oldtm = ut_clock();
+
+ for (i = 0; i < N_SPACES; i++) {
+ for (j = 0; j < FILE_SIZE * N_FILES; j++) {
+ mtr_start(&mtr);
+
+ block = buf_page_create(i, j, &mtr);
+
+ frame = buf_block_get_frame(block);
+
+ buf_page_x_lock(block, &mtr);
+
+ if (j > FILE_SIZE * N_FILES
+ - 64 * 2 - 1) {
+ mlog_write_ulint(frame + FIL_PAGE_PREV, j - 5,
+ MLOG_4BYTES, &mtr);
+ mlog_write_ulint(frame + FIL_PAGE_NEXT, j - 7,
+ MLOG_4BYTES, &mtr);
+ } else {
+ mlog_write_ulint(frame + FIL_PAGE_PREV, j - 1,
+ MLOG_4BYTES, &mtr);
+ mlog_write_ulint(frame + FIL_PAGE_NEXT, j + 1,
+ MLOG_4BYTES, &mtr);
+ }
+
+ mlog_write_ulint(frame + FIL_PAGE_OFFSET, j,
+ MLOG_4BYTES, &mtr);
+ mlog_write_ulint(frame + FIL_PAGE_SPACE, i,
+ MLOG_4BYTES, &mtr);
+ mlog_write_ulint(frame + COUNTER_OFFSET, 0,
+ MLOG_4BYTES, &mtr);
+
+ mtr_commit(&mtr);
+ }
+ }
+
+ tm = ut_clock();
+ printf("Wall clock time for test %lu milliseconds\n", tm - oldtm);
+}
+
+/************************************************************************
+Reads the test database files. */
+
+void
+test1(void)
+/*=======*/
+{
+ ulint i, j, k;
+ buf_block_t* block;
+ byte* frame;
+ ulint tm, oldtm;
+ mtr_t mtr;
+
+ printf("--------------------------------------------------------\n");
+ printf("TEST 1. Read linearly database files\n");
+
+ oldtm = ut_clock();
+
+ for (k = 0; k < 1; k++) {
+ for (i = 0; i < N_SPACES; i++) {
+ for (j = 0; j < N_FILES * FILE_SIZE; j++) {
+ mtr_start(&mtr);
+
+ block = buf_page_get(i, j, &mtr);
+
+ frame = buf_block_get_frame(block);
+
+ buf_page_s_lock(block, &mtr);
+
+ ut_a(mtr_read_ulint(frame + FIL_PAGE_OFFSET,
+ MLOG_4BYTES, &mtr)
+ == j);
+ ut_a(mtr_read_ulint(frame + FIL_PAGE_SPACE,
+ MLOG_4BYTES, &mtr)
+ == i);
+
+ mtr_commit(&mtr);
+ }
+ }
+ }
+
+ tm = ut_clock();
+ printf("Wall clock time for %lu pages %lu milliseconds\n",
+ k * i * j, tm - oldtm);
+ buf_validate();
+}
+
+/************************************************************************
+Test for file-based lists. */
+
+void
+test2(void)
+/*=======*/
+{
+ mtr_t mtr;
+ buf_frame_t* frame;
+ buf_block_t* block;
+ ulint i;
+ flst_base_node_t* base1;
+ fil_addr_t base_addr1;
+ flst_base_node_t* base2;
+ fil_addr_t base_addr2;
+ flst_node_t* node;
+ fil_addr_t node_addr;
+ flst_node_t* node2;
+ fil_addr_t node_addr2;
+ flst_node_t* node3;
+ fil_addr_t node_addr3;
+
+#define BPAGE 10
+#define BASE1 300
+#define BASE2 500
+#define NODE1 800
+#define NODE2 900
+#define NODE3 1000
+#define NODE4 1100
+#define INDEX 30
+
+ buf_validate();
+
+ mtr_start(&mtr);
+
+ block = buf_page_get(0, BPAGE, &mtr);
+ frame = buf_block_get_frame(block);
+
+ flst_init(frame + BASE1, &mtr);
+ flst_init(frame + BASE2, &mtr);
+
+ mtr_commit(&mtr);
+
+ printf("-------------------------------------------\n");
+ printf("TEST 2. Test of file-based two-way lists \n");
+
+ base_addr1.page = BPAGE;
+ base_addr1.boffset = BASE1;
+
+ base_addr2.page = BPAGE;
+ base_addr2.boffset = BASE2;
+
+ printf(
+ "Add 1000 elements in list1 in reversed order and in list2 in order\n");
+ for (i = 0; i < 1000; i++) {
+ mtr_start(&mtr);
+
+ base1 = fut_get_ptr(0, base_addr1, &mtr);
+ base2 = fut_get_ptr(0, base_addr2, &mtr);
+
+ block = buf_page_get(0, i, &mtr);
+ frame = buf_block_get_frame(block);
+
+ buf_page_x_lock(block, &mtr);
+
+ flst_add_first(base1, frame + NODE1, &mtr);
+ mlog_write_ulint(frame + NODE1 + INDEX, i,
+ MLOG_4BYTES, &mtr);
+ flst_add_last(base2, frame + NODE2, &mtr);
+ mlog_write_ulint(frame + NODE2 + INDEX, i,
+ MLOG_4BYTES, &mtr);
+
+ mtr_commit(&mtr);
+ }
+
+ mtr_start(&mtr);
+
+ base1 = fut_get_ptr(0, base_addr1, &mtr);
+ base2 = fut_get_ptr(0, base_addr2, &mtr);
+
+ flst_validate(base1, &mtr);
+ flst_validate(base2, &mtr);
+
+ flst_print(base1, &mtr);
+ flst_print(base2, &mtr);
+
+ mtr_commit(&mtr);
+
+ mtr_start(&mtr);
+
+ base1 = fut_get_ptr_s_lock(0, base_addr1, &mtr);
+
+ node_addr = flst_get_first(base1, &mtr);
+
+ mtr_commit(&mtr);
+
+ printf("Check order of elements in list1\n");
+ for (i = 0; i < 1000; i++) {
+ mtr_start(&mtr);
+ ut_a(!fil_addr_is_null(node_addr));
+
+ node = fut_get_ptr_x_lock(0, node_addr, &mtr);
+
+ ut_a(mtr_read_ulint(node + INDEX, MLOG_4BYTES, &mtr) ==
+ 999 - i);
+
+ node_addr = flst_get_next_addr(node, &mtr);
+ mtr_commit(&mtr);
+ }
+
+ ut_a(fil_addr_is_null(node_addr));
+
+ mtr_start(&mtr);
+
+ base2 = fut_get_ptr_s_lock(0, base_addr2, &mtr);
+
+ node_addr = flst_get_first(base2, &mtr);
+
+ mtr_commit(&mtr);
+
+ printf("Check order of elements in list2\n");
+ for (i = 0; i < 1000; i++) {
+ mtr_start(&mtr);
+ ut_a(!fil_addr_is_null(node_addr));
+
+ node = fut_get_ptr_x_lock(0, node_addr, &mtr);
+
+ ut_a(mtr_read_ulint(node + INDEX, MLOG_4BYTES, &mtr)
+ == i);
+
+ node_addr = flst_get_next_addr(node, &mtr);
+ mtr_commit(&mtr);
+ }
+
+ ut_a(fil_addr_is_null(node_addr));
+
+ mtr_start(&mtr);
+
+ base1 = fut_get_ptr(0, base_addr1, &mtr);
+ base2 = fut_get_ptr(0, base_addr2, &mtr);
+
+ flst_validate(base1, &mtr);
+ flst_validate(base2, &mtr);
+
+ mtr_commit(&mtr);
+
+ mtr_start(&mtr);
+
+ base1 = fut_get_ptr_s_lock(0, base_addr1, &mtr);
+
+ node_addr = flst_get_first(base1, &mtr);
+
+ mtr_commit(&mtr);
+
+ for (i = 0; i < 500; i++) {
+ mtr_start(&mtr);
+ ut_a(!fil_addr_is_null(node_addr));
+
+ node = fut_get_ptr_x_lock(0, node_addr, &mtr);
+
+ node_addr = flst_get_next_addr(node, &mtr);
+ mtr_commit(&mtr);
+ }
+
+ printf("Add 200 elements to the middle of list1\n");
+ for (i = 0; i < 100; i++) {
+ mtr_start(&mtr);
+
+ node = fut_get_ptr_x_lock(0, node_addr, &mtr);
+
+ node_addr2.page = i;
+ node_addr2.boffset = NODE3;
+ node2 = fut_get_ptr_x_lock(0, node_addr2, &mtr);
+
+ node_addr3.page = i;
+ node_addr3.boffset = NODE4;
+ node3 = fut_get_ptr_x_lock(0, node_addr3, &mtr);
+
+ mlog_write_ulint(node2 + INDEX, 99 - i, MLOG_4BYTES, &mtr);
+
+ block = buf_page_get(0, BPAGE, &mtr);
+ frame = buf_block_get_frame(block);
+
+ base1 = frame + BASE1;
+
+ flst_insert_after(base1, node, node2, &mtr);
+ flst_insert_before(base1, node3, node, &mtr);
+
+ if (i % 17 == 0) {
+ flst_validate(base1, &mtr);
+ }
+ mtr_commit(&mtr);
+ }
+
+ printf("Check that 100 of the inserted nodes are in order\n");
+ mtr_start(&mtr);
+ ut_a(!fil_addr_is_null(node_addr));
+
+ node = fut_get_ptr_x_lock(0, node_addr, &mtr);
+
+ node_addr = flst_get_next_addr(node, &mtr);
+ mtr_commit(&mtr);
+
+ for (i = 0; i < 100; i++) {
+ mtr_start(&mtr);
+ ut_a(!fil_addr_is_null(node_addr));
+
+ node = fut_get_ptr_x_lock(0, node_addr, &mtr);
+
+ ut_a(mtr_read_ulint(node + INDEX, MLOG_4BYTES, &mtr)
+ == i);
+
+ node_addr = flst_get_next_addr(node, &mtr);
+ mtr_commit(&mtr);
+ }
+
+ printf("Remove 899 elements from the middle of list1\n");
+ mtr_start(&mtr);
+
+ base1 = fut_get_ptr_x_lock(0, base_addr1, &mtr);
+
+ node_addr = flst_get_first(base1, &mtr);
+
+ flst_print(base1, &mtr);
+ mtr_commit(&mtr);
+
+ for (i = 0; i < 300; i++) {
+ mtr_start(&mtr);
+ ut_a(!fil_addr_is_null(node_addr));
+
+ node = fut_get_ptr_x_lock(0, node_addr, &mtr);
+
+ node_addr = flst_get_next_addr(node, &mtr);
+ mtr_commit(&mtr);
+ }
+
+ for (i = 0; i < 899; i++) {
+
+ mtr_start(&mtr);
+
+ base1 = fut_get_ptr_x_lock(0, base_addr1, &mtr);
+
+ node_addr = flst_get_first(base1, &mtr);
+
+ node = fut_get_ptr_x_lock(0, node_addr, &mtr);
+
+ node_addr = flst_get_next_addr(node, &mtr);
+
+ ut_a(!fil_addr_is_null(node_addr));
+
+ node2 = fut_get_ptr_x_lock(0, node_addr, &mtr);
+
+ flst_remove(base1, node2, &mtr);
+
+ if (i % 17 == 0) {
+ flst_validate(base1, &mtr);
+ }
+
+ mtr_commit(&mtr);
+ }
+
+ printf("Remove 301 elements from the start of list1\n");
+ for (i = 0; i < 301; i++) {
+
+ mtr_start(&mtr);
+
+ base1 = fut_get_ptr_x_lock(0, base_addr1, &mtr);
+
+ node_addr = flst_get_first(base1, &mtr);
+
+ node = fut_get_ptr_x_lock(0, node_addr, &mtr);
+
+ flst_remove(base1, node, &mtr);
+
+ if (i % 17 == 0) {
+ flst_validate(base1, &mtr);
+ }
+
+ mtr_commit(&mtr);
+ }
+
+ mtr_start(&mtr);
+
+ base1 = fut_get_ptr_x_lock(0, base_addr1, &mtr);
+
+ ut_a(flst_get_len(base1, &mtr) == 0);
+ flst_print(base1, &mtr);
+
+ mtr_commit(&mtr);
+}
+
+/************************************************************************
+Inits space header of space 0. */
+
+void
+init_space(void)
+/*============*/
+{
+ mtr_t mtr;
+
+ printf("Init space header\n");
+
+ mtr_start(&mtr);
+
+ fsp_header_init(0, FILE_SIZE * N_FILES, &mtr);
+
+ mtr_commit(&mtr);
+}
+
+/************************************************************************
+Test for file space management. */
+
+void
+test5(void)
+/*=======*/
+{
+ mtr_t mtr;
+ ulint seg_page;
+ ulint new_page;
+ ulint seg_page2;
+ ulint new_page2;
+ ulint seg_page3;
+ buf_block_t* block;
+ bool finished;
+ ulint i;
+ ulint reserved;
+ ulint used;
+ ulint tm, oldtm;
+
+ buf_validate();
+
+ fsp_validate(0);
+ fsp_print(0);
+
+ mtr_start(&mtr);
+
+ seg_page = fseg_create(0, 0, 1000, &mtr);
+
+ mtr_commit(&mtr);
+
+ fsp_validate(0);
+
+ buf_validate();
+ printf("Segment created: header page %lu, byte offset %lu\n",
+ seg_page, 1000);
+ fsp_print(0);
+
+ mtr_start(&mtr);
+
+ block = buf_page_get(0, seg_page, &mtr);
+
+ new_page = fseg_alloc_free_page(buf_block_get_frame(block) + 1000,
+ 2, FSP_UP, &mtr);
+
+ mtr_commit(&mtr);
+
+ fsp_print(0);
+ fsp_validate(0);
+ buf_validate();
+ printf("Segment page allocated %lu\n", new_page);
+
+
+ mtr_start(&mtr);
+
+ block = buf_page_get(0, seg_page, &mtr);
+
+ fseg_free_page(buf_block_get_frame(block) + 1000,
+ 0, new_page, &mtr);
+
+ mtr_commit(&mtr);
+
+ fsp_validate(0);
+ printf("Segment page freed %lu\n", new_page);
+
+ finished = FALSE;
+
+ while (!finished) {
+
+ mtr_start(&mtr);
+
+ block = buf_page_get(0, seg_page, &mtr);
+
+ finished = fseg_free_step(
+ buf_block_get_frame(block) + 1000, &mtr);
+
+ mtr_commit(&mtr);
+ }
+ fsp_validate(0);
+
+ /***********************************************/
+ buf_validate();
+ mtr_start(&mtr);
+
+ seg_page = fseg_create(0, 0, 1000, &mtr);
+
+ mtr_commit(&mtr);
+
+ ut_a(seg_page == 2);
+
+ printf("Segment created: header page %lu\n", seg_page);
+
+ new_page = seg_page;
+ for (i = 0; i < 511; i++) {
+
+ mtr_start(&mtr);
+
+ block = buf_page_get(0, seg_page, &mtr);
+
+ new_page = fseg_alloc_free_page(
+ buf_block_get_frame(block) + 1000,
+ new_page + 1, FSP_UP, &mtr);
+ printf("%lu %lu; ", i, new_page);
+ if (i % 10 == 0) {
+ printf("\n");
+ }
+
+ mtr_commit(&mtr);
+
+ if (i % 117 == 0) {
+ fsp_validate(0);
+ }
+ }
+
+ fsp_validate(0);
+ buf_validate();
+
+ mtr_start(&mtr);
+
+ block = buf_page_get(0, seg_page, &mtr);
+
+ reserved = fseg_n_reserved_pages(buf_block_get_frame(block) + 1000,
+ &used, &mtr);
+
+ ut_a(used == 512);
+ ut_a(reserved >= 512);
+
+ printf("Pages used in segment %lu reserved by segment %lu \n",
+ used, reserved);
+
+ mtr_commit(&mtr);
+
+ finished = FALSE;
+
+ while (!finished) {
+ i++;
+
+ mtr_start(&mtr);
+
+ block = buf_page_get(0, seg_page, &mtr);
+
+ finished = fseg_free_step(
+ buf_block_get_frame(block) + 1000, &mtr);
+
+ mtr_commit(&mtr);
+
+ if (i % 117 == 0) {
+ fsp_validate(0);
+ }
+ }
+
+ fsp_validate(0);
+ buf_validate();
+
+ /***********************************************/
+
+ mtr_start(&mtr);
+
+ seg_page = fseg_create(0, 0, 1000, &mtr);
+
+ mtr_commit(&mtr);
+
+ ut_a(seg_page == 2);
+
+ mtr_start(&mtr);
+
+ seg_page2 = fseg_create(0, 0, 1000, &mtr);
+
+ mtr_commit(&mtr);
+
+ ut_a(seg_page2 == 3);
+
+ new_page = seg_page;
+ new_page2 = seg_page2;
+
+ for (;;) {
+
+ mtr_start(&mtr);
+
+ block = buf_page_get(0, seg_page, &mtr);
+
+ new_page = fseg_alloc_free_page(
+ buf_block_get_frame(block) + 1000,
+ new_page + 1, FSP_UP, &mtr);
+
+ printf("1:%lu %lu; ", i, new_page);
+ if (i % 10 == 0) {
+ printf("\n");
+ }
+
+ new_page = fseg_alloc_free_page(
+ buf_block_get_frame(block) + 1000,
+ new_page + 1, FSP_UP, &mtr);
+
+ printf("1:%lu %lu; ", i, new_page);
+ if (i % 10 == 0) {
+ printf("\n");
+ }
+
+ mtr_commit(&mtr);
+
+ i++;
+ if (i % 217 == 0) {
+ fsp_validate(0);
+ }
+
+ mtr_start(&mtr);
+
+ block = buf_page_get(0, seg_page2, &mtr);
+
+ new_page2 = fseg_alloc_free_page(
+ buf_block_get_frame(block) + 1000,
+ new_page2 + 1, FSP_DOWN, &mtr);
+
+ printf("2:%lu %lu; ", i, new_page2);
+ if (i % 10 == 0) {
+ printf("\n");
+ }
+
+ mtr_commit(&mtr);
+
+ if (new_page2 == FIL_NULL) {
+ break;
+ }
+ }
+
+ mtr_start(&mtr);
+
+ block = buf_page_get(0, seg_page, &mtr);
+
+ reserved = fseg_n_reserved_pages(buf_block_get_frame(block) + 1000,
+ &used, &mtr);
+
+ printf("Pages used in segment 1 %lu, reserved by segment %lu \n",
+ used, reserved);
+
+ mtr_commit(&mtr);
+ fsp_validate(0);
+
+ mtr_start(&mtr);
+
+ block = buf_page_get(0, seg_page2, &mtr);
+
+ reserved = fseg_n_reserved_pages(buf_block_get_frame(block) + 1000,
+ &used, &mtr);
+
+ printf("Pages used in segment 2 %lu, reserved by segment %lu \n",
+ used, reserved);
+
+ mtr_commit(&mtr);
+
+ fsp_print(0);
+
+ for (;;) {
+
+ i++;
+ mtr_start(&mtr);
+
+ block = buf_page_get(0, seg_page, &mtr);
+
+ fseg_free_step(
+ buf_block_get_frame(block) + 1000, &mtr);
+
+ block = buf_page_get(0, seg_page2, &mtr);
+
+ finished = fseg_free_step(
+ buf_block_get_frame(block) + 1000, &mtr);
+
+ mtr_commit(&mtr);
+
+ if (finished) {
+ break;
+ }
+
+ if (i % 117 == 0) {
+ fsp_validate(0);
+ }
+ }
+
+ fsp_validate(0);
+
+ mtr_start(&mtr);
+
+ seg_page3 = fseg_create(0, 0, 1000, &mtr);
+ page_nos[0] = seg_page3;
+ new_page2 = seg_page3;
+
+ mtr_commit(&mtr);
+
+ for (i = 1; i < 250; i++) {
+ mtr_start(&mtr);
+
+ block = buf_page_get(0, seg_page3, &mtr);
+
+ new_page2 = fseg_alloc_free_page(
+ buf_block_get_frame(block) + 1000,
+ new_page2 + 1, FSP_UP, &mtr);
+ page_nos[i] = new_page2;
+
+ mtr_commit(&mtr);
+ }
+
+ /*************************************************/
+
+ mtr_start(&mtr);
+
+ fseg_create(0, seg_page3, 1500, &mtr);
+
+ mtr_commit(&mtr);
+
+ for (i = 0; i < 250; i++) {
+ mtr_start(&mtr);
+
+ block = buf_page_get(0, seg_page3, &mtr);
+
+ new_page2 = fseg_alloc_free_page(
+ buf_block_get_frame(block) + 1500,
+ new_page2 + 1, FSP_UP, &mtr);
+ page_nos[i] = new_page2;
+
+ mtr_commit(&mtr);
+ }
+
+ printf("---------------------------------------------------------\n");
+ printf("TEST 5A13. Test free_step.\n");
+
+ fseg_free(0, seg_page3, 1500);
+
+ printf("---------------------------------------------------------\n");
+ printf("TEST 5A3. Test free_step.\n");
+
+ for (;;) {
+
+ mtr_start(&mtr);
+
+ block = buf_page_get(0, seg_page3, &mtr);
+
+ finished = fseg_free_step(
+ buf_block_get_frame(block) + 1000, &mtr);
+
+ mtr_commit(&mtr);
+
+ if (finished) {
+ break;
+ }
+ }
+
+ /***************************************************/
+
+ mtr_start(&mtr);
+
+ seg_page2 = fseg_create(0, 0, 1000, &mtr);
+ page_nos[0] = seg_page2;
+ new_page2 = seg_page2;
+
+ mtr_commit(&mtr);
+
+ i = 1;
+ for (;;) {
+ mtr_start(&mtr);
+
+ block = buf_page_get(0, seg_page2, &mtr);
+
+ new_page2 = fseg_alloc_free_page(
+ buf_block_get_frame(block) + 1000,
+ new_page2 + 1, FSP_UP, &mtr);
+ page_nos[i] = new_page2;
+/*
+ printf("%lu %lu; ", i, new_page2);
+*/
+ mtr_commit(&mtr);
+
+ if (new_page2 == FIL_NULL) {
+ break;
+ }
+ i++;
+ }
+
+ printf("---------------------------------------------------------\n");
+ printf("TEST 5D. Test free_step.\n");
+
+ for (;;) {
+
+ mtr_start(&mtr);
+
+ block = buf_page_get(0, seg_page, &mtr);
+
+ finished = fseg_free_step(
+ buf_block_get_frame(block) + 1000, &mtr);
+
+ mtr_commit(&mtr);
+
+ if (finished) {
+ break;
+ }
+ }
+
+ for (;;) {
+
+ mtr_start(&mtr);
+
+ block = buf_page_get(0, seg_page2, &mtr);
+
+ finished = fseg_free_step(
+ buf_block_get_frame(block) + 1000, &mtr);
+
+ mtr_commit(&mtr);
+
+ if (finished) {
+ break;
+ }
+ }
+
+
+ /***************************************/
+
+ oldtm = ut_clock();
+
+ fsp_validate(0);
+
+ for (i = 0; i < 10; i++) {
+ mtr_start(&mtr);
+
+ seg_page = fseg_create(0, 0, 1000, &mtr);
+
+ mtr_commit(&mtr);
+
+ mtr_start(&mtr);
+
+ block = buf_page_get(0, seg_page, &mtr);
+
+ new_page = fseg_alloc_free_page(buf_block_get_frame(block) + 1000,
+ 3, FSP_UP, &mtr);
+
+ mtr_commit(&mtr);
+
+ finished = FALSE;
+
+ while (!finished) {
+
+ mtr_start(&mtr);
+
+ block = buf_page_get(0, seg_page, &mtr);
+
+ finished = fseg_free_step(
+ buf_block_get_frame(block) + 1000, &mtr);
+
+ mtr_commit(&mtr);
+ }
+ }
+
+ tm = ut_clock();
+ printf("Wall clock time for %lu seg crea+free %lu millisecs\n",
+ i, tm - oldtm);
+
+ buf_validate();
+ fsp_validate(0);
+ fsp_print(0);
+
+ buf_flush_batch(BUF_FLUSH_LIST, 2000);
+ os_thread_sleep(3000000);
+
+/* buf_print(); */
+ buf_all_freed();
+}
+
+/************************************************************************
+Random test thread function. */
+
+ulint
+random_thread(
+/*===========*/
+ void* arg)
+{
+ ulint n;
+ ulint i, j, t, p, sp, d, b;
+ ulint s;
+ ulint arr[FILE_SIZE * N_FILES];
+ ulint seg_page;
+ fseg_header_t* seg_header;
+ fil_addr_t seg_addr;
+ byte dir;
+ ulint k;
+ mtr_t mtr;
+ bool finished;
+ ulint used;
+ ulint reserved;
+
+ n = *((ulint*)arg);
+ n = os_thread_get_curr_id();
+
+ printf("Random test thread %lu starts\n", n);
+
+ for (i = 0; i < 30; i++) {
+ t = ut_rnd_gen_ulint() % 10;
+ s = ut_rnd_gen_ulint() % FILE_SIZE * N_FILES;
+ p = 0;
+ sp = ut_rnd_gen_ulint() % N_SPACES;
+ d = ut_rnd_gen_ulint() % 3;
+ b = ut_rnd_gen_ulint() % 3;
+
+ if (i % 10 == 0) {
+ printf("Thr %lu round %lu starts\n", n, i);
+ }
+ ut_a(buf_validate());
+
+ if (t != 0) {
+ do {
+ mtr_start(&mtr);
+ seg_page = fseg_create(sp, p, 1000, &mtr);
+ mtr_commit(&mtr);
+ } while (seg_page == FIL_NULL);
+
+ seg_addr.page = seg_page;
+ seg_addr.boffset = 1000;
+
+ k = 0;
+ j = 0;
+ while (j < s) {
+ j++;
+ if (d == 0) {
+ dir = FSP_DOWN;
+ } else if (d == 1) {
+ dir = FSP_NO_DIR;
+ } else {
+ dir = FSP_UP;
+ }
+ mtr_start(&mtr);
+ seg_header = fut_get_ptr(sp, seg_addr, &mtr);
+
+ if (b != 0) {
+ arr[k] = fseg_alloc_free_page(seg_header,
+ p, dir, &mtr);
+ k++;
+ } else if (k > 0) {
+ fseg_free_page(seg_header, sp, arr[k - 1],
+ &mtr);
+ k--;
+ }
+
+ mtr_commit(&mtr);
+ if ((k > 0) && (arr[k - 1] == FIL_NULL)) {
+ k--;
+ break;
+ }
+ if (p > 0) {
+ p = arr[k - 1] + dir - 1;
+ }
+ if (j % 577 == 0) {
+ if (k > 0) {
+ p = arr[k / 2] + 1;
+ } else {
+ p = 0;
+ }
+ d = ut_rnd_gen_ulint() % 3;
+ b = ut_rnd_gen_ulint() % 3;
+ }
+ }
+ finished = FALSE;
+ mtr_start(&mtr);
+
+ seg_header = fut_get_ptr(sp, seg_addr, &mtr);
+
+ reserved = fseg_n_reserved_pages(seg_header,
+ &used, &mtr);
+
+ printf("Pages used in segment %lu reserved by segment %lu \n",
+ used, reserved);
+
+ mtr_commit(&mtr);
+
+ printf("Thread %lu starts releasing seg %lu size %lu\n", n,
+ seg_addr.page, k);
+ while (!finished) {
+ mtr_start(&mtr);
+ seg_header = fut_get_ptr(sp, seg_addr, &mtr);
+
+ finished = fseg_free_step(seg_header, &mtr);
+ mtr_commit(&mtr);
+ }
+ } else {
+ fsp_print(sp);
+ printf("Thread %lu validates fsp\n", n);
+ fsp_validate(sp);
+ buf_validate();
+ }
+ } /* for i */
+ printf("\nRandom test thread %lu exits\n", os_thread_get_curr_id());
+ return(0);
+}
+
+/*************************************************************************
+Performs random operations on the buffer with several threads. */
+
+void
+test6(void)
+/*=======*/
+{
+ ulint i;
+ os_thread_t thr[N_THREADS + 1];
+ os_thread_id_t id[N_THREADS + 1];
+ ulint n[N_THREADS + 1];
+
+ printf("--------------------------------------------------------\n");
+ printf("TEST 6. Random multi-thread test on the buffer \n");
+
+ incs = 0;
+ mutex_create(&incs_mutex);
+
+ for (i = 0; i < N_THREADS; i++) {
+ n[i] = i;
+
+ thr[i] = os_thread_create(random_thread, n + i, id + i);
+ }
+
+ for (i = 0; i < N_THREADS; i++) {
+ os_thread_wait(thr[i]);
+ }
+}
+
+/************************************************************************
+Main test function. */
+
+void
+main(void)
+/*======*/
+{
+ ulint tm, oldtm;
+
+ oldtm = ut_clock();
+
+ os_aio_init(160, 5);
+ sync_init();
+ mem_init();
+ fil_init(26); /* Allow 25 open files at a time */
+ buf_pool_init(POOL_SIZE, POOL_SIZE);
+ log_init();
+ fsp_init();
+
+ buf_validate();
+
+ ut_a(fil_validate());
+
+ create_files();
+
+ create_db();
+
+ buf_validate();
+
+/* test1(); */
+/* buf_validate(); */
+/*
+ test2();
+ buf_validate();
+*/
+ init_space();
+
+ test5();
+ buf_validate();
+
+/* test6(); */
+
+ buf_flush_batch(BUF_FLUSH_LIST, POOL_SIZE + 1);
+/* buf_print(); */
+ buf_validate();
+
+ os_thread_sleep(1000000);
+
+/* buf_print(); */
+ buf_all_freed();
+
+ tm = ut_clock();
+ printf("Wall clock time for test %lu milliseconds\n", tm - oldtm);
+ printf("TESTS COMPLETED SUCCESSFULLY!\n");
+}