1/*****************************************************************************
2
3Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved.
4Copyright (c) 2008, Google Inc.
5Copyright (c) 2014, 2020, MariaDB Corporation.
6
7Portions of this file contain modifications contributed and copyrighted by
8Google, Inc. Those modifications are gratefully acknowledged and are described
9briefly in the InnoDB documentation. The contributions by Google are
10incorporated with their permission, and subject to the conditions contained in
11the file COPYING.Google.
12
13This program is free software; you can redistribute it and/or modify it under
14the terms of the GNU General Public License as published by the Free Software
15Foundation; version 2 of the License.
16
17This program is distributed in the hope that it will be useful, but WITHOUT
18ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
19FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
20
21You should have received a copy of the GNU General Public License along with
22this program; if not, write to the Free Software Foundation, Inc.,
2351 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
24
25*****************************************************************************/
26
27/**************************************************//**
28@file include/buf0buf.ic
29The database buffer buf_pool
30
31Created 11/5/1995 Heikki Tuuri
32*******************************************************/
33
34#include "mtr0mtr.h"
35#include "buf0flu.h"
36#include "buf0lru.h"
37#include "buf0rea.h"
38#include "fsp0types.h"
39
40/*********************************************************************//**
41Gets the current size of buffer buf_pool in bytes.
42@return size in bytes */
43UNIV_INLINE
44ulint
45buf_pool_get_curr_size(void)
46/*========================*/
47{
48	return(srv_buf_pool_curr_size);
49}
50
51/********************************************************************//**
52Reads the freed_page_clock of a buffer block.
53@return freed_page_clock */
54UNIV_INLINE
55unsigned
56buf_page_get_freed_page_clock(
57/*==========================*/
58	const buf_page_t*	bpage)	/*!< in: block */
59{
60	/* This is sometimes read without holding buf_pool.mutex. */
61	return(bpage->freed_page_clock);
62}
63
64/********************************************************************//**
65Reads the freed_page_clock of a buffer block.
66@return freed_page_clock */
67UNIV_INLINE
68unsigned
69buf_block_get_freed_page_clock(
70/*===========================*/
71	const buf_block_t*	block)	/*!< in: block */
72{
73	return(buf_page_get_freed_page_clock(&block->page));
74}
75
76/** Determine if a block is still close enough to the MRU end of the LRU list
77meaning that it is not in danger of getting evicted and also implying
78that it has been accessed recently.
79The page must be either buffer-fixed, or its page hash must be locked.
80@param[in]	bpage		buffer pool page
81@return whether bpage is close to MRU end of LRU */
82inline bool buf_page_peek_if_young(const buf_page_t *bpage)
83{
84	/* FIXME: bpage->freed_page_clock is 31 bits */
85	return((buf_pool.freed_page_clock & ((1UL << 31) - 1))
86	       < (bpage->freed_page_clock
87		  + (buf_pool.curr_size
88		     * (BUF_LRU_OLD_RATIO_DIV - buf_pool.LRU_old_ratio)
89		     / (BUF_LRU_OLD_RATIO_DIV * 4))));
90}
91
92/** Determine if a block should be moved to the start of the LRU list if
93there is danger of dropping from the buffer pool.
94@param[in]	bpage		buffer pool page
95@return true if bpage should be made younger */
96inline bool buf_page_peek_if_too_old(const buf_page_t *bpage)
97{
98	if (buf_pool.freed_page_clock == 0) {
99		/* If eviction has not started yet, do not update the
100		statistics or move blocks in the LRU list.  This is
101		either the warm-up phase or an in-memory workload. */
102		return(FALSE);
103	} else if (buf_LRU_old_threshold_ms && bpage->old) {
104		uint32_t access_time = bpage->is_accessed();
105
106		/* It is possible that the below comparison returns an
107		unexpected result. 2^32 milliseconds pass in about 50 days,
108		so if the difference between ut_time_ms() and access_time
109		is e.g. 50 days + 15 ms, then the below will behave as if
110		it is 15 ms. This is known and fixing it would require to
111		increase buf_page_t::access_time from 32 to 64 bits. */
112		if (access_time
113		    && ((ib_uint32_t) (ut_time_ms() - access_time))
114		    >= buf_LRU_old_threshold_ms) {
115			return(TRUE);
116		}
117
118		buf_pool.stat.n_pages_not_made_young++;
119		return false;
120	} else {
121		return !buf_page_peek_if_young(bpage);
122	}
123}
124
125#ifdef UNIV_DEBUG
126/*********************************************************************//**
127Gets a pointer to the memory frame of a block.
128@return pointer to the frame */
129UNIV_INLINE
130buf_frame_t*
131buf_block_get_frame(
132/*================*/
133	const buf_block_t*	block)	/*!< in: pointer to the control block */
134{
135	if (!block) {
136		return NULL;
137	}
138
139	switch (block->page.state()) {
140	case BUF_BLOCK_ZIP_PAGE:
141	case BUF_BLOCK_NOT_USED:
142		ut_error;
143		break;
144	case BUF_BLOCK_FILE_PAGE:
145		ut_a(block->page.buf_fix_count());
146		/* fall through */
147	case BUF_BLOCK_MEMORY:
148	case BUF_BLOCK_REMOVE_HASH:
149		goto ok;
150	}
151	ut_error;
152ok:
153	return((buf_frame_t*) block->frame);
154}
155#endif /* UNIV_DEBUG */
156
157/********************************************************************//**
158Allocates a buf_page_t descriptor. This function must succeed. In case
159of failure we assert in this function.
160@return: the allocated descriptor. */
161UNIV_INLINE
162buf_page_t*
163buf_page_alloc_descriptor(void)
164/*===========================*/
165{
166	buf_page_t*	bpage;
167
168	bpage = (buf_page_t*) ut_zalloc_nokey(sizeof *bpage);
169	ut_ad(bpage);
170	MEM_UNDEFINED(bpage, sizeof *bpage);
171
172	return(bpage);
173}
174
175/********************************************************************//**
176Free a buf_page_t descriptor. */
177UNIV_INLINE
178void
179buf_page_free_descriptor(
180/*=====================*/
181	buf_page_t*	bpage)	/*!< in: bpage descriptor to free. */
182{
183	ut_free(bpage);
184}
185
186/** Allocate a buffer block.
187@return own: the allocated block, in state BUF_BLOCK_MEMORY */
188inline buf_block_t *buf_block_alloc()
189{
190  return buf_LRU_get_free_block(false);
191}
192
193/********************************************************************//**
194Frees a buffer block which does not contain a file page. */
195UNIV_INLINE
196void
197buf_block_free(
198/*===========*/
199	buf_block_t*	block)	/*!< in, own: block to be freed */
200{
201	mysql_mutex_lock(&buf_pool.mutex);
202	buf_LRU_block_free_non_file_page(block);
203	mysql_mutex_unlock(&buf_pool.mutex);
204}
205
206/********************************************************************//**
207Increments the modify clock of a frame by 1. The caller must (1) own the
208buf_pool mutex and block bufferfix count has to be zero, (2) or own an x-lock
209on the block. */
210UNIV_INLINE
211void
212buf_block_modify_clock_inc(
213/*=======================*/
214	buf_block_t*	block)	/*!< in: block */
215{
216#ifdef SAFE_MUTEX
217	/* No latch is acquired for the shared temporary tablespace. */
218	ut_ad(fsp_is_system_temporary(block->page.id().space())
219	      || (mysql_mutex_is_owner(&buf_pool.mutex)
220		  && !block->page.buf_fix_count())
221	      || rw_lock_own_flagged(&block->lock,
222				     RW_LOCK_FLAG_X | RW_LOCK_FLAG_SX));
223#else /* SAFE_MUTEX */
224	/* No latch is acquired for the shared temporary tablespace. */
225	ut_ad(fsp_is_system_temporary(block->page.id().space())
226	      || !block->page.buf_fix_count()
227	      || rw_lock_own_flagged(&block->lock,
228				     RW_LOCK_FLAG_X | RW_LOCK_FLAG_SX));
229#endif /* SAFE_MUTEX */
230	assert_block_ahi_valid(block);
231
232	block->modify_clock++;
233}
234
235/********************************************************************//**
236Returns the value of the modify clock. The caller must have an s-lock
237or x-lock on the block.
238@return value */
239UNIV_INLINE
240ib_uint64_t
241buf_block_get_modify_clock(
242/*=======================*/
243	buf_block_t*	block)	/*!< in: block */
244{
245#ifdef UNIV_DEBUG
246	/* No latch is acquired for the shared temporary tablespace. */
247	if (!fsp_is_system_temporary(block->page.id().space())) {
248		ut_ad(rw_lock_own(&(block->lock), RW_LOCK_S)
249		      || rw_lock_own(&(block->lock), RW_LOCK_X)
250		      || rw_lock_own(&(block->lock), RW_LOCK_SX));
251	}
252#endif /* UNIV_DEBUG */
253
254	return(block->modify_clock);
255}
256
257/*******************************************************************//**
258Increments the bufferfix count. */
259UNIV_INLINE
260void
261buf_block_buf_fix_inc_func(
262/*=======================*/
263#ifdef UNIV_DEBUG
264	const char*	file,	/*!< in: file name */
265	unsigned	line,	/*!< in: line */
266#endif /* UNIV_DEBUG */
267	buf_block_t*	block)	/*!< in/out: block to bufferfix */
268{
269#ifdef UNIV_DEBUG
270	/* No debug latch is acquired if block belongs to system temporary.
271	Debug latch is not of much help if access to block is single
272	threaded. */
273	if (!fsp_is_system_temporary(block->page.id().space())) {
274		ibool   ret;
275		ret = rw_lock_s_lock_nowait(block->debug_latch, file, line);
276		ut_a(ret);
277	}
278#endif /* UNIV_DEBUG */
279
280	block->fix();
281}
282
283/*******************************************************************//**
284Decrements the bufferfix count. */
285UNIV_INLINE
286void
287buf_block_buf_fix_dec(
288/*==================*/
289	buf_block_t*	block)	/*!< in/out: block to bufferunfix */
290{
291#ifdef UNIV_DEBUG
292	/* No debug latch is acquired if block belongs to system temporary.
293	Debug latch is not of much help if access to block is single
294	threaded. */
295	if (!fsp_is_system_temporary(block->page.id().space())) {
296		rw_lock_s_unlock(block->debug_latch);
297	}
298#endif /* UNIV_DEBUG */
299
300	block->unfix();
301}
302
303/********************************************************************//**
304Releases a compressed-only page acquired with buf_page_get_zip(). */
305UNIV_INLINE
306void
307buf_page_release_zip(
308/*=================*/
309	buf_page_t*	bpage)		/*!< in: buffer block */
310{
311	ut_ad(bpage);
312	ut_a(bpage->buf_fix_count());
313
314	switch (bpage->state()) {
315	case BUF_BLOCK_FILE_PAGE:
316#ifdef UNIV_DEBUG
317	{
318		/* No debug latch is acquired if block belongs to system
319		temporary. Debug latch is not of much help if access to block
320		is single threaded. */
321		buf_block_t*	block = reinterpret_cast<buf_block_t*>(bpage);
322		if (!fsp_is_system_temporary(block->page.id().space())) {
323			rw_lock_s_unlock(block->debug_latch);
324		}
325	}
326#endif /* UNIV_DEBUG */
327		/* Fall through */
328	case BUF_BLOCK_ZIP_PAGE:
329		reinterpret_cast<buf_block_t*>(bpage)->unfix();
330		return;
331
332	case BUF_BLOCK_NOT_USED:
333	case BUF_BLOCK_MEMORY:
334	case BUF_BLOCK_REMOVE_HASH:
335		break;
336	}
337
338	ut_error;
339}
340
341/********************************************************************//**
342Releases a latch, if specified. */
343UNIV_INLINE
344void
345buf_page_release_latch(
346/*===================*/
347	buf_block_t*	block,		/*!< in: buffer block */
348	ulint		rw_latch)	/*!< in: RW_S_LATCH, RW_X_LATCH,
349					RW_NO_LATCH */
350{
351#ifdef UNIV_DEBUG
352	/* No debug latch is acquired if block belongs to system
353	temporary. Debug latch is not of much help if access to block
354	is single threaded. */
355	if (!fsp_is_system_temporary(block->page.id().space())) {
356		rw_lock_s_unlock(block->debug_latch);
357	}
358#endif /* UNIV_DEBUG */
359
360	if (rw_latch == RW_S_LATCH) {
361		rw_lock_s_unlock(&block->lock);
362	} else if (rw_latch == RW_SX_LATCH) {
363		rw_lock_sx_unlock(&block->lock);
364	} else if (rw_latch == RW_X_LATCH) {
365		rw_lock_x_unlock(&block->lock);
366	}
367}
368
369#ifdef UNIV_DEBUG
370/*********************************************************************//**
371Adds latch level info for the rw-lock protecting the buffer frame. This
372should be called in the debug version after a successful latching of a
373page if we know the latching order level of the acquired latch. */
374UNIV_INLINE
375void
376buf_block_dbg_add_level(
377/*====================*/
378	buf_block_t*	block,	/*!< in: buffer page
379				where we have acquired latch */
380	latch_level_t	level)	/*!< in: latching order level */
381{
382	sync_check_lock(&block->lock, level);
383}
384#endif /* UNIV_DEBUG */
385
386/********************************************************************//**
387Get buf frame. */
388UNIV_INLINE
389void *
390buf_page_get_frame(
391/*===============*/
392	const buf_page_t*	bpage) /*!< in: buffer pool page */
393{
394	/* In encryption/compression buffer pool page may contain extra
395	buffer where result is stored. */
396	if (bpage->slot && bpage->slot->out_buf) {
397		return bpage->slot->out_buf;
398	} else if (bpage->zip.data) {
399		return bpage->zip.data;
400	} else {
401		return ((buf_block_t*) bpage)->frame;
402	}
403}
404