| 1 | #include "block/sched/tests/test_internal.h" |
| 2 | |
| 3 | #define EXT2_ROOT struct vfs_node *root = global.root_node |
| 4 | |
| 5 | static cc_unused bool done2 = false; |
| 6 | static uint64_t avg_complete_time[BIO_SCHED_LEVELS] = {0}; |
| 7 | static uint64_t total_complete_time[BIO_SCHED_LEVELS] = {0}; |
| 8 | static atomic_uint32_t runs = 0; |
| 9 | |
| 10 | static 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) |
| 22 | static uint64_t runs_per_lvl[BIO_SCHED_LEVELS] = {0}; |
| 23 | static struct bio_request *rqs[BIO_SCHED_TEST_RUNS_MAX] = {0}; |
| 24 | static uint8_t *buffers[BIO_SCHED_TEST_RUNS_MAX] = {0}; |
| 25 | |
| 26 | TEST_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 | |