summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Teigland <teigland@redhat.com>2021-09-02 16:55:04 -0500
committerDavid Teigland <teigland@redhat.com>2021-10-29 12:06:55 -0500
commit325a4ea67e698d9dcc2319dfeaf64dba77bfa4e2 (patch)
tree6f02dec2b76e864b3b82ea5b19bcd7e09b9bfb46
parentbaf834f9f62fd59ec1aa1ada0894028e2cb10548 (diff)
downloadlvm2-dev-dct-activation-switch-9.tar.gz
add activation servicesdev-dct-activation-switch-9
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. 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 any more 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 enables event activation by creating the /run/lvm/event-activation-on file. Event-based activation will activate any further VGs that appear on the system after service (the udev rule will run vgchange -aay <vgname> via a transient service lvm-activate-<vgname>.service.) 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.ac45
-rw-r--r--lib/config/config_settings.h23
-rw-r--r--lib/config/defaults.h4
-rw-r--r--lib/label/hints.c9
-rw-r--r--lib/label/label.c3
-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.c78
-rw-r--r--tools/pvscan.c265
-rw-r--r--tools/toollib.c93
-rw-r--r--tools/toollib.h5
-rw-r--r--tools/tools.h8
-rw-r--r--tools/vgchange.c198
-rw-r--r--udev/69-dm-lvm-metad.rules.in2
-rw-r--r--udev/69-dm-lvm.rules.in7
22 files changed, 765 insertions, 111 deletions
diff --git a/configure.ac b/configure.ac
index 6cdf1a7e6..45183f422 100644
--- a/configure.ac
+++ b/configure.ac
@@ -782,39 +782,6 @@ AC_DEFINE_UNQUOTED(DEFAULT_RUN_DIR, ["$DEFAULT_RUN_DIR"],
[Default LVM run directory.])
################################################################################
-dnl -- Build cluster mirror log daemon
-AC_MSG_CHECKING(whether to build cluster mirror log daemon)
-AC_ARG_ENABLE(cmirrord,
- AS_HELP_STRING([--enable-cmirrord],
- [enable the cluster mirror log daemon]),
- CMIRRORD=$enableval, CMIRRORD=no)
-AC_MSG_RESULT($CMIRRORD)
-
-BUILD_CMIRRORD=$CMIRRORD
-
-################################################################################
-dnl -- cmirrord pidfile
-if test "$BUILD_CMIRRORD" = yes; then
- AC_ARG_WITH(cmirrord-pidfile,
- AS_HELP_STRING([--with-cmirrord-pidfile=PATH],
- [cmirrord pidfile [PID_DIR/cmirrord.pid]]),
- CMIRRORD_PIDFILE=$withval,
- CMIRRORD_PIDFILE="$DEFAULT_PID_DIR/cmirrord.pid")
- AC_DEFINE_UNQUOTED(CMIRRORD_PIDFILE, ["$CMIRRORD_PIDFILE"],
- [Path to cmirrord pidfile.])
-fi
-
-################################################################################
-dnl -- Look for corosync libraries if required.
-if [[ "$BUILD_CMIRRORD" = yes ]]; then
- pkg_config_init
-
- if test "$HAVE_CPG" != yes; then
- PKG_CHECK_MODULES(CPG, libcpg)
- fi
-fi
-
-################################################################################
dnl -- Enable debugging
AC_MSG_CHECKING(whether to enable debugging)
AC_ARG_ENABLE(debug, AS_HELP_STRING([--enable-debug], [enable debugging]),
@@ -1655,10 +1622,6 @@ fi
AC_MSG_CHECKING(whether to enable editline)
AC_MSG_RESULT($EDITLINE)
-if test "$BUILD_CMIRRORD" = yes; then
- AC_CHECK_FUNCS(atexit,,hard_bailout)
-fi
-
if test "$BUILD_LVMLOCKD" = yes; then
AS_IF([test "$HAVE_REALTIME" != yes], [AC_MSG_ERROR([Realtime clock support is mandatory for lvmlockd.])])
AC_CHECK_FUNCS(strtoull,,hard_bailout)
@@ -1834,7 +1797,6 @@ AC_ARG_VAR([UDEV_LIBS], [linker flags for udev])
################################################################################
AC_SUBST(AWK)
AC_SUBST(BLKID_PC)
-AC_SUBST(BUILD_CMIRRORD)
AC_SUBST(BUILD_DMEVENTD)
AC_SUBST(BUILD_LVMDBUSD)
AC_SUBST(BUILD_LVMPOLLD)
@@ -1967,7 +1929,6 @@ AC_SUBST(WRITE_INSTALL)
AC_SUBST(DMEVENTD_PIDFILE)
AC_SUBST(LVMPOLLD_PIDFILE)
AC_SUBST(LVMLOCKD_PIDFILE)
-AC_SUBST(CMIRRORD_PIDFILE)
AC_SUBST(interface)
AC_SUBST(kerneldir)
AC_SUBST(missingkernel)
@@ -1989,7 +1950,6 @@ Makefile
make.tmpl
libdm/make.tmpl
daemons/Makefile
-daemons/cmirrord/Makefile
daemons/dmeventd/Makefile
daemons/dmeventd/libdevmapper-event.pc
daemons/dmeventd/plugins/Makefile
@@ -2023,14 +1983,15 @@ 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
-scripts/cmirrord_init_red_hat
scripts/com.redhat.lvmdbus1.service
scripts/dm_event_systemd_red_hat.service
scripts/dm_event_systemd_red_hat.socket
-scripts/lvm2_cmirrord_systemd_red_hat.service
scripts/lvm2_lvmdbusd_systemd_red_hat.service
scripts/lvm2_lvmpolld_init_red_hat
scripts/lvm2_lvmpolld_systemd_red_hat.service
diff --git a/lib/config/config_settings.h b/lib/config/config_settings.h
index d280e7adb..57597bccc 100644
--- a/lib/config/config_settings.h
+++ b/lib/config/config_settings.h
@@ -1121,12 +1121,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 +1405,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..bc361f7f6 100644
--- a/lib/config/defaults.h
+++ b/lib/config/defaults.h
@@ -332,4 +332,8 @@
#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
+
#endif /* _LVM_DEFAULTS_H */
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 3cd912270..c5e0e11ba 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/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..8a1faf072
--- /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 cryptsetup.target 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 23ea33e6d..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, --vgonline, 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 8d9634848..e359920fd 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,36 @@ 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 changes = 0;
dm_list_init(&search_pvids);
+ dm_list_init(&wait_pvids);
dm_list_init(&found_devs);
if (!setup_devices_file(cmd))
@@ -141,6 +160,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)) {
@@ -454,6 +476,62 @@ 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);
+ }
+
+ log_print("Waiting for PVs online for %u matched devices file entries.", dm_list_size(&wait_pvids));
+ 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. */
+
+ /* TODO: lvm.conf lvmdevices_wait_settings "timeout=N" */
+ if (time(NULL) - begin >= 10) {
+ 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 d68daa62b..e4f90a759 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 --eventactivation */
+ 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,84 @@ out:
}
/*
+ * Event based activation
+ * lvm.conf event_activation_options = "event_only"
+ * . all events are used for activation
+ * . no fixed services are used for activation
+ * . lvm.conf event_activation=1 required
+ *
+ * vgchange -aay --eventactivation service
+ * . does nothing
+ * vgchange -aay --eventactivation event
+ * . does activation
+ * pvscan --eventactivation event
+ * . does activation
+ *
+ * ---
+ *
+ * Non-event based activation
+ * lvm.conf event_activation_options = "service_only"
+ * . fixed services are used for activation
+ * . no events are used for activation
+ * . lvm.conf event_activation=0 is equivalent to
+ * event_activation=1 event_activation_options="service_only"
+ *
+ * vgchange -aay --eventactivation service
+ * . does activation
+ * vgchange -aay --eventactivation event
+ * . does nothing
+ * pvscan --eventactivation event
+ * . does nothing
+ *
+ * ---
+ *
+ * Mix of event and non-event based activation
+ * lvm.conf event_activation_options = "service_and_event"
+ * . both services and events are used for activation
+ * . fixed services are used for activation initially,
+ * and last service enables event based activation
+ * by creating the event-activation-on file
+ *
+ * vgchange -aay --eventactivation service
+ * . does activation only if event-activation-on does not exist
+ * vgchange -aay --eventactivation event
+ * . does activation only if event-activation-on exists
+ * vgchange -aay --eventactivation service,on
+ * . does activation only if event-activation-on does not exist
+ * . creates event-activation-on to enable event-based activation
+ * vgchange --eventactivation on|off
+ * . create or remove event-activation-on to enable|disable
+ * event-based activation
+ * pvscan --eventactivation event
+ * . does 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 +461,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 +501,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 +526,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 +565,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 +651,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)++;
@@ -727,7 +823,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 +846,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++;
}
@@ -1038,7 +1134,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++;
}
@@ -1242,7 +1338,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 +1378,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 +1518,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 +1535,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
@@ -1660,13 +1770,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 */
@@ -1852,7 +1963,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 +1980,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 +2113,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.");
@@ -1946,7 +2172,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 e2661f272..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,8 +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 985907a02..b6dc5a2fc 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 (arg_is_set(cmd, vgonline_ARG) && !online_vg_file_create(cmd, vg->name)) {
+ log_print("VG %s already online", vg->name);
+ return 1;
+ }
+
+ if (do_activate && vg_complete_to_activate) {
+ dm_list_iterate_items(pvl, &vg->pvs) {
+ if (!pvl->pv->dev) {
+ log_print("VG %s is incomplete.", vg->name);
+ return 1;
+ }
+ }
+ }
+
/*
* Safe, since we never write out new metadata here. Required for
* partial activation to work.
@@ -262,11 +313,6 @@ int vgchange_activate(struct cmd_context *cmd, struct volume_group *vg,
}
}
- if (arg_is_set(cmd, vgonline_ARG) && !online_vg_file_create(cmd, vg->name)) {
- log_print("VG %s finished", vg->name);
- return 1;
- }
-
if (!_activate_lvs_in_vg(cmd, vg, activate)) {
stack;
r = 0;
@@ -652,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;
@@ -709,7 +756,7 @@ static int _vgchange_single(struct cmd_context *cmd, const char *vg_name,
if (arg_is_set(cmd, activate_ARG)) {
activate = (activation_change_t) arg_uint_value(cmd, activate_ARG, 0);
- if (!vgchange_activate(cmd, vg, activate))
+ if (!vgchange_activate(cmd, vg, activate, vp->vg_complete_to_activate))
return_ECMD_FAILED;
} else if (arg_is_set(cmd, refresh_ARG)) {
/* refreshes the visible LVs (which starts polling) */
@@ -730,8 +777,123 @@ 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;
+
+ 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;
@@ -845,6 +1007,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))
@@ -855,6 +1037,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"