summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Teigland <teigland@redhat.com>2019-02-06 13:39:41 -0600
committerDavid Teigland <teigland@redhat.com>2019-04-11 12:03:04 -0500
commit77c8e031af7e901e8c30584bb04b44dbadb8cd63 (patch)
treeb3a35db47989ca653b1be908d41525a866d0f1e7
parent92534a31ebaeb5bb2031c898467545b1fa3d5057 (diff)
downloadlvm2-77c8e031af7e901e8c30584bb04b44dbadb8cd63.tar.gz
vgck --updatemetadata is a new command
uses vg_write to correct more common or less severe issues, and also adds the ability to repair some metadata corruption that couldn't be handled previously.
-rw-r--r--lib/format_text/text_label.c3
-rw-r--r--lib/metadata/metadata-exported.h2
-rw-r--r--lib/metadata/metadata.c81
-rw-r--r--lib/metadata/metadata.h1
-rw-r--r--tools/args.h3
-rw-r--r--tools/command-lines.in5
-rw-r--r--tools/vgck.c54
7 files changed, 149 insertions, 0 deletions
diff --git a/lib/format_text/text_label.c b/lib/format_text/text_label.c
index 1b3295076..c37781b75 100644
--- a/lib/format_text/text_label.c
+++ b/lib/format_text/text_label.c
@@ -341,6 +341,9 @@ static int _read_mda_header_and_metadata(struct metadata_area *mda, void *baton)
goto fail;
}
+ if (mda)
+ mda->header_start = mdah->start;
+
mda_set_ignored(mda, rlocn_is_ignored(mdah->raw_locns));
if (mda_is_ignored(mda)) {
diff --git a/lib/metadata/metadata-exported.h b/lib/metadata/metadata-exported.h
index 0683d5f32..e72127101 100644
--- a/lib/metadata/metadata-exported.h
+++ b/lib/metadata/metadata-exported.h
@@ -1383,4 +1383,6 @@ int lv_on_pmem(struct logical_volume *lv);
int vg_is_foreign(struct volume_group *vg);
+void vg_write_commit_bad_mdas(struct cmd_context *cmd, struct volume_group *vg);
+
#endif
diff --git a/lib/metadata/metadata.c b/lib/metadata/metadata.c
index a307af0b5..a9c27dc6e 100644
--- a/lib/metadata/metadata.c
+++ b/lib/metadata/metadata.c
@@ -5644,3 +5644,84 @@ int vg_is_foreign(struct volume_group *vg)
return _is_foreign_vg(vg);
}
+void vg_write_commit_bad_mdas(struct cmd_context *cmd, struct volume_group *vg)
+{
+ struct dm_list bad_mdas;
+ struct metadata_area *mda;
+ struct device *dev;
+
+ dm_list_init(&bad_mdas);
+
+ lvmcache_get_bad_mdas(cmd, vg->name, (const char *)&vg->id, &bad_mdas);
+
+ dm_list_iterate_items(mda, &bad_mdas) {
+ dev = mda_get_device(mda);
+
+ /*
+ * bad_fields:
+ *
+ * 0: shouldn't happen
+ *
+ * READ|INTERNAL: there's probably nothing wrong on disk
+ *
+ * MAGIC|START: there's a good chance that we were
+ * reading the mda_header from the wrong location; maybe
+ * the pv_header location was wrong. We don't want to
+ * write new metadata to the wrong location. To handle
+ * this we would want to do some further verification that
+ * we have the mda location correct.
+ *
+ * VERSION|CHECKSUM: when the others are correct these
+ * look safe to repair.
+ *
+ * HEADER: general error related to header, covered by fields
+ * above.
+ *
+ * TEXT: general error related to text metadata, we can repair.
+ */
+ if (!mda->bad_fields ||
+ (mda->bad_fields & BAD_MDA_READ) ||
+ (mda->bad_fields & BAD_MDA_INTERNAL) ||
+ (mda->bad_fields & BAD_MDA_MAGIC) ||
+ (mda->bad_fields & BAD_MDA_START)) {
+ log_warn("WARNING: not repairing bad metadata (0x%x) for mda%d on %s",
+ mda->bad_fields, mda->mda_num, dev_name(dev));
+ continue;
+ }
+
+ /*
+ * vg_write/vg_commit reread the mda_header which checks the
+ * mda header fields and fails if any are bad, which stops
+ * vg_write/vg_commit from continuing. Suppress these header
+ * field checks when we know the field is bad and we are going
+ * to replace it. FIXME: do vg_write/vg_commit really need to
+ * reread and recheck the mda_header again (probably not)?
+ */
+
+ if (mda->bad_fields & BAD_MDA_CHECKSUM)
+ mda->ignore_bad_fields |= BAD_MDA_CHECKSUM;
+ if (mda->bad_fields & BAD_MDA_VERSION)
+ mda->ignore_bad_fields |= BAD_MDA_VERSION;
+
+ log_warn("WARNING: repairing bad metadata (0x%x) in mda%d at %llu on %s.",
+ mda->bad_fields, mda->mda_num, (unsigned long long)mda->header_start, dev_name(dev));
+
+ if (!mda->ops->vg_write(vg->fid, vg, mda)) {
+ log_warn("WARNING: failed to write VG %s metadata to bad mda%d at %llu on %s.",
+ vg->name, mda->mda_num, (unsigned long long)mda->header_start, dev_name(dev));
+ continue;
+ }
+
+ if (!mda->ops->vg_precommit(vg->fid, vg, mda)) {
+ log_warn("WARNING: failed to precommit VG %s metadata to bad mda%d at %llu on %s.",
+ vg->name, mda->mda_num, (unsigned long long)mda->header_start, dev_name(dev));
+ continue;
+ }
+
+ if (!mda->ops->vg_commit(vg->fid, vg, mda)) {
+ log_warn("WARNING: failed to commit VG %s metadata to bad mda%d at %llu on %s.",
+ vg->name, mda->mda_num, (unsigned long long)mda->header_start, dev_name(dev));
+ continue;
+ }
+ }
+}
diff --git a/lib/metadata/metadata.h b/lib/metadata/metadata.h
index 07ae29f8b..6d903c9de 100644
--- a/lib/metadata/metadata.h
+++ b/lib/metadata/metadata.h
@@ -185,6 +185,7 @@ struct metadata_area {
struct metadata_area_ops *ops;
void *metadata_locn;
uint32_t status;
+ uint64_t header_start; /* mda_header.start */
int mda_num;
uint32_t bad_fields; /* BAD_MDA_ flags are set to indicate errors found when reading */
uint32_t ignore_bad_fields; /* BAD_MDA_ flags are set to indicate errors to ignore */
diff --git a/tools/args.h b/tools/args.h
index 3d72e8a20..b60d9c9d0 100644
--- a/tools/args.h
+++ b/tools/args.h
@@ -1386,6 +1386,9 @@ arg(thin_ARG, 'T', "thin", 0, 0, 0,
"See --type thin, --type thin-pool, and --virtualsize.\n"
"See \\fBlvmthin\\fP(7) for more information about LVM thin provisioning.\n")
+arg(updatemetadata_ARG, '\0', "updatemetadata", 0, 0, 0,
+ "Update VG metadata to correct problems.\n")
+
arg(uuid_ARG, 'u', "uuid", 0, 0, 0,
"#pvchange\n"
"Generate new random UUID for specified PVs.\n"
diff --git a/tools/command-lines.in b/tools/command-lines.in
index 0de3de71e..3973d7011 100644
--- a/tools/command-lines.in
+++ b/tools/command-lines.in
@@ -1618,6 +1618,11 @@ vgck
OO: --reportformat ReportFmt
OP: VG|Tag ...
ID: vgck_general
+DESC: Read and display information about a VG.
+
+vgck --updatemetadata VG
+ID: vgck_update_metadata
+DESC: Rewrite VG metadata to correct problems.
---
diff --git a/tools/vgck.c b/tools/vgck.c
index a126c2924..90fc5a3aa 100644
--- a/tools/vgck.c
+++ b/tools/vgck.c
@@ -15,6 +15,57 @@
#include "tools.h"
+/*
+ * TODO: we cannot yet repair corruption in label_header, pv_header/locations,
+ * or corruption of some mda_header fields.
+ */
+
+static int _update_metadata_single(struct cmd_context *cmd __attribute__((unused)),
+ const char *vg_name,
+ struct volume_group *vg,
+ struct processing_handle *handle __attribute__((unused)))
+{
+
+ /*
+ * Simply calling vg_write can correct or clean up various things:
+ * . some mda's have old versions of metdadata
+ * . wipe outdated PVs
+ * . fix pv_header used flag and version
+ * . strip historical lvs
+ * . clear missing pv flag on unused PV
+ */
+ if (!vg_write(vg)) {
+ log_error("Failed to write VG.");
+ return 0;
+ }
+
+ if (!vg_commit(vg)) {
+ log_error("Failed to commit VG.");
+ return 0;
+ }
+
+ /*
+ * vg_write does not write to "bad" mdas (where "bad" is corrupt, can't
+ * be processed when reading). bad mdas are not kept in
+ * fid->metadata_areas_in_use so vg_read and vg_write ignore them, but
+ * they are saved in lvmcache. this gets them from lvmcache and tries
+ * to write this metadata to them.
+ */
+ vg_write_commit_bad_mdas(cmd, vg);
+
+ return 1;
+}
+
+static int _update_metadata(struct cmd_context *cmd, int argc, char **argv)
+{
+ cmd->handles_missing_pvs = 1;
+ cmd->wipe_outdated_pvs = 1;
+ cmd->handles_unknown_segments = 1;
+
+ return process_each_vg(cmd, argc, argv, NULL, NULL, READ_FOR_UPDATE, 0, NULL,
+ &_update_metadata_single);
+}
+
static int vgck_single(struct cmd_context *cmd __attribute__((unused)),
const char *vg_name,
struct volume_group *vg,
@@ -37,6 +88,9 @@ static int vgck_single(struct cmd_context *cmd __attribute__((unused)),
int vgck(struct cmd_context *cmd, int argc, char **argv)
{
+ if (arg_is_set(cmd, updatemetadata_ARG))
+ return _update_metadata(cmd, argc, argv);
+
return process_each_vg(cmd, argc, argv, NULL, NULL, 0, 0, NULL,
&vgck_single);
}