1bc68bb02SChiyoung Seo/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2bc68bb02SChiyoung Seo/*
3bc68bb02SChiyoung Seo *     Copyright 2010 Couchbase, Inc
4bc68bb02SChiyoung Seo *
5bc68bb02SChiyoung Seo *   Licensed under the Apache License, Version 2.0 (the "License");
6bc68bb02SChiyoung Seo *   you may not use this file except in compliance with the License.
7bc68bb02SChiyoung Seo *   You may obtain a copy of the License at
8bc68bb02SChiyoung Seo *
9bc68bb02SChiyoung Seo *       http://www.apache.org/licenses/LICENSE-2.0
10bc68bb02SChiyoung Seo *
11bc68bb02SChiyoung Seo *   Unless required by applicable law or agreed to in writing, software
12bc68bb02SChiyoung Seo *   distributed under the License is distributed on an "AS IS" BASIS,
13bc68bb02SChiyoung Seo *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14bc68bb02SChiyoung Seo *   See the License for the specific language governing permissions and
15bc68bb02SChiyoung Seo *   limitations under the License.
16bc68bb02SChiyoung Seo */
17bc68bb02SChiyoung Seo
187c0433f5SJung-Sang Ahn#include <stdio.h>
197c0433f5SJung-Sang Ahn#include <stdlib.h>
207c0433f5SJung-Sang Ahn#include <string.h>
217c0433f5SJung-Sang Ahn
227c0433f5SJung-Sang Ahn#include "test.h"
237c0433f5SJung-Sang Ahn#include "blockcache.h"
247c0433f5SJung-Sang Ahn#include "filemgr.h"
25f693a021SSundar Sridharan#include "filemgr_ops.h"
262534ac38SJung-Sang Ahn#include "crc32.h"
272534ac38SJung-Sang Ahn
282534ac38SJung-Sang Ahn#include "memleak.h"
297c0433f5SJung-Sang Ahn
30733d0c43Sabhinavdangeti#undef THREAD_SANITIZER
31733d0c43Sabhinavdangeti#if __clang__
32733d0c43Sabhinavdangeti#   if defined(__has_feature) && __has_feature(thread_sanitizer)
33733d0c43Sabhinavdangeti#define THREAD_SANITIZER
34733d0c43Sabhinavdangeti#   endif
377c0433f5SJung-Sang Ahnvoid basic_test()
387c0433f5SJung-Sang Ahn{
392889254eSJung-Sang Ahn    TEST_INIT();
402889254eSJung-Sang Ahn
412889254eSJung-Sang Ahn    struct filemgr *file;
422889254eSJung-Sang Ahn    struct filemgr_config config;
434dc52e2dSTrond Norbye    int i;
442889254eSJung-Sang Ahn    uint8_t buf[4096];
45225450a4SSundar Sridharan    char *fname = (char *) "./bcache_testfile";
462889254eSJung-Sang Ahn
472534ac38SJung-Sang Ahn    memset(&config, 0, sizeof(config));
482889254eSJung-Sang Ahn    config.blocksize = 4096;
492889254eSJung-Sang Ahn    config.ncacheblock = 5;
50a765ad59SChiyoung Seo    config.options = FILEMGR_CREATE;
51b84b312aSChiyoung Seo    config.num_wal_shards = 8;
52a765ad59SChiyoung Seo    filemgr_open_result result = filemgr_open(fname, get_filemgr_ops(), &config, NULL);
53a765ad59SChiyoung Seo    file = result.file;
542889254eSJung-Sang Ahn
552889254eSJung-Sang Ahn    for (i=0;i<5;++i) {
5601e33605SChiyoung Seo        filemgr_alloc(file, NULL);
5701e33605SChiyoung Seo        filemgr_write(file, i, buf, NULL);
582889254eSJung-Sang Ahn    }
59d4c9c4bbSSundar Sridharan    filemgr_commit(file, true, NULL);
602889254eSJung-Sang Ahn    for (i=5;i<10;++i) {
6101e33605SChiyoung Seo        filemgr_alloc(file, NULL);
6201e33605SChiyoung Seo        filemgr_write(file, i, buf, NULL);
632889254eSJung-Sang Ahn    }
64d4c9c4bbSSundar Sridharan    filemgr_commit(file, true, NULL);
659ceccb2cSJung-Sang Ahn
66c6c3d274SChiyoung Seo    filemgr_read(file, 8, buf, NULL, true);
67c6c3d274SChiyoung Seo    filemgr_read(file, 9, buf, NULL, true);
682889254eSJung-Sang Ahn
69c6c3d274SChiyoung Seo    filemgr_read(file, 1, buf, NULL, true);
70c6c3d274SChiyoung Seo    filemgr_read(file, 2, buf, NULL, true);
71c6c3d274SChiyoung Seo    filemgr_read(file, 3, buf, NULL, true);
722889254eSJung-Sang Ahn
73c6c3d274SChiyoung Seo    filemgr_read(file, 7, buf, NULL, true);
74c6c3d274SChiyoung Seo    filemgr_read(file, 1, buf, NULL, true);
75c6c3d274SChiyoung Seo    filemgr_read(file, 9, buf, NULL, true);
762889254eSJung-Sang Ahn
7701e33605SChiyoung Seo    filemgr_alloc(file, NULL);
7801e33605SChiyoung Seo    filemgr_write(file, 10, buf, NULL);
792889254eSJung-Sang Ahn
802889254eSJung-Sang Ahn    TEST_RESULT("basic test");
817c0433f5SJung-Sang Ahn}
827c0433f5SJung-Sang Ahn
837c0433f5SJung-Sang Ahnvoid basic_test2()
847c0433f5SJung-Sang Ahn{
852889254eSJung-Sang Ahn    TEST_INIT();
862889254eSJung-Sang Ahn
872889254eSJung-Sang Ahn    struct filemgr *file;
882889254eSJung-Sang Ahn    struct filemgr_config config;
894dc52e2dSTrond Norbye    int i;
902889254eSJung-Sang Ahn    uint8_t buf[4096];
91225450a4SSundar Sridharan    char *fname = (char *) "./bcache_testfile";
922534ac38SJung-Sang Ahn    int r;
93225450a4SSundar Sridharan    r = system(SHELL_DEL " bcache_testfile");
9445647b9eSSundar Sridharan    (void)r;
959ceccb2cSJung-Sang Ahn
962534ac38SJung-Sang Ahn    memset(&config, 0, sizeof(config));
972889254eSJung-Sang Ahn    config.blocksize = 4096;
982889254eSJung-Sang Ahn    config.ncacheblock = 5;
999ceccb2cSJung-Sang Ahn    config.flag = 0x0;
100a765ad59SChiyoung Seo    config.options = FILEMGR_CREATE;
101b84b312aSChiyoung Seo    config.num_wal_shards = 8;
102a765ad59SChiyoung Seo    filemgr_open_result result = filemgr_open(fname, get_filemgr_ops(), &config, NULL);
103a765ad59SChiyoung Seo    file = result.file;
1042889254eSJung-Sang Ahn
1052889254eSJung-Sang Ahn    for (i=0;i<5;++i) {
10601e33605SChiyoung Seo        filemgr_alloc(file, NULL);
10701e33605SChiyoung Seo        filemgr_write(file, i, buf, NULL);
1082889254eSJung-Sang Ahn    }
1092889254eSJung-Sang Ahn    for (i=5;i<10;++i) {
11001e33605SChiyoung Seo        filemgr_alloc(file, NULL);
11101e33605SChiyoung Seo        filemgr_write(file, i, buf, NULL);
1122889254eSJung-Sang Ahn    }
113d4c9c4bbSSundar Sridharan    filemgr_commit(file, true, NULL);
114fef67a48SChiyoung Seo    filemgr_close(file, true, NULL, NULL);
1152534ac38SJung-Sang Ahn    filemgr_shutdown();
1162889254eSJung-Sang Ahn
1172889254eSJung-Sang Ahn    TEST_RESULT("basic test");
1187c0433f5SJung-Sang Ahn
1197c0433f5SJung-Sang Ahn}
1207c0433f5SJung-Sang Ahn
1212534ac38SJung-Sang Ahnstruct worker_args{
122d25f8d6dSJung-Sang Ahn    size_t n;
1232534ac38SJung-Sang Ahn    struct filemgr *file;
1242534ac38SJung-Sang Ahn    size_t writer;
1252534ac38SJung-Sang Ahn    size_t nblocks;
1262534ac38SJung-Sang Ahn    size_t time_sec;
1272534ac38SJung-Sang Ahn};
1282534ac38SJung-Sang Ahn
1292534ac38SJung-Sang Ahnvoid * worker(void *voidargs)
1302534ac38SJung-Sang Ahn{
1318e1b9ec2SJung-Sang Ahn    uint8_t *buf = (uint8_t *)malloc(4096);
1322534ac38SJung-Sang Ahn    struct worker_args *args = (struct worker_args*)voidargs;
133eccdfcd9SJung-Sang Ahn    struct timeval ts_begin, ts_cur, ts_gap;
13473a5afcaSSundar Sridharan
13514fb1c02SChiyoung Seo    ssize_t ret;
1362534ac38SJung-Sang Ahn    bid_t bid;
1372534ac38SJung-Sang Ahn    uint32_t crc, crc_file;
1382534ac38SJung-Sang Ahn    uint64_t i, c, run_count=0;
13945647b9eSSundar Sridharan    TEST_INIT();
1402534ac38SJung-Sang Ahn
1412534ac38SJung-Sang Ahn    memset(buf, 0, 4096);
142eccdfcd9SJung-Sang Ahn    gettimeofday(&ts_begin, NULL);
1432534ac38SJung-Sang Ahn
1442534ac38SJung-Sang Ahn    while(1) {
1452534ac38SJung-Sang Ahn        bid = rand() % args->nblocks;
1462534ac38SJung-Sang Ahn        ret = bcache_read(args->file, bid, buf);
1472534ac38SJung-Sang Ahn        if (ret <= 0) {
14814fb1c02SChiyoung Seo            ret = args->file->ops->pread(args->file->fd, buf,
14914fb1c02SChiyoung Seo                                         args->file->blocksize, bid * args->file->blocksize);
1501de419c8SSundar Sridharan            TEST_CHK(ret == (ssize_t)args->file->blocksize);
15167d223e3SSundar Sridharan            ret = bcache_write(args->file, bid, buf, BCACHE_REQ_CLEAN, false);
1521de419c8SSundar Sridharan            TEST_CHK(ret == (ssize_t)args->file->blocksize);
1532534ac38SJung-Sang Ahn        }
1542534ac38SJung-Sang Ahn        crc_file = crc32_8(buf, sizeof(uint64_t)*2, 0);
155575a6f76SSundar Sridharan        (void)crc_file;
1562534ac38SJung-Sang Ahn        memcpy(&i, buf, sizeof(i));
1572534ac38SJung-Sang Ahn        memcpy(&crc, buf + sizeof(uint64_t)*2, sizeof(crc));
158305477c7SChiyoung Seo        // Disable checking the CRC value at this time as pread and pwrite are
159305477c7SChiyoung Seo        // not thread-safe.
160305477c7SChiyoung Seo        // TEST_CHK(crc == crc_file && i==bid);
161d25f8d6dSJung-Sang Ahn        //DBG("%d %d %d %x %x\n", (int)args->n, (int)i, (int)bid, (int)crc, (int)crc_file);
1629ceccb2cSJung-Sang Ahn
1632534ac38SJung-Sang Ahn        if (args->writer) {
1642534ac38SJung-Sang Ahn            memcpy(&c, buf+sizeof(i), sizeof(c));
1652534ac38SJung-Sang Ahn            c++;
1662534ac38SJung-Sang Ahn            memcpy(buf+sizeof(i), &c, sizeof(c));
1672534ac38SJung-Sang Ahn            crc = crc32_8(buf, sizeof(uint64_t)*2, 0);
1682534ac38SJung-Sang Ahn            memcpy(buf + sizeof(uint64_t)*2, &crc, sizeof(crc));
1692534ac38SJung-Sang Ahn
17067d223e3SSundar Sridharan            ret = bcache_write(args->file, bid, buf, BCACHE_REQ_DIRTY, true);
1711de419c8SSundar Sridharan            TEST_CHK(ret == (ssize_t)args->file->blocksize);
17267d223e3SSundar Sridharan        } else { // have some of the reader threads flush dirty immutable blocks
17367d223e3SSundar Sridharan            if (bid <= args->nblocks / 4) { // 25% probability
17467d223e3SSundar Sridharan                filemgr_flush_immutable(args->file, NULL);
17567d223e3SSundar Sridharan            }
1762534ac38SJung-Sang Ahn        }
1779ceccb2cSJung-Sang Ahn
178eccdfcd9SJung-Sang Ahn        gettimeofday(&ts_cur, NULL);
179eccdfcd9SJung-Sang Ahn        ts_gap = _utime_gap(ts_begin, ts_cur);
180575a6f76SSundar Sridharan        if ((size_t)ts_gap.tv_sec >= args->time_sec) break;
1812534ac38SJung-Sang Ahn
1822534ac38SJung-Sang Ahn        run_count++;
1832534ac38SJung-Sang Ahn    }
1842534ac38SJung-Sang Ahn
1852534ac38SJung-Sang Ahn    free(buf);
1868e1b9ec2SJung-Sang Ahn    thread_exit(0);
187eccdfcd9SJung-Sang Ahn    return NULL;
1882534ac38SJung-Sang Ahn}
1892534ac38SJung-Sang Ahn
1902534ac38SJung-Sang Ahnvoid multi_thread_test(
1912534ac38SJung-Sang Ahn    int nblocks, int cachesize, int blocksize, int time_sec, int nwriters, int nreaders)
1927c0433f5SJung-Sang Ahn{
1932534ac38SJung-Sang Ahn    TEST_INIT();
1942534ac38SJung-Sang Ahn
1952534ac38SJung-Sang Ahn    struct filemgr *file;
1962534ac38SJung-Sang Ahn    struct filemgr_config config;
1972534ac38SJung-Sang Ahn    int n = nwriters + nreaders;
1982534ac38SJung-Sang Ahn    uint64_t i, j;
1992534ac38SJung-Sang Ahn    uint32_t crc;
2008e1b9ec2SJung-Sang Ahn    uint8_t *buf;
2012889254eSJung-Sang Ahn    int r;
202225450a4SSundar Sridharan    char *fname = (char *) "./bcache_testfile";
2038e1b9ec2SJung-Sang Ahn    thread_t *tid = alca(thread_t, n);
2048e1b9ec2SJung-Sang Ahn    struct worker_args *args = alca(struct worker_args, n);
2058e1b9ec2SJung-Sang Ahn    void **ret = alca(void *, n);
2069ceccb2cSJung-Sang Ahn
207225450a4SSundar Sridharan    r = system(SHELL_DEL " bcache_testfile");
20845647b9eSSundar Sridharan    (void)r;
2092534ac38SJung-Sang Ahn
210ca602719SJung-Sang Ahn    memleak_start();
2112534ac38SJung-Sang Ahn
2128e1b9ec2SJung-Sang Ahn    buf = (uint8_t *)malloc(4096);
2132534ac38SJung-Sang Ahn    memset(buf, 0, 4096);
2142534ac38SJung-Sang Ahn
2152534ac38SJung-Sang Ahn    memset(&config, 0, sizeof(config));
2162534ac38SJung-Sang Ahn    config.blocksize = blocksize;
2172534ac38SJung-Sang Ahn    config.ncacheblock = cachesize;
2182534ac38SJung-Sang Ahn    config.flag = 0x0;
219a765ad59SChiyoung Seo    config.options = FILEMGR_CREATE;
220b84b312aSChiyoung Seo    config.num_wal_shards = 8;
221a765ad59SChiyoung Seo    filemgr_open_result result = filemgr_open(fname, get_filemgr_ops(), &config, NULL);
222a765ad59SChiyoung Seo    file = result.file;
2232534ac38SJung-Sang Ahn
224575a6f76SSundar Sridharan    for (i=0;i<(uint64_t)nblocks;++i) {
2252534ac38SJung-Sang Ahn        memcpy(buf, &i, sizeof(i));
2262534ac38SJung-Sang Ahn        j = 0;
2272534ac38SJung-Sang Ahn        memcpy(buf + sizeof(i), &j, sizeof(j));
2282534ac38SJung-Sang Ahn        crc = crc32_8(buf, sizeof(i) + sizeof(j), 0);
2292534ac38SJung-Sang Ahn        memcpy(buf + sizeof(i) + sizeof(j), &crc, sizeof(crc));
23067d223e3SSundar Sridharan        bcache_write(file, (bid_t)i, buf, BCACHE_REQ_DIRTY, false);
2312534ac38SJung-Sang Ahn    }
2322534ac38SJung-Sang Ahn
233575a6f76SSundar Sridharan    for (i=0;i<(uint64_t)n;++i){
234d25f8d6dSJung-Sang Ahn        args[i].n = i;
2352534ac38SJung-Sang Ahn        args[i].file = file;
236575a6f76SSundar Sridharan        args[i].writer = ((i<(uint64_t)nwriters)?(1):(0));
2372534ac38SJung-Sang Ahn        args[i].nblocks = nblocks;
2382534ac38SJung-Sang Ahn        args[i].time_sec = time_sec;
2398e1b9ec2SJung-Sang Ahn        thread_create(&tid[i], worker, &args[i]);
2402534ac38SJung-Sang Ahn    }
2412534ac38SJung-Sang Ahn
2422534ac38SJung-Sang Ahn    DBG("wait for %d seconds..\n", time_sec);
243575a6f76SSundar Sridharan    for (i=0;i<(uint64_t)n;++i){
2448e1b9ec2SJung-Sang Ahn        thread_join(tid[i], &ret[i]);
2452534ac38SJung-Sang Ahn    }
2462534ac38SJung-Sang Ahn
247d4c9c4bbSSundar Sridharan    filemgr_commit(file, true, NULL);
248fef67a48SChiyoung Seo    filemgr_close(file, true, NULL, NULL);
2492534ac38SJung-Sang Ahn    filemgr_shutdown();
2502534ac38SJung-Sang Ahn    free(buf);
2512534ac38SJung-Sang Ahn
252ca602719SJung-Sang Ahn    memleak_end();
2532534ac38SJung-Sang Ahn    TEST_RESULT("multi thread test");
2542534ac38SJung-Sang Ahn}
2552534ac38SJung-Sang Ahn
2562534ac38SJung-Sang Ahnint main()
2572534ac38SJung-Sang Ahn{
2582889254eSJung-Sang Ahn    basic_test2();
259733d0c43Sabhinavdangeti#if !defined(THREAD_SANITIZER)
260733d0c43Sabhinavdangeti    /**
261733d0c43Sabhinavdangeti     * The following tests will be disabled when the code is run with
262733d0c43Sabhinavdangeti     * thread sanitizer, because they point out a data race in writing/
263733d0c43Sabhinavdangeti     * reading from a dirty block which will not happen in reality.
264733d0c43Sabhinavdangeti     *
265733d0c43Sabhinavdangeti     * The bcache partition lock is release iff a given dirty block has
266733d0c43Sabhinavdangeti     * already been marked as immutable. These unit tests attempt to
267733d0c43Sabhinavdangeti     * write to the same immutable block again causing this race. In
268733d0c43Sabhinavdangeti     * reality, this won't happen as these operations go through
269733d0c43Sabhinavdangeti     * filemgr_read() and filemgr_write().
270733d0c43Sabhinavdangeti     */
2718e1b9ec2SJung-Sang Ahn    multi_thread_test(4, 1, 32, 20, 1, 7);
27267d223e3SSundar Sridharan    multi_thread_test(100, 1, 32, 10, 1, 7);
2747c0433f5SJung-Sang Ahn
2752889254eSJung-Sang Ahn    return 0;
2767c0433f5SJung-Sang Ahn}