]> git.proxmox.com Git - ceph.git/blame - ceph/src/test/osd/RadosModel.h
import quincy beta 17.1.0
[ceph.git] / ceph / src / test / osd / RadosModel.h
CommitLineData
7c673cae
FG
1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2// vim: ts=8 sw=2 smarttab
3#include "include/int_types.h"
4
9f95a23c 5#include "common/ceph_mutex.h"
7c673cae
FG
6#include "include/rados/librados.hpp"
7
8#include <iostream>
f67539c2 9#include <iterator>
7c673cae
FG
10#include <sstream>
11#include <map>
12#include <set>
13#include <list>
14#include <string>
15#include <string.h>
16#include <stdlib.h>
17#include <errno.h>
18#include <time.h>
19#include "Object.h"
20#include "TestOpStat.h"
21#include "test/librados/test.h"
7c673cae
FG
22#include "common/sharedptr_registry.hpp"
23#include "common/errno.h"
24#include "osd/HitSet.h"
20effc67
TL
25#include "common/ceph_crypto.h"
26
27#include "cls/cas/cls_cas_client.h"
28#include "cls/cas/cls_cas_internal.h"
7c673cae
FG
29
30#ifndef RADOSMODEL_H
31#define RADOSMODEL_H
32
33using namespace std;
34
35class RadosTestContext;
36class TestOpStat;
37
38template <typename T>
39typename T::iterator rand_choose(T &cont) {
f67539c2
TL
40 if (std::empty(cont)) {
41 return std::end(cont);
7c673cae 42 }
f67539c2 43 return std::next(std::begin(cont), rand() % cont.size());
7c673cae
FG
44}
45
46enum TestOpType {
47 TEST_OP_READ,
48 TEST_OP_WRITE,
49 TEST_OP_WRITE_EXCL,
50 TEST_OP_WRITESAME,
51 TEST_OP_DELETE,
52 TEST_OP_SNAP_CREATE,
53 TEST_OP_SNAP_REMOVE,
54 TEST_OP_ROLLBACK,
55 TEST_OP_SETATTR,
56 TEST_OP_RMATTR,
57 TEST_OP_WATCH,
58 TEST_OP_COPY_FROM,
59 TEST_OP_HIT_SET_LIST,
60 TEST_OP_UNDIRTY,
61 TEST_OP_IS_DIRTY,
62 TEST_OP_CACHE_FLUSH,
63 TEST_OP_CACHE_TRY_FLUSH,
64 TEST_OP_CACHE_EVICT,
65 TEST_OP_APPEND,
31f18b77
FG
66 TEST_OP_APPEND_EXCL,
67 TEST_OP_SET_REDIRECT,
11fdf7f2
TL
68 TEST_OP_UNSET_REDIRECT,
69 TEST_OP_CHUNK_READ,
9f95a23c 70 TEST_OP_TIER_PROMOTE,
20effc67
TL
71 TEST_OP_TIER_FLUSH,
72 TEST_OP_SET_CHUNK,
73 TEST_OP_TIER_EVICT
7c673cae
FG
74};
75
76class TestWatchContext : public librados::WatchCtx2 {
77 TestWatchContext(const TestWatchContext&);
78public:
9f95a23c
TL
79 ceph::condition_variable cond;
80 uint64_t handle = 0;
81 bool waiting = false;
82 ceph::mutex lock = ceph::make_mutex("watch lock");
83 TestWatchContext() = default;
7c673cae
FG
84 void handle_notify(uint64_t notify_id, uint64_t cookie,
85 uint64_t notifier_id,
86 bufferlist &bl) override {
9f95a23c 87 std::lock_guard l{lock};
7c673cae 88 waiting = false;
9f95a23c 89 cond.notify_all();
7c673cae
FG
90 }
91 void handle_error(uint64_t cookie, int err) override {
9f95a23c 92 std::lock_guard l{lock};
7c673cae
FG
93 cout << "watch handle_error " << err << std::endl;
94 }
95 void start() {
9f95a23c 96 std::lock_guard l{lock};
7c673cae
FG
97 waiting = true;
98 }
99 void wait() {
9f95a23c
TL
100 std::unique_lock l{lock};
101 cond.wait(l, [this] { return !waiting; });
7c673cae
FG
102 }
103 uint64_t &get_handle() {
104 return handle;
105 }
106};
107
108class TestOp {
109public:
f67539c2 110 const int num;
7c673cae
FG
111 RadosTestContext *context;
112 TestOpStat *stat;
f67539c2 113 bool done = false;
7c673cae
FG
114 TestOp(int n, RadosTestContext *context,
115 TestOpStat *stat = 0)
116 : num(n),
117 context(context),
f67539c2 118 stat(stat)
7c673cae
FG
119 {}
120
121 virtual ~TestOp() {};
122
123 /**
124 * This struct holds data to be passed by a callback
125 * to a TestOp::finish method.
126 */
127 struct CallbackInfo {
128 uint64_t id;
129 explicit CallbackInfo(uint64_t id) : id(id) {}
130 virtual ~CallbackInfo() {};
131 };
132
133 virtual void _begin() = 0;
134
135 /**
136 * Called when the operation completes.
137 * This should be overridden by asynchronous operations.
138 *
139 * @param info information stored by a callback, or NULL -
140 * useful for multi-operation TestOps
141 */
142 virtual void _finish(CallbackInfo *info)
143 {
144 return;
145 }
146 virtual string getType() = 0;
147 virtual bool finished()
148 {
149 return true;
150 }
151
152 void begin();
153 void finish(CallbackInfo *info);
154 virtual bool must_quiesce_other_ops() { return false; }
155};
156
157class TestOpGenerator {
158public:
159 virtual ~TestOpGenerator() {};
160 virtual TestOp *next(RadosTestContext &context) = 0;
161};
162
163class RadosTestContext {
164public:
9f95a23c
TL
165 ceph::mutex state_lock = ceph::make_mutex("Context Lock");
166 ceph::condition_variable wait_cond;
f67539c2 167 // snap => {oid => desc}
7c673cae
FG
168 map<int, map<string,ObjectDesc> > pool_obj_cont;
169 set<string> oid_in_use;
170 set<string> oid_not_in_use;
171 set<string> oid_flushing;
172 set<string> oid_not_flushing;
31f18b77
FG
173 set<string> oid_redirect_not_in_use;
174 set<string> oid_redirect_in_use;
20effc67 175 set<string> oid_set_chunk_tgt_pool;
7c673cae
FG
176 SharedPtrRegistry<int, int> snaps_in_use;
177 int current_snap;
178 string pool_name;
179 librados::IoCtx io_ctx;
180 librados::Rados rados;
181 int next_oid;
182 string prefix;
183 int errors;
184 int max_in_flight;
185 int seq_num;
186 map<int,uint64_t> snaps;
187 uint64_t seq;
188 const char *rados_id;
189 bool initialized;
190 map<string, TestWatchContext*> watches;
191 const uint64_t max_size;
192 const uint64_t min_stride_size;
193 const uint64_t max_stride_size;
194 AttrGenerator attr_gen;
195 const bool no_omap;
196 const bool no_sparse;
197 bool pool_snaps;
198 bool write_fadvise_dontneed;
11fdf7f2
TL
199 string low_tier_pool_name;
200 librados::IoCtx low_tier_io_ctx;
7c673cae 201 int snapname_num;
31f18b77 202 map<string,string > redirect_objs;
9f95a23c 203 bool enable_dedup;
20effc67
TL
204 string chunk_algo;
205 string chunk_size;
7c673cae
FG
206
207 RadosTestContext(const string &pool_name,
208 int max_in_flight,
209 uint64_t max_size,
210 uint64_t min_stride_size,
211 uint64_t max_stride_size,
212 bool no_omap,
213 bool no_sparse,
214 bool pool_snaps,
215 bool write_fadvise_dontneed,
11fdf7f2 216 const string &low_tier_pool_name,
9f95a23c 217 bool enable_dedup,
20effc67
TL
218 string chunk_algo,
219 string chunk_size,
7c673cae 220 const char *id = 0) :
7c673cae
FG
221 pool_obj_cont(),
222 current_snap(0),
223 pool_name(pool_name),
224 next_oid(0),
225 errors(0),
226 max_in_flight(max_in_flight),
227 seq_num(0), seq(0),
228 rados_id(id), initialized(false),
229 max_size(max_size),
230 min_stride_size(min_stride_size), max_stride_size(max_stride_size),
231 attr_gen(2000, 20000),
232 no_omap(no_omap),
233 no_sparse(no_sparse),
234 pool_snaps(pool_snaps),
235 write_fadvise_dontneed(write_fadvise_dontneed),
11fdf7f2 236 low_tier_pool_name(low_tier_pool_name),
9f95a23c 237 snapname_num(0),
20effc67
TL
238 enable_dedup(enable_dedup),
239 chunk_algo(chunk_algo),
240 chunk_size(chunk_size)
7c673cae
FG
241 {
242 }
243
244 int init()
245 {
246 int r = rados.init(rados_id);
247 if (r < 0)
248 return r;
249 r = rados.conf_read_file(NULL);
250 if (r < 0)
251 return r;
252 r = rados.conf_parse_env(NULL);
253 if (r < 0)
254 return r;
255 r = rados.connect();
256 if (r < 0)
257 return r;
258 r = rados.ioctx_create(pool_name.c_str(), io_ctx);
259 if (r < 0) {
260 rados.shutdown();
261 return r;
262 }
11fdf7f2
TL
263 if (!low_tier_pool_name.empty()) {
264 r = rados.ioctx_create(low_tier_pool_name.c_str(), low_tier_io_ctx);
265 if (r < 0) {
266 rados.shutdown();
267 return r;
268 }
269 }
7c673cae
FG
270 bufferlist inbl;
271 r = rados.mon_command(
272 "{\"prefix\": \"osd pool set\", \"pool\": \"" + pool_name +
273 "\", \"var\": \"write_fadvise_dontneed\", \"val\": \"" + (write_fadvise_dontneed ? "true" : "false") + "\"}",
274 inbl, NULL, NULL);
275 if (r < 0) {
276 rados.shutdown();
277 return r;
278 }
9f95a23c
TL
279 if (enable_dedup) {
280 r = rados.mon_command(
281 "{\"prefix\": \"osd pool set\", \"pool\": \"" + pool_name +
282 "\", \"var\": \"fingerprint_algorithm\", \"val\": \"" + "sha256" + "\"}",
283 inbl, NULL, NULL);
284 if (r < 0) {
285 rados.shutdown();
286 return r;
287 }
20effc67
TL
288 r = rados.mon_command(
289 "{\"prefix\": \"osd pool set\", \"pool\": \"" + pool_name +
290 "\", \"var\": \"dedup_tier\", \"val\": \"" + low_tier_pool_name + "\"}",
291 inbl, NULL, NULL);
292 if (r < 0) {
293 rados.shutdown();
294 return r;
295 }
296 r = rados.mon_command(
297 "{\"prefix\": \"osd pool set\", \"pool\": \"" + pool_name +
298 "\", \"var\": \"dedup_chunk_algorithm\", \"val\": \"" + chunk_algo + "\"}",
299 inbl, NULL, NULL);
300 if (r < 0) {
301 rados.shutdown();
302 return r;
303 }
304 r = rados.mon_command(
305 "{\"prefix\": \"osd pool set\", \"pool\": \"" + pool_name +
306 "\", \"var\": \"dedup_cdc_chunk_size\", \"val\": \"" + chunk_size + "\"}",
307 inbl, NULL, NULL);
308 if (r < 0) {
309 rados.shutdown();
310 return r;
311 }
9f95a23c
TL
312 }
313
7c673cae
FG
314 char hostname_cstr[100];
315 gethostname(hostname_cstr, 100);
316 stringstream hostpid;
317 hostpid << hostname_cstr << getpid() << "-";
318 prefix = hostpid.str();
11fdf7f2 319 ceph_assert(!initialized);
7c673cae
FG
320 initialized = true;
321 return 0;
322 }
323
324 void shutdown()
325 {
326 if (initialized) {
327 rados.shutdown();
328 }
329 }
330
331 void loop(TestOpGenerator *gen)
332 {
11fdf7f2 333 ceph_assert(initialized);
7c673cae 334 list<TestOp*> inflight;
9f95a23c 335 std::unique_lock state_locker{state_lock};
7c673cae
FG
336
337 TestOp *next = gen->next(*this);
338 TestOp *waiting = NULL;
339
340 while (next || !inflight.empty()) {
341 if (next && next->must_quiesce_other_ops() && !inflight.empty()) {
342 waiting = next;
343 next = NULL; // Force to wait for inflight to drain
344 }
345 if (next) {
346 inflight.push_back(next);
347 }
9f95a23c 348 state_lock.unlock();
7c673cae
FG
349 if (next) {
350 (*inflight.rbegin())->begin();
351 }
9f95a23c 352 state_lock.lock();
7c673cae
FG
353 while (1) {
354 for (list<TestOp*>::iterator i = inflight.begin();
355 i != inflight.end();) {
356 if ((*i)->finished()) {
357 cout << (*i)->num << ": done (" << (inflight.size()-1) << " left)" << std::endl;
358 delete *i;
359 inflight.erase(i++);
360 } else {
361 ++i;
362 }
363 }
364
365 if (inflight.size() >= (unsigned) max_in_flight || (!next && !inflight.empty())) {
366 cout << " waiting on " << inflight.size() << std::endl;
9f95a23c 367 wait_cond.wait(state_locker);
7c673cae
FG
368 } else {
369 break;
370 }
371 }
372 if (waiting) {
373 next = waiting;
374 waiting = NULL;
375 } else {
376 next = gen->next(*this);
377 }
378 }
7c673cae
FG
379 }
380
381 void kick()
382 {
9f95a23c 383 wait_cond.notify_all();
7c673cae
FG
384 }
385
386 TestWatchContext *get_watch_context(const string &oid) {
387 return watches.count(oid) ? watches[oid] : 0;
388 }
389
390 TestWatchContext *watch(const string &oid) {
11fdf7f2 391 ceph_assert(!watches.count(oid));
7c673cae
FG
392 return (watches[oid] = new TestWatchContext);
393 }
394
395 void unwatch(const string &oid) {
11fdf7f2 396 ceph_assert(watches.count(oid));
7c673cae
FG
397 delete watches[oid];
398 watches.erase(oid);
399 }
400
401 ObjectDesc get_most_recent(const string &oid) {
402 ObjectDesc new_obj;
403 for (map<int, map<string,ObjectDesc> >::reverse_iterator i =
404 pool_obj_cont.rbegin();
405 i != pool_obj_cont.rend();
406 ++i) {
407 map<string,ObjectDesc>::iterator j = i->second.find(oid);
408 if (j != i->second.end()) {
409 new_obj = j->second;
410 break;
411 }
412 }
413 return new_obj;
414 }
415
416 void rm_object_attrs(const string &oid, const set<string> &attrs)
417 {
418 ObjectDesc new_obj = get_most_recent(oid);
419 for (set<string>::const_iterator i = attrs.begin();
420 i != attrs.end();
421 ++i) {
422 new_obj.attrs.erase(*i);
423 }
424 new_obj.dirty = true;
20effc67 425 new_obj.flushed = false;
f67539c2 426 pool_obj_cont[current_snap].insert_or_assign(oid, new_obj);
7c673cae
FG
427 }
428
429 void remove_object_header(const string &oid)
430 {
431 ObjectDesc new_obj = get_most_recent(oid);
432 new_obj.header = bufferlist();
433 new_obj.dirty = true;
20effc67 434 new_obj.flushed = false;
f67539c2 435 pool_obj_cont[current_snap].insert_or_assign(oid, new_obj);
7c673cae
FG
436 }
437
438
439 void update_object_header(const string &oid, const bufferlist &bl)
440 {
441 ObjectDesc new_obj = get_most_recent(oid);
442 new_obj.header = bl;
443 new_obj.exists = true;
444 new_obj.dirty = true;
20effc67 445 new_obj.flushed = false;
f67539c2 446 pool_obj_cont[current_snap].insert_or_assign(oid, new_obj);
7c673cae
FG
447 }
448
449 void update_object_attrs(const string &oid, const map<string, ContDesc> &attrs)
450 {
451 ObjectDesc new_obj = get_most_recent(oid);
452 for (map<string, ContDesc>::const_iterator i = attrs.begin();
453 i != attrs.end();
454 ++i) {
455 new_obj.attrs[i->first] = i->second;
456 }
457 new_obj.exists = true;
458 new_obj.dirty = true;
20effc67 459 new_obj.flushed = false;
f67539c2 460 pool_obj_cont[current_snap].insert_or_assign(oid, new_obj);
7c673cae
FG
461 }
462
463 void update_object(ContentsGenerator *cont_gen,
464 const string &oid, const ContDesc &contents)
465 {
466 ObjectDesc new_obj = get_most_recent(oid);
467 new_obj.exists = true;
468 new_obj.dirty = true;
20effc67 469 new_obj.flushed = false;
7c673cae
FG
470 new_obj.update(cont_gen,
471 contents);
f67539c2 472 pool_obj_cont[current_snap].insert_or_assign(oid, new_obj);
7c673cae
FG
473 }
474
475 void update_object_full(const string &oid, const ObjectDesc &contents)
476 {
f67539c2 477 pool_obj_cont[current_snap].insert_or_assign(oid, contents);
7c673cae
FG
478 pool_obj_cont[current_snap][oid].dirty = true;
479 }
480
481 void update_object_undirty(const string &oid)
482 {
483 ObjectDesc new_obj = get_most_recent(oid);
484 new_obj.dirty = false;
f67539c2 485 pool_obj_cont[current_snap].insert_or_assign(oid, new_obj);
7c673cae
FG
486 }
487
488 void update_object_version(const string &oid, uint64_t version,
489 int snap = -1)
490 {
491 for (map<int, map<string,ObjectDesc> >::reverse_iterator i =
492 pool_obj_cont.rbegin();
493 i != pool_obj_cont.rend();
494 ++i) {
495 if (snap != -1 && snap < i->first)
496 continue;
497 map<string,ObjectDesc>::iterator j = i->second.find(oid);
498 if (j != i->second.end()) {
499 if (version)
500 j->second.version = version;
501 cout << __func__ << " oid " << oid
502 << " v " << version << " " << j->second.most_recent()
503 << " " << (j->second.dirty ? "dirty" : "clean")
504 << " " << (j->second.exists ? "exists" : "dne")
505 << std::endl;
506 break;
507 }
508 }
509 }
510
511 void remove_object(const string &oid)
512 {
11fdf7f2 513 ceph_assert(!get_watch_context(oid));
7c673cae 514 ObjectDesc new_obj;
f67539c2 515 pool_obj_cont[current_snap].insert_or_assign(oid, new_obj);
7c673cae
FG
516 }
517
518 bool find_object(const string &oid, ObjectDesc *contents, int snap = -1) const
519 {
520 for (map<int, map<string,ObjectDesc> >::const_reverse_iterator i =
521 pool_obj_cont.rbegin();
522 i != pool_obj_cont.rend();
523 ++i) {
524 if (snap != -1 && snap < i->first) continue;
525 if (i->second.count(oid) != 0) {
526 *contents = i->second.find(oid)->second;
527 return true;
528 }
529 }
530 return false;
531 }
532
31f18b77
FG
533 void update_object_redirect_target(const string &oid, const string &target)
534 {
535 redirect_objs[oid] = target;
536 }
537
11fdf7f2
TL
538 void update_object_chunk_target(const string &oid, uint64_t offset, const ChunkDesc &info)
539 {
540 for (map<int, map<string,ObjectDesc> >::const_reverse_iterator i =
541 pool_obj_cont.rbegin();
542 i != pool_obj_cont.rend();
543 ++i) {
544 if (i->second.count(oid) != 0) {
545 ObjectDesc obj_desc = i->second.find(oid)->second;
546 obj_desc.chunk_info[offset] = info;
547 update_object_full(oid, obj_desc);
548 return ;
549 }
550 }
551 return;
552 }
553
7c673cae
FG
554 bool object_existed_at(const string &oid, int snap = -1) const
555 {
556 ObjectDesc contents;
557 bool found = find_object(oid, &contents, snap);
558 return found && contents.exists;
559 }
560
561 void remove_snap(int snap)
562 {
563 map<int, map<string,ObjectDesc> >::iterator next_iter = pool_obj_cont.find(snap);
11fdf7f2 564 ceph_assert(next_iter != pool_obj_cont.end());
7c673cae 565 map<int, map<string,ObjectDesc> >::iterator current_iter = next_iter++;
11fdf7f2 566 ceph_assert(current_iter != pool_obj_cont.end());
7c673cae
FG
567 map<string,ObjectDesc> &current = current_iter->second;
568 map<string,ObjectDesc> &next = next_iter->second;
569 for (map<string,ObjectDesc>::iterator i = current.begin();
570 i != current.end();
571 ++i) {
572 if (next.count(i->first) == 0) {
573 next.insert(pair<string,ObjectDesc>(i->first, i->second));
574 }
575 }
576 pool_obj_cont.erase(current_iter);
577 snaps.erase(snap);
578 }
579
580 void add_snap(uint64_t snap)
581 {
582 snaps[current_snap] = snap;
583 current_snap++;
584 pool_obj_cont[current_snap];
585 seq = snap;
586 }
587
588 void roll_back(const string &oid, int snap)
589 {
11fdf7f2 590 ceph_assert(!get_watch_context(oid));
7c673cae
FG
591 ObjectDesc contents;
592 find_object(oid, &contents, snap);
593 contents.dirty = true;
20effc67 594 contents.flushed = false;
f67539c2 595 pool_obj_cont.rbegin()->second.insert_or_assign(oid, contents);
7c673cae 596 }
20effc67
TL
597
598 void update_object_tier_flushed(const string &oid, int snap)
599 {
600 for (map<int, map<string,ObjectDesc> >::reverse_iterator i =
601 pool_obj_cont.rbegin();
602 i != pool_obj_cont.rend();
603 ++i) {
604 if (snap != -1 && snap < i->first)
605 continue;
606 map<string,ObjectDesc>::iterator j = i->second.find(oid);
607 if (j != i->second.end()) {
608 j->second.flushed = true;
609 break;
610 }
611 }
612 }
613
614 bool check_oldest_snap_flushed(const string &oid, int snap)
615 {
616 for (map<int, map<string,ObjectDesc> >::reverse_iterator i =
617 pool_obj_cont.rbegin();
618 i != pool_obj_cont.rend();
619 ++i) {
620 if (snap != -1 && snap < i->first)
621 continue;
622 map<string,ObjectDesc>::iterator j = i->second.find(oid);
623 if (j != i->second.end() && !j->second.flushed) {
624 cout << __func__ << " oid " << oid
625 << " v " << j->second.version << " " << j->second.most_recent()
626 << " " << (j->second.flushed ? "flushed" : "unflushed")
627 << " " << i->first << std::endl;
628 return false;
629 }
630 }
631 return true;
632 }
633
634 bool check_chunks_refcount(librados::IoCtx &chunk_pool_ctx, librados::IoCtx &manifest_pool_ctx)
635 {
636 librados::ObjectCursor shard_start;
637 librados::ObjectCursor shard_end;
638 librados::ObjectCursor begin;
639 librados::ObjectCursor end;
640 begin = chunk_pool_ctx.object_list_begin();
641 end = chunk_pool_ctx.object_list_end();
642
643 chunk_pool_ctx.object_list_slice(
644 begin,
645 end,
646 1,
647 1,
648 &shard_start,
649 &shard_end);
650
651 librados::ObjectCursor c(shard_start);
652 while(c < shard_end)
653 {
654 std::vector<librados::ObjectItem> result;
655 int r = chunk_pool_ctx.object_list(c, shard_end, 12, {}, &result, &c);
656 if (r < 0) {
657 cerr << "error object_list : " << cpp_strerror(r) << std::endl;
658 return false;
659 }
660
661 for (const auto & i : result) {
662 auto oid = i.oid;
663 chunk_refs_t refs;
664 {
665 bufferlist t;
666 r = chunk_pool_ctx.getxattr(oid, CHUNK_REFCOUNT_ATTR, t);
667 if (r < 0) {
668 continue;
669 }
670 auto p = t.cbegin();
671 decode(refs, p);
672 }
673 ceph_assert(refs.get_type() == chunk_refs_t::TYPE_BY_OBJECT);
674
675 chunk_refs_by_object_t *byo =
676 static_cast<chunk_refs_by_object_t*>(refs.r.get());
677
678 for (auto& pp : byo->by_object) {
679 int src_refcount = 0;
680 int dst_refcount = byo->by_object.count(pp);
681 for (int tries = 0; tries < 10; tries++) {
682 r = cls_cas_references_chunk(manifest_pool_ctx, pp.oid.name, oid);
683 if (r == -ENOENT || r == -ENOLINK) {
684 src_refcount = 0;
685 } else if (r == -EBUSY) {
686 sleep(10);
687 continue;
688 } else {
689 src_refcount = r;
690 }
691 break;
692 }
693 if (src_refcount > dst_refcount) {
694 cerr << " src_object " << pp
695 << ": src_refcount " << src_refcount
696 << ", dst_object " << oid
697 << ": dst_refcount " << dst_refcount
698 << std::endl;
699 return false;
700 }
701 }
702 }
703 }
704 return true;
705 }
7c673cae
FG
706};
707
708void read_callback(librados::completion_t comp, void *arg);
709void write_callback(librados::completion_t comp, void *arg);
710
f67539c2
TL
711/// remove random xattrs from given object, and optionally remove omap
712/// entries if @c no_omap is not specified in context
7c673cae
FG
713class RemoveAttrsOp : public TestOp {
714public:
715 string oid;
716 librados::ObjectWriteOperation op;
717 librados::AioCompletion *comp;
718 RemoveAttrsOp(int n, RadosTestContext *context,
719 const string &oid,
720 TestOpStat *stat)
721 : TestOp(n, context, stat), oid(oid), comp(NULL)
722 {}
723
724 void _begin() override
725 {
726 ContDesc cont;
727 set<string> to_remove;
728 {
9f95a23c 729 std::lock_guard l{context->state_lock};
7c673cae
FG
730 ObjectDesc obj;
731 if (!context->find_object(oid, &obj)) {
732 context->kick();
733 done = true;
734 return;
735 }
736 cont = ContDesc(context->seq_num, context->current_snap,
737 context->seq_num, "");
738 context->oid_in_use.insert(oid);
739 context->oid_not_in_use.erase(oid);
740
741 if (rand() % 30) {
742 ContentsGenerator::iterator iter = context->attr_gen.get_iterator(cont);
743 for (map<string, ContDesc>::iterator i = obj.attrs.begin();
744 i != obj.attrs.end();
745 ++i, ++iter) {
746 if (!(*iter % 3)) {
7c673cae
FG
747 to_remove.insert(i->first);
748 op.rmxattr(i->first.c_str());
749 }
750 }
751 if (to_remove.empty()) {
752 context->kick();
753 context->oid_in_use.erase(oid);
754 context->oid_not_in_use.insert(oid);
755 done = true;
756 return;
757 }
758 if (!context->no_omap) {
759 op.omap_rm_keys(to_remove);
760 }
761 } else {
762 if (!context->no_omap) {
763 op.omap_clear();
764 }
765 for (map<string, ContDesc>::iterator i = obj.attrs.begin();
766 i != obj.attrs.end();
767 ++i) {
768 op.rmxattr(i->first.c_str());
769 to_remove.insert(i->first);
770 }
771 context->remove_object_header(oid);
772 }
773 context->rm_object_attrs(oid, to_remove);
774 }
775
776 pair<TestOp*, TestOp::CallbackInfo*> *cb_arg =
777 new pair<TestOp*, TestOp::CallbackInfo*>(this,
778 new TestOp::CallbackInfo(0));
9f95a23c 779 comp = context->rados.aio_create_completion((void*) cb_arg,
7c673cae
FG
780 &write_callback);
781 context->io_ctx.aio_operate(context->prefix+oid, comp, &op);
782 }
783
784 void _finish(CallbackInfo *info) override
785 {
9f95a23c 786 std::lock_guard l{context->state_lock};
7c673cae
FG
787 done = true;
788 context->update_object_version(oid, comp->get_version64());
789 context->oid_in_use.erase(oid);
790 context->oid_not_in_use.insert(oid);
791 context->kick();
792 }
793
794 bool finished() override
795 {
796 return done;
797 }
798
799 string getType() override
800 {
801 return "RemoveAttrsOp";
802 }
803};
804
f67539c2
TL
805/// add random xattrs to given object, and optionally add omap
806/// entries if @c no_omap is not specified in context
7c673cae
FG
807class SetAttrsOp : public TestOp {
808public:
809 string oid;
810 librados::ObjectWriteOperation op;
811 librados::AioCompletion *comp;
812 SetAttrsOp(int n,
813 RadosTestContext *context,
814 const string &oid,
815 TestOpStat *stat)
816 : TestOp(n, context, stat),
817 oid(oid), comp(NULL)
818 {}
819
820 void _begin() override
821 {
822 ContDesc cont;
823 {
9f95a23c 824 std::lock_guard l{context->state_lock};
7c673cae
FG
825 cont = ContDesc(context->seq_num, context->current_snap,
826 context->seq_num, "");
827 context->oid_in_use.insert(oid);
828 context->oid_not_in_use.erase(oid);
829 }
830
831 map<string, bufferlist> omap_contents;
832 map<string, ContDesc> omap;
833 bufferlist header;
834 ContentsGenerator::iterator keygen = context->attr_gen.get_iterator(cont);
835 op.create(false);
836 while (!*keygen) ++keygen;
837 while (*keygen) {
838 if (*keygen != '_')
839 header.append(*keygen);
840 ++keygen;
841 }
842 for (int i = 0; i < 20; ++i) {
843 string key;
844 while (!*keygen) ++keygen;
845 while (*keygen && key.size() < 40) {
846 key.push_back((*keygen % 20) + 'a');
847 ++keygen;
848 }
849 ContDesc val(cont);
850 val.seqnum += (unsigned)(*keygen);
851 val.prefix = ("oid: " + oid);
852 omap[key] = val;
853 bufferlist val_buffer = context->attr_gen.gen_bl(val);
854 omap_contents[key] = val_buffer;
855 op.setxattr(key.c_str(), val_buffer);
856 }
857 if (!context->no_omap) {
858 op.omap_set_header(header);
859 op.omap_set(omap_contents);
860 }
861
862 {
9f95a23c 863 std::lock_guard l{context->state_lock};
7c673cae
FG
864 context->update_object_header(oid, header);
865 context->update_object_attrs(oid, omap);
866 }
867
868 pair<TestOp*, TestOp::CallbackInfo*> *cb_arg =
869 new pair<TestOp*, TestOp::CallbackInfo*>(this,
870 new TestOp::CallbackInfo(0));
9f95a23c 871 comp = context->rados.aio_create_completion((void*) cb_arg, &write_callback);
7c673cae
FG
872 context->io_ctx.aio_operate(context->prefix+oid, comp, &op);
873 }
874
875 void _finish(CallbackInfo *info) override
876 {
9f95a23c 877 std::lock_guard l{context->state_lock};
7c673cae
FG
878 int r;
879 if ((r = comp->get_return_value())) {
880 cerr << "err " << r << std::endl;
881 ceph_abort();
882 }
883 done = true;
884 context->update_object_version(oid, comp->get_version64());
885 context->oid_in_use.erase(oid);
886 context->oid_not_in_use.insert(oid);
887 context->kick();
888 }
889
890 bool finished() override
891 {
892 return done;
893 }
894
895 string getType() override
896 {
897 return "SetAttrsOp";
898 }
899};
900
901class WriteOp : public TestOp {
902public:
f67539c2 903 const string oid;
7c673cae
FG
904 ContDesc cont;
905 set<librados::AioCompletion *> waiting;
f67539c2
TL
906 librados::AioCompletion *rcompletion = nullptr;
907 // numbers of async ops submitted
908 uint64_t waiting_on = 0;
909 uint64_t last_acked_tid = 0;
7c673cae
FG
910
911 librados::ObjectReadOperation read_op;
912 librados::ObjectWriteOperation write_op;
913 bufferlist rbuffer;
914
f67539c2
TL
915 const bool do_append;
916 const bool do_excl;
7c673cae
FG
917
918 WriteOp(int n,
919 RadosTestContext *context,
920 const string &oid,
921 bool do_append,
922 bool do_excl,
923 TestOpStat *stat = 0)
924 : TestOp(n, context, stat),
f67539c2
TL
925 oid(oid),
926 do_append(do_append),
7c673cae
FG
927 do_excl(do_excl)
928 {}
929
930 void _begin() override
931 {
f67539c2 932 assert(!done);
7c673cae 933 stringstream acc;
f67539c2 934 std::lock_guard state_locker{context->state_lock};
7c673cae
FG
935 acc << context->prefix << "OID: " << oid << " snap " << context->current_snap << std::endl;
936 string prefix = acc.str();
937
938 cont = ContDesc(context->seq_num, context->current_snap, context->seq_num, prefix);
939
940 ContentsGenerator *cont_gen;
941 if (do_append) {
942 ObjectDesc old_value;
943 bool found = context->find_object(oid, &old_value);
944 uint64_t prev_length = found && old_value.has_contents() ?
945 old_value.most_recent_gen()->get_length(old_value.most_recent()) :
946 0;
f67539c2
TL
947 bool requires_alignment;
948 int r = context->io_ctx.pool_requires_alignment2(&requires_alignment);
11fdf7f2 949 ceph_assert(r == 0);
7c673cae 950 uint64_t alignment = 0;
f67539c2 951 if (requires_alignment) {
7c673cae 952 r = context->io_ctx.pool_required_alignment2(&alignment);
11fdf7f2
TL
953 ceph_assert(r == 0);
954 ceph_assert(alignment != 0);
7c673cae
FG
955 }
956 cont_gen = new AppendGenerator(
957 prev_length,
958 alignment,
959 context->min_stride_size,
960 context->max_stride_size,
961 3);
962 } else {
963 cont_gen = new VarLenGenerator(
964 context->max_size, context->min_stride_size, context->max_stride_size);
965 }
966 context->update_object(cont_gen, oid, cont);
967
968 context->oid_in_use.insert(oid);
969 context->oid_not_in_use.erase(oid);
970
971 map<uint64_t, uint64_t> ranges;
972
973 cont_gen->get_ranges_map(cont, ranges);
974 std::cout << num << ": seq_num " << context->seq_num << " ranges " << ranges << std::endl;
975 context->seq_num++;
976
977 waiting_on = ranges.size();
7c673cae 978 ContentsGenerator::iterator gen_pos = cont_gen->get_iterator(cont);
f67539c2
TL
979 // assure that tid is greater than last_acked_tid
980 uint64_t tid = last_acked_tid + 1;
981 for (auto [offset, len] : ranges) {
982 gen_pos.seek(offset);
983 bufferlist to_write = gen_pos.gen_bl_advance(len);
984 ceph_assert(to_write.length() == len);
11fdf7f2 985 ceph_assert(to_write.length() > 0);
7c673cae 986 std::cout << num << ": writing " << context->prefix+oid
f67539c2
TL
987 << " from " << offset
988 << " to " << len + offset << " tid " << tid << std::endl;
989 auto cb_arg =
7c673cae 990 new pair<TestOp*, TestOp::CallbackInfo*>(this,
f67539c2 991 new TestOp::CallbackInfo(tid++));
7c673cae 992 librados::AioCompletion *completion =
9f95a23c 993 context->rados.aio_create_completion((void*) cb_arg, &write_callback);
7c673cae
FG
994 waiting.insert(completion);
995 librados::ObjectWriteOperation op;
996 if (do_append) {
997 op.append(to_write);
998 } else {
f67539c2 999 op.write(offset, to_write);
7c673cae 1000 }
f67539c2 1001 if (do_excl && cb_arg->second->id == last_acked_tid + 1)
7c673cae
FG
1002 op.assert_exists();
1003 context->io_ctx.aio_operate(
1004 context->prefix+oid, completion,
1005 &op);
1006 }
1007
1008 bufferlist contbl;
11fdf7f2 1009 encode(cont, contbl);
7c673cae
FG
1010 pair<TestOp*, TestOp::CallbackInfo*> *cb_arg =
1011 new pair<TestOp*, TestOp::CallbackInfo*>(
1012 this,
f67539c2 1013 new TestOp::CallbackInfo(tid++));
7c673cae 1014 librados::AioCompletion *completion = context->rados.aio_create_completion(
9f95a23c 1015 (void*) cb_arg, &write_callback);
7c673cae
FG
1016 waiting.insert(completion);
1017 waiting_on++;
1018 write_op.setxattr("_header", contbl);
1019 if (!do_append) {
1020 write_op.truncate(cont_gen->get_length(cont));
1021 }
1022 context->io_ctx.aio_operate(
1023 context->prefix+oid, completion, &write_op);
1024
1025 cb_arg =
1026 new pair<TestOp*, TestOp::CallbackInfo*>(
1027 this,
f67539c2 1028 new TestOp::CallbackInfo(tid++));
7c673cae 1029 rcompletion = context->rados.aio_create_completion(
9f95a23c 1030 (void*) cb_arg, &write_callback);
7c673cae
FG
1031 waiting_on++;
1032 read_op.read(0, 1, &rbuffer, 0);
1033 context->io_ctx.aio_operate(
1034 context->prefix+oid, rcompletion,
1035 &read_op,
1036 librados::OPERATION_ORDER_READS_WRITES, // order wrt previous write/update
1037 0);
7c673cae
FG
1038 }
1039
1040 void _finish(CallbackInfo *info) override
1041 {
11fdf7f2 1042 ceph_assert(info);
9f95a23c 1043 std::lock_guard state_locker{context->state_lock};
7c673cae
FG
1044 uint64_t tid = info->id;
1045
1046 cout << num << ": finishing write tid " << tid << " to " << context->prefix + oid << std::endl;
1047
1048 if (tid <= last_acked_tid) {
1049 cerr << "Error: finished tid " << tid
1050 << " when last_acked_tid was " << last_acked_tid << std::endl;
1051 ceph_abort();
1052 }
1053 last_acked_tid = tid;
1054
11fdf7f2 1055 ceph_assert(!done);
7c673cae
FG
1056 waiting_on--;
1057 if (waiting_on == 0) {
1058 uint64_t version = 0;
1059 for (set<librados::AioCompletion *>::iterator i = waiting.begin();
1060 i != waiting.end();
1061 ) {
11fdf7f2 1062 ceph_assert((*i)->is_complete());
7c673cae
FG
1063 if (int err = (*i)->get_return_value()) {
1064 cerr << "Error: oid " << oid << " write returned error code "
1065 << err << std::endl;
1066 }
1067 if ((*i)->get_version64() > version)
1068 version = (*i)->get_version64();
1069 (*i)->release();
1070 waiting.erase(i++);
1071 }
1072
1073 context->update_object_version(oid, version);
1074 if (rcompletion->get_version64() != version) {
1075 cerr << "Error: racing read on " << oid << " returned version "
1076 << rcompletion->get_version64() << " rather than version "
1077 << version << std::endl;
11fdf7f2 1078 ceph_abort_msg("racing read got wrong version");
7c673cae
FG
1079 }
1080
1081 {
1082 ObjectDesc old_value;
11fdf7f2 1083 ceph_assert(context->find_object(oid, &old_value, -1));
7c673cae
FG
1084 if (old_value.deleted())
1085 std::cout << num << ": left oid " << oid << " deleted" << std::endl;
1086 else
1087 std::cout << num << ": left oid " << oid << " "
1088 << old_value.most_recent() << std::endl;
1089 }
1090
1091 rcompletion->release();
1092 context->oid_in_use.erase(oid);
1093 context->oid_not_in_use.insert(oid);
1094 context->kick();
1095 done = true;
1096 }
7c673cae
FG
1097 }
1098
1099 bool finished() override
1100 {
1101 return done;
1102 }
1103
1104 string getType() override
1105 {
1106 return "WriteOp";
1107 }
1108};
1109
1110class WriteSameOp : public TestOp {
1111public:
1112 string oid;
1113 ContDesc cont;
1114 set<librados::AioCompletion *> waiting;
1115 librados::AioCompletion *rcompletion;
1116 uint64_t waiting_on;
1117 uint64_t last_acked_tid;
1118
1119 librados::ObjectReadOperation read_op;
1120 librados::ObjectWriteOperation write_op;
1121 bufferlist rbuffer;
1122
1123 WriteSameOp(int n,
1124 RadosTestContext *context,
1125 const string &oid,
1126 TestOpStat *stat = 0)
1127 : TestOp(n, context, stat),
1128 oid(oid), rcompletion(NULL), waiting_on(0),
1129 last_acked_tid(0)
1130 {}
1131
1132 void _begin() override
1133 {
9f95a23c 1134 std::lock_guard state_locker{context->state_lock};
7c673cae
FG
1135 done = 0;
1136 stringstream acc;
1137 acc << context->prefix << "OID: " << oid << " snap " << context->current_snap << std::endl;
1138 string prefix = acc.str();
1139
1140 cont = ContDesc(context->seq_num, context->current_snap, context->seq_num, prefix);
1141
1142 ContentsGenerator *cont_gen;
1143 cont_gen = new VarLenGenerator(
1144 context->max_size, context->min_stride_size, context->max_stride_size);
1145 context->update_object(cont_gen, oid, cont);
1146
1147 context->oid_in_use.insert(oid);
1148 context->oid_not_in_use.erase(oid);
1149
1150 map<uint64_t, uint64_t> ranges;
1151
1152 cont_gen->get_ranges_map(cont, ranges);
1153 std::cout << num << ": seq_num " << context->seq_num << " ranges " << ranges << std::endl;
1154 context->seq_num++;
1155
1156 waiting_on = ranges.size();
1157 ContentsGenerator::iterator gen_pos = cont_gen->get_iterator(cont);
f67539c2
TL
1158 // assure that tid is greater than last_acked_tid
1159 uint64_t tid = last_acked_tid + 1;
1160 for (auto [offset, len] : ranges) {
1161 gen_pos.seek(offset);
1162 bufferlist to_write = gen_pos.gen_bl_advance(len);
1163 ceph_assert(to_write.length() == len);
11fdf7f2 1164 ceph_assert(to_write.length() > 0);
7c673cae 1165 std::cout << num << ": writing " << context->prefix+oid
f67539c2
TL
1166 << " from " << offset
1167 << " to " << offset + len << " tid " << tid << std::endl;
1168 auto cb_arg =
7c673cae 1169 new pair<TestOp*, TestOp::CallbackInfo*>(this,
f67539c2 1170 new TestOp::CallbackInfo(tid++));
7c673cae 1171 librados::AioCompletion *completion =
9f95a23c 1172 context->rados.aio_create_completion((void*) cb_arg,
7c673cae
FG
1173 &write_callback);
1174 waiting.insert(completion);
1175 librados::ObjectWriteOperation op;
1176 /* no writesame multiplication factor for now */
f67539c2 1177 op.writesame(offset, to_write.length(), to_write);
7c673cae
FG
1178
1179 context->io_ctx.aio_operate(
1180 context->prefix+oid, completion,
1181 &op);
1182 }
1183
1184 bufferlist contbl;
11fdf7f2 1185 encode(cont, contbl);
7c673cae
FG
1186 pair<TestOp*, TestOp::CallbackInfo*> *cb_arg =
1187 new pair<TestOp*, TestOp::CallbackInfo*>(
1188 this,
f67539c2 1189 new TestOp::CallbackInfo(tid++));
7c673cae 1190 librados::AioCompletion *completion = context->rados.aio_create_completion(
9f95a23c 1191 (void*) cb_arg, &write_callback);
7c673cae
FG
1192 waiting.insert(completion);
1193 waiting_on++;
1194 write_op.setxattr("_header", contbl);
1195 write_op.truncate(cont_gen->get_length(cont));
1196 context->io_ctx.aio_operate(
1197 context->prefix+oid, completion, &write_op);
1198
1199 cb_arg =
1200 new pair<TestOp*, TestOp::CallbackInfo*>(
1201 this,
f67539c2 1202 new TestOp::CallbackInfo(tid++));
7c673cae 1203 rcompletion = context->rados.aio_create_completion(
9f95a23c 1204 (void*) cb_arg, &write_callback);
7c673cae
FG
1205 waiting_on++;
1206 read_op.read(0, 1, &rbuffer, 0);
1207 context->io_ctx.aio_operate(
1208 context->prefix+oid, rcompletion,
1209 &read_op,
1210 librados::OPERATION_ORDER_READS_WRITES, // order wrt previous write/update
1211 0);
7c673cae
FG
1212 }
1213
1214 void _finish(CallbackInfo *info) override
1215 {
11fdf7f2 1216 ceph_assert(info);
9f95a23c 1217 std::lock_guard state_locker{context->state_lock};
7c673cae
FG
1218 uint64_t tid = info->id;
1219
1220 cout << num << ": finishing writesame tid " << tid << " to " << context->prefix + oid << std::endl;
1221
1222 if (tid <= last_acked_tid) {
1223 cerr << "Error: finished tid " << tid
1224 << " when last_acked_tid was " << last_acked_tid << std::endl;
1225 ceph_abort();
1226 }
1227 last_acked_tid = tid;
1228
11fdf7f2 1229 ceph_assert(!done);
7c673cae
FG
1230 waiting_on--;
1231 if (waiting_on == 0) {
1232 uint64_t version = 0;
1233 for (set<librados::AioCompletion *>::iterator i = waiting.begin();
1234 i != waiting.end();
1235 ) {
11fdf7f2 1236 ceph_assert((*i)->is_complete());
7c673cae
FG
1237 if (int err = (*i)->get_return_value()) {
1238 cerr << "Error: oid " << oid << " writesame returned error code "
1239 << err << std::endl;
1240 }
1241 if ((*i)->get_version64() > version)
1242 version = (*i)->get_version64();
1243 (*i)->release();
1244 waiting.erase(i++);
1245 }
1246
1247 context->update_object_version(oid, version);
f67539c2
TL
1248 ceph_assert(rcompletion->is_complete());
1249 ceph_assert(rcompletion->get_return_value() == 1);
7c673cae
FG
1250 if (rcompletion->get_version64() != version) {
1251 cerr << "Error: racing read on " << oid << " returned version "
1252 << rcompletion->get_version64() << " rather than version "
1253 << version << std::endl;
11fdf7f2 1254 ceph_abort_msg("racing read got wrong version");
7c673cae 1255 }
f67539c2 1256 rcompletion->release();
7c673cae
FG
1257
1258 {
1259 ObjectDesc old_value;
11fdf7f2 1260 ceph_assert(context->find_object(oid, &old_value, -1));
7c673cae
FG
1261 if (old_value.deleted())
1262 std::cout << num << ": left oid " << oid << " deleted" << std::endl;
1263 else
1264 std::cout << num << ": left oid " << oid << " "
1265 << old_value.most_recent() << std::endl;
1266 }
1267
7c673cae
FG
1268 context->oid_in_use.erase(oid);
1269 context->oid_not_in_use.insert(oid);
1270 context->kick();
1271 done = true;
1272 }
7c673cae
FG
1273 }
1274
1275 bool finished() override
1276 {
1277 return done;
1278 }
1279
1280 string getType() override
1281 {
1282 return "WriteSameOp";
1283 }
1284};
1285
1286class DeleteOp : public TestOp {
1287public:
1288 string oid;
1289
1290 DeleteOp(int n,
1291 RadosTestContext *context,
1292 const string &oid,
1293 TestOpStat *stat = 0)
1294 : TestOp(n, context, stat), oid(oid)
1295 {}
1296
1297 void _begin() override
1298 {
9f95a23c 1299 std::unique_lock state_locker{context->state_lock};
7c673cae
FG
1300 if (context->get_watch_context(oid)) {
1301 context->kick();
7c673cae
FG
1302 return;
1303 }
1304
1305 ObjectDesc contents;
1306 context->find_object(oid, &contents);
1307 bool present = !contents.deleted();
1308
1309 context->oid_in_use.insert(oid);
1310 context->oid_not_in_use.erase(oid);
1311 context->seq_num++;
1312
1313 context->remove_object(oid);
1314
1315 interval_set<uint64_t> ranges;
9f95a23c 1316 state_locker.unlock();
7c673cae
FG
1317
1318 int r = 0;
1319 if (rand() % 2) {
1320 librados::ObjectWriteOperation op;
1321 op.assert_exists();
1322 op.remove();
1323 r = context->io_ctx.operate(context->prefix+oid, &op);
1324 } else {
1325 r = context->io_ctx.remove(context->prefix+oid);
1326 }
1327 if (r && !(r == -ENOENT && !present)) {
1328 cerr << "r is " << r << " while deleting " << oid << " and present is " << present << std::endl;
1329 ceph_abort();
1330 }
1331
9f95a23c 1332 state_locker.lock();
7c673cae
FG
1333 context->oid_in_use.erase(oid);
1334 context->oid_not_in_use.insert(oid);
1335 context->kick();
7c673cae
FG
1336 }
1337
1338 string getType() override
1339 {
1340 return "DeleteOp";
1341 }
1342};
1343
1344class ReadOp : public TestOp {
1345public:
1346 vector<librados::AioCompletion *> completions;
1347 librados::ObjectReadOperation op;
1348 string oid;
1349 ObjectDesc old_value;
1350 int snap;
1351 bool balance_reads;
9f95a23c 1352 bool localize_reads;
7c673cae 1353
11fdf7f2 1354 std::shared_ptr<int> in_use;
7c673cae
FG
1355
1356 vector<bufferlist> results;
1357 vector<int> retvals;
1358 vector<std::map<uint64_t, uint64_t>> extent_results;
1359 vector<bool> is_sparse_read;
1360 uint64_t waiting_on;
1361
1362 vector<bufferlist> checksums;
1363 vector<int> checksum_retvals;
1364
1365 map<string, bufferlist> attrs;
1366 int attrretval;
1367
1368 set<string> omap_requested_keys;
1369 map<string, bufferlist> omap_returned_values;
1370 set<string> omap_keys;
1371 map<string, bufferlist> omap;
1372 bufferlist header;
1373
1374 map<string, bufferlist> xattrs;
1375 ReadOp(int n,
1376 RadosTestContext *context,
1377 const string &oid,
1378 bool balance_reads,
9f95a23c 1379 bool localize_reads,
7c673cae
FG
1380 TestOpStat *stat = 0)
1381 : TestOp(n, context, stat),
1382 completions(3),
1383 oid(oid),
1384 snap(0),
1385 balance_reads(balance_reads),
9f95a23c 1386 localize_reads(localize_reads),
7c673cae
FG
1387 results(3),
1388 retvals(3),
1389 extent_results(3),
1390 is_sparse_read(3, false),
1391 waiting_on(0),
1392 checksums(3),
1393 checksum_retvals(3),
1394 attrretval(0)
1395 {}
1396
1397 void _do_read(librados::ObjectReadOperation& read_op, int index) {
1398 uint64_t len = 0;
1399 if (old_value.has_contents())
1400 len = old_value.most_recent_gen()->get_length(old_value.most_recent());
1401 if (context->no_sparse || rand() % 2) {
1402 is_sparse_read[index] = false;
1403 read_op.read(0,
1404 len,
1405 &results[index],
1406 &retvals[index]);
1407 bufferlist init_value_bl;
11fdf7f2 1408 encode(static_cast<uint32_t>(-1), init_value_bl);
7c673cae
FG
1409 read_op.checksum(LIBRADOS_CHECKSUM_TYPE_CRC32C, init_value_bl, 0, len,
1410 0, &checksums[index], &checksum_retvals[index]);
1411 } else {
1412 is_sparse_read[index] = true;
1413 read_op.sparse_read(0,
1414 len,
1415 &extent_results[index],
1416 &results[index],
1417 &retvals[index]);
1418 }
1419 }
1420
1421 void _begin() override
1422 {
9f95a23c 1423 std::unique_lock state_locker{context->state_lock};
7c673cae
FG
1424 if (!(rand() % 4) && !context->snaps.empty()) {
1425 snap = rand_choose(context->snaps)->first;
1426 in_use = context->snaps_in_use.lookup_or_create(snap, snap);
1427 } else {
1428 snap = -1;
1429 }
1430 std::cout << num << ": read oid " << oid << " snap " << snap << std::endl;
1431 done = 0;
1432 for (uint32_t i = 0; i < 3; i++) {
9f95a23c 1433 completions[i] = context->rados.aio_create_completion((void *) this, &read_callback);
7c673cae
FG
1434 }
1435
1436 context->oid_in_use.insert(oid);
1437 context->oid_not_in_use.erase(oid);
11fdf7f2 1438 ceph_assert(context->find_object(oid, &old_value, snap));
7c673cae
FG
1439 if (old_value.deleted())
1440 std::cout << num << ": expect deleted" << std::endl;
1441 else
1442 std::cout << num << ": expect " << old_value.most_recent() << std::endl;
1443
1444 TestWatchContext *ctx = context->get_watch_context(oid);
9f95a23c 1445 state_locker.unlock();
7c673cae 1446 if (ctx) {
11fdf7f2 1447 ceph_assert(old_value.exists);
7c673cae
FG
1448 TestAlarm alarm;
1449 std::cerr << num << ": about to start" << std::endl;
1450 ctx->start();
1451 std::cerr << num << ": started" << std::endl;
1452 bufferlist bl;
1453 context->io_ctx.set_notify_timeout(600);
1454 int r = context->io_ctx.notify2(context->prefix+oid, bl, 0, NULL);
1455 if (r < 0) {
1456 std::cerr << "r is " << r << std::endl;
1457 ceph_abort();
1458 }
1459 std::cerr << num << ": notified, waiting" << std::endl;
1460 ctx->wait();
1461 }
9f95a23c 1462 state_locker.lock();
7c673cae
FG
1463 if (snap >= 0) {
1464 context->io_ctx.snap_set_read(context->snaps[snap]);
1465 }
1466 _do_read(op, 0);
1467 for (map<string, ContDesc>::iterator i = old_value.attrs.begin();
1468 i != old_value.attrs.end();
1469 ++i) {
1470 if (rand() % 2) {
1471 string key = i->first;
1472 if (rand() % 2)
1473 key.push_back((rand() % 26) + 'a');
1474 omap_requested_keys.insert(key);
1475 }
1476 }
1477 if (!context->no_omap) {
1478 op.omap_get_vals_by_keys(omap_requested_keys, &omap_returned_values, 0);
1479 // NOTE: we're ignore pmore here, which assumes the OSD limit is high
1480 // enough for us.
1481 op.omap_get_keys2("", -1, &omap_keys, nullptr, nullptr);
1482 op.omap_get_vals2("", -1, &omap, nullptr, nullptr);
1483 op.omap_get_header(&header, 0);
1484 }
1485 op.getxattrs(&xattrs, 0);
1486
1487 unsigned flags = 0;
1488 if (balance_reads)
1489 flags |= librados::OPERATION_BALANCE_READS;
9f95a23c
TL
1490 if (localize_reads)
1491 flags |= librados::OPERATION_LOCALIZE_READS;
7c673cae 1492
11fdf7f2 1493 ceph_assert(!context->io_ctx.aio_operate(context->prefix+oid, completions[0], &op,
7c673cae
FG
1494 flags, NULL));
1495 waiting_on++;
1496
1497 // send 2 pipelined reads on the same object/snap. This can help testing
1498 // OSD's read behavior in some scenarios
1499 for (uint32_t i = 1; i < 3; ++i) {
1500 librados::ObjectReadOperation pipeline_op;
1501 _do_read(pipeline_op, i);
11fdf7f2 1502 ceph_assert(!context->io_ctx.aio_operate(context->prefix+oid, completions[i], &pipeline_op, 0));
7c673cae
FG
1503 waiting_on++;
1504 }
1505
1506 if (snap >= 0) {
1507 context->io_ctx.snap_set_read(0);
1508 }
7c673cae
FG
1509 }
1510
1511 void _finish(CallbackInfo *info) override
1512 {
9f95a23c 1513 std::unique_lock state_locker{context->state_lock};
11fdf7f2
TL
1514 ceph_assert(!done);
1515 ceph_assert(waiting_on > 0);
7c673cae
FG
1516 if (--waiting_on) {
1517 return;
1518 }
1519
1520 context->oid_in_use.erase(oid);
1521 context->oid_not_in_use.insert(oid);
1522 int retval = completions[0]->get_return_value();
1523 for (vector<librados::AioCompletion *>::iterator it = completions.begin();
1524 it != completions.end(); ++it) {
11fdf7f2 1525 ceph_assert((*it)->is_complete());
7c673cae
FG
1526 uint64_t version = (*it)->get_version64();
1527 int err = (*it)->get_return_value();
1528 if (err != retval) {
1529 cerr << num << ": Error: oid " << oid << " read returned different error codes: "
1530 << retval << " and " << err << std::endl;
1531 ceph_abort();
1532 }
1533 if (err) {
1534 if (!(err == -ENOENT && old_value.deleted())) {
1535 cerr << num << ": Error: oid " << oid << " read returned error code "
1536 << err << std::endl;
1537 ceph_abort();
1538 }
1539 } else if (version != old_value.version) {
1540 cerr << num << ": oid " << oid << " version is " << version
1541 << " and expected " << old_value.version << std::endl;
11fdf7f2 1542 ceph_assert(version == old_value.version);
7c673cae
FG
1543 }
1544 }
1545 if (!retval) {
1546 map<string, bufferlist>::iterator iter = xattrs.find("_header");
1547 bufferlist headerbl;
1548 if (iter == xattrs.end()) {
1549 if (old_value.has_contents()) {
1550 cerr << num << ": Error: did not find header attr, has_contents: "
1551 << old_value.has_contents()
1552 << std::endl;
11fdf7f2 1553 ceph_assert(!old_value.has_contents());
7c673cae
FG
1554 }
1555 } else {
1556 headerbl = iter->second;
1557 xattrs.erase(iter);
1558 }
1559 if (old_value.deleted()) {
1560 std::cout << num << ": expect deleted" << std::endl;
11fdf7f2 1561 ceph_abort_msg("expected deleted");
7c673cae
FG
1562 } else {
1563 std::cout << num << ": expect " << old_value.most_recent() << std::endl;
1564 }
1565 if (old_value.has_contents()) {
1566 ContDesc to_check;
11fdf7f2
TL
1567 auto p = headerbl.cbegin();
1568 decode(to_check, p);
7c673cae
FG
1569 if (to_check != old_value.most_recent()) {
1570 cerr << num << ": oid " << oid << " found incorrect object contents " << to_check
1571 << ", expected " << old_value.most_recent() << std::endl;
1572 context->errors++;
1573 }
1574 for (unsigned i = 0; i < results.size(); i++) {
1575 if (is_sparse_read[i]) {
1576 if (!old_value.check_sparse(extent_results[i], results[i])) {
1577 cerr << num << ": oid " << oid << " contents " << to_check << " corrupt" << std::endl;
1578 context->errors++;
1579 }
1580 } else {
1581 if (!old_value.check(results[i])) {
1582 cerr << num << ": oid " << oid << " contents " << to_check << " corrupt" << std::endl;
1583 context->errors++;
1584 }
1585
1586 uint32_t checksum = 0;
1587 if (checksum_retvals[i] == 0) {
1588 try {
11fdf7f2 1589 auto bl_it = checksums[i].cbegin();
7c673cae 1590 uint32_t csum_count;
11fdf7f2
TL
1591 decode(csum_count, bl_it);
1592 decode(checksum, bl_it);
7c673cae
FG
1593 } catch (const buffer::error &err) {
1594 checksum_retvals[i] = -EBADMSG;
1595 }
1596 }
1597 if (checksum_retvals[i] != 0 || checksum != results[i].crc32c(-1)) {
1598 cerr << num << ": oid " << oid << " checksum " << checksums[i]
1599 << " incorrect, expecting " << results[i].crc32c(-1)
1600 << std::endl;
1601 context->errors++;
1602 }
1603 }
1604 }
1605 if (context->errors) ceph_abort();
1606 }
1607
1608 // Attributes
1609 if (!context->no_omap) {
1610 if (!(old_value.header == header)) {
1611 cerr << num << ": oid " << oid << " header does not match, old size: "
1612 << old_value.header.length() << " new size " << header.length()
1613 << std::endl;
11fdf7f2 1614 ceph_assert(old_value.header == header);
7c673cae
FG
1615 }
1616 if (omap.size() != old_value.attrs.size()) {
1617 cerr << num << ": oid " << oid << " omap.size() is " << omap.size()
1618 << " and old is " << old_value.attrs.size() << std::endl;
11fdf7f2 1619 ceph_assert(omap.size() == old_value.attrs.size());
7c673cae
FG
1620 }
1621 if (omap_keys.size() != old_value.attrs.size()) {
1622 cerr << num << ": oid " << oid << " omap.size() is " << omap_keys.size()
1623 << " and old is " << old_value.attrs.size() << std::endl;
11fdf7f2 1624 ceph_assert(omap_keys.size() == old_value.attrs.size());
7c673cae
FG
1625 }
1626 }
1627 if (xattrs.size() != old_value.attrs.size()) {
1628 cerr << num << ": oid " << oid << " xattrs.size() is " << xattrs.size()
1629 << " and old is " << old_value.attrs.size() << std::endl;
11fdf7f2 1630 ceph_assert(xattrs.size() == old_value.attrs.size());
7c673cae
FG
1631 }
1632 for (map<string, ContDesc>::iterator iter = old_value.attrs.begin();
1633 iter != old_value.attrs.end();
1634 ++iter) {
1635 bufferlist bl = context->attr_gen.gen_bl(
1636 iter->second);
1637 if (!context->no_omap) {
1638 map<string, bufferlist>::iterator omap_iter = omap.find(iter->first);
11fdf7f2
TL
1639 ceph_assert(omap_iter != omap.end());
1640 ceph_assert(bl.length() == omap_iter->second.length());
7c673cae
FG
1641 bufferlist::iterator k = bl.begin();
1642 for(bufferlist::iterator l = omap_iter->second.begin();
1643 !k.end() && !l.end();
1644 ++k, ++l) {
11fdf7f2 1645 ceph_assert(*l == *k);
7c673cae
FG
1646 }
1647 }
1648 map<string, bufferlist>::iterator xattr_iter = xattrs.find(iter->first);
11fdf7f2
TL
1649 ceph_assert(xattr_iter != xattrs.end());
1650 ceph_assert(bl.length() == xattr_iter->second.length());
7c673cae
FG
1651 bufferlist::iterator k = bl.begin();
1652 for (bufferlist::iterator j = xattr_iter->second.begin();
1653 !k.end() && !j.end();
1654 ++j, ++k) {
11fdf7f2 1655 ceph_assert(*j == *k);
7c673cae
FG
1656 }
1657 }
1658 if (!context->no_omap) {
1659 for (set<string>::iterator i = omap_requested_keys.begin();
1660 i != omap_requested_keys.end();
1661 ++i) {
1662 if (!omap_returned_values.count(*i))
11fdf7f2 1663 ceph_assert(!old_value.attrs.count(*i));
7c673cae 1664 if (!old_value.attrs.count(*i))
11fdf7f2 1665 ceph_assert(!omap_returned_values.count(*i));
7c673cae
FG
1666 }
1667 for (map<string, bufferlist>::iterator i = omap_returned_values.begin();
1668 i != omap_returned_values.end();
1669 ++i) {
11fdf7f2
TL
1670 ceph_assert(omap_requested_keys.count(i->first));
1671 ceph_assert(omap.count(i->first));
1672 ceph_assert(old_value.attrs.count(i->first));
1673 ceph_assert(i->second == omap[i->first]);
7c673cae
FG
1674 }
1675 }
1676 }
1677 for (vector<librados::AioCompletion *>::iterator it = completions.begin();
1678 it != completions.end(); ++it) {
1679 (*it)->release();
1680 }
1681 context->kick();
1682 done = true;
1683 }
1684
1685 bool finished() override
1686 {
1687 return done;
1688 }
1689
1690 string getType() override
1691 {
1692 return "ReadOp";
1693 }
1694};
1695
1696class SnapCreateOp : public TestOp {
1697public:
1698 SnapCreateOp(int n,
1699 RadosTestContext *context,
1700 TestOpStat *stat = 0)
1701 : TestOp(n, context, stat)
1702 {}
1703
1704 void _begin() override
1705 {
1706 uint64_t snap;
1707 string snapname;
1708
1709 if (context->pool_snaps) {
1710 stringstream ss;
1711
1712 ss << context->prefix << "snap" << ++context->snapname_num;
1713 snapname = ss.str();
1714
1715 int ret = context->io_ctx.snap_create(snapname.c_str());
1716 if (ret) {
1717 cerr << "snap_create returned " << ret << std::endl;
1718 ceph_abort();
1719 }
11fdf7f2 1720 ceph_assert(!context->io_ctx.snap_lookup(snapname.c_str(), &snap));
7c673cae
FG
1721
1722 } else {
11fdf7f2 1723 ceph_assert(!context->io_ctx.selfmanaged_snap_create(&snap));
7c673cae
FG
1724 }
1725
9f95a23c 1726 std::unique_lock state_locker{context->state_lock};
7c673cae
FG
1727 context->add_snap(snap);
1728
9f95a23c 1729 if (!context->pool_snaps) {
7c673cae
FG
1730 vector<uint64_t> snapset(context->snaps.size());
1731
1732 int j = 0;
1733 for (map<int,uint64_t>::reverse_iterator i = context->snaps.rbegin();
1734 i != context->snaps.rend();
1735 ++i, ++j) {
1736 snapset[j] = i->second;
1737 }
1738
9f95a23c 1739 state_locker.unlock();
7c673cae
FG
1740
1741 int r = context->io_ctx.selfmanaged_snap_set_write_ctx(context->seq, snapset);
1742 if (r) {
1743 cerr << "r is " << r << " snapset is " << snapset << " seq is " << context->seq << std::endl;
1744 ceph_abort();
1745 }
1746 }
1747 }
1748
1749 string getType() override
1750 {
1751 return "SnapCreateOp";
1752 }
1753 bool must_quiesce_other_ops() override { return context->pool_snaps; }
1754};
1755
1756class SnapRemoveOp : public TestOp {
1757public:
1758 int to_remove;
1759 SnapRemoveOp(int n, RadosTestContext *context,
1760 int snap,
1761 TestOpStat *stat = 0)
1762 : TestOp(n, context, stat),
1763 to_remove(snap)
1764 {}
1765
1766 void _begin() override
1767 {
9f95a23c 1768 std::unique_lock state_locker{context->state_lock};
7c673cae
FG
1769 uint64_t snap = context->snaps[to_remove];
1770 context->remove_snap(to_remove);
1771
1772 if (context->pool_snaps) {
1773 string snapname;
1774
11fdf7f2
TL
1775 ceph_assert(!context->io_ctx.snap_get_name(snap, &snapname));
1776 ceph_assert(!context->io_ctx.snap_remove(snapname.c_str()));
7c673cae 1777 } else {
11fdf7f2 1778 ceph_assert(!context->io_ctx.selfmanaged_snap_remove(snap));
7c673cae
FG
1779
1780 vector<uint64_t> snapset(context->snaps.size());
1781 int j = 0;
1782 for (map<int,uint64_t>::reverse_iterator i = context->snaps.rbegin();
1783 i != context->snaps.rend();
1784 ++i, ++j) {
1785 snapset[j] = i->second;
1786 }
1787
1788 int r = context->io_ctx.selfmanaged_snap_set_write_ctx(context->seq, snapset);
1789 if (r) {
1790 cerr << "r is " << r << " snapset is " << snapset << " seq is " << context->seq << std::endl;
1791 ceph_abort();
1792 }
1793 }
7c673cae
FG
1794 }
1795
1796 string getType() override
1797 {
1798 return "SnapRemoveOp";
1799 }
1800};
1801
1802class WatchOp : public TestOp {
1803 string oid;
1804public:
1805 WatchOp(int n,
1806 RadosTestContext *context,
1807 const string &_oid,
1808 TestOpStat *stat = 0)
1809 : TestOp(n, context, stat),
1810 oid(_oid)
1811 {}
1812
1813 void _begin() override
1814 {
9f95a23c 1815 std::unique_lock state_locker{context->state_lock};
7c673cae
FG
1816 ObjectDesc contents;
1817 context->find_object(oid, &contents);
1818 if (contents.deleted()) {
1819 context->kick();
7c673cae
FG
1820 return;
1821 }
1822 context->oid_in_use.insert(oid);
1823 context->oid_not_in_use.erase(oid);
1824
1825 TestWatchContext *ctx = context->get_watch_context(oid);
9f95a23c 1826 state_locker.unlock();
7c673cae
FG
1827 int r;
1828 if (!ctx) {
1829 {
9f95a23c 1830 std::lock_guard l{context->state_lock};
7c673cae
FG
1831 ctx = context->watch(oid);
1832 }
1833
1834 r = context->io_ctx.watch2(context->prefix+oid,
1835 &ctx->get_handle(),
1836 ctx);
1837 } else {
1838 r = context->io_ctx.unwatch2(ctx->get_handle());
1839 {
9f95a23c 1840 std::lock_guard l{context->state_lock};
7c673cae
FG
1841 context->unwatch(oid);
1842 }
1843 }
1844
1845 if (r) {
1846 cerr << "r is " << r << std::endl;
1847 ceph_abort();
1848 }
1849
1850 {
9f95a23c 1851 std::lock_guard l{context->state_lock};
7c673cae
FG
1852 context->oid_in_use.erase(oid);
1853 context->oid_not_in_use.insert(oid);
1854 }
1855 }
1856
1857 string getType() override
1858 {
1859 return "WatchOp";
1860 }
1861};
1862
1863class RollbackOp : public TestOp {
1864public:
1865 string oid;
1866 int roll_back_to;
1867 librados::ObjectWriteOperation zero_write_op1;
1868 librados::ObjectWriteOperation zero_write_op2;
1869 librados::ObjectWriteOperation op;
1870 vector<librados::AioCompletion *> comps;
11fdf7f2 1871 std::shared_ptr<int> in_use;
7c673cae
FG
1872 int last_finished;
1873 int outstanding;
1874
1875 RollbackOp(int n,
1876 RadosTestContext *context,
1877 const string &_oid,
1878 TestOpStat *stat = 0)
1879 : TestOp(n, context, stat),
1880 oid(_oid), roll_back_to(-1),
1881 comps(3, NULL),
1882 last_finished(-1), outstanding(3)
1883 {}
1884
1885 void _begin() override
1886 {
9f95a23c 1887 context->state_lock.lock();
7c673cae
FG
1888 if (context->get_watch_context(oid)) {
1889 context->kick();
9f95a23c 1890 context->state_lock.unlock();
7c673cae
FG
1891 return;
1892 }
1893
1894 if (context->snaps.empty()) {
1895 context->kick();
9f95a23c 1896 context->state_lock.unlock();
7c673cae
FG
1897 done = true;
1898 return;
1899 }
1900
1901 context->oid_in_use.insert(oid);
1902 context->oid_not_in_use.erase(oid);
1903
1904 roll_back_to = rand_choose(context->snaps)->first;
1905 in_use = context->snaps_in_use.lookup_or_create(
1906 roll_back_to,
1907 roll_back_to);
1908
1909
1910 cout << "rollback oid " << oid << " to " << roll_back_to << std::endl;
1911
1912 bool existed_before = context->object_existed_at(oid);
1913 bool existed_after = context->object_existed_at(oid, roll_back_to);
1914
1915 context->roll_back(oid, roll_back_to);
1916 uint64_t snap = context->snaps[roll_back_to];
1917
1918 outstanding -= (!existed_before) + (!existed_after);
1919
9f95a23c 1920 context->state_lock.unlock();
7c673cae
FG
1921
1922 bufferlist bl, bl2;
1923 zero_write_op1.append(bl);
1924 zero_write_op2.append(bl2);
1925
1926 if (context->pool_snaps) {
1927 op.snap_rollback(snap);
1928 } else {
1929 op.selfmanaged_snap_rollback(snap);
1930 }
1931
1932 if (existed_before) {
1933 pair<TestOp*, TestOp::CallbackInfo*> *cb_arg =
1934 new pair<TestOp*, TestOp::CallbackInfo*>(this,
1935 new TestOp::CallbackInfo(0));
1936 comps[0] =
9f95a23c 1937 context->rados.aio_create_completion((void*) cb_arg,
7c673cae
FG
1938 &write_callback);
1939 context->io_ctx.aio_operate(
1940 context->prefix+oid, comps[0], &zero_write_op1);
1941 }
1942 {
1943 pair<TestOp*, TestOp::CallbackInfo*> *cb_arg =
1944 new pair<TestOp*, TestOp::CallbackInfo*>(this,
1945 new TestOp::CallbackInfo(1));
1946 comps[1] =
9f95a23c 1947 context->rados.aio_create_completion((void*) cb_arg,
7c673cae
FG
1948 &write_callback);
1949 context->io_ctx.aio_operate(
1950 context->prefix+oid, comps[1], &op);
1951 }
1952 if (existed_after) {
1953 pair<TestOp*, TestOp::CallbackInfo*> *cb_arg =
1954 new pair<TestOp*, TestOp::CallbackInfo*>(this,
1955 new TestOp::CallbackInfo(2));
1956 comps[2] =
9f95a23c 1957 context->rados.aio_create_completion((void*) cb_arg,
7c673cae
FG
1958 &write_callback);
1959 context->io_ctx.aio_operate(
1960 context->prefix+oid, comps[2], &zero_write_op2);
1961 }
1962 }
1963
1964 void _finish(CallbackInfo *info) override
1965 {
9f95a23c 1966 std::lock_guard l{context->state_lock};
7c673cae
FG
1967 uint64_t tid = info->id;
1968 cout << num << ": finishing rollback tid " << tid
1969 << " to " << context->prefix + oid << std::endl;
11fdf7f2 1970 ceph_assert((int)(info->id) > last_finished);
7c673cae
FG
1971 last_finished = info->id;
1972
1973 int r;
1974 if ((r = comps[last_finished]->get_return_value()) != 0) {
1975 cerr << "err " << r << std::endl;
1976 ceph_abort();
1977 }
1978 if (--outstanding == 0) {
1979 done = true;
1980 context->update_object_version(oid, comps[tid]->get_version64());
1981 context->oid_in_use.erase(oid);
1982 context->oid_not_in_use.insert(oid);
11fdf7f2 1983 in_use = std::shared_ptr<int>();
7c673cae
FG
1984 context->kick();
1985 }
1986 }
1987
1988 bool finished() override
1989 {
1990 return done;
1991 }
1992
1993 string getType() override
1994 {
1995 return "RollBackOp";
1996 }
1997};
1998
1999class CopyFromOp : public TestOp {
2000public:
2001 string oid, oid_src;
2002 ObjectDesc src_value;
2003 librados::ObjectWriteOperation op;
2004 librados::ObjectReadOperation rd_op;
2005 librados::AioCompletion *comp;
11fdf7f2
TL
2006 librados::AioCompletion *comp_racing_read = nullptr;
2007 std::shared_ptr<int> in_use;
7c673cae
FG
2008 int snap;
2009 int done;
2010 uint64_t version;
2011 int r;
2012 CopyFromOp(int n,
2013 RadosTestContext *context,
2014 const string &oid,
2015 const string &oid_src,
2016 TestOpStat *stat)
2017 : TestOp(n, context, stat),
2018 oid(oid), oid_src(oid_src),
2019 comp(NULL), snap(-1), done(0),
2020 version(0), r(0)
2021 {}
2022
2023 void _begin() override
2024 {
2025 ContDesc cont;
2026 {
9f95a23c 2027 std::lock_guard l{context->state_lock};
7c673cae
FG
2028 cont = ContDesc(context->seq_num, context->current_snap,
2029 context->seq_num, "");
2030 context->oid_in_use.insert(oid);
2031 context->oid_not_in_use.erase(oid);
2032 context->oid_in_use.insert(oid_src);
2033 context->oid_not_in_use.erase(oid_src);
2034
2035 // choose source snap
2036 if (0 && !(rand() % 4) && !context->snaps.empty()) {
2037 snap = rand_choose(context->snaps)->first;
2038 in_use = context->snaps_in_use.lookup_or_create(snap, snap);
2039 } else {
2040 snap = -1;
2041 }
2042 context->find_object(oid_src, &src_value, snap);
2043 if (!src_value.deleted())
2044 context->update_object_full(oid, src_value);
2045 }
2046
2047 string src = context->prefix+oid_src;
11fdf7f2 2048 op.copy_from(src.c_str(), context->io_ctx, src_value.version, 0);
7c673cae
FG
2049
2050 pair<TestOp*, TestOp::CallbackInfo*> *cb_arg =
2051 new pair<TestOp*, TestOp::CallbackInfo*>(this,
2052 new TestOp::CallbackInfo(0));
9f95a23c 2053 comp = context->rados.aio_create_completion((void*) cb_arg,
7c673cae
FG
2054 &write_callback);
2055 context->io_ctx.aio_operate(context->prefix+oid, comp, &op);
2056
2057 // queue up a racing read, too.
2058 pair<TestOp*, TestOp::CallbackInfo*> *read_cb_arg =
2059 new pair<TestOp*, TestOp::CallbackInfo*>(this,
2060 new TestOp::CallbackInfo(1));
9f95a23c 2061 comp_racing_read = context->rados.aio_create_completion((void*) read_cb_arg, &write_callback);
7c673cae
FG
2062 rd_op.stat(NULL, NULL, NULL);
2063 context->io_ctx.aio_operate(context->prefix+oid, comp_racing_read, &rd_op,
2064 librados::OPERATION_ORDER_READS_WRITES, // order wrt previous write/update
2065 NULL);
2066
2067 }
2068
2069 void _finish(CallbackInfo *info) override
2070 {
9f95a23c 2071 std::lock_guard l{context->state_lock};
7c673cae
FG
2072
2073 // note that the read can (and atm will) come back before the
2074 // write reply, but will reflect the update and the versions will
2075 // match.
2076
2077 if (info->id == 0) {
2078 // copy_from
11fdf7f2 2079 ceph_assert(comp->is_complete());
7c673cae
FG
2080 cout << num << ": finishing copy_from to " << context->prefix + oid << std::endl;
2081 if ((r = comp->get_return_value())) {
2082 if (r == -ENOENT && src_value.deleted()) {
2083 cout << num << ": got expected ENOENT (src dne)" << std::endl;
2084 } else {
2085 cerr << "Error: oid " << oid << " copy_from " << oid_src << " returned error code "
2086 << r << std::endl;
2087 ceph_abort();
2088 }
2089 } else {
11fdf7f2 2090 ceph_assert(!version || comp->get_version64() == version);
7c673cae
FG
2091 version = comp->get_version64();
2092 context->update_object_version(oid, comp->get_version64());
2093 }
2094 } else if (info->id == 1) {
2095 // racing read
11fdf7f2 2096 ceph_assert(comp_racing_read->is_complete());
7c673cae
FG
2097 cout << num << ": finishing copy_from racing read to " << context->prefix + oid << std::endl;
2098 if ((r = comp_racing_read->get_return_value())) {
2099 if (!(r == -ENOENT && src_value.deleted())) {
2100 cerr << "Error: oid " << oid << " copy_from " << oid_src << " returned error code "
2101 << r << std::endl;
2102 }
2103 } else {
11fdf7f2
TL
2104 ceph_assert(comp_racing_read->get_return_value() == 0);
2105 ceph_assert(!version || comp_racing_read->get_version64() == version);
7c673cae
FG
2106 version = comp_racing_read->get_version64();
2107 }
2108 }
2109 if (++done == 2) {
2110 context->oid_in_use.erase(oid);
2111 context->oid_not_in_use.insert(oid);
2112 context->oid_in_use.erase(oid_src);
2113 context->oid_not_in_use.insert(oid_src);
2114 context->kick();
2115 }
2116 }
2117
2118 bool finished() override
2119 {
2120 return done == 2;
2121 }
2122
2123 string getType() override
2124 {
2125 return "CopyFromOp";
2126 }
2127};
2128
11fdf7f2
TL
2129class ChunkReadOp : public TestOp {
2130public:
2131 vector<librados::AioCompletion *> completions;
2132 librados::ObjectReadOperation op;
2133 string oid;
2134 ObjectDesc old_value;
2135 ObjectDesc tgt_value;
2136 int snap;
2137 bool balance_reads;
9f95a23c 2138 bool localize_reads;
11fdf7f2
TL
2139
2140 std::shared_ptr<int> in_use;
2141
2142 vector<bufferlist> results;
2143 vector<int> retvals;
2144 vector<bool> is_sparse_read;
2145 uint64_t waiting_on;
2146
2147 vector<bufferlist> checksums;
2148 vector<int> checksum_retvals;
2149 uint32_t offset = 0;
2150 uint32_t length = 0;
2151 string tgt_oid;
2152 string tgt_pool_name;
2153 uint32_t tgt_offset = 0;
2154
2155 ChunkReadOp(int n,
2156 RadosTestContext *context,
2157 const string &oid,
2158 const string &tgt_pool_name,
2159 bool balance_reads,
9f95a23c 2160 bool localize_reads,
11fdf7f2
TL
2161 TestOpStat *stat = 0)
2162 : TestOp(n, context, stat),
2163 completions(2),
2164 oid(oid),
2165 snap(0),
2166 balance_reads(balance_reads),
9f95a23c 2167 localize_reads(localize_reads),
11fdf7f2
TL
2168 results(2),
2169 retvals(2),
2170 waiting_on(0),
2171 checksums(2),
2172 checksum_retvals(2),
2173 tgt_pool_name(tgt_pool_name)
2174 {}
2175
2176 void _do_read(librados::ObjectReadOperation& read_op, uint32_t offset, uint32_t length, int index) {
2177 read_op.read(offset,
2178 length,
2179 &results[index],
2180 &retvals[index]);
2181 if (index != 0) {
2182 bufferlist init_value_bl;
2183 encode(static_cast<uint32_t>(-1), init_value_bl);
2184 read_op.checksum(LIBRADOS_CHECKSUM_TYPE_CRC32C, init_value_bl, offset, length,
2185 0, &checksums[index], &checksum_retvals[index]);
2186 }
2187
2188 }
2189
2190 void _begin() override
2191 {
9f95a23c 2192 context->state_lock.lock();
11fdf7f2
TL
2193 std::cout << num << ": chunk read oid " << oid << " snap " << snap << std::endl;
2194 done = 0;
2195 for (uint32_t i = 0; i < 2; i++) {
9f95a23c 2196 completions[i] = context->rados.aio_create_completion((void *) this, &read_callback);
11fdf7f2
TL
2197 }
2198
2199 context->find_object(oid, &old_value);
2200
2201 if (old_value.chunk_info.size() == 0) {
2202 std::cout << ": no chunks" << std::endl;
2203 context->kick();
9f95a23c 2204 context->state_lock.unlock();
11fdf7f2
TL
2205 done = true;
2206 return;
2207 }
2208
2209 context->oid_in_use.insert(oid);
2210 context->oid_not_in_use.erase(oid);
2211 if (old_value.deleted()) {
2212 std::cout << num << ": expect deleted" << std::endl;
2213 } else {
2214 std::cout << num << ": expect " << old_value.most_recent() << std::endl;
2215 }
2216
2217 int rand_index = rand() % old_value.chunk_info.size();
2218 auto iter = old_value.chunk_info.begin();
2219 for (int i = 0; i < rand_index; i++) {
2220 iter++;
2221 }
2222 offset = iter->first;
2223 offset += (rand() % iter->second.length)/2;
2224 uint32_t t_length = rand() % iter->second.length;
2225 while (t_length + offset > iter->first + iter->second.length) {
2226 t_length = rand() % iter->second.length;
2227 }
2228 length = t_length;
2229 tgt_offset = iter->second.offset + offset - iter->first;
2230 tgt_oid = iter->second.oid;
2231
2232 std::cout << num << ": ori offset " << iter->first << " req offset " << offset
2233 << " ori length " << iter->second.length << " req length " << length
2234 << " ori tgt_offset " << iter->second.offset << " req tgt_offset " << tgt_offset
2235 << " tgt_oid " << tgt_oid << std::endl;
2236
2237 TestWatchContext *ctx = context->get_watch_context(oid);
9f95a23c 2238 context->state_lock.unlock();
11fdf7f2
TL
2239 if (ctx) {
2240 ceph_assert(old_value.exists);
2241 TestAlarm alarm;
2242 std::cerr << num << ": about to start" << std::endl;
2243 ctx->start();
2244 std::cerr << num << ": started" << std::endl;
2245 bufferlist bl;
2246 context->io_ctx.set_notify_timeout(600);
2247 int r = context->io_ctx.notify2(context->prefix+oid, bl, 0, NULL);
2248 if (r < 0) {
2249 std::cerr << "r is " << r << std::endl;
2250 ceph_abort();
2251 }
2252 std::cerr << num << ": notified, waiting" << std::endl;
2253 ctx->wait();
2254 }
9f95a23c 2255 std::lock_guard state_locker{context->state_lock};
11fdf7f2
TL
2256
2257 _do_read(op, offset, length, 0);
2258
2259 unsigned flags = 0;
2260 if (balance_reads)
2261 flags |= librados::OPERATION_BALANCE_READS;
9f95a23c
TL
2262 if (localize_reads)
2263 flags |= librados::OPERATION_LOCALIZE_READS;
11fdf7f2
TL
2264
2265 ceph_assert(!context->io_ctx.aio_operate(context->prefix+oid, completions[0], &op,
2266 flags, NULL));
2267 waiting_on++;
2268
2269 _do_read(op, tgt_offset, length, 1);
2270 ceph_assert(!context->io_ctx.aio_operate(context->prefix+tgt_oid, completions[1], &op,
2271 flags, NULL));
2272
2273 waiting_on++;
11fdf7f2
TL
2274 }
2275
2276 void _finish(CallbackInfo *info) override
2277 {
9f95a23c 2278 std::lock_guard l{context->state_lock};
11fdf7f2
TL
2279 ceph_assert(!done);
2280 ceph_assert(waiting_on > 0);
2281 if (--waiting_on) {
2282 return;
2283 }
2284
2285 context->oid_in_use.erase(oid);
2286 context->oid_not_in_use.insert(oid);
2287 int retval = completions[0]->get_return_value();
2288 std::cout << ": finish!! ret: " << retval << std::endl;
2289 context->find_object(tgt_oid, &tgt_value);
2290
2291 for (int i = 0; i < 2; i++) {
2292 ceph_assert(completions[i]->is_complete());
2293 int err = completions[i]->get_return_value();
2294 if (err != retval) {
2295 cerr << num << ": Error: oid " << oid << " read returned different error codes: "
2296 << retval << " and " << err << std::endl;
2297 ceph_abort();
2298 }
2299 if (err) {
2300 if (!(err == -ENOENT && old_value.deleted())) {
2301 cerr << num << ": Error: oid " << oid << " read returned error code "
2302 << err << std::endl;
2303 ceph_abort();
2304 }
2305 }
2306 }
2307
2308 if (!retval) {
2309 if (old_value.deleted()) {
2310 std::cout << num << ": expect deleted" << std::endl;
2311 ceph_abort_msg("expected deleted");
2312 } else {
2313 std::cout << num << ": expect " << old_value.most_recent() << std::endl;
2314 }
2315 if (tgt_value.has_contents()) {
2316 uint32_t checksum[2] = {0};
2317 if (checksum_retvals[1] == 0) {
2318 try {
2319 auto bl_it = checksums[1].cbegin();
2320 uint32_t csum_count;
2321 decode(csum_count, bl_it);
2322 decode(checksum[1], bl_it);
2323 } catch (const buffer::error &err) {
2324 checksum_retvals[1] = -EBADMSG;
2325 }
2326 }
2327
2328 if (checksum_retvals[1] != 0) {
2329 cerr << num << ": oid " << oid << " checksum retvals " << checksums[0]
2330 << " error " << std::endl;
2331 context->errors++;
2332 }
2333
2334 checksum[0] = results[0].crc32c(-1);
2335
2336 if (checksum[0] != checksum[1]) {
2337 cerr << num << ": oid " << oid << " checksum src " << checksum[0]
2338 << " chunksum tgt " << checksum[1] << " incorrect, expecting "
2339 << results[0].crc32c(-1)
2340 << std::endl;
2341 context->errors++;
2342 }
2343 if (context->errors) ceph_abort();
2344 }
2345 }
2346 for (vector<librados::AioCompletion *>::iterator it = completions.begin();
2347 it != completions.end(); ++it) {
2348 (*it)->release();
2349 }
2350 context->kick();
2351 done = true;
2352 }
2353
2354 bool finished() override
2355 {
2356 return done;
2357 }
2358
2359 string getType() override
2360 {
2361 return "ChunkReadOp";
2362 }
2363};
2364
2365class CopyOp : public TestOp {
2366public:
2367 string oid, oid_src, tgt_pool_name;
2368 librados::ObjectWriteOperation op;
2369 librados::ObjectReadOperation rd_op;
2370 librados::AioCompletion *comp;
2371 ObjectDesc src_value, tgt_value;
2372 int done;
2373 int r;
2374 CopyOp(int n,
2375 RadosTestContext *context,
2376 const string &oid_src,
2377 const string &oid,
2378 const string &tgt_pool_name,
2379 TestOpStat *stat = 0)
2380 : TestOp(n, context, stat),
2381 oid(oid), oid_src(oid_src), tgt_pool_name(tgt_pool_name),
2382 comp(NULL), done(0), r(0)
2383 {}
2384
2385 void _begin() override
2386 {
9f95a23c 2387 std::lock_guard l{context->state_lock};
11fdf7f2
TL
2388 context->oid_in_use.insert(oid_src);
2389 context->oid_not_in_use.erase(oid_src);
2390
2391 string src = context->prefix+oid_src;
2392 context->find_object(oid_src, &src_value);
2393 op.copy_from(src.c_str(), context->io_ctx, src_value.version, 0);
2394
2395 cout << "copy op oid " << oid_src << " to " << oid << " tgt_pool_name " << tgt_pool_name << std::endl;
2396
2397 pair<TestOp*, TestOp::CallbackInfo*> *cb_arg =
2398 new pair<TestOp*, TestOp::CallbackInfo*>(this,
2399 new TestOp::CallbackInfo(0));
9f95a23c 2400 comp = context->rados.aio_create_completion((void*) cb_arg, &write_callback);
11fdf7f2
TL
2401 if (tgt_pool_name == context->low_tier_pool_name) {
2402 context->low_tier_io_ctx.aio_operate(context->prefix+oid, comp, &op);
2403 } else {
2404 context->io_ctx.aio_operate(context->prefix+oid, comp, &op);
2405 }
2406 }
2407
2408 void _finish(CallbackInfo *info) override
2409 {
9f95a23c 2410 std::lock_guard l{context->state_lock};
11fdf7f2
TL
2411
2412 if (info->id == 0) {
2413 ceph_assert(comp->is_complete());
2414 cout << num << ": finishing copy op to oid " << oid << std::endl;
2415 if ((r = comp->get_return_value())) {
2416 cerr << "Error: oid " << oid << " write returned error code "
2417 << r << std::endl;
2418 ceph_abort();
2419 }
2420 }
2421
2422 if (++done == 1) {
2423 context->oid_in_use.erase(oid_src);
2424 context->oid_not_in_use.insert(oid_src);
2425 context->kick();
2426 }
2427 }
2428
2429 bool finished() override
2430 {
2431 return done == 1;
2432 }
2433
2434 string getType() override
2435 {
2436 return "CopyOp";
2437 }
2438};
2439
2440class SetChunkOp : public TestOp {
2441public:
20effc67 2442 string oid, oid_tgt;
11fdf7f2 2443 ObjectDesc src_value, tgt_value;
f67539c2 2444 librados::ObjectReadOperation op;
11fdf7f2 2445 librados::AioCompletion *comp;
11fdf7f2
TL
2446 int done;
2447 int r;
2448 uint64_t offset;
2449 uint32_t length;
20effc67
TL
2450 uint32_t tgt_offset;
2451 int snap;
2452 std::shared_ptr<int> in_use;
11fdf7f2
TL
2453 SetChunkOp(int n,
2454 RadosTestContext *context,
2455 const string &oid,
11fdf7f2 2456 const string &oid_tgt,
f67539c2 2457 TestOpStat *stat = 0)
11fdf7f2 2458 : TestOp(n, context, stat),
20effc67 2459 oid(oid), oid_tgt(oid_tgt),
11fdf7f2 2460 comp(NULL), done(0),
20effc67
TL
2461 r(0), offset(0), length(0),
2462 tgt_offset(0),
2463 snap(0)
11fdf7f2
TL
2464 {}
2465
20effc67
TL
2466 pair<uint64_t, uint64_t> get_rand_off_len(uint32_t max_len) {
2467 pair<uint64_t, uint64_t> r (0, 0);
2468 r.first = rand() % max_len;
2469 r.second = rand() % max_len;
2470 r.first = r.first - (r.first % 512);
2471 r.second = r.second - (r.second % 512);
2472
2473 while (r.first + r.second > max_len || r.second == 0) {
2474 r.first = rand() % max_len;
2475 r.second = rand() % max_len;
2476 r.first = r.first - (r.first % 512);
2477 r.second = r.second - (r.second % 512);
2478 }
2479 return r;
2480 }
2481
11fdf7f2
TL
2482 void _begin() override
2483 {
9f95a23c 2484 std::lock_guard l{context->state_lock};
20effc67
TL
2485 if (!(rand() % 4) && !context->snaps.empty()) {
2486 snap = rand_choose(context->snaps)->first;
2487 in_use = context->snaps_in_use.lookup_or_create(snap, snap);
2488 } else {
2489 snap = -1;
2490 }
11fdf7f2
TL
2491 context->oid_in_use.insert(oid);
2492 context->oid_not_in_use.erase(oid);
2493
20effc67 2494 context->find_object(oid, &src_value, snap);
11fdf7f2
TL
2495 context->find_object(oid_tgt, &tgt_value);
2496
20effc67
TL
2497 uint32_t max_len = 0;
2498 if (src_value.deleted()) {
2499 /* just random length to check ENOENT */
2500 max_len = context->max_size;
2501 } else {
2502 max_len = src_value.most_recent_gen()->get_length(src_value.most_recent());
2503 }
2504 pair<uint64_t, uint64_t> off_len; // first: offset, second: length
2505 if (snap >= 0) {
2506 context->io_ctx.snap_set_read(context->snaps[snap]);
2507 off_len = get_rand_off_len(max_len);
2508 } else if (src_value.version != 0 && !src_value.deleted()) {
11fdf7f2 2509 op.assert_version(src_value.version);
20effc67
TL
2510 off_len = get_rand_off_len(max_len);
2511 } else if (src_value.deleted()) {
2512 off_len.first = 0;
2513 off_len.second = max_len;
2514 }
2515 offset = off_len.first;
2516 length = off_len.second;
2517 tgt_offset = offset;
2518
2519 string target_oid;
2520 if (!src_value.deleted() && oid_tgt.empty()) {
2521 bufferlist bl;
2522 int r = context->io_ctx.read(context->prefix+oid, bl, length, offset);
2523 ceph_assert(r > 0);
2524 string fp_oid = ceph::crypto::digest<ceph::crypto::SHA256>(bl).to_str();
2525 r = context->low_tier_io_ctx.write(fp_oid, bl, bl.length(), 0);
2526 ceph_assert(r == 0);
2527 target_oid = fp_oid;
2528 tgt_offset = 0;
2529 } else {
2530 target_oid = context->prefix+oid_tgt;
2531 }
2532
2533 cout << num << ": " << "set_chunk oid " << oid << " offset: " << offset
2534 << " length: " << length << " target oid " << target_oid
2535 << " offset: " << tgt_offset << " snap " << snap << std::endl;
2536
f67539c2 2537 op.set_chunk(offset, length, context->low_tier_io_ctx,
20effc67 2538 target_oid, tgt_offset, CEPH_OSD_OP_FLAG_WITH_REFERENCE);
11fdf7f2
TL
2539
2540 pair<TestOp*, TestOp::CallbackInfo*> *cb_arg =
2541 new pair<TestOp*, TestOp::CallbackInfo*>(this,
2542 new TestOp::CallbackInfo(0));
9f95a23c 2543 comp = context->rados.aio_create_completion((void*) cb_arg,
11fdf7f2
TL
2544 &write_callback);
2545 context->io_ctx.aio_operate(context->prefix+oid, comp, &op,
f67539c2 2546 librados::OPERATION_ORDER_READS_WRITES, NULL);
20effc67
TL
2547 if (snap >= 0) {
2548 context->io_ctx.snap_set_read(0);
2549 }
11fdf7f2
TL
2550 }
2551
2552 void _finish(CallbackInfo *info) override
2553 {
9f95a23c 2554 std::lock_guard l{context->state_lock};
11fdf7f2
TL
2555
2556 if (info->id == 0) {
2557 ceph_assert(comp->is_complete());
2558 cout << num << ": finishing set_chunk to oid " << oid << std::endl;
2559 if ((r = comp->get_return_value())) {
2560 if (r == -ENOENT && src_value.deleted()) {
2561 cout << num << ": got expected ENOENT (src dne)" << std::endl;
20effc67
TL
2562 } else if (r == -ENOENT && context->oid_set_chunk_tgt_pool.find(oid_tgt) !=
2563 context->oid_set_chunk_tgt_pool.end()) {
2564 cout << num << ": get expected ENOENT tgt oid " << oid_tgt << std::endl;
2565 } else if (r == -ERANGE && src_value.deleted()) {
2566 cout << num << ": got expected ERANGE (src dne)" << std::endl;
11fdf7f2 2567 } else if (r == -EOPNOTSUPP) {
20effc67
TL
2568 cout << "Range is overlapped: oid " << oid << " set_chunk " << oid_tgt << " returned error code "
2569 << r << " offset: " << offset << " length: " << length << std::endl;
2570 context->update_object_version(oid, comp->get_version64());
11fdf7f2
TL
2571 } else {
2572 cerr << "Error: oid " << oid << " set_chunk " << oid_tgt << " returned error code "
2573 << r << std::endl;
2574 ceph_abort();
2575 }
2576 } else {
20effc67
TL
2577 if (snap == -1) {
2578 ChunkDesc info {tgt_offset, length, oid_tgt};
2579 context->update_object_chunk_target(oid, offset, info);
2580 context->update_object_version(oid, comp->get_version64());
2581 }
11fdf7f2
TL
2582 }
2583 }
2584
2585 if (++done == 1) {
20effc67 2586 context->oid_set_chunk_tgt_pool.insert(oid_tgt);
11fdf7f2
TL
2587 context->oid_in_use.erase(oid);
2588 context->oid_not_in_use.insert(oid);
2589 context->kick();
2590 }
2591 }
2592
2593 bool finished() override
2594 {
2595 return done == 1;
2596 }
2597
2598 string getType() override
2599 {
2600 return "SetChunkOp";
2601 }
2602};
2603
31f18b77
FG
2604class SetRedirectOp : public TestOp {
2605public:
2606 string oid, oid_tgt, tgt_pool_name;
2607 ObjectDesc src_value, tgt_value;
2608 librados::ObjectWriteOperation op;
2609 librados::ObjectReadOperation rd_op;
2610 librados::AioCompletion *comp;
11fdf7f2 2611 std::shared_ptr<int> in_use;
31f18b77
FG
2612 int done;
2613 int r;
2614 SetRedirectOp(int n,
2615 RadosTestContext *context,
2616 const string &oid,
2617 const string &oid_tgt,
2618 const string &tgt_pool_name,
2619 TestOpStat *stat = 0)
2620 : TestOp(n, context, stat),
2621 oid(oid), oid_tgt(oid_tgt), tgt_pool_name(tgt_pool_name),
2622 comp(NULL), done(0),
2623 r(0)
2624 {}
2625
2626 void _begin() override
2627 {
9f95a23c 2628 std::lock_guard l{context->state_lock};
31f18b77
FG
2629 context->oid_in_use.insert(oid);
2630 context->oid_not_in_use.erase(oid);
2631 context->oid_redirect_in_use.insert(oid_tgt);
2632 context->oid_redirect_not_in_use.erase(oid_tgt);
2633
11fdf7f2
TL
2634 if (tgt_pool_name.empty()) ceph_abort();
2635
31f18b77
FG
2636 context->find_object(oid, &src_value);
2637 if(!context->redirect_objs[oid].empty()) {
11fdf7f2 2638 /* copy_from oid --> oid_tgt */
31f18b77 2639 comp = context->rados.aio_create_completion();
11fdf7f2
TL
2640 string src = context->prefix+oid;
2641 op.copy_from(src.c_str(), context->io_ctx, src_value.version, 0);
2642 context->low_tier_io_ctx.aio_operate(context->prefix+oid_tgt, comp, &op,
2643 librados::OPERATION_ORDER_READS_WRITES);
9f95a23c 2644 comp->wait_for_complete();
11fdf7f2
TL
2645 if ((r = comp->get_return_value())) {
2646 cerr << "Error: oid " << oid << " copy_from " << oid_tgt << " returned error code "
2647 << r << std::endl;
2648 ceph_abort();
2649 }
31f18b77
FG
2650 comp->release();
2651
2652 /* unset redirect target */
2653 comp = context->rados.aio_create_completion();
2654 bool present = !src_value.deleted();
f67539c2 2655 op.unset_manifest();
31f18b77
FG
2656 context->io_ctx.aio_operate(context->prefix+oid, comp, &op,
2657 librados::OPERATION_ORDER_READS_WRITES |
2658 librados::OPERATION_IGNORE_REDIRECT);
9f95a23c 2659 comp->wait_for_complete();
31f18b77 2660 if ((r = comp->get_return_value())) {
f67539c2 2661 if (!(r == -ENOENT && !present) && r != -EOPNOTSUPP) {
31f18b77
FG
2662 cerr << "r is " << r << " while deleting " << oid << " and present is " << present << std::endl;
2663 ceph_abort();
2664 }
2665 }
2666 comp->release();
2667
2668 context->oid_redirect_not_in_use.insert(context->redirect_objs[oid]);
2669 context->oid_redirect_in_use.erase(context->redirect_objs[oid]);
31f18b77
FG
2670 }
2671
2672 comp = context->rados.aio_create_completion();
2673 rd_op.stat(NULL, NULL, NULL);
2674 context->io_ctx.aio_operate(context->prefix+oid, comp, &rd_op,
2675 librados::OPERATION_ORDER_READS_WRITES |
2676 librados::OPERATION_IGNORE_REDIRECT,
2677 NULL);
9f95a23c 2678 comp->wait_for_complete();
31f18b77
FG
2679 if ((r = comp->get_return_value()) && !src_value.deleted()) {
2680 cerr << "Error: oid " << oid << " stat returned error code "
2681 << r << std::endl;
2682 ceph_abort();
2683 }
2684 context->update_object_version(oid, comp->get_version64());
2685 comp->release();
2686
11fdf7f2
TL
2687 comp = context->rados.aio_create_completion();
2688 rd_op.stat(NULL, NULL, NULL);
2689 context->low_tier_io_ctx.aio_operate(context->prefix+oid_tgt, comp, &rd_op,
2690 librados::OPERATION_ORDER_READS_WRITES |
2691 librados::OPERATION_IGNORE_REDIRECT,
2692 NULL);
9f95a23c 2693 comp->wait_for_complete();
11fdf7f2
TL
2694 if ((r = comp->get_return_value())) {
2695 cerr << "Error: oid " << oid_tgt << " stat returned error code "
2696 << r << std::endl;
2697 ceph_abort();
2698 }
2699 uint64_t tgt_version = comp->get_version64();
2700 comp->release();
2701
2702
31f18b77 2703 context->find_object(oid, &src_value);
31f18b77
FG
2704
2705 if (src_value.version != 0 && !src_value.deleted())
2706 op.assert_version(src_value.version);
11fdf7f2 2707 op.set_redirect(context->prefix+oid_tgt, context->low_tier_io_ctx, tgt_version);
31f18b77
FG
2708
2709 pair<TestOp*, TestOp::CallbackInfo*> *cb_arg =
2710 new pair<TestOp*, TestOp::CallbackInfo*>(this,
2711 new TestOp::CallbackInfo(0));
9f95a23c 2712 comp = context->rados.aio_create_completion((void*) cb_arg, &write_callback);
31f18b77
FG
2713 context->io_ctx.aio_operate(context->prefix+oid, comp, &op,
2714 librados::OPERATION_ORDER_READS_WRITES);
2715 }
2716
2717 void _finish(CallbackInfo *info) override
2718 {
9f95a23c 2719 std::lock_guard l{context->state_lock};
31f18b77
FG
2720
2721 if (info->id == 0) {
11fdf7f2 2722 ceph_assert(comp->is_complete());
31f18b77
FG
2723 cout << num << ": finishing set_redirect to oid " << oid << std::endl;
2724 if ((r = comp->get_return_value())) {
2725 if (r == -ENOENT && src_value.deleted()) {
2726 cout << num << ": got expected ENOENT (src dne)" << std::endl;
2727 } else {
2728 cerr << "Error: oid " << oid << " set_redirect " << oid_tgt << " returned error code "
2729 << r << std::endl;
2730 ceph_abort();
2731 }
2732 } else {
2733 context->update_object_redirect_target(oid, oid_tgt);
2734 context->update_object_version(oid, comp->get_version64());
2735 }
2736 }
2737
2738 if (++done == 1) {
2739 context->oid_in_use.erase(oid);
2740 context->oid_not_in_use.insert(oid);
2741 context->kick();
2742 }
2743 }
2744
2745 bool finished() override
2746 {
2747 return done == 1;
2748 }
2749
2750 string getType() override
2751 {
2752 return "SetRedirectOp";
2753 }
2754};
2755
2756class UnsetRedirectOp : public TestOp {
2757public:
2758 string oid;
2759 librados::ObjectWriteOperation op;
11fdf7f2 2760 librados::AioCompletion *comp = nullptr;
31f18b77
FG
2761
2762 UnsetRedirectOp(int n,
2763 RadosTestContext *context,
2764 const string &oid,
2765 TestOpStat *stat = 0)
2766 : TestOp(n, context, stat), oid(oid)
2767 {}
2768
2769 void _begin() override
2770 {
9f95a23c 2771 std::unique_lock state_locker{context->state_lock};
31f18b77
FG
2772 if (context->get_watch_context(oid)) {
2773 context->kick();
31f18b77
FG
2774 return;
2775 }
2776
2777 ObjectDesc contents;
2778 context->find_object(oid, &contents);
2779 bool present = !contents.deleted();
2780
2781 context->oid_in_use.insert(oid);
2782 context->oid_not_in_use.erase(oid);
2783 context->seq_num++;
2784
2785 context->remove_object(oid);
2786
9f95a23c 2787 state_locker.unlock();
31f18b77
FG
2788
2789 comp = context->rados.aio_create_completion();
2790 op.remove();
2791 context->io_ctx.aio_operate(context->prefix+oid, comp, &op,
2792 librados::OPERATION_ORDER_READS_WRITES |
2793 librados::OPERATION_IGNORE_REDIRECT);
9f95a23c 2794 comp->wait_for_complete();
31f18b77
FG
2795 int r = comp->get_return_value();
2796 if (r && !(r == -ENOENT && !present)) {
2797 cerr << "r is " << r << " while deleting " << oid << " and present is " << present << std::endl;
2798 ceph_abort();
2799 }
9f95a23c 2800 state_locker.lock();
31f18b77
FG
2801 context->oid_in_use.erase(oid);
2802 context->oid_not_in_use.insert(oid);
2803 if(!context->redirect_objs[oid].empty()) {
2804 context->oid_redirect_not_in_use.insert(context->redirect_objs[oid]);
2805 context->oid_redirect_in_use.erase(context->redirect_objs[oid]);
2806 context->update_object_redirect_target(oid, string());
2807 }
2808 context->kick();
31f18b77
FG
2809 }
2810
2811 string getType() override
2812 {
2813 return "UnsetRedirectOp";
2814 }
2815};
2816
11fdf7f2
TL
2817class TierPromoteOp : public TestOp {
2818public:
2819 librados::AioCompletion *completion;
2820 librados::ObjectWriteOperation op;
2821 string oid;
2822 std::shared_ptr<int> in_use;
20effc67 2823 ObjectDesc src_value;
11fdf7f2
TL
2824
2825 TierPromoteOp(int n,
2826 RadosTestContext *context,
2827 const string &oid,
2828 TestOpStat *stat)
2829 : TestOp(n, context, stat),
2830 completion(NULL),
2831 oid(oid)
2832 {}
2833
2834 void _begin() override
2835 {
9f95a23c 2836 context->state_lock.lock();
11fdf7f2
TL
2837
2838 context->oid_in_use.insert(oid);
2839 context->oid_not_in_use.erase(oid);
2840
20effc67
TL
2841 context->find_object(oid, &src_value);
2842
11fdf7f2
TL
2843 pair<TestOp*, TestOp::CallbackInfo*> *cb_arg =
2844 new pair<TestOp*, TestOp::CallbackInfo*>(this,
2845 new TestOp::CallbackInfo(0));
9f95a23c 2846 completion = context->rados.aio_create_completion((void *) cb_arg,
11fdf7f2 2847 &write_callback);
9f95a23c 2848 context->state_lock.unlock();
11fdf7f2
TL
2849
2850 op.tier_promote();
2851 int r = context->io_ctx.aio_operate(context->prefix+oid, completion,
2852 &op);
2853 ceph_assert(!r);
2854 }
2855
2856 void _finish(CallbackInfo *info) override
2857 {
9f95a23c 2858 std::lock_guard l{context->state_lock};
11fdf7f2
TL
2859 ceph_assert(!done);
2860 ceph_assert(completion->is_complete());
2861
2862 ObjectDesc oid_value;
2863 context->find_object(oid, &oid_value);
2864 int r = completion->get_return_value();
2865 cout << num << ": got " << cpp_strerror(r) << std::endl;
2866 if (r == 0) {
2867 // sucess
20effc67
TL
2868 } else if (r == -ENOENT && src_value.deleted()) {
2869 cout << num << ": got expected ENOENT (src dne)" << std::endl;
11fdf7f2
TL
2870 } else {
2871 ceph_abort_msg("shouldn't happen");
2872 }
2873 context->update_object_version(oid, completion->get_version64());
2874 context->find_object(oid, &oid_value);
2875 context->oid_in_use.erase(oid);
2876 context->oid_not_in_use.insert(oid);
2877 context->kick();
2878 done = true;
11fdf7f2
TL
2879 }
2880
2881 bool finished() override
2882 {
2883 return done;
2884 }
2885
2886 string getType() override
2887 {
2888 return "TierPromoteOp";
2889 }
2890};
2891
9f95a23c
TL
2892class TierFlushOp : public TestOp {
2893public:
2894 librados::AioCompletion *completion;
f67539c2 2895 librados::ObjectReadOperation op;
9f95a23c
TL
2896 string oid;
2897 std::shared_ptr<int> in_use;
20effc67
TL
2898 int snap;
2899 ObjectDesc src_value;
2900
9f95a23c
TL
2901
2902 TierFlushOp(int n,
2903 RadosTestContext *context,
2904 const string &oid,
2905 TestOpStat *stat)
2906 : TestOp(n, context, stat),
2907 completion(NULL),
20effc67
TL
2908 oid(oid),
2909 snap(-1)
9f95a23c
TL
2910 {}
2911
2912 void _begin() override
2913 {
2914 context->state_lock.lock();
2915
2916 context->oid_in_use.insert(oid);
2917 context->oid_not_in_use.erase(oid);
2918
20effc67
TL
2919 if (0 && !(rand() % 4) && !context->snaps.empty()) {
2920 snap = rand_choose(context->snaps)->first;
2921 in_use = context->snaps_in_use.lookup_or_create(snap, snap);
2922 } else {
2923 snap = -1;
2924 }
2925
2926 cout << num << ": tier_flush oid " << oid << " snap " << snap << std::endl;
2927
2928 if (snap >= 0) {
2929 context->io_ctx.snap_set_read(context->snaps[snap]);
2930 }
2931
2932 context->find_object(oid, &src_value, snap);
2933
9f95a23c
TL
2934 pair<TestOp*, TestOp::CallbackInfo*> *cb_arg =
2935 new pair<TestOp*, TestOp::CallbackInfo*>(this,
2936 new TestOp::CallbackInfo(0));
2937 completion = context->rados.aio_create_completion((void *) cb_arg,
2938 &write_callback);
2939 context->state_lock.unlock();
2940
2941 op.tier_flush();
f67539c2 2942 unsigned flags = librados::OPERATION_IGNORE_CACHE;
9f95a23c 2943 int r = context->io_ctx.aio_operate(context->prefix+oid, completion,
f67539c2 2944 &op, flags, NULL);
9f95a23c 2945 ceph_assert(!r);
20effc67
TL
2946
2947 if (snap >= 0) {
2948 context->io_ctx.snap_set_read(0);
2949 }
9f95a23c
TL
2950 }
2951
2952 void _finish(CallbackInfo *info) override
2953 {
2954 context->state_lock.lock();
2955 ceph_assert(!done);
2956 ceph_assert(completion->is_complete());
2957
2958 int r = completion->get_return_value();
2959 cout << num << ": got " << cpp_strerror(r) << std::endl;
2960 if (r == 0) {
2961 // sucess
20effc67
TL
2962 context->update_object_tier_flushed(oid, snap);
2963 context->update_object_version(oid, completion->get_version64(), snap);
2964 } else if (r == -EBUSY) {
2965 // could fail if snap is not oldest
2966 ceph_assert(!context->check_oldest_snap_flushed(oid, snap));
2967 } else if (r == -ENOENT) {
2968 // could fail if object is removed
2969 if (src_value.deleted()) {
2970 cout << num << ": got expected ENOENT (src dne)" << std::endl;
2971 } else {
2972 cerr << num << ": got unexpected ENOENT" << std::endl;
2973 ceph_abort();
2974 }
9f95a23c 2975 } else {
20effc67
TL
2976 if (r != -ENOENT && src_value.deleted()) {
2977 cerr << num << ": src dne, but r is not ENOENT" << std::endl;
2978 }
9f95a23c
TL
2979 ceph_abort_msg("shouldn't happen");
2980 }
9f95a23c
TL
2981 context->oid_in_use.erase(oid);
2982 context->oid_not_in_use.insert(oid);
2983 context->kick();
2984 done = true;
2985 context->state_lock.unlock();
2986 }
2987
2988 bool finished() override
2989 {
2990 return done;
2991 }
2992
2993 string getType() override
2994 {
2995 return "TierFlushOp";
2996 }
2997};
2998
20effc67
TL
2999class TierEvictOp : public TestOp {
3000public:
3001 librados::AioCompletion *completion;
3002 librados::ObjectReadOperation op;
3003 string oid;
3004 std::shared_ptr<int> in_use;
3005 int snap;
3006 ObjectDesc src_value;
3007
3008 TierEvictOp(int n,
3009 RadosTestContext *context,
3010 const string &oid,
3011 TestOpStat *stat)
3012 : TestOp(n, context, stat),
3013 completion(NULL),
3014 oid(oid),
3015 snap(-1)
3016 {}
3017
3018 void _begin() override
3019 {
3020 context->state_lock.lock();
3021
3022 context->oid_in_use.insert(oid);
3023 context->oid_not_in_use.erase(oid);
3024
3025 if (0 && !(rand() % 4) && !context->snaps.empty()) {
3026 snap = rand_choose(context->snaps)->first;
3027 in_use = context->snaps_in_use.lookup_or_create(snap, snap);
3028 } else {
3029 snap = -1;
3030 }
3031
3032 cout << num << ": tier_evict oid " << oid << " snap " << snap << std::endl;
3033
3034 if (snap >= 0) {
3035 context->io_ctx.snap_set_read(context->snaps[snap]);
3036 }
3037
3038 context->find_object(oid, &src_value, snap);
3039
3040 pair<TestOp*, TestOp::CallbackInfo*> *cb_arg =
3041 new pair<TestOp*, TestOp::CallbackInfo*>(this,
3042 new TestOp::CallbackInfo(0));
3043 completion = context->rados.aio_create_completion((void *) cb_arg,
3044 &write_callback);
3045 context->state_lock.unlock();
3046
3047 op.cache_evict();
3048 int r = context->io_ctx.aio_operate(context->prefix+oid, completion,
3049 &op, librados::OPERATION_IGNORE_CACHE,
3050 NULL);
3051 ceph_assert(!r);
3052
3053 if (snap >= 0) {
3054 context->io_ctx.snap_set_read(0);
3055 }
3056 }
3057
3058 void _finish(CallbackInfo *info) override
3059 {
3060 std::lock_guard state_locker{context->state_lock};
3061 ceph_assert(!done);
3062 ceph_assert(completion->is_complete());
3063
3064 int r = completion->get_return_value();
3065 cout << num << ": got " << cpp_strerror(r) << std::endl;
3066 if (r == 0) {
3067 // ok
3068 } else if (r == -EINVAL) {
3069 // modifying manifest object makes existing chunk_map clear
3070 // as a result, the modified object is no longer manifest object
3071 // this casues to return -EINVAL
3072 } else if (r == -ENOENT) {
3073 // could fail if object is removed
3074 if (src_value.deleted()) {
3075 cout << num << ": got expected ENOENT (src dne)" << std::endl;
3076 } else {
3077 cerr << num << ": got unexpected ENOENT" << std::endl;
3078 ceph_abort();
3079 }
3080 } else {
3081 if (r != -ENOENT && src_value.deleted()) {
3082 cerr << num << ": src dne, but r is not ENOENT" << std::endl;
3083 }
3084 ceph_abort_msg("shouldn't happen");
3085 }
3086 context->oid_in_use.erase(oid);
3087 context->oid_not_in_use.insert(oid);
3088 context->kick();
3089 done = true;
3090 }
3091
3092 bool finished() override
3093 {
3094 return done;
3095 }
3096
3097 string getType() override
3098 {
3099 return "TierEvictOp";
3100 }
3101};
3102
7c673cae
FG
3103class HitSetListOp : public TestOp {
3104 librados::AioCompletion *comp1, *comp2;
3105 uint32_t hash;
3106 std::list< std::pair<time_t, time_t> > ls;
3107 bufferlist bl;
3108
3109public:
3110 HitSetListOp(int n,
3111 RadosTestContext *context,
3112 uint32_t hash,
3113 TestOpStat *stat = 0)
3114 : TestOp(n, context, stat),
3115 comp1(NULL), comp2(NULL),
3116 hash(hash)
3117 {}
3118
3119 void _begin() override
3120 {
3121 pair<TestOp*, TestOp::CallbackInfo*> *cb_arg =
3122 new pair<TestOp*, TestOp::CallbackInfo*>(this,
3123 new TestOp::CallbackInfo(0));
9f95a23c 3124 comp1 = context->rados.aio_create_completion((void*) cb_arg,
7c673cae
FG
3125 &write_callback);
3126 int r = context->io_ctx.hit_set_list(hash, comp1, &ls);
11fdf7f2 3127 ceph_assert(r == 0);
7c673cae
FG
3128 }
3129
3130 void _finish(CallbackInfo *info) override {
9f95a23c 3131 std::lock_guard l{context->state_lock};
7c673cae
FG
3132 if (!comp2) {
3133 if (ls.empty()) {
3134 cerr << num << ": no hitsets" << std::endl;
3135 done = true;
3136 } else {
3137 cerr << num << ": hitsets are " << ls << std::endl;
3138 int r = rand() % ls.size();
3139 std::list<pair<time_t,time_t> >::iterator p = ls.begin();
3140 while (r--)
3141 ++p;
3142 pair<TestOp*, TestOp::CallbackInfo*> *cb_arg =
3143 new pair<TestOp*, TestOp::CallbackInfo*>(this,
3144 new TestOp::CallbackInfo(0));
9f95a23c 3145 comp2 = context->rados.aio_create_completion((void*) cb_arg, &write_callback);
7c673cae 3146 r = context->io_ctx.hit_set_get(hash, comp2, p->second, &bl);
11fdf7f2 3147 ceph_assert(r == 0);
7c673cae
FG
3148 }
3149 } else {
3150 int r = comp2->get_return_value();
3151 if (r == 0) {
3152 HitSet hitset;
11fdf7f2
TL
3153 auto p = bl.cbegin();
3154 decode(hitset, p);
7c673cae
FG
3155 cout << num << ": got hitset of type " << hitset.get_type_name()
3156 << " size " << bl.length()
3157 << std::endl;
3158 } else {
3159 // FIXME: we could verify that we did in fact race with a trim...
11fdf7f2 3160 ceph_assert(r == -ENOENT);
7c673cae
FG
3161 }
3162 done = true;
3163 }
3164
3165 context->kick();
3166 }
3167
3168 bool finished() override {
3169 return done;
3170 }
3171
3172 string getType() override {
3173 return "HitSetListOp";
3174 }
3175};
3176
3177class UndirtyOp : public TestOp {
3178public:
3179 librados::AioCompletion *completion;
3180 librados::ObjectWriteOperation op;
3181 string oid;
3182
3183 UndirtyOp(int n,
3184 RadosTestContext *context,
3185 const string &oid,
3186 TestOpStat *stat = 0)
3187 : TestOp(n, context, stat),
3188 completion(NULL),
3189 oid(oid)
3190 {}
3191
3192 void _begin() override
3193 {
9f95a23c 3194 context->state_lock.lock();
7c673cae
FG
3195 pair<TestOp*, TestOp::CallbackInfo*> *cb_arg =
3196 new pair<TestOp*, TestOp::CallbackInfo*>(this,
3197 new TestOp::CallbackInfo(0));
9f95a23c 3198 completion = context->rados.aio_create_completion((void *) cb_arg,
7c673cae
FG
3199 &write_callback);
3200
3201 context->oid_in_use.insert(oid);
3202 context->oid_not_in_use.erase(oid);
3203 context->update_object_undirty(oid);
9f95a23c 3204 context->state_lock.unlock();
7c673cae
FG
3205
3206 op.undirty();
3207 int r = context->io_ctx.aio_operate(context->prefix+oid, completion,
3208 &op, 0);
11fdf7f2 3209 ceph_assert(!r);
7c673cae
FG
3210 }
3211
3212 void _finish(CallbackInfo *info) override
3213 {
9f95a23c 3214 std::lock_guard state_locker{context->state_lock};
11fdf7f2
TL
3215 ceph_assert(!done);
3216 ceph_assert(completion->is_complete());
7c673cae
FG
3217 context->oid_in_use.erase(oid);
3218 context->oid_not_in_use.insert(oid);
3219 context->update_object_version(oid, completion->get_version64());
3220 context->kick();
3221 done = true;
7c673cae
FG
3222 }
3223
3224 bool finished() override
3225 {
3226 return done;
3227 }
3228
3229 string getType() override
3230 {
3231 return "UndirtyOp";
3232 }
3233};
3234
3235class IsDirtyOp : public TestOp {
3236public:
3237 librados::AioCompletion *completion;
3238 librados::ObjectReadOperation op;
3239 string oid;
3240 bool dirty;
3241 ObjectDesc old_value;
11fdf7f2
TL
3242 int snap = 0;
3243 std::shared_ptr<int> in_use;
7c673cae
FG
3244
3245 IsDirtyOp(int n,
3246 RadosTestContext *context,
3247 const string &oid,
3248 TestOpStat *stat = 0)
3249 : TestOp(n, context, stat),
3250 completion(NULL),
3251 oid(oid),
3252 dirty(false)
3253 {}
3254
3255 void _begin() override
3256 {
9f95a23c 3257 context->state_lock.lock();
7c673cae
FG
3258
3259 if (!(rand() % 4) && !context->snaps.empty()) {
3260 snap = rand_choose(context->snaps)->first;
3261 in_use = context->snaps_in_use.lookup_or_create(snap, snap);
3262 } else {
3263 snap = -1;
3264 }
3265 std::cout << num << ": is_dirty oid " << oid << " snap " << snap
3266 << std::endl;
3267
3268 pair<TestOp*, TestOp::CallbackInfo*> *cb_arg =
3269 new pair<TestOp*, TestOp::CallbackInfo*>(this,
3270 new TestOp::CallbackInfo(0));
9f95a23c 3271 completion = context->rados.aio_create_completion((void *) cb_arg,
7c673cae
FG
3272 &write_callback);
3273
3274 context->oid_in_use.insert(oid);
3275 context->oid_not_in_use.erase(oid);
9f95a23c 3276 context->state_lock.unlock();
7c673cae
FG
3277
3278 if (snap >= 0) {
3279 context->io_ctx.snap_set_read(context->snaps[snap]);
3280 }
3281
3282 op.is_dirty(&dirty, NULL);
3283 int r = context->io_ctx.aio_operate(context->prefix+oid, completion,
3284 &op, 0);
11fdf7f2 3285 ceph_assert(!r);
7c673cae
FG
3286
3287 if (snap >= 0) {
3288 context->io_ctx.snap_set_read(0);
3289 }
3290 }
3291
3292 void _finish(CallbackInfo *info) override
3293 {
9f95a23c 3294 std::lock_guard state_locker{context->state_lock};
11fdf7f2
TL
3295 ceph_assert(!done);
3296 ceph_assert(completion->is_complete());
7c673cae
FG
3297 context->oid_in_use.erase(oid);
3298 context->oid_not_in_use.insert(oid);
3299
11fdf7f2 3300 ceph_assert(context->find_object(oid, &old_value, snap));
7c673cae
FG
3301
3302 int r = completion->get_return_value();
3303 if (r == 0) {
3304 cout << num << ": " << (dirty ? "dirty" : "clean") << std::endl;
11fdf7f2
TL
3305 ceph_assert(!old_value.deleted());
3306 ceph_assert(dirty == old_value.dirty);
7c673cae
FG
3307 } else {
3308 cout << num << ": got " << r << std::endl;
11fdf7f2
TL
3309 ceph_assert(r == -ENOENT);
3310 ceph_assert(old_value.deleted());
7c673cae
FG
3311 }
3312 context->kick();
3313 done = true;
7c673cae
FG
3314 }
3315
3316 bool finished() override
3317 {
3318 return done;
3319 }
3320
3321 string getType() override
3322 {
3323 return "IsDirtyOp";
3324 }
3325};
3326
3327
3328
3329class CacheFlushOp : public TestOp {
3330public:
3331 librados::AioCompletion *completion;
3332 librados::ObjectReadOperation op;
3333 string oid;
3334 bool blocking;
3335 int snap;
3336 bool can_fail;
11fdf7f2 3337 std::shared_ptr<int> in_use;
7c673cae
FG
3338
3339 CacheFlushOp(int n,
3340 RadosTestContext *context,
3341 const string &oid,
3342 TestOpStat *stat,
3343 bool b)
3344 : TestOp(n, context, stat),
3345 completion(NULL),
3346 oid(oid),
3347 blocking(b),
3348 snap(0),
3349 can_fail(false)
3350 {}
3351
3352 void _begin() override
3353 {
9f95a23c 3354 context->state_lock.lock();
7c673cae
FG
3355
3356 if (!(rand() % 4) && !context->snaps.empty()) {
3357 snap = rand_choose(context->snaps)->first;
3358 in_use = context->snaps_in_use.lookup_or_create(snap, snap);
3359 } else {
3360 snap = -1;
3361 }
3362 // not being particularly specific here about knowing which
3363 // flushes are on the oldest clean snap and which ones are not.
3364 can_fail = !blocking || !context->snaps.empty();
31f18b77 3365 // FIXME: we could fail if we've ever removed a snap due to
7c673cae
FG
3366 // the async snap trimming.
3367 can_fail = true;
3368 cout << num << ": " << (blocking ? "cache_flush" : "cache_try_flush")
3369 << " oid " << oid << " snap " << snap << std::endl;
3370
3371 if (snap >= 0) {
3372 context->io_ctx.snap_set_read(context->snaps[snap]);
3373 }
3374
3375 pair<TestOp*, TestOp::CallbackInfo*> *cb_arg =
3376 new pair<TestOp*, TestOp::CallbackInfo*>(this,
3377 new TestOp::CallbackInfo(0));
9f95a23c 3378 completion = context->rados.aio_create_completion((void *) cb_arg,
7c673cae 3379 &write_callback);
7c673cae
FG
3380 context->oid_flushing.insert(oid);
3381 context->oid_not_flushing.erase(oid);
9f95a23c 3382 context->state_lock.unlock();
7c673cae
FG
3383
3384 unsigned flags = librados::OPERATION_IGNORE_CACHE;
3385 if (blocking) {
3386 op.cache_flush();
3387 } else {
3388 op.cache_try_flush();
3389 flags = librados::OPERATION_SKIPRWLOCKS;
3390 }
3391 int r = context->io_ctx.aio_operate(context->prefix+oid, completion,
3392 &op, flags, NULL);
11fdf7f2 3393 ceph_assert(!r);
7c673cae
FG
3394
3395 if (snap >= 0) {
3396 context->io_ctx.snap_set_read(0);
3397 }
3398 }
3399
3400 void _finish(CallbackInfo *info) override
3401 {
9f95a23c 3402 std::lock_guard state_locker{context->state_lock};
11fdf7f2
TL
3403 ceph_assert(!done);
3404 ceph_assert(completion->is_complete());
7c673cae
FG
3405 context->oid_flushing.erase(oid);
3406 context->oid_not_flushing.insert(oid);
3407 int r = completion->get_return_value();
3408 cout << num << ": got " << cpp_strerror(r) << std::endl;
3409 if (r == 0) {
3410 context->update_object_version(oid, 0, snap);
3411 } else if (r == -EBUSY) {
11fdf7f2 3412 ceph_assert(can_fail);
7c673cae
FG
3413 } else if (r == -EINVAL) {
3414 // caching not enabled?
3415 } else if (r == -ENOENT) {
3416 // may have raced with a remove?
3417 } else {
11fdf7f2 3418 ceph_abort_msg("shouldn't happen");
7c673cae
FG
3419 }
3420 context->kick();
3421 done = true;
7c673cae
FG
3422 }
3423
3424 bool finished() override
3425 {
3426 return done;
3427 }
3428
3429 string getType() override
3430 {
3431 return "CacheFlushOp";
3432 }
3433};
3434
3435class CacheEvictOp : public TestOp {
3436public:
3437 librados::AioCompletion *completion;
3438 librados::ObjectReadOperation op;
3439 string oid;
11fdf7f2 3440 std::shared_ptr<int> in_use;
7c673cae
FG
3441
3442 CacheEvictOp(int n,
3443 RadosTestContext *context,
3444 const string &oid,
3445 TestOpStat *stat)
3446 : TestOp(n, context, stat),
3447 completion(NULL),
3448 oid(oid)
3449 {}
3450
3451 void _begin() override
3452 {
9f95a23c 3453 context->state_lock.lock();
7c673cae
FG
3454
3455 int snap;
3456 if (!(rand() % 4) && !context->snaps.empty()) {
3457 snap = rand_choose(context->snaps)->first;
3458 in_use = context->snaps_in_use.lookup_or_create(snap, snap);
3459 } else {
3460 snap = -1;
3461 }
3462 cout << num << ": cache_evict oid " << oid << " snap " << snap << std::endl;
3463
3464 if (snap >= 0) {
3465 context->io_ctx.snap_set_read(context->snaps[snap]);
3466 }
3467
3468 pair<TestOp*, TestOp::CallbackInfo*> *cb_arg =
3469 new pair<TestOp*, TestOp::CallbackInfo*>(this,
3470 new TestOp::CallbackInfo(0));
9f95a23c 3471 completion = context->rados.aio_create_completion((void *) cb_arg,
7c673cae 3472 &write_callback);
9f95a23c 3473 context->state_lock.unlock();
7c673cae
FG
3474
3475 op.cache_evict();
3476 int r = context->io_ctx.aio_operate(context->prefix+oid, completion,
3477 &op, librados::OPERATION_IGNORE_CACHE,
3478 NULL);
11fdf7f2 3479 ceph_assert(!r);
7c673cae
FG
3480
3481 if (snap >= 0) {
3482 context->io_ctx.snap_set_read(0);
3483 }
3484 }
3485
3486 void _finish(CallbackInfo *info) override
3487 {
9f95a23c 3488 std::lock_guard state_locker{context->state_lock};
11fdf7f2
TL
3489 ceph_assert(!done);
3490 ceph_assert(completion->is_complete());
31f18b77 3491
7c673cae
FG
3492 int r = completion->get_return_value();
3493 cout << num << ": got " << cpp_strerror(r) << std::endl;
3494 if (r == 0) {
3495 // yay!
3496 } else if (r == -EBUSY) {
3497 // raced with something that dirtied the object
3498 } else if (r == -EINVAL) {
3499 // caching not enabled?
3500 } else if (r == -ENOENT) {
3501 // may have raced with a remove?
3502 } else {
11fdf7f2 3503 ceph_abort_msg("shouldn't happen");
7c673cae
FG
3504 }
3505 context->kick();
3506 done = true;
7c673cae
FG
3507 }
3508
3509 bool finished() override
3510 {
3511 return done;
3512 }
3513
3514 string getType() override
3515 {
3516 return "CacheEvictOp";
3517 }
3518};
3519
3520
3521#endif