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; 30*32359d52SEmilio G. Cota void *existing; 31*32359d52SEmilio G. Cota bool inserted; 321a95404fSEmilio G. Cota 331a95404fSEmilio G. Cota arr[i] = i; 341a95404fSEmilio G. Cota hash = i; 351a95404fSEmilio G. Cota 36*32359d52SEmilio G. Cota inserted = qht_insert(&ht, &arr[i], hash, NULL); 37*32359d52SEmilio G. Cota g_assert_true(inserted); 38*32359d52SEmilio G. Cota inserted = qht_insert(&ht, &arr[i], hash, &existing); 39*32359d52SEmilio G. Cota g_assert_false(inserted); 40*32359d52SEmilio G. Cota g_assert_true(existing == &arr[i]); 411a95404fSEmilio G. Cota } 421a95404fSEmilio G. Cota } 431a95404fSEmilio G. Cota 441a95404fSEmilio G. Cota static void rm(int init, int end) 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]; 521a95404fSEmilio G. Cota g_assert_true(qht_remove(&ht, &arr[i], hash)); 531a95404fSEmilio G. Cota } 541a95404fSEmilio G. Cota } 551a95404fSEmilio G. Cota 561a95404fSEmilio G. Cota static void check(int a, int b, bool expected) 571a95404fSEmilio G. Cota { 581a95404fSEmilio G. Cota struct qht_stats stats; 591a95404fSEmilio G. Cota int i; 601a95404fSEmilio G. Cota 619c7d64ebSEmilio G. Cota rcu_read_lock(); 621a95404fSEmilio G. Cota for (i = a; i < b; i++) { 631a95404fSEmilio G. Cota void *p; 641a95404fSEmilio G. Cota uint32_t hash; 651a95404fSEmilio G. Cota int32_t val; 661a95404fSEmilio G. Cota 671a95404fSEmilio G. Cota val = i; 681a95404fSEmilio G. Cota hash = i; 6961b8cef1SEmilio G. Cota /* test both lookup variants; results should be the same */ 7061b8cef1SEmilio G. Cota if (i % 2) { 7161b8cef1SEmilio G. Cota p = qht_lookup(&ht, &val, hash); 7261b8cef1SEmilio G. Cota } else { 7361b8cef1SEmilio G. Cota p = qht_lookup_custom(&ht, &val, hash, is_equal); 7461b8cef1SEmilio G. Cota } 751a95404fSEmilio G. Cota g_assert_true(!!p == expected); 761a95404fSEmilio G. Cota } 779c7d64ebSEmilio G. Cota rcu_read_unlock(); 789c7d64ebSEmilio G. Cota 791a95404fSEmilio G. Cota qht_statistics_init(&ht, &stats); 801a95404fSEmilio G. Cota if (stats.used_head_buckets) { 811a95404fSEmilio G. Cota g_assert_cmpfloat(qdist_avg(&stats.chain), >=, 1.0); 821a95404fSEmilio G. Cota } 831a95404fSEmilio G. Cota g_assert_cmpuint(stats.head_buckets, >, 0); 841a95404fSEmilio G. Cota qht_statistics_destroy(&stats); 851a95404fSEmilio G. Cota } 861a95404fSEmilio G. Cota 871a95404fSEmilio G. Cota static void count_func(struct qht *ht, void *p, uint32_t hash, void *userp) 881a95404fSEmilio G. Cota { 891a95404fSEmilio G. Cota unsigned int *curr = userp; 901a95404fSEmilio G. Cota 911a95404fSEmilio G. Cota (*curr)++; 921a95404fSEmilio G. Cota } 931a95404fSEmilio G. Cota 941a95404fSEmilio G. Cota static void check_n(size_t expected) 951a95404fSEmilio G. Cota { 961a95404fSEmilio G. Cota struct qht_stats stats; 971a95404fSEmilio G. Cota 981a95404fSEmilio G. Cota qht_statistics_init(&ht, &stats); 991a95404fSEmilio G. Cota g_assert_cmpuint(stats.entries, ==, expected); 1001a95404fSEmilio G. Cota qht_statistics_destroy(&stats); 1011a95404fSEmilio G. Cota } 1021a95404fSEmilio G. Cota 1031a95404fSEmilio G. Cota static void iter_check(unsigned int count) 1041a95404fSEmilio G. Cota { 1051a95404fSEmilio G. Cota unsigned int curr = 0; 1061a95404fSEmilio G. Cota 1071a95404fSEmilio G. Cota qht_iter(&ht, count_func, &curr); 1081a95404fSEmilio G. Cota g_assert_cmpuint(curr, ==, count); 1091a95404fSEmilio G. Cota } 1101a95404fSEmilio G. Cota 1111a95404fSEmilio G. Cota static void qht_do_test(unsigned int mode, size_t init_entries) 1121a95404fSEmilio G. Cota { 1137266ae91SEmilio G. Cota /* under KVM we might fetch stats from an uninitialized qht */ 1147266ae91SEmilio G. Cota check_n(0); 1157266ae91SEmilio G. Cota 11661b8cef1SEmilio G. Cota qht_init(&ht, is_equal, 0, mode); 1171a95404fSEmilio G. Cota 1187266ae91SEmilio G. Cota check_n(0); 1191a95404fSEmilio G. Cota insert(0, N); 1201a95404fSEmilio G. Cota check(0, N, true); 1211a95404fSEmilio G. Cota check_n(N); 1221a95404fSEmilio G. Cota check(-N, -1, false); 1231a95404fSEmilio G. Cota iter_check(N); 1241a95404fSEmilio G. Cota 1251a95404fSEmilio G. Cota rm(101, 102); 1261a95404fSEmilio G. Cota check_n(N - 1); 1271a95404fSEmilio G. Cota insert(N, N * 2); 1281a95404fSEmilio G. Cota check_n(N + N - 1); 1291a95404fSEmilio G. Cota rm(N, N * 2); 1301a95404fSEmilio G. Cota check_n(N - 1); 1311a95404fSEmilio G. Cota insert(101, 102); 1321a95404fSEmilio G. Cota check_n(N); 1331a95404fSEmilio G. Cota 1341a95404fSEmilio G. Cota rm(10, 200); 1351a95404fSEmilio G. Cota check_n(N - 190); 1361a95404fSEmilio G. Cota insert(150, 200); 1371a95404fSEmilio G. Cota check_n(N - 190 + 50); 1381a95404fSEmilio G. Cota insert(10, 150); 1391a95404fSEmilio G. Cota check_n(N); 1401a95404fSEmilio G. Cota 1411a95404fSEmilio G. Cota rm(1, 2); 1421a95404fSEmilio G. Cota check_n(N - 1); 1431a95404fSEmilio G. Cota qht_reset_size(&ht, 0); 1441a95404fSEmilio G. Cota check_n(0); 1451a95404fSEmilio G. Cota check(0, N, false); 1461a95404fSEmilio G. Cota 1471a95404fSEmilio G. Cota qht_destroy(&ht); 1481a95404fSEmilio G. Cota } 1491a95404fSEmilio G. Cota 1501a95404fSEmilio G. Cota static void qht_test(unsigned int mode) 1511a95404fSEmilio G. Cota { 1521a95404fSEmilio G. Cota qht_do_test(mode, 0); 1531a95404fSEmilio G. Cota qht_do_test(mode, 1); 1541a95404fSEmilio G. Cota qht_do_test(mode, 2); 1551a95404fSEmilio G. Cota qht_do_test(mode, 8); 1561a95404fSEmilio G. Cota qht_do_test(mode, 16); 1571a95404fSEmilio G. Cota qht_do_test(mode, 8192); 1581a95404fSEmilio G. Cota qht_do_test(mode, 16384); 1591a95404fSEmilio G. Cota } 1601a95404fSEmilio G. Cota 1611a95404fSEmilio G. Cota static void test_default(void) 1621a95404fSEmilio G. Cota { 1631a95404fSEmilio G. Cota qht_test(0); 1641a95404fSEmilio G. Cota } 1651a95404fSEmilio G. Cota 1661a95404fSEmilio G. Cota static void test_resize(void) 1671a95404fSEmilio G. Cota { 1681a95404fSEmilio G. Cota qht_test(QHT_MODE_AUTO_RESIZE); 1691a95404fSEmilio G. Cota } 1701a95404fSEmilio G. Cota 1711a95404fSEmilio G. Cota int main(int argc, char *argv[]) 1721a95404fSEmilio G. Cota { 1731a95404fSEmilio G. Cota g_test_init(&argc, &argv, NULL); 1741a95404fSEmilio G. Cota g_test_add_func("/qht/mode/default", test_default); 1751a95404fSEmilio G. Cota g_test_add_func("/qht/mode/resize", test_resize); 1761a95404fSEmilio G. Cota return g_test_run(); 1771a95404fSEmilio G. Cota } 178