summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlasdair Kergon <agk@redhat.com>2001-12-13 00:07:29 +0000
committerAlasdair Kergon <agk@redhat.com>2001-12-13 00:07:29 +0000
commitfaa556e75922e2992a17c9ada4e1fd1c884847b6 (patch)
tree3d5a324b15f371c41e20d49665615af34495294c
parent394fae7fc50f36432ae6e2cb66bcfa30258415c5 (diff)
downloadlvm2-faa556e75922e2992a17c9ada4e1fd1c884847b6.tar.gz
lvm2-faa556e75922e2992a17c9ada4e1fd1c884847b6.tar.xz
lvm2-faa556e75922e2992a17c9ada4e1fd1c884847b6.zip
Add internal cache holding a 'hint' list of the PVs belonging to each VG.
A substantial speed-up - particularly in readline mode. If the hints turn out to be wrong, the relevant parts get thrown away. vgscan destroys it totally. In both cases it then rebuilds itself as required.
-rw-r--r--include/.symlinks3
-rw-r--r--lib/Makefile.in3
-rw-r--r--lib/format1/disk-rep.c155
-rw-r--r--lib/format1/import-extents.c88
-rw-r--r--lib/metadata/lv_manip.c2
-rw-r--r--lib/mm/dbg_malloc.h11
-rw-r--r--lib/vgcache/vgcache.c139
-rw-r--r--lib/vgcache/vgcache.h36
-rw-r--r--tools/lvm.c2
-rw-r--r--tools/vgscan.c11
10 files changed, 347 insertions, 103 deletions
diff --git a/include/.symlinks b/include/.symlinks
index a382cea9..acc56c67 100644
--- a/include/.symlinks
+++ b/include/.symlinks
@@ -13,6 +13,7 @@
../lib/filters/filter-regex.h
../lib/filters/filter.h
../lib/format1/format1.h
+../lib/label/label.h
../lib/log/log.h
../lib/metadata/metadata.h
../lib/mm/dbg_malloc.h
@@ -20,4 +21,4 @@
../lib/mm/xlate.h
../lib/regex/matcher.h
../lib/uuid/uuid.h
-../lib/label/label.h
+../lib/vgcache/vgcache.h
diff --git a/lib/Makefile.in b/lib/Makefile.in
index 38d153cf..a1339604 100644
--- a/lib/Makefile.in
+++ b/lib/Makefile.in
@@ -29,6 +29,7 @@ SOURCES=\
format1/import-extents.c \
format1/layout.c \
format1/vg_number.c \
+ label/label.c \
log/log.c \
metadata/lv_manip.c \
metadata/metadata.c \
@@ -39,7 +40,7 @@ SOURCES=\
regex/parse_rx.c \
regex/ttree.c \
uuid/uuid.c \
- label/label.c
+ vgcache/vgcache.c
TARGETS=liblvm.a
diff --git a/lib/format1/disk-rep.c b/lib/format1/disk-rep.c
index 78b33375..a6905ef2 100644
--- a/lib/format1/disk-rep.c
+++ b/lib/format1/disk-rep.c
@@ -8,6 +8,7 @@
#include "pool.h"
#include "xlate.h"
#include "log.h"
+#include "vgcache.h"
#include <sys/types.h>
#include <sys/stat.h>
@@ -25,66 +26,71 @@
*/
static void _xlate_pvd(struct pv_disk *disk)
{
- xx16(version);
-
- xx32(pv_on_disk.base); xx32(pv_on_disk.size);
- xx32(vg_on_disk.base); xx32(vg_on_disk.size);
- xx32(pv_uuidlist_on_disk.base); xx32(pv_uuidlist_on_disk.size);
- xx32(lv_on_disk.base); xx32(lv_on_disk.size);
- xx32(pe_on_disk.base); xx32(pe_on_disk.size);
-
- xx32(pv_major);
- xx32(pv_number);
- xx32(pv_status);
- xx32(pv_allocatable);
- xx32(pv_size);
- xx32(lv_cur);
- xx32(pe_size);
- xx32(pe_total);
- xx32(pe_allocated);
+ xx16(version);
+
+ xx32(pv_on_disk.base);
+ xx32(pv_on_disk.size);
+ xx32(vg_on_disk.base);
+ xx32(vg_on_disk.size);
+ xx32(pv_uuidlist_on_disk.base);
+ xx32(pv_uuidlist_on_disk.size);
+ xx32(lv_on_disk.base);
+ xx32(lv_on_disk.size);
+ xx32(pe_on_disk.base);
+ xx32(pe_on_disk.size);
+
+ xx32(pv_major);
+ xx32(pv_number);
+ xx32(pv_status);
+ xx32(pv_allocatable);
+ xx32(pv_size);
+ xx32(lv_cur);
+ xx32(pe_size);
+ xx32(pe_total);
+ xx32(pe_allocated);
xx32(pe_start);
}
static void _xlate_lvd(struct lv_disk *disk)
{
- xx32(lv_access);
- xx32(lv_status);
- xx32(lv_open);
- xx32(lv_dev);
- xx32(lv_number);
- xx32(lv_mirror_copies);
- xx32(lv_recovery);
- xx32(lv_schedule);
- xx32(lv_size);
- xx32(lv_snapshot_minor);
- xx16(lv_chunk_size);
- xx16(dummy);
- xx32(lv_allocated_le);
- xx32(lv_stripes);
- xx32(lv_stripesize);
- xx32(lv_badblock);
- xx32(lv_allocation);
- xx32(lv_io_timeout);
- xx32(lv_read_ahead);
+ xx32(lv_access);
+ xx32(lv_status);
+ xx32(lv_open);
+ xx32(lv_dev);
+ xx32(lv_number);
+ xx32(lv_mirror_copies);
+ xx32(lv_recovery);
+ xx32(lv_schedule);
+ xx32(lv_size);
+ xx32(lv_snapshot_minor);
+ xx16(lv_chunk_size);
+ xx16(dummy);
+ xx32(lv_allocated_le);
+ xx32(lv_stripes);
+ xx32(lv_stripesize);
+ xx32(lv_badblock);
+ xx32(lv_allocation);
+ xx32(lv_io_timeout);
+ xx32(lv_read_ahead);
}
static void _xlate_vgd(struct vg_disk *disk)
{
- xx32(vg_number);
- xx32(vg_access);
- xx32(vg_status);
- xx32(lv_max);
- xx32(lv_cur);
- xx32(lv_open);
- xx32(pv_max);
- xx32(pv_cur);
- xx32(pv_act);
- xx32(dummy);
- xx32(vgda);
- xx32(pe_size);
- xx32(pe_total);
- xx32(pe_allocated);
- xx32(pvg_total);
+ xx32(vg_number);
+ xx32(vg_access);
+ xx32(vg_status);
+ xx32(lv_max);
+ xx32(lv_cur);
+ xx32(lv_open);
+ xx32(pv_max);
+ xx32(pv_cur);
+ xx32(pv_act);
+ xx32(dummy);
+ xx32(vgda);
+ xx32(pe_size);
+ xx32(pe_total);
+ xx32(pe_allocated);
+ xx32(pvg_total);
}
static void _xlate_extents(struct pe_disk *extents, int count)
@@ -163,7 +169,7 @@ static int _read_uuids(struct disk_list *data)
ulong pos = data->pvd.pv_uuidlist_on_disk.base;
ulong end = pos + data->pvd.pv_uuidlist_on_disk.size;
- while(pos < end && num_read < data->vgd.pv_cur) {
+ while (pos < end && num_read < data->vgd.pv_cur) {
if (dev_read(data->dev, pos, sizeof(buffer), buffer) !=
sizeof(buffer))
fail;
@@ -195,7 +201,7 @@ static int _read_lvs(struct disk_list *data)
struct lvd_list *ll;
struct vg_disk *vgd = &data->vgd;
- for(i = 0; (i < vgd->lv_max) && (read < vgd->lv_cur); i++) {
+ for (i = 0; (i < vgd->lv_max) && (read < vgd->lv_cur); i++) {
pos = data->pvd.lv_on_disk.base + (i * sizeof(struct lv_disk));
ll = pool_alloc(data->mem, sizeof(*ll));
@@ -266,6 +272,9 @@ static struct disk_list *__read_disk(struct device *dev, struct pool *mem,
goto bad;
}
+ /* Update VG cache with whatever we found */
+ vgcache_add(data->pvd.vg_name, dev);
+
/*
* is it an orphan ?
*/
@@ -304,7 +313,7 @@ static struct disk_list *__read_disk(struct device *dev, struct pool *mem,
return data;
- bad:
+ bad:
pool_free(data->mem, data);
return NULL;
}
@@ -327,12 +336,10 @@ struct disk_list *read_disk(struct device *dev, struct pool *mem,
return r;
}
-
/*
- * Build a list of pv_d's structures, allocated
- * from mem. We keep track of the first object
- * allocated form the pool so we can free off all
- * the memory if something goes wrong.
+ * Build a list of pv_d's structures, allocated from mem.
+ * We keep track of the first object allocated form the pool
+ * so we can free off all the memory if something goes wrong.
*/
int read_pvs_in_vg(const char *vg_name, struct dev_filter *filter,
struct pool *mem, struct list *head)
@@ -341,9 +348,33 @@ int read_pvs_in_vg(const char *vg_name, struct dev_filter *filter,
struct device *dev;
struct disk_list *data = NULL;
+ struct list *pvdh, *pvdh2;
+
+ /* Fast path if we already saw this VG and cached the list of PVs */
+ if ((pvdh = vgcache_find(vg_name))) {
+ list_iterate(pvdh2, pvdh) {
+ dev = list_item(pvdh2, struct pvdev_list)->dev;
+ if ((data = read_disk(dev, mem, vg_name)))
+ list_add(head, &data->list);
+ }
+
+ /* Did we find the whole VG? */
+ if (!vg_name || !*vg_name ||
+ (data && *data->pvd.vg_name &&
+ list_size(head) == data->vgd.pv_cur))
+ return 1;
+
+ /* Something changed. */
+ list_init(head);
+ /* FIXME Do at a lower level? */
+ vgcache_del(vg_name);
+ }
+
+ /* Otherwise do a complete scan */
for (dev = dev_iter_get(iter); dev; dev = dev_iter_get(iter)) {
- if ((data = read_disk(dev, mem, vg_name)))
+ if ((data = read_disk(dev, mem, vg_name))) {
list_add(head, &data->list);
+ }
}
dev_iter_destroy(iter);
@@ -353,7 +384,6 @@ int read_pvs_in_vg(const char *vg_name, struct dev_filter *filter,
return 1;
}
-
static int _write_vgd(struct disk_list *data)
{
struct vg_disk *vgd = &data->vgd;
@@ -412,7 +442,7 @@ static int _write_lvs(struct disk_list *data)
if (!dev_zero(data->dev, pos, data->pvd.lv_on_disk.size)) {
log_error("Couldn't zero lv area on device '%s'",
- dev_name(data->dev));
+ dev_name(data->dev));
return 0;
}
@@ -517,7 +547,6 @@ static int _write_all_pvd(struct disk_list *data)
return r;
}
-
/*
* Writes all the given pv's to disk. Does very
* little sanity checking, so make sure correct
diff --git a/lib/format1/import-extents.c b/lib/format1/import-extents.c
index 469bc5d5..9140d3c2 100644
--- a/lib/format1/import-extents.c
+++ b/lib/format1/import-extents.c
@@ -33,8 +33,8 @@ struct lv_map {
struct pe_specifier *map;
};
-static struct hash_table *
-_create_lv_maps(struct pool *mem, struct volume_group *vg)
+static struct hash_table *_create_lv_maps(struct pool *mem,
+ struct volume_group *vg)
{
struct hash_table *maps = hash_create(32);
struct list *llh;
@@ -47,7 +47,7 @@ _create_lv_maps(struct pool *mem, struct volume_group *vg)
return NULL;
}
- list_iterate (llh, &vg->lvs) {
+ list_iterate(llh, &vg->lvs) {
ll = list_item(llh, struct lv_list);
if (!(lvm = pool_alloc(mem, sizeof(*lvm)))) {
@@ -70,7 +70,7 @@ _create_lv_maps(struct pool *mem, struct volume_group *vg)
return maps;
- bad:
+ bad:
hash_destroy(maps);
return NULL;
}
@@ -86,7 +86,7 @@ static int _fill_lv_array(struct lv_map **lvs,
struct lvd_list *ll = list_item(lvh, struct lvd_list);
if (!(lvm = hash_lookup(maps, strrchr(ll->lvd.lv_name, '/')
- + 1 ))) {
+ + 1))) {
log_err("Physical volume (%s) contains an "
"unknown logical volume (%s).",
dev_name(dl->dev), ll->lvd.lv_name);
@@ -133,7 +133,7 @@ static int _fill_maps(struct hash_table *maps, struct volume_group *vg,
lv_num--;
lvm = lvms[lv_num];
- if(!lvm) {
+ if (!lvm) {
log_err("invalid lv in extent map");
return 0;
}
@@ -236,14 +236,36 @@ static int _read_linear(struct pool *mem, struct lv_map *lvm)
return 1;
}
+static int _check_for_stripe(struct lv_map *lvm, uint32_t base_le,
+ uint32_t stripe, uint32_t length)
+{
+ uint32_t next_pe, pe, base;
+ struct physical_volume *next_pv;
+
+ base = base_le + stripe * length;
+
+ next_pe = lvm->map[base].pe;
+ next_pv = lvm->map[base].pv;
+
+ for (pe = 1; pe < length; pe++) {
+ if (lvm->map[base + pe].pe != next_pe + pe ||
+ lvm->map[base + pe].pv != next_pv)
+ return 0;
+ }
+
+ return 1;
+}
+
static int _read_stripes(struct pool *mem, struct lv_map *lvm)
{
- uint32_t stripes = lvm->stripes, s, le = 0, pe;
+ uint32_t st, le = 0, flag_warning = 0;
struct stripe_segment *seg;
- struct pe_specifier *pes;
+ size_t seg_size;
while (le < lvm->lv->le_count) {
- if (!(seg = _alloc_seg(mem, stripes))) {
+ seg_size = sizeof(*seg) + sizeof(seg->area[0]);
+
+ if (!(seg = _alloc_seg(mem, lvm->stripes))) {
stack;
return 0;
}
@@ -252,8 +274,8 @@ static int _read_stripes(struct pool *mem, struct lv_map *lvm)
seg->le = le;
seg->len = 0;
seg->stripe_size = lvm->stripe_size;
- seg->stripes = stripes;
-
+ st = 1;
+
seg->area[0].pv = lvm->map[le].pv;
seg->area[0].pe = lvm->map[le].pe;
@@ -263,30 +285,33 @@ static int _read_stripes(struct pool *mem, struct lv_map *lvm)
(lvm->map[le + seg->len].pe == seg->area[0].pe +
seg->len));
- for (s = 1; s < stripes; s++) {
- pes = &lvm->map[le + s * seg->len];
-
- seg->area[s].pv = pes->pv;
- seg->area[s].pe = pes->pe;
-
- for (pe = 0; pe < seg->len; pe++) {
- if (lvm->map[le + s * seg->len + pe].pe !=
- pes->pe + pe) {
- log_error("Incompatible striping at LE"
- " %d on %s",
- le + s * seg->len + pe,
- seg->lv->name);
- return 0;
- }
- }
+ while (st < lvm->stripes &&
+ _check_for_stripe(lvm, le, st, seg->len)) {
+
+ seg->area[st].pv = lvm->map[le + st * seg->len].pv;
+ seg->area[st].pe = lvm->map[le + st * seg->len].pe;
+
+ st++;
}
-
- seg->len *= stripes;
+
+ if (st != lvm->stripes)
+ flag_warning++;
+
+ seg->stripes = st;
+ seg->len *= seg->stripes;
le += seg->len;
list_add(&lvm->lv->segments, &seg->list);
}
+ if (flag_warning) {
+ log_error("WARNING: Found %d segments with different numbers "
+ "of stripes.", flag_warning);
+ log_error("Risk of data corruption.");
+ log_error("Check the mapping is what you intended before you "
+ "use %s!", seg->lv->name);
+ }
+
return 1;
}
@@ -312,8 +337,7 @@ static int _build_all_segments(struct pool *mem, struct hash_table *maps)
return 1;
}
-int import_extents(struct pool *mem, struct volume_group *vg,
- struct list *pvds)
+int import_extents(struct pool *mem, struct volume_group *vg, struct list *pvds)
{
int r = 0;
struct pool *scratch = pool_create(10 * 1024);
@@ -345,7 +369,7 @@ int import_extents(struct pool *mem, struct volume_group *vg,
}
r = 1;
- out:
+ out:
if (maps)
hash_destroy(maps);
pool_destroy(scratch);
diff --git a/lib/metadata/lv_manip.c b/lib/metadata/lv_manip.c
index 675a02da..e70092b8 100644
--- a/lib/metadata/lv_manip.c
+++ b/lib/metadata/lv_manip.c
@@ -487,6 +487,8 @@ int lv_extend(struct logical_volume *lv,
lv->le_count += extents;
lv->size += extents * lv->vg->extent_size;
+ /* FIXME: Format1 must ensure stripes is consistent with 1st seg */
+
if (!_allocate(lv->vg, lv, acceptable_pvs, old_le_count,
stripes, stripe_size)) {
lv->le_count = old_le_count;
diff --git a/lib/mm/dbg_malloc.h b/lib/mm/dbg_malloc.h
index 3bf33047..33b8c959 100644
--- a/lib/mm/dbg_malloc.h
+++ b/lib/mm/dbg_malloc.h
@@ -28,5 +28,14 @@ void bounds_check(void);
#define bounds_check()
#endif
-#endif
+static inline char *dbg_strdup(const char *str)
+{
+ char *ret = dbg_malloc(strlen(str) + 1);
+
+ if (ret)
+ strcpy(ret, str);
+ return ret;
+}
+
+#endif
diff --git a/lib/vgcache/vgcache.c b/lib/vgcache/vgcache.c
new file mode 100644
index 00000000..68e71e6f
--- /dev/null
+++ b/lib/vgcache/vgcache.c
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2001 Sistina Software (UK) Limited.
+ *
+ * This file is released under the LGPL.
+ *
+ */
+
+#include <stdlib.h>
+#include "vgcache.h"
+#include "hash.h"
+#include "dbg_malloc.h"
+#include "log.h"
+
+static struct hash_table *_vghash;
+
+/* NULL is a special case that returns all devices */
+const char *null_const = "\0";
+
+int vgcache_init()
+{
+ if (!(_vghash = hash_create(128))) {
+ return 0;
+ }
+
+ return 1;
+}
+
+struct list *vgcache_find(const char *vg_name)
+{
+ struct vgname_entry *vgn;
+
+ if (!_vghash)
+ return NULL;
+
+ if (!vg_name)
+ vg_name = null_const;
+
+ if (!(vgn = hash_lookup(_vghash, vg_name)))
+ return NULL;
+
+ return &vgn->pvdevs;
+}
+
+int vgcache_add_entry(const char *vg_name, struct device *dev)
+{
+
+ struct vgname_entry *vgn;
+ struct pvdev_list *pvdev;
+ struct list *pvdh, *pvdevs;
+
+ if (!(pvdevs = vgcache_find(vg_name))) {
+ if (!(vgn = dbg_malloc(sizeof(struct vgname_entry)))) {
+ log_error("struct vgname_entry allocation failed");
+ return 0;
+ }
+
+ pvdevs = &vgn->pvdevs;
+ list_init(pvdevs);
+
+ if (!(vgn->vgname = dbg_strdup(vg_name))) {
+ log_error("vgcache_add: strdup vg_name failed");
+ return 0;
+ }
+
+ if (!hash_insert(_vghash, vg_name, vgn)) {
+ log_error("vgcache_add: hash insertion failed");
+ return 0;
+ }
+ }
+
+ list_iterate(pvdh, pvdevs) {
+ pvdev = list_item(pvdh, struct pvdev_list);
+ if (dev == pvdev->dev)
+ return 1;
+ }
+
+ if (!(pvdev = dbg_malloc(sizeof(struct pvdev_list)))) {
+ log_error("struct pvdev_list allocation failed");
+ return 0;
+ }
+
+ pvdev->dev = dev;
+ list_add(pvdevs, &pvdev->list);
+
+ return 1;
+}
+
+int vgcache_add(const char *vg_name, struct device *dev)
+{
+ if (!_vghash && !vgcache_init())
+ return 0;
+
+ if (vg_name && !vgcache_add_entry(vg_name, dev))
+ return 0;
+
+ return vgcache_add_entry(null_const, dev);
+}
+
+void vgcache_destroy_entry(struct vgname_entry *vgn)
+{
+ struct list *pvdh;
+ struct pvdev_list *pvdev;
+
+ if (vgn) {
+ pvdh = vgn->pvdevs.n;
+ while (pvdh != &vgn->pvdevs) {
+ pvdev = list_item(pvdh, struct pvdev_list);
+ pvdh = pvdh->n;
+ dbg_free(pvdev);
+ }
+ dbg_free(vgn->vgname);
+ }
+ dbg_free(vgn);
+}
+
+void vgcache_del(const char *vg_name)
+{
+ struct vgname_entry *vgn;
+
+ if (!_vghash)
+ return;
+
+ if (!vg_name)
+ vg_name = null_const;
+
+ if (!(vgn = hash_lookup(_vghash, vg_name)))
+ return;
+
+ hash_remove(_vghash, vg_name);
+ vgcache_destroy_entry(vgn);
+}
+
+void vgcache_destroy()
+{
+ if (_vghash) {
+ hash_iterate(_vghash, (iterate_fn)vgcache_destroy_entry);
+ hash_destroy(_vghash);
+ }
+}
diff --git a/lib/vgcache/vgcache.h b/lib/vgcache/vgcache.h
new file mode 100644
index 00000000..caa7815e
--- /dev/null
+++ b/lib/vgcache/vgcache.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2001 Sistina Software (UK) Limited.
+ *
+ * This file is released under the LGPL.
+ *
+ */
+
+#ifndef _LVM_VGCACHE_H
+#define _LVM_VGCACHE_H
+
+#include <sys/types.h>
+#include <asm/page.h>
+#include "dev-cache.h"
+#include "list.h"
+
+struct vgname_entry {
+ struct list pvdevs;
+ char *vgname;
+};
+
+struct pvdev_list {
+ struct list list;
+ struct device *dev;
+};
+
+int vgcache_init();
+void vgcache_destroy();
+
+/* Return list of PVs in named VG */
+struct list *vgcache_find(const char *vg_name);
+
+/* Add/delete a device */
+int vgcache_add(const char *vg_name, struct device *dev);
+void vgcache_del(const char *vg_name);
+
+#endif
diff --git a/tools/lvm.c b/tools/lvm.c
index f1424463..f10101b3 100644
--- a/tools/lvm.c
+++ b/tools/lvm.c
@@ -29,6 +29,7 @@
#include <ctype.h>
#include "stub.h"
+#include "vgcache.h"
#ifdef READLINE_SUPPORT
#include <readline/readline.h>
@@ -792,6 +793,7 @@ static void fin(void)
fid->ops->destroy(fid);
cmd->filter->destroy(cmd->filter);
pool_destroy(cmd->mem);
+ vgcache_destroy();
dev_cache_exit();
destroy_config_file(cmd->cf);
dbg_free(cmd);
diff --git a/tools/vgscan.c b/tools/vgscan.c
index 420fa29d..5ca6999c 100644
--- a/tools/vgscan.c
+++ b/tools/vgscan.c
@@ -29,8 +29,11 @@ int vgscan(int argc, char **argv)
return EINVALID_CMD_LINE;
}
- log_verbose("Wiping cache of LVM-capable devices");
- persistent_filter_wipe(fid->cmd->filter);
+ log_verbose("Wiping cache of LVM-capable devices");
+ persistent_filter_wipe(fid->cmd->filter);
+
+ log_verbose("Wiping internal cache of PVs in VGs");
+ vgcache_destroy();
log_print("Reading all physical volumes (this may take a while...)");
@@ -62,9 +65,7 @@ static int vgscan_single(const char *vg_name)
}
*********/
-
-
- log_print("%d logical volumes in volume group %s activated",
+ log_print("%d logical volumes in volume group %s activated",
activate_lvs_in_vg(vg), vg_name);
return 0;