1
0
mirror of git://sourceware.org/git/lvm2.git synced 2024-12-21 13:34:40 +03:00

add activation services

New systemd services for startup:

lvm-devices-wait.service
  Used in place of systemd-udev-settle, this service waits
  for udev+pvscan to process PVs listed in system.devices.
  It runs the command "lvmdevices --wait pvsonline".
  This only waits for PVs that can be matched to a device in
  sysfs, so it only waits for devices attached to the system.
  It waits specifically for the /run/lvm/pvs_online/<pvid>
  files to be created by pvscan.  It quits waiting after a
  configurable number of seconds.  This service gives the
  first activation service a chance to activate VGs from
  PVs that are available immediately at startup.  If this
  service quits waiting before all the expected pvid files
  appear, then the VG associated with those PVs will most
  likely be activated by the -last service rather than the
  initial -main service.  If those PVs are even slower to
  complete processing than the -last service, then the VG
  will be activated by event activation whenever they are
  finally complete.

lvm-activate-vgs-main.service
  Calls "vgchange -aay", after lvm-devices-wait, to activate
  complete VGs.  It only considers PVs that have been
  processed by udev+pvscan and have pvs_online files.
  This is expected to activate VGs from basic devices
  (not virtual device types) that are present at startup.

lvm-activate-vgs-last.service
  Calls "vgchange -aay", after multipathd has started, to
  activate VGs that became available after virtual device
  services were started, e.g. VGs on multipath devices.
  Like -main, it only looks at PVs that have been processed
  by pvscan.

This vgchange in the -last service enables event activation
by creating the /run/lvm/event-activation-on file.  Event
activation will activate any further VGs that appear on the
system (or complete udev processing) after the -last service.
In the case of event activation, the udev rule will run
vgchange -aay <vgname> via a transient service
lvm-activate-<vgname>.service.  This vgchange only scans
PVs in the VG being activated, also based on the pvs_online
files from pvscan.

When there are many VGs that need activation during system
startup, the two fixed services can activate them all much
faster than activating each VG individually via events.

lvm.conf auto_activation_settings can be used to configure
the behavior (default ["service_and_event", "pvscan_hints"]).

"service_and_event" - the behavior described above, where
activation services are used first, and event activation
is used afterward.

"service_only" - only lvm-activate-vgs-* are used, and
no event-based activation occurs after the services finish.
(Equivalent to setting lvm.conf event_activation=0.)

"event_only" - the lvm-activate-vgs* services are skipped,
and all VGs are activated individually with event-based
activation.

"pvscan_hints" - the vgchange autoactivation commands
use pvs_online files created by pvscan.  This optimization
limits the devices scanned by the vgchange command to only
PVs that have been processed by pvscan.
This commit is contained in:
David Teigland 2021-09-02 15:15:46 -05:00
parent f056bf8f94
commit 0f9d271bd9
25 changed files with 807 additions and 74 deletions

View File

@ -2023,6 +2023,9 @@ libdm/libdevmapper.pc
man/Makefile
po/Makefile
scripts/lvm2-pvscan.service
scripts/lvm-activate-vgs-main.service
scripts/lvm-activate-vgs-last.service
scripts/lvm-devices-wait.service
scripts/blkdeactivate.sh
scripts/blk_availability_init_red_hat
scripts/blk_availability_systemd_red_hat.service

View File

@ -174,7 +174,7 @@ struct cmd_context {
unsigned activate_component:1; /* command activates component LV */
unsigned process_component_lvs:1; /* command processes also component LVs */
unsigned mirror_warn_printed:1; /* command already printed warning about non-monitored mirrors */
unsigned pvscan_cache_single:1;
unsigned expect_missing_vg_device:1; /* when reading a vg it's expected that a dev for a pv isn't found */
unsigned can_use_one_scan:1;
unsigned is_clvmd:1;
unsigned md_component_detection:1;

View File

@ -257,6 +257,12 @@ cfg(devices_hints_CFG, "hints", devices_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_
" Use no hints.\n"
"#\n")
cfg(devices_lvmdevices_wait_seconds_CFG, "lvmdevices_wait_seconds", devices_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_INT, DEFAULT_LVMDEVICES_WAIT_SECONDS, vsn(2, 3, 14), NULL, 0, NULL,
"The max number of seconds the lvmdevices --wait command will wait.\n"
"Set to -1 to use a variable timeout based on the number of devices.\n"
"Set to 0 to disable waiting, in which case the command will report\n"
"the current status and then exit.\n")
cfg_array(devices_preferred_names_CFG, "preferred_names", devices_CFG_SECTION, CFG_ALLOW_EMPTY | CFG_DEFAULT_UNDEFINED , CFG_TYPE_STRING, NULL, vsn(1, 2, 19), NULL, 0, NULL,
"Select which path name to display for a block device.\n"
"If multiple path names exist for a block device, and LVM needs to\n"
@ -1121,12 +1127,8 @@ cfg(global_lvdisplay_shows_full_device_path_CFG, "lvdisplay_shows_full_device_pa
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"
"the pvscan command, and autoactivation when all PVs for a VG are online.\n"
"Also see auto_activation_settings.\n"
"See the --setautoactivation option or the auto_activation_volume_list\n"
"setting to configure autoactivation for specific VGs or LVs.\n")
@ -1409,6 +1411,21 @@ cfg_array(activation_volume_list_CFG, "volume_list", activation_CFG_SECTION, CFG
"volume_list = [ \"vg1\", \"vg2/lvol1\", \"@tag1\", \"@*\" ]\n"
"#\n")
cfg_array(activation_auto_activation_settings_CFG, "auto_activation_settings", global_CFG_SECTION, CFG_ALLOW_EMPTY | CFG_DEFAULT_COMMENTED, CFG_TYPE_STRING, DEFAULT_AUTOACTIVATION_SETTINGS, vsn(2, 3, 14), NULL, 0, NULL,
"Configure autoactivation behavior.\n"
"service_and_event: use fixed activation services, then switch to event activation\n."
"(Only used when event_activation=1.)\n"
"event_only: use only event activation\n"
"(Only used when event_activation=1.)\n"
"service_only: use only fixed activation services\n"
"(Effectively the equivalent of event_activation=0.)\n"
"pvscan_hints: autoactivation commands will use PVs that\n"
"that have been processed by pvscan (from udev rule.)\n"
"Without pvscan_hints, pvscan only be used when it is needed\n"
"to perform event activation.\n"
"Autoactivation commands should set --autoactivation service|event\n"
"to indicate if they are performing service or event activation.\n")
cfg_array(activation_auto_activation_volume_list_CFG, "auto_activation_volume_list", activation_CFG_SECTION, CFG_ALLOW_EMPTY | CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, NULL, vsn(2, 2, 97), NULL, 0, NULL,
"A list of VGs or LVs that should be autoactivated.\n"
"Autoactivation is an activation command run with -aay,\n"

View File

@ -332,4 +332,10 @@
#define VGS_ONLINE_DIR DEFAULT_RUN_DIR "/vgs_online"
#define PVS_LOOKUP_DIR DEFAULT_RUN_DIR "/pvs_lookup"
#define DEFAULT_AUTOACTIVATION_SETTING1 "service_and_event"
#define DEFAULT_AUTOACTIVATION_SETTING2 "pvscan_hints"
#define DEFAULT_AUTOACTIVATION_SETTINGS "#S" DEFAULT_AUTOACTIVATION_SETTING1 "#S" DEFAULT_AUTOACTIVATION_SETTING2
#define DEFAULT_LVMDEVICES_WAIT_SECONDS -1
#endif /* _LVM_DEFAULTS_H */

View File

@ -779,7 +779,7 @@ static void _device_ids_update_try(struct cmd_context *cmd)
int held = 0;
/* Defer updates to non-pvscan-cache commands. */
if (cmd->pvscan_cache_single) {
if (cmd->expect_missing_vg_device) {
log_print("pvscan[%d] skip updating devices file.", getpid());
return;
}

View File

@ -156,6 +156,7 @@
#include <sys/file.h>
#include <sys/sysmacros.h>
/* FIXME: move online pv functions to pvs_online.c */
int online_pvid_file_read(char *path, int *major, int *minor, char *vgname);
static const char *_hints_file = DEFAULT_RUN_DIR "/hints";
@ -1406,7 +1407,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)
if (!cmd->enable_hints && !cmd->hints_pvs_online)
return 0;
/*
@ -1426,7 +1427,11 @@ int get_hints(struct cmd_context *cmd, struct dm_list *hints_out, int *newhints,
if (!cmd->use_hints)
return 0;
/* hints = "pvs_online" */
/*
* 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");

View File

@ -1130,6 +1130,9 @@ int label_scan(struct cmd_context *cmd)
}
}
}
log_debug_devs("Filtering devices to scan done (nodata)");
cmd->filter_nodata_only = 0;
dm_list_iterate_items(devl, &all_devs)

View File

@ -3558,7 +3558,7 @@ static void _set_pv_device(struct format_instance *fid,
if (!id_write_format(&pv->id, buffer, sizeof(buffer)))
buffer[0] = '\0';
if (cmd && !cmd->pvscan_cache_single &&
if (cmd && !cmd->expect_missing_vg_device &&
(!vg_is_foreign(vg) && !cmd->include_foreign_vgs))
log_warn("WARNING: Couldn't find device with uuid %s.", buffer);
else
@ -5084,7 +5084,7 @@ struct volume_group *vg_read(struct cmd_context *cmd, const char *vg_name, const
if (!pvl->pv->dev) {
/* The obvious and common case of a missing device. */
if (vg_is_foreign(vg) && !cmd->include_foreign_vgs)
if ((vg_is_foreign(vg) && !cmd->include_foreign_vgs) || cmd->expect_missing_vg_device)
log_debug("VG %s is missing PV %s (last written to %s)", vg_name, uuidstr, pvl->pv->device_hint ?: "na");
else if (pvl->pv->device_hint)
log_warn("WARNING: VG %s is missing PV %s (last written to %s).", vg_name, uuidstr, pvl->pv->device_hint);

View File

@ -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")
@ -66,7 +63,7 @@ install_initscripts:
@echo " [INSTALL] initscripts"
$(Q) $(INSTALL_DIR) $(initdir)
ifeq ("@BUILD_DMEVENTD@", "yes")
$(Q) $(INSTALL_SCRIPT) lvm2_monitoring_init_red_hat $(initdir)/lvm2-monitor
$(Q) $(INSTALL_SCRIPT) lvm2_monitoring_init_red_hat $(initdir)/lvm-monitor
endif
ifeq ("@BUILD_LVMPOLLD@", "yes")
$(Q) $(INSTALL_SCRIPT) lvm2_lvmpolld_init_red_hat $(initdir)/lvm2-lvmpolld
@ -78,24 +75,15 @@ 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)
$(Q) $(INSTALL_DATA) lvm-activate-vgs-main.service $(systemd_unit_dir)/lvm-activate-vgs-main.service
$(Q) $(INSTALL_DATA) lvm-activate-vgs-last.service $(systemd_unit_dir)/lvm-activate-vgs-last.service
ifeq ("@BUILD_DMEVENTD@", "yes")
$(Q) $(INSTALL_DATA) dm_event_systemd_red_hat.socket $(systemd_unit_dir)/dm-event.socket
$(Q) $(INSTALL_DATA) dm_event_systemd_red_hat.service $(systemd_unit_dir)/dm-event.service
$(Q) $(INSTALL_DATA) lvm2_monitoring_systemd_red_hat.service $(systemd_unit_dir)/lvm2-monitor.service
$(Q) $(INSTALL_DATA) lvm2_monitoring_systemd_red_hat.service $(systemd_unit_dir)/lvm-monitor.service
endif
ifeq ("@BLKDEACTIVATE@", "yes")
$(Q) $(INSTALL_DATA) blk_availability_systemd_red_hat.service $(systemd_unit_dir)/blk-availability.service
@ -155,7 +143,9 @@ DISTCLEAN_TARGETS += \
lvm2_monitoring_init_red_hat \
lvm2_monitoring_systemd_red_hat.service \
lvm2_pvscan_systemd_red_hat@.service \
lvm2_tmpfiles_red_hat.conf
lvm2_tmpfiles_red_hat.conf \
lvm-activate-vgs-main.service \
lvm-activate-vgs-last.service
# Remove ancient files
DISTCLEAN_TARGETS += \

View File

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

View File

@ -0,0 +1,24 @@
[Unit]
Description=Activate LVM Volume Groups (last)
Documentation=man:vgchange(8)
After=lvm-activate-vgs-main.service multipathd.service dm-event.socket dm-event.service
Before=local-fs-pre.target shutdown.target
DefaultDependencies=no
Conflicts=shutdown.target
# "--autoactivation service" tells vgchange it is being called
# from an activation service, so it will do nothing if
# lvm.conf autoactivation_settings has "event_only".
# "--autoactivation event_enable" tells vgchange to enable
# event-based pvscan activations by creating /run/lvm/event-activation-on.
# By default this vgchange will use device hints from pvs_online
# files, so it will only look at PVs that have finished being
# processed by udev and pvscan --cache.
[Service]
Type=oneshot
ExecStart=@SBINDIR@/lvm vgchange -aay --vgonline --autoactivation service,event_enable
RemainAfterExit=yes
[Install]
WantedBy=sysinit.target

View File

@ -0,0 +1,22 @@
[Unit]
Description=Activate LVM Volume Groups
Documentation=man:vgchange(8)
After=dm-event.socket dm-event.service lvm-devices-wait.service
Before=local-fs-pre.target shutdown.target
DefaultDependencies=no
Conflicts=shutdown.target
# "--autoactivation service" tells vgchange it is being called
# from an autoactivation service, so it will do nothing if
# lvm.conf autoactivation_settings has "event_only".
# By default this vgchange will use device hints from pvs_online
# files, so it will only look at PVs that have finished being
# processed by udev and pvscan --cache.
[Service]
Type=oneshot
ExecStart=@SBINDIR@/lvm vgchange -aay --vgonline --autoactivation service
RemainAfterExit=yes
[Install]
WantedBy=sysinit.target

View File

@ -0,0 +1,21 @@
[Unit]
Description=Wait for LVM devices to be ready
Documentation=man:lvmdevices(8)
After=systemd-udev-trigger.service
Before=lvm-activate-vgs-main.service lvm-activate-vgs-last.service
DefaultDependencies=no
Conflicts=shutdown.target
# Waits for entries in /etc/lvm/devices/system.devices to be processed
# by udev and pvscan --cache. Only device entries that are found in
# sysfs are waited for. See lvm.conf devices_wait_service "timeout=N"
# to configure the number of seconds that this service will wait.
[Service]
Type=oneshot
ExecStart=@SBINDIR@/lvmdevices --wait pvsonline
RemainAfterExit=yes
[Install]
WantedBy=sysinit.target

View File

@ -1,8 +1,8 @@
[Unit]
Description=Monitoring of LVM2 mirrors, snapshots etc. using dmeventd or progress polling
Description=Monitor LVM Logical Volumes
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 lvm-activate-vgs-last.service
Before=local-fs-pre.target shutdown.target
DefaultDependencies=no
Conflicts=shutdown.target

View File

@ -23,10 +23,10 @@ if [ $1 = 0 ]; then
fi
%triggerun -- %{name} < 2.02.86-2
%{_bindir}/systemd-sysv-convert --save lvm2-monitor >/dev/null 2>&1 || :
/bin/systemctl --no-reload enable lvm2-monitor.service > /dev/null 2>&1 || :
/sbin/chkconfig --del lvm2-monitor > /dev/null 2>&1 || :
/bin/systemctl try-restart lvm2-monitor.service > /dev/null 2>&1 || :
%{_bindir}/systemd-sysv-convert --save lvm-monitor >/dev/null 2>&1 || :
/bin/systemctl --no-reload enable lvm-monitor.service > /dev/null 2>&1 || :
/sbin/chkconfig --del lvm-monitor > /dev/null 2>&1 || :
/bin/systemctl try-restart lvm-monitor.service > /dev/null 2>&1 || :
# files in the main package
%files
@ -100,9 +100,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
@ -187,16 +184,17 @@ fi
%dir %{_default_run_dir}
%if %{enable_systemd}
%{_tmpfilesdir}/%{name}.conf
%{_unitdir}/lvm-activate-vgs-main.service
%{_unitdir}/lvm-activate-vgs-last.service
%{_unitdir}/lvm-monitor.service
%{_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
%endif
%else
%{_sysconfdir}/rc.d/init.d/blk-availability
%{_sysconfdir}/rc.d/init.d/lvm2-monitor
%{_sysconfdir}/rc.d/init.d/lvm-monitor
%if %{have_service lvmpolld}
%{_sysconfdir}/rc.d/init.d/lvm2-lvmpolld
%endif

View File

@ -87,6 +87,11 @@ arg(atversion_ARG, '\0', "atversion", string_VAL, 0, 0,
"which does not contain any newer settings for which LVM would\n"
"issue a warning message when checking the configuration.\n")
arg(autoactivation_ARG, '\0', "autoactivation", string_VAL, 0, 0,
"Specify if the command is running autoactivation from an event\n"
"or a service. lvm.conf autoactivation_settings determine if\n"
"activation commands from services or events are used.\n")
arg(setautoactivation_ARG, '\0', "setautoactivation", bool_VAL, 0, 0,
"Set the autoactivation property on a VG or LV.\n"
"Display the property with vgs or lvs \"-o autoactivation\".\n"
@ -920,6 +925,10 @@ arg(vgonline_ARG, '\0', "vgonline", 0, 0, 0,
"The first command to see a complete VG will report it uniquely.\n"
"Other commands to see the complete VG will report it differently.\n")
arg(wait_ARG, '\0', "wait", string_VAL, 0, 0,
"pvsonline: wait for /run/lvm/pvs_online files to exist\n"
"for all devices file entries matched to a system device.\n")
arg(withsummary_ARG, '\0', "withsummary", 0, 0, 0,
"Display a one line comment for each configuration node.\n")

View File

@ -1447,6 +1447,10 @@ lvmdevices --delpvid String
ID: lvmdevices_edit
DESC: Remove the devices file entry for the given PVID.
lvmdevices --wait String
ID: lvmdevices_wait
DESC: Wait for matched PV entries to be online.
---
lvreduce --size NSizeMB LV
@ -1642,14 +1646,15 @@ DESC: Record that a PV is online or offline.
pvscan --cache_long --activate ay
OO: --ignorelockingfailure, --reportformat ReportFmt,
--major Number, --minor Number, --noudevsync
--major Number, --minor Number, --noudevsync, --autoactivation String
OP: PV|String ...
IO: --background
ID: pvscan_cache
DESC: Record that a PV is online and autoactivate the VG if complete.
pvscan --cache_long --listvg PV
OO: --ignorelockingfailure, --checkcomplete, --vgonline, --udevoutput
OO: --ignorelockingfailure, --checkcomplete, --vgonline, --udevoutput,
--autoactivation String
ID: pvscan_cache
DESC: Record that a PV is online and list the VG using the PV.
@ -1747,7 +1752,8 @@ DESC: Start or stop processing LV conversions.
vgchange --activate Active
OO: --activationmode ActivationMode, --ignoreactivationskip, --partial, --sysinit,
--readonly, --ignorelockingfailure, --monitor Bool, --poll Bool, OO_VGCHANGE
--readonly, --ignorelockingfailure, --monitor Bool, --poll Bool,
--vgonline, --autoactivation String, OO_VGCHANGE
OP: VG|Tag|Select ...
IO: --ignoreskippedcluster
ID: vgchange_activate

View File

@ -19,6 +19,7 @@
/* coverity[unnecessary_header] needed for MuslC */
#include <sys/file.h>
#include <time.h>
static void _search_devs_for_pvids(struct cmd_context *cmd, struct dm_list *search_pvids, struct dm_list *found_devs)
{
@ -119,18 +120,38 @@ static void _search_devs_for_pvids(struct cmd_context *cmd, struct dm_list *sear
}
}
static int _all_pvids_online(struct cmd_context *cmd, struct dm_list *wait_pvids)
{
struct device_id_list *dil, *dil2;
int notfound = 0;
dm_list_iterate_items_safe(dil, dil2, wait_pvids) {
if (online_pvid_file_exists(dil->pvid))
dm_list_del(&dil->list);
else
notfound++;
}
return notfound ? 0 : 1;
}
int lvmdevices(struct cmd_context *cmd, int argc, char **argv)
{
struct dm_list search_pvids;
struct dm_list wait_pvids;
struct dm_list found_devs;
struct device_id_list *dil;
struct device_list *devl;
struct device *dev;
struct dev_use *du, *du2;
const char *deviceidtype;
time_t begin;
int wait_pvids_count;
int wait_sec;
int changes = 0;
dm_list_init(&search_pvids);
dm_list_init(&wait_pvids);
dm_list_init(&found_devs);
if (!setup_devices_file(cmd))
@ -141,6 +162,9 @@ int lvmdevices(struct cmd_context *cmd, int argc, char **argv)
return ECMD_FAILED;
}
if (arg_is_set(cmd, wait_ARG))
cmd->print_device_id_not_found = 0;
if (arg_is_set(cmd, update_ARG) ||
arg_is_set(cmd, adddev_ARG) || arg_is_set(cmd, deldev_ARG) ||
arg_is_set(cmd, addpvid_ARG) || arg_is_set(cmd, delpvid_ARG)) {
@ -457,6 +481,74 @@ int lvmdevices(struct cmd_context *cmd, int argc, char **argv)
goto out;
}
if (arg_is_set(cmd, wait_ARG)) {
if (strcmp("pvsonline", arg_str_value(cmd, wait_ARG, ""))) {
log_error("wait option invalid.");
goto bad;
}
/* TODO: lvm.conf lvmdevices_wait_settings "disabled" do nothing */
/* TODO: lvm.conf auto_activation_settings "event_only" do nothing */
/* TODO: if no devices file exists, what should this do?
do a udev-settle? do nothing and cause more event-based activations? */
/* for each du, if du->wwid matched, wait for /run/lvm/pvs_online/du->pvid */
dm_list_iterate_items(du, &cmd->use_devices) {
if (!du->dev)
continue;
if (!(dil = dm_pool_zalloc(cmd->mem, sizeof(*dil))))
continue;
dil->dev = du->dev;
memcpy(dil->pvid, du->pvid, ID_LEN);
dm_list_add(&wait_pvids, &dil->list);
}
wait_pvids_count = dm_list_size(&wait_pvids);
wait_sec = find_config_tree_int(cmd, devices_lvmdevices_wait_seconds_CFG, NULL);
if (wait_sec == -1) {
if (wait_pvids_count <= 20)
wait_sec = 1;
else if (wait_pvids_count <= 100)
wait_sec = 5;
else if (wait_pvids_count <= 500)
wait_sec = 10;
else
wait_sec = 20;
}
log_print("Waiting for PVs online for %u matched devices file entries for %u sec.", wait_pvids_count, wait_sec);
begin = time(NULL);
while (1) {
if (_all_pvids_online(cmd, &wait_pvids)) {
log_print("Found all PVs online");
goto out;
}
log_print("Waiting for PVs online for %u devices.", dm_list_size(&wait_pvids));
/* TODO: lvm.conf lvmdevices_wait_ids "sys_wwid=111", "sys_wwid=222" etc
waits for the specifically named devices even if the devices do not exist. */
if (!wait_sec || (time(NULL) - begin >= wait_sec)) {
log_print("Time out waiting for PVs online:");
dm_list_iterate_items(dil, &wait_pvids)
log_print("Need PVID %s on %s", dil->pvid, dev_name(dil->dev));
break;
}
if (dm_list_size(&wait_pvids) > 10) {
if (interruptible_usleep(1000000)) /* 1 sec */
break;
} else {
if (interruptible_usleep(500000)) /* .5 sec */
break;
}
}
goto out;
}
/* If no options, print use_devices list */
dm_list_iterate_items(du, &cmd->use_devices) {

View File

@ -21,7 +21,19 @@
#include <dirent.h>
int online_pvid_file_read(char *path, int *major, int *minor, char *vgname);
struct aa_settings {
/* from lvm.conf */
int event_activation;
int service_only;
int event_only;
int service_and_event;
int pvscan_hints;
/* from --autoactivation */
int opt_service;
int opt_event;
int opt_event_enable;
};
struct pvscan_params {
int new_pvs_found;
@ -48,6 +60,8 @@ static const char *_pvs_online_dir = DEFAULT_RUN_DIR "/pvs_online";
static const char *_vgs_online_dir = DEFAULT_RUN_DIR "/vgs_online";
static const char *_pvs_lookup_dir = DEFAULT_RUN_DIR "/pvs_lookup";
static const char *_event_activation_file = DEFAULT_RUN_DIR "/event-activation-on";
static int _pvscan_display_pv(struct cmd_context *cmd,
struct physical_volume *pv,
struct pvscan_params *params)
@ -181,6 +195,80 @@ out:
return ret;
}
/*
* Event based activation
* lvm.conf auto_activation_settings = "event_only"
* . all events are used for activation
* . no fixed services are used for activation
* . lvm.conf event_activation=1 required
*
* vgchange -aay --autoactivation service
* . does nothing
* vgchange -aay --autoactivation event
* . does activation
* pvscan --autoactivation event
* . does activation
*
* ---
*
* Non-event based activation
* lvm.conf event_activaion=0 or
* lvm.conf event_activaion=1 and auto_activation_settings = "service_only"
* . fixed services are used for activation
* . no events are used for activation
*
* vgchange -aay --autoactivation service
* . does activation
* vgchange -aay --autoactivation event
* . does nothing
* pvscan --autoactivation event
* . does not trigger activation
* . may still create pvs_online for hints
*
* ---
*
* Mix of event and service based activation
* lvm.conf auto_activation_settings = "service_and_event"
* . both services and events are used for activation
* . fixed services are used for activation initially,
* and the last service enables event based activation
* by creating the event-activation-on file
*
* vgchange -aay --autoactivation service
* . does activation only if event-activation-on does not exist
* vgchange -aay --autoactivation event
* . does activation only if event-activation-on exists
* vgchange -aay --autoactivation service,event_enable
* . does activation only if event-activation-on does not exist
* . creates event-activation-on to enable event-based activation
* pvscan --autoactivation event
* . triggers activation only if event-activation-on exists
*/
int event_activation_enable(struct cmd_context *cmd)
{
FILE *fp;
if (!(fp = fopen(_event_activation_file, "w")))
return_0;
if (fclose(fp))
stack;
return 1;
}
int event_activation_is_on(struct cmd_context *cmd)
{
struct stat buf;
if (!stat(_event_activation_file, &buf))
return 1;
if (errno != ENOENT)
log_debug("event_activation_is_on errno %d", errno);
return 0;
}
/*
* Avoid a duplicate pvscan[%d] prefix when logging to the journal.
* FIXME: this should probably replace if (udevoutput) with
@ -369,7 +457,7 @@ static void _online_files_remove(const char *dirpath)
log_sys_debug("closedir", dirpath);
}
static int _online_pvid_file_create(struct cmd_context *cmd, struct device *dev, const char *vgname)
int online_pvid_file_create(struct cmd_context *cmd, struct device *dev, const char *vgname, int ignore_existing, int *exists)
{
char path[PATH_MAX];
char buf[MAX_PVID_FILE_SIZE] = { 0 };
@ -409,8 +497,13 @@ static int _online_pvid_file_create(struct cmd_context *cmd, struct device *dev,
fd = open(path, O_CREAT | O_EXCL | O_RDWR, S_IRUSR | S_IWUSR);
if (fd < 0) {
if (errno == EEXIST)
if (errno == EEXIST) {
if (exists)
*exists = 1;
if (ignore_existing)
return 1;
goto check_duplicate;
}
log_error_pvscan(cmd, "Failed to create online file for %s path %s error %d", dev_name(dev), path, errno);
return 0;
}
@ -429,7 +522,6 @@ static int _online_pvid_file_create(struct cmd_context *cmd, struct device *dev,
}
/* We don't care about syncing, these files are not even persistent. */
if (close(fd))
log_sys_debug("close", path);
@ -469,7 +561,7 @@ check_duplicate:
return 0;
}
static int _online_pvid_file_exists(const char *pvid)
int online_pvid_file_exists(const char *pvid)
{
char path[PATH_MAX] = { 0 };
struct stat buf;
@ -555,7 +647,7 @@ static void _lookup_file_count_pvid_files(FILE *fp, const char *vgname, int *pvs
continue;
}
if (_online_pvid_file_exists((const char *)pvid))
if (online_pvid_file_exists((const char *)pvid))
(*pvs_online)++;
else
(*pvs_offline)++;
@ -662,7 +754,7 @@ static int _count_pvid_files_from_lookup_file(struct cmd_context *cmd, struct de
return (vgname) ? 1 : 0;
}
static void _online_dir_setup(struct cmd_context *cmd)
void online_dir_setup(struct cmd_context *cmd)
{
struct stat st;
int rv;
@ -727,7 +819,7 @@ static void _count_pvid_files(struct volume_group *vg, int *pvs_online, int *pvs
dm_list_iterate_items(pvl, &vg->pvs) {
memcpy(pvid, &pvl->pv->id.uuid, ID_LEN);
if (_online_pvid_file_exists(pvid))
if (online_pvid_file_exists(pvid))
(*pvs_online)++;
else
(*pvs_offline)++;
@ -750,7 +842,7 @@ static int _pvscan_aa_single(struct cmd_context *cmd, const char *vg_name,
log_debug("pvscan autoactivating VG %s.", vg_name);
if (!vgchange_activate(cmd, vg, CHANGE_AAY)) {
if (!vgchange_activate(cmd, vg, CHANGE_AAY, 1)) {
log_error_pvscan(cmd, "%s: autoactivation failed.", vg->name);
pp->activate_errors++;
}
@ -758,7 +850,7 @@ static int _pvscan_aa_single(struct cmd_context *cmd, const char *vg_name,
return ECMD_PROCESSED;
}
static int _online_vg_file_create(struct cmd_context *cmd, const char *vgname)
int online_vg_file_create(struct cmd_context *cmd, const char *vgname)
{
char path[PATH_MAX];
int fd;
@ -1038,7 +1130,7 @@ static int _pvscan_aa_quick(struct cmd_context *cmd, struct pvscan_aa_params *pp
log_debug("pvscan autoactivating VG %s.", vgname);
if (!vgchange_activate(cmd, vg, CHANGE_AAY)) {
if (!vgchange_activate(cmd, vg, CHANGE_AAY, 1)) {
log_error_pvscan(cmd, "%s: autoactivation failed.", vg->name);
pp->activate_errors++;
}
@ -1072,7 +1164,7 @@ static int _pvscan_aa(struct cmd_context *cmd, struct pvscan_aa_params *pp,
* to run the activation. The first to create the file will do it.
*/
dm_list_iterate_items_safe(sl, sl2, vgnames) {
if (!_online_vg_file_create(cmd, sl->str)) {
if (!online_vg_file_create(cmd, sl->str)) {
log_print_pvscan(cmd, "VG %s skip autoactivation.", sl->str);
str_list_del(vgnames, sl->str);
continue;
@ -1242,7 +1334,7 @@ static void _set_pv_devices_online(struct cmd_context *cmd, struct volume_group
continue;
}
if (!_online_pvid_file_exists(pvid)) {
if (!online_pvid_file_exists(pvid)) {
log_debug("set_pv_devices_online vg %s pv %s no online file",
vg->name, pvid);
pvl->pv->status |= MISSING_PV;
@ -1282,7 +1374,7 @@ static void _set_pv_devices_online(struct cmd_context *cmd, struct volume_group
}
static int _online_devs(struct cmd_context *cmd, int do_all, struct dm_list *pvscan_devs,
int *pv_count, struct dm_list *complete_vgnames)
int *pv_count, struct dm_list *complete_vgnames, struct aa_settings *set)
{
struct device_list *devl, *devl2;
struct device *dev;
@ -1422,7 +1514,7 @@ static int _online_devs(struct cmd_context *cmd, int do_all, struct dm_list *pvs
* Create file named for pvid to record this PV is online.
* The command creates/checks online files only when --cache is used.
*/
if (do_cache && !_online_pvid_file_create(cmd, dev, vg ? vg->name : NULL)) {
if (do_cache && !online_pvid_file_create(cmd, dev, vg ? vg->name : NULL, 0, NULL)) {
log_error_pvscan(cmd, "PV %s failed to create online file.", dev_name(dev));
release_vg(vg);
ret = 0;
@ -1438,6 +1530,20 @@ static int _online_devs(struct cmd_context *cmd, int do_all, struct dm_list *pvs
continue;
}
/*
* A fixed activation service will create event-activation-on
* after which this pvscan will do the steps to trigger
* event based activation. We get to this point because the
* fixed activation service uses pvscan_hints which requires
* this pvscan to create the pvs_online file. The online
* file has now been created so the command is done.
*/
if (set && set->service_and_event && !event_activation_is_on(cmd)) {
log_print_pvscan(cmd, "PV %s online before event activation.", dev_name(dev));
release_vg(vg);
continue;
}
/*
* Check if all the PVs for this VG are online. If the arrival
* of this dev completes the VG, then save the vgname in
@ -1507,7 +1613,7 @@ static int _online_devs(struct cmd_context *cmd, int do_all, struct dm_list *pvs
} else if (!do_check_complete) {
log_print("VG %s", vgname);
} else if (vg_complete) {
if (do_vgonline && !_online_vg_file_create(cmd, vgname)) {
if (do_vgonline && !online_vg_file_create(cmd, vgname)) {
log_print("VG %s finished", vgname);
} else {
/*
@ -1660,13 +1766,14 @@ static int _pvscan_cache_all(struct cmd_context *cmd, int argc, char **argv,
}
dev_iter_destroy(iter);
_online_devs(cmd, 1, &pvscan_devs, &pv_count, complete_vgnames);
_online_devs(cmd, 1, &pvscan_devs, &pv_count, complete_vgnames, NULL);
return 1;
}
static int _pvscan_cache_args(struct cmd_context *cmd, int argc, char **argv,
struct dm_list *complete_vgnames)
struct dm_list *complete_vgnames,
struct aa_settings *settings)
{
struct dm_list pvscan_args; /* struct pvscan_arg */
struct dm_list pvscan_devs; /* struct device_list */
@ -1679,7 +1786,7 @@ static int _pvscan_cache_args(struct cmd_context *cmd, int argc, char **argv,
dm_list_init(&pvscan_args);
dm_list_init(&pvscan_devs);
cmd->pvscan_cache_single = 1;
cmd->expect_missing_vg_device = 1;
/*
* Special pvscan-specific setup steps to avoid looking
@ -1852,7 +1959,7 @@ static int _pvscan_cache_args(struct cmd_context *cmd, int argc, char **argv,
*/
label_scan_devs_cached(cmd, NULL, &pvscan_devs);
ret = _online_devs(cmd, 0, &pvscan_devs, &pv_count, complete_vgnames);
ret = _online_devs(cmd, 0, &pvscan_devs, &pv_count, complete_vgnames, settings);
/*
* When a new PV appears, the system runs pvscan --cache dev.
@ -1869,12 +1976,126 @@ static int _pvscan_cache_args(struct cmd_context *cmd, int argc, char **argv,
return ret;
}
/*
event_activation=1 service_and_event=1 pvscan_hints=1
services, then events
pvscan --cache creates pvs_online for all PVs
lvm-activate-vgs* used first
lvm-activate-<vgname> used later
vgchanges use hints=pvs_online from services and events
event_activation=1 event_only=1 pvscan_hints=1
only event activations
pvscan --cache creates pvs_online for all PVs
lvm-activate-vgs* skipped
lvm-activate-<vgname> used
vgchanges use hints=pvs_online from events
event_activation=1 service_only=1 pvscan_hints=1
only service activations
pvscan --cache creates pvs_online for all PVs
lvm-activate-vgs* used first
lvm-activate-<vgname> skipped
vgchanges use hints=pvs_online from services
(pvscan --cache could be skipped after services finish)
event_activation=1 service_and_event=1 pvscan_hints=0
services, then events
pvscan --cache skipped in service mode
pvscan --cache creates pvs_online in event mode
vgchange when enabling event mode must create pvs_online for existing PVs
lvm-activate-vgs* used first
lvm-activate-<vgname> used later
vgchanges scan all PVs from services and events
event_activation=1 event_only=1 pvscan_hints=0
only event activations
pvscan --cache creates pvs_online for all PVs
lvm-activate-vgs* skipped
lvm-activate-<vgname> used
vgchanges scan all PVs from events
event_activation=1 service_only=1 pvscan_hints=0
only service activations
pvscan --cache always skipped
lvm-activate-vgs* used first
lvm-activate-<vgname> skipped
vgchanges scan all PVs from services
event_activation=0
only service activations
ignores service_and_events=1 or events_only=1
. for pvscan_hints=1
pvscan --cache creates pvs_online for all PVs
lvm-activate-vgs* used first
lvm-activate-<vgname> skipped
vgchanges use hints=pvs_online from services
(pvscan --cache could be skipped after services finish)
. for pvscan_hints=0
pvscan --cache always skipped
lvm-activate-vgs* used first
lvm-activate-<vgname> skipped
vgchanges scan all PVs from services
*/
static int _get_autoactivation(struct cmd_context *cmd,
struct aa_settings *set,
int *skip_command)
{
const char *aa_str;
/*
* The lvm.conf settings only apply when the command uses --autoactivation
* which says if the command is used for event or service activation.
*/
if (!(aa_str = arg_str_value(cmd, autoactivation_ARG, NULL)))
return 1;
if (!get_autoactivation_config_settings(cmd, &set->service_only, &set->event_only, &set->service_and_event, &set->pvscan_hints))
return 0;
if (!get_autoactivation_command_options(cmd, aa_str, &set->opt_service, &set->opt_event, &set->opt_event_enable))
return 0;
if (!set->opt_event) {
log_print_pvscan(cmd, "Skip pvscan without autoactivation=event.");
*skip_command = 1;
return 1;
}
if (!set->event_activation && !set->pvscan_hints) {
log_print_pvscan(cmd, "Skip pvscan with event_activation=0.");
*skip_command = 1;
return 1;
}
if (set->service_only && !set->pvscan_hints) {
log_print_pvscan(cmd, "Skip pvscan with service_only.");
*skip_command = 1;
return 1;
}
if (set->service_and_event && !set->pvscan_hints && !event_activation_is_on(cmd)) {
/*
* Note that when vgchange enables events, it needs to compensate for this
* skipped pvscan by creating pvs_online files for all existing PVs.
*/
log_print_pvscan(cmd, "Skip pvscan without event-activation-on.");
*skip_command = 1;
return 1;
}
return 1;
}
int pvscan_cache_cmd(struct cmd_context *cmd, int argc, char **argv)
{
struct pvscan_aa_params pp = { 0 };
struct dm_list complete_vgnames;
struct aa_settings settings = { 0 };
int do_activate = arg_is_set(cmd, activate_ARG);
int event_activation;
int skip_command = 0;
int devno_args = 0;
int do_all;
int ret;
@ -1888,6 +2109,7 @@ int pvscan_cache_cmd(struct cmd_context *cmd, int argc, char **argv)
cmd->ignore_device_name_mismatch = 1;
event_activation = find_config_tree_bool(cmd, global_event_activation_CFG, NULL);
settings.event_activation = event_activation;
if (do_activate && !event_activation) {
log_verbose("Ignoring pvscan --cache -aay because event_activation is disabled.");
@ -1935,7 +2157,7 @@ int pvscan_cache_cmd(struct cmd_context *cmd, int argc, char **argv)
do_all = !argc && !devno_args;
_online_dir_setup(cmd);
online_dir_setup(cmd);
if (do_all) {
if (!_pvscan_cache_all(cmd, argc, argv, &complete_vgnames))
@ -1946,7 +2168,14 @@ int pvscan_cache_cmd(struct cmd_context *cmd, int argc, char **argv)
log_verbose("Ignoring pvscan --cache because event_activation is disabled.");
return ECMD_PROCESSED;
}
if (!_pvscan_cache_args(cmd, argc, argv, &complete_vgnames))
if (!_get_autoactivation(cmd, &settings, &skip_command))
return_ECMD_FAILED;
if (skip_command)
return ECMD_PROCESSED;
if (!_pvscan_cache_args(cmd, argc, argv, &complete_vgnames, &settings))
return ECMD_FAILED;
}

View File

@ -5714,3 +5714,96 @@ bad:
out:
return 0;
}
int get_autoactivation_config_settings(struct cmd_context *cmd,
int *service_only, int *event_only, int *service_and_event, int *pvscan_hints)
{
const struct dm_config_node *cn;
const struct dm_config_value *cv;
int eo = 0, so = 0, se = 0, ph = 0;
if (!(cn = find_config_tree_array(cmd, activation_auto_activation_settings_CFG, NULL)))
return 1;
for (cv = cn->v; cv; cv = cv->next) {
if (cv->type != DM_CFG_STRING)
continue;
if (!strcmp(cv->v.str, "service_only"))
so = 1;
else if (!strcmp(cv->v.str, "event_only"))
eo = 1;
else if (!strcmp(cv->v.str, "service_and_event"))
se = 1;
else if (!strcmp(cv->v.str, "pvscan_hints"))
ph = 1;
else if (strlen(cv->v.str) > 0)
log_warn("WARNING: ignoring unrecognized autoactivation_settings value %s.", cv->v.str);
}
if (se && (so || eo)) {
log_warn("WARNING: ignoring incompatible autoactivation_settings, using service_and_event.");
*service_and_event = 1;
} else if (so && eo) {
log_warn("WARNING: ignoring incompatible autoactivation_settings, using event_only.");
*event_only = 1;
} else if (se) {
*service_and_event = 1;
} else if (so) {
*service_only = 1;
} else if (eo) {
*event_only = 1;
} else {
*service_and_event = 1;
}
if (ph)
*pvscan_hints = 1;
return 1;
}
static int _aa_option_value(const char *val, int *opt_service, int *opt_event, int *opt_event_enable)
{
if (!strcmp(val, "service"))
*opt_service = 1;
else if (!strcmp(val, "event"))
*opt_event = 1;
else if (!strcmp(val, "event_enable"))
*opt_event_enable = 1;
else {
log_error("Unknown --autoactivation value.");
return 0;
}
return 1;
}
int get_autoactivation_command_options(struct cmd_context *cmd, const char *aa, int *opt_service, int *opt_event, int *opt_event_enable)
{
char vals[128] = { 0 };
char *val1, *val2;
strncpy(vals, aa, 127);
/* Currently only two values can be used at once. */
val1 = vals;
if ((val2 = strchr(vals, ','))) {
*val2 = '\0';
val2++;
}
if (val1 && !_aa_option_value(val1, opt_service, opt_event, opt_event_enable))
return 0;
if (val2 && !_aa_option_value(val2, opt_service, opt_event, opt_event_enable))
return 0;
if (*opt_service && *opt_event) {
log_error("Invalid --autoactivation options, service and event are incompatible.");
return 0;
}
return 1;
}

View File

@ -237,4 +237,9 @@ int lvremove_single(struct cmd_context *cmd, struct logical_volume *lv,
int get_lvt_enum(struct logical_volume *lv);
int get_autoactivation_config_settings(struct cmd_context *cmd, int *service_only,
int *event_only, int *service_and_event, int *pvscan_hints);
int get_autoactivation_command_options(struct cmd_context *cmd, const char *aa,
int *opt_service, int *opt_event, int *opt_event_enable);
#endif

View File

@ -226,9 +226,8 @@ int lvconvert_poll(struct cmd_context *cmd, struct logical_volume *lv, unsigned
int mirror_remove_missing(struct cmd_context *cmd,
struct logical_volume *lv, int force);
int vgchange_activate(struct cmd_context *cmd, struct volume_group *vg,
activation_change_t activate);
activation_change_t activate, int vg_complete_to_activate);
int vgchange_background_polling(struct cmd_context *cmd, struct volume_group *vg);
@ -295,6 +294,13 @@ int lvconvert_cachevol_attach_single(struct cmd_context *cmd,
struct logical_volume *lv,
struct processing_handle *handle);
int online_pvid_file_read(char *path, int *major, int *minor, char *vgname);
int online_pvid_file_create(struct cmd_context *cmd, struct device *dev, const char *vgname, int ignore_existing, int *exists);
void online_vg_file_remove(const char *vgname);
int online_pvid_file_exists(const char *pvid);
int online_vg_file_create(struct cmd_context *cmd, const char *vgname);
void online_dir_setup(struct cmd_context *cmd);
int event_activation_enable(struct cmd_context *cmd);
int event_activation_is_on(struct cmd_context *cmd);
#endif

View File

@ -19,6 +19,7 @@
struct vgchange_params {
int lock_start_count;
unsigned int lock_start_sanlock : 1;
unsigned int vg_complete_to_activate : 1;
};
/*
@ -194,11 +195,47 @@ int vgchange_background_polling(struct cmd_context *cmd, struct volume_group *vg
return 1;
}
static int _online_pvid_file_create_all(struct cmd_context *cmd)
{
struct lvmcache_info *info;
struct dev_iter *iter;
struct device *dev;
const char *vgname;
int exists;
int exist_count = 0;
int create_count = 0;
if (!(iter = dev_iter_create(NULL, 0)))
return 0;
while ((dev = dev_iter_get(cmd, iter))) {
if (dev->pvid[0] &&
(info = lvmcache_info_from_pvid(dev->pvid, dev, 0))) {
vgname = lvmcache_vgname_from_info(info);
if (vgname && !is_orphan_vg(vgname)) {
/*
* Ignore exsting pvid file because a pvscan may be creating
* the same file as the same time we are, which is expected.
*/
exists = 0;
online_pvid_file_create(cmd, dev, vgname, 1, &exists);
if (exists)
exist_count++;
else
create_count++;
}
}
}
dev_iter_destroy(iter);
log_debug("PV online files created %d exist %d", create_count, exist_count);
return 1;
}
int vgchange_activate(struct cmd_context *cmd, struct volume_group *vg,
activation_change_t activate)
activation_change_t activate, int vg_complete_to_activate)
{
int lv_open, active, monitored = 0, r = 1;
const struct lv_list *lvl;
struct pv_list *pvl;
int do_activate = is_change_activating(activate);
/*
@ -219,6 +256,20 @@ int vgchange_activate(struct cmd_context *cmd, struct volume_group *vg,
return 1;
}
if (do_activate && vg_complete_to_activate) {
dm_list_iterate_items(pvl, &vg->pvs) {
if (!pvl->pv->dev) {
log_print("VG %s is incomplete.", vg->name);
return 1;
}
}
}
if (arg_is_set(cmd, vgonline_ARG) && !online_vg_file_create(cmd, vg->name)) {
log_print("VG %s already online", vg->name);
return 1;
}
/*
* Safe, since we never write out new metadata here. Required for
* partial activation to work.
@ -647,6 +698,7 @@ static int _vgchange_single(struct cmd_context *cmd, const char *vg_name,
struct volume_group *vg,
struct processing_handle *handle)
{
struct vgchange_params *vp = (struct vgchange_params *)handle->custom_handle;
int ret = ECMD_PROCESSED;
unsigned i;
activation_change_t activate;
@ -699,9 +751,12 @@ static int _vgchange_single(struct cmd_context *cmd, const char *vg_name,
log_print_unless_silent("Volume group \"%s\" successfully changed", vg->name);
}
if (arg_is_set(cmd, vgonline_ARG))
online_dir_setup(cmd);
if (arg_is_set(cmd, activate_ARG)) {
activate = (activation_change_t) arg_uint_value(cmd, activate_ARG, 0);
if (!vgchange_activate(cmd, vg, activate))
if (!vgchange_activate(cmd, vg, activate, vp->vg_complete_to_activate))
return_ECMD_FAILED;
} else if (arg_is_set(cmd, refresh_ARG)) {
/* refreshes the visible LVs (which starts polling) */
@ -722,8 +777,132 @@ 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, int *enable_events,
int *create_pvs_online)
{
const char *aa;
int service_only = 0, event_only = 0, service_and_event = 0, pvscan_hints = 0;
int opt_service = 0, opt_event = 0, opt_event_enable = 0;
int on_file_exists;
int event_activation;
if (!(aa = arg_str_value(cmd, autoactivation_ARG, NULL))) {
log_error("No autoactivation value.");
return 0;
}
/* lvm.conf auto_activation_settings */
if (!get_autoactivation_config_settings(cmd, &service_only, &event_only, &service_and_event, &pvscan_hints))
return_0;
/* --autoactivation values */
if (!get_autoactivation_command_options(cmd, aa, &opt_service, &opt_event, &opt_event_enable))
return_0;
event_activation = find_config_tree_bool(cmd, global_event_activation_CFG, NULL);
/*
* The combination of lvm.conf event_activation/auto_activation_settings
* and the --autoactivation service|event value determines if this
* command should do anything or be skipped, along with the existence of
* the /run/lvm/event-activation-on file in case of service_and_event.
*/
if (!event_activation) {
if (opt_event) {
log_print("Skip vgchange for event and event_activation=0.");
*skip_command = 1;
return 1;
}
} else {
if (event_only && opt_service) {
log_print("Skip vgchange for service and autoactivation_settings event_only.");
*skip_command = 1;
return 1;
}
if (service_only && opt_event) {
log_print("Skip vgchange for event and autoactivation_settings service_only.");
*skip_command = 1;
return 1;
}
on_file_exists = event_activation_is_on(cmd);
if (service_and_event && opt_service && on_file_exists) {
log_print("Skip vgchange for service and event-activation-on.");
*skip_command = 1;
return 1;
}
if (service_and_event && opt_event && !on_file_exists) {
log_print("Skip vgchange for event and no event-activation-on.");
*skip_command = 1;
return 1;
}
}
/*
* Switch from service activation to event activation when:
* lvm.conf event_activation=1,
* auto_activation_settings=service_and_event,
* and --autoactivation service,event_enable
*
* When enabling event-based activation, first create the
* /run/lvm/event-activation-on file to tell other commands
* to begin responding to PV events and doing activation
* for newly completed VGs.
*
* When no pvscan_hints are used, pvscan --cache has not been
* creating pvs_online files during service mode, so now
* we need to create pvs_online files for all existing PVs
* to make up for that.
*/
if (event_activation && service_and_event && opt_service && opt_event_enable) {
if (!event_activation_enable(cmd))
log_warn("WARNING: Failed to create event-activation-on.");
*enable_events = 1;
if (!pvscan_hints)
*create_pvs_online = 1;
}
/*
* lvm.conf service_and_event, and vgchange -aay --autoactivation service,
* then only activate LVs if the VG is complete.
* A later event will complete the VG and activate it.
*/
if (event_activation && service_and_event && opt_service)
vp->vg_complete_to_activate = 1;
/*
* vgchange -aay --autoactivation service, and lvm.conf
* autoactivation_settings with pvscan_hints means the vgchange
* should use the special hints=pvs_online.
* Otherwise, the vgchange uses the equivalent of --nohints.
* If --nohints is on the vgchange command line, that overrides
* and disables autoactivation_settings pvscan_hints.
* hints=pvs_online means the command will only use devices that
* have been processed by pvscan --cache (which have a pvid file
* created under /run/lvm/pvs_online.)
*/
if (!arg_is_set(cmd, nohints_ARG) && pvscan_hints)
cmd->hints_pvs_online = 1;
else
cmd->use_hints = 0;
/*
* An activation service will just run "vgchange -aay" and activate
* whichever VGs it finds are complete. It's expected that some
* VGs may not yet be complete and will be activated later when
* more PVs appear, so don't print warnings.
*/
if (opt_service)
cmd->expect_missing_vg_device = 1;
return 1;
}
int vgchange(struct cmd_context *cmd, int argc, char **argv)
{
struct vgchange_params vp = { 0 };
struct processing_handle *handle;
uint32_t flags = 0;
int ret;
@ -837,6 +1016,26 @@ int vgchange(struct cmd_context *cmd, int argc, char **argv)
cmd->lockd_vg_enforce_sh = 1;
}
if (arg_is_set(cmd, autoactivation_ARG)) {
int skip_command = 0, enable_events = 0, create_pvs_online = 0;
if (!_check_autoactivation(cmd, &vp, &skip_command, &enable_events, &create_pvs_online))
return ECMD_FAILED;
if (skip_command)
return ECMD_PROCESSED;
if (enable_events) {
if (!event_activation_enable(cmd))
log_warn("WARNING: Failed to create event-activation-on.");
if (create_pvs_online) {
/* The process_each_vg lock_global/lvmcache_label_scan will be skipped. */
if (!lock_global(cmd, "sh"))
return ECMD_FAILED;
lvmcache_label_scan(cmd);
_online_pvid_file_create_all(cmd);
flags |= PROCESS_SKIP_SCAN;
}
}
}
if (update)
flags |= READ_FOR_UPDATE;
else if (arg_is_set(cmd, activate_ARG))
@ -847,6 +1046,8 @@ int vgchange(struct cmd_context *cmd, int argc, char **argv)
return ECMD_FAILED;
}
handle->custom_handle = &vp;
ret = process_each_vg(cmd, argc, argv, NULL, NULL, flags, 0, handle, &_vgchange_single);
destroy_processing_handle(cmd, handle);

View File

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

View File

@ -78,9 +78,12 @@ ENV{SYSTEMD_READY}="1"
#
# TODO: adjust the output of vgchange -aay so that
# it's better suited to appearing in the journal.
#
# "--autoactivation event" used with pvscan or vgchange
# tells the command that it is being run from an event.
IMPORT{program}="(LVM_EXEC)/lvm pvscan --cache --listvg --checkcomplete --vgonline --udevoutput --journal=output $env{DEVNAME}"
ENV{LVM_VG_NAME_COMPLETE}=="?*", RUN+="/usr/bin/systemd-run -r --no-block --property DefaultDependencies=no --unit lvm-activate-$env{LVM_VG_NAME_COMPLETE} (LVM_EXEC)/lvm vgchange -aay --nohints $env{LVM_VG_NAME_COMPLETE}"
IMPORT{program}="(LVM_EXEC)/lvm pvscan --cache --listvg --checkcomplete --vgonline --autoactivation event --udevoutput --journal=output $env{DEVNAME}"
ENV{LVM_VG_NAME_COMPLETE}=="?*", RUN+="/usr/bin/systemd-run -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}"
GOTO="lvm_end"
LABEL="lvm_end"