Commit | Line | Data |
---|---|---|
da751ca9 JA |
1 | /* |
2 | * CPU engine | |
3 | * | |
4 | * Doesn't transfer any data, merely burns CPU cycles according to | |
5 | * the settings. | |
6 | * | |
7 | */ | |
5f350952 | 8 | #include "../fio.h" |
d220c761 | 9 | #include "../optgroup.h" |
2866c82d | 10 | |
9de473a8 EV |
11 | // number of 32 bit integers to sort |
12 | size_t qsort_size = (256 * (1ULL << 10)); // 256KB | |
13 | ||
b2139223 | 14 | struct mwc { |
9de473a8 | 15 | uint32_t w; |
b2139223 | 16 | uint32_t z; |
9de473a8 EV |
17 | }; |
18 | ||
19 | enum stress_mode { | |
20 | FIO_CPU_NOOP = 0, | |
21 | FIO_CPU_QSORT = 1, | |
22 | }; | |
23 | ||
0353050f | 24 | struct cpu_options { |
a1f871c7 | 25 | void *pad; |
0353050f JA |
26 | unsigned int cpuload; |
27 | unsigned int cpucycle; | |
9de473a8 | 28 | enum stress_mode cpumode; |
046395d7 | 29 | unsigned int exit_io_done; |
9de473a8 | 30 | int32_t *qsort_data; |
0353050f JA |
31 | }; |
32 | ||
33 | static struct fio_option options[] = { | |
34 | { | |
35 | .name = "cpuload", | |
36 | .lname = "CPU load", | |
37 | .type = FIO_OPT_INT, | |
38 | .off1 = offsetof(struct cpu_options, cpuload), | |
39 | .help = "Use this percentage of CPU", | |
cbd9148f | 40 | .category = FIO_OPT_C_ENGINE, |
0353050f JA |
41 | .group = FIO_OPT_G_INVALID, |
42 | }, | |
9de473a8 EV |
43 | { |
44 | .name = "cpumode", | |
45 | .lname = "cpumode", | |
46 | .type = FIO_OPT_STR, | |
47 | .help = "Stress mode", | |
48 | .off1 = offsetof(struct cpu_options, cpumode), | |
49 | .def = "noop", | |
50 | .posval = { | |
51 | { .ival = "noop", | |
52 | .oval = FIO_CPU_NOOP, | |
53 | .help = "NOOP instructions", | |
54 | }, | |
55 | { .ival = "qsort", | |
56 | .oval = FIO_CPU_QSORT, | |
57 | .help = "QSORT computation", | |
58 | }, | |
59 | }, | |
60 | .category = FIO_OPT_C_ENGINE, | |
61 | .group = FIO_OPT_G_INVALID, | |
62 | }, | |
0353050f JA |
63 | { |
64 | .name = "cpuchunks", | |
65 | .lname = "CPU chunk", | |
66 | .type = FIO_OPT_INT, | |
67 | .off1 = offsetof(struct cpu_options, cpucycle), | |
68 | .help = "Length of the CPU burn cycles (usecs)", | |
69 | .def = "50000", | |
70 | .parent = "cpuload", | |
71 | .hide = 1, | |
cbd9148f | 72 | .category = FIO_OPT_C_ENGINE, |
0353050f JA |
73 | .group = FIO_OPT_G_INVALID, |
74 | }, | |
046395d7 JA |
75 | { |
76 | .name = "exit_on_io_done", | |
77 | .lname = "Exit when IO threads are done", | |
78 | .type = FIO_OPT_BOOL, | |
79 | .off1 = offsetof(struct cpu_options, exit_io_done), | |
80 | .help = "Exit when IO threads finish", | |
81 | .def = "0", | |
cbd9148f | 82 | .category = FIO_OPT_C_ENGINE, |
046395d7 JA |
83 | .group = FIO_OPT_G_INVALID, |
84 | }, | |
0353050f JA |
85 | { |
86 | .name = NULL, | |
87 | }, | |
88 | }; | |
89 | ||
9de473a8 EV |
90 | /* |
91 | * mwc32() | |
92 | * Multiply-with-carry random numbers | |
93 | * fast pseudo random number generator, see | |
94 | * http://www.cse.yorku.ca/~oz/marsaglia-rng.html | |
95 | */ | |
b2139223 | 96 | uint32_t mwc32(struct mwc *mwc) |
9de473a8 EV |
97 | { |
98 | mwc->z = 36969 * (mwc->z & 65535) + (mwc->z >> 16); | |
99 | mwc->w = 18000 * (mwc->w & 65535) + (mwc->w >> 16); | |
100 | return (mwc->z << 16) + mwc->w; | |
101 | } | |
102 | ||
103 | /* | |
104 | * stress_qsort_cmp_1() | |
105 | * qsort comparison - sort on int32 values | |
106 | */ | |
107 | static int stress_qsort_cmp_1(const void *p1, const void *p2) | |
108 | { | |
109 | const int32_t *i1 = (const int32_t *)p1; | |
110 | const int32_t *i2 = (const int32_t *)p2; | |
111 | ||
112 | if (*i1 > *i2) | |
113 | return 1; | |
114 | else if (*i1 < *i2) | |
115 | return -1; | |
116 | else | |
117 | return 0; | |
118 | } | |
119 | ||
120 | /* | |
121 | * stress_qsort_cmp_2() | |
122 | * qsort comparison - reverse sort on int32 values | |
123 | */ | |
124 | static int stress_qsort_cmp_2(const void *p1, const void *p2) | |
125 | { | |
126 | return stress_qsort_cmp_1(p2, p1); | |
127 | } | |
128 | ||
129 | /* | |
130 | * stress_qsort_cmp_3() | |
131 | * qsort comparison - sort on int8 values | |
132 | */ | |
133 | static int stress_qsort_cmp_3(const void *p1, const void *p2) | |
134 | { | |
135 | const int8_t *i1 = (const int8_t *)p1; | |
136 | const int8_t *i2 = (const int8_t *)p2; | |
137 | ||
138 | /* Force re-ordering on 8 bit value */ | |
139 | return *i1 - *i2; | |
140 | } | |
141 | ||
142 | static int do_qsort(struct thread_data *td) | |
143 | { | |
144 | struct thread_options *o = &td->o; | |
145 | struct cpu_options *co = td->eo; | |
146 | struct timespec start, now; | |
b2139223 | 147 | |
9de473a8 | 148 | fio_get_mono_time(&start); |
b2139223 | 149 | |
9de473a8 EV |
150 | /* Sort "random" data */ |
151 | qsort(co->qsort_data, qsort_size, sizeof(*(co->qsort_data)), stress_qsort_cmp_1); | |
152 | ||
153 | /* Reverse sort */ | |
154 | qsort(co->qsort_data, qsort_size, sizeof(*(co->qsort_data)), stress_qsort_cmp_2); | |
155 | ||
156 | /* And re-order by byte compare */ | |
157 | qsort((uint8_t *)co->qsort_data, qsort_size * 4, sizeof(uint8_t), stress_qsort_cmp_3); | |
158 | ||
159 | /* Reverse sort this again */ | |
160 | qsort(co->qsort_data, qsort_size, sizeof(*(co->qsort_data)), stress_qsort_cmp_2); | |
161 | fio_get_mono_time(&now); | |
162 | ||
b2139223 JA |
163 | /* Adjusting cpucycle automatically to be as close as possible to the |
164 | * expected cpuload The time to execute do_qsort() may change over time | |
165 | * as per : - the job concurrency - the cpu clock adjusted by the power | |
166 | * management After every do_qsort() call, the next thinktime is | |
167 | * adjusted regarding the last run performance | |
9de473a8 EV |
168 | */ |
169 | co->cpucycle = utime_since(&start, &now); | |
b2139223 JA |
170 | o->thinktime = ((unsigned long long) co->cpucycle * |
171 | (100 - co->cpuload)) / co->cpuload; | |
9de473a8 EV |
172 | |
173 | return 0; | |
174 | } | |
0353050f | 175 | |
2e4ef4fb JA |
176 | static enum fio_q_status fio_cpuio_queue(struct thread_data *td, |
177 | struct io_u fio_unused *io_u) | |
ba0fbe10 | 178 | { |
0353050f JA |
179 | struct cpu_options *co = td->eo; |
180 | ||
046395d7 JA |
181 | if (co->exit_io_done && !fio_running_or_pending_io_threads()) { |
182 | td->done = 1; | |
183 | return FIO_Q_BUSY; | |
184 | } | |
185 | ||
9de473a8 | 186 | switch (co->cpumode) { |
b2139223 JA |
187 | case FIO_CPU_NOOP: |
188 | usec_spin(co->cpucycle); | |
189 | break; | |
190 | case FIO_CPU_QSORT: | |
191 | do_qsort(td); | |
192 | break; | |
9de473a8 EV |
193 | } |
194 | ||
ba0fbe10 JA |
195 | return FIO_Q_COMPLETED; |
196 | } | |
197 | ||
9de473a8 EV |
198 | static int noop_init(struct thread_data *td) |
199 | { | |
200 | struct cpu_options *co = td->eo; | |
201 | ||
202 | log_info("%s (noop): ioengine=%s, cpuload=%u, cpucycle=%u\n", | |
203 | td->o.name, td->io_ops->name, co->cpuload, co->cpucycle); | |
204 | return 0; | |
205 | } | |
206 | ||
207 | static int qsort_cleanup(struct thread_data *td) | |
208 | { | |
209 | struct cpu_options *co = td->eo; | |
b2139223 JA |
210 | |
211 | if (co->qsort_data) { | |
9de473a8 | 212 | free(co->qsort_data); |
b2139223 JA |
213 | co->qsort_data = NULL; |
214 | } | |
215 | ||
9de473a8 EV |
216 | return 0; |
217 | } | |
218 | ||
219 | static int qsort_init(struct thread_data *td) | |
220 | { | |
b2139223 JA |
221 | /* Setting up a default entropy */ |
222 | struct mwc mwc = { 521288629UL, 362436069UL }; | |
9de473a8 | 223 | struct cpu_options *co = td->eo; |
b2139223 JA |
224 | int32_t *ptr; |
225 | int i; | |
9de473a8 EV |
226 | |
227 | co->qsort_data = calloc(qsort_size, sizeof(*co->qsort_data)); | |
228 | if (co->qsort_data == NULL) { | |
229 | td_verror(td, ENOMEM, "qsort_init"); | |
230 | return 1; | |
231 | } | |
232 | ||
233 | /* This is expensive, init the memory once */ | |
b2139223 | 234 | for (ptr = co->qsort_data, i = 0; i < qsort_size; i++) |
9de473a8 EV |
235 | *ptr++ = mwc32(&mwc); |
236 | ||
237 | log_info("%s (qsort): ioengine=%s, cpuload=%u, cpucycle=%u\n", | |
238 | td->o.name, td->io_ops->name, co->cpuload, co->cpucycle); | |
239 | ||
240 | return 0; | |
241 | } | |
242 | ||
2866c82d JA |
243 | static int fio_cpuio_init(struct thread_data *td) |
244 | { | |
2dc1bbeb | 245 | struct thread_options *o = &td->o; |
0353050f | 246 | struct cpu_options *co = td->eo; |
9de473a8 | 247 | int td_previous_state; |
f0fb0d14 | 248 | char *msg; |
2dc1bbeb | 249 | |
0353050f | 250 | if (!co->cpuload) { |
ba0fbe10 | 251 | td_vmsg(td, EINVAL, "cpu thread needs rate (cpuload=)","cpuio"); |
2866c82d | 252 | return 1; |
ba0fbe10 JA |
253 | } |
254 | ||
0353050f JA |
255 | if (co->cpuload > 100) |
256 | co->cpuload = 100; | |
2866c82d | 257 | |
b2139223 | 258 | /* Saving the current thread state */ |
9de473a8 | 259 | td_previous_state = td->runstate; |
b2139223 | 260 | |
9de473a8 EV |
261 | /* Reporting that we are preparing the engine |
262 | * This is useful as the qsort() calibration takes time | |
263 | * This prevents the job from starting before init is completed | |
264 | */ | |
265 | td_set_runstate(td, TD_SETTING_UP); | |
266 | ||
ba0fbe10 JA |
267 | /* |
268 | * set thinktime_sleep and thinktime_spin appropriately | |
269 | */ | |
2dc1bbeb | 270 | o->thinktime_blocks = 1; |
33f42c20 | 271 | o->thinktime_blocks_type = THINKTIME_BLOCKS_TYPE_COMPLETE; |
2dc1bbeb | 272 | o->thinktime_spin = 0; |
b2139223 JA |
273 | o->thinktime = ((unsigned long long) co->cpucycle * |
274 | (100 - co->cpuload)) / co->cpuload; | |
2866c82d | 275 | |
2dc1bbeb | 276 | o->nr_files = o->open_files = 1; |
0353050f | 277 | |
9de473a8 | 278 | switch (co->cpumode) { |
b2139223 JA |
279 | case FIO_CPU_NOOP: |
280 | noop_init(td); | |
281 | break; | |
282 | case FIO_CPU_QSORT: | |
283 | qsort_init(td); | |
284 | break; | |
285 | default: | |
f0fb0d14 BVA |
286 | if (asprintf(&msg, "bad cpu engine mode: %d", co->cpumode) < 0) |
287 | msg = NULL; | |
288 | td_vmsg(td, EINVAL, msg ? : "(?)", __func__); | |
289 | free(msg); | |
b2139223 | 290 | return 1; |
9de473a8 | 291 | } |
0353050f | 292 | |
b2139223 | 293 | /* Let's restore the previous state. */ |
9de473a8 | 294 | td_set_runstate(td, td_previous_state); |
ba0fbe10 JA |
295 | return 0; |
296 | } | |
297 | ||
9de473a8 EV |
298 | static void fio_cpuio_cleanup(struct thread_data *td) |
299 | { | |
300 | struct cpu_options *co = td->eo; | |
301 | ||
302 | switch (co->cpumode) { | |
b2139223 JA |
303 | case FIO_CPU_NOOP: |
304 | break; | |
305 | case FIO_CPU_QSORT: | |
306 | qsort_cleanup(td); | |
307 | break; | |
9de473a8 EV |
308 | } |
309 | } | |
310 | ||
f4e62a5f JA |
311 | static int fio_cpuio_open(struct thread_data fio_unused *td, |
312 | struct fio_file fio_unused *f) | |
ba0fbe10 | 313 | { |
2866c82d JA |
314 | return 0; |
315 | } | |
316 | ||
5f350952 | 317 | static struct ioengine_ops ioengine = { |
b2139223 JA |
318 | .name = "cpuio", |
319 | .version = FIO_IOOPS_VERSION, | |
320 | .queue = fio_cpuio_queue, | |
321 | .init = fio_cpuio_init, | |
322 | .cleanup = fio_cpuio_cleanup, | |
323 | .open_file = fio_cpuio_open, | |
324 | .flags = FIO_SYNCIO | FIO_DISKLESSIO | FIO_NOIO, | |
0353050f JA |
325 | .options = options, |
326 | .option_struct_size = sizeof(struct cpu_options), | |
2866c82d | 327 | }; |
5f350952 JA |
328 | |
329 | static void fio_init fio_cpuio_register(void) | |
330 | { | |
331 | register_ioengine(&ioengine); | |
332 | } | |
333 | ||
334 | static void fio_exit fio_cpuio_unregister(void) | |
335 | { | |
336 | unregister_ioengine(&ioengine); | |
337 | } |