summaryrefslogtreecommitdiff
path: root/storage/maria/ma_bitmap.c
diff options
context:
space:
mode:
Diffstat (limited to 'storage/maria/ma_bitmap.c')
-rw-r--r--storage/maria/ma_bitmap.c412
1 files changed, 335 insertions, 77 deletions
diff --git a/storage/maria/ma_bitmap.c b/storage/maria/ma_bitmap.c
index fb12ecdbe3b..c2cf7d32d48 100644
--- a/storage/maria/ma_bitmap.c
+++ b/storage/maria/ma_bitmap.c
@@ -104,10 +104,11 @@
- On checkpoint
(Ie: When we do a checkpoint, we have to ensure that all bitmaps are
put on disk even if they are not in the page cache).
- - When explicitely requested (for example on backup or after recvoery,
+ - When explicitely requested (for example on backup or after recovery,
to simplify things)
The flow of writing a row is that:
+ - Mark the bitmap not flushable (_ma_bitmap_flushable(X, 1))
- Lock the bitmap
- Decide which data pages we will write to
- Mark them full in the bitmap page so that other threads do not try to
@@ -119,6 +120,7 @@
pages (that is, we marked pages full but when we are done we realize
we didn't fill them)
- Unlock the bitmap.
+ - Mark the bitmap flushable (_ma_bitmap_flushable(X, -1))
*/
#include "maria_def.h"
@@ -127,6 +129,12 @@
#define FULL_HEAD_PAGE 4
#define FULL_TAIL_PAGE 7
+const char *bits_to_txt[]=
+{
+ "empty", "00-30% full", "30-60% full", "60-90% full", "full",
+ "tail 00-40 % full", "tail 40-80 % full", "tail/blob full"
+};
+
/*#define WRONG_BITMAP_FLUSH 1*/ /*define only for provoking bugs*/
#undef WRONG_BITMAP_FLUSH
@@ -136,12 +144,15 @@ static my_bool _ma_read_bitmap_page(MARIA_HA *info,
static my_bool _ma_bitmap_create_missing(MARIA_HA *info,
MARIA_FILE_BITMAP *bitmap,
pgcache_page_no_t page);
+static void _ma_bitmap_unpin_all(MARIA_SHARE *share);
+
/* Write bitmap page to key cache */
static inline my_bool write_changed_bitmap(MARIA_SHARE *share,
MARIA_FILE_BITMAP *bitmap)
{
+ my_bool res;
DBUG_ENTER("write_changed_bitmap");
DBUG_ASSERT(share->pagecache->block_size == bitmap->block_size);
DBUG_ASSERT(bitmap->file.write_callback != 0);
@@ -159,18 +170,28 @@ static inline my_bool write_changed_bitmap(MARIA_SHARE *share,
#endif
)
{
- my_bool res= pagecache_write(share->pagecache,
+ res= pagecache_write(share->pagecache,
&bitmap->file, bitmap->page, 0,
bitmap->map, PAGECACHE_PLAIN_PAGE,
PAGECACHE_LOCK_LEFT_UNLOCKED,
PAGECACHE_PIN_LEFT_UNPINNED,
PAGECACHE_WRITE_DELAY, 0, LSN_IMPOSSIBLE);
+ DBUG_ASSERT(!res);
DBUG_RETURN(res);
}
else
{
+ /*
+ bitmap->non_flushable means that someone has changed the bitmap,
+ but it's not yet complete so it can't yet be written to disk.
+ In this case we write the changed bitmap to the disk cache,
+ but keep it pinned until the change is completed. The page will
+ be unpinned later by _ma_bitmap_unpin_all() as soon as non_flushable
+ is set back to 0.
+ */
MARIA_PINNED_PAGE page_link;
- int res= pagecache_write(share->pagecache,
+ DBUG_PRINT("info", ("Writing pinned bitmap page"));
+ res= pagecache_write(share->pagecache,
&bitmap->file, bitmap->page, 0,
bitmap->map, PAGECACHE_PLAIN_PAGE,
PAGECACHE_LOCK_LEFT_UNLOCKED, PAGECACHE_PIN,
@@ -178,7 +199,8 @@ static inline my_bool write_changed_bitmap(MARIA_SHARE *share,
LSN_IMPOSSIBLE);
page_link.unlock= PAGECACHE_LOCK_LEFT_UNLOCKED;
page_link.changed= 1;
- push_dynamic(&bitmap->pinned_pages, (void*) &page_link);
+ push_dynamic(&bitmap->pinned_pages, (const uchar*) (void*) &page_link);
+ DBUG_ASSERT(!res);
DBUG_RETURN(res);
}
}
@@ -189,7 +211,10 @@ static inline my_bool write_changed_bitmap(MARIA_SHARE *share,
SYNOPSIS
_ma_bitmap_init()
share Share handler
- file data file handler
+ file Data file handler
+ last_page Pointer to last page (max_file_size) that needs to be
+ mapped by the bitmap. This is adjusted to bitmap
+ alignment.
NOTES
This is called the first time a file is opened.
@@ -199,12 +224,14 @@ static inline my_bool write_changed_bitmap(MARIA_SHARE *share,
1 error
*/
-my_bool _ma_bitmap_init(MARIA_SHARE *share, File file)
+my_bool _ma_bitmap_init(MARIA_SHARE *share, File file,
+ pgcache_page_no_t *last_page)
{
uint aligned_bit_blocks;
uint max_page_size;
MARIA_FILE_BITMAP *bitmap= &share->bitmap;
uint size= share->block_size;
+ pgcache_page_no_t first_bitmap_with_space;
#ifndef DBUG_OFF
/* We want to have a copy of the bitmap to be able to print differences */
size*= 2;
@@ -221,13 +248,14 @@ my_bool _ma_bitmap_init(MARIA_SHARE *share, File file)
/* Size needs to be aligned on 6 */
aligned_bit_blocks= (share->block_size - PAGE_SUFFIX_SIZE) / 6;
- bitmap->total_size= aligned_bit_blocks * 6;
+ bitmap->max_total_size= bitmap->total_size= aligned_bit_blocks * 6;
/*
In each 6 bytes, we have 6*8/3 = 16 pages covered
The +1 is to add the bitmap page, as this doesn't have to be covered
*/
bitmap->pages_covered= aligned_bit_blocks * 16 + 1;
- bitmap->flush_all_requested= 0;
+ bitmap->flush_all_requested= bitmap->waiting_for_flush_all_requested=
+ bitmap->waiting_for_non_flushable= 0;
bitmap->non_flushable= 0;
/* Update size for bits */
@@ -247,13 +275,35 @@ my_bool _ma_bitmap_init(MARIA_SHARE *share, File file)
mysql_cond_init(key_SHARE_BITMAP_cond,
&share->bitmap.bitmap_cond, 0);
+ first_bitmap_with_space= share->state.first_bitmap_with_space;
_ma_bitmap_reset_cache(share);
- if (share->state.first_bitmap_with_space == ~(pgcache_page_no_t) 0)
+ /*
+ The bitmap used to map the file are aligned on 6 bytes. We now
+ calculate the max file size that can be used by the bitmap. This
+ is needed to get ma_info() give a true file size so that the user can
+ estimate if there is still space free for records in the file.
+ */
{
- /* Start scanning for free space from start of file */
- share->state.first_bitmap_with_space = 0;
+ pgcache_page_no_t last_bitmap_page;
+ ulong blocks, bytes;
+
+ last_bitmap_page= *last_page - *last_page % bitmap->pages_covered;
+ blocks= *last_page - last_bitmap_page;
+ bytes= (blocks * 3) / 8; /* 3 bit per page / 8 bits per byte */
+ /* Size needs to be aligned on 6 */
+ bytes/= 6;
+ bytes*= 6;
+ bitmap->last_bitmap_page= last_bitmap_page;
+ bitmap->last_total_size= bytes;
+ *last_page= ((last_bitmap_page + bytes*8/3));
}
+
+ /* Restore first_bitmap_with_space if it's resonable */
+ if (first_bitmap_with_space <= (share->state.state.data_file_length /
+ share->block_size))
+ share->state.first_bitmap_with_space= first_bitmap_with_space;
+
return 0;
}
@@ -268,16 +318,63 @@ my_bool _ma_bitmap_init(MARIA_SHARE *share, File file)
my_bool _ma_bitmap_end(MARIA_SHARE *share)
{
- my_bool res= _ma_bitmap_flush(share);
+ my_bool res;
mysql_mutex_assert_owner(&share->close_lock);
+ DBUG_ASSERT(share->bitmap.non_flushable == 0);
+ DBUG_ASSERT(share->bitmap.flush_all_requested == 0);
+ DBUG_ASSERT(share->bitmap.waiting_for_non_flushable == 0 &&
+ share->bitmap.waiting_for_flush_all_requested == 0);
+ DBUG_ASSERT(share->bitmap.pinned_pages.elements == 0);
+
+ res= _ma_bitmap_flush(share);
mysql_mutex_destroy(&share->bitmap.bitmap_lock);
mysql_cond_destroy(&share->bitmap.bitmap_cond);
delete_dynamic(&share->bitmap.pinned_pages);
my_free(share->bitmap.map);
share->bitmap.map= 0;
+ /*
+ This is to not get an assert in checkpoint. The bitmap will be flushed
+ at once by _ma_once_end_block_record() as part of the normal flush
+ of the kfile.
+ */
+ share->bitmap.changed_not_flushed= 0;
return res;
}
+/*
+ Ensure that we have incremented open count before we try to read/write
+ a page while we have the bitmap lock.
+ This is needed to ensure that we don't call _ma_mark_file_changed() as
+ part of flushing a page to disk, as this locks share->internal_lock
+ and then mutex lock would happen in the wrong order.
+*/
+
+static inline void _ma_bitmap_mark_file_changed(MARIA_SHARE *share,
+ my_bool flush_translog)
+{
+ /*
+ It's extremely unlikely that the following test is true as it
+ only happens once if the table has changed.
+ */
+ if (unlikely(!share->global_changed &&
+ (share->state.changed & STATE_CHANGED)))
+ {
+ /* purecov: begin inspected */
+ /* unlock mutex as it can't be hold during _ma_mark_file_changed() */
+ mysql_mutex_unlock(&share->bitmap.bitmap_lock);
+
+ /*
+ We have to flush the translog to ensure we have registered that the
+ table is open.
+ */
+ if (flush_translog && share->now_transactional)
+ (void) translog_flush(share->state.logrec_file_id);
+
+ _ma_mark_file_changed(share);
+ mysql_mutex_lock(&share->bitmap.bitmap_lock);
+ /* purecov: end */
+ }
+}
/*
Send updated bitmap to the page cache
@@ -314,6 +411,12 @@ my_bool _ma_bitmap_flush(MARIA_SHARE *share)
mysql_mutex_lock(&share->bitmap.bitmap_lock);
if (share->bitmap.changed)
{
+ /*
+ We have to mark the file changed here, as otherwise the following
+ write to pagecache may force a page out from this file, which would
+ cause _ma_mark_file_changed() to be called with bitmaplock hold!
+ */
+ _ma_bitmap_mark_file_changed(share, 1);
res= write_changed_bitmap(share, &share->bitmap);
share->bitmap.changed= 0;
}
@@ -353,12 +456,45 @@ filter_flush_bitmap_pages(enum pagecache_page_type type
my_bool _ma_bitmap_flush_all(MARIA_SHARE *share)
{
my_bool res= 0;
+ uint send_signal= 0;
MARIA_FILE_BITMAP *bitmap= &share->bitmap;
DBUG_ENTER("_ma_bitmap_flush_all");
+
+#ifdef EXTRA_DEBUG_BITMAP
+ {
+ char buff[160];
+ uint len= my_sprintf(buff,
+ (buff, "bitmap_flush: fd: %d id: %u "
+ "changed: %d changed_not_flushed: %d "
+ "flush_all_requested: %d",
+ share->bitmap.file.file,
+ share->id,
+ bitmap->changed,
+ bitmap->changed_not_flushed,
+ bitmap->flush_all_requested));
+ (void) translog_log_debug_info(0, LOGREC_DEBUG_INFO_QUERY,
+ (uchar*) buff, len);
+ }
+#endif
+
mysql_mutex_lock(&bitmap->bitmap_lock);
+ if (!bitmap->changed && !bitmap->changed_not_flushed)
+ {
+ mysql_mutex_unlock(&bitmap->bitmap_lock);
+ DBUG_RETURN(0);
+ }
+
+ _ma_bitmap_mark_file_changed(share, 0);
+
+ /*
+ The following should be true as it was tested above. We have to test
+ this again as _ma_bitmap_mark_file_changed() did temporarly release
+ the bitmap mutex.
+ */
if (bitmap->changed || bitmap->changed_not_flushed)
{
bitmap->flush_all_requested++;
+ bitmap->waiting_for_non_flushable++;
#ifndef WRONG_BITMAP_FLUSH
while (bitmap->non_flushable > 0)
{
@@ -366,6 +502,16 @@ my_bool _ma_bitmap_flush_all(MARIA_SHARE *share)
mysql_cond_wait(&bitmap->bitmap_cond, &bitmap->bitmap_lock);
}
#endif
+ bitmap->waiting_for_non_flushable--;
+#ifdef EXTRA_DEBUG_BITMAP
+ {
+ char tmp[MAX_BITMAP_INFO_LENGTH];
+ _ma_get_bitmap_description(bitmap, bitmap->map, bitmap->page, tmp);
+ (void) translog_log_debug_info(0, LOGREC_DEBUG_INFO_QUERY,
+ (uchar*) tmp, strlen(tmp));
+ }
+#endif
+
DBUG_ASSERT(bitmap->flush_all_requested == 1);
/*
Bitmap is in a flushable state: its contents in memory are reflected by
@@ -401,9 +547,12 @@ my_bool _ma_bitmap_flush_all(MARIA_SHARE *share)
become false, wake them up.
*/
DBUG_PRINT("info", ("bitmap flusher waking up others"));
- mysql_cond_broadcast(&bitmap->bitmap_cond);
+ send_signal= (bitmap->waiting_for_flush_all_requested |
+ bitmap->waiting_for_non_flushable);
}
mysql_mutex_unlock(&bitmap->bitmap_lock);
+ if (send_signal)
+ mysql_cond_broadcast(&bitmap->bitmap_cond);
DBUG_RETURN(res);
}
@@ -433,11 +582,13 @@ void _ma_bitmap_lock(MARIA_SHARE *share)
mysql_mutex_lock(&bitmap->bitmap_lock);
bitmap->flush_all_requested++;
+ bitmap->waiting_for_non_flushable++;
while (bitmap->non_flushable)
{
DBUG_PRINT("info", ("waiting for bitmap to be flushable"));
mysql_cond_wait(&bitmap->bitmap_cond, &bitmap->bitmap_lock);
}
+ bitmap->waiting_for_non_flushable--;
/*
Ensure that _ma_bitmap_flush_all() and _ma_bitmap_lock() are blocked.
ma_bitmap_flushable() is blocked thanks to 'flush_all_requested'.
@@ -457,6 +608,7 @@ void _ma_bitmap_lock(MARIA_SHARE *share)
void _ma_bitmap_unlock(MARIA_SHARE *share)
{
MARIA_FILE_BITMAP *bitmap= &share->bitmap;
+ uint send_signal;
DBUG_ENTER("_ma_bitmap_unlock");
if (!share->now_transactional)
@@ -464,10 +616,14 @@ void _ma_bitmap_unlock(MARIA_SHARE *share)
DBUG_ASSERT(bitmap->flush_all_requested > 0 && bitmap->non_flushable == 1);
mysql_mutex_lock(&bitmap->bitmap_lock);
- bitmap->flush_all_requested--;
bitmap->non_flushable= 0;
+ _ma_bitmap_unpin_all(share);
+ send_signal= bitmap->waiting_for_non_flushable;
+ if (!--bitmap->flush_all_requested)
+ send_signal|= bitmap->waiting_for_flush_all_requested;
mysql_mutex_unlock(&bitmap->bitmap_lock);
- mysql_cond_broadcast(&bitmap->bitmap_cond);
+ if (send_signal)
+ mysql_cond_broadcast(&bitmap->bitmap_cond);
DBUG_VOID_RETURN;
}
@@ -494,7 +650,7 @@ static void _ma_bitmap_unpin_all(MARIA_SHARE *share)
while (pinned_page-- != page_link)
pagecache_unlock_by_link(share->pagecache, pinned_page->link,
pinned_page->unlock, PAGECACHE_UNPIN,
- LSN_IMPOSSIBLE, LSN_IMPOSSIBLE, TRUE, TRUE);
+ LSN_IMPOSSIBLE, LSN_IMPOSSIBLE, FALSE, TRUE);
bitmap->pinned_pages.elements= 0;
DBUG_VOID_RETURN;
}
@@ -520,7 +676,7 @@ void _ma_bitmap_delete_all(MARIA_SHARE *share)
bzero(bitmap->map, bitmap->block_size);
bitmap->changed= 1;
bitmap->page= 0;
- bitmap->used_size= bitmap->total_size;
+ bitmap->used_size= bitmap->total_size= bitmap->max_total_size;
}
DBUG_VOID_RETURN;
}
@@ -534,7 +690,8 @@ void _ma_bitmap_delete_all(MARIA_SHARE *share)
@notes
This is called after we have swapped file descriptors and we want
- bitmap to forget all cached information
+ bitmap to forget all cached information.
+ It's also called directly after we have opened a file.
*/
void _ma_bitmap_reset_cache(MARIA_SHARE *share)
@@ -550,13 +707,20 @@ void _ma_bitmap_reset_cache(MARIA_SHARE *share)
We can't read a page yet, as in some case we don't have an active
page cache yet.
Pretend we have a dummy, full and not changed bitmap page in memory.
+
+ We set bitmap->page to a value so that if we use it in
+ move_to_next_bitmap() it will point to page 0.
+ (This can only happen if writing to a bitmap page fails)
*/
- bitmap->page= ~(ulonglong) 0;
- bitmap->used_size= bitmap->total_size;
+ bitmap->page= ((pgcache_page_no_t) 0) - bitmap->pages_covered;
+ bitmap->used_size= bitmap->total_size= bitmap->max_total_size;
bfill(bitmap->map, share->block_size, 255);
#ifndef DBUG_OFF
memcpy(bitmap->map + bitmap->block_size, bitmap->map, bitmap->block_size);
#endif
+
+ /* Start scanning for free space from start of file */
+ share->state.first_bitmap_with_space = 0;
}
}
@@ -680,7 +844,7 @@ static inline uint pattern_to_size(MARIA_FILE_BITMAP *bitmap, uint pattern)
Print bitmap for debugging
SYNOPSIS
- _ma_print_bitmap()
+ _ma_print_bitmap_changes()
bitmap Bitmap to print
IMPLEMENTATION
@@ -691,12 +855,6 @@ static inline uint pattern_to_size(MARIA_FILE_BITMAP *bitmap, uint pattern)
#ifndef DBUG_OFF
-const char *bits_to_txt[]=
-{
- "empty", "00-30% full", "30-60% full", "60-90% full", "full",
- "tail 00-40 % full", "tail 40-80 % full", "tail/blob full"
-};
-
static void _ma_print_bitmap_changes(MARIA_FILE_BITMAP *bitmap)
{
uchar *pos, *end, *org_pos;
@@ -747,12 +905,11 @@ void _ma_print_bitmap(MARIA_FILE_BITMAP *bitmap, uchar *data,
uchar *pos, *end;
char llbuff[22];
- end= bitmap->map + bitmap->used_size;
DBUG_LOCK_FILE;
fprintf(DBUG_FILE,"\nDump of bitmap page at %s\n", llstr(page, llbuff));
page++; /* Skip bitmap page */
- for (pos= data, end= pos + bitmap->total_size;
+ for (pos= data, end= pos + bitmap->max_total_size;
pos < end ;
pos+= 6)
{
@@ -781,6 +938,70 @@ void _ma_print_bitmap(MARIA_FILE_BITMAP *bitmap, uchar *data,
#endif /* DBUG_OFF */
+/*
+ Return content of bitmap as a printable string
+*/
+
+void _ma_get_bitmap_description(MARIA_FILE_BITMAP *bitmap,
+ uchar *bitmap_data,
+ pgcache_page_no_t page,
+ char *out)
+{
+ uchar *pos, *end;
+ uint count=0, dot_printed= 0, len;
+ char buff[80], last[80];
+
+ page++;
+ last[0]=0;
+ for (pos= bitmap_data, end= pos+ bitmap->used_size ; pos < end ; pos+= 6)
+ {
+ ulonglong bits= uint6korr(pos); /* 6 bytes = 6*8/3= 16 patterns */
+ uint i;
+
+ for (i= 0; i < 16 ; i++, bits>>= 3)
+ {
+ if (count > 60)
+ {
+ if (memcmp(buff, last, count))
+ {
+ memcpy(last, buff, count);
+ len= sprintf(out, "%8lu: ", (ulong) page - count);
+ memcpy(out+len, buff, count);
+ out+= len + count + 1;
+ out[-1]= '\n';
+ dot_printed= 0;
+ }
+ else if (!(dot_printed++))
+ {
+ out= strmov(out, "...\n");
+ }
+ count= 0;
+ }
+ buff[count++]= '0' + (uint) (bits & 7);
+ page++;
+ }
+ }
+ len= sprintf(out, "%8lu: ", (ulong) page - count);
+ memcpy(out+len, buff, count);
+ out[len + count]= '\n';
+ out[len + count + 1]= 0;
+}
+
+
+/*
+ Adjust bitmap->total_size to not go over max_data_file_size
+*/
+
+static void adjust_total_size(MARIA_HA *info, pgcache_page_no_t page)
+{
+ MARIA_FILE_BITMAP *bitmap= &info->s->bitmap;
+
+ if (page < bitmap->last_bitmap_page)
+ bitmap->total_size= bitmap->max_total_size; /* Use all bits in bitmap */
+ else
+ bitmap->total_size= bitmap->last_total_size;
+}
+
/***************************************************************************
Reading & writing bitmap pages
***************************************************************************/
@@ -817,12 +1038,16 @@ static my_bool _ma_read_bitmap_page(MARIA_HA *info,
DBUG_ASSERT(!bitmap->changed);
bitmap->page= page;
- if (((page + 1) * bitmap->block_size) > share->state.state.data_file_length)
+ if ((page + 1) * bitmap->block_size > share->state.state.data_file_length)
{
/* Inexistent or half-created page */
res= _ma_bitmap_create_missing(info, bitmap, page);
+ if (!res)
+ adjust_total_size(info, page);
DBUG_RETURN(res);
}
+
+ adjust_total_size(info, page);
bitmap->used_size= bitmap->total_size;
DBUG_ASSERT(share->pagecache->block_size == bitmap->block_size);
res= pagecache_read(share->pagecache,
@@ -871,6 +1096,13 @@ static my_bool _ma_change_bitmap_page(MARIA_HA *info,
{
DBUG_ENTER("_ma_change_bitmap_page");
+ /*
+ We have to mark the file changed here, as otherwise the following
+ read/write to pagecache may force a page out from this file, which would
+ cause _ma_mark_file_changed() to be called with bitmaplock hold!
+ */
+ _ma_bitmap_mark_file_changed(info->s, 1);
+
if (bitmap->changed)
{
if (write_changed_bitmap(info->s, bitmap))
@@ -906,14 +1138,18 @@ static my_bool move_to_next_bitmap(MARIA_HA *info, MARIA_FILE_BITMAP *bitmap)
MARIA_STATE_INFO *state= &info->s->state;
DBUG_ENTER("move_to_next_bitmap");
- if (state->first_bitmap_with_space != ~(ulonglong) 0 &&
+ if (state->first_bitmap_with_space != ~(pgcache_page_no_t) 0 &&
state->first_bitmap_with_space != page)
{
page= state->first_bitmap_with_space;
- state->first_bitmap_with_space= ~(ulonglong) 0;
+ state->first_bitmap_with_space= ~(pgcache_page_no_t) 0;
+ DBUG_ASSERT(page % bitmap->pages_covered == 0);
}
else
+ {
page+= bitmap->pages_covered;
+ DBUG_ASSERT(page % bitmap->pages_covered == 0);
+ }
DBUG_RETURN(_ma_change_bitmap_page(info, bitmap, page));
}
@@ -1308,10 +1544,7 @@ static ulong allocate_full_pages(MARIA_FILE_BITMAP *bitmap,
best_prefix_bits|= tmp;
int6store(best_data, best_prefix_bits);
if (!(best_area_size-= best_prefix_area_size))
- {
- DBUG_EXECUTE("bitmap", _ma_print_bitmap_changes(bitmap););
- DBUG_RETURN(block->page_count);
- }
+ goto end;
best_data+= 6;
}
best_area_size*= 3; /* Bits to set */
@@ -1329,6 +1562,7 @@ static ulong allocate_full_pages(MARIA_FILE_BITMAP *bitmap,
bitmap->used_size= (uint) (best_data - bitmap->map);
DBUG_ASSERT(bitmap->used_size <= bitmap->total_size);
}
+end:
bitmap->changed= 1;
DBUG_EXECUTE("bitmap", _ma_print_bitmap_changes(bitmap););
DBUG_RETURN(block->page_count);
@@ -1621,7 +1855,7 @@ static void use_head(MARIA_HA *info, pgcache_page_no_t page, uint size,
find_where_to_split_row()
share Maria share
row Information of what is in the row (from calc_record_size())
- extents_length Number of bytes needed to store all extents
+ extents Max number of extents we have to store in header
split_size Free size on the page (The head length must be less
than this)
@@ -1630,7 +1864,7 @@ static void use_head(MARIA_HA *info, pgcache_page_no_t page, uint size,
*/
static uint find_where_to_split_row(MARIA_SHARE *share, MARIA_ROW *row,
- uint extents_length, uint split_size)
+ uint extents, uint split_size)
{
uint *lengths, *lengths_end;
/*
@@ -1640,19 +1874,20 @@ static uint find_where_to_split_row(MARIA_SHARE *share, MARIA_ROW *row,
- One extent
*/
uint row_length= (row->min_length +
- size_to_store_key_length(extents_length) +
+ size_to_store_key_length(extents) +
ROW_EXTENT_SIZE);
- DBUG_ASSERT(row_length < split_size);
+ DBUG_ASSERT(row_length <= split_size);
+
/*
Store first in all_field_lengths the different parts that are written
to the row. This needs to be in same order as in
ma_block_rec.c::write_block_record()
*/
- row->null_field_lengths[-3]= extents_length;
+ row->null_field_lengths[-3]= extents * ROW_EXTENT_SIZE;
row->null_field_lengths[-2]= share->base.fixed_not_null_fields_length;
row->null_field_lengths[-1]= row->field_lengths_length;
for (lengths= row->null_field_lengths - EXTRA_LENGTH_FIELDS,
- lengths_end= (lengths + share->base.pack_fields - share->base.blobs +
+ lengths_end= (lengths + share->base.fields - share->base.blobs +
EXTRA_LENGTH_FIELDS); lengths < lengths_end; lengths++)
{
if (row_length + *lengths > split_size)
@@ -1808,18 +2043,19 @@ my_bool _ma_bitmap_find_place(MARIA_HA *info, MARIA_ROW *row,
head_length+= ELEMENTS_RESERVED_FOR_MAIN_PART * ROW_EXTENT_SIZE;
/* The first segment size is stored in 'row_length' */
- row_length= find_where_to_split_row(share, row, extents_length,
+ row_length= find_where_to_split_row(share, row, row->extents_count +
+ ELEMENTS_RESERVED_FOR_MAIN_PART-1,
max_page_size);
full_page_size= MAX_TAIL_SIZE(share->block_size);
position= 0;
- if (head_length - row_length <= full_page_size)
+ rest_length= head_length - row_length;
+ if (rest_length <= full_page_size)
position= ELEMENTS_RESERVED_FOR_MAIN_PART -2; /* Only head and tail */
if (find_head(info, row_length, position))
goto abort;
row->space_on_head_page= row_length;
- rest_length= head_length - row_length;
if (write_rest_of_head(info, position, rest_length))
goto abort;
@@ -1886,8 +2122,7 @@ my_bool _ma_bitmap_find_new_place(MARIA_HA *info, MARIA_ROW *row,
goto abort;
/* Switch bitmap to current head page */
- bitmap_page= page / share->bitmap.pages_covered;
- bitmap_page*= share->bitmap.pages_covered;
+ bitmap_page= page - page % share->bitmap.pages_covered;
if (share->bitmap.page != bitmap_page &&
_ma_change_bitmap_page(info, &share->bitmap, bitmap_page))
@@ -1906,16 +2141,22 @@ my_bool _ma_bitmap_find_new_place(MARIA_HA *info, MARIA_ROW *row,
/* Allocate enough space */
head_length+= ELEMENTS_RESERVED_FOR_MAIN_PART * ROW_EXTENT_SIZE;
- /* The first segment size is stored in 'row_length' */
- row_length= find_where_to_split_row(share, row, extents_length, free_size);
+ /*
+ The first segment size is stored in 'row_length'
+ We have to add ELEMENTS_RESERVED_FOR_MAIN_PART here as the extent
+ information may be up to this size when the header splits.
+ */
+ row_length= find_where_to_split_row(share, row, row->extents_count +
+ ELEMENTS_RESERVED_FOR_MAIN_PART-1,
+ free_size);
position= 0;
- if (head_length - row_length < MAX_TAIL_SIZE(share->block_size))
+ rest_length= head_length - row_length;
+ if (rest_length <= MAX_TAIL_SIZE(share->block_size))
position= ELEMENTS_RESERVED_FOR_MAIN_PART -2; /* Only head and tail */
use_head(info, page, row_length, position);
row->space_on_head_page= row_length;
- rest_length= head_length - row_length;
if (write_rest_of_head(info, position, rest_length))
goto abort;
@@ -2003,7 +2244,7 @@ static my_bool set_page_bits(MARIA_HA *info, MARIA_FILE_BITMAP *bitmap,
Get bitmap pattern for a given page
SYNOPSIS
- get_page_bits()
+ bitmap_get_page_bits()
info Maria handler
bitmap Bitmap handler
page Page number
@@ -2013,8 +2254,8 @@ static my_bool set_page_bits(MARIA_HA *info, MARIA_FILE_BITMAP *bitmap,
~0 Error (couldn't read page)
*/
-uint _ma_bitmap_get_page_bits(MARIA_HA *info, MARIA_FILE_BITMAP *bitmap,
- pgcache_page_no_t page)
+static uint bitmap_get_page_bits(MARIA_HA *info, MARIA_FILE_BITMAP *bitmap,
+ pgcache_page_no_t page)
{
pgcache_page_no_t bitmap_page;
uint offset_page, offset, tmp;
@@ -2040,6 +2281,19 @@ uint _ma_bitmap_get_page_bits(MARIA_HA *info, MARIA_FILE_BITMAP *bitmap,
}
+/* As above, but take a lock while getting the data */
+
+uint _ma_bitmap_get_page_bits(MARIA_HA *info, MARIA_FILE_BITMAP *bitmap,
+ pgcache_page_no_t page)
+{
+ uint tmp;
+ mysql_mutex_lock(&bitmap->bitmap_lock);
+ tmp= bitmap_get_page_bits(info, bitmap, page);
+ mysql_mutex_unlock(&bitmap->bitmap_lock);
+ return tmp;
+}
+
+
/*
Mark all pages in a region as free
@@ -2119,6 +2373,7 @@ my_bool _ma_bitmap_reset_full_page_bits(MARIA_HA *info,
DBUG_RETURN(0);
}
+
/*
Set all pages in a region as used
@@ -2151,7 +2406,7 @@ my_bool _ma_bitmap_set_full_page_bits(MARIA_HA *info,
bitmap_page= page - page % bitmap->pages_covered;
if (page == bitmap_page ||
- page + page_count >= bitmap_page + bitmap->pages_covered)
+ page + page_count > bitmap_page + bitmap->pages_covered)
{
DBUG_ASSERT(0); /* Wrong in data */
DBUG_RETURN(1);
@@ -2250,7 +2505,7 @@ void _ma_bitmap_flushable(MARIA_HA *info, int non_flushable_inc)
the bitmap's mutex.
*/
_ma_bitmap_unpin_all(share);
- if (unlikely(bitmap->flush_all_requested))
+ if (unlikely(bitmap->waiting_for_non_flushable))
{
DBUG_PRINT("info", ("bitmap flushable waking up flusher"));
mysql_cond_broadcast(&bitmap->bitmap_cond);
@@ -2263,6 +2518,8 @@ void _ma_bitmap_flushable(MARIA_HA *info, int non_flushable_inc)
}
DBUG_ASSERT(non_flushable_inc == 1);
DBUG_ASSERT(info->non_flushable_state == 0);
+
+ bitmap->waiting_for_flush_all_requested++;
while (unlikely(bitmap->flush_all_requested))
{
/*
@@ -2279,6 +2536,7 @@ void _ma_bitmap_flushable(MARIA_HA *info, int non_flushable_inc)
DBUG_PRINT("info", ("waiting for bitmap flusher"));
mysql_cond_wait(&bitmap->bitmap_cond, &bitmap->bitmap_lock);
}
+ bitmap->waiting_for_flush_all_requested--;
bitmap->non_flushable++;
DBUG_PRINT("info", ("bitmap->non_flushable: %u", bitmap->non_flushable));
mysql_mutex_unlock(&bitmap->bitmap_lock);
@@ -2352,7 +2610,7 @@ my_bool _ma_bitmap_release_unused(MARIA_HA *info, MARIA_BITMAP_BLOCKS *blocks)
else
{
DBUG_ASSERT(current_bitmap_value ==
- _ma_bitmap_get_page_bits(info, bitmap, block->page));
+ bitmap_get_page_bits(info, bitmap, block->page));
}
/* Handle all full pages and tail pages (for head page and blob) */
@@ -2383,16 +2641,14 @@ my_bool _ma_bitmap_release_unused(MARIA_HA *info, MARIA_BITMAP_BLOCKS *blocks)
The page has all bits set; The following test is an optimization
to not set the bits to the same value as before.
*/
+ DBUG_ASSERT(current_bitmap_value ==
+ bitmap_get_page_bits(info, bitmap, block->page));
+
if (bits != current_bitmap_value)
{
if (set_page_bits(info, bitmap, block->page, bits))
goto err;
}
- else
- {
- DBUG_ASSERT(current_bitmap_value ==
- _ma_bitmap_get_page_bits(info, bitmap, block->page));
- }
}
else if (!(block->used & BLOCKUSED_USED) &&
_ma_bitmap_reset_full_page_bits(info, bitmap,
@@ -2408,7 +2664,7 @@ my_bool _ma_bitmap_release_unused(MARIA_HA *info, MARIA_BITMAP_BLOCKS *blocks)
if (--bitmap->non_flushable == 0)
{
_ma_bitmap_unpin_all(info->s);
- if (unlikely(bitmap->flush_all_requested))
+ if (unlikely(bitmap->waiting_for_non_flushable))
{
DBUG_PRINT("info", ("bitmap flushable waking up flusher"));
mysql_cond_broadcast(&bitmap->bitmap_cond);
@@ -2448,9 +2704,9 @@ my_bool _ma_bitmap_free_full_pages(MARIA_HA *info, const uchar *extents,
uint count)
{
MARIA_FILE_BITMAP *bitmap= &info->s->bitmap;
+ my_bool res;
DBUG_ENTER("_ma_bitmap_free_full_pages");
- mysql_mutex_lock(&bitmap->bitmap_lock);
for (; count--; extents+= ROW_EXTENT_SIZE)
{
pgcache_page_no_t page= uint5korr(extents);
@@ -2461,15 +2717,15 @@ my_bool _ma_bitmap_free_full_pages(MARIA_HA *info, const uchar *extents,
if (page == 0 && page_count == 0)
continue; /* Not used extent */
if (pagecache_delete_pages(info->s->pagecache, &info->dfile, page,
- page_count, PAGECACHE_LOCK_WRITE, 1) ||
- _ma_bitmap_reset_full_page_bits(info, bitmap, page, page_count))
- {
- mysql_mutex_unlock(&bitmap->bitmap_lock);
+ page_count, PAGECACHE_LOCK_WRITE, 1))
+ DBUG_RETURN(1);
+ mysql_mutex_lock(&bitmap->bitmap_lock);
+ res= _ma_bitmap_reset_full_page_bits(info, bitmap, page, page_count);
+ mysql_mutex_unlock(&bitmap->bitmap_lock);
+ if (res)
DBUG_RETURN(1);
- }
}
}
- mysql_mutex_unlock(&bitmap->bitmap_lock);
DBUG_RETURN(0);
}
@@ -2521,17 +2777,15 @@ my_bool _ma_bitmap_set(MARIA_HA *info, pgcache_page_no_t page, my_bool head,
page_type What kind of page this is
page Adress to page
empty_space Empty space on page
- bitmap_pattern Store here the pattern that was in the bitmap for the
- page. This is always updated.
+ bitmap_pattern Bitmap pattern for page (from bitmap)
RETURN
0 ok
1 error
*/
-my_bool _ma_check_bitmap_data(MARIA_HA *info,
- enum en_page_type page_type, pgcache_page_no_t page,
- uint empty_space, uint *bitmap_pattern)
+my_bool _ma_check_bitmap_data(MARIA_HA *info, enum en_page_type page_type,
+ uint empty_space, uint bitmap_pattern)
{
uint bits;
switch (page_type) {
@@ -2552,8 +2806,7 @@ my_bool _ma_check_bitmap_data(MARIA_HA *info,
bits= 0; /* to satisfy compiler */
DBUG_ASSERT(0);
}
- return ((*bitmap_pattern= _ma_bitmap_get_page_bits(info, &info->s->bitmap,
- page)) != bits);
+ return (bitmap_pattern != bits);
}
@@ -2798,6 +3051,11 @@ static my_bool _ma_bitmap_create_missing(MARIA_HA *info,
/* First (in offset order) bitmap page to create */
if (data_file_length < block_size)
goto err; /* corrupted, should have first bitmap page */
+ if (page * block_size >= share->base.max_data_file_length)
+ {
+ my_errno= HA_ERR_RECORD_FILE_FULL;
+ goto err;
+ }
from= (data_file_length / block_size - 1) / bitmap->pages_covered + 1;
from*= bitmap->pages_covered;