Strip xseg stuff
[archipelago] / xseg / bench-xseg.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 <xseg/xseg.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <unistd.h>
40 #include <sys/syscall.h>
41 #include <sys/types.h>
42 #include <pthread.h>
43 #include <peer.h>
44 #include <time.h>
45 #include <sys/util.h>
46 #include <signal.h>
47 #include <bench-xseg.h>
48 #include <bench-lfsr.h>
49 #include <limits.h>
50 #include <math.h>
51
52 /*
53  * This macro checks two things:
54  * a) If in-flight requests are less than given iodepth
55  * b) If we have submitted all of the requests
56  * c) If we are not in ping mode
57  * d) If we have been asked to terminate
58  */
59 #define CAN_SEND_REQUEST(__p)                                               \
60         ((__p->status->submitted - __p->status->received < __p->iodepth) && \
61         (__p->status->submitted < __p->status->max) &&                      \
62         (GET_FLAG(PING, __p->flags) == PING_MODE_OFF) &&                    \
63          !isTerminate())
64
65 #define CAN_VERIFY(__p)                                                     \
66         ((GET_FLAG(VERIFY, __p->flags) != VERIFY_NO) && __p->op == X_READ)
67
68 #define CAN_PRINT_PROGRESS(__p, __nr)                                       \
69         ((GET_FLAG(PROGRESS, __p->flags) != PROGRESS_NO) &&                 \
70         (GET_FLAG(PING, __p->flags) == PING_MODE_OFF) &&                    \
71         (__p->status->received == __nr))
72
73 void custom_peer_usage()
74 {
75         fprintf(stderr, "Custom peer options: \n"
76                 "  --------------------------------------------\n"
77                 "\n"
78                 "a) Benchmark options: \n"
79                 "  --------------------------------------------\n"
80                 "    -op       | None    | XSEG operation:\n"
81                 "              |         |     [read|write|info|delete]\n"
82                 "    --pattern | None    | I/O pattern [seq|rand]\n"
83                 "    -rc       | None    | Request cap\n"
84                 "    -to       | None    | Total objects\n"
85                 "    -ts       | None    | Total I/O size\n"
86                 "    -os       | 4M      | Object size\n"
87                 "    -bs       | 4k      | Block size\n"
88                 "    -tp       | None    | Target port\n"
89                 "    --iodepth | 1       | Number of in-flight I/O requests\n"
90                 "    --seed    | None    | Initialize LFSR and target names\n"
91                 "\n"
92                 "b) Object naming options: \n"
93                 "  --------------------------------------------\n"
94                 "    --prefix  | bench   | Add a common prefix to all object names\n"
95                 "    --objname | None    | Use only one object with this name\n"
96                 "\n"
97                 "c) Data verification options: \n"
98                 "  --------------------------------------------\n"
99                 "    --verify  | no      | Verify written requests:\n"
100                 "              |         |     [no|meta|full]\n"
101                 "\n"
102                 "d) Progress report options: \n"
103                 "  --------------------------------------------\n"
104                 "    --progress  | yes     | Show progress of benchmark:\n"
105                 "                |         |     [yes|no]\n"
106                 "    --ptype     | both    | Progress report type:\n"
107                 "                |         |     [req|io|both]\n"
108                 "    --pinterval | 5%%      | Intervals at which progress is shown\n"
109                 "    --insanity  | sane    | Adjust insanity level of benchmark:\n"
110                 "                |         |     [sane|eccentric|manic|paranoid]\n"
111                 "\n"
112                 "e) Misc options: \n"
113                 "  --------------------------------------------\n"
114                 "    --ping    | no      | Ping target before starting:\n"
115                 "              |         |     [yes|no]\n"
116                 "\n"
117                 "Additional information:\n"
118                 "  --------------------------------------------\n"
119                 "  * The -to and -ts options are mutually exclusive\n"
120                 "\n"
121                 "  * The object name is always not null-terminated and\n"
122                 "    defaults to the following structure:\n"
123                 "           <prefix>-<seed>-<object number>\n"
124                 "\n"
125                 "    where:\n"
126                 "    a. <prefix> is given by user or defaults to 'bench'\n"
127                 "    b. <seed> is given by user or defaults to a random value.\n"
128                 "       Its length will be 9 digits, with trailing zeros where\n"
129                 "       necessary\n"
130                 "    c. <object number> is out of the user's control. It is\n"
131                 "       calculated during the benchmark and is a 15-digit\n"
132                 "       number, allowing a maximum of 1 quadrillion objects\n"
133                 "\n"
134                 "   So, if bench is called with the arguments:\n"
135                 "           --prefix obj --seed 999\n"
136                 "\n"
137                 "   and <object number> is 9,the resulting object name will\n"
138                 "   be:\n"
139                 "           obj-000000999-000000000000009\n"
140                 "\n"
141                 " * The above object name structure can be bypassed with the\n"
142                 "   --objname <object name> argument. This implies the\n"
143                 "   following:\n"
144                 "\n"
145                 "   a. -to option is strictly restricted to 1\n"
146                 "   b. -ts option defaults to (and can't be larger than)\n"
147                 "      the object size (-os argument)\n"
148                 "   c. --prefix is must be unused. If used, it produces an\n"
149                 "      error to alert the user\n"
150                 "\n"
151                 " * The progress report is printed by default at intervals of\n"
152                 "   5%%.\n"
153                 "   There are three progress types:\n"
154                 "\n"
155                 "   a. req: it prints the request status so far i.e. how many\n"
156                 "      requests have been subitted, received, failed etc.\n"
157                 "   b. io: it prints the bandwidth and IOPS status of the\n"
158                 "      elapsed 5%% of the benchmark\n"
159                 "   c. both: it combines the output of <req> and <io>.\n"
160                 "\n"
161                 " * Interval is commonly a percentage of max requests. This\n"
162                 "   means that when a user gives:\n"
163                 "           --pinterval 33%%\n"
164                 "\n"
165                 "   the progress report will be printed 3 times during the\n"
166                 "   benchmark. Else, if the user wants to, he/she can give:\n"
167                 "           --pinterval 1234\n"
168                 "\n"
169                 "  and the progress report will be printed every 1234\n"
170                 "  requests.\n"
171                 "\n");
172 }
173
174 int custom_peer_init(struct peerd *peer, int argc, char *argv[])
175 {
176         struct bench *prefs;
177         char request_cap[MAX_ARG_LEN + 1];
178         char total_objects[MAX_ARG_LEN + 1];
179         char total_size[MAX_ARG_LEN + 1];
180         char object_size[MAX_ARG_LEN + 1];
181         char block_size[MAX_ARG_LEN + 1];
182         char op[MAX_ARG_LEN + 1];
183         char pattern[MAX_ARG_LEN + 1];
184         char insanity[MAX_ARG_LEN + 1];
185         char verify[MAX_ARG_LEN + 1];
186         char progress[MAX_ARG_LEN + 1];
187         char ptype[MAX_ARG_LEN + 1];
188         char pinterval[MAX_ARG_LEN + 1];
189         char ping[MAX_ARG_LEN + 1];
190         char prefix[XSEG_MAX_TARGETLEN + 1];
191         char objname[XSEG_MAX_TARGETLEN + 1];
192         struct xseg *xseg = peer->xseg;
193         struct object_vars *obv;
194         unsigned int xseg_page_size = 1 << xseg->config.page_shift;
195         long iodepth = -1;
196         long dst_port = -1;
197         unsigned long seed = -1;
198         unsigned long seed_max;
199         uint64_t rc;
200         struct timespec *ts;
201         int set_by_hand = 1;
202         int j, r;
203
204         op[0] = 0;
205         pattern[0] = 0;
206         total_objects[0] = 0;
207         total_size[0] = 0;
208         block_size[0] = 0;
209         object_size[0] = 0;
210         insanity[0] = 0;
211         verify[0] = 0;
212         request_cap[0] = 0;
213         progress[0] = 0;
214         ptype[0] = 0;
215         pinterval[0] = 0;
216         ping[0] = 0;
217         prefix[0] = 0;
218         objname[0] = 0;
219
220         /* allocate struct bench */
221         prefs = malloc(sizeof(struct bench));
222         if (!prefs) {
223                 perror("malloc");
224                 goto prefs_fail;
225         }
226         memset(prefs, 0, sizeof(struct bench));
227
228         /* allocate struct req_status */
229         prefs->status = malloc(sizeof(struct req_status));
230         if (!prefs->status) {
231                 perror("malloc");
232                 goto status_fail;
233         }
234         memset(prefs->status, 0, sizeof(struct req_status));
235
236         /* allocate struct object_name */
237         prefs->objvars = malloc(sizeof(struct object_vars));
238         if (!prefs->objvars) {
239                 perror("malloc");
240                 goto object_name_fail;
241         }
242         memset(prefs->objvars, 0, sizeof(struct object_vars));
243
244         /* allocate struct object_name */
245         prefs->rep = malloc(sizeof(struct progress_report));
246         if (!prefs->rep) {
247                 perror("malloc");
248                 goto progress_report_fail;
249         }
250         memset(prefs->rep, 0, sizeof(struct progress_report));
251
252         /* allocate a struct timespec for each peer request */
253         for (j = 0; j < peer->nr_ops; j++) {
254                 ts = malloc(sizeof(struct timespec));
255                 if (!ts) {
256                         perror("malloc");
257                         goto priv_fail;
258                 }
259                 peer->peer_reqs[j].priv = ts;
260         }
261
262         //Begin reading the benchmark-specific arguments
263         BEGIN_READ_ARGS(argc, argv);
264         READ_ARG_STRING("-rc", request_cap, MAX_ARG_LEN);
265         READ_ARG_STRING("-op", op, MAX_ARG_LEN);
266         READ_ARG_STRING("--pattern", pattern, MAX_ARG_LEN);
267         READ_ARG_STRING("-to", total_objects, MAX_ARG_LEN);
268         READ_ARG_STRING("-ts", total_size, MAX_ARG_LEN);
269         READ_ARG_STRING("-os", object_size, MAX_ARG_LEN);
270         READ_ARG_STRING("-bs", block_size, MAX_ARG_LEN);
271         READ_ARG_ULONG("--iodepth", iodepth);
272         READ_ARG_ULONG("-tp", dst_port);
273         READ_ARG_ULONG("--seed", seed);
274         READ_ARG_STRING("--insanity", insanity, MAX_ARG_LEN);
275         READ_ARG_STRING("--verify", verify, MAX_ARG_LEN);
276         READ_ARG_STRING("--progress", progress, MAX_ARG_LEN);
277         READ_ARG_STRING("--ptype", ptype, MAX_ARG_LEN);
278         READ_ARG_STRING("--pinterval", pinterval, MAX_ARG_LEN);
279         READ_ARG_STRING("--ping", ping, MAX_ARG_LEN);
280         READ_ARG_STRING("--prefix", prefix, XSEG_MAX_TARGETLEN);
281         READ_ARG_STRING("--objname", objname, XSEG_MAX_TARGETLEN);
282         END_READ_ARGS();
283
284         /********************************\
285          * Check object name parameters *
286         \********************************/
287         if (objname[0] && prefix[0]) {
288                 XSEGLOG2(&lc, E, "--objname and --prefix options cannot be"
289                                 "used together.");
290                 goto arg_fail;
291         }
292
293         obv = prefs->objvars;
294         obv->seedlen = SEEDLEN;
295         obv->objnumlen = OBJNUMLEN;
296         if (objname[0]) {
297                 strncpy(obv->name, objname, XSEG_MAX_TARGETLEN);
298                 obv->prefixlen = 0;
299                 obv->namelen = strlen(objname);
300         } else {
301                 if (!prefix[0]) /* In this case we use a default value */
302                         strcpy(prefix, "bench");
303                 strncpy(obv->prefix, prefix, XSEG_MAX_TARGETLEN);
304                 obv->prefixlen = strlen(prefix);
305                 /* We add 2 for the extra dashes */
306                 obv->namelen = obv->prefixlen + obv->seedlen +
307                         obv->objnumlen + 2;
308         }
309
310         /* Only --prefix can exceed bounds since --objname is bounded */
311         if (obv->namelen > XSEG_MAX_TARGETLEN) {
312                 XSEGLOG2(&lc, E, "--prefix %s: Prefix is too long.", prefix);
313                 goto arg_fail;
314         }
315
316         /*****************************\
317          * Check I/O type parameters *
318         \*****************************/
319
320         //We support 4 xseg operations: X_READ, X_WRITE, X_DELETE, X_INFO
321         //The I/O pattern of these operations can be either sequential (seq) or
322         //random (rand)
323         if (!op[0]) {
324                 XSEGLOG2(&lc, E, "xseg operation needs to be supplied\n");
325                 goto arg_fail;
326         }
327         r = read_op(op);
328         if (r < 0) {
329                 XSEGLOG2(&lc, E, "Invalid syntax: -op %s\n", op);
330                 goto arg_fail;
331         }
332         prefs->op = r;
333
334         if (!pattern[0]) {
335                 XSEGLOG2(&lc, E, "I/O pattern needs to be supplied\n");
336                 goto arg_fail;
337         }
338         r = read_pattern(pattern);
339         if (r < 0) {
340                 XSEGLOG2(&lc, E, "Invalid syntax: --pattern %s\n", pattern);
341                 goto arg_fail;
342         }
343         SET_FLAG(PATTERN, prefs->flags, r);
344
345         if (!verify[0])
346                 strcpy(verify, "no");
347         r = read_verify(verify);
348         if (r < 0) {
349                 XSEGLOG2(&lc, E, "Invalid syntax: --verify %s\n", verify);
350                 goto arg_fail;
351         }
352         SET_FLAG(VERIFY, prefs->flags, r);
353
354         //Default iodepth value is 1
355         if (iodepth < 0)
356                 prefs->iodepth = 1;
357         else
358                 prefs->iodepth = iodepth;
359
360         /**************************\
361          * Check timer parameters *
362         \**************************/
363
364         //Most of the times, not all timers need to be used.
365         //We can choose which timers will be used by adjusting the "insanity"
366         //level of the benchmark i.e. the obscurity of code paths (get request,
367         //submit request) that will be timed.
368         if (!insanity[0])
369                 strcpy(insanity, "sane");
370
371         r = read_insanity(insanity);
372         if (r < 0) {
373                 XSEGLOG2(&lc, E, "Invalid syntax: --insanity %s\n", insanity);
374                 goto arg_fail;
375         }
376         SET_FLAG(INSANITY, prefs->flags, r);
377
378         /*****************************\
379          * Check I/O size parameters *
380         \*****************************/
381
382         //Block size (bs): Defaults to 4K.
383         //It must be a number followed by one of these characters:
384         //                                              [k|K|m|M|g|G]
385         //If not, it will be considered as size in bytes.
386         //Must be integer multiple of segment's page size (typically 4k).
387         if (!block_size[0])
388                 strcpy(block_size,"4k");
389
390         prefs->bs = str2num(block_size);
391         if (!prefs->bs) {
392                 XSEGLOG2(&lc, E, "Invalid syntax: -bs %s\n", block_size);
393                 goto arg_fail;
394         } else if (prefs->bs % xseg_page_size) {
395                 XSEGLOG2(&lc, E, "Misaligned block size: %s\n", block_size);
396                 goto arg_fail;
397         }
398
399         //Object size (os): Defaults to 4M.
400         //Must have the same format as "block size"
401         //Must be integer multiple of "block size"
402         if (!object_size[0])
403                 strcpy(object_size,"4M");
404
405         prefs->os = str2num(object_size);
406         if (!prefs->os) {
407                 XSEGLOG2(&lc, E, "Invalid syntax: -os %s\n", object_size);
408                 goto arg_fail;
409         } else if (prefs->os % prefs->bs) {
410                 XSEGLOG2(&lc, E, "Misaligned object size: %s\n", object_size);
411                 goto arg_fail;
412         }
413
414         //Total objects (to) or total I/O size (ts).
415         //Must have the same format as "block size"
416         //They are mutually exclusive
417         if (total_objects[0] && total_size[0]) {
418                 XSEGLOG2(&lc, E, "Total objects and total size are "
419                                 "mutually exclusive\n");
420                 goto arg_fail;
421         } else if (total_objects[0]) {
422                 prefs->to = str2num(total_objects);
423                 if (!prefs->to) {
424                         XSEGLOG2(&lc, E, "Invalid syntax: -to %s\n",
425                                         total_objects);
426                         goto arg_fail;
427                 }
428                 //In this case, the maximum number of requests is the total
429                 //number of objects we will handle
430                 prefs->status->max = prefs->to;
431         } else if (total_size[0]) {
432                 if (prefs->op != X_READ && prefs->op != X_WRITE) {
433                         XSEGLOG2(&lc, E, "Total objects must be supplied "
434                                         "(required by -op %s)\n", op);
435                         goto arg_fail;
436                 }
437                 prefs->ts = str2num(total_size);
438                 if (!prefs->ts) {
439                         XSEGLOG2(&lc, E, "Invalid syntax: -ts %s\n", total_size);
440                         goto arg_fail;
441                 } else if (prefs->ts % prefs->bs) {
442                         XSEGLOG2(&lc, E, "Misaligned total I/O size: %s\n", total_size);
443                         goto arg_fail;
444                 }
445                 //In this case, the maximum number of requests is the number of
446                 //blocks we need to cover the total I/O size
447                 prefs->status->max = prefs->ts / prefs->bs;
448         } else if (!objname[0]) {
449                 XSEGLOG2(&lc, E, "Total objects or total size must be supplied\n");
450                 goto arg_fail;
451         }
452
453         /*
454          * Enforce --objname restrictions here.
455          */
456         if (obv->name[0]) {
457                 if (prefs->to > 1) {
458                         XSEGLOG2(&lc, E, "-to %s: Total objects are restricted "
459                                         "to 1 due to --objname %s\n",
460                                         total_objects, objname);
461                         goto arg_fail;
462                 } else if (prefs->ts > prefs->os) {
463                         XSEGLOG2(&lc, E, "-ts %s: Total size can't be larger "
464                                         "than object size (%s) due to "
465                                         "--objname %s\n",
466                                         total_size, object_size, objname);
467                         goto arg_fail;
468                 } else if (prefs->op == X_READ || prefs->op == X_WRITE) {
469                         prefs->ts = prefs->os;
470                         prefs->status->max = prefs->ts / prefs->bs;
471                 } else {
472                         prefs->to = 1;
473                         prefs->status->max = 1;
474                 }
475         }
476
477         if (prefs->status->max == 1)
478                 SET_FLAG(PATTERN, prefs->flags, PATTERN_SEQ);
479
480         /*************************\
481          * Check port parameters *
482         \*************************/
483
484         if (dst_port < 0){
485                 XSEGLOG2(&lc, E, "Target port must be supplied\n");
486                 goto arg_fail;
487         }
488
489         prefs->src_port = peer->portno_start; //TODO: allow user to change this
490         prefs->dst_port = (xport) dst_port;
491
492         /*********************************\
493          * Create timers for all metrics *
494         \*********************************/
495
496         if (init_timer(&prefs->total_tm, INSANITY_SANE))
497                 goto tm_fail;
498         if (init_timer(&prefs->sub_tm, INSANITY_MANIC))
499                 goto tm_fail;
500         if (init_timer(&prefs->get_tm, INSANITY_PARANOID))
501                 goto tm_fail;
502         if (init_timer(&prefs->rec_tm, INSANITY_ECCENTRIC))
503                 goto tm_fail;
504
505         /***********************\
506          * Initialize the LFSR *
507         \***********************/
508
509         seed_max = pow(10, obv->seedlen + 1) - 1;
510         if (seed == -1) {
511                 srand(time(NULL));
512                 set_by_hand = 0;
513         } else if (validate_seed(prefs, seed)) {
514                 XSEGLOG2(&lc, E, "--seed %lu: Seed larger than %lu. Only its "
515                                 "first %d digits will be used",
516                                 seed, seed_max, obv->seedlen);
517                 goto arg_fail;
518         }
519
520 reseed:
521         if (!set_by_hand)
522                 seed = rand() % seed_max + 1;
523
524         if (GET_FLAG(PATTERN, prefs->flags) == PATTERN_RAND) {
525                 prefs->lfsr = malloc(sizeof(struct bench_lfsr));
526                 if (!prefs->lfsr) {
527                         perror("malloc");
528                         goto lfsr_fail;
529                 }
530
531                 r = lfsr_init(prefs->lfsr, prefs->status->max,
532                                 seed, seed & 0xF);
533                 if (r) {
534                         if (!set_by_hand) {
535                                 free(prefs->lfsr);
536                                 goto reseed;
537                         }
538                         XSEGLOG2(&lc, E, "LFSR could not be initialized.\n");
539                         goto lfsr_fail;
540                 }
541         }
542         obv->seed = seed;
543
544         /*********************************\
545          * Miscellaneous initializations *
546         \*********************************/
547
548         /* The request cap must be enforced only after the LFSR is initialized */
549         if (request_cap[0]) {
550                 rc = str2num(request_cap);
551                 if (!rc) {
552                         XSEGLOG2(&lc, E, "Invalid syntax: -rc %s\n", request_cap);
553                         goto arg_fail;
554                 } else if (rc > prefs->status->max) {
555                         XSEGLOG2(&lc, E, "Request cap exceeds current request total.\n");
556                         goto arg_fail;
557                 }
558                 prefs->status->max = rc;
559         }
560
561         /**********************************\
562          * Progress report initialization *
563         \**********************************/
564
565         /* Progress report is on by default */
566         if (!progress[0])
567                 strcpy(progress, "yes");
568         r = read_progress(progress);
569         if (r < 0) {
570                 XSEGLOG2(&lc, E, "Invalid syntax: --progress %s\n", progress);
571                 goto arg_fail;
572         }
573         SET_FLAG(PROGRESS, prefs->flags, r);
574
575         /*
576          * Progress report interval definition or progress type definition makes
577          * no sense with disabled progress reports.
578          */
579         if ((GET_FLAG(PROGRESS, prefs->flags) == PROGRESS_NO) &&
580                         (pinterval[0] || ptype[0])) {
581                 XSEGLOG2(&lc, E, "Cannot define progress interval or progress "
582                                 "type without enabling progress report\n");
583                 goto arg_fail;
584         }
585
586         if (GET_FLAG(PROGRESS, prefs->flags) != PROGRESS_NO) {
587                 /* Progress type is 'both' by default */
588                 if (!ptype[0])
589                         strcpy(ptype, "both");
590                 prefs->rep->type = read_progress_type(ptype);
591                 if (prefs->rep->type < 0) {
592                         XSEGLOG2(&lc, E, "Invalid syntax: --ptype %s\n", ptype);
593                         goto arg_fail;
594                 }
595                 prefs->rep->lines = calculate_report_lines(prefs);
596         }
597
598         if (GET_FLAG(PROGRESS, prefs->flags) != PROGRESS_NO) {
599                 /* By default, we print every 5% */
600                 if (!pinterval[0])
601                         strcpy(pinterval, "5%");
602                 prefs->rep->interval = read_interval(prefs, pinterval);
603                 if (prefs->rep->interval == 0) {
604                         XSEGLOG2(&lc, E, "Invalid syntax: --pinterval %s\n",
605                                         pinterval);
606                         goto arg_fail;
607                 }
608         }
609
610         /* Pinging the target peer is on by default */
611         if (!ping[0])
612                 strcpy(ping, "no");
613         r = read_ping(ping);
614         if (r < 0) {
615                 XSEGLOG2(&lc, E, "Invalid syntax: --ping %s\n", ping);
616                 goto arg_fail;
617         }
618         SET_FLAG(PING, prefs->flags, r);
619
620         prefs->peer = peer;
621         peer->peerd_loop = bench_peerd_loop;
622         peer->priv = (void *) prefs;
623
624         if (obv->prefixlen)
625                 XSEGLOG2(&lc, I, "Seed is %u, prefix is %s",
626                                 obv->seed, obv->prefix);
627         else
628                 XSEGLOG2(&lc, I, "Seed is %u, object name is %s",
629                                 obv->seed, obv->name);
630
631         return 0;
632
633 arg_fail:
634         custom_peer_usage();
635 lfsr_fail:
636         free(prefs->lfsr);
637 tm_fail:
638         free(prefs->total_tm);
639         free(prefs->sub_tm);
640         free(prefs->get_tm);
641         free(prefs->rec_tm);
642 priv_fail:
643         j--;
644         for (; j >= 0; j--) {
645                 free(peer->peer_reqs[j].priv);
646         }
647 progress_report_fail:
648         free(prefs->rep);
649 object_name_fail:
650         free(prefs->objvars);
651 status_fail:
652         free(prefs->status);
653 prefs_fail:
654         free(prefs);
655         return -1;
656 }
657
658
659 static int send_request(struct peerd *peer, struct bench *prefs)
660 {
661         struct xseg_request *req;
662         struct xseg *xseg = peer->xseg;
663         struct peer_req *pr;
664         struct object_vars *obv = prefs->objvars;
665         xport srcport = prefs->src_port;
666         xport dstport = prefs->dst_port;
667         xport p;
668
669         int r;
670         uint64_t new;
671         uint64_t size = prefs->bs;
672         struct timespec *ts;
673
674         //srcport and dstport must already be provided by the user.
675         //returns struct xseg_request with basic initializations
676         XSEGLOG2(&lc, D, "Get new request\n");
677         timer_start(prefs, prefs->get_tm);
678         req = xseg_get_request(xseg, srcport, dstport, X_ALLOC);
679         if (!req) {
680                 XSEGLOG2(&lc, W, "Cannot get request\n");
681                 return -1;
682         }
683         timer_stop(prefs, prefs->get_tm, NULL);
684
685         /*
686          * Allocate enough space for the data and the target's name.
687          * Also, allocate one extra byte to prevent buffer overflow due to the
688          * obligatory null termination of snprint(). This extra byte will not be
689          * counted as part of the target's name.
690          */
691         XSEGLOG2(&lc, D, "Prepare new request\n");
692         r = xseg_prep_request(xseg, req, obv->namelen + 1, size);
693         if (r < 0) {
694                 XSEGLOG2(&lc, W, "Cannot prepare request! (%lu, %llu)\n",
695                                 obv->namelen + 1, (unsigned long long)size);
696                 goto put_xseg_request;
697         }
698         req->targetlen--;
699
700         //Determine what the next target/chunk will be, based on I/O pattern
701         new = determine_next(prefs);
702         req->op = prefs->op;
703         XSEGLOG2(&lc, I, "Our new request is %lu\n", new);
704         obv->objnum = __get_object(prefs, new);
705         create_target(prefs, req);
706
707         if (prefs->op == X_WRITE || prefs->op == X_READ) {
708                 req->size = size;
709                 //Calculate the chunk's offset inside the object
710                 req->offset = calculate_offset(prefs, new);
711                 XSEGLOG2(&lc, D, "Offset of request %lu is %lu\n", new, req->offset);
712
713                 if (prefs->op == X_WRITE)
714                         create_chunk(prefs, req, new);
715         }
716
717         XSEGLOG2(&lc, D, "Allocate peer request\n");
718         pr = alloc_peer_req(peer);
719         if (!pr) {
720                 XSEGLOG2(&lc, W, "Cannot allocate peer request (%ld remaining)\n",
721                                 peer->nr_ops - xq_count(&peer->free_reqs));
722                 goto put_xseg_request;
723         }
724         pr->peer = peer;
725         pr->portno = srcport;
726         pr->req = req;
727
728         //XSEGLOG2(&lc, D, "Set request data\n");
729         r = xseg_set_req_data(xseg, req, pr);
730         if (r < 0) {
731                 XSEGLOG2(&lc, W, "Cannot set request data\n");
732                 goto put_peer_request;
733         }
734
735         /*
736          * Start measuring receive time.
737          * When we receive a request, we need to have its submission time to
738          * measure elapsed time. Thus, we copy its submission time to pr->priv.
739          * QUESTION: Is this the fastest way?
740          */
741         timer_start(prefs, prefs->rec_tm);
742         if (prefs->rec_tm->insanity <= GET_FLAG(INSANITY, prefs->flags)) {
743                 ts = (struct timespec *)pr->priv;
744                 ts->tv_sec = prefs->rec_tm->start_time.tv_sec;
745                 ts->tv_nsec = prefs->rec_tm->start_time.tv_nsec;
746         }
747
748         //Submit the request from the source port to the target port
749         XSEGLOG2(&lc, D, "Submit request %lu\n", new);
750         timer_start(prefs, prefs->sub_tm);
751         p = xseg_submit(xseg, req, srcport, X_ALLOC);
752         if (p == NoPort) {
753                 XSEGLOG2(&lc, W, "Cannot submit request\n");
754                 goto put_peer_request;
755         }
756         prefs->status->submitted++;
757         timer_stop(prefs, prefs->sub_tm, NULL);
758
759         //Send SIGIO to the process that has bound this port to inform that
760         //IO is possible
761         r = xseg_signal(xseg, p);
762         //if (r < 0)
763         //      XSEGLOG2(&lc, W, "Cannot signal destination peer (reason %d)\n", r);
764
765         return 0;
766
767 put_peer_request:
768         free_peer_req(peer, pr);
769 put_xseg_request:
770         if (xseg_put_request(xseg, req, srcport))
771                 XSEGLOG2(&lc, W, "Cannot put request\n");
772         return -1;
773 }
774
775 static int send_ping_request(struct peerd *peer, struct bench *prefs)
776 {
777         struct xseg_request *req;
778         struct xseg *xseg = peer->xseg;
779         struct peer_req *pr;
780         xport srcport = prefs->src_port;
781         xport dstport = prefs->dst_port;
782         xport p;
783         int r;
784
785         XSEGLOG2(&lc, I, "Sending ping request...");
786         //srcport and dstport must already be provided by the user.
787         //returns struct xseg_request with basic initializations
788         XSEGLOG2(&lc, D, "Get new request\n");
789         req = xseg_get_request(xseg, srcport, dstport, X_ALLOC);
790         if (!req) {
791                 XSEGLOG2(&lc, W, "Cannot get request\n");
792                 return -1;
793         }
794         req->op = X_PING;
795
796         XSEGLOG2(&lc, D, "Allocate peer request\n");
797         pr = alloc_peer_req(peer);
798         if (!pr) {
799                 XSEGLOG2(&lc, W, "Cannot allocate peer request (%ld remaining)\n",
800                                 peer->nr_ops - xq_count(&peer->free_reqs));
801                 goto put_xseg_request;
802         }
803         pr->peer = peer;
804         pr->portno = srcport;
805         pr->req = req;
806
807         r = xseg_set_req_data(xseg, req, pr);
808         if (r < 0) {
809                 XSEGLOG2(&lc, W, "Cannot set request data\n");
810                 goto put_peer_request;
811         }
812
813         //Submit the request from the source port to the target port
814         XSEGLOG2(&lc, D, "Submit ping request");
815         p = xseg_submit(xseg, req, srcport, X_ALLOC);
816         if (p == NoPort) {
817                 XSEGLOG2(&lc, W, "Cannot submit request\n");
818                 goto put_peer_request;
819         }
820         timer_stop(prefs, prefs->sub_tm, NULL);
821
822         //Send SIGIO to the process that has bound this port to inform that
823         //IO is possible
824         r = xseg_signal(xseg, p);
825         //if (r < 0)
826         //      XSEGLOG2(&lc, W, "Cannot signal destination peer (reason %d)\n", r);
827
828         return 0;
829
830 put_peer_request:
831         free_peer_req(peer, pr);
832 put_xseg_request:
833         if (xseg_put_request(xseg, req, srcport))
834                 XSEGLOG2(&lc, W, "Cannot put request\n");
835         return -1;
836 }
837
838 /*
839  * This function substitutes the default generic_peerd_loop of peer.c.
840  * It's plugged to struct peerd at custom peer's initialisation
841  */
842 int bench_peerd_loop(void *arg)
843 {
844 #ifdef MT
845         struct thread *t = (struct thread *) arg;
846         struct peerd *peer = t->peer;
847         char *id = t->arg;
848 #else
849         struct peerd *peer = (struct peerd *) arg;
850         char id[4] = {'P','e','e','r'};
851 #endif
852         struct xseg *xseg = peer->xseg;
853         struct bench *prefs = peer->priv;
854         xport portno_start = peer->portno_start;
855         xport portno_end = peer->portno_end;
856         pid_t pid = syscall(SYS_gettid);
857         uint64_t threshold = peer->threshold;
858         threshold /= (1 + portno_end - portno_start);
859         threshold += 1;
860         uint64_t next_report = prefs->rep->interval;
861         uint64_t loops;
862         int r;
863
864         XSEGLOG2(&lc, I, "%s has tid %u.\n",id, pid);
865         xseg_init_local_signal(xseg, peer->portno_start);
866
867         if (GET_FLAG(PROGRESS, prefs->flags) != PROGRESS_NO)
868                 print_dummy_progress(prefs);
869
870         /* If no ping is going to be sent, we can begin the benchmark now. */
871         if (GET_FLAG(PING, prefs->flags) == PING_MODE_ON)
872                 send_ping_request(peer, prefs);
873         else
874                 timer_start(prefs, prefs->total_tm);
875
876 send_request:
877         while (!(isTerminate() && all_peer_reqs_free(peer))) {
878                 while (CAN_SEND_REQUEST(prefs)) {
879                         xseg_cancel_wait(xseg, peer->portno_start);
880                         XSEGLOG2(&lc, D, "...because %lu < %lu && %lu < %lu\n",
881                                 prefs->status->submitted - prefs->status->received,
882                                 prefs->iodepth, prefs->status->received,
883                                 prefs->status->max);
884                         XSEGLOG2(&lc, D, "Start sending new request\n");
885                         r = send_request(peer, prefs);
886                         if (r < 0)
887                                 break;
888                 }
889                 //Heart of peerd_loop. This loop is common for everyone.
890                 for (loops = threshold; loops > 0; loops--) {
891                         if (loops == 1)
892                                 xseg_prepare_wait(xseg, peer->portno_start);
893
894                         if (CAN_PRINT_PROGRESS(prefs, next_report)) {
895                                 print_progress(prefs);
896                                 next_report += prefs->rep->interval;
897                         }
898
899                         if (check_ports(peer)) {
900                                 //If an old request has just been acked, the
901                                 //most sensible thing to do is to immediately
902                                 //send a new one
903                                 if (prefs->status->received < prefs->status->max)
904                                         goto send_request;
905                                 else
906                                         return 0;
907                         }
908                 }
909                 XSEGLOG2(&lc, I, "%s goes to sleep\n", id);
910                 xseg_wait_signal(xseg, peer->sd, 10000000UL);
911                 xseg_cancel_wait(xseg, peer->portno_start);
912                 XSEGLOG2(&lc, I, "%s woke up\n", id);
913         }
914
915         XSEGLOG2(&lc, I, "peer->free_reqs = %d, peer->nr_ops = %d\n",
916                         xq_count(&peer->free_reqs), peer->nr_ops);
917         return 0;
918 }
919
920 void custom_peer_finalize(struct peerd *peer)
921 {
922         struct bench *prefs = peer->priv;
923         //TODO: Measure mean time, standard variation
924
925         timer_stop(prefs, prefs->total_tm, NULL);
926
927         if (GET_FLAG(PROGRESS, prefs->flags) != PROGRESS_NO)
928                 clear_report_lines(prefs->rep->lines);
929
930         print_req_stats(prefs);
931         print_remaining(prefs);
932         print_total_res(prefs);
933
934         if (GET_FLAG(INSANITY, prefs->flags) >= prefs->rec_tm->insanity)
935                 print_rec_res(prefs);
936
937         print_divider();
938         /*
939          * This is kinda hacky but is reasonable.
940          * During the benchmark, we need to calculate the bandwidth within an
941          * interval.
942          * After the benchmark, we need to calculate the total bandwidth. To do
943          * so, we treat the benchmark as one single interval, and that's what
944          * the two lines below do. This way, the same code can aplly to both
945          * cases
946          */
947         prefs->total_tm->elapsed_time = prefs->total_tm->sum;
948         prefs->rep->interval = prefs->status->received;
949         print_io_stats(prefs);
950         fflush(stdout);
951         return;
952 }
953
954 /*
955  * handle_received: +1 to our received requests.
956  * Do some sanity checks and then check if request is failed.
957  * If not try to verify the request if asked.
958  */
959 static void handle_received(struct peerd *peer, struct peer_req *pr)
960 {
961         //FIXME: handle null pointer
962         struct bench *prefs = peer->priv;
963         struct timer *rec = prefs->rec_tm;
964         int start_timer = 0;
965
966         if (!pr->req) {
967                 //This is a serious error, so we must stop
968                 XSEGLOG2(&lc, E, "Received peer request with no xseg request");
969                 terminated++;
970                 return;
971         }
972
973         /*
974          * If we were in ping mode, we can now switch off and start the
975          * benchmark.
976          */
977         if (GET_FLAG(PING, prefs->flags) == PING_MODE_ON) {
978                 XSEGLOG2(&lc, I, "Ping received. Benchmark can start now.");
979                 SET_FLAG(PING, prefs->flags, PING_MODE_OFF);
980                 start_timer = 1;
981                 goto out;
982         }
983
984         prefs->status->received++;
985
986         if ((GET_FLAG(INSANITY, prefs->flags) < rec->insanity) && !pr->priv) {
987                 XSEGLOG2(&lc, W, "Cannot find submission time of request");
988                 return;
989         }
990
991         timer_stop(prefs, rec, (struct timespec *)pr->priv);
992
993         if (!(pr->req->state & XS_SERVED))
994                 prefs->status->failed++;
995         else if (CAN_VERIFY(prefs) && read_chunk(prefs, pr->req))
996                 prefs->status->corrupted++;
997
998 out:
999         if (xseg_put_request(peer->xseg, pr->req, pr->portno))
1000                 XSEGLOG2(&lc, W, "Cannot put xseg request\n");
1001
1002         free_peer_req(peer, pr);
1003
1004         if (start_timer)
1005                 timer_start(prefs, prefs->total_tm);
1006 }
1007
1008 int dispatch(struct peerd *peer, struct peer_req *pr, struct xseg_request *req,
1009                 enum dispatch_reason reason)
1010 {
1011         switch (reason) {
1012                 case dispatch_accept:
1013                         //This is wrong, benchmarking peer should not accept requests,
1014                         //only receive them.
1015                         XSEGLOG2(&lc, W, "Bench peer should not accept requests\n");
1016                         complete(peer, pr);
1017                         break;
1018                 case dispatch_receive:
1019                         handle_received(peer, pr);
1020                         break;
1021                 default:
1022                         fail(peer, pr);
1023         }
1024         return 0;
1025 }