]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | #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 |
33 | class RadosTestContext; |
34 | class TestOpStat; | |
35 | ||
36 | template <typename T> | |
37 | typename 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 | ||
44 | enum 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 | ||
74 | class TestWatchContext : public librados::WatchCtx2 { | |
75 | TestWatchContext(const TestWatchContext&); | |
76 | public: | |
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 | ||
106 | class TestOp { | |
107 | public: | |
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 | ||
155 | class TestOpGenerator { | |
156 | public: | |
157 | virtual ~TestOpGenerator() {}; | |
158 | virtual TestOp *next(RadosTestContext &context) = 0; | |
159 | }; | |
160 | ||
161 | class RadosTestContext { | |
162 | public: | |
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> ¤t = 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 | ||
698 | void read_callback(librados::completion_t comp, void *arg); | |
699 | void 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 |
703 | class RemoveAttrsOp : public TestOp { |
704 | public: | |
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 |
797 | class SetAttrsOp : public TestOp { |
798 | public: | |
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 | ||
891 | class WriteOp : public TestOp { | |
892 | public: | |
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 | ||
1108 | class WriteSameOp : public TestOp { | |
1109 | public: | |
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 | ||
1290 | class DeleteOp : public TestOp { | |
1291 | public: | |
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 | ||
1348 | class ReadOp : public TestOp { | |
1349 | public: | |
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 | ||
1697 | class SnapCreateOp : public TestOp { | |
1698 | public: | |
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 | ||
1757 | class SnapRemoveOp : public TestOp { | |
1758 | public: | |
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 | ||
1803 | class WatchOp : public TestOp { | |
1e59de90 | 1804 | std::string oid; |
7c673cae FG |
1805 | public: |
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 | ||
1864 | class RollbackOp : public TestOp { | |
1865 | public: | |
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 | ||
2000 | class CopyFromOp : public TestOp { | |
2001 | public: | |
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 |
2130 | class ChunkReadOp : public TestOp { |
2131 | public: | |
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 | ||
2365 | class CopyOp : public TestOp { | |
2366 | public: | |
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 | ||
2440 | class SetChunkOp : public TestOp { | |
2441 | public: | |
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 |
2604 | class SetRedirectOp : public TestOp { |
2605 | public: | |
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 | ||
2756 | class UnsetRedirectOp : public TestOp { | |
2757 | public: | |
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 |
2817 | class TierPromoteOp : public TestOp { |
2818 | public: | |
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 |
2892 | class TierFlushOp : public TestOp { |
2893 | public: | |
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 |
2999 | class TierEvictOp : public TestOp { |
3000 | public: | |
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 |
3103 | class 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 | ||
3109 | public: | |
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 | ||
3176 | class UndirtyOp : public TestOp { | |
3177 | public: | |
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 | ||
3234 | class IsDirtyOp : public TestOp { | |
3235 | public: | |
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 | ||
3328 | class CacheFlushOp : public TestOp { | |
3329 | public: | |
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 | ||
3434 | class CacheEvictOp : public TestOp { | |
3435 | public: | |
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 |