11a95404fSEmilio G. Cota /* 21a95404fSEmilio G. Cota * Copyright (C) 2016, Emilio G. Cota <cota@braap.org> 31a95404fSEmilio G. Cota * 41a95404fSEmilio G. Cota * License: GNU GPL, version 2 or later. 51a95404fSEmilio G. Cota * See the COPYING file in the top-level directory. 61a95404fSEmilio G. Cota */ 71a95404fSEmilio G. Cota #include "qemu/osdep.h" 81a95404fSEmilio G. Cota #include "qemu/qht.h" 99c7d64ebSEmilio G. Cota #include "qemu/rcu.h" 101a95404fSEmilio G. Cota 111a95404fSEmilio G. Cota #define N 5000 121a95404fSEmilio G. Cota 131a95404fSEmilio G. Cota static struct qht ht; 141a95404fSEmilio G. Cota static int32_t arr[N * 2]; 151a95404fSEmilio G. Cota 1661b8cef1SEmilio G. Cota static bool is_equal(const void *ap, const void *bp) 171a95404fSEmilio G. Cota { 1861b8cef1SEmilio G. Cota const int32_t *a = ap; 1961b8cef1SEmilio G. Cota const int32_t *b = bp; 201a95404fSEmilio G. Cota 211a95404fSEmilio G. Cota return *a == *b; 221a95404fSEmilio G. Cota } 231a95404fSEmilio G. Cota 241a95404fSEmilio G. Cota static void insert(int a, int b) 251a95404fSEmilio G. Cota { 261a95404fSEmilio G. Cota int i; 271a95404fSEmilio G. Cota 281a95404fSEmilio G. Cota for (i = a; i < b; i++) { 291a95404fSEmilio G. Cota uint32_t hash; 3032359d52SEmilio G. Cota void *existing; 3132359d52SEmilio G. Cota bool inserted; 321a95404fSEmilio G. Cota 331a95404fSEmilio G. Cota arr[i] = i; 341a95404fSEmilio G. Cota hash = i; 351a95404fSEmilio G. Cota 3632359d52SEmilio G. Cota inserted = qht_insert(&ht, &arr[i], hash, NULL); 3732359d52SEmilio G. Cota g_assert_true(inserted); 3832359d52SEmilio G. Cota inserted = qht_insert(&ht, &arr[i], hash, &existing); 3932359d52SEmilio G. Cota g_assert_false(inserted); 4032359d52SEmilio G. Cota g_assert_true(existing == &arr[i]); 411a95404fSEmilio G. Cota } 421a95404fSEmilio G. Cota } 431a95404fSEmilio G. Cota 44f44641bbSEmilio G. Cota static void do_rm(int init, int end, bool exist) 451a95404fSEmilio G. Cota { 461a95404fSEmilio G. Cota int i; 471a95404fSEmilio G. Cota 481a95404fSEmilio G. Cota for (i = init; i < end; i++) { 491a95404fSEmilio G. Cota uint32_t hash; 501a95404fSEmilio G. Cota 511a95404fSEmilio G. Cota hash = arr[i]; 52f44641bbSEmilio G. Cota if (exist) { 531a95404fSEmilio G. Cota g_assert_true(qht_remove(&ht, &arr[i], hash)); 54f44641bbSEmilio G. Cota } else { 55f44641bbSEmilio G. Cota g_assert_false(qht_remove(&ht, &arr[i], hash)); 561a95404fSEmilio G. Cota } 571a95404fSEmilio G. Cota } 58f44641bbSEmilio G. Cota } 59f44641bbSEmilio G. Cota 60f44641bbSEmilio G. Cota static void rm(int init, int end) 61f44641bbSEmilio G. Cota { 62f44641bbSEmilio G. Cota do_rm(init, end, true); 63f44641bbSEmilio G. Cota } 64f44641bbSEmilio G. Cota 65f44641bbSEmilio G. Cota static void rm_nonexist(int init, int end) 66f44641bbSEmilio G. Cota { 67f44641bbSEmilio G. Cota do_rm(init, end, false); 68f44641bbSEmilio G. Cota } 691a95404fSEmilio G. Cota 701a95404fSEmilio G. Cota static void check(int a, int b, bool expected) 711a95404fSEmilio G. Cota { 721a95404fSEmilio G. Cota struct qht_stats stats; 731a95404fSEmilio G. Cota int i; 741a95404fSEmilio G. Cota 759c7d64ebSEmilio G. Cota rcu_read_lock(); 761a95404fSEmilio G. Cota for (i = a; i < b; i++) { 771a95404fSEmilio G. Cota void *p; 781a95404fSEmilio G. Cota uint32_t hash; 791a95404fSEmilio G. Cota int32_t val; 801a95404fSEmilio G. Cota 811a95404fSEmilio G. Cota val = i; 821a95404fSEmilio G. Cota hash = i; 8361b8cef1SEmilio G. Cota /* test both lookup variants; results should be the same */ 8461b8cef1SEmilio G. Cota if (i % 2) { 8561b8cef1SEmilio G. Cota p = qht_lookup(&ht, &val, hash); 8661b8cef1SEmilio G. Cota } else { 8761b8cef1SEmilio G. Cota p = qht_lookup_custom(&ht, &val, hash, is_equal); 8861b8cef1SEmilio G. Cota } 891a95404fSEmilio G. Cota g_assert_true(!!p == expected); 901a95404fSEmilio G. Cota } 919c7d64ebSEmilio G. Cota rcu_read_unlock(); 929c7d64ebSEmilio G. Cota 931a95404fSEmilio G. Cota qht_statistics_init(&ht, &stats); 941a95404fSEmilio G. Cota if (stats.used_head_buckets) { 951a95404fSEmilio G. Cota g_assert_cmpfloat(qdist_avg(&stats.chain), >=, 1.0); 961a95404fSEmilio G. Cota } 971a95404fSEmilio G. Cota g_assert_cmpuint(stats.head_buckets, >, 0); 981a95404fSEmilio G. Cota qht_statistics_destroy(&stats); 991a95404fSEmilio G. Cota } 1001a95404fSEmilio G. Cota 1011a95404fSEmilio G. Cota static void count_func(struct qht *ht, void *p, uint32_t hash, void *userp) 1021a95404fSEmilio G. Cota { 1031a95404fSEmilio G. Cota unsigned int *curr = userp; 1041a95404fSEmilio G. Cota 1051a95404fSEmilio G. Cota (*curr)++; 1061a95404fSEmilio G. Cota } 1071a95404fSEmilio G. Cota 1081a95404fSEmilio G. Cota static void check_n(size_t expected) 1091a95404fSEmilio G. Cota { 1101a95404fSEmilio G. Cota struct qht_stats stats; 1111a95404fSEmilio G. Cota 1121a95404fSEmilio G. Cota qht_statistics_init(&ht, &stats); 1131a95404fSEmilio G. Cota g_assert_cmpuint(stats.entries, ==, expected); 1141a95404fSEmilio G. Cota qht_statistics_destroy(&stats); 1151a95404fSEmilio G. Cota } 1161a95404fSEmilio G. Cota 1171a95404fSEmilio G. Cota static void iter_check(unsigned int count) 1181a95404fSEmilio G. Cota { 1191a95404fSEmilio G. Cota unsigned int curr = 0; 1201a95404fSEmilio G. Cota 1211a95404fSEmilio G. Cota qht_iter(&ht, count_func, &curr); 1221a95404fSEmilio G. Cota g_assert_cmpuint(curr, ==, count); 1231a95404fSEmilio G. Cota } 1241a95404fSEmilio G. Cota 125922034e7SEmilio G. Cota static void sum_func(struct qht *ht, void *p, uint32_t hash, void *userp) 126922034e7SEmilio G. Cota { 127922034e7SEmilio G. Cota uint32_t *sum = userp; 128922034e7SEmilio G. Cota uint32_t a = *(uint32_t *)p; 129922034e7SEmilio G. Cota 130922034e7SEmilio G. Cota *sum += a; 131922034e7SEmilio G. Cota } 132922034e7SEmilio G. Cota 133922034e7SEmilio G. Cota static void iter_sum_check(unsigned int expected) 134922034e7SEmilio G. Cota { 135922034e7SEmilio G. Cota unsigned int sum = 0; 136922034e7SEmilio G. Cota 137922034e7SEmilio G. Cota qht_iter(&ht, sum_func, &sum); 138922034e7SEmilio G. Cota g_assert_cmpuint(sum, ==, expected); 139922034e7SEmilio G. Cota } 140922034e7SEmilio G. Cota 141922034e7SEmilio G. Cota static bool rm_mod_func(struct qht *ht, void *p, uint32_t hash, void *userp) 142922034e7SEmilio G. Cota { 143922034e7SEmilio G. Cota uint32_t a = *(uint32_t *)p; 144922034e7SEmilio G. Cota unsigned int mod = *(unsigned int *)userp; 145922034e7SEmilio G. Cota 146922034e7SEmilio G. Cota return a % mod == 0; 147922034e7SEmilio G. Cota } 148922034e7SEmilio G. Cota 149922034e7SEmilio G. Cota static void iter_rm_mod(unsigned int mod) 150922034e7SEmilio G. Cota { 151922034e7SEmilio G. Cota qht_iter_remove(&ht, rm_mod_func, &mod); 152922034e7SEmilio G. Cota } 153922034e7SEmilio G. Cota 154922034e7SEmilio G. Cota static void iter_rm_mod_check(unsigned int mod) 155922034e7SEmilio G. Cota { 156922034e7SEmilio G. Cota unsigned int expected = 0; 157922034e7SEmilio G. Cota unsigned int i; 158922034e7SEmilio G. Cota 159922034e7SEmilio G. Cota for (i = 0; i < N; i++) { 160922034e7SEmilio G. Cota if (i % mod == 0) { 161922034e7SEmilio G. Cota continue; 162922034e7SEmilio G. Cota } 163922034e7SEmilio G. Cota expected += i; 164922034e7SEmilio G. Cota } 165922034e7SEmilio G. Cota iter_sum_check(expected); 166922034e7SEmilio G. Cota } 167922034e7SEmilio G. Cota 1681a95404fSEmilio G. Cota static void qht_do_test(unsigned int mode, size_t init_entries) 1691a95404fSEmilio G. Cota { 1707266ae91SEmilio G. Cota /* under KVM we might fetch stats from an uninitialized qht */ 1717266ae91SEmilio G. Cota check_n(0); 1727266ae91SEmilio G. Cota 17361b8cef1SEmilio G. Cota qht_init(&ht, is_equal, 0, mode); 174f44641bbSEmilio G. Cota rm_nonexist(0, 4); 175321a33f5SEmilio G. Cota /* 176321a33f5SEmilio G. Cota * Test that we successfully delete the last element in a bucket. 177321a33f5SEmilio G. Cota * This is a hard-to-reach code path when resizing is on, but without 178321a33f5SEmilio G. Cota * resizing we can easily hit it if init_entries <= 1. 179321a33f5SEmilio G. Cota * Given that the number of elements per bucket can be 4 or 6 depending on 180321a33f5SEmilio G. Cota * the host's pointer size, test the removal of the 4th and 6th elements. 181321a33f5SEmilio G. Cota */ 182f44641bbSEmilio G. Cota insert(0, 4); 183f44641bbSEmilio G. Cota rm_nonexist(5, 6); 184321a33f5SEmilio G. Cota rm(3, 4); 185321a33f5SEmilio G. Cota check_n(3); 186321a33f5SEmilio G. Cota insert(3, 6); 187321a33f5SEmilio G. Cota rm(5, 6); 188321a33f5SEmilio G. Cota check_n(5); 189f44641bbSEmilio G. Cota rm_nonexist(7, 8); 190f44641bbSEmilio G. Cota iter_rm_mod(1); 1911a95404fSEmilio G. Cota 192*ca8897a4SEmilio G. Cota if (!(mode & QHT_MODE_AUTO_RESIZE)) { 193*ca8897a4SEmilio G. Cota qht_resize(&ht, init_entries * 4 + 4); 194*ca8897a4SEmilio G. Cota } 195*ca8897a4SEmilio G. Cota 1967266ae91SEmilio G. Cota check_n(0); 197f44641bbSEmilio G. Cota rm_nonexist(0, 10); 1981a95404fSEmilio G. Cota insert(0, N); 1991a95404fSEmilio G. Cota check(0, N, true); 2001a95404fSEmilio G. Cota check_n(N); 2011a95404fSEmilio G. Cota check(-N, -1, false); 2021a95404fSEmilio G. Cota iter_check(N); 2031a95404fSEmilio G. Cota 2041a95404fSEmilio G. Cota rm(101, 102); 2051a95404fSEmilio G. Cota check_n(N - 1); 2061a95404fSEmilio G. Cota insert(N, N * 2); 2071a95404fSEmilio G. Cota check_n(N + N - 1); 2081a95404fSEmilio G. Cota rm(N, N * 2); 2091a95404fSEmilio G. Cota check_n(N - 1); 2101a95404fSEmilio G. Cota insert(101, 102); 2111a95404fSEmilio G. Cota check_n(N); 2121a95404fSEmilio G. Cota 2131a95404fSEmilio G. Cota rm(10, 200); 2141a95404fSEmilio G. Cota check_n(N - 190); 2151a95404fSEmilio G. Cota insert(150, 200); 2161a95404fSEmilio G. Cota check_n(N - 190 + 50); 2171a95404fSEmilio G. Cota insert(10, 150); 2181a95404fSEmilio G. Cota check_n(N); 2191a95404fSEmilio G. Cota 220922034e7SEmilio G. Cota qht_reset(&ht); 221922034e7SEmilio G. Cota insert(0, N); 222f44641bbSEmilio G. Cota rm_nonexist(N, N + 32); 223922034e7SEmilio G. Cota iter_rm_mod(10); 224922034e7SEmilio G. Cota iter_rm_mod_check(10); 225922034e7SEmilio G. Cota check_n(N * 9 / 10); 2261a95404fSEmilio G. Cota qht_reset_size(&ht, 0); 2271a95404fSEmilio G. Cota check_n(0); 2281a95404fSEmilio G. Cota check(0, N, false); 2291a95404fSEmilio G. Cota 2301a95404fSEmilio G. Cota qht_destroy(&ht); 2311a95404fSEmilio G. Cota } 2321a95404fSEmilio G. Cota 2331a95404fSEmilio G. Cota static void qht_test(unsigned int mode) 2341a95404fSEmilio G. Cota { 2351a95404fSEmilio G. Cota qht_do_test(mode, 0); 2361a95404fSEmilio G. Cota qht_do_test(mode, 1); 2371a95404fSEmilio G. Cota qht_do_test(mode, 2); 2381a95404fSEmilio G. Cota qht_do_test(mode, 8); 2391a95404fSEmilio G. Cota qht_do_test(mode, 16); 2401a95404fSEmilio G. Cota qht_do_test(mode, 8192); 2411a95404fSEmilio G. Cota qht_do_test(mode, 16384); 2421a95404fSEmilio G. Cota } 2431a95404fSEmilio G. Cota 2441a95404fSEmilio G. Cota static void test_default(void) 2451a95404fSEmilio G. Cota { 2461a95404fSEmilio G. Cota qht_test(0); 2471a95404fSEmilio G. Cota } 2481a95404fSEmilio G. Cota 2491a95404fSEmilio G. Cota static void test_resize(void) 2501a95404fSEmilio G. Cota { 2511a95404fSEmilio G. Cota qht_test(QHT_MODE_AUTO_RESIZE); 2521a95404fSEmilio G. Cota } 2531a95404fSEmilio G. Cota 2541a95404fSEmilio G. Cota int main(int argc, char *argv[]) 2551a95404fSEmilio G. Cota { 2561a95404fSEmilio G. Cota g_test_init(&argc, &argv, NULL); 2571a95404fSEmilio G. Cota g_test_add_func("/qht/mode/default", test_default); 2581a95404fSEmilio G. Cota g_test_add_func("/qht/mode/resize", test_resize); 2591a95404fSEmilio G. Cota return g_test_run(); 2601a95404fSEmilio G. Cota } 261