]>
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 | |
7c673cae FG |
3 | #include "common/Cond.h" |
4 | #include "common/errno.h" | |
5 | #include "common/version.h" | |
6 | ||
7 | #include <iostream> | |
8 | #include <sstream> | |
9 | #include <map> | |
10 | #include <numeric> | |
11 | #include <string> | |
12 | #include <vector> | |
13 | #include <stdlib.h> | |
14 | #include <unistd.h> | |
15 | ||
16 | #include "test/osd/RadosModel.h" | |
17 | ||
7c673cae FG |
18 | using namespace std; |
19 | ||
20 | class WeightedTestGenerator : public TestOpGenerator | |
21 | { | |
22 | public: | |
23 | ||
24 | WeightedTestGenerator(int ops, | |
25 | int objects, | |
26 | map<TestOpType, unsigned int> op_weights, | |
27 | TestOpStat *stats, | |
28 | int max_seconds, | |
29 | bool ec_pool, | |
31f18b77 | 30 | bool balance_reads, |
9f95a23c | 31 | bool localize_reads, |
11fdf7f2 | 32 | bool set_redirect, |
9f95a23c TL |
33 | bool set_chunk, |
34 | bool enable_dedup) : | |
7c673cae FG |
35 | m_nextop(NULL), m_op(0), m_ops(ops), m_seconds(max_seconds), |
36 | m_objects(objects), m_stats(stats), | |
37 | m_total_weight(0), | |
38 | m_ec_pool(ec_pool), | |
31f18b77 | 39 | m_balance_reads(balance_reads), |
9f95a23c | 40 | m_localize_reads(localize_reads), |
11fdf7f2 | 41 | m_set_redirect(set_redirect), |
9f95a23c TL |
42 | m_set_chunk(set_chunk), |
43 | m_enable_dedup(enable_dedup) | |
7c673cae FG |
44 | { |
45 | m_start = time(0); | |
46 | for (map<TestOpType, unsigned int>::const_iterator it = op_weights.begin(); | |
47 | it != op_weights.end(); | |
48 | ++it) { | |
49 | m_total_weight += it->second; | |
50 | m_weight_sums.insert(pair<TestOpType, unsigned int>(it->first, | |
51 | m_total_weight)); | |
52 | } | |
11fdf7f2 TL |
53 | if (m_set_redirect || m_set_chunk) { |
54 | if (m_set_redirect) { | |
55 | m_ops = ops+m_objects+m_objects; | |
56 | } else { | |
57 | /* create 10 chunks per an object*/ | |
58 | m_ops = ops+m_objects+m_objects*10; | |
59 | } | |
31f18b77 | 60 | } |
7c673cae FG |
61 | } |
62 | ||
63 | TestOp *next(RadosTestContext &context) override | |
64 | { | |
65 | TestOp *retval = NULL; | |
66 | ||
67 | ++m_op; | |
11fdf7f2 | 68 | if (m_op <= m_objects && !m_set_redirect && !m_set_chunk ) { |
7c673cae FG |
69 | stringstream oid; |
70 | oid << m_op; | |
9f95a23c | 71 | /*if (m_op % 2) { |
7c673cae FG |
72 | // make it a long name |
73 | oid << " " << string(300, 'o'); | |
9f95a23c | 74 | }*/ |
7c673cae FG |
75 | cout << m_op << ": write initial oid " << oid.str() << std::endl; |
76 | context.oid_not_flushing.insert(oid.str()); | |
77 | if (m_ec_pool) { | |
78 | return new WriteOp(m_op, &context, oid.str(), true, true); | |
79 | } else { | |
80 | return new WriteOp(m_op, &context, oid.str(), false, true); | |
81 | } | |
82 | } else if (m_op >= m_ops) { | |
83 | return NULL; | |
84 | } | |
31f18b77 | 85 | |
11fdf7f2 TL |
86 | if (m_set_redirect || m_set_chunk) { |
87 | if (init_extensible_tier(context, retval)) { | |
88 | return retval; | |
31f18b77 | 89 | } |
11fdf7f2 | 90 | } |
7c673cae FG |
91 | |
92 | if (m_nextop) { | |
93 | retval = m_nextop; | |
94 | m_nextop = NULL; | |
95 | return retval; | |
96 | } | |
97 | ||
98 | while (retval == NULL) { | |
99 | unsigned int rand_val = rand() % m_total_weight; | |
100 | ||
101 | time_t now = time(0); | |
102 | if (m_seconds && now - m_start > m_seconds) | |
103 | break; | |
104 | ||
105 | for (map<TestOpType, unsigned int>::const_iterator it = m_weight_sums.begin(); | |
106 | it != m_weight_sums.end(); | |
107 | ++it) { | |
108 | if (rand_val < it->second) { | |
109 | retval = gen_op(context, it->first); | |
110 | break; | |
111 | } | |
112 | } | |
113 | } | |
114 | return retval; | |
115 | } | |
116 | ||
11fdf7f2 TL |
117 | bool init_extensible_tier(RadosTestContext &context, TestOp *& op) { |
118 | /* | |
119 | * set-redirect or set-chunk test (manifest test) | |
120 | * 0. make default objects (using create op) | |
121 | * 1. set-redirect or set-chunk | |
122 | * 2. initialize target objects (using write op) | |
123 | * 3. wait for set-* completion | |
124 | */ | |
125 | int copy_manifest_end = 0; | |
126 | if (m_set_chunk) { | |
127 | copy_manifest_end = m_objects*2; | |
128 | } else { | |
129 | copy_manifest_end = m_objects*3; | |
130 | } | |
131 | int make_manifest_end = copy_manifest_end; | |
132 | if (m_set_chunk) { | |
133 | /* make 10 chunks per an object*/ | |
134 | make_manifest_end = make_manifest_end + m_objects * 10; | |
135 | } else { | |
136 | /* redirect */ | |
137 | make_manifest_end = make_manifest_end + m_objects; | |
138 | } | |
139 | ||
140 | if (m_op <= m_objects) { | |
141 | stringstream oid; | |
142 | oid << m_op; | |
9f95a23c | 143 | /*if (m_op % 2) { |
11fdf7f2 | 144 | oid << " " << string(300, 'o'); |
9f95a23c | 145 | }*/ |
11fdf7f2 TL |
146 | cout << m_op << ": write initial oid " << oid.str() << std::endl; |
147 | context.oid_not_flushing.insert(oid.str()); | |
148 | if (m_ec_pool) { | |
149 | op = new WriteOp(m_op, &context, oid.str(), true, true); | |
150 | } else { | |
151 | op = new WriteOp(m_op, &context, oid.str(), false, true); | |
152 | } | |
153 | return true; | |
154 | } else if (m_op <= copy_manifest_end) { | |
155 | stringstream oid, oid2; | |
156 | //int _oid = m_op-m_objects; | |
157 | int _oid = m_op % m_objects + 1; | |
158 | oid << _oid; | |
9f95a23c | 159 | /*if ((_oid) % 2) { |
11fdf7f2 | 160 | oid << " " << string(300, 'o'); |
9f95a23c TL |
161 | }*/ |
162 | ||
163 | if (context.oid_in_use.count(oid.str())) { | |
164 | /* previous write is not finished */ | |
165 | op = NULL; | |
166 | m_op--; | |
167 | cout << m_op << " wait for completion of write op! " << std::endl; | |
168 | return true; | |
169 | } | |
170 | ||
11fdf7f2 TL |
171 | int _oid2 = m_op - m_objects + 1; |
172 | if (_oid2 > copy_manifest_end - m_objects) { | |
173 | _oid2 -= (copy_manifest_end - m_objects); | |
174 | } | |
175 | oid2 << _oid2 << " " << context.low_tier_pool_name; | |
176 | if ((_oid2) % 2) { | |
177 | oid2 << " " << string(300, 'm'); | |
178 | } | |
179 | cout << m_op << ": " << "copy oid " << oid.str() << " target oid " | |
180 | << oid2.str() << std::endl; | |
181 | op = new CopyOp(m_op, &context, oid.str(), oid2.str(), context.low_tier_pool_name); | |
182 | return true; | |
183 | } else if (m_op <= make_manifest_end) { | |
184 | if (m_set_redirect) { | |
185 | stringstream oid, oid2; | |
186 | int _oid = m_op-copy_manifest_end; | |
187 | oid << _oid; | |
9f95a23c | 188 | /*if ((_oid) % 2) { |
11fdf7f2 | 189 | oid << " " << string(300, 'o'); |
9f95a23c | 190 | }*/ |
11fdf7f2 TL |
191 | oid2 << _oid << " " << context.low_tier_pool_name; |
192 | if ((_oid) % 2) { | |
193 | oid2 << " " << string(300, 'm'); | |
194 | } | |
195 | if (context.oid_in_use.count(oid.str())) { | |
196 | /* previous copy is not finished */ | |
197 | op = NULL; | |
198 | m_op--; | |
199 | cout << m_op << " retry set_redirect !" << std::endl; | |
200 | return true; | |
201 | } | |
202 | cout << m_op << ": " << "set_redirect oid " << oid.str() << " target oid " | |
203 | << oid2.str() << std::endl; | |
204 | op = new SetRedirectOp(m_op, &context, oid.str(), oid2.str(), context.pool_name); | |
205 | return true; | |
206 | } else if (m_set_chunk) { | |
207 | stringstream oid; | |
208 | int _oid = m_op % m_objects +1; | |
209 | oid << _oid; | |
9f95a23c | 210 | /*if ((_oid) % 2) { |
11fdf7f2 | 211 | oid << " " << string(300, 'o'); |
9f95a23c | 212 | }*/ |
11fdf7f2 TL |
213 | if (context.oid_in_use.count(oid.str())) { |
214 | /* previous set-chunk is not finished */ | |
215 | op = NULL; | |
216 | m_op--; | |
217 | cout << m_op << " retry set_chunk !" << std::endl; | |
218 | return true; | |
219 | } | |
220 | stringstream oid2; | |
221 | oid2 << _oid << " " << context.low_tier_pool_name; | |
222 | if ((_oid) % 2) { | |
223 | oid2 << " " << string(300, 'm'); | |
224 | } | |
225 | ||
20effc67 TL |
226 | cout << m_op << ": " << "set_chunk oid " << oid.str() |
227 | << " target oid " << oid2.str() << std::endl; | |
228 | op = new SetChunkOp(m_op, &context, oid.str(), oid2.str(), m_stats); | |
11fdf7f2 TL |
229 | return true; |
230 | } | |
231 | } else if (m_op == make_manifest_end + 1) { | |
232 | int set_size = context.oid_not_in_use.size(); | |
233 | int set_manifest_size = context.oid_redirect_not_in_use.size(); | |
234 | cout << m_op << " oid_not_in_use " << set_size << " oid_redirect_not_in_use " << set_manifest_size << std::endl; | |
235 | /* wait for redirect or set_chunk initialization */ | |
236 | if (set_size != m_objects || set_manifest_size != 0) { | |
237 | op = NULL; | |
238 | m_op--; | |
239 | cout << m_op << " wait for manifest initialization " << std::endl; | |
240 | return true; | |
241 | } | |
242 | for (int t_op = m_objects+1; t_op <= m_objects*2; t_op++) { | |
243 | stringstream oid; | |
244 | oid << t_op << " " << context.low_tier_pool_name; | |
245 | if (t_op % 2) { | |
246 | oid << " " << string(300, 'm'); | |
247 | } | |
248 | cout << " redirect_not_in_use: " << oid.str() << std::endl; | |
249 | context.oid_redirect_not_in_use.insert(oid.str()); | |
250 | } | |
9f95a23c | 251 | } |
11fdf7f2 TL |
252 | |
253 | return false; | |
254 | } | |
255 | ||
7c673cae FG |
256 | private: |
257 | ||
258 | TestOp *gen_op(RadosTestContext &context, TestOpType type) | |
259 | { | |
260 | string oid, oid2; | |
11fdf7f2 | 261 | ceph_assert(context.oid_not_in_use.size()); |
7c673cae FG |
262 | |
263 | switch (type) { | |
264 | case TEST_OP_READ: | |
265 | oid = *(rand_choose(context.oid_not_in_use)); | |
9f95a23c TL |
266 | return new ReadOp(m_op, &context, oid, m_balance_reads, m_localize_reads, |
267 | m_stats); | |
7c673cae FG |
268 | |
269 | case TEST_OP_WRITE: | |
270 | oid = *(rand_choose(context.oid_not_in_use)); | |
271 | cout << m_op << ": " << "write oid " << oid << " current snap is " | |
272 | << context.current_snap << std::endl; | |
273 | return new WriteOp(m_op, &context, oid, false, false, m_stats); | |
274 | ||
275 | case TEST_OP_WRITE_EXCL: | |
276 | oid = *(rand_choose(context.oid_not_in_use)); | |
277 | cout << m_op << ": " << "write (excl) oid " | |
278 | << oid << " current snap is " | |
279 | << context.current_snap << std::endl; | |
280 | return new WriteOp(m_op, &context, oid, false, true, m_stats); | |
281 | ||
282 | case TEST_OP_WRITESAME: | |
283 | oid = *(rand_choose(context.oid_not_in_use)); | |
284 | cout << m_op << ": " << "writesame oid " | |
285 | << oid << " current snap is " | |
286 | << context.current_snap << std::endl; | |
287 | return new WriteSameOp(m_op, &context, oid, m_stats); | |
288 | ||
289 | case TEST_OP_DELETE: | |
290 | oid = *(rand_choose(context.oid_not_in_use)); | |
291 | cout << m_op << ": " << "delete oid " << oid << " current snap is " | |
292 | << context.current_snap << std::endl; | |
293 | return new DeleteOp(m_op, &context, oid, m_stats); | |
294 | ||
295 | case TEST_OP_SNAP_CREATE: | |
296 | cout << m_op << ": " << "snap_create" << std::endl; | |
297 | return new SnapCreateOp(m_op, &context, m_stats); | |
298 | ||
299 | case TEST_OP_SNAP_REMOVE: | |
300 | if (context.snaps.size() <= context.snaps_in_use.size()) { | |
301 | return NULL; | |
302 | } | |
303 | while (true) { | |
304 | int snap = rand_choose(context.snaps)->first; | |
305 | if (context.snaps_in_use.lookup(snap)) | |
306 | continue; // in use; try again! | |
307 | cout << m_op << ": " << "snap_remove snap " << snap << std::endl; | |
308 | return new SnapRemoveOp(m_op, &context, snap, m_stats); | |
309 | } | |
310 | ||
311 | case TEST_OP_ROLLBACK: | |
312 | { | |
313 | string oid = *(rand_choose(context.oid_not_in_use)); | |
314 | cout << m_op << ": " << "rollback oid " << oid << " current snap is " | |
315 | << context.current_snap << std::endl; | |
316 | return new RollbackOp(m_op, &context, oid); | |
317 | } | |
318 | ||
319 | case TEST_OP_SETATTR: | |
320 | oid = *(rand_choose(context.oid_not_in_use)); | |
321 | cout << m_op << ": " << "setattr oid " << oid | |
322 | << " current snap is " << context.current_snap << std::endl; | |
323 | return new SetAttrsOp(m_op, &context, oid, m_stats); | |
324 | ||
325 | case TEST_OP_RMATTR: | |
326 | oid = *(rand_choose(context.oid_not_in_use)); | |
327 | cout << m_op << ": " << "rmattr oid " << oid | |
328 | << " current snap is " << context.current_snap << std::endl; | |
329 | return new RemoveAttrsOp(m_op, &context, oid, m_stats); | |
330 | ||
331 | case TEST_OP_WATCH: | |
332 | oid = *(rand_choose(context.oid_not_in_use)); | |
333 | cout << m_op << ": " << "watch oid " << oid | |
334 | << " current snap is " << context.current_snap << std::endl; | |
335 | return new WatchOp(m_op, &context, oid, m_stats); | |
336 | ||
337 | case TEST_OP_COPY_FROM: | |
338 | oid = *(rand_choose(context.oid_not_in_use)); | |
339 | do { | |
340 | oid2 = *(rand_choose(context.oid_not_in_use)); | |
341 | } while (oid == oid2); | |
342 | cout << m_op << ": " << "copy_from oid " << oid << " from oid " << oid2 | |
343 | << " current snap is " << context.current_snap << std::endl; | |
344 | return new CopyFromOp(m_op, &context, oid, oid2, m_stats); | |
345 | ||
346 | case TEST_OP_HIT_SET_LIST: | |
347 | { | |
348 | uint32_t hash = rjhash32(rand()); | |
349 | cout << m_op << ": " << "hit_set_list " << hash << std::endl; | |
350 | return new HitSetListOp(m_op, &context, hash, m_stats); | |
351 | } | |
352 | ||
353 | case TEST_OP_UNDIRTY: | |
354 | { | |
355 | oid = *(rand_choose(context.oid_not_in_use)); | |
356 | cout << m_op << ": " << "undirty oid " << oid << std::endl; | |
357 | return new UndirtyOp(m_op, &context, oid, m_stats); | |
358 | } | |
359 | ||
360 | case TEST_OP_IS_DIRTY: | |
361 | { | |
362 | oid = *(rand_choose(context.oid_not_flushing)); | |
363 | return new IsDirtyOp(m_op, &context, oid, m_stats); | |
364 | } | |
365 | ||
366 | case TEST_OP_CACHE_FLUSH: | |
367 | { | |
368 | oid = *(rand_choose(context.oid_not_in_use)); | |
369 | return new CacheFlushOp(m_op, &context, oid, m_stats, true); | |
370 | } | |
371 | ||
372 | case TEST_OP_CACHE_TRY_FLUSH: | |
373 | { | |
374 | oid = *(rand_choose(context.oid_not_in_use)); | |
375 | return new CacheFlushOp(m_op, &context, oid, m_stats, false); | |
376 | } | |
377 | ||
378 | case TEST_OP_CACHE_EVICT: | |
379 | { | |
380 | oid = *(rand_choose(context.oid_not_in_use)); | |
381 | return new CacheEvictOp(m_op, &context, oid, m_stats); | |
382 | } | |
383 | ||
384 | case TEST_OP_APPEND: | |
385 | oid = *(rand_choose(context.oid_not_in_use)); | |
386 | cout << "append oid " << oid << " current snap is " | |
387 | << context.current_snap << std::endl; | |
388 | return new WriteOp(m_op, &context, oid, true, false, m_stats); | |
389 | ||
390 | case TEST_OP_APPEND_EXCL: | |
391 | oid = *(rand_choose(context.oid_not_in_use)); | |
392 | cout << "append oid (excl) " << oid << " current snap is " | |
393 | << context.current_snap << std::endl; | |
394 | return new WriteOp(m_op, &context, oid, true, true, m_stats); | |
395 | ||
11fdf7f2 TL |
396 | case TEST_OP_CHUNK_READ: |
397 | oid = *(rand_choose(context.oid_not_in_use)); | |
398 | cout << m_op << ": " << "chunk read oid " << oid << " target oid " << oid2 << std::endl; | |
399 | return new ChunkReadOp(m_op, &context, oid, context.pool_name, false, m_stats); | |
400 | ||
401 | case TEST_OP_TIER_PROMOTE: | |
402 | oid = *(rand_choose(context.oid_not_in_use)); | |
403 | cout << m_op << ": " << "tier_promote oid " << oid << std::endl; | |
404 | return new TierPromoteOp(m_op, &context, oid, m_stats); | |
405 | ||
9f95a23c TL |
406 | case TEST_OP_TIER_FLUSH: |
407 | oid = *(rand_choose(context.oid_not_in_use)); | |
408 | cout << m_op << ": " << "tier_flush oid " << oid << std::endl; | |
409 | return new TierFlushOp(m_op, &context, oid, m_stats); | |
410 | ||
31f18b77 FG |
411 | case TEST_OP_SET_REDIRECT: |
412 | oid = *(rand_choose(context.oid_not_in_use)); | |
413 | oid2 = *(rand_choose(context.oid_redirect_not_in_use)); | |
414 | cout << m_op << ": " << "set_redirect oid " << oid << " target oid " << oid2 << std::endl; | |
415 | return new SetRedirectOp(m_op, &context, oid, oid2, context.pool_name, m_stats); | |
416 | ||
417 | case TEST_OP_UNSET_REDIRECT: | |
418 | oid = *(rand_choose(context.oid_not_in_use)); | |
419 | cout << m_op << ": " << "unset_redirect oid " << oid << std::endl; | |
420 | return new UnsetRedirectOp(m_op, &context, oid, m_stats); | |
421 | ||
20effc67 TL |
422 | case TEST_OP_SET_CHUNK: |
423 | { | |
424 | ceph_assert(m_enable_dedup); | |
425 | oid = *(rand_choose(context.oid_not_in_use)); | |
426 | cout << m_op << ": " << "set_chunk oid " << oid | |
427 | << " target oid " << std::endl; | |
428 | return new SetChunkOp(m_op, &context, oid, "", m_stats); | |
429 | } | |
430 | ||
431 | case TEST_OP_TIER_EVICT: | |
432 | oid = *(rand_choose(context.oid_not_in_use)); | |
433 | cout << m_op << ": " << "tier_evict oid " << oid << std::endl; | |
434 | return new TierEvictOp(m_op, &context, oid, m_stats); | |
435 | ||
7c673cae FG |
436 | default: |
437 | cerr << m_op << ": Invalid op type " << type << std::endl; | |
438 | ceph_abort(); | |
439 | return nullptr; | |
440 | } | |
441 | } | |
442 | ||
443 | TestOp *m_nextop; | |
444 | int m_op; | |
445 | int m_ops; | |
446 | int m_seconds; | |
447 | int m_objects; | |
448 | time_t m_start; | |
449 | TestOpStat *m_stats; | |
450 | map<TestOpType, unsigned int> m_weight_sums; | |
451 | unsigned int m_total_weight; | |
452 | bool m_ec_pool; | |
453 | bool m_balance_reads; | |
9f95a23c | 454 | bool m_localize_reads; |
31f18b77 | 455 | bool m_set_redirect; |
11fdf7f2 | 456 | bool m_set_chunk; |
9f95a23c | 457 | bool m_enable_dedup; |
7c673cae FG |
458 | }; |
459 | ||
460 | int main(int argc, char **argv) | |
461 | { | |
462 | int ops = 1000; | |
463 | int objects = 50; | |
464 | int max_in_flight = 16; | |
465 | int64_t size = 4000000; // 4 MB | |
466 | int64_t min_stride_size = -1, max_stride_size = -1; | |
467 | int max_seconds = 0; | |
468 | bool pool_snaps = false; | |
469 | bool write_fadvise_dontneed = false; | |
470 | ||
471 | struct { | |
472 | TestOpType op; | |
473 | const char *name; | |
474 | bool ec_pool_valid; | |
475 | } op_types[] = { | |
476 | { TEST_OP_READ, "read", true }, | |
477 | { TEST_OP_WRITE, "write", false }, | |
478 | { TEST_OP_WRITE_EXCL, "write_excl", false }, | |
479 | { TEST_OP_WRITESAME, "writesame", false }, | |
480 | { TEST_OP_DELETE, "delete", true }, | |
481 | { TEST_OP_SNAP_CREATE, "snap_create", true }, | |
482 | { TEST_OP_SNAP_REMOVE, "snap_remove", true }, | |
483 | { TEST_OP_ROLLBACK, "rollback", true }, | |
484 | { TEST_OP_SETATTR, "setattr", true }, | |
485 | { TEST_OP_RMATTR, "rmattr", true }, | |
486 | { TEST_OP_WATCH, "watch", true }, | |
487 | { TEST_OP_COPY_FROM, "copy_from", true }, | |
488 | { TEST_OP_HIT_SET_LIST, "hit_set_list", true }, | |
489 | { TEST_OP_IS_DIRTY, "is_dirty", true }, | |
490 | { TEST_OP_UNDIRTY, "undirty", true }, | |
491 | { TEST_OP_CACHE_FLUSH, "cache_flush", true }, | |
492 | { TEST_OP_CACHE_TRY_FLUSH, "cache_try_flush", true }, | |
493 | { TEST_OP_CACHE_EVICT, "cache_evict", true }, | |
494 | { TEST_OP_APPEND, "append", true }, | |
495 | { TEST_OP_APPEND_EXCL, "append_excl", true }, | |
31f18b77 FG |
496 | { TEST_OP_SET_REDIRECT, "set_redirect", true }, |
497 | { TEST_OP_UNSET_REDIRECT, "unset_redirect", true }, | |
11fdf7f2 TL |
498 | { TEST_OP_CHUNK_READ, "chunk_read", true }, |
499 | { TEST_OP_TIER_PROMOTE, "tier_promote", true }, | |
9f95a23c | 500 | { TEST_OP_TIER_FLUSH, "tier_flush", true }, |
20effc67 TL |
501 | { TEST_OP_SET_CHUNK, "set_chunk", true }, |
502 | { TEST_OP_TIER_EVICT, "tier_evict", true }, | |
7c673cae FG |
503 | { TEST_OP_READ /* grr */, NULL }, |
504 | }; | |
505 | ||
20effc67 TL |
506 | struct { |
507 | const char *name; | |
508 | } chunk_algo_types[] = { | |
509 | { "fastcdc" }, | |
510 | { "fixcdc" }, | |
511 | }; | |
512 | ||
7c673cae FG |
513 | map<TestOpType, unsigned int> op_weights; |
514 | string pool_name = "rbd"; | |
11fdf7f2 | 515 | string low_tier_pool_name = ""; |
7c673cae FG |
516 | bool ec_pool = false; |
517 | bool no_omap = false; | |
518 | bool no_sparse = false; | |
519 | bool balance_reads = false; | |
9f95a23c | 520 | bool localize_reads = false; |
31f18b77 | 521 | bool set_redirect = false; |
11fdf7f2 | 522 | bool set_chunk = false; |
9f95a23c | 523 | bool enable_dedup = false; |
20effc67 TL |
524 | string chunk_algo = ""; |
525 | string chunk_size = ""; | |
526 | ||
7c673cae FG |
527 | |
528 | for (int i = 1; i < argc; ++i) { | |
529 | if (strcmp(argv[i], "--max-ops") == 0) | |
530 | ops = atoi(argv[++i]); | |
531 | else if (strcmp(argv[i], "--pool") == 0) | |
532 | pool_name = argv[++i]; | |
533 | else if (strcmp(argv[i], "--max-seconds") == 0) | |
534 | max_seconds = atoi(argv[++i]); | |
535 | else if (strcmp(argv[i], "--objects") == 0) | |
536 | objects = atoi(argv[++i]); | |
537 | else if (strcmp(argv[i], "--max-in-flight") == 0) | |
538 | max_in_flight = atoi(argv[++i]); | |
539 | else if (strcmp(argv[i], "--size") == 0) | |
540 | size = atoi(argv[++i]); | |
541 | else if (strcmp(argv[i], "--min-stride-size") == 0) | |
542 | min_stride_size = atoi(argv[++i]); | |
543 | else if (strcmp(argv[i], "--max-stride-size") == 0) | |
544 | max_stride_size = atoi(argv[++i]); | |
545 | else if (strcmp(argv[i], "--no-omap") == 0) | |
546 | no_omap = true; | |
547 | else if (strcmp(argv[i], "--no-sparse") == 0) | |
548 | no_sparse = true; | |
9f95a23c | 549 | else if (strcmp(argv[i], "--balance-reads") == 0) |
7c673cae | 550 | balance_reads = true; |
9f95a23c TL |
551 | else if (strcmp(argv[i], "--localize-reads") == 0) |
552 | localize_reads = true; | |
7c673cae FG |
553 | else if (strcmp(argv[i], "--pool-snaps") == 0) |
554 | pool_snaps = true; | |
555 | else if (strcmp(argv[i], "--write-fadvise-dontneed") == 0) | |
556 | write_fadvise_dontneed = true; | |
557 | else if (strcmp(argv[i], "--ec-pool") == 0) { | |
558 | if (!op_weights.empty()) { | |
559 | cerr << "--ec-pool must be specified prior to any ops" << std::endl; | |
560 | exit(1); | |
561 | } | |
562 | ec_pool = true; | |
563 | no_omap = true; | |
564 | no_sparse = true; | |
565 | } else if (strcmp(argv[i], "--op") == 0) { | |
566 | i++; | |
567 | if (i == argc) { | |
568 | cerr << "Missing op after --op" << std::endl; | |
569 | return 1; | |
570 | } | |
571 | int j; | |
572 | for (j = 0; op_types[j].name; ++j) { | |
573 | if (strcmp(op_types[j].name, argv[i]) == 0) { | |
574 | break; | |
575 | } | |
576 | } | |
577 | if (!op_types[j].name) { | |
578 | cerr << "unknown op " << argv[i] << std::endl; | |
579 | exit(1); | |
580 | } | |
581 | i++; | |
582 | if (i == argc) { | |
583 | cerr << "Weight unspecified." << std::endl; | |
584 | return 1; | |
585 | } | |
586 | int weight = atoi(argv[i]); | |
587 | if (weight < 0) { | |
588 | cerr << "Weights must be nonnegative." << std::endl; | |
589 | return 1; | |
590 | } else if (weight > 0) { | |
591 | if (ec_pool && !op_types[j].ec_pool_valid) { | |
592 | cerr << "Error: cannot use op type " << op_types[j].name | |
593 | << " with --ec-pool" << std::endl; | |
594 | exit(1); | |
595 | } | |
596 | cout << "adding op weight " << op_types[j].name << " -> " << weight << std::endl; | |
597 | op_weights.insert(pair<TestOpType, unsigned int>(op_types[j].op, weight)); | |
598 | } | |
31f18b77 FG |
599 | } else if (strcmp(argv[i], "--set_redirect") == 0) { |
600 | set_redirect = true; | |
11fdf7f2 TL |
601 | } else if (strcmp(argv[i], "--set_chunk") == 0) { |
602 | set_chunk = true; | |
603 | } else if (strcmp(argv[i], "--low_tier_pool") == 0) { | |
604 | /* | |
605 | * disallow redirect or chunk object into the same pool | |
606 | * to prevent the race. see https://github.com/ceph/ceph/pull/20096 | |
607 | */ | |
608 | low_tier_pool_name = argv[++i]; | |
9f95a23c TL |
609 | } else if (strcmp(argv[i], "--enable_dedup") == 0) { |
610 | enable_dedup = true; | |
20effc67 TL |
611 | } else if (strcmp(argv[i], "--dedup_chunk_algo") == 0) { |
612 | i++; | |
613 | if (i == argc) { | |
614 | cerr << "Missing chunking algorithm after --dedup_chunk_algo" << std::endl; | |
615 | return 1; | |
616 | } | |
617 | int j; | |
618 | for (j = 0; chunk_algo_types[j].name; ++j) { | |
619 | if (strcmp(chunk_algo_types[j].name, argv[i]) == 0) { | |
620 | break; | |
621 | } | |
622 | } | |
623 | if (!chunk_algo_types[j].name) { | |
624 | cerr << "unknown op " << argv[i] << std::endl; | |
625 | exit(1); | |
626 | } | |
627 | chunk_algo = chunk_algo_types[j].name; | |
628 | } else if (strcmp(argv[i], "--dedup_chunk_size") == 0) { | |
629 | chunk_size = argv[++i]; | |
7c673cae FG |
630 | } else { |
631 | cerr << "unknown arg " << argv[i] << std::endl; | |
7c673cae FG |
632 | exit(1); |
633 | } | |
634 | } | |
635 | ||
11fdf7f2 TL |
636 | if (set_redirect || set_chunk) { |
637 | if (low_tier_pool_name == "") { | |
9f95a23c | 638 | cerr << "low_tier_pool is needed" << std::endl; |
11fdf7f2 TL |
639 | exit(1); |
640 | } | |
641 | } | |
642 | ||
20effc67 TL |
643 | if (enable_dedup) { |
644 | if (chunk_algo == "" || chunk_size == "") { | |
645 | cerr << "Missing chunking algorithm: " << chunk_algo | |
646 | << " or chunking size: " << chunk_size << std::endl; | |
647 | exit(1); | |
648 | } | |
649 | } | |
650 | ||
7c673cae FG |
651 | if (op_weights.empty()) { |
652 | cerr << "No operations specified" << std::endl; | |
7c673cae FG |
653 | exit(1); |
654 | } | |
655 | ||
656 | if (min_stride_size < 0) | |
657 | min_stride_size = size / 10; | |
658 | if (max_stride_size < 0) | |
659 | max_stride_size = size / 5; | |
660 | ||
661 | cout << pretty_version_to_str() << std::endl; | |
662 | cout << "Configuration:" << std::endl | |
663 | << "\tNumber of operations: " << ops << std::endl | |
664 | << "\tNumber of objects: " << objects << std::endl | |
665 | << "\tMax in flight operations: " << max_in_flight << std::endl | |
666 | << "\tObject size (in bytes): " << size << std::endl | |
667 | << "\tWrite stride min: " << min_stride_size << std::endl | |
668 | << "\tWrite stride max: " << max_stride_size << std::endl; | |
669 | ||
c07f9fc5 FG |
670 | if (min_stride_size >= max_stride_size) { |
671 | cerr << "Error: max_stride_size must be more than min_stride_size" | |
7c673cae FG |
672 | << std::endl; |
673 | return 1; | |
674 | } | |
675 | ||
676 | if (min_stride_size > size || max_stride_size > size) { | |
677 | cerr << "Error: min_stride_size and max_stride_size must be " | |
678 | << "smaller than object size" << std::endl; | |
679 | return 1; | |
680 | } | |
681 | ||
682 | if (max_in_flight * 2 > objects) { | |
683 | cerr << "Error: max_in_flight must be <= than the number of objects / 2" | |
684 | << std::endl; | |
685 | return 1; | |
686 | } | |
687 | ||
688 | char *id = getenv("CEPH_CLIENT_ID"); | |
689 | RadosTestContext context( | |
690 | pool_name, | |
691 | max_in_flight, | |
692 | size, | |
693 | min_stride_size, | |
694 | max_stride_size, | |
695 | no_omap, | |
696 | no_sparse, | |
697 | pool_snaps, | |
698 | write_fadvise_dontneed, | |
11fdf7f2 | 699 | low_tier_pool_name, |
9f95a23c | 700 | enable_dedup, |
20effc67 TL |
701 | chunk_algo, |
702 | chunk_size, | |
7c673cae FG |
703 | id); |
704 | ||
705 | TestOpStat stats; | |
706 | WeightedTestGenerator gen = WeightedTestGenerator( | |
707 | ops, objects, | |
708 | op_weights, &stats, max_seconds, | |
9f95a23c TL |
709 | ec_pool, balance_reads, localize_reads, |
710 | set_redirect, set_chunk, enable_dedup); | |
7c673cae FG |
711 | int r = context.init(); |
712 | if (r < 0) { | |
713 | cerr << "Error initializing rados test context: " | |
714 | << cpp_strerror(r) << std::endl; | |
715 | exit(1); | |
716 | } | |
717 | context.loop(&gen); | |
20effc67 TL |
718 | if (enable_dedup) { |
719 | if (!context.check_chunks_refcount(context.low_tier_io_ctx, context.io_ctx)) { | |
720 | cerr << " Invalid refcount " << std::endl; | |
721 | exit(1); | |
722 | } | |
723 | } | |
7c673cae FG |
724 | |
725 | context.shutdown(); | |
726 | cerr << context.errors << " errors." << std::endl; | |
727 | cerr << stats << std::endl; | |
728 | return 0; | |
729 | } |