| 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 | |
| 10 | static bool done2 = false; |
| 11 | static uint64_t avg_complete_time[BIO_SCHED_LEVELS] = {0}; |
| 12 | static uint64_t total_complete_time[BIO_SCHED_LEVELS] = {0}; |
| 13 | static _Atomic uint32_t runs = 0; |
| 14 | |
| 15 | static 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 |
| 29 | static uint64_t runs_per_lvl[BIO_SCHED_LEVELS] = {0}; |
| 30 | static struct bio_request *rqs[BIO_SCHED_TEST_RUNS_MAX] = {0}; |
| 31 | static uint8_t *buffers[BIO_SCHED_TEST_RUNS_MAX] = {0}; |
| 32 | |
| 33 | TEST_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 | |