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