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