2 * Copyright 2012 GRNET S.A. All rights reserved.
4 * Redistribution and use in source and binary forms, with or
5 * without modification, are permitted provided that the following
8 * 1. Redistributions of source code must retain the above
9 * copyright notice, this list of conditions and the following
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.
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.
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.
39 #include <sys/syscall.h>
40 #include <sys/types.h>
42 #include <xseg/xseg.h>
47 #include <bench-xseg.h>
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);
59 /******************************\
60 * Static miscellaneous tools *
61 \******************************/
62 struct timespec delay = {0, 4000000};
64 static inline uint64_t _get_id()
66 return atol(global_id + 6); /* cut the "bench-" part*/
69 static inline uint64_t _get_object_from_name(char *name)
71 return atol(name + IDLEN); /* cut the "bench-908135-" part*/
74 static inline uint64_t _get_object(struct bench *prefs, uint64_t new)
76 return new / (prefs->os / prefs->bs);
79 static inline int _snap_to_bound8(uint64_t space)
81 return space > 8 ? 8 : space;
84 static inline double timespec2double(struct timespec num)
86 return (double) (num.tv_sec * pow(10, 9) + num.tv_nsec);
89 static inline void write_sig(struct bench_lfsr *sg, uint64_t *d, uint64_t s,
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);
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));
107 static inline int read_sig(struct bench_lfsr *sg, uint64_t *d, uint64_t s,
114 /* TODO: Should we use memcmp? */
115 for (i = pos; i < (s / 8) - (3 - pos); i += 3) {
116 if (*(d + i) != lfsr_next(sg))
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)))
129 * Seperates a double number in seconds, msec, usec, nsec
130 * Expects a number in nanoseconds (e.g. a number from timespec2double)
132 static struct tm_result separate_by_order(double num)
134 struct tm_result res;
136 //The format we expect is the following:
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));
145 res.ns = fmod(num, 1000);
150 /******************************\
151 * Argument-parsing functions *
152 \******************************/
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.
159 uint64_t str2num(char *str)
164 num = strtoll(str, &unit, 10);
165 if (strlen(unit) > 1) //Invalid syntax
167 else if (strlen(unit) < 1) //Plain number in bytes
188 * Converts struct timespec to double (units in nanoseconds)
190 int read_insanity(char *insanity)
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;
203 int read_op(char *op)
205 if (strncmp(op, "read", MAX_ARG_LEN + 1) == 0)
207 if (strncmp(op, "write", MAX_ARG_LEN + 1) == 0)
209 if (strncmp(op, "info", MAX_ARG_LEN + 1) == 0)
211 if (strncmp(op, "delete", MAX_ARG_LEN + 1) == 0)
216 int read_verify(char *verify)
218 if (strncmp(verify, "no", MAX_ARG_LEN + 1) == 0)
220 if (strncmp(verify, "meta", MAX_ARG_LEN + 1) == 0)
222 if (strncmp(verify, "full", MAX_ARG_LEN + 1) == 0)
227 int read_pattern(char *pattern)
229 if (strncmp(pattern, "seq", MAX_ARG_LEN + 1) == 0)
231 if (strncmp(pattern, "rand", MAX_ARG_LEN + 1) == 0)
236 /*******************\
238 \*******************/
240 void print_stats(struct bench *prefs)
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);
253 remaining = prefs->status->max - prefs->status->received;
255 printf("Requests remaining: %10lu\n", remaining);
257 printf("All requests have been served.\n");
260 void print_res(struct bench *prefs, struct timer *tm, char *type)
262 struct tm_result res;
265 sum = timespec2double(tm->sum);
266 res = separate_by_order(sum);
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);
275 if (!prefs->status->received)
278 res = separate_by_order(sum / prefs->status->received);
280 printf("Mean Time: %3u. %03u %03u %03u\n",
281 res.s, res.ms, res.us, res.ns);
286 /**************************\
287 * Benchmarking functions *
288 \**************************/
290 void create_id(unsigned long seed)
292 if (seed > pow(10, 9))
293 XSEGLOG2(&lc, W, "Seed larger than 10^9, only its first 9 digits will "
296 //nanoseconds can't be more than 9 digits
297 snprintf(global_id, IDLEN, "bench-%09lu", seed);
300 void create_target(struct bench *prefs, struct xseg_request *req,
303 struct xseg *xseg = prefs->peer->xseg;
306 req_target = xseg_get_target(xseg, req);
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);
317 uint64_t determine_next(struct bench *prefs)
319 if (GET_FLAG(PATTERN, prefs->flags) == PATTERN_SEQ)
320 return prefs->status->submitted;
322 return lfsr_next(prefs->lfsr);
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
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:
337 * || 1 | 2 | 3 | 1 | 2 | 3 | 1 | 2 | 3 ||
338 * ^ 8 16 24 32 40 48 56 64 ^
343 * 1,2,3 differ between each iteration
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)
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.
354 static int _readwrite_chunk_full(struct xseg *xseg, struct xseg_request *req,
355 uint64_t id, uint64_t object)
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;
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);
368 if (s < sizeof(struct signature)) {
369 XSEGLOG2(&lc, E, "Too small chunk size (%lu butes). Leaving.", s);
374 * Every write operation has its read counterpart which, if it finds any
375 * corruption, returns 1
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);
383 if (read_sig(&id_lfsr, d, s, 0))
385 if (read_sig(&obj_lfsr, d, s, 1))
387 if(read_sig(&off_lfsr, d, s, 2))
394 static int _readwrite_chunk_meta(struct xseg *xseg, struct xseg_request *req,
395 uint64_t id, uint64_t object)
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);
405 sig.offset = req->offset;
408 XSEGLOG2(&lc, E, "Too small chunk size (%lu butes). Leaving.", s);
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);
418 if (memcmp(d, &sig, sig_s))
420 else if (memcmp(d + s - sig_s, &sig, sig_s))
423 //PRINT_SIG(start, d);
424 //PRINT_SIG(end, (d + s - sig_s));
429 * We want these functions to be as fast as possible in case we haven't asked
431 * TODO: Make them prettier but keep the speed of this implementation
433 void create_chunk(struct bench *prefs, struct xseg_request *req, uint64_t new)
435 struct xseg *xseg = prefs->peer->xseg;
440 verify = GET_FLAG(VERIFY, prefs->flags);
446 object = _get_object(prefs, new);
447 _readwrite_chunk_meta(xseg, req, id, object);
451 object = _get_object(prefs, new);
452 _readwrite_chunk_full(xseg, req, id, object);
455 XSEGLOG2(&lc, W, "Unexpected verification mode: %d\n", verify);
459 int read_chunk(struct bench *prefs, struct xseg_request *req)
461 struct xseg *xseg = prefs->peer->xseg;
468 verify = GET_FLAG(VERIFY, prefs->flags);
474 target = xseg_get_target(xseg, req);
475 object = _get_object_from_name(target);
476 r = _readwrite_chunk_meta(xseg, req, id, object);
480 target = xseg_get_target(xseg, req);
481 object = _get_object_from_name(target);
482 r = _readwrite_chunk_full(xseg, req, id, object);
485 XSEGLOG2(&lc, W, "Unexpected verification mode: %d\n", verify);