1
0
mirror of git://sourceware.org/git/lvm2.git synced 2025-10-05 07:33:15 +03:00

Compare commits

...

55 Commits

Author SHA1 Message Date
Marian Csontos
60bd9e8406 pre-release 2019-06-15 09:21:47 +02:00
Marian Csontos
b4ff865b44 build: make generate 2019-06-15 08:30:04 +02:00
David Teigland
9f5e46965b fix man page generation
The man page generation for pvchange/lvchange/vgchange was
incorrect (leaving out some option listings) as a result of
commit e225bf5 "fix command definition for pvchange -a"
2019-06-14 09:26:08 -05:00
David Teigland
a4dbbefaff WHATS_NEW for recent changes 2019-06-13 17:44:14 -05:00
David Teigland
208a09745d tests: aux have_writecache
function was never defined, causing writecache.sh to be skipped
2019-06-13 11:36:18 -05:00
David Teigland
7eaa3adedf vgchange: change debug message level
A debug message was mistakely left visible.
2019-06-11 16:14:07 -05:00
David Teigland
4bb7d3da0e lvmcache: remove wrapper around lvmcache_get_vgnameids
This was left over from when there was an lvmetad
version of the function.
2019-06-11 14:10:14 -05:00
David Teigland
0f350ba890 remove unused trustcache option 2019-06-11 11:42:49 -05:00
Zdenek Kabelac
c9203a6106 tests: correct checked target name
So when the target name happened to be a suffix of another one,
the grep was filtering incorrect line
(i.e. dm-cache && dm-writecache) - so do a line head matching.
2019-06-11 16:43:14 +02:00
David Teigland
e225bf59ff fix command definition for pvchange -a
The -a was being included in the set of "one or more"
options instead of an actual required option.  Even
though the cmd def was not implementing the restrictions
correctly, the command internally was.

Adjust the cmd def code which did not support a command
with some real required options and a set of "one or more"
options.
2019-06-10 13:43:20 -05:00
David Teigland
b7850faba7 locking: fix repeated convert to ex
Some uncommon commands like pvchange -a -u may
call convert to ex multiple times.
2019-06-10 13:37:03 -05:00
David Teigland
49b8846567 lvmcache: remove unused function
Drop lvmcache_fmt_from_vgname(), the way it was called made
it identical to the existing lvmcache_vginfo_from_vgname().
2019-06-10 10:38:32 -05:00
David Teigland
550536474f vgsplit: simplify vg creation
The way that this command now uses the global lock
followed by a label scan, it can simply check if the
new VG name exists, and if not lock it and create it.
2019-06-10 10:38:32 -05:00
David Teigland
5036244ce8 lvmcache: remove unused code 2019-06-10 10:38:32 -05:00
David Teigland
a07cc8dbef reset cmd wipe_outdated_pvs
at the start of a command, which is needed in case the cmd
struct is reused.
2019-06-10 10:34:58 -05:00
David Teigland
36cbc6db24 locking: reset global_ex flag at end of cmd
These two flags may be not reset at the end of
the command when the unlock is implicit, which
is a problem if the cmd struct is reused.
Clear the flags in the general fin_locking.
2019-06-10 10:34:58 -05:00
Marian Csontos
4c020b4d4a Merge remote-tracking branch 'origin/master'
* origin/master: (22 commits)
  tests: add metadata-bad-mdaheader.sh
  tests: add metadata-bad-text.sh
  tests: add outdated-pv.sh
  tests: add metadata-old.sh
  tests: add missing-pv missing-pv-unused
  metadata.c: removed unused code
  improve reading and repairing vg metadata
  add a warning message when updating old metadata
  vgcfgbackup add error messages
  vgck --updatemetadata is a new command
  move pv header repairs to vg_write
  process_each_pv handle outdated pvs
  move wipe_outdated_pvs to vg_write
  create separate lvmcache update functions for read and write
  fix vg_commit return value
  change args for text label read function
  add mda arg to add_mda
  keep track of which mdas have old metadata in lvmcache
  ability to keep track of outdated pvs in lvmcache
  ability to keep track of bad mdas in lvmcache
  ...
2019-06-10 17:05:04 +02:00
Marian Csontos
dbc5543cbb post-release 2019-06-10 17:04:30 +02:00
Marian Csontos
f1b4aeba66 pre-release 2019-06-10 16:59:49 +02:00
David Teigland
d7c1168c6a tests: add metadata-bad-mdaheader.sh
needs xxd command
2019-06-07 15:54:04 -05:00
David Teigland
878741502a tests: add metadata-bad-text.sh 2019-06-07 15:54:04 -05:00
David Teigland
4fa1638301 tests: add outdated-pv.sh 2019-06-07 15:54:04 -05:00
David Teigland
9156640b60 tests: add metadata-old.sh 2019-06-07 15:54:04 -05:00
David Teigland
d3636ff832 tests: add missing-pv missing-pv-unused 2019-06-07 15:54:04 -05:00
David Teigland
a3a676e0e7 metadata.c: removed unused code
if 0 was placed around old vg_read code by
the previous commit.
2019-06-07 15:54:04 -05:00
David Teigland
ba7ff96faf improve reading and repairing vg metadata
The fact that vg repair is implemented as a part of vg read
has led to a messy and complicated implementation of vg_read,
and limited and uncontrolled repair capability.  This splits
read and repair apart.

Summary
-------

- take all kinds of various repairs out of vg_read
- vg_read no longer writes anything
- vg_read now simply reads and returns vg metadata
- vg_read ignores bad or old copies of metadata
- vg_read proceeds with a single good copy of metadata
- improve error checks and handling when reading
- keep track of bad (corrupt) copies of metadata in lvmcache
- keep track of old (seqno) copies of metadata in lvmcache
- keep track of outdated PVs in lvmcache
- vg_write will do basic repairs
- new command vgck --updatemetdata will do all repairs

Details
-------

- In scan, do not delete dev from lvmcache if reading/processing fails;
  the dev is still present, and removing it makes it look like the dev
  is not there.  Records are now kept about the problems with each PV
  so they be fixed/repaired in the appropriate places.

- In scan, record a bad mda on failure, and delete the mda from
  mda in use list so it will not be used by vg_read or vg_write,
  only by repair.

- In scan, succeed if any good mda on a device is found, instead of
  failing if any is bad.  The bad/old copies of metadata should not
  interfere with normal usage while good copies can be used.

- In scan, add a record of old mdas in lvmcache for later, do not repair
  them while reading, and do not let them prevent us from finding and
  using a good copy of metadata from elsewhere.  One result is that
  "inconsistent metadata" is no longer a read error, but instead a
  record in lvmcache that can be addressed separate from the read.

- Treat a dev with no good mdas like a dev with no mdas, which is an
  existing case we already handle.

- Don't use a fake vg "handle" for returning an error from vg_read,
  or the vg_read_error function for getting that error number;
  just return null if the vg cannot be read or used, and an error_flags
  arg with flags set for the specific kind of error (which can be used
  later for determining the kind of repair.)

- Saving an original copy of the vg metadata, for purposes of reverting
  a write, is now done explicitly in vg_read instead of being hidden in
  the vg_make_handle function.

- When a vg is not accessible due to "access restrictions" but is
  otherwise fine, return the vg through the new error_vg arg so that
  process_each_pv can skip the PVs in the VG while processing.
  (This is a temporary accomodation for the way process_each_pv
  tracks which devs have been looked at, and can be dropped later
  when process_each_pv implementation dev tracking is changed.)

- vg_read does not try to fix or recover a vg, but now just reads the
  metadata, checks access restrictions and returns it.
  (Checking access restrictions might be better done outside of vg_read,
   but this is a later improvement.)

- _vg_read now simply makes one attempt to read metadata from
  each mda, and uses the most recent copy to return to the caller
  in the form of a 'vg' struct.
  (bad mdas were excluded during the scan and are not retried)
  (old mdas were not excluded during scan and are retried here)

- vg_read uses _vg_read to get the latest copy of metadata from mdas,
  and then makes various checks against it to produce warnings,
  and to check if VG access is allowed (access restrictions include:
  writable, foreign, shared, clustered, missing pvs).

- Things that were previously silently/automatically written by vg_read
  that are now done by vg_write, based on the records made in lvmcache
  during the scan and read:
  . clearing the missing flag
  . updating old copies of metadata
  . clearing outdated pvs
  . updating pv header flags

- Bad/corrupt metadata are now repaired; they were not before.

Test changes
------------

- A read command no longer writes the VG to repair it, so add a write
  command to do a repair.
  (inconsistent-metadata, unlost-pv)

- When a missing PV is removed from a VG, and then the device is
  enabled again, vgck --updatemetadata is needed to clear the
  outdated PV before it can be used again, where it wasn't before.
  (lvconvert-repair-policy, lvconvert-repair-raid, lvconvert-repair,
   mirror-vgreduce-removemissing, pv-ext-flags, unlost-pv)

Reading bad/old metadata
------------------------

- "bad metadata": the mda_header or metadata text has invalid fields
  or can't be parsed by lvm.  This is a form of corruption that would
  not be caused by known failure scenarios.  A checksum error is
  typically included among the errors reported.

- "old metadata": a valid copy of the metadata that has a smaller seqno
  than other copies of the metadata.  This can happen if the device
  failed, or io failed, or lvm failed while commiting new metadata
  to all the metadata areas.  Old metadata on a PV that has been
  removed from the VG is the "outdated" case below.

When a VG has some PVs with bad/old metadata, lvm can simply ignore
the bad/old copies, and use a good copy.  This is why there are
multiple copies of the metadata -- so it's available even when some
of the copies cannot be used.  The bad/old copies do not have to be
repaired before the VG can be used (the repair can happen later.)

A PV with no good copies of the metadata simply falls back to being
treated like a PV with no mdas; a common and harmless configuration.

When bad/old metadata exists, lvm warns the user about it, and
suggests repairing it using a new metadata repair command.
Bad metadata in particular is something that users will want to
investigate and repair themselves, since it should not happen and
may indicate some other problem that needs to be fixed.

PVs with bad/old metadata are not the same as missing devices.
Missing devices will block various kinds of VG modification or
activation, but bad/old metadata will not.

Previously, lvm would attempt to repair bad/old metadata whenever
it was read.  This was unnecessary since lvm does not require every
copy of the metadata to be used.  It would also hide potential
problems that should be investigated by the user.  It was also
dangerous in cases where the VG was on shared storage.  The user
is now allowed to investigate potential problems and decide how
and when to repair them.

Repairing bad/old metadata
--------------------------

When label scan sees bad metadata in an mda, that mda is removed
from the lvmcache info->mdas list.  This means that vg_read will
skip it, and not attempt to read/process it again.  If it was
the only in-use mda on a PV, that PV is treated like a PV with
no mdas.  It also means that vg_write will also skip the bad mda,
and not attempt to write new metadata to it.  The only way to
repair bad metadata is with the metadata repair command.

When label scan sees old metadata in an mda, that mda is kept
in the lvmcache info->mdas list.  This means that vg_read will
read/process it again, and likely see the same mismatch with
the other copies of the metadata.  Like the label_scan, the
vg_read will simply ignore the old copy of the metadata and
use the latest copy.  If the command is modifying the vg
(e.g. lvcreate), then vg_write, which writes new metadata to
every mda on info->mdas, will write the new metadata to the
mda that had the old version.  If successful, this will resolve
the old metadata problem (without needing to run a metadata
repair command.)

Outdated PVs
------------

An outdated PV is a PV that has an old copy of VG metadata
that shows it is a member of the VG, but the latest copy of
the VG metadata does not include this PV.  This happens if
the PV is disconnected, vgreduce --removemissing is run to
remove the PV from the VG, then the PV is reconnected.
In this case, the outdated PV needs have its outdated metadata
removed and the PV used flag needs to be cleared.  This repair
will be done by the subsequent repair command.  It is also done
if vgremove is run on the VG.

MISSING PVs
-----------

When a device is missing, most commands will refuse to modify
the VG.  This is the simple case.  More complicated is when
a command is allowed to modify the VG while it is missing a
device.

When a VG is written while a device is missing for one of it's PVs,
the VG metadata is written to disk with the MISSING flag on the PV
with the missing device.  When the VG is next used, it is treated
as if the PV with the MISSING flag still has a missing device, even
if that device has reappeared.

If all LVs that were using a PV with the MISSING flag are removed
or repaired so that the MISSING PV is no longer used, then the
next time the VG metadata is written, the MISSING flag will be
dropped.

Alternative methods of clearing the MISSING flag are:

vgreduce --removemissing will remove PVs with missing devices,
or PVs with the MISSING flag where the device has reappeared.

vgextend --restoremissing will clear the MISSING flag on PVs
where the device has reappeared, allowing the VG to be used
normally.  This must be done with caution since the reappeared
device may have old data that is inconsistent with data on other PVs.

Bad mda repair
--------------

The new command:
vgck --updatemetadata VG

first uses vg_write to repair old metadata, and other basic
issues mentioned above (old metadata, outdated PVs, pv_header
flags, MISSING_PV flags).  It will also go further and repair
bad metadata:

. text metadata that has a bad checksum
. text metadata that is not parsable
. corrupt mda_header checksum and version fields

(To keep a clean diff, #if 0 is added around functions that
are replaced by new code.  These commented functions are
removed by the following commit.)
2019-06-07 15:54:04 -05:00
David Teigland
015b906069 add a warning message when updating old metadata
in an mda that had previously not been updated
2019-06-07 15:54:04 -05:00
David Teigland
5dd32680b0 vgcfgbackup add error messages 2019-06-07 15:54:04 -05:00
David Teigland
47effdc025 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.
2019-06-07 15:54:04 -05:00
David Teigland
de3d3b11f4 move pv header repairs to vg_write
Correct PV header in-use or version fields
from vg_write instead of vg_read.
2019-06-07 15:54:04 -05:00
David Teigland
89914a541f process_each_pv handle outdated pvs
process_each_pv should account for outdated pvs
in the list of all devices it is processing.
2019-06-07 15:54:04 -05:00
David Teigland
ab61a6d85d move wipe_outdated_pvs to vg_write
and implement it based on a device, not based
on a pv struct (which is not available when the
device is not a part of the vg.)

currently only the vgremove command wipes outdated
pvs until more advanced recovery is added in a
subsequent commit
2019-06-07 15:54:04 -05:00
David Teigland
45b164f62c create separate lvmcache update functions for read and write
The vg read and vg write cases need to update lvmcache
differently, so create separate functions for them.

The read case now handles checking for outdated mdas
and moves them aside into a new list to be repaired in
a subsequent commit.
2019-06-07 15:54:04 -05:00
David Teigland
027e0e92e6 fix vg_commit return value
The existing comment was desribing the correct behavior,
but the code didn't match.  The commit is successful if
one mda was committed.  Making it depend on the result of
the internal lvmcache update was wrong.
2019-06-07 15:54:04 -05:00
David Teigland
86d831b916 change args for text label read function
Have the caller pass the label_sector to the read
function so the read function can set the sector
field in the label struct, instead of having the
read function return a pointer to the label for
the caller to set the sector field.

Also have the read function return a flag indicating
to the caller that the scanned device was identified
as a duplicate pv.
2019-06-07 15:54:04 -05:00
David Teigland
889b5d3183 add mda arg to add_mda
Allow the caller of lvmcache_add_mda() to have the
new mda returned.
2019-06-07 15:54:04 -05:00
David Teigland
b2447e3538 keep track of which mdas have old metadata in lvmcache
This will be used for more advanced repair in a
subsequent commit.
2019-06-07 15:54:04 -05:00
David Teigland
0b18c25d93 ability to keep track of outdated pvs in lvmcache
Outdated PVs hold metadata for VG from which they
have been removed.  Add the ability to keep track
of these in lvmcache.
This will be used for more advanced repair in a
subsequent commit.
2019-06-07 15:54:04 -05:00
David Teigland
650524b955 ability to keep track of bad mdas in lvmcache
mda's that cannot be processed by lvm because of
some corruption can be kept on a separate list.
These will be used for more advanced repair in a
subsequent commit.
2019-06-07 15:54:04 -05:00
David Teigland
aeafdc1f45 add flags to keep track of bad metadata
When reading metadata headers and text, use a new set
of flags to identify specific errors that are seen.
These will be used for more advanced repair in a
subsequent commit.
2019-06-07 15:54:04 -05:00
David Teigland
db98a6e362 Additional MD component checking
If udev info is missing for a device, (which would indicate
if it's an MD component), then do an end-of-device read to
check if a PV is an MD component.  (This is skipped when
using hints since we already know devs in hints are good.)

A new config setting md_component_checks can be used to
disable the additional end-of-device MD checks, or to
always enable end-of-device MD checks.

When both hints and udev info are disabled/unavailable,
the end of PVs will now be scanned by default.  If md
devices with end-of-device superblocks are not being
used, the extra I/O overhead can be avoided by setting
md_component_checks="start".
2019-06-07 13:27:16 -05:00
Marian Csontos
a2c309a5c5 build: make generate 2019-06-07 17:59:43 +02:00
Marian Csontos
07d41de74c build: autoreconf 2019-06-07 17:56:56 +02:00
Marian Csontos
24bd35b4ce Merge remote-tracking branch 'origin/master'
* origin/master:
  lvmcache: remove unused_duplicate_devs list from cmd
2019-06-07 17:29:45 +02:00
Marian Csontos
4d11bf8d50 post-release 2019-06-07 17:24:51 +02:00
Marian Csontos
cb6277aa8a pre-release 2019-06-07 17:24:51 +02:00
David Teigland
2bcd43c683 lvmcache: remove unused_duplicate_devs list from cmd
Save the previous duplicate PVs in a global list instead
of a list on the cmd struct.  dmeventd reuses the cmd struct
for multiple commands, and the list entries between commands
were being freed (apparently), causing a segfault in dmeventd
when it tried to use items in cmd->unused_duplicate_devs
that had been saved there by the previous command.
2019-06-07 10:14:33 -05:00
David Teigland
c315112a3b tests: pvscan-autoactivate check for machine-id 2019-06-06 15:32:42 -05:00
David Teigland
2b241eb1f6 pvck: use new dump routines for old output
Use the recently added dump routines to produce the
old/traditional pvck output, and remove the code that
had been used for that.

The validation/checking done by the new routines means
that new lines prefixed with CHECK are printed for
incorrect values.
2019-06-05 16:28:52 -05:00
David Teigland
356ea897cc tests: pvck-dump 2019-06-05 13:58:26 -05:00
David Teigland
bada89a224 pvck: dump metadata_all
This searches the entire metadata area for any
copy of the metadata and dumps it to file.
2019-06-05 12:25:34 -05:00
Zdenek Kabelac
4d9f41b119 tests: check no_discard_passdown
Check reporting works
2019-06-05 15:48:44 +02:00
Zdenek Kabelac
ddd68fbead tests: automatically set scan_lvs when using extend_filter
When using 'aux extend_filter' we always want to use LV as PV.
2019-06-05 15:48:44 +02:00
Zdenek Kabelac
e3c4ab0cc7 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).
2019-06-05 15:48:41 +02:00
David Teigland
d18e491f68 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.
2019-06-03 15:13:32 -05:00
86 changed files with 4841 additions and 2863 deletions

View File

@@ -1 +1 @@
2.03.02(2)-git (2018-10-31)
2.03.05(2) (2019-06-15)

View File

@@ -1 +1 @@
1.02.155-git (2018-10-31)
1.02.163 (2019-06-15)

View File

@@ -1,5 +1,20 @@
Version 2.03.02 -
===================================
Version 2.03.05 - 15th June 2019
================================
Fix command definition for pvchange -a.
Add vgck --updatemetadata command that will repair metadata problems.
Improve VG reading to work if one good copy of metadata is found.
Report/display/scan commands that read VGs will no longer write/repair.
Move metadata repairs from VG reading to VG writing.
Add config setting md_component_checks to control MD component checks.
Add end of device MD component checks when dev has no udev info.
Version 2.03.04 - 10th June 2019
================================
Remove unused_duplicate_devs from cmd causing segfault in dmeventd.
Version 2.03.03 - 07th June 2019
================================
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.
@@ -34,10 +49,14 @@ Version 2.03.02 -
Fix missing unlock on lvm2 dmeventd plugin error path initialization.
Improve Makefile dependency tracking.
Move VDO support towards V2 target (6.2) support.
Version 2.03.02 - 18th December 2018
====================================
Fix missing proper initialization of pv_list struct when adding pv.
Fix (de)activation of RaidLVs with visible SubLVs
Prohibit mirrored 'mirror' log via lvcreate and lvconvert
Fix (de)activation of RaidLVs with visible SubLVs.
Prohibit mirrored 'mirror' log via lvcreate and lvconvert.
Use sync io if async io_setup fails, or use_aio=0 is set in config.
Fix more issues reported by coverity scan.
Version 2.03.01 - 31st October 2018
===================================

View File

@@ -1,10 +1,21 @@
Version 1.02.155 -
====================================
Version 1.02.163 - 15th June 2019
=================================
Version 1.02.161 - 10th June 2019
=================================
Version 1.02.159 - 07th June 2019
=================================
Parsing of cache status understand no_discard_passdown.
Ensure migration_threshold for cache is at least 8 chunks.
Version 1.02.155 - 18th December 2018
=====================================
Include correct internal header inside libdm list.c.
Enhance ioctl flattening and add parameters only when needed.
Add DM_DEVICE_ARM_POLL for API completness matching kernel.
Do not add parameters for RESUME with DM_DEVICE_CREATE dm task.
Fix dmstats report printing no output.
Version 1.02.153 - 31st October 2018
====================================

View File

@@ -88,6 +88,22 @@ devices {
#
external_device_info_source = "none"
# Configuration option devices/hints.
# Use a local file to remember which devices have PVs on them.
# Some commands will use this as an optimization to reduce device
# scanning, and will only scan the listed PVs. Removing the hint file
# will cause lvm to generate a new one. Disable hints if PVs will
# be copied onto devices using non-lvm commands, like dd.
#
# Accepted values:
# all
# Use all hints.
# none
# Use no hints.
#
# This configuration option has an automatic default value.
# hints = "all"
# Configuration option devices/preferred_names.
# Select which path name to display for a block device.
# If multiple path names exist for a block device, and LVM needs to
@@ -167,17 +183,52 @@ devices {
sysfs_scan = 1
# Configuration option devices/scan_lvs.
# Scan LVM LVs for layered PVs.
scan_lvs = 1
# Scan LVM LVs for layered PVs, allowing LVs to be used as PVs.
# When 1, LVM will detect PVs layered on LVs, and caution must be
# taken to avoid a host accessing a layered VG that may not belong
# to it, e.g. from a guest image. This generally requires excluding
# the LVs with device filters. Also, when this setting is enabled,
# every LVM command will scan every active LV on the system (unless
# filtered), which can cause performance problems on systems with
# many active LVs. When this setting is 0, LVM will not detect or
# use PVs that exist on LVs, and will not allow a PV to be created on
# an LV. The LVs are ignored using a built in device filter that
# identifies and excludes LVs.
scan_lvs = 0
# Configuration option devices/multipath_component_detection.
# Ignore devices that are components of DM multipath devices.
multipath_component_detection = 1
# Configuration option devices/md_component_detection.
# Ignore devices that are components of software RAID (md) devices.
# Enable detection and exclusion of MD component devices.
# An MD component device is a block device that MD uses as part
# of a software RAID virtual device. When an LVM PV is created
# on an MD device, LVM must only use the top level MD device as
# the PV, and should ignore the underlying component devices.
# In cases where the MD superblock is located at the end of the
# component devices, it is more difficult for LVM to consistently
# identify an MD component, see the md_component_checks setting.
md_component_detection = 1
# Configuration option devices/md_component_checks.
# The checks LVM should use to detect MD component devices.
# MD component devices are block devices used by MD software RAID.
#
# Accepted values:
# auto
# LVM will skip scanning the end of devices when it has other
# indications that the device is not an MD component.
# start
# LVM will only scan the start of devices for MD superblocks.
# This does not incur extra I/O by LVM.
# full
# LVM will scan the start and end of devices for MD superblocks.
# This requires an extra read at the end of devices.
#
# This configuration option has an automatic default value.
# md_component_checks = "auto"
# Configuration option devices/fw_raid_component_detection.
# Ignore devices that are components of firmware RAID devices.
# LVM must use an external_device_info_source other than none for this
@@ -720,7 +771,8 @@ log {
# Configuration option log/indent.
# Indent messages according to their severity.
indent = 1
# This configuration option has an automatic default value.
# indent = 0
# Configuration option log/command_names.
# Display the command name on each line of output.
@@ -746,6 +798,20 @@ log {
# available: memory, devices, io, activation, allocation,
# metadata, cache, locking, lvmpolld. Use "all" to see everything.
debug_classes = [ "memory", "devices", "io", "activation", "allocation", "metadata", "cache", "locking", "lvmpolld", "dbus" ]
# Configuration option log/debug_file_fields.
# The fields included in debug output written to log file.
# Use "all" to include everything (the default).
# This configuration option is advanced.
# This configuration option has an automatic default value.
# debug_file_fields = [ "time", "command", "fileline", "message" ]
# Configuration option log/debug_output_fields.
# The fields included in debug output written to stderr.
# Use "all" to include everything (the default).
# This configuration option is advanced.
# This configuration option has an automatic default value.
# debug_output_fields = [ "time", "command", "fileline", "message" ]
}
# Configuration section backup.
@@ -1174,6 +1240,16 @@ global {
# When enabled, an LVM command that changes PVs, changes VG metadata,
# or changes the activation state of an LV will send a notification.
notify_dbus = 1
# Configuration option global/io_memory_size.
# The amount of memory in KiB that LVM allocates to perform disk io.
# LVM performance may benefit from more io memory when there are many
# disks or VG metadata is large. Increasing this size may be necessary
# when a single copy of VG metadata is larger than the current setting.
# This value should usually not be decreased from the default; setting
# it too low can result in lvm failing to read VGs.
# This configuration option has an automatic default value.
# io_memory_size = 8192
}
# Configuration section activation.

105
configure vendored
View File

@@ -742,6 +742,7 @@ CLDNOWHOLEARCHIVE
CLDFLAGS
CACHE
BUILD_DMFILEMAPD
BUILD_LOCKDDLM_CONTROL
BUILD_LOCKDDLM
BUILD_LOCKDSANLOCK
BUILD_LVMLOCKD
@@ -771,6 +772,8 @@ BLKID_LIBS
BLKID_CFLAGS
NOTIFY_DBUS_LIBS
NOTIFY_DBUS_CFLAGS
LOCKD_DLM_CONTROL_LIBS
LOCKD_DLM_CONTROL_CFLAGS
LOCKD_DLM_LIBS
LOCKD_DLM_CFLAGS
LOCKD_SANLOCK_LIBS
@@ -932,6 +935,7 @@ enable_devmapper
enable_lvmpolld
enable_lvmlockd_sanlock
enable_lvmlockd_dlm
enable_lvmlockd_dlmcontrol
enable_use_lvmlockd
with_lvmlockd_pidfile
enable_use_lvmpolld
@@ -1000,6 +1004,8 @@ LOCKD_SANLOCK_CFLAGS
LOCKD_SANLOCK_LIBS
LOCKD_DLM_CFLAGS
LOCKD_DLM_LIBS
LOCKD_DLM_CONTROL_CFLAGS
LOCKD_DLM_CONTROL_LIBS
NOTIFY_DBUS_CFLAGS
NOTIFY_DBUS_LIBS
BLKID_CFLAGS
@@ -1643,6 +1649,8 @@ Optional Features:
--enable-lvmlockd-sanlock
enable the LVM lock daemon using sanlock
--enable-lvmlockd-dlm enable the LVM lock daemon using dlm
--enable-lvmlockd-dlmcontrol
enable lvmlockd remote refresh using libdlmcontrol
--disable-use-lvmlockd disable usage of LVM lock daemon
--disable-use-lvmpolld disable usage of LVM Poll Daemon
--enable-dmfilemapd enable the dmstats filemap daemon
@@ -1788,6 +1796,10 @@ Some influential environment variables:
C compiler flags for LOCKD_DLM, overriding pkg-config
LOCKD_DLM_LIBS
linker flags for LOCKD_DLM, overriding pkg-config
LOCKD_DLM_CONTROL_CFLAGS
C compiler flags for LOCKD_DLM_CONTROL, overriding pkg-config
LOCKD_DLM_CONTROL_LIBS
linker flags for LOCKD_DLM_CONTROL, overriding pkg-config
NOTIFY_DBUS_CFLAGS
C compiler flags for NOTIFY_DBUS, overriding pkg-config
NOTIFY_DBUS_LIBS
@@ -3077,6 +3089,7 @@ case "$host_os" in
BUILD_LVMPOLLD=no
LOCKDSANLOCK=no
LOCKDDLM=no
LOCKDDLM_CONTROL=no
ODIRECT=yes
DM_IOCTLS=yes
SELINUX=yes
@@ -10958,6 +10971,97 @@ $as_echo "#define LOCKDDLM_SUPPORT 1" >>confdefs.h
BUILD_LVMLOCKD=yes
fi
################################################################################
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build lvmlockddlmcontrol" >&5
$as_echo_n "checking whether to build lvmlockddlmcontrol... " >&6; }
# Check whether --enable-lvmlockd-dlmcontrol was given.
if test "${enable_lvmlockd_dlmcontrol+set}" = set; then :
enableval=$enable_lvmlockd_dlmcontrol; LOCKDDLM_CONTROL=$enableval
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $LOCKDDLM_CONTROL" >&5
$as_echo "$LOCKDDLM_CONTROL" >&6; }
BUILD_LOCKDDLM_CONTROL=$LOCKDDLM_CONTROL
if test "$BUILD_LOCKDDLM_CONTROL" = yes; then
pkg_failed=no
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for LOCKD_DLM_CONTROL" >&5
$as_echo_n "checking for LOCKD_DLM_CONTROL... " >&6; }
if test -n "$LOCKD_DLM_CONTROL_CFLAGS"; then
pkg_cv_LOCKD_DLM_CONTROL_CFLAGS="$LOCKD_DLM_CONTROL_CFLAGS"
elif test -n "$PKG_CONFIG"; then
if test -n "$PKG_CONFIG" && \
{ { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libdlmcontrol >= 3.2\""; } >&5
($PKG_CONFIG --exists --print-errors "libdlmcontrol >= 3.2") 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
pkg_cv_LOCKD_DLM_CONTROL_CFLAGS=`$PKG_CONFIG --cflags "libdlmcontrol >= 3.2" 2>/dev/null`
test "x$?" != "x0" && pkg_failed=yes
else
pkg_failed=yes
fi
else
pkg_failed=untried
fi
if test -n "$LOCKD_DLM_CONTROL_LIBS"; then
pkg_cv_LOCKD_DLM_CONTROL_LIBS="$LOCKD_DLM_CONTROL_LIBS"
elif test -n "$PKG_CONFIG"; then
if test -n "$PKG_CONFIG" && \
{ { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libdlmcontrol >= 3.2\""; } >&5
($PKG_CONFIG --exists --print-errors "libdlmcontrol >= 3.2") 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
pkg_cv_LOCKD_DLM_CONTROL_LIBS=`$PKG_CONFIG --libs "libdlmcontrol >= 3.2" 2>/dev/null`
test "x$?" != "x0" && pkg_failed=yes
else
pkg_failed=yes
fi
else
pkg_failed=untried
fi
if test $pkg_failed = yes; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
_pkg_short_errors_supported=yes
else
_pkg_short_errors_supported=no
fi
if test $_pkg_short_errors_supported = yes; then
LOCKD_DLM_CONTROL_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libdlmcontrol >= 3.2" 2>&1`
else
LOCKD_DLM_CONTROL_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libdlmcontrol >= 3.2" 2>&1`
fi
# Put the nasty error message in config.log where it belongs
echo "$LOCKD_DLM_CONTROL_PKG_ERRORS" >&5
$bailout
elif test $pkg_failed = untried; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
$bailout
else
LOCKD_DLM_CONTROL_CFLAGS=$pkg_cv_LOCKD_DLM_CONTROL_CFLAGS
LOCKD_DLM_CONTROL_LIBS=$pkg_cv_LOCKD_DLM_CONTROL_LIBS
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
HAVE_LOCKD_DLM_CONTROL=yes
fi
$as_echo "#define LOCKDDLM_CONTROL_SUPPORT 1" >>confdefs.h
BUILD_LVMLOCKD=yes
fi
################################################################################
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build lvmlockd" >&5
$as_echo_n "checking whether to build lvmlockd... " >&6; }
@@ -13784,6 +13888,7 @@ _ACEOF
################################################################################

View File

@@ -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;
/*

View File

@@ -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);

View File

@@ -295,9 +295,6 @@
/* Define to 1 if you have the `pselect' function. */
#undef HAVE_PSELECT
/* Define to 1 if you have the <pthread.h> header file. */
#undef HAVE_PTHREAD_H
/* Define to 1 if the system has the type `ptrdiff_t'. */
#undef HAVE_PTRDIFF_T
@@ -537,6 +534,9 @@
/* Locale-dependent data */
#undef LOCALEDIR
/* Define to 1 to include code that uses lvmlockd dlm control option. */
#undef LOCKDDLM_CONTROL_SUPPORT
/* Define to 1 to include code that uses lvmlockd dlm option. */
#undef LOCKDDLM_SUPPORT

667
lib/cache/lvmcache.c vendored
View File

@@ -31,6 +31,7 @@ struct lvmcache_info {
struct dm_list mdas; /* list head for metadata areas */
struct dm_list das; /* list head for data areas */
struct dm_list bas; /* list head for bootloader areas */
struct dm_list bad_mdas;/* list head for bad metadata areas */
struct lvmcache_vginfo *vginfo; /* NULL == unknown */
struct label *label;
const struct format_type *fmt;
@@ -39,12 +40,19 @@ struct lvmcache_info {
uint32_t ext_version; /* Extension version */
uint32_t ext_flags; /* Extension flags */
uint32_t status;
bool mda1_bad; /* label scan found bad metadata in mda1 */
bool mda2_bad; /* label scan found bad metadata in mda2 */
bool summary_seqno_mismatch; /* two mdas on this dev has mismatching metadata */
int summary_seqno; /* vg seqno found on this dev during scan */
int mda1_seqno;
int mda2_seqno;
};
/* One per VG */
struct lvmcache_vginfo {
struct dm_list list; /* Join these vginfos together */
struct dm_list infos; /* List head for lvmcache_infos */
struct dm_list outdated_infos; /* vg_read moves info from infos to outdated_infos */
const struct format_type *fmt;
char *vgname; /* "" == orphan */
uint32_t status;
@@ -57,7 +65,7 @@ struct lvmcache_vginfo {
uint32_t mda_checksum;
size_t mda_size;
int seqno;
int scan_summary_mismatch; /* vgsummary from devs had mismatching seqno or checksum */
bool scan_summary_mismatch; /* vgsummary from devs had mismatching seqno or checksum */
};
static struct dm_hash_table *_pvid_hash = NULL;
@@ -66,6 +74,7 @@ static struct dm_hash_table *_vgname_hash = NULL;
static DM_LIST_INIT(_vginfos);
static DM_LIST_INIT(_found_duplicate_devs);
static DM_LIST_INIT(_unused_duplicate_devs);
static DM_LIST_INIT(_prev_unused_duplicate_devs);
static int _vgs_locked = 0;
static int _found_duplicate_pvs = 0; /* If we never see a duplicate PV we can skip checking for them later. */
static int _found_duplicate_vgnames = 0;
@@ -81,6 +90,7 @@ int lvmcache_init(struct cmd_context *cmd)
dm_list_init(&_vginfos);
dm_list_init(&_found_duplicate_devs);
dm_list_init(&_unused_duplicate_devs);
dm_list_init(&_prev_unused_duplicate_devs);
if (!(_vgname_hash = dm_hash_create(128)))
return 0;
@@ -107,11 +117,6 @@ void lvmcache_unlock_vgname(const char *vgname)
}
}
int lvmcache_vgs_locked(void)
{
return _vgs_locked;
}
/*
* When lvmcache sees a duplicate PV, this is set.
* process_each_pv() can avoid searching for duplicates
@@ -173,6 +178,54 @@ static void _destroy_duplicate_device_list(struct dm_list *head)
dm_list_init(head);
}
bool lvmcache_has_bad_metadata(struct device *dev)
{
struct lvmcache_info *info;
if (!(info = lvmcache_info_from_pvid(dev->pvid, dev, 0))) {
/* shouldn't happen */
log_error("No lvmcache info for checking bad metadata on %s", dev_name(dev));
return false;
}
if (info->mda1_bad || info->mda2_bad)
return true;
return false;
}
void lvmcache_save_bad_mda(struct lvmcache_info *info, struct metadata_area *mda)
{
if (mda->mda_num == 1)
info->mda1_bad = true;
else if (mda->mda_num == 2)
info->mda2_bad = true;
dm_list_add(&info->bad_mdas, &mda->list);
}
void lvmcache_get_bad_mdas(struct cmd_context *cmd,
const char *vgname, const char *vgid,
struct dm_list *bad_mda_list)
{
struct lvmcache_vginfo *vginfo;
struct lvmcache_info *info;
struct mda_list *mdal;
struct metadata_area *mda, *mda2;
if (!(vginfo = lvmcache_vginfo_from_vgname(vgname, vgid))) {
log_error(INTERNAL_ERROR "lvmcache_get_bad_mdas no vginfo %s", vgname);
return;
}
dm_list_iterate_items(info, &vginfo->infos) {
dm_list_iterate_items_safe(mda, mda2, &info->bad_mdas) {
if (!(mdal = zalloc(sizeof(*mdal))))
continue;
mdal->mda = mda;
dm_list_add(bad_mda_list, &mdal->list);
}
}
}
static void _vginfo_attach_info(struct lvmcache_vginfo *vginfo,
struct lvmcache_info *info)
{
@@ -225,60 +278,6 @@ struct lvmcache_vginfo *lvmcache_vginfo_from_vgname(const char *vgname, const ch
return vginfo;
}
const struct format_type *lvmcache_fmt_from_vgname(struct cmd_context *cmd,
const char *vgname, const char *vgid,
unsigned revalidate_labels)
{
struct lvmcache_vginfo *vginfo;
struct lvmcache_info *info;
struct dm_list *devh, *tmp;
struct dm_list devs;
struct device_list *devl;
char vgid_found[ID_LEN + 1] __attribute__((aligned(8)));
if (!(vginfo = lvmcache_vginfo_from_vgname(vgname, vgid))) {
stack;
return NULL;
}
/*
* If this function is called repeatedly, only the first one needs to revalidate.
*/
if (!revalidate_labels)
goto out;
/*
* This function is normally called before reading metadata so
* we check cached labels here. Unfortunately vginfo is volatile.
*/
dm_list_init(&devs);
dm_list_iterate_items(info, &vginfo->infos) {
if (!(devl = malloc(sizeof(*devl)))) {
log_error("device_list element allocation failed");
return NULL;
}
devl->dev = info->dev;
dm_list_add(&devs, &devl->list);
}
memcpy(vgid_found, vginfo->vgid, sizeof(vgid_found));
dm_list_iterate_safe(devh, tmp, &devs) {
devl = dm_list_item(devh, struct device_list);
label_read(devl->dev);
dm_list_del(&devl->list);
free(devl);
}
/* If vginfo changed, caller needs to rescan */
if (!(vginfo = lvmcache_vginfo_from_vgname(vgname, vgid_found)) ||
strncmp(vginfo->vgid, vgid_found, ID_LEN))
return NULL;
out:
return vginfo->fmt;
}
struct lvmcache_vginfo *lvmcache_vginfo_from_vgid(const char *vgid)
{
struct lvmcache_vginfo *vginfo;
@@ -553,8 +552,11 @@ next:
*/
if (!(info = lvmcache_info_from_pvid(alt->dev->pvid, NULL, 0))) {
/* This shouldn't happen */
log_warn("WARNING: PV %s on duplicate device %s not found in cache.",
/*
* This will happen if a duplicate dev has been dropped already,
* e.g. it was found to be an md component.
*/
log_debug("PVID %s on duplicate device %s not found in cache.",
alt->dev->pvid, dev_name(alt->dev));
goto next;
}
@@ -581,14 +583,14 @@ next:
if (!prev_unchosen1 && !prev_unchosen2) {
/*
* The cmd list saves the unchosen preference across
* The prev list saves the unchosen preference across
* lvmcache_destroy. Sometimes a single command will
* fill lvmcache, destroy it, and refill it, and we
* want the same duplicate preference to be preserved
* in each instance of lvmcache for a single command.
*/
prev_unchosen1 = dev_in_device_list(dev1, &cmd->unused_duplicate_devs);
prev_unchosen2 = dev_in_device_list(dev2, &cmd->unused_duplicate_devs);
prev_unchosen1 = dev_in_device_list(dev1, &_prev_unused_duplicate_devs);
prev_unchosen2 = dev_in_device_list(dev2, &_prev_unused_duplicate_devs);
}
dev1_major = MAJOR(dev1->dev);
@@ -945,49 +947,26 @@ int lvmcache_label_scan(struct cmd_context *cmd)
return r;
}
/*
* lvmcache_label_scan() detects duplicates in the basic label_scan(), then
* filters out some dups, and chooses preferred duplicates to use.
*/
void lvmcache_pvscan_duplicate_check(struct cmd_context *cmd)
{
struct device_list *devl;
/* Check if label_scan() detected any dups. */
if (!_found_duplicate_pvs)
return;
/*
* Once all the dups are identified, they are moved from the
* "found" list to the "unused" list to sort out.
*/
dm_list_splice(&_unused_duplicate_devs, &_found_duplicate_devs);
/*
* Remove items from the dups list that we know are the same
* underlying dev, e.g. md components, that we want to just ignore.
*/
_filter_duplicate_devs(cmd);
/*
* no more dups after ignoring some
*/
if (!_found_duplicate_pvs)
return;
/* Duplicates are found where we would have to pick one. */
dm_list_iterate_items(devl, &_unused_duplicate_devs)
log_warn("WARNING: found device with duplicate %s", dev_name(devl->dev));
}
int lvmcache_get_vgnameids(struct cmd_context *cmd, int include_internal,
struct dm_list *vgnameids)
int lvmcache_get_vgnameids(struct cmd_context *cmd,
struct dm_list *vgnameids,
const char *only_this_vgname,
int include_internal)
{
struct vgnameid_list *vgnl;
struct lvmcache_vginfo *vginfo;
if (only_this_vgname) {
if (!(vgnl = dm_pool_alloc(cmd->mem, sizeof(*vgnl)))) {
log_error("vgnameid_list allocation failed.");
return 0;
}
vgnl->vg_name = dm_pool_strdup(cmd->mem, only_this_vgname);
vgnl->vgid = NULL;
dm_list_add(vgnameids, &vgnl->list);
return 1;
}
dm_list_iterate_items(vginfo, &_vginfos) {
if (!include_internal && is_orphan_vg(vginfo->vgname))
continue;
@@ -1011,49 +990,6 @@ int lvmcache_get_vgnameids(struct cmd_context *cmd, int include_internal,
return 1;
}
struct dm_list *lvmcache_get_pvids(struct cmd_context *cmd, const char *vgname,
const char *vgid)
{
struct dm_list *pvids;
struct lvmcache_vginfo *vginfo;
struct lvmcache_info *info;
if (!(pvids = str_list_create(cmd->mem))) {
log_error("pvids list allocation failed");
return NULL;
}
if (!(vginfo = lvmcache_vginfo_from_vgname(vgname, vgid)))
return pvids;
dm_list_iterate_items(info, &vginfo->infos) {
if (!str_list_add(cmd->mem, pvids,
dm_pool_strdup(cmd->mem, info->dev->pvid))) {
log_error("strlist allocation failed");
return NULL;
}
}
return pvids;
}
int lvmcache_get_vg_devs(struct cmd_context *cmd,
struct lvmcache_vginfo *vginfo,
struct dm_list *devs)
{
struct lvmcache_info *info;
struct device_list *devl;
dm_list_iterate_items(info, &vginfo->infos) {
if (!(devl = dm_pool_zalloc(cmd->mem, sizeof(*devl))))
return_0;
devl->dev = info->dev;
dm_list_add(devs, &devl->list);
}
return 1;
}
static struct device *_device_from_pvid(const struct id *pvid, uint64_t *label_sector)
{
struct lvmcache_info *info;
@@ -1333,6 +1269,7 @@ static int _lvmcache_update_vgname(struct lvmcache_info *info,
return 0;
}
dm_list_init(&vginfo->infos);
dm_list_init(&vginfo->outdated_infos);
/*
* A different VG (different uuid) can exist with the same name.
@@ -1457,12 +1394,9 @@ int lvmcache_add_orphan_vginfo(const char *vgname, struct format_type *fmt)
}
/*
* FIXME: get rid of other callers of this function which call it
* in odd cases to "fix up" some bit of lvmcache state. Make those
* callers fix up what they need to directly, and leave this function
* with one purpose and caller.
* Returning 0 causes the caller to remove the info struct for this
* device from lvmcache, which will make it look like a missing device.
*/
int lvmcache_update_vgname_and_id(struct lvmcache_info *info, struct lvmcache_vgsummary *vgsummary)
{
const char *vgname = vgsummary->vgname;
@@ -1488,6 +1422,7 @@ int lvmcache_update_vgname_and_id(struct lvmcache_info *info, struct lvmcache_vg
* Puts the vginfo into the vgname hash table.
*/
if (!_lvmcache_update_vgname(info, vgname, vgid, vgsummary->vgstatus, vgsummary->creation_host, info->fmt)) {
/* shouldn't happen, internal error */
log_error("Failed to update VG %s info in lvmcache.", vgname);
return 0;
}
@@ -1496,6 +1431,7 @@ int lvmcache_update_vgname_and_id(struct lvmcache_info *info, struct lvmcache_vg
* Puts the vginfo into the vgid hash table.
*/
if (!_lvmcache_update_vgid(info, info->vginfo, vgid)) {
/* shouldn't happen, internal error */
log_error("Failed to update VG %s info in lvmcache.", vgname);
return 0;
}
@@ -1511,56 +1447,140 @@ int lvmcache_update_vgname_and_id(struct lvmcache_info *info, struct lvmcache_vg
if (!vgsummary->seqno && !vgsummary->mda_size && !vgsummary->mda_checksum)
return 1;
/*
* Keep track of which devs/mdas have old versions of the metadata.
* The values we keep in vginfo are from the metadata with the largest
* seqno. One dev may have more recent metadata than another dev, and
* one mda may have more recent metadata than the other mda on the same
* device.
*
* When a device holds old metadata, the info struct for the device
* remains in lvmcache, so the device is not treated as missing.
* Also the mda struct containing the old metadata is kept on
* info->mdas. This means that vg_read will read metadata from
* the mda again (and probably see the same old metadata). It
* also means that vg_write will use the mda to write new metadata
* into the mda that currently has the old metadata.
*/
if (vgsummary->mda_num == 1)
info->mda1_seqno = vgsummary->seqno;
else if (vgsummary->mda_num == 2)
info->mda2_seqno = vgsummary->seqno;
if (!info->summary_seqno)
info->summary_seqno = vgsummary->seqno;
else {
if (info->summary_seqno == vgsummary->seqno) {
/* This mda has the same metadata as the prev mda on this dev. */
return 1;
} else if (info->summary_seqno > vgsummary->seqno) {
/* This mda has older metadata than the prev mda on this dev. */
info->summary_seqno_mismatch = true;
} else if (info->summary_seqno < vgsummary->seqno) {
/* This mda has newer metadata than the prev mda on this dev. */
info->summary_seqno_mismatch = true;
info->summary_seqno = vgsummary->seqno;
}
}
/* this shouldn't happen */
if (!(vginfo = info->vginfo))
return 1;
if (!vginfo->seqno) {
vginfo->seqno = vgsummary->seqno;
log_debug_cache("lvmcache %s: VG %s: set seqno to %d",
dev_name(info->dev), vginfo->vgname, vginfo->seqno);
} else if (vgsummary->seqno != vginfo->seqno) {
log_warn("Scan of VG %s from %s found metadata seqno %d vs previous %d.",
vgname, dev_name(info->dev), vgsummary->seqno, vginfo->seqno);
vginfo->scan_summary_mismatch = 1;
/* If we don't return success, this dev info will be removed from lvmcache,
and then we won't be able to rescan it or repair it. */
return 1;
}
if (!vginfo->mda_size) {
vginfo->mda_checksum = vgsummary->mda_checksum;
vginfo->mda_size = vgsummary->mda_size;
log_debug_cache("lvmcache %s: VG %s: set mda_checksum to %x mda_size to %zu",
dev_name(info->dev), vginfo->vgname,
vginfo->mda_checksum, vginfo->mda_size);
log_debug_cache("lvmcache %s mda%d VG %s set seqno %u checksum %x mda_size %zu",
dev_name(info->dev), vgsummary->mda_num, vgname,
vgsummary->seqno, vgsummary->mda_checksum, vgsummary->mda_size);
goto update_vginfo;
} else if ((vginfo->mda_size != vgsummary->mda_size) || (vginfo->mda_checksum != vgsummary->mda_checksum)) {
log_warn("Scan of VG %s from %s found mda_checksum %x mda_size %zu vs previous %x %zu",
vgname, dev_name(info->dev), vgsummary->mda_checksum, vgsummary->mda_size,
vginfo->mda_checksum, vginfo->mda_size);
vginfo->scan_summary_mismatch = 1;
/* If we don't return success, this dev info will be removed from lvmcache,
and then we won't be able to rescan it or repair it. */
} else if (vgsummary->seqno < vginfo->seqno) {
vginfo->scan_summary_mismatch = true;
log_debug_cache("lvmcache %s mda%d VG %s older seqno %u checksum %x mda_size %zu",
dev_name(info->dev), vgsummary->mda_num, vgname,
vgsummary->seqno, vgsummary->mda_checksum, vgsummary->mda_size);
return 1;
} else if (vgsummary->seqno > vginfo->seqno) {
vginfo->scan_summary_mismatch = true;
/* Replace vginfo values with values from newer metadata. */
vginfo->seqno = vgsummary->seqno;
vginfo->mda_checksum = vgsummary->mda_checksum;
vginfo->mda_size = vgsummary->mda_size;
log_debug_cache("lvmcache %s mda%d VG %s newer seqno %u checksum %x mda_size %zu",
dev_name(info->dev), vgsummary->mda_num, vgname,
vgsummary->seqno, vgsummary->mda_checksum, vgsummary->mda_size);
goto update_vginfo;
} else {
/*
* Same seqno as previous metadata we saw for this VG.
* If the metadata somehow has a different checksum or size,
* even though it has the same seqno, something has gone wrong.
* FIXME: test this case: VG has two PVs, first goes missing,
* second updated to seqno 4, first comes back and second goes
* missing, first updated to seqno 4, second comes back, now
* both are present with same seqno but different checksums.
*/
if ((vginfo->mda_size != vgsummary->mda_size) || (vginfo->mda_checksum != vgsummary->mda_checksum)) {
log_warn("WARNING: scan of VG %s from %s mda%d found mda_checksum %x mda_size %zu vs %x %zu",
vgname, dev_name(info->dev), vgsummary->mda_num,
vgsummary->mda_checksum, vgsummary->mda_size,
vginfo->mda_checksum, vginfo->mda_size);
vginfo->scan_summary_mismatch = true;
return 0;
}
/*
* The seqno and checksum matches what was previously seen;
* the summary values have already been saved in vginfo.
*/
return 1;
}
/*
* If a dev has an unmatching checksum, ignore the other
* info from it, keeping the info we already saved.
*/
update_vginfo:
if (!_lvmcache_update_vgstatus(info, vgsummary->vgstatus, vgsummary->creation_host,
vgsummary->lock_type, vgsummary->system_id)) {
/*
* This shouldn't happen, it's an internal errror, and we can leave
* the info in place without saving the summary values in vginfo.
*/
log_error("Failed to update VG %s info in lvmcache.", vgname);
return 0;
}
return 1;
}
int lvmcache_update_vg(struct volume_group *vg, unsigned precommitted)
/*
* FIXME: quit trying to mirror changes that a command is making into lvmcache.
*
* First, it's complicated and hard to ensure it's done correctly in every case
* (it would be much easier and safer to just toss out what's in lvmcache and
* reread the info to recreate it from scratch instead of trying to make sure
* every possible discrete state change is correct.)
*
* Second, it's unnecessary if commands just use the vg they are modifying
* rather than also trying to get info from lvmcache. The lvmcache state
* should be populated by label_scan, used to perform vg_read's, and then
* ignored (or dropped so it can't be used).
*
* lvmcache info is already used very little after a command begins its
* operation. The code that's supposed to keep the lvmcache in sync with
* changes being made to disk could be half wrong and we wouldn't know it.
* That creates a landmine for someone who might try to use a bit of it that
* isn't being updated correctly.
*/
int lvmcache_update_vg_from_write(struct volume_group *vg)
{
struct pv_list *pvl;
struct lvmcache_info *info;
@@ -1584,6 +1604,110 @@ int lvmcache_update_vg(struct volume_group *vg, unsigned precommitted)
return 1;
}
/*
* The lvmcache representation of a VG after label_scan can be incorrect
* because the label_scan does not use the full VG metadata to construct
* vginfo/info. PVs that don't hold VG metadata weren't attached to the vginfo
* during label scan, and PVs with outdated metadata (claiming to be in the VG,
* but not listed in the latest metadata) were attached to the vginfo, but
* shouldn't be. After vg_read() gets the full metdata in the form of a 'vg',
* this function is called to fix up the lvmcache representation of the VG
* using the 'vg'.
*/
int lvmcache_update_vg_from_read(struct volume_group *vg, unsigned precommitted)
{
struct pv_list *pvl;
struct lvmcache_vginfo *vginfo;
struct lvmcache_info *info, *info2;
struct metadata_area *mda;
char pvid_s[ID_LEN + 1] __attribute__((aligned(8)));
struct lvmcache_vgsummary vgsummary = {
.vgname = vg->name,
.vgstatus = vg->status,
.vgid = vg->id,
.system_id = vg->system_id,
.lock_type = vg->lock_type
};
if (!(vginfo = lvmcache_vginfo_from_vgname(vg->name, (const char *)&vg->id))) {
log_error(INTERNAL_ERROR "lvmcache_update_vg %s no vginfo", vg->name);
return 0;
}
/*
* The label scan doesn't know when a PV with old metadata has been
* removed from the VG. Now with the vg we can tell, so remove the
* info for a PV that has been removed from the VG with
* vgreduce --removemissing.
*/
dm_list_iterate_items_safe(info, info2, &vginfo->infos) {
int found = 0;
dm_list_iterate_items(pvl, &vg->pvs) {
if (pvl->pv->dev != info->dev)
continue;
found = 1;
break;
}
if (found)
continue;
log_warn("WARNING: outdated PV %s seqno %u has been removed in current VG %s seqno %u.",
dev_name(info->dev), info->summary_seqno, vg->name, vginfo->seqno);
_drop_vginfo(info, vginfo); /* remove from vginfo->infos */
dm_list_add(&vginfo->outdated_infos, &info->list);
}
dm_list_iterate_items(pvl, &vg->pvs) {
(void) dm_strncpy(pvid_s, (char *) &pvl->pv->id, sizeof(pvid_s));
if (!(info = lvmcache_info_from_pvid(pvid_s, pvl->pv->dev, 0))) {
log_debug_cache("lvmcache_update_vg %s no info for %s %s",
vg->name,
(char *) &pvl->pv->id,
pvl->pv->dev ? dev_name(pvl->pv->dev) : "missing");
continue;
}
log_debug_cache("lvmcache_update_vg %s for info %s",
vg->name, dev_name(info->dev));
/*
* FIXME: use a different function that just attaches info's that
* had no metadata onto the correct vginfo.
*
* info's for PVs without metadata were not connected to the
* vginfo by label_scan, so do it here.
*/
if (!lvmcache_update_vgname_and_id(info, &vgsummary)) {
log_debug_cache("lvmcache_update_vg %s failed to update info for %s",
vg->name, dev_name(info->dev));
}
/*
* Ignored mdas were not copied from info->mdas to
* fid->metadata_areas... when create_text_instance (at the
* start of vg_read) called lvmcache_fid_add_mdas_vg because at
* that point the info's were not connected to the vginfo
* (since label_scan didn't know this without metadata.)
*/
dm_list_iterate_items(mda, &info->mdas) {
if (!mda_is_ignored(mda))
continue;
log_debug("lvmcache_update_vg %s copy ignored mdas for %s", vg->name, dev_name(info->dev));
if (!lvmcache_fid_add_mdas_pv(info, vg->fid)) {
log_debug_cache("lvmcache_update_vg %s failed to update mdas for %s",
vg->name, dev_name(info->dev));
}
break;
}
}
return 1;
}
/*
* We can see multiple different devices with the
* same pvid, i.e. duplicates.
@@ -1635,7 +1759,7 @@ int lvmcache_update_vg(struct volume_group *vg, unsigned precommitted)
* transient duplicate?
*/
static struct lvmcache_info * _create_info(struct labeller *labeller, struct device *dev)
static struct lvmcache_info * _create_info(struct labeller *labeller, struct device *dev, uint64_t label_sector)
{
struct lvmcache_info *info;
struct label *label;
@@ -1648,6 +1772,9 @@ static struct lvmcache_info * _create_info(struct labeller *labeller, struct dev
return NULL;
}
label->dev = dev;
label->sector = label_sector;
info->dev = dev;
info->fmt = labeller->fmt;
@@ -1663,8 +1790,9 @@ static struct lvmcache_info * _create_info(struct labeller *labeller, struct dev
}
struct lvmcache_info *lvmcache_add(struct labeller *labeller,
const char *pvid, struct device *dev,
const char *vgname, const char *vgid, uint32_t vgstatus)
const char *pvid, struct device *dev, uint64_t label_sector,
const char *vgname, const char *vgid, uint32_t vgstatus,
int *is_duplicate)
{
char pvid_s[ID_LEN + 1] __attribute__((aligned(8)));
char uuid[64] __attribute__((aligned(8)));
@@ -1692,7 +1820,7 @@ struct lvmcache_info *lvmcache_add(struct labeller *labeller,
info = lvmcache_info_from_pvid(dev->pvid, NULL, 0);
if (!info) {
info = _create_info(labeller, dev);
info = _create_info(labeller, dev, label_sector);
created = 1;
}
@@ -1724,6 +1852,8 @@ struct lvmcache_info *lvmcache_add(struct labeller *labeller,
dm_list_add(&_found_duplicate_devs, &devl->list);
_found_duplicate_pvs = 1;
if (is_duplicate)
*is_duplicate = 1;
return NULL;
}
@@ -1828,8 +1958,8 @@ void lvmcache_destroy(struct cmd_context *cmd, int retain_orphans, int reset)
dm_list_init(&_vginfos);
/*
* Copy the current _unused_duplicate_devs into a cmd list before
* destroying _unused_duplicate_devs.
* Move the current _unused_duplicate_devs to _prev_unused_duplicate_devs
* before destroying _unused_duplicate_devs.
*
* One command can init/populate/destroy lvmcache multiple times. Each
* time it will encounter duplicates and choose the preferrred devs.
@@ -1837,8 +1967,8 @@ void lvmcache_destroy(struct cmd_context *cmd, int retain_orphans, int reset)
* the unpreferred devs here so that _choose_preferred_devs can use
* this to make the same choice each time.
*/
dm_list_init(&cmd->unused_duplicate_devs);
lvmcache_get_unused_duplicate_devs(cmd, &cmd->unused_duplicate_devs);
_destroy_duplicate_device_list(&_prev_unused_duplicate_devs);
dm_list_splice(&_prev_unused_duplicate_devs, &_unused_duplicate_devs);
_destroy_duplicate_device_list(&_unused_duplicate_devs);
_destroy_duplicate_device_list(&_found_duplicate_devs); /* should be empty anyway */
_found_duplicate_pvs = 0;
@@ -1867,6 +1997,14 @@ int lvmcache_fid_add_mdas_pv(struct lvmcache_info *info, struct format_instance
return lvmcache_fid_add_mdas(info, fid, info->dev->pvid, ID_LEN);
}
/*
* This is the linkage where information is passed from
* the label_scan to vg_read.
*
* Called by create_text_instance in vg_read to copy the
* mda's found during label_scan and saved in info->mdas,
* to fid->metadata_areas_in_use which is used by vg_read.
*/
int lvmcache_fid_add_mdas_vg(struct lvmcache_vginfo *vginfo, struct format_instance *fid)
{
struct lvmcache_info *info;
@@ -1940,6 +2078,10 @@ void lvmcache_del_mdas(struct lvmcache_info *info)
if (info->mdas.n)
del_mdas(&info->mdas);
dm_list_init(&info->mdas);
if (info->bad_mdas.n)
del_mdas(&info->bad_mdas);
dm_list_init(&info->bad_mdas);
}
void lvmcache_del_das(struct lvmcache_info *info)
@@ -1957,9 +2099,10 @@ void lvmcache_del_bas(struct lvmcache_info *info)
}
int lvmcache_add_mda(struct lvmcache_info *info, struct device *dev,
uint64_t start, uint64_t size, unsigned ignored)
uint64_t start, uint64_t size, unsigned ignored,
struct metadata_area **mda_new)
{
return add_mda(info->fmt, NULL, &info->mdas, dev, start, size, ignored);
return add_mda(info->fmt, NULL, &info->mdas, dev, start, size, ignored, mda_new);
}
int lvmcache_add_da(struct lvmcache_info *info, uint64_t start, uint64_t size)
@@ -2127,26 +2270,6 @@ uint32_t lvmcache_ext_flags(struct lvmcache_info *info) {
return info->ext_flags;
}
int lvmcache_is_orphan(struct lvmcache_info *info) {
if (!info->vginfo)
return 1; /* FIXME? */
return is_orphan_vg(info->vginfo->vgname);
}
int lvmcache_vgid_is_cached(const char *vgid) {
struct lvmcache_vginfo *vginfo;
vginfo = lvmcache_vginfo_from_vgid(vgid);
if (!vginfo || !vginfo->vgname)
return 0;
if (is_orphan_vg(vginfo->vgname))
return 0;
return 1;
}
uint64_t lvmcache_smallest_mda_size(struct lvmcache_info *info)
{
if (!info)
@@ -2263,17 +2386,17 @@ int lvmcache_vg_is_foreign(struct cmd_context *cmd, const char *vgname, const ch
* lvmcache: info for dev5 is deleted, FIXME: use a defective state
*/
int lvmcache_scan_mismatch(struct cmd_context *cmd, const char *vgname, const char *vgid)
bool lvmcache_scan_mismatch(struct cmd_context *cmd, const char *vgname, const char *vgid)
{
struct lvmcache_vginfo *vginfo;
if (!vgname || !vgid)
return 1;
return true;
if ((vginfo = lvmcache_vginfo_from_vgid(vgid)))
return vginfo->scan_summary_mismatch;
return 1;
return true;
}
static uint64_t _max_metadata_size;
@@ -2332,3 +2455,117 @@ struct metadata_area *lvmcache_get_mda(struct cmd_context *cmd,
return NULL;
}
/*
* This is used by the metadata repair command to check if
* the metadata on a dev needs repair because it's old.
*/
bool lvmcache_has_old_metadata(struct cmd_context *cmd, const char *vgname, const char *vgid, struct device *dev)
{
struct lvmcache_vginfo *vginfo;
struct lvmcache_info *info;
/* shouldn't happen */
if (!vgname || !vgid)
return false;
/* shouldn't happen */
if (!(vginfo = lvmcache_vginfo_from_vgid(vgid)))
return false;
/* shouldn't happen */
if (!(info = lvmcache_info_from_pvid(dev->pvid, NULL, 0)))
return false;
/* writing to a new PV */
if (!info->summary_seqno)
return false;
/* on same dev, one mda has newer metadata than the other */
if (info->summary_seqno_mismatch)
return true;
/* one or both mdas on this dev has older metadata than another dev */
if (vginfo->seqno > info->summary_seqno)
return true;
return false;
}
void lvmcache_get_outdated_devs(struct cmd_context *cmd,
const char *vgname, const char *vgid,
struct dm_list *devs)
{
struct lvmcache_vginfo *vginfo;
struct lvmcache_info *info;
struct device_list *devl;
if (!(vginfo = lvmcache_vginfo_from_vgname(vgname, vgid))) {
log_error(INTERNAL_ERROR "lvmcache_get_outdated_devs no vginfo %s", vgname);
return;
}
dm_list_iterate_items(info, &vginfo->outdated_infos) {
if (!(devl = zalloc(sizeof(*devl))))
return;
devl->dev = info->dev;
dm_list_add(devs, &devl->list);
}
}
void lvmcache_del_outdated_devs(struct cmd_context *cmd,
const char *vgname, const char *vgid)
{
struct lvmcache_vginfo *vginfo;
struct lvmcache_info *info, *info2;
if (!(vginfo = lvmcache_vginfo_from_vgname(vgname, vgid))) {
log_error(INTERNAL_ERROR "lvmcache_del_outdated_devs no vginfo");
return;
}
dm_list_iterate_items_safe(info, info2, &vginfo->outdated_infos)
lvmcache_del(info);
}
void lvmcache_get_outdated_mdas(struct cmd_context *cmd,
const char *vgname, const char *vgid,
struct device *dev,
struct dm_list **mdas)
{
struct lvmcache_vginfo *vginfo;
struct lvmcache_info *info;
*mdas = NULL;
if (!(vginfo = lvmcache_vginfo_from_vgname(vgname, vgid))) {
log_error(INTERNAL_ERROR "lvmcache_get_outdated_mdas no vginfo");
return;
}
dm_list_iterate_items(info, &vginfo->outdated_infos) {
if (info->dev != dev)
continue;
*mdas = &info->mdas;
return;
}
}
bool lvmcache_is_outdated_dev(struct cmd_context *cmd,
const char *vgname, const char *vgid,
struct device *dev)
{
struct lvmcache_vginfo *vginfo;
struct lvmcache_info *info;
if (!(vginfo = lvmcache_vginfo_from_vgname(vgname, vgid))) {
log_error(INTERNAL_ERROR "lvmcache_get_outdated_mdas no vginfo");
return false;
}
dm_list_iterate_items(info, &vginfo->outdated_infos) {
if (info->dev == dev)
return true;
}
return false;
}

80
lib/cache/lvmcache.h vendored
View File

@@ -57,10 +57,12 @@ struct lvmcache_vgsummary {
char *creation_host;
const char *system_id;
const char *lock_type;
uint32_t seqno;
uint32_t mda_checksum;
size_t mda_size;
int zero_offset;
int seqno;
int mda_num; /* 1 = summary from mda1, 2 = summary from mda2 */
unsigned mda_ignored:1;
unsigned zero_offset:1;
};
int lvmcache_init(struct cmd_context *cmd);
@@ -72,9 +74,9 @@ int lvmcache_label_rescan_vg(struct cmd_context *cmd, const char *vgname, const
/* Add/delete a device */
struct lvmcache_info *lvmcache_add(struct labeller *labeller, const char *pvid,
struct device *dev,
const char *vgname, const char *vgid,
uint32_t vgstatus);
struct device *dev, uint64_t label_sector,
const char *vgname, const char *vgid,
uint32_t vgstatus, int *is_duplicate);
int lvmcache_add_orphan_vginfo(const char *vgname, struct format_type *fmt);
void lvmcache_del(struct lvmcache_info *info);
void lvmcache_del_dev(struct device *dev);
@@ -82,18 +84,15 @@ void lvmcache_del_dev(struct device *dev);
/* Update things */
int lvmcache_update_vgname_and_id(struct lvmcache_info *info,
struct lvmcache_vgsummary *vgsummary);
int lvmcache_update_vg(struct volume_group *vg, unsigned precommitted);
int lvmcache_update_vg_from_read(struct volume_group *vg, unsigned precommitted);
int lvmcache_update_vg_from_write(struct volume_group *vg);
void lvmcache_lock_vgname(const char *vgname, int read_only);
void lvmcache_unlock_vgname(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);
struct lvmcache_vginfo *lvmcache_vginfo_from_vgname(const char *vgname,
const char *vgid);
struct lvmcache_vginfo *lvmcache_vginfo_from_vgid(const char *vgid);
@@ -103,14 +102,11 @@ const char *lvmcache_vgid_from_vgname(struct cmd_context *cmd, const char *vgnam
struct device *lvmcache_device_from_pvid(struct cmd_context *cmd, const struct id *pvid, uint64_t *label_sector);
const char *lvmcache_vgname_from_info(struct lvmcache_info *info);
const struct format_type *lvmcache_fmt_from_info(struct lvmcache_info *info);
int lvmcache_vgs_locked(void);
int lvmcache_get_vgnameids(struct cmd_context *cmd, int include_internal,
struct dm_list *vgnameids);
/* Returns list of struct dm_str_list containing pool-allocated copy of pvids */
struct dm_list *lvmcache_get_pvids(struct cmd_context *cmd, const char *vgname,
const char *vgid);
int lvmcache_get_vgnameids(struct cmd_context *cmd,
struct dm_list *vgnameids,
const char *only_this_vgname,
int include_internal);
void lvmcache_drop_metadata(const char *vgname, int drop_precommitted);
void lvmcache_commit_metadata(const char *vgname);
@@ -127,7 +123,8 @@ void lvmcache_del_mdas(struct lvmcache_info *info);
void lvmcache_del_das(struct lvmcache_info *info);
void lvmcache_del_bas(struct lvmcache_info *info);
int lvmcache_add_mda(struct lvmcache_info *info, struct device *dev,
uint64_t start, uint64_t size, unsigned ignored);
uint64_t start, uint64_t size, unsigned ignored,
struct metadata_area **mda_new);
int lvmcache_add_da(struct lvmcache_info *info, uint64_t start, uint64_t size);
int lvmcache_add_ba(struct lvmcache_info *info, uint64_t start, uint64_t size);
@@ -163,9 +160,7 @@ int lvmcache_foreach_pv(struct lvmcache_vginfo *vginfo,
uint64_t lvmcache_device_size(struct lvmcache_info *info);
void lvmcache_set_device_size(struct lvmcache_info *info, uint64_t size);
struct device *lvmcache_device(struct lvmcache_info *info);
int lvmcache_is_orphan(struct lvmcache_info *info);
unsigned lvmcache_mda_count(struct lvmcache_info *info);
int lvmcache_vgid_is_cached(const char *vgid);
uint64_t lvmcache_smallest_mda_size(struct lvmcache_info *info);
struct metadata_area *lvmcache_get_mda(struct cmd_context *cmd,
@@ -176,8 +171,6 @@ struct metadata_area *lvmcache_get_mda(struct cmd_context *cmd,
int lvmcache_found_duplicate_pvs(void);
int lvmcache_found_duplicate_vgnames(void);
void lvmcache_pvscan_duplicate_check(struct cmd_context *cmd);
int lvmcache_get_unused_duplicate_devs(struct cmd_context *cmd, struct dm_list *head);
int vg_has_duplicate_pvs(struct volume_group *vg);
@@ -189,35 +182,44 @@ void lvmcache_get_max_name_lengths(struct cmd_context *cmd,
int lvmcache_vg_is_foreign(struct cmd_context *cmd, const char *vgname, const char *vgid);
void lvmcache_lock_ordering(int enable);
int lvmcache_dev_is_unchosen_duplicate(struct device *dev);
void lvmcache_remove_unchosen_duplicate(struct device *dev);
int lvmcache_pvid_in_unchosen_duplicates(const char *pvid);
int lvmcache_get_vg_devs(struct cmd_context *cmd,
struct lvmcache_vginfo *vginfo,
struct dm_list *devs);
void lvmcache_set_independent_location(const char *vgname);
int lvmcache_scan_mismatch(struct cmd_context *cmd, const char *vgname, const char *vgid);
bool lvmcache_scan_mismatch(struct cmd_context *cmd, const char *vgname, const char *vgid);
int lvmcache_vginfo_has_pvid(struct lvmcache_vginfo *vginfo, char *pvid);
/*
* These are clvmd-specific functions and are not related to lvmcache.
* FIXME: rename these with a clvm_ prefix in place of lvmcache_
*/
void lvmcache_save_vg(struct volume_group *vg, int precommitted);
struct volume_group *lvmcache_get_saved_vg(const char *vgid, int precommitted);
struct volume_group *lvmcache_get_saved_vg_latest(const char *vgid);
void lvmcache_drop_saved_vgid(const char *vgid);
uint64_t lvmcache_max_metadata_size(void);
void lvmcache_save_metadata_size(uint64_t val);
int dev_in_device_list(struct device *dev, struct dm_list *head);
bool lvmcache_has_bad_metadata(struct device *dev);
bool lvmcache_has_old_metadata(struct cmd_context *cmd, const char *vgname, const char *vgid, struct device *dev);
void lvmcache_get_outdated_devs(struct cmd_context *cmd,
const char *vgname, const char *vgid,
struct dm_list *devs);
void lvmcache_get_outdated_mdas(struct cmd_context *cmd,
const char *vgname, const char *vgid,
struct device *dev,
struct dm_list **mdas);
bool lvmcache_is_outdated_dev(struct cmd_context *cmd,
const char *vgname, const char *vgid,
struct device *dev);
void lvmcache_del_outdated_devs(struct cmd_context *cmd,
const char *vgname, const char *vgid);
void lvmcache_save_bad_mda(struct lvmcache_info *info, struct metadata_area *mda);
void lvmcache_get_bad_mdas(struct cmd_context *cmd,
const char *vgname, const char *vgid,
struct dm_list *bad_mda_list);
#endif

View File

@@ -1715,8 +1715,6 @@ struct cmd_context *create_toolcontext(unsigned is_clvmd,
if (!init_lvmcache_orphans(cmd))
goto_out;
dm_list_init(&cmd->unused_duplicate_devs);
if (!_init_segtypes(cmd))
goto_out;

View File

@@ -172,18 +172,21 @@ struct cmd_context {
unsigned pvscan_cache_single:1;
unsigned can_use_one_scan:1;
unsigned is_clvmd:1;
unsigned md_component_detection:1;
unsigned use_full_md_check:1;
unsigned is_activating:1;
unsigned enable_hints:1; /* hints are enabled for cmds in general */
unsigned use_hints:1; /* if hints are enabled this cmd can use them */
unsigned pvscan_recreate_hints:1; /* enable special case hint handling for pvscan --cache */
unsigned scan_lvs:1;
unsigned wipe_outdated_pvs:1;
/*
* Devices and filtering.
*/
struct dev_filter *filter;
struct dm_list hints;
const char *md_component_checks;
/*
* Configuration.
@@ -234,7 +237,6 @@ struct cmd_context {
const char *report_list_item_separator;
const char *time_format;
unsigned rand_seed;
struct dm_list unused_duplicate_devs; /* save preferences between lvmcache instances */
};
/*

View File

@@ -368,7 +368,30 @@ cfg(devices_multipath_component_detection_CFG, "multipath_component_detection",
"Ignore devices that are components of DM multipath devices.\n")
cfg(devices_md_component_detection_CFG, "md_component_detection", devices_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_MD_COMPONENT_DETECTION, vsn(1, 0, 18), NULL, 0, NULL,
"Ignore devices that are components of software RAID (md) devices.\n")
"Enable detection and exclusion of MD component devices.\n"
"An MD component device is a block device that MD uses as part\n"
"of a software RAID virtual device. When an LVM PV is created\n"
"on an MD device, LVM must only use the top level MD device as\n"
"the PV, and should ignore the underlying component devices.\n"
"In cases where the MD superblock is located at the end of the\n"
"component devices, it is more difficult for LVM to consistently\n"
"identify an MD component, see the md_component_checks setting.\n")
cfg(devices_md_component_checks_CFG, "md_component_checks", devices_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_STRING, DEFAULT_MD_COMPONENT_CHECKS, vsn(2, 3, 2), NULL, 0, NULL,
"The checks LVM should use to detect MD component devices.\n"
"MD component devices are block devices used by MD software RAID.\n"
"#\n"
"Accepted values:\n"
" auto\n"
" LVM will skip scanning the end of devices when it has other\n"
" indications that the device is not an MD component.\n"
" start\n"
" LVM will only scan the start of devices for MD superblocks.\n"
" This does not incur extra I/O by LVM.\n"
" full\n"
" LVM will scan the start and end of devices for MD superblocks.\n"
" This requires an extra read at the end of devices.\n"
"#\n")
cfg(devices_fw_raid_component_detection_CFG, "fw_raid_component_detection", devices_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_FW_RAID_COMPONENT_DETECTION, vsn(2, 2, 112), NULL, 0, NULL,
"Ignore devices that are components of firmware RAID devices.\n"

View File

@@ -318,4 +318,6 @@
#define DEFAULT_IO_MEMORY_SIZE_KB 8192
#define DEFAULT_MD_COMPONENT_CHECKS "auto"
#endif /* _LVM_DEFAULTS_H */

View File

@@ -110,14 +110,17 @@ static int _udev_dev_is_md_component(struct device *dev)
if (!(ext = dev_ext_get(dev)))
return_0;
if (!(value = udev_device_get_property_value((struct udev_device *)ext->handle, DEV_EXT_UDEV_BLKID_TYPE)))
if (!(value = udev_device_get_property_value((struct udev_device *)ext->handle, DEV_EXT_UDEV_BLKID_TYPE))) {
dev->flags |= DEV_UDEV_INFO_MISSING;
return 0;
}
return !strcmp(value, DEV_EXT_UDEV_BLKID_TYPE_SW_RAID);
}
#else
static int _udev_dev_is_md_component(struct device *dev)
{
dev->flags |= DEV_UDEV_INFO_MISSING;
return 0;
}
#endif

View File

@@ -1170,8 +1170,10 @@ int udev_dev_is_md_component(struct device *dev)
const char *value;
int ret = 0;
if (!obtain_device_list_from_udev())
if (!obtain_device_list_from_udev()) {
dev->flags |= DEV_UDEV_INFO_MISSING;
return 0;
}
if (!(udev_device = _udev_get_dev(dev)))
return 0;
@@ -1198,6 +1200,7 @@ int udev_dev_is_mpath_component(struct device *dev)
int udev_dev_is_md_component(struct device *dev)
{
dev->flags |= DEV_UDEV_INFO_MISSING;
return 0;
}

View File

@@ -37,6 +37,7 @@
#define DEV_BCACHE_WRITE 0x00008000 /* bcache_fd is open with RDWR */
#define DEV_SCAN_FOUND_LABEL 0x00010000 /* label scan read dev and found label */
#define DEV_IS_MD_COMPONENT 0x00020000 /* device is an md component */
#define DEV_UDEV_INFO_MISSING 0x00040000 /* we have no udev info for this device */
/*
* Support for external device info.

View File

@@ -121,157 +121,6 @@ static struct device *_mda_get_device_raw(struct metadata_area *mda)
return mdac->area.dev;
}
/*
* For circular region between region_start and region_start + region_size,
* back up one SECTOR_SIZE from 'region_ptr' and return the value.
* This allows reverse traversal through text metadata area to find old
* metadata.
*
* Parameters:
* region_start: start of the region (bytes)
* region_size: size of the region (bytes)
* region_ptr: pointer within the region (bytes)
* NOTE: region_start <= region_ptr <= region_start + region_size
*/
static uint64_t _get_prev_sector_circular(uint64_t region_start,
uint64_t region_size,
uint64_t region_ptr)
{
if (region_ptr >= region_start + SECTOR_SIZE)
return region_ptr - SECTOR_SIZE;
return (region_start + region_size - SECTOR_SIZE);
}
/*
* Analyze a metadata area for old metadata records in the circular buffer.
* This function just looks through and makes a first pass at the data in
* the sectors for particular things.
* FIXME: do something with each metadata area (try to extract vg, write
* raw data to file, etc)
*/
static int _pv_analyze_mda_raw (const struct format_type * fmt,
struct metadata_area *mda)
{
struct mda_header *mdah;
struct raw_locn *rlocn;
uint64_t area_start;
uint64_t area_size;
uint64_t prev_sector, prev_sector2;
uint64_t latest_mrec_offset;
uint64_t offset;
uint64_t offset2;
size_t size;
size_t size2;
char *buf=NULL;
struct device_area *area;
struct mda_context *mdac;
int r=0;
mdac = (struct mda_context *) mda->metadata_locn;
log_print("Found text metadata area: offset=" FMTu64 ", size="
FMTu64, mdac->area.start, mdac->area.size);
area = &mdac->area;
if (!(mdah = raw_read_mda_header(fmt, area, mda_is_primary(mda))))
goto_out;
rlocn = mdah->raw_locns;
/*
* The device area includes the metadata header as well as the
* records, so remove the metadata header from the start and size
*/
area_start = area->start + MDA_HEADER_SIZE;
area_size = area->size - MDA_HEADER_SIZE;
latest_mrec_offset = rlocn->offset + area->start;
/*
* Start searching at rlocn (point of live metadata) and go
* backwards.
*/
prev_sector = _get_prev_sector_circular(area_start, area_size,
latest_mrec_offset);
offset = prev_sector;
size = SECTOR_SIZE;
offset2 = size2 = 0;
while (prev_sector != latest_mrec_offset) {
prev_sector2 = prev_sector;
prev_sector = _get_prev_sector_circular(area_start, area_size,
prev_sector);
if (prev_sector > prev_sector2)
goto_out;
/*
* FIXME: for some reason, the whole metadata region from
* area->start to area->start+area->size is not used.
* Only ~32KB seems to contain valid metadata records
* (LVM2 format - format_text). As a result, I end up with
* "dm_config_maybe_section" returning true when there's no valid
* metadata in a sector (sectors with all nulls).
*/
if (!(buf = malloc(size + size2)))
goto_out;
if (!dev_read_bytes(area->dev, offset, size, buf)) {
log_error("Failed to read dev %s offset %llu size %llu",
dev_name(area->dev),
(unsigned long long)offset,
(unsigned long long)size);
goto out;
}
if (size2) {
if (!dev_read_bytes(area->dev, offset2, size2, buf + size)) {
log_error("Failed to read dev %s offset %llu size %llu",
dev_name(area->dev),
(unsigned long long)offset2,
(unsigned long long)size2);
goto out;
}
}
/*
* FIXME: We could add more sophisticated metadata detection
*/
if (dm_config_maybe_section(buf, size + size2)) {
/* FIXME: Validate region, pull out timestamp?, etc */
/* FIXME: Do something with this region */
log_verbose ("Found LVM2 metadata record at "
"offset=" FMTu64 ", size=" FMTsize_t ", "
"offset2=" FMTu64 " size2=" FMTsize_t,
offset, size, offset2, size2);
offset = prev_sector;
size = SECTOR_SIZE;
offset2 = size2 = 0;
} else {
/*
* Not a complete metadata record, assume we have
* metadata and just increase the size and offset.
* Start the second region if the previous sector is
* wrapping around towards the end of the disk.
*/
if (prev_sector > offset) {
offset2 = prev_sector;
size2 += SECTOR_SIZE;
} else {
offset = prev_sector;
size += SECTOR_SIZE;
}
}
free(buf);
buf = NULL;
}
r = 1;
out:
free(buf);
return r;
}
static int _text_lv_setup(struct format_instance *fid __attribute__((unused)),
struct logical_volume *lv)
{
@@ -312,7 +161,8 @@ static void _xlate_mdah(struct mda_header *mdah)
}
}
static int _raw_read_mda_header(struct mda_header *mdah, struct device_area *dev_area, int primary_mda)
static int _raw_read_mda_header(struct mda_header *mdah, struct device_area *dev_area,
int primary_mda, uint32_t ignore_bad_fields, uint32_t *bad_fields)
{
log_debug_metadata("Reading mda header sector from %s at %llu",
dev_name(dev_area->dev), (unsigned long long)dev_area->start);
@@ -320,53 +170,62 @@ static int _raw_read_mda_header(struct mda_header *mdah, struct device_area *dev
if (!dev_read_bytes(dev_area->dev, dev_area->start, MDA_HEADER_SIZE, mdah)) {
log_error("Failed to read metadata area header on %s at %llu",
dev_name(dev_area->dev), (unsigned long long)dev_area->start);
*bad_fields |= BAD_MDA_READ;
return 0;
}
if (mdah->checksum_xl != xlate32(calc_crc(INITIAL_CRC, (uint8_t *)mdah->magic,
MDA_HEADER_SIZE -
sizeof(mdah->checksum_xl)))) {
log_error("Incorrect checksum in metadata area header on %s at %llu",
log_warn("WARNING: wrong checksum %x in mda header on %s at %llu",
mdah->checksum_xl,
dev_name(dev_area->dev), (unsigned long long)dev_area->start);
return 0;
*bad_fields |= BAD_MDA_CHECKSUM;
}
_xlate_mdah(mdah);
if (memcmp(mdah->magic, FMTT_MAGIC, sizeof(mdah->magic))) {
log_error("Wrong magic number in metadata area header on %s at %llu",
log_warn("WARNING: wrong magic number in mda header on %s at %llu",
dev_name(dev_area->dev), (unsigned long long)dev_area->start);
return 0;
*bad_fields |= BAD_MDA_MAGIC;
}
if (mdah->version != FMTT_VERSION) {
log_error("Incompatible version %u metadata area header on %s at %llu",
log_warn("WARNING: wrong version %u in mda header on %s at %llu",
mdah->version,
dev_name(dev_area->dev), (unsigned long long)dev_area->start);
return 0;
*bad_fields |= BAD_MDA_VERSION;
}
if (mdah->start != dev_area->start) {
log_error("Incorrect start sector %llu in metadata area header on %s at %llu",
log_warn("WARNING: wrong start sector %llu in mda header on %s at %llu",
(unsigned long long)mdah->start,
dev_name(dev_area->dev), (unsigned long long)dev_area->start);
return 0;
*bad_fields |= BAD_MDA_START;
}
*bad_fields &= ~ignore_bad_fields;
if (*bad_fields)
return 0;
return 1;
}
struct mda_header *raw_read_mda_header(const struct format_type *fmt,
struct device_area *dev_area, int primary_mda)
struct device_area *dev_area,
int primary_mda, uint32_t ignore_bad_fields, uint32_t *bad_fields)
{
struct mda_header *mdah;
if (!(mdah = dm_pool_alloc(fmt->cmd->mem, MDA_HEADER_SIZE))) {
log_error("struct mda_header allocation failed");
*bad_fields |= BAD_MDA_INTERNAL;
return NULL;
}
if (!_raw_read_mda_header(mdah, dev_area, primary_mda)) {
if (!_raw_read_mda_header(mdah, dev_area, primary_mda, ignore_bad_fields, bad_fields)) {
dm_pool_free(fmt->cmd->mem, mdah);
return NULL;
}
@@ -564,8 +423,9 @@ static struct volume_group *_vg_read_raw_area(struct format_instance *fid,
time_t when;
char *desc;
uint32_t wrap = 0;
uint32_t bad_fields = 0;
if (!(mdah = raw_read_mda_header(fid->fmt, area, primary_mda))) {
if (!(mdah = raw_read_mda_header(fid->fmt, area, primary_mda, 0, &bad_fields))) {
log_error("Failed to read vg %s from %s", vgname, dev_name(area->dev));
goto out;
}
@@ -686,6 +546,7 @@ static int _vg_write_raw(struct format_instance *fid, struct volume_group *vg,
uint64_t old_start = 0, old_last = 0, old_size = 0, old_wrap = 0;
uint64_t new_start = 0, new_last = 0, new_size = 0, new_wrap = 0;
uint64_t max_size;
uint32_t bad_fields = 0;
char *new_buf = NULL;
int overlap;
int found = 0;
@@ -701,7 +562,7 @@ static int _vg_write_raw(struct format_instance *fid, struct volume_group *vg,
if (!found)
return 1;
if (!(mdah = raw_read_mda_header(fid->fmt, &mdac->area, mda_is_primary(mda))))
if (!(mdah = raw_read_mda_header(fid->fmt, &mdac->area, mda_is_primary(mda), mda->ignore_bad_fields, &bad_fields)))
goto_out;
/*
@@ -972,6 +833,7 @@ static int _vg_commit_raw_rlocn(struct format_instance *fid,
struct raw_locn *rlocn_slot1;
struct raw_locn *rlocn_new;
struct pv_list *pvl;
uint32_t bad_fields = 0;
int r = 0;
int found = 0;
@@ -992,7 +854,7 @@ static int _vg_commit_raw_rlocn(struct format_instance *fid,
* mdah buffer, but the mdah buffer is not modified and mdac->rlocn is
* modified.
*/
if (!(mdab = raw_read_mda_header(fid->fmt, &mdac->area, mda_is_primary(mda))))
if (!(mdab = raw_read_mda_header(fid->fmt, &mdac->area, mda_is_primary(mda), mda->ignore_bad_fields, &bad_fields)))
goto_out;
/*
@@ -1184,6 +1046,7 @@ static int _vg_remove_raw(struct format_instance *fid, struct volume_group *vg,
struct mda_header *mdah;
struct raw_locn *rlocn_slot0;
struct raw_locn *rlocn_slot1;
uint32_t bad_fields = 0;
int r = 0;
if (!(mdah = dm_pool_alloc(fid->fmt->cmd->mem, MDA_HEADER_SIZE))) {
@@ -1197,7 +1060,7 @@ static int _vg_remove_raw(struct format_instance *fid, struct volume_group *vg,
* Just to print the warning?
*/
if (!_raw_read_mda_header(mdah, &mdac->area, mda_is_primary(mda)))
if (!_raw_read_mda_header(mdah, &mdac->area, mda_is_primary(mda), 0, &bad_fields))
log_warn("WARNING: Removing metadata location on %s with bad mda header.",
dev_name(mdac->area.dev));
@@ -1494,7 +1357,7 @@ int read_metadata_location_summary(const struct format_type *fmt,
* valid vg name.
*/
if (!validate_name(namebuf)) {
log_error("Metadata location on %s at %llu begins with invalid VG name.",
log_warn("WARNING: Metadata location on %s at %llu begins with invalid VG name.",
dev_name(dev_area->dev),
(unsigned long long)(dev_area->start + rlocn->offset));
return 0;
@@ -1560,7 +1423,7 @@ int read_metadata_location_summary(const struct format_type *fmt,
(off_t) (dev_area->start + MDA_HEADER_SIZE),
wrap, calc_crc, vgsummary->vgname ? 1 : 0,
vgsummary)) {
log_error("Metadata location on %s at %llu has invalid summary for VG.",
log_warn("WARNING: metadata on %s at %llu has invalid summary for VG.",
dev_name(dev_area->dev),
(unsigned long long)(dev_area->start + rlocn->offset));
return 0;
@@ -1568,7 +1431,7 @@ int read_metadata_location_summary(const struct format_type *fmt,
/* Ignore this entry if the characters aren't permissible */
if (!validate_name(vgsummary->vgname)) {
log_error("Metadata location on %s at %llu has invalid VG name.",
log_warn("WARNING: metadata on %s at %llu has invalid VG name.",
dev_name(dev_area->dev),
(unsigned long long)(dev_area->start + rlocn->offset));
return 0;
@@ -1650,13 +1513,12 @@ static int _text_pv_write(const struct format_type *fmt, struct physical_volume
/* Add a new cache entry with PV info or update existing one. */
if (!(info = lvmcache_add(fmt->labeller, (const char *) &pv->id,
pv->dev, pv->vg_name,
is_orphan_vg(pv->vg_name) ? pv->vg_name : pv->vg ? (const char *) &pv->vg->id : NULL, 0)))
pv->dev, pv->label_sector, pv->vg_name,
is_orphan_vg(pv->vg_name) ? pv->vg_name : pv->vg ? (const char *) &pv->vg->id : NULL, 0, NULL)))
return_0;
/* lvmcache_add() creates info and info->label structs for the dev, get info->label. */
label = lvmcache_get_label(info);
label->sector = pv->label_sector;
label->dev = pv->dev;
lvmcache_update_pv(info, pv, fmt);
@@ -1684,7 +1546,7 @@ static int _text_pv_write(const struct format_type *fmt, struct physical_volume
// if fmt is not the same as info->fmt we are in trouble
if (!lvmcache_add_mda(info, mdac->area.dev,
mdac->area.start, mdac->area.size,
mda_is_ignored(mda)))
mda_is_ignored(mda), NULL))
return_0;
}
@@ -1738,12 +1600,16 @@ static int _text_pv_needs_rewrite(const struct format_type *fmt, struct physical
{
struct lvmcache_info *info;
uint32_t ext_vsn;
uint32_t ext_flags;
*needs_rewrite = 0;
if (!pv->is_labelled)
return 1;
if (!pv->dev)
return 1;
if (!(info = lvmcache_info_from_pvid((const char *)&pv->id, pv->dev, 0))) {
log_error("Failed to find cached info for PV %s.", pv_dev_name(pv));
return 0;
@@ -1751,8 +1617,16 @@ static int _text_pv_needs_rewrite(const struct format_type *fmt, struct physical
ext_vsn = lvmcache_ext_version(info);
if (ext_vsn < PV_HEADER_EXTENSION_VSN)
if (ext_vsn < PV_HEADER_EXTENSION_VSN) {
log_debug("PV %s header needs rewrite for new ext version", dev_name(pv->dev));
*needs_rewrite = 1;
}
ext_flags = lvmcache_ext_flags(info);
if (!(ext_flags & PV_EXT_USED)) {
log_debug("PV %s header needs rewrite to set ext used", dev_name(pv->dev));
*needs_rewrite = 1;
}
return 1;
}
@@ -1962,7 +1836,6 @@ static struct metadata_area_ops _metadata_text_raw_ops = {
.mda_free_sectors = _mda_free_sectors_raw,
.mda_total_sectors = _mda_total_sectors_raw,
.mda_in_vg = _mda_in_vg_raw,
.pv_analyze_mda = _pv_analyze_mda_raw,
.mda_locns_match = _mda_locns_match_raw,
.mda_get_device = _mda_get_device_raw,
};
@@ -2603,220 +2476,36 @@ 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)
int text_wipe_outdated_pv_mda(struct cmd_context *cmd, struct device *dev,
struct metadata_area *mda)
{
struct mda_header *mh;
struct mda_context *mdac = mda->metadata_locn;
uint64_t start_byte = mdac->area.start;
struct mda_header *mdab;
struct raw_locn *rlocn_slot0;
uint64_t text_offset, text_size;
char *area_buf;
char *text_buf;
struct raw_locn *rlocn_slot1;
uint32_t bad_fields = 0;
/*
* 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");
if (!(mdab = raw_read_mda_header(cmd->fmt, &mdac->area, mda_is_primary(mda), 0, &bad_fields))) {
log_error("Failed to read outdated pv mda header on %s", dev_name(dev));
return 0;
}
mdac = mda->metadata_locn;
rlocn_slot0 = &mdab->raw_locns[0];
rlocn_slot1 = &mdab->raw_locns[1];
/*
* 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);
rlocn_slot0->offset = 0;
rlocn_slot0->size = 0;
rlocn_slot0->checksum = 0;
rlocn_slot1->offset = 0;
rlocn_slot1->size = 0;
rlocn_slot1->checksum = 0;
if (!_raw_write_mda_header(cmd->fmt, dev, mda_is_primary(mda), start_byte, mdab)) {
log_error("Failed to write outdated pv mda header on %s", dev_name(dev));
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;
return 1;
}
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;
}

View File

@@ -61,7 +61,8 @@ int add_ba(struct dm_pool *mem, struct dm_list *eas,
uint64_t start, uint64_t size);
void del_bas(struct dm_list *bas);
int add_mda(const struct format_type *fmt, struct dm_pool *mem, struct dm_list *mdas,
struct device *dev, uint64_t start, uint64_t size, unsigned ignored);
struct device *dev, uint64_t start, uint64_t size, unsigned ignored,
struct metadata_area **mda_new);
void del_mdas(struct dm_list *mdas);
/* On disk */
@@ -76,18 +77,7 @@ 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);
int text_wipe_outdated_pv_mda(struct cmd_context *cmd, struct device *dev,
struct metadata_area *mda);
#endif

View File

@@ -61,13 +61,13 @@ int text_read_metadata_summary(const struct format_type *fmt,
offset2, size2, checksum_fn,
vgsummary->mda_checksum,
checksum_only, 1)) {
/* FIXME: handle errors */
log_error("Couldn't read volume group metadata from %s.", dev_name(dev));
log_warn("WARNING: invalid metadata text from %s at %llu.",
dev_name(dev), (unsigned long long)offset);
goto out;
}
} else {
if (!config_file_read(cft)) {
log_error("Couldn't read volume group metadata from file.");
log_warn("WARNING: invalid metadata text from file.");
goto out;
}
}

View File

@@ -81,7 +81,9 @@ struct mda_header {
} __attribute__ ((packed));
struct mda_header *raw_read_mda_header(const struct format_type *fmt,
struct device_area *dev_area, int primary_mda);
struct device_area *dev_area, int primary_mda,
uint32_t ignore_bad_fields,
uint32_t *bad_fields);
struct mda_lists {
struct metadata_area_ops *file_ops;

View File

@@ -241,11 +241,10 @@ void del_bas(struct dm_list *bas)
del_das(bas);
}
/* FIXME: refactor this function with other mda constructor code */
int add_mda(const struct format_type *fmt, struct dm_pool *mem, struct dm_list *mdas,
struct device *dev, uint64_t start, uint64_t size, unsigned ignored)
struct device *dev, uint64_t start, uint64_t size, unsigned ignored,
struct metadata_area **mda_new)
{
/* FIXME List size restricted by pv_header SECTOR_SIZE */
struct metadata_area *mdal, *mda;
struct mda_lists *mda_lists = (struct mda_lists *) fmt->private;
struct mda_context *mdac, *mdac2;
@@ -295,6 +294,8 @@ int add_mda(const struct format_type *fmt, struct dm_pool *mem, struct dm_list *
mda_set_ignored(mdal, ignored);
dm_list_add(mdas, &mdal->list);
if (mda_new)
*mda_new = mdal;
return 1;
}
@@ -319,78 +320,103 @@ static int _text_initialise_label(struct labeller *l __attribute__((unused)),
return 1;
}
struct _update_mda_baton {
struct lvmcache_info *info;
struct label *label;
};
static int _read_mda_header_and_metadata(struct metadata_area *mda, void *baton)
static int _read_mda_header_and_metadata(const struct format_type *fmt,
struct metadata_area *mda,
struct lvmcache_vgsummary *vgsummary,
uint32_t *bad_fields)
{
struct _update_mda_baton *p = 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 lvmcache_vgsummary vgsummary = { 0 };
if (!(mdah = raw_read_mda_header(fmt, &mdac->area, mda_is_primary(mda)))) {
log_error("Failed to read mda header from %s", dev_name(mdac->area.dev));
goto fail;
if (!(mdah = raw_read_mda_header(fmt, &mdac->area, (mda->mda_num == 1), 0, bad_fields))) {
log_warn("WARNING: bad metadata header on %s at %llu.",
dev_name(mdac->area.dev),
(unsigned long long)mdac->area.start);
if (mda)
mda->header_start = mdac->area.start;
*bad_fields |= BAD_MDA_HEADER;
return 0;
}
if (mda)
mda->header_start = mdah->start;
mda_set_ignored(mda, rlocn_is_ignored(mdah->raw_locns));
if (mda_is_ignored(mda)) {
log_debug_metadata("Ignoring mda on device %s at offset " FMTu64,
dev_name(mdac->area.dev),
mdac->area.start);
vgsummary->mda_ignored = 1;
return 1;
}
if (!read_metadata_location_summary(fmt, mdah, mda_is_primary(mda), &mdac->area,
&vgsummary, &mdac->free_sectors)) {
if (vgsummary.zero_offset)
vgsummary, &mdac->free_sectors)) {
if (vgsummary->zero_offset)
return 1;
log_error("Failed to read metadata summary from %s", dev_name(mdac->area.dev));
goto fail;
}
if (!lvmcache_update_vgname_and_id(p->info, &vgsummary)) {
log_error("Failed to save lvm summary for %s", dev_name(mdac->area.dev));
goto fail;
log_warn("WARNING: bad metadata text on %s in mda%d",
dev_name(mdac->area.dev), mda->mda_num);
*bad_fields |= BAD_MDA_TEXT;
return 0;
}
return 1;
fail:
lvmcache_del(p->info);
return 0;
}
static int _text_read(struct labeller *l, struct device *dev, void *label_buf,
struct label **label)
/*
* Used by label_scan to get a summary of the VG that exists on this PV. This
* summary is stored in lvmcache vginfo/info/info->mdas and is used later by
* vg_read which needs to know which PVs to read for a given VG name, and where
* the metadata is at for those PVs.
*/
static int _text_read(struct labeller *labeller, struct device *dev, void *label_buf,
uint64_t label_sector, int *is_duplicate)
{
struct lvmcache_vgsummary vgsummary;
struct lvmcache_info *info;
const struct format_type *fmt = labeller->fmt;
struct label_header *lh = (struct label_header *) label_buf;
struct pv_header *pvhdr;
struct pv_header_extension *pvhdr_ext;
struct lvmcache_info *info;
struct metadata_area *mda;
struct metadata_area *mda1 = NULL;
struct metadata_area *mda2 = NULL;
struct disk_locn *dlocn_xl;
uint64_t offset;
uint32_t ext_version;
struct _update_mda_baton baton;
uint32_t bad_fields;
int mda_count = 0;
int good_mda_count = 0;
int bad_mda_count = 0;
int rv1, rv2;
/*
* PV header base
*/
pvhdr = (struct pv_header *) ((char *) label_buf + xlate32(lh->offset_xl));
if (!(info = lvmcache_add(l, (char *)pvhdr->pv_uuid, dev,
/*
* FIXME: stop adding the device to lvmcache initially as an orphan
* (and then moving it later) and instead just add it when we know the
* VG.
*
* If another device with this same PVID has already been seen,
* lvmcache_add will put this device in the duplicates list in lvmcache
* and return NULL. At the end of label_scan, the duplicate devs are
* compared, and if another dev is preferred for this PV, then the
* existing dev is removed from lvmcache and _text_read is called again
* for this dev, and lvmcache_add will add it.
*
* Other reasons for lvmcache_add to return NULL are internal errors.
*/
if (!(info = lvmcache_add(labeller, (char *)pvhdr->pv_uuid, dev, label_sector,
FMT_TEXT_ORPHAN_VG_NAME,
FMT_TEXT_ORPHAN_VG_NAME, 0)))
FMT_TEXT_ORPHAN_VG_NAME, 0, is_duplicate)))
return_0;
*label = lvmcache_get_label(info);
lvmcache_set_device_size(info, xlate64(pvhdr->device_size_xl));
lvmcache_del_das(info);
@@ -404,11 +430,27 @@ static int _text_read(struct labeller *l, struct device *dev, void *label_buf,
dlocn_xl++;
}
/* Metadata area headers */
dlocn_xl++;
/* Metadata areas */
while ((offset = xlate64(dlocn_xl->offset))) {
lvmcache_add_mda(info, dev, offset, xlate64(dlocn_xl->size), 0);
/*
* This just calls add_mda() above, replacing info with info->mdas.
*/
lvmcache_add_mda(info, dev, offset, xlate64(dlocn_xl->size), 0, &mda);
dlocn_xl++;
mda_count++;
if (mda_count == 1) {
mda1 = mda;
mda1->mda_num = 1;
}
else if (mda_count == 2) {
mda2 = mda;
mda2->mda_num = 2;
}
}
dlocn_xl++;
@@ -418,7 +460,7 @@ static int _text_read(struct labeller *l, struct device *dev, void *label_buf,
*/
pvhdr_ext = (struct pv_header_extension *) ((char *) dlocn_xl);
if (!(ext_version = xlate32(pvhdr_ext->version)))
goto out;
goto scan_mdas;
log_debug_metadata("%s: PV header extension version " FMTu32 " found",
dev_name(dev), ext_version);
@@ -435,22 +477,117 @@ static int _text_read(struct labeller *l, struct device *dev, void *label_buf,
lvmcache_add_ba(info, offset, xlate64(dlocn_xl->size));
dlocn_xl++;
}
out:
baton.info = info;
baton.label = *label;
/*
* In the vg_read phase, we compare all mdas and decide which to use
* which are bad and need repair.
*
* FIXME: this quits if the first mda is bad, but we need something
* smarter to be able to use the second mda if it's good.
*/
if (!lvmcache_foreach_mda(info, _read_mda_header_and_metadata, &baton)) {
log_error("Failed to scan VG from %s", dev_name(dev));
return 0;
scan_mdas:
if (!mda_count) {
log_debug_metadata("Scanning %s found no mdas.", dev_name(dev));
return 1;
}
/*
* Track which devs have bad metadata so repair can find them (even if
* this dev also has good metadata that we are able to use).
*
* When bad metadata is seen, the unusable mda struct is removed from
* lvmcache info->mdas. This means that vg_read and vg_write will skip
* the bad mda not try to read or write the bad metadata. The bad mdas
* are saved in a separate bad_mdas list in lvmcache so that repair can
* find them to repair.
*/
if (mda1) {
log_debug_metadata("Scanning %s mda1 summary.", dev_name(dev));
memset(&vgsummary, 0, sizeof(vgsummary));
bad_fields = 0;
vgsummary.mda_num = 1;
rv1 = _read_mda_header_and_metadata(fmt, mda1, &vgsummary, &bad_fields);
if (rv1 && !vgsummary.zero_offset && !vgsummary.mda_ignored) {
if (!lvmcache_update_vgname_and_id(info, &vgsummary)) {
/* I believe this is only an internal error. */
log_warn("WARNING: Scanning %s mda1 failed to save internal summary.", dev_name(dev));
dm_list_del(&mda1->list);
bad_fields |= BAD_MDA_INTERNAL;
mda1->bad_fields = bad_fields;
lvmcache_save_bad_mda(info, mda1);
mda1 = NULL;
bad_mda_count++;
} else {
/* The normal success path */
log_debug("Scanned %s mda1 seqno %u", dev_name(dev), vgsummary.seqno);
good_mda_count++;
}
}
if (!rv1) {
/*
* Remove the bad mda from normal mda list so it's not
* used by vg_read/vg_write, but keep track of it in
* lvmcache for repair.
*/
log_warn("WARNING: scanning %s mda1 failed to read metadata summary.", dev_name(dev));
log_warn("WARNING: repair VG metadata on %s with vgck --updatemetadata.", dev_name(dev));
dm_list_del(&mda1->list);
mda1->bad_fields = bad_fields;
lvmcache_save_bad_mda(info, mda1);
mda1 = NULL;
bad_mda_count++;
}
}
if (mda2) {
log_debug_metadata("Scanning %s mda2 summary.", dev_name(dev));
memset(&vgsummary, 0, sizeof(vgsummary));
bad_fields = 0;
vgsummary.mda_num = 2;
rv2 = _read_mda_header_and_metadata(fmt, mda2, &vgsummary, &bad_fields);
if (rv2 && !vgsummary.zero_offset && !vgsummary.mda_ignored) {
if (!lvmcache_update_vgname_and_id(info, &vgsummary)) {
/* I believe this is only an internal error. */
log_warn("WARNING: Scanning %s mda2 failed to save internal summary.", dev_name(dev));
dm_list_del(&mda2->list);
bad_fields |= BAD_MDA_INTERNAL;
mda2->bad_fields = bad_fields;
lvmcache_save_bad_mda(info, mda2);
mda2 = NULL;
bad_mda_count++;
} else {
/* The normal success path */
log_debug("Scanned %s mda2 seqno %u", dev_name(dev), vgsummary.seqno);
good_mda_count++;
}
}
if (!rv2) {
/*
* Remove the bad mda from normal mda list so it's not
* used by vg_read/vg_write, but keep track of it in
* lvmcache for repair.
*/
log_warn("WARNING: scanning %s mda2 failed to read metadata summary.", dev_name(dev));
log_warn("WARNING: repair VG metadata on %s with vgck --updatemetadata.", dev_name(dev));
dm_list_del(&mda2->list);
mda2->bad_fields = bad_fields;
lvmcache_save_bad_mda(info, mda2);
mda2 = NULL;
bad_mda_count++;
}
}
if (good_mda_count)
return 1;
if (bad_mda_count)
return 0;
/* no metadata in the mdas */
return 1;
}

View File

@@ -356,9 +356,9 @@ static int _process_block(struct cmd_context *cmd, struct dev_filter *f,
int *is_lvm_device)
{
char label_buf[LABEL_SIZE] __attribute__((aligned(8)));
struct label *label = NULL;
struct labeller *labeller;
uint64_t sector = 0;
int is_duplicate = 0;
int ret = 0;
int pass;
@@ -423,17 +423,38 @@ static int _process_block(struct cmd_context *cmd, struct dev_filter *f,
/*
* This is the point where the scanning code dives into the rest of
* lvm. ops->read() is usually _text_read() which reads the pv_header,
* mda locations, mda contents. As these bits of data are read, they
* are saved into lvmcache as info/vginfo structs.
* lvm. ops->read() is _text_read() which reads the pv_header, mda
* locations, and metadata text. All of the info it finds about the PV
* and VG is stashed in lvmcache which saves it in the form of
* info/vginfo structs. That lvmcache info is used later when the
* command wants to read the VG to do something to it.
*/
ret = labeller->ops->read(labeller, dev, label_buf, sector, &is_duplicate);
if ((ret = (labeller->ops->read)(labeller, dev, label_buf, &label)) && label) {
label->dev = dev;
label->sector = sector;
} else {
/* FIXME: handle errors */
lvmcache_del_dev(dev);
if (!ret) {
if (is_duplicate) {
/*
* _text_read() called lvmcache_add() which found an
* existing info struct for this PVID but for a
* different dev. lvmcache_add() did not add an info
* struct for this dev, but added this dev to the list
* of duplicate devs.
*/
log_warn("WARNING: scan found duplicate PVID %s on %s", dev->pvid, dev_name(dev));
} else {
/*
* Leave the info in lvmcache because the device is
* present and can still be used even if it has
* metadata that we can't process (we can get metadata
* from another PV/mda.) _text_read only saves mdas
* with good metadata in lvmcache (this includes old
* metadata), and if a PV has no mdas with good
* metadata, then the info for the PV will be in
* lvmcache with empty info->mdas, and it will behave
* like a PV with no mdas (a common configuration.)
*/
log_warn("WARNING: scan failed to get metadata summary from %s PVID %s", dev_name(dev), dev->pvid);
}
}
out:
return ret;
@@ -696,7 +717,6 @@ static int _scan_list(struct cmd_context *cmd, struct dev_filter *f,
scan_failed = 1;
scan_process_errors++;
scan_failed_count++;
lvmcache_del_dev(devl->dev);
}
}
@@ -1022,6 +1042,33 @@ int label_scan(struct cmd_context *cmd)
}
}
/*
* Stronger exclusion of md components that might have been
* misidentified as PVs due to having an end-of-device md superblock.
* If we're not using hints, and are not already doing a full md check
* on devs being scanned, then if udev info is missing for a PV, scan
* the end of the PV to verify it's not an md component. The full
* dev_is_md_component call will do new reads at the end of the dev.
*/
if (cmd->md_component_detection && !cmd->use_full_md_check && !using_hints &&
!strcmp(cmd->md_component_checks, "auto")) {
int once = 0;
dm_list_iterate_items(devl, &scan_devs) {
if (!(devl->dev->flags & DEV_SCAN_FOUND_LABEL))
continue;
if (!(devl->dev->flags & DEV_UDEV_INFO_MISSING))
continue;
if (!once++)
log_debug_devs("Scanning end of PVs with no udev info for MD components");
if (dev_is_md_component(devl->dev, NULL, 1)) {
log_debug_devs("Drop PV from MD component %s", dev_name(devl->dev));
devl->dev->flags &= ~DEV_SCAN_FOUND_LABEL;
lvmcache_del_dev(devl->dev);
}
}
}
dm_list_iterate_items_safe(devl, devl2, &all_devs) {
dm_list_del(&devl->list);
free(devl);
@@ -1198,66 +1245,6 @@ int label_read(struct device *dev)
return 1;
}
/*
* Read a label from a specfic, non-zero sector. This is used in only
* one place: pvck/pv_analyze.
*/
int label_read_sector(struct device *dev, uint64_t read_sector)
{
struct block *bb = NULL;
uint64_t block_num;
uint64_t block_sector;
uint64_t start_sector;
int is_lvm_device = 0;
int result;
int ret;
block_num = read_sector / BCACHE_BLOCK_SIZE_IN_SECTORS;
block_sector = block_num * BCACHE_BLOCK_SIZE_IN_SECTORS;
start_sector = read_sector % BCACHE_BLOCK_SIZE_IN_SECTORS;
if (!label_scan_open(dev)) {
log_error("Error opening device %s for prefetch %llu sector.",
dev_name(dev), (unsigned long long)block_num);
return false;
}
bcache_prefetch(scan_bcache, dev->bcache_fd, block_num);
if (!bcache_get(scan_bcache, dev->bcache_fd, block_num, 0, &bb)) {
log_error("Scan failed to read %s at %llu",
dev_name(dev), (unsigned long long)block_num);
ret = 0;
goto out;
}
/*
* TODO: check if scan_sector is larger than the bcache block size.
* If it is, we need to fetch a later block from bcache.
*/
result = _process_block(NULL, NULL, dev, bb, block_sector, start_sector, &is_lvm_device);
if (!result && is_lvm_device) {
log_error("Scan failed to process %s", dev_name(dev));
ret = 0;
goto out;
}
if (!result || !is_lvm_device) {
log_error("Could not find LVM label on %s", dev_name(dev));
ret = 0;
goto out;
}
ret = 1;
out:
if (bb)
bcache_put(bb);
return ret;
}
int label_scan_setup_bcache(void)
{
if (!scan_bcache) {

View File

@@ -65,7 +65,7 @@ struct label_ops {
* Read a label from a volume.
*/
int (*read) (struct labeller * l, struct device * dev,
void *label_buf, struct label ** label);
void *label_buf, uint64_t label_sector, int *is_duplicate);
/*
* Populate label_type etc.

View File

@@ -156,12 +156,20 @@ int init_locking(struct cmd_context *cmd,
return 1;
}
void fin_locking(void)
void fin_locking(struct cmd_context *cmd)
{
/* file locking disabled */
if (!_locking.flags)
return;
/*
* These may be automatically released when the
* command ends, without an explicit unlock call,
* in which case these flags would not be cleared.
*/
cmd->lockf_global_ex = 0;
cmd->lockd_global_ex = 0;
_locking.fin_locking();
}
@@ -376,6 +384,10 @@ int lockf_global(struct cmd_context *cmd, const char *mode)
int lockf_global_convert(struct cmd_context *cmd, const char *mode)
{
/* some uncommon cases like pvchange -a can call this multiple times */
if (cmd->lockf_global_ex && !strcmp(mode, "ex"))
return 1;
return _lockf_global(cmd, mode, 1);
}

View File

@@ -22,7 +22,7 @@
struct logical_volume;
int init_locking(struct cmd_context *cmd, int file_locking_sysinit, int file_locking_readonly, int file_locking_ignorefail);
void fin_locking(void);
void fin_locking(struct cmd_context *cmd);
void reset_locking(void);
int vg_write_lock_held(void);

View File

@@ -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))) {

View File

@@ -181,15 +181,14 @@
#define MIRROR_SKIP_INIT_SYNC 0x00000010U /* skip initial sync */
/* vg_read and vg_read_for_update flags */
#define READ_ALLOW_INCONSISTENT 0x00010000U
#define READ_ALLOW_EXPORTED 0x00020000U
#define READ_OK_NOTFOUND 0x00040000U
#define READ_WARN_INCONSISTENT 0x00080000U
#define READ_FOR_UPDATE 0x00100000U /* A meta-flag, useful with toollib for_each_* functions. */
#define PROCESS_SKIP_SCAN 0x00200000U /* skip lvmcache_label_scan in process_each_pv */
/* vg's "read_status" field */
#define FAILED_INCONSISTENT 0x00000001U
/* vg_read returns these in error_flags */
#define FAILED_NOT_ENABLED 0x00000001U
#define FAILED_LOCKING 0x00000002U
#define FAILED_NOTFOUND 0x00000004U
#define FAILED_READ_ONLY 0x00000008U
@@ -202,6 +201,7 @@
#define FAILED_SYSTEMID 0x00000400U
#define FAILED_LOCK_TYPE 0x00000800U
#define FAILED_LOCK_MODE 0x00001000U
#define FAILED_INTERNAL_ERROR 0x00002000U
#define SUCCESS 0x00000000U
#define VGMETADATACOPIES_ALL UINT32_MAX
@@ -696,9 +696,6 @@ int unlink_lv_from_vg(struct logical_volume *lv);
void lv_set_visible(struct logical_volume *lv);
void lv_set_hidden(struct logical_volume *lv);
int get_vgnameids(struct cmd_context *cmd, struct dm_list *vgnameids,
const char *only_this_vgname, int include_internal);
int pv_write(struct cmd_context *cmd, struct physical_volume *pv, int allow_non_orphan);
int move_pv(struct volume_group *vg_from, struct volume_group *vg_to,
const char *pv_name);
@@ -717,24 +714,14 @@ int lv_resize(struct logical_volume *lv,
struct lvresize_params *lp,
struct dm_list *pvh);
/*
* Return a handle to VG metadata.
*/
struct volume_group *vg_read_internal(struct cmd_context *cmd,
const char *vgname, const char *vgid,
uint32_t lockd_state, uint32_t warn_flags,
int enable_repair,
int *mdas_consistent);
struct volume_group *vg_read(struct cmd_context *cmd, const char *vg_name,
const char *vgid, uint32_t read_flags, uint32_t lockd_state);
struct volume_group *vg_read(struct cmd_context *cmd, const char *vg_name, const char *vgid,
uint32_t read_flags, uint32_t lockd_state,
uint32_t *error_flags, struct volume_group **error_vg);
struct volume_group *vg_read_for_update(struct cmd_context *cmd, const char *vg_name,
const char *vgid, uint32_t read_flags, uint32_t lockd_state);
struct volume_group *vg_read_orphans(struct cmd_context *cmd,
uint32_t warn_flags,
const char *orphan_vgname);
/*
* Test validity of a VG handle.
*/
struct volume_group *vg_read_orphans(struct cmd_context *cmd, const char *orphan_vgname);
/* this is historical and being removed, don't use */
uint32_t vg_read_error(struct volume_group *vg_handle);
/* pe_start and pe_end relate to any existing data so that new metadata
@@ -757,7 +744,7 @@ uint32_t pv_list_extents_free(const struct dm_list *pvh);
int validate_new_vg_name(struct cmd_context *cmd, const char *vg_name);
int vg_validate(struct volume_group *vg);
struct volume_group *vg_create(struct cmd_context *cmd, const char *vg_name);
struct volume_group *vg_lock_and_create(struct cmd_context *cmd, const char *vg_name);
struct volume_group *vg_lock_and_create(struct cmd_context *cmd, const char *vg_name, int *exists);
int vg_remove_mdas(struct volume_group *vg);
int vg_remove_check(struct volume_group *vg);
void vg_remove_pvs(struct volume_group *vg);
@@ -1381,4 +1368,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

File diff suppressed because it is too large Load Diff

View File

@@ -168,11 +168,27 @@ struct metadata_area_ops {
#define MDA_CONTENT_REASON(primary_mda) ((primary_mda) ? DEV_IO_MDA_CONTENT : DEV_IO_MDA_EXTRA_CONTENT)
#define MDA_HEADER_REASON(primary_mda) ((primary_mda) ? DEV_IO_MDA_HEADER : DEV_IO_MDA_EXTRA_HEADER)
/*
* Flags describing errors found while reading.
*/
#define BAD_MDA_INTERNAL 0x00000001 /* internal lvm error */
#define BAD_MDA_READ 0x00000002 /* read io failed */
#define BAD_MDA_HEADER 0x00000004 /* general problem with header */
#define BAD_MDA_TEXT 0x00000008 /* general problem with text */
#define BAD_MDA_CHECKSUM 0x00000010
#define BAD_MDA_MAGIC 0x00000020
#define BAD_MDA_VERSION 0x00000040
#define BAD_MDA_START 0x00000080
struct metadata_area {
struct dm_list list;
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 */
};
struct metadata_area *mda_copy(struct dm_pool *mem,
struct metadata_area *mda);
@@ -234,7 +250,7 @@ struct name_list {
struct mda_list {
struct dm_list list;
struct device_area mda;
struct metadata_area *mda;
};
struct peg_list {

View File

@@ -59,6 +59,7 @@ struct physical_volume {
/* This is true whenever the represented PV has a label associated. */
uint64_t is_labelled:1;
uint64_t unused_missing_cleared:1;
/* NB. label_sector is valid whenever is_labelled is true */
uint64_t label_sector;

View File

@@ -84,7 +84,7 @@ static void _free_vg(struct volume_group *vg)
void release_vg(struct volume_group *vg)
{
if (!vg || (vg->fid && vg == vg->fid->fmt->orphan_vg))
if (!vg || is_orphan_vg(vg->name))
return;
release_vg(vg->vg_committed);
@@ -711,9 +711,9 @@ int vgreduce_single(struct cmd_context *cmd, struct volume_group *vg,
vg->extent_count -= pv_pe_count(pv);
/* FIXME: we don't need to vg_read the orphan vg here */
orphan_vg = vg_read_orphans(cmd, 0, vg->fid->fmt->orphan_vg_name);
orphan_vg = vg_read_orphans(cmd, vg->fid->fmt->orphan_vg_name);
if (vg_read_error(orphan_vg))
if (!orphan_vg)
goto bad;
if (!vg_split_mdas(cmd, vg, orphan_vg) || !vg->pv_count) {

View File

@@ -122,11 +122,6 @@ struct volume_group {
struct dm_list removed_pvs;
uint32_t open_mode; /* FIXME: read or write - check lock type? */
/*
* Store result of the last vg_read().
* 0 for success else appropriate FAILURE_* bits set.
*/
uint32_t read_status;
uint32_t mda_copies; /* target number of mdas for this VG */
struct dm_hash_table *hostnames; /* map of creation hostnames */

View File

@@ -31,7 +31,6 @@ static int _fwraid_filtering = 0;
static int _pvmove = 0;
static int _obtain_device_list_from_udev = DEFAULT_OBTAIN_DEVICE_LIST_FROM_UDEV;
static enum dev_ext_e _external_device_info_source = DEV_EXT_NONE;
static int _trust_cache = 0; /* Don't scan when incomplete VGs encountered */
static int _debug_level = 0;
static int _debug_classes_logged = 0;
static int _security_level = SECURITY_LEVEL;
@@ -107,11 +106,6 @@ void init_external_device_info_source(enum dev_ext_e src)
_external_device_info_source = src;
}
void init_trust_cache(int trustcache)
{
_trust_cache = trustcache;
}
void init_security_level(int level)
{
_security_level = level;
@@ -285,11 +279,6 @@ enum dev_ext_e external_device_info_source(void)
return _external_device_info_source;
}
int trust_cache(void)
{
return _trust_cache;
}
int background_polling(void)
{
return _background_polling;

View File

@@ -32,7 +32,6 @@ void init_fwraid_filtering(int level);
void init_pvmove(int level);
void init_external_device_info_source(enum dev_ext_e src);
void init_obtain_device_list_from_udev(int device_list_from_udev);
void init_trust_cache(int trustcache);
void init_debug(int level);
void init_debug_classes_logged(int classes);
void init_cmd_name(int status);
@@ -65,7 +64,6 @@ int fwraid_filtering(void);
int pvmove_mode(void);
int obtain_device_list_from_udev(void);
enum dev_ext_e external_device_info_source(void);
int trust_cache(void);
int verbose_level(void);
int silent_mode(void);
int debug_level(void);

View File

@@ -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;
/*

View File

@@ -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);

View File

@@ -42,6 +42,10 @@ lvconvert - Change logical volume layout
\fB--cachesettings\fP \fIString\fP
.ad b
.br
.ad l
\fB--cachevol\fP \fILV\fP
.ad b
.br
.ad l
\fB-c\fP|\fB--chunksize\fP \fISize\fP[k|UNIT]
.ad b
@@ -567,7 +571,7 @@ Convert LV to a thin LV, using the original LV as an external origin.
.br
-
Attach a cache to an LV, converts the LV to type cache.
Attach a cache pool to an LV, converts the LV to type cache.
.br
.P
\fBlvconvert\fP \fB--type\fP \fBcache\fP \fB--cachepool\fP \fILV\fP \fILV\fP\fI_linear_striped_thinpool_vdo_vdopool_vdopooldata_raid\fP
@@ -629,7 +633,7 @@ Attach a cache to an LV, converts the LV to type cache.
Attach a writecache to an LV, converts the LV to type writecache.
.br
.P
\fBlvconvert\fP \fB--type\fP \fBwritecache\fP \fB--cachepool\fP \fILV\fP \fILV\fP\fI_linear_striped_raid\fP
\fBlvconvert\fP \fB--type\fP \fBwritecache\fP \fB--cachevol\fP \fILV\fP \fILV\fP\fI_linear_striped_raid\fP
.br
.RS 4
.ad l
@@ -641,6 +645,49 @@ Attach a writecache to an LV, converts the LV to type writecache.
.br
-
Attach a cache to an LV, converts the LV to type cache.
.br
.P
\fBlvconvert\fP \fB--type\fP \fBcache\fP \fB--cachevol\fP \fILV\fP \fILV\fP\fI_linear_striped_thinpool_raid\fP
.br
.RS 4
.ad l
[ \fB-H\fP|\fB--cache\fP ]
.ad b
.br
.ad l
[ \fB-Z\fP|\fB--zero\fP \fBy\fP|\fBn\fP ]
.ad b
.br
.ad l
[ \fB-c\fP|\fB--chunksize\fP \fISize\fP[k|UNIT] ]
.ad b
.br
.ad l
[ \fB--cachemetadataformat\fP \fBauto\fP|\fB1\fP|\fB2\fP ]
.ad b
.br
.ad l
[ \fB--cachemode\fP \fBwritethrough\fP|\fBwriteback\fP|\fBpassthrough\fP ]
.ad b
.br
.ad l
[ \fB--cachepolicy\fP \fIString\fP ]
.ad b
.br
.ad l
[ \fB--cachesettings\fP \fIString\fP ]
.ad b
.br
.ad l
[ \fB--poolmetadatasize\fP \fISize\fP[m|UNIT] ]
.ad b
.br
[ COMMON_OPTIONS ]
.RE
.br
-
Convert LV to type thin-pool.
.br
.P
@@ -1047,7 +1094,7 @@ See \fBlvmcache\fP(7) for more information.
.ad l
\fB--cachepool\fP \fILV\fP
.br
The name of a cache pool LV.
The name of a cache pool.
.ad b
.HP
.ad l
@@ -1063,6 +1110,12 @@ See \fBlvmcache\fP(7) for more information.
.ad b
.HP
.ad l
\fB--cachevol\fP \fILV\fP
.br
The name of a cache volume.
.ad b
.HP
.ad l
\fB-c\fP|\fB--chunksize\fP \fISize\fP[k|UNIT]
.br
The size of chunks in a snapshot, cache pool or thin pool.
@@ -1689,7 +1742,7 @@ Convert LV to a thin LV, using the original LV as an external origin
.br
-
Attach a cache to an LV (infers --type cache).
Attach a cache pool to an LV (infers --type cache).
.br
.P
\fBlvconvert\fP \fB-H\fP|\fB--cache\fP \fB--cachepool\fP \fILV\fP \fILV\fP\fI_linear_striped_thinpool_vdo_vdopool_vdopooldata_raid\fP

View File

@@ -309,7 +309,8 @@ numeric suffix.
In the usage section below, when creating a pool and the name is omitted
the new LV pool name is generated with the
"vpool" for vdo-pools for prefix and a unique numeric suffix.
Also pool name can be specified together with \fIVG\fP name i.e.:
Pool name can be specified together with \fIVG\fP name i.e.:
vg00/mythinpool.
.SH USAGE
Create a linear LV.
@@ -1097,7 +1098,7 @@ See \fBlvmcache\fP(7) for more information.
.ad l
\fB--cachepool\fP \fILV\fP
.br
The name of a cache pool LV.
The name of a cache pool.
.ad b
.HP
.ad l

View File

@@ -92,10 +92,6 @@ if information changes between commands.
.ad b
.br
.ad l
[ \fB--trustcache\fP ]
.ad b
.br
.ad l
[ \fB--unbuffered\fP ]
.ad b
.br
@@ -406,12 +402,6 @@ back metadata it believes has changed but hasn't.
.ad b
.HP
.ad l
\fB--trustcache\fP
.br
Avoids certain device scanning during command processing. Do not use.
.ad b
.HP
.ad l
\fB--unbuffered\fP
.br
Produce output immediately without sorting or aligning the columns properly.

View File

@@ -96,10 +96,6 @@ lvs produces formatted output about LVs.
.ad b
.br
.ad l
[ \fB--trustcache\fP ]
.ad b
.br
.ad l
[ \fB--unbuffered\fP ]
.ad b
.br
@@ -427,12 +423,6 @@ back metadata it believes has changed but hasn't.
.ad b
.HP
.ad l
\fB--trustcache\fP
.br
Avoids certain device scanning during command processing. Do not use.
.ad b
.HP
.ad l
\fB--unbuffered\fP
.br
Produce output immediately without sorting or aligning the columns properly.

View File

@@ -16,7 +16,7 @@ others are optional.
Change properties of all PVs.
.br
.P
\fBpvchange\fP
\fBpvchange\fP \fB-a\fP|\fB--all\fP
.RS 4
( \fB-x\fP|\fB--allocatable\fP \fBy\fP|\fBn\fP,
.ad b
@@ -25,10 +25,6 @@ Change properties of all PVs.
\fB-u\fP|\fB--uuid\fP,
.ad b
.br
.ad l
\fB-a\fP|\fB--all\fP,
.ad b
.br
.ad l
\fB--addtag\fP \fITag\fP,
.ad b

View File

@@ -1,26 +1,58 @@
.TH PVCK 8 "LVM TOOLS #VERSION#" "Red Hat, Inc."
.SH NAME
pvck - Check the consistency of physical volume(s)
pvck - Check metadata on physical volumes
.
.SH SYNOPSIS
\fBpvck\fP \fIposition_args\fP
\fBpvck\fP \fIoption_args\fP \fIposition_args\fP
.br
[ \fIoption_args\fP ]
.br
.SH DESCRIPTION
pvck checks the LVM metadata for consistency on PVs.
pvck checks LVM metadata on PVs.
Use the --dump option to extract metadata from PVs for debugging.
With dump, set --pvmetadatacopies 2 to extract metadata from a
second metadata area at the end of the device. Use the --file
option to save the raw metadata to a specified file. (The raw
metadata is not usable with vgcfgbackup and vgcfgrestore.)
.SH USAGE
Check for metadata on a device
.br
.P
\fBpvck\fP \fIPV\fP ...
.br
.RS 4
[ COMMON_OPTIONS ]
.RE
.br
Print metadata from a device
.br
.P
\fBpvck\fP \fB--dump\fP \fIString\fP \fIPV\fP
.br
.RS 4
.ad l
[ \fB--labelsector\fP \fINumber\fP ]
[ \fB-f\fP|\fB--file\fP \fIString\fP ]
.ad b
.br
.ad l
[ \fB--[pv]metadatacopies\fP \fB0\fP|\fB1\fP|\fB2\fP ]
.ad b
.br
[ COMMON_OPTIONS ]
.RE
.br
Common options for command:
.
.RS 4
.ad l
[ \fB--labelsector\fP \fINumber\fP ]
.ad b
.RE
Common options for lvm:
.
.RS 4
@@ -113,6 +145,21 @@ For testing and debugging.
.ad b
.HP
.ad l
\fB--dump\fP \fIString\fP
.br
Dump metadata from a PV. Option values include \fBmetadata\fP
to print or save the current text metadata, \fBmetadata_area\fP
to save the entire text metadata area to a file, \fBmetadata_all\fP
to save the current and any previous complete versions of metadata
to a file, and \fBheaders\fP to print and check LVM headers.
.ad b
.HP
.ad l
\fB-f\fP|\fB--file\fP \fIString\fP
.br
.ad b
.HP
.ad l
\fB-h\fP|\fB--help\fP
.br
Display help text.
@@ -154,6 +201,18 @@ on the command.
.ad b
.HP
.ad l
\fB--[pv]metadatacopies\fP \fB0\fP|\fB1\fP|\fB2\fP
.br
The number of metadata areas to set aside on a PV for storing VG metadata.
When 2, one copy of the VG metadata is stored at the front of the PV
and a second copy is stored at the end.
When 1, one copy of the VG metadata is stored at the front of the PV.
When 0, no copies of the VG metadata are stored on the given PV.
This may be useful in VGs containing many PVs (this places limitations
on the ability to use vgsplit later.)
.ad b
.HP
.ad l
\fB-q\fP|\fB--quiet\fP ...
.br
Suppress output and log messages. Overrides --debug and --verbose.

View File

@@ -92,10 +92,6 @@ pvs produces formatted output about PVs.
.ad b
.br
.ad l
[ \fB--trustcache\fP ]
.ad b
.br
.ad l
[ \fB--unbuffered\fP ]
.ad b
.br
@@ -416,12 +412,6 @@ back metadata it believes has changed but hasn't.
.ad b
.HP
.ad l
\fB--trustcache\fP
.br
Avoids certain device scanning during command processing. Do not use.
.ad b
.HP
.ad l
\fB--unbuffered\fP
.br
Produce output immediately without sorting or aligning the columns properly.

View File

@@ -3,7 +3,7 @@
vgck - Check the consistency of volume group(s)
.
.SH SYNOPSIS
\fBvgck\fP
\fBvgck\fP \fIoption_args\fP \fIposition_args\fP
.br
[ \fIoption_args\fP ]
.br
@@ -12,6 +12,9 @@ vgck - Check the consistency of volume group(s)
.SH DESCRIPTION
vgck checks LVM metadata for consistency.
.SH USAGE
Read and display information about a VG.
.br
.P
\fBvgck\fP
.br
.RS 4
@@ -26,6 +29,21 @@ vgck checks LVM metadata for consistency.
[ \fIVG\fP|\fITag\fP ... ]
.RE
Rewrite VG metadata to correct problems.
.br
.P
\fBvgck\fP \fB--updatemetadata\fP \fIVG\fP
.br
.RS 4
[ COMMON_OPTIONS ]
.RE
.br
Common options for command:
.
.RS 4
.RE
Common options for lvm:
.
.RS 4
@@ -178,6 +196,12 @@ back metadata it believes has changed but hasn't.
.ad b
.HP
.ad l
\fB--updatemetadata\fP
.br
Update VG metadata to correct problems.
.ad b
.HP
.ad l
\fB-v\fP|\fB--verbose\fP ...
.br
Set verbose level. Repeat from 1 to 4 times to increase the detail

View File

@@ -88,10 +88,6 @@ vgs produces formatted output about VGs.
.ad b
.br
.ad l
[ \fB--trustcache\fP ]
.ad b
.br
.ad l
[ \fB--unbuffered\fP ]
.ad b
.br
@@ -403,12 +399,6 @@ back metadata it believes has changed but hasn't.
.ad b
.HP
.ad l
\fB--trustcache\fP
.br
Avoids certain device scanning during command processing. Do not use.
.ad b
.HP
.ad l
\fB--unbuffered\fP
.br
Produce output immediately without sorting or aligning the columns properly.

View File

@@ -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() {
@@ -1421,7 +1421,7 @@ target_at_least() {
fi
local version
version=$(dmsetup targets 2>/dev/null | grep "${1##dm-} " 2>/dev/null)
version=$(dmsetup targets 2>/dev/null | grep "^${1##dm-} " 2>/dev/null)
version=${version##* v}
version_at_least "$version" "${@:2}" || {
@@ -1478,6 +1478,14 @@ have_vdo() {
target_at_least dm-vdo "$@"
}
have_writecache() {
lvm segtypes 2>/dev/null | grep -q writecache$ || {
echo "writecache is not built-in." >&2
return 1
}
target_at_least dm-writecache "$@"
}
have_raid() {
target_at_least dm-raid "$@"

View File

@@ -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

View File

@@ -24,53 +24,48 @@ lvchange -a n $vg/mirror
aux backup_dev "${DEVICES[@]}"
init() {
makeold() {
# reset metadata on all devs to starting condition
aux restore_dev "${DEVICES[@]}"
not check lv_field $vg/resized lv_size "8.00m"
# change the metadata on all devs
lvresize -L 8192K $vg/resized
# reset metadata on just dev1 to the previous version
aux restore_dev "$dev1"
}
init
vgscan 2>&1 | tee cmd.out
grep "Inconsistent metadata found for VG $vg" cmd.out
vgscan 2>&1 | tee cmd.out
not grep "Inconsistent metadata found for VG $vg" cmd.out
check lv_field $vg/resized lv_size "8.00m"
# create old metadata
makeold
# vgdisplay fixes
init
vgdisplay $vg 2>&1 | tee cmd.out
grep "Inconsistent metadata found for VG $vg" cmd.out
vgdisplay $vg 2>&1 | tee cmd.out
not grep "Inconsistent metadata found for VG $vg" cmd.out
check lv_field $vg/resized lv_size "8.00m"
# lvs fixes up
init
lvs $vg 2>&1 | tee cmd.out
grep "Inconsistent metadata found for VG $vg" cmd.out
vgdisplay $vg 2>&1 | tee cmd.out
not grep "Inconsistent metadata found for VG $vg" cmd.out
check lv_field $vg/resized lv_size "8.00m"
# vgs fixes up as well
init
# reports old metadata
vgs $vg 2>&1 | tee cmd.out
grep "Inconsistent metadata found for VG $vg" cmd.out
vgs $vg 2>&1 | tee cmd.out
not grep "Inconsistent metadata found for VG $vg" cmd.out
grep "ignoring metadata" cmd.out
check lv_field $vg/resized lv_size "8.00m"
echo Check auto-repair of failed vgextend - metadata written to original pv but not new pv
# corrects old metadata
lvcreate -l1 -an $vg
# no old report
vgs $vg 2>&1 | tee cmd.out
not grep "ignoring metadata" cmd.out
check lv_field $vg/resized lv_size "8.00m"
echo Check auto-repair of failed vgextend
echo - metadata written to original pv but not new pv
vgremove -f $vg
pvremove -ff "${DEVICES[@]}"
pvcreate "${DEVICES[@]}"
aux backup_dev "$dev2"
vgcreate $SHARED $vg "$dev1"
vgextend $vg "$dev2"
aux restore_dev "$dev2"
vgscan
vgs -o+vg_mda_count $vg
pvs -o+vg_mda_count
should check compare_fields vgs $vg vg_mda_count pvs "$dev2" vg_mda_count
vgremove -ff $vg

View File

@@ -93,6 +93,9 @@ lvconvert --yes --uncache $vg/$lv1
aux enable_dev "$dev2"
# vg was changed while dev was missing
vgextend --restoremissing $vg "$dev2"
# FIXME: temporary workaround
lvcreate -L1 -n $lv5 $vg
lvremove -ff $vg

View File

@@ -24,6 +24,8 @@ aux lvmconf 'allocation/maximise_cling = 0' \
cleanup_() {
vgreduce --removemissing $vg
for d in "$@"; do aux enable_dev "$d"; done
# clear the outdated metadata on enabled devs before we can reuse them
vgck --updatemetadata $vg
for d in "$@"; do vgextend $vg "$d"; done
lvremove -ff $vg/mirror
lvcreate -aey --type mirror -m 1 --ignoremonitoring -l 2 -n mirror $vg "$dev1" "$dev2" "$dev3:0"

View File

@@ -65,6 +65,7 @@ aux disable_dev "$dev2"
lvconvert -y --repair $vg/$lv1
vgreduce --removemissing $vg
aux enable_dev "$dev2"
vgck --updatemetadata $vg
vgextend $vg "$dev2"
lvremove -ff $vg/$lv1
@@ -80,6 +81,7 @@ aux wait_for_sync $vg $lv1
lvconvert -y --repair $vg/$lv1
vgreduce --removemissing $vg
aux enable_dev "$dev2" "$dev3"
vgck --updatemetadata $vg
vgextend $vg "$dev2" "$dev3"
lvremove -ff $vg/$lv1
@@ -96,6 +98,7 @@ aux disable_dev "$dev3"
vgreduce --removemissing -f $vg
lvconvert -y --repair $vg/$lv1
aux enable_dev "$dev3"
vgck --updatemetadata $vg
pvcreate -yff "$dev3"
vgextend $vg "$dev3"
lvremove -ff $vg/$lv1
@@ -114,6 +117,7 @@ aux wait_for_sync $vg $lv1
lvconvert -y --repair $vg/$lv1
vgreduce --removemissing $vg
aux enable_dev "$dev3"
vgck --updatemetadata $vg
vgextend $vg "$dev3"
lvremove -ff $vg/$lv1
@@ -128,6 +132,7 @@ aux disable_dev "$dev4" "$dev5"
lvconvert -y --repair $vg/$lv1
vgreduce --removemissing $vg
aux enable_dev "$dev4" "$dev5"
vgck --updatemetadata $vg
vgextend $vg "$dev4" "$dev5"
lvremove -ff $vg/$lv1
@@ -145,6 +150,7 @@ aux wait_for_sync $vg $lv1
lvconvert -y --repair $vg/$lv1
vgreduce --removemissing $vg
aux enable_dev "$dev4"
vgck --updatemetadata $vg
vgextend $vg "$dev4"
lvremove -ff $vg/$lv1
@@ -163,6 +169,7 @@ aux wait_for_sync $vg $lv1
lvconvert -y --repair $vg/$lv1
vgreduce --removemissing $vg
aux enable_dev "$dev4"
vgck --updatemetadata $vg
vgextend $vg "$dev4"
lvremove -ff $vg/$lv1

View File

@@ -106,17 +106,23 @@ lvconvert -y --repair $vg/mirror
vgreduce --removemissing $vg
aux enable_dev "$dev1"
# clear the outdated dev before we can reuse it
vgck --updatemetadata $vg
vgextend $vg "$dev1"
aux disable_dev "$dev2"
lvconvert -y --repair $vg/mirror
vgreduce --removemissing $vg
aux enable_dev "$dev2"
# clear the outdated dev before we can reuse it
vgck --updatemetadata $vg
vgextend $vg "$dev2"
aux disable_dev "$dev3"
lvconvert -y --repair $vg/mirror
vgreduce --removemissing $vg
aux enable_dev "$dev3"
# clear the outdated dev before we can reuse it
vgck --updatemetadata $vg
vgextend $vg "$dev3"
vgremove -ff $vg

View File

@@ -31,6 +31,10 @@ test -f /proc/mdstat && grep -q raid1 /proc/mdstat || \
aux lvmconf 'devices/md_component_detection = 1'
# This stops lvm from taking advantage of hints which
# will have already excluded md components.
aux lvmconf 'devices/hints = "none"'
# This stops lvm from asking udev if a dev is an md component.
# LVM will ask udev if a dev is an md component, but we don't
# want to rely on that ability in this test.
@@ -61,6 +65,10 @@ check lv_field $vg/$lv1 lv_active "active"
pvs "$mddev"
not pvs "$dev1"
not pvs "$dev2"
pvs > out
not grep "$dev1" out
not grep "$dev2" out
sleep 1
vgchange -an $vg
@@ -72,12 +80,14 @@ sleep 1
mdadm --stop "$mddev"
aux udev_wait
# with md superblock 1.0 this pvs will report duplicates
# for the two md legs since the md device itself is not
# started
pvs 2>&1 |tee out
cat out
grep "prefers device" out
# The md components should still be detected and excluded.
not pvs "$dev1"
not pvs "$dev2"
pvs > out
not grep "$dev1" out
not grep "$dev2" out
pvs -vvvv
# should not activate from the md legs
not vgchange -ay $vg
@@ -104,16 +114,20 @@ not grep "active" out
mdadm --assemble "$mddev" "$dev1" "$dev2"
aux udev_wait
# Now that the md dev is online, pvs can see it and
# ignore the two legs, so there's no duplicate warning
# Now that the md dev is online, pvs can see it
# and check for components even if
# md_component_checks is "start" (which disables
# most default end-of-device scans)
aux lvmconf 'devices/md_component_checks = "start"'
pvs 2>&1 |tee out
cat out
not grep "prefers device" out
not pvs "$dev1"
not pvs "$dev2"
pvs > out
not grep "$dev1" out
not grep "$dev2" out
vgchange -ay $vg 2>&1 |tee out
cat out
not grep "prefers device" out
vgchange -ay $vg
check lv_field $vg/$lv1 lv_active "active"
@@ -124,6 +138,9 @@ vgremove -f $vg
aux cleanup_md_dev
# Put this setting back to the default
aux lvmconf 'devices/md_component_checks = "auto"'
# create 2 disk MD raid0 array
# by default using metadata format 1.0 with data at the end of device
# When a raid0 md array is stopped, the components will not look like
@@ -149,6 +166,10 @@ check lv_field $vg/$lv1 lv_active "active"
pvs "$mddev"
not pvs "$dev1"
not pvs "$dev2"
pvs > out
not grep "$dev1" out
not grep "$dev2" out
sleep 1
vgchange -an $vg
@@ -160,7 +181,14 @@ sleep 1
mdadm --stop "$mddev"
aux udev_wait
pvs 2>&1 |tee pvs.out
# The md components should still be detected and excluded.
not pvs "$dev1"
not pvs "$dev2"
pvs > out
not grep "$dev1" out
not grep "$dev2" out
pvs -vvvv
# should not activate from the md legs
not vgchange -ay $vg
@@ -187,16 +215,19 @@ not grep "active" out
mdadm --assemble "$mddev" "$dev1" "$dev2"
aux udev_wait
# Now that the md dev is online, pvs can see it and
# ignore the two legs, so there's no duplicate warning
# Now that the md dev is online, pvs can see it
# and check for components even if
# md_component_checks is "start" (which disables
# most default end-of-device scans)
aux lvmconf 'devices/md_component_checks = "start"'
pvs 2>&1 |tee out
cat out
not grep "prefers device" out
not pvs "$dev1"
not pvs "$dev2"
pvs > out
not grep "$dev1" out
not grep "$dev2" out
vgchange -ay $vg 2>&1 |tee out
cat out
not grep "prefers device" out
check lv_field $vg/$lv1 lv_active "active"

View File

@@ -43,14 +43,16 @@ pvs "$dev1"
lvcreate -aey -m2 --type mirror -l4 --alloc anywhere --corelog -n $lv1 $vg2
aux disable_dev "$dev3"
pvs 2>&1| tee out
grep "is missing PV" out
lvconvert --yes --repair $vg2/$lv1
aux enable_dev "$dev3"
# here it should fix any reappeared devices
lvs
lvs -a $vg2 -o+devices 2>&1 | tee out
not grep reappeared out
not grep "is missing PV" out
# This removes the first "vg1" using its uuid
vgremove -ff -S vg_uuid=$UUID1

View File

@@ -0,0 +1,80 @@
#!/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
#
# Test corrupted mda_header.version field, which also
# causes the mda_header checksum to be bad.
#
# FIXME: if a VG has only a single PV, this repair
# doesn't work since there's no good PV to get
# metadata from. A more advanced repair capability
# is needed.
#
dd if=/dev/zero of="$dev1" || true
dd if=/dev/zero of="$dev2" || true
dd if=/dev/zero of="$dev3" || true
vgcreate $SHARED $vg "$dev1" "$dev2" "$dev3"
pvs
if [ -e "/usr/bin/xxd" ]; then
# read mda_header which is 4k from start of disk
dd if="$dev1" of=meta1 bs=4k count=1 skip=1
# convert binary to text
xxd meta1 > meta1.txt
# Corrupt mda_header by changing the version field from 0100 to 0200
sed 's/0000010:\ 304e\ 2a3e\ 0100\ 0000\ 0010\ 0000\ 0000\ 0000/0000010:\ 304e\ 2a3e\ 0200\ 0000\ 0010\ 0000\ 0000\ 0000/' meta1.txt > meta1-bad.txt
# convert text to binary
xxd -r meta1-bad.txt > meta1-bad
# write bad mda_header back to disk
dd if=meta1-bad of="$dev1" bs=4k seek=1
# pvs reports bad metadata header
pvs 2>&1 | tee out
grep "bad metadata header" out
fi
pvs "$dev1"
pvs "$dev2"
pvs "$dev3"
# bad metadata in one mda doesn't prevent using
# the VG since other mdas are fine and usable
lvcreate -l1 $vg
vgck --updatemetadata $vg
pvs 2>&1 | tee out
not grep "bad metadata header" out
pvs "$dev1"
pvs "$dev2"
pvs "$dev3"
vgchange -an $vg
vgremove -ff $vg

View File

@@ -0,0 +1,236 @@
#!/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
vgcreate $SHARED $vg "$dev1" "$dev2" "$dev3"
pvs
dd if="$dev1" of=meta1 bs=4k count=2
sed 's/flags =/flagx =/' meta1 > meta1.bad
dd if=meta1.bad of="$dev1"
pvs 2>&1 | tee out
grep "bad metadata text" out
pvs "$dev1"
pvs "$dev2"
pvs "$dev3"
# bad metadata in one mda doesn't prevent using
# the VG since other mdas are fine and usable
lvcreate -l1 $vg
vgck --updatemetadata $vg
pvs 2>&1 | tee out
not grep "bad metadata text" out
pvs "$dev1"
pvs "$dev2"
pvs "$dev3"
vgchange -an $vg
vgremove -ff $vg
#
# Same test as above, but corrupt metadata text
# on two of the three PVs, leaving one good
# copy of the metadata.
#
dd if=/dev/zero of="$dev1" || true
dd if=/dev/zero of="$dev2" || true
dd if=/dev/zero of="$dev3" || true
vgcreate $SHARED $vg "$dev1" "$dev2" "$dev3"
pvs
dd if="$dev1" of=meta1 bs=4k count=2
dd if="$dev2" of=meta2 bs=4k count=2
sed 's/READ/RRRR/' meta1 > meta1.bad
sed 's/seqno =/sss =/' meta2 > meta2.bad
dd if=meta1.bad of="$dev1"
dd if=meta2.bad of="$dev2"
pvs 2>&1 | tee out
grep "bad metadata text" out > out2
grep "$dev1" out2
grep "$dev2" out2
pvs "$dev1"
pvs "$dev2"
pvs "$dev3"
# bad metadata in one mda doesn't prevent using
# the VG since other mdas are fine
lvcreate -l1 $vg
vgck --updatemetadata $vg
pvs 2>&1 | tee out
not grep "bad metadata text" out
pvs "$dev1"
pvs "$dev2"
pvs "$dev3"
vgchange -an $vg
vgremove -ff $vg
#
# Three PVs where two have one mda, and the third
# has two mdas. The first mda is corrupted on all
# thee PVs, but the second mda on the third PV
# makes the VG usable.
#
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"
pvs
dd if="$dev1" of=meta1 bs=4k count=2
dd if="$dev2" of=meta2 bs=4k count=2
dd if="$dev3" of=meta3 bs=4k count=2
sed 's/READ/RRRR/' meta1 > meta1.bad
sed 's/seqno =/sss =/' meta2 > meta2.bad
sed 's/id =/id/' meta3 > meta3.bad
dd if=meta1.bad of="$dev1"
dd if=meta2.bad of="$dev2"
dd if=meta3.bad of="$dev3"
pvs 2>&1 | tee out
grep "bad metadata text" out > out2
grep "$dev1" out2
grep "$dev2" out2
grep "$dev3" out2
pvs "$dev1"
pvs "$dev2"
pvs "$dev3"
# bad metadata in some mdas doesn't prevent using
# the VG if there's a good mda found
lvcreate -l1 $vg
vgck --updatemetadata $vg
pvs 2>&1 | tee out
not grep "bad metadata text" out
pvs "$dev1"
pvs "$dev2"
pvs "$dev3"
vgchange -an $vg
vgremove -ff $vg
#
# Test that vgck --updatemetadata will update old metadata
# and repair bad metadata text at the same time from different
# devices.
#
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"
# Put bad metadata onto dev1
dd if="$dev1" of=meta1 bs=4k count=2
sed 's/READ/RRRR/' meta1 > meta1.bad
dd if=meta1.bad of="$dev1"
pvs 2>&1 | tee out
grep "bad metadata text" out > out2
grep "$dev1" out2
# We can still use the VG with other available
# mdas, skipping the bad mda.
lvcreate -n $lv1 -l1 -an $vg "$dev1"
lvcreate -n $lv2 -l1 -an $vg "$dev1"
# Put old metadata onto dev2 by updating
# the VG while dev2 is disabled.
aux disable_dev "$dev2"
pvs
pvs "$dev1"
not pvs "$dev2"
pvs "$dev3"
lvs $vg/$lv1
lvs $vg/$lv2
lvremove $vg/$lv2
aux enable_dev "$dev2"
# Both old and bad metadata are reported.
pvs 2>&1 | tee out
grep "ignoring metadata seqno" out
grep "bad metadata text" out
pvs "$dev1"
pvs "$dev2"
pvs "$dev3"
lvs $vg/$lv1
not lvs $vg/$lv2
# fixes the bad metadata on dev1, and
# fixes the old metadata on dev2.
vgck --updatemetadata $vg
pvs 2>&1 | tee out
not grep "ignoring metadata seqno" out
not grep "bad metadata text" out
pvs "$dev1"
pvs "$dev2"
pvs "$dev3"
lvs $vg/$lv1
vgchange -an $vg
vgremove -ff $vg

177
test/shell/metadata-old.sh Normal file
View File

@@ -0,0 +1,177 @@
#!/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
#
# Test "old metadata" repair which occurs when the VG is written
# and one of the PVs in the VG does not get written to, and then
# the PV reappears with the old metadata. This can happen if
# a command is killed or crashes after writing new metadata to
# only some of the PVs in the VG, or if a PV is temporarily
# inaccessible while a VG is written.
#
vgcreate $SHARED $vg "$dev1" "$dev2" "$dev3"
#
# Test that vgck --updatemetadata will update old metadata.
#
lvcreate -n $lv1 -l1 -an $vg "$dev1"
lvcreate -n $lv2 -l1 -an $vg "$dev1"
aux disable_dev "$dev2"
pvs
pvs "$dev1"
not pvs "$dev2"
pvs "$dev3"
lvs $vg/$lv1
lvs $vg/$lv2
lvremove $vg/$lv2
aux enable_dev "$dev2"
pvs 2>&1 | tee out
grep "ignoring metadata seqno" out
pvs "$dev1"
pvs "$dev2"
pvs "$dev3"
lvs $vg/$lv1
not lvs $vg/$lv2
# fixes the old metadata on dev1
vgck --updatemetadata $vg
pvs 2>&1 | tee out
not grep "ignoring metadata seqno" out
pvs "$dev1"
pvs "$dev2"
pvs "$dev3"
lvs $vg/$lv1
not lvs $vg/$lv2
#
# Test that any writing command will also update the
# old metadata.
#
lvcreate -n $lv2 -l1 -an $vg "$dev1"
aux disable_dev "$dev2"
pvs
pvs "$dev1"
not pvs "$dev2"
pvs "$dev3"
lvs $vg/$lv1
lvs $vg/$lv2
lvremove $vg/$lv2
aux enable_dev "$dev2"
pvs 2>&1 | tee out
grep "ignoring metadata seqno" out
pvs "$dev1"
pvs "$dev2"
pvs "$dev3"
lvs $vg/$lv1
not lvs $vg/$lv2
# fixes the old metadata on dev1
lvcreate -n $lv3 -l1 -an $vg
pvs 2>&1 | tee out
not grep "ignoring metadata seqno" out
pvs "$dev1"
pvs "$dev2"
pvs "$dev3"
lvs $vg/$lv1
not lvs $vg/$lv2
vgremove -ff $vg
#
# First two PVs with one mda, where both have old metadata.
# Third PV with two mdas, where the first mda has old
# metadata, and the second mda has current metadata.
#
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"
lvcreate -n $lv1 -l1 -an $vg "$dev3"
lvcreate -n $lv2 -l1 -an $vg "$dev3"
# Save the metadata at this point...
dd if="$dev1" of=meta1 bs=4k count=4
dd if="$dev2" of=meta2 bs=4k count=4
dd if="$dev3" of=meta3 bs=4k count=4
# and now change metadata so the saved copies are old
lvcreate -n $lv3 -l1 -an $vg "$dev3"
# Copy the saved metadata back to the three
# devs first mda, leaving the second mda on
# dev3 as the only latest copy of the metadata.
dd if=meta1 of="$dev1"
dd if=meta2 of="$dev2"
dd if=meta3 of="$dev3"
pvs 2>&1 | tee out
grep "ignoring metadata seqno" out
pvs "$dev1"
pvs "$dev2"
pvs "$dev3"
# We still see the three LVs since we are using
# the latest copy of metadata from dev3:mda2
lvs $vg/$lv1
lvs $vg/$lv2
lvs $vg/$lv3
# This command which writes the VG should update
# all of the old copies.
lvcreate -n $lv4 -l1 -an $vg
pvs 2>&1 | tee out
not grep "ignoring metadata seqno" out
pvs "$dev1"
pvs "$dev2"
pvs "$dev3"
lvs $vg/$lv1
lvs $vg/$lv2
lvs $vg/$lv3
lvs $vg/$lv4
vgchange -an $vg
vgremove -ff $vg

View File

@@ -123,8 +123,16 @@ check_and_cleanup_lvs_()
recover_vg_()
{
aux enable_dev "$@"
# clear outdated metadata on PVs so they can be used again
vgck --updatemetadata $vg
pvscan --cache
pvcreate -ff "$@"
# wipefs -a "$@"
vgextend $vg "$@"
check_and_cleanup_lvs_
}

View File

@@ -0,0 +1,86 @@
#!/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
vgcreate $SHARED $vg "$dev1" "$dev2" "$dev3"
lvcreate -n $lv1 -L8M $vg "$dev2"
lvcreate -n $lv2 -L8M $vg "$dev3"
lvcreate -n $lv3 -L8M $vg "$dev2"
lvcreate -n $lv4 -L8M $vg "$dev3"
vgchange -an $vg
pvs
vgs
lvs -a -o+devices
# Fail device that is not used by any LVs.
aux disable_dev "$dev1"
pvs
vgs
lvs -a -o+devices
# Cannot do normal activation of LVs not using failed PV.
lvchange -ay $vg/$lv1
lvchange -ay $vg/$lv2
vgchange -an $vg
# Check that MISSING flag is not set in ondisk metadata.
pvck --dump metadata "$dev2" > meta
not grep MISSING meta
rm meta
pvs
vgs
lvs -a -o+devices
# lvremove is one of the few commands that is allowed to run
# when PVs are missing. The vg_write from this command sets
# the MISSING flag on the PV in the ondisk metadata.
# (this could be changed, the MISSING flag wouldn't need
# to be set in the first place since the PV isn't used.)
lvremove $vg/$lv1
# Check that MISSING flag is set in ondisk metadata.
pvck --dump metadata "$dev2" > meta
grep MISSING meta
rm meta
# with MISSING flag in metadata, restrictions apply
not lvcreate -l1 $vg
aux enable_dev "$dev1"
# No LVs are using the PV with MISSING flag, so no restrictions
# are applied, and the vg_write here clears the MISSING flag on disk.
lvcreate -l1 $vg
# Check that MISSING flag is not set in ondisk metadata.
pvck --dump metadata "$dev2" > meta
not grep MISSING meta
rm meta
pvs
vgs
lvs -a -o+devices
vgchange -an $vg
vgremove -ff $vg

152
test/shell/missing-pv.sh Normal file
View File

@@ -0,0 +1,152 @@
#!/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
vgcreate $SHARED $vg "$dev1" "$dev2" "$dev3"
lvcreate -n $lv1 -L8M --type mirror -m 1 $vg
lvcreate -n $lv2 -L8M --type mirror -m 1 $vg
vgchange -an $vg
pvs
vgs
lvs -a -o+devices
# Fail one leg of each mirror LV.
aux disable_dev "$dev1"
pvs
vgs
lvs -a -o+devices
# Cannot do normal activate of either LV with a failed leg.
not lvchange -ay $vg/$lv1
not lvchange -ay $vg/$lv2
# Can activate with partial option.
lvchange -ay --activationmode partial $vg/$lv1
lvchange -ay --activationmode partial $vg/$lv2
pvs
vgs
lvs -a -o+devices
# Repair lv1 so it no longer uses failed dev.
lvconvert --repair --yes $vg/$lv1
# Check that MISSING flag is set in ondisk metadata,
# it should have been written by the lvconvert since the
# missing PV is still used by lv2.
pvck --dump metadata "$dev2" > meta
grep MISSING meta
rm meta
pvs
vgs
lvs -a -o+devices
# Verify normal activation is possible of lv1 since it's
# not using any failed devs, and partial activation is
# required for lv2 since it's still using the failed dev.
vgchange -an $vg
lvchange -ay $vg/$lv1
not lvchange -ay $vg/$lv2
vgchange -an $vg
aux enable_dev "$dev1"
pvs
vgs
lvs -a -o+devices
# TODO: check that lv2 has partial flag, lv1 does not
# (there's no partial reporting option, only attr p.)
# Check that MISSING flag is still set in ondisk
# metadata since the previously missing dev is still
# used by lv2.
pvck --dump metadata "$dev2" > meta
grep MISSING meta
rm meta
# The missing pv restrictions still apply even after
# the dev has reappeared since it has the MISSING flag.
not lvchange -ay $vg/$lv2
not lvcreate -l1 $vg
# Update old metadata on the previously missing PV.
# This should not clear the MISSING flag because the
# previously missing PV is still used by lv2.
# This would be done by any command that writes
# metadata, e.g. lvcreate, but since we are in a
# state with a missing pv, most commands that write
# metadata are restricted, so use a command that
# explicitly writes/fixes metadata.
vgck --updatemetadata $vg
pvs
vgs
lvs -a -o+devices
# Check that MISSING flag is still set in ondisk
# metadata since the previously missing dev is still
# used by lv2.
pvck --dump metadata "$dev2" > meta
grep MISSING meta
rm meta
# The missing pv restrictions still apply since it
# has the MISSING flag.
not lvchange -ay $vg/$lv2
not lvcreate -l1 $vg
lvchange -ay --activationmode partial $vg/$lv2
# After repair, no more LVs will be using the previously
# missing PV.
lvconvert --repair --yes $vg/$lv2
pvs
vgs
lvs -a -o+devices
vgchange -an $vg
# The next write of the metadata will clear the MISSING
# flag in ondisk metadata because the previously missing
# PV is no longer used by any LVs.
# Run a command to write ondisk metadata, which should clear
# the MISSING flag, could also use vgck --updatemetadata vg.
lvcreate -l1 $vg
# Check that the MISSING flag is no longer set
# in the ondisk metadata.
pvck --dump metadata "$dev2" > meta
not grep MISSING meta
rm meta
pvs
vgs
lvs -a -o+devices
vgchange -an $vg
vgremove -ff $vg

66
test/shell/outdated-pv.sh Normal file
View File

@@ -0,0 +1,66 @@
#!/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
#
# Test handling of "outdated PV" which occurs when a PV goes missing
# from a VG, and while it's missing the PV is removed from the VG.
# Then the PV reappears with the old VG metadata that shows it is a
# member. That outdated metadata needs to be cleared.
#
vgcreate $SHARED $vg "$dev1" "$dev2" "$dev3"
lvcreate -n $lv1 -l1 -an $vg "$dev1"
lvcreate -n $lv2 -l1 -an $vg "$dev1"
aux disable_dev "$dev2"
vgreduce --removemissing $vg
pvs
aux enable_dev "$dev2"
pvs 2>&1 | tee out
grep "outdated" out
not pvs "$dev2"
# The VG can still be used with the outdated PV around
lvcreate -n $lv3 -l1 $vg
lvchange -ay $vg
lvs $vg
lvchange -an $vg
# Clears the outdated PV
vgck --updatemetadata $vg
pvs 2>&1 | tee out
not grep "outdated" out
# The PV is no longer in the VG
pvs "$dev2" | tee out
not grep "$vg" out
# The cleared PV can be added back to the VG
vgextend $vg "$dev2"
pvs "$dev2" | tee out
grep "$vg" out
vgremove -ff $vg

View File

@@ -39,7 +39,6 @@ check pv_field "$dev2" pv_in_use "used"
# disable $dev2 and dev1 with 0 MDAs remains, but still
# marked as used, so pvcreate/vgcreate/pvremove should fail
aux disable_dev "$dev2"
pvscan --cache
check pv_field "$dev1" pv_in_use "used"
not pvcreate "$dev1" 2>err
@@ -71,20 +70,14 @@ vgcreate $vg1 "$dev1" "$dev2"
# disable $dev1, then repair the VG - $dev1 is removed from VG
aux disable_dev "$dev1"
vgreduce --removemissing $vg1
# now, enable $dev1, automatic repair will happen on pvs call
# (or any other lvm command that does vg_read with repair inside)
aux enable_dev "$dev1"
# FIXME: once persistent cache does not cause races with timestamps
# causing LVM tools to not see the VG inconsistency and once
# VG repair is always done, delete this line which removes
# persistent .cache as a workaround
rm -f "$TESTDIR/etc/.cache"
# now, enable $dev1 and clear the old metadata from it
aux enable_dev "$dev1"
vgck --updatemetadata $vg1
vgck $vg1
# check $dev1 does not contain the PV_EXT_FLAG anymore - it
# should be removed as part of the repaid during vg_read since
# $dev1 is not part of $vg1 anymore
# check $dev1 does not contain the PV_EXT_FLAG anymore
check pv_field "$dev1" pv_in_use ""
#############################################
@@ -105,7 +98,6 @@ check pv_field "$dev2" pv_in_use "used"
pvchange --metadataignore y "$dev1"
aux disable_dev "$dev2"
pvscan --cache
check pv_field "$dev1" pv_in_use "used"
not pvcreate "$dev1" 2>err
@@ -136,20 +128,14 @@ vgcreate $vg1 "$dev1" "$dev2"
# disable $dev1, then repair the VG - $dev1 is removed from VG
aux disable_dev "$dev1"
vgreduce --removemissing $vg1
# now, enable $dev1, automatic repair will happen on pvs call
# (or any other lvm command that does vg_read with repair inside)
aux enable_dev "$dev1"
# FIXME: once persistent cache does not cause races with timestamps
# causing LVM tools to not see the VG inconsistency and once
# VG repair is always done, delete this line which removes
# persistent .cache as a workaround
rm -f "$TESTDIR/etc/.cache"
# now, enable $dev1 and clear the old metadata from it
aux enable_dev "$dev1"
vgck --updatemetadata $vg1
vgck $vg1
# check $dev1 does not contain the PV_EXT_FLAG anymore - it
# should be removed as part of the repaid during vg_read since
# $dev1 is not part of $vg1 anymore
# check $dev1 does not contain the PV_EXT_FLAG anymore
check pv_field "$dev1" pv_in_use ""
###########################

119
test/shell/pvck-dump.sh Normal file
View File

@@ -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

View File

@@ -134,6 +134,8 @@ not ls "$RUNDIR/lvm/pvs_online/$PVID3"
# pvscan cache ignores pv in a foreign vg
if [ -e "/etc/machine-id" ]; then
aux lvmconf "global/system_id_source = machineid"
_clear_online_files
@@ -168,6 +170,8 @@ cat tmp
grep $lv2 tmp
check lv_field $vg2/$lv2 lv_active "" --foreign
fi
# Test the case where pvscan --cache -aay (with no devs)
# gets the final PV to complete the VG, where that final PV

View File

@@ -15,47 +15,59 @@ SKIP_WITH_LVMPOLLD=1
. lib/inittest
check_() {
local cache=""
# vgscan needs --cache option for direct scan if lvmetad is used
test -e LOCAL_LVMETAD && cache="--cache"
vgscan $cache 2>&1 | tee vgscan.out
"$@" grep "Inconsistent metadata found for VG $vg" vgscan.out
}
aux prepare_vg 3
lvcreate -an -Zn --type mirror -m 1 -l 1 -n mirror $vg
#lvchange -a n $vg
# try orphaning a missing PV (bz45867)
aux disable_dev "$dev1"
vgreduce --removemissing --force $vg
aux enable_dev "$dev1"
check_
test -e LOCAL_LVMETAD && pvcreate -f "$dev1"
check_ not
vgscan 2>&1 | tee vgscan.out
grep "Inconsistent metadata found for VG $vg" vgscan.out
# erase outdated dev1
vgck --updatemetadata $vg
vgscan 2>&1 | tee vgscan.out
not grep "Inconsistent metadata found for VG $vg" vgscan.out
# try to just change metadata; we expect the new version (with MISSING_PV set
# on the reappeared volume) to be written out to the previously missing PV
vgextend $vg "$dev1"
lvcreate -l 1 -n boo -a n --zero n $vg
aux disable_dev "$dev1"
lvremove $vg/mirror
aux enable_dev "$dev1"
check_
test -e LOCAL_LVMETAD && lvremove $vg/boo # FIXME trigger a write :-(
check_ not
aux disable_dev "$dev1"
lvremove $vg/mirror
aux enable_dev "$dev1"
vgscan 2>&1 | tee vgscan.out
grep "Inconsistent metadata found for VG $vg" vgscan.out
# write the vg to update the metadata on dev1
vgck --updatemetadata $vg
vgscan 2>&1 | tee vgscan.out
not grep "Inconsistent metadata found for VG $vg" vgscan.out
aux disable_dev "$dev1"
vgreduce --removemissing --force $vg
aux enable_dev "$dev1"
vgscan 2>&1 | tee out
grep 'Removing PV' out
vgs 2>&1 | tee out
not grep 'Removing PV' out
vgscan 2>&1 | tee vgscan.out
grep "Inconsistent metadata found for VG $vg" vgscan.out
# erase outdated dev1
vgck --updatemetadata $vg
vgscan 2>&1 | tee vgscan.out
not grep "Inconsistent metadata found for VG $vg" vgscan.out
vgremove -ff $vg

View File

@@ -24,11 +24,11 @@ dd if=/dev/urandom bs=512 seek=2 count=32 of="$dev2"
vgscan 2>&1 | tee vgscan.out || true
grep "Failed" vgscan.out
grep "checksum" vgscan.out
dd if=/dev/urandom bs=512 seek=2 count=32 of="$dev2"
vgck $vg 2>&1 | tee vgck.out || true
grep Incorrect vgck.out
grep "checksum" vgck.out
vgremove -ff $vg

View File

@@ -215,8 +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, and \\fBmetadata_area\\fP\n"
"to extract the entire text metadata area.\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"
@@ -711,9 +713,8 @@ arg(trackchanges_ARG, '\0', "trackchanges", 0, 0, 0,
"merging the split image (see --mergemirrors) or permanently splitting\n"
"the image (see --splitmirrors with --name.\n")
/* TODO: hide this? */
arg(trustcache_ARG, '\0', "trustcache", 0, 0, 0,
"Avoids certain device scanning during command processing. Do not use.\n")
"No longer used.\n")
arg(type_ARG, '\0', "type", segtype_VAL, 0, 0,
"The LV type, also known as \"segment type\" or \"segtype\".\n"
@@ -1391,6 +1392,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"

View File

@@ -198,7 +198,7 @@ OO_REPORT: --aligned, --all, --binary, --configreport ConfigReport, --foreign,
--nameprefixes, --noheadings, --nosuffix,
--options String, --readonly, --reportformat ReportFmt, --rows,
--select String, --separator String, --shared, --sort String,
--trustcache, --unbuffered, --units Units, --unquoted
--unbuffered, --units Units, --unquoted
#
# options for config, dumpconfig, lvmconfig
@@ -1384,7 +1384,7 @@ DESC: Resize a pool metadata SubLV by a specified size.
lvs
OO: --history, --segments, OO_REPORT
OP: VG|LV|Tag ...
IO: --partial, --ignoreskippedcluster
IO: --partial, --ignoreskippedcluster, --trustcache
ID: lvs_general
---
@@ -1404,7 +1404,7 @@ OO_PVCHANGE: --autobackup Bool, --force, --reportformat ReportFmt, --uuid
OO_PVCHANGE_META: --allocatable Bool, --addtag Tag, --deltag Tag,
--uuid, --metadataignore Bool
pvchange OO_PVCHANGE_META --all
pvchange --all OO_PVCHANGE_META
OO: OO_PVCHANGE
IO: --ignoreskippedcluster
ID: pvchange_properties_all
@@ -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
---
@@ -1486,7 +1486,7 @@ ID: pvremove_general
pvs
OO: --segments, OO_REPORT
OP: PV|Tag ...
IO: --partial, --ignoreskippedcluster
IO: --partial, --ignoreskippedcluster, --trustcache
ID: pvs_general
---
@@ -1624,6 +1624,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.
---
@@ -1747,7 +1752,7 @@ DESC: Rename a VG by specifying the VG UUID.
vgs
OO: OO_REPORT
OP: VG|Tag ...
IO: --partial, --ignoreskippedcluster
IO: --partial, --ignoreskippedcluster, --trustcache
ID: vgs_general
---
@@ -1802,7 +1807,7 @@ ID: devtypes_general
fullreport
OO: OO_REPORT
OP: VG ...
IO: --partial, --ignoreskippedcluster
IO: --partial, --ignoreskippedcluster, --trustcache
ID: fullreport_general
lastlog

View File

@@ -1070,7 +1070,7 @@ static void _add_required_opt_line(struct cmd_context *cmdtool, struct command *
* (This is the special case of vgchange/lvchange where one
* optional option is required, and others are then optional.)
* The set of options from OO_FOO are saved in required_opt_args,
* and flag CMD_FLAG_ONE_REQUIRED_OPT is set on the cmd indicating
* and flag CMD_FLAG_ANY_REQUIRED_OPT is set on the cmd indicating
* this special case.
*/
@@ -1104,6 +1104,7 @@ static void _add_required_line(struct cmd_context *cmdtool, struct command *cmd,
int i;
int takes_arg;
int prev_was_opt = 0, prev_was_pos = 0;
int orig_ro_count = 0;
/* argv[0] is command name */
@@ -1128,10 +1129,20 @@ static void _add_required_line(struct cmd_context *cmdtool, struct command *cmd,
prev_was_pos = 1;
} else if (!strncmp(argv[i], "OO_", 3)) {
/* one required_opt_arg is required, special case lv/vgchange */
cmd->cmd_flags |= CMD_FLAG_ONE_REQUIRED_OPT;
/*
* the first ro_count entries in required_opt_arg required,
* after which one or more of the next any_ro_count entries
* in required_opt_arg are required. required_opt_arg
* has a total of ro_count+any_ro_count entries.
*/
cmd->cmd_flags |= CMD_FLAG_ANY_REQUIRED_OPT;
orig_ro_count = cmd->ro_count;
_include_required_opt_args(cmdtool, cmd, argv[i]);
cmd->any_ro_count = cmd->ro_count - orig_ro_count;
cmd->ro_count = orig_ro_count;
} else if (prev_was_pos) {
/* set property for previous required_pos_arg */
_update_prev_pos_arg(cmd, argv[i], REQUIRED);
@@ -1281,7 +1292,7 @@ void factor_common_options(void)
if (strcmp(cmd->name, command_names[cn].name))
continue;
if (cmd->ro_count)
if (cmd->ro_count || cmd->any_ro_count)
command_names[cn].variant_has_ro = 1;
if (cmd->rp_count)
command_names[cn].variant_has_rp = 1;
@@ -1290,7 +1301,7 @@ void factor_common_options(void)
if (cmd->op_count)
command_names[cn].variant_has_op = 1;
for (ro = 0; ro < cmd->ro_count; ro++) {
for (ro = 0; ro < cmd->ro_count + cmd->any_ro_count; ro++) {
command_names[cn].all_options[cmd->required_opt_args[ro].opt] = 1;
if ((cmd->required_opt_args[ro].opt == size_ARG) && !strncmp(cmd->name, "lv", 2))
@@ -1773,7 +1784,7 @@ static void _print_usage_def(struct command *cmd, int opt_enum, struct arg_def *
void print_usage(struct command *cmd, int longhelp, int desc_first)
{
struct command_name *cname = _find_command_name(cmd->name);
int onereq = (cmd->cmd_flags & CMD_FLAG_ONE_REQUIRED_OPT) ? 1 : 0;
int any_req = (cmd->cmd_flags & CMD_FLAG_ANY_REQUIRED_OPT) ? 1 : 0;
int include_extents = 0;
int ro, rp, oo, op, opt_enum, first;
@@ -1788,20 +1799,31 @@ void print_usage(struct command *cmd, int longhelp, int desc_first)
printf(" %s", cmd->name);
if (onereq && cmd->ro_count) {
if (any_req) {
for (ro = 0; ro < cmd->ro_count; ro++) {
opt_enum = cmd->required_opt_args[ro].opt;
if (opt_names[opt_enum].short_opt)
printf(" -%c|%s", opt_names[opt_enum].short_opt, opt_names[opt_enum].long_opt);
else
printf(" %s", opt_names[opt_enum].long_opt);
if (cmd->required_opt_args[ro].def.val_bits) {
printf(" ");
_print_usage_def(cmd, opt_enum, &cmd->required_opt_args[ro].def);
}
}
/* one required option in a set */
first = 1;
/* options with short and long */
for (ro = 0; ro < cmd->ro_count; ro++) {
for (ro = cmd->ro_count; ro < cmd->ro_count + cmd->any_ro_count; ro++) {
opt_enum = cmd->required_opt_args[ro].opt;
if (!opt_names[opt_enum].short_opt)
continue;
if ((opt_enum == size_ARG) && command_has_alternate_extents(cmd->name))
include_extents = 1;
if (first)
printf("\n\t(");
else
@@ -1817,7 +1839,7 @@ void print_usage(struct command *cmd, int longhelp, int desc_first)
}
/* options with only long */
for (ro = 0; ro < cmd->ro_count; ro++) {
for (ro = cmd->ro_count; ro < cmd->ro_count + cmd->any_ro_count; ro++) {
opt_enum = cmd->required_opt_args[ro].opt;
if (opt_names[opt_enum].short_opt)
@@ -1843,7 +1865,7 @@ void print_usage(struct command *cmd, int longhelp, int desc_first)
printf(" )\n");
}
if (!onereq && cmd->ro_count) {
if (!any_req && cmd->ro_count) {
for (ro = 0; ro < cmd->ro_count; ro++) {
opt_enum = cmd->required_opt_args[ro].opt;
@@ -1863,7 +1885,7 @@ void print_usage(struct command *cmd, int longhelp, int desc_first)
}
if (cmd->rp_count) {
if (onereq)
if (any_req)
printf("\t");
for (rp = 0; rp < cmd->rp_count; rp++) {
if (cmd->required_pos_args[rp].def.val_bits) {
@@ -2403,7 +2425,7 @@ static const char *_man_long_opt_name(const char *cmdname, int opt_enum)
static void _print_man_usage(char *lvmname, struct command *cmd)
{
struct command_name *cname;
int onereq = (cmd->cmd_flags & CMD_FLAG_ONE_REQUIRED_OPT) ? 1 : 0;
int any_req = (cmd->cmd_flags & CMD_FLAG_ANY_REQUIRED_OPT) ? 1 : 0;
int sep, ro, rp, oo, op, opt_enum;
int need_ro_indent_end = 0;
int include_extents = 0;
@@ -2413,9 +2435,44 @@ static void _print_man_usage(char *lvmname, struct command *cmd)
printf("\\fB%s\\fP", lvmname);
if (!onereq)
if (!any_req)
goto ro_normal;
/*
* required options that follow command name, all required
*/
if (cmd->ro_count) {
sep = 0;
for (ro = 0; ro < cmd->ro_count; ro++) {
/* avoid long line wrapping */
if ((cmd->ro_count > 2) && (sep == 2)) {
printf("\n.RS 5\n");
need_ro_indent_end = 1;
}
opt_enum = cmd->required_opt_args[ro].opt;
if ((opt_enum == size_ARG) && command_has_alternate_extents(cmd->name))
include_extents = 1;
if (opt_names[opt_enum].short_opt) {
printf(" \\fB-%c\\fP|\\fB%s\\fP",
opt_names[opt_enum].short_opt,
_man_long_opt_name(cmd->name, opt_enum));
} else
printf(" \\fB%s\\fP", opt_names[cmd->required_opt_args[ro].opt].long_opt);
if (cmd->required_opt_args[ro].def.val_bits) {
printf(" ");
_print_def_man(cname, opt_enum, &cmd->required_opt_args[ro].def, 1);
}
sep++;
}
}
/*
* one required option in a set, print as:
* ( -a|--a,
@@ -2427,7 +2484,7 @@ static void _print_man_usage(char *lvmname, struct command *cmd)
* and the second loop prints those without short opts.
*/
if (cmd->ro_count) {
if (cmd->any_ro_count) {
printf("\n");
printf(".RS 4\n");
printf("(");
@@ -2435,7 +2492,7 @@ static void _print_man_usage(char *lvmname, struct command *cmd)
sep = 0;
/* print required options with a short opt */
for (ro = 0; ro < cmd->ro_count; ro++) {
for (ro = cmd->ro_count; ro < cmd->ro_count + cmd->any_ro_count; ro++) {
opt_enum = cmd->required_opt_args[ro].opt;
if (!opt_names[opt_enum].short_opt)
@@ -2467,7 +2524,7 @@ static void _print_man_usage(char *lvmname, struct command *cmd)
}
/* print required options without a short opt */
for (ro = 0; ro < cmd->ro_count; ro++) {
for (ro = cmd->ro_count; ro < cmd->ro_count + cmd->any_ro_count; ro++) {
opt_enum = cmd->required_opt_args[ro].opt;
if (opt_names[opt_enum].short_opt)
@@ -2497,7 +2554,7 @@ static void _print_man_usage(char *lvmname, struct command *cmd)
printf(".RE\n");
}
/* print required position args on a new line after the onereq set */
/* print required position args on a new line after the any_req set */
if (cmd->rp_count) {
printf(".RS 4\n");
for (rp = 0; rp < cmd->rp_count; rp++) {

View File

@@ -164,8 +164,16 @@ struct cmd_rule {
/*
* one or more from required_opt_args is required,
* then the rest are optional.
*
* CMD_FLAG_ANY_REQUIRED_OPT: for lvchange/vgchange special case.
* The first ro_count entries of required_opt_args must be met
* (ro_count may be 0.) After this, one or more options must be
* set from the remaining required_opt_args. So, the first
* ro_count options in required_opt_args must match, and after
* that one or more of the remaining options in required_opt_args
* must match.
*/
#define CMD_FLAG_ONE_REQUIRED_OPT 1 /* lvchange/vgchage require one item from required_opt_args */
#define CMD_FLAG_ANY_REQUIRED_OPT 1
#define CMD_FLAG_SECONDARY_SYNTAX 2 /* allows syntax variants to be suppressed in certain output */
#define CMD_FLAG_PREVIOUS_SYNTAX 4 /* allows syntax variants to not be advertised in output */
#define CMD_FLAG_PARSE_ERROR 8 /* error parsing command-lines.in def */
@@ -202,6 +210,8 @@ struct command {
struct cmd_rule rules[CMD_MAX_RULES];
int any_ro_count;
int ro_count;
int oo_count;
int rp_count;

View File

@@ -177,7 +177,7 @@ xx(vgchange,
xx(vgck,
"Check the consistency of volume group(s)",
ALL_VGS_IS_DEFAULT | LOCKD_VG_SH | ALLOW_HINTS)
ALL_VGS_IS_DEFAULT | LOCKD_VG_SH)
xx(vgconvert,
"Change volume group metadata format",

View File

@@ -1084,7 +1084,7 @@ static int _lvchange_properties_single(struct cmd_context *cmd,
*/
/* First group of options which allow for one metadata commit/update for the whole group */
for (i = 0; i < cmd->command->ro_count; i++) {
for (i = 0; i < cmd->command->ro_count + cmd->command->any_ro_count; i++) {
opt_enum = cmd->command->required_opt_args[i].opt;
if (!arg_is_set(cmd, opt_enum))
@@ -1206,7 +1206,7 @@ static int _lvchange_properties_single(struct cmd_context *cmd,
return_ECMD_FAILED;
/* Second group of options which need per option metadata commit+reload(s) */
for (i = 0; i < cmd->command->ro_count; i++) {
for (i = 0; i < cmd->command->ro_count + cmd->command->any_ro_count; i++) {
opt_enum = cmd->command->required_opt_args[i].opt;
if (!arg_is_set(cmd, opt_enum))

View File

@@ -1192,7 +1192,7 @@ static void _set_valid_args_for_command_name(int ci)
if (strcmp(commands[i].name, command_names[ci].name))
continue;
for (ro = 0; ro < commands[i].ro_count; ro++) {
for (ro = 0; ro < (commands[i].ro_count + commands[i].any_ro_count); ro++) {
opt_enum = commands[i].required_opt_args[ro].opt;
all_args[opt_enum] = 1;
@@ -1533,7 +1533,7 @@ static int _command_required_pos_matches(struct cmd_context *cmd, int ci, int rp
* For each command[i], check how many required opt/pos args cmd matches.
* Save the command[i] that matches the most.
*
* commands[i].cmd_flags & CMD_FLAG_ONE_REQUIRED_OPT means
* commands[i].cmd_flags & CMD_FLAG_ANY_REQUIRED_OPT means
* any one item from commands[i].required_opt_args needs to be
* set to match.
*
@@ -1551,7 +1551,7 @@ static struct command *_find_command(struct cmd_context *cmd, const char *path,
const char *name;
char opts_msg[MAX_OPTS_MSG];
char check_opts_msg[MAX_OPTS_MSG];
int match_required, match_ro, match_rp, match_type, match_unused, mismatch_required;
int match_required, match_ro, match_rp, match_any_ro, match_type, match_unused, mismatch_required;
int best_i = 0, best_required = 0, best_type = 0, best_unused = 0;
int close_i = 0, close_ro = 0, close_type = 0;
int only_i = 0;
@@ -1600,6 +1600,7 @@ static struct command *_find_command(struct cmd_context *cmd, const char *path,
match_required = 0; /* required parameters that match */
match_ro = 0; /* required opt_args that match */
match_rp = 0; /* required pos_args that match */
match_any_ro = 0;
match_type = 0; /* type arg matches */
match_unused = 0; /* options set that are not accepted by command */
mismatch_required = 0; /* required parameters that do not match */
@@ -1628,20 +1629,19 @@ static struct command *_find_command(struct cmd_context *cmd, const char *path,
}
}
/*
* Special case where missing required_opt_arg's does not matter
* if one required_opt_arg did match.
*/
if (commands[i].cmd_flags & CMD_FLAG_ONE_REQUIRED_OPT) {
if (match_ro) {
/* one or more of the required_opt_args is used */
mismatch_required = 0;
} else {
/* not even one of the required_opt_args is used */
mismatch_required = 1;
for (ro = commands[i].ro_count; ro < commands[i].ro_count + commands[i].any_ro_count; ro++) {
if (_command_required_opt_matches(cmd, i, ro)) {
/* log_warn("match %d any ro opt %d", i, commands[i].required_opt_args[ro].opt); */
match_any_ro++;
}
}
if ((commands[i].cmd_flags & CMD_FLAG_ANY_REQUIRED_OPT) && !match_any_ro) {
/* not even one of the any ro is used */
/* log_warn("match %d not one from any", i); */
mismatch_required = 1;
}
/* match required_pos_args */
for (rp = 0; rp < commands[i].rp_count; rp++) {
@@ -1697,7 +1697,7 @@ static struct command *_find_command(struct cmd_context *cmd, const char *path,
accepted = 0;
/* NB in some cases required_opt_args are optional */
for (j = 0; j < commands[i].ro_count; j++) {
for (j = 0; j < commands[i].ro_count + commands[i].any_ro_count; j++) {
if (commands[i].required_opt_args[j].opt == opt_enum) {
accepted = 1;
break;
@@ -2396,17 +2396,6 @@ static int _get_current_settings(struct cmd_context *cmd)
if (arg_is_set(cmd, binary_ARG))
cmd->report_binary_values_as_numeric = 1;
if (arg_is_set(cmd, trustcache_ARG)) {
if (arg_is_set(cmd, all_ARG)) {
log_error("--trustcache is incompatible with --all");
return EINVALID_CMD_LINE;
}
init_trust_cache(1);
log_warn("WARNING: Cache file of PVs will be trusted. "
"New devices holding PVs may get ignored.");
} else
init_trust_cache(0);
if (arg_is_set(cmd, noudevsync_ARG))
cmd->current_settings.udev_sync = 0;
@@ -2766,20 +2755,53 @@ static int _init_lvmlockd(struct cmd_context *cmd)
return 1;
}
/*
* md_component_check full: always set use_full_md_check
* which causes filter-md to read the start+end of every
* device on the system (this could be optimized to only
* read the end of PVs.)
*
* md_component_check start: the end of devices will
* not generally be read to check for an md superblock
* (lvm may still scan for end-of-device md superblocks
* if it knows that some exists.)
*
* md_component_check auto: lvm will use some built-in
* heuristics to decide when it should scan the end of
* devices to look for md superblocks, e.g. commands
* like pvcreate that could clobber a component, or if
* udev info is not available and hints are not available.
*/
static void _init_md_checks(struct cmd_context *cmd)
{
/*
* use_full_md_check can also be set later.
* These commands are chosen to always perform
* a full md component check because they initialize
* a new device that could be an md component,
* and they are not run frequently during normal
* operation.
*/
if (!strcmp(cmd->name, "pvcreate") ||
!strcmp(cmd->name, "vgcreate") ||
!strcmp(cmd->name, "vgextend"))
const char *md_check;
cmd->md_component_detection = find_config_tree_bool(cmd, devices_md_component_detection_CFG, NULL);
md_check = find_config_tree_str(cmd, devices_md_component_checks_CFG, NULL);
if (!md_check)
cmd->md_component_checks = "auto";
else if (!strcmp(md_check, "auto") ||
!strcmp(md_check, "start") ||
!strcmp(md_check, "full"))
cmd->md_component_checks = md_check;
else {
log_warn("Ignoring unknown md_component_checks setting, using auto.");
cmd->md_component_checks = "auto";
}
if (!strcmp(cmd->md_component_checks, "full"))
cmd->use_full_md_check = 1;
else if (!strcmp(cmd->md_component_checks, "auto")) {
/* use_full_md_check can also be set later */
if (!strcmp(cmd->name, "pvcreate") ||
!strcmp(cmd->name, "vgcreate") ||
!strcmp(cmd->name, "vgextend"))
cmd->use_full_md_check = 1;
}
log_debug("Using md_component_checks %s use_full_md_check %d",
cmd->md_component_checks, cmd->use_full_md_check);
}
static int _cmd_no_meta_proc(struct cmd_context *cmd)
@@ -2909,12 +2931,12 @@ int lvm_run_command(struct cmd_context *cmd, int argc, char **argv)
!init_filters(cmd, !refresh_done))
return_ECMD_FAILED;
if (arg_is_set(cmd, readonly_ARG))
cmd->metadata_read_only = 1;
cmd->metadata_read_only = arg_is_set(cmd, readonly_ARG);
if ((cmd->command->command_enum == vgchange_activate_CMD) ||
(cmd->command->command_enum == lvchange_activate_CMD))
cmd->is_activating = 1;
cmd->is_activating = (cmd->command->command_enum == vgchange_activate_CMD) ||
(cmd->command->command_enum == lvchange_activate_CMD);
cmd->wipe_outdated_pvs = 0;
/*
* Now that all configs, profiles and command lines args are available,
@@ -3010,7 +3032,7 @@ int lvm_run_command(struct cmd_context *cmd, int argc, char **argv)
ret = cmd->command->fn(cmd, argc, argv);
lvmlockd_disconnect();
fin_locking();
fin_locking(cmd);
if (!_cmd_no_meta_proc(cmd) && find_config_tree_bool(cmd, global_notify_dbus_CFG, NULL))
lvmnotify_send(cmd);

View File

@@ -148,6 +148,7 @@ int wait_for_single_lv(struct cmd_context *cmd, struct poll_operation_id *id,
struct logical_volume *lv;
int finished = 0;
uint32_t lockd_state = 0;
uint32_t error_flags = 0;
int ret;
if (!parms->wait_before_testing)
@@ -168,12 +169,10 @@ int wait_for_single_lv(struct cmd_context *cmd, struct poll_operation_id *id,
}
/* Locks the (possibly renamed) VG again */
vg = vg_read(cmd, id->vg_name, NULL, READ_FOR_UPDATE, lockd_state);
if (vg_read_error(vg)) {
vg = vg_read(cmd, id->vg_name, NULL, READ_FOR_UPDATE, lockd_state, &error_flags, NULL);
if (!vg) {
/* What more could we do here? */
log_error("ABORTING: Can't reread VG for %s.", id->display_name);
release_vg(vg);
vg = NULL;
log_error("ABORTING: Can't reread VG for %s error flags %x.", id->display_name, error_flags);
ret = 0;
goto out;
}
@@ -395,6 +394,7 @@ static int _report_progress(struct cmd_context *cmd, struct poll_operation_id *i
struct volume_group *vg;
struct logical_volume *lv;
uint32_t lockd_state = 0;
uint32_t error_flags = 0;
int ret;
/*
@@ -407,10 +407,9 @@ static int _report_progress(struct cmd_context *cmd, struct poll_operation_id *i
* change done locally.
*/
vg = vg_read(cmd, id->vg_name, NULL, 0, lockd_state);
if (vg_read_error(vg)) {
release_vg(vg);
log_error("Can't reread VG for %s", id->display_name);
vg = vg_read(cmd, id->vg_name, NULL, 0, lockd_state, &error_flags, NULL);
if (!vg) {
log_error("Can't reread VG for %s error flags %x", id->display_name, error_flags);
ret = 0;
goto out_ret;
}

File diff suppressed because it is too large Load Diff

View File

@@ -189,11 +189,12 @@ static int _printed_clustered_vg_advice = 0;
* Case c covers the other errors returned when reading the VG.
* If *skip is 1, it's OK for the caller to read the list of PVs in the VG.
*/
static int _ignore_vg(struct volume_group *vg, const char *vg_name,
struct dm_list *arg_vgnames, uint32_t read_flags,
int *skip, int *notfound)
static int _ignore_vg(struct cmd_context *cmd,
uint32_t error_flags, struct volume_group *error_vg,
const char *vg_name, struct dm_list *arg_vgnames,
uint32_t read_flags, int *skip, int *notfound)
{
uint32_t read_error = vg_read_error(vg);
uint32_t read_error = error_flags;
*skip = 0;
*notfound = 0;
@@ -203,12 +204,9 @@ static int _ignore_vg(struct volume_group *vg, const char *vg_name,
return 0;
}
if ((read_error & FAILED_INCONSISTENT) && (read_flags & READ_ALLOW_INCONSISTENT))
read_error &= ~FAILED_INCONSISTENT; /* Check for other errors */
if (read_error & FAILED_CLUSTERED) {
if (arg_vgnames && str_list_match_item(arg_vgnames, vg->name)) {
log_error("Cannot access clustered VG %s.", vg->name);
if (arg_vgnames && str_list_match_item(arg_vgnames, vg_name)) {
log_error("Cannot access clustered VG %s.", vg_name);
if (!_printed_clustered_vg_advice) {
_printed_clustered_vg_advice = 1;
log_error("See lvmlockd(8) for changing a clvm/clustered VG to a shared VG.");
@@ -233,10 +231,13 @@ static int _ignore_vg(struct volume_group *vg, const char *vg_name,
* would expect to fail.
*/
if (read_error & FAILED_SYSTEMID) {
if (arg_vgnames && str_list_match_item(arg_vgnames, vg->name)) {
if (arg_vgnames && str_list_match_item(arg_vgnames, vg_name)) {
log_error("Cannot access VG %s with system ID %s with %slocal system ID%s%s.",
vg->name, vg->system_id, vg->cmd->system_id ? "" : "unknown ",
vg->cmd->system_id ? " " : "", vg->cmd->system_id ? vg->cmd->system_id : "");
vg_name,
error_vg ? error_vg->system_id : "unknown ",
cmd->system_id ? "" : "unknown ",
cmd->system_id ? " " : "",
cmd->system_id ? cmd->system_id : "");
return 1;
} else {
read_error &= ~FAILED_SYSTEMID; /* Check for other errors */
@@ -255,10 +256,11 @@ static int _ignore_vg(struct volume_group *vg, const char *vg_name,
* command failed to acquire the necessary lock.)
*/
if (read_error & (FAILED_LOCK_TYPE | FAILED_LOCK_MODE)) {
if (arg_vgnames && str_list_match_item(arg_vgnames, vg->name)) {
if (arg_vgnames && str_list_match_item(arg_vgnames, vg_name)) {
if (read_error & FAILED_LOCK_TYPE)
log_error("Cannot access VG %s with lock type %s that requires lvmlockd.",
vg->name, vg->lock_type);
vg_name,
error_vg ? error_vg->lock_type : "unknown");
/* For FAILED_LOCK_MODE, the error is printed in vg_read. */
return 1;
} else {
@@ -1924,10 +1926,12 @@ static int _process_vgnameid_list(struct cmd_context *cmd, uint32_t read_flags,
log_report_t saved_log_report_state = log_get_report_state();
char uuid[64] __attribute__((aligned(8)));
struct volume_group *vg;
struct volume_group *error_vg = NULL;
struct vgnameid_list *vgnl;
const char *vg_name;
const char *vg_uuid;
uint32_t lockd_state = 0;
uint32_t error_flags = 0;
int whole_selected = 0;
int ret_max = ECMD_PROCESSED;
int ret;
@@ -1977,13 +1981,18 @@ static int _process_vgnameid_list(struct cmd_context *cmd, uint32_t read_flags,
continue;
}
vg = vg_read(cmd, vg_name, vg_uuid, read_flags, lockd_state);
if (_ignore_vg(vg, vg_name, arg_vgnames, read_flags, &skip, &notfound)) {
vg = vg_read(cmd, vg_name, vg_uuid, read_flags, lockd_state, &error_flags, &error_vg);
if (_ignore_vg(cmd, error_flags, error_vg, vg_name, arg_vgnames, read_flags, &skip, &notfound)) {
stack;
ret_max = ECMD_FAILED;
report_log_ret_code(ret_max);
if (error_vg)
unlock_and_release_vg(cmd, error_vg, vg_name);
goto endvg;
}
if (error_vg)
unlock_and_release_vg(cmd, error_vg, vg_name);
if (skip || notfound)
goto endvg;
@@ -2004,8 +2013,7 @@ static int _process_vgnameid_list(struct cmd_context *cmd, uint32_t read_flags,
ret_max = ret;
}
if (!vg_read_error(vg))
unlock_vg(cmd, vg, vg_name);
unlock_vg(cmd, vg, vg_name);
endvg:
release_vg(vg);
if (!lockd_vg(cmd, vg_name, "un", 0, &lockd_state))
@@ -2254,7 +2262,7 @@ int process_each_vg(struct cmd_context *cmd,
*/
log_very_verbose("Obtaining the complete list of VGs to process");
if (!get_vgnameids(cmd, &vgnameids_on_system, NULL, include_internal)) {
if (!lvmcache_get_vgnameids(cmd, &vgnameids_on_system, NULL, include_internal)) {
ret_max = ECMD_FAILED;
goto_out;
}
@@ -3590,11 +3598,13 @@ static int _process_lv_vgnameid_list(struct cmd_context *cmd, uint32_t read_flag
log_report_t saved_log_report_state = log_get_report_state();
char uuid[64] __attribute__((aligned(8)));
struct volume_group *vg;
struct volume_group *error_vg = NULL;
struct vgnameid_list *vgnl;
struct dm_str_list *sl;
struct dm_list *tags_arg;
struct dm_list lvnames;
uint32_t lockd_state = 0;
uint32_t error_flags = 0;
const char *vg_name;
const char *vg_uuid;
const char *vgn;
@@ -3663,13 +3673,18 @@ static int _process_lv_vgnameid_list(struct cmd_context *cmd, uint32_t read_flag
continue;
}
vg = vg_read(cmd, vg_name, vg_uuid, read_flags, lockd_state);
if (_ignore_vg(vg, vg_name, arg_vgnames, read_flags, &skip, &notfound)) {
vg = vg_read(cmd, vg_name, vg_uuid, read_flags, lockd_state, &error_flags, &error_vg);
if (_ignore_vg(cmd, error_flags, error_vg, vg_name, arg_vgnames, read_flags, &skip, &notfound)) {
stack;
ret_max = ECMD_FAILED;
report_log_ret_code(ret_max);
if (error_vg)
unlock_and_release_vg(cmd, error_vg, vg_name);
goto endvg;
}
if (error_vg)
unlock_and_release_vg(cmd, error_vg, vg_name);
if (skip || notfound)
goto endvg;
@@ -3792,7 +3807,7 @@ int process_each_lv(struct cmd_context *cmd,
*/
log_very_verbose("Obtaining the complete list of VGs before processing their LVs");
if (!get_vgnameids(cmd, &vgnameids_on_system, NULL, 0)) {
if (!lvmcache_get_vgnameids(cmd, &vgnameids_on_system, NULL, 0)) {
ret_max = ECMD_FAILED;
goto_out;
}
@@ -4156,12 +4171,16 @@ static int _process_pvs_in_vg(struct cmd_context *cmd,
struct physical_volume *pv;
struct pv_list *pvl;
struct device_id_list *dil;
struct device_list *devl;
struct dm_list outdated_devs;
const char *pv_name;
int process_pv;
int do_report_ret_code = 1;
int ret_max = ECMD_PROCESSED;
int ret = 0;
dm_list_init(&outdated_devs);
log_set_report_object_type(LOG_REPORT_OBJECT_TYPE_PV);
vg_uuid[0] = '\0';
@@ -4247,6 +4266,12 @@ static int _process_pvs_in_vg(struct cmd_context *cmd,
break;
log_set_report_object_name_and_id(NULL, NULL);
}
if (!is_orphan_vg(vg->name))
lvmcache_get_outdated_devs(cmd, vg->name, (const char *)&vg->id, &outdated_devs);
dm_list_iterate_items(devl, &outdated_devs)
_device_list_remove(all_devices, devl->dev);
do_report_ret_code = 0;
out:
if (do_report_ret_code)
@@ -4284,10 +4309,12 @@ static int _process_pvs_in_vgs(struct cmd_context *cmd, uint32_t read_flags,
log_report_t saved_log_report_state = log_get_report_state();
char uuid[64] __attribute__((aligned(8)));
struct volume_group *vg;
struct volume_group *error_vg;
struct vgnameid_list *vgnl;
const char *vg_name;
const char *vg_uuid;
uint32_t lockd_state = 0;
uint32_t error_flags = 0;
int ret_max = ECMD_PROCESSED;
int ret;
int skip;
@@ -4325,8 +4352,8 @@ static int _process_pvs_in_vgs(struct cmd_context *cmd, uint32_t read_flags,
log_debug("Processing PVs in VG %s", vg_name);
vg = vg_read(cmd, vg_name, vg_uuid, read_flags, lockd_state);
if (_ignore_vg(vg, vg_name, NULL, read_flags, &skip, &notfound)) {
vg = vg_read(cmd, vg_name, vg_uuid, read_flags, lockd_state, &error_flags, &error_vg);
if (_ignore_vg(cmd, error_flags, error_vg, vg_name, NULL, read_flags, &skip, &notfound)) {
stack;
ret_max = ECMD_FAILED;
report_log_ret_code(ret_max);
@@ -4338,22 +4365,26 @@ static int _process_pvs_in_vgs(struct cmd_context *cmd, uint32_t read_flags,
goto endvg;
/*
* Don't continue when skip is set, because we need to remove
* vg->pvs entries from devices list.
* Don't call "continue" when skip is set, because we need to remove
* error_vg->pvs entries from devices list.
*/
ret = _process_pvs_in_vg(cmd, vg, all_devices, arg_devices, arg_tags,
ret = _process_pvs_in_vg(cmd, vg ? vg : error_vg, all_devices, arg_devices, arg_tags,
process_all_pvs, process_all_devices, skip,
handle, process_single_pv);
if (ret != ECMD_PROCESSED)
stack;
report_log_ret_code(ret);
if (ret > ret_max)
ret_max = ret;
if (!skip)
unlock_vg(cmd, vg, vg->name);
endvg:
if (error_vg)
unlock_and_release_vg(cmd, error_vg, vg_name);
release_vg(vg);
if (!lockd_vg(cmd, vg_name, "un", 0, &lockd_state))
stack;
@@ -4448,7 +4479,7 @@ int process_each_pv(struct cmd_context *cmd,
if (!(read_flags & PROCESS_SKIP_SCAN))
lvmcache_label_scan(cmd);
if (!get_vgnameids(cmd, &all_vgnameids, only_this_vgname, 1)) {
if (!lvmcache_get_vgnameids(cmd, &all_vgnameids, only_this_vgname, 1)) {
ret_max = ret;
goto_out;
}
@@ -5574,7 +5605,7 @@ do_command:
if (pp->preserve_existing && pp->orphan_vg_name) {
log_debug("Using existing orphan PVs in %s.", pp->orphan_vg_name);
if (!(orphan_vg = vg_read_orphans(cmd, 0, pp->orphan_vg_name))) {
if (!(orphan_vg = vg_read_orphans(cmd, pp->orphan_vg_name))) {
log_error("Cannot read orphans VG %s.", pp->orphan_vg_name);
goto bad;
}

View File

@@ -67,9 +67,12 @@ static int _vg_backup_single(struct cmd_context *cmd, const char *vg_name,
if (!backup_to_file(filename, vg->cmd->cmd_line, vg))
return_ECMD_FAILED;
} else {
if (vg_read_error(vg) == FAILED_INCONSISTENT) {
log_error("No backup taken: specify filename with -f "
"to backup an inconsistent VG");
if (vg_missing_pv_count(vg)) {
log_error("No backup taken: specify filename with -f to backup with missing PVs.");
return ECMD_FAILED;
}
if (vg_has_unknown_segments(vg)) {
log_error("No backup taken: specify filename with -f to backup with unknown segments.");
return ECMD_FAILED;
}
@@ -97,9 +100,17 @@ int vgcfgbackup(struct cmd_context *cmd, int argc, char **argv)
handle->custom_handle = &last_filename;
/*
* Just set so that we can do the check ourselves above and
* report a helpful error message in place of the error message
* that would be generated from vg_read.
*/
cmd->handles_missing_pvs = 1;
cmd->handles_unknown_segments = 1;
init_pvmove(1);
ret = process_each_vg(cmd, argc, argv, NULL, NULL, READ_ALLOW_INCONSISTENT, 0,
ret = process_each_vg(cmd, argc, argv, NULL, NULL, 0, 0,
handle, &_vg_backup_single);
free(last_filename);

View File

@@ -407,8 +407,8 @@ static int _vgchange_metadata_copies(struct cmd_context *cmd,
{
uint32_t mda_copies = arg_uint_value(cmd, vgmetadatacopies_ARG, DEFAULT_VGMETADATACOPIES);
log_warn("vgchange_metadata_copies new %u vg_mda_copies %u D %u",
mda_copies, vg_mda_copies(vg), DEFAULT_VGMETADATACOPIES);
log_debug("vgchange_metadata_copies new %u vg_mda_copies %u D %u",
mda_copies, vg_mda_copies(vg), DEFAULT_VGMETADATACOPIES);
if (mda_copies == vg_mda_copies(vg)) {
if (vg_mda_copies(vg) == VGMETADATACOPIES_UNMANAGED)

View File

@@ -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);
}

View File

@@ -71,7 +71,7 @@ int vgcreate(struct cmd_context *cmd, int argc, char **argv)
* then do the initial label scan which reads all devices and
* populates lvmcache with any VG name it finds. If the VG name
* we want to use exists, then the label scan will find it,
* and the fmt_from_vgname call (used to check if the name exists)
* and the vginfo_from_vgname call (used to check if the name exists)
* will return non-NULL.
*/
@@ -82,7 +82,7 @@ int vgcreate(struct cmd_context *cmd, int argc, char **argv)
lvmcache_label_scan(cmd);
if (lvmcache_fmt_from_vgname(cmd, vp_new.vg_name, NULL, 0)) {
if (lvmcache_vginfo_from_vgname(vp_new.vg_name, NULL)) {
unlock_vg(cmd, NULL, vp_new.vg_name);
log_error("A volume group called %s already exists.", vp_new.vg_name);
return ECMD_FAILED;

View File

@@ -28,16 +28,25 @@ static int _restore_pv(struct volume_group *vg, const char *pv_name)
return 0;
}
if (!(pvl->pv->status & MISSING_PV)) {
log_warn("WARNING: PV %s was not missing in VG %s", pv_name, vg->name);
return 0;
}
if (!pvl->pv->dev) {
log_warn("WARNING: The PV %s is still missing.", pv_name);
return 0;
}
if (pvl->pv->status & MISSING_PV)
goto clear_flag;
/*
* when the PV has no used PE's vg_read clears the MISSING_PV flag
* and sets this so we know.
*/
if (pvl->pv->unused_missing_cleared)
goto clear_flag;
log_warn("WARNING: PV %s was not missing in VG %s", pv_name, vg->name);
return 0;
clear_flag:
pvl->pv->status &= ~MISSING_PV;
return 1;
}

View File

@@ -296,7 +296,7 @@ int vgimportclone(struct cmd_context *cmd, int argc, char **argv)
vgname_count = 1;
}
if (!get_vgnameids(cmd, &vgnameids_on_system, NULL, 0))
if (!lvmcache_get_vgnameids(cmd, &vgnameids_on_system, NULL, 0))
goto_out;
retry_name:

View File

@@ -99,6 +99,8 @@ int vgremove(struct cmd_context *cmd, int argc, char **argv)
clear_hint_file(cmd);
cmd->wipe_outdated_pvs = 1;
cmd->handles_missing_pvs = 1;
ret = process_each_vg(cmd, argc, argv, NULL, NULL,
READ_FOR_UPDATE, 0,

View File

@@ -445,79 +445,6 @@ static int _move_cache(struct volume_group *vg_from,
return 1;
}
/*
* Create or open the destination of the vgsplit operation.
* Returns
* - non-NULL: VG handle w/VG lock held
* - NULL: no VG lock held
*/
static struct volume_group *_vgsplit_to(struct cmd_context *cmd,
const char *vg_name_to,
int *existing_vg)
{
struct volume_group *vg_to = NULL;
log_verbose("Checking for new volume group \"%s\"", vg_name_to);
/*
* First try to create a new VG. If we cannot create it,
* and we get FAILED_EXIST (we will not be holding a lock),
* a VG must already exist with this name. We then try to
* read the existing VG - the vgsplit will be into an existing VG.
*
* Otherwise, if the lock was successful, it must be the case that
* we obtained a WRITE lock and could not find the vgname in the
* system. Thus, the split will be into a new VG.
*/
vg_to = vg_lock_and_create(cmd, vg_name_to);
if (vg_read_error(vg_to) == FAILED_LOCKING) {
log_error("Can't get lock for %s", vg_name_to);
release_vg(vg_to);
return NULL;
}
if (vg_read_error(vg_to) == FAILED_EXIST) {
*existing_vg = 1;
release_vg(vg_to);
vg_to = vg_read_for_update(cmd, vg_name_to, NULL, 0, 0);
if (vg_read_error(vg_to)) {
release_vg(vg_to);
return_NULL;
}
} else if (vg_read_error(vg_to) == SUCCESS) {
*existing_vg = 0;
}
return vg_to;
}
/*
* Open the source of the vgsplit operation.
* Returns
* - non-NULL: VG handle w/VG lock held
* - NULL: no VG lock held
*/
static struct volume_group *_vgsplit_from(struct cmd_context *cmd,
const char *vg_name_from)
{
struct volume_group *vg_from;
log_verbose("Checking for volume group \"%s\"", vg_name_from);
vg_from = vg_read_for_update(cmd, vg_name_from, NULL, 0, 0);
if (vg_read_error(vg_from)) {
release_vg(vg_from);
return NULL;
}
if (vg_is_shared(vg_from)) {
log_error("vgsplit not allowed for lock_type %s", vg_from->lock_type);
unlock_and_release_vg(cmd, vg_from, vg_name_from);
return NULL;
}
return vg_from;
}
/*
* Has the user given an option related to a new vg as the split destination?
*/
@@ -536,11 +463,11 @@ int vgsplit(struct cmd_context *cmd, int argc, char **argv)
struct vgcreate_params vp_def;
const char *vg_name_from, *vg_name_to;
struct volume_group *vg_to = NULL, *vg_from = NULL;
struct lvmcache_vginfo *vginfo_to;
int opt;
int existing_vg = 0;
int r = ECMD_FAILED;
const char *lv_name;
int lock_vg_from_first = 1;
if ((arg_is_set(cmd, name_ARG) + argc) < 3) {
log_error("Existing VG, new VG and either physical volumes "
@@ -576,47 +503,44 @@ int vgsplit(struct cmd_context *cmd, int argc, char **argv)
lvmcache_label_scan(cmd);
if (strcmp(vg_name_to, vg_name_from) < 0)
lock_vg_from_first = 0;
if (!(vginfo_to = lvmcache_vginfo_from_vgname(vg_name_to, NULL))) {
if (!validate_name(vg_name_to)) {
log_error("Invalid vg name %s.", vg_name_to);
return ECMD_FAILED;
}
if (lock_vg_from_first) {
if (!(vg_from = _vgsplit_from(cmd, vg_name_from)))
return_ECMD_FAILED;
/*
* Set metadata format of original VG.
* NOTE: We must set the format before calling vg_lock_and_create()
* since vg_lock_and_create() calls the per-format constructor.
*/
cmd->fmt = vg_from->fid->fmt;
if (!lock_vol(cmd, vg_name_to, LCK_VG_WRITE, NULL)) {
log_error("Failed to lock new VG name %s.", vg_name_to);
return ECMD_FAILED;
}
if (!(vg_to = _vgsplit_to(cmd, vg_name_to, &existing_vg))) {
unlock_and_release_vg(cmd, vg_from, vg_name_from);
return_ECMD_FAILED;
if (!(vg_to = vg_create(cmd, vg_name_to))) {
log_error("Failed to create new VG %s.", vg_name_to);
unlock_vg(cmd, NULL, vg_name_to);
return ECMD_FAILED;
}
} else {
if (!(vg_to = _vgsplit_to(cmd, vg_name_to, &existing_vg)))
return_ECMD_FAILED;
if (!(vg_from = _vgsplit_from(cmd, vg_name_from))) {
unlock_and_release_vg(cmd, vg_to, vg_name_to);
return_ECMD_FAILED;
}
if (cmd->fmt != vg_from->fid->fmt) {
/* In this case we don't know the vg_from->fid->fmt */
log_error("Unable to set new VG metadata type based on "
"source VG format - use -M option.");
goto bad;
if (!(vg_to = vg_read_for_update(cmd, vg_name_to, NULL, 0, 0))) {
log_error("Failed to read VG %s.", vg_name_to);
return ECMD_FAILED;
}
existing_vg = 1;
}
if (!(vg_from = vg_read_for_update(cmd, vg_name_from, NULL, 0, 0))) {
log_error("Failed to read VG %s.", vg_name_to);
unlock_and_release_vg(cmd, vg_to, vg_name_to);
return ECMD_FAILED;
}
cmd->fmt = vg_from->fid->fmt;
if (existing_vg) {
if (_new_vg_option_specified(cmd)) {
log_error("Volume group \"%s\" exists, but new VG "
"option specified", vg_name_to);
log_error("Volume group \"%s\" exists, but new VG option specified", vg_name_to);
goto bad;
}
if (!vgs_are_compatible(cmd, vg_from,vg_to))
if (!vgs_are_compatible(cmd, vg_from, vg_to))
goto_bad;
} else {
if (!vgcreate_params_set_defaults(cmd, &vp_def, vg_from)) {