Skip to content

Commit fc25437

Browse files
committed
MDEV-10104 Table lock race condition with replication
Problem was two race condtion in Aria page cache: - find_block() didn't inform free_block() that it had released requests - free_block() didn't handle pinned blocks, which could happen if free_block() was called as part of flush. This is fixed by not freeing blocks that are pinned. This is safe as when maria_close() is called when last thread is using a table, there can be no pinned blocks. For other flush calls it's safe to ignore pinned blocks.
1 parent e3521ab commit fc25437

File tree

1 file changed

+46
-13
lines changed

1 file changed

+46
-13
lines changed

storage/maria/ma_pagecache.c

Lines changed: 46 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -493,7 +493,8 @@ static my_bool info_check_lock(PAGECACHE_BLOCK_LINK *block,
493493

494494
#define FLUSH_CACHE 2000 /* sort this many blocks at once */
495495

496-
static void free_block(PAGECACHE *pagecache, PAGECACHE_BLOCK_LINK *block);
496+
static my_bool free_block(PAGECACHE *pagecache, PAGECACHE_BLOCK_LINK *block,
497+
my_bool abort_if_pinned);
497498
static void unlink_hash(PAGECACHE *pagecache, PAGECACHE_HASH_LINK *hash_link);
498499
#ifndef DBUG_OFF
499500
static void test_key_cache(PAGECACHE *pagecache,
@@ -1939,7 +1940,7 @@ static PAGECACHE_BLOCK_LINK *find_block(PAGECACHE *pagecache,
19391940
removed from the cache as we set the PCBLOCK_REASSIGNED
19401941
flag (see the code below that handles reading requests).
19411942
*/
1942-
free_block(pagecache, block);
1943+
free_block(pagecache, block, 0);
19431944
return 0;
19441945
}
19451946
/* Wait until the page is flushed on disk */
@@ -1950,7 +1951,7 @@ static PAGECACHE_BLOCK_LINK *find_block(PAGECACHE *pagecache,
19501951
/* Invalidate page in the block if it has not been done yet */
19511952
DBUG_ASSERT(block->status); /* Should always be true */
19521953
if (block->status)
1953-
free_block(pagecache, block);
1954+
free_block(pagecache, block, 0);
19541955
return 0;
19551956
}
19561957

@@ -1975,8 +1976,13 @@ static PAGECACHE_BLOCK_LINK *find_block(PAGECACHE *pagecache,
19751976
}
19761977
else
19771978
{
1978-
DBUG_ASSERT(hash_link->requests > 0);
1979-
hash_link->requests--;
1979+
/*
1980+
When we come here either PCBLOCK_REASSIGNED or PCBLOCK_IN_SWITCH are
1981+
active. In both cases wqueue_release_queue() is called when the
1982+
state changes.
1983+
*/
1984+
DBUG_ASSERT(block->hash_link == hash_link);
1985+
remove_reader(block);
19801986
KEYCACHE_DBUG_PRINT("find_block",
19811987
("request waiting for old page to be saved"));
19821988
{
@@ -3635,7 +3641,7 @@ static my_bool pagecache_delete_internal(PAGECACHE *pagecache,
36353641
DBUG_ASSERT(block->hash_link->requests > 0);
36363642
page_link->requests--;
36373643
/* See NOTE for pagecache_unlock() about registering requests. */
3638-
free_block(pagecache, block);
3644+
free_block(pagecache, block, 0);
36393645
dec_counter_for_resize_op(pagecache);
36403646
return 0;
36413647

@@ -4227,7 +4233,8 @@ my_bool pagecache_write_part(PAGECACHE *pagecache,
42274233
and add it to the free list.
42284234
*/
42294235

4230-
static void free_block(PAGECACHE *pagecache, PAGECACHE_BLOCK_LINK *block)
4236+
static my_bool free_block(PAGECACHE *pagecache, PAGECACHE_BLOCK_LINK *block,
4237+
my_bool abort_if_pinned)
42314238
{
42324239
uint status= block->status;
42334240
KEYCACHE_THREAD_TRACE("free block");
@@ -4241,11 +4248,27 @@ static void free_block(PAGECACHE *pagecache, PAGECACHE_BLOCK_LINK *block)
42414248
/*
42424249
While waiting for readers to finish, new readers might request the
42434250
block. But since we set block->status|= PCBLOCK_REASSIGNED, they
4244-
will wait on block->wqueue[COND_FOR_SAVED]. They must be signalled
4251+
will wait on block->wqueue[COND_FOR_SAVED]. They must be signaled
42454252
later.
42464253
*/
42474254
block->status|= PCBLOCK_REASSIGNED;
42484255
wait_for_readers(pagecache, block);
4256+
if (unlikely(abort_if_pinned) && unlikely(block->pins))
4257+
{
4258+
/*
4259+
Block got pinned while waiting for readers.
4260+
This can only happens when called from flush_pagecache_blocks_int()
4261+
when flushing blocks as part of prepare for maria_close() or from
4262+
flush_cached_blocks()
4263+
*/
4264+
block->status&= ~PCBLOCK_REASSIGNED;
4265+
unreg_request(pagecache, block, 0);
4266+
4267+
/* All pending requests for this page must be resubmitted. */
4268+
if (block->wqueue[COND_FOR_SAVED].last_thread)
4269+
wqueue_release_queue(&block->wqueue[COND_FOR_SAVED]);
4270+
return 1;
4271+
}
42494272
unlink_hash(pagecache, block->hash_link);
42504273
}
42514274

@@ -4296,6 +4319,8 @@ static void free_block(PAGECACHE *pagecache, PAGECACHE_BLOCK_LINK *block)
42964319
/* All pending requests for this page must be resubmitted. */
42974320
if (block->wqueue[COND_FOR_SAVED].last_thread)
42984321
wqueue_release_queue(&block->wqueue[COND_FOR_SAVED]);
4322+
4323+
return 0;
42994324
}
43004325

43014326

@@ -4431,9 +4456,16 @@ static int flush_cached_blocks(PAGECACHE *pagecache,
44314456
if (! (type == FLUSH_KEEP || type == FLUSH_KEEP_LAZY ||
44324457
type == FLUSH_FORCE_WRITE))
44334458
{
4434-
pagecache->blocks_changed--;
4435-
pagecache->global_blocks_changed--;
4436-
free_block(pagecache, block);
4459+
if (!free_block(pagecache, block, 1))
4460+
{
4461+
pagecache->blocks_changed--;
4462+
pagecache->global_blocks_changed--;
4463+
}
4464+
else
4465+
{
4466+
block->status&= ~PCBLOCK_IN_FLUSH;
4467+
link_to_file_list(pagecache, block, file, 1);
4468+
}
44374469
}
44384470
else
44394471
{
@@ -4671,7 +4703,7 @@ static int flush_pagecache_blocks_int(PAGECACHE *pagecache,
46714703
/* It's a temporary file */
46724704
pagecache->blocks_changed--;
46734705
pagecache->global_blocks_changed--;
4674-
free_block(pagecache, block);
4706+
free_block(pagecache, block, 0);
46754707
}
46764708
}
46774709
else if (type != FLUSH_KEEP_LAZY)
@@ -4741,11 +4773,12 @@ static int flush_pagecache_blocks_int(PAGECACHE *pagecache,
47414773
#endif
47424774
next= block->next_changed;
47434775
if (block->hash_link->file.file == file->file &&
4776+
!block->pins &&
47444777
(! (block->status & PCBLOCK_CHANGED)
47454778
|| type == FLUSH_IGNORE_CHANGED))
47464779
{
47474780
reg_requests(pagecache, block, 1);
4748-
free_block(pagecache, block);
4781+
free_block(pagecache, block, 1);
47494782
}
47504783
}
47514784
}

0 commit comments

Comments
 (0)