master - tests: pvck-dump
by David Teigland
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=356ea897ccfca5a80b0...
Commit: 356ea897ccfca5a80b00a0a6b2479042f6efd144
Parent: bada89a224b0135ac4415d43e6b128989bbf3b2b
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Wed Jun 5 13:58:26 2019 -0500
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Wed Jun 5 13:58:26 2019 -0500
tests: pvck-dump
---
test/shell/pvck-dump.sh | 119 +++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 119 insertions(+), 0 deletions(-)
diff --git a/test/shell/pvck-dump.sh b/test/shell/pvck-dump.sh
new file mode 100644
index 0000000..ed8766a
--- /dev/null
+++ b/test/shell/pvck-dump.sh
@@ -0,0 +1,119 @@
+#!/usr/bin/env bash
+
+# Copyright (C) 2008-2013,2018 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+. lib/inittest
+
+aux prepare_devs 3
+get_devs
+
+dd if=/dev/zero of="$dev1" || true
+dd if=/dev/zero of="$dev2" || true
+dd if=/dev/zero of="$dev3" || true
+
+pvcreate "$dev1"
+pvcreate "$dev2"
+pvcreate --pvmetadatacopies 2 "$dev3"
+
+vgcreate $SHARED $vg "$dev1" "$dev2" "$dev3"
+
+pvck --dump headers "$dev1" > h1
+pvck --dump headers "$dev2" > h2
+pvck --dump headers "$dev3" > h3
+
+grep "label_header at 512" h1
+grep "label_header at 512" h2
+grep "label_header at 512" h3
+
+grep "pv_header at 544" h1
+grep "pv_header at 544" h2
+grep "pv_header at 544" h3
+
+grep "pv_header.disk_locn\[0\].offset 1048576" h1
+grep "pv_header.disk_locn\[0\].offset 1048576" h2
+grep "pv_header.disk_locn\[0\].offset 1048576" h3
+
+grep "pv_header.disk_locn\[2\].offset 4096" h1
+grep "pv_header.disk_locn\[2\].offset 4096" h2
+grep "pv_header.disk_locn\[2\].offset 4096" h3
+
+grep "pv_header.disk_locn\[2\].size 1044480" h1
+grep "pv_header.disk_locn\[2\].size 1044480" h2
+grep "pv_header.disk_locn\[2\].size 1044480" h3
+
+grep "mda_header_1 at 4096" h1
+grep "mda_header_1 at 4096" h2
+grep "mda_header_1 at 4096" h3
+
+grep "mda_header_1.start 4096" h1
+grep "mda_header_1.start 4096" h2
+grep "mda_header_1.start 4096" h3
+
+grep "mda_header_1.size 1044480" h1
+grep "mda_header_1.size 1044480" h2
+grep "mda_header_1.size 1044480" h3
+
+grep "mda_header_2 at " h3
+grep "mda_header_2.start " h3
+
+grep "metadata text at " h1
+grep "metadata text at " h2
+grep "metadata text at " h3
+
+not grep CHECK h1
+not grep CHECK h2
+not grep CHECK h3
+
+pvck --dump metadata "$dev1" > m1
+pvck --dump metadata "$dev2" > m2
+pvck --dump metadata "$dev3" > m3
+pvck --dump metadata --pvmetadatacopies 2 "$dev3" > m3b
+
+diff m1 m2
+diff m1 m3
+
+not diff m1 m3b > tmp
+grep "metadata text at" tmp
+
+lvcreate -an -l1 $vg
+
+pvck --dump metadata_all -f all1 "$dev1" > out1
+pvck --dump metadata_all -f all2 "$dev2" > out2
+pvck --dump metadata_all -f all3 "$dev3" > out3
+pvck --dump metadata_all --pvmetadatacopies 2 -f all3b "$dev3" > out3b
+
+diff out1 out2
+diff out1 out3
+
+grep "seqno 1 with" out1
+grep "seqno 1 with" out3b
+grep "seqno 2 with" out1
+grep "seqno 2 with" out3b
+
+diff all1 all2
+diff all1 all3
+diff all1 all3b
+
+grep "seqno = 1" all1
+grep "seqno = 2" all1
+
+
+pvck --dump metadata_area -f area1 "$dev1"
+pvck --dump metadata_area -f area2 "$dev2"
+pvck --dump metadata_area -f area3 "$dev3"
+pvck --dump metadata_area -f area3b "$dev3"
+
+diff area1 area2
+diff area1 area3
+diff area1 area3b
+
+vgremove -ff $vg
+
3 years, 9 months
master - pvck: dump metadata_all
by David Teigland
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=bada89a224b0135ac44...
Commit: bada89a224b0135ac4415d43e6b128989bbf3b2b
Parent: 4d9f41b11945df83b37b55439a44372b894a78f5
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Wed Jun 5 11:10:37 2019 -0500
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Wed Jun 5 12:25:34 2019 -0500
pvck: dump metadata_all
This searches the entire metadata area for any
copy of the metadata and dumps it to file.
---
tools/args.h | 7 +-
tools/pvck.c | 355 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 348 insertions(+), 14 deletions(-)
diff --git a/tools/args.h b/tools/args.h
index 83c1cfe..69f7e07 100644
--- a/tools/args.h
+++ b/tools/args.h
@@ -215,9 +215,10 @@ arg(driverloaded_ARG, '\0', "driverloaded", bool_VAL, 0, 0,
arg(dump_ARG, '\0', "dump", string_VAL, 0, 0,
"Dump metadata from a PV. Option values include \\fBmetadata\\fP\n"
- "to extract the current text metadata, \\fBmetadata_area\\fP\n"
- "to extract the entire text metadata area, and \\fBheaders\\fP\n"
- "to print and check LVM headers.\n")
+ "to print or save the current text metadata, \\fBmetadata_area\\fP\n"
+ "to save the entire text metadata area to a file, \\fBmetadata_all\\fP\n"
+ "to save the current and any previous complete versions of metadata\n"
+ "to a file, and \\fBheaders\\fP to print and check LVM headers.\n")
arg(errorwhenfull_ARG, '\0', "errorwhenfull", bool_VAL, 0, 0,
"Specifies thin pool behavior when data space is exhausted.\n"
diff --git a/tools/pvck.c b/tools/pvck.c
index 9e690a8..c9b833a 100644
--- a/tools/pvck.c
+++ b/tools/pvck.c
@@ -291,17 +291,326 @@ static int _dump_meta_area(struct device *dev, const char *tofile,
return 1;
}
+/*
+ * Search for any instance of id_str[] in the metadata area,
+ * where the id_str indicates the start of a metadata copy
+ * (which could be complete or a fragment.)
+ * id_str is an open brace followed by id = <uuid>.
+ *
+ * {\n
+ * id = "lL7Mnk-oCGn-Bde2-9B6S-44Z7-VrHa-wvfC3v"
+ *
+ * 1\23456789012345678901234567890123456789012345678
+ * 10 20 30 40
+ */
+#define ID_STR_SIZE 48
+
+#define SEARCH_VGNAME_LEN 512
+
+static int _dump_meta_all(struct device *dev, const char *tofile,
+ uint64_t mda_offset, uint64_t mda_size,
+ uint64_t meta_offset, uint64_t meta_size,
+ char *meta_buf)
+{
+ FILE *fp = NULL;
+ struct dm_config_tree *cft;
+ char vgname[SEARCH_VGNAME_LEN];
+ char id_str[ID_STR_SIZE];
+ char *text_buf, *new_buf;
+ char *p, *brace, *start, *buf_begin, *buf_end;
+ uint64_t search_offset, start_offset;
+ uint32_t brace_dist, new_len, len_a, len_b;
+ uint32_t left_count, right_count;
+ uint32_t seqno;
+ int search_wrapped = 0;
+ int p_wrapped;
+ int save_bad;
+ int i;
+
+ /*
+ * metadata begins:
+ *
+ * <vgname> {
+ * id = "<uuid>"
+ *
+ * Search the metadata buffer for each instance
+ * of the string:
+ *
+ * {
+ * id = "<uuid>"
+ *
+ * Then reverse by the length of the <vgname> +1
+ * to get to the start of the metadata. The
+ * vgname must come from the original/current copy
+ * of metadata found through the mda_header pointer.
+ *
+ * From the start of the metadata, find the end of
+ * the metadata by searching foward until \0 is found.
+ * Metadata ends with the three bytes:
+ * \n\n\0 (0x0a0a00)
+ */
+
+ memset(vgname, 0, sizeof(vgname));
+ memset(id_str, 0, sizeof(id_str));
+
+ for (i = 0; i < SEARCH_VGNAME_LEN; i++) {
+ if (meta_buf[i] == ' ')
+ break;
+ vgname[i] = meta_buf[i];
+ }
+
+ if (!(p = strchr(meta_buf, '{')))
+ return_0;
+
+ for (i = 0; i < ID_STR_SIZE; i++) {
+ id_str[i] = *p;
+ p++;
+ }
+
+ if (!(text_buf = malloc(mda_size)))
+ return_0;
+ memset(text_buf, 0, mda_size);
+
+ if (!dev_read_bytes(dev, mda_offset, mda_size, text_buf)) {
+ log_print("CHECK: failed to read metadata area at offset %llu size %llu",
+ (unsigned long long)mda_offset, (unsigned long long)mda_size);
+ free(text_buf);
+ return 0;
+ }
+
+ search_offset = meta_offset + meta_size;
+
+ search_next:
+ if (search_offset > mda_size) {
+ if (search_wrapped)
+ goto done;
+ search_offset = 512;
+ search_wrapped = 1;
+ }
+
+ /*
+ * Search between buf_begin and buf_end for next open brace.
+ */
+ buf_begin = text_buf + search_offset;
+ buf_end = text_buf + mda_size;
+ brace = NULL;
+
+ for (p = buf_begin; p < buf_end; p++) {
+ if (*p != '{')
+ continue;
+ brace = p;
+ break;
+ }
+
+ if (!brace && search_wrapped)
+ goto done;
+ if (!brace) {
+ search_offset = 512;
+ search_wrapped = 1;
+ goto search_next;
+ }
+
+ /*
+ * brace_dist is the distance from the last place we
+ * began searching to the brace we are testing for
+ * metadata. If this brace is not the start of valid
+ * metadata, then advance brace_dist and search for
+ * the next brace to check.
+ */
+ brace_dist = (uint32_t)(brace - buf_begin);
+
+ /*
+ * Found an open brace, check if it's the start of new metadata
+ * by checking if brace is followed by id = "<uuid>".
+ */
+
+ if (memcmp(brace, id_str, ID_STR_SIZE)) {
+ /* It's not, look for next open brace. */
+ search_offset += (brace_dist + 1);
+ goto search_next;
+ }
+
+ /*
+ * This looks like a new instance of metadata, check if it's complete.
+ * The start of the metadata is the vgname preceding the open brace,
+ * so step backward through the text_buf to find the start of vgname.
+ * There is no delimiter preceding the vgname, there can be any
+ * text or data in the byte immediately before vgname (this means
+ * we cannot handle extracting metadata prior to a vgrename.)
+ *
+ * <vgname> {
+ * id = "..."
+ */
+ start = brace - (strlen(vgname) + 1);
+
+ /* Offset from the begininng of device to the start of this metadata. */
+ start_offset = (uint64_t)(start - text_buf) + mda_offset;
+
+ /*
+ * The end of the metadata is found by searching forward in text_buf
+ * until \0, at which point open and close braces should match.
+ * This forward search may wrap around to the start of text_buf.
+ *
+ * Metadata ends with the three bytes \n\n\0: 0a 0a 00
+ */
+ p = start;
+ p_wrapped = 0;
+ len_a = 0;
+ len_b = 0;
+ new_len = 0;
+ new_buf = NULL;
+ left_count = 0;
+ right_count = 0;
+ save_bad = 0;
+ seqno = 0;
+
+ while (1) {
+ p++;
+
+ if (p == (buf_end)) {
+ p = text_buf + 512;
+ p_wrapped = 1;
+ }
+
+ if (*p == '{')
+ left_count++;
+ else if (*p == '}')
+ right_count++;
+ else if (*p == '\0')
+ break;
+ }
+
+ /* \0 should be preceded by \n\n (0x0a0a) */
+ if ((*(p - 2) != 0x0a) || (*(p - 1) != 0x0a))
+ log_print("Unexpected metadata end bytes.");
+
+ if (p_wrapped) {
+ len_a = (uint32_t)(buf_end - start);
+ len_b = (uint32_t)(p - (text_buf + 512));
+ new_len = len_a + len_b;
+ search_wrapped = 1;
+ } else {
+ new_len = (uint32_t)(p - start);
+ }
+
+ /*
+ * A couple simple tests indicate if this could be valid metadata
+ * before attempting to parse it. (min length is probably greater
+ * than 256, so this could be increased.)
+ *
+ * If this is complete but corrupt, we should save it.
+ * TODO: If this is a fragment we should skip it.
+ */
+ if ((left_count != right_count) || (new_len < 256)) {
+ /*
+ * To skip this:
+ * search_offset += (brace_dist + 1);
+ * goto search_next;
+ */
+ log_print("Found incorrect metadata at %llu length %u with braces %u %u",
+ (unsigned long long)start_offset, new_len, left_count, right_count);
+ save_bad = 1;
+ }
+
+ /*
+ * Copy the potential metadata into a new buffer to parse.
+ */
+ if (!(new_buf = malloc(new_len + 1))) {
+ search_offset += (brace_dist + 1);
+ log_print("No memory for metadata at %llu length %u with %u sections",
+ (unsigned long long)start_offset, new_len, right_count);
+ goto search_next;
+ }
+ memset(new_buf, 0, new_len + 1);
+
+ if (p_wrapped) {
+ memcpy(new_buf, start, len_a);
+ memcpy(new_buf + len_a, text_buf + 512, len_b);
+ } else {
+ memcpy(new_buf, start, new_len);
+ }
+
+ if (save_bad)
+ goto save;
+
+ /*
+ * Check the metadata is parsable.
+ * If this is complete but corrupt, we should save it.
+ * TODO: If this is a fragment we should skip it.
+ */
+ if ((cft = config_open(CONFIG_FILE_SPECIAL, NULL, 0))) {
+ if (!dm_config_parse(cft, new_buf, new_buf + new_len)) {
+ /*
+ * To skip this:
+ * search_offset += (brace_dist + 1);
+ * goto search_next;
+ */
+ log_print("Found unparsable metadata at %llu length %u with %u sections",
+ (unsigned long long)start_offset, new_len, right_count);
+ config_destroy(cft);
+ goto save;
+ }
+
+ if (cft->root && cft->root->child)
+ dm_config_get_uint32(cft->root->child, "seqno", &seqno);
+ config_destroy(cft);
+ }
+
+ log_print("Found metadata at %llu length %u seqno %u with %u sections",
+ (unsigned long long)start_offset, new_len, seqno, right_count);
+
+ save:
+ if (!fp && tofile) {
+ if (!(fp = fopen(tofile, "a"))) {
+ log_error("Failed to open file %s", tofile);
+ goto out;
+ }
+ }
+
+ if (fp) {
+ fprintf(fp, "%s", new_buf);
+ fprintf(fp, "\n--\n");
+ }
+
+ out:
+ free(new_buf);
+
+ /*
+ * Look for another id_str instance after the metadata
+ * that was just finished.
+ */
+
+ if (p_wrapped)
+ search_offset = len_b;
+ else
+ search_offset += new_len;
+ goto search_next;
+
+ done:
+ if (fp) {
+ if (fflush(fp))
+ stack;
+ if (fclose(fp))
+ stack;
+ }
+
+ free(text_buf);
+ return 1;
+}
+
static int _dump_meta_text(struct device *dev,
int print_fields, int print_metadata, const char *tofile,
int mda_num, int rlocn_index,
uint64_t mda_offset, uint64_t mda_size,
uint64_t meta_offset, uint64_t meta_size,
- uint32_t meta_checksum)
+ uint32_t meta_checksum,
+ char **meta_buf_out)
{
char *meta_buf;
struct dm_config_tree *cft;
const char *vgname = NULL;
uint32_t crc;
+ uint32_t seqno = 0;
int mn = mda_num; /* 1 or 2 */
int ri = rlocn_index; /* 0 or 1 */
int bad = 0;
@@ -367,14 +676,17 @@ static int _dump_meta_text(struct device *dev,
(unsigned long long)meta_size);
bad++;
} else {
- vgname = strdup(cft->root->key);
+ if (cft->root && cft->root->key)
+ vgname = strdup(cft->root->key);
+ if (cft->root && cft->root->child)
+ dm_config_get_uint32(cft->root->child, "seqno", &seqno);
}
config_destroy(cft);
}
- log_print("metadata text at %llu crc 0x%x # vgname %s",
+ log_print("metadata text at %llu crc 0x%x # vgname %s seqno %u",
(unsigned long long)(mda_offset + meta_offset), crc,
- vgname ? vgname : "?");
+ vgname ? vgname : "?", seqno);
if (!print_metadata)
goto out;
@@ -399,6 +711,8 @@ static int _dump_meta_text(struct device *dev,
}
out:
+ *meta_buf_out = meta_buf;
+
if (bad)
return 0;
return 1;
@@ -662,6 +976,7 @@ static int _dump_mda_header(struct cmd_context *cmd,
{
char str[256];
char *buf;
+ char *meta_buf = NULL;
struct mda_header *mh;
struct raw_locn *rlocn0, *rlocn1;
uint64_t rlocn0_offset, rlocn1_offset;
@@ -749,8 +1064,24 @@ static int _dump_mda_header(struct cmd_context *cmd,
if (!meta_offset)
goto out;
- if (!_dump_meta_text(dev, print_fields, print_metadata, tofile, mda_num, 0, mda_offset, mda_size, meta_offset, meta_size, meta_checksum))
- bad++;
+ /*
+ * looking at the current copy of metadata referenced by raw_locn
+ */
+ if (print_metadata < 2) {
+ if (!_dump_meta_text(dev, print_fields, print_metadata, tofile, mda_num, 0, mda_offset, mda_size, meta_offset, meta_size, meta_checksum, &meta_buf))
+ bad++;
+ }
+
+ /*
+ * looking at all copies of the metadata in the area
+ */
+ if (print_metadata == 2) {
+ if (!_dump_meta_text(dev, 0, 0, NULL, mda_num, 0, mda_offset, mda_size, meta_offset, meta_size, meta_checksum, &meta_buf))
+ bad++;
+
+ if (!_dump_meta_all(dev, tofile, mda_offset, mda_size, meta_offset, meta_size, meta_buf))
+ bad++;
+ }
/* Should we also check text metadata if it exists in rlocn1? */
out:
@@ -821,15 +1152,14 @@ static int _dump_headers(struct cmd_context *cmd,
}
static int _dump_metadata(struct cmd_context *cmd,
- int argc, char **argv, int full_area)
+ int argc, char **argv,
+ int print_metadata, int print_area)
{
struct device *dev;
const char *pv_name;
const char *tofile = NULL;
uint64_t mda1_offset = 0, mda1_size = 0, mda2_offset = 0, mda2_size = 0;
uint32_t mda1_checksum, mda2_checksum;
- int print_metadata = !full_area;
- int print_area = full_area;
int mda_num = 1;
int bad = 0;
@@ -908,10 +1238,13 @@ int pvck(struct cmd_context *cmd, int argc, char **argv)
dump = arg_str_value(cmd, dump_ARG, NULL);
if (!strcmp(dump, "metadata"))
- return _dump_metadata(cmd, argc, argv, 0);
+ return _dump_metadata(cmd, argc, argv, 1, 0);
+
+ if (!strcmp(dump, "metadata_all"))
+ return _dump_metadata(cmd, argc, argv, 2, 0);
if (!strcmp(dump, "metadata_area"))
- return _dump_metadata(cmd, argc, argv, 1);
+ return _dump_metadata(cmd, argc, argv, 0, 1);
if (!strcmp(dump, "headers"))
return _dump_headers(cmd, argc, argv);
3 years, 9 months
stable-2.02 - tests: check no_discard_passdown
by Zdenek Kabelac
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=cb7766bc1411c3350e7...
Commit: cb7766bc1411c3350e79241122a974dac7fcc902
Parent: 1308572a8754f1f5f614218dc08d15f1b562fb0d
Author: Zdenek Kabelac <zkabelac(a)redhat.com>
AuthorDate: Wed Jun 5 15:46:43 2019 +0200
Committer: Zdenek Kabelac <zkabelac(a)redhat.com>
CommitterDate: Wed Jun 5 15:54:59 2019 +0200
tests: check no_discard_passdown
Check reporting works
---
test/shell/cache-no-discard.sh | 45 ++++++++++++++++++++++++++++++++++++++++
1 files changed, 45 insertions(+), 0 deletions(-)
diff --git a/test/shell/cache-no-discard.sh b/test/shell/cache-no-discard.sh
new file mode 100644
index 0000000..3ab0594
--- /dev/null
+++ b/test/shell/cache-no-discard.sh
@@ -0,0 +1,45 @@
+#!/usr/bin/env bash
+
+# Copyright (C) 2019 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+# Check reporting of no_discard_passdown
+
+SKIP_WITH_LVMPOLLD=1
+
+# Until new version of cache_check tools - no integrity validation
+LVM_TEST_CACHE_CHECK_CMD=""
+
+. lib/inittest
+
+aux kernel_at_least 5 1 || skip
+
+aux have_cache 2 0
+
+aux prepare_vg 1
+
+# Create thinLV without discard
+lvcreate --discards=ignore -T -V20 -L20 -n $lv $vg/pool
+
+aux extend_filter_LVMTEST
+
+# Use discard-less LV as PV for $vg1
+pvcreate "$DM_DEV_DIR/$vg/$lv"
+vgcreate -s 128K $vg1 "$DM_DEV_DIR/$vg/$lv"
+
+# Create simple cache LV
+lvcreate -L2 -n $lv1 $vg1
+lvcreate -H -L2 $vg1/$lv1
+
+#lvs -ao+kernel_discards $vg1
+check lv_field $vg1/$lv1 kernel_discards "nopassdown"
+
+vgremove -f $vg1
+vgremove -f $vg
3 years, 9 months
stable-2.02 - cache: support no_discard_passdown
by Zdenek Kabelac
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=adf9bf80a32500b45b3...
Commit: adf9bf80a32500b45b37eb24b98fa7c2c933019e
Parent: f633d68c542e62fe68b6df8ec03b9851fd6e74d9
Author: Zdenek Kabelac <zkabelac(a)redhat.com>
AuthorDate: Wed Jun 5 14:31:34 2019 +0200
Committer: Zdenek Kabelac <zkabelac(a)redhat.com>
CommitterDate: Wed Jun 5 15:54:58 2019 +0200
cache: support no_discard_passdown
Recent kernel version from kernel commit:
de7180ff908b2bc0342e832dbdaa9a5f1ecaa33a
started to report in cache status line new flag:
no_discard_passdown
Whenever lvm spots unknown status it reports:
Unknown feature in status:
So add reconginzing this feature flag and also report this with
'lvs -o+kernel_discards'
When no_discard_passdown is found in status 'nopassdown' gets reported
for this field (roughly matching what we report for thin-pools).
---
WHATS_NEW | 1 +
WHATS_NEW_DM | 1 +
lib/metadata/lv.c | 5 +++++
libdm/libdevmapper.h | 1 +
libdm/libdm-targets.c | 2 ++
5 files changed, 10 insertions(+), 0 deletions(-)
diff --git a/WHATS_NEW b/WHATS_NEW
index 106661f..d3da403 100644
--- a/WHATS_NEW
+++ b/WHATS_NEW
@@ -1,5 +1,6 @@
Version 2.02.186 -
================================
+ Report no_discard_passdown for cache LVs with lvs -o+kernel_discards.
Prevent shared active mirror LVs with lvmlockd.
Version 2.02.185 - 13th May 2019
diff --git a/WHATS_NEW_DM b/WHATS_NEW_DM
index 15e5b8b..8c78724 100644
--- a/WHATS_NEW_DM
+++ b/WHATS_NEW_DM
@@ -1,5 +1,6 @@
Version 1.02.160 -
================================
+ Parsing of cache status understand no_discard_passdown.
Version 1.02.158 - 13th May 2019
================================
diff --git a/lib/metadata/lv.c b/lib/metadata/lv.c
index 2abe42d..538b292 100644
--- a/lib/metadata/lv.c
+++ b/lib/metadata/lv.c
@@ -245,6 +245,11 @@ char *lvseg_kernel_discards_dup_with_info_and_seg_status(struct dm_pool *mem, co
return 0;
}
s = get_pool_discards_name(d);
+ } else if (lvdm->seg_status.type == SEG_STATUS_CACHE) {
+ if (lvdm->seg_status.cache->feature_flags &
+ DM_CACHE_FEATURE_NO_DISCARD_PASSDOWN) {
+ s = "nopassdown";
+ }
}
if (!(ret = dm_pool_strdup(mem, s))) {
diff --git a/libdm/libdevmapper.h b/libdm/libdevmapper.h
index 19032d7..e885f52 100644
--- a/libdm/libdevmapper.h
+++ b/libdm/libdevmapper.h
@@ -1893,6 +1893,7 @@ int dm_tree_node_add_raid_target_with_params_v2(struct dm_tree_node *node,
#define DM_CACHE_FEATURE_WRITETHROUGH 0x00000002
#define DM_CACHE_FEATURE_PASSTHROUGH 0x00000004
#define DM_CACHE_FEATURE_METADATA2 0x00000008 /* cache v1.10 */
+#define DM_CACHE_FEATURE_NO_DISCARD_PASSDOWN 0x00000010
struct dm_config_node;
/*
diff --git a/libdm/libdm-targets.c b/libdm/libdm-targets.c
index 8766789..d0a8b43 100644
--- a/libdm/libdm-targets.c
+++ b/libdm/libdm-targets.c
@@ -296,6 +296,8 @@ int dm_get_status_cache(struct dm_pool *mem, const char *params,
s->feature_flags |= DM_CACHE_FEATURE_PASSTHROUGH;
else if (!strncmp(p, "metadata2 ", 10))
s->feature_flags |= DM_CACHE_FEATURE_METADATA2;
+ else if (!strncmp(p, "no_discard_passdown ", 20))
+ s->feature_flags |= DM_CACHE_FEATURE_NO_DISCARD_PASSDOWN;
else
log_error("Unknown feature in status: %s", params);
3 years, 9 months
stable-2.02 - tests: automatically set scan_lvs when using extend_filter
by Zdenek Kabelac
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=1308572a8754f1f5f61...
Commit: 1308572a8754f1f5f614218dc08d15f1b562fb0d
Parent: adf9bf80a32500b45b37eb24b98fa7c2c933019e
Author: Zdenek Kabelac <zkabelac(a)redhat.com>
AuthorDate: Wed Jun 5 15:45:30 2019 +0200
Committer: Zdenek Kabelac <zkabelac(a)redhat.com>
CommitterDate: Wed Jun 5 15:54:59 2019 +0200
tests: automatically set scan_lvs when using extend_filter
When using 'aux extend_filter' we always want to use LV as PV.
---
test/lib/aux.sh | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/test/lib/aux.sh b/test/lib/aux.sh
index bd42f30..3a66f53 100644
--- a/test/lib/aux.sh
+++ b/test/lib/aux.sh
@@ -1171,7 +1171,7 @@ extend_filter() {
for rx in "$@"; do
filter=$(echo "$filter" | sed -e "s:\\[:[ \"$rx\", :")
done
- lvmconf "$filter"
+ lvmconf "$filter" "devices/scan_lvs = 1"
}
extend_filter_LVMTEST() {
3 years, 9 months
master - tests: check no_discard_passdown
by Zdenek Kabelac
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=4d9f41b11945df83b37...
Commit: 4d9f41b11945df83b37b55439a44372b894a78f5
Parent: ddd68fbeadb42d5cc0fdae49e133524bab049f49
Author: Zdenek Kabelac <zkabelac(a)redhat.com>
AuthorDate: Wed Jun 5 15:46:43 2019 +0200
Committer: Zdenek Kabelac <zkabelac(a)redhat.com>
CommitterDate: Wed Jun 5 15:48:44 2019 +0200
tests: check no_discard_passdown
Check reporting works
---
test/shell/cache-no-discard.sh | 45 ++++++++++++++++++++++++++++++++++++++++
1 files changed, 45 insertions(+), 0 deletions(-)
diff --git a/test/shell/cache-no-discard.sh b/test/shell/cache-no-discard.sh
new file mode 100644
index 0000000..3ab0594
--- /dev/null
+++ b/test/shell/cache-no-discard.sh
@@ -0,0 +1,45 @@
+#!/usr/bin/env bash
+
+# Copyright (C) 2019 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+# Check reporting of no_discard_passdown
+
+SKIP_WITH_LVMPOLLD=1
+
+# Until new version of cache_check tools - no integrity validation
+LVM_TEST_CACHE_CHECK_CMD=""
+
+. lib/inittest
+
+aux kernel_at_least 5 1 || skip
+
+aux have_cache 2 0
+
+aux prepare_vg 1
+
+# Create thinLV without discard
+lvcreate --discards=ignore -T -V20 -L20 -n $lv $vg/pool
+
+aux extend_filter_LVMTEST
+
+# Use discard-less LV as PV for $vg1
+pvcreate "$DM_DEV_DIR/$vg/$lv"
+vgcreate -s 128K $vg1 "$DM_DEV_DIR/$vg/$lv"
+
+# Create simple cache LV
+lvcreate -L2 -n $lv1 $vg1
+lvcreate -H -L2 $vg1/$lv1
+
+#lvs -ao+kernel_discards $vg1
+check lv_field $vg1/$lv1 kernel_discards "nopassdown"
+
+vgremove -f $vg1
+vgremove -f $vg
3 years, 9 months
master - tests: automatically set scan_lvs when using extend_filter
by Zdenek Kabelac
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=ddd68fbeadb42d5cc0f...
Commit: ddd68fbeadb42d5cc0fdae49e133524bab049f49
Parent: e3c4ab0cc788b0190ab58446189c2d6b07a27b98
Author: Zdenek Kabelac <zkabelac(a)redhat.com>
AuthorDate: Wed Jun 5 15:45:30 2019 +0200
Committer: Zdenek Kabelac <zkabelac(a)redhat.com>
CommitterDate: Wed Jun 5 15:48:44 2019 +0200
tests: automatically set scan_lvs when using extend_filter
When using 'aux extend_filter' we always want to use LV as PV.
---
test/lib/aux.sh | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/test/lib/aux.sh b/test/lib/aux.sh
index 1e8f0de..b178ab7 100644
--- a/test/lib/aux.sh
+++ b/test/lib/aux.sh
@@ -1090,7 +1090,7 @@ extend_filter() {
for rx in "$@"; do
filter=$(echo "$filter" | sed -e "s:\\[:[ \"$rx\", :")
done
- lvmconf "$filter"
+ lvmconf "$filter" "devices/scan_lvs = 1"
}
extend_filter_LVMTEST() {
3 years, 9 months
master - cache: support no_discard_passdown
by Zdenek Kabelac
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=e3c4ab0cc788b0190ab...
Commit: e3c4ab0cc788b0190ab58446189c2d6b07a27b98
Parent: d18e491f6890e15a677e8189de0699ccc7412f16
Author: Zdenek Kabelac <zkabelac(a)redhat.com>
AuthorDate: Wed Jun 5 14:31:34 2019 +0200
Committer: Zdenek Kabelac <zkabelac(a)redhat.com>
CommitterDate: Wed Jun 5 15:48:41 2019 +0200
cache: support no_discard_passdown
Recent kernel version from kernel commit:
de7180ff908b2bc0342e832dbdaa9a5f1ecaa33a
started to report in cache status line new flag:
no_discard_passdown
Whenever lvm spots unknown status it reports:
Unknown feature in status:
So add reconginzing this feature flag and also report this with
'lvs -o+kernel_discards'
When no_discard_passdown is found in status 'nopassdown' gets reported
for this field (roughly matching what we report for thin-pools).
---
WHATS_NEW | 1 +
WHATS_NEW_DM | 1 +
device_mapper/all.h | 1 +
device_mapper/libdm-targets.c | 2 ++
lib/metadata/lv.c | 5 +++++
libdm/libdevmapper.h | 1 +
libdm/libdm-targets.c | 2 ++
7 files changed, 13 insertions(+), 0 deletions(-)
diff --git a/WHATS_NEW b/WHATS_NEW
index fd27c33..b5deceb 100644
--- a/WHATS_NEW
+++ b/WHATS_NEW
@@ -1,5 +1,6 @@
Version 2.03.02 -
===================================
+ Report no_discard_passdown for cache LVs with lvs -o+kernel_discards.
Add pvck --dump option to extract metadata.
Fix signal delivery checking race in libdaemon (lvmetad).
Add missing Before=shutdown.target to LVM2 services to fix shutdown ordering.
diff --git a/WHATS_NEW_DM b/WHATS_NEW_DM
index 7c8b853..8a2146d 100644
--- a/WHATS_NEW_DM
+++ b/WHATS_NEW_DM
@@ -1,5 +1,6 @@
Version 1.02.155 -
====================================
+ Parsing of cache status understand no_discard_passdown.
Ensure migration_threshold for cache is at least 8 chunks.
Include correct internal header inside libdm list.c.
Enhance ioctl flattening and add parameters only when needed.
diff --git a/device_mapper/all.h b/device_mapper/all.h
index 05cd83b..bdb69e1 100644
--- a/device_mapper/all.h
+++ b/device_mapper/all.h
@@ -903,6 +903,7 @@ int dm_tree_node_add_raid_target_with_params_v2(struct dm_tree_node *node,
#define DM_CACHE_FEATURE_WRITETHROUGH 0x00000002
#define DM_CACHE_FEATURE_PASSTHROUGH 0x00000004
#define DM_CACHE_FEATURE_METADATA2 0x00000008 /* cache v1.10 */
+#define DM_CACHE_FEATURE_NO_DISCARD_PASSDOWN 0x00000010
struct dm_config_node;
/*
diff --git a/device_mapper/libdm-targets.c b/device_mapper/libdm-targets.c
index 607f429..d82e28b 100644
--- a/device_mapper/libdm-targets.c
+++ b/device_mapper/libdm-targets.c
@@ -296,6 +296,8 @@ int dm_get_status_cache(struct dm_pool *mem, const char *params,
s->feature_flags |= DM_CACHE_FEATURE_PASSTHROUGH;
else if (!strncmp(p, "metadata2 ", 10))
s->feature_flags |= DM_CACHE_FEATURE_METADATA2;
+ else if (!strncmp(p, "no_discard_passdown ", 20))
+ s->feature_flags |= DM_CACHE_FEATURE_NO_DISCARD_PASSDOWN;
else
log_error("Unknown feature in status: %s", params);
diff --git a/lib/metadata/lv.c b/lib/metadata/lv.c
index e8f1fab..b4ed712 100644
--- a/lib/metadata/lv.c
+++ b/lib/metadata/lv.c
@@ -245,6 +245,11 @@ char *lvseg_kernel_discards_dup_with_info_and_seg_status(struct dm_pool *mem, co
return 0;
}
s = get_pool_discards_name(d);
+ } else if (lvdm->seg_status.type == SEG_STATUS_CACHE) {
+ if (lvdm->seg_status.cache->feature_flags &
+ DM_CACHE_FEATURE_NO_DISCARD_PASSDOWN) {
+ s = "nopassdown";
+ }
}
if (!(ret = dm_pool_strdup(mem, s))) {
diff --git a/libdm/libdevmapper.h b/libdm/libdevmapper.h
index 19032d7..e885f52 100644
--- a/libdm/libdevmapper.h
+++ b/libdm/libdevmapper.h
@@ -1893,6 +1893,7 @@ int dm_tree_node_add_raid_target_with_params_v2(struct dm_tree_node *node,
#define DM_CACHE_FEATURE_WRITETHROUGH 0x00000002
#define DM_CACHE_FEATURE_PASSTHROUGH 0x00000004
#define DM_CACHE_FEATURE_METADATA2 0x00000008 /* cache v1.10 */
+#define DM_CACHE_FEATURE_NO_DISCARD_PASSDOWN 0x00000010
struct dm_config_node;
/*
diff --git a/libdm/libdm-targets.c b/libdm/libdm-targets.c
index f2bac51..164d515 100644
--- a/libdm/libdm-targets.c
+++ b/libdm/libdm-targets.c
@@ -296,6 +296,8 @@ int dm_get_status_cache(struct dm_pool *mem, const char *params,
s->feature_flags |= DM_CACHE_FEATURE_PASSTHROUGH;
else if (!strncmp(p, "metadata2 ", 10))
s->feature_flags |= DM_CACHE_FEATURE_METADATA2;
+ else if (!strncmp(p, "no_discard_passdown ", 20))
+ s->feature_flags |= DM_CACHE_FEATURE_NO_DISCARD_PASSDOWN;
else
log_error("Unknown feature in status: %s", params);
3 years, 9 months
stable-2.02 - tests: update cache test
by Marian Csontos
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=f633d68c542e62fe68b...
Commit: f633d68c542e62fe68b6df8ec03b9851fd6e74d9
Parent: c733e9644537c7901caf245c1b3fdcdeba76cc12
Author: Zdenek Kabelac <zkabelac(a)redhat.com>
AuthorDate: Sun Jan 20 12:11:04 2019 +0100
Committer: Marian Csontos <mcsontos(a)redhat.com>
CommitterDate: Wed Jun 5 11:23:25 2019 +0200
tests: update cache test
Since migration_threshold is now protected to not be smaller
then 8*chunk_size - update tests to count with this modification.
(cherry picked from commit 2cac4bfc150ea1e5459580e3bac4907446ea7f3a)
---
test/shell/lvchange-cache.sh | 22 +++++++++++-----------
test/shell/lvcreate-cache.sh | 15 +++++++++------
2 files changed, 20 insertions(+), 17 deletions(-)
diff --git a/test/shell/lvchange-cache.sh b/test/shell/lvchange-cache.sh
index 7e95f61..ab7c8b0 100644
--- a/test/shell/lvchange-cache.sh
+++ b/test/shell/lvchange-cache.sh
@@ -38,27 +38,27 @@ check lv_field $vg/corigin kernel_cache_policy "cleaner"
# device-mapper: space map common: index_check failed: blocknr 17179869216 != wanted 11
if aux have_cache 1 5 0 ; then
-lvchange --cachepolicy mq --cachesettings migration_threshold=333 $vg/corigin
+lvchange --cachepolicy mq --cachesettings migration_threshold=1333 $vg/corigin
# TODO once mq->smq happens we will get here some 0 for mq settings
check lv_field $vg/corigin kernel_cache_policy "mq"
-get lv_field $vg/corigin kernel_cache_settings | grep 'migration_threshold=333'
+get lv_field $vg/corigin kernel_cache_settings | grep 'migration_threshold=1333'
lvchange --refresh $vg/corigin
-get lv_field $vg/corigin kernel_cache_settings | grep 'migration_threshold=333'
+get lv_field $vg/corigin kernel_cache_settings | grep 'migration_threshold=1333'
lvchange -an $vg
lvchange -ay $vg
-get lv_field $vg/corigin kernel_cache_settings | grep 'migration_threshold=333'
+get lv_field $vg/corigin kernel_cache_settings | grep 'migration_threshold=1333'
-lvchange --cachesettings 'migration_threshold = 233 sequential_threshold = 13' $vg/corigin
+lvchange --cachesettings 'migration_threshold = 1233 sequential_threshold = 13' $vg/corigin
get lv_field $vg/corigin kernel_cache_settings | tee out
-grep 'migration_threshold=233' out
+grep 'migration_threshold=1233' out
if grep 'sequential_threshold=13' out ; then
-lvchange --cachesettings 'migration_threshold = 17' $vg/corigin
+lvchange --cachesettings 'migration_threshold = 1117' $vg/corigin
get lv_field $vg/corigin kernel_cache_settings | tee out
-grep 'migration_threshold=17' out
+grep 'migration_threshold=1117' out
grep 'sequential_threshold=13' out
lvchange --cachesettings 'migration_threshold = default' $vg/corigin
@@ -66,16 +66,16 @@ get lv_field $vg/corigin kernel_cache_settings | tee out
grep 'migration_threshold=2048' out
grep 'sequential_threshold=13' out
-lvchange --cachesettings 'migration_threshold = 233 sequential_threshold = 13 random_threshold = 1' $vg/corigin
+lvchange --cachesettings 'migration_threshold = 1233 sequential_threshold = 13 random_threshold = 1' $vg/corigin
lvchange --cachesettings 'random_threshold = default migration_threshold = default' $vg/corigin
get lv_field $vg/corigin kernel_cache_settings | tee out
grep 'migration_threshold=2048' out
grep 'sequential_threshold=13' out
grep 'random_threshold=4' out
-lvchange --cachesettings migration_threshold=233 --cachesettings sequential_threshold=13 --cachesettings random_threshold=1 $vg/corigin
+lvchange --cachesettings migration_threshold=1233 --cachesettings sequential_threshold=13 --cachesettings random_threshold=1 $vg/corigin
get lv_field $vg/corigin kernel_cache_settings | tee out
-grep 'migration_threshold=233' out
+grep 'migration_threshold=1233' out
grep 'sequential_threshold=13' out
grep 'random_threshold=1' out
diff --git a/test/shell/lvcreate-cache.sh b/test/shell/lvcreate-cache.sh
index 010c06f..0663033 100644
--- a/test/shell/lvcreate-cache.sh
+++ b/test/shell/lvcreate-cache.sh
@@ -256,20 +256,23 @@ lvdisplay --maps $vg
lvremove -f $vg
+# migration_threshold is protected to not be smaller then 8*chunk_size
+# so even when user sets migration threshold to lower value,
+# activation will ensure the minimal size is preserved
lvcreate --type cache-pool -L10 $vg/cpool
-lvcreate --type cache -l 1 --cachepool $vg/cpool -n corigin $vg --cachesettings migration_threshold=233
+lvcreate --type cache -l 1 --cachepool $vg/cpool -c 64k -n corigin $vg --cachesettings migration_threshold=233
dmsetup status | grep $vg
-dmsetup status | grep $vg-corigin | grep 'migration_threshold 233'
+dmsetup status | grep $vg-corigin | grep 'migration_threshold 1024'
lvchange -an $vg
lvchange -ay $vg
-dmsetup status | grep $vg-corigin | grep 'migration_threshold 233'
+dmsetup status | grep $vg-corigin | grep 'migration_threshold 1024'
lvremove -f $vg
-lvcreate --type cache-pool -L10 --cachepolicy mq --cachesettings migration_threshold=233 $vg/cpool
+lvcreate --type cache-pool -L10 --cachepolicy mq --cachesettings migration_threshold=1233 $vg/cpool
lvcreate --type cache -l 1 --cachepool $vg/cpool -n corigin $vg
dmsetup status | grep $vg
-dmsetup status | grep $vg-corigin | grep 'migration_threshold 233'
+dmsetup status | grep $vg-corigin | grep 'migration_threshold 1233'
lvremove -f $vg
@@ -281,7 +284,7 @@ lvremove -f $vg
# Creation of read-only cache pool is not supported
invalid lvcreate -pr --type cache-pool -l1 -n $vg/cpool
-# Atempt to use bigger chunk size then cache pool data size
+# Attempt to use bigger chunk size then cache pool data size
fail lvcreate -l 1 --type cache-pool --chunksize 16M $vg 2>out
grep "chunk size" out
3 years, 9 months
master - pvck: dump headers and metadata
by David Teigland
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=d18e491f6890e15a677...
Commit: d18e491f6890e15a677e8189de0699ccc7412f16
Parent: 669a834981c0bc808d42dbe5097d7beeead982ad
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Fri May 31 14:10:44 2019 -0500
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Mon Jun 3 15:13:32 2019 -0500
pvck: dump headers and metadata
Add 'pvck --dump headers' to print all the
lvm ondisk structs. Also checks the values
and prints any problems.
The previous dump metadata is also converted to
use these same routines, which do not depend on lvm
fully scanning/reading/processing the headers and
metadata on disk. This makes it useful to get data in
cases where there is corruption that would otherwise
prevent the normal functions from working.
---
lib/format_text/format-text.c | 218 ----------
lib/format_text/format-text.h | 14 -
tools/args.h | 5 +-
tools/command-lines.in | 6 +-
tools/pvck.c | 888 ++++++++++++++++++++++++++++++++++++++---
5 files changed, 848 insertions(+), 283 deletions(-)
diff --git a/lib/format_text/format-text.c b/lib/format_text/format-text.c
index abf8afc..3828d5f 100644
--- a/lib/format_text/format-text.c
+++ b/lib/format_text/format-text.c
@@ -2602,221 +2602,3 @@ bad:
return NULL;
}
-
-static char *_read_metadata_text(struct cmd_context *cmd, struct device *dev,
- uint64_t area_start, uint64_t area_size,
- uint32_t *len, uint64_t *disk_offset)
-{
- struct mda_header *mh;
- struct raw_locn *rlocn_slot0;
- uint64_t text_offset, text_size;
- char *area_buf;
- char *text_buf;
-
- /*
- * Read the entire metadata area, including mda_header and entire
- * circular buffer.
- */
- if (!(area_buf = malloc(area_size)))
- return_NULL;
-
- if (!dev_read_bytes(dev, area_start, area_size, area_buf)) {
- log_error("Failed to read device %s at %llu size %llu",
- dev_name(dev),
- (unsigned long long)area_start,
- (unsigned long long)area_size);
- return NULL;
- }
-
- mh = (struct mda_header *)area_buf;
- _xlate_mdah(mh);
-
- rlocn_slot0 = &mh->raw_locns[0];
- text_offset = rlocn_slot0->offset;
- text_size = rlocn_slot0->size;
-
- /*
- * Copy and return the current metadata text out of the metadata area.
- */
-
- if (!(text_buf = malloc(text_size)))
- return_NULL;
-
- memcpy(text_buf, area_buf + text_offset, text_size);
-
- if (len)
- *len = (uint32_t)text_size;
- if (disk_offset)
- *disk_offset = area_start + text_offset;
-
- free(area_buf);
-
- return text_buf;
-}
-
-int dump_metadata_text(struct cmd_context *cmd,
- const char *vgname,
- const char *vgid,
- struct device *dev,
- struct metadata_area *mda,
- const char *tofile)
-{
- char *textbuf;
- struct format_instance *fid;
- struct format_instance_ctx fic;
- struct mda_context *mdac;
- struct volume_group *vg;
- unsigned use_previous_vg = 0;
- uint32_t textlen = 0;
- uint32_t textcrc;
- uint64_t text_disk_offset;
- int ret = 0;
-
- /*
- * Set up overhead/abstractions for reading a given vgname
- * (fmt/fid/fic/vgid).
- */
-
- fic.type = FMT_INSTANCE_MDAS | FMT_INSTANCE_AUX_MDAS;
- fic.context.vg_ref.vg_name = vgname;
- fic.context.vg_ref.vg_id = vgid;
-
- if (!(fid = _text_create_text_instance(cmd->fmt, &fic))) {
- log_error("Failed to create format instance");
- return 0;
- }
-
- mdac = mda->metadata_locn;
-
- /*
- * Read the VG metadata from the device as a raw chunk of original text.
- */
- textbuf = _read_metadata_text(cmd, dev,
- mdac->area.start, mdac->area.size,
- &textlen, &text_disk_offset);
- if (!textbuf || !textlen) {
- log_error("No metadata text found on %s", dev_name(dev));
- _text_destroy_instance(fid);
- return 0;
- }
-
- textcrc = calc_crc(INITIAL_CRC, (uint8_t *)textbuf, textlen);
-
- /*
- * Read the same VG metadata, but imported/parsed into a vg struct
- * format so we know it's valid/parsable, and can look at values in it.
- */
- if (!(vg = _vg_read_raw(fid, vgname, mda, NULL, &use_previous_vg))) {
- log_warn("WARNING: parse error for metadata on %s.", dev_name(dev));
- _text_destroy_instance(fid);
- }
-
- log_print("Metadata for %s from %s at %llu size %u with seqno %u checksum 0x%x.",
- vgname, dev_name(dev),
- (unsigned long long)text_disk_offset, textlen,
- vg ? vg->seqno : 0, textcrc);
-
- if (!tofile) {
- log_print("---");
- printf("%s\n", textbuf);
- log_print("---");
- } else {
- FILE *fp;
- if (!(fp = fopen(tofile, "wx"))) {
- log_error("Failed to create file %s", tofile);
- goto out;
- }
-
- fprintf(fp, "%s", textbuf);
-
- if (fflush(fp))
- stack;
- if (fclose(fp))
- stack;
- }
-
- if (vg)
- release_vg(vg);
-
- free(textbuf);
- ret = 1;
-out:
- return ret;
-}
-
-static char *_read_metadata_area(struct cmd_context *cmd, struct device *dev,
- uint64_t area_start, uint64_t area_size)
-{
- char *area_buf;
-
- /*
- * Read the entire metadata area, including mda_header and entire
- * circular buffer.
- */
- if (!(area_buf = malloc(area_size)))
- return_NULL;
-
- if (!dev_read_bytes(dev, area_start, area_size, area_buf)) {
- log_error("Failed to read device %s at %llu size %llu",
- dev_name(dev),
- (unsigned long long)area_start,
- (unsigned long long)area_size);
- return NULL;
- }
-
- return area_buf;
-}
-
-int dump_metadata_area(struct cmd_context *cmd,
- const char *vgname,
- const char *vgid,
- struct device *dev,
- struct metadata_area *mda,
- const char *tofile)
-{
- char *areabuf;
- char *textbuf;
- struct mda_context *mdac;
- int ret = 0;
-
- mdac = mda->metadata_locn;
-
- areabuf = _read_metadata_area(cmd, dev,
- mdac->area.start, mdac->area.size);
- if (!areabuf) {
- log_error("No metadata area found on %s", dev_name(dev));
- return 0;
- }
-
- log_print("Metadata buffer for %s from %s in area at %llu size %llu offset 512.",
- vgname, dev_name(dev),
- (unsigned long long)mdac->area.start,
- (unsigned long long)mdac->area.size);
-
- /* text starts after mda_header which uses 512 bytes */
- textbuf = areabuf + 512;
-
- if (!tofile) {
- /* N.B. this will often include unprintable data */
- log_print("---");
- fwrite(textbuf, mdac->area.size - 512, 1, stdout);
- log_print("---");
- } else {
- FILE *fp;
- if (!(fp = fopen(tofile, "wx"))) {
- log_error("Failed to create file %s", tofile);
- goto out;
- }
-
- fwrite(textbuf, mdac->area.size - 512, 1, fp);
-
- if (fflush(fp))
- stack;
- if (fclose(fp))
- stack;
- }
- ret = 1;
-out:
- free(areabuf);
- return ret;
-}
diff --git a/lib/format_text/format-text.h b/lib/format_text/format-text.h
index 6be237b..1300e58 100644
--- a/lib/format_text/format-text.h
+++ b/lib/format_text/format-text.h
@@ -76,18 +76,4 @@ struct data_area_list {
struct disk_locn disk_locn;
};
-int dump_metadata_text(struct cmd_context *cmd,
- const char *vgname,
- const char *vgid,
- struct device *dev,
- struct metadata_area *mda,
- const char *tofile);
-
-int dump_metadata_area(struct cmd_context *cmd,
- const char *vgname,
- const char *vgid,
- struct device *dev,
- struct metadata_area *mda,
- const char *tofile);
-
#endif
diff --git a/tools/args.h b/tools/args.h
index c1efdd6..83c1cfe 100644
--- a/tools/args.h
+++ b/tools/args.h
@@ -215,8 +215,9 @@ arg(driverloaded_ARG, '\0', "driverloaded", bool_VAL, 0, 0,
arg(dump_ARG, '\0', "dump", string_VAL, 0, 0,
"Dump metadata from a PV. Option values include \\fBmetadata\\fP\n"
- "to extract the current text metadata, and \\fBmetadata_area\\fP\n"
- "to extract the entire text metadata area.\n")
+ "to extract the current text metadata, \\fBmetadata_area\\fP\n"
+ "to extract the entire text metadata area, and \\fBheaders\\fP\n"
+ "to print and check LVM headers.\n")
arg(errorwhenfull_ARG, '\0', "errorwhenfull", bool_VAL, 0, 0,
"Specifies thin pool behavior when data space is exhausted.\n"
diff --git a/tools/command-lines.in b/tools/command-lines.in
index 0d97348..03dbf57 100644
--- a/tools/command-lines.in
+++ b/tools/command-lines.in
@@ -1430,9 +1430,9 @@ ID: pvck_general
DESC: Check for metadata on a device
pvck --dump String PV
-OO: --file String, --pvmetadatacopies MetadataCopiesPV
-ID: pvck_dumpmetadata
-DESC: Dump raw metadata from a device
+OO: --file String, --pvmetadatacopies MetadataCopiesPV, --labelsector Number
+ID: pvck_dump
+DESC: Print metadata from a device
---
diff --git a/tools/pvck.c b/tools/pvck.c
index 15c7e6d..9e690a8 100644
--- a/tools/pvck.c
+++ b/tools/pvck.c
@@ -16,38 +16,757 @@
#include "base/memory/zalloc.h"
#include "tools.h"
#include "lib/format_text/format-text.h"
+#include "lib/format_text/layout.h"
+#include "lib/mm/xlate.h"
+#include "lib/misc/crc.h"
+
+static char *_chars_to_str(void *in, void *out, int num, int max, const char *field)
+{
+ char *i = in;
+ char *o = out;
+ int n;
+
+ memset(out, 0, max);
+
+ if (num > max-1) {
+ log_print("CHECK: abbreviating output for %s", field);
+ num = max - 1;
+ }
+
+ for (n = 0; n < num; n++) {
+ if (isprint((int)*i))
+ *o = *i;
+ else
+ *o = '?';
+ i++;
+ o++;
+ }
+
+ return out;
+}
+
+/*
+ * This is used to print mda_header.magic as a series of hex values
+ * since it only contains some printable chars.
+ */
+static char *_chars_to_hexstr(void *in, void *out, int num, int max, const char *field)
+{
+ char *tmp;
+ char *i = in;
+ int n;
+ int off = 0;
+ int ret;
+
+ if (!(tmp = zalloc(max))) {
+ log_print("CHECK: no mem for printing %s", field);
+ return out;
+ }
+
+ memset(out, 0, max);
+ memset(tmp, 0, max);
+
+ if (num > max-1) {
+ log_print("CHECK: abbreviating output for %s", field);
+ num = max - 1;
+ }
+
+ for (n = 0; n < num; n++) {
+ ret = sprintf(tmp+off, "%x", *i & 0xFF);
+ off += ret;
+ i++;
+ }
+
+ memcpy(out, tmp, 256);
+
+ free(tmp);
+
+ return out;
+}
+
+static int _check_label_header(struct label_header *lh, uint64_t labelsector)
+{
+ uint32_t crc;
+ int bad = 0;
+
+ if (memcmp(lh->id, LABEL_ID, sizeof(lh->id))) {
+ log_print("CHECK: label_header.id expected %s", LABEL_ID);
+ bad++;
+ }
+
+ if (xlate64(lh->sector_xl) != labelsector) {
+ log_print("CHECK: label_header.sector expected %d", (int)labelsector);
+ bad++;
+ }
+
+ crc = calc_crc(INITIAL_CRC, (uint8_t *)&lh->offset_xl,
+ LABEL_SIZE - ((uint8_t *) &lh->offset_xl - (uint8_t *) lh));
+
+ if (crc != xlate32(lh->crc_xl)) {
+ log_print("CHECK: label_header.crc expected 0x%x", crc);
+ bad++;
+ }
+
+ if (xlate32(lh->offset_xl) != 32) {
+ log_print("CHECK: label_header.offset expected 32");
+ bad++;
+ }
+
+ if (memcmp(lh->type, LVM2_LABEL, sizeof(lh->type))) {
+ log_print("CHECK: label_header.type expected %s", LVM2_LABEL);
+ bad++;
+ }
+
+ if (bad)
+ return 0;
+ return 1;
+}
+
+static int _check_pv_header(struct pv_header *ph)
+{
+ struct id id;
+ int bad = 0;
+
+ if (!id_read_format_try(&id, (char *)&ph->pv_uuid)) {
+ log_print("CHECK: pv_header.pv_uuid invalid format");
+ bad++;
+ }
+
+ if (bad)
+ return 0;
+ return 1;
+}
/*
- * TODO: option to dump all copies of metadata that are found
+ * mda_offset/mda_size are from the pv_header/disk_locn and could
+ * be incorrect.
+ */
+static int _check_mda_header(struct mda_header *mh, int mda_num, uint64_t mda_offset, uint64_t mda_size)
+{
+ char str[256];
+ uint32_t crc;
+ int bad = 0;
+
+ crc = calc_crc(INITIAL_CRC, (uint8_t *)mh->magic,
+ MDA_HEADER_SIZE - sizeof(mh->checksum_xl));
+
+ if (crc != xlate32(mh->checksum_xl)) {
+ log_print("CHECK: mda_header_%d.checksum expected 0x%x", mda_num, crc);
+ bad++;
+ }
+
+ if (memcmp(mh->magic, FMTT_MAGIC, sizeof(mh->magic))) {
+ log_print("CHECK: mda_header_%d.magic expected 0x%s", mda_num, _chars_to_hexstr((void *)&FMTT_MAGIC, str, 16, 256, "mda_header.magic"));
+ bad++;
+ }
+
+ if (xlate32(mh->version) != FMTT_VERSION) {
+ log_print("CHECK: mda_header_%d.version expected %u", mda_num, FMTT_VERSION);
+ bad++;
+ }
+
+ if (xlate64(mh->start) != mda_offset) {
+ log_print("CHECK: mda_header_%d.start does not match pv_header.disk_locn.offset %llu", mda_num, (unsigned long long)mda_offset);
+ bad++;
+ }
+
+ if (xlate64(mh->size) != mda_size) {
+ log_print("CHECK: mda_header_%d.size does not match pv_header.disk_locn.size %llu", mda_num, (unsigned long long)mda_size);
+ bad++;
+ }
+
+ if (bad)
+ return 0;
+ return 1;
+}
+
+/*
+ *
+ * mda_offset, mda_size are from pv_header.disk_locn
+ * (the location of the metadata area.)
*
- * TODO: option to intelligently search for mda locations on
- * disk in case the pv_header and/or mda_header are damaged.
+ * meta_offset, meta_size, meta_checksum are from mda_header.raw_locn
+ * (the location of the metadata text in the metadata area.)
*/
-static int _dump_metadata(struct cmd_context *cmd, int argc, char **argv, int full_area)
+static int _dump_raw_locn(struct device *dev, int print_fields,
+ struct raw_locn *rlocn, int rlocn_index, uint64_t rlocn_offset,
+ int mda_num, uint64_t mda_offset, uint64_t mda_size,
+ uint64_t *meta_offset_ret,
+ uint64_t *meta_size_ret,
+ uint32_t *meta_checksum_ret)
{
- struct dm_list devs;
- struct device_list *devl;
- struct device *dev;
- const char *pv_name;
- const char *vgname;
- const char *vgid;
- struct lvmcache_info *info;
- struct metadata_area *mda;
- const char *tofile = NULL;
- int mda_num = 1;
- int ret;
+ uint64_t meta_offset, meta_size;
+ uint32_t meta_checksum;
+ uint32_t meta_flags;
+ int bad = 0;
+ int mn = mda_num; /* 1 or 2 */
+ int ri = rlocn_index; /* 0 or 1 */
+ int wrapped = 0;
+
+ meta_offset = xlate64(rlocn->offset);
+ meta_size = xlate64(rlocn->size);
+ meta_checksum = xlate32(rlocn->checksum);
+ meta_flags = xlate32(rlocn->flags);
+
+ if (meta_offset + meta_size > mda_size)
+ wrapped = 1;
+
+ if (print_fields) {
+ log_print("mda_header_%d.raw_locn[%d] at %llu # %s%s", mn, ri, (unsigned long long)rlocn_offset, (ri == 0) ? "commit" : "precommit", wrapped ? " wrapped" : "");
+ log_print("mda_header_%d.raw_locn[%d].offset %llu", mn, ri, (unsigned long long)meta_offset);
+ log_print("mda_header_%d.raw_locn[%d].size %llu", mn, ri, (unsigned long long)meta_size);
+ log_print("mda_header_%d.raw_locn[%d].checksum 0x%x", mn, ri, meta_checksum);
+
+ if (meta_flags & RAW_LOCN_IGNORED)
+ log_print("mda_header_%d.raw_locn[%d].flags 0x%x # RAW_LOCN_IGNORED", mn, ri, meta_flags);
+ else
+ log_print("mda_header_%d.raw_locn[%d].flags 0x%x", mn, ri, meta_flags);
+ }
- dm_list_init(&devs);
+ /* The precommit pointer will usually be empty. */
+ if ((rlocn_index == 1) && meta_offset)
+ log_print("CHECK: mda_header_%d.raw_locn[%d] for precommit not empty", mn, ri);
+
+ /* This metadata area is not being used to hold text metadata. */
+ /* Old, out of date text metadata may exist if the area was once used. */
+ if (meta_flags & RAW_LOCN_IGNORED)
+ return 1;
+
+ /*
+ * A valid meta_size can be no larger than the metadata area size minus
+ * the 512 bytes used by the mda_header sector.
+ */
+ if (meta_size > (mda_size - 512)) {
+ log_print("CHECK: mda_header_%d.raw_locn[%d].size larger than metadata area size", mn, ri);
+ /* If meta_size is bad, try to continue using a reasonable value */
+ meta_size = (mda_size - 512);
+ }
- if (arg_is_set(cmd, file_ARG)) {
- if (!(tofile = arg_str_value(cmd, file_ARG, NULL)))
- return ECMD_FAILED;
+ if (meta_offset_ret)
+ *meta_offset_ret = meta_offset;
+ if (meta_size_ret)
+ *meta_size_ret = meta_size;
+ if (meta_checksum_ret)
+ *meta_checksum_ret = meta_checksum;
+
+ /* No text metadata exists in this metadata area. */
+ if (!meta_offset)
+ return 1;
+
+ if (bad)
+ return 0;
+ return 1;
+}
+
+static int _dump_meta_area(struct device *dev, const char *tofile,
+ uint64_t mda_offset, uint64_t mda_size)
+{
+ FILE *fp;
+ char *meta_buf;
+
+ if (!tofile)
+ return_0;
+
+ if (!(meta_buf = malloc(mda_size)))
+ return_0;
+ memset(meta_buf, 0, mda_size);
+
+ if (!dev_read_bytes(dev, mda_offset, mda_size, meta_buf)) {
+ log_print("CHECK: failed to read metadata area at offset %llu size %llu",
+ (unsigned long long)mda_offset, (unsigned long long)mda_size);
+ return 0;
}
- /* 1: dump metadata from first mda, 2: dump metadata from second mda */
- if (arg_is_set(cmd, pvmetadatacopies_ARG))
- mda_num = arg_int_value(cmd, pvmetadatacopies_ARG, 1);
+ if (!(fp = fopen(tofile, "wx"))) {
+ log_error("Failed to create file %s", tofile);
+ return 0;
+ }
+
+ fwrite(meta_buf, mda_size - 512, 1, fp);
+
+ if (fflush(fp))
+ stack;
+ if (fclose(fp))
+ stack;
+ return 1;
+}
+
+static int _dump_meta_text(struct device *dev,
+ int print_fields, int print_metadata, const char *tofile,
+ int mda_num, int rlocn_index,
+ uint64_t mda_offset, uint64_t mda_size,
+ uint64_t meta_offset, uint64_t meta_size,
+ uint32_t meta_checksum)
+{
+ char *meta_buf;
+ struct dm_config_tree *cft;
+ const char *vgname = NULL;
+ uint32_t crc;
+ int mn = mda_num; /* 1 or 2 */
+ int ri = rlocn_index; /* 0 or 1 */
+ int bad = 0;
+
+ if (!(meta_buf = malloc(meta_size))) {
+ log_print("CHECK: mda_header_%d.raw_locn[%d] no mem for metadata text size %llu", mn, ri,
+ (unsigned long long)meta_size);
+ return 0;
+ }
+ memset(meta_buf, 0, meta_size);
+
+ /*
+ * Read the metadata text specified by the raw_locn so we can
+ * check the raw_locn values.
+ *
+ * meta_offset is the offset from the start of the mda_header,
+ * so the text location from the start of the disk is
+ * mda_offset + meta_offset.
+ */
+ if (meta_offset + meta_size > mda_size) {
+ /* text metadata wraps to start of text metadata area */
+ uint32_t wrap = (uint32_t) ((meta_offset + meta_size) - mda_size);
+ off_t offset_a = mda_offset + meta_offset;
+ uint32_t size_a = meta_size - wrap;
+ off_t offset_b = mda_offset + 512; /* continues after mda_header sector */
+ uint32_t size_b = wrap;
+
+ if (!dev_read_bytes(dev, offset_a, size_a, meta_buf)) {
+ log_print("CHECK: failed to read metadata text at mda_header_%d.raw_locn[%d].offset %llu size %llu part_a %llu %llu", mn, ri,
+ (unsigned long long)meta_offset, (unsigned long long)meta_size,
+ (unsigned long long)offset_a, (unsigned long long)size_a);
+ return 0;
+ }
+
+ if (!dev_read_bytes(dev, offset_b, size_b, meta_buf + size_a)) {
+ log_print("CHECK: failed to read metadata text at mda_header_%d.raw_locn[%d].offset %llu size %llu part_b %llu %llu", mn, ri,
+ (unsigned long long)meta_offset, (unsigned long long)meta_size,
+ (unsigned long long)offset_b, (unsigned long long)size_b);
+ return 0;
+ }
+ } else {
+ if (!dev_read_bytes(dev, mda_offset + meta_offset, meta_size, meta_buf)) {
+ log_print("CHECK: failed to read metadata text at mda_header_%d.raw_locn[%d].offset %llu size %llu", mn, ri,
+ (unsigned long long)meta_offset, (unsigned long long)meta_size);
+ return 0;
+ }
+ }
+
+ crc = calc_crc(INITIAL_CRC, (uint8_t *)meta_buf, meta_size);
+ if (crc != meta_checksum) {
+ log_print("CHECK: metadata text at %llu crc does not match mda_header_%d.raw_locn[%d].checksum",
+ (unsigned long long)(mda_offset + meta_offset), mn, ri);
+ bad++;
+ }
+
+ if (!(cft = config_open(CONFIG_FILE_SPECIAL, NULL, 0))) {
+ log_print("CHECK: failed to set up metadata parsing");
+ bad++;
+ } else {
+ if (!dm_config_parse(cft, meta_buf, meta_buf + meta_size)) {
+ log_print("CHECK: failed to parse metadata text at %llu size %llu",
+ (unsigned long long)(mda_offset + meta_offset),
+ (unsigned long long)meta_size);
+ bad++;
+ } else {
+ vgname = strdup(cft->root->key);
+ }
+ config_destroy(cft);
+ }
+
+ log_print("metadata text at %llu crc 0x%x # vgname %s",
+ (unsigned long long)(mda_offset + meta_offset), crc,
+ vgname ? vgname : "?");
+
+ if (!print_metadata)
+ goto out;
+
+ if (!tofile) {
+ log_print("---");
+ printf("%s\n", meta_buf);
+ log_print("---");
+ } else {
+ FILE *fp;
+ if (!(fp = fopen(tofile, "wx"))) {
+ log_error("Failed to create file %s", tofile);
+ goto out;
+ }
+
+ fprintf(fp, "%s", meta_buf);
+
+ if (fflush(fp))
+ stack;
+ if (fclose(fp))
+ stack;
+ }
+
+ out:
+ if (bad)
+ return 0;
+ return 1;
+}
+
+static int _dump_label_and_pv_header(struct cmd_context *cmd, int print_fields,
+ struct device *dev,
+ uint64_t *mda1_offset, uint64_t *mda1_size,
+ uint64_t *mda2_offset, uint64_t *mda2_size)
+{
+ char str[256];
+ struct label_header *lh;
+ struct pv_header *pvh;
+ struct pv_header_extension *pvhe;
+ struct disk_locn *dlocn;
+ uint64_t lh_offset;
+ uint64_t pvh_offset;
+ uint64_t pvhe_offset;
+ uint64_t dlocn_offset;
+ char *buf;
+ uint64_t labelsector;
+ uint64_t tmp;
+ int mda_count = 0;
+ int bad = 0;
+ int di;
+
+ /*
+ * By default LVM skips the first sector (sector 0), and writes
+ * the label_header in the second sector (sector 1).
+ * (sector size 512 bytes)
+ */
+ if (arg_is_set(cmd, labelsector_ARG))
+ labelsector = arg_uint64_value(cmd, labelsector_ARG, UINT64_C(0));
+ else
+ labelsector = 1;
+
+ lh_offset = labelsector * 512; /* from start of disk */
+
+ if (!(buf = zalloc(512)))
+ return_0;
+
+ if (!dev_read_bytes(dev, lh_offset, 512, buf)) {
+ log_print("CHECK: failed to read label_header at %llu",
+ (unsigned long long)lh_offset);
+ return 0;
+ }
+
+ lh = (struct label_header *)buf;
+
+ if (print_fields) {
+ log_print("label_header at %llu", (unsigned long long)lh_offset);
+ log_print("label_header.id %s", _chars_to_str(lh->id, str, 8, 256, "label_header.id"));
+ log_print("label_header.sector %llu", (unsigned long long)xlate64(lh->sector_xl));
+ log_print("label_header.crc 0x%x", xlate32(lh->crc_xl));
+ log_print("label_header.offset %u", xlate32(lh->offset_xl));
+ log_print("label_header.type %s", _chars_to_str(lh->type, str, 8, 256, "label_header.type"));
+ }
+
+ if (!_check_label_header(lh, labelsector))
+ bad++;
+
+ /*
+ * The label_header is 32 bytes in size (size of struct label_header).
+ * The pv_header should begin immediately after the label_header.
+ * The label_header.offset gives the offset of pv_header from the
+ * start of the label_header, which should always be 32.
+ *
+ * If label_header.offset is corrupted, then we should print a
+ * warning about the bad value, and read the pv_header from the
+ * correct location instead of the bogus location.
+ */
+
+ pvh = (struct pv_header *)(buf + 32);
+ pvh_offset = lh_offset + 32; /* from start of disk */
+
+ /* sanity check */
+ if ((void *)pvh != (void *)(buf + pvh_offset - lh_offset))
+ log_print("CHECK: problem with pv_header offset calculation");
+
+ if (print_fields) {
+ log_print("pv_header at %llu", (unsigned long long)pvh_offset);
+ log_print("pv_header.pv_uuid %s", _chars_to_str(pvh->pv_uuid, str, ID_LEN, 256, "pv_header.pv_uuid"));
+ log_print("pv_header.device_size %llu", (unsigned long long)xlate64(pvh->device_size_xl));
+ }
+
+ if (!_check_pv_header(pvh))
+ bad++;
+
+ /*
+ * The pv_header is 40 bytes, excluding disk_locn's.
+ * disk_locn structs immediately follow the pv_header.
+ * Each disk_locn is 16 bytes.
+ */
+ di = 0;
+ dlocn = pvh->disk_areas_xl;
+ dlocn_offset = pvh_offset + 40; /* from start of disk */
+
+ /* sanity check */
+ if ((void *)dlocn != (void *)(buf + dlocn_offset - lh_offset))
+ log_print("CHECK: problem with pv_header.disk_locn[%d] offset calculation", di);
+
+ while ((tmp = xlate64(dlocn->offset))) {
+ if (print_fields) {
+ log_print("pv_header.disk_locn[%d] at %llu # location of data area", di,
+ (unsigned long long)dlocn_offset);
+ log_print("pv_header.disk_locn[%d].offset %llu", di,
+ (unsigned long long)xlate64(dlocn->offset));
+ log_print("pv_header.disk_locn[%d].size %llu", di,
+ (unsigned long long)xlate64(dlocn->size));
+ }
+ di++;
+ dlocn++;
+ dlocn_offset += 16;
+ }
+
+ /* all-zero dlocn struct is area list end */
+ if (print_fields) {
+ log_print("pv_header.disk_locn[%d] at %llu # location list end", di,
+ (unsigned long long) dlocn_offset);
+ log_print("pv_header.disk_locn[%d].offset %llu", di,
+ (unsigned long long)xlate64(dlocn->offset));
+ log_print("pv_header.disk_locn[%d].size %llu", di,
+ (unsigned long long)xlate64(dlocn->size));
+ }
+
+ /* advance past the all-zero dlocn struct */
+ di++;
+ dlocn++;
+ dlocn_offset += 16;
+
+ /* sanity check */
+ if ((void *)dlocn != (void *)(buf + dlocn_offset - lh_offset))
+ log_print("CHECK: problem with pv_header.disk_locn[%d] offset calculation", di);
+
+ while ((tmp = xlate64(dlocn->offset))) {
+ if (print_fields) {
+ log_print("pv_header.disk_locn[%d] at %llu # location of metadata area", di,
+ (unsigned long long)dlocn_offset);
+ log_print("pv_header.disk_locn[%d].offset %llu", di,
+ (unsigned long long)xlate64(dlocn->offset));
+ log_print("pv_header.disk_locn[%d].size %llu", di,
+ (unsigned long long)xlate64(dlocn->size));
+ }
+
+ if (!mda_count) {
+ *mda1_offset = xlate64(dlocn->offset);
+ *mda1_size = xlate64(dlocn->size);
+
+ if (*mda1_offset != 4096) {
+ log_print("CHECK: pv_header.disk_locn[%d].offset expected 4096 # for first mda", di);
+ bad++;
+ }
+ } else {
+ *mda2_offset = xlate64(dlocn->offset);
+ *mda2_size = xlate64(dlocn->size);
+
+ /*
+ * No fixed location for second mda, so we have to look for
+ * mda_header at this offset to see if it's correct.
+ */
+ }
+
+ di++;
+ dlocn++;
+ dlocn_offset += 16;
+ mda_count++;
+ }
+
+ /* all-zero dlocn struct is area list end */
+ if (print_fields) {
+ log_print("pv_header.disk_locn[%d] at %llu # location list end", di,
+ (unsigned long long) dlocn_offset);
+ log_print("pv_header.disk_locn[%d].offset %llu", di,
+ (unsigned long long)xlate64(dlocn->offset));
+ log_print("pv_header.disk_locn[%d].size %llu", di,
+ (unsigned long long)xlate64(dlocn->size));
+ }
+
+ /* advance past the all-zero dlocn struct */
+ di++;
+ dlocn++;
+ dlocn_offset += 16;
+
+ /*
+ * pv_header_extension follows the last disk_locn
+ * terminating struct, so it's not always at the
+ * same location.
+ */
+
+ pvhe = (struct pv_header_extension *)dlocn;
+ pvhe_offset = dlocn_offset; /* from start of disk */
+
+ /* sanity check */
+ if ((void *)pvhe != (void *)(buf + pvhe_offset - lh_offset))
+ log_print("CHECK: problem with pv_header_extension offset calculation");
+
+ if (print_fields) {
+ log_print("pv_header_extension at %llu", (unsigned long long)pvhe_offset);
+ log_print("pv_header_extension.version %u", xlate32(pvhe->version));
+ log_print("pv_header_extension.flags %u", xlate32(pvhe->flags));
+ }
+
+ /*
+ * The pv_header_extension is 8 bytes, excluding disk_locn's.
+ * disk_locn structs immediately follow the pv_header_extension.
+ * Each disk_locn is 16 bytes.
+ */
+ di = 0;
+ dlocn = pvhe->bootloader_areas_xl;
+ dlocn_offset = pvhe_offset + 8;
+
+ while ((tmp = xlate64(dlocn->offset))) {
+ if (print_fields) {
+ log_print("pv_header_extension.disk_locn[%d] at %llu # bootloader area", di,
+ (unsigned long long)dlocn_offset);
+ log_print("pv_header_extension.disk_locn[%d].offset %llu", di,
+ (unsigned long long)xlate64(dlocn->offset));
+ log_print("pv_header_extension.disk_locn[%d].size %llu", di,
+ (unsigned long long)xlate64(dlocn->size));
+ }
+
+ di++;
+ dlocn++;
+ dlocn_offset += 16;
+ }
+
+ /* all-zero dlocn struct is area list end */
+ if (print_fields) {
+ log_print("pv_header_extension.disk_locn[%d] at %llu # location list end", di,
+ (unsigned long long) dlocn_offset);
+ log_print("pv_header_extension.disk_locn[%d].offset %llu", di,
+ (unsigned long long)xlate64(dlocn->offset));
+ log_print("pv_header_extension.disk_locn[%d].size %llu", di,
+ (unsigned long long)xlate64(dlocn->size));
+ }
+
+ if (bad)
+ return 0;
+ return 1;
+}
+
+/*
+ * mda_offset and mda_size are the location/size of the metadata area,
+ * which starts with the mda_header and continues through the circular
+ * buffer of text.
+ *
+ * mda_offset and mda_size values come from the pv_header/disk_locn,
+ * which could be incorrect.
+ *
+ * We know that the first mda_offset will always be 4096, so we use
+ * that value regardless of what the first mda_offset value in the
+ * pv_header is.
+ */
+
+static int _dump_mda_header(struct cmd_context *cmd,
+ int print_fields, int print_metadata, int print_area,
+ const char *tofile,
+ struct device *dev,
+ uint64_t mda_offset, uint64_t mda_size,
+ uint32_t *checksum0_ret)
+{
+ char str[256];
+ char *buf;
+ struct mda_header *mh;
+ struct raw_locn *rlocn0, *rlocn1;
+ uint64_t rlocn0_offset, rlocn1_offset;
+ uint64_t meta_offset = 0;
+ uint64_t meta_size = 0;
+ uint32_t meta_checksum = 0;
+ int mda_num = (mda_offset == 4096) ? 1 : 2;
+ int bad = 0;
+
+ *checksum0_ret = 0; /* checksum from raw_locn[0] */
+
+ if (!(buf = zalloc(512)))
+ return_0;
+
+ /*
+ * The first mda_header is 4096 bytes from the start
+ * of the device. Each mda_header is 512 bytes.
+ *
+ * The start/size values in the mda_header should
+ * match the mda_offset/mda_size values that came
+ * from the pv_header/disk_locn.
+ *
+ * (Why was mda_header magic made only partially printable?)
+ */
+
+ if (!dev_read_bytes(dev, mda_offset, 512, buf)) {
+ log_print("CHECK: failed to read mda_header at %llu", (unsigned long long)mda_offset);
+ return 0;
+ }
+
+ mh = (struct mda_header *)buf;
+
+ if (print_fields) {
+ log_print("mda_header_%d at %llu # metadata area", mda_num, (unsigned long long)mda_offset);
+ log_print("mda_header_%d.checksum 0x%x", mda_num, xlate32(mh->checksum_xl));
+ log_print("mda_header_%d.magic 0x%s", mda_num, _chars_to_hexstr(mh->magic, str, 16, 256, "mda_header.magic"));
+ log_print("mda_header_%d.version %u", mda_num, xlate32(mh->version));
+ log_print("mda_header_%d.start %llu", mda_num, (unsigned long long)xlate64(mh->start));
+ log_print("mda_header_%d.size %llu", mda_num, (unsigned long long)xlate64(mh->size));
+ }
+
+ if (!_check_mda_header(mh, mda_num, mda_offset, mda_size))
+ bad++;
+
+ if (print_area) {
+ if (!_dump_meta_area(dev, tofile, mda_offset, mda_size))
+ bad++;
+ goto out;
+ }
+
+ /*
+ * mda_header is 40 bytes, the raw_locn structs
+ * follow immediately after, each raw_locn struct
+ * is 24 bytes.
+ */
+
+ rlocn0 = mh->raw_locns;
+ rlocn0_offset = mda_offset + 40; /* from start of disk */
+
+ /* sanity check */
+ if ((void *)rlocn0 != (void *)(buf + rlocn0_offset - mda_offset))
+ log_print("CHECK: problem with rlocn0 offset calculation");
+
+ meta_offset = 0;
+ meta_size = 0;
+ meta_checksum = 0;
+
+ if (!_dump_raw_locn(dev, print_fields, rlocn0, 0, rlocn0_offset, mda_num, mda_offset, mda_size,
+ &meta_offset, &meta_size, &meta_checksum))
+ bad++;
+
+ *checksum0_ret = meta_checksum;
+
+ rlocn1 = (struct raw_locn *)((char *)mh->raw_locns + 24);
+ rlocn1_offset = rlocn0_offset + 24;
+
+ /* sanity check */
+ if ((void *)rlocn1 != (void *)(buf + rlocn1_offset - mda_offset))
+ log_print("CHECK: problem with rlocn1 offset calculation");
+
+ if (!_dump_raw_locn(dev, print_fields, rlocn1, 1, rlocn1_offset, mda_num, mda_offset, mda_size,
+ NULL, NULL, NULL))
+ bad++;
+
+ if (!meta_offset)
+ goto out;
+
+ if (!_dump_meta_text(dev, print_fields, print_metadata, tofile, mda_num, 0, mda_offset, mda_size, meta_offset, meta_size, meta_checksum))
+ bad++;
+
+ /* Should we also check text metadata if it exists in rlocn1? */
+ out:
+ if (bad)
+ return 0;
+ return 1;
+}
+
+static int _dump_headers(struct cmd_context *cmd,
+ int argc, char **argv)
+{
+ struct device *dev;
+ const char *pv_name;
+ uint64_t mda1_offset = 0, mda1_size = 0, mda2_offset = 0, mda2_size = 0;
+ uint32_t mda1_checksum, mda2_checksum;
+ int bad = 0;
pv_name = argv[0];
@@ -56,47 +775,121 @@ static int _dump_metadata(struct cmd_context *cmd, int argc, char **argv, int fu
return ECMD_FAILED;
}
- if (!(devl = zalloc(sizeof(*devl))))
- return ECMD_FAILED;
-
- devl->dev = dev;
- dm_list_add(&devs, &devl->list);
-
label_scan_setup_bcache();
- label_scan_devs(cmd, cmd->filter, &devs);
- if (!dev->pvid[0]) {
- log_error("No PV ID found for %s", dev_name(dev));
- return ECMD_FAILED;
+ if (!_dump_label_and_pv_header(cmd, 1, dev,
+ &mda1_offset, &mda1_size, &mda2_offset, &mda2_size))
+ bad++;
+
+ /* N.B. mda1_size and mda2_size may be different */
+
+ /*
+ * The first mda is always 4096 bytes from the start of the device.
+ *
+ * TODO: A second mda may not exist. If the pv_header says there
+ * is no second mda, we may still want to check for a second mda
+ * in case it's the pv_header that is wrong. Try looking for
+ * an mda_header at 1MB prior to the end of the device, if
+ * mda2_offset is 0 or if we don't find an mda_header at mda2_offset
+ * which may have been corrupted.
+ */
+
+ if (!_dump_mda_header(cmd, 1, 0, 0, NULL, dev, 4096, mda1_size, &mda1_checksum))
+ bad++;
+
+ /*
+ * mda2_offset may be incorrect. Probe for a valid mda_header at
+ * mda2_offset and at other possible/expected locations, e.g.
+ * 1MB before end of device. Call dump_mda_header with a different
+ * offset than mda2_offset if there's no valid header at mda2_offset
+ * but there is a valid header elsewhere.
+ */
+ if (mda2_offset) {
+ if (!_dump_mda_header(cmd, 1, 0, 0, NULL, dev, mda2_offset, mda2_size, &mda2_checksum))
+ bad++;
+
+ /* This probably indicates that one was committed and the other not. */
+ if (mda1_checksum && mda2_checksum && (mda1_checksum != mda2_checksum))
+ log_print("CHECK: mdas have different raw_locn[0].checksum values");
}
- if (!(info = lvmcache_info_from_pvid(dev->pvid, dev, 0))) {
- log_error("No VG info found for %s", dev_name(dev));
+ if (bad) {
+ log_error("Found bad header or metadata values.");
return ECMD_FAILED;
}
+ return ECMD_PROCESSED;
+}
- if (!(vgname = lvmcache_vgname_from_info(info))) {
- log_error("No VG name found for %s", dev_name(dev));
- return ECMD_FAILED;
+static int _dump_metadata(struct cmd_context *cmd,
+ int argc, char **argv, int full_area)
+{
+ struct device *dev;
+ const char *pv_name;
+ const char *tofile = NULL;
+ uint64_t mda1_offset = 0, mda1_size = 0, mda2_offset = 0, mda2_size = 0;
+ uint32_t mda1_checksum, mda2_checksum;
+ int print_metadata = !full_area;
+ int print_area = full_area;
+ int mda_num = 1;
+ int bad = 0;
+
+ if (arg_is_set(cmd, file_ARG)) {
+ if (!(tofile = arg_str_value(cmd, file_ARG, NULL)))
+ return ECMD_FAILED;
}
- if (!(vgid = lvmcache_vgid_from_vgname(cmd, vgname))) {
- log_error("No VG ID found for %s", dev_name(dev));
- return ECMD_FAILED;
- }
+ /* 1: dump metadata from first mda, 2: dump metadata from second mda */
+ if (arg_is_set(cmd, pvmetadatacopies_ARG))
+ mda_num = arg_int_value(cmd, pvmetadatacopies_ARG, 1);
+
+ pv_name = argv[0];
- if (!(mda = lvmcache_get_mda(cmd, vgname, dev, mda_num))) {
- log_error("No mda %d found for %s", mda_num, dev_name(dev));
+ if (!(dev = dev_cache_get(cmd, pv_name, cmd->filter))) {
+ log_error("No device found for %s %s.", pv_name, dev_cache_filtered_reason(pv_name));
return ECMD_FAILED;
}
- if (full_area)
- ret = dump_metadata_area(cmd, vgname, vgid, dev, mda, tofile);
- else
- ret = dump_metadata_text(cmd, vgname, vgid, dev, mda, tofile);
+ label_scan_setup_bcache();
+
+ if (!_dump_label_and_pv_header(cmd, 0, dev,
+ &mda1_offset, &mda1_size, &mda2_offset, &mda2_size))
+ bad++;
+
+
+ /*
+ * The first mda is always 4096 bytes from the start of the device.
+ *
+ * TODO: A second mda may not exist. If the pv_header says there
+ * is no second mda, we may still want to check for a second mda
+ * in case it's the pv_header that is wrong. Try looking for
+ * an mda_header at 1MB prior to the end of the device, if
+ * mda2_offset is 0 or if we don't find an mda_header at mda2_offset
+ * which may have been corrupted.
+ *
+ * mda2_offset may be incorrect. Probe for a valid mda_header at
+ * mda2_offset and at other possible/expected locations, e.g.
+ * 1MB before end of device. Call dump_mda_header with a different
+ * offset than mda2_offset if there's no valid header at mda2_offset
+ * but there is a valid header elsewhere.
+ */
+
+ if (mda_num == 1) {
+ if (!_dump_mda_header(cmd, 0, print_metadata, print_area, tofile, dev, 4096, mda1_size, &mda1_checksum))
+ bad++;
+ } else if (mda_num == 2) {
+ if (!mda2_offset) {
+ log_print("CHECK: second mda not found");
+ bad++;
+ } else {
+ if (!_dump_mda_header(cmd, 0, print_metadata, print_area, tofile, dev, mda2_offset, mda2_size, &mda2_checksum))
+ bad++;
+ }
+ }
- if (!ret)
+ if (bad) {
+ log_error("Found bad header or metadata values.");
return ECMD_FAILED;
+ }
return ECMD_PROCESSED;
}
@@ -120,6 +913,9 @@ int pvck(struct cmd_context *cmd, int argc, char **argv)
if (!strcmp(dump, "metadata_area"))
return _dump_metadata(cmd, argc, argv, 1);
+ if (!strcmp(dump, "headers"))
+ return _dump_headers(cmd, argc, argv);
+
log_error("Unknown dump value.");
return ECMD_FAILED;
}
3 years, 9 months