Skip to content

Commit e0ba68b

Browse files
committed
MDEV-23510: arm64 lf_hash alignment of pointers
Like the 10.2 version 1635686, except C++ on internal functions for my_assume_aligned. volatile != atomic. volatile has no memory barrier schemantics, its for mmaped IO so lets allow some optimizer gains and stop pretending it helps with memory atomicity. The MDEV lists a SEGV an assumption is made that an address was partially read. As C packs structs strictly in order and on arm64 the cache line size is 128 bits. A pointer (link - 64 bits), followed by a hashnr (uint32 - 32 bits), leaves the following key (uchar * 64 bits), neither naturally aligned to any pointer and worse, split across a cache line which is the processors view of an atomic reservation of memory. lf_dynarray_lvalue is assumed to return a 64 bit aligned address. As a solution move the 32bit hashnr to the end so we don't get the *key pointer split across two cache lines. Tested by: Krunal Bauskar Reviewer: Marko Mäkelä
1 parent cea0328 commit e0ba68b

File tree

3 files changed

+35
-24
lines changed

3 files changed

+35
-24
lines changed

include/lf.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ void *lf_alloc_new(LF_PINS *pins);
125125
C_MODE_END
126126

127127
/*
128-
extendible hash, lf_hash.c
128+
extendible hash, lf_hash.cc
129129
*/
130130
#include <hash.h>
131131

mysys/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ SET(MYSYS_SOURCES array.c charset-def.c charset.c crc32ieee.cc my_default.c
3939
tree.c typelib.c base64.c my_memmem.c
4040
my_getpagesize.c
4141
guess_malloc_library.c
42-
lf_alloc-pin.c lf_dynarray.c lf_hash.c
42+
lf_alloc-pin.c lf_dynarray.c lf_hash.cc
4343
safemalloc.c my_new.cc
4444
my_getncpus.c my_safehash.c my_chmod.c my_rnd.c
4545
my_uuid.c wqueue.c waiting_threads.c ma_dyncol.c ../sql-common/my_time.c

mysys/lf_hash.c renamed to mysys/lf_hash.cc

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,14 @@
2828
#include <my_bit.h>
2929
#include <lf.h>
3030
#include "my_cpu.h"
31+
#include "assume_aligned.h"
3132

3233
/* An element of the list */
3334
typedef struct {
34-
intptr volatile link; /* a pointer to the next element in a list and a flag */
35-
uint32 hashnr; /* reversed hash number, for sorting */
35+
intptr link; /* a pointer to the next element in a list and a flag */
3636
const uchar *key;
3737
size_t keylen;
38+
uint32 hashnr; /* reversed hash number, for sorting */
3839
/*
3940
data is stored here, directly after the keylen.
4041
thus the pointer to data is (void*)(slist_element_ptr+1)
@@ -48,7 +49,7 @@ const int LF_HASH_OVERHEAD= sizeof(LF_SLIST);
4849
in a list) from l_find to l_insert/l_delete
4950
*/
5051
typedef struct {
51-
intptr volatile *prev;
52+
intptr *prev;
5253
LF_SLIST *curr, *next;
5354
} CURSOR;
5455

@@ -85,7 +86,7 @@ typedef struct {
8586
0 - ok
8687
1 - error (callbck returned 1)
8788
*/
88-
static int l_find(LF_SLIST * volatile *head, CHARSET_INFO *cs, uint32 hashnr,
89+
static int l_find(LF_SLIST **head, CHARSET_INFO *cs, uint32 hashnr,
8990
const uchar *key, size_t keylen, CURSOR *cursor, LF_PINS *pins,
9091
my_hash_walk_action callback)
9192
{
@@ -98,11 +99,11 @@ static int l_find(LF_SLIST * volatile *head, CHARSET_INFO *cs, uint32 hashnr,
9899
DBUG_ASSERT(!keylen || !callback); /* should not be set both */
99100

100101
retry:
101-
cursor->prev= (intptr *)head;
102+
cursor->prev= (intptr *) my_assume_aligned<sizeof(intptr)>(head);
102103
do { /* PTR() isn't necessary below, head is a dummy node */
103-
cursor->curr= (LF_SLIST *)(*cursor->prev);
104+
cursor->curr= my_assume_aligned<sizeof(LF_SLIST *)>((LF_SLIST *)(*cursor->prev));
104105
lf_pin(pins, 1, cursor->curr);
105-
} while (my_atomic_loadptr((void**)cursor->prev) != cursor->curr &&
106+
} while (my_atomic_loadptr((void **)my_assume_aligned<sizeof(LF_SLIST *)>(cursor->prev)) != cursor->curr &&
106107
LF_BACKOFF());
107108
for (;;)
108109
{
@@ -111,11 +112,14 @@ static int l_find(LF_SLIST * volatile *head, CHARSET_INFO *cs, uint32 hashnr,
111112

112113
cur_hashnr= cursor->curr->hashnr;
113114
cur_keylen= cursor->curr->keylen;
115+
/* The key element needs to be aligned, not necessary what it points to */
116+
my_assume_aligned<sizeof(const uchar *)>(&cursor->curr->key);
114117
cur_key= cursor->curr->key;
115118

116119
do {
120+
/* attempting to my_assume_aligned onlink below broke the implementation */
117121
link= cursor->curr->link;
118-
cursor->next= PTR(link);
122+
cursor->next= my_assume_aligned<sizeof(LF_SLIST *)>(PTR(link));
119123
lf_pin(pins, 0, cursor->next);
120124
} while (link != cursor->curr->link && LF_BACKOFF());
121125

@@ -155,6 +159,10 @@ static int l_find(LF_SLIST * volatile *head, CHARSET_INFO *cs, uint32 hashnr,
155159
}
156160
}
157161

162+
163+
/* static l_find is the only user my_assume_aligned, keep the rest as c scoped */
164+
C_MODE_START
165+
158166
/*
159167
DESCRIPTION
160168
insert a 'node' in the list that starts from 'head' in the correct
@@ -168,7 +176,7 @@ static int l_find(LF_SLIST * volatile *head, CHARSET_INFO *cs, uint32 hashnr,
168176
it uses pins[0..2], on return all pins are removed.
169177
if there're nodes with the same key value, a new node is added before them.
170178
*/
171-
static LF_SLIST *l_insert(LF_SLIST * volatile *head, CHARSET_INFO *cs,
179+
static LF_SLIST *l_insert(LF_SLIST **head, CHARSET_INFO *cs,
172180
LF_SLIST *node, LF_PINS *pins, uint flags)
173181
{
174182
CURSOR cursor;
@@ -220,7 +228,7 @@ static LF_SLIST *l_insert(LF_SLIST * volatile *head, CHARSET_INFO *cs,
220228
NOTE
221229
it uses pins[0..2], on return all pins are removed.
222230
*/
223-
static int l_delete(LF_SLIST * volatile *head, CHARSET_INFO *cs, uint32 hashnr,
231+
static int l_delete(LF_SLIST **head, CHARSET_INFO *cs, uint32 hashnr,
224232
const uchar *key, uint keylen, LF_PINS *pins)
225233
{
226234
CURSOR cursor;
@@ -278,7 +286,7 @@ static int l_delete(LF_SLIST * volatile *head, CHARSET_INFO *cs, uint32 hashnr,
278286
it uses pins[0..2], on return the pin[2] keeps the node found
279287
all other pins are removed.
280288
*/
281-
static LF_SLIST *l_search(LF_SLIST * volatile *head, CHARSET_INFO *cs,
289+
static LF_SLIST *l_search(LF_SLIST **head, CHARSET_INFO *cs,
282290
uint32 hashnr, const uchar *key, uint keylen,
283291
LF_PINS *pins)
284292
{
@@ -319,13 +327,14 @@ static inline my_hash_value_type calc_hash(CHARSET_INFO *cs,
319327

320328
#define MAX_LOAD 1.0 /* average number of elements in a bucket */
321329

322-
static int initialize_bucket(LF_HASH *, LF_SLIST * volatile*, uint, LF_PINS *);
330+
static int initialize_bucket(LF_HASH *, LF_SLIST **, uint, LF_PINS *);
323331

324332
static void default_initializer(LF_HASH *hash, void *dst, const void *src)
325333
{
326334
memcpy(dst, src, hash->element_size);
327335
}
328336

337+
329338
/*
330339
Initializes lf_hash, the arguments are compatible with hash_init
331340
@@ -398,7 +407,7 @@ void lf_hash_destroy(LF_HASH *hash)
398407
int lf_hash_insert(LF_HASH *hash, LF_PINS *pins, const void *data)
399408
{
400409
int csize, bucket, hashnr;
401-
LF_SLIST *node, * volatile *el;
410+
LF_SLIST *node, **el;
402411

403412
node= (LF_SLIST *)lf_alloc_new(pins);
404413
if (unlikely(!node))
@@ -407,7 +416,7 @@ int lf_hash_insert(LF_HASH *hash, LF_PINS *pins, const void *data)
407416
node->key= hash_key(hash, (uchar *)(node+1), &node->keylen);
408417
hashnr= hash->hash_function(hash->charset, node->key, node->keylen) & INT_MAX32;
409418
bucket= hashnr % hash->size;
410-
el= lf_dynarray_lvalue(&hash->array, bucket);
419+
el= (LF_SLIST **)lf_dynarray_lvalue(&hash->array, bucket);
411420
if (unlikely(!el))
412421
return -1;
413422
if (*el == NULL && unlikely(initialize_bucket(hash, el, bucket, pins)))
@@ -437,15 +446,15 @@ int lf_hash_insert(LF_HASH *hash, LF_PINS *pins, const void *data)
437446
*/
438447
int lf_hash_delete(LF_HASH *hash, LF_PINS *pins, const void *key, uint keylen)
439448
{
440-
LF_SLIST * volatile *el;
449+
LF_SLIST **el;
441450
uint bucket, hashnr;
442451

443452
hashnr= hash->hash_function(hash->charset, (uchar *)key, keylen) & INT_MAX32;
444453

445454
/* hide OOM errors - if we cannot initialize a bucket, try the previous one */
446455
for (bucket= hashnr % hash->size; ;bucket= my_clear_highest_bit(bucket))
447456
{
448-
el= lf_dynarray_lvalue(&hash->array, bucket);
457+
el= (LF_SLIST **)lf_dynarray_lvalue(&hash->array, bucket);
449458
if (el && (*el || initialize_bucket(hash, el, bucket, pins) == 0))
450459
break;
451460
if (unlikely(bucket == 0))
@@ -473,13 +482,13 @@ void *lf_hash_search_using_hash_value(LF_HASH *hash, LF_PINS *pins,
473482
my_hash_value_type hashnr,
474483
const void *key, uint keylen)
475484
{
476-
LF_SLIST * volatile *el, *found;
485+
LF_SLIST **el, *found;
477486
uint bucket;
478487

479488
/* hide OOM errors - if we cannot initialize a bucket, try the previous one */
480489
for (bucket= hashnr % hash->size; ;bucket= my_clear_highest_bit(bucket))
481490
{
482-
el= lf_dynarray_lvalue(&hash->array, bucket);
491+
el= (LF_SLIST **)lf_dynarray_lvalue(&hash->array, bucket);
483492
if (el && (*el || initialize_bucket(hash, el, bucket, pins) == 0))
484493
break;
485494
if (unlikely(bucket == 0))
@@ -507,9 +516,9 @@ int lf_hash_iterate(LF_HASH *hash, LF_PINS *pins,
507516
CURSOR cursor;
508517
uint bucket= 0;
509518
int res;
510-
LF_SLIST * volatile *el;
519+
LF_SLIST **el;
511520

512-
el= lf_dynarray_lvalue(&hash->array, bucket);
521+
el= (LF_SLIST **)lf_dynarray_lvalue(&hash->array, bucket);
513522
if (unlikely(!el))
514523
return 0; /* if there's no bucket==0, the hash is empty */
515524
if (*el == NULL && unlikely(initialize_bucket(hash, el, bucket, pins)))
@@ -539,14 +548,14 @@ static const uchar *dummy_key= (uchar*)"";
539548
0 - ok
540549
-1 - out of memory
541550
*/
542-
static int initialize_bucket(LF_HASH *hash, LF_SLIST * volatile *node,
551+
static int initialize_bucket(LF_HASH *hash, LF_SLIST **node,
543552
uint bucket, LF_PINS *pins)
544553
{
545554
uint parent= my_clear_highest_bit(bucket);
546555
LF_SLIST *dummy= (LF_SLIST *)my_malloc(key_memory_lf_slist,
547556
sizeof(LF_SLIST), MYF(MY_WME));
548557
LF_SLIST **tmp= 0, *cur;
549-
LF_SLIST * volatile *el= lf_dynarray_lvalue(&hash->array, parent);
558+
LF_SLIST **el= (LF_SLIST **)lf_dynarray_lvalue(&hash->array, parent);
550559
if (unlikely(!el || !dummy))
551560
return -1;
552561
if (*el == NULL && bucket &&
@@ -574,3 +583,5 @@ static int initialize_bucket(LF_HASH *hash, LF_SLIST * volatile *node,
574583
*/
575584
return 0;
576585
}
586+
587+
C_MODE_END

0 commit comments

Comments
 (0)