1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3 #include "common/Mutex.h"
4 #include "common/Cond.h"
5 #include "common/errno.h"
6 #include "common/version.h"
17 #include "test/osd/RadosModel.h"
22 class WeightedTestGenerator
: public TestOpGenerator
26 WeightedTestGenerator(int ops
,
28 map
<TestOpType
, unsigned int> op_weights
,
34 m_nextop(NULL
), m_op(0), m_ops(ops
), m_seconds(max_seconds
),
35 m_objects(objects
), m_stats(stats
),
38 m_balance_reads(balance_reads
),
39 m_set_redirect(set_redirect
)
42 for (map
<TestOpType
, unsigned int>::const_iterator it
= op_weights
.begin();
43 it
!= op_weights
.end();
45 m_total_weight
+= it
->second
;
46 m_weight_sums
.insert(pair
<TestOpType
, unsigned int>(it
->first
,
50 /* create redirect objects + set-redirect*/
51 m_redirect_objects
= objects
*2; // for copy_from + set-redirect test
52 m_initial_redirected_objects
= objects
;
53 m_ops
= ops
+m_redirect_objects
+m_initial_redirected_objects
;
57 TestOp
*next(RadosTestContext
&context
) override
59 TestOp
*retval
= NULL
;
62 if (m_op
<= m_objects
) {
66 // make it a long name
67 oid
<< " " << string(300, 'o');
69 cout
<< m_op
<< ": write initial oid " << oid
.str() << std::endl
;
70 context
.oid_not_flushing
.insert(oid
.str());
72 return new WriteOp(m_op
, &context
, oid
.str(), true, true);
74 return new WriteOp(m_op
, &context
, oid
.str(), false, true);
76 } else if (m_op
>= m_ops
) {
83 * 1. create objects (copy from)
86 int create_objects_end
= m_objects
+ m_redirect_objects
;
87 int set_redirect_end
= create_objects_end
+ m_initial_redirected_objects
;
89 if (m_op
<= create_objects_end
) {
94 oid
<< " " << string(300, 'o');
97 int _oid2
= _oid
- m_objects
;
100 oid2
<< " " << string(300, 'o');
102 cout
<< m_op
<< ": " << "(create redirect oid) copy_from oid " << oid
.str()
103 << " from oid " << oid2
.str() << std::endl
;
104 return new CopyFromOp(m_op
, &context
, oid
.str(), oid2
.str(), m_stats
);
105 } else if (m_op
<= set_redirect_end
) {
107 int _oid
= m_op
-create_objects_end
;
110 oid
<< " " << string(300, 'o');
113 int _oid2
= _oid
+ m_objects
;
116 oid2
<< " " << string(300, 'o');
118 cout
<< m_op
<< ": " << "set_redirect oid " << oid
.str() << " target oid "
119 << oid2
.str() << std::endl
;
120 return new SetRedirectOp(m_op
, &context
, oid
.str(), oid2
.str(), context
.pool_name
);
123 if (!context
.oid_redirect_not_in_use
.size() && m_op
> set_redirect_end
) {
124 int set_size
= context
.oid_not_in_use
.size();
125 if (set_size
< m_objects
+ m_redirect_objects
) {
128 for (int t_op
= m_objects
+1; t_op
<= create_objects_end
; t_op
++) {
132 oid
<< " " << string(300, 'o');
134 context
.oid_not_flushing
.erase(oid
.str());
135 context
.oid_not_in_use
.erase(oid
.str());
136 context
.oid_in_use
.erase(oid
.str());
137 cout
<< m_op
<< ": " << " remove oid " << oid
.str() << " from oid_*_use " << std::endl
;
138 if (t_op
> m_objects
+ m_initial_redirected_objects
) {
139 context
.oid_redirect_not_in_use
.insert(oid
.str());
142 cout
<< m_op
<< ": " << " oid_not_in_use: " << context
.oid_not_in_use
.size()
143 << " oid_in_use: " << context
.oid_in_use
.size() << std::endl
;
153 while (retval
== NULL
) {
154 unsigned int rand_val
= rand() % m_total_weight
;
156 time_t now
= time(0);
157 if (m_seconds
&& now
- m_start
> m_seconds
)
160 for (map
<TestOpType
, unsigned int>::const_iterator it
= m_weight_sums
.begin();
161 it
!= m_weight_sums
.end();
163 if (rand_val
< it
->second
) {
164 retval
= gen_op(context
, it
->first
);
174 TestOp
*gen_op(RadosTestContext
&context
, TestOpType type
)
177 assert(context
.oid_not_in_use
.size());
181 oid
= *(rand_choose(context
.oid_not_in_use
));
182 return new ReadOp(m_op
, &context
, oid
, m_balance_reads
, m_stats
);
185 oid
= *(rand_choose(context
.oid_not_in_use
));
186 cout
<< m_op
<< ": " << "write oid " << oid
<< " current snap is "
187 << context
.current_snap
<< std::endl
;
188 return new WriteOp(m_op
, &context
, oid
, false, false, m_stats
);
190 case TEST_OP_WRITE_EXCL
:
191 oid
= *(rand_choose(context
.oid_not_in_use
));
192 cout
<< m_op
<< ": " << "write (excl) oid "
193 << oid
<< " current snap is "
194 << context
.current_snap
<< std::endl
;
195 return new WriteOp(m_op
, &context
, oid
, false, true, m_stats
);
197 case TEST_OP_WRITESAME
:
198 oid
= *(rand_choose(context
.oid_not_in_use
));
199 cout
<< m_op
<< ": " << "writesame oid "
200 << oid
<< " current snap is "
201 << context
.current_snap
<< std::endl
;
202 return new WriteSameOp(m_op
, &context
, oid
, m_stats
);
205 oid
= *(rand_choose(context
.oid_not_in_use
));
206 cout
<< m_op
<< ": " << "delete oid " << oid
<< " current snap is "
207 << context
.current_snap
<< std::endl
;
208 return new DeleteOp(m_op
, &context
, oid
, m_stats
);
210 case TEST_OP_SNAP_CREATE
:
211 cout
<< m_op
<< ": " << "snap_create" << std::endl
;
212 return new SnapCreateOp(m_op
, &context
, m_stats
);
214 case TEST_OP_SNAP_REMOVE
:
215 if (context
.snaps
.size() <= context
.snaps_in_use
.size()) {
219 int snap
= rand_choose(context
.snaps
)->first
;
220 if (context
.snaps_in_use
.lookup(snap
))
221 continue; // in use; try again!
222 cout
<< m_op
<< ": " << "snap_remove snap " << snap
<< std::endl
;
223 return new SnapRemoveOp(m_op
, &context
, snap
, m_stats
);
226 case TEST_OP_ROLLBACK
:
228 string oid
= *(rand_choose(context
.oid_not_in_use
));
229 cout
<< m_op
<< ": " << "rollback oid " << oid
<< " current snap is "
230 << context
.current_snap
<< std::endl
;
231 return new RollbackOp(m_op
, &context
, oid
);
234 case TEST_OP_SETATTR
:
235 oid
= *(rand_choose(context
.oid_not_in_use
));
236 cout
<< m_op
<< ": " << "setattr oid " << oid
237 << " current snap is " << context
.current_snap
<< std::endl
;
238 return new SetAttrsOp(m_op
, &context
, oid
, m_stats
);
241 oid
= *(rand_choose(context
.oid_not_in_use
));
242 cout
<< m_op
<< ": " << "rmattr oid " << oid
243 << " current snap is " << context
.current_snap
<< std::endl
;
244 return new RemoveAttrsOp(m_op
, &context
, oid
, m_stats
);
247 oid
= *(rand_choose(context
.oid_not_in_use
));
248 cout
<< m_op
<< ": " << "watch oid " << oid
249 << " current snap is " << context
.current_snap
<< std::endl
;
250 return new WatchOp(m_op
, &context
, oid
, m_stats
);
252 case TEST_OP_COPY_FROM
:
253 oid
= *(rand_choose(context
.oid_not_in_use
));
255 oid2
= *(rand_choose(context
.oid_not_in_use
));
256 } while (oid
== oid2
);
257 cout
<< m_op
<< ": " << "copy_from oid " << oid
<< " from oid " << oid2
258 << " current snap is " << context
.current_snap
<< std::endl
;
259 return new CopyFromOp(m_op
, &context
, oid
, oid2
, m_stats
);
261 case TEST_OP_HIT_SET_LIST
:
263 uint32_t hash
= rjhash32(rand());
264 cout
<< m_op
<< ": " << "hit_set_list " << hash
<< std::endl
;
265 return new HitSetListOp(m_op
, &context
, hash
, m_stats
);
268 case TEST_OP_UNDIRTY
:
270 oid
= *(rand_choose(context
.oid_not_in_use
));
271 cout
<< m_op
<< ": " << "undirty oid " << oid
<< std::endl
;
272 return new UndirtyOp(m_op
, &context
, oid
, m_stats
);
275 case TEST_OP_IS_DIRTY
:
277 oid
= *(rand_choose(context
.oid_not_flushing
));
278 return new IsDirtyOp(m_op
, &context
, oid
, m_stats
);
281 case TEST_OP_CACHE_FLUSH
:
283 oid
= *(rand_choose(context
.oid_not_in_use
));
284 return new CacheFlushOp(m_op
, &context
, oid
, m_stats
, true);
287 case TEST_OP_CACHE_TRY_FLUSH
:
289 oid
= *(rand_choose(context
.oid_not_in_use
));
290 return new CacheFlushOp(m_op
, &context
, oid
, m_stats
, false);
293 case TEST_OP_CACHE_EVICT
:
295 oid
= *(rand_choose(context
.oid_not_in_use
));
296 return new CacheEvictOp(m_op
, &context
, oid
, m_stats
);
300 oid
= *(rand_choose(context
.oid_not_in_use
));
301 cout
<< "append oid " << oid
<< " current snap is "
302 << context
.current_snap
<< std::endl
;
303 return new WriteOp(m_op
, &context
, oid
, true, false, m_stats
);
305 case TEST_OP_APPEND_EXCL
:
306 oid
= *(rand_choose(context
.oid_not_in_use
));
307 cout
<< "append oid (excl) " << oid
<< " current snap is "
308 << context
.current_snap
<< std::endl
;
309 return new WriteOp(m_op
, &context
, oid
, true, true, m_stats
);
311 case TEST_OP_SET_REDIRECT
:
312 oid
= *(rand_choose(context
.oid_not_in_use
));
313 oid2
= *(rand_choose(context
.oid_redirect_not_in_use
));
314 cout
<< m_op
<< ": " << "set_redirect oid " << oid
<< " target oid " << oid2
<< std::endl
;
315 return new SetRedirectOp(m_op
, &context
, oid
, oid2
, context
.pool_name
, m_stats
);
317 case TEST_OP_UNSET_REDIRECT
:
318 oid
= *(rand_choose(context
.oid_not_in_use
));
319 cout
<< m_op
<< ": " << "unset_redirect oid " << oid
<< std::endl
;
320 return new UnsetRedirectOp(m_op
, &context
, oid
, m_stats
);
323 cerr
<< m_op
<< ": Invalid op type " << type
<< std::endl
;
336 map
<TestOpType
, unsigned int> m_weight_sums
;
337 unsigned int m_total_weight
;
339 bool m_balance_reads
;
341 int m_redirect_objects
;
342 int m_initial_redirected_objects
;
345 int main(int argc
, char **argv
)
349 int max_in_flight
= 16;
350 int64_t size
= 4000000; // 4 MB
351 int64_t min_stride_size
= -1, max_stride_size
= -1;
353 bool pool_snaps
= false;
354 bool write_fadvise_dontneed
= false;
361 { TEST_OP_READ
, "read", true },
362 { TEST_OP_WRITE
, "write", false },
363 { TEST_OP_WRITE_EXCL
, "write_excl", false },
364 { TEST_OP_WRITESAME
, "writesame", false },
365 { TEST_OP_DELETE
, "delete", true },
366 { TEST_OP_SNAP_CREATE
, "snap_create", true },
367 { TEST_OP_SNAP_REMOVE
, "snap_remove", true },
368 { TEST_OP_ROLLBACK
, "rollback", true },
369 { TEST_OP_SETATTR
, "setattr", true },
370 { TEST_OP_RMATTR
, "rmattr", true },
371 { TEST_OP_WATCH
, "watch", true },
372 { TEST_OP_COPY_FROM
, "copy_from", true },
373 { TEST_OP_HIT_SET_LIST
, "hit_set_list", true },
374 { TEST_OP_IS_DIRTY
, "is_dirty", true },
375 { TEST_OP_UNDIRTY
, "undirty", true },
376 { TEST_OP_CACHE_FLUSH
, "cache_flush", true },
377 { TEST_OP_CACHE_TRY_FLUSH
, "cache_try_flush", true },
378 { TEST_OP_CACHE_EVICT
, "cache_evict", true },
379 { TEST_OP_APPEND
, "append", true },
380 { TEST_OP_APPEND_EXCL
, "append_excl", true },
381 { TEST_OP_SET_REDIRECT
, "set_redirect", true },
382 { TEST_OP_UNSET_REDIRECT
, "unset_redirect", true },
383 { TEST_OP_READ
/* grr */, NULL
},
386 map
<TestOpType
, unsigned int> op_weights
;
387 string pool_name
= "rbd";
388 bool ec_pool
= false;
389 bool no_omap
= false;
390 bool no_sparse
= false;
391 bool balance_reads
= false;
392 bool set_redirect
= false;
394 for (int i
= 1; i
< argc
; ++i
) {
395 if (strcmp(argv
[i
], "--max-ops") == 0)
396 ops
= atoi(argv
[++i
]);
397 else if (strcmp(argv
[i
], "--pool") == 0)
398 pool_name
= argv
[++i
];
399 else if (strcmp(argv
[i
], "--max-seconds") == 0)
400 max_seconds
= atoi(argv
[++i
]);
401 else if (strcmp(argv
[i
], "--objects") == 0)
402 objects
= atoi(argv
[++i
]);
403 else if (strcmp(argv
[i
], "--max-in-flight") == 0)
404 max_in_flight
= atoi(argv
[++i
]);
405 else if (strcmp(argv
[i
], "--size") == 0)
406 size
= atoi(argv
[++i
]);
407 else if (strcmp(argv
[i
], "--min-stride-size") == 0)
408 min_stride_size
= atoi(argv
[++i
]);
409 else if (strcmp(argv
[i
], "--max-stride-size") == 0)
410 max_stride_size
= atoi(argv
[++i
]);
411 else if (strcmp(argv
[i
], "--no-omap") == 0)
413 else if (strcmp(argv
[i
], "--no-sparse") == 0)
415 else if (strcmp(argv
[i
], "--balance_reads") == 0)
416 balance_reads
= true;
417 else if (strcmp(argv
[i
], "--pool-snaps") == 0)
419 else if (strcmp(argv
[i
], "--write-fadvise-dontneed") == 0)
420 write_fadvise_dontneed
= true;
421 else if (strcmp(argv
[i
], "--ec-pool") == 0) {
422 if (!op_weights
.empty()) {
423 cerr
<< "--ec-pool must be specified prior to any ops" << std::endl
;
429 } else if (strcmp(argv
[i
], "--op") == 0) {
432 cerr
<< "Missing op after --op" << std::endl
;
436 for (j
= 0; op_types
[j
].name
; ++j
) {
437 if (strcmp(op_types
[j
].name
, argv
[i
]) == 0) {
441 if (!op_types
[j
].name
) {
442 cerr
<< "unknown op " << argv
[i
] << std::endl
;
447 cerr
<< "Weight unspecified." << std::endl
;
450 int weight
= atoi(argv
[i
]);
452 cerr
<< "Weights must be nonnegative." << std::endl
;
454 } else if (weight
> 0) {
455 if (ec_pool
&& !op_types
[j
].ec_pool_valid
) {
456 cerr
<< "Error: cannot use op type " << op_types
[j
].name
457 << " with --ec-pool" << std::endl
;
460 cout
<< "adding op weight " << op_types
[j
].name
<< " -> " << weight
<< std::endl
;
461 op_weights
.insert(pair
<TestOpType
, unsigned int>(op_types
[j
].op
, weight
));
463 } else if (strcmp(argv
[i
], "--set_redirect") == 0) {
466 cerr
<< "unknown arg " << argv
[i
] << std::endl
;
471 if (op_weights
.empty()) {
472 cerr
<< "No operations specified" << std::endl
;
476 if (min_stride_size
< 0)
477 min_stride_size
= size
/ 10;
478 if (max_stride_size
< 0)
479 max_stride_size
= size
/ 5;
481 cout
<< pretty_version_to_str() << std::endl
;
482 cout
<< "Configuration:" << std::endl
483 << "\tNumber of operations: " << ops
<< std::endl
484 << "\tNumber of objects: " << objects
<< std::endl
485 << "\tMax in flight operations: " << max_in_flight
<< std::endl
486 << "\tObject size (in bytes): " << size
<< std::endl
487 << "\tWrite stride min: " << min_stride_size
<< std::endl
488 << "\tWrite stride max: " << max_stride_size
<< std::endl
;
490 if (min_stride_size
> max_stride_size
) {
491 cerr
<< "Error: min_stride_size cannot be more than max_stride_size"
496 if (min_stride_size
> size
|| max_stride_size
> size
) {
497 cerr
<< "Error: min_stride_size and max_stride_size must be "
498 << "smaller than object size" << std::endl
;
502 if (max_in_flight
* 2 > objects
) {
503 cerr
<< "Error: max_in_flight must be <= than the number of objects / 2"
508 char *id
= getenv("CEPH_CLIENT_ID");
509 RadosTestContext
context(
518 write_fadvise_dontneed
,
522 WeightedTestGenerator gen
= WeightedTestGenerator(
524 op_weights
, &stats
, max_seconds
,
525 ec_pool
, balance_reads
, set_redirect
);
526 int r
= context
.init();
528 cerr
<< "Error initializing rados test context: "
529 << cpp_strerror(r
) << std::endl
;
535 cerr
<< context
.errors
<< " errors." << std::endl
;
536 cerr
<< stats
<< std::endl
;