Fix verification full mode
[archipelago] / xseg / peers / user / bench-utils.c
1 /*
2  * Copyright 2012 GRNET S.A. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or
5  * without modification, are permitted provided that the following
6  * conditions are met:
7  *
8  *   1. Redistributions of source code must retain the above
9  *      copyright notice, this list of conditions and the following
10  *      disclaimer.
11  *   2. Redistributions in binary form must reproduce the above
12  *      copyright notice, this list of conditions and the following
13  *      disclaimer in the documentation and/or other materials
14  *      provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  *
29  * The views and conclusions contained in the software and
30  * documentation are those of the authors and should not be
31  * interpreted as representing official policies, either expressed
32  * or implied, of GRNET S.A.
33  */
34
35 #define _GNU_SOURCE
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <unistd.h>
39 #include <sys/syscall.h>
40 #include <sys/types.h>
41 #include <pthread.h>
42 #include <xseg/xseg.h>
43 #include <peer.h>
44 #include <time.h>
45 #include <sys/util.h>
46 #include <signal.h>
47 #include <bench-xseg.h>
48
49 #include <math.h>
50 #include <string.h>
51
52 #define PRINT_SIG(__who, __sig)                                                                 \
53         printf("%s (%lu): id %lu, object %lu, offset %lu\n",            \
54                         #__who, (uint64_t)(__sig),                                                      \
55                         ((struct signature *)__sig)->id,                                        \
56                         ((struct signature *)__sig)->object,                            \
57                         ((struct signature *)__sig)->offset);
58
59 /******************************\
60  * Static miscellaneous tools *
61 \******************************/
62 struct timespec delay = {0, 4000000};
63
64 static inline uint64_t _get_id()
65 {
66         return atol(global_id + 6); /* cut the "bench-" part*/
67 }
68
69 static inline uint64_t _get_object_from_name(char *name)
70 {
71         return atol(name + IDLEN); /* cut the "bench-908135-" part*/
72 }
73
74 static inline uint64_t _get_object(struct bench *prefs, uint64_t new)
75 {
76         return new / (prefs->os / prefs->bs);
77 }
78
79 static inline int _snap_to_bound8(uint64_t space)
80 {
81         return space > 8 ? 8 : space;
82 }
83
84 static inline double timespec2double(struct timespec num)
85 {
86         return (double) (num.tv_sec * pow(10, 9) + num.tv_nsec);
87 }
88
89 static inline void write_sig(struct bench_lfsr *sg,     uint64_t *d, uint64_t s,
90                 int pos)
91 {
92         uint64_t i;
93         uint64_t last_val;
94         uint64_t space_left;
95
96         /* Write random numbers (based on global_id) every 24 bytes */
97         /* TODO: Should we use memcpy? */
98         for (i = pos; i < (s / 8) - (3 - pos); i += 3)
99                 *(d + i) = lfsr_next(sg);
100
101         /* special care for last chunk */
102         last_val = lfsr_next(sg);
103         space_left = s - (i * 8);
104         memcpy(d + i, &last_val, _snap_to_bound8(space_left));
105 }
106
107 static inline int read_sig(struct bench_lfsr *sg, uint64_t *d, uint64_t s,
108                 int pos)
109 {
110         uint64_t i;
111         uint64_t last_val;
112         uint64_t space_left;
113
114         /* TODO: Should we use memcmp? */
115         for (i = pos; i < (s / 8) - (3 - pos); i += 3) {
116                 if (*(d + i) != lfsr_next(sg))
117                         return 1;
118         }
119         /* special care for last chunk */
120         last_val = lfsr_next(sg);
121         space_left = s - (i * 8);
122         if (memcmp(d + i, &last_val, _snap_to_bound8(space_left)))
123                 return 1;
124
125         return 0;
126 }
127
128 /*
129  * Seperates a double number in seconds, msec, usec, nsec
130  * Expects a number in nanoseconds (e.g. a number from timespec2double)
131  */
132 static struct tm_result separate_by_order(double num)
133 {
134         struct tm_result res;
135
136         //The format we expect is the following:
137         //
138         //              |-s-|-ms-|-us-|-ns|
139         //num =  123 456  789  012 . 000000000000
140         res.s = num / pow(10,9);
141         num = fmod(num, pow(10,9));
142         res.ms = num / pow(10,6);
143         num = fmod(num, pow(10,6));
144         res.us = num / 1000;
145         res.ns = fmod(num, 1000);
146
147         return res;
148 }
149
150 /******************************\
151  * Argument-parsing functions *
152 \******************************/
153
154 /*
155  * Convert string to size in bytes.
156  * If syntax is invalid, return 0. Values such as zero and non-integer
157  * multiples of segment's page size should not be accepted.
158  */
159 uint64_t str2num(char *str)
160 {
161         char *unit;
162         uint64_t num;
163
164         num = strtoll(str, &unit, 10);
165         if (strlen(unit) > 1) //Invalid syntax
166                 return 0;
167         else if (strlen(unit) < 1) //Plain number in bytes
168                 return num;
169
170         switch (*unit) {
171                 case 'g':
172                 case 'G':
173                         num *= 1024;
174                 case 'm':
175                 case 'M':
176                         num *= 1024;
177                 case 'k':
178                 case 'K':
179                         num *= 1024;
180                         break;
181                 default:
182                         num = 0;
183         }
184         return num;
185 }
186
187 /*
188  * Converts struct timespec to double (units in nanoseconds)
189  */
190 int read_insanity(char *insanity)
191 {
192         if (strncmp(insanity, "sane", MAX_ARG_LEN + 1) == 0)
193                 return INSANITY_SANE;
194         if (strncmp(insanity, "eccentric", MAX_ARG_LEN + 1) == 0)
195                 return INSANITY_ECCENTRIC;
196         if (strncmp(insanity, "manic", MAX_ARG_LEN + 1) == 0)
197                 return INSANITY_MANIC;
198         if (strncmp(insanity, "paranoid", MAX_ARG_LEN + 1) == 0)
199                 return INSANITY_PARANOID;
200         return -1;
201 }
202
203 int read_op(char *op)
204 {
205         if (strncmp(op, "read", MAX_ARG_LEN + 1) == 0)
206                 return X_READ;
207         if (strncmp(op, "write", MAX_ARG_LEN + 1) == 0)
208                 return X_WRITE;
209         if (strncmp(op, "info", MAX_ARG_LEN + 1) == 0)
210                 return X_INFO;
211         if (strncmp(op, "delete", MAX_ARG_LEN + 1) == 0)
212                 return X_DELETE;
213         return -1;
214 }
215
216 int read_verify(char *verify)
217 {
218         if (strncmp(verify, "no", MAX_ARG_LEN + 1) == 0)
219                 return VERIFY_NO;
220         if (strncmp(verify, "meta", MAX_ARG_LEN + 1) == 0)
221                 return VERIFY_META;
222         if (strncmp(verify, "full", MAX_ARG_LEN + 1) == 0)
223                 return VERIFY_FULL;
224         return -1;
225 }
226
227 int read_pattern(char *pattern)
228 {
229         if (strncmp(pattern, "seq", MAX_ARG_LEN + 1) == 0)
230                 return PATTERN_SEQ;
231         if (strncmp(pattern, "rand", MAX_ARG_LEN + 1) == 0)
232                 return PATTERN_RAND;
233         return -1;
234 }
235
236 /*******************\
237  * Print functions *
238 \*******************/
239
240 void print_stats(struct bench *prefs)
241 {
242         uint64_t remaining;
243
244         printf("\n");
245         printf("Requests total:     %10lu\n", prefs->status->max);
246         printf("Requests submitted: %10lu\n", prefs->status->submitted);
247         printf("Requests received:  %10lu\n", prefs->status->received);
248         printf("Requests failed:    %10lu\n", prefs->status->failed);
249         if ((prefs->op == X_READ) && (GET_FLAG(VERIFY, prefs->flags) != VERIFY_NO))
250                 printf("Requests corrupted: %10lu\n", prefs->status->corrupted);
251         printf("\n");
252
253         remaining = prefs->status->max - prefs->status->received;
254         if (remaining)
255                 printf("Requests remaining: %10lu\n", remaining);
256         else
257                 printf("All requests have been served.\n");
258 }
259
260 void print_res(struct bench *prefs, struct timer *tm, char *type)
261 {
262         struct tm_result res;
263         double sum;
264
265         sum = timespec2double(tm->sum);
266         res = separate_by_order(sum);
267
268         printf("\n");
269         printf("              %s\n", type);
270         printf("           ========================\n");
271         printf("             |-s-||-ms-|-us-|-ns-|\n");
272         printf("Total time:   %3u. %03u  %03u  %03u\n",
273                         res.s, res.ms, res.us, res.ns);
274
275         if (!prefs->status->received)
276                 return;
277
278         res = separate_by_order(sum / prefs->status->received);
279
280         printf("Mean Time:    %3u. %03u  %03u  %03u\n",
281                         res.s, res.ms, res.us, res.ns);
282
283         //TODO: Add std
284 }
285
286 /**************************\
287  * Benchmarking functions *
288 \**************************/
289
290 void create_id(unsigned long seed)
291 {
292         if (seed > pow(10, 9))
293                 XSEGLOG2(&lc, W, "Seed larger than 10^9, only its first 9 digits will "
294                                 "be used\n");
295
296         //nanoseconds can't be more than 9 digits
297         snprintf(global_id, IDLEN, "bench-%09lu", seed);
298 }
299
300 void create_target(struct bench *prefs, struct xseg_request *req,
301                 uint64_t new)
302 {
303         struct xseg *xseg = prefs->peer->xseg;
304         char *req_target;
305
306         req_target = xseg_get_target(xseg, req);
307
308         //For read/write, the target object does not correspond to `new`, which is
309         //actually the chunk number.
310         if (prefs->op == X_READ || prefs->op == X_WRITE)
311                 new = _get_object(prefs, new);
312         snprintf(req_target, TARGETLEN, "%s-%016lu", global_id, new);
313         XSEGLOG2(&lc, D, "Target name of request is %s\n", req_target);
314 }
315
316
317 uint64_t determine_next(struct bench *prefs)
318 {
319         if (GET_FLAG(PATTERN, prefs->flags) == PATTERN_SEQ)
320                 return prefs->status->submitted;
321         else
322                 return lfsr_next(prefs->lfsr);
323 }
324
325 /*
326  * ***********************************************
327  * `create_chunk` handles 3 identifiers:
328  * 1. The benchmark's global_id
329  * 2. The object's number
330  * 3. The chunk offset in the object
331  *
332  * ************************************************
333  * `_create_chunk_full` takes the above 3 identifiers and feeds them as seeds
334  * in 63-bit LFSRs. The numbers generated are written consecutively in chunk's
335  * memory range. For example, for a 72-byte chunk:
336  *
337  * || 1 | 2 | 3 | 1 | 2 | 3 | 1 | 2 | 3 ||
338  *  ^   8  16  24  32  40  48  56  64   ^
339  *  |                                   |
340  *  |                                   |
341  * start                               end
342  *
343  * 1,2,3 differ between each iteration
344  *
345  * **************************************************
346  * `_create_chunk_meta` simply writes the above 3 ids in the start and end of
347  * the chunk's memory range, so it should be much faster (but less safe)
348  *
349  * **************************************************
350  * In both cases, special care is taken not to exceed the chunk's memory range.
351  * Also, the bare minimum chunk to verify should be 48 bytes. This limit is set
352  * by _create_chunk_meta, which expects to write in a memory at least this big.
353  */
354 static int _readwrite_chunk_full(struct xseg *xseg, struct xseg_request *req,
355                 uint64_t id, uint64_t object)
356 {
357         struct bench_lfsr id_lfsr;
358         struct bench_lfsr obj_lfsr;
359         struct bench_lfsr off_lfsr;
360         uint64_t *d = (uint64_t *)xseg_get_data(xseg, req);
361         uint64_t s = req->size;
362
363         /* Create 63-bit LFSRs */
364         lfsr_init(&id_lfsr, 0x7FFFFFFF, id, 0);
365         lfsr_init(&obj_lfsr, 0x7FFFFFFF, object, 0);
366         lfsr_init(&off_lfsr, 0x7FFFFFFF, req->offset, 0);
367
368         if (s < sizeof(struct signature)) {
369                 XSEGLOG2(&lc, E, "Too small chunk size (%lu butes). Leaving.", s);
370                 return 1;
371         }
372
373         /*
374          * Every write operation has its read counterpart which, if it finds any
375          * corruption, returns 1
376          */
377
378         if (req->op == X_WRITE) {
379                 write_sig(&id_lfsr, d, s, 0);
380                 write_sig(&obj_lfsr, d, s, 1);
381                 write_sig(&off_lfsr, d, s, 2);
382         } else {
383                 if (read_sig(&id_lfsr, d, s, 0))
384                         return 1;
385                 if (read_sig(&obj_lfsr, d, s, 1))
386                         return 1;
387                 if(read_sig(&off_lfsr, d, s, 2))
388                         return 1;
389         }
390
391         return 0;
392 }
393
394 static int _readwrite_chunk_meta(struct xseg *xseg, struct xseg_request *req,
395                 uint64_t id, uint64_t object)
396 {
397         char *d = xseg_get_data(xseg, req);
398         uint64_t s = req->size;
399         struct signature sig;
400         int sig_s = sizeof(struct signature);
401         int r = 0;
402
403         sig.id = id;
404         sig.object = object;
405         sig.offset = req->offset;
406
407         if (s < sig_s) {
408                 XSEGLOG2(&lc, E, "Too small chunk size (%lu butes). Leaving.", s);
409                 return 1;
410         }
411
412         //PRINT_SIG(expected, (&sig));
413         /* Read/Write chunk signature both at its start and at its end */
414         if (req->op == X_WRITE) {
415                 memcpy(d, &sig, sig_s);
416                 memcpy(d + s - sig_s, &sig, sig_s);
417         } else {
418                 if (memcmp(d, &sig, sig_s))
419                         r = 1;
420                 else if (memcmp(d + s - sig_s, &sig, sig_s))
421                         r = 1;
422         }
423         //PRINT_SIG(start, d);
424         //PRINT_SIG(end, (d + s - sig_s));
425         return r;
426 }
427
428 /*
429  * We want these functions to be as fast as possible in case we haven't asked
430  * for verification
431  * TODO: Make them prettier but keep the speed of this implementation
432  */
433 void create_chunk(struct bench *prefs, struct xseg_request *req, uint64_t new)
434 {
435         struct xseg *xseg = prefs->peer->xseg;
436         uint64_t id;
437         uint64_t object;
438         int verify;
439
440         verify = GET_FLAG(VERIFY, prefs->flags);
441         switch (verify) {
442                 case VERIFY_NO:
443                         break;
444                 case VERIFY_META:
445                         id = _get_id();
446                         object = _get_object(prefs, new);
447                         _readwrite_chunk_meta(xseg, req, id, object);
448                         break;
449                 case VERIFY_FULL:
450                         id = _get_id();
451                         object = _get_object(prefs, new);
452                         _readwrite_chunk_full(xseg, req, id, object);
453                         break;
454                 default:
455                         XSEGLOG2(&lc, W, "Unexpected verification mode: %d\n", verify);
456         }
457 }
458
459 int read_chunk(struct bench *prefs, struct xseg_request *req)
460 {
461         struct xseg *xseg = prefs->peer->xseg;
462         uint64_t id;
463         uint64_t object;
464         char *target;
465         int verify;
466         int r = 0;
467
468         verify = GET_FLAG(VERIFY, prefs->flags);
469         switch (verify) {
470                 case VERIFY_NO:
471                         break;
472                 case VERIFY_META:
473                         id = _get_id();
474                         target = xseg_get_target(xseg, req);
475                         object = _get_object_from_name(target);
476                         r = _readwrite_chunk_meta(xseg, req, id, object);
477                         break;
478                 case VERIFY_FULL:
479                         id = _get_id();
480                         target = xseg_get_target(xseg, req);
481                         object = _get_object_from_name(target);
482                         r = _readwrite_chunk_full(xseg, req, id, object);
483                         break;
484                 default:
485                         XSEGLOG2(&lc, W, "Unexpected verification mode: %d\n", verify);
486         }
487         return r;
488 }
489
490