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