]> git.proxmox.com Git - ceph.git/blame - ceph/src/seastar/tests/unit/io_queue_test.cc
import quincy beta 17.1.0
[ceph.git] / ceph / src / seastar / tests / unit / io_queue_test.cc
CommitLineData
20effc67
TL
1/*
2 * This file is open source software, licensed to you under the terms
3 * of the Apache License, Version 2.0 (the "License"). See the NOTICE file
4 * distributed with this work for additional information regarding copyright
5 * ownership. You may not use this file except in compliance with the License.
6 *
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing,
12 * software distributed under the License is distributed on an
13 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 * KIND, either express or implied. See the License for the
15 * specific language governing permissions and limitations
16 * under the License.
17 */
18
19/*
20 * Copyright (C) 2021 ScyllaDB
21 */
22
23#include <seastar/core/thread.hh>
24#include <seastar/testing/test_case.hh>
25#include <seastar/testing/thread_test_case.hh>
26#include <seastar/testing/test_runner.hh>
27#include <seastar/core/reactor.hh>
28#include <seastar/core/smp.hh>
29#include <seastar/core/when_all.hh>
30#include <seastar/core/file.hh>
31#include <seastar/core/io_queue.hh>
32#include <seastar/core/io_intent.hh>
33#include <seastar/core/internal/io_request.hh>
34#include <seastar/core/internal/io_sink.hh>
35
36using namespace seastar;
37
38template <size_t Len>
39struct fake_file {
40 int data[Len] = {};
41
42 static internal::io_request make_write_req(size_t idx, int* buf) {
43 return internal::io_request::make_write(0, idx, buf, 1, false);
44 }
45
46 void execute_write_req(internal::io_request& rq, io_completion* desc) {
47 data[rq.pos()] = *(reinterpret_cast<int*>(rq.address()));
48 desc->complete_with(rq.size());
49 }
50};
51
52struct io_queue_for_tests {
53 io_group_ptr group;
54 internal::io_sink sink;
55 io_queue queue;
56
57 io_queue_for_tests()
58 : group(std::make_shared<io_group>(io_queue::config{0}))
59 , sink()
60 , queue(group, sink)
61 {}
62};
63
64SEASTAR_THREAD_TEST_CASE(test_basic_flow) {
65 io_queue_for_tests tio;
66 fake_file<1> file;
67
68 auto val = std::make_unique<int>(42);
69 auto f = tio.queue.queue_request(default_priority_class(), 0, file.make_write_req(0, val.get()), nullptr)
70 .then([&file] (size_t len) {
71 BOOST_REQUIRE(file.data[0] == 42);
72 });
73
74 tio.queue.poll_io_queue();
75 tio.sink.drain([&file] (internal::io_request& rq, io_completion* desc) -> bool {
76 file.execute_write_req(rq, desc);
77 return true;
78 });
79
80 f.get();
81}
82
83SEASTAR_THREAD_TEST_CASE(test_intent_safe_ref) {
84 auto get_cancelled = [] (internal::intent_reference& iref) -> bool {
85 try {
86 iref.retrieve();
87 return false;
88 } catch(seastar::cancelled_error& err) {
89 return true;
90 }
91 };
92
93 io_intent intent, intent_x;
94
95 internal::intent_reference ref_orig(&intent);
96 BOOST_REQUIRE(ref_orig.retrieve() == &intent);
97
98 // Test move armed
99 internal::intent_reference ref_armed(std::move(ref_orig));
100 BOOST_REQUIRE(ref_orig.retrieve() == nullptr);
101 BOOST_REQUIRE(ref_armed.retrieve() == &intent);
102
103 internal::intent_reference ref_armed_2(&intent_x);
104 ref_armed_2 = std::move(ref_armed);
105 BOOST_REQUIRE(ref_armed.retrieve() == nullptr);
106 BOOST_REQUIRE(ref_armed_2.retrieve() == &intent);
107
108 intent.cancel();
109 BOOST_REQUIRE(get_cancelled(ref_armed_2));
110
111 // Test move cancelled
112 internal::intent_reference ref_cancelled(std::move(ref_armed_2));
113 BOOST_REQUIRE(ref_armed_2.retrieve() == nullptr);
114 BOOST_REQUIRE(get_cancelled(ref_cancelled));
115
116 internal::intent_reference ref_cancelled_2(&intent_x);
117 ref_cancelled_2 = std::move(ref_cancelled);
118 BOOST_REQUIRE(ref_cancelled.retrieve() == nullptr);
119 BOOST_REQUIRE(get_cancelled(ref_cancelled_2));
120
121 // Test move empty
122 internal::intent_reference ref_empty(std::move(ref_orig));
123 BOOST_REQUIRE(ref_empty.retrieve() == nullptr);
124
125 internal::intent_reference ref_empty_2(&intent_x);
126 ref_empty_2 = std::move(ref_empty);
127 BOOST_REQUIRE(ref_empty_2.retrieve() == nullptr);
128}
129
130static constexpr int nr_requests = 24;
131
132SEASTAR_THREAD_TEST_CASE(test_io_cancellation) {
133 fake_file<nr_requests> file;
134
135 io_queue_for_tests tio;
136 io_priority_class pc0 = io_priority_class::register_one("a", 100);
137 io_priority_class pc1 = io_priority_class::register_one("b", 100);
138
139 size_t idx = 0;
140 int val = 100;
141
142 io_intent live, dead;
143
144 std::vector<future<>> finished;
145 std::vector<future<>> cancelled;
146
147 auto queue_legacy_request = [&] (io_queue_for_tests& q, io_priority_class& pc) {
148 auto buf = std::make_unique<int>(val);
149 auto f = q.queue.queue_request(pc, 0, file.make_write_req(idx, buf.get()), nullptr)
150 .then([&file, idx, val, buf = std::move(buf)] (size_t len) {
151 BOOST_REQUIRE(file.data[idx] == val);
152 return make_ready_future<>();
153 });
154 finished.push_back(std::move(f));
155 idx++;
156 val++;
157 };
158
159 auto queue_live_request = [&] (io_queue_for_tests& q, io_priority_class& pc) {
160 auto buf = std::make_unique<int>(val);
161 auto f = q.queue.queue_request(pc, 0, file.make_write_req(idx, buf.get()), &live)
162 .then([&file, idx, val, buf = std::move(buf)] (size_t len) {
163 BOOST_REQUIRE(file.data[idx] == val);
164 return make_ready_future<>();
165 });
166 finished.push_back(std::move(f));
167 idx++;
168 val++;
169 };
170
171 auto queue_dead_request = [&] (io_queue_for_tests& q, io_priority_class& pc) {
172 auto buf = std::make_unique<int>(val);
173 auto f = q.queue.queue_request(pc, 0, file.make_write_req(idx, buf.get()), &dead)
174 .then_wrapped([buf = std::move(buf)] (auto&& f) {
175 try {
176 f.get();
177 BOOST_REQUIRE(false);
178 } catch(...) {}
179 return make_ready_future<>();
180 })
181 .then([&file, idx] () {
182 BOOST_REQUIRE(file.data[idx] == 0);
183 });
184 cancelled.push_back(std::move(f));
185 idx++;
186 val++;
187 };
188
189 auto seed = std::random_device{}();
190 std::default_random_engine reng(seed);
191 std::uniform_int_distribution<> dice(0, 5);
192
193 for (int i = 0; i < nr_requests; i++) {
194 int pc = dice(reng) % 2;
195 if (dice(reng) < 3) {
196 fmt::print("queue live req to pc {}\n", pc);
197 queue_live_request(tio, pc == 0 ? pc0 : pc1);
198 } else if (dice(reng) < 5) {
199 fmt::print("queue dead req to pc {}\n", pc);
200 queue_dead_request(tio, pc == 0 ? pc0 : pc1);
201 } else {
202 fmt::print("queue legacy req to pc {}\n", pc);
203 queue_legacy_request(tio, pc == 0 ? pc0 : pc1);
204 }
205 }
206
207 dead.cancel();
208
209 // cancelled requests must resolve right at once
210
211 when_all_succeed(cancelled.begin(), cancelled.end()).get();
212
213 tio.queue.poll_io_queue();
214 tio.sink.drain([&file] (internal::io_request& rq, io_completion* desc) -> bool {
215 file.execute_write_req(rq, desc);
216 return true;
217 });
218
219 when_all_succeed(finished.begin(), finished.end()).get();
220}