1
0
mirror of git://sourceware.org/git/lvm2.git synced 2025-09-17 21:44:24 +03:00

Compare commits

...

3 Commits

Author SHA1 Message Date
David Teigland
9388ca6e90 new udev and systemd autoactivation
udev rule calls pvscan directly which indicates if the
VG can be activated.  If so, the lvm-vgchange service
is run, which runs vgchange -aay.
2021-03-31 14:06:19 -05:00
David Teigland
e719522e29 logging: to the systemd journal
Configure via lvm.conf log/journal or command line --journal.

Possible values:
"command" records command information.
"output" records default command output.
"debug" records full command debugging.

Multiple values can be set in lvm.conf as an array.
One value can be set in --journal which is added to
values set in lvm.conf
2021-03-31 14:06:19 -05:00
David Teigland
27a19e46f4 pvscan: add options listlvs listvg checkcomplete
pvscan --cache <dev>
    . read only dev
    . create online file for dev

pvscan --listvg <dev>
    . read only dev
    . list VG using dev

pvscan --listlvs <dev>
    . read only dev
    . list VG using dev
    . list LVs using dev

pvscan --cache --listvg [--checkcomplete] <dev>
    . read only dev
    . create online file for dev
    . list VG using dev
    . [check online files and report if VG is complete]

pvscan --cache --listlvs [--checkcomplete] <dev>
    . read only dev
    . create online file for dev
    . list VG using dev
    . list LVs using dev
    . [check online files and report if VG is complete]
    . [check online files and report if LVs are complete]

[--vgonline]
can be used with --checkcomplete, to enable use of a vg online
file.  This results in only the first pvscan command to see
the complete VG to report 'VG complete', and others will report
'VG finished'.  This allows the caller to easily run a single
activation of the VG.

[--udevoutput]
can be used with --cache --listvg --checkcomplete, to enable
an output mode that prints LVM_VG_NAME_COMPLETE='vgname' that
a udev rule can import, and prevents other output from the
command (other output causes udev to ignore the command.)

The list of complete LVs is meant to be passed to lvchange -aay,
or the complete VG used with vgchange -aay.

When --checkcomplete is used, lvm assumes that that the output
will be used to trigger event-based autoactivation, so the pvscan
does nothing if event_activation=0 and --checkcomplete is used.

Example of listlvs
------------------

$ lvs -a vg -olvname,devices
  LV     Devices
  lv_a   /dev/loop0(0)
  lv_ab  /dev/loop0(1),/dev/loop1(1)
  lv_abc /dev/loop0(3),/dev/loop1(3),/dev/loop2(1)
  lv_b   /dev/loop1(0)
  lv_c   /dev/loop2(0)

$ pvscan --cache --listlvs --checkcomplete /dev/loop0
  pvscan[35680] PV /dev/loop0 online, VG vg incomplete (need 2).
  VG vg incomplete
  LV vg/lv_a complete
  LV vg/lv_ab incomplete
  LV vg/lv_abc incomplete

$ pvscan --cache --listlvs --checkcomplete /dev/loop1
  pvscan[35681] PV /dev/loop1 online, VG vg incomplete (need 1).
  VG vg incomplete
  LV vg/lv_b complete
  LV vg/lv_ab complete
  LV vg/lv_abc incomplete

$ pvscan --cache --listlvs --checkcomplete /dev/loop2
  pvscan[35682] PV /dev/loop2 online, VG vg is complete.
  VG vg complete
  LV vg/lv_c complete
  LV vg/lv_abc complete

Example of listvg
-----------------

$ pvscan --cache --listvg --checkcomplete /dev/loop0
  pvscan[35684] PV /dev/loop0 online, VG vg incomplete (need 2).
  VG vg incomplete

$ pvscan --cache --listvg --checkcomplete /dev/loop1
  pvscan[35685] PV /dev/loop1 online, VG vg incomplete (need 1).
  VG vg incomplete

$ pvscan --cache --listvg --checkcomplete /dev/loop2
  pvscan[35686] PV /dev/loop2 online, VG vg is complete.
  VG vg complete
2021-03-31 14:06:14 -05:00
16 changed files with 671 additions and 101 deletions

View File

@@ -320,6 +320,33 @@ static int _parse_debug_classes(struct cmd_context *cmd)
return debug_classes;
}
static uint32_t _parse_log_journal(struct cmd_context *cmd, int cfg, const char *cfgname)
{
const struct dm_config_node *cn;
const struct dm_config_value *cv;
uint32_t fields = 0;
uint32_t val;
if (!(cn = find_config_tree_array(cmd, cfg, NULL))) {
log_debug("Unable to find configuration for log/%s.", cfgname);
return 0;
}
for (cv = cn->v; cv; cv = cv->next) {
if (cv->type != DM_CFG_STRING) {
log_verbose("log/%s contains a value which is not a string. Ignoring.", cfgname);
continue;
}
if ((val = log_journal_str_to_val(cv->v.str)))
fields |= val;
else
log_verbose("Unrecognised value for log/%s: %s", cfgname, cv->v.str);
}
return fields;
}
static void _init_logging(struct cmd_context *cmd)
{
int append = 1;
@@ -388,6 +415,9 @@ static void _init_logging(struct cmd_context *cmd)
init_debug_file_fields(_parse_debug_fields(cmd, log_debug_file_fields_CFG, "debug_file_fields"));
init_debug_output_fields(_parse_debug_fields(cmd, log_debug_output_fields_CFG, "debug_output_fields"));
cmd->default_settings.journal = _parse_log_journal(cmd, log_journal_CFG, "journal");
init_log_journal(cmd->default_settings.journal);
t = time(NULL);
ctime_r(&t, &timebuf[0]);
timebuf[24] = '\0';

View File

@@ -29,6 +29,7 @@ struct config_info {
int debug_classes;
int verbose;
int silent;
int suppress;
int test;
int syslog;
int activation;
@@ -40,6 +41,7 @@ struct config_info {
int udev_sync;
int udev_fallback;
int issue_discards;
uint32_t journal;
const char *msg_prefix;
const char *fmt_name;
const char *dmeventd_executable;

View File

@@ -876,6 +876,12 @@ cfg(log_syslog_CFG, "syslog", log_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_SYSLOG,
cfg(log_file_CFG, "file", log_CFG_SECTION, CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, NULL, vsn(1, 0, 0), NULL, 0, NULL,
"Write error and debug log messages to a file specified here.\n")
cfg_array(log_journal_CFG, "journal", log_CFG_SECTION, CFG_ALLOW_EMPTY | CFG_DEFAULT_COMMENTED, CFG_TYPE_STRING, NULL, vsn(2, 3, 12), NULL, 0, NULL,
"Record lvm information in the systemd journal.\n"
"command: record commands that are run.\n"
"output: record default output from commands.\n"
"debug: record debug messages from commands.\n")
cfg(log_overwrite_CFG, "overwrite", log_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_OVERWRITE, vsn(1, 0, 0), NULL, 0, NULL,
"Overwrite the log file each time the program is run.\n")

View File

@@ -25,6 +25,7 @@
#include <syslog.h>
#include <ctype.h>
#include <time.h>
#include <systemd/sd-journal.h>
static FILE *_log_file;
static char _log_file_path[PATH_MAX];
@@ -40,6 +41,7 @@ static char _msg_prefix[30] = " ";
static int _abort_on_internal_errors_config = 0;
static uint32_t _debug_file_fields;
static uint32_t _debug_output_fields;
static uint32_t _log_journal = 0;
static lvm2_log_fn_t _lvm2_log_fn = NULL;
@@ -455,6 +457,11 @@ void init_debug_output_fields(uint32_t debug_fields)
_debug_output_fields = debug_fields;
}
void init_log_journal(uint32_t fields)
{
_log_journal = fields;
}
static void _set_time_prefix(char *prefix, int buflen)
{
@@ -609,6 +616,33 @@ static void _vprint_log(int level, const char *file, int line, int dm_errno_or_c
}
log_it:
if (_log_journal) {
int to_journal = 0;
/* By default the visible command output is _LOG_WARN or less. */
if (_log_journal & LOG_JOURNAL_DEBUG)
to_journal = 1;
if ((_log_journal & LOG_JOURNAL_OUTPUT) && (log_level(level) <= _LOG_WARN))
to_journal = 1;
if (to_journal) {
int prio;
switch (log_level(level)) {
case _LOG_ERR: prio = LOG_ERR; break;
case _LOG_WARN: prio = LOG_WARNING; break;
case _LOG_INFO: prio = LOG_INFO; break;
case _LOG_NOTICE: prio = LOG_NOTICE; break;
case _LOG_DEBUG: prio = LOG_DEBUG; break;
default: prio = LOG_INFO;
}
va_copy(ap, orig_ap);
sd_journal_printv(prio, trformat, ap);
va_end(ap);
}
}
if (!logged_via_report && ((verbose_level() >= level) && !_log_suppress)) {
if (verbose_level() > _LOG_DEBUG) {
memset(buf, 0, sizeof(buf));
@@ -792,3 +826,60 @@ void log_set_report_object_name_and_id(const char *name, const char *id)
_log_report.object_name = name;
_log_report.object_id = id;
}
/*
* TODO: log/journal=["daemon_command"]
* daemon_command: record commands that are run by an lvm daemon.
* (i.e. not commands run directly by a user.)
* For this we need to be able to clearly identify when a command is
* being run by dmeventd/lvmpolld/lvmdbusd.
*
* TODO: log/journal_commmand_names=["lvcreate","lvconvert"]
* This would restrict log/journal=["command"] to the listed command names.
* Also allow "!command" to exclude a command, e.g. ["!pvs"]
*
* TODO: log/journal_daemon_command_names=["lvcreate","lvconvert"]
* This would restrict log/journal=["dameon_command"] to the listed command names.
*
* TODO: log/journal_daemon_names=["dmeventd"]
* This would restrict log/journal=["daemon_command"] to commands run by
* the named daemon.
*
* TODO: log/command_to_file=<path> would write this info to the file.
*
* TODO: log/debug_to_file=<path> would write full debugging to the file.
* (the same effect as log/file=<path> log/level=7)
*/
void log_command(const char *cmd_line, const char *cmd_name, const char *cmd_id)
{
if (_log_journal & LOG_JOURNAL_COMMAND) {
/*
* TODO: DAEMON=dmeventd|lvmpolld|lvmdbusd,
* Could we include caller info such as libblkid, udev rule, etc?
* Does systemd already record the caller for us?
*/
/* The command line, pid, and other things are automatically included. */
sd_journal_send("MESSAGE=lvm command %s", cmd_name,
"MESSAGE_ID=3ca432788c374e4ba684b834188eca36",
"LVM_CMD_NAME=%s", cmd_name,
"LVM_CMD_ID=%s", cmd_id,
"PRIORITY=%i", LOG_INFO,
NULL);
}
}
uint32_t log_journal_str_to_val(const char *str)
{
if (!strcasecmp(str, "command"))
return LOG_JOURNAL_COMMAND;
if (!strcasecmp(str, "output"))
return LOG_JOURNAL_OUTPUT;
if (!strcasecmp(str, "debug"))
return LOG_JOURNAL_DEBUG;
return 0;
}

View File

@@ -63,6 +63,10 @@
#define LOG_DEBUG_FIELD_FILELINE 0x0004
#define LOG_DEBUG_FIELD_MESSAGE 0x0008
#define LOG_JOURNAL_COMMAND 0x0001
#define LOG_JOURNAL_OUTPUT 0x0002
#define LOG_JOURNAL_DEBUG 0x0004
/*
* Classes available for debug log messages.

View File

@@ -62,6 +62,12 @@ void reset_log_duplicated(void);
void init_syslog(int facility);
void fin_syslog(void);
void init_log_journal(uint32_t fields);
uint32_t log_journal_str_to_val(const char *str);
void log_command(const char *cmd_line, const char *cmd_name, const char *cmd_id);
int error_message_produced(void);
void reset_lvm_errno(int store_errmsg);
int stored_errno(void);

View File

@@ -5264,3 +5264,36 @@ struct volume_group *vg_read_for_update(struct cmd_context *cmd, const char *vg_
return vg;
}
int get_visible_lvs_using_pv(struct cmd_context *cmd, struct volume_group *vg, struct device *dev,
struct dm_list *lvs_list)
{
struct pv_list *pvl;
struct lv_list *lvl, *lvl2;
struct physical_volume *pv = NULL;
dm_list_iterate_items(pvl, &vg->pvs) {
if (pvl->pv->dev == dev) {
pv = pvl->pv;
break;
}
}
if (!pv)
return_0;
dm_list_iterate_items(lvl, &vg->lvs) {
if (!lv_is_visible(lvl->lv))
continue;
if (!lv_is_on_pv(lvl->lv, pv))
continue;
if (!(lvl2 = dm_pool_zalloc(cmd->mem, sizeof(*lvl2))))
return_0;
lvl2->lv = lvl->lv;
dm_list_add(lvs_list, &lvl2->list);
}
return 1;
}

View File

@@ -539,4 +539,8 @@ char *tags_format_and_copy(struct dm_pool *mem, const struct dm_list *tagsl);
void set_pv_devices(struct format_instance *fid, struct volume_group *vg);
int get_visible_lvs_using_pv(struct cmd_context *cmd, struct volume_group *vg, struct device *dev,
struct dm_list *lvs_list);
#endif

View File

@@ -0,0 +1,12 @@
[Unit]
Description=LVM event activation of VG %i
Documentation=man:vgchange(8)
DefaultDependencies=no
StartLimitIntervalSec=0
Before=shutdown.target
Conflicts=shutdown.target
[Service]
Type=oneshot
RemainAfterExit=no
ExecStart=@SBINDIR@/lvm vgchange -aay %i

View File

@@ -307,12 +307,33 @@ arg(ignoreunsupported_ARG, '\0', "ignoreunsupported", 0, 0, 0,
arg(importdevices_ARG, '\0', "importdevices", 0, 0, 0,
"Add devices to the devices file.\n")
arg(journal_ARG, '\0', "journal", string_VAL, 0, 0,
"Record information in the systemd journal.\n"
"This information is in addition to information\n"
"enabled by the lvm.conf log/journal setting.\n"
"command: record information about the command.\n"
"output: record the default command output.\n"
"debug: record full command debugging.\n")
arg(labelsector_ARG, '\0', "labelsector", number_VAL, 0, 0,
"By default the PV is labelled with an LVM2 identifier in its second\n"
"sector (sector 1). This lets you use a different sector near the\n"
"start of the disk (between 0 and 3 inclusive - see LABEL_SCAN_SECTORS\n"
"in the source). Use with care.\n")
arg(listlvs_ARG, '\0', "listlvs", 0, 0, 0,
"Print a list of LVs that use the device.\n")
arg(listvg_ARG, '\0', "listvg", 0, 0, 0,
"Print the VG that uses the device.\n")
arg(checkcomplete_ARG, '\0', "checkcomplete", 0, 0, 0,
"Check if all the devices used by a VG or LV are present,\n"
"and print \"complete\" or \"incomplete\" for each listed\n"
"VG or LV. This option is used as a part of event-based\n"
"autoactivation, so pvscan will do nothing if this option\n"
"is set and event_activation=0 in the config settings.\n")
arg(lockopt_ARG, '\0', "lockopt", string_VAL, 0, 0,
"Used to pass options for special cases to lvmlockd.\n"
"See \\fBlvmlockd\\fP(8) for more information.\n")
@@ -795,6 +816,9 @@ arg(type_ARG, '\0', "type", segtype_VAL, 0, 0,
"(e.g. --stripes, --mirrors, --snapshot, --virtualsize, --thin, --cache, --vdo).\n"
"Use inferred types with care because it can lead to unexpected results.\n")
arg(udevoutput_ARG, '\0', "udevoutput", 0, 0, 0,
"Command output is modified to be imported from a udev rule.\n")
arg(unbuffered_ARG, '\0', "unbuffered", 0, 0, 0,
"Produce output immediately without sorting or aligning the columns properly.\n")
@@ -871,6 +895,10 @@ arg(vgmetadatacopies_ARG, '\0', "vgmetadatacopies", vgmetadatacopies_VAL, 0, 0,
"\\fBall\\fP causes LVM to first clear the metadataignore flags on\n"
"all PVs, and then to become unmanaged.\n")
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(withsummary_ARG, '\0', "withsummary", 0, 0, 0,
"Display a one line comment for each configuration node.\n")

View File

@@ -188,7 +188,7 @@
#
OO_ALL: --commandprofile String, --config String, --debug,
--driverloaded Bool, --help, --nolocking, --lockopt String, --longhelp, --profile String, --quiet,
--verbose, --version, --yes, --test, --devicesfile String, --devices PV
--verbose, --version, --yes, --test, --devicesfile String, --devices PV, --journal String
#
# options for pvs, lvs, vgs, fullreport
@@ -1603,11 +1603,37 @@ DESC: Display PV information.
pvscan --cache_long
OO: --ignorelockingfailure, --reportformat ReportFmt,
--activate ay, --major Number, --minor Number, --noudevsync
--major Number, --minor Number, --noudevsync
OP: PV|String ...
IO: --background
ID: pvscan_cache
DESC: Autoactivate a VG when all PVs are online.
DESC: Record that a PV is online or offline.
pvscan --cache_long --activate ay
OO: --ignorelockingfailure, --reportformat ReportFmt,
--major Number, --minor Number, --noudevsync
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
ID: pvscan_cache
DESC: Record that a PV is online and list the VG using the PV.
pvscan --cache_long --listlvs PV
OO: --ignorelockingfailure, --checkcomplete, --vgonline
ID: pvscan_cache
DESC: Record that a PV is online and list LVs using the PV.
pvscan --listlvs PV
ID: pvscan_cache
DESC: List LVs using the PV.
pvscan --listvg PV
ID: pvscan_cache
DESC: List the VG using the PV.
---

View File

@@ -2007,6 +2007,8 @@ out:
log_debug("Recognised command %s (id %d / enum %d).",
commands[best_i].command_id, best_i, commands[best_i].command_enum);
log_command(cmd->cmd_line, commands[best_i].name, commands[best_i].command_id);
return &commands[best_i];
}
@@ -2381,6 +2383,9 @@ static void _reset_current_settings_to_default(struct cmd_context *cmd)
static void _get_current_output_settings_from_args(struct cmd_context *cmd)
{
if (arg_is_set(cmd, udevoutput_ARG))
cmd->current_settings.suppress = 1;
if (arg_is_set(cmd, debug_ARG))
cmd->current_settings.debug = _LOG_FATAL + (arg_count(cmd, debug_ARG) - 1);
@@ -2392,14 +2397,25 @@ static void _get_current_output_settings_from_args(struct cmd_context *cmd)
cmd->current_settings.verbose = 0;
cmd->current_settings.silent = (arg_count(cmd, quiet_ARG) > 1) ? 1 : 0;
}
/*
* default_settings.journal is already set from config and has already been
* applied using init_log_journal().
* current_settings have been set to default_settings.
* now --journal value adds to current_settings.
*/
if (arg_is_set(cmd, journal_ARG))
cmd->current_settings.journal |= log_journal_str_to_val(arg_str_value(cmd, journal_ARG, ""));
}
static void _apply_current_output_settings(struct cmd_context *cmd)
{
log_suppress(cmd->current_settings.suppress);
init_debug(cmd->current_settings.debug);
init_debug_classes_logged(cmd->default_settings.debug_classes);
init_verbose(cmd->current_settings.verbose + VERBOSE_BASE_LEVEL);
init_silent(cmd->current_settings.silent);
init_log_journal(cmd->current_settings.journal);
}
static int _read_devices_list(struct cmd_context *cmd)

View File

@@ -179,6 +179,27 @@ out:
return ret;
}
/*
* 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)
*/
#define log_print_pvscan(cmd, fmt, args...) \
do \
if (arg_is_set(cmd, udevoutput_ARG)) \
log_print(fmt, ##args); \
else \
log_print("pvscan[%d] " fmt, getpid(), ##args); \
while (0)
#define log_error_pvscan(cmd, fmt, args...) \
do \
if (arg_is_set(cmd, udevoutput_ARG)) \
log_error(fmt, ##args); \
else \
log_error("pvscan[%d] " fmt, getpid(), ##args); \
while (0)
static char *_vgname_in_pvid_file_buf(char *buf)
{
char *p, *n;
@@ -259,7 +280,7 @@ static void _lookup_file_remove(char *vgname)
* that the vg will be activated again when it becomes complete.
*/
static void _online_vg_file_remove(const char *vgname)
void online_vg_file_remove(const char *vgname)
{
char path[PATH_MAX];
@@ -314,7 +335,7 @@ static void _online_pvid_file_remove_devno(int major, int minor)
log_sys_debug("unlink", path);
if (file_vgname[0]) {
_online_vg_file_remove(file_vgname);
online_vg_file_remove(file_vgname);
_lookup_file_remove(file_vgname);
}
}
@@ -345,7 +366,7 @@ static void _online_files_remove(const char *dirpath)
log_sys_debug("closedir", dirpath);
}
static int _online_pvid_file_create(struct device *dev, const char *vgname)
static int _online_pvid_file_create(struct cmd_context *cmd, struct device *dev, const char *vgname)
{
char path[PATH_MAX];
char buf[MAX_PVID_FILE_SIZE] = { 0 };
@@ -362,18 +383,18 @@ static int _online_pvid_file_create(struct device *dev, const char *vgname)
minor = (int)MINOR(dev->dev);
if (dm_snprintf(path, sizeof(path), "%s/%s", _pvs_online_dir, dev->pvid) < 0) {
log_error("Path %s/%s is too long.", _pvs_online_dir, dev->pvid);
log_error_pvscan(cmd, "Path %s/%s is too long.", _pvs_online_dir, dev->pvid);
return 0;
}
if ((len1 = dm_snprintf(buf, sizeof(buf), "%d:%d\n", major, minor)) < 0) {
log_error("Cannot create online file path for %s %d:%d.", dev_name(dev), major, minor);
log_error_pvscan(cmd, "Cannot create online file path for %s %d:%d.", dev_name(dev), major, minor);
return 0;
}
if (vgname) {
if ((len2 = dm_snprintf(buf + len1, sizeof(buf) - len1, "vg:%s\n", vgname)) < 0) {
log_warn("Incomplete online file for %s %d:%d vg %s.", dev_name(dev), major, minor, vgname);
log_print_pvscan(cmd, "Incomplete online file for %s %d:%d vg %s.", dev_name(dev), major, minor, vgname);
/* can still continue without vgname */
len2 = 0;
}
@@ -387,7 +408,7 @@ static int _online_pvid_file_create(struct device *dev, const char *vgname)
if (fd < 0) {
if (errno == EEXIST)
goto check_duplicate;
log_error("Failed to create online file for %s path %s error %d", dev_name(dev), path, errno);
log_error_pvscan(cmd, "Failed to create online file for %s path %s error %d", dev_name(dev), path, errno);
return 0;
}
@@ -435,12 +456,12 @@ check_duplicate:
/* Don't know how vgname might not match, but it's not good so fail. */
if ((file_major != major) || (file_minor != minor))
log_error("pvscan[%d] PV %s is duplicate for PVID %s on %d:%d and %d:%d.",
getpid(), dev_name(dev), dev->pvid, major, minor, file_major, file_minor);
log_error_pvscan(cmd, "PV %s is duplicate for PVID %s on %d:%d and %d:%d.",
dev_name(dev), dev->pvid, major, minor, file_major, file_minor);
if (file_vgname[0] && vgname && strcmp(file_vgname, vgname))
log_error("pvscan[%d] PV %s has unexpected VG %s vs %s.",
getpid(), dev_name(dev), vgname, file_vgname);
log_error_pvscan(cmd, "PV %s has unexpected VG %s vs %s.",
dev_name(dev), vgname, file_vgname);
return 0;
}
@@ -475,7 +496,7 @@ static int _write_lookup_file(struct cmd_context *cmd, struct volume_group *vg)
int fd;
if (dm_snprintf(path, sizeof(path), "%s/%s", _pvs_lookup_dir, vg->name) < 0) {
log_error("Path %s/%s is too long.", _pvs_lookup_dir, vg->name);
log_error_pvscan(cmd, "Path %s/%s is too long.", _pvs_lookup_dir, vg->name);
return 0;
}
@@ -638,7 +659,7 @@ static int _count_pvid_files_from_lookup_file(struct cmd_context *cmd, struct de
return (vgname) ? 1 : 0;
}
static void _online_dir_setup(void)
static void _online_dir_setup(struct cmd_context *cmd)
{
struct stat st;
int rv;
@@ -652,7 +673,7 @@ static void _online_dir_setup(void)
dm_prepare_selinux_context(NULL, 0);
if ((rv < 0) && stat(DEFAULT_RUN_DIR, &st))
log_error("Failed to create %s %d", DEFAULT_RUN_DIR, errno);
log_error_pvscan(cmd, "Failed to create %s %d", DEFAULT_RUN_DIR, errno);
do_pvs:
if (!stat(_pvs_online_dir, &st))
@@ -664,7 +685,7 @@ do_pvs:
dm_prepare_selinux_context(NULL, 0);
if ((rv < 0) && stat(_pvs_online_dir, &st))
log_error("Failed to create %s %d", _pvs_online_dir, errno);
log_error_pvscan(cmd, "Failed to create %s %d", _pvs_online_dir, errno);
do_vgs:
if (!stat(_vgs_online_dir, &st))
@@ -676,7 +697,7 @@ do_vgs:
dm_prepare_selinux_context(NULL, 0);
if ((rv < 0) && stat(_vgs_online_dir, &st))
log_error("Failed to create %s %d", _vgs_online_dir, errno);
log_error_pvscan(cmd, "Failed to create %s %d", _vgs_online_dir, errno);
do_lookup:
if (!stat(_pvs_lookup_dir, &st))
@@ -688,7 +709,7 @@ do_lookup:
dm_prepare_selinux_context(NULL, 0);
if ((rv < 0) && stat(_pvs_lookup_dir, &st))
log_error("Failed to create %s %d", _pvs_lookup_dir, errno);
log_error_pvscan(cmd, "Failed to create %s %d", _pvs_lookup_dir, errno);
}
@@ -725,7 +746,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)) {
log_error("%s: autoactivation failed.", vg->name);
log_error_pvscan(cmd, "%s: autoactivation failed.", vg->name);
pp->activate_errors++;
}
@@ -738,7 +759,7 @@ static int _online_vg_file_create(struct cmd_context *cmd, const char *vgname)
int fd;
if (dm_snprintf(path, sizeof(path), "%s/%s", _vgs_online_dir, vgname) < 0) {
log_error("Path %s/%s is too long.", _vgs_online_dir, vgname);
log_error_pvscan(cmd, "Path %s/%s is too long.", _vgs_online_dir, vgname);
return 0;
}
@@ -826,15 +847,15 @@ static int _get_devs_from_saved_vg(struct cmd_context *cmd, const char *vgname,
_online_pvid_file_read(path, &file_major, &file_minor, file_vgname);
if (file_vgname[0] && strcmp(vgname, file_vgname)) {
log_error("Wrong VG found for %d:%d PVID %s: %s vs %s",
file_major, file_minor, pvid, vgname, file_vgname);
log_error_pvscan(cmd, "Wrong VG found for %d:%d PVID %s: %s vs %s",
file_major, file_minor, pvid, vgname, file_vgname);
goto bad;
}
devno = MKDEV(file_major, file_minor);
if (!(dev = dev_cache_get_by_devt(cmd, devno, NULL, NULL))) {
log_error("No device found for %d:%d PVID %s", file_major, file_minor, pvid);
log_error_pvscan(cmd, "No device found for %d:%d PVID %s", file_major, file_minor, pvid);
goto bad;
}
@@ -844,7 +865,7 @@ static int _get_devs_from_saved_vg(struct cmd_context *cmd, const char *vgname,
if (strcmp(name1, name2)) {
if (!id_write_format((const struct id *)pvid, uuidstr, sizeof(uuidstr)))
uuidstr[0] = '\0';
log_print("PVID %s read from %s last written to %s.", uuidstr, name1, name2);
log_print_pvscan(cmd, "PVID %s read from %s last written to %s.", uuidstr, name1, name2);
goto bad;
}
@@ -921,7 +942,7 @@ static int _pvscan_aa_quick(struct cmd_context *cmd, struct pvscan_aa_params *pp
* The dev_cache gives us struct devices from the devnums.
*/
if (!_get_devs_from_saved_vg(cmd, vgname, &devs)) {
log_print("pvscan[%d] VG %s not using quick activation.", getpid(), vgname);
log_print_pvscan(cmd, "VG %s not using quick activation.", vgname);
*no_quick = 1;
return ECMD_FAILED;
}
@@ -932,7 +953,7 @@ static int _pvscan_aa_quick(struct cmd_context *cmd, struct pvscan_aa_params *pp
* label rescan are then disabled in vg_read.)
*/
if (!lock_vol(cmd, vgname, LCK_VG_WRITE, NULL)) {
log_error("pvscan activation for VG %s failed to lock VG.", vgname);
log_error_pvscan(cmd, "activation for VG %s failed to lock VG.", vgname);
return ECMD_FAILED;
}
@@ -945,7 +966,7 @@ static int _pvscan_aa_quick(struct cmd_context *cmd, struct pvscan_aa_params *pp
label_scan_devs(cmd, NULL, &devs);
if (!(vgid = lvmcache_vgid_from_vgname(cmd, vgname))) {
log_error("pvscan activation for VG %s failed to find vgid.", vgname);
log_error_pvscan(cmd, "activation for VG %s failed to find vgid.", vgname);
return ECMD_FAILED;
}
@@ -965,7 +986,7 @@ static int _pvscan_aa_quick(struct cmd_context *cmd, struct pvscan_aa_params *pp
* original device arg scan. There will be very few and unusual
* cases that would be caught here.
*/
log_error("pvscan activation for VG %s cannot read (%x).", vgname, error_flags);
log_error_pvscan(cmd, "activation for VG %s cannot read (%x).", vgname, error_flags);
return ECMD_FAILED;
}
@@ -987,7 +1008,7 @@ static int _pvscan_aa_quick(struct cmd_context *cmd, struct pvscan_aa_params *pp
dm_list_iterate_items(pvl, &vg->pvs) {
if (dev_in_device_list(pvl->pv->dev, &devs))
continue;
log_error("pvscan activation for VG %s found different devices.", vgname);
log_error_pvscan(cmd, "activation for VG %s found different devices.", vgname);
ret = ECMD_FAILED;
goto out;
}
@@ -995,7 +1016,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)) {
log_error("%s: autoactivation failed.", vg->name);
log_error_pvscan(cmd, "%s: autoactivation failed.", vg->name);
pp->activate_errors++;
}
@@ -1014,7 +1035,7 @@ static int _pvscan_aa(struct cmd_context *cmd, struct pvscan_aa_params *pp,
int ret = ECMD_FAILED;
if (!(handle = init_processing_handle(cmd, NULL))) {
log_error("Failed to initialize processing handle.");
log_error_pvscan(cmd, "Failed to initialize processing handle.");
goto out;
}
@@ -1029,11 +1050,11 @@ static int _pvscan_aa(struct cmd_context *cmd, struct pvscan_aa_params *pp,
*/
dm_list_iterate_items_safe(sl, sl2, vgnames) {
if (!_online_vg_file_create(cmd, sl->str)) {
log_print("pvscan[%d] VG %s skip autoactivation.", getpid(), sl->str);
log_print_pvscan(cmd, "VG %s skip autoactivation.", sl->str);
str_list_del(vgnames, sl->str);
continue;
}
log_print("pvscan[%d] VG %s run autoactivation.", getpid(), sl->str);
log_print_pvscan(cmd, "VG %s run autoactivation.", sl->str);
}
if (dm_list_empty(vgnames)) {
@@ -1165,6 +1186,64 @@ static int _get_args_devs(struct cmd_context *cmd, struct dm_list *pvscan_args,
return 1;
}
static void _set_pv_devices_online(struct cmd_context *cmd, struct volume_group *vg)
{
char path[PATH_MAX];
char file_vgname[NAME_LEN];
char pvid[ID_LEN+1] = { 0 };
struct pv_list *pvl;
struct device *dev;
int major, minor;
dev_t devno;
dm_list_iterate_items(pvl, &vg->pvs) {
memcpy(&pvid, &pvl->pv->id.uuid, ID_LEN);
if (pvl->pv->status & MISSING_PV) {
log_debug("set_pv_devices_online vg %s pv %s missing flag already set",
vg->name, pvid);
continue;
}
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;
continue;
}
memset(path, 0, sizeof(path));
snprintf(path, sizeof(path), "%s/%s", _pvs_online_dir, pvid);
major = 0;
minor = 0;
file_vgname[0] = '\0';
_online_pvid_file_read(path, &major, &minor, file_vgname);
if (file_vgname[0] && strcmp(vg->name, file_vgname)) {
log_warn("WARNING: VG %s PV %s wrong vgname in online file %s",
vg->name, pvid, file_vgname);
pvl->pv->status |= MISSING_PV;
continue;
}
devno = MKDEV(major, minor);
if (!(dev = dev_cache_get_by_devt(cmd, devno, NULL, NULL))) {
log_print_pvscan(cmd, "VG %s PV %s no device found for %d:%d",
vg->name, pvid, major, minor);
pvl->pv->status |= MISSING_PV;
continue;
}
log_debug("set_pv_devices_online vg %s pv %s is online %s",
vg->name, pvid, dev_name(dev));
pvl->pv->dev = dev;
}
}
static int _online_devs(struct cmd_context *cmd, int do_all, struct dm_list *pvscan_devs,
int *pv_count, struct dm_list *complete_vgnames)
{
@@ -1177,15 +1256,20 @@ static int _online_devs(struct cmd_context *cmd, int do_all, struct dm_list *pvs
struct metadata_area *mda1, *mda2;
struct volume_group *vg;
struct physical_volume *pv;
const char *vgname;
uint32_t ext_version, ext_flags;
const char *vgname = NULL;
uint64_t devsize;
uint32_t ext_version, ext_flags;
int do_cache = arg_is_set(cmd, cache_long_ARG);
int do_activate = arg_is_set(cmd, activate_ARG);
int do_full_check;
int do_list_lvs = arg_is_set(cmd, listlvs_ARG);
int do_list_vg = arg_is_set(cmd, listvg_ARG);
int do_check_complete = arg_is_set(cmd, checkcomplete_ARG);
int do_vgonline = arg_is_set(cmd, vgonline_ARG);
int pvs_online;
int pvs_offline;
int pvs_unknown;
int vg_complete;
int do_full_check;
int ret = 1;
dm_list_iterate_items_safe(devl, devl2, pvscan_devs) {
@@ -1199,20 +1283,20 @@ static int _online_devs(struct cmd_context *cmd, int do_all, struct dm_list *pvs
* using udev and the native version didn't catch it.
*/
if (udev_dev_is_mpath_component(dev)) {
log_print("pvscan[%d] ignore multipath component %s.", getpid(), dev_name(dev));
log_print_pvscan(cmd, "ignore multipath component %s.", dev_name(dev));
continue;
}
if (!(info = lvmcache_info_from_pvid(dev->pvid, dev, 0))) {
if (!do_all)
log_print("pvscan[%d] ignore %s with no lvm info.", getpid(), dev_name(dev));
log_print_pvscan(cmd, "ignore %s with no lvm info.", dev_name(dev));
continue;
}
ext_version = lvmcache_ext_version(info);
ext_flags = lvmcache_ext_flags(info);
if ((ext_version >= 2) && !(ext_flags & PV_EXT_USED)) {
log_print("pvscan[%d] PV %s not used.", getpid(), dev_name(dev));
log_print_pvscan(cmd, "PV %s not used.", dev_name(dev));
(*pv_count)++;
continue;
}
@@ -1231,7 +1315,7 @@ static int _online_devs(struct cmd_context *cmd, int do_all, struct dm_list *pvs
vg = mda2->ops->vg_read(cmd, fid, "", mda2, NULL, NULL);
if (!vg) {
log_print("pvscan[%d] PV %s has no VG metadata.", getpid(), dev_name(dev));
log_print_pvscan(cmd, "PV %s has no VG metadata.", dev_name(dev));
fmt->ops->destroy_instance(fid);
goto online;
}
@@ -1239,7 +1323,7 @@ static int _online_devs(struct cmd_context *cmd, int do_all, struct dm_list *pvs
set_pv_devices(fid, vg);
if (!(pv = find_pv(vg, dev))) {
log_print("pvscan[%d] PV %s not found in VG %s.", getpid(), dev_name(dev), vg->name);
log_print_pvscan(cmd, "PV %s not found in VG %s.", dev_name(dev), vg->name);
release_vg(vg);
continue;
}
@@ -1257,13 +1341,13 @@ static int _online_devs(struct cmd_context *cmd, int do_all, struct dm_list *pvs
do_full_check = 1;
}
if (do_full_check && dev_is_md_component(dev, NULL, 1)) {
log_print("pvscan[%d] ignore md component %s.", getpid(), dev_name(dev));
log_print_pvscan(cmd, "ignore md component %s.", dev_name(dev));
release_vg(vg);
continue;
}
if (vg_is_shared(vg)) {
log_print("pvscan[%d] PV %s ignore shared VG.", getpid(), dev_name(dev));
log_print_pvscan(cmd, "PV %s ignore shared VG.", dev_name(dev));
release_vg(vg);
continue;
}
@@ -1273,7 +1357,13 @@ static int _online_devs(struct cmd_context *cmd, int do_all, struct dm_list *pvs
vg_is_foreign(vg)) {
log_verbose("Ignore PV %s with VG system id %s with our system id %s",
dev_name(dev), vg->system_id, cmd->system_id);
log_print("pvscan[%d] PV %s ignore foreign VG.", getpid(), dev_name(dev));
log_print_pvscan(cmd, "PV %s ignore foreign VG.", dev_name(dev));
release_vg(vg);
continue;
}
if (vg_is_exported(vg)) {
log_print_pvscan(cmd, "PV %s ignore exported VG.", dev_name(dev));
release_vg(vg);
continue;
}
@@ -1292,19 +1382,20 @@ 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 (!_online_pvid_file_create(dev, vg ? vg->name : NULL)) {
log_error("pvscan[%d] PV %s failed to create online file.", getpid(), dev_name(dev));
if (do_cache && !_online_pvid_file_create(cmd, dev, vg ? vg->name : NULL)) {
log_error_pvscan(cmd, "PV %s failed to create online file.", dev_name(dev));
release_vg(vg);
ret = 0;
continue;
}
/*
* When not activating we don't need to know about vg completeness.
* A plain pvscan --cache <dev> just creates the online file.
*/
if (!do_activate) {
log_print("pvscan[%d] PV %s online.", getpid(), dev_name(dev));
if (!do_activate && !do_list_lvs && !do_list_vg) {
log_print_pvscan(cmd, "PV %s online.", dev_name(dev));
release_vg(vg);
continue;
}
@@ -1312,58 +1403,159 @@ static int _online_devs(struct cmd_context *cmd, int do_all, struct dm_list *pvs
/*
* 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 so it will be activated.
* complete_vgnames (activation phase will want to know which
* VGs to activate.)
*/
pvs_online = 0;
pvs_offline = 0;
pvs_unknown = 0;
vg_complete = 0;
if (do_activate || do_check_complete) {
pvs_online = 0;
pvs_offline = 0;
pvs_unknown = 0;
vg_complete = 0;
if (vg) {
/*
* Use the VG metadata from this PV for a list of all
* PVIDs. Write a lookup file of PVIDs in case another
* pvscan needs it. After writing lookup file, recheck
* pvid files to resolve a possible race with another
* pvscan reading the lookup file that missed it.
*/
log_debug("checking all pvid files from vg %s", vg->name);
_count_pvid_files(vg, &pvs_online, &pvs_offline);
if (pvs_offline && _write_lookup_file(cmd, vg)) {
log_debug("rechecking all pvid files from vg %s", vg->name);
if (vg) {
/*
* Use the VG metadata from this PV for a list of all
* PVIDs. Write a lookup file of PVIDs in case another
* pvscan needs it. After writing lookup file, recheck
* pvid files to resolve a possible race with another
* pvscan reading the lookup file that missed it.
*/
log_debug("checking all pvid files from vg %s", vg->name);
_count_pvid_files(vg, &pvs_online, &pvs_offline);
if (!pvs_offline)
log_print("pvscan[%d] VG %s complete after recheck.", getpid(), vg->name);
if (pvs_offline && _write_lookup_file(cmd, vg)) {
log_debug("rechecking all pvid files from vg %s", vg->name);
_count_pvid_files(vg, &pvs_online, &pvs_offline);
if (!pvs_offline)
log_print_pvscan(cmd, "VG %s complete after recheck.", vg->name);
}
vgname = vg->name;
} else {
/*
* No VG metadata on this PV, so try to use a lookup
* file written by a prior pvscan for a list of all
* PVIDs. A lookup file may not exist for this PV if
* it's the first to appear from the VG.
*/
log_debug("checking all pvid files from lookup file");
if (!_count_pvid_files_from_lookup_file(cmd, dev, &pvs_online, &pvs_offline, &vgname))
pvs_unknown = 1;
}
if (pvs_unknown) {
log_print_pvscan(cmd, "PV %s online, VG unknown.", dev_name(dev));
vg_complete = 0;
} else if (pvs_offline) {
log_print_pvscan(cmd, "PV %s online, VG %s incomplete (need %d).",
dev_name(dev), vgname, pvs_offline);
vg_complete = 0;
} else {
log_print_pvscan(cmd, "PV %s online, VG %s is complete.", dev_name(dev), vgname);
if (!str_list_add(cmd->mem, complete_vgnames, dm_pool_strdup(cmd->mem, vgname)))
stack;
vg_complete = 1;
}
}
if (!vgname && vg)
vgname = vg->name;
if (do_list_vg || do_list_lvs) {
if (!vgname) {
log_print("VG unknown");
} else if (!do_check_complete) {
log_print("VG %s", vgname);
} else if (vg_complete) {
if (do_vgonline && !_online_vg_file_create(cmd, vgname)) {
log_print("VG %s finished", vgname);
} else {
/*
* A udev rule imports KEY=val from a program's stdout.
* Other output causes udev to ignore everything.
* Run pvscan from udev rule using --udevoutput to
* enable this printf, and suppress all log output
*/
if (arg_is_set(cmd, udevoutput_ARG))
printf("LVM_VG_NAME_COMPLETE='%s'\n", vgname);
else
log_print("VG %s complete", vgname);
}
} else {
if (arg_is_set(cmd, udevoutput_ARG))
printf("LVM_VG_NAME_INCOMPLETE='%s'\n", vgname);
else
log_print("VG %s incomplete", vgname);
}
vgname = vg->name;
} else {
/*
* No VG metadata on this PV, so try to use a lookup
* file written by a prior pvscan for a list of all
* PVIDs. A lookup file may not exist for this PV if
* it's the first to appear from the VG.
* When the VG is complete|finished, we could print
* a list of devices in the VG, by reading the pvid files
* that were counted, which provides major:minor of each
* device and using that to get the struct dev and dev_name.
* The user could pass this list of devices to --devices
* to optimize a subsequent command (activation) on the VG.
* Just call set_pv_devices_online (if not done othewise)
* since that finds the devs.
*/
log_debug("checking all pvid files from lookup file");
if (!_count_pvid_files_from_lookup_file(cmd, dev, &pvs_online, &pvs_offline, &vgname))
pvs_unknown = 1;
}
if (pvs_unknown) {
log_print("pvscan[%d] PV %s online, VG unknown.",
getpid(), dev_name(dev));
} else if (pvs_offline) {
log_print("pvscan[%d] PV %s online, VG %s incomplete (need %d).",
getpid(), dev_name(dev), vgname, pvs_offline);
} else {
log_print("pvscan[%d] PV %s online, VG %s is complete.", getpid(), dev_name(dev), vgname);
if (!str_list_add(cmd->mem, complete_vgnames, dm_pool_strdup(cmd->mem, vgname)))
stack;
vg_complete = 1;
if (do_list_lvs && !vg) {
/* require all PVs used for booting have metadata */
log_print_pvscan(cmd, "Cannot list LVs from device without metadata.");
}
if (!saved_vg && vg && vg_complete && !do_all && (dm_list_size(pvscan_devs) == 1))
if (do_list_lvs && vg) {
struct dm_list lvs_list;
struct lv_list *lvl;
dm_list_init(&lvs_list);
/*
* For each vg->pvs entry, get the dev based on the online file
* for the pvid and set pv->dev or pv->status MISSING_PV.
*/
_set_pv_devices_online(cmd, vg);
/*
* lvs_list are LVs that use dev.
*/
if (!get_visible_lvs_using_pv(cmd, vg, dev, &lvs_list))
log_print_pvscan(cmd, "Failed to find LVs using %s.", dev_name(dev));
if (!do_check_complete) {
dm_list_iterate_items(lvl, &lvs_list)
log_print("LV %s", display_lvname(lvl->lv));
} else if (vg_complete) {
/*
* A shortcut; the vg complete implies all lvs are complete.
*/
dm_list_iterate_items(lvl, &lvs_list)
log_print("LV %s complete", display_lvname(lvl->lv));
} else {
/*
* For each LV in VG, check if all devs are present.
* Sets the PARTIAL flag on LVs that are not complete.
*/
if (!vg_mark_partial_lvs(vg, 1))
log_print_pvscan(cmd, "Failed to check partial lvs.");
dm_list_iterate_items(lvl, &lvs_list) {
if (!lv_is_partial(lvl->lv))
log_print("LV %s complete", display_lvname(lvl->lv));
else
log_print("LV %s incomplete", display_lvname(lvl->lv));
}
}
}
/*
* When "pvscan --cache -aay <dev>" completes the vg, save the
* struct vg to use for quick activation function.
*/
if (do_activate && !saved_vg && vg && vg_complete && !do_all && (dm_list_size(pvscan_devs) == 1))
saved_vg = vg;
else
release_vg(vg);
@@ -1452,7 +1644,7 @@ static int _pvscan_cache_args(struct cmd_context *cmd, int argc, char **argv,
cmd->pvscan_cache_single = 1;
if (!setup_devices(cmd)) {
log_error("Failed to set up devices.");
log_error_pvscan(cmd, "Failed to set up devices.");
return 0;
}
@@ -1518,8 +1710,8 @@ static int _pvscan_cache_args(struct cmd_context *cmd, int argc, char **argv,
dm_list_iterate_items_safe(devl, devl2, &pvscan_devs) {
if (!cmd->filter->passes_filter(cmd, cmd->filter, devl->dev, NULL)) {
log_print("pvscan[%d] %s excluded by filters: %s.", getpid(),
dev_name(devl->dev), dev_filtered_reason(devl->dev));
log_print_pvscan(cmd, "%s excluded by filters: %s.",
dev_name(devl->dev), dev_filtered_reason(devl->dev));
dm_list_del(&devl->list);
}
}
@@ -1547,7 +1739,7 @@ static int _pvscan_cache_args(struct cmd_context *cmd, int argc, char **argv,
dm_list_iterate_items_safe(devl, devl2, &pvscan_devs) {
if (!label_read_pvid(devl->dev)) {
/* Not an lvm device */
log_print("pvscan[%d] %s not an lvm device.", getpid(), dev_name(devl->dev));
log_print_pvscan(cmd, "%s not an lvm device.", dev_name(devl->dev));
dm_list_del(&devl->list);
continue;
}
@@ -1558,8 +1750,8 @@ static int _pvscan_cache_args(struct cmd_context *cmd, int argc, char **argv,
*/
if (relax_deviceid_filter) {
if (!get_du_for_pvid(cmd, devl->dev->pvid)) {
log_print("pvscan[%d] %s excluded by devices file (checking PVID).",
getpid(), dev_name(devl->dev));
log_print_pvscan(cmd, "%s excluded by devices file (checking PVID).",
dev_name(devl->dev));
dm_list_del(&devl->list);
continue;
}
@@ -1567,8 +1759,8 @@ static int _pvscan_cache_args(struct cmd_context *cmd, int argc, char **argv,
/* Applies all filters, including those that need data from dev. */
if (!cmd->filter->passes_filter(cmd, cmd->filter, devl->dev, NULL)) {
log_print("pvscan[%d] %s excluded by filters: %s.", getpid(),
dev_name(devl->dev), dev_filtered_reason(devl->dev));
log_print_pvscan(cmd, "%s excluded by filters: %s.",
dev_name(devl->dev), dev_filtered_reason(devl->dev));
dm_list_del(&devl->list);
}
}
@@ -1624,6 +1816,29 @@ int pvscan_cache_cmd(struct cmd_context *cmd, int argc, char **argv)
return ECMD_PROCESSED;
}
/*
* lvm udev rules call:
* pvscan --cache --listvg|--listlvs --checkcomplete PV
* when PVs appear, even if event_activation=0 in lvm.conf.
*
* The udev rules will do autoactivation if they see complete
* VGs/LVs reported from the pvscan.
*
* When event_activation=0 we do not want to do autoactivation
* from udev events, so we need the pvscan to not report any
* complete VGs/LVs when event_activation=0 so that the udev
* rules do not attempt to autoactivate.
*/
if (arg_is_set(cmd, checkcomplete_ARG) &&
!find_config_tree_bool(cmd, global_event_activation_CFG, NULL)) {
if (arg_is_set(cmd, udevoutput_ARG))
printf("LVM_EVENT_ACTIVATION=0\n");
else
log_print_pvscan(cmd, "Ignoring pvscan with --checkcomplete because event_activation is disabled.");
return ECMD_PROCESSED;
}
if (arg_is_set(cmd, major_ARG) + arg_is_set(cmd, minor_ARG))
devno_args = 1;
@@ -1634,7 +1849,7 @@ int pvscan_cache_cmd(struct cmd_context *cmd, int argc, char **argv)
do_all = !argc && !devno_args;
_online_dir_setup();
_online_dir_setup(cmd);
if (do_all) {
if (!_pvscan_cache_all(cmd, argc, argv, &complete_vgnames))

View File

@@ -810,6 +810,24 @@ int lv_change_activate(struct cmd_context *cmd, struct logical_volume *lv,
lv_clear_integrity_recalculate_metadata(lv);
}
/*
* When LVs are deactivated, then autoactivation of the VG is
* "re-armed" by removing the vg online file. So, after deactivation
* of LVs, if PVs are disconnected and reconnected again, event
* activation will trigger autoactivation again. This secondary
* autoactivation is somewhat different from, and not as important as
* the initial autoactivation during system startup. The secondary
* autoactivation will happen to a VG on a running system and may be
* mixing with user commands, so the end result is unpredictable.
*
* It's possible that we might want a config setting for usersto
* disable secondary autoactivations. Once a system is up, the
* user may want to take charge of activation changes to the VG
* and not have the system autoactivation interfere.
*/
if (!is_change_activating(activate) && find_config_tree_bool(cmd, global_event_activation_CFG, NULL))
online_vg_file_remove(lv->vg->name);
set_lv_notify(lv->vg->cmd);
return r;

View File

@@ -291,4 +291,6 @@ int lvconvert_cachevol_attach_single(struct cmd_context *cmd,
struct logical_volume *lv,
struct processing_handle *handle);
void online_vg_file_remove(const char *vgname);
#endif

77
udev/69-dm-lvm.rules Normal file
View File

@@ -0,0 +1,77 @@
# Copyright (C) 2012,2021 Red Hat, Inc. All rights reserved.
#
# This file is part of LVM2.
# Udev rules for LVM.
#
# This rule requires blkid to be called on block devices before so only devices
# used as LVM PVs are processed (ID_FS_TYPE="LVM2_member").
SUBSYSTEM!="block", GOTO="lvm_end"
ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}=="1", GOTO="lvm_end"
# Only process devices already marked as a PV - this requires blkid to be called before.
ENV{ID_FS_TYPE}!="LVM2_member", GOTO="lvm_end"
ENV{DM_MULTIPATH_DEVICE_PATH}=="1", GOTO="lvm_end"
ACTION=="remove", GOTO="lvm_end"
# Create /dev/disk/by-id/lvm-pv-uuid-<PV_UUID> symlink for each PV
ENV{ID_FS_UUID_ENC}=="?*", SYMLINK+="disk/by-id/lvm-pv-uuid-$env{ID_FS_UUID_ENC}"
# If the PV is a special device listed below, scan only if the device is
# properly activated. These devices are not usable after an ADD event,
# but they require an extra setup and they are ready after a CHANGE event.
# Also support coldplugging with ADD event but only if the device is already
# properly activated.
# This logic should be eventually moved to rules where those particular
# devices are processed primarily (MD and loop).
# DM device:
KERNEL!="dm-[0-9]*", GOTO="next"
ENV{DM_UDEV_PRIMARY_SOURCE_FLAG}=="1", ENV{DM_ACTIVATION}=="1", GOTO="lvm_scan"
GOTO="lvm_end"
# MD device:
LABEL="next"
KERNEL!="md[0-9]*", GOTO="next"
IMPORT{db}="LVM_MD_PV_ACTIVATED"
ACTION=="add", ENV{LVM_MD_PV_ACTIVATED}=="1", GOTO="lvm_scan"
ACTION=="change", ENV{LVM_MD_PV_ACTIVATED}!="1", TEST=="md/array_state", ENV{LVM_MD_PV_ACTIVATED}="1", GOTO="lvm_scan"
ACTION=="add", KERNEL=="md[0-9]*p[0-9]*", GOTO="lvm_scan"
ENV{LVM_MD_PV_ACTIVATED}!="1", ENV{SYSTEMD_READY}="0"
GOTO="lvm_end"
# Loop device:
LABEL="next"
KERNEL!="loop[0-9]*", GOTO="next"
ACTION=="add", ENV{LVM_LOOP_PV_ACTIVATED}=="1", GOTO="lvm_scan"
ACTION=="change", ENV{LVM_LOOP_PV_ACTIVATED}!="1", TEST=="loop/backing_file", ENV{LVM_LOOP_PV_ACTIVATED}="1", GOTO="lvm_scan"
ENV{LVM_LOOP_PV_ACTIVATED}!="1", ENV{SYSTEMD_READY}="0"
GOTO="lvm_end"
LABEL="next"
ACTION!="add", GOTO="lvm_end"
LABEL="lvm_scan"
ENV{SYSTEMD_READY}="1"
# pvscan will check if this device completes a VG,
# i.e. all PVs in the VG are now present with the
# arrival of this PV. If so, it prints to stdout:
# LVM_VG_NAME_COMPLETE='foo'
#
# When the VG is complete it can be activated, so
# the lvm-vgchange service is started which runs
# vgchange -aay <vgname>.
#
# pvscan only reads the single device specified,
# and uses temp files under /run/lvm to check if
# other PVs in the VG are present.
IMPORT{program}="/usr/sbin/lvm pvscan --cache --listvg --checkcomplete --vgonline --udevoutput --journal=output $env{DEVNAME}"
ENV{LVM_VG_NAME_COMPLETE}=="?*", RUN+="/usr/bin/systemctl start lvm-vgchange@$env{LVM_VG_NAME_COMPLETE}.service"
GOTO="lvm_end"
LABEL="lvm_end"