mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-21 13:34:40 +03:00
Merge branch '2018-05-03-improve-bcache-utils'
This commit is contained in:
commit
49db9b5e0b
@ -29,6 +29,7 @@ SOURCES =\
|
||||
datastruct/btree.c \
|
||||
datastruct/str_list.c \
|
||||
device/bcache.c \
|
||||
device/bcache-utils.c \
|
||||
device/dev-cache.c \
|
||||
device/dev-ext.c \
|
||||
device/dev-io.c \
|
||||
|
229
lib/device/bcache-utils.c
Normal file
229
lib/device/bcache-utils.c
Normal file
@ -0,0 +1,229 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "bcache.h"
|
||||
|
||||
// FIXME: need to define this in a common place (that doesn't pull in deps)
|
||||
#ifndef SECTOR_SHIFT
|
||||
#define SECTOR_SHIFT 9
|
||||
#endif
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
static void byte_range_to_block_range(struct bcache *cache, uint64_t start, size_t len,
|
||||
block_address *bb, block_address *be)
|
||||
{
|
||||
block_address block_size = bcache_block_sectors(cache) << SECTOR_SHIFT;
|
||||
*bb = start / block_size;
|
||||
*be = (start + len + block_size - 1) / block_size;
|
||||
}
|
||||
|
||||
static uint64_t _min(uint64_t lhs, uint64_t rhs)
|
||||
{
|
||||
if (rhs < lhs)
|
||||
return rhs;
|
||||
|
||||
return lhs;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
void bcache_prefetch_bytes(struct bcache *cache, int fd, uint64_t start, size_t len)
|
||||
{
|
||||
block_address bb, be;
|
||||
|
||||
byte_range_to_block_range(cache, start, len, &bb, &be);
|
||||
while (bb < be) {
|
||||
bcache_prefetch(cache, fd, bb);
|
||||
bb++;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
bool bcache_read_bytes(struct bcache *cache, int fd, uint64_t start, size_t len, void *data)
|
||||
{
|
||||
struct block *b;
|
||||
block_address bb, be;
|
||||
uint64_t block_size = bcache_block_sectors(cache) << SECTOR_SHIFT;
|
||||
uint64_t block_offset = start % block_size;
|
||||
|
||||
bcache_prefetch_bytes(cache, fd, start, len);
|
||||
|
||||
byte_range_to_block_range(cache, start, len, &bb, &be);
|
||||
|
||||
for (; bb != be; bb++) {
|
||||
if (!bcache_get(cache, fd, bb, 0, &b, NULL))
|
||||
return false;
|
||||
|
||||
size_t blen = _min(block_size - block_offset, len);
|
||||
memcpy(data, ((unsigned char *) b->data) + block_offset, blen);
|
||||
bcache_put(b);
|
||||
|
||||
block_offset = 0;
|
||||
len -= blen;
|
||||
data = ((unsigned char *) data) + blen;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
// Writing bytes and zeroing bytes are very similar, so we factor out
|
||||
// this common code.
|
||||
|
||||
struct updater;
|
||||
|
||||
typedef bool (*partial_update_fn)(struct updater *u, int fd, block_address bb, uint64_t offset, size_t len);
|
||||
typedef bool (*whole_update_fn)(struct updater *u, int fd, block_address bb, block_address be);
|
||||
|
||||
struct updater {
|
||||
struct bcache *cache;
|
||||
partial_update_fn partial_fn;
|
||||
whole_update_fn whole_fn;
|
||||
void *data;
|
||||
};
|
||||
|
||||
static bool _update_bytes(struct updater *u, int fd, uint64_t start, size_t len)
|
||||
{
|
||||
struct bcache *cache = u->cache;
|
||||
block_address bb, be;
|
||||
uint64_t block_size = bcache_block_sectors(cache) << SECTOR_SHIFT;
|
||||
uint64_t block_offset = start % block_size;
|
||||
uint64_t nr_whole;
|
||||
|
||||
byte_range_to_block_range(cache, start, len, &bb, &be);
|
||||
|
||||
// If the last block is partial, we will require a read, so let's
|
||||
// prefetch it.
|
||||
if ((start + len) % block_size)
|
||||
bcache_prefetch(cache, fd, (start + len) / block_size);
|
||||
|
||||
// First block may be partial
|
||||
if (block_offset) {
|
||||
size_t blen = _min(block_size - block_offset, len);
|
||||
if (!u->partial_fn(u, fd, bb, block_offset, blen))
|
||||
return false;
|
||||
|
||||
len -= blen;
|
||||
if (!len)
|
||||
return true;
|
||||
|
||||
bb++;
|
||||
}
|
||||
|
||||
// Now we write out a set of whole blocks
|
||||
nr_whole = len / block_size;
|
||||
if (!u->whole_fn(u, fd, bb, bb + nr_whole))
|
||||
return false;
|
||||
|
||||
bb += nr_whole;
|
||||
len -= nr_whole * block_size;
|
||||
|
||||
if (!len)
|
||||
return true;
|
||||
|
||||
// Finally we write a partial end block
|
||||
return u->partial_fn(u, fd, bb, 0, len);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
static bool _write_partial(struct updater *u, int fd, block_address bb,
|
||||
uint64_t offset, size_t len)
|
||||
{
|
||||
struct block *b;
|
||||
|
||||
if (!bcache_get(u->cache, fd, bb, GF_DIRTY, &b, NULL))
|
||||
return false;
|
||||
|
||||
memcpy(((unsigned char *) b->data) + offset, u->data, len);
|
||||
u->data = ((unsigned char *) u->data) + len;
|
||||
|
||||
bcache_put(b);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _write_whole(struct updater *u, int fd, block_address bb, block_address be)
|
||||
{
|
||||
struct block *b;
|
||||
uint64_t block_size = bcache_block_sectors(u->cache) << SECTOR_SHIFT;
|
||||
|
||||
for (; bb != be; bb++) {
|
||||
// We don't need to read the block since we are overwriting
|
||||
// it completely.
|
||||
if (!bcache_get(u->cache, fd, bb, GF_ZERO, &b, NULL))
|
||||
return false;
|
||||
memcpy(b->data, u->data, block_size);
|
||||
u->data = ((unsigned char *) u->data) + block_size;
|
||||
bcache_put(b);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bcache_write_bytes(struct bcache *cache, int fd, uint64_t start, size_t len, void *data)
|
||||
{
|
||||
struct updater u;
|
||||
|
||||
u.cache = cache;
|
||||
u.partial_fn = _write_partial;
|
||||
u.whole_fn = _write_whole;
|
||||
u.data = data;
|
||||
|
||||
return _update_bytes(&u, fd, start, len);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
static bool _zero_partial(struct updater *u, int fd, block_address bb, uint64_t offset, size_t len)
|
||||
{
|
||||
struct block *b;
|
||||
|
||||
if (!bcache_get(u->cache, fd, bb, GF_DIRTY, &b, NULL))
|
||||
return false;
|
||||
|
||||
memset(((unsigned char *) b->data) + offset, 0, len);
|
||||
bcache_put(b);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _zero_whole(struct updater *u, int fd, block_address bb, block_address be)
|
||||
{
|
||||
struct block *b;
|
||||
|
||||
for (; bb != be; bb++) {
|
||||
if (!bcache_get(u->cache, fd, bb, GF_ZERO, &b, NULL))
|
||||
return false;
|
||||
bcache_put(b);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bcache_zero_bytes(struct bcache *cache, int fd, uint64_t start, size_t len)
|
||||
{
|
||||
struct updater u;
|
||||
|
||||
u.cache = cache;
|
||||
u.partial_fn = _zero_partial;
|
||||
u.whole_fn = _zero_whole;
|
||||
u.data = NULL;
|
||||
|
||||
return _update_bytes(&u, fd, start, len);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
@ -863,6 +863,11 @@ void bcache_destroy(struct bcache *cache)
|
||||
dm_free(cache);
|
||||
}
|
||||
|
||||
sector_t bcache_block_sectors(struct bcache *cache)
|
||||
{
|
||||
return cache->block_sectors;
|
||||
}
|
||||
|
||||
unsigned bcache_nr_cache_blocks(struct bcache *cache)
|
||||
{
|
||||
return cache->nr_cache_blocks;
|
||||
@ -1029,150 +1034,5 @@ bool bcache_invalidate_fd(struct bcache *cache, int fd)
|
||||
return r;
|
||||
}
|
||||
|
||||
static void byte_range_to_block_range(struct bcache *cache, off_t start, size_t len,
|
||||
block_address *bb, block_address *be)
|
||||
{
|
||||
block_address block_size = cache->block_sectors << SECTOR_SHIFT;
|
||||
*bb = start / block_size;
|
||||
*be = (start + len + block_size - 1) / block_size;
|
||||
}
|
||||
|
||||
void bcache_prefetch_bytes(struct bcache *cache, int fd, off_t start, size_t len)
|
||||
{
|
||||
block_address bb, be;
|
||||
|
||||
byte_range_to_block_range(cache, start, len, &bb, &be);
|
||||
while (bb < be) {
|
||||
bcache_prefetch(cache, fd, bb);
|
||||
bb++;
|
||||
}
|
||||
}
|
||||
|
||||
static off_t _min(off_t lhs, off_t rhs)
|
||||
{
|
||||
if (rhs < lhs)
|
||||
return rhs;
|
||||
|
||||
return lhs;
|
||||
}
|
||||
|
||||
// These functions are all utilities, they should only use the public
|
||||
// interface to bcache.
|
||||
// FIXME: there's common code that can be factored out of these 3
|
||||
bool bcache_read_bytes(struct bcache *cache, int fd, off_t start, size_t len, void *data)
|
||||
{
|
||||
struct block *b;
|
||||
block_address bb, be, i;
|
||||
unsigned char *udata = data;
|
||||
off_t block_size = cache->block_sectors << SECTOR_SHIFT;
|
||||
int errors = 0;
|
||||
|
||||
byte_range_to_block_range(cache, start, len, &bb, &be);
|
||||
for (i = bb; i < be; i++)
|
||||
bcache_prefetch(cache, fd, i);
|
||||
|
||||
for (i = bb; i < be; i++) {
|
||||
if (!bcache_get(cache, fd, i, 0, &b, NULL)) {
|
||||
log_error("bcache_read_bytes failed to get block %u fd %d bb %u be %u",
|
||||
(uint32_t)i, fd, (uint32_t)bb, (uint32_t)be);
|
||||
errors++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i == bb) {
|
||||
off_t block_offset = start % block_size;
|
||||
size_t blen = _min(block_size - block_offset, len);
|
||||
memcpy(udata, ((unsigned char *) b->data) + block_offset, blen);
|
||||
len -= blen;
|
||||
udata += blen;
|
||||
} else {
|
||||
size_t blen = _min(block_size, len);
|
||||
memcpy(udata, b->data, blen);
|
||||
len -= blen;
|
||||
udata += blen;
|
||||
}
|
||||
|
||||
bcache_put(b);
|
||||
}
|
||||
|
||||
return errors ? false : true;
|
||||
}
|
||||
|
||||
bool bcache_write_bytes(struct bcache *cache, int fd, off_t start, size_t len, void *data)
|
||||
{
|
||||
struct block *b;
|
||||
block_address bb, be, i;
|
||||
unsigned char *udata = data;
|
||||
off_t block_size = cache->block_sectors << SECTOR_SHIFT;
|
||||
int errors = 0;
|
||||
|
||||
byte_range_to_block_range(cache, start, len, &bb, &be);
|
||||
for (i = bb; i < be; i++)
|
||||
bcache_prefetch(cache, fd, i);
|
||||
|
||||
for (i = bb; i < be; i++) {
|
||||
if (!bcache_get(cache, fd, i, GF_DIRTY, &b, NULL)) {
|
||||
log_error("bcache_write_bytes failed to get block %u fd %d bb %u be %u",
|
||||
(uint32_t)i, fd, (uint32_t)bb, (uint32_t)be);
|
||||
errors++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i == bb) {
|
||||
off_t block_offset = start % block_size;
|
||||
size_t blen = _min(block_size - block_offset, len);
|
||||
memcpy(((unsigned char *) b->data) + block_offset, udata, blen);
|
||||
len -= blen;
|
||||
udata += blen;
|
||||
} else {
|
||||
size_t blen = _min(block_size, len);
|
||||
memcpy(b->data, udata, blen);
|
||||
len -= blen;
|
||||
udata += blen;
|
||||
}
|
||||
|
||||
bcache_put(b);
|
||||
}
|
||||
|
||||
return errors ? false : true;
|
||||
}
|
||||
|
||||
bool bcache_write_zeros(struct bcache *cache, int fd, off_t start, size_t len)
|
||||
{
|
||||
struct block *b;
|
||||
block_address bb, be, i;
|
||||
off_t block_size = cache->block_sectors << SECTOR_SHIFT;
|
||||
int errors = 0;
|
||||
|
||||
byte_range_to_block_range(cache, start, len, &bb, &be);
|
||||
for (i = bb; i < be; i++)
|
||||
bcache_prefetch(cache, fd, i);
|
||||
|
||||
for (i = bb; i < be; i++) {
|
||||
if (!bcache_get(cache, fd, i, GF_DIRTY, &b, NULL)) {
|
||||
log_error("bcache_write_bytes failed to get block %u fd %d bb %u be %u",
|
||||
(uint32_t)i, fd, (uint32_t)bb, (uint32_t)be);
|
||||
errors++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i == bb) {
|
||||
off_t block_offset = start % block_size;
|
||||
size_t blen = _min(block_size - block_offset, len);
|
||||
memset(((unsigned char *) b->data) + block_offset, 0, blen);
|
||||
len -= blen;
|
||||
} else {
|
||||
size_t blen = _min(block_size, len);
|
||||
memset(b->data, 0, blen);
|
||||
len -= blen;
|
||||
}
|
||||
|
||||
bcache_put(b);
|
||||
}
|
||||
|
||||
return errors ? false : true;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
|
@ -97,6 +97,7 @@ enum bcache_get_flags {
|
||||
GF_DIRTY = (1 << 1)
|
||||
};
|
||||
|
||||
sector_t bcache_block_sectors(struct bcache *cache);
|
||||
unsigned bcache_nr_cache_blocks(struct bcache *cache);
|
||||
unsigned bcache_max_prefetches(struct bcache *cache);
|
||||
|
||||
@ -150,18 +151,18 @@ bool bcache_invalidate(struct bcache *cache, int fd, block_address index);
|
||||
*/
|
||||
bool bcache_invalidate_fd(struct bcache *cache, int fd);
|
||||
|
||||
/*
|
||||
* Prefetches the blocks neccessary to satisfy a byte range.
|
||||
*/
|
||||
void bcache_prefetch_bytes(struct bcache *cache, int fd, off_t start, size_t len);
|
||||
|
||||
/*
|
||||
* Reads and writes the bytes. Returns false if errors occur.
|
||||
*/
|
||||
bool bcache_read_bytes(struct bcache *cache, int fd, off_t start, size_t len, void *data);
|
||||
bool bcache_write_bytes(struct bcache *cache, int fd, off_t start, size_t len, void *data);
|
||||
bool bcache_write_zeros(struct bcache *cache, int fd, off_t start, size_t len);
|
||||
//----------------------------------------------------------------
|
||||
// The next four functions are utilities written in terms of the above api.
|
||||
|
||||
// Prefetches the blocks neccessary to satisfy a byte range.
|
||||
void bcache_prefetch_bytes(struct bcache *cache, int fd, uint64_t start, size_t len);
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
// Reads, writes and zeroes bytes. Returns false if errors occur.
|
||||
bool bcache_read_bytes(struct bcache *cache, int fd, uint64_t start, size_t len, void *data);
|
||||
bool bcache_write_bytes(struct bcache *cache, int fd, uint64_t start, size_t len, void *data);
|
||||
bool bcache_zero_bytes(struct bcache *cache, int fd, uint64_t start, size_t len);
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
#endif
|
||||
|
@ -988,7 +988,7 @@ bool dev_write_zeros(struct device *dev, off_t start, size_t len)
|
||||
}
|
||||
}
|
||||
|
||||
if (!bcache_write_zeros(scan_bcache, dev->bcache_fd, start, len)) {
|
||||
if (!bcache_zero_bytes(scan_bcache, dev->bcache_fd, start, len)) {
|
||||
log_error("dev_write_zeros %s at %u bcache write failed invalidate fd %d",
|
||||
dev_name(dev), (uint32_t)start, dev->bcache_fd);
|
||||
label_scan_invalidate(dev);
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
UNIT_SOURCE=\
|
||||
test/unit/bcache_t.c \
|
||||
test/unit/bcache_utils_t.c \
|
||||
test/unit/bitset_t.c \
|
||||
test/unit/config_t.c \
|
||||
test/unit/dmlist_t.c \
|
||||
|
352
test/unit/bcache_utils_t.c
Normal file
352
test/unit/bcache_utils_t.c
Normal file
@ -0,0 +1,352 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU General Public License v.2.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "bcache.h"
|
||||
#include "framework.h"
|
||||
#include "units.h"
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
#define T_BLOCK_SIZE 4096
|
||||
#define NR_BLOCKS 64
|
||||
#define INIT_PATTERN 123
|
||||
|
||||
struct fixture {
|
||||
int fd;
|
||||
char fname[32];
|
||||
struct bcache *cache;
|
||||
};
|
||||
|
||||
static inline uint8_t _pattern_at(uint8_t pat, uint8_t byte)
|
||||
{
|
||||
return pat + byte;
|
||||
}
|
||||
|
||||
static uint64_t byte(block_address b, uint64_t offset)
|
||||
{
|
||||
return b * T_BLOCK_SIZE + offset;
|
||||
}
|
||||
|
||||
static void *_fix_init(void)
|
||||
{
|
||||
uint8_t buffer[T_BLOCK_SIZE];
|
||||
struct io_engine *engine;
|
||||
struct fixture *f = malloc(sizeof(*f));
|
||||
unsigned b, i;
|
||||
|
||||
T_ASSERT(f);
|
||||
|
||||
snprintf(f->fname, sizeof(f->fname), "unit-test-XXXXXX");
|
||||
f->fd = mkostemp(f->fname, O_RDWR | O_CREAT | O_EXCL);
|
||||
T_ASSERT(f->fd >= 0);
|
||||
|
||||
for (b = 0; b < NR_BLOCKS; b++) {
|
||||
for (i = 0; i < sizeof(buffer); i++)
|
||||
buffer[i] = _pattern_at(INIT_PATTERN, byte(b, i));
|
||||
T_ASSERT(write(f->fd, buffer, T_BLOCK_SIZE) > 0);
|
||||
}
|
||||
close(f->fd);
|
||||
|
||||
// reopen with O_DIRECT
|
||||
f->fd = open(f->fname, O_RDWR | O_DIRECT);
|
||||
T_ASSERT(f->fd >= 0);
|
||||
|
||||
engine = create_async_io_engine();
|
||||
T_ASSERT(engine);
|
||||
|
||||
f->cache = bcache_create(T_BLOCK_SIZE / 512, NR_BLOCKS, engine);
|
||||
T_ASSERT(f->cache);
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
static void _fix_exit(void *fixture)
|
||||
{
|
||||
struct fixture *f = fixture;
|
||||
|
||||
bcache_destroy(f->cache);
|
||||
close(f->fd);
|
||||
unlink(f->fname);
|
||||
free(f);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
static void _verify_bytes(struct block *b, uint64_t base,
|
||||
uint64_t offset, uint64_t len, uint8_t pat)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
T_ASSERT_EQUAL(((uint8_t *) b->data)[offset + i], _pattern_at(pat, base + offset + i));
|
||||
}
|
||||
|
||||
static void _zero_bytes(struct block *b, uint64_t offset, uint64_t len)
|
||||
{
|
||||
memset(((uint8_t *) b->data) + offset, 0, len);
|
||||
}
|
||||
|
||||
static uint64_t _min(uint64_t lhs, uint64_t rhs)
|
||||
{
|
||||
return rhs < lhs ? rhs : lhs;
|
||||
}
|
||||
|
||||
static void _verify(struct fixture *f, uint64_t byte_b, uint64_t byte_e, uint8_t pat)
|
||||
{
|
||||
int err;
|
||||
struct block *b;
|
||||
block_address bb = byte_b / T_BLOCK_SIZE;
|
||||
block_address be = (byte_e + T_BLOCK_SIZE - 1) / T_BLOCK_SIZE;
|
||||
uint64_t offset = byte_b % T_BLOCK_SIZE;
|
||||
uint64_t blen, len = byte_e - byte_b;
|
||||
|
||||
// Verify via bcache_read_bytes
|
||||
{
|
||||
unsigned i;
|
||||
size_t len2 = byte_e - byte_b;
|
||||
uint8_t *buffer = malloc(len2);
|
||||
T_ASSERT(bcache_read_bytes(f->cache, f->fd, byte_b, len2, buffer));
|
||||
for (i = 0; i < len; i++)
|
||||
T_ASSERT_EQUAL(buffer[i], _pattern_at(pat, byte_b + i));
|
||||
}
|
||||
|
||||
// Verify again, driving bcache directly
|
||||
for (; bb != be; bb++) {
|
||||
T_ASSERT(bcache_get(f->cache, f->fd, bb, 0, &b, &err));
|
||||
|
||||
blen = _min(T_BLOCK_SIZE - offset, len);
|
||||
_verify_bytes(b, bb * T_BLOCK_SIZE, offset, blen, pat);
|
||||
|
||||
offset = 0;
|
||||
len -= blen;
|
||||
|
||||
bcache_put(b);
|
||||
}
|
||||
}
|
||||
|
||||
static void _verify_zeroes(struct fixture *f, uint64_t byte_b, uint64_t byte_e)
|
||||
{
|
||||
int err;
|
||||
unsigned i;
|
||||
struct block *b;
|
||||
block_address bb = byte_b / T_BLOCK_SIZE;
|
||||
block_address be = (byte_e + T_BLOCK_SIZE - 1) / T_BLOCK_SIZE;
|
||||
uint64_t offset = byte_b % T_BLOCK_SIZE;
|
||||
uint64_t blen, len = byte_e - byte_b;
|
||||
|
||||
for (; bb != be; bb++) {
|
||||
T_ASSERT(bcache_get(f->cache, f->fd, bb, 0, &b, &err));
|
||||
|
||||
blen = _min(T_BLOCK_SIZE - offset, len);
|
||||
for (i = 0; i < blen; i++)
|
||||
T_ASSERT(((uint8_t *) b->data)[offset + i] == 0);
|
||||
|
||||
offset = 0;
|
||||
len -= blen;
|
||||
|
||||
bcache_put(b);
|
||||
}
|
||||
}
|
||||
|
||||
static void _do_write(struct fixture *f, uint64_t byte_b, uint64_t byte_e, uint8_t pat)
|
||||
{
|
||||
unsigned i;
|
||||
size_t len = byte_e - byte_b;
|
||||
uint8_t *buffer = malloc(len);
|
||||
T_ASSERT(buffer);
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
buffer[i] = _pattern_at(pat, byte_b + i);
|
||||
|
||||
T_ASSERT(bcache_write_bytes(f->cache, f->fd, byte_b, i, buffer));
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
static void _do_zero(struct fixture *f, uint64_t byte_b, uint64_t byte_e)
|
||||
{
|
||||
int err;
|
||||
struct block *b;
|
||||
block_address bb = byte_b / T_BLOCK_SIZE;
|
||||
block_address be = (byte_e + T_BLOCK_SIZE - 1) / T_BLOCK_SIZE;
|
||||
uint64_t offset = byte_b % T_BLOCK_SIZE;
|
||||
uint64_t blen, len = byte_e - byte_b;
|
||||
|
||||
for (; bb != be; bb++) {
|
||||
T_ASSERT(bcache_get(f->cache, f->fd, bb, GF_DIRTY, &b, &err));
|
||||
|
||||
blen = _min(T_BLOCK_SIZE - offset, len);
|
||||
_zero_bytes(b, offset, blen);
|
||||
|
||||
offset = 0;
|
||||
len -= blen;
|
||||
|
||||
bcache_put(b);
|
||||
}
|
||||
}
|
||||
|
||||
static void _reopen(struct fixture *f)
|
||||
{
|
||||
struct io_engine *engine;
|
||||
|
||||
bcache_destroy(f->cache);
|
||||
engine = create_async_io_engine();
|
||||
T_ASSERT(engine);
|
||||
|
||||
f->cache = bcache_create(T_BLOCK_SIZE / 512, NR_BLOCKS, engine);
|
||||
T_ASSERT(f->cache);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
static uint8_t _random_pattern(void)
|
||||
{
|
||||
return random();
|
||||
}
|
||||
|
||||
static uint64_t _max_byte(void)
|
||||
{
|
||||
return T_BLOCK_SIZE * NR_BLOCKS;
|
||||
}
|
||||
|
||||
static void _rwv_cycle(struct fixture *f, uint64_t b, uint64_t e)
|
||||
{
|
||||
uint8_t pat = _random_pattern();
|
||||
|
||||
_verify(f, b, e, INIT_PATTERN);
|
||||
_do_write(f, b, e, pat);
|
||||
_reopen(f);
|
||||
_verify(f, b < 128 ? 0 : b - 128, b, INIT_PATTERN);
|
||||
_verify(f, b, e, pat);
|
||||
_verify(f, e, _min(e + 128, _max_byte()), INIT_PATTERN);
|
||||
}
|
||||
|
||||
static void _test_rw_first_block(void *fixture)
|
||||
{
|
||||
_rwv_cycle(fixture, byte(0, 0), byte(0, T_BLOCK_SIZE));
|
||||
}
|
||||
|
||||
static void _test_rw_last_block(void *fixture)
|
||||
{
|
||||
uint64_t last_block = NR_BLOCKS - 1;
|
||||
_rwv_cycle(fixture, byte(last_block, 0),
|
||||
byte(last_block, T_BLOCK_SIZE));
|
||||
}
|
||||
|
||||
static void _test_rw_several_whole_blocks(void *fixture)
|
||||
{
|
||||
_rwv_cycle(fixture, byte(5, 0), byte(10, 0));
|
||||
}
|
||||
|
||||
static void _test_rw_within_single_block(void *fixture)
|
||||
{
|
||||
_rwv_cycle(fixture, byte(7, 3), byte(7, T_BLOCK_SIZE / 2));
|
||||
}
|
||||
|
||||
static void _test_rw_cross_one_boundary(void *fixture)
|
||||
{
|
||||
_rwv_cycle(fixture, byte(13, 43), byte(14, 43));
|
||||
}
|
||||
|
||||
static void _test_rw_many_boundaries(void *fixture)
|
||||
{
|
||||
_rwv_cycle(fixture, byte(13, 13), byte(23, 13));
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
static void _zero_cycle(struct fixture *f, uint64_t b, uint64_t e)
|
||||
{
|
||||
_verify(f, b, e, INIT_PATTERN);
|
||||
_do_zero(f, b, e);
|
||||
_reopen(f);
|
||||
_verify(f, b < 128 ? 0 : b - 128, b, INIT_PATTERN);
|
||||
_verify_zeroes(f, b, e);
|
||||
_verify(f, e, _min(e + 128, _max_byte()), INIT_PATTERN);
|
||||
}
|
||||
|
||||
static void _test_zero_first_block(void *fixture)
|
||||
{
|
||||
_zero_cycle(fixture, byte(0, 0), byte(0, T_BLOCK_SIZE));
|
||||
}
|
||||
|
||||
static void _test_zero_last_block(void *fixture)
|
||||
{
|
||||
uint64_t last_block = NR_BLOCKS - 1;
|
||||
_zero_cycle(fixture, byte(last_block, 0), byte(last_block, T_BLOCK_SIZE));
|
||||
}
|
||||
|
||||
static void _test_zero_several_whole_blocks(void *fixture)
|
||||
{
|
||||
_zero_cycle(fixture, byte(5, 0), byte(10, 0));
|
||||
}
|
||||
|
||||
static void _test_zero_within_single_block(void *fixture)
|
||||
{
|
||||
_zero_cycle(fixture, byte(7, 3), byte(7, T_BLOCK_SIZE / 2));
|
||||
}
|
||||
|
||||
static void _test_zero_cross_one_boundary(void *fixture)
|
||||
{
|
||||
_zero_cycle(fixture, byte(13, 43), byte(14, 43));
|
||||
}
|
||||
|
||||
static void _test_zero_many_boundaries(void *fixture)
|
||||
{
|
||||
_zero_cycle(fixture, byte(13, 13), byte(23, 13));
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
#define T(path, desc, fn) register_test(ts, "/base/device/bcache/utils/" path, desc, fn)
|
||||
|
||||
static struct test_suite *_tests(void)
|
||||
{
|
||||
struct test_suite *ts = test_suite_create(_fix_init, _fix_exit);
|
||||
if (!ts) {
|
||||
fprintf(stderr, "out of memory\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
T("rw-first-block", "read/write/verify the first block", _test_rw_first_block);
|
||||
T("rw-last-block", "read/write/verify the last block", _test_rw_last_block);
|
||||
T("rw-several-blocks", "read/write/verify several whole blocks", _test_rw_several_whole_blocks);
|
||||
T("rw-within-single-block", "read/write/verify within single block", _test_rw_within_single_block);
|
||||
T("rw-cross-one-boundary", "read/write/verify across one boundary", _test_rw_cross_one_boundary);
|
||||
T("rw-many-boundaries", "read/write/verify many boundaries", _test_rw_many_boundaries);
|
||||
|
||||
T("zero-first-block", "zero the first block", _test_zero_first_block);
|
||||
T("zero-last-block", "zero the last block", _test_zero_last_block);
|
||||
T("zero-several-blocks", "zero several whole blocks", _test_zero_several_whole_blocks);
|
||||
T("zero-within-single-block", "zero within single block", _test_zero_within_single_block);
|
||||
T("zero-cross-one-boundary", "zero across one boundary", _test_zero_cross_one_boundary);
|
||||
T("zero-many-boundaries", "zero many boundaries", _test_zero_many_boundaries);
|
||||
|
||||
return ts;
|
||||
}
|
||||
|
||||
void bcache_utils_tests(struct dm_list *all_tests)
|
||||
{
|
||||
dm_list_add(all_tests, &_tests()->list);
|
||||
}
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
// Declare the function that adds tests suites here ...
|
||||
void bcache_tests(struct dm_list *suites);
|
||||
void bcache_utils_tests(struct dm_list *suites);
|
||||
void bitset_tests(struct dm_list *suites);
|
||||
void config_tests(struct dm_list *suites);
|
||||
void dm_list_tests(struct dm_list *suites);
|
||||
@ -34,6 +35,7 @@ void string_tests(struct dm_list *suites);
|
||||
static inline void register_all_tests(struct dm_list *suites)
|
||||
{
|
||||
bcache_tests(suites);
|
||||
bcache_utils_tests(suites);
|
||||
bitset_tests(suites);
|
||||
config_tests(suites);
|
||||
dm_list_tests(suites);
|
||||
|
Loading…
Reference in New Issue
Block a user