1
0
mirror of git://sourceware.org/git/lvm2.git synced 2026-01-11 00:32:47 +03:00

Compare commits

..

71 Commits

Author SHA1 Message Date
David Teigland
556889f170 remove static autoactivation
event based autoactivation is now the only method that lvm
provides for autoactivation.

Setting lvm.conf event_activation=0 can still be used to disable
event based autoactivation commands, but doing so will no longer
enable static autoactivation.
2022-01-25 16:40:43 -06:00
David Teigland
8f50c5e79b lvmdevices: fix checks when adding entries
Removes some incorrect and unnecessary checks for other entries
when adding a new devices.  The removed checks and corrections were
mostly redundant with what is already done by device id matching.
Other checking is reworked so the warnings are a bit different.
2022-01-25 15:18:29 -06:00
David Teigland
9e9f02acf0 device_id: fix search for renamed device when the wwid is ignored
When a device has a wwid (from sysfs), but lvm ignores the wwid,
e.g. because it contains an unreliable "QEMU" value, then lvm
falls back to using IDTYPE=devname (the device name) as the
device id.  If the device name changes after reboot, then lvm
automatically searches for the PV on other devices to find the
new device name and correct system.devices.  When searching for
the PV, lvm skips looking at devices that would use other id types,
e.g. if a device would use a wwid and not a devname, then it
skips checking it.  However, it failed to account for the fact
that a device may have wwid that was ignored, in which case it
should be checked.
2022-01-25 10:47:50 -06:00
David Teigland
de7892f0af Revert "pvcreate: overwrite partition header with -f"
This reverts commit d5a950ca67.

This commit did not properly recognize GPT cases.
2022-01-18 12:15:03 -06:00
David Teigland
a972d63c54 lvmdevices check: error exit if update is needed
. error exit means that lvmdevices --update would make a change.

. remove check of PART field from --check because it isn't used.

. unlink searched_devnames file to ensure check|update will search
2022-01-14 14:59:28 -06:00
David Teigland
d5a950ca67 pvcreate: overwrite partition header with -f
$ pvcreate /dev/sdc
  Cannot use /dev/sdc: device is partitioned
$ pvcreate -f /dev/sdc
  Physical volume "/dev/sdc" successfully created.
2022-01-14 13:57:20 -06:00
David Teigland
0c80ea8847 remove unused variable
resulting from commit cb798ee1c1
  "lvmcache: remove lvmcache_update_vg_from_write"
2022-01-13 11:41:39 -06:00
David Teigland
18f451e09e handle duplicate vgids
The approach to duplicate VGIDs has been that it is not possible
or not allowed, so the behavior has been undefined.  The actual
result was unpredictable and/or broken, and generally unhelpful.

Improve this by recognizing the problem, displaying the VGs,
and printing a warning to fix the problem.  Beyond this,
using VGs with duplicate VGIDs remains undefined, but should
work well enough to correct the problem with vgchange -u.

It's possible to create this condition without too much difficulty
by cloning PVs, followed by an incomplete attempt at making the two
VGs unique (vgrename and pvchange -u, but missing vgchange -u.)
2022-01-13 10:01:24 -06:00
David Teigland
cb798ee1c1 lvmcache: remove lvmcache_update_vg_from_write
After a vg_write, this function was used to attempt to
make lvmcache data match the new state written to disk.
It was not updated correctly in a many or most cases,
and the resulting lvmcache is not actually used after
vg_write, making the update unnecessary.
2022-01-13 10:01:16 -06:00
David Teigland
5e428d22d9 vgsplit: don't reread vg_to
The destination vg is first written with the EXPORTED flag,
then the source vg is written, then the destination vg is
written again without the EXPORTED flag.  Remove an unnecessary
vg_read of the destination vg just before the second write.
2022-01-12 16:42:01 -06:00
David Teigland
7502f78678 Revert "handle duplicate vgids"
This reverts commit bd2baeaaa6.

This commit broke vgrename because vgrename relies on old bugs
in lvmcache_update_vg_from_write and lvmcache_update_vgname
which need to be fixed first.
2022-01-11 16:04:51 -06:00
David Teigland
bd2baeaaa6 handle duplicate vgids
The approach to duplicate VGIDs has been that it is not possible
or not allowed, so the behavior has been undefined.  The actual
result was unpredictable and/or broken, and generally unhelpful.

Improve this by recognizing the problem, displaying the VGs,
and printing a warning to fix the problem.  Beyond this,
using VGs with duplicate VGIDs remains undefined, but should
work well enough to correct the problem with vgchange -u.

It's possible to create this condition without too much difficulty
by cloning PVs, followed by an incomplete attempt at making the two
VGs unique (vgrename and pvchange -u, but missing vgchange -u.)
2022-01-06 10:15:16 -06:00
David Teigland
42a16aa6f3 lvmlockd: cleanup after sanlock_rem_lockspace error
When storage is lost under a sanlock VG, and kill_vg/drop_vg
are used, sanlock_rem_lockspace() may return an error, but
the cleanup steps should still be performed.  Without the
cleanup, gl_lsname_sanlock was not cleared.  This caused
future lock requests to fail with ENOLS, but the NO_GL_LS
flag was not set due to gl_lsname_sanlock being set.
This caused lockd_global(sh) in lvm commands to fail when
they could succeed.

Fix from guozhonghua216
2022-01-04 14:53:47 -06:00
Zdenek Kabelac
04fbffb116 label: cache dm device list
Since we check for present DM devices - cache result for
futher use of checking presence of such device.

lvm2 uses cache result for label scan, but also when
it tries to activate or deactivate LV - however only simple
target 'striped' is reasonably supported.

Use disable_dm_devs to be able to control when lv_info()
get cache or uncached results.

TODO: support more type, however this is getting very complicated.
2021-12-20 16:13:28 +01:00
Zdenek Kabelac
0d67bc96fd activate: add get_device_list
Add funtion get_device_list() to get list of active DM devices.
Handled through new dm_task_get_device_list().
2021-12-20 16:13:28 +01:00
Zdenek Kabelac
988ea0e94c devicemapper: add dm_task_get_device_list
New API extension (internal ATM) for getting a list
of active DM device with extra features like i.e. uuid.

To easily lookout for existing UUID in device list,
there is: dm_device_list_find_by_uuid()

Once the returned structure is no longer usable call:
dm_device_list_destroy()

Struct dm_active_device {} holds all the info,
but is always allocated and destroyed within library.

TODO: once it's stable, copy to libdm
2021-12-20 16:13:28 +01:00
Zdenek Kabelac
90da953fd2 libdm: unmangling UUID for DM_DEVICE_LIST
Properly unmangle UUID if they are provided as result
of DM_DEVICE_LIST ioctl on newer linux kernels.
2021-12-20 16:13:28 +01:00
Zdenek Kabelac
65236ee722 activate: device_is_usable
Move checking of usable uuid into separate function
and pass in also cmd context.
2021-12-20 16:13:28 +01:00
Zdenek Kabelac
5a78979b68 lvmcmdline: comment reset of configuration settings
We used to reset 'settings' to their defaults after command is finished.
This however has a drawback we lose all the logging after this point.

So this patch disables this 'reset' to observe for side-effects.

lvm shell should be getting reset when next command is run -
so this might or might not have some 'hidden' effects.

ATM it looks like nothing really bad should happen - we just should be
able to get more logs - at least from normal commands.
2021-12-20 16:13:28 +01:00
Zdenek Kabelac
39a121ddbc libdm: correct version check
If there ever would be API version 5,
these check would give incorrect results.
2021-12-20 16:13:28 +01:00
Zdenek Kabelac
47ac2659d5 activate: cache driver_version result 2021-12-20 16:13:28 +01:00
Zdenek Kabelac
26e6580dfb toolcontext: reuse destroy_config_context
Call existing destroy_config_context() to destroy
some parts of cmd_context.
2021-12-20 16:13:28 +01:00
Zdenek Kabelac
09a4b56895 hash: raise hash table size
With slightly bigger hash tables, there is considerable
less hash collisions.
2021-12-20 16:13:28 +01:00
Zdenek Kabelac
ed1651d11f toollib: avoid repeated remove of online vg
Call just once unlink after every deactivation of LV from VG.
2021-12-20 16:13:28 +01:00
Andrew Walsh
522561e64b vdo: ensure VDO config is removed
Make sure to remove the VDO config after conversion
of LVM-backed VDO.

Addresses point 3 in rhbz#1987024#c5
2021-12-20 16:13:28 +01:00
David Teigland
0f71183f94 man lvmautoactivation: replace systemctl with journalctl 2021-12-14 12:02:08 -06:00
David Teigland
fbd8b0cf43 udev: remove -r from systemd-run
If the transient service remains after it's done, then
it prevents the same transient service from being run
again later if the PVs are detached and reattached
(although the behavior of a second autoactivation is not
well defined and may only work in limited cases.)
2021-12-14 11:57:13 -06:00
наб
6a431eb242 devices: recognise rbd (ceph rados block device)
Description stolen from linux d/b/rbd.c L3:
  rbd.c -- Export ceph rados objects as a Linux block device

16 partitions seem to make sense according to L90:
  #define RBD_SINGLE_MAJOR_PART_SHIFT 4

Running *scan -vvvvvvdddddd yields
  #filters/filter-type.c:28            /dev/rbd1p5: Skipping: Unrecognised LVM device type 252
  #filters/filter-persistent.c:131           filter caching bad /dev/rbd1p5
right now, and adding
  types = ["rbd", 252]
to /e/l/lvm.conf (with the matching "252 rbd" in /p/devices) works as a
per-machine fix:
  rbd1               252:16   0      1T  1 disk
  |-rbd1p1           252:17   0    243M  1 part
  |-rbd1p2           252:18   0      1K  1 part
  `-rbd1p5           252:21   0 1023.8G  1 part
    `-dev01--vg-root 253:0    0 1023.8G  0 lvm
but rbd is supported by upstream so it'd be nice to have it work OOB
2021-12-13 13:32:06 -06:00
David Teigland
c28541eccd lvcreate: include recent options
The permitted option list in lvcreate has not kept
up with command-lines.in.
2021-12-13 08:59:31 -06:00
David Teigland
d803d9202c man: add section about static autoactivation 2021-12-06 13:20:32 -06:00
David Teigland
ae54e75176 device_id: handle wwid with spaces or control characters
non-standard wwid can be reported from sysfs with spaces/etc.
replace with "_"
2021-12-02 13:33:18 -06:00
David Teigland
455c29b10d print warning about unrecognized journal option value 2021-12-02 12:40:52 -06:00
David Teigland
c42a05c3ec pvscan: fix error message for invalid devname
uninitialized name buffer used in message.
fixes "pvs_online: include devname in pvid files"
2021-12-01 14:38:17 -06:00
David Teigland
05d9a01351 pvscan: fix filter symlink checks
Fixes commit "pvscan: match device arg to filter symlink"
which failed to account for the fact that filter entries
are not just path names but include "a" or "r", etc.
2021-12-01 13:42:32 -06:00
David Teigland
d5402e55f6 devices file: don't write in test mode 2021-12-01 10:08:08 -06:00
David Teigland
71e7ebb3e4 tests devicesfile-devname.sh drop mdadm chunk 2021-12-01 08:56:05 -06:00
Marian Csontos
36095ac374 spec: Add lvmautoactivation man page 2021-12-01 15:36:34 +01:00
David Teigland
470b967bc5 pvscan: limit md device_hint for slow autoactivation
The device_hint name in the metadata was meant to prevent
autoactivation from md components, but the name checks were
more general and would catch unnecessary cases.
2021-11-30 09:06:08 -06:00
David Teigland
d12baba1a9 pvscan: match device arg to filter symlink
This fixes an issue related to the optimization in
  "pvscan: only add device args to dev cache"

If the devices file is not used, and the lvm.conf filter
accepts devices via symlink names, then those devices won't
be accepted by pvscan for autoactivation.  To resolve this,
recognize when the filter contains symlinks and disable the
optimization.  When the optimization is disabled, a full
dev_cache_scan is performed, and symlinks are associated
with the device names passed to pvscan.  filter-regex
will accept a device if symlinks to that device are accepted.
2021-11-29 17:13:44 -06:00
David Teigland
009007484b man: lvmautoactivation
new topical man page describing autoactivation
2021-11-29 11:10:02 -06:00
David Teigland
01bf8e1747 devices: exclude md components when duplicate pvs are seen
Improve handling of md components that get through the
filter, like the previous improvement for multipath.
If md components get through the filter and trigger
duplicate PV code, then eliminate any devs entirely
that are not an md device.
2021-11-22 15:10:43 -06:00
David Teigland
114e1cfee5 fix spelling of pruning 2021-11-19 12:02:35 -06:00
David Teigland
212b1fc529 devices: exclude multipath components based on matching wwid
If multipath component devices get through the filter and
cause lvm to see duplicate PVs, then check the wwid of the
devs and drop the component devices as if they had been
filtered.  If a dm mpath device was found among the duplicates
then use that as the PV, otherwise do not use any of the
components as the PV.

"duplicate PVs" associated with multipath configs will no
longer stop commands from working.
2021-11-18 17:41:00 -06:00
David Teigland
b8f4ec846d display: ignore --reportformat
Using the option would do nothing useful but would
print extraneous braces.
2021-11-17 10:40:27 -06:00
David Teigland
e4b8726b6d Revert "tests devicesfile-devname: remove searched_devnames"
This reverts commit 8e61c0ad6e.

The changes from "device_id: searched_devnames improvements"
allow this to work without the external removal.
2021-11-16 14:30:09 -06:00
David Teigland
5c71aa7510 tests pv-ext-flags: work with devices file 2021-11-16 14:29:22 -06:00
David Teigland
5c4ce4669e device_id: searched_devnames improvements
Remove the searched_devnames file in a couple more places:
. When hints need refreshing it's possible that a missing
  devices file entry could be found by searching devices
  again.
. When a devices file entry devname is first found to be
  incorrect, a new search for missing entries may be
  useful.
2021-11-16 14:29:17 -06:00
David Teigland
fee3937002 device_id: fix search on filtered device
When devnames are used as device ids and devnames change,
then new devices need to be located for the PVs.  If the old
devname is now used by a filtered device, this was preventing
the code from searching for the new device, so the PV was
reported as missing.
2021-11-16 09:29:24 -06:00
David Teigland
8e61c0ad6e tests devicesfile-devname: remove searched_devnames
remove /run/lvm/searched_devnames when preparing each test
in case it has appeared on the system
2021-11-15 18:04:10 -06:00
David Teigland
89c54db16c vgchange autoactivation: error path cleanup
If the optimized label scan fails (using online files),
then clear the device state prior to falling back to the
standard label_scan.  This avoids printing output about
unexpected state.
2021-11-15 11:07:11 -06:00
David Teigland
c5f998aec4 device_id: match different dm device names
If a devices file entry for a dm device is using the devname
for the device id, then recognize different dm names as matching.
2021-11-12 16:42:51 -06:00
David Teigland
4926061d32 tests: udev-pvscan-vgchange clear services 2021-11-12 12:10:26 -06:00
David Teigland
66f0fe57c3 online files: fix vgname check
The pvs_online file for a PV will not contain a vgname
if the PV has no metadata, so don't require matching
vgname with the pvs_lookup file.
2021-11-12 11:52:36 -06:00
David Teigland
5dbf316cee pvscan: consistent creation of pvs_lookup file
Consistently create the pvs_lookup file for VGs with
more than one PV.  Previously the file create would be
skipped if all the PVs happened to already be online.
That led to unpredicatable results in an uncommon case
(when the last PV to come online is the only PV with
metadata.)
2021-11-12 11:40:06 -06:00
David Teigland
b945ea1c93 tests vgchange-pvs-online: clean up with devices file
changing the dev names resulted in stale devices file
entries that created noise in the output.
2021-11-11 16:38:10 -06:00
David Teigland
20c550ab10 tests: udev-pvscan-vgchange fix wait
the service now remains after completion
2021-11-11 16:04:24 -06:00
David Teigland
0e0faf30e0 vgchange autoactivation: lock vg early to avoid second label scan
Copy another optimization from pvscan -aay to vgchange -aay.
When using the optimized label scan for only one VG, acquire the
VG lock prior to the scan.  This allows vg_read to then skip the
repeated label scan that normally happens after locking the vg.
2021-11-10 16:50:50 -06:00
David Teigland
92e741eda0 vgchange: move autoactivation setup code
into its own function, no functional change.
2021-11-10 14:44:11 -06:00
David Teigland
d608837b2a filter-sysfs: support old kernels without sys/dev/block
rhel5 for example doesn't have /sys/dev/block
2021-11-09 11:54:48 -06:00
David Teigland
73b4602f21 pvs_online: include devname in pvid files
Include the device name in the /run/lvm/pvs_online/pvid files.
Commands using the pvid file can use the devname to more quickly
find the correct device, vs finding the device using the
major:minor number.  If the devname in the pvid file is missing
or incorrect, fall back to using the devno.
2021-11-09 11:26:26 -06:00
David Teigland
024ce50f06 vgchange -aay: improve unexpected command variations
For completeness and consistency, adjust the behavior
for some variations of:

  vgchange -aay --autoactivation event [vgname]

The current standard use is with a VG name arg, and the
command is only called when all pvs_online files exist.
This is the optimal case, in which only pvs_online devs
are read.  This remains the same.

Clean up behaviors for some other unexpected uses of the
command:

. With no VG name arg, the command activates any VGs
  that are complete according to pvs_online.  If no
  pvs_online files exist, it does nothing.

. If a VG name is used but no PVs online files exist for
  the VG, or the PVs online files are incomplete, then
  consider there could be a problem with the pvs_online
  files, and fall back to a full label scan prior to
  attempting the activation.
2021-11-08 15:19:25 -06:00
David Teigland
14b68ea313 vgchange -aay: fall back to dev_cache_scan if optimization fails
Part of the optimization to avoid a full dev_cache_scan requires
translating major:minor numbers to a device name.  If this devno
translation fails, then fall back to doing a full dev_cache_scan
which is slower but certain to provide the info.  This preserves
the most important part of the label scanning optimization in the
vgchange aay (avoiding dev_cache_scan is a relatively small part
of the optimized activation compared to label scanning.)
2021-11-05 17:07:13 -05:00
David Teigland
b4067e84c7 fix device name from devno for partitions
sysfs files for partitions are different from
whole devices and will require more work to translate
to device names.
2021-11-05 16:21:23 -05:00
David Teigland
62533ae3fa vgchange -aay: optimize device list using pvs_online files
Port another optimization from pvscan -aay to vgchange -aay:
  "pvscan: only add device args to dev cache"

This optimization avoids doing a full dev_cache_scan, and
instead populates dev-cache with only the devices in the
VG being activated.

This involves shifting the use of pvs_online files from
the hints interface up to the higher level label_scan
interface.  This specialized label_scan is structured
around creating a list of devices from the pvs_online
files.  Previously, a list of all devices was created
first, and then reduced based on the pvs_online files.
The initial step of listing all devices was slow when
thousands of devices are present on the system.

This optimization extends the previous optimization that
used pvs_online files to limit the devices that were
actually scanned (i.e. reading to identify the device):
  "vgchange -aay: optimize device scan using pvs_online files"
2021-11-05 12:19:35 -05:00
David Teigland
5f7cb97743 lvm2-pvscan: include --autoactivation event
in the pvscan --cache -aay command so that the use
of the command for event activation is explicit.
2021-11-04 14:14:37 -05:00
David Teigland
f40fd88374 move code from pvscan.c to online.c
related to managing files in /run/lvm/pvs_online
and /run/lvm/vgs_online
2021-11-04 11:09:29 -05:00
David Teigland
d558b3ad7e vgchange -aay: optimize device scan using pvs_online files
Port the old pvscan -aay scanning optimization to vgchange -aay.
The optimization uses pvs_online files created by pvscan --cache
to derive a list of devices to use when activating a VG.  This
allows autoactivation of a VG to avoid scanning all devices, and
only scan the devices used by the VG itself.  The optimization is
applied internally using the device hints interface.

The new option "--autoactivation event" is given to pvscan and
vgchange commands that are called by event activation.  This
informs the command that it is being used for event activation,
so that it can apply checks and optimizations that are specific
to event activation.  Those include:

- skipping the command if lvm.conf event_activation=0
- checking that a VG is complete before activating it
- using pvs_online files to limit device scanning
2021-11-04 11:08:38 -05:00
David Teigland
594f6ca970 rename pvscan_cache_single to expect_missing_vg_device in cmd 2021-11-04 11:08:38 -05:00
David Teigland
726dd25969 add hints interface to the pvs_online file information
The information in /run/lvm/pvs_online/<pvid> files can
be used to build a list of devices for a given VG.

The pvscan -aay command has long used this information to
activate a VG while scanning only devices in that VG, which
is an important optimization for autoactivation.

This patch implements the same thing through the existing
device hints interface, so that the optimization can be
applied elsewhere.  A future patch will take advantage of
this optimization in vgchange -aay, which is now used in
place of pvscan -aay for event activation.
2021-11-04 10:58:16 -05:00
David Teigland
6ea8d975b2 lvmdevices: increase open file limit 2021-11-03 08:50:57 -05:00
David Teigland
b5b0369e4d filter-sysfs: skip when device id is set
When a device id is set for a device, using an idtype other
than devname, it means that sysfs has been used with the device
to match the device id.  So, checking for a sysfs entry for the
device in filter-sysfs is redundant.  For any other cases not
covered by this (e.g. devname ids), have filter-sysfs simply
stat /sys/dev/block/major:minor to test if the device exists
in sysfs.

The extensive processing done by filter-sysfs init is removed.
It was taking an immense amount of time with many devices, e.g.
. 1024 PVs in 520 VGs
. 520 concurrent vgchange -ay <vgname> commands
. vgchange scans only PVs in the named VG (based on pvs_online
  files from a pending patch)

A large number of the vgchange commands were taking over 1 min,
and nearly half of that time was used by filter-sysfs init.
With this patch, the vgchange commands take about half the time.
2021-11-02 16:54:53 -05:00
74 changed files with 3267 additions and 1647 deletions

View File

@@ -1,5 +1,8 @@
Version 2.03.15 -
===================================
Use cache or active DM device when available with new kernels.
Introduce function to utilize UUIDs from DM_DEVICE_LIST.
Increase some hash table size to better support large device sets.
Version 2.03.14 - 20th October 2021
===================================

View File

@@ -1,5 +1,6 @@
Version 1.02.183 -
====================================
Unmangle UUIDs for DM_DEVICE_LIST ioctl.
Version 1.02.181 - 20th October 2021
====================================

View File

@@ -1576,10 +1576,8 @@ int lm_rem_lockspace_sanlock(struct lockspace *ls, int free_vg)
goto out;
rv = sanlock_rem_lockspace(&lms->ss, 0);
if (rv < 0) {
if (rv < 0)
log_error("S %s rem_lockspace_san error %d", ls->name, rv);
return rv;
}
if (free_vg) {
/*

View File

@@ -173,6 +173,16 @@ struct dm_names {
char name[];
};
struct dm_active_device {
struct dm_list list;
int major;
int minor;
char *name; /* device name */
uint32_t event_nr; /* valid when DM_DEVICE_LIST_HAS_EVENT_NR is set */
char *uuid; /* valid uuid when DM_DEVICE_LIST_HAS_UUID is set */
};
struct dm_versions {
uint32_t next; /* Offset to next struct from start of this struct */
uint32_t version[3];
@@ -210,6 +220,25 @@ const char *dm_task_get_message_response(struct dm_task *dmt);
*/
const char *dm_task_get_name(const struct dm_task *dmt);
struct dm_names *dm_task_get_names(struct dm_task *dmt);
/*
* Retrieve the list of devices and put them into easily accessible
* struct dm_active_device list elements.
* devs_features provides flag-set with used features so it's easy to check
* whether the kernel provides i.e. UUID info together with DM names
*/
#define DM_DEVICE_LIST_HAS_EVENT_NR 1
#define DM_DEVICE_LIST_HAS_UUID 2
int dm_task_get_device_list(struct dm_task *dmt, struct dm_list **devs_list,
unsigned *devs_features);
/*
* -1: no idea about uuid (not provided by DM_DEVICE_LIST ioctl)
* 0: uuid not present
* 1: listed and dm_active_device will be set for not NULL pointer
*/
int dm_device_list_find_by_uuid(struct dm_list *devs_list, const char *uuid,
const struct dm_active_device **dev);
/* Release all associated memory with list of active DM devices */
void dm_device_list_destroy(struct dm_list **devs_list);
int dm_task_set_ro(struct dm_task *dmt);
int dm_task_set_newname(struct dm_task *dmt, const char *newname);

View File

@@ -616,8 +616,7 @@ int dm_check_version(void)
int dm_cookie_supported(void)
{
return (dm_check_version() &&
_dm_version >= 4 &&
_dm_version_minor >= 15);
((_dm_version == 4) ? _dm_version_minor >= 15 : _dm_version > 4));
}
static int _dm_inactive_supported(void)
@@ -755,6 +754,159 @@ struct dm_deps *dm_task_get_deps(struct dm_task *dmt)
dmt->dmi.v4->data_start);
}
/*
* Round up the ptr to an 8-byte boundary.
* Follow kernel pattern.
*/
#define ALIGN_MASK 7
static size_t _align_val(size_t val)
{
return (val + ALIGN_MASK) & ~ALIGN_MASK;
}
static void *_align_ptr(void *ptr)
{
return (void *)_align_val((size_t)ptr);
}
static int _check_has_event_nr(void) {
static int _has_event_nr = -1;
if (_has_event_nr < 0)
_has_event_nr = dm_check_version() &&
((_dm_version == 4) ? _dm_version_minor >= 38 : _dm_version > 4);
return _has_event_nr;
}
struct dm_device_list {
struct dm_list list;
unsigned count;
unsigned features;
struct dm_hash_table *uuids;
};
int dm_task_get_device_list(struct dm_task *dmt, struct dm_list **devs_list,
unsigned *devs_features)
{
struct dm_names *names, *names1;
struct dm_active_device *dm_dev, *dm_new_dev;
struct dm_device_list *devs;
unsigned next = 0;
uint32_t *event_nr;
char *uuid_ptr;
size_t len;
int cnt = 0;
*devs_list = 0;
*devs_features = 0;
if ((names = dm_task_get_names(dmt)) && names->dev) {
names1 = names;
if (!names->name[0])
cnt = -1; /* -> cnt == 0 when no device is really present */
do {
names1 = (struct dm_names *)((char *) names1 + next);
next = names1->next;
++cnt;
} while (next);
}
if (!(devs = malloc(sizeof(*devs) + (cnt ? cnt * sizeof(*dm_dev) + (char*)names1 - (char*)names + 256 : 0))))
return_0;
dm_list_init(&devs->list);
devs->count = cnt;
devs->uuids = NULL;
if (!cnt) {
/* nothing in the list -> mark all features present */
*devs_features |= (DM_DEVICE_LIST_HAS_EVENT_NR | DM_DEVICE_LIST_HAS_UUID);
goto out; /* nothing else to do */
}
dm_dev = (struct dm_active_device *) (devs + 1);
do {
names = (struct dm_names *)((char *) names + next);
dm_dev->major = MAJOR(names->dev);
dm_dev->minor = MINOR(names->dev);
dm_dev->name = (char*)(dm_dev + 1);
dm_dev->event_nr = 0;
dm_dev->uuid = NULL;
strcpy(dm_dev->name, names->name);
len = strlen(names->name) + 1;
dm_new_dev = _align_ptr((char*)(dm_dev + 1) + len);
if (_check_has_event_nr()) {
/* Hash for UUIDs with some more bits to reduce colision count */
if (!devs->uuids && !(devs->uuids = dm_hash_create(cnt * 8))) {
free(devs);
return_0;
}
*devs_features |= DM_DEVICE_LIST_HAS_EVENT_NR;
event_nr = _align_ptr(names->name + len);
dm_dev->event_nr = event_nr[0];
if ((event_nr[1] & DM_NAME_LIST_FLAG_HAS_UUID)) {
*devs_features |= DM_DEVICE_LIST_HAS_UUID;
uuid_ptr = _align_ptr(event_nr + 2);
dm_dev->uuid = (char*) dm_new_dev;
dm_new_dev = _align_ptr((char*)dm_new_dev + strlen(uuid_ptr) + 1);
strcpy(dm_dev->uuid, uuid_ptr);
if (!dm_hash_insert(devs->uuids, dm_dev->uuid, dm_dev))
return_0; // FIXME
#if 0
log_debug("Active %s (%s) %d:%d event:%u",
dm_dev->name, dm_dev->uuid,
dm_dev->major, dm_dev->minor, dm_dev->event_nr);
#endif
}
}
dm_list_add(&devs->list, &dm_dev->list);
dm_dev = dm_new_dev;
next = names->next;
} while (next);
out:
*devs_list = (struct dm_list *)devs;
return 1;
}
int dm_device_list_find_by_uuid(struct dm_list *devs_list, const char *uuid,
const struct dm_active_device **dev)
{
struct dm_device_list *devs = (struct dm_device_list *) devs_list;
struct dm_active_device *dm_dev;
if (devs->uuids &&
(dm_dev = dm_hash_lookup(devs->uuids, uuid))) {
if (dev)
*dev = dm_dev;
return 1;
}
return 0;
}
void dm_device_list_destroy(struct dm_list **devs_list)
{
struct dm_device_list *devs = (struct dm_device_list *) *devs_list;
if (devs) {
if (devs->uuids)
dm_hash_destroy(devs->uuids);
free(devs);
*devs_list = NULL;
}
}
struct dm_names *dm_task_get_names(struct dm_task *dmt)
{
return (struct dm_names *) (((char *) dmt->dmi.v4) +
@@ -1441,8 +1593,7 @@ static int _udev_complete(struct dm_task *dmt)
static int _check_uevent_generated(struct dm_ioctl *dmi)
{
if (!dm_check_version() ||
_dm_version < 4 ||
_dm_version_minor < 17)
((_dm_version == 4) ? _dm_version_minor < 17 : _dm_version < 4))
/* can't check, assume uevent is generated */
return 1;
@@ -1808,23 +1959,34 @@ static int _do_dm_ioctl_unmangle_string(char *str, const char *str_name,
static int _dm_ioctl_unmangle_names(int type, struct dm_ioctl *dmi)
{
char buf[DM_NAME_LEN];
struct dm_names *names;
char buf_uuid[DM_UUID_LEN];
struct dm_name_list *names;
unsigned next = 0;
char *name;
int r = 1;
uint32_t *event_nr;
char *uuid_ptr;
dm_string_mangling_t mangling_mode = dm_get_name_mangling_mode();
if ((name = dmi->name))
r = _do_dm_ioctl_unmangle_string(name, "name", buf, sizeof(buf),
dm_get_name_mangling_mode());
r &= _do_dm_ioctl_unmangle_string(name, "name", buf, sizeof(buf),
mangling_mode);
if (type == DM_DEVICE_LIST &&
((names = ((struct dm_names *) ((char *)dmi + dmi->data_start)))) &&
((names = ((struct dm_name_list *) ((char *)dmi + dmi->data_start)))) &&
names->dev) {
do {
names = (struct dm_names *)((char *) names + next);
r = _do_dm_ioctl_unmangle_string(names->name, "name",
buf, sizeof(buf),
dm_get_name_mangling_mode());
names = (struct dm_name_list *)((char *) names + next);
event_nr = _align_ptr(names->name + strlen(names->name) + 1);
r &= _do_dm_ioctl_unmangle_string(names->name, "name",
buf, sizeof(buf), mangling_mode);
/* Unmangle also UUID within same loop */
if (_check_has_event_nr() &&
(event_nr[1] & DM_NAME_LIST_FLAG_HAS_UUID)) {
uuid_ptr = _align_ptr(event_nr + 2);
r &= _do_dm_ioctl_unmangle_string(uuid_ptr, "UUID", buf_uuid,
sizeof(buf_uuid), mangling_mode);
}
next = names->next;
} while (next);
}

View File

@@ -40,6 +40,7 @@ SOURCES =\
device/dev-luks.c \
device/dev-dasd.c \
device/dev-lvm1-pool.c \
device/online.c \
display/display.c \
error/errseg.c \
unknown/unknown.c \

View File

@@ -413,7 +413,7 @@ int add_areas_line(struct dev_manager *dm, struct lv_segment *seg,
{
return 0;
}
int device_is_usable(struct device *dev, struct dev_usable_check_params check, int *is_lv)
int device_is_usable(struct cmd_context *cmd, struct device *dev, struct dev_usable_check_params check, int *is_lv)
{
return 0;
}
@@ -486,12 +486,20 @@ int library_version(char *version, size_t size)
int driver_version(char *version, size_t size)
{
static char _vsn[80] = { 0 };
if (!activation())
return 0;
log_very_verbose("Getting driver version");
return dm_driver_version(version, size);
if (!_vsn[0] &&
!dm_driver_version(_vsn, sizeof(_vsn)))
return_0;
(void) dm_strncpy(version, _vsn, size);
return 1;
}
int target_version(const char *target_name, uint32_t *maj,
@@ -616,6 +624,15 @@ int target_present(struct cmd_context *cmd, const char *target_name,
&maj, &min, &patchlevel);
}
int get_device_list(const struct volume_group *vg, struct dm_list **devs,
unsigned *devs_features)
{
if (!activation())
return 0;
return dev_manager_get_device_list(NULL, devs, devs_features);
}
/*
* When '*info' is NULL, returns 1 only when LV is active.
* When '*info' != NULL, returns 1 when info structure is populated.
@@ -2374,6 +2391,7 @@ int lv_deactivate(struct cmd_context *cmd, const char *lvid_s, const struct logi
static const struct lv_activate_opts laopts = { .skip_in_use = 1 };
struct dm_list *snh;
int r = 0;
unsigned tmp_state;
if (!activation())
return 1;
@@ -2446,12 +2464,17 @@ int lv_deactivate(struct cmd_context *cmd, const char *lvid_s, const struct logi
}
critical_section_dec(cmd, "deactivated");
tmp_state = cmd->disable_dm_devs;
cmd->disable_dm_devs = 1;
if (!lv_info(cmd, lv, 0, &info, 0, 0) || info.exists) {
/* Turn into log_error, but we do not log error */
log_debug_activation("Deactivated volume is still %s present.",
display_lvname(lv));
r = 0;
}
cmd->disable_dm_devs = tmp_state;
out:
return r;

View File

@@ -106,6 +106,10 @@ int target_present(struct cmd_context *cmd, const char *target_name,
int use_modprobe);
int target_version(const char *target_name, uint32_t *maj,
uint32_t *min, uint32_t *patchlevel);
int get_device_list(const struct volume_group *vg, struct dm_list **devs,
unsigned *devs_features);
int raid4_is_supported(struct cmd_context *cmd, const struct segment_type *segtype);
int lvm_dm_prefix_check(int major, int minor, const char *prefix);
int list_segment_modules(struct dm_pool *mem, const struct lv_segment *seg,
@@ -254,7 +258,7 @@ struct dev_usable_check_params {
* Returns 1 if mapped device is not suspended, blocked or
* is using a reserved name.
*/
int device_is_usable(struct device *dev, struct dev_usable_check_params check, int *is_lv);
int device_is_usable(struct cmd_context *cmd, struct device *dev, struct dev_usable_check_params check, int *is_lv);
/*
* Declaration moved here from fs.h to keep header fs.h hidden

View File

@@ -138,8 +138,16 @@ static struct dm_task *_setup_task_run(int task, struct dm_info *info,
if (!with_flush && !dm_task_no_flush(dmt))
log_warn("WARNING: Failed to set no_flush.");
if (task == DM_DEVICE_TARGET_MSG)
switch (task) {
case DM_DEVICE_TARGET_MSG:
return dmt; /* TARGET_MSG needs more local tweaking before task_run() */
case DM_DEVICE_LIST:
if (!dm_task_set_newuuid(dmt, " ")) // new uuid has no meaning here
log_warn("WARNING: Failed to query uuid with LIST.");
break;
default:
break;
}
if (!dm_task_run(dmt))
goto_out;
@@ -359,7 +367,8 @@ static int _info_run(const char *dlid, struct dm_info *dminfo,
*
* Returns: 1 if mirror should be ignored, 0 if safe to use
*/
static int _ignore_blocked_mirror_devices(struct device *dev,
static int _ignore_blocked_mirror_devices(struct cmd_context *cmd,
struct device *dev,
uint64_t start, uint64_t length,
char *mirror_status_str)
{
@@ -402,7 +411,7 @@ static int _ignore_blocked_mirror_devices(struct device *dev,
goto_out;
tmp_dev->dev = MKDEV(sm->logs[0].major, sm->logs[0].minor);
if (device_is_usable(tmp_dev, (struct dev_usable_check_params)
if (device_is_usable(cmd, tmp_dev, (struct dev_usable_check_params)
{ .check_empty = 1,
.check_blocked = 1,
.check_suspended = ignore_suspended_devices(),
@@ -606,6 +615,60 @@ static int _ignore_frozen_raid(struct device *dev, const char *params)
return r;
}
static int _is_usable_uuid(const struct device *dev, const char *name, const char *uuid, int check_reserved, int check_lv, int *is_lv)
{
char *vgname, *lvname, *layer;
char vg_name[NAME_LEN];
if (!check_reserved && !check_lv)
return 1;
if (!strncmp(uuid, UUID_PREFIX, sizeof(UUID_PREFIX) - 1)) { /* with LVM- prefix */
if (check_reserved) {
/* Check internal lvm devices */
if (strlen(uuid) > (sizeof(UUID_PREFIX) + 2 * ID_LEN)) { /* 68 with suffix */
log_debug_activation("%s: Reserved uuid %s on internal LV device %s not usable.",
dev_name(dev), uuid, name);
return 0;
}
/* Recognize some older reserved LVs just from the LV name (snapshot, pvmove...) */
vgname = vg_name;
if (!dm_strncpy(vg_name, name, sizeof(vg_name)) ||
!dm_split_lvm_name(NULL, NULL, &vgname, &lvname, &layer))
return_0;
/* FIXME: fails to handle dev aliases i.e. /dev/dm-5, replace with UUID suffix */
if (lvname && (is_reserved_lvname(lvname) || *layer)) {
log_debug_activation("%s: Reserved internal LV device %s/%s%s%s not usable.",
dev_name(dev), vgname, lvname, *layer ? "-" : "", layer);
return 0;
}
}
if (check_lv) {
/* Skip LVs */
if (is_lv)
*is_lv = 1;
return 0;
}
}
if (check_reserved &&
(!strncmp(uuid, CRYPT_TEMP, sizeof(CRYPT_TEMP) - 1) ||
!strncmp(uuid, CRYPT_SUBDEV, sizeof(CRYPT_SUBDEV) - 1) ||
!strncmp(uuid, STRATIS, sizeof(STRATIS) - 1))) {
/* Skip private crypto devices */
log_debug_activation("%s: Reserved uuid %s on %s device %s not usable.",
dev_name(dev), uuid,
uuid[0] == 'C' ? "crypto" : "stratis",
name);
return 0;
}
return 1;
}
/*
* device_is_usable
* @dev
@@ -622,15 +685,14 @@ static int _ignore_frozen_raid(struct device *dev, const char *params)
*
* Returns: 1 if usable, 0 otherwise
*/
int device_is_usable(struct device *dev, struct dev_usable_check_params check, int *is_lv)
int device_is_usable(struct cmd_context *cmd, struct device *dev, struct dev_usable_check_params check, int *is_lv)
{
struct dm_task *dmt;
struct dm_info info;
const char *name, *uuid;
uint64_t start, length;
char *target_type = NULL;
char *params, *vgname, *lvname, *layer;
char vg_name[NAME_LEN];
char *params;
void *next = NULL;
int only_error_or_zero_target = 1;
int r = 0;
@@ -655,50 +717,9 @@ int device_is_usable(struct device *dev, struct dev_usable_check_params check, i
goto out;
}
if (uuid && (check.check_reserved || check.check_lv)) {
if (!strncmp(uuid, UUID_PREFIX, sizeof(UUID_PREFIX) - 1)) { /* with LVM- prefix */
if (check.check_reserved) {
/* Check internal lvm devices */
if (strlen(uuid) > (sizeof(UUID_PREFIX) + 2 * ID_LEN)) { /* 68 with suffix */
log_debug_activation("%s: Reserved uuid %s on internal LV device %s not usable.",
dev_name(dev), uuid, name);
goto out;
}
/* Recognize some older reserved LVs just from the LV name (snapshot, pvmove...) */
vgname = vg_name;
if (!dm_strncpy(vg_name, name, sizeof(vg_name)) ||
!dm_split_lvm_name(NULL, NULL, &vgname, &lvname, &layer))
goto_out;
/* FIXME: fails to handle dev aliases i.e. /dev/dm-5, replace with UUID suffix */
if (lvname && (is_reserved_lvname(lvname) || *layer)) {
log_debug_activation("%s: Reserved internal LV device %s/%s%s%s not usable.",
dev_name(dev), vgname, lvname, *layer ? "-" : "", layer);
goto out;
}
}
if (check.check_lv) {
/* Skip LVs */
if (is_lv)
*is_lv = 1;
goto out;
}
}
if (check.check_reserved &&
(!strncmp(uuid, CRYPT_TEMP, sizeof(CRYPT_TEMP) - 1) ||
!strncmp(uuid, CRYPT_SUBDEV, sizeof(CRYPT_SUBDEV) - 1) ||
!strncmp(uuid, STRATIS, sizeof(STRATIS) - 1))) {
/* Skip private crypto devices */
log_debug_activation("%s: Reserved uuid %s on %s device %s not usable.",
dev_name(dev), uuid,
uuid[0] == 'C' ? "crypto" : "stratis",
name);
goto out;
}
}
if (uuid &&
!_is_usable_uuid(dev, name, uuid, check.check_reserved, check.check_lv, is_lv))
goto out;
/* FIXME Also check for mpath no paths */
do {
@@ -713,7 +734,7 @@ int device_is_usable(struct device *dev, struct dev_usable_check_params check, i
log_debug_activation("%s: Scanning mirror devices is disabled.", dev_name(dev));
goto out;
}
if (!_ignore_blocked_mirror_devices(dev, start,
if (!_ignore_blocked_mirror_devices(cmd, dev, start,
length, params)) {
log_debug_activation("%s: Mirror device %s not usable.",
dev_name(dev), name);
@@ -909,6 +930,25 @@ int dev_manager_check_prefix_dm_major_minor(uint32_t major, uint32_t minor, cons
return r;
}
int dev_manager_get_device_list(const char *prefix, struct dm_list **devs, unsigned *devs_features)
{
struct dm_task *dmt;
int r = 1;
if (!(dmt = _setup_task_run(DM_DEVICE_LIST, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0)))
return_0;
if (!dm_task_get_device_list(dmt, devs, devs_features)) {
r = 0;
goto_out;
}
out:
dm_task_destroy(dmt);
return r;
}
int dev_manager_info(struct cmd_context *cmd,
const struct logical_volume *lv, const char *layer,
int with_open_count, int with_read_ahead, int with_name_check,
@@ -924,6 +964,16 @@ int dev_manager_info(struct cmd_context *cmd,
if (!(dlid = build_dm_uuid(cmd->mem, lv, layer)))
goto_out;
if (!cmd->disable_dm_devs &&
cmd->cache_dm_devs &&
!dm_device_list_find_by_uuid(cmd->cache_dm_devs, dlid, NULL)) {
log_debug("Cached as inactive %s.", name);
if (dminfo)
memset(dminfo, 0, sizeof(*dminfo));
r = 1;
goto out;
}
if (!(r = _info(cmd, name, dlid,
with_open_count, with_read_ahead, with_name_check,
dminfo, read_ahead, seg_status)))
@@ -2205,6 +2255,13 @@ static int _add_dev_to_dtree(struct dev_manager *dm, struct dm_tree *dtree,
if (!(dlid = build_dm_uuid(dm->track_pending_delete ? dm->cmd->pending_delete_mem : dm->mem, lv, layer)))
return_0;
if (!dm->cmd->disable_dm_devs &&
dm->cmd->cache_dm_devs &&
!dm_device_list_find_by_uuid(dm->cmd->cache_dm_devs, dlid, NULL)) {
log_debug("Cached as not present %s.", name);
return 1;
}
if (!_info(dm->cmd, name, dlid, 1, 0, 0, &info, NULL, NULL))
return_0;
@@ -2350,6 +2407,9 @@ static int _pool_callback(struct dm_tree_node *node,
return 0;
}
}
dm_device_list_destroy(&cmd->cache_dm_devs); /* Cache no longer valid */
log_debug("Running check command on %s", mpath);
if (data->skip_zero) {
@@ -3737,6 +3797,7 @@ static int _tree_action(struct dev_manager *dm, const struct logical_volume *lv,
struct dm_tree_node *root;
char *dlid;
int r = 0;
unsigned tmp_state;
if (action < DM_ARRAY_SIZE(_action_names))
log_debug_activation("Creating %s%s tree for %s.",
@@ -3756,9 +3817,17 @@ static int _tree_action(struct dev_manager *dm, const struct logical_volume *lv,
dm->suspend = (action == SUSPEND_WITH_LOCKFS) || (action == SUSPEND);
dm->track_external_lv_deps = 1;
/* ATM do not use caching for anything else then striped target.
* And also skip for CLEAN action */
tmp_state = dm->cmd->disable_dm_devs;
if (!seg_is_striped_target(first_seg(lv)) || (action == CLEAN))
dm->cmd->disable_dm_devs = 1;
if (!(dtree = _create_partial_dtree(dm, lv, laopts->origin_only)))
return_0;
dm->cmd->disable_dm_devs = tmp_state;
if (!(root = dm_tree_find_node(dtree, 0, 0))) {
log_error("Lost dependency tree root node.");
goto out_no_root;

View File

@@ -103,5 +103,7 @@ int dev_manager_device_uses_vg(struct device *dev,
int dev_manager_remove_dm_major_minor(uint32_t major, uint32_t minor);
int dev_manager_check_prefix_dm_major_minor(uint32_t major, uint32_t minor, const char *prefix);
int dev_manager_get_device_list(const char *prefix, struct dm_list **devs,
unsigned *devs_features);
#endif

414
lib/cache/lvmcache.c vendored
View File

@@ -354,9 +354,11 @@ static struct lvmcache_vginfo *_vginfo_lookup(const char *vgname, const char *vg
if (vgid_arg) {
if ((vginfo = dm_hash_lookup(_vgid_hash, vgid))) {
if (vgname && strcmp(vginfo->vgname, vgname)) {
/* should never happen */
log_error(INTERNAL_ERROR "vginfo_lookup vgid %s has two names %s %s",
vgid, vginfo->vgname, vgname);
log_warn("WARNING: lookup found duplicate VGID %s for VGs %s and %s.", vgid, vginfo->vgname, vgname);
if ((vginfo = dm_hash_lookup(_vgname_hash, vgname))) {
if (!memcmp(vginfo->vgid, vgid, ID_LEN))
return vginfo;
}
return NULL;
}
return vginfo;
@@ -572,6 +574,16 @@ static const char *_get_pvsummary_device_id(const char *pvid_arg, const char **d
return NULL;
}
int lvmcache_pvsummary_count(const char *vgname)
{
struct lvmcache_vginfo *vginfo;
if (!(vginfo = lvmcache_vginfo_from_vgname(vgname, NULL)))
return_0;
return dm_list_size(&vginfo->pvsummaries);
}
/*
* Check if any PVs in vg->pvs have the same PVID as any
* entries in _unused_duplicates.
@@ -625,6 +637,148 @@ static void _warn_unused_duplicates(struct cmd_context *cmd)
}
}
static int _all_multipath_components(struct cmd_context *cmd, struct lvmcache_info *info, const char *pvid,
struct dm_list *altdevs, struct device **dev_mpath)
{
struct device_list *devl;
struct device *dev_mp = NULL;
struct device *dev1 = NULL;
struct device *dev;
const char *wwid1 = NULL;
const char *wwid;
int diff_wwid = 0;
int same_wwid = 0;
int dev_is_mp;
*dev_mpath = NULL;
/* This function only makes sense with more than one dev. */
if ((info && dm_list_empty(altdevs)) || (!info && (dm_list_size(altdevs) == 1))) {
log_debug("Skip multipath component checks with single device for PVID %s", pvid);
return 0;
}
log_debug("Checking for multipath components for duplicate PVID %s", pvid);
if (info) {
dev = info->dev;
dev_is_mp = (cmd->dev_types->device_mapper_major == MAJOR(dev->dev)) && dev_has_mpath_uuid(cmd, dev, NULL);
if (dev_is_mp) {
if ((wwid1 = dev_mpath_component_wwid(cmd, dev))) {
dev_mp = dev;
dev1 = dev;
}
} else {
if ((wwid1 = device_id_system_read(cmd, dev, DEV_ID_TYPE_SYS_WWID)))
dev1 = dev;
}
}
dm_list_iterate_items(devl, altdevs) {
dev = devl->dev;
dev_is_mp = (cmd->dev_types->device_mapper_major == MAJOR(dev->dev)) && dev_has_mpath_uuid(cmd, dev, NULL);
if (dev_is_mp)
wwid = dev_mpath_component_wwid(cmd, dev);
else
wwid = device_id_system_read(cmd, dev, DEV_ID_TYPE_SYS_WWID);
if (!wwid && wwid1) {
log_debug("Different wwids for duplicate PVs %s %s %s none",
dev_name(dev1), wwid1, dev_name(dev));
diff_wwid++;
continue;
}
if (!wwid)
continue;
if (!wwid1) {
wwid1 = wwid;
dev1 = dev;
continue;
}
/* Different wwids indicates these are not multipath components. */
if (strcmp(wwid1, wwid)) {
log_debug("Different wwids for duplicate PVs %s %s %s %s",
dev_name(dev1), wwid1, dev_name(dev), wwid);
diff_wwid++;
continue;
}
/* Different mpath devs with the same wwid shouldn't happen. */
if (dev_is_mp && dev_mp) {
log_print("Found multiple multipath devices for PVID %s WWID %s: %s %s",
pvid, wwid1, dev_name(dev_mp), dev_name(dev));
continue;
}
log_debug("Same wwids for duplicate PVs %s %s", dev_name(dev1), dev_name(dev));
same_wwid++;
/* Save the mpath device so it can be used as the PV. */
if (dev_is_mp)
dev_mp = dev;
}
if (diff_wwid || !same_wwid)
return 0;
if (dev_mp)
log_debug("Found multipath device %s for PVID %s WWID %s.", dev_name(dev_mp), pvid, wwid1);
*dev_mpath = dev_mp;
return 1;
}
static int _all_md_components(struct cmd_context *cmd, struct lvmcache_info *info, const char *pvid,
struct dm_list *altdevs, struct device **dev_md_out)
{
struct device_list *devl;
struct device *dev_md = NULL;
struct device *dev;
int real_dup = 0;
*dev_md_out = NULL;
/* There will often be no info struct because of the extra_md_checks function. */
if (info && (cmd->dev_types->md_major == MAJOR(info->dev->dev)))
dev_md = info->dev;
dm_list_iterate_items(devl, altdevs) {
dev = devl->dev;
if (cmd->dev_types->md_major == MAJOR(dev->dev)) {
if (dev_md) {
/* md devs themselves are dups */
log_debug("Found multiple md devices for PVID %s: %s %s",
pvid, dev_name(dev_md), dev_name(dev));
real_dup = 1;
break;
} else
dev_md = dev;
} else {
if (!dev_is_md_component(cmd, dev, NULL, 1)) {
/* md dev copied to another device */
real_dup = 1;
break;
}
}
}
if (real_dup)
return 0;
if (dev_md)
log_debug("Found md device %s for PVID %s.", dev_name(dev_md), pvid);
*dev_md_out = dev_md;
return 1;
}
/*
* If we've found devices with the same PVID, decide which one
* to use.
@@ -680,6 +834,8 @@ static void _choose_duplicates(struct cmd_context *cmd,
struct device_list *devl, *devl_safe, *devl_add, *devl_del;
struct lvmcache_info *info;
struct device *dev1, *dev2;
struct device *dev_mpath, *dev_md;
struct device *dev_drop;
const char *device_id = NULL, *device_id_type = NULL;
const char *idname1 = NULL, *idname2 = NULL;
uint32_t dev1_major, dev1_minor, dev2_major, dev2_minor;
@@ -702,6 +858,8 @@ static void _choose_duplicates(struct cmd_context *cmd,
next:
dm_list_init(&altdevs);
pvid = NULL;
dev_mpath = NULL;
dev_md = NULL;
dm_list_iterate_items_safe(devl, devl_safe, &_initial_duplicates) {
if (!pvid) {
@@ -720,30 +878,173 @@ next:
return;
}
info = lvmcache_info_from_pvid(pvid, NULL, 0);
/*
* Get rid of any md components before comparing alternatives.
* (Since an md component can never be used, it's not an
* option to use like other kinds of alternatives.)
* Usually and ideally, components of md and multipath devs should have
* been excluded by filters, and not scanned for a PV. In some unusual
* cases the components can get through the filters, and a PV can be
* found on them. Detecting the same PVID on both the component and
* the md/mpath device gives us a last chance to drop the component.
* An md/mpath component device is completely ignored, as if it had
* been filtered, and not kept in the list unused duplicates.
*
* One issue related to eliminating mpath/md duplicate PVs here is
* that it occurs after label_scan, and hints are created based
* on what label_scan finds, so hints are disabled due to duplicate
* PVs that are later resolved here.
*/
info = lvmcache_info_from_pvid(pvid, NULL, 0);
if (info && dev_is_md_component(cmd, info->dev, NULL, 1)) {
/* does not go in del_cache_devs which become unused_duplicates */
log_debug_cache("PV %s drop MD component from scan selection %s", pvid, dev_name(info->dev));
lvmcache_del(info);
info = NULL;
}
/*
* Get rid of multipath components based on matching wwids.
*/
if (_all_multipath_components(cmd, info, pvid, &altdevs, &dev_mpath)) {
if (info && dev_mpath && (info->dev != dev_mpath)) {
/*
* info should be dropped from lvmcache and info->dev
* should be treated as if it had been excluded by a filter.
* dev_mpath should be added to lvmcache by the caller.
*/
dev_drop = info->dev;
dm_list_iterate_items_safe(devl, devl_safe, &altdevs) {
if (dev_is_md_component(cmd, devl->dev, NULL, 1)) {
log_debug_cache("PV %s drop MD component from scan duplicates %s", pvid, dev_name(devl->dev));
dm_list_del(&devl->list);
/* Have caller add dev_mpath to lvmcache. */
log_debug("Using multipath device %s for PVID %s.", dev_name(dev_mpath), pvid);
if ((devl_add = zalloc(sizeof(*devl_add)))) {
devl_add->dev = dev_mpath;
dm_list_add(add_cache_devs, &devl_add->list);
}
/* Remove dev_mpath from altdevs. */
if ((devl = _get_devl_in_device_list(dev_mpath, &altdevs)))
dm_list_del(&devl->list);
/* Remove info from lvmcache that came from the component dev. */
log_debug("Ignoring multipath component %s with PVID %s (dropping info)", dev_name(dev_drop), pvid);
lvmcache_del(info);
info = NULL;
/* Make the component dev look like it was filtered. */
cmd->filter->wipe(cmd, cmd->filter, dev_drop, NULL);
dev_drop->flags &= ~DEV_SCAN_FOUND_LABEL;
}
if (info && !dev_mpath) {
/*
* Only mpath component devs were found and no actual
* multipath dev, so drop the component from lvmcache.
*/
dev_drop = info->dev;
log_debug("Ignoring multipath component %s with PVID %s (dropping info)", dev_name(dev_drop), pvid);
lvmcache_del(info);
info = NULL;
/* Make the component dev look like it was filtered. */
cmd->filter->wipe(cmd, cmd->filter, dev_drop, NULL);
dev_drop->flags &= ~DEV_SCAN_FOUND_LABEL;
}
dm_list_iterate_items_safe(devl, devl_safe, &altdevs) {
/*
* The altdevs are all mpath components that should look
* like they were filtered, they are not in lvmcache.
*/
dev_drop = devl->dev;
log_debug("Ignoring multipath component %s with PVID %s (dropping duplicate)", dev_name(dev_drop), pvid);
dm_list_del(&devl->list);
cmd->filter->wipe(cmd, cmd->filter, dev_drop, NULL);
dev_drop->flags &= ~DEV_SCAN_FOUND_LABEL;
}
goto next;
}
if (dm_list_empty(&altdevs))
goto next;
/*
* Get rid of any md components.
*/
if (_all_md_components(cmd, info, pvid, &altdevs, &dev_md)) {
if (info && dev_md && (info->dev != dev_md)) {
/*
* info should be dropped from lvmcache and info->dev
* should be treated as if it had been excluded by a filter.
* dev_md should be added to lvmcache by the caller.
* Often this info struct has been removed by
* lvmcache_extra_md_component_checks.
*/
dev_drop = info->dev;
/* Have caller add dev_md to lvmcache. */
log_debug("Using md device %s for PVID %s.", dev_name(dev_md), pvid);
if ((devl_add = zalloc(sizeof(*devl_add)))) {
devl_add->dev = dev_md;
dm_list_add(add_cache_devs, &devl_add->list);
}
/* Remove dev_md from altdevs. */
if ((devl = _get_devl_in_device_list(dev_md, &altdevs)))
dm_list_del(&devl->list);
/* Remove info from lvmcache that came from the component dev. */
log_debug("Ignoring md component %s with PVID %s (dropping info)", dev_name(dev_drop), pvid);
lvmcache_del(info);
info = NULL;
/* Make the component dev look like it was filtered. */
cmd->filter->wipe(cmd, cmd->filter, dev_drop, NULL);
dev_drop->flags &= ~DEV_SCAN_FOUND_LABEL;
}
if (!info && dev_md) {
/*
* The info struct was from a component and was dropped
* and the actual md dev was found on initial_duplicates
* and the caller should add it to lvmcache.
*/
/* Have caller add dev_md to lvmcache. */
log_debug("Using md device %s for PVID %s.", dev_name(dev_md), pvid);
if ((devl_add = zalloc(sizeof(*devl_add)))) {
devl_add->dev = dev_md;
dm_list_add(add_cache_devs, &devl_add->list);
}
/* Remove dev_md from altdevs. */
if ((devl = _get_devl_in_device_list(dev_md, &altdevs)))
dm_list_del(&devl->list);
}
if (info && !dev_md) {
/*
* Only md component devs were found and no actual
* md dev, so drop the component from lvmcache.
*/
dev_drop = info->dev;
log_debug("Ignoring md component %s with PVID %s (dropping info)", dev_name(dev_drop), pvid);
lvmcache_del(info);
info = NULL;
/* Make the component dev look like it was filtered. */
cmd->filter->wipe(cmd, cmd->filter, dev_drop, NULL);
dev_drop->flags &= ~DEV_SCAN_FOUND_LABEL;
}
dm_list_iterate_items_safe(devl, devl_safe, &altdevs) {
/*
* The altdevs are all md components that should look
* like they were filtered, they are not in lvmcache.
*/
dev_drop = devl->dev;
log_debug("Ignoring md component %s with PVID %s (dropping duplicate)", dev_name(dev_drop), pvid);
dm_list_del(&devl->list);
cmd->filter->wipe(cmd, cmd->filter, dev_drop, NULL);
dev_drop->flags &= ~DEV_SCAN_FOUND_LABEL;
}
goto next;
}
/*
* Find the device for the pvid that's currently in lvmcache.
@@ -1149,6 +1450,18 @@ int lvmcache_label_reopen_vg_rw(struct cmd_context *cmd, const char *vgname, con
* times it can be a clue that label_scan mistakenly read the pv from an md
* component device instead of from the md device itself. So for unmatching
* sizes, we do a full md component check on the device.
*
* It might be nice to do this checking in the filter (when passes_filter is
* called after the initial read), but that doesn't work because passes_filter
* is called before _text_read so metadata/pvsummary info is not yet available
* which this function uses.
*
* The unique value of this function is that it can eliminate md components
* without there being duplicate PVs. But, there will often be duplicate PVs,
* handled by _all_md_components(), where other devs with the same pvid will be
* in _initial_duplicates. One could be the md device itself which will be
* added to lvmcache by choose_duplicates, and other duplicates that are
* components will be dropped.
*/
void lvmcache_extra_md_component_checks(struct cmd_context *cmd)
@@ -1210,7 +1523,8 @@ void lvmcache_extra_md_component_checks(struct cmd_context *cmd)
*/
if (pvsize && devsize && (pvsize != devsize))
do_check_size = 1;
if (device_hint && !strncmp(device_hint, "/dev/md", 7))
if (device_hint && !strncmp(device_hint, "/dev/md", 7) &&
(MAJOR(info->dev->dev) != cmd->dev_types->md_major))
do_check_name = 1;
if (!do_check_size && !do_check_name)
@@ -1240,11 +1554,11 @@ void lvmcache_extra_md_component_checks(struct cmd_context *cmd)
device_hint ?: "none", dev_name(dev));
if (dev_is_md_component(cmd, dev, NULL, 1)) {
log_debug("dropping PV from md component %s", dev_name(dev));
log_debug("Ignoring PV from md component %s with PVID %s (metadata %s %llu)",
dev_name(dev), dev->pvid, device_hint ?: "none", (unsigned long long)pvsize);
dev->flags &= ~DEV_SCAN_FOUND_LABEL;
/* lvmcache_del will also delete vginfo if info was last one */
lvmcache_del(info);
lvmcache_del_dev_from_duplicates(dev);
cmd->filter->wipe(cmd, cmd->filter, dev, NULL);
}
}
@@ -1572,7 +1886,17 @@ static int _lvmcache_update_vgname(struct cmd_context *cmd,
_drop_vginfo(info, info->vginfo);
if (!(vginfo = lvmcache_vginfo_from_vgid(vgid))) {
vginfo = lvmcache_vginfo_from_vgid(vgid);
if (vginfo && strcmp(vginfo->vgname, vgname)) {
log_warn("WARNING: fix duplicate VGID %s for VGs %s and %s (see vgchange -u).", vgid_dashed, vgname, vginfo->vgname);
vginfo = lvmcache_vginfo_from_vgname(vgname, NULL);
if (vginfo && memcmp(vginfo->vgid, vgid, ID_LEN)) {
log_error("Ignoring %s with conflicting VG info %s %s.", dev_name(info->dev), vgid_dashed, vgname);
return_0;
}
}
if (!vginfo) {
/*
* Create a vginfo struct for this VG and put the vginfo
* into the hash table.
@@ -1927,50 +2251,6 @@ int lvmcache_update_vgname_and_id(struct cmd_context *cmd, struct lvmcache_info
return 1;
}
/*
* 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)
{
char vgid[ID_LEN + 1] __attribute__((aligned(8))) = { 0 };
struct pv_list *pvl;
struct lvmcache_info *info;
struct lvmcache_vgsummary vgsummary = {
.vgname = vg->name,
.vgstatus = vg->status,
.system_id = vg->system_id,
.lock_type = vg->lock_type
};
memcpy(vgid, &vg->id, ID_LEN);
memcpy(vgsummary.vgid, vgid, ID_LEN);
dm_list_iterate_items(pvl, &vg->pvs) {
if ((info = lvmcache_info_from_pv_id(&pvl->pv->id, pvl->pv->dev, 0)) &&
!lvmcache_update_vgname_and_id(vg->cmd, info, &vgsummary))
return_0;
}
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

View File

@@ -84,7 +84,6 @@ void lvmcache_del_dev(struct device *dev);
int lvmcache_update_vgname_and_id(struct cmd_context *cmd, struct lvmcache_info *info,
struct lvmcache_vgsummary *vgsummary);
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);
@@ -229,4 +228,6 @@ void lvmcache_extra_md_component_checks(struct cmd_context *cmd);
unsigned int lvmcache_vg_info_count(void);
int lvmcache_pvsummary_count(const char *vgname);
#endif

View File

@@ -2041,8 +2041,6 @@ void destroy_toolcontext(struct cmd_context *cmd)
_destroy_segtypes(&cmd->segtypes);
_destroy_formats(cmd, &cmd->formats);
_destroy_filters(cmd);
if (cmd->mem)
dm_pool_destroy(cmd->mem);
devices_file_exit(cmd);
dev_cache_exit();
_destroy_dev_types(cmd);
@@ -2050,16 +2048,11 @@ void destroy_toolcontext(struct cmd_context *cmd)
if ((cft_cmdline = remove_config_tree_by_source(cmd, CONFIG_STRING)))
config_destroy(cft_cmdline);
_destroy_config(cmd);
if (cmd->cft_def_hash)
dm_hash_destroy(cmd->cft_def_hash);
if (cmd->libmem)
dm_pool_destroy(cmd->libmem);
if (cmd->pending_delete_mem)
dm_pool_destroy(cmd->pending_delete_mem);
dm_device_list_destroy(&cmd->cache_dm_devs);
#ifndef VALGRIND_POOL
if (cmd->linebuffer) {
/* Reset stream buffering to defaults */
@@ -2084,7 +2077,7 @@ void destroy_toolcontext(struct cmd_context *cmd)
free(cmd->linebuffer);
}
#endif
free(cmd);
destroy_config_context(cmd);
lvmpolld_disconnect();

View File

@@ -174,7 +174,7 @@ struct cmd_context {
unsigned activate_component:1; /* command activates component LV */
unsigned process_component_lvs:1; /* command processes also component LVs */
unsigned mirror_warn_printed:1; /* command already printed warning about non-monitored mirrors */
unsigned pvscan_cache_single:1;
unsigned expect_missing_vg_device:1; /* when reading a vg it's expected that a dev for a pv isn't found */
unsigned can_use_one_scan:1;
unsigned is_clvmd:1;
unsigned md_component_detection:1;
@@ -201,6 +201,9 @@ struct cmd_context {
unsigned ignore_device_name_mismatch:1; /* skip updating devices file names */
unsigned backup_disabled:1; /* skip repeated debug message */
unsigned event_activation:1; /* whether event_activation is set */
unsigned udevoutput:1;
unsigned online_vg_file_removed:1;
unsigned disable_dm_devs:1; /* temporarily disable use of dm devs cache */
/*
* Devices and filtering.
@@ -212,6 +215,8 @@ struct cmd_context {
const char *devicesfile; /* from --devicesfile option */
struct dm_list deviceslist; /* from --devices option, struct dm_str_list */
struct dm_list *cache_dm_devs; /* cache with UUIDs from DM_DEVICE_LIST (when available) */
/*
* Configuration.
*/

View File

@@ -1167,7 +1167,7 @@ int config_def_check(struct cft_check_handle *handle)
*vp = 0;
*rp = 0;
if (!handle->cmd->cft_def_hash) {
if (!(handle->cmd->cft_def_hash = dm_hash_create(60))) {
if (!(handle->cmd->cft_def_hash = dm_hash_create(500))) {
log_error("Failed to create configuration definition hash.");
r = 0; goto out;
}

View File

@@ -1119,16 +1119,10 @@ cfg(global_lvdisplay_shows_full_device_path_CFG, "lvdisplay_shows_full_device_pa
"was never a valid path in the /dev filesystem.\n")
cfg(global_event_activation_CFG, "event_activation", global_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_BOOL, 1, vsn(2, 3, 1), 0, 0, NULL,
"Activate LVs based on system-generated device events.\n"
"When a PV appears on the system, a system-generated uevent triggers\n"
"the lvm2-pvscan service which runs the pvscan --cache -aay command.\n"
"If the new PV completes a VG, pvscan autoactivates LVs in the VG.\n"
"When event_activation is disabled, the lvm2-activation services are\n"
"generated and run at fixed points during system startup. These\n"
"services run vgchange -aay to autoactivate LVs in VGs that happen\n"
"to be present at that point in time.\n"
"See the --setautoactivation option or the auto_activation_volume_list\n"
"setting to configure autoactivation for specific VGs or LVs.\n")
"Disable event based autoactivation commands. WARNING: setting this to zero\n"
"no longer enables static autoactivation services, and a machine may fail to\n"
"boot if this is set to zero.\n"
"Static autoactivation services are no longer provided by lvm.\n")
cfg(global_use_lvmetad_CFG, "use_lvmetad", global_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_BOOL, 0, vsn(2, 2, 93), 0, vsn(2, 3, 0), NULL,
NULL)

View File

@@ -328,4 +328,8 @@
#define DEFAULT_WWIDS_FILE "/etc/multipath/wwids"
#define PVS_ONLINE_DIR DEFAULT_RUN_DIR "/pvs_online"
#define VGS_ONLINE_DIR DEFAULT_RUN_DIR "/vgs_online"
#define PVS_LOOKUP_DIR DEFAULT_RUN_DIR "/pvs_lookup"
#endif /* _LVM_DEFAULTS_H */

View File

@@ -1246,7 +1246,7 @@ int dev_cache_init(struct cmd_context *cmd)
if (!(_cache.mem = dm_pool_create("dev_cache", 10 * 1024)))
return_0;
if (!(_cache.names = dm_hash_create(120)) ||
if (!(_cache.names = dm_hash_create(1020)) ||
!(_cache.vgid_index = dm_hash_create(30)) ||
!(_cache.lvid_index = dm_hash_create(29))) {
dm_pool_destroy(_cache.mem);
@@ -2056,12 +2056,12 @@ int setup_device(struct cmd_context *cmd, const char *devname)
}
/*
* pvscan --cache is specialized/optimized to look only at command args,
* autoactivation is specialized/optimized to look only at command args,
* so this just sets up the devices file, then individual devices are
* added to dev-cache and matched with device_ids later in pvscan.
* added to dev-cache and matched with device_ids.
*/
int setup_devices_for_pvscan_cache(struct cmd_context *cmd)
int setup_devices_for_online_autoactivation(struct cmd_context *cmd)
{
if (cmd->enable_devices_list) {
if (!_setup_devices_list(cmd))
@@ -2102,7 +2102,7 @@ int setup_devices_for_pvscan_cache(struct cmd_context *cmd)
static char *_get_devname_from_devno(struct cmd_context *cmd, dev_t devno)
{
char path[PATH_MAX];
char devname[PATH_MAX];
char devname[PATH_MAX] = { 0 };
char namebuf[NAME_LEN];
char line[1024];
int major = MAJOR(devno);
@@ -2114,6 +2114,9 @@ static char *_get_devname_from_devno(struct cmd_context *cmd, dev_t devno)
struct dirent *dirent;
FILE *fp;
if (!devno)
return NULL;
/*
* $ ls /sys/dev/block/8:0/device/block/
* sda
@@ -2125,7 +2128,7 @@ static char *_get_devname_from_devno(struct cmd_context *cmd, dev_t devno)
}
if (!(dir = opendir(path)))
return NULL;
goto try_partition;
while ((dirent = readdir(dir))) {
if (dirent->d_name[0] == '.')
@@ -2164,7 +2167,7 @@ static char *_get_devname_from_devno(struct cmd_context *cmd, dev_t devno)
}
if (devname[0]) {
log_debug("Found %s for %d:%d from sys", devname, major, minor);
log_debug("Found %s for %d:%d from sys dm", devname, major, minor);
return _strdup(devname);
}
return NULL;
@@ -2175,6 +2178,7 @@ static char *_get_devname_from_devno(struct cmd_context *cmd, dev_t devno)
* major minor #blocks name
*/
try_partition:
if (!(fp = fopen("/proc/partitions", "r")))
return NULL;
@@ -2240,3 +2244,55 @@ int setup_devno_in_dev_cache(struct cmd_context *cmd, dev_t devno)
return setup_devname_in_dev_cache(cmd, devname);
}
struct device *setup_dev_in_dev_cache(struct cmd_context *cmd, dev_t devno, const char *devname)
{
struct device *dev;
struct stat buf;
int major = (int)MAJOR(devno);
int minor = (int)MINOR(devno);
if (devname) {
if (stat(devname, &buf) < 0) {
log_error("Cannot access device %s for %d:%d.", devname, major, minor);
if (!devno)
return_NULL;
if (!(devname = _get_devname_from_devno(cmd, devno))) {
log_error("No device name found from %d:%d.", major, minor);
return_NULL;
}
if (stat(devname, &buf) < 0) {
log_error("Cannot access device %s from %d:%d.", devname, major, minor);
return_NULL;
}
}
} else {
if (!(devname = _get_devname_from_devno(cmd, devno))) {
log_error("No device name found from %d:%d.", major, minor);
return_NULL;
}
if (stat(devname, &buf) < 0) {
log_error("Cannot access device %s from %d:%d.", devname, major, minor);
return_NULL;
}
}
if (!S_ISBLK(buf.st_mode)) {
log_error("Invaild device type %s.", devname);
return_NULL;
}
if (devno && (buf.st_rdev != devno)) {
log_warn("Found %s devno %d:%d expected %d:%d.", devname,
(int)MAJOR(buf.st_rdev), (int)MINOR(buf.st_rdev), major, minor);
}
if (!_insert_dev(devname, buf.st_rdev))
return_NULL;
if (!(dev = (struct device *) dm_hash_lookup(_cache.names, devname))) {
log_error("Device lookup failed for %d:%d %s", major, minor, devname);
return_NULL;
}
return dev;
}

View File

@@ -79,9 +79,9 @@ int setup_devices_file(struct cmd_context *cmd);
int setup_devices(struct cmd_context *cmd);
int setup_device(struct cmd_context *cmd, const char *devname);
/* Normal device setup functions are split up for pvscan optimization. */
int setup_devices_for_pvscan_cache(struct cmd_context *cmd);
int setup_devices_for_online_autoactivation(struct cmd_context *cmd);
int setup_devname_in_dev_cache(struct cmd_context *cmd, const char *devname);
int setup_devno_in_dev_cache(struct cmd_context *cmd, dev_t devno);
struct device *setup_dev_in_dev_cache(struct cmd_context *cmd, dev_t devno, const char *devname);
#endif

View File

@@ -482,3 +482,74 @@ found:
return 1;
}
const char *dev_mpath_component_wwid(struct cmd_context *cmd, struct device *dev)
{
char slaves_path[PATH_MAX];
char wwid_path[PATH_MAX];
char sysbuf[PATH_MAX] = { 0 };
char *slave_name;
const char *wwid = NULL;
struct stat info;
DIR *dr;
struct dirent *de;
/* /sys/dev/block/253:7/slaves/sda/device/wwid */
if (dm_snprintf(slaves_path, sizeof(slaves_path), "%s/dev/block/%d:%d/slaves",
dm_sysfs_dir(), (int)MAJOR(dev->dev), (int)MINOR(dev->dev)) < 0) {
log_warn("Sysfs path to check mpath components is too long.");
return NULL;
}
if (stat(slaves_path, &info))
return NULL;
if (!S_ISDIR(info.st_mode)) {
log_warn("Path %s is not a directory.", slaves_path);
return NULL;
}
/* Get wwid from first component */
if (!(dr = opendir(slaves_path))) {
log_debug("Device %s has no slaves dir", dev_name(dev));
return NULL;
}
while ((de = readdir(dr))) {
if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
continue;
/* slave_name "sda" */
slave_name = de->d_name;
/* read /sys/block/sda/device/wwid */
if (dm_snprintf(wwid_path, sizeof(wwid_path), "%s/block/%s/device/wwid",
dm_sysfs_dir(), slave_name) < 0) {
log_warn("Failed to create sysfs wwid path for %s", slave_name);
continue;
}
get_sysfs_value(wwid_path, sysbuf, sizeof(sysbuf), 0);
if (!sysbuf[0])
continue;
if (strstr(sysbuf, "scsi_debug")) {
int i;
for (i = 0; i < strlen(sysbuf); i++) {
if (sysbuf[i] == ' ')
sysbuf[i] = '_';
}
}
if ((wwid = dm_pool_strdup(cmd->mem, sysbuf)))
break;
}
if (closedir(dr))
stack;
return wwid;
}

View File

@@ -63,6 +63,8 @@ int dev_is_swap(struct cmd_context *cmd, struct device *dev, uint64_t *signature
int dev_is_luks(struct cmd_context *cmd, struct device *dev, uint64_t *signature, int full);
int dasd_is_cdl_formatted(struct device *dev);
const char *dev_mpath_component_wwid(struct cmd_context *cmd, struct device *dev);
int dev_is_lvm1(struct device *dev, char *buf, int buflen);
int dev_is_pool(struct device *dev, char *buf, int buflen);

View File

@@ -43,6 +43,7 @@ static const dev_known_type_t _dev_known_types[] = {
{"ubd", 16, "User-mode virtual block device"},
{"ataraid", 16, "ATA Raid"},
{"drbd", 16, "Distributed Replicated Block Device (DRBD)"},
{"rbd", 16, "Ceph rados object as a Linux block device"},
{"emcpower", 16, "EMC Powerpath"},
{"power2", 16, "EMC Powerpath"},
{"i2o_block", 16, "i2o Block Disk"},

View File

@@ -74,6 +74,8 @@ void unlink_searched_devnames(struct cmd_context *cmd)
if (unlink(_searched_file))
log_debug("unlink %s errno %d", _searched_file, errno);
else
log_debug("unlink %s", _searched_file);
}
static int _searched_devnames_exists(struct cmd_context *cmd)
@@ -241,7 +243,7 @@ static int _dm_uuid_has_prefix(char *sysbuf, const char *prefix)
}
/* the dm uuid uses the wwid of the underlying dev */
static int _dev_has_mpath_uuid(struct cmd_context *cmd, struct device *dev, const char **idname_out)
int dev_has_mpath_uuid(struct cmd_context *cmd, struct device *dev, const char **idname_out)
{
char sysbuf[PATH_MAX] = { 0 };
const char *idname;
@@ -302,6 +304,7 @@ const char *device_id_system_read(struct cmd_context *cmd, struct device *dev, u
{
char sysbuf[PATH_MAX] = { 0 };
const char *idname = NULL;
int i;
if (idtype == DEV_ID_TYPE_SYS_WWID) {
read_sys_block(cmd, dev, "device/wwid", sysbuf, sizeof(sysbuf));
@@ -309,10 +312,6 @@ const char *device_id_system_read(struct cmd_context *cmd, struct device *dev, u
if (!sysbuf[0])
read_sys_block(cmd, dev, "wwid", sysbuf, sizeof(sysbuf));
/* scsi_debug wwid begins "t10.Linux scsi_debug ..." */
if (strstr(sysbuf, "scsi_debug"))
sysbuf[0] = '\0';
/* qemu wwid begins "t10.ATA QEMU HARDDISK ..." */
if (strstr(sysbuf, "QEMU HARDDISK"))
sysbuf[0] = '\0';
@@ -353,6 +352,11 @@ const char *device_id_system_read(struct cmd_context *cmd, struct device *dev, u
return idname;
}
for (i = 0; i < strlen(sysbuf); i++) {
if (isblank(sysbuf[i]) || isspace(sysbuf[i]) || iscntrl(sysbuf[i]))
sysbuf[i] = '_';
}
if (!sysbuf[0])
goto_bad;
@@ -361,7 +365,6 @@ const char *device_id_system_read(struct cmd_context *cmd, struct device *dev, u
return idname;
bad:
log_debug("No idtype %s for %s", idtype_to_str(idtype), dev_name(dev));
return NULL;
}
@@ -373,20 +376,40 @@ static int _dev_has_stable_id(struct cmd_context *cmd, struct device *dev)
{
char sysbuf[PATH_MAX] = { 0 };
struct dev_id *id;
const char *idname;
/*
* An idtype other than DEVNAME is stable, i.e. it doesn't change after
* reboot or device reattach.
* An id on dev->ids with idtype set and !idname means that idtype does
* not exist for the dev. (Optimization to avoid repeated negative
* system_read.)
*/
dm_list_iterate_items(id, &dev->ids) {
if (id->idtype != DEV_ID_TYPE_DEVNAME)
if ((id->idtype != DEV_ID_TYPE_DEVNAME) && id->idname)
return 1;
}
if (read_sys_block(cmd, dev, "device/wwid", sysbuf, sizeof(sysbuf)))
return 1;
/*
* Use device_id_system_read() instead of read_sys_block() when
* system_read ignores some values from sysfs.
*/
if (read_sys_block(cmd, dev, "wwid", sysbuf, sizeof(sysbuf)))
if ((idname = device_id_system_read(cmd, dev, DEV_ID_TYPE_SYS_WWID))) {
free((void*)idname);
return 1;
}
if (read_sys_block(cmd, dev, "device/serial", sysbuf, sizeof(sysbuf)))
if ((idname = device_id_system_read(cmd, dev, DEV_ID_TYPE_SYS_SERIAL))) {
free((void*)idname);
return 1;
}
if ((MAJOR(dev->dev) == cmd->dev_types->loop_major) &&
(idname = device_id_system_read(cmd, dev, DEV_ID_TYPE_LOOP_FILE))) {
free((void*)idname);
return 1;
}
if ((MAJOR(dev->dev) == cmd->dev_types->device_mapper_major)) {
if (!read_sys_block(cmd, dev, "dm/uuid", sysbuf, sizeof(sysbuf)))
@@ -404,9 +427,6 @@ static int _dev_has_stable_id(struct cmd_context *cmd, struct device *dev)
read_sys_block(cmd, dev, "md/uuid", sysbuf, sizeof(sysbuf)))
return 1;
if ((MAJOR(dev->dev) == cmd->dev_types->loop_major) &&
read_sys_block(cmd, dev, "loop/backing_file", sysbuf, sizeof(sysbuf)))
return 1;
out:
/* DEV_ID_TYPE_DEVNAME would be used for this dev. */
return 0;
@@ -671,6 +691,9 @@ int device_ids_write(struct cmd_context *cmd)
cmd->enable_devices_file = 1;
}
if (test_mode())
return 1;
if (_devices_file_version[0]) {
if (sscanf(_devices_file_version, "%u.%u.%u", &df_major, &df_minor, &df_counter) != 3) {
/* don't update a file we can't parse */
@@ -778,9 +801,8 @@ static void _device_ids_update_try(struct cmd_context *cmd)
{
int held = 0;
/* Defer updates to non-pvscan-cache commands. */
if (cmd->pvscan_cache_single) {
log_print("pvscan[%d] skip updating devices file.", getpid());
if (cmd->expect_missing_vg_device) {
log_print("Devices file update skipped.");
return;
}
@@ -928,6 +950,10 @@ int device_id_add(struct cmd_context *cmd, struct device *dev, const char *pvid_
struct dev_use *du, *update_du = NULL, *du_dev, *du_pvid, *du_devname, *du_devid;
struct dev_id *id;
int found_id = 0;
int part = 0;
if (!dev_get_partition_number(dev, &part))
return_0;
/*
* When enable_devices_file=0 and pending_devices_file=1 we let
@@ -946,10 +972,6 @@ int device_id_add(struct cmd_context *cmd, struct device *dev, const char *pvid_
*/
memcpy(&pvid, pvid_arg, ID_LEN);
du_dev = get_du_for_dev(cmd, dev);
du_pvid = get_du_for_pvid(cmd, pvid);
du_devname = _get_du_for_devname(cmd, dev_name(dev));
/*
* Choose the device_id type for the device being added.
*
@@ -981,7 +1003,7 @@ int device_id_add(struct cmd_context *cmd, struct device *dev, const char *pvid_
}
if (MAJOR(dev->dev) == cmd->dev_types->device_mapper_major) {
if (_dev_has_mpath_uuid(cmd, dev, &idname)) {
if (dev_has_mpath_uuid(cmd, dev, &idname)) {
idtype = DEV_ID_TYPE_MPATH_UUID;
goto id_done;
}
@@ -1065,6 +1087,9 @@ id_done:
idtype = 0;
/*
* "dev" is the device we are adding.
* "id" is the device_id it's using, set in dev->id.
*
* Update the cmd->use_devices list for the new device. The
* use_devices list will be used to update the devices file.
*
@@ -1076,23 +1101,57 @@ id_done:
* those other entries to fix any incorrect info.
*/
/* Is there already an entry matched to this device? */
du_dev = get_du_for_dev(cmd, dev);
/* Is there already an entry matched to this device's pvid? */
du_pvid = get_du_for_pvid(cmd, pvid);
/* Is there already an entry using this device's name? */
du_devname = _get_du_for_devname(cmd, dev_name(dev));
/* Is there already an entry using the device_id for this device? */
du_devid = _get_du_for_device_id(cmd, id->idtype, id->idname);
if (du_dev)
log_debug("device_id_add %s pvid %s matches du_dev %p dev %s",
log_debug("device_id_add %s pvid %s matches entry %p dev %s",
dev_name(dev), pvid, du_dev, dev_name(du_dev->dev));
if (du_pvid)
log_debug("device_id_add %s pvid %s matches du_pvid %p dev %s pvid %s",
log_debug("device_id_add %s pvid %s matches entry %p dev %s with same pvid %s",
dev_name(dev), pvid, du_pvid, du_pvid->dev ? dev_name(du_pvid->dev) : ".",
du_pvid->pvid);
if (du_devid)
log_debug("device_id_add %s pvid %s matches du_devid %p dev %s pvid %s",
log_debug("device_id_add %s pvid %s matches entry %p dev %s with same device_id %d %s",
dev_name(dev), pvid, du_devid, du_devid->dev ? dev_name(du_devid->dev) : ".",
du_devid->pvid);
du_devid->idtype, du_devid->idname);
if (du_devname)
log_debug("device_id_add %s pvid %s matches du_devname %p dev %s pvid %s",
log_debug("device_id_add %s pvid %s matches entry %p dev %s with same devname %s",
dev_name(dev), pvid, du_devname, du_devname->dev ? dev_name(du_devname->dev) : ".",
du_devname->pvid);
du_devname->devname);
if (du_pvid && (du_pvid->dev != dev))
log_warn("WARNING: adding device %s with PVID %s which is already used for %s.",
dev_name(dev), pvid, du_pvid->dev ? dev_name(du_pvid->dev) : "missing device");
if (du_devid && (du_devid->dev != dev)) {
if (!du_devid->dev) {
log_warn("WARNING: adding device %s with idname %s which is already used for missing device.",
dev_name(dev), id->idname);
} else {
int ret1, ret2;
dev_t devt1, devt2;
/* Check if both entries are partitions of the same device. */
ret1 = dev_get_primary_dev(cmd->dev_types, dev, &devt1);
ret2 = dev_get_primary_dev(cmd->dev_types, du_devid->dev, &devt2);
if ((ret1 == 2) && (ret2 == 2) && (devt1 == devt2)) {
log_debug("Using separate entries for partitions of same device %s part %d %s part %d.",
dev_name(dev), part, dev_name(du_devid->dev), du_devid->part);
} else {
log_warn("WARNING: adding device %s with idname %s which is already used for %s.",
dev_name(dev), id->idname, dev_name(du_devid->dev));
}
}
}
/*
* If one of the existing entries (du_dev, du_pvid, du_devid, du_devname)
@@ -1105,29 +1164,6 @@ id_done:
dm_list_del(&update_du->list);
update_matching_kind = "device";
update_matching_name = dev_name(dev);
if (du_devid && (du_devid != du_dev)) {
log_warn("WARNING: device %s (%s) and %s (%s) have duplicate device ID.",
dev_name(dev), id->idname,
(du_pvid && du_pvid->dev) ? dev_name(du_pvid->dev) : "none",
du_pvid ? du_pvid->idname : "");
}
if (du_pvid && (du_pvid != du_dev)) {
log_warn("WARNING: device %s (%s) and %s (%s) have duplicate PVID %s",
dev_name(dev), id->idname,
du_pvid->dev ? dev_name(du_pvid->dev) : "none", du_pvid->idname,
pvid);
}
if (du_devname && (du_devname != du_dev)) {
/* clear devname in another entry with our devname */
log_warn("Devices file PVID %s clearing wrong DEVNAME %s.",
du_devname->pvid, du_devname->devname);
free(du_devname->devname);
du_devname->devname = NULL;
}
} else if (du_pvid) {
/*
* If the device_id of the existing entry for PVID is the same
@@ -1147,11 +1183,6 @@ id_done:
update_matching_kind = "PVID";
update_matching_name = pvid;
} else {
log_warn("WARNING: device %s (%s) and %s (%s) have duplicate PVID %s",
dev_name(dev), id->idname,
du_pvid->dev ? dev_name(du_pvid->dev) : "none", du_pvid->idname,
pvid);
if (!cmd->current_settings.yes &&
yes_no_prompt("Add device with duplicate PV to devices file?") == 'n') {
log_print("Device not added.");
@@ -1159,21 +1190,6 @@ id_done:
return 1;
}
}
if (du_devid && (du_devid != du_pvid)) {
/* warn about another entry using the same device_id */
log_warn("WARNING: duplicate device_id %s for PVIDs %s %s",
du_devid->idname, du_devid->pvid, du_pvid->pvid);
}
if (du_devname && (du_devname != du_pvid)) {
/* clear devname in another entry with our devname */
log_warn("Devices file PVID %s clearing wrong DEVNAME %s.",
du_devname->pvid, du_devname->devname);
free(du_devname->devname);
du_devname->devname = NULL;
}
} else if (du_devid) {
/*
* Do we create a new du or update the existing du?
@@ -1188,64 +1204,13 @@ id_done:
* the same device_id (create a new du for dev.)
* If not, then update the existing du_devid.
*/
if (du_devid->dev != dev)
check_idname = device_id_system_read(cmd, du_devid->dev, id->idtype);
if (check_idname && !strcmp(check_idname, id->idname)) {
int ret1, ret2;
dev_t devt1, devt2;
/*
* two different devices have the same device_id,
* create a new du for the device being added
*/
/* dev_is_partitioned() the dev open to read it. */
if (!label_scan_open(du_devid->dev))
log_warn("Cannot open %s", dev_name(du_devid->dev));
if (dev_is_partitioned(cmd, du_devid->dev)) {
/* Check if existing entry is whole device and new entry is a partition of it. */
ret1 = dev_get_primary_dev(cmd->dev_types, dev, &devt1);
if ((ret1 == 2) && (devt1 == du_devid->dev->dev))
log_warn("Remove partitioned device %s from devices file.", dev_name(du_devid->dev));
} else {
/* Check if both entries are partitions of the same device. */
ret1 = dev_get_primary_dev(cmd->dev_types, dev, &devt1);
ret2 = dev_get_primary_dev(cmd->dev_types, du_devid->dev, &devt2);
if ((ret1 == 2) && (ret2 == 2) && (devt1 == devt2)) {
log_warn("Partitions %s %s have same device_id %s",
dev_name(dev), dev_name(du_devid->dev), id->idname);
} else {
log_warn("Duplicate device_id %s %s for %s and %s",
idtype_to_str(id->idtype), check_idname,
dev_name(dev), dev_name(du_devid->dev));
}
}
} else {
if (du_devid->dev == dev) {
/* update the existing entry with matching devid */
update_du = du_devid;
dm_list_del(&update_du->list);
update_matching_kind = "device_id";
update_matching_name = id->idname;
}
if (du_devname && (du_devname != du_devid)) {
/* clear devname in another entry with our devname */
log_warn("Devices file PVID %s clearing wrong DEVNAME %s",
du_devname->pvid, du_devname->devname);
free(du_devname->devname);
du_devname->devname = NULL;
}
} else if (du_devname) {
/* clear devname in another entry with our devname */
log_warn("Devices file PVID %s clearing wrong DEVNAME %s",
du_devname->pvid, du_devname->devname);
free(du_devname->devname);
du_devname->devname = NULL;
}
free((void *)check_idname);
@@ -1360,6 +1325,10 @@ void device_id_update_vg_uuid(struct cmd_context *cmd, struct volume_group *vg,
static int _idtype_compatible_with_major_number(struct cmd_context *cmd, int idtype, int major)
{
/* devname can be used with any kind of device */
if (idtype == DEV_ID_TYPE_DEVNAME)
return 1;
if (idtype == DEV_ID_TYPE_MPATH_UUID ||
idtype == DEV_ID_TYPE_CRYPT_UUID ||
idtype == DEV_ID_TYPE_LVMLV_UUID)
@@ -1388,6 +1357,43 @@ static int _idtype_compatible_with_major_number(struct cmd_context *cmd, int idt
return 1;
}
static int _match_dm_devnames(struct cmd_context *cmd, struct device *dev,
struct dev_id *id, struct dev_use *du)
{
struct stat buf;
if (MAJOR(dev->dev) != cmd->dev_types->device_mapper_major)
return 0;
if (id->idname && du->idname && !strcmp(id->idname, du->idname))
return 1;
if (du->idname && !strcmp(du->idname, dev_name(dev))) {
log_debug("Match device_id %s %s to %s: ignoring idname %s",
idtype_to_str(du->idtype), du->idname, dev_name(dev), id->idname ?: ".");
return 1;
}
if (!du->idname)
return 0;
/* detect that a du entry is for a dm device */
if (!strncmp(du->idname, "/dev/dm-", 8) || !strncmp(du->idname, "/dev/mapper/", 12)) {
if (stat(du->idname, &buf))
return 0;
if ((MAJOR(buf.st_rdev) == cmd->dev_types->device_mapper_major) &&
(MINOR(buf.st_rdev) == MINOR(dev->dev))) {
log_debug("Match device_id %s %s to %s: using other dm name, ignoring %s",
idtype_to_str(du->idtype), du->idname, dev_name(dev), id->idname ?: ".");
return 1;
}
}
return 0;
}
/*
* check for dev->ids entry with du->idtype, if found compare it,
* if not, system_read of this type and add entry to dev->ids, compare it.
@@ -1400,43 +1406,74 @@ static int _match_du_to_dev(struct cmd_context *cmd, struct dev_use *du, struct
const char *idname;
int part;
if (!du->idname || !du->idtype)
/*
* The idname will be removed from an entry with devname type when the
* devname is read and found to hold a different PVID than the PVID in
* the entry. At that point we only have the PVID and no known
* location for it.
*/
if (!du->idname || !du->idtype) {
/*
log_debug("Mismatch device_id %s %s %s to %s",
du->idtype ? idtype_to_str(du->idtype) : "idtype_missing",
du->idname ? du->idname : "idname_missing",
du->devname ? du->devname : "devname_missing",
dev_name(dev));
*/
return 0;
}
/*
* Some idtypes can only match devices with a specific major number,
* so we can skip trying to match certain du entries based simply on
* the major number of dev.
*/
if (!_idtype_compatible_with_major_number(cmd, du->idtype, (int)MAJOR(dev->dev)))
if (!_idtype_compatible_with_major_number(cmd, du->idtype, (int)MAJOR(dev->dev))) {
/*
log_debug("Mismatch device_id %s %s to %s: wrong major",
idtype_to_str(du->idtype), du->idname ?: ".", dev_name(dev));
*/
return 0;
}
if (!dev_get_partition_number(dev, &part)) {
log_debug("compare %s failed to get dev partition", dev_name(dev));
/*
log_debug("Mismatch device_id %s %s to %s: no partition",
idtype_to_str(du->idtype), du->idname ?: ".", dev_name(dev));
*/
return 0;
}
if (part != du->part) {
/*
log_debug("compare mis %s %s part %d to %s part %d",
idtype_to_str(du->idtype), du->idname ?: ".", du->part, dev_name(dev), part);
log_debug("Mismatch device_id %s %s to %s: wrong partition %d vs %d",
idtype_to_str(du->idtype), du->idname ?: ".", dev_name(dev), du->part, part);
*/
return 0;
}
dm_list_iterate_items(id, &dev->ids) {
if (id->idtype == du->idtype) {
if (id->idname && !strcmp(id->idname, du->idname)) {
if ((id->idtype == DEV_ID_TYPE_DEVNAME) && _match_dm_devnames(cmd, dev, id, du)) {
/* dm devs can have differing names that we know still match */
du->dev = dev;
dev->id = id;
dev->flags |= DEV_MATCHED_USE_ID;
log_debug("Match device_id %s %s to %s: dm names",
idtype_to_str(du->idtype), du->idname, dev_name(dev));
return 1;
} else if (id->idname && !strcmp(id->idname, du->idname)) {
du->dev = dev;
dev->id = id;
dev->flags |= DEV_MATCHED_USE_ID;
log_debug("Match device_id %s %s to %s",
idtype_to_str(du->idtype), du->idname, dev_name(dev));
return 1;
} else {
/*
log_debug("compare mis %s %s to %s %s",
idtype_to_str(du->idtype), du->idname ?: ".", dev_name(dev),
((id->idtype != DEV_ID_TYPE_DEVNAME) && id->idname) ? id->idname : "");
log_debug("Mismatch device_id %s %s to %s: idname %s",
idtype_to_str(du->idtype), du->idname ?: ".", dev_name(dev), id->idname ?: ":");
*/
return 0;
}
@@ -1456,7 +1493,7 @@ static int _match_du_to_dev(struct cmd_context *cmd, struct dev_use *du, struct
id->dev = dev;
dm_list_add(&dev->ids, &id->list);
/*
log_debug("compare mis %s %s to %s no idtype",
log_debug("Mismatch device_id %s %s to %s: no idtype",
idtype_to_str(du->idtype), du->idname ?: ".", dev_name(dev));
*/
return 0;
@@ -1481,9 +1518,8 @@ static int _match_du_to_dev(struct cmd_context *cmd, struct dev_use *du, struct
}
/*
log_debug("compare mis %s %s to %s %s",
idtype_to_str(du->idtype), du->idname ?: ".", dev_name(dev),
((id->idtype != DEV_ID_TYPE_DEVNAME) && id->idname) ? id->idname : "");
log_debug("Mismatch device_id %s %s to %s: idname %s",
idtype_to_str(du->idtype), du->idname ?: ".", dev_name(dev), idname);
*/
return 0;
}
@@ -1907,6 +1943,14 @@ void device_ids_validate(struct cmd_context *cmd, struct dm_list *scanned_devs,
*device_ids_invalid = 1;
}
/*
* When a new devname/pvid mismatch is discovered, a new search for the
* pvid should be permitted (searched_devnames may exist to suppress
* searching for other pvids.)
*/
if (update_file)
unlink_searched_devnames(cmd);
/* FIXME: for wrong devname cases, wait to write new until device_ids_find_renamed_devs? */
/*
@@ -1975,12 +2019,19 @@ void device_ids_find_renamed_devs(struct cmd_context *cmd, struct dm_list *dev_l
search_auto = !strcmp(cmd->search_for_devnames, "auto");
dm_list_iterate_items(du, &cmd->use_devices) {
if (du->dev)
continue;
if (!du->pvid)
continue;
if (du->idtype != DEV_ID_TYPE_DEVNAME)
continue;
/*
* if the old incorrect devname is now a device that's
* filtered and not scanned, e.g. an mpath component,
* then we want to look for the pvid on a new device.
*/
if (du->dev && !du->dev->filtered_flags)
continue;
if (!(dil = dm_pool_zalloc(cmd->mem, sizeof(*dil))))
continue;
@@ -2005,6 +2056,11 @@ void device_ids_find_renamed_devs(struct cmd_context *cmd, struct dm_list *dev_l
* the searched file, so a subsequent lvm command will do the search
* again. In future perhaps we could add a policy to automatically
* remove a devices file entry that's not been found for some time.
*
* TODO: like the hint file, add a hash of all devnames to the searched
* file so it can be ignored and removed if the devs/hash change.
* If hints are enabled, the hints invalidation could also remove the
* searched file.
*/
if (_searched_devnames_exists(cmd)) {
log_debug("Search for PVIDs skipped for %s", _searched_file);
@@ -2071,6 +2127,12 @@ void device_ids_find_renamed_devs(struct cmd_context *cmd, struct dm_list *dev_l
*
* TODO: in auto mode should we look in other non-system
* devices files and skip any devs included in those?
*
* Note that a user can override a stable id type and use
* devname for a device's id, in which case this optimization
* can prevent a search from finding a renamed dev. So, if a
* user forces a devname id, then they should probably also
* set search_for_devnames=all.
*/
if (search_auto && _dev_has_stable_id(cmd, dev)) {
other_idtype++;
@@ -2180,7 +2242,8 @@ void device_ids_find_renamed_devs(struct cmd_context *cmd, struct dm_list *dev_l
continue;
}
log_warn("Devices file PVID %s updating IDNAME to %s.", dev->pvid, devname);
if (!noupdate)
log_warn("Devices file PVID %s updating IDNAME to %s.", dev->pvid, devname);
free(du->idname);
free(du->devname);

View File

@@ -56,4 +56,6 @@ void unlink_searched_devnames(struct cmd_context *cmd);
int read_sys_block(struct cmd_context *cmd, struct device *dev, const char *suffix, char *sysbuf, int sysbufsize);
int dev_has_mpath_uuid(struct cmd_context *cmd, struct device *dev, const char **idname_out);
#endif

507
lib/device/online.c Normal file
View File

@@ -0,0 +1,507 @@
/*
* Copyright (C) 2021 Red Hat, Inc. All rights reserved.
*
* This file is part of LVM2.
*
* 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 Lesser General Public License v.2.1.
*
* You should have received a copy of the GNU Lesser 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
*/
#include "base/memory/zalloc.h"
#include "lib/misc/lib.h"
#include "lib/commands/toolcontext.h"
#include "lib/device/device.h"
#include "lib/device/device_id.h"
#include "lib/device/online.h"
#include <dirent.h>
/*
* file contains:
* <major>:<minor>\n
* vg:<vgname>\n
* dev:<devname>\n\0
*
* It's possible that vg and dev may not exist.
*/
static int _copy_pvid_file_field(const char *field, char *buf, int bufsize, char *out, int outsize)
{
char *p;
int i = 0;
if (!(p = strstr(buf, field)))
return 0;
p += strlen(field);
while (1) {
if (*p == '\n')
break;
if (*p == '\0')
break;
if (p >= (buf + bufsize))
return 0;
if (i >= outsize-1)
return 0;
out[i] = *p;
i++;
p++;
}
return i ? 1 : 0;
}
#define MAX_PVID_FILE_SIZE 512
int online_pvid_file_read(char *path, int *major, int *minor, char *vgname, char *devname)
{
char buf[MAX_PVID_FILE_SIZE] = { 0 };
int fd, rv;
fd = open(path, O_RDONLY);
if (fd < 0) {
log_warn("Failed to open %s", path);
return 0;
}
rv = read(fd, buf, sizeof(buf) - 1);
if (close(fd))
log_sys_debug("close", path);
if (!rv || rv < 0) {
log_warn("No info in %s", path);
return 0;
}
buf[rv] = 0; /* \0 terminated buffer */
if (sscanf(buf, "%d:%d", major, minor) != 2) {
log_warn("No device numbers in %s", path);
return 0;
}
if (vgname) {
if (!strstr(buf, "vg:")) {
log_debug("No vgname in %s", path);
vgname[0] = '\0';
goto copy_dev;
}
if (!_copy_pvid_file_field("vg:", buf, MAX_PVID_FILE_SIZE, vgname, NAME_LEN)) {
log_warn("Ignoring invalid vg field in %s", path);
vgname[0] = '\0';
goto copy_dev;
}
if (!validate_name(vgname)) {
log_warn("Ignoring invalid vgname in %s (%s)", path, vgname);
vgname[0] = '\0';
goto copy_dev;
}
}
copy_dev:
if (devname) {
if (!strstr(buf, "dev:")) {
log_debug("No devname in %s", path);
devname[0] = '\0';
goto out;
}
if (!_copy_pvid_file_field("dev:", buf, MAX_PVID_FILE_SIZE, devname, NAME_LEN)) {
log_warn("Ignoring invalid devname field in %s", path);
devname[0] = '\0';
goto out;
}
if (strncmp(devname, "/dev/", 5)) {
log_warn("Ignoring invalid devname in %s (%s)", path, devname);
devname[0] = '\0';
goto out;
}
}
out:
return 1;
}
void free_po_list(struct dm_list *list)
{
struct pv_online *po, *po2;
dm_list_iterate_items_safe(po, po2, list) {
dm_list_del(&po->list);
free(po);
}
}
int get_pvs_online(struct dm_list *pvs_online, const char *vgname)
{
char path[PATH_MAX];
char file_vgname[NAME_LEN];
char file_devname[NAME_LEN];
DIR *dir;
struct dirent *de;
struct pv_online *po;
int file_major = 0, file_minor = 0;
if (!(dir = opendir(PVS_ONLINE_DIR)))
return 0;
while ((de = readdir(dir))) {
if (de->d_name[0] == '.')
continue;
if (strlen(de->d_name) != ID_LEN)
continue;
memset(path, 0, sizeof(path));
snprintf(path, sizeof(path), "%s/%s", PVS_ONLINE_DIR, de->d_name);
file_major = 0;
file_minor = 0;
memset(file_vgname, 0, sizeof(file_vgname));
memset(file_devname, 0, sizeof(file_devname));
if (!online_pvid_file_read(path, &file_major, &file_minor, file_vgname, file_devname))
continue;
if (vgname && strcmp(file_vgname, vgname))
continue;
if (!(po = zalloc(sizeof(*po))))
continue;
memcpy(po->pvid, de->d_name, ID_LEN);
if (file_major || file_minor)
po->devno = MKDEV(file_major, file_minor);
if (file_vgname[0])
strncpy(po->vgname, file_vgname, NAME_LEN);
if (file_devname[0])
strncpy(po->devname, file_devname, NAME_LEN);
log_debug("Found PV online %s for VG %s %s", path, vgname, file_devname);
dm_list_add(pvs_online, &po->list);
}
if (closedir(dir))
log_sys_debug("closedir", PVS_ONLINE_DIR);
log_debug("Found PVs online %d for %s", dm_list_size(pvs_online), vgname ?: "all");
return 1;
}
/*
* When a PV goes offline, remove the vg online file for that VG
* (even if other PVs for the VG are still online). This means
* that the vg will be activated again when it becomes complete.
*/
void online_vg_file_remove(const char *vgname)
{
char path[PATH_MAX];
if (dm_snprintf(path, sizeof(path), "%s/%s", VGS_ONLINE_DIR, vgname) < 0) {
log_error("Path %s/%s is too long.", VGS_ONLINE_DIR, vgname);
return;
}
log_debug("Unlink vg online: %s", path);
if (unlink(path) && (errno != ENOENT))
log_sys_debug("unlink", path);
}
int online_vg_file_create(struct cmd_context *cmd, const char *vgname)
{
char path[PATH_MAX];
int fd;
if (dm_snprintf(path, sizeof(path), "%s/%s", VGS_ONLINE_DIR, vgname) < 0) {
log_error_pvscan(cmd, "Path %s/%s is too long.", VGS_ONLINE_DIR, vgname);
return 0;
}
log_debug("Create vg online: %s", path);
fd = open(path, O_CREAT | O_EXCL | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR);
if (fd < 0) {
log_debug("Failed to create %s: %d", path, errno);
return 0;
}
/* We don't care about syncing, these files are not even persistent. */
if (close(fd))
log_sys_debug("close", path);
return 1;
}
int online_pvid_file_create(struct cmd_context *cmd, struct device *dev, const char *vgname)
{
char path[PATH_MAX];
char buf[MAX_PVID_FILE_SIZE] = { 0 };
char file_vgname[NAME_LEN];
char file_devname[NAME_LEN];
char devname[NAME_LEN];
int devnamelen;
int file_major = 0, file_minor = 0;
int major, minor;
int fd;
int rv;
int len;
int len1 = 0;
int len2 = 0;
int len3 = 0;
major = (int)MAJOR(dev->dev);
minor = (int)MINOR(dev->dev);
if (dm_snprintf(path, sizeof(path), "%s/%s", PVS_ONLINE_DIR, dev->pvid) < 0) {
log_error_pvscan(cmd, "Path %s/%s is too long.", PVS_ONLINE_DIR, dev->pvid);
return 0;
}
if ((len1 = dm_snprintf(buf, sizeof(buf), "%d:%d\n", major, minor)) < 0) {
log_error_pvscan(cmd, "Cannot create online file path for %s %d:%d.", dev_name(dev), major, minor);
return 0;
}
if (vgname) {
if ((len2 = dm_snprintf(buf + len1, sizeof(buf) - len1, "vg:%s\n", vgname)) < 0) {
log_print("Incomplete online file for %s %d:%d vg %s.", dev_name(dev), major, minor, vgname);
/* can still continue without vgname */
len2 = 0;
}
}
devnamelen = dm_snprintf(devname, sizeof(devname), "%s", dev_name(dev));
if ((devnamelen > 5) && (devnamelen < NAME_LEN-1)) {
if ((len3 = dm_snprintf(buf + len1 + len2, sizeof(buf) - len1 - len2, "dev:%s\n", devname)) < 0) {
log_print("Incomplete devname in online file for %s.", dev_name(dev));
/* can continue without devname */
len3 = 0;
}
}
len = len1 + len2 + len3;
log_debug("Create pv online: %s %d:%d %s", path, major, minor, dev_name(dev));
fd = open(path, O_CREAT | O_EXCL | O_RDWR, S_IRUSR | S_IWUSR);
if (fd < 0) {
if (errno == EEXIST)
goto check_duplicate;
log_error_pvscan(cmd, "Failed to create online file for %s path %s error %d", dev_name(dev), path, errno);
return 0;
}
while (len > 0) {
rv = write(fd, buf, len);
if (rv < 0) {
/* file exists so it still works in part */
log_warn("Cannot write online file for %s to %s error %d",
dev_name(dev), path, errno);
if (close(fd))
log_sys_debug("close", path);
return 1;
}
len -= rv;
}
/* We don't care about syncing, these files are not even persistent. */
if (close(fd))
log_sys_debug("close", path);
return 1;
check_duplicate:
/*
* If a PVID online file already exists for this PVID, check if the
* file contains a different device number, and if so we may have a
* duplicate PV.
*
* FIXME: disable autoactivation of the VG somehow?
* The VG may or may not already be activated when a dupicate appears.
* Perhaps write a new field in the pv online or vg online file?
*/
memset(file_vgname, 0, sizeof(file_vgname));
memset(file_devname, 0, sizeof(file_devname));
online_pvid_file_read(path, &file_major, &file_minor, file_vgname, file_devname);
if ((file_major == major) && (file_minor == minor)) {
log_debug("Existing online file for %d:%d", major, minor);
return 1;
}
/* Don't know how vgname might not match, but it's not good so fail. */
if ((file_major != major) || (file_minor != minor))
log_error_pvscan(cmd, "PV %s %d:%d is duplicate for PVID %s on %d:%d %s.",
dev_name(dev), major, minor, dev->pvid, file_major, file_minor, file_devname);
if (file_vgname[0] && vgname && strcmp(file_vgname, vgname))
log_error_pvscan(cmd, "PV %s has unexpected VG %s vs %s.",
dev_name(dev), vgname, file_vgname);
return 0;
}
int online_pvid_file_exists(const char *pvid)
{
char path[PATH_MAX] = { 0 };
struct stat buf;
int rv;
if (dm_snprintf(path, sizeof(path), "%s/%s", PVS_ONLINE_DIR, pvid) < 0) {
log_debug(INTERNAL_ERROR "Path %s/%s is too long.", PVS_ONLINE_DIR, pvid);
return 0;
}
log_debug("Check pv online: %s", path);
rv = stat(path, &buf);
if (!rv) {
log_debug("Check pv online %s: yes", pvid);
return 1;
}
log_debug("Check pv online %s: no", pvid);
return 0;
}
int get_pvs_lookup(struct dm_list *pvs_online, const char *vgname)
{
char lookup_path[PATH_MAX] = { 0 };
char path[PATH_MAX] = { 0 };
char line[64];
char pvid[ID_LEN + 1] __attribute__((aligned(8))) = { 0 };
char file_vgname[NAME_LEN];
char file_devname[NAME_LEN];
struct pv_online *po;
int file_major = 0, file_minor = 0;
FILE *fp;
if (dm_snprintf(lookup_path, sizeof(path), "%s/%s", PVS_LOOKUP_DIR, vgname) < 0)
return_0;
if (!(fp = fopen(lookup_path, "r")))
return_0;
while (fgets(line, sizeof(line), fp)) {
memcpy(pvid, line, ID_LEN);
if (strlen(pvid) != ID_LEN)
goto_bad;
memset(path, 0, sizeof(path));
snprintf(path, sizeof(path), "%s/%s", PVS_ONLINE_DIR, pvid);
file_major = 0;
file_minor = 0;
memset(file_vgname, 0, sizeof(file_vgname));
memset(file_devname, 0, sizeof(file_devname));
if (!online_pvid_file_read(path, &file_major, &file_minor, file_vgname, file_devname))
goto_bad;
/*
* PVs without metadata will not have a vgname in their pvid
* file, but the purpose of using the lookup file is that we
* know the PV is for this VG even without the pvid vgname
* field.
*/
if (vgname && file_vgname[0] && strcmp(file_vgname, vgname)) {
/* Should never happen */
log_error("Incorrect VG lookup file %s PVID %s %s.", vgname, pvid, file_vgname);
goto_bad;
}
if (!(po = zalloc(sizeof(*po))))
goto_bad;
memcpy(po->pvid, pvid, ID_LEN);
if (file_major || file_minor)
po->devno = MKDEV(file_major, file_minor);
if (file_vgname[0])
strncpy(po->vgname, file_vgname, NAME_LEN-1);
if (file_devname[0])
strncpy(po->devname, file_devname, NAME_LEN-1);
log_debug("Found PV online lookup %s for VG %s on %s", path, vgname, file_devname);
dm_list_add(pvs_online, &po->list);
}
log_debug("Found PVs online lookup %d for %s", dm_list_size(pvs_online), vgname);
fclose(fp);
return 1;
bad:
free_po_list(pvs_online);
fclose(fp);
return 0;
}
void online_dir_setup(struct cmd_context *cmd)
{
struct stat st;
int rv;
if (!stat(DEFAULT_RUN_DIR, &st))
goto do_pvs;
log_debug("Creating run_dir.");
dm_prepare_selinux_context(DEFAULT_RUN_DIR, S_IFDIR);
rv = mkdir(DEFAULT_RUN_DIR, 0755);
dm_prepare_selinux_context(NULL, 0);
if ((rv < 0) && stat(DEFAULT_RUN_DIR, &st))
log_error_pvscan(cmd, "Failed to create %s %d", DEFAULT_RUN_DIR, errno);
do_pvs:
if (!stat(PVS_ONLINE_DIR, &st))
goto do_vgs;
log_debug("Creating pvs_online_dir.");
dm_prepare_selinux_context(PVS_ONLINE_DIR, S_IFDIR);
rv = mkdir(PVS_ONLINE_DIR, 0755);
dm_prepare_selinux_context(NULL, 0);
if ((rv < 0) && stat(PVS_ONLINE_DIR, &st))
log_error_pvscan(cmd, "Failed to create %s %d", PVS_ONLINE_DIR, errno);
do_vgs:
if (!stat(VGS_ONLINE_DIR, &st))
goto do_lookup;
log_debug("Creating vgs_online_dir.");
dm_prepare_selinux_context(VGS_ONLINE_DIR, S_IFDIR);
rv = mkdir(VGS_ONLINE_DIR, 0755);
dm_prepare_selinux_context(NULL, 0);
if ((rv < 0) && stat(VGS_ONLINE_DIR, &st))
log_error_pvscan(cmd, "Failed to create %s %d", VGS_ONLINE_DIR, errno);
do_lookup:
if (!stat(PVS_LOOKUP_DIR, &st))
return;
log_debug("Creating pvs_lookup_dir.");
dm_prepare_selinux_context(PVS_LOOKUP_DIR, S_IFDIR);
rv = mkdir(PVS_LOOKUP_DIR, 0755);
dm_prepare_selinux_context(NULL, 0);
if ((rv < 0) && stat(PVS_LOOKUP_DIR, &st))
log_error_pvscan(cmd, "Failed to create %s %d", PVS_LOOKUP_DIR, errno);
}

58
lib/device/online.h Normal file
View File

@@ -0,0 +1,58 @@
/*
* Copyright (C) 2021 Red Hat, Inc. All rights reserved.
*
* This file is part of LVM2.
*
* 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 Lesser General Public License v.2.1.
*
* You should have received a copy of the GNU Lesser 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
*/
#ifndef _ONLINE_H
#define _ONLINE_H
struct pv_online {
struct dm_list list;
struct device *dev;
dev_t devno;
char pvid[ID_LEN + 1];
char vgname[NAME_LEN];
char devname[NAME_LEN];
};
/*
* Avoid a duplicate pvscan[%d] prefix when logging to the journal.
* FIXME: this should probably replace if (udevoutput) with
* if (log_journal & LOG_JOURNAL_OUTPUT)
*/
#define log_print_pvscan(cmd, fmt, args...) \
do \
if (cmd->udevoutput) \
log_print(fmt, ##args); \
else \
log_print("pvscan[%d] " fmt, getpid(), ##args); \
while (0)
#define log_error_pvscan(cmd, fmt, args...) \
do \
if (cmd->udevoutput) \
log_error(fmt, ##args); \
else \
log_error("pvscan[%d] " fmt, getpid(), ##args); \
while (0)
int online_pvid_file_read(char *path, int *major, int *minor, char *vgname, char *devname);
int online_vg_file_create(struct cmd_context *cmd, const char *vgname);
void online_vg_file_remove(const char *vgname);
int online_pvid_file_create(struct cmd_context *cmd, struct device *dev, const char *vgname);
int online_pvid_file_exists(const char *pvid);
void online_dir_setup(struct cmd_context *cmd);
int get_pvs_online(struct dm_list *pvs_online, const char *vgname);
int get_pvs_lookup(struct dm_list *pvs_online, const char *vgname);
void free_po_list(struct dm_list *list);
#endif

View File

@@ -58,7 +58,7 @@ static int _init_hash(struct pfilter *pf)
if (pf->devices)
dm_hash_destroy(pf->devices);
if (!(pf->devices = dm_hash_create(111)))
if (!(pf->devices = dm_hash_create(511)))
return_0;
return 1;

View File

@@ -15,6 +15,8 @@
#include "lib/misc/lib.h"
#include "lib/filters/filter.h"
static int _sys_dev_block_found;
#ifdef __linux__
static int _accept_p(struct cmd_context *cmd, struct dev_filter *f, struct device *dev, const char *use_filter_name)
@@ -23,6 +25,9 @@ static int _accept_p(struct cmd_context *cmd, struct dev_filter *f, struct devic
const char *sysfs_dir;
struct stat info;
if (!_sys_dev_block_found)
return 1;
dev->filtered_flags &= ~DEV_FILTERED_SYSFS;
/*
@@ -57,6 +62,26 @@ static void _destroy(struct dev_filter *f)
free(f);
}
static void _check_sys_dev_block(void)
{
char path[PATH_MAX];
const char *sysfs_dir;
struct stat info;
sysfs_dir = dm_sysfs_dir();
if (sysfs_dir && *sysfs_dir) {
if (dm_snprintf(path, sizeof(path), "%sdev/block", sysfs_dir) < 0)
return;
if (lstat(path, &info)) {
log_debug("filter-sysfs disabled: /sys/dev/block not found");
_sys_dev_block_found = 0;
} else {
_sys_dev_block_found = 1;
}
}
}
struct dev_filter *sysfs_filter_create(void)
{
const char *sysfs_dir = dm_sysfs_dir();
@@ -67,6 +92,9 @@ struct dev_filter *sysfs_filter_create(void)
return NULL;
}
/* support old kernels that don't have this */
_check_sys_dev_block();
if (!(f = zalloc(sizeof(*f))))
goto_bad;

View File

@@ -87,7 +87,7 @@ static int _passes_usable_filter(struct cmd_context *cmd, struct dev_filter *f,
break;
}
if (!(r = device_is_usable(dev, ucp, &is_lv))) {
if (!(r = device_is_usable(cmd, dev, ucp, &is_lv))) {
if (is_lv)
dev->filtered_flags |= DEV_FILTERED_IS_LV;
else

View File

@@ -219,7 +219,7 @@ static void _remove_expired(const char *dir, const char *vgname,
sum /= 1024 * 1024;
if (sum > 128 || archives_size > 8192)
log_print_unless_silent("Consider prunning %s VG archive with more then %u MiB in %u files (check archiving is needed in lvm.conf).",
log_print_unless_silent("Consider pruning %s VG archive with more then %u MiB in %u files (check archiving is needed in lvm.conf).",
vgname, (unsigned)sum, archives_size);
}

View File

@@ -146,10 +146,12 @@
#include "lib/label/hints.h"
#include "lib/device/dev-type.h"
#include "lib/device/device_id.h"
#include "lib/device/online.h"
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
#include <time.h>
#include <sys/types.h>
#include <sys/file.h>
@@ -1212,8 +1214,8 @@ void invalidate_hints(struct cmd_context *cmd)
* probably want to exclude that command from attempting this optimization,
* because it would be difficult to know what VG that command wanted to use.
*/
static void _get_single_vgname_cmd_arg(struct cmd_context *cmd,
struct dm_list *hints, char **vgname)
void get_single_vgname_cmd_arg(struct cmd_context *cmd,
struct dm_list *hints, char **vgname)
{
struct hint *hint;
char namebuf[NAME_LEN];
@@ -1262,6 +1264,11 @@ static void _get_single_vgname_cmd_arg(struct cmd_context *cmd,
return;
check:
if (!hints) {
*vgname = name;
return;
}
/*
* Only use this vgname hint if there are hints that contain this
* vgname. This might happen if we aren't able to properly extract the
@@ -1390,6 +1397,16 @@ int get_hints(struct cmd_context *cmd, struct dm_list *hints_out, int *newhints,
log_debug("get_hints: needs refresh");
free_hints(&hints_list);
/*
* This is not related to hints, and is probably unnecessary,
* but it could possibly help. When hints become invalid it's
* usually becaues devs on the system have changed, and that
* also means that a missing devices file entry might be found
* by searching devices again. (the searched_devnames
* mechanism should eventually be replaced)
*/
unlink_searched_devnames(cmd);
if (!_lock_hints(cmd, LOCK_EX, NONBLOCK))
return 0;
@@ -1423,7 +1440,7 @@ int get_hints(struct cmd_context *cmd, struct dm_list *hints_out, int *newhints,
* us which devs are PVs. We might want to enable this optimization
* separately.)
*/
_get_single_vgname_cmd_arg(cmd, &hints_list, &vgname);
get_single_vgname_cmd_arg(cmd, &hints_list, &vgname);
_apply_hints(cmd, &hints_list, vgname, devs_in, devs_out);

View File

@@ -41,5 +41,8 @@ void hints_exit(struct cmd_context *cmd);
void pvscan_recreate_hints_begin(struct cmd_context *cmd);
void get_single_vgname_cmd_arg(struct cmd_context *cmd,
struct dm_list *hints, char **vgname);
#endif

View File

@@ -26,6 +26,7 @@
#include "lib/metadata/metadata.h"
#include "lib/format_text/layout.h"
#include "lib/device/device_id.h"
#include "lib/device/online.h"
#include <sys/stat.h>
#include <fcntl.h>
@@ -891,7 +892,7 @@ static int _setup_bcache(void)
#define BASE_FD_COUNT 32 /* Number of open files we want apart from devs */
static void _prepare_open_file_limit(struct cmd_context *cmd, unsigned int num_devs)
void prepare_open_file_limit(struct cmd_context *cmd, unsigned int num_devs)
{
#ifdef HAVE_PRLIMIT
struct rlimit old = { 0 }, new;
@@ -1020,6 +1021,281 @@ int label_scan_for_pvid(struct cmd_context *cmd, char *pvid, struct device **dev
return ret;
}
/*
* Clear state that label_scan_vg_online() created so it will not
* confuse the standard label_scan() that the caller falls back to.
* the results of filtering (call filter->wipe)
* the results of matching device_id (reset dev and du)
* the results of scanning in lvmcache
*/
static void _clear_scan_state(struct cmd_context *cmd, struct dm_list *devs)
{
struct device_list *devl;
struct device *dev;
struct dev_use *du;
dm_list_iterate_items(devl, devs) {
dev = devl->dev;
cmd->filter->wipe(cmd, cmd->filter, dev, NULL);
dev->flags &= ~DEV_MATCHED_USE_ID;
dev->id = NULL;
if ((du = get_du_for_dev(cmd, dev)))
du->dev = NULL;
lvmcache_del_dev(dev);
memset(dev->pvid, 0, ID_LEN);
}
}
/*
* Use files under /run/lvm/, created by pvscan --cache autoactivation,
* to optimize device setup/scanning. autoactivation happens during
* system startup when the hints file is not useful, but he pvs_online
* files can provide a similar optimization to the hints file.
*/
int label_scan_vg_online(struct cmd_context *cmd, const char *vgname,
int *found_none, int *found_all, int *found_incomplete)
{
struct dm_list pvs_online;
struct dm_list devs;
struct dm_list devs_drop;
struct pv_online *po;
struct device_list *devl, *devl2;
int relax_deviceid_filter = 0;
int metadata_pv_count;
int try_dev_scan = 0;
dm_list_init(&pvs_online);
dm_list_init(&devs);
dm_list_init(&devs_drop);
log_debug_devs("Finding online devices to scan");
/*
* First attempt to use /run/lvm/pvs_lookup/vgname which should be
* used in cases where all PVs in a VG do not contain metadata.
* When the pvs_lookup file does not exist, then simply use all
* /run/lvm/pvs_online/pvid files that contain a matching vgname.
* The list of po structs represents the PVs in the VG, and the
* info from the online files tell us which devices those PVs are
* located on.
*/
if (vgname) {
if (!get_pvs_lookup(&pvs_online, vgname)) {
if (!get_pvs_online(&pvs_online, vgname))
goto bad;
}
} else {
if (!get_pvs_online(&pvs_online, NULL))
goto bad;
}
if (dm_list_empty(&pvs_online)) {
*found_none = 1;
return 1;
}
/*
* For each po add a struct dev to dev-cache. This is a faster
* alternative to the usual dev_cache_scan() which looks at all
* devices. If this optimization fails, then fall back to the usual
* dev_cache_scan().
*/
dm_list_iterate_items(po, &pvs_online) {
if (!(po->dev = setup_dev_in_dev_cache(cmd, po->devno, po->devname[0] ? po->devname : NULL))) {
log_debug("No device found for quick mapping of online PV %d:%d %s PVID %s",
(int)MAJOR(po->devno), (int)MINOR(po->devno), po->devname, po->pvid);
try_dev_scan = 1;
continue;
}
if (!(devl = dm_pool_zalloc(cmd->mem, sizeof(*devl))))
goto_bad;
devl->dev = po->dev;
dm_list_add(&devs, &devl->list);
}
/*
* Translating a devno (major:minor) into a device name can be
* problematic for some devices that have unusual sysfs layouts, so if
* this happens, do a full dev_cache_scan, which is slower, but is
* sure to find the device.
*/
if (try_dev_scan) {
dev_cache_scan(cmd);
dm_list_iterate_items(po, &pvs_online) {
if (po->dev)
continue;
if (!(po->dev = dev_cache_get_by_devt(cmd, po->devno, NULL, NULL))) {
log_error("No device found for %d:%d PVID %s",
(int)MAJOR(po->devno), (int)MINOR(po->devno), po->pvid);
goto bad;
}
if (!(devl = dm_pool_zalloc(cmd->mem, sizeof(*devl))))
goto_bad;
devl->dev = po->dev;
dm_list_add(&devs, &devl->list);
}
}
/*
* factor code common to pvscan_cache_args
*/
/*
* Match devs with the devices file because special/optimized
* device setup was used which does not check the devices file.
* If a match fails here do not exclude it, that will be done below by
* passes_filter() which runs filter-deviceid. The
* relax_deviceid_filter case needs to be able to work around
* unmatching devs.
*/
if (cmd->enable_devices_file) {
dm_list_iterate_items(devl, &devs)
device_ids_match_dev(cmd, devl->dev);
}
if (cmd->enable_devices_list)
device_ids_match_device_list(cmd);
if (cmd->enable_devices_file && device_ids_use_devname(cmd)) {
relax_deviceid_filter = 1;
cmd->filter_deviceid_skip = 1;
/* PVIDs read from devs matched to devices file below instead. */
log_debug("Skipping device_id filtering due to devname ids.");
}
cmd->filter_nodata_only = 1;
dm_list_iterate_items_safe(devl, devl2, &devs) {
if (!cmd->filter->passes_filter(cmd, cmd->filter, devl->dev, NULL)) {
log_print("%s excluded by filters: %s.",
dev_name(devl->dev), dev_filtered_reason(devl->dev));
dm_list_del(&devl->list);
dm_list_add(&devs_drop, &devl->list);
}
}
cmd->filter_nodata_only = 0;
/*
* Clear the results of nodata filters that were saved by the
* persistent filter so that the complete set of filters will
* be checked by passes_filter below.
*/
dm_list_iterate_items(devl, &devs)
cmd->filter->wipe(cmd, cmd->filter, devl->dev, NULL);
/*
* Read header from each dev.
* Eliminate non-lvm devs.
* Apply all filters.
*/
log_debug("label_scan_vg_online: read and filter devs");
label_scan_setup_bcache();
dm_list_iterate_items_safe(devl, devl2, &devs) {
struct dev_use *du;
int has_pvid;
if (!label_read_pvid(devl->dev, &has_pvid)) {
log_print("%s cannot read label.", dev_name(devl->dev));
dm_list_del(&devl->list);
dm_list_add(&devs_drop, &devl->list);
continue;
}
if (!has_pvid) {
/* Not an lvm device */
log_print("%s not an lvm device.", dev_name(devl->dev));
dm_list_del(&devl->list);
dm_list_add(&devs_drop, &devl->list);
continue;
}
/*
* filter-deviceid is not being used because of unstable devnames,
* so in place of that check if the pvid is in the devices file.
*/
if (relax_deviceid_filter) {
if (!(du = get_du_for_pvid(cmd, devl->dev->pvid))) {
log_print("%s excluded by devices file (checking PVID).",
dev_name(devl->dev));
dm_list_del(&devl->list);
dm_list_add(&devs_drop, &devl->list);
continue;
} else {
/* Special case matching for devname entries based on pvid. */
log_debug("Match device_id %s %s to %s: matching PVID",
idtype_to_str(du->idtype), du->idname, dev_name(devl->dev));
}
}
/* Applies all filters, including those that need data from dev. */
if (!cmd->filter->passes_filter(cmd, cmd->filter, devl->dev, NULL)) {
log_print("%s excluded by filters: %s.",
dev_name(devl->dev), dev_filtered_reason(devl->dev));
dm_list_del(&devl->list);
dm_list_add(&devs_drop, &devl->list);
}
}
if (relax_deviceid_filter)
cmd->filter_deviceid_skip = 0;
free_po_list(&pvs_online);
if (dm_list_empty(&devs)) {
_clear_scan_state(cmd, &devs_drop);
*found_none = 1;
return 1;
}
/*
* Scan devs to populate lvmcache info, which includes the mda info that's
* needed to read vg metadata.
* bcache data from label_read_pvid above is not invalidated so it can
* be reused (more data may need to be read depending on how much of the
* metadata was covered when reading the pvid.)
*/
_scan_list(cmd, NULL, &devs, 0, NULL);
/*
* Check if all PVs from the VG were found after scanning the devs
* produced from the online files. The online files are effectively
* hints that usually work, but are not definitive, so we need to
* be able to fall back to a standard label scan if the online hints
* gave fewer PVs than listed in VG metadata.
*/
if (vgname) {
metadata_pv_count = lvmcache_pvsummary_count(vgname);
if (metadata_pv_count > dm_list_size(&devs)) {
log_debug("Incomplete PV list from online files %d metadata %d.",
dm_list_size(&devs), metadata_pv_count);
_clear_scan_state(cmd, &devs_drop);
_clear_scan_state(cmd, &devs);
*found_incomplete = 1;
return 1;
}
}
*found_all = 1;
return 1;
bad:
_clear_scan_state(cmd, &devs_drop);
_clear_scan_state(cmd, &devs);
free_po_list(&pvs_online);
return 0;
}
/*
* Scan devices on the system to discover which are LVM devices.
* Info about the LVM devices (PVs) is saved in lvmcache in a
@@ -1130,6 +1406,9 @@ int label_scan(struct cmd_context *cmd)
}
}
}
log_debug_devs("Filtering devices to scan done (nodata)");
cmd->filter_nodata_only = 0;
dm_list_iterate_items(devl, &all_devs)
@@ -1165,7 +1444,7 @@ int label_scan(struct cmd_context *cmd)
* which we want to keep open) is higher than the current
* soft limit.
*/
_prepare_open_file_limit(cmd, dm_list_size(&scan_devs));
prepare_open_file_limit(cmd, dm_list_size(&scan_devs));
/*
* Do the main scan.
@@ -1305,7 +1584,7 @@ int label_read_pvid(struct device *dev, int *has_pvid)
lh = (struct label_header *)(buf + 512);
if (memcmp(lh->id, LABEL_ID, sizeof(lh->id))) {
/* Not an lvm deice */
/* Not an lvm device */
label_scan_invalidate(dev);
return 1;
}
@@ -1315,7 +1594,7 @@ int label_read_pvid(struct device *dev, int *has_pvid)
* rest of the label_header intact.
*/
if (memcmp(lh->type, LVM2_LABEL, sizeof(lh->type))) {
/* Not an lvm deice */
/* Not an lvm device */
label_scan_invalidate(dev);
return 1;
}
@@ -1440,6 +1719,41 @@ void label_scan_invalidate_lv(struct cmd_context *cmd, struct logical_volume *lv
}
}
void label_scan_invalidate_lvs(struct cmd_context *cmd, struct dm_list *lvs)
{
struct dm_list *devs;
struct dm_active_device *dm_dev;
unsigned devs_features = 0;
struct device *dev;
struct lv_list *lvl;
dev_t devt;
if (get_device_list(NULL, &devs, &devs_features)) {
if (devs_features & DM_DEVICE_LIST_HAS_UUID) {
dm_list_iterate_items(dm_dev, devs)
if (dm_dev->uuid &&
strncmp(dm_dev->uuid, UUID_PREFIX, sizeof(UUID_PREFIX) - 1) == 0) {
devt = MKDEV(dm_dev->major, dm_dev->minor);
if ((dev = dev_cache_get_by_devt(cmd, devt, NULL, NULL)))
label_scan_invalidate(dev);
}
/* ATM no further caching for any lvconvert command
* TODO: any other command to be skipped ??
*/
if (strcmp(cmd->name, "lvconvert")) {
dm_device_list_destroy(&cmd->cache_dm_devs);
cmd->cache_dm_devs = devs; /* cache to avoid unneeded checks */
devs = NULL;
}
}
dm_device_list_destroy(&devs);
}
if (!(devs_features & DM_DEVICE_LIST_HAS_UUID))
dm_list_iterate_items(lvl, lvs)
label_scan_invalidate_lv(cmd, lvl->lv);
}
/*
* Empty the bcache of all blocks and close all open fds,
* but keep the bcache set up.

View File

@@ -110,6 +110,7 @@ int label_scan_devs_excl(struct cmd_context *cmd, struct dev_filter *f, struct d
int label_scan_dev(struct cmd_context *cmd, struct device *dev);
void label_scan_invalidate(struct device *dev);
void label_scan_invalidate_lv(struct cmd_context *cmd, struct logical_volume *lv);
void label_scan_invalidate_lvs(struct cmd_context *cmd, struct dm_list *lvs);
void label_scan_drop(struct cmd_context *cmd);
void label_scan_destroy(struct cmd_context *cmd);
int label_scan_setup_bcache(void);
@@ -118,6 +119,9 @@ int label_scan_open_excl(struct device *dev);
int label_scan_open_rw(struct device *dev);
int label_scan_reopen_rw(struct device *dev);
int label_read_pvid(struct device *dev, int *has_pvid);
int label_scan_vg_online(struct cmd_context *cmd, const char *vgname,
int *found_none, int *found_all, int *found_incomplete);
int label_scan_for_pvid(struct cmd_context *cmd, char *pvid, struct device **dev_out);
@@ -134,4 +138,6 @@ void dev_invalidate(struct device *dev);
void dev_set_last_byte(struct device *dev, uint64_t offset);
void dev_unset_last_byte(struct device *dev);
void prepare_open_file_limit(struct cmd_context *cmd, unsigned int num_devs);
#endif

View File

@@ -330,6 +330,7 @@ int vg_write_lock_held(void)
int sync_local_dev_names(struct cmd_context* cmd)
{
dm_device_list_destroy(&cmd->cache_dm_devs);
memlock_unlock(cmd);
fs_unlock();
return 1;

View File

@@ -892,6 +892,7 @@ uint32_t log_journal_str_to_val(const char *str)
return LOG_JOURNAL_OUTPUT;
if (!strcasecmp(str, "debug"))
return LOG_JOURNAL_DEBUG;
log_warn("Ignoring unrecognized journal value.");
return 0;
}

View File

@@ -3100,9 +3100,7 @@ static int _vg_commit_mdas(struct volume_group *vg)
{
struct metadata_area *mda, *tmda;
DM_LIST_INIT(ignored);
int failed = 0;
int good = 0;
int cache_updated = 0;
/* Rearrange the metadata_areas_in_use so ignored mdas come first. */
dm_list_iterate_items_safe(mda, tmda, &vg->fid->metadata_areas_in_use)
@@ -3116,19 +3114,11 @@ static int _vg_commit_mdas(struct volume_group *vg)
dm_list_iterate_items(mda, &vg->fid->metadata_areas_in_use) {
if (mda->status & MDA_FAILED)
continue;
failed = 0;
if (mda->ops->vg_commit &&
!mda->ops->vg_commit(vg->fid, vg, mda)) {
stack;
failed = 1;
} else
good++;
/* Update cache first time we succeed */
if (!failed && !cache_updated) {
lvmcache_update_vg_from_write(vg);
cache_updated = 1;
}
}
if (good)
return 1;
@@ -3558,7 +3548,7 @@ static void _set_pv_device(struct format_instance *fid,
if (!id_write_format(&pv->id, buffer, sizeof(buffer)))
buffer[0] = '\0';
if (cmd && !cmd->pvscan_cache_single &&
if (cmd && !cmd->expect_missing_vg_device &&
(!vg_is_foreign(vg) && !cmd->include_foreign_vgs))
log_warn("WARNING: Couldn't find device with uuid %s.", buffer);
else
@@ -5084,7 +5074,7 @@ struct volume_group *vg_read(struct cmd_context *cmd, const char *vg_name, const
if (!pvl->pv->dev) {
/* The obvious and common case of a missing device. */
if (vg_is_foreign(vg) && !cmd->include_foreign_vgs)
if ((vg_is_foreign(vg) && !cmd->include_foreign_vgs) || cmd->expect_missing_vg_device)
log_debug("VG %s is missing PV %s (last written to %s)", vg_name, uuidstr, pvl->pv->device_hint ?: "na");
else if (pvl->pv->device_hint)
log_warn("WARNING: VG %s is missing PV %s (last written to %s).", vg_name, uuidstr, pvl->pv->device_hint);

View File

@@ -609,8 +609,7 @@ int dm_check_version(void)
int dm_cookie_supported(void)
{
return (dm_check_version() &&
_dm_version >= 4 &&
_dm_version_minor >= 15);
((_dm_version == 4) ? _dm_version_minor >= 15 : _dm_version > 4));
}
static int _dm_inactive_supported(void)
@@ -763,6 +762,30 @@ struct dm_deps *dm_task_get_deps(struct dm_task *dmt)
dmt->dmi.v4->data_start);
}
/*
* Round up the ptr to an 8-byte boundary.
* Follow kernel pattern.
*/
#define ALIGN_MASK 7
static size_t _align_val(size_t val)
{
return (val + ALIGN_MASK) & ~ALIGN_MASK;
}
static void *_align_ptr(void *ptr)
{
return (void *)_align_val((size_t)ptr);
}
static int _check_has_event_nr(void) {
static int _has_event_nr = -1;
if (_has_event_nr < 0)
_has_event_nr = dm_check_version() &&
((_dm_version == 4) ? _dm_version_minor >= 38 : _dm_version > 4);
return _has_event_nr;
}
struct dm_names *dm_task_get_names(struct dm_task *dmt)
{
return (struct dm_names *) (((char *) dmt->dmi.v4) +
@@ -1436,8 +1459,7 @@ static int _udev_complete(struct dm_task *dmt)
static int _check_uevent_generated(struct dm_ioctl *dmi)
{
if (!dm_check_version() ||
_dm_version < 4 ||
_dm_version_minor < 17)
((_dm_version == 4) ? _dm_version_minor < 17 : _dm_version < 4))
/* can't check, assume uevent is generated */
return 1;
@@ -1784,23 +1806,34 @@ static int _do_dm_ioctl_unmangle_string(char *str, const char *str_name,
static int _dm_ioctl_unmangle_names(int type, struct dm_ioctl *dmi)
{
char buf[DM_NAME_LEN];
struct dm_names *names;
char buf_uuid[DM_UUID_LEN];
struct dm_name_list *names;
unsigned next = 0;
char *name;
int r = 1;
uint32_t *event_nr;
char *uuid_ptr;
dm_string_mangling_t mangling_mode = dm_get_name_mangling_mode();
if ((name = dmi->name))
r = _do_dm_ioctl_unmangle_string(name, "name", buf, sizeof(buf),
dm_get_name_mangling_mode());
r &= _do_dm_ioctl_unmangle_string(name, "name", buf, sizeof(buf),
mangling_mode);
if (type == DM_DEVICE_LIST &&
((names = ((struct dm_names *) ((char *)dmi + dmi->data_start)))) &&
((names = ((struct dm_name_list *) ((char *)dmi + dmi->data_start)))) &&
names->dev) {
do {
names = (struct dm_names *)((char *) names + next);
r = _do_dm_ioctl_unmangle_string(names->name, "name",
buf, sizeof(buf),
dm_get_name_mangling_mode());
names = (struct dm_name_list *)((char *) names + next);
event_nr = _align_ptr(names->name + strlen(names->name) + 1);
r &= _do_dm_ioctl_unmangle_string(names->name, "name",
buf, sizeof(buf), mangling_mode);
/* Unmangle also UUID within same loop */
if (_check_has_event_nr() &&
(event_nr[1] & DM_NAME_LIST_FLAG_HAS_UUID)) {
uuid_ptr = _align_ptr(event_nr + 2);
r &= _do_dm_ioctl_unmangle_string(uuid_ptr, "UUID", buf_uuid,
sizeof(buf_uuid), mangling_mode);
}
next = names->next;
} while (next);
}

View File

@@ -33,7 +33,7 @@ CMIRRORDMAN = cmirrord.8
LVMDBUSDMAN = lvmdbusd.8
MAN5=lvm.conf.5
MAN7=lvmsystemid.7 lvmreport.7 lvmraid.7
MAN7=lvmsystemid.7 lvmreport.7 lvmraid.7 lvmautoactivation.7
MAN8=lvm.8 lvmdump.8 lvm-fullreport.8 lvm-lvpoll.8 \
lvcreate.8 lvchange.8 lvmconfig.8 lvconvert.8 lvdisplay.8 \
@@ -47,7 +47,6 @@ MAN8=lvm.8 lvmdump.8 lvm-fullreport.8 lvm-lvpoll.8 \
MAN8SO=lvm-config.8 lvm-dumpconfig.8
MAN8DM=dmsetup.8 dmstats.8
MAN8CLUSTER=
MAN8SYSTEMD_GENERATORS=lvm2-activation-generator.8
ifeq (,$(findstring $(MAKECMDGOALS), distclean all_man install_all_man))
MAN7 += lvmcache.7 lvmthin.7 lvmvdo.7
@@ -119,7 +118,7 @@ TESTMAN=test.gen
include $(top_builddir)/make.tmpl
CLEAN_TARGETS+=$(MAN5) $(MAN7) $(MAN8) $(MAN8SO) $(MAN8:%.8=%.8_gen) $(MAN8CLUSTER) \
$(MAN8SYSTEMD_GENERATORS) $(MAN8DM) $(TESTMAN)
$(MAN8DM) $(TESTMAN)
all: man device-mapper
@@ -127,11 +126,11 @@ all: man device-mapper
device-mapper: $(MAN8DM)
man: $(MAN5) $(MAN7) $(MAN8) $(MAN8SO) $(MAN8CLUSTER) $(MAN8SYSTEMD_GENERATORS)
man: $(MAN5) $(MAN7) $(MAN8) $(MAN8SO) $(MAN8CLUSTER)
all_man: man
$(MAN5) $(MAN7) $(MAN8) $(MAN8SO) $(MAN8DM) $(MAN8CLUSTER) $(MAN8SYSTEMD_GENERATORS): Makefile
$(MAN5) $(MAN7) $(MAN8) $(MAN8SO) $(MAN8DM) $(MAN8CLUSTER): Makefile
$(MANGENERATOR):
@echo " [MAKE] $<"
@@ -289,11 +288,6 @@ install_device-mapper: $(MAN8DM)
$(Q) $(INSTALL) -d $(MAN8DIR)
$(Q) $(INSTALL_DATA) $^ $(MAN8DIR)/
install_systemd_generators: $(MAN8SYSTEMD_GENERATORS)
@echo " [INSTALL] $^"
$(Q) $(INSTALL) -d $(MAN8DIR)
$(Q) $(INSTALL_DATA) $^ $(MAN8DIR)/
install: install_lvm2 install_device-mapper install_cluster
install_all_man: install install_systemd_generators

View File

@@ -563,7 +563,6 @@ Prepends source file name and code line number with libdm debugging.
.P
.BR lvm-fullreport (8),
.BR lvm-lvpoll (8),
.BR lvm2-activation-generator (8),
.BR blkdeactivate (8),
.BR lvmdump (8),
.P
@@ -579,6 +578,7 @@ Prepends source file name and code line number with libdm debugging.
.BR lvmraid (7),
.BR lvmthin (7),
.BR lvmcache (7),
.BR lvmautoactivation (7),
.P
.BR dmsetup (8),
.BR dmstats (8),

View File

@@ -1,58 +0,0 @@
.TH "LVM2-ACTIVATION-GENERATOR" "8" "LVM TOOLS #VERSION#" "Red Hat, Inc" "\""
.
.SH "NAME"
.
lvm2-activation-generator - generator for systemd units to activate LVM volumes on boot
.
.SH SYNOPSIS
.
.B #SYSTEMD_GENERATOR_DIR#/lvm2-activation-generator
.
.SH DESCRIPTION
.
The lvm2-activation-generator is called by \fBsystemd\fP(1) on boot to
generate systemd units at runtime to activate LVM Logical Volumes (LVs)
when global/event_activation=0 is set in \fBlvm.conf\fP(5). These units use
\fBvgchange -aay\fP to activate LVs.
.P
If event_activation=1, the lvm2-activation-generator exits immediately without
generating any systemd units, and LVM fully relies on event-based
activation to activate LVs. In this case, event-generated
.B pvscan --cache -aay
commands activate LVs.
.P
These systemd units are generated by lvm2-activation-generator:
.P
.I lvm2-activation-early.service
is run before systemd's special \fBcryptsetup.target\fP to activate
LVs that are not layered on top of encrypted devices.
.P
.I lvm2-activation.service
is run after systemd's special \fBcryptsetup.target\fP to activate
LVs that are layered on top of encrypted devices.
.P
.I lvm2-activation-net.service
is run after systemd's special \fBremote-fs-pre.target\fP to activate
LVs that are layered on attached remote devices.
.P
Note that all the underlying LVM devices (Physical Volumes) need to be
present when the service is run. If the there are any devices that appear
to the system later, LVs using these devices need to be activated directly
by \fBlvchange\fP(8) or \fBvgchange\fP(8).
.P
The lvm2-activation-generator implements the \fBGenerators Specification\fP
as referenced in \fBsystemd\fP(1).
.
.SH SEE ALSO
.nh
.ad l
.BR lvm.conf (5),
.BR vgchange (8),
.BR lvchange (8),
.BR pvscan (8),
.P
.BR systemd (1),
.BR systemd.target (5),
.BR systemd.special (7),
.P
.BR udev (7)

View File

@@ -0,0 +1,289 @@
.TH "LVMAUTOACTIVATION" "7" "LVM TOOLS #VERSION#" "Red Hat, Inc" "\""
.
.SH NAME
.
lvmautoactivation \(em LVM autoactivation
.
.SH DESCRIPTION
.
Autoactivation is the activation of LVs performed automatically by the
system in response to LVM devices being attached to the machine. When all
PVs in a VG have been attached, the VG is complete, and LVs in the VG are
activated.
.P
Autoactivation of VGs, or specific LVs, can be prevented using vgchange or
lvchange --setautoactivation n. The lvm.conf auto_activation_volume_list
is another way to limit autoactivation.
.
.SS event autoactivation
.P
LVM autoactivation is "event based", in which complete VGs are activated
in response to uevents which occur during system startup or at any time
after the system has started. An old form of autoactivation was "static"
in which complete VGs are activated at a fixed point during system startup
by a systemd service, and not in response to events.
.P
Event based autoactivation is driven by udev, udev rules, and systemd.
When a device is attached to a machine, a uevent is generated by the
kernel to notify userspace of the new device. systemd-udev runs udev
rules to process the new device. Udev rules use blkid to identify the
device as an LVM PV and then execute the lvm-specific udev rule for the
device, which triggers autoactivation.
.P
There are two variations of event based autoactivation that may be used on
a system, depending on the LVM udev rule that is installed (found in
/lib/udev/rules.d/.) The following summarizes the steps in each rule
which lead to autoactivation:
.P
.B 69-dm-lvm-metad.rules
.
.IP \[bu] 2
device /dev/name with major:minor X:Y is attached to the machine
.
.IP \[bu] 2
systemd/udev runs blkid to identify /dev/name as an LVM PV
.
.IP \[bu] 2
udev rule 69-dm-lvm-metad.rules is run for /dev/name
.
.IP \[bu] 2
the lvm udev rule runs the systemd service lvm2-pvscan@X:Yservice
.
.IP \[bu] 2
the lvm2-pvscan service runs:
.br
pvscan --cache -aay --major X --minor Y
.
.IP \[bu] 2
pvscan reads the device, records that the PV is online
(see pvs_online), and checks if the VG is complete.
.
.IP \[bu] 2
if the VG is complete, pvscan creates the vgs_online temp file,
and activates the VG.
.
.IP \[bu] 2
the activation command output can be seen from
systemctl status lvm2-pvscan*
.P
.B 69-dm-lvm.rules
.
.IP \[bu] 2
device /dev/name with major:minor X:Y is attached to the machine
.
.IP \[bu] 2
systemd/udev runs blkid to identify /dev/name as an LVM PV
.
.IP \[bu] 2
udev rule 69-dm-lvm.rules is run for /dev/name
.
.IP \[bu] 2
the lvm udev rule runs:
.br
pvscan --cache --listvg --checkcomplete --vgonline
.br
--autoactivation event --udevoutput --journal=output /dev/name
.
.IP \[bu] 2
pvscan reads the device, records that the PV is online
(see pvs_online), and checks if the VG is complete.
.
.IP \[bu] 2
if the VG is complete, pvscan creates the vgs_online temp file,
and prints the name of the VG for the udev rule to import:
LVM_VG_NAME_COMPLETE='vgname'
.
.IP \[bu] 2
if the lvm udev rule sees LVM_VG_NAME_COMPLETE from pvscan,
it activates the VG using a transient systemd service named
lvm-activate-<vgname>.
.
.IP \[bu] 2
the lvm-activate-<vgname> service runs
.br
vgchange -aay --autoactivation event <vgname>
.
.IP \[bu] 2
the activation command output can be seen from
journalctl -u lvm-activate-<vgname>
.P
.
.SS pvscan options
.P
.B --cache
.br
Read the <device> arg (and only that device), and record that
the PV is online by creating the /run/lvm/pvs_online/<pvid>
file containing the name of the VG and the device for the PV.
.P
.B -aay
.br
Activate the VG from the pvscan command
(includes implicit --checkcomplete and --vgonline.)
.P
.B --checkcomplete
.br
Check if the VG is complete, i.e. all PVs are present on
the system, by checking /run/lvm/pvs_online/<pvid> files.
.P
.B --vgonline
.br
Create /run/lvm/vgs_online/<vgname> if the VG is complete
(used to ensure only one command performs activation.)
.P
.B --autoactivation event
.br
Inform the command it is used for event based autoactivation.
.P
.B --listvg
.br
Print the name of the VG using the device.
.P
.B --udevoutput
.br
Only print output that can be imported to the udev rule,
using the udev environment key format, i.e. NAME='value'.
.P
.B --journal=output
.br
Send standard command output to the journal (when stdout
is reserved for udev output.)
.P
.SS run files
.P
Autoactivation commands use a number of temp files in /run/lvm (with the
expectation that /run is cleared between boots.)
.P
.B pvs_online
.br
pvscan --cache creates a file here for each PV that is attached. The file
is named with the PVID and contains the VG name and device information.
The existence of the file is used to determine when all PVs for a given VG
are present. The device information in these files is also used to
optimize locating devices for a VG when the VG is activated.
.P
.B pvs_lookup
.br
pvscan --cache creates a file here named for a VG (if one doesn't already
exist.) The file contains a list of PVIDs in the VG. This is needed when
a PV is processed which has no VG metadata, in which case the list of
PVIDs from the lookup file is used to check if the VG is complete.
.P
.B vgs_online
.br
The first activation command (pvscan or vgchange) to create a file here,
named for the VG, will activate the VG. This resolves a race when
concurrent commands attempt to activate a VG at once.
.
.SS static autoactivation
.P
A static autoactivation method is no longer provided by lvm.
Setting event_activation=0 still disables event based autoactivation.
WARNING: disabling event activation without an alternative may prevent a
system from booting. A custom systemd service could be written to run
autoactivation during system startup, in which case disabling event
autoactivation may be useful.
.
.SH EXAMPLES
.P
VG "vg" contains two PVs:
.nf
$ pvs -o name,vgname,uuid /dev/sdb /dev/sdc
PV VG PV UUID
/dev/sdb vg 1uKpaT-lFOZ-NLHX-j4jI-OBi1-QpdE-HZ5hZY
/dev/sdc vg 5J3tM8-aIPe-2vbd-DBe7-bvRq-TGj0-DaKV2G
.fi
.P
use of --cache:
.nf
$ pvscan --cache /dev/sdb
pvscan[12922] PV /dev/sdb online.
$ pvscan --cache /dev/sdc
pvscan[12923] PV /dev/sdc online.
$ cat /run/lvm/pvs_online/1uKpaTlFOZNLHXj4jIOBi1QpdEHZ5hZY
8:16
vg:vg
dev:/dev/sdb
$ cat /run/lvm/pvs_online/5J3tM8aIPe2vbdDBe7bvRqTGj0DaKV2G
8:32
vg:vg
dev:/dev/sdc
.fi
.P
use of -aay:
.nf
$ pvscan --cache -aay /dev/sdb
pvscan[12935] PV /dev/sdb online, VG vg incomplete (need 1).
$ pvscan --cache -aay /dev/sdc
pvscan[12936] PV /dev/sdc online, VG vg is complete.
pvscan[12936] VG vg run autoactivation.
1 logical volume(s) in volume group "vg" now active
$ cat /run/lvm/pvs_online/1uKpaTlFOZNLHXj4jIOBi1QpdEHZ5hZY
8:16
vg:vg
dev:/dev/sdb
$ cat /run/lvm/pvs_online/5J3tM8aIPe2vbdDBe7bvRqTGj0DaKV2G
8:32
vg:vg
dev:/dev/sdc
$ ls /run/lvm/vgs_online/vg
/run/lvm/vgs_online/vg
.fi
.P
use of --listvg:
.nf
$ pvscan --cache --listvg /dev/sdb
VG vg
$ pvscan --cache --listvg /dev/sdc
VG vg
$ cat /run/lvm/pvs_online/1uKpaTlFOZNLHXj4jIOBi1QpdEHZ5hZY
8:16
vg:vg
dev:/dev/sdb
$ cat /run/lvm/pvs_online/5J3tM8aIPe2vbdDBe7bvRqTGj0DaKV2G
8:32
vg:vg
dev:/dev/sdc
.fi
.P
use of --checkcomplete:
.nf
$ pvscan --cache --listvg --checkcomplete --vgonline /dev/sdb
pvscan[12996] PV /dev/sdb online, VG vg incomplete (need 1).
VG vg incomplete
$ pvscan --cache --listvg --checkcomplete --vgonline /dev/sdc
pvscan[12997] PV /dev/sdc online, VG vg is complete.
VG vg complete
.fi
.P
use of --udevoutput:
.nf
$ pvscan --cache --listvg --checkcomplete --vgonline --udevoutput /dev/sdb
LVM_VG_NAME_INCOMPLETE='vg'
$ pvscan --cache --listvg --checkcomplete --vgonline --udevoutput /dev/sdc
LVM_VG_NAME_COMPLETE='vg'
.fi
.P
use of --listlvs:
.nf
$ lvs -o name,devices vg
LV Devices
lvol0 /dev/sdb(0)
lvol1 /dev/sdc(0)
lvol2 /dev/sdb(1),/dev/sdc(1)
$ pvscan --cache --listlvs --checkcomplete /dev/sdb
pvscan[13288] PV /dev/sdb online, VG vg incomplete (need 1).
VG vg incomplete
LV vg/lvol0 complete
LV vg/lvol2 incomplete
$ pvscan --cache --listlvs --checkcomplete /dev/sdc
pvscan[13289] PV /dev/sdc online, VG vg is complete.
VG vg complete
LV vg/lvol1 complete
LV vg/lvol2 complete
.fi

View File

@@ -4,56 +4,47 @@ like
or
.BR pvdisplay (8).
.P
When the --cache and -aay options are used, pvscan records which PVs are
available on the system, and activates LVs in completed VGs. A VG is
complete when pvscan sees that the final PV in the VG has appeared. This
is used by event-based system startup (systemd, udev) to activate LVs.
.P
The four main variations of this are:
When --cache is used, pvscan updates runtime lvm state on the system, or
with -aay performs autoactivation.
.P
.B pvscan --cache
.I device
.P
If device is present, lvm adds a record that the PV on device is online.
If device is present, lvm records that the PV on device is online.
If device is not present, lvm removes the online record for the PV.
In most cases, the pvscan will only read the named devices.
.P
.B pvscan --cache -aay
.IR device ...
.P
This begins by performing the same steps as above. Afterward, if the VG
for the specified PV is complete, then pvscan will activate LVs in the VG
(the same as vgchange -aay vgname would do.)
pvscan only reads the named device.
.P
.B pvscan --cache
.P
This first clears all existing PV online records, then scans all devices
on the system, adding PV online records for any PVs that are found.
Updates the runtime state for all lvm devices.
.P
.B pvscan --cache -aay
.I device
.P
Performs the --cache steps for the device, then checks if the VG using the
device is complete. If so, LVs in the VG are autoactivated, the same as
vgchange -aay vgname would do. (A device name may be replaced with major
and minor numbers.)
.P
.B pvscan --cache -aay
.P
This begins by performing the same steps as pvscan --cache. Afterward, it
activates LVs in any complete VGs.
Performs the --cache steps for all devices, then autoactivates any complete VGs.
.P
To prevent devices from being scanned by pvscan --cache, add them
to
.BR lvm.conf (5)
.B devices/global_filter.
For more information, see:
.br
.B lvmconfig --withcomments devices/global_filter
.B pvscan --cache --listvg|--listlvs
.I device
.P
Auto-activation of VGs or LVs can be enabled/disabled using:
.br
Performs the --cache steps for the device, then prints the name of the VG
using the device, or the names of LVs using the device. --checkcomplete
is usually included to check if all PVs for the VG or LVs are online.
When this command is called by a udev rule, the output must conform to
udev rule specifications (see --udevoutput.) The udev rule will use the
results to perform autoactivation.
.P
Autoactivation of VGs or LVs can be enabled/disabled using vgchange or
lvchange with --setautoactivation y|n, or by adding names to
.BR lvm.conf (5)
.B activation/auto_activation_volume_list
.P
For more information, see:
.br
.B lvmconfig --withcomments activation/auto_activation_volume_list
.P
To disable auto-activation, explicitly set this list to an empty list,
i.e. auto_activation_volume_list = [ ].
.P
When this setting is undefined (e.g. commented), then all LVs are
auto-activated.
See
.BR lvmautoactivation (7)
for more information about how pvscan is used for autoactivation.

View File

@@ -53,7 +53,6 @@
.P
.BR lvm-fullreport (8),
.BR lvm-lvpoll (8),
.BR lvm2-activation-generator (8),
.BR blkdeactivate (8),
.BR lvmdump (8),
.P

View File

@@ -15,9 +15,6 @@ srcdir = @srcdir@
top_srcdir = @top_srcdir@
top_builddir = @top_builddir@
SOURCES = lvm2_activation_generator_systemd_red_hat.c
TARGETS = lvm2_activation_generator_systemd_red_hat
include $(top_builddir)/make.tmpl
ifeq ("@BUILD_DMEVENTD@", "yes")
@@ -78,17 +75,6 @@ ifeq ("@BLKDEACTIVATE@", "yes")
$(Q) $(INSTALL_SCRIPT) blk_availability_init_red_hat $(initdir)/blk-availability
endif
CFLAGS_lvm2_activation_generator_systemd_red_hat.o += $(EXTRA_EXEC_CFLAGS)
lvm2_activation_generator_systemd_red_hat: $(OBJECTS) $(LVMINTERNAL_LIBS)
@echo " [CC] $@"
$(Q) $(CC) -o $@ $(OBJECTS) $(CFLAGS) $(LDFLAGS) $(EXTRA_EXEC_LDFLAGS) $(ELDFLAGS) $(LVMINTERNAL_LIBS) $(LIBS)
install_systemd_generators:
@echo " [INSTALL] systemd_generators"
$(Q) $(INSTALL_DIR) $(systemd_generator_dir)
$(Q) $(INSTALL_PROGRAM) lvm2_activation_generator_systemd_red_hat $(systemd_generator_dir)/lvm2-activation-generator
install_systemd_units: install_dbus_service
@echo " [INSTALL] systemd_units"
$(Q) $(INSTALL_DIR) $(systemd_unit_dir)

View File

@@ -1,7 +1,7 @@
[Unit]
Description=Availability of block devices
Before=shutdown.target
After=lvm2-activation.service iscsi-shutdown.service iscsi.service iscsid.service fcoe.service rbdmap.service
After=iscsi-shutdown.service iscsi.service iscsid.service fcoe.service rbdmap.service
DefaultDependencies=no
Conflicts=shutdown.target

View File

@@ -1,221 +0,0 @@
/*
* Copyright (C) 2018 Red Hat, Inc. All rights reserved.
*
* This file is part of the device-mapper userspace tools.
*
* 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
*/
// This file contains the unit testable parts of
// lvm2_activation_generator_systemd_red_hat
#include "device_mapper/all.h"
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h> /* For PATH_MAX for musl libc */
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
//----------------------------------------------------------------
static void _error(const char *format, ...) __attribute__ ((format(printf, 1, 2)));
//----------------------------------------------------------------
// I'm rolling my own version of popen() here because I do not want to
// go through the shell.
struct child_process {
pid_t pid;
FILE *fp;
};
static bool _open_child(struct child_process *child, const char *cmd, const char *argv[])
{
int r, pipe_fd[2];
r = pipe(pipe_fd);
if (r < 0) {
_error("call to pipe() failed: %d\n", r);
return false;
}
child->pid = fork();
if (child->pid < 0) {
(void) close(pipe_fd[0]);
(void) close(pipe_fd[1]);
_error("call to fork() failed: %d\n", r);
return false;
}
if (child->pid == 0) {
// child
(void) close(pipe_fd[0]);
if (pipe_fd[1] != STDOUT_FILENO) {
(void) dup2(pipe_fd[1], STDOUT_FILENO);
(void) close(pipe_fd[1]);
}
/* Suppressing any use of syslog */
(void) setenv("LVM_SUPPRESS_SYSLOG", "1", 1);
if (execv(cmd, (char *const *) argv) < 0)
_error("execv failed: %s\n", strerror(errno));
// Shouldn't get here unless exec failed.
exit(1);
} else {
// parent
(void) close(pipe_fd[1]);
child->fp = fdopen(pipe_fd[0], "r");
if (!child->fp) {
_error("call to fdopen() failed\n");
return false;
}
}
return true;
}
// Returns the child's exit status
static bool _close_child(struct child_process *child)
{
int status;
(void) fclose(child->fp);
while (waitpid(child->pid, &status, 0) < 0)
if (errno != EINTR)
return -1;
if (WIFEXITED(status) && !WEXITSTATUS(status))
return true;
return false;
}
//----------------------------------------------------------------
// Aquiring config from the lvmconfig process
#define LVM_CONF_EVENT_ACTIVATION "global/event_activation"
#define LVM_CONF_USE_LVMPOLLD "global/use_lvmpolld"
struct config {
bool event_activation;
bool sysinit_needed;
};
static bool _begins_with(const char *line, const char *prefix, const char **rest)
{
size_t len = strlen(prefix);
if (strlen(line) < len)
return false;
if (strncmp(line, prefix, len))
return false;
*rest = line + len;
return true;
}
static bool _parse_bool(const char *val, bool * result)
{
const char *b = val, *e;
while (*b && isspace(*b))
b++;
if (!*b)
goto parse_error;
e = b;
while (*e && !isspace(*e))
e++;
if ((e - b) != 1)
goto parse_error;
// We only handle '1', or '0'
if (*b == '1') {
*result = true;
return true;
} else if (*b == '0') {
*result = false;
return true;
}
// Fallthrough
parse_error:
_error("couldn't parse bool value '%s'\n", val);
return false;
}
static bool _parse_line(const char *line, struct config *cfg)
{
const char *val;
if (_begins_with(line, "event_activation=", &val)) {
return _parse_bool(val, &cfg->event_activation);
} else if (_begins_with(line, "use_lvmpolld=", &val)) {
bool r;
if (!_parse_bool(val, &r))
return false;
cfg->sysinit_needed = !r;
return true;
}
return false;
}
static bool _get_config(struct config *cfg, const char *lvmconfig_path)
{
static const char *_argv[] = {
"lvmconfig", "--type", "full",
LVM_CONF_EVENT_ACTIVATION, LVM_CONF_USE_LVMPOLLD, NULL
};
bool r = true;
char buffer[256];
struct child_process child;
cfg->event_activation = false;
cfg->sysinit_needed = true;
if (!_open_child(&child, lvmconfig_path, _argv)) {
_error("couldn't open lvmconfig process\n");
return false;
}
while (fgets(buffer, sizeof(buffer), child.fp)) {
if (!_parse_line(buffer, cfg)) {
_error("_parse_line() failed\n");
r = false;
}
}
if (!_close_child(&child)) {
_error("lvmconfig failed\n");
r = false;
}
return r;
}

View File

@@ -10,5 +10,5 @@ Conflicts=shutdown.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=@SBINDIR@/lvm pvscan --cache --activate ay %i
ExecStart=@SBINDIR@/lvm pvscan --cache --activate ay --autoactivation event %i
ExecStop=@SBINDIR@/lvm pvscan --cache %i

View File

@@ -1,233 +0,0 @@
/*
* Copyright (C) 2012 Red Hat, Inc. All rights reserved.
*
* This file is part of the device-mapper userspace tools.
*
* 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
*/
// Code in this file gets included in the unit tests.
#include "generator-internals.c"
// Logging
#define KMSG_DEV_PATH "/dev/kmsg"
static int _kmsg_fd;
static void _log_init(void)
{
// failing is harmless
_kmsg_fd = open(KMSG_DEV_PATH, O_WRONLY | O_NOCTTY);
}
static void _log_exit(void)
{
if (_kmsg_fd != -1)
(void) close(_kmsg_fd);
}
__attribute__ ((format(printf, 1, 2)))
static void _error(const char *format, ...)
{
int n;
va_list ap;
char message[PATH_MAX + 30]; /* +3 for '<n>' where n is the log level and +27 for lvm2-activation-generator: " prefix */
snprintf(message, 31, "<%d>lvm2-activation-generator: ", LOG_ERR);
va_start(ap, format);
n = vsnprintf(message + 30, PATH_MAX, format, ap);
va_end(ap);
if (_kmsg_fd < 0 || (n < 0 || ((unsigned) n + 1 > PATH_MAX)))
return;
/* The n+31: +30 for "<n>lvm2-activation-generator: " prefix and +1 for '\0' suffix */
if (write(_kmsg_fd, message, n + 31) < 0)
_error("Failed to write activation message %s: %m.\n", message);
}
//----------------------------------------------------------------
#define UNIT_TARGET_LOCAL_FS "local-fs-pre.target"
#define UNIT_TARGET_REMOTE_FS "remote-fs-pre.target"
struct generator {
const char *dir;
struct config cfg;
int kmsg_fd;
char unit_path[PATH_MAX];
char target_path[PATH_MAX];
};
enum {
UNIT_EARLY,
UNIT_MAIN,
UNIT_NET
};
static const char *_unit_names[] = {
[UNIT_EARLY] = "lvm2-activation-early.service",
[UNIT_MAIN] = "lvm2-activation.service",
[UNIT_NET] = "lvm2-activation-net.service"
};
//----------------------------------------------------------------
static int register_unit_with_target(struct generator *gen, const char *unit,
const char *target)
{
int r = 1;
if (dm_snprintf(gen->target_path, PATH_MAX, "%s/%s.wants", gen->dir, target) < 0) {
r = 0;
goto out;
}
(void) dm_prepare_selinux_context(gen->target_path, S_IFDIR);
if (mkdir(gen->target_path, 0755) < 0 && errno != EEXIST) {
_error("Failed to create target directory %s: %m.\n", gen->target_path);
r = 0;
goto out;
}
if (dm_snprintf
(gen->target_path, PATH_MAX, "%s/%s.wants/%s", gen->dir, target, unit) < 0) {
r = 0;
goto out;
}
(void) dm_prepare_selinux_context(gen->target_path, S_IFLNK);
if (symlink(gen->unit_path, gen->target_path) < 0) {
_error("Failed to create symlink for unit %s: %m.\n", unit);
r = 0;
}
out:
dm_prepare_selinux_context(NULL, 0);
return r;
}
static int generate_unit(struct generator *gen, int unit)
{
FILE *f;
const char *unit_name = _unit_names[unit];
const char *target_name =
unit == UNIT_NET ? UNIT_TARGET_REMOTE_FS : UNIT_TARGET_LOCAL_FS;
if (dm_snprintf(gen->unit_path, PATH_MAX, "%s/%s", gen->dir, unit_name)
< 0)
return 0;
if (!(f = fopen(gen->unit_path, "wxe"))) {
_error("Failed to create unit file %s: %m.\n", unit_name);
return 0;
}
fputs("# Automatically generated by lvm2-activation-generator.\n"
"#\n"
"# This unit is responsible for direct activation of LVM logical volumes\n"
"# if event-based activation not used (global/event_activation=0 in\n"
"# lvm.conf). Direct LVM activation requires udev to be settled!\n\n"
"[Unit]\n"
"Description=LVM direct activation of logical volumes\n"
"Documentation=man:lvm2-activation-generator(8)\n"
"SourcePath=/etc/lvm/lvm.conf\n" "DefaultDependencies=no\n", f);
fputs("Conflicts=shutdown.target\n", f);
if (unit == UNIT_NET) {
fprintf(f, "After=%s iscsi.service fcoe.service rbdmap.service\n"
"Before=remote-fs-pre.target shutdown.target\n\n"
"[Service]\n"
"ExecStartPre=/usr/bin/udevadm settle\n", _unit_names[UNIT_MAIN]);
} else {
if (unit == UNIT_EARLY)
fputs("After=systemd-udev-settle.service\n"
"Before=cryptsetup.target\n", f);
else
fprintf(f, "After=%s cryptsetup.target\n", _unit_names[UNIT_EARLY]);
fputs("Before=local-fs-pre.target shutdown.target\n"
"Wants=systemd-udev-settle.service\n\n" "[Service]\n", f);
}
fputs("ExecStart=" LVM_PATH " vgchange -aay", f);
if (gen->cfg.sysinit_needed)
fputs(" --sysinit", f);
fputs("\nType=oneshot\n", f);
if (fclose(f) < 0) {
_error("Failed to write unit file %s: %m.\n", unit_name);
return 0;
}
if (!register_unit_with_target(gen, unit_name, target_name)) {
_error("Failed to register unit %s with target %s.\n",
unit_name, target_name);
return 0;
}
return 1;
}
static bool _parse_command_line(struct generator *gen, int argc, const char **argv)
{
if (argc != 4) {
_error("Incorrect number of arguments for activation generator.\n");
return false;
}
gen->dir = argv[1];
return true;
}
static bool _run(int argc, const char **argv)
{
bool r;
mode_t old_mask;
struct generator gen;
if (!_parse_command_line(&gen, argc, argv))
return false;
if (_get_config(&gen.cfg, LVMCONFIG_PATH)) {
if (gen.cfg.event_activation)
// If event_activation=1, pvscan --cache -aay does activation.
return true;
}
/*
* Create the activation units if:
* - _get_config succeeded and event_activation=0
* - _get_config failed, then this is a failsafe fallback
*/
/* mark lvm2-activation.*.service as world-accessible */
old_mask = umask(0022);
r = generate_unit(&gen, UNIT_EARLY) &&
generate_unit(&gen, UNIT_MAIN) && generate_unit(&gen, UNIT_NET);
umask(old_mask);
return r;
}
int main(int argc, const char **argv)
{
bool r;
_log_init();
r = _run(argc, argv);
if (!r)
_error("Activation generator failed.\n");
_log_exit();
return r ? EXIT_SUCCESS : EXIT_FAILURE;
}

View File

@@ -2,7 +2,7 @@
Description=Monitoring of LVM2 mirrors, snapshots etc. using dmeventd or progress polling
Documentation=man:dmeventd(8) man:lvcreate(8) man:lvchange(8) man:vgchange(8)
Requires=dm-event.socket
After=dm-event.socket dm-event.service lvm2-activation.service
After=dm-event.socket dm-event.service
Before=local-fs-pre.target shutdown.target
DefaultDependencies=no
Conflicts=shutdown.target

View File

@@ -414,6 +414,12 @@ EOF
verbose "Converting to VDO pool."
dry "$LVM" lvconvert $YES $VERB $FORCE --config "$PARAMS" -Zn -V "${vdo_logicalSize}k" -n "$LVNAME" --type vdo-pool "$VGNAME/${LVNAME}_vpool"
# Note: that this is spelled OPPOSITE the other $IS_LV checks.
if [ "$IS_LV" = "1" ]; then
verbose "Removing now-unused VDO entry from VDO config."
dry "$VDO" remove $VDOCONF --force --verbose --name "$VDONAME"
fi
rm -fr "$TEMPDIR"
}

View File

@@ -88,6 +88,7 @@ fi
%{_sbindir}/lvmpolld
%endif
%{_mandir}/man5/lvm.conf.5.gz
%{_mandir}/man7/lvmautoactivation.7.gz
%{_mandir}/man7/lvmsystemid.7.gz
%{_mandir}/man7/lvmreport.7.gz
%{_mandir}/man7/lvmraid.7.gz
@@ -100,9 +101,6 @@ fi
%{_mandir}/man8/lvm-config.8.gz
%{_mandir}/man8/lvm-dumpconfig.8.gz
%{_mandir}/man8/lvm.8.gz
%if %{enable_systemd}
%{_mandir}/man8/lvm2-activation-generator.8.gz
%endif
%{_mandir}/man8/lvmconfig.8.gz
%{_mandir}/man8/lvmdevices.8.gz
%{_mandir}/man8/lvmdiskscan.8.gz
@@ -189,7 +187,6 @@ fi
%{_tmpfilesdir}/%{name}.conf
%{_unitdir}/blk-availability.service
%{_unitdir}/lvm2-monitor.service
%attr(555, -, -) %{_prefix}/lib/systemd/system-generators/lvm2-activation-generator
%if %{have_service lvmpolld}
%{_unitdir}/lvm2-lvmpolld.service
%{_unitdir}/lvm2-lvmpolld.socket

View File

@@ -545,4 +545,73 @@ grep "$PVID2" "$DF" |tee out
grep "$dev2" out
not grep "$dev1" out
vgchange -an $vg1
vgchange -an $vg2
vgremove -ff $vg1
vgremove -ff $vg2
# devnames change so the new devname now refers to a filtered device,
# e.g. an mpath or md component, which is not scanned
wait_md_create() {
local md=$1
while :; do
if ! grep "$(basename $md)" /proc/mdstat; then
echo "$md not ready"
cat /proc/mdstat
sleep 2
else
break
fi
done
echo "$md" > WAIT_MD_DEV
}
aux wipefs_a "$dev1"
aux wipefs_a "$dev2"
aux wipefs_a "$dev3"
aux wipefs_a "$dev4"
mddev="/dev/md33"
not grep $mddev /proc/mdstat || skip
rm "$DF"
touch "$DF"
vgcreate $vg1 "$dev1" "$dev2"
cat "$DF"
cp "$DF" "$ORIG"
# PVID with dashes for matching pvs -o+uuid output
OPVID1=`pvs "$dev1" --noheading -o uuid | awk '{print $1}'`
OPVID2=`pvs "$dev2" --noheading -o uuid | awk '{print $1}'`
# PVID without dashes for matching devices file fields
PVID1=`pvs "$dev1" --noheading -o uuid | tr -d - | awk '{print $1}'`
PVID2=`pvs "$dev2" --noheading -o uuid | tr -d - | awk '{print $1}'`
mdadm --create --metadata=1.0 "$mddev" --level 1 --raid-devices=2 "$dev3" "$dev4"
wait_md_create "$mddev"
sed -e "s|DEVNAME=$dev1|DEVNAME=$dev3|" "$ORIG" > tmp1.devices
sed -e "s|IDNAME=$dev1|IDNAME=$dev3|" tmp1.devices > "$DF"
cat "$DF"
pvs -o+uuid |tee out
grep "$dev1" out
grep "$dev2" out
grep "$OPVID1" out
grep "$OPVID2" out
not grep "$dev3" out
not grep "$dev4" out
grep "$dev1" "$DF"
grep "$dev2" "$DF"
grep "$PVID1" "$DF"
grep "$PVID2" "$DF"
not grep "$dev3" "$DF"
not grep "$dev4" "$DF"
mdadm --stop "$mddev"
aux udev_wait
vgremove -ff $vg1

View File

@@ -423,7 +423,7 @@ sed "s/$pvid1/badpvid/" "$DF.orig" |tee $DF
not grep $pvid1 $DF
grep $did1 $DF
lvmdevices --check 2>&1|tee out
not lvmdevices --check 2>&1|tee out
grep $dev1 out
grep badpvid out
grep $pvid1 out
@@ -493,7 +493,7 @@ rm $DF
d1=$(basename $dev1)
d3=$(basename $dev3)
sed "s/$d1/$d3/" "$DF.orig" |tee $DF
lvmdevices --check 2>&1 |tee out
not lvmdevices --check 2>&1 |tee out
grep $dev1 out
lvmdevices --update
@@ -515,7 +515,7 @@ sed "s/$d1/tmp/" "$DF.orig" |tee ${DF}_1
sed "s/$d2/$d1/" "${DF}_1" |tee ${DF}_2
sed "s/tmp/$d2/" "${DF}_2" |tee $DF
rm ${DF}_1 ${DF}_2
lvmdevices --check 2>&1 |tee out
not lvmdevices --check 2>&1 |tee out
grep $dev1 out
grep $dev2 out
@@ -536,7 +536,7 @@ rm $DF
d1=$(basename $dev1)
d3=$(basename $dev3)
sed "s/$d1/$d3/" "$DF.orig" |tee $DF
lvmdevices --check 2>&1 |tee out
not lvmdevices --check 2>&1 |tee out
grep $dev1 out
pvs -o+uuid,deviceid | grep $vg |tee out

View File

@@ -0,0 +1,67 @@
#!/usr/bin/env bash
# Copyright (C) 2021 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
test_description='udev rule and systemd unit run vgchange'
SKIP_WITH_LVMPOLLD=1
SKIP_WITH_LVMLOCKD=1
. lib/inittest
# FIXME: skip until mpath/scsi_debug cleanup works after a failure
skip
modprobe --dry-run scsi_debug || skip
multipath -l || skip
multipath -l | grep scsi_debug && skip
# Turn off multipath_component_detection so that the duplicate
# resolution of mpath components is used.
aux lvmconf 'devices/multipath_component_detection = 0'
# Prevent wwids from being used for filtering.
aux lvmconf 'devices/multipath_wwids_file = "/dev/null"'
# Need to use /dev/mapper/mpath
aux lvmconf 'devices/dir = "/dev"'
aux lvmconf 'devices/scan = "/dev"'
# Could set filter to $MP and the component /dev/sd devs
aux lvmconf "devices/filter = [ \"a|.*|\" ]"
aux lvmconf "devices/global_filter = [ \"a|.*|\" ]"
modprobe scsi_debug dev_size_mb=100 num_tgts=1 vpd_use_hostno=0 add_host=4 delay=20 max_luns=2 no_lun_0=1
sleep 2
multipath -r
sleep 2
MPB=$(multipath -l | grep scsi_debug | cut -f1 -d ' ')
echo $MPB
MP=/dev/mapper/$MPB
echo $MP
pvcreate $MP
vgcreate $vg1 $MP
lvcreate -l1 $vg1
vgchange -an $vg1
pvs |tee out
grep $MP out
for i in $(grep -H scsi_debug /sys/block/sd*/device/model | cut -f4 -d /); do
not grep /dev/$i out;
done
vgchange -an $vg1
vgremove -y $vg1
sleep 2
multipath -f $MP
sleep 1
rmmod scsi_debug

View File

@@ -0,0 +1,52 @@
#!/usr/bin/env bash
# Copyright (C) 2008-2013 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.
SKIP_WITH_LVMLOCKD=1
SKIP_WITH_LVMPOLLD=1
. lib/inittest
aux prepare_devs 2
vgcreate $vg1 "$dev1"
vgchange --setautoactivation n $vg1
UUID1=$(vgs --noheading -o vg_uuid $vg1 | xargs)
lvcreate -l1 -an -n $lv1 $vg1
dd if="$dev1" of="$dev2" bs=1M count=1
aux disable_dev "$dev1"
vgrename $vg1 $vg2
pvchange -u "$dev2"
aux enable_dev "$dev1"
vgs -o+uuid |tee out
grep $vg1 out | tee out1
grep $UUID1 out1
grep $vg2 out | tee out2
grep $UUID1 out2
vgs $vg1
vgs $vg2
lvs $vg1/$lv1
lvs $vg2/$lv1
lvremove $vg1/$lv1
lvremove $vg2/$lv1
lvcreate -l1 -an -n $lv2 $vg1
lvcreate -l1 -an -n $lv3 $vg2
vgchange -u $vg2
vgs -o uuid $vg1 |tee out
grep $UUID1 out
vgs -o uuid $vg2 |tee out
not grep $UUID1 out
vgremove -ff $vg1
vgremove -ff $vg2

View File

@@ -57,9 +57,11 @@ dd if="$dev1" of=dev1_backup bs=1M
# pvcreate and pvremove can be forced even if the PV is marked as used
pvremove -ff -y "$dev1"
lvmdevices --deldev "$dev1" || true
dd if=dev1_backup of="$dev1" bs=1M
pvcreate -ff -y "$dev1"
dd if=dev1_backup of="$dev1" bs=1M
lvmdevices --adddev "$dev1" || true
# prepare a VG with $dev1 and $dev both having 1 MDA
aux enable_dev "$dev2"
@@ -116,9 +118,11 @@ dd if="$dev1" of=dev1_backup bs=1M
# pvcreate and pvremove can be forced even if the PV is marked as used
pvremove -ff -y "$dev1"
lvmdevices --deldev "$dev1" || true
dd if=dev1_backup of="$dev1" bs=1M
pvcreate -ff -y "$dev1"
dd if=dev1_backup of="$dev1" bs=1M
lvmdevices --adddev "$dev1" || true
# prepare a VG with $dev1 and $dev both having 1 MDA
aux enable_dev "$dev2"

View File

@@ -71,15 +71,11 @@ wipe_all() {
done
}
# udevadm trigger runs udev rule which runs systemd-run --no-wait vgchange -aay
# Because of --no-wait, we need to wait for the transient systemd
# service to be gone before checking the effects of the vgchange.
wait_lvm_activate() {
local vgw=$1
local wait=0
while systemctl status lvm-activate-$vgw > /dev/null && test "$wait" -le 30; do
while systemctl status lvm-activate-$vgw | grep "active (running)" && test "$wait" -le 30; do
sleep .2
wait=$(( wait + 1 ))
done
@@ -219,6 +215,8 @@ udevadm trigger -c add /sys/block/$BDEV3
aux udev_wait
wait_lvm_activate $vg4
ls "$RUNDIR/lvm/pvs_lookup/"
cat "$RUNDIR/lvm/pvs_lookup/$vg4" || true
ls "$RUNDIR/lvm/pvs_online/$PVID1"
ls "$RUNDIR/lvm/pvs_online/$PVID2"
ls "$RUNDIR/lvm/pvs_online/$PVID3"
@@ -375,6 +373,7 @@ touch $DF
mdadm --create --metadata=1.0 "$mddev" --level 1 --chunk=64 --raid-devices=2 "$dev1" "$dev2"
wait_md_create "$mddev"
vgcreate $vg9 "$mddev"
lvmdevices --adddev "$mddev" || true
PVIDMD=`pvs $mddev --noheading -o uuid | tr -d - | awk '{print $1}'`
BDEVMD=$(basename "$mddev")
@@ -382,8 +381,13 @@ BDEVMD=$(basename "$mddev")
lvcreate -l1 -an -n $lv1 $vg9
lvcreate -l1 -an -n $lv2 $vg9
mdadm --stop "$mddev"
systemctl stop lvm-activate-$vg9 || true
_clear_online_files
mdadm --assemble "$mddev" "$dev1" "$dev2"
# this trigger might be redundant because the mdadm --assemble
# probably triggers an add uevent
udevadm trigger --settle -c add /sys/block/$BDEVMD
wait_lvm_activate $vg9
@@ -401,3 +405,61 @@ mdadm --stop "$mddev"
aux udev_wait
wipe_all
systemctl stop lvm-activate-$vg1
systemctl stop lvm-activate-$vg2
systemctl stop lvm-activate-$vg3
systemctl stop lvm-activate-$vg4
systemctl stop lvm-activate-$vg5
systemctl stop lvm-activate-$vg6
systemctl stop lvm-activate-$vg7
systemctl stop lvm-activate-$vg8
systemctl stop lvm-activate-$vg9
# no devices file, filter with symlink of PV
# the pvscan needs to look at all dev names to
# match the symlink in the filter with the
# dev name (or major minor) passed to pvscan.
# This test doesn't really belong in this file
# because it's not testing lvm-activate.
aux lvmconf 'devices/use_devicesfile = 0'
_clear_online_files
rm "$DF"
vgcreate $vg10 "$dev1"
lvcreate -l1 -an -n $lv1 $vg10 "$dev1"
PVID1=$(pvs "$dev1" --noheading -o uuid | tr -d - | awk '{print $1}')
# PVID with dashes
OPVID1=`pvs "$dev1" --noheading -o uuid | awk '{print $1}'`
udevadm trigger --settle -c add /sys/block/$BDEV1
# uevent from the trigger should create this symlink
ls /dev/disk/by-id/lvm-pv-uuid-$OPVID1
vgchange -an $vg10
systemctl stop lvm-activate-$vg10
_clear_online_files
aux lvmconf "devices/filter = [ \"a|/dev/disk/by-id/lvm-pv-uuid-$OPVID1|\", \"r|.*|\" ]"
aux lvmconf 'devices/global_filter = [ "a|.*|" ]'
pvscan --cache -aay "$dev1"
check lv_field $vg10/$lv1 lv_active "active"
vgchange -an $vg10
_clear_online_files
aux lvmconf 'devices/filter = [ "a|lvm-pv-uuid|", "r|.*|" ]'
aux lvmconf 'devices/global_filter = [ "a|.*|" ]'
pvscan --cache -aay "$dev1"
check lv_field $vg10/$lv1 lv_active "active"
vgchange -an $vg10
vgremove -y $vg10
wipe_all

View File

@@ -0,0 +1,199 @@
SKIP_WITH_LVMPOLLD=1
SKIP_WITH_LVMLOCKD=1
RUNDIR="/run"
test -d "$RUNDIR" || RUNDIR="/var/run"
PVS_ONLINE_DIR="$RUNDIR/lvm/pvs_online"
VGS_ONLINE_DIR="$RUNDIR/lvm/vgs_online"
PVS_LOOKUP_DIR="$RUNDIR/lvm/pvs_lookup"
_clear_online_files() {
# wait till udev is finished
aux udev_wait
rm -f "$PVS_ONLINE_DIR"/*
rm -f "$VGS_ONLINE_DIR"/*
rm -f "$PVS_LOOKUP_DIR"/*
}
. lib/inittest
aux prepare_devs 4
DFDIR="$LVM_SYSTEM_DIR/devices"
mkdir -p "$DFDIR" || true
DF="$DFDIR/system.devices"
# Because mapping devno to devname gets dm name from sysfs
aux lvmconf 'devices/scan = "/dev"'
base1=$(basename $dev1)
base2=$(basename $dev2)
base3=$(basename $dev3)
base4=$(basename $dev4)
bd1=/dev/mapper/$base1
bd2=/dev/mapper/$base2
bd3=/dev/mapper/$base3
bd4=/dev/mapper/$base4
aux extend_filter "a|/dev/mapper/$base1|"
aux extend_filter "a|/dev/mapper/$base2|"
aux extend_filter "a|/dev/mapper/$base3|"
aux extend_filter "a|/dev/mapper/$base4|"
# Changing names will confuse df based on devname
if lvmdevices; then
rm -f "$DF"
touch "$DF"
lvmdevices --adddev "$bd1"
lvmdevices --adddev "$bd2"
lvmdevices --adddev "$bd3"
lvmdevices --adddev "$bd4"
cat "$DF"
fi
# Using $bd instead of $dev because validation of pvid file content
# checks that the devname begins with /dev.
# FIXME: test vgchange aay with pvs_online that includes devname in pvid file
# and the devices file entry uses devname with a stale name.
vgcreate $vg1 "$bd1" "$bd2"
vgcreate $vg2 "$bd3"
pvcreate "$bd4"
lvcreate -l1 -n $lv1 -an $vg1
lvcreate -l1 -n $lv2 -an $vg1
lvcreate -l1 -n $lv1 -an $vg2
# Expected use, with vg name and all online files exist for vgchange.
_clear_online_files
pvscan --cache "$bd1"
pvscan --cache "$bd2"
vgchange -aay --autoactivation event $vg1
check lv_field $vg1/$lv1 lv_active "active"
check lv_field $vg1/$lv2 lv_active "active"
check lv_field $vg2/$lv1 lv_active ""
pvscan --cache "$bd3"
vgchange -aay --autoactivation event $vg2
check lv_field $vg2/$lv1 lv_active "active"
# Count io to check the pvs_online optimization
# is working to limit scanning.
if which strace; then
vgchange -an
_clear_online_files
pvscan --cache "$bd1"
pvscan --cache "$bd2"
strace -e io_submit vgchange -aay --autoactivation event $vg1 2>&1|tee trace.out
test "$(grep io_submit trace.out | wc -l)" -eq 3
rm trace.out
strace -e io_submit pvscan --cache "$bd3" 2>&1|tee trace.out
test "$(grep io_submit trace.out | wc -l)" -eq 1
rm trace.out
strace -e io_submit vgchange -aay --autoactivation event $vg2 2>&1|tee trace.out
test "$(grep io_submit trace.out | wc -l)" -eq 2
rm trace.out
fi
# non-standard usage: no VG name arg, vgchange will only used pvs_online files
vgchange -an
_clear_online_files
vgchange -aay --autoactivation event
check lv_field $vg1/$lv1 lv_active ""
check lv_field $vg1/$lv2 lv_active ""
check lv_field $vg2/$lv1 lv_active ""
pvscan --cache "$bd1"
vgchange -aay --autoactivation event
check lv_field $vg1/$lv1 lv_active ""
check lv_field $vg1/$lv2 lv_active ""
check lv_field $vg2/$lv1 lv_active ""
pvscan --cache "$bd2"
vgchange -aay --autoactivation event
check lv_field $vg1/$lv1 lv_active "active"
check lv_field $vg1/$lv2 lv_active "active"
check lv_field $vg2/$lv1 lv_active ""
pvscan --cache "$bd3"
vgchange -aay --autoactivation event
check lv_field $vg2/$lv1 lv_active "active"
# non-standard usage: include VG name arg, but missing or incomplete pvs_online files
vgchange -an
_clear_online_files
# all missing pvs_online, vgchange falls back to full label scan
vgchange -aay --autoactivation event $vg1
check lv_field $vg1/$lv1 lv_active "active"
check lv_field $vg1/$lv2 lv_active "active"
vgchange -an
_clear_online_files
# incomplete pvs_online, vgchange falls back to full label scan
pvscan --cache "$bd1"
vgchange -aay --autoactivation event $vg1
check lv_field $vg1/$lv1 lv_active "active"
check lv_field $vg1/$lv2 lv_active "active"
vgchange -an
_clear_online_files
# incomplete pvs_online, pvs_online from different vg,
# no pvs_online found for vg arg so vgchange falls back to full label scan
pvscan --cache "$bd3"
vgchange -aay --autoactivation event $vg1
check lv_field $vg1/$lv1 lv_active "active"
check lv_field $vg1/$lv2 lv_active "active"
check lv_field $vg2/$lv1 lv_active ""
vgchange -aay --autoactivation event $vg2
check lv_field $vg2/$lv1 lv_active "active"
vgchange -an
_clear_online_files
# same tests but using command options matching udev rule
pvscan --cache --listvg --checkcomplete --vgonline --autoactivation event --udevoutput --journal=output "$bd1"
pvscan --cache --listvg --checkcomplete --vgonline --autoactivation event --udevoutput --journal=output "$bd2"
vgchange -aay --autoactivation event $vg1
check lv_field $vg1/$lv1 lv_active "active"
check lv_field $vg1/$lv2 lv_active "active"
check lv_field $vg2/$lv1 lv_active ""
pvscan --cache --listvg --checkcomplete --vgonline --autoactivation event --udevoutput --journal=output "$bd3"
vgchange -aay --autoactivation event $vg2
check lv_field $vg2/$lv1 lv_active "active"
vgchange -an $vg1
vgchange -an $vg2
# with a full pvscan --cache
_clear_online_files
pvscan --cache
check lv_field $vg1/$lv1 lv_active ""
check lv_field $vg2/$lv1 lv_active ""
vgchange -aay --autoactivation event $vg1
vgchange -aay --autoactivation event $vg2
check lv_field $vg1/$lv1 lv_active "active"
check lv_field $vg2/$lv1 lv_active "active"
vgchange -an $vg1
vgchange -an $vg2
vgremove -f $vg1
vgremove -f $vg2

View File

@@ -16,7 +16,6 @@
UNIT_SOURCE=\
device_mapper/vdo/status.c \
\
test/unit/activation-generator_t.c \
test/unit/bcache_t.c \
test/unit/bcache_utils_t.c \
test/unit/bitset_t.c \

View File

@@ -1,268 +0,0 @@
/*
* Copyright (C) 2018 Red Hat, Inc. All rights reserved.
*
* This file is part of LVM2.
*
* 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
*/
#include "units.h"
#include "scripts/generator-internals.c"
//----------------------------------------------------------------
static void _error(const char *format, ...)
{
va_list ap;
va_start(ap, format);
vfprintf(stderr, format, ap);
va_end(ap);
}
//----------------------------------------------------------------
struct bw_test {
const char *input;
const char *prefix;
const char *val;
};
static void _test_begins_with(void *fixture)
{
static struct bw_test _tests[] = {
{"", "foo", NULL},
{"lskdj", "foo", NULL},
{"foo", "foobar", NULL},
{"fish", "fish", ""},
{"foo=bar ", "foo=", "bar "},
};
unsigned i;
for (i = 0; i < DM_ARRAY_SIZE(_tests); i++) {
const char *val;
struct bw_test *t = _tests + i;
if (t->val) {
if (!_begins_with(t->input, t->prefix, &val))
test_fail("_begins_with('%s', '%s') failed", t->input, t->prefix);
if (strcmp(val, t->val))
test_fail("_begins_with('%s', '%s') -> '%s', expected '%s'",
t->input, t->prefix, val, t->val);
} else {
if (_begins_with(t->input, t->prefix, &val))
test_fail("_begins_with('%s', '%s') unexpectedly succeeded",
t->input, t->prefix);
}
}
}
struct pb_test {
const char *input;
bool parsed;
bool result;
};
static const char *_bool(bool v)
{
return v ? "true" : "false";
}
static void _test_parse_bool(void *fixture)
{
static struct pb_test _tests[] = {
{"", false, false},
{"fish", false, false},
{"true", false, false},
{"false", false, false},
{"1", true, true},
{" \t 1\t\t", true, true},
{"0", true, false},
{" \t0 ", true, false}
};
unsigned i;
for (i = 0; i < DM_ARRAY_SIZE(_tests); i++) {
bool result;
struct pb_test *t = _tests + i;
if (t->parsed) {
if (!_parse_bool(t->input, &result))
test_fail("_parse_bool('%s') unexpectedly failed", t->input);
if (result != t->result)
test_fail("_parse_bool('%s') -> %s", t->input, _bool(result));
} else {
if (_parse_bool(t->input, &result))
test_fail("_parse_bool('%s') unexpectedly succeeded", t->input);
}
}
}
struct pl_test {
const char *input;
bool success;
bool event_activation;
bool sysinit_needed;
};
static void _test_parse_line(void *fixture)
{
static struct pl_test _tests[] = {
{"", false, false, false},
{"sldkjfs", false, false, false},
{"event_activation=1", true, true, true},
{"event_activation=0", true, false, true},
{"use_lvmpolld=1", true, false, false},
{"use_lvmpolld=0", true, false, true}
};
unsigned i;
for (i = 0; i< DM_ARRAY_SIZE(_tests); i++) {
bool r;
struct config cfg = {
.sysinit_needed = true
};
struct pl_test *t = _tests + i;
r = _parse_line(t->input, &cfg);
if (t->success) {
if (!r)
test_fail("_parse_line('%s') failed", t->input);
if (cfg.event_activation != t->event_activation)
test_fail("_parse_line('%s') -> event_activation='%s'",
t->input, _bool(cfg.event_activation));
if (cfg.sysinit_needed != t->sysinit_needed)
test_fail("_parse_line('%s') -> sysinit_needed='%s'",
t->input, _bool(cfg.sysinit_needed));
} else if (r)
test_fail("_parse_line('%s') succeeded", t->input);
}
}
static void _test_get_config_bad_path(void *fixture)
{
struct config cfg;
if (_get_config(&cfg, "/usr/bin/no-such-file"))
test_fail("_get_config() succeeded despite a bad lvmconfig path");
}
static void _test_get_config_bad_exit(void *fixture)
{
struct config cfg;
if (_get_config(&cfg, "/usr/bin/false"))
test_fail("_get_config() succeeded despite a bad lvmconfig exit");
}
struct gc_test {
const char *output;
bool success;
bool event_activation;
bool sysinit_needed;
};
static const char *_fake_lvmconfig(const char *output)
{
const char *path = "./fake-lvmconfig";
FILE *fp = fopen(path, "w");
if (!fp)
return NULL;
fprintf(fp, "#!/usr/bin/env bash\n");
fprintf(fp, "cat <<EOF\n");
fprintf(fp, "%s", output);
fprintf(fp, "EOF\n");
(void) fclose(fp);
if (chmod(path, 0770))
test_fail("chmod 0777 failed on path %s", path);
return path;
}
static void _test_get_config(void *fixture)
{
static struct gc_test _tests[] = {
{"", true, false, true},
{"lsdjkf\n\n\n", false, false, false},
{"event_activation=0\nuse_lvmpolld=1\n", true, false, false},
{"event_activation=1\nuse_lvmpolld=1\n", true, true, false},
{"event_activation=1\nuse_lvmpolld=0\n", true, true, true},
};
bool r;
unsigned i;
const char *path;
for (i = 0; i < DM_ARRAY_SIZE(_tests); i++) {
struct gc_test *t = _tests + i;
struct config cfg = {
.sysinit_needed = true
};
path = _fake_lvmconfig(t->output);
if (!path)
test_fail("couldn't create fake lvmconfig");
r = _get_config(&cfg, path);
if (t->success) {
if (!r)
test_fail("_get_config() <- '%s' failed", t->output);
if (t->event_activation != cfg.event_activation)
test_fail("_get_config() <- '%s', event_activation = %s",
t->output, _bool(cfg.event_activation));
if (t->sysinit_needed != cfg.sysinit_needed)
test_fail("_get_config() <- '%s', sysinit = %s",
t->output, _bool(cfg.sysinit_needed));
} else {
if (r)
test_fail("_get_config() <- '%s' unexpectedly succeeded", t->output);
}
(void) unlink(path);
}
}
//----------------------------------------------------------------
#define T(path, desc, fn) register_test(ts, "/activation-generator/" path, desc, fn)
static struct test_suite *_tests(void)
{
struct test_suite *ts = test_suite_create(NULL, NULL);
if (!ts) {
fprintf(stderr, "out of memory\n");
exit(1);
};
T("begins-with", "Test cases for _begins_with()", _test_begins_with);
T("parse-bool", "Test cases for _parse_bool()", _test_parse_bool);
T("parse-line", "Test cases for _parse_line()", _test_parse_line);
T("get-config-bad-path", "_get_config() needs a valid lvmconfig path", _test_get_config_bad_path);
T("get-config-bad-exit", "lvmconfig bad exit code gets propagated", _test_get_config_bad_exit);
T("get-config", "Test cases for _get_config()", _test_get_config);
return ts;
}
void activation_generator_tests(struct dm_list *all_tests)
{
dm_list_add(all_tests, &_tests()->list);
}
//----------------------------------------------------------------

View File

@@ -20,7 +20,6 @@
//-----------------------------------------------------------------
// Declare the function that adds tests suites here ...
void activation_generator_tests(struct dm_list *suites);
void bcache_tests(struct dm_list *suites);
void bcache_utils_tests(struct dm_list *suites);
void bitset_tests(struct dm_list *suites);
@@ -37,7 +36,6 @@ void vdo_tests(struct dm_list *suites);
// ... and call it in here.
static inline void register_all_tests(struct dm_list *suites)
{
activation_generator_tests(suites);
bcache_tests(suites);
bcache_utils_tests(suites);
bitset_tests(suites);

View File

@@ -87,6 +87,12 @@ arg(atversion_ARG, '\0', "atversion", string_VAL, 0, 0,
"which does not contain any newer settings for which LVM would\n"
"issue a warning message when checking the configuration.\n")
arg(autoactivation_ARG, '\0', "autoactivation", string_VAL, 0, 0,
"Specify if autoactivation is being used from an event.\n"
"This allows the command to apply settings that are specific\n"
"to event activation, such as device scanning optimizations\n"
"using pvs_online files created by event-based pvscans.\n")
arg(setautoactivation_ARG, '\0', "setautoactivation", bool_VAL, 0, 0,
"Set the autoactivation property on a VG or LV.\n"
"Display the property with vgs or lvs \"-o autoactivation\".\n"
@@ -153,7 +159,8 @@ arg(cachesize_ARG, '\0', "cachesize", sizemb_VAL, 0, 0,
"The size of cache to use.\n")
arg(check_ARG, '\0', "check", 0, 0, 0,
"Check the content of the devices file.\n")
"Checks the content of the devices file.\n"
"Reports incorrect device names or PVIDs for entries.\n")
arg(commandprofile_ARG, '\0', "commandprofile", string_VAL, 0, 0,
"The command profile to use for command configuration.\n"

View File

@@ -1359,10 +1359,10 @@ OO: --aligned, --all, --binary, --colon, --columns,
--configreport ConfigReport, --foreign, --history, --ignorelockingfailure,
--logonly, --maps, --noheadings,
--nosuffix, --options String, --sort String, --readonly,
--reportformat ReportFmt, --segments, --select String, --separator String,
--segments, --select String, --separator String,
--shared, --unbuffered, --units Units
OP: VG|LV|Tag ...
IO: --partial, --ignoreskippedcluster
IO: --partial, --ignoreskippedcluster, --reportformat ReportFmt
ID: lvdisplay_general
---
@@ -1590,10 +1590,10 @@ pvdisplay
OO: --aligned, --all, --binary, --colon, --columns, --configreport ConfigReport,
--foreign, --ignorelockingfailure,
--logonly, --maps, --noheadings, --nosuffix, --options String,
--readonly, --reportformat ReportFmt, --select String, --separator String, --shared,
--readonly, --select String, --separator String, --shared,
--short, --sort String, --unbuffered, --units Units
OP: PV|Tag ...
IO: --ignoreskippedcluster
IO: --ignoreskippedcluster, --reportformat ReportFmt
ID: pvdisplay_general
---
@@ -1642,14 +1642,15 @@ DESC: Record that a PV is online or offline.
pvscan --cache_long --activate ay
OO: --ignorelockingfailure, --reportformat ReportFmt,
--major Number, --minor Number, --noudevsync
--major Number, --minor Number, --noudevsync, --autoactivation String
OP: PV|String ...
IO: --background
ID: pvscan_cache
DESC: Record that a PV is online and autoactivate the VG if complete.
pvscan --cache_long --listvg PV
OO: --ignorelockingfailure, --checkcomplete, --vgonline, --udevoutput
OO: --ignorelockingfailure, --checkcomplete, --vgonline, --udevoutput,
--autoactivation String
ID: pvscan_cache
DESC: Record that a PV is online and list the VG using the PV.
@@ -1747,7 +1748,8 @@ DESC: Start or stop processing LV conversions.
vgchange --activate Active
OO: --activationmode ActivationMode, --ignoreactivationskip, --partial, --sysinit,
--readonly, --ignorelockingfailure, --monitor Bool, --poll Bool, OO_VGCHANGE
--readonly, --ignorelockingfailure, --monitor Bool, --poll Bool,
--autoactivation String, OO_VGCHANGE
OP: VG|Tag|Select ...
IO: --ignoreskippedcluster
ID: vgchange_activate
@@ -1809,10 +1811,10 @@ vgdisplay
OO: --activevolumegroups, --aligned, --binary, --colon, --columns,
--configreport ConfigReport, --foreign, --ignorelockingfailure,
--logonly, --noheadings, --nosuffix,
--options String, --readonly, --reportformat ReportFmt, --select String,
--options String, --readonly, --select String,
--shared, --short, --separator String, --sort String, --unbuffered, --units Units
OP: VG|Tag ...
IO: --partial, --ignoreskippedcluster
IO: --partial, --ignoreskippedcluster, --reportformat ReportFmt
ID: vgdisplay_general
---

View File

@@ -824,12 +824,16 @@ static int _lvcreate_params(struct cmd_context *cmd,
autobackup_ARG,\
available_ARG,\
contiguous_ARG,\
devices_ARG,\
devicesfile_ARG,\
ignoreactivationskip_ARG,\
ignoremonitoring_ARG,\
journal_ARG,\
metadataprofile_ARG,\
monitor_ARG,\
mirrors_ARG,\
name_ARG,\
nohints_ARG,\
noudevsync_ARG,\
permission_ARG,\
persistent_ARG,\

View File

@@ -2393,8 +2393,10 @@ static void _reset_current_settings_to_default(struct cmd_context *cmd)
static void _get_current_output_settings_from_args(struct cmd_context *cmd)
{
if (arg_is_set(cmd, udevoutput_ARG))
if (arg_is_set(cmd, udevoutput_ARG)) {
cmd->current_settings.suppress = 1;
cmd->udevoutput = 1;
}
if (arg_is_set(cmd, debug_ARG))
cmd->current_settings.debug = _LOG_FATAL + (arg_count(cmd, debug_ARG) - 1);
@@ -3058,6 +3060,7 @@ int lvm_run_command(struct cmd_context *cmd, int argc, char **argv)
int i;
int skip_hyphens;
int refresh_done = 0;
int io;
/* Avoid excessive access to /etc/localtime and set TZ variable for glibc
* so it does not need to check /etc/localtime everytime that needs that info */
@@ -3140,6 +3143,20 @@ int lvm_run_command(struct cmd_context *cmd, int argc, char **argv)
if (!(cmd->command = _find_command(cmd, cmd->name, &argc, argv)))
return EINVALID_CMD_LINE;
/*
* If option --foo is set which is listed in IO (ignore option) in
* command-lines.in, then unset foo. Commands won't usually use an
* ignored option, but there can be shared code that checks for --foo,
* and should not find it to be set.
*/
for (io = 0; io < cmd->command->io_count; io++) {
int opt = cmd->command->ignore_opt_args[io].opt;
if (arg_is_set(cmd, opt)) {
log_debug("Ignore opt %d", opt);
cmd->opt_arg_values[opt].count = 0;
}
}
/*
* Remaining position args after command name and --options are removed.
*/
@@ -3318,8 +3335,8 @@ int lvm_run_command(struct cmd_context *cmd, int argc, char **argv)
* ignore everything supplied on the command line of the
* completed command.
*/
_reset_current_settings_to_default(cmd);
_apply_current_settings(cmd);
//_reset_current_settings_to_default(cmd);
//_apply_current_settings(cmd);
/*
* free off any memory the command used.

View File

@@ -128,7 +128,6 @@ int lvmdevices(struct cmd_context *cmd, int argc, char **argv)
struct device *dev;
struct dev_use *du, *du2;
const char *deviceidtype;
int changes = 0;
dm_list_init(&search_pvids);
dm_list_init(&found_devs);
@@ -176,13 +175,19 @@ int lvmdevices(struct cmd_context *cmd, int argc, char **argv)
log_error("Failed to read the devices file.");
return ECMD_FAILED;
}
prepare_open_file_limit(cmd, dm_list_size(&cmd->use_devices));
dev_cache_scan(cmd);
device_ids_match(cmd);
if (arg_is_set(cmd, check_ARG) || arg_is_set(cmd, update_ARG)) {
int search_count = 0;
int update_needed = 0;
int invalid = 0;
unlink_searched_devnames(cmd);
label_scan_setup_bcache();
dm_list_iterate_items(du, &cmd->use_devices) {
@@ -222,6 +227,8 @@ int lvmdevices(struct cmd_context *cmd, int argc, char **argv)
* run just above.
*/
device_ids_validate(cmd, NULL, &invalid, 1);
if (invalid)
update_needed = 1;
/*
* Find and fix any devname entries that have moved to a
@@ -237,33 +244,24 @@ int lvmdevices(struct cmd_context *cmd, int argc, char **argv)
label_scan_invalidate(du->dev);
}
/*
* check du->part
*/
dm_list_iterate_items(du, &cmd->use_devices) {
int part = 0;
if (!du->dev)
continue;
dev = du->dev;
dev_get_partition_number(dev, &part);
if (part != du->part) {
log_warn("Device %s partition %u has incorrect PART in devices file (%u)",
dev_name(dev), part, du->part);
du->part = part;
changes++;
}
}
if (arg_is_set(cmd, update_ARG)) {
if (invalid || !dm_list_empty(&found_devs)) {
if (update_needed || !dm_list_empty(&found_devs)) {
if (!device_ids_write(cmd))
goto_bad;
log_print("Updated devices file to version %s", devices_file_version());
} else {
log_print("No update for devices file is needed.");
}
} else {
/*
* --check exits with an error if the devices file
* needs updates, i.e. running --update would make
* changes.
*/
if (update_needed) {
log_error("Updates needed for devices file.");
goto bad;
}
}
goto out;
}

View File

@@ -18,6 +18,7 @@
#include "lib/cache/lvmcache.h"
#include "lib/metadata/metadata.h"
#include "lib/label/hints.h"
#include "lib/device/online.h"
#include <dirent.h>
@@ -42,10 +43,6 @@ struct pvscan_aa_params {
*/
static struct volume_group *saved_vg;
static const char *_pvs_online_dir = DEFAULT_RUN_DIR "/pvs_online";
static const char *_vgs_online_dir = DEFAULT_RUN_DIR "/vgs_online";
static const char *_pvs_lookup_dir = DEFAULT_RUN_DIR "/pvs_lookup";
static int _pvscan_display_pv(struct cmd_context *cmd,
struct physical_volume *pv,
struct pvscan_params *params)
@@ -179,93 +176,12 @@ out:
return ret;
}
/*
* Avoid a duplicate pvscan[%d] prefix when logging to the journal.
* FIXME: this should probably replace if (udevoutput) with
* if (log_journal & LOG_JOURNAL_OUTPUT)
*/
#define log_print_pvscan(cmd, fmt, args...) \
do \
if (arg_is_set(cmd, udevoutput_ARG)) \
log_print(fmt, ##args); \
else \
log_print("pvscan[%d] " fmt, getpid(), ##args); \
while (0)
#define log_error_pvscan(cmd, fmt, args...) \
do \
if (arg_is_set(cmd, udevoutput_ARG)) \
log_error(fmt, ##args); \
else \
log_error("pvscan[%d] " fmt, getpid(), ##args); \
while (0)
static char *_vgname_in_pvid_file_buf(char *buf)
{
char *p, *n;
/*
* file contains:
* <major>:<minor>\n
* vg:<vgname>\n\0
*/
if (!(p = strchr(buf, '\n')))
return NULL;
p++; /* skip \n */
if (*p && !strncmp(p, "vg:", 3)) {
if ((n = strchr(p, '\n')))
*n = '\0';
return p + 3;
}
return NULL;
}
#define MAX_PVID_FILE_SIZE 512
static int _online_pvid_file_read(char *path, int *major, int *minor, char *vgname)
{
char buf[MAX_PVID_FILE_SIZE] = { 0 };
char *name;
int fd, rv;
fd = open(path, O_RDONLY);
if (fd < 0) {
log_warn("Failed to open %s", path);
return 0;
}
rv = read(fd, buf, sizeof(buf) - 1);
if (close(fd))
log_sys_debug("close", path);
if (!rv || rv < 0) {
log_warn("No info in %s", path);
return 0;
}
buf[rv] = 0; /* \0 terminated buffer */
if (sscanf(buf, "%d:%d", major, minor) != 2) {
log_warn("No device numbers in %s", path);
return 0;
}
/* vgname points to an offset in buf */
if ((name = _vgname_in_pvid_file_buf(buf)))
strncpy(vgname, name, NAME_LEN);
else
log_debug("No vgname in %s", path);
return 1;
}
static void _lookup_file_remove(char *vgname)
{
char path[PATH_MAX];
if (dm_snprintf(path, sizeof(path), "%s/%s", _pvs_lookup_dir, vgname) < 0) {
log_error("Path %s/%s is too long.", _pvs_lookup_dir, vgname);
if (dm_snprintf(path, sizeof(path), "%s/%s", PVS_LOOKUP_DIR, vgname) < 0) {
log_error("Path %s/%s is too long.", PVS_LOOKUP_DIR, vgname);
return;
}
@@ -275,27 +191,6 @@ static void _lookup_file_remove(char *vgname)
log_sys_debug("unlink", path);
}
/*
* When a PV goes offline, remove the vg online file for that VG
* (even if other PVs for the VG are still online). This means
* that the vg will be activated again when it becomes complete.
*/
void online_vg_file_remove(const char *vgname)
{
char path[PATH_MAX];
if (dm_snprintf(path, sizeof(path), "%s/%s", _vgs_online_dir, vgname) < 0) {
log_error("Path %s/%s is too long.", _vgs_online_dir, vgname);
return;
}
log_debug("Unlink vg online: %s", path);
if (unlink(path) && (errno != ENOENT))
log_sys_debug("unlink", path);
}
/*
* When a device goes offline we only know its major:minor, not its PVID.
* Since the dev isn't around, we can't read it to get its PVID, so we have to
@@ -314,7 +209,7 @@ static void _online_pvid_file_remove_devno(int major, int minor)
log_debug("Remove pv online devno %d:%d", major, minor);
if (!(dir = opendir(_pvs_online_dir)))
if (!(dir = opendir(PVS_ONLINE_DIR)))
return;
while ((de = readdir(dir))) {
@@ -322,13 +217,13 @@ static void _online_pvid_file_remove_devno(int major, int minor)
continue;
memset(path, 0, sizeof(path));
snprintf(path, sizeof(path), "%s/%s", _pvs_online_dir, de->d_name);
snprintf(path, sizeof(path), "%s/%s", PVS_ONLINE_DIR, de->d_name);
file_major = 0;
file_minor = 0;
memset(file_vgname, 0, sizeof(file_vgname));
_online_pvid_file_read(path, &file_major, &file_minor, file_vgname);
online_pvid_file_read(path, &file_major, &file_minor, file_vgname, NULL);
if ((file_major == major) && (file_minor == minor)) {
log_debug("Unlink pv online %s", path);
@@ -342,7 +237,7 @@ static void _online_pvid_file_remove_devno(int major, int minor)
}
}
if (closedir(dir))
log_sys_debug("closedir", _pvs_online_dir);
log_sys_debug("closedir", PVS_ONLINE_DIR);
}
static void _online_files_remove(const char *dirpath)
@@ -367,128 +262,6 @@ static void _online_files_remove(const char *dirpath)
log_sys_debug("closedir", dirpath);
}
static int _online_pvid_file_create(struct cmd_context *cmd, struct device *dev, const char *vgname)
{
char path[PATH_MAX];
char buf[MAX_PVID_FILE_SIZE] = { 0 };
char file_vgname[NAME_LEN];
int file_major = 0, file_minor = 0;
int major, minor;
int fd;
int rv;
int len;
int len1 = 0;
int len2 = 0;
major = (int)MAJOR(dev->dev);
minor = (int)MINOR(dev->dev);
if (dm_snprintf(path, sizeof(path), "%s/%s", _pvs_online_dir, dev->pvid) < 0) {
log_error_pvscan(cmd, "Path %s/%s is too long.", _pvs_online_dir, dev->pvid);
return 0;
}
if ((len1 = dm_snprintf(buf, sizeof(buf), "%d:%d\n", major, minor)) < 0) {
log_error_pvscan(cmd, "Cannot create online file path for %s %d:%d.", dev_name(dev), major, minor);
return 0;
}
if (vgname) {
if ((len2 = dm_snprintf(buf + len1, sizeof(buf) - len1, "vg:%s\n", vgname)) < 0) {
log_print_pvscan(cmd, "Incomplete online file for %s %d:%d vg %s.", dev_name(dev), major, minor, vgname);
/* can still continue without vgname */
len2 = 0;
}
}
len = len1 + len2;
log_debug("Create pv online: %s %d:%d %s", path, major, minor, dev_name(dev));
fd = open(path, O_CREAT | O_EXCL | O_RDWR, S_IRUSR | S_IWUSR);
if (fd < 0) {
if (errno == EEXIST)
goto check_duplicate;
log_error_pvscan(cmd, "Failed to create online file for %s path %s error %d", dev_name(dev), path, errno);
return 0;
}
while (len > 0) {
rv = write(fd, buf, len);
if (rv < 0) {
/* file exists so it still works in part */
log_warn("Cannot write online file for %s to %s error %d",
dev_name(dev), path, errno);
if (close(fd))
log_sys_debug("close", path);
return 1;
}
len -= rv;
}
/* We don't care about syncing, these files are not even persistent. */
if (close(fd))
log_sys_debug("close", path);
return 1;
check_duplicate:
/*
* If a PVID online file already exists for this PVID, check if the
* file contains a different device number, and if so we may have a
* duplicate PV.
*
* FIXME: disable autoactivation of the VG somehow?
* The VG may or may not already be activated when a dupicate appears.
* Perhaps write a new field in the pv online or vg online file?
*/
memset(file_vgname, 0, sizeof(file_vgname));
_online_pvid_file_read(path, &file_major, &file_minor, file_vgname);
if ((file_major == major) && (file_minor == minor)) {
log_debug("Existing online file for %d:%d", major, minor);
return 1;
}
/* Don't know how vgname might not match, but it's not good so fail. */
if ((file_major != major) || (file_minor != minor))
log_error_pvscan(cmd, "PV %s is duplicate for PVID %s on %d:%d and %d:%d.",
dev_name(dev), dev->pvid, major, minor, file_major, file_minor);
if (file_vgname[0] && vgname && strcmp(file_vgname, vgname))
log_error_pvscan(cmd, "PV %s has unexpected VG %s vs %s.",
dev_name(dev), vgname, file_vgname);
return 0;
}
static int _online_pvid_file_exists(const char *pvid)
{
char path[PATH_MAX] = { 0 };
struct stat buf;
int rv;
if (dm_snprintf(path, sizeof(path), "%s/%s", _pvs_online_dir, pvid) < 0) {
log_debug(INTERNAL_ERROR "Path %s/%s is too long.", _pvs_online_dir, pvid);
return 0;
}
log_debug("Check pv online: %s", path);
rv = stat(path, &buf);
if (!rv) {
log_debug("Check pv online %s: yes", pvid);
return 1;
}
log_debug("Check pv online %s: no", pvid);
return 0;
}
static int _write_lookup_file(struct cmd_context *cmd, struct volume_group *vg)
{
char path[PATH_MAX];
@@ -496,8 +269,8 @@ static int _write_lookup_file(struct cmd_context *cmd, struct volume_group *vg)
struct pv_list *pvl;
int fd;
if (dm_snprintf(path, sizeof(path), "%s/%s", _pvs_lookup_dir, vg->name) < 0) {
log_error_pvscan(cmd, "Path %s/%s is too long.", _pvs_lookup_dir, vg->name);
if (dm_snprintf(path, sizeof(path), "%s/%s", PVS_LOOKUP_DIR, vg->name) < 0) {
log_error_pvscan(cmd, "Path %s/%s is too long.", PVS_LOOKUP_DIR, vg->name);
return 0;
}
@@ -516,7 +289,7 @@ static int _write_lookup_file(struct cmd_context *cmd, struct volume_group *vg)
line[ID_LEN+1] = '\0';
if (write(fd, &line, ID_LEN+1) < 0)
log_sys_debug("write", path);
log_error_pvscan(cmd, "Failed to write lookup entry %s %s", path, line);
}
if (close(fd))
@@ -553,7 +326,7 @@ static void _lookup_file_count_pvid_files(FILE *fp, const char *vgname, int *pvs
continue;
}
if (_online_pvid_file_exists((const char *)pvid))
if (online_pvid_file_exists((const char *)pvid))
(*pvs_online)++;
else
(*pvs_offline)++;
@@ -614,8 +387,8 @@ static int _count_pvid_files_from_lookup_file(struct cmd_context *cmd, struct de
*pvs_online = 0;
*pvs_offline = 0;
if (!(dir = opendir(_pvs_lookup_dir))) {
log_sys_debug("opendir", _pvs_lookup_dir);
if (!(dir = opendir(PVS_LOOKUP_DIR))) {
log_sys_debug("opendir", PVS_LOOKUP_DIR);
return 0;
}
@@ -627,8 +400,8 @@ static int _count_pvid_files_from_lookup_file(struct cmd_context *cmd, struct de
if (de->d_name[0] == '.')
continue;
if (dm_snprintf(path, sizeof(path), "%s/%s", _pvs_lookup_dir, de->d_name) < 0) {
log_warn("WARNING: Path %s/%s is too long.", _pvs_lookup_dir, de->d_name);
if (dm_snprintf(path, sizeof(path), "%s/%s", PVS_LOOKUP_DIR, de->d_name) < 0) {
log_warn("WARNING: Path %s/%s is too long.", PVS_LOOKUP_DIR, de->d_name);
continue;
}
@@ -653,68 +426,13 @@ static int _count_pvid_files_from_lookup_file(struct cmd_context *cmd, struct de
log_sys_debug("fclose", path);
}
if (closedir(dir))
log_sys_debug("closedir", _pvs_lookup_dir);
log_sys_debug("closedir", PVS_LOOKUP_DIR);
*vgname_out = vgname;
return (vgname) ? 1 : 0;
}
static void _online_dir_setup(struct cmd_context *cmd)
{
struct stat st;
int rv;
if (!stat(DEFAULT_RUN_DIR, &st))
goto do_pvs;
log_debug("Creating run_dir.");
dm_prepare_selinux_context(DEFAULT_RUN_DIR, S_IFDIR);
rv = mkdir(DEFAULT_RUN_DIR, 0755);
dm_prepare_selinux_context(NULL, 0);
if ((rv < 0) && stat(DEFAULT_RUN_DIR, &st))
log_error_pvscan(cmd, "Failed to create %s %d", DEFAULT_RUN_DIR, errno);
do_pvs:
if (!stat(_pvs_online_dir, &st))
goto do_vgs;
log_debug("Creating pvs_online_dir.");
dm_prepare_selinux_context(_pvs_online_dir, S_IFDIR);
rv = mkdir(_pvs_online_dir, 0755);
dm_prepare_selinux_context(NULL, 0);
if ((rv < 0) && stat(_pvs_online_dir, &st))
log_error_pvscan(cmd, "Failed to create %s %d", _pvs_online_dir, errno);
do_vgs:
if (!stat(_vgs_online_dir, &st))
goto do_lookup;
log_debug("Creating vgs_online_dir.");
dm_prepare_selinux_context(_vgs_online_dir, S_IFDIR);
rv = mkdir(_vgs_online_dir, 0755);
dm_prepare_selinux_context(NULL, 0);
if ((rv < 0) && stat(_vgs_online_dir, &st))
log_error_pvscan(cmd, "Failed to create %s %d", _vgs_online_dir, errno);
do_lookup:
if (!stat(_pvs_lookup_dir, &st))
return;
log_debug("Creating pvs_lookup_dir.");
dm_prepare_selinux_context(_pvs_lookup_dir, S_IFDIR);
rv = mkdir(_pvs_lookup_dir, 0755);
dm_prepare_selinux_context(NULL, 0);
if ((rv < 0) && stat(_pvs_lookup_dir, &st))
log_error_pvscan(cmd, "Failed to create %s %d", _pvs_lookup_dir, errno);
}
static void _count_pvid_files(struct volume_group *vg, int *pvs_online, int *pvs_offline)
{
char pvid[ID_LEN + 1] __attribute__((aligned(8))) = { 0 };
@@ -725,7 +443,7 @@ static void _count_pvid_files(struct volume_group *vg, int *pvs_online, int *pvs
dm_list_iterate_items(pvl, &vg->pvs) {
memcpy(pvid, &pvl->pv->id.uuid, ID_LEN);
if (_online_pvid_file_exists(pvid))
if (online_pvid_file_exists(pvid))
(*pvs_online)++;
else
(*pvs_offline)++;
@@ -748,7 +466,7 @@ static int _pvscan_aa_single(struct cmd_context *cmd, const char *vg_name,
log_debug("pvscan autoactivating VG %s.", vg_name);
if (!vgchange_activate(cmd, vg, CHANGE_AAY)) {
if (!vgchange_activate(cmd, vg, CHANGE_AAY, 1)) {
log_error_pvscan(cmd, "%s: autoactivation failed.", vg->name);
pp->activate_errors++;
}
@@ -756,32 +474,6 @@ static int _pvscan_aa_single(struct cmd_context *cmd, const char *vg_name,
return ECMD_PROCESSED;
}
static int _online_vg_file_create(struct cmd_context *cmd, const char *vgname)
{
char path[PATH_MAX];
int fd;
if (dm_snprintf(path, sizeof(path), "%s/%s", _vgs_online_dir, vgname) < 0) {
log_error_pvscan(cmd, "Path %s/%s is too long.", _vgs_online_dir, vgname);
return 0;
}
log_debug("Create vg online: %s", path);
fd = open(path, O_CREAT | O_EXCL | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR);
if (fd < 0) {
log_debug("Failed to create %s: %d", path, errno);
return 0;
}
/* We don't care about syncing, these files are not even persistent. */
if (close(fd))
log_sys_debug("close", path);
return 1;
}
/*
* This is a very unconventional way of doing things because
* we want to figure out which devices to read the VG from
@@ -817,6 +509,7 @@ static int _get_devs_from_saved_vg(struct cmd_context *cmd, const char *vgname,
{
char path[PATH_MAX];
char file_vgname[NAME_LEN];
char file_devname[NAME_LEN];
char pvid[ID_LEN + 1] __attribute__((aligned(8))) = { 0 };
char uuidstr[64] __attribute__((aligned(8)));
struct pv_list *pvl;
@@ -841,13 +534,14 @@ static int _get_devs_from_saved_vg(struct cmd_context *cmd, const char *vgname,
memcpy(pvid, &pvl->pv->id.uuid, ID_LEN);
memset(path, 0, sizeof(path));
snprintf(path, sizeof(path), "%s/%s", _pvs_online_dir, pvid);
snprintf(path, sizeof(path), "%s/%s", PVS_ONLINE_DIR, pvid);
file_major = 0;
file_minor = 0;
memset(file_vgname, 0, sizeof(file_vgname));
memset(file_devname, 0, sizeof(file_devname));
_online_pvid_file_read(path, &file_major, &file_minor, file_vgname);
online_pvid_file_read(path, &file_major, &file_minor, file_vgname, file_devname);
if (file_vgname[0] && strcmp(vgname, file_vgname)) {
log_error_pvscan(cmd, "Wrong VG found for %d:%d PVID %s: %s vs %s",
@@ -857,13 +551,8 @@ static int _get_devs_from_saved_vg(struct cmd_context *cmd, const char *vgname,
devno = MKDEV(file_major, file_minor);
if (!setup_devno_in_dev_cache(cmd, devno)) {
log_error_pvscan(cmd, "No device set up for %d:%d PVID %s", file_major, file_minor, pvid);
goto bad;
}
if (!(dev = dev_cache_get_by_devt(cmd, devno, NULL, NULL))) {
log_error_pvscan(cmd, "No device found for %d:%d PVID %s", file_major, file_minor, pvid);
if (!(dev = setup_dev_in_dev_cache(cmd, devno, file_devname[0] ? file_devname : NULL))) {
log_error_pvscan(cmd, "No device set up for online PV %d:%d %s PVID %s", file_major, file_minor, file_devname, pvid);
goto bad;
}
@@ -875,7 +564,8 @@ static int _get_devs_from_saved_vg(struct cmd_context *cmd, const char *vgname,
name1 = dev_name(dev);
name2 = pvl->pv->device_hint;
if (strcmp(name1, name2)) {
/* Probably pointless since dev is from online file which was already checked. */
if (!strncmp(name2, "/dev/md", 7) && strncmp(name1, "/dev/md", 7)) {
if (!id_write_format((const struct id *)pvid, uuidstr, sizeof(uuidstr)))
uuidstr[0] = '\0';
log_print_pvscan(cmd, "PVID %s read from %s last written to %s.", uuidstr, name1, name2);
@@ -1036,7 +726,7 @@ static int _pvscan_aa_quick(struct cmd_context *cmd, struct pvscan_aa_params *pp
log_debug("pvscan autoactivating VG %s.", vgname);
if (!vgchange_activate(cmd, vg, CHANGE_AAY)) {
if (!vgchange_activate(cmd, vg, CHANGE_AAY, 1)) {
log_error_pvscan(cmd, "%s: autoactivation failed.", vg->name);
pp->activate_errors++;
}
@@ -1070,7 +760,7 @@ static int _pvscan_aa(struct cmd_context *cmd, struct pvscan_aa_params *pp,
* to run the activation. The first to create the file will do it.
*/
dm_list_iterate_items_safe(sl, sl2, vgnames) {
if (!_online_vg_file_create(cmd, sl->str)) {
if (!online_vg_file_create(cmd, sl->str)) {
log_print_pvscan(cmd, "VG %s skip autoactivation.", sl->str);
str_list_del(vgnames, sl->str);
continue;
@@ -1127,6 +817,49 @@ out:
return ret;
}
/*
* The optimization in which only the pvscan arg devname is added to dev-cache
* does not work if there's an lvm.conf filter containing symlinks to the dev
* like /dev/disk/by-id/lvm-pv-uuid-xyz entries. A full dev_cache_scan will
* associate the symlinks with the system dev name passed to pvscan, which lets
* filter-regex match the devname with the symlink name in the filter.
*/
static int _filter_uses_symlinks(struct cmd_context *cmd, int filter_cfg)
{
const struct dm_config_node *cn;
const struct dm_config_value *cv;
const char *fname;
if ((cn = find_config_tree_array(cmd, filter_cfg, NULL))) {
for (cv = cn->v; cv; cv = cv->next) {
if (cv->type != DM_CFG_STRING)
continue;
if (!cv->v.str)
continue;
fname = cv->v.str;
if (fname[0] != 'a')
continue;
if (strstr(fname, "/dev/disk/"))
return 1;
if (strstr(fname, "/dev/mapper/"))
return 1;
/* In case /dev/disk/by was omitted */
if (strstr(fname, "lvm-pv-uuid"))
return 1;
if (strstr(fname, "dm-uuid"))
return 1;
if (strstr(fname, "wwn-"))
return 1;
}
}
return 0;
}
struct pvscan_arg {
struct dm_list list;
const char *devname;
@@ -1191,21 +924,41 @@ static int _get_args_devs(struct cmd_context *cmd, struct dm_list *pvscan_args,
struct pvscan_arg *arg;
struct device_list *devl;
/*
* If no devices file is used, and lvm.conf filter is set to
* accept /dev/disk/by-id/lvm-pv-uuid-xyz or another symlink,
* but pvscan --cache is passed devname or major:minor, so
* pvscan needs to match its arg device to the filter symlink.
* setup_dev_in_dev_cache() adds /dev/sda2 to dev-cache which
* does not match a symlink to /dev/sda2, so we need a full
* dev_cache_scan that will associate all symlinks to sda2,
* which allows filter-regex to work. This case could be
* optimized if needed by adding dev-cache entries for each
* filter "a" entry (filter symlink patterns would still need
* a full dev_cache_scan.)
* (When no devices file is used and 69-dm-lvm.rules is
* used which calls pvscan directly, symlinks may not
* have been created by other rules when pvscan runs, so
* the full dev_cache_scan may still not find them.)
*/
if (!cmd->enable_devices_file && !cmd->enable_devices_list &&
(_filter_uses_symlinks(cmd, devices_filter_CFG) ||
_filter_uses_symlinks(cmd, devices_global_filter_CFG))) {
log_print_pvscan(cmd, "finding all devices for filter symlinks.");
dev_cache_scan(cmd);
}
/* pass NULL filter when getting devs from dev-cache, filtering is done separately */
/* in common usage, no dev will be found for a devno */
dm_list_iterate_items(arg, pvscan_args) {
if (arg->devname) {
if (!setup_devname_in_dev_cache(cmd, arg->devname))
log_error_pvscan(cmd, "No device set up for name arg %s", arg->devname);
arg->dev = dev_cache_get(cmd, arg->devname, NULL);
} else if (arg->devno) {
if (!setup_devno_in_dev_cache(cmd, arg->devno))
log_error_pvscan(cmd, "No device set up for devno arg %d", (int)arg->devno);
arg->dev = dev_cache_get_by_devt(cmd, arg->devno, NULL, NULL);
} else
if (!arg->devname && !arg->devno)
return_0;
if (!(arg->dev = setup_dev_in_dev_cache(cmd, arg->devno, arg->devname))) {
log_error_pvscan(cmd, "No device set up for arg %s %d:%d",
arg->devname ?: "", (int)MAJOR(arg->devno), (int)MINOR(arg->devno));
}
}
dm_list_iterate_items(arg, pvscan_args) {
@@ -1225,6 +978,7 @@ static void _set_pv_devices_online(struct cmd_context *cmd, struct volume_group
{
char path[PATH_MAX];
char file_vgname[NAME_LEN];
char file_devname[NAME_LEN];
char pvid[ID_LEN+1] = { 0 };
struct pv_list *pvl;
struct device *dev;
@@ -1240,7 +994,7 @@ static void _set_pv_devices_online(struct cmd_context *cmd, struct volume_group
continue;
}
if (!_online_pvid_file_exists(pvid)) {
if (!online_pvid_file_exists(pvid)) {
log_debug("set_pv_devices_online vg %s pv %s no online file",
vg->name, pvid);
pvl->pv->status |= MISSING_PV;
@@ -1248,13 +1002,14 @@ static void _set_pv_devices_online(struct cmd_context *cmd, struct volume_group
}
memset(path, 0, sizeof(path));
snprintf(path, sizeof(path), "%s/%s", _pvs_online_dir, pvid);
snprintf(path, sizeof(path), "%s/%s", PVS_ONLINE_DIR, pvid);
major = 0;
minor = 0;
file_vgname[0] = '\0';
memset(file_vgname, 0, sizeof(file_vgname));
memset(file_devname, 0, sizeof(file_devname));
_online_pvid_file_read(path, &major, &minor, file_vgname);
online_pvid_file_read(path, &major, &minor, file_vgname, file_devname);
if (file_vgname[0] && strcmp(vg->name, file_vgname)) {
log_warn("WARNING: VG %s PV %s wrong vgname in online file %s",
@@ -1265,9 +1020,9 @@ static void _set_pv_devices_online(struct cmd_context *cmd, struct volume_group
devno = MKDEV(major, minor);
if (!(dev = dev_cache_get_by_devt(cmd, devno, NULL, NULL))) {
log_print_pvscan(cmd, "VG %s PV %s no device found for %d:%d",
vg->name, pvid, major, minor);
if (!(dev = setup_dev_in_dev_cache(cmd, devno, file_devname[0] ? file_devname : NULL))) {
log_print_pvscan(cmd, "VG %s PV %s no device found for online PV %d:%d %s",
vg->name, pvid, major, minor, file_devname);
pvl->pv->status |= MISSING_PV;
continue;
}
@@ -1369,7 +1124,7 @@ static int _online_devs(struct cmd_context *cmd, int do_all, struct dm_list *pvs
do_full_check = 0;
/* If use_full_md_check is set then this has already been done by filter. */
if (!cmd->use_full_md_check) {
if (!cmd->use_full_md_check && (cmd->dev_types->md_major != MAJOR(dev->dev))) {
if (devsize && (pv->size != devsize))
do_full_check = 1;
if (pv->device_hint && !strncmp(pv->device_hint, "/dev/md", 7))
@@ -1420,7 +1175,7 @@ static int _online_devs(struct cmd_context *cmd, int do_all, struct dm_list *pvs
* Create file named for pvid to record this PV is online.
* The command creates/checks online files only when --cache is used.
*/
if (do_cache && !_online_pvid_file_create(cmd, dev, vg ? vg->name : NULL)) {
if (do_cache && !online_pvid_file_create(cmd, dev, vg ? vg->name : NULL)) {
log_error_pvscan(cmd, "PV %s failed to create online file.", dev_name(dev));
release_vg(vg);
ret = 0;
@@ -1450,22 +1205,39 @@ static int _online_devs(struct cmd_context *cmd, int do_all, struct dm_list *pvs
if (vg) {
/*
* Use the VG metadata from this PV for a list of all
* PVIDs. Write a lookup file of PVIDs in case another
* pvscan needs it. After writing lookup file, recheck
* pvid files to resolve a possible race with another
* pvscan reading the lookup file that missed it.
* Check if the VG is complete by checking that
* pvs_online/<pvid> files exist for all vg->pvs.
*/
log_debug("checking all pvid files from vg %s", vg->name);
_count_pvid_files(vg, &pvs_online, &pvs_offline);
if (pvs_offline && _write_lookup_file(cmd, vg)) {
log_debug("rechecking all pvid files from vg %s", vg->name);
_count_pvid_files(vg, &pvs_online, &pvs_offline);
if (!pvs_offline)
log_print_pvscan(cmd, "VG %s complete after recheck.", vg->name);
/*
* When there is more than one PV in the VG, write
* /run/lvm/pvs_lookup/<vgname> with a list of PVIDs in
* the VG. This is used in case a later PV comes
* online that has no metadata, in which case pvscan
* for that PV needs to use the lookup file to check if
* the VG is complete. The lookup file is also used by
* vgchange -aay --autoactivation event <vgname>
* to check if all pvs_online files for the VG exist.
*
* For multiple concurrent pvscan's, they will race to
* create the lookup file and the first will succeed.
*
* After writing the lookup file, recheck pvid files to
* resolve a possible race with another pvscan reading
* the lookup file that missed it.
*/
if (dm_list_size(&vg->pvs) > 1) {
if (_write_lookup_file(cmd, vg)) {
if (pvs_offline) {
log_debug("rechecking all pvid files from vg %s", vg->name);
_count_pvid_files(vg, &pvs_online, &pvs_offline);
if (!pvs_offline)
log_print_pvscan(cmd, "VG %s complete after recheck.", vg->name);
}
}
}
vgname = vg->name;
} else {
/*
@@ -1505,7 +1277,7 @@ static int _online_devs(struct cmd_context *cmd, int do_all, struct dm_list *pvs
} else if (!do_check_complete) {
log_print("VG %s", vgname);
} else if (vg_complete) {
if (do_vgonline && !_online_vg_file_create(cmd, vgname)) {
if (do_vgonline && !online_vg_file_create(cmd, vgname)) {
log_print("VG %s finished", vgname);
} else {
/*
@@ -1611,9 +1383,9 @@ static int _pvscan_cache_all(struct cmd_context *cmd, int argc, char **argv,
dm_list_init(&pvscan_devs);
_online_files_remove(_pvs_online_dir);
_online_files_remove(_vgs_online_dir);
_online_files_remove(_pvs_lookup_dir);
_online_files_remove(PVS_ONLINE_DIR);
_online_files_remove(VGS_ONLINE_DIR);
_online_files_remove(PVS_LOOKUP_DIR);
unlink_searched_devnames(cmd);
@@ -1677,7 +1449,7 @@ static int _pvscan_cache_args(struct cmd_context *cmd, int argc, char **argv,
dm_list_init(&pvscan_args);
dm_list_init(&pvscan_devs);
cmd->pvscan_cache_single = 1;
cmd->expect_missing_vg_device = 1;
/*
* Special pvscan-specific setup steps to avoid looking
@@ -1686,7 +1458,7 @@ static int _pvscan_cache_args(struct cmd_context *cmd, int argc, char **argv,
* Does not do dev_cache_scan (adds nothing to dev-cache), and
* does not do any device id matching.
*/
if (!setup_devices_for_pvscan_cache(cmd)) {
if (!setup_devices_for_online_autoactivation(cmd)) {
log_error_pvscan(cmd, "Failed to set up devices.");
return 0;
}
@@ -1867,12 +1639,35 @@ static int _pvscan_cache_args(struct cmd_context *cmd, int argc, char **argv,
return ret;
}
static int _get_autoactivation(struct cmd_context *cmd, int event_activation, int *skip_command)
{
const char *aa_str;
if (!(aa_str = arg_str_value(cmd, autoactivation_ARG, NULL)))
return 1;
if (strcmp(aa_str, "event")) {
log_print_pvscan(cmd, "Skip pvscan for unknown autoactivation value.");
*skip_command = 1;
return 1;
}
if (!event_activation) {
log_print_pvscan(cmd, "Skip pvscan for event with event_activation=0.");
*skip_command = 1;
return 1;
}
return 1;
}
int pvscan_cache_cmd(struct cmd_context *cmd, int argc, char **argv)
{
struct pvscan_aa_params pp = { 0 };
struct dm_list complete_vgnames;
int do_activate = arg_is_set(cmd, activate_ARG);
int event_activation;
int skip_command = 0;
int devno_args = 0;
int do_all;
int ret;
@@ -1933,7 +1728,7 @@ int pvscan_cache_cmd(struct cmd_context *cmd, int argc, char **argv)
do_all = !argc && !devno_args;
_online_dir_setup(cmd);
online_dir_setup(cmd);
if (do_all) {
if (!_pvscan_cache_all(cmd, argc, argv, &complete_vgnames))
@@ -1944,6 +1739,13 @@ int pvscan_cache_cmd(struct cmd_context *cmd, int argc, char **argv)
log_verbose("Ignoring pvscan --cache because event_activation is disabled.");
return ECMD_PROCESSED;
}
if (!_get_autoactivation(cmd, event_activation, &skip_command))
return_ECMD_FAILED;
if (skip_command)
return ECMD_PROCESSED;
if (!_pvscan_cache_args(cmd, argc, argv, &complete_vgnames))
return ECMD_FAILED;
}

View File

@@ -17,6 +17,7 @@
#include "lib/format_text/format-text.h"
#include "lib/label/hints.h"
#include "lib/device/device_id.h"
#include "lib/device/online.h"
#include <sys/stat.h>
#include <signal.h>
@@ -828,8 +829,11 @@ int lv_change_activate(struct cmd_context *cmd, struct logical_volume *lv,
* user may want to take charge of activation changes to the VG
* and not have the system autoactivation interfere.
*/
if (!is_change_activating(activate) && cmd->event_activation)
if (!is_change_activating(activate) && cmd->event_activation &&
!cmd->online_vg_file_removed) {
cmd->online_vg_file_removed = 1;
online_vg_file_remove(lv->vg->name);
}
set_lv_notify(lv->vg->cmd);
@@ -2982,6 +2986,8 @@ int process_each_lv_in_vg(struct cmd_context *cmd, struct volume_group *vg,
struct glv_list *glvl, *tglvl;
int do_report_ret_code = 1;
cmd->online_vg_file_removed = 0;
log_set_report_object_type(LOG_REPORT_OBJECT_TYPE_LV);
vg_uuid[0] = '\0';
@@ -3116,8 +3122,7 @@ int process_each_lv_in_vg(struct cmd_context *cmd, struct volume_group *vg,
* in bcache, and needs to be closed so the open fd doesn't
* interfere with processing the LV.
*/
dm_list_iterate_items(lvl, &final_lvs)
label_scan_invalidate_lv(cmd, lvl->lv);
label_scan_invalidate_lvs(cmd, &final_lvs);
dm_list_iterate_items(lvl, &final_lvs) {
lv_uuid[0] = '\0';

View File

@@ -228,7 +228,7 @@ int mirror_remove_missing(struct cmd_context *cmd,
int vgchange_activate(struct cmd_context *cmd, struct volume_group *vg,
activation_change_t activate);
activation_change_t activate, int vg_complete_to_activate);
int vgchange_background_polling(struct cmd_context *cmd, struct volume_group *vg);
@@ -295,6 +295,4 @@ int lvconvert_cachevol_attach_single(struct cmd_context *cmd,
struct logical_volume *lv,
struct processing_handle *handle);
void online_vg_file_remove(const char *vgname);
#endif

View File

@@ -15,10 +15,12 @@
#include "tools.h"
#include "lib/device/device_id.h"
#include "lib/label/hints.h"
struct vgchange_params {
int lock_start_count;
unsigned int lock_start_sanlock : 1;
unsigned int vg_complete_to_activate : 1;
};
/*
@@ -195,10 +197,11 @@ int vgchange_background_polling(struct cmd_context *cmd, struct volume_group *vg
}
int vgchange_activate(struct cmd_context *cmd, struct volume_group *vg,
activation_change_t activate)
activation_change_t activate, int vg_complete_to_activate)
{
int lv_open, active, monitored = 0, r = 1;
const struct lv_list *lvl;
struct pv_list *pvl;
int do_activate = is_change_activating(activate);
/*
@@ -219,6 +222,15 @@ int vgchange_activate(struct cmd_context *cmd, struct volume_group *vg,
return 1;
}
if (do_activate && vg_complete_to_activate) {
dm_list_iterate_items(pvl, &vg->pvs) {
if (!pvl->pv->dev) {
log_print("VG %s is incomplete.", vg->name);
return 1;
}
}
}
/*
* Safe, since we never write out new metadata here. Required for
* partial activation to work.
@@ -647,6 +659,7 @@ static int _vgchange_single(struct cmd_context *cmd, const char *vg_name,
struct volume_group *vg,
struct processing_handle *handle)
{
struct vgchange_params *vp = (struct vgchange_params *)handle->custom_handle;
int ret = ECMD_PROCESSED;
unsigned i;
activation_change_t activate;
@@ -701,7 +714,7 @@ static int _vgchange_single(struct cmd_context *cmd, const char *vg_name,
if (arg_is_set(cmd, activate_ARG)) {
activate = (activation_change_t) arg_uint_value(cmd, activate_ARG, 0);
if (!vgchange_activate(cmd, vg, activate))
if (!vgchange_activate(cmd, vg, activate, vp->vg_complete_to_activate))
return_ECMD_FAILED;
} else if (arg_is_set(cmd, refresh_ARG)) {
/* refreshes the visible LVs (which starts polling) */
@@ -722,9 +735,143 @@ static int _vgchange_single(struct cmd_context *cmd, const char *vg_name,
return ret;
}
static int _vgchange_autoactivation_setup(struct cmd_context *cmd,
struct vgchange_params *vp,
int *skip_command,
const char **vgname_ret,
uint32_t *flags)
{
const char *aa;
char *vgname = NULL;
int vg_locked = 0;
int found_none = 0, found_all = 0, found_incomplete = 0;
if (!(aa = arg_str_value(cmd, autoactivation_ARG, NULL)))
return_0;
if (strcmp(aa, "event")) {
log_print("Skip vgchange for unknown autoactivation value.");
*skip_command = 1;
return 1;
}
if (!find_config_tree_bool(cmd, global_event_activation_CFG, NULL)) {
log_print("Skip vgchange for event and event_activation=0.");
*skip_command = 1;
return 1;
}
vp->vg_complete_to_activate = 1;
cmd->use_hints = 0;
/*
* Add an option to skip the pvs_online optimization? e.g.
* "online_skip" in --autoactivation / auto_activation_settings
*
* if (online_skip)
* return 1;
*/
/* reads devices file, does not populate dev-cache */
if (!setup_devices_for_online_autoactivation(cmd))
return_0;
get_single_vgname_cmd_arg(cmd, NULL, &vgname);
/*
* Lock the VG before scanning the PVs so _vg_read can avoid the normal
* lock_vol+rescan (READ_WITHOUT_LOCK avoids the normal lock_vol and
* can_use_one_scan avoids the normal rescan.) If this early lock_vol
* fails, continue and use the normal lock_vol in _vg_read.
*/
if (vgname) {
if (!lock_vol(cmd, vgname, LCK_VG_WRITE, NULL)) {
log_debug("Failed early VG locking for autoactivation.");
} else {
*flags |= READ_WITHOUT_LOCK;
cmd->can_use_one_scan = 1;
vg_locked = 1;
}
}
/*
* Perform label_scan on PVs that are online (per /run/lvm files)
* for the given VG (or when no VG name is given, all online PVs.)
* If this fails, the caller will do a normal process_each_vg without
* optimizations (which will do a full label_scan.)
*/
if (!label_scan_vg_online(cmd, vgname, &found_none, &found_all, &found_incomplete)) {
log_print("PVs online error%s%s, using all devices.", vgname ? " for VG " : "", vgname ?: "");
goto bad;
}
/*
* Not the expected usage, activate any VGs that are complete based on
* pvs_online. Only online pvs are used.
*/
if (!vgname) {
*flags |= PROCESS_SKIP_SCAN;
return 1;
}
/*
* The expected and optimal usage, which is the purpose of
* this function. We expect online files to be found for
* all PVs because the udev rule calls
* vgchange -aay --autoactivation event <vgname>
* only after all PVs for vgname are found online.
*/
if (found_all) {
*flags |= PROCESS_SKIP_SCAN;
*vgname_ret = vgname;
return 1;
}
/*
* Not expected usage, no online pvs for the vgname were found. The
* caller will fall back to process_each doing a full label_scan to
* look for the VG. (No optimization used.)
*/
if (found_none) {
log_print("PVs online not found for VG %s, using all devices.", vgname);
goto bad;
}
/*
* Not expected usage, only some online pvs for the vgname were found.
* The caller will fall back to process_each doing a full label_scan to
* look for all PVs in the VG. (No optimization used.)
*/
if (found_incomplete) {
log_print("PVs online incomplete for VG %s, using all devicess.", vgname);
goto bad;
}
/*
* Shouldn't happen, the caller will fall back to standard
* process_each. (No optimization used.)
*/
log_print("PVs online unknown for VG %s, using all devices.", vgname);
bad:
/*
* The online scanning optimization didn't work, so undo the vg
* locking optimization before falling back to normal processing.
*/
if (vg_locked) {
unlock_vg(cmd, NULL, vgname);
*flags &= ~READ_WITHOUT_LOCK;
cmd->can_use_one_scan = 0;
}
return 1;
}
int vgchange(struct cmd_context *cmd, int argc, char **argv)
{
struct vgchange_params vp = { 0 };
struct processing_handle *handle;
const char *vgname = NULL;
uint32_t flags = 0;
int ret;
@@ -837,6 +984,14 @@ int vgchange(struct cmd_context *cmd, int argc, char **argv)
cmd->lockd_vg_enforce_sh = 1;
}
if (arg_is_set(cmd, autoactivation_ARG)) {
int skip_command = 0;
if (!_vgchange_autoactivation_setup(cmd, &vp, &skip_command, &vgname, &flags))
return ECMD_FAILED;
if (skip_command)
return ECMD_PROCESSED;
}
if (update)
flags |= READ_FOR_UPDATE;
else if (arg_is_set(cmd, activate_ARG))
@@ -847,7 +1002,9 @@ int vgchange(struct cmd_context *cmd, int argc, char **argv)
return ECMD_FAILED;
}
ret = process_each_vg(cmd, argc, argv, NULL, NULL, flags, 0, handle, &_vgchange_single);
handle->custom_handle = &vp;
ret = process_each_vg(cmd, argc, argv, vgname, NULL, flags, 0, handle, &_vgchange_single);
destroy_processing_handle(cmd, handle);
return ret;

View File

@@ -727,30 +727,6 @@ int vgsplit(struct cmd_context *cmd, int argc, char **argv)
backup(vg_from);
}
/*
* Finally, remove the EXPORTED flag from the new VG and write it out.
* We need to unlock vg_to because vg_read_for_update wants to lock it.
*/
if (!test_mode()) {
unlock_vg(cmd, NULL, vg_name_to);
release_vg(vg_to);
/*
* This command uses the exported vg flag internally, but
* exported VGs are not allowed to be split from the command
* level, so ALLOW_EXPORTED is not set in commands.h.
*/
cmd->include_exported_vgs = 1;
vg_to = vg_read_for_update(cmd, vg_name_to, NULL, 0, 0);
if (!vg_to) {
log_error("Volume group \"%s\" became inconsistent: "
"please fix manually", vg_name_to);
goto bad;
}
}
vg_to->status &= ~EXPORTED_VG;
if (!vg_write(vg_to) || !vg_commit(vg_to))

View File

@@ -121,6 +121,6 @@ LABEL="direct_pvscan"
# MD | | X | X* | |
# loop | | X | X* | |
# other | X | | X | | X
RUN+="(LVM_EXEC)/lvm pvscan --background --cache --activate ay --major $major --minor $minor", ENV{LVM_SCANNED}="1"
RUN+="(LVM_EXEC)/lvm pvscan --cache --aay --autoactivation event --major $major --minor $minor", ENV{LVM_SCANNED}="1"
LABEL="lvm_end"

View File

@@ -79,8 +79,8 @@ ENV{SYSTEMD_READY}="1"
# TODO: adjust the output of vgchange -aay so that
# it's better suited to appearing in the journal.
IMPORT{program}="(LVM_EXEC)/lvm pvscan --cache --listvg --checkcomplete --vgonline --udevoutput --journal=output $env{DEVNAME}"
ENV{LVM_VG_NAME_COMPLETE}=="?*", RUN+="/usr/bin/systemd-run -r --no-block --property DefaultDependencies=no --unit lvm-activate-$env{LVM_VG_NAME_COMPLETE} (LVM_EXEC)/lvm vgchange -aay --nohints $env{LVM_VG_NAME_COMPLETE}"
IMPORT{program}="(LVM_EXEC)/lvm pvscan --cache --listvg --checkcomplete --vgonline --autoactivation event --udevoutput --journal=output $env{DEVNAME}"
ENV{LVM_VG_NAME_COMPLETE}=="?*", RUN+="/usr/bin/systemd-run --no-block --property DefaultDependencies=no --unit lvm-activate-$env{LVM_VG_NAME_COMPLETE} (LVM_EXEC)/lvm vgchange -aay --autoactivation event $env{LVM_VG_NAME_COMPLETE}"
GOTO="lvm_end"
LABEL="lvm_end"