1*bb611c8fSApple OSS Distributions #include <darwintest.h> 2*bb611c8fSApple OSS Distributions #include <darwintest_utils.h> 3*bb611c8fSApple OSS Distributions #include <stdio.h> 4*bb611c8fSApple OSS Distributions #include <assert.h> 5*bb611c8fSApple OSS Distributions #include <setjmp.h> 6*bb611c8fSApple OSS Distributions #include <algorithm> 7*bb611c8fSApple OSS Distributions 8*bb611c8fSApple OSS Distributions #define DEVELOPMENT 0 9*bb611c8fSApple OSS Distributions #define DEBUG 0 10*bb611c8fSApple OSS Distributions #define XNU_KERNEL_PRIVATE 1 11*bb611c8fSApple OSS Distributions 12*bb611c8fSApple OSS Distributions #define OS_REFCNT_DEBUG 1 13*bb611c8fSApple OSS Distributions #define STRESS_TESTS 0 14*bb611c8fSApple OSS Distributions 15*bb611c8fSApple OSS Distributions #define __container_of(ptr, type, field) __extension__({ \ 16*bb611c8fSApple OSS Distributions const __typeof__(((type *)nullptr)->field) *__ptr = (ptr); \ 17*bb611c8fSApple OSS Distributions (type *)((uintptr_t)__ptr - offsetof(type, field)); \ 18*bb611c8fSApple OSS Distributions }) 19*bb611c8fSApple OSS Distributions 20*bb611c8fSApple OSS Distributions #pragma clang diagnostic ignored "-Watomic-implicit-seq-cst" 21*bb611c8fSApple OSS Distributions #pragma clang diagnostic ignored "-Wc++98-compat" 22*bb611c8fSApple OSS Distributions 23*bb611c8fSApple OSS Distributions #include "../osfmk/kern/macro_help.h" 24*bb611c8fSApple OSS Distributions #include "../osfmk/kern/priority_queue.h" 25*bb611c8fSApple OSS Distributions #include "../libkern/c++/priority_queue.cpp" 26*bb611c8fSApple OSS Distributions 27*bb611c8fSApple OSS Distributions T_GLOBAL_META(T_META_RUN_CONCURRENTLY(true)); 28*bb611c8fSApple OSS Distributions 29*bb611c8fSApple OSS Distributions static int 30*bb611c8fSApple OSS Distributions compare_numbers_descending(const void * a, const void * b) 31*bb611c8fSApple OSS Distributions { 32*bb611c8fSApple OSS Distributions const uint16_t x = *(const uint16_t *)a; 33*bb611c8fSApple OSS Distributions const uint16_t y = *(const uint16_t *)b; 34*bb611c8fSApple OSS Distributions if (x > y) { 35*bb611c8fSApple OSS Distributions return -1; 36*bb611c8fSApple OSS Distributions } else if (x < y) { 37*bb611c8fSApple OSS Distributions return 1; 38*bb611c8fSApple OSS Distributions } else { 39*bb611c8fSApple OSS Distributions return 0; 40*bb611c8fSApple OSS Distributions } 41*bb611c8fSApple OSS Distributions } 42*bb611c8fSApple OSS Distributions 43*bb611c8fSApple OSS Distributions #define PRIORITY_QUEUE_NODES 8 44*bb611c8fSApple OSS Distributions 45*bb611c8fSApple OSS Distributions typedef union test_node { 46*bb611c8fSApple OSS Distributions struct { 47*bb611c8fSApple OSS Distributions struct priority_queue_entry e; 48*bb611c8fSApple OSS Distributions uint32_t node_key; 49*bb611c8fSApple OSS Distributions }; 50*bb611c8fSApple OSS Distributions struct priority_queue_entry_sched ke; 51*bb611c8fSApple OSS Distributions struct priority_queue_entry_stable se; 52*bb611c8fSApple OSS Distributions } *test_node_t; 53*bb611c8fSApple OSS Distributions 54*bb611c8fSApple OSS Distributions static void 55*bb611c8fSApple OSS Distributions dump_pqueue_entry(priority_queue_entry_sched_t e, int depth) 56*bb611c8fSApple OSS Distributions { 57*bb611c8fSApple OSS Distributions priority_queue_entry_sched_t t; 58*bb611c8fSApple OSS Distributions 59*bb611c8fSApple OSS Distributions printf("%*s [%02d] %p\n", depth * 4, "", e->key, (void *)e); 60*bb611c8fSApple OSS Distributions t = pqueue_sched_max_t::unpack_child(e); 61*bb611c8fSApple OSS Distributions if (t) { 62*bb611c8fSApple OSS Distributions dump_pqueue_entry(t, depth + 1); 63*bb611c8fSApple OSS Distributions } 64*bb611c8fSApple OSS Distributions while (e->next) { 65*bb611c8fSApple OSS Distributions e = e->next; 66*bb611c8fSApple OSS Distributions dump_pqueue_entry(e, depth); 67*bb611c8fSApple OSS Distributions } 68*bb611c8fSApple OSS Distributions } 69*bb611c8fSApple OSS Distributions 70*bb611c8fSApple OSS Distributions __unused 71*bb611c8fSApple OSS Distributions static void 72*bb611c8fSApple OSS Distributions dump_pqueue(struct priority_queue_sched_max *pq) 73*bb611c8fSApple OSS Distributions { 74*bb611c8fSApple OSS Distributions dump_pqueue_entry(pq->pq_root, 0); 75*bb611c8fSApple OSS Distributions printf("\n"); 76*bb611c8fSApple OSS Distributions } 77*bb611c8fSApple OSS Distributions 78*bb611c8fSApple OSS Distributions T_DECL(priority_queue_sched_max, "Basic sched priority queue testing") 79*bb611c8fSApple OSS Distributions { 80*bb611c8fSApple OSS Distributions /* Configuration for the test */ 81*bb611c8fSApple OSS Distributions static uint16_t priority_list[] = { 20, 3, 7, 6, 50, 2, 8, 12}; 82*bb611c8fSApple OSS Distributions 83*bb611c8fSApple OSS Distributions struct priority_queue_sched_max pq; 84*bb611c8fSApple OSS Distributions uint16_t increase_pri = 100; 85*bb611c8fSApple OSS Distributions uint16_t decrease_pri = 90; 86*bb611c8fSApple OSS Distributions uint16_t key = 0; 87*bb611c8fSApple OSS Distributions boolean_t update_result = false; 88*bb611c8fSApple OSS Distributions test_node_t node = NULL; 89*bb611c8fSApple OSS Distributions 90*bb611c8fSApple OSS Distributions priority_queue_init(&pq); 91*bb611c8fSApple OSS Distributions 92*bb611c8fSApple OSS Distributions /* Add all priorities to the first priority queue */ 93*bb611c8fSApple OSS Distributions for (int i = 0; i < PRIORITY_QUEUE_NODES; i++) { 94*bb611c8fSApple OSS Distributions node = new test_node; 95*bb611c8fSApple OSS Distributions T_QUIET; T_ASSERT_NOTNULL(node, NULL); 96*bb611c8fSApple OSS Distributions 97*bb611c8fSApple OSS Distributions priority_queue_entry_init(&node->ke); 98*bb611c8fSApple OSS Distributions priority_queue_entry_set_sched_pri(&pq, &node->ke, priority_list[i], 0); 99*bb611c8fSApple OSS Distributions priority_queue_insert(&pq, &node->ke); 100*bb611c8fSApple OSS Distributions } 101*bb611c8fSApple OSS Distributions 102*bb611c8fSApple OSS Distributions /* Test the priority increase operation by updating the last node added (7) */ 103*bb611c8fSApple OSS Distributions priority_queue_entry_set_sched_pri(&pq, &node->ke, increase_pri, 0); 104*bb611c8fSApple OSS Distributions update_result = priority_queue_entry_increased(&pq, &node->ke); 105*bb611c8fSApple OSS Distributions T_ASSERT_TRUE(update_result, "increase key updated root"); 106*bb611c8fSApple OSS Distributions key = priority_queue_max_sched_pri(&pq); 107*bb611c8fSApple OSS Distributions T_ASSERT_EQ(key, increase_pri, "verify priority_queue_entry_increased() operation"); 108*bb611c8fSApple OSS Distributions 109*bb611c8fSApple OSS Distributions /* Test the priority decrease operation by updating the last node added */ 110*bb611c8fSApple OSS Distributions priority_queue_entry_set_sched_pri(&pq, &node->ke, decrease_pri, 0); 111*bb611c8fSApple OSS Distributions update_result = priority_queue_entry_decreased(&pq, &node->ke); 112*bb611c8fSApple OSS Distributions T_ASSERT_TRUE(update_result, "decrease key updated root"); 113*bb611c8fSApple OSS Distributions key = priority_queue_max_sched_pri(&pq); 114*bb611c8fSApple OSS Distributions T_ASSERT_EQ(key, decrease_pri, "verify priority_queue_entry_decreased() operation"); 115*bb611c8fSApple OSS Distributions 116*bb611c8fSApple OSS Distributions /* Update our local priority list as well */ 117*bb611c8fSApple OSS Distributions priority_list[PRIORITY_QUEUE_NODES - 1] = decrease_pri; 118*bb611c8fSApple OSS Distributions 119*bb611c8fSApple OSS Distributions /* Sort the local list in descending order */ 120*bb611c8fSApple OSS Distributions qsort(priority_list, PRIORITY_QUEUE_NODES, sizeof(priority_list[0]), compare_numbers_descending); 121*bb611c8fSApple OSS Distributions 122*bb611c8fSApple OSS Distributions priority_queue_entry_sched_t k = NULL; 123*bb611c8fSApple OSS Distributions 124*bb611c8fSApple OSS Distributions node = pqe_element_fast(k, test_node, ke); 125*bb611c8fSApple OSS Distributions 126*bb611c8fSApple OSS Distributions /* Test the maximum operation by comparing max node with local list */ 127*bb611c8fSApple OSS Distributions for (int i = 0; i < PRIORITY_QUEUE_NODES; i++) { 128*bb611c8fSApple OSS Distributions key = priority_queue_max_sched_pri(&pq); 129*bb611c8fSApple OSS Distributions T_ASSERT_EQ(key, priority_list[i], "[%d] priority queue max node removal", i); 130*bb611c8fSApple OSS Distributions node = priority_queue_remove_max(&pq, test_node, ke); 131*bb611c8fSApple OSS Distributions delete node; 132*bb611c8fSApple OSS Distributions } 133*bb611c8fSApple OSS Distributions 134*bb611c8fSApple OSS Distributions T_ASSERT_TRUE(priority_queue_empty(&pq), "queue is empty"); 135*bb611c8fSApple OSS Distributions priority_queue_destroy(&pq, union test_node, ke, ^(test_node_t n) { 136*bb611c8fSApple OSS Distributions T_FAIL("Called with %p", n); 137*bb611c8fSApple OSS Distributions }); 138*bb611c8fSApple OSS Distributions } 139*bb611c8fSApple OSS Distributions 140*bb611c8fSApple OSS Distributions T_DECL(priority_queue_max, "Basic generic priority queue testing") 141*bb611c8fSApple OSS Distributions { 142*bb611c8fSApple OSS Distributions /* Configuration for the test */ 143*bb611c8fSApple OSS Distributions static uint16_t priority_list[] = { 20, 3, 7, 6, 50, 2, 8, 12}; 144*bb611c8fSApple OSS Distributions 145*bb611c8fSApple OSS Distributions struct priority_queue_max pq; 146*bb611c8fSApple OSS Distributions uint16_t increase_pri = 100; 147*bb611c8fSApple OSS Distributions uint16_t decrease_pri = 90; 148*bb611c8fSApple OSS Distributions test_node_t result; 149*bb611c8fSApple OSS Distributions boolean_t update_result = false; 150*bb611c8fSApple OSS Distributions test_node_t node = NULL; 151*bb611c8fSApple OSS Distributions 152*bb611c8fSApple OSS Distributions priority_queue_compare_fn_t cmp_fn = 153*bb611c8fSApple OSS Distributions priority_heap_make_comparator(a, b, union test_node, e, { 154*bb611c8fSApple OSS Distributions if (a->node_key != b->node_key) { 155*bb611c8fSApple OSS Distributions return priority_heap_compare_ints(a->node_key, b->node_key); 156*bb611c8fSApple OSS Distributions } 157*bb611c8fSApple OSS Distributions return 0; 158*bb611c8fSApple OSS Distributions }); 159*bb611c8fSApple OSS Distributions 160*bb611c8fSApple OSS Distributions priority_queue_init(&pq, cmp_fn); 161*bb611c8fSApple OSS Distributions 162*bb611c8fSApple OSS Distributions /* Add all priorities to the first priority queue */ 163*bb611c8fSApple OSS Distributions for (int i = 0; i < PRIORITY_QUEUE_NODES; i++) { 164*bb611c8fSApple OSS Distributions node = new test_node; 165*bb611c8fSApple OSS Distributions T_QUIET; T_ASSERT_NOTNULL(node, NULL); 166*bb611c8fSApple OSS Distributions 167*bb611c8fSApple OSS Distributions priority_queue_entry_init(&node->e); 168*bb611c8fSApple OSS Distributions node->node_key = priority_list[i]; 169*bb611c8fSApple OSS Distributions priority_queue_insert(&pq, &node->e); 170*bb611c8fSApple OSS Distributions } 171*bb611c8fSApple OSS Distributions 172*bb611c8fSApple OSS Distributions /* Test the priority increase operation by updating the last node added (8) */ 173*bb611c8fSApple OSS Distributions node->node_key = increase_pri; 174*bb611c8fSApple OSS Distributions update_result = priority_queue_entry_increased(&pq, &node->e); 175*bb611c8fSApple OSS Distributions T_ASSERT_TRUE(update_result, "increase key updated root"); 176*bb611c8fSApple OSS Distributions result = priority_queue_max(&pq, union test_node, e); 177*bb611c8fSApple OSS Distributions T_ASSERT_EQ(result->node_key, increase_pri, "verify priority_queue_entry_increased() operation"); 178*bb611c8fSApple OSS Distributions 179*bb611c8fSApple OSS Distributions 180*bb611c8fSApple OSS Distributions /* Test the priority decrease operation by updating the last node added */ 181*bb611c8fSApple OSS Distributions node->node_key = decrease_pri; 182*bb611c8fSApple OSS Distributions update_result = priority_queue_entry_decreased(&pq, &node->e); 183*bb611c8fSApple OSS Distributions T_ASSERT_TRUE(update_result, "decrease key updated root"); 184*bb611c8fSApple OSS Distributions result = priority_queue_max(&pq, union test_node, e); 185*bb611c8fSApple OSS Distributions T_ASSERT_EQ(result->node_key, decrease_pri, "verify priority_queue_entry_decreased() operation"); 186*bb611c8fSApple OSS Distributions 187*bb611c8fSApple OSS Distributions /* Update our local priority list as well */ 188*bb611c8fSApple OSS Distributions priority_list[PRIORITY_QUEUE_NODES - 1] = decrease_pri; 189*bb611c8fSApple OSS Distributions 190*bb611c8fSApple OSS Distributions /* Sort the local list in descending order */ 191*bb611c8fSApple OSS Distributions qsort(priority_list, PRIORITY_QUEUE_NODES, sizeof(priority_list[0]), compare_numbers_descending); 192*bb611c8fSApple OSS Distributions 193*bb611c8fSApple OSS Distributions /* Test the maximum operation by comparing max node with local list */ 194*bb611c8fSApple OSS Distributions for (int i = 0; i < PRIORITY_QUEUE_NODES; i++) { 195*bb611c8fSApple OSS Distributions result = priority_queue_remove_max(&pq, union test_node, e); 196*bb611c8fSApple OSS Distributions T_ASSERT_EQ(result->node_key, priority_list[i], 197*bb611c8fSApple OSS Distributions "[%d] priority queue max node removal", i); 198*bb611c8fSApple OSS Distributions delete result; 199*bb611c8fSApple OSS Distributions } 200*bb611c8fSApple OSS Distributions 201*bb611c8fSApple OSS Distributions T_ASSERT_TRUE(priority_queue_empty(&pq), "queue is empty"); 202*bb611c8fSApple OSS Distributions priority_queue_destroy(&pq, union test_node, e, ^(test_node_t n) { 203*bb611c8fSApple OSS Distributions T_FAIL("Called with %p", n); 204*bb611c8fSApple OSS Distributions }); 205*bb611c8fSApple OSS Distributions } 206*bb611c8fSApple OSS Distributions 207*bb611c8fSApple OSS Distributions T_DECL(priority_queue_sched_stable_max, "Basic stable sched priority queue testing") 208*bb611c8fSApple OSS Distributions { 209*bb611c8fSApple OSS Distributions /* Configuration for the test */ 210*bb611c8fSApple OSS Distributions static struct config { 211*bb611c8fSApple OSS Distributions uint16_t pri; 212*bb611c8fSApple OSS Distributions priority_queue_entry_sched_modifier_t modifier; 213*bb611c8fSApple OSS Distributions uint64_t stamp; 214*bb611c8fSApple OSS Distributions } config[] = { 215*bb611c8fSApple OSS Distributions { 20, PRIORITY_QUEUE_ENTRY_NONE, 8 }, 216*bb611c8fSApple OSS Distributions { 3, PRIORITY_QUEUE_ENTRY_NONE, 7 }, 217*bb611c8fSApple OSS Distributions { 3, PRIORITY_QUEUE_ENTRY_PREEMPTED, 6 }, 218*bb611c8fSApple OSS Distributions { 6, PRIORITY_QUEUE_ENTRY_NONE, 5 }, 219*bb611c8fSApple OSS Distributions { 50, PRIORITY_QUEUE_ENTRY_PREEMPTED, 4 }, 220*bb611c8fSApple OSS Distributions { 50, PRIORITY_QUEUE_ENTRY_PREEMPTED, 3 }, 221*bb611c8fSApple OSS Distributions { 50, PRIORITY_QUEUE_ENTRY_NONE, 2 }, 222*bb611c8fSApple OSS Distributions { 50, PRIORITY_QUEUE_ENTRY_NONE, 1 }, 223*bb611c8fSApple OSS Distributions }; 224*bb611c8fSApple OSS Distributions 225*bb611c8fSApple OSS Distributions struct priority_queue_sched_stable_max pq; 226*bb611c8fSApple OSS Distributions test_node_t node = NULL; 227*bb611c8fSApple OSS Distributions 228*bb611c8fSApple OSS Distributions priority_queue_init(&pq); 229*bb611c8fSApple OSS Distributions 230*bb611c8fSApple OSS Distributions /* Add all priorities to the first priority queue */ 231*bb611c8fSApple OSS Distributions for (int i = 0; i < PRIORITY_QUEUE_NODES; i++) { 232*bb611c8fSApple OSS Distributions node = new test_node; 233*bb611c8fSApple OSS Distributions T_QUIET; T_ASSERT_NOTNULL(node, NULL); 234*bb611c8fSApple OSS Distributions 235*bb611c8fSApple OSS Distributions priority_queue_entry_init(node); 236*bb611c8fSApple OSS Distributions node->se.stamp = config[i].stamp; 237*bb611c8fSApple OSS Distributions priority_queue_entry_set_sched_pri(&pq, &node->se, 238*bb611c8fSApple OSS Distributions config[i].pri, config[i].modifier); 239*bb611c8fSApple OSS Distributions priority_queue_insert(&pq, &node->se); 240*bb611c8fSApple OSS Distributions } 241*bb611c8fSApple OSS Distributions 242*bb611c8fSApple OSS Distributions /* Sort the local list in descending order */ 243*bb611c8fSApple OSS Distributions qsort_b(config, PRIORITY_QUEUE_NODES, sizeof(struct config), ^(const void *a, const void *b){ 244*bb611c8fSApple OSS Distributions const struct config &c1 = *(const struct config *)a; 245*bb611c8fSApple OSS Distributions const struct config &c2 = *(const struct config *)b; 246*bb611c8fSApple OSS Distributions if (c1.pri != c2.pri) { 247*bb611c8fSApple OSS Distributions return c1.pri < c2.pri ? 1 : -1; 248*bb611c8fSApple OSS Distributions } 249*bb611c8fSApple OSS Distributions if (c1.modifier != c2.modifier) { 250*bb611c8fSApple OSS Distributions return c1.modifier < c2.modifier ? 1 : -1; 251*bb611c8fSApple OSS Distributions } 252*bb611c8fSApple OSS Distributions if (c1.stamp != c2.stamp) { 253*bb611c8fSApple OSS Distributions if (c1.modifier) { 254*bb611c8fSApple OSS Distributions /* younger is better */ 255*bb611c8fSApple OSS Distributions return c1.stamp < c1.stamp ? 1 : -1; 256*bb611c8fSApple OSS Distributions } else { 257*bb611c8fSApple OSS Distributions /* older is better */ 258*bb611c8fSApple OSS Distributions return c1.stamp > c2.stamp ? 1 : -1; 259*bb611c8fSApple OSS Distributions } 260*bb611c8fSApple OSS Distributions } 261*bb611c8fSApple OSS Distributions return 0; 262*bb611c8fSApple OSS Distributions }); 263*bb611c8fSApple OSS Distributions 264*bb611c8fSApple OSS Distributions /* Test the maximum operation by comparing max node with local list */ 265*bb611c8fSApple OSS Distributions for (int i = 0; i < PRIORITY_QUEUE_NODES; i++) { 266*bb611c8fSApple OSS Distributions node = priority_queue_max(&pq, union test_node, se); 267*bb611c8fSApple OSS Distributions T_LOG("[%d]: { pri: %2d, modifier: %d, stamp: %lld }\n", 268*bb611c8fSApple OSS Distributions i, config[i].pri, config[i].modifier, config[i].stamp); 269*bb611c8fSApple OSS Distributions auto pri = priority_queue_entry_sched_pri(&pq, &node->se); 270*bb611c8fSApple OSS Distributions T_ASSERT_EQ(pri, config[i].pri, 271*bb611c8fSApple OSS Distributions "[%d] priority queue max node removal", i); 272*bb611c8fSApple OSS Distributions auto modifier = priority_queue_entry_sched_modifier(&pq, &node->se); 273*bb611c8fSApple OSS Distributions T_ASSERT_EQ(modifier, config[i].modifier, 274*bb611c8fSApple OSS Distributions "[%d] priority queue max node removal", i); 275*bb611c8fSApple OSS Distributions T_ASSERT_EQ(node->se.stamp, config[i].stamp, 276*bb611c8fSApple OSS Distributions "[%d] priority queue max node removal", i); 277*bb611c8fSApple OSS Distributions priority_queue_remove_max(&pq, union test_node, se); 278*bb611c8fSApple OSS Distributions delete node; 279*bb611c8fSApple OSS Distributions } 280*bb611c8fSApple OSS Distributions 281*bb611c8fSApple OSS Distributions T_ASSERT_TRUE(priority_queue_empty(&pq), "queue is empty"); 282*bb611c8fSApple OSS Distributions priority_queue_destroy(&pq, union test_node, se, ^(test_node_t n) { 283*bb611c8fSApple OSS Distributions T_FAIL("Called with %p", n); 284*bb611c8fSApple OSS Distributions }); 285*bb611c8fSApple OSS Distributions } 286