bench: Break large source file into smaller files
[archipelago] / xseg / peers / user / bench-verify.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 #include <stdio.h>
36 #include <stdlib.h>
37 #include <unistd.h>
38 #include <sys/syscall.h>
39 #include <sys/types.h>
40 #include <pthread.h>
41 #include <xseg/xseg.h>
42 #include <peer.h>
43 #include <time.h>
44 #include <sys/util.h>
45 #include <signal.h>
46 #include <bench-xseg.h>
47
48 #include <math.h>
49 #include <string.h>
50
51 /********************\
52  * Diagnostic tools *
53 \********************/
54
55 #define PRINT_SIG(__who, __sig)                                         \
56         fprintf(stdout, "%s (%lu): id %lu, object %lu, offset %lu\n",   \
57                         #__who, (uint64_t)(__sig),                      \
58                         ((struct signature *)__sig)->id,                \
59                         ((struct signature *)__sig)->object,            \
60                         ((struct signature *)__sig)->offset);
61
62 __attribute__ ((unused))
63 void inspect_obv(struct object_vars *obv)
64 {
65         XSEGLOG2(&lc, D, "Struct object vars:\n"
66                         "\tname: %s (%d),\n"
67                         "\tprefix: %s (%d),\n"
68                         "\tseed: %lu (%d),\n"
69                         "\tobjnum: %lu (%d)",
70                         obv->name, obv->namelen, obv->prefix, obv->prefixlen,
71                         obv->seed, obv->seedlen,
72                         obv->objnum, obv->objnumlen);
73 }
74
75 /******************************\
76  * Static miscellaneous tools *
77 \******************************/
78
79 static inline uint64_t __get_object_from_name(struct object_vars *obv,
80                 char *name)
81 {
82         /* In case of --objname switch */
83         if (obv->name[0])
84                 return 0;
85
86         /* Keep only the object number */
87         return atol(name + obv->namelen - obv->objnumlen);
88 }
89
90 static inline int __snap_to_bound8(uint64_t space)
91 {
92         return space > 8 ? 8 : space;
93 }
94
95 static inline void __write_sig(struct bench_lfsr *sg, uint64_t *d, uint64_t s,
96                 int pos)
97 {
98         uint64_t i;
99         uint64_t last_val;
100         uint64_t space_left;
101
102         /* Write random numbers (based on global_id) every 24 bytes */
103         /* TODO: Should we use memcpy? */
104         for (i = pos; i < (s / 8) - (3 - pos); i += 3)
105                 *(d + i) = lfsr_next(sg);
106
107         /* special care for last chunk */
108         last_val = lfsr_next(sg);
109         space_left = s - (i * 8);
110         memcpy(d + i, &last_val, __snap_to_bound8(space_left));
111 }
112
113 static inline int __read_sig(struct bench_lfsr *sg, uint64_t *d, uint64_t s,
114                 int pos)
115 {
116         uint64_t i;
117         uint64_t last_val;
118         uint64_t space_left;
119
120         /* TODO: Should we use memcmp? */
121         for (i = pos; i < (s / 8) - (3 - pos); i += 3) {
122                 if (*(d + i) != lfsr_next(sg))
123                         return 1;
124         }
125         /* special care for last chunk */
126         last_val = lfsr_next(sg);
127         space_left = s - (i * 8);
128         if (memcmp(d + i, &last_val, __snap_to_bound8(space_left)))
129                 return 1;
130
131         return 0;
132 }
133
134 /*
135  * ***********************************************
136  * `create_chunk` handles 3 identifiers:
137  * 1. The benchmark's global_id
138  * 2. The object's number
139  * 3. The chunk offset in the object
140  *
141  * ************************************************
142  * `readwrite_chunk_full` takes the above 3 identifiers and feeds them as seeds
143  * in 63-bit LFSRs. The numbers generated are written consecutively in chunk's
144  * memory range. For example, for a 72-byte chunk:
145  *
146  * || 1 | 2 | 3 | 1 | 2 | 3 | 1 | 2 | 3 ||
147  *  ^   8  16  24  32  40  48  56  64   ^
148  *  |                                   |
149  *  |                                   |
150  * start                               end
151  *
152  * 1,2,3 differ between each iteration
153  *
154  * **************************************************
155  * `_create_chunk_meta` simply writes the above 3 ids in the start and end of
156  * the chunk's memory range, so it should be much faster (but less safe)
157  *
158  * **************************************************
159  * In both cases, special care is taken not to exceed the chunk's memory range.
160  * Also, the bare minimum chunk to verify should be 48 bytes. This limit is set
161  * by readwrite_chunk_meta, which expects to write in a memory at least this
162  * big.
163  *
164  * **************************************************
165  * Note: The diagram above also represents the x86_64's endianness.
166  * Endianness must be taken into careful consideration when examining a memory
167  * chunk.
168  */
169 static int readwrite_chunk_full(struct bench *prefs, struct xseg_request *req)
170 {
171         struct bench_lfsr id_lfsr;
172         struct bench_lfsr obj_lfsr;
173         struct bench_lfsr off_lfsr;
174         struct xseg *xseg = prefs->peer->xseg;
175         uint64_t id = prefs->objvars->seed;
176         uint64_t object = prefs->objvars->objnum;
177         uint64_t *d = (uint64_t *)xseg_get_data(xseg, req);
178         uint64_t s = req->size;
179
180         /* Create 63-bit LFSRs */
181         lfsr_init(&id_lfsr, 0x7FFFFFFFFFFFFFFF, id, 0);
182         lfsr_init(&obj_lfsr, 0x7FFFFFFFFFFFFFFF, object, 0);
183         lfsr_init(&off_lfsr, 0x7FFFFFFFFFFFFFFF, req->offset, 0);
184
185         if (s < sizeof(struct signature)) {
186                 XSEGLOG2(&lc, E, "Too small chunk size (%lu butes). Leaving.", s);
187                 return 1;
188         }
189
190         /*
191          * Every write operation has its read counterpart which, if it finds any
192          * corruption, returns 1
193          */
194
195         if (req->op == X_WRITE) {
196                 __write_sig(&id_lfsr, d, s, 0);
197                 __write_sig(&obj_lfsr, d, s, 1);
198                 __write_sig(&off_lfsr, d, s, 2);
199         } else {
200                 if (__read_sig(&id_lfsr, d, s, 0))
201                         return 1;
202                 if (__read_sig(&obj_lfsr, d, s, 1))
203                         return 1;
204                 if(__read_sig(&off_lfsr, d, s, 2))
205                         return 1;
206         }
207
208         return 0;
209 }
210
211 static int readwrite_chunk_meta(struct bench *prefs, struct xseg_request *req)
212 {
213         struct xseg *xseg = prefs->peer->xseg;
214         struct signature sig;
215         uint64_t id = prefs->objvars->seed;
216         uint64_t object = prefs->objvars->objnum;
217         char *d = xseg_get_data(xseg, req);
218         uint64_t s = req->size;
219         int sig_s = sizeof(struct signature);
220         int r = 0;
221
222         sig.id = id;
223         sig.object = object;
224         sig.offset = req->offset;
225
226         if (s < sig_s) {
227                 XSEGLOG2(&lc, E, "Too small chunk size (%lu butes). "
228                                 "Leaving.", s);
229                 return 1;
230         }
231
232         //PRINT_SIG(expected, (&sig));
233         /* Read/Write chunk signature both at its start and at its end */
234         if (req->op == X_WRITE) {
235                 memcpy(d, &sig, sig_s);
236                 memcpy(d + s - sig_s, &sig, sig_s);
237         } else {
238                 if (memcmp(d, &sig, sig_s))
239                         r = 1;
240                 else if (memcmp(d + s - sig_s, &sig, sig_s))
241                         r = 1;
242         }
243         //PRINT_SIG(start, d);
244         //PRINT_SIG(end, (d + s - sig_s));
245         return r;
246 }
247
248 /*
249  * We want these functions to be as fast as possible in case we haven't asked
250  * for verification
251  */
252 void create_chunk(struct bench *prefs, struct xseg_request *req, uint64_t new)
253 {
254         int verify;
255
256         verify = GET_FLAG(VERIFY, prefs->flags);
257         switch (verify) {
258                 case VERIFY_NO:
259                         break;
260                 case VERIFY_META:
261                         readwrite_chunk_meta(prefs, req);
262                         break;
263                 case VERIFY_FULL:
264                         readwrite_chunk_full(prefs, req);
265                         break;
266                 default:
267                         XSEGLOG2(&lc, W, "Unexpected verification mode: %d\n",
268                                         verify);
269         }
270 }
271
272 int read_chunk(struct bench *prefs, struct xseg_request *req)
273 {
274         struct xseg *xseg = prefs->peer->xseg;
275         struct object_vars *obv = prefs->objvars;
276         char *target;
277         int verify;
278         int r = 0;
279
280         verify = GET_FLAG(VERIFY, prefs->flags);
281         switch (verify) {
282                 case VERIFY_NO:
283                         break;
284                 case VERIFY_META:
285                         target = xseg_get_target(xseg, req);
286                         obv->objnum = __get_object_from_name(obv, target);
287                         r = readwrite_chunk_meta(prefs, req);
288                         break;
289                 case VERIFY_FULL:
290                         target = xseg_get_target(xseg, req);
291                         obv->objnum = __get_object_from_name(obv, target);
292                         r = readwrite_chunk_full(prefs, req);
293                         break;
294                 default:
295                         XSEGLOG2(&lc, W, "Unexpected verification mode: %d\n",
296                                         verify);
297         }
298         return r;
299 }
300