]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | ||
7c673cae FG |
4 | #include "test/librbd/test_fixture.h" |
5 | #include "test/librbd/test_support.h" | |
6 | #include "cls/rbd/cls_rbd_types.h" | |
7 | #include "cls/journal/cls_journal_types.h" | |
8 | #include "cls/journal/cls_journal_client.h" | |
9 | #include "journal/Journaler.h" | |
10 | #include "librbd/ExclusiveLock.h" | |
11 | #include "librbd/ImageCtx.h" | |
12 | #include "librbd/ImageState.h" | |
13 | #include "librbd/ImageWatcher.h" | |
14 | #include "librbd/internal.h" | |
15 | #include "librbd/Journal.h" | |
16 | #include "librbd/Operations.h" | |
17 | #include "librbd/io/AioCompletion.h" | |
11fdf7f2 | 18 | #include "librbd/io/ImageDispatchSpec.h" |
7c673cae FG |
19 | #include "librbd/io/ImageRequest.h" |
20 | #include "librbd/io/ImageRequestWQ.h" | |
21 | #include "librbd/io/ReadResult.h" | |
22 | #include "librbd/journal/Types.h" | |
23 | ||
24 | void register_test_journal_replay() { | |
25 | } | |
26 | ||
27 | class TestJournalReplay : public TestFixture { | |
28 | public: | |
29 | ||
30 | int when_acquired_lock(librbd::ImageCtx *ictx) { | |
31 | C_SaferCond lock_ctx; | |
32 | { | |
33 | RWLock::WLocker owner_locker(ictx->owner_lock); | |
34 | ictx->exclusive_lock->acquire_lock(&lock_ctx); | |
35 | } | |
36 | int r = lock_ctx.wait(); | |
37 | if (r < 0) { | |
38 | return r; | |
39 | } | |
40 | ||
41 | C_SaferCond refresh_ctx; | |
42 | ictx->state->refresh(&refresh_ctx); | |
43 | return refresh_ctx.wait(); | |
44 | } | |
45 | ||
46 | template<typename T> | |
47 | void inject_into_journal(librbd::ImageCtx *ictx, T event) { | |
48 | C_SaferCond ctx; | |
49 | librbd::journal::EventEntry event_entry(event); | |
7c673cae FG |
50 | { |
51 | RWLock::RLocker owner_locker(ictx->owner_lock); | |
11fdf7f2 TL |
52 | uint64_t tid = ictx->journal->append_io_event(std::move(event_entry),0, 0, |
53 | true, 0); | |
7c673cae FG |
54 | ictx->journal->wait_event(tid, &ctx); |
55 | } | |
56 | ASSERT_EQ(0, ctx.wait()); | |
57 | } | |
58 | ||
59 | void get_journal_commit_position(librbd::ImageCtx *ictx, int64_t *tag, | |
60 | int64_t *entry) | |
61 | { | |
62 | const std::string client_id = ""; | |
63 | std::string journal_id = ictx->id; | |
64 | ||
65 | C_SaferCond close_cond; | |
66 | ictx->journal->close(&close_cond); | |
67 | ASSERT_EQ(0, close_cond.wait()); | |
68 | delete ictx->journal; | |
69 | ictx->journal = nullptr; | |
70 | ||
71 | C_SaferCond cond; | |
72 | uint64_t minimum_set; | |
73 | uint64_t active_set; | |
74 | std::set<cls::journal::Client> registered_clients; | |
75 | std::string oid = ::journal::Journaler::header_oid(journal_id); | |
76 | cls::journal::client::get_mutable_metadata(ictx->md_ctx, oid, &minimum_set, | |
77 | &active_set, ®istered_clients, &cond); | |
78 | ASSERT_EQ(0, cond.wait()); | |
79 | std::set<cls::journal::Client>::const_iterator c; | |
80 | for (c = registered_clients.begin(); c != registered_clients.end(); ++c) { | |
81 | if (c->id == client_id) { | |
82 | break; | |
83 | } | |
84 | } | |
85 | if (c == registered_clients.end() || | |
86 | c->commit_position.object_positions.empty()) { | |
87 | *tag = 0; | |
88 | *entry = -1; | |
89 | } else { | |
90 | const cls::journal::ObjectPosition &object_position = | |
91 | *c->commit_position.object_positions.begin(); | |
92 | *tag = object_position.tag_tid; | |
93 | *entry = object_position.entry_tid; | |
94 | } | |
95 | ||
96 | C_SaferCond open_cond; | |
97 | ictx->journal = new librbd::Journal<>(*ictx); | |
98 | ictx->journal->open(&open_cond); | |
99 | ASSERT_EQ(0, open_cond.wait()); | |
100 | } | |
101 | }; | |
102 | ||
103 | TEST_F(TestJournalReplay, AioDiscardEvent) { | |
104 | REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); | |
105 | ||
106 | // write to the image w/o using the journal | |
107 | librbd::ImageCtx *ictx; | |
108 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
109 | ictx->features &= ~RBD_FEATURE_JOURNALING; | |
110 | ||
111 | std::string payload(4096, '1'); | |
112 | bufferlist payload_bl; | |
113 | payload_bl.append(payload); | |
114 | auto aio_comp = new librbd::io::AioCompletion(); | |
115 | ictx->io_work_queue->aio_write(aio_comp, 0, payload.size(), | |
116 | std::move(payload_bl), 0); | |
117 | ASSERT_EQ(0, aio_comp->wait_for_complete()); | |
118 | aio_comp->release(); | |
119 | ||
120 | aio_comp = new librbd::io::AioCompletion(); | |
121 | ictx->io_work_queue->aio_flush(aio_comp); | |
122 | ASSERT_EQ(0, aio_comp->wait_for_complete()); | |
123 | aio_comp->release(); | |
124 | ||
125 | std::string read_payload(4096, '\0'); | |
126 | librbd::io::ReadResult read_result{&read_payload[0], read_payload.size()}; | |
127 | aio_comp = new librbd::io::AioCompletion(); | |
128 | ictx->io_work_queue->aio_read(aio_comp, 0, read_payload.size(), | |
129 | librbd::io::ReadResult{read_result}, 0); | |
130 | ASSERT_EQ(0, aio_comp->wait_for_complete()); | |
131 | aio_comp->release(); | |
132 | ASSERT_EQ(payload, read_payload); | |
133 | close_image(ictx); | |
134 | ||
135 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
136 | ASSERT_EQ(0, when_acquired_lock(ictx)); | |
137 | ||
138 | // get current commit position | |
139 | int64_t initial_tag; | |
140 | int64_t initial_entry; | |
141 | get_journal_commit_position(ictx, &initial_tag, &initial_entry); | |
142 | ||
143 | // inject a discard operation into the journal | |
144 | inject_into_journal(ictx, | |
11fdf7f2 TL |
145 | librbd::journal::AioDiscardEvent( |
146 | 0, payload.size(), ictx->discard_granularity_bytes)); | |
7c673cae FG |
147 | close_image(ictx); |
148 | ||
149 | // re-open the journal so that it replays the new entry | |
150 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
151 | ASSERT_EQ(0, when_acquired_lock(ictx)); | |
152 | ||
153 | aio_comp = new librbd::io::AioCompletion(); | |
154 | ictx->io_work_queue->aio_read(aio_comp, 0, read_payload.size(), | |
155 | librbd::io::ReadResult{read_result}, 0); | |
156 | ASSERT_EQ(0, aio_comp->wait_for_complete()); | |
157 | aio_comp->release(); | |
11fdf7f2 | 158 | if (ictx->discard_granularity_bytes > 0) { |
7c673cae FG |
159 | ASSERT_EQ(payload, read_payload); |
160 | } else { | |
161 | ASSERT_EQ(std::string(read_payload.size(), '\0'), read_payload); | |
162 | } | |
163 | ||
164 | // check the commit position is properly updated | |
165 | int64_t current_tag; | |
166 | int64_t current_entry; | |
167 | get_journal_commit_position(ictx, ¤t_tag, ¤t_entry); | |
168 | ASSERT_EQ(initial_tag + 1, current_tag); | |
169 | ASSERT_EQ(0, current_entry); | |
170 | ||
171 | // replay several envents and check the commit position | |
172 | inject_into_journal(ictx, | |
11fdf7f2 TL |
173 | librbd::journal::AioDiscardEvent( |
174 | 0, payload.size(), ictx->discard_granularity_bytes)); | |
7c673cae | 175 | inject_into_journal(ictx, |
11fdf7f2 TL |
176 | librbd::journal::AioDiscardEvent( |
177 | 0, payload.size(), ictx->discard_granularity_bytes)); | |
7c673cae FG |
178 | close_image(ictx); |
179 | ||
180 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
181 | ASSERT_EQ(0, when_acquired_lock(ictx)); | |
182 | get_journal_commit_position(ictx, ¤t_tag, ¤t_entry); | |
183 | ASSERT_EQ(initial_tag + 2, current_tag); | |
184 | ASSERT_EQ(1, current_entry); | |
185 | ||
186 | // verify lock ordering constraints | |
187 | aio_comp = new librbd::io::AioCompletion(); | |
181888fb | 188 | ictx->io_work_queue->aio_discard(aio_comp, 0, read_payload.size(), |
11fdf7f2 | 189 | ictx->discard_granularity_bytes); |
7c673cae FG |
190 | ASSERT_EQ(0, aio_comp->wait_for_complete()); |
191 | aio_comp->release(); | |
192 | } | |
193 | ||
194 | TEST_F(TestJournalReplay, AioWriteEvent) { | |
195 | REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); | |
196 | ||
197 | librbd::ImageCtx *ictx; | |
198 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
199 | ASSERT_EQ(0, when_acquired_lock(ictx)); | |
200 | ||
201 | // get current commit position | |
202 | int64_t initial_tag; | |
203 | int64_t initial_entry; | |
204 | get_journal_commit_position(ictx, &initial_tag, &initial_entry); | |
205 | ||
206 | // inject a write operation into the journal | |
207 | std::string payload(4096, '1'); | |
208 | bufferlist payload_bl; | |
209 | payload_bl.append(payload); | |
210 | inject_into_journal(ictx, | |
211 | librbd::journal::AioWriteEvent(0, payload.size(), payload_bl)); | |
212 | close_image(ictx); | |
213 | ||
214 | // re-open the journal so that it replays the new entry | |
215 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
216 | ASSERT_EQ(0, when_acquired_lock(ictx)); | |
217 | ||
218 | std::string read_payload(4096, '\0'); | |
219 | librbd::io::ReadResult read_result{&read_payload[0], read_payload.size()}; | |
220 | auto aio_comp = new librbd::io::AioCompletion(); | |
221 | ictx->io_work_queue->aio_read(aio_comp, 0, read_payload.size(), | |
222 | std::move(read_result), 0); | |
223 | ASSERT_EQ(0, aio_comp->wait_for_complete()); | |
224 | aio_comp->release(); | |
225 | ASSERT_EQ(payload, read_payload); | |
226 | ||
227 | // check the commit position is properly updated | |
228 | int64_t current_tag; | |
229 | int64_t current_entry; | |
230 | get_journal_commit_position(ictx, ¤t_tag, ¤t_entry); | |
231 | ASSERT_EQ(initial_tag + 1, current_tag); | |
232 | ASSERT_EQ(0, current_entry); | |
233 | ||
234 | // replay several events and check the commit position | |
235 | inject_into_journal(ictx, | |
236 | librbd::journal::AioWriteEvent(0, payload.size(), payload_bl)); | |
237 | inject_into_journal(ictx, | |
238 | librbd::journal::AioWriteEvent(0, payload.size(), payload_bl)); | |
239 | close_image(ictx); | |
240 | ||
241 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
242 | ASSERT_EQ(0, when_acquired_lock(ictx)); | |
243 | get_journal_commit_position(ictx, ¤t_tag, ¤t_entry); | |
244 | ASSERT_EQ(initial_tag + 2, current_tag); | |
245 | ASSERT_EQ(1, current_entry); | |
246 | ||
247 | // verify lock ordering constraints | |
248 | aio_comp = new librbd::io::AioCompletion(); | |
249 | ictx->io_work_queue->aio_write(aio_comp, 0, payload.size(), | |
250 | bufferlist{payload_bl}, 0); | |
251 | ASSERT_EQ(0, aio_comp->wait_for_complete()); | |
252 | aio_comp->release(); | |
253 | } | |
254 | ||
255 | TEST_F(TestJournalReplay, AioFlushEvent) { | |
256 | REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); | |
257 | ||
258 | librbd::ImageCtx *ictx; | |
259 | ||
260 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
261 | ASSERT_EQ(0, when_acquired_lock(ictx)); | |
262 | ||
263 | // get current commit position | |
264 | int64_t initial_tag; | |
265 | int64_t initial_entry; | |
266 | get_journal_commit_position(ictx, &initial_tag, &initial_entry); | |
267 | ||
268 | // inject a flush operation into the journal | |
269 | inject_into_journal(ictx, librbd::journal::AioFlushEvent()); | |
270 | close_image(ictx); | |
271 | ||
272 | // re-open the journal so that it replays the new entry | |
273 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
274 | ASSERT_EQ(0, when_acquired_lock(ictx)); | |
275 | ||
276 | // check the commit position is properly updated | |
277 | int64_t current_tag; | |
278 | int64_t current_entry; | |
279 | get_journal_commit_position(ictx, ¤t_tag, ¤t_entry); | |
280 | ASSERT_EQ(initial_tag + 1, current_tag); | |
281 | ASSERT_EQ(0, current_entry); | |
282 | ||
283 | // replay several events and check the commit position | |
284 | inject_into_journal(ictx, librbd::journal::AioFlushEvent()); | |
285 | inject_into_journal(ictx, librbd::journal::AioFlushEvent()); | |
286 | close_image(ictx); | |
287 | ||
288 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
289 | ASSERT_EQ(0, when_acquired_lock(ictx)); | |
290 | get_journal_commit_position(ictx, ¤t_tag, ¤t_entry); | |
291 | ASSERT_EQ(initial_tag + 2, current_tag); | |
292 | ASSERT_EQ(1, current_entry); | |
293 | ||
294 | // verify lock ordering constraints | |
295 | auto aio_comp = new librbd::io::AioCompletion(); | |
296 | ictx->io_work_queue->aio_flush(aio_comp); | |
297 | ASSERT_EQ(0, aio_comp->wait_for_complete()); | |
298 | aio_comp->release(); | |
299 | } | |
300 | ||
301 | TEST_F(TestJournalReplay, SnapCreate) { | |
302 | REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); | |
303 | ||
304 | librbd::ImageCtx *ictx; | |
305 | ||
306 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
307 | ASSERT_EQ(0, when_acquired_lock(ictx)); | |
308 | ||
309 | // get current commit position | |
310 | int64_t initial_tag; | |
311 | int64_t initial_entry; | |
312 | get_journal_commit_position(ictx, &initial_tag, &initial_entry); | |
313 | ||
314 | // inject snapshot ops into journal | |
315 | inject_into_journal(ictx, librbd::journal::SnapCreateEvent(1, cls::rbd::UserSnapshotNamespace(), | |
316 | "snap")); | |
317 | inject_into_journal(ictx, librbd::journal::OpFinishEvent(1, 0)); | |
318 | close_image(ictx); | |
319 | ||
320 | // replay journal | |
321 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
322 | ASSERT_EQ(0, when_acquired_lock(ictx)); | |
323 | ||
324 | int64_t current_tag; | |
325 | int64_t current_entry; | |
326 | get_journal_commit_position(ictx, ¤t_tag, ¤t_entry); | |
327 | ASSERT_EQ(initial_tag + 1, current_tag); | |
328 | ASSERT_EQ(1, current_entry); | |
329 | ||
330 | { | |
331 | RWLock::RLocker snap_locker(ictx->snap_lock); | |
332 | ASSERT_NE(CEPH_NOSNAP, ictx->get_snap_id(cls::rbd::UserSnapshotNamespace(), | |
333 | "snap")); | |
334 | } | |
335 | ||
336 | // verify lock ordering constraints | |
337 | ASSERT_EQ(0, ictx->operations->snap_create(cls::rbd::UserSnapshotNamespace(), | |
338 | "snap2")); | |
339 | } | |
340 | ||
341 | TEST_F(TestJournalReplay, SnapProtect) { | |
342 | REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); | |
343 | ||
344 | librbd::ImageCtx *ictx; | |
345 | ||
346 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
347 | ASSERT_EQ(0, when_acquired_lock(ictx)); | |
348 | ||
349 | ASSERT_EQ(0, ictx->operations->snap_create(cls::rbd::UserSnapshotNamespace(), | |
350 | "snap")); | |
351 | ||
352 | // get current commit position | |
353 | int64_t initial_tag; | |
354 | int64_t initial_entry; | |
355 | get_journal_commit_position(ictx, &initial_tag, &initial_entry); | |
356 | ||
357 | // inject snapshot ops into journal | |
358 | inject_into_journal(ictx, | |
359 | librbd::journal::SnapProtectEvent(1, | |
360 | cls::rbd::UserSnapshotNamespace(), | |
361 | "snap")); | |
362 | inject_into_journal(ictx, librbd::journal::OpFinishEvent(1, 0)); | |
363 | close_image(ictx); | |
364 | ||
365 | // replay journal | |
366 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
367 | ASSERT_EQ(0, when_acquired_lock(ictx)); | |
368 | ||
369 | int64_t current_tag; | |
370 | int64_t current_entry; | |
371 | get_journal_commit_position(ictx, ¤t_tag, ¤t_entry); | |
372 | ASSERT_EQ(initial_tag, current_tag); | |
373 | ASSERT_EQ(initial_entry + 2, current_entry); | |
374 | ||
375 | bool is_protected; | |
376 | ASSERT_EQ(0, librbd::snap_is_protected(ictx, "snap", &is_protected)); | |
377 | ASSERT_TRUE(is_protected); | |
378 | ||
379 | // verify lock ordering constraints | |
380 | ASSERT_EQ(0, ictx->operations->snap_create(cls::rbd::UserSnapshotNamespace(), | |
381 | "snap2")); | |
382 | ASSERT_EQ(0, ictx->operations->snap_protect(cls::rbd::UserSnapshotNamespace(), | |
383 | "snap2")); | |
384 | } | |
385 | ||
386 | TEST_F(TestJournalReplay, SnapUnprotect) { | |
387 | REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); | |
388 | ||
389 | librbd::ImageCtx *ictx; | |
390 | ||
391 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
392 | ASSERT_EQ(0, when_acquired_lock(ictx)); | |
393 | ||
394 | ASSERT_EQ(0, ictx->operations->snap_create(cls::rbd::UserSnapshotNamespace(), | |
395 | "snap")); | |
396 | uint64_t snap_id; | |
397 | { | |
398 | RWLock::RLocker snap_locker(ictx->snap_lock); | |
399 | snap_id = ictx->get_snap_id(cls::rbd::UserSnapshotNamespace(), "snap"); | |
400 | ASSERT_NE(CEPH_NOSNAP, snap_id); | |
401 | } | |
402 | ASSERT_EQ(0, ictx->operations->snap_protect(cls::rbd::UserSnapshotNamespace(), | |
403 | "snap")); | |
404 | ||
405 | // get current commit position | |
406 | int64_t initial_tag; | |
407 | int64_t initial_entry; | |
408 | get_journal_commit_position(ictx, &initial_tag, &initial_entry); | |
409 | ||
410 | // inject snapshot ops into journal | |
411 | inject_into_journal(ictx, | |
412 | librbd::journal::SnapUnprotectEvent(1, | |
413 | cls::rbd::UserSnapshotNamespace(), | |
414 | "snap")); | |
415 | inject_into_journal(ictx, librbd::journal::OpFinishEvent(1, 0)); | |
416 | close_image(ictx); | |
417 | ||
418 | // replay journal | |
419 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
420 | ASSERT_EQ(0, when_acquired_lock(ictx)); | |
421 | ||
422 | int64_t current_tag; | |
423 | int64_t current_entry; | |
424 | get_journal_commit_position(ictx, ¤t_tag, ¤t_entry); | |
425 | ASSERT_EQ(initial_tag, current_tag); | |
426 | ASSERT_EQ(initial_entry + 2, current_entry); | |
427 | ||
428 | bool is_protected; | |
429 | ASSERT_EQ(0, librbd::snap_is_protected(ictx, "snap", &is_protected)); | |
430 | ASSERT_FALSE(is_protected); | |
431 | ||
432 | // verify lock ordering constraints | |
433 | ASSERT_EQ(0, ictx->operations->snap_create(cls::rbd::UserSnapshotNamespace(), | |
434 | "snap2")); | |
435 | ASSERT_EQ(0, ictx->operations->snap_protect(cls::rbd::UserSnapshotNamespace(), | |
436 | "snap2")); | |
437 | ASSERT_EQ(0, ictx->operations->snap_unprotect(cls::rbd::UserSnapshotNamespace(), | |
438 | "snap2")); | |
439 | } | |
440 | ||
441 | TEST_F(TestJournalReplay, SnapRename) { | |
442 | REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); | |
443 | ||
444 | librbd::ImageCtx *ictx; | |
445 | ||
446 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
447 | ASSERT_EQ(0, when_acquired_lock(ictx)); | |
448 | ||
449 | ASSERT_EQ(0, ictx->operations->snap_create(cls::rbd::UserSnapshotNamespace(), | |
450 | "snap")); | |
451 | uint64_t snap_id; | |
452 | { | |
453 | RWLock::RLocker snap_locker(ictx->snap_lock); | |
454 | snap_id = ictx->get_snap_id(cls::rbd::UserSnapshotNamespace(), "snap"); | |
455 | ASSERT_NE(CEPH_NOSNAP, snap_id); | |
456 | } | |
457 | ||
458 | // get current commit position | |
459 | int64_t initial_tag; | |
460 | int64_t initial_entry; | |
461 | get_journal_commit_position(ictx, &initial_tag, &initial_entry); | |
462 | ||
463 | // inject snapshot ops into journal | |
464 | inject_into_journal(ictx, librbd::journal::SnapRenameEvent(1, snap_id, "snap", | |
465 | "snap2")); | |
466 | inject_into_journal(ictx, librbd::journal::OpFinishEvent(1, 0)); | |
467 | close_image(ictx); | |
468 | ||
469 | // replay journal | |
470 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
471 | ASSERT_EQ(0, when_acquired_lock(ictx)); | |
472 | ||
473 | int64_t current_tag; | |
474 | int64_t current_entry; | |
475 | get_journal_commit_position(ictx, ¤t_tag, ¤t_entry); | |
476 | ASSERT_EQ(initial_tag, current_tag); | |
477 | ASSERT_EQ(initial_entry + 2, current_entry); | |
478 | ASSERT_EQ(0, ictx->state->refresh()); | |
479 | ||
480 | { | |
481 | RWLock::RLocker snap_locker(ictx->snap_lock); | |
482 | snap_id = ictx->get_snap_id(cls::rbd::UserSnapshotNamespace(), "snap2"); | |
483 | ASSERT_NE(CEPH_NOSNAP, snap_id); | |
484 | } | |
485 | ||
486 | // verify lock ordering constraints | |
487 | ASSERT_EQ(0, ictx->operations->snap_rename("snap2", "snap3")); | |
488 | } | |
489 | ||
490 | TEST_F(TestJournalReplay, SnapRollback) { | |
491 | REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); | |
492 | ||
493 | librbd::ImageCtx *ictx; | |
494 | ||
495 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
496 | ASSERT_EQ(0, when_acquired_lock(ictx)); | |
497 | ||
498 | ASSERT_EQ(0, ictx->operations->snap_create(cls::rbd::UserSnapshotNamespace(), | |
499 | "snap")); | |
500 | ||
501 | // get current commit position | |
502 | int64_t initial_tag; | |
503 | int64_t initial_entry; | |
504 | get_journal_commit_position(ictx, &initial_tag, &initial_entry); | |
505 | ||
506 | // inject snapshot ops into journal | |
507 | inject_into_journal(ictx, | |
508 | librbd::journal::SnapRollbackEvent(1, | |
509 | cls::rbd::UserSnapshotNamespace(), | |
510 | "snap")); | |
511 | inject_into_journal(ictx, librbd::journal::OpFinishEvent(1, 0)); | |
512 | close_image(ictx); | |
513 | ||
514 | // replay journal | |
515 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
516 | ASSERT_EQ(0, when_acquired_lock(ictx)); | |
517 | ||
518 | int64_t current_tag; | |
519 | int64_t current_entry; | |
520 | get_journal_commit_position(ictx, ¤t_tag, ¤t_entry); | |
521 | ASSERT_EQ(initial_tag, current_tag); | |
522 | ASSERT_EQ(initial_entry + 2, current_entry); | |
523 | ||
524 | // verify lock ordering constraints | |
525 | librbd::NoOpProgressContext no_op_progress; | |
526 | ASSERT_EQ(0, ictx->operations->snap_rollback(cls::rbd::UserSnapshotNamespace(), | |
527 | "snap", | |
528 | no_op_progress)); | |
529 | } | |
530 | ||
531 | TEST_F(TestJournalReplay, SnapRemove) { | |
532 | REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); | |
533 | ||
534 | librbd::ImageCtx *ictx; | |
535 | ||
536 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
537 | ASSERT_EQ(0, when_acquired_lock(ictx)); | |
538 | ||
539 | ASSERT_EQ(0, ictx->operations->snap_create(cls::rbd::UserSnapshotNamespace(), | |
540 | "snap")); | |
541 | ||
542 | // get current commit position | |
543 | int64_t initial_tag; | |
544 | int64_t initial_entry; | |
545 | get_journal_commit_position(ictx, &initial_tag, &initial_entry); | |
546 | ||
547 | // inject snapshot ops into journal | |
548 | inject_into_journal(ictx, | |
549 | librbd::journal::SnapRemoveEvent(1, | |
550 | cls::rbd::UserSnapshotNamespace(), | |
551 | "snap")); | |
552 | inject_into_journal(ictx, librbd::journal::OpFinishEvent(1, 0)); | |
553 | close_image(ictx); | |
554 | ||
555 | // replay journal | |
556 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
557 | ASSERT_EQ(0, when_acquired_lock(ictx)); | |
558 | ||
559 | int64_t current_tag; | |
560 | int64_t current_entry; | |
561 | get_journal_commit_position(ictx, ¤t_tag, ¤t_entry); | |
562 | ASSERT_EQ(initial_tag, current_tag); | |
563 | ASSERT_EQ(initial_entry + 2, current_entry); | |
564 | ||
565 | { | |
566 | RWLock::RLocker snap_locker(ictx->snap_lock); | |
567 | uint64_t snap_id = ictx->get_snap_id(cls::rbd::UserSnapshotNamespace(), | |
568 | "snap"); | |
569 | ASSERT_EQ(CEPH_NOSNAP, snap_id); | |
570 | } | |
571 | ||
572 | // verify lock ordering constraints | |
573 | ASSERT_EQ(0, ictx->operations->snap_create(cls::rbd::UserSnapshotNamespace(), | |
574 | "snap")); | |
575 | ASSERT_EQ(0, ictx->operations->snap_remove(cls::rbd::UserSnapshotNamespace(), | |
576 | "snap")); | |
577 | } | |
578 | ||
579 | TEST_F(TestJournalReplay, Rename) { | |
580 | REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); | |
581 | ||
582 | librbd::ImageCtx *ictx; | |
583 | ||
584 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
585 | ASSERT_EQ(0, when_acquired_lock(ictx)); | |
586 | ||
587 | // get current commit position | |
588 | int64_t initial_tag; | |
589 | int64_t initial_entry; | |
590 | get_journal_commit_position(ictx, &initial_tag, &initial_entry); | |
591 | ||
592 | // inject snapshot ops into journal | |
593 | std::string new_image_name(get_temp_image_name()); | |
594 | inject_into_journal(ictx, librbd::journal::RenameEvent(1, new_image_name)); | |
595 | inject_into_journal(ictx, librbd::journal::OpFinishEvent(1, 0)); | |
596 | close_image(ictx); | |
597 | ||
598 | // replay journal | |
599 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
600 | ASSERT_EQ(0, when_acquired_lock(ictx)); | |
601 | ||
602 | int64_t current_tag; | |
603 | int64_t current_entry; | |
604 | get_journal_commit_position(ictx, ¤t_tag, ¤t_entry); | |
605 | ASSERT_EQ(initial_tag + 1, current_tag); | |
606 | ASSERT_EQ(1, current_entry); | |
607 | ||
608 | // verify lock ordering constraints | |
609 | librbd::RBD rbd; | |
610 | ASSERT_EQ(0, rbd.rename(m_ioctx, new_image_name.c_str(), m_image_name.c_str())); | |
611 | } | |
612 | ||
613 | TEST_F(TestJournalReplay, Resize) { | |
614 | REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); | |
615 | ||
616 | librbd::ImageCtx *ictx; | |
617 | ||
618 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
619 | ASSERT_EQ(0, when_acquired_lock(ictx)); | |
620 | ||
621 | // get current commit position | |
622 | int64_t initial_tag; | |
623 | int64_t initial_entry; | |
624 | get_journal_commit_position(ictx, &initial_tag, &initial_entry); | |
625 | ||
626 | // inject snapshot ops into journal | |
627 | inject_into_journal(ictx, librbd::journal::ResizeEvent(1, 16)); | |
628 | inject_into_journal(ictx, librbd::journal::OpFinishEvent(1, 0)); | |
629 | close_image(ictx); | |
630 | ||
631 | // replay journal | |
632 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
633 | ASSERT_EQ(0, when_acquired_lock(ictx)); | |
634 | ||
635 | int64_t current_tag; | |
636 | int64_t current_entry; | |
637 | get_journal_commit_position(ictx, ¤t_tag, ¤t_entry); | |
638 | ASSERT_EQ(initial_tag + 1, current_tag); | |
639 | ASSERT_EQ(1, current_entry); | |
640 | ||
641 | // verify lock ordering constraints | |
642 | librbd::NoOpProgressContext no_op_progress; | |
643 | ASSERT_EQ(0, ictx->operations->resize(0, true, no_op_progress)); | |
644 | } | |
645 | ||
646 | TEST_F(TestJournalReplay, Flatten) { | |
647 | REQUIRE_FEATURE(RBD_FEATURE_LAYERING | RBD_FEATURE_JOURNALING); | |
648 | ||
649 | librbd::ImageCtx *ictx; | |
650 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
651 | ASSERT_EQ(0, ictx->operations->snap_create(cls::rbd::UserSnapshotNamespace(), | |
652 | "snap")); | |
653 | ASSERT_EQ(0, ictx->operations->snap_protect(cls::rbd::UserSnapshotNamespace(), | |
654 | "snap")); | |
655 | ||
656 | std::string clone_name = get_temp_image_name(); | |
657 | int order = ictx->order; | |
658 | ASSERT_EQ(0, librbd::clone(m_ioctx, m_image_name.c_str(), "snap", m_ioctx, | |
659 | clone_name.c_str(), ictx->features, &order, 0, 0)); | |
660 | ||
661 | librbd::ImageCtx *ictx2; | |
662 | ASSERT_EQ(0, open_image(clone_name, &ictx2)); | |
663 | ASSERT_EQ(0, when_acquired_lock(ictx2)); | |
664 | ||
665 | // get current commit position | |
666 | int64_t initial_tag; | |
667 | int64_t initial_entry; | |
668 | get_journal_commit_position(ictx2, &initial_tag, &initial_entry); | |
669 | ||
670 | // inject snapshot ops into journal | |
671 | inject_into_journal(ictx2, librbd::journal::FlattenEvent(1)); | |
672 | inject_into_journal(ictx2, librbd::journal::OpFinishEvent(1, 0)); | |
673 | close_image(ictx2); | |
674 | ||
675 | // replay journal | |
676 | ASSERT_EQ(0, open_image(clone_name, &ictx2)); | |
677 | ASSERT_EQ(0, when_acquired_lock(ictx2)); | |
678 | ||
679 | int64_t current_tag; | |
680 | int64_t current_entry; | |
681 | get_journal_commit_position(ictx2, ¤t_tag, ¤t_entry); | |
682 | ASSERT_EQ(initial_tag + 1, current_tag); | |
683 | ASSERT_EQ(1, current_entry); | |
684 | ASSERT_EQ(0, ictx->operations->snap_unprotect(cls::rbd::UserSnapshotNamespace(), | |
685 | "snap")); | |
686 | ||
687 | // verify lock ordering constraints | |
688 | librbd::NoOpProgressContext no_op; | |
689 | ASSERT_EQ(-EINVAL, ictx2->operations->flatten(no_op)); | |
690 | } | |
691 | ||
692 | TEST_F(TestJournalReplay, UpdateFeatures) { | |
693 | REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); | |
694 | ||
695 | librbd::ImageCtx *ictx; | |
696 | ||
697 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
698 | ASSERT_EQ(0, when_acquired_lock(ictx)); | |
699 | ||
700 | uint64_t features = RBD_FEATURE_OBJECT_MAP | RBD_FEATURE_FAST_DIFF; | |
701 | bool enabled = !ictx->test_features(features); | |
702 | ||
703 | // get current commit position | |
704 | int64_t initial_tag; | |
705 | int64_t initial_entry; | |
706 | get_journal_commit_position(ictx, &initial_tag, &initial_entry); | |
707 | ||
708 | // inject update_features op into journal | |
709 | inject_into_journal(ictx, librbd::journal::UpdateFeaturesEvent(1, features, | |
710 | enabled)); | |
711 | close_image(ictx); | |
712 | ||
713 | // replay journal | |
714 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
715 | ASSERT_EQ(0, when_acquired_lock(ictx)); | |
716 | ||
717 | int64_t current_tag; | |
718 | int64_t current_entry; | |
719 | get_journal_commit_position(ictx, ¤t_tag, ¤t_entry); | |
720 | ASSERT_EQ(initial_tag + 1, current_tag); | |
721 | ASSERT_EQ(0, current_entry); | |
722 | ||
723 | ASSERT_EQ(enabled, ictx->test_features(features)); | |
724 | ||
725 | // verify lock ordering constraints | |
726 | ASSERT_EQ(0, ictx->operations->update_features(features, !enabled)); | |
727 | } | |
728 | ||
729 | TEST_F(TestJournalReplay, MetadataSet) { | |
730 | REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); | |
731 | ||
732 | librbd::ImageCtx *ictx; | |
733 | ||
734 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
735 | ASSERT_EQ(0, when_acquired_lock(ictx)); | |
736 | ||
737 | // get current commit position | |
738 | int64_t initial_tag; | |
739 | int64_t initial_entry; | |
740 | get_journal_commit_position(ictx, &initial_tag, &initial_entry); | |
741 | ||
742 | // inject metadata_set op into journal | |
b32b8144 FG |
743 | inject_into_journal(ictx, librbd::journal::MetadataSetEvent( |
744 | 1, "conf_rbd_mirroring_replay_delay", "9876")); | |
7c673cae FG |
745 | inject_into_journal(ictx, librbd::journal::OpFinishEvent(1, 0)); |
746 | close_image(ictx); | |
747 | ||
748 | // replay journal | |
749 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
750 | ASSERT_EQ(0, when_acquired_lock(ictx)); | |
751 | ||
752 | int64_t current_tag; | |
753 | int64_t current_entry; | |
754 | get_journal_commit_position(ictx, ¤t_tag, ¤t_entry); | |
755 | ASSERT_EQ(initial_tag + 1, current_tag); | |
756 | ASSERT_EQ(1, current_entry); | |
757 | ||
11fdf7f2 | 758 | ASSERT_EQ(9876U, ictx->mirroring_replay_delay); |
b32b8144 | 759 | |
7c673cae | 760 | std::string value; |
b32b8144 FG |
761 | ASSERT_EQ(0, librbd::metadata_get(ictx, "conf_rbd_mirroring_replay_delay", |
762 | &value)); | |
763 | ASSERT_EQ("9876", value); | |
7c673cae FG |
764 | |
765 | // verify lock ordering constraints | |
766 | ASSERT_EQ(0, ictx->operations->metadata_set("key2", "value")); | |
767 | } | |
768 | ||
769 | TEST_F(TestJournalReplay, MetadataRemove) { | |
770 | REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); | |
771 | ||
772 | librbd::ImageCtx *ictx; | |
773 | ||
774 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
775 | ASSERT_EQ(0, when_acquired_lock(ictx)); | |
776 | ||
b32b8144 FG |
777 | ASSERT_EQ(0, ictx->operations->metadata_set( |
778 | "conf_rbd_mirroring_replay_delay", "9876")); | |
7c673cae FG |
779 | |
780 | // get current commit position | |
781 | int64_t initial_tag; | |
782 | int64_t initial_entry; | |
783 | get_journal_commit_position(ictx, &initial_tag, &initial_entry); | |
784 | ||
785 | // inject metadata_remove op into journal | |
b32b8144 FG |
786 | inject_into_journal(ictx, librbd::journal::MetadataRemoveEvent( |
787 | 1, "conf_rbd_mirroring_replay_delay")); | |
7c673cae FG |
788 | inject_into_journal(ictx, librbd::journal::OpFinishEvent(1, 0)); |
789 | close_image(ictx); | |
790 | ||
791 | // replay journal | |
792 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
793 | ASSERT_EQ(0, when_acquired_lock(ictx)); | |
794 | ||
795 | int64_t current_tag; | |
796 | int64_t current_entry; | |
797 | get_journal_commit_position(ictx, ¤t_tag, ¤t_entry); | |
798 | ASSERT_EQ(initial_tag, current_tag); | |
799 | ASSERT_EQ(initial_entry + 2, current_entry); | |
11fdf7f2 | 800 | ASSERT_EQ(0U, ictx->mirroring_replay_delay); |
7c673cae FG |
801 | |
802 | std::string value; | |
b32b8144 FG |
803 | ASSERT_EQ(-ENOENT, |
804 | librbd::metadata_get(ictx, "conf_rbd_mirroring_replay_delay", | |
805 | &value)); | |
7c673cae FG |
806 | |
807 | // verify lock ordering constraints | |
808 | ASSERT_EQ(0, ictx->operations->metadata_set("key", "value")); | |
809 | ASSERT_EQ(0, ictx->operations->metadata_remove("key")); | |
810 | } | |
811 | ||
812 | TEST_F(TestJournalReplay, ObjectPosition) { | |
813 | REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); | |
814 | ||
815 | librbd::ImageCtx *ictx; | |
816 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
817 | ASSERT_EQ(0, when_acquired_lock(ictx)); | |
818 | ||
819 | // get current commit position | |
820 | int64_t initial_tag; | |
821 | int64_t initial_entry; | |
822 | get_journal_commit_position(ictx, &initial_tag, &initial_entry); | |
823 | ||
824 | std::string payload(4096, '1'); | |
825 | bufferlist payload_bl; | |
826 | payload_bl.append(payload); | |
827 | auto aio_comp = new librbd::io::AioCompletion(); | |
828 | ictx->io_work_queue->aio_write(aio_comp, 0, payload.size(), | |
829 | bufferlist{payload_bl}, 0); | |
830 | ASSERT_EQ(0, aio_comp->wait_for_complete()); | |
831 | aio_comp->release(); | |
832 | ||
833 | aio_comp = new librbd::io::AioCompletion(); | |
834 | ictx->io_work_queue->aio_flush(aio_comp); | |
835 | ASSERT_EQ(0, aio_comp->wait_for_complete()); | |
836 | aio_comp->release(); | |
837 | ||
838 | // check the commit position updated | |
839 | int64_t current_tag; | |
840 | int64_t current_entry; | |
841 | get_journal_commit_position(ictx, ¤t_tag, ¤t_entry); | |
842 | ASSERT_EQ(initial_tag + 1, current_tag); | |
843 | ASSERT_EQ(1, current_entry); | |
844 | ||
845 | // write again | |
846 | ||
847 | aio_comp = new librbd::io::AioCompletion(); | |
848 | ictx->io_work_queue->aio_write(aio_comp, 0, payload.size(), | |
849 | bufferlist{payload_bl}, 0); | |
850 | ASSERT_EQ(0, aio_comp->wait_for_complete()); | |
851 | aio_comp->release(); | |
852 | ||
853 | aio_comp = new librbd::io::AioCompletion(); | |
854 | ictx->io_work_queue->aio_flush(aio_comp); | |
855 | ASSERT_EQ(0, aio_comp->wait_for_complete()); | |
856 | aio_comp->release(); | |
857 | ||
11fdf7f2 TL |
858 | // user flush requests are ignored when journaling + cache are enabled |
859 | C_SaferCond flush_ctx; | |
860 | aio_comp = librbd::io::AioCompletion::create( | |
861 | &flush_ctx, ictx, librbd::io::AIO_TYPE_FLUSH); | |
862 | auto req = librbd::io::ImageDispatchSpec<>::create_flush_request( | |
863 | *ictx, aio_comp, librbd::io::FLUSH_SOURCE_INTERNAL, {}); | |
864 | req->send(); | |
865 | delete req; | |
866 | ASSERT_EQ(0, flush_ctx.wait()); | |
7c673cae FG |
867 | |
868 | // check the commit position updated | |
869 | get_journal_commit_position(ictx, ¤t_tag, ¤t_entry); | |
870 | ASSERT_EQ(initial_tag + 1, current_tag); | |
871 | ASSERT_EQ(3, current_entry); | |
872 | } |