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
,
33 m_nextop(NULL
), m_op(0), m_ops(ops
), m_seconds(max_seconds
),
34 m_objects(objects
), m_stats(stats
),
37 m_balance_reads(balance_reads
)
40 for (map
<TestOpType
, unsigned int>::const_iterator it
= op_weights
.begin();
41 it
!= op_weights
.end();
43 m_total_weight
+= it
->second
;
44 m_weight_sums
.insert(pair
<TestOpType
, unsigned int>(it
->first
,
49 TestOp
*next(RadosTestContext
&context
) override
51 TestOp
*retval
= NULL
;
54 if (m_op
<= m_objects
) {
58 // make it a long name
59 oid
<< " " << string(300, 'o');
61 cout
<< m_op
<< ": write initial oid " << oid
.str() << std::endl
;
62 context
.oid_not_flushing
.insert(oid
.str());
64 return new WriteOp(m_op
, &context
, oid
.str(), true, true);
66 return new WriteOp(m_op
, &context
, oid
.str(), false, true);
68 } else if (m_op
>= m_ops
) {
78 while (retval
== NULL
) {
79 unsigned int rand_val
= rand() % m_total_weight
;
82 if (m_seconds
&& now
- m_start
> m_seconds
)
85 for (map
<TestOpType
, unsigned int>::const_iterator it
= m_weight_sums
.begin();
86 it
!= m_weight_sums
.end();
88 if (rand_val
< it
->second
) {
89 retval
= gen_op(context
, it
->first
);
99 TestOp
*gen_op(RadosTestContext
&context
, TestOpType type
)
102 //cout << "oids not in use " << context.oid_not_in_use.size() << std::endl;
103 assert(context
.oid_not_in_use
.size());
107 oid
= *(rand_choose(context
.oid_not_in_use
));
108 return new ReadOp(m_op
, &context
, oid
, m_balance_reads
, m_stats
);
111 oid
= *(rand_choose(context
.oid_not_in_use
));
112 cout
<< m_op
<< ": " << "write oid " << oid
<< " current snap is "
113 << context
.current_snap
<< std::endl
;
114 return new WriteOp(m_op
, &context
, oid
, false, false, m_stats
);
116 case TEST_OP_WRITE_EXCL
:
117 oid
= *(rand_choose(context
.oid_not_in_use
));
118 cout
<< m_op
<< ": " << "write (excl) oid "
119 << oid
<< " current snap is "
120 << context
.current_snap
<< std::endl
;
121 return new WriteOp(m_op
, &context
, oid
, false, true, m_stats
);
123 case TEST_OP_WRITESAME
:
124 oid
= *(rand_choose(context
.oid_not_in_use
));
125 cout
<< m_op
<< ": " << "writesame oid "
126 << oid
<< " current snap is "
127 << context
.current_snap
<< std::endl
;
128 return new WriteSameOp(m_op
, &context
, oid
, m_stats
);
131 oid
= *(rand_choose(context
.oid_not_in_use
));
132 cout
<< m_op
<< ": " << "delete oid " << oid
<< " current snap is "
133 << context
.current_snap
<< std::endl
;
134 return new DeleteOp(m_op
, &context
, oid
, m_stats
);
136 case TEST_OP_SNAP_CREATE
:
137 cout
<< m_op
<< ": " << "snap_create" << std::endl
;
138 return new SnapCreateOp(m_op
, &context
, m_stats
);
140 case TEST_OP_SNAP_REMOVE
:
141 if (context
.snaps
.size() <= context
.snaps_in_use
.size()) {
145 int snap
= rand_choose(context
.snaps
)->first
;
146 if (context
.snaps_in_use
.lookup(snap
))
147 continue; // in use; try again!
148 cout
<< m_op
<< ": " << "snap_remove snap " << snap
<< std::endl
;
149 return new SnapRemoveOp(m_op
, &context
, snap
, m_stats
);
152 case TEST_OP_ROLLBACK
:
154 string oid
= *(rand_choose(context
.oid_not_in_use
));
155 cout
<< m_op
<< ": " << "rollback oid " << oid
<< " current snap is "
156 << context
.current_snap
<< std::endl
;
157 return new RollbackOp(m_op
, &context
, oid
);
160 case TEST_OP_SETATTR
:
161 oid
= *(rand_choose(context
.oid_not_in_use
));
162 cout
<< m_op
<< ": " << "setattr oid " << oid
163 << " current snap is " << context
.current_snap
<< std::endl
;
164 return new SetAttrsOp(m_op
, &context
, oid
, m_stats
);
167 oid
= *(rand_choose(context
.oid_not_in_use
));
168 cout
<< m_op
<< ": " << "rmattr oid " << oid
169 << " current snap is " << context
.current_snap
<< std::endl
;
170 return new RemoveAttrsOp(m_op
, &context
, oid
, m_stats
);
173 oid
= *(rand_choose(context
.oid_not_in_use
));
174 cout
<< m_op
<< ": " << "watch oid " << oid
175 << " current snap is " << context
.current_snap
<< std::endl
;
176 return new WatchOp(m_op
, &context
, oid
, m_stats
);
178 case TEST_OP_COPY_FROM
:
179 oid
= *(rand_choose(context
.oid_not_in_use
));
181 oid2
= *(rand_choose(context
.oid_not_in_use
));
182 } while (oid
== oid2
);
183 cout
<< m_op
<< ": " << "copy_from oid " << oid
<< " from oid " << oid2
184 << " current snap is " << context
.current_snap
<< std::endl
;
185 return new CopyFromOp(m_op
, &context
, oid
, oid2
, m_stats
);
187 case TEST_OP_HIT_SET_LIST
:
189 uint32_t hash
= rjhash32(rand());
190 cout
<< m_op
<< ": " << "hit_set_list " << hash
<< std::endl
;
191 return new HitSetListOp(m_op
, &context
, hash
, m_stats
);
194 case TEST_OP_UNDIRTY
:
196 oid
= *(rand_choose(context
.oid_not_in_use
));
197 cout
<< m_op
<< ": " << "undirty oid " << oid
<< std::endl
;
198 return new UndirtyOp(m_op
, &context
, oid
, m_stats
);
201 case TEST_OP_IS_DIRTY
:
203 oid
= *(rand_choose(context
.oid_not_flushing
));
204 return new IsDirtyOp(m_op
, &context
, oid
, m_stats
);
207 case TEST_OP_CACHE_FLUSH
:
209 oid
= *(rand_choose(context
.oid_not_in_use
));
210 return new CacheFlushOp(m_op
, &context
, oid
, m_stats
, true);
213 case TEST_OP_CACHE_TRY_FLUSH
:
215 oid
= *(rand_choose(context
.oid_not_in_use
));
216 return new CacheFlushOp(m_op
, &context
, oid
, m_stats
, false);
219 case TEST_OP_CACHE_EVICT
:
221 oid
= *(rand_choose(context
.oid_not_in_use
));
222 return new CacheEvictOp(m_op
, &context
, oid
, m_stats
);
226 oid
= *(rand_choose(context
.oid_not_in_use
));
227 cout
<< "append oid " << oid
<< " current snap is "
228 << context
.current_snap
<< std::endl
;
229 return new WriteOp(m_op
, &context
, oid
, true, false, m_stats
);
231 case TEST_OP_APPEND_EXCL
:
232 oid
= *(rand_choose(context
.oid_not_in_use
));
233 cout
<< "append oid (excl) " << oid
<< " current snap is "
234 << context
.current_snap
<< std::endl
;
235 return new WriteOp(m_op
, &context
, oid
, true, true, m_stats
);
238 cerr
<< m_op
<< ": Invalid op type " << type
<< std::endl
;
251 map
<TestOpType
, unsigned int> m_weight_sums
;
252 unsigned int m_total_weight
;
254 bool m_balance_reads
;
257 int main(int argc
, char **argv
)
261 int max_in_flight
= 16;
262 int64_t size
= 4000000; // 4 MB
263 int64_t min_stride_size
= -1, max_stride_size
= -1;
265 bool pool_snaps
= false;
266 bool write_fadvise_dontneed
= false;
273 { TEST_OP_READ
, "read", true },
274 { TEST_OP_WRITE
, "write", false },
275 { TEST_OP_WRITE_EXCL
, "write_excl", false },
276 { TEST_OP_WRITESAME
, "writesame", false },
277 { TEST_OP_DELETE
, "delete", true },
278 { TEST_OP_SNAP_CREATE
, "snap_create", true },
279 { TEST_OP_SNAP_REMOVE
, "snap_remove", true },
280 { TEST_OP_ROLLBACK
, "rollback", true },
281 { TEST_OP_SETATTR
, "setattr", true },
282 { TEST_OP_RMATTR
, "rmattr", true },
283 { TEST_OP_WATCH
, "watch", true },
284 { TEST_OP_COPY_FROM
, "copy_from", true },
285 { TEST_OP_HIT_SET_LIST
, "hit_set_list", true },
286 { TEST_OP_IS_DIRTY
, "is_dirty", true },
287 { TEST_OP_UNDIRTY
, "undirty", true },
288 { TEST_OP_CACHE_FLUSH
, "cache_flush", true },
289 { TEST_OP_CACHE_TRY_FLUSH
, "cache_try_flush", true },
290 { TEST_OP_CACHE_EVICT
, "cache_evict", true },
291 { TEST_OP_APPEND
, "append", true },
292 { TEST_OP_APPEND_EXCL
, "append_excl", true },
293 { TEST_OP_READ
/* grr */, NULL
},
296 map
<TestOpType
, unsigned int> op_weights
;
297 string pool_name
= "rbd";
298 bool ec_pool
= false;
299 bool no_omap
= false;
300 bool no_sparse
= false;
301 bool balance_reads
= false;
303 for (int i
= 1; i
< argc
; ++i
) {
304 if (strcmp(argv
[i
], "--max-ops") == 0)
305 ops
= atoi(argv
[++i
]);
306 else if (strcmp(argv
[i
], "--pool") == 0)
307 pool_name
= argv
[++i
];
308 else if (strcmp(argv
[i
], "--max-seconds") == 0)
309 max_seconds
= atoi(argv
[++i
]);
310 else if (strcmp(argv
[i
], "--objects") == 0)
311 objects
= atoi(argv
[++i
]);
312 else if (strcmp(argv
[i
], "--max-in-flight") == 0)
313 max_in_flight
= atoi(argv
[++i
]);
314 else if (strcmp(argv
[i
], "--size") == 0)
315 size
= atoi(argv
[++i
]);
316 else if (strcmp(argv
[i
], "--min-stride-size") == 0)
317 min_stride_size
= atoi(argv
[++i
]);
318 else if (strcmp(argv
[i
], "--max-stride-size") == 0)
319 max_stride_size
= atoi(argv
[++i
]);
320 else if (strcmp(argv
[i
], "--no-omap") == 0)
322 else if (strcmp(argv
[i
], "--no-sparse") == 0)
324 else if (strcmp(argv
[i
], "--balance_reads") == 0)
325 balance_reads
= true;
326 else if (strcmp(argv
[i
], "--pool-snaps") == 0)
328 else if (strcmp(argv
[i
], "--write-fadvise-dontneed") == 0)
329 write_fadvise_dontneed
= true;
330 else if (strcmp(argv
[i
], "--ec-pool") == 0) {
331 if (!op_weights
.empty()) {
332 cerr
<< "--ec-pool must be specified prior to any ops" << std::endl
;
338 } else if (strcmp(argv
[i
], "--op") == 0) {
341 cerr
<< "Missing op after --op" << std::endl
;
345 for (j
= 0; op_types
[j
].name
; ++j
) {
346 if (strcmp(op_types
[j
].name
, argv
[i
]) == 0) {
350 if (!op_types
[j
].name
) {
351 cerr
<< "unknown op " << argv
[i
] << std::endl
;
356 cerr
<< "Weight unspecified." << std::endl
;
359 int weight
= atoi(argv
[i
]);
361 cerr
<< "Weights must be nonnegative." << std::endl
;
363 } else if (weight
> 0) {
364 if (ec_pool
&& !op_types
[j
].ec_pool_valid
) {
365 cerr
<< "Error: cannot use op type " << op_types
[j
].name
366 << " with --ec-pool" << std::endl
;
369 cout
<< "adding op weight " << op_types
[j
].name
<< " -> " << weight
<< std::endl
;
370 op_weights
.insert(pair
<TestOpType
, unsigned int>(op_types
[j
].op
, weight
));
373 cerr
<< "unknown arg " << argv
[i
] << std::endl
;
379 if (op_weights
.empty()) {
380 cerr
<< "No operations specified" << std::endl
;
385 if (min_stride_size
< 0)
386 min_stride_size
= size
/ 10;
387 if (max_stride_size
< 0)
388 max_stride_size
= size
/ 5;
390 cout
<< pretty_version_to_str() << std::endl
;
391 cout
<< "Configuration:" << std::endl
392 << "\tNumber of operations: " << ops
<< std::endl
393 << "\tNumber of objects: " << objects
<< std::endl
394 << "\tMax in flight operations: " << max_in_flight
<< std::endl
395 << "\tObject size (in bytes): " << size
<< std::endl
396 << "\tWrite stride min: " << min_stride_size
<< std::endl
397 << "\tWrite stride max: " << max_stride_size
<< std::endl
;
399 if (min_stride_size
> max_stride_size
) {
400 cerr
<< "Error: min_stride_size cannot be more than max_stride_size"
405 if (min_stride_size
> size
|| max_stride_size
> size
) {
406 cerr
<< "Error: min_stride_size and max_stride_size must be "
407 << "smaller than object size" << std::endl
;
411 if (max_in_flight
* 2 > objects
) {
412 cerr
<< "Error: max_in_flight must be <= than the number of objects / 2"
417 char *id
= getenv("CEPH_CLIENT_ID");
418 RadosTestContext
context(
427 write_fadvise_dontneed
,
431 WeightedTestGenerator gen
= WeightedTestGenerator(
433 op_weights
, &stats
, max_seconds
,
434 ec_pool
, balance_reads
);
435 int r
= context
.init();
437 cerr
<< "Error initializing rados test context: "
438 << cpp_strerror(r
) << std::endl
;
444 cerr
<< context
.errors
<< " errors." << std::endl
;
445 cerr
<< stats
<< std::endl
;