1 /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  *     Copyright 2010 Couchbase, Inc
4  *
5  *   Licensed under the Apache License, Version 2.0 (the "License");
6  *   you may not use this file except in compliance with the License.
7  *   You may obtain a copy of the License at
8  *
9  *       http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *   Unless required by applicable law or agreed to in writing, software
12  *   distributed under the License is distributed on an "AS IS" BASIS,
13  *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *   See the License for the specific language governing permissions and
15  *   limitations under the License.
16  */
17 
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 
22 #include "test.h"
23 #include "blockcache.h"
24 #include "filemgr.h"
25 #include "filemgr_ops.h"
26 #include "crc32.h"
27 
28 #include "memleak.h"
29 
30 #undef THREAD_SANITIZER
31 #if __clang__
32 #   if defined(__has_feature) && __has_feature(thread_sanitizer)
33 #define THREAD_SANITIZER
34 #   endif
35 #endif
36 
basic_test()37 void basic_test()
38 {
39     TEST_INIT();
40 
41     struct filemgr *file;
42     struct filemgr_config config;
43     int i;
44     uint8_t buf[4096];
45     char *fname = (char *) "./bcache_testfile";
46 
47     memset(&config, 0, sizeof(config));
48     config.blocksize = 4096;
49     config.ncacheblock = 5;
50     config.options = FILEMGR_CREATE;
51     config.num_wal_shards = 8;
52     filemgr_open_result result = filemgr_open(fname, get_filemgr_ops(), &config, NULL);
53     file = result.file;
54 
55     for (i=0;i<5;++i) {
56         filemgr_alloc(file, NULL);
57         filemgr_write(file, i, buf, NULL);
58     }
59     filemgr_commit(file, true, NULL);
60     for (i=5;i<10;++i) {
61         filemgr_alloc(file, NULL);
62         filemgr_write(file, i, buf, NULL);
63     }
64     filemgr_commit(file, true, NULL);
65 
66     filemgr_read(file, 8, buf, NULL, true);
67     filemgr_read(file, 9, buf, NULL, true);
68 
69     filemgr_read(file, 1, buf, NULL, true);
70     filemgr_read(file, 2, buf, NULL, true);
71     filemgr_read(file, 3, buf, NULL, true);
72 
73     filemgr_read(file, 7, buf, NULL, true);
74     filemgr_read(file, 1, buf, NULL, true);
75     filemgr_read(file, 9, buf, NULL, true);
76 
77     filemgr_alloc(file, NULL);
78     filemgr_write(file, 10, buf, NULL);
79 
80     TEST_RESULT("basic test");
81 }
82 
basic_test2()83 void basic_test2()
84 {
85     TEST_INIT();
86 
87     struct filemgr *file;
88     struct filemgr_config config;
89     int i;
90     uint8_t buf[4096];
91     char *fname = (char *) "./bcache_testfile";
92     int r;
93     r = system(SHELL_DEL " bcache_testfile");
94     (void)r;
95 
96     memset(&config, 0, sizeof(config));
97     config.blocksize = 4096;
98     config.ncacheblock = 5;
99     config.flag = 0x0;
100     config.options = FILEMGR_CREATE;
101     config.num_wal_shards = 8;
102     filemgr_open_result result = filemgr_open(fname, get_filemgr_ops(), &config, NULL);
103     file = result.file;
104 
105     for (i=0;i<5;++i) {
106         filemgr_alloc(file, NULL);
107         filemgr_write(file, i, buf, NULL);
108     }
109     for (i=5;i<10;++i) {
110         filemgr_alloc(file, NULL);
111         filemgr_write(file, i, buf, NULL);
112     }
113     filemgr_commit(file, true, NULL);
114     filemgr_close(file, true, NULL, NULL);
115     filemgr_shutdown();
116 
117     TEST_RESULT("basic test");
118 
119 }
120 
121 struct worker_args{
122     size_t n;
123     struct filemgr *file;
124     size_t writer;
125     size_t nblocks;
126     size_t time_sec;
127 };
128 
worker(void *voidargs)129 void * worker(void *voidargs)
130 {
131     uint8_t *buf = (uint8_t *)malloc(4096);
132     struct worker_args *args = (struct worker_args*)voidargs;
133     struct timeval ts_begin, ts_cur, ts_gap;
134 
135     ssize_t ret;
136     bid_t bid;
137     uint32_t crc, crc_file;
138     uint64_t i, c, run_count=0;
139     TEST_INIT();
140 
141     memset(buf, 0, 4096);
142     gettimeofday(&ts_begin, NULL);
143 
144     while(1) {
145         bid = rand() % args->nblocks;
146         ret = bcache_read(args->file, bid, buf);
147         if (ret <= 0) {
148             ret = args->file->ops->pread(args->file->fd, buf,
149                                          args->file->blocksize, bid * args->file->blocksize);
150             TEST_CHK(ret == (ssize_t)args->file->blocksize);
151             ret = bcache_write(args->file, bid, buf, BCACHE_REQ_CLEAN, false);
152             TEST_CHK(ret == (ssize_t)args->file->blocksize);
153         }
154         crc_file = crc32_8(buf, sizeof(uint64_t)*2, 0);
155         (void)crc_file;
156         memcpy(&i, buf, sizeof(i));
157         memcpy(&crc, buf + sizeof(uint64_t)*2, sizeof(crc));
158         // Disable checking the CRC value at this time as pread and pwrite are
159         // not thread-safe.
160         // TEST_CHK(crc == crc_file && i==bid);
161         //DBG("%d %d %d %x %x\n", (int)args->n, (int)i, (int)bid, (int)crc, (int)crc_file);
162 
163         if (args->writer) {
164             memcpy(&c, buf+sizeof(i), sizeof(c));
165             c++;
166             memcpy(buf+sizeof(i), &c, sizeof(c));
167             crc = crc32_8(buf, sizeof(uint64_t)*2, 0);
168             memcpy(buf + sizeof(uint64_t)*2, &crc, sizeof(crc));
169 
170             ret = bcache_write(args->file, bid, buf, BCACHE_REQ_DIRTY, true);
171             TEST_CHK(ret == (ssize_t)args->file->blocksize);
172         } else { // have some of the reader threads flush dirty immutable blocks
173             if (bid <= args->nblocks / 4) { // 25% probability
174                 filemgr_flush_immutable(args->file, NULL);
175             }
176         }
177 
178         gettimeofday(&ts_cur, NULL);
179         ts_gap = _utime_gap(ts_begin, ts_cur);
180         if ((size_t)ts_gap.tv_sec >= args->time_sec) break;
181 
182         run_count++;
183     }
184 
185     free(buf);
186     thread_exit(0);
187     return NULL;
188 }
189 
multi_thread_test( int nblocks, int cachesize, int blocksize, int time_sec, int nwriters, int nreaders)190 void multi_thread_test(
191     int nblocks, int cachesize, int blocksize, int time_sec, int nwriters, int nreaders)
192 {
193     TEST_INIT();
194 
195     struct filemgr *file;
196     struct filemgr_config config;
197     int n = nwriters + nreaders;
198     uint64_t i, j;
199     uint32_t crc;
200     uint8_t *buf;
201     int r;
202     char *fname = (char *) "./bcache_testfile";
203     thread_t *tid = alca(thread_t, n);
204     struct worker_args *args = alca(struct worker_args, n);
205     void **ret = alca(void *, n);
206 
207     r = system(SHELL_DEL " bcache_testfile");
208     (void)r;
209 
210     memleak_start();
211 
212     buf = (uint8_t *)malloc(4096);
213     memset(buf, 0, 4096);
214 
215     memset(&config, 0, sizeof(config));
216     config.blocksize = blocksize;
217     config.ncacheblock = cachesize;
218     config.flag = 0x0;
219     config.options = FILEMGR_CREATE;
220     config.num_wal_shards = 8;
221     filemgr_open_result result = filemgr_open(fname, get_filemgr_ops(), &config, NULL);
222     file = result.file;
223 
224     for (i=0;i<(uint64_t)nblocks;++i) {
225         memcpy(buf, &i, sizeof(i));
226         j = 0;
227         memcpy(buf + sizeof(i), &j, sizeof(j));
228         crc = crc32_8(buf, sizeof(i) + sizeof(j), 0);
229         memcpy(buf + sizeof(i) + sizeof(j), &crc, sizeof(crc));
230         bcache_write(file, (bid_t)i, buf, BCACHE_REQ_DIRTY, false);
231     }
232 
233     for (i=0;i<(uint64_t)n;++i){
234         args[i].n = i;
235         args[i].file = file;
236         args[i].writer = ((i<(uint64_t)nwriters)?(1):(0));
237         args[i].nblocks = nblocks;
238         args[i].time_sec = time_sec;
239         thread_create(&tid[i], worker, &args[i]);
240     }
241 
242     DBG("wait for %d seconds..\n", time_sec);
243     for (i=0;i<(uint64_t)n;++i){
244         thread_join(tid[i], &ret[i]);
245     }
246 
247     filemgr_commit(file, true, NULL);
248     filemgr_close(file, true, NULL, NULL);
249     filemgr_shutdown();
250     free(buf);
251 
252     memleak_end();
253     TEST_RESULT("multi thread test");
254 }
255 
main()256 int main()
257 {
258     basic_test2();
259 #if !defined(THREAD_SANITIZER)
260     /**
261      * The following tests will be disabled when the code is run with
262      * thread sanitizer, because they point out a data race in writing/
263      * reading from a dirty block which will not happen in reality.
264      *
265      * The bcache partition lock is release iff a given dirty block has
266      * already been marked as immutable. These unit tests attempt to
267      * write to the same immutable block again causing this race. In
268      * reality, this won't happen as these operations go through
269      * filemgr_read() and filemgr_write().
270      */
271     multi_thread_test(4, 1, 32, 20, 1, 7);
272     multi_thread_test(100, 1, 32, 10, 1, 7);
273 #endif
274 
275     return 0;
276 }
277