mirror of
git://sourceware.org/git/lvm2.git
synced 2025-10-09 23:33:17 +03:00
Compare commits
4 Commits
v2_03_24
...
dev-dct-pv
Author | SHA1 | Date | |
---|---|---|---|
|
c310e23b51 | ||
|
2bdc2d4f5e | ||
|
662080514a | ||
|
1b170a7646 |
@@ -40,6 +40,7 @@ SOURCES =\
|
||||
device/dev-luks.c \
|
||||
device/dev-dasd.c \
|
||||
device/dev-lvm1-pool.c \
|
||||
device/online.c \
|
||||
display/display.c \
|
||||
error/errseg.c \
|
||||
unknown/unknown.c \
|
||||
|
@@ -174,7 +174,7 @@ struct cmd_context {
|
||||
unsigned activate_component:1; /* command activates component LV */
|
||||
unsigned process_component_lvs:1; /* command processes also component LVs */
|
||||
unsigned mirror_warn_printed:1; /* command already printed warning about non-monitored mirrors */
|
||||
unsigned pvscan_cache_single:1;
|
||||
unsigned expect_missing_vg_device:1; /* when reading a vg it's expected that a dev for a pv isn't found */
|
||||
unsigned can_use_one_scan:1;
|
||||
unsigned is_clvmd:1;
|
||||
unsigned md_component_detection:1;
|
||||
@@ -183,6 +183,7 @@ struct cmd_context {
|
||||
unsigned enable_hints:1; /* hints are enabled for cmds in general */
|
||||
unsigned use_hints:1; /* if hints are enabled this cmd can use them */
|
||||
unsigned pvscan_recreate_hints:1; /* enable special case hint handling for pvscan --cache */
|
||||
unsigned hints_pvs_online:1; /* hints="pvs_online" */
|
||||
unsigned scan_lvs:1;
|
||||
unsigned wipe_outdated_pvs:1;
|
||||
unsigned enable_devices_list:1; /* command is using --devices option */
|
||||
@@ -201,6 +202,7 @@ struct cmd_context {
|
||||
unsigned ignore_device_name_mismatch:1; /* skip updating devices file names */
|
||||
unsigned backup_disabled:1; /* skip repeated debug message */
|
||||
unsigned event_activation:1; /* whether event_activation is set */
|
||||
unsigned udevoutput:1;
|
||||
|
||||
/*
|
||||
* Devices and filtering.
|
||||
|
@@ -328,4 +328,8 @@
|
||||
|
||||
#define DEFAULT_WWIDS_FILE "/etc/multipath/wwids"
|
||||
|
||||
#define PVS_ONLINE_DIR DEFAULT_RUN_DIR "/pvs_online"
|
||||
#define VGS_ONLINE_DIR DEFAULT_RUN_DIR "/vgs_online"
|
||||
#define PVS_LOOKUP_DIR DEFAULT_RUN_DIR "/pvs_lookup"
|
||||
|
||||
#endif /* _LVM_DEFAULTS_H */
|
||||
|
@@ -778,9 +778,8 @@ static void _device_ids_update_try(struct cmd_context *cmd)
|
||||
{
|
||||
int held = 0;
|
||||
|
||||
/* Defer updates to non-pvscan-cache commands. */
|
||||
if (cmd->pvscan_cache_single) {
|
||||
log_print("pvscan[%d] skip updating devices file.", getpid());
|
||||
if (cmd->expect_missing_vg_device) {
|
||||
log_print("skip updating devices file.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
306
lib/device/online.c
Normal file
306
lib/device/online.c
Normal file
@@ -0,0 +1,306 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "base/memory/zalloc.h"
|
||||
#include "lib/misc/lib.h"
|
||||
#include "lib/commands/toolcontext.h"
|
||||
#include "lib/device/device.h"
|
||||
#include "lib/device/device_id.h"
|
||||
#include "lib/device/online.h"
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
static char *_vgname_in_pvid_file_buf(char *buf)
|
||||
{
|
||||
char *p, *n;
|
||||
|
||||
/*
|
||||
* file contains:
|
||||
* <major>:<minor>\n
|
||||
* vg:<vgname>\n\0
|
||||
*/
|
||||
|
||||
if (!(p = strchr(buf, '\n')))
|
||||
return NULL;
|
||||
|
||||
p++; /* skip \n */
|
||||
|
||||
if (*p && !strncmp(p, "vg:", 3)) {
|
||||
if ((n = strchr(p, '\n')))
|
||||
*n = '\0';
|
||||
return p + 3;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define MAX_PVID_FILE_SIZE 512
|
||||
|
||||
int online_pvid_file_read(char *path, int *major, int *minor, char *vgname)
|
||||
{
|
||||
char buf[MAX_PVID_FILE_SIZE] = { 0 };
|
||||
char *name;
|
||||
int fd, rv;
|
||||
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
log_warn("Failed to open %s", path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
rv = read(fd, buf, sizeof(buf) - 1);
|
||||
if (close(fd))
|
||||
log_sys_debug("close", path);
|
||||
if (!rv || rv < 0) {
|
||||
log_warn("No info in %s", path);
|
||||
return 0;
|
||||
}
|
||||
buf[rv] = 0; /* \0 terminated buffer */
|
||||
|
||||
if (sscanf(buf, "%d:%d", major, minor) != 2) {
|
||||
log_warn("No device numbers in %s", path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* vgname points to an offset in buf */
|
||||
if ((name = _vgname_in_pvid_file_buf(buf)))
|
||||
strncpy(vgname, name, NAME_LEN);
|
||||
else
|
||||
log_debug("No vgname in %s", path);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* When a PV goes offline, remove the vg online file for that VG
|
||||
* (even if other PVs for the VG are still online). This means
|
||||
* that the vg will be activated again when it becomes complete.
|
||||
*/
|
||||
|
||||
void online_vg_file_remove(const char *vgname)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
log_debug("Unlink vg online: %s", path);
|
||||
|
||||
if (unlink(path) && (errno != ENOENT))
|
||||
log_sys_debug("unlink", path);
|
||||
}
|
||||
|
||||
int online_vg_file_create(struct cmd_context *cmd, const char *vgname)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
int fd;
|
||||
|
||||
if (dm_snprintf(path, sizeof(path), "%s/%s", VGS_ONLINE_DIR, vgname) < 0) {
|
||||
log_error_pvscan(cmd, "Path %s/%s is too long.", VGS_ONLINE_DIR, vgname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_debug("Create vg online: %s", path);
|
||||
|
||||
fd = open(path, O_CREAT | O_EXCL | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR);
|
||||
if (fd < 0) {
|
||||
log_debug("Failed to create %s: %d", path, errno);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We don't care about syncing, these files are not even persistent. */
|
||||
|
||||
if (close(fd))
|
||||
log_sys_debug("close", path);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
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 };
|
||||
char file_vgname[NAME_LEN];
|
||||
int file_major = 0, file_minor = 0;
|
||||
int major, minor;
|
||||
int fd;
|
||||
int rv;
|
||||
int len;
|
||||
int len1 = 0;
|
||||
int len2 = 0;
|
||||
|
||||
major = (int)MAJOR(dev->dev);
|
||||
minor = (int)MINOR(dev->dev);
|
||||
|
||||
if (dm_snprintf(path, sizeof(path), "%s/%s", PVS_ONLINE_DIR, dev->pvid) < 0) {
|
||||
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_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_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;
|
||||
}
|
||||
}
|
||||
|
||||
len = len1 + len2;
|
||||
|
||||
log_debug("Create pv online: %s %d:%d %s", path, major, minor, dev_name(dev));
|
||||
|
||||
fd = open(path, O_CREAT | O_EXCL | O_RDWR, S_IRUSR | S_IWUSR);
|
||||
if (fd < 0) {
|
||||
if (errno == EEXIST)
|
||||
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;
|
||||
}
|
||||
|
||||
while (len > 0) {
|
||||
rv = write(fd, buf, len);
|
||||
if (rv < 0) {
|
||||
/* file exists so it still works in part */
|
||||
log_warn("Cannot write online file for %s to %s error %d",
|
||||
dev_name(dev), path, errno);
|
||||
if (close(fd))
|
||||
log_sys_debug("close", path);
|
||||
return 1;
|
||||
}
|
||||
len -= rv;
|
||||
}
|
||||
|
||||
/* We don't care about syncing, these files are not even persistent. */
|
||||
|
||||
if (close(fd))
|
||||
log_sys_debug("close", path);
|
||||
|
||||
return 1;
|
||||
|
||||
check_duplicate:
|
||||
|
||||
/*
|
||||
* If a PVID online file already exists for this PVID, check if the
|
||||
* file contains a different device number, and if so we may have a
|
||||
* duplicate PV.
|
||||
*
|
||||
* FIXME: disable autoactivation of the VG somehow?
|
||||
* The VG may or may not already be activated when a dupicate appears.
|
||||
* Perhaps write a new field in the pv online or vg online file?
|
||||
*/
|
||||
|
||||
memset(file_vgname, 0, sizeof(file_vgname));
|
||||
|
||||
online_pvid_file_read(path, &file_major, &file_minor, file_vgname);
|
||||
|
||||
if ((file_major == major) && (file_minor == minor)) {
|
||||
log_debug("Existing online file for %d:%d", major, minor);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* 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(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(cmd, "PV %s has unexpected VG %s vs %s.",
|
||||
dev_name(dev), vgname, file_vgname);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int online_pvid_file_exists(const char *pvid)
|
||||
{
|
||||
char path[PATH_MAX] = { 0 };
|
||||
struct stat buf;
|
||||
int rv;
|
||||
|
||||
if (dm_snprintf(path, sizeof(path), "%s/%s", PVS_ONLINE_DIR, pvid) < 0) {
|
||||
log_debug(INTERNAL_ERROR "Path %s/%s is too long.", PVS_ONLINE_DIR, pvid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_debug("Check pv online: %s", path);
|
||||
|
||||
rv = stat(path, &buf);
|
||||
if (!rv) {
|
||||
log_debug("Check pv online %s: yes", pvid);
|
||||
return 1;
|
||||
}
|
||||
log_debug("Check pv online %s: no", pvid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void online_dir_setup(struct cmd_context *cmd)
|
||||
{
|
||||
struct stat st;
|
||||
int rv;
|
||||
|
||||
if (!stat(DEFAULT_RUN_DIR, &st))
|
||||
goto do_pvs;
|
||||
|
||||
log_debug("Creating run_dir.");
|
||||
dm_prepare_selinux_context(DEFAULT_RUN_DIR, S_IFDIR);
|
||||
rv = mkdir(DEFAULT_RUN_DIR, 0755);
|
||||
dm_prepare_selinux_context(NULL, 0);
|
||||
|
||||
if ((rv < 0) && stat(DEFAULT_RUN_DIR, &st))
|
||||
log_error_pvscan(cmd, "Failed to create %s %d", DEFAULT_RUN_DIR, errno);
|
||||
|
||||
do_pvs:
|
||||
if (!stat(PVS_ONLINE_DIR, &st))
|
||||
goto do_vgs;
|
||||
|
||||
log_debug("Creating pvs_online_dir.");
|
||||
dm_prepare_selinux_context(PVS_ONLINE_DIR, S_IFDIR);
|
||||
rv = mkdir(PVS_ONLINE_DIR, 0755);
|
||||
dm_prepare_selinux_context(NULL, 0);
|
||||
|
||||
if ((rv < 0) && stat(PVS_ONLINE_DIR, &st))
|
||||
log_error_pvscan(cmd, "Failed to create %s %d", PVS_ONLINE_DIR, errno);
|
||||
|
||||
do_vgs:
|
||||
if (!stat(VGS_ONLINE_DIR, &st))
|
||||
goto do_lookup;
|
||||
|
||||
log_debug("Creating vgs_online_dir.");
|
||||
dm_prepare_selinux_context(VGS_ONLINE_DIR, S_IFDIR);
|
||||
rv = mkdir(VGS_ONLINE_DIR, 0755);
|
||||
dm_prepare_selinux_context(NULL, 0);
|
||||
|
||||
if ((rv < 0) && stat(VGS_ONLINE_DIR, &st))
|
||||
log_error_pvscan(cmd, "Failed to create %s %d", VGS_ONLINE_DIR, errno);
|
||||
|
||||
do_lookup:
|
||||
if (!stat(PVS_LOOKUP_DIR, &st))
|
||||
return;
|
||||
|
||||
log_debug("Creating pvs_lookup_dir.");
|
||||
dm_prepare_selinux_context(PVS_LOOKUP_DIR, S_IFDIR);
|
||||
rv = mkdir(PVS_LOOKUP_DIR, 0755);
|
||||
dm_prepare_selinux_context(NULL, 0);
|
||||
|
||||
if ((rv < 0) && stat(PVS_LOOKUP_DIR, &st))
|
||||
log_error_pvscan(cmd, "Failed to create %s %d", PVS_LOOKUP_DIR, errno);
|
||||
|
||||
|
||||
}
|
46
lib/device/online.h
Normal file
46
lib/device/online.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef _ONLINE_H
|
||||
#define _ONLINE_H
|
||||
|
||||
/*
|
||||
* 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 (cmd->udevoutput) \
|
||||
log_print(fmt, ##args); \
|
||||
else \
|
||||
log_print("pvscan[%d] " fmt, getpid(), ##args); \
|
||||
while (0)
|
||||
|
||||
#define log_error_pvscan(cmd, fmt, args...) \
|
||||
do \
|
||||
if (cmd->udevoutput) \
|
||||
log_error(fmt, ##args); \
|
||||
else \
|
||||
log_error("pvscan[%d] " fmt, getpid(), ##args); \
|
||||
while (0)
|
||||
|
||||
int online_pvid_file_read(char *path, int *major, int *minor, char *vgname);
|
||||
int online_vg_file_create(struct cmd_context *cmd, const char *vgname);
|
||||
void online_vg_file_remove(const char *vgname);
|
||||
int online_pvid_file_create(struct cmd_context *cmd, struct device *dev, const char *vgname);
|
||||
int online_pvid_file_exists(const char *pvid);
|
||||
void online_dir_setup(struct cmd_context *cmd);
|
||||
|
||||
#endif
|
@@ -146,10 +146,12 @@
|
||||
#include "lib/label/hints.h"
|
||||
#include "lib/device/dev-type.h"
|
||||
#include "lib/device/device_id.h"
|
||||
#include "lib/device/online.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/file.h>
|
||||
@@ -1278,6 +1280,109 @@ check:
|
||||
free(name);
|
||||
}
|
||||
|
||||
static int _get_hints_from_pvs_online(struct cmd_context *cmd, struct dm_list *hints_out,
|
||||
struct dm_list *devs_in, struct dm_list *devs_out)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
char file_vgname[NAME_LEN];
|
||||
struct dm_list hints_list;
|
||||
struct hint file_hint;
|
||||
struct hint *alloc_hint;
|
||||
struct hint *hint, *hint2;
|
||||
struct device_list *devl, *devl2;
|
||||
int file_major, file_minor;
|
||||
int found = 0;
|
||||
DIR *dir;
|
||||
struct dirent *de;
|
||||
char *vgname = NULL;
|
||||
char *pvid;
|
||||
|
||||
dm_list_init(&hints_list);
|
||||
|
||||
if (!(dir = opendir(PVS_ONLINE_DIR)))
|
||||
return 0;
|
||||
|
||||
while ((de = readdir(dir))) {
|
||||
if (de->d_name[0] == '.')
|
||||
continue;
|
||||
|
||||
pvid = de->d_name;
|
||||
|
||||
if (strlen(pvid) != ID_LEN) /* 32 */
|
||||
continue;
|
||||
|
||||
memset(path, 0, sizeof(path));
|
||||
snprintf(path, sizeof(path), "%s/%s", PVS_ONLINE_DIR, pvid);
|
||||
|
||||
memset(&file_hint, 0, sizeof(file_hint));
|
||||
memset(file_vgname, 0, sizeof(file_vgname));
|
||||
file_major = 0;
|
||||
file_minor = 0;
|
||||
|
||||
if (!online_pvid_file_read(path, &file_major, &file_minor, file_vgname))
|
||||
continue;
|
||||
|
||||
if (!dm_strncpy(file_hint.pvid, pvid, sizeof(file_hint.pvid)))
|
||||
continue;
|
||||
|
||||
file_hint.devt = makedev(file_major, file_minor);
|
||||
|
||||
if (file_vgname[0] && validate_name(file_vgname)) {
|
||||
if (!dm_strncpy(file_hint.vgname, file_vgname, sizeof(file_hint.vgname)))
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(alloc_hint = malloc(sizeof(struct hint))))
|
||||
continue;
|
||||
|
||||
memcpy(alloc_hint, &file_hint, sizeof(struct hint));
|
||||
|
||||
log_debug("add hint %s %d:%d %s from pvs_online", file_hint.pvid, file_major, file_minor, file_vgname);
|
||||
dm_list_add(&hints_list, &alloc_hint->list);
|
||||
found++;
|
||||
}
|
||||
|
||||
if (closedir(dir))
|
||||
stack;
|
||||
|
||||
log_debug("accept hints found %d from pvs_online", found);
|
||||
|
||||
_get_single_vgname_cmd_arg(cmd, &hints_list, &vgname);
|
||||
|
||||
/*
|
||||
* apply_hints equivalent, move devs from devs_in to devs_out if
|
||||
* their devno matches the devno of a hint (and if the hint matches
|
||||
* the vgname when a vgname is present.)
|
||||
*/
|
||||
dm_list_iterate_items_safe(devl, devl2, devs_in) {
|
||||
dm_list_iterate_items_safe(hint, hint2, &hints_list) {
|
||||
if ((MAJOR(devl->dev->dev) == MAJOR(hint->devt)) &&
|
||||
(MINOR(devl->dev->dev) == MINOR(hint->devt))) {
|
||||
|
||||
if (vgname && hint->vgname[0] && strcmp(vgname, hint->vgname))
|
||||
goto next_dev;
|
||||
|
||||
snprintf(hint->name, sizeof(hint->name), "%s", dev_name(devl->dev));
|
||||
hint->chosen = 1;
|
||||
|
||||
dm_list_del(&devl->list);
|
||||
dm_list_add(devs_out, &devl->list);
|
||||
}
|
||||
}
|
||||
next_dev:
|
||||
;
|
||||
}
|
||||
|
||||
log_debug("applied hints using %d other %d vgname %s from pvs_online",
|
||||
dm_list_size(devs_out), dm_list_size(devs_in), vgname ?: "");
|
||||
|
||||
dm_list_splice(hints_out, &hints_list);
|
||||
|
||||
free(vgname);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns 0: no hints are used.
|
||||
* . newhints is set if this command should create new hints after scan
|
||||
@@ -1299,7 +1404,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;
|
||||
|
||||
/*
|
||||
@@ -1319,6 +1424,19 @@ int get_hints(struct cmd_context *cmd, struct dm_list *hints_out, int *newhints,
|
||||
if (!cmd->use_hints)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* 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");
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if another command created the nohints file to prevent us from
|
||||
* using hints.
|
||||
|
@@ -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)
|
||||
|
@@ -3558,7 +3558,7 @@ static void _set_pv_device(struct format_instance *fid,
|
||||
if (!id_write_format(&pv->id, buffer, sizeof(buffer)))
|
||||
buffer[0] = '\0';
|
||||
|
||||
if (cmd && !cmd->pvscan_cache_single &&
|
||||
if (cmd && !cmd->expect_missing_vg_device &&
|
||||
(!vg_is_foreign(vg) && !cmd->include_foreign_vgs))
|
||||
log_warn("WARNING: Couldn't find device with uuid %s.", buffer);
|
||||
else
|
||||
@@ -5084,7 +5084,7 @@ struct volume_group *vg_read(struct cmd_context *cmd, const char *vg_name, const
|
||||
if (!pvl->pv->dev) {
|
||||
/* The obvious and common case of a missing device. */
|
||||
|
||||
if (vg_is_foreign(vg) && !cmd->include_foreign_vgs)
|
||||
if ((vg_is_foreign(vg) && !cmd->include_foreign_vgs) || cmd->expect_missing_vg_device)
|
||||
log_debug("VG %s is missing PV %s (last written to %s)", vg_name, uuidstr, pvl->pv->device_hint ?: "na");
|
||||
else if (pvl->pv->device_hint)
|
||||
log_warn("WARNING: VG %s is missing PV %s (last written to %s).", vg_name, uuidstr, pvl->pv->device_hint);
|
||||
|
98
test/shell/vgchange-pvs-online.sh
Normal file
98
test/shell/vgchange-pvs-online.sh
Normal file
@@ -0,0 +1,98 @@
|
||||
SKIP_WITH_LVMPOLLD=1
|
||||
SKIP_WITH_LVMLOCKD=1
|
||||
|
||||
RUNDIR="/run"
|
||||
test -d "$RUNDIR" || RUNDIR="/var/run"
|
||||
PVS_ONLINE_DIR="$RUNDIR/lvm/pvs_online"
|
||||
VGS_ONLINE_DIR="$RUNDIR/lvm/vgs_online"
|
||||
PVS_LOOKUP_DIR="$RUNDIR/lvm/pvs_lookup"
|
||||
|
||||
_clear_online_files() {
|
||||
# wait till udev is finished
|
||||
aux udev_wait
|
||||
rm -f "$PVS_ONLINE_DIR"/*
|
||||
rm -f "$VGS_ONLINE_DIR"/*
|
||||
rm -f "$PVS_LOOKUP_DIR"/*
|
||||
}
|
||||
|
||||
. lib/inittest
|
||||
|
||||
aux prepare_devs 4
|
||||
|
||||
vgcreate $vg1 "$dev1" "$dev2"
|
||||
vgcreate $vg2 "$dev3"
|
||||
pvcreate "$dev4"
|
||||
|
||||
lvcreate -l1 -n $lv1 -an $vg1
|
||||
lvcreate -l1 -n $lv1 -an $vg2
|
||||
|
||||
# With no pv online files, vgchange that uses online files
|
||||
# will find no PVs to activate from.
|
||||
|
||||
_clear_online_files
|
||||
|
||||
not vgchange -aay --autoactivation event $vg1
|
||||
not vgchange -aay --autoactivation event $vg2
|
||||
vgchange -aay --autoactivation event
|
||||
|
||||
check lv_field $vg1/$lv1 lv_active ""
|
||||
check lv_field $vg2/$lv1 lv_active ""
|
||||
|
||||
# incomplete vg will not be activated
|
||||
|
||||
pvscan --cache "$dev1"
|
||||
vgchange -aay --autoactivation event $vg1
|
||||
# VG foo is incomplete
|
||||
check lv_field $vg1/$lv1 lv_active ""
|
||||
|
||||
# complete vg is activated
|
||||
|
||||
pvscan --cache "$dev3"
|
||||
vgchange -aay --autoactivation event $vg2
|
||||
check lv_field $vg2/$lv1 lv_active "active"
|
||||
|
||||
pvscan --cache "$dev2"
|
||||
vgchange -aay --autoactivation event $vg1
|
||||
check lv_field $vg1/$lv1 lv_active "active"
|
||||
|
||||
vgchange -an $vg1
|
||||
vgchange -an $vg2
|
||||
|
||||
# the same tests but using command options matching udev rule
|
||||
|
||||
_clear_online_files
|
||||
|
||||
pvscan --cache --listvg --checkcomplete --vgonline --autoactivation event --udevoutput --journal=output "$dev1"
|
||||
vgchange -aay --autoactivation event $vg1
|
||||
# VG foo is incomplete
|
||||
check lv_field $vg1/$lv1 lv_active ""
|
||||
|
||||
pvscan --cache --listvg --checkcomplete --vgonline --autoactivation event --udevoutput --journal=output "$dev3"
|
||||
vgchange -aay --autoactivation event $vg2
|
||||
check lv_field $vg2/$lv1 lv_active "active"
|
||||
|
||||
pvscan --cache --listvg --checkcomplete --vgonline --autoactivation event --udevoutput --journal=output "$dev2"
|
||||
vgchange -aay --autoactivation event $vg1
|
||||
check lv_field $vg1/$lv1 lv_active "active"
|
||||
|
||||
vgchange -an $vg1
|
||||
vgchange -an $vg2
|
||||
|
||||
# with a full pvscan --cache
|
||||
|
||||
_clear_online_files
|
||||
|
||||
pvscan --cache
|
||||
check lv_field $vg1/$lv1 lv_active ""
|
||||
check lv_field $vg2/$lv1 lv_active ""
|
||||
vgchange -aay --autoactivation event $vg1
|
||||
vgchange -aay --autoactivation event $vg2
|
||||
check lv_field $vg1/$lv1 lv_active "active"
|
||||
check lv_field $vg2/$lv1 lv_active "active"
|
||||
|
||||
vgchange -an $vg1
|
||||
vgchange -an $vg2
|
||||
|
||||
vgremove -f $vg1
|
||||
vgremove -f $vg2
|
||||
|
@@ -87,6 +87,12 @@ 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 autoactivation is being used from an event.\n"
|
||||
"This allows the command to apply settings that are specific\n"
|
||||
"to event activation, such as device scanning optimizations\n"
|
||||
"using pvs_online files created by event-based pvscans.\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"
|
||||
|
@@ -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, --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 +1748,8 @@ DESC: Start or stop processing LV conversions.
|
||||
|
||||
vgchange --activate Active
|
||||
OO: --activationmode ActivationMode, --ignoreactivationskip, --partial, --sysinit,
|
||||
--readonly, --ignorelockingfailure, --monitor Bool, --poll Bool, OO_VGCHANGE
|
||||
--readonly, --ignorelockingfailure, --monitor Bool, --poll Bool,
|
||||
--autoactivation String, OO_VGCHANGE
|
||||
OP: VG|Tag|Select ...
|
||||
IO: --ignoreskippedcluster
|
||||
ID: vgchange_activate
|
||||
|
@@ -2393,8 +2393,10 @@ 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))
|
||||
if (arg_is_set(cmd, udevoutput_ARG)) {
|
||||
cmd->current_settings.suppress = 1;
|
||||
cmd->udevoutput = 1;
|
||||
}
|
||||
|
||||
if (arg_is_set(cmd, debug_ARG))
|
||||
cmd->current_settings.debug = _LOG_FATAL + (arg_count(cmd, debug_ARG) - 1);
|
||||
@@ -2540,6 +2542,8 @@ static int _get_current_settings(struct cmd_context *cmd)
|
||||
if (!strcmp(hint_mode, "none")) {
|
||||
cmd->enable_hints = 0;
|
||||
cmd->use_hints = 0;
|
||||
} else if (!strcmp(hint_mode, "pvs_online")) {
|
||||
cmd->hints_pvs_online = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
400
tools/pvscan.c
400
tools/pvscan.c
@@ -18,6 +18,7 @@
|
||||
#include "lib/cache/lvmcache.h"
|
||||
#include "lib/metadata/metadata.h"
|
||||
#include "lib/label/hints.h"
|
||||
#include "lib/device/online.h"
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
@@ -42,10 +43,6 @@ struct pvscan_aa_params {
|
||||
*/
|
||||
static struct volume_group *saved_vg;
|
||||
|
||||
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 int _pvscan_display_pv(struct cmd_context *cmd,
|
||||
struct physical_volume *pv,
|
||||
struct pvscan_params *params)
|
||||
@@ -179,93 +176,12 @@ 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;
|
||||
|
||||
/*
|
||||
* file contains:
|
||||
* <major>:<minor>\n
|
||||
* vg:<vgname>\n\0
|
||||
*/
|
||||
|
||||
if (!(p = strchr(buf, '\n')))
|
||||
return NULL;
|
||||
|
||||
p++; /* skip \n */
|
||||
|
||||
if (*p && !strncmp(p, "vg:", 3)) {
|
||||
if ((n = strchr(p, '\n')))
|
||||
*n = '\0';
|
||||
return p + 3;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define MAX_PVID_FILE_SIZE 512
|
||||
|
||||
static int _online_pvid_file_read(char *path, int *major, int *minor, char *vgname)
|
||||
{
|
||||
char buf[MAX_PVID_FILE_SIZE] = { 0 };
|
||||
char *name;
|
||||
int fd, rv;
|
||||
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
log_warn("Failed to open %s", path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
rv = read(fd, buf, sizeof(buf) - 1);
|
||||
if (close(fd))
|
||||
log_sys_debug("close", path);
|
||||
if (!rv || rv < 0) {
|
||||
log_warn("No info in %s", path);
|
||||
return 0;
|
||||
}
|
||||
buf[rv] = 0; /* \0 terminated buffer */
|
||||
|
||||
if (sscanf(buf, "%d:%d", major, minor) != 2) {
|
||||
log_warn("No device numbers in %s", path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* vgname points to an offset in buf */
|
||||
if ((name = _vgname_in_pvid_file_buf(buf)))
|
||||
strncpy(vgname, name, NAME_LEN);
|
||||
else
|
||||
log_debug("No vgname in %s", path);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void _lookup_file_remove(char *vgname)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
|
||||
if (dm_snprintf(path, sizeof(path), "%s/%s", _pvs_lookup_dir, vgname) < 0) {
|
||||
log_error("Path %s/%s is too long.", _pvs_lookup_dir, vgname);
|
||||
if (dm_snprintf(path, sizeof(path), "%s/%s", PVS_LOOKUP_DIR, vgname) < 0) {
|
||||
log_error("Path %s/%s is too long.", PVS_LOOKUP_DIR, vgname);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -275,27 +191,6 @@ static void _lookup_file_remove(char *vgname)
|
||||
log_sys_debug("unlink", path);
|
||||
}
|
||||
|
||||
/*
|
||||
* When a PV goes offline, remove the vg online file for that VG
|
||||
* (even if other PVs for the VG are still online). This means
|
||||
* that the vg will be activated again when it becomes complete.
|
||||
*/
|
||||
|
||||
void online_vg_file_remove(const char *vgname)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
log_debug("Unlink vg online: %s", path);
|
||||
|
||||
if (unlink(path) && (errno != ENOENT))
|
||||
log_sys_debug("unlink", path);
|
||||
}
|
||||
|
||||
/*
|
||||
* When a device goes offline we only know its major:minor, not its PVID.
|
||||
* Since the dev isn't around, we can't read it to get its PVID, so we have to
|
||||
@@ -314,7 +209,7 @@ static void _online_pvid_file_remove_devno(int major, int minor)
|
||||
|
||||
log_debug("Remove pv online devno %d:%d", major, minor);
|
||||
|
||||
if (!(dir = opendir(_pvs_online_dir)))
|
||||
if (!(dir = opendir(PVS_ONLINE_DIR)))
|
||||
return;
|
||||
|
||||
while ((de = readdir(dir))) {
|
||||
@@ -322,13 +217,13 @@ static void _online_pvid_file_remove_devno(int major, int minor)
|
||||
continue;
|
||||
|
||||
memset(path, 0, sizeof(path));
|
||||
snprintf(path, sizeof(path), "%s/%s", _pvs_online_dir, de->d_name);
|
||||
snprintf(path, sizeof(path), "%s/%s", PVS_ONLINE_DIR, de->d_name);
|
||||
|
||||
file_major = 0;
|
||||
file_minor = 0;
|
||||
memset(file_vgname, 0, sizeof(file_vgname));
|
||||
|
||||
_online_pvid_file_read(path, &file_major, &file_minor, file_vgname);
|
||||
online_pvid_file_read(path, &file_major, &file_minor, file_vgname);
|
||||
|
||||
if ((file_major == major) && (file_minor == minor)) {
|
||||
log_debug("Unlink pv online %s", path);
|
||||
@@ -342,7 +237,7 @@ static void _online_pvid_file_remove_devno(int major, int minor)
|
||||
}
|
||||
}
|
||||
if (closedir(dir))
|
||||
log_sys_debug("closedir", _pvs_online_dir);
|
||||
log_sys_debug("closedir", PVS_ONLINE_DIR);
|
||||
}
|
||||
|
||||
static void _online_files_remove(const char *dirpath)
|
||||
@@ -367,128 +262,6 @@ 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)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
char buf[MAX_PVID_FILE_SIZE] = { 0 };
|
||||
char file_vgname[NAME_LEN];
|
||||
int file_major = 0, file_minor = 0;
|
||||
int major, minor;
|
||||
int fd;
|
||||
int rv;
|
||||
int len;
|
||||
int len1 = 0;
|
||||
int len2 = 0;
|
||||
|
||||
major = (int)MAJOR(dev->dev);
|
||||
minor = (int)MINOR(dev->dev);
|
||||
|
||||
if (dm_snprintf(path, sizeof(path), "%s/%s", _pvs_online_dir, dev->pvid) < 0) {
|
||||
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_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_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;
|
||||
}
|
||||
}
|
||||
|
||||
len = len1 + len2;
|
||||
|
||||
log_debug("Create pv online: %s %d:%d %s", path, major, minor, dev_name(dev));
|
||||
|
||||
fd = open(path, O_CREAT | O_EXCL | O_RDWR, S_IRUSR | S_IWUSR);
|
||||
if (fd < 0) {
|
||||
if (errno == EEXIST)
|
||||
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;
|
||||
}
|
||||
|
||||
while (len > 0) {
|
||||
rv = write(fd, buf, len);
|
||||
if (rv < 0) {
|
||||
/* file exists so it still works in part */
|
||||
log_warn("Cannot write online file for %s to %s error %d",
|
||||
dev_name(dev), path, errno);
|
||||
if (close(fd))
|
||||
log_sys_debug("close", path);
|
||||
return 1;
|
||||
}
|
||||
len -= rv;
|
||||
}
|
||||
|
||||
/* We don't care about syncing, these files are not even persistent. */
|
||||
|
||||
if (close(fd))
|
||||
log_sys_debug("close", path);
|
||||
|
||||
return 1;
|
||||
|
||||
check_duplicate:
|
||||
|
||||
/*
|
||||
* If a PVID online file already exists for this PVID, check if the
|
||||
* file contains a different device number, and if so we may have a
|
||||
* duplicate PV.
|
||||
*
|
||||
* FIXME: disable autoactivation of the VG somehow?
|
||||
* The VG may or may not already be activated when a dupicate appears.
|
||||
* Perhaps write a new field in the pv online or vg online file?
|
||||
*/
|
||||
|
||||
memset(file_vgname, 0, sizeof(file_vgname));
|
||||
|
||||
_online_pvid_file_read(path, &file_major, &file_minor, file_vgname);
|
||||
|
||||
if ((file_major == major) && (file_minor == minor)) {
|
||||
log_debug("Existing online file for %d:%d", major, minor);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* 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(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(cmd, "PV %s has unexpected VG %s vs %s.",
|
||||
dev_name(dev), vgname, file_vgname);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _online_pvid_file_exists(const char *pvid)
|
||||
{
|
||||
char path[PATH_MAX] = { 0 };
|
||||
struct stat buf;
|
||||
int rv;
|
||||
|
||||
if (dm_snprintf(path, sizeof(path), "%s/%s", _pvs_online_dir, pvid) < 0) {
|
||||
log_debug(INTERNAL_ERROR "Path %s/%s is too long.", _pvs_online_dir, pvid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_debug("Check pv online: %s", path);
|
||||
|
||||
rv = stat(path, &buf);
|
||||
if (!rv) {
|
||||
log_debug("Check pv online %s: yes", pvid);
|
||||
return 1;
|
||||
}
|
||||
log_debug("Check pv online %s: no", pvid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _write_lookup_file(struct cmd_context *cmd, struct volume_group *vg)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
@@ -496,8 +269,8 @@ static int _write_lookup_file(struct cmd_context *cmd, struct volume_group *vg)
|
||||
struct pv_list *pvl;
|
||||
int fd;
|
||||
|
||||
if (dm_snprintf(path, sizeof(path), "%s/%s", _pvs_lookup_dir, vg->name) < 0) {
|
||||
log_error_pvscan(cmd, "Path %s/%s is too long.", _pvs_lookup_dir, vg->name);
|
||||
if (dm_snprintf(path, sizeof(path), "%s/%s", PVS_LOOKUP_DIR, vg->name) < 0) {
|
||||
log_error_pvscan(cmd, "Path %s/%s is too long.", PVS_LOOKUP_DIR, vg->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -553,7 +326,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)++;
|
||||
@@ -614,8 +387,8 @@ static int _count_pvid_files_from_lookup_file(struct cmd_context *cmd, struct de
|
||||
*pvs_online = 0;
|
||||
*pvs_offline = 0;
|
||||
|
||||
if (!(dir = opendir(_pvs_lookup_dir))) {
|
||||
log_sys_debug("opendir", _pvs_lookup_dir);
|
||||
if (!(dir = opendir(PVS_LOOKUP_DIR))) {
|
||||
log_sys_debug("opendir", PVS_LOOKUP_DIR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -627,8 +400,8 @@ static int _count_pvid_files_from_lookup_file(struct cmd_context *cmd, struct de
|
||||
if (de->d_name[0] == '.')
|
||||
continue;
|
||||
|
||||
if (dm_snprintf(path, sizeof(path), "%s/%s", _pvs_lookup_dir, de->d_name) < 0) {
|
||||
log_warn("WARNING: Path %s/%s is too long.", _pvs_lookup_dir, de->d_name);
|
||||
if (dm_snprintf(path, sizeof(path), "%s/%s", PVS_LOOKUP_DIR, de->d_name) < 0) {
|
||||
log_warn("WARNING: Path %s/%s is too long.", PVS_LOOKUP_DIR, de->d_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -653,68 +426,13 @@ static int _count_pvid_files_from_lookup_file(struct cmd_context *cmd, struct de
|
||||
log_sys_debug("fclose", path);
|
||||
}
|
||||
if (closedir(dir))
|
||||
log_sys_debug("closedir", _pvs_lookup_dir);
|
||||
log_sys_debug("closedir", PVS_LOOKUP_DIR);
|
||||
|
||||
*vgname_out = vgname;
|
||||
|
||||
return (vgname) ? 1 : 0;
|
||||
}
|
||||
|
||||
static void _online_dir_setup(struct cmd_context *cmd)
|
||||
{
|
||||
struct stat st;
|
||||
int rv;
|
||||
|
||||
if (!stat(DEFAULT_RUN_DIR, &st))
|
||||
goto do_pvs;
|
||||
|
||||
log_debug("Creating run_dir.");
|
||||
dm_prepare_selinux_context(DEFAULT_RUN_DIR, S_IFDIR);
|
||||
rv = mkdir(DEFAULT_RUN_DIR, 0755);
|
||||
dm_prepare_selinux_context(NULL, 0);
|
||||
|
||||
if ((rv < 0) && stat(DEFAULT_RUN_DIR, &st))
|
||||
log_error_pvscan(cmd, "Failed to create %s %d", DEFAULT_RUN_DIR, errno);
|
||||
|
||||
do_pvs:
|
||||
if (!stat(_pvs_online_dir, &st))
|
||||
goto do_vgs;
|
||||
|
||||
log_debug("Creating pvs_online_dir.");
|
||||
dm_prepare_selinux_context(_pvs_online_dir, S_IFDIR);
|
||||
rv = mkdir(_pvs_online_dir, 0755);
|
||||
dm_prepare_selinux_context(NULL, 0);
|
||||
|
||||
if ((rv < 0) && stat(_pvs_online_dir, &st))
|
||||
log_error_pvscan(cmd, "Failed to create %s %d", _pvs_online_dir, errno);
|
||||
|
||||
do_vgs:
|
||||
if (!stat(_vgs_online_dir, &st))
|
||||
goto do_lookup;
|
||||
|
||||
log_debug("Creating vgs_online_dir.");
|
||||
dm_prepare_selinux_context(_vgs_online_dir, S_IFDIR);
|
||||
rv = mkdir(_vgs_online_dir, 0755);
|
||||
dm_prepare_selinux_context(NULL, 0);
|
||||
|
||||
if ((rv < 0) && stat(_vgs_online_dir, &st))
|
||||
log_error_pvscan(cmd, "Failed to create %s %d", _vgs_online_dir, errno);
|
||||
|
||||
do_lookup:
|
||||
if (!stat(_pvs_lookup_dir, &st))
|
||||
return;
|
||||
|
||||
log_debug("Creating pvs_lookup_dir.");
|
||||
dm_prepare_selinux_context(_pvs_lookup_dir, S_IFDIR);
|
||||
rv = mkdir(_pvs_lookup_dir, 0755);
|
||||
dm_prepare_selinux_context(NULL, 0);
|
||||
|
||||
if ((rv < 0) && stat(_pvs_lookup_dir, &st))
|
||||
log_error_pvscan(cmd, "Failed to create %s %d", _pvs_lookup_dir, errno);
|
||||
|
||||
|
||||
}
|
||||
|
||||
static void _count_pvid_files(struct volume_group *vg, int *pvs_online, int *pvs_offline)
|
||||
{
|
||||
char pvid[ID_LEN + 1] __attribute__((aligned(8))) = { 0 };
|
||||
@@ -725,7 +443,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)++;
|
||||
@@ -748,7 +466,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++;
|
||||
}
|
||||
@@ -756,32 +474,6 @@ static int _pvscan_aa_single(struct cmd_context *cmd, const char *vg_name,
|
||||
return ECMD_PROCESSED;
|
||||
}
|
||||
|
||||
static int _online_vg_file_create(struct cmd_context *cmd, const char *vgname)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
int fd;
|
||||
|
||||
if (dm_snprintf(path, sizeof(path), "%s/%s", _vgs_online_dir, vgname) < 0) {
|
||||
log_error_pvscan(cmd, "Path %s/%s is too long.", _vgs_online_dir, vgname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_debug("Create vg online: %s", path);
|
||||
|
||||
fd = open(path, O_CREAT | O_EXCL | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR);
|
||||
if (fd < 0) {
|
||||
log_debug("Failed to create %s: %d", path, errno);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We don't care about syncing, these files are not even persistent. */
|
||||
|
||||
if (close(fd))
|
||||
log_sys_debug("close", path);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is a very unconventional way of doing things because
|
||||
* we want to figure out which devices to read the VG from
|
||||
@@ -841,13 +533,13 @@ static int _get_devs_from_saved_vg(struct cmd_context *cmd, const char *vgname,
|
||||
memcpy(pvid, &pvl->pv->id.uuid, ID_LEN);
|
||||
|
||||
memset(path, 0, sizeof(path));
|
||||
snprintf(path, sizeof(path), "%s/%s", _pvs_online_dir, pvid);
|
||||
snprintf(path, sizeof(path), "%s/%s", PVS_ONLINE_DIR, pvid);
|
||||
|
||||
file_major = 0;
|
||||
file_minor = 0;
|
||||
memset(file_vgname, 0, sizeof(file_vgname));
|
||||
|
||||
_online_pvid_file_read(path, &file_major, &file_minor, file_vgname);
|
||||
online_pvid_file_read(path, &file_major, &file_minor, file_vgname);
|
||||
|
||||
if (file_vgname[0] && strcmp(vgname, file_vgname)) {
|
||||
log_error_pvscan(cmd, "Wrong VG found for %d:%d PVID %s: %s vs %s",
|
||||
@@ -1036,7 +728,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++;
|
||||
}
|
||||
@@ -1070,7 +762,7 @@ static int _pvscan_aa(struct cmd_context *cmd, struct pvscan_aa_params *pp,
|
||||
* to run the activation. The first to create the file will do it.
|
||||
*/
|
||||
dm_list_iterate_items_safe(sl, sl2, vgnames) {
|
||||
if (!_online_vg_file_create(cmd, sl->str)) {
|
||||
if (!online_vg_file_create(cmd, sl->str)) {
|
||||
log_print_pvscan(cmd, "VG %s skip autoactivation.", sl->str);
|
||||
str_list_del(vgnames, sl->str);
|
||||
continue;
|
||||
@@ -1240,7 +932,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;
|
||||
@@ -1248,13 +940,13 @@ static void _set_pv_devices_online(struct cmd_context *cmd, struct volume_group
|
||||
}
|
||||
|
||||
memset(path, 0, sizeof(path));
|
||||
snprintf(path, sizeof(path), "%s/%s", _pvs_online_dir, pvid);
|
||||
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);
|
||||
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",
|
||||
@@ -1420,7 +1112,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)) {
|
||||
log_error_pvscan(cmd, "PV %s failed to create online file.", dev_name(dev));
|
||||
release_vg(vg);
|
||||
ret = 0;
|
||||
@@ -1505,7 +1197,7 @@ static int _online_devs(struct cmd_context *cmd, int do_all, struct dm_list *pvs
|
||||
} else if (!do_check_complete) {
|
||||
log_print("VG %s", vgname);
|
||||
} else if (vg_complete) {
|
||||
if (do_vgonline && !_online_vg_file_create(cmd, vgname)) {
|
||||
if (do_vgonline && !online_vg_file_create(cmd, vgname)) {
|
||||
log_print("VG %s finished", vgname);
|
||||
} else {
|
||||
/*
|
||||
@@ -1611,9 +1303,9 @@ static int _pvscan_cache_all(struct cmd_context *cmd, int argc, char **argv,
|
||||
|
||||
dm_list_init(&pvscan_devs);
|
||||
|
||||
_online_files_remove(_pvs_online_dir);
|
||||
_online_files_remove(_vgs_online_dir);
|
||||
_online_files_remove(_pvs_lookup_dir);
|
||||
_online_files_remove(PVS_ONLINE_DIR);
|
||||
_online_files_remove(VGS_ONLINE_DIR);
|
||||
_online_files_remove(PVS_LOOKUP_DIR);
|
||||
|
||||
unlink_searched_devnames(cmd);
|
||||
|
||||
@@ -1677,7 +1369,7 @@ static int _pvscan_cache_args(struct cmd_context *cmd, int argc, char **argv,
|
||||
dm_list_init(&pvscan_args);
|
||||
dm_list_init(&pvscan_devs);
|
||||
|
||||
cmd->pvscan_cache_single = 1;
|
||||
cmd->expect_missing_vg_device = 1;
|
||||
|
||||
/*
|
||||
* Special pvscan-specific setup steps to avoid looking
|
||||
@@ -1867,12 +1559,35 @@ static int _pvscan_cache_args(struct cmd_context *cmd, int argc, char **argv,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _get_autoactivation(struct cmd_context *cmd, int event_activation, int *skip_command)
|
||||
{
|
||||
const char *aa_str;
|
||||
|
||||
if (!(aa_str = arg_str_value(cmd, autoactivation_ARG, NULL)))
|
||||
return 1;
|
||||
|
||||
if (strcmp(aa_str, "event")) {
|
||||
log_print_pvscan(cmd, "Skip pvscan for unknown autoactivation value.");
|
||||
*skip_command = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!event_activation) {
|
||||
log_print_pvscan(cmd, "Skip pvscan for event with event_activation=0.");
|
||||
*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;
|
||||
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;
|
||||
@@ -1933,7 +1648,7 @@ int pvscan_cache_cmd(struct cmd_context *cmd, int argc, char **argv)
|
||||
|
||||
do_all = !argc && !devno_args;
|
||||
|
||||
_online_dir_setup(cmd);
|
||||
online_dir_setup(cmd);
|
||||
|
||||
if (do_all) {
|
||||
if (!_pvscan_cache_all(cmd, argc, argv, &complete_vgnames))
|
||||
@@ -1944,6 +1659,13 @@ 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 (!_get_autoactivation(cmd, event_activation, &skip_command))
|
||||
return_ECMD_FAILED;
|
||||
|
||||
if (skip_command)
|
||||
return ECMD_PROCESSED;
|
||||
|
||||
if (!_pvscan_cache_args(cmd, argc, argv, &complete_vgnames))
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
|
@@ -17,6 +17,7 @@
|
||||
#include "lib/format_text/format-text.h"
|
||||
#include "lib/label/hints.h"
|
||||
#include "lib/device/device_id.h"
|
||||
#include "lib/device/online.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <signal.h>
|
||||
|
@@ -228,7 +228,7 @@ int mirror_remove_missing(struct cmd_context *cmd,
|
||||
|
||||
|
||||
int vgchange_activate(struct cmd_context *cmd, struct volume_group *vg,
|
||||
activation_change_t activate);
|
||||
activation_change_t activate, int vg_complete_to_activate);
|
||||
|
||||
int vgchange_background_polling(struct cmd_context *cmd, struct volume_group *vg);
|
||||
|
||||
@@ -295,6 +295,4 @@ 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
|
||||
|
@@ -19,6 +19,7 @@
|
||||
struct vgchange_params {
|
||||
int lock_start_count;
|
||||
unsigned int lock_start_sanlock : 1;
|
||||
unsigned int vg_complete_to_activate : 1;
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -195,10 +196,11 @@ int vgchange_background_polling(struct cmd_context *cmd, struct volume_group *vg
|
||||
}
|
||||
|
||||
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 +221,15 @@ int vgchange_activate(struct cmd_context *cmd, struct volume_group *vg,
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (do_activate && vg_complete_to_activate) {
|
||||
dm_list_iterate_items(pvl, &vg->pvs) {
|
||||
if (!pvl->pv->dev) {
|
||||
log_print("VG %s is incomplete.", vg->name);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Safe, since we never write out new metadata here. Required for
|
||||
* partial activation to work.
|
||||
@@ -647,6 +658,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;
|
||||
@@ -701,7 +713,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) */
|
||||
@@ -722,8 +734,38 @@ 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)
|
||||
{
|
||||
const char *aa;
|
||||
|
||||
if (!(aa = arg_str_value(cmd, autoactivation_ARG, NULL)))
|
||||
return 1;
|
||||
|
||||
if (strcmp(aa, "event")) {
|
||||
log_print("Skip vgchange for unknown autoactivation value.");
|
||||
*skip_command = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!find_config_tree_bool(cmd, global_event_activation_CFG, NULL)) {
|
||||
log_print("Skip vgchange for event and event_activation=0.");
|
||||
*skip_command = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
vp->vg_complete_to_activate = 1;
|
||||
|
||||
if (!arg_is_set(cmd, nohints_ARG))
|
||||
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;
|
||||
@@ -837,6 +879,14 @@ 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;
|
||||
if (!_check_autoactivation(cmd, &vp, &skip_command))
|
||||
return ECMD_FAILED;
|
||||
if (skip_command)
|
||||
return ECMD_PROCESSED;
|
||||
}
|
||||
|
||||
if (update)
|
||||
flags |= READ_FOR_UPDATE;
|
||||
else if (arg_is_set(cmd, activate_ARG))
|
||||
@@ -847,6 +897,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);
|
||||
|
@@ -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"
|
||||
|
@@ -79,8 +79,8 @@ ENV{SYSTEMD_READY}="1"
|
||||
# TODO: adjust the output of vgchange -aay so that
|
||||
# it's better suited to appearing in the journal.
|
||||
|
||||
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"
|
||||
|
Reference in New Issue
Block a user