1#include "block/tests/test_internal.h"
2
3#include <mem/nofail.h>
4#include <thread/thread.h>
5#include <time/time.h>
6
7/* The completion has to reach the driver's worker thread and back, so the
8 * budget is loose */
9#define BIO_COMPLETE_TIMEOUT_MS 2000
10#define BIO_POLL_INTERVAL_US 200
11
12TEST_GROUP_DEFINE(bio, .intensity_desc = {
13 .curve = SCALE_PIECEWISE_LINEAR,
14 .unit = "ios",
15 });
16
17#define EXT2_ROOT struct vfs_node *root = global.root_node
18
19static atomic_bool done = false;
20static void bio_callback(struct bio_request *req) {
21 cc_ignore(req);
22 atomic_store(&done, true);
23}
24
25TEST_DEFINE_INTEGRATION(bio, async_submit, TEST_INTENSITY(1, 1, 16),
26 .required_fs = FS_EXT2) {
27 EXT2_ROOT;
28 struct ext2_fs *fs = root->fs_data;
29 struct block_device *d = fs->drive;
30 uint64_t run_times = ctx->intensity_val ? ctx->intensity_val : 1;
31 time_ms_t worst_ms = 0;
32 atomic_store(&done, false);
33 irq_enable_untracked();
34
35 for (uint64_t i = 0; i < run_times; i++) {
36 /* nofail, not TEST_ASSERT: earlier requests may still be in flight */
37 struct bio_request *bio = nofail_vnew(bio, .flags = 0);
38 uint8_t *buf = nofail(valloc_aligned(64 * PAGE_SIZE, PAGE_SIZE));
39 *bio = (struct bio_request){
40 .lba = 0,
41 .buffer = buf,
42 .size = 512 * 512,
43 .sector_count = 512,
44 .write = false,
45 .done = false,
46 .status = -1,
47 .on_complete = bio_callback,
48 .priority = BIO_RQ_MEDIUM,
49 .user_data = (void *) BIO_RQ_MEDIUM,
50 };
51
52 if (i % 2 == 0) {
53 bio->priority = BIO_RQ_HIGH;
54 bio->user_data = (void *) BIO_RQ_HIGH;
55 }
56
57 /* Reset per request */
58 atomic_store(&done, false);
59
60 bool submitted = d->submit_bio_async(d, bio);
61 if (!submitted)
62 return TEST_FAIL("submit_bio_async rejected the request");
63
64 time_ms_t start = time_get_ms();
65 while (!atomic_load(&done)) {
66 if (time_get_ms() - start >= BIO_COMPLETE_TIMEOUT_MS) {
67 test_err("request %llu never completed: %u ms elapsed, "
68 "status still %d",
69 (unsigned long long) i, BIO_COMPLETE_TIMEOUT_MS,
70 bio->status);
71 return TEST_FAIL("bio did not complete before the deadline");
72 }
73 thread_sleep_for_us(BIO_POLL_INTERVAL_US);
74 }
75
76 time_ms_t took = time_get_ms() - start;
77 if (took > worst_ms)
78 worst_ms = took;
79
80 TEST_ASSERT_OK(bio->status);
81
82 /* Now that the request is known to be finished, its buffers can go
83 * back */
84 vfree_aligned(buf);
85 vfree(bio);
86 }
87
88 test_info("%llu request(s), worst completion %llu ms",
89 (unsigned long long) run_times, (unsigned long long) worst_ms);
90
91 TEST_ASSERT_TRUE(atomic_load(&done));
92 return TEST_SUCCESS;
93}
94