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