]>
Commit | Line | Data |
---|---|---|
20effc67 TL |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | ||
4 | #include "test/crimson/gtest_seastar.h" | |
5 | #include "test/crimson/seastore/transaction_manager_test_state.h" | |
6 | ||
7 | #include "crimson/os/seastore/onode.h" | |
8 | #include "crimson/os/seastore/object_data_handler.h" | |
9 | ||
10 | using namespace crimson; | |
11 | using namespace crimson::os; | |
12 | using namespace crimson::os::seastore; | |
13 | ||
14 | #define MAX_OBJECT_SIZE (16<<20) | |
15 | #define DEFAULT_OBJECT_DATA_RESERVATION (16<<20) | |
16 | #define DEFAULT_OBJECT_METADATA_RESERVATION (16<<20) | |
17 | ||
18 | namespace { | |
19 | [[maybe_unused]] seastar::logger& logger() { | |
20 | return crimson::get_logger(ceph_subsys_test); | |
21 | } | |
22 | } | |
23 | ||
24 | class TestOnode final : public Onode { | |
25 | onode_layout_t layout; | |
26 | bool dirty = false; | |
27 | ||
28 | public: | |
29 | TestOnode(uint32_t ddr, uint32_t dmr) : Onode(ddr, dmr) {} | |
30 | const onode_layout_t &get_layout() const final { | |
31 | return layout; | |
32 | } | |
33 | onode_layout_t &get_mutable_layout(Transaction &t) final { | |
34 | dirty = true; | |
35 | return layout; | |
36 | } | |
37 | bool is_dirty() const { return dirty; } | |
38 | laddr_t get_hint() const final {return L_ADDR_MIN; } | |
39 | ~TestOnode() final = default; | |
40 | }; | |
41 | ||
42 | struct object_data_handler_test_t: | |
43 | public seastar_test_suite_t, | |
44 | TMTestState { | |
45 | OnodeRef onode; | |
46 | ||
47 | bufferptr known_contents; | |
48 | extent_len_t size = 0; | |
49 | ||
50 | object_data_handler_test_t() {} | |
51 | ||
52 | void write(Transaction &t, objaddr_t offset, extent_len_t len, char fill) { | |
53 | ceph_assert(offset + len <= known_contents.length()); | |
54 | size = std::max<extent_len_t>(size, offset + len); | |
55 | memset( | |
56 | known_contents.c_str() + offset, | |
57 | fill, | |
58 | len); | |
59 | bufferlist bl; | |
60 | bl.append( | |
61 | bufferptr( | |
62 | known_contents, | |
63 | offset, | |
64 | len)); | |
65 | with_trans_intr(t, [&](auto &t) { | |
66 | return ObjectDataHandler(MAX_OBJECT_SIZE).write( | |
67 | ObjectDataHandler::context_t{ | |
68 | *tm, | |
69 | t, | |
70 | *onode, | |
71 | }, | |
72 | offset, | |
73 | bl); | |
74 | }).unsafe_get0(); | |
75 | } | |
76 | void write(objaddr_t offset, extent_len_t len, char fill) { | |
77 | auto t = create_mutate_transaction(); | |
78 | write(*t, offset, len, fill); | |
79 | return submit_transaction(std::move(t)); | |
80 | } | |
81 | ||
82 | void truncate(Transaction &t, objaddr_t offset) { | |
83 | if (size > offset) { | |
84 | memset( | |
85 | known_contents.c_str() + offset, | |
86 | 0, | |
87 | size - offset); | |
88 | with_trans_intr(t, [&](auto &t) { | |
89 | return ObjectDataHandler(MAX_OBJECT_SIZE).truncate( | |
90 | ObjectDataHandler::context_t{ | |
91 | *tm, | |
92 | t, | |
93 | *onode | |
94 | }, | |
95 | offset); | |
96 | }).unsafe_get0(); | |
97 | } | |
98 | size = offset; | |
99 | } | |
100 | void truncate(objaddr_t offset) { | |
101 | auto t = create_mutate_transaction(); | |
102 | truncate(*t, offset); | |
103 | return submit_transaction(std::move(t)); | |
104 | } | |
105 | ||
106 | void read(Transaction &t, objaddr_t offset, extent_len_t len) { | |
107 | bufferlist bl = with_trans_intr(t, [&](auto &t) { | |
108 | return ObjectDataHandler(MAX_OBJECT_SIZE).read( | |
109 | ObjectDataHandler::context_t{ | |
110 | *tm, | |
111 | t, | |
112 | *onode | |
113 | }, | |
114 | offset, | |
115 | len); | |
116 | }).unsafe_get0(); | |
117 | bufferlist known; | |
118 | known.append( | |
119 | bufferptr( | |
120 | known_contents, | |
121 | offset, | |
122 | len)); | |
123 | EXPECT_EQ(bl.length(), known.length()); | |
124 | EXPECT_EQ(bl, known); | |
125 | } | |
126 | void read(objaddr_t offset, extent_len_t len) { | |
127 | auto t = create_read_transaction(); | |
128 | read(*t, offset, len); | |
129 | } | |
130 | void read_near(objaddr_t offset, extent_len_t len, extent_len_t fuzz) { | |
131 | auto fuzzes = std::vector<int32_t>{-1 * (int32_t)fuzz, 0, (int32_t)fuzz}; | |
132 | for (auto left_fuzz : fuzzes) { | |
133 | for (auto right_fuzz : fuzzes) { | |
134 | read(offset + left_fuzz, len - left_fuzz + right_fuzz); | |
135 | } | |
136 | } | |
137 | } | |
1e59de90 TL |
138 | std::list<LBAMappingRef> get_mappings(objaddr_t offset, extent_len_t length) { |
139 | auto t = create_mutate_transaction(); | |
140 | auto ret = with_trans_intr(*t, [&](auto &t) { | |
141 | return tm->get_pins(t, offset, length); | |
142 | }).unsafe_get0(); | |
143 | return ret; | |
144 | } | |
20effc67 TL |
145 | |
146 | seastar::future<> set_up_fut() final { | |
147 | onode = new TestOnode( | |
148 | DEFAULT_OBJECT_DATA_RESERVATION, | |
149 | DEFAULT_OBJECT_METADATA_RESERVATION); | |
150 | known_contents = buffer::create(4<<20 /* 4MB */); | |
151 | memset(known_contents.c_str(), 0, known_contents.length()); | |
152 | size = 0; | |
153 | return tm_setup(); | |
154 | } | |
155 | ||
156 | seastar::future<> tear_down_fut() final { | |
157 | onode.reset(); | |
158 | size = 0; | |
159 | return tm_teardown(); | |
160 | } | |
161 | }; | |
162 | ||
163 | TEST_F(object_data_handler_test_t, single_write) | |
164 | { | |
165 | run_async([this] { | |
166 | write(1<<20, 8<<10, 'c'); | |
167 | ||
168 | read_near(1<<20, 8<<10, 1); | |
169 | read_near(1<<20, 8<<10, 512); | |
170 | }); | |
171 | } | |
172 | ||
173 | TEST_F(object_data_handler_test_t, multi_write) | |
174 | { | |
175 | run_async([this] { | |
176 | write((1<<20) - (4<<10), 4<<10, 'a'); | |
177 | write(1<<20, 4<<10, 'b'); | |
178 | write((1<<20) + (4<<10), 4<<10, 'c'); | |
179 | ||
180 | read_near(1<<20, 4<<10, 1); | |
181 | read_near(1<<20, 4<<10, 512); | |
182 | ||
183 | read_near((1<<20)-(4<<10), 12<<10, 1); | |
184 | read_near((1<<20)-(4<<10), 12<<10, 512); | |
185 | }); | |
186 | } | |
187 | ||
188 | TEST_F(object_data_handler_test_t, write_hole) | |
189 | { | |
190 | run_async([this] { | |
191 | write((1<<20) - (4<<10), 4<<10, 'a'); | |
192 | // hole at 1<<20 | |
193 | write((1<<20) + (4<<10), 4<<10, 'c'); | |
194 | ||
195 | read_near(1<<20, 4<<10, 1); | |
196 | read_near(1<<20, 4<<10, 512); | |
197 | ||
198 | read_near((1<<20)-(4<<10), 12<<10, 1); | |
199 | read_near((1<<20)-(4<<10), 12<<10, 512); | |
200 | }); | |
201 | } | |
202 | ||
203 | TEST_F(object_data_handler_test_t, overwrite_single) | |
204 | { | |
205 | run_async([this] { | |
206 | write((1<<20), 4<<10, 'a'); | |
207 | write((1<<20), 4<<10, 'c'); | |
208 | ||
209 | read_near(1<<20, 4<<10, 1); | |
210 | read_near(1<<20, 4<<10, 512); | |
211 | }); | |
212 | } | |
213 | ||
214 | TEST_F(object_data_handler_test_t, overwrite_double) | |
215 | { | |
216 | run_async([this] { | |
217 | write((1<<20), 4<<10, 'a'); | |
218 | write((1<<20)+(4<<10), 4<<10, 'c'); | |
219 | write((1<<20), 8<<10, 'b'); | |
220 | ||
221 | read_near(1<<20, 8<<10, 1); | |
222 | read_near(1<<20, 8<<10, 512); | |
223 | ||
224 | read_near(1<<20, 4<<10, 1); | |
225 | read_near(1<<20, 4<<10, 512); | |
226 | ||
227 | read_near((1<<20) + (4<<10), 4<<10, 1); | |
228 | read_near((1<<20) + (4<<10), 4<<10, 512); | |
229 | }); | |
230 | } | |
231 | ||
232 | TEST_F(object_data_handler_test_t, overwrite_partial) | |
233 | { | |
234 | run_async([this] { | |
235 | write((1<<20), 12<<10, 'a'); | |
236 | read_near(1<<20, 12<<10, 1); | |
237 | ||
238 | write((1<<20)+(8<<10), 4<<10, 'b'); | |
239 | read_near(1<<20, 12<<10, 1); | |
240 | ||
241 | write((1<<20)+(4<<10), 4<<10, 'c'); | |
242 | read_near(1<<20, 12<<10, 1); | |
243 | ||
244 | write((1<<20), 4<<10, 'd'); | |
245 | ||
246 | read_near(1<<20, 12<<10, 1); | |
247 | read_near(1<<20, 12<<10, 512); | |
248 | ||
249 | read_near(1<<20, 4<<10, 1); | |
250 | read_near(1<<20, 4<<10, 512); | |
251 | ||
252 | read_near((1<<20) + (4<<10), 4<<10, 1); | |
253 | read_near((1<<20) + (4<<10), 4<<10, 512); | |
254 | }); | |
255 | } | |
256 | ||
257 | TEST_F(object_data_handler_test_t, unaligned_write) | |
258 | { | |
259 | run_async([this] { | |
260 | objaddr_t base = 1<<20; | |
261 | write(base, (4<<10)+(1<<10), 'a'); | |
262 | read_near(base-(4<<10), 12<<10, 512); | |
263 | ||
264 | base = (1<<20) + (64<<10); | |
265 | write(base+(1<<10), (4<<10)+(1<<10), 'b'); | |
266 | read_near(base-(4<<10), 12<<10, 512); | |
267 | ||
268 | base = (1<<20) + (128<<10); | |
269 | write(base-(1<<10), (4<<10)+(2<<20), 'c'); | |
270 | read_near(base-(4<<10), 12<<10, 512); | |
271 | }); | |
272 | } | |
273 | ||
274 | TEST_F(object_data_handler_test_t, unaligned_overwrite) | |
275 | { | |
276 | run_async([this] { | |
277 | objaddr_t base = 1<<20; | |
278 | write(base, (128<<10) + (16<<10), 'x'); | |
279 | ||
280 | write(base, (4<<10)+(1<<10), 'a'); | |
281 | read_near(base-(4<<10), 12<<10, 2<<10); | |
282 | ||
283 | base = (1<<20) + (64<<10); | |
284 | write(base+(1<<10), (4<<10)+(1<<10), 'b'); | |
285 | read_near(base-(4<<10), 12<<10, 2<<10); | |
286 | ||
287 | base = (1<<20) + (128<<10); | |
288 | write(base-(1<<10), (4<<10)+(2<<20), 'c'); | |
289 | read_near(base-(4<<10), 12<<10, 2<<10); | |
290 | ||
291 | read(base, (128<<10) + (16<<10)); | |
292 | }); | |
293 | } | |
294 | ||
295 | TEST_F(object_data_handler_test_t, truncate) | |
296 | { | |
297 | run_async([this] { | |
298 | objaddr_t base = 1<<20; | |
299 | write(base, 8<<10, 'a'); | |
300 | write(base+(8<<10), 8<<10, 'b'); | |
301 | write(base+(16<<10), 8<<10, 'c'); | |
302 | ||
303 | truncate(base + (32<<10)); | |
304 | read(base, 64<<10); | |
305 | ||
306 | truncate(base + (24<<10)); | |
307 | read(base, 64<<10); | |
308 | ||
309 | truncate(base + (12<<10)); | |
310 | read(base, 64<<10); | |
311 | ||
312 | truncate(base - (12<<10)); | |
313 | read(base, 64<<10); | |
314 | }); | |
315 | } | |
1e59de90 TL |
316 | |
317 | TEST_F(object_data_handler_test_t, no_split) { | |
318 | run_async([this] { | |
319 | write(0, 8<<10, 'x'); | |
320 | write(0, 8<<10, 'a'); | |
321 | ||
322 | auto pins = get_mappings(0, 8<<10); | |
323 | EXPECT_EQ(pins.size(), 1); | |
324 | ||
325 | read(0, 8<<10); | |
326 | }); | |
327 | } | |
328 | ||
329 | TEST_F(object_data_handler_test_t, split_left) { | |
330 | run_async([this] { | |
331 | write(0, 128<<10, 'x'); | |
332 | ||
333 | write(64<<10, 60<<10, 'a'); | |
334 | ||
335 | auto pins = get_mappings(0, 128<<10); | |
336 | EXPECT_EQ(pins.size(), 2); | |
337 | ||
338 | size_t res[2] = {0, 64<<10}; | |
339 | auto base = pins.front()->get_key(); | |
340 | int i = 0; | |
341 | for (auto &pin : pins) { | |
342 | EXPECT_EQ(pin->get_key() - base, res[i]); | |
343 | i++; | |
344 | } | |
345 | read(0, 128<<10); | |
346 | }); | |
347 | } | |
348 | ||
349 | TEST_F(object_data_handler_test_t, split_right) { | |
350 | run_async([this] { | |
351 | write(0, 128<<10, 'x'); | |
352 | write(4<<10, 60<<10, 'a'); | |
353 | ||
354 | auto pins = get_mappings(0, 128<<10); | |
355 | EXPECT_EQ(pins.size(), 2); | |
356 | ||
357 | size_t res[2] = {0, 64<<10}; | |
358 | auto base = pins.front()->get_key(); | |
359 | int i = 0; | |
360 | for (auto &pin : pins) { | |
361 | EXPECT_EQ(pin->get_key() - base, res[i]); | |
362 | i++; | |
363 | } | |
364 | read(0, 128<<10); | |
365 | }); | |
366 | } | |
367 | TEST_F(object_data_handler_test_t, split_left_right) { | |
368 | run_async([this] { | |
369 | write(0, 128<<10, 'x'); | |
370 | write(48<<10, 32<<10, 'a'); | |
371 | ||
372 | auto pins = get_mappings(0, 128<<10); | |
373 | EXPECT_EQ(pins.size(), 3); | |
374 | ||
375 | size_t res[3] = {0, 48<<10, 80<<10}; | |
376 | auto base = pins.front()->get_key(); | |
377 | int i = 0; | |
378 | for (auto &pin : pins) { | |
379 | EXPECT_EQ(pin->get_key() - base, res[i]); | |
380 | i++; | |
381 | } | |
382 | }); | |
383 | } | |
384 | TEST_F(object_data_handler_test_t, multiple_split) { | |
385 | run_async([this] { | |
386 | write(0, 128<<10, 'x'); | |
387 | ||
388 | auto t = create_mutate_transaction(); | |
389 | // normal split | |
390 | write(*t, 120<<10, 4<<10, 'a'); | |
391 | // not aligned right | |
392 | write(*t, 4<<10, 5<<10, 'b'); | |
393 | // split right extent of last split result | |
394 | write(*t, 32<<10, 4<<10, 'c'); | |
395 | // non aligned overwrite | |
396 | write(*t, 13<<10, 4<<10, 'd'); | |
397 | ||
398 | write(*t, 64<<10, 32<<10, 'e'); | |
399 | // not split right | |
400 | write(*t, 60<<10, 8<<10, 'f'); | |
401 | ||
402 | submit_transaction(std::move(t)); | |
403 | ||
404 | auto pins = get_mappings(0, 128<<10); | |
405 | EXPECT_EQ(pins.size(), 10); | |
406 | ||
407 | size_t res[10] = {0, 4<<10, 12<<10, 20<<10, 32<<10, | |
408 | 36<<10, 60<<10, 96<<10, 120<<10, 124<<10}; | |
409 | auto base = pins.front()->get_key(); | |
410 | int i = 0; | |
411 | for (auto &pin : pins) { | |
412 | EXPECT_EQ(pin->get_key() - base, res[i]); | |
413 | i++; | |
414 | } | |
415 | read(0, 128<<10); | |
416 | }); | |
417 | } |