fsp0fsp.c 117 KB
Newer Older
osku's avatar
osku committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/**********************************************************************
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"
22
#include "page0zip.h"
osku's avatar
osku committed
23 24 25 26 27 28 29 30 31 32 33 34 35
#include "ibuf0ibuf.h"
#include "btr0btr.h"
#include "btr0sea.h"
#include "dict0boot.h"
#include "dict0mem.h"
#include "log0log.h"


#define FSP_HEADER_OFFSET	FIL_PAGE_DATA	/* Offset of the space header
						within a file page */

/* The data structures in files are defined just as byte strings in C */
typedef	byte	fsp_header_t;
36
typedef	byte	xdes_t;
osku's avatar
osku committed
37

38
/*			SPACE HEADER
osku's avatar
osku committed
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
			============

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_SPACE_ID		0	/* space id */
#define FSP_NOT_USED		4	/* 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; note that in a
					single-table tablespace where size
					< 64 pages, this number is 64, i.e.,
					we have initialized the space
					about the first extent, but have not
					physically allocted those pages to the
					file */
63
#define	FSP_SPACE_FLAGS		16	/* table->flags & ~DICT_TF_COMPACT */
osku's avatar
osku committed
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
#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 */
91

osku's avatar
osku committed
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
/*			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 */
/*-------------------------------------*/
134 135 136
#define FSEG_INODE_SIZE					\
	(16 + 3 * FLST_BASE_NODE_SIZE			\
	 + FSEG_FRAG_ARR_N_SLOTS * FSEG_FRAG_SLOT_SIZE)
osku's avatar
osku committed
137

138 139
#define FSP_SEG_INODES_PER_PAGE(zip_size)		\
	(((zip_size ? zip_size : UNIV_PAGE_SIZE)	\
140
	  - FSEG_ARR_OFFSET - 10) / FSEG_INODE_SIZE)
osku's avatar
osku committed
141 142 143 144
				/* Number of segment inodes which fit on a
				single page */

#define FSEG_MAGIC_N_VALUE	97937874
145

osku's avatar
osku committed
146 147 148 149 150 151 152 153 154
#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. */
155

osku's avatar
osku committed
156 157 158 159 160 161 162 163 164 165 166 167 168
#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
169

osku's avatar
osku committed
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188

/*			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 */
/*-------------------------------------*/
189

osku's avatar
osku committed
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
#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 */

205
/* File extent data structure size in bytes. */
206
#define	XDES_SIZE							\
207
	(XDES_BITMAP + UT_BITS_IN_BYTES(FSP_EXTENT_SIZE * XDES_BITS_PER_PAGE))
osku's avatar
osku committed
208 209 210

/* Offset of the descriptor array on a descriptor page */
#define	XDES_ARR_OFFSET		(FSP_HEADER_OFFSET + FSP_HEADER_SIZE)
211

osku's avatar
osku committed
212 213 214 215 216 217 218
/**************************************************************************
Returns an extent to the free list of a space. */
static
void
fsp_free_extent(
/*============*/
	ulint		space,	/* in: space id */
219 220
	ulint		zip_size,/* in: compressed page size in bytes
				or 0 for uncompressed pages */
osku's avatar
osku committed
221 222 223 224 225 226 227 228 229 230
	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 */
231 232
	ulint		zip_size,/* in: compressed page size in bytes
				or 0 for uncompressed pages */
osku's avatar
osku committed
233 234 235 236 237 238 239 240 241 242
	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 */
243
	fseg_inode_t*	header,	/* in: segment inode */
osku's avatar
osku committed
244 245 246 247 248 249 250 251 252 253 254
	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 */
255 256
	ulint		zip_size,/* in: compressed page size in bytes
				or 0 for uncompressed pages */
osku's avatar
osku committed
257 258 259 260 261 262 263 264 265 266 267 268 269
	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 */
270 271 272
	ulint		space,	/* in: space id */
	ulint		zip_size,/* in: compressed page size in bytes
				or 0 for uncompressed pages */
osku's avatar
osku committed
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
	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(
/*===============*/
	ibool		init_space,	/* in: TRUE if this is a single-table
					tablespace and we are only initing
					the tablespace's first extent
					descriptor page and ibuf bitmap page;
					then we do not allocate more extents */
	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 */
302 303
	ulint		zip_size,/* in: compressed page size in bytes
				or 0 for uncompressed pages */
304
	fseg_inode_t*	seg_inode, /* in: segment inode */
osku's avatar
osku committed
305 306 307 308 309 310 311 312 313 314 315
	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 */


/**************************************************************************
Reads the file space size stored in the header page. */
316
UNIV_INTERN
osku's avatar
osku committed
317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333
ulint
fsp_get_size_low(
/*=============*/
			/* out: tablespace size stored in the space header */
	page_t*	page)	/* in: header page (page 0 in the tablespace) */
{
	return(mach_read_from_4(page + FSP_HEADER_OFFSET + FSP_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 */
334 335
	ulint	zip_size,/* in: compressed page size in bytes
			or 0 for uncompressed pages */
osku's avatar
osku committed
336 337
	mtr_t*	mtr)	/* in: mtr */
{
338
	buf_block_t*	block;
osku's avatar
osku committed
339 340
	fsp_header_t*	header;

341 342 343 344 345 346
	ut_ad(ut_is_2pow(zip_size));
	ut_ad(zip_size <= UNIV_PAGE_SIZE);
	ut_ad(!zip_size || zip_size >= PAGE_ZIP_MIN_SIZE);
	ut_ad(id || !zip_size);

	block = buf_page_get(id, zip_size, 0, RW_X_LATCH, mtr);
347 348
	header = FSP_HEADER_OFFSET + buf_block_get_frame(block);
	buf_block_dbg_add_level(block, SYNC_FSP_PAGE);
marko's avatar
marko committed
349

350 351 352
	ut_ad(id == mach_read_from_4(FSP_SPACE_ID + header));
	ut_ad(zip_size == dict_table_flags_to_zip_size(
		      mach_read_from_4(FSP_SPACE_FLAGS + header)));
osku's avatar
osku committed
353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372
	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;

373
	ut_ad(mtr_memo_contains_page(mtr, descr, MTR_MEMO_PAGE_X_FIX));
osku's avatar
osku committed
374 375
	ut_ad((bit == XDES_FREE_BIT) || (bit == XDES_CLEAN_BIT));
	ut_ad(offset < FSP_EXTENT_SIZE);
376

osku's avatar
osku committed
377 378 379 380
	index = bit + XDES_BITS_PER_PAGE * offset;

	byte_index = index / 8;
	bit_index = index % 8;
381

382 383 384
	return(ut_bit_get_nth(mtr_read_ulint(descr + XDES_BITMAP + byte_index,
					     MLOG_1BYTE, mtr),
			      bit_index));
385
}
osku's avatar
osku committed
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403

/**************************************************************************
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;
404

405
	ut_ad(mtr_memo_contains_page(mtr, descr, MTR_MEMO_PAGE_X_FIX));
osku's avatar
osku committed
406 407 408 409 410 411 412 413 414
	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,
415
				    MLOG_1BYTE, mtr);
osku's avatar
osku committed
416 417 418
	descr_byte = ut_bit_set_nth(descr_byte, bit_index, val);

	mlog_write_ulint(descr + XDES_BITMAP + byte_index, descr_byte,
419
			 MLOG_1BYTE, mtr);
420
}
osku's avatar
osku committed
421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438

/**************************************************************************
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;
439

osku's avatar
osku committed
440 441 442
	ut_ad(descr && mtr);
	ut_ad(val <= TRUE);
	ut_ad(hint < FSP_EXTENT_SIZE);
443
	ut_ad(mtr_memo_contains_page(mtr, descr, MTR_MEMO_PAGE_X_FIX));
osku's avatar
osku committed
444 445 446 447 448 449 450 451 452 453 454 455 456
	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);
		}
	}
457 458 459

	return(ULINT_UNDEFINED);
}
osku's avatar
osku committed
460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476

/**************************************************************************
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;
477

osku's avatar
osku committed
478 479 480
	ut_ad(descr && mtr);
	ut_ad(val <= TRUE);
	ut_ad(hint < FSP_EXTENT_SIZE);
481
	ut_ad(mtr_memo_contains_page(mtr, descr, MTR_MEMO_PAGE_X_FIX));
osku's avatar
osku committed
482 483 484 485 486 487 488 489 490 491 492 493 494
	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);
		}
	}
495 496 497

	return(ULINT_UNDEFINED);
}
osku's avatar
osku committed
498 499 500 501 502 503 504 505 506 507 508 509 510

/**************************************************************************
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;
511

osku's avatar
osku committed
512
	ut_ad(descr && mtr);
513
	ut_ad(mtr_memo_contains_page(mtr, descr, MTR_MEMO_PAGE_X_FIX));
osku's avatar
osku committed
514 515 516 517 518 519
	for (i = 0; i < FSP_EXTENT_SIZE; i++) {
		if (FALSE == xdes_get_bit(descr, XDES_FREE_BIT, i, mtr)) {
			count++;
		}
	}

520 521
	return(count);
}
osku's avatar
osku committed
522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571

/**************************************************************************
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);
572
	ut_ad(mtr_memo_contains_page(mtr, descr, MTR_MEMO_PAGE_X_FIX));
osku's avatar
osku committed
573

574
	mlog_write_ulint(descr + XDES_STATE, state, MLOG_4BYTES, mtr);
osku's avatar
osku committed
575 576 577 578 579 580 581 582 583 584 585 586
}

/**************************************************************************
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 */
{
marko's avatar
marko committed
587 588
	ulint	state;

osku's avatar
osku committed
589
	ut_ad(descr && mtr);
590
	ut_ad(mtr_memo_contains_page(mtr, descr, MTR_MEMO_PAGE_X_FIX));
osku's avatar
osku committed
591

marko's avatar
marko committed
592 593 594
	state = mtr_read_ulint(descr + XDES_STATE, MLOG_4BYTES, mtr);
	ut_ad(state - 1 < XDES_FSEG);
	return(state);
osku's avatar
osku committed
595 596 597 598 599 600 601 602 603 604 605 606 607 608
}

/**************************************************************************
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);
609
	ut_ad(mtr_memo_contains_page(mtr, descr, MTR_MEMO_PAGE_X_FIX));
osku's avatar
osku committed
610 611 612 613 614 615 616
	ut_ad((XDES_SIZE - XDES_BITMAP) % 4 == 0);

	for (i = XDES_BITMAP; i < XDES_SIZE; i += 4) {
		mlog_write_ulint(descr + i, 0xFFFFFFFFUL, MLOG_4BYTES, mtr);
	}

	xdes_set_state(descr, XDES_FREE, mtr);
617
}
osku's avatar
osku committed
618 619 620 621 622 623 624 625

/************************************************************************
Calculates the page where the descriptor of a page resides. */
UNIV_INLINE
ulint
xdes_calc_descriptor_page(
/*======================*/
				/* out: descriptor page offset */
626 627
	ulint	zip_size,	/* in: compressed page size in bytes;
				0 for uncompressed pages */
osku's avatar
osku committed
628 629
	ulint	offset)		/* in: page offset */
{
630
#if UNIV_PAGE_SIZE <= XDES_ARR_OFFSET \
631
		+ (UNIV_PAGE_SIZE / FSP_EXTENT_SIZE) * XDES_SIZE
632 633
# error
#endif
634 635 636
#if PAGE_ZIP_MIN_SIZE <= XDES_ARR_OFFSET \
		+ (PAGE_ZIP_MIN_SIZE / FSP_EXTENT_SIZE) * XDES_SIZE
# error
637
#endif
638 639
	ut_ad(ut_is_2pow(zip_size));

640 641 642
	if (!zip_size) {
		return(ut_2pow_round(offset, UNIV_PAGE_SIZE));
	} else {
643
		ut_ad(zip_size > XDES_ARR_OFFSET
644
		      + (zip_size / FSP_EXTENT_SIZE) * XDES_SIZE);
645 646
		return(ut_2pow_round(offset, zip_size));
	}
osku's avatar
osku committed
647 648 649 650 651 652 653 654 655
}

/************************************************************************
Calculates the descriptor index within a descriptor page. */
UNIV_INLINE
ulint
xdes_calc_descriptor_index(
/*=======================*/
				/* out: descriptor index */
656 657
	ulint	zip_size,	/* in: compressed page size in bytes;
				0 for uncompressed pages */
osku's avatar
osku committed
658 659
	ulint	offset)		/* in: page offset */
{
660 661
	ut_ad(ut_is_2pow(zip_size));

662 663
	if (!zip_size) {
		return(ut_2pow_remainder(offset, UNIV_PAGE_SIZE)
664
		       / FSP_EXTENT_SIZE);
665 666 667
	} else {
		return(ut_2pow_remainder(offset, zip_size) / FSP_EXTENT_SIZE);
	}
osku's avatar
osku committed
668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684
}

/************************************************************************
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 */
685
	ulint		offset,	/* in: page offset;
osku's avatar
osku committed
686 687 688 689 690 691 692
				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;
693
	ulint	zip_size;
osku's avatar
osku committed
694 695 696 697
	ulint	descr_page_no;
	page_t*	descr_page;

	ut_ad(mtr);
698
	ut_ad(mtr_memo_contains(mtr, fil_space_get_latch(space, NULL),
699
				MTR_MEMO_X_LOCK));
700 701
	ut_ad(mtr_memo_contains_page(mtr, sp_header, MTR_MEMO_PAGE_S_FIX)
	      || mtr_memo_contains_page(mtr, sp_header, MTR_MEMO_PAGE_X_FIX));
702
	ut_ad(page_offset(sp_header) == FSP_HEADER_OFFSET);
osku's avatar
osku committed
703
	/* Read free limit and space size */
704 705
	limit = mach_read_from_4(sp_header + FSP_FREE_LIMIT);
	size  = mach_read_from_4(sp_header + FSP_SIZE);
706 707
	zip_size = dict_table_flags_to_zip_size(
		mach_read_from_4(sp_header + FSP_SPACE_FLAGS));
osku's avatar
osku committed
708 709 710 711 712 713 714 715 716 717 718 719 720 721

	/* 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(FALSE, space, sp_header, mtr);
	}

722
	descr_page_no = xdes_calc_descriptor_page(zip_size, offset);
osku's avatar
osku committed
723 724 725 726

	if (descr_page_no == 0) {
		/* It is on the space header page */

727
		descr_page = page_align(sp_header);
osku's avatar
osku committed
728
	} else {
729 730
		buf_block_t*	block;

731 732
		block = buf_page_get(space, zip_size, descr_page_no,
				     RW_X_LATCH, mtr);
733
		buf_block_dbg_add_level(block, SYNC_FSP_PAGE);
marko's avatar
marko committed
734

735
		descr_page = buf_block_get_frame(block);
736
	}
osku's avatar
osku committed
737 738

	return(descr_page + XDES_ARR_OFFSET
739
	       + XDES_SIZE * xdes_calc_descriptor_index(zip_size, offset));
osku's avatar
osku committed
740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756
}

/************************************************************************
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 */
757 758
	ulint	zip_size,/* in: compressed page size in bytes
			or 0 for uncompressed pages */
osku's avatar
osku committed
759 760 761 762
	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 */
{
763
	buf_block_t*	block;
osku's avatar
osku committed
764 765
	fsp_header_t*	sp_header;

766
	block = buf_page_get(space, zip_size, 0, RW_X_LATCH, mtr);
767
	buf_block_dbg_add_level(block, SYNC_FSP_PAGE);
marko's avatar
marko committed
768

769
	sp_header = FSP_HEADER_OFFSET + buf_block_get_frame(block);
osku's avatar
osku committed
770
	return(xdes_get_descriptor_with_space_hdr(sp_header, space, offset,
771
						  mtr));
osku's avatar
osku committed
772 773 774 775 776 777 778 779 780 781 782 783
}

/************************************************************************
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 */
784 785
	ulint		zip_size,/* in: compressed page size in bytes
				or 0 for uncompressed pages */
osku's avatar
osku committed
786 787 788 789 790 791 792
	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);
793
	ut_ad(mtr_memo_contains(mtr, fil_space_get_latch(space, NULL),
794
				MTR_MEMO_X_LOCK));
795 796
	descr = fut_get_ptr(space, zip_size, lst_node, RW_X_LATCH, mtr)
		- XDES_FLST_NODE;
osku's avatar
osku committed
797 798 799 800 801 802 803 804 805 806 807 808 809 810 811

	return(descr);
}

/************************************************************************
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);

812
	return(page_get_page_no(page_align(descr))
813
	       + ((page_offset(descr) - XDES_ARR_OFFSET) / XDES_SIZE)
814
	       * FSP_EXTENT_SIZE);
osku's avatar
osku committed
815 816 817 818 819 820 821
}

/***************************************************************
Inits a file page whose prior contents should be ignored. */
static
void
fsp_init_file_page_low(
822
/*===================*/
823
	buf_block_t*	block)	/* in: pointer to a page */
osku's avatar
osku committed
824
{
825
	page_t*		page	= buf_block_get_frame(block);
826
	page_zip_des_t*	page_zip= buf_block_get_page_zip(block);
827 828 829 830 831

	block->check_index_page_at_flush = FALSE;

	if (UNIV_LIKELY_NULL(page_zip)) {
		memset(page, 0, UNIV_PAGE_SIZE);
832
		memset(page_zip->data, 0, page_zip_get_size(page_zip));
833 834
		mach_write_to_4(page + FIL_PAGE_OFFSET,
				buf_block_get_page_no(block));
835 836
		mach_write_to_4(page
				+ FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID,
837 838 839 840 841
				buf_block_get_space(block));
		memcpy(page_zip->data + FIL_PAGE_OFFSET,
		       page + FIL_PAGE_OFFSET, 4);
		memcpy(page_zip->data + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID,
		       page + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID, 4);
842 843
		return;
	}
osku's avatar
osku committed
844 845 846 847

#ifdef UNIV_BASIC_LOG_DEBUG
	memset(page, 0xff, UNIV_PAGE_SIZE);
#endif
848
	mach_write_to_4(page + FIL_PAGE_OFFSET, buf_block_get_page_no(block));
849
	memset(page + FIL_PAGE_LSN, 0, 8);
850 851
	mach_write_to_4(page + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID,
			buf_block_get_space(block));
852
	memset(page + UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM, 0, 8);
osku's avatar
osku committed
853 854 855 856 857 858 859 860
}

/***************************************************************
Inits a file page whose prior contents should be ignored. */
static
void
fsp_init_file_page(
/*===============*/
861 862
	buf_block_t*	block,	/* in: pointer to a page */
	mtr_t*		mtr)	/* in: mtr */
osku's avatar
osku committed
863
{
864
	fsp_init_file_page_low(block);
865

866 867
	mlog_write_initial_log_record(buf_block_get_frame(block),
				      MLOG_INIT_FILE_PAGE, mtr);
osku's avatar
osku committed
868
}
869

osku's avatar
osku committed
870 871
/***************************************************************
Parses a redo log record of a file page init. */
872
UNIV_INTERN
osku's avatar
osku committed
873 874 875
byte*
fsp_parse_init_file_page(
/*=====================*/
876 877 878 879
				/* out: end of log record or NULL */
	byte*		ptr,	/* in: buffer */
	byte*		end_ptr __attribute__((unused)), /* in: buffer end */
	buf_block_t*	block)	/* in: block or NULL */
osku's avatar
osku committed
880 881 882
{
	ut_ad(ptr && end_ptr);

883 884
	if (block) {
		fsp_init_file_page_low(block);
osku's avatar
osku committed
885
	}
886

osku's avatar
osku committed
887 888 889 890 891
	return(ptr);
}

/**************************************************************************
Initializes the fsp system. */
892
UNIV_INTERN
osku's avatar
osku committed
893 894 895 896 897 898 899 900
void
fsp_init(void)
/*==========*/
{
	/* Does nothing at the moment */
}

/**************************************************************************
901 902 903
Writes the space id and compressed page size to a tablespace header.
This function is used past the buffer pool when we in fil0fil.c create
a new single-table tablespace. */
904
UNIV_INTERN
osku's avatar
osku committed
905
void
906 907 908 909
fsp_header_init_fields(
/*===================*/
	page_t*	page,		/* in/out: first page in the space */
	ulint	space_id,	/* in: space id */
910 911
	ulint	flags)		/* in: tablespace flags (FSP_SPACE_FLAGS):
				0, or table->flags if newer than COMPACT */
osku's avatar
osku committed
912
{
913 914 915 916 917 918
	/* The tablespace flags (FSP_SPACE_FLAGS) should be 0 for
	ROW_FORMAT=COMPACT (table->flags == DICT_TF_COMPACT) and
	ROW_FORMAT=REDUNDANT (table->flags == 0).  For any other
	format, the tablespace flags should equal table->flags. */
	ut_a(flags != DICT_TF_COMPACT);

919 920
	mach_write_to_4(FSP_HEADER_OFFSET + FSP_SPACE_ID + page,
			space_id);
921 922
	mach_write_to_4(FSP_HEADER_OFFSET + FSP_SPACE_FLAGS + page,
			flags);
osku's avatar
osku committed
923 924 925 926 927
}

/**************************************************************************
Initializes the space header of a new created space and creates also the
insert buffer tree root if space == 0. */
928
UNIV_INTERN
osku's avatar
osku committed
929 930 931
void
fsp_header_init(
/*============*/
932 933 934
	ulint	space,		/* in: space id */
	ulint	size,		/* in: current size in blocks */
	mtr_t*	mtr)		/* in: mini-transaction handle */
osku's avatar
osku committed
935 936
{
	fsp_header_t*	header;
937
	buf_block_t*	block;
osku's avatar
osku committed
938
	page_t*		page;
939
	ulint		flags;
940
	ulint		zip_size;
941

osku's avatar
osku committed
942 943
	ut_ad(mtr);

944
	mtr_x_lock(fil_space_get_latch(space, &flags), mtr);
osku's avatar
osku committed
945

946
	zip_size = dict_table_flags_to_zip_size(flags);
947
	block = buf_page_create(space, 0, zip_size, mtr);
948
	buf_page_get(space, zip_size, 0, RW_X_LATCH, mtr);
949
	buf_block_dbg_add_level(block, SYNC_FSP_PAGE);
osku's avatar
osku committed
950 951 952

	/* The prior contents of the file page should be ignored */

953 954
	fsp_init_file_page(block, mtr);
	page = buf_block_get_frame(block);
osku's avatar
osku committed
955

956
	mlog_write_ulint(page + FIL_PAGE_TYPE, FIL_PAGE_TYPE_FSP_HDR,
957
			 MLOG_2BYTES, mtr);
958

osku's avatar
osku committed
959 960 961
	header = FSP_HEADER_OFFSET + page;

	mlog_write_ulint(header + FSP_SPACE_ID, space, MLOG_4BYTES, mtr);
962 963 964 965
	mlog_write_ulint(header + FSP_NOT_USED, 0, MLOG_4BYTES, mtr);

	mlog_write_ulint(header + FSP_SIZE, size, MLOG_4BYTES, mtr);
	mlog_write_ulint(header + FSP_FREE_LIMIT, 0, MLOG_4BYTES, mtr);
966
	mlog_write_ulint(header + FSP_SPACE_FLAGS, flags,
967
			 MLOG_4BYTES, mtr);
968
	mlog_write_ulint(header + FSP_FRAG_N_USED, 0, MLOG_4BYTES, mtr);
osku's avatar
osku committed
969 970 971 972 973 974 975

	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);

976
	mlog_write_dulint(header + FSP_SEG_ID, ut_dulint_create(0, 1), mtr);
osku's avatar
osku committed
977 978
	if (space == 0) {
		fsp_fill_free_list(FALSE, space, header, mtr);
979 980
		btr_create(DICT_CLUSTERED | DICT_UNIVERSAL | DICT_IBUF,
			   0, 0, ut_dulint_add(DICT_IBUF_ID_MIN, space),
981
			   srv_sys->dummy_ind1, mtr);
osku's avatar
osku committed
982 983 984 985 986 987 988
	} else {
		fsp_fill_free_list(TRUE, space, header, mtr);
	}
}

/**************************************************************************
Reads the space id from the first page of a tablespace. */
989
UNIV_INTERN
osku's avatar
osku committed
990 991 992
ulint
fsp_header_get_space_id(
/*====================*/
993 994
				/* out: space id, ULINT UNDEFINED if error */
	const page_t*	page)	/* in: first page of a tablespace */
osku's avatar
osku committed
995 996 997 998 999 1000 1001 1002 1003
{
	ulint	fsp_id;
	ulint	id;

	fsp_id = mach_read_from_4(FSP_HEADER_OFFSET + page + FSP_SPACE_ID);

	id = mach_read_from_4(page + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID);

	if (id != fsp_id) {
1004
		fprintf(stderr,
1005 1006
			"InnoDB: Error: space id in fsp header %lu,"
			" but in the page header %lu\n",
1007 1008
			(ulong) fsp_id, (ulong) id);

osku's avatar
osku committed
1009 1010 1011 1012 1013 1014
		return(ULINT_UNDEFINED);
	}

	return(id);
}

1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028
/**************************************************************************
Reads the space flags from the first page of a tablespace. */
UNIV_INTERN
ulint
fsp_header_get_flags(
/*=================*/
				/* out: flags */
	const page_t*	page)	/* in: first page of a tablespace */
{
	ut_ad(!page_offset(page));

	return(mach_read_from_4(FSP_HEADER_OFFSET + FSP_SPACE_FLAGS + page));
}

1029 1030
/**************************************************************************
Reads the compressed page size from the first page of a tablespace. */
1031
UNIV_INTERN
1032 1033
ulint
fsp_header_get_zip_size(
1034
/*====================*/
1035 1036 1037
				/* out: compressed page size in bytes,
				or 0 if uncompressed */
	const page_t*	page)	/* in: first page of a tablespace */
1038
{
1039 1040 1041
	ulint	flags = fsp_header_get_flags(page);

	return(dict_table_flags_to_zip_size(flags));
1042 1043
}

osku's avatar
osku committed
1044 1045
/**************************************************************************
Increases the space size field of a space. */
1046
UNIV_INTERN
osku's avatar
osku committed
1047 1048 1049 1050 1051
void
fsp_header_inc_size(
/*================*/
	ulint	space,	/* in: space id */
	ulint	size_inc,/* in: size increment in pages */
1052
	mtr_t*	mtr)	/* in: mini-transaction handle */
osku's avatar
osku committed
1053 1054 1055
{
	fsp_header_t*	header;
	ulint		size;
1056
	ulint		flags;
1057

osku's avatar
osku committed
1058 1059
	ut_ad(mtr);

1060
	mtr_x_lock(fil_space_get_latch(space, &flags), mtr);
osku's avatar
osku committed
1061

1062 1063 1064
	header = fsp_get_space_header(space,
				      dict_table_flags_to_zip_size(flags),
				      mtr);
osku's avatar
osku committed
1065 1066 1067 1068

	size = mtr_read_ulint(header + FSP_SIZE, MLOG_4BYTES, mtr);

	mlog_write_ulint(header + FSP_SIZE, size + size_inc, MLOG_4BYTES,
1069
			 mtr);
osku's avatar
osku committed
1070 1071 1072
}

/**************************************************************************
1073 1074 1075 1076
Gets the current free limit of the system tablespace.  The free limit
means the place of the first page which has never been put to the the
free list for allocation.  The space above that address is initialized
to zero.  Sets also the global variable log_fsp_current_free_limit. */
1077
UNIV_INTERN
osku's avatar
osku committed
1078
ulint
1079 1080
fsp_header_get_free_limit(void)
/*===========================*/
osku's avatar
osku committed
1081 1082 1083 1084 1085 1086 1087 1088
			/* out: free limit in megabytes */
{
	fsp_header_t*	header;
	ulint		limit;
	mtr_t		mtr;

	mtr_start(&mtr);

1089
	mtr_x_lock(fil_space_get_latch(0, NULL), &mtr);
osku's avatar
osku committed
1090

1091
	header = fsp_get_space_header(0, 0, &mtr);
osku's avatar
osku committed
1092 1093 1094

	limit = mtr_read_ulint(header + FSP_FREE_LIMIT, MLOG_4BYTES, &mtr);

1095
	limit /= ((1024 * 1024) / UNIV_PAGE_SIZE);
1096

osku's avatar
osku committed
1097 1098 1099 1100 1101 1102 1103 1104
	log_fsp_current_free_limit_set_and_checkpoint(limit);

	mtr_commit(&mtr);

	return(limit);
}

/**************************************************************************
1105 1106 1107 1108
Gets the size of the system tablespace from the tablespace header.  If
we do not have an auto-extending data file, this should be equal to
the size of the data files.  If there is an auto-extending data file,
this can be smaller. */
1109
UNIV_INTERN
osku's avatar
osku committed
1110
ulint
1111 1112
fsp_header_get_tablespace_size(void)
/*================================*/
osku's avatar
osku committed
1113 1114 1115 1116 1117 1118 1119 1120
			/* out: size in pages */
{
	fsp_header_t*	header;
	ulint		size;
	mtr_t		mtr;

	mtr_start(&mtr);

1121
	mtr_x_lock(fil_space_get_latch(0, NULL), &mtr);
osku's avatar
osku committed
1122

1123
	header = fsp_get_space_header(0, 0, &mtr);
osku's avatar
osku committed
1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151

	size = mtr_read_ulint(header + FSP_SIZE, MLOG_4BYTES, &mtr);

	mtr_commit(&mtr);

	return(size);
}

/***************************************************************************
Tries to extend a single-table tablespace so that a page would fit in the
data file. */
static
ibool
fsp_try_extend_data_file_with_pages(
/*================================*/
					/* out: TRUE if success */
	ulint		space,		/* in: space */
	ulint		page_no,	/* in: page number */
	fsp_header_t*	header,		/* in: space header */
	mtr_t*		mtr)		/* in: mtr */
{
	ibool	success;
	ulint	actual_size;
	ulint	size;

	ut_a(space != 0);

	size = mtr_read_ulint(header + FSP_SIZE, MLOG_4BYTES, mtr);
1152

osku's avatar
osku committed
1153 1154 1155
	ut_a(page_no >= size);

	success = fil_extend_space_to_desired_size(&actual_size, space,
1156
						   page_no + 1);
osku's avatar
osku committed
1157 1158
	/* actual_size now has the space size in pages; it may be less than
	we wanted if we ran out of disk space */
1159

osku's avatar
osku committed
1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181
	mlog_write_ulint(header + FSP_SIZE, actual_size, MLOG_4BYTES, mtr);

	return(success);
}

/***************************************************************************
Tries to extend the last data file of a tablespace if it is auto-extending. */
static
ibool
fsp_try_extend_data_file(
/*=====================*/
					/* out: FALSE if not auto-extending */
	ulint*		actual_increase,/* out: actual increase in pages, where
					we measure the tablespace size from
					what the header field says; it may be
					the actual file size rounded down to
					megabyte */
	ulint		space,		/* in: space */
	fsp_header_t*	header,		/* in: space header */
	mtr_t*		mtr)		/* in: mtr */
{
	ulint	size;
1182
	ulint	zip_size;
osku's avatar
osku committed
1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196
	ulint	new_size;
	ulint	old_size;
	ulint	size_increase;
	ulint	actual_size;
	ibool	success;

	*actual_increase = 0;

	if (space == 0 && !srv_auto_extend_last_data_file) {

		return(FALSE);
	}

	size = mtr_read_ulint(header + FSP_SIZE, MLOG_4BYTES, mtr);
1197 1198
	zip_size = dict_table_flags_to_zip_size(
		mach_read_from_4(header + FSP_SPACE_FLAGS));
osku's avatar
osku committed
1199 1200 1201

	old_size = size;

1202 1203 1204 1205 1206
	if (space == 0) {
		if (!srv_last_file_size_max) {
			size_increase = SRV_AUTO_EXTEND_INCREMENT;
		} else {
			if (srv_last_file_size_max
1207
			    < srv_data_file_sizes[srv_n_data_files - 1]) {
osku's avatar
osku committed
1208

1209
				fprintf(stderr,
1210 1211 1212 1213
					"InnoDB: Error: Last data file size"
					" is %lu, max size allowed %lu\n",
					(ulong) srv_data_file_sizes[
						srv_n_data_files - 1],
1214 1215
					(ulong) srv_last_file_size_max);
			}
osku's avatar
osku committed
1216

1217
			size_increase = srv_last_file_size_max
1218
				- srv_data_file_sizes[srv_n_data_files - 1];
1219 1220 1221
			if (size_increase > SRV_AUTO_EXTEND_INCREMENT) {
				size_increase = SRV_AUTO_EXTEND_INCREMENT;
			}
osku's avatar
osku committed
1222 1223
		}
	} else {
1224 1225 1226 1227 1228 1229 1230 1231
		/* We extend single-table tablespaces first one extent
		at a time, but for bigger tablespaces more. It is not
		enough to extend always by one extent, because some
		extents are frag page extents. */
		ulint	extent_size;	/* one megabyte, in pages */

		if (!zip_size) {
			extent_size = FSP_EXTENT_SIZE;
osku's avatar
osku committed
1232
		} else {
1233
			extent_size = FSP_EXTENT_SIZE
1234
				* UNIV_PAGE_SIZE / zip_size;
1235 1236 1237 1238
		}

		if (size < extent_size) {
			/* Let us first extend the file to extent_size */
1239 1240
			success = fsp_try_extend_data_file_with_pages(
				space, extent_size - 1, header, mtr);
1241
			if (!success) {
1242 1243
				new_size = mtr_read_ulint(header + FSP_SIZE,
							  MLOG_4BYTES, mtr);
osku's avatar
osku committed
1244

1245
				*actual_increase = new_size - old_size;
osku's avatar
osku committed
1246

1247
				return(FALSE);
osku's avatar
osku committed
1248 1249
			}

1250 1251 1252 1253 1254 1255 1256 1257 1258 1259
			size = extent_size;
		}

		if (size < 32 * extent_size) {
			size_increase = extent_size;
		} else {
			/* Below in fsp_fill_free_list() we assume
			that we add at most FSP_FREE_ADD extents at
			a time */
			size_increase = FSP_FREE_ADD * extent_size;
osku's avatar
osku committed
1260 1261
		}
	}
1262

osku's avatar
osku committed
1263 1264 1265 1266
	if (size_increase == 0) {

		return(TRUE);
	}
1267

osku's avatar
osku committed
1268
	success = fil_extend_space_to_desired_size(&actual_size, space,
1269
						   size + size_increase);
osku's avatar
osku committed
1270 1271 1272
	/* We ignore any fragments of a full megabyte when storing the size
	to the space header */

1273 1274
	if (!zip_size) {
		new_size = ut_calc_align_down(actual_size,
1275
					      (1024 * 1024) / UNIV_PAGE_SIZE);
1276 1277
	} else {
		new_size = ut_calc_align_down(actual_size,
1278
					      (1024 * 1024) / zip_size);
1279 1280
	}
	mlog_write_ulint(header + FSP_SIZE, new_size, MLOG_4BYTES, mtr);
osku's avatar
osku committed
1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305

	*actual_increase = new_size - old_size;

	return(TRUE);
}

/**************************************************************************
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(
/*===============*/
	ibool		init_space,	/* in: TRUE if this is a single-table
					tablespace and we are only initing
					the tablespace's first extent
					descriptor page and ibuf bitmap page;
					then we do not allocate more extents */
	ulint		space,		/* in: space */
	fsp_header_t*	header,		/* in: space header */
	mtr_t*		mtr)		/* in: mtr */
{
	ulint	limit;
	ulint	size;
1306
	ulint	zip_size;
osku's avatar
osku committed
1307
	xdes_t*	descr;
1308
	ulint	count		= 0;
osku's avatar
osku committed
1309 1310 1311 1312 1313 1314
	ulint	frag_n_used;
	ulint	actual_increase;
	ulint	i;
	mtr_t	ibuf_mtr;

	ut_ad(header && mtr);
1315
	ut_ad(page_offset(header) == FSP_HEADER_OFFSET);
1316

osku's avatar
osku committed
1317 1318 1319 1320
	/* 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);

1321 1322
	zip_size = dict_table_flags_to_zip_size(
		mach_read_from_4(FSP_SPACE_FLAGS + header));
1323
	ut_a(ut_is_2pow(zip_size));
1324 1325
	ut_a(zip_size <= UNIV_PAGE_SIZE);
	ut_a(!zip_size || zip_size >= PAGE_ZIP_MIN_SIZE);
1326

osku's avatar
osku committed
1327
	if (space == 0 && srv_auto_extend_last_data_file
1328
	    && size < limit + FSP_EXTENT_SIZE * FSP_FREE_ADD) {
osku's avatar
osku committed
1329 1330 1331 1332 1333 1334 1335

		/* Try to increase the last data file size */
		fsp_try_extend_data_file(&actual_increase, space, header, mtr);
		size = mtr_read_ulint(header + FSP_SIZE, MLOG_4BYTES, mtr);
	}

	if (space != 0 && !init_space
1336
	    && size < limit + FSP_EXTENT_SIZE * FSP_FREE_ADD) {
osku's avatar
osku committed
1337 1338 1339 1340 1341 1342 1343

		/* Try to increase the .ibd file size */
		fsp_try_extend_data_file(&actual_increase, space, header, mtr);
		size = mtr_read_ulint(header + FSP_SIZE, MLOG_4BYTES, mtr);
	}

	i = limit;
1344

osku's avatar
osku committed
1345
	while ((init_space && i < 1)
1346
	       || ((i + FSP_EXTENT_SIZE <= size) && (count < FSP_FREE_ADD))) {
osku's avatar
osku committed
1347

1348 1349
		ibool	init_xdes;
		if (zip_size) {
1350
			init_xdes = ut_2pow_remainder(i, zip_size) == 0;
1351
		} else {
1352
			init_xdes = ut_2pow_remainder(i, UNIV_PAGE_SIZE) == 0;
1353
		}
1354

osku's avatar
osku committed
1355
		mlog_write_ulint(header + FSP_FREE_LIMIT, i + FSP_EXTENT_SIZE,
1356
				 MLOG_4BYTES, mtr);
osku's avatar
osku committed
1357 1358 1359 1360

		/* Update the free limit info in the log system and make
		a checkpoint */
		if (space == 0) {
1361
			ut_a(!zip_size);
1362 1363 1364
			log_fsp_current_free_limit_set_and_checkpoint(
				(i + FSP_EXTENT_SIZE)
				/ ((1024 * 1024) / UNIV_PAGE_SIZE));
osku's avatar
osku committed
1365 1366
		}

1367
		if (UNIV_UNLIKELY(init_xdes)) {
osku's avatar
osku committed
1368

1369 1370
			buf_block_t*	block;

osku's avatar
osku committed
1371 1372 1373 1374 1375
			/* 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) {
1376
				block = buf_page_create(
1377
					space, i, zip_size, mtr);
1378 1379
				buf_page_get(space, zip_size, i,
					     RW_X_LATCH, mtr);
1380 1381
				buf_block_dbg_add_level(block,
							SYNC_FSP_PAGE);
marko's avatar
marko committed
1382

1383 1384 1385
				fsp_init_file_page(block, mtr);
				mlog_write_ulint(buf_block_get_frame(block)
						 + FIL_PAGE_TYPE,
1386 1387
						 FIL_PAGE_TYPE_XDES,
						 MLOG_2BYTES, mtr);
osku's avatar
osku committed
1388 1389 1390 1391 1392 1393
			}

			/* Initialize the ibuf bitmap page in a separate
			mini-transaction because it is low in the latching
			order, and we must be able to release its latch
			before returning from the fsp routine */
1394

osku's avatar
osku committed
1395 1396
			mtr_start(&ibuf_mtr);

1397
			block = buf_page_create(space,
1398 1399
						    i + FSP_IBUF_BITMAP_OFFSET,
						    zip_size, &ibuf_mtr);
1400 1401
			buf_page_get(space, zip_size,
				     i + FSP_IBUF_BITMAP_OFFSET,
1402
				     RW_X_LATCH, &ibuf_mtr);
1403
			buf_block_dbg_add_level(block, SYNC_FSP_PAGE);
marko's avatar
marko committed
1404

1405
			fsp_init_file_page(block, &ibuf_mtr);
osku's avatar
osku committed
1406

1407
			ibuf_bitmap_page_init(block, &ibuf_mtr);
osku's avatar
osku committed
1408 1409 1410 1411 1412

			mtr_commit(&ibuf_mtr);
		}

		descr = xdes_get_descriptor_with_space_hdr(header, space, i,
1413
							   mtr);
osku's avatar
osku committed
1414 1415
		xdes_init(descr, mtr);

1416 1417 1418
#if UNIV_PAGE_SIZE % FSP_EXTENT_SIZE
# error "UNIV_PAGE_SIZE % FSP_EXTENT_SIZE != 0"
#endif
1419 1420
#if PAGE_ZIP_MIN_SIZE % FSP_EXTENT_SIZE
# error "PAGE_ZIP_MIN_SIZE % FSP_EXTENT_SIZE != 0"
1421
#endif
osku's avatar
osku committed
1422

1423
		if (UNIV_UNLIKELY(init_xdes)) {
osku's avatar
osku committed
1424 1425 1426 1427 1428 1429 1430

			/* 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,
1431
				     FSP_IBUF_BITMAP_OFFSET, FALSE, mtr);
osku's avatar
osku committed
1432 1433 1434
			xdes_set_state(descr, XDES_FREE_FRAG, mtr);

			flst_add_last(header + FSP_FREE_FRAG,
1435
				      descr + XDES_FLST_NODE, mtr);
osku's avatar
osku committed
1436
			frag_n_used = mtr_read_ulint(header + FSP_FRAG_N_USED,
1437
						     MLOG_4BYTES, mtr);
osku's avatar
osku committed
1438
			mlog_write_ulint(header + FSP_FRAG_N_USED,
1439
					 frag_n_used + 2, MLOG_4BYTES, mtr);
osku's avatar
osku committed
1440 1441
		} else {
			flst_add_last(header + FSP_FREE,
1442
				      descr + XDES_FLST_NODE, mtr);
osku's avatar
osku committed
1443 1444 1445 1446 1447
			count++;
		}

		i += FSP_EXTENT_SIZE;
	}
1448
}
osku's avatar
osku committed
1449 1450 1451 1452 1453 1454 1455 1456 1457 1458

/**************************************************************************
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 */
1459 1460
	ulint	zip_size,/* in: compressed page size in bytes
			or 0 for uncompressed pages */
osku's avatar
osku committed
1461 1462 1463 1464 1465 1466 1467 1468
	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;
1469

osku's avatar
osku committed
1470 1471
	ut_ad(mtr);

1472
	header = fsp_get_space_header(space, zip_size, mtr);
osku's avatar
osku committed
1473 1474 1475 1476 1477

	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 */
1478
	} else {
osku's avatar
osku committed
1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491
		/* 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(FALSE, space, header, mtr);

			first = flst_get_first(header + FSP_FREE, mtr);
		}

		if (fil_addr_is_null(first)) {

			return(NULL);	/* No free extents left */
		}
1492

1493
		descr = xdes_lst_get_descriptor(space, zip_size, first, mtr);
osku's avatar
osku committed
1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509
	}

	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 */
1510 1511
	ulint	zip_size,/* in: compressed page size in bytes
			or 0 for uncompressed pages */
osku's avatar
osku committed
1512 1513 1514 1515 1516 1517
	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;
1518
	buf_block_t*	block;
osku's avatar
osku committed
1519 1520 1521 1522 1523
	ulint		free;
	ulint		frag_n_used;
	ulint		page_no;
	ulint		space_size;
	ibool		success;
1524

osku's avatar
osku committed
1525 1526
	ut_ad(mtr);

1527
	header = fsp_get_space_header(space, zip_size, mtr);
osku's avatar
osku committed
1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544

	/* 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. */
1545

1546 1547
			descr = fsp_alloc_free_extent(space, zip_size,
						      hint, mtr);
osku's avatar
osku committed
1548 1549 1550 1551 1552 1553 1554 1555 1556

			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,
1557
				      descr + XDES_FLST_NODE, mtr);
osku's avatar
osku committed
1558
		} else {
1559 1560
			descr = xdes_lst_get_descriptor(space, zip_size,
							first, mtr);
osku's avatar
osku committed
1561 1562 1563 1564 1565 1566 1567 1568 1569 1570
		}

		/* 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,
1571
			     hint % FSP_EXTENT_SIZE, mtr);
osku's avatar
osku committed
1572 1573 1574
	if (free == ULINT_UNDEFINED) {

		ut_print_buf(stderr, ((byte*)descr) - 500, 1000);
1575
		putc('\n', stderr);
osku's avatar
osku committed
1576 1577 1578 1579 1580 1581 1582 1583 1584

		ut_error;
	}

	page_no = xdes_get_offset(descr) + free;

	space_size = mtr_read_ulint(header + FSP_SIZE, MLOG_4BYTES, mtr);

	if (space_size <= page_no) {
1585
		/* It must be that we are extending a single-table tablespace
osku's avatar
osku committed
1586 1587 1588 1589
		whose size is still < 64 pages */

		ut_a(space != 0);
		if (page_no >= FSP_EXTENT_SIZE) {
1590
			fprintf(stderr,
1591 1592 1593 1594 1595 1596
				"InnoDB: Error: trying to extend a"
				" single-table tablespace %lu\n"
				"InnoDB: by single page(s) though the"
				" space size %lu. Page no %lu.\n",
				(ulong) space, (ulong) space_size,
				(ulong) page_no);
osku's avatar
osku committed
1597 1598 1599
			return(FIL_NULL);
		}
		success = fsp_try_extend_data_file_with_pages(space, page_no,
1600
							      header, mtr);
osku's avatar
osku committed
1601 1602
		if (!success) {
			/* No disk space left */
1603
			return(FIL_NULL);
osku's avatar
osku committed
1604 1605 1606 1607 1608 1609 1610
		}
	}

	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,
1611
				     mtr);
osku's avatar
osku committed
1612 1613
	frag_n_used++;
	mlog_write_ulint(header + FSP_FRAG_N_USED, frag_n_used, MLOG_4BYTES,
1614
			 mtr);
osku's avatar
osku committed
1615 1616 1617
	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,
1618
			    mtr);
osku's avatar
osku committed
1619
		xdes_set_state(descr, XDES_FULL_FRAG, mtr);
1620

osku's avatar
osku committed
1621
		flst_add_last(header + FSP_FULL_FRAG, descr + XDES_FLST_NODE,
1622
			      mtr);
osku's avatar
osku committed
1623
		mlog_write_ulint(header + FSP_FRAG_N_USED,
1624 1625
				 frag_n_used - FSP_EXTENT_SIZE, MLOG_4BYTES,
				 mtr);
osku's avatar
osku committed
1626 1627 1628 1629 1630
	}

	/* 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. */
1631

1632
	buf_page_create(space, page_no, zip_size, mtr);
osku's avatar
osku committed
1633

1634
	block = buf_page_get(space, zip_size, page_no, RW_X_LATCH, mtr);
1635
	buf_block_dbg_add_level(block, SYNC_FSP_PAGE);
osku's avatar
osku committed
1636 1637

	/* Prior contents of the page should be ignored */
1638
	fsp_init_file_page(block, mtr);
1639

osku's avatar
osku committed
1640 1641 1642 1643 1644 1645 1646 1647 1648 1649
	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 */
1650 1651
	ulint	zip_size,/* in: compressed page size in bytes
			or 0 for uncompressed pages */
osku's avatar
osku committed
1652 1653 1654 1655 1656 1657 1658
	ulint	page,	/* in: page offset */
	mtr_t*	mtr)	/* in: mtr handle */
{
	fsp_header_t*	header;
	xdes_t*		descr;
	ulint		state;
	ulint		frag_n_used;
1659

osku's avatar
osku committed
1660 1661
	ut_ad(mtr);

1662
	/* fprintf(stderr, "Freeing page %lu in space %lu\n", page, space); */
osku's avatar
osku committed
1663

1664
	header = fsp_get_space_header(space, zip_size, mtr);
osku's avatar
osku committed
1665 1666 1667 1668

	descr = xdes_get_descriptor_with_space_hdr(header, space, page, mtr);

	state = xdes_get_state(descr, mtr);
1669

osku's avatar
osku committed
1670 1671
	if (state != XDES_FREE_FRAG && state != XDES_FULL_FRAG) {
		fprintf(stderr,
1672 1673 1674 1675
			"InnoDB: Error: File space extent descriptor"
			" of page %lu has state %lu\n",
			(ulong) page,
			(ulong) state);
osku's avatar
osku committed
1676 1677 1678
		fputs("InnoDB: Dump of descriptor: ", stderr);
		ut_print_buf(stderr, ((byte*)descr) - 50, 200);
		putc('\n', stderr);
1679

osku's avatar
osku committed
1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691
		if (state == XDES_FREE) {
			/* We put here some fault tolerance: if the page
			is already free, return without doing anything! */

			return;
		}

		ut_error;
	}

	if (xdes_get_bit(descr, XDES_FREE_BIT, page % FSP_EXTENT_SIZE, mtr)) {
		fprintf(stderr,
1692 1693 1694
			"InnoDB: Error: File space extent descriptor"
			" of page %lu says it is free\n"
			"InnoDB: Dump of descriptor: ", (ulong) page);
osku's avatar
osku committed
1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707
		ut_print_buf(stderr, ((byte*)descr) - 50, 200);
		putc('\n', stderr);

		/* We put here some fault tolerance: if the page
		is already free, return without doing anything! */

		return;
	}

	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,
1708
				     mtr);
osku's avatar
osku committed
1709 1710 1711
	if (state == XDES_FULL_FRAG) {
		/* The fragment was full: move it to another list */
		flst_remove(header + FSP_FULL_FRAG, descr + XDES_FLST_NODE,
1712
			    mtr);
osku's avatar
osku committed
1713 1714
		xdes_set_state(descr, XDES_FREE_FRAG, mtr);
		flst_add_last(header + FSP_FREE_FRAG, descr + XDES_FLST_NODE,
1715
			      mtr);
osku's avatar
osku committed
1716
		mlog_write_ulint(header + FSP_FRAG_N_USED,
1717 1718
				 frag_n_used + FSP_EXTENT_SIZE - 1,
				 MLOG_4BYTES, mtr);
osku's avatar
osku committed
1719 1720 1721
	} else {
		ut_a(frag_n_used > 0);
		mlog_write_ulint(header + FSP_FRAG_N_USED, frag_n_used - 1,
1722
				 MLOG_4BYTES, mtr);
osku's avatar
osku committed
1723 1724 1725
	}

	if (xdes_is_free(descr, mtr)) {
1726
		/* The extent has become free: move it to another list */
osku's avatar
osku committed
1727
		flst_remove(header + FSP_FREE_FRAG, descr + XDES_FLST_NODE,
1728
			    mtr);
1729
		fsp_free_extent(space, zip_size, page, mtr);
1730
	}
osku's avatar
osku committed
1731 1732 1733 1734 1735 1736 1737 1738 1739
}

/**************************************************************************
Returns an extent to the free list of a space. */
static
void
fsp_free_extent(
/*============*/
	ulint	space,	/* in: space id */
1740 1741
	ulint	zip_size,/* in: compressed page size in bytes
			or 0 for uncompressed pages */
osku's avatar
osku committed
1742 1743 1744 1745 1746
	ulint	page,	/* in: page offset in the extent */
	mtr_t*	mtr)	/* in: mtr */
{
	fsp_header_t*	header;
	xdes_t*		descr;
1747

osku's avatar
osku committed
1748 1749
	ut_ad(mtr);

1750
	header = fsp_get_space_header(space, zip_size, mtr);
osku's avatar
osku committed
1751 1752 1753 1754 1755 1756

	descr = xdes_get_descriptor_with_space_hdr(header, space, page, mtr);

	if (xdes_get_state(descr, mtr) == XDES_FREE) {

		ut_print_buf(stderr, (byte*)descr - 500, 1000);
1757
		putc('\n', stderr);
osku's avatar
osku committed
1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775

		ut_error;
	}

	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 */
1776 1777 1778 1779
	ulint	zip_size __attribute__((unused)),
			/* in: compressed page size, or 0 */
	mtr_t*	mtr __attribute__((unused)))
			/* in: mini-transaction handle */
osku's avatar
osku committed
1780
{
1781
	ut_ad(i < FSP_SEG_INODES_PER_PAGE(zip_size));
1782
	ut_ad(mtr_memo_contains_page(mtr, page, MTR_MEMO_PAGE_X_FIX));
osku's avatar
osku committed
1783 1784 1785 1786 1787

	return(page + FSEG_ARR_OFFSET + FSEG_INODE_SIZE * i);
}

/**************************************************************************
1788
Looks for a used segment inode on a segment inode page. */
osku's avatar
osku committed
1789 1790 1791 1792 1793 1794 1795
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 */
1796
	ulint	zip_size,/* in: compressed page size, or 0 */
osku's avatar
osku committed
1797 1798 1799 1800 1801
	mtr_t*	mtr)	/* in: mini-transaction handle */
{
	ulint		i;
	fseg_inode_t*	inode;

1802
	for (i = 0; i < FSP_SEG_INODES_PER_PAGE(zip_size); i++) {
osku's avatar
osku committed
1803

1804 1805
		inode = fsp_seg_inode_page_get_nth_inode(
			page, i, zip_size, mtr);
osku's avatar
osku committed
1806

1807
		if (!ut_dulint_is_zero(mach_read_from_8(inode + FSEG_ID))) {
osku's avatar
osku committed
1808
			/* This is used */
1809

osku's avatar
osku committed
1810 1811 1812 1813 1814 1815 1816 1817
			return(i);
		}
	}

	return(ULINT_UNDEFINED);
}

/**************************************************************************
1818
Looks for an unused segment inode on a segment inode page. */
osku's avatar
osku committed
1819 1820 1821 1822 1823 1824 1825
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 */
1826 1827
	ulint	i,	/* in: search forward starting from this index */
	ulint	zip_size,/* in: compressed page size, or 0 */
osku's avatar
osku committed
1828 1829 1830 1831
	mtr_t*	mtr)	/* in: mini-transaction handle */
{
	fseg_inode_t*	inode;

1832
	for (; i < FSP_SEG_INODES_PER_PAGE(zip_size); i++) {
osku's avatar
osku committed
1833

1834 1835
		inode = fsp_seg_inode_page_get_nth_inode(
			page, i, zip_size, mtr);
osku's avatar
osku committed
1836

1837
		if (ut_dulint_is_zero(mach_read_from_8(inode + FSEG_ID))) {
osku's avatar
osku committed
1838
			/* This is unused */
1839

osku's avatar
osku committed
1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857
			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;
1858
	buf_block_t*	block;
osku's avatar
osku committed
1859 1860 1861
	page_t*		page;
	ulint		page_no;
	ulint		space;
1862
	ulint		zip_size;
osku's avatar
osku committed
1863 1864
	ulint		i;

1865
	ut_ad(page_offset(space_header) == FSP_HEADER_OFFSET);
1866

1867
	space = page_get_space_id(page_align(space_header));
1868 1869
	zip_size = dict_table_flags_to_zip_size(
		mach_read_from_4(FSP_SPACE_FLAGS + space_header));
1870

1871
	page_no = fsp_alloc_free_page(space, zip_size, 0, mtr);
osku's avatar
osku committed
1872 1873 1874 1875 1876 1877

	if (page_no == FIL_NULL) {

		return(FALSE);
	}

1878
	block = buf_page_get(space, zip_size, page_no, RW_X_LATCH, mtr);
1879 1880 1881
	buf_block_dbg_add_level(block, SYNC_FSP_PAGE);

	block->check_index_page_at_flush = FALSE;
osku's avatar
osku committed
1882

1883
	page = buf_block_get_frame(block);
osku's avatar
osku committed
1884

1885
	mlog_write_ulint(page + FIL_PAGE_TYPE, FIL_PAGE_INODE,
1886
			 MLOG_2BYTES, mtr);
osku's avatar
osku committed
1887

1888
	for (i = 0; i < FSP_SEG_INODES_PER_PAGE(zip_size); i++) {
osku's avatar
osku committed
1889

1890 1891
		inode = fsp_seg_inode_page_get_nth_inode(page, i,
							 zip_size, mtr);
osku's avatar
osku committed
1892 1893 1894 1895 1896

		mlog_write_dulint(inode + FSEG_ID, ut_dulint_zero, mtr);
	}

	flst_add_last(space_header + FSP_SEG_INODES_FREE,
1897
		      page + FSEG_INODE_PAGE_NODE, mtr);
osku's avatar
osku committed
1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912
	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;
1913
	buf_block_t*	block;
osku's avatar
osku committed
1914 1915 1916
	page_t*		page;
	fseg_inode_t*	inode;
	ibool		success;
1917
	ulint		zip_size;
osku's avatar
osku committed
1918
	ulint		n;
1919

1920
	ut_ad(page_offset(space_header) == FSP_HEADER_OFFSET);
1921

osku's avatar
osku committed
1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934
	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;

1935 1936
	zip_size = dict_table_flags_to_zip_size(
		mach_read_from_4(FSP_SPACE_FLAGS + space_header));
1937
	block = buf_page_get(page_get_space_id(page_align(space_header)),
1938
			     zip_size, page_no, RW_X_LATCH, mtr);
1939
	buf_block_dbg_add_level(block, SYNC_FSP_PAGE);
marko's avatar
marko committed
1940

1941
	page = buf_block_get_frame(block);
osku's avatar
osku committed
1942

1943
	n = fsp_seg_inode_page_find_free(page, 0, zip_size, mtr);
osku's avatar
osku committed
1944 1945 1946

	ut_a(n != ULINT_UNDEFINED);

1947
	inode = fsp_seg_inode_page_get_nth_inode(page, n, zip_size, mtr);
osku's avatar
osku committed
1948 1949

	if (ULINT_UNDEFINED == fsp_seg_inode_page_find_free(page, n + 1,
1950
							    zip_size, mtr)) {
osku's avatar
osku committed
1951 1952 1953 1954
		/* There are no other unused headers left on the page: move it
		to another list */

		flst_remove(space_header + FSP_SEG_INODES_FREE,
1955
			    page + FSEG_INODE_PAGE_NODE, mtr);
osku's avatar
osku committed
1956 1957

		flst_add_last(space_header + FSP_SEG_INODES_FULL,
1958
			      page + FSEG_INODE_PAGE_NODE, mtr);
osku's avatar
osku committed
1959 1960
	}

1961
	return(inode);
osku's avatar
osku committed
1962 1963 1964 1965 1966 1967 1968 1969 1970
}

/**************************************************************************
Frees a file segment inode. */
static
void
fsp_free_seg_inode(
/*===============*/
	ulint		space,	/* in: space id */
1971 1972
	ulint		zip_size,/* in: compressed page size in bytes
				or 0 for uncompressed pages */
osku's avatar
osku committed
1973 1974 1975 1976 1977
	fseg_inode_t*	inode,	/* in: segment inode */
	mtr_t*		mtr)	/* in: mini-transaction handle */
{
	page_t*		page;
	fsp_header_t*	space_header;
1978

1979
	page = page_align(inode);
osku's avatar
osku committed
1980

1981
	space_header = fsp_get_space_header(space, zip_size, mtr);
osku's avatar
osku committed
1982

1983
	ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE);
osku's avatar
osku committed
1984

1985 1986
	if (ULINT_UNDEFINED
	    == fsp_seg_inode_page_find_free(page, 0, zip_size, mtr)) {
osku's avatar
osku committed
1987 1988 1989 1990

		/* Move the page to another list */

		flst_remove(space_header + FSP_SEG_INODES_FULL,
1991
			    page + FSEG_INODE_PAGE_NODE, mtr);
osku's avatar
osku committed
1992 1993

		flst_add_last(space_header + FSP_SEG_INODES_FREE,
1994
			      page + FSEG_INODE_PAGE_NODE, mtr);
osku's avatar
osku committed
1995 1996
	}

1997 1998 1999
	mlog_write_dulint(inode + FSEG_ID, ut_dulint_zero, mtr);
	mlog_write_ulint(inode + FSEG_MAGIC_N, 0, MLOG_4BYTES, mtr);

2000 2001
	if (ULINT_UNDEFINED
	    == fsp_seg_inode_page_find_used(page, zip_size, mtr)) {
osku's avatar
osku committed
2002 2003 2004 2005

		/* There are no other used headers left on the page: free it */

		flst_remove(space_header + FSP_SEG_INODES_FREE,
2006
			    page + FSEG_INODE_PAGE_NODE, mtr);
osku's avatar
osku committed
2007

2008
		fsp_free_page(space, zip_size, page_get_page_no(page), mtr);
osku's avatar
osku committed
2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019
	}
}

/**************************************************************************
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 */
2020 2021 2022
	ulint		space,	/* in: space id */
	ulint		zip_size,/* in: compressed page size in bytes
				or 0 for uncompressed pages */
osku's avatar
osku committed
2023 2024 2025 2026 2027 2028 2029
	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);
2030
	ut_ad(space == mach_read_from_4(header + FSEG_HDR_SPACE));
2031

2032
	inode = fut_get_ptr(space, zip_size, inode_addr, RW_X_LATCH, mtr);
2033

2034
	ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE);
osku's avatar
osku committed
2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045

	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 */
2046
	fseg_inode_t*	inode,	/* in: segment inode */
osku's avatar
osku committed
2047 2048 2049 2050 2051
	ulint		n,	/* in: slot index */
	mtr_t*		mtr __attribute__((unused))) /* in: mtr handle */
{
	ut_ad(inode && mtr);
	ut_ad(n < FSEG_FRAG_ARR_N_SLOTS);
2052
	ut_ad(mtr_memo_contains_page(mtr, inode, MTR_MEMO_PAGE_X_FIX));
osku's avatar
osku committed
2053
	return(mach_read_from_4(inode + FSEG_FRAG_ARR
2054
				+ n * FSEG_FRAG_SLOT_SIZE));
osku's avatar
osku committed
2055 2056 2057 2058 2059 2060 2061 2062
}

/**************************************************************************
Sets the page number in the nth fragment page slot. */
UNIV_INLINE
void
fseg_set_nth_frag_page_no(
/*======================*/
2063
	fseg_inode_t*	inode,	/* in: segment inode */
osku's avatar
osku committed
2064 2065 2066 2067 2068 2069
	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);
2070
	ut_ad(mtr_memo_contains_page(mtr, inode, MTR_MEMO_PAGE_X_FIX));
osku's avatar
osku committed
2071 2072

	mlog_write_ulint(inode + FSEG_FRAG_ARR + n * FSEG_FRAG_SLOT_SIZE,
2073
			 page_no, MLOG_4BYTES, mtr);
osku's avatar
osku committed
2074 2075 2076 2077 2078 2079 2080 2081 2082 2083
}

/**************************************************************************
Finds a fragment page slot which is free. */
static
ulint
fseg_find_free_frag_page_slot(
/*==========================*/
				/* out: slot index; ULINT_UNDEFINED if none
				found */
2084
	fseg_inode_t*	inode,	/* in: segment inode */
osku's avatar
osku committed
2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111
	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 */
2112
	fseg_inode_t*	inode,	/* in: segment inode */
osku's avatar
osku committed
2113 2114 2115 2116 2117 2118 2119 2120
	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++) {
2121 2122
		page_no = fseg_get_nth_frag_page_no(
			inode, FSEG_FRAG_ARR_N_SLOTS - i - 1, mtr);
osku's avatar
osku committed
2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139

		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 */
2140
	fseg_inode_t*	inode,	/* in: segment inode */
osku's avatar
osku committed
2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158
	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. */
2159
UNIV_INTERN
2160
buf_block_t*
osku's avatar
osku committed
2161 2162
fseg_create_general(
/*================*/
2163
			/* out: the block where the segment header is placed,
osku's avatar
osku committed
2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180
			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 the other for the segment) then there is
			no need to do the check for this individual
			operation */
	mtr_t*	mtr)	/* in: mtr */
{
2181
	ulint		flags;
2182
	ulint		zip_size;
osku's avatar
osku committed
2183 2184 2185
	fsp_header_t*	space_header;
	fseg_inode_t*	inode;
	dulint		seg_id;
2186 2187
	buf_block_t*	block	= 0; /* remove warning */
	fseg_header_t*	header	= 0; /* remove warning */
osku's avatar
osku committed
2188 2189 2190 2191 2192 2193
	rw_lock_t*	latch;
	ibool		success;
	ulint		n_reserved;
	ulint		i;

	ut_ad(mtr);
2194 2195
	ut_ad(byte_offset + FSEG_HEADER_SIZE
	      <= UNIV_PAGE_SIZE - FIL_PAGE_DATA_END);
osku's avatar
osku committed
2196

2197 2198
	latch = fil_space_get_latch(space, &flags);
	zip_size = dict_table_flags_to_zip_size(flags);
2199

osku's avatar
osku committed
2200
	if (page != 0) {
2201
		block = buf_page_get(space, zip_size, page, RW_X_LATCH, mtr);
2202
		header = byte_offset + buf_block_get_frame(block);
2203 2204
	}

osku's avatar
osku committed
2205
	ut_ad(!mutex_own(&kernel_mutex)
2206
	      || mtr_memo_contains(mtr, latch, MTR_MEMO_X_LOCK));
osku's avatar
osku committed
2207

2208
	mtr_x_lock(latch, mtr);
osku's avatar
osku committed
2209 2210 2211 2212 2213

	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 */

2214 2215
		if (space == IBUF_SPACE_ID) {
			ibuf_free_excess_pages();
osku's avatar
osku committed
2216 2217 2218
		}
	}

2219
	if (!has_done_reservation) {
osku's avatar
osku committed
2220
		success = fsp_reserve_free_extents(&n_reserved, space, 2,
2221
						   FSP_NORMAL, mtr);
osku's avatar
osku committed
2222 2223 2224 2225 2226
		if (!success) {
			return(NULL);
		}
	}

2227
	space_header = fsp_get_space_header(space, zip_size, mtr);
osku's avatar
osku committed
2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241

	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, mtr);

	mlog_write_dulint(space_header + FSP_SEG_ID, ut_dulint_add(seg_id, 1),
2242
			  mtr);
osku's avatar
osku committed
2243

2244 2245
	mlog_write_dulint(inode + FSEG_ID, seg_id, mtr);
	mlog_write_ulint(inode + FSEG_NOT_FULL_N_USED, 0, MLOG_4BYTES, mtr);
osku's avatar
osku committed
2246 2247 2248 2249 2250 2251

	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,
2252
			 MLOG_4BYTES, mtr);
osku's avatar
osku committed
2253 2254 2255 2256 2257
	for (i = 0; i < FSEG_FRAG_ARR_N_SLOTS; i++) {
		fseg_set_nth_frag_page_no(inode, i, FIL_NULL, mtr);
	}

	if (page == 0) {
2258 2259
		page = fseg_alloc_free_page_low(space, zip_size,
						inode, 0, FSP_UP, mtr);
2260

osku's avatar
osku committed
2261 2262
		if (page == FIL_NULL) {

2263
			fsp_free_seg_inode(space, zip_size, inode, mtr);
osku's avatar
osku committed
2264 2265 2266 2267

			goto funct_exit;
		}

2268
		block = buf_page_get(space, zip_size, page, RW_X_LATCH, mtr);
2269
		header = byte_offset + buf_block_get_frame(block);
2270
		mlog_write_ulint(header - byte_offset + FIL_PAGE_TYPE,
2271
				 FIL_PAGE_TYPE_SYS, MLOG_2BYTES, mtr);
2272
	}
osku's avatar
osku committed
2273 2274

	mlog_write_ulint(header + FSEG_HDR_OFFSET,
2275
			 page_offset(inode), MLOG_2BYTES, mtr);
osku's avatar
osku committed
2276 2277

	mlog_write_ulint(header + FSEG_HDR_PAGE_NO,
2278 2279
			 page_get_page_no(page_align(inode)),
			 MLOG_4BYTES, mtr);
osku's avatar
osku committed
2280 2281 2282 2283

	mlog_write_ulint(header + FSEG_HDR_SPACE, space, MLOG_4BYTES, mtr);

funct_exit:
2284 2285
	if (!has_done_reservation) {

osku's avatar
osku committed
2286 2287
		fil_space_release_free_extents(space, n_reserved);
	}
2288

2289
	return(block);
osku's avatar
osku committed
2290 2291 2292 2293
}

/**************************************************************************
Creates a new segment. */
2294
UNIV_INTERN
2295
buf_block_t*
osku's avatar
osku committed
2296 2297
fseg_create(
/*========*/
2298
			/* out: the block where the segment header is placed,
osku's avatar
osku committed
2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320
			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 */
2321
	fseg_inode_t*	inode,	/* in: segment inode */
osku's avatar
osku committed
2322 2323 2324 2325 2326 2327
	ulint*		used,	/* out: number of pages used (<= reserved) */
	mtr_t*		mtr)	/* in: mtr handle */
{
	ulint	ret;

	ut_ad(inode && used && mtr);
2328
	ut_ad(mtr_memo_contains_page(mtr, inode, MTR_MEMO_PAGE_X_FIX));
2329

osku's avatar
osku committed
2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344
	*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. */
2345
UNIV_INTERN
osku's avatar
osku committed
2346 2347 2348 2349
ulint
fseg_n_reserved_pages(
/*==================*/
				/* out: number of reserved pages */
2350
	fseg_header_t*	header,	/* in: segment header */
osku's avatar
osku committed
2351 2352 2353 2354 2355 2356
	ulint*		used,	/* out: number of pages used (<= reserved) */
	mtr_t*		mtr)	/* in: mtr handle */
{
	ulint		ret;
	fseg_inode_t*	inode;
	ulint		space;
2357
	ulint		flags;
2358
	ulint		zip_size;
2359
	rw_lock_t*	latch;
osku's avatar
osku committed
2360

2361
	space = page_get_space_id(page_align(header));
2362 2363
	latch = fil_space_get_latch(space, &flags);
	zip_size = dict_table_flags_to_zip_size(flags);
osku's avatar
osku committed
2364 2365

	ut_ad(!mutex_own(&kernel_mutex)
2366
	      || mtr_memo_contains(mtr, latch, MTR_MEMO_X_LOCK));
2367

2368
	mtr_x_lock(latch, mtr);
osku's avatar
osku committed
2369

2370
	inode = fseg_inode_get(header, space, zip_size, mtr);
2371

osku's avatar
osku committed
2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387
	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 */
2388 2389
	ulint		zip_size,/* in: compressed page size in bytes
				or 0 for uncompressed pages */
osku's avatar
osku committed
2390 2391 2392 2393 2394 2395 2396 2397 2398
	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;
2399

osku's avatar
osku committed
2400
	ut_ad(inode && mtr);
2401
	ut_ad(!((page_offset(inode) - FSEG_ARR_OFFSET) % FSEG_INODE_SIZE));
osku's avatar
osku committed
2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416

	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;
	}
2417

osku's avatar
osku committed
2418
	for (i = 0; i < FSEG_FREE_LIST_MAX_LEN; i++) {
2419
		descr = xdes_get_descriptor(space, zip_size, hint, mtr);
osku's avatar
osku committed
2420

2421 2422
		if ((descr == NULL)
		    || (XDES_FREE != xdes_get_state(descr, mtr))) {
osku's avatar
osku committed
2423 2424 2425

			/* We cannot allocate the desired extent: stop */

2426
			return;
osku's avatar
osku committed
2427 2428
		}

2429
		descr = fsp_alloc_free_extent(space, zip_size, hint, mtr);
2430

osku's avatar
osku committed
2431
		xdes_set_state(descr, XDES_FSEG, mtr);
2432

osku's avatar
osku committed
2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453
		seg_id = mtr_read_dulint(inode + FSEG_ID, mtr);
		mlog_write_dulint(descr + XDES_ID, seg_id, 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 */
2454 2455
	ulint		zip_size,/* in: compressed page size in bytes
				or 0 for uncompressed pages */
osku's avatar
osku committed
2456 2457 2458 2459
	mtr_t*		mtr)	/* in: mtr */
{
	xdes_t*		descr;
	dulint		seg_id;
2460 2461
	fil_addr_t	first;

2462
	ut_ad(!((page_offset(inode) - FSEG_ARR_OFFSET) % FSEG_INODE_SIZE));
2463

osku's avatar
osku committed
2464 2465
	if (flst_get_len(inode + FSEG_FREE, mtr) > 0) {
		/* Segment free list is not empty, allocate from it */
2466

osku's avatar
osku committed
2467 2468
		first = flst_get_first(inode + FSEG_FREE, mtr);

2469
		descr = xdes_lst_get_descriptor(space, zip_size, first, mtr);
osku's avatar
osku committed
2470 2471
	} else {
		/* Segment free list was empty, allocate from space */
2472
		descr = fsp_alloc_free_extent(space, zip_size, 0, mtr);
osku's avatar
osku committed
2473 2474 2475 2476 2477 2478 2479

		if (descr == NULL) {

			return(NULL);
		}

		seg_id = mtr_read_dulint(inode + FSEG_ID, mtr);
2480

osku's avatar
osku committed
2481 2482 2483
		xdes_set_state(descr, XDES_FSEG, mtr);
		mlog_write_dulint(descr + XDES_ID, seg_id, mtr);
		flst_add_last(inode + FSEG_FREE, descr + XDES_FLST_NODE, mtr);
2484

osku's avatar
osku committed
2485
		/* Try to fill the segment free list */
2486
		fseg_fill_free_list(inode, space, zip_size,
2487 2488
				    xdes_get_offset(descr) + FSP_EXTENT_SIZE,
				    mtr);
osku's avatar
osku committed
2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504
	}

	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 */
2505 2506
	ulint		zip_size,/* in: compressed page size in bytes
				or 0 for uncompressed pages */
2507
	fseg_inode_t*	seg_inode, /* in: segment inode */
osku's avatar
osku committed
2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527
	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 */
{
	fsp_header_t*	space_header;
	ulint		space_size;
	dulint		seg_id;
	ulint		used;
	ulint		reserved;
	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 */
	ibool		frag_page_allocated = FALSE;
	ibool		success;
	ulint		n;
2528

osku's avatar
osku committed
2529 2530
	ut_ad(mtr);
	ut_ad((direction >= FSP_UP) && (direction <= FSP_NO_DIR));
2531 2532 2533
	ut_ad(mach_read_from_4(seg_inode + FSEG_MAGIC_N)
	      == FSEG_MAGIC_N_VALUE);
	ut_ad(!((page_offset(seg_inode) - FSEG_ARR_OFFSET) % FSEG_INODE_SIZE));
osku's avatar
osku committed
2534 2535
	seg_id = mtr_read_dulint(seg_inode + FSEG_ID, mtr);

2536
	ut_ad(!ut_dulint_is_zero(seg_id));
2537

osku's avatar
osku committed
2538
	reserved = fseg_n_reserved_pages_low(seg_inode, &used, mtr);
2539

2540
	space_header = fsp_get_space_header(space, zip_size, mtr);
osku's avatar
osku committed
2541 2542

	descr = xdes_get_descriptor_with_space_hdr(space_header, space,
2543
						   hint, mtr);
osku's avatar
osku committed
2544 2545 2546 2547
	if (descr == NULL) {
		/* Hint outside space or too high above free limit: reset
		hint */
		hint = 0;
2548
		descr = xdes_get_descriptor(space, zip_size, hint, mtr);
osku's avatar
osku committed
2549
	}
2550

osku's avatar
osku committed
2551
	/* In the big if-else below we look for ret_page and ret_descr */
2552
	/*-------------------------------------------------------------*/
osku's avatar
osku committed
2553
	if ((xdes_get_state(descr, mtr) == XDES_FSEG)
2554 2555 2556 2557
	    && (0 == ut_dulint_cmp(mtr_read_dulint(descr + XDES_ID,
						   mtr), seg_id))
	    && (xdes_get_bit(descr, XDES_FREE_BIT,
			     hint % FSP_EXTENT_SIZE, mtr) == TRUE)) {
osku's avatar
osku committed
2558 2559 2560 2561 2562

		/* 1. We can take the hinted page
		=================================*/
		ret_descr = descr;
		ret_page = hint;
2563
		/*-----------------------------------------------------------*/
osku's avatar
osku committed
2564 2565 2566 2567 2568 2569 2570 2571
	} 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
		===============*/
2572
		ret_descr = fsp_alloc_free_extent(space, zip_size, hint, mtr);
osku's avatar
osku committed
2573 2574

		ut_a(ret_descr == descr);
2575

osku's avatar
osku committed
2576 2577 2578
		xdes_set_state(ret_descr, XDES_FSEG, mtr);
		mlog_write_dulint(ret_descr + XDES_ID, seg_id, mtr);
		flst_add_last(seg_inode + FSEG_FREE,
2579
			      ret_descr + XDES_FLST_NODE, mtr);
osku's avatar
osku committed
2580 2581

		/* Try to fill the segment free list */
2582
		fseg_fill_free_list(seg_inode, space, zip_size,
2583
				    hint + FSP_EXTENT_SIZE, mtr);
osku's avatar
osku committed
2584
		ret_page = hint;
2585
		/*-----------------------------------------------------------*/
osku's avatar
osku committed
2586 2587 2588
	} else if ((direction != FSP_NO_DIR)
		   && ((reserved - used) < reserved / FSEG_FILLFACTOR)
		   && (used >= FSEG_FRAG_LIMIT)
2589
		   && (!!(ret_descr
2590 2591
			  = fseg_alloc_free_extent(seg_inode,
						   space, zip_size, mtr)))) {
osku's avatar
osku committed
2592 2593 2594 2595 2596 2597 2598

		/* 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
		==============================================*/
2599
		ret_page = xdes_get_offset(ret_descr);
osku's avatar
osku committed
2600 2601 2602 2603

		if (direction == FSP_DOWN) {
			ret_page += FSP_EXTENT_SIZE - 1;
		}
2604
		/*-----------------------------------------------------------*/
osku's avatar
osku committed
2605
	} else if ((xdes_get_state(descr, mtr) == XDES_FSEG)
2606
		   && (0 == ut_dulint_cmp(mtr_read_dulint(descr + XDES_ID,
2607
							  mtr), seg_id))
2608
		   && (!xdes_is_full(descr, mtr))) {
osku's avatar
osku committed
2609 2610 2611 2612 2613 2614 2615 2616

		/* 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;
2617 2618
		ret_page = xdes_get_offset(ret_descr)
			+ xdes_find_bit(ret_descr, XDES_FREE_BIT, TRUE,
osku's avatar
osku committed
2619
					hint % FSP_EXTENT_SIZE, mtr);
2620
		/*-----------------------------------------------------------*/
osku's avatar
osku committed
2621 2622 2623 2624 2625 2626 2627
	} else if (reserved - used > 0) {
		/* 5. We take any unused page from the segment
		==============================================*/
		fil_addr_t	first;

		if (flst_get_len(seg_inode + FSEG_NOT_FULL, mtr) > 0) {
			first = flst_get_first(seg_inode + FSEG_NOT_FULL,
2628
					       mtr);
osku's avatar
osku committed
2629 2630 2631 2632 2633 2634 2635
		} else if (flst_get_len(seg_inode + FSEG_FREE, mtr) > 0) {
			first = flst_get_first(seg_inode + FSEG_FREE, mtr);
		} else {
			ut_error;
			return(FIL_NULL);
		}

2636 2637
		ret_descr = xdes_lst_get_descriptor(space, zip_size,
						    first, mtr);
2638 2639 2640 2641
		ret_page = xdes_get_offset(ret_descr)
			+ xdes_find_bit(ret_descr, XDES_FREE_BIT, TRUE,
					0, mtr);
		/*-----------------------------------------------------------*/
osku's avatar
osku committed
2642 2643 2644
	} else if (used < FSEG_FRAG_LIMIT) {
		/* 6. We allocate an individual page from the space
		===================================================*/
2645
		ret_page = fsp_alloc_free_page(space, zip_size, hint, mtr);
osku's avatar
osku committed
2646
		ret_descr = NULL;
2647

osku's avatar
osku committed
2648
		frag_page_allocated = TRUE;
2649

osku's avatar
osku committed
2650 2651 2652 2653 2654 2655 2656
		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,
2657
						  mtr);
osku's avatar
osku committed
2658
		}
2659
		/*-----------------------------------------------------------*/
osku's avatar
osku committed
2660 2661 2662
	} else {
		/* 7. We allocate a new extent and take its first page
		======================================================*/
2663 2664
		ret_descr = fseg_alloc_free_extent(seg_inode,
						   space, zip_size, mtr);
osku's avatar
osku committed
2665 2666 2667 2668 2669

		if (ret_descr == NULL) {
			ret_page = FIL_NULL;
		} else {
			ret_page = xdes_get_offset(ret_descr);
2670
		}
osku's avatar
osku committed
2671
	}
2672

osku's avatar
osku committed
2673 2674
	if (ret_page == FIL_NULL) {
		/* Page could not be allocated */
2675

osku's avatar
osku committed
2676 2677 2678 2679 2680 2681 2682
		return(FIL_NULL);
	}

	if (space != 0) {
		space_size = fil_space_get_size(space);

		if (space_size <= ret_page) {
2683
			/* It must be that we are extending a single-table
osku's avatar
osku committed
2684 2685 2686
			tablespace whose size is still < 64 pages */

			if (ret_page >= FSP_EXTENT_SIZE) {
2687
				fprintf(stderr,
2688 2689 2690 2691
					"InnoDB: Error (2): trying to extend"
					" a single-table tablespace %lu\n"
					"InnoDB: by single page(s) though"
					" the space size %lu. Page no %lu.\n",
osku's avatar
osku committed
2692 2693 2694 2695
					(ulong) space, (ulong) space_size,
					(ulong) ret_page);
				return(FIL_NULL);
			}
2696

2697 2698
			success = fsp_try_extend_data_file_with_pages(
				space, ret_page, space_header, mtr);
osku's avatar
osku committed
2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709
			if (!success) {
				/* No disk space left */
				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 */
2710
		buf_block_t*	block;
2711 2712
		ulint		zip_size = dict_table_flags_to_zip_size(
			mach_read_from_4(FSP_SPACE_FLAGS + space_header));
2713

2714
		block = buf_page_create(space, ret_page, zip_size, mtr);
2715
		buf_block_dbg_add_level(block, SYNC_FSP_PAGE);
marko's avatar
marko committed
2716

2717 2718 2719 2720 2721
		if (UNIV_UNLIKELY(block != buf_page_get(space, zip_size,
							ret_page, RW_X_LATCH,
							mtr))) {
			ut_error;
		}
osku's avatar
osku committed
2722 2723

		/* The prior contents of the page should be ignored */
2724
		fsp_init_file_page(block, mtr);
2725

osku's avatar
osku committed
2726 2727 2728
		/* 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. */
2729

2730 2731
		ut_ad(xdes_get_descriptor(space, zip_size, ret_page, mtr)
		      == ret_descr);
osku's avatar
osku committed
2732
		ut_ad(xdes_get_bit(ret_descr, XDES_FREE_BIT,
2733
				   ret_page % FSP_EXTENT_SIZE, mtr) == TRUE);
2734

2735
		fseg_mark_page_used(seg_inode, space, zip_size, ret_page, mtr);
osku's avatar
osku committed
2736 2737 2738
	}

	buf_reset_check_index_page_at_flush(space, ret_page);
2739 2740

	return(ret_page);
osku's avatar
osku committed
2741 2742 2743 2744 2745 2746
}

/**************************************************************************
Allocates a single free page from a segment. This function implements
the intelligent allocation strategy which tries to minimize file space
fragmentation. */
2747
UNIV_INTERN
osku's avatar
osku committed
2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768
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;
2769
	ulint		flags;
2770
	ulint		zip_size;
osku's avatar
osku committed
2771 2772 2773 2774 2775
	rw_lock_t*	latch;
	ibool		success;
	ulint		page_no;
	ulint		n_reserved;

2776
	space = page_get_space_id(page_align(seg_header));
osku's avatar
osku committed
2777

2778 2779 2780
	latch = fil_space_get_latch(space, &flags);

	zip_size = dict_table_flags_to_zip_size(flags);
2781

osku's avatar
osku committed
2782
	ut_ad(!mutex_own(&kernel_mutex)
2783
	      || mtr_memo_contains(mtr, latch, MTR_MEMO_X_LOCK));
osku's avatar
osku committed
2784

2785 2786
	mtr_x_lock(latch, mtr);

osku's avatar
osku committed
2787 2788 2789 2790
	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 */

2791 2792
		if (space == IBUF_SPACE_ID) {
			ibuf_free_excess_pages();
osku's avatar
osku committed
2793 2794 2795
		}
	}

2796
	inode = fseg_inode_get(seg_header, space, zip_size, mtr);
osku's avatar
osku committed
2797 2798 2799

	if (!has_done_reservation) {
		success = fsp_reserve_free_extents(&n_reserved, space, 2,
2800
						   FSP_NORMAL, mtr);
osku's avatar
osku committed
2801 2802 2803 2804 2805
		if (!success) {
			return(FIL_NULL);
		}
	}

2806
	page_no = fseg_alloc_free_page_low(space, zip_size,
2807
					   inode, hint, direction, mtr);
osku's avatar
osku committed
2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818
	if (!has_done_reservation) {
		fil_space_release_free_extents(space, n_reserved);
	}

	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. */
2819
UNIV_INTERN
osku's avatar
osku committed
2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834
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,
2835
					    FALSE, mtr));
osku's avatar
osku committed
2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849
}

/**************************************************************************
Checks that we have at least 2 frag pages free in the first extent of a
single-table tablespace, and they are also physically initialized to the data
file. That is we have already extended the data file so that those pages are
inside the data file. If not, this function extends the tablespace with
pages. */
static
ibool
fsp_reserve_free_pages(
/*===================*/
					/* out: TRUE if there were >= 3 free
					pages, or we were able to extend */
2850
	ulint		space,		/* in: space id, must be != 0 */
osku's avatar
osku committed
2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863
	fsp_header_t*	space_header,	/* in: header of that space,
					x-latched */
	ulint		size,		/* in: size of the tablespace in pages,
					must be < FSP_EXTENT_SIZE / 2 */
	mtr_t*		mtr)		/* in: mtr */
{
	xdes_t*	descr;
	ulint	n_used;

	ut_a(space != 0);
	ut_a(size < FSP_EXTENT_SIZE / 2);

	descr = xdes_get_descriptor_with_space_hdr(space_header, space, 0,
2864
						   mtr);
osku's avatar
osku committed
2865 2866 2867 2868 2869 2870 2871 2872 2873 2874
	n_used = xdes_get_n_used(descr, mtr);

	ut_a(n_used <= size);

	if (size >= n_used + 2) {

		return(TRUE);
	}

	return(fsp_try_extend_data_file_with_pages(space, n_used + 1,
2875
						   space_header, mtr));
osku's avatar
osku committed
2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902
}

/**************************************************************************
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.

Single-table tablespaces whose size is < 32 pages are a special case. In this
function we would liberally reserve several 64 page extents for every page
split or merge in a B-tree. But we do not want to waste disk space if the table
only occupies < 32 pages. That is why we apply different rules in that special
case, just ensuring that there are 3 free pages available. */
2903
UNIV_INTERN
osku's avatar
osku committed
2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920
ibool
fsp_reserve_free_extents(
/*=====================*/
			/* out: TRUE if we were able to make the reservation */
	ulint*	n_reserved,/* out: number of extents actually reserved; if we
			return TRUE and the tablespace size is < 64 pages,
			then this can be 0, otherwise it is n_ext */
	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;
	rw_lock_t*	latch;
	ulint		n_free_list_ext;
	ulint		free_limit;
	ulint		size;
2921
	ulint		flags;
2922
	ulint		zip_size;
osku's avatar
osku committed
2923 2924 2925 2926 2927 2928
	ulint		n_free;
	ulint		n_free_up;
	ulint		reserve;
	ibool		success;
	ulint		n_pages_added;

2929
	ut_ad(mtr);
2930 2931
	*n_reserved = n_ext;

2932 2933 2934
	latch = fil_space_get_latch(space, &flags);
	zip_size = dict_table_flags_to_zip_size(flags);

osku's avatar
osku committed
2935
	ut_ad(!mutex_own(&kernel_mutex)
2936
	      || mtr_memo_contains(mtr, latch, MTR_MEMO_X_LOCK));
osku's avatar
osku committed
2937 2938 2939

	mtr_x_lock(latch, mtr);

2940
	space_header = fsp_get_space_header(space, zip_size, mtr);
osku's avatar
osku committed
2941 2942
try_again:
	size = mtr_read_ulint(space_header + FSP_SIZE, MLOG_4BYTES, mtr);
2943

osku's avatar
osku committed
2944 2945 2946 2947 2948 2949 2950
	if (size < FSP_EXTENT_SIZE / 2) {
		/* Use different rules for small single-table tablespaces */
		*n_reserved = 0;
		return(fsp_reserve_free_pages(space, space_header, size, mtr));
	}

	n_free_list_ext = flst_get_len(space_header + FSP_FREE, mtr);
2951

osku's avatar
osku committed
2952
	free_limit = mtr_read_ulint(space_header + FSP_FREE_LIMIT,
2953
				    MLOG_4BYTES, mtr);
osku's avatar
osku committed
2954 2955 2956 2957 2958 2959 2960 2961 2962

	/* 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--;
2963 2964
		if (!zip_size) {
			n_free_up -= n_free_up
2965
				/ (UNIV_PAGE_SIZE / FSP_EXTENT_SIZE);
2966 2967
		} else {
			n_free_up -= n_free_up
2968
				/ (zip_size / FSP_EXTENT_SIZE);
2969
		}
osku's avatar
osku committed
2970
	}
2971

osku's avatar
osku committed
2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004
	n_free = n_free_list_ext + n_free_up;

	if (alloc_type == FSP_NORMAL) {
		/* We reserve 1 extent + 0.5 % of the space size to undo logs
		and 1 extent + 0.5 % to cleaning operations; NOTE: this source
		code is duplicated in the function below! */

		reserve = 2 + ((size / FSP_EXTENT_SIZE) * 2) / 200;

		if (n_free <= reserve + n_ext) {

			goto try_to_extend;
		}
	} else if (alloc_type == FSP_UNDO) {
		/* We reserve 0.5 % of the space size to cleaning operations */

		reserve = 1 + ((size / FSP_EXTENT_SIZE) * 1) / 200;

		if (n_free <= reserve + n_ext) {

			goto try_to_extend;
		}
	} else {
		ut_a(alloc_type == FSP_CLEANING);
	}

	success = fil_space_reserve_free_extents(space, n_free, n_ext);

	if (success) {
		return(TRUE);
	}
try_to_extend:
	success = fsp_try_extend_data_file(&n_pages_added, space,
3005
					   space_header, mtr);
osku's avatar
osku committed
3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018
	if (success && n_pages_added > 0) {

		goto try_again;
	}

	return(FALSE);
}

/**************************************************************************
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. */
3019
UNIV_INTERN
3020
ullint
osku's avatar
osku committed
3021 3022 3023 3024 3025 3026 3027 3028 3029
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;
3030
	ulint		flags;
3031
	ulint		zip_size;
osku's avatar
osku committed
3032 3033 3034 3035 3036
	ulint		n_free;
	ulint		n_free_up;
	ulint		reserve;
	rw_lock_t*	latch;
	mtr_t		mtr;
3037

osku's avatar
osku committed
3038
	ut_ad(!mutex_own(&kernel_mutex));
3039

osku's avatar
osku committed
3040
	mtr_start(&mtr);
3041

3042 3043
	latch = fil_space_get_latch(space, &flags);
	zip_size = dict_table_flags_to_zip_size(flags);
osku's avatar
osku committed
3044 3045 3046

	mtr_x_lock(latch, &mtr);

3047
	space_header = fsp_get_space_header(space, zip_size, &mtr);
3048

osku's avatar
osku committed
3049
	size = mtr_read_ulint(space_header + FSP_SIZE, MLOG_4BYTES, &mtr);
3050

osku's avatar
osku committed
3051
	n_free_list_ext = flst_get_len(space_header + FSP_FREE, &mtr);
3052

osku's avatar
osku committed
3053
	free_limit = mtr_read_ulint(space_header + FSP_FREE_LIMIT,
3054
				    MLOG_4BYTES, &mtr);
osku's avatar
osku committed
3055 3056 3057
	mtr_commit(&mtr);

	if (size < FSP_EXTENT_SIZE) {
3058 3059 3060 3061 3062
		ut_a(space != 0);	/* This must be a single-table
					tablespace */

		return(0);		/* TODO: count free frag pages and
					return a value based on that */
osku's avatar
osku committed
3063
	}
3064

osku's avatar
osku committed
3065 3066 3067 3068 3069 3070 3071 3072
	/* 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--;
3073 3074
		if (!zip_size) {
			n_free_up -= n_free_up
3075
				/ (UNIV_PAGE_SIZE / FSP_EXTENT_SIZE);
3076 3077
		} else {
			n_free_up -= n_free_up
3078
				/ (zip_size / FSP_EXTENT_SIZE);
3079
		}
osku's avatar
osku committed
3080
	}
3081

osku's avatar
osku committed
3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093
	n_free = n_free_list_ext + n_free_up;

	/* We reserve 1 extent + 0.5 % of the space size to undo logs
	and 1 extent + 0.5 % to cleaning operations; NOTE: this source
	code is duplicated in the function above! */

	reserve = 2 + ((size / FSP_EXTENT_SIZE) * 2) / 200;

	if (reserve > n_free) {
		return(0);
	}

3094
	if (!zip_size) {
3095 3096
		return((ullint) (n_free - reserve)
		       * FSP_EXTENT_SIZE
3097
		       * (UNIV_PAGE_SIZE / 1024));
3098
	} else {
3099 3100
		return((ullint) (n_free - reserve)
		       * FSP_EXTENT_SIZE
3101
		       * (zip_size / 1024));
3102
	}
osku's avatar
osku committed
3103
}
3104

osku's avatar
osku committed
3105 3106 3107 3108 3109 3110 3111 3112 3113
/************************************************************************
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 */
3114 3115
	ulint		zip_size,/* in: compressed page size in bytes
				or 0 for uncompressed pages */
osku's avatar
osku committed
3116 3117 3118 3119 3120 3121 3122
	ulint		page,	/* in: page offset */
	mtr_t*		mtr)	/* in: mtr */
{
	xdes_t*	descr;
	ulint	not_full_n_used;

	ut_ad(seg_inode && mtr);
3123
	ut_ad(!((page_offset(seg_inode) - FSEG_ARR_OFFSET) % FSEG_INODE_SIZE));
3124

3125
	descr = xdes_get_descriptor(space, zip_size, page, mtr);
3126

3127 3128
	ut_ad(mtr_read_ulint(seg_inode + FSEG_ID, MLOG_4BYTES, mtr)
	      == mtr_read_ulint(descr + XDES_ID, MLOG_4BYTES, mtr));
osku's avatar
osku committed
3129 3130 3131 3132 3133

	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,
3134
			    mtr);
osku's avatar
osku committed
3135
		flst_add_last(seg_inode + FSEG_NOT_FULL,
3136
			      descr + XDES_FLST_NODE, mtr);
osku's avatar
osku committed
3137 3138 3139
	}

	ut_ad(xdes_get_bit(descr, XDES_FREE_BIT, page % FSP_EXTENT_SIZE, mtr)
3140
	      == TRUE);
osku's avatar
osku committed
3141 3142 3143 3144
	/* 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,
3145
					 MLOG_4BYTES, mtr);
osku's avatar
osku committed
3146 3147
	not_full_n_used++;
	mlog_write_ulint(seg_inode + FSEG_NOT_FULL_N_USED, not_full_n_used,
3148
			 MLOG_4BYTES, mtr);
osku's avatar
osku committed
3149 3150 3151 3152
	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,
3153
			    descr + XDES_FLST_NODE, mtr);
osku's avatar
osku committed
3154
		flst_add_last(seg_inode + FSEG_FULL,
3155
			      descr + XDES_FLST_NODE, mtr);
3156

osku's avatar
osku committed
3157
		mlog_write_ulint(seg_inode + FSEG_NOT_FULL_N_USED,
3158 3159
				 not_full_n_used - FSP_EXTENT_SIZE,
				 MLOG_4BYTES, mtr);
osku's avatar
osku committed
3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170
	}
}

/**************************************************************************
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 */
3171 3172
	ulint		zip_size,/* in: compressed page size in bytes
				or 0 for uncompressed pages */
osku's avatar
osku committed
3173 3174 3175 3176 3177 3178 3179 3180 3181
	ulint		page,	/* in: page offset */
	mtr_t*		mtr)	/* in: mtr handle */
{
	xdes_t*	descr;
	ulint	not_full_n_used;
	ulint	state;
	dulint	descr_id;
	dulint	seg_id;
	ulint	i;
3182

osku's avatar
osku committed
3183
	ut_ad(seg_inode && mtr);
3184 3185 3186
	ut_ad(mach_read_from_4(seg_inode + FSEG_MAGIC_N)
	      == FSEG_MAGIC_N_VALUE);
	ut_ad(!((page_offset(seg_inode) - FSEG_ARR_OFFSET) % FSEG_INODE_SIZE));
osku's avatar
osku committed
3187 3188 3189 3190

	/* Drop search system page hash index if the page is found in
	the pool and is hashed */

3191
	btr_search_drop_page_hash_when_freed(space, zip_size, page);
osku's avatar
osku committed
3192

3193
	descr = xdes_get_descriptor(space, zip_size, page, mtr);
osku's avatar
osku committed
3194 3195 3196 3197

	ut_a(descr);
	if (xdes_get_bit(descr, XDES_FREE_BIT, page % FSP_EXTENT_SIZE, mtr)) {
		fputs("InnoDB: Dump of the tablespace extent descriptor: ",
3198
		      stderr);
osku's avatar
osku committed
3199 3200 3201
		ut_print_buf(stderr, descr, 40);

		fprintf(stderr, "\n"
3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214
			"InnoDB: Serious error! InnoDB is trying to"
			" free page %lu\n"
			"InnoDB: though it is already marked as free"
			" in the tablespace!\n"
			"InnoDB: The tablespace free space info is corrupt.\n"
			"InnoDB: You may need to dump your"
			" InnoDB tables and recreate the whole\n"
			"InnoDB: database!\n", (ulong) page);
crash:
		fputs("InnoDB: Please refer to\n"
		      "InnoDB: http://dev.mysql.com/doc/refman/5.1/en/"
		      "forcing-recovery.html\n"
		      "InnoDB: about forcing recovery.\n", stderr);
osku's avatar
osku committed
3215 3216
		ut_error;
	}
3217

osku's avatar
osku committed
3218 3219 3220 3221 3222 3223 3224
	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)
3225
			    == page) {
osku's avatar
osku committed
3226 3227

				fseg_set_nth_frag_page_no(seg_inode, i,
3228
							  FIL_NULL, mtr);
osku's avatar
osku committed
3229 3230 3231 3232
				break;
			}
		}

3233
		fsp_free_page(space, zip_size, page, mtr);
3234

osku's avatar
osku committed
3235 3236 3237
		return;
	}

3238
	/* If we get here, the page is in some extent of the segment */
osku's avatar
osku committed
3239 3240 3241

	descr_id = mtr_read_dulint(descr + XDES_ID, mtr);
	seg_id = mtr_read_dulint(seg_inode + FSEG_ID, mtr);
3242
#if 0
osku's avatar
osku committed
3243
	fprintf(stderr,
3244 3245 3246 3247 3248 3249 3250 3251 3252
		"InnoDB: InnoDB is freeing space %lu page %lu,\n"
		"InnoDB: which belongs to descr seg %lu %lu\n"
		"InnoDB: segment %lu %lu.\n",
		(ulong) space, (ulong) page,
		(ulong) ut_dulint_get_high(descr_id),
		(ulong) ut_dulint_get_low(descr_id),
		(ulong) ut_dulint_get_high(seg_id),
		(ulong) ut_dulint_get_low(seg_id));
#endif /* 0 */
osku's avatar
osku committed
3253 3254
	if (0 != ut_dulint_cmp(descr_id, seg_id)) {
		fputs("InnoDB: Dump of the tablespace extent descriptor: ",
3255
		      stderr);
osku's avatar
osku committed
3256 3257 3258 3259 3260
		ut_print_buf(stderr, descr, 40);
		fputs("\nInnoDB: Dump of the segment inode: ", stderr);
		ut_print_buf(stderr, seg_inode, 40);
		putc('\n', stderr);

3261
		fprintf(stderr,
3262 3263 3264 3265 3266 3267 3268 3269 3270 3271
			"InnoDB: Serious error: InnoDB is trying to"
			" free space %lu page %lu,\n"
			"InnoDB: which does not belong to"
			" segment %lu %lu but belongs\n"
			"InnoDB: to segment %lu %lu.\n",
			(ulong) space, (ulong) page,
			(ulong) ut_dulint_get_high(descr_id),
			(ulong) ut_dulint_get_low(descr_id),
			(ulong) ut_dulint_get_high(seg_id),
			(ulong) ut_dulint_get_low(seg_id));
osku's avatar
osku committed
3272 3273 3274 3275
		goto crash;
	}

	not_full_n_used = mtr_read_ulint(seg_inode + FSEG_NOT_FULL_N_USED,
3276
					 MLOG_4BYTES, mtr);
osku's avatar
osku committed
3277 3278 3279
	if (xdes_is_full(descr, mtr)) {
		/* The fragment is full: move it to another list */
		flst_remove(seg_inode + FSEG_FULL,
3280
			    descr + XDES_FLST_NODE, mtr);
osku's avatar
osku committed
3281
		flst_add_last(seg_inode + FSEG_NOT_FULL,
3282
			      descr + XDES_FLST_NODE, mtr);
osku's avatar
osku committed
3283
		mlog_write_ulint(seg_inode + FSEG_NOT_FULL_N_USED,
3284 3285
				 not_full_n_used + FSP_EXTENT_SIZE - 1,
				 MLOG_4BYTES, mtr);
osku's avatar
osku committed
3286 3287 3288
	} else {
		ut_a(not_full_n_used > 0);
		mlog_write_ulint(seg_inode + FSEG_NOT_FULL_N_USED,
3289
				 not_full_n_used - 1, MLOG_4BYTES, mtr);
osku's avatar
osku committed
3290 3291 3292 3293 3294 3295
	}

	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)) {
3296
		/* The extent has become free: free it to space */
osku's avatar
osku committed
3297
		flst_remove(seg_inode + FSEG_NOT_FULL,
3298
			    descr + XDES_FLST_NODE, mtr);
3299
		fsp_free_extent(space, zip_size, page, mtr);
3300
	}
osku's avatar
osku committed
3301 3302 3303 3304
}

/**************************************************************************
Frees a single page of a segment. */
3305
UNIV_INTERN
osku's avatar
osku committed
3306 3307 3308 3309 3310 3311 3312 3313
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 */
{
3314
	ulint		flags;
3315
	ulint		zip_size;
osku's avatar
osku committed
3316
	fseg_inode_t*	seg_inode;
3317 3318 3319 3320
	rw_lock_t*	latch;

	latch = fil_space_get_latch(space, &flags);
	zip_size = dict_table_flags_to_zip_size(flags);
osku's avatar
osku committed
3321 3322

	ut_ad(!mutex_own(&kernel_mutex)
3323
	      || mtr_memo_contains(mtr, latch, MTR_MEMO_X_LOCK));
3324

3325
	mtr_x_lock(latch, mtr);
osku's avatar
osku committed
3326

3327
	seg_inode = fseg_inode_get(seg_header, space, zip_size, mtr);
osku's avatar
osku committed
3328

3329
	fseg_free_page_low(seg_inode, space, zip_size, page, mtr);
osku's avatar
osku committed
3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343

#ifdef UNIV_DEBUG_FILE_ACCESSES
	buf_page_set_file_page_was_freed(space, page);
#endif
}

/**************************************************************************
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 */
3344 3345
	ulint		zip_size,/* in: compressed page size in bytes
				or 0 for uncompressed pages */
osku's avatar
osku committed
3346 3347 3348 3349 3350 3351 3352 3353
	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;
3354

osku's avatar
osku committed
3355 3356
	ut_ad(seg_inode && mtr);

3357
	descr = xdes_get_descriptor(space, zip_size, page, mtr);
osku's avatar
osku committed
3358 3359

	ut_a(xdes_get_state(descr, mtr) == XDES_FSEG);
3360 3361
	ut_a(0 == ut_dulint_cmp(mtr_read_dulint(descr + XDES_ID, mtr),
				mtr_read_dulint(seg_inode + FSEG_ID, mtr)));
osku's avatar
osku committed
3362 3363

	first_page_in_extent = page - (page % FSP_EXTENT_SIZE);
3364

osku's avatar
osku committed
3365 3366 3367 3368 3369 3370
	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 */

3371
			btr_search_drop_page_hash_when_freed(
3372
				space, zip_size, first_page_in_extent + i);
osku's avatar
osku committed
3373 3374 3375 3376 3377
		}
	}

	if (xdes_is_full(descr, mtr)) {
		flst_remove(seg_inode + FSEG_FULL,
3378
			    descr + XDES_FLST_NODE, mtr);
osku's avatar
osku committed
3379 3380
	} else if (xdes_is_free(descr, mtr)) {
		flst_remove(seg_inode + FSEG_FREE,
3381
			    descr + XDES_FLST_NODE, mtr);
osku's avatar
osku committed
3382 3383
	} else {
		flst_remove(seg_inode + FSEG_NOT_FULL,
3384
			    descr + XDES_FLST_NODE, mtr);
osku's avatar
osku committed
3385

3386 3387
		not_full_n_used = mtr_read_ulint(
			seg_inode + FSEG_NOT_FULL_N_USED, MLOG_4BYTES, mtr);
osku's avatar
osku committed
3388 3389 3390 3391

		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,
3392 3393
				 not_full_n_used - descr_n_used,
				 MLOG_4BYTES, mtr);
osku's avatar
osku committed
3394 3395
	}

3396
	fsp_free_extent(space, zip_size, page, mtr);
osku's avatar
osku committed
3397 3398 3399 3400

#ifdef UNIV_DEBUG_FILE_ACCESSES
	for (i = 0; i < FSP_EXTENT_SIZE; i++) {

3401
		buf_page_set_file_page_was_freed(space,
3402
						 first_page_in_extent + i);
osku's avatar
osku committed
3403 3404 3405 3406 3407 3408 3409 3410 3411
	}
#endif
}

/**************************************************************************
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. */
3412
UNIV_INTERN
osku's avatar
osku committed
3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427
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;
3428
	ulint		flags;
3429
	ulint		zip_size;
3430
	ulint		header_page;
3431
	rw_lock_t*	latch;
osku's avatar
osku committed
3432

3433 3434
	space = page_get_space_id(page_align(header));
	header_page = page_get_page_no(page_align(header));
osku's avatar
osku committed
3435

3436 3437 3438
	latch = fil_space_get_latch(space, &flags);
	zip_size = dict_table_flags_to_zip_size(flags);

osku's avatar
osku committed
3439
	ut_ad(!mutex_own(&kernel_mutex)
3440
	      || mtr_memo_contains(mtr, latch, MTR_MEMO_X_LOCK));
3441

3442
	mtr_x_lock(latch, mtr);
osku's avatar
osku committed
3443

3444
	descr = xdes_get_descriptor(space, zip_size, header_page, mtr);
osku's avatar
osku committed
3445 3446 3447 3448 3449

	/* Check that the header resides on a page which has not been
	freed yet */

	ut_a(descr);
3450 3451
	ut_a(xdes_get_bit(descr, XDES_FREE_BIT,
			  header_page % FSP_EXTENT_SIZE, mtr) == FALSE);
3452
	inode = fseg_inode_get(header, space, zip_size, mtr);
osku's avatar
osku committed
3453

3454
	descr = fseg_get_first_extent(inode, space, zip_size, mtr);
osku's avatar
osku committed
3455 3456 3457 3458 3459

	if (descr != NULL) {
		/* Free the extent held by the segment */
		page = xdes_get_offset(descr);

3460
		fseg_free_extent(inode, space, zip_size, page, mtr);
3461

osku's avatar
osku committed
3462 3463 3464 3465 3466 3467 3468 3469
		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 */
3470
		fsp_free_seg_inode(space, zip_size, inode, mtr);
osku's avatar
osku committed
3471 3472 3473 3474

		return(TRUE);
	}

3475
	fseg_free_page_low(inode, space, zip_size,
3476
			   fseg_get_nth_frag_page_no(inode, n, mtr), mtr);
osku's avatar
osku committed
3477 3478 3479 3480 3481

	n = fseg_find_last_used_frag_page_slot(inode, mtr);

	if (n == ULINT_UNDEFINED) {
		/* Freeing completed: free the segment inode */
3482
		fsp_free_seg_inode(space, zip_size, inode, mtr);
osku's avatar
osku committed
3483 3484 3485 3486 3487 3488 3489 3490 3491 3492

		return(TRUE);
	}

	return(FALSE);
}

/**************************************************************************
Frees part of a segment. Differs from fseg_free_step because this function
leaves the header page unfreed. */
3493
UNIV_INTERN
osku's avatar
osku committed
3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507
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;
3508
	ulint		flags;
3509
	ulint		zip_size;
osku's avatar
osku committed
3510
	ulint		page_no;
3511
	rw_lock_t*	latch;
osku's avatar
osku committed
3512

3513
	space = page_get_space_id(page_align(header));
3514

3515 3516 3517
	latch = fil_space_get_latch(space, &flags);
	zip_size = dict_table_flags_to_zip_size(flags);

osku's avatar
osku committed
3518
	ut_ad(!mutex_own(&kernel_mutex)
3519
	      || mtr_memo_contains(mtr, latch, MTR_MEMO_X_LOCK));
3520

3521
	mtr_x_lock(latch, mtr);
osku's avatar
osku committed
3522

3523
	inode = fseg_inode_get(header, space, zip_size, mtr);
osku's avatar
osku committed
3524

3525
	descr = fseg_get_first_extent(inode, space, zip_size, mtr);
osku's avatar
osku committed
3526 3527 3528 3529 3530

	if (descr != NULL) {
		/* Free the extent held by the segment */
		page = xdes_get_offset(descr);

3531
		fseg_free_extent(inode, space, zip_size, page, mtr);
3532

osku's avatar
osku committed
3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544
		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);
3545

3546
	if (page_no == page_get_page_no(page_align(header))) {
osku's avatar
osku committed
3547 3548 3549

		return(TRUE);
	}
3550

3551
	fseg_free_page_low(inode, space, zip_size, page_no, mtr);
osku's avatar
osku committed
3552 3553 3554 3555 3556 3557 3558

	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. */
3559
UNIV_INTERN
osku's avatar
osku committed
3560 3561 3562 3563
void
fseg_free(
/*======*/
	ulint	space,	/* in: space id */
3564 3565
	ulint	zip_size,/* in: compressed page size in bytes
			or 0 for uncompressed pages */
osku's avatar
osku committed
3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581
	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);

3582
		header = fut_get_ptr(space, zip_size, addr, RW_X_LATCH, &mtr);
osku's avatar
osku committed
3583 3584

		finished = fseg_free_step(header, &mtr);
3585

osku's avatar
osku committed
3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605
		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 */
3606 3607 3608
	ulint		space,	/* in: space id */
	ulint		zip_size,/* in: compressed page size in bytes
				or 0 for uncompressed pages */
osku's avatar
osku committed
3609 3610 3611 3612
	mtr_t*		mtr)	/* in: mtr */
{
	fil_addr_t	first;
	xdes_t*		descr;
3613

osku's avatar
osku committed
3614 3615
	ut_ad(inode && mtr);

3616
	ut_ad(space == page_get_space_id(page_align(inode)));
osku's avatar
osku committed
3617 3618

	first = fil_addr_null;
3619

osku's avatar
osku committed
3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636
	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);
	}
3637
	descr = xdes_lst_get_descriptor(space, zip_size, first, mtr);
osku's avatar
osku committed
3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 3658

	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;
3659

3660
	ut_ad(mtr_memo_contains_page(mtr2, inode, MTR_MEMO_PAGE_X_FIX));
3661
	ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE);
osku's avatar
osku committed
3662

3663
	space = page_get_space_id(page_align(inode));
3664 3665

	seg_id = mtr_read_dulint(inode + FSEG_ID, mtr2);
osku's avatar
osku committed
3666
	n_used = mtr_read_ulint(inode + FSEG_NOT_FULL_N_USED,
3667
				MLOG_4BYTES, mtr2);
osku's avatar
osku committed
3668 3669 3670 3671 3672 3673 3674 3675
	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)) {
3676
		ulint	flags;
3677 3678
		ulint	zip_size;

osku's avatar
osku committed
3679
		mtr_start(&mtr);
3680 3681
		mtr_x_lock(fil_space_get_latch(space, &flags), &mtr);
		zip_size = dict_table_flags_to_zip_size(flags);
osku's avatar
osku committed
3682

3683 3684
		descr = xdes_lst_get_descriptor(space, zip_size,
						node_addr, &mtr);
osku's avatar
osku committed
3685 3686 3687

		ut_a(xdes_get_n_used(descr, &mtr) == 0);
		ut_a(xdes_get_state(descr, &mtr) == XDES_FSEG);
3688 3689
		ut_a(!ut_dulint_cmp(mtr_read_dulint(descr + XDES_ID, &mtr),
				    seg_id));
osku's avatar
osku committed
3690 3691 3692 3693 3694 3695 3696 3697 3698 3699

		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)) {
3700
		ulint	flags;
3701 3702
		ulint	zip_size;

osku's avatar
osku committed
3703
		mtr_start(&mtr);
3704 3705
		mtr_x_lock(fil_space_get_latch(space, &flags), &mtr);
		zip_size = dict_table_flags_to_zip_size(flags);
osku's avatar
osku committed
3706

3707 3708
		descr = xdes_lst_get_descriptor(space, zip_size,
						node_addr, &mtr);
osku's avatar
osku committed
3709 3710 3711 3712

		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);
3713 3714
		ut_a(!ut_dulint_cmp(mtr_read_dulint(descr + XDES_ID, &mtr),
				    seg_id));
osku's avatar
osku committed
3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726

		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)) {
3727
		ulint	flags;
3728 3729
		ulint	zip_size;

osku's avatar
osku committed
3730
		mtr_start(&mtr);
3731 3732
		mtr_x_lock(fil_space_get_latch(space, &flags), &mtr);
		zip_size = dict_table_flags_to_zip_size(flags);
osku's avatar
osku committed
3733

3734 3735
		descr = xdes_lst_get_descriptor(space, zip_size,
						node_addr, &mtr);
osku's avatar
osku committed
3736 3737 3738

		ut_a(xdes_get_n_used(descr, &mtr) == FSP_EXTENT_SIZE);
		ut_a(xdes_get_state(descr, &mtr) == XDES_FSEG);
3739 3740
		ut_a(!ut_dulint_cmp(mtr_read_dulint(descr + XDES_ID, &mtr),
				    seg_id));
osku's avatar
osku committed
3741 3742 3743 3744 3745 3746 3747 3748 3749

		node_addr = flst_get_next_addr(descr + XDES_FLST_NODE, &mtr);
		mtr_commit(&mtr);
	}

	ut_a(n_used == n_used2);

	return(TRUE);
}
3750

osku's avatar
osku committed
3751 3752
/***********************************************************************
Validates a segment. */
3753
UNIV_INTERN
osku's avatar
osku committed
3754 3755 3756 3757 3758
ibool
fseg_validate(
/*==========*/
				/* out: TRUE if ok */
	fseg_header_t*	header, /* in: segment header */
3759
	mtr_t*		mtr)	/* in: mtr */
osku's avatar
osku committed
3760 3761 3762 3763
{
	fseg_inode_t*	inode;
	ibool		ret;
	ulint		space;
3764
	ulint		flags;
3765
	ulint		zip_size;
osku's avatar
osku committed
3766

3767
	space = page_get_space_id(page_align(header));
osku's avatar
osku committed
3768

3769 3770
	mtr_x_lock(fil_space_get_latch(space, &flags), mtr);
	zip_size = dict_table_flags_to_zip_size(flags);
osku's avatar
osku committed
3771

3772
	inode = fseg_inode_get(header, space, zip_size, mtr);
osku's avatar
osku committed
3773

3774
	ret = fseg_validate_low(inode, mtr);
osku's avatar
osku committed
3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798

	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;
3799 3800
	dulint	 d_var;

3801
	ut_ad(mtr_memo_contains_page(mtr, inode, MTR_MEMO_PAGE_X_FIX));
3802 3803
	space = page_get_space_id(page_align(inode));
	page_no = page_get_page_no(page_align(inode));
osku's avatar
osku committed
3804 3805 3806

	reserved = fseg_n_reserved_pages_low(inode, &used, mtr);

3807
	d_var = mtr_read_dulint(inode + FSEG_ID, mtr);
osku's avatar
osku committed
3808 3809 3810

	seg_id_low = ut_dulint_get_low(d_var);
	seg_id_high = ut_dulint_get_high(d_var);
3811

osku's avatar
osku committed
3812
	n_used = mtr_read_ulint(inode + FSEG_NOT_FULL_N_USED,
3813
				MLOG_4BYTES, mtr);
osku's avatar
osku committed
3814 3815 3816 3817 3818 3819
	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);

	fprintf(stderr,
3820 3821 3822 3823 3824 3825
		"SEGMENT id %lu %lu space %lu; page %lu;"
		" res %lu used %lu; full ext %lu\n"
		"fragm pages %lu; free extents %lu;"
		" not full extents %lu: pages %lu\n",
		(ulong) seg_id_high, (ulong) seg_id_low,
		(ulong) space, (ulong) page_no,
osku's avatar
osku committed
3826 3827
		(ulong) reserved, (ulong) used, (ulong) n_full,
		(ulong) n_frag, (ulong) n_free, (ulong) n_not_full,
3828
		(ulong) n_used);
osku's avatar
osku committed
3829 3830
}

3831
#ifdef UNIV_BTR_PRINT
osku's avatar
osku committed
3832 3833
/***********************************************************************
Writes info of a segment. */
3834
UNIV_INTERN
osku's avatar
osku committed
3835 3836 3837 3838 3839 3840 3841 3842
void
fseg_print(
/*=======*/
	fseg_header_t*	header, /* in: segment header */
	mtr_t*		mtr)	/* in: mtr */
{
	fseg_inode_t*	inode;
	ulint		space;
3843
	ulint		flags;
3844
	ulint		zip_size;
osku's avatar
osku committed
3845

3846
	space = page_get_space_id(page_align(header));
osku's avatar
osku committed
3847

3848 3849
	mtr_x_lock(fil_space_get_latch(space, &flags), mtr);
	zip_size = dict_table_flags_to_zip_size(flags);
osku's avatar
osku committed
3850

3851
	inode = fseg_inode_get(header, space, zip_size, mtr);
osku's avatar
osku committed
3852 3853 3854

	fseg_print_low(inode, mtr);
}
3855
#endif /* UNIV_BTR_PRINT */
osku's avatar
osku committed
3856 3857 3858

/***********************************************************************
Validates the file space system and its segments. */
3859
UNIV_INTERN
osku's avatar
osku committed
3860 3861 3862 3863 3864 3865 3866 3867 3868
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;
3869
	rw_lock_t*	latch;
osku's avatar
osku committed
3870
	ulint		size;
3871
	ulint		flags;
3872
	ulint		zip_size;
osku's avatar
osku committed
3873 3874 3875 3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886
	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;
3887

3888 3889
	latch = fil_space_get_latch(space, &flags);
	zip_size = dict_table_flags_to_zip_size(flags);
3890 3891 3892 3893
	ut_a(ut_is_2pow(zip_size));
	ut_a(zip_size <= UNIV_PAGE_SIZE);
	ut_a(!zip_size || zip_size >= PAGE_ZIP_MIN_SIZE);

osku's avatar
osku committed
3894 3895 3896
	/* Start first a mini-transaction mtr2 to lock out all other threads
	from the fsp system */
	mtr_start(&mtr2);
3897
	mtr_x_lock(latch, &mtr2);
3898

osku's avatar
osku committed
3899
	mtr_start(&mtr);
3900
	mtr_x_lock(latch, &mtr);
osku's avatar
osku committed
3901

3902
	header = fsp_get_space_header(space, zip_size, &mtr);
3903

3904
	size = mtr_read_ulint(header + FSP_SIZE, MLOG_4BYTES, &mtr);
osku's avatar
osku committed
3905
	free_limit = mtr_read_ulint(header + FSP_FREE_LIMIT,
3906
				    MLOG_4BYTES, &mtr);
osku's avatar
osku committed
3907
	frag_n_used = mtr_read_ulint(header + FSP_FRAG_N_USED,
3908
				     MLOG_4BYTES, &mtr);
osku's avatar
osku committed
3909

3910 3911
	n_full_frag_pages = FSP_EXTENT_SIZE
		* flst_get_len(header + FSP_FULL_FRAG, &mtr);
3912

3913 3914 3915 3916 3917
	if (UNIV_UNLIKELY(free_limit > size)) {

		ut_a(space != 0);
		ut_a(size < FSP_EXTENT_SIZE);
	}
3918

osku's avatar
osku committed
3919 3920 3921 3922 3923 3924 3925 3926
	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);
3927
	mtr_x_lock(latch, &mtr);
osku's avatar
osku committed
3928

3929
	header = fsp_get_space_header(space, zip_size, &mtr);
osku's avatar
osku committed
3930 3931 3932 3933 3934 3935
	node_addr = flst_get_first(header + FSP_FREE, &mtr);

	mtr_commit(&mtr);

	while (!fil_addr_is_null(node_addr)) {
		mtr_start(&mtr);
3936
		mtr_x_lock(latch, &mtr);
osku's avatar
osku committed
3937 3938

		descr_count++;
3939 3940
		descr = xdes_lst_get_descriptor(space, zip_size,
						node_addr, &mtr);
osku's avatar
osku committed
3941 3942 3943 3944 3945 3946 3947 3948 3949 3950

		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);
3951
	mtr_x_lock(latch, &mtr);
osku's avatar
osku committed
3952

3953
	header = fsp_get_space_header(space, zip_size, &mtr);
osku's avatar
osku committed
3954 3955 3956 3957 3958 3959
	node_addr = flst_get_first(header + FSP_FREE_FRAG, &mtr);

	mtr_commit(&mtr);

	while (!fil_addr_is_null(node_addr)) {
		mtr_start(&mtr);
3960
		mtr_x_lock(latch, &mtr);
osku's avatar
osku committed
3961 3962

		descr_count++;
3963 3964
		descr = xdes_lst_get_descriptor(space, zip_size,
						node_addr, &mtr);
osku's avatar
osku committed
3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977

		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);
3978
	mtr_x_lock(latch, &mtr);
osku's avatar
osku committed
3979

3980
	header = fsp_get_space_header(space, zip_size, &mtr);
osku's avatar
osku committed
3981 3982 3983 3984 3985 3986
	node_addr = flst_get_first(header + FSP_FULL_FRAG, &mtr);

	mtr_commit(&mtr);

	while (!fil_addr_is_null(node_addr)) {
		mtr_start(&mtr);
3987
		mtr_x_lock(latch, &mtr);
osku's avatar
osku committed
3988 3989

		descr_count++;
3990 3991
		descr = xdes_lst_get_descriptor(space, zip_size,
						node_addr, &mtr);
osku's avatar
osku committed
3992 3993 3994 3995 3996 3997 3998

		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);
	}
3999

osku's avatar
osku committed
4000 4001
	/* Validate segments */
	mtr_start(&mtr);
4002
	mtr_x_lock(latch, &mtr);
osku's avatar
osku committed
4003

4004
	header = fsp_get_space_header(space, zip_size, &mtr);
osku's avatar
osku committed
4005

4006
	node_addr = flst_get_first(header + FSP_SEG_INODES_FULL, &mtr);
osku's avatar
osku committed
4007 4008 4009 4010 4011 4012 4013

	seg_inode_len_full = flst_get_len(header + FSP_SEG_INODES_FULL, &mtr);

	mtr_commit(&mtr);

	while (!fil_addr_is_null(node_addr)) {

4014 4015
		n = 0;
		do {
4016
			mtr_start(&mtr);
4017
			mtr_x_lock(latch, &mtr);
osku's avatar
osku committed
4018

4019
			seg_inode_page = fut_get_ptr(
4020
				space, zip_size, node_addr, RW_X_LATCH, &mtr)
4021
				- FSEG_INODE_PAGE_NODE;
osku's avatar
osku committed
4022

4023 4024
			seg_inode = fsp_seg_inode_page_get_nth_inode(
				seg_inode_page, n, zip_size, &mtr);
4025 4026
			ut_a(!ut_dulint_is_zero(
				     mach_read_from_8(seg_inode + FSEG_ID)));
4027
			fseg_validate_low(seg_inode, &mtr);
osku's avatar
osku committed
4028

4029
			descr_count += flst_get_len(seg_inode + FSEG_FREE,
4030
						    &mtr);
4031
			descr_count += flst_get_len(seg_inode + FSEG_FULL,
4032
						    &mtr);
4033
			descr_count += flst_get_len(seg_inode + FSEG_NOT_FULL,
4034
						    &mtr);
osku's avatar
osku committed
4035

4036
			n_used2 += fseg_get_n_frag_pages(seg_inode, &mtr);
osku's avatar
osku committed
4037

4038 4039
			next_node_addr = flst_get_next_addr(
				seg_inode_page + FSEG_INODE_PAGE_NODE, &mtr);
4040
			mtr_commit(&mtr);
4041
		} while (++n < FSP_SEG_INODES_PER_PAGE(zip_size));
osku's avatar
osku committed
4042

4043
		node_addr = next_node_addr;
osku's avatar
osku committed
4044 4045 4046
	}

	mtr_start(&mtr);
4047
	mtr_x_lock(latch, &mtr);
osku's avatar
osku committed
4048

4049
	header = fsp_get_space_header(space, zip_size, &mtr);
osku's avatar
osku committed
4050 4051 4052 4053

	node_addr = flst_get_first(header + FSP_SEG_INODES_FREE, &mtr);

	seg_inode_len_free = flst_get_len(header + FSP_SEG_INODES_FREE, &mtr);
4054

osku's avatar
osku committed
4055 4056 4057 4058
	mtr_commit(&mtr);

	while (!fil_addr_is_null(node_addr)) {

4059
		n = 0;
4060

4061
		do {
4062
			mtr_start(&mtr);
4063
			mtr_x_lock(latch, &mtr);
4064

4065
			seg_inode_page = fut_get_ptr(
4066
				space, zip_size, node_addr, RW_X_LATCH, &mtr)
4067
				- FSEG_INODE_PAGE_NODE;
4068

4069 4070
			seg_inode = fsp_seg_inode_page_get_nth_inode(
				seg_inode_page, n, zip_size, &mtr);
4071 4072
			if (!ut_dulint_is_zero(
				    mach_read_from_8(seg_inode + FSEG_ID))) {
4073 4074
				fseg_validate_low(seg_inode, &mtr);

4075 4076 4077 4078 4079 4080 4081 4082
				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);
4083
			}
osku's avatar
osku committed
4084

4085 4086
			next_node_addr = flst_get_next_addr(
				seg_inode_page + FSEG_INODE_PAGE_NODE, &mtr);
4087
			mtr_commit(&mtr);
4088
		} while (++n < FSP_SEG_INODES_PER_PAGE(zip_size));
osku's avatar
osku committed
4089

4090
		node_addr = next_node_addr;
osku's avatar
osku committed
4091
	}
4092

osku's avatar
osku committed
4093
	ut_a(descr_count * FSP_EXTENT_SIZE == free_limit);
4094 4095
	if (!zip_size) {
		ut_a(n_used + n_full_frag_pages
4096 4097 4098
		     == n_used2 + 2 * ((free_limit + (UNIV_PAGE_SIZE - 1))
				       / UNIV_PAGE_SIZE)
		     + seg_inode_len_full + seg_inode_len_free);
4099 4100
	} else {
		ut_a(n_used + n_full_frag_pages
4101 4102 4103
		     == n_used2 + 2 * ((free_limit + (zip_size - 1))
				       / zip_size)
		     + seg_inode_len_full + seg_inode_len_free);
4104
	}
osku's avatar
osku committed
4105 4106 4107
	ut_a(frag_n_used == n_used);

	mtr_commit(&mtr2);
4108

osku's avatar
osku committed
4109 4110 4111 4112 4113
	return(TRUE);
}

/***********************************************************************
Prints info of a file space. */
4114
UNIV_INTERN
osku's avatar
osku committed
4115 4116 4117 4118 4119 4120 4121 4122
void
fsp_print(
/*======*/
	ulint	space)	/* in: space id */
{
	fsp_header_t*	header;
	fseg_inode_t*	seg_inode;
	page_t*		seg_inode_page;
4123
	rw_lock_t*	latch;
4124
	ulint		flags;
4125
	ulint		zip_size;
osku's avatar
osku committed
4126 4127 4128 4129 4130 4131 4132 4133 4134 4135 4136 4137
	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;
4138
	dulint		d_var;
osku's avatar
osku committed
4139 4140
	mtr_t		mtr;
	mtr_t		mtr2;
4141

4142 4143
	latch = fil_space_get_latch(space, &flags);
	zip_size = dict_table_flags_to_zip_size(flags);
4144

osku's avatar
osku committed
4145 4146 4147 4148 4149
	/* Start first a mini-transaction mtr2 to lock out all other threads
	from the fsp system */

	mtr_start(&mtr2);

4150
	mtr_x_lock(latch, &mtr2);
osku's avatar
osku committed
4151 4152 4153

	mtr_start(&mtr);

4154
	mtr_x_lock(latch, &mtr);
4155

4156
	header = fsp_get_space_header(space, zip_size, &mtr);
osku's avatar
osku committed
4157

4158
	size = mtr_read_ulint(header + FSP_SIZE, MLOG_4BYTES, &mtr);
osku's avatar
osku committed
4159 4160

	free_limit = mtr_read_ulint(header + FSP_FREE_LIMIT, MLOG_4BYTES,
4161
				    &mtr);
osku's avatar
osku committed
4162
	frag_n_used = mtr_read_ulint(header + FSP_FRAG_N_USED, MLOG_4BYTES,
4163
				     &mtr);
osku's avatar
osku committed
4164 4165 4166 4167 4168 4169 4170 4171 4172 4173
	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);

	d_var = mtr_read_dulint(header + FSP_SEG_ID, &mtr);

	seg_id_low = ut_dulint_get_low(d_var);
	seg_id_high = ut_dulint_get_high(d_var);

	fprintf(stderr,
4174 4175 4176 4177 4178
		"FILE SPACE INFO: id %lu\n"
		"size %lu, free limit %lu, free extents %lu\n"
		"not full frag extents %lu: used pages %lu,"
		" full frag extents %lu\n"
		"first seg id not used %lu %lu\n",
4179
		(ulong) space,
osku's avatar
osku committed
4180 4181 4182 4183
		(ulong) size, (ulong) free_limit, (ulong) n_free,
		(ulong) n_free_frag, (ulong) frag_n_used, (ulong) n_full_frag,
		(ulong) seg_id_high, (ulong) seg_id_low);

4184
	mtr_commit(&mtr);
osku's avatar
osku committed
4185 4186 4187 4188

	/* Print segments */

	mtr_start(&mtr);
4189
	mtr_x_lock(latch, &mtr);
osku's avatar
osku committed
4190

4191
	header = fsp_get_space_header(space, zip_size, &mtr);
osku's avatar
osku committed
4192

4193
	node_addr = flst_get_first(header + FSP_SEG_INODES_FULL, &mtr);
osku's avatar
osku committed
4194 4195 4196 4197 4198

	mtr_commit(&mtr);

	while (!fil_addr_is_null(node_addr)) {

4199 4200 4201
		n = 0;

		do {
osku's avatar
osku committed
4202

4203
			mtr_start(&mtr);
4204
			mtr_x_lock(latch, &mtr);
osku's avatar
osku committed
4205

4206
			seg_inode_page = fut_get_ptr(
4207
				space, zip_size, node_addr, RW_X_LATCH, &mtr)
4208
				- FSEG_INODE_PAGE_NODE;
osku's avatar
osku committed
4209

4210 4211
			seg_inode = fsp_seg_inode_page_get_nth_inode(
				seg_inode_page, n, zip_size, &mtr);
4212 4213
			ut_a(!ut_dulint_is_zero(
				     mach_read_from_8(seg_inode + FSEG_ID)));
4214
			fseg_print_low(seg_inode, &mtr);
osku's avatar
osku committed
4215

4216 4217
			n_segs++;

4218 4219
			next_node_addr = flst_get_next_addr(
				seg_inode_page + FSEG_INODE_PAGE_NODE, &mtr);
4220
			mtr_commit(&mtr);
4221
		} while (++n < FSP_SEG_INODES_PER_PAGE(zip_size));
osku's avatar
osku committed
4222

4223
		node_addr = next_node_addr;
osku's avatar
osku committed
4224 4225 4226
	}

	mtr_start(&mtr);
4227
	mtr_x_lock(latch, &mtr);
osku's avatar
osku committed
4228

4229
	header = fsp_get_space_header(space, zip_size, &mtr);
osku's avatar
osku committed
4230 4231 4232 4233 4234 4235 4236

	node_addr = flst_get_first(header + FSP_SEG_INODES_FREE, &mtr);

	mtr_commit(&mtr);

	while (!fil_addr_is_null(node_addr)) {

4237 4238 4239
		n = 0;

		do {
osku's avatar
osku committed
4240

4241
			mtr_start(&mtr);
4242
			mtr_x_lock(latch, &mtr);
osku's avatar
osku committed
4243

4244
			seg_inode_page = fut_get_ptr(
4245
				space, zip_size, node_addr, RW_X_LATCH, &mtr)
4246
				- FSEG_INODE_PAGE_NODE;
osku's avatar
osku committed
4247

4248 4249
			seg_inode = fsp_seg_inode_page_get_nth_inode(
				seg_inode_page, n, zip_size, &mtr);
4250 4251
			if (!ut_dulint_is_zero(
				    mach_read_from_8(seg_inode + FSEG_ID))) {
osku's avatar
osku committed
4252

4253 4254 4255
				fseg_print_low(seg_inode, &mtr);
				n_segs++;
			}
osku's avatar
osku committed
4256

4257 4258
			next_node_addr = flst_get_next_addr(
				seg_inode_page + FSEG_INODE_PAGE_NODE, &mtr);
4259
			mtr_commit(&mtr);
4260
		} while (++n < FSP_SEG_INODES_PER_PAGE(zip_size));
osku's avatar
osku committed
4261

4262
		node_addr = next_node_addr;
osku's avatar
osku committed
4263
	}
4264

osku's avatar
osku committed
4265 4266 4267 4268
	mtr_commit(&mtr2);

	fprintf(stderr, "NUMBER of file segments: %lu\n", (ulong) n_segs);
}