~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to plugin/innobase/buf/buf0buddy.c

Merge Revision revid:marko.makela@oracle.com-20100505101406-u4low2x26q6itck0 from MySQL InnoDB

Original revid:marko.makela@oracle.com-20100505101406-u4low2x26q6itck0

Original Authors: Marko Mäkelä <marko.makela@oracle.com>
Original commit message:
Merge from mysql-5.1-innodb:

  ------------------------------------------------------------
  revno: 3446
  revision-id: marko.makela@oracle.com-20100505100507-6kcd2hf32hruxbv7
  parent: marko.makela@oracle.com-20100505095328-vetnl0flhmhao7p5
  committer: Marko Mäkelä <marko.makela@oracle.com>
  branch nick: 5.1-innodb
  timestamp: Wed 2010-05-05 13:05:07 +0300
  message:
    Add Valgrind diagnostics to track down Bug #38999.
  ------------------------------------------------------------

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*****************************************************************************
 
2
 
 
3
Copyright (c) 2006, 2010, Innobase Oy. All Rights Reserved.
 
4
 
 
5
This program is free software; you can redistribute it and/or modify it under
 
6
the terms of the GNU General Public License as published by the Free Software
 
7
Foundation; version 2 of the License.
 
8
 
 
9
This program is distributed in the hope that it will be useful, but WITHOUT
 
10
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 
11
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 
12
 
 
13
You should have received a copy of the GNU General Public License along with
 
14
this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
 
15
St, Fifth Floor, Boston, MA 02110-1301 USA
 
16
 
 
17
*****************************************************************************/
 
18
 
 
19
/**************************************************//**
 
20
@file buf/buf0buddy.c
 
21
Binary buddy allocator for compressed pages
 
22
 
 
23
Created December 2006 by Marko Makela
 
24
*******************************************************/
 
25
 
 
26
#define THIS_MODULE
 
27
#include "buf0buddy.h"
 
28
#ifdef UNIV_NONINL
 
29
# include "buf0buddy.ic"
 
30
#endif
 
31
#undef THIS_MODULE
 
32
#include "buf0buf.h"
 
33
#include "buf0lru.h"
 
34
#include "buf0flu.h"
 
35
#include "page0zip.h"
 
36
 
 
37
/**********************************************************************//**
 
38
Get the offset of the buddy of a compressed page frame.
 
39
@return the buddy relative of page */
 
40
UNIV_INLINE
 
41
byte*
 
42
buf_buddy_get(
 
43
/*==========*/
 
44
        byte*   page,   /*!< in: compressed page */
 
45
        ulint   size)   /*!< in: page size in bytes */
 
46
{
 
47
        ut_ad(ut_is_2pow(size));
 
48
        ut_ad(size >= BUF_BUDDY_LOW);
 
49
        ut_ad(size < BUF_BUDDY_HIGH);
 
50
        ut_ad(!ut_align_offset(page, size));
 
51
 
 
52
        if (((ulint) page) & size) {
 
53
                return(page - size);
 
54
        } else {
 
55
                return(page + size);
 
56
        }
 
57
}
 
58
 
 
59
/**********************************************************************//**
 
60
Add a block to the head of the appropriate buddy free list. */
 
61
UNIV_INLINE
 
62
void
 
63
buf_buddy_add_to_free(
 
64
/*==================*/
 
65
        buf_pool_t*     buf_pool,       /*!< in: buffer pool instance */
 
66
        buf_page_t*     bpage,          /*!< in,own: block to be freed */
 
67
        ulint           i)              /*!< in: index of
 
68
                                        buf_pool->zip_free[] */
 
69
{
 
70
#ifdef UNIV_DEBUG_VALGRIND
 
71
        buf_page_t*     b  = UT_LIST_GET_FIRST(buf_pool->zip_free[i]);
 
72
 
 
73
        if (b) UNIV_MEM_VALID(b, BUF_BUDDY_LOW << i);
 
74
#endif /* UNIV_DEBUG_VALGRIND */
 
75
 
 
76
        ut_ad(buf_pool_mutex_own(buf_pool));
 
77
        ut_ad(buf_page_get_state(bpage) == BUF_BLOCK_ZIP_FREE);
 
78
        ut_ad(buf_pool->zip_free[i].start != bpage);
 
79
        UT_LIST_ADD_FIRST(list, buf_pool->zip_free[i], bpage);
 
80
 
 
81
#ifdef UNIV_DEBUG_VALGRIND
 
82
        if (b) UNIV_MEM_FREE(b, BUF_BUDDY_LOW << i);
 
83
        UNIV_MEM_ASSERT_AND_FREE(bpage, BUF_BUDDY_LOW << i);
 
84
#endif /* UNIV_DEBUG_VALGRIND */
 
85
}
 
86
 
 
87
/**********************************************************************//**
 
88
Remove a block from the appropriate buddy free list. */
 
89
UNIV_INLINE
 
90
void
 
91
buf_buddy_remove_from_free(
 
92
/*=======================*/
 
93
        buf_pool_t*     buf_pool,       /*!< in: buffer pool instance */
 
94
        buf_page_t*     bpage,          /*!< in: block to be removed */
 
95
        ulint           i)              /*!< in: index of
 
96
                                        buf_pool->zip_free[] */
 
97
{
 
98
#ifdef UNIV_DEBUG_VALGRIND
 
99
        buf_page_t*     prev = UT_LIST_GET_PREV(list, bpage);
 
100
        buf_page_t*     next = UT_LIST_GET_NEXT(list, bpage);
 
101
 
 
102
        if (prev) UNIV_MEM_VALID(prev, BUF_BUDDY_LOW << i);
 
103
        if (next) UNIV_MEM_VALID(next, BUF_BUDDY_LOW << i);
 
104
 
 
105
        ut_ad(!prev || buf_page_get_state(prev) == BUF_BLOCK_ZIP_FREE);
 
106
        ut_ad(!next || buf_page_get_state(next) == BUF_BLOCK_ZIP_FREE);
 
107
#endif /* UNIV_DEBUG_VALGRIND */
 
108
 
 
109
        ut_ad(buf_pool_mutex_own(buf_pool));
 
110
        ut_ad(buf_page_get_state(bpage) == BUF_BLOCK_ZIP_FREE);
 
111
        UT_LIST_REMOVE(list, buf_pool->zip_free[i], bpage);
 
112
 
 
113
#ifdef UNIV_DEBUG_VALGRIND
 
114
        if (prev) UNIV_MEM_FREE(prev, BUF_BUDDY_LOW << i);
 
115
        if (next) UNIV_MEM_FREE(next, BUF_BUDDY_LOW << i);
 
116
#endif /* UNIV_DEBUG_VALGRIND */
 
117
}
 
118
 
 
119
/**********************************************************************//**
 
120
Try to allocate a block from buf_pool->zip_free[].
 
121
@return allocated block, or NULL if buf_pool->zip_free[] was empty */
 
122
static
 
123
void*
 
124
buf_buddy_alloc_zip(
 
125
/*================*/
 
126
        buf_pool_t*     buf_pool,       /*!< in: buffer pool instance */
 
127
        ulint           i)              /*!< in: index of buf_pool->zip_free[] */
 
128
{
 
129
        buf_page_t*     bpage;
 
130
 
 
131
        ut_ad(buf_pool_mutex_own(buf_pool));
 
132
        ut_a(i < BUF_BUDDY_SIZES);
 
133
 
 
134
#ifndef UNIV_DEBUG_VALGRIND
 
135
        /* Valgrind would complain about accessing free memory. */
 
136
        ut_d(UT_LIST_VALIDATE(list, buf_page_t, buf_pool->zip_free[i],
 
137
                              ut_ad(buf_page_get_state(ut_list_node_313)
 
138
                                    == BUF_BLOCK_ZIP_FREE)));
 
139
#endif /* !UNIV_DEBUG_VALGRIND */
 
140
        bpage = UT_LIST_GET_FIRST(buf_pool->zip_free[i]);
 
141
 
 
142
        if (bpage) {
 
143
                UNIV_MEM_VALID(bpage, BUF_BUDDY_LOW << i);
 
144
                ut_a(buf_page_get_state(bpage) == BUF_BLOCK_ZIP_FREE);
 
145
 
 
146
                buf_buddy_remove_from_free(buf_pool, bpage, i);
 
147
        } else if (i + 1 < BUF_BUDDY_SIZES) {
 
148
                /* Attempt to split. */
 
149
                bpage = buf_buddy_alloc_zip(buf_pool, i + 1);
 
150
 
 
151
                if (bpage) {
 
152
                        buf_page_t*     buddy = (buf_page_t*)
 
153
                                (((char*) bpage) + (BUF_BUDDY_LOW << i));
 
154
 
 
155
                        ut_ad(!buf_pool_contains_zip(buf_pool, buddy));
 
156
                        ut_d(memset(buddy, i, BUF_BUDDY_LOW << i));
 
157
                        buddy->state = BUF_BLOCK_ZIP_FREE;
 
158
                        buf_buddy_add_to_free(buf_pool, buddy, i);
 
159
                }
 
160
        }
 
161
 
 
162
#ifdef UNIV_DEBUG
 
163
        if (bpage) {
 
164
                memset(bpage, ~i, BUF_BUDDY_LOW << i);
 
165
        }
 
166
#endif /* UNIV_DEBUG */
 
167
 
 
168
        UNIV_MEM_ALLOC(bpage, BUF_BUDDY_SIZES << i);
 
169
 
 
170
        return(bpage);
 
171
}
 
172
 
 
173
/**********************************************************************//**
 
174
Deallocate a buffer frame of UNIV_PAGE_SIZE. */
 
175
static
 
176
void
 
177
buf_buddy_block_free(
 
178
/*=================*/
 
179
        buf_pool_t*     buf_pool,       /*!< in: buffer pool instance */
 
180
        void*           buf)            /*!< in: buffer frame to deallocate */
 
181
{
 
182
        const ulint     fold    = BUF_POOL_ZIP_FOLD_PTR(buf);
 
183
        buf_page_t*     bpage;
 
184
        buf_block_t*    block;
 
185
 
 
186
        ut_ad(buf_pool_mutex_own(buf_pool));
 
187
        ut_ad(!mutex_own(&buf_pool->zip_mutex));
 
188
        ut_a(!ut_align_offset(buf, UNIV_PAGE_SIZE));
 
189
 
 
190
        HASH_SEARCH(hash, buf_pool->zip_hash, fold, buf_page_t*, bpage,
 
191
                    ut_ad(buf_page_get_state(bpage) == BUF_BLOCK_MEMORY
 
192
                          && bpage->in_zip_hash && !bpage->in_page_hash),
 
193
                    ((buf_block_t*) bpage)->frame == buf);
 
194
        ut_a(bpage);
 
195
        ut_a(buf_page_get_state(bpage) == BUF_BLOCK_MEMORY);
 
196
        ut_ad(!bpage->in_page_hash);
 
197
        ut_ad(bpage->in_zip_hash);
 
198
        ut_d(bpage->in_zip_hash = FALSE);
 
199
        HASH_DELETE(buf_page_t, hash, buf_pool->zip_hash, fold, bpage);
 
200
 
 
201
        ut_d(memset(buf, 0, UNIV_PAGE_SIZE));
 
202
        UNIV_MEM_INVALID(buf, UNIV_PAGE_SIZE);
 
203
 
 
204
        block = (buf_block_t*) bpage;
 
205
        mutex_enter(&block->mutex);
 
206
        buf_LRU_block_free_non_file_page(block);
 
207
        mutex_exit(&block->mutex);
 
208
 
 
209
        ut_ad(buf_pool->buddy_n_frames > 0);
 
210
        ut_d(buf_pool->buddy_n_frames--);
 
211
}
 
212
 
 
213
/**********************************************************************//**
 
214
Allocate a buffer block to the buddy allocator. */
 
215
static
 
216
void
 
217
buf_buddy_block_register(
 
218
/*=====================*/
 
219
        buf_block_t*    block)  /*!< in: buffer frame to allocate */
 
220
{
 
221
        buf_pool_t*     buf_pool = buf_pool_from_block(block);
 
222
        const ulint     fold = BUF_POOL_ZIP_FOLD(block);
 
223
        ut_ad(buf_pool_mutex_own(buf_pool));
 
224
        ut_ad(!mutex_own(&buf_pool->zip_mutex));
 
225
        ut_ad(buf_block_get_state(block) == BUF_BLOCK_READY_FOR_USE);
 
226
 
 
227
        buf_block_set_state(block, BUF_BLOCK_MEMORY);
 
228
 
 
229
        ut_a(block->frame);
 
230
        ut_a(!ut_align_offset(block->frame, UNIV_PAGE_SIZE));
 
231
 
 
232
        ut_ad(!block->page.in_page_hash);
 
233
        ut_ad(!block->page.in_zip_hash);
 
234
        ut_d(block->page.in_zip_hash = TRUE);
 
235
        HASH_INSERT(buf_page_t, hash, buf_pool->zip_hash, fold, &block->page);
 
236
 
 
237
        ut_d(buf_pool->buddy_n_frames++);
 
238
}
 
239
 
 
240
/**********************************************************************//**
 
241
Allocate a block from a bigger object.
 
242
@return allocated block */
 
243
static
 
244
void*
 
245
buf_buddy_alloc_from(
 
246
/*=================*/
 
247
        buf_pool_t*     buf_pool,       /*!< in: buffer pool instance */
 
248
        void*           buf,            /*!< in: a block that is free to use */
 
249
        ulint           i,              /*!< in: index of
 
250
                                        buf_pool->zip_free[] */
 
251
        ulint           j)              /*!< in: size of buf as an index
 
252
                                        of buf_pool->zip_free[] */
 
253
{
 
254
        ulint   offs    = BUF_BUDDY_LOW << j;
 
255
        ut_ad(j <= BUF_BUDDY_SIZES);
 
256
        ut_ad(j >= i);
 
257
        ut_ad(!ut_align_offset(buf, offs));
 
258
 
 
259
        /* Add the unused parts of the block to the free lists. */
 
260
        while (j > i) {
 
261
                buf_page_t*     bpage;
 
262
 
 
263
                offs >>= 1;
 
264
                j--;
 
265
 
 
266
                bpage = (buf_page_t*) ((byte*) buf + offs);
 
267
                ut_d(memset(bpage, j, BUF_BUDDY_LOW << j));
 
268
                bpage->state = BUF_BLOCK_ZIP_FREE;
 
269
#ifndef UNIV_DEBUG_VALGRIND
 
270
                /* Valgrind would complain about accessing free memory. */
 
271
                ut_d(UT_LIST_VALIDATE(list, buf_page_t, buf_pool->zip_free[i],
 
272
                                      ut_ad(buf_page_get_state(
 
273
                                                    ut_list_node_313)
 
274
                                            == BUF_BLOCK_ZIP_FREE)));
 
275
#endif /* !UNIV_DEBUG_VALGRIND */
 
276
                buf_buddy_add_to_free(buf_pool, bpage, j);
 
277
        }
 
278
 
 
279
        return(buf);
 
280
}
 
281
 
 
282
/**********************************************************************//**
 
283
Allocate a block.  The thread calling this function must hold
 
284
buf_pool->mutex and must not hold buf_pool_zip_mutex or any block->mutex.
 
285
The buf_pool->mutex may only be released and reacquired if lru != NULL.
 
286
@return allocated block, possibly NULL if lru==NULL */
 
287
UNIV_INTERN
 
288
void*
 
289
buf_buddy_alloc_low(
 
290
/*================*/
 
291
        buf_pool_t*     buf_pool,       /*!< in: buffer pool instance */
 
292
        ulint           i,              /*!< in: index of buf_pool->zip_free[],
 
293
                                        or BUF_BUDDY_SIZES */
 
294
        ibool*          lru)            /*!< in: pointer to a variable that
 
295
                                        will be assigned TRUE if storage was
 
296
                                        allocated from the LRU list and
 
297
                                        buf_pool->mutex was temporarily
 
298
                                        released, or NULL if the LRU list
 
299
                                        should not be used */
 
300
{
 
301
        buf_block_t*    block;
 
302
 
 
303
        ut_ad(buf_pool_mutex_own(buf_pool));
 
304
        ut_ad(!mutex_own(&buf_pool->zip_mutex));
 
305
 
 
306
        if (i < BUF_BUDDY_SIZES) {
 
307
                /* Try to allocate from the buddy system. */
 
308
                block = buf_buddy_alloc_zip(buf_pool, i);
 
309
 
 
310
                if (block) {
 
311
                        goto func_exit;
 
312
                }
 
313
        }
 
314
 
 
315
        /* Try allocating from the buf_pool->free list. */
 
316
        block = buf_LRU_get_free_only(buf_pool);
 
317
 
 
318
        if (block) {
 
319
 
 
320
                goto alloc_big;
 
321
        }
 
322
 
 
323
        if (!lru) {
 
324
 
 
325
                return(NULL);
 
326
        }
 
327
 
 
328
        /* Try replacing an uncompressed page in the buffer pool. */
 
329
        buf_pool_mutex_exit(buf_pool);
 
330
        block = buf_LRU_get_free_block(buf_pool, 0);
 
331
        *lru = TRUE;
 
332
        buf_pool_mutex_enter(buf_pool);
 
333
 
 
334
alloc_big:
 
335
        buf_buddy_block_register(block);
 
336
 
 
337
        block = buf_buddy_alloc_from(
 
338
                buf_pool, block->frame, i, BUF_BUDDY_SIZES);
 
339
 
 
340
func_exit:
 
341
        buf_pool->buddy_stat[i].used++;
 
342
        return(block);
 
343
}
 
344
 
 
345
/**********************************************************************//**
 
346
Try to relocate the control block of a compressed page.
 
347
@return TRUE if relocated */
 
348
static
 
349
ibool
 
350
buf_buddy_relocate_block(
 
351
/*=====================*/
 
352
        buf_page_t*     bpage,  /*!< in: block to relocate */
 
353
        buf_page_t*     dpage)  /*!< in: free block to relocate to */
 
354
{
 
355
        buf_page_t*     b;
 
356
        buf_pool_t*     buf_pool = buf_pool_from_bpage(bpage);
 
357
 
 
358
        ut_ad(buf_pool_mutex_own(buf_pool));
 
359
 
 
360
        switch (buf_page_get_state(bpage)) {
 
361
        case BUF_BLOCK_ZIP_FREE:
 
362
        case BUF_BLOCK_NOT_USED:
 
363
        case BUF_BLOCK_READY_FOR_USE:
 
364
        case BUF_BLOCK_FILE_PAGE:
 
365
        case BUF_BLOCK_MEMORY:
 
366
        case BUF_BLOCK_REMOVE_HASH:
 
367
                ut_error;
 
368
        case BUF_BLOCK_ZIP_DIRTY:
 
369
                /* Cannot relocate dirty pages. */
 
370
                return(FALSE);
 
371
 
 
372
        case BUF_BLOCK_ZIP_PAGE:
 
373
                break;
 
374
        }
 
375
 
 
376
        mutex_enter(&buf_pool->zip_mutex);
 
377
 
 
378
        if (!buf_page_can_relocate(bpage)) {
 
379
                mutex_exit(&buf_pool->zip_mutex);
 
380
                return(FALSE);
 
381
        }
 
382
 
 
383
        buf_relocate(bpage, dpage);
 
384
        ut_d(bpage->state = BUF_BLOCK_ZIP_FREE);
 
385
 
 
386
        /* relocate buf_pool->zip_clean */
 
387
        b = UT_LIST_GET_PREV(list, dpage);
 
388
        UT_LIST_REMOVE(list, buf_pool->zip_clean, dpage);
 
389
 
 
390
        if (b) {
 
391
                UT_LIST_INSERT_AFTER(list, buf_pool->zip_clean, b, dpage);
 
392
        } else {
 
393
                UT_LIST_ADD_FIRST(list, buf_pool->zip_clean, dpage);
 
394
        }
 
395
 
 
396
        UNIV_MEM_INVALID(bpage, sizeof *bpage);
 
397
 
 
398
        mutex_exit(&buf_pool->zip_mutex);
 
399
        return(TRUE);
 
400
}
 
401
 
 
402
/**********************************************************************//**
 
403
Try to relocate a block.
 
404
@return TRUE if relocated */
 
405
static
 
406
ibool
 
407
buf_buddy_relocate(
 
408
/*===============*/
 
409
        buf_pool_t*     buf_pool,       /*!< in: buffer pool instance */
 
410
        void*           src,            /*!< in: block to relocate */
 
411
        void*           dst,            /*!< in: free block to relocate to */
 
412
        ulint           i)              /*!< in: index of
 
413
                                        buf_pool->zip_free[] */
 
414
{
 
415
        buf_page_t*     bpage;
 
416
        const ulint     size    = BUF_BUDDY_LOW << i;
 
417
        ullint          usec    = ut_time_us(NULL);
 
418
 
 
419
        ut_ad(buf_pool_mutex_own(buf_pool));
 
420
        ut_ad(!mutex_own(&buf_pool->zip_mutex));
 
421
        ut_ad(!ut_align_offset(src, size));
 
422
        ut_ad(!ut_align_offset(dst, size));
 
423
        UNIV_MEM_ASSERT_W(dst, size);
 
424
 
 
425
        /* We assume that all memory from buf_buddy_alloc()
 
426
        is used for either compressed pages or buf_page_t
 
427
        objects covering compressed pages. */
 
428
 
 
429
        /* We look inside the allocated objects returned by
 
430
        buf_buddy_alloc() and assume that anything of
 
431
        PAGE_ZIP_MIN_SIZE or larger is a compressed page that contains
 
432
        a valid space_id and page_no in the page header.  Should the
 
433
        fields be invalid, we will be unable to relocate the block.
 
434
        We also assume that anything that fits sizeof(buf_page_t)
 
435
        actually is a properly initialized buf_page_t object. */
 
436
 
 
437
        if (size >= PAGE_ZIP_MIN_SIZE) {
 
438
                /* This is a compressed page. */
 
439
                mutex_t*        mutex;
 
440
 
 
441
                /* The src block may be split into smaller blocks,
 
442
                some of which may be free.  Thus, the
 
443
                mach_read_from_4() calls below may attempt to read
 
444
                from free memory.  The memory is "owned" by the buddy
 
445
                allocator (and it has been allocated from the buffer
 
446
                pool), so there is nothing wrong about this.  The
 
447
                mach_read_from_4() calls here will only trigger bogus
 
448
                Valgrind memcheck warnings in UNIV_DEBUG_VALGRIND builds. */
 
449
                bpage = buf_page_hash_get(
 
450
                        buf_pool,
 
451
                        mach_read_from_4((const byte*) src
 
452
                                         + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID),
 
453
                        mach_read_from_4((const byte*) src
 
454
                                         + FIL_PAGE_OFFSET));
 
455
 
 
456
                if (!bpage || bpage->zip.data != src) {
 
457
                        /* The block has probably been freshly
 
458
                        allocated by buf_LRU_get_free_block() but not
 
459
                        added to buf_pool->page_hash yet.  Obviously,
 
460
                        it cannot be relocated. */
 
461
 
 
462
                        return(FALSE);
 
463
                }
 
464
 
 
465
                ut_ad(!buf_pool_watch_is_sentinel(buf_pool, bpage));
 
466
 
 
467
                if (page_zip_get_size(&bpage->zip) != size) {
 
468
                        /* The block is of different size.  We would
 
469
                        have to relocate all blocks covered by src.
 
470
                        For the sake of simplicity, give up. */
 
471
                        ut_ad(page_zip_get_size(&bpage->zip) < size);
 
472
 
 
473
                        return(FALSE);
 
474
                }
 
475
 
 
476
                /* The block must have been allocated, but it may
 
477
                contain uninitialized data. */
 
478
                UNIV_MEM_ASSERT_W(src, size);
 
479
 
 
480
                mutex = buf_page_get_mutex(bpage);
 
481
 
 
482
                mutex_enter(mutex);
 
483
 
 
484
                if (buf_page_can_relocate(bpage)) {
 
485
                        /* Relocate the compressed page. */
 
486
                        ut_a(bpage->zip.data == src);
 
487
                        memcpy(dst, src, size);
 
488
                        bpage->zip.data = dst;
 
489
                        mutex_exit(mutex);
 
490
success:
 
491
                        UNIV_MEM_INVALID(src, size);
 
492
                        {
 
493
                                buf_buddy_stat_t*       buddy_stat
 
494
                                        = &buf_pool->buddy_stat[i];
 
495
                                buddy_stat->relocated++;
 
496
                                buddy_stat->relocated_usec
 
497
                                        += ut_time_us(NULL) - usec;
 
498
                        }
 
499
                        return(TRUE);
 
500
                }
 
501
 
 
502
                mutex_exit(mutex);
 
503
        } else if (i == buf_buddy_get_slot(sizeof(buf_page_t))) {
 
504
                /* This must be a buf_page_t object. */
 
505
                UNIV_MEM_ASSERT_RW(src, size);
 
506
                if (buf_buddy_relocate_block(src, dst)) {
 
507
 
 
508
                        goto success;
 
509
                }
 
510
        }
 
511
 
 
512
        return(FALSE);
 
513
}
 
514
 
 
515
/**********************************************************************//**
 
516
Deallocate a block. */
 
517
UNIV_INTERN
 
518
void
 
519
buf_buddy_free_low(
 
520
/*===============*/
 
521
        buf_pool_t*     buf_pool,       /*!< in: buffer pool instance */
 
522
        void*           buf,            /*!< in: block to be freed, must not be
 
523
                                        pointed to by the buffer pool */
 
524
        ulint           i)              /*!< in: index of buf_pool->zip_free[],
 
525
                                        or BUF_BUDDY_SIZES */
 
526
{
 
527
        buf_page_t*     bpage;
 
528
        buf_page_t*     buddy;
 
529
 
 
530
        ut_ad(buf_pool_mutex_own(buf_pool));
 
531
        ut_ad(!mutex_own(&buf_pool->zip_mutex));
 
532
        ut_ad(i <= BUF_BUDDY_SIZES);
 
533
        ut_ad(buf_pool->buddy_stat[i].used > 0);
 
534
 
 
535
        buf_pool->buddy_stat[i].used--;
 
536
recombine:
 
537
        UNIV_MEM_ASSERT_AND_ALLOC(buf, BUF_BUDDY_LOW << i);
 
538
        ut_d(((buf_page_t*) buf)->state = BUF_BLOCK_ZIP_FREE);
 
539
 
 
540
        if (i == BUF_BUDDY_SIZES) {
 
541
                buf_buddy_block_free(buf_pool, buf);
 
542
                return;
 
543
        }
 
544
 
 
545
        ut_ad(i < BUF_BUDDY_SIZES);
 
546
        ut_ad(buf == ut_align_down(buf, BUF_BUDDY_LOW << i));
 
547
        ut_ad(!buf_pool_contains_zip(buf_pool, buf));
 
548
 
 
549
        /* Try to combine adjacent blocks. */
 
550
 
 
551
        buddy = (buf_page_t*) buf_buddy_get(((byte*) buf), BUF_BUDDY_LOW << i);
 
552
 
 
553
#ifndef UNIV_DEBUG_VALGRIND
 
554
        /* Valgrind would complain about accessing free memory. */
 
555
 
 
556
        if (buddy->state != BUF_BLOCK_ZIP_FREE) {
 
557
 
 
558
                goto buddy_nonfree;
 
559
        }
 
560
 
 
561
        /* The field buddy->state can only be trusted for free blocks.
 
562
        If buddy->state == BUF_BLOCK_ZIP_FREE, the block is free if
 
563
        it is in the free list. */
 
564
#endif /* !UNIV_DEBUG_VALGRIND */
 
565
 
 
566
        for (bpage = UT_LIST_GET_FIRST(buf_pool->zip_free[i]); bpage; ) {
 
567
                UNIV_MEM_VALID(bpage, BUF_BUDDY_LOW << i);
 
568
                ut_ad(buf_page_get_state(bpage) == BUF_BLOCK_ZIP_FREE);
 
569
 
 
570
                if (bpage == buddy) {
 
571
buddy_free:
 
572
                        /* The buddy is free: recombine */
 
573
                        buf_buddy_remove_from_free(buf_pool, bpage, i);
 
574
buddy_free2:
 
575
                        ut_ad(buf_page_get_state(buddy) == BUF_BLOCK_ZIP_FREE);
 
576
                        ut_ad(!buf_pool_contains_zip(buf_pool, buddy));
 
577
                        i++;
 
578
                        buf = ut_align_down(buf, BUF_BUDDY_LOW << i);
 
579
 
 
580
                        goto recombine;
 
581
                }
 
582
 
 
583
                ut_a(bpage != buf);
 
584
 
 
585
                {
 
586
                        buf_page_t*     next = UT_LIST_GET_NEXT(list, bpage);
 
587
                        UNIV_MEM_ASSERT_AND_FREE(bpage, BUF_BUDDY_LOW << i);
 
588
                        bpage = next;
 
589
                }
 
590
        }
 
591
 
 
592
#ifndef UNIV_DEBUG_VALGRIND
 
593
buddy_nonfree:
 
594
        /* Valgrind would complain about accessing free memory. */
 
595
        ut_d(UT_LIST_VALIDATE(list, buf_page_t, buf_pool->zip_free[i],
 
596
                              ut_ad(buf_page_get_state(ut_list_node_313)
 
597
                                    == BUF_BLOCK_ZIP_FREE)));
 
598
#endif /* UNIV_DEBUG_VALGRIND */
 
599
 
 
600
        /* The buddy is not free. Is there a free block of this size? */
 
601
        bpage = UT_LIST_GET_FIRST(buf_pool->zip_free[i]);
 
602
 
 
603
        if (bpage) {
 
604
                /* Remove the block from the free list, because a successful
 
605
                buf_buddy_relocate() will overwrite bpage->list. */
 
606
 
 
607
                UNIV_MEM_VALID(bpage, BUF_BUDDY_LOW << i);
 
608
                buf_buddy_remove_from_free(buf_pool, bpage, i);
 
609
 
 
610
                /* Try to relocate the buddy of buf to the free block. */
 
611
                if (buf_buddy_relocate(buf_pool, buddy, bpage, i)) {
 
612
 
 
613
                        ut_d(buddy->state = BUF_BLOCK_ZIP_FREE);
 
614
                        goto buddy_free2;
 
615
                }
 
616
 
 
617
                buf_buddy_add_to_free(buf_pool, bpage, i);
 
618
 
 
619
                /* Try to relocate the buddy of the free block to buf. */
 
620
                buddy = (buf_page_t*) buf_buddy_get(((byte*) bpage),
 
621
                                                    BUF_BUDDY_LOW << i);
 
622
 
 
623
#ifndef UNIV_DEBUG_VALGRIND
 
624
                /* Valgrind would complain about accessing free memory. */
 
625
 
 
626
                /* The buddy must not be (completely) free, because we
 
627
                always recombine adjacent free blocks.
 
628
 
 
629
                (Parts of the buddy can be free in
 
630
                buf_pool->zip_free[j] with j < i.) */
 
631
                ut_d(UT_LIST_VALIDATE(list, buf_page_t, buf_pool->zip_free[i],
 
632
                                      ut_ad(buf_page_get_state(
 
633
                                                    ut_list_node_313)
 
634
                                            == BUF_BLOCK_ZIP_FREE
 
635
                                            && ut_list_node_313 != buddy)));
 
636
#endif /* !UNIV_DEBUG_VALGRIND */
 
637
 
 
638
                if (buf_buddy_relocate(buf_pool, buddy, buf, i)) {
 
639
 
 
640
                        buf = bpage;
 
641
                        UNIV_MEM_VALID(bpage, BUF_BUDDY_LOW << i);
 
642
                        ut_d(buddy->state = BUF_BLOCK_ZIP_FREE);
 
643
                        goto buddy_free;
 
644
                }
 
645
        }
 
646
 
 
647
        /* Free the block to the buddy list. */
 
648
        bpage = buf;
 
649
#ifdef UNIV_DEBUG
 
650
        if (i < buf_buddy_get_slot(PAGE_ZIP_MIN_SIZE)) {
 
651
                /* This area has most likely been allocated for at
 
652
                least one compressed-only block descriptor.  Check
 
653
                that there are no live objects in the area.  This is
 
654
                not a complete check: it may yield false positives as
 
655
                well as false negatives.  Also, due to buddy blocks
 
656
                being recombined, it is possible (although unlikely)
 
657
                that this branch is never reached. */
 
658
 
 
659
                char* c;
 
660
 
 
661
# ifndef UNIV_DEBUG_VALGRIND
 
662
                /* Valgrind would complain about accessing
 
663
                uninitialized memory.  Besides, Valgrind performs a
 
664
                more exhaustive check, at every memory access. */
 
665
                const buf_page_t* b = buf;
 
666
                const buf_page_t* const b_end = (buf_page_t*)
 
667
                        ((char*) b + (BUF_BUDDY_LOW << i));
 
668
 
 
669
                for (; b < b_end; b++) {
 
670
                        /* Avoid false positives (and cause false
 
671
                        negatives) by checking for b->space < 1000. */
 
672
 
 
673
                        if ((b->state == BUF_BLOCK_ZIP_PAGE
 
674
                             || b->state == BUF_BLOCK_ZIP_DIRTY)
 
675
                            && b->space > 0 && b->space < 1000) {
 
676
                                fprintf(stderr,
 
677
                                        "buddy dirty %p %u (%u,%u) %p,%lu\n",
 
678
                                        (void*) b,
 
679
                                        b->state, b->space, b->offset,
 
680
                                        buf, i);
 
681
                        }
 
682
                }
 
683
# endif /* !UNIV_DEBUG_VALGRIND */
 
684
 
 
685
                /* Scramble the block.  This should make any pointers
 
686
                invalid and trigger a segmentation violation.  Because
 
687
                the scrambling can be reversed, it may be possible to
 
688
                track down the object pointing to the freed data by
 
689
                dereferencing the unscrambled bpage->LRU or
 
690
                bpage->list pointers. */
 
691
                for (c = (char*) buf + (BUF_BUDDY_LOW << i);
 
692
                     c-- > (char*) buf; ) {
 
693
                        *c = ~*c ^ i;
 
694
                }
 
695
        } else {
 
696
                /* Fill large blocks with a constant pattern. */
 
697
                memset(bpage, i, BUF_BUDDY_LOW << i);
 
698
        }
 
699
#endif /* UNIV_DEBUG */
 
700
        bpage->state = BUF_BLOCK_ZIP_FREE;
 
701
        buf_buddy_add_to_free(buf_pool, bpage, i);
 
702
}