mirror of
git://sourceware.org/git/lvm2.git
synced 2026-01-11 00:32:47 +03:00
Compare commits
71 Commits
dev-dct-fi
...
dev-dct-no
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
556889f170 | ||
|
|
8f50c5e79b | ||
|
|
9e9f02acf0 | ||
|
|
de7892f0af | ||
|
|
a972d63c54 | ||
|
|
d5a950ca67 | ||
|
|
0c80ea8847 | ||
|
|
18f451e09e | ||
|
|
cb798ee1c1 | ||
|
|
5e428d22d9 | ||
|
|
7502f78678 | ||
|
|
bd2baeaaa6 | ||
|
|
42a16aa6f3 | ||
|
|
04fbffb116 | ||
|
|
0d67bc96fd | ||
|
|
988ea0e94c | ||
|
|
90da953fd2 | ||
|
|
65236ee722 | ||
|
|
5a78979b68 | ||
|
|
39a121ddbc | ||
|
|
47ac2659d5 | ||
|
|
26e6580dfb | ||
|
|
09a4b56895 | ||
|
|
ed1651d11f | ||
|
|
522561e64b | ||
|
|
0f71183f94 | ||
|
|
fbd8b0cf43 | ||
|
|
6a431eb242 | ||
|
|
c28541eccd | ||
|
|
d803d9202c | ||
|
|
ae54e75176 | ||
|
|
455c29b10d | ||
|
|
c42a05c3ec | ||
|
|
05d9a01351 | ||
|
|
d5402e55f6 | ||
|
|
71e7ebb3e4 | ||
|
|
36095ac374 | ||
|
|
470b967bc5 | ||
|
|
d12baba1a9 | ||
|
|
009007484b | ||
|
|
01bf8e1747 | ||
|
|
114e1cfee5 | ||
|
|
212b1fc529 | ||
|
|
b8f4ec846d | ||
|
|
e4b8726b6d | ||
|
|
5c71aa7510 | ||
|
|
5c4ce4669e | ||
|
|
fee3937002 | ||
|
|
8e61c0ad6e | ||
|
|
89c54db16c | ||
|
|
c5f998aec4 | ||
|
|
4926061d32 | ||
|
|
66f0fe57c3 | ||
|
|
5dbf316cee | ||
|
|
b945ea1c93 | ||
|
|
20c550ab10 | ||
|
|
0e0faf30e0 | ||
|
|
92e741eda0 | ||
|
|
d608837b2a | ||
|
|
73b4602f21 | ||
|
|
024ce50f06 | ||
|
|
14b68ea313 | ||
|
|
b4067e84c7 | ||
|
|
62533ae3fa | ||
|
|
5f7cb97743 | ||
|
|
f40fd88374 | ||
|
|
d558b3ad7e | ||
|
|
594f6ca970 | ||
|
|
726dd25969 | ||
|
|
6ea8d975b2 | ||
|
|
b5b0369e4d |
@@ -1,5 +1,8 @@
|
||||
Version 2.03.15 -
|
||||
===================================
|
||||
Use cache or active DM device when available with new kernels.
|
||||
Introduce function to utilize UUIDs from DM_DEVICE_LIST.
|
||||
Increase some hash table size to better support large device sets.
|
||||
|
||||
Version 2.03.14 - 20th October 2021
|
||||
===================================
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
Version 1.02.183 -
|
||||
====================================
|
||||
Unmangle UUIDs for DM_DEVICE_LIST ioctl.
|
||||
|
||||
Version 1.02.181 - 20th October 2021
|
||||
====================================
|
||||
|
||||
@@ -1576,10 +1576,8 @@ int lm_rem_lockspace_sanlock(struct lockspace *ls, int free_vg)
|
||||
goto out;
|
||||
|
||||
rv = sanlock_rem_lockspace(&lms->ss, 0);
|
||||
if (rv < 0) {
|
||||
if (rv < 0)
|
||||
log_error("S %s rem_lockspace_san error %d", ls->name, rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (free_vg) {
|
||||
/*
|
||||
|
||||
@@ -173,6 +173,16 @@ struct dm_names {
|
||||
char name[];
|
||||
};
|
||||
|
||||
struct dm_active_device {
|
||||
struct dm_list list;
|
||||
int major;
|
||||
int minor;
|
||||
char *name; /* device name */
|
||||
|
||||
uint32_t event_nr; /* valid when DM_DEVICE_LIST_HAS_EVENT_NR is set */
|
||||
char *uuid; /* valid uuid when DM_DEVICE_LIST_HAS_UUID is set */
|
||||
};
|
||||
|
||||
struct dm_versions {
|
||||
uint32_t next; /* Offset to next struct from start of this struct */
|
||||
uint32_t version[3];
|
||||
@@ -210,6 +220,25 @@ const char *dm_task_get_message_response(struct dm_task *dmt);
|
||||
*/
|
||||
const char *dm_task_get_name(const struct dm_task *dmt);
|
||||
struct dm_names *dm_task_get_names(struct dm_task *dmt);
|
||||
/*
|
||||
* Retrieve the list of devices and put them into easily accessible
|
||||
* struct dm_active_device list elements.
|
||||
* devs_features provides flag-set with used features so it's easy to check
|
||||
* whether the kernel provides i.e. UUID info together with DM names
|
||||
*/
|
||||
#define DM_DEVICE_LIST_HAS_EVENT_NR 1
|
||||
#define DM_DEVICE_LIST_HAS_UUID 2
|
||||
int dm_task_get_device_list(struct dm_task *dmt, struct dm_list **devs_list,
|
||||
unsigned *devs_features);
|
||||
/*
|
||||
* -1: no idea about uuid (not provided by DM_DEVICE_LIST ioctl)
|
||||
* 0: uuid not present
|
||||
* 1: listed and dm_active_device will be set for not NULL pointer
|
||||
*/
|
||||
int dm_device_list_find_by_uuid(struct dm_list *devs_list, const char *uuid,
|
||||
const struct dm_active_device **dev);
|
||||
/* Release all associated memory with list of active DM devices */
|
||||
void dm_device_list_destroy(struct dm_list **devs_list);
|
||||
|
||||
int dm_task_set_ro(struct dm_task *dmt);
|
||||
int dm_task_set_newname(struct dm_task *dmt, const char *newname);
|
||||
|
||||
@@ -616,8 +616,7 @@ int dm_check_version(void)
|
||||
int dm_cookie_supported(void)
|
||||
{
|
||||
return (dm_check_version() &&
|
||||
_dm_version >= 4 &&
|
||||
_dm_version_minor >= 15);
|
||||
((_dm_version == 4) ? _dm_version_minor >= 15 : _dm_version > 4));
|
||||
}
|
||||
|
||||
static int _dm_inactive_supported(void)
|
||||
@@ -755,6 +754,159 @@ struct dm_deps *dm_task_get_deps(struct dm_task *dmt)
|
||||
dmt->dmi.v4->data_start);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Round up the ptr to an 8-byte boundary.
|
||||
* Follow kernel pattern.
|
||||
*/
|
||||
#define ALIGN_MASK 7
|
||||
static size_t _align_val(size_t val)
|
||||
{
|
||||
return (val + ALIGN_MASK) & ~ALIGN_MASK;
|
||||
}
|
||||
static void *_align_ptr(void *ptr)
|
||||
{
|
||||
return (void *)_align_val((size_t)ptr);
|
||||
}
|
||||
|
||||
static int _check_has_event_nr(void) {
|
||||
static int _has_event_nr = -1;
|
||||
|
||||
if (_has_event_nr < 0)
|
||||
_has_event_nr = dm_check_version() &&
|
||||
((_dm_version == 4) ? _dm_version_minor >= 38 : _dm_version > 4);
|
||||
|
||||
return _has_event_nr;
|
||||
}
|
||||
|
||||
struct dm_device_list {
|
||||
struct dm_list list;
|
||||
unsigned count;
|
||||
unsigned features;
|
||||
struct dm_hash_table *uuids;
|
||||
};
|
||||
|
||||
int dm_task_get_device_list(struct dm_task *dmt, struct dm_list **devs_list,
|
||||
unsigned *devs_features)
|
||||
{
|
||||
struct dm_names *names, *names1;
|
||||
struct dm_active_device *dm_dev, *dm_new_dev;
|
||||
struct dm_device_list *devs;
|
||||
unsigned next = 0;
|
||||
uint32_t *event_nr;
|
||||
char *uuid_ptr;
|
||||
size_t len;
|
||||
int cnt = 0;
|
||||
|
||||
*devs_list = 0;
|
||||
*devs_features = 0;
|
||||
|
||||
if ((names = dm_task_get_names(dmt)) && names->dev) {
|
||||
names1 = names;
|
||||
if (!names->name[0])
|
||||
cnt = -1; /* -> cnt == 0 when no device is really present */
|
||||
do {
|
||||
names1 = (struct dm_names *)((char *) names1 + next);
|
||||
next = names1->next;
|
||||
++cnt;
|
||||
} while (next);
|
||||
}
|
||||
|
||||
if (!(devs = malloc(sizeof(*devs) + (cnt ? cnt * sizeof(*dm_dev) + (char*)names1 - (char*)names + 256 : 0))))
|
||||
return_0;
|
||||
|
||||
dm_list_init(&devs->list);
|
||||
devs->count = cnt;
|
||||
devs->uuids = NULL;
|
||||
|
||||
if (!cnt) {
|
||||
/* nothing in the list -> mark all features present */
|
||||
*devs_features |= (DM_DEVICE_LIST_HAS_EVENT_NR | DM_DEVICE_LIST_HAS_UUID);
|
||||
goto out; /* nothing else to do */
|
||||
}
|
||||
|
||||
dm_dev = (struct dm_active_device *) (devs + 1);
|
||||
|
||||
do {
|
||||
names = (struct dm_names *)((char *) names + next);
|
||||
|
||||
dm_dev->major = MAJOR(names->dev);
|
||||
dm_dev->minor = MINOR(names->dev);
|
||||
dm_dev->name = (char*)(dm_dev + 1);
|
||||
dm_dev->event_nr = 0;
|
||||
dm_dev->uuid = NULL;
|
||||
|
||||
strcpy(dm_dev->name, names->name);
|
||||
len = strlen(names->name) + 1;
|
||||
|
||||
dm_new_dev = _align_ptr((char*)(dm_dev + 1) + len);
|
||||
if (_check_has_event_nr()) {
|
||||
/* Hash for UUIDs with some more bits to reduce colision count */
|
||||
if (!devs->uuids && !(devs->uuids = dm_hash_create(cnt * 8))) {
|
||||
free(devs);
|
||||
return_0;
|
||||
}
|
||||
|
||||
*devs_features |= DM_DEVICE_LIST_HAS_EVENT_NR;
|
||||
event_nr = _align_ptr(names->name + len);
|
||||
dm_dev->event_nr = event_nr[0];
|
||||
|
||||
if ((event_nr[1] & DM_NAME_LIST_FLAG_HAS_UUID)) {
|
||||
*devs_features |= DM_DEVICE_LIST_HAS_UUID;
|
||||
uuid_ptr = _align_ptr(event_nr + 2);
|
||||
dm_dev->uuid = (char*) dm_new_dev;
|
||||
dm_new_dev = _align_ptr((char*)dm_new_dev + strlen(uuid_ptr) + 1);
|
||||
strcpy(dm_dev->uuid, uuid_ptr);
|
||||
if (!dm_hash_insert(devs->uuids, dm_dev->uuid, dm_dev))
|
||||
return_0; // FIXME
|
||||
#if 0
|
||||
log_debug("Active %s (%s) %d:%d event:%u",
|
||||
dm_dev->name, dm_dev->uuid,
|
||||
dm_dev->major, dm_dev->minor, dm_dev->event_nr);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
dm_list_add(&devs->list, &dm_dev->list);
|
||||
dm_dev = dm_new_dev;
|
||||
next = names->next;
|
||||
} while (next);
|
||||
|
||||
out:
|
||||
*devs_list = (struct dm_list *)devs;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int dm_device_list_find_by_uuid(struct dm_list *devs_list, const char *uuid,
|
||||
const struct dm_active_device **dev)
|
||||
{
|
||||
struct dm_device_list *devs = (struct dm_device_list *) devs_list;
|
||||
struct dm_active_device *dm_dev;
|
||||
|
||||
if (devs->uuids &&
|
||||
(dm_dev = dm_hash_lookup(devs->uuids, uuid))) {
|
||||
if (dev)
|
||||
*dev = dm_dev;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dm_device_list_destroy(struct dm_list **devs_list)
|
||||
{
|
||||
struct dm_device_list *devs = (struct dm_device_list *) *devs_list;
|
||||
|
||||
if (devs) {
|
||||
if (devs->uuids)
|
||||
dm_hash_destroy(devs->uuids);
|
||||
|
||||
free(devs);
|
||||
*devs_list = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
struct dm_names *dm_task_get_names(struct dm_task *dmt)
|
||||
{
|
||||
return (struct dm_names *) (((char *) dmt->dmi.v4) +
|
||||
@@ -1441,8 +1593,7 @@ static int _udev_complete(struct dm_task *dmt)
|
||||
static int _check_uevent_generated(struct dm_ioctl *dmi)
|
||||
{
|
||||
if (!dm_check_version() ||
|
||||
_dm_version < 4 ||
|
||||
_dm_version_minor < 17)
|
||||
((_dm_version == 4) ? _dm_version_minor < 17 : _dm_version < 4))
|
||||
/* can't check, assume uevent is generated */
|
||||
return 1;
|
||||
|
||||
@@ -1808,23 +1959,34 @@ static int _do_dm_ioctl_unmangle_string(char *str, const char *str_name,
|
||||
static int _dm_ioctl_unmangle_names(int type, struct dm_ioctl *dmi)
|
||||
{
|
||||
char buf[DM_NAME_LEN];
|
||||
struct dm_names *names;
|
||||
char buf_uuid[DM_UUID_LEN];
|
||||
struct dm_name_list *names;
|
||||
unsigned next = 0;
|
||||
char *name;
|
||||
int r = 1;
|
||||
uint32_t *event_nr;
|
||||
char *uuid_ptr;
|
||||
dm_string_mangling_t mangling_mode = dm_get_name_mangling_mode();
|
||||
|
||||
if ((name = dmi->name))
|
||||
r = _do_dm_ioctl_unmangle_string(name, "name", buf, sizeof(buf),
|
||||
dm_get_name_mangling_mode());
|
||||
r &= _do_dm_ioctl_unmangle_string(name, "name", buf, sizeof(buf),
|
||||
mangling_mode);
|
||||
|
||||
if (type == DM_DEVICE_LIST &&
|
||||
((names = ((struct dm_names *) ((char *)dmi + dmi->data_start)))) &&
|
||||
((names = ((struct dm_name_list *) ((char *)dmi + dmi->data_start)))) &&
|
||||
names->dev) {
|
||||
do {
|
||||
names = (struct dm_names *)((char *) names + next);
|
||||
r = _do_dm_ioctl_unmangle_string(names->name, "name",
|
||||
buf, sizeof(buf),
|
||||
dm_get_name_mangling_mode());
|
||||
names = (struct dm_name_list *)((char *) names + next);
|
||||
event_nr = _align_ptr(names->name + strlen(names->name) + 1);
|
||||
r &= _do_dm_ioctl_unmangle_string(names->name, "name",
|
||||
buf, sizeof(buf), mangling_mode);
|
||||
/* Unmangle also UUID within same loop */
|
||||
if (_check_has_event_nr() &&
|
||||
(event_nr[1] & DM_NAME_LIST_FLAG_HAS_UUID)) {
|
||||
uuid_ptr = _align_ptr(event_nr + 2);
|
||||
r &= _do_dm_ioctl_unmangle_string(uuid_ptr, "UUID", buf_uuid,
|
||||
sizeof(buf_uuid), mangling_mode);
|
||||
}
|
||||
next = names->next;
|
||||
} while (next);
|
||||
}
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -413,7 +413,7 @@ int add_areas_line(struct dev_manager *dm, struct lv_segment *seg,
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int device_is_usable(struct device *dev, struct dev_usable_check_params check, int *is_lv)
|
||||
int device_is_usable(struct cmd_context *cmd, struct device *dev, struct dev_usable_check_params check, int *is_lv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -486,12 +486,20 @@ int library_version(char *version, size_t size)
|
||||
|
||||
int driver_version(char *version, size_t size)
|
||||
{
|
||||
static char _vsn[80] = { 0 };
|
||||
|
||||
if (!activation())
|
||||
return 0;
|
||||
|
||||
log_very_verbose("Getting driver version");
|
||||
|
||||
return dm_driver_version(version, size);
|
||||
if (!_vsn[0] &&
|
||||
!dm_driver_version(_vsn, sizeof(_vsn)))
|
||||
return_0;
|
||||
|
||||
(void) dm_strncpy(version, _vsn, size);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int target_version(const char *target_name, uint32_t *maj,
|
||||
@@ -616,6 +624,15 @@ int target_present(struct cmd_context *cmd, const char *target_name,
|
||||
&maj, &min, &patchlevel);
|
||||
}
|
||||
|
||||
int get_device_list(const struct volume_group *vg, struct dm_list **devs,
|
||||
unsigned *devs_features)
|
||||
{
|
||||
if (!activation())
|
||||
return 0;
|
||||
|
||||
return dev_manager_get_device_list(NULL, devs, devs_features);
|
||||
}
|
||||
|
||||
/*
|
||||
* When '*info' is NULL, returns 1 only when LV is active.
|
||||
* When '*info' != NULL, returns 1 when info structure is populated.
|
||||
@@ -2374,6 +2391,7 @@ int lv_deactivate(struct cmd_context *cmd, const char *lvid_s, const struct logi
|
||||
static const struct lv_activate_opts laopts = { .skip_in_use = 1 };
|
||||
struct dm_list *snh;
|
||||
int r = 0;
|
||||
unsigned tmp_state;
|
||||
|
||||
if (!activation())
|
||||
return 1;
|
||||
@@ -2446,12 +2464,17 @@ int lv_deactivate(struct cmd_context *cmd, const char *lvid_s, const struct logi
|
||||
}
|
||||
critical_section_dec(cmd, "deactivated");
|
||||
|
||||
tmp_state = cmd->disable_dm_devs;
|
||||
cmd->disable_dm_devs = 1;
|
||||
|
||||
if (!lv_info(cmd, lv, 0, &info, 0, 0) || info.exists) {
|
||||
/* Turn into log_error, but we do not log error */
|
||||
log_debug_activation("Deactivated volume is still %s present.",
|
||||
display_lvname(lv));
|
||||
r = 0;
|
||||
}
|
||||
|
||||
cmd->disable_dm_devs = tmp_state;
|
||||
out:
|
||||
|
||||
return r;
|
||||
|
||||
@@ -106,6 +106,10 @@ int target_present(struct cmd_context *cmd, const char *target_name,
|
||||
int use_modprobe);
|
||||
int target_version(const char *target_name, uint32_t *maj,
|
||||
uint32_t *min, uint32_t *patchlevel);
|
||||
|
||||
int get_device_list(const struct volume_group *vg, struct dm_list **devs,
|
||||
unsigned *devs_features);
|
||||
|
||||
int raid4_is_supported(struct cmd_context *cmd, const struct segment_type *segtype);
|
||||
int lvm_dm_prefix_check(int major, int minor, const char *prefix);
|
||||
int list_segment_modules(struct dm_pool *mem, const struct lv_segment *seg,
|
||||
@@ -254,7 +258,7 @@ struct dev_usable_check_params {
|
||||
* Returns 1 if mapped device is not suspended, blocked or
|
||||
* is using a reserved name.
|
||||
*/
|
||||
int device_is_usable(struct device *dev, struct dev_usable_check_params check, int *is_lv);
|
||||
int device_is_usable(struct cmd_context *cmd, struct device *dev, struct dev_usable_check_params check, int *is_lv);
|
||||
|
||||
/*
|
||||
* Declaration moved here from fs.h to keep header fs.h hidden
|
||||
|
||||
@@ -138,8 +138,16 @@ static struct dm_task *_setup_task_run(int task, struct dm_info *info,
|
||||
if (!with_flush && !dm_task_no_flush(dmt))
|
||||
log_warn("WARNING: Failed to set no_flush.");
|
||||
|
||||
if (task == DM_DEVICE_TARGET_MSG)
|
||||
switch (task) {
|
||||
case DM_DEVICE_TARGET_MSG:
|
||||
return dmt; /* TARGET_MSG needs more local tweaking before task_run() */
|
||||
case DM_DEVICE_LIST:
|
||||
if (!dm_task_set_newuuid(dmt, " ")) // new uuid has no meaning here
|
||||
log_warn("WARNING: Failed to query uuid with LIST.");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!dm_task_run(dmt))
|
||||
goto_out;
|
||||
@@ -359,7 +367,8 @@ static int _info_run(const char *dlid, struct dm_info *dminfo,
|
||||
*
|
||||
* Returns: 1 if mirror should be ignored, 0 if safe to use
|
||||
*/
|
||||
static int _ignore_blocked_mirror_devices(struct device *dev,
|
||||
static int _ignore_blocked_mirror_devices(struct cmd_context *cmd,
|
||||
struct device *dev,
|
||||
uint64_t start, uint64_t length,
|
||||
char *mirror_status_str)
|
||||
{
|
||||
@@ -402,7 +411,7 @@ static int _ignore_blocked_mirror_devices(struct device *dev,
|
||||
goto_out;
|
||||
|
||||
tmp_dev->dev = MKDEV(sm->logs[0].major, sm->logs[0].minor);
|
||||
if (device_is_usable(tmp_dev, (struct dev_usable_check_params)
|
||||
if (device_is_usable(cmd, tmp_dev, (struct dev_usable_check_params)
|
||||
{ .check_empty = 1,
|
||||
.check_blocked = 1,
|
||||
.check_suspended = ignore_suspended_devices(),
|
||||
@@ -606,6 +615,60 @@ static int _ignore_frozen_raid(struct device *dev, const char *params)
|
||||
return r;
|
||||
}
|
||||
|
||||
static int _is_usable_uuid(const struct device *dev, const char *name, const char *uuid, int check_reserved, int check_lv, int *is_lv)
|
||||
{
|
||||
char *vgname, *lvname, *layer;
|
||||
char vg_name[NAME_LEN];
|
||||
|
||||
if (!check_reserved && !check_lv)
|
||||
return 1;
|
||||
|
||||
if (!strncmp(uuid, UUID_PREFIX, sizeof(UUID_PREFIX) - 1)) { /* with LVM- prefix */
|
||||
if (check_reserved) {
|
||||
/* Check internal lvm devices */
|
||||
if (strlen(uuid) > (sizeof(UUID_PREFIX) + 2 * ID_LEN)) { /* 68 with suffix */
|
||||
log_debug_activation("%s: Reserved uuid %s on internal LV device %s not usable.",
|
||||
dev_name(dev), uuid, name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Recognize some older reserved LVs just from the LV name (snapshot, pvmove...) */
|
||||
vgname = vg_name;
|
||||
if (!dm_strncpy(vg_name, name, sizeof(vg_name)) ||
|
||||
!dm_split_lvm_name(NULL, NULL, &vgname, &lvname, &layer))
|
||||
return_0;
|
||||
|
||||
/* FIXME: fails to handle dev aliases i.e. /dev/dm-5, replace with UUID suffix */
|
||||
if (lvname && (is_reserved_lvname(lvname) || *layer)) {
|
||||
log_debug_activation("%s: Reserved internal LV device %s/%s%s%s not usable.",
|
||||
dev_name(dev), vgname, lvname, *layer ? "-" : "", layer);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (check_lv) {
|
||||
/* Skip LVs */
|
||||
if (is_lv)
|
||||
*is_lv = 1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (check_reserved &&
|
||||
(!strncmp(uuid, CRYPT_TEMP, sizeof(CRYPT_TEMP) - 1) ||
|
||||
!strncmp(uuid, CRYPT_SUBDEV, sizeof(CRYPT_SUBDEV) - 1) ||
|
||||
!strncmp(uuid, STRATIS, sizeof(STRATIS) - 1))) {
|
||||
/* Skip private crypto devices */
|
||||
log_debug_activation("%s: Reserved uuid %s on %s device %s not usable.",
|
||||
dev_name(dev), uuid,
|
||||
uuid[0] == 'C' ? "crypto" : "stratis",
|
||||
name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* device_is_usable
|
||||
* @dev
|
||||
@@ -622,15 +685,14 @@ static int _ignore_frozen_raid(struct device *dev, const char *params)
|
||||
*
|
||||
* Returns: 1 if usable, 0 otherwise
|
||||
*/
|
||||
int device_is_usable(struct device *dev, struct dev_usable_check_params check, int *is_lv)
|
||||
int device_is_usable(struct cmd_context *cmd, struct device *dev, struct dev_usable_check_params check, int *is_lv)
|
||||
{
|
||||
struct dm_task *dmt;
|
||||
struct dm_info info;
|
||||
const char *name, *uuid;
|
||||
uint64_t start, length;
|
||||
char *target_type = NULL;
|
||||
char *params, *vgname, *lvname, *layer;
|
||||
char vg_name[NAME_LEN];
|
||||
char *params;
|
||||
void *next = NULL;
|
||||
int only_error_or_zero_target = 1;
|
||||
int r = 0;
|
||||
@@ -655,50 +717,9 @@ int device_is_usable(struct device *dev, struct dev_usable_check_params check, i
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (uuid && (check.check_reserved || check.check_lv)) {
|
||||
if (!strncmp(uuid, UUID_PREFIX, sizeof(UUID_PREFIX) - 1)) { /* with LVM- prefix */
|
||||
if (check.check_reserved) {
|
||||
/* Check internal lvm devices */
|
||||
if (strlen(uuid) > (sizeof(UUID_PREFIX) + 2 * ID_LEN)) { /* 68 with suffix */
|
||||
log_debug_activation("%s: Reserved uuid %s on internal LV device %s not usable.",
|
||||
dev_name(dev), uuid, name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Recognize some older reserved LVs just from the LV name (snapshot, pvmove...) */
|
||||
vgname = vg_name;
|
||||
if (!dm_strncpy(vg_name, name, sizeof(vg_name)) ||
|
||||
!dm_split_lvm_name(NULL, NULL, &vgname, &lvname, &layer))
|
||||
goto_out;
|
||||
|
||||
/* FIXME: fails to handle dev aliases i.e. /dev/dm-5, replace with UUID suffix */
|
||||
if (lvname && (is_reserved_lvname(lvname) || *layer)) {
|
||||
log_debug_activation("%s: Reserved internal LV device %s/%s%s%s not usable.",
|
||||
dev_name(dev), vgname, lvname, *layer ? "-" : "", layer);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (check.check_lv) {
|
||||
/* Skip LVs */
|
||||
if (is_lv)
|
||||
*is_lv = 1;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (check.check_reserved &&
|
||||
(!strncmp(uuid, CRYPT_TEMP, sizeof(CRYPT_TEMP) - 1) ||
|
||||
!strncmp(uuid, CRYPT_SUBDEV, sizeof(CRYPT_SUBDEV) - 1) ||
|
||||
!strncmp(uuid, STRATIS, sizeof(STRATIS) - 1))) {
|
||||
/* Skip private crypto devices */
|
||||
log_debug_activation("%s: Reserved uuid %s on %s device %s not usable.",
|
||||
dev_name(dev), uuid,
|
||||
uuid[0] == 'C' ? "crypto" : "stratis",
|
||||
name);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
if (uuid &&
|
||||
!_is_usable_uuid(dev, name, uuid, check.check_reserved, check.check_lv, is_lv))
|
||||
goto out;
|
||||
|
||||
/* FIXME Also check for mpath no paths */
|
||||
do {
|
||||
@@ -713,7 +734,7 @@ int device_is_usable(struct device *dev, struct dev_usable_check_params check, i
|
||||
log_debug_activation("%s: Scanning mirror devices is disabled.", dev_name(dev));
|
||||
goto out;
|
||||
}
|
||||
if (!_ignore_blocked_mirror_devices(dev, start,
|
||||
if (!_ignore_blocked_mirror_devices(cmd, dev, start,
|
||||
length, params)) {
|
||||
log_debug_activation("%s: Mirror device %s not usable.",
|
||||
dev_name(dev), name);
|
||||
@@ -909,6 +930,25 @@ int dev_manager_check_prefix_dm_major_minor(uint32_t major, uint32_t minor, cons
|
||||
return r;
|
||||
}
|
||||
|
||||
int dev_manager_get_device_list(const char *prefix, struct dm_list **devs, unsigned *devs_features)
|
||||
{
|
||||
struct dm_task *dmt;
|
||||
int r = 1;
|
||||
|
||||
if (!(dmt = _setup_task_run(DM_DEVICE_LIST, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0)))
|
||||
return_0;
|
||||
|
||||
if (!dm_task_get_device_list(dmt, devs, devs_features)) {
|
||||
r = 0;
|
||||
goto_out;
|
||||
}
|
||||
|
||||
out:
|
||||
dm_task_destroy(dmt);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int dev_manager_info(struct cmd_context *cmd,
|
||||
const struct logical_volume *lv, const char *layer,
|
||||
int with_open_count, int with_read_ahead, int with_name_check,
|
||||
@@ -924,6 +964,16 @@ int dev_manager_info(struct cmd_context *cmd,
|
||||
if (!(dlid = build_dm_uuid(cmd->mem, lv, layer)))
|
||||
goto_out;
|
||||
|
||||
if (!cmd->disable_dm_devs &&
|
||||
cmd->cache_dm_devs &&
|
||||
!dm_device_list_find_by_uuid(cmd->cache_dm_devs, dlid, NULL)) {
|
||||
log_debug("Cached as inactive %s.", name);
|
||||
if (dminfo)
|
||||
memset(dminfo, 0, sizeof(*dminfo));
|
||||
r = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!(r = _info(cmd, name, dlid,
|
||||
with_open_count, with_read_ahead, with_name_check,
|
||||
dminfo, read_ahead, seg_status)))
|
||||
@@ -2205,6 +2255,13 @@ static int _add_dev_to_dtree(struct dev_manager *dm, struct dm_tree *dtree,
|
||||
if (!(dlid = build_dm_uuid(dm->track_pending_delete ? dm->cmd->pending_delete_mem : dm->mem, lv, layer)))
|
||||
return_0;
|
||||
|
||||
if (!dm->cmd->disable_dm_devs &&
|
||||
dm->cmd->cache_dm_devs &&
|
||||
!dm_device_list_find_by_uuid(dm->cmd->cache_dm_devs, dlid, NULL)) {
|
||||
log_debug("Cached as not present %s.", name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!_info(dm->cmd, name, dlid, 1, 0, 0, &info, NULL, NULL))
|
||||
return_0;
|
||||
|
||||
@@ -2350,6 +2407,9 @@ static int _pool_callback(struct dm_tree_node *node,
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
dm_device_list_destroy(&cmd->cache_dm_devs); /* Cache no longer valid */
|
||||
|
||||
log_debug("Running check command on %s", mpath);
|
||||
|
||||
if (data->skip_zero) {
|
||||
@@ -3737,6 +3797,7 @@ static int _tree_action(struct dev_manager *dm, const struct logical_volume *lv,
|
||||
struct dm_tree_node *root;
|
||||
char *dlid;
|
||||
int r = 0;
|
||||
unsigned tmp_state;
|
||||
|
||||
if (action < DM_ARRAY_SIZE(_action_names))
|
||||
log_debug_activation("Creating %s%s tree for %s.",
|
||||
@@ -3756,9 +3817,17 @@ static int _tree_action(struct dev_manager *dm, const struct logical_volume *lv,
|
||||
dm->suspend = (action == SUSPEND_WITH_LOCKFS) || (action == SUSPEND);
|
||||
dm->track_external_lv_deps = 1;
|
||||
|
||||
/* ATM do not use caching for anything else then striped target.
|
||||
* And also skip for CLEAN action */
|
||||
tmp_state = dm->cmd->disable_dm_devs;
|
||||
if (!seg_is_striped_target(first_seg(lv)) || (action == CLEAN))
|
||||
dm->cmd->disable_dm_devs = 1;
|
||||
|
||||
if (!(dtree = _create_partial_dtree(dm, lv, laopts->origin_only)))
|
||||
return_0;
|
||||
|
||||
dm->cmd->disable_dm_devs = tmp_state;
|
||||
|
||||
if (!(root = dm_tree_find_node(dtree, 0, 0))) {
|
||||
log_error("Lost dependency tree root node.");
|
||||
goto out_no_root;
|
||||
|
||||
@@ -103,5 +103,7 @@ int dev_manager_device_uses_vg(struct device *dev,
|
||||
int dev_manager_remove_dm_major_minor(uint32_t major, uint32_t minor);
|
||||
|
||||
int dev_manager_check_prefix_dm_major_minor(uint32_t major, uint32_t minor, const char *prefix);
|
||||
int dev_manager_get_device_list(const char *prefix, struct dm_list **devs,
|
||||
unsigned *devs_features);
|
||||
|
||||
#endif
|
||||
|
||||
414
lib/cache/lvmcache.c
vendored
414
lib/cache/lvmcache.c
vendored
@@ -354,9 +354,11 @@ static struct lvmcache_vginfo *_vginfo_lookup(const char *vgname, const char *vg
|
||||
if (vgid_arg) {
|
||||
if ((vginfo = dm_hash_lookup(_vgid_hash, vgid))) {
|
||||
if (vgname && strcmp(vginfo->vgname, vgname)) {
|
||||
/* should never happen */
|
||||
log_error(INTERNAL_ERROR "vginfo_lookup vgid %s has two names %s %s",
|
||||
vgid, vginfo->vgname, vgname);
|
||||
log_warn("WARNING: lookup found duplicate VGID %s for VGs %s and %s.", vgid, vginfo->vgname, vgname);
|
||||
if ((vginfo = dm_hash_lookup(_vgname_hash, vgname))) {
|
||||
if (!memcmp(vginfo->vgid, vgid, ID_LEN))
|
||||
return vginfo;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
return vginfo;
|
||||
@@ -572,6 +574,16 @@ static const char *_get_pvsummary_device_id(const char *pvid_arg, const char **d
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int lvmcache_pvsummary_count(const char *vgname)
|
||||
{
|
||||
struct lvmcache_vginfo *vginfo;
|
||||
|
||||
if (!(vginfo = lvmcache_vginfo_from_vgname(vgname, NULL)))
|
||||
return_0;
|
||||
|
||||
return dm_list_size(&vginfo->pvsummaries);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if any PVs in vg->pvs have the same PVID as any
|
||||
* entries in _unused_duplicates.
|
||||
@@ -625,6 +637,148 @@ static void _warn_unused_duplicates(struct cmd_context *cmd)
|
||||
}
|
||||
}
|
||||
|
||||
static int _all_multipath_components(struct cmd_context *cmd, struct lvmcache_info *info, const char *pvid,
|
||||
struct dm_list *altdevs, struct device **dev_mpath)
|
||||
{
|
||||
struct device_list *devl;
|
||||
struct device *dev_mp = NULL;
|
||||
struct device *dev1 = NULL;
|
||||
struct device *dev;
|
||||
const char *wwid1 = NULL;
|
||||
const char *wwid;
|
||||
int diff_wwid = 0;
|
||||
int same_wwid = 0;
|
||||
int dev_is_mp;
|
||||
|
||||
*dev_mpath = NULL;
|
||||
|
||||
/* This function only makes sense with more than one dev. */
|
||||
if ((info && dm_list_empty(altdevs)) || (!info && (dm_list_size(altdevs) == 1))) {
|
||||
log_debug("Skip multipath component checks with single device for PVID %s", pvid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_debug("Checking for multipath components for duplicate PVID %s", pvid);
|
||||
|
||||
if (info) {
|
||||
dev = info->dev;
|
||||
dev_is_mp = (cmd->dev_types->device_mapper_major == MAJOR(dev->dev)) && dev_has_mpath_uuid(cmd, dev, NULL);
|
||||
|
||||
if (dev_is_mp) {
|
||||
if ((wwid1 = dev_mpath_component_wwid(cmd, dev))) {
|
||||
dev_mp = dev;
|
||||
dev1 = dev;
|
||||
}
|
||||
} else {
|
||||
if ((wwid1 = device_id_system_read(cmd, dev, DEV_ID_TYPE_SYS_WWID)))
|
||||
dev1 = dev;
|
||||
}
|
||||
}
|
||||
|
||||
dm_list_iterate_items(devl, altdevs) {
|
||||
dev = devl->dev;
|
||||
dev_is_mp = (cmd->dev_types->device_mapper_major == MAJOR(dev->dev)) && dev_has_mpath_uuid(cmd, dev, NULL);
|
||||
|
||||
if (dev_is_mp)
|
||||
wwid = dev_mpath_component_wwid(cmd, dev);
|
||||
else
|
||||
wwid = device_id_system_read(cmd, dev, DEV_ID_TYPE_SYS_WWID);
|
||||
|
||||
if (!wwid && wwid1) {
|
||||
log_debug("Different wwids for duplicate PVs %s %s %s none",
|
||||
dev_name(dev1), wwid1, dev_name(dev));
|
||||
diff_wwid++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!wwid)
|
||||
continue;
|
||||
|
||||
if (!wwid1) {
|
||||
wwid1 = wwid;
|
||||
dev1 = dev;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Different wwids indicates these are not multipath components. */
|
||||
if (strcmp(wwid1, wwid)) {
|
||||
log_debug("Different wwids for duplicate PVs %s %s %s %s",
|
||||
dev_name(dev1), wwid1, dev_name(dev), wwid);
|
||||
diff_wwid++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Different mpath devs with the same wwid shouldn't happen. */
|
||||
if (dev_is_mp && dev_mp) {
|
||||
log_print("Found multiple multipath devices for PVID %s WWID %s: %s %s",
|
||||
pvid, wwid1, dev_name(dev_mp), dev_name(dev));
|
||||
continue;
|
||||
}
|
||||
|
||||
log_debug("Same wwids for duplicate PVs %s %s", dev_name(dev1), dev_name(dev));
|
||||
same_wwid++;
|
||||
|
||||
/* Save the mpath device so it can be used as the PV. */
|
||||
if (dev_is_mp)
|
||||
dev_mp = dev;
|
||||
}
|
||||
|
||||
if (diff_wwid || !same_wwid)
|
||||
return 0;
|
||||
|
||||
if (dev_mp)
|
||||
log_debug("Found multipath device %s for PVID %s WWID %s.", dev_name(dev_mp), pvid, wwid1);
|
||||
|
||||
*dev_mpath = dev_mp;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _all_md_components(struct cmd_context *cmd, struct lvmcache_info *info, const char *pvid,
|
||||
struct dm_list *altdevs, struct device **dev_md_out)
|
||||
{
|
||||
struct device_list *devl;
|
||||
struct device *dev_md = NULL;
|
||||
struct device *dev;
|
||||
int real_dup = 0;
|
||||
|
||||
*dev_md_out = NULL;
|
||||
|
||||
/* There will often be no info struct because of the extra_md_checks function. */
|
||||
|
||||
if (info && (cmd->dev_types->md_major == MAJOR(info->dev->dev)))
|
||||
dev_md = info->dev;
|
||||
|
||||
dm_list_iterate_items(devl, altdevs) {
|
||||
dev = devl->dev;
|
||||
|
||||
if (cmd->dev_types->md_major == MAJOR(dev->dev)) {
|
||||
if (dev_md) {
|
||||
/* md devs themselves are dups */
|
||||
log_debug("Found multiple md devices for PVID %s: %s %s",
|
||||
pvid, dev_name(dev_md), dev_name(dev));
|
||||
real_dup = 1;
|
||||
break;
|
||||
} else
|
||||
dev_md = dev;
|
||||
} else {
|
||||
if (!dev_is_md_component(cmd, dev, NULL, 1)) {
|
||||
/* md dev copied to another device */
|
||||
real_dup = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (real_dup)
|
||||
return 0;
|
||||
|
||||
if (dev_md)
|
||||
log_debug("Found md device %s for PVID %s.", dev_name(dev_md), pvid);
|
||||
|
||||
*dev_md_out = dev_md;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we've found devices with the same PVID, decide which one
|
||||
* to use.
|
||||
@@ -680,6 +834,8 @@ static void _choose_duplicates(struct cmd_context *cmd,
|
||||
struct device_list *devl, *devl_safe, *devl_add, *devl_del;
|
||||
struct lvmcache_info *info;
|
||||
struct device *dev1, *dev2;
|
||||
struct device *dev_mpath, *dev_md;
|
||||
struct device *dev_drop;
|
||||
const char *device_id = NULL, *device_id_type = NULL;
|
||||
const char *idname1 = NULL, *idname2 = NULL;
|
||||
uint32_t dev1_major, dev1_minor, dev2_major, dev2_minor;
|
||||
@@ -702,6 +858,8 @@ static void _choose_duplicates(struct cmd_context *cmd,
|
||||
next:
|
||||
dm_list_init(&altdevs);
|
||||
pvid = NULL;
|
||||
dev_mpath = NULL;
|
||||
dev_md = NULL;
|
||||
|
||||
dm_list_iterate_items_safe(devl, devl_safe, &_initial_duplicates) {
|
||||
if (!pvid) {
|
||||
@@ -720,30 +878,173 @@ next:
|
||||
return;
|
||||
}
|
||||
|
||||
info = lvmcache_info_from_pvid(pvid, NULL, 0);
|
||||
|
||||
/*
|
||||
* Get rid of any md components before comparing alternatives.
|
||||
* (Since an md component can never be used, it's not an
|
||||
* option to use like other kinds of alternatives.)
|
||||
* Usually and ideally, components of md and multipath devs should have
|
||||
* been excluded by filters, and not scanned for a PV. In some unusual
|
||||
* cases the components can get through the filters, and a PV can be
|
||||
* found on them. Detecting the same PVID on both the component and
|
||||
* the md/mpath device gives us a last chance to drop the component.
|
||||
* An md/mpath component device is completely ignored, as if it had
|
||||
* been filtered, and not kept in the list unused duplicates.
|
||||
*
|
||||
* One issue related to eliminating mpath/md duplicate PVs here is
|
||||
* that it occurs after label_scan, and hints are created based
|
||||
* on what label_scan finds, so hints are disabled due to duplicate
|
||||
* PVs that are later resolved here.
|
||||
*/
|
||||
|
||||
info = lvmcache_info_from_pvid(pvid, NULL, 0);
|
||||
if (info && dev_is_md_component(cmd, info->dev, NULL, 1)) {
|
||||
/* does not go in del_cache_devs which become unused_duplicates */
|
||||
log_debug_cache("PV %s drop MD component from scan selection %s", pvid, dev_name(info->dev));
|
||||
lvmcache_del(info);
|
||||
info = NULL;
|
||||
}
|
||||
/*
|
||||
* Get rid of multipath components based on matching wwids.
|
||||
*/
|
||||
if (_all_multipath_components(cmd, info, pvid, &altdevs, &dev_mpath)) {
|
||||
if (info && dev_mpath && (info->dev != dev_mpath)) {
|
||||
/*
|
||||
* info should be dropped from lvmcache and info->dev
|
||||
* should be treated as if it had been excluded by a filter.
|
||||
* dev_mpath should be added to lvmcache by the caller.
|
||||
*/
|
||||
dev_drop = info->dev;
|
||||
|
||||
dm_list_iterate_items_safe(devl, devl_safe, &altdevs) {
|
||||
if (dev_is_md_component(cmd, devl->dev, NULL, 1)) {
|
||||
log_debug_cache("PV %s drop MD component from scan duplicates %s", pvid, dev_name(devl->dev));
|
||||
dm_list_del(&devl->list);
|
||||
/* Have caller add dev_mpath to lvmcache. */
|
||||
log_debug("Using multipath device %s for PVID %s.", dev_name(dev_mpath), pvid);
|
||||
if ((devl_add = zalloc(sizeof(*devl_add)))) {
|
||||
devl_add->dev = dev_mpath;
|
||||
dm_list_add(add_cache_devs, &devl_add->list);
|
||||
}
|
||||
|
||||
/* Remove dev_mpath from altdevs. */
|
||||
if ((devl = _get_devl_in_device_list(dev_mpath, &altdevs)))
|
||||
dm_list_del(&devl->list);
|
||||
|
||||
/* Remove info from lvmcache that came from the component dev. */
|
||||
log_debug("Ignoring multipath component %s with PVID %s (dropping info)", dev_name(dev_drop), pvid);
|
||||
lvmcache_del(info);
|
||||
info = NULL;
|
||||
|
||||
/* Make the component dev look like it was filtered. */
|
||||
cmd->filter->wipe(cmd, cmd->filter, dev_drop, NULL);
|
||||
dev_drop->flags &= ~DEV_SCAN_FOUND_LABEL;
|
||||
}
|
||||
|
||||
if (info && !dev_mpath) {
|
||||
/*
|
||||
* Only mpath component devs were found and no actual
|
||||
* multipath dev, so drop the component from lvmcache.
|
||||
*/
|
||||
dev_drop = info->dev;
|
||||
|
||||
log_debug("Ignoring multipath component %s with PVID %s (dropping info)", dev_name(dev_drop), pvid);
|
||||
lvmcache_del(info);
|
||||
info = NULL;
|
||||
|
||||
/* Make the component dev look like it was filtered. */
|
||||
cmd->filter->wipe(cmd, cmd->filter, dev_drop, NULL);
|
||||
dev_drop->flags &= ~DEV_SCAN_FOUND_LABEL;
|
||||
}
|
||||
|
||||
dm_list_iterate_items_safe(devl, devl_safe, &altdevs) {
|
||||
/*
|
||||
* The altdevs are all mpath components that should look
|
||||
* like they were filtered, they are not in lvmcache.
|
||||
*/
|
||||
dev_drop = devl->dev;
|
||||
|
||||
log_debug("Ignoring multipath component %s with PVID %s (dropping duplicate)", dev_name(dev_drop), pvid);
|
||||
dm_list_del(&devl->list);
|
||||
|
||||
cmd->filter->wipe(cmd, cmd->filter, dev_drop, NULL);
|
||||
dev_drop->flags &= ~DEV_SCAN_FOUND_LABEL;
|
||||
}
|
||||
goto next;
|
||||
}
|
||||
|
||||
if (dm_list_empty(&altdevs))
|
||||
goto next;
|
||||
/*
|
||||
* Get rid of any md components.
|
||||
*/
|
||||
if (_all_md_components(cmd, info, pvid, &altdevs, &dev_md)) {
|
||||
if (info && dev_md && (info->dev != dev_md)) {
|
||||
/*
|
||||
* info should be dropped from lvmcache and info->dev
|
||||
* should be treated as if it had been excluded by a filter.
|
||||
* dev_md should be added to lvmcache by the caller.
|
||||
* Often this info struct has been removed by
|
||||
* lvmcache_extra_md_component_checks.
|
||||
*/
|
||||
dev_drop = info->dev;
|
||||
|
||||
/* Have caller add dev_md to lvmcache. */
|
||||
log_debug("Using md device %s for PVID %s.", dev_name(dev_md), pvid);
|
||||
if ((devl_add = zalloc(sizeof(*devl_add)))) {
|
||||
devl_add->dev = dev_md;
|
||||
dm_list_add(add_cache_devs, &devl_add->list);
|
||||
}
|
||||
|
||||
/* Remove dev_md from altdevs. */
|
||||
if ((devl = _get_devl_in_device_list(dev_md, &altdevs)))
|
||||
dm_list_del(&devl->list);
|
||||
|
||||
/* Remove info from lvmcache that came from the component dev. */
|
||||
log_debug("Ignoring md component %s with PVID %s (dropping info)", dev_name(dev_drop), pvid);
|
||||
lvmcache_del(info);
|
||||
info = NULL;
|
||||
|
||||
/* Make the component dev look like it was filtered. */
|
||||
cmd->filter->wipe(cmd, cmd->filter, dev_drop, NULL);
|
||||
dev_drop->flags &= ~DEV_SCAN_FOUND_LABEL;
|
||||
}
|
||||
|
||||
if (!info && dev_md) {
|
||||
/*
|
||||
* The info struct was from a component and was dropped
|
||||
* and the actual md dev was found on initial_duplicates
|
||||
* and the caller should add it to lvmcache.
|
||||
*/
|
||||
|
||||
/* Have caller add dev_md to lvmcache. */
|
||||
log_debug("Using md device %s for PVID %s.", dev_name(dev_md), pvid);
|
||||
if ((devl_add = zalloc(sizeof(*devl_add)))) {
|
||||
devl_add->dev = dev_md;
|
||||
dm_list_add(add_cache_devs, &devl_add->list);
|
||||
}
|
||||
|
||||
/* Remove dev_md from altdevs. */
|
||||
if ((devl = _get_devl_in_device_list(dev_md, &altdevs)))
|
||||
dm_list_del(&devl->list);
|
||||
}
|
||||
|
||||
if (info && !dev_md) {
|
||||
/*
|
||||
* Only md component devs were found and no actual
|
||||
* md dev, so drop the component from lvmcache.
|
||||
*/
|
||||
dev_drop = info->dev;
|
||||
|
||||
log_debug("Ignoring md component %s with PVID %s (dropping info)", dev_name(dev_drop), pvid);
|
||||
lvmcache_del(info);
|
||||
info = NULL;
|
||||
|
||||
/* Make the component dev look like it was filtered. */
|
||||
cmd->filter->wipe(cmd, cmd->filter, dev_drop, NULL);
|
||||
dev_drop->flags &= ~DEV_SCAN_FOUND_LABEL;
|
||||
}
|
||||
|
||||
dm_list_iterate_items_safe(devl, devl_safe, &altdevs) {
|
||||
/*
|
||||
* The altdevs are all md components that should look
|
||||
* like they were filtered, they are not in lvmcache.
|
||||
*/
|
||||
dev_drop = devl->dev;
|
||||
|
||||
log_debug("Ignoring md component %s with PVID %s (dropping duplicate)", dev_name(dev_drop), pvid);
|
||||
dm_list_del(&devl->list);
|
||||
|
||||
cmd->filter->wipe(cmd, cmd->filter, dev_drop, NULL);
|
||||
dev_drop->flags &= ~DEV_SCAN_FOUND_LABEL;
|
||||
}
|
||||
goto next;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the device for the pvid that's currently in lvmcache.
|
||||
@@ -1149,6 +1450,18 @@ int lvmcache_label_reopen_vg_rw(struct cmd_context *cmd, const char *vgname, con
|
||||
* times it can be a clue that label_scan mistakenly read the pv from an md
|
||||
* component device instead of from the md device itself. So for unmatching
|
||||
* sizes, we do a full md component check on the device.
|
||||
*
|
||||
* It might be nice to do this checking in the filter (when passes_filter is
|
||||
* called after the initial read), but that doesn't work because passes_filter
|
||||
* is called before _text_read so metadata/pvsummary info is not yet available
|
||||
* which this function uses.
|
||||
*
|
||||
* The unique value of this function is that it can eliminate md components
|
||||
* without there being duplicate PVs. But, there will often be duplicate PVs,
|
||||
* handled by _all_md_components(), where other devs with the same pvid will be
|
||||
* in _initial_duplicates. One could be the md device itself which will be
|
||||
* added to lvmcache by choose_duplicates, and other duplicates that are
|
||||
* components will be dropped.
|
||||
*/
|
||||
|
||||
void lvmcache_extra_md_component_checks(struct cmd_context *cmd)
|
||||
@@ -1210,7 +1523,8 @@ void lvmcache_extra_md_component_checks(struct cmd_context *cmd)
|
||||
*/
|
||||
if (pvsize && devsize && (pvsize != devsize))
|
||||
do_check_size = 1;
|
||||
if (device_hint && !strncmp(device_hint, "/dev/md", 7))
|
||||
if (device_hint && !strncmp(device_hint, "/dev/md", 7) &&
|
||||
(MAJOR(info->dev->dev) != cmd->dev_types->md_major))
|
||||
do_check_name = 1;
|
||||
|
||||
if (!do_check_size && !do_check_name)
|
||||
@@ -1240,11 +1554,11 @@ void lvmcache_extra_md_component_checks(struct cmd_context *cmd)
|
||||
device_hint ?: "none", dev_name(dev));
|
||||
|
||||
if (dev_is_md_component(cmd, dev, NULL, 1)) {
|
||||
log_debug("dropping PV from md component %s", dev_name(dev));
|
||||
log_debug("Ignoring PV from md component %s with PVID %s (metadata %s %llu)",
|
||||
dev_name(dev), dev->pvid, device_hint ?: "none", (unsigned long long)pvsize);
|
||||
dev->flags &= ~DEV_SCAN_FOUND_LABEL;
|
||||
/* lvmcache_del will also delete vginfo if info was last one */
|
||||
lvmcache_del(info);
|
||||
lvmcache_del_dev_from_duplicates(dev);
|
||||
cmd->filter->wipe(cmd, cmd->filter, dev, NULL);
|
||||
}
|
||||
}
|
||||
@@ -1572,7 +1886,17 @@ static int _lvmcache_update_vgname(struct cmd_context *cmd,
|
||||
|
||||
_drop_vginfo(info, info->vginfo);
|
||||
|
||||
if (!(vginfo = lvmcache_vginfo_from_vgid(vgid))) {
|
||||
vginfo = lvmcache_vginfo_from_vgid(vgid);
|
||||
if (vginfo && strcmp(vginfo->vgname, vgname)) {
|
||||
log_warn("WARNING: fix duplicate VGID %s for VGs %s and %s (see vgchange -u).", vgid_dashed, vgname, vginfo->vgname);
|
||||
vginfo = lvmcache_vginfo_from_vgname(vgname, NULL);
|
||||
if (vginfo && memcmp(vginfo->vgid, vgid, ID_LEN)) {
|
||||
log_error("Ignoring %s with conflicting VG info %s %s.", dev_name(info->dev), vgid_dashed, vgname);
|
||||
return_0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!vginfo) {
|
||||
/*
|
||||
* Create a vginfo struct for this VG and put the vginfo
|
||||
* into the hash table.
|
||||
@@ -1927,50 +2251,6 @@ int lvmcache_update_vgname_and_id(struct cmd_context *cmd, struct lvmcache_info
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME: quit trying to mirror changes that a command is making into lvmcache.
|
||||
*
|
||||
* First, it's complicated and hard to ensure it's done correctly in every case
|
||||
* (it would be much easier and safer to just toss out what's in lvmcache and
|
||||
* reread the info to recreate it from scratch instead of trying to make sure
|
||||
* every possible discrete state change is correct.)
|
||||
*
|
||||
* Second, it's unnecessary if commands just use the vg they are modifying
|
||||
* rather than also trying to get info from lvmcache. The lvmcache state
|
||||
* should be populated by label_scan, used to perform vg_read's, and then
|
||||
* ignored (or dropped so it can't be used).
|
||||
*
|
||||
* lvmcache info is already used very little after a command begins its
|
||||
* operation. The code that's supposed to keep the lvmcache in sync with
|
||||
* changes being made to disk could be half wrong and we wouldn't know it.
|
||||
* That creates a landmine for someone who might try to use a bit of it that
|
||||
* isn't being updated correctly.
|
||||
*/
|
||||
|
||||
int lvmcache_update_vg_from_write(struct volume_group *vg)
|
||||
{
|
||||
char vgid[ID_LEN + 1] __attribute__((aligned(8))) = { 0 };
|
||||
struct pv_list *pvl;
|
||||
struct lvmcache_info *info;
|
||||
struct lvmcache_vgsummary vgsummary = {
|
||||
.vgname = vg->name,
|
||||
.vgstatus = vg->status,
|
||||
.system_id = vg->system_id,
|
||||
.lock_type = vg->lock_type
|
||||
};
|
||||
|
||||
memcpy(vgid, &vg->id, ID_LEN);
|
||||
memcpy(vgsummary.vgid, vgid, ID_LEN);
|
||||
|
||||
dm_list_iterate_items(pvl, &vg->pvs) {
|
||||
if ((info = lvmcache_info_from_pv_id(&pvl->pv->id, pvl->pv->dev, 0)) &&
|
||||
!lvmcache_update_vgname_and_id(vg->cmd, info, &vgsummary))
|
||||
return_0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* The lvmcache representation of a VG after label_scan can be incorrect
|
||||
* because the label_scan does not use the full VG metadata to construct
|
||||
|
||||
3
lib/cache/lvmcache.h
vendored
3
lib/cache/lvmcache.h
vendored
@@ -84,7 +84,6 @@ void lvmcache_del_dev(struct device *dev);
|
||||
int lvmcache_update_vgname_and_id(struct cmd_context *cmd, struct lvmcache_info *info,
|
||||
struct lvmcache_vgsummary *vgsummary);
|
||||
int lvmcache_update_vg_from_read(struct volume_group *vg, unsigned precommitted);
|
||||
int lvmcache_update_vg_from_write(struct volume_group *vg);
|
||||
|
||||
void lvmcache_lock_vgname(const char *vgname, int read_only);
|
||||
void lvmcache_unlock_vgname(const char *vgname);
|
||||
@@ -229,4 +228,6 @@ void lvmcache_extra_md_component_checks(struct cmd_context *cmd);
|
||||
|
||||
unsigned int lvmcache_vg_info_count(void);
|
||||
|
||||
int lvmcache_pvsummary_count(const char *vgname);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -2041,8 +2041,6 @@ void destroy_toolcontext(struct cmd_context *cmd)
|
||||
_destroy_segtypes(&cmd->segtypes);
|
||||
_destroy_formats(cmd, &cmd->formats);
|
||||
_destroy_filters(cmd);
|
||||
if (cmd->mem)
|
||||
dm_pool_destroy(cmd->mem);
|
||||
devices_file_exit(cmd);
|
||||
dev_cache_exit();
|
||||
_destroy_dev_types(cmd);
|
||||
@@ -2050,16 +2048,11 @@ void destroy_toolcontext(struct cmd_context *cmd)
|
||||
|
||||
if ((cft_cmdline = remove_config_tree_by_source(cmd, CONFIG_STRING)))
|
||||
config_destroy(cft_cmdline);
|
||||
_destroy_config(cmd);
|
||||
|
||||
if (cmd->cft_def_hash)
|
||||
dm_hash_destroy(cmd->cft_def_hash);
|
||||
|
||||
if (cmd->libmem)
|
||||
dm_pool_destroy(cmd->libmem);
|
||||
|
||||
if (cmd->pending_delete_mem)
|
||||
dm_pool_destroy(cmd->pending_delete_mem);
|
||||
dm_device_list_destroy(&cmd->cache_dm_devs);
|
||||
#ifndef VALGRIND_POOL
|
||||
if (cmd->linebuffer) {
|
||||
/* Reset stream buffering to defaults */
|
||||
@@ -2084,7 +2077,7 @@ void destroy_toolcontext(struct cmd_context *cmd)
|
||||
free(cmd->linebuffer);
|
||||
}
|
||||
#endif
|
||||
free(cmd);
|
||||
destroy_config_context(cmd);
|
||||
|
||||
lvmpolld_disconnect();
|
||||
|
||||
|
||||
@@ -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;
|
||||
@@ -201,6 +201,9 @@ 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;
|
||||
unsigned online_vg_file_removed:1;
|
||||
unsigned disable_dm_devs:1; /* temporarily disable use of dm devs cache */
|
||||
|
||||
/*
|
||||
* Devices and filtering.
|
||||
@@ -212,6 +215,8 @@ struct cmd_context {
|
||||
const char *devicesfile; /* from --devicesfile option */
|
||||
struct dm_list deviceslist; /* from --devices option, struct dm_str_list */
|
||||
|
||||
struct dm_list *cache_dm_devs; /* cache with UUIDs from DM_DEVICE_LIST (when available) */
|
||||
|
||||
/*
|
||||
* Configuration.
|
||||
*/
|
||||
|
||||
@@ -1167,7 +1167,7 @@ int config_def_check(struct cft_check_handle *handle)
|
||||
*vp = 0;
|
||||
*rp = 0;
|
||||
if (!handle->cmd->cft_def_hash) {
|
||||
if (!(handle->cmd->cft_def_hash = dm_hash_create(60))) {
|
||||
if (!(handle->cmd->cft_def_hash = dm_hash_create(500))) {
|
||||
log_error("Failed to create configuration definition hash.");
|
||||
r = 0; goto out;
|
||||
}
|
||||
|
||||
@@ -1119,16 +1119,10 @@ cfg(global_lvdisplay_shows_full_device_path_CFG, "lvdisplay_shows_full_device_pa
|
||||
"was never a valid path in the /dev filesystem.\n")
|
||||
|
||||
cfg(global_event_activation_CFG, "event_activation", global_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_BOOL, 1, vsn(2, 3, 1), 0, 0, NULL,
|
||||
"Activate LVs based on system-generated device events.\n"
|
||||
"When a PV appears on the system, a system-generated uevent triggers\n"
|
||||
"the lvm2-pvscan service which runs the pvscan --cache -aay command.\n"
|
||||
"If the new PV completes a VG, pvscan autoactivates LVs in the VG.\n"
|
||||
"When event_activation is disabled, the lvm2-activation services are\n"
|
||||
"generated and run at fixed points during system startup. These\n"
|
||||
"services run vgchange -aay to autoactivate LVs in VGs that happen\n"
|
||||
"to be present at that point in time.\n"
|
||||
"See the --setautoactivation option or the auto_activation_volume_list\n"
|
||||
"setting to configure autoactivation for specific VGs or LVs.\n")
|
||||
"Disable event based autoactivation commands. WARNING: setting this to zero\n"
|
||||
"no longer enables static autoactivation services, and a machine may fail to\n"
|
||||
"boot if this is set to zero.\n"
|
||||
"Static autoactivation services are no longer provided by lvm.\n")
|
||||
|
||||
cfg(global_use_lvmetad_CFG, "use_lvmetad", global_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_BOOL, 0, vsn(2, 2, 93), 0, vsn(2, 3, 0), NULL,
|
||||
NULL)
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -1246,7 +1246,7 @@ int dev_cache_init(struct cmd_context *cmd)
|
||||
if (!(_cache.mem = dm_pool_create("dev_cache", 10 * 1024)))
|
||||
return_0;
|
||||
|
||||
if (!(_cache.names = dm_hash_create(120)) ||
|
||||
if (!(_cache.names = dm_hash_create(1020)) ||
|
||||
!(_cache.vgid_index = dm_hash_create(30)) ||
|
||||
!(_cache.lvid_index = dm_hash_create(29))) {
|
||||
dm_pool_destroy(_cache.mem);
|
||||
@@ -2056,12 +2056,12 @@ int setup_device(struct cmd_context *cmd, const char *devname)
|
||||
}
|
||||
|
||||
/*
|
||||
* pvscan --cache is specialized/optimized to look only at command args,
|
||||
* autoactivation is specialized/optimized to look only at command args,
|
||||
* so this just sets up the devices file, then individual devices are
|
||||
* added to dev-cache and matched with device_ids later in pvscan.
|
||||
* added to dev-cache and matched with device_ids.
|
||||
*/
|
||||
|
||||
int setup_devices_for_pvscan_cache(struct cmd_context *cmd)
|
||||
int setup_devices_for_online_autoactivation(struct cmd_context *cmd)
|
||||
{
|
||||
if (cmd->enable_devices_list) {
|
||||
if (!_setup_devices_list(cmd))
|
||||
@@ -2102,7 +2102,7 @@ int setup_devices_for_pvscan_cache(struct cmd_context *cmd)
|
||||
static char *_get_devname_from_devno(struct cmd_context *cmd, dev_t devno)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
char devname[PATH_MAX];
|
||||
char devname[PATH_MAX] = { 0 };
|
||||
char namebuf[NAME_LEN];
|
||||
char line[1024];
|
||||
int major = MAJOR(devno);
|
||||
@@ -2114,6 +2114,9 @@ static char *_get_devname_from_devno(struct cmd_context *cmd, dev_t devno)
|
||||
struct dirent *dirent;
|
||||
FILE *fp;
|
||||
|
||||
if (!devno)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* $ ls /sys/dev/block/8:0/device/block/
|
||||
* sda
|
||||
@@ -2125,7 +2128,7 @@ static char *_get_devname_from_devno(struct cmd_context *cmd, dev_t devno)
|
||||
}
|
||||
|
||||
if (!(dir = opendir(path)))
|
||||
return NULL;
|
||||
goto try_partition;
|
||||
|
||||
while ((dirent = readdir(dir))) {
|
||||
if (dirent->d_name[0] == '.')
|
||||
@@ -2164,7 +2167,7 @@ static char *_get_devname_from_devno(struct cmd_context *cmd, dev_t devno)
|
||||
}
|
||||
|
||||
if (devname[0]) {
|
||||
log_debug("Found %s for %d:%d from sys", devname, major, minor);
|
||||
log_debug("Found %s for %d:%d from sys dm", devname, major, minor);
|
||||
return _strdup(devname);
|
||||
}
|
||||
return NULL;
|
||||
@@ -2175,6 +2178,7 @@ static char *_get_devname_from_devno(struct cmd_context *cmd, dev_t devno)
|
||||
* major minor #blocks name
|
||||
*/
|
||||
|
||||
try_partition:
|
||||
if (!(fp = fopen("/proc/partitions", "r")))
|
||||
return NULL;
|
||||
|
||||
@@ -2240,3 +2244,55 @@ int setup_devno_in_dev_cache(struct cmd_context *cmd, dev_t devno)
|
||||
return setup_devname_in_dev_cache(cmd, devname);
|
||||
}
|
||||
|
||||
struct device *setup_dev_in_dev_cache(struct cmd_context *cmd, dev_t devno, const char *devname)
|
||||
{
|
||||
struct device *dev;
|
||||
struct stat buf;
|
||||
int major = (int)MAJOR(devno);
|
||||
int minor = (int)MINOR(devno);
|
||||
|
||||
if (devname) {
|
||||
if (stat(devname, &buf) < 0) {
|
||||
log_error("Cannot access device %s for %d:%d.", devname, major, minor);
|
||||
if (!devno)
|
||||
return_NULL;
|
||||
if (!(devname = _get_devname_from_devno(cmd, devno))) {
|
||||
log_error("No device name found from %d:%d.", major, minor);
|
||||
return_NULL;
|
||||
}
|
||||
if (stat(devname, &buf) < 0) {
|
||||
log_error("Cannot access device %s from %d:%d.", devname, major, minor);
|
||||
return_NULL;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!(devname = _get_devname_from_devno(cmd, devno))) {
|
||||
log_error("No device name found from %d:%d.", major, minor);
|
||||
return_NULL;
|
||||
}
|
||||
if (stat(devname, &buf) < 0) {
|
||||
log_error("Cannot access device %s from %d:%d.", devname, major, minor);
|
||||
return_NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!S_ISBLK(buf.st_mode)) {
|
||||
log_error("Invaild device type %s.", devname);
|
||||
return_NULL;
|
||||
}
|
||||
|
||||
if (devno && (buf.st_rdev != devno)) {
|
||||
log_warn("Found %s devno %d:%d expected %d:%d.", devname,
|
||||
(int)MAJOR(buf.st_rdev), (int)MINOR(buf.st_rdev), major, minor);
|
||||
}
|
||||
|
||||
if (!_insert_dev(devname, buf.st_rdev))
|
||||
return_NULL;
|
||||
|
||||
if (!(dev = (struct device *) dm_hash_lookup(_cache.names, devname))) {
|
||||
log_error("Device lookup failed for %d:%d %s", major, minor, devname);
|
||||
return_NULL;
|
||||
}
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
@@ -79,9 +79,9 @@ int setup_devices_file(struct cmd_context *cmd);
|
||||
int setup_devices(struct cmd_context *cmd);
|
||||
int setup_device(struct cmd_context *cmd, const char *devname);
|
||||
|
||||
/* Normal device setup functions are split up for pvscan optimization. */
|
||||
int setup_devices_for_pvscan_cache(struct cmd_context *cmd);
|
||||
int setup_devices_for_online_autoactivation(struct cmd_context *cmd);
|
||||
int setup_devname_in_dev_cache(struct cmd_context *cmd, const char *devname);
|
||||
int setup_devno_in_dev_cache(struct cmd_context *cmd, dev_t devno);
|
||||
struct device *setup_dev_in_dev_cache(struct cmd_context *cmd, dev_t devno, const char *devname);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -482,3 +482,74 @@ found:
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *dev_mpath_component_wwid(struct cmd_context *cmd, struct device *dev)
|
||||
{
|
||||
char slaves_path[PATH_MAX];
|
||||
char wwid_path[PATH_MAX];
|
||||
char sysbuf[PATH_MAX] = { 0 };
|
||||
char *slave_name;
|
||||
const char *wwid = NULL;
|
||||
struct stat info;
|
||||
DIR *dr;
|
||||
struct dirent *de;
|
||||
|
||||
/* /sys/dev/block/253:7/slaves/sda/device/wwid */
|
||||
|
||||
if (dm_snprintf(slaves_path, sizeof(slaves_path), "%s/dev/block/%d:%d/slaves",
|
||||
dm_sysfs_dir(), (int)MAJOR(dev->dev), (int)MINOR(dev->dev)) < 0) {
|
||||
log_warn("Sysfs path to check mpath components is too long.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (stat(slaves_path, &info))
|
||||
return NULL;
|
||||
|
||||
if (!S_ISDIR(info.st_mode)) {
|
||||
log_warn("Path %s is not a directory.", slaves_path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Get wwid from first component */
|
||||
|
||||
if (!(dr = opendir(slaves_path))) {
|
||||
log_debug("Device %s has no slaves dir", dev_name(dev));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while ((de = readdir(dr))) {
|
||||
if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
|
||||
continue;
|
||||
|
||||
/* slave_name "sda" */
|
||||
slave_name = de->d_name;
|
||||
|
||||
/* read /sys/block/sda/device/wwid */
|
||||
|
||||
if (dm_snprintf(wwid_path, sizeof(wwid_path), "%s/block/%s/device/wwid",
|
||||
dm_sysfs_dir(), slave_name) < 0) {
|
||||
log_warn("Failed to create sysfs wwid path for %s", slave_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
get_sysfs_value(wwid_path, sysbuf, sizeof(sysbuf), 0);
|
||||
if (!sysbuf[0])
|
||||
continue;
|
||||
|
||||
if (strstr(sysbuf, "scsi_debug")) {
|
||||
int i;
|
||||
for (i = 0; i < strlen(sysbuf); i++) {
|
||||
if (sysbuf[i] == ' ')
|
||||
sysbuf[i] = '_';
|
||||
}
|
||||
}
|
||||
|
||||
if ((wwid = dm_pool_strdup(cmd->mem, sysbuf)))
|
||||
break;
|
||||
}
|
||||
if (closedir(dr))
|
||||
stack;
|
||||
|
||||
return wwid;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -63,6 +63,8 @@ int dev_is_swap(struct cmd_context *cmd, struct device *dev, uint64_t *signature
|
||||
int dev_is_luks(struct cmd_context *cmd, struct device *dev, uint64_t *signature, int full);
|
||||
int dasd_is_cdl_formatted(struct device *dev);
|
||||
|
||||
const char *dev_mpath_component_wwid(struct cmd_context *cmd, struct device *dev);
|
||||
|
||||
int dev_is_lvm1(struct device *dev, char *buf, int buflen);
|
||||
int dev_is_pool(struct device *dev, char *buf, int buflen);
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ static const dev_known_type_t _dev_known_types[] = {
|
||||
{"ubd", 16, "User-mode virtual block device"},
|
||||
{"ataraid", 16, "ATA Raid"},
|
||||
{"drbd", 16, "Distributed Replicated Block Device (DRBD)"},
|
||||
{"rbd", 16, "Ceph rados object as a Linux block device"},
|
||||
{"emcpower", 16, "EMC Powerpath"},
|
||||
{"power2", 16, "EMC Powerpath"},
|
||||
{"i2o_block", 16, "i2o Block Disk"},
|
||||
|
||||
@@ -74,6 +74,8 @@ void unlink_searched_devnames(struct cmd_context *cmd)
|
||||
|
||||
if (unlink(_searched_file))
|
||||
log_debug("unlink %s errno %d", _searched_file, errno);
|
||||
else
|
||||
log_debug("unlink %s", _searched_file);
|
||||
}
|
||||
|
||||
static int _searched_devnames_exists(struct cmd_context *cmd)
|
||||
@@ -241,7 +243,7 @@ static int _dm_uuid_has_prefix(char *sysbuf, const char *prefix)
|
||||
}
|
||||
|
||||
/* the dm uuid uses the wwid of the underlying dev */
|
||||
static int _dev_has_mpath_uuid(struct cmd_context *cmd, struct device *dev, const char **idname_out)
|
||||
int dev_has_mpath_uuid(struct cmd_context *cmd, struct device *dev, const char **idname_out)
|
||||
{
|
||||
char sysbuf[PATH_MAX] = { 0 };
|
||||
const char *idname;
|
||||
@@ -302,6 +304,7 @@ const char *device_id_system_read(struct cmd_context *cmd, struct device *dev, u
|
||||
{
|
||||
char sysbuf[PATH_MAX] = { 0 };
|
||||
const char *idname = NULL;
|
||||
int i;
|
||||
|
||||
if (idtype == DEV_ID_TYPE_SYS_WWID) {
|
||||
read_sys_block(cmd, dev, "device/wwid", sysbuf, sizeof(sysbuf));
|
||||
@@ -309,10 +312,6 @@ const char *device_id_system_read(struct cmd_context *cmd, struct device *dev, u
|
||||
if (!sysbuf[0])
|
||||
read_sys_block(cmd, dev, "wwid", sysbuf, sizeof(sysbuf));
|
||||
|
||||
/* scsi_debug wwid begins "t10.Linux scsi_debug ..." */
|
||||
if (strstr(sysbuf, "scsi_debug"))
|
||||
sysbuf[0] = '\0';
|
||||
|
||||
/* qemu wwid begins "t10.ATA QEMU HARDDISK ..." */
|
||||
if (strstr(sysbuf, "QEMU HARDDISK"))
|
||||
sysbuf[0] = '\0';
|
||||
@@ -353,6 +352,11 @@ const char *device_id_system_read(struct cmd_context *cmd, struct device *dev, u
|
||||
return idname;
|
||||
}
|
||||
|
||||
for (i = 0; i < strlen(sysbuf); i++) {
|
||||
if (isblank(sysbuf[i]) || isspace(sysbuf[i]) || iscntrl(sysbuf[i]))
|
||||
sysbuf[i] = '_';
|
||||
}
|
||||
|
||||
if (!sysbuf[0])
|
||||
goto_bad;
|
||||
|
||||
@@ -361,7 +365,6 @@ const char *device_id_system_read(struct cmd_context *cmd, struct device *dev, u
|
||||
|
||||
return idname;
|
||||
bad:
|
||||
log_debug("No idtype %s for %s", idtype_to_str(idtype), dev_name(dev));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -373,20 +376,40 @@ static int _dev_has_stable_id(struct cmd_context *cmd, struct device *dev)
|
||||
{
|
||||
char sysbuf[PATH_MAX] = { 0 };
|
||||
struct dev_id *id;
|
||||
const char *idname;
|
||||
|
||||
/*
|
||||
* An idtype other than DEVNAME is stable, i.e. it doesn't change after
|
||||
* reboot or device reattach.
|
||||
* An id on dev->ids with idtype set and !idname means that idtype does
|
||||
* not exist for the dev. (Optimization to avoid repeated negative
|
||||
* system_read.)
|
||||
*/
|
||||
dm_list_iterate_items(id, &dev->ids) {
|
||||
if (id->idtype != DEV_ID_TYPE_DEVNAME)
|
||||
if ((id->idtype != DEV_ID_TYPE_DEVNAME) && id->idname)
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (read_sys_block(cmd, dev, "device/wwid", sysbuf, sizeof(sysbuf)))
|
||||
return 1;
|
||||
/*
|
||||
* Use device_id_system_read() instead of read_sys_block() when
|
||||
* system_read ignores some values from sysfs.
|
||||
*/
|
||||
|
||||
if (read_sys_block(cmd, dev, "wwid", sysbuf, sizeof(sysbuf)))
|
||||
if ((idname = device_id_system_read(cmd, dev, DEV_ID_TYPE_SYS_WWID))) {
|
||||
free((void*)idname);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (read_sys_block(cmd, dev, "device/serial", sysbuf, sizeof(sysbuf)))
|
||||
if ((idname = device_id_system_read(cmd, dev, DEV_ID_TYPE_SYS_SERIAL))) {
|
||||
free((void*)idname);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((MAJOR(dev->dev) == cmd->dev_types->loop_major) &&
|
||||
(idname = device_id_system_read(cmd, dev, DEV_ID_TYPE_LOOP_FILE))) {
|
||||
free((void*)idname);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((MAJOR(dev->dev) == cmd->dev_types->device_mapper_major)) {
|
||||
if (!read_sys_block(cmd, dev, "dm/uuid", sysbuf, sizeof(sysbuf)))
|
||||
@@ -404,9 +427,6 @@ static int _dev_has_stable_id(struct cmd_context *cmd, struct device *dev)
|
||||
read_sys_block(cmd, dev, "md/uuid", sysbuf, sizeof(sysbuf)))
|
||||
return 1;
|
||||
|
||||
if ((MAJOR(dev->dev) == cmd->dev_types->loop_major) &&
|
||||
read_sys_block(cmd, dev, "loop/backing_file", sysbuf, sizeof(sysbuf)))
|
||||
return 1;
|
||||
out:
|
||||
/* DEV_ID_TYPE_DEVNAME would be used for this dev. */
|
||||
return 0;
|
||||
@@ -671,6 +691,9 @@ int device_ids_write(struct cmd_context *cmd)
|
||||
cmd->enable_devices_file = 1;
|
||||
}
|
||||
|
||||
if (test_mode())
|
||||
return 1;
|
||||
|
||||
if (_devices_file_version[0]) {
|
||||
if (sscanf(_devices_file_version, "%u.%u.%u", &df_major, &df_minor, &df_counter) != 3) {
|
||||
/* don't update a file we can't parse */
|
||||
@@ -778,9 +801,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("Devices file update skipped.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -928,6 +950,10 @@ int device_id_add(struct cmd_context *cmd, struct device *dev, const char *pvid_
|
||||
struct dev_use *du, *update_du = NULL, *du_dev, *du_pvid, *du_devname, *du_devid;
|
||||
struct dev_id *id;
|
||||
int found_id = 0;
|
||||
int part = 0;
|
||||
|
||||
if (!dev_get_partition_number(dev, &part))
|
||||
return_0;
|
||||
|
||||
/*
|
||||
* When enable_devices_file=0 and pending_devices_file=1 we let
|
||||
@@ -946,10 +972,6 @@ int device_id_add(struct cmd_context *cmd, struct device *dev, const char *pvid_
|
||||
*/
|
||||
memcpy(&pvid, pvid_arg, ID_LEN);
|
||||
|
||||
du_dev = get_du_for_dev(cmd, dev);
|
||||
du_pvid = get_du_for_pvid(cmd, pvid);
|
||||
du_devname = _get_du_for_devname(cmd, dev_name(dev));
|
||||
|
||||
/*
|
||||
* Choose the device_id type for the device being added.
|
||||
*
|
||||
@@ -981,7 +1003,7 @@ int device_id_add(struct cmd_context *cmd, struct device *dev, const char *pvid_
|
||||
}
|
||||
|
||||
if (MAJOR(dev->dev) == cmd->dev_types->device_mapper_major) {
|
||||
if (_dev_has_mpath_uuid(cmd, dev, &idname)) {
|
||||
if (dev_has_mpath_uuid(cmd, dev, &idname)) {
|
||||
idtype = DEV_ID_TYPE_MPATH_UUID;
|
||||
goto id_done;
|
||||
}
|
||||
@@ -1065,6 +1087,9 @@ id_done:
|
||||
idtype = 0;
|
||||
|
||||
/*
|
||||
* "dev" is the device we are adding.
|
||||
* "id" is the device_id it's using, set in dev->id.
|
||||
*
|
||||
* Update the cmd->use_devices list for the new device. The
|
||||
* use_devices list will be used to update the devices file.
|
||||
*
|
||||
@@ -1076,23 +1101,57 @@ id_done:
|
||||
* those other entries to fix any incorrect info.
|
||||
*/
|
||||
|
||||
/* Is there already an entry matched to this device? */
|
||||
du_dev = get_du_for_dev(cmd, dev);
|
||||
|
||||
/* Is there already an entry matched to this device's pvid? */
|
||||
du_pvid = get_du_for_pvid(cmd, pvid);
|
||||
|
||||
/* Is there already an entry using this device's name? */
|
||||
du_devname = _get_du_for_devname(cmd, dev_name(dev));
|
||||
|
||||
/* Is there already an entry using the device_id for this device? */
|
||||
du_devid = _get_du_for_device_id(cmd, id->idtype, id->idname);
|
||||
|
||||
if (du_dev)
|
||||
log_debug("device_id_add %s pvid %s matches du_dev %p dev %s",
|
||||
log_debug("device_id_add %s pvid %s matches entry %p dev %s",
|
||||
dev_name(dev), pvid, du_dev, dev_name(du_dev->dev));
|
||||
if (du_pvid)
|
||||
log_debug("device_id_add %s pvid %s matches du_pvid %p dev %s pvid %s",
|
||||
log_debug("device_id_add %s pvid %s matches entry %p dev %s with same pvid %s",
|
||||
dev_name(dev), pvid, du_pvid, du_pvid->dev ? dev_name(du_pvid->dev) : ".",
|
||||
du_pvid->pvid);
|
||||
if (du_devid)
|
||||
log_debug("device_id_add %s pvid %s matches du_devid %p dev %s pvid %s",
|
||||
log_debug("device_id_add %s pvid %s matches entry %p dev %s with same device_id %d %s",
|
||||
dev_name(dev), pvid, du_devid, du_devid->dev ? dev_name(du_devid->dev) : ".",
|
||||
du_devid->pvid);
|
||||
du_devid->idtype, du_devid->idname);
|
||||
if (du_devname)
|
||||
log_debug("device_id_add %s pvid %s matches du_devname %p dev %s pvid %s",
|
||||
log_debug("device_id_add %s pvid %s matches entry %p dev %s with same devname %s",
|
||||
dev_name(dev), pvid, du_devname, du_devname->dev ? dev_name(du_devname->dev) : ".",
|
||||
du_devname->pvid);
|
||||
du_devname->devname);
|
||||
|
||||
if (du_pvid && (du_pvid->dev != dev))
|
||||
log_warn("WARNING: adding device %s with PVID %s which is already used for %s.",
|
||||
dev_name(dev), pvid, du_pvid->dev ? dev_name(du_pvid->dev) : "missing device");
|
||||
|
||||
if (du_devid && (du_devid->dev != dev)) {
|
||||
if (!du_devid->dev) {
|
||||
log_warn("WARNING: adding device %s with idname %s which is already used for missing device.",
|
||||
dev_name(dev), id->idname);
|
||||
} else {
|
||||
int ret1, ret2;
|
||||
dev_t devt1, devt2;
|
||||
/* Check if both entries are partitions of the same device. */
|
||||
ret1 = dev_get_primary_dev(cmd->dev_types, dev, &devt1);
|
||||
ret2 = dev_get_primary_dev(cmd->dev_types, du_devid->dev, &devt2);
|
||||
if ((ret1 == 2) && (ret2 == 2) && (devt1 == devt2)) {
|
||||
log_debug("Using separate entries for partitions of same device %s part %d %s part %d.",
|
||||
dev_name(dev), part, dev_name(du_devid->dev), du_devid->part);
|
||||
} else {
|
||||
log_warn("WARNING: adding device %s with idname %s which is already used for %s.",
|
||||
dev_name(dev), id->idname, dev_name(du_devid->dev));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If one of the existing entries (du_dev, du_pvid, du_devid, du_devname)
|
||||
@@ -1105,29 +1164,6 @@ id_done:
|
||||
dm_list_del(&update_du->list);
|
||||
update_matching_kind = "device";
|
||||
update_matching_name = dev_name(dev);
|
||||
|
||||
if (du_devid && (du_devid != du_dev)) {
|
||||
log_warn("WARNING: device %s (%s) and %s (%s) have duplicate device ID.",
|
||||
dev_name(dev), id->idname,
|
||||
(du_pvid && du_pvid->dev) ? dev_name(du_pvid->dev) : "none",
|
||||
du_pvid ? du_pvid->idname : "");
|
||||
}
|
||||
|
||||
if (du_pvid && (du_pvid != du_dev)) {
|
||||
log_warn("WARNING: device %s (%s) and %s (%s) have duplicate PVID %s",
|
||||
dev_name(dev), id->idname,
|
||||
du_pvid->dev ? dev_name(du_pvid->dev) : "none", du_pvid->idname,
|
||||
pvid);
|
||||
}
|
||||
|
||||
if (du_devname && (du_devname != du_dev)) {
|
||||
/* clear devname in another entry with our devname */
|
||||
log_warn("Devices file PVID %s clearing wrong DEVNAME %s.",
|
||||
du_devname->pvid, du_devname->devname);
|
||||
free(du_devname->devname);
|
||||
du_devname->devname = NULL;
|
||||
}
|
||||
|
||||
} else if (du_pvid) {
|
||||
/*
|
||||
* If the device_id of the existing entry for PVID is the same
|
||||
@@ -1147,11 +1183,6 @@ id_done:
|
||||
update_matching_kind = "PVID";
|
||||
update_matching_name = pvid;
|
||||
} else {
|
||||
log_warn("WARNING: device %s (%s) and %s (%s) have duplicate PVID %s",
|
||||
dev_name(dev), id->idname,
|
||||
du_pvid->dev ? dev_name(du_pvid->dev) : "none", du_pvid->idname,
|
||||
pvid);
|
||||
|
||||
if (!cmd->current_settings.yes &&
|
||||
yes_no_prompt("Add device with duplicate PV to devices file?") == 'n') {
|
||||
log_print("Device not added.");
|
||||
@@ -1159,21 +1190,6 @@ id_done:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (du_devid && (du_devid != du_pvid)) {
|
||||
/* warn about another entry using the same device_id */
|
||||
log_warn("WARNING: duplicate device_id %s for PVIDs %s %s",
|
||||
du_devid->idname, du_devid->pvid, du_pvid->pvid);
|
||||
}
|
||||
|
||||
if (du_devname && (du_devname != du_pvid)) {
|
||||
/* clear devname in another entry with our devname */
|
||||
log_warn("Devices file PVID %s clearing wrong DEVNAME %s.",
|
||||
du_devname->pvid, du_devname->devname);
|
||||
free(du_devname->devname);
|
||||
du_devname->devname = NULL;
|
||||
}
|
||||
|
||||
} else if (du_devid) {
|
||||
/*
|
||||
* Do we create a new du or update the existing du?
|
||||
@@ -1188,64 +1204,13 @@ id_done:
|
||||
* the same device_id (create a new du for dev.)
|
||||
* If not, then update the existing du_devid.
|
||||
*/
|
||||
|
||||
if (du_devid->dev != dev)
|
||||
check_idname = device_id_system_read(cmd, du_devid->dev, id->idtype);
|
||||
|
||||
if (check_idname && !strcmp(check_idname, id->idname)) {
|
||||
int ret1, ret2;
|
||||
dev_t devt1, devt2;
|
||||
|
||||
/*
|
||||
* two different devices have the same device_id,
|
||||
* create a new du for the device being added
|
||||
*/
|
||||
|
||||
/* dev_is_partitioned() the dev open to read it. */
|
||||
if (!label_scan_open(du_devid->dev))
|
||||
log_warn("Cannot open %s", dev_name(du_devid->dev));
|
||||
|
||||
if (dev_is_partitioned(cmd, du_devid->dev)) {
|
||||
/* Check if existing entry is whole device and new entry is a partition of it. */
|
||||
ret1 = dev_get_primary_dev(cmd->dev_types, dev, &devt1);
|
||||
if ((ret1 == 2) && (devt1 == du_devid->dev->dev))
|
||||
log_warn("Remove partitioned device %s from devices file.", dev_name(du_devid->dev));
|
||||
} else {
|
||||
/* Check if both entries are partitions of the same device. */
|
||||
ret1 = dev_get_primary_dev(cmd->dev_types, dev, &devt1);
|
||||
ret2 = dev_get_primary_dev(cmd->dev_types, du_devid->dev, &devt2);
|
||||
|
||||
if ((ret1 == 2) && (ret2 == 2) && (devt1 == devt2)) {
|
||||
log_warn("Partitions %s %s have same device_id %s",
|
||||
dev_name(dev), dev_name(du_devid->dev), id->idname);
|
||||
} else {
|
||||
log_warn("Duplicate device_id %s %s for %s and %s",
|
||||
idtype_to_str(id->idtype), check_idname,
|
||||
dev_name(dev), dev_name(du_devid->dev));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (du_devid->dev == dev) {
|
||||
/* update the existing entry with matching devid */
|
||||
update_du = du_devid;
|
||||
dm_list_del(&update_du->list);
|
||||
update_matching_kind = "device_id";
|
||||
update_matching_name = id->idname;
|
||||
}
|
||||
|
||||
if (du_devname && (du_devname != du_devid)) {
|
||||
/* clear devname in another entry with our devname */
|
||||
log_warn("Devices file PVID %s clearing wrong DEVNAME %s",
|
||||
du_devname->pvid, du_devname->devname);
|
||||
free(du_devname->devname);
|
||||
du_devname->devname = NULL;
|
||||
}
|
||||
|
||||
} else if (du_devname) {
|
||||
/* clear devname in another entry with our devname */
|
||||
log_warn("Devices file PVID %s clearing wrong DEVNAME %s",
|
||||
du_devname->pvid, du_devname->devname);
|
||||
free(du_devname->devname);
|
||||
du_devname->devname = NULL;
|
||||
}
|
||||
|
||||
free((void *)check_idname);
|
||||
@@ -1360,6 +1325,10 @@ void device_id_update_vg_uuid(struct cmd_context *cmd, struct volume_group *vg,
|
||||
|
||||
static int _idtype_compatible_with_major_number(struct cmd_context *cmd, int idtype, int major)
|
||||
{
|
||||
/* devname can be used with any kind of device */
|
||||
if (idtype == DEV_ID_TYPE_DEVNAME)
|
||||
return 1;
|
||||
|
||||
if (idtype == DEV_ID_TYPE_MPATH_UUID ||
|
||||
idtype == DEV_ID_TYPE_CRYPT_UUID ||
|
||||
idtype == DEV_ID_TYPE_LVMLV_UUID)
|
||||
@@ -1388,6 +1357,43 @@ static int _idtype_compatible_with_major_number(struct cmd_context *cmd, int idt
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _match_dm_devnames(struct cmd_context *cmd, struct device *dev,
|
||||
struct dev_id *id, struct dev_use *du)
|
||||
{
|
||||
struct stat buf;
|
||||
|
||||
if (MAJOR(dev->dev) != cmd->dev_types->device_mapper_major)
|
||||
return 0;
|
||||
|
||||
if (id->idname && du->idname && !strcmp(id->idname, du->idname))
|
||||
return 1;
|
||||
|
||||
if (du->idname && !strcmp(du->idname, dev_name(dev))) {
|
||||
log_debug("Match device_id %s %s to %s: ignoring idname %s",
|
||||
idtype_to_str(du->idtype), du->idname, dev_name(dev), id->idname ?: ".");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!du->idname)
|
||||
return 0;
|
||||
|
||||
/* detect that a du entry is for a dm device */
|
||||
|
||||
if (!strncmp(du->idname, "/dev/dm-", 8) || !strncmp(du->idname, "/dev/mapper/", 12)) {
|
||||
if (stat(du->idname, &buf))
|
||||
return 0;
|
||||
|
||||
if ((MAJOR(buf.st_rdev) == cmd->dev_types->device_mapper_major) &&
|
||||
(MINOR(buf.st_rdev) == MINOR(dev->dev))) {
|
||||
log_debug("Match device_id %s %s to %s: using other dm name, ignoring %s",
|
||||
idtype_to_str(du->idtype), du->idname, dev_name(dev), id->idname ?: ".");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* check for dev->ids entry with du->idtype, if found compare it,
|
||||
* if not, system_read of this type and add entry to dev->ids, compare it.
|
||||
@@ -1400,43 +1406,74 @@ static int _match_du_to_dev(struct cmd_context *cmd, struct dev_use *du, struct
|
||||
const char *idname;
|
||||
int part;
|
||||
|
||||
if (!du->idname || !du->idtype)
|
||||
/*
|
||||
* The idname will be removed from an entry with devname type when the
|
||||
* devname is read and found to hold a different PVID than the PVID in
|
||||
* the entry. At that point we only have the PVID and no known
|
||||
* location for it.
|
||||
*/
|
||||
if (!du->idname || !du->idtype) {
|
||||
/*
|
||||
log_debug("Mismatch device_id %s %s %s to %s",
|
||||
du->idtype ? idtype_to_str(du->idtype) : "idtype_missing",
|
||||
du->idname ? du->idname : "idname_missing",
|
||||
du->devname ? du->devname : "devname_missing",
|
||||
dev_name(dev));
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Some idtypes can only match devices with a specific major number,
|
||||
* so we can skip trying to match certain du entries based simply on
|
||||
* the major number of dev.
|
||||
*/
|
||||
if (!_idtype_compatible_with_major_number(cmd, du->idtype, (int)MAJOR(dev->dev)))
|
||||
if (!_idtype_compatible_with_major_number(cmd, du->idtype, (int)MAJOR(dev->dev))) {
|
||||
/*
|
||||
log_debug("Mismatch device_id %s %s to %s: wrong major",
|
||||
idtype_to_str(du->idtype), du->idname ?: ".", dev_name(dev));
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!dev_get_partition_number(dev, &part)) {
|
||||
log_debug("compare %s failed to get dev partition", dev_name(dev));
|
||||
/*
|
||||
log_debug("Mismatch device_id %s %s to %s: no partition",
|
||||
idtype_to_str(du->idtype), du->idname ?: ".", dev_name(dev));
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
if (part != du->part) {
|
||||
/*
|
||||
log_debug("compare mis %s %s part %d to %s part %d",
|
||||
idtype_to_str(du->idtype), du->idname ?: ".", du->part, dev_name(dev), part);
|
||||
log_debug("Mismatch device_id %s %s to %s: wrong partition %d vs %d",
|
||||
idtype_to_str(du->idtype), du->idname ?: ".", dev_name(dev), du->part, part);
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
dm_list_iterate_items(id, &dev->ids) {
|
||||
if (id->idtype == du->idtype) {
|
||||
if (id->idname && !strcmp(id->idname, du->idname)) {
|
||||
if ((id->idtype == DEV_ID_TYPE_DEVNAME) && _match_dm_devnames(cmd, dev, id, du)) {
|
||||
/* dm devs can have differing names that we know still match */
|
||||
du->dev = dev;
|
||||
dev->id = id;
|
||||
dev->flags |= DEV_MATCHED_USE_ID;
|
||||
log_debug("Match device_id %s %s to %s: dm names",
|
||||
idtype_to_str(du->idtype), du->idname, dev_name(dev));
|
||||
return 1;
|
||||
|
||||
} else if (id->idname && !strcmp(id->idname, du->idname)) {
|
||||
du->dev = dev;
|
||||
dev->id = id;
|
||||
dev->flags |= DEV_MATCHED_USE_ID;
|
||||
log_debug("Match device_id %s %s to %s",
|
||||
idtype_to_str(du->idtype), du->idname, dev_name(dev));
|
||||
return 1;
|
||||
|
||||
} else {
|
||||
/*
|
||||
log_debug("compare mis %s %s to %s %s",
|
||||
idtype_to_str(du->idtype), du->idname ?: ".", dev_name(dev),
|
||||
((id->idtype != DEV_ID_TYPE_DEVNAME) && id->idname) ? id->idname : "");
|
||||
log_debug("Mismatch device_id %s %s to %s: idname %s",
|
||||
idtype_to_str(du->idtype), du->idname ?: ".", dev_name(dev), id->idname ?: ":");
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
@@ -1456,7 +1493,7 @@ static int _match_du_to_dev(struct cmd_context *cmd, struct dev_use *du, struct
|
||||
id->dev = dev;
|
||||
dm_list_add(&dev->ids, &id->list);
|
||||
/*
|
||||
log_debug("compare mis %s %s to %s no idtype",
|
||||
log_debug("Mismatch device_id %s %s to %s: no idtype",
|
||||
idtype_to_str(du->idtype), du->idname ?: ".", dev_name(dev));
|
||||
*/
|
||||
return 0;
|
||||
@@ -1481,9 +1518,8 @@ static int _match_du_to_dev(struct cmd_context *cmd, struct dev_use *du, struct
|
||||
}
|
||||
|
||||
/*
|
||||
log_debug("compare mis %s %s to %s %s",
|
||||
idtype_to_str(du->idtype), du->idname ?: ".", dev_name(dev),
|
||||
((id->idtype != DEV_ID_TYPE_DEVNAME) && id->idname) ? id->idname : "");
|
||||
log_debug("Mismatch device_id %s %s to %s: idname %s",
|
||||
idtype_to_str(du->idtype), du->idname ?: ".", dev_name(dev), idname);
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
@@ -1907,6 +1943,14 @@ void device_ids_validate(struct cmd_context *cmd, struct dm_list *scanned_devs,
|
||||
*device_ids_invalid = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* When a new devname/pvid mismatch is discovered, a new search for the
|
||||
* pvid should be permitted (searched_devnames may exist to suppress
|
||||
* searching for other pvids.)
|
||||
*/
|
||||
if (update_file)
|
||||
unlink_searched_devnames(cmd);
|
||||
|
||||
/* FIXME: for wrong devname cases, wait to write new until device_ids_find_renamed_devs? */
|
||||
|
||||
/*
|
||||
@@ -1975,12 +2019,19 @@ void device_ids_find_renamed_devs(struct cmd_context *cmd, struct dm_list *dev_l
|
||||
search_auto = !strcmp(cmd->search_for_devnames, "auto");
|
||||
|
||||
dm_list_iterate_items(du, &cmd->use_devices) {
|
||||
if (du->dev)
|
||||
continue;
|
||||
if (!du->pvid)
|
||||
continue;
|
||||
if (du->idtype != DEV_ID_TYPE_DEVNAME)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* if the old incorrect devname is now a device that's
|
||||
* filtered and not scanned, e.g. an mpath component,
|
||||
* then we want to look for the pvid on a new device.
|
||||
*/
|
||||
if (du->dev && !du->dev->filtered_flags)
|
||||
continue;
|
||||
|
||||
if (!(dil = dm_pool_zalloc(cmd->mem, sizeof(*dil))))
|
||||
continue;
|
||||
|
||||
@@ -2005,6 +2056,11 @@ void device_ids_find_renamed_devs(struct cmd_context *cmd, struct dm_list *dev_l
|
||||
* the searched file, so a subsequent lvm command will do the search
|
||||
* again. In future perhaps we could add a policy to automatically
|
||||
* remove a devices file entry that's not been found for some time.
|
||||
*
|
||||
* TODO: like the hint file, add a hash of all devnames to the searched
|
||||
* file so it can be ignored and removed if the devs/hash change.
|
||||
* If hints are enabled, the hints invalidation could also remove the
|
||||
* searched file.
|
||||
*/
|
||||
if (_searched_devnames_exists(cmd)) {
|
||||
log_debug("Search for PVIDs skipped for %s", _searched_file);
|
||||
@@ -2071,6 +2127,12 @@ void device_ids_find_renamed_devs(struct cmd_context *cmd, struct dm_list *dev_l
|
||||
*
|
||||
* TODO: in auto mode should we look in other non-system
|
||||
* devices files and skip any devs included in those?
|
||||
*
|
||||
* Note that a user can override a stable id type and use
|
||||
* devname for a device's id, in which case this optimization
|
||||
* can prevent a search from finding a renamed dev. So, if a
|
||||
* user forces a devname id, then they should probably also
|
||||
* set search_for_devnames=all.
|
||||
*/
|
||||
if (search_auto && _dev_has_stable_id(cmd, dev)) {
|
||||
other_idtype++;
|
||||
@@ -2180,7 +2242,8 @@ void device_ids_find_renamed_devs(struct cmd_context *cmd, struct dm_list *dev_l
|
||||
continue;
|
||||
}
|
||||
|
||||
log_warn("Devices file PVID %s updating IDNAME to %s.", dev->pvid, devname);
|
||||
if (!noupdate)
|
||||
log_warn("Devices file PVID %s updating IDNAME to %s.", dev->pvid, devname);
|
||||
|
||||
free(du->idname);
|
||||
free(du->devname);
|
||||
|
||||
@@ -56,4 +56,6 @@ void unlink_searched_devnames(struct cmd_context *cmd);
|
||||
|
||||
int read_sys_block(struct cmd_context *cmd, struct device *dev, const char *suffix, char *sysbuf, int sysbufsize);
|
||||
|
||||
int dev_has_mpath_uuid(struct cmd_context *cmd, struct device *dev, const char **idname_out);
|
||||
|
||||
#endif
|
||||
|
||||
507
lib/device/online.c
Normal file
507
lib/device/online.c
Normal file
@@ -0,0 +1,507 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
/*
|
||||
* file contains:
|
||||
* <major>:<minor>\n
|
||||
* vg:<vgname>\n
|
||||
* dev:<devname>\n\0
|
||||
*
|
||||
* It's possible that vg and dev may not exist.
|
||||
*/
|
||||
|
||||
static int _copy_pvid_file_field(const char *field, char *buf, int bufsize, char *out, int outsize)
|
||||
{
|
||||
char *p;
|
||||
int i = 0;
|
||||
|
||||
if (!(p = strstr(buf, field)))
|
||||
return 0;
|
||||
|
||||
p += strlen(field);
|
||||
|
||||
while (1) {
|
||||
if (*p == '\n')
|
||||
break;
|
||||
if (*p == '\0')
|
||||
break;
|
||||
|
||||
if (p >= (buf + bufsize))
|
||||
return 0;
|
||||
if (i >= outsize-1)
|
||||
return 0;
|
||||
|
||||
out[i] = *p;
|
||||
|
||||
i++;
|
||||
p++;
|
||||
}
|
||||
|
||||
return i ? 1 : 0;
|
||||
}
|
||||
|
||||
#define MAX_PVID_FILE_SIZE 512
|
||||
|
||||
int online_pvid_file_read(char *path, int *major, int *minor, char *vgname, char *devname)
|
||||
{
|
||||
char buf[MAX_PVID_FILE_SIZE] = { 0 };
|
||||
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;
|
||||
}
|
||||
|
||||
if (vgname) {
|
||||
if (!strstr(buf, "vg:")) {
|
||||
log_debug("No vgname in %s", path);
|
||||
vgname[0] = '\0';
|
||||
goto copy_dev;
|
||||
}
|
||||
|
||||
if (!_copy_pvid_file_field("vg:", buf, MAX_PVID_FILE_SIZE, vgname, NAME_LEN)) {
|
||||
log_warn("Ignoring invalid vg field in %s", path);
|
||||
vgname[0] = '\0';
|
||||
goto copy_dev;
|
||||
}
|
||||
|
||||
if (!validate_name(vgname)) {
|
||||
log_warn("Ignoring invalid vgname in %s (%s)", path, vgname);
|
||||
vgname[0] = '\0';
|
||||
goto copy_dev;
|
||||
}
|
||||
}
|
||||
|
||||
copy_dev:
|
||||
if (devname) {
|
||||
if (!strstr(buf, "dev:")) {
|
||||
log_debug("No devname in %s", path);
|
||||
devname[0] = '\0';
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!_copy_pvid_file_field("dev:", buf, MAX_PVID_FILE_SIZE, devname, NAME_LEN)) {
|
||||
log_warn("Ignoring invalid devname field in %s", path);
|
||||
devname[0] = '\0';
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (strncmp(devname, "/dev/", 5)) {
|
||||
log_warn("Ignoring invalid devname in %s (%s)", path, devname);
|
||||
devname[0] = '\0';
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
out:
|
||||
return 1;
|
||||
}
|
||||
|
||||
void free_po_list(struct dm_list *list)
|
||||
{
|
||||
struct pv_online *po, *po2;
|
||||
|
||||
dm_list_iterate_items_safe(po, po2, list) {
|
||||
dm_list_del(&po->list);
|
||||
free(po);
|
||||
}
|
||||
}
|
||||
|
||||
int get_pvs_online(struct dm_list *pvs_online, const char *vgname)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
char file_vgname[NAME_LEN];
|
||||
char file_devname[NAME_LEN];
|
||||
DIR *dir;
|
||||
struct dirent *de;
|
||||
struct pv_online *po;
|
||||
int file_major = 0, file_minor = 0;
|
||||
|
||||
if (!(dir = opendir(PVS_ONLINE_DIR)))
|
||||
return 0;
|
||||
|
||||
while ((de = readdir(dir))) {
|
||||
if (de->d_name[0] == '.')
|
||||
continue;
|
||||
|
||||
if (strlen(de->d_name) != ID_LEN)
|
||||
continue;
|
||||
|
||||
memset(path, 0, sizeof(path));
|
||||
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));
|
||||
memset(file_devname, 0, sizeof(file_devname));
|
||||
|
||||
if (!online_pvid_file_read(path, &file_major, &file_minor, file_vgname, file_devname))
|
||||
continue;
|
||||
|
||||
if (vgname && strcmp(file_vgname, vgname))
|
||||
continue;
|
||||
|
||||
if (!(po = zalloc(sizeof(*po))))
|
||||
continue;
|
||||
|
||||
memcpy(po->pvid, de->d_name, ID_LEN);
|
||||
if (file_major || file_minor)
|
||||
po->devno = MKDEV(file_major, file_minor);
|
||||
if (file_vgname[0])
|
||||
strncpy(po->vgname, file_vgname, NAME_LEN);
|
||||
if (file_devname[0])
|
||||
strncpy(po->devname, file_devname, NAME_LEN);
|
||||
|
||||
log_debug("Found PV online %s for VG %s %s", path, vgname, file_devname);
|
||||
dm_list_add(pvs_online, &po->list);
|
||||
}
|
||||
|
||||
if (closedir(dir))
|
||||
log_sys_debug("closedir", PVS_ONLINE_DIR);
|
||||
|
||||
log_debug("Found PVs online %d for %s", dm_list_size(pvs_online), vgname ?: "all");
|
||||
|
||||
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];
|
||||
char file_devname[NAME_LEN];
|
||||
char devname[NAME_LEN];
|
||||
int devnamelen;
|
||||
int file_major = 0, file_minor = 0;
|
||||
int major, minor;
|
||||
int fd;
|
||||
int rv;
|
||||
int len;
|
||||
int len1 = 0;
|
||||
int len2 = 0;
|
||||
int len3 = 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("Incomplete online file for %s %d:%d vg %s.", dev_name(dev), major, minor, vgname);
|
||||
/* can still continue without vgname */
|
||||
len2 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
devnamelen = dm_snprintf(devname, sizeof(devname), "%s", dev_name(dev));
|
||||
if ((devnamelen > 5) && (devnamelen < NAME_LEN-1)) {
|
||||
if ((len3 = dm_snprintf(buf + len1 + len2, sizeof(buf) - len1 - len2, "dev:%s\n", devname)) < 0) {
|
||||
log_print("Incomplete devname in online file for %s.", dev_name(dev));
|
||||
/* can continue without devname */
|
||||
len3 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
len = len1 + len2 + len3;
|
||||
|
||||
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));
|
||||
memset(file_devname, 0, sizeof(file_devname));
|
||||
|
||||
online_pvid_file_read(path, &file_major, &file_minor, file_vgname, file_devname);
|
||||
|
||||
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 %d:%d is duplicate for PVID %s on %d:%d %s.",
|
||||
dev_name(dev), major, minor, dev->pvid, file_major, file_minor, file_devname);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int get_pvs_lookup(struct dm_list *pvs_online, const char *vgname)
|
||||
{
|
||||
char lookup_path[PATH_MAX] = { 0 };
|
||||
char path[PATH_MAX] = { 0 };
|
||||
char line[64];
|
||||
char pvid[ID_LEN + 1] __attribute__((aligned(8))) = { 0 };
|
||||
char file_vgname[NAME_LEN];
|
||||
char file_devname[NAME_LEN];
|
||||
struct pv_online *po;
|
||||
int file_major = 0, file_minor = 0;
|
||||
FILE *fp;
|
||||
|
||||
if (dm_snprintf(lookup_path, sizeof(path), "%s/%s", PVS_LOOKUP_DIR, vgname) < 0)
|
||||
return_0;
|
||||
|
||||
if (!(fp = fopen(lookup_path, "r")))
|
||||
return_0;
|
||||
|
||||
while (fgets(line, sizeof(line), fp)) {
|
||||
memcpy(pvid, line, ID_LEN);
|
||||
if (strlen(pvid) != ID_LEN)
|
||||
goto_bad;
|
||||
|
||||
memset(path, 0, sizeof(path));
|
||||
snprintf(path, sizeof(path), "%s/%s", PVS_ONLINE_DIR, pvid);
|
||||
|
||||
file_major = 0;
|
||||
file_minor = 0;
|
||||
memset(file_vgname, 0, sizeof(file_vgname));
|
||||
memset(file_devname, 0, sizeof(file_devname));
|
||||
|
||||
if (!online_pvid_file_read(path, &file_major, &file_minor, file_vgname, file_devname))
|
||||
goto_bad;
|
||||
|
||||
/*
|
||||
* PVs without metadata will not have a vgname in their pvid
|
||||
* file, but the purpose of using the lookup file is that we
|
||||
* know the PV is for this VG even without the pvid vgname
|
||||
* field.
|
||||
*/
|
||||
if (vgname && file_vgname[0] && strcmp(file_vgname, vgname)) {
|
||||
/* Should never happen */
|
||||
log_error("Incorrect VG lookup file %s PVID %s %s.", vgname, pvid, file_vgname);
|
||||
goto_bad;
|
||||
}
|
||||
|
||||
if (!(po = zalloc(sizeof(*po))))
|
||||
goto_bad;
|
||||
|
||||
memcpy(po->pvid, pvid, ID_LEN);
|
||||
if (file_major || file_minor)
|
||||
po->devno = MKDEV(file_major, file_minor);
|
||||
if (file_vgname[0])
|
||||
strncpy(po->vgname, file_vgname, NAME_LEN-1);
|
||||
if (file_devname[0])
|
||||
strncpy(po->devname, file_devname, NAME_LEN-1);
|
||||
|
||||
log_debug("Found PV online lookup %s for VG %s on %s", path, vgname, file_devname);
|
||||
dm_list_add(pvs_online, &po->list);
|
||||
}
|
||||
|
||||
log_debug("Found PVs online lookup %d for %s", dm_list_size(pvs_online), vgname);
|
||||
|
||||
fclose(fp);
|
||||
return 1;
|
||||
|
||||
bad:
|
||||
free_po_list(pvs_online);
|
||||
fclose(fp);
|
||||
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);
|
||||
}
|
||||
58
lib/device/online.h
Normal file
58
lib/device/online.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
struct pv_online {
|
||||
struct dm_list list;
|
||||
struct device *dev;
|
||||
dev_t devno;
|
||||
char pvid[ID_LEN + 1];
|
||||
char vgname[NAME_LEN];
|
||||
char devname[NAME_LEN];
|
||||
};
|
||||
|
||||
/*
|
||||
* 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, char *devname);
|
||||
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);
|
||||
int get_pvs_online(struct dm_list *pvs_online, const char *vgname);
|
||||
int get_pvs_lookup(struct dm_list *pvs_online, const char *vgname);
|
||||
void free_po_list(struct dm_list *list);
|
||||
|
||||
#endif
|
||||
@@ -58,7 +58,7 @@ static int _init_hash(struct pfilter *pf)
|
||||
if (pf->devices)
|
||||
dm_hash_destroy(pf->devices);
|
||||
|
||||
if (!(pf->devices = dm_hash_create(111)))
|
||||
if (!(pf->devices = dm_hash_create(511)))
|
||||
return_0;
|
||||
|
||||
return 1;
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
#include "lib/misc/lib.h"
|
||||
#include "lib/filters/filter.h"
|
||||
|
||||
static int _sys_dev_block_found;
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
static int _accept_p(struct cmd_context *cmd, struct dev_filter *f, struct device *dev, const char *use_filter_name)
|
||||
@@ -23,6 +25,9 @@ static int _accept_p(struct cmd_context *cmd, struct dev_filter *f, struct devic
|
||||
const char *sysfs_dir;
|
||||
struct stat info;
|
||||
|
||||
if (!_sys_dev_block_found)
|
||||
return 1;
|
||||
|
||||
dev->filtered_flags &= ~DEV_FILTERED_SYSFS;
|
||||
|
||||
/*
|
||||
@@ -57,6 +62,26 @@ static void _destroy(struct dev_filter *f)
|
||||
free(f);
|
||||
}
|
||||
|
||||
static void _check_sys_dev_block(void)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
const char *sysfs_dir;
|
||||
struct stat info;
|
||||
|
||||
sysfs_dir = dm_sysfs_dir();
|
||||
if (sysfs_dir && *sysfs_dir) {
|
||||
if (dm_snprintf(path, sizeof(path), "%sdev/block", sysfs_dir) < 0)
|
||||
return;
|
||||
|
||||
if (lstat(path, &info)) {
|
||||
log_debug("filter-sysfs disabled: /sys/dev/block not found");
|
||||
_sys_dev_block_found = 0;
|
||||
} else {
|
||||
_sys_dev_block_found = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct dev_filter *sysfs_filter_create(void)
|
||||
{
|
||||
const char *sysfs_dir = dm_sysfs_dir();
|
||||
@@ -67,6 +92,9 @@ struct dev_filter *sysfs_filter_create(void)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* support old kernels that don't have this */
|
||||
_check_sys_dev_block();
|
||||
|
||||
if (!(f = zalloc(sizeof(*f))))
|
||||
goto_bad;
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ static int _passes_usable_filter(struct cmd_context *cmd, struct dev_filter *f,
|
||||
break;
|
||||
}
|
||||
|
||||
if (!(r = device_is_usable(dev, ucp, &is_lv))) {
|
||||
if (!(r = device_is_usable(cmd, dev, ucp, &is_lv))) {
|
||||
if (is_lv)
|
||||
dev->filtered_flags |= DEV_FILTERED_IS_LV;
|
||||
else
|
||||
|
||||
@@ -219,7 +219,7 @@ static void _remove_expired(const char *dir, const char *vgname,
|
||||
|
||||
sum /= 1024 * 1024;
|
||||
if (sum > 128 || archives_size > 8192)
|
||||
log_print_unless_silent("Consider prunning %s VG archive with more then %u MiB in %u files (check archiving is needed in lvm.conf).",
|
||||
log_print_unless_silent("Consider pruning %s VG archive with more then %u MiB in %u files (check archiving is needed in lvm.conf).",
|
||||
vgname, (unsigned)sum, archives_size);
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
@@ -1212,8 +1214,8 @@ void invalidate_hints(struct cmd_context *cmd)
|
||||
* probably want to exclude that command from attempting this optimization,
|
||||
* because it would be difficult to know what VG that command wanted to use.
|
||||
*/
|
||||
static void _get_single_vgname_cmd_arg(struct cmd_context *cmd,
|
||||
struct dm_list *hints, char **vgname)
|
||||
void get_single_vgname_cmd_arg(struct cmd_context *cmd,
|
||||
struct dm_list *hints, char **vgname)
|
||||
{
|
||||
struct hint *hint;
|
||||
char namebuf[NAME_LEN];
|
||||
@@ -1262,6 +1264,11 @@ static void _get_single_vgname_cmd_arg(struct cmd_context *cmd,
|
||||
return;
|
||||
|
||||
check:
|
||||
if (!hints) {
|
||||
*vgname = name;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Only use this vgname hint if there are hints that contain this
|
||||
* vgname. This might happen if we aren't able to properly extract the
|
||||
@@ -1390,6 +1397,16 @@ int get_hints(struct cmd_context *cmd, struct dm_list *hints_out, int *newhints,
|
||||
log_debug("get_hints: needs refresh");
|
||||
free_hints(&hints_list);
|
||||
|
||||
/*
|
||||
* This is not related to hints, and is probably unnecessary,
|
||||
* but it could possibly help. When hints become invalid it's
|
||||
* usually becaues devs on the system have changed, and that
|
||||
* also means that a missing devices file entry might be found
|
||||
* by searching devices again. (the searched_devnames
|
||||
* mechanism should eventually be replaced)
|
||||
*/
|
||||
unlink_searched_devnames(cmd);
|
||||
|
||||
if (!_lock_hints(cmd, LOCK_EX, NONBLOCK))
|
||||
return 0;
|
||||
|
||||
@@ -1423,7 +1440,7 @@ int get_hints(struct cmd_context *cmd, struct dm_list *hints_out, int *newhints,
|
||||
* us which devs are PVs. We might want to enable this optimization
|
||||
* separately.)
|
||||
*/
|
||||
_get_single_vgname_cmd_arg(cmd, &hints_list, &vgname);
|
||||
get_single_vgname_cmd_arg(cmd, &hints_list, &vgname);
|
||||
|
||||
_apply_hints(cmd, &hints_list, vgname, devs_in, devs_out);
|
||||
|
||||
|
||||
@@ -41,5 +41,8 @@ void hints_exit(struct cmd_context *cmd);
|
||||
|
||||
void pvscan_recreate_hints_begin(struct cmd_context *cmd);
|
||||
|
||||
void get_single_vgname_cmd_arg(struct cmd_context *cmd,
|
||||
struct dm_list *hints, char **vgname);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "lib/metadata/metadata.h"
|
||||
#include "lib/format_text/layout.h"
|
||||
#include "lib/device/device_id.h"
|
||||
#include "lib/device/online.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
@@ -891,7 +892,7 @@ static int _setup_bcache(void)
|
||||
|
||||
#define BASE_FD_COUNT 32 /* Number of open files we want apart from devs */
|
||||
|
||||
static void _prepare_open_file_limit(struct cmd_context *cmd, unsigned int num_devs)
|
||||
void prepare_open_file_limit(struct cmd_context *cmd, unsigned int num_devs)
|
||||
{
|
||||
#ifdef HAVE_PRLIMIT
|
||||
struct rlimit old = { 0 }, new;
|
||||
@@ -1020,6 +1021,281 @@ int label_scan_for_pvid(struct cmd_context *cmd, char *pvid, struct device **dev
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear state that label_scan_vg_online() created so it will not
|
||||
* confuse the standard label_scan() that the caller falls back to.
|
||||
* the results of filtering (call filter->wipe)
|
||||
* the results of matching device_id (reset dev and du)
|
||||
* the results of scanning in lvmcache
|
||||
*/
|
||||
static void _clear_scan_state(struct cmd_context *cmd, struct dm_list *devs)
|
||||
{
|
||||
struct device_list *devl;
|
||||
struct device *dev;
|
||||
struct dev_use *du;
|
||||
|
||||
dm_list_iterate_items(devl, devs) {
|
||||
dev = devl->dev;
|
||||
|
||||
cmd->filter->wipe(cmd, cmd->filter, dev, NULL);
|
||||
dev->flags &= ~DEV_MATCHED_USE_ID;
|
||||
dev->id = NULL;
|
||||
|
||||
if ((du = get_du_for_dev(cmd, dev)))
|
||||
du->dev = NULL;
|
||||
|
||||
lvmcache_del_dev(dev);
|
||||
|
||||
memset(dev->pvid, 0, ID_LEN);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Use files under /run/lvm/, created by pvscan --cache autoactivation,
|
||||
* to optimize device setup/scanning. autoactivation happens during
|
||||
* system startup when the hints file is not useful, but he pvs_online
|
||||
* files can provide a similar optimization to the hints file.
|
||||
*/
|
||||
|
||||
int label_scan_vg_online(struct cmd_context *cmd, const char *vgname,
|
||||
int *found_none, int *found_all, int *found_incomplete)
|
||||
{
|
||||
struct dm_list pvs_online;
|
||||
struct dm_list devs;
|
||||
struct dm_list devs_drop;
|
||||
struct pv_online *po;
|
||||
struct device_list *devl, *devl2;
|
||||
int relax_deviceid_filter = 0;
|
||||
int metadata_pv_count;
|
||||
int try_dev_scan = 0;
|
||||
|
||||
dm_list_init(&pvs_online);
|
||||
dm_list_init(&devs);
|
||||
dm_list_init(&devs_drop);
|
||||
|
||||
log_debug_devs("Finding online devices to scan");
|
||||
|
||||
/*
|
||||
* First attempt to use /run/lvm/pvs_lookup/vgname which should be
|
||||
* used in cases where all PVs in a VG do not contain metadata.
|
||||
* When the pvs_lookup file does not exist, then simply use all
|
||||
* /run/lvm/pvs_online/pvid files that contain a matching vgname.
|
||||
* The list of po structs represents the PVs in the VG, and the
|
||||
* info from the online files tell us which devices those PVs are
|
||||
* located on.
|
||||
*/
|
||||
if (vgname) {
|
||||
if (!get_pvs_lookup(&pvs_online, vgname)) {
|
||||
if (!get_pvs_online(&pvs_online, vgname))
|
||||
goto bad;
|
||||
}
|
||||
} else {
|
||||
if (!get_pvs_online(&pvs_online, NULL))
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (dm_list_empty(&pvs_online)) {
|
||||
*found_none = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* For each po add a struct dev to dev-cache. This is a faster
|
||||
* alternative to the usual dev_cache_scan() which looks at all
|
||||
* devices. If this optimization fails, then fall back to the usual
|
||||
* dev_cache_scan().
|
||||
*/
|
||||
dm_list_iterate_items(po, &pvs_online) {
|
||||
if (!(po->dev = setup_dev_in_dev_cache(cmd, po->devno, po->devname[0] ? po->devname : NULL))) {
|
||||
log_debug("No device found for quick mapping of online PV %d:%d %s PVID %s",
|
||||
(int)MAJOR(po->devno), (int)MINOR(po->devno), po->devname, po->pvid);
|
||||
try_dev_scan = 1;
|
||||
continue;
|
||||
}
|
||||
if (!(devl = dm_pool_zalloc(cmd->mem, sizeof(*devl))))
|
||||
goto_bad;
|
||||
|
||||
devl->dev = po->dev;
|
||||
dm_list_add(&devs, &devl->list);
|
||||
}
|
||||
|
||||
/*
|
||||
* Translating a devno (major:minor) into a device name can be
|
||||
* problematic for some devices that have unusual sysfs layouts, so if
|
||||
* this happens, do a full dev_cache_scan, which is slower, but is
|
||||
* sure to find the device.
|
||||
*/
|
||||
if (try_dev_scan) {
|
||||
dev_cache_scan(cmd);
|
||||
dm_list_iterate_items(po, &pvs_online) {
|
||||
if (po->dev)
|
||||
continue;
|
||||
if (!(po->dev = dev_cache_get_by_devt(cmd, po->devno, NULL, NULL))) {
|
||||
log_error("No device found for %d:%d PVID %s",
|
||||
(int)MAJOR(po->devno), (int)MINOR(po->devno), po->pvid);
|
||||
goto bad;
|
||||
}
|
||||
if (!(devl = dm_pool_zalloc(cmd->mem, sizeof(*devl))))
|
||||
goto_bad;
|
||||
|
||||
devl->dev = po->dev;
|
||||
dm_list_add(&devs, &devl->list);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* factor code common to pvscan_cache_args
|
||||
*/
|
||||
|
||||
/*
|
||||
* Match devs with the devices file because special/optimized
|
||||
* device setup was used which does not check the devices file.
|
||||
* If a match fails here do not exclude it, that will be done below by
|
||||
* passes_filter() which runs filter-deviceid. The
|
||||
* relax_deviceid_filter case needs to be able to work around
|
||||
* unmatching devs.
|
||||
*/
|
||||
|
||||
if (cmd->enable_devices_file) {
|
||||
dm_list_iterate_items(devl, &devs)
|
||||
device_ids_match_dev(cmd, devl->dev);
|
||||
}
|
||||
|
||||
if (cmd->enable_devices_list)
|
||||
device_ids_match_device_list(cmd);
|
||||
|
||||
if (cmd->enable_devices_file && device_ids_use_devname(cmd)) {
|
||||
relax_deviceid_filter = 1;
|
||||
cmd->filter_deviceid_skip = 1;
|
||||
/* PVIDs read from devs matched to devices file below instead. */
|
||||
log_debug("Skipping device_id filtering due to devname ids.");
|
||||
}
|
||||
|
||||
cmd->filter_nodata_only = 1;
|
||||
|
||||
dm_list_iterate_items_safe(devl, devl2, &devs) {
|
||||
if (!cmd->filter->passes_filter(cmd, cmd->filter, devl->dev, NULL)) {
|
||||
log_print("%s excluded by filters: %s.",
|
||||
dev_name(devl->dev), dev_filtered_reason(devl->dev));
|
||||
dm_list_del(&devl->list);
|
||||
dm_list_add(&devs_drop, &devl->list);
|
||||
}
|
||||
}
|
||||
|
||||
cmd->filter_nodata_only = 0;
|
||||
|
||||
/*
|
||||
* Clear the results of nodata filters that were saved by the
|
||||
* persistent filter so that the complete set of filters will
|
||||
* be checked by passes_filter below.
|
||||
*/
|
||||
dm_list_iterate_items(devl, &devs)
|
||||
cmd->filter->wipe(cmd, cmd->filter, devl->dev, NULL);
|
||||
|
||||
/*
|
||||
* Read header from each dev.
|
||||
* Eliminate non-lvm devs.
|
||||
* Apply all filters.
|
||||
*/
|
||||
|
||||
log_debug("label_scan_vg_online: read and filter devs");
|
||||
|
||||
label_scan_setup_bcache();
|
||||
|
||||
dm_list_iterate_items_safe(devl, devl2, &devs) {
|
||||
struct dev_use *du;
|
||||
int has_pvid;
|
||||
|
||||
if (!label_read_pvid(devl->dev, &has_pvid)) {
|
||||
log_print("%s cannot read label.", dev_name(devl->dev));
|
||||
dm_list_del(&devl->list);
|
||||
dm_list_add(&devs_drop, &devl->list);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!has_pvid) {
|
||||
/* Not an lvm device */
|
||||
log_print("%s not an lvm device.", dev_name(devl->dev));
|
||||
dm_list_del(&devl->list);
|
||||
dm_list_add(&devs_drop, &devl->list);
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* filter-deviceid is not being used because of unstable devnames,
|
||||
* so in place of that check if the pvid is in the devices file.
|
||||
*/
|
||||
if (relax_deviceid_filter) {
|
||||
if (!(du = get_du_for_pvid(cmd, devl->dev->pvid))) {
|
||||
log_print("%s excluded by devices file (checking PVID).",
|
||||
dev_name(devl->dev));
|
||||
dm_list_del(&devl->list);
|
||||
dm_list_add(&devs_drop, &devl->list);
|
||||
continue;
|
||||
} else {
|
||||
/* Special case matching for devname entries based on pvid. */
|
||||
log_debug("Match device_id %s %s to %s: matching PVID",
|
||||
idtype_to_str(du->idtype), du->idname, dev_name(devl->dev));
|
||||
}
|
||||
}
|
||||
|
||||
/* Applies all filters, including those that need data from dev. */
|
||||
if (!cmd->filter->passes_filter(cmd, cmd->filter, devl->dev, NULL)) {
|
||||
log_print("%s excluded by filters: %s.",
|
||||
dev_name(devl->dev), dev_filtered_reason(devl->dev));
|
||||
dm_list_del(&devl->list);
|
||||
dm_list_add(&devs_drop, &devl->list);
|
||||
}
|
||||
}
|
||||
|
||||
if (relax_deviceid_filter)
|
||||
cmd->filter_deviceid_skip = 0;
|
||||
|
||||
free_po_list(&pvs_online);
|
||||
|
||||
if (dm_list_empty(&devs)) {
|
||||
_clear_scan_state(cmd, &devs_drop);
|
||||
*found_none = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Scan devs to populate lvmcache info, which includes the mda info that's
|
||||
* needed to read vg metadata.
|
||||
* bcache data from label_read_pvid above is not invalidated so it can
|
||||
* be reused (more data may need to be read depending on how much of the
|
||||
* metadata was covered when reading the pvid.)
|
||||
*/
|
||||
_scan_list(cmd, NULL, &devs, 0, NULL);
|
||||
|
||||
/*
|
||||
* Check if all PVs from the VG were found after scanning the devs
|
||||
* produced from the online files. The online files are effectively
|
||||
* hints that usually work, but are not definitive, so we need to
|
||||
* be able to fall back to a standard label scan if the online hints
|
||||
* gave fewer PVs than listed in VG metadata.
|
||||
*/
|
||||
if (vgname) {
|
||||
metadata_pv_count = lvmcache_pvsummary_count(vgname);
|
||||
if (metadata_pv_count > dm_list_size(&devs)) {
|
||||
log_debug("Incomplete PV list from online files %d metadata %d.",
|
||||
dm_list_size(&devs), metadata_pv_count);
|
||||
_clear_scan_state(cmd, &devs_drop);
|
||||
_clear_scan_state(cmd, &devs);
|
||||
*found_incomplete = 1;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
*found_all = 1;
|
||||
return 1;
|
||||
bad:
|
||||
_clear_scan_state(cmd, &devs_drop);
|
||||
_clear_scan_state(cmd, &devs);
|
||||
free_po_list(&pvs_online);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Scan devices on the system to discover which are LVM devices.
|
||||
* Info about the LVM devices (PVs) is saved in lvmcache in a
|
||||
@@ -1130,6 +1406,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)
|
||||
@@ -1165,7 +1444,7 @@ int label_scan(struct cmd_context *cmd)
|
||||
* which we want to keep open) is higher than the current
|
||||
* soft limit.
|
||||
*/
|
||||
_prepare_open_file_limit(cmd, dm_list_size(&scan_devs));
|
||||
prepare_open_file_limit(cmd, dm_list_size(&scan_devs));
|
||||
|
||||
/*
|
||||
* Do the main scan.
|
||||
@@ -1305,7 +1584,7 @@ int label_read_pvid(struct device *dev, int *has_pvid)
|
||||
|
||||
lh = (struct label_header *)(buf + 512);
|
||||
if (memcmp(lh->id, LABEL_ID, sizeof(lh->id))) {
|
||||
/* Not an lvm deice */
|
||||
/* Not an lvm device */
|
||||
label_scan_invalidate(dev);
|
||||
return 1;
|
||||
}
|
||||
@@ -1315,7 +1594,7 @@ int label_read_pvid(struct device *dev, int *has_pvid)
|
||||
* rest of the label_header intact.
|
||||
*/
|
||||
if (memcmp(lh->type, LVM2_LABEL, sizeof(lh->type))) {
|
||||
/* Not an lvm deice */
|
||||
/* Not an lvm device */
|
||||
label_scan_invalidate(dev);
|
||||
return 1;
|
||||
}
|
||||
@@ -1440,6 +1719,41 @@ void label_scan_invalidate_lv(struct cmd_context *cmd, struct logical_volume *lv
|
||||
}
|
||||
}
|
||||
|
||||
void label_scan_invalidate_lvs(struct cmd_context *cmd, struct dm_list *lvs)
|
||||
{
|
||||
struct dm_list *devs;
|
||||
struct dm_active_device *dm_dev;
|
||||
unsigned devs_features = 0;
|
||||
struct device *dev;
|
||||
struct lv_list *lvl;
|
||||
dev_t devt;
|
||||
|
||||
if (get_device_list(NULL, &devs, &devs_features)) {
|
||||
if (devs_features & DM_DEVICE_LIST_HAS_UUID) {
|
||||
dm_list_iterate_items(dm_dev, devs)
|
||||
if (dm_dev->uuid &&
|
||||
strncmp(dm_dev->uuid, UUID_PREFIX, sizeof(UUID_PREFIX) - 1) == 0) {
|
||||
devt = MKDEV(dm_dev->major, dm_dev->minor);
|
||||
if ((dev = dev_cache_get_by_devt(cmd, devt, NULL, NULL)))
|
||||
label_scan_invalidate(dev);
|
||||
}
|
||||
/* ATM no further caching for any lvconvert command
|
||||
* TODO: any other command to be skipped ??
|
||||
*/
|
||||
if (strcmp(cmd->name, "lvconvert")) {
|
||||
dm_device_list_destroy(&cmd->cache_dm_devs);
|
||||
cmd->cache_dm_devs = devs; /* cache to avoid unneeded checks */
|
||||
devs = NULL;
|
||||
}
|
||||
}
|
||||
dm_device_list_destroy(&devs);
|
||||
}
|
||||
|
||||
if (!(devs_features & DM_DEVICE_LIST_HAS_UUID))
|
||||
dm_list_iterate_items(lvl, lvs)
|
||||
label_scan_invalidate_lv(cmd, lvl->lv);
|
||||
}
|
||||
|
||||
/*
|
||||
* Empty the bcache of all blocks and close all open fds,
|
||||
* but keep the bcache set up.
|
||||
|
||||
@@ -110,6 +110,7 @@ int label_scan_devs_excl(struct cmd_context *cmd, struct dev_filter *f, struct d
|
||||
int label_scan_dev(struct cmd_context *cmd, struct device *dev);
|
||||
void label_scan_invalidate(struct device *dev);
|
||||
void label_scan_invalidate_lv(struct cmd_context *cmd, struct logical_volume *lv);
|
||||
void label_scan_invalidate_lvs(struct cmd_context *cmd, struct dm_list *lvs);
|
||||
void label_scan_drop(struct cmd_context *cmd);
|
||||
void label_scan_destroy(struct cmd_context *cmd);
|
||||
int label_scan_setup_bcache(void);
|
||||
@@ -118,6 +119,9 @@ int label_scan_open_excl(struct device *dev);
|
||||
int label_scan_open_rw(struct device *dev);
|
||||
int label_scan_reopen_rw(struct device *dev);
|
||||
int label_read_pvid(struct device *dev, int *has_pvid);
|
||||
int label_scan_vg_online(struct cmd_context *cmd, const char *vgname,
|
||||
int *found_none, int *found_all, int *found_incomplete);
|
||||
|
||||
|
||||
int label_scan_for_pvid(struct cmd_context *cmd, char *pvid, struct device **dev_out);
|
||||
|
||||
@@ -134,4 +138,6 @@ void dev_invalidate(struct device *dev);
|
||||
void dev_set_last_byte(struct device *dev, uint64_t offset);
|
||||
void dev_unset_last_byte(struct device *dev);
|
||||
|
||||
void prepare_open_file_limit(struct cmd_context *cmd, unsigned int num_devs);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -330,6 +330,7 @@ int vg_write_lock_held(void)
|
||||
|
||||
int sync_local_dev_names(struct cmd_context* cmd)
|
||||
{
|
||||
dm_device_list_destroy(&cmd->cache_dm_devs);
|
||||
memlock_unlock(cmd);
|
||||
fs_unlock();
|
||||
return 1;
|
||||
|
||||
@@ -892,6 +892,7 @@ uint32_t log_journal_str_to_val(const char *str)
|
||||
return LOG_JOURNAL_OUTPUT;
|
||||
if (!strcasecmp(str, "debug"))
|
||||
return LOG_JOURNAL_DEBUG;
|
||||
log_warn("Ignoring unrecognized journal value.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -3100,9 +3100,7 @@ static int _vg_commit_mdas(struct volume_group *vg)
|
||||
{
|
||||
struct metadata_area *mda, *tmda;
|
||||
DM_LIST_INIT(ignored);
|
||||
int failed = 0;
|
||||
int good = 0;
|
||||
int cache_updated = 0;
|
||||
|
||||
/* Rearrange the metadata_areas_in_use so ignored mdas come first. */
|
||||
dm_list_iterate_items_safe(mda, tmda, &vg->fid->metadata_areas_in_use)
|
||||
@@ -3116,19 +3114,11 @@ static int _vg_commit_mdas(struct volume_group *vg)
|
||||
dm_list_iterate_items(mda, &vg->fid->metadata_areas_in_use) {
|
||||
if (mda->status & MDA_FAILED)
|
||||
continue;
|
||||
failed = 0;
|
||||
if (mda->ops->vg_commit &&
|
||||
!mda->ops->vg_commit(vg->fid, vg, mda)) {
|
||||
stack;
|
||||
failed = 1;
|
||||
} else
|
||||
good++;
|
||||
|
||||
/* Update cache first time we succeed */
|
||||
if (!failed && !cache_updated) {
|
||||
lvmcache_update_vg_from_write(vg);
|
||||
cache_updated = 1;
|
||||
}
|
||||
}
|
||||
if (good)
|
||||
return 1;
|
||||
@@ -3558,7 +3548,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 +5074,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);
|
||||
|
||||
@@ -609,8 +609,7 @@ int dm_check_version(void)
|
||||
int dm_cookie_supported(void)
|
||||
{
|
||||
return (dm_check_version() &&
|
||||
_dm_version >= 4 &&
|
||||
_dm_version_minor >= 15);
|
||||
((_dm_version == 4) ? _dm_version_minor >= 15 : _dm_version > 4));
|
||||
}
|
||||
|
||||
static int _dm_inactive_supported(void)
|
||||
@@ -763,6 +762,30 @@ struct dm_deps *dm_task_get_deps(struct dm_task *dmt)
|
||||
dmt->dmi.v4->data_start);
|
||||
}
|
||||
|
||||
/*
|
||||
* Round up the ptr to an 8-byte boundary.
|
||||
* Follow kernel pattern.
|
||||
*/
|
||||
#define ALIGN_MASK 7
|
||||
static size_t _align_val(size_t val)
|
||||
{
|
||||
return (val + ALIGN_MASK) & ~ALIGN_MASK;
|
||||
}
|
||||
static void *_align_ptr(void *ptr)
|
||||
{
|
||||
return (void *)_align_val((size_t)ptr);
|
||||
}
|
||||
|
||||
static int _check_has_event_nr(void) {
|
||||
static int _has_event_nr = -1;
|
||||
|
||||
if (_has_event_nr < 0)
|
||||
_has_event_nr = dm_check_version() &&
|
||||
((_dm_version == 4) ? _dm_version_minor >= 38 : _dm_version > 4);
|
||||
|
||||
return _has_event_nr;
|
||||
}
|
||||
|
||||
struct dm_names *dm_task_get_names(struct dm_task *dmt)
|
||||
{
|
||||
return (struct dm_names *) (((char *) dmt->dmi.v4) +
|
||||
@@ -1436,8 +1459,7 @@ static int _udev_complete(struct dm_task *dmt)
|
||||
static int _check_uevent_generated(struct dm_ioctl *dmi)
|
||||
{
|
||||
if (!dm_check_version() ||
|
||||
_dm_version < 4 ||
|
||||
_dm_version_minor < 17)
|
||||
((_dm_version == 4) ? _dm_version_minor < 17 : _dm_version < 4))
|
||||
/* can't check, assume uevent is generated */
|
||||
return 1;
|
||||
|
||||
@@ -1784,23 +1806,34 @@ static int _do_dm_ioctl_unmangle_string(char *str, const char *str_name,
|
||||
static int _dm_ioctl_unmangle_names(int type, struct dm_ioctl *dmi)
|
||||
{
|
||||
char buf[DM_NAME_LEN];
|
||||
struct dm_names *names;
|
||||
char buf_uuid[DM_UUID_LEN];
|
||||
struct dm_name_list *names;
|
||||
unsigned next = 0;
|
||||
char *name;
|
||||
int r = 1;
|
||||
uint32_t *event_nr;
|
||||
char *uuid_ptr;
|
||||
dm_string_mangling_t mangling_mode = dm_get_name_mangling_mode();
|
||||
|
||||
if ((name = dmi->name))
|
||||
r = _do_dm_ioctl_unmangle_string(name, "name", buf, sizeof(buf),
|
||||
dm_get_name_mangling_mode());
|
||||
r &= _do_dm_ioctl_unmangle_string(name, "name", buf, sizeof(buf),
|
||||
mangling_mode);
|
||||
|
||||
if (type == DM_DEVICE_LIST &&
|
||||
((names = ((struct dm_names *) ((char *)dmi + dmi->data_start)))) &&
|
||||
((names = ((struct dm_name_list *) ((char *)dmi + dmi->data_start)))) &&
|
||||
names->dev) {
|
||||
do {
|
||||
names = (struct dm_names *)((char *) names + next);
|
||||
r = _do_dm_ioctl_unmangle_string(names->name, "name",
|
||||
buf, sizeof(buf),
|
||||
dm_get_name_mangling_mode());
|
||||
names = (struct dm_name_list *)((char *) names + next);
|
||||
event_nr = _align_ptr(names->name + strlen(names->name) + 1);
|
||||
r &= _do_dm_ioctl_unmangle_string(names->name, "name",
|
||||
buf, sizeof(buf), mangling_mode);
|
||||
/* Unmangle also UUID within same loop */
|
||||
if (_check_has_event_nr() &&
|
||||
(event_nr[1] & DM_NAME_LIST_FLAG_HAS_UUID)) {
|
||||
uuid_ptr = _align_ptr(event_nr + 2);
|
||||
r &= _do_dm_ioctl_unmangle_string(uuid_ptr, "UUID", buf_uuid,
|
||||
sizeof(buf_uuid), mangling_mode);
|
||||
}
|
||||
next = names->next;
|
||||
} while (next);
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ CMIRRORDMAN = cmirrord.8
|
||||
LVMDBUSDMAN = lvmdbusd.8
|
||||
|
||||
MAN5=lvm.conf.5
|
||||
MAN7=lvmsystemid.7 lvmreport.7 lvmraid.7
|
||||
MAN7=lvmsystemid.7 lvmreport.7 lvmraid.7 lvmautoactivation.7
|
||||
|
||||
MAN8=lvm.8 lvmdump.8 lvm-fullreport.8 lvm-lvpoll.8 \
|
||||
lvcreate.8 lvchange.8 lvmconfig.8 lvconvert.8 lvdisplay.8 \
|
||||
@@ -47,7 +47,6 @@ MAN8=lvm.8 lvmdump.8 lvm-fullreport.8 lvm-lvpoll.8 \
|
||||
MAN8SO=lvm-config.8 lvm-dumpconfig.8
|
||||
MAN8DM=dmsetup.8 dmstats.8
|
||||
MAN8CLUSTER=
|
||||
MAN8SYSTEMD_GENERATORS=lvm2-activation-generator.8
|
||||
|
||||
ifeq (,$(findstring $(MAKECMDGOALS), distclean all_man install_all_man))
|
||||
MAN7 += lvmcache.7 lvmthin.7 lvmvdo.7
|
||||
@@ -119,7 +118,7 @@ TESTMAN=test.gen
|
||||
include $(top_builddir)/make.tmpl
|
||||
|
||||
CLEAN_TARGETS+=$(MAN5) $(MAN7) $(MAN8) $(MAN8SO) $(MAN8:%.8=%.8_gen) $(MAN8CLUSTER) \
|
||||
$(MAN8SYSTEMD_GENERATORS) $(MAN8DM) $(TESTMAN)
|
||||
$(MAN8DM) $(TESTMAN)
|
||||
|
||||
all: man device-mapper
|
||||
|
||||
@@ -127,11 +126,11 @@ all: man device-mapper
|
||||
|
||||
device-mapper: $(MAN8DM)
|
||||
|
||||
man: $(MAN5) $(MAN7) $(MAN8) $(MAN8SO) $(MAN8CLUSTER) $(MAN8SYSTEMD_GENERATORS)
|
||||
man: $(MAN5) $(MAN7) $(MAN8) $(MAN8SO) $(MAN8CLUSTER)
|
||||
|
||||
all_man: man
|
||||
|
||||
$(MAN5) $(MAN7) $(MAN8) $(MAN8SO) $(MAN8DM) $(MAN8CLUSTER) $(MAN8SYSTEMD_GENERATORS): Makefile
|
||||
$(MAN5) $(MAN7) $(MAN8) $(MAN8SO) $(MAN8DM) $(MAN8CLUSTER): Makefile
|
||||
|
||||
$(MANGENERATOR):
|
||||
@echo " [MAKE] $<"
|
||||
@@ -289,11 +288,6 @@ install_device-mapper: $(MAN8DM)
|
||||
$(Q) $(INSTALL) -d $(MAN8DIR)
|
||||
$(Q) $(INSTALL_DATA) $^ $(MAN8DIR)/
|
||||
|
||||
install_systemd_generators: $(MAN8SYSTEMD_GENERATORS)
|
||||
@echo " [INSTALL] $^"
|
||||
$(Q) $(INSTALL) -d $(MAN8DIR)
|
||||
$(Q) $(INSTALL_DATA) $^ $(MAN8DIR)/
|
||||
|
||||
install: install_lvm2 install_device-mapper install_cluster
|
||||
|
||||
install_all_man: install install_systemd_generators
|
||||
|
||||
@@ -563,7 +563,6 @@ Prepends source file name and code line number with libdm debugging.
|
||||
.P
|
||||
.BR lvm-fullreport (8),
|
||||
.BR lvm-lvpoll (8),
|
||||
.BR lvm2-activation-generator (8),
|
||||
.BR blkdeactivate (8),
|
||||
.BR lvmdump (8),
|
||||
.P
|
||||
@@ -579,6 +578,7 @@ Prepends source file name and code line number with libdm debugging.
|
||||
.BR lvmraid (7),
|
||||
.BR lvmthin (7),
|
||||
.BR lvmcache (7),
|
||||
.BR lvmautoactivation (7),
|
||||
.P
|
||||
.BR dmsetup (8),
|
||||
.BR dmstats (8),
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
.TH "LVM2-ACTIVATION-GENERATOR" "8" "LVM TOOLS #VERSION#" "Red Hat, Inc" "\""
|
||||
.
|
||||
.SH "NAME"
|
||||
.
|
||||
lvm2-activation-generator - generator for systemd units to activate LVM volumes on boot
|
||||
.
|
||||
.SH SYNOPSIS
|
||||
.
|
||||
.B #SYSTEMD_GENERATOR_DIR#/lvm2-activation-generator
|
||||
.
|
||||
.SH DESCRIPTION
|
||||
.
|
||||
The lvm2-activation-generator is called by \fBsystemd\fP(1) on boot to
|
||||
generate systemd units at runtime to activate LVM Logical Volumes (LVs)
|
||||
when global/event_activation=0 is set in \fBlvm.conf\fP(5). These units use
|
||||
\fBvgchange -aay\fP to activate LVs.
|
||||
.P
|
||||
If event_activation=1, the lvm2-activation-generator exits immediately without
|
||||
generating any systemd units, and LVM fully relies on event-based
|
||||
activation to activate LVs. In this case, event-generated
|
||||
.B pvscan --cache -aay
|
||||
commands activate LVs.
|
||||
.P
|
||||
These systemd units are generated by lvm2-activation-generator:
|
||||
.P
|
||||
.I lvm2-activation-early.service
|
||||
is run before systemd's special \fBcryptsetup.target\fP to activate
|
||||
LVs that are not layered on top of encrypted devices.
|
||||
.P
|
||||
.I lvm2-activation.service
|
||||
is run after systemd's special \fBcryptsetup.target\fP to activate
|
||||
LVs that are layered on top of encrypted devices.
|
||||
.P
|
||||
.I lvm2-activation-net.service
|
||||
is run after systemd's special \fBremote-fs-pre.target\fP to activate
|
||||
LVs that are layered on attached remote devices.
|
||||
.P
|
||||
Note that all the underlying LVM devices (Physical Volumes) need to be
|
||||
present when the service is run. If the there are any devices that appear
|
||||
to the system later, LVs using these devices need to be activated directly
|
||||
by \fBlvchange\fP(8) or \fBvgchange\fP(8).
|
||||
.P
|
||||
The lvm2-activation-generator implements the \fBGenerators Specification\fP
|
||||
as referenced in \fBsystemd\fP(1).
|
||||
.
|
||||
.SH SEE ALSO
|
||||
.nh
|
||||
.ad l
|
||||
.BR lvm.conf (5),
|
||||
.BR vgchange (8),
|
||||
.BR lvchange (8),
|
||||
.BR pvscan (8),
|
||||
.P
|
||||
.BR systemd (1),
|
||||
.BR systemd.target (5),
|
||||
.BR systemd.special (7),
|
||||
.P
|
||||
.BR udev (7)
|
||||
289
man/lvmautoactivation.7_main
Normal file
289
man/lvmautoactivation.7_main
Normal file
@@ -0,0 +1,289 @@
|
||||
.TH "LVMAUTOACTIVATION" "7" "LVM TOOLS #VERSION#" "Red Hat, Inc" "\""
|
||||
.
|
||||
.SH NAME
|
||||
.
|
||||
lvmautoactivation \(em LVM autoactivation
|
||||
.
|
||||
.SH DESCRIPTION
|
||||
.
|
||||
Autoactivation is the activation of LVs performed automatically by the
|
||||
system in response to LVM devices being attached to the machine. When all
|
||||
PVs in a VG have been attached, the VG is complete, and LVs in the VG are
|
||||
activated.
|
||||
.P
|
||||
Autoactivation of VGs, or specific LVs, can be prevented using vgchange or
|
||||
lvchange --setautoactivation n. The lvm.conf auto_activation_volume_list
|
||||
is another way to limit autoactivation.
|
||||
.
|
||||
.SS event autoactivation
|
||||
.P
|
||||
LVM autoactivation is "event based", in which complete VGs are activated
|
||||
in response to uevents which occur during system startup or at any time
|
||||
after the system has started. An old form of autoactivation was "static"
|
||||
in which complete VGs are activated at a fixed point during system startup
|
||||
by a systemd service, and not in response to events.
|
||||
.P
|
||||
Event based autoactivation is driven by udev, udev rules, and systemd.
|
||||
When a device is attached to a machine, a uevent is generated by the
|
||||
kernel to notify userspace of the new device. systemd-udev runs udev
|
||||
rules to process the new device. Udev rules use blkid to identify the
|
||||
device as an LVM PV and then execute the lvm-specific udev rule for the
|
||||
device, which triggers autoactivation.
|
||||
.P
|
||||
There are two variations of event based autoactivation that may be used on
|
||||
a system, depending on the LVM udev rule that is installed (found in
|
||||
/lib/udev/rules.d/.) The following summarizes the steps in each rule
|
||||
which lead to autoactivation:
|
||||
.P
|
||||
.B 69-dm-lvm-metad.rules
|
||||
.
|
||||
.IP \[bu] 2
|
||||
device /dev/name with major:minor X:Y is attached to the machine
|
||||
.
|
||||
.IP \[bu] 2
|
||||
systemd/udev runs blkid to identify /dev/name as an LVM PV
|
||||
.
|
||||
.IP \[bu] 2
|
||||
udev rule 69-dm-lvm-metad.rules is run for /dev/name
|
||||
.
|
||||
.IP \[bu] 2
|
||||
the lvm udev rule runs the systemd service lvm2-pvscan@X:Yservice
|
||||
.
|
||||
.IP \[bu] 2
|
||||
the lvm2-pvscan service runs:
|
||||
.br
|
||||
pvscan --cache -aay --major X --minor Y
|
||||
.
|
||||
.IP \[bu] 2
|
||||
pvscan reads the device, records that the PV is online
|
||||
(see pvs_online), and checks if the VG is complete.
|
||||
.
|
||||
.IP \[bu] 2
|
||||
if the VG is complete, pvscan creates the vgs_online temp file,
|
||||
and activates the VG.
|
||||
.
|
||||
.IP \[bu] 2
|
||||
the activation command output can be seen from
|
||||
systemctl status lvm2-pvscan*
|
||||
.P
|
||||
.B 69-dm-lvm.rules
|
||||
.
|
||||
.IP \[bu] 2
|
||||
device /dev/name with major:minor X:Y is attached to the machine
|
||||
.
|
||||
.IP \[bu] 2
|
||||
systemd/udev runs blkid to identify /dev/name as an LVM PV
|
||||
.
|
||||
.IP \[bu] 2
|
||||
udev rule 69-dm-lvm.rules is run for /dev/name
|
||||
.
|
||||
.IP \[bu] 2
|
||||
the lvm udev rule runs:
|
||||
.br
|
||||
pvscan --cache --listvg --checkcomplete --vgonline
|
||||
.br
|
||||
--autoactivation event --udevoutput --journal=output /dev/name
|
||||
.
|
||||
.IP \[bu] 2
|
||||
pvscan reads the device, records that the PV is online
|
||||
(see pvs_online), and checks if the VG is complete.
|
||||
.
|
||||
.IP \[bu] 2
|
||||
if the VG is complete, pvscan creates the vgs_online temp file,
|
||||
and prints the name of the VG for the udev rule to import:
|
||||
LVM_VG_NAME_COMPLETE='vgname'
|
||||
.
|
||||
.IP \[bu] 2
|
||||
if the lvm udev rule sees LVM_VG_NAME_COMPLETE from pvscan,
|
||||
it activates the VG using a transient systemd service named
|
||||
lvm-activate-<vgname>.
|
||||
.
|
||||
.IP \[bu] 2
|
||||
the lvm-activate-<vgname> service runs
|
||||
.br
|
||||
vgchange -aay --autoactivation event <vgname>
|
||||
.
|
||||
.IP \[bu] 2
|
||||
the activation command output can be seen from
|
||||
journalctl -u lvm-activate-<vgname>
|
||||
.P
|
||||
.
|
||||
.SS pvscan options
|
||||
.P
|
||||
.B --cache
|
||||
.br
|
||||
Read the <device> arg (and only that device), and record that
|
||||
the PV is online by creating the /run/lvm/pvs_online/<pvid>
|
||||
file containing the name of the VG and the device for the PV.
|
||||
.P
|
||||
.B -aay
|
||||
.br
|
||||
Activate the VG from the pvscan command
|
||||
(includes implicit --checkcomplete and --vgonline.)
|
||||
.P
|
||||
.B --checkcomplete
|
||||
.br
|
||||
Check if the VG is complete, i.e. all PVs are present on
|
||||
the system, by checking /run/lvm/pvs_online/<pvid> files.
|
||||
.P
|
||||
.B --vgonline
|
||||
.br
|
||||
Create /run/lvm/vgs_online/<vgname> if the VG is complete
|
||||
(used to ensure only one command performs activation.)
|
||||
.P
|
||||
.B --autoactivation event
|
||||
.br
|
||||
Inform the command it is used for event based autoactivation.
|
||||
.P
|
||||
.B --listvg
|
||||
.br
|
||||
Print the name of the VG using the device.
|
||||
.P
|
||||
.B --udevoutput
|
||||
.br
|
||||
Only print output that can be imported to the udev rule,
|
||||
using the udev environment key format, i.e. NAME='value'.
|
||||
.P
|
||||
.B --journal=output
|
||||
.br
|
||||
Send standard command output to the journal (when stdout
|
||||
is reserved for udev output.)
|
||||
.P
|
||||
.SS run files
|
||||
.P
|
||||
Autoactivation commands use a number of temp files in /run/lvm (with the
|
||||
expectation that /run is cleared between boots.)
|
||||
.P
|
||||
.B pvs_online
|
||||
.br
|
||||
pvscan --cache creates a file here for each PV that is attached. The file
|
||||
is named with the PVID and contains the VG name and device information.
|
||||
The existence of the file is used to determine when all PVs for a given VG
|
||||
are present. The device information in these files is also used to
|
||||
optimize locating devices for a VG when the VG is activated.
|
||||
.P
|
||||
.B pvs_lookup
|
||||
.br
|
||||
pvscan --cache creates a file here named for a VG (if one doesn't already
|
||||
exist.) The file contains a list of PVIDs in the VG. This is needed when
|
||||
a PV is processed which has no VG metadata, in which case the list of
|
||||
PVIDs from the lookup file is used to check if the VG is complete.
|
||||
.P
|
||||
.B vgs_online
|
||||
.br
|
||||
The first activation command (pvscan or vgchange) to create a file here,
|
||||
named for the VG, will activate the VG. This resolves a race when
|
||||
concurrent commands attempt to activate a VG at once.
|
||||
.
|
||||
.SS static autoactivation
|
||||
.P
|
||||
A static autoactivation method is no longer provided by lvm.
|
||||
Setting event_activation=0 still disables event based autoactivation.
|
||||
WARNING: disabling event activation without an alternative may prevent a
|
||||
system from booting. A custom systemd service could be written to run
|
||||
autoactivation during system startup, in which case disabling event
|
||||
autoactivation may be useful.
|
||||
.
|
||||
.SH EXAMPLES
|
||||
.P
|
||||
VG "vg" contains two PVs:
|
||||
.nf
|
||||
$ pvs -o name,vgname,uuid /dev/sdb /dev/sdc
|
||||
PV VG PV UUID
|
||||
/dev/sdb vg 1uKpaT-lFOZ-NLHX-j4jI-OBi1-QpdE-HZ5hZY
|
||||
/dev/sdc vg 5J3tM8-aIPe-2vbd-DBe7-bvRq-TGj0-DaKV2G
|
||||
.fi
|
||||
.P
|
||||
use of --cache:
|
||||
.nf
|
||||
$ pvscan --cache /dev/sdb
|
||||
pvscan[12922] PV /dev/sdb online.
|
||||
$ pvscan --cache /dev/sdc
|
||||
pvscan[12923] PV /dev/sdc online.
|
||||
|
||||
$ cat /run/lvm/pvs_online/1uKpaTlFOZNLHXj4jIOBi1QpdEHZ5hZY
|
||||
8:16
|
||||
vg:vg
|
||||
dev:/dev/sdb
|
||||
$ cat /run/lvm/pvs_online/5J3tM8aIPe2vbdDBe7bvRqTGj0DaKV2G
|
||||
8:32
|
||||
vg:vg
|
||||
dev:/dev/sdc
|
||||
.fi
|
||||
.P
|
||||
use of -aay:
|
||||
.nf
|
||||
$ pvscan --cache -aay /dev/sdb
|
||||
pvscan[12935] PV /dev/sdb online, VG vg incomplete (need 1).
|
||||
$ pvscan --cache -aay /dev/sdc
|
||||
pvscan[12936] PV /dev/sdc online, VG vg is complete.
|
||||
pvscan[12936] VG vg run autoactivation.
|
||||
1 logical volume(s) in volume group "vg" now active
|
||||
|
||||
$ cat /run/lvm/pvs_online/1uKpaTlFOZNLHXj4jIOBi1QpdEHZ5hZY
|
||||
8:16
|
||||
vg:vg
|
||||
dev:/dev/sdb
|
||||
$ cat /run/lvm/pvs_online/5J3tM8aIPe2vbdDBe7bvRqTGj0DaKV2G
|
||||
8:32
|
||||
vg:vg
|
||||
dev:/dev/sdc
|
||||
$ ls /run/lvm/vgs_online/vg
|
||||
/run/lvm/vgs_online/vg
|
||||
.fi
|
||||
.P
|
||||
use of --listvg:
|
||||
.nf
|
||||
$ pvscan --cache --listvg /dev/sdb
|
||||
VG vg
|
||||
$ pvscan --cache --listvg /dev/sdc
|
||||
VG vg
|
||||
|
||||
$ cat /run/lvm/pvs_online/1uKpaTlFOZNLHXj4jIOBi1QpdEHZ5hZY
|
||||
8:16
|
||||
vg:vg
|
||||
dev:/dev/sdb
|
||||
$ cat /run/lvm/pvs_online/5J3tM8aIPe2vbdDBe7bvRqTGj0DaKV2G
|
||||
8:32
|
||||
vg:vg
|
||||
dev:/dev/sdc
|
||||
.fi
|
||||
.P
|
||||
use of --checkcomplete:
|
||||
.nf
|
||||
$ pvscan --cache --listvg --checkcomplete --vgonline /dev/sdb
|
||||
pvscan[12996] PV /dev/sdb online, VG vg incomplete (need 1).
|
||||
VG vg incomplete
|
||||
$ pvscan --cache --listvg --checkcomplete --vgonline /dev/sdc
|
||||
pvscan[12997] PV /dev/sdc online, VG vg is complete.
|
||||
VG vg complete
|
||||
.fi
|
||||
.P
|
||||
use of --udevoutput:
|
||||
.nf
|
||||
$ pvscan --cache --listvg --checkcomplete --vgonline --udevoutput /dev/sdb
|
||||
LVM_VG_NAME_INCOMPLETE='vg'
|
||||
$ pvscan --cache --listvg --checkcomplete --vgonline --udevoutput /dev/sdc
|
||||
LVM_VG_NAME_COMPLETE='vg'
|
||||
.fi
|
||||
.P
|
||||
use of --listlvs:
|
||||
.nf
|
||||
$ lvs -o name,devices vg
|
||||
LV Devices
|
||||
lvol0 /dev/sdb(0)
|
||||
lvol1 /dev/sdc(0)
|
||||
lvol2 /dev/sdb(1),/dev/sdc(1)
|
||||
|
||||
$ pvscan --cache --listlvs --checkcomplete /dev/sdb
|
||||
pvscan[13288] PV /dev/sdb online, VG vg incomplete (need 1).
|
||||
VG vg incomplete
|
||||
LV vg/lvol0 complete
|
||||
LV vg/lvol2 incomplete
|
||||
$ pvscan --cache --listlvs --checkcomplete /dev/sdc
|
||||
pvscan[13289] PV /dev/sdc online, VG vg is complete.
|
||||
VG vg complete
|
||||
LV vg/lvol1 complete
|
||||
LV vg/lvol2 complete
|
||||
.fi
|
||||
|
||||
@@ -4,56 +4,47 @@ like
|
||||
or
|
||||
.BR pvdisplay (8).
|
||||
.P
|
||||
When the --cache and -aay options are used, pvscan records which PVs are
|
||||
available on the system, and activates LVs in completed VGs. A VG is
|
||||
complete when pvscan sees that the final PV in the VG has appeared. This
|
||||
is used by event-based system startup (systemd, udev) to activate LVs.
|
||||
.P
|
||||
The four main variations of this are:
|
||||
When --cache is used, pvscan updates runtime lvm state on the system, or
|
||||
with -aay performs autoactivation.
|
||||
.P
|
||||
.B pvscan --cache
|
||||
.I device
|
||||
.P
|
||||
If device is present, lvm adds a record that the PV on device is online.
|
||||
If device is present, lvm records that the PV on device is online.
|
||||
If device is not present, lvm removes the online record for the PV.
|
||||
In most cases, the pvscan will only read the named devices.
|
||||
.P
|
||||
.B pvscan --cache -aay
|
||||
.IR device ...
|
||||
.P
|
||||
This begins by performing the same steps as above. Afterward, if the VG
|
||||
for the specified PV is complete, then pvscan will activate LVs in the VG
|
||||
(the same as vgchange -aay vgname would do.)
|
||||
pvscan only reads the named device.
|
||||
.P
|
||||
.B pvscan --cache
|
||||
.P
|
||||
This first clears all existing PV online records, then scans all devices
|
||||
on the system, adding PV online records for any PVs that are found.
|
||||
Updates the runtime state for all lvm devices.
|
||||
.P
|
||||
.B pvscan --cache -aay
|
||||
.I device
|
||||
.P
|
||||
Performs the --cache steps for the device, then checks if the VG using the
|
||||
device is complete. If so, LVs in the VG are autoactivated, the same as
|
||||
vgchange -aay vgname would do. (A device name may be replaced with major
|
||||
and minor numbers.)
|
||||
.P
|
||||
.B pvscan --cache -aay
|
||||
.P
|
||||
This begins by performing the same steps as pvscan --cache. Afterward, it
|
||||
activates LVs in any complete VGs.
|
||||
Performs the --cache steps for all devices, then autoactivates any complete VGs.
|
||||
.P
|
||||
To prevent devices from being scanned by pvscan --cache, add them
|
||||
to
|
||||
.BR lvm.conf (5)
|
||||
.B devices/global_filter.
|
||||
For more information, see:
|
||||
.br
|
||||
.B lvmconfig --withcomments devices/global_filter
|
||||
.B pvscan --cache --listvg|--listlvs
|
||||
.I device
|
||||
.P
|
||||
Auto-activation of VGs or LVs can be enabled/disabled using:
|
||||
.br
|
||||
Performs the --cache steps for the device, then prints the name of the VG
|
||||
using the device, or the names of LVs using the device. --checkcomplete
|
||||
is usually included to check if all PVs for the VG or LVs are online.
|
||||
When this command is called by a udev rule, the output must conform to
|
||||
udev rule specifications (see --udevoutput.) The udev rule will use the
|
||||
results to perform autoactivation.
|
||||
.P
|
||||
Autoactivation of VGs or LVs can be enabled/disabled using vgchange or
|
||||
lvchange with --setautoactivation y|n, or by adding names to
|
||||
.BR lvm.conf (5)
|
||||
.B activation/auto_activation_volume_list
|
||||
.P
|
||||
For more information, see:
|
||||
.br
|
||||
.B lvmconfig --withcomments activation/auto_activation_volume_list
|
||||
.P
|
||||
To disable auto-activation, explicitly set this list to an empty list,
|
||||
i.e. auto_activation_volume_list = [ ].
|
||||
.P
|
||||
When this setting is undefined (e.g. commented), then all LVs are
|
||||
auto-activated.
|
||||
See
|
||||
.BR lvmautoactivation (7)
|
||||
for more information about how pvscan is used for autoactivation.
|
||||
|
||||
@@ -53,7 +53,6 @@
|
||||
.P
|
||||
.BR lvm-fullreport (8),
|
||||
.BR lvm-lvpoll (8),
|
||||
.BR lvm2-activation-generator (8),
|
||||
.BR blkdeactivate (8),
|
||||
.BR lvmdump (8),
|
||||
.P
|
||||
|
||||
@@ -15,9 +15,6 @@ srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
top_builddir = @top_builddir@
|
||||
|
||||
SOURCES = lvm2_activation_generator_systemd_red_hat.c
|
||||
TARGETS = lvm2_activation_generator_systemd_red_hat
|
||||
|
||||
include $(top_builddir)/make.tmpl
|
||||
|
||||
ifeq ("@BUILD_DMEVENTD@", "yes")
|
||||
@@ -78,17 +75,6 @@ ifeq ("@BLKDEACTIVATE@", "yes")
|
||||
$(Q) $(INSTALL_SCRIPT) blk_availability_init_red_hat $(initdir)/blk-availability
|
||||
endif
|
||||
|
||||
CFLAGS_lvm2_activation_generator_systemd_red_hat.o += $(EXTRA_EXEC_CFLAGS)
|
||||
|
||||
lvm2_activation_generator_systemd_red_hat: $(OBJECTS) $(LVMINTERNAL_LIBS)
|
||||
@echo " [CC] $@"
|
||||
$(Q) $(CC) -o $@ $(OBJECTS) $(CFLAGS) $(LDFLAGS) $(EXTRA_EXEC_LDFLAGS) $(ELDFLAGS) $(LVMINTERNAL_LIBS) $(LIBS)
|
||||
|
||||
install_systemd_generators:
|
||||
@echo " [INSTALL] systemd_generators"
|
||||
$(Q) $(INSTALL_DIR) $(systemd_generator_dir)
|
||||
$(Q) $(INSTALL_PROGRAM) lvm2_activation_generator_systemd_red_hat $(systemd_generator_dir)/lvm2-activation-generator
|
||||
|
||||
install_systemd_units: install_dbus_service
|
||||
@echo " [INSTALL] systemd_units"
|
||||
$(Q) $(INSTALL_DIR) $(systemd_unit_dir)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[Unit]
|
||||
Description=Availability of block devices
|
||||
Before=shutdown.target
|
||||
After=lvm2-activation.service iscsi-shutdown.service iscsi.service iscsid.service fcoe.service rbdmap.service
|
||||
After=iscsi-shutdown.service iscsi.service iscsid.service fcoe.service rbdmap.service
|
||||
DefaultDependencies=no
|
||||
Conflicts=shutdown.target
|
||||
|
||||
|
||||
@@ -1,221 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of the device-mapper userspace tools.
|
||||
*
|
||||
* 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 General Public License v.2.
|
||||
*
|
||||
* You should have received a copy of the GNU 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
|
||||
*/
|
||||
|
||||
// This file contains the unit testable parts of
|
||||
// lvm2_activation_generator_systemd_red_hat
|
||||
|
||||
#include "device_mapper/all.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h> /* For PATH_MAX for musl libc */
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
static void _error(const char *format, ...) __attribute__ ((format(printf, 1, 2)));
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
// I'm rolling my own version of popen() here because I do not want to
|
||||
// go through the shell.
|
||||
|
||||
struct child_process {
|
||||
pid_t pid;
|
||||
FILE *fp;
|
||||
};
|
||||
|
||||
static bool _open_child(struct child_process *child, const char *cmd, const char *argv[])
|
||||
{
|
||||
int r, pipe_fd[2];
|
||||
|
||||
r = pipe(pipe_fd);
|
||||
if (r < 0) {
|
||||
_error("call to pipe() failed: %d\n", r);
|
||||
return false;
|
||||
}
|
||||
|
||||
child->pid = fork();
|
||||
if (child->pid < 0) {
|
||||
(void) close(pipe_fd[0]);
|
||||
(void) close(pipe_fd[1]);
|
||||
_error("call to fork() failed: %d\n", r);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (child->pid == 0) {
|
||||
// child
|
||||
(void) close(pipe_fd[0]);
|
||||
if (pipe_fd[1] != STDOUT_FILENO) {
|
||||
(void) dup2(pipe_fd[1], STDOUT_FILENO);
|
||||
(void) close(pipe_fd[1]);
|
||||
}
|
||||
|
||||
/* Suppressing any use of syslog */
|
||||
(void) setenv("LVM_SUPPRESS_SYSLOG", "1", 1);
|
||||
|
||||
if (execv(cmd, (char *const *) argv) < 0)
|
||||
_error("execv failed: %s\n", strerror(errno));
|
||||
// Shouldn't get here unless exec failed.
|
||||
exit(1);
|
||||
} else {
|
||||
// parent
|
||||
(void) close(pipe_fd[1]);
|
||||
child->fp = fdopen(pipe_fd[0], "r");
|
||||
if (!child->fp) {
|
||||
_error("call to fdopen() failed\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns the child's exit status
|
||||
static bool _close_child(struct child_process *child)
|
||||
{
|
||||
int status;
|
||||
|
||||
(void) fclose(child->fp);
|
||||
|
||||
while (waitpid(child->pid, &status, 0) < 0)
|
||||
if (errno != EINTR)
|
||||
return -1;
|
||||
|
||||
if (WIFEXITED(status) && !WEXITSTATUS(status))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Aquiring config from the lvmconfig process
|
||||
|
||||
#define LVM_CONF_EVENT_ACTIVATION "global/event_activation"
|
||||
#define LVM_CONF_USE_LVMPOLLD "global/use_lvmpolld"
|
||||
|
||||
struct config {
|
||||
bool event_activation;
|
||||
bool sysinit_needed;
|
||||
};
|
||||
|
||||
static bool _begins_with(const char *line, const char *prefix, const char **rest)
|
||||
{
|
||||
size_t len = strlen(prefix);
|
||||
|
||||
if (strlen(line) < len)
|
||||
return false;
|
||||
|
||||
if (strncmp(line, prefix, len))
|
||||
return false;
|
||||
|
||||
*rest = line + len;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _parse_bool(const char *val, bool * result)
|
||||
{
|
||||
const char *b = val, *e;
|
||||
|
||||
while (*b && isspace(*b))
|
||||
b++;
|
||||
|
||||
if (!*b)
|
||||
goto parse_error;
|
||||
|
||||
e = b;
|
||||
while (*e && !isspace(*e))
|
||||
e++;
|
||||
|
||||
if ((e - b) != 1)
|
||||
goto parse_error;
|
||||
|
||||
// We only handle '1', or '0'
|
||||
if (*b == '1') {
|
||||
*result = true;
|
||||
return true;
|
||||
|
||||
} else if (*b == '0') {
|
||||
*result = false;
|
||||
return true;
|
||||
}
|
||||
// Fallthrough
|
||||
|
||||
parse_error:
|
||||
_error("couldn't parse bool value '%s'\n", val);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool _parse_line(const char *line, struct config *cfg)
|
||||
{
|
||||
const char *val;
|
||||
|
||||
if (_begins_with(line, "event_activation=", &val)) {
|
||||
return _parse_bool(val, &cfg->event_activation);
|
||||
|
||||
} else if (_begins_with(line, "use_lvmpolld=", &val)) {
|
||||
bool r;
|
||||
if (!_parse_bool(val, &r))
|
||||
return false;
|
||||
cfg->sysinit_needed = !r;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool _get_config(struct config *cfg, const char *lvmconfig_path)
|
||||
{
|
||||
static const char *_argv[] = {
|
||||
"lvmconfig", "--type", "full",
|
||||
LVM_CONF_EVENT_ACTIVATION, LVM_CONF_USE_LVMPOLLD, NULL
|
||||
};
|
||||
|
||||
bool r = true;
|
||||
char buffer[256];
|
||||
struct child_process child;
|
||||
|
||||
cfg->event_activation = false;
|
||||
cfg->sysinit_needed = true;
|
||||
|
||||
if (!_open_child(&child, lvmconfig_path, _argv)) {
|
||||
_error("couldn't open lvmconfig process\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
while (fgets(buffer, sizeof(buffer), child.fp)) {
|
||||
if (!_parse_line(buffer, cfg)) {
|
||||
_error("_parse_line() failed\n");
|
||||
r = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!_close_child(&child)) {
|
||||
_error("lvmconfig failed\n");
|
||||
r = false;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
@@ -10,5 +10,5 @@ Conflicts=shutdown.target
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
ExecStart=@SBINDIR@/lvm pvscan --cache --activate ay %i
|
||||
ExecStart=@SBINDIR@/lvm pvscan --cache --activate ay --autoactivation event %i
|
||||
ExecStop=@SBINDIR@/lvm pvscan --cache %i
|
||||
|
||||
@@ -1,233 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of the device-mapper userspace tools.
|
||||
*
|
||||
* 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 General Public License v.2.
|
||||
*
|
||||
* You should have received a copy of the GNU 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
|
||||
*/
|
||||
|
||||
// Code in this file gets included in the unit tests.
|
||||
#include "generator-internals.c"
|
||||
|
||||
// Logging
|
||||
|
||||
#define KMSG_DEV_PATH "/dev/kmsg"
|
||||
static int _kmsg_fd;
|
||||
|
||||
static void _log_init(void)
|
||||
{
|
||||
// failing is harmless
|
||||
_kmsg_fd = open(KMSG_DEV_PATH, O_WRONLY | O_NOCTTY);
|
||||
}
|
||||
|
||||
static void _log_exit(void)
|
||||
{
|
||||
if (_kmsg_fd != -1)
|
||||
(void) close(_kmsg_fd);
|
||||
}
|
||||
|
||||
__attribute__ ((format(printf, 1, 2)))
|
||||
static void _error(const char *format, ...)
|
||||
{
|
||||
int n;
|
||||
va_list ap;
|
||||
char message[PATH_MAX + 30]; /* +3 for '<n>' where n is the log level and +27 for lvm2-activation-generator: " prefix */
|
||||
|
||||
snprintf(message, 31, "<%d>lvm2-activation-generator: ", LOG_ERR);
|
||||
|
||||
va_start(ap, format);
|
||||
n = vsnprintf(message + 30, PATH_MAX, format, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (_kmsg_fd < 0 || (n < 0 || ((unsigned) n + 1 > PATH_MAX)))
|
||||
return;
|
||||
|
||||
/* The n+31: +30 for "<n>lvm2-activation-generator: " prefix and +1 for '\0' suffix */
|
||||
if (write(_kmsg_fd, message, n + 31) < 0)
|
||||
_error("Failed to write activation message %s: %m.\n", message);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
#define UNIT_TARGET_LOCAL_FS "local-fs-pre.target"
|
||||
#define UNIT_TARGET_REMOTE_FS "remote-fs-pre.target"
|
||||
|
||||
struct generator {
|
||||
const char *dir;
|
||||
struct config cfg;
|
||||
|
||||
int kmsg_fd;
|
||||
char unit_path[PATH_MAX];
|
||||
char target_path[PATH_MAX];
|
||||
};
|
||||
|
||||
enum {
|
||||
UNIT_EARLY,
|
||||
UNIT_MAIN,
|
||||
UNIT_NET
|
||||
};
|
||||
|
||||
static const char *_unit_names[] = {
|
||||
[UNIT_EARLY] = "lvm2-activation-early.service",
|
||||
[UNIT_MAIN] = "lvm2-activation.service",
|
||||
[UNIT_NET] = "lvm2-activation-net.service"
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
static int register_unit_with_target(struct generator *gen, const char *unit,
|
||||
const char *target)
|
||||
{
|
||||
int r = 1;
|
||||
|
||||
if (dm_snprintf(gen->target_path, PATH_MAX, "%s/%s.wants", gen->dir, target) < 0) {
|
||||
r = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
(void) dm_prepare_selinux_context(gen->target_path, S_IFDIR);
|
||||
if (mkdir(gen->target_path, 0755) < 0 && errno != EEXIST) {
|
||||
_error("Failed to create target directory %s: %m.\n", gen->target_path);
|
||||
r = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (dm_snprintf
|
||||
(gen->target_path, PATH_MAX, "%s/%s.wants/%s", gen->dir, target, unit) < 0) {
|
||||
r = 0;
|
||||
goto out;
|
||||
}
|
||||
(void) dm_prepare_selinux_context(gen->target_path, S_IFLNK);
|
||||
if (symlink(gen->unit_path, gen->target_path) < 0) {
|
||||
_error("Failed to create symlink for unit %s: %m.\n", unit);
|
||||
r = 0;
|
||||
}
|
||||
out:
|
||||
dm_prepare_selinux_context(NULL, 0);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int generate_unit(struct generator *gen, int unit)
|
||||
{
|
||||
FILE *f;
|
||||
const char *unit_name = _unit_names[unit];
|
||||
const char *target_name =
|
||||
unit == UNIT_NET ? UNIT_TARGET_REMOTE_FS : UNIT_TARGET_LOCAL_FS;
|
||||
|
||||
if (dm_snprintf(gen->unit_path, PATH_MAX, "%s/%s", gen->dir, unit_name)
|
||||
< 0)
|
||||
return 0;
|
||||
|
||||
if (!(f = fopen(gen->unit_path, "wxe"))) {
|
||||
_error("Failed to create unit file %s: %m.\n", unit_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
fputs("# Automatically generated by lvm2-activation-generator.\n"
|
||||
"#\n"
|
||||
"# This unit is responsible for direct activation of LVM logical volumes\n"
|
||||
"# if event-based activation not used (global/event_activation=0 in\n"
|
||||
"# lvm.conf). Direct LVM activation requires udev to be settled!\n\n"
|
||||
"[Unit]\n"
|
||||
"Description=LVM direct activation of logical volumes\n"
|
||||
"Documentation=man:lvm2-activation-generator(8)\n"
|
||||
"SourcePath=/etc/lvm/lvm.conf\n" "DefaultDependencies=no\n", f);
|
||||
|
||||
fputs("Conflicts=shutdown.target\n", f);
|
||||
|
||||
if (unit == UNIT_NET) {
|
||||
fprintf(f, "After=%s iscsi.service fcoe.service rbdmap.service\n"
|
||||
"Before=remote-fs-pre.target shutdown.target\n\n"
|
||||
"[Service]\n"
|
||||
"ExecStartPre=/usr/bin/udevadm settle\n", _unit_names[UNIT_MAIN]);
|
||||
} else {
|
||||
if (unit == UNIT_EARLY)
|
||||
fputs("After=systemd-udev-settle.service\n"
|
||||
"Before=cryptsetup.target\n", f);
|
||||
else
|
||||
fprintf(f, "After=%s cryptsetup.target\n", _unit_names[UNIT_EARLY]);
|
||||
|
||||
fputs("Before=local-fs-pre.target shutdown.target\n"
|
||||
"Wants=systemd-udev-settle.service\n\n" "[Service]\n", f);
|
||||
}
|
||||
|
||||
fputs("ExecStart=" LVM_PATH " vgchange -aay", f);
|
||||
if (gen->cfg.sysinit_needed)
|
||||
fputs(" --sysinit", f);
|
||||
fputs("\nType=oneshot\n", f);
|
||||
|
||||
if (fclose(f) < 0) {
|
||||
_error("Failed to write unit file %s: %m.\n", unit_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!register_unit_with_target(gen, unit_name, target_name)) {
|
||||
_error("Failed to register unit %s with target %s.\n",
|
||||
unit_name, target_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool _parse_command_line(struct generator *gen, int argc, const char **argv)
|
||||
{
|
||||
if (argc != 4) {
|
||||
_error("Incorrect number of arguments for activation generator.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
gen->dir = argv[1];
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _run(int argc, const char **argv)
|
||||
{
|
||||
bool r;
|
||||
mode_t old_mask;
|
||||
struct generator gen;
|
||||
|
||||
if (!_parse_command_line(&gen, argc, argv))
|
||||
return false;
|
||||
|
||||
if (_get_config(&gen.cfg, LVMCONFIG_PATH)) {
|
||||
if (gen.cfg.event_activation)
|
||||
// If event_activation=1, pvscan --cache -aay does activation.
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create the activation units if:
|
||||
* - _get_config succeeded and event_activation=0
|
||||
* - _get_config failed, then this is a failsafe fallback
|
||||
*/
|
||||
|
||||
/* mark lvm2-activation.*.service as world-accessible */
|
||||
old_mask = umask(0022);
|
||||
|
||||
r = generate_unit(&gen, UNIT_EARLY) &&
|
||||
generate_unit(&gen, UNIT_MAIN) && generate_unit(&gen, UNIT_NET);
|
||||
|
||||
umask(old_mask);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
bool r;
|
||||
|
||||
_log_init();
|
||||
r = _run(argc, argv);
|
||||
if (!r)
|
||||
_error("Activation generator failed.\n");
|
||||
_log_exit();
|
||||
|
||||
return r ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
Description=Monitoring of LVM2 mirrors, snapshots etc. using dmeventd or progress polling
|
||||
Documentation=man:dmeventd(8) man:lvcreate(8) man:lvchange(8) man:vgchange(8)
|
||||
Requires=dm-event.socket
|
||||
After=dm-event.socket dm-event.service lvm2-activation.service
|
||||
After=dm-event.socket dm-event.service
|
||||
Before=local-fs-pre.target shutdown.target
|
||||
DefaultDependencies=no
|
||||
Conflicts=shutdown.target
|
||||
|
||||
@@ -414,6 +414,12 @@ EOF
|
||||
verbose "Converting to VDO pool."
|
||||
dry "$LVM" lvconvert $YES $VERB $FORCE --config "$PARAMS" -Zn -V "${vdo_logicalSize}k" -n "$LVNAME" --type vdo-pool "$VGNAME/${LVNAME}_vpool"
|
||||
|
||||
# Note: that this is spelled OPPOSITE the other $IS_LV checks.
|
||||
if [ "$IS_LV" = "1" ]; then
|
||||
verbose "Removing now-unused VDO entry from VDO config."
|
||||
dry "$VDO" remove $VDOCONF --force --verbose --name "$VDONAME"
|
||||
fi
|
||||
|
||||
rm -fr "$TEMPDIR"
|
||||
}
|
||||
|
||||
|
||||
@@ -88,6 +88,7 @@ fi
|
||||
%{_sbindir}/lvmpolld
|
||||
%endif
|
||||
%{_mandir}/man5/lvm.conf.5.gz
|
||||
%{_mandir}/man7/lvmautoactivation.7.gz
|
||||
%{_mandir}/man7/lvmsystemid.7.gz
|
||||
%{_mandir}/man7/lvmreport.7.gz
|
||||
%{_mandir}/man7/lvmraid.7.gz
|
||||
@@ -100,9 +101,6 @@ fi
|
||||
%{_mandir}/man8/lvm-config.8.gz
|
||||
%{_mandir}/man8/lvm-dumpconfig.8.gz
|
||||
%{_mandir}/man8/lvm.8.gz
|
||||
%if %{enable_systemd}
|
||||
%{_mandir}/man8/lvm2-activation-generator.8.gz
|
||||
%endif
|
||||
%{_mandir}/man8/lvmconfig.8.gz
|
||||
%{_mandir}/man8/lvmdevices.8.gz
|
||||
%{_mandir}/man8/lvmdiskscan.8.gz
|
||||
@@ -189,7 +187,6 @@ fi
|
||||
%{_tmpfilesdir}/%{name}.conf
|
||||
%{_unitdir}/blk-availability.service
|
||||
%{_unitdir}/lvm2-monitor.service
|
||||
%attr(555, -, -) %{_prefix}/lib/systemd/system-generators/lvm2-activation-generator
|
||||
%if %{have_service lvmpolld}
|
||||
%{_unitdir}/lvm2-lvmpolld.service
|
||||
%{_unitdir}/lvm2-lvmpolld.socket
|
||||
|
||||
@@ -545,4 +545,73 @@ grep "$PVID2" "$DF" |tee out
|
||||
grep "$dev2" out
|
||||
not grep "$dev1" out
|
||||
|
||||
vgchange -an $vg1
|
||||
vgchange -an $vg2
|
||||
vgremove -ff $vg1
|
||||
vgremove -ff $vg2
|
||||
|
||||
# devnames change so the new devname now refers to a filtered device,
|
||||
# e.g. an mpath or md component, which is not scanned
|
||||
|
||||
wait_md_create() {
|
||||
local md=$1
|
||||
|
||||
while :; do
|
||||
if ! grep "$(basename $md)" /proc/mdstat; then
|
||||
echo "$md not ready"
|
||||
cat /proc/mdstat
|
||||
sleep 2
|
||||
else
|
||||
break
|
||||
fi
|
||||
done
|
||||
echo "$md" > WAIT_MD_DEV
|
||||
}
|
||||
|
||||
aux wipefs_a "$dev1"
|
||||
aux wipefs_a "$dev2"
|
||||
aux wipefs_a "$dev3"
|
||||
aux wipefs_a "$dev4"
|
||||
|
||||
mddev="/dev/md33"
|
||||
not grep $mddev /proc/mdstat || skip
|
||||
|
||||
rm "$DF"
|
||||
touch "$DF"
|
||||
vgcreate $vg1 "$dev1" "$dev2"
|
||||
cat "$DF"
|
||||
cp "$DF" "$ORIG"
|
||||
|
||||
# PVID with dashes for matching pvs -o+uuid output
|
||||
OPVID1=`pvs "$dev1" --noheading -o uuid | awk '{print $1}'`
|
||||
OPVID2=`pvs "$dev2" --noheading -o uuid | awk '{print $1}'`
|
||||
|
||||
# PVID without dashes for matching devices file fields
|
||||
PVID1=`pvs "$dev1" --noheading -o uuid | tr -d - | awk '{print $1}'`
|
||||
PVID2=`pvs "$dev2" --noheading -o uuid | tr -d - | awk '{print $1}'`
|
||||
|
||||
mdadm --create --metadata=1.0 "$mddev" --level 1 --raid-devices=2 "$dev3" "$dev4"
|
||||
wait_md_create "$mddev"
|
||||
|
||||
sed -e "s|DEVNAME=$dev1|DEVNAME=$dev3|" "$ORIG" > tmp1.devices
|
||||
sed -e "s|IDNAME=$dev1|IDNAME=$dev3|" tmp1.devices > "$DF"
|
||||
cat "$DF"
|
||||
pvs -o+uuid |tee out
|
||||
grep "$dev1" out
|
||||
grep "$dev2" out
|
||||
grep "$OPVID1" out
|
||||
grep "$OPVID2" out
|
||||
not grep "$dev3" out
|
||||
not grep "$dev4" out
|
||||
|
||||
grep "$dev1" "$DF"
|
||||
grep "$dev2" "$DF"
|
||||
grep "$PVID1" "$DF"
|
||||
grep "$PVID2" "$DF"
|
||||
not grep "$dev3" "$DF"
|
||||
not grep "$dev4" "$DF"
|
||||
|
||||
mdadm --stop "$mddev"
|
||||
aux udev_wait
|
||||
|
||||
vgremove -ff $vg1
|
||||
|
||||
@@ -423,7 +423,7 @@ sed "s/$pvid1/badpvid/" "$DF.orig" |tee $DF
|
||||
not grep $pvid1 $DF
|
||||
grep $did1 $DF
|
||||
|
||||
lvmdevices --check 2>&1|tee out
|
||||
not lvmdevices --check 2>&1|tee out
|
||||
grep $dev1 out
|
||||
grep badpvid out
|
||||
grep $pvid1 out
|
||||
@@ -493,7 +493,7 @@ rm $DF
|
||||
d1=$(basename $dev1)
|
||||
d3=$(basename $dev3)
|
||||
sed "s/$d1/$d3/" "$DF.orig" |tee $DF
|
||||
lvmdevices --check 2>&1 |tee out
|
||||
not lvmdevices --check 2>&1 |tee out
|
||||
grep $dev1 out
|
||||
|
||||
lvmdevices --update
|
||||
@@ -515,7 +515,7 @@ sed "s/$d1/tmp/" "$DF.orig" |tee ${DF}_1
|
||||
sed "s/$d2/$d1/" "${DF}_1" |tee ${DF}_2
|
||||
sed "s/tmp/$d2/" "${DF}_2" |tee $DF
|
||||
rm ${DF}_1 ${DF}_2
|
||||
lvmdevices --check 2>&1 |tee out
|
||||
not lvmdevices --check 2>&1 |tee out
|
||||
grep $dev1 out
|
||||
grep $dev2 out
|
||||
|
||||
@@ -536,7 +536,7 @@ rm $DF
|
||||
d1=$(basename $dev1)
|
||||
d3=$(basename $dev3)
|
||||
sed "s/$d1/$d3/" "$DF.orig" |tee $DF
|
||||
lvmdevices --check 2>&1 |tee out
|
||||
not lvmdevices --check 2>&1 |tee out
|
||||
grep $dev1 out
|
||||
|
||||
pvs -o+uuid,deviceid | grep $vg |tee out
|
||||
|
||||
67
test/shell/duplicate-pvs-multipath.sh
Normal file
67
test/shell/duplicate-pvs-multipath.sh
Normal file
@@ -0,0 +1,67 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright (C) 2021 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# 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 General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU 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
|
||||
|
||||
test_description='udev rule and systemd unit run vgchange'
|
||||
|
||||
SKIP_WITH_LVMPOLLD=1
|
||||
SKIP_WITH_LVMLOCKD=1
|
||||
|
||||
. lib/inittest
|
||||
|
||||
# FIXME: skip until mpath/scsi_debug cleanup works after a failure
|
||||
skip
|
||||
|
||||
modprobe --dry-run scsi_debug || skip
|
||||
multipath -l || skip
|
||||
multipath -l | grep scsi_debug && skip
|
||||
|
||||
# Turn off multipath_component_detection so that the duplicate
|
||||
# resolution of mpath components is used.
|
||||
aux lvmconf 'devices/multipath_component_detection = 0'
|
||||
# Prevent wwids from being used for filtering.
|
||||
aux lvmconf 'devices/multipath_wwids_file = "/dev/null"'
|
||||
# Need to use /dev/mapper/mpath
|
||||
aux lvmconf 'devices/dir = "/dev"'
|
||||
aux lvmconf 'devices/scan = "/dev"'
|
||||
# Could set filter to $MP and the component /dev/sd devs
|
||||
aux lvmconf "devices/filter = [ \"a|.*|\" ]"
|
||||
aux lvmconf "devices/global_filter = [ \"a|.*|\" ]"
|
||||
|
||||
modprobe scsi_debug dev_size_mb=100 num_tgts=1 vpd_use_hostno=0 add_host=4 delay=20 max_luns=2 no_lun_0=1
|
||||
sleep 2
|
||||
|
||||
multipath -r
|
||||
sleep 2
|
||||
|
||||
MPB=$(multipath -l | grep scsi_debug | cut -f1 -d ' ')
|
||||
echo $MPB
|
||||
MP=/dev/mapper/$MPB
|
||||
echo $MP
|
||||
|
||||
pvcreate $MP
|
||||
vgcreate $vg1 $MP
|
||||
lvcreate -l1 $vg1
|
||||
vgchange -an $vg1
|
||||
|
||||
pvs |tee out
|
||||
grep $MP out
|
||||
for i in $(grep -H scsi_debug /sys/block/sd*/device/model | cut -f4 -d /); do
|
||||
not grep /dev/$i out;
|
||||
done
|
||||
|
||||
vgchange -an $vg1
|
||||
vgremove -y $vg1
|
||||
|
||||
sleep 2
|
||||
multipath -f $MP
|
||||
sleep 1
|
||||
rmmod scsi_debug
|
||||
52
test/shell/duplicate-vgid.sh
Normal file
52
test/shell/duplicate-vgid.sh
Normal file
@@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright (C) 2008-2013 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# 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 General Public License v.2.
|
||||
|
||||
SKIP_WITH_LVMLOCKD=1
|
||||
SKIP_WITH_LVMPOLLD=1
|
||||
|
||||
. lib/inittest
|
||||
|
||||
aux prepare_devs 2
|
||||
|
||||
vgcreate $vg1 "$dev1"
|
||||
vgchange --setautoactivation n $vg1
|
||||
UUID1=$(vgs --noheading -o vg_uuid $vg1 | xargs)
|
||||
lvcreate -l1 -an -n $lv1 $vg1
|
||||
dd if="$dev1" of="$dev2" bs=1M count=1
|
||||
aux disable_dev "$dev1"
|
||||
vgrename $vg1 $vg2
|
||||
pvchange -u "$dev2"
|
||||
aux enable_dev "$dev1"
|
||||
|
||||
vgs -o+uuid |tee out
|
||||
grep $vg1 out | tee out1
|
||||
grep $UUID1 out1
|
||||
grep $vg2 out | tee out2
|
||||
grep $UUID1 out2
|
||||
|
||||
vgs $vg1
|
||||
vgs $vg2
|
||||
lvs $vg1/$lv1
|
||||
lvs $vg2/$lv1
|
||||
|
||||
lvremove $vg1/$lv1
|
||||
lvremove $vg2/$lv1
|
||||
|
||||
lvcreate -l1 -an -n $lv2 $vg1
|
||||
lvcreate -l1 -an -n $lv3 $vg2
|
||||
|
||||
vgchange -u $vg2
|
||||
|
||||
vgs -o uuid $vg1 |tee out
|
||||
grep $UUID1 out
|
||||
|
||||
vgs -o uuid $vg2 |tee out
|
||||
not grep $UUID1 out
|
||||
|
||||
vgremove -ff $vg1
|
||||
vgremove -ff $vg2
|
||||
@@ -57,9 +57,11 @@ dd if="$dev1" of=dev1_backup bs=1M
|
||||
|
||||
# pvcreate and pvremove can be forced even if the PV is marked as used
|
||||
pvremove -ff -y "$dev1"
|
||||
lvmdevices --deldev "$dev1" || true
|
||||
dd if=dev1_backup of="$dev1" bs=1M
|
||||
pvcreate -ff -y "$dev1"
|
||||
dd if=dev1_backup of="$dev1" bs=1M
|
||||
lvmdevices --adddev "$dev1" || true
|
||||
|
||||
# prepare a VG with $dev1 and $dev both having 1 MDA
|
||||
aux enable_dev "$dev2"
|
||||
@@ -116,9 +118,11 @@ dd if="$dev1" of=dev1_backup bs=1M
|
||||
|
||||
# pvcreate and pvremove can be forced even if the PV is marked as used
|
||||
pvremove -ff -y "$dev1"
|
||||
lvmdevices --deldev "$dev1" || true
|
||||
dd if=dev1_backup of="$dev1" bs=1M
|
||||
pvcreate -ff -y "$dev1"
|
||||
dd if=dev1_backup of="$dev1" bs=1M
|
||||
lvmdevices --adddev "$dev1" || true
|
||||
|
||||
# prepare a VG with $dev1 and $dev both having 1 MDA
|
||||
aux enable_dev "$dev2"
|
||||
|
||||
@@ -71,15 +71,11 @@ wipe_all() {
|
||||
done
|
||||
}
|
||||
|
||||
# udevadm trigger runs udev rule which runs systemd-run --no-wait vgchange -aay
|
||||
# Because of --no-wait, we need to wait for the transient systemd
|
||||
# service to be gone before checking the effects of the vgchange.
|
||||
|
||||
wait_lvm_activate() {
|
||||
local vgw=$1
|
||||
local wait=0
|
||||
|
||||
while systemctl status lvm-activate-$vgw > /dev/null && test "$wait" -le 30; do
|
||||
while systemctl status lvm-activate-$vgw | grep "active (running)" && test "$wait" -le 30; do
|
||||
sleep .2
|
||||
wait=$(( wait + 1 ))
|
||||
done
|
||||
@@ -219,6 +215,8 @@ udevadm trigger -c add /sys/block/$BDEV3
|
||||
aux udev_wait
|
||||
wait_lvm_activate $vg4
|
||||
|
||||
ls "$RUNDIR/lvm/pvs_lookup/"
|
||||
cat "$RUNDIR/lvm/pvs_lookup/$vg4" || true
|
||||
ls "$RUNDIR/lvm/pvs_online/$PVID1"
|
||||
ls "$RUNDIR/lvm/pvs_online/$PVID2"
|
||||
ls "$RUNDIR/lvm/pvs_online/$PVID3"
|
||||
@@ -375,6 +373,7 @@ touch $DF
|
||||
mdadm --create --metadata=1.0 "$mddev" --level 1 --chunk=64 --raid-devices=2 "$dev1" "$dev2"
|
||||
wait_md_create "$mddev"
|
||||
vgcreate $vg9 "$mddev"
|
||||
lvmdevices --adddev "$mddev" || true
|
||||
|
||||
PVIDMD=`pvs $mddev --noheading -o uuid | tr -d - | awk '{print $1}'`
|
||||
BDEVMD=$(basename "$mddev")
|
||||
@@ -382,8 +381,13 @@ BDEVMD=$(basename "$mddev")
|
||||
lvcreate -l1 -an -n $lv1 $vg9
|
||||
lvcreate -l1 -an -n $lv2 $vg9
|
||||
|
||||
mdadm --stop "$mddev"
|
||||
systemctl stop lvm-activate-$vg9 || true
|
||||
_clear_online_files
|
||||
mdadm --assemble "$mddev" "$dev1" "$dev2"
|
||||
|
||||
# this trigger might be redundant because the mdadm --assemble
|
||||
# probably triggers an add uevent
|
||||
udevadm trigger --settle -c add /sys/block/$BDEVMD
|
||||
|
||||
wait_lvm_activate $vg9
|
||||
@@ -401,3 +405,61 @@ mdadm --stop "$mddev"
|
||||
aux udev_wait
|
||||
wipe_all
|
||||
|
||||
systemctl stop lvm-activate-$vg1
|
||||
systemctl stop lvm-activate-$vg2
|
||||
systemctl stop lvm-activate-$vg3
|
||||
systemctl stop lvm-activate-$vg4
|
||||
systemctl stop lvm-activate-$vg5
|
||||
systemctl stop lvm-activate-$vg6
|
||||
systemctl stop lvm-activate-$vg7
|
||||
systemctl stop lvm-activate-$vg8
|
||||
systemctl stop lvm-activate-$vg9
|
||||
|
||||
|
||||
# no devices file, filter with symlink of PV
|
||||
# the pvscan needs to look at all dev names to
|
||||
# match the symlink in the filter with the
|
||||
# dev name (or major minor) passed to pvscan.
|
||||
# This test doesn't really belong in this file
|
||||
# because it's not testing lvm-activate.
|
||||
|
||||
aux lvmconf 'devices/use_devicesfile = 0'
|
||||
_clear_online_files
|
||||
rm "$DF"
|
||||
vgcreate $vg10 "$dev1"
|
||||
lvcreate -l1 -an -n $lv1 $vg10 "$dev1"
|
||||
|
||||
PVID1=$(pvs "$dev1" --noheading -o uuid | tr -d - | awk '{print $1}')
|
||||
# PVID with dashes
|
||||
OPVID1=`pvs "$dev1" --noheading -o uuid | awk '{print $1}'`
|
||||
|
||||
udevadm trigger --settle -c add /sys/block/$BDEV1
|
||||
|
||||
# uevent from the trigger should create this symlink
|
||||
ls /dev/disk/by-id/lvm-pv-uuid-$OPVID1
|
||||
|
||||
vgchange -an $vg10
|
||||
systemctl stop lvm-activate-$vg10
|
||||
_clear_online_files
|
||||
|
||||
aux lvmconf "devices/filter = [ \"a|/dev/disk/by-id/lvm-pv-uuid-$OPVID1|\", \"r|.*|\" ]"
|
||||
aux lvmconf 'devices/global_filter = [ "a|.*|" ]'
|
||||
|
||||
pvscan --cache -aay "$dev1"
|
||||
|
||||
check lv_field $vg10/$lv1 lv_active "active"
|
||||
|
||||
vgchange -an $vg10
|
||||
_clear_online_files
|
||||
|
||||
aux lvmconf 'devices/filter = [ "a|lvm-pv-uuid|", "r|.*|" ]'
|
||||
aux lvmconf 'devices/global_filter = [ "a|.*|" ]'
|
||||
|
||||
pvscan --cache -aay "$dev1"
|
||||
|
||||
check lv_field $vg10/$lv1 lv_active "active"
|
||||
|
||||
vgchange -an $vg10
|
||||
vgremove -y $vg10
|
||||
wipe_all
|
||||
|
||||
|
||||
199
test/shell/vgchange-pvs-online.sh
Normal file
199
test/shell/vgchange-pvs-online.sh
Normal file
@@ -0,0 +1,199 @@
|
||||
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
|
||||
|
||||
DFDIR="$LVM_SYSTEM_DIR/devices"
|
||||
mkdir -p "$DFDIR" || true
|
||||
DF="$DFDIR/system.devices"
|
||||
|
||||
# Because mapping devno to devname gets dm name from sysfs
|
||||
aux lvmconf 'devices/scan = "/dev"'
|
||||
base1=$(basename $dev1)
|
||||
base2=$(basename $dev2)
|
||||
base3=$(basename $dev3)
|
||||
base4=$(basename $dev4)
|
||||
bd1=/dev/mapper/$base1
|
||||
bd2=/dev/mapper/$base2
|
||||
bd3=/dev/mapper/$base3
|
||||
bd4=/dev/mapper/$base4
|
||||
aux extend_filter "a|/dev/mapper/$base1|"
|
||||
aux extend_filter "a|/dev/mapper/$base2|"
|
||||
aux extend_filter "a|/dev/mapper/$base3|"
|
||||
aux extend_filter "a|/dev/mapper/$base4|"
|
||||
|
||||
# Changing names will confuse df based on devname
|
||||
if lvmdevices; then
|
||||
rm -f "$DF"
|
||||
touch "$DF"
|
||||
lvmdevices --adddev "$bd1"
|
||||
lvmdevices --adddev "$bd2"
|
||||
lvmdevices --adddev "$bd3"
|
||||
lvmdevices --adddev "$bd4"
|
||||
cat "$DF"
|
||||
fi
|
||||
|
||||
# Using $bd instead of $dev because validation of pvid file content
|
||||
# checks that the devname begins with /dev.
|
||||
|
||||
# FIXME: test vgchange aay with pvs_online that includes devname in pvid file
|
||||
# and the devices file entry uses devname with a stale name.
|
||||
|
||||
vgcreate $vg1 "$bd1" "$bd2"
|
||||
vgcreate $vg2 "$bd3"
|
||||
pvcreate "$bd4"
|
||||
|
||||
lvcreate -l1 -n $lv1 -an $vg1
|
||||
lvcreate -l1 -n $lv2 -an $vg1
|
||||
lvcreate -l1 -n $lv1 -an $vg2
|
||||
|
||||
# Expected use, with vg name and all online files exist for vgchange.
|
||||
|
||||
_clear_online_files
|
||||
|
||||
pvscan --cache "$bd1"
|
||||
pvscan --cache "$bd2"
|
||||
vgchange -aay --autoactivation event $vg1
|
||||
check lv_field $vg1/$lv1 lv_active "active"
|
||||
check lv_field $vg1/$lv2 lv_active "active"
|
||||
check lv_field $vg2/$lv1 lv_active ""
|
||||
|
||||
pvscan --cache "$bd3"
|
||||
vgchange -aay --autoactivation event $vg2
|
||||
check lv_field $vg2/$lv1 lv_active "active"
|
||||
|
||||
# Count io to check the pvs_online optimization
|
||||
# is working to limit scanning.
|
||||
|
||||
if which strace; then
|
||||
vgchange -an
|
||||
_clear_online_files
|
||||
|
||||
pvscan --cache "$bd1"
|
||||
pvscan --cache "$bd2"
|
||||
strace -e io_submit vgchange -aay --autoactivation event $vg1 2>&1|tee trace.out
|
||||
test "$(grep io_submit trace.out | wc -l)" -eq 3
|
||||
rm trace.out
|
||||
|
||||
strace -e io_submit pvscan --cache "$bd3" 2>&1|tee trace.out
|
||||
test "$(grep io_submit trace.out | wc -l)" -eq 1
|
||||
rm trace.out
|
||||
|
||||
strace -e io_submit vgchange -aay --autoactivation event $vg2 2>&1|tee trace.out
|
||||
test "$(grep io_submit trace.out | wc -l)" -eq 2
|
||||
rm trace.out
|
||||
fi
|
||||
|
||||
# non-standard usage: no VG name arg, vgchange will only used pvs_online files
|
||||
|
||||
vgchange -an
|
||||
_clear_online_files
|
||||
|
||||
vgchange -aay --autoactivation event
|
||||
check lv_field $vg1/$lv1 lv_active ""
|
||||
check lv_field $vg1/$lv2 lv_active ""
|
||||
check lv_field $vg2/$lv1 lv_active ""
|
||||
|
||||
pvscan --cache "$bd1"
|
||||
vgchange -aay --autoactivation event
|
||||
check lv_field $vg1/$lv1 lv_active ""
|
||||
check lv_field $vg1/$lv2 lv_active ""
|
||||
check lv_field $vg2/$lv1 lv_active ""
|
||||
|
||||
pvscan --cache "$bd2"
|
||||
vgchange -aay --autoactivation event
|
||||
check lv_field $vg1/$lv1 lv_active "active"
|
||||
check lv_field $vg1/$lv2 lv_active "active"
|
||||
check lv_field $vg2/$lv1 lv_active ""
|
||||
|
||||
pvscan --cache "$bd3"
|
||||
vgchange -aay --autoactivation event
|
||||
check lv_field $vg2/$lv1 lv_active "active"
|
||||
|
||||
# non-standard usage: include VG name arg, but missing or incomplete pvs_online files
|
||||
|
||||
vgchange -an
|
||||
_clear_online_files
|
||||
|
||||
# all missing pvs_online, vgchange falls back to full label scan
|
||||
vgchange -aay --autoactivation event $vg1
|
||||
check lv_field $vg1/$lv1 lv_active "active"
|
||||
check lv_field $vg1/$lv2 lv_active "active"
|
||||
|
||||
vgchange -an
|
||||
_clear_online_files
|
||||
|
||||
# incomplete pvs_online, vgchange falls back to full label scan
|
||||
pvscan --cache "$bd1"
|
||||
vgchange -aay --autoactivation event $vg1
|
||||
check lv_field $vg1/$lv1 lv_active "active"
|
||||
check lv_field $vg1/$lv2 lv_active "active"
|
||||
|
||||
vgchange -an
|
||||
_clear_online_files
|
||||
|
||||
# incomplete pvs_online, pvs_online from different vg,
|
||||
# no pvs_online found for vg arg so vgchange falls back to full label scan
|
||||
|
||||
pvscan --cache "$bd3"
|
||||
vgchange -aay --autoactivation event $vg1
|
||||
check lv_field $vg1/$lv1 lv_active "active"
|
||||
check lv_field $vg1/$lv2 lv_active "active"
|
||||
check lv_field $vg2/$lv1 lv_active ""
|
||||
|
||||
vgchange -aay --autoactivation event $vg2
|
||||
check lv_field $vg2/$lv1 lv_active "active"
|
||||
|
||||
vgchange -an
|
||||
_clear_online_files
|
||||
|
||||
# same tests but using command options matching udev rule
|
||||
|
||||
pvscan --cache --listvg --checkcomplete --vgonline --autoactivation event --udevoutput --journal=output "$bd1"
|
||||
pvscan --cache --listvg --checkcomplete --vgonline --autoactivation event --udevoutput --journal=output "$bd2"
|
||||
vgchange -aay --autoactivation event $vg1
|
||||
check lv_field $vg1/$lv1 lv_active "active"
|
||||
check lv_field $vg1/$lv2 lv_active "active"
|
||||
check lv_field $vg2/$lv1 lv_active ""
|
||||
|
||||
pvscan --cache --listvg --checkcomplete --vgonline --autoactivation event --udevoutput --journal=output "$bd3"
|
||||
vgchange -aay --autoactivation event $vg2
|
||||
check lv_field $vg2/$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
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
UNIT_SOURCE=\
|
||||
device_mapper/vdo/status.c \
|
||||
\
|
||||
test/unit/activation-generator_t.c \
|
||||
test/unit/bcache_t.c \
|
||||
test/unit/bcache_utils_t.c \
|
||||
test/unit/bitset_t.c \
|
||||
|
||||
@@ -1,268 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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 General Public License v.2.
|
||||
*
|
||||
* You should have received a copy of the GNU 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 "units.h"
|
||||
#include "scripts/generator-internals.c"
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
static void _error(const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
vfprintf(stderr, format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
struct bw_test {
|
||||
const char *input;
|
||||
const char *prefix;
|
||||
const char *val;
|
||||
};
|
||||
|
||||
static void _test_begins_with(void *fixture)
|
||||
{
|
||||
static struct bw_test _tests[] = {
|
||||
{"", "foo", NULL},
|
||||
{"lskdj", "foo", NULL},
|
||||
{"foo", "foobar", NULL},
|
||||
{"fish", "fish", ""},
|
||||
{"foo=bar ", "foo=", "bar "},
|
||||
};
|
||||
|
||||
unsigned i;
|
||||
for (i = 0; i < DM_ARRAY_SIZE(_tests); i++) {
|
||||
const char *val;
|
||||
struct bw_test *t = _tests + i;
|
||||
if (t->val) {
|
||||
if (!_begins_with(t->input, t->prefix, &val))
|
||||
test_fail("_begins_with('%s', '%s') failed", t->input, t->prefix);
|
||||
if (strcmp(val, t->val))
|
||||
test_fail("_begins_with('%s', '%s') -> '%s', expected '%s'",
|
||||
t->input, t->prefix, val, t->val);
|
||||
} else {
|
||||
if (_begins_with(t->input, t->prefix, &val))
|
||||
test_fail("_begins_with('%s', '%s') unexpectedly succeeded",
|
||||
t->input, t->prefix);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct pb_test {
|
||||
const char *input;
|
||||
bool parsed;
|
||||
bool result;
|
||||
};
|
||||
|
||||
static const char *_bool(bool v)
|
||||
{
|
||||
return v ? "true" : "false";
|
||||
}
|
||||
|
||||
static void _test_parse_bool(void *fixture)
|
||||
{
|
||||
static struct pb_test _tests[] = {
|
||||
{"", false, false},
|
||||
{"fish", false, false},
|
||||
{"true", false, false},
|
||||
{"false", false, false},
|
||||
{"1", true, true},
|
||||
{" \t 1\t\t", true, true},
|
||||
{"0", true, false},
|
||||
{" \t0 ", true, false}
|
||||
};
|
||||
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < DM_ARRAY_SIZE(_tests); i++) {
|
||||
bool result;
|
||||
struct pb_test *t = _tests + i;
|
||||
|
||||
if (t->parsed) {
|
||||
if (!_parse_bool(t->input, &result))
|
||||
test_fail("_parse_bool('%s') unexpectedly failed", t->input);
|
||||
if (result != t->result)
|
||||
test_fail("_parse_bool('%s') -> %s", t->input, _bool(result));
|
||||
} else {
|
||||
if (_parse_bool(t->input, &result))
|
||||
test_fail("_parse_bool('%s') unexpectedly succeeded", t->input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct pl_test {
|
||||
const char *input;
|
||||
bool success;
|
||||
bool event_activation;
|
||||
bool sysinit_needed;
|
||||
};
|
||||
|
||||
static void _test_parse_line(void *fixture)
|
||||
{
|
||||
static struct pl_test _tests[] = {
|
||||
{"", false, false, false},
|
||||
{"sldkjfs", false, false, false},
|
||||
{"event_activation=1", true, true, true},
|
||||
{"event_activation=0", true, false, true},
|
||||
{"use_lvmpolld=1", true, false, false},
|
||||
{"use_lvmpolld=0", true, false, true}
|
||||
};
|
||||
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i< DM_ARRAY_SIZE(_tests); i++) {
|
||||
bool r;
|
||||
struct config cfg = {
|
||||
.sysinit_needed = true
|
||||
};
|
||||
struct pl_test *t = _tests + i;
|
||||
|
||||
r = _parse_line(t->input, &cfg);
|
||||
if (t->success) {
|
||||
if (!r)
|
||||
test_fail("_parse_line('%s') failed", t->input);
|
||||
|
||||
if (cfg.event_activation != t->event_activation)
|
||||
test_fail("_parse_line('%s') -> event_activation='%s'",
|
||||
t->input, _bool(cfg.event_activation));
|
||||
|
||||
if (cfg.sysinit_needed != t->sysinit_needed)
|
||||
test_fail("_parse_line('%s') -> sysinit_needed='%s'",
|
||||
t->input, _bool(cfg.sysinit_needed));
|
||||
} else if (r)
|
||||
test_fail("_parse_line('%s') succeeded", t->input);
|
||||
}
|
||||
}
|
||||
|
||||
static void _test_get_config_bad_path(void *fixture)
|
||||
{
|
||||
struct config cfg;
|
||||
|
||||
if (_get_config(&cfg, "/usr/bin/no-such-file"))
|
||||
test_fail("_get_config() succeeded despite a bad lvmconfig path");
|
||||
}
|
||||
|
||||
static void _test_get_config_bad_exit(void *fixture)
|
||||
{
|
||||
struct config cfg;
|
||||
|
||||
if (_get_config(&cfg, "/usr/bin/false"))
|
||||
test_fail("_get_config() succeeded despite a bad lvmconfig exit");
|
||||
}
|
||||
|
||||
struct gc_test {
|
||||
const char *output;
|
||||
bool success;
|
||||
bool event_activation;
|
||||
bool sysinit_needed;
|
||||
};
|
||||
|
||||
static const char *_fake_lvmconfig(const char *output)
|
||||
{
|
||||
const char *path = "./fake-lvmconfig";
|
||||
|
||||
FILE *fp = fopen(path, "w");
|
||||
if (!fp)
|
||||
return NULL;
|
||||
|
||||
fprintf(fp, "#!/usr/bin/env bash\n");
|
||||
fprintf(fp, "cat <<EOF\n");
|
||||
fprintf(fp, "%s", output);
|
||||
fprintf(fp, "EOF\n");
|
||||
|
||||
(void) fclose(fp);
|
||||
if (chmod(path, 0770))
|
||||
test_fail("chmod 0777 failed on path %s", path);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
static void _test_get_config(void *fixture)
|
||||
{
|
||||
static struct gc_test _tests[] = {
|
||||
{"", true, false, true},
|
||||
{"lsdjkf\n\n\n", false, false, false},
|
||||
|
||||
{"event_activation=0\nuse_lvmpolld=1\n", true, false, false},
|
||||
{"event_activation=1\nuse_lvmpolld=1\n", true, true, false},
|
||||
{"event_activation=1\nuse_lvmpolld=0\n", true, true, true},
|
||||
};
|
||||
|
||||
bool r;
|
||||
unsigned i;
|
||||
const char *path;
|
||||
|
||||
for (i = 0; i < DM_ARRAY_SIZE(_tests); i++) {
|
||||
struct gc_test *t = _tests + i;
|
||||
struct config cfg = {
|
||||
.sysinit_needed = true
|
||||
};
|
||||
|
||||
path = _fake_lvmconfig(t->output);
|
||||
if (!path)
|
||||
test_fail("couldn't create fake lvmconfig");
|
||||
|
||||
r = _get_config(&cfg, path);
|
||||
if (t->success) {
|
||||
if (!r)
|
||||
test_fail("_get_config() <- '%s' failed", t->output);
|
||||
|
||||
if (t->event_activation != cfg.event_activation)
|
||||
test_fail("_get_config() <- '%s', event_activation = %s",
|
||||
t->output, _bool(cfg.event_activation));
|
||||
|
||||
if (t->sysinit_needed != cfg.sysinit_needed)
|
||||
test_fail("_get_config() <- '%s', sysinit = %s",
|
||||
t->output, _bool(cfg.sysinit_needed));
|
||||
} else {
|
||||
if (r)
|
||||
test_fail("_get_config() <- '%s' unexpectedly succeeded", t->output);
|
||||
}
|
||||
|
||||
(void) unlink(path);
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
#define T(path, desc, fn) register_test(ts, "/activation-generator/" path, desc, fn)
|
||||
|
||||
static struct test_suite *_tests(void)
|
||||
{
|
||||
struct test_suite *ts = test_suite_create(NULL, NULL);
|
||||
if (!ts) {
|
||||
fprintf(stderr, "out of memory\n");
|
||||
exit(1);
|
||||
};
|
||||
|
||||
T("begins-with", "Test cases for _begins_with()", _test_begins_with);
|
||||
T("parse-bool", "Test cases for _parse_bool()", _test_parse_bool);
|
||||
T("parse-line", "Test cases for _parse_line()", _test_parse_line);
|
||||
T("get-config-bad-path", "_get_config() needs a valid lvmconfig path", _test_get_config_bad_path);
|
||||
T("get-config-bad-exit", "lvmconfig bad exit code gets propagated", _test_get_config_bad_exit);
|
||||
T("get-config", "Test cases for _get_config()", _test_get_config);
|
||||
|
||||
return ts;
|
||||
}
|
||||
|
||||
void activation_generator_tests(struct dm_list *all_tests)
|
||||
{
|
||||
dm_list_add(all_tests, &_tests()->list);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
@@ -20,7 +20,6 @@
|
||||
//-----------------------------------------------------------------
|
||||
|
||||
// Declare the function that adds tests suites here ...
|
||||
void activation_generator_tests(struct dm_list *suites);
|
||||
void bcache_tests(struct dm_list *suites);
|
||||
void bcache_utils_tests(struct dm_list *suites);
|
||||
void bitset_tests(struct dm_list *suites);
|
||||
@@ -37,7 +36,6 @@ void vdo_tests(struct dm_list *suites);
|
||||
// ... and call it in here.
|
||||
static inline void register_all_tests(struct dm_list *suites)
|
||||
{
|
||||
activation_generator_tests(suites);
|
||||
bcache_tests(suites);
|
||||
bcache_utils_tests(suites);
|
||||
bitset_tests(suites);
|
||||
|
||||
@@ -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"
|
||||
@@ -153,7 +159,8 @@ arg(cachesize_ARG, '\0', "cachesize", sizemb_VAL, 0, 0,
|
||||
"The size of cache to use.\n")
|
||||
|
||||
arg(check_ARG, '\0', "check", 0, 0, 0,
|
||||
"Check the content of the devices file.\n")
|
||||
"Checks the content of the devices file.\n"
|
||||
"Reports incorrect device names or PVIDs for entries.\n")
|
||||
|
||||
arg(commandprofile_ARG, '\0', "commandprofile", string_VAL, 0, 0,
|
||||
"The command profile to use for command configuration.\n"
|
||||
|
||||
@@ -1359,10 +1359,10 @@ OO: --aligned, --all, --binary, --colon, --columns,
|
||||
--configreport ConfigReport, --foreign, --history, --ignorelockingfailure,
|
||||
--logonly, --maps, --noheadings,
|
||||
--nosuffix, --options String, --sort String, --readonly,
|
||||
--reportformat ReportFmt, --segments, --select String, --separator String,
|
||||
--segments, --select String, --separator String,
|
||||
--shared, --unbuffered, --units Units
|
||||
OP: VG|LV|Tag ...
|
||||
IO: --partial, --ignoreskippedcluster
|
||||
IO: --partial, --ignoreskippedcluster, --reportformat ReportFmt
|
||||
ID: lvdisplay_general
|
||||
|
||||
---
|
||||
@@ -1590,10 +1590,10 @@ pvdisplay
|
||||
OO: --aligned, --all, --binary, --colon, --columns, --configreport ConfigReport,
|
||||
--foreign, --ignorelockingfailure,
|
||||
--logonly, --maps, --noheadings, --nosuffix, --options String,
|
||||
--readonly, --reportformat ReportFmt, --select String, --separator String, --shared,
|
||||
--readonly, --select String, --separator String, --shared,
|
||||
--short, --sort String, --unbuffered, --units Units
|
||||
OP: PV|Tag ...
|
||||
IO: --ignoreskippedcluster
|
||||
IO: --ignoreskippedcluster, --reportformat ReportFmt
|
||||
ID: pvdisplay_general
|
||||
|
||||
---
|
||||
@@ -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
|
||||
@@ -1809,10 +1811,10 @@ vgdisplay
|
||||
OO: --activevolumegroups, --aligned, --binary, --colon, --columns,
|
||||
--configreport ConfigReport, --foreign, --ignorelockingfailure,
|
||||
--logonly, --noheadings, --nosuffix,
|
||||
--options String, --readonly, --reportformat ReportFmt, --select String,
|
||||
--options String, --readonly, --select String,
|
||||
--shared, --short, --separator String, --sort String, --unbuffered, --units Units
|
||||
OP: VG|Tag ...
|
||||
IO: --partial, --ignoreskippedcluster
|
||||
IO: --partial, --ignoreskippedcluster, --reportformat ReportFmt
|
||||
ID: vgdisplay_general
|
||||
|
||||
---
|
||||
|
||||
@@ -824,12 +824,16 @@ static int _lvcreate_params(struct cmd_context *cmd,
|
||||
autobackup_ARG,\
|
||||
available_ARG,\
|
||||
contiguous_ARG,\
|
||||
devices_ARG,\
|
||||
devicesfile_ARG,\
|
||||
ignoreactivationskip_ARG,\
|
||||
ignoremonitoring_ARG,\
|
||||
journal_ARG,\
|
||||
metadataprofile_ARG,\
|
||||
monitor_ARG,\
|
||||
mirrors_ARG,\
|
||||
name_ARG,\
|
||||
nohints_ARG,\
|
||||
noudevsync_ARG,\
|
||||
permission_ARG,\
|
||||
persistent_ARG,\
|
||||
|
||||
@@ -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);
|
||||
@@ -3058,6 +3060,7 @@ int lvm_run_command(struct cmd_context *cmd, int argc, char **argv)
|
||||
int i;
|
||||
int skip_hyphens;
|
||||
int refresh_done = 0;
|
||||
int io;
|
||||
|
||||
/* Avoid excessive access to /etc/localtime and set TZ variable for glibc
|
||||
* so it does not need to check /etc/localtime everytime that needs that info */
|
||||
@@ -3140,6 +3143,20 @@ int lvm_run_command(struct cmd_context *cmd, int argc, char **argv)
|
||||
if (!(cmd->command = _find_command(cmd, cmd->name, &argc, argv)))
|
||||
return EINVALID_CMD_LINE;
|
||||
|
||||
/*
|
||||
* If option --foo is set which is listed in IO (ignore option) in
|
||||
* command-lines.in, then unset foo. Commands won't usually use an
|
||||
* ignored option, but there can be shared code that checks for --foo,
|
||||
* and should not find it to be set.
|
||||
*/
|
||||
for (io = 0; io < cmd->command->io_count; io++) {
|
||||
int opt = cmd->command->ignore_opt_args[io].opt;
|
||||
if (arg_is_set(cmd, opt)) {
|
||||
log_debug("Ignore opt %d", opt);
|
||||
cmd->opt_arg_values[opt].count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Remaining position args after command name and --options are removed.
|
||||
*/
|
||||
@@ -3318,8 +3335,8 @@ int lvm_run_command(struct cmd_context *cmd, int argc, char **argv)
|
||||
* ignore everything supplied on the command line of the
|
||||
* completed command.
|
||||
*/
|
||||
_reset_current_settings_to_default(cmd);
|
||||
_apply_current_settings(cmd);
|
||||
//_reset_current_settings_to_default(cmd);
|
||||
//_apply_current_settings(cmd);
|
||||
|
||||
/*
|
||||
* free off any memory the command used.
|
||||
|
||||
@@ -128,7 +128,6 @@ int lvmdevices(struct cmd_context *cmd, int argc, char **argv)
|
||||
struct device *dev;
|
||||
struct dev_use *du, *du2;
|
||||
const char *deviceidtype;
|
||||
int changes = 0;
|
||||
|
||||
dm_list_init(&search_pvids);
|
||||
dm_list_init(&found_devs);
|
||||
@@ -176,13 +175,19 @@ int lvmdevices(struct cmd_context *cmd, int argc, char **argv)
|
||||
log_error("Failed to read the devices file.");
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
|
||||
prepare_open_file_limit(cmd, dm_list_size(&cmd->use_devices));
|
||||
|
||||
dev_cache_scan(cmd);
|
||||
device_ids_match(cmd);
|
||||
|
||||
if (arg_is_set(cmd, check_ARG) || arg_is_set(cmd, update_ARG)) {
|
||||
int search_count = 0;
|
||||
int update_needed = 0;
|
||||
int invalid = 0;
|
||||
|
||||
unlink_searched_devnames(cmd);
|
||||
|
||||
label_scan_setup_bcache();
|
||||
|
||||
dm_list_iterate_items(du, &cmd->use_devices) {
|
||||
@@ -222,6 +227,8 @@ int lvmdevices(struct cmd_context *cmd, int argc, char **argv)
|
||||
* run just above.
|
||||
*/
|
||||
device_ids_validate(cmd, NULL, &invalid, 1);
|
||||
if (invalid)
|
||||
update_needed = 1;
|
||||
|
||||
/*
|
||||
* Find and fix any devname entries that have moved to a
|
||||
@@ -237,33 +244,24 @@ int lvmdevices(struct cmd_context *cmd, int argc, char **argv)
|
||||
label_scan_invalidate(du->dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* check du->part
|
||||
*/
|
||||
dm_list_iterate_items(du, &cmd->use_devices) {
|
||||
int part = 0;
|
||||
if (!du->dev)
|
||||
continue;
|
||||
dev = du->dev;
|
||||
|
||||
dev_get_partition_number(dev, &part);
|
||||
|
||||
if (part != du->part) {
|
||||
log_warn("Device %s partition %u has incorrect PART in devices file (%u)",
|
||||
dev_name(dev), part, du->part);
|
||||
du->part = part;
|
||||
changes++;
|
||||
}
|
||||
}
|
||||
|
||||
if (arg_is_set(cmd, update_ARG)) {
|
||||
if (invalid || !dm_list_empty(&found_devs)) {
|
||||
if (update_needed || !dm_list_empty(&found_devs)) {
|
||||
if (!device_ids_write(cmd))
|
||||
goto_bad;
|
||||
log_print("Updated devices file to version %s", devices_file_version());
|
||||
} else {
|
||||
log_print("No update for devices file is needed.");
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* --check exits with an error if the devices file
|
||||
* needs updates, i.e. running --update would make
|
||||
* changes.
|
||||
*/
|
||||
if (update_needed) {
|
||||
log_error("Updates needed for devices file.");
|
||||
goto bad;
|
||||
}
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
552
tools/pvscan.c
552
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, NULL);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -516,7 +289,7 @@ static int _write_lookup_file(struct cmd_context *cmd, struct volume_group *vg)
|
||||
line[ID_LEN+1] = '\0';
|
||||
|
||||
if (write(fd, &line, ID_LEN+1) < 0)
|
||||
log_sys_debug("write", path);
|
||||
log_error_pvscan(cmd, "Failed to write lookup entry %s %s", path, line);
|
||||
}
|
||||
|
||||
if (close(fd))
|
||||
@@ -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
|
||||
@@ -817,6 +509,7 @@ static int _get_devs_from_saved_vg(struct cmd_context *cmd, const char *vgname,
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
char file_vgname[NAME_LEN];
|
||||
char file_devname[NAME_LEN];
|
||||
char pvid[ID_LEN + 1] __attribute__((aligned(8))) = { 0 };
|
||||
char uuidstr[64] __attribute__((aligned(8)));
|
||||
struct pv_list *pvl;
|
||||
@@ -841,13 +534,14 @@ 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));
|
||||
memset(file_devname, 0, sizeof(file_devname));
|
||||
|
||||
_online_pvid_file_read(path, &file_major, &file_minor, file_vgname);
|
||||
online_pvid_file_read(path, &file_major, &file_minor, file_vgname, file_devname);
|
||||
|
||||
if (file_vgname[0] && strcmp(vgname, file_vgname)) {
|
||||
log_error_pvscan(cmd, "Wrong VG found for %d:%d PVID %s: %s vs %s",
|
||||
@@ -857,13 +551,8 @@ static int _get_devs_from_saved_vg(struct cmd_context *cmd, const char *vgname,
|
||||
|
||||
devno = MKDEV(file_major, file_minor);
|
||||
|
||||
if (!setup_devno_in_dev_cache(cmd, devno)) {
|
||||
log_error_pvscan(cmd, "No device set up for %d:%d PVID %s", file_major, file_minor, pvid);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!(dev = dev_cache_get_by_devt(cmd, devno, NULL, NULL))) {
|
||||
log_error_pvscan(cmd, "No device found for %d:%d PVID %s", file_major, file_minor, pvid);
|
||||
if (!(dev = setup_dev_in_dev_cache(cmd, devno, file_devname[0] ? file_devname : NULL))) {
|
||||
log_error_pvscan(cmd, "No device set up for online PV %d:%d %s PVID %s", file_major, file_minor, file_devname, pvid);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
@@ -875,7 +564,8 @@ static int _get_devs_from_saved_vg(struct cmd_context *cmd, const char *vgname,
|
||||
name1 = dev_name(dev);
|
||||
name2 = pvl->pv->device_hint;
|
||||
|
||||
if (strcmp(name1, name2)) {
|
||||
/* Probably pointless since dev is from online file which was already checked. */
|
||||
if (!strncmp(name2, "/dev/md", 7) && strncmp(name1, "/dev/md", 7)) {
|
||||
if (!id_write_format((const struct id *)pvid, uuidstr, sizeof(uuidstr)))
|
||||
uuidstr[0] = '\0';
|
||||
log_print_pvscan(cmd, "PVID %s read from %s last written to %s.", uuidstr, name1, name2);
|
||||
@@ -1036,7 +726,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 +760,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;
|
||||
@@ -1127,6 +817,49 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* The optimization in which only the pvscan arg devname is added to dev-cache
|
||||
* does not work if there's an lvm.conf filter containing symlinks to the dev
|
||||
* like /dev/disk/by-id/lvm-pv-uuid-xyz entries. A full dev_cache_scan will
|
||||
* associate the symlinks with the system dev name passed to pvscan, which lets
|
||||
* filter-regex match the devname with the symlink name in the filter.
|
||||
*/
|
||||
static int _filter_uses_symlinks(struct cmd_context *cmd, int filter_cfg)
|
||||
{
|
||||
const struct dm_config_node *cn;
|
||||
const struct dm_config_value *cv;
|
||||
const char *fname;
|
||||
|
||||
if ((cn = find_config_tree_array(cmd, filter_cfg, NULL))) {
|
||||
for (cv = cn->v; cv; cv = cv->next) {
|
||||
if (cv->type != DM_CFG_STRING)
|
||||
continue;
|
||||
if (!cv->v.str)
|
||||
continue;
|
||||
|
||||
fname = cv->v.str;
|
||||
|
||||
if (fname[0] != 'a')
|
||||
continue;
|
||||
|
||||
if (strstr(fname, "/dev/disk/"))
|
||||
return 1;
|
||||
if (strstr(fname, "/dev/mapper/"))
|
||||
return 1;
|
||||
|
||||
/* In case /dev/disk/by was omitted */
|
||||
if (strstr(fname, "lvm-pv-uuid"))
|
||||
return 1;
|
||||
if (strstr(fname, "dm-uuid"))
|
||||
return 1;
|
||||
if (strstr(fname, "wwn-"))
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct pvscan_arg {
|
||||
struct dm_list list;
|
||||
const char *devname;
|
||||
@@ -1191,21 +924,41 @@ static int _get_args_devs(struct cmd_context *cmd, struct dm_list *pvscan_args,
|
||||
struct pvscan_arg *arg;
|
||||
struct device_list *devl;
|
||||
|
||||
/*
|
||||
* If no devices file is used, and lvm.conf filter is set to
|
||||
* accept /dev/disk/by-id/lvm-pv-uuid-xyz or another symlink,
|
||||
* but pvscan --cache is passed devname or major:minor, so
|
||||
* pvscan needs to match its arg device to the filter symlink.
|
||||
* setup_dev_in_dev_cache() adds /dev/sda2 to dev-cache which
|
||||
* does not match a symlink to /dev/sda2, so we need a full
|
||||
* dev_cache_scan that will associate all symlinks to sda2,
|
||||
* which allows filter-regex to work. This case could be
|
||||
* optimized if needed by adding dev-cache entries for each
|
||||
* filter "a" entry (filter symlink patterns would still need
|
||||
* a full dev_cache_scan.)
|
||||
* (When no devices file is used and 69-dm-lvm.rules is
|
||||
* used which calls pvscan directly, symlinks may not
|
||||
* have been created by other rules when pvscan runs, so
|
||||
* the full dev_cache_scan may still not find them.)
|
||||
*/
|
||||
if (!cmd->enable_devices_file && !cmd->enable_devices_list &&
|
||||
(_filter_uses_symlinks(cmd, devices_filter_CFG) ||
|
||||
_filter_uses_symlinks(cmd, devices_global_filter_CFG))) {
|
||||
log_print_pvscan(cmd, "finding all devices for filter symlinks.");
|
||||
dev_cache_scan(cmd);
|
||||
}
|
||||
|
||||
/* pass NULL filter when getting devs from dev-cache, filtering is done separately */
|
||||
|
||||
/* in common usage, no dev will be found for a devno */
|
||||
|
||||
dm_list_iterate_items(arg, pvscan_args) {
|
||||
if (arg->devname) {
|
||||
if (!setup_devname_in_dev_cache(cmd, arg->devname))
|
||||
log_error_pvscan(cmd, "No device set up for name arg %s", arg->devname);
|
||||
arg->dev = dev_cache_get(cmd, arg->devname, NULL);
|
||||
} else if (arg->devno) {
|
||||
if (!setup_devno_in_dev_cache(cmd, arg->devno))
|
||||
log_error_pvscan(cmd, "No device set up for devno arg %d", (int)arg->devno);
|
||||
arg->dev = dev_cache_get_by_devt(cmd, arg->devno, NULL, NULL);
|
||||
} else
|
||||
if (!arg->devname && !arg->devno)
|
||||
return_0;
|
||||
if (!(arg->dev = setup_dev_in_dev_cache(cmd, arg->devno, arg->devname))) {
|
||||
log_error_pvscan(cmd, "No device set up for arg %s %d:%d",
|
||||
arg->devname ?: "", (int)MAJOR(arg->devno), (int)MINOR(arg->devno));
|
||||
}
|
||||
}
|
||||
|
||||
dm_list_iterate_items(arg, pvscan_args) {
|
||||
@@ -1225,6 +978,7 @@ static void _set_pv_devices_online(struct cmd_context *cmd, struct volume_group
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
char file_vgname[NAME_LEN];
|
||||
char file_devname[NAME_LEN];
|
||||
char pvid[ID_LEN+1] = { 0 };
|
||||
struct pv_list *pvl;
|
||||
struct device *dev;
|
||||
@@ -1240,7 +994,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 +1002,14 @@ 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';
|
||||
memset(file_vgname, 0, sizeof(file_vgname));
|
||||
memset(file_devname, 0, sizeof(file_devname));
|
||||
|
||||
_online_pvid_file_read(path, &major, &minor, file_vgname);
|
||||
online_pvid_file_read(path, &major, &minor, file_vgname, file_devname);
|
||||
|
||||
if (file_vgname[0] && strcmp(vg->name, file_vgname)) {
|
||||
log_warn("WARNING: VG %s PV %s wrong vgname in online file %s",
|
||||
@@ -1265,9 +1020,9 @@ static void _set_pv_devices_online(struct cmd_context *cmd, struct volume_group
|
||||
|
||||
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);
|
||||
if (!(dev = setup_dev_in_dev_cache(cmd, devno, file_devname[0] ? file_devname : NULL))) {
|
||||
log_print_pvscan(cmd, "VG %s PV %s no device found for online PV %d:%d %s",
|
||||
vg->name, pvid, major, minor, file_devname);
|
||||
pvl->pv->status |= MISSING_PV;
|
||||
continue;
|
||||
}
|
||||
@@ -1369,7 +1124,7 @@ static int _online_devs(struct cmd_context *cmd, int do_all, struct dm_list *pvs
|
||||
do_full_check = 0;
|
||||
|
||||
/* If use_full_md_check is set then this has already been done by filter. */
|
||||
if (!cmd->use_full_md_check) {
|
||||
if (!cmd->use_full_md_check && (cmd->dev_types->md_major != MAJOR(dev->dev))) {
|
||||
if (devsize && (pv->size != devsize))
|
||||
do_full_check = 1;
|
||||
if (pv->device_hint && !strncmp(pv->device_hint, "/dev/md", 7))
|
||||
@@ -1420,7 +1175,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;
|
||||
@@ -1450,22 +1205,39 @@ static int _online_devs(struct cmd_context *cmd, int do_all, struct dm_list *pvs
|
||||
|
||||
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.
|
||||
* Check if the VG is complete by checking that
|
||||
* pvs_online/<pvid> files exist for all vg->pvs.
|
||||
*/
|
||||
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);
|
||||
_count_pvid_files(vg, &pvs_online, &pvs_offline);
|
||||
if (!pvs_offline)
|
||||
log_print_pvscan(cmd, "VG %s complete after recheck.", vg->name);
|
||||
|
||||
/*
|
||||
* When there is more than one PV in the VG, write
|
||||
* /run/lvm/pvs_lookup/<vgname> with a list of PVIDs in
|
||||
* the VG. This is used in case a later PV comes
|
||||
* online that has no metadata, in which case pvscan
|
||||
* for that PV needs to use the lookup file to check if
|
||||
* the VG is complete. The lookup file is also used by
|
||||
* vgchange -aay --autoactivation event <vgname>
|
||||
* to check if all pvs_online files for the VG exist.
|
||||
*
|
||||
* For multiple concurrent pvscan's, they will race to
|
||||
* create the lookup file and the first will succeed.
|
||||
*
|
||||
* After writing the lookup file, recheck pvid files to
|
||||
* resolve a possible race with another pvscan reading
|
||||
* the lookup file that missed it.
|
||||
*/
|
||||
if (dm_list_size(&vg->pvs) > 1) {
|
||||
if (_write_lookup_file(cmd, vg)) {
|
||||
if (pvs_offline) {
|
||||
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 {
|
||||
/*
|
||||
@@ -1505,7 +1277,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 +1383,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 +1449,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
|
||||
@@ -1686,7 +1458,7 @@ static int _pvscan_cache_args(struct cmd_context *cmd, int argc, char **argv,
|
||||
* Does not do dev_cache_scan (adds nothing to dev-cache), and
|
||||
* does not do any device id matching.
|
||||
*/
|
||||
if (!setup_devices_for_pvscan_cache(cmd)) {
|
||||
if (!setup_devices_for_online_autoactivation(cmd)) {
|
||||
log_error_pvscan(cmd, "Failed to set up devices.");
|
||||
return 0;
|
||||
}
|
||||
@@ -1867,12 +1639,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 +1728,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 +1739,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>
|
||||
@@ -828,8 +829,11 @@ int lv_change_activate(struct cmd_context *cmd, struct logical_volume *lv,
|
||||
* user may want to take charge of activation changes to the VG
|
||||
* and not have the system autoactivation interfere.
|
||||
*/
|
||||
if (!is_change_activating(activate) && cmd->event_activation)
|
||||
if (!is_change_activating(activate) && cmd->event_activation &&
|
||||
!cmd->online_vg_file_removed) {
|
||||
cmd->online_vg_file_removed = 1;
|
||||
online_vg_file_remove(lv->vg->name);
|
||||
}
|
||||
|
||||
set_lv_notify(lv->vg->cmd);
|
||||
|
||||
@@ -2982,6 +2986,8 @@ int process_each_lv_in_vg(struct cmd_context *cmd, struct volume_group *vg,
|
||||
struct glv_list *glvl, *tglvl;
|
||||
int do_report_ret_code = 1;
|
||||
|
||||
cmd->online_vg_file_removed = 0;
|
||||
|
||||
log_set_report_object_type(LOG_REPORT_OBJECT_TYPE_LV);
|
||||
|
||||
vg_uuid[0] = '\0';
|
||||
@@ -3116,8 +3122,7 @@ int process_each_lv_in_vg(struct cmd_context *cmd, struct volume_group *vg,
|
||||
* in bcache, and needs to be closed so the open fd doesn't
|
||||
* interfere with processing the LV.
|
||||
*/
|
||||
dm_list_iterate_items(lvl, &final_lvs)
|
||||
label_scan_invalidate_lv(cmd, lvl->lv);
|
||||
label_scan_invalidate_lvs(cmd, &final_lvs);
|
||||
|
||||
dm_list_iterate_items(lvl, &final_lvs) {
|
||||
lv_uuid[0] = '\0';
|
||||
|
||||
@@ -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
|
||||
|
||||
163
tools/vgchange.c
163
tools/vgchange.c
@@ -15,10 +15,12 @@
|
||||
|
||||
#include "tools.h"
|
||||
#include "lib/device/device_id.h"
|
||||
#include "lib/label/hints.h"
|
||||
|
||||
struct vgchange_params {
|
||||
int lock_start_count;
|
||||
unsigned int lock_start_sanlock : 1;
|
||||
unsigned int vg_complete_to_activate : 1;
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -195,10 +197,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 +222,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 +659,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 +714,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,9 +735,143 @@ static int _vgchange_single(struct cmd_context *cmd, const char *vg_name,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _vgchange_autoactivation_setup(struct cmd_context *cmd,
|
||||
struct vgchange_params *vp,
|
||||
int *skip_command,
|
||||
const char **vgname_ret,
|
||||
uint32_t *flags)
|
||||
{
|
||||
const char *aa;
|
||||
char *vgname = NULL;
|
||||
int vg_locked = 0;
|
||||
int found_none = 0, found_all = 0, found_incomplete = 0;
|
||||
|
||||
if (!(aa = arg_str_value(cmd, autoactivation_ARG, NULL)))
|
||||
return_0;
|
||||
|
||||
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;
|
||||
cmd->use_hints = 0;
|
||||
|
||||
/*
|
||||
* Add an option to skip the pvs_online optimization? e.g.
|
||||
* "online_skip" in --autoactivation / auto_activation_settings
|
||||
*
|
||||
* if (online_skip)
|
||||
* return 1;
|
||||
*/
|
||||
|
||||
/* reads devices file, does not populate dev-cache */
|
||||
if (!setup_devices_for_online_autoactivation(cmd))
|
||||
return_0;
|
||||
|
||||
get_single_vgname_cmd_arg(cmd, NULL, &vgname);
|
||||
|
||||
/*
|
||||
* Lock the VG before scanning the PVs so _vg_read can avoid the normal
|
||||
* lock_vol+rescan (READ_WITHOUT_LOCK avoids the normal lock_vol and
|
||||
* can_use_one_scan avoids the normal rescan.) If this early lock_vol
|
||||
* fails, continue and use the normal lock_vol in _vg_read.
|
||||
*/
|
||||
if (vgname) {
|
||||
if (!lock_vol(cmd, vgname, LCK_VG_WRITE, NULL)) {
|
||||
log_debug("Failed early VG locking for autoactivation.");
|
||||
} else {
|
||||
*flags |= READ_WITHOUT_LOCK;
|
||||
cmd->can_use_one_scan = 1;
|
||||
vg_locked = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Perform label_scan on PVs that are online (per /run/lvm files)
|
||||
* for the given VG (or when no VG name is given, all online PVs.)
|
||||
* If this fails, the caller will do a normal process_each_vg without
|
||||
* optimizations (which will do a full label_scan.)
|
||||
*/
|
||||
if (!label_scan_vg_online(cmd, vgname, &found_none, &found_all, &found_incomplete)) {
|
||||
log_print("PVs online error%s%s, using all devices.", vgname ? " for VG " : "", vgname ?: "");
|
||||
goto bad;
|
||||
}
|
||||
|
||||
/*
|
||||
* Not the expected usage, activate any VGs that are complete based on
|
||||
* pvs_online. Only online pvs are used.
|
||||
*/
|
||||
if (!vgname) {
|
||||
*flags |= PROCESS_SKIP_SCAN;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* The expected and optimal usage, which is the purpose of
|
||||
* this function. We expect online files to be found for
|
||||
* all PVs because the udev rule calls
|
||||
* vgchange -aay --autoactivation event <vgname>
|
||||
* only after all PVs for vgname are found online.
|
||||
*/
|
||||
if (found_all) {
|
||||
*flags |= PROCESS_SKIP_SCAN;
|
||||
*vgname_ret = vgname;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Not expected usage, no online pvs for the vgname were found. The
|
||||
* caller will fall back to process_each doing a full label_scan to
|
||||
* look for the VG. (No optimization used.)
|
||||
*/
|
||||
if (found_none) {
|
||||
log_print("PVs online not found for VG %s, using all devices.", vgname);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
/*
|
||||
* Not expected usage, only some online pvs for the vgname were found.
|
||||
* The caller will fall back to process_each doing a full label_scan to
|
||||
* look for all PVs in the VG. (No optimization used.)
|
||||
*/
|
||||
if (found_incomplete) {
|
||||
log_print("PVs online incomplete for VG %s, using all devicess.", vgname);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
/*
|
||||
* Shouldn't happen, the caller will fall back to standard
|
||||
* process_each. (No optimization used.)
|
||||
*/
|
||||
log_print("PVs online unknown for VG %s, using all devices.", vgname);
|
||||
|
||||
bad:
|
||||
/*
|
||||
* The online scanning optimization didn't work, so undo the vg
|
||||
* locking optimization before falling back to normal processing.
|
||||
*/
|
||||
if (vg_locked) {
|
||||
unlock_vg(cmd, NULL, vgname);
|
||||
*flags &= ~READ_WITHOUT_LOCK;
|
||||
cmd->can_use_one_scan = 0;
|
||||
}
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
int vgchange(struct cmd_context *cmd, int argc, char **argv)
|
||||
{
|
||||
struct vgchange_params vp = { 0 };
|
||||
struct processing_handle *handle;
|
||||
const char *vgname = NULL;
|
||||
uint32_t flags = 0;
|
||||
int ret;
|
||||
|
||||
@@ -837,6 +984,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 (!_vgchange_autoactivation_setup(cmd, &vp, &skip_command, &vgname, &flags))
|
||||
return ECMD_FAILED;
|
||||
if (skip_command)
|
||||
return ECMD_PROCESSED;
|
||||
}
|
||||
|
||||
if (update)
|
||||
flags |= READ_FOR_UPDATE;
|
||||
else if (arg_is_set(cmd, activate_ARG))
|
||||
@@ -847,7 +1002,9 @@ int vgchange(struct cmd_context *cmd, int argc, char **argv)
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
|
||||
ret = process_each_vg(cmd, argc, argv, NULL, NULL, flags, 0, handle, &_vgchange_single);
|
||||
handle->custom_handle = &vp;
|
||||
|
||||
ret = process_each_vg(cmd, argc, argv, vgname, NULL, flags, 0, handle, &_vgchange_single);
|
||||
|
||||
destroy_processing_handle(cmd, handle);
|
||||
return ret;
|
||||
|
||||
@@ -727,30 +727,6 @@ int vgsplit(struct cmd_context *cmd, int argc, char **argv)
|
||||
backup(vg_from);
|
||||
}
|
||||
|
||||
/*
|
||||
* Finally, remove the EXPORTED flag from the new VG and write it out.
|
||||
* We need to unlock vg_to because vg_read_for_update wants to lock it.
|
||||
*/
|
||||
if (!test_mode()) {
|
||||
unlock_vg(cmd, NULL, vg_name_to);
|
||||
release_vg(vg_to);
|
||||
|
||||
/*
|
||||
* This command uses the exported vg flag internally, but
|
||||
* exported VGs are not allowed to be split from the command
|
||||
* level, so ALLOW_EXPORTED is not set in commands.h.
|
||||
*/
|
||||
cmd->include_exported_vgs = 1;
|
||||
|
||||
vg_to = vg_read_for_update(cmd, vg_name_to, NULL, 0, 0);
|
||||
|
||||
if (!vg_to) {
|
||||
log_error("Volume group \"%s\" became inconsistent: "
|
||||
"please fix manually", vg_name_to);
|
||||
goto bad;
|
||||
}
|
||||
}
|
||||
|
||||
vg_to->status &= ~EXPORTED_VG;
|
||||
|
||||
if (!vg_write(vg_to) || !vg_commit(vg_to))
|
||||
|
||||
@@ -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 --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