psset.c 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749
  1. #include "test/jemalloc_test.h"
  2. #include "jemalloc/internal/psset.h"
  3. #define PAGESLAB_ADDR ((void *)(1234 * HUGEPAGE))
  4. #define PAGESLAB_AGE 5678
  5. #define ALLOC_ARENA_IND 111
  6. #define ALLOC_ESN 222
  7. static void
  8. edata_init_test(edata_t *edata) {
  9. memset(edata, 0, sizeof(*edata));
  10. edata_arena_ind_set(edata, ALLOC_ARENA_IND);
  11. edata_esn_set(edata, ALLOC_ESN);
  12. }
  13. static void
  14. test_psset_fake_purge(hpdata_t *ps) {
  15. hpdata_purge_state_t purge_state;
  16. hpdata_alloc_allowed_set(ps, false);
  17. hpdata_purge_begin(ps, &purge_state);
  18. void *addr;
  19. size_t size;
  20. while (hpdata_purge_next(ps, &purge_state, &addr, &size)) {
  21. }
  22. hpdata_purge_end(ps, &purge_state);
  23. hpdata_alloc_allowed_set(ps, true);
  24. }
  25. static void
  26. test_psset_alloc_new(psset_t *psset, hpdata_t *ps, edata_t *r_edata,
  27. size_t size) {
  28. hpdata_assert_empty(ps);
  29. test_psset_fake_purge(ps);
  30. psset_insert(psset, ps);
  31. psset_update_begin(psset, ps);
  32. void *addr = hpdata_reserve_alloc(ps, size);
  33. edata_init(r_edata, edata_arena_ind_get(r_edata), addr, size,
  34. /* slab */ false, SC_NSIZES, /* sn */ 0, extent_state_active,
  35. /* zeroed */ false, /* committed */ true, EXTENT_PAI_HPA,
  36. EXTENT_NOT_HEAD);
  37. edata_ps_set(r_edata, ps);
  38. psset_update_end(psset, ps);
  39. }
  40. static bool
  41. test_psset_alloc_reuse(psset_t *psset, edata_t *r_edata, size_t size) {
  42. hpdata_t *ps = psset_pick_alloc(psset, size);
  43. if (ps == NULL) {
  44. return true;
  45. }
  46. psset_update_begin(psset, ps);
  47. void *addr = hpdata_reserve_alloc(ps, size);
  48. edata_init(r_edata, edata_arena_ind_get(r_edata), addr, size,
  49. /* slab */ false, SC_NSIZES, /* sn */ 0, extent_state_active,
  50. /* zeroed */ false, /* committed */ true, EXTENT_PAI_HPA,
  51. EXTENT_NOT_HEAD);
  52. edata_ps_set(r_edata, ps);
  53. psset_update_end(psset, ps);
  54. return false;
  55. }
  56. static hpdata_t *
  57. test_psset_dalloc(psset_t *psset, edata_t *edata) {
  58. hpdata_t *ps = edata_ps_get(edata);
  59. psset_update_begin(psset, ps);
  60. hpdata_unreserve(ps, edata_addr_get(edata), edata_size_get(edata));
  61. psset_update_end(psset, ps);
  62. if (hpdata_empty(ps)) {
  63. psset_remove(psset, ps);
  64. return ps;
  65. } else {
  66. return NULL;
  67. }
  68. }
  69. static void
  70. edata_expect(edata_t *edata, size_t page_offset, size_t page_cnt) {
  71. /*
  72. * Note that allocations should get the arena ind of their home
  73. * arena, *not* the arena ind of the pageslab allocator.
  74. */
  75. expect_u_eq(ALLOC_ARENA_IND, edata_arena_ind_get(edata),
  76. "Arena ind changed");
  77. expect_ptr_eq(
  78. (void *)((uintptr_t)PAGESLAB_ADDR + (page_offset << LG_PAGE)),
  79. edata_addr_get(edata), "Didn't allocate in order");
  80. expect_zu_eq(page_cnt << LG_PAGE, edata_size_get(edata), "");
  81. expect_false(edata_slab_get(edata), "");
  82. expect_u_eq(SC_NSIZES, edata_szind_get_maybe_invalid(edata),
  83. "");
  84. expect_u64_eq(0, edata_sn_get(edata), "");
  85. expect_d_eq(edata_state_get(edata), extent_state_active, "");
  86. expect_false(edata_zeroed_get(edata), "");
  87. expect_true(edata_committed_get(edata), "");
  88. expect_d_eq(EXTENT_PAI_HPA, edata_pai_get(edata), "");
  89. expect_false(edata_is_head_get(edata), "");
  90. }
  91. TEST_BEGIN(test_empty) {
  92. bool err;
  93. hpdata_t pageslab;
  94. hpdata_init(&pageslab, PAGESLAB_ADDR, PAGESLAB_AGE);
  95. edata_t alloc;
  96. edata_init_test(&alloc);
  97. psset_t psset;
  98. psset_init(&psset);
  99. /* Empty psset should return fail allocations. */
  100. err = test_psset_alloc_reuse(&psset, &alloc, PAGE);
  101. expect_true(err, "Empty psset succeeded in an allocation.");
  102. }
  103. TEST_END
  104. TEST_BEGIN(test_fill) {
  105. bool err;
  106. hpdata_t pageslab;
  107. hpdata_init(&pageslab, PAGESLAB_ADDR, PAGESLAB_AGE);
  108. edata_t alloc[HUGEPAGE_PAGES];
  109. psset_t psset;
  110. psset_init(&psset);
  111. edata_init_test(&alloc[0]);
  112. test_psset_alloc_new(&psset, &pageslab, &alloc[0], PAGE);
  113. for (size_t i = 1; i < HUGEPAGE_PAGES; i++) {
  114. edata_init_test(&alloc[i]);
  115. err = test_psset_alloc_reuse(&psset, &alloc[i], PAGE);
  116. expect_false(err, "Nonempty psset failed page allocation.");
  117. }
  118. for (size_t i = 0; i < HUGEPAGE_PAGES; i++) {
  119. edata_t *edata = &alloc[i];
  120. edata_expect(edata, i, 1);
  121. }
  122. /* The pageslab, and thus psset, should now have no allocations. */
  123. edata_t extra_alloc;
  124. edata_init_test(&extra_alloc);
  125. err = test_psset_alloc_reuse(&psset, &extra_alloc, PAGE);
  126. expect_true(err, "Alloc succeeded even though psset should be empty");
  127. }
  128. TEST_END
  129. TEST_BEGIN(test_reuse) {
  130. bool err;
  131. hpdata_t *ps;
  132. hpdata_t pageslab;
  133. hpdata_init(&pageslab, PAGESLAB_ADDR, PAGESLAB_AGE);
  134. edata_t alloc[HUGEPAGE_PAGES];
  135. psset_t psset;
  136. psset_init(&psset);
  137. edata_init_test(&alloc[0]);
  138. test_psset_alloc_new(&psset, &pageslab, &alloc[0], PAGE);
  139. for (size_t i = 1; i < HUGEPAGE_PAGES; i++) {
  140. edata_init_test(&alloc[i]);
  141. err = test_psset_alloc_reuse(&psset, &alloc[i], PAGE);
  142. expect_false(err, "Nonempty psset failed page allocation.");
  143. }
  144. /* Free odd indices. */
  145. for (size_t i = 0; i < HUGEPAGE_PAGES; i ++) {
  146. if (i % 2 == 0) {
  147. continue;
  148. }
  149. ps = test_psset_dalloc(&psset, &alloc[i]);
  150. expect_ptr_null(ps, "Nonempty pageslab evicted");
  151. }
  152. /* Realloc into them. */
  153. for (size_t i = 0; i < HUGEPAGE_PAGES; i++) {
  154. if (i % 2 == 0) {
  155. continue;
  156. }
  157. err = test_psset_alloc_reuse(&psset, &alloc[i], PAGE);
  158. expect_false(err, "Nonempty psset failed page allocation.");
  159. edata_expect(&alloc[i], i, 1);
  160. }
  161. /* Now, free the pages at indices 0 or 1 mod 2. */
  162. for (size_t i = 0; i < HUGEPAGE_PAGES; i++) {
  163. if (i % 4 > 1) {
  164. continue;
  165. }
  166. ps = test_psset_dalloc(&psset, &alloc[i]);
  167. expect_ptr_null(ps, "Nonempty pageslab evicted");
  168. }
  169. /* And realloc 2-page allocations into them. */
  170. for (size_t i = 0; i < HUGEPAGE_PAGES; i++) {
  171. if (i % 4 != 0) {
  172. continue;
  173. }
  174. err = test_psset_alloc_reuse(&psset, &alloc[i], 2 * PAGE);
  175. expect_false(err, "Nonempty psset failed page allocation.");
  176. edata_expect(&alloc[i], i, 2);
  177. }
  178. /* Free all the 2-page allocations. */
  179. for (size_t i = 0; i < HUGEPAGE_PAGES; i++) {
  180. if (i % 4 != 0) {
  181. continue;
  182. }
  183. ps = test_psset_dalloc(&psset, &alloc[i]);
  184. expect_ptr_null(ps, "Nonempty pageslab evicted");
  185. }
  186. /*
  187. * Free up a 1-page hole next to a 2-page hole, but somewhere in the
  188. * middle of the pageslab. Index 11 should be right before such a hole
  189. * (since 12 % 4 == 0).
  190. */
  191. size_t index_of_3 = 11;
  192. ps = test_psset_dalloc(&psset, &alloc[index_of_3]);
  193. expect_ptr_null(ps, "Nonempty pageslab evicted");
  194. err = test_psset_alloc_reuse(&psset, &alloc[index_of_3], 3 * PAGE);
  195. expect_false(err, "Should have been able to find alloc.");
  196. edata_expect(&alloc[index_of_3], index_of_3, 3);
  197. /*
  198. * Free up a 4-page hole at the end. Recall that the pages at offsets 0
  199. * and 1 mod 4 were freed above, so we just have to free the last
  200. * allocations.
  201. */
  202. ps = test_psset_dalloc(&psset, &alloc[HUGEPAGE_PAGES - 1]);
  203. expect_ptr_null(ps, "Nonempty pageslab evicted");
  204. ps = test_psset_dalloc(&psset, &alloc[HUGEPAGE_PAGES - 2]);
  205. expect_ptr_null(ps, "Nonempty pageslab evicted");
  206. /* Make sure we can satisfy an allocation at the very end of a slab. */
  207. size_t index_of_4 = HUGEPAGE_PAGES - 4;
  208. err = test_psset_alloc_reuse(&psset, &alloc[index_of_4], 4 * PAGE);
  209. expect_false(err, "Should have been able to find alloc.");
  210. edata_expect(&alloc[index_of_4], index_of_4, 4);
  211. }
  212. TEST_END
  213. TEST_BEGIN(test_evict) {
  214. bool err;
  215. hpdata_t *ps;
  216. hpdata_t pageslab;
  217. hpdata_init(&pageslab, PAGESLAB_ADDR, PAGESLAB_AGE);
  218. edata_t alloc[HUGEPAGE_PAGES];
  219. psset_t psset;
  220. psset_init(&psset);
  221. /* Alloc the whole slab. */
  222. edata_init_test(&alloc[0]);
  223. test_psset_alloc_new(&psset, &pageslab, &alloc[0], PAGE);
  224. for (size_t i = 1; i < HUGEPAGE_PAGES; i++) {
  225. edata_init_test(&alloc[i]);
  226. err = test_psset_alloc_reuse(&psset, &alloc[i], PAGE);
  227. expect_false(err, "Unxpected allocation failure");
  228. }
  229. /* Dealloc the whole slab, going forwards. */
  230. for (size_t i = 0; i < HUGEPAGE_PAGES - 1; i++) {
  231. ps = test_psset_dalloc(&psset, &alloc[i]);
  232. expect_ptr_null(ps, "Nonempty pageslab evicted");
  233. }
  234. ps = test_psset_dalloc(&psset, &alloc[HUGEPAGE_PAGES - 1]);
  235. expect_ptr_eq(&pageslab, ps, "Empty pageslab not evicted.");
  236. err = test_psset_alloc_reuse(&psset, &alloc[0], PAGE);
  237. expect_true(err, "psset should be empty.");
  238. }
  239. TEST_END
  240. TEST_BEGIN(test_multi_pageslab) {
  241. bool err;
  242. hpdata_t *ps;
  243. hpdata_t pageslab[2];
  244. hpdata_init(&pageslab[0], PAGESLAB_ADDR, PAGESLAB_AGE);
  245. hpdata_init(&pageslab[1],
  246. (void *)((uintptr_t)PAGESLAB_ADDR + HUGEPAGE),
  247. PAGESLAB_AGE + 1);
  248. edata_t alloc[2][HUGEPAGE_PAGES];
  249. psset_t psset;
  250. psset_init(&psset);
  251. /* Insert both slabs. */
  252. edata_init_test(&alloc[0][0]);
  253. test_psset_alloc_new(&psset, &pageslab[0], &alloc[0][0], PAGE);
  254. edata_init_test(&alloc[1][0]);
  255. test_psset_alloc_new(&psset, &pageslab[1], &alloc[1][0], PAGE);
  256. /* Fill them both up; make sure we do so in first-fit order. */
  257. for (size_t i = 0; i < 2; i++) {
  258. for (size_t j = 1; j < HUGEPAGE_PAGES; j++) {
  259. edata_init_test(&alloc[i][j]);
  260. err = test_psset_alloc_reuse(&psset, &alloc[i][j], PAGE);
  261. expect_false(err,
  262. "Nonempty psset failed page allocation.");
  263. assert_ptr_eq(&pageslab[i], edata_ps_get(&alloc[i][j]),
  264. "Didn't pick pageslabs in first-fit");
  265. }
  266. }
  267. /*
  268. * Free up a 2-page hole in the earlier slab, and a 1-page one in the
  269. * later one. We should still pick the later one.
  270. */
  271. ps = test_psset_dalloc(&psset, &alloc[0][0]);
  272. expect_ptr_null(ps, "Unexpected eviction");
  273. ps = test_psset_dalloc(&psset, &alloc[0][1]);
  274. expect_ptr_null(ps, "Unexpected eviction");
  275. ps = test_psset_dalloc(&psset, &alloc[1][0]);
  276. expect_ptr_null(ps, "Unexpected eviction");
  277. err = test_psset_alloc_reuse(&psset, &alloc[0][0], PAGE);
  278. expect_ptr_eq(&pageslab[1], edata_ps_get(&alloc[0][0]),
  279. "Should have picked the fuller pageslab");
  280. /*
  281. * Now both slabs have 1-page holes. Free up a second one in the later
  282. * slab.
  283. */
  284. ps = test_psset_dalloc(&psset, &alloc[1][1]);
  285. expect_ptr_null(ps, "Unexpected eviction");
  286. /*
  287. * We should be able to allocate a 2-page object, even though an earlier
  288. * size class is nonempty.
  289. */
  290. err = test_psset_alloc_reuse(&psset, &alloc[1][0], 2 * PAGE);
  291. expect_false(err, "Allocation should have succeeded");
  292. }
  293. TEST_END
  294. static void
  295. stats_expect_empty(psset_bin_stats_t *stats) {
  296. assert_zu_eq(0, stats->npageslabs,
  297. "Supposedly empty bin had positive npageslabs");
  298. expect_zu_eq(0, stats->nactive, "Unexpected nonempty bin"
  299. "Supposedly empty bin had positive nactive");
  300. }
  301. static void
  302. stats_expect(psset_t *psset, size_t nactive) {
  303. if (nactive == HUGEPAGE_PAGES) {
  304. expect_zu_eq(1, psset->stats.full_slabs[0].npageslabs,
  305. "Expected a full slab");
  306. expect_zu_eq(HUGEPAGE_PAGES,
  307. psset->stats.full_slabs[0].nactive,
  308. "Should have exactly filled the bin");
  309. } else {
  310. stats_expect_empty(&psset->stats.full_slabs[0]);
  311. }
  312. size_t ninactive = HUGEPAGE_PAGES - nactive;
  313. pszind_t nonempty_pind = PSSET_NPSIZES;
  314. if (ninactive != 0 && ninactive < HUGEPAGE_PAGES) {
  315. nonempty_pind = sz_psz2ind(sz_psz_quantize_floor(
  316. ninactive << LG_PAGE));
  317. }
  318. for (pszind_t i = 0; i < PSSET_NPSIZES; i++) {
  319. if (i == nonempty_pind) {
  320. assert_zu_eq(1,
  321. psset->stats.nonfull_slabs[i][0].npageslabs,
  322. "Should have found a slab");
  323. expect_zu_eq(nactive,
  324. psset->stats.nonfull_slabs[i][0].nactive,
  325. "Mismatch in active pages");
  326. } else {
  327. stats_expect_empty(&psset->stats.nonfull_slabs[i][0]);
  328. }
  329. }
  330. expect_zu_eq(nactive, psset_nactive(psset), "");
  331. }
  332. TEST_BEGIN(test_stats) {
  333. bool err;
  334. hpdata_t pageslab;
  335. hpdata_init(&pageslab, PAGESLAB_ADDR, PAGESLAB_AGE);
  336. edata_t alloc[HUGEPAGE_PAGES];
  337. psset_t psset;
  338. psset_init(&psset);
  339. stats_expect(&psset, 0);
  340. edata_init_test(&alloc[0]);
  341. test_psset_alloc_new(&psset, &pageslab, &alloc[0], PAGE);
  342. for (size_t i = 1; i < HUGEPAGE_PAGES; i++) {
  343. stats_expect(&psset, i);
  344. edata_init_test(&alloc[i]);
  345. err = test_psset_alloc_reuse(&psset, &alloc[i], PAGE);
  346. expect_false(err, "Nonempty psset failed page allocation.");
  347. }
  348. stats_expect(&psset, HUGEPAGE_PAGES);
  349. hpdata_t *ps;
  350. for (ssize_t i = HUGEPAGE_PAGES - 1; i >= 0; i--) {
  351. ps = test_psset_dalloc(&psset, &alloc[i]);
  352. expect_true((ps == NULL) == (i != 0),
  353. "test_psset_dalloc should only evict a slab on the last "
  354. "free");
  355. stats_expect(&psset, i);
  356. }
  357. test_psset_alloc_new(&psset, &pageslab, &alloc[0], PAGE);
  358. stats_expect(&psset, 1);
  359. psset_update_begin(&psset, &pageslab);
  360. stats_expect(&psset, 0);
  361. psset_update_end(&psset, &pageslab);
  362. stats_expect(&psset, 1);
  363. }
  364. TEST_END
  365. /*
  366. * Fills in and inserts two pageslabs, with the first better than the second,
  367. * and each fully allocated (into the allocations in allocs and worse_allocs,
  368. * each of which should be HUGEPAGE_PAGES long), except for a single free page
  369. * at the end.
  370. *
  371. * (There's nothing magic about these numbers; it's just useful to share the
  372. * setup between the oldest fit and the insert/remove test).
  373. */
  374. static void
  375. init_test_pageslabs(psset_t *psset, hpdata_t *pageslab,
  376. hpdata_t *worse_pageslab, edata_t *alloc, edata_t *worse_alloc) {
  377. bool err;
  378. hpdata_init(pageslab, (void *)(10 * HUGEPAGE), PAGESLAB_AGE);
  379. /*
  380. * This pageslab would be better from an address-first-fit POV, but
  381. * worse from an age POV.
  382. */
  383. hpdata_init(worse_pageslab, (void *)(9 * HUGEPAGE), PAGESLAB_AGE + 1);
  384. psset_init(psset);
  385. edata_init_test(&alloc[0]);
  386. test_psset_alloc_new(psset, pageslab, &alloc[0], PAGE);
  387. for (size_t i = 1; i < HUGEPAGE_PAGES; i++) {
  388. edata_init_test(&alloc[i]);
  389. err = test_psset_alloc_reuse(psset, &alloc[i], PAGE);
  390. expect_false(err, "Nonempty psset failed page allocation.");
  391. expect_ptr_eq(pageslab, edata_ps_get(&alloc[i]),
  392. "Allocated from the wrong pageslab");
  393. }
  394. edata_init_test(&worse_alloc[0]);
  395. test_psset_alloc_new(psset, worse_pageslab, &worse_alloc[0], PAGE);
  396. expect_ptr_eq(worse_pageslab, edata_ps_get(&worse_alloc[0]),
  397. "Allocated from the wrong pageslab");
  398. /*
  399. * Make the two pssets otherwise indistinguishable; all full except for
  400. * a single page.
  401. */
  402. for (size_t i = 1; i < HUGEPAGE_PAGES - 1; i++) {
  403. edata_init_test(&worse_alloc[i]);
  404. err = test_psset_alloc_reuse(psset, &alloc[i], PAGE);
  405. expect_false(err, "Nonempty psset failed page allocation.");
  406. expect_ptr_eq(worse_pageslab, edata_ps_get(&alloc[i]),
  407. "Allocated from the wrong pageslab");
  408. }
  409. /* Deallocate the last page from the older pageslab. */
  410. hpdata_t *evicted = test_psset_dalloc(psset,
  411. &alloc[HUGEPAGE_PAGES - 1]);
  412. expect_ptr_null(evicted, "Unexpected eviction");
  413. }
  414. TEST_BEGIN(test_oldest_fit) {
  415. bool err;
  416. edata_t alloc[HUGEPAGE_PAGES];
  417. edata_t worse_alloc[HUGEPAGE_PAGES];
  418. hpdata_t pageslab;
  419. hpdata_t worse_pageslab;
  420. psset_t psset;
  421. init_test_pageslabs(&psset, &pageslab, &worse_pageslab, alloc,
  422. worse_alloc);
  423. /* The edata should come from the better pageslab. */
  424. edata_t test_edata;
  425. edata_init_test(&test_edata);
  426. err = test_psset_alloc_reuse(&psset, &test_edata, PAGE);
  427. expect_false(err, "Nonempty psset failed page allocation");
  428. expect_ptr_eq(&pageslab, edata_ps_get(&test_edata),
  429. "Allocated from the wrong pageslab");
  430. }
  431. TEST_END
  432. TEST_BEGIN(test_insert_remove) {
  433. bool err;
  434. hpdata_t *ps;
  435. edata_t alloc[HUGEPAGE_PAGES];
  436. edata_t worse_alloc[HUGEPAGE_PAGES];
  437. hpdata_t pageslab;
  438. hpdata_t worse_pageslab;
  439. psset_t psset;
  440. init_test_pageslabs(&psset, &pageslab, &worse_pageslab, alloc,
  441. worse_alloc);
  442. /* Remove better; should still be able to alloc from worse. */
  443. psset_update_begin(&psset, &pageslab);
  444. err = test_psset_alloc_reuse(&psset, &worse_alloc[HUGEPAGE_PAGES - 1],
  445. PAGE);
  446. expect_false(err, "Removal should still leave an empty page");
  447. expect_ptr_eq(&worse_pageslab,
  448. edata_ps_get(&worse_alloc[HUGEPAGE_PAGES - 1]),
  449. "Allocated out of wrong ps");
  450. /*
  451. * After deallocating the previous alloc and reinserting better, it
  452. * should be preferred for future allocations.
  453. */
  454. ps = test_psset_dalloc(&psset, &worse_alloc[HUGEPAGE_PAGES - 1]);
  455. expect_ptr_null(ps, "Incorrect eviction of nonempty pageslab");
  456. psset_update_end(&psset, &pageslab);
  457. err = test_psset_alloc_reuse(&psset, &alloc[HUGEPAGE_PAGES - 1], PAGE);
  458. expect_false(err, "psset should be nonempty");
  459. expect_ptr_eq(&pageslab, edata_ps_get(&alloc[HUGEPAGE_PAGES - 1]),
  460. "Removal/reinsertion shouldn't change ordering");
  461. /*
  462. * After deallocating and removing both, allocations should fail.
  463. */
  464. ps = test_psset_dalloc(&psset, &alloc[HUGEPAGE_PAGES - 1]);
  465. expect_ptr_null(ps, "Incorrect eviction");
  466. psset_update_begin(&psset, &pageslab);
  467. psset_update_begin(&psset, &worse_pageslab);
  468. err = test_psset_alloc_reuse(&psset, &alloc[HUGEPAGE_PAGES - 1], PAGE);
  469. expect_true(err, "psset should be empty, but an alloc succeeded");
  470. }
  471. TEST_END
  472. TEST_BEGIN(test_purge_prefers_nonhuge) {
  473. /*
  474. * All else being equal, we should prefer purging non-huge pages over
  475. * huge ones for non-empty extents.
  476. */
  477. /* Nothing magic about this constant. */
  478. enum {
  479. NHP = 23,
  480. };
  481. hpdata_t *hpdata;
  482. psset_t psset;
  483. psset_init(&psset);
  484. hpdata_t hpdata_huge[NHP];
  485. uintptr_t huge_begin = (uintptr_t)&hpdata_huge[0];
  486. uintptr_t huge_end = (uintptr_t)&hpdata_huge[NHP];
  487. hpdata_t hpdata_nonhuge[NHP];
  488. uintptr_t nonhuge_begin = (uintptr_t)&hpdata_nonhuge[0];
  489. uintptr_t nonhuge_end = (uintptr_t)&hpdata_nonhuge[NHP];
  490. for (size_t i = 0; i < NHP; i++) {
  491. hpdata_init(&hpdata_huge[i], (void *)((10 + i) * HUGEPAGE),
  492. 123 + i);
  493. psset_insert(&psset, &hpdata_huge[i]);
  494. hpdata_init(&hpdata_nonhuge[i],
  495. (void *)((10 + NHP + i) * HUGEPAGE),
  496. 456 + i);
  497. psset_insert(&psset, &hpdata_nonhuge[i]);
  498. }
  499. for (int i = 0; i < 2 * NHP; i++) {
  500. hpdata = psset_pick_alloc(&psset, HUGEPAGE * 3 / 4);
  501. psset_update_begin(&psset, hpdata);
  502. void *ptr;
  503. ptr = hpdata_reserve_alloc(hpdata, HUGEPAGE * 3 / 4);
  504. /* Ignore the first alloc, which will stick around. */
  505. (void)ptr;
  506. /*
  507. * The second alloc is to dirty the pages; free it immediately
  508. * after allocating.
  509. */
  510. ptr = hpdata_reserve_alloc(hpdata, HUGEPAGE / 4);
  511. hpdata_unreserve(hpdata, ptr, HUGEPAGE / 4);
  512. if (huge_begin <= (uintptr_t)hpdata
  513. && (uintptr_t)hpdata < huge_end) {
  514. hpdata_hugify(hpdata);
  515. }
  516. hpdata_purge_allowed_set(hpdata, true);
  517. psset_update_end(&psset, hpdata);
  518. }
  519. /*
  520. * We've got a bunch of 1/8th dirty hpdatas. It should give us all the
  521. * non-huge ones to purge, then all the huge ones, then refuse to purge
  522. * further.
  523. */
  524. for (int i = 0; i < NHP; i++) {
  525. hpdata = psset_pick_purge(&psset);
  526. assert_true(nonhuge_begin <= (uintptr_t)hpdata
  527. && (uintptr_t)hpdata < nonhuge_end, "");
  528. psset_update_begin(&psset, hpdata);
  529. test_psset_fake_purge(hpdata);
  530. hpdata_purge_allowed_set(hpdata, false);
  531. psset_update_end(&psset, hpdata);
  532. }
  533. for (int i = 0; i < NHP; i++) {
  534. hpdata = psset_pick_purge(&psset);
  535. expect_true(huge_begin <= (uintptr_t)hpdata
  536. && (uintptr_t)hpdata < huge_end, "");
  537. psset_update_begin(&psset, hpdata);
  538. hpdata_dehugify(hpdata);
  539. test_psset_fake_purge(hpdata);
  540. hpdata_purge_allowed_set(hpdata, false);
  541. psset_update_end(&psset, hpdata);
  542. }
  543. }
  544. TEST_END
  545. TEST_BEGIN(test_purge_prefers_empty) {
  546. void *ptr;
  547. psset_t psset;
  548. psset_init(&psset);
  549. hpdata_t hpdata_empty;
  550. hpdata_t hpdata_nonempty;
  551. hpdata_init(&hpdata_empty, (void *)(10 * HUGEPAGE), 123);
  552. psset_insert(&psset, &hpdata_empty);
  553. hpdata_init(&hpdata_nonempty, (void *)(11 * HUGEPAGE), 456);
  554. psset_insert(&psset, &hpdata_nonempty);
  555. psset_update_begin(&psset, &hpdata_empty);
  556. ptr = hpdata_reserve_alloc(&hpdata_empty, PAGE);
  557. expect_ptr_eq(hpdata_addr_get(&hpdata_empty), ptr, "");
  558. hpdata_unreserve(&hpdata_empty, ptr, PAGE);
  559. hpdata_purge_allowed_set(&hpdata_empty, true);
  560. psset_update_end(&psset, &hpdata_empty);
  561. psset_update_begin(&psset, &hpdata_nonempty);
  562. ptr = hpdata_reserve_alloc(&hpdata_nonempty, 10 * PAGE);
  563. expect_ptr_eq(hpdata_addr_get(&hpdata_nonempty), ptr, "");
  564. hpdata_unreserve(&hpdata_nonempty, ptr, 9 * PAGE);
  565. hpdata_purge_allowed_set(&hpdata_nonempty, true);
  566. psset_update_end(&psset, &hpdata_nonempty);
  567. /*
  568. * The nonempty slab has 9 dirty pages, while the empty one has only 1.
  569. * We should still pick the empty one for purging.
  570. */
  571. hpdata_t *to_purge = psset_pick_purge(&psset);
  572. expect_ptr_eq(&hpdata_empty, to_purge, "");
  573. }
  574. TEST_END
  575. TEST_BEGIN(test_purge_prefers_empty_huge) {
  576. void *ptr;
  577. psset_t psset;
  578. psset_init(&psset);
  579. enum {NHP = 10 };
  580. hpdata_t hpdata_huge[NHP];
  581. hpdata_t hpdata_nonhuge[NHP];
  582. uintptr_t cur_addr = 100 * HUGEPAGE;
  583. uint64_t cur_age = 123;
  584. for (int i = 0; i < NHP; i++) {
  585. hpdata_init(&hpdata_huge[i], (void *)cur_addr, cur_age);
  586. cur_addr += HUGEPAGE;
  587. cur_age++;
  588. psset_insert(&psset, &hpdata_huge[i]);
  589. hpdata_init(&hpdata_nonhuge[i], (void *)cur_addr, cur_age);
  590. cur_addr += HUGEPAGE;
  591. cur_age++;
  592. psset_insert(&psset, &hpdata_nonhuge[i]);
  593. /*
  594. * Make the hpdata_huge[i] fully dirty, empty, purgable, and
  595. * huge.
  596. */
  597. psset_update_begin(&psset, &hpdata_huge[i]);
  598. ptr = hpdata_reserve_alloc(&hpdata_huge[i], HUGEPAGE);
  599. expect_ptr_eq(hpdata_addr_get(&hpdata_huge[i]), ptr, "");
  600. hpdata_hugify(&hpdata_huge[i]);
  601. hpdata_unreserve(&hpdata_huge[i], ptr, HUGEPAGE);
  602. hpdata_purge_allowed_set(&hpdata_huge[i], true);
  603. psset_update_end(&psset, &hpdata_huge[i]);
  604. /*
  605. * Make hpdata_nonhuge[i] fully dirty, empty, purgable, and
  606. * non-huge.
  607. */
  608. psset_update_begin(&psset, &hpdata_nonhuge[i]);
  609. ptr = hpdata_reserve_alloc(&hpdata_nonhuge[i], HUGEPAGE);
  610. expect_ptr_eq(hpdata_addr_get(&hpdata_nonhuge[i]), ptr, "");
  611. hpdata_unreserve(&hpdata_nonhuge[i], ptr, HUGEPAGE);
  612. hpdata_purge_allowed_set(&hpdata_nonhuge[i], true);
  613. psset_update_end(&psset, &hpdata_nonhuge[i]);
  614. }
  615. /*
  616. * We have a bunch of empty slabs, half huge, half nonhuge, inserted in
  617. * alternating order. We should pop all the huge ones before popping
  618. * any of the non-huge ones for purging.
  619. */
  620. for (int i = 0; i < NHP; i++) {
  621. hpdata_t *to_purge = psset_pick_purge(&psset);
  622. expect_ptr_eq(&hpdata_huge[i], to_purge, "");
  623. psset_update_begin(&psset, to_purge);
  624. hpdata_purge_allowed_set(to_purge, false);
  625. psset_update_end(&psset, to_purge);
  626. }
  627. for (int i = 0; i < NHP; i++) {
  628. hpdata_t *to_purge = psset_pick_purge(&psset);
  629. expect_ptr_eq(&hpdata_nonhuge[i], to_purge, "");
  630. psset_update_begin(&psset, to_purge);
  631. hpdata_purge_allowed_set(to_purge, false);
  632. psset_update_end(&psset, to_purge);
  633. }
  634. }
  635. TEST_END
  636. int
  637. main(void) {
  638. return test_no_reentrancy(
  639. test_empty,
  640. test_fill,
  641. test_reuse,
  642. test_evict,
  643. test_multi_pageslab,
  644. test_stats,
  645. test_oldest_fit,
  646. test_insert_remove,
  647. test_purge_prefers_nonhuge,
  648. test_purge_prefers_empty,
  649. test_purge_prefers_empty_huge);
  650. }