~percona-dev/percona-innodb-plugin/percona-innodb-1.0

« back to all changes in this revision

Viewing changes to buf/buf0buddy.c

  • Committer: Vadim Tkachenko
  • Date: 2008-12-01 02:05:57 UTC
  • Revision ID: vadim@percona.com-20081201020557-p7k2m94mjtdg1a83
New rw-locks

Show diffs side-by-side

added added

removed removed

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