mirror of
git://sourceware.org/git/lvm2.git
synced 2025-09-27 05:44:18 +03:00
Compare commits
52 Commits
dev-dct-pv
...
dev-rhel90
Author | SHA1 | Date | |
---|---|---|---|
|
611c3f8686 | ||
|
4a1f6173d2 | ||
|
61706793de | ||
|
84503342f7 | ||
|
b8c7810617 | ||
|
ba16b6f227 | ||
|
185c7dcbeb | ||
|
522561e64b | ||
|
0f71183f94 | ||
|
fbd8b0cf43 | ||
|
6a431eb242 | ||
|
c28541eccd | ||
|
d803d9202c | ||
|
ae54e75176 | ||
|
455c29b10d | ||
|
c42a05c3ec | ||
|
05d9a01351 | ||
|
d5402e55f6 | ||
|
71e7ebb3e4 | ||
|
36095ac374 | ||
|
470b967bc5 | ||
|
d12baba1a9 | ||
|
009007484b | ||
|
01bf8e1747 | ||
|
114e1cfee5 | ||
|
212b1fc529 | ||
|
b8f4ec846d | ||
|
e4b8726b6d | ||
|
5c71aa7510 | ||
|
5c4ce4669e | ||
|
fee3937002 | ||
|
8e61c0ad6e | ||
|
89c54db16c | ||
|
c5f998aec4 | ||
|
4926061d32 | ||
|
66f0fe57c3 | ||
|
5dbf316cee | ||
|
b945ea1c93 | ||
|
20c550ab10 | ||
|
0e0faf30e0 | ||
|
92e741eda0 | ||
|
d608837b2a | ||
|
73b4602f21 | ||
|
024ce50f06 | ||
|
14b68ea313 | ||
|
b4067e84c7 | ||
|
62533ae3fa | ||
|
5f7cb97743 | ||
|
f40fd88374 | ||
|
d558b3ad7e | ||
|
594f6ca970 | ||
|
726dd25969 |
@@ -1151,16 +1151,11 @@ global {
|
||||
# lvdisplay_shows_full_device_path = 0
|
||||
|
||||
# Configuration option global/event_activation.
|
||||
# Activate LVs based on system-generated device events.
|
||||
# When a PV appears on the system, a system-generated uevent triggers
|
||||
# the lvm2-pvscan service which runs the pvscan --cache -aay command.
|
||||
# If the new PV completes a VG, pvscan autoactivates LVs in the VG.
|
||||
# When event_activation is disabled, the lvm2-activation services are
|
||||
# generated and run at fixed points during system startup. These
|
||||
# services run vgchange -aay to autoactivate LVs in VGs that happen
|
||||
# to be present at that point in time.
|
||||
# See the --setautoactivation option or the auto_activation_volume_list
|
||||
# setting to configure autoactivation for specific VGs or LVs.
|
||||
# Disable event based autoactivation commands.
|
||||
# WARNING: setting this to zero may cause machine startup to fail.
|
||||
# Previously, setting this to zero would enable static autoactivation
|
||||
# services (via the lvm2-activation-generator), but the autoactivation
|
||||
# services and generator have been removed.
|
||||
# This configuration option has an automatic default value.
|
||||
# event_activation = 1
|
||||
|
||||
|
350
lib/cache/lvmcache.c
vendored
350
lib/cache/lvmcache.c
vendored
@@ -572,6 +572,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 +635,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 +832,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 +856,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 +876,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 +1448,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 +1521,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 +1552,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);
|
||||
}
|
||||
}
|
||||
|
2
lib/cache/lvmcache.h
vendored
2
lib/cache/lvmcache.h
vendored
@@ -229,4 +229,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
|
||||
|
@@ -183,7 +183,6 @@ struct cmd_context {
|
||||
unsigned enable_hints:1; /* hints are enabled for cmds in general */
|
||||
unsigned use_hints:1; /* if hints are enabled this cmd can use them */
|
||||
unsigned pvscan_recreate_hints:1; /* enable special case hint handling for pvscan --cache */
|
||||
unsigned hints_pvs_online:1; /* hints="pvs_online" */
|
||||
unsigned scan_lvs:1;
|
||||
unsigned wipe_outdated_pvs:1;
|
||||
unsigned enable_devices_list:1; /* command is using --devices option */
|
||||
|
@@ -1119,16 +1119,11 @@ 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.\n"
|
||||
"WARNING: setting this to zero may cause machine startup to fail.\n"
|
||||
"Previously, setting this to zero would enable static autoactivation\n"
|
||||
"services (via the lvm2-activation-generator), but the autoactivation\n"
|
||||
"services and generator have been removed.\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)
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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"},
|
||||
|
@@ -40,6 +40,7 @@
|
||||
#define DEV_IS_NVME 0x00040000 /* set if dev is nvme */
|
||||
#define DEV_MATCHED_USE_ID 0x00080000 /* matched an entry from cmd->use_devices */
|
||||
#define DEV_SCAN_FOUND_NOLABEL 0x00100000 /* label_scan read, passed filters, but no lvm label */
|
||||
#define DEV_SCAN_NOT_READ 0x00200000 /* label_scan not able to read dev */
|
||||
|
||||
/*
|
||||
* Support for external device info.
|
||||
|
@@ -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 */
|
||||
@@ -779,7 +802,7 @@ static void _device_ids_update_try(struct cmd_context *cmd)
|
||||
int held = 0;
|
||||
|
||||
if (cmd->expect_missing_vg_device) {
|
||||
log_print("skip updating devices file.");
|
||||
log_print("Devices file update skipped.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -886,7 +909,7 @@ struct dev_use *get_du_for_pvid(struct cmd_context *cmd, const char *pvid)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct dev_use *_get_du_for_devname(struct cmd_context *cmd, const char *devname)
|
||||
struct dev_use *get_du_for_devname(struct cmd_context *cmd, const char *devname)
|
||||
{
|
||||
struct dev_use *du;
|
||||
|
||||
@@ -927,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
|
||||
@@ -945,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.
|
||||
*
|
||||
@@ -980,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;
|
||||
}
|
||||
@@ -1064,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.
|
||||
*
|
||||
@@ -1075,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)
|
||||
@@ -1104,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
|
||||
@@ -1146,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.");
|
||||
@@ -1158,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?
|
||||
@@ -1187,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);
|
||||
@@ -1359,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)
|
||||
@@ -1387,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.
|
||||
@@ -1399,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;
|
||||
}
|
||||
@@ -1455,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;
|
||||
@@ -1480,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;
|
||||
}
|
||||
@@ -1492,7 +1529,7 @@ int device_ids_match_dev(struct cmd_context *cmd, struct device *dev)
|
||||
struct dev_use *du;
|
||||
|
||||
/* First check the du entry with matching devname since it's likely correct. */
|
||||
if ((du = _get_du_for_devname(cmd, dev_name(dev)))) {
|
||||
if ((du = get_du_for_devname(cmd, dev_name(dev)))) {
|
||||
if (_match_du_to_dev(cmd, du, dev))
|
||||
return 1;
|
||||
}
|
||||
@@ -1709,6 +1746,13 @@ void device_ids_validate(struct cmd_context *cmd, struct dm_list *scanned_devs,
|
||||
if (scanned_devs && !dev_in_device_list(dev, scanned_devs))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* The matched device could not be read so we do not have
|
||||
* the PVID from disk and cannot verify the devices file entry.
|
||||
*/
|
||||
if (dev->flags & DEV_SCAN_NOT_READ)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* du and dev may have been matched, but the dev could still
|
||||
* have been excluded by other filters during label scan.
|
||||
@@ -1791,6 +1835,13 @@ void device_ids_validate(struct cmd_context *cmd, struct dm_list *scanned_devs,
|
||||
if (scanned_devs && !dev_in_device_list(dev, scanned_devs))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* The matched device could not be read so we do not have
|
||||
* the PVID from disk and cannot verify the devices file entry.
|
||||
*/
|
||||
if (dev->flags & DEV_SCAN_NOT_READ)
|
||||
continue;
|
||||
|
||||
if (!cmd->filter->passes_filter(cmd, cmd->filter, dev, "persistent")) {
|
||||
log_warn("Devices file %s is excluded by filter: %s.",
|
||||
dev_name(dev), dev_filtered_reason(dev));
|
||||
@@ -1906,6 +1957,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? */
|
||||
|
||||
/*
|
||||
@@ -1974,12 +2033,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;
|
||||
|
||||
@@ -2004,6 +2070,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);
|
||||
@@ -2070,6 +2141,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++;
|
||||
@@ -2179,7 +2256,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);
|
||||
|
@@ -41,6 +41,7 @@ void device_id_update_vg_uuid(struct cmd_context *cmd, struct volume_group *vg,
|
||||
|
||||
struct dev_use *get_du_for_dev(struct cmd_context *cmd, struct device *dev);
|
||||
struct dev_use *get_du_for_pvid(struct cmd_context *cmd, const char *pvid);
|
||||
struct dev_use *get_du_for_devname(struct cmd_context *cmd, const char *devname);
|
||||
|
||||
char *devices_file_version(void);
|
||||
int devices_file_exists(struct cmd_context *cmd);
|
||||
@@ -56,4 +57,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
|
||||
|
@@ -21,35 +21,50 @@
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
static char *_vgname_in_pvid_file_buf(char *buf)
|
||||
/*
|
||||
* 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, *n;
|
||||
char *p;
|
||||
int i = 0;
|
||||
|
||||
if (!(p = strstr(buf, field)))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* file contains:
|
||||
* <major>:<minor>\n
|
||||
* vg:<vgname>\n\0
|
||||
*/
|
||||
p += strlen(field);
|
||||
|
||||
if (!(p = strchr(buf, '\n')))
|
||||
return NULL;
|
||||
while (1) {
|
||||
if (*p == '\n')
|
||||
break;
|
||||
if (*p == '\0')
|
||||
break;
|
||||
|
||||
p++; /* skip \n */
|
||||
if (p >= (buf + bufsize))
|
||||
return 0;
|
||||
if (i >= outsize-1)
|
||||
return 0;
|
||||
|
||||
if (*p && !strncmp(p, "vg:", 3)) {
|
||||
if ((n = strchr(p, '\n')))
|
||||
*n = '\0';
|
||||
return p + 3;
|
||||
out[i] = *p;
|
||||
|
||||
i++;
|
||||
p++;
|
||||
}
|
||||
return NULL;
|
||||
|
||||
return i ? 1 : 0;
|
||||
}
|
||||
|
||||
#define MAX_PVID_FILE_SIZE 512
|
||||
|
||||
int online_pvid_file_read(char *path, int *major, int *minor, char *vgname)
|
||||
int online_pvid_file_read(char *path, int *major, int *minor, char *vgname, char *devname)
|
||||
{
|
||||
char buf[MAX_PVID_FILE_SIZE] = { 0 };
|
||||
char *name;
|
||||
int fd, rv;
|
||||
|
||||
fd = open(path, O_RDONLY);
|
||||
@@ -72,11 +87,113 @@ int online_pvid_file_read(char *path, int *major, int *minor, char *vgname)
|
||||
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);
|
||||
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;
|
||||
}
|
||||
@@ -117,7 +234,7 @@ int online_vg_file_create(struct cmd_context *cmd, const char *vgname)
|
||||
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;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We don't care about syncing, these files are not even persistent. */
|
||||
@@ -133,6 +250,9 @@ int online_pvid_file_create(struct cmd_context *cmd, struct device *dev, const c
|
||||
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;
|
||||
@@ -140,6 +260,7 @@ int online_pvid_file_create(struct cmd_context *cmd, struct device *dev, const c
|
||||
int len;
|
||||
int len1 = 0;
|
||||
int len2 = 0;
|
||||
int len3 = 0;
|
||||
|
||||
major = (int)MAJOR(dev->dev);
|
||||
minor = (int)MINOR(dev->dev);
|
||||
@@ -156,13 +277,22 @@ int online_pvid_file_create(struct cmd_context *cmd, struct device *dev, const c
|
||||
|
||||
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);
|
||||
log_print("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;
|
||||
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));
|
||||
|
||||
@@ -207,8 +337,9 @@ check_duplicate:
|
||||
*/
|
||||
|
||||
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_major == major) && (file_minor == minor)) {
|
||||
log_debug("Existing online file for %d:%d", major, minor);
|
||||
@@ -218,8 +349,8 @@ check_duplicate:
|
||||
/* 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);
|
||||
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.",
|
||||
@@ -250,6 +381,78 @@ int online_pvid_file_exists(const char *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;
|
||||
@@ -301,6 +504,4 @@ do_lookup:
|
||||
|
||||
if ((rv < 0) && stat(PVS_LOOKUP_DIR, &st))
|
||||
log_error_pvscan(cmd, "Failed to create %s %d", PVS_LOOKUP_DIR, errno);
|
||||
|
||||
|
||||
}
|
||||
|
@@ -15,6 +15,15 @@
|
||||
#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
|
||||
@@ -36,11 +45,14 @@ do \
|
||||
log_error("pvscan[%d] " fmt, getpid(), ##args); \
|
||||
while (0)
|
||||
|
||||
int online_pvid_file_read(char *path, int *major, int *minor, char *vgname);
|
||||
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
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -236,6 +236,7 @@ static int _touch_newhints(void)
|
||||
return_0;
|
||||
if (fclose(fp))
|
||||
stack;
|
||||
log_debug("newhints created");
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -506,6 +507,19 @@ int validate_hints(struct cmd_context *cmd, struct dm_list *hints)
|
||||
if (!hint->chosen)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* label_scan was unable to read the dev so we don't know its pvid.
|
||||
* Since we are unable to verify the hint is correct, it's possible
|
||||
* that the PVID is actually found on a different device, so don't
|
||||
* depend on hints. (This would also fail the following pvid check.)
|
||||
*/
|
||||
if (dev->flags & DEV_SCAN_NOT_READ) {
|
||||
log_debug("Uncertain hint for unread device %d:%d %s",
|
||||
major(hint->devt), minor(hint->devt), dev_name(dev));
|
||||
ret = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strcmp(dev->pvid, hint->pvid)) {
|
||||
log_debug("Invalid hint device %d:%d %s pvid %s had hint pvid %s",
|
||||
major(hint->devt), minor(hint->devt), dev_name(dev),
|
||||
@@ -1214,8 +1228,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];
|
||||
@@ -1264,6 +1278,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
|
||||
@@ -1280,109 +1299,6 @@ check:
|
||||
free(name);
|
||||
}
|
||||
|
||||
static int _get_hints_from_pvs_online(struct cmd_context *cmd, struct dm_list *hints_out,
|
||||
struct dm_list *devs_in, struct dm_list *devs_out)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
char file_vgname[NAME_LEN];
|
||||
struct dm_list hints_list;
|
||||
struct hint file_hint;
|
||||
struct hint *alloc_hint;
|
||||
struct hint *hint, *hint2;
|
||||
struct device_list *devl, *devl2;
|
||||
int file_major, file_minor;
|
||||
int found = 0;
|
||||
DIR *dir;
|
||||
struct dirent *de;
|
||||
char *vgname = NULL;
|
||||
char *pvid;
|
||||
|
||||
dm_list_init(&hints_list);
|
||||
|
||||
if (!(dir = opendir(PVS_ONLINE_DIR)))
|
||||
return 0;
|
||||
|
||||
while ((de = readdir(dir))) {
|
||||
if (de->d_name[0] == '.')
|
||||
continue;
|
||||
|
||||
pvid = de->d_name;
|
||||
|
||||
if (strlen(pvid) != ID_LEN) /* 32 */
|
||||
continue;
|
||||
|
||||
memset(path, 0, sizeof(path));
|
||||
snprintf(path, sizeof(path), "%s/%s", PVS_ONLINE_DIR, pvid);
|
||||
|
||||
memset(&file_hint, 0, sizeof(file_hint));
|
||||
memset(file_vgname, 0, sizeof(file_vgname));
|
||||
file_major = 0;
|
||||
file_minor = 0;
|
||||
|
||||
if (!online_pvid_file_read(path, &file_major, &file_minor, file_vgname))
|
||||
continue;
|
||||
|
||||
if (!dm_strncpy(file_hint.pvid, pvid, sizeof(file_hint.pvid)))
|
||||
continue;
|
||||
|
||||
file_hint.devt = makedev(file_major, file_minor);
|
||||
|
||||
if (file_vgname[0] && validate_name(file_vgname)) {
|
||||
if (!dm_strncpy(file_hint.vgname, file_vgname, sizeof(file_hint.vgname)))
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(alloc_hint = malloc(sizeof(struct hint))))
|
||||
continue;
|
||||
|
||||
memcpy(alloc_hint, &file_hint, sizeof(struct hint));
|
||||
|
||||
log_debug("add hint %s %d:%d %s from pvs_online", file_hint.pvid, file_major, file_minor, file_vgname);
|
||||
dm_list_add(&hints_list, &alloc_hint->list);
|
||||
found++;
|
||||
}
|
||||
|
||||
if (closedir(dir))
|
||||
stack;
|
||||
|
||||
log_debug("accept hints found %d from pvs_online", found);
|
||||
|
||||
_get_single_vgname_cmd_arg(cmd, &hints_list, &vgname);
|
||||
|
||||
/*
|
||||
* apply_hints equivalent, move devs from devs_in to devs_out if
|
||||
* their devno matches the devno of a hint (and if the hint matches
|
||||
* the vgname when a vgname is present.)
|
||||
*/
|
||||
dm_list_iterate_items_safe(devl, devl2, devs_in) {
|
||||
dm_list_iterate_items_safe(hint, hint2, &hints_list) {
|
||||
if ((MAJOR(devl->dev->dev) == MAJOR(hint->devt)) &&
|
||||
(MINOR(devl->dev->dev) == MINOR(hint->devt))) {
|
||||
|
||||
if (vgname && hint->vgname[0] && strcmp(vgname, hint->vgname))
|
||||
goto next_dev;
|
||||
|
||||
snprintf(hint->name, sizeof(hint->name), "%s", dev_name(devl->dev));
|
||||
hint->chosen = 1;
|
||||
|
||||
dm_list_del(&devl->list);
|
||||
dm_list_add(devs_out, &devl->list);
|
||||
}
|
||||
}
|
||||
next_dev:
|
||||
;
|
||||
}
|
||||
|
||||
log_debug("applied hints using %d other %d vgname %s from pvs_online",
|
||||
dm_list_size(devs_out), dm_list_size(devs_in), vgname ?: "");
|
||||
|
||||
dm_list_splice(hints_out, &hints_list);
|
||||
|
||||
free(vgname);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns 0: no hints are used.
|
||||
* . newhints is set if this command should create new hints after scan
|
||||
@@ -1404,7 +1320,7 @@ int get_hints(struct cmd_context *cmd, struct dm_list *hints_out, int *newhints,
|
||||
*newhints = NEWHINTS_NONE;
|
||||
|
||||
/* No commands are using hints. */
|
||||
if (!cmd->enable_hints && !cmd->hints_pvs_online)
|
||||
if (!cmd->enable_hints)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
@@ -1424,19 +1340,6 @@ int get_hints(struct cmd_context *cmd, struct dm_list *hints_out, int *newhints,
|
||||
if (!cmd->use_hints)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* enable_hints is 0 for the special hints=pvs_online
|
||||
* and by lvm.conf hints="none" does not disable hints=pvs_online.
|
||||
* hints=pvs_online can be disabled with --nohints.
|
||||
*/
|
||||
if (cmd->hints_pvs_online) {
|
||||
if (!_get_hints_from_pvs_online(cmd, &hints_list, devs_in, devs_out)) {
|
||||
log_debug("get_hints: pvs_online failed");
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if another command created the nohints file to prevent us from
|
||||
* using hints.
|
||||
@@ -1508,6 +1411,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;
|
||||
|
||||
@@ -1541,7 +1454,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);
|
||||
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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>
|
||||
@@ -686,6 +687,8 @@ static int _scan_list(struct cmd_context *cmd, struct dev_filter *f,
|
||||
|
||||
dm_list_iterate_items_safe(devl, devl2, devs) {
|
||||
|
||||
devl->dev->flags &= ~DEV_SCAN_NOT_READ;
|
||||
|
||||
/*
|
||||
* If we prefetch more devs than blocks in the cache, then the
|
||||
* cache will wait for earlier reads to complete, toss the
|
||||
@@ -701,6 +704,7 @@ static int _scan_list(struct cmd_context *cmd, struct dev_filter *f,
|
||||
log_debug_devs("Scan failed to open %s.", dev_name(devl->dev));
|
||||
dm_list_del(&devl->list);
|
||||
dm_list_add(&reopen_devs, &devl->list);
|
||||
devl->dev->flags |= DEV_SCAN_NOT_READ;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -724,6 +728,7 @@ static int _scan_list(struct cmd_context *cmd, struct dev_filter *f,
|
||||
log_debug_devs("Scan failed to read %s.", dev_name(devl->dev));
|
||||
scan_read_errors++;
|
||||
scan_failed_count++;
|
||||
devl->dev->flags |= DEV_SCAN_NOT_READ;
|
||||
lvmcache_del_dev(devl->dev);
|
||||
if (bb)
|
||||
bcache_put(bb);
|
||||
@@ -1020,6 +1025,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
|
||||
@@ -1113,6 +1393,10 @@ int label_scan(struct cmd_context *cmd)
|
||||
* filter", and this result needs to be cleared (wiped) so that the
|
||||
* complete set of filters (including those that require data) can be
|
||||
* checked in _process_block, where headers have been read.
|
||||
*
|
||||
* FIXME: devs that are filtered with data in _process_block
|
||||
* are not moved to the filtered_devs list like devs filtered
|
||||
* here without data. Does that have any effect?
|
||||
*/
|
||||
log_debug_devs("Filtering devices to scan (nodata)");
|
||||
|
||||
@@ -1308,7 +1592,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;
|
||||
}
|
||||
@@ -1318,7 +1602,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;
|
||||
}
|
||||
|
@@ -118,6 +118,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);
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -61,8 +61,6 @@ and more, using a more compact and configurable output format.
|
||||
.br
|
||||
[ \fB--readonly\fP ]
|
||||
.br
|
||||
[ \fB--reportformat\fP \fBbasic\fP|\fBjson\fP ]
|
||||
.br
|
||||
[ \fB--segments\fP ]
|
||||
.br
|
||||
[ \fB--separator\fP \fIString\fP ]
|
||||
@@ -332,16 +330,6 @@ device-mapper kernel driver, so this option is unable to report whether
|
||||
or not LVs are actually in use.
|
||||
.
|
||||
.HP
|
||||
\fB--reportformat\fP \fBbasic\fP|\fBjson\fP
|
||||
.br
|
||||
Overrides current output format for reports which is defined globally by
|
||||
the report/output_format setting in \fBlvm.conf\fP(5).
|
||||
\fBbasic\fP is the original format with columns and rows.
|
||||
If there is more than one report per command, each report is prefixed
|
||||
with the report name for identification. \fBjson\fP produces report
|
||||
output in JSON format. See \fBlvmreport\fP(7) for more information.
|
||||
.
|
||||
.HP
|
||||
\fB--segments\fP
|
||||
.br
|
||||
.
|
||||
|
@@ -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),
|
||||
|
@@ -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)
|
289
man/lvmautoactivation.7_main
Normal file
289
man/lvmautoactivation.7_main
Normal 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
|
||||
|
@@ -322,7 +322,8 @@ Find a device with the PVID and add the device to the devices file.
|
||||
.HP
|
||||
\fB--check\fP
|
||||
.br
|
||||
Check the content of the devices file.
|
||||
Checks the content of the devices file.
|
||||
Reports incorrect device names or PVIDs for entries.
|
||||
.
|
||||
.HP
|
||||
\fB--commandprofile\fP \fIString\fP
|
||||
|
@@ -61,8 +61,6 @@ and more, using a more compact and configurable output format.
|
||||
.br
|
||||
[ \fB--readonly\fP ]
|
||||
.br
|
||||
[ \fB--reportformat\fP \fBbasic\fP|\fBjson\fP ]
|
||||
.br
|
||||
[ \fB--separator\fP \fIString\fP ]
|
||||
.br
|
||||
[ \fB--shared\fP ]
|
||||
@@ -320,16 +318,6 @@ device-mapper kernel driver, so this option is unable to report whether
|
||||
or not LVs are actually in use.
|
||||
.
|
||||
.HP
|
||||
\fB--reportformat\fP \fBbasic\fP|\fBjson\fP
|
||||
.br
|
||||
Overrides current output format for reports which is defined globally by
|
||||
the report/output_format setting in \fBlvm.conf\fP(5).
|
||||
\fBbasic\fP is the original format with columns and rows.
|
||||
If there is more than one report per command, each report is prefixed
|
||||
with the report name for identification. \fBjson\fP produces report
|
||||
output in JSON format. See \fBlvmreport\fP(7) for more information.
|
||||
.
|
||||
.HP
|
||||
\fB-S\fP|\fB--select\fP \fIString\fP
|
||||
.br
|
||||
Select objects for processing and reporting based on specified criteria.
|
||||
|
@@ -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.
|
||||
|
@@ -15,6 +15,8 @@ pvscan \(em List all physical volumes
|
||||
.P
|
||||
.ad l
|
||||
\fB-a\fP|\fB--activate\fP \fBy\fP|\fBn\fP|\fBay\fP
|
||||
.br
|
||||
\fB--autoactivation\fP \fIString\fP
|
||||
.br
|
||||
\fB--cache\fP
|
||||
.br
|
||||
@@ -91,59 +93,50 @@ 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.
|
||||
.
|
||||
.SH USAGE
|
||||
.
|
||||
@@ -215,6 +208,8 @@ Record that a PV is online and autoactivate the VG if complete.
|
||||
.br
|
||||
[ \fB--noudevsync\fP ]
|
||||
.br
|
||||
[ \fB--autoactivation\fP \fIString\fP ]
|
||||
.br
|
||||
[ COMMON_OPTIONS ]
|
||||
.ad b
|
||||
.RE
|
||||
@@ -239,6 +234,8 @@ Record that a PV is online and list the VG using the PV.
|
||||
.br
|
||||
[ \fB--udevoutput\fP ]
|
||||
.br
|
||||
[ \fB--autoactivation\fP \fIString\fP ]
|
||||
.br
|
||||
[ COMMON_OPTIONS ]
|
||||
.ad b
|
||||
.RE
|
||||
@@ -342,6 +339,14 @@ Auto-activate LVs in a VG when the PVs scanned have completed the VG.
|
||||
(Only \fBay\fP is applicable.)
|
||||
.
|
||||
.HP
|
||||
\fB--autoactivation\fP \fIString\fP
|
||||
.br
|
||||
Specify if autoactivation is being used from an event.
|
||||
This allows the command to apply settings that are specific
|
||||
to event activation, such as device scanning optimizations
|
||||
using pvs_online files created by event-based pvscans.
|
||||
.
|
||||
.HP
|
||||
\fB--cache\fP
|
||||
.br
|
||||
Scan one or more devices and record that they are online.
|
||||
|
@@ -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
|
||||
|
@@ -24,6 +24,8 @@ vgchange \(em Change volume group attributes
|
||||
.nh
|
||||
\%\fBcontiguous\fP|\:\fBcling\fP|\:\fBcling_by_tags\fP|\:\fBnormal\fP|\:\fBanywhere\fP|\:\fBinherit\fP
|
||||
.hy
|
||||
.br
|
||||
\fB--autoactivation\fP \fIString\fP
|
||||
.br
|
||||
\fB-A\fP|\fB--autobackup\fP \fBy\fP|\fBn\fP
|
||||
.br
|
||||
@@ -286,6 +288,8 @@ Activate or deactivate LVs.
|
||||
.br
|
||||
[ \fB--poll\fP \fBy\fP|\fBn\fP ]
|
||||
.br
|
||||
[ \fB--autoactivation\fP \fIString\fP ]
|
||||
.br
|
||||
[ \fB--ignoremonitoring\fP ]
|
||||
.br
|
||||
[ \fB--noudevsync\fP ]
|
||||
@@ -516,6 +520,14 @@ which PVs the command will use for allocation.
|
||||
See \fBlvm\fP(8) for more information about allocation.
|
||||
.
|
||||
.HP
|
||||
\fB--autoactivation\fP \fIString\fP
|
||||
.br
|
||||
Specify if autoactivation is being used from an event.
|
||||
This allows the command to apply settings that are specific
|
||||
to event activation, such as device scanning optimizations
|
||||
using pvs_online files created by event-based pvscans.
|
||||
.
|
||||
.HP
|
||||
\fB-A\fP|\fB--autobackup\fP \fBy\fP|\fBn\fP
|
||||
.br
|
||||
Specifies if metadata should be backed up automatically after a change.
|
||||
|
@@ -58,8 +58,6 @@ and more, using a more compact and configurable output format.
|
||||
.br
|
||||
[ \fB--readonly\fP ]
|
||||
.br
|
||||
[ \fB--reportformat\fP \fBbasic\fP|\fBjson\fP ]
|
||||
.br
|
||||
[ \fB--shared\fP ]
|
||||
.br
|
||||
[ \fB--separator\fP \fIString\fP ]
|
||||
@@ -312,16 +310,6 @@ device-mapper kernel driver, so this option is unable to report whether
|
||||
or not LVs are actually in use.
|
||||
.
|
||||
.HP
|
||||
\fB--reportformat\fP \fBbasic\fP|\fBjson\fP
|
||||
.br
|
||||
Overrides current output format for reports which is defined globally by
|
||||
the report/output_format setting in \fBlvm.conf\fP(5).
|
||||
\fBbasic\fP is the original format with columns and rows.
|
||||
If there is more than one report per command, each report is prefixed
|
||||
with the report name for identification. \fBjson\fP produces report
|
||||
output in JSON format. See \fBlvmreport\fP(7) for more information.
|
||||
.
|
||||
.HP
|
||||
\fB-S\fP|\fB--select\fP \fIString\fP
|
||||
.br
|
||||
Select objects for processing and reporting based on specified criteria.
|
||||
|
@@ -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)
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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;
|
||||
}
|
@@ -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
|
||||
|
@@ -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;
|
||||
}
|
@@ -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
|
||||
|
@@ -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"
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
67
test/shell/duplicate-pvs-multipath.sh
Normal file
67
test/shell/duplicate-pvs-multipath.sh
Normal 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
|
@@ -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"
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -19,61 +19,162 @@ _clear_online_files() {
|
||||
|
||||
aux prepare_devs 4
|
||||
|
||||
vgcreate $vg1 "$dev1" "$dev2"
|
||||
vgcreate $vg2 "$dev3"
|
||||
pvcreate "$dev4"
|
||||
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
|
||||
|
||||
# With no pv online files, vgchange that uses online files
|
||||
# will find no PVs to activate from.
|
||||
# Expected use, with vg name and all online files exist for vgchange.
|
||||
|
||||
_clear_online_files
|
||||
|
||||
not vgchange -aay --autoactivation event $vg1
|
||||
not vgchange -aay --autoactivation event $vg2
|
||||
vgchange -aay --autoactivation event
|
||||
|
||||
check lv_field $vg1/$lv1 lv_active ""
|
||||
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 ""
|
||||
|
||||
# incomplete vg will not be activated
|
||||
|
||||
pvscan --cache "$dev1"
|
||||
vgchange -aay --autoactivation event $vg1
|
||||
# VG foo is incomplete
|
||||
check lv_field $vg1/$lv1 lv_active ""
|
||||
|
||||
# complete vg is activated
|
||||
|
||||
pvscan --cache "$dev3"
|
||||
pvscan --cache "$bd3"
|
||||
vgchange -aay --autoactivation event $vg2
|
||||
check lv_field $vg2/$lv1 lv_active "active"
|
||||
|
||||
pvscan --cache "$dev2"
|
||||
vgchange -aay --autoactivation event $vg1
|
||||
check lv_field $vg1/$lv1 lv_active "active"
|
||||
|
||||
vgchange -an $vg1
|
||||
vgchange -an $vg2
|
||||
|
||||
# the same tests but using command options matching udev rule
|
||||
# Count io to check the pvs_online optimization
|
||||
# is working to limit scanning.
|
||||
|
||||
if which strace; then
|
||||
vgchange -an
|
||||
_clear_online_files
|
||||
|
||||
pvscan --cache --listvg --checkcomplete --vgonline --autoactivation event --udevoutput --journal=output "$dev1"
|
||||
vgchange -aay --autoactivation event $vg1
|
||||
# VG foo is incomplete
|
||||
check lv_field $vg1/$lv1 lv_active ""
|
||||
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 ""
|
||||
|
||||
pvscan --cache --listvg --checkcomplete --vgonline --autoactivation event --udevoutput --journal=output "$dev3"
|
||||
vgchange -aay --autoactivation event $vg2
|
||||
check lv_field $vg2/$lv1 lv_active "active"
|
||||
|
||||
pvscan --cache --listvg --checkcomplete --vgonline --autoactivation event --udevoutput --journal=output "$dev2"
|
||||
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
|
||||
|
@@ -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 \
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
@@ -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);
|
||||
|
@@ -159,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"
|
||||
|
@@ -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
|
||||
|
||||
---
|
||||
@@ -1811,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
|
||||
|
||||
---
|
||||
|
@@ -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,\
|
||||
|
@@ -2542,8 +2542,6 @@ static int _get_current_settings(struct cmd_context *cmd)
|
||||
if (!strcmp(hint_mode, "none")) {
|
||||
cmd->enable_hints = 0;
|
||||
cmd->use_hints = 0;
|
||||
} else if (!strcmp(hint_mode, "pvs_online")) {
|
||||
cmd->hints_pvs_online = 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3062,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 */
|
||||
@@ -3144,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.
|
||||
*/
|
||||
|
@@ -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);
|
||||
@@ -184,8 +183,11 @@ int lvmdevices(struct cmd_context *cmd, int argc, char **argv)
|
||||
|
||||
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) {
|
||||
@@ -225,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
|
||||
@@ -240,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;
|
||||
}
|
||||
@@ -388,28 +383,27 @@ int lvmdevices(struct cmd_context *cmd, int argc, char **argv)
|
||||
* No filter because we always want to allow removing a device
|
||||
* by name from the devices file.
|
||||
*/
|
||||
if (!(dev = dev_cache_get(cmd, devname, NULL))) {
|
||||
log_error("No device found for %s.", devname);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
/*
|
||||
* dev_cache_scan uses sysfs to check if an LV is using each dev
|
||||
* and sets this flag is so.
|
||||
*/
|
||||
if (dev_is_used_by_active_lv(cmd, dev, NULL, NULL, NULL, NULL)) {
|
||||
if (!arg_count(cmd, yes_ARG) &&
|
||||
yes_no_prompt("Device %s is used by an active LV, continue to remove? ", devname) == 'n') {
|
||||
log_error("Device not removed.");
|
||||
goto bad;
|
||||
if ((dev = dev_cache_get(cmd, devname, NULL))) {
|
||||
/*
|
||||
* dev_cache_scan uses sysfs to check if an LV is using each dev
|
||||
* and sets this flag is so.
|
||||
*/
|
||||
if (dev_is_used_by_active_lv(cmd, dev, NULL, NULL, NULL, NULL)) {
|
||||
if (!arg_count(cmd, yes_ARG) &&
|
||||
yes_no_prompt("Device %s is used by an active LV, continue to remove? ", devname) == 'n') {
|
||||
log_error("Device not removed.");
|
||||
goto bad;
|
||||
}
|
||||
}
|
||||
if ((du = get_du_for_dev(cmd, dev)))
|
||||
goto dev_del;
|
||||
}
|
||||
|
||||
if (!(du = get_du_for_dev(cmd, dev))) {
|
||||
log_error("Device not found in devices file.");
|
||||
if (!(du = get_du_for_devname(cmd, devname))) {
|
||||
log_error("No devices file entry for %s.", devname);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
dev_del:
|
||||
dm_list_del(&du->list);
|
||||
free_du(du);
|
||||
device_ids_write(cmd);
|
||||
|
158
tools/pvscan.c
158
tools/pvscan.c
@@ -223,7 +223,7 @@ static void _online_pvid_file_remove_devno(int major, int minor)
|
||||
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);
|
||||
@@ -289,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))
|
||||
@@ -509,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;
|
||||
@@ -538,8 +539,9 @@ static int _get_devs_from_saved_vg(struct cmd_context *cmd, const char *vgname,
|
||||
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",
|
||||
@@ -549,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;
|
||||
}
|
||||
|
||||
@@ -567,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);
|
||||
@@ -819,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;
|
||||
@@ -883,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) {
|
||||
@@ -917,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;
|
||||
@@ -944,9 +1006,10 @@ static void _set_pv_devices_online(struct cmd_context *cmd, struct volume_group
|
||||
|
||||
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",
|
||||
@@ -957,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;
|
||||
}
|
||||
@@ -1061,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))
|
||||
@@ -1142,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 {
|
||||
/*
|
||||
@@ -1378,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;
|
||||
}
|
||||
|
121
tools/vgchange.c
121
tools/vgchange.c
@@ -15,6 +15,7 @@
|
||||
|
||||
#include "tools.h"
|
||||
#include "lib/device/device_id.h"
|
||||
#include "lib/label/hints.h"
|
||||
|
||||
struct vgchange_params {
|
||||
int lock_start_count;
|
||||
@@ -734,12 +735,19 @@ static int _vgchange_single(struct cmd_context *cmd, const char *vg_name,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _check_autoactivation(struct cmd_context *cmd, struct vgchange_params *vp, int *skip_command)
|
||||
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 1;
|
||||
return_0;
|
||||
|
||||
if (strcmp(aa, "event")) {
|
||||
log_print("Skip vgchange for unknown autoactivation value.");
|
||||
@@ -754,19 +762,116 @@ static int _check_autoactivation(struct cmd_context *cmd, struct vgchange_params
|
||||
}
|
||||
|
||||
vp->vg_complete_to_activate = 1;
|
||||
cmd->use_hints = 0;
|
||||
|
||||
if (!arg_is_set(cmd, nohints_ARG))
|
||||
cmd->hints_pvs_online = 1;
|
||||
else
|
||||
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;
|
||||
|
||||
@@ -881,7 +986,7 @@ int vgchange(struct cmd_context *cmd, int argc, char **argv)
|
||||
|
||||
if (arg_is_set(cmd, autoactivation_ARG)) {
|
||||
int skip_command = 0;
|
||||
if (!_check_autoactivation(cmd, &vp, &skip_command))
|
||||
if (!_vgchange_autoactivation_setup(cmd, &vp, &skip_command, &vgname, &flags))
|
||||
return ECMD_FAILED;
|
||||
if (skip_command)
|
||||
return ECMD_PROCESSED;
|
||||
@@ -899,7 +1004,7 @@ int vgchange(struct cmd_context *cmd, int argc, char **argv)
|
||||
|
||||
handle->custom_handle = &vp;
|
||||
|
||||
ret = process_each_vg(cmd, argc, argv, NULL, NULL, flags, 0, handle, &_vgchange_single);
|
||||
ret = process_each_vg(cmd, argc, argv, vgname, NULL, flags, 0, handle, &_vgchange_single);
|
||||
|
||||
destroy_processing_handle(cmd, handle);
|
||||
return ret;
|
||||
|
@@ -80,7 +80,7 @@ ENV{SYSTEMD_READY}="1"
|
||||
# it's better suited to appearing in the journal.
|
||||
|
||||
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 -r --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}"
|
||||
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"
|
||||
|
Reference in New Issue
Block a user