From 6407d184d1d4ce491035fe4d9e3ebbe6aefaa394 Mon Sep 17 00:00:00 2001 From: Alasdair G Kergon Date: Wed, 18 Mar 2015 23:43:02 +0000 Subject: [PATCH] cache: Store metadata size and checksum. Refactor the recent metadata-reading optimisation patches. Remove the recently-added cache fields from struct labeller and struct format_instance. Instead, introduce struct lvmcache_vgsummary to wrap the VG information that lvmcache holds and add the metadata size and checksum to it. Allow this VG summary information to be looked up by metadata size + checksum. Adjust the debug log messages to make it clear when this shortcut has been successful. (This changes the optimisation slightly, and might be extendable further.) Add struct cached_vg_fmtdata to format-specific vg_read calls to preserve state alongside the VG across separate calls and indicate if the details supplied match, avoiding the need to read and process the VG metadata again. --- WHATS_NEW | 3 +- lib/cache/lvmcache.c | 78 ++++++++++++++++--- lib/cache/lvmcache.h | 13 +++- lib/cache/lvmetad.c | 4 +- lib/config/config.c | 6 +- lib/format1/format1.c | 2 + lib/format_pool/format_pool.c | 2 + lib/format_text/archiver.c | 2 +- lib/format_text/format-text.c | 127 +++++++++++++++++-------------- lib/format_text/import-export.h | 26 ++++--- lib/format_text/import.c | 76 ++++++++++-------- lib/format_text/import_vsn1.c | 23 +++--- lib/format_text/layout.h | 12 +-- lib/format_text/text_label.c | 12 +-- lib/label/label.c | 24 +++--- lib/label/label.h | 8 -- lib/metadata/metadata-exported.h | 5 -- lib/metadata/metadata.c | 39 ++++++++-- lib/metadata/metadata.h | 7 +- 19 files changed, 288 insertions(+), 181 deletions(-) diff --git a/WHATS_NEW b/WHATS_NEW index b1829250d..c9242feb6 100644 --- a/WHATS_NEW +++ b/WHATS_NEW @@ -1,5 +1,6 @@ Version 2.02.118 - ================================= + Store metadata size + checksum in lvmcache and add struct lvmcache_vgsummary. Remove inaccessible clustered PVs from 'pvs -a'. Don't invalidate cached orphan information while global lock is held. Avoid rescan of all devices when requested pvscan for removed device. @@ -9,7 +10,7 @@ Version 2.02.118 - Add After=iscsi-shutdown.service to blk-availability.service systemd unit. Disallow vgconvert from changing metadata format when lvmetad is used. Don't do a full read of VG when creating a new VG with an existing name. - Reduce number of VG metadata parsing when looking for vgname on a PV. + Reduce amount of VG metadata parsing when looking for vgname on a PV. Avoid reparsing same metadata when reading same metadata from multiple PVs. Save extra device open/close when scanning device for size. Fix seg_monitor field to report status also for mirrors and thick snapshots. diff --git a/lib/cache/lvmcache.c b/lib/cache/lvmcache.c index b8096d7a2..2b4e2c618 100644 --- a/lib/cache/lvmcache.c +++ b/lib/cache/lvmcache.c @@ -56,6 +56,8 @@ struct lvmcache_vginfo { char _padding[7]; struct lvmcache_vginfo *next; /* Another VG with same name? */ char *creation_host; + uint32_t mda_checksum; + size_t mda_size; size_t vgmetadata_size; char *vgmetadata; /* Copy of VG metadata as format_text string */ struct dm_config_tree *cft; /* Config tree created from vgmetadata */ @@ -1406,6 +1408,26 @@ static int _lvmcache_update_vgstatus(struct lvmcache_info *info, uint32_t vgstat return 1; } +static int _lvmcache_update_vg_mda_info(struct lvmcache_info *info, uint32_t mda_checksum, + size_t mda_size) +{ + if (!info || !info->vginfo || !mda_size) + return 1; + + if (info->vginfo->mda_checksum == mda_checksum || info->vginfo->mda_size == mda_size) + return 1; + + info->vginfo->mda_checksum = mda_checksum; + info->vginfo->mda_size = mda_size; + + /* FIXME Add checksum index */ + + log_debug_cache("lvmcache: %s: VG %s: Stored metadata checksum %" PRIu32 " with size %" PRIsize_t ".", + dev_name(info->dev), info->vginfo->vgname, mda_checksum, mda_size); + + return 1; +} + int lvmcache_add_orphan_vginfo(const char *vgname, struct format_type *fmt) { if (!_lock_hash && !lvmcache_init()) { @@ -1416,10 +1438,11 @@ int lvmcache_add_orphan_vginfo(const char *vgname, struct format_type *fmt) return _lvmcache_update_vgname(NULL, vgname, vgname, 0, "", fmt); } -int lvmcache_update_vgname_and_id(struct lvmcache_info *info, - const char *vgname, const char *vgid, - uint32_t vgstatus, const char *creation_host) +int lvmcache_update_vgname_and_id(struct lvmcache_info *info, struct lvmcache_vgsummary *vgsummary) { + const char *vgname = vgsummary->vgname; + const char *vgid = (char *)&vgsummary->vgid; + if (!vgname && !info->vginfo) { log_error(INTERNAL_ERROR "NULL vgname handed to cache"); /* FIXME Remove this */ @@ -1447,10 +1470,11 @@ int lvmcache_update_vgname_and_id(struct lvmcache_info *info, if (!is_orphan_vg(vgname)) info->status &= ~CACHE_INVALID; - if (!_lvmcache_update_vgname(info, vgname, vgid, vgstatus, - creation_host, info->fmt) || + if (!_lvmcache_update_vgname(info, vgname, vgid, vgsummary->vgstatus, + vgsummary->creation_host, info->fmt) || !_lvmcache_update_vgid(info, info->vginfo, vgid) || - !_lvmcache_update_vgstatus(info, vgstatus, creation_host)) + !_lvmcache_update_vgstatus(info, vgsummary->vgstatus, vgsummary->creation_host) || + !_lvmcache_update_vg_mda_info(info, vgsummary->mda_checksum, vgsummary->mda_size)) return_0; return 1; @@ -1461,6 +1485,11 @@ int lvmcache_update_vg(struct volume_group *vg, unsigned precommitted) struct pv_list *pvl; struct lvmcache_info *info; char pvid_s[ID_LEN + 1] __attribute__((aligned(8))); + struct lvmcache_vgsummary vgsummary = { + .vgname = vg->name, + .vgstatus = vg->status, + .vgid = vg->id + }; pvid_s[sizeof(pvid_s) - 1] = '\0'; @@ -1468,9 +1497,7 @@ int lvmcache_update_vg(struct volume_group *vg, unsigned precommitted) strncpy(pvid_s, (char *) &pvl->pv->id, sizeof(pvid_s) - 1); /* FIXME Could pvl->pv->dev->pvid ever be different? */ if ((info = lvmcache_info_from_pvid(pvid_s, 0)) && - !lvmcache_update_vgname_and_id(info, vg->name, - (char *) &vg->id, - vg->status, NULL)) + !lvmcache_update_vgname_and_id(info, &vgsummary)) return_0; } @@ -1512,6 +1539,13 @@ struct lvmcache_info *lvmcache_add(struct labeller *labeller, const char *pvid, struct label *label; struct lvmcache_info *existing, *info; char pvid_s[ID_LEN + 1] __attribute__((aligned(8))); + struct lvmcache_vgsummary vgsummary = { + .vgname = vgname, + .vgstatus = vgstatus, + }; + + if (vgid) + strncpy((char *)&vgsummary.vgid, vgid, sizeof(vgsummary.vgid)); if (!_vgname_hash && !lvmcache_init()) { log_error("Internal cache initialisation failed"); @@ -1610,7 +1644,7 @@ struct lvmcache_info *lvmcache_add(struct labeller *labeller, const char *pvid, return NULL; } - if (!lvmcache_update_vgname_and_id(info, vgname, vgid, vgstatus, NULL)) { + if (!lvmcache_update_vgname_and_id(info, &vgsummary)) { if (!existing) { dm_hash_remove(_pvid_hash, pvid_s); strcpy(info->dev->pvid, ""); @@ -2019,3 +2053,27 @@ uint64_t lvmcache_smallest_mda_size(struct lvmcache_info *info) const struct format_type *lvmcache_fmt(struct lvmcache_info *info) { return info->fmt; } + +int lvmcache_lookup_mda(struct lvmcache_vgsummary *vgsummary) +{ + struct lvmcache_vginfo *vginfo; + + if (!vgsummary->mda_size) + return 0; + + /* FIXME Index the checksums */ + dm_list_iterate_items(vginfo, &_vginfos) { + if (vgsummary->mda_checksum == vginfo->mda_checksum && + vgsummary->mda_size == vginfo->mda_size && + !is_orphan_vg(vginfo->vgname)) { + vgsummary->vgname = vginfo->vgname; + vgsummary->creation_host = vginfo->creation_host; + vgsummary->vgstatus = vginfo->status; + memcpy((char *)&vgsummary->vgid, vginfo->vgid, sizeof(vginfo->vgid)); + + return 1; + } + } + + return 0; +} diff --git a/lib/cache/lvmcache.h b/lib/cache/lvmcache.h index 0a7d8987a..6c4c927ed 100644 --- a/lib/cache/lvmcache.h +++ b/lib/cache/lvmcache.h @@ -39,6 +39,15 @@ struct disk_locn; struct lvmcache_vginfo; +struct lvmcache_vgsummary { + const char *vgname; + struct id vgid; + uint64_t vgstatus; + char *creation_host; + uint32_t mda_checksum; + size_t mda_size; +}; + int lvmcache_init(void); void lvmcache_allow_reads_with_lvmetad(void); @@ -58,8 +67,7 @@ void lvmcache_del(struct lvmcache_info *info); /* Update things */ int lvmcache_update_vgname_and_id(struct lvmcache_info *info, - const char *vgname, const char *vgid, - uint32_t vgstatus, const char *hostname); + struct lvmcache_vgsummary *vgsummary); int lvmcache_update_vg(struct volume_group *vg, unsigned precommitted); void lvmcache_lock_vgname(const char *vgname, int read_only); @@ -68,6 +76,7 @@ int lvmcache_verify_lock_order(const char *vgname); /* Queries */ const struct format_type *lvmcache_fmt_from_vgname(struct cmd_context *cmd, const char *vgname, const char *vgid, unsigned revalidate_labels); +int lvmcache_lookup_mda(struct lvmcache_vgsummary *vgsummary); /* Decrement and test if there are still vg holders in vginfo. */ int lvmcache_vginfo_holders_dec_and_test_for_zero(struct lvmcache_vginfo *vginfo); diff --git a/lib/cache/lvmetad.c b/lib/cache/lvmetad.c index d878c174d..7bb67017d 100644 --- a/lib/cache/lvmetad.c +++ b/lib/cache/lvmetad.c @@ -897,7 +897,7 @@ static int _lvmetad_pvscan_single(struct metadata_area *mda, void *baton) struct _lvmetad_pvscan_baton *b = baton; struct volume_group *this; - this = mda_is_ignored(mda) ? NULL : mda->ops->vg_read(b->fid, "", mda, 1); + this = mda_is_ignored(mda) ? NULL : mda->ops->vg_read(b->fid, "", mda, NULL, NULL, 1); /* FIXME Also ensure contents match etc. */ if (!b->vg || this->seqno > b->vg->seqno) @@ -960,7 +960,7 @@ int lvmetad_pvscan_single(struct cmd_context *cmd, struct device *dev, * can scan further devices. */ if (!baton.vg && !(baton.fid->fmt->features & FMT_MDAS)) - baton.vg = ((struct metadata_area *) dm_list_first(&baton.fid->metadata_areas_in_use))->ops->vg_read(baton.fid, lvmcache_vgname_from_info(info), NULL, 1); + baton.vg = ((struct metadata_area *) dm_list_first(&baton.fid->metadata_areas_in_use))->ops->vg_read(baton.fid, lvmcache_vgname_from_info(info), NULL, NULL, NULL, 1); if (!baton.vg) lvmcache_fmt(info)->ops->destroy_instance(baton.fid); diff --git a/lib/config/config.c b/lib/config/config.c index b63565498..58eab5159 100644 --- a/lib/config/config.c +++ b/lib/config/config.c @@ -482,14 +482,14 @@ int override_config_tree_from_profile(struct cmd_context *cmd, } /* - * When skip_parse is set, the checksum of buffer is only matched + * When checksum_only is set, the checksum of buffer is only matched * and function avoids parsing of mda into config tree which * remains unmodified and should not be used. */ int config_file_read_fd(struct dm_config_tree *cft, struct device *dev, off_t offset, size_t size, off_t offset2, size_t size2, checksum_fn_t checksum_fn, uint32_t checksum, - int skip_parse) + int checksum_only) { char *fb, *fe; int r = 0; @@ -538,7 +538,7 @@ int config_file_read_fd(struct dm_config_tree *cft, struct device *dev, goto out; } - if (!skip_parse) { + if (!checksum_only) { fe = fb + size + size2; if (!dm_config_parse(cft, fb, fe)) goto_out; diff --git a/lib/format1/format1.c b/lib/format1/format1.c index 19df8aa7a..1b86ef5d6 100644 --- a/lib/format1/format1.c +++ b/lib/format1/format1.c @@ -180,6 +180,8 @@ out: static struct volume_group *_format1_vg_read(struct format_instance *fid, const char *vg_name, struct metadata_area *mda __attribute__((unused)), + struct cached_vg_fmtdata **vg_fmtdata __attribute__((unused)), + unsigned *use_previous_vg __attribute__((unused)), int single_device __attribute__((unused))) { struct volume_group *vg; diff --git a/lib/format_pool/format_pool.c b/lib/format_pool/format_pool.c index 503005a7e..2a8819d5d 100644 --- a/lib/format_pool/format_pool.c +++ b/lib/format_pool/format_pool.c @@ -101,6 +101,8 @@ static int _check_usp(const char *vgname, struct user_subpool *usp, int sp_count static struct volume_group *_pool_vg_read(struct format_instance *fid, const char *vg_name, struct metadata_area *mda __attribute__((unused)), + struct cached_vg_fmtdata **vg_fmtdata __attribute__((unused)), + unsigned *use_previous_vg __attribute__((unused)), int single_device __attribute__((unused))) { struct volume_group *vg; diff --git a/lib/format_text/archiver.c b/lib/format_text/archiver.c index a2f40f2df..e3d3d570c 100644 --- a/lib/format_text/archiver.c +++ b/lib/format_text/archiver.c @@ -308,7 +308,7 @@ struct volume_group *backup_read_vg(struct cmd_context *cmd, } dm_list_iterate_items(mda, &tf->metadata_areas_in_use) { - if (!(vg = mda->ops->vg_read(tf, vg_name, mda, 0))) + if (!(vg = mda->ops->vg_read(tf, vg_name, mda, NULL, NULL, 0))) stack; break; } diff --git a/lib/format_text/format-text.c b/lib/format_text/format-text.c index d80ebf6d3..6b09d0096 100644 --- a/lib/format_text/format-text.c +++ b/lib/format_text/format-text.c @@ -412,6 +412,11 @@ static struct raw_locn *_find_vg_rlocn(struct device_area *dev_area, char vgnamebuf[NAME_LEN + 2] __attribute__((aligned(8))); struct raw_locn *rlocn, *rlocn_precommitted; struct lvmcache_info *info; + struct lvmcache_vgsummary vgsummary_orphan = { + .vgname = FMT_TEXT_ORPHAN_VG_NAME, + }; + + memcpy(&vgsummary_orphan.vgid, FMT_TEXT_ORPHAN_VG_NAME, sizeof(FMT_TEXT_ORPHAN_VG_NAME)); rlocn = mdah->raw_locns; /* Slot 0 */ rlocn_precommitted = rlocn + 1; /* Slot 1 */ @@ -449,8 +454,7 @@ static struct raw_locn *_find_vg_rlocn(struct device_area *dev_area, bad: if ((info = lvmcache_info_from_pvid(dev_area->dev->pvid, 0))) - lvmcache_update_vgname_and_id(info, FMT_TEXT_ORPHAN_VG_NAME, - FMT_TEXT_ORPHAN_VG_NAME, 0, NULL); + lvmcache_update_vgname_and_id(info, &vgsummary_orphan); return NULL; } @@ -498,6 +502,8 @@ static int _raw_holds_vgname(struct format_instance *fid, static struct volume_group *_vg_read_raw_area(struct format_instance *fid, const char *vgname, struct device_area *area, + struct cached_vg_fmtdata **vg_fmtdata, + unsigned *use_previous_vg, int precommitted, int single_device) { @@ -526,17 +532,24 @@ static struct volume_group *_vg_read_raw_area(struct format_instance *fid, } /* FIXME 64-bit */ - if (!(vg = text_vg_import_fd(fid, NULL, single_device, area->dev, + if (!(vg = text_vg_import_fd(fid, NULL, vg_fmtdata, use_previous_vg, single_device, area->dev, (off_t) (area->start + rlocn->offset), (uint32_t) (rlocn->size - wrap), (off_t) (area->start + MDA_HEADER_SIZE), wrap, calc_crc, rlocn->checksum, &when, - &desc))) + &desc)) && (!use_previous_vg || !*use_previous_vg)) goto_out; - log_debug_metadata("Read %s %smetadata (%u) from %s at %" PRIu64 " size %" - PRIu64, vg->name, precommitted ? "pre-commit " : "", - vg->seqno, dev_name(area->dev), - area->start + rlocn->offset, rlocn->size); + + if (vg) + log_debug_metadata("Read %s %smetadata (%u) from %s at %" PRIu64 " size %" + PRIu64, vg->name, precommitted ? "pre-commit " : "", + vg->seqno, dev_name(area->dev), + area->start + rlocn->offset, rlocn->size); + else + log_debug_metadata("Skipped reading %smetadata from %s at %" PRIu64 " size %" + PRIu64 " with matching checksum.", precommitted ? "pre-commit " : "", + dev_name(area->dev), + area->start + rlocn->offset, rlocn->size); if (precommitted) vg->status |= PRECOMMITTED; @@ -548,6 +561,8 @@ static struct volume_group *_vg_read_raw_area(struct format_instance *fid, static struct volume_group *_vg_read_raw(struct format_instance *fid, const char *vgname, struct metadata_area *mda, + struct cached_vg_fmtdata **vg_fmtdata, + unsigned *use_previous_vg, int single_device) { struct mda_context *mdac = (struct mda_context *) mda->metadata_locn; @@ -556,7 +571,7 @@ static struct volume_group *_vg_read_raw(struct format_instance *fid, if (!dev_open_readonly(mdac->area.dev)) return_NULL; - vg = _vg_read_raw_area(fid, vgname, &mdac->area, 0, single_device); + vg = _vg_read_raw_area(fid, vgname, &mdac->area, vg_fmtdata, use_previous_vg, 0, single_device); if (!dev_close(mdac->area.dev)) stack; @@ -566,7 +581,9 @@ static struct volume_group *_vg_read_raw(struct format_instance *fid, static struct volume_group *_vg_read_precommit_raw(struct format_instance *fid, const char *vgname, - struct metadata_area *mda) + struct metadata_area *mda, + struct cached_vg_fmtdata **vg_fmtdata, + unsigned *use_previous_vg) { struct mda_context *mdac = (struct mda_context *) mda->metadata_locn; struct volume_group *vg; @@ -574,7 +591,7 @@ static struct volume_group *_vg_read_precommit_raw(struct format_instance *fid, if (!dev_open_readonly(mdac->area.dev)) return_NULL; - vg = _vg_read_raw_area(fid, vgname, &mdac->area, 1, 0); + vg = _vg_read_raw_area(fid, vgname, &mdac->area, vg_fmtdata, use_previous_vg, 1, 0); if (!dev_close(mdac->area.dev)) stack; @@ -885,6 +902,8 @@ static struct volume_group *_vg_read_file_name(struct format_instance *fid, static struct volume_group *_vg_read_file(struct format_instance *fid, const char *vgname, struct metadata_area *mda, + struct cached_vg_fmtdata **vg_fmtdata, + unsigned *use_previous_vg __attribute__((unused)), int single_device __attribute__((unused))) { struct text_context *tc = (struct text_context *) mda->metadata_locn; @@ -894,7 +913,9 @@ static struct volume_group *_vg_read_file(struct format_instance *fid, static struct volume_group *_vg_read_precommit_file(struct format_instance *fid, const char *vgname, - struct metadata_area *mda) + struct metadata_area *mda, + struct cached_vg_fmtdata **vg_fmtdata, + unsigned *use_previous_vg __attribute__((unused))) { struct text_context *tc = (struct text_context *) mda->metadata_locn; struct volume_group *vg; @@ -1123,12 +1144,9 @@ static int _scan_file(const struct format_type *fmt, const char *vgname) return 1; } -const char *vgname_from_mda(const struct format_type *fmt, - struct mda_header *mdah, struct device_area *dev_area, - uint32_t *mda_checksum, size_t *mda_size, - const char *vgname, struct id *vgid, - uint64_t *vgstatus, char **creation_host, - uint64_t *mda_free_sectors) +int vgname_from_mda(const struct format_type *fmt, + struct mda_header *mdah, struct device_area *dev_area, + struct lvmcache_vgsummary *vgsummary, uint64_t *mda_free_sectors) { struct raw_locn *rlocn; uint32_t wrap = 0; @@ -1136,13 +1154,14 @@ const char *vgname_from_mda(const struct format_type *fmt, char buf[NAME_LEN + 1] __attribute__((aligned(8))); char uuid[64] __attribute__((aligned(8))); uint64_t buffer_size, current_usage; + unsigned used_cached_metadata = 0; if (mda_free_sectors) *mda_free_sectors = ((dev_area->size - MDA_HEADER_SIZE) / 2) >> SECTOR_SHIFT; if (!mdah) { log_error(INTERNAL_ERROR "vgname_from_mda called with NULL pointer for mda_header"); - return NULL; + return 0; } /* FIXME Cope with returning a list */ @@ -1154,13 +1173,13 @@ const char *vgname_from_mda(const struct format_type *fmt, if (!rlocn->offset) { log_debug("%s: found metadata with offset 0.", dev_name(dev_area->dev)); - return NULL; + return 0; } /* Do quick check for a vgname */ if (!dev_read(dev_area->dev, dev_area->start + rlocn->offset, NAME_LEN, buf)) - return_NULL; + return_0; while (buf[len] && !isspace(buf[len]) && buf[len] != '{' && len < (NAME_LEN - 1)) @@ -1170,7 +1189,7 @@ const char *vgname_from_mda(const struct format_type *fmt, /* Ignore this entry if the characters aren't permissible */ if (!validate_name(buf)) - return_NULL; + return_0; /* We found a VG - now check the metadata */ if (rlocn->offset + rlocn->size > mdah->size) @@ -1179,37 +1198,39 @@ const char *vgname_from_mda(const struct format_type *fmt, if (wrap > rlocn->offset) { log_error("%s: metadata too large for circular buffer", dev_name(dev_area->dev)); - return NULL; + return 0; } - /* Check if it could be the same VG */ - if ((rlocn->checksum != *mda_checksum) || (rlocn->size != *mda_size)) - vgname = NULL; /* nope, reset to NULL */ + /* Did we see this metadata before? */ + vgsummary->mda_checksum = rlocn->checksum; + vgsummary->mda_size = rlocn->size; + + if (lvmcache_lookup_mda(vgsummary)) + used_cached_metadata = 1; /* FIXME 64-bit */ - if (!(vgname = text_vgname_import(fmt, dev_area->dev, - (off_t) (dev_area->start + - rlocn->offset), - (uint32_t) (rlocn->size - wrap), - (off_t) (dev_area->start + - MDA_HEADER_SIZE), - wrap, calc_crc, rlocn->checksum, - vgname, - vgid, vgstatus, creation_host))) - return_NULL; + if (!text_vgname_import(fmt, dev_area->dev, + (off_t) (dev_area->start + rlocn->offset), + (uint32_t) (rlocn->size - wrap), + (off_t) (dev_area->start + MDA_HEADER_SIZE), + wrap, calc_crc, vgsummary->vgname ? 1 : 0, + vgsummary)) + return_0; /* Ignore this entry if the characters aren't permissible */ - if (!validate_name(vgname)) - return_NULL; + if (!validate_name(vgsummary->vgname)) + return_0; - if (!id_write_format(vgid, uuid, sizeof(uuid))) - return_NULL; + if (!id_write_format((struct id *)&vgsummary->vgid, uuid, sizeof(uuid))) + return_0; - log_debug_metadata("%s: Found metadata at %" PRIu64 " size %" PRIu64 + log_debug_metadata("%s: %s metadata at %" PRIu64 " size %" PRIu64 " (in area at %" PRIu64 " size %" PRIu64 ") for %s (%s)", - dev_name(dev_area->dev), dev_area->start + rlocn->offset, - rlocn->size, dev_area->start, dev_area->size, vgname, uuid); + dev_name(dev_area->dev), + used_cached_metadata ? "Using cached" : "Found", + dev_area->start + rlocn->offset, + rlocn->size, dev_area->start, dev_area->size, vgsummary->vgname, uuid); if (mda_free_sectors) { current_usage = (rlocn->size + SECTOR_SIZE - UINT64_C(1)) - @@ -1222,24 +1243,17 @@ const char *vgname_from_mda(const struct format_type *fmt, *mda_free_sectors = ((buffer_size - 2 * current_usage) / 2) >> SECTOR_SHIFT; } - *mda_checksum = rlocn->checksum; - *mda_size = rlocn->size; - - return vgname; + return 1; } static int _scan_raw(const struct format_type *fmt, const char *vgname __attribute__((unused))) { struct raw_list *rl; struct dm_list *raw_list; - const char *scanned_vgname; struct volume_group *vg; struct format_instance fid; - struct id vgid; - uint64_t vgstatus; + struct lvmcache_vgsummary vgsummary = { 0 }; struct mda_header *mdah; - uint32_t mda_checksum = 0; - size_t mda_size = 0; raw_list = &((struct mda_lists *) fmt->private)->raws; @@ -1260,15 +1274,10 @@ static int _scan_raw(const struct format_type *fmt, const char *vgname __attribu } /* TODO: caching as in vgname_from_mda() (trigger this code?) */ - if ((scanned_vgname = vgname_from_mda(fmt, mdah, - &rl->dev_area, - &mda_checksum, &mda_size, NULL, - &vgid, &vgstatus, - NULL, NULL))) { - vg = _vg_read_raw_area(&fid, scanned_vgname, &rl->dev_area, 0, 0); + if (vgname_from_mda(fmt, mdah, &rl->dev_area, &vgsummary, NULL)) { + vg = _vg_read_raw_area(&fid, vgsummary.vgname, &rl->dev_area, NULL, NULL, 0, 0); if (vg) lvmcache_update_vg(vg, 0); - } close_dev: if (!dev_close(rl->dev_area.dev)) diff --git a/lib/format_text/import-export.h b/lib/format_text/import-export.h index 4b2756d5b..1ee647ba5 100644 --- a/lib/format_text/import-export.h +++ b/lib/format_text/import-export.h @@ -18,6 +18,7 @@ #include "config.h" #include "metadata.h" +#include "lvmcache.h" #include @@ -49,10 +50,9 @@ struct text_vg_version_ops { unsigned use_cached_pvs); void (*read_desc) (struct dm_pool * mem, const struct dm_config_tree *cf, time_t *when, char **desc); - const char *(*read_vgname) (const struct format_type *fmt, - const struct dm_config_tree *cft, - struct id *vgid, uint64_t *vgstatus, - char **creation_host); + int (*read_vgname) (const struct format_type *fmt, + const struct dm_config_tree *cft, + struct lvmcache_vgsummary *vgsummary); }; struct text_vg_version_ops *text_vg_vsn1_init(void); @@ -70,6 +70,8 @@ struct volume_group *text_vg_import_file(struct format_instance *fid, time_t *when, char **desc); struct volume_group *text_vg_import_fd(struct format_instance *fid, const char *file, + struct cached_vg_fmtdata **vg_fmtdata, + unsigned *use_previous_vg, int single_device, struct device *dev, off_t offset, uint32_t size, @@ -77,13 +79,13 @@ struct volume_group *text_vg_import_fd(struct format_instance *fid, checksum_fn_t checksum_fn, uint32_t checksum, time_t *when, char **desc); -const char *text_vgname_import(const struct format_type *fmt, - struct device *dev, - off_t offset, uint32_t size, - off_t offset2, uint32_t size2, - checksum_fn_t checksum_fn, uint32_t checksum, - const char *vgname, - struct id *vgid, uint64_t *vgstatus, - char **creation_host); + +int text_vgname_import(const struct format_type *fmt, + struct device *dev, + off_t offset, uint32_t size, + off_t offset2, uint32_t size2, + checksum_fn_t checksum_fn, + int checksum_only, + struct lvmcache_vgsummary *vgsummary); #endif diff --git a/lib/format_text/import.c b/lib/format_text/import.c index c8ba5ce6d..84230cb35 100644 --- a/lib/format_text/import.c +++ b/lib/format_text/import.c @@ -34,36 +34,38 @@ static void _init_text_import(void) /* * Find out vgname on a given device. - * If the checksum and metadata size is matching the vgname discovered in last read - * (multi PVs VG) could be passed back and it may skip parsing of such metadata. */ -const char *text_vgname_import(const struct format_type *fmt, - struct device *dev, - off_t offset, uint32_t size, - off_t offset2, uint32_t size2, - checksum_fn_t checksum_fn, uint32_t checksum, - const char *vgname, - struct id *vgid, uint64_t *vgstatus, - char **creation_host) +int text_vgname_import(const struct format_type *fmt, + struct device *dev, + off_t offset, uint32_t size, + off_t offset2, uint32_t size2, + checksum_fn_t checksum_fn, + int checksum_only, + struct lvmcache_vgsummary *vgsummary) { struct dm_config_tree *cft; struct text_vg_version_ops **vsn; + int r = 0; _init_text_import(); if (!(cft = config_open(CONFIG_FILE_SPECIAL, NULL, 0))) - return_NULL; + return_0; if ((!dev && !config_file_read(cft)) || (dev && !config_file_read_fd(cft, dev, offset, size, - offset2, size2, checksum_fn, checksum, - (vgname != NULL)))) { + offset2, size2, checksum_fn, + vgsummary->mda_checksum, + checksum_only))) { log_error("Couldn't read volume group metadata."); goto out; } - if (vgname) - goto out; /* Everything is matching from the last call */ + if (checksum_only) { + /* Checksum matches already-cached content - no need to reparse. */ + r = 1; + goto out; + } /* * Find a set of version functions that can read this file @@ -72,20 +74,27 @@ const char *text_vgname_import(const struct format_type *fmt, if (!(*vsn)->check_version(cft)) continue; - if (!(vgname = (*vsn)->read_vgname(fmt, cft, vgid, vgstatus, - creation_host))) + if (!(*vsn)->read_vgname(fmt, cft, vgsummary)) goto_out; + r = 1; break; } out: config_destroy(cft); - return vgname; + return r; } +struct cached_vg_fmtdata { + uint32_t cached_mda_checksum; + size_t cached_mda_size; +}; + struct volume_group *text_vg_import_fd(struct format_instance *fid, const char *file, + struct cached_vg_fmtdata **vg_fmtdata, + unsigned *use_previous_vg, int single_device, struct device *dev, off_t offset, uint32_t size, @@ -99,6 +108,12 @@ struct volume_group *text_vg_import_fd(struct format_instance *fid, struct text_vg_version_ops **vsn; int skip_parse; + if (vg_fmtdata && !*vg_fmtdata && + !(*vg_fmtdata = dm_pool_zalloc(fid->mem, sizeof(**vg_fmtdata)))) { + log_error("Failed to allocate VG fmtdata for text format."); + return NULL; + } + _init_text_import(); *desc = NULL; @@ -107,8 +122,10 @@ struct volume_group *text_vg_import_fd(struct format_instance *fid, if (!(cft = config_open(CONFIG_FILE_SPECIAL, file, 0))) return_NULL; - skip_parse = fid->vg && (fid->mda_checksum == checksum) && - (fid->mda_size == (size + size2)); + /* Does the metadata match the already-cached VG? */ + skip_parse = vg_fmtdata && + ((*vg_fmtdata)->cached_mda_checksum == checksum) && + ((*vg_fmtdata)->cached_mda_size == (size + size2)); if ((!dev && !config_file_read(cft)) || (dev && !config_file_read_fd(cft, dev, offset, size, @@ -117,7 +134,8 @@ struct volume_group *text_vg_import_fd(struct format_instance *fid, goto_out; if (skip_parse) { - vg = fid->vg; + if (use_previous_vg) + *use_previous_vg = 1; goto out; } @@ -135,16 +153,14 @@ struct volume_group *text_vg_import_fd(struct format_instance *fid, break; } - if (vg && (!fid->vg || (vg->seqno > fid->vg->seqno))) { - /* - * Remember vg pointer to newest VG for reuse. - * NOTE: _vg_read() will not release same VG - */ - fid->vg = vg; - fid->mda_size = (size + size2); - fid->mda_checksum = checksum; + if (vg && vg_fmtdata && *vg_fmtdata) { + (*vg_fmtdata)->cached_mda_size = (size + size2); + (*vg_fmtdata)->cached_mda_checksum = checksum; } + if (use_previous_vg) + *use_previous_vg = 0; + out: config_destroy(cft); return vg; @@ -154,7 +170,7 @@ struct volume_group *text_vg_import_file(struct format_instance *fid, const char *file, time_t *when, char **desc) { - return text_vg_import_fd(fid, file, 0, NULL, (off_t)0, 0, (off_t)0, 0, NULL, 0, + return text_vg_import_fd(fid, file, NULL, NULL, 0, NULL, (off_t)0, 0, (off_t)0, 0, NULL, 0, when, desc); } diff --git a/lib/format_text/import_vsn1.c b/lib/format_text/import_vsn1.c index 1569aaa76..5e8e049b2 100644 --- a/lib/format_text/import_vsn1.c +++ b/lib/format_text/import_vsn1.c @@ -941,19 +941,16 @@ static void _read_desc(struct dm_pool *mem, *when = u; } -static const char *_read_vgname(const struct format_type *fmt, - const struct dm_config_tree *cft, struct id *vgid, - uint64_t *vgstatus, char **creation_host) +static int _read_vgname(const struct format_type *fmt, const struct dm_config_tree *cft, + struct lvmcache_vgsummary *vgsummary) { const struct dm_config_node *vgn; struct dm_pool *mem = fmt->cmd->mem; - char *vgname; int old_suppress; old_suppress = log_suppress(2); - *creation_host = dm_pool_strdup(mem, - dm_config_find_str_allow_empty(cft->root, - "creation_host", "")); + vgsummary->creation_host = + dm_pool_strdup(mem, dm_config_find_str_allow_empty(cft->root, "creation_host", "")); log_suppress(old_suppress); /* skip any top-level values */ @@ -964,23 +961,23 @@ static const char *_read_vgname(const struct format_type *fmt, return 0; } - if (!(vgname = dm_pool_strdup(mem, vgn->key))) + if (!(vgsummary->vgname = dm_pool_strdup(mem, vgn->key))) return_0; vgn = vgn->child; - if (!_read_id(vgid, vgn, "id")) { - log_error("Couldn't read uuid for volume group %s.", vgname); + if (!_read_id(&vgsummary->vgid, vgn, "id")) { + log_error("Couldn't read uuid for volume group %s.", vgsummary->vgname); return 0; } - if (!_read_flag_config(vgn, vgstatus, VG_FLAGS)) { + if (!_read_flag_config(vgn, &vgsummary->vgstatus, VG_FLAGS)) { log_error("Couldn't find status flags for volume group %s.", - vgname); + vgsummary->vgname); return 0; } - return vgname; + return 1; } static struct text_vg_version_ops _vsn1_ops = { diff --git a/lib/format_text/layout.h b/lib/format_text/layout.h index 5f6b549f2..64fc0e1ca 100644 --- a/lib/format_text/layout.h +++ b/lib/format_text/layout.h @@ -18,6 +18,7 @@ #include "config.h" #include "metadata.h" +#include "lvmcache.h" #include "uuid.h" /* disk_locn and data_area_list are defined in format-text.h */ @@ -97,13 +98,8 @@ struct mda_context { #define LVM2_LABEL "LVM2 001" #define MDA_SIZE_MIN (8 * (unsigned) lvm_getpagesize()) - -const char *vgname_from_mda(const struct format_type *fmt, - struct mda_header *mdah, - struct device_area *dev_area, - uint32_t *mda_checksum, size_t *mda_size, - const char *vgname, struct id *vgid, - uint64_t *vgstatus, char **creation_host, - uint64_t *mda_free_sectors); +int vgname_from_mda(const struct format_type *fmt, struct mda_header *mdah, + struct device_area *dev_area, struct lvmcache_vgsummary *vgsummary, + uint64_t *mda_free_sectors); #endif diff --git a/lib/format_text/text_label.c b/lib/format_text/text_label.c index 3a4835cea..599be419e 100644 --- a/lib/format_text/text_label.c +++ b/lib/format_text/text_label.c @@ -319,7 +319,7 @@ static int _update_mda(struct metadata_area *mda, void *baton) const struct format_type *fmt = p->label->labeller->fmt; struct mda_context *mdac = (struct mda_context *) mda->metadata_locn; struct mda_header *mdah; - struct labeller *l = p->label->labeller; + struct lvmcache_vgsummary vgsummary = { 0 }; /* * Using the labeller struct to preserve info about @@ -350,13 +350,9 @@ static int _update_mda(struct metadata_area *mda, void *baton) return 1; } - if ((l->vgname = vgname_from_mda(fmt, mdah, &mdac->area, - &l->mda_checksum, &l->mda_size, l->vgname, - &l->vgid, &l->vgstatus, &l->creation_host, - &mdac->free_sectors)) && - !lvmcache_update_vgname_and_id(p->info, l->vgname, - (char *) &l->vgid, l->vgstatus, - l->creation_host)) { + if (vgname_from_mda(fmt, mdah, &mdac->area, &vgsummary, + &mdac->free_sectors) && + !lvmcache_update_vgname_and_id(p->info, &vgsummary)) { if (!dev_close(mdac->area.dev)) stack; return_0; diff --git a/lib/label/label.c b/lib/label/label.c index ce59da8c5..451040087 100644 --- a/lib/label/label.c +++ b/lib/label/label.c @@ -97,6 +97,17 @@ struct labeller *label_get_handler(const char *name) return NULL; } +static void _update_lvmcache_orphan(struct lvmcache_info *info) +{ + struct lvmcache_vgsummary vgsummary_orphan = { + .vgname = lvmcache_fmt(info)->orphan_vg_name, + }; + + memcpy(&vgsummary_orphan.vgid, lvmcache_fmt(info)->orphan_vg_name, strlen(lvmcache_fmt(info)->orphan_vg_name)); + + lvmcache_update_vgname_and_id(info, &vgsummary_orphan); +} + static struct labeller *_find_labeller(struct device *dev, char *buf, uint64_t *label_sector, uint64_t scan_sector) @@ -173,9 +184,7 @@ static struct labeller *_find_labeller(struct device *dev, char *buf, out: if (!found) { if ((info = lvmcache_info_from_pvid(dev->pvid, 0))) - lvmcache_update_vgname_and_id(info, lvmcache_fmt(info)->orphan_vg_name, - lvmcache_fmt(info)->orphan_vg_name, - 0, NULL); + _update_lvmcache_orphan(info); log_very_verbose("%s: No label detected", dev_name(dev)); } @@ -271,9 +280,7 @@ int label_read(struct device *dev, struct label **result, stack; if ((info = lvmcache_info_from_pvid(dev->pvid, 0))) - lvmcache_update_vgname_and_id(info, lvmcache_fmt(info)->orphan_vg_name, - lvmcache_fmt(info)->orphan_vg_name, - 0, NULL); + _update_lvmcache_orphan(info); return r; } @@ -348,10 +355,7 @@ int label_verify(struct device *dev) if (!dev_open_readonly(dev)) { if ((info = lvmcache_info_from_pvid(dev->pvid, 0))) - lvmcache_update_vgname_and_id(info, lvmcache_fmt(info)->orphan_vg_name, - lvmcache_fmt(info)->orphan_vg_name, - 0, NULL); - + _update_lvmcache_orphan(info); return_0; } diff --git a/lib/label/label.h b/lib/label/label.h index 831977fef..253e1e8d6 100644 --- a/lib/label/label.h +++ b/lib/label/label.h @@ -89,14 +89,6 @@ struct label_ops { struct labeller { struct label_ops *ops; const struct format_type *fmt; - - /* Caching info */ - const char *vgname; - struct id vgid; - uint64_t vgstatus; - char *creation_host; - uint32_t mda_checksum; - size_t mda_size; }; int label_init(void); diff --git a/lib/metadata/metadata-exported.h b/lib/metadata/metadata-exported.h index 414216a1d..1555c87e3 100644 --- a/lib/metadata/metadata-exported.h +++ b/lib/metadata/metadata-exported.h @@ -324,11 +324,6 @@ struct format_instance { struct dm_list metadata_areas_ignored; struct dm_hash_table *metadata_areas_index; - /* Remember last vg to avoid parsing same mda content for multiple PVs */ - struct volume_group *vg; - uint32_t mda_checksum; - size_t mda_size; - void *private; }; diff --git a/lib/metadata/metadata.c b/lib/metadata/metadata.c index ed91948e0..33ce3708d 100644 --- a/lib/metadata/metadata.c +++ b/lib/metadata/metadata.c @@ -3248,6 +3248,8 @@ static struct volume_group *_vg_read(struct cmd_context *cmd, char uuid[64] __attribute__((aligned(8))); unsigned seqno = 0; int reappeared = 0; + struct cached_vg_fmtdata *vg_fmtdata = NULL; /* Additional format-specific data about the vg */ + unsigned use_previous_vg; if (is_orphan_vg(vgname)) { if (use_precommitted) { @@ -3334,12 +3336,20 @@ static struct volume_group *_vg_read(struct cmd_context *cmd, /* Ensure contents of all metadata areas match - else do recovery */ inconsistent_mda_count=0; dm_list_iterate_items(mda, &fid->metadata_areas_in_use) { + use_previous_vg = 0; if ((use_precommitted && - !(vg = mda->ops->vg_read_precommit(fid, vgname, mda))) || + !(vg = mda->ops->vg_read_precommit(fid, vgname, mda, &vg_fmtdata, &use_previous_vg)) && !use_previous_vg) || (!use_precommitted && - !(vg = mda->ops->vg_read(fid, vgname, mda, 0)))) { + !(vg = mda->ops->vg_read(fid, vgname, mda, &vg_fmtdata, &use_previous_vg, 0)) && !use_previous_vg)) { inconsistent = 1; + vg_fmtdata = NULL; + continue; + } + + /* Use previous VG because checksum matches */ + if (!vg) { + vg = correct_vg; continue; } @@ -3366,9 +3376,10 @@ static struct volume_group *_vg_read(struct cmd_context *cmd, } } - if (vg != correct_vg) - /* NOTE: tied to fid->vg logic in text_vg_import_fd() */ + if (vg != correct_vg) { release_vg(vg); + vg_fmtdata = NULL; + } } fid->ref_count--; @@ -3484,6 +3495,7 @@ static struct volume_group *_vg_read(struct cmd_context *cmd, * but we failed to do so (so there's a dangling fid now). */ _destroy_fid(&fid); + vg_fmtdata = NULL; inconsistent = 0; @@ -3514,14 +3526,23 @@ static struct volume_group *_vg_read(struct cmd_context *cmd, /* Ensure contents of all metadata areas match - else recover */ inconsistent_mda_count=0; dm_list_iterate_items(mda, &fid->metadata_areas_in_use) { + use_previous_vg = 0; + if ((use_precommitted && - !(vg = mda->ops->vg_read_precommit(fid, vgname, - mda))) || + !(vg = mda->ops->vg_read_precommit(fid, vgname, mda, &vg_fmtdata, &use_previous_vg)) && !use_previous_vg) || (!use_precommitted && - !(vg = mda->ops->vg_read(fid, vgname, mda, 0)))) { + !(vg = mda->ops->vg_read(fid, vgname, mda, &vg_fmtdata, &use_previous_vg, 0)) && !use_previous_vg)) { inconsistent = 1; + vg_fmtdata = NULL; continue; } + + /* Use previous VG because checksum matches */ + if (!vg) { + vg = correct_vg; + continue; + } + if (!correct_vg) { correct_vg = vg; if (!_update_pv_list(cmd->mem, &all_pvs, correct_vg)) { @@ -3564,8 +3585,10 @@ static struct volume_group *_vg_read(struct cmd_context *cmd, } } - if (vg != correct_vg) + if (vg != correct_vg) { release_vg(vg); + vg_fmtdata = NULL; + } } fid->ref_count--; diff --git a/lib/metadata/metadata.h b/lib/metadata/metadata.h index 75abf6378..031d19e5f 100644 --- a/lib/metadata/metadata.h +++ b/lib/metadata/metadata.h @@ -72,6 +72,7 @@ struct dm_config_tree; struct metadata_area; struct alloc_handle; struct lvmcache_info; +struct cached_vg_fmtdata; /* Per-format per-metadata area operations */ struct metadata_area_ops { @@ -79,10 +80,14 @@ struct metadata_area_ops { struct volume_group *(*vg_read) (struct format_instance * fi, const char *vg_name, struct metadata_area * mda, + struct cached_vg_fmtdata **vg_fmtdata, + unsigned *use_previous_vg, int single_device); struct volume_group *(*vg_read_precommit) (struct format_instance * fi, const char *vg_name, - struct metadata_area * mda); + struct metadata_area * mda, + struct cached_vg_fmtdata **vg_fmtdata, + unsigned *use_previous_vg); /* * Write out complete VG metadata. You must ensure internal * consistency before calling. eg. PEs can't refer to PVs not