From f3c1ce4d3547755c6577b2f686f96d769c846114 Mon Sep 17 00:00:00 2001 From: David Teigland Date: Thu, 2 Sep 2021 16:55:04 -0500 Subject: [PATCH] add fixed event activation services Add two new fixed activation services for startup: lvm-activate-vgs-main and lvm-activate-vgs-last. The -main service runs early, without service dependencies. The -last service runs later, after other services have started (other services that may set up devices for PVs, like mulitpathd.) The lvm-activate-vgs services both run "vgchange -aay" to activate all VGs that are preset and complete at the time of system startup. When there are many VGs, these two vgchange commands will replace many per-VG vgchange commands. The last service enables event activation by creating the file /run/lvm/event-activation-on. After that, new devices that appear will be processed by event based autoactivation commands. Prior to the existence of the event-activation-on file, event-based commands do nothing. lvm.conf event_activation_options can be used to configure service-based activation only, or event-based activation only, or the approach described above which begins with service based activation and then switches to event-based. service_only: only the -main and -last services will activate VGs. VGs on devices that appear later will not be autoactivated. event_only: the -main and -last services will not activate any VGs, and all VGs will be activated from per-VG event-based activation commands. service_to_event: the -main and -last services will activate VGs that are complete at the time the services run, and VGs that appear later will be activated from per-VG event-based activation commands. --- configure.ac | 44 +---- lib/config/config_settings.h | 12 ++ lib/config/defaults.h | 3 + lib/label/label.c | 3 + scripts/Makefile.in | 24 +-- ...lk_availability_systemd_red_hat.service.in | 2 +- scripts/lvm-activate-vgs-last.service.in | 22 +++ scripts/lvm-activate-vgs-main.service.in | 19 ++ ...lvm2_monitoring_systemd_red_hat.service.in | 4 +- spec/packages.inc | 18 +- tools/args.h | 5 + tools/command-lines.in | 8 +- tools/pvscan.c | 127 +++++++++++- tools/toollib.c | 87 ++++++++ tools/toollib.h | 5 + tools/tools.h | 6 +- tools/vgchange.c | 186 +++++++++++++++++- udev/69-dm-lvm-metad.rules.in | 2 +- udev/69-dm-lvm.rules.in | 14 +- 19 files changed, 498 insertions(+), 93 deletions(-) create mode 100644 scripts/lvm-activate-vgs-last.service.in create mode 100644 scripts/lvm-activate-vgs-main.service.in diff --git a/configure.ac b/configure.ac index 6cdf1a7e6..a9ad9d545 100644 --- a/configure.ac +++ b/configure.ac @@ -781,39 +781,6 @@ AC_ARG_WITH(default-run-dir, 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) @@ -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,14 @@ libdm/libdevmapper.pc man/Makefile po/Makefile scripts/lvm2-pvscan.service +scripts/lvm-activate-vgs-main.service +scripts/lvm-activate-vgs-last.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..d2a40f234 100644 --- a/lib/config/config_settings.h +++ b/lib/config/config_settings.h @@ -1130,6 +1130,18 @@ cfg(global_event_activation_CFG, "event_activation", global_CFG_SECTION, CFG_DEF "See the --setautoactivation option or the auto_activation_volume_list\n" "setting to configure autoactivation for specific VGs or LVs.\n") +cfg_array(global_event_activation_options_CFG, "event_activation_options", global_CFG_SECTION, CFG_ALLOW_EMPTY | CFG_DEFAULT_COMMENTED, CFG_TYPE_STRING, DEFAULT_EVENT_ACTIVATION_OPTIONS, vsn(2, 3, 14), NULL, 0, NULL, + "Set event activation options.\n" + "service_to_event: begin with fixed activation services,\n" + "then switch to event based activation.\n" + "event_only: only use event based activation.\n" + "service_only: only use fixed activation services.\n" + "(This is equivalent to event_activation=0.)\n" + "Autoactivation commands should set --eventactivation service|event\n" + "to indicate if they are performing service or event activation.\n" + "An autoactivation command may then be skipped according to the\n" + "value of this setting.\n") + cfg(global_use_lvmetad_CFG, "use_lvmetad", global_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_BOOL, 0, vsn(2, 2, 93), 0, vsn(2, 3, 0), NULL, NULL) diff --git a/lib/config/defaults.h b/lib/config/defaults.h index 1b1568a7b..9bf839537 100644 --- a/lib/config/defaults.h +++ b/lib/config/defaults.h @@ -328,4 +328,7 @@ #define DEFAULT_WWIDS_FILE "/etc/multipath/wwids" +#define DEFAULT_EVENT_ACTIVATION_OPTION1 "service_to_event" +#define DEFAULT_EVENT_ACTIVATION_OPTIONS "#S" DEFAULT_EVENT_ACTIVATION_OPTION1 + #endif /* _LVM_DEFAULTS_H */ 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..76eea5a8c --- /dev/null +++ b/scripts/lvm-activate-vgs-last.service.in @@ -0,0 +1,22 @@ +[Unit] +Description=Activate LVM Volume Groups (last) +Documentation=man:vgchange(8) +Wants=systemd-udev-settle.service +After=lvm-activate-vgs-main.service systemd-udev-settle.service multipathd.service cryptsetup.target +Before=local-fs-pre.target shutdown.target +DefaultDependencies=no +Conflicts=shutdown.target + +# "--eventactivation service" tells vgchange it is being called +# from an activation service, so it will do nothing if +# lvm.conf event_activation_options = "event_only". +# "--eventactivation on" tells vgchange to enable event-based +# pvscan activations by creating /run/lvm/event-activation-on. + +[Service] +Type=oneshot +ExecStart=@SBINDIR@/lvm vgchange -aay --nohints --vgonline --eventactivation service,on +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..94aad8426 --- /dev/null +++ b/scripts/lvm-activate-vgs-main.service.in @@ -0,0 +1,19 @@ +[Unit] +Description=Activate LVM Volume Groups +Documentation=man:vgchange(8) +After=dm-event.socket dm-event.service +Before=local-fs-pre.target shutdown.target +DefaultDependencies=no +Conflicts=shutdown.target + +# "--eventactivation service" tells vgchange it is being called +# from an activation service, so it will do nothing if +# lvm.conf event_activation_options = "event_only". + +[Service] +Type=oneshot +ExecStart=@SBINDIR@/lvm vgchange -aay --nohints --vgonline --eventactivation service +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..f7a4fc6f7 100644 --- a/tools/args.h +++ b/tools/args.h @@ -278,6 +278,11 @@ arg(errorwhenfull_ARG, '\0', "errorwhenfull", bool_VAL, 0, 0, "(Also see dm-thin-pool kernel module option no_space_timeout.)\n" "See \\fBlvmthin\\fP(7) for more information.\n") +arg(eventactivation_ARG, '\0', "eventactivation", string_VAL, 0, 0, + "Specify if the command is running autoactivation from an event\n" + "or a fixed service. The lvm.conf event_activation_options setting\n" + "determines if event or service based activation commands are used.\n") + arg(force_long_ARG, '\0', "force", 0, ARG_COUNTABLE, 0, "Force metadata restore even with thin pool LVs.\n" "Use with extreme caution. Most changes to thin metadata\n" diff --git a/tools/command-lines.in b/tools/command-lines.in index 23ea33e6d..6e00f22b4 100644 --- a/tools/command-lines.in +++ b/tools/command-lines.in @@ -1642,14 +1642,15 @@ DESC: Record that a PV is online or offline. pvscan --cache_long --activate ay OO: --ignorelockingfailure, --reportformat ReportFmt, ---major Number, --minor Number, --noudevsync +--major Number, --minor Number, --noudevsync, --eventactivation 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, +--eventactivation String ID: pvscan_cache DESC: Record that a PV is online and list the VG using the PV. @@ -1747,7 +1748,8 @@ DESC: Start or stop processing LV conversions. vgchange --activate Active OO: --activationmode ActivationMode, --ignoreactivationskip, --partial, --sysinit, ---readonly, --ignorelockingfailure, --monitor Bool, --poll Bool, --vgonline, OO_VGCHANGE +--readonly, --ignorelockingfailure, --monitor Bool, --poll Bool, +--vgonline, --eventactivation String, OO_VGCHANGE OP: VG|Tag|Select ... IO: --ignoreskippedcluster ID: vgchange_activate diff --git a/tools/pvscan.c b/tools/pvscan.c index 569f34c31..bb7491eb2 100644 --- a/tools/pvscan.c +++ b/tools/pvscan.c @@ -46,6 +46,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) @@ -179,6 +181,84 @@ out: return ret; } +/* + * 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_to_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 @@ -367,7 +447,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 }; @@ -407,8 +487,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; } @@ -427,7 +512,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); @@ -748,7 +832,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++; } @@ -1036,7 +1120,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++; } @@ -1420,7 +1504,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; @@ -1871,6 +1955,7 @@ int pvscan_cache_cmd(struct cmd_context *cmd, int argc, char **argv) { struct pvscan_aa_params pp = { 0 }; struct dm_list complete_vgnames; + const char *ea; int do_activate = arg_is_set(cmd, activate_ARG); int event_activation; int devno_args = 0; @@ -1944,6 +2029,36 @@ 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 ((ea = arg_str_value(cmd, eventactivation_ARG, NULL))) { + int service_only = 0, event_only = 0, service_to_event = 0; + int ea_service = 0, ea_event = 0, ea_on = 0; + + if (!get_event_activation_config_settings(cmd, &service_only, &event_only, &service_to_event)) + return ECMD_FAILED; + if (!get_event_activation_command_options(cmd, ea, &ea_service, &ea_event, &ea_on)) + return ECMD_FAILED; + + if (ea_event) { + if (!event_activation) { + log_print_pvscan(cmd, "Skip pvscan for event and event_activation=0."); + return ECMD_PROCESSED; + } + if (service_only) { + log_print_pvscan(cmd, "Skip pvscan for event and event_activation_options service_only."); + return ECMD_PROCESSED; + } + if (service_to_event && !event_activation_is_on(cmd)) { + log_print_pvscan(cmd, "Skip pvscan for event and no event-activation-on."); + return ECMD_PROCESSED; + } + } else { + log_error("Option --eventactivation %s is not used by pvscan.", ea); + return ECMD_FAILED; + } + } + if (!_pvscan_cache_args(cmd, argc, argv, &complete_vgnames)) return ECMD_FAILED; } diff --git a/tools/toollib.c b/tools/toollib.c index d6f48aad2..4841d30e5 100644 --- a/tools/toollib.c +++ b/tools/toollib.c @@ -5714,3 +5714,90 @@ bad: out: return 0; } + +int get_event_activation_config_settings(struct cmd_context *cmd, + int *service_only, int *event_only, int *service_to_event) +{ + const struct dm_config_node *cn; + const struct dm_config_value *cv; + int eo = 0, so = 0, se = 0; + + if (!(cn = find_config_tree_array(cmd, global_event_activation_options_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_to_event")) + se = 1; + else if (strlen(cv->v.str) > 0) + log_warn("WARNING: ignoring unrecognized event_activation_options value %s.", cv->v.str); + } + + if (se && (so || eo)) { + log_warn("WARNING: ignoring incompatible event_activation_options, using service_to_event."); + *service_to_event = 1; + } else if (so && eo) { + log_warn("WARNING: ignoring incompatible event_activation_options, using event_only."); + *event_only = 1; + } else if (se) { + *service_to_event = 1; + } else if (so) { + *service_only = 1; + } else if (eo) { + *event_only = 1; + } else { + *service_to_event = 1; + } + return 1; +} + +static int _ea_option_value(const char *val, int *ea_service, int *ea_event, int *ea_on) +{ + if (!strcmp(val, "service")) + *ea_service = 1; + else if (!strcmp(val, "event")) + *ea_event = 1; + else if (!strcmp(val, "on")) + *ea_on = 1; + else { + log_error("Unknown --eventactivation value."); + return 0; + } + return 1; +} + +int get_event_activation_command_options(struct cmd_context *cmd, const char *ea, int *ea_service, int *ea_event, int *ea_on) +{ + char ea_vals[128] = {0}; + char *val1, *val2; + + strncpy(ea_vals, ea, 127); + + /* Currently only two values can be used together. */ + + val1 = ea_vals; + + if ((val2 = strchr(ea_vals, ','))) { + *val2 = '\0'; + val2++; + } + + if (val1 && !_ea_option_value(val1, ea_service, ea_event, ea_on)) + return 0; + + if (val2 && !_ea_option_value(val2, ea_service, ea_event, ea_on)) + return 0; + + if (*ea_service && *ea_event) { + log_error("Invalid --eventactivation options, service and event are incompatible."); + return 0; + } + + return 1; +} + diff --git a/tools/toollib.h b/tools/toollib.h index f3a60fbc4..3f203d309 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_event_activation_config_settings(struct cmd_context *cmd, + int *service_only, int *event_only, int *service_to_event); +int get_event_activation_command_options(struct cmd_context *cmd, + const char *ea, int *ea_service, int *ea_event, int *ea_on); + #endif diff --git a/tools/tools.h b/tools/tools.h index e2661f272..8c5a56893 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,11 @@ int lvconvert_cachevol_attach_single(struct cmd_context *cmd, struct logical_volume *lv, struct processing_handle *handle); +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_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..4d4d083c5 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,113 @@ static int _vgchange_single(struct cmd_context *cmd, const char *vg_name, return ret; } +static int _check_event_activation(struct cmd_context *cmd, struct vgchange_params *vp, int *skip_command, int *enable_events) +{ + const char *ea; + int service_only = 0, event_only = 0, service_to_event = 0; + int ea_service = 0, ea_event = 0, ea_on = 0; + int on_file_exists; + int event_activation; + + if (!(ea = arg_str_value(cmd, eventactivation_ARG, NULL))) { + log_error("No eventactivation value."); + return 0; + } + + /* lvm.conf event_activation_options */ + if (!get_event_activation_config_settings(cmd, &service_only, &event_only, &service_to_event)) + return_0; + + /* --eventactivation values */ + if (!get_event_activation_command_options(cmd, ea, &ea_service, &ea_event, &ea_on)) + return_0; + + event_activation = find_config_tree_bool(cmd, global_event_activation_CFG, NULL); + + /* + * The combination of lvm.conf event_activation/event_activation_options + * and the --eventactivation 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_to_event. + */ + if (!event_activation) { + if (ea_event) { + log_print("Skip vgchange for event and event_activation=0."); + *skip_command = 1; + return 1; + } + } else { + if (event_only && ea_service) { + log_print("Skip vgchange for service and event_activation_options event_only."); + *skip_command = 1; + return 1; + } + if (service_only && ea_event) { + log_print("Skip vgchange for event and event_activation_options service_only."); + *skip_command = 1; + return 1; + } + + on_file_exists = event_activation_is_on(cmd); + + if (service_to_event && ea_service && on_file_exists) { + log_print("Skip vgchange for service and event-activation-on."); + *skip_command = 1; + return 1; + } + if (service_to_event && ea_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, + * event_activation_options=service_to_event, + * and --eventactivation service,on. + * + * 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. It also needs to create online + * files for existing PVs because some VGs may be incomplete + * at this point, and future pvscan commands need to + * find online files for PVs that have already appeared. + * The label scan provides info to know which PVs are + * present and should have pvid online files created. + * + * process_each_vg() usually begins with lock_global() and + * lvmcache_label_scan(), and then processes each VG. + * In this case, lock_global/lvmcache_label_scan are done + * before calling process_each_vg. This allows a special + * step to be inserted between the label scan and processing + * vgs. That step creates the pvid online files, which + * requires label scan info. The lock_global and + * lvmcache_label_scan will be skipped by process_each_vg + * since they are already done here. + */ + if (event_activation && service_to_event && ea_service && ea_on) { + if (!event_activation_enable(cmd)) + log_warn("WARNING: Failed to create event-activation-on."); + *enable_events = 1; + } + + /* + * lvm.conf service_to_event, and vgchange -aay --eventactivation service, + * then only activate LVs if the VG is complete. + * A later event will complete the VG and activate it. + */ + if (event_activation && service_to_event && ea_service) + vp->vg_complete_to_activate = 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; @@ -845,6 +997,24 @@ int vgchange(struct cmd_context *cmd, int argc, char **argv) cmd->lockd_vg_enforce_sh = 1; } + if (arg_is_set(cmd, eventactivation_ARG)) { + int skip_command = 0, enable_events = 0; + if (!_check_event_activation(cmd, &vp, &skip_command, &enable_events)) + 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."); + /* 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 +1025,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..3e2424d06 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 --eventactivation 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 39e5b9807..025b62e7b 100644 --- a/udev/69-dm-lvm.rules.in +++ b/udev/69-dm-lvm.rules.in @@ -78,9 +78,19 @@ ENV{SYSTEMD_READY}="1" # # TODO: adjust the output of vgchange -aay so that # it's better suited to appearing in the journal. +# +# "--eventactivation event" used with pvscan or vgchange +# tells the command that it is being run from an event. +# The command does nothing if lvm.conf event_activation=0. +# The command does nothing if lvm.conf event_activation=1, +# and lvm.conf event_activation_options="service_only". +# The command goes ahead if event_activation_options="event_only", +# or if event_activation_options="service_to_event" and the +# event-activation-on file exists. +# -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 vgchange -aay --nohints $env{LVM_VG_NAME_COMPLETE}" +IMPORT{program}="(LVM_EXEC)/lvm pvscan --cache --listvg --checkcomplete --vgonline --eventactivation 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 vgchange -aay --nohints --eventactivation event $env{LVM_VG_NAME_COMPLETE}" GOTO="lvm_end" LABEL="lvm_end"