fsp0fsp.cc 90.5 KB
Newer Older
1 2
/*****************************************************************************

Sergei Golubchik's avatar
Sergei Golubchik committed
3
Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved.
4
Copyright (c) 2017, 2019, MariaDB Corporation.
5 6 7 8 9 10 11 12 13 14

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; version 2 of the License.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
15
this program; if not, write to the Free Software Foundation, Inc.,
16
51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
17 18 19

*****************************************************************************/

20
/******************************************************************//**
21
@file fsp/fsp0fsp.cc
osku's avatar
osku committed
22 23 24 25 26 27 28 29
File space management

Created 11/29/1995 Heikki Tuuri
***********************************************************************/

#include "fsp0fsp.h"
#include "buf0buf.h"
#include "fil0fil.h"
30
#include "fil0crypt.h"
osku's avatar
osku committed
31 32
#include "mtr0log.h"
#include "ut0byte.h"
33
#include "page0page.h"
34 35 36 37 38 39 40 41 42
#include "fut0fut.h"
#include "srv0srv.h"
#include "srv0start.h"
#include "ibuf0ibuf.h"
#include "btr0btr.h"
#include "btr0sea.h"
#include "dict0boot.h"
#include "log0log.h"
#include "dict0mem.h"
43
#include "fsp0types.h"
osku's avatar
osku committed
44

45 46
// JAN: MySQL 5.7 Encryption
// #include <my_aes.h>
47

48 49 50 51 52
typedef ulint page_no_t;

/** Return an extent to the free list of a space.
@param[in,out]	space		tablespace
@param[in]	offset		page number in the extent
53
@param[in,out]	mtr		mini-transaction */
54
MY_ATTRIBUTE((nonnull))
osku's avatar
osku committed
55 56 57
static
void
fsp_free_extent(
58 59
	fil_space_t*		space,
	page_no_t		offset,
60 61
	mtr_t*			mtr);

62
/********************************************************************//**
osku's avatar
osku committed
63 64
Marks a page used. The page must reside within the extents of the given
segment. */
Sergei Golubchik's avatar
Sergei Golubchik committed
65
static MY_ATTRIBUTE((nonnull))
osku's avatar
osku committed
66 67 68
void
fseg_mark_page_used(
/*================*/
69
	fseg_inode_t*	seg_inode,/*!< in: segment inode */
70
	page_no_t	page,	/*!< in: page offset */
71
	xdes_t*		descr,  /*!< in: extent descriptor */
72
	mtr_t*		mtr);	/*!< in/out: mini-transaction */
73 74 75 76 77

/** 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.
@param[in]	inode		segment inode
78
@param[in]	space		tablespace
79 80
@param[in,out]	mtr		mini-transaction
@return the first extent descriptor, or NULL if none */
81
MY_ATTRIBUTE((nonnull, warn_unused_result))
osku's avatar
osku committed
82 83 84
static
xdes_t*
fseg_get_first_extent(
85
	fseg_inode_t*		inode,
86
	const fil_space_t*	space,
87 88 89 90 91 92 93 94 95 96 97
	mtr_t*			mtr);

/** Put 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.
@param[in]	init_space	true if this is a single-table tablespace
and we are only initializing the first extent and the first bitmap pages;
then we will not allocate more extents
@param[in,out]	space		tablespace
@param[in,out]	header		tablespace header
@param[in,out]	mtr		mini-transaction */
98
static ATTRIBUTE_COLD
osku's avatar
osku committed
99 100
void
fsp_fill_free_list(
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
	bool		init_space,
	fil_space_t*	space,
	fsp_header_t*	header,
	mtr_t*		mtr);

/** Allocates a single free page from a segment.
This function implements the intelligent allocation strategy which tries
to minimize file space fragmentation.
@param[in,out]	space			tablespace
@param[in,out]	seg_inode		segment inode
@param[in]	hint			hint of which page would be desirable
@param[in]	direction		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
@param[in]	rw_latch		RW_SX_LATCH, RW_X_LATCH
@param[in,out]	mtr			mini-transaction
@param[in,out]	init_mtr		mtr or another mini-transaction in
which the page should be initialized. If init_mtr != mtr, but the page is
already latched in mtr, do not initialize the page
@param[in]	has_done_reservation	TRUE if the space has already been
reserved, in this case we will never return NULL
@retval NULL	if no page could be allocated
@retval block	rw_lock_x_lock_count(&block->lock) == 1 if allocation succeeded
124
(init_mtr == mtr, or the page was not previously freed in mtr)
125
@retval block	(not allocated or initialized) otherwise */
osku's avatar
osku committed
126
static
127
buf_block_t*
osku's avatar
osku committed
128
fseg_alloc_free_page_low(
129 130 131 132 133 134 135 136 137 138 139
	fil_space_t*		space,
	fseg_inode_t*		seg_inode,
	ulint			hint,
	byte			direction,
	rw_lock_type_t		rw_latch,
	mtr_t*			mtr,
	mtr_t*			init_mtr
#ifdef UNIV_DEBUG
	, ibool			has_done_reservation
#endif /* UNIV_DEBUG */
)
140
	MY_ATTRIBUTE((warn_unused_result));
141 142

/** Gets a pointer to the space header and x-locks its page.
143
@param[in]	space		tablespace
144 145
@param[in,out]	mtr		mini-transaction
@return pointer to the space header, page x-locked */
146
inline fsp_header_t* fsp_get_space_header(const fil_space_t* space, mtr_t* mtr)
osku's avatar
osku committed
147
{
148
	buf_block_t*	block;
osku's avatar
osku committed
149 150
	fsp_header_t*	header;

151
	ut_ad(space->purpose != FIL_TYPE_LOG);
152

153
	block = buf_page_get(page_id_t(space->id, 0), space->zip_size(),
154
			     RW_SX_LATCH, mtr);
155 156
	header = FSP_HEADER_OFFSET + buf_block_get_frame(block);
	buf_block_dbg_add_level(block, SYNC_FSP_PAGE);
157

158
	ut_ad(space->id == mach_read_from_4(FSP_SPACE_ID + header));
osku's avatar
osku committed
159 160 161
	return(header);
}

162 163 164 165 166 167 168
/** Set the XDES_FREE_BIT of a page.
@tparam         free    desired value of XDES_FREE_BIT
@param[in,out]  descr   extent descriptor
@param[in]      offset  page offset within the extent
@param[in,out]  mtr     mini-transaction */
template<bool free>
inline void xdes_set_free(xdes_t *descr, ulint offset, mtr_t *mtr)
osku's avatar
osku committed
169
{
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
  ut_ad(mtr_memo_contains_page(mtr, descr, MTR_MEMO_PAGE_SX_FIX));
  ut_ad(offset < FSP_EXTENT_SIZE);
  compile_time_assert(XDES_BITS_PER_PAGE == 2);
  compile_time_assert(XDES_FREE_BIT == 0);
  compile_time_assert(XDES_CLEAN_BIT == 1);

  ulint index= XDES_BITS_PER_PAGE * offset;
  byte *b= &descr[XDES_BITMAP + (index >> 3)];
  /* xdes_init() should have set all XDES_CLEAN_BIT. */
  ut_ad(!(~*b & 0xaa));
  /* Clear or set XDES_FREE_BIT. */
  byte val= free
    ? *b | 1 << (index & 7)
    : *b & ~(1 << (index & 7));
  mlog_write_ulint(b, val, MLOG_1BYTE, mtr);
185
}
osku's avatar
osku committed
186

187 188 189 190 191 192 193
/**
Find a free page.
@param descr   extent descriptor
@param hint    page offset to start searching from (towards larger pages)
@return free page offset
@retval ULINT_UNDEFINED if no page is free */
inline ulint xdes_find_free(const xdes_t *descr, ulint hint= 0)
osku's avatar
osku committed
194
{
195 196 197 198 199 200 201 202
  ut_ad(hint < FSP_EXTENT_SIZE);
  for (ulint i= hint; i < FSP_EXTENT_SIZE; i++)
    if (xdes_is_free(descr, i))
      return i;
  for (ulint i= 0; i < hint; i++)
    if (xdes_is_free(descr, i))
      return i;
  return ULINT_UNDEFINED;
203
}
osku's avatar
osku committed
204

205 206 207
/**
Determine the number of used pages in a descriptor.
@param descr  file descriptor
208
@return number of pages used */
209
inline ulint xdes_get_n_used(const xdes_t *descr)
osku's avatar
osku committed
210
{
211
  ulint count= 0;
osku's avatar
osku committed
212

213 214 215
  for (ulint i= 0; i < FSP_EXTENT_SIZE; ++i)
    if (!xdes_is_free(descr, i))
      count++;
osku's avatar
osku committed
216

217
  return count;
osku's avatar
osku committed
218 219
}

220 221 222 223 224
/**
Determine whether a file extent is full.
@param descr  file descriptor
@return whether all pages have been allocated */
inline bool xdes_is_full(const xdes_t *descr)
osku's avatar
osku committed
225
{
226
  return FSP_EXTENT_SIZE == xdes_get_n_used(descr);
osku's avatar
osku committed
227 228
}

229
/**********************************************************************//**
osku's avatar
osku committed
230 231 232 233 234
Sets the state of an xdes. */
UNIV_INLINE
void
xdes_set_state(
/*===========*/
235
	xdes_t*	descr,	/*!< in/out: descriptor */
236
	ulint	state,	/*!< in: state to set */
237
	mtr_t*	mtr)	/*!< in/out: mini-transaction */
osku's avatar
osku committed
238 239 240 241
{
	ut_ad(descr && mtr);
	ut_ad(state >= XDES_FREE);
	ut_ad(state <= XDES_FSEG);
242
	ut_ad(mtr_memo_contains_page(mtr, descr, MTR_MEMO_PAGE_SX_FIX));
osku's avatar
osku committed
243

244
	mlog_write_ulint(descr + XDES_STATE, state, MLOG_4BYTES, mtr);
osku's avatar
osku committed
245 246
}

247
/**********************************************************************//**
248
Gets the state of an xdes.
249
@return state */
osku's avatar
osku committed
250 251 252 253
UNIV_INLINE
ulint
xdes_get_state(
/*===========*/
254
	const xdes_t*	descr,	/*!< in: descriptor */
255
	mtr_t*		mtr)	/*!< in/out: mini-transaction */
osku's avatar
osku committed
256
{
marko's avatar
marko committed
257 258
	ulint	state;

osku's avatar
osku committed
259
	ut_ad(descr && mtr);
260
	ut_ad(mtr_memo_contains_page(mtr, descr, MTR_MEMO_PAGE_SX_FIX));
osku's avatar
osku committed
261

262
	state = mach_read_from_4(descr + XDES_STATE);
marko's avatar
marko committed
263 264
	ut_ad(state - 1 < XDES_FSEG);
	return(state);
osku's avatar
osku committed
265 266
}

267
/**********************************************************************//**
osku's avatar
osku committed
268 269 270 271 272
Inits an extent descriptor to the free and clean state. */
UNIV_INLINE
void
xdes_init(
/*======*/
273
	xdes_t*	descr,	/*!< in: descriptor */
274
	mtr_t*	mtr)	/*!< in/out: mini-transaction */
osku's avatar
osku committed
275
{
276
	ut_ad(mtr_memo_contains_page(mtr, descr, MTR_MEMO_PAGE_SX_FIX));
277
	mlog_memset(descr + XDES_BITMAP, XDES_SIZE - XDES_BITMAP, 0xff, mtr);
osku's avatar
osku committed
278
	xdes_set_state(descr, XDES_FREE, mtr);
279
}
osku's avatar
osku committed
280

281 282
/** Get pointer to a the extent descriptor of a page.
@param[in,out]	sp_header	tablespace header page, x-latched
283
@param[in]	space		tablespace
284 285 286 287 288
@param[in]	offset		page offset
@param[in,out]	mtr		mini-transaction
@param[in]	init_space	whether the tablespace is being initialized
@param[out]	desc_block	descriptor block, or NULL if it is
the same as the tablespace header
289
@return pointer to the extent descriptor, NULL if the page does not
290
exist in the space or if the offset exceeds free limit */
291
UNIV_INLINE MY_ATTRIBUTE((warn_unused_result))
osku's avatar
osku committed
292 293
xdes_t*
xdes_get_descriptor_with_space_hdr(
294 295 296 297 298 299
	fsp_header_t*		sp_header,
	const fil_space_t*	space,
	page_no_t		offset,
	mtr_t*			mtr,
	bool			init_space = false,
	buf_block_t**		desc_block = NULL)
osku's avatar
osku committed
300 301 302 303 304
{
	ulint	limit;
	ulint	size;
	ulint	descr_page_no;
	page_t*	descr_page;
305
	ut_ad(mtr_memo_contains(mtr, &space->latch, MTR_MEMO_X_LOCK));
306
	ut_ad(mtr_memo_contains_page(mtr, sp_header, MTR_MEMO_PAGE_SX_FIX));
307
	ut_ad(page_offset(sp_header) == FSP_HEADER_OFFSET);
osku's avatar
osku committed
308
	/* Read free limit and space size */
309 310
	limit = mach_read_from_4(sp_header + FSP_FREE_LIMIT);
	size  = mach_read_from_4(sp_header + FSP_SIZE);
311 312
	ut_ad(limit == space->free_limit
	      || (space->free_limit == 0
313
		  && (init_space
314
		      || space->purpose == FIL_TYPE_TEMPORARY
315
		      || (srv_startup_is_before_trx_rollback_phase
316 317
			  && (space->id == TRX_SYS_SPACE
			      || srv_is_undo_tablespace(space->id))))));
318
	ut_ad(size == space->size_in_header);
osku's avatar
osku committed
319

320
	if ((offset >= size) || (offset >= limit)) {
osku's avatar
osku committed
321 322 323
		return(NULL);
	}

324
	const ulint zip_size = space->zip_size();
325

326
	descr_page_no = xdes_calc_descriptor_page(zip_size, offset);
327 328

	buf_block_t*		block;
osku's avatar
osku committed
329 330 331 332

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

333
		descr_page = page_align(sp_header);
334
		block = NULL;
osku's avatar
osku committed
335
	} else {
336
		block = buf_page_get(
337
			page_id_t(space->id, descr_page_no), zip_size,
338
			RW_SX_LATCH, mtr);
339 340

		buf_block_dbg_add_level(block, SYNC_FSP_PAGE);
341

342
		descr_page = buf_block_get_frame(block);
343
	}
osku's avatar
osku committed
344

345 346 347 348
	if (desc_block != NULL) {
		*desc_block = block;
	}

osku's avatar
osku committed
349
	return(descr_page + XDES_ARR_OFFSET
350
	       + XDES_SIZE * xdes_calc_descriptor_index(zip_size, offset));
osku's avatar
osku committed
351 352
}

353 354 355 356 357 358 359
/** Get 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, we will add 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.
@param[in]	space		tablespace
360 361 362
@param[in]	offset		page offset; if equal to the free limit, we
try to add new extents to the space free list
@param[in,out]	mtr		mini-transaction
363
@return the extent descriptor */
364
MY_ATTRIBUTE((warn_unused_result))
365
static
osku's avatar
osku committed
366
xdes_t*
367
xdes_get_descriptor(const fil_space_t* space, page_no_t offset, mtr_t* mtr)
osku's avatar
osku committed
368
{
369
	buf_block_t*	block;
osku's avatar
osku committed
370 371
	fsp_header_t*	sp_header;

372
	block = buf_page_get(page_id_t(space->id, 0), space->zip_size(),
373 374
			     RW_SX_LATCH, mtr);

375
	buf_block_dbg_add_level(block, SYNC_FSP_PAGE);
376

377
	sp_header = FSP_HEADER_OFFSET + buf_block_get_frame(block);
378 379
	return(xdes_get_descriptor_with_space_hdr(
		       sp_header, space, offset, mtr));
osku's avatar
osku committed
380 381
}

382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406
/** Get 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, we will add 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.
@param[in]	space		tablespace
@param[in]	page		descriptor page offset
@param[in]	offset		page offset
@param[in,out]	mtr		mini-transaction
@return	the extent descriptor
@retval	NULL	if the descriptor is not available */
MY_ATTRIBUTE((warn_unused_result))
static
const xdes_t*
xdes_get_descriptor_const(
	const fil_space_t*	space,
	page_no_t		page,
	page_no_t		offset,
	mtr_t*			mtr)
{
	ut_ad(mtr_memo_contains(mtr, &space->latch, MTR_MEMO_S_LOCK));
	ut_ad(offset < space->free_limit);
	ut_ad(offset < space->size_in_header);

407 408
	const ulint zip_size = space->zip_size();

409
	if (buf_block_t* block = buf_page_get(page_id_t(space->id, page),
410
					      zip_size, RW_S_LATCH, mtr)) {
411 412 413 414 415 416 417 418 419 420
		buf_block_dbg_add_level(block, SYNC_FSP_PAGE);

		ut_ad(page != 0 || space->free_limit == mach_read_from_4(
			      FSP_FREE_LIMIT + FSP_HEADER_OFFSET
			      + block->frame));
		ut_ad(page != 0 || space->size_in_header == mach_read_from_4(
			      FSP_SIZE + FSP_HEADER_OFFSET
			      + block->frame));

		return(block->frame + XDES_ARR_OFFSET + XDES_SIZE
421
		       * xdes_calc_descriptor_index(zip_size, offset));
422 423 424 425 426
	}

	return(NULL);
}

427
/** Get a pointer to the extent descriptor. The page where the
428
extent descriptor resides is x-locked.
429 430 431 432
@param[in]	space		tablespace
@param[in]	lst_node	file address of the list node
				contained in the descriptor
@param[in,out]	mtr		mini-transaction
433
@return pointer to the extent descriptor */
434
MY_ATTRIBUTE((nonnull, warn_unused_result))
osku's avatar
osku committed
435 436 437
UNIV_INLINE
xdes_t*
xdes_lst_get_descriptor(
438 439 440
	const fil_space_t*	space,
	fil_addr_t		lst_node,
	mtr_t*			mtr)
osku's avatar
osku committed
441
{
442
	ut_ad(mtr_memo_contains(mtr, &space->latch, MTR_MEMO_X_LOCK));
443 444 445
	return fut_get_ptr(space->id, space->zip_size(),
			   lst_node, RW_SX_LATCH, mtr)
		- XDES_FLST_NODE;
osku's avatar
osku committed
446 447
}

448
/********************************************************************//**
449
Returns page offset of the first page in extent described by a descriptor.
450
@return offset of the first page in extent */
osku's avatar
osku committed
451 452 453 454
UNIV_INLINE
ulint
xdes_get_offset(
/*============*/
455
	const xdes_t*	descr)	/*!< in: extent descriptor */
osku's avatar
osku committed
456 457 458
{
	ut_ad(descr);

459
	return(page_get_page_no(page_align(descr))
460
	       + ((page_offset(descr) - XDES_ARR_OFFSET) / XDES_SIZE)
461
	       * FSP_EXTENT_SIZE);
osku's avatar
osku committed
462 463
}

464 465 466
/** Initialize a file page whose prior contents should be ignored.
@param[in,out]	block	buffer pool block */
void fsp_apply_init_file_page(buf_block_t* block)
osku's avatar
osku committed
467
{
468 469
	page_t*		page	= buf_block_get_frame(block);

470
	memset(page, 0, srv_page_size);
471 472 473 474

	mach_write_to_4(page + FIL_PAGE_OFFSET, block->page.id.page_no());
	mach_write_to_4(page + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID,
			block->page.id.space());
475

476
	if (page_zip_des_t* page_zip= buf_block_get_page_zip(block)) {
477
		memset(page_zip->data, 0, page_zip_get_size(page_zip));
478 479 480 481
		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);
482 483 484
	}
}

485
#ifdef UNIV_DEBUG
486 487 488
/** Assert that the mini-transaction is compatible with
updating an allocation bitmap page.
@param[in]	mtr	mini-transaction */
489
void fil_space_t::modify_check(const mtr_t& mtr) const
490
{
491
	switch (mtr.get_log_mode()) {
492 493 494 495
	case MTR_LOG_SHORT_INSERTS:
	case MTR_LOG_NONE:
		/* These modes are only allowed within a non-bitmap page
		when there is a higher-level redo log record written. */
496 497
		ut_ad(purpose == FIL_TYPE_TABLESPACE
		      || purpose == FIL_TYPE_TEMPORARY);
498 499
		break;
	case MTR_LOG_NO_REDO:
500 501
		ut_ad(purpose == FIL_TYPE_TEMPORARY
		      || purpose == FIL_TYPE_IMPORT
Marko Mäkelä's avatar
Marko Mäkelä committed
502
		      || redo_skipped_count);
503 504
		return;
	case MTR_LOG_ALL:
505 506 507 508
		/* We may only write redo log for a persistent
		tablespace. */
		ut_ad(purpose == FIL_TYPE_TABLESPACE);
		ut_ad(mtr.is_named_space(id));
509 510
		return;
	}
osku's avatar
osku committed
511

512
	ut_ad(!"invalid log mode");
osku's avatar
osku committed
513
}
514 515
#endif

516
/**********************************************************************//**
517 518 519
Writes the space id and flags to a tablespace header.  The flags contain
row type, physical/compressed page size, and logical/uncompressed page
size of the tablespace. */
osku's avatar
osku committed
520
void
521 522
fsp_header_init_fields(
/*===================*/
523 524
	page_t*	page,		/*!< in/out: first page in the space */
	ulint	space_id,	/*!< in: space id */
525
	ulint	flags)		/*!< in: tablespace flags (FSP_SPACE_FLAGS) */
osku's avatar
osku committed
526
{
527
	flags &= ~FSP_FLAGS_MEM_MASK;
528
	ut_a(fil_space_t::is_valid_flags(flags, space_id));
529

530 531
	mach_write_to_4(FSP_HEADER_OFFSET + FSP_SPACE_ID + page,
			space_id);
532 533
	mach_write_to_4(FSP_HEADER_OFFSET + FSP_SPACE_FLAGS + page,
			flags);
osku's avatar
osku committed
534 535
}

536
/** Initialize a tablespace header.
537 538 539 540
@param[in,out]	space	tablespace
@param[in]	size	current size in blocks
@param[in,out]	mtr	mini-transaction */
void fsp_header_init(fil_space_t* space, ulint size, mtr_t* mtr)
osku's avatar
osku committed
541
{
542 543
	const page_id_t page_id(space->id, 0);
	const ulint zip_size = space->zip_size();
osku's avatar
osku committed
544

545
	mtr_x_lock(&space->latch, mtr);
546 547
	buf_block_t* block = buf_page_create(page_id, zip_size, mtr);
	buf_page_get(page_id, zip_size, RW_SX_LATCH, mtr);
548
	buf_block_dbg_add_level(block, SYNC_FSP_PAGE);
osku's avatar
osku committed
549

550 551 552 553
	space->size_in_header = size;
	space->free_len = 0;
	space->free_limit = 0;

osku's avatar
osku committed
554 555
	/* The prior contents of the file page should be ignored */

556
	fsp_init_file_page(space, block, mtr);
osku's avatar
osku committed
557

558
	mlog_write_ulint(block->frame + FIL_PAGE_TYPE, FIL_PAGE_TYPE_FSP_HDR,
559
			 MLOG_2BYTES, mtr);
560

561 562
	mlog_write_ulint(FSP_HEADER_OFFSET + FSP_SPACE_ID + block->frame,
			 space->id, MLOG_4BYTES, mtr);
563 564
	ut_ad(0 == mach_read_from_4(FSP_HEADER_OFFSET + FSP_NOT_USED
				    + block->frame));
565 566
	mlog_write_ulint(FSP_HEADER_OFFSET + FSP_SIZE + block->frame, size,
			 MLOG_4BYTES, mtr);
567 568
	ut_ad(0 == mach_read_from_4(FSP_HEADER_OFFSET + FSP_FREE_LIMIT
				    + block->frame));
569
	mlog_write_ulint(FSP_HEADER_OFFSET + FSP_SPACE_FLAGS + block->frame,
Marko Mäkelä's avatar
Marko Mäkelä committed
570
			 space->flags & ~FSP_FLAGS_MEM_MASK,
571
			 MLOG_4BYTES, mtr);
572 573 574 575 576 577 578 579
	ut_ad(0 == mach_read_from_4(FSP_HEADER_OFFSET + FSP_FRAG_N_USED
				    + block->frame));

	flst_init(block, FSP_HEADER_OFFSET + FSP_FREE, mtr);
	flst_init(block, FSP_HEADER_OFFSET + FSP_FREE_FRAG, mtr);
	flst_init(block, FSP_HEADER_OFFSET + FSP_FULL_FRAG, mtr);
	flst_init(block, FSP_HEADER_OFFSET + FSP_SEG_INODES_FULL, mtr);
	flst_init(block, FSP_HEADER_OFFSET + FSP_SEG_INODES_FREE, mtr);
osku's avatar
osku committed
580

581
	mlog_write_ull(FSP_HEADER_OFFSET + FSP_SEG_ID + block->frame, 1, mtr);
582

583 584
	fsp_fill_free_list(!is_system_tablespace(space->id),
			   space, FSP_HEADER_OFFSET + block->frame, mtr);
585

586 587 588 589 590
	/* Write encryption metadata to page 0 if tablespace is
	encrypted or encryption is disabled by table option. */
	if (space->crypt_data &&
	    (space->crypt_data->should_encrypt() ||
	     space->crypt_data->not_encrypted())) {
591
		space->crypt_data->write_page0(space, block->frame, mtr);
592
	}
593
}
osku's avatar
osku committed
594

595
/**********************************************************************//**
596
Reads the space id from the first page of a tablespace.
597
@return space id, ULINT UNDEFINED if error */
osku's avatar
osku committed
598 599 600
ulint
fsp_header_get_space_id(
/*====================*/
601
	const page_t*	page)	/*!< in: first page of a tablespace */
osku's avatar
osku committed
602 603 604 605 606 607 608 609
{
	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);

610 611 612
	DBUG_EXECUTE_IF("fsp_header_get_space_id_failure",
			id = ULINT_UNDEFINED;);

osku's avatar
osku committed
613
	if (id != fsp_id) {
614 615
		ib::error() << "Space ID in fsp header is " << fsp_id
			<< ", but in the page header it is " << id << ".";
osku's avatar
osku committed
616 617 618 619 620 621
		return(ULINT_UNDEFINED);
	}

	return(id);
}

622
/** Try to extend a single-table tablespace so that a page would fit in the
623
data file.
624 625 626 627 628
@param[in,out]	space	tablespace
@param[in]	page_no	page number
@param[in,out]	header	tablespace header
@param[in,out]	mtr	mini-transaction
@return true if success */
629
static ATTRIBUTE_COLD __attribute__((warn_unused_result))
630
bool
osku's avatar
osku committed
631
fsp_try_extend_data_file_with_pages(
632 633 634 635
	fil_space_t*	space,
	ulint		page_no,
	fsp_header_t*	header,
	mtr_t*		mtr)
osku's avatar
osku committed
636
{
637
	bool	success;
osku's avatar
osku committed
638 639
	ulint	size;

640
	ut_a(!is_system_tablespace(space->id));
641
	ut_d(space->modify_check(*mtr));
osku's avatar
osku committed
642

643 644
	size = mach_read_from_4(header + FSP_SIZE);
	ut_ad(size == space->size_in_header);
645

osku's avatar
osku committed
646 647
	ut_a(page_no >= size);

648 649 650 651
	success = fil_space_extend(space, page_no + 1);
	/* The size may be less than we wanted if we ran out of disk space. */
	mlog_write_ulint(header + FSP_SIZE, space->size, MLOG_4BYTES, mtr);
	space->size_in_header = space->size;
osku's avatar
osku committed
652 653 654 655

	return(success);
}

656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693
/** Calculate the number of physical pages in an extent for this file.
@param[in]	physical_size	page_size of the datafile
@return number of pages in an extent for this file */
inline ulint fsp_get_extent_size_in_pages(ulint physical_size)
{
	return (FSP_EXTENT_SIZE << srv_page_size_shift) / physical_size;
}


/** Calculate the number of pages to extend a datafile.
We extend single-table tablespaces first one extent at a time,
but 4 at a time for bigger tablespaces. It is not enough to extend always
by one extent, because we need to add at least one extent to FSP_FREE.
A single extent descriptor page will track many extents. And the extent
that uses its extent descriptor page is put onto the FSP_FREE_FRAG list.
Extents that do not use their extent descriptor page are added to FSP_FREE.
The physical page size is used to determine how many extents are tracked
on one extent descriptor page. See xdes_calc_descriptor_page().
@param[in]	physical_size	page size in data file
@param[in]	size		current number of pages in the datafile
@return number of pages to extend the file. */
static ulint fsp_get_pages_to_extend_ibd(ulint physical_size, ulint size)
{
	ulint extent_size = fsp_get_extent_size_in_pages(physical_size);
	/* The threshold is set at 32MiB except when the physical page
	size is small enough that it must be done sooner. */
	ulint threshold = std::min(32 * extent_size, physical_size);

	if (size >= threshold) {
		/* Below in fsp_fill_free_list() we assume
		that we add at most FSP_FREE_ADD extents at
		a time */
		extent_size *= FSP_FREE_ADD;
	}

	return extent_size;
}

694 695 696 697
/** Try to extend the last data file of a tablespace if it is auto-extending.
@param[in,out]	space	tablespace
@param[in,out]	header	tablespace header
@param[in,out]	mtr	mini-transaction
Marko Mäkelä's avatar
Marko Mäkelä committed
698 699
@return	number of pages added
@retval	0 if the tablespace was not extended */
700
ATTRIBUTE_COLD __attribute__((nonnull))
Marko Mäkelä's avatar
Marko Mäkelä committed
701
static
702
ulint
Marko Mäkelä's avatar
Marko Mäkelä committed
703
fsp_try_extend_data_file(fil_space_t* space, fsp_header_t* header, mtr_t* mtr)
osku's avatar
osku committed
704
{
705 706 707 708 709
	ulint	size;		/* current number of pages in the datafile */
	ulint	size_increase;	/* number of pages to extend this file */
	const char* OUT_OF_SPACE_MSG =
		"ran out of space. Please add another file or use"
		" 'autoextend' for the last file in setting";
osku's avatar
osku committed
710

711
	ut_d(space->modify_check(*mtr));
osku's avatar
osku committed
712

713
	if (space->id == TRX_SYS_SPACE
714
	    && !srv_sys_space.can_auto_extend_last_file()) {
osku's avatar
osku committed
715

716 717
		/* We print the error message only once to avoid
		spamming the error log. Note that we don't need
718
		to reset the flag to false as dealing with this
719
		error requires server restart. */
720
		if (!srv_sys_space.get_tablespace_full_status()) {
Marko Mäkelä's avatar
Marko Mäkelä committed
721 722
			ib::error() << "The InnoDB system tablespace "
				<< OUT_OF_SPACE_MSG
723 724
				<< " innodb_data_file_path.";
			srv_sys_space.set_tablespace_full_status(true);
725
		}
Marko Mäkelä's avatar
Marko Mäkelä committed
726
		return(0);
727
	} else if (space->id == SRV_TMP_SPACE_ID
728 729 730 731 732 733 734
		   && !srv_tmp_space.can_auto_extend_last_file()) {

		/* We print the error message only once to avoid
		spamming the error log. Note that we don't need
		to reset the flag to false as dealing with this
		error requires server restart. */
		if (!srv_tmp_space.get_tablespace_full_status()) {
Marko Mäkelä's avatar
Marko Mäkelä committed
735 736
			ib::error() << "The InnoDB temporary tablespace "
				<< OUT_OF_SPACE_MSG
737 738 739
				<< " innodb_temp_data_file_path.";
			srv_tmp_space.set_tablespace_full_status(true);
		}
Marko Mäkelä's avatar
Marko Mäkelä committed
740
		return(0);
osku's avatar
osku committed
741 742
	}

743 744 745
	size = mach_read_from_4(header + FSP_SIZE);
	ut_ad(size == space->size_in_header);

746
	const ulint ps = space->physical_size();
osku's avatar
osku committed
747

748 749
	switch (space->id) {
	case TRX_SYS_SPACE:
750
		size_increase = srv_sys_space.get_increment();
751 752
		break;
	case SRV_TMP_SPACE_ID:
753
		size_increase = srv_tmp_space.get_increment();
754 755
		break;
	default:
756
		ulint extent_pages = fsp_get_extent_size_in_pages(ps);
757
		if (size < extent_pages) {
758
			/* Let us first extend the file to extent_size */
759 760
			if (!fsp_try_extend_data_file_with_pages(
				    space, extent_pages - 1, header, mtr)) {
Marko Mäkelä's avatar
Marko Mäkelä committed
761
				return(0);
osku's avatar
osku committed
762 763
			}

764
			size = extent_pages;
765 766
		}

767
		size_increase = fsp_get_pages_to_extend_ibd(ps, size);
osku's avatar
osku committed
768
	}
769

osku's avatar
osku committed
770
	if (size_increase == 0) {
Marko Mäkelä's avatar
Marko Mäkelä committed
771
		return(0);
osku's avatar
osku committed
772
	}
773

774
	if (!fil_space_extend(space, size + size_increase)) {
Marko Mäkelä's avatar
Marko Mäkelä committed
775
		return(0);
776 777
	}

osku's avatar
osku committed
778 779 780
	/* We ignore any fragments of a full megabyte when storing the size
	to the space header */

Marko Mäkelä's avatar
Marko Mäkelä committed
781
	space->size_in_header = ut_2pow_round(space->size, (1024 * 1024) / ps);
782 783 784 785

	mlog_write_ulint(
		header + FSP_SIZE, space->size_in_header, MLOG_4BYTES, mtr);

Marko Mäkelä's avatar
Marko Mäkelä committed
786
	return(size_increase);
787 788
}

Marko Mäkelä's avatar
Marko Mäkelä committed
789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804
/** Reset the page type.
Data files created before MySQL 5.1.48 may contain garbage in FIL_PAGE_TYPE.
In MySQL 3.23.53, only undo log pages and index pages were tagged.
Any other pages were written with uninitialized bytes in FIL_PAGE_TYPE.
@param[in]	block	block with invalid FIL_PAGE_TYPE
@param[in]	type	expected page type
@param[in,out]	mtr	mini-transaction */
ATTRIBUTE_COLD
void fil_block_reset_type(const buf_block_t& block, ulint type, mtr_t* mtr)
{
	ib::info()
		<< "Resetting invalid page " << block.page.id << " type "
		<< fil_page_get_type(block.frame) << " to " << type << ".";
	mlog_write_ulint(block.frame + FIL_PAGE_TYPE, type, MLOG_2BYTES, mtr);
}

805
/** Put new extents to the free list if there are free extents above the free
osku's avatar
osku committed
806
limit. If an extent happens to contain an extent descriptor page, the extent
807 808 809 810 811 812 813
is put to the FSP_FREE_FRAG list with the page marked as used.
@param[in]	init_space	true if this is a single-table tablespace
and we are only initializing the first extent and the first bitmap pages;
then we will not allocate more extents
@param[in,out]	space		tablespace
@param[in,out]	header		tablespace header
@param[in,out]	mtr		mini-transaction */
osku's avatar
osku committed
814 815 816
static
void
fsp_fill_free_list(
817 818 819 820
	bool		init_space,
	fil_space_t*	space,
	fsp_header_t*	header,
	mtr_t*		mtr)
osku's avatar
osku committed
821 822 823 824
{
	ulint	limit;
	ulint	size;
	xdes_t*	descr;
825
	ulint	count		= 0;
osku's avatar
osku committed
826 827 828
	ulint	frag_n_used;
	ulint	i;

829
	ut_ad(page_offset(header) == FSP_HEADER_OFFSET);
830
	ut_d(space->modify_check(*mtr));
831

osku's avatar
osku committed
832
	/* Check if we can fill free list from above the free list limit */
833 834 835 836 837 838
	size = mach_read_from_4(header + FSP_SIZE);
	limit = mach_read_from_4(header + FSP_FREE_LIMIT);

	ut_ad(size == space->size_in_header);
	ut_ad(limit == space->free_limit);

839
	const ulint zip_size = space->zip_size();
840 841

	if (size < limit + FSP_EXTENT_SIZE * FSP_FREE_ADD) {
842 843 844 845 846 847
		bool	skip_resize	= init_space;
		switch (space->id) {
		case TRX_SYS_SPACE:
			skip_resize = !srv_sys_space.can_auto_extend_last_file();
			break;
		case SRV_TMP_SPACE_ID:
848
			skip_resize = !srv_tmp_space.can_auto_extend_last_file();
849 850 851 852
			break;
		}

		if (!skip_resize) {
Marko Mäkelä's avatar
Marko Mäkelä committed
853
			fsp_try_extend_data_file(space, header, mtr);
854 855
			size = space->size_in_header;
		}
osku's avatar
osku committed
856 857 858
	}

	i = limit;
859

osku's avatar
osku committed
860
	while ((init_space && i < 1)
861
	       || ((i + FSP_EXTENT_SIZE <= size) && (count < FSP_FREE_ADD))) {
osku's avatar
osku committed
862

Marko Mäkelä's avatar
Marko Mäkelä committed
863 864
		const bool init_xdes = 0
			== ut_2pow_remainder(i, ulint(space->physical_size()));
865

866
		space->free_limit = i + FSP_EXTENT_SIZE;
osku's avatar
osku committed
867
		mlog_write_ulint(header + FSP_FREE_LIMIT, i + FSP_EXTENT_SIZE,
868
				 MLOG_4BYTES, mtr);
osku's avatar
osku committed
869

870
		if (init_xdes) {
osku's avatar
osku committed
871

872 873
			buf_block_t*	block;

osku's avatar
osku committed
874 875 876 877 878
			/* 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) {
879 880
				const page_id_t	page_id(space->id, i);

881
				block = buf_page_create(
882
					page_id, zip_size, mtr);
883 884

				buf_page_get(
885
					page_id, zip_size, RW_SX_LATCH, mtr);
886 887

				buf_block_dbg_add_level(block, SYNC_FSP_PAGE);
888

889
				fsp_init_file_page(space, block, mtr);
890 891
				mlog_write_ulint(buf_block_get_frame(block)
						 + FIL_PAGE_TYPE,
892 893
						 FIL_PAGE_TYPE_XDES,
						 MLOG_2BYTES, mtr);
osku's avatar
osku committed
894 895 896 897
			}

			/* Initialize the ibuf bitmap page in a separate
			mini-transaction because it is low in the latching
898 899 900
			order, and we must be able to release its latch.
			Note: Insert-Buffering is disabled for tables that
			reside in the temp-tablespace. */
901
			if (space->purpose != FIL_TYPE_TEMPORARY) {
902 903 904 905 906 907 908 909 910 911
				mtr_t	ibuf_mtr;

				mtr_start(&ibuf_mtr);
				ibuf_mtr.set_named_space(space);

				const page_id_t	page_id(
					space->id,
					i + FSP_IBUF_BITMAP_OFFSET);

				block = buf_page_create(
912
					page_id, zip_size, &ibuf_mtr);
913

914
				buf_page_get(
915
					page_id, zip_size, RW_SX_LATCH,
916
					&ibuf_mtr);
osku's avatar
osku committed
917

918
				buf_block_dbg_add_level(block, SYNC_FSP_PAGE);
919

920
				fsp_init_file_page(space, block, &ibuf_mtr);
921 922 923
				mlog_write_ulint(block->frame + FIL_PAGE_TYPE,
						 FIL_PAGE_IBUF_BITMAP,
						 MLOG_2BYTES, &ibuf_mtr);
924 925
				mtr_commit(&ibuf_mtr);
			}
osku's avatar
osku committed
926 927
		}

928 929
		buf_block_t*	desc_block = NULL;
		descr = xdes_get_descriptor_with_space_hdr(
930
			header, space, i, mtr, init_space, &desc_block);
931 932
		if (desc_block != NULL) {
			fil_block_check_type(
933
				*desc_block, FIL_PAGE_TYPE_XDES, mtr);
934
		}
osku's avatar
osku committed
935 936
		xdes_init(descr, mtr);

937
		if (UNIV_UNLIKELY(init_xdes)) {
osku's avatar
osku committed
938 939 940 941 942

			/* The first page in the extent is a descriptor page
			and the second is an ibuf bitmap page: mark them
			used */

943 944 945
			xdes_set_free<false>(descr, 0, mtr);
			xdes_set_free<false>(descr, FSP_IBUF_BITMAP_OFFSET,
					     mtr);
osku's avatar
osku committed
946 947 948
			xdes_set_state(descr, XDES_FREE_FRAG, mtr);

			flst_add_last(header + FSP_FREE_FRAG,
949
				      descr + XDES_FLST_NODE, mtr);
950 951
			frag_n_used = mach_read_from_4(
				header + FSP_FRAG_N_USED);
osku's avatar
osku committed
952
			mlog_write_ulint(header + FSP_FRAG_N_USED,
953
					 frag_n_used + 2, MLOG_4BYTES, mtr);
osku's avatar
osku committed
954 955
		} else {
			flst_add_last(header + FSP_FREE,
956
				      descr + XDES_FLST_NODE, mtr);
osku's avatar
osku committed
957 958 959 960 961
			count++;
		}

		i += FSP_EXTENT_SIZE;
	}
962 963

	space->free_len += count;
964
}
osku's avatar
osku committed
965

966
/** Allocates a new free extent.
967
@param[in,out]	space		tablespace
968 969 970 971
@param[in]	hint		hint of which extent would be desirable: any
page offset in the extent goes; the hint must not be > FSP_FREE_LIMIT
@param[in,out]	mtr		mini-transaction
@return extent descriptor, NULL if cannot be allocated */
osku's avatar
osku committed
972 973 974
static
xdes_t*
fsp_alloc_free_extent(
975
	fil_space_t*		space,
976 977
	ulint			hint,
	mtr_t*			mtr)
osku's avatar
osku committed
978 979 980 981
{
	fsp_header_t*	header;
	fil_addr_t	first;
	xdes_t*		descr;
982
	buf_block_t*	desc_block = NULL;
983

984
	header = fsp_get_space_header(space, mtr);
osku's avatar
osku committed
985

986
	descr = xdes_get_descriptor_with_space_hdr(
987
		header, space, hint, mtr, false, &desc_block);
988 989

	if (desc_block != NULL) {
990
		fil_block_check_type(*desc_block, FIL_PAGE_TYPE_XDES, mtr);
991
	}
osku's avatar
osku committed
992 993 994

	if (descr && (xdes_get_state(descr, mtr) == XDES_FREE)) {
		/* Ok, we can take this extent */
995
	} else {
osku's avatar
osku committed
996 997 998 999
		/* Take the first extent in the free list */
		first = flst_get_first(header + FSP_FREE, mtr);

		if (fil_addr_is_null(first)) {
1000
			fsp_fill_free_list(false, space, header, mtr);
osku's avatar
osku committed
1001 1002 1003 1004 1005 1006 1007 1008

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

		if (fil_addr_is_null(first)) {

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

1010
		descr = xdes_lst_get_descriptor(space, first, mtr);
osku's avatar
osku committed
1011 1012 1013
	}

	flst_remove(header + FSP_FREE, descr + XDES_FLST_NODE, mtr);
1014
	space->free_len--;
osku's avatar
osku committed
1015 1016 1017 1018

	return(descr);
}

1019
/**********************************************************************//**
1020
Allocates a single free page from a space. */
Sergei Golubchik's avatar
Sergei Golubchik committed
1021
static MY_ATTRIBUTE((nonnull))
1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032
void
fsp_alloc_from_free_frag(
/*=====================*/
	fsp_header_t*	header,	/*!< in/out: tablespace header */
	xdes_t*		descr,	/*!< in/out: extent descriptor */
	ulint		bit,	/*!< in: slot to allocate in the extent */
	mtr_t*		mtr)	/*!< in/out: mini-transaction */
{
	ulint		frag_n_used;

	ut_ad(xdes_get_state(descr, mtr) == XDES_FREE_FRAG);
1033
	ut_a(xdes_is_free(descr, bit));
1034
	xdes_set_free<false>(descr, bit, mtr);
1035 1036

	/* Update the FRAG_N_USED field */
1037
	frag_n_used = mach_read_from_4(header + FSP_FRAG_N_USED);
1038 1039 1040
	frag_n_used++;
	mlog_write_ulint(header + FSP_FRAG_N_USED, frag_n_used, MLOG_4BYTES,
			 mtr);
1041
	if (xdes_is_full(descr)) {
1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054
		/* The fragment is full: move it to another list */
		flst_remove(header + FSP_FREE_FRAG, descr + XDES_FLST_NODE,
			    mtr);
		xdes_set_state(descr, XDES_FULL_FRAG, mtr);

		flst_add_last(header + FSP_FULL_FRAG, descr + XDES_FLST_NODE,
			      mtr);
		mlog_write_ulint(header + FSP_FRAG_N_USED,
				 frag_n_used - FSP_EXTENT_SIZE, MLOG_4BYTES,
				 mtr);
	}
}

1055
/** Gets a buffer block for an allocated page.
1056 1057 1058
NOTE: If init_mtr != mtr, the block will only be initialized if it was
not previously x-latched. It is assumed that the block has been
x-latched only by mtr, and freed in mtr in that case.
1059 1060
@param[in,out]	space		tablespace
@param[in]	offset		page number of the allocated page
1061 1062 1063
@param[in]	rw_latch	RW_SX_LATCH, RW_X_LATCH
@param[in,out]	mtr		mini-transaction of the allocation
@param[in,out]	init_mtr	mini-transaction for initializing the page
1064 1065
@return block, initialized if init_mtr==mtr
or rw_lock_x_lock_count(&block->lock) == 1 */
1066
static
1067 1068
buf_block_t*
fsp_page_create(
1069 1070
	fil_space_t*		space,
	page_no_t		offset,
1071 1072 1073
	rw_lock_type_t		rw_latch,
	mtr_t*			mtr,
	mtr_t*			init_mtr)
1074
{
1075
	buf_block_t*	block = buf_page_create(page_id_t(space->id, offset),
1076
						space->zip_size(), init_mtr);
1077

1078 1079 1080
	ut_d(bool latched = mtr_memo_contains_flagged(mtr, block,
						      MTR_MEMO_PAGE_X_FIX
						      | MTR_MEMO_PAGE_SX_FIX));
1081 1082

	ut_ad(rw_latch == RW_X_LATCH || rw_latch == RW_SX_LATCH);
1083 1084

	/* Mimic buf_page_get(), but avoid the buf_pool->page_hash lookup. */
1085 1086 1087 1088 1089 1090
	if (rw_latch == RW_X_LATCH) {
		rw_lock_x_lock(&block->lock);
	} else {
		rw_lock_sx_lock(&block->lock);
	}

1091
	buf_block_buf_fix_inc(block, __FILE__, __LINE__);
1092 1093
	mtr_memo_push(init_mtr, block, rw_latch == RW_X_LATCH
		      ? MTR_MEMO_PAGE_X_FIX : MTR_MEMO_PAGE_SX_FIX);
1094 1095

	if (init_mtr == mtr
1096 1097 1098
	    || (rw_latch == RW_X_LATCH
		? rw_lock_get_x_lock_count(&block->lock) == 1
		: rw_lock_get_sx_lock_count(&block->lock) == 1)) {
1099 1100

		/* Initialize the page, unless it was already
1101
		SX-latched in mtr. (In this case, we would want to
1102
		allocate another page that has not been freed in mtr.) */
1103 1104
		ut_ad(init_mtr == mtr || !latched);
		fsp_init_file_page(space, block, init_mtr);
1105 1106 1107 1108 1109
	}

	return(block);
}

1110 1111
/** Allocates a single free page from a space.
The page is marked as used.
1112
@param[in,out]	space		tablespace
1113 1114 1115 1116 1117 1118 1119
@param[in]	hint		hint of which page would be desirable
@param[in]	rw_latch	RW_SX_LATCH, RW_X_LATCH
@param[in,out]	mtr		mini-transaction
@param[in,out]	init_mtr	mini-transaction in which the page should be
initialized (may be the same as mtr)
@retval NULL	if no page could be allocated
@retval block	rw_lock_x_lock_count(&block->lock) == 1 if allocation succeeded
1120
(init_mtr == mtr, or the page was not previously freed in mtr)
1121
@retval block	(not allocated or initialized) otherwise */
1122
static MY_ATTRIBUTE((warn_unused_result, nonnull))
1123
buf_block_t*
osku's avatar
osku committed
1124
fsp_alloc_free_page(
1125
	fil_space_t*		space,
1126 1127 1128 1129
	ulint			hint,
	rw_lock_type_t		rw_latch,
	mtr_t*			mtr,
	mtr_t*			init_mtr)
osku's avatar
osku committed
1130 1131 1132 1133 1134
{
	fsp_header_t*	header;
	fil_addr_t	first;
	xdes_t*		descr;
	ulint		free;
1135
	const ulint	space_id = space->id;
1136

1137
	ut_d(space->modify_check(*mtr));
1138
	header = fsp_get_space_header(space, mtr);
osku's avatar
osku committed
1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155

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

1157
			descr = fsp_alloc_free_extent(space, hint, mtr);
osku's avatar
osku committed
1158 1159 1160 1161

			if (descr == NULL) {
				/* No free space left */

1162
				return(NULL);
osku's avatar
osku committed
1163 1164 1165 1166
			}

			xdes_set_state(descr, XDES_FREE_FRAG, mtr);
			flst_add_last(header + FSP_FREE_FRAG,
1167
				      descr + XDES_FLST_NODE, mtr);
osku's avatar
osku committed
1168
		} else {
1169
			descr = xdes_lst_get_descriptor(space, first, mtr);
osku's avatar
osku committed
1170 1171 1172 1173 1174 1175 1176 1177 1178
		}

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

1179
	free = xdes_find_free(descr, hint % FSP_EXTENT_SIZE);
osku's avatar
osku committed
1180 1181
	if (free == ULINT_UNDEFINED) {

1182
		ut_print_buf(stderr, ((byte*) descr) - 500, 1000);
1183
		putc('\n', stderr);
osku's avatar
osku committed
1184 1185 1186 1187

		ut_error;
	}

1188
	page_no_t page_no = xdes_get_offset(descr) + free;
osku's avatar
osku committed
1189

1190 1191 1192
	page_no_t space_size = mach_read_from_4(header + FSP_SIZE);
	ut_ad(space_size == space->size_in_header
	      || (space_id == TRX_SYS_SPACE
1193
		  && srv_startup_is_before_trx_rollback_phase));
osku's avatar
osku committed
1194 1195

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

1199
		ut_a(!is_system_tablespace(space_id));
osku's avatar
osku committed
1200
		if (page_no >= FSP_EXTENT_SIZE) {
1201 1202 1203 1204
			ib::error() << "Trying to extend a single-table"
				" tablespace " << space << " , by single"
				" page(s) though the space size " << space_size
				<< ". Page no " << page_no << ".";
1205
			return(NULL);
osku's avatar
osku committed
1206
		}
1207

1208
		if (!fsp_try_extend_data_file_with_pages(space, page_no,
1209
							 header, mtr)) {
osku's avatar
osku committed
1210
			/* No disk space left */
1211
			return(NULL);
osku's avatar
osku committed
1212 1213 1214
		}
	}

1215
	fsp_alloc_from_free_frag(header, descr, free, mtr);
1216
	return fsp_page_create(space, page_no, rw_latch, mtr, init_mtr);
osku's avatar
osku committed
1217 1218
}

1219 1220
/** Frees a single page of a space.
The page is marked as free and clean.
1221
@param[in,out]	space		tablespace
1222
@param[in]	offset		page number
1223
@param[in]	log		whether to write MLOG_INIT_FREE_PAGE record
1224
@param[in,out]	mtr		mini-transaction */
1225 1226
static void fsp_free_page(fil_space_t* space, page_no_t offset,
			  bool log, mtr_t* mtr)
osku's avatar
osku committed
1227 1228 1229 1230 1231
{
	fsp_header_t*	header;
	xdes_t*		descr;
	ulint		state;
	ulint		frag_n_used;
1232

osku's avatar
osku committed
1233
	ut_ad(mtr);
1234
	ut_d(space->modify_check(*mtr));
osku's avatar
osku committed
1235

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

1238
	header = fsp_get_space_header(space, mtr);
osku's avatar
osku committed
1239

1240
	descr = xdes_get_descriptor_with_space_hdr(
1241
		header, space, offset, mtr);
osku's avatar
osku committed
1242 1243

	state = xdes_get_state(descr, mtr);
1244

1245 1246
	if (UNIV_UNLIKELY(state != XDES_FREE_FRAG
			  && state != XDES_FULL_FRAG)) {
1247
		ib::error() << "File space extent descriptor of page "
1248 1249
			<< page_id_t(space->id, offset)
			<< " has state " << state;
1250 1251 1252
		/* Crash in debug version, so that we get a core dump
		of this corruption. */
		ut_ad(0);
1253

osku's avatar
osku committed
1254 1255 1256 1257 1258 1259 1260 1261 1262 1263
		if (state == XDES_FREE) {
			/* We put here some fault tolerance: if the page
			is already free, return without doing anything! */

			return;
		}

		ut_error;
	}

1264
	if (xdes_is_free(descr, offset % FSP_EXTENT_SIZE)) {
1265
		ib::error() << "File space extent descriptor of page "
1266 1267
			<< page_id_t(space->id, offset)
			<< " says it is free.";
1268 1269 1270
		/* Crash in debug version, so that we get a core dump
		of this corruption. */
		ut_ad(0);
osku's avatar
osku committed
1271 1272 1273 1274 1275 1276 1277

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

		return;
	}

1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288
	if (UNIV_UNLIKELY(!log)) {
		/* The last page freed in BtrBulk::finish() must be
		written with redo logging disabled for the page
		itself. The modifications of the allocation data
		structures are covered by redo log. */
	} else if (byte* log_ptr = mlog_open(mtr, 11)) {
		log_ptr = mlog_write_initial_log_record_low(
			MLOG_INIT_FREE_PAGE, space->id, offset, log_ptr, mtr);
		mlog_close(mtr, log_ptr);
	}

1289
	const ulint	bit = offset % FSP_EXTENT_SIZE;
1290

1291
	xdes_set_free<true>(descr, bit, mtr);
osku's avatar
osku committed
1292

1293
	frag_n_used = mach_read_from_4(header + FSP_FRAG_N_USED);
1294

osku's avatar
osku committed
1295 1296 1297
	if (state == XDES_FULL_FRAG) {
		/* The fragment was full: move it to another list */
		flst_remove(header + FSP_FULL_FRAG, descr + XDES_FLST_NODE,
1298
			    mtr);
osku's avatar
osku committed
1299 1300
		xdes_set_state(descr, XDES_FREE_FRAG, mtr);
		flst_add_last(header + FSP_FREE_FRAG, descr + XDES_FLST_NODE,
1301
			      mtr);
osku's avatar
osku committed
1302
		mlog_write_ulint(header + FSP_FRAG_N_USED,
1303 1304
				 frag_n_used + FSP_EXTENT_SIZE - 1,
				 MLOG_4BYTES, mtr);
osku's avatar
osku committed
1305 1306 1307
	} else {
		ut_a(frag_n_used > 0);
		mlog_write_ulint(header + FSP_FRAG_N_USED, frag_n_used - 1,
1308
				 MLOG_4BYTES, mtr);
osku's avatar
osku committed
1309 1310
	}

1311
	if (!xdes_get_n_used(descr)) {
1312
		/* The extent has become free: move it to another list */
osku's avatar
osku committed
1313
		flst_remove(header + FSP_FREE_FRAG, descr + XDES_FLST_NODE,
1314
			    mtr);
1315
		fsp_free_extent(space, offset, mtr);
1316
	}
osku's avatar
osku committed
1317 1318
}

1319 1320 1321
/** Return an extent to the free list of a space.
@param[in,out]	space		tablespace
@param[in]	offset		page number in the extent
1322
@param[in,out]	mtr		mini-transaction */
1323
static void fsp_free_extent(fil_space_t* space, page_no_t offset, mtr_t* mtr)
osku's avatar
osku committed
1324 1325 1326
{
	fsp_header_t*	header;
	xdes_t*		descr;
1327

1328
	ut_ad(mtr_memo_contains(mtr, &space->latch, MTR_MEMO_X_LOCK));
osku's avatar
osku committed
1329

1330
	header = fsp_get_space_header(space, mtr);
osku's avatar
osku committed
1331

1332
	descr = xdes_get_descriptor_with_space_hdr(
1333
		header, space, offset, mtr);
osku's avatar
osku committed
1334

1335
	ut_a(xdes_get_state(descr, mtr) != XDES_FREE);
osku's avatar
osku committed
1336 1337 1338 1339

	xdes_init(descr, mtr);

	flst_add_last(header + FSP_FREE, descr + XDES_FLST_NODE, mtr);
1340
	space->free_len++;
osku's avatar
osku committed
1341 1342
}

1343 1344 1345 1346 1347 1348
/** @return Number of segment inodes which fit on a single page */
inline ulint FSP_SEG_INODES_PER_PAGE(ulint physical_size)
{
	return (physical_size - FSEG_ARR_OFFSET - 10) / FSEG_INODE_SIZE;
}

1349 1350 1351 1352
/** Returns the nth inode slot on an inode page.
@param[in]	page		segment inode page
@param[in]	i		inode index on page
@return segment inode */
1353 1354
#define fsp_seg_inode_page_get_nth_inode(page, i)	\
	FSEG_ARR_OFFSET + FSEG_INODE_SIZE * i + page
osku's avatar
osku committed
1355

1356 1357
/** Looks for a used segment inode on a segment inode page.
@param[in]	page		segment inode page
1358
@param[in]	physical_size	page size
1359
@return segment inode index, or ULINT_UNDEFINED if not found */
osku's avatar
osku committed
1360 1361
static
ulint
1362
fsp_seg_inode_page_find_used(const page_t* page, ulint physical_size)
osku's avatar
osku committed
1363
{
1364 1365 1366 1367 1368
	for (ulint i = 0; i < FSP_SEG_INODES_PER_PAGE(physical_size); i++) {
		if (!mach_read_from_8(
			    FSEG_ID
			    + fsp_seg_inode_page_get_nth_inode(page, i))) {
			continue;
osku's avatar
osku committed
1369
		}
1370 1371 1372 1373 1374
		/* This is used */
		ut_ad(FSEG_MAGIC_N_VALUE == mach_read_from_4(
			      FSEG_MAGIC_N
			      + fsp_seg_inode_page_get_nth_inode(page, i)));
		return i;
osku's avatar
osku committed
1375 1376 1377 1378 1379
	}

	return(ULINT_UNDEFINED);
}

1380 1381 1382
/** Looks for an unused segment inode on a segment inode page.
@param[in]	page		segment inode page
@param[in]	i		search forward starting from this index
1383
@param[in]	physical_size	page size
1384
@return segment inode index, or ULINT_UNDEFINED if not found */
osku's avatar
osku committed
1385 1386
static
ulint
1387
fsp_seg_inode_page_find_free(const page_t* page, ulint i, ulint physical_size)
osku's avatar
osku committed
1388
{
1389
	for (; i < FSP_SEG_INODES_PER_PAGE(physical_size); i++) {
1390 1391 1392
		if (!mach_read_from_8(
			    FSEG_ID
			    + fsp_seg_inode_page_get_nth_inode(page, i))) {
osku's avatar
osku committed
1393
			/* This is unused */
1394
			return i;
osku's avatar
osku committed
1395
		}
1396

1397 1398 1399
		ut_ad(FSEG_MAGIC_N_VALUE == mach_read_from_4(
			      FSEG_MAGIC_N
			      + fsp_seg_inode_page_get_nth_inode(page, i)));
osku's avatar
osku committed
1400 1401
	}

1402
	return ULINT_UNDEFINED;
osku's avatar
osku committed
1403 1404
}

1405 1406 1407 1408 1409 1410
/** Allocate a file segment inode page.
@param[in,out]	space		tablespace
@param[in,out]	space_header	tablespace header
@param[in,out]	mtr		mini-transaction
@return whether the allocation succeeded */
MY_ATTRIBUTE((nonnull, warn_unused_result))
osku's avatar
osku committed
1411
static
1412
bool
osku's avatar
osku committed
1413
fsp_alloc_seg_inode_page(
1414 1415 1416
	fil_space_t*	space,
	fsp_header_t*	space_header,
	mtr_t*		mtr)
osku's avatar
osku committed
1417
{
1418
	buf_block_t*	block;
osku's avatar
osku committed
1419

1420
	ut_ad(page_offset(space_header) == FSP_HEADER_OFFSET);
1421
	ut_ad(page_get_space_id(page_align(space_header)) == space->id);
1422

1423
	block = fsp_alloc_free_page(space, 0, RW_SX_LATCH, mtr, mtr);
osku's avatar
osku committed
1424

1425
	if (block == NULL) {
osku's avatar
osku committed
1426

1427
		return(false);
osku's avatar
osku committed
1428 1429
	}

1430
	buf_block_dbg_add_level(block, SYNC_FSP_PAGE);
1431
	ut_ad(rw_lock_get_sx_lock_count(&block->lock) == 1);
osku's avatar
osku committed
1432

1433
	mlog_write_ulint(block->frame + FIL_PAGE_TYPE, FIL_PAGE_INODE,
1434
			 MLOG_2BYTES, mtr);
osku's avatar
osku committed
1435

1436 1437 1438 1439 1440
#ifdef UNIV_DEBUG
	const byte* inode = FSEG_ID + FSEG_ARR_OFFSET + block->frame;
	for (ulint i = FSP_SEG_INODES_PER_PAGE(space->physical_size()); i--;
	     inode += FSEG_INODE_SIZE) {
		ut_ad(!mach_read_from_8(inode));
osku's avatar
osku committed
1441
	}
1442
#endif
osku's avatar
osku committed
1443

1444 1445
	flst_add_last(
		space_header + FSP_SEG_INODES_FREE,
1446
		block->frame + FSEG_INODE_PAGE_NODE, mtr);
1447

1448
	return(true);
osku's avatar
osku committed
1449 1450
}

1451 1452 1453 1454 1455 1456 1457
/** Allocate a file segment inode.
@param[in,out]	space		tablespace
@param[in,out]	space_header	tablespace header
@param[in,out]	mtr		mini-transaction
@return segment inode
@retval NULL if not enough space */
MY_ATTRIBUTE((nonnull, warn_unused_result))
osku's avatar
osku committed
1458 1459 1460
static
fseg_inode_t*
fsp_alloc_seg_inode(
1461 1462 1463
	fil_space_t*	space,
	fsp_header_t*	space_header,
	mtr_t*		mtr)
osku's avatar
osku committed
1464
{
1465
	buf_block_t*	block;
osku's avatar
osku committed
1466 1467
	page_t*		page;
	fseg_inode_t*	inode;
1468

1469 1470
	ut_ad(page_offset(space_header) == FSP_HEADER_OFFSET);

1471 1472
	/* Allocate a new segment inode page if needed. */
	if (flst_get_len(space_header + FSP_SEG_INODES_FREE) == 0
1473
	    && !fsp_alloc_seg_inode_page(space, space_header, mtr)) {
1474
		return(NULL);
osku's avatar
osku committed
1475
	}
1476
	const page_id_t		page_id(
1477
		space->id,
1478 1479
		flst_get_first(space_header + FSP_SEG_INODES_FREE, mtr).page);

1480
	block = buf_page_get(page_id, space->zip_size(), RW_SX_LATCH, mtr);
1481
	buf_block_dbg_add_level(block, SYNC_FSP_PAGE);
1482
	fil_block_check_type(*block, FIL_PAGE_INODE, mtr);
1483

1484
	page = buf_block_get_frame(block);
osku's avatar
osku committed
1485

1486 1487
	const ulint physical_size = space->physical_size();

1488
	ulint n = fsp_seg_inode_page_find_free(page, 0, physical_size);
osku's avatar
osku committed
1489

1490
	ut_a(n < FSP_SEG_INODES_PER_PAGE(physical_size));
osku's avatar
osku committed
1491

1492
	inode = fsp_seg_inode_page_get_nth_inode(page, n);
osku's avatar
osku committed
1493 1494

	if (ULINT_UNDEFINED == fsp_seg_inode_page_find_free(page, n + 1,
1495
							    physical_size)) {
osku's avatar
osku committed
1496 1497 1498 1499
		/* There are no other unused headers left on the page: move it
		to another list */

		flst_remove(space_header + FSP_SEG_INODES_FREE,
1500
			    page + FSEG_INODE_PAGE_NODE, mtr);
osku's avatar
osku committed
1501 1502

		flst_add_last(space_header + FSP_SEG_INODES_FULL,
1503
			      page + FSEG_INODE_PAGE_NODE, mtr);
osku's avatar
osku committed
1504 1505
	}

1506
	ut_ad(!mach_read_from_8(inode + FSEG_ID)
1507
	      || mach_read_from_4(inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE);
1508
	return(inode);
osku's avatar
osku committed
1509 1510
}

1511
/** Frees a file segment inode.
1512
@param[in,out]	space		tablespace
1513 1514
@param[in,out]	inode		segment inode
@param[in,out]	mtr		mini-transaction */
1515
static void fsp_free_seg_inode(
1516
	fil_space_t*		space,
1517 1518
	fseg_inode_t*		inode,
	mtr_t*			mtr)
osku's avatar
osku committed
1519 1520 1521
{
	page_t*		page;
	fsp_header_t*	space_header;
1522

1523
	ut_d(space->modify_check(*mtr));
1524

1525
	page = page_align(inode);
osku's avatar
osku committed
1526

1527
	space_header = fsp_get_space_header(space, mtr);
osku's avatar
osku committed
1528 1529 1530

	ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE);

1531 1532
	const ulint physical_size = space->physical_size();

1533
	if (ULINT_UNDEFINED
1534
	    == fsp_seg_inode_page_find_free(page, 0, physical_size)) {
osku's avatar
osku committed
1535 1536 1537 1538

		/* Move the page to another list */

		flst_remove(space_header + FSP_SEG_INODES_FULL,
1539
			    page + FSEG_INODE_PAGE_NODE, mtr);
osku's avatar
osku committed
1540 1541

		flst_add_last(space_header + FSP_SEG_INODES_FREE,
1542
			      page + FSEG_INODE_PAGE_NODE, mtr);
osku's avatar
osku committed
1543 1544
	}

1545
	mlog_write_ull(inode + FSEG_ID, 0, mtr);
1546
	mlog_write_ulint(inode + FSEG_MAGIC_N, 0xfa051ce3, MLOG_4BYTES, mtr);
1547

1548
	if (ULINT_UNDEFINED
1549
	    == fsp_seg_inode_page_find_used(page, physical_size)) {
osku's avatar
osku committed
1550 1551 1552 1553

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

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

1556
		fsp_free_page(space, page_get_page_no(page), true, mtr);
osku's avatar
osku committed
1557 1558 1559
	}
}

1560 1561 1562
/** Returns the file segment inode, page x-latched.
@param[in]	header		segment header
@param[in]	space		space id
1563
@param[in]	zip_size	ROW_FORMAT=COMPRESSED page size, or 0
1564 1565 1566
@param[in,out]	mtr		mini-transaction
@param[out]	block		inode block, or NULL to ignore
@return segment inode, page x-latched; NULL if the inode is free */
osku's avatar
osku committed
1567 1568
static
fseg_inode_t*
1569
fseg_inode_try_get(
1570 1571
	fseg_header_t*		header,
	ulint			space,
1572
	ulint			zip_size,
1573 1574
	mtr_t*			mtr,
	buf_block_t**		block)
osku's avatar
osku committed
1575 1576 1577 1578 1579 1580
{
	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);
1581
	ut_ad(space == mach_read_from_4(header + FSEG_HDR_SPACE));
1582

1583
	inode = fut_get_ptr(space, zip_size, inode_addr, RW_SX_LATCH, mtr,
1584
			    block);
1585

1586
	if (UNIV_UNLIKELY(!mach_read_from_8(inode + FSEG_ID))) {
1587 1588 1589 1590 1591 1592

		inode = NULL;
	} else {
		ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N)
		      == FSEG_MAGIC_N_VALUE);
	}
osku's avatar
osku committed
1593 1594 1595 1596

	return(inode);
}

1597 1598 1599
/** Returns the file segment inode, page x-latched.
@param[in]	header		segment header
@param[in]	space		space id
1600
@param[in]	zip_size	ROW_FORMAT=COMPRESSED page size, or 0
1601 1602 1603
@param[in,out]	mtr		mini-transaction
@param[out]	block		inode block
@return segment inode, page x-latched */
1604 1605 1606
static
fseg_inode_t*
fseg_inode_get(
1607 1608
	fseg_header_t*		header,
	ulint			space,
1609
	ulint			zip_size,
1610 1611
	mtr_t*			mtr,
	buf_block_t**		block = NULL)
1612 1613
{
	fseg_inode_t*	inode
1614
		= fseg_inode_try_get(header, space, zip_size, mtr, block);
1615 1616 1617 1618
	ut_a(inode);
	return(inode);
}

1619
/**********************************************************************//**
1620
Gets the page number from the nth fragment page slot.
1621
@return page number, FIL_NULL if not in use */
osku's avatar
osku committed
1622 1623 1624 1625
UNIV_INLINE
ulint
fseg_get_nth_frag_page_no(
/*======================*/
1626 1627
	fseg_inode_t*	inode,	/*!< in: segment inode */
	ulint		n,	/*!< in: slot index */
Sergei Golubchik's avatar
Sergei Golubchik committed
1628
	mtr_t*		mtr MY_ATTRIBUTE((unused)))
1629
				/*!< in/out: mini-transaction */
osku's avatar
osku committed
1630 1631 1632
{
	ut_ad(inode && mtr);
	ut_ad(n < FSEG_FRAG_ARR_N_SLOTS);
1633
	ut_ad(mtr_memo_contains_page(mtr, inode, MTR_MEMO_PAGE_SX_FIX));
1634
	ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE);
osku's avatar
osku committed
1635
	return(mach_read_from_4(inode + FSEG_FRAG_ARR
1636
				+ n * FSEG_FRAG_SLOT_SIZE));
osku's avatar
osku committed
1637 1638
}

1639
/**********************************************************************//**
osku's avatar
osku committed
1640 1641 1642 1643 1644
Sets the page number in the nth fragment page slot. */
UNIV_INLINE
void
fseg_set_nth_frag_page_no(
/*======================*/
1645 1646 1647
	fseg_inode_t*	inode,	/*!< in: segment inode */
	ulint		n,	/*!< in: slot index */
	ulint		page_no,/*!< in: page number to set */
1648
	mtr_t*		mtr)	/*!< in/out: mini-transaction */
osku's avatar
osku committed
1649 1650 1651
{
	ut_ad(inode && mtr);
	ut_ad(n < FSEG_FRAG_ARR_N_SLOTS);
1652
	ut_ad(mtr_memo_contains_page(mtr, inode, MTR_MEMO_PAGE_SX_FIX));
1653
	ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE);
osku's avatar
osku committed
1654 1655

	mlog_write_ulint(inode + FSEG_FRAG_ARR + n * FSEG_FRAG_SLOT_SIZE,
1656
			 page_no, MLOG_4BYTES, mtr);
osku's avatar
osku committed
1657 1658
}

1659
/**********************************************************************//**
1660
Finds a fragment page slot which is free.
1661
@return slot index; ULINT_UNDEFINED if none found */
osku's avatar
osku committed
1662 1663 1664 1665
static
ulint
fseg_find_free_frag_page_slot(
/*==========================*/
1666
	fseg_inode_t*	inode,	/*!< in: segment inode */
1667
	mtr_t*		mtr)	/*!< in/out: mini-transaction */
osku's avatar
osku committed
1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685
{
	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);
}

1686
/**********************************************************************//**
1687
Finds a fragment page slot which is used and last in the array.
1688
@return slot index; ULINT_UNDEFINED if none found */
osku's avatar
osku committed
1689 1690 1691 1692
static
ulint
fseg_find_last_used_frag_page_slot(
/*===============================*/
1693
	fseg_inode_t*	inode,	/*!< in: segment inode */
1694
	mtr_t*		mtr)	/*!< in/out: mini-transaction */
osku's avatar
osku committed
1695 1696 1697 1698 1699 1700 1701
{
	ulint	i;
	ulint	page_no;

	ut_ad(inode && mtr);

	for (i = 0; i < FSEG_FRAG_ARR_N_SLOTS; i++) {
1702 1703
		page_no = fseg_get_nth_frag_page_no(
			inode, FSEG_FRAG_ARR_N_SLOTS - i - 1, mtr);
osku's avatar
osku committed
1704 1705 1706 1707 1708 1709 1710 1711 1712 1713

		if (page_no != FIL_NULL) {

			return(FSEG_FRAG_ARR_N_SLOTS - i - 1);
		}
	}

	return(ULINT_UNDEFINED);
}

1714
/**********************************************************************//**
1715
Calculates reserved fragment page slots.
1716
@return number of fragment pages */
osku's avatar
osku committed
1717 1718 1719 1720
static
ulint
fseg_get_n_frag_pages(
/*==================*/
1721
	fseg_inode_t*	inode,	/*!< in: segment inode */
1722
	mtr_t*		mtr)	/*!< in/out: mini-transaction */
osku's avatar
osku committed
1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737
{
	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);
}

1738
/**********************************************************************//**
1739
Creates a new segment.
1740 1741
@return the block where the segment header is placed, x-latched, NULL
if could not create segment because of lack of space */
1742
buf_block_t*
1743 1744
fseg_create(
	fil_space_t* space, /*!< in,out: tablespace */
1745
	ulint	page,	/*!< in: page where the segment header is placed: if
osku's avatar
osku committed
1746 1747 1748
			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 */
1749
	ulint	byte_offset, /*!< in: byte offset of the created segment header
osku's avatar
osku committed
1750
			on the page */
1751 1752 1753
	mtr_t*	mtr,
   	bool	has_done_reservation) /*!< in: whether the caller
			has already done the reservation for the pages with
osku's avatar
osku committed
1754 1755 1756 1757 1758 1759 1760
			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 */
{
	fsp_header_t*	space_header;
	fseg_inode_t*	inode;
1761
	ib_id_t		seg_id;
1762 1763
	buf_block_t*	block	= 0; /* remove warning */
	fseg_header_t*	header	= 0; /* remove warning */
osku's avatar
osku committed
1764 1765 1766
	ulint		n_reserved;
	ulint		i;

1767
	DBUG_ENTER("fseg_create");
1768

osku's avatar
osku committed
1769
	ut_ad(mtr);
1770
	ut_ad(byte_offset + FSEG_HEADER_SIZE
1771
	      <= srv_page_size - FIL_PAGE_DATA_END);
osku's avatar
osku committed
1772

1773
	mtr_x_lock(&space->latch, mtr);
1774
	ut_d(space->modify_check(*mtr));
1775

osku's avatar
osku committed
1776
	if (page != 0) {
1777 1778
		block = buf_page_get(page_id_t(space->id, page),
				     space->zip_size(),
1779 1780
				     RW_SX_LATCH, mtr);

1781
		header = byte_offset + buf_block_get_frame(block);
1782

1783
		const ulint	type = space->id == TRX_SYS_SPACE
1784 1785 1786 1787
			&& page == TRX_SYS_PAGE_NO
			? FIL_PAGE_TYPE_TRX_SYS
			: FIL_PAGE_TYPE_SYS;

1788
		fil_block_check_type(*block, type, mtr);
1789
	}
osku's avatar
osku committed
1790

1791
	if (!has_done_reservation
1792
	    && !fsp_reserve_free_extents(&n_reserved, space, 2,
1793 1794
					 FSP_NORMAL, mtr)) {
		DBUG_RETURN(NULL);
osku's avatar
osku committed
1795 1796
	}

1797
	space_header = fsp_get_space_header(space, mtr);
osku's avatar
osku committed
1798

1799
	inode = fsp_alloc_seg_inode(space, space_header, mtr);
osku's avatar
osku committed
1800 1801 1802 1803 1804 1805 1806 1807

	if (inode == NULL) {
		goto funct_exit;
	}

	/* Read the next segment id from space header and increment the
	value in space header */

1808
	seg_id = mach_read_from_8(space_header + FSP_SEG_ID);
osku's avatar
osku committed
1809

1810
	mlog_write_ull(space_header + FSP_SEG_ID, seg_id + 1, mtr);
osku's avatar
osku committed
1811

1812
	mlog_write_ull(inode + FSEG_ID, seg_id, mtr);
1813
	mlog_write_ulint(inode + FSEG_NOT_FULL_N_USED, 0, MLOG_4BYTES, mtr);
osku's avatar
osku committed
1814 1815 1816 1817 1818 1819

	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,
1820
			 MLOG_4BYTES, mtr);
osku's avatar
osku committed
1821 1822 1823 1824 1825
	for (i = 0; i < FSEG_FRAG_ARR_N_SLOTS; i++) {
		fseg_set_nth_frag_page_no(inode, i, FIL_NULL, mtr);
	}

	if (page == 0) {
1826
		block = fseg_alloc_free_page_low(space,
1827 1828 1829 1830 1831 1832 1833 1834 1835 1836
						 inode, 0, FSP_UP, RW_SX_LATCH,
						 mtr, mtr
#ifdef UNIV_DEBUG
						 , has_done_reservation
#endif /* UNIV_DEBUG */
						 );

		/* The allocation cannot fail if we have already reserved a
		space for the page. */
		ut_ad(!has_done_reservation || block != NULL);
1837

1838
		if (block == NULL) {
1839
			fsp_free_seg_inode(space, inode, mtr);
osku's avatar
osku committed
1840 1841 1842
			goto funct_exit;
		}

1843
		ut_ad(rw_lock_get_sx_lock_count(&block->lock) == 1);
1844

1845
		header = byte_offset + buf_block_get_frame(block);
1846
		mlog_write_ulint(buf_block_get_frame(block) + FIL_PAGE_TYPE,
1847
				 FIL_PAGE_TYPE_SYS, MLOG_2BYTES, mtr);
1848
	}
osku's avatar
osku committed
1849 1850

	mlog_write_ulint(header + FSEG_HDR_OFFSET,
1851
			 page_offset(inode), MLOG_2BYTES, mtr);
osku's avatar
osku committed
1852 1853

	mlog_write_ulint(header + FSEG_HDR_PAGE_NO,
1854 1855
			 page_get_page_no(page_align(inode)),
			 MLOG_4BYTES, mtr);
osku's avatar
osku committed
1856

1857
	mlog_write_ulint(header + FSEG_HDR_SPACE, space->id, MLOG_4BYTES, mtr);
osku's avatar
osku committed
1858 1859

funct_exit:
1860
	if (!has_done_reservation) {
1861
		space->release_free_extents(n_reserved);
osku's avatar
osku committed
1862
	}
1863

1864
	DBUG_RETURN(block);
osku's avatar
osku committed
1865 1866
}

1867
/**********************************************************************//**
osku's avatar
osku committed
1868
Calculates the number of pages reserved by a segment, and how many pages are
1869
currently used.
1870
@return number of reserved pages */
osku's avatar
osku committed
1871 1872 1873 1874
static
ulint
fseg_n_reserved_pages_low(
/*======================*/
1875
	fseg_inode_t*	inode,	/*!< in: segment inode */
1876 1877
	ulint*		used,	/*!< out: number of pages used (not
				more than reserved) */
1878
	mtr_t*		mtr)	/*!< in/out: mini-transaction */
osku's avatar
osku committed
1879 1880 1881 1882
{
	ulint	ret;

	ut_ad(inode && used && mtr);
1883
	ut_ad(mtr_memo_contains_page(mtr, inode, MTR_MEMO_PAGE_SX_FIX));
1884

1885 1886
	*used = mach_read_from_4(inode + FSEG_NOT_FULL_N_USED)
		+ FSP_EXTENT_SIZE * flst_get_len(inode + FSEG_FULL)
osku's avatar
osku committed
1887 1888 1889
		+ fseg_get_n_frag_pages(inode, mtr);

	ret = fseg_get_n_frag_pages(inode, mtr)
1890 1891 1892
		+ FSP_EXTENT_SIZE * flst_get_len(inode + FSEG_FREE)
		+ FSP_EXTENT_SIZE * flst_get_len(inode + FSEG_NOT_FULL)
		+ FSP_EXTENT_SIZE * flst_get_len(inode + FSEG_FULL);
osku's avatar
osku committed
1893 1894 1895 1896

	return(ret);
}

1897
/**********************************************************************//**
osku's avatar
osku committed
1898
Calculates the number of pages reserved by a segment, and how many pages are
1899
currently used.
1900
@return number of reserved pages */
osku's avatar
osku committed
1901 1902 1903
ulint
fseg_n_reserved_pages(
/*==================*/
1904 1905
	fseg_header_t*	header,	/*!< in: segment header */
	ulint*		used,	/*!< out: number of pages used (<= reserved) */
1906
	mtr_t*		mtr)	/*!< in/out: mini-transaction */
osku's avatar
osku committed
1907 1908 1909
{
	ulint		ret;
	fseg_inode_t*	inode;
1910 1911
	ulint		space_id;
	fil_space_t*	space;
osku's avatar
osku committed
1912

1913 1914
	space_id = page_get_space_id(page_align(header));
	space = mtr_x_lock_space(space_id, mtr);
1915

1916
	inode = fseg_inode_get(header, space_id, space->zip_size(), mtr);
1917

osku's avatar
osku committed
1918 1919 1920 1921 1922
	ret = fseg_n_reserved_pages_low(inode, used, mtr);

	return(ret);
}

1923
/** Tries to fill the free list of a segment with consecutive free extents.
osku's avatar
osku committed
1924 1925
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
1926 1927
the hint onward.
@param[in]	inode		segment inode
1928
@param[in]	space		tablespace
1929 1930 1931
@param[in]	hint		hint which extent would be good as the first
extent
@param[in,out]	mtr		mini-transaction */
osku's avatar
osku committed
1932 1933 1934
static
void
fseg_fill_free_list(
1935
	fseg_inode_t*		inode,
1936
	fil_space_t*		space,
1937 1938
	ulint			hint,
	mtr_t*			mtr)
osku's avatar
osku committed
1939 1940 1941
{
	xdes_t*	descr;
	ulint	i;
1942
	ib_id_t	seg_id;
osku's avatar
osku committed
1943 1944
	ulint	reserved;
	ulint	used;
1945

osku's avatar
osku committed
1946
	ut_ad(inode && mtr);
1947
	ut_ad(!((page_offset(inode) - FSEG_ARR_OFFSET) % FSEG_INODE_SIZE));
1948
	ut_d(space->modify_check(*mtr));
osku's avatar
osku committed
1949 1950 1951 1952 1953 1954 1955 1956 1957 1958

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

1959
	if (flst_get_len(inode + FSEG_FREE) > 0) {
osku's avatar
osku committed
1960 1961 1962 1963
		/* Free list is not empty */

		return;
	}
1964

osku's avatar
osku committed
1965
	for (i = 0; i < FSEG_FREE_LIST_MAX_LEN; i++) {
1966
		descr = xdes_get_descriptor(space, hint, mtr);
osku's avatar
osku committed
1967

1968 1969
		if ((descr == NULL)
		    || (XDES_FREE != xdes_get_state(descr, mtr))) {
osku's avatar
osku committed
1970 1971 1972

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

1973
			return;
osku's avatar
osku committed
1974 1975
		}

1976
		descr = fsp_alloc_free_extent(space, hint, mtr);
1977

osku's avatar
osku committed
1978
		xdes_set_state(descr, XDES_FSEG, mtr);
1979

1980
		seg_id = mach_read_from_8(inode + FSEG_ID);
1981 1982
		ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N)
		      == FSEG_MAGIC_N_VALUE);
1983
		mlog_write_ull(descr + XDES_ID, seg_id, mtr);
osku's avatar
osku committed
1984 1985 1986 1987 1988 1989

		flst_add_last(inode + FSEG_FREE, descr + XDES_FLST_NODE, mtr);
		hint += FSP_EXTENT_SIZE;
	}
}

1990 1991 1992 1993 1994
/** 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!
@param[in]	inode		segment inode
1995
@param[in,out]	space		tablespace
1996 1997 1998
@param[in,out]	mtr		mini-transaction
@retval NULL	if no page could be allocated
@retval block	rw_lock_x_lock_count(&block->lock) == 1 if allocation succeeded
1999
(init_mtr == mtr, or the page was not previously freed in mtr)
2000
@retval block	(not allocated or initialized) otherwise */
osku's avatar
osku committed
2001 2002 2003
static
xdes_t*
fseg_alloc_free_extent(
2004
	fseg_inode_t*		inode,
2005
	fil_space_t*		space,
2006
	mtr_t*			mtr)
osku's avatar
osku committed
2007 2008
{
	xdes_t*		descr;
2009
	ib_id_t		seg_id;
2010 2011
	fil_addr_t	first;

2012
	ut_ad(!((page_offset(inode) - FSEG_ARR_OFFSET) % FSEG_INODE_SIZE));
2013
	ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE);
2014
	ut_d(space->modify_check(*mtr));
2015

2016
	if (flst_get_len(inode + FSEG_FREE) > 0) {
osku's avatar
osku committed
2017
		/* Segment free list is not empty, allocate from it */
2018

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

2021
		descr = xdes_lst_get_descriptor(space, first, mtr);
osku's avatar
osku committed
2022 2023
	} else {
		/* Segment free list was empty, allocate from space */
2024
		descr = fsp_alloc_free_extent(space, 0, mtr);
osku's avatar
osku committed
2025 2026 2027 2028 2029 2030

		if (descr == NULL) {

			return(NULL);
		}

2031
		seg_id = mach_read_from_8(inode + FSEG_ID);
2032

osku's avatar
osku committed
2033
		xdes_set_state(descr, XDES_FSEG, mtr);
2034
		mlog_write_ull(descr + XDES_ID, seg_id, mtr);
osku's avatar
osku committed
2035
		flst_add_last(inode + FSEG_FREE, descr + XDES_FLST_NODE, mtr);
2036

osku's avatar
osku committed
2037
		/* Try to fill the segment free list */
2038
		fseg_fill_free_list(inode, space,
2039 2040
				    xdes_get_offset(descr) + FSP_EXTENT_SIZE,
				    mtr);
osku's avatar
osku committed
2041 2042 2043 2044 2045
	}

	return(descr);
}

2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063
/** Allocates a single free page from a segment.
This function implements the intelligent allocation strategy which tries to
minimize file space fragmentation.
@param[in,out]	space			tablespace
@param[in,out]	seg_inode		segment inode
@param[in]	hint			hint of which page would be desirable
@param[in]	direction		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
@param[in]	rw_latch		RW_SX_LATCH, RW_X_LATCH
@param[in,out]	mtr			mini-transaction
@param[in,out]	init_mtr		mtr or another mini-transaction in
which the page should be initialized. If init_mtr != mtr, but the page is
already latched in mtr, do not initialize the page
@param[in]	has_done_reservation	TRUE if the space has already been
reserved, in this case we will never return NULL
@retval NULL	if no page could be allocated
@retval block	rw_lock_x_lock_count(&block->lock) == 1 if allocation succeeded
2064
(init_mtr == mtr, or the page was not previously freed in mtr)
2065
@retval block	(not allocated or initialized) otherwise */
osku's avatar
osku committed
2066
static
2067
buf_block_t*
osku's avatar
osku committed
2068
fseg_alloc_free_page_low(
2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079
	fil_space_t*		space,
	fseg_inode_t*		seg_inode,
	ulint			hint,
	byte			direction,
	rw_lock_type_t		rw_latch,
	mtr_t*			mtr,
	mtr_t*			init_mtr
#ifdef UNIV_DEBUG
	, ibool			has_done_reservation
#endif /* UNIV_DEBUG */
)
osku's avatar
osku committed
2080 2081
{
	fsp_header_t*	space_header;
2082
	ib_id_t		seg_id;
osku's avatar
osku committed
2083 2084
	ulint		used;
	ulint		reserved;
2085 2086
	xdes_t*		descr;		/*!< extent of the hinted page */
	ulint		ret_page;	/*!< the allocated page offset, FIL_NULL
osku's avatar
osku committed
2087
					if could not be allocated */
2088
	xdes_t*		ret_descr;	/*!< the extent of the allocated page */
osku's avatar
osku committed
2089
	ulint		n;
2090
	const ulint	space_id	= space->id;
2091

osku's avatar
osku committed
2092
	ut_ad((direction >= FSP_UP) && (direction <= FSP_NO_DIR));
2093 2094
	ut_ad(mach_read_from_4(seg_inode + FSEG_MAGIC_N)
	      == FSEG_MAGIC_N_VALUE);
2095
	ut_ad(!((page_offset(seg_inode) - FSEG_ARR_OFFSET) % FSEG_INODE_SIZE));
2096
	seg_id = mach_read_from_8(seg_inode + FSEG_ID);
osku's avatar
osku committed
2097

2098
	ut_ad(seg_id);
2099
	ut_d(space->modify_check(*mtr));
2100
	ut_ad(fil_page_get_type(page_align(seg_inode)) == FIL_PAGE_INODE);
2101

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

2104
	space_header = fsp_get_space_header(space, mtr);
osku's avatar
osku committed
2105

2106
	descr = xdes_get_descriptor_with_space_hdr(space_header, space,
2107
						   hint, mtr);
osku's avatar
osku committed
2108 2109 2110
	if (descr == NULL) {
		/* Hint outside space or too high above free limit: reset
		hint */
2111
		/* The file space header page is always allocated. */
osku's avatar
osku committed
2112
		hint = 0;
2113
		descr = xdes_get_descriptor(space, hint, mtr);
osku's avatar
osku committed
2114
	}
2115

osku's avatar
osku committed
2116
	/* In the big if-else below we look for ret_page and ret_descr */
2117
	/*-------------------------------------------------------------*/
osku's avatar
osku committed
2118
	if ((xdes_get_state(descr, mtr) == XDES_FSEG)
2119
	    && mach_read_from_8(descr + XDES_ID) == seg_id
2120
	    && xdes_is_free(descr, hint % FSP_EXTENT_SIZE)) {
2121
take_hinted_page:
osku's avatar
osku committed
2122 2123 2124 2125
		/* 1. We can take the hinted page
		=================================*/
		ret_descr = descr;
		ret_page = hint;
2126 2127 2128 2129
		/* Skip the check for extending the tablespace. If the
		page hint were not within the size of the tablespace,
		we would have got (descr == NULL) above and reset the hint. */
		goto got_hinted_page;
2130
		/*-----------------------------------------------------------*/
2131 2132 2133
	} else if (xdes_get_state(descr, mtr) == XDES_FREE
		   && reserved - used < reserved / FSEG_FILLFACTOR
		   && used >= FSEG_FRAG_LIMIT) {
osku's avatar
osku committed
2134 2135 2136 2137 2138

		/* 2. We allocate the free extent from space and can take
		=========================================================
		the hinted page
		===============*/
2139
		ret_descr = fsp_alloc_free_extent(space, hint, mtr);
osku's avatar
osku committed
2140 2141

		ut_a(ret_descr == descr);
2142

osku's avatar
osku committed
2143
		xdes_set_state(ret_descr, XDES_FSEG, mtr);
2144
		mlog_write_ull(ret_descr + XDES_ID, seg_id, mtr);
osku's avatar
osku committed
2145
		flst_add_last(seg_inode + FSEG_FREE,
2146
			      ret_descr + XDES_FLST_NODE, mtr);
osku's avatar
osku committed
2147 2148

		/* Try to fill the segment free list */
2149
		fseg_fill_free_list(seg_inode, space,
2150
				    hint + FSP_EXTENT_SIZE, mtr);
2151
		goto take_hinted_page;
2152
		/*-----------------------------------------------------------*/
osku's avatar
osku committed
2153 2154 2155
	} else if ((direction != FSP_NO_DIR)
		   && ((reserved - used) < reserved / FSEG_FILLFACTOR)
		   && (used >= FSEG_FRAG_LIMIT)
2156
		   && (!!(ret_descr
2157
			  = fseg_alloc_free_extent(seg_inode, space, mtr)))) {
osku's avatar
osku committed
2158 2159 2160 2161 2162 2163 2164

		/* 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
		==============================================*/
2165
		ret_page = xdes_get_offset(ret_descr);
osku's avatar
osku committed
2166 2167 2168 2169

		if (direction == FSP_DOWN) {
			ret_page += FSP_EXTENT_SIZE - 1;
		}
2170
		ut_ad(!has_done_reservation || ret_page != FIL_NULL);
2171
		/*-----------------------------------------------------------*/
osku's avatar
osku committed
2172
	} else if ((xdes_get_state(descr, mtr) == XDES_FSEG)
2173
		   && mach_read_from_8(descr + XDES_ID) == seg_id
2174
		   && (!xdes_is_full(descr))) {
osku's avatar
osku committed
2175 2176 2177 2178 2179 2180 2181 2182

		/* 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;
2183
		ret_page = xdes_get_offset(ret_descr)
2184
			+ xdes_find_free(ret_descr, hint % FSP_EXTENT_SIZE);
2185
		ut_ad(!has_done_reservation || ret_page != FIL_NULL);
2186
		/*-----------------------------------------------------------*/
osku's avatar
osku committed
2187 2188 2189 2190 2191
	} else if (reserved - used > 0) {
		/* 5. We take any unused page from the segment
		==============================================*/
		fil_addr_t	first;

2192
		if (flst_get_len(seg_inode + FSEG_NOT_FULL) > 0) {
osku's avatar
osku committed
2193
			first = flst_get_first(seg_inode + FSEG_NOT_FULL,
2194
					       mtr);
2195
		} else if (flst_get_len(seg_inode + FSEG_FREE) > 0) {
osku's avatar
osku committed
2196 2197
			first = flst_get_first(seg_inode + FSEG_FREE, mtr);
		} else {
2198
			ut_ad(!has_done_reservation);
2199
			return(NULL);
osku's avatar
osku committed
2200 2201
		}

2202
		ret_descr = xdes_lst_get_descriptor(space, first, mtr);
2203
		ret_page = xdes_get_offset(ret_descr)
2204
			+ xdes_find_free(ret_descr);
2205
		ut_ad(!has_done_reservation || ret_page != FIL_NULL);
2206
		/*-----------------------------------------------------------*/
osku's avatar
osku committed
2207 2208 2209
	} else if (used < FSEG_FRAG_LIMIT) {
		/* 6. We allocate an individual page from the space
		===================================================*/
2210
		buf_block_t* block = fsp_alloc_free_page(
2211
			space, hint, rw_latch, mtr, init_mtr);
2212 2213

		ut_ad(!has_done_reservation || block != NULL);
2214

2215
		if (block != NULL) {
osku's avatar
osku committed
2216 2217 2218
			/* Put the page in the fragment page array of the
			segment */
			n = fseg_find_free_frag_page_slot(seg_inode, mtr);
2219
			ut_a(n != ULINT_UNDEFINED);
osku's avatar
osku committed
2220

2221
			fseg_set_nth_frag_page_no(
2222
				seg_inode, n, block->page.id.page_no(),
2223
				mtr);
osku's avatar
osku committed
2224
		}
2225 2226 2227 2228

		/* fsp_alloc_free_page() invoked fsp_init_file_page()
		already. */
		return(block);
2229
		/*-----------------------------------------------------------*/
osku's avatar
osku committed
2230 2231 2232
	} else {
		/* 7. We allocate a new extent and take its first page
		======================================================*/
2233
		ret_descr = fseg_alloc_free_extent(seg_inode, space, mtr);
osku's avatar
osku committed
2234 2235 2236

		if (ret_descr == NULL) {
			ret_page = FIL_NULL;
2237
			ut_ad(!has_done_reservation);
osku's avatar
osku committed
2238 2239
		} else {
			ret_page = xdes_get_offset(ret_descr);
2240
			ut_ad(!has_done_reservation || ret_page != FIL_NULL);
2241
		}
osku's avatar
osku committed
2242
	}
2243

osku's avatar
osku committed
2244 2245
	if (ret_page == FIL_NULL) {
		/* Page could not be allocated */
2246

2247
		ut_ad(!has_done_reservation);
2248
		return(NULL);
osku's avatar
osku committed
2249 2250
	}

2251 2252 2253
	if (space->size <= ret_page && !is_system_tablespace(space_id)) {
		/* It must be that we are extending a single-table
		tablespace whose size is still < 64 pages */
2254

2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269
		if (ret_page >= FSP_EXTENT_SIZE) {
			ib::error() << "Error (2): trying to extend"
			" a single-table tablespace " << space_id
			<< " by single page(s) though the"
			<< " space size " << space->size
			<< ". Page no " << ret_page << ".";
			ut_ad(!has_done_reservation);
			return(NULL);
		}

		if (!fsp_try_extend_data_file_with_pages(
			    space, ret_page, space_header, mtr)) {
			/* No disk space left */
			ut_ad(!has_done_reservation);
			return(NULL);
osku's avatar
osku committed
2270 2271 2272
		}
	}

2273 2274 2275 2276
got_hinted_page:
	/* ret_descr == NULL if the block was allocated from free_frag
	(XDES_FREE_FRAG) */
	if (ret_descr != NULL) {
osku's avatar
osku committed
2277 2278 2279
		/* 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. */
2280

2281
		ut_ad(xdes_get_descriptor(space, ret_page, mtr) == ret_descr);
2282
		ut_ad(xdes_is_free(ret_descr, ret_page % FSP_EXTENT_SIZE));
2283 2284

		fseg_mark_page_used(seg_inode, ret_page, ret_descr, mtr);
osku's avatar
osku committed
2285 2286
	}

2287
	return fsp_page_create(space, ret_page, rw_latch, mtr, init_mtr);
osku's avatar
osku committed
2288 2289
}

2290
/**********************************************************************//**
osku's avatar
osku committed
2291 2292
Allocates a single free page from a segment. This function implements
the intelligent allocation strategy which tries to minimize file space
2293
fragmentation.
2294 2295 2296 2297 2298
@retval NULL if no page could be allocated
@retval block, rw_lock_x_lock_count(&block->lock) == 1 if allocation succeeded
(init_mtr == mtr, or the page was not previously freed in mtr)
@retval block (not allocated or initialized) otherwise */
buf_block_t*
osku's avatar
osku committed
2299 2300
fseg_alloc_free_page_general(
/*=========================*/
2301 2302 2303
	fseg_header_t*	seg_header,/*!< in/out: segment header */
	ulint		hint,	/*!< in: hint of which page would be
				desirable */
2304
	byte		direction,/*!< in: if the new page is needed because
osku's avatar
osku committed
2305 2306 2307 2308
				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 */
2309
	ibool		has_done_reservation, /*!< in: TRUE if the caller has
osku's avatar
osku committed
2310 2311 2312 2313
				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 */
2314
	mtr_t*		mtr,	/*!< in/out: mini-transaction */
2315 2316 2317 2318
	mtr_t*		init_mtr)/*!< in/out: mtr or another mini-transaction
				in which the page should be initialized.
				If init_mtr!=mtr, but the page is already
				latched in mtr, do not initialize the page. */
osku's avatar
osku committed
2319 2320
{
	fseg_inode_t*	inode;
2321 2322 2323
	ulint		space_id;
	fil_space_t*	space;
	buf_block_t*	iblock;
2324
	buf_block_t*	block;
osku's avatar
osku committed
2325 2326
	ulint		n_reserved;

2327 2328
	space_id = page_get_space_id(page_align(seg_header));
	space = mtr_x_lock_space(space_id, mtr);
2329 2330
	inode = fseg_inode_get(seg_header, space_id, space->zip_size(),
			       mtr, &iblock);
2331
	fil_block_check_type(*iblock, FIL_PAGE_INODE, mtr);
osku's avatar
osku committed
2332

2333
	if (!has_done_reservation
2334
	    && !fsp_reserve_free_extents(&n_reserved, space, 2,
2335 2336
					 FSP_NORMAL, mtr)) {
		return(NULL);
osku's avatar
osku committed
2337 2338
	}

2339
	block = fseg_alloc_free_page_low(space,
2340
					 inode, hint, direction,
2341 2342 2343 2344 2345 2346 2347 2348 2349 2350
					 RW_X_LATCH, mtr, init_mtr
#ifdef UNIV_DEBUG
					 , has_done_reservation
#endif /* UNIV_DEBUG */
					 );

	/* The allocation cannot fail if we have already reserved a
	space for the page. */
	ut_ad(!has_done_reservation || block != NULL);

osku's avatar
osku committed
2351
	if (!has_done_reservation) {
2352
		space->release_free_extents(n_reserved);
osku's avatar
osku committed
2353 2354
	}

2355
	return(block);
2356 2357
}

2358 2359 2360 2361 2362
/** Check that we have at least n_pages 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.
2363 2364 2365
@param[in,out]	space		tablespace
@param[in,out]	space_header	tablespace header, x-latched
@param[in]	size		size of the tablespace in pages,
2366
must be less than FSP_EXTENT_SIZE
2367
@param[in,out]	mtr		mini-transaction
2368 2369 2370
@param[in]	n_pages		number of pages to reserve
@return true if there were at least n_pages free pages, or we were able
to extend */
osku's avatar
osku committed
2371
static
2372
bool
osku's avatar
osku committed
2373
fsp_reserve_free_pages(
2374 2375 2376
	fil_space_t*	space,
	fsp_header_t*	space_header,
	ulint		size,
2377 2378
	mtr_t*		mtr,
	ulint		n_pages)
osku's avatar
osku committed
2379 2380 2381 2382
{
	xdes_t*	descr;
	ulint	n_used;

2383 2384
	ut_a(!is_system_tablespace(space->id));
	ut_a(size < FSP_EXTENT_SIZE);
osku's avatar
osku committed
2385

2386
	descr = xdes_get_descriptor_with_space_hdr(
2387
		space_header, space, 0, mtr);
2388
	n_used = xdes_get_n_used(descr);
osku's avatar
osku committed
2389 2390 2391

	ut_a(n_used <= size);

2392
	return(size >= n_used + n_pages
2393
	       || fsp_try_extend_data_file_with_pages(
2394
		       space, n_used + n_pages - 1, space_header, mtr));
osku's avatar
osku committed
2395 2396
}

2397
/** Reserves free pages from a tablespace. All mini-transactions which may
osku's avatar
osku committed
2398 2399 2400
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
2401
must be released with function fil_space_t::release_free_extents()!
osku's avatar
osku committed
2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415

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.

2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426
Single-table tablespaces whose size is < FSP_EXTENT_SIZE pages are a special
case. In this function we would liberally reserve several 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 < FSP_EXTENT_SIZE pages. That is why we apply
different rules in that special case, just ensuring that there are n_pages
free pages available.

@param[out]	n_reserved	number of extents actually reserved; if we
				return true and the tablespace size is <
				FSP_EXTENT_SIZE pages, then this can be 0,
				otherwise it is n_ext
2427
@param[in,out]	space		tablespace
2428 2429 2430 2431 2432 2433 2434
@param[in]	n_ext		number of extents to reserve
@param[in]	alloc_type	page reservation type (FSP_BLOB, etc)
@param[in,out]	mtr		the mini transaction
@param[in]	n_pages		for small tablespaces (tablespace size is
				less than FSP_EXTENT_SIZE), number of free
				pages to reserve.
@return true if we were able to make the reservation */
2435
bool
osku's avatar
osku committed
2436
fsp_reserve_free_extents(
2437
	ulint*		n_reserved,
2438
	fil_space_t*	space,
2439
	ulint		n_ext,
2440
	fsp_reserve_t	alloc_type,
2441 2442
	mtr_t*		mtr,
	ulint		n_pages)
osku's avatar
osku committed
2443 2444 2445 2446 2447 2448 2449 2450
{
	fsp_header_t*	space_header;
	ulint		n_free_list_ext;
	ulint		free_limit;
	ulint		size;
	ulint		n_free;
	ulint		n_free_up;
	ulint		reserve;
2451
	size_t		total_reserved = 0;
osku's avatar
osku committed
2452

2453
	ut_ad(mtr);
2454 2455
	*n_reserved = n_ext;

2456
	mtr_x_lock(&space->latch, mtr);
2457
	const ulint physical_size = space->physical_size();
osku's avatar
osku committed
2458

2459
	space_header = fsp_get_space_header(space, mtr);
osku's avatar
osku committed
2460
try_again:
2461 2462
	size = mach_read_from_4(space_header + FSP_SIZE);
	ut_ad(size == space->size_in_header);
2463

2464
	if (size < FSP_EXTENT_SIZE && n_pages < FSP_EXTENT_SIZE / 2) {
osku's avatar
osku committed
2465 2466
		/* Use different rules for small single-table tablespaces */
		*n_reserved = 0;
2467 2468
		return(fsp_reserve_free_pages(space, space_header, size,
					      mtr, n_pages));
osku's avatar
osku committed
2469 2470
	}

2471 2472
	n_free_list_ext = flst_get_len(space_header + FSP_FREE);
	ut_ad(space->free_len == n_free_list_ext);
2473

2474
	free_limit = mach_read_from_4(space_header + FSP_FREE_LIMIT);
2475
	ut_ad(space->free_limit == free_limit);
osku's avatar
osku committed
2476 2477 2478 2479 2480

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

2481 2482 2483 2484 2485 2486
	if (size >= free_limit) {
		n_free_up = (size - free_limit) / FSP_EXTENT_SIZE;
	} else {
		ut_ad(alloc_type == FSP_BLOB);
		n_free_up = 0;
	}
osku's avatar
osku committed
2487 2488 2489

	if (n_free_up > 0) {
		n_free_up--;
2490
		n_free_up -= n_free_up / (physical_size / FSP_EXTENT_SIZE);
osku's avatar
osku committed
2491
	}
2492

osku's avatar
osku committed
2493 2494
	n_free = n_free_list_ext + n_free_up;

2495 2496
	switch (alloc_type) {
	case FSP_NORMAL:
osku's avatar
osku committed
2497 2498 2499 2500 2501 2502 2503 2504 2505 2506
		/* 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;
		}
2507 2508
		break;
	case FSP_UNDO:
osku's avatar
osku committed
2509 2510 2511 2512 2513 2514 2515 2516
		/* 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;
		}
2517 2518 2519
		break;
	case FSP_CLEANING:
	case FSP_BLOB:
Marko Mäkelä's avatar
Marko Mäkelä committed
2520
		reserve = 0;
2521 2522 2523
		break;
	default:
		ut_error;
osku's avatar
osku committed
2524 2525
	}

2526
	if (space->reserve_free_extents(n_free, n_ext)) {
2527
		return(true);
osku's avatar
osku committed
2528 2529
	}
try_to_extend:
Marko Mäkelä's avatar
Marko Mäkelä committed
2530 2531
	if (ulint n = fsp_try_extend_data_file(space, space_header, mtr)) {
		total_reserved += n;
osku's avatar
osku committed
2532 2533 2534
		goto try_again;
	}

2535
	return(false);
osku's avatar
osku committed
2536 2537
}

2538
/********************************************************************//**
osku's avatar
osku committed
2539 2540
Marks a page used. The page must reside within the extents of the given
segment. */
Sergei Golubchik's avatar
Sergei Golubchik committed
2541
static MY_ATTRIBUTE((nonnull))
osku's avatar
osku committed
2542 2543 2544
void
fseg_mark_page_used(
/*================*/
2545 2546
	fseg_inode_t*	seg_inode,/*!< in: segment inode */
	ulint		page,	/*!< in: page offset */
2547
	xdes_t*		descr,  /*!< in: extent descriptor */
2548
	mtr_t*		mtr)	/*!< in/out: mini-transaction */
osku's avatar
osku committed
2549 2550 2551
{
	ulint	not_full_n_used;

2552
	ut_ad(fil_page_get_type(page_align(seg_inode)) == FIL_PAGE_INODE);
2553
	ut_ad(!((page_offset(seg_inode) - FSEG_ARR_OFFSET) % FSEG_INODE_SIZE));
2554 2555
	ut_ad(mach_read_from_4(seg_inode + FSEG_MAGIC_N)
	      == FSEG_MAGIC_N_VALUE);
2556
	ut_ad(!memcmp(seg_inode + FSEG_ID, descr + XDES_ID, 4));
osku's avatar
osku committed
2557

2558
	if (!xdes_get_n_used(descr)) {
osku's avatar
osku committed
2559 2560 2561
		/* We move the extent from the free list to the
		NOT_FULL list */
		flst_remove(seg_inode + FSEG_FREE, descr + XDES_FLST_NODE,
2562
			    mtr);
osku's avatar
osku committed
2563
		flst_add_last(seg_inode + FSEG_NOT_FULL,
2564
			      descr + XDES_FLST_NODE, mtr);
osku's avatar
osku committed
2565 2566
	}

2567
	ut_ad(xdes_is_free(descr, page % FSP_EXTENT_SIZE));
2568

osku's avatar
osku committed
2569
	/* We mark the page as used */
2570
	xdes_set_free<false>(descr, page % FSP_EXTENT_SIZE, mtr);
osku's avatar
osku committed
2571

2572
	not_full_n_used = mach_read_from_4(seg_inode + FSEG_NOT_FULL_N_USED);
osku's avatar
osku committed
2573 2574
	not_full_n_used++;
	mlog_write_ulint(seg_inode + FSEG_NOT_FULL_N_USED, not_full_n_used,
2575
			 MLOG_4BYTES, mtr);
2576
	if (xdes_is_full(descr)) {
osku's avatar
osku committed
2577 2578 2579
		/* We move the extent from the NOT_FULL list to the
		FULL list */
		flst_remove(seg_inode + FSEG_NOT_FULL,
2580
			    descr + XDES_FLST_NODE, mtr);
osku's avatar
osku committed
2581
		flst_add_last(seg_inode + FSEG_FULL,
2582
			      descr + XDES_FLST_NODE, mtr);
2583

osku's avatar
osku committed
2584
		mlog_write_ulint(seg_inode + FSEG_NOT_FULL_N_USED,
2585 2586
				 not_full_n_used - FSP_EXTENT_SIZE,
				 MLOG_4BYTES, mtr);
osku's avatar
osku committed
2587 2588 2589
	}
}

2590 2591
/** Frees a single page of a segment.
@param[in]	seg_inode	segment inode
2592 2593
@param[in,out]	space		tablespace
@param[in]	offset		page number
2594 2595
@param[in]	ahi		whether we may need to drop the adaptive
hash index
2596
@param[in]	log		whether to write MLOG_INIT_FREE_PAGE record
2597
@param[in,out]	mtr		mini-transaction */
osku's avatar
osku committed
2598 2599 2600
static
void
fseg_free_page_low(
2601
	fseg_inode_t*		seg_inode,
2602 2603
	fil_space_t*		space,
	page_no_t		offset,
2604
#ifdef BTR_CUR_HASH_ADAPT
2605
	bool			ahi,
2606
#endif /* BTR_CUR_HASH_ADAPT */
2607
	bool			log,
2608
	mtr_t*			mtr)
osku's avatar
osku committed
2609 2610 2611 2612
{
	xdes_t*	descr;
	ulint	not_full_n_used;
	ulint	state;
2613 2614
	ib_id_t	descr_id;
	ib_id_t	seg_id;
2615

Sergei Golubchik's avatar
Sergei Golubchik committed
2616 2617
	ut_ad(seg_inode != NULL);
	ut_ad(mtr != NULL);
2618 2619
	ut_ad(mach_read_from_4(seg_inode + FSEG_MAGIC_N)
	      == FSEG_MAGIC_N_VALUE);
2620
	ut_ad(!((page_offset(seg_inode) - FSEG_ARR_OFFSET) % FSEG_INODE_SIZE));
2621
	ut_d(space->modify_check(*mtr));
2622
#ifdef BTR_CUR_HASH_ADAPT
osku's avatar
osku committed
2623 2624 2625
	/* Drop search system page hash index if the page is found in
	the pool and is hashed */

2626
	if (ahi) {
2627
		btr_search_drop_page_hash_when_freed(
2628
			page_id_t(space->id, offset));
2629
	}
2630
#endif /* BTR_CUR_HASH_ADAPT */
osku's avatar
osku committed
2631

2632
	descr = xdes_get_descriptor(space, offset, mtr);
osku's avatar
osku committed
2633

2634
	if (xdes_is_free(descr, offset % FSP_EXTENT_SIZE)) {
2635 2636
		ib::fatal() << "InnoDB is trying to free page "
			<< page_id_t(space->id, offset)
2637 2638 2639
			<< " though it is already marked as free in the"
			" tablespace! The tablespace free space info is"
			" corrupt. You may need to dump your tables and"
2640 2641
			" recreate the whole database!"
			<< FORCE_RECOVERY_MSG;
osku's avatar
osku committed
2642
	}
2643

osku's avatar
osku committed
2644 2645 2646 2647 2648
	state = xdes_get_state(descr, mtr);

	if (state != XDES_FSEG) {
		/* The page is in the fragment pages of the segment */

2649
		for (ulint i = 0;; i++) {
osku's avatar
osku committed
2650
			if (fseg_get_nth_frag_page_no(seg_inode, i, mtr)
2651
			    == offset) {
osku's avatar
osku committed
2652 2653

				fseg_set_nth_frag_page_no(seg_inode, i,
2654
							  FIL_NULL, mtr);
osku's avatar
osku committed
2655 2656 2657 2658
				break;
			}
		}

2659
		fsp_free_page(space, offset, log, mtr);
osku's avatar
osku committed
2660 2661 2662
		return;
	}

2663
	/* If we get here, the page is in some extent of the segment */
osku's avatar
osku committed
2664

2665 2666
	descr_id = mach_read_from_8(descr + XDES_ID);
	seg_id = mach_read_from_8(seg_inode + FSEG_ID);
2667

2668
	if (UNIV_UNLIKELY(descr_id != seg_id)) {
osku's avatar
osku committed
2669
		fputs("InnoDB: Dump of the tablespace extent descriptor: ",
2670
		      stderr);
osku's avatar
osku committed
2671 2672 2673 2674 2675
		ut_print_buf(stderr, descr, 40);
		fputs("\nInnoDB: Dump of the segment inode: ", stderr);
		ut_print_buf(stderr, seg_inode, 40);
		putc('\n', stderr);

2676 2677
		ib::fatal() << "InnoDB is trying to free page "
			<< page_id_t(space->id, offset)
2678
			<< ", which does not belong to segment " << descr_id
2679 2680
			<< " but belongs to segment " << seg_id << "."
			<< FORCE_RECOVERY_MSG;
osku's avatar
osku committed
2681 2682
	}

2683
	not_full_n_used = mach_read_from_4(seg_inode + FSEG_NOT_FULL_N_USED);
2684
	if (xdes_is_full(descr)) {
osku's avatar
osku committed
2685 2686
		/* The fragment is full: move it to another list */
		flst_remove(seg_inode + FSEG_FULL,
2687
			    descr + XDES_FLST_NODE, mtr);
osku's avatar
osku committed
2688
		flst_add_last(seg_inode + FSEG_NOT_FULL,
2689
			      descr + XDES_FLST_NODE, mtr);
osku's avatar
osku committed
2690
		mlog_write_ulint(seg_inode + FSEG_NOT_FULL_N_USED,
2691 2692
				 not_full_n_used + FSP_EXTENT_SIZE - 1,
				 MLOG_4BYTES, mtr);
osku's avatar
osku committed
2693 2694 2695
	} else {
		ut_a(not_full_n_used > 0);
		mlog_write_ulint(seg_inode + FSEG_NOT_FULL_N_USED,
2696
				 not_full_n_used - 1, MLOG_4BYTES, mtr);
osku's avatar
osku committed
2697 2698
	}

2699
	const ulint	bit = offset % FSP_EXTENT_SIZE;
2700

2701
	xdes_set_free<true>(descr, bit, mtr);
osku's avatar
osku committed
2702

2703
	if (!xdes_get_n_used(descr)) {
2704
		/* The extent has become free: free it to space */
osku's avatar
osku committed
2705
		flst_remove(seg_inode + FSEG_NOT_FULL,
2706
			    descr + XDES_FLST_NODE, mtr);
2707
		fsp_free_extent(space, offset, mtr);
2708
	}
osku's avatar
osku committed
2709 2710
}

2711
#ifndef BTR_CUR_HASH_ADAPT
2712 2713
# define fseg_free_page_low(inode, space, offset, ahi, log, mtr)	\
	fseg_free_page_low(inode, space, offset, log, mtr)
2714 2715
#endif /* !BTR_CUR_HASH_ADAPT */

2716 2717 2718 2719 2720 2721
/** Free a page in a file segment.
@param[in,out]	seg_header	file segment header
@param[in,out]	space		tablespace
@param[in]	offset		page number
@param[in]	ahi		whether we may need to drop the adaptive
hash index
2722
@param[in]	log		whether to write MLOG_INIT_FREE_PAGE record
2723
@param[in,out]	mtr		mini-transaction */
osku's avatar
osku committed
2724
void
2725
fseg_free_page_func(
2726 2727 2728
	fseg_header_t*	seg_header,
	fil_space_t*	space,
	ulint		offset,
2729
#ifdef BTR_CUR_HASH_ADAPT
2730
	bool		ahi,
2731
#endif /* BTR_CUR_HASH_ADAPT */
2732
	bool		log,
2733
	mtr_t*		mtr)
osku's avatar
osku committed
2734
{
2735
	DBUG_ENTER("fseg_free_page");
2736 2737
	fseg_inode_t*		seg_inode;
	buf_block_t*		iblock;
2738
	mtr_x_lock(&space->latch, mtr);
2739

2740 2741
	DBUG_LOG("fseg_free_page", "space_id: " << space->id
		 << ", page_no: " << offset);
2742

Marko Mäkelä's avatar
Marko Mäkelä committed
2743
	seg_inode = fseg_inode_get(seg_header, space->id, space->zip_size(),
2744
				   mtr,
2745
				   &iblock);
2746
	fil_block_check_type(*iblock, FIL_PAGE_INODE, mtr);
osku's avatar
osku committed
2747

2748
	fseg_free_page_low(seg_inode, space, offset, ahi, log, mtr);
osku's avatar
osku committed
2749

2750
	ut_d(buf_page_set_file_page_was_freed(page_id_t(space->id, offset)));
2751 2752

	DBUG_VOID_RETURN;
osku's avatar
osku committed
2753 2754
}

2755 2756 2757 2758
/** Determine whether a page is free.
@param[in,out]	space	tablespace
@param[in]	page	page number
@return whether the page is marked as free */
2759
bool
2760
fseg_page_is_free(fil_space_t* space, unsigned page)
2761
{
2762
	bool		is_free;
2763
	mtr_t		mtr;
2764 2765
	page_no_t	dpage = xdes_calc_descriptor_page(space->zip_size(),
							  page);
2766

2767 2768
	mtr.start();
	mtr_s_lock(&space->latch, &mtr);
2769

2770 2771 2772
	if (page >= space->free_limit || page >= space->size_in_header) {
		is_free = true;
	} else if (const xdes_t* descr = xdes_get_descriptor_const(
2773
			   space, dpage, page, &mtr)) {
2774
		is_free = xdes_is_free(descr, page % FSP_EXTENT_SIZE);
2775 2776 2777 2778
	} else {
		is_free = true;
	}
	mtr.commit();
2779 2780 2781 2782

	return(is_free);
}

2783 2784 2785 2786 2787 2788 2789 2790 2791
/** Free an extent of a segment to the space free list.
@param[in,out]	seg_inode	segment inode
@param[in,out]	space		tablespace
@param[in]	page		page number in the extent
@param[in]	ahi		whether we may need to drop
				the adaptive hash index
@param[in,out]	mtr		mini-transaction */
MY_ATTRIBUTE((nonnull))
static
osku's avatar
osku committed
2792 2793
void
fseg_free_extent(
2794 2795 2796
	fseg_inode_t*		seg_inode,
	fil_space_t*		space,
	ulint			page,
2797
#ifdef BTR_CUR_HASH_ADAPT
2798
	bool			ahi,
2799
#endif /* BTR_CUR_HASH_ADAPT */
2800
	mtr_t*			mtr)
osku's avatar
osku committed
2801 2802 2803 2804 2805
{
	ulint	first_page_in_extent;
	xdes_t*	descr;
	ulint	not_full_n_used;
	ulint	descr_n_used;
2806

Sergei Golubchik's avatar
Sergei Golubchik committed
2807
	ut_ad(mtr != NULL);
osku's avatar
osku committed
2808

2809
	descr = xdes_get_descriptor(space, page, mtr);
osku's avatar
osku committed
2810 2811

	ut_a(xdes_get_state(descr, mtr) == XDES_FSEG);
2812
	ut_a(!memcmp(descr + XDES_ID, seg_inode + FSEG_ID, 8));
2813 2814
	ut_ad(mach_read_from_4(seg_inode + FSEG_MAGIC_N)
	      == FSEG_MAGIC_N_VALUE);
2815
	ut_d(space->modify_check(*mtr));
osku's avatar
osku committed
2816 2817

	first_page_in_extent = page - (page % FSP_EXTENT_SIZE);
2818

2819
#ifdef BTR_CUR_HASH_ADAPT
2820
	if (ahi) {
2821
		for (ulint i = 0; i < FSP_EXTENT_SIZE; i++) {
2822
			if (!xdes_is_free(descr, i)) {
2823 2824 2825
				/* Drop search system page hash index
				if the page is found in the pool and
				is hashed */
osku's avatar
osku committed
2826

2827
				btr_search_drop_page_hash_when_freed(
2828
					page_id_t(space->id,
2829
						  first_page_in_extent + i));
2830
			}
osku's avatar
osku committed
2831 2832
		}
	}
2833
#endif /* BTR_CUR_HASH_ADAPT */
osku's avatar
osku committed
2834

2835
	if (xdes_is_full(descr)) {
osku's avatar
osku committed
2836
		flst_remove(seg_inode + FSEG_FULL,
2837
			    descr + XDES_FLST_NODE, mtr);
2838
	} else if (!xdes_get_n_used(descr)) {
osku's avatar
osku committed
2839
		flst_remove(seg_inode + FSEG_FREE,
2840
			    descr + XDES_FLST_NODE, mtr);
osku's avatar
osku committed
2841 2842
	} else {
		flst_remove(seg_inode + FSEG_NOT_FULL,
2843
			    descr + XDES_FLST_NODE, mtr);
osku's avatar
osku committed
2844

2845 2846
		not_full_n_used = mach_read_from_4(FSEG_NOT_FULL_N_USED
						   + seg_inode);
2847
		descr_n_used = xdes_get_n_used(descr);
osku's avatar
osku committed
2848 2849
		ut_a(not_full_n_used >= descr_n_used);
		mlog_write_ulint(seg_inode + FSEG_NOT_FULL_N_USED,
2850 2851
				 not_full_n_used - descr_n_used,
				 MLOG_4BYTES, mtr);
osku's avatar
osku committed
2852 2853
	}

2854
	fsp_free_extent(space, page, mtr);
osku's avatar
osku committed
2855

2856
#ifdef UNIV_DEBUG
2857
	for (ulint i = 0; i < FSP_EXTENT_SIZE; i++) {
osku's avatar
osku committed
2858

2859
		buf_page_set_file_page_was_freed(
2860
			page_id_t(space->id, first_page_in_extent + i));
osku's avatar
osku committed
2861
	}
2862
#endif /* UNIV_DEBUG */
osku's avatar
osku committed
2863 2864
}

2865
#ifndef BTR_CUR_HASH_ADAPT
2866 2867
# define fseg_free_extent(inode, space, page, ahi, mtr)	\
	fseg_free_extent(inode, space, page, mtr)
2868 2869
#endif /* !BTR_CUR_HASH_ADAPT */

2870
/**********************************************************************//**
osku's avatar
osku committed
2871 2872 2873
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
2874
mini-transaction.
2875
@return TRUE if freeing completed */
osku's avatar
osku committed
2876
ibool
2877
fseg_free_step_func(
2878
	fseg_header_t*	header,	/*!< in, own: segment header; NOTE: if the header
osku's avatar
osku committed
2879 2880 2881
				resides on the first page of the frag list
				of the segment, this pointer becomes obsolete
				after the last freeing step */
2882
#ifdef BTR_CUR_HASH_ADAPT
2883 2884
	bool		ahi,	/*!< in: whether we may need to drop
				the adaptive hash index */
2885
#endif /* BTR_CUR_HASH_ADAPT */
2886
	mtr_t*		mtr)	/*!< in/out: mini-transaction */
osku's avatar
osku committed
2887 2888 2889 2890 2891
{
	ulint		n;
	ulint		page;
	xdes_t*		descr;
	fseg_inode_t*	inode;
2892
	ulint		space_id;
2893
	ulint		header_page;
osku's avatar
osku committed
2894

2895
	DBUG_ENTER("fseg_free_step");
osku's avatar
osku committed
2896

2897 2898
	space_id = page_get_space_id(page_align(header));
	header_page = page_get_page_no(page_align(header));
2899

2900
	fil_space_t*		space = mtr_x_lock_space(space_id, mtr);
osku's avatar
osku committed
2901

2902
	descr = xdes_get_descriptor(space, header_page, mtr);
osku's avatar
osku committed
2903 2904 2905 2906

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

2907
	ut_a(!xdes_is_free(descr, header_page % FSP_EXTENT_SIZE));
2908
	buf_block_t*		iblock;
2909 2910
	const ulint zip_size = space->zip_size();
	inode = fseg_inode_try_get(header, space_id, zip_size, mtr, &iblock);
2911

2912 2913 2914 2915
	if (inode == NULL) {
		ib::info() << "Double free of inode from "
			<< page_id_t(space_id, header_page);
		DBUG_RETURN(TRUE);
2916
	}
osku's avatar
osku committed
2917

2918
	fil_block_check_type(*iblock, FIL_PAGE_INODE, mtr);
2919
	descr = fseg_get_first_extent(inode, space, mtr);
osku's avatar
osku committed
2920 2921 2922 2923

	if (descr != NULL) {
		/* Free the extent held by the segment */
		page = xdes_get_offset(descr);
2924
		fseg_free_extent(inode, space, page, ahi, mtr);
2925
		DBUG_RETURN(FALSE);
osku's avatar
osku committed
2926 2927 2928 2929 2930 2931 2932
	}

	/* Free a frag page */
	n = fseg_find_last_used_frag_page_slot(inode, mtr);

	if (n == ULINT_UNDEFINED) {
		/* Freeing completed: free the segment inode */
2933
		fsp_free_seg_inode(space, inode, mtr);
osku's avatar
osku committed
2934

2935
		DBUG_RETURN(TRUE);
osku's avatar
osku committed
2936 2937
	}

2938
	fseg_free_page_low(
2939 2940
		inode, space,
		fseg_get_nth_frag_page_no(inode, n, mtr),
2941
		ahi, true, mtr);
osku's avatar
osku committed
2942 2943 2944 2945 2946

	n = fseg_find_last_used_frag_page_slot(inode, mtr);

	if (n == ULINT_UNDEFINED) {
		/* Freeing completed: free the segment inode */
2947
		fsp_free_seg_inode(space, inode, mtr);
osku's avatar
osku committed
2948

2949
		DBUG_RETURN(TRUE);
osku's avatar
osku committed
2950 2951
	}

2952
	DBUG_RETURN(FALSE);
osku's avatar
osku committed
2953 2954
}

2955
/**********************************************************************//**
osku's avatar
osku committed
2956
Frees part of a segment. Differs from fseg_free_step because this function
2957
leaves the header page unfreed.
2958
@return TRUE if freeing completed, except the header page */
osku's avatar
osku committed
2959
ibool
2960
fseg_free_step_not_header_func(
2961
	fseg_header_t*	header,	/*!< in: segment header which must reside on
osku's avatar
osku committed
2962
				the first fragment page of the segment */
2963
#ifdef BTR_CUR_HASH_ADAPT
2964 2965
	bool		ahi,	/*!< in: whether we may need to drop
				the adaptive hash index */
2966
#endif /* BTR_CUR_HASH_ADAPT */
2967
	mtr_t*		mtr)	/*!< in/out: mini-transaction */
osku's avatar
osku committed
2968 2969 2970 2971 2972
{
	ulint		n;
	ulint		page;
	xdes_t*		descr;
	fseg_inode_t*	inode;
2973
	ulint		space_id;
osku's avatar
osku committed
2974
	ulint		page_no;
2975

2976 2977
	space_id = page_get_space_id(page_align(header));
	ut_ad(mtr->is_named_space(space_id));
2978

2979
	fil_space_t*		space = mtr_x_lock_space(space_id, mtr);
2980
	buf_block_t*		iblock;
osku's avatar
osku committed
2981

2982 2983
	inode = fseg_inode_get(header, space_id, space->zip_size(), mtr,
			       &iblock);
2984
	fil_block_check_type(*iblock, FIL_PAGE_INODE, mtr);
osku's avatar
osku committed
2985

2986
	descr = fseg_get_first_extent(inode, space, mtr);
osku's avatar
osku committed
2987 2988 2989 2990 2991

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

2992
		fseg_free_extent(inode, space, page, ahi, mtr);
2993

osku's avatar
osku committed
2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005
		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);
3006

3007
	if (page_no == page_get_page_no(page_align(header))) {
osku's avatar
osku committed
3008 3009 3010

		return(TRUE);
	}
3011

3012
	fseg_free_page_low(inode, space, page_no, ahi, true, mtr);
osku's avatar
osku committed
3013 3014 3015 3016

	return(FALSE);
}

3017 3018 3019 3020
/** 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.
@param[in]	inode		segment inode
3021
@param[in]	space		tablespace
3022 3023
@param[in,out]	mtr		mini-transaction
@return the first extent descriptor, or NULL if none */
3024
MY_ATTRIBUTE((nonnull, warn_unused_result))
osku's avatar
osku committed
3025 3026 3027
static
xdes_t*
fseg_get_first_extent(
3028
	fseg_inode_t*		inode,
3029
	const fil_space_t*	space,
3030
	mtr_t*			mtr)
osku's avatar
osku committed
3031 3032 3033
{
	fil_addr_t	first;

3034
	ut_ad(space->id == page_get_space_id(page_align(inode)));
3035
	ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE);
osku's avatar
osku committed
3036

3037
	if (flst_get_len(inode + FSEG_FULL) > 0) {
osku's avatar
osku committed
3038 3039 3040

		first = flst_get_first(inode + FSEG_FULL, mtr);

3041
	} else if (flst_get_len(inode + FSEG_NOT_FULL) > 0) {
osku's avatar
osku committed
3042 3043 3044

		first = flst_get_first(inode + FSEG_NOT_FULL, mtr);

3045
	} else if (flst_get_len(inode + FSEG_FREE) > 0) {
osku's avatar
osku committed
3046 3047

		first = flst_get_first(inode + FSEG_FREE, mtr);
3048
	} else {
osku's avatar
osku committed
3049 3050 3051
		return(NULL);
	}

3052 3053 3054
	ut_ad(first.page != FIL_NULL);

	return(first.page == FIL_NULL ? NULL
3055
	       : xdes_lst_get_descriptor(space, first, mtr));
osku's avatar
osku committed
3056 3057
}

3058
#ifdef UNIV_BTR_PRINT
3059
/*******************************************************************//**
osku's avatar
osku committed
3060 3061 3062 3063 3064
Writes info of a segment. */
static
void
fseg_print_low(
/*===========*/
3065
	fseg_inode_t*	inode, /*!< in: segment inode */
3066
	mtr_t*		mtr)	/*!< in/out: mini-transaction */
osku's avatar
osku committed
3067 3068 3069 3070 3071 3072 3073 3074 3075 3076
{
	ulint	space;
	ulint	n_used;
	ulint	n_frag;
	ulint	n_free;
	ulint	n_not_full;
	ulint	n_full;
	ulint	reserved;
	ulint	used;
	ulint	page_no;
3077
	ib_id_t	seg_id;
3078

3079
	ut_ad(mtr_memo_contains_page(mtr, inode, MTR_MEMO_PAGE_SX_FIX));
3080 3081
	space = page_get_space_id(page_align(inode));
	page_no = page_get_page_no(page_align(inode));
osku's avatar
osku committed
3082 3083 3084

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

3085
	seg_id = mach_read_from_8(inode + FSEG_ID);
3086
	n_used = mach_read_from_4(inode + FSEG_NOT_FULL_N_USED);
osku's avatar
osku committed
3087
	n_frag = fseg_get_n_frag_pages(inode, mtr);
3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100
	n_free = flst_get_len(inode + FSEG_FREE);
	n_not_full = flst_get_len(inode + FSEG_NOT_FULL);
	n_full = flst_get_len(inode + FSEG_FULL);

	ib::info() << "SEGMENT id " << seg_id
		<< " space " << space << ";"
		<< " page " << page_no << ";"
		<< " res " << reserved << " used " << used << ";"
		<< " full ext " << n_full << ";"
		<< " fragm pages " << n_frag << ";"
		<< " free extents " << n_free << ";"
		<< " not full extents " << n_not_full << ": pages " << n_used;

3101
	ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE);
osku's avatar
osku committed
3102 3103
}

3104
/*******************************************************************//**
osku's avatar
osku committed
3105 3106 3107 3108
Writes info of a segment. */
void
fseg_print(
/*=======*/
3109
	fseg_header_t*	header, /*!< in: segment header */
3110
	mtr_t*		mtr)	/*!< in/out: mini-transaction */
osku's avatar
osku committed
3111 3112
{
	fseg_inode_t*	inode;
3113
	ulint		space_id;
osku's avatar
osku committed
3114

3115 3116
	space_id = page_get_space_id(page_align(header));
	const fil_space_t*	space = mtr_x_lock_space(space_id, mtr);
osku's avatar
osku committed
3117

3118
	inode = fseg_inode_get(header, space_id, space->zip_size(), mtr);
osku's avatar
osku committed
3119 3120 3121

	fseg_print_low(inode, mtr);
}
3122
#endif /* UNIV_BTR_PRINT */
osku's avatar
osku committed
3123

3124
#ifdef UNIV_DEBUG
3125
std::ostream &fseg_header::to_stream(std::ostream &out) const
osku's avatar
osku committed
3126
{
3127 3128 3129 3130 3131
  out << "[fseg_header_t: space="
      << mach_read_from_4(m_header + FSEG_HDR_SPACE)
      << ", page=" << mach_read_from_4(m_header + FSEG_HDR_PAGE_NO)
      << ", offset=" << mach_read_from_2(m_header + FSEG_HDR_OFFSET) << "]";
  return out;
osku's avatar
osku committed
3132
}
3133
#endif /* UNIV_DEBUG */