mirror of
git://sourceware.org/git/lvm2.git
synced 2025-10-27 11:33:19 +03:00
Compare commits
3 Commits
dev-dct-de
...
dev-dct-ac
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
325a4ea67e | ||
|
|
baf834f9f6 | ||
|
|
94866597de |
@@ -1 +1 @@
|
||||
1.02.185-git (2022-02-07)
|
||||
1.02.183-git (2021-10-20)
|
||||
|
||||
12
WHATS_NEW
12
WHATS_NEW
@@ -1,13 +1,5 @@
|
||||
Version 2.03.16 -
|
||||
====================================
|
||||
|
||||
Version 2.03.15 - 07th February 2022
|
||||
====================================
|
||||
Remove service based autoactivation. global/event_activation = 0 is NOOP.
|
||||
Improve support for metadata profiles for --type writecache.
|
||||
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.15 -
|
||||
===================================
|
||||
|
||||
Version 2.03.14 - 20th October 2021
|
||||
===================================
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
Version 1.02.185 -
|
||||
=====================================
|
||||
|
||||
Version 1.02.183 - 07th February 2022
|
||||
=====================================
|
||||
Unmangle UUIDs for DM_DEVICE_LIST ioctl.
|
||||
Version 1.02.183 -
|
||||
====================================
|
||||
|
||||
Version 1.02.181 - 20th October 2021
|
||||
====================================
|
||||
|
||||
@@ -1151,11 +1151,16 @@ global {
|
||||
# lvdisplay_shows_full_device_path = 0
|
||||
|
||||
# Configuration option global/event_activation.
|
||||
# Disable event based autoactivation commands.
|
||||
# WARNING: setting this to zero may cause machine startup to fail.
|
||||
# Previously, setting this to zero would enable static autoactivation
|
||||
# services (via the lvm2-activation-generator), but the autoactivation
|
||||
# services and generator have been removed.
|
||||
# Activate LVs based on system-generated device events.
|
||||
# When a PV appears on the system, a system-generated uevent triggers
|
||||
# the lvm2-pvscan service which runs the pvscan --cache -aay command.
|
||||
# If the new PV completes a VG, pvscan autoactivates LVs in the VG.
|
||||
# When event_activation is disabled, the lvm2-activation services are
|
||||
# generated and run at fixed points during system startup. These
|
||||
# services run vgchange -aay to autoactivate LVs in VGs that happen
|
||||
# to be present at that point in time.
|
||||
# See the --setautoactivation option or the auto_activation_volume_list
|
||||
# setting to configure autoactivation for specific VGs or LVs.
|
||||
# This configuration option has an automatic default value.
|
||||
# event_activation = 1
|
||||
|
||||
|
||||
45
configure.ac
45
configure.ac
@@ -781,39 +781,6 @@ AC_ARG_WITH(default-run-dir,
|
||||
AC_DEFINE_UNQUOTED(DEFAULT_RUN_DIR, ["$DEFAULT_RUN_DIR"],
|
||||
[Default LVM run directory.])
|
||||
|
||||
################################################################################
|
||||
dnl -- Build cluster mirror log daemon
|
||||
AC_MSG_CHECKING(whether to build cluster mirror log daemon)
|
||||
AC_ARG_ENABLE(cmirrord,
|
||||
AS_HELP_STRING([--enable-cmirrord],
|
||||
[enable the cluster mirror log daemon]),
|
||||
CMIRRORD=$enableval, CMIRRORD=no)
|
||||
AC_MSG_RESULT($CMIRRORD)
|
||||
|
||||
BUILD_CMIRRORD=$CMIRRORD
|
||||
|
||||
################################################################################
|
||||
dnl -- cmirrord pidfile
|
||||
if test "$BUILD_CMIRRORD" = yes; then
|
||||
AC_ARG_WITH(cmirrord-pidfile,
|
||||
AS_HELP_STRING([--with-cmirrord-pidfile=PATH],
|
||||
[cmirrord pidfile [PID_DIR/cmirrord.pid]]),
|
||||
CMIRRORD_PIDFILE=$withval,
|
||||
CMIRRORD_PIDFILE="$DEFAULT_PID_DIR/cmirrord.pid")
|
||||
AC_DEFINE_UNQUOTED(CMIRRORD_PIDFILE, ["$CMIRRORD_PIDFILE"],
|
||||
[Path to cmirrord pidfile.])
|
||||
fi
|
||||
|
||||
################################################################################
|
||||
dnl -- Look for corosync libraries if required.
|
||||
if [[ "$BUILD_CMIRRORD" = yes ]]; then
|
||||
pkg_config_init
|
||||
|
||||
if test "$HAVE_CPG" != yes; then
|
||||
PKG_CHECK_MODULES(CPG, libcpg)
|
||||
fi
|
||||
fi
|
||||
|
||||
################################################################################
|
||||
dnl -- Enable debugging
|
||||
AC_MSG_CHECKING(whether to enable debugging)
|
||||
@@ -1655,10 +1622,6 @@ fi
|
||||
AC_MSG_CHECKING(whether to enable editline)
|
||||
AC_MSG_RESULT($EDITLINE)
|
||||
|
||||
if test "$BUILD_CMIRRORD" = yes; then
|
||||
AC_CHECK_FUNCS(atexit,,hard_bailout)
|
||||
fi
|
||||
|
||||
if test "$BUILD_LVMLOCKD" = yes; then
|
||||
AS_IF([test "$HAVE_REALTIME" != yes], [AC_MSG_ERROR([Realtime clock support is mandatory for lvmlockd.])])
|
||||
AC_CHECK_FUNCS(strtoull,,hard_bailout)
|
||||
@@ -1834,7 +1797,6 @@ AC_ARG_VAR([UDEV_LIBS], [linker flags for udev])
|
||||
################################################################################
|
||||
AC_SUBST(AWK)
|
||||
AC_SUBST(BLKID_PC)
|
||||
AC_SUBST(BUILD_CMIRRORD)
|
||||
AC_SUBST(BUILD_DMEVENTD)
|
||||
AC_SUBST(BUILD_LVMDBUSD)
|
||||
AC_SUBST(BUILD_LVMPOLLD)
|
||||
@@ -1967,7 +1929,6 @@ AC_SUBST(WRITE_INSTALL)
|
||||
AC_SUBST(DMEVENTD_PIDFILE)
|
||||
AC_SUBST(LVMPOLLD_PIDFILE)
|
||||
AC_SUBST(LVMLOCKD_PIDFILE)
|
||||
AC_SUBST(CMIRRORD_PIDFILE)
|
||||
AC_SUBST(interface)
|
||||
AC_SUBST(kerneldir)
|
||||
AC_SUBST(missingkernel)
|
||||
@@ -1989,7 +1950,6 @@ Makefile
|
||||
make.tmpl
|
||||
libdm/make.tmpl
|
||||
daemons/Makefile
|
||||
daemons/cmirrord/Makefile
|
||||
daemons/dmeventd/Makefile
|
||||
daemons/dmeventd/libdevmapper-event.pc
|
||||
daemons/dmeventd/plugins/Makefile
|
||||
@@ -2023,14 +1983,15 @@ libdm/libdevmapper.pc
|
||||
man/Makefile
|
||||
po/Makefile
|
||||
scripts/lvm2-pvscan.service
|
||||
scripts/lvm-activate-vgs-main.service
|
||||
scripts/lvm-activate-vgs-last.service
|
||||
scripts/lvm-devices-wait.service
|
||||
scripts/blkdeactivate.sh
|
||||
scripts/blk_availability_init_red_hat
|
||||
scripts/blk_availability_systemd_red_hat.service
|
||||
scripts/cmirrord_init_red_hat
|
||||
scripts/com.redhat.lvmdbus1.service
|
||||
scripts/dm_event_systemd_red_hat.service
|
||||
scripts/dm_event_systemd_red_hat.socket
|
||||
scripts/lvm2_cmirrord_systemd_red_hat.service
|
||||
scripts/lvm2_lvmdbusd_systemd_red_hat.service
|
||||
scripts/lvm2_lvmpolld_init_red_hat
|
||||
scripts/lvm2_lvmpolld_systemd_red_hat.service
|
||||
|
||||
@@ -108,7 +108,7 @@ static SaVersionT version = { 'B', 1, 1 };
|
||||
#endif
|
||||
|
||||
#define DEBUGGING_HISTORY 100
|
||||
#define DEBUGGING_BUFLEN 270
|
||||
#define DEBUGGING_BUFLEN 128
|
||||
#define LOG_SPRINT(cc, f, arg...) do { \
|
||||
cc->idx++; \
|
||||
cc->idx = cc->idx % DEBUGGING_HISTORY; \
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
#define LOG_OFFSET 2
|
||||
|
||||
#define RESYNC_HISTORY 50
|
||||
#define RESYNC_BUFLEN 270
|
||||
#define RESYNC_BUFLEN 128
|
||||
//static char resync_history[RESYNC_HISTORY][128];
|
||||
//static int idx = 0;
|
||||
#define LOG_SPRINT(_lc, f, arg...) do { \
|
||||
|
||||
@@ -4777,7 +4777,7 @@ static void client_recv_action(struct client *cl)
|
||||
const char *path;
|
||||
const char *str;
|
||||
struct pvs pvs;
|
||||
char buf[18]; /* "path[%d]\0", %d outputs signed integer so max to 10 bytes */
|
||||
char buf[17]; /* "path[%d]\0", %d outputs signed integer so max to 10 bytes */
|
||||
int64_t val;
|
||||
uint32_t opts = 0;
|
||||
int result = 0;
|
||||
|
||||
@@ -1576,8 +1576,10 @@ 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,16 +173,6 @@ 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];
|
||||
@@ -220,25 +210,6 @@ 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,7 +616,8 @@ 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 >= 4 &&
|
||||
_dm_version_minor >= 15);
|
||||
}
|
||||
|
||||
static int _dm_inactive_supported(void)
|
||||
@@ -754,159 +755,6 @@ 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) +
|
||||
@@ -1593,7 +1441,8 @@ 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 < 4 ||
|
||||
_dm_version_minor < 17)
|
||||
/* can't check, assume uevent is generated */
|
||||
return 1;
|
||||
|
||||
@@ -1959,34 +1808,23 @@ 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];
|
||||
char buf_uuid[DM_UUID_LEN];
|
||||
struct dm_name_list *names;
|
||||
struct dm_names *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),
|
||||
mangling_mode);
|
||||
r = _do_dm_ioctl_unmangle_string(name, "name", buf, sizeof(buf),
|
||||
dm_get_name_mangling_mode());
|
||||
|
||||
if (type == DM_DEVICE_LIST &&
|
||||
((names = ((struct dm_name_list *) ((char *)dmi + dmi->data_start)))) &&
|
||||
((names = ((struct dm_names *) ((char *)dmi + dmi->data_start)))) &&
|
||||
names->dev) {
|
||||
do {
|
||||
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);
|
||||
}
|
||||
names = (struct dm_names *)((char *) names + next);
|
||||
r = _do_dm_ioctl_unmangle_string(names->name, "name",
|
||||
buf, sizeof(buf),
|
||||
dm_get_name_mangling_mode());
|
||||
next = names->next;
|
||||
} while (next);
|
||||
}
|
||||
|
||||
@@ -40,7 +40,6 @@ 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 cmd_context *cmd, struct device *dev, struct dev_usable_check_params check, int *is_lv)
|
||||
int device_is_usable(struct device *dev, struct dev_usable_check_params check, int *is_lv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -486,20 +486,12 @@ 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");
|
||||
|
||||
if (!_vsn[0] &&
|
||||
!dm_driver_version(_vsn, sizeof(_vsn)))
|
||||
return_0;
|
||||
|
||||
(void) dm_strncpy(version, _vsn, size);
|
||||
|
||||
return 1;
|
||||
return dm_driver_version(version, size);
|
||||
}
|
||||
|
||||
int target_version(const char *target_name, uint32_t *maj,
|
||||
@@ -624,15 +616,6 @@ 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.
|
||||
@@ -2391,7 +2374,6 @@ 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;
|
||||
@@ -2464,17 +2446,12 @@ 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,10 +106,6 @@ 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,
|
||||
@@ -258,7 +254,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 cmd_context *cmd, struct device *dev, struct dev_usable_check_params check, int *is_lv);
|
||||
int device_is_usable(struct device *dev, struct dev_usable_check_params check, int *is_lv);
|
||||
|
||||
/*
|
||||
* Declaration moved here from fs.h to keep header fs.h hidden
|
||||
|
||||
@@ -107,8 +107,6 @@ static struct dm_task *_setup_task_run(int task, struct dm_info *info,
|
||||
int with_flush,
|
||||
int query_inactive)
|
||||
{
|
||||
char vsn[80];
|
||||
unsigned maj, min;
|
||||
struct dm_task *dmt;
|
||||
|
||||
if (!(dmt = dm_task_create(task)))
|
||||
@@ -140,20 +138,8 @@ 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.");
|
||||
|
||||
switch (task) {
|
||||
case DM_DEVICE_TARGET_MSG:
|
||||
if (task == DM_DEVICE_TARGET_MSG)
|
||||
return dmt; /* TARGET_MSG needs more local tweaking before task_run() */
|
||||
case DM_DEVICE_LIST:
|
||||
/* Use 'newuuid' only with DM version that supports it */
|
||||
if (driver_version(vsn, sizeof(vsn)) &&
|
||||
(sscanf(vsn, "%u.%u", &maj, &min) == 2) &&
|
||||
(maj == 4 ? min >= 19 : maj > 4) &&
|
||||
!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;
|
||||
@@ -204,7 +190,7 @@ static int _get_segment_status_from_target_params(const char *target_name,
|
||||
/* If kernel's type isn't an exact match is it compatible? */
|
||||
(!segtype->ops->target_status_compatible ||
|
||||
!segtype->ops->target_status_compatible(target_name))) {
|
||||
log_warn("WARNING: Detected %s segment type does not match expected type %s for %s.",
|
||||
log_warn(INTERNAL_ERROR "WARNING: Segment type %s found does not match expected type %s for %s.",
|
||||
target_name, segtype->name, display_lvname(seg_status->seg->lv));
|
||||
return 0;
|
||||
}
|
||||
@@ -373,8 +359,7 @@ 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 cmd_context *cmd,
|
||||
struct device *dev,
|
||||
static int _ignore_blocked_mirror_devices(struct device *dev,
|
||||
uint64_t start, uint64_t length,
|
||||
char *mirror_status_str)
|
||||
{
|
||||
@@ -417,7 +402,7 @@ static int _ignore_blocked_mirror_devices(struct cmd_context *cmd,
|
||||
goto_out;
|
||||
|
||||
tmp_dev->dev = MKDEV(sm->logs[0].major, sm->logs[0].minor);
|
||||
if (device_is_usable(cmd, tmp_dev, (struct dev_usable_check_params)
|
||||
if (device_is_usable(tmp_dev, (struct dev_usable_check_params)
|
||||
{ .check_empty = 1,
|
||||
.check_blocked = 1,
|
||||
.check_suspended = ignore_suspended_devices(),
|
||||
@@ -621,60 +606,6 @@ 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
|
||||
@@ -691,14 +622,15 @@ static int _is_usable_uuid(const struct device *dev, const char *name, const cha
|
||||
*
|
||||
* Returns: 1 if usable, 0 otherwise
|
||||
*/
|
||||
int device_is_usable(struct cmd_context *cmd, struct device *dev, struct dev_usable_check_params check, int *is_lv)
|
||||
int device_is_usable(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;
|
||||
char *params, *vgname, *lvname, *layer;
|
||||
char vg_name[NAME_LEN];
|
||||
void *next = NULL;
|
||||
int only_error_or_zero_target = 1;
|
||||
int r = 0;
|
||||
@@ -723,9 +655,50 @@ int device_is_usable(struct cmd_context *cmd, struct device *dev, struct dev_usa
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (uuid &&
|
||||
!_is_usable_uuid(dev, name, uuid, check.check_reserved, check.check_lv, is_lv))
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/* FIXME Also check for mpath no paths */
|
||||
do {
|
||||
@@ -740,7 +713,7 @@ int device_is_usable(struct cmd_context *cmd, struct device *dev, struct dev_usa
|
||||
log_debug_activation("%s: Scanning mirror devices is disabled.", dev_name(dev));
|
||||
goto out;
|
||||
}
|
||||
if (!_ignore_blocked_mirror_devices(cmd, dev, start,
|
||||
if (!_ignore_blocked_mirror_devices(dev, start,
|
||||
length, params)) {
|
||||
log_debug_activation("%s: Mirror device %s not usable.",
|
||||
dev_name(dev), name);
|
||||
@@ -936,25 +909,6 @@ 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,
|
||||
@@ -970,16 +924,6 @@ 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)))
|
||||
@@ -2254,7 +2198,6 @@ static int _add_dev_to_dtree(struct dev_manager *dm, struct dm_tree *dtree,
|
||||
{
|
||||
char *dlid, *name;
|
||||
struct dm_info info, info2;
|
||||
const struct dm_active_device *dev;
|
||||
|
||||
if (!(name = dm_build_dm_name(dm->mem, lv->vg->name, lv->name, layer)))
|
||||
return_0;
|
||||
@@ -2262,21 +2205,9 @@ 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) {
|
||||
if (!dm_device_list_find_by_uuid(dm->cmd->cache_dm_devs, dlid, &dev)) {
|
||||
log_debug("Cached as not present %s.", name);
|
||||
return 1;
|
||||
}
|
||||
info = (struct dm_info) {
|
||||
.exists = 1,
|
||||
.major = dev->major,
|
||||
.minor = dev->minor,
|
||||
};
|
||||
log_debug("Cached as present %s %s (%d:%d).",
|
||||
name, dlid, info.major, info.minor);
|
||||
} else if (!_info(dm->cmd, name, dlid, 0, 0, 0, &info, NULL, NULL))
|
||||
if (!_info(dm->cmd, name, dlid, 1, 0, 0, &info, NULL, NULL))
|
||||
return_0;
|
||||
|
||||
/*
|
||||
* For top level volumes verify that existing device match
|
||||
* requested major/minor and that major/minor pair is available for use
|
||||
@@ -2419,9 +2350,6 @@ 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) {
|
||||
@@ -2947,10 +2875,6 @@ int add_areas_line(struct dev_manager *dm, struct lv_segment *seg,
|
||||
|
||||
/* FIXME Avoid repeating identical stat in dm_tree_node_add_target_area */
|
||||
for (s = start_area; s < areas; s++) {
|
||||
|
||||
/* FIXME: dev_name() does not return NULL! It needs to check if dm_list_empty(&dev->aliases)
|
||||
but this knot of logic is too complex to pull apart without careful deconstruction. */
|
||||
|
||||
if ((seg_type(seg, s) == AREA_PV &&
|
||||
(!seg_pvseg(seg, s) || !seg_pv(seg, s) || !seg_dev(seg, s) ||
|
||||
!(name = dev_name(seg_dev(seg, s))) || !*name ||
|
||||
@@ -2969,10 +2893,7 @@ int add_areas_line(struct dev_manager *dm, struct lv_segment *seg,
|
||||
return_0;
|
||||
num_error_areas++;
|
||||
} else if (seg_type(seg, s) == AREA_PV) {
|
||||
struct device *dev = seg_dev(seg, s);
|
||||
name = dm_list_empty(&dev->aliases) ? NULL : dev_name(dev);
|
||||
|
||||
if (!dm_tree_node_add_target_area(node, name, NULL,
|
||||
if (!dm_tree_node_add_target_area(node, dev_name(seg_dev(seg, s)), NULL,
|
||||
(seg_pv(seg, s)->pe_start + (extent_size * seg_pe(seg, s)))))
|
||||
return_0;
|
||||
num_existing_areas++;
|
||||
@@ -3816,7 +3737,6 @@ 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.",
|
||||
@@ -3836,17 +3756,9 @@ 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,7 +103,5 @@ 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
|
||||
|
||||
418
lib/cache/lvmcache.c
vendored
418
lib/cache/lvmcache.c
vendored
@@ -354,11 +354,9 @@ 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)) {
|
||||
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;
|
||||
}
|
||||
/* should never happen */
|
||||
log_error(INTERNAL_ERROR "vginfo_lookup vgid %s has two names %s %s",
|
||||
vgid, vginfo->vgname, vgname);
|
||||
return NULL;
|
||||
}
|
||||
return vginfo;
|
||||
@@ -574,16 +572,6 @@ 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.
|
||||
@@ -637,148 +625,6 @@ 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.
|
||||
@@ -834,8 +680,6 @@ 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;
|
||||
@@ -858,8 +702,6 @@ 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) {
|
||||
@@ -878,174 +720,31 @@ next:
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.)
|
||||
*/
|
||||
|
||||
info = lvmcache_info_from_pvid(pvid, NULL, 0);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/* 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 (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 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_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);
|
||||
|
||||
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;
|
||||
|
||||
|
||||
/*
|
||||
* Find the device for the pvid that's currently in lvmcache.
|
||||
*/
|
||||
@@ -1450,18 +1149,6 @@ 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)
|
||||
@@ -1523,8 +1210,7 @@ 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) &&
|
||||
(MAJOR(info->dev->dev) != cmd->dev_types->md_major))
|
||||
if (device_hint && !strncmp(device_hint, "/dev/md", 7))
|
||||
do_check_name = 1;
|
||||
|
||||
if (!do_check_size && !do_check_name)
|
||||
@@ -1554,11 +1240,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("Ignoring PV from md component %s with PVID %s (metadata %s %llu)",
|
||||
dev_name(dev), dev->pvid, device_hint ?: "none", (unsigned long long)pvsize);
|
||||
log_debug("dropping PV from md component %s", dev_name(dev));
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1886,17 +1572,7 @@ static int _lvmcache_update_vgname(struct cmd_context *cmd,
|
||||
|
||||
_drop_vginfo(info, info->vginfo);
|
||||
|
||||
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) {
|
||||
if (!(vginfo = lvmcache_vginfo_from_vgid(vgid))) {
|
||||
/*
|
||||
* Create a vginfo struct for this VG and put the vginfo
|
||||
* into the hash table.
|
||||
@@ -2251,6 +1927,50 @@ 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,6 +84,7 @@ 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);
|
||||
@@ -228,6 +229,4 @@ 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
|
||||
|
||||
@@ -1143,6 +1143,19 @@ static struct dev_filter *_init_filter_chain(struct cmd_context *cmd)
|
||||
* Update MAX_FILTERS definition above when adding new filters.
|
||||
*/
|
||||
|
||||
/*
|
||||
* sysfs filter. Only available on 2.6 kernels. Non-critical.
|
||||
* Listed first because it's very efficient at eliminating
|
||||
* unavailable devices.
|
||||
*
|
||||
* TODO: I suspect that using the lvm_type and device_id
|
||||
* filters before this one may be more efficient.
|
||||
*/
|
||||
if (find_config_tree_bool(cmd, devices_sysfs_scan_CFG, NULL)) {
|
||||
if ((filters[nr_filt] = sysfs_filter_create()))
|
||||
nr_filt++;
|
||||
}
|
||||
|
||||
/* internal filter used by command processing. */
|
||||
if (!(filters[nr_filt] = internal_filter_create())) {
|
||||
log_error("Failed to create internal device filter");
|
||||
@@ -1182,17 +1195,6 @@ static struct dev_filter *_init_filter_chain(struct cmd_context *cmd)
|
||||
}
|
||||
nr_filt++;
|
||||
|
||||
/*
|
||||
* sysfs filter. Only available on 2.6 kernels. Non-critical.
|
||||
* Eliminates unavailable devices.
|
||||
* TODO: this may be unnecessary now with device ids
|
||||
* (currently not used for devs match to device id using syfs)
|
||||
*/
|
||||
if (find_config_tree_bool(cmd, devices_sysfs_scan_CFG, NULL)) {
|
||||
if ((filters[nr_filt] = sysfs_filter_create()))
|
||||
nr_filt++;
|
||||
}
|
||||
|
||||
/* usable device filter. Required. */
|
||||
if (!(filters[nr_filt] = usable_filter_create(cmd, cmd->dev_types, FILTER_MODE_NO_LVMETAD))) {
|
||||
log_error("Failed to create usabled device filter");
|
||||
@@ -1603,6 +1605,7 @@ struct cmd_context *create_config_context(void)
|
||||
|
||||
dm_list_init(&cmd->config_files);
|
||||
dm_list_init(&cmd->tags);
|
||||
dm_list_init(&cmd->hints);
|
||||
|
||||
if (!_init_lvm_conf(cmd))
|
||||
goto_out;
|
||||
@@ -1667,6 +1670,7 @@ struct cmd_context *create_toolcontext(unsigned is_clvmd,
|
||||
dm_list_init(&cmd->formats);
|
||||
dm_list_init(&cmd->segtypes);
|
||||
dm_list_init(&cmd->tags);
|
||||
dm_list_init(&cmd->hints);
|
||||
dm_list_init(&cmd->config_files);
|
||||
label_init();
|
||||
|
||||
@@ -2041,6 +2045,8 @@ 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);
|
||||
@@ -2048,11 +2054,16 @@ 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);
|
||||
|
||||
dm_device_list_destroy(&cmd->cache_dm_devs);
|
||||
if (cmd->libmem)
|
||||
dm_pool_destroy(cmd->libmem);
|
||||
|
||||
if (cmd->pending_delete_mem)
|
||||
dm_pool_destroy(cmd->pending_delete_mem);
|
||||
#ifndef VALGRIND_POOL
|
||||
if (cmd->linebuffer) {
|
||||
/* Reset stream buffering to defaults */
|
||||
@@ -2077,7 +2088,7 @@ void destroy_toolcontext(struct cmd_context *cmd)
|
||||
free(cmd->linebuffer);
|
||||
}
|
||||
#endif
|
||||
destroy_config_context(cmd);
|
||||
free(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 expect_missing_vg_device:1; /* when reading a vg it's expected that a dev for a pv isn't found */
|
||||
unsigned pvscan_cache_single:1;
|
||||
unsigned can_use_one_scan:1;
|
||||
unsigned is_clvmd:1;
|
||||
unsigned md_component_detection:1;
|
||||
@@ -183,6 +183,7 @@ struct cmd_context {
|
||||
unsigned enable_hints:1; /* hints are enabled for cmds in general */
|
||||
unsigned use_hints:1; /* if hints are enabled this cmd can use them */
|
||||
unsigned pvscan_recreate_hints:1; /* enable special case hint handling for pvscan --cache */
|
||||
unsigned hints_pvs_online:1; /* hints="pvs_online" */
|
||||
unsigned scan_lvs:1;
|
||||
unsigned wipe_outdated_pvs:1;
|
||||
unsigned enable_devices_list:1; /* command is using --devices option */
|
||||
@@ -201,22 +202,18 @@ 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.
|
||||
*/
|
||||
struct dev_filter *filter;
|
||||
struct dm_list hints;
|
||||
struct dm_list use_devices; /* struct dev_use for each entry in devices file */
|
||||
const char *md_component_checks;
|
||||
const char *search_for_devnames; /* config file setting */
|
||||
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.
|
||||
*/
|
||||
|
||||
@@ -522,9 +522,7 @@ int config_file_read_fd(struct dm_config_tree *cft, struct device *dev, dev_io_r
|
||||
if (!(dev->flags & DEV_REGULAR) || size2)
|
||||
use_plain_read = 0;
|
||||
|
||||
/* Ensure there is extra '\0' after end of buffer since we pass
|
||||
* buffer to funtions like strtoll() */
|
||||
if (!(buf = zalloc(size + size2 + 1))) {
|
||||
if (!(buf = zalloc(size + size2))) {
|
||||
log_error("Failed to allocate circular buffer.");
|
||||
return 0;
|
||||
}
|
||||
@@ -1169,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(500))) {
|
||||
if (!(handle->cmd->cft_def_hash = dm_hash_create(60))) {
|
||||
log_error("Failed to create configuration definition hash.");
|
||||
r = 0; goto out;
|
||||
}
|
||||
|
||||
@@ -1119,11 +1119,12 @@ 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,
|
||||
"Disable event based autoactivation commands.\n"
|
||||
"WARNING: setting this to zero may cause machine startup to fail.\n"
|
||||
"Previously, setting this to zero would enable static autoactivation\n"
|
||||
"services (via the lvm2-activation-generator), but the autoactivation\n"
|
||||
"services and generator have been removed.\n")
|
||||
"Activate LVs based on system-generated device events.\n"
|
||||
"When a PV appears on the system, a system-generated uevent triggers\n"
|
||||
"the pvscan command, and autoactivation when all PVs for a VG are online.\n"
|
||||
"Also see auto_activation_settings.\n"
|
||||
"See the --setautoactivation option or the auto_activation_volume_list\n"
|
||||
"setting to configure autoactivation for specific VGs or LVs.\n")
|
||||
|
||||
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)
|
||||
@@ -1404,6 +1405,21 @@ cfg_array(activation_volume_list_CFG, "volume_list", activation_CFG_SECTION, CFG
|
||||
"volume_list = [ \"vg1\", \"vg2/lvol1\", \"@tag1\", \"@*\" ]\n"
|
||||
"#\n")
|
||||
|
||||
cfg_array(activation_auto_activation_settings_CFG, "auto_activation_settings", global_CFG_SECTION, CFG_ALLOW_EMPTY | CFG_DEFAULT_COMMENTED, CFG_TYPE_STRING, DEFAULT_AUTOACTIVATION_SETTINGS, vsn(2, 3, 14), NULL, 0, NULL,
|
||||
"Configure autoactivation behavior.\n"
|
||||
"service_and_event: use fixed activation services, then switch to event activation\n."
|
||||
"(Only used when event_activation=1.)\n"
|
||||
"event_only: use only event activation\n"
|
||||
"(Only used when event_activation=1.)\n"
|
||||
"service_only: use only fixed activation services\n"
|
||||
"(Effectively the equivalent of event_activation=0.)\n"
|
||||
"pvscan_hints: autoactivation commands will use PVs that\n"
|
||||
"that have been processed by pvscan (from udev rule.)\n"
|
||||
"Without pvscan_hints, pvscan only be used when it is needed\n"
|
||||
"to perform event activation.\n"
|
||||
"Autoactivation commands should set --autoactivation service|event\n"
|
||||
"to indicate if they are performing service or event activation.\n")
|
||||
|
||||
cfg_array(activation_auto_activation_volume_list_CFG, "auto_activation_volume_list", activation_CFG_SECTION, CFG_ALLOW_EMPTY | CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, NULL, vsn(2, 2, 97), NULL, 0, NULL,
|
||||
"A list of VGs or LVs that should be autoactivated.\n"
|
||||
"Autoactivation is an activation command run with -aay,\n"
|
||||
|
||||
@@ -332,4 +332,8 @@
|
||||
#define VGS_ONLINE_DIR DEFAULT_RUN_DIR "/vgs_online"
|
||||
#define PVS_LOOKUP_DIR DEFAULT_RUN_DIR "/pvs_lookup"
|
||||
|
||||
#define DEFAULT_AUTOACTIVATION_SETTING1 "service_and_event"
|
||||
#define DEFAULT_AUTOACTIVATION_SETTING2 "pvscan_hints"
|
||||
#define DEFAULT_AUTOACTIVATION_SETTINGS "#S" DEFAULT_AUTOACTIVATION_SETTING1 "#S" DEFAULT_AUTOACTIVATION_SETTING2
|
||||
|
||||
#endif /* _LVM_DEFAULTS_H */
|
||||
|
||||
@@ -351,7 +351,7 @@ static int _add_alias(struct device *dev, const char *path, enum add_hash hash)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!(path = _strdup(path)) ||
|
||||
if (!(path = dm_pool_strdup(_cache.mem, path)) ||
|
||||
!(sl = _zalloc(sizeof(*sl)))) {
|
||||
log_error("Failed to add allias to dev cache.");
|
||||
return 0;
|
||||
@@ -1162,17 +1162,6 @@ static int _insert(const char *path, const struct stat *info,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void _drop_all_aliases(struct device *dev)
|
||||
{
|
||||
struct dm_str_list *strl, *strl2;
|
||||
|
||||
dm_list_iterate_items_safe(strl, strl2, &dev->aliases) {
|
||||
log_debug("Drop alias for %d:%d %s.", (int)MAJOR(dev->dev), (int)MINOR(dev->dev), strl->str);
|
||||
dm_hash_remove(_cache.names, strl->str);
|
||||
dm_list_del(&strl->list);
|
||||
}
|
||||
}
|
||||
|
||||
void dev_cache_scan(struct cmd_context *cmd)
|
||||
{
|
||||
log_debug_devs("Creating list of system devices.");
|
||||
@@ -1257,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(1020)) ||
|
||||
if (!(_cache.names = dm_hash_create(120)) ||
|
||||
!(_cache.vgid_index = dm_hash_create(30)) ||
|
||||
!(_cache.lvid_index = dm_hash_create(29))) {
|
||||
dm_pool_destroy(_cache.mem);
|
||||
@@ -1382,6 +1371,59 @@ int dev_cache_add_dir(const char *path)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Check cached device name is still valid before returning it */
|
||||
/* This should be a rare occurrence */
|
||||
/* set quiet if the cache is expected to be out-of-date */
|
||||
/* FIXME Make rest of code pass/cache struct device instead of dev_name */
|
||||
const char *dev_name_confirmed(struct device *dev, int quiet)
|
||||
{
|
||||
struct stat buf;
|
||||
const char *name;
|
||||
int r;
|
||||
|
||||
if ((dev->flags & DEV_REGULAR))
|
||||
return dev_name(dev);
|
||||
|
||||
while ((r = stat(name = dm_list_item(dev->aliases.n,
|
||||
struct dm_str_list)->str, &buf)) ||
|
||||
(buf.st_rdev != dev->dev)) {
|
||||
if (r < 0) {
|
||||
if (quiet)
|
||||
log_sys_debug("stat", name);
|
||||
else
|
||||
log_sys_error("stat", name);
|
||||
}
|
||||
if (quiet)
|
||||
log_debug_devs("Path %s no longer valid for device(%d,%d)",
|
||||
name, (int) MAJOR(dev->dev),
|
||||
(int) MINOR(dev->dev));
|
||||
else
|
||||
log_warn("Path %s no longer valid for device(%d,%d)",
|
||||
name, (int) MAJOR(dev->dev),
|
||||
(int) MINOR(dev->dev));
|
||||
|
||||
/* Remove the incorrect hash entry */
|
||||
dm_hash_remove(_cache.names, name);
|
||||
|
||||
/* Leave list alone if there isn't an alternative name */
|
||||
/* so dev_name will always find something to return. */
|
||||
/* Otherwise add the name to the correct device. */
|
||||
if (dm_list_size(&dev->aliases) > 1) {
|
||||
dm_list_del(dev->aliases.n);
|
||||
if (!r)
|
||||
_insert(name, &buf, 0, obtain_device_list_from_udev());
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Scanning issues this inappropriately sometimes. */
|
||||
log_debug_devs("Aborting - please provide new pathname for what "
|
||||
"used to be %s", name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return dev_name(dev);
|
||||
}
|
||||
|
||||
struct device *dev_hash_get(const char *name)
|
||||
{
|
||||
return (struct device *) dm_hash_lookup(_cache.names, name);
|
||||
@@ -1410,23 +1452,26 @@ static void _remove_alias(struct device *dev, const char *name)
|
||||
* deactivated LV. Those old paths are all invalid and are dropped here.
|
||||
*/
|
||||
|
||||
static void _verify_aliases(struct device *dev)
|
||||
static void _verify_aliases(struct device *dev, const char *newname)
|
||||
{
|
||||
struct dm_str_list *strl, *strl2;
|
||||
struct stat st;
|
||||
|
||||
dm_list_iterate_items_safe(strl, strl2, &dev->aliases) {
|
||||
/* newname was just stat'd and added by caller */
|
||||
if (newname && !strcmp(strl->str, newname))
|
||||
continue;
|
||||
|
||||
if (stat(strl->str, &st) || (st.st_rdev != dev->dev)) {
|
||||
log_debug("Drop alias for %d:%d invalid path %s %d:%d.",
|
||||
(int)MAJOR(dev->dev), (int)MINOR(dev->dev), strl->str,
|
||||
(int)MAJOR(st.st_rdev), (int)MINOR(st.st_rdev));
|
||||
log_debug("Drop invalid path %s for %d:%d (new path %s).",
|
||||
strl->str, (int)MAJOR(dev->dev), (int)MINOR(dev->dev), newname ?: "");
|
||||
dm_hash_remove(_cache.names, strl->str);
|
||||
dm_list_del(&strl->list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct device *_dev_cache_get(struct cmd_context *cmd, const char *name, struct dev_filter *f, int existing)
|
||||
struct device *dev_cache_get(struct cmd_context *cmd, const char *name, struct dev_filter *f)
|
||||
{
|
||||
struct device *dev = (struct device *) dm_hash_lookup(_cache.names, name);
|
||||
struct stat st;
|
||||
@@ -1440,18 +1485,13 @@ static struct device *_dev_cache_get(struct cmd_context *cmd, const char *name,
|
||||
if (dev && (dev->flags & DEV_REGULAR))
|
||||
return dev;
|
||||
|
||||
if (dev && dm_list_empty(&dev->aliases)) {
|
||||
/* shouldn't happen */
|
||||
log_warn("Ignoring dev with no valid paths for %s.", name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* The requested path is invalid, remove any dev-cache info for it.
|
||||
* The requested path is invalid, remove any dev-cache
|
||||
* info for it.
|
||||
*/
|
||||
if (stat(name, &st)) {
|
||||
if (dev) {
|
||||
log_debug("Device path %s is invalid for %d:%d %s.",
|
||||
log_print("Device path %s is invalid for %d:%d %s.",
|
||||
name, (int)MAJOR(dev->dev), (int)MINOR(dev->dev), dev_name(dev));
|
||||
|
||||
dm_hash_remove(_cache.names, name);
|
||||
@@ -1459,17 +1499,11 @@ static struct device *_dev_cache_get(struct cmd_context *cmd, const char *name,
|
||||
_remove_alias(dev, name);
|
||||
|
||||
/* Remove any other names in dev->aliases that are incorrect. */
|
||||
_verify_aliases(dev);
|
||||
_verify_aliases(dev, NULL);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (dev && dm_list_empty(&dev->aliases)) {
|
||||
/* shouldn't happen */
|
||||
log_warn("Ignoring dev with no valid paths for %s.", name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!S_ISBLK(st.st_mode)) {
|
||||
log_debug("Not a block device %s.", name);
|
||||
return NULL;
|
||||
@@ -1480,110 +1514,26 @@ static struct device *_dev_cache_get(struct cmd_context *cmd, const char *name,
|
||||
* Remove incorrect info and then add new dev-cache entry.
|
||||
*/
|
||||
if (dev && (st.st_rdev != dev->dev)) {
|
||||
struct device *dev_by_devt = (struct device *) btree_lookup(_cache.devices, (uint32_t) st.st_rdev);
|
||||
log_debug("Device path %s does not match %d:%d %s.",
|
||||
name, (int)MAJOR(dev->dev), (int)MINOR(dev->dev), dev_name(dev));
|
||||
|
||||
/*
|
||||
* lvm commands create this condition when they
|
||||
* activate/deactivate LVs combined with creating new LVs.
|
||||
* The command does not purge dev structs when deactivating
|
||||
* an LV (which it probably should do), but the better
|
||||
* approach would be not using dev-cache at all for LVs.
|
||||
*/
|
||||
dm_hash_remove(_cache.names, name);
|
||||
|
||||
log_debug("Dropping aliases for device entry %d:%d %s for new device %d:%d %s.",
|
||||
(int)MAJOR(dev->dev), (int)MINOR(dev->dev), dev_name(dev),
|
||||
(int)MAJOR(st.st_rdev), (int)MINOR(st.st_rdev), name);
|
||||
_remove_alias(dev, name);
|
||||
|
||||
_drop_all_aliases(dev);
|
||||
/* Remove any other names in dev->aliases that are incorrect. */
|
||||
_verify_aliases(dev, NULL);
|
||||
|
||||
if (dev_by_devt) {
|
||||
log_debug("Dropping aliases for device entry %d:%d %s for new device %d:%d %s.",
|
||||
(int)MAJOR(dev_by_devt->dev), (int)MINOR(dev_by_devt->dev), dev_name(dev_by_devt),
|
||||
(int)MAJOR(st.st_rdev), (int)MINOR(st.st_rdev), name);
|
||||
|
||||
_drop_all_aliases(dev_by_devt);
|
||||
}
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* I think only lvm's own dm devs should be added here, so use
|
||||
* a warning to look for any other unknown cases.
|
||||
*/
|
||||
if (MAJOR(st.st_rdev) != cmd->dev_types->device_mapper_major) {
|
||||
log_warn("WARNING: new device appeared %d:%d %s",
|
||||
(int)MAJOR(st.st_rdev), (int)(MINOR(st.st_rdev)), name);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!_insert_dev(name, st.st_rdev))
|
||||
return_NULL;
|
||||
|
||||
/* Get the struct dev that was just added. */
|
||||
dev = (struct device *) dm_hash_lookup(_cache.names, name);
|
||||
|
||||
if (!dev) {
|
||||
log_error("Failed to get device %s", name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
goto out;
|
||||
/* Add new dev-cache entry next. */
|
||||
dev = NULL;
|
||||
}
|
||||
|
||||
if (dev && dm_list_empty(&dev->aliases)) {
|
||||
/* shouldn't happen */
|
||||
log_warn("Ignoring dev with no valid paths for %s.", name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!dev && existing)
|
||||
return_NULL;
|
||||
|
||||
/*
|
||||
* This case should never be hit for a PV. It should only
|
||||
* happen when the command is opening a new LV it has created.
|
||||
* Add an arg to all callers indicating when the arg should be
|
||||
* new (for an LV) and not existing.
|
||||
* FIXME: fix this further by not using dev-cache struct devs
|
||||
* at all for new dm devs (LVs) that lvm uses. Make the
|
||||
* dev-cache contain only devs for PVs.
|
||||
* Places to fix that use a dev for LVs include:
|
||||
* . lv_resize opening lv to discard
|
||||
* . wipe_lv opening lv to zero it
|
||||
* . _extend_sanlock_lv opening lv to extend it
|
||||
* . _write_log_header opening lv to write header
|
||||
* Also, io to LVs should not go through bcache.
|
||||
* bcache should contain only labels and metadata
|
||||
* scanned from PVs.
|
||||
* Either add a new struct dev for st_rdev and name,
|
||||
* or add name as a new alias for an existing struct dev
|
||||
* for st_rdev.
|
||||
*/
|
||||
if (!dev) {
|
||||
/*
|
||||
* This case should only be used for new devices created by this
|
||||
* command (opening LVs it's created), so if a dev exists for the
|
||||
* dev_t referenced by the name, then drop all aliases for before
|
||||
* _insert_dev adds the new name. lvm commands actually hit this
|
||||
* fairly often when it uses some LV, deactivates the LV, then
|
||||
* creates some new LV which ends up with the same major:minor.
|
||||
* Without dropping the aliases, it's plausible that lvm commands
|
||||
* could end up using the wrong dm device.
|
||||
*/
|
||||
struct device *dev_by_devt = (struct device *) btree_lookup(_cache.devices, (uint32_t) st.st_rdev);
|
||||
if (dev_by_devt) {
|
||||
log_debug("Dropping aliases for %d:%d before adding new path %s.",
|
||||
(int)MAJOR(st.st_rdev), (int)(MINOR(st.st_rdev)), name);
|
||||
_drop_all_aliases(dev_by_devt);
|
||||
}
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* I think only lvm's own dm devs should be added here, so use
|
||||
* a warning to look for any other unknown cases.
|
||||
*/
|
||||
if (MAJOR(st.st_rdev) != cmd->dev_types->device_mapper_major) {
|
||||
log_warn("WARNING: new device appeared %d:%d %s",
|
||||
(int)MAJOR(st.st_rdev), (int)(MINOR(st.st_rdev)), name);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!_insert_dev(name, st.st_rdev))
|
||||
return_NULL;
|
||||
|
||||
@@ -1594,9 +1544,10 @@ static struct device *_dev_cache_get(struct cmd_context *cmd, const char *name,
|
||||
log_error("Failed to get device %s", name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
_verify_aliases(dev, name);
|
||||
}
|
||||
|
||||
out:
|
||||
/*
|
||||
* The caller passed a filter if they only want the dev if it
|
||||
* passes filters.
|
||||
@@ -1626,23 +1577,63 @@ static struct device *_dev_cache_get(struct cmd_context *cmd, const char *name,
|
||||
return dev;
|
||||
}
|
||||
|
||||
struct device *dev_cache_get_existing(struct cmd_context *cmd, const char *name, struct dev_filter *f)
|
||||
struct device *dev_cache_get_by_devt(struct cmd_context *cmd, dev_t dev, struct dev_filter *f, int *filtered)
|
||||
{
|
||||
return _dev_cache_get(cmd, name, f, 1);
|
||||
}
|
||||
char path[PATH_MAX];
|
||||
const char *sysfs_dir;
|
||||
struct stat info;
|
||||
struct device *d = (struct device *) btree_lookup(_cache.devices, (uint32_t) dev);
|
||||
int ret;
|
||||
|
||||
struct device *dev_cache_get(struct cmd_context *cmd, const char *name, struct dev_filter *f)
|
||||
{
|
||||
return _dev_cache_get(cmd, name, f, 0);
|
||||
}
|
||||
if (filtered)
|
||||
*filtered = 0;
|
||||
|
||||
struct device *dev_cache_get_by_devt(struct cmd_context *cmd, dev_t devt)
|
||||
{
|
||||
struct device *dev = (struct device *) btree_lookup(_cache.devices, (uint32_t) devt);
|
||||
if (!d) {
|
||||
sysfs_dir = dm_sysfs_dir();
|
||||
if (sysfs_dir && *sysfs_dir) {
|
||||
/* First check if dev is sysfs to avoid useless scan */
|
||||
if (dm_snprintf(path, sizeof(path), "%sdev/block/%d:%d",
|
||||
sysfs_dir, (int)MAJOR(dev), (int)MINOR(dev)) < 0) {
|
||||
log_error("dm_snprintf partition failed.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (lstat(path, &info)) {
|
||||
log_debug("No sysfs entry for %d:%d errno %d at %s.",
|
||||
(int)MAJOR(dev), (int)MINOR(dev), errno, path);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
log_debug_devs("Device num not found in dev_cache repeat dev_cache_scan for %d:%d",
|
||||
(int)MAJOR(dev), (int)MINOR(dev));
|
||||
dev_cache_scan(cmd);
|
||||
d = (struct device *) btree_lookup(_cache.devices, (uint32_t) dev);
|
||||
|
||||
if (!d)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (d->flags & DEV_REGULAR)
|
||||
return d;
|
||||
|
||||
if (!f)
|
||||
return d;
|
||||
|
||||
ret = f->passes_filter(cmd, f, d, NULL);
|
||||
|
||||
if (ret == -EAGAIN) {
|
||||
log_debug_devs("get device by number defer filter %s", dev_name(d));
|
||||
d->flags |= DEV_FILTER_AFTER_SCAN;
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
return d;
|
||||
|
||||
if (filtered)
|
||||
*filtered = 1;
|
||||
|
||||
if (dev)
|
||||
return dev;
|
||||
log_debug_devs("No devno %d:%d in dev cache.", (int)MAJOR(devt), (int)MINOR(devt));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -1712,10 +1703,8 @@ int dev_fd(struct device *dev)
|
||||
|
||||
const char *dev_name(const struct device *dev)
|
||||
{
|
||||
if (dev && dev->aliases.n && !dm_list_empty(&dev->aliases))
|
||||
return dm_list_item(dev->aliases.n, struct dm_str_list)->str;
|
||||
else
|
||||
return unknown_device_name();
|
||||
return (dev && dev->aliases.n) ? dm_list_item(dev->aliases.n, struct dm_str_list)->str :
|
||||
unknown_device_name();
|
||||
}
|
||||
|
||||
bool dev_cache_has_md_with_end_superblock(struct dev_types *dt)
|
||||
@@ -2067,12 +2056,12 @@ int setup_device(struct cmd_context *cmd, const char *devname)
|
||||
}
|
||||
|
||||
/*
|
||||
* autoactivation is specialized/optimized to look only at command args,
|
||||
* pvscan --cache 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.
|
||||
* added to dev-cache and matched with device_ids later in pvscan.
|
||||
*/
|
||||
|
||||
int setup_devices_for_online_autoactivation(struct cmd_context *cmd)
|
||||
int setup_devices_for_pvscan_cache(struct cmd_context *cmd)
|
||||
{
|
||||
if (cmd->enable_devices_list) {
|
||||
if (!_setup_devices_list(cmd))
|
||||
@@ -2113,7 +2102,7 @@ int setup_devices_for_online_autoactivation(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] = { 0 };
|
||||
char devname[PATH_MAX];
|
||||
char namebuf[NAME_LEN];
|
||||
char line[1024];
|
||||
int major = MAJOR(devno);
|
||||
@@ -2125,9 +2114,6 @@ 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
|
||||
@@ -2139,7 +2125,7 @@ static char *_get_devname_from_devno(struct cmd_context *cmd, dev_t devno)
|
||||
}
|
||||
|
||||
if (!(dir = opendir(path)))
|
||||
goto try_partition;
|
||||
return NULL;
|
||||
|
||||
while ((dirent = readdir(dir))) {
|
||||
if (dirent->d_name[0] == '.')
|
||||
@@ -2178,7 +2164,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 dm", devname, major, minor);
|
||||
log_debug("Found %s for %d:%d from sys", devname, major, minor);
|
||||
return _strdup(devname);
|
||||
}
|
||||
return NULL;
|
||||
@@ -2189,7 +2175,6 @@ 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;
|
||||
|
||||
@@ -2255,55 +2240,3 @@ 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;
|
||||
}
|
||||
|
||||
@@ -53,8 +53,8 @@ int dev_cache_has_scanned(void);
|
||||
|
||||
int dev_cache_add_dir(const char *path);
|
||||
struct device *dev_cache_get(struct cmd_context *cmd, const char *name, struct dev_filter *f);
|
||||
struct device *dev_cache_get_existing(struct cmd_context *cmd, const char *name, struct dev_filter *f);
|
||||
struct device *dev_cache_get_by_devt(struct cmd_context *cmd, dev_t devt);
|
||||
|
||||
struct device *dev_cache_get_by_devt(struct cmd_context *cmd, dev_t device, struct dev_filter *f, int *filtered);
|
||||
|
||||
struct device *dev_hash_get(const char *name);
|
||||
|
||||
@@ -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);
|
||||
|
||||
int setup_devices_for_online_autoactivation(struct cmd_context *cmd);
|
||||
/* Normal device setup functions are split up for pvscan optimization. */
|
||||
int setup_devices_for_pvscan_cache(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
|
||||
|
||||
@@ -58,9 +58,6 @@ static int _dev_get_size_file(struct device *dev, uint64_t *size)
|
||||
const char *name = dev_name(dev);
|
||||
struct stat info;
|
||||
|
||||
if (dm_list_empty(&dev->aliases))
|
||||
return_0;
|
||||
|
||||
if (dev->size_seqno == _dev_size_seqno) {
|
||||
log_very_verbose("%s: using cached size %" PRIu64 " sectors",
|
||||
name, dev->size);
|
||||
@@ -90,7 +87,7 @@ static int _dev_get_size_dev(struct device *dev, uint64_t *size)
|
||||
int do_close = 0;
|
||||
|
||||
if (dm_list_empty(&dev->aliases))
|
||||
return_0;
|
||||
return 0;
|
||||
|
||||
if (dev->size_seqno == _dev_size_seqno) {
|
||||
log_very_verbose("%s: using cached size %" PRIu64 " sectors",
|
||||
@@ -308,13 +305,6 @@ int dev_open_flags(struct device *dev, int flags, int direct, int quiet)
|
||||
if ((flags & O_EXCL))
|
||||
need_excl = 1;
|
||||
|
||||
if (dm_list_empty(&dev->aliases)) {
|
||||
/* shouldn't happen */
|
||||
log_print("Cannot open device %d:%d with no valid paths.", (int)MAJOR(dev->dev), (int)MINOR(dev->dev));
|
||||
return 0;
|
||||
}
|
||||
name = dev_name(dev);
|
||||
|
||||
if (dev->fd >= 0) {
|
||||
if (((dev->flags & DEV_OPENED_RW) || !need_rw) &&
|
||||
((dev->flags & DEV_OPENED_EXCL) || !need_excl)) {
|
||||
@@ -324,7 +314,7 @@ int dev_open_flags(struct device *dev, int flags, int direct, int quiet)
|
||||
|
||||
if (dev->open_count && !need_excl)
|
||||
log_debug_devs("%s: Already opened read-only. Upgrading "
|
||||
"to read-write.", name);
|
||||
"to read-write.", dev_name(dev));
|
||||
|
||||
/* dev_close_immediate will decrement this */
|
||||
dev->open_count++;
|
||||
@@ -337,7 +327,11 @@ int dev_open_flags(struct device *dev, int flags, int direct, int quiet)
|
||||
|
||||
if (critical_section())
|
||||
/* FIXME Make this log_error */
|
||||
log_verbose("dev_open(%s) called while suspended", name);
|
||||
log_verbose("dev_open(%s) called while suspended",
|
||||
dev_name(dev));
|
||||
|
||||
if (!(name = dev_name_confirmed(dev, quiet)))
|
||||
return_0;
|
||||
|
||||
#ifdef O_DIRECT_SUPPORT
|
||||
if (direct) {
|
||||
@@ -378,9 +372,9 @@ int dev_open_flags(struct device *dev, int flags, int direct, int quiet)
|
||||
}
|
||||
#endif
|
||||
if (quiet)
|
||||
log_debug("Failed to open device path %s (%d).", name, errno);
|
||||
log_sys_debug("open", name);
|
||||
else
|
||||
log_error("Failed to open device path %s (%d).", name, errno);
|
||||
log_sys_error("open", name);
|
||||
|
||||
dev->flags |= DEV_OPEN_FAILURE;
|
||||
return 0;
|
||||
@@ -421,12 +415,10 @@ int dev_open_flags(struct device *dev, int flags, int direct, int quiet)
|
||||
if ((flags & O_CREAT) && !(flags & O_TRUNC))
|
||||
dev->end = lseek(dev->fd, (off_t) 0, SEEK_END);
|
||||
|
||||
if (!quiet) {
|
||||
log_debug_devs("Opened %s %s%s%s", name,
|
||||
dev->flags & DEV_OPENED_RW ? "RW" : "RO",
|
||||
dev->flags & DEV_OPENED_EXCL ? " O_EXCL" : "",
|
||||
dev->flags & DEV_O_DIRECT ? " O_DIRECT" : "");
|
||||
}
|
||||
log_debug_devs("Opened %s %s%s%s", dev_name(dev),
|
||||
dev->flags & DEV_OPENED_RW ? "RW" : "RO",
|
||||
dev->flags & DEV_OPENED_EXCL ? " O_EXCL" : "",
|
||||
dev->flags & DEV_O_DIRECT ? " O_DIRECT" : "");
|
||||
|
||||
dev->flags &= ~DEV_OPEN_FAILURE;
|
||||
return 1;
|
||||
|
||||
@@ -482,74 +482,3 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -966,9 +966,6 @@ static int _wipe_known_signatures_with_blkid(struct device *dev, const char *nam
|
||||
|
||||
/* TODO: Should we check for valid dev - _dev_is_valid(dev)? */
|
||||
|
||||
if (dm_list_empty(&dev->aliases))
|
||||
goto_out;
|
||||
|
||||
if (!(probe = blkid_new_probe_from_filename(dev_name(dev)))) {
|
||||
log_error("Failed to create a new blkid probe for device %s.", dev_name(dev));
|
||||
goto out;
|
||||
|
||||
@@ -63,8 +63,6 @@ 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,7 +43,6 @@ 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"},
|
||||
|
||||
@@ -40,7 +40,6 @@
|
||||
#define DEV_IS_NVME 0x00040000 /* set if dev is nvme */
|
||||
#define DEV_MATCHED_USE_ID 0x00080000 /* matched an entry from cmd->use_devices */
|
||||
#define DEV_SCAN_FOUND_NOLABEL 0x00100000 /* label_scan read, passed filters, but no lvm label */
|
||||
#define DEV_SCAN_NOT_READ 0x00200000 /* label_scan not able to read dev */
|
||||
|
||||
/*
|
||||
* Support for external device info.
|
||||
@@ -204,6 +203,9 @@ struct device *dev_create_file(const char *filename, struct device *dev,
|
||||
struct dm_str_list *alias, int use_malloc);
|
||||
void dev_destroy_file(struct device *dev);
|
||||
|
||||
/* Return a valid device name from the alias list; NULL otherwise */
|
||||
const char *dev_name_confirmed(struct device *dev, int quiet);
|
||||
|
||||
int dev_mpath_init(const char *config_wwids_file);
|
||||
void dev_mpath_exit(void);
|
||||
|
||||
|
||||
@@ -74,8 +74,6 @@ 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)
|
||||
@@ -243,7 +241,7 @@ static int _dm_uuid_has_prefix(char *sysbuf, const char *prefix)
|
||||
}
|
||||
|
||||
/* the dm uuid uses the wwid of the underlying dev */
|
||||
int dev_has_mpath_uuid(struct cmd_context *cmd, struct device *dev, const char **idname_out)
|
||||
static int _dev_has_mpath_uuid(struct cmd_context *cmd, struct device *dev, const char **idname_out)
|
||||
{
|
||||
char sysbuf[PATH_MAX] = { 0 };
|
||||
const char *idname;
|
||||
@@ -304,7 +302,6 @@ 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));
|
||||
@@ -312,6 +309,10 @@ 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';
|
||||
@@ -347,18 +348,11 @@ const char *device_id_system_read(struct cmd_context *cmd, struct device *dev, u
|
||||
}
|
||||
|
||||
else if (idtype == DEV_ID_TYPE_DEVNAME) {
|
||||
if (dm_list_empty(&dev->aliases))
|
||||
goto_bad;
|
||||
if (!(idname = strdup(dev_name(dev))))
|
||||
goto_bad;
|
||||
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;
|
||||
|
||||
@@ -367,6 +361,7 @@ 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;
|
||||
}
|
||||
|
||||
@@ -378,40 +373,20 @@ 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) && id->idname)
|
||||
if (id->idtype != DEV_ID_TYPE_DEVNAME)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Use device_id_system_read() instead of read_sys_block() when
|
||||
* system_read ignores some values from sysfs.
|
||||
*/
|
||||
|
||||
if ((idname = device_id_system_read(cmd, dev, DEV_ID_TYPE_SYS_WWID))) {
|
||||
free((void*)idname);
|
||||
if (read_sys_block(cmd, dev, "device/wwid", sysbuf, sizeof(sysbuf)))
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((idname = device_id_system_read(cmd, dev, DEV_ID_TYPE_SYS_SERIAL))) {
|
||||
free((void*)idname);
|
||||
if (read_sys_block(cmd, dev, "wwid", sysbuf, sizeof(sysbuf)))
|
||||
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);
|
||||
if (read_sys_block(cmd, dev, "device/serial", sysbuf, sizeof(sysbuf)))
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((MAJOR(dev->dev) == cmd->dev_types->device_mapper_major)) {
|
||||
if (!read_sys_block(cmd, dev, "dm/uuid", sysbuf, sizeof(sysbuf)))
|
||||
@@ -429,6 +404,9 @@ 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;
|
||||
@@ -693,9 +671,6 @@ 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 */
|
||||
@@ -803,8 +778,9 @@ static void _device_ids_update_try(struct cmd_context *cmd)
|
||||
{
|
||||
int held = 0;
|
||||
|
||||
if (cmd->expect_missing_vg_device) {
|
||||
log_print("Devices file update skipped.");
|
||||
/* Defer updates to non-pvscan-cache commands. */
|
||||
if (cmd->pvscan_cache_single) {
|
||||
log_print("pvscan[%d] skip updating devices file.", getpid());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -911,7 +887,7 @@ struct dev_use *get_du_for_pvid(struct cmd_context *cmd, const char *pvid)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct dev_use *get_du_for_devname(struct cmd_context *cmd, const char *devname)
|
||||
static struct dev_use *_get_du_for_devname(struct cmd_context *cmd, const char *devname)
|
||||
{
|
||||
struct dev_use *du;
|
||||
|
||||
@@ -952,14 +928,6 @@ 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;
|
||||
|
||||
/* Ensure valid dev_name(dev) below. */
|
||||
if (dm_list_empty(&dev->aliases))
|
||||
return_0;
|
||||
|
||||
/*
|
||||
* When enable_devices_file=0 and pending_devices_file=1 we let
|
||||
@@ -978,6 +946,10 @@ 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.
|
||||
*
|
||||
@@ -1009,7 +981,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;
|
||||
}
|
||||
@@ -1093,9 +1065,6 @@ 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.
|
||||
*
|
||||
@@ -1107,57 +1076,23 @@ 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 entry %p dev %s",
|
||||
log_debug("device_id_add %s pvid %s matches du_dev %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 entry %p dev %s with same pvid %s",
|
||||
log_debug("device_id_add %s pvid %s matches du_pvid %p dev %s 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 entry %p dev %s with same device_id %d %s",
|
||||
log_debug("device_id_add %s pvid %s matches du_devid %p dev %s pvid %s",
|
||||
dev_name(dev), pvid, du_devid, du_devid->dev ? dev_name(du_devid->dev) : ".",
|
||||
du_devid->idtype, du_devid->idname);
|
||||
du_devid->pvid);
|
||||
if (du_devname)
|
||||
log_debug("device_id_add %s pvid %s matches entry %p dev %s with same devname %s",
|
||||
log_debug("device_id_add %s pvid %s matches du_devname %p dev %s pvid %s",
|
||||
dev_name(dev), pvid, du_devname, du_devname->dev ? dev_name(du_devname->dev) : ".",
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
du_devname->pvid);
|
||||
|
||||
/*
|
||||
* If one of the existing entries (du_dev, du_pvid, du_devid, du_devname)
|
||||
@@ -1170,6 +1105,29 @@ 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
|
||||
@@ -1189,6 +1147,11 @@ 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.");
|
||||
@@ -1196,6 +1159,21 @@ 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?
|
||||
@@ -1210,13 +1188,64 @@ 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) {
|
||||
|
||||
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 {
|
||||
/* 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);
|
||||
@@ -1331,10 +1360,6 @@ 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)
|
||||
@@ -1363,43 +1388,6 @@ 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.
|
||||
@@ -1412,74 +1400,43 @@ static int _match_du_to_dev(struct cmd_context *cmd, struct dev_use *du, struct
|
||||
const char *idname;
|
||||
int part;
|
||||
|
||||
/*
|
||||
* 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));
|
||||
*/
|
||||
if (!du->idname || !du->idtype)
|
||||
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))) {
|
||||
/*
|
||||
log_debug("Mismatch device_id %s %s to %s: wrong major",
|
||||
idtype_to_str(du->idtype), du->idname ?: ".", dev_name(dev));
|
||||
*/
|
||||
if (!_idtype_compatible_with_major_number(cmd, du->idtype, (int)MAJOR(dev->dev)))
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!dev_get_partition_number(dev, &part)) {
|
||||
/*
|
||||
log_debug("Mismatch device_id %s %s to %s: no partition",
|
||||
idtype_to_str(du->idtype), du->idname ?: ".", dev_name(dev));
|
||||
*/
|
||||
log_debug("compare %s failed to get dev partition", dev_name(dev));
|
||||
return 0;
|
||||
}
|
||||
if (part != du->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);
|
||||
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);
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
dm_list_iterate_items(id, &dev->ids) {
|
||||
if (id->idtype == du->idtype) {
|
||||
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)) {
|
||||
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("Mismatch device_id %s %s to %s: idname %s",
|
||||
idtype_to_str(du->idtype), du->idname ?: ".", dev_name(dev), id->idname ?: ":");
|
||||
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 : "");
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
@@ -1499,7 +1456,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("Mismatch device_id %s %s to %s: no idtype",
|
||||
log_debug("compare mis %s %s to %s no idtype",
|
||||
idtype_to_str(du->idtype), du->idname ?: ".", dev_name(dev));
|
||||
*/
|
||||
return 0;
|
||||
@@ -1524,8 +1481,9 @@ static int _match_du_to_dev(struct cmd_context *cmd, struct dev_use *du, struct
|
||||
}
|
||||
|
||||
/*
|
||||
log_debug("Mismatch device_id %s %s to %s: idname %s",
|
||||
idtype_to_str(du->idtype), du->idname ?: ".", dev_name(dev), idname);
|
||||
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 : "");
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
@@ -1535,7 +1493,7 @@ int device_ids_match_dev(struct cmd_context *cmd, struct device *dev)
|
||||
struct dev_use *du;
|
||||
|
||||
/* First check the du entry with matching devname since it's likely correct. */
|
||||
if ((du = get_du_for_devname(cmd, dev_name(dev)))) {
|
||||
if ((du = _get_du_for_devname(cmd, dev_name(dev)))) {
|
||||
if (_match_du_to_dev(cmd, du, dev))
|
||||
return 1;
|
||||
}
|
||||
@@ -1583,7 +1541,7 @@ void device_ids_match_device_list(struct cmd_context *cmd)
|
||||
dm_list_iterate_items(du, &cmd->use_devices) {
|
||||
if (du->dev)
|
||||
continue;
|
||||
if (!(du->dev = dev_cache_get_existing(cmd, du->devname, NULL))) {
|
||||
if (!(du->dev = dev_cache_get(cmd, du->devname, NULL))) {
|
||||
log_warn("Device not found for %s.", du->devname);
|
||||
} else {
|
||||
/* Should we set dev->id? Which idtype? Use --deviceidtype? */
|
||||
@@ -1631,7 +1589,7 @@ void device_ids_match(struct cmd_context *cmd)
|
||||
* the du/dev pairs in preparation for using the filters.
|
||||
*/
|
||||
if (du->devname &&
|
||||
(dev = dev_cache_get_existing(cmd, du->devname, NULL))) {
|
||||
(dev = dev_cache_get(cmd, du->devname, NULL))) {
|
||||
/* On successful match, du, dev, and id are linked. */
|
||||
if (_match_du_to_dev(cmd, du, dev))
|
||||
continue;
|
||||
@@ -1752,13 +1710,6 @@ void device_ids_validate(struct cmd_context *cmd, struct dm_list *scanned_devs,
|
||||
if (scanned_devs && !dev_in_device_list(dev, scanned_devs))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* The matched device could not be read so we do not have
|
||||
* the PVID from disk and cannot verify the devices file entry.
|
||||
*/
|
||||
if (dev->flags & DEV_SCAN_NOT_READ)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* du and dev may have been matched, but the dev could still
|
||||
* have been excluded by other filters during label scan.
|
||||
@@ -1841,16 +1792,6 @@ void device_ids_validate(struct cmd_context *cmd, struct dm_list *scanned_devs,
|
||||
if (scanned_devs && !dev_in_device_list(dev, scanned_devs))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* The matched device could not be read so we do not have
|
||||
* the PVID from disk and cannot verify the devices file entry.
|
||||
*/
|
||||
if (dev->flags & DEV_SCAN_NOT_READ)
|
||||
continue;
|
||||
|
||||
if (dm_list_empty(&dev->aliases))
|
||||
continue;
|
||||
|
||||
if (!cmd->filter->passes_filter(cmd, cmd->filter, dev, "persistent")) {
|
||||
log_warn("Devices file %s is excluded by filter: %s.",
|
||||
dev_name(dev), dev_filtered_reason(dev));
|
||||
@@ -1966,14 +1907,6 @@ 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? */
|
||||
|
||||
/*
|
||||
@@ -2042,19 +1975,12 @@ 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;
|
||||
|
||||
@@ -2079,11 +2005,6 @@ 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);
|
||||
@@ -2150,12 +2071,6 @@ 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++;
|
||||
@@ -2234,14 +2149,14 @@ void device_ids_find_renamed_devs(struct cmd_context *cmd, struct dm_list *dev_l
|
||||
dm_list_iterate_items(dil, &search_pvids) {
|
||||
char *dup_devname1, *dup_devname2, *dup_devname3;
|
||||
|
||||
if (!dil->dev || dm_list_empty(&dil->dev->aliases)) {
|
||||
if (!dil->dev) {
|
||||
not_found++;
|
||||
continue;
|
||||
}
|
||||
found++;
|
||||
|
||||
dev = dil->dev;
|
||||
devname = dev_name(dev);
|
||||
found++;
|
||||
|
||||
if (!(du = get_du_for_pvid(cmd, dil->pvid))) {
|
||||
/* shouldn't happen */
|
||||
@@ -2265,8 +2180,7 @@ void device_ids_find_renamed_devs(struct cmd_context *cmd, struct dm_list *dev_l
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!noupdate)
|
||||
log_warn("Devices file PVID %s updating IDNAME to %s.", dev->pvid, devname);
|
||||
log_warn("Devices file PVID %s updating IDNAME to %s.", dev->pvid, devname);
|
||||
|
||||
free(du->idname);
|
||||
free(du->devname);
|
||||
|
||||
@@ -41,7 +41,6 @@ void device_id_update_vg_uuid(struct cmd_context *cmd, struct volume_group *vg,
|
||||
|
||||
struct dev_use *get_du_for_dev(struct cmd_context *cmd, struct device *dev);
|
||||
struct dev_use *get_du_for_pvid(struct cmd_context *cmd, const char *pvid);
|
||||
struct dev_use *get_du_for_devname(struct cmd_context *cmd, const char *devname);
|
||||
|
||||
char *devices_file_version(void);
|
||||
int devices_file_exists(struct cmd_context *cmd);
|
||||
@@ -57,6 +56,4 @@ 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
|
||||
|
||||
@@ -1,507 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
* 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(511)))
|
||||
if (!(pf->devices = dm_hash_create(111)))
|
||||
return_0;
|
||||
|
||||
return 1;
|
||||
|
||||
@@ -15,41 +15,268 @@
|
||||
#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)
|
||||
#include <sys/sysmacros.h>
|
||||
#include <dirent.h>
|
||||
|
||||
static int _locate_sysfs_blocks(const char *sysfs_dir, char *path, size_t len,
|
||||
unsigned *sysfs_depth)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
const char *sysfs_dir;
|
||||
struct stat info;
|
||||
unsigned i;
|
||||
static const struct dir_class {
|
||||
const char path[32];
|
||||
int depth;
|
||||
} classes[] = {
|
||||
/*
|
||||
* unified classification directory for all kernel subsystems
|
||||
*
|
||||
* /sys/subsystem/block/devices
|
||||
* |-- sda -> ../../../devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda
|
||||
* |-- sda1 -> ../../../devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1
|
||||
* `-- sr0 -> ../../../devices/pci0000:00/0000:00:1f.2/host1/target1:0:0/1:0:0:0/block/sr0
|
||||
*
|
||||
*/
|
||||
{ "subsystem/block/devices", 0 },
|
||||
|
||||
if (!_sys_dev_block_found)
|
||||
return 1;
|
||||
/*
|
||||
* block subsystem as a class
|
||||
*
|
||||
* /sys/class/block
|
||||
* |-- sda -> ../../devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda
|
||||
* |-- sda1 -> ../../devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1
|
||||
* `-- sr0 -> ../../devices/pci0000:00/0000:00:1f.2/host1/target1:0:0/1:0:0:0/block/sr0
|
||||
*
|
||||
*/
|
||||
{ "class/block", 0 },
|
||||
|
||||
dev->filtered_flags &= ~DEV_FILTERED_SYSFS;
|
||||
/*
|
||||
* old block subsystem layout with nested directories
|
||||
*
|
||||
* /sys/block/
|
||||
* |-- sda
|
||||
* | |-- capability
|
||||
* | |-- dev
|
||||
* ...
|
||||
* | |-- sda1
|
||||
* | | |-- dev
|
||||
* ...
|
||||
* |
|
||||
* `-- sr0
|
||||
* |-- capability
|
||||
* |-- dev
|
||||
* ...
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Any kind of device id other than devname has been set
|
||||
* using sysfs so we know that sysfs info exists for dev.
|
||||
*/
|
||||
if (dev->id && dev->id->idtype && (dev->id->idtype != DEV_ID_TYPE_DEVNAME))
|
||||
return 1;
|
||||
{ "block", 1 }
|
||||
};
|
||||
|
||||
sysfs_dir = dm_sysfs_dir();
|
||||
if (sysfs_dir && *sysfs_dir) {
|
||||
if (dm_snprintf(path, sizeof(path), "%sdev/block/%d:%d",
|
||||
sysfs_dir, (int)MAJOR(dev->dev), (int)MINOR(dev->dev)) < 0) {
|
||||
log_debug("failed to create sysfs path");
|
||||
for (i = 0; i < DM_ARRAY_SIZE(classes); ++i)
|
||||
if ((dm_snprintf(path, len, "%s%s", sysfs_dir, classes[i].path) >= 0) &&
|
||||
(stat(path, &info) == 0)) {
|
||||
*sysfs_depth = classes[i].depth;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (lstat(path, &info)) {
|
||||
log_debug_devs("%s: Skipping (sysfs)", dev_name(dev));
|
||||
dev->filtered_flags |= DEV_FILTERED_SYSFS;
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------
|
||||
* We need to store a set of dev_t.
|
||||
*--------------------------------------------------------------*/
|
||||
struct entry {
|
||||
struct entry *next;
|
||||
dev_t dev;
|
||||
};
|
||||
|
||||
#define SET_BUCKETS 64
|
||||
struct dev_set {
|
||||
struct dm_pool *mem;
|
||||
const char *sys_block;
|
||||
unsigned sysfs_depth;
|
||||
int initialised;
|
||||
struct entry *slots[SET_BUCKETS];
|
||||
};
|
||||
|
||||
static struct dev_set *_dev_set_create(struct dm_pool *mem,
|
||||
const char *sys_block,
|
||||
unsigned sysfs_depth)
|
||||
{
|
||||
struct dev_set *ds;
|
||||
|
||||
if (!(ds = dm_pool_zalloc(mem, sizeof(*ds))))
|
||||
return NULL;
|
||||
|
||||
ds->mem = mem;
|
||||
if (!(ds->sys_block = dm_pool_strdup(mem, sys_block)))
|
||||
return NULL;
|
||||
|
||||
ds->sysfs_depth = sysfs_depth;
|
||||
ds->initialised = 0;
|
||||
|
||||
return ds;
|
||||
}
|
||||
|
||||
static unsigned _hash_dev(dev_t dev)
|
||||
{
|
||||
return (major(dev) ^ minor(dev)) & (SET_BUCKETS - 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Doesn't check that the set already contains dev.
|
||||
*/
|
||||
static int _set_insert(struct dev_set *ds, dev_t dev)
|
||||
{
|
||||
struct entry *e;
|
||||
unsigned h = _hash_dev(dev);
|
||||
|
||||
if (!(e = dm_pool_alloc(ds->mem, sizeof(*e))))
|
||||
return 0;
|
||||
|
||||
e->next = ds->slots[h];
|
||||
e->dev = dev;
|
||||
ds->slots[h] = e;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _set_lookup(struct dev_set *ds, dev_t dev)
|
||||
{
|
||||
unsigned h = _hash_dev(dev);
|
||||
struct entry *e;
|
||||
|
||||
for (e = ds->slots[h]; e; e = e->next)
|
||||
if (e->dev == dev)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------
|
||||
* filter methods
|
||||
*--------------------------------------------------------------*/
|
||||
static int _parse_dev(const char *file, FILE *fp, dev_t *result)
|
||||
{
|
||||
unsigned major, minor;
|
||||
char buffer[64];
|
||||
|
||||
if (!fgets(buffer, sizeof(buffer), fp)) {
|
||||
log_error("Empty sysfs device file: %s", file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (sscanf(buffer, "%u:%u", &major, &minor) != 2) {
|
||||
log_error("Incorrect format for sysfs device file: %s.", file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
*result = makedev(major, minor);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _read_dev(const char *file, dev_t *result)
|
||||
{
|
||||
int r;
|
||||
FILE *fp;
|
||||
|
||||
if (!(fp = fopen(file, "r"))) {
|
||||
log_sys_error("fopen", file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = _parse_dev(file, fp, result);
|
||||
|
||||
if (fclose(fp))
|
||||
log_sys_error("fclose", file);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* Recurse through sysfs directories, inserting any devs found.
|
||||
*/
|
||||
static int _read_devs(struct dev_set *ds, const char *dir, unsigned sysfs_depth)
|
||||
{
|
||||
struct dirent *d;
|
||||
DIR *dr;
|
||||
struct stat info;
|
||||
char path[PATH_MAX];
|
||||
char file[PATH_MAX];
|
||||
dev_t dev = { 0 };
|
||||
int r = 1;
|
||||
|
||||
if (!(dr = opendir(dir))) {
|
||||
log_sys_error("opendir", dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
while ((d = readdir(dr))) {
|
||||
if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
|
||||
continue;
|
||||
|
||||
if (dm_snprintf(path, sizeof(path), "%s/%s", dir,
|
||||
d->d_name) < 0) {
|
||||
log_warn("WARNING: sysfs path name too long: %s in %s.",
|
||||
d->d_name, dir);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* devices have a "dev" file */
|
||||
if (dm_snprintf(file, sizeof(file), "%s/dev", path) < 0) {
|
||||
log_warn("WARNING: sysfs path name too long: %s in %s.",
|
||||
d->d_name, dir);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!stat(file, &info)) {
|
||||
/* recurse if we found a device and expect subdirs */
|
||||
if (sysfs_depth)
|
||||
_read_devs(ds, path, sysfs_depth - 1);
|
||||
|
||||
/* add the device we have found */
|
||||
if (_read_dev(file, &dev))
|
||||
_set_insert(ds, dev);
|
||||
}
|
||||
}
|
||||
|
||||
if (closedir(dr))
|
||||
log_sys_debug("closedir", dir);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int _init_devs(struct dev_set *ds)
|
||||
{
|
||||
if (!_read_devs(ds, ds->sys_block, ds->sysfs_depth)) {
|
||||
ds->initialised = -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ds->initialised = 1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int _accept_p(struct cmd_context *cmd, struct dev_filter *f, struct device *dev, const char *use_filter_name)
|
||||
{
|
||||
struct dev_set *ds = (struct dev_set *) f->private;
|
||||
|
||||
dev->filtered_flags &= ~DEV_FILTERED_SYSFS;
|
||||
|
||||
if (!ds->initialised)
|
||||
_init_devs(ds);
|
||||
|
||||
/* Pass through if initialisation failed */
|
||||
if (ds->initialised != 1)
|
||||
return 1;
|
||||
|
||||
if (!_set_lookup(ds, dev->dev)) {
|
||||
log_debug_devs("%s: Skipping (sysfs)", dev_name(dev));
|
||||
dev->filtered_flags |= DEV_FILTERED_SYSFS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
@@ -57,34 +284,21 @@ static int _accept_p(struct cmd_context *cmd, struct dev_filter *f, struct devic
|
||||
|
||||
static void _destroy(struct dev_filter *f)
|
||||
{
|
||||
struct dev_set *ds = (struct dev_set *) f->private;
|
||||
|
||||
if (f->use_count)
|
||||
log_error(INTERNAL_ERROR "Destroying sysfs filter while in use %u times.", f->use_count);
|
||||
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;
|
||||
}
|
||||
}
|
||||
dm_pool_destroy(ds->mem);
|
||||
}
|
||||
|
||||
struct dev_filter *sysfs_filter_create(void)
|
||||
{
|
||||
const char *sysfs_dir = dm_sysfs_dir();
|
||||
char sys_block[PATH_MAX];
|
||||
unsigned sysfs_depth;
|
||||
struct dm_pool *mem;
|
||||
struct dev_set *ds;
|
||||
struct dev_filter *f;
|
||||
|
||||
if (!*sysfs_dir) {
|
||||
@@ -92,15 +306,26 @@ struct dev_filter *sysfs_filter_create(void)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* support old kernels that don't have this */
|
||||
_check_sys_dev_block();
|
||||
if (!_locate_sysfs_blocks(sysfs_dir, sys_block, sizeof(sys_block), &sysfs_depth))
|
||||
return NULL;
|
||||
|
||||
if (!(f = zalloc(sizeof(*f))))
|
||||
if (!(mem = dm_pool_create("sysfs", 256))) {
|
||||
log_error("sysfs pool creation failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(ds = _dev_set_create(mem, sys_block, sysfs_depth))) {
|
||||
log_error("sysfs dev_set creation failed");
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!(f = dm_pool_zalloc(mem, sizeof(*f))))
|
||||
goto_bad;
|
||||
|
||||
f->passes_filter = _accept_p;
|
||||
f->destroy = _destroy;
|
||||
f->use_count = 0;
|
||||
f->private = ds;
|
||||
f->name = "sysfs";
|
||||
|
||||
log_debug_devs("Sysfs filter initialised.");
|
||||
@@ -108,6 +333,7 @@ struct dev_filter *sysfs_filter_create(void)
|
||||
return f;
|
||||
|
||||
bad:
|
||||
dm_pool_destroy(mem);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ static int _passes_usable_filter(struct cmd_context *cmd, struct dev_filter *f,
|
||||
break;
|
||||
}
|
||||
|
||||
if (!(r = device_is_usable(cmd, dev, ucp, &is_lv))) {
|
||||
if (!(r = device_is_usable(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 pruning %s VG archive with more then %u MiB in %u files (check archiving is needed in lvm.conf).",
|
||||
log_print_unless_silent("Consider prunning %s VG archive with more then %u MiB in %u files (check archiving is needed in lvm.conf).",
|
||||
vgname, (unsigned)sum, archives_size);
|
||||
}
|
||||
|
||||
|
||||
@@ -2601,7 +2601,7 @@ struct format_type *create_text_format(struct cmd_context *cmd)
|
||||
fmt->ops = &_text_handler;
|
||||
fmt->name = FMT_TEXT_NAME;
|
||||
fmt->alias = FMT_TEXT_ALIAS;
|
||||
strncpy(fmt->orphan_vg_name, ORPHAN_VG_NAME(FMT_TEXT_NAME), sizeof(fmt->orphan_vg_name));
|
||||
fmt->orphan_vg_name = ORPHAN_VG_NAME(FMT_TEXT_NAME);
|
||||
fmt->features = FMT_SEGMENTS | FMT_TAGS | FMT_PRECOMMIT |
|
||||
FMT_UNLIMITED_VOLS | FMT_RESIZE_PV |
|
||||
FMT_UNLIMITED_STRIPESIZE | FMT_CONFIG_PROFILE |
|
||||
|
||||
@@ -410,7 +410,6 @@ static int _text_read(struct cmd_context *cmd, struct labeller *labeller, struct
|
||||
{
|
||||
struct lvmcache_vgsummary vgsummary;
|
||||
char pvid[ID_LEN + 1] __attribute__((aligned(8))) = { 0 };
|
||||
char vgid[ID_LEN + 1] __attribute__((aligned(8))) = { 0 };
|
||||
struct lvmcache_info *info;
|
||||
const struct format_type *fmt = labeller->fmt;
|
||||
struct label_header *lh = (struct label_header *) label_buf;
|
||||
@@ -434,7 +433,6 @@ static int _text_read(struct cmd_context *cmd, struct labeller *labeller, struct
|
||||
pvhdr = (struct pv_header *) ((char *) label_buf + xlate32(lh->offset_xl));
|
||||
|
||||
memcpy(pvid, &pvhdr->pv_uuid, ID_LEN);
|
||||
strncpy(vgid, FMT_TEXT_ORPHAN_VG_NAME, ID_LEN);
|
||||
|
||||
/*
|
||||
* FIXME: stop adding the device to lvmcache initially as an orphan
|
||||
@@ -451,8 +449,8 @@ static int _text_read(struct cmd_context *cmd, struct labeller *labeller, struct
|
||||
* Other reasons for lvmcache_add to return NULL are internal errors.
|
||||
*/
|
||||
if (!(info = lvmcache_add(cmd, labeller, pvid, dev, label_sector,
|
||||
vgid,
|
||||
vgid, 0, is_duplicate)))
|
||||
FMT_TEXT_ORPHAN_VG_NAME,
|
||||
FMT_TEXT_ORPHAN_VG_NAME, 0, is_duplicate)))
|
||||
return_0;
|
||||
|
||||
lvmcache_set_device_size(info, xlate64(pvhdr->device_size_xl));
|
||||
|
||||
@@ -146,7 +146,6 @@
|
||||
#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>
|
||||
@@ -157,6 +156,9 @@
|
||||
#include <sys/file.h>
|
||||
#include <sys/sysmacros.h>
|
||||
|
||||
/* FIXME: move online pv functions to pvs_online.c */
|
||||
int online_pvid_file_read(char *path, int *major, int *minor, char *vgname);
|
||||
|
||||
static const char *_hints_file = DEFAULT_RUN_DIR "/hints";
|
||||
static const char *_nohints_file = DEFAULT_RUN_DIR "/nohints";
|
||||
static const char *_newhints_file = DEFAULT_RUN_DIR "/newhints";
|
||||
@@ -236,7 +238,6 @@ static int _touch_newhints(void)
|
||||
return_0;
|
||||
if (fclose(fp))
|
||||
stack;
|
||||
log_debug("newhints created");
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -368,6 +369,7 @@ static void _unlock_hints(struct cmd_context *cmd)
|
||||
|
||||
void hints_exit(struct cmd_context *cmd)
|
||||
{
|
||||
free_hints(&cmd->hints);
|
||||
if (_hints_fd == -1)
|
||||
return;
|
||||
_unlock_hints(cmd);
|
||||
@@ -500,8 +502,6 @@ int validate_hints(struct cmd_context *cmd, struct dm_list *hints)
|
||||
if (!(iter = dev_iter_create(NULL, 0)))
|
||||
return 0;
|
||||
while ((dev = dev_iter_get(cmd, iter))) {
|
||||
if (dm_list_empty(&dev->aliases))
|
||||
continue;
|
||||
if (!(hint = _find_hint_name(hints, dev_name(dev))))
|
||||
continue;
|
||||
|
||||
@@ -509,19 +509,6 @@ int validate_hints(struct cmd_context *cmd, struct dm_list *hints)
|
||||
if (!hint->chosen)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* label_scan was unable to read the dev so we don't know its pvid.
|
||||
* Since we are unable to verify the hint is correct, it's possible
|
||||
* that the PVID is actually found on a different device, so don't
|
||||
* depend on hints. (This would also fail the following pvid check.)
|
||||
*/
|
||||
if (dev->flags & DEV_SCAN_NOT_READ) {
|
||||
log_debug("Uncertain hint for unread device %d:%d %s",
|
||||
major(hint->devt), minor(hint->devt), dev_name(dev));
|
||||
ret = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strcmp(dev->pvid, hint->pvid)) {
|
||||
log_debug("Invalid hint device %d:%d %s pvid %s had hint pvid %s",
|
||||
major(hint->devt), minor(hint->devt), dev_name(dev),
|
||||
@@ -1230,8 +1217,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.
|
||||
*/
|
||||
void get_single_vgname_cmd_arg(struct cmd_context *cmd,
|
||||
struct dm_list *hints, char **vgname)
|
||||
static void _get_single_vgname_cmd_arg(struct cmd_context *cmd,
|
||||
struct dm_list *hints, char **vgname)
|
||||
{
|
||||
struct hint *hint;
|
||||
char namebuf[NAME_LEN];
|
||||
@@ -1280,11 +1267,6 @@ 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
|
||||
@@ -1301,6 +1283,109 @@ check:
|
||||
free(name);
|
||||
}
|
||||
|
||||
static int _get_hints_from_pvs_online(struct cmd_context *cmd, struct dm_list *hints_out,
|
||||
struct dm_list *devs_in, struct dm_list *devs_out)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
char file_vgname[NAME_LEN];
|
||||
struct dm_list hints_list;
|
||||
struct hint file_hint;
|
||||
struct hint *alloc_hint;
|
||||
struct hint *hint, *hint2;
|
||||
struct device_list *devl, *devl2;
|
||||
int file_major, file_minor;
|
||||
int found = 0;
|
||||
DIR *dir;
|
||||
struct dirent *de;
|
||||
char *vgname = NULL;
|
||||
char *pvid;
|
||||
|
||||
dm_list_init(&hints_list);
|
||||
|
||||
if (!(dir = opendir(PVS_ONLINE_DIR)))
|
||||
return 0;
|
||||
|
||||
while ((de = readdir(dir))) {
|
||||
if (de->d_name[0] == '.')
|
||||
continue;
|
||||
|
||||
pvid = de->d_name;
|
||||
|
||||
if (strlen(pvid) != ID_LEN) /* 32 */
|
||||
continue;
|
||||
|
||||
memset(path, 0, sizeof(path));
|
||||
snprintf(path, sizeof(path), "%s/%s", PVS_ONLINE_DIR, pvid);
|
||||
|
||||
memset(&file_hint, 0, sizeof(file_hint));
|
||||
memset(file_vgname, 0, sizeof(file_vgname));
|
||||
file_major = 0;
|
||||
file_minor = 0;
|
||||
|
||||
if (!online_pvid_file_read(path, &file_major, &file_minor, file_vgname))
|
||||
continue;
|
||||
|
||||
if (!dm_strncpy(file_hint.pvid, pvid, sizeof(file_hint.pvid)))
|
||||
continue;
|
||||
|
||||
file_hint.devt = makedev(file_major, file_minor);
|
||||
|
||||
if (file_vgname[0] && validate_name(file_vgname)) {
|
||||
if (!dm_strncpy(file_hint.vgname, file_vgname, sizeof(file_hint.vgname)))
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(alloc_hint = malloc(sizeof(struct hint))))
|
||||
continue;
|
||||
|
||||
memcpy(alloc_hint, &file_hint, sizeof(struct hint));
|
||||
|
||||
log_debug("add hint %s %d:%d %s from pvs_online", file_hint.pvid, file_major, file_minor, file_vgname);
|
||||
dm_list_add(&hints_list, &alloc_hint->list);
|
||||
found++;
|
||||
}
|
||||
|
||||
if (closedir(dir))
|
||||
stack;
|
||||
|
||||
log_debug("accept hints found %d from pvs_online", found);
|
||||
|
||||
_get_single_vgname_cmd_arg(cmd, &hints_list, &vgname);
|
||||
|
||||
/*
|
||||
* apply_hints equivalent, move devs from devs_in to devs_out if
|
||||
* their devno matches the devno of a hint (and if the hint matches
|
||||
* the vgname when a vgname is present.)
|
||||
*/
|
||||
dm_list_iterate_items_safe(devl, devl2, devs_in) {
|
||||
dm_list_iterate_items_safe(hint, hint2, &hints_list) {
|
||||
if ((MAJOR(devl->dev->dev) == MAJOR(hint->devt)) &&
|
||||
(MINOR(devl->dev->dev) == MINOR(hint->devt))) {
|
||||
|
||||
if (vgname && hint->vgname[0] && strcmp(vgname, hint->vgname))
|
||||
goto next_dev;
|
||||
|
||||
snprintf(hint->name, sizeof(hint->name), "%s", dev_name(devl->dev));
|
||||
hint->chosen = 1;
|
||||
|
||||
dm_list_del(&devl->list);
|
||||
dm_list_add(devs_out, &devl->list);
|
||||
}
|
||||
}
|
||||
next_dev:
|
||||
;
|
||||
}
|
||||
|
||||
log_debug("applied hints using %d other %d vgname %s from pvs_online",
|
||||
dm_list_size(devs_out), dm_list_size(devs_in), vgname ?: "");
|
||||
|
||||
dm_list_splice(hints_out, &hints_list);
|
||||
|
||||
free(vgname);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns 0: no hints are used.
|
||||
* . newhints is set if this command should create new hints after scan
|
||||
@@ -1322,7 +1407,7 @@ int get_hints(struct cmd_context *cmd, struct dm_list *hints_out, int *newhints,
|
||||
*newhints = NEWHINTS_NONE;
|
||||
|
||||
/* No commands are using hints. */
|
||||
if (!cmd->enable_hints)
|
||||
if (!cmd->enable_hints && !cmd->hints_pvs_online)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
@@ -1342,6 +1427,19 @@ int get_hints(struct cmd_context *cmd, struct dm_list *hints_out, int *newhints,
|
||||
if (!cmd->use_hints)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* enable_hints is 0 for the special hints=pvs_online
|
||||
* and by lvm.conf hints="none" does not disable hints=pvs_online.
|
||||
* hints=pvs_online can be disabled with --nohints.
|
||||
*/
|
||||
if (cmd->hints_pvs_online) {
|
||||
if (!_get_hints_from_pvs_online(cmd, &hints_list, devs_in, devs_out)) {
|
||||
log_debug("get_hints: pvs_online failed");
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if another command created the nohints file to prevent us from
|
||||
* using hints.
|
||||
@@ -1413,16 +1511,6 @@ 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;
|
||||
|
||||
@@ -1456,7 +1544,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,8 +41,5 @@ 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,7 +26,6 @@
|
||||
#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>
|
||||
@@ -687,8 +686,6 @@ static int _scan_list(struct cmd_context *cmd, struct dev_filter *f,
|
||||
|
||||
dm_list_iterate_items_safe(devl, devl2, devs) {
|
||||
|
||||
devl->dev->flags &= ~DEV_SCAN_NOT_READ;
|
||||
|
||||
/*
|
||||
* If we prefetch more devs than blocks in the cache, then the
|
||||
* cache will wait for earlier reads to complete, toss the
|
||||
@@ -704,7 +701,6 @@ static int _scan_list(struct cmd_context *cmd, struct dev_filter *f,
|
||||
log_debug_devs("Scan failed to open %s.", dev_name(devl->dev));
|
||||
dm_list_del(&devl->list);
|
||||
dm_list_add(&reopen_devs, &devl->list);
|
||||
devl->dev->flags |= DEV_SCAN_NOT_READ;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -728,7 +724,6 @@ static int _scan_list(struct cmd_context *cmd, struct dev_filter *f,
|
||||
log_debug_devs("Scan failed to read %s.", dev_name(devl->dev));
|
||||
scan_read_errors++;
|
||||
scan_failed_count++;
|
||||
devl->dev->flags |= DEV_SCAN_NOT_READ;
|
||||
lvmcache_del_dev(devl->dev);
|
||||
if (bb)
|
||||
bcache_put(bb);
|
||||
@@ -896,7 +891,7 @@ static int _setup_bcache(void)
|
||||
|
||||
#define BASE_FD_COUNT 32 /* Number of open files we want apart from devs */
|
||||
|
||||
void prepare_open_file_limit(struct cmd_context *cmd, unsigned int num_devs)
|
||||
static void _prepare_open_file_limit(struct cmd_context *cmd, unsigned int num_devs)
|
||||
{
|
||||
#ifdef HAVE_PRLIMIT
|
||||
struct rlimit old = { 0 }, new;
|
||||
@@ -1025,282 +1020,6 @@ 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) {
|
||||
log_debug("Repeat dev cache scan to translate devnos.");
|
||||
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))) {
|
||||
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
|
||||
@@ -1394,10 +1113,6 @@ int label_scan(struct cmd_context *cmd)
|
||||
* filter", and this result needs to be cleared (wiped) so that the
|
||||
* complete set of filters (including those that require data) can be
|
||||
* checked in _process_block, where headers have been read.
|
||||
*
|
||||
* FIXME: devs that are filtered with data in _process_block
|
||||
* are not moved to the filtered_devs list like devs filtered
|
||||
* here without data. Does that have any effect?
|
||||
*/
|
||||
log_debug_devs("Filtering devices to scan (nodata)");
|
||||
|
||||
@@ -1453,7 +1168,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.
|
||||
@@ -1495,6 +1210,8 @@ int label_scan(struct cmd_context *cmd)
|
||||
(unsigned long long)want_size_kb);
|
||||
}
|
||||
|
||||
dm_list_init(&cmd->hints);
|
||||
|
||||
/*
|
||||
* If we're using hints to limit which devs we scanned, verify
|
||||
* that those hints were valid, and if not we need to scan the
|
||||
@@ -1506,16 +1223,18 @@ int label_scan(struct cmd_context *cmd)
|
||||
_scan_list(cmd, cmd->filter, &all_devs, 0, NULL);
|
||||
/* scan_devs are the devs that have been scanned */
|
||||
dm_list_splice(&scan_devs, &all_devs);
|
||||
free_hints(&hints_list);
|
||||
using_hints = 0;
|
||||
create_hints = 0;
|
||||
/* invalid hints means a new dev probably appeared and
|
||||
we should search for any missing pvids again. */
|
||||
unlink_searched_devnames(cmd);
|
||||
} else {
|
||||
/* The hints may be used by another device iteration. */
|
||||
dm_list_splice(&cmd->hints, &hints_list);
|
||||
}
|
||||
}
|
||||
|
||||
free_hints(&hints_list);
|
||||
|
||||
/*
|
||||
* Check if the devices_file content is up to date and
|
||||
* if not update it.
|
||||
@@ -1593,7 +1312,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 device */
|
||||
/* Not an lvm deice */
|
||||
label_scan_invalidate(dev);
|
||||
return 1;
|
||||
}
|
||||
@@ -1603,7 +1322,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 device */
|
||||
/* Not an lvm deice */
|
||||
label_scan_invalidate(dev);
|
||||
return 1;
|
||||
}
|
||||
@@ -1723,52 +1442,11 @@ void label_scan_invalidate_lv(struct cmd_context *cmd, struct logical_volume *lv
|
||||
if (lv_info(cmd, lv, 0, &lvinfo, 0, 0) && lvinfo.exists) {
|
||||
/* FIXME: Still unclear what is it supposed to find */
|
||||
devt = MKDEV(lvinfo.major, lvinfo.minor);
|
||||
if ((dev = dev_cache_get_by_devt(cmd, devt)))
|
||||
if ((dev = dev_cache_get_by_devt(cmd, devt, NULL, NULL)))
|
||||
label_scan_invalidate(dev);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/*
|
||||
* FIXME: this is all unnecessary unless there are PVs stacked on LVs,
|
||||
* so we can skip all of this if scan_lvs=0.
|
||||
*/
|
||||
log_debug("invalidating devs for any pvs on lvs");
|
||||
|
||||
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)))
|
||||
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.
|
||||
@@ -1886,24 +1564,10 @@ int label_scan_open_rw(struct device *dev)
|
||||
|
||||
int label_scan_reopen_rw(struct device *dev)
|
||||
{
|
||||
const char *name;
|
||||
int flags = 0;
|
||||
int prev_fd = dev->bcache_fd;
|
||||
int fd;
|
||||
|
||||
if (dm_list_empty(&dev->aliases)) {
|
||||
log_error("Cannot reopen rw device %d:%d with no valid paths di %d fd %d.",
|
||||
(int)MAJOR(dev->dev), (int)MINOR(dev->dev), dev->bcache_di, dev->bcache_fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
name = dev_name(dev);
|
||||
if (!name || name[0] != '/') {
|
||||
log_error("Cannot reopen rw device %d:%d with no valid name di %d fd %d.",
|
||||
(int)MAJOR(dev->dev), (int)MINOR(dev->dev), dev->bcache_di, dev->bcache_fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(dev->flags & DEV_IN_BCACHE)) {
|
||||
if ((dev->bcache_fd != -1) || (dev->bcache_di != -1)) {
|
||||
/* shouldn't happen */
|
||||
@@ -1933,7 +1597,7 @@ int label_scan_reopen_rw(struct device *dev)
|
||||
flags |= O_NOATIME;
|
||||
flags |= O_RDWR;
|
||||
|
||||
fd = open(name, flags, 0777);
|
||||
fd = open(dev_name(dev), flags, 0777);
|
||||
if (fd < 0) {
|
||||
log_error("Failed to open rw %s errno %d di %d fd %d.",
|
||||
dev_name(dev), errno, dev->bcache_di, dev->bcache_fd);
|
||||
|
||||
@@ -110,7 +110,6 @@ 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);
|
||||
@@ -119,9 +118,6 @@ 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);
|
||||
|
||||
@@ -138,6 +134,4 @@ 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,7 +330,6 @@ 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;
|
||||
|
||||
@@ -272,8 +272,6 @@ static void _lockd_retrive_vg_pv_list(struct volume_group *vg,
|
||||
|
||||
i = 0;
|
||||
dm_list_iterate_items(pvl, &vg->pvs) {
|
||||
if (!pvl->pv->dev || dm_list_empty(&pvl->pv->dev->aliases))
|
||||
continue;
|
||||
lock_pvs->path[i] = strdup(pv_dev_name(pvl->pv));
|
||||
if (!lock_pvs->path[i]) {
|
||||
log_error("Fail to allocate PV path for VG %s", vg->name);
|
||||
@@ -343,8 +341,6 @@ static void _lockd_retrive_lv_pv_list(struct volume_group *vg,
|
||||
|
||||
dm_list_iterate_items(pvl, &vg->pvs) {
|
||||
if (lv_is_on_pv(lv, pvl->pv)) {
|
||||
if (!pvl->pv->dev || dm_list_empty(&pvl->pv->dev->aliases))
|
||||
continue;
|
||||
lock_pvs->path[i] = strdup(pv_dev_name(pvl->pv));
|
||||
if (!lock_pvs->path[i]) {
|
||||
log_error("Fail to allocate PV path for LV %s/%s",
|
||||
|
||||
@@ -892,7 +892,6 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -371,7 +371,7 @@ struct format_type {
|
||||
struct labeller *labeller;
|
||||
const char *name;
|
||||
const char *alias;
|
||||
char orphan_vg_name[ID_LEN];
|
||||
const char *orphan_vg_name;
|
||||
struct volume_group *orphan_vg; /* Only one ever exists. */
|
||||
uint32_t features;
|
||||
void *library;
|
||||
|
||||
@@ -2197,7 +2197,7 @@ static int _validate_lock_args_chars(const char *lock_args)
|
||||
|
||||
static int _validate_vg_lock_args(struct volume_group *vg)
|
||||
{
|
||||
if (!vg->lock_args || !_validate_lock_args_chars(vg->lock_args)) {
|
||||
if (!_validate_lock_args_chars(vg->lock_args)) {
|
||||
log_error(INTERNAL_ERROR "VG %s has invalid lock_args chars", vg->name);
|
||||
return 0;
|
||||
}
|
||||
@@ -3100,7 +3100,9 @@ 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)
|
||||
@@ -3114,11 +3116,19 @@ 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;
|
||||
@@ -3548,7 +3558,7 @@ static void _set_pv_device(struct format_instance *fid,
|
||||
if (!id_write_format(&pv->id, buffer, sizeof(buffer)))
|
||||
buffer[0] = '\0';
|
||||
|
||||
if (cmd && !cmd->expect_missing_vg_device &&
|
||||
if (cmd && !cmd->pvscan_cache_single &&
|
||||
(!vg_is_foreign(vg) && !cmd->include_foreign_vgs))
|
||||
log_warn("WARNING: Couldn't find device with uuid %s.", buffer);
|
||||
else
|
||||
@@ -5074,7 +5084,7 @@ struct volume_group *vg_read(struct cmd_context *cmd, const char *vg_name, const
|
||||
if (!pvl->pv->dev) {
|
||||
/* The obvious and common case of a missing device. */
|
||||
|
||||
if ((vg_is_foreign(vg) && !cmd->include_foreign_vgs) || cmd->expect_missing_vg_device)
|
||||
if (vg_is_foreign(vg) && !cmd->include_foreign_vgs)
|
||||
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);
|
||||
|
||||
@@ -1231,23 +1231,14 @@ int remove_mirrors_from_segments(struct logical_volume *lv,
|
||||
const char *get_pvmove_pvname_from_lv_mirr(const struct logical_volume *lv_mirr)
|
||||
{
|
||||
struct lv_segment *seg;
|
||||
struct device *dev;
|
||||
|
||||
dm_list_iterate_items(seg, &lv_mirr->segments) {
|
||||
if (!seg_is_mirrored(seg))
|
||||
continue;
|
||||
if (seg_type(seg, 0) == AREA_PV) {
|
||||
dev = seg_dev(seg, 0);
|
||||
if (!dev || dm_list_empty(&dev->aliases))
|
||||
return NULL;
|
||||
return dev_name(dev);
|
||||
}
|
||||
if (seg_type(seg, 0) == AREA_LV) {
|
||||
dev = seg_dev(first_seg(seg_lv(seg, 0)), 0);
|
||||
if (!dev || dm_list_empty(&dev->aliases))
|
||||
return NULL;
|
||||
return dev_name(dev);
|
||||
}
|
||||
if (seg_type(seg, 0) == AREA_PV)
|
||||
return dev_name(seg_dev(seg, 0));
|
||||
if (seg_type(seg, 0) == AREA_LV)
|
||||
return dev_name(seg_dev(first_seg(seg_lv(seg, 0)), 0));
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
||||
@@ -152,11 +152,6 @@ static int _create_pv_entry(struct dm_pool *mem, struct pv_list *pvl,
|
||||
struct pv_list *new_pvl = NULL, *pvl2;
|
||||
struct dm_list *pe_ranges;
|
||||
|
||||
if (!pvl->pv->dev || dm_list_empty(&pvl->pv->dev->aliases)) {
|
||||
log_error("Failed to create PV entry for missing device.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
pvname = pv_dev_name(pvl->pv);
|
||||
if (allocatable_only && !(pvl->pv->status & ALLOCATABLE_PV)) {
|
||||
log_warn("WARNING: Physical volume %s not allocatable.", pvname);
|
||||
|
||||
@@ -679,11 +679,6 @@ int vgreduce_single(struct cmd_context *cmd, struct volume_group *vg,
|
||||
return r;
|
||||
}
|
||||
|
||||
if (!pv->dev || dm_list_empty(&pv->dev->aliases)) {
|
||||
log_error("No device found for PV.");
|
||||
return r;
|
||||
}
|
||||
|
||||
log_debug("vgreduce_single VG %s PV %s", vg->name, pv_dev_name(pv));
|
||||
|
||||
if (pv_pe_alloc_count(pv)) {
|
||||
|
||||
@@ -108,7 +108,6 @@ FIELD(LVS, lv, TIM, "RTime", lvid, 26, lvtimeremoved, lv_time_removed, "Removal
|
||||
FIELD(LVS, lv, STR, "Host", lvid, 10, lvhost, lv_host, "Creation host of the LV, if known.", 0)
|
||||
FIELD(LVS, lv, STR_LIST, "Modules", lvid, 0, modules, lv_modules, "Kernel device-mapper modules required for this LV.", 0)
|
||||
FIELD(LVS, lv, BIN, "Historical", lvid, 0, lvhistorical, lv_historical, "Set if the LV is historical.", 0)
|
||||
FIELD(LVS, lv, NUM, "WCacheBlkSize", lvid, 0, writecache_block_size, writecache_block_size, "The writecache block size", 0)
|
||||
/*
|
||||
* End of LVS type fields
|
||||
*/
|
||||
|
||||
@@ -353,8 +353,6 @@ GET_PV_STR_PROPERTY_FN(pv_device_id_type, pv->device_id_type)
|
||||
#define _writecache_writeback_blocks_get prop_not_implemented_get
|
||||
#define _writecache_error_set prop_not_implemented_set
|
||||
#define _writecache_error_get prop_not_implemented_get
|
||||
#define _writecache_block_size_set prop_not_implemented_set
|
||||
#define _writecache_block_size_get prop_not_implemented_get
|
||||
|
||||
#define _vdo_operating_mode_set prop_not_implemented_set
|
||||
#define _vdo_operating_mode_get prop_not_implemented_get
|
||||
|
||||
@@ -3346,26 +3346,6 @@ static int _integritymismatches_disp(struct dm_report *rh __attribute__((unused)
|
||||
return _field_set_value(field, "", &GET_TYPE_RESERVED_VALUE(num_undef_64));
|
||||
}
|
||||
|
||||
static int _writecache_block_size_disp(struct dm_report *rh __attribute__((unused)),
|
||||
struct dm_pool *mem,
|
||||
struct dm_report_field *field,
|
||||
const void *data,
|
||||
void *private __attribute__((unused)))
|
||||
{
|
||||
struct logical_volume *lv = (struct logical_volume *) data;
|
||||
uint32_t bs = 0;
|
||||
|
||||
if (lv_is_writecache(lv)) {
|
||||
struct lv_segment *seg = first_seg(lv);
|
||||
bs = seg->writecache_block_size;
|
||||
}
|
||||
|
||||
if (!bs)
|
||||
return dm_report_field_int32(rh, field, &GET_TYPE_RESERVED_VALUE(num_undef_32));
|
||||
|
||||
return dm_report_field_uint32(rh, field, &bs);
|
||||
}
|
||||
|
||||
static int _datapercent_disp(struct dm_report *rh, struct dm_pool *mem,
|
||||
struct dm_report_field *field,
|
||||
const void *data, void *private)
|
||||
|
||||
@@ -168,11 +168,6 @@ int id_write_format(const struct id *id, char *buffer, size_t size)
|
||||
|
||||
assert(ID_LEN == 32);
|
||||
|
||||
if (id->uuid[0] == '#') {
|
||||
(void) dm_strncpy(buffer, (char*)id->uuid, size);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* split into groups separated by dashes */
|
||||
if (size < (32 + 6 + 1)) {
|
||||
if (size > 0)
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h> /* help musl C */
|
||||
#include <pthread.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
@@ -609,7 +609,8 @@ 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 >= 4 &&
|
||||
_dm_version_minor >= 15);
|
||||
}
|
||||
|
||||
static int _dm_inactive_supported(void)
|
||||
@@ -762,30 +763,6 @@ 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) +
|
||||
@@ -1459,7 +1436,8 @@ 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 < 4 ||
|
||||
_dm_version_minor < 17)
|
||||
/* can't check, assume uevent is generated */
|
||||
return 1;
|
||||
|
||||
@@ -1806,34 +1784,23 @@ 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];
|
||||
char buf_uuid[DM_UUID_LEN];
|
||||
struct dm_name_list *names;
|
||||
struct dm_names *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),
|
||||
mangling_mode);
|
||||
r = _do_dm_ioctl_unmangle_string(name, "name", buf, sizeof(buf),
|
||||
dm_get_name_mangling_mode());
|
||||
|
||||
if (type == DM_DEVICE_LIST &&
|
||||
((names = ((struct dm_name_list *) ((char *)dmi + dmi->data_start)))) &&
|
||||
((names = ((struct dm_names *) ((char *)dmi + dmi->data_start)))) &&
|
||||
names->dev) {
|
||||
do {
|
||||
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);
|
||||
}
|
||||
names = (struct dm_names *)((char *) names + next);
|
||||
r = _do_dm_ioctl_unmangle_string(names->name, "name",
|
||||
buf, sizeof(buf),
|
||||
dm_get_name_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 lvmautoactivation.7
|
||||
MAN7=lvmsystemid.7 lvmreport.7 lvmraid.7
|
||||
|
||||
MAN8=lvm.8 lvmdump.8 lvm-fullreport.8 lvm-lvpoll.8 \
|
||||
lvcreate.8 lvchange.8 lvmconfig.8 lvconvert.8 lvdisplay.8 \
|
||||
@@ -47,6 +47,7 @@ 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
|
||||
@@ -118,7 +119,7 @@ TESTMAN=test.gen
|
||||
include $(top_builddir)/make.tmpl
|
||||
|
||||
CLEAN_TARGETS+=$(MAN5) $(MAN7) $(MAN8) $(MAN8SO) $(MAN8:%.8=%.8_gen) $(MAN8CLUSTER) \
|
||||
$(MAN8DM) $(TESTMAN)
|
||||
$(MAN8SYSTEMD_GENERATORS) $(MAN8DM) $(TESTMAN)
|
||||
|
||||
all: man device-mapper
|
||||
|
||||
@@ -126,11 +127,11 @@ all: man device-mapper
|
||||
|
||||
device-mapper: $(MAN8DM)
|
||||
|
||||
man: $(MAN5) $(MAN7) $(MAN8) $(MAN8SO) $(MAN8CLUSTER)
|
||||
man: $(MAN5) $(MAN7) $(MAN8) $(MAN8SO) $(MAN8CLUSTER) $(MAN8SYSTEMD_GENERATORS)
|
||||
|
||||
all_man: man
|
||||
|
||||
$(MAN5) $(MAN7) $(MAN8) $(MAN8SO) $(MAN8DM) $(MAN8CLUSTER): Makefile
|
||||
$(MAN5) $(MAN7) $(MAN8) $(MAN8SO) $(MAN8DM) $(MAN8CLUSTER) $(MAN8SYSTEMD_GENERATORS): Makefile
|
||||
|
||||
$(MANGENERATOR):
|
||||
@echo " [MAKE] $<"
|
||||
@@ -288,6 +289,11 @@ 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
|
||||
|
||||
@@ -61,6 +61,8 @@ and more, using a more compact and configurable output format.
|
||||
.br
|
||||
[ \fB--readonly\fP ]
|
||||
.br
|
||||
[ \fB--reportformat\fP \fBbasic\fP|\fBjson\fP ]
|
||||
.br
|
||||
[ \fB--segments\fP ]
|
||||
.br
|
||||
[ \fB--separator\fP \fIString\fP ]
|
||||
@@ -330,6 +332,16 @@ device-mapper kernel driver, so this option is unable to report whether
|
||||
or not LVs are actually in use.
|
||||
.
|
||||
.HP
|
||||
\fB--reportformat\fP \fBbasic\fP|\fBjson\fP
|
||||
.br
|
||||
Overrides current output format for reports which is defined globally by
|
||||
the report/output_format setting in \fBlvm.conf\fP(5).
|
||||
\fBbasic\fP is the original format with columns and rows.
|
||||
If there is more than one report per command, each report is prefixed
|
||||
with the report name for identification. \fBjson\fP produces report
|
||||
output in JSON format. See \fBlvmreport\fP(7) for more information.
|
||||
.
|
||||
.HP
|
||||
\fB--segments\fP
|
||||
.br
|
||||
.
|
||||
|
||||
@@ -563,6 +563,7 @@ 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
|
||||
@@ -578,7 +579,6 @@ 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),
|
||||
|
||||
58
man/lvm2-activation-generator.8_main
Normal file
58
man/lvm2-activation-generator.8_main
Normal file
@@ -0,0 +1,58 @@
|
||||
.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)
|
||||
@@ -1,289 +0,0 @@
|
||||
.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
|
||||
|
||||
@@ -240,59 +240,18 @@ The writecache block size should be chosen to match the xfs sectsz value.
|
||||
It is also possible to specify a sector size of 4096 to mkfs.xfs when
|
||||
creating the file system. In this case the writecache block size of 4096
|
||||
can be used.
|
||||
.P
|
||||
The writecache block size is displayed by the command:
|
||||
.br
|
||||
lvs -o writecacheblocksize VG/LV
|
||||
.P
|
||||
.SS dm-writecache memory usage
|
||||
.P
|
||||
The amount of main system memory used by dm-writecache can be a factor
|
||||
when selecting the writecache cachevol size and the writecache block size.
|
||||
.P
|
||||
.IP \[bu] 2
|
||||
writecache block size 4096: each 100 GiB of writecache cachevol uses
|
||||
slighly over 2 GiB of system memory.
|
||||
.IP \[bu] 2
|
||||
writecache block size 512: each 100 GiB of writecache cachevol uses
|
||||
a little over 16 GiB of system memory.
|
||||
.
|
||||
.SS dm-writecache settings
|
||||
.
|
||||
To specify dm-writecache tunable settings on the command line, use:
|
||||
.br
|
||||
--cachesettings 'option=N' or
|
||||
.br
|
||||
--cachesettings 'option1=N option2=N ...'
|
||||
.P
|
||||
For example, --cachesettings 'high_watermark=90 writeback_jobs=4'.
|
||||
.P
|
||||
To include settings when caching is started, run:
|
||||
Tunable parameters can be passed to the dm-writecache kernel module using
|
||||
the --cachesettings option when caching is started, e.g.
|
||||
.P
|
||||
.nf
|
||||
# lvconvert --type writecache --cachevol fast \\
|
||||
--cachesettings 'option=N' vg/main
|
||||
--cachesettings 'high_watermark=N writeback_jobs=N' vg/main
|
||||
.fi
|
||||
.P
|
||||
To change settings for an existing writecache, run:
|
||||
.P
|
||||
.nf
|
||||
# lvchange --cachesettings 'option=N' vg/main
|
||||
.fi
|
||||
.P
|
||||
To clear all settings that have been applied, run:
|
||||
.P
|
||||
.nf
|
||||
# lvchange --cachesettings '' vg/main
|
||||
.fi
|
||||
.P
|
||||
To view the settings that are applied to a writecache LV, run:
|
||||
.P
|
||||
.nf
|
||||
# lvs -o cachesettings vg/main
|
||||
.fi
|
||||
.P
|
||||
Tunable settings are:
|
||||
Tunable options are:
|
||||
.
|
||||
.TP
|
||||
high_watermark = <percent>
|
||||
@@ -340,41 +299,6 @@ perform. If not done beforehand, the splitcache command enables the
|
||||
cleaner mode and waits for flushing to complete before detaching the
|
||||
writecache. Adding cleaner=0 to the splitcache command will skip the
|
||||
cleaner mode, and any required flushing is performed in device suspend.
|
||||
|
||||
.SS dm-writecache using metadata profiles
|
||||
.
|
||||
In addition to specifying writecache settings on the command line, they
|
||||
can also be set in lvm.conf, or in a profile file, using the
|
||||
allocation/cache_settings/writecache config structure shown below.
|
||||
.P
|
||||
It's possible to prepare a number of different profile files in the
|
||||
\fI#DEFAULT_SYS_DIR#/profile\fP directory and specify the file name
|
||||
of the profile when starting writecache.
|
||||
.P
|
||||
.I Example
|
||||
.nf
|
||||
# cat <<EOF > #DEFAULT_SYS_DIR#/profile/cache_writecache.profile
|
||||
allocation {
|
||||
.RS
|
||||
cache_settings {
|
||||
.RS
|
||||
writecache {
|
||||
.RS
|
||||
high_watermark=60
|
||||
writeback_jobs=1024
|
||||
.RE
|
||||
}
|
||||
.RE
|
||||
}
|
||||
.RE
|
||||
}
|
||||
EOF
|
||||
.P
|
||||
|
||||
# lvcreate -an -L10G --name fast vg /dev/fast_ssd
|
||||
# lvcreate --type writecache -L10G --name main --cachevol fast \\
|
||||
--metadataprofile cache_writecache vg /dev/slow_hdd
|
||||
.fi
|
||||
.
|
||||
.SS dm-cache with separate data and metadata LVs
|
||||
.
|
||||
|
||||
@@ -322,8 +322,7 @@ Find a device with the PVID and add the device to the devices file.
|
||||
.HP
|
||||
\fB--check\fP
|
||||
.br
|
||||
Checks the content of the devices file.
|
||||
Reports incorrect device names or PVIDs for entries.
|
||||
Check the content of the devices file.
|
||||
.
|
||||
.HP
|
||||
\fB--commandprofile\fP \fIString\fP
|
||||
|
||||
@@ -61,6 +61,8 @@ and more, using a more compact and configurable output format.
|
||||
.br
|
||||
[ \fB--readonly\fP ]
|
||||
.br
|
||||
[ \fB--reportformat\fP \fBbasic\fP|\fBjson\fP ]
|
||||
.br
|
||||
[ \fB--separator\fP \fIString\fP ]
|
||||
.br
|
||||
[ \fB--shared\fP ]
|
||||
@@ -318,6 +320,16 @@ device-mapper kernel driver, so this option is unable to report whether
|
||||
or not LVs are actually in use.
|
||||
.
|
||||
.HP
|
||||
\fB--reportformat\fP \fBbasic\fP|\fBjson\fP
|
||||
.br
|
||||
Overrides current output format for reports which is defined globally by
|
||||
the report/output_format setting in \fBlvm.conf\fP(5).
|
||||
\fBbasic\fP is the original format with columns and rows.
|
||||
If there is more than one report per command, each report is prefixed
|
||||
with the report name for identification. \fBjson\fP produces report
|
||||
output in JSON format. See \fBlvmreport\fP(7) for more information.
|
||||
.
|
||||
.HP
|
||||
\fB-S\fP|\fB--select\fP \fIString\fP
|
||||
.br
|
||||
Select objects for processing and reporting based on specified criteria.
|
||||
|
||||
@@ -4,47 +4,56 @@ like
|
||||
or
|
||||
.BR pvdisplay (8).
|
||||
.P
|
||||
When --cache is used, pvscan updates runtime lvm state on the system, or
|
||||
with -aay performs autoactivation.
|
||||
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:
|
||||
.P
|
||||
.B pvscan --cache
|
||||
.I device
|
||||
.P
|
||||
If device is present, lvm records that the PV on device is online.
|
||||
If device is present, lvm adds a record that the PV on device is online.
|
||||
If device is not present, lvm removes the online record for the PV.
|
||||
pvscan only reads the named device.
|
||||
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.)
|
||||
.P
|
||||
.B pvscan --cache
|
||||
.P
|
||||
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.)
|
||||
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.
|
||||
.P
|
||||
.B pvscan --cache -aay
|
||||
.P
|
||||
Performs the --cache steps for all devices, then autoactivates any complete VGs.
|
||||
This begins by performing the same steps as pvscan --cache. Afterward, it
|
||||
activates LVs in any complete VGs.
|
||||
.P
|
||||
.B pvscan --cache --listvg|--listlvs
|
||||
.I device
|
||||
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
|
||||
.P
|
||||
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
|
||||
Auto-activation of VGs or LVs can be enabled/disabled using:
|
||||
.br
|
||||
.BR lvm.conf (5)
|
||||
.B activation/auto_activation_volume_list
|
||||
.P
|
||||
See
|
||||
.BR lvmautoactivation (7)
|
||||
for more information about how pvscan is used for autoactivation.
|
||||
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.
|
||||
|
||||
@@ -15,8 +15,6 @@ pvscan \(em List all physical volumes
|
||||
.P
|
||||
.ad l
|
||||
\fB-a\fP|\fB--activate\fP \fBy\fP|\fBn\fP|\fBay\fP
|
||||
.br
|
||||
\fB--autoactivation\fP \fIString\fP
|
||||
.br
|
||||
\fB--cache\fP
|
||||
.br
|
||||
@@ -93,50 +91,59 @@ like
|
||||
or
|
||||
.BR pvdisplay (8).
|
||||
.P
|
||||
When --cache is used, pvscan updates runtime lvm state on the system, or
|
||||
with -aay performs autoactivation.
|
||||
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:
|
||||
.P
|
||||
.B pvscan --cache
|
||||
.I device
|
||||
.P
|
||||
If device is present, lvm records that the PV on device is online.
|
||||
If device is present, lvm adds a record that the PV on device is online.
|
||||
If device is not present, lvm removes the online record for the PV.
|
||||
pvscan only reads the named device.
|
||||
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.)
|
||||
.P
|
||||
.B pvscan --cache
|
||||
.P
|
||||
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.)
|
||||
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.
|
||||
.P
|
||||
.B pvscan --cache -aay
|
||||
.P
|
||||
Performs the --cache steps for all devices, then autoactivates any complete VGs.
|
||||
This begins by performing the same steps as pvscan --cache. Afterward, it
|
||||
activates LVs in any complete VGs.
|
||||
.P
|
||||
.B pvscan --cache --listvg|--listlvs
|
||||
.I device
|
||||
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
|
||||
.P
|
||||
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
|
||||
Auto-activation of VGs or LVs can be enabled/disabled using:
|
||||
.br
|
||||
.BR lvm.conf (5)
|
||||
.B activation/auto_activation_volume_list
|
||||
.P
|
||||
See
|
||||
.BR lvmautoactivation (7)
|
||||
for more information about how pvscan is used for autoactivation.
|
||||
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.
|
||||
.
|
||||
.SH USAGE
|
||||
.
|
||||
@@ -208,8 +215,6 @@ Record that a PV is online and autoactivate the VG if complete.
|
||||
.br
|
||||
[ \fB--noudevsync\fP ]
|
||||
.br
|
||||
[ \fB--autoactivation\fP \fIString\fP ]
|
||||
.br
|
||||
[ COMMON_OPTIONS ]
|
||||
.ad b
|
||||
.RE
|
||||
@@ -234,8 +239,6 @@ Record that a PV is online and list the VG using the PV.
|
||||
.br
|
||||
[ \fB--udevoutput\fP ]
|
||||
.br
|
||||
[ \fB--autoactivation\fP \fIString\fP ]
|
||||
.br
|
||||
[ COMMON_OPTIONS ]
|
||||
.ad b
|
||||
.RE
|
||||
@@ -339,14 +342,6 @@ Auto-activate LVs in a VG when the PVs scanned have completed the VG.
|
||||
(Only \fBay\fP is applicable.)
|
||||
.
|
||||
.HP
|
||||
\fB--autoactivation\fP \fIString\fP
|
||||
.br
|
||||
Specify if autoactivation is being used from an event.
|
||||
This allows the command to apply settings that are specific
|
||||
to event activation, such as device scanning optimizations
|
||||
using pvs_online files created by event-based pvscans.
|
||||
.
|
||||
.HP
|
||||
\fB--cache\fP
|
||||
.br
|
||||
Scan one or more devices and record that they are online.
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
.P
|
||||
.BR lvm-fullreport (8),
|
||||
.BR lvm-lvpoll (8),
|
||||
.BR lvm2-activation-generator (8),
|
||||
.BR blkdeactivate (8),
|
||||
.BR lvmdump (8),
|
||||
.P
|
||||
|
||||
@@ -24,8 +24,6 @@ vgchange \(em Change volume group attributes
|
||||
.nh
|
||||
\%\fBcontiguous\fP|\:\fBcling\fP|\:\fBcling_by_tags\fP|\:\fBnormal\fP|\:\fBanywhere\fP|\:\fBinherit\fP
|
||||
.hy
|
||||
.br
|
||||
\fB--autoactivation\fP \fIString\fP
|
||||
.br
|
||||
\fB-A\fP|\fB--autobackup\fP \fBy\fP|\fBn\fP
|
||||
.br
|
||||
@@ -288,8 +286,6 @@ Activate or deactivate LVs.
|
||||
.br
|
||||
[ \fB--poll\fP \fBy\fP|\fBn\fP ]
|
||||
.br
|
||||
[ \fB--autoactivation\fP \fIString\fP ]
|
||||
.br
|
||||
[ \fB--ignoremonitoring\fP ]
|
||||
.br
|
||||
[ \fB--noudevsync\fP ]
|
||||
@@ -520,14 +516,6 @@ which PVs the command will use for allocation.
|
||||
See \fBlvm\fP(8) for more information about allocation.
|
||||
.
|
||||
.HP
|
||||
\fB--autoactivation\fP \fIString\fP
|
||||
.br
|
||||
Specify if autoactivation is being used from an event.
|
||||
This allows the command to apply settings that are specific
|
||||
to event activation, such as device scanning optimizations
|
||||
using pvs_online files created by event-based pvscans.
|
||||
.
|
||||
.HP
|
||||
\fB-A\fP|\fB--autobackup\fP \fBy\fP|\fBn\fP
|
||||
.br
|
||||
Specifies if metadata should be backed up automatically after a change.
|
||||
|
||||
@@ -58,6 +58,8 @@ and more, using a more compact and configurable output format.
|
||||
.br
|
||||
[ \fB--readonly\fP ]
|
||||
.br
|
||||
[ \fB--reportformat\fP \fBbasic\fP|\fBjson\fP ]
|
||||
.br
|
||||
[ \fB--shared\fP ]
|
||||
.br
|
||||
[ \fB--separator\fP \fIString\fP ]
|
||||
@@ -310,6 +312,16 @@ device-mapper kernel driver, so this option is unable to report whether
|
||||
or not LVs are actually in use.
|
||||
.
|
||||
.HP
|
||||
\fB--reportformat\fP \fBbasic\fP|\fBjson\fP
|
||||
.br
|
||||
Overrides current output format for reports which is defined globally by
|
||||
the report/output_format setting in \fBlvm.conf\fP(5).
|
||||
\fBbasic\fP is the original format with columns and rows.
|
||||
If there is more than one report per command, each report is prefixed
|
||||
with the report name for identification. \fBjson\fP produces report
|
||||
output in JSON format. See \fBlvmreport\fP(7) for more information.
|
||||
.
|
||||
.HP
|
||||
\fB-S\fP|\fB--select\fP \fIString\fP
|
||||
.br
|
||||
Select objects for processing and reporting based on specified criteria.
|
||||
|
||||
@@ -63,7 +63,7 @@ install_initscripts:
|
||||
@echo " [INSTALL] initscripts"
|
||||
$(Q) $(INSTALL_DIR) $(initdir)
|
||||
ifeq ("@BUILD_DMEVENTD@", "yes")
|
||||
$(Q) $(INSTALL_SCRIPT) lvm2_monitoring_init_red_hat $(initdir)/lvm2-monitor
|
||||
$(Q) $(INSTALL_SCRIPT) lvm2_monitoring_init_red_hat $(initdir)/lvm-monitor
|
||||
endif
|
||||
ifeq ("@BUILD_LVMPOLLD@", "yes")
|
||||
$(Q) $(INSTALL_SCRIPT) lvm2_lvmpolld_init_red_hat $(initdir)/lvm2-lvmpolld
|
||||
@@ -78,10 +78,12 @@ endif
|
||||
install_systemd_units: install_dbus_service
|
||||
@echo " [INSTALL] systemd_units"
|
||||
$(Q) $(INSTALL_DIR) $(systemd_unit_dir)
|
||||
$(Q) $(INSTALL_DATA) lvm-activate-vgs-main.service $(systemd_unit_dir)/lvm-activate-vgs-main.service
|
||||
$(Q) $(INSTALL_DATA) lvm-activate-vgs-last.service $(systemd_unit_dir)/lvm-activate-vgs-last.service
|
||||
ifeq ("@BUILD_DMEVENTD@", "yes")
|
||||
$(Q) $(INSTALL_DATA) dm_event_systemd_red_hat.socket $(systemd_unit_dir)/dm-event.socket
|
||||
$(Q) $(INSTALL_DATA) dm_event_systemd_red_hat.service $(systemd_unit_dir)/dm-event.service
|
||||
$(Q) $(INSTALL_DATA) lvm2_monitoring_systemd_red_hat.service $(systemd_unit_dir)/lvm2-monitor.service
|
||||
$(Q) $(INSTALL_DATA) lvm2_monitoring_systemd_red_hat.service $(systemd_unit_dir)/lvm-monitor.service
|
||||
endif
|
||||
ifeq ("@BLKDEACTIVATE@", "yes")
|
||||
$(Q) $(INSTALL_DATA) blk_availability_systemd_red_hat.service $(systemd_unit_dir)/blk-availability.service
|
||||
@@ -141,7 +143,9 @@ DISTCLEAN_TARGETS += \
|
||||
lvm2_monitoring_init_red_hat \
|
||||
lvm2_monitoring_systemd_red_hat.service \
|
||||
lvm2_pvscan_systemd_red_hat@.service \
|
||||
lvm2_tmpfiles_red_hat.conf
|
||||
lvm2_tmpfiles_red_hat.conf \
|
||||
lvm-activate-vgs-main.service \
|
||||
lvm-activate-vgs-last.service
|
||||
|
||||
# Remove ancient files
|
||||
DISTCLEAN_TARGETS += \
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[Unit]
|
||||
Description=Availability of block devices
|
||||
Before=shutdown.target
|
||||
After=iscsi-shutdown.service iscsi.service iscsid.service fcoe.service rbdmap.service
|
||||
After=lvm-activate-vgs-main.service lvm-activate-vgs-last.service iscsi-shutdown.service iscsi.service iscsid.service fcoe.service rbdmap.service
|
||||
DefaultDependencies=no
|
||||
Conflicts=shutdown.target
|
||||
|
||||
|
||||
221
scripts/generator-internals.c
Normal file
221
scripts/generator-internals.c
Normal file
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
24
scripts/lvm-activate-vgs-last.service.in
Normal file
24
scripts/lvm-activate-vgs-last.service.in
Normal file
@@ -0,0 +1,24 @@
|
||||
[Unit]
|
||||
Description=Activate LVM Volume Groups (last)
|
||||
Documentation=man:vgchange(8)
|
||||
After=lvm-activate-vgs-main.service multipathd.service cryptsetup.target dm-event.socket dm-event.service
|
||||
Before=local-fs-pre.target shutdown.target
|
||||
DefaultDependencies=no
|
||||
Conflicts=shutdown.target
|
||||
|
||||
# "--autoactivation service" tells vgchange it is being called
|
||||
# from an activation service, so it will do nothing if
|
||||
# lvm.conf autoactivation_settings has "event_only".
|
||||
# "--autoactivation event_enable" tells vgchange to enable
|
||||
# event-based pvscan activations by creating /run/lvm/event-activation-on.
|
||||
# By default this vgchange will use device hints from pvs_online
|
||||
# files, so it will only look at PVs that have finished being
|
||||
# processed by udev and pvscan --cache.
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=@SBINDIR@/lvm vgchange -aay --vgonline --autoactivation service,event_enable
|
||||
RemainAfterExit=yes
|
||||
|
||||
[Install]
|
||||
WantedBy=sysinit.target
|
||||
22
scripts/lvm-activate-vgs-main.service.in
Normal file
22
scripts/lvm-activate-vgs-main.service.in
Normal file
@@ -0,0 +1,22 @@
|
||||
[Unit]
|
||||
Description=Activate LVM Volume Groups
|
||||
Documentation=man:vgchange(8)
|
||||
After=dm-event.socket dm-event.service lvm-devices-wait.service
|
||||
Before=local-fs-pre.target shutdown.target
|
||||
DefaultDependencies=no
|
||||
Conflicts=shutdown.target
|
||||
|
||||
# "--autoactivation service" tells vgchange it is being called
|
||||
# from an autoactivation service, so it will do nothing if
|
||||
# lvm.conf autoactivation_settings has "event_only".
|
||||
# By default this vgchange will use device hints from pvs_online
|
||||
# files, so it will only look at PVs that have finished being
|
||||
# processed by udev and pvscan --cache.
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=@SBINDIR@/lvm vgchange -aay --vgonline --autoactivation service
|
||||
RemainAfterExit=yes
|
||||
|
||||
[Install]
|
||||
WantedBy=sysinit.target
|
||||
21
scripts/lvm-devices-wait.service.in
Normal file
21
scripts/lvm-devices-wait.service.in
Normal file
@@ -0,0 +1,21 @@
|
||||
[Unit]
|
||||
Description=Wait for LVM devices to be ready
|
||||
Documentation=man:lvmdevices(8)
|
||||
After=systemd-udev-trigger.service
|
||||
Before=lvm-activate-vgs-main.service lvm-activate-vgs-last.service
|
||||
DefaultDependencies=no
|
||||
Conflicts=shutdown.target
|
||||
|
||||
# Waits for entries in /etc/lvm/devices/system.devices to be processed
|
||||
# by udev and pvscan --cache. Only device entries that are found in
|
||||
# sysfs are waited for. See lvm.conf devices_wait_service "timeout=N"
|
||||
# to configure the number of seconds that this service will wait.
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=@SBINDIR@/lvmdevices --wait pvsonline
|
||||
RemainAfterExit=yes
|
||||
|
||||
[Install]
|
||||
WantedBy=sysinit.target
|
||||
|
||||
@@ -10,5 +10,5 @@ Conflicts=shutdown.target
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
ExecStart=@SBINDIR@/lvm pvscan --cache --activate ay --autoactivation event %i
|
||||
ExecStart=@SBINDIR@/lvm pvscan --cache --activate ay %i
|
||||
ExecStop=@SBINDIR@/lvm pvscan --cache %i
|
||||
|
||||
233
scripts/lvm2_activation_generator_systemd_red_hat.c
Normal file
233
scripts/lvm2_activation_generator_systemd_red_hat.c
Normal file
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
[Unit]
|
||||
Description=Monitoring of LVM2 mirrors, snapshots etc. using dmeventd or progress polling
|
||||
Description=Monitor LVM Logical Volumes
|
||||
Documentation=man:dmeventd(8) man:lvcreate(8) man:lvchange(8) man:vgchange(8)
|
||||
Requires=dm-event.socket
|
||||
After=dm-event.socket dm-event.service
|
||||
After=dm-event.socket dm-event.service lvm-activate-vgs-last.service
|
||||
Before=local-fs-pre.target shutdown.target
|
||||
DefaultDependencies=no
|
||||
Conflicts=shutdown.target
|
||||
|
||||
@@ -414,12 +414,6 @@ 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"
|
||||
}
|
||||
|
||||
|
||||
@@ -23,10 +23,10 @@ if [ $1 = 0 ]; then
|
||||
fi
|
||||
|
||||
%triggerun -- %{name} < 2.02.86-2
|
||||
%{_bindir}/systemd-sysv-convert --save lvm2-monitor >/dev/null 2>&1 || :
|
||||
/bin/systemctl --no-reload enable lvm2-monitor.service > /dev/null 2>&1 || :
|
||||
/sbin/chkconfig --del lvm2-monitor > /dev/null 2>&1 || :
|
||||
/bin/systemctl try-restart lvm2-monitor.service > /dev/null 2>&1 || :
|
||||
%{_bindir}/systemd-sysv-convert --save lvm-monitor >/dev/null 2>&1 || :
|
||||
/bin/systemctl --no-reload enable lvm-monitor.service > /dev/null 2>&1 || :
|
||||
/sbin/chkconfig --del lvm-monitor > /dev/null 2>&1 || :
|
||||
/bin/systemctl try-restart lvm-monitor.service > /dev/null 2>&1 || :
|
||||
# files in the main package
|
||||
|
||||
%files
|
||||
@@ -88,7 +88,6 @@ 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
|
||||
@@ -185,15 +184,17 @@ fi
|
||||
%dir %{_default_run_dir}
|
||||
%if %{enable_systemd}
|
||||
%{_tmpfilesdir}/%{name}.conf
|
||||
%{_unitdir}/lvm-activate-vgs-main.service
|
||||
%{_unitdir}/lvm-activate-vgs-last.service
|
||||
%{_unitdir}/lvm-monitor.service
|
||||
%{_unitdir}/blk-availability.service
|
||||
%{_unitdir}/lvm2-monitor.service
|
||||
%if %{have_service lvmpolld}
|
||||
%{_unitdir}/lvm2-lvmpolld.service
|
||||
%{_unitdir}/lvm2-lvmpolld.socket
|
||||
%endif
|
||||
%else
|
||||
%{_sysconfdir}/rc.d/init.d/blk-availability
|
||||
%{_sysconfdir}/rc.d/init.d/lvm2-monitor
|
||||
%{_sysconfdir}/rc.d/init.d/lvm-monitor
|
||||
%if %{have_service lvmpolld}
|
||||
%{_sysconfdir}/rc.d/init.d/lvm2-lvmpolld
|
||||
%endif
|
||||
|
||||
@@ -545,73 +545,4 @@ 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
|
||||
|
||||
@@ -1,224 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright (C) 2020 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='devices file editing with lvmdevices'
|
||||
|
||||
. lib/inittest
|
||||
|
||||
aux lvmconf 'devices/scan = "/dev"'
|
||||
|
||||
aux prepare_devs 1
|
||||
|
||||
# The tests run with system dir of "/etc" but lvm when running
|
||||
# normally has cmd->system_dir set to "/etc/lvm".
|
||||
DFDIR="$LVM_SYSTEM_DIR/devices"
|
||||
mkdir -p "$DFDIR" || true
|
||||
DF="$DFDIR/system.devices"
|
||||
|
||||
aux lvmconf 'devices/use_devicesfile = 1'
|
||||
|
||||
losetup -h | grep sector-size || skip
|
||||
which fallocate || skip
|
||||
|
||||
fallocate -l 2M loopa
|
||||
fallocate -l 2M loopb
|
||||
|
||||
setup_loop_devs() {
|
||||
for i in {1..5} ; do
|
||||
LOOP1=$(losetup -f loopa --show || true)
|
||||
test -n "$LOOP1" && break
|
||||
done
|
||||
for i in {1..5} ; do
|
||||
LOOP2=$(losetup -f loopb --show || true)
|
||||
test -n "$LOOP2" && break
|
||||
done
|
||||
}
|
||||
|
||||
setup_loop_devs
|
||||
|
||||
# Tests of devices without PV on them.
|
||||
|
||||
# add/del with default idtype loop_file
|
||||
lvmdevices --adddev "$LOOP1"
|
||||
grep "$LOOP1" $DF
|
||||
lvmdevices --adddev "$LOOP2"
|
||||
grep "$LOOP2" $DF
|
||||
grep "IDTYPE=loop_file" $DF
|
||||
not grep "IDTYPE=devname" $DF
|
||||
lvmdevices --deldev "$LOOP1"
|
||||
not grep "$LOOP1" $DF
|
||||
lvmdevices --deldev "$LOOP2"
|
||||
not grep "$LOOP2" $DF
|
||||
|
||||
# add/del with non-default idtype devname
|
||||
lvmdevices --adddev "$LOOP1" --deviceidtype devname
|
||||
grep "$LOOP1" $DF
|
||||
lvmdevices --adddev "$LOOP2" --deviceidtype devname
|
||||
grep "$LOOP2" $DF
|
||||
grep "IDTYPE=devname" $DF
|
||||
not grep "IDTYPE=loop_file" $DF
|
||||
lvmdevices --deldev "$LOOP1"
|
||||
not grep "$LOOP1" $DF
|
||||
lvmdevices --deldev "$LOOP2"
|
||||
not grep "$LOOP2" $DF
|
||||
|
||||
# add/del when dev is missing, using default idtype
|
||||
lvmdevices --adddev "$LOOP1"
|
||||
grep "$LOOP1" $DF
|
||||
lvmdevices --adddev "$LOOP2"
|
||||
grep "$LOOP2" $DF
|
||||
losetup -D
|
||||
grep "$LOOP1" $DF
|
||||
grep "$LOOP2" $DF
|
||||
lvmdevices --deldev "$LOOP1"
|
||||
not grep "$LOOP1" $DF
|
||||
lvmdevices --deldev "$LOOP2"
|
||||
not grep "$LOOP2" $DF
|
||||
not lvmdevices --adddev "$LOOP1"
|
||||
not lvmdevices --adddev "$LOOP2"
|
||||
not grep "$LOOP1" $DF
|
||||
not grep "$LOOP2" $DF
|
||||
setup_loop_devs
|
||||
rm $DF
|
||||
|
||||
# add/del when dev is missing, using devname idtype
|
||||
lvmdevices --adddev "$LOOP1" --deviceidtype devname
|
||||
grep "$LOOP1" $DF
|
||||
lvmdevices --adddev "$LOOP2" --deviceidtype devname
|
||||
grep "$LOOP2" $DF
|
||||
losetup -D
|
||||
grep "$LOOP1" $DF
|
||||
grep "$LOOP2" $DF
|
||||
lvmdevices --deldev "$LOOP1"
|
||||
not grep "$LOOP1" $DF
|
||||
lvmdevices --deldev "$LOOP2"
|
||||
not grep "$LOOP2" $DF
|
||||
setup_loop_devs
|
||||
rm $DF
|
||||
|
||||
# Tests of devices with PV on them.
|
||||
|
||||
touch $DF
|
||||
pvcreate "$LOOP1"
|
||||
pvcreate "$LOOP2"
|
||||
# PVID without dashes for matching devices file fields
|
||||
PVID1=`pvs "$LOOP1" --noheading -o uuid | tr -d - | awk '{print $1}'`
|
||||
PVID2=`pvs "$LOOP2" --noheading -o uuid | tr -d - | awk '{print $1}'`
|
||||
# PVID with dashes for matching pvs -o+uuid output
|
||||
OPVID1=`pvs "$LOOP1" --noheading -o uuid | awk '{print $1}'`
|
||||
OPVID2=`pvs "$LOOP2" --noheading -o uuid | awk '{print $1}'`
|
||||
grep "$LOOP1" $DF
|
||||
grep "$LOOP2" $DF
|
||||
grep "$PVID1" $DF
|
||||
grep "$PVID2" $DF
|
||||
rm $DF
|
||||
|
||||
# add/deldev with default idtype loop_file
|
||||
lvmdevices --adddev "$LOOP1"
|
||||
grep "$LOOP1" $DF
|
||||
grep "$PVID1" $DF
|
||||
lvmdevices --adddev "$LOOP2"
|
||||
grep "$LOOP2" $DF
|
||||
grep "$PVID2" $DF
|
||||
grep "IDTYPE=loop_file" $DF
|
||||
not grep "IDTYPE=devname" $DF
|
||||
lvmdevices --deldev "$LOOP1"
|
||||
not grep "$LOOP1" $DF
|
||||
lvmdevices --deldev "$LOOP2"
|
||||
not grep "$LOOP2" $DF
|
||||
|
||||
# add/delpvid with default idtype loop_file
|
||||
lvmdevices --addpvid "$PVID1"
|
||||
grep "$LOOP1" $DF
|
||||
grep "$PVID1" $DF
|
||||
lvmdevices --addpvid "$PVID2"
|
||||
grep "$LOOP2" $DF
|
||||
grep "$PVID2" $DF
|
||||
grep "IDTYPE=loop_file" $DF
|
||||
not grep "IDTYPE=devname" $DF
|
||||
lvmdevices --delpvid "$PVID1"
|
||||
not grep "$LOOP1" $DF
|
||||
not grep "$PVID1" $DF
|
||||
lvmdevices --delpvid "$PVID2"
|
||||
not grep "$LOOP2" $DF
|
||||
not grep "$PVID2" $DF
|
||||
|
||||
# add/deldev with non-default idtype devname
|
||||
lvmdevices --adddev "$LOOP1" --deviceidtype devname
|
||||
grep "$LOOP1" $DF
|
||||
grep "$PVID1" $DF
|
||||
lvmdevices --adddev "$LOOP2" --deviceidtype devname
|
||||
grep "$LOOP2" $DF
|
||||
grep "$PVID2" $DF
|
||||
grep "IDTYPE=devname" $DF
|
||||
not grep "IDTYPE=loop_file" $DF
|
||||
lvmdevices --deldev "$LOOP1"
|
||||
not grep "$LOOP1" $DF
|
||||
lvmdevices --deldev "$LOOP2"
|
||||
not grep "$LOOP2" $DF
|
||||
|
||||
# add/delpvid with non-default idtype devname
|
||||
lvmdevices --addpvid "$PVID1" --deviceidtype devname
|
||||
grep "$LOOP1" $DF
|
||||
grep "$PVID1" $DF
|
||||
lvmdevices --addpvid "$PVID2" --deviceidtype devname
|
||||
grep "$LOOP2" $DF
|
||||
grep "$PVID2" $DF
|
||||
grep "IDTYPE=devname" $DF
|
||||
not grep "IDTYPE=loop_file" $DF
|
||||
lvmdevices --deldev "$LOOP1"
|
||||
not grep "$LOOP1" $DF
|
||||
lvmdevices --deldev "$LOOP2"
|
||||
not grep "$LOOP2" $DF
|
||||
|
||||
# add/deldev when dev is missing, using default idtype
|
||||
lvmdevices --adddev "$LOOP1"
|
||||
grep "$LOOP1" $DF
|
||||
grep "$PVID1" $DF
|
||||
lvmdevices --adddev "$LOOP2"
|
||||
grep "$LOOP2" $DF
|
||||
grep "$PVID2" $DF
|
||||
losetup -D
|
||||
grep "$LOOP1" $DF
|
||||
grep "$LOOP2" $DF
|
||||
lvmdevices --deldev "$LOOP1"
|
||||
not grep "$LOOP1" $DF
|
||||
not grep "$PVID1" $DF
|
||||
lvmdevices --deldev "$LOOP2"
|
||||
not grep "$LOOP2" $DF
|
||||
not grep "$PVID2" $DF
|
||||
setup_loop_devs
|
||||
rm $DF
|
||||
|
||||
# add/delpvid when dev is missing, using devname idtype
|
||||
lvmdevices --addpvid "$PVID1" --deviceidtype devname
|
||||
grep "$LOOP1" $DF
|
||||
grep "$PVID1" $DF
|
||||
lvmdevices --addpvid "$PVID2" --deviceidtype devname
|
||||
grep "$LOOP2" $DF
|
||||
grep "$PVID2" $DF
|
||||
losetup -D
|
||||
grep "$LOOP1" $DF
|
||||
grep "$LOOP2" $DF
|
||||
lvmdevices --delpvid "$PVID1"
|
||||
not grep "$LOOP1" $DF
|
||||
not grep "$PVID1" $DF
|
||||
lvmdevices --delpvid "$PVID2"
|
||||
not grep "$LOOP2" $DF
|
||||
not grep "$PVID2" $DF
|
||||
setup_loop_devs
|
||||
rm $DF
|
||||
|
||||
# TODO: add/rem of partitions of same device
|
||||
|
||||
losetup -D
|
||||
rm loopa loopb
|
||||
@@ -423,7 +423,7 @@ sed "s/$pvid1/badpvid/" "$DF.orig" |tee $DF
|
||||
not grep $pvid1 $DF
|
||||
grep $did1 $DF
|
||||
|
||||
not lvmdevices --check 2>&1|tee out
|
||||
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
|
||||
not lvmdevices --check 2>&1 |tee out
|
||||
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
|
||||
not lvmdevices --check 2>&1 |tee out
|
||||
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
|
||||
not lvmdevices --check 2>&1 |tee out
|
||||
lvmdevices --check 2>&1 |tee out
|
||||
grep $dev1 out
|
||||
|
||||
pvs -o+uuid,deviceid | grep $vg |tee out
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
#!/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
|
||||
@@ -1,52 +0,0 @@
|
||||
#!/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
|
||||
@@ -33,8 +33,6 @@ aux udev_wait
|
||||
ls -la "${LOOP}"*
|
||||
test -e "${LOOP}p1"
|
||||
|
||||
aux lvmconf 'devices/scan = "/dev"'
|
||||
|
||||
aux extend_filter "a|$LOOP|"
|
||||
aux extend_devices "$LOOP"
|
||||
|
||||
|
||||
@@ -43,9 +43,6 @@ allocation {
|
||||
sequential_threshold = 100
|
||||
random_threshold = 200
|
||||
}
|
||||
writecache {
|
||||
high_watermark = 60
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
@@ -61,14 +58,6 @@ EOF
|
||||
|
||||
aux prepare_vg 2 1000000
|
||||
|
||||
# Check writecache read data from profile
|
||||
if aux have_writecache 1 0 0 ; then
|
||||
lvcreate -n $lv1 -l 4 -an $vg "$dev1"
|
||||
lvcreate -y --type writecache -l 4 --cachevol $lv1 -n $lv2 --metadataprofile $PFILE $vg "$dev2"
|
||||
check lv_field $vg/$lv2 cachesettings "high_watermark=60"
|
||||
lvremove -y $vg
|
||||
fi
|
||||
|
||||
# Check chunk_size is grabbed from configuration
|
||||
lvcreate -L1G --config 'allocation/cache_pool_chunk_size=512' --type cache-pool $vg/cpool
|
||||
check lv_field $vg/cpool chunksize "512.00k"
|
||||
|
||||
@@ -57,11 +57,9 @@ 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"
|
||||
@@ -118,11 +116,9 @@ 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,6 +71,10 @@ 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
|
||||
@@ -215,8 +219,6 @@ 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"
|
||||
@@ -373,7 +375,6 @@ 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")
|
||||
@@ -381,12 +382,8 @@ BDEVMD=$(basename "$mddev")
|
||||
lvcreate -l1 -an -n $lv1 $vg9
|
||||
lvcreate -l1 -an -n $lv2 $vg9
|
||||
|
||||
mdadm --stop "$mddev"
|
||||
_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
|
||||
@@ -404,49 +401,3 @@ mdadm --stop "$mddev"
|
||||
aux udev_wait
|
||||
wipe_all
|
||||
|
||||
# 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
|
||||
_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
|
||||
|
||||
|
||||
@@ -1,202 +0,0 @@
|
||||
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
|
||||
|
||||
# skip rhel5 which doesn't seem to have /dev/mapper/LVMTESTpv1
|
||||
aux driver_at_least 4 15 || skip
|
||||
|
||||
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
|
||||
|
||||
@@ -222,8 +222,6 @@ vgextend $vg "$dev2"
|
||||
lvcreate -n $lv1 -l 8 -an $vg "$dev1"
|
||||
lvcreate -n $lv2 -l 4 -an $vg "$dev2"
|
||||
lvconvert --yes --type writecache --cachevol $lv2 --cachesettings "block_size=4096" $vg/$lv1
|
||||
lvs -o writecacheblocksize $vg/$lv1 |tee out
|
||||
grep 4096 out
|
||||
lvchange -ay $vg/$lv1
|
||||
mkfs.xfs -f "$DM_DEV_DIR/$vg/$lv1" |tee out
|
||||
grep "sectsz=4096" out
|
||||
|
||||
@@ -52,12 +52,6 @@ aux prepare_devs 4
|
||||
vgcreate $vg "$dev1" "$dev2" "$dev3" "$dev4"
|
||||
|
||||
|
||||
# Create writecache without a specified name so it gets automatic name
|
||||
lvcreate -n $lv1 -l 4 -an $vg "$dev1"
|
||||
lvcreate -y --type writecache -l 4 --cachevol $lv1 $vg "$dev2"
|
||||
check lv_exists $vg lvol0
|
||||
lvremove -y $vg
|
||||
|
||||
#
|
||||
# Test pvmove with writecache
|
||||
#
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
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 \
|
||||
|
||||
268
test/unit/activation-generator_t.c
Normal file
268
test/unit/activation-generator_t.c
Normal file
@@ -0,0 +1,268 @@
|
||||
/*
|
||||
* 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,6 +20,7 @@
|
||||
//-----------------------------------------------------------------
|
||||
|
||||
// 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);
|
||||
@@ -36,6 +37,7 @@ 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);
|
||||
|
||||
@@ -190,9 +190,9 @@ command-lines-input.h: $(srcdir)/command-lines.in Makefile
|
||||
$(Q) set -o pipefail && \
|
||||
( cat $(srcdir)/license.inc && \
|
||||
echo "/* Do not edit. This file is generated by the Makefile. */" && \
|
||||
echo -en "static const char _command_input[] =\n\n\"" && \
|
||||
echo -en "const char _command_input[] =\n\n\"" && \
|
||||
$(EGREP) -v '^#|\-\-\-|^$$' $(srcdir)/command-lines.in | $(AWK) 'BEGIN {ORS = "\\n\"\n\""} //' && \
|
||||
echo "\\n\\n\";" \
|
||||
echo "\\n\";" \
|
||||
) > $@
|
||||
|
||||
$(SOURCES:%.c=%.d) $(SOURCES2:%.c=%.d): command-lines-input.h command-count.h cmds.h
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user