1#include "block/sched/tests/test_internal.h"
2
3#define EXT2_ROOT struct vfs_node *root = global.root_node
4
5static cc_unused bool done2 = false;
6static uint64_t avg_complete_time[BIO_SCHED_LEVELS] = {0};
7static uint64_t total_complete_time[BIO_SCHED_LEVELS] = {0};
8static atomic_uint32_t runs = 0;
9
10static void bio_sch_callback(struct bio_request *req) {
11 done2 = true;
12 uint64_t q_ms = (uint64_t) req->user_data >> 12;
13 uint64_t q_lvl = (uint64_t) req->user_data & 7;
14 time_ms_t time = time_get_ms() - q_ms;
15 total_complete_time[q_lvl] += time;
16 req->user_data = NULL;
17 atomic_inc(&runs);
18 TEST_ASSERT_VOID(req->status == BIO_STATUS_OK);
19}
20
21#define BIO_SCHED_TEST_RUNS_MAX ((size_t) 4096)
22static uint64_t runs_per_lvl[BIO_SCHED_LEVELS] = {0};
23static struct bio_request *rqs[BIO_SCHED_TEST_RUNS_MAX] = {0};
24static uint8_t *buffers[BIO_SCHED_TEST_RUNS_MAX] = {0};
25
26TEST_DEFINE_INTEGRATION(bio_sched, delay_enqueue,
27 TEST_INTENSITY(64, 1024, 4096), .min_ram_mib = 8,
28 .required_fs = FS_EXT2) {
29 EXT2_ROOT;
30 struct ext2_fs *fs = root->fs_data;
31 struct block_device *d = fs->drive;
32 enforce(d);
33
34 size_t test_runs = MIN(ctx->intensity_val ? ctx->intensity_val : 1024,
35 BIO_SCHED_TEST_RUNS_MAX);
36
37 memset(runs_per_lvl, 0, sizeof(runs_per_lvl));
38 memset(total_complete_time, 0, sizeof(total_complete_time));
39 memset(avg_complete_time, 0, sizeof(avg_complete_time));
40 atomic_store(&runs, 0);
41
42 prng_seed(seed: ctx->seed ? ctx->seed : time_get_us());
43
44 for (uint64_t i = 0; i < test_runs; i++) {
45 uint8_t *buf = valloc_aligned(PAGE_SIZE, PAGE_SIZE);
46 struct bio_request *rq = vnew(rq, ALLOC_ZERO);
47 TEST_ASSERT_NONNULL(rq);
48 TEST_ASSERT_NONNULL(buf);
49 TEST_ASSERT(IS_PAGE_ALIGNED(buf));
50
51 rq->disk = d;
52 rq->lba = (i * 2) % 512;
53 rq->sector_count = 1;
54 rq->size = 512;
55 rq->on_complete = bio_sch_callback;
56 rq->buffer = buf;
57 rq->priority = prng_next() % BIO_SCHED_LEVELS;
58 rq->write = false;
59 list_node_init(n: &rq->list);
60
61 rqs[i] = rq;
62 buffers[i] = buf;
63 }
64
65 for (size_t i = 0; i < test_runs; i++)
66 for (size_t j = 0; j < test_runs; j++)
67 if (i != j && rqs[i] == rqs[j])
68 test_err("duplicate at %zu and %zu\n", i, j);
69
70 for (size_t i = 0; i < test_runs; i++) {
71 if (!rqs[i]->disk) {
72 test_err("rq %p %zu\n", rqs[i], i);
73 return TEST_SUCCESS;
74 }
75
76 enforce(rqs[i]->disk);
77 }
78
79 uint64_t ms = time_get_ms();
80 for (uint64_t i = 0; i < test_runs; i++) {
81 struct bio_request *rq = rqs[i];
82 runs_per_lvl[rq->priority]++;
83 rq->user_data = (void *) ((time_get_ms() << 12) | rq->priority);
84 bio_sched_enqueue(disk: d, req: rq);
85 }
86 ms = time_get_ms() - ms;
87
88 char *msg = valloc(100);
89 TEST_ASSERT_NONNULL(msg);
90 snprintf(msg, 100, "Total time spent enqueuing is %lu ms", ms);
91 test_info("%s", msg);
92
93 bio_sched_dispatch_all(disk: d);
94
95 for (uint64_t i = 0; i < 150000; i++)
96 cpu_pause();
97
98 for (uint64_t i = 0; i < BIO_SCHED_LEVELS; i++) {
99 if (runs_per_lvl[i] > 0)
100 avg_complete_time[i] = total_complete_time[i] / runs_per_lvl[i];
101 else
102 avg_complete_time[i] = 0;
103 char *lvl_msg = valloc(100, ALLOC_ZERO);
104 TEST_ASSERT_NONNULL(lvl_msg);
105 snprintf(lvl_msg, 100, "Average completion time of level %lu is %lu ms",
106 i, avg_complete_time[i]);
107 test_info("%s", lvl_msg);
108 }
109
110 char *m2 = valloc(100);
111 TEST_ASSERT_NONNULL(m2);
112 snprintf(m2, 100, "Runs is %d, test_runs is %zu", atomic_load(&runs),
113 test_runs);
114 test_info("%s", m2);
115 TEST_ASSERT_LE(atomic_load(&runs), test_runs);
116
117 return TEST_SUCCESS;
118}
119