master - scan: check for errors in text layer
by David Teigland
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=196579af1f7ebe25c02...
Commit: 196579af1f7ebe25c0266043c7d966b344699d5d
Parent: 44726ed9cb9559801868ade5440d9e6ea6a61127
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Thu Mar 1 12:42:18 2018 -0600
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Fri Apr 20 11:22:47 2018 -0500
scan: check for errors in text layer
The scanning code in the format_text layer
has previously ignored errors. Start checking
for and returning them.
---
lib/format_text/format-text.c | 5 ++++-
lib/format_text/text_label.c | 38 ++++++++++++++++++++++----------------
2 files changed, 26 insertions(+), 17 deletions(-)
diff --git a/lib/format_text/format-text.c b/lib/format_text/format-text.c
index 4a9c303..4146e7c 100644
--- a/lib/format_text/format-text.c
+++ b/lib/format_text/format-text.c
@@ -536,8 +536,10 @@ static struct volume_group *_vg_read_raw_area(struct format_instance *fid,
char *desc;
uint32_t wrap = 0;
- if (!(mdah = raw_read_mda_header(fid->fmt, area, primary_mda)))
+ if (!(mdah = raw_read_mda_header(fid->fmt, area, primary_mda))) {
+ log_error("Failed to read vg %s from %s", vgname, dev_name(area->dev));
goto_out;
+ }
if (!(rlocn = _read_metadata_location_vg(area, mdah, primary_mda, vgname, &precommitted))) {
log_debug_metadata("VG %s not found on %s", vgname, dev_name(area->dev));
@@ -1213,6 +1215,7 @@ int read_metadata_location_summary(const struct format_type *fmt,
log_debug_metadata("Metadata location on %s at %llu has offset 0.",
dev_name(dev_area->dev),
(unsigned long long)(dev_area->start + rlocn->offset));
+ vgsummary->zero_offset = 1;
return 0;
}
diff --git a/lib/format_text/text_label.c b/lib/format_text/text_label.c
index 206ae3f..e65079e 100644
--- a/lib/format_text/text_label.c
+++ b/lib/format_text/text_label.c
@@ -331,16 +331,9 @@ static int _read_mda_header_and_metadata(struct metadata_area *mda, void *baton)
struct mda_header *mdah;
struct lvmcache_vgsummary vgsummary = { 0 };
- /*
- * Using the labeller struct to preserve info about
- * the last parsed vgname, vgid, creation host
- *
- * TODO: make lvmcache smarter and move this cache logic there
- */
-
if (!(mdah = raw_read_mda_header(fmt, &mdac->area, mda_is_primary(mda)))) {
- stack;
- goto close_dev;
+ log_error("Failed to read mda header from %s", dev_name(mdac->area.dev));
+ goto fail;
}
mda_set_ignored(mda, rlocn_is_ignored(mdah->raw_locns));
@@ -352,14 +345,25 @@ static int _read_mda_header_and_metadata(struct metadata_area *mda, void *baton)
return 1;
}
- if (read_metadata_location_summary(fmt, mdah, mda_is_primary(mda), &mdac->area, &vgsummary,
- &mdac->free_sectors) &&
- !lvmcache_update_vgname_and_id(p->info, &vgsummary)) {
- return_0;
+ if (!read_metadata_location_summary(fmt, mdah, mda_is_primary(mda), &mdac->area,
+ &vgsummary, &mdac->free_sectors)) {
+ if (vgsummary.zero_offset)
+ return 1;
+
+ log_error("Failed to read metadata summary from %s", dev_name(mdac->area.dev));
+ goto fail;
+ }
+
+ if (!lvmcache_update_vgname_and_id(p->info, &vgsummary)) {
+ log_error("Failed to save lvm summary for %s", dev_name(mdac->area.dev));
+ goto fail;
}
-close_dev:
return 1;
+
+fail:
+ lvmcache_del(p->info);
+ return 0;
}
static int _text_read(struct labeller *l, struct device *dev, void *label_buf,
@@ -434,10 +438,12 @@ out:
baton.info = info;
baton.label = *label;
- lvmcache_foreach_mda(info, _read_mda_header_and_metadata, &baton);
+ if (!lvmcache_foreach_mda(info, _read_mda_header_and_metadata, &baton)) {
+ log_error("Failed to scan VG from %s", dev_name(dev));
+ return 0;
+ }
lvmcache_make_valid(info);
-
return 1;
}
6 years
master - [device/bcache] add bcache_prefetch_bytes() and bcache_read_bytes()
by David Teigland
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=6a57ed17a28aee0e8fb...
Commit: 6a57ed17a28aee0e8fb9557ec3c03a02f0b2a4be
Parent: 467adfa082c3be10d012fa156db7810d23221648
Author: Joe Thornber <ejt(a)redhat.com>
AuthorDate: Mon Feb 5 16:56:56 2018 +0000
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Fri Apr 20 11:12:50 2018 -0500
[device/bcache] add bcache_prefetch_bytes() and bcache_read_bytes()
Not tested yet.
---
lib/device/bcache.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++
lib/device/bcache.h | 10 ++++++++
2 files changed, 69 insertions(+), 0 deletions(-)
diff --git a/lib/device/bcache.c b/lib/device/bcache.c
index 86b56c0..1d83306 100644
--- a/lib/device/bcache.c
+++ b/lib/device/bcache.c
@@ -966,5 +966,64 @@ void bcache_invalidate_fd(struct bcache *cache, int fd)
_recycle_block(cache, b);
}
+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;
+}
+
+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;
+
+ 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))
+ return false;
+
+ 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;
+ }
+ }
+
+ return true;
+}
+
//----------------------------------------------------------------
diff --git a/lib/device/bcache.h b/lib/device/bcache.h
index 818dee2..7d38d33 100644
--- a/lib/device/bcache.h
+++ b/lib/device/bcache.h
@@ -137,6 +137,16 @@ void bcache_invalidate(struct bcache *cache, int fd, block_address index);
*/
void 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 the bytes.
+ */
+bool bcache_read_bytes(struct bcache *cache, int fd, off_t start, size_t len, void *data);
+
/*----------------------------------------------------------------*/
#endif
6 years
master - [device/bcache] More tests and some bug fixes
by David Teigland
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=467adfa082c3be10d01...
Commit: 467adfa082c3be10d012fa156db7810d23221648
Parent: 8ae3b244fcbc207b51a81514e51008fe64d13368
Author: Joe Thornber <ejt(a)redhat.com>
AuthorDate: Mon Feb 5 16:04:23 2018 +0000
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Fri Apr 20 11:12:50 2018 -0500
[device/bcache] More tests and some bug fixes
---
lib/device/bcache.c | 127 ++++++-----
lib/device/bcache.h | 32 +++-
test/unit/Makefile.in | 26 ++-
test/unit/bcache_t.c | 589 +++++++++++++++++++++++++++++++++++++++++--------
4 files changed, 616 insertions(+), 158 deletions(-)
diff --git a/lib/device/bcache.c b/lib/device/bcache.c
index e5d0e1b..86b56c0 100644
--- a/lib/device/bcache.c
+++ b/lib/device/bcache.c
@@ -130,44 +130,21 @@ static struct control_block *_iocb_to_cb(struct iocb *icb)
//----------------------------------------------------------------
// FIXME: write a sync engine too
-enum dir {
- DIR_READ,
- DIR_WRITE
-};
-
-struct io_engine {
+struct async_engine {
+ struct io_engine e;
io_context_t aio_context;
struct cb_set *cbs;
};
-static struct io_engine *_engine_create(unsigned max_io)
+static struct async_engine *_to_async(struct io_engine *e)
{
- int r;
- struct io_engine *e = dm_malloc(sizeof(*e));
-
- if (!e)
- return NULL;
-
- e->aio_context = 0;
- r = io_setup(max_io, &e->aio_context);
- if (r < 0) {
- log_warn("io_setup failed");
- return NULL;
- }
-
- e->cbs = _cb_set_create(max_io);
- if (!e->cbs) {
- log_warn("couldn't create control block set");
- dm_free(e);
- return NULL;
- }
-
- return e;
+ return container_of(e, struct async_engine, e);
}
-static void _engine_destroy(struct io_engine *e)
+static void _async_destroy(struct io_engine *ioe)
{
int r;
+ struct async_engine *e = _to_async(ioe);
_cb_set_destroy(e->cbs);
@@ -179,12 +156,13 @@ static void _engine_destroy(struct io_engine *e)
dm_free(e);
}
-static bool _engine_issue(struct io_engine *e, enum dir d, int fd,
- sector_t sb, sector_t se, void *data, void *context)
+static bool _async_issue(struct io_engine *ioe, enum dir d, int fd,
+ sector_t sb, sector_t se, void *data, void *context)
{
int r;
struct iocb *cb_array[1];
struct control_block *cb;
+ struct async_engine *e = _to_async(ioe);
if (((uint64_t) data) & (PAGE_SIZE - 1)) {
log_warn("misaligned data buffer");
@@ -218,13 +196,13 @@ static bool _engine_issue(struct io_engine *e, enum dir d, int fd,
#define MAX_IO 1024
#define MAX_EVENT 64
-typedef void complete_fn(void *context, int io_error);
-static bool _engine_wait(struct io_engine *e, complete_fn fn)
+static bool _async_wait(struct io_engine *ioe, io_complete_fn fn)
{
int i, r;
struct io_event event[MAX_EVENT];
struct control_block *cb;
+ struct async_engine *e = _to_async(ioe);
memset(&event, 0, sizeof(event));
r = io_getevents(e->aio_context, 1, MAX_EVENT, event, NULL);
@@ -255,6 +233,36 @@ static bool _engine_wait(struct io_engine *e, complete_fn fn)
return true;
}
+struct io_engine *create_async_io_engine(unsigned max_io)
+{
+ int r;
+ struct async_engine *e = dm_malloc(sizeof(*e));
+
+ if (!e)
+ return NULL;
+
+ e->e.destroy = _async_destroy;
+ e->e.issue = _async_issue;
+ e->e.wait = _async_wait;
+
+ e->aio_context = 0;
+ r = io_setup(max_io, &e->aio_context);
+ if (r < 0) {
+ log_warn("io_setup failed");
+ dm_free(e);
+ return NULL;
+ }
+
+ e->cbs = _cb_set_create(max_io);
+ if (!e->cbs) {
+ log_warn("couldn't create control block set");
+ dm_free(e);
+ return NULL;
+ }
+
+ return &e->e;
+}
+
//----------------------------------------------------------------
#define MIN_BLOCKS 16
@@ -536,7 +544,9 @@ static bool _issue_low_level(struct block *b, enum dir d)
return false;
_set_flags(b, BF_IO_PENDING);
- if (!_engine_issue(cache->engine, d, b->fd, sb, se, b->data, b)) {
+ dm_list_add(&cache->io_pending, &b->list);
+
+ if (!cache->engine->issue(cache->engine, d, b->fd, sb, se, b->data, b)) {
_complete_io(b, -EIO);
return false;
}
@@ -557,7 +567,7 @@ static inline bool _issue_write(struct block *b)
static bool _wait_io(struct bcache *cache)
{
- return _engine_wait(cache->engine, _complete_io);
+ return cache->engine->wait(cache->engine, _complete_io);
}
/*----------------------------------------------------------------
@@ -614,17 +624,20 @@ static struct block *_find_unused_clean_block(struct bcache *cache)
return NULL;
}
-static struct block *_new_block(struct bcache *cache, int fd, block_address index)
+static struct block *_new_block(struct bcache *cache, int fd, block_address index, bool can_wait)
{
struct block *b;
b = _alloc_block(cache);
- while (!b && cache->nr_locked < cache->nr_cache_blocks) {
+ while (!b && !dm_list_empty(&cache->clean)) {
b = _find_unused_clean_block(cache);
if (!b) {
- if (dm_list_empty(&cache->io_pending))
- _writeback(cache, 16);
- _wait_io(cache);
+ if (can_wait) {
+ if (dm_list_empty(&cache->io_pending))
+ _writeback(cache, 16); // FIXME: magic number
+ _wait_io(cache);
+ } else
+ return NULL;
}
}
@@ -702,7 +715,7 @@ static struct block *_lookup_or_read_block(struct bcache *cache,
} else {
_miss(cache, flags);
- b = _new_block(cache, fd, index);
+ b = _new_block(cache, fd, index, true);
if (b) {
if (flags & GF_ZERO)
_zero_block(b);
@@ -741,9 +754,11 @@ static void _preemptive_writeback(struct bcache *cache)
/*----------------------------------------------------------------
* Public interface
*--------------------------------------------------------------*/
-struct bcache *bcache_create(sector_t block_sectors, unsigned nr_cache_blocks)
+struct bcache *bcache_create(sector_t block_sectors, unsigned nr_cache_blocks,
+ struct io_engine *engine)
{
struct bcache *cache;
+ unsigned max_io = engine->max_io(engine);
if (!nr_cache_blocks) {
log_warn("bcache must have at least one cache block");
@@ -766,13 +781,8 @@ struct bcache *bcache_create(sector_t block_sectors, unsigned nr_cache_blocks)
cache->block_sectors = block_sectors;
cache->nr_cache_blocks = nr_cache_blocks;
- cache->max_io = nr_cache_blocks < MAX_IO ? nr_cache_blocks : MAX_IO;
- cache->engine = _engine_create(cache->max_io);
- if (!cache->engine) {
- dm_free(cache);
- return NULL;
- }
-
+ cache->max_io = nr_cache_blocks < max_io ? nr_cache_blocks : max_io;
+ cache->engine = engine;
cache->nr_locked = 0;
cache->nr_dirty = 0;
cache->nr_io_pending = 0;
@@ -784,7 +794,7 @@ struct bcache *bcache_create(sector_t block_sectors, unsigned nr_cache_blocks)
dm_list_init(&cache->io_pending);
if (!_hash_table_init(cache, nr_cache_blocks)) {
- _engine_destroy(cache->engine);
+ cache->engine->destroy(cache->engine);
dm_free(cache);
return NULL;
}
@@ -797,7 +807,7 @@ struct bcache *bcache_create(sector_t block_sectors, unsigned nr_cache_blocks)
cache->prefetches = 0;
if (!_init_free_list(cache, nr_cache_blocks)) {
- _engine_destroy(cache->engine);
+ cache->engine->destroy(cache->engine);
_hash_table_exit(cache);
dm_free(cache);
return NULL;
@@ -815,7 +825,7 @@ void bcache_destroy(struct bcache *cache)
_wait_all(cache);
_exit_free_list(cache);
_hash_table_exit(cache);
- _engine_destroy(cache->engine);
+ cache->engine->destroy(cache->engine);
dm_free(cache);
}
@@ -834,10 +844,12 @@ void bcache_prefetch(struct bcache *cache, int fd, block_address index)
struct block *b = _hash_lookup(cache, fd, index);
if (!b) {
- b = _new_block(cache, fd, index);
- if (b && (cache->nr_io_pending < cache->max_io)) {
- cache->prefetches++;
- _issue_read(b);
+ if (cache->nr_io_pending < cache->max_io) {
+ b = _new_block(cache, fd, index, false);
+ if (b) {
+ cache->prefetches++;
+ _issue_read(b);
+ }
}
}
}
@@ -881,9 +893,10 @@ int bcache_flush(struct bcache *cache)
{
while (!dm_list_empty(&cache->dirty)) {
struct block *b = dm_list_item(_list_pop(&cache->dirty), struct block);
- if (b->ref_count || _test_flags(b, BF_IO_PENDING))
+ if (b->ref_count || _test_flags(b, BF_IO_PENDING)) {
// The superblock may well be still locked.
continue;
+ }
_issue_write(b);
}
diff --git a/lib/device/bcache.h b/lib/device/bcache.h
index 14204be..818dee2 100644
--- a/lib/device/bcache.h
+++ b/lib/device/bcache.h
@@ -15,6 +15,7 @@
#ifndef BCACHE_H
#define BCACHE_H
+#include <linux/fs.h>
#include <stdint.h>
#include <stdbool.h>
@@ -22,9 +23,34 @@
/*----------------------------------------------------------------*/
+// FIXME: move somewhere more sensible
+#define container_of(v, t, head) \
+ ((t *)((const char *)(v) - (const char *)&((t *) 0)->head))
+
+/*----------------------------------------------------------------*/
+
+enum dir {
+ DIR_READ,
+ DIR_WRITE
+};
+
typedef uint64_t block_address;
typedef uint64_t sector_t;
+typedef void io_complete_fn(void *context, int io_error);
+
+struct io_engine {
+ void (*destroy)(struct io_engine *e);
+ bool (*issue)(struct io_engine *e, enum dir d, int fd,
+ sector_t sb, sector_t se, void *data, void *context);
+ bool (*wait)(struct io_engine *e, io_complete_fn fn);
+ unsigned (*max_io)(struct io_engine *e);
+};
+
+struct io_engine *create_async_io_engine(unsigned max_io);
+
+/*----------------------------------------------------------------*/
+
struct bcache;
struct block {
/* clients may only access these three fields */
@@ -41,7 +67,11 @@ struct block {
int error;
};
-struct bcache *bcache_create(sector_t block_size, unsigned nr_cache_blocks);
+/*
+ * Ownership of engine passes. Engine will be destroyed even if this fails.
+ */
+struct bcache *bcache_create(sector_t block_size, unsigned nr_cache_blocks,
+ struct io_engine *engine);
void bcache_destroy(struct bcache *cache);
enum bcache_get_flags {
diff --git a/test/unit/Makefile.in b/test/unit/Makefile.in
index 2e2c819..a070329 100644
--- a/test/unit/Makefile.in
+++ b/test/unit/Makefile.in
@@ -12,22 +12,28 @@
UNIT_SOURCE=\
test/unit/bcache_t.c \
- test/unit/bitset_t.c\
- test/unit/config_t.c\
- test/unit/dmlist_t.c\
- test/unit/dmstatus_t.c\
- test/unit/matcher_t.c\
- test/unit/percent_t.c\
- test/unit/string_t.c\
- test/unit/run.c
+
+
+# test/unit/run.c
+
+# test/unit/bitset_t.c\
+# test/unit/config_t.c\
+# test/unit/dmlist_t.c\
+# test/unit/dmstatus_t.c\
+# test/unit/matcher_t.c\
+# test/unit/percent_t.c\
+# test/unit/string_t.c\
+
UNIT_OBJECTS=$(UNIT_SOURCE:%.c=%.o)
-UNIT_LDLIBS += $(LVMINTERNAL_LIBS) -ldevmapper -laio -lcunit
+UNIT_LDLIBS += $(LVMINTERNAL_LIBS) -ldevmapper -laio
test/unit/run: $(UNIT_OBJECTS) libdm/libdevmapper.$(LIB_SUFFIX) lib/liblvm-internal.a
- $(CC) $(CFLAGS) $(LDFLAGS) $(EXTRA_EXEC_LDFLAGS) -L$(top_builddir)/libdm \
+ @echo " [LD] $@"
+ $(Q) $(CC) $(CFLAGS) $(LDFLAGS) $(EXTRA_EXEC_LDFLAGS) -L$(top_builddir)/libdm \
-o $@ $(UNIT_OBJECTS) $(UNIT_LDLIBS)
+.PHONEY: unit-test
unit-test: test/unit/run
@echo Running unit tests
LD_LIBRARY_PATH=libdm test/unit/run
diff --git a/test/unit/bcache_t.c b/test/unit/bcache_t.c
index 3db9cc7..c2d2df0 100644
--- a/test/unit/bcache_t.c
+++ b/test/unit/bcache_t.c
@@ -12,168 +12,540 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#define _GNU_SOURCE
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <unistd.h>
+#include <setjmp.h>
-#include "units.h"
#include "bcache.h"
-#define MEG 2048
-#define SECTOR_SHIFT 9
+#define SHOW_MOCK_CALLS 0
+
+/*----------------------------------------------------------------
+ * Assertions
+ *--------------------------------------------------------------*/
+
+static jmp_buf _test_k;
+#define TEST_FAILED 1
+
+static void _fail(const char *fmt, ...)
+ __attribute__((format (printf, 1, 2)));
-static const char *_test_path = "test.bin";
-int bcache_init(void)
+static void _fail(const char *fmt, ...)
{
- return 0;
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ fprintf(stderr, "\n");
+
+ longjmp(_test_k, TEST_FAILED);
}
-int bcache_fini(void)
+#define T_ASSERT(e) if (!(e)) {_fail("assertion failed: '%s'", # e);}
+
+/*----------------------------------------------------------------
+ * Mock engine
+ *--------------------------------------------------------------*/
+struct mock_engine {
+ struct io_engine e;
+ struct dm_list expected_calls;
+ struct dm_list issued_io;
+ unsigned max_io;
+};
+
+enum method {
+ E_DESTROY,
+ E_ISSUE,
+ E_WAIT,
+ E_MAX_IO
+};
+
+struct mock_call {
+ struct dm_list list;
+ enum method m;
+};
+
+struct mock_io {
+ struct dm_list list;
+ int fd;
+ sector_t sb;
+ sector_t se;
+ void *data;
+ void *context;
+};
+
+static const char *_show_method(enum method m)
{
- return 0;
+ switch (m) {
+ case E_DESTROY:
+ return "destroy()";
+ case E_ISSUE:
+ return "issue()";
+ case E_WAIT:
+ return "wait()";
+ case E_MAX_IO:
+ return "max_io()";
+ }
+
+ return "<unknown>";
+}
+
+static void _expect(struct mock_engine *e, enum method m)
+{
+ struct mock_call *mc = malloc(sizeof(*mc));
+ mc->m = m;
+ dm_list_add(&e->expected_calls, &mc->list);
+}
+
+static void _expect_read(struct mock_engine *e)
+{
+ // FIXME: finish
+ _expect(e, E_ISSUE);
}
-static int open_file(const char *path)
+static void _expect_write(struct mock_engine *e)
{
- return open(path, O_EXCL | O_RDWR | O_DIRECT, 0666);
+ // FIXME: finish
+ _expect(e, E_ISSUE);
}
-static int _prep_file(const char *path)
+static void _match(struct mock_engine *e, enum method m)
{
- int fd, r;
+ struct mock_call *mc;
+
+ if (dm_list_empty(&e->expected_calls))
+ _fail("unexpected call to method %s\n", _show_method(m));
- fd = open(path, O_CREAT | O_TRUNC | O_EXCL | O_RDWR | O_DIRECT, 0666);
- if (fd < 0)
- return -1;
+ mc = dm_list_item(e->expected_calls.n, struct mock_call);
+ dm_list_del(&mc->list);
- r = fallocate(fd, FALLOC_FL_ZERO_RANGE, 0, (1 * MEG) << SECTOR_SHIFT);
- if (r) {
- close(fd);
- return -1;
+ if (mc->m != m)
+ _fail("expected %s, but got %s\n", _show_method(mc->m), _show_method(m));
+#if SHOW_MOCK_CALLS
+ else
+ fprintf(stderr, "%s called (expected)\n", _show_method(m));
+#endif
+
+ free(mc);
+}
+
+static void _no_outstanding_expectations(struct mock_engine *e)
+{
+ struct mock_call *mc;
+
+ if (!dm_list_empty(&e->expected_calls)) {
+ fprintf(stderr, "unsatisfied expectations:\n");
+ dm_list_iterate_items (mc, &e->expected_calls)
+ fprintf(stderr, " %s\n", _show_method(mc->m));
}
+ T_ASSERT(dm_list_empty(&e->expected_calls));
+}
- close(fd);
- return 0;
+static struct mock_engine *_to_mock(struct io_engine *e)
+{
+ return container_of(e, struct mock_engine, e);
}
+static void _mock_destroy(struct io_engine *e)
+{
+ struct mock_engine *me = _to_mock(e);
+
+ _match(me, E_DESTROY);
+ T_ASSERT(dm_list_empty(&me->issued_io));
+ T_ASSERT(dm_list_empty(&me->expected_calls));
+ free(_to_mock(e));
+}
-static int test_init(void)
+static bool _mock_issue(struct io_engine *e, enum dir d, int fd,
+ sector_t sb, sector_t se, void *data, void *context)
{
- unlink(_test_path);
- return _prep_file(_test_path);
+ struct mock_io *io;
+ struct mock_engine *me = _to_mock(e);
+
+ _match(me, E_ISSUE);
+ io = malloc(sizeof(*io));
+ if (!io)
+ abort();
+
+ io->fd = fd;
+ io->sb = sb;
+ io->se = se;
+ io->data = data;
+ io->context = context;
+
+ dm_list_add(&me->issued_io, &io->list);
+ return true;
}
-static int test_exit(void)
+static bool _mock_wait(struct io_engine *e, io_complete_fn fn)
{
- unlink(_test_path);
- return 0;
+ struct mock_io *io;
+ struct mock_engine *me = _to_mock(e);
+ _match(me, E_WAIT);
+
+ // FIXME: provide a way to control how many are completed and whether
+ // they error.
+ T_ASSERT(!dm_list_empty(&me->issued_io));
+ io = dm_list_item(me->issued_io.n, struct mock_io);
+ dm_list_del(&io->list);
+ fn(io->context, 0);
+ return true;
}
-static void test_create(void)
+static unsigned _mock_max_io(struct io_engine *e)
+{
+ struct mock_engine *me = _to_mock(e);
+ _match(me, E_MAX_IO);
+ return me->max_io;
+}
+
+static struct mock_engine *_mock_create(unsigned max_io)
+{
+ struct mock_engine *m = malloc(sizeof(*m));
+
+ m->e.destroy = _mock_destroy;
+ m->e.issue = _mock_issue;
+ m->e.wait = _mock_wait;
+ m->e.max_io = _mock_max_io;
+
+ m->max_io = max_io;
+ dm_list_init(&m->expected_calls);
+ dm_list_init(&m->issued_io);
+
+ return m;
+}
+
+/*----------------------------------------------------------------
+ * Tests
+ *--------------------------------------------------------------*/
+#define MEG 2048
+#define SECTOR_SHIFT 9
+
+static void good_create(sector_t block_size, unsigned nr_cache_blocks)
{
- struct bcache *cache = bcache_create(8, 16);
- CU_ASSERT_PTR_NOT_NULL(cache);
+ struct bcache *cache;
+ struct mock_engine *me = _mock_create(16);
+
+ _expect(me, E_MAX_IO);
+ cache = bcache_create(block_size, nr_cache_blocks, &me->e);
+ T_ASSERT(cache);
+
+ _expect(me, E_DESTROY);
bcache_destroy(cache);
}
+static void bad_create(sector_t block_size, unsigned nr_cache_blocks)
+{
+ struct bcache *cache;
+ struct mock_engine *me = _mock_create(16);
+
+ _expect(me, E_MAX_IO);
+ cache = bcache_create(block_size, nr_cache_blocks, &me->e);
+ T_ASSERT(!cache);
+
+ _expect(me, E_DESTROY);
+ me->e.destroy(&me->e);
+}
+
+static void test_create(void)
+{
+ good_create(8, 16);
+}
+
static void test_nr_cache_blocks_must_be_positive(void)
{
- struct bcache *cache = bcache_create(8, 0);
- CU_ASSERT_PTR_NULL(cache);
+ bad_create(8, 0);
}
static void test_block_size_must_be_positive(void)
{
- struct bcache *cache = bcache_create(0, 16);
- CU_ASSERT_PTR_NULL(cache);
+ bad_create(0, 16);
}
static void test_block_size_must_be_multiple_of_page_size(void)
{
+ static unsigned _bad_examples[] = {3, 9, 13, 1025};
+
unsigned i;
+
+ for (i = 0; i < DM_ARRAY_SIZE(_bad_examples); i++)
+ bad_create(_bad_examples[i], 16);
+
+ for (i = 1; i < 1000; i++)
+ good_create(i * 8, 16);
+}
+
+static void test_get_triggers_read(void)
+{
struct bcache *cache;
+ struct mock_engine *me = _mock_create(16);
+
+ // FIXME: use a fixture
+ _expect(me, E_MAX_IO);
+ cache = bcache_create(64, 16, &me->e);
+ T_ASSERT(cache);
{
- static unsigned _bad_examples[] = {3, 9, 13, 1025};
+ int fd = 17; // arbitrary key
+ struct block *b;
- for (i = 0; i < DM_ARRAY_SIZE(_bad_examples); i++) {
- cache = bcache_create(_bad_examples[i], 16);
- CU_ASSERT_PTR_NULL(cache);
- }
+ _expect(me, E_ISSUE);
+ _expect(me, E_WAIT);
+ T_ASSERT(bcache_get(cache, fd, 0, 0, &b));
+ bcache_put(b);
}
+ _expect(me, E_DESTROY);
+ bcache_destroy(cache);
+}
+
+static void test_repeated_reads_are_cached(void)
+{
+ struct bcache *cache;
+ struct mock_engine *me = _mock_create(16);
+
+ // FIXME: use a fixture
+ _expect(me, E_MAX_IO);
+ cache = bcache_create(64, 16, &me->e);
+ T_ASSERT(cache);
+
{
- // Only testing a few sizes because io_destroy is seriously
- // slow.
- for (i = 1; i < 25; i++) {
- cache = bcache_create(8 * i, 16);
- CU_ASSERT_PTR_NOT_NULL(cache);
- bcache_destroy(cache);
+ int fd = 17; // arbitrary key
+ unsigned i;
+ struct block *b;
+
+ _expect(me, E_ISSUE);
+ _expect(me, E_WAIT);
+ for (i = 0; i < 100; i++) {
+ T_ASSERT(bcache_get(cache, fd, 0, 0, &b));
+ bcache_put(b);
}
}
+
+ _expect(me, E_DESTROY);
+ bcache_destroy(cache);
}
-static void test_reads_work(void)
+static void test_block_gets_evicted_with_many_reads(void)
{
- int fd;
+ const unsigned nr_cache_blocks = 16;
+ struct bcache *cache;
+ struct mock_engine *me = _mock_create(16);
- // FIXME: add fixtures.
- test_init();
- fd = open_file("./test.bin");
- CU_ASSERT(fd >= 0);
+ // FIXME: use a fixture
+ _expect(me, E_MAX_IO);
+ cache = bcache_create(64, nr_cache_blocks, &me->e);
+ T_ASSERT(cache);
{
- int i;
+ int fd = 17; // arbitrary key
+ unsigned i;
struct block *b;
- struct bcache *cache = bcache_create(8, 16);
- CU_ASSERT(bcache_get(cache, fd, 0, 0, &b));
- for (i = 0; i < 8 << SECTOR_SHIFT; i++)
- CU_ASSERT(((unsigned char *) b->data)[i] == 0);
+ for (i = 0; i < nr_cache_blocks; i++) {
+ _expect(me, E_ISSUE);
+ _expect(me, E_WAIT);
+ T_ASSERT(bcache_get(cache, fd, i, 0, &b));
+ bcache_put(b);
+ }
+
+ // Not enough cache blocks to hold this one
+ _expect(me, E_ISSUE);
+ _expect(me, E_WAIT);
+ T_ASSERT(bcache_get(cache, fd, nr_cache_blocks, 0, &b));
bcache_put(b);
- bcache_destroy(cache);
+ // Now if we run through we should find one block has been
+ // evicted. We go backwards because the oldest is normally
+ // evicted first.
+ _expect(me, E_ISSUE);
+ _expect(me, E_WAIT);
+ for (i = nr_cache_blocks; i; i--) {
+ T_ASSERT(bcache_get(cache, fd, i - 1, 0, &b));
+ bcache_put(b);
+ }
}
- close(fd);
-
- test_exit();
+ _expect(me, E_DESTROY);
+ bcache_destroy(cache);
}
-static void test_prefetch_works(void)
+static void test_prefetch_issues_a_read(void)
{
- int fd;
+ const unsigned nr_cache_blocks = 16;
+ struct bcache *cache;
+ struct mock_engine *me = _mock_create(16);
- // FIXME: add fixtures.
- test_init();
- fd = open_file("./test.bin");
- CU_ASSERT(fd >= 0);
+ // FIXME: use a fixture
+ _expect(me, E_MAX_IO);
+ cache = bcache_create(64, nr_cache_blocks, &me->e);
+ T_ASSERT(cache);
{
- int i;
+ int fd = 17; // arbitrary key
+ unsigned i;
struct block *b;
- struct bcache *cache = bcache_create(8, 16);
- for (i = 0; i < 16; i++)
+ for (i = 0; i < nr_cache_blocks; i++) {
+ // prefetch should not wait
+ _expect(me, E_ISSUE);
bcache_prefetch(cache, fd, i);
+ }
+
- for (i = 0; i < 16; i++) {
- CU_ASSERT(bcache_get(cache, fd, i, 0, &b));
+ for (i = 0; i < nr_cache_blocks; i++) {
+ _expect(me, E_WAIT);
+ T_ASSERT(bcache_get(cache, fd, i, 0, &b));
bcache_put(b);
}
+ }
- bcache_destroy(cache);
+ _expect(me, E_DESTROY);
+ bcache_destroy(cache);
+}
+
+static void test_too_many_prefetches_does_not_trigger_a_wait(void)
+{
+ const unsigned nr_cache_blocks = 16;
+ struct bcache *cache;
+ struct mock_engine *me = _mock_create(16);
+
+ // FIXME: use a fixture
+ _expect(me, E_MAX_IO);
+ cache = bcache_create(64, nr_cache_blocks, &me->e);
+ T_ASSERT(cache);
+
+ {
+ int fd = 17; // arbitrary key
+ unsigned i;
+
+ for (i = 0; i < 10 * nr_cache_blocks; i++) {
+ // prefetch should not wait
+ if (i < nr_cache_blocks)
+ _expect(me, E_ISSUE);
+ bcache_prefetch(cache, fd, i);
+ }
+
+ // Destroy will wait for any in flight IO triggered by prefetches.
+ for (i = 0; i < nr_cache_blocks; i++)
+ _expect(me, E_WAIT);
}
- close(fd);
+ _expect(me, E_DESTROY);
+ bcache_destroy(cache);
+}
- test_exit();
+static void test_dirty_data_gets_written_back(void)
+{
+ const unsigned nr_cache_blocks = 16;
+ struct bcache *cache;
+ struct mock_engine *me = _mock_create(16);
+
+ // FIXME: use a fixture
+ _expect(me, E_MAX_IO);
+ cache = bcache_create(64, nr_cache_blocks, &me->e);
+ T_ASSERT(cache);
+
+ {
+ int fd = 17; // arbitrary key
+ struct block *b;
+
+ // FIXME: be specific about the IO direction
+ // Expect the read
+ _expect(me, E_ISSUE);
+ _expect(me, E_WAIT);
+ T_ASSERT(bcache_get(cache, fd, 0, GF_DIRTY, &b));
+ bcache_put(b);
+
+ // Expect the write
+ _expect(me, E_ISSUE);
+ _expect(me, E_WAIT);
+ }
+
+ _expect(me, E_DESTROY);
+ bcache_destroy(cache);
+}
+
+static void test_zeroed_data_counts_as_dirty(void)
+{
+ const unsigned nr_cache_blocks = 16;
+ struct bcache *cache;
+ struct mock_engine *me = _mock_create(16);
+
+ // FIXME: use a fixture
+ _expect(me, E_MAX_IO);
+ cache = bcache_create(64, nr_cache_blocks, &me->e);
+ T_ASSERT(cache);
+
+ {
+ int fd = 17; // arbitrary key
+ struct block *b;
+
+ // No read
+ T_ASSERT(bcache_get(cache, fd, 0, GF_ZERO, &b));
+ bcache_put(b);
+
+ // Expect the write
+ _expect(me, E_ISSUE);
+ _expect(me, E_WAIT);
+ }
+
+ _expect(me, E_DESTROY);
+ bcache_destroy(cache);
+}
+
+static void test_flush_waits_for_all_dirty(void)
+{
+ const unsigned nr_cache_blocks = 128, count = 16;
+ struct bcache *cache;
+ struct mock_engine *me = _mock_create(16);
+
+ // FIXME: use a fixture
+ _expect(me, E_MAX_IO);
+
+ // I'm using a large nr of cache blocks to avoid triggering writeback
+ // early.
+ cache = bcache_create(64, nr_cache_blocks, &me->e);
+ T_ASSERT(cache);
+
+ {
+ int fd = 17; // arbitrary key
+ unsigned i;
+ struct block *b;
+
+ for (i = 0; i < count; i++) {
+ if (i % 2) {
+ T_ASSERT(bcache_get(cache, fd, i, GF_ZERO, &b));
+ } else {
+ _expect_read(me);
+ _expect(me, E_WAIT);
+ T_ASSERT(bcache_get(cache, fd, i, 0, &b));
+ }
+ bcache_put(b);
+ }
+
+ for (i = 0; i < count; i++) {
+ if (i % 2)
+ _expect_write(me);
+ }
+
+ for (i = 0; i < count; i++) {
+ if (i % 2)
+ _expect(me, E_WAIT);
+ }
+
+ bcache_flush(cache);
+ _no_outstanding_expectations(me);
+ }
+
+ _expect(me, E_DESTROY);
+ bcache_destroy(cache);
}
+#if 0
#define NR_FILES 4
static void test_read_multiple_files(void)
{
@@ -211,21 +583,58 @@ static void test_read_multiple_files(void)
test_exit();
}
-
+#endif
// Tests to be written
// Open multiple files and prove the blocks are coming from the correct file
// show invalidate works
// show invalidate_fd works
// show writeback is working
// check zeroing
-//
-CU_TestInfo bcache_list[] = {
- { (char*)"create", test_create },
- { (char*)"nr cache block must be positive", test_nr_cache_blocks_must_be_positive },
- { (char*)"block size must be positive", test_block_size_must_be_positive },
- { (char*)"block size must be multiple of page size", test_block_size_must_be_multiple_of_page_size },
- { (char*)"reads work", test_reads_work },
- { (char*)"prefetch works", test_prefetch_works },
- { (char*)"read multiple files", test_read_multiple_files },
- CU_TEST_INFO_NULL
+
+struct test_details {
+ const char *name;
+ void (*fn)(void);
};
+
+int main(int argc, char **argv)
+{
+ static struct test_details _tests[] = {
+ {"simple create/destroy", test_create},
+ {"nr cache blocks must be positive", test_nr_cache_blocks_must_be_positive},
+ {"block size must be positive", test_block_size_must_be_positive},
+ {"block size must be a multiple of page size", test_block_size_must_be_multiple_of_page_size},
+ {"bcache_get() triggers read", test_get_triggers_read},
+ {"repeated reads are cached", test_repeated_reads_are_cached},
+ {"block get evicted with many reads", test_block_gets_evicted_with_many_reads},
+ {"prefetch issues a read", test_prefetch_issues_a_read},
+ {"too many prefetches does not trigger a wait", test_too_many_prefetches_does_not_trigger_a_wait},
+ {"dirty data gets written back", test_dirty_data_gets_written_back},
+ {"zeroed data counts as dirty", test_zeroed_data_counts_as_dirty},
+ {"flush waits for all dirty", test_flush_waits_for_all_dirty},
+ };
+
+ // We have to declare these as volatile because of the setjmp()
+ volatile unsigned i = 0, passed = 0;
+
+ for (i = 0; i < DM_ARRAY_SIZE(_tests); i++) {
+ struct test_details *t = _tests + i;
+ fprintf(stderr, "[RUN ] %s\n", t->name);
+
+ if (setjmp(_test_k))
+ fprintf(stderr, "[ FAIL] %s\n", t->name);
+ else {
+ t->fn();
+ passed++;
+ fprintf(stderr, "[ OK] %s\n", t->name);
+ }
+ }
+
+ fprintf(stderr, "\n%u/%lu tests passed\n", passed, DM_ARRAY_SIZE(_tests));
+
+#if 0
+ test_prefetch_works();
+ test_read_multiple_files();
+#endif
+
+ return 0;
+}
6 years
master - scan: remove lvmcache info for failed devs
by David Teigland
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=44726ed9cb955980186...
Commit: 44726ed9cb9559801868ade5440d9e6ea6a61127
Parent: 1717d4cb178b88dc70bc14a11aba199b34931bb0
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Thu Mar 1 12:40:37 2018 -0600
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Fri Apr 20 11:22:47 2018 -0500
scan: remove lvmcache info for failed devs
When scanning a device fails, drop an lvmcache
info struct for it.
---
lib/label/label.c | 37 +++++++++++++++++++++++++------------
1 files changed, 25 insertions(+), 12 deletions(-)
diff --git a/lib/label/label.c b/lib/label/label.c
index dd455ec..82d4bf3 100644
--- a/lib/label/label.c
+++ b/lib/label/label.c
@@ -333,7 +333,6 @@ static int _process_block(struct device *dev, struct block *bb, int *is_lvm_devi
char label_buf[LABEL_SIZE] __attribute__((aligned(8)));
struct label *label = NULL;
struct labeller *labeller;
- struct lvmcache_info *info;
uint64_t sector;
int ret = 0;
@@ -356,11 +355,7 @@ static int _process_block(struct device *dev, struct block *bb, int *is_lvm_devi
log_very_verbose("%s: No lvm label detected", dev_name(dev));
- if ((info = lvmcache_info_from_pvid(dev->pvid, dev, 0))) {
- /* FIXME: if this case is actually happening, fix it. */
- log_warn("Device %s has no label, removing PV info from lvmcache.", dev_name(dev));
- lvmcache_del(info);
- }
+ lvmcache_del_dev(dev); /* FIXME: if this is needed, fix it. */
*is_lvm_device = 0;
goto_out;
@@ -380,6 +375,7 @@ static int _process_block(struct device *dev, struct block *bb, int *is_lvm_devi
label->sector = sector;
} else {
/* FIXME: handle errors */
+ lvmcache_del_dev(dev);
}
out:
return ret;
@@ -475,6 +471,7 @@ static int _scan_list(struct dm_list *devs, int *failed)
int rem_prefetches;
int scan_failed;
int is_lvm_device;
+ int ret;
dm_list_init(&wait_devs);
dm_list_init(&done_devs);
@@ -498,7 +495,7 @@ static int _scan_list(struct dm_list *devs, int *failed)
if (!_in_bcache(devl->dev)) {
if (!_scan_dev_open(devl->dev)) {
- log_debug_devs("%s: Failed to open device.", dev_name(devl->dev));
+ log_debug_devs("Scan failed to open %s.", dev_name(devl->dev));
dm_list_del(&devl->list);
dm_list_add(&done_devs, &devl->list);
scan_failed_count++;
@@ -518,14 +515,24 @@ static int _scan_list(struct dm_list *devs, int *failed)
bb = NULL;
if (!bcache_get(scan_bcache, devl->dev->bcache_fd, 0, 0, &bb)) {
- log_debug_devs("%s: Failed to scan device.", dev_name(devl->dev));
+ log_error("Scan failed to read %s.", dev_name(devl->dev));
scan_failed_count++;
scan_failed = 1;
+ lvmcache_del_dev(devl->dev);
} else {
log_debug_devs("Processing data from device %s fd %d block %p", dev_name(devl->dev), devl->dev->bcache_fd, bb);
- _process_block(devl->dev, bb, &is_lvm_device);
- scan_lvm_count++;
- scan_failed = 0;
+
+ ret = _process_block(devl->dev, bb, &is_lvm_device);
+
+ if (!ret && is_lvm_device) {
+ log_error("Scan failed to process %s", dev_name(devl->dev));
+ scan_failed_count++;
+ scan_failed = 1;
+ lvmcache_del_dev(devl->dev);
+ } else {
+ scan_lvm_count++;
+ scan_failed = 0;
+ }
}
if (bb)
@@ -884,6 +891,8 @@ bool dev_read_bytes(struct device *dev, off_t start, size_t len, void *data)
}
if (!bcache_read_bytes(scan_bcache, dev->bcache_fd, start, len, data)) {
+ log_error("dev_read_bytes %s at %u failed invalidate fd %d",
+ dev_name(dev), (uint32_t)start, dev->bcache_fd);
label_scan_invalidate(dev);
return false;
}
@@ -913,6 +922,8 @@ bool dev_write_bytes(struct device *dev, off_t start, size_t len, void *data)
}
if (!bcache_write_bytes(scan_bcache, dev->bcache_fd, start, len, data)) {
+ log_error("dev_write_bytes %s at %u failed invalidate fd %d",
+ dev_name(dev), (uint32_t)start, dev->bcache_fd);
label_scan_invalidate(dev);
return false;
}
@@ -936,11 +947,13 @@ bool dev_write_zeros(struct device *dev, off_t start, size_t len)
}
if (dev->bcache_fd <= 0) {
- log_error("dev_write_bytes %s with invalid fd %d", dev_name(dev), dev->bcache_fd);
+ log_error("dev_write_zeros %s with invalid fd %d", dev_name(dev), dev->bcache_fd);
return false;
}
if (!bcache_write_zeros(scan_bcache, dev->bcache_fd, start, len)) {
+ log_error("dev_write_zeros %s at %u failed invalidate fd %d",
+ dev_name(dev), (uint32_t)start, dev->bcache_fd);
label_scan_invalidate(dev);
return false;
}
6 years
master - lvmcache: add shorter way to delete dev info
by David Teigland
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=1717d4cb178b88dc70b...
Commit: 1717d4cb178b88dc70bc14a11aba199b34931bb0
Parent: 570c6239eebf18e6884d350654274d3bc8f1c4f1
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Thu Mar 1 10:20:34 2018 -0600
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Fri Apr 20 11:22:47 2018 -0500
lvmcache: add shorter way to delete dev info
Don't make the caller look up the info first.
---
lib/cache/lvmcache.c | 8 ++++++++
lib/cache/lvmcache.h | 2 ++
2 files changed, 10 insertions(+), 0 deletions(-)
diff --git a/lib/cache/lvmcache.c b/lib/cache/lvmcache.c
index 53254f4..6479080 100644
--- a/lib/cache/lvmcache.c
+++ b/lib/cache/lvmcache.c
@@ -1608,6 +1608,14 @@ void lvmcache_del(struct lvmcache_info *info)
dm_free(info);
}
+void lvmcache_del_dev(struct device *dev)
+{
+ struct lvmcache_info *info;
+
+ if ((info = lvmcache_info_from_pvid((const char *)dev->pvid, dev, 0)))
+ lvmcache_del(info);
+}
+
/*
* vginfo must be info->vginfo unless info is NULL (orphans)
*/
diff --git a/lib/cache/lvmcache.h b/lib/cache/lvmcache.h
index 3967b29..4343060 100644
--- a/lib/cache/lvmcache.h
+++ b/lib/cache/lvmcache.h
@@ -59,6 +59,7 @@ struct lvmcache_vgsummary {
const char *lock_type;
uint32_t mda_checksum;
size_t mda_size;
+ int zero_offset;
};
int lvmcache_init(void);
@@ -83,6 +84,7 @@ struct lvmcache_info *lvmcache_add(struct labeller *labeller, const char *pvid,
uint32_t vgstatus);
int lvmcache_add_orphan_vginfo(const char *vgname, struct format_type *fmt);
void lvmcache_del(struct lvmcache_info *info);
+void lvmcache_del_dev(struct device *dev);
/* Update things */
int lvmcache_update_vgname_and_id(struct lvmcache_info *info,
6 years
master - [build] include test/unit/Makefile rather than recursive build
by David Teigland
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=8ae3b244fcbc207b51a...
Commit: 8ae3b244fcbc207b51a81514e51008fe64d13368
Parent: b03e55a5130ffdf6be9188b227c59e6793dc0dfc
Author: Joe Thornber <ejt(a)redhat.com>
AuthorDate: Fri Feb 2 15:39:17 2018 +0000
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Fri Apr 20 11:12:50 2018 -0500
[build] include test/unit/Makefile rather than recursive build
FIXME: unit tests are not currently run as part of make check.
---
Makefile.in | 23 +------------------
test/Makefile.in | 2 +-
test/unit/Makefile.in | 58 ++++++++++++++++---------------------------------
3 files changed, 21 insertions(+), 62 deletions(-)
diff --git a/Makefile.in b/Makefile.in
index 31d428d..146ed55 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -212,28 +212,7 @@ endif
endif
ifeq ("$(TESTING)", "yes")
-# testing and report generation
-RUBY=ruby1.9 -Ireport-generators/lib -Ireport-generators/test
-
-.PHONY: unit-test ruby-test test-programs
-
-# FIXME: put dependencies on libdm and liblvm
-# FIXME: Should be handled by Makefiles in subdirs, not here at top level.
-test-programs:
- cd unit-tests/regex && $(MAKE)
- cd unit-tests/datastruct && $(MAKE)
- cd unit-tests/mm && $(MAKE)
-
-unit-test: test-programs
- $(RUBY) report-generators/unit_test.rb $(shell find . -name TESTS)
- $(RUBY) report-generators/title_page.rb
-
-memcheck: test-programs
- $(RUBY) report-generators/memcheck.rb $(shell find . -name TESTS)
- $(RUBY) report-generators/title_page.rb
-
-ruby-test:
- $(RUBY) report-generators/test/ts.rb
+include test/unit/Makefile
endif
ifneq ($(shell which ctags),)
diff --git a/test/Makefile.in b/test/Makefile.in
index 230ce5b..097b2fa 100644
--- a/test/Makefile.in
+++ b/test/Makefile.in
@@ -27,7 +27,7 @@ datarootdir = @datarootdir@
LVM_TEST_RESULTS ?= results
-SUBDIRS = api unit
+SUBDIRS = api
SOURCES = lib/not.c lib/harness.c
CXXSOURCES = lib/runner.cpp
CXXFLAGS += $(EXTRA_EXEC_CFLAGS)
diff --git a/test/unit/Makefile.in b/test/unit/Makefile.in
index 5cf92ba..2e2c819 100644
--- a/test/unit/Makefile.in
+++ b/test/unit/Makefile.in
@@ -1,4 +1,4 @@
-# Copyright (C) 2011-2017 Red Hat, Inc. All rights reserved.
+# Copyright (C) 2011-2018 Red Hat, Inc. All rights reserved.
#
# This file is part of LVM2.
#
@@ -10,44 +10,24 @@
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-srcdir = @srcdir@
-top_srcdir = @top_srcdir@
-top_builddir = @top_builddir@
-
-VPATH = $(srcdir)
-UNITS = \
- bcache_t.c \
- bitset_t.c\
- config_t.c\
- dmlist_t.c\
- dmstatus_t.c\
- matcher_t.c\
- percent_t.c\
- string_t.c\
- run.c
-
-ifeq ("@TESTING@", "yes")
-SOURCES = $(UNITS)
-TARGETS = run
-endif
-
-include $(top_builddir)/make.tmpl
-
-ifeq ($(MAKECMDGOALS),distclean)
-SOURCES = $(UNITS)
-endif
-
-ifeq ("$(TESTING)", "yes")
-LDLIBS += $(LVMINTERNAL_LIBS) -ldevmapper -laio @CUNIT_LIBS@
-CFLAGS += @CUNIT_CFLAGS@
-
-check: unit
-
-$(TARGETS): $(OBJECTS) $(top_builddir)/libdm/libdevmapper.$(LIB_SUFFIX)
+UNIT_SOURCE=\
+ test/unit/bcache_t.c \
+ test/unit/bitset_t.c\
+ test/unit/config_t.c\
+ test/unit/dmlist_t.c\
+ test/unit/dmstatus_t.c\
+ test/unit/matcher_t.c\
+ test/unit/percent_t.c\
+ test/unit/string_t.c\
+ test/unit/run.c
+UNIT_OBJECTS=$(UNIT_SOURCE:%.c=%.o)
+
+UNIT_LDLIBS += $(LVMINTERNAL_LIBS) -ldevmapper -laio -lcunit
+
+test/unit/run: $(UNIT_OBJECTS) libdm/libdevmapper.$(LIB_SUFFIX) lib/liblvm-internal.a
$(CC) $(CFLAGS) $(LDFLAGS) $(EXTRA_EXEC_LDFLAGS) -L$(top_builddir)/libdm \
- -o $@ $(OBJECTS) $(LDLIBS)
+ -o $@ $(UNIT_OBJECTS) $(UNIT_LDLIBS)
-unit: $(TARGETS) $(top_builddir)/lib/liblvm-internal.a
+unit-test: test/unit/run
@echo Running unit tests
- LD_LIBRARY_PATH=$(top_builddir)/libdm ./$(TARGETS)
-endif
+ LD_LIBRARY_PATH=libdm test/unit/run
6 years
master - bcache: fix error handling
by David Teigland
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=570c6239eebf18e6884...
Commit: 570c6239eebf18e6884d350654274d3bc8f1c4f1
Parent: 217f3f8741c1f516ab1f039b8725ac3ce638e302
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Thu Mar 1 10:17:32 2018 -0600
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Fri Apr 20 11:22:47 2018 -0500
bcache: fix error handling
The error handling code wasn't working, but it
appears that just removing it is what we need.
The doesn't really need any different behavior
related to bcache blocks on an io error, it just
wants to know if there was an error.
---
lib/device/bcache.c | 16 +++++++++++++++-
1 files changed, 15 insertions(+), 1 deletions(-)
diff --git a/lib/device/bcache.c b/lib/device/bcache.c
index 94623a8..9034776 100644
--- a/lib/device/bcache.c
+++ b/lib/device/bcache.c
@@ -539,6 +539,14 @@ static void _complete_io(void *context, int err)
dm_list_del(&b->list);
if (b->error) {
+ log_warn("bcache io error %d fd %d", b->error, b->fd);
+ memset(b->data, 0, cache->block_sectors << SECTOR_SHIFT);
+ }
+
+ /* Things don't work with this block of code, but work without it. */
+#if 0
+ if (b->error) {
+ log_warn("bcache io error %d fd %d", b->error, b->fd);
if (b->io_dir == DIR_READ) {
// We can just forget about this block, since there's
// no dirty data to be written back.
@@ -552,6 +560,9 @@ static void _complete_io(void *context, int err)
_clear_flags(b, BF_DIRTY);
_link_block(b);
}
+#endif
+ _clear_flags(b, BF_DIRTY);
+ _link_block(b);
}
/*
@@ -768,7 +779,7 @@ static struct block *_lookup_or_read_block(struct bcache *cache,
}
}
- if (b && !b->error) {
+ if (b) {
if (flags & (GF_DIRTY | GF_ZERO))
_set_flags(b, BF_DIRTY);
@@ -904,6 +915,9 @@ bool bcache_get(struct bcache *cache, int fd, block_address index,
b->ref_count++;
*result = b;
+
+ if (b->error)
+ return false;
return true;
}
6 years
master - [device/bcache] rename a unit test
by David Teigland
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=b03e55a5130ffdf6be9...
Commit: b03e55a5130ffdf6be9188b227c59e6793dc0dfc
Parent: 0d0fab3d2ddb0c0f16c01e569e3f1f218701592e
Author: Joe Thornber <ejt(a)redhat.com>
AuthorDate: Fri Feb 2 15:38:46 2018 +0000
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Fri Apr 20 11:12:50 2018 -0500
[device/bcache] rename a unit test
---
test/unit/bcache_t.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/test/unit/bcache_t.c b/test/unit/bcache_t.c
index 5532c92..3db9cc7 100644
--- a/test/unit/bcache_t.c
+++ b/test/unit/bcache_t.c
@@ -175,7 +175,7 @@ static void test_prefetch_works(void)
}
#define NR_FILES 4
-static void test_multiple_files(void)
+static void test_read_multiple_files(void)
{
unsigned i;
int fd[NR_FILES];
@@ -226,6 +226,6 @@ CU_TestInfo bcache_list[] = {
{ (char*)"block size must be multiple of page size", test_block_size_must_be_multiple_of_page_size },
{ (char*)"reads work", test_reads_work },
{ (char*)"prefetch works", test_prefetch_works },
- { (char*)"multiple files", test_multiple_files },
+ { (char*)"read multiple files", test_read_multiple_files },
CU_TEST_INFO_NULL
};
6 years
master - [device/bcache] another unit test
by David Teigland
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=0d0fab3d2ddb0c0f16c...
Commit: 0d0fab3d2ddb0c0f16c01e569e3f1f218701592e
Parent: 19647d1cd44d029a8aa2f7e74dbdbd1f114a8c08
Author: Joe Thornber <ejt(a)redhat.com>
AuthorDate: Fri Feb 2 14:35:11 2018 +0000
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Fri Apr 20 11:12:50 2018 -0500
[device/bcache] another unit test
---
test/unit/bcache_t.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 47 insertions(+), 1 deletions(-)
diff --git a/test/unit/bcache_t.c b/test/unit/bcache_t.c
index ef92721..5532c92 100644
--- a/test/unit/bcache_t.c
+++ b/test/unit/bcache_t.c
@@ -50,7 +50,7 @@ static int _prep_file(const char *path)
if (fd < 0)
return -1;
- r = fallocate(fd, FALLOC_FL_ZERO_RANGE, 0, (16 * MEG) << SECTOR_SHIFT);
+ r = fallocate(fd, FALLOC_FL_ZERO_RANGE, 0, (1 * MEG) << SECTOR_SHIFT);
if (r) {
close(fd);
return -1;
@@ -174,6 +174,51 @@ static void test_prefetch_works(void)
test_exit();
}
+#define NR_FILES 4
+static void test_multiple_files(void)
+{
+ unsigned i;
+ int fd[NR_FILES];
+ char buffer[128];
+
+
+ // FIXME: add fixtures.
+ test_init();
+ for (i = 0; i < NR_FILES; i++) {
+ snprintf(buffer, sizeof(buffer), "./test%u.bin", i);
+ unlink(buffer);
+ _prep_file(buffer);
+ fd[i] = open_file(buffer);
+ CU_ASSERT(fd[i] >= 0);
+ }
+
+ {
+ struct block *b;
+ struct bcache *cache = bcache_create(8, 16);
+
+ for (i = 0; i < 64; i++) {
+ if (!bcache_get(cache, fd[i % NR_FILES], i, 0, &b)) {
+ CU_ASSERT(false);
+ } else
+ bcache_put(b);
+ }
+
+ bcache_destroy(cache);
+ }
+
+ for (i = 0; i < NR_FILES; i++)
+ close(fd[i]);
+
+ test_exit();
+}
+
+// Tests to be written
+// Open multiple files and prove the blocks are coming from the correct file
+// show invalidate works
+// show invalidate_fd works
+// show writeback is working
+// check zeroing
+//
CU_TestInfo bcache_list[] = {
{ (char*)"create", test_create },
{ (char*)"nr cache block must be positive", test_nr_cache_blocks_must_be_positive },
@@ -181,5 +226,6 @@ CU_TestInfo bcache_list[] = {
{ (char*)"block size must be multiple of page size", test_block_size_must_be_multiple_of_page_size },
{ (char*)"reads work", test_reads_work },
{ (char*)"prefetch works", test_prefetch_works },
+ { (char*)"multiple files", test_multiple_files },
CU_TEST_INFO_NULL
};
6 years
master - scan: add function to drop bcache blocks
by David Teigland
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=217f3f8741c1f516ab1...
Commit: 217f3f8741c1f516ab1f039b8725ac3ce638e302
Parent: da2b155a9da4c4c9caf502b87ad263de6e678b30
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Tue Feb 27 16:35:47 2018 -0600
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Fri Apr 20 11:22:47 2018 -0500
scan: add function to drop bcache blocks
which can be a little more efficient that destroy.
---
lib/label/label.c | 24 ++++++++++++++++--------
lib/label/label.h | 1 +
2 files changed, 17 insertions(+), 8 deletions(-)
diff --git a/lib/label/label.c b/lib/label/label.c
index 0514ddf..dd455ec 100644
--- a/lib/label/label.c
+++ b/lib/label/label.c
@@ -733,20 +733,15 @@ void label_scan_invalidate_lv(struct cmd_context *cmd, struct logical_volume *lv
}
/*
- * Undo label_scan()
- *
- * Close devices that are open because bcache is holding blocks for them.
- * Destroy the bcache.
+ * Empty the bcache of all blocks and close all open fds,
+ * but keep the bcache set up.
*/
-void label_scan_destroy(struct cmd_context *cmd)
+void label_scan_drop(struct cmd_context *cmd)
{
struct dev_iter *iter;
struct device *dev;
- if (!scan_bcache)
- return;
-
if (!(iter = dev_iter_create(cmd->full_filter, 0))) {
return;
}
@@ -756,6 +751,19 @@ void label_scan_destroy(struct cmd_context *cmd)
_scan_dev_close(dev);
}
dev_iter_destroy(iter);
+}
+
+/*
+ * Close devices that are open because bcache is holding blocks for them.
+ * Destroy the bcache.
+ */
+
+void label_scan_destroy(struct cmd_context *cmd)
+{
+ if (!scan_bcache)
+ return;
+
+ label_scan_drop(cmd);
bcache_destroy(scan_bcache);
scan_bcache = NULL;
diff --git a/lib/label/label.h b/lib/label/label.h
index 8ef687b..55e92e8 100644
--- a/lib/label/label.h
+++ b/lib/label/label.h
@@ -107,6 +107,7 @@ int label_scan_devs(struct cmd_context *cmd, struct dm_list *devs);
int label_scan_devs_excl(struct dm_list *devs);
void label_scan_invalidate(struct device *dev);
void label_scan_invalidate_lv(struct cmd_context *cmd, struct logical_volume *lv);
+void label_scan_drop(struct cmd_context *cmd);
void label_scan_destroy(struct cmd_context *cmd);
int label_read(struct device *dev, struct label **labelp, uint64_t unused_sector);
int label_read_sector(struct device *dev, struct label **labelp, uint64_t scan_sector);
6 years