mirror of
git://sourceware.org/git/lvm2.git
synced 2025-09-23 17:44:22 +03:00
Compare commits
40 Commits
dev-dct-cm
...
dev-bmr-dm
Author | SHA1 | Date | |
---|---|---|---|
|
2350ea0060 | ||
|
b999d5f501 | ||
|
e0d19feb85 | ||
|
1c00bb5da3 | ||
|
ca427a711a | ||
|
ec93f37b86 | ||
|
836eb122ce | ||
|
4a7f2155c1 | ||
|
2d48317d3a | ||
|
e2fa90bf38 | ||
|
1a2b88516b | ||
|
47c11c7b1c | ||
|
2e0605d6db | ||
|
85dab3963f | ||
|
8c4f3633ac | ||
|
8b95551ade | ||
|
43e3268ada | ||
|
46c23dfb87 | ||
|
bc7a1d70d4 | ||
|
14746a6c00 | ||
|
2e935c0967 | ||
|
e5bef50827 | ||
|
0d945ddbad | ||
|
eca964b554 | ||
|
d80f9a107f | ||
|
04a9cad499 | ||
|
ee754500db | ||
|
f8234d6e5f | ||
|
6a20b22151 | ||
|
15e657f110 | ||
|
d757b2431a | ||
|
a4be2be5a4 | ||
|
0d2a9ebec6 | ||
|
8a93cde75e | ||
|
d90320f4f1 | ||
|
b92a9c3e1a | ||
|
c64f4447d9 | ||
|
74969c9a38 | ||
|
d6a74025df | ||
|
3e9c03cbbc |
@@ -1,5 +1,10 @@
|
||||
Version 2.02.169 -
|
||||
=====================================
|
||||
Support new internal command _dmeventd_thin_command.
|
||||
Introduce new dmeventd/thin_command configurable setting.
|
||||
Use new default units 'r' for displaying sizes.
|
||||
Also unmount mount point on top of MD device if using blkdeactivate -u.
|
||||
Restore check preventing resize of cache type volumes (2.02.158).
|
||||
Add missing udev sync when flushing dirty cache content.
|
||||
vgchange -p accepts only uint32 numbers.
|
||||
Report thin LV date for merged LV when the merge is in progress.
|
||||
|
@@ -1,5 +1,7 @@
|
||||
Version 1.02.138 -
|
||||
=====================================
|
||||
Support configurable command executed from dmeventd thin plugin.
|
||||
Support new R|r human readable units output format.
|
||||
Thin dmeventd plugin reacts faster on lvextend failure path with umount.
|
||||
Add dm_stats_bind_from_fd() to bind a stats handle from a file descriptor.
|
||||
Do not try call callback when reverting activation on error path.
|
||||
|
@@ -665,7 +665,7 @@ global {
|
||||
|
||||
# Configuration option global/units.
|
||||
# Default value for --units argument.
|
||||
units = "h"
|
||||
units = "r"
|
||||
|
||||
# Configuration option global/si_unit_consistency.
|
||||
# Distinguish between powers of 1024 and 1000 bytes.
|
||||
@@ -1156,7 +1156,8 @@ activation {
|
||||
# Configuration option activation/missing_stripe_filler.
|
||||
# Method to fill missing stripes when activating an incomplete LV.
|
||||
# Using 'error' will make inaccessible parts of the device return I/O
|
||||
# errors on access. You can instead use a device path, in which case,
|
||||
# errors on access. Using 'zero' will return success (and zero) on I/O
|
||||
# You can instead use a device path, in which case,
|
||||
# that device will be used in place of missing stripes. Using anything
|
||||
# other than 'error' with mirrored or snapshotted volumes is likely to
|
||||
# result in data corruption.
|
||||
@@ -2048,6 +2049,14 @@ dmeventd {
|
||||
# warning is repeated when 85%, 90% and 95% of the pool is filled.
|
||||
thin_library = "libdevmapper-event-lvm2thin.so"
|
||||
|
||||
# Configuration option dmeventd/thin_command.
|
||||
# The plugin runs command with each 5% increment when thin-pool data volume
|
||||
# or metadata volume gets above 50%.
|
||||
# Command which starts with 'lvm ' prefix is internal lvm command.
|
||||
# You can write your own handler to customise behaviour in more details.
|
||||
# This configuration option has an automatic default value.
|
||||
# thin_command = "lvm lvextend --use-policies"
|
||||
|
||||
# Configuration option dmeventd/executable.
|
||||
# The full path to the dmeventd binary.
|
||||
# This configuration option has an automatic default value.
|
||||
|
@@ -121,6 +121,7 @@ int dmeventd_lvm2_run(const char *cmdline)
|
||||
int dmeventd_lvm2_command(struct dm_pool *mem, char *buffer, size_t size,
|
||||
const char *cmd, const char *device)
|
||||
{
|
||||
static char _internal_prefix[] = "_dmeventd_";
|
||||
char *vg = NULL, *lv = NULL, *layer;
|
||||
int r;
|
||||
|
||||
@@ -135,6 +136,21 @@ int dmeventd_lvm2_command(struct dm_pool *mem, char *buffer, size_t size,
|
||||
(layer = strstr(lv, "_mlog")))
|
||||
*layer = '\0';
|
||||
|
||||
if (!strncmp(cmd, _internal_prefix, sizeof(_internal_prefix) - 1)) {
|
||||
dmeventd_lvm2_lock();
|
||||
/* output of internal command passed via env var */
|
||||
if (!dmeventd_lvm2_run(cmd))
|
||||
cmd = NULL;
|
||||
else if ((cmd = getenv(cmd)))
|
||||
cmd = dm_pool_strdup(mem, cmd); /* copy with lock */
|
||||
dmeventd_lvm2_unlock();
|
||||
|
||||
if (!cmd) {
|
||||
log_error("Unable to find configured command.");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
r = dm_snprintf(buffer, size, "%s %s/%s", cmd, vg, lv);
|
||||
|
||||
dm_pool_free(mem, vg);
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2016 Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2011-2017 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
@@ -40,17 +40,26 @@
|
||||
|
||||
#define UMOUNT_COMMAND "/bin/umount"
|
||||
|
||||
#define MAX_FAILS (10)
|
||||
#define MAX_FAILS (256) /* ~42 mins between cmd call retry with 10s delay */
|
||||
|
||||
#define THIN_DEBUG 0
|
||||
|
||||
struct dso_state {
|
||||
struct dm_pool *mem;
|
||||
int metadata_percent_check;
|
||||
int metadata_percent;
|
||||
int metadata_warn_once;
|
||||
int data_percent_check;
|
||||
int data_percent;
|
||||
int data_warn_once;
|
||||
uint64_t known_metadata_size;
|
||||
uint64_t known_data_size;
|
||||
unsigned fails;
|
||||
unsigned max_fails;
|
||||
int restore_sigset;
|
||||
sigset_t old_sigset;
|
||||
pid_t pid;
|
||||
char **argv;
|
||||
char cmd_str[1024];
|
||||
};
|
||||
|
||||
@@ -58,259 +67,95 @@ DM_EVENT_LOG_FN("thin")
|
||||
|
||||
#define UUID_PREFIX "LVM-"
|
||||
|
||||
/* Figure out device UUID has LVM- prefix and is OPEN */
|
||||
static int _has_unmountable_prefix(int major, int minor)
|
||||
static int _run_command(struct dso_state *state)
|
||||
{
|
||||
struct dm_task *dmt;
|
||||
struct dm_info info;
|
||||
const char *uuid;
|
||||
int r = 0;
|
||||
char val[3][36];
|
||||
char *env[] = { val[0], val[1], val[2], NULL };
|
||||
int i;
|
||||
|
||||
if (!(dmt = dm_task_create(DM_DEVICE_INFO)))
|
||||
return_0;
|
||||
/* Mark for possible lvm2 command we are running from dmeventd
|
||||
* lvm2 will not try to talk back to dmeventd while processing it */
|
||||
(void) dm_snprintf(val[0], sizeof(val[0]), "LVM_RUN_BY_DMEVENTD=1");
|
||||
|
||||
if (!dm_task_set_major_minor(dmt, major, minor, 1))
|
||||
goto_out;
|
||||
if (state->data_percent) {
|
||||
/* Prepare some known data to env vars for easy use */
|
||||
(void) dm_snprintf(val[1], sizeof(val[1]), "DMEVENTD_THIN_POOL_DATA=%d",
|
||||
state->data_percent / DM_PERCENT_1);
|
||||
(void) dm_snprintf(val[2], sizeof(val[2]), "DMEVENTD_THIN_POOL_METADATA=%d",
|
||||
state->metadata_percent / DM_PERCENT_1);
|
||||
} else {
|
||||
/* For an error event it's for a user to check status and decide */
|
||||
env[1] = NULL;
|
||||
log_debug("Error event processing");
|
||||
}
|
||||
|
||||
if (!dm_task_no_flush(dmt))
|
||||
stack;
|
||||
log_verbose("Executing command: %s", state->cmd_str);
|
||||
|
||||
if (!dm_task_run(dmt))
|
||||
goto out;
|
||||
|
||||
if (!dm_task_get_info(dmt, &info))
|
||||
goto out;
|
||||
|
||||
if (!info.exists || !info.open_count)
|
||||
goto out; /* Not open -> not mounted */
|
||||
|
||||
if (!(uuid = dm_task_get_uuid(dmt)))
|
||||
goto out;
|
||||
|
||||
/* Check it's public mountable LV
|
||||
* has prefix LVM- and UUID size is 68 chars */
|
||||
if (memcmp(uuid, UUID_PREFIX, sizeof(UUID_PREFIX) - 1) ||
|
||||
strlen(uuid) != 68)
|
||||
goto out;
|
||||
|
||||
#if THIN_DEBUG
|
||||
log_debug("Found logical volume %s (%u:%u).", uuid, major, minor);
|
||||
#endif
|
||||
r = 1;
|
||||
out:
|
||||
dm_task_destroy(dmt);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Get dependencies for device, and try to find matching device */
|
||||
static int _has_deps(const char *name, int tp_major, int tp_minor, int *dev_minor)
|
||||
{
|
||||
struct dm_task *dmt;
|
||||
const struct dm_deps *deps;
|
||||
struct dm_info info;
|
||||
int major, minor;
|
||||
int r = 0;
|
||||
|
||||
if (!(dmt = dm_task_create(DM_DEVICE_DEPS)))
|
||||
/* TODO:
|
||||
* Support parallel run of 'task' and it's waitpid maintainence
|
||||
* ATM we can't handle signaling of SIGALRM
|
||||
* as signalling is not allowed while 'process_event()' is running
|
||||
*/
|
||||
if (!(state->pid = fork())) {
|
||||
/* child */
|
||||
(void) close(0);
|
||||
for (i = 3; i < 255; ++i) (void) close(i);
|
||||
execve(state->argv[0], state->argv, env);
|
||||
_exit(errno);
|
||||
} else if (state->pid == -1) {
|
||||
log_error("Can't fork command %s.", state->cmd_str);
|
||||
state->fails = 1;
|
||||
return 0;
|
||||
|
||||
if (!dm_task_set_name(dmt, name))
|
||||
goto out;
|
||||
|
||||
if (!dm_task_no_open_count(dmt))
|
||||
goto out;
|
||||
|
||||
if (!dm_task_run(dmt))
|
||||
goto out;
|
||||
|
||||
if (!dm_task_get_info(dmt, &info))
|
||||
goto out;
|
||||
|
||||
if (!(deps = dm_task_get_deps(dmt)))
|
||||
goto out;
|
||||
|
||||
if (!info.exists || deps->count != 1)
|
||||
goto out;
|
||||
|
||||
major = (int) MAJOR(deps->device[0]);
|
||||
minor = (int) MINOR(deps->device[0]);
|
||||
if ((major != tp_major) || (minor != tp_minor))
|
||||
goto out;
|
||||
|
||||
*dev_minor = info.minor;
|
||||
|
||||
if (!_has_unmountable_prefix(major, info.minor))
|
||||
goto out;
|
||||
|
||||
#if THIN_DEBUG
|
||||
{
|
||||
char dev_name[PATH_MAX];
|
||||
if (dm_device_get_name(major, minor, 0, dev_name, sizeof(dev_name)))
|
||||
log_debug("Found %s (%u:%u) depends on %s.",
|
||||
name, major, *dev_minor, dev_name);
|
||||
}
|
||||
#endif
|
||||
r = 1;
|
||||
out:
|
||||
dm_task_destroy(dmt);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Get all active devices */
|
||||
static int _find_all_devs(dm_bitset_t bs, int tp_major, int tp_minor)
|
||||
{
|
||||
struct dm_task *dmt;
|
||||
struct dm_names *names;
|
||||
unsigned next = 0;
|
||||
int minor, r = 1;
|
||||
|
||||
if (!(dmt = dm_task_create(DM_DEVICE_LIST)))
|
||||
return 0;
|
||||
|
||||
if (!dm_task_run(dmt)) {
|
||||
r = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!(names = dm_task_get_names(dmt))) {
|
||||
r = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!names->dev)
|
||||
goto out;
|
||||
|
||||
do {
|
||||
names = (struct dm_names *)((char *) names + next);
|
||||
if (_has_deps(names->name, tp_major, tp_minor, &minor))
|
||||
dm_bit_set(bs, minor);
|
||||
next = names->next;
|
||||
} while (next);
|
||||
|
||||
out:
|
||||
dm_task_destroy(dmt);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int _run(const char *cmd, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int argc = 1; /* for argv[0], i.e. cmd */
|
||||
int i = 0;
|
||||
const char **argv;
|
||||
pid_t pid = fork();
|
||||
int status;
|
||||
|
||||
if (pid == 0) { /* child */
|
||||
va_start(ap, cmd);
|
||||
while (va_arg(ap, const char *))
|
||||
++argc;
|
||||
va_end(ap);
|
||||
|
||||
/* + 1 for the terminating NULL */
|
||||
argv = alloca(sizeof(const char *) * (argc + 1));
|
||||
|
||||
argv[0] = cmd;
|
||||
va_start(ap, cmd);
|
||||
while ((argv[++i] = va_arg(ap, const char *)));
|
||||
va_end(ap);
|
||||
|
||||
execvp(cmd, (char **)argv);
|
||||
log_sys_error("exec", cmd);
|
||||
exit(127);
|
||||
}
|
||||
|
||||
if (pid > 0) { /* parent */
|
||||
if (waitpid(pid, &status, 0) != pid)
|
||||
return 0; /* waitpid failed */
|
||||
if (!WIFEXITED(status) || WEXITSTATUS(status))
|
||||
return 0; /* the child failed */
|
||||
}
|
||||
|
||||
if (pid < 0)
|
||||
return 0; /* fork failed */
|
||||
|
||||
return 1; /* all good */
|
||||
}
|
||||
|
||||
struct mountinfo_s {
|
||||
const char *device;
|
||||
struct dm_info info;
|
||||
dm_bitset_t minors; /* Bitset for active thin pool minors */
|
||||
};
|
||||
|
||||
static int _umount_device(char *buffer, unsigned major, unsigned minor,
|
||||
char *target, void *cb_data)
|
||||
{
|
||||
struct mountinfo_s *data = cb_data;
|
||||
char *words[10];
|
||||
|
||||
if ((major == data->info.major) && dm_bit(data->minors, minor)) {
|
||||
if (dm_split_words(buffer, DM_ARRAY_SIZE(words), 0, words) < DM_ARRAY_SIZE(words))
|
||||
words[9] = NULL; /* just don't show device name */
|
||||
log_info("Unmounting thin %s (%d:%d) of thin pool %s (%u:%u) from mount point \"%s\".",
|
||||
words[9] ? : "", major, minor, data->device,
|
||||
data->info.major, data->info.minor,
|
||||
target);
|
||||
if (!_run(UMOUNT_COMMAND, "-fl", target, NULL))
|
||||
log_error("Failed to lazy umount thin %s (%d:%d) from %s: %s.",
|
||||
words[9], major, minor, target, strerror(errno));
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find all thin pool LV users and try to umount them.
|
||||
* TODO: work with read-only thin pool support
|
||||
*/
|
||||
static void _umount(struct dm_task *dmt)
|
||||
{
|
||||
/* TODO: Convert to use hash to reduce memory usage */
|
||||
static const size_t MINORS = (1U << 20); /* 20 bit */
|
||||
struct mountinfo_s data = { NULL };
|
||||
|
||||
if (!dm_task_get_info(dmt, &data.info))
|
||||
return;
|
||||
|
||||
data.device = dm_task_get_name(dmt);
|
||||
|
||||
if (!(data.minors = dm_bitset_create(NULL, MINORS))) {
|
||||
log_error("Failed to allocate bitset. Not unmounting %s.", data.device);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!_find_all_devs(data.minors, data.info.major, data.info.minor)) {
|
||||
log_error("Failed to detect mounted volumes for %s.", data.device);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!dm_mountinfo_read(_umount_device, &data)) {
|
||||
log_error("Could not parse mountinfo file.");
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
if (data.minors)
|
||||
dm_bitset_destroy(data.minors);
|
||||
}
|
||||
|
||||
static int _use_policy(struct dm_task *dmt, struct dso_state *state)
|
||||
{
|
||||
#if THIN_DEBUG
|
||||
log_debug("dmeventd executes: %s.", state->cmd_str);
|
||||
#endif
|
||||
if (state->argv)
|
||||
return _run_command(state);
|
||||
|
||||
if (!dmeventd_lvm2_run_with_lock(state->cmd_str)) {
|
||||
log_error("Failed to extend thin pool %s.",
|
||||
dm_task_get_name(dmt));
|
||||
state->fails++;
|
||||
log_error("Failed command for %s.", dm_task_get_name(dmt));
|
||||
state->fails = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
state->fails = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Check if executed command has finished
|
||||
* Only 1 command may run */
|
||||
static int _wait_for_pid(struct dso_state *state)
|
||||
{
|
||||
int status = 0;
|
||||
|
||||
if (state->pid == -1)
|
||||
return 1;
|
||||
|
||||
if (!waitpid(state->pid, &status, WNOHANG))
|
||||
return 0;
|
||||
|
||||
/* Wait for finish */
|
||||
if (WIFEXITED(status)) {
|
||||
log_verbose("Child %d exited with status %d.",
|
||||
state->pid, WEXITSTATUS(status));
|
||||
state->fails = WEXITSTATUS(status) ? 1 : 0;
|
||||
} else {
|
||||
if (WIFSIGNALED(status))
|
||||
log_verbose("Child %d was terminated with status %d.",
|
||||
state->pid, WTERMSIG(status));
|
||||
state->fails = 1;
|
||||
}
|
||||
|
||||
state->pid = -1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -319,7 +164,6 @@ void process_event(struct dm_task *dmt,
|
||||
void **user)
|
||||
{
|
||||
const char *device = dm_task_get_name(dmt);
|
||||
int percent;
|
||||
struct dso_state *state = *user;
|
||||
struct dm_status_thin_pool *tps = NULL;
|
||||
void *next = NULL;
|
||||
@@ -327,7 +171,6 @@ void process_event(struct dm_task *dmt,
|
||||
char *target_type = NULL;
|
||||
char *params;
|
||||
int needs_policy = 0;
|
||||
int needs_umount = 0;
|
||||
struct dm_task *new_dmt = NULL;
|
||||
|
||||
#if THIN_DEBUG
|
||||
@@ -335,14 +178,15 @@ void process_event(struct dm_task *dmt,
|
||||
dm_percent_to_float(state->data_percent_check),
|
||||
dm_percent_to_float(state->metadata_percent_check));
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
/* No longer monitoring, waiting for remove */
|
||||
if (!state->meta_percent_check && !state->data_percent_check)
|
||||
if (!_wait_for_pid(state)) {
|
||||
log_warn("WARNING: Skipping event, child %d is still running (%s).",
|
||||
state->pid, state->cmd_str);
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (event & DM_EVENT_DEVICE_ERROR) {
|
||||
/* Error -> no need to check and do instant resize */
|
||||
state->data_percent = state->metadata_percent = 0;
|
||||
if (_use_policy(dmt, state))
|
||||
goto out;
|
||||
|
||||
@@ -380,7 +224,6 @@ void process_event(struct dm_task *dmt,
|
||||
|
||||
if (!dm_get_status_thin_pool(state->mem, params, &tps)) {
|
||||
log_error("Failed to parse status.");
|
||||
needs_umount = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -395,72 +238,114 @@ void process_event(struct dm_task *dmt,
|
||||
if (state->known_metadata_size != tps->total_metadata_blocks) {
|
||||
state->metadata_percent_check = CHECK_MINIMUM;
|
||||
state->known_metadata_size = tps->total_metadata_blocks;
|
||||
state->fails = 0;
|
||||
}
|
||||
|
||||
if (state->known_data_size != tps->total_data_blocks) {
|
||||
state->data_percent_check = CHECK_MINIMUM;
|
||||
state->known_data_size = tps->total_data_blocks;
|
||||
state->fails = 0;
|
||||
}
|
||||
|
||||
percent = dm_make_percent(tps->used_metadata_blocks, tps->total_metadata_blocks);
|
||||
if (percent >= state->metadata_percent_check) {
|
||||
/*
|
||||
* Usage has raised more than CHECK_STEP since the last
|
||||
* time. Run actions.
|
||||
*/
|
||||
state->metadata_percent_check = (percent / CHECK_STEP) * CHECK_STEP + CHECK_STEP;
|
||||
/*
|
||||
* Trigger action when threshold boundary is exceeded.
|
||||
* Report 80% threshold warning when it's used above 80%.
|
||||
* Only 100% is exception as it cannot be surpased so policy
|
||||
* action is called for: >50%, >55% ... >95%, 100%
|
||||
*/
|
||||
state->metadata_percent = dm_make_percent(tps->used_metadata_blocks, tps->total_metadata_blocks);
|
||||
if (state->metadata_percent <= WARNING_THRESH)
|
||||
state->metadata_warn_once = 0; /* Dropped bellow threshold, reset warn once */
|
||||
else if (!state->metadata_warn_once++) /* Warn once when raised above threshold */
|
||||
log_warn("WARNING: Thin pool %s metadata is now %.2f%% full.",
|
||||
device, dm_percent_to_float(state->metadata_percent));
|
||||
if (state->metadata_percent > CHECK_MINIMUM) {
|
||||
/* Run action when usage raised more than CHECK_STEP since the last time */
|
||||
if (state->metadata_percent > state->metadata_percent_check)
|
||||
needs_policy = 1;
|
||||
state->metadata_percent_check = (state->metadata_percent / CHECK_STEP + 1) * CHECK_STEP;
|
||||
if (state->metadata_percent_check == DM_PERCENT_100)
|
||||
state->metadata_percent_check--; /* Can't get bigger then 100% */
|
||||
} else
|
||||
state->metadata_percent_check = CHECK_MINIMUM;
|
||||
|
||||
/* FIXME: extension of metadata needs to be written! */
|
||||
if (percent >= WARNING_THRESH) /* Print a warning to syslog. */
|
||||
log_warn("WARNING: Thin pool %s metadata is now %.2f%% full.",
|
||||
device, dm_percent_to_float(percent));
|
||||
needs_policy = 1;
|
||||
state->data_percent = dm_make_percent(tps->used_data_blocks, tps->total_data_blocks);
|
||||
if (state->data_percent <= WARNING_THRESH)
|
||||
state->data_warn_once = 0;
|
||||
else if (!state->data_warn_once++)
|
||||
log_warn("WARNING: Thin pool %s data is now %.2f%% full.",
|
||||
device, dm_percent_to_float(state->data_percent));
|
||||
if (state->data_percent > CHECK_MINIMUM) {
|
||||
/* Run action when usage raised more than CHECK_STEP since the last time */
|
||||
if (state->data_percent > state->data_percent_check)
|
||||
needs_policy = 1;
|
||||
state->data_percent_check = (state->data_percent / CHECK_STEP + 1) * CHECK_STEP;
|
||||
if (state->data_percent_check == DM_PERCENT_100)
|
||||
state->data_percent_check--; /* Can't get bigger then 100% */
|
||||
} else
|
||||
state->data_percent_check = CHECK_MINIMUM;
|
||||
|
||||
if (percent >= UMOUNT_THRESH)
|
||||
needs_umount = 1;
|
||||
}
|
||||
/* Reduce number of _use_policy() calls by power-of-2 factor till frequency of MAX_FAILS is reached.
|
||||
* Avoids too high number of error retries, yet shows some status messages in log regularly.
|
||||
* i.e. PV could have been pvmoved and VG/LV was locked for a while...
|
||||
*/
|
||||
if (state->fails) {
|
||||
if (state->fails++ <= state->max_fails) {
|
||||
log_debug("Postponing frequently failing policy (%u <= %u).",
|
||||
state->fails - 1, state->max_fails);
|
||||
return;
|
||||
}
|
||||
if (state->max_fails < MAX_FAILS)
|
||||
state->max_fails <<= 1;
|
||||
state->fails = needs_policy = 1; /* Retry failing command */
|
||||
} else
|
||||
state->max_fails = 1; /* Reset on success */
|
||||
|
||||
percent = dm_make_percent(tps->used_data_blocks, tps->total_data_blocks);
|
||||
if (percent >= state->data_percent_check) {
|
||||
/*
|
||||
* Usage has raised more than CHECK_STEP since
|
||||
* the last time. Run actions.
|
||||
*/
|
||||
state->data_percent_check = (percent / CHECK_STEP) * CHECK_STEP + CHECK_STEP;
|
||||
|
||||
if (percent >= WARNING_THRESH) /* Print a warning to syslog. */
|
||||
log_warn("WARNING: Thin pool %s data is now %.2f%% full.",
|
||||
device, dm_percent_to_float(percent));
|
||||
needs_policy = 1;
|
||||
|
||||
if (percent >= UMOUNT_THRESH)
|
||||
needs_umount = 1;
|
||||
}
|
||||
|
||||
if (needs_policy &&
|
||||
_use_policy(dmt, state))
|
||||
needs_umount = 0; /* No umount when command was successful */
|
||||
if (needs_policy)
|
||||
_use_policy(dmt, state);
|
||||
out:
|
||||
if (needs_umount) {
|
||||
_umount(dmt);
|
||||
/* Until something changes, do not retry any more actions */
|
||||
state->data_percent_check = state->metadata_percent_check = (DM_PERCENT_1 * 101);
|
||||
}
|
||||
|
||||
if (tps)
|
||||
dm_pool_free(state->mem, tps);
|
||||
|
||||
if (state->fails >= MAX_FAILS) {
|
||||
log_warn("WARNING: Dropping monitoring of %s. "
|
||||
"lvm2 command fails too often (%u times in row).",
|
||||
device, state->fails);
|
||||
pthread_kill(pthread_self(), SIGALRM);
|
||||
}
|
||||
|
||||
if (new_dmt)
|
||||
dm_task_destroy(new_dmt);
|
||||
}
|
||||
|
||||
/* Handle SIGCHLD for a thread */
|
||||
static void _sig_child(int signum __attribute__((unused)))
|
||||
{
|
||||
/* empty SIG_IGN */;
|
||||
}
|
||||
|
||||
/* Setup handler for SIGCHLD when executing external command
|
||||
* to get quick 'waitpid()' reaction
|
||||
* It will interrupt syscall just like SIGALRM and
|
||||
* invoke process_event().
|
||||
*/
|
||||
static void _init_thread_signals(struct dso_state *state)
|
||||
{
|
||||
struct sigaction act = { .sa_handler = _sig_child };
|
||||
sigset_t my_sigset;
|
||||
|
||||
sigemptyset(&my_sigset);
|
||||
|
||||
if (sigaction(SIGCHLD, &act, NULL))
|
||||
log_warn("WARNING: Failed to set SIGCHLD action.");
|
||||
else if (sigaddset(&my_sigset, SIGCHLD))
|
||||
log_warn("WARNING: Failed to add SIGCHLD to set.");
|
||||
else if (pthread_sigmask(SIG_UNBLOCK, &my_sigset, &state->old_sigset))
|
||||
log_warn("WARNING: Failed to unblock SIGCHLD.");
|
||||
else
|
||||
state->restore_sigset = 1;
|
||||
}
|
||||
|
||||
static void _restore_thread_signals(struct dso_state *state)
|
||||
{
|
||||
if (state->restore_sigset &&
|
||||
pthread_sigmask(SIG_SETMASK, &state->old_sigset, NULL))
|
||||
log_warn("WARNING: Failed to block SIGCHLD.");
|
||||
}
|
||||
|
||||
int register_device(const char *device,
|
||||
const char *uuid __attribute__((unused)),
|
||||
int major __attribute__((unused)),
|
||||
@@ -468,20 +353,36 @@ int register_device(const char *device,
|
||||
void **user)
|
||||
{
|
||||
struct dso_state *state;
|
||||
int maxcmd;
|
||||
char *str;
|
||||
|
||||
if (!dmeventd_lvm2_init_with_pool("thin_pool_state", state))
|
||||
goto_bad;
|
||||
|
||||
if (!dmeventd_lvm2_command(state->mem, state->cmd_str,
|
||||
sizeof(state->cmd_str),
|
||||
"lvextend --use-policies",
|
||||
device)) {
|
||||
"_dmeventd_thin_command", device)) {
|
||||
dmeventd_lvm2_exit_with_pool(state);
|
||||
goto_bad;
|
||||
}
|
||||
|
||||
state->metadata_percent_check = CHECK_MINIMUM;
|
||||
state->data_percent_check = CHECK_MINIMUM;
|
||||
if (strncmp(state->cmd_str, "lvm ", 4)) {
|
||||
maxcmd = 2; /* space for last NULL element */
|
||||
for (str = state->cmd_str; *str; str++)
|
||||
if (*str == ' ')
|
||||
maxcmd++;
|
||||
if (!(str = dm_pool_strdup(state->mem, state->cmd_str)) ||
|
||||
!(state->argv = dm_pool_zalloc(state->mem, maxcmd * sizeof(char *)))) {
|
||||
log_error("Failed to allocate memory for command.");
|
||||
goto bad;
|
||||
}
|
||||
|
||||
dm_split_words(str, maxcmd - 1, 0, state->argv);
|
||||
_init_thread_signals(state);
|
||||
} else
|
||||
memmove(state->cmd_str, state->cmd_str + 4, strlen(state->cmd_str + 4) + 1);
|
||||
|
||||
state->pid = -1;
|
||||
*user = state;
|
||||
|
||||
log_info("Monitoring thin pool %s.", device);
|
||||
@@ -500,6 +401,28 @@ int unregister_device(const char *device,
|
||||
void **user)
|
||||
{
|
||||
struct dso_state *state = *user;
|
||||
int i;
|
||||
|
||||
for (i = 0; !_wait_for_pid(state) && (i < 6); ++i) {
|
||||
if (i == 0)
|
||||
/* Give it 2 seconds, then try to terminate & kill it */
|
||||
log_verbose("Child %d still not finished (%s) waiting.",
|
||||
state->pid, state->cmd_str);
|
||||
else if (i == 3) {
|
||||
log_warn("WARNING: Terminating child %d.", state->pid);
|
||||
kill(state->pid, SIGINT);
|
||||
kill(state->pid, SIGTERM);
|
||||
} else if (i == 5) {
|
||||
log_warn("WARNING: Killing child %d.", state->pid);
|
||||
kill(state->pid, SIGKILL);
|
||||
}
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
if (state->pid != -1)
|
||||
log_warn("WARNING: Cannot kill child %d!", state->pid);
|
||||
|
||||
_restore_thread_signals(state);
|
||||
|
||||
dmeventd_lvm2_exit_with_pool(state);
|
||||
log_info("No longer monitoring thin pool %s.", device);
|
||||
|
@@ -192,6 +192,9 @@ static int _get_env_vars(struct cmd_context *cmd)
|
||||
}
|
||||
}
|
||||
|
||||
if (strcmp((getenv("LVM_RUN_BY_DMEVENTD") ? : "0"), "1") == 0)
|
||||
init_run_by_dmeventd(cmd);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1755,6 +1758,15 @@ bad:
|
||||
return 0;
|
||||
}
|
||||
|
||||
int init_run_by_dmeventd(struct cmd_context *cmd)
|
||||
{
|
||||
init_dmeventd_monitor(DMEVENTD_MONITOR_IGNORE);
|
||||
init_ignore_suspended_devices(1);
|
||||
init_disable_dmeventd_monitoring(1); /* Lock settings */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void destroy_config_context(struct cmd_context *cmd)
|
||||
{
|
||||
_destroy_config(cmd);
|
||||
|
@@ -88,19 +88,11 @@ struct cmd_context {
|
||||
* Command line and arguments.
|
||||
*/
|
||||
const char *cmd_line;
|
||||
const char *name; /* needed before cmd->command is set */
|
||||
struct command *command;
|
||||
char **argv;
|
||||
struct arg_values *opt_arg_values;
|
||||
struct arg_values *arg_values;
|
||||
struct dm_list arg_value_groups;
|
||||
|
||||
/*
|
||||
* Position args remaining after command name
|
||||
* and --options are removed from original argc/argv.
|
||||
*/
|
||||
int position_argc;
|
||||
char **position_argv;
|
||||
|
||||
/*
|
||||
* Format handlers.
|
||||
*/
|
||||
@@ -241,6 +233,7 @@ int config_files_changed(struct cmd_context *cmd);
|
||||
int init_lvmcache_orphans(struct cmd_context *cmd);
|
||||
int init_filters(struct cmd_context *cmd, unsigned load_persistent_cache);
|
||||
int init_connections(struct cmd_context *cmd);
|
||||
int init_run_by_dmeventd(struct cmd_context *cmd);
|
||||
|
||||
/*
|
||||
* A config context is a very light weight cmd struct that
|
||||
|
@@ -1859,6 +1859,12 @@ cfg(dmeventd_thin_library_CFG, "thin_library", dmeventd_CFG_SECTION, 0, CFG_TYPE
|
||||
"and emits a warning through syslog when the usage exceeds 80%. The\n"
|
||||
"warning is repeated when 85%, 90% and 95% of the pool is filled.\n")
|
||||
|
||||
cfg(dmeventd_thin_command_CFG, "thin_command", dmeventd_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_STRING, DEFAULT_DMEVENTD_THIN_COMMAND, vsn(2, 2, 169), NULL, 0, NULL,
|
||||
"The plugin runs command with each 5% increment when thin-pool data volume\n"
|
||||
"or metadata volume gets above 50%.\n"
|
||||
"Command which starts with 'lvm ' prefix is internal lvm command.\n"
|
||||
"You can write your own handler to customise behaviour in more details.\n")
|
||||
|
||||
cfg(dmeventd_executable_CFG, "executable", dmeventd_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_STRING, DEFAULT_DMEVENTD_PATH, vsn(2, 2, 73), "@DMEVENTD_PATH@", 0, NULL,
|
||||
"The full path to the dmeventd binary.\n")
|
||||
|
||||
|
@@ -81,6 +81,7 @@
|
||||
#define DEFAULT_DMEVENTD_MIRROR_LIB "libdevmapper-event-lvm2mirror.so"
|
||||
#define DEFAULT_DMEVENTD_SNAPSHOT_LIB "libdevmapper-event-lvm2snapshot.so"
|
||||
#define DEFAULT_DMEVENTD_THIN_LIB "libdevmapper-event-lvm2thin.so"
|
||||
#define DEFAULT_DMEVENTD_THIN_COMMAND "lvm lvextend --use-policies"
|
||||
#define DEFAULT_DMEVENTD_MONITOR 1
|
||||
#define DEFAULT_BACKGROUND_POLLING 1
|
||||
|
||||
@@ -176,7 +177,7 @@
|
||||
#define DEFAULT_INDENT 1
|
||||
#define DEFAULT_ABORT_ON_INTERNAL_ERRORS 0
|
||||
#define DEFAULT_DETECT_INTERNAL_VG_CACHE_CORRUPTION 0
|
||||
#define DEFAULT_UNITS "h"
|
||||
#define DEFAULT_UNITS "r"
|
||||
#define DEFAULT_SUFFIX 1
|
||||
#define DEFAULT_HOSTTAGS 0
|
||||
|
||||
|
@@ -125,6 +125,10 @@ struct dev_types *create_dev_types(const char *proc_dir,
|
||||
if (!strncmp("emcpower", line + i, 8) && isspace(*(line + i + 8)))
|
||||
dt->emcpower_major = line_maj;
|
||||
|
||||
/* Look for Veritas Dynamic Multipathing */
|
||||
if (!strncmp("VxDMP", line + i, 5) && isspace(*(line + i + 5)))
|
||||
dt->vxdmp_major = line_maj;
|
||||
|
||||
if (!strncmp("loop", line + i, 4) && isspace(*(line + i + 4)))
|
||||
dt->loop_major = line_maj;
|
||||
|
||||
@@ -218,6 +222,9 @@ int dev_subsystem_part_major(struct dev_types *dt, struct device *dev)
|
||||
if (MAJOR(dev->dev) == dt->power2_major)
|
||||
return 1;
|
||||
|
||||
if (MAJOR(dev->dev) == dt->vxdmp_major)
|
||||
return 1;
|
||||
|
||||
if ((MAJOR(dev->dev) == dt->blkext_major) &&
|
||||
dev_get_primary_dev(dt, dev, &primary_dev) &&
|
||||
(MAJOR(primary_dev) == dt->md_major))
|
||||
@@ -246,6 +253,9 @@ const char *dev_subsystem_name(struct dev_types *dt, struct device *dev)
|
||||
if (MAJOR(dev->dev) == dt->power2_major)
|
||||
return "POWER2";
|
||||
|
||||
if (MAJOR(dev->dev) == dt->vxdmp_major)
|
||||
return "VXDMP";
|
||||
|
||||
if (MAJOR(dev->dev) == dt->blkext_major)
|
||||
return "BLKEXT";
|
||||
|
||||
|
@@ -41,6 +41,7 @@ struct dev_types {
|
||||
int drbd_major;
|
||||
int device_mapper_major;
|
||||
int emcpower_major;
|
||||
int vxdmp_major;
|
||||
int power2_major;
|
||||
int dasd_major;
|
||||
int loop_major;
|
||||
|
@@ -63,5 +63,6 @@ static const dev_known_type_t _dev_known_types[] = {
|
||||
{"bcache", 1, "bcache block device cache"},
|
||||
{"nvme", 64, "NVM Express"},
|
||||
{"zvol", 16, "ZFS Zvols"},
|
||||
{"VxDMP", 16, "Veritas Dynamic Multipathing"},
|
||||
{"", 0, ""}
|
||||
};
|
||||
|
@@ -319,6 +319,7 @@ static int _lv_layout_and_role_thin(struct dm_pool *mem,
|
||||
{
|
||||
int top_level = 0;
|
||||
unsigned snap_count;
|
||||
struct lv_segment *seg;
|
||||
|
||||
/* non-top-level LVs */
|
||||
if (lv_is_thin_pool_metadata(lv)) {
|
||||
@@ -352,7 +353,7 @@ static int _lv_layout_and_role_thin(struct dm_pool *mem,
|
||||
!str_list_add_no_dup_check(mem, role, _lv_type_names[LV_TYPE_MULTITHINORIGIN]))
|
||||
goto_bad;
|
||||
}
|
||||
if (lv_is_thin_snapshot(lv))
|
||||
if ((seg = first_seg(lv)) && (seg->origin || seg->external_lv))
|
||||
if (!str_list_add(mem, role, _lv_type_names[LV_TYPE_SNAPSHOT]) ||
|
||||
!str_list_add_no_dup_check(mem, role, _lv_type_names[LV_TYPE_THINSNAPSHOT]))
|
||||
goto_bad;
|
||||
@@ -4686,6 +4687,11 @@ static int _lvresize_check(struct logical_volume *lv,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (lv_is_cache_type(lv)) {
|
||||
log_error("Unable to resize logical volumes of cache type.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!lv_is_visible(lv) &&
|
||||
!lv_is_thin_pool_metadata(lv) &&
|
||||
!lv_is_lockd_sanlock_lv(lv)) {
|
||||
@@ -6527,12 +6533,43 @@ int remove_layer_from_lv(struct logical_volume *lv,
|
||||
* Before removal, the layer should be cleaned up,
|
||||
* i.e. additional segments and areas should have been removed.
|
||||
*/
|
||||
if (dm_list_size(&parent_lv->segments) != 1 ||
|
||||
parent_seg->area_count != 1 ||
|
||||
seg_type(parent_seg, 0) != AREA_LV ||
|
||||
layer_lv != seg_lv(parent_seg, 0) ||
|
||||
parent_lv->le_count != layer_lv->le_count)
|
||||
return_0;
|
||||
/* FIXME:
|
||||
* These are all INTERNAL_ERROR, but ATM there is
|
||||
* some internal API problem and this code is wrongle
|
||||
* executed with certain mirror manipulations.
|
||||
* So we need to fix mirror code first, then switch...
|
||||
*/
|
||||
if (dm_list_size(&parent_lv->segments) != 1) {
|
||||
log_error("Invalid %d segments in %s, expected only 1.",
|
||||
dm_list_size(&parent_lv->segments),
|
||||
display_lvname(parent_lv));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (parent_seg->area_count != 1) {
|
||||
log_error("Invalid %d area count(s) in %s, expected only 1.",
|
||||
parent_seg->area_count, display_lvname(parent_lv));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (seg_type(parent_seg, 0) != AREA_LV) {
|
||||
log_error("Invalid seg_type %d in %s, expected LV.",
|
||||
seg_type(parent_seg, 0), display_lvname(parent_lv));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (layer_lv != seg_lv(parent_seg, 0)) {
|
||||
log_error("Layer doesn't match segment in %s.",
|
||||
display_lvname(parent_lv));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (parent_lv->le_count != layer_lv->le_count) {
|
||||
log_error("Inconsistent extent count (%u != %u) of layer %s.",
|
||||
parent_lv->le_count, layer_lv->le_count,
|
||||
display_lvname(parent_lv));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!lv_empty(parent_lv))
|
||||
return_0;
|
||||
|
@@ -1067,16 +1067,9 @@ struct lv_segment *get_only_segment_using_this_lv(const struct logical_volume *l
|
||||
* Useful functions for managing snapshots.
|
||||
*/
|
||||
int lv_is_origin(const struct logical_volume *lv);
|
||||
#define lv_is_thick_origin lv_is_origin
|
||||
|
||||
int lv_is_thin_origin(const struct logical_volume *lv, unsigned *snap_count);
|
||||
int lv_is_thin_snapshot(const struct logical_volume *lv);
|
||||
|
||||
int lv_is_cow(const struct logical_volume *lv);
|
||||
#define lv_is_thick_snapshot lv_is_cow
|
||||
|
||||
int lv_is_cache_origin(const struct logical_volume *lv);
|
||||
|
||||
int lv_is_cow(const struct logical_volume *lv);
|
||||
int lv_is_merging_cow(const struct logical_volume *cow);
|
||||
uint32_t cow_max_extents(const struct logical_volume *origin, uint32_t chunk_size);
|
||||
int cow_has_min_chunks(const struct volume_group *vg, uint32_t cow_extents, uint32_t chunk_size);
|
||||
|
@@ -5629,11 +5629,6 @@ static int _access_vg_lock_type(struct cmd_context *cmd, struct volume_group *vg
|
||||
}
|
||||
}
|
||||
|
||||
if (test_mode()) {
|
||||
log_error("Test mode is not yet supported with lock type %s.", vg->lock_type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@@ -3952,9 +3952,6 @@ static int _lv_raid_rebuild_or_replace(struct logical_volume *lv,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!archive(lv->vg))
|
||||
return_0;
|
||||
|
||||
/*
|
||||
* How many sub-LVs are being removed?
|
||||
*/
|
||||
@@ -3972,6 +3969,9 @@ static int _lv_raid_rebuild_or_replace(struct logical_volume *lv,
|
||||
lv_is_on_pvs(seg_metalv(raid_seg, s), remove_pvs)) {
|
||||
match_count++;
|
||||
if (rebuild) {
|
||||
if ((match_count == 1) &&
|
||||
!archive(lv->vg))
|
||||
return_0;
|
||||
seg_lv(raid_seg, s)->status |= LV_REBUILD;
|
||||
seg_metalv(raid_seg, s)->status |= LV_REBUILD;
|
||||
}
|
||||
@@ -4017,6 +4017,9 @@ static int _lv_raid_rebuild_or_replace(struct logical_volume *lv,
|
||||
if (rebuild)
|
||||
goto skip_alloc;
|
||||
|
||||
if (!archive(lv->vg))
|
||||
return_0;
|
||||
|
||||
/* Prevent any PVs holding image components from being used for allocation */
|
||||
if (!_avoid_pvs_with_other_images_of_lv(lv, allocate_pvs)) {
|
||||
log_error("Failed to prevent PVs holding image components "
|
||||
|
@@ -752,19 +752,6 @@ int lv_is_thin_origin(const struct logical_volume *lv, unsigned int *snap_count)
|
||||
return r;
|
||||
}
|
||||
|
||||
int lv_is_thin_snapshot(const struct logical_volume *lv)
|
||||
{
|
||||
struct lv_segment *seg;
|
||||
|
||||
if (!lv_is_thin_volume(lv))
|
||||
return 0;
|
||||
|
||||
if ((seg = first_seg(lv)) && (seg->origin || seg->external_lv))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Explict check of new thin pool for usability
|
||||
*
|
||||
|
@@ -1,4 +1,5 @@
|
||||
dm_bit_get_last
|
||||
dm_bit_get_prev
|
||||
dm_stats_update_regions_from_fd
|
||||
dm_bitset_parse_list
|
||||
dm_stats_bind_from_fd
|
||||
|
@@ -1320,8 +1320,9 @@ int dm_stats_get_group_descriptor(const struct dm_stats *dms,
|
||||
* On success the function returns a pointer to an array of uint64_t
|
||||
* containing the IDs of the newly created regions. The region_id
|
||||
* array is terminated by the value DM_STATS_REGION_NOT_PRESENT and
|
||||
* should be freed using dm_free() when no longer required. On error
|
||||
* NULL is returned.
|
||||
* should be freed using dm_free() when no longer required.
|
||||
*
|
||||
* On error NULL is returned.
|
||||
*
|
||||
* Following a call to dm_stats_create_regions_from_fd() the handle
|
||||
* is guaranteed to be in a listed state, and to contain any region
|
||||
@@ -1329,12 +1330,43 @@ int dm_stats_get_group_descriptor(const struct dm_stats *dms,
|
||||
*
|
||||
* The group_id for the new group is equal to the region_id value in
|
||||
* the first array element.
|
||||
*
|
||||
*/
|
||||
uint64_t *dm_stats_create_regions_from_fd(struct dm_stats *dms, int fd,
|
||||
int group, int precise,
|
||||
struct dm_histogram *bounds,
|
||||
const char *alias);
|
||||
/*
|
||||
* Update a group of regions that correspond to the extents of a file
|
||||
* in the filesystem, adding and removing regions to account for
|
||||
* allocation changes in the underlying file.
|
||||
*
|
||||
* File descriptor fd must reference a regular file, open for reading,
|
||||
* in a local file system that supports the FIEMAP ioctl, and that
|
||||
* returns data describing the physical location of extents.
|
||||
*
|
||||
* The file descriptor can be closed by the caller following the call
|
||||
* to dm_stats_update_regions_from_fd().
|
||||
*
|
||||
* On success the function returns a pointer to an array of uint64_t
|
||||
* containing the IDs of the updated regions (including any existing
|
||||
* regions that were not modified by the call).
|
||||
*
|
||||
* The region_id array is terminated by the special value
|
||||
* DM_STATS_REGION_NOT_PRESENT and should be freed using dm_free()
|
||||
* when no longer required.
|
||||
*
|
||||
* On error NULL is returned.
|
||||
*
|
||||
* Following a call to dm_stats_update_regions_from_fd() the handle
|
||||
* is guaranteed to be in a listed state, and to contain any region
|
||||
* and group identifiers created by the operation.
|
||||
*
|
||||
* This function cannot be used with file mapped regions that are
|
||||
* not members of a group: either group the regions, or remove them
|
||||
* and re-map them with dm_stats_create_regions_from_fd().
|
||||
*/
|
||||
uint64_t *dm_stats_update_regions_from_fd(struct dm_stats *dms, int fd,
|
||||
uint64_t group_id);
|
||||
|
||||
/*
|
||||
* Call this to actually run the ioctl.
|
||||
|
@@ -261,6 +261,9 @@ static int _stats_group_id_present(const struct dm_stats *dms, uint64_t id)
|
||||
{
|
||||
struct dm_stats_group *group = NULL;
|
||||
|
||||
if (id == DM_STATS_GROUP_NOT_PRESENT)
|
||||
return 0;
|
||||
|
||||
if (!dms || !dms->regions)
|
||||
return_0;
|
||||
|
||||
@@ -3865,6 +3868,37 @@ static int _extent_start_compare(const void *p1, const void *p2)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Resize the group bitmap corresponding to group_id so that it can
|
||||
* contain at least num_regions members.
|
||||
*/
|
||||
static int _stats_resize_group(struct dm_stats_group *group, int num_regions)
|
||||
{
|
||||
int last_bit = dm_bit_get_last(group->regions);
|
||||
dm_bitset_t new, old;
|
||||
|
||||
if (last_bit >= num_regions) {
|
||||
log_error("Cannot resize group bitmap to %d with bit %d set.",
|
||||
num_regions, last_bit);
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_very_verbose("Resizing group bitmap from %d to %d (last_bit: %d).",
|
||||
group->regions[0], num_regions, last_bit);
|
||||
|
||||
new = dm_bitset_create(NULL, num_regions);
|
||||
if (!new) {
|
||||
log_error("Could not allocate memory for new group bitmap.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
old = group->regions;
|
||||
dm_bit_copy(new, old);
|
||||
group->regions = new;
|
||||
dm_bitset_destroy(old);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _stats_create_group(struct dm_stats *dms, dm_bitset_t regions,
|
||||
const char *alias, uint64_t *group_id)
|
||||
{
|
||||
@@ -4218,8 +4252,8 @@ bad:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _stats_add_extent(struct dm_pool *mem, struct fiemap_extent *fm_ext,
|
||||
uint64_t id)
|
||||
static int _stats_add_file_extent(int fd, struct dm_pool *mem, uint64_t id,
|
||||
struct fiemap_extent *fm_ext)
|
||||
{
|
||||
struct _extent extent;
|
||||
|
||||
@@ -4229,16 +4263,17 @@ static int _stats_add_extent(struct dm_pool *mem, struct fiemap_extent *fm_ext,
|
||||
/* convert bytes to dm (512b) sectors */
|
||||
extent.start = fm_ext->fe_physical >> SECTOR_SHIFT;
|
||||
extent.len = fm_ext->fe_length >> SECTOR_SHIFT;
|
||||
|
||||
extent.id = id;
|
||||
|
||||
log_very_verbose("Extent " FMTu64 " on fd %d at " FMTu64 "+"
|
||||
FMTu64, extent.id, fd, extent.start, extent.len);
|
||||
|
||||
if (!dm_pool_grow_object(mem, &extent,
|
||||
sizeof(extent))) {
|
||||
log_error("Cannot map file: failed to grow extent map.");
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
/* test for the boundary of an extent */
|
||||
@@ -4255,7 +4290,7 @@ do { \
|
||||
*(to) = *(from); \
|
||||
} while (0)
|
||||
|
||||
static uint64_t _stats_map_extents(struct dm_pool *mem,
|
||||
static uint64_t _stats_map_extents(int fd, struct dm_pool *mem,
|
||||
struct fiemap *fiemap,
|
||||
struct fiemap_extent *fm_ext,
|
||||
struct fiemap_extent *fm_last,
|
||||
@@ -4286,16 +4321,34 @@ static uint64_t _stats_map_extents(struct dm_pool *mem,
|
||||
& (FIEMAP_EXTENT_UNKNOWN | FIEMAP_EXTENT_DELALLOC))
|
||||
continue;
|
||||
|
||||
if (ext_boundary(fm_ext[i], expected)) {
|
||||
if (!_stats_add_extent(mem, fm_pending, nr_extents))
|
||||
/*
|
||||
* Begin a new extent if the current physical address differs
|
||||
* from the expected address yielded by fm_last.fe_physical +
|
||||
* fm_last.fe_length.
|
||||
*
|
||||
* A logical discontinuity is seen at the start of the file if
|
||||
* unwritten space exists before the first extent: do not add
|
||||
* any extent record until we have accumulated a non-zero length
|
||||
* in fm_pending.
|
||||
*/
|
||||
if (fm_pending->fe_length &&
|
||||
ext_boundary(fm_ext[i], expected)) {
|
||||
if (!_stats_add_file_extent(fd, mem, nr_extents,
|
||||
fm_pending))
|
||||
goto_bad;
|
||||
nr_extents++;
|
||||
/* Begin a new pending extent. */
|
||||
ext_copy(fm_pending, fm_ext + i);
|
||||
} else {
|
||||
expected = 0;
|
||||
/* Begin a new pending extent for extent 0. */
|
||||
if (fm_ext[i].fe_logical == 0)
|
||||
/* Begin a new pending extent for extent 0. If there is
|
||||
* a hole at the start of the file, the first allocated
|
||||
* extent will have a non-zero fe_logical. Detect this
|
||||
* case by testing fm_pending->fe_length: if no length
|
||||
* has been accumulated we are handling the first
|
||||
* physical extent of the file.
|
||||
*/
|
||||
if (!fm_pending->fe_length || fm_ext[i].fe_logical == 0)
|
||||
ext_copy(fm_pending, fm_ext + i);
|
||||
else
|
||||
/* accumulate this logical extent's length */
|
||||
@@ -4308,8 +4361,8 @@ static uint64_t _stats_map_extents(struct dm_pool *mem,
|
||||
* If the file only has a single extent, no boundary is ever
|
||||
* detected to trigger addition of the first extent.
|
||||
*/
|
||||
if (fm_ext[i - 1].fe_logical == 0) {
|
||||
_stats_add_extent(mem, fm_pending, nr_extents);
|
||||
if (*eof || (fm_ext[i - 1].fe_logical == 0)) {
|
||||
_stats_add_file_extent(fd, mem, nr_extents, fm_pending);
|
||||
nr_extents++;
|
||||
}
|
||||
|
||||
@@ -4341,7 +4394,6 @@ static struct _extent *_stats_get_extents_for_file(struct dm_pool *mem, int fd,
|
||||
struct _extent *extents;
|
||||
unsigned long flags = 0;
|
||||
uint64_t *buf;
|
||||
int rc;
|
||||
|
||||
buf = dm_zalloc(STATS_FIE_BUF_LEN);
|
||||
if (!buf) {
|
||||
@@ -4361,7 +4413,7 @@ static struct _extent *_stats_get_extents_for_file(struct dm_pool *mem, int fd,
|
||||
if (!dm_pool_begin_object(mem, sizeof(*extents)))
|
||||
return NULL;
|
||||
|
||||
flags |= FIEMAP_FLAG_SYNC;
|
||||
flags = FIEMAP_FLAG_SYNC;
|
||||
|
||||
do {
|
||||
/* start of ioctl loop - zero size and set count to bufsize */
|
||||
@@ -4370,10 +4422,8 @@ static struct _extent *_stats_get_extents_for_file(struct dm_pool *mem, int fd,
|
||||
fiemap->fm_extent_count = *count;
|
||||
|
||||
/* get count-sized chunk of extents */
|
||||
rc = ioctl(fd, FS_IOC_FIEMAP, (unsigned long) fiemap);
|
||||
if (rc < 0) {
|
||||
rc = -errno;
|
||||
if (rc == -EBADR)
|
||||
if (ioctl(fd, FS_IOC_FIEMAP, (unsigned long) fiemap) < 0) {
|
||||
if (errno == EBADR)
|
||||
log_err_once("FIEMAP failed with unknown "
|
||||
"flags %x.", fiemap->fm_flags);
|
||||
goto bad;
|
||||
@@ -4383,8 +4433,9 @@ static struct _extent *_stats_get_extents_for_file(struct dm_pool *mem, int fd,
|
||||
if (fiemap->fm_mapped_extents == 0)
|
||||
break;
|
||||
|
||||
nr_extents += _stats_map_extents(mem, fiemap, fm_ext, &fm_last,
|
||||
&fm_pending, nr_extents, &eof);
|
||||
nr_extents += _stats_map_extents(fd, mem, fiemap, fm_ext,
|
||||
&fm_last, &fm_pending,
|
||||
nr_extents, &eof);
|
||||
|
||||
/* check for extent mapping error */
|
||||
if (eof < 0)
|
||||
@@ -4412,23 +4463,140 @@ bad:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define MATCH_EXTENT(e, s, l) \
|
||||
(((e).start == (s)) && ((e).len == (l)))
|
||||
|
||||
static struct _extent *_find_extent(size_t nr_extents, struct _extent *extents,
|
||||
uint64_t start, uint64_t len)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < nr_extents; i++)
|
||||
if (MATCH_EXTENT(extents[i], start, len))
|
||||
return extents + i;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a set of regions representing the extents of a file and
|
||||
* return a table of uint64_t region_id values. The number of regions
|
||||
* Clean up a table of region_id values that were created during a
|
||||
* failed dm_stats_create_regions_from_fd, or dm_stats_update_regions_from_fd
|
||||
* operation.
|
||||
*/
|
||||
static void _stats_cleanup_region_ids(struct dm_stats *dms, uint64_t *regions,
|
||||
uint64_t nr_regions)
|
||||
{
|
||||
uint64_t i;
|
||||
|
||||
for (i = 0; i < nr_regions; i++)
|
||||
if (!_stats_delete_region(dms, regions[i]))
|
||||
log_error("Could not delete region " FMTu64 ".", i);
|
||||
}
|
||||
|
||||
/*
|
||||
* First update pass: prune no-longer-allocated extents from the group
|
||||
* and build a table of the remaining extents so that their creation
|
||||
* can be skipped in the second pass.
|
||||
*/
|
||||
static int _stats_unmap_regions(struct dm_stats *dms, uint64_t group_id,
|
||||
struct dm_pool *mem, struct _extent *extents,
|
||||
struct _extent **old_extents, uint64_t *count,
|
||||
int *regroup)
|
||||
{
|
||||
struct dm_stats_region *region = NULL;
|
||||
struct dm_stats_group *group = NULL;
|
||||
int64_t nr_kept, nr_old, i;
|
||||
struct _extent ext;
|
||||
|
||||
group = &dms->groups[group_id];
|
||||
|
||||
log_very_verbose("Checking for changed file extents in group ID "
|
||||
FMTu64, group_id);
|
||||
|
||||
if (!dm_pool_begin_object(mem, sizeof(**old_extents))) {
|
||||
log_error("Could not allocate extent table.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
nr_kept = nr_old = 0; /* counts of old and retained extents */
|
||||
|
||||
/*
|
||||
* First pass: delete de-allocated extents and set regroup=1 if
|
||||
* deleting the current group leader.
|
||||
*/
|
||||
i = dm_bit_get_last(group->regions);
|
||||
for (; i >= 0; i = dm_bit_get_prev(group->regions, i)) {
|
||||
region = &dms->regions[i];
|
||||
nr_old++;
|
||||
|
||||
if (_find_extent(*count, extents,
|
||||
region->start, region->len)) {
|
||||
ext.start = region->start;
|
||||
ext.len = region->len;
|
||||
ext.id = i;
|
||||
nr_kept++;
|
||||
|
||||
dm_pool_grow_object(mem, &ext,
|
||||
sizeof(ext));
|
||||
log_very_verbose("Kept region " FMTu64, i);
|
||||
} else {
|
||||
|
||||
if (i == group_id)
|
||||
*regroup = 1;
|
||||
|
||||
if (!_stats_delete_region(dms, i)) {
|
||||
log_error("Could not remove region ID " FMTu64,
|
||||
i);
|
||||
goto out;
|
||||
}
|
||||
|
||||
log_very_verbose("Deleted region " FMTu64, i);
|
||||
}
|
||||
}
|
||||
|
||||
*old_extents = dm_pool_end_object(mem);
|
||||
if (!*old_extents) {
|
||||
log_error("Could not finalize region extent table.");
|
||||
goto out;
|
||||
}
|
||||
log_very_verbose("Kept %ld of %ld old extents",
|
||||
nr_kept, nr_old);
|
||||
log_very_verbose("Found " FMTu64 " new extents",
|
||||
*count - nr_kept);
|
||||
|
||||
return nr_kept;
|
||||
out:
|
||||
dm_pool_abandon_object(mem);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create or update a set of regions representing the extents of a file
|
||||
* and return a table of uint64_t region_id values. The number of regions
|
||||
* created is returned in the memory pointed to by count (which must be
|
||||
* non-NULL).
|
||||
*
|
||||
* If group_id is not equal to DM_STATS_GROUP_NOT_PRESENT, it is assumed
|
||||
* that group_id corresponds to a group containing existing regions that
|
||||
* were mapped to this file at an earlier time: regions will be added or
|
||||
* removed to reflect the current status of the file.
|
||||
*/
|
||||
static uint64_t *_stats_create_file_regions(struct dm_stats *dms, int fd,
|
||||
struct dm_histogram *bounds,
|
||||
int precise, uint64_t *count)
|
||||
static uint64_t *_stats_map_file_regions(struct dm_stats *dms, int fd,
|
||||
struct dm_histogram *bounds,
|
||||
int precise, uint64_t group_id,
|
||||
uint64_t *count, int *regroup)
|
||||
{
|
||||
uint64_t *regions = NULL, i, fail_region;
|
||||
struct _extent *extents = NULL, *old_extents;
|
||||
uint64_t *regions = NULL, fail_region;
|
||||
struct dm_stats_group *group = NULL;
|
||||
struct dm_pool *extent_mem = NULL;
|
||||
struct _extent *extents = NULL;
|
||||
struct _extent *old_ext;
|
||||
char *hist_arg = NULL;
|
||||
int update, num_bits;
|
||||
struct statfs fsbuf;
|
||||
int64_t nr_kept, i;
|
||||
struct stat buf;
|
||||
|
||||
update = _stats_group_id_present(dms, group_id);
|
||||
|
||||
#ifdef BTRFS_SUPER_MAGIC
|
||||
if (fstatfs(fd, &fsbuf)) {
|
||||
log_error("fstatfs failed for fd %d", fd);
|
||||
@@ -4457,6 +4625,18 @@ static uint64_t *_stats_create_file_regions(struct dm_stats *dms, int fd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* If regroup is set here, we are creating a new filemap: otherwise
|
||||
* we are updating a group with a valid group identifier in group_id.
|
||||
*/
|
||||
if (update)
|
||||
log_very_verbose("Updating extents from fd %d with group ID "
|
||||
FMTu64 " on (%d:%d)", fd, group_id,
|
||||
major(buf.st_dev), minor(buf.st_dev));
|
||||
else
|
||||
log_very_verbose("Mapping extents from fd %d on (%d:%d)",
|
||||
fd, major(buf.st_dev), minor(buf.st_dev));
|
||||
|
||||
/* Use a temporary, private pool for the extent table. This avoids
|
||||
* hijacking the dms->mem (region table) pool which would lead to
|
||||
* interleaving temporary allocations with dm_stats_list() data,
|
||||
@@ -4470,19 +4650,41 @@ static uint64_t *_stats_create_file_regions(struct dm_stats *dms, int fd,
|
||||
return_0;
|
||||
}
|
||||
|
||||
if (bounds) {
|
||||
/* _build_histogram_arg enables precise if vals < 1ms. */
|
||||
if (update) {
|
||||
group = &dms->groups[group_id];
|
||||
if ((nr_kept = _stats_unmap_regions(dms, group_id, extent_mem,
|
||||
extents, &old_extents,
|
||||
count, regroup)) < 0)
|
||||
goto_out;
|
||||
}
|
||||
|
||||
if (bounds)
|
||||
if (!(hist_arg = _build_histogram_arg(bounds, &precise)))
|
||||
goto_out;
|
||||
}
|
||||
|
||||
/* make space for end-of-table marker */
|
||||
if (!(regions = dm_malloc((1 + *count) * sizeof(*regions)))) {
|
||||
log_error("Could not allocate memory for region IDs.");
|
||||
goto out;
|
||||
goto_out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Second pass (first for non-update case): create regions for
|
||||
* all extents not retained from the prior mapping, and insert
|
||||
* retained regions into the table of region_id values.
|
||||
*
|
||||
* If a regroup is not scheduled, set group bits for newly
|
||||
* created regions in the group leader bitmap.
|
||||
*/
|
||||
for (i = 0; i < *count; i++) {
|
||||
if (update) {
|
||||
if ((old_ext = _find_extent(nr_kept, old_extents,
|
||||
extents[i].start,
|
||||
extents[i].len))) {
|
||||
regions[i] = old_ext->id;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!_stats_create_region(dms, regions + i, extents[i].start,
|
||||
extents[i].len, -1, precise, hist_arg,
|
||||
dms->program_id, "")) {
|
||||
@@ -4491,13 +4693,39 @@ static uint64_t *_stats_create_file_regions(struct dm_stats *dms, int fd,
|
||||
extents[i].start);
|
||||
goto out_remove;
|
||||
}
|
||||
|
||||
log_very_verbose("Created new region mapping " FMTu64 "+" FMTu64
|
||||
" with region ID " FMTu64, extents[i].start,
|
||||
extents[i].len, regions[i]);
|
||||
|
||||
if (!*regroup && update) {
|
||||
/* expand group bitmap */
|
||||
if (regions[i] > (group->regions[0] - 1)) {
|
||||
num_bits = regions[i] + *count;
|
||||
if (!_stats_resize_group(group, num_bits)) {
|
||||
log_error("Failed to resize group "
|
||||
"bitmap.");
|
||||
goto out_remove;
|
||||
}
|
||||
}
|
||||
dm_bit_set(group->regions, regions[i]);
|
||||
}
|
||||
|
||||
}
|
||||
regions[*count] = DM_STATS_REGION_NOT_PRESENT;
|
||||
|
||||
/* Update group leader aux_data for new group members. */
|
||||
if (!*regroup && update)
|
||||
if (!_stats_set_aux(dms, group_id,
|
||||
dms->regions[group_id].aux_data))
|
||||
log_error("Failed to update group aux_data.");
|
||||
|
||||
if (bounds)
|
||||
dm_free(hist_arg);
|
||||
|
||||
dm_pool_free(extent_mem, extents);
|
||||
dm_pool_destroy(extent_mem);
|
||||
dm_free(hist_arg);
|
||||
return regions;
|
||||
|
||||
out_remove:
|
||||
@@ -4511,13 +4739,15 @@ out_remove:
|
||||
dm_stats_list(dms, NULL);
|
||||
|
||||
fail_region = i;
|
||||
for (i = 0; i < fail_region; i++)
|
||||
if (!_stats_delete_region(dms, regions[i]))
|
||||
log_error("Could not delete region " FMTu64 ".", i);
|
||||
|
||||
_stats_cleanup_region_ids(dms, regions, fail_region);
|
||||
*count = 0;
|
||||
|
||||
out:
|
||||
/*
|
||||
* The table of file extents in 'extents' is always built, so free
|
||||
* it explicitly: this will also free any 'old_extents' table that
|
||||
* was later allocated from the 'extent_mem' pool by this function.
|
||||
*/
|
||||
dm_pool_free(extent_mem, extents);
|
||||
dm_pool_destroy(extent_mem);
|
||||
dm_free(hist_arg);
|
||||
@@ -4531,15 +4761,16 @@ uint64_t *dm_stats_create_regions_from_fd(struct dm_stats *dms, int fd,
|
||||
const char *alias)
|
||||
{
|
||||
uint64_t *regions, count = 0;
|
||||
int regroup = 1;
|
||||
|
||||
if (alias && !group) {
|
||||
log_error("Cannot set alias without grouping regions.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
regions = _stats_create_file_regions(dms, fd, bounds, precise, &count);
|
||||
if (!regions)
|
||||
return_0;
|
||||
if (!(regions = _stats_map_file_regions(dms, fd, bounds, precise,
|
||||
-1, &count, ®roup)))
|
||||
return NULL;
|
||||
|
||||
if (!group)
|
||||
return regions;
|
||||
@@ -4553,11 +4784,97 @@ uint64_t *dm_stats_create_regions_from_fd(struct dm_stats *dms, int fd,
|
||||
|
||||
return regions;
|
||||
out:
|
||||
_stats_cleanup_region_ids(dms, regions, count);
|
||||
dm_free(regions);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint64_t *dm_stats_update_regions_from_fd(struct dm_stats *dms, int fd,
|
||||
uint64_t group_id)
|
||||
{
|
||||
struct dm_histogram *bounds = NULL;
|
||||
int nr_bins, precise, regroup;
|
||||
uint64_t *regions, count = 0;
|
||||
const char *alias = NULL;
|
||||
|
||||
if (!dms->regions || !dm_stats_group_present(dms, group_id)) {
|
||||
if (!dm_stats_list(dms, dms->program_id)) {
|
||||
log_error("Could not obtain region list while "
|
||||
"updating group " FMTu64 ".", group_id);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!dm_stats_group_present(dms, group_id)) {
|
||||
log_error("Group ID " FMTu64 " does not exist.", group_id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the extent corresponding to the group leader's region has been
|
||||
* deallocated, _stats_map_file_regions() will remove the region and
|
||||
* the group. In this case, regroup will be set by the call and the
|
||||
* group will be re-created using saved values.
|
||||
*/
|
||||
regroup = 0;
|
||||
|
||||
/*
|
||||
* A copy of the alias is needed to re-create the group when regroup=1.
|
||||
*/
|
||||
if (dms->groups[group_id].alias) {
|
||||
alias = dm_strdup(dms->groups[group_id].alias);
|
||||
if (!alias) {
|
||||
log_error("Failed to allocate group alias string.");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (dms->regions[group_id].bounds) {
|
||||
/*
|
||||
* A copy of the histogram bounds must be passed to
|
||||
* _stats_map_file_regions() to be used when creating new
|
||||
* regions: it is not safe to use the copy in the current group
|
||||
* leader since it may be destroyed during the first group
|
||||
* update pass.
|
||||
*/
|
||||
nr_bins = dms->regions[group_id].bounds->nr_bins;
|
||||
bounds = _alloc_dm_histogram(nr_bins);
|
||||
if (!bounds) {
|
||||
log_error("Could not allocate memory for group "
|
||||
"histogram bounds.");
|
||||
return NULL;
|
||||
}
|
||||
_stats_copy_histogram_bounds(bounds,
|
||||
dms->regions[group_id].bounds);
|
||||
}
|
||||
|
||||
precise = (dms->regions[group_id].timescale == 1);
|
||||
|
||||
regions = _stats_map_file_regions(dms, fd, bounds, precise,
|
||||
group_id, &count, ®roup);
|
||||
|
||||
if (!regions)
|
||||
goto bad;
|
||||
|
||||
if (!dm_stats_list(dms, NULL))
|
||||
goto bad;
|
||||
|
||||
if (regroup)
|
||||
if (!_stats_group_file_regions(dms, regions, count, alias))
|
||||
goto bad;
|
||||
|
||||
dm_free(bounds);
|
||||
dm_free((char *) alias);
|
||||
return regions;
|
||||
bad:
|
||||
_stats_cleanup_region_ids(dms, regions, count);
|
||||
dm_free(bounds);
|
||||
dm_free((char *) alias);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#else /* HAVE_LINUX_FIEMAP */
|
||||
|
||||
uint64_t *dm_stats_create_regions_from_fd(struct dm_stats *dms, int fd,
|
||||
int group, int precise,
|
||||
struct dm_histogram *bounds,
|
||||
@@ -4566,6 +4883,13 @@ uint64_t *dm_stats_create_regions_from_fd(struct dm_stats *dms, int fd,
|
||||
log_error("File mapping requires FIEMAP ioctl support.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t *dm_stats_update_regions_from_fd(struct dm_stats *dms, int fd,
|
||||
uint64_t group_id)
|
||||
{
|
||||
log_error("File mapping requires FIEMAP ioctl support.");
|
||||
return 0;
|
||||
}
|
||||
#endif /* HAVE_LINUX_FIEMAP */
|
||||
|
||||
/*
|
||||
|
@@ -468,10 +468,12 @@ const char *dm_size_to_string(struct dm_pool *mem, uint64_t size,
|
||||
unsigned base = BASE_UNKNOWN;
|
||||
unsigned s;
|
||||
int precision;
|
||||
double d;
|
||||
uint64_t byte = UINT64_C(0);
|
||||
uint64_t units = UINT64_C(1024);
|
||||
char *size_buf = NULL;
|
||||
char new_unit_type = '\0', unit_type_buf[2];
|
||||
const char *prefix = "";
|
||||
const char * const size_str[][3] = {
|
||||
/* BASE_UNKNOWN */
|
||||
{" ", " ", " "}, /* [0] */
|
||||
@@ -570,7 +572,7 @@ const char *dm_size_to_string(struct dm_pool *mem, uint64_t size,
|
||||
byte = unit_factor;
|
||||
} else {
|
||||
/* Human-readable style */
|
||||
if (unit_type == 'H') {
|
||||
if (unit_type == 'H' || unit_type == 'R') {
|
||||
units = UINT64_C(1000);
|
||||
base = BASE_1000;
|
||||
} else {
|
||||
@@ -586,6 +588,16 @@ const char *dm_size_to_string(struct dm_pool *mem, uint64_t size,
|
||||
for (s = 0; s < NUM_UNIT_PREFIXES && size < byte; s++)
|
||||
byte /= units;
|
||||
|
||||
if ((s < NUM_UNIT_PREFIXES) &&
|
||||
((unit_type == 'R') || (unit_type == 'r'))) {
|
||||
/* When the rounding would cause difference, add '<' prefix
|
||||
* i.e. 2043M is more then 1.9949G prints <2.00G
|
||||
* This version is for 2 digits fixed precision */
|
||||
d = 100. * (double) size / byte;
|
||||
if (!_close_enough(floorl(d), nearbyintl(d)))
|
||||
prefix = "<";
|
||||
}
|
||||
|
||||
include_suffix = 1;
|
||||
}
|
||||
|
||||
@@ -599,7 +611,7 @@ const char *dm_size_to_string(struct dm_pool *mem, uint64_t size,
|
||||
precision = 2;
|
||||
}
|
||||
|
||||
snprintf(size_buf, SIZE_BUF - 1, "%.*f%s", precision,
|
||||
snprintf(size_buf, SIZE_BUF - 1, "%s%.*f%s", prefix, precision,
|
||||
(double) size / byte, include_suffix ? size_str[base + s][suffix_type] : "");
|
||||
|
||||
return size_buf;
|
||||
@@ -639,6 +651,8 @@ uint64_t dm_units_to_factor(const char *units, char *unit_type,
|
||||
switch (*units) {
|
||||
case 'h':
|
||||
case 'H':
|
||||
case 'r':
|
||||
case 'R':
|
||||
multiplier = v = UINT64_C(1);
|
||||
*unit_type = *units;
|
||||
break;
|
||||
|
@@ -140,13 +140,6 @@ Makefile: Makefile.in
|
||||
*) echo "Creating $@" ; $(SED) -e "s+#VERSION#+$(LVM_VERSION)+;s+#DEFAULT_SYS_DIR#+$(DEFAULT_SYS_DIR)+;s+#DEFAULT_ARCHIVE_DIR#+$(DEFAULT_ARCHIVE_DIR)+;s+#DEFAULT_BACKUP_DIR#+$(DEFAULT_BACKUP_DIR)+;s+#DEFAULT_PROFILE_DIR#+$(DEFAULT_PROFILE_DIR)+;s+#DEFAULT_CACHE_DIR#+$(DEFAULT_CACHE_DIR)+;s+#DEFAULT_LOCK_DIR#+$(DEFAULT_LOCK_DIR)+;s+#CLVMD_PATH#+@CLVMD_PATH@+;s+#LVM_PATH#+@LVM_PATH@+;s+#DEFAULT_RUN_DIR#+@DEFAULT_RUN_DIR@+;s+#DEFAULT_PID_DIR#+@DEFAULT_PID_DIR@+;s+#SYSTEMD_GENERATOR_DIR#+$(SYSTEMD_GENERATOR_DIR)+;s+#DEFAULT_MANGLING#+$(DEFAULT_MANGLING)+;" $< > $@ ;; \
|
||||
esac
|
||||
|
||||
ccmd: ../tools/create-commands.c
|
||||
$(CC) ../tools/create-commands.c -o ccmd
|
||||
|
||||
generate: ccmd
|
||||
./ccmd --output man -s 0 -p 1 -c lvcreate ../tools/command-lines.in > lvcreate.8.a
|
||||
cat lvcreate.8.a lvcreate.8.b > lvcreate.8.in
|
||||
|
||||
install_man5: $(MAN5)
|
||||
$(INSTALL) -d $(MAN5DIR)
|
||||
$(INSTALL_DATA) $(MAN5) $(MAN5DIR)/
|
||||
|
@@ -212,6 +212,17 @@ dmstats \(em device-mapper statistics management
|
||||
. ad b
|
||||
..
|
||||
.CMD_UNGROUP
|
||||
.HP
|
||||
.B dmstats
|
||||
.de CMD_UPDATE_FILEMAP
|
||||
. ad l
|
||||
. BR update_filemap
|
||||
. RI file_path
|
||||
. RB [ \-\-groupid
|
||||
. IR id ]
|
||||
. ad b
|
||||
..
|
||||
.CMD_UPDATE_FILEMAP
|
||||
.
|
||||
.PD
|
||||
.ad b
|
||||
@@ -668,6 +679,21 @@ Remove an existing group and return all the group's regions to their
|
||||
original state.
|
||||
|
||||
The group to be removed is specified using \fB\-\-groupid\fP.
|
||||
.HP
|
||||
.CMD_UPDATE_FILEMAP
|
||||
.br
|
||||
Update a group of \fBdmstats\fP regions specified by \fBgroup_id\fP,
|
||||
that were previously created with \fB\-\-filemap\fP. This will add
|
||||
and remove regions to reflect changes in the allocated extents of
|
||||
the file on-disk, since the time that it was crated or last updated.
|
||||
|
||||
Use of this command is not normally needed since the \fBdmfilemapd\fP
|
||||
daemon will automatically monitor filemap groups and perform these
|
||||
updates when required.
|
||||
|
||||
If a filemapped group was created with \fB\-\-nominitor\fP, or the
|
||||
daemon has been killed, the \fBupdate_filemap\fP can be used to
|
||||
manually force an update.
|
||||
.
|
||||
.SH REGIONS, AREAS, AND GROUPS
|
||||
.
|
||||
|
263
man/lvm.8.in
263
man/lvm.8.in
@@ -45,9 +45,6 @@ A file containing a simple script with one command per line
|
||||
can also be given on the command line. The script can also be
|
||||
executed directly if the first line is #! followed by the absolute
|
||||
path of \fBlvm\fP.
|
||||
.P
|
||||
Additional hyphens within option names are ignored. For example,
|
||||
\fB\-\-readonly\fP and \fB\-\-read\-only\fP are both accepted.
|
||||
.
|
||||
.SH BUILT-IN COMMANDS
|
||||
.
|
||||
@@ -241,6 +238,261 @@ The following commands are not implemented in LVM2 but might be
|
||||
in the future:
|
||||
.BR lvmsadc ", " lvmsar ", " pvdata .
|
||||
.
|
||||
.SH OPTIONS
|
||||
.
|
||||
The following options are available for many of the commands.
|
||||
They are implemented generically and documented here rather
|
||||
than repeated on individual manual pages.
|
||||
.P
|
||||
Additional hyphens within option names are ignored. For example,
|
||||
\fB\-\-readonly\fP and \fB\-\-read\-only\fP are both accepted.
|
||||
.
|
||||
.HP
|
||||
.BR \-h | \-? | \-\-help
|
||||
.br
|
||||
Display the help text.
|
||||
.
|
||||
.HP
|
||||
.BR \-\-version
|
||||
.br
|
||||
Display version information.
|
||||
.
|
||||
.HP
|
||||
.BR \-v | \-\-verbose
|
||||
.br
|
||||
Set verbose level. Repeat from 1 to 3 times to increase the detail
|
||||
of messages sent to stdout and stderr. Overrides config file setting.
|
||||
.
|
||||
.HP
|
||||
.BR \-d | \-\-debug
|
||||
.br
|
||||
Set debug level. Repeat from 1 to 6 times to increase the detail of
|
||||
messages sent to the log file and/or syslog (if configured).
|
||||
Overrides config file setting.
|
||||
.
|
||||
.HP
|
||||
.BR \-q | \-\-quiet
|
||||
.br
|
||||
Suppress output and log messages.
|
||||
Overrides \fB\-d\fP and \fB\-v\fP.
|
||||
Repeat once to also suppress any prompts with answer 'no'.
|
||||
.
|
||||
.HP
|
||||
.BR \-\-yes
|
||||
.br
|
||||
Don't prompt for confirmation interactively but instead always assume the
|
||||
answer is 'yes'. Take great care if you use this!
|
||||
.
|
||||
.HP
|
||||
.BR \-t | \-\-test
|
||||
.br
|
||||
Run in test mode. Commands will not update metadata.
|
||||
This is implemented by disabling all metadata writing but nevertheless
|
||||
returning success to the calling function. This may lead to unusual
|
||||
error messages in multi-stage operations if a tool relies on reading
|
||||
back metadata it believes has changed but hasn't.
|
||||
.
|
||||
.HP
|
||||
.BR \-\-driverloaded
|
||||
.RB { y | n }
|
||||
.br
|
||||
Whether or not the device-mapper kernel driver is loaded.
|
||||
If you set this to \fBn\fP, no attempt will be made to contact the driver.
|
||||
.
|
||||
.HP
|
||||
.BR \-A | \-\-autobackup
|
||||
.RB { y | n }
|
||||
.br
|
||||
Whether or not to metadata should be backed up automatically after a change.
|
||||
You are strongly advised not to disable this!
|
||||
See \fBvgcfgbackup\fP(8).
|
||||
.
|
||||
.HP
|
||||
.BR \-P | \-\-partial
|
||||
.br
|
||||
When set, the tools will do their best to provide access to Volume Groups
|
||||
that are only partially available (one or more Physical Volumes belonging
|
||||
to the Volume Group are missing from the system). Where part of a logical
|
||||
volume is missing, \fI\%/dev/ioerror\fP will be substituted, and you could use
|
||||
\fBdmsetup\fP(8) to set this up to return I/O errors when accessed,
|
||||
or create it as a large block device of nulls. Metadata may not be
|
||||
changed with this option. To insert a replacement Physical Volume
|
||||
of the same or large size use \fBpvcreate \-u\fP to set the uuid to
|
||||
match the original followed by \fBvgcfgrestore\fP(8).
|
||||
.
|
||||
.HP
|
||||
.BR \-S | \-\-select
|
||||
.IR Selection
|
||||
.br
|
||||
For reporting commands, display only rows that match \fISelection\fP criteria.
|
||||
All rows are displayed with the additional "selected" column (\fB-o selected\fP)
|
||||
showing 1 if the row matches the \fISelection\fP and 0 otherwise. For non-reporting
|
||||
commands which process LVM entities, the selection can be used to match items
|
||||
to process. See \fBSelection\fP section in \fBlvmreport\fP(7) man page for more
|
||||
information about the way the selection criteria are constructed.
|
||||
.
|
||||
.HP
|
||||
.BR \-M | \-\-metadatatype
|
||||
.IR Type
|
||||
.br
|
||||
Specifies which \fItype\fP of on-disk metadata to use, such as \fBlvm1\fP
|
||||
or \fBlvm2\fP, which can be abbreviated to \fB1\fP or \fB2\fP respectively.
|
||||
The default (\fBlvm2\fP) can be changed by setting \fBformat\fP
|
||||
in the \fBglobal\fP section of the config file \fBlvm.conf\fP(5).
|
||||
.
|
||||
.HP
|
||||
.BR \-\-ignorelockingfailure
|
||||
.br
|
||||
This lets you proceed with read-only metadata operations such as
|
||||
\fBlvchange \-ay\fP and \fBvgchange \-ay\fP even if the locking module fails.
|
||||
One use for this is in a system init script if the lock directory
|
||||
is mounted read-only when the script runs.
|
||||
.
|
||||
.HP
|
||||
.BR \-\-ignoreskippedcluster
|
||||
.br
|
||||
Use to avoid exiting with an non-zero status code if the command is run
|
||||
without clustered locking and some clustered Volume Groups have to be
|
||||
skipped over.
|
||||
.
|
||||
.HP
|
||||
.BR \-\-readonly
|
||||
.br
|
||||
Run the command in a special read-only mode which will read on-disk
|
||||
metadata without needing to take any locks. This can be used to peek
|
||||
inside metadata used by a virtual machine image while the virtual
|
||||
machine is running.
|
||||
It can also be used to peek inside the metadata of clustered Volume
|
||||
Groups when clustered locking is not configured or running. No attempt
|
||||
will be made to communicate with the device-mapper kernel driver, so
|
||||
this option is unable to report whether or not Logical Volumes are
|
||||
actually in use.
|
||||
.
|
||||
.HP
|
||||
.BR \-\-foreign
|
||||
.br
|
||||
Cause the command to access foreign VGs, that would otherwise be skipped.
|
||||
It can be used to report or display a VG that is owned by another host.
|
||||
This option can cause a command to perform poorly because lvmetad caching
|
||||
is not used and metadata is read from disks.
|
||||
.
|
||||
.HP
|
||||
.BR \-\-shared
|
||||
.br
|
||||
Cause the command to access shared VGs, that would otherwise be skipped
|
||||
when lvmlockd is not being used. It can be used to report or display a
|
||||
lockd VG without locking. Applicable only if LVM is compiled with lockd
|
||||
support.
|
||||
.
|
||||
.HP
|
||||
.BR \-\-addtag
|
||||
.IR Tag
|
||||
.br
|
||||
Add the tag \fITag\fP to a PV, VG or LV.
|
||||
Supply this argument multiple times to add more than one tag at once.
|
||||
A tag is a word that can be used to group LVM2 objects of the same type
|
||||
together.
|
||||
Tags can be given on the command line in place of PV, VG or LV
|
||||
arguments. Tags should be prefixed with @ to avoid ambiguity.
|
||||
Each tag is expanded by replacing it with all objects possessing
|
||||
that tag which are of the type expected by its position on the command line.
|
||||
PVs can only possess tags while they are part of a Volume Group:
|
||||
PV tags are discarded if the PV is removed from the VG.
|
||||
As an example, you could tag some LVs as \fBdatabase\fP and others
|
||||
as \fBuserdata\fP and then activate the database ones
|
||||
with \fBlvchange \-ay @database\fP.
|
||||
Objects can possess multiple tags simultaneously.
|
||||
Only the new LVM2 metadata format supports tagging: objects using the
|
||||
LVM1 metadata format cannot be tagged because the on-disk format does not
|
||||
support it.
|
||||
Characters allowed in tags are:
|
||||
.BR A - Z
|
||||
.BR a - z
|
||||
.BR 0 - 9
|
||||
.BR "_ + . -"
|
||||
and as of version 2.02.78 the following characters are also accepted:
|
||||
.BR "/ = ! : # &" .
|
||||
.
|
||||
.HP
|
||||
.BR \-\-deltag
|
||||
.IR Tag
|
||||
.br
|
||||
Delete the tag \fITag\fP from a PV, VG or LV, if it's present.
|
||||
Supply this argument multiple times to remove more than one tag at once.
|
||||
.
|
||||
.HP
|
||||
.BR \-\-alloc
|
||||
.RB { anywhere | contiguous | cling | inherit | normal }
|
||||
.br
|
||||
Selects the allocation policy when a command needs to allocate
|
||||
Physical Extents from the Volume Group.
|
||||
Each Volume Group and Logical Volume has an allocation policy defined.
|
||||
The default for a Volume Group is \fBnormal\fP which applies
|
||||
common-sense rules such as not placing parallel stripes on the same
|
||||
Physical Volume. The default for a Logical Volume is \fBinherit\fP
|
||||
which applies the same policy as for the Volume Group. These policies can
|
||||
be changed using \fBlvchange\fP(8) and \fBvgchange\fP(8) or overridden
|
||||
on the command line of any command that performs allocation.
|
||||
The \fBcontiguous\fP policy requires that new Physical Extents be placed adjacent
|
||||
to existing Physical Extents.
|
||||
The \fBcling\fP policy places new Physical Extents on the same Physical
|
||||
Volume as existing Physical Extents in the same stripe of the Logical Volume.
|
||||
If there are sufficient free Physical Extents to satisfy
|
||||
an allocation request but \fBnormal\fP doesn't use them,
|
||||
\fBanywhere\fP will - even if that reduces performance by
|
||||
placing two stripes on the same Physical Volume.
|
||||
.
|
||||
.HP
|
||||
.BR \-\-commandprofile
|
||||
.IR ProfileName
|
||||
.br
|
||||
Selects the command configuration profile to use when processing an LVM command.
|
||||
See also \fBlvm.conf\fP(5) for more information about \fBcommand profile config\fP and
|
||||
the way it fits with other LVM configuration methods. Using \fB\-\-commandprofile\fP
|
||||
option overrides any command profile specified via \fBLVM_COMMAND_PROFILE\fP
|
||||
environment variable.
|
||||
.
|
||||
.HP
|
||||
.BR \-\-metadataprofile
|
||||
.IR ProfileName
|
||||
.br
|
||||
Selects the metadata configuration profile to use when processing an LVM command.
|
||||
When using metadata profile during Volume Group or Logical Volume creation,
|
||||
the metadata profile name is saved in metadata. When such Volume Group or Logical
|
||||
Volume is processed next time, the metadata profile is automatically applied
|
||||
and the use of \fB\-\-metadataprofile\fP option is not necessary. See also
|
||||
\fBlvm.conf\fP(5) for more information about \fBmetadata profile config\fP and the
|
||||
way it fits with other LVM configuration methods.
|
||||
.
|
||||
.HP
|
||||
.BR \-\-profile
|
||||
.IR ProfileName
|
||||
.br
|
||||
A short form of \fB\-\-metadataprofile\fP for \fBvgcreate\fP, \fBlvcreate\fP,
|
||||
\fBvgchange\fP and \fBlvchange\fP command and a short form of \fB\-\-commandprofile\fP
|
||||
for any other command (with the exception of \fBlvmconfig\fP command where the
|
||||
\fB\-\-profile\fP has special meaning, see \fBlvmconfig\fP(8) for more information).
|
||||
.
|
||||
.HP
|
||||
.BR \-\-reportformat
|
||||
.IR {basic|json}
|
||||
.br
|
||||
Overrides current output format for reports which is defined globally by
|
||||
\fBreport/output_format\fP configuration setting in \fBlvm.conf\fP(5).
|
||||
The \fBbasic\fP format is the original format with columns and rows and
|
||||
if there is more than one report per command, each report is prefixed
|
||||
with report's name for identification. The \fBjson\fP stands for report
|
||||
output in JSON format.
|
||||
.HP
|
||||
.BR \-\-config
|
||||
.IR ConfigurationString
|
||||
.br
|
||||
Uses the ConfigurationString as direct string representation of the configuration
|
||||
to override the existing configuration. The ConfigurationString is of exactly
|
||||
the same format as used in any LVM configuration file. See \fBlvm.conf\fP(5)
|
||||
for more information about \fBdirect config override on command line\fP and the
|
||||
way it fits with other LVM configuration methods.
|
||||
.
|
||||
.SH VALID NAMES
|
||||
.
|
||||
The valid characters for VG and LV names are:
|
||||
@@ -416,6 +668,11 @@ File descriptor to use for report output from LVM commands.
|
||||
Name of default command profile to use for LVM commands. This profile
|
||||
is overriden by direct use of \fB\-\-commandprofile\fP command line option.
|
||||
.TP
|
||||
.B LVM_RUN_BY_DMEVENTD
|
||||
This variable is normally set by dmeventd plugin to inform lvm2 command
|
||||
it is running from dmeventd plugin so lvm2 takes some extra action
|
||||
to avoid comunication and deadlocks with dmeventd.
|
||||
.TP
|
||||
.B LVM_SYSTEM_DIR
|
||||
Directory containing \fBlvm.conf\fP(5) and other LVM system files.
|
||||
Defaults to "\fI#DEFAULT_SYS_DIR#\fP".
|
||||
|
@@ -171,7 +171,8 @@ device_umount_one() {
|
||||
}
|
||||
|
||||
device_umount() {
|
||||
test "$devtype" != "lvm" && test "${kname:0:3}" != "dm-" && return 0
|
||||
test "$devtype" != "lvm" && test "${kname:0:3}" != "dm-" \
|
||||
&& test "${kname:0:2}" != "md" && return 0
|
||||
|
||||
# FINDMNT is defined only if umount --all-targets is not available.
|
||||
# In that case, read the list of multiple mount points of one device
|
||||
|
@@ -975,6 +975,31 @@ enable_dev() {
|
||||
done
|
||||
}
|
||||
|
||||
# Once there is $name.devtable
|
||||
# this is a quick way to restore to this table entry
|
||||
restore_from_devtable() {
|
||||
local dev
|
||||
local silent
|
||||
|
||||
if test "$1" = "--silent"; then
|
||||
silent=1
|
||||
shift
|
||||
fi
|
||||
|
||||
rm -f debug.log strace.log
|
||||
init_udev_transaction
|
||||
for dev in "$@"; do
|
||||
local name=$(echo "$dev" | sed -e 's,.*/,,')
|
||||
dmsetup load "$name" "$name.devtable"
|
||||
dmsetup resume "$name"
|
||||
done
|
||||
finish_udev_transaction
|
||||
|
||||
test -n "$silent" || for dev in "$@"; do
|
||||
notify_lvmetad "$dev"
|
||||
done
|
||||
}
|
||||
|
||||
#
|
||||
# Convert device to device with errors
|
||||
# Takes the list of pairs of error segment from:len
|
||||
|
@@ -18,10 +18,20 @@ aux have_raid 1 3 2 || skip
|
||||
|
||||
aux prepare_vg 8
|
||||
|
||||
_sync() {
|
||||
aux enable_dev $(< DEVICES)
|
||||
|
||||
aux wait_for_sync $vg $lv1
|
||||
test -z "$1" || check raid_leg_status $vg $lv1 $1
|
||||
|
||||
# restore to delay_dev tables for all devices
|
||||
aux restore_from_devtable $(< DEVICES)
|
||||
}
|
||||
|
||||
# Delay legs so that rebuilding status characters can be read
|
||||
for d in $(< DEVICES)
|
||||
do
|
||||
aux delay_dev "$d" 0 20 $(get first_extent_sector "$d")
|
||||
aux delay_dev "$d" 0 50 $(get first_extent_sector "$d")
|
||||
done
|
||||
|
||||
# rhbz 1064592
|
||||
@@ -30,115 +40,101 @@ done
|
||||
# Create an 8-way striped raid10 with 4 mirror
|
||||
# groups and rebuild selected PVs.
|
||||
lvcreate --type raid10 -m 1 -i 4 -l 2 -n $lv1 $vg
|
||||
aux wait_for_sync $vg $lv1
|
||||
_sync
|
||||
|
||||
# Rebuild 1st and 2nd device would rebuild a
|
||||
# whole mirror group and needs to be rejected.
|
||||
not lvchange --yes --rebuild "$dev1" --rebuild "$dev2" $vg/$lv1
|
||||
not check raid_leg_status $vg $lv1 "aAaAAAAA"
|
||||
aux wait_for_sync $vg $lv1
|
||||
check raid_leg_status $vg $lv1 "AAAAAAAA"
|
||||
_sync "AAAAAAAA"
|
||||
|
||||
# Rebuild 1st and 3rd device from different mirror groups is fine.
|
||||
lvchange --yes --rebuild "$dev1" --rebuild "$dev3" $vg/$lv1
|
||||
aux have_raid 1 9 && check raid_leg_status $vg $lv1 "aAaAAAAA"
|
||||
aux wait_for_sync $vg $lv1
|
||||
check raid_leg_status $vg $lv1 "AAAAAAAA"
|
||||
_sync "AAAAAAAA"
|
||||
|
||||
# Rebuild devices 1, 3, 6 from different mirror groups is fine.
|
||||
lvchange --yes --rebuild "$dev1" --rebuild "$dev3" --rebuild "$dev6" $vg/$lv1
|
||||
aux have_raid 1 9 && check raid_leg_status $vg $lv1 "aAaAAaAA"
|
||||
aux wait_for_sync $vg $lv1
|
||||
check raid_leg_status $vg $lv1 "AAAAAAAA"
|
||||
_sync "AAAAAAAA"
|
||||
|
||||
# Rebuild devices 1, 3, 5 and 6 with 5+6 being
|
||||
# being a whole mirror group needs to be rejected.
|
||||
not lvchange --yes --rebuild "$dev1" --rebuild "$dev3" --rebuild "$dev6" --rebuild "$dev5" $vg/$lv1
|
||||
not check raid_leg_status $vg $lv1 "aAaAaaAA"
|
||||
aux wait_for_sync $vg $lv1
|
||||
check raid_leg_status $vg $lv1 "AAAAAAAA"
|
||||
_sync "AAAAAAAA"
|
||||
|
||||
# Rebuild devices 1, 3, 5 and 7 from different mirror groups is fine.
|
||||
lvchange --yes --rebuild "$dev1" --rebuild "$dev3" --rebuild "$dev5" --rebuild "$dev7" $vg/$lv1
|
||||
aux have_raid 1 9 && check raid_leg_status $vg $lv1 "aAaAaAaA"
|
||||
aux wait_for_sync $vg $lv1
|
||||
_sync
|
||||
|
||||
# Rebuild devices 2, 4, 6 and 8 from different mirror groups is fine.
|
||||
lvchange --yes --rebuild "$dev2" --rebuild "$dev4" --rebuild "$dev6" --rebuild "$dev8" $vg/$lv1
|
||||
aux have_raid 1 9 && check raid_leg_status $vg $lv1 "AaAaAaAa"
|
||||
aux wait_for_sync $vg $lv1
|
||||
check raid_leg_status $vg $lv1 "AAAAAAAA"
|
||||
_sync "AAAAAAAA"
|
||||
|
||||
##############################################
|
||||
# Create an 8-legged raid1 and rebuild selected PVs
|
||||
lvremove --yes $vg/$lv1
|
||||
lvcreate --yes --type raid1 -m 7 -l 2 -n $lv1 $vg
|
||||
aux wait_for_sync $vg $lv1
|
||||
check raid_leg_status $vg $lv1 "AAAAAAAA"
|
||||
_sync "AAAAAAAA"
|
||||
|
||||
# Rebuilding all raid1 legs needs to be rejected.
|
||||
not lvchange --yes --rebuild "$dev1" --rebuild "$dev2" --rebuild "$dev3" --rebuild "$dev4" \
|
||||
--rebuild "$dev5" --rebuild "$dev6" --rebuild "$dev7" --rebuild "$dev8" $vg/$lv1
|
||||
not check raid_leg_status $vg $lv1 "aaaaaaaa"
|
||||
aux wait_for_sync $vg $lv1
|
||||
check raid_leg_status $vg $lv1 "AAAAAAAA"
|
||||
_sync "AAAAAAAA"
|
||||
|
||||
# Rebuilding all but the raid1 master leg is fine.
|
||||
lvchange --yes --rebuild "$dev2" --rebuild "$dev3" --rebuild "$dev4" \
|
||||
--rebuild "$dev5" --rebuild "$dev6" --rebuild "$dev7" --rebuild "$dev8" $vg/$lv1
|
||||
aux have_raid 1 9 && check raid_leg_status $vg $lv1 "Aaaaaaaa"
|
||||
aux wait_for_sync $vg $lv1
|
||||
check raid_leg_status $vg $lv1 "AAAAAAAA"
|
||||
_sync "AAAAAAAA"
|
||||
|
||||
# Rebuilding the raid1 master leg is fine.
|
||||
lvchange --yes --rebuild "$dev1" $vg/$lv1
|
||||
aux have_raid 1 9 && check raid_leg_status $vg $lv1 "aAAAAAAA"
|
||||
aux wait_for_sync $vg $lv1
|
||||
check raid_leg_status $vg $lv1 "AAAAAAAA"
|
||||
_sync "AAAAAAAA"
|
||||
|
||||
# Rebuild legs on devices 2, 4, 6 and 8 is fine.
|
||||
lvchange --yes --rebuild "$dev2" --rebuild "$dev4" --rebuild "$dev6" --rebuild "$dev8" $vg/$lv1
|
||||
aux have_raid 1 9 && check raid_leg_status $vg $lv1 "AaAaAaAa"
|
||||
aux wait_for_sync $vg $lv1
|
||||
check raid_leg_status $vg $lv1 "AAAAAAAA"
|
||||
_sync "AAAAAAAA"
|
||||
|
||||
##############################################
|
||||
# Create an 6-legged raid6 and rebuild selected PVs
|
||||
lvremove --yes $vg/$lv1
|
||||
lvcreate --yes --type raid6 -i 4 -l 2 -n $lv1 $vg
|
||||
aux wait_for_sync $vg $lv1
|
||||
check raid_leg_status $vg $lv1 "AAAAAA"
|
||||
_sync "AAAAAA"
|
||||
|
||||
# Rebuilding all raid6 stripes needs to be rejected.
|
||||
not lvchange --yes --rebuild "$dev1" --rebuild "$dev2" --rebuild "$dev3" \
|
||||
--rebuild "$dev4" --rebuild "$dev5" --rebuild "$dev6" $vg/$lv1
|
||||
not check raid_leg_status $vg $lv1 "aaaaaa"
|
||||
aux wait_for_sync $vg $lv1
|
||||
check raid_leg_status $vg $lv1 "AAAAAA"
|
||||
_sync "AAAAAA"
|
||||
|
||||
# Rebuilding more than 2 raid6 stripes needs to be rejected.
|
||||
not lvchange --yes --rebuild "$dev2" --rebuild "$dev4" --rebuild "$dev6" $vg/$lv1
|
||||
not check raid_leg_status $vg $lv1 "AaAaAa"
|
||||
aux wait_for_sync $vg $lv1
|
||||
check raid_leg_status $vg $lv1 "AAAAAA"
|
||||
_sync "AAAAAA"
|
||||
|
||||
# Rebuilding any 1 raid6 stripe is fine.
|
||||
lvchange --yes --rebuild "$dev2" $vg/$lv1
|
||||
aux have_raid 1 9 && check raid_leg_status $vg $lv1 "AaAAAA"
|
||||
aux wait_for_sync $vg $lv1
|
||||
_sync
|
||||
|
||||
lvchange --yes --rebuild "$dev5" $vg/$lv1
|
||||
aux have_raid 1 9 && check raid_leg_status $vg $lv1 "AAAAaA"
|
||||
aux wait_for_sync $vg $lv1
|
||||
check raid_leg_status $vg $lv1 "AAAAAA"
|
||||
_sync "AAAAAA"
|
||||
|
||||
# Rebuilding any 2 raid6 stripes is fine.
|
||||
lvchange --yes --rebuild "$dev2" --rebuild "$dev4" $vg/$lv1
|
||||
aux have_raid 1 9 && check raid_leg_status $vg $lv1 "AaAaAA"
|
||||
aux wait_for_sync $vg $lv1
|
||||
check raid_leg_status $vg $lv1 "AAAAAA"
|
||||
_sync "AAAAAA"
|
||||
|
||||
lvchange --yes --rebuild "$dev1" --rebuild "$dev5" $vg/$lv1
|
||||
aux have_raid 1 9 && check raid_leg_status $vg $lv1 "aAAAaA"
|
||||
aux wait_for_sync $vg $lv1
|
||||
check raid_leg_status $vg $lv1 "AAAAAA"
|
||||
_sync "AAAAAA"
|
||||
|
||||
vgremove -ff $vg
|
||||
|
@@ -33,7 +33,7 @@ lvcreate -L3 -n cow $vg
|
||||
not lvconvert -s cow $vg/$lv1
|
||||
|
||||
# Use cached LV with 'striped' cow volume
|
||||
lvconvert -y -s $vg/$lv1 $vg/cow
|
||||
lvconvert -y -s $vg/$lv1 cow
|
||||
check lv_field $vg/cow segtype linear
|
||||
check lv_field $vg/$lv1 segtype cache
|
||||
|
||||
|
@@ -60,7 +60,7 @@ check lv_field $vg/$lv2 cache_settings "random_threshold=56,sequential_threshold
|
||||
# Check swap of cache pool metadata
|
||||
lvconvert --yes --type cache-pool --poolmetadata $lv4 $vg/$lv3
|
||||
UUID=$(get lv_field $vg/$lv5 uuid)
|
||||
lvconvert --yes --swapmetadata --poolmetadata $lv5 $vg/$lv3
|
||||
lvconvert --yes --cachepool $vg/$lv3 --poolmetadata $lv5
|
||||
check lv_field $vg/${lv3}_cmeta uuid "$UUID"
|
||||
|
||||
|
||||
@@ -108,30 +108,30 @@ lvcreate -an -Zn -L 8 -n $lv4 $vg
|
||||
invalid lvconvert --type cache --poolmetadata $vg/$lv2 $vg/$lv1
|
||||
|
||||
# Cannot mix with thins
|
||||
not lvconvert --type cache --poolmetadata $vg/$lv2 --thinpool $vg/$lv1
|
||||
not lvconvert --type cache --thin --poolmetadata $vg/$lv2 $vg/$lv1
|
||||
invalid lvconvert --type cache --poolmetadata $vg/$lv2 --thinpool $vg/$lv1
|
||||
invalid lvconvert --type cache --thin --poolmetadata $vg/$lv2 $vg/$lv1
|
||||
|
||||
# Undefined cached volume
|
||||
not lvconvert --type cache --cachepool $vg/$lv1
|
||||
not lvconvert --cache --cachepool $vg/$lv1
|
||||
invalid lvconvert --type cache --cachepool $vg/$lv1
|
||||
invalid lvconvert --cache --cachepool $vg/$lv1
|
||||
|
||||
# Single vg is required
|
||||
not lvconvert --type cache --cachepool $vg/$lv1 --poolmetadata $vg1/$lv2 $vg/$lv3
|
||||
not lvconvert --type cache --cachepool $vg/$lv1 --poolmetadata $lv2 $vg1/$lv3
|
||||
not lvconvert --type cache --cachepool $vg1/$lv1 --poolmetadata $vg2/$lv2 $vg/$lv3
|
||||
not lvconvert --type cache-pool --poolmetadata $vg2/$lv2 $vg1/$lv1
|
||||
invalid lvconvert --type cache --cachepool $vg/$lv1 --poolmetadata $vg1/$lv2 $vg/$lv3
|
||||
invalid lvconvert --type cache --cachepool $vg/$lv1 --poolmetadata $lv2 $vg1/$lv3
|
||||
invalid lvconvert --type cache --cachepool $vg1/$lv1 --poolmetadata $vg2/$lv2 $vg/$lv3
|
||||
invalid lvconvert --type cache-pool --poolmetadata $vg2/$lv2 $vg1/$lv1
|
||||
|
||||
not lvconvert --cachepool $vg1/$lv1 --poolmetadata $vg2/$lv2
|
||||
invalid lvconvert --cachepool $vg1/$lv1 --poolmetadata $vg2/$lv2
|
||||
|
||||
# Invalid syntax, vg is unknown
|
||||
not lvconvert --yes --cachepool $lv3 --poolmetadata $lv4
|
||||
invalid lvconvert --yes --cachepool $lv3 --poolmetadata $lv4
|
||||
|
||||
# Invalid chunk size is <32KiB >1GiB
|
||||
not lvconvert --type cache-pool --chunksize 16 --poolmetadata $lv2 $vg/$lv1
|
||||
not lvconvert --type cache-pool --chunksize 2G --poolmetadata $lv2 $vg/$lv1
|
||||
invalid lvconvert --type cache-pool --chunksize 16 --poolmetadata $lv2 $vg/$lv1
|
||||
invalid lvconvert --type cache-pool --chunksize 2G --poolmetadata $lv2 $vg/$lv1
|
||||
|
||||
# Invalid chunk size is bigger then data size, needs to open VG
|
||||
not lvconvert --yes --type cache-pool --chunksize 16M --poolmetadata $lv2 $vg/$lv1
|
||||
fail lvconvert --yes --type cache-pool --chunksize 16M --poolmetadata $lv2 $vg/$lv1
|
||||
|
||||
lvremove -f $vg
|
||||
|
||||
@@ -142,7 +142,7 @@ lvcreate --type cache-pool -an -v -L 2 -n cpool $vg
|
||||
lvcreate -H -L 4 -n corigin --cachepool $vg/cpool
|
||||
|
||||
# unsupported yet
|
||||
not lvconvert --repair $vg/cpool 2>&1 | tee out
|
||||
fail lvconvert --repair $vg/cpool 2>&1 | tee out
|
||||
#grep "Cannot convert internal LV" out
|
||||
|
||||
lvremove -f $vg
|
||||
@@ -154,13 +154,13 @@ lvcreate --type cache-pool -L10 $vg/$lv1
|
||||
lvcreate --cache -L20 $vg/$lv1
|
||||
lvcreate -L10 -n $lv2 $vg
|
||||
|
||||
not lvconvert --yes --type cache $vg/$lv2 --cachepool $vg/$lv1
|
||||
not lvconvert --yes --type cache $vg/$lv1 --cachepool $vg/$lv2
|
||||
not lvconvert --yes --type cache-pool $vg/$lv1
|
||||
not lvconvert --yes --type mirror -m1 $vg/$lv1
|
||||
fail lvconvert --yes --type cache $vg/$lv2 --cachepool $vg/$lv1
|
||||
fail lvconvert --yes --type cache $vg/$lv1 --cachepool $vg/$lv2
|
||||
fail lvconvert --yes --type cache-pool $vg/$lv1
|
||||
fail lvconvert --yes --type mirror -m1 $vg/$lv1
|
||||
not aux have_raid 1 0 0 || fail lvconvert --yes --type raid1 -m1 $vg/$lv1
|
||||
not lvconvert --yes --type snapshot $vg/$lv1 $vg/$lv2
|
||||
not lvconvert --yes --type snapshot $vg/$lv2 $vg/$lv1
|
||||
fail lvconvert --yes --type snapshot $vg/$lv1 $vg/$lv2
|
||||
fail lvconvert --yes --type snapshot $vg/$lv2 $vg/$lv1
|
||||
not aux have_thin 1 0 0 || fail lvconvert --yes -T --thinpool $vg/$lv2 $vg/$lv1
|
||||
|
||||
lvremove -f $vg
|
||||
|
@@ -85,9 +85,12 @@ offset=$(( offset + 2 ))
|
||||
# update in case mirror ever gets faster and allows parallel read
|
||||
aux delay_dev "$dev2" 0 2000 ${offset}:1
|
||||
lvcreate -aey -l5 -Zn -Wn --type mirror --regionsize 16K -m2 -n $lv1 $vg "$dev1" "$dev2" "$dev4" "$dev3:$DEVRANGE"
|
||||
# FIXME: add a new explicit option to define the polling behavior
|
||||
# done here with 'lvconvert vg/lv'. That option can specify
|
||||
# that the command succeeds even if the LV doesn't need polling.
|
||||
should not lvconvert -m-1 $vg/$lv1 "$dev1"
|
||||
aux enable_dev "$dev2"
|
||||
lvconvert --startpoll $vg/$lv1 || true # wait
|
||||
should lvconvert $vg/$lv1 # wait
|
||||
lvconvert -m2 $vg/$lv1 "$dev1" "$dev2" "$dev4" "$dev3:0" # If the above "should" failed...
|
||||
|
||||
aux wait_for_sync $vg $lv1
|
||||
@@ -113,7 +116,7 @@ LVM_TEST_TAG="kill_me_$PREFIX" lvconvert -m+1 -b $vg/$lv1 "$dev4"
|
||||
# Next convert should fail b/c we can't have 2 at once
|
||||
should not lvconvert -m+1 $vg/$lv1 "$dev5"
|
||||
aux enable_dev "$dev4"
|
||||
lvconvert --startpoll $vg/$lv1 || true # wait
|
||||
should lvconvert $vg/$lv1 # wait
|
||||
lvconvert -m2 $vg/$lv1 # In case the above "should" actually failed
|
||||
|
||||
check mirror $vg $lv1 "$dev3"
|
||||
@@ -156,7 +159,7 @@ lvcreate -aey -l2 --type mirror -m1 -n $lv1 $vg "$dev1" "$dev2" "$dev3:$DEVRANGE
|
||||
lvchange -an $vg/$lv1
|
||||
lvconvert -m+1 $vg/$lv1 "$dev4"
|
||||
lvchange -aey $vg/$lv1
|
||||
lvconvert --startpoll $vg/$lv1 || true # wait
|
||||
should lvconvert $vg/$lv1 # wait
|
||||
check mirror $vg $lv1 "$dev3"
|
||||
check mirror_no_temporaries $vg $lv1
|
||||
lvremove -ff $vg
|
||||
@@ -168,7 +171,7 @@ lvremove -ff $vg
|
||||
lvcreate -aey -l2 --type mirror -m1 -n $lv1 $vg "$dev1" "$dev2" "$dev3:$DEVRANGE"
|
||||
LVM_TEST_TAG="kill_me_$PREFIX" lvconvert -m+1 -b $vg/$lv1 "$dev4"
|
||||
lvconvert -m-1 $vg/$lv1 "$dev4"
|
||||
lvconvert --startpoll $vg/$lv1 || true # wait
|
||||
should lvconvert $vg/$lv1 # wait
|
||||
|
||||
check mirror $vg $lv1 "$dev3"
|
||||
check mirror_no_temporaries $vg $lv1
|
||||
@@ -179,7 +182,7 @@ lvremove -ff $vg
|
||||
lvcreate -aey -l2 --type mirror -m1 -n $lv1 $vg "$dev1" "$dev2" "$dev3:$DEVRANGE"
|
||||
LVM_TEST_TAG="kill_me_$PREFIX" lvconvert -m+2 -b $vg/$lv1 "$dev4" "$dev5"
|
||||
lvconvert -m-1 $vg/$lv1 "$dev4"
|
||||
lvconvert --startpoll $vg/$lv1 || true # wait
|
||||
should lvconvert $vg/$lv1 # wait
|
||||
|
||||
check mirror $vg $lv1 "$dev3"
|
||||
check mirror_no_temporaries $vg $lv1
|
||||
@@ -192,9 +195,9 @@ LVM_TEST_TAG="kill_me_$PREFIX" lvconvert -m+1 -b $vg/$lv1 "$dev4"
|
||||
# FIXME: Extra wait here for mirror upconvert synchronization
|
||||
# otherwise we may fail her on parallel upconvert and downconvert
|
||||
# lvconvert-mirror-updown.sh tests this errornous case separately
|
||||
lvconvert --startpoll $vg/$lv1 || true
|
||||
should lvconvert $vg/$lv1
|
||||
lvconvert -m-1 $vg/$lv1 "$dev2"
|
||||
lvconvert --startpoll $vg/$lv1 || true
|
||||
should lvconvert $vg/$lv1
|
||||
|
||||
check mirror $vg $lv1 "$dev3"
|
||||
check mirror_no_temporaries $vg $lv1
|
||||
@@ -207,9 +210,9 @@ LVM_TEST_TAG="kill_me_$PREFIX" lvconvert -m+1 -b $vg/$lv1 "$dev4"
|
||||
# FIXME: Extra wait here for mirror upconvert synchronization
|
||||
# otherwise we may fail her on parallel upconvert and downconvert
|
||||
# lvconvert-mirror-updown.sh tests this errornous case separately
|
||||
lvconvert --startpoll $vg/$lv1 || true
|
||||
should lvconvert $vg/$lv1
|
||||
lvconvert -m-1 $vg/$lv1 "$dev2"
|
||||
lvconvert --startpoll $vg/$lv1 || true
|
||||
should lvconvert $vg/$lv1
|
||||
|
||||
check mirror $vg $lv1 "$dev3"
|
||||
check mirror_no_temporaries $vg $lv1
|
||||
|
@@ -79,12 +79,15 @@ vgextend $vg "$dev3"
|
||||
lvremove -ff $vg/$lv1
|
||||
|
||||
# Delayed sync to allow for repair during rebuild
|
||||
delay 50
|
||||
delay 60
|
||||
|
||||
# RAID5 single replace during initial sync
|
||||
lvcreate --type raid5 -i 2 -L $RAID_SIZE -n $lv1 $vg "$dev1" "$dev2" "$dev3"
|
||||
aux disable_dev "$dev3"
|
||||
not lvconvert -y --repair $vg/$lv1
|
||||
# FIXME: there is quite big sleep on several 'status' read retries
|
||||
# so over 3sec - it may actually finish full sync
|
||||
# Use 'should' for this test result.
|
||||
should not lvconvert -y --repair $vg/$lv1
|
||||
aux wait_for_sync $vg $lv1
|
||||
lvconvert -y --repair $vg/$lv1
|
||||
vgreduce --removemissing $vg
|
||||
|
@@ -45,7 +45,7 @@ lvchange -an $vg/$lv2 $vg/$lv1 $vg/pool $vg/repair
|
||||
|
||||
# Manual repair steps:
|
||||
# Test swapping - swap out thin-pool's metadata with our repair volume
|
||||
lvconvert -y -f --swapmetadata --poolmetadata $vg/repair $vg/pool
|
||||
lvconvert -y -f --poolmetadata $vg/repair --thinpool $vg/pool
|
||||
|
||||
lvchange -ay $vg/repair
|
||||
|
||||
@@ -74,7 +74,7 @@ not "$LVM_TEST_THIN_DUMP_CMD" "$DM_DEV_DIR/$vg/repair" | tee dump
|
||||
lvchange -an $vg
|
||||
|
||||
# Swap repaired metadata back
|
||||
lvconvert -y -f --swapmetadata --poolmetadata $vg/fixed $vg/pool
|
||||
lvconvert -y -f --poolmetadata $vg/fixed --thinpool $vg/pool
|
||||
|
||||
# Check pool still preserves its original settings
|
||||
check lv_field $vg/pool chunksize "128.00k"
|
||||
@@ -87,7 +87,7 @@ vgchange -ay $vg
|
||||
vgchange -an $vg
|
||||
|
||||
# Put back 'broken' metadata
|
||||
lvconvert -y -f --swapmetadata --poolmetadata $vg/repair $vg/pool
|
||||
lvconvert -y -f --poolmetadata $vg/repair --thinpool $vg/pool
|
||||
|
||||
# Check --repair usage
|
||||
lvconvert -v --repair $vg/pool
|
||||
@@ -98,7 +98,7 @@ lvchange -ay $vg/pool
|
||||
vgchange -an $vg
|
||||
|
||||
# Restore damaged metadata
|
||||
lvconvert -y -f --swapmetadata --poolmetadata $vg/pool_meta0 $vg/pool
|
||||
lvconvert -y -f --poolmetadata $vg/pool_meta0 --thinpool $vg/pool
|
||||
|
||||
# Check lvremove -ff works even with damaged pool
|
||||
lvremove -ff $vg
|
||||
|
@@ -50,11 +50,8 @@ not lvconvert --thin --thinpool $vg/tpool $vg/$lv1
|
||||
# Switch to 'writethrough' - this should be supported
|
||||
lvchange --cachemode writethrough $vg/$lv1
|
||||
|
||||
# FIXME
|
||||
# systemd on fc23 'strikes-in' and unmounts mnt
|
||||
# ATM the reason is unclear (bug in systemd, bad udev rules?)
|
||||
# as a workaround mount again and 'WARN' test
|
||||
should not mount "$DM_DEV_DIR/$vg/$lv1" mnt
|
||||
# Check $lv1 remains mounted (so it's not been unmounted by systemd)
|
||||
not mount "$DM_DEV_DIR/$vg/$lv1" mnt
|
||||
|
||||
lvconvert --thin $vg/$lv1 --originname extorg --thinpool $vg/tpool
|
||||
|
||||
|
@@ -30,8 +30,8 @@ aux wait_for_sync $vg $lv2
|
||||
lvchange -an $vg/$lv1
|
||||
|
||||
# conversion fails for internal volumes
|
||||
not lvconvert --thinpool $vg/${lv1}_rimage_0
|
||||
not lvconvert --yes --thinpool $vg/$lv1 --poolmetadata $vg/${lv2}_rimage_0
|
||||
invalid lvconvert --thinpool $vg/${lv1}_rimage_0
|
||||
invalid lvconvert --yes --thinpool $vg/$lv1 --poolmetadata $vg/${lv2}_rimage_0
|
||||
|
||||
lvconvert --yes --thinpool $vg/$lv1 --poolmetadata $vg/$lv2
|
||||
|
||||
|
@@ -58,13 +58,13 @@ lvchange -an $vg/$lv1
|
||||
# conversion fails for mirror segment type
|
||||
fail lvconvert --thinpool $vg/$lv1
|
||||
# cannot use same LV
|
||||
not lvconvert --yes --thinpool $vg/$lv2 --poolmetadata $vg/$lv2
|
||||
invalid lvconvert --yes --thinpool $vg/$lv2 --poolmetadata $vg/$lv2
|
||||
|
||||
prepare_lvs
|
||||
|
||||
# conversion fails for internal volumes
|
||||
# can't use --readahead with --poolmetadata
|
||||
not lvconvert --thinpool $vg/$lv1 --poolmetadata $vg/$lv2 --readahead 512
|
||||
invalid lvconvert --thinpool $vg/$lv1 --poolmetadata $vg/$lv2 --readahead 512
|
||||
lvconvert --yes --thinpool $vg/$lv1 --poolmetadata $vg/$lv2
|
||||
|
||||
prepare_lvs
|
||||
@@ -81,9 +81,9 @@ grep "Pool zeroing and large" err
|
||||
UUID=$(get lv_field $vg/$lv2 uuid)
|
||||
# Fail is pool is active
|
||||
# TODO maybe detect inactive pool and deactivate
|
||||
fail lvconvert --swapmetadata --yes --poolmetadata $lv2 $vg/$lv1
|
||||
fail lvconvert --yes --thinpool $vg/$lv1 --poolmetadata $lv2
|
||||
lvchange -an $vg
|
||||
lvconvert --swapmetadata --yes --poolmetadata $lv2 $vg/$lv1
|
||||
lvconvert --yes --thinpool $vg/$lv1 --poolmetadata $lv2
|
||||
check lv_field $vg/${lv1}_tmeta uuid "$UUID"
|
||||
lvremove -f $vg
|
||||
|
||||
@@ -96,20 +96,20 @@ lvcreate -L1M -n $lv3 $vg
|
||||
# chunk size is bigger then size of thin pool data
|
||||
fail lvconvert --yes -c 1G --thinpool $vg/$lv3
|
||||
# stripes can't be used with poolmetadata
|
||||
not lvconvert --stripes 2 --thinpool $vg/$lv1 --poolmetadata $vg/$lv2
|
||||
invalid lvconvert --stripes 2 --thinpool $vg/$lv1 --poolmetadata $vg/$lv2
|
||||
# too small metadata (<2M)
|
||||
fail lvconvert --yes -c 64 --thinpool $vg/$lv1 --poolmetadata $vg/$lv3
|
||||
# too small chunk size fails
|
||||
not lvconvert -c 4 --thinpool $vg/$lv1 --poolmetadata $vg/$lv2
|
||||
invalid lvconvert -c 4 --thinpool $vg/$lv1 --poolmetadata $vg/$lv2
|
||||
# too big chunk size fails
|
||||
not lvconvert -c 2G --thinpool $vg/$lv1 --poolmetadata $vg/$lv2
|
||||
invalid lvconvert -c 2G --thinpool $vg/$lv1 --poolmetadata $vg/$lv2
|
||||
# negative chunk size fails
|
||||
not lvconvert -c -256 --thinpool $vg/$lv1 --poolmetadata $vg/$lv2
|
||||
invalid lvconvert -c -256 --thinpool $vg/$lv1 --poolmetadata $vg/$lv2
|
||||
# non multiple of 64KiB fails
|
||||
not lvconvert -c 88 --thinpool $vg/$lv1 --poolmetadata $vg/$lv2
|
||||
invalid lvconvert -c 88 --thinpool $vg/$lv1 --poolmetadata $vg/$lv2
|
||||
|
||||
# cannot use same LV for pool and convertion
|
||||
not lvconvert --yes --thinpool $vg/$lv3 -T $vg/$lv3
|
||||
invalid lvconvert --yes --thinpool $vg/$lv3 -T $vg/$lv3
|
||||
|
||||
# Warning about smaller then suggested
|
||||
lvconvert --yes -c 256 --thinpool $vg/$lv1 --poolmetadata $vg/$lv2 2>&1 | tee err
|
||||
@@ -129,7 +129,7 @@ if test "$TSIZE" = 64T; then
|
||||
lvcreate -L24T -n $lv1 $vg
|
||||
# Warning about bigger then needed (24T data and 16G -> 128K chunk)
|
||||
lvconvert --yes -c 64 --thinpool $vg/$lv1 2>&1 | tee err
|
||||
grep "too small" err
|
||||
grep "WARNING: Chunk size is too small" err
|
||||
lvremove -f $vg
|
||||
fi
|
||||
|
||||
|
@@ -56,7 +56,7 @@ fail lvcreate -l 1 --cachepool pool8 $vg
|
||||
|
||||
# no size specified
|
||||
invalid lvcreate --cachepool pool $vg 2>&1 | tee err
|
||||
# grep "specify either size or extents" err
|
||||
grep "specify either size or extents" err
|
||||
|
||||
# Check nothing has been created yet
|
||||
check vg_field $vg lv_count 0
|
||||
|
@@ -32,15 +32,15 @@ pvcreate "$DM_DEV_DIR/$vg/$lv"
|
||||
vgcreate $vg1 "$DM_DEV_DIR/$vg/$lv"
|
||||
|
||||
lvcreate -l 100%FREE -n $lv1 $vg1
|
||||
check lv_field $vg1/$lv1 size "1024.00t"
|
||||
check lv_field $vg1/$lv1 size "1024.00t" --units t
|
||||
lvresize -f -l 72%VG $vg1/$lv1
|
||||
check lv_field $vg1/$lv1 size "737.28t"
|
||||
check lv_field $vg1/$lv1 size "737.28t" --units t
|
||||
lvremove -ff $vg1/$lv1
|
||||
|
||||
lvcreate -l 100%VG -n $lv1 $vg1
|
||||
check lv_field $vg1/$lv1 size "1024.00t"
|
||||
check lv_field $vg1/$lv1 size "1024.00t" --units t
|
||||
lvresize -f -l 72%VG $vg1/$lv1
|
||||
check lv_field $vg1/$lv1 size "737.28t"
|
||||
check lv_field $vg1/$lv1 size "737.28t" --units t
|
||||
lvremove -ff $vg1/$lv1
|
||||
|
||||
lvremove -ff $vg/$lv
|
||||
|
@@ -21,67 +21,68 @@ aux have_raid4 && segtypes="raid4 raid5"
|
||||
|
||||
aux prepare_vg 6
|
||||
|
||||
_sync() {
|
||||
aux enable_dev "$dev1"
|
||||
|
||||
aux wait_for_sync $vg $lv1
|
||||
test -z "$1" || check raid_leg_status $vg $lv1 $1
|
||||
lvremove --yes $vg/$lv1
|
||||
|
||||
# restore to delay_dev tables for all devices
|
||||
aux restore_from_devtable "$dev1"
|
||||
}
|
||||
|
||||
|
||||
# Delay 1st leg so that rebuilding status characters
|
||||
# can be read before resync finished too quick.
|
||||
aux delay_dev "$dev1" 0 50 $(get first_extent_sector "$dev1")
|
||||
aux delay_dev "$dev1" 0 90 $(get first_extent_sector "$dev1")
|
||||
|
||||
# raid0/raid0_meta don't support resynchronization
|
||||
for r in raid0 raid0_meta
|
||||
do
|
||||
lvcreate --yes --type $r -i 3 -l 1 -n $lv1 $vg
|
||||
lvcreate --type $r -i 3 -l 1 -n $lv1 $vg
|
||||
check raid_leg_status $vg $lv1 "AAA"
|
||||
lvremove --yes $vg/$lv1
|
||||
done
|
||||
|
||||
# raid1 supports resynchronization
|
||||
lvcreate --yes --type raid1 -m 2 -l 2 -n $lv1 $vg
|
||||
lvcreate --type raid1 -m 2 -l 4 -n $lv1 $vg
|
||||
check raid_leg_status $vg $lv1 "aaa"
|
||||
aux wait_for_sync $vg $lv1
|
||||
check raid_leg_status $vg $lv1 "AAA"
|
||||
lvremove --yes $vg/$lv1
|
||||
_sync "AAA"
|
||||
|
||||
# raid1 supports --nosync
|
||||
lvcreate --yes --type raid1 --nosync -m 2 -l 1 -n $lv1 $vg
|
||||
lvcreate --type raid1 --nosync -m 2 -l 1 -n $lv1 $vg
|
||||
check raid_leg_status $vg $lv1 "AAA"
|
||||
lvremove --yes $vg/$lv1
|
||||
|
||||
for r in $segtypes
|
||||
do
|
||||
# raid4/5 support resynchronization
|
||||
lvcreate --yes --type $r -i 3 -l 2 -n $lv1 $vg
|
||||
lvcreate --type $r -i 3 -l 4 -n $lv1 $vg
|
||||
check raid_leg_status $vg $lv1 "aaaa"
|
||||
aux wait_for_sync $vg $lv1
|
||||
check raid_leg_status $vg $lv1 "AAAA"
|
||||
_sync "AAAA"
|
||||
|
||||
# raid4/5 support --nosync
|
||||
lvcreate --yes --type $r --nosync -i 3 -l 1 -n $lv2 $vg
|
||||
lvcreate --type $r --nosync -i 3 -l 1 -n $lv2 $vg
|
||||
check raid_leg_status $vg $lv2 "AAAA"
|
||||
lvremove --yes $vg
|
||||
done
|
||||
|
||||
# raid6 supports resynchronization
|
||||
lvcreate --yes --type raid6 -i 3 -l 2 -n $lv1 $vg
|
||||
lvcreate --type raid6 -i 3 -l 4 -n $lv1 $vg
|
||||
check raid_leg_status $vg $lv1 "aaaaa"
|
||||
aux wait_for_sync $vg $lv1
|
||||
check raid_leg_status $vg $lv1 "AAAAA"
|
||||
lvremove --yes $vg/$lv1
|
||||
_sync "AAAAA"
|
||||
|
||||
# raid6 rejects --nosync; it has to initialize P- and Q-Syndromes
|
||||
not lvcreate --yes --type raid6 --nosync -i 3 -l 1 -n $lv1 $vg
|
||||
not lvcreate --type raid6 --nosync -i 3 -l 1 -n $lv1 $vg
|
||||
|
||||
# raid10 supports resynchronization
|
||||
lvcreate --yes --type raid10 -m 1 -i 3 -l 2 -n $lv1 $vg
|
||||
lvcreate --type raid10 -m 1 -i 3 -l 4 -n $lv1 $vg
|
||||
check raid_leg_status $vg $lv1 "aaaaaa"
|
||||
aux wait_for_sync $vg $lv1
|
||||
check raid_leg_status $vg $lv1 "AAAAAA"
|
||||
aux wait_for_sync $vg $lv1
|
||||
lvremove --yes $vg/$lv1
|
||||
_sync "AAAAAA"
|
||||
|
||||
# raid10 supports --nosync
|
||||
lvcreate --yes --type raid10 --nosync -m 1 -i 3 -l 1 -n $lv1 $vg
|
||||
lvcreate --type raid10 --nosync -m 1 -i 3 -l 1 -n $lv1 $vg
|
||||
check raid_leg_status $vg $lv1 "AAAAAA"
|
||||
aux wait_for_sync $vg $lv1
|
||||
lvremove --yes $vg/$lv1
|
||||
|
||||
vgremove -ff $vg
|
||||
|
@@ -38,7 +38,7 @@ lvchange -an $vg
|
||||
|
||||
lvcreate -L2M -n $lv1 $vg
|
||||
"$LVM_TEST_THIN_RESTORE_CMD" -i data -o "$DM_DEV_DIR/mapper/$vg-$lv1"
|
||||
lvconvert -y --swapmetadata --poolmetadata $vg/$lv1 $vg/pool
|
||||
lvconvert -y --thinpool $vg/pool --poolmetadata $vg/$lv1
|
||||
|
||||
# Cannot resize if set to 0%
|
||||
not lvextend --use-policies --config 'activation{thin_pool_autoextend_percent = 0}' $vg/pool 2>&1 | tee err
|
||||
|
@@ -78,7 +78,7 @@ fake_metadata_ 400 2 >data
|
||||
"$LVM_TEST_THIN_RESTORE_CMD" -i data -o "$DM_DEV_DIR/mapper/$vg-$lv1"
|
||||
|
||||
# Swap volume with restored fake metadata
|
||||
lvconvert -y --chunksize 64k --swapmetadata --poolmetadata $vg/$lv1 $vg/pool
|
||||
lvconvert -y --chunksize 64k --thinpool $vg/pool --poolmetadata $vg/$lv1
|
||||
|
||||
# Not alllowed when thin-pool metadata free space is <75% for 2M meta
|
||||
fail lvcreate -V20 $vg/pool
|
||||
@@ -91,7 +91,7 @@ lvchange -an $vg/pool
|
||||
fake_metadata_ 7400 2 >data
|
||||
"$LVM_TEST_THIN_RESTORE_CMD" -i data -o "$DM_DEV_DIR/mapper/$vg-$lv2"
|
||||
# Swap volume with restored fake metadata
|
||||
lvconvert -y --chunksize 64k --swapmetadata --poolmetadata $vg/$lv2 $vg/pool
|
||||
lvconvert -y --chunksize 64k --thinpool $vg/pool --poolmetadata $vg/$lv2
|
||||
lvchange -ay $vg/pool
|
||||
# Check generated metadata consume more then 88%
|
||||
test "$(meta_percent_)" -gt "88"
|
||||
@@ -138,7 +138,7 @@ lvchange -an $vg/thin $vg/thin2 $vg/pool
|
||||
# Transaction_id is lower by 1 and there are no messages -> ERROR
|
||||
fake_metadata_ 10 0 >data
|
||||
"$LVM_TEST_THIN_RESTORE_CMD" -i data -o "$DM_DEV_DIR/mapper/$vg-$lv1"
|
||||
lvconvert -y --swapmetadata --poolmetadata $vg/$lv1 $vg/pool
|
||||
lvconvert -y --thinpool $vg/pool --poolmetadata $vg/$lv1
|
||||
not vgchange -ay $vg 2>&1 | tee out
|
||||
grep expected out
|
||||
|
||||
@@ -147,7 +147,7 @@ check inactive $vg pool_tmeta
|
||||
# Transaction_id is higher by 1
|
||||
fake_metadata_ 10 3 >data
|
||||
"$LVM_TEST_THIN_RESTORE_CMD" -i data -o "$DM_DEV_DIR/mapper/$vg-$lv1"
|
||||
lvconvert -y --swapmetadata --poolmetadata $vg/$lv1 $vg/pool
|
||||
lvconvert -y --thinpool $vg/pool --poolmetadata $vg/$lv1
|
||||
not vgchange -ay $vg 2>&1 | tee out
|
||||
grep expected out
|
||||
|
||||
@@ -158,7 +158,7 @@ fake_metadata_ 400 2 >data
|
||||
"$LVM_TEST_THIN_RESTORE_CMD" -i data -o "$DM_DEV_DIR/mapper/$vg-$lv1"
|
||||
|
||||
# Swap volume with restored fake metadata
|
||||
lvconvert -y --chunksize 64k --swapmetadata --poolmetadata $vg/$lv1 $vg/pool
|
||||
lvconvert -y --chunksize 64k --thinpool $vg/pool --poolmetadata $vg/$lv1
|
||||
|
||||
vgchange -ay $vg
|
||||
|
||||
@@ -173,7 +173,7 @@ fake_metadata_ 350 2 >data
|
||||
lvchange -ay $vg/$lv1
|
||||
"$LVM_TEST_THIN_RESTORE_CMD" -i data -o "$DM_DEV_DIR/mapper/$vg-$lv1"
|
||||
|
||||
lvconvert -y --chunksize 64k --swapmetadata --poolmetadata $vg/$lv1 $vg/pool
|
||||
lvconvert -y --chunksize 64k --thinpool $vg/pool --poolmetadata $vg/$lv1
|
||||
lvchange -ay $vg/pool $vg/$lv1
|
||||
lvs -a $vg
|
||||
|
||||
|
@@ -54,7 +54,7 @@ mkdir test_mnt
|
||||
|
||||
setup_merge_ $vg1 $lv1
|
||||
mount "$(lvdev_ $vg1 $lv1)" test_mnt
|
||||
lvconvert --mergesnapshot $vg1/$(snap_lv_name_ $lv1)
|
||||
lvconvert --merge $vg1/$(snap_lv_name_ $lv1)
|
||||
umount test_mnt
|
||||
vgchange -an $vg1
|
||||
|
||||
|
@@ -32,7 +32,7 @@ snap_and_merge() {
|
||||
SLEEP_PID=$(aux hold_device_open $vg $lv1 20)
|
||||
|
||||
# initiate background merge
|
||||
lvconvert -b --mergesnapshot $vg/$lv2
|
||||
lvconvert -b --merge $vg/$lv2
|
||||
|
||||
lvs -a -o+lv_merging,lv_merge_failed $vg
|
||||
get lv_field $vg/$lv1 lv_attr | grep "Owi-ao"
|
||||
|
@@ -51,15 +51,15 @@ mkdir test_mnt
|
||||
# test full merge of a single LV
|
||||
setup_merge_ $vg $lv1
|
||||
|
||||
# make sure lvconvert --mergesnapshot requires explicit LV listing
|
||||
not lvconvert --mergesnapshot
|
||||
lvconvert --mergesnapshot $vg/$(snap_lv_name_ $lv1)
|
||||
# make sure lvconvert --merge requires explicit LV listing
|
||||
not lvconvert --merge
|
||||
lvconvert --merge $vg/$(snap_lv_name_ $lv1)
|
||||
lvremove -f $vg/$lv1
|
||||
|
||||
|
||||
# test that an actively merging snapshot may not be removed
|
||||
setup_merge_ $vg $lv1
|
||||
lvconvert -i+100 --mergesnapshot --background $vg/$(snap_lv_name_ $lv1)
|
||||
lvconvert -i+100 --merge --background $vg/$(snap_lv_name_ $lv1)
|
||||
not lvremove -f $vg/$(snap_lv_name_ $lv1)
|
||||
lvremove -f $vg/$lv1
|
||||
|
||||
@@ -67,7 +67,7 @@ lvremove -f $vg/$lv1
|
||||
# "onactivate merge" test
|
||||
setup_merge_ $vg $lv1
|
||||
mount "$(lvdev_ $vg $lv1)" test_mnt
|
||||
lvconvert --mergesnapshot $vg/$(snap_lv_name_ $lv1)
|
||||
lvconvert --merge $vg/$(snap_lv_name_ $lv1)
|
||||
# -- refresh LV while FS is still mounted (merge must not start),
|
||||
# verify 'snapshot-origin' target is still being used
|
||||
lvchange --refresh $vg/$lv1
|
||||
@@ -88,7 +88,7 @@ lvremove -f $vg/$lv1
|
||||
# to make sure preload of origin's metadata is _not_ performed
|
||||
setup_merge_ $vg $lv1
|
||||
mount "$(lvdev_ $vg $lv1)" test_mnt
|
||||
lvconvert --mergesnapshot $vg/$(snap_lv_name_ $lv1)
|
||||
lvconvert --merge $vg/$(snap_lv_name_ $lv1)
|
||||
# -- refresh LV while FS is still mounted (merge must not start),
|
||||
# verify 'snapshot-origin' target is still being used
|
||||
lvchange --refresh $vg/$lv1
|
||||
@@ -99,7 +99,7 @@ lvremove -f $vg/$lv1
|
||||
|
||||
# test multiple snapshot merge; tests copy out that is driven by merge
|
||||
setup_merge_ $vg $lv1 1
|
||||
lvconvert --mergesnapshot $vg/$(snap_lv_name_ $lv1)
|
||||
lvconvert --merge $vg/$(snap_lv_name_ $lv1)
|
||||
lvremove -f $vg/$lv1
|
||||
|
||||
|
||||
@@ -108,7 +108,7 @@ setup_merge_ $vg $lv1
|
||||
setup_merge_ $vg $lv2
|
||||
lvchange --addtag this_is_a_test $vg/$(snap_lv_name_ $lv1)
|
||||
lvchange --addtag this_is_a_test $vg/$(snap_lv_name_ $lv2)
|
||||
lvconvert --mergesnapshot @this_is_a_test
|
||||
lvconvert --merge @this_is_a_test
|
||||
lvs $vg | tee out
|
||||
not grep $(snap_lv_name_ $lv1) out
|
||||
not grep $(snap_lv_name_ $lv2) out
|
||||
|
@@ -16,6 +16,8 @@ SKIP_WITH_LVMPOLLD=1
|
||||
|
||||
export LVM_TEST_THIN_REPAIR_CMD=${LVM_TEST_THIN_REPAIR_CMD-/bin/false}
|
||||
|
||||
. lib/inittest
|
||||
|
||||
mntdir="${PREFIX}mnt with space"
|
||||
mntusedir="${PREFIX}mntuse"
|
||||
|
||||
@@ -32,8 +34,6 @@ is_lv_opened_()
|
||||
test $(get lv_field "$1" lv_device_open --binary) = "1"
|
||||
}
|
||||
|
||||
. lib/inittest
|
||||
|
||||
#
|
||||
# Main
|
||||
#
|
||||
@@ -42,6 +42,30 @@ export MKE2FS_CONFIG="$TESTDIR/lib/mke2fs.conf"
|
||||
|
||||
aux have_thin 1 0 0 || skip
|
||||
|
||||
aux lvmconf "dmeventd/thin_command = \"$PWD/testcmd.sh\""
|
||||
|
||||
# Simple implementation of umount when lvextend fails
|
||||
cat <<- EOF >testcmd.sh
|
||||
#!/bin/sh
|
||||
|
||||
echo "Data: \$DMEVENTD_THIN_POOL_DATA"
|
||||
echo "Metadata: \$DMEVENTD_THIN_POOL_METADATA"
|
||||
|
||||
$TESTDIR/lib/lvextend --use-policies \$1 || {
|
||||
umount "$mntdir" || true
|
||||
umount "$mntusedir" || true
|
||||
return 1
|
||||
}
|
||||
test \$($TESTDIR/lib/lvs -o selected -S "data_percent>95||metadata_percent>95" --noheadings \$1) -eq 0 || {
|
||||
umount "$mntdir" || true
|
||||
umount "$mntusedir" || true
|
||||
return 1
|
||||
}
|
||||
EOF
|
||||
chmod +x testcmd.sh
|
||||
# Show prepared script
|
||||
cat testcmd.sh
|
||||
|
||||
aux prepare_dmeventd
|
||||
|
||||
# Use autoextend percent 0 - so extension fails and triggers umount...
|
||||
|
@@ -71,7 +71,7 @@ aux prepare_thin_metadata 490 1 | tee data
|
||||
"$LVM_TEST_THIN_RESTORE_CMD" -i data -o "$DM_DEV_DIR/mapper/$vg-$lv1"
|
||||
|
||||
# Swap volume with restored fake metadata
|
||||
lvconvert -y --swapmetadata --poolmetadata $vg/$lv1 $vg/pool
|
||||
lvconvert -y --thinpool $vg/pool --poolmetadata $vg/$lv1
|
||||
|
||||
lvchange -ay $vg
|
||||
|
||||
|
@@ -44,7 +44,7 @@ touch mntsnap/test_snap
|
||||
|
||||
lvs -o+tags,thin_id $vg
|
||||
|
||||
lvconvert --mergethin $vg/snap
|
||||
lvconvert --merge $vg/snap
|
||||
|
||||
umount mnt
|
||||
|
||||
@@ -102,12 +102,12 @@ lvcreate -s -n snap $vg/$lv1
|
||||
lvcreate -s -L10 -n oldsnapof_${lv1} $vg/$lv1
|
||||
not lvconvert --merge $vg/snap
|
||||
$MKFS "$DM_DEV_DIR/$vg/oldsnapof_${lv1}"
|
||||
lvconvert --mergesnapshot $vg/oldsnapof_${lv1}
|
||||
lvconvert --merge $vg/oldsnapof_${lv1}
|
||||
fsck -n "$DM_DEV_DIR/$vg/$lv1"
|
||||
check lv_not_exists $vg oldsnapof_${lv1}
|
||||
# Add old snapshot to thin snapshot
|
||||
lvcreate -s -L10 -n oldsnapof_snap $vg/snap
|
||||
lvconvert --mergethin $vg/snap
|
||||
lvconvert --merge $vg/snap
|
||||
lvremove -f $vg/oldsnapof_snap
|
||||
|
||||
vgremove -ff $vg
|
||||
|
@@ -34,7 +34,7 @@ mount "$DM_DEV_DIR/$vg/$lv1" mnt
|
||||
lvcreate -s -n snap $vg/$lv1
|
||||
check lv_field $vg/snap thin_id "3"
|
||||
|
||||
lvconvert --mergethin $vg/snap
|
||||
lvconvert --merge $vg/snap
|
||||
|
||||
umount mnt
|
||||
|
||||
|
@@ -63,6 +63,11 @@ check vg_field $vg max_pv 4294967295
|
||||
# vgchange -l MaxLogicalVolumes
|
||||
check vg_field $vg max_lv 0
|
||||
invalid vgchange -l -128 $vg
|
||||
vgchange -l 4294967295 $vg
|
||||
invalid vgchange -l 4294967296 $vg
|
||||
invalid vgchange -l 18446744073709551615 $vg
|
||||
invalid vgchange -l 18446744073709551616 $vg
|
||||
check vg_field $vg max_lv 4294967295
|
||||
vgchange -l 128 $vg
|
||||
check vg_field $vg max_lv 128
|
||||
|
||||
|
@@ -23,6 +23,7 @@ lvcreate -l 1 -n lv1 $vg "$dev1"
|
||||
invalid vgextend
|
||||
# --metadatacopies => use --pvmetadatacopies
|
||||
invalid vgextend --metadatacopies 3 $vg "$dev1" 2>&1 | tee out
|
||||
grep -- "use --pvmetadatacopies" out
|
||||
|
||||
# VG name should exist
|
||||
fail vgextend --restoremissing $vg-invalid "$dev1"
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
#
|
||||
# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||
# Copyright (C) 2004-2012 Red Hat, Inc. All rights reserved.
|
||||
@@ -77,7 +76,6 @@ SOURCES2 =\
|
||||
|
||||
TARGETS =\
|
||||
.commands \
|
||||
command-lines.h \
|
||||
liblvm2cmd.a \
|
||||
lvm
|
||||
|
||||
@@ -101,8 +99,7 @@ LIB_VERSION = $(LIB_VERSION_LVM)
|
||||
CLEAN_TARGETS = liblvm2cmd.$(LIB_SUFFIX) $(TARGETS_DM) \
|
||||
liblvm2cmd.$(LIB_SUFFIX).$(LIB_VERSION) lvm-static.o \
|
||||
liblvm2cmd-static.a dmsetup.static lvm.static \
|
||||
$(LDDEPS) .exported_symbols_generated \
|
||||
ccmd command-lines.h command-lines-count.h
|
||||
$(LDDEPS) .exported_symbols_generated
|
||||
|
||||
ifeq ("@CMDLIB@", "yes")
|
||||
TARGETS += liblvm2cmd.$(LIB_SUFFIX).$(LIB_VERSION)
|
||||
@@ -141,8 +138,6 @@ all: device-mapper
|
||||
CFLAGS_lvm.o += $(EXTRA_EXEC_CFLAGS)
|
||||
CFLAGS_lvmcmdline.o += $(VALGRIND_CFLAGS)
|
||||
|
||||
INCLUDES += -I$(top_builddir)/tools
|
||||
|
||||
lvm: $(OBJECTS) lvm.o $(top_builddir)/lib/liblvm-internal.a
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) $(EXTRA_EXEC_LDFLAGS) $(ELDFLAGS) -o $@ $(OBJECTS) lvm.o \
|
||||
$(LVMLIBS) $(READLINE_LIBS) $(LIBS) -rdynamic
|
||||
@@ -176,36 +171,6 @@ liblvm2cmd.$(LIB_SUFFIX).$(LIB_VERSION): liblvm2cmd.$(LIB_SUFFIX)
|
||||
$(CC) -E -P $(srcdir)/cmdnames.h 2> /dev/null | \
|
||||
egrep -v '^ *(|#.*|config|devtypes|dumpconfig|formats|fullreport|help|lastlog|lvpoll|pvdata|segtypes|systemid|tags|version) *$$' > .commands
|
||||
|
||||
ccmd: $(srcdir)/create-commands.c
|
||||
$(CC) $(srcdir)/create-commands.c -o ccmd
|
||||
|
||||
.DELETE_ON_ERROR:
|
||||
|
||||
command-lines.h: $(srcdir)/command-lines.in ccmd
|
||||
$(top_builddir)/tools/ccmd --output struct $(srcdir)/command-lines.in > $@
|
||||
|
||||
command-lines-count.h: $(srcdir)/command-lines.in ccmd
|
||||
$(top_builddir)/tools/ccmd --output count $(srcdir)/command-lines.in > $@
|
||||
|
||||
# move properly to configure
|
||||
WC = /usr/bin/wc
|
||||
GREP = /bin/grep
|
||||
SORT = /bin/sort
|
||||
CUT = /bin/cut
|
||||
SED = /bin/sed
|
||||
|
||||
# FIXME Add licence text from template file
|
||||
command-lines-count-new.h: $(srcdir)/command-lines.in ccmd Makefile
|
||||
set -o pipefail && \
|
||||
(echo -n "#define COMMAND_COUNT " && \
|
||||
$(GREP) '^ID:' $(srcdir)/command-lines.in | $(WC) -l && \
|
||||
echo -e "enum {\n\tno_CMD," && \
|
||||
$(GREP) '^ID:' $(srcdir)/command-lines.in | $(CUT) -d\ -f2 | $(SORT) -u | $(SED) 's/\(.*\)/\t&_CMD,/' && \
|
||||
echo -e "\tCOMMAND_ID_COUNT,\n};" \
|
||||
) > $@
|
||||
|
||||
$(SOURCES:%.c=%.d): command-lines.h command-lines-count.h
|
||||
|
||||
ifneq ("$(CFLOW_CMD)", "")
|
||||
CFLOW_SOURCES = $(addprefix $(srcdir)/, $(SOURCES))
|
||||
-include $(top_builddir)/libdm/libdevmapper.cflow
|
||||
|
1576
tools/args.h
1576
tools/args.h
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
207
tools/command.h
207
tools/command.h
@@ -1,207 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2015 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 _LVM_COMMAND_H
|
||||
#define _LVM_COMMAND_H
|
||||
|
||||
struct cmd_context;
|
||||
|
||||
/* old per-command-name function */
|
||||
typedef int (*command_fn) (struct cmd_context *cmd, int argc, char **argv);
|
||||
|
||||
/* new per-command-line-id functions */
|
||||
typedef int (*command_line_fn) (struct cmd_context *cmd, int argc, char **argv);
|
||||
|
||||
struct command_function {
|
||||
int command_line_enum;
|
||||
command_line_fn fn;
|
||||
};
|
||||
|
||||
struct command_name {
|
||||
const char *name;
|
||||
const char *desc; /* general command description from commands.h */
|
||||
unsigned int flags;
|
||||
|
||||
/* union of {required,optional}_opt_args for all commands with this name */
|
||||
int valid_args[ARG_COUNT];
|
||||
int num_args;
|
||||
};
|
||||
|
||||
/*
|
||||
* Command defintion
|
||||
*
|
||||
* A command is defined in terms of a command name,
|
||||
* required options (+args), optional options (+args),
|
||||
* required positional args, optional positional args.
|
||||
*
|
||||
* A positional arg always has non-zero pos_arg.def.types.
|
||||
* The first positional arg has pos_arg.pos of 1.
|
||||
*/
|
||||
|
||||
/* arg_def flags */
|
||||
#define ARG_DEF_FLAG_NEW_VG 1 << 0
|
||||
#define ARG_DEF_FLAG_NEW_LV 1 << 1
|
||||
#define ARG_DEF_FLAG_MAY_REPEAT 1 << 2
|
||||
|
||||
static inline int val_bit_is_set(uint64_t val_bits, int val_enum)
|
||||
{
|
||||
return (val_bits & (1 << val_enum)) ? 1 : 0;
|
||||
}
|
||||
|
||||
static inline uint64_t val_enum_to_bit(int val_enum)
|
||||
{
|
||||
return (1ULL << val_enum);
|
||||
}
|
||||
|
||||
static inline int lvp_bit_is_set(uint64_t lvp_bits, int lvp_enum)
|
||||
{
|
||||
return (lvp_bits & (1 << lvp_enum)) ? 1 : 0;
|
||||
}
|
||||
|
||||
static inline uint64_t lvp_enum_to_bit(int lvp_enum)
|
||||
{
|
||||
return (1ULL << lvp_enum);
|
||||
}
|
||||
|
||||
static inline int lvt_bit_is_set(uint64_t lvt_bits, int lvt_enum)
|
||||
{
|
||||
return (lvt_bits & (1 << lvt_enum)) ? 1 : 0;
|
||||
}
|
||||
|
||||
static inline uint64_t lvt_enum_to_bit(int lvt_enum)
|
||||
{
|
||||
return (1ULL << lvt_enum);
|
||||
}
|
||||
|
||||
/* Description a value that follows an option or exists in a position. */
|
||||
|
||||
struct arg_def {
|
||||
uint64_t val_bits; /* bits of x_VAL, can be multiple for pos_arg */
|
||||
uint64_t lvt_bits; /* lvt_enum_to_bit(x_LVT) for lv_VAL, can be multiple */
|
||||
uint64_t num; /* a literal number for conststr_VAL */
|
||||
const char *str; /* a literal string for constnum_VAL */
|
||||
uint32_t flags; /* ARG_DEF_FLAG_ */
|
||||
};
|
||||
|
||||
/* Description of an option and the value that follows it. */
|
||||
|
||||
struct opt_arg {
|
||||
int opt; /* option, e.g. foo_ARG */
|
||||
struct arg_def def; /* defines accepted values */
|
||||
};
|
||||
|
||||
/* Description of a position and the value that exists there. */
|
||||
|
||||
struct pos_arg {
|
||||
int pos; /* position, e.g. first is 1 */
|
||||
struct arg_def def; /* defines accepted values */
|
||||
};
|
||||
|
||||
/*
|
||||
*
|
||||
* Commands using a given command definition must follow a set
|
||||
* of rules. If a given command+LV matches the conditions in
|
||||
* opts/lvt_bits/lvp_bits, then the checks are applied.
|
||||
* If one condition is not met, the checks are not applied.
|
||||
* If no conditions are set, the checks are always applied.
|
||||
*/
|
||||
|
||||
#define RULE_INVALID 1
|
||||
#define RULE_REQUIRE 2
|
||||
|
||||
struct cmd_rule {
|
||||
int *opts; /* if any option in this list is set, the check may apply */
|
||||
uint64_t lvt_bits; /* if LV has one of these types (lvt_enum_to_bit), the check may apply */
|
||||
uint64_t lvp_bits; /* if LV has all of these properties (lvp_enum_to_bit), the check may apply */
|
||||
|
||||
int *check_opts; /* used options must [not] be in this list */
|
||||
uint64_t check_lvt_bits; /* LV must [not] have one of these type */
|
||||
uint64_t check_lvp_bits; /* LV must [not] have all of these properties */
|
||||
|
||||
uint32_t rule; /* RULE_INVALID, RULE_REQUIRE: check values must [not] be true */
|
||||
int opts_count; /* entries in opts[] */
|
||||
int check_opts_count; /* entries in check_opts[] */
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
* CMD_RO_ARGS needs to accomodate a list of options,
|
||||
* of which one is required after which the rest are
|
||||
* optional.
|
||||
*/
|
||||
#define CMD_RO_ARGS 64 /* required opt args */
|
||||
#define CMD_OO_ARGS 150 /* optional opt args */
|
||||
#define CMD_RP_ARGS 8 /* required positional args */
|
||||
#define CMD_OP_ARGS 8 /* optional positional args */
|
||||
#define CMD_IO_ARGS 8 /* ignore opt args */
|
||||
#define CMD_MAX_RULES 32 /* max number of rules per command def */
|
||||
|
||||
/*
|
||||
* one or more from required_opt_args is required,
|
||||
* then the rest are optional.
|
||||
*/
|
||||
#define CMD_FLAG_ONE_REQUIRED_OPT 1
|
||||
#define CMD_FLAG_SECONDARY_SYNTAX 2
|
||||
|
||||
/* a register of the lvm commands */
|
||||
struct command {
|
||||
const char *name;
|
||||
const char *desc; /* specific command description from command-lines.h */
|
||||
const char *usage; /* excludes common options like --help, --debug */
|
||||
const char *usage_common; /* includes commmon options like --help, --debug */
|
||||
const char *command_line_id;
|
||||
int command_line_enum; /* <command_line_id>_CMD */
|
||||
|
||||
struct command_name *cname;
|
||||
|
||||
command_fn fn; /* old style */
|
||||
struct command_function *functions; /* new style */
|
||||
|
||||
unsigned int flags; /* copied from command_name.flags from commands.h */
|
||||
|
||||
unsigned int cmd_flags; /* CMD_FLAG_ */
|
||||
|
||||
/* definitions of opt/pos args */
|
||||
|
||||
/* required args following an --opt */
|
||||
struct opt_arg required_opt_args[CMD_RO_ARGS];
|
||||
|
||||
/* optional args following an --opt */
|
||||
struct opt_arg optional_opt_args[CMD_OO_ARGS];
|
||||
|
||||
/* required positional args */
|
||||
struct pos_arg required_pos_args[CMD_RP_ARGS];
|
||||
|
||||
/* optional positional args */
|
||||
struct pos_arg optional_pos_args[CMD_OP_ARGS];
|
||||
|
||||
/* unused opt args, are ignored instead of causing an error */
|
||||
struct opt_arg ignore_opt_args[CMD_IO_ARGS];
|
||||
|
||||
struct cmd_rule rules[CMD_MAX_RULES];
|
||||
|
||||
int ro_count;
|
||||
int oo_count;
|
||||
int rp_count;
|
||||
int op_count;
|
||||
int io_count;
|
||||
|
||||
/* used for processing current position */
|
||||
int pos_count;
|
||||
|
||||
int rule_count;
|
||||
};
|
||||
|
||||
#endif
|
1428
tools/commands.h
1428
tools/commands.h
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
131
tools/dmsetup.c
131
tools/dmsetup.c
@@ -4957,16 +4957,8 @@ static char *_get_abspath(const char *path)
|
||||
return _path;
|
||||
}
|
||||
|
||||
static int _stats_create_file(CMD_ARGS)
|
||||
static int _stats_check_filemap_switches(void)
|
||||
{
|
||||
const char *alias, *program_id = DM_STATS_PROGRAM_ID;
|
||||
const char *bounds_str = _string_args[BOUNDS_ARG];
|
||||
uint64_t *regions, *region, count = 0;
|
||||
struct dm_histogram *bounds = NULL;
|
||||
char *path, *abspath = NULL;
|
||||
struct dm_stats *dms = NULL;
|
||||
int group, fd = -1, precise;
|
||||
|
||||
if (_switches[AREAS_ARG] || _switches[AREA_SIZE_ARG]) {
|
||||
log_error("--filemap is incompatible with --areas and --area-size.");
|
||||
return 0;
|
||||
@@ -4997,6 +4989,27 @@ static int _stats_create_file(CMD_ARGS)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _stats_create_file(CMD_ARGS)
|
||||
{
|
||||
const char *alias, *program_id = DM_STATS_PROGRAM_ID;
|
||||
const char *bounds_str = _string_args[BOUNDS_ARG];
|
||||
uint64_t *regions, *region, count = 0;
|
||||
struct dm_histogram *bounds = NULL;
|
||||
char *path, *abspath = NULL;
|
||||
struct dm_stats *dms = NULL;
|
||||
int group, fd = -1, precise;
|
||||
|
||||
if (names) {
|
||||
err("Device names are not compatibile with --filemap.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!_stats_check_filemap_switches())
|
||||
return 0;
|
||||
|
||||
/* _stats_create_file does not use _process_all() */
|
||||
if (!argc) {
|
||||
log_error("--filemap requires a file path argument");
|
||||
@@ -5598,6 +5611,105 @@ out:
|
||||
return r;
|
||||
}
|
||||
|
||||
static int _stats_update_file(CMD_ARGS)
|
||||
{
|
||||
uint64_t group_id, *region, *regions, count = 0;
|
||||
const char *program_id = DM_STATS_PROGRAM_ID;
|
||||
struct dm_stats *dms;
|
||||
char *path, *abspath;
|
||||
int fd = -1;
|
||||
|
||||
if (names) {
|
||||
err("Device names are not compatibile with update_filemap.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!_stats_check_filemap_switches())
|
||||
return 0;
|
||||
|
||||
/* _stats_update_file does not use _process_all() */
|
||||
if (!argc) {
|
||||
log_error("update_filemap requires a file path argument");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!_switches[GROUP_ID_ARG]) {
|
||||
err("--groupid is required to update a filemap group.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
path = argv[0];
|
||||
|
||||
if (!(abspath = _get_abspath(path))) {
|
||||
log_error("Could not canonicalize file name: %s", path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
group_id = (uint64_t) _int_args[GROUP_ID_ARG];
|
||||
|
||||
if (_switches[PROGRAM_ID_ARG])
|
||||
program_id = _string_args[PROGRAM_ID_ARG];
|
||||
if (!strlen(program_id) && !_switches[FORCE_ARG])
|
||||
program_id = DM_STATS_PROGRAM_ID;
|
||||
|
||||
if (!(dms = dm_stats_create(DM_STATS_PROGRAM_ID)))
|
||||
goto_bad;
|
||||
|
||||
fd = open(abspath, O_RDONLY);
|
||||
|
||||
if (fd < 0) {
|
||||
log_error("Could not open %s for reading", abspath);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!dm_stats_bind_from_fd(dms, fd))
|
||||
goto_bad;
|
||||
|
||||
if (!strlen(program_id))
|
||||
/* force creation of a region with no id */
|
||||
dm_stats_set_program_id(dms, 1, NULL);
|
||||
|
||||
regions = dm_stats_update_regions_from_fd(dms, fd, group_id);
|
||||
|
||||
if (close(fd))
|
||||
log_error("Error closing %s", abspath);
|
||||
|
||||
fd = -1;
|
||||
|
||||
if (!regions) {
|
||||
log_error("Could not update regions from file %s", abspath);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
for (region = regions; *region != DM_STATS_REGIONS_ALL; region++)
|
||||
count++;
|
||||
|
||||
if (group_id != regions[0]) {
|
||||
printf("Group ID changed from " FMTu64 " to " FMTu64,
|
||||
group_id, regions[0]);
|
||||
group_id = regions[0];
|
||||
}
|
||||
|
||||
printf("%s: Updated group ID " FMTu64 " with "FMTu64" region(s).\n",
|
||||
path, group_id, count);
|
||||
|
||||
dm_free(regions);
|
||||
dm_free(abspath);
|
||||
dm_stats_destroy(dms);
|
||||
return 1;
|
||||
|
||||
bad:
|
||||
dm_free(abspath);
|
||||
|
||||
if ((fd > -1) && close(fd))
|
||||
log_error("Error closing %s", path);
|
||||
|
||||
if (dms)
|
||||
dm_stats_destroy(dms);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Command dispatch tables and usage.
|
||||
*/
|
||||
@@ -5678,6 +5790,7 @@ static struct command _stats_subcommands[] = {
|
||||
{"print", PRINT_OPTS, 0, -1, 1, 0, _stats_print},
|
||||
{"report", REPORT_OPTS "[<device...>]", 0, -1, 1, 0, _stats_report},
|
||||
{"ungroup", "--groupid <id> " UNGROUP_OPTS, 1, -1, 1, 0, _stats_ungroup},
|
||||
{"update_filemap", "--groupid <id> <file_path>", 1, 1, 0, 0, _stats_update_file},
|
||||
{"version", "", 0, -1, 1, 0, _version},
|
||||
{NULL, NULL, 0, 0, 0, 0, NULL}
|
||||
};
|
||||
|
@@ -1,56 +0,0 @@
|
||||
|
||||
/*
|
||||
* NULL in the last arg can be replaced with actual
|
||||
* calls to the lv_is_prop() function when those
|
||||
* become functions (are #define now), take uniform
|
||||
* args (e.g. some take cmd others don't), and are
|
||||
* exposed in tools.h
|
||||
*
|
||||
* Until then, the lv_is_prop() functions are
|
||||
* called indirectly through _lv_is_prop().
|
||||
*/
|
||||
|
||||
lvp(LVP_NONE, "", NULL) /* enum value 0 means none */
|
||||
lvp(is_locked_LVP, "lv_is_locked", NULL)
|
||||
lvp(is_partial_LVP, "lv_is_partial", NULL)
|
||||
lvp(is_virtual_LVP, "lv_is_virtual", NULL)
|
||||
lvp(is_merging_LVP, "lv_is_merging", NULL)
|
||||
lvp(is_merging_origin_LVP, "lv_is_merging_origin", NULL)
|
||||
lvp(is_converting_LVP, "lv_is_converting", NULL)
|
||||
lvp(is_external_origin_LVP, "lv_is_external_origin", NULL)
|
||||
lvp(is_virtual_origin_LVP, "lv_is_virtual_origin", NULL)
|
||||
lvp(is_not_synced_LVP, "lv_is_not_synced", NULL)
|
||||
lvp(is_pending_delete_LVP, "lv_is_pending_delete", NULL)
|
||||
lvp(is_error_when_full_LVP, "lv_is_error_when_full", NULL)
|
||||
lvp(is_pvmove_LVP, "lv_is_pvmove", NULL)
|
||||
lvp(is_removed_LVP, "lv_is_removed", NULL)
|
||||
lvp(is_vg_writable_LVP, "lv_is_vg_writable", NULL)
|
||||
|
||||
/* kinds of sub LV */
|
||||
lvp(is_thinpool_data_LVP, "lv_is_thinpool_data", NULL)
|
||||
lvp(is_thinpool_metadata_LVP, "lv_is_thinpool_metadata", NULL)
|
||||
lvp(is_cachepool_data_LVP, "lv_is_cachepool_data", NULL)
|
||||
lvp(is_cachepool_metadata_LVP, "lv_is_cachepool_metadata", NULL)
|
||||
lvp(is_mirror_image_LVP, "lv_is_mirror_image", NULL)
|
||||
lvp(is_mirror_log_LVP, "lv_is_mirror_log", NULL)
|
||||
lvp(is_raid_image_LVP, "lv_is_raid_image", NULL)
|
||||
lvp(is_raid_metadata_LVP, "lv_is_raid_metadata", NULL)
|
||||
|
||||
/*
|
||||
* is_thick_origin should be used instead of is_origin
|
||||
* is_thick_snapshot is generally used as LV_snapshot from lv_types.h
|
||||
*/
|
||||
lvp(is_origin_LVP, "lv_is_origin", NULL)
|
||||
lvp(is_thick_origin_LVP, "lv_is_thick_origin", NULL)
|
||||
lvp(is_thick_snapshot_LVP, "lv_is_thick_snapshot", NULL)
|
||||
lvp(is_thin_origin_LVP, "lv_is_thin_origin", NULL)
|
||||
lvp(is_thin_snapshot_LVP, "lv_is_thin_snapshot", NULL)
|
||||
|
||||
lvp(is_cache_origin_LVP, "lv_is_cache_origin", NULL)
|
||||
lvp(is_merging_cow_LVP, "lv_is_merging_cow", NULL)
|
||||
lvp(is_cow_covering_origin_LVP, "lv_is_cow_covering_origin", NULL)
|
||||
lvp(is_visible_LVP, "lv_is_visible", NULL)
|
||||
lvp(is_historical_LVP, "lv_is_historical", NULL)
|
||||
lvp(is_raid_with_tracking_LVP, "lv_is_raid_with_tracking", NULL)
|
||||
lvp(LVP_COUNT, "", NULL)
|
||||
|
@@ -1,34 +0,0 @@
|
||||
|
||||
|
||||
/*
|
||||
* LV types used in command definitions. The type strings are used
|
||||
* as LV suffixes, e.g. LV_type or LV_type1_type2.
|
||||
*
|
||||
* The final NULL arg can be replaced with lv_is_type() functions
|
||||
* if the current lv_is_type #defines become functions and are
|
||||
* moved to tools.h
|
||||
*
|
||||
* Until then, the lv_is_type() functions are called indirectly
|
||||
* through _lv_is_type().
|
||||
*/
|
||||
|
||||
lvt(LVT_NONE, "", NULL)
|
||||
lvt(linear_LVT, "linear", NULL)
|
||||
lvt(striped_LVT, "striped", NULL)
|
||||
lvt(snapshot_LVT, "snapshot", NULL) /* lv_is_cow, lv_is_thick_snapshot */
|
||||
lvt(thin_LVT, "thin", NULL)
|
||||
lvt(thinpool_LVT, "thinpool", NULL)
|
||||
lvt(cache_LVT, "cache", NULL)
|
||||
lvt(cachepool_LVT, "cachepool", NULL)
|
||||
lvt(mirror_LVT, "mirror", NULL)
|
||||
lvt(raid_LVT, "raid", NULL)
|
||||
lvt(raid0_LVT, "raid0", NULL)
|
||||
lvt(raid1_LVT, "raid1", NULL)
|
||||
lvt(raid4_LVT, "raid4", NULL)
|
||||
lvt(raid5_LVT, "raid5", NULL)
|
||||
lvt(raid6_LVT, "raid6", NULL)
|
||||
lvt(raid10_LVT, "raid10", NULL)
|
||||
lvt(error_LVT, "error", NULL)
|
||||
lvt(zero_LVT, "zero", NULL)
|
||||
lvt(LVT_COUNT, "", NULL)
|
||||
|
928
tools/lvchange.c
928
tools/lvchange.c
File diff suppressed because it is too large
Load Diff
4518
tools/lvconvert.c
4518
tools/lvconvert.c
File diff suppressed because it is too large
Load Diff
@@ -58,5 +58,5 @@ int lvdisplay(struct cmd_context *cmd, int argc, char **argv)
|
||||
return EINVALID_CMD_LINE;
|
||||
}
|
||||
|
||||
return process_each_lv(cmd, argc, argv, NULL, NULL, 0, NULL, NULL, &_lvdisplay_single);
|
||||
return process_each_lv(cmd, argc, argv, NULL, NULL, 0, NULL, &_lvdisplay_single);
|
||||
}
|
||||
|
37
tools/lvm.c
37
tools/lvm.c
@@ -45,9 +45,9 @@ static char *_list_cmds(const char *text, int state)
|
||||
len = strlen(text);
|
||||
}
|
||||
|
||||
while (i < _cmdline->num_command_names)
|
||||
if (!strncmp(text, _cmdline->command_names[i++].name, len))
|
||||
return strdup(_cmdline->command_names[i - 1].name);
|
||||
while (i < _cmdline->num_commands)
|
||||
if (!strncmp(text, _cmdline->commands[i++].name, len))
|
||||
return strdup(_cmdline->commands[i - 1].name);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -57,7 +57,7 @@ static char *_list_args(const char *text, int state)
|
||||
{
|
||||
static int match_no = 0;
|
||||
static size_t len = 0;
|
||||
static struct command_name *cname;
|
||||
static struct command *com;
|
||||
|
||||
/* Initialise if this is a new completion attempt */
|
||||
if (!state) {
|
||||
@@ -65,40 +65,40 @@ static char *_list_args(const char *text, int state)
|
||||
int j;
|
||||
|
||||
match_no = 0;
|
||||
cname = NULL;
|
||||
com = NULL;
|
||||
len = strlen(text);
|
||||
|
||||
/* Find start of first word in line buffer */
|
||||
while (isspace(*s))
|
||||
s++;
|
||||
|
||||
/* Look for word in list of command names */
|
||||
for (j = 0; j < _cmdline->num_command_names; j++) {
|
||||
/* Look for word in list of commands */
|
||||
for (j = 0; j < _cmdline->num_commands; j++) {
|
||||
const char *p;
|
||||
char *q = s;
|
||||
|
||||
p = _cmdline->command_names[j].name;
|
||||
p = _cmdline->commands[j].name;
|
||||
while (*p == *q) {
|
||||
p++;
|
||||
q++;
|
||||
}
|
||||
if ((!*p) && *q == ' ') {
|
||||
cname = _cmdline->command_names + j;
|
||||
com = _cmdline->commands + j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!cname)
|
||||
if (!com)
|
||||
return NULL;
|
||||
|
||||
/* Short form arguments */
|
||||
if (len < 3) {
|
||||
while (match_no < cname->num_args) {
|
||||
while (match_no < com->num_args) {
|
||||
char s[3];
|
||||
char c;
|
||||
if (!(c = (_cmdline->arg_props +
|
||||
cname->valid_args[match_no++])->short_arg))
|
||||
com->valid_args[match_no++])->short_arg))
|
||||
continue;
|
||||
|
||||
sprintf(s, "-%c", c);
|
||||
@@ -108,13 +108,13 @@ static char *_list_args(const char *text, int state)
|
||||
}
|
||||
|
||||
/* Long form arguments */
|
||||
if (match_no < cname->num_args)
|
||||
match_no = cname->num_args;
|
||||
if (match_no < com->num_args)
|
||||
match_no = com->num_args;
|
||||
|
||||
while (match_no - cname->num_args < cname->num_args) {
|
||||
while (match_no - com->num_args < com->num_args) {
|
||||
const char *l;
|
||||
l = (_cmdline->arg_props +
|
||||
cname->valid_args[match_no++ - cname->num_args])->long_arg;
|
||||
com->valid_args[match_no++ - com->num_args])->long_arg;
|
||||
if (*(l + 2) && !strncmp(text, l, len))
|
||||
return strdup(l);
|
||||
}
|
||||
@@ -210,7 +210,7 @@ int lvm_shell(struct cmd_context *cmd, struct cmdline_context *cmdline)
|
||||
{
|
||||
log_report_t saved_log_report_state = log_get_report_state();
|
||||
char *orig_command_log_selection = NULL;
|
||||
int is_lastlog_cmd = 0, argc, ret, i;
|
||||
int is_lastlog_cmd = 0, argc, ret;
|
||||
char *input = NULL, *args[MAX_ARGS], **argv;
|
||||
|
||||
rl_readline_name = "lvm";
|
||||
@@ -262,9 +262,6 @@ int lvm_shell(struct cmd_context *cmd, struct cmdline_context *cmdline)
|
||||
|
||||
add_history(input);
|
||||
|
||||
for (i = 0; i < MAX_ARGS; i++)
|
||||
args[i] = NULL;
|
||||
|
||||
argv = args;
|
||||
|
||||
if (lvm_split(input, &argc, argv, MAX_ARGS) == MAX_ARGS) {
|
||||
|
@@ -19,11 +19,10 @@
|
||||
struct cmd_context;
|
||||
|
||||
struct cmdline_context {
|
||||
struct arg_props *arg_props;
|
||||
struct command *commands;
|
||||
int num_commands;
|
||||
struct command_name *command_names;
|
||||
int num_command_names;
|
||||
struct arg_props *arg_props;
|
||||
struct command *commands;
|
||||
int num_commands;
|
||||
int commands_size;
|
||||
};
|
||||
|
||||
int lvm2_main(int argc, char **argv);
|
||||
|
@@ -30,12 +30,12 @@ void *cmdlib_lvm2_init(unsigned static_compile)
|
||||
{
|
||||
struct cmd_context *cmd;
|
||||
|
||||
lvm_register_commands();
|
||||
|
||||
init_is_static(static_compile);
|
||||
if (!(cmd = init_lvm(1, 1)))
|
||||
return NULL;
|
||||
|
||||
lvm_register_commands();
|
||||
|
||||
return (void *) cmd;
|
||||
}
|
||||
|
||||
@@ -83,7 +83,10 @@ int lvm2_run(void *handle, const char *cmdline)
|
||||
memlock_inc_daemon(cmd);
|
||||
} else if (!strcmp(cmdline, "_memlock_dec"))
|
||||
memlock_dec_daemon(cmd);
|
||||
else
|
||||
else if (!strcmp(cmdline, "_dmeventd_thin_command")) {
|
||||
if (setenv(cmdline, find_config_tree_str(cmd, dmeventd_thin_command_CFG, NULL), 1))
|
||||
ret = ECMD_FAILED;
|
||||
} else
|
||||
ret = lvm_run_command(cmd, argc, argv);
|
||||
|
||||
out:
|
||||
@@ -95,10 +98,9 @@ int lvm2_run(void *handle, const char *cmdline)
|
||||
return ret;
|
||||
}
|
||||
|
||||
void lvm2_disable_dmeventd_monitoring(void *handle) {
|
||||
init_dmeventd_monitor(DMEVENTD_MONITOR_IGNORE);
|
||||
init_ignore_suspended_devices(1);
|
||||
init_disable_dmeventd_monitoring(1); /* Lock settings */
|
||||
void lvm2_disable_dmeventd_monitoring(void *handle)
|
||||
{
|
||||
init_run_by_dmeventd((struct cmd_context *) handle);
|
||||
}
|
||||
|
||||
void lvm2_log_level(void *handle, int level)
|
||||
|
1490
tools/lvmcmdline.c
1490
tools/lvmcmdline.c
File diff suppressed because it is too large
Load Diff
@@ -27,5 +27,5 @@ int lvremove(struct cmd_context *cmd, int argc, char **argv)
|
||||
cmd->include_historical_lvs = 1;
|
||||
|
||||
return process_each_lv(cmd, argc, argv, NULL, NULL, READ_FOR_UPDATE, NULL,
|
||||
NULL, &lvremove_single);
|
||||
&lvremove_single);
|
||||
}
|
||||
|
@@ -119,5 +119,5 @@ int lvscan(struct cmd_context *cmd, int argc, char **argv)
|
||||
*/
|
||||
}
|
||||
|
||||
return process_each_lv(cmd, argc, argv, NULL, NULL, 0, NULL, NULL, &lvscan_single);
|
||||
return process_each_lv(cmd, argc, argv, NULL, NULL, 0, NULL, &lvscan_single);
|
||||
}
|
||||
|
@@ -136,11 +136,14 @@ static int _check_merging_origin(const struct logical_volume *lv,
|
||||
case SEG_STATUS_SNAPSHOT:
|
||||
break;
|
||||
default:
|
||||
/* When inactive, it's technically merging */
|
||||
if (status->info_ok && !status->info.exists)
|
||||
break;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Origin is gone */
|
||||
log_debug_activation("Merge is progress, reporting merged LV %s.",
|
||||
log_debug_activation("Merge is in progress, reporting merged LV %s.",
|
||||
display_lvname(lv->snapshot->lv));
|
||||
*merged = 1;
|
||||
|
||||
@@ -542,14 +545,14 @@ static int _report_all_in_vg(struct cmd_context *cmd, struct processing_handle *
|
||||
r = _vgs_single(cmd, vg->name, vg, handle);
|
||||
break;
|
||||
case LVS:
|
||||
r = process_each_lv_in_vg(cmd, vg, NULL, NULL, 0, handle, NULL,
|
||||
r = process_each_lv_in_vg(cmd, vg, NULL, NULL, 0, handle,
|
||||
do_lv_info && !do_lv_seg_status ? &_lvs_with_info_single :
|
||||
!do_lv_info && do_lv_seg_status ? &_lvs_with_status_single :
|
||||
do_lv_info && do_lv_seg_status ? &_lvs_with_info_and_status_single :
|
||||
&_lvs_single);
|
||||
break;
|
||||
case SEGS:
|
||||
r = process_each_lv_in_vg(cmd, vg, NULL, NULL, 0, handle, NULL,
|
||||
r = process_each_lv_in_vg(cmd, vg, NULL, NULL, 0, handle,
|
||||
do_lv_info && !do_lv_seg_status ? &_lvsegs_with_info_single :
|
||||
!do_lv_info && do_lv_seg_status ? &_lvsegs_with_status_single :
|
||||
do_lv_info && do_lv_seg_status ? &_lvsegs_with_info_and_status_single :
|
||||
@@ -1124,7 +1127,7 @@ static int _do_report(struct cmd_context *cmd, struct processing_handle *handle,
|
||||
if (args->full_report_vg)
|
||||
r = _report_all_in_vg(cmd, handle, args->full_report_vg, LVS, lv_info_needed, lv_segment_status_needed);
|
||||
else
|
||||
r = process_each_lv(cmd, args->argc, args->argv, NULL, NULL, 0, handle, NULL,
|
||||
r = process_each_lv(cmd, args->argc, args->argv, NULL, NULL, 0, handle,
|
||||
lv_info_needed && !lv_segment_status_needed ? &_lvs_with_info_single :
|
||||
!lv_info_needed && lv_segment_status_needed ? &_lvs_with_status_single :
|
||||
lv_info_needed && lv_segment_status_needed ? &_lvs_with_info_and_status_single :
|
||||
@@ -1158,7 +1161,7 @@ static int _do_report(struct cmd_context *cmd, struct processing_handle *handle,
|
||||
if (args->full_report_vg)
|
||||
r = _report_all_in_vg(cmd, handle, args->full_report_vg, SEGS, lv_info_needed, lv_segment_status_needed);
|
||||
else
|
||||
r = process_each_lv(cmd, args->argc, args->argv, NULL, NULL, 0, handle, NULL,
|
||||
r = process_each_lv(cmd, args->argc, args->argv, NULL, NULL, 0, handle,
|
||||
lv_info_needed && !lv_segment_status_needed ? &_lvsegs_with_info_single :
|
||||
!lv_info_needed && lv_segment_status_needed ? &_lvsegs_with_status_single :
|
||||
lv_info_needed && lv_segment_status_needed ? &_lvsegs_with_info_and_status_single :
|
||||
|
812
tools/toollib.c
812
tools/toollib.c
@@ -800,7 +800,10 @@ int vgcreate_params_set_from_args(struct cmd_context *cmd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (arg_is_set(cmd, vgmetadatacopies_ARG))
|
||||
if (arg_is_set(cmd, metadatacopies_ARG))
|
||||
vp_new->vgmetadatacopies = arg_int_value(cmd, metadatacopies_ARG,
|
||||
DEFAULT_VGMETADATACOPIES);
|
||||
else if (arg_is_set(cmd, vgmetadatacopies_ARG))
|
||||
vp_new->vgmetadatacopies = arg_int_value(cmd, vgmetadatacopies_ARG,
|
||||
DEFAULT_VGMETADATACOPIES);
|
||||
else
|
||||
@@ -2325,606 +2328,10 @@ static struct lv_segment _historical_lv_segment = {
|
||||
.origin_list = DM_LIST_HEAD_INIT(_historical_lv_segment.origin_list),
|
||||
};
|
||||
|
||||
int opt_in_list_is_set(struct cmd_context *cmd, int *opts, int count,
|
||||
int *match_count, int *unmatch_count)
|
||||
{
|
||||
int match = 0;
|
||||
int unmatch = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
if (arg_is_set(cmd, opts[i]))
|
||||
match++;
|
||||
else
|
||||
unmatch++;
|
||||
}
|
||||
|
||||
if (match_count)
|
||||
*match_count = match;
|
||||
if (unmatch_count)
|
||||
*unmatch_count = unmatch;
|
||||
|
||||
return match ? 1 : 0;
|
||||
}
|
||||
|
||||
void opt_array_to_str(struct cmd_context *cmd, int *opts, int count,
|
||||
char *buf, int len)
|
||||
{
|
||||
int pos = 0;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
ret = snprintf(buf + pos, len - pos, "%s ", arg_long_option_name(opts[i]));
|
||||
if (ret >= len - pos)
|
||||
break;
|
||||
pos += ret;
|
||||
}
|
||||
|
||||
buf[len - 1] = '\0';
|
||||
}
|
||||
|
||||
static void lvp_bits_to_str(uint64_t bits, char *buf, int len)
|
||||
{
|
||||
struct lv_props *prop;
|
||||
int lvp_enum;
|
||||
int pos = 0;
|
||||
int ret;
|
||||
|
||||
for (lvp_enum = 0; lvp_enum < LVP_COUNT; lvp_enum++) {
|
||||
if (!(prop = get_lv_prop(lvp_enum)))
|
||||
continue;
|
||||
|
||||
if (lvp_bit_is_set(bits, lvp_enum)) {
|
||||
ret = snprintf(buf + pos, len - pos, "%s ", prop->name);
|
||||
if (ret >= len - pos)
|
||||
break;
|
||||
pos += ret;
|
||||
}
|
||||
}
|
||||
buf[len - 1] = '\0';
|
||||
}
|
||||
|
||||
static void lvt_bits_to_str(uint64_t bits, char *buf, int len)
|
||||
{
|
||||
struct lv_types *type;
|
||||
int lvt_enum;
|
||||
int pos = 0;
|
||||
int ret;
|
||||
|
||||
for (lvt_enum = 0; lvt_enum < LVT_COUNT; lvt_enum++) {
|
||||
if (!(type = get_lv_type(lvt_enum)))
|
||||
continue;
|
||||
|
||||
if (lvt_bit_is_set(bits, lvt_enum)) {
|
||||
ret = snprintf(buf + pos, len - pos, "%s ", type->name);
|
||||
if (ret >= len - pos)
|
||||
break;
|
||||
pos += ret;
|
||||
}
|
||||
}
|
||||
buf[len - 1] = '\0';
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the lv_prop function pointer used for lv_is_foo() #defines.
|
||||
* Alternatively, lv_is_foo() could all be turned into functions.
|
||||
*/
|
||||
|
||||
static int _lv_is_prop(struct cmd_context *cmd, struct logical_volume *lv, int lvp_enum)
|
||||
{
|
||||
switch (lvp_enum) {
|
||||
case is_locked_LVP:
|
||||
return lv_is_locked(lv);
|
||||
case is_partial_LVP:
|
||||
return lv_is_partial(lv);
|
||||
case is_virtual_LVP:
|
||||
return lv_is_virtual(lv);
|
||||
case is_merging_LVP:
|
||||
return lv_is_merging(lv);
|
||||
case is_merging_origin_LVP:
|
||||
return lv_is_merging_origin(lv);
|
||||
case is_converting_LVP:
|
||||
return lv_is_converting(lv);
|
||||
case is_external_origin_LVP:
|
||||
return lv_is_external_origin(lv);
|
||||
case is_virtual_origin_LVP:
|
||||
return lv_is_virtual_origin(lv);
|
||||
case is_not_synced_LVP:
|
||||
return lv_is_not_synced(lv);
|
||||
case is_pending_delete_LVP:
|
||||
return lv_is_pending_delete(lv);
|
||||
case is_error_when_full_LVP:
|
||||
return lv_is_error_when_full(lv);
|
||||
case is_pvmove_LVP:
|
||||
return lv_is_pvmove(lv);
|
||||
case is_removed_LVP:
|
||||
return lv_is_removed(lv);
|
||||
case is_vg_writable_LVP:
|
||||
return (lv->vg->status & LVM_WRITE) ? 1 : 0;
|
||||
case is_thinpool_data_LVP:
|
||||
return lv_is_thin_pool_data(lv);
|
||||
case is_thinpool_metadata_LVP:
|
||||
return lv_is_thin_pool_metadata(lv);
|
||||
case is_cachepool_data_LVP:
|
||||
return lv_is_cache_pool_data(lv);
|
||||
case is_cachepool_metadata_LVP:
|
||||
return lv_is_cache_pool_metadata(lv);
|
||||
case is_mirror_image_LVP:
|
||||
return lv_is_mirror_image(lv);
|
||||
case is_mirror_log_LVP:
|
||||
return lv_is_mirror_log(lv);
|
||||
case is_raid_image_LVP:
|
||||
return lv_is_raid_image(lv);
|
||||
case is_raid_metadata_LVP:
|
||||
return lv_is_raid_metadata(lv);
|
||||
case is_origin_LVP: /* use lv_is_thick_origin */
|
||||
return lv_is_origin(lv);
|
||||
case is_thick_origin_LVP:
|
||||
return lv_is_thick_origin(lv);
|
||||
case is_thick_snapshot_LVP:
|
||||
return lv_is_thick_snapshot(lv);
|
||||
case is_thin_origin_LVP:
|
||||
return lv_is_thin_origin(lv, NULL);
|
||||
case is_thin_snapshot_LVP:
|
||||
return lv_is_thin_snapshot(lv);
|
||||
case is_cache_origin_LVP:
|
||||
return lv_is_cache_origin(lv);
|
||||
case is_merging_cow_LVP:
|
||||
return lv_is_merging_cow(lv);
|
||||
case is_cow_covering_origin_LVP:
|
||||
return lv_is_cow_covering_origin(lv);
|
||||
case is_visible_LVP:
|
||||
return lv_is_visible(lv);
|
||||
case is_historical_LVP:
|
||||
return lv_is_historical(lv);
|
||||
case is_raid_with_tracking_LVP:
|
||||
return lv_is_raid_with_tracking(lv);
|
||||
default:
|
||||
log_error(INTERNAL_ERROR "unknown lv property value lvp_enum %d", lvp_enum);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if an LV matches a given LV type enum.
|
||||
*/
|
||||
|
||||
static int _lv_is_type(struct cmd_context *cmd, struct logical_volume *lv, int lvt_enum)
|
||||
{
|
||||
struct lv_segment *seg = first_seg(lv);
|
||||
|
||||
switch (lvt_enum) {
|
||||
case striped_LVT:
|
||||
return seg_is_striped(seg) && !lv_is_cow(lv);
|
||||
case linear_LVT:
|
||||
return seg_is_linear(seg) && !lv_is_cow(lv);
|
||||
case snapshot_LVT:
|
||||
return lv_is_cow(lv);
|
||||
case thin_LVT:
|
||||
return lv_is_thin_volume(lv);
|
||||
case thinpool_LVT:
|
||||
return lv_is_thin_pool(lv);
|
||||
case cache_LVT:
|
||||
return lv_is_cache(lv);
|
||||
case cachepool_LVT:
|
||||
return lv_is_cache_pool(lv);
|
||||
case mirror_LVT:
|
||||
return lv_is_mirror(lv);
|
||||
case raid_LVT:
|
||||
return lv_is_raid(lv);
|
||||
case raid0_LVT:
|
||||
return seg_is_raid0(seg);
|
||||
case raid1_LVT:
|
||||
return seg_is_raid1(seg);
|
||||
case raid4_LVT:
|
||||
return seg_is_raid4(seg);
|
||||
#if 0
|
||||
case raid5_LVT:
|
||||
return seg_is_raid5(seg);
|
||||
case raid6_LVT:
|
||||
return seg_is_raid6(seg);
|
||||
#endif
|
||||
case raid10_LVT:
|
||||
return seg_is_raid10(seg);
|
||||
case error_LVT:
|
||||
return !strcmp(seg->segtype->name, SEG_TYPE_NAME_ERROR);
|
||||
case zero_LVT:
|
||||
return !strcmp(seg->segtype->name, SEG_TYPE_NAME_ZERO);
|
||||
default:
|
||||
log_error(INTERNAL_ERROR "unknown lv type value lvt_enum %d", lvt_enum);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_lvt_enum(struct logical_volume *lv)
|
||||
{
|
||||
struct lv_segment *seg = first_seg(lv);
|
||||
|
||||
/*
|
||||
* The order these are checked is important, because a snapshot LV has
|
||||
* a linear seg type.
|
||||
*/
|
||||
|
||||
if (lv_is_cow(lv))
|
||||
return snapshot_LVT;
|
||||
if (seg_is_linear(seg))
|
||||
return linear_LVT;
|
||||
if (seg_is_striped(seg))
|
||||
return striped_LVT;
|
||||
if (lv_is_thin_volume(lv))
|
||||
return thin_LVT;
|
||||
if (lv_is_thin_pool(lv))
|
||||
return thinpool_LVT;
|
||||
if (lv_is_cache(lv))
|
||||
return cache_LVT;
|
||||
if (lv_is_cache_pool(lv))
|
||||
return cachepool_LVT;
|
||||
if (lv_is_mirror(lv))
|
||||
return mirror_LVT;
|
||||
if (lv_is_raid(lv))
|
||||
return raid_LVT;
|
||||
if (seg_is_raid0(seg))
|
||||
return raid0_LVT;
|
||||
if (seg_is_raid1(seg))
|
||||
return raid1_LVT;
|
||||
if (seg_is_raid4(seg))
|
||||
return raid4_LVT;
|
||||
#if 0
|
||||
if (seg_is_raid5(seg))
|
||||
return raid5_LVT;
|
||||
if (seg_is_raid6(seg))
|
||||
return raid6_LVT;
|
||||
#endif
|
||||
if (seg_is_raid10(seg))
|
||||
return raid10_LVT;
|
||||
|
||||
if (!strcmp(seg->segtype->name, SEG_TYPE_NAME_ERROR))
|
||||
return error_LVT;
|
||||
if (!strcmp(seg->segtype->name, SEG_TYPE_NAME_ZERO))
|
||||
return zero_LVT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Call lv_is_<type> for each <type>_LVT bit set in lvt_bits.
|
||||
* If lv matches one of the specified lv types, then return 1.
|
||||
*/
|
||||
|
||||
static int _lv_types_match(struct cmd_context *cmd, struct logical_volume *lv, uint64_t lvt_bits,
|
||||
uint64_t *match_bits, uint64_t *unmatch_bits)
|
||||
{
|
||||
struct lv_types *type;
|
||||
int lvt_enum;
|
||||
int found_a_match = 0;
|
||||
int match;
|
||||
|
||||
if (match_bits)
|
||||
*match_bits = 0;
|
||||
if (unmatch_bits)
|
||||
*unmatch_bits = 0;
|
||||
|
||||
for (lvt_enum = 1; lvt_enum < LVT_COUNT; lvt_enum++) {
|
||||
if (!lvt_bit_is_set(lvt_bits, lvt_enum))
|
||||
continue;
|
||||
|
||||
if (!(type = get_lv_type(lvt_enum)))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* All types are currently handled by _lv_is_type()
|
||||
* because lv_is_type() are #defines and not exposed
|
||||
* in tools.h
|
||||
*/
|
||||
|
||||
if (!type->fn)
|
||||
match = _lv_is_type(cmd, lv, lvt_enum);
|
||||
else
|
||||
match = type->fn(cmd, lv);
|
||||
|
||||
if (match)
|
||||
found_a_match = 1;
|
||||
|
||||
if (match_bits && match)
|
||||
*match_bits |= lvt_enum_to_bit(lvt_enum);
|
||||
|
||||
if (unmatch_bits && !match)
|
||||
*unmatch_bits |= lvt_enum_to_bit(lvt_enum);
|
||||
}
|
||||
|
||||
return found_a_match;
|
||||
}
|
||||
|
||||
/*
|
||||
* Call lv_is_<prop> for each <prop>_LVP bit set in lvp_bits.
|
||||
* If lv matches all of the specified lv properties, then return 1.
|
||||
*/
|
||||
|
||||
static int _lv_props_match(struct cmd_context *cmd, struct logical_volume *lv, uint64_t lvp_bits,
|
||||
uint64_t *match_bits, uint64_t *unmatch_bits)
|
||||
{
|
||||
struct lv_props *prop;
|
||||
int lvp_enum;
|
||||
int found_a_mismatch = 0;
|
||||
int match;
|
||||
|
||||
if (match_bits)
|
||||
*match_bits = 0;
|
||||
if (unmatch_bits)
|
||||
*unmatch_bits = 0;
|
||||
|
||||
for (lvp_enum = 1; lvp_enum < LVP_COUNT; lvp_enum++) {
|
||||
if (!lvp_bit_is_set(lvp_bits, lvp_enum))
|
||||
continue;
|
||||
|
||||
if (!(prop = get_lv_prop(lvp_enum)))
|
||||
continue;
|
||||
|
||||
if (!prop->fn)
|
||||
match = _lv_is_prop(cmd, lv, lvp_enum);
|
||||
else
|
||||
match = prop->fn(cmd, lv);
|
||||
|
||||
if (!match)
|
||||
found_a_mismatch = 1;
|
||||
|
||||
if (match_bits && match)
|
||||
*match_bits |= lvp_enum_to_bit(lvp_enum);
|
||||
|
||||
if (unmatch_bits && !match)
|
||||
*unmatch_bits |= lvp_enum_to_bit(lvp_enum);
|
||||
}
|
||||
|
||||
return !found_a_mismatch;
|
||||
}
|
||||
|
||||
static int _check_lv_types(struct cmd_context *cmd, struct logical_volume *lv, int pos)
|
||||
{
|
||||
int ret = 1;
|
||||
|
||||
if (!pos)
|
||||
return 1;
|
||||
|
||||
if (!cmd->command->required_pos_args[pos-1].def.lvt_bits)
|
||||
return 1;
|
||||
|
||||
if (!val_bit_is_set(cmd->command->required_pos_args[pos-1].def.val_bits, lv_VAL)) {
|
||||
log_error(INTERNAL_ERROR "Command (%s %d) arg position %d does not permit an LV (%llx)",
|
||||
cmd->command->command_line_id, cmd->command->command_line_enum,
|
||||
pos, (unsigned long long)cmd->command->required_pos_args[pos-1].def.val_bits);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = _lv_types_match(cmd, lv, cmd->command->required_pos_args[pos-1].def.lvt_bits, NULL, NULL);
|
||||
if (!ret) {
|
||||
int lvt_enum = get_lvt_enum(lv);
|
||||
struct lv_types *type = get_lv_type(lvt_enum);
|
||||
log_warn("Operation on LV %s which has invalid type %s.",
|
||||
display_lvname(lv), type ? type->name : "unknown");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Check if LV passes each rule specified in command definition. */
|
||||
|
||||
static int _check_lv_rules(struct cmd_context *cmd, struct logical_volume *lv)
|
||||
{
|
||||
char buf[64];
|
||||
struct cmd_rule *rule;
|
||||
struct lv_types *lvtype = NULL;
|
||||
uint64_t lv_props_match_bits, lv_props_unmatch_bits;
|
||||
uint64_t lv_types_match_bits, lv_types_unmatch_bits;
|
||||
int opts_match_count, opts_unmatch_count;
|
||||
int lvt_enum;
|
||||
int ret = 1;
|
||||
int i;
|
||||
|
||||
lvt_enum = get_lvt_enum(lv);
|
||||
if (lvt_enum)
|
||||
lvtype = get_lv_type(lvt_enum);
|
||||
|
||||
for (i = 0; i < cmd->command->rule_count; i++) {
|
||||
rule = &cmd->command->rules[i];
|
||||
|
||||
/*
|
||||
* RULE: <conditions> INVALID|REQUIRE <checks>
|
||||
*
|
||||
* If all the conditions apply to the command+LV, then
|
||||
* the checks are performed. If all conditions are zero
|
||||
* (!opts_count, !lvt_bits, !lvp_bits), then the check
|
||||
* is always performed.
|
||||
*
|
||||
* Conditions:
|
||||
*
|
||||
* 1. options (opts): if any of the specified options are set,
|
||||
* then the checks may apply.
|
||||
*
|
||||
* 2. LV types (lvt_bits): if any of the specified LV types
|
||||
* match the LV, then the checks may apply.
|
||||
*
|
||||
* 3. LV properties (lvp_bits): if all of the specified
|
||||
* LV properties match the LV, then the checks may apply.
|
||||
*
|
||||
* If conditions 1, 2, 3 all pass, then the checks apply.
|
||||
*
|
||||
* Checks:
|
||||
*
|
||||
* 1. options (check_opts):
|
||||
* INVALID: if any of the specified options are set,
|
||||
* then the command fails.
|
||||
* REQUIRE: if any of the specified options are not set,
|
||||
* then the command fails.
|
||||
*
|
||||
* 2. LV types (check_lvt_bits):
|
||||
* INVALID: if any of the specified LV types match the LV,
|
||||
* then the command fails.
|
||||
* REQUIRE: if none of the specified LV types match the LV,
|
||||
* then the command fails.
|
||||
*
|
||||
* 3. LV properties (check_lvp_bits):
|
||||
* INVALID: if any of the specified LV properties match
|
||||
* the LV, then the command fails.
|
||||
* REQUIRE: if any of the specified LV properties do not match
|
||||
* the LV, then the command fails.
|
||||
*/
|
||||
|
||||
if (rule->opts_count && !opt_in_list_is_set(cmd, rule->opts, rule->opts_count, NULL, NULL))
|
||||
continue;
|
||||
|
||||
/* If LV matches one type in lvt_bits, this returns 1. */
|
||||
if (rule->lvt_bits && !_lv_types_match(cmd, lv, rule->lvt_bits, NULL, NULL))
|
||||
continue;
|
||||
|
||||
/* If LV matches all properties in lvp_bits, this returns 1. */
|
||||
if (rule->lvp_bits && !_lv_props_match(cmd, lv, rule->lvp_bits, NULL, NULL))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Check the options, LV types, LV properties.
|
||||
*/
|
||||
|
||||
if (rule->check_opts)
|
||||
opt_in_list_is_set(cmd, rule->check_opts, rule->check_opts_count,
|
||||
&opts_match_count, &opts_unmatch_count);
|
||||
|
||||
if (rule->check_lvt_bits)
|
||||
_lv_types_match(cmd, lv, rule->check_lvt_bits,
|
||||
&lv_types_match_bits, &lv_types_unmatch_bits);
|
||||
|
||||
if (rule->check_lvp_bits)
|
||||
_lv_props_match(cmd, lv, rule->check_lvp_bits,
|
||||
&lv_props_match_bits, &lv_props_unmatch_bits);
|
||||
|
||||
/*
|
||||
* Evaluate if the check results pass based on the rule.
|
||||
* The options are checked again here because the previous
|
||||
* option validation (during command matching) does not cover
|
||||
* cases where the option is combined with conditions of LV types
|
||||
* or properties.
|
||||
*/
|
||||
|
||||
/* Fail if any invalid options are set. */
|
||||
|
||||
if (rule->check_opts && (rule->rule == RULE_INVALID) && opts_match_count) {
|
||||
memset(buf, 0, sizeof(buf));
|
||||
opt_array_to_str(cmd, rule->check_opts, rule->check_opts_count, buf, sizeof(buf));
|
||||
log_warn("An invalid option is set: %s", buf);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
/* Fail if any required options are not set. */
|
||||
|
||||
if (rule->check_opts && (rule->rule == RULE_REQUIRE) && opts_unmatch_count) {
|
||||
memset(buf, 0, sizeof(buf));
|
||||
opt_array_to_str(cmd, rule->check_opts, rule->check_opts_count, buf, sizeof(buf));
|
||||
log_warn("A required option is not set: %s", buf);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
/* Fail if the LV matches any of the invalid LV types. */
|
||||
|
||||
if (rule->check_lvt_bits && (rule->rule == RULE_INVALID) && lv_types_match_bits) {
|
||||
log_warn("Command on LV %s with invalid type: %s",
|
||||
display_lvname(lv), lvtype ? lvtype->name : "unknown");
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
/* Fail if the LV does not match any of the required LV types. */
|
||||
|
||||
if (rule->check_lvt_bits && (rule->rule == RULE_REQUIRE) && !lv_types_match_bits) {
|
||||
memset(buf, 0, sizeof(buf));
|
||||
lvt_bits_to_str(rule->check_lvt_bits, buf, sizeof(buf));
|
||||
log_warn("Command on LV %s with type %s does not match required type: %s",
|
||||
display_lvname(lv), lvtype ? lvtype->name : "unknown", buf);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
/* Fail if the LV matches any of the invalid LV properties. */
|
||||
|
||||
if (rule->check_lvp_bits && (rule->rule == RULE_INVALID) && lv_props_match_bits) {
|
||||
memset(buf, 0, sizeof(buf));
|
||||
lvp_bits_to_str(lv_props_match_bits, buf, sizeof(buf));
|
||||
log_warn("Command on LV %s with invalid properties: %s",
|
||||
display_lvname(lv), buf);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
/* Fail if the LV does not match any of the required LV properties. */
|
||||
|
||||
if (rule->check_lvp_bits && (rule->rule == RULE_REQUIRE) && lv_props_unmatch_bits) {
|
||||
memset(buf, 0, sizeof(buf));
|
||||
lvp_bits_to_str(lv_props_unmatch_bits, buf, sizeof(buf));
|
||||
log_warn("Command on LV %s requires properties: %s",
|
||||
display_lvname(lv), buf);
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return which arg position the given LV is at,
|
||||
* where 1 represents the first position arg.
|
||||
* When the first position arg is repeatable,
|
||||
* return 1 for all.
|
||||
*
|
||||
* Return 0 when the command has no required
|
||||
* position args. (optional position args are
|
||||
* not considered.)
|
||||
*/
|
||||
|
||||
static int _find_lv_arg_position(struct cmd_context *cmd, struct logical_volume *lv)
|
||||
{
|
||||
const char *sep, *lvname;
|
||||
int i;
|
||||
|
||||
if (cmd->command->rp_count == 0)
|
||||
return 0;
|
||||
|
||||
if (cmd->command->rp_count == 1)
|
||||
return 1;
|
||||
|
||||
for (i = 0; i < cmd->position_argc; i++) {
|
||||
if (i == cmd->command->rp_count)
|
||||
break;
|
||||
|
||||
if (!val_bit_is_set(cmd->command->required_pos_args[i].def.val_bits, lv_VAL))
|
||||
continue;
|
||||
|
||||
if ((sep = strstr(cmd->position_argv[i], "/")))
|
||||
lvname = sep + 1;
|
||||
else
|
||||
lvname = cmd->position_argv[i];
|
||||
|
||||
if (!strcmp(lvname, lv->name))
|
||||
return i + 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the last position arg is an LV and this
|
||||
* arg is beyond that position, then the last
|
||||
* LV position arg is repeatable, so return
|
||||
* that position.
|
||||
*/
|
||||
if (i == cmd->command->rp_count) {
|
||||
int last_pos = cmd->command->rp_count;
|
||||
if (val_bit_is_set(cmd->command->required_pos_args[last_pos-1].def.val_bits, lv_VAL))
|
||||
return last_pos;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int process_each_lv_in_vg(struct cmd_context *cmd, struct volume_group *vg,
|
||||
struct dm_list *arg_lvnames, const struct dm_list *tags_in,
|
||||
int stop_on_error,
|
||||
struct processing_handle *handle,
|
||||
check_single_lv_fn_t check_single_lv,
|
||||
process_single_lv_fn_t process_single_lv)
|
||||
{
|
||||
log_report_t saved_log_report_state = log_get_report_state();
|
||||
@@ -2938,13 +2345,10 @@ int process_each_lv_in_vg(struct cmd_context *cmd, struct volume_group *vg,
|
||||
unsigned process_all = 0;
|
||||
unsigned tags_supplied = 0;
|
||||
unsigned lvargs_supplied = 0;
|
||||
int lv_is_named_arg;
|
||||
int lv_arg_pos;
|
||||
struct lv_list *lvl;
|
||||
struct dm_str_list *sl;
|
||||
struct dm_list final_lvs;
|
||||
struct lv_list *final_lvl;
|
||||
struct dm_list found_arg_lvnames;
|
||||
struct glv_list *glvl, *tglvl;
|
||||
int do_report_ret_code = 1;
|
||||
|
||||
@@ -2955,7 +2359,6 @@ int process_each_lv_in_vg(struct cmd_context *cmd, struct volume_group *vg,
|
||||
stack;
|
||||
|
||||
dm_list_init(&final_lvs);
|
||||
dm_list_init(&found_arg_lvnames);
|
||||
|
||||
if (!vg_check_status(vg, EXPORTED_VG)) {
|
||||
ret_max = ECMD_FAILED;
|
||||
@@ -3049,7 +2452,6 @@ int process_each_lv_in_vg(struct cmd_context *cmd, struct volume_group *vg,
|
||||
if (lvargs_supplied && str_list_match_item(arg_lvnames, lvl->lv->name)) {
|
||||
/* Remove LV from list of unprocessed LV names */
|
||||
str_list_del(arg_lvnames, lvl->lv->name);
|
||||
str_list_add(cmd->mem, &found_arg_lvnames, lvl->lv->name);
|
||||
process_lv = 1;
|
||||
}
|
||||
|
||||
@@ -3097,43 +2499,6 @@ int process_each_lv_in_vg(struct cmd_context *cmd, struct volume_group *vg,
|
||||
if (lv_is_removed(lvl->lv))
|
||||
continue;
|
||||
|
||||
lv_is_named_arg = str_list_match_item(&found_arg_lvnames, lvl->lv->name);
|
||||
|
||||
lv_arg_pos = _find_lv_arg_position(cmd, lvl->lv);
|
||||
|
||||
/*
|
||||
* The command definition may include restrictions on the
|
||||
* types and properties of LVs that can be processed.
|
||||
*/
|
||||
|
||||
if (!_check_lv_types(cmd, lvl->lv, lv_arg_pos)) {
|
||||
/* FIXME: include this result in report log? */
|
||||
if (lv_is_named_arg) {
|
||||
log_error("Operation not permitted (%s %d) on LV %s.",
|
||||
cmd->command->command_line_id, cmd->command->command_line_enum,
|
||||
display_lvname(lvl->lv));
|
||||
ret_max = ECMD_FAILED;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!_check_lv_rules(cmd, lvl->lv)) {
|
||||
/* FIXME: include this result in report log? */
|
||||
if (lv_is_named_arg) {
|
||||
log_error("Operation not permitted (%s %d) on LV %s.",
|
||||
cmd->command->command_line_id, cmd->command->command_line_enum,
|
||||
display_lvname(lvl->lv));
|
||||
ret_max = ECMD_FAILED;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (check_single_lv && !check_single_lv(cmd, lvl->lv, handle, lv_is_named_arg)) {
|
||||
if (lv_is_named_arg)
|
||||
ret_max = ECMD_FAILED;
|
||||
continue;
|
||||
}
|
||||
|
||||
log_very_verbose("Processing LV %s in VG %s.", lvl->lv->name, vg->name);
|
||||
|
||||
ret = process_single_lv(cmd, lvl->lv, handle);
|
||||
@@ -3364,164 +2729,12 @@ static int _get_arg_lvnames(struct cmd_context *cmd,
|
||||
return ret_max;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is a non-standard way of finding vgname/lvname to process. It exists
|
||||
* because an earlier form of lvconvert did not follow the standard form, and
|
||||
* came up with its own inconsistent approach.
|
||||
*
|
||||
* In this case, when the position arg is a single name, it is treated as an LV
|
||||
* name (not a VG name). This leaves the VG unknown. So, other option values
|
||||
* must be searched for a VG name. If one of those option values contains a
|
||||
* vgname/lvname value, then the VG name is extracted and used for the LV
|
||||
* position arg.
|
||||
*
|
||||
* Other option values that are searched for a VG name are:
|
||||
* --thinpool, --cachepool.
|
||||
*
|
||||
* . command vg/lv1
|
||||
* . add vg to arg_vgnames
|
||||
* . add vg/lv1 to arg_lvnames
|
||||
*
|
||||
* command lv1
|
||||
* . error: no vg name
|
||||
*
|
||||
* command --option vg/lv1 vg/lv2
|
||||
* . verify both vg names match
|
||||
* . add vg to arg_vgnames
|
||||
* . add vg/lv2 to arg_lvnames
|
||||
*
|
||||
* command --option lv1 lv2
|
||||
* . error: no vg name
|
||||
*
|
||||
* command --option vg/lv1 lv2
|
||||
* . add vg to arg_vgnames
|
||||
* . add vg/lv2 to arg_lvnames
|
||||
*
|
||||
* command --option lv1 vg/lv2
|
||||
* . add vg to arg_vgnames
|
||||
* . add vg/lv2 to arg_lvnames
|
||||
*/
|
||||
|
||||
static int _get_arg_lvnames_using_options(struct cmd_context *cmd,
|
||||
int argc, char **argv,
|
||||
struct dm_list *arg_vgnames,
|
||||
struct dm_list *arg_lvnames,
|
||||
struct dm_list *arg_tags)
|
||||
{
|
||||
const char *pos_name = NULL;
|
||||
const char *arg_name = NULL;
|
||||
const char *pos_vgname = NULL;
|
||||
const char *opt_vgname = NULL;
|
||||
const char *pos_lvname = NULL;
|
||||
const char *use_vgname = NULL;
|
||||
char *tmp_name;
|
||||
char *split;
|
||||
char *vglv;
|
||||
size_t vglv_sz;
|
||||
|
||||
if (argc != 1) {
|
||||
log_error("One LV position arg is required.");
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
|
||||
if (!(pos_name = dm_pool_strdup(cmd->mem, argv[0]))) {
|
||||
log_error("string alloc failed.");
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
|
||||
if (*pos_name == '@') {
|
||||
if (!validate_tag(pos_name + 1)) {
|
||||
log_error("Skipping invalid tag %s.", pos_name);
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
if (!str_list_add(cmd->mem, arg_tags,
|
||||
dm_pool_strdup(cmd->mem, pos_name + 1))) {
|
||||
log_error("strlist allocation failed.");
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
return ECMD_PROCESSED;
|
||||
}
|
||||
|
||||
if ((split = strchr(pos_name, '/'))) {
|
||||
pos_vgname = pos_name;
|
||||
pos_lvname = split + 1;
|
||||
*split = '\0';
|
||||
} else {
|
||||
pos_lvname = pos_name;
|
||||
pos_vgname = NULL;
|
||||
}
|
||||
|
||||
if (arg_is_set(cmd, thinpool_ARG))
|
||||
arg_name = arg_str_value(cmd, thinpool_ARG, NULL);
|
||||
else if (arg_is_set(cmd, cachepool_ARG))
|
||||
arg_name = arg_str_value(cmd, cachepool_ARG, NULL);
|
||||
|
||||
if (!pos_vgname && !arg_name) {
|
||||
log_error("Cannot find VG name for LV %s.", pos_lvname);
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
|
||||
if (arg_name && (split = strchr(arg_name, '/'))) {
|
||||
/* combined VG/LV */
|
||||
|
||||
if (!(tmp_name = dm_pool_strdup(cmd->mem, arg_name))) {
|
||||
log_error("string alloc failed.");
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
|
||||
if (!(split = strchr(tmp_name, '/')))
|
||||
return ECMD_FAILED;
|
||||
|
||||
opt_vgname = tmp_name;
|
||||
/* Don't care about opt lvname. */
|
||||
/* opt_lvname = split + 1; */
|
||||
*split = '\0';
|
||||
} else {
|
||||
/* Don't care about opt lvname. */
|
||||
/* opt_lvname = arg_name; */
|
||||
opt_vgname = NULL;
|
||||
}
|
||||
|
||||
if (!pos_vgname && !opt_vgname) {
|
||||
log_error("Cannot find VG name for LV %s.", pos_lvname);
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
|
||||
if (pos_vgname && opt_vgname && strcmp(pos_vgname, opt_vgname)) {
|
||||
log_error("VG name mismatch from position arg (%s) and option arg (%s).",
|
||||
pos_vgname, opt_vgname);
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
|
||||
use_vgname = pos_vgname ? pos_vgname : opt_vgname;
|
||||
|
||||
if (!str_list_add(cmd->mem, arg_vgnames, dm_pool_strdup(cmd->mem, use_vgname))) {
|
||||
log_error("strlist allocation failed.");
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
|
||||
vglv_sz = strlen(use_vgname) + strlen(pos_lvname) + 2;
|
||||
|
||||
if (!(vglv = dm_pool_alloc(cmd->mem, vglv_sz)) ||
|
||||
dm_snprintf(vglv, vglv_sz, "%s/%s", use_vgname, pos_lvname) < 0) {
|
||||
log_error("vg/lv string alloc failed.");
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
if (!str_list_add(cmd->mem, arg_lvnames, vglv)) {
|
||||
log_error("strlist allocation failed.");
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
|
||||
return ECMD_PROCESSED;
|
||||
}
|
||||
|
||||
static int _process_lv_vgnameid_list(struct cmd_context *cmd, uint32_t read_flags,
|
||||
struct dm_list *vgnameids_to_process,
|
||||
struct dm_list *arg_vgnames,
|
||||
struct dm_list *arg_lvnames,
|
||||
struct dm_list *arg_tags,
|
||||
struct processing_handle *handle,
|
||||
check_single_lv_fn_t check_single_lv,
|
||||
process_single_lv_fn_t process_single_lv)
|
||||
{
|
||||
log_report_t saved_log_report_state = log_get_report_state();
|
||||
@@ -3614,7 +2827,7 @@ static int _process_lv_vgnameid_list(struct cmd_context *cmd, uint32_t read_flag
|
||||
goto endvg;
|
||||
|
||||
ret = process_each_lv_in_vg(cmd, vg, &lvnames, tags_arg, 0,
|
||||
handle, check_single_lv, process_single_lv);
|
||||
handle, process_single_lv);
|
||||
if (ret != ECMD_PROCESSED)
|
||||
stack;
|
||||
report_log_ret_code(ret);
|
||||
@@ -3645,7 +2858,6 @@ int process_each_lv(struct cmd_context *cmd,
|
||||
const char *one_vgname, const char *one_lvname,
|
||||
uint32_t read_flags,
|
||||
struct processing_handle *handle,
|
||||
check_single_lv_fn_t check_single_lv,
|
||||
process_single_lv_fn_t process_single_lv)
|
||||
{
|
||||
log_report_t saved_log_report_state = log_get_report_state();
|
||||
@@ -3674,12 +2886,7 @@ int process_each_lv(struct cmd_context *cmd,
|
||||
/*
|
||||
* Find any LVs, VGs or tags explicitly provided on the command line.
|
||||
*/
|
||||
if (cmd->command->flags & GET_VGNAME_FROM_OPTIONS)
|
||||
ret = _get_arg_lvnames_using_options(cmd, argc, argv, &arg_vgnames, &arg_lvnames, &arg_tags);
|
||||
else
|
||||
ret = _get_arg_lvnames(cmd, argc, argv, one_vgname, one_lvname, &arg_vgnames, &arg_lvnames, &arg_tags);
|
||||
|
||||
if (ret != ECMD_PROCESSED) {
|
||||
if ((ret = _get_arg_lvnames(cmd, argc, argv, one_vgname, one_lvname, &arg_vgnames, &arg_lvnames, &arg_tags) != ECMD_PROCESSED)) {
|
||||
ret_max = ret;
|
||||
goto_out;
|
||||
}
|
||||
@@ -3766,7 +2973,7 @@ int process_each_lv(struct cmd_context *cmd,
|
||||
_choose_vgs_to_process(cmd, &arg_vgnames, &vgnameids_on_system, &vgnameids_to_process);
|
||||
|
||||
ret = _process_lv_vgnameid_list(cmd, read_flags, &vgnameids_to_process, &arg_vgnames, &arg_lvnames,
|
||||
&arg_tags, handle, check_single_lv, process_single_lv);
|
||||
&arg_tags, handle, process_single_lv);
|
||||
|
||||
if (ret > ret_max)
|
||||
ret_max = ret;
|
||||
@@ -4728,6 +3935,11 @@ int pvcreate_params_from_args(struct cmd_context *cmd, struct pvcreate_params *p
|
||||
if (pp->pva.pvmetadatacopies < 0)
|
||||
pp->pva.pvmetadatacopies = find_config_tree_int(cmd, metadata_pvmetadatacopies_CFG, NULL);
|
||||
|
||||
if (pp->pva.pvmetadatacopies > 2) {
|
||||
log_error("Metadatacopies may only be 0, 1 or 2");
|
||||
return 0;
|
||||
}
|
||||
|
||||
pp->pva.ba_size = arg_uint64_value(cmd, bootloaderareasize_ARG, pp->pva.ba_size);
|
||||
|
||||
return 1;
|
||||
|
@@ -98,18 +98,6 @@ typedef int (*process_single_pvseg_fn_t) (struct cmd_context * cmd,
|
||||
struct pv_segment * pvseg,
|
||||
struct processing_handle *handle);
|
||||
|
||||
/*
|
||||
* Called prior to process_single_lv() to decide if the LV should be
|
||||
* processed. If this returns 0, the LV is not processed.
|
||||
*
|
||||
* This can evaluate the combination of command definition and
|
||||
* the LV object to decide if the combination is allowed.
|
||||
*/
|
||||
typedef int (*check_single_lv_fn_t) (struct cmd_context *cmd,
|
||||
struct logical_volume *lv,
|
||||
struct processing_handle *handle,
|
||||
int lv_is_named_arg);
|
||||
|
||||
int process_each_vg(struct cmd_context *cmd,
|
||||
int argc, char **argv,
|
||||
const char *one_vgname,
|
||||
@@ -137,7 +125,6 @@ int process_each_segment_in_pv(struct cmd_context *cmd,
|
||||
int process_each_lv(struct cmd_context *cmd, int argc, char **argv,
|
||||
const char *one_vgname, const char *one_lvname,
|
||||
uint32_t flags, struct processing_handle *handle,
|
||||
check_single_lv_fn_t check_single_lv,
|
||||
process_single_lv_fn_t process_single_lv);
|
||||
|
||||
|
||||
@@ -154,7 +141,6 @@ int process_each_pv_in_vg(struct cmd_context *cmd, struct volume_group *vg,
|
||||
int process_each_lv_in_vg(struct cmd_context *cmd, struct volume_group *vg,
|
||||
struct dm_list *arg_lvnames, const struct dm_list *tagsl,
|
||||
int stop_on_error, struct processing_handle *handle,
|
||||
check_single_lv_fn_t check_single_lv,
|
||||
process_single_lv_fn_t process_single_lv);
|
||||
|
||||
struct processing_handle *init_processing_handle(struct cmd_context *cmd, struct processing_handle *parent_handle);
|
||||
@@ -173,12 +159,6 @@ const char *extract_vgname(struct cmd_context *cmd, const char *lv_name);
|
||||
const char *skip_dev_dir(struct cmd_context *cmd, const char *vg_name,
|
||||
unsigned *dev_dir_found);
|
||||
|
||||
int opt_in_list_is_set(struct cmd_context *cmd, int *opts, int count,
|
||||
int *match_count, int *unmatch_count);
|
||||
|
||||
void opt_array_to_str(struct cmd_context *cmd, int *opts, int count,
|
||||
char *buf, int len);
|
||||
|
||||
int pvcreate_params_from_args(struct cmd_context *cmd, struct pvcreate_params *pp);
|
||||
int pvcreate_each_device(struct cmd_context *cmd, struct processing_handle *handle, struct pvcreate_params *pp);
|
||||
|
||||
@@ -240,6 +220,4 @@ int validate_restricted_lvname_param(struct cmd_context *cmd, const char **vg_na
|
||||
int lvremove_single(struct cmd_context *cmd, struct logical_volume *lv,
|
||||
struct processing_handle *handle __attribute__((unused)));
|
||||
|
||||
int get_lvt_enum(struct logical_volume *lv);
|
||||
|
||||
#endif
|
||||
|
123
tools/tools.h
123
tools/tools.h
@@ -50,41 +50,20 @@
|
||||
#define CMD_LEN 256
|
||||
#define MAX_ARGS 64
|
||||
|
||||
/* define the enums for the values accepted by command line --options, foo_VAL */
|
||||
enum {
|
||||
#define val(a, b, c, d) a ,
|
||||
#include "vals.h"
|
||||
#undef val
|
||||
};
|
||||
|
||||
/* define the enums for the command line --options, foo_ARG */
|
||||
enum {
|
||||
#define arg(a, b, c, d, e, f, g) a ,
|
||||
#include "args.h"
|
||||
#undef arg
|
||||
};
|
||||
|
||||
/* command functions */
|
||||
typedef int (*command_fn) (struct cmd_context * cmd, int argc, char **argv);
|
||||
|
||||
#define xx(a, b...) int a(struct cmd_context *cmd, int argc, char **argv);
|
||||
#include "commands.h"
|
||||
#undef xx
|
||||
|
||||
/* define enums for LV properties, foo_LVP */
|
||||
/* define the enums for the command line switches */
|
||||
enum {
|
||||
#define lvp(a, b, c) a ,
|
||||
#include "lv_props.h"
|
||||
#undef lvp
|
||||
#define arg(a, b, c, d, e, f) a ,
|
||||
#include "args.h"
|
||||
#undef arg
|
||||
};
|
||||
|
||||
/* define enums for LV types, foo_LVT */
|
||||
enum {
|
||||
#define lvt(a, b, c) a ,
|
||||
#include "lv_types.h"
|
||||
#undef lvt
|
||||
};
|
||||
|
||||
#include "command.h"
|
||||
|
||||
#define ARG_COUNTABLE 0x00000001 /* E.g. -vvvv */
|
||||
#define ARG_GROUPABLE 0x00000002 /* E.g. --addtag */
|
||||
|
||||
@@ -100,16 +79,15 @@ struct arg_values {
|
||||
/* void *ptr; // Currently not used. */
|
||||
};
|
||||
|
||||
/* a global table of possible --option's */
|
||||
/* a global table of possible arguments */
|
||||
struct arg_props {
|
||||
int arg_enum; /* foo_ARG from args.h */
|
||||
const char short_arg;
|
||||
char _padding[7];
|
||||
const char *long_arg;
|
||||
int val_enum; /* foo_VAL from vals.h */
|
||||
|
||||
int (*fn) (struct cmd_context *cmd, struct arg_values *av);
|
||||
uint32_t flags;
|
||||
uint32_t prio;
|
||||
const char *desc;
|
||||
};
|
||||
|
||||
struct arg_value_group_list {
|
||||
@@ -118,29 +96,6 @@ struct arg_value_group_list {
|
||||
uint32_t prio;
|
||||
};
|
||||
|
||||
/* a global table of possible --option values */
|
||||
struct val_props {
|
||||
int val_enum; /* foo_VAL from vals.h */
|
||||
int (*fn) (struct cmd_context *cmd, struct arg_values *av);
|
||||
const char *name;
|
||||
const char *usage;
|
||||
};
|
||||
|
||||
/* a global table of possible LV properties */
|
||||
struct lv_props {
|
||||
int lvp_enum; /* is_foo_LVP from lv_props.h */
|
||||
const char *name; /* "lv_is_foo" used in command-lines.in */
|
||||
int (*fn) (struct cmd_context *cmd, struct logical_volume *lv); /* lv_is_foo() */
|
||||
};
|
||||
|
||||
/* a global table of possible LV types */
|
||||
/* (as exposed externally in command line interface, not exactly as internal segtype is used) */
|
||||
struct lv_types {
|
||||
int lvt_enum; /* is_foo_LVT from lv_types.h */
|
||||
const char *name; /* "foo" used in command-lines.in, i.e. LV_foo */
|
||||
int (*fn) (struct cmd_context *cmd, struct logical_volume *lv); /* lv_is_foo() */
|
||||
};
|
||||
|
||||
#define CACHE_VGMETADATA 0x00000001
|
||||
#define PERMITTED_READ_ONLY 0x00000002
|
||||
/* Process all VGs if none specified on the command line. */
|
||||
@@ -163,8 +118,19 @@ struct lv_types {
|
||||
#define ENABLE_DUPLICATE_DEVS 0x00000400
|
||||
/* Command does not accept tags as args. */
|
||||
#define DISALLOW_TAG_ARGS 0x00000800
|
||||
/* Command may need to find VG name in an option value. */
|
||||
#define GET_VGNAME_FROM_OPTIONS 0x00001000
|
||||
|
||||
/* a register of the lvm commands */
|
||||
struct command {
|
||||
const char *name;
|
||||
const char *desc;
|
||||
const char *usage;
|
||||
command_fn fn;
|
||||
|
||||
unsigned flags;
|
||||
|
||||
int num_args;
|
||||
int *valid_args;
|
||||
};
|
||||
|
||||
void usage(const char *name);
|
||||
|
||||
@@ -192,15 +158,7 @@ int segtype_arg(struct cmd_context *cmd, struct arg_values *av);
|
||||
int alloc_arg(struct cmd_context *cmd, struct arg_values *av);
|
||||
int locktype_arg(struct cmd_context *cmd, struct arg_values *av);
|
||||
int readahead_arg(struct cmd_context *cmd, struct arg_values *av);
|
||||
int vgmetadatacopies_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av);
|
||||
int pvmetadatacopies_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av);
|
||||
int metadatacopies_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av);
|
||||
int polloperation_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av);
|
||||
int writemostly_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av);
|
||||
int syncaction_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av);
|
||||
int reportformat_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av);
|
||||
int configreport_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av);
|
||||
int configtype_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av);
|
||||
|
||||
/* we use the enums to access the switches */
|
||||
unsigned arg_count(const struct cmd_context *cmd, int a);
|
||||
@@ -242,41 +200,4 @@ int vgchange_activate(struct cmd_context *cmd, struct volume_group *vg,
|
||||
|
||||
int vgchange_background_polling(struct cmd_context *cmd, struct volume_group *vg);
|
||||
|
||||
struct lv_props *get_lv_prop(int lvp_enum);
|
||||
struct lv_types *get_lv_type(int lvt_enum);
|
||||
struct command *get_command(int cmd_enum);
|
||||
|
||||
int lvchange_properties_cmd(struct cmd_context *cmd, int argc, char **argv);
|
||||
int lvchange_activate_cmd(struct cmd_context *cmd, int argc, char **argv);
|
||||
int lvchange_refresh_cmd(struct cmd_context *cmd, int argc, char **argv);
|
||||
int lvchange_resync_cmd(struct cmd_context *cmd, int argc, char **argv);
|
||||
int lvchange_syncaction_cmd(struct cmd_context *cmd, int argc, char **argv);
|
||||
int lvchange_rebuild_cmd(struct cmd_context *cmd, int argc, char **argv);
|
||||
int lvchange_monitor_poll_cmd(struct cmd_context *cmd, int argc, char **argv);
|
||||
int lvchange_persistent_cmd(struct cmd_context *cmd, int argc, char **argv);
|
||||
|
||||
int lvconvert_repair_pvs_or_thinpool_cmd(struct cmd_context *cmd, int argc, char **argv);
|
||||
int lvconvert_replace_pv_cmd(struct cmd_context *cmd, int argc, char **argv);
|
||||
|
||||
int lvconvert_merge_snapshot_cmd(struct cmd_context *cmd, int argc, char **argv);
|
||||
int lvconvert_split_snapshot_cmd(struct cmd_context *cmd, int argc, char **argv);
|
||||
int lvconvert_combine_split_snapshot_cmd(struct cmd_context *cmd, int argc, char **argv);
|
||||
|
||||
int lvconvert_start_poll_cmd(struct cmd_context *cmd, int argc, char **argv);
|
||||
|
||||
int lvconvert_to_pool_cmd(struct cmd_context *cmd, int argc, char **argv);
|
||||
int lvconvert_to_pool_noarg_cmd(struct cmd_context *cmd, int argc, char **argv);
|
||||
int lvconvert_to_cache_vol_cmd(struct cmd_context *cmd, int argc, char **argv);
|
||||
int lvconvert_to_thin_with_external_cmd(struct cmd_context *cmd, int argc, char **argv);
|
||||
int lvconvert_swap_pool_metadata_cmd(struct cmd_context *cmd, int argc, char **argv);
|
||||
int lvconvert_merge_thin_cmd(struct cmd_context *cmd, int argc, char **argv);
|
||||
int lvconvert_split_cachepool_cmd(struct cmd_context *cmd, int argc, char **argv);
|
||||
|
||||
int lvconvert_raid_types_cmd(struct cmd_context * cmd, int argc, char **argv);
|
||||
int lvconvert_split_mirror_images_cmd(struct cmd_context * cmd, int argc, char **argv);
|
||||
int lvconvert_merge_mirror_images_cmd(struct cmd_context *cmd, int argc, char **argv);
|
||||
int lvconvert_change_mirrorlog_cmd(struct cmd_context * cmd, int argc, char **argv);
|
||||
|
||||
int lvconvert_merge_cmd(struct cmd_context *cmd, int argc, char **argv);
|
||||
|
||||
#endif
|
||||
|
137
tools/vals.h
137
tools/vals.h
@@ -1,137 +0,0 @@
|
||||
|
||||
/*
|
||||
* Define value types which describe values accepted
|
||||
* by the --option's in args.h, and can also describe
|
||||
* the values accepted as positional args.
|
||||
*
|
||||
* Previously, accepted values were only "described"
|
||||
* by identifying the parsing function to use.
|
||||
*
|
||||
* Some standard val types are used by many options,
|
||||
* e.g. many options (aa_ARG, bb_ARG, cc_ARG) all
|
||||
* accept a number_VAL.
|
||||
*
|
||||
* Other special val types are used by only one option,
|
||||
* e.g. only mirrorlog_ARG accepts a mirrorlog_VAL.
|
||||
* This typically means that there are some specific
|
||||
* words that are recognized after the option.
|
||||
*
|
||||
* Some options currently take a standard val type,
|
||||
* (esp string_VAL), but they could be given their
|
||||
* own custom val type. The advantage of using a
|
||||
* custom val type is the possibility of validating
|
||||
* the value when parsing it with a custom parsing
|
||||
* function, and the possibility of displaying the
|
||||
* actual accepted values in the command usage.
|
||||
* Without a custom val type, the code must do ad hoc
|
||||
* validation of the string values, and the usage
|
||||
* output for the option will only say "String"
|
||||
* rather than giving the accepted string values.
|
||||
* Even without a custom parsing function, there is
|
||||
* reason to define a custom x_VAL enum so that a
|
||||
* more descriptive usage string can be specified
|
||||
* as opposed to just "String".
|
||||
*
|
||||
* Most of the val types defined here are used after
|
||||
* --option's, and are referenced in foo_ARG entries
|
||||
* in args.h. But, some val types are only used to
|
||||
* represent positional values in command definitions,
|
||||
* e.g. vg_VAL.
|
||||
*
|
||||
* val(a, b, c, d)
|
||||
*
|
||||
* a: foo_VAL enums
|
||||
* b: the function to parse and set the value
|
||||
* c: the name used to reference this value in command defs
|
||||
* d: what to display in usage output for this value
|
||||
*
|
||||
* command defintions will use --option NAME, where NAME
|
||||
* is shown in val() field c. NAME will be translated to
|
||||
* foo_VAL enum in field a, which is used in commands[]
|
||||
* structs.
|
||||
*
|
||||
* option definitions (arg.h) will reference foo_VAL enum
|
||||
* in field a.
|
||||
*
|
||||
* FIXME: for specialized val types, the set of recognized
|
||||
* words is not defined or stored in a consistent way,
|
||||
* but is just whatever the parsing function happens to look
|
||||
* for, so adding a new accepted value for the val type is
|
||||
* generally making the parsing function recognize a new
|
||||
* word, and making the implementation code also recognize
|
||||
* that word to do something different. This new word should
|
||||
* then also be added to the usage string for the val type here.
|
||||
* It would be nice if the accepted values could be defined in a
|
||||
* more consistent way, and perhaps in a single place, perhaps in
|
||||
* struct val_props.
|
||||
*
|
||||
* The usage text for an option is not always the full
|
||||
* set of words accepted for an option, but may be a
|
||||
* subset. i.e. an outdated word that no longer does
|
||||
* anything may not be shown, but may still be recognized
|
||||
* and ignored, or an option that shouldn't be used in
|
||||
* general isn't shown to avoid suggesting it.
|
||||
* e.g. for --activate we show the most common "y|n|ay"
|
||||
* without showing the lvmlockd variations "ey|sy" which
|
||||
* are not applicable in general.
|
||||
*
|
||||
* FIXME: are there some specialized or irrelevant
|
||||
* options included in the usage text below that should
|
||||
* be removed? Should "lvm1" be removed?
|
||||
*
|
||||
* For Number args that take optional units, a full usage
|
||||
* could be "Number[bBsSkKmMgGtTpPeE]" (with implied |),
|
||||
* but repeating this full specification produces cluttered
|
||||
* output, and doesn't indicate which unit is the default.
|
||||
* "Number[units]" would be cleaner, as would a subset of
|
||||
* common units, e.g. "Number[kmg...]", but neither helps
|
||||
* with default. "Number[k|unit]" and "Number[m|unit]" show
|
||||
* the default, and "unit" indicates that other units
|
||||
* are possible without listing them all. This also
|
||||
* suggests using the preferred lower case letters, because
|
||||
* --size and other option args treat upper/lower letters
|
||||
* the same, all as 1024 SI base. For this reason, we
|
||||
* should avoid suggesting the upper case letters.
|
||||
*/
|
||||
|
||||
val(none_VAL, NULL, "None", "ERR") /* unused, for enum value 0 */
|
||||
val(conststr_VAL, NULL, "ConstString", "ERR") /* used only for command defs */
|
||||
val(constnum_VAL, NULL, "ConstNumber", "ERR") /* used only for command defs */
|
||||
val(bool_VAL, yes_no_arg, "Bool", "y|n")
|
||||
val(number_VAL, int_arg, "Number", NULL)
|
||||
val(uint32_VAL, uint32_arg, "Uint32", "Number")
|
||||
val(string_VAL, string_arg, "String", NULL)
|
||||
val(vg_VAL, string_arg, "VG", NULL)
|
||||
val(lv_VAL, string_arg, "LV", NULL)
|
||||
val(pv_VAL, string_arg, "PV", NULL)
|
||||
val(tag_VAL, tag_arg, "Tag", NULL)
|
||||
val(select_VAL, NULL, "Select", NULL) /* used only for command defs */
|
||||
val(activationmode_VAL, string_arg, "ActivationMode", "partial|degraded|complete")
|
||||
val(activation_VAL, activation_arg, "Active", "y|n|ay")
|
||||
val(cachemode_VAL, cachemode_arg, "CacheMode", "writethrough|writeback")
|
||||
val(discards_VAL, discards_arg, "Discards", "passdown|nopassdown|ignore")
|
||||
val(mirrorlog_VAL, mirrorlog_arg, "MirrorLog", "core|disk")
|
||||
val(sizekb_VAL, size_kb_arg, "SizeKB", "Number[k|unit]")
|
||||
val(sizemb_VAL, size_mb_arg, "SizeMB", "Number[m|unit]")
|
||||
val(numsigned_VAL, int_arg_with_sign, "SNumber", "[+|-]Number")
|
||||
val(numsignedper_VAL, int_arg_with_sign_and_percent, "SNumberP", "[+|-]Number[%VG|%PVS|%FREE]")
|
||||
val(permission_VAL, permission_arg, "Permission", "rw|r")
|
||||
val(metadatatype_VAL, metadatatype_arg, "MetadataType", "lvm2|lvm1")
|
||||
val(units_VAL, string_arg, "Units", "hHbBsSkKmMgGtTpPeE")
|
||||
val(segtype_VAL, segtype_arg, "SegType", "linear|striped|snapshot|mirror|raid*|thin|cache|thin-pool|cache-pool")
|
||||
val(alloc_VAL, alloc_arg, "Alloc", "contiguous|cling|cling_by_tags|normal|anywhere|inherit")
|
||||
val(locktype_VAL, locktype_arg, "LockType", "sanlock|dlm|none")
|
||||
val(readahead_VAL, readahead_arg, "Readahead", "auto|none|NumberSectors")
|
||||
val(vgmetadatacopies_VAL, vgmetadatacopies_arg, "MetadataCopiesVG", "all|unmanaged|Number")
|
||||
val(pvmetadatacopies_VAL, pvmetadatacopies_arg, "MetadataCopiesPV", "0|1|2")
|
||||
val(metadatacopies_VAL, metadatacopies_arg, "unused", "unused")
|
||||
val(polloperation_VAL, polloperation_arg, "PollOp", "pvmove|convert|merge|merge_thin")
|
||||
val(writemostly_VAL, writemostly_arg, "WriteMostlyPV", "PV[:t|n|y]")
|
||||
val(syncaction_VAL, syncaction_arg, "SyncAction", "check|repair")
|
||||
val(reportformat_VAL, reportformat_arg, "ReportFmt", "basic|json")
|
||||
val(configreport_VAL, configreport_arg, "ConfigReport", "log|vg|lv|pv|pvseg|seg")
|
||||
val(configtype_VAL, configtype_arg, "ConfigType", "current|default|diff|full|list|missing|new|profilable|profilable-command|profilable-metadata")
|
||||
|
||||
/* this should always be last */
|
||||
val(VAL_COUNT, NULL, NULL, NULL)
|
||||
|
@@ -482,9 +482,6 @@ static int _vgchange_metadata_copies(struct cmd_context *cmd,
|
||||
{
|
||||
uint32_t mda_copies = arg_uint_value(cmd, vgmetadatacopies_ARG, DEFAULT_VGMETADATACOPIES);
|
||||
|
||||
log_warn("vgchange_metadata_copies new %u vg_mda_copies %u D %u",
|
||||
mda_copies, vg_mda_copies(vg), DEFAULT_VGMETADATACOPIES);
|
||||
|
||||
if (mda_copies == vg_mda_copies(vg)) {
|
||||
if (vg_mda_copies(vg) == VGMETADATACOPIES_UNMANAGED)
|
||||
log_warn("Number of metadata copies for VG %s is already unmanaged.",
|
||||
|
@@ -157,12 +157,24 @@ int vgconvert(struct cmd_context *cmd, int argc, char **argv)
|
||||
return EINVALID_CMD_LINE;
|
||||
}
|
||||
|
||||
if (arg_is_set(cmd, metadatacopies_ARG)) {
|
||||
log_error("Invalid option --metadatacopies, "
|
||||
"use --pvmetadatacopies instead.");
|
||||
return EINVALID_CMD_LINE;
|
||||
}
|
||||
if (!(cmd->fmt->features & FMT_MDAS) &&
|
||||
arg_is_set(cmd, pvmetadatacopies_ARG)) {
|
||||
(arg_is_set(cmd, pvmetadatacopies_ARG) ||
|
||||
arg_is_set(cmd, metadatasize_ARG))) {
|
||||
log_error("Metadata parameters only apply to text format");
|
||||
return EINVALID_CMD_LINE;
|
||||
}
|
||||
|
||||
if (arg_is_set(cmd, pvmetadatacopies_ARG) &&
|
||||
arg_int_value(cmd, pvmetadatacopies_ARG, -1) > 2) {
|
||||
log_error("Metadatacopies may only be 0, 1 or 2");
|
||||
return EINVALID_CMD_LINE;
|
||||
}
|
||||
|
||||
if (!(cmd->fmt->features & FMT_BAS) &&
|
||||
arg_is_set(cmd, bootloaderareasize_ARG)) {
|
||||
log_error("Bootloader area parameters only apply to text format");
|
||||
|
@@ -38,7 +38,7 @@ static int vgdisplay_single(struct cmd_context *cmd, const char *vg_name,
|
||||
vgdisplay_extents(vg);
|
||||
|
||||
process_each_lv_in_vg(cmd, vg, NULL, NULL, 0, NULL,
|
||||
NULL, (process_single_lv_fn_t)lvdisplay_full);
|
||||
(process_single_lv_fn_t)lvdisplay_full);
|
||||
|
||||
log_print("--- Physical volumes ---");
|
||||
process_each_pv_in_vg(cmd, vg, NULL,
|
||||
|
@@ -136,6 +136,12 @@ int vgextend(struct cmd_context *cmd, int argc, char **argv)
|
||||
return EINVALID_CMD_LINE;
|
||||
}
|
||||
|
||||
if (arg_is_set(cmd, metadatacopies_ARG)) {
|
||||
log_error("Invalid option --metadatacopies, "
|
||||
"use --pvmetadatacopies instead.");
|
||||
return EINVALID_CMD_LINE;
|
||||
}
|
||||
|
||||
vg_name = skip_dev_dir(cmd, argv[0], NULL);
|
||||
argc--;
|
||||
argv++;
|
||||
|
@@ -33,5 +33,5 @@ int vgmknodes(struct cmd_context *cmd, int argc, char **argv)
|
||||
if (!lv_mknodes(cmd, NULL))
|
||||
return_ECMD_FAILED;
|
||||
|
||||
return process_each_lv(cmd, argc, argv, NULL, NULL, LCK_VG_READ, NULL, NULL, &_vgmknodes_single);
|
||||
return process_each_lv(cmd, argc, argv, NULL, NULL, LCK_VG_READ, NULL, &_vgmknodes_single);
|
||||
}
|
||||
|
@@ -62,7 +62,7 @@ static int vgremove_single(struct cmd_context *cmd, const char *vg_name,
|
||||
}
|
||||
|
||||
if ((ret = process_each_lv_in_vg(cmd, vg, NULL, NULL, 1, &void_handle,
|
||||
NULL, (process_single_lv_fn_t)lvremove_single)) != ECMD_PROCESSED) {
|
||||
(process_single_lv_fn_t)lvremove_single)) != ECMD_PROCESSED) {
|
||||
stack;
|
||||
return ret;
|
||||
}
|
||||
|
Reference in New Issue
Block a user