summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Teigland <teigland@redhat.com>2021-09-02 15:15:46 -0500
committerDavid Teigland <teigland@redhat.com>2021-11-01 17:49:18 -0500
commit0f9d271bd9d4eb6aba7db522d9ec041e23396e69 (patch)
tree0de601b4a74de86965232073e49064588804d2fb
parentf056bf8f9438c38dfeb81d32121ddf54cc06c3c8 (diff)
downloadlvm2-dev-dct-activation-switch-11.tar.gz
add activation servicesdev-dct-activation-switch-11
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.
-rw-r--r--configure.ac3
-rw-r--r--lib/commands/toolcontext.h2
-rw-r--r--lib/config/config_settings.h29
-rw-r--r--lib/config/defaults.h6
-rw-r--r--lib/device/device_id.c2
-rw-r--r--lib/label/hints.c9
-rw-r--r--lib/label/label.c3
-rw-r--r--lib/metadata/metadata.c4
-rw-r--r--scripts/Makefile.in24
-rw-r--r--scripts/blk_availability_systemd_red_hat.service.in2
-rw-r--r--scripts/lvm-activate-vgs-last.service.in24
-rw-r--r--scripts/lvm-activate-vgs-main.service.in22
-rw-r--r--scripts/lvm-devices-wait.service.in21
-rw-r--r--scripts/lvm2_monitoring_systemd_red_hat.service.in4
-rw-r--r--spec/packages.inc18
-rw-r--r--tools/args.h9
-rw-r--r--tools/command-lines.in12
-rw-r--r--tools/lvmdevices.c92
-rw-r--r--tools/pvscan.c273
-rw-r--r--tools/toollib.c93
-rw-r--r--tools/toollib.h5
-rw-r--r--tools/tools.h10
-rw-r--r--tools/vgchange.c205
-rw-r--r--udev/69-dm-lvm-metad.rules.in2
-rw-r--r--udev/69-dm-lvm.rules.in7
25 files changed, 807 insertions, 74 deletions
diff --git a/configure.ac b/configure.ac
index 6cdf1a7e6..ae61b0cb1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -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
diff --git a/lib/commands/toolcontext.h b/lib/commands/toolcontext.h
index 79da7d87d..5cab998ff 100644
--- a/lib/commands/toolcontext.h
+++ b/lib/commands/toolcontext.h
@@ -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;
diff --git a/lib/config/config_settings.h b/lib/config/config_settings.h
index d280e7adb..0e1593ba5 100644
--- a/lib/config/config_settings.h
+++ b/lib/config/config_settings.h
@@ -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"
diff --git a/lib/config/defaults.h b/lib/config/defaults.h
index 3308b1ea6..794ac2014 100644
--- a/lib/config/defaults.h
+++ b/lib/config/defaults.h
@@ -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 */
diff --git a/lib/device/device_id.c b/lib/device/device_id.c
index 167bf661b..d6c896b4f 100644
--- a/lib/device/device_id.c
+++ b/lib/device/device_id.c
@@ -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;
}
diff --git a/lib/label/hints.c b/lib/label/hints.c
index 4caadf774..67dcbd775 100644
--- a/lib/label/hints.c
+++ b/lib/label/hints.c
@@ -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");
diff --git a/lib/label/label.c b/lib/label/label.c
index b00bc3438..7b79e0b80 100644
--- a/lib/label/label.c
+++ b/lib/label/label.c
@@ -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)
diff --git a/lib/metadata/metadata.c b/lib/metadata/metadata.c
index c4f65366b..f01a0bea6 100644
--- a/lib/metadata/metadata.c
+++ b/lib/metadata/metadata.c
@@ -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);
diff --git a/scripts/Makefile.in b/scripts/Makefile.in
index 0d7f45680..bc567555e 100644
--- a/scripts/Makefile.in
+++ b/scripts/Makefile.in
@@ -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 += \
diff --git a/scripts/blk_availability_systemd_red_hat.service.in b/scripts/blk_availability_systemd_red_hat.service.in
index 82d3b82d0..e42b3ab5f 100644
--- a/scripts/blk_availability_systemd_red_hat.service.in
+++ b/scripts/blk_availability_systemd_red_hat.service.in
@@ -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
diff --git a/scripts/lvm-activate-vgs-last.service.in b/scripts/lvm-activate-vgs-last.service.in
new file mode 100644
index 000000000..1b5bde726
--- /dev/null
+++ b/scripts/lvm-activate-vgs-last.service.in
@@ -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
diff --git a/scripts/lvm-activate-vgs-main.service.in b/scripts/lvm-activate-vgs-main.service.in
new file mode 100644
index 000000000..f95edfa9a
--- /dev/null
+++ b/scripts/lvm-activate-vgs-main.service.in
@@ -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
diff --git a/scripts/lvm-devices-wait.service.in b/scripts/lvm-devices-wait.service.in
new file mode 100644
index 000000000..680f2aa40
--- /dev/null
+++ b/scripts/lvm-devices-wait.service.in
@@ -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
+
diff --git a/scripts/lvm2_monitoring_systemd_red_hat.service.in b/scripts/lvm2_monitoring_systemd_red_hat.service.in
index 4bf744a7f..e912c6a85 100644
--- a/scripts/lvm2_monitoring_systemd_red_hat.service.in
+++ b/scripts/lvm2_monitoring_systemd_red_hat.service.in
@@ -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
diff --git a/spec/packages.inc b/spec/packages.inc
index 74d795520..3d6ef2de9 100644
--- a/spec/packages.inc
+++ b/spec/packages.inc
@@ -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
diff --git a/tools/args.h b/tools/args.h
index 774ce33f4..67d7317b4 100644
--- a/tools/args.h
+++ b/tools/args.h
@@ -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")
diff --git a/tools/command-lines.in b/tools/command-lines.in
index 10b23e75d..b5ef7e65e 100644
--- a/tools/command-lines.in
+++ b/tools/command-lines.in
@@ -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
diff --git a/tools/lvmdevices.c b/tools/lvmdevices.c
index 3f104f7de..f29b9f07c 100644
--- a/tools/lvmdevices.c
+++ b/tools/lvmdevices.c
@@ -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) {
diff --git a/tools/pvscan.c b/tools/pvscan.c
index 01f57af0c..ffe444e75 100644
--- a/tools/pvscan.c
+++ b/tools/pvscan.c
@@ -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)
@@ -182,6 +196,80 @@ out:
}
/*
+ * 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
* if (log_journal & LOG_JOURNAL_OUTPUT)
@@ -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;
@@ -1439,6 +1531,20 @@ static int _online_devs(struct cmd_context *cmd, int do_all, struct dm_list *pvs
}
/*
+ * 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
* complete_vgnames (activation phase will want to know which
@@ -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;
}
diff --git a/tools/toollib.c b/tools/toollib.c
index d6f48aad2..b43b44f2f 100644
--- a/tools/toollib.c
+++ b/tools/toollib.c
@@ -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;
+}
+
diff --git a/tools/toollib.h b/tools/toollib.h
index f3a60fbc4..76806d4c9 100644
--- a/tools/toollib.h
+++ b/tools/toollib.h
@@ -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
diff --git a/tools/tools.h b/tools/tools.h
index bc98fcb09..fb2542fa2 100644
--- a/tools/tools.h
+++ b/tools/tools.h
@@ -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
diff --git a/tools/vgchange.c b/tools/vgchange.c
index e4b57dbd2..0db329a67 100644
--- a/tools/vgchange.c
+++ b/tools/vgchange.c
@@ -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);
diff --git a/udev/69-dm-lvm-metad.rules.in b/udev/69-dm-lvm-metad.rules.in
index 78f506520..eda58b868 100644
--- a/udev/69-dm-lvm-metad.rules.in
+++ b/udev/69-dm-lvm-metad.rules.in
@@ -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"
diff --git a/udev/69-dm-lvm.rules.in b/udev/69-dm-lvm.rules.in
index d6bf1e0e9..8f2d4e2f9 100644
--- a/udev/69-dm-lvm.rules.in
+++ b/udev/69-dm-lvm.rules.in
@@ -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"