1
0
mirror of git://sourceware.org/git/lvm2.git synced 2025-10-22 19:33:16 +03:00

Compare commits

..

15 Commits

Author SHA1 Message Date
Bryn M. Reeves
63e6ef8491 man: add --headers to dmstats.8.in 2015-07-31 15:10:28 +01:00
Bryn M. Reeves
a20989174f man: document --timestamps in dmstats.8.in 2015-07-31 15:10:28 +01:00
Bryn M. Reeves
8df54e25c6 dmsetup: make '--timestamps' set --headers=time
Make '--timestamps' a shorthand for --headers=time.
2015-07-31 15:10:27 +01:00
Bryn M. Reeves
8ca04e316e dmsetup: add an optional header to dmsetup reports
Add optional headers to dmsetup and dmstats reports. These are
selected by the user in a similar way to report fields, e.g.:

Time: 27/07/15 12:07:56           Count:            0
Name             RgID  ArID  RRqM/s   WRqM/s   R/s   W/s   RSz/s WSz/s AvRqS QSize SvcTm Util% AWait
vg_hex-lv_home       0     0     0.00     0.00  0.00 43.00     0 416.00k 9.50k  1.00  1.86  8.00 30.44
vg_hex-lv_root       0     0     0.00     0.00  0.00  0.00     0       0     0  0.00  0.00  0.00  0.00
vg_hex-lv_images     0     0     0.00     0.00  0.00  0.00     0       0     0  0.00  0.00  0.00  0.00

Selects the 'time' and 'report_count' headers to be output.
2015-07-31 15:10:27 +01:00
Bryn M. Reeves
e8eed522a8 dmsetup: add --timestamps switch
Add a switch to optionally print a timestamp before displaying
each report. Use the same format as iostat for now (ISO format
controlled by S_FORMAT_TIME is also easy to add).
2015-07-31 15:10:27 +01:00
Bryn M. Reeves
cd8e3e28e5 libdm-report: add support for optional report headers
Add the ability to output a reow of header data before the main
report. This can be used to print a repeating banner including
data such as the time, the report count, and system performance
metrics.

A 'header' behaves in a similar way to a field; they are defined
by passing in an array of header types and selected using a string
of names. This allows programs using dm_report to customize the
available set of headers and allow their display to be configured
by the user.

Headers do not participate in any way in sorting or selection and
can only appear in the special 'header' section of the report.

A row of headers is added to a report by passing in a string of
header names to be parsed. Header output is either written as
soon as it is defined (unbuffered) or when the library user calls
the dm_report_output_header() function.
2015-07-31 15:10:27 +01:00
Bryn M. Reeves
8cc8b30ba7 man: add dmstats.8 2015-07-31 15:10:27 +01:00
Bryn M. Reeves
1638e090fc dmsetup: add dmstats command
Add arguments, report types, and a 'stats' command that dm-stats will
use and implement 'clear', 'create', 'delete', 'list', 'print', and
'report' sub-commands.

Adapt _display_info_cols() to allow reporting of statistics with the
DR_STATS report type. Since a single object (device) may have many rows
of statistics to report the call to dm_report_object() is placed inside
a loop over each statistics area present.

For non-stats reports or for devices with a single region spanning the
entire device the body of the loop is executed once.

Regions and the areas that they contain are always traversed in
ascending order beginning with area zero of region zero: all sorting is
handled by the report engine.
2015-07-31 15:10:27 +01:00
Bryn M. Reeves
e33656fc53 dmsetup: rename 'char *argc' and 'char ***argv' in _process_switches
Rename these two variables to 'argcp' and 'argvp' to make it clear
we are dealing with pointers to an 'int argc' and 'char **argv'.
2015-07-31 12:57:35 +01:00
Bryn M. Reeves
13d5b8100a libdevmapper: add statistics data structures and interface
Add data structures, type definitions and interfaces to
libdevmapper to work with device-mapper statistics.

To simplify handling gaps in the sequence of region_ids for users
of the library, and to allow for future changes in the data
structures used to contain statistics values in userspace, the data
structures themselves are not exported in libdevmapper.h.

Instead an opaque handle of type struct dm_stats* is obtained by
calling the library and all subsequent statistics operations are
carried out using this handle. A dm_stats object represents the
complete set of available counter sets for an individual mapped
device.

The dm_stats handle contains a pointer to a table of one or more
dm_stats_region objects representing the regions registered with the
@stats_create message. These in turn point to a table of one or more
dm_stats_counters objects containing the counter sets for each defined
area within the region:

  dm_stats->dm_stats_region[nr_regions]->dm_stats_counters[nr_areas]

This structure is private to the library and may change in future
versions: all users should make use of the public interface and treat
the dm_stats type as an opaque handle. Accessor methods are provided
to obtain values stored in individual region and area objects.

Ranges and counter sets are stored in order of increasing device
sector.

Public methods are provided to create and destroy handles and to
list, create, and destroy, statistics regions as well as to obtain and
parse the counter data.

Linux iostat-style derived performance metrics are provided to return
higher-level performance metrics:

    dm_stats_get_throughput()
    dm_stats_get_utilization()
    dm_stats_get_service_time()
    dm_stats_get_rd_merges_per_sec()
    dm_stats_get_wr_merges_per_sec()
    dm_stats_get_reads_per_sec()
    dm_stats_get_read_sectors_per_sec()
    dm_stats_get_writes_per_sec()
    dm_stats_get_write_sectors_per_sec()
    dm_stats_get_average_request_size()
    dm_stats_get_average_queue_size()
    dm_stats_get_await()
    dm_stats_get_r_await()
    dm_stats_get_w_await()
2015-07-31 12:57:35 +01:00
Bryn M. Reeves
077afe5b23 libdm-report: rename dm_report_headings()
Rename dm_report_headings to dm_report_column_headings() to make
it clear that it's the column headings being output.
2015-07-31 12:17:38 +01:00
Bryn M. Reeves
aa4d97d318 libdm-report: fix row and headings leaks
Not releasing objects back to the pool is fine for short-lived
pools since the memory will be freed when dm_pool_destroy() is
called.

Any pool that may be long-lived needs to be more careful to free
objects back to the pool to avoid leaking memory that will not be
reclaimed until the pool is destroyed at process exit time.

The report pool currently leaks each headings lines and some row
data.

Although dm_report_output() tries to free the first allocated row
this may end up freeing a later row due to sorting of the row list
while reporting. Store a pointer to the first allocated row from
_do_report_obect() instead and free this at the end of
_output_as_columns(), _output_as_rows(), and dm_report_clear().

Also make sure to call dm_pool_free() for the headings line built
in _report_headings().

Without these changes dmstats reports can leak around 600k in 10m
(exact rate depends on fields and values):

 top - 12:11:32 up 4 days,  3:16, 15 users,  load average: 0.01, 0.12, 0.14
  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
 6473 root      20   0  130196   3124   2792 S   0.0  0.0   0:00.00 dmstats

 top - 12:22:04 up 4 days,  3:26, 15 users,  load average: 0.06, 0.11, 0.13
  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
 6498 root      20   0  130836   3712   2752 S   0.0  0.0   0:00.60 dmstats

With this patch no increase in RSS is seen:

 top - 13:54:58 up 4 days,  4:59, 15 users,  load average: 0.12, 0.14, 0.14
  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
13962 root      20   0  130196   2996   2688 S   0.0  0.0   0:00.00 dmstats

 top - 14:04:31 up 4 days,  5:09, 15 users,  load average: 1.02, 0.67, 0.36
  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
13962 root      20   0  130196   2996   2688 S   0.3  0.0   0:00.32 dmstats
2015-07-31 12:06:10 +01:00
Bryn M. Reeves
a4b5f8ef39 libdm-report: add dm_report_clear()
Add a call to clear (abandon) a report's current data. This can
be used by callers that make repeating reports, such as dmstats,
in order to throw away the data of the first iteration (which will
have accumulated over some unknown interval).
2015-07-31 12:06:10 +01:00
Bryn M. Reeves
795c590980 libdm-report: add dm_report_headings()
Add a function to output the headings of columns-based reports
even if they have already been shown.

This will be used by dmstats reports to produce iostat-like
repeating reports of statistics values.

This patch removes a check for RH_HEADINGS_PRINTED from
_report_headings that prevents headings being displaye if the flag
is already set; this check is redundant since the only existing
caller (_output_as_columns()) already tests the flag before
calling the function.
2015-07-31 12:06:10 +01:00
Bryn M. Reeves
ba2da29c4c libdm: add interval support to libdm reports
Add functions for dealing with reports that repeat at a set time
interval:

  dm_report_get_interval()
  dm_report_set_interval()
  dm_report_wait_interval()

The fist should be called following dm_report_init() to set the
desired interval to be used with this report. Once the report has
been prepared the program should call dm_report_wait_interval()
to suspend execution until the current interval expires. When the
wait function returns the caller should obtain and output report
data for the new interval.

Measure the actual wait interval in dm_report_wait_interval and
add dm_report_get_last_interval() so that callers can obtain it
to pass to statistics methods.

Make report interval handling consistent everywhere in libdm by
storing the report interval in nanoseconds and adding additional
helper functions to get and set a value in miliseconds. This is
consistent with the other parts of libdm that handle statistics
intervals and removes the need to convert between different
representations within the library - scaling is only needed to
either present a value to the user or to pass to an external
function that expects a particular unit of time (e.g. usleep()).
2015-07-31 12:06:04 +01:00
71 changed files with 2489 additions and 3188 deletions

View File

@@ -1 +1 @@
2.02.128(2)-git (2015-08-10)
2.02.127(2)-git (2015-07-24)

View File

@@ -1 +1 @@
1.02.105-git (2015-08-10)
1.02.104-git (2015-07-24)

View File

@@ -1,22 +1,8 @@
Version 2.02.128 -
===================================
Check for valid cache mode in validation of cache segment.
Enhance internal API cache_set_mode() and cache_set_policy().
Enhance toollib's get_cache_params().
Runtime detect presence of cache smq policy.
Add demo cache-mq and cache-smq profiles.
Add cmd profilable allocation/cache_policy,cache_settings,cache_mode.
Require cache_check 0.5.4 for use of --clear-needs-check-flag.
Fix lvmetad udev rules to not override SYSTEMD_WANTS, add the service instead.
Version 2.02.127 - 10th August 2015
===================================
Version 2.02.127 -
=================================
Do not init filters, locking, lvmetad, lvmpolld if command doesn't use it.
Order fields in struct cmd_context more logically.
Add lock_type to lvmcache VG summary and info structs.
Recognise vg/lv name format in dmsetup.
Fix regression in cache causing some PVs to bypass filters (2.02.105).
Make configure --enable-realtime the default now.
Update .gitignore and configure.in files to reflect usage of current tree.
Version 2.02.126 - 24th July 2015
=================================

View File

@@ -1,32 +1,6 @@
Version 1.02.105 -
===================================
Add more arg validation for dm_tree_node_add_cache_target().
Add --alldevices switch to replace use of --force for stats create / delete.
Version 1.02.104 - 10th August 2015
===================================
Add dmstats.8 man page
Add dmstats --segments switch to create one region per device segment.
Add dmstats --regionid, --allregions to specify a single / all stats regions.
Add dmstats --allprograms for stats commands that filter by program ID.
Add dmstats --auxdata and --programid args to specify aux data and program ID.
Add report stats sub-command to provide repeating stats reports.
Add clear, delete, list, and print stats sub-commands.
Add create stats sub-command and --start, --length, --areas and --areasize.
Recognize 'dmstats' as an alias for 'dmsetup stats' when run with this name.
Add a 'stats' command to dmsetup to configure, manage and report stats data.
Add statistics fields to dmsetup -o.
Add libdm-stats library to allow management of device-mapper statistics.
Add --nosuffix to suppress dmsetup unit suffixes in report output.
Add --units to control dmsetup report field output units.
Add support to redisplay column headings for repeating column reports.
Fix report header and row resource leaks.
Report timestamps of ioctls with dmsetup -vvv.
Recognize report field name variants without any underscores too.
Add dmsetup --interval and --count to repeat reports at specified intervals.
Version 1.02.104 -
=================================
Add dm_timestamp functions to libdevmapper.
Recognise vg/lv name format in dmsetup.
Move size display code to libdevmapper as dm_size_to_string.
Version 1.02.103 - 24th July 2015
=================================

View File

@@ -1,5 +1,5 @@
#
# Copyright (C) 2004-2015 Red Hat, Inc. All rights reserved.
# Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
#
# This file is part of LVM2.
#
@@ -20,11 +20,7 @@ CONFDEST=lvm.conf
CONFLOCAL=lvmlocal.conf
PROFILE_TEMPLATES=command_profile_template.profile metadata_profile_template.profile
PROFILES=$(PROFILE_TEMPLATES) \
$(srcdir)/cache-mq.profile \
$(srcdir)/cache-smq.profile \
$(srcdir)/thin-generic.profile \
$(srcdir)/thin-performance.profile
PROFILES=$(PROFILE_TEMPLATES) $(srcdir)/thin-generic.profile $(srcdir)/thin-performance.profile
include $(top_builddir)/make.tmpl

View File

@@ -1,20 +0,0 @@
# Demo configuration 'mq' cache policy
#
# Note: This policy has been deprecated in favor of the smq policy
# keyword "default" means, setting is left with kernel defaults.
#
allocation {
cache_pool_chunk_size = 64
cache_mode = "writethrough"
cache_policy = "mq"
cache_settings {
mq {
sequential_threshold = "default" # #nr_sequential_ios
random_threshold = "default" # #nr_random_ios
read_promote_adjustment = "default"
write_promote_adjustment = "default"
discard_promote_adjustment = "default"
}
}
}

View File

@@ -1,14 +0,0 @@
# Demo configuration 'smq' cache policy
#
# The stochastic multi-queue (smq) policy addresses some of the problems
# with the multiqueue (mq) policy and uses less memory.
#
allocation {
cache_pool_chunk_size = 64
cache_mode = "writethrough"
cache_policy = "smq"
cache_settings {
# currently no settins for "smq" policy
}
}

25
configure vendored
View File

@@ -5734,7 +5734,7 @@ fi
done
for ac_header in termios.h sys/statvfs.h sys/timerfd.h
for ac_header in termios.h sys/statvfs.h
do :
as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
@@ -8813,27 +8813,20 @@ $as_echo "$as_me: WARNING: cache_check not found in path $PATH" >&2;}
fi
fi
if test "$CACHE_CHECK_NEEDS_CHECK" = yes; then
$CACHE_CHECK_CMD -V 2>/dev/null >conftest.tmp
read -r CACHE_CHECK_VSN < conftest.tmp
IFS=. read -r CACHE_CHECK_VSN_MAJOR CACHE_CHECK_VSN_MINOR CACHE_CHECK_VSN_PATCH < conftest.tmp
rm -f conftest.tmp
CACHE_CHECK_VSN=`"$CACHE_CHECK_CMD" -V 2>/dev/null`
CACHE_CHECK_VSN_MAJOR=`echo "$CACHE_CHECK_VSN" | $AWK -F '.' '{print $1}'`
CACHE_CHECK_VSN_MINOR=`echo "$CACHE_CHECK_VSN" | $AWK -F '.' '{print $2}'`
# Require version >= 0.5.4 for --clear-needs-check-flag
if test -z "$CACHE_CHECK_VSN_MAJOR" \
|| test -z "$CACHE_CHECK_VSN_MINOR" \
|| test -z "$CACHE_CHECK_VSN_PATCH"; then
if test -z "$CACHE_CHECK_VSN_MAJOR" -o -z "$CACHE_CHECK_VSN_MINOR"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $CACHE_CHECK_CMD: Bad version \"$CACHE_CHECK_VSN\" found" >&5
$as_echo "$as_me: WARNING: $CACHE_CHECK_CMD: Bad version \"$CACHE_CHECK_VSN\" found" >&2;}
CACHE_CHECK_VERSION_WARN=y
CACHE_CHECK_NEEDS_CHECK=no
elif test "$CACHE_CHECK_VSN_MAJOR" -eq 0 ; then
if test "$CACHE_CHECK_VSN_MINOR" -lt 5 \
|| test "$CACHE_CHECK_VSN_MINOR" -eq 5 -a "$CACHE_CHECK_VSN_PATCH" -lt 4; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $CACHE_CHECK_CMD: Old version \"$CACHE_CHECK_VSN\" found" >&5
elif test "$CACHE_CHECK_VSN_MAJOR" -eq 0 -a "$CACHE_CHECK_VSN_MINOR" -lt 5; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $CACHE_CHECK_CMD: Old version \"$CACHE_CHECK_VSN\" found" >&5
$as_echo "$as_me: WARNING: $CACHE_CHECK_CMD: Old version \"$CACHE_CHECK_VSN\" found" >&2;}
CACHE_CHECK_VERSION_WARN=y
CACHE_CHECK_NEEDS_CHECK=no
fi
CACHE_CHECK_VERSION_WARN=y
CACHE_CHECK_NEEDS_CHECK=no
fi
fi
# Empty means a config way to ignore cache dumping

View File

@@ -103,7 +103,7 @@ AC_CHECK_HEADERS([assert.h ctype.h dirent.h errno.h fcntl.h float.h \
sys/time.h sys/types.h sys/utsname.h sys/wait.h time.h \
unistd.h], , [AC_MSG_ERROR(bailing out)])
AC_CHECK_HEADERS(termios.h sys/statvfs.h sys/timerfd.h)
AC_CHECK_HEADERS(termios.h sys/statvfs.h)
case "$host_os" in
linux*)
@@ -584,25 +584,18 @@ case "$CACHE" in
fi
fi
if test "$CACHE_CHECK_NEEDS_CHECK" = yes; then
$CACHE_CHECK_CMD -V 2>/dev/null >conftest.tmp
read -r CACHE_CHECK_VSN < conftest.tmp
IFS=. read -r CACHE_CHECK_VSN_MAJOR CACHE_CHECK_VSN_MINOR CACHE_CHECK_VSN_PATCH < conftest.tmp
rm -f conftest.tmp
CACHE_CHECK_VSN=`"$CACHE_CHECK_CMD" -V 2>/dev/null`
CACHE_CHECK_VSN_MAJOR=`echo "$CACHE_CHECK_VSN" | $AWK -F '.' '{print $1}'`
CACHE_CHECK_VSN_MINOR=`echo "$CACHE_CHECK_VSN" | $AWK -F '.' '{print $2}'`
# Require version >= 0.5.4 for --clear-needs-check-flag
if test -z "$CACHE_CHECK_VSN_MAJOR" \
|| test -z "$CACHE_CHECK_VSN_MINOR" \
|| test -z "$CACHE_CHECK_VSN_PATCH"; then
if test -z "$CACHE_CHECK_VSN_MAJOR" -o -z "$CACHE_CHECK_VSN_MINOR"; then
AC_MSG_WARN([$CACHE_CHECK_CMD: Bad version "$CACHE_CHECK_VSN" found])
CACHE_CHECK_VERSION_WARN=y
CACHE_CHECK_NEEDS_CHECK=no
elif test "$CACHE_CHECK_VSN_MAJOR" -eq 0 ; then
if test "$CACHE_CHECK_VSN_MINOR" -lt 5 \
|| test "$CACHE_CHECK_VSN_MINOR" -eq 5 -a "$CACHE_CHECK_VSN_PATCH" -lt 4; then
AC_MSG_WARN([$CACHE_CHECK_CMD: Old version "$CACHE_CHECK_VSN" found])
CACHE_CHECK_VERSION_WARN=y
CACHE_CHECK_NEEDS_CHECK=no
fi
elif test "$CACHE_CHECK_VSN_MAJOR" -eq 0 -a "$CACHE_CHECK_VSN_MINOR" -lt 5; then
AC_MSG_WARN([$CACHE_CHECK_CMD: Old version "$CACHE_CHECK_VSN" found])
CACHE_CHECK_VERSION_WARN=y
CACHE_CHECK_NEEDS_CHECK=no
fi
fi
# Empty means a config way to ignore cache dumping

View File

@@ -17,7 +17,6 @@
#include <signal.h>
#include <errno.h>
#include <fcntl.h>
#include <syslog.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/un.h>
@@ -27,16 +26,14 @@ static int info = 0;
static int dump = 0;
static int wait_opt = 0;
static int force_opt = 0;
static int kill_vg = 0;
static int drop_vg = 0;
static int gl_enable = 0;
static int gl_disable = 0;
static int stop_lockspaces = 0;
static char *arg_vg_name = NULL;
static char *able_vg_name = NULL;
#define DUMP_SOCKET_NAME "lvmlockd-dump.sock"
#define DUMP_BUF_SIZE (1024 * 1024)
static char dump_buf[DUMP_BUF_SIZE+1];
static char dump_buf[DUMP_BUF_SIZE];
static int dump_len;
static struct sockaddr_un dump_addr;
static socklen_t dump_addrlen;
@@ -449,9 +446,9 @@ static int do_able(const char *req_name)
int rv;
reply = _lvmlockd_send(req_name,
"cmd = %s", "lvmlockctl",
"cmd = %s", "lvmlock",
"pid = %d", getpid(),
"vg_name = %s", arg_vg_name,
"vg_name = %s", able_vg_name,
NULL);
if (!_lvmlockd_result(reply, &result)) {
@@ -480,7 +477,7 @@ static int do_stop_lockspaces(void)
strcat(opts, "force ");
reply = _lvmlockd_send("stop_all",
"cmd = %s", "lvmlockctl",
"cmd = %s", "lvmlock",
"pid = %d", getpid(),
"opts = %s", opts[0] ? opts : "none",
NULL);
@@ -496,87 +493,6 @@ static int do_stop_lockspaces(void)
return rv;
}
static int do_kill(void)
{
daemon_reply reply;
int result;
int rv;
syslog(LOG_EMERG, "Lost access to sanlock lease storage in VG %s.", arg_vg_name);
/* These two lines explain the manual alternative to the FIXME below. */
syslog(LOG_EMERG, "Immediately deactivate LVs in VG %s.", arg_vg_name);
syslog(LOG_EMERG, "Once VG is unused, run lvmlockctl --drop %s.", arg_vg_name);
/*
* It may not be strictly necessary to notify lvmlockd of the kill, but
* lvmlockd can use this information to avoid attempting any new lock
* requests in the VG (which would fail anyway), and can return an
* error indicating that the VG has been killed.
*/
reply = _lvmlockd_send("kill_vg",
"cmd = %s", "lvmlockctl",
"pid = %d", getpid(),
"vg_name = %s", arg_vg_name,
NULL);
if (!_lvmlockd_result(reply, &result)) {
log_error("lvmlockd result %d", result);
rv = result;
} else {
rv = 0;
}
daemon_reply_destroy(reply);
/*
* FIXME: here is where we should implement a strong form of
* blkdeactivate, and if it completes successfully, automatically call
* do_drop() afterward. (The drop step may not always be necessary
* if the lvm commands run while shutting things down release all the
* leases.)
*
* run_strong_blkdeactivate();
* do_drop();
*/
return rv;
}
static int do_drop(void)
{
daemon_reply reply;
int result;
int rv;
syslog(LOG_WARNING, "Dropping locks for VG %s.", arg_vg_name);
/*
* Check for misuse by looking for any active LVs in the VG
* and refusing this operation if found? One possible way
* to kill LVs (e.g. if fs cannot be unmounted) is to suspend
* them, or replace them with the error target. In that
* case the LV will still appear to be active, but it is
* safe to release the lock.
*/
reply = _lvmlockd_send("drop_vg",
"cmd = %s", "lvmlockctl",
"pid = %d", getpid(),
"vg_name = %s", arg_vg_name,
NULL);
if (!_lvmlockd_result(reply, &result)) {
log_error("lvmlockd result %d", result);
rv = result;
} else {
rv = 0;
}
daemon_reply_destroy(reply);
return rv;
}
static void print_usage(void)
{
printf("lvmlockctl options\n");
@@ -593,16 +509,12 @@ static void print_usage(void)
printf(" Wait option for other commands.\n");
printf("--force | -f 0|1>\n");
printf(" Force option for other commands.\n");
printf("--kill | -k <vg_name>\n");
printf(" Kill access to the vg when sanlock cannot renew lease.\n");
printf("--drop | -r <vg_name>\n");
printf(" Clear locks for the vg after it has been killed and is no longer used.\n");
printf("--stop-lockspaces | -S\n");
printf(" Stop all lockspaces.\n");
printf("--gl-enable <vg_name>\n");
printf(" Tell lvmlockd to enable the global lock in a sanlock vg.\n");
printf("--gl-disable <vg_name>\n");
printf(" Tell lvmlockd to disable the global lock in a sanlock vg.\n");
printf("--stop-lockspaces | -S\n");
printf(" Stop all lockspaces.\n");
}
static int read_options(int argc, char *argv[])
@@ -617,8 +529,6 @@ static int read_options(int argc, char *argv[])
{"dump", no_argument, 0, 'd' },
{"wait", required_argument, 0, 'w' },
{"force", required_argument, 0, 'f' },
{"kill", required_argument, 0, 'k' },
{"drop", required_argument, 0, 'r' },
{"gl-enable", required_argument, 0, 'E' },
{"gl-disable", required_argument, 0, 'D' },
{"stop-lockspaces", no_argument, 0, 'S' },
@@ -631,7 +541,7 @@ static int read_options(int argc, char *argv[])
}
while (1) {
c = getopt_long(argc, argv, "hqidE:D:w:k:r:S", long_options, &option_index);
c = getopt_long(argc, argv, "hqidE:D:w:S", long_options, &option_index);
if (c == -1)
break;
@@ -655,21 +565,13 @@ static int read_options(int argc, char *argv[])
case 'w':
wait_opt = atoi(optarg);
break;
case 'k':
kill_vg = 1;
arg_vg_name = strdup(optarg);
break;
case 'r':
drop_vg = 1;
arg_vg_name = strdup(optarg);
break;
case 'E':
gl_enable = 1;
arg_vg_name = strdup(optarg);
able_vg_name = strdup(optarg);
break;
case 'D':
gl_disable = 1;
arg_vg_name = strdup(optarg);
able_vg_name = strdup(optarg);
break;
case 'S':
stop_lockspaces = 1;
@@ -714,16 +616,6 @@ int main(int argc, char **argv)
goto out;
}
if (kill_vg) {
rv = do_kill();
goto out;
}
if (drop_vg) {
rv = do_drop();
goto out;
}
if (gl_enable) {
rv = do_able("enable_gl");
goto out;

View File

@@ -45,7 +45,5 @@ static inline void lvmlockd_close(daemon_handle h)
#define EMANAGER 214
#define EPREPARE 215
#define ELOCKD 216
#define EVGKILLED 217 /* sanlock lost access to leases and VG is killed. */
#define ELOCKIO 218 /* sanlock io errors during lock op, may be transient. */
#endif /* _LVM_LVMLOCKD_CLIENT_H */

View File

@@ -735,10 +735,6 @@ static const char *op_str(int x)
return "find_free_lock";
case LD_OP_FORGET_VG_NAME:
return "forget_vg_name";
case LD_OP_KILL_VG:
return "kill_vg";
case LD_OP_DROP_VG:
return "drop_vg";
default:
return "op_unknown";
};
@@ -790,9 +786,7 @@ int version_from_args(char *args, unsigned int *major, unsigned int *minor, unsi
char *major_str, *minor_str, *patch_str;
char *n, *d1, *d2;
memset(version, 0, sizeof(version));
strncpy(version, args, MAX_ARGS);
version[MAX_ARGS] = '\0';
n = strstr(version, ":");
if (n)
@@ -1022,10 +1016,7 @@ static int res_lock(struct lockspace *ls, struct resource *r, struct action *act
uint32_t r_version = 0;
int rv;
if (r->type == LD_RT_LV)
log_debug("S %s R %s res_lock mode %s (%s)", ls->name, r->name, mode_str(act->mode), act->lv_name);
else
log_debug("S %s R %s res_lock mode %s", ls->name, r->name, mode_str(act->mode));
log_debug("S %s R %s res_lock mode %s", ls->name, r->name, mode_str(act->mode));
if (r->mode == LD_LK_SH && act->mode == LD_LK_SH)
goto add_lk;
@@ -1287,12 +1278,8 @@ static int res_unlock(struct lockspace *ls, struct resource *r,
return -ENOENT;
do_unlock:
if (act->op == LD_OP_CLOSE)
log_debug("S %s R %s res_unlock from close", ls->name, r->name);
else if (r->type == LD_RT_LV)
log_debug("S %s R %s res_unlock (%s)", ls->name, r->name, act->lv_name);
else
log_debug("S %s R %s res_unlock", ls->name, r->name);
log_debug("S %s R %s res_unlock %s", ls->name, r->name,
(act->op == LD_OP_CLOSE) ? "from close" : "");
/* send unlock to lm when last sh lock is unlocked */
if (lk->mode == LD_LK_SH) {
@@ -1840,7 +1827,7 @@ static int for_each_lock(struct lockspace *ls, int locks_do)
return 0;
}
static int clear_locks(struct lockspace *ls, int free_vg, int drop_vg)
static int clear_locks(struct lockspace *ls, int free_vg)
{
struct resource *r, *r_safe;
struct lock *lk, *lk_safe;
@@ -1859,10 +1846,10 @@ static int clear_locks(struct lockspace *ls, int free_vg, int drop_vg)
/*
* Stopping a lockspace shouldn't happen with LV locks
* still held, but it will be stopped with GL and VG
* locks held. The drop_vg case may see LV locks.
* locks held.
*/
if (lk->flags & LD_LF_PERSISTENT && !drop_vg)
if (lk->flags & LD_LF_PERSISTENT)
log_error("S %s R %s clear lock persistent", ls->name, r->name);
else
log_debug("S %s R %s clear lock mode %s client %d", ls->name, r->name, mode_str(lk->mode), lk->client_id);
@@ -1896,8 +1883,8 @@ static int clear_locks(struct lockspace *ls, int free_vg, int drop_vg)
rv = lm_unlock(ls, r, NULL, r_version, free_vg ? LMUF_FREE_VG : 0);
if (rv < 0) {
/* should never happen */
log_error("S %s R %s clear_locks free %d drop %d lm unlock error %d",
ls->name, r->name, free_vg, drop_vg, rv);
log_error("S %s R %s clear_locks free %d lm unlock error %d",
ls->name, r->name, free_vg, rv);
}
list_for_each_entry_safe(act, act_safe, &r->actions, list) {
@@ -1988,15 +1975,11 @@ static int other_sanlock_vgs_exist(struct lockspace *ls_rem)
struct lockspace *ls;
list_for_each_entry(ls, &lockspaces_inactive, list) {
if (ls->lm_type != LD_LM_SANLOCK)
continue;
log_debug("other sanlock vg exists inactive %s", ls->name);
return 1;
}
list_for_each_entry(ls, &lockspaces, list) {
if (ls->lm_type != LD_LM_SANLOCK)
continue;
if (!strcmp(ls->name, ls_rem->name))
continue;
log_debug("other sanlock vg exists %s", ls->name);
@@ -2006,28 +1989,6 @@ static int other_sanlock_vgs_exist(struct lockspace *ls_rem)
return 0;
}
/*
* LOCK is the main thing we're interested in; the others are unlikely.
*/
static int process_op_during_kill(struct action *act)
{
if (act->op == LD_OP_LOCK && act->mode == LD_LK_UN)
return 1;
switch (act->op) {
case LD_OP_LOCK:
case LD_OP_ENABLE:
case LD_OP_DISABLE:
case LD_OP_UPDATE:
case LD_OP_RENAME_BEFORE:
case LD_OP_RENAME_FINAL:
case LD_OP_FIND_FREE_LOCK:
return 0;
};
return 1;
}
/*
* Process actions queued for this lockspace by
* client_recv_action / add_lock_action.
@@ -2048,7 +2009,6 @@ static void *lockspace_thread_main(void *arg_in)
struct list_head tmp_act;
struct list_head act_close;
int free_vg = 0;
int drop_vg = 0;
int error = 0;
int adopt_flag = 0;
int wait_flag = 0;
@@ -2153,43 +2113,7 @@ static void *lockspace_thread_main(void *arg_in)
act = list_first_entry(&ls->actions, struct action, list);
if (act->op == LD_OP_KILL_VG && act->rt == LD_RT_VG) {
/* Continue processing until DROP_VG arrives. */
log_debug("S %s kill_vg", ls->name);
ls->kill_vg = 1;
list_del(&act->list);
act->result = 0;
add_client_result(act);
continue;
}
if (ls->kill_vg && !process_op_during_kill(act)) {
log_debug("S %s disallow op %s after kill_vg", ls->name, op_str(act->op));
list_del(&act->list);
act->result = -EVGKILLED;
add_client_result(act);
continue;
}
if (act->op == LD_OP_DROP_VG && act->rt == LD_RT_VG) {
/*
* If leases are released after i/o errors begin
* but before lvmlockctl --kill, then the VG is not
* killed, but drop is still needed to clean up the
* VG, so in that case there would be a drop op without
* a preceding kill op.
*/
if (!ls->kill_vg)
log_debug("S %s received drop without kill", ls->name);
log_debug("S %s drop_vg", ls->name);
ls->thread_work = 0;
ls->thread_stop = 1;
drop_vg = 1;
break;
}
if (act->op == LD_OP_STOP) {
/* thread_stop is already set */
ls->thread_work = 0;
break;
}
@@ -2313,9 +2237,6 @@ out_rem:
* allowed in emergency/force situations, otherwise it's
* obviously dangerous, since the lock holders are still
* operating under the assumption that they hold the lock.
* drop_vg drops all existing locks, but should only
* happen when the VG access has been forcibly and
* succesfully terminated.
*
* For vgremove of a sanlock vg, the vg lock will be held,
* and possibly the gl lock if this vg holds the gl.
@@ -2324,7 +2245,7 @@ out_rem:
log_debug("S %s clearing locks", ls->name);
rv = clear_locks(ls, free_vg, drop_vg);
rv = clear_locks(ls, free_vg);
/*
* Tell any other hosts in the lockspace to leave it
@@ -2362,8 +2283,6 @@ out_act:
act->result = 0;
} else if (act->op == LD_OP_STOP)
act->result = 0;
else if (act->op == LD_OP_DROP_VG)
act->result = 0;
else if (act->op == LD_OP_RENAME_BEFORE)
act->result = 0;
else
@@ -2397,7 +2316,6 @@ out_act:
pthread_mutex_lock(&lockspaces_mutex);
ls->thread_done = 1;
ls->free_vg = free_vg;
ls->drop_vg = drop_vg;
pthread_mutex_unlock(&lockspaces_mutex);
/*
@@ -2588,8 +2506,18 @@ static int add_dlm_global_lockspace(struct action *act)
if (gl_running_dlm)
return -EEXIST;
gl_running_dlm = 1;
/* Keep track of whether we automatically added
the global ls, so we know to automatically
remove it. */
if (act)
gl_auto_dlm = 0;
else
gl_auto_dlm = 1;
/*
* There's a short period after which a previous gl lockspace thread
* has set gl_running_dlm = 0, but before its ls struct has been
@@ -2598,9 +2526,11 @@ static int add_dlm_global_lockspace(struct action *act)
*/
rv = add_lockspace_thread(gl_lsname_dlm, NULL, NULL, LD_LM_DLM, NULL, act);
if (rv < 0) {
log_error("add_dlm_global_lockspace add_lockspace_thread %d", rv);
gl_running_dlm = 0;
gl_auto_dlm = 0;
}
return rv;
@@ -2653,12 +2583,28 @@ out:
}
/*
* When the first dlm lockspace is added for a vg, automatically add a separate
* dlm lockspace for the global lock.
* When the first dlm lockspace is added for a vg,
* automatically add a separate dlm lockspace for the
* global lock if it hasn't been done explicitly.
* This is to make the dlm global lockspace work similarly to
* the sanlock global lockspace, which is "automatic" by
* nature of being one of the vg lockspaces.
*
* For sanlock, a separate lockspace is not used for the global lock, but the
* gl lock lives in a vg lockspace, (although it's recommended to create a
* For sanlock, a separate lockspace is not used for
* the global lock, but the gl lock lives in a vg
* lockspace, (although it's recommended to create a
* special vg dedicated to holding the gl).
*
* N.B. for dlm, if this is an add+WAIT action for a vg
* lockspace, and this triggered the automatic addition
* of the global lockspace, then the action may complete
* for the vg ls add, while the gl ls add is still in
* progress. If the caller wants to ensure that the
* gl ls add is complete, they should explicitly add+WAIT
* the gl ls.
*
* If this function returns and error, the caller
* will queue the act with that error for the client.
*/
static int add_lockspace(struct action *act)
@@ -2668,11 +2614,6 @@ static int add_lockspace(struct action *act)
memset(ls_name, 0, sizeof(ls_name));
/*
* FIXME: I don't think this is used any more.
* Remove it, or add the ability to start the global
* dlm lockspace using lvmlockctl?
*/
if (act->rt == LD_RT_GL) {
if (gl_use_dlm) {
rv = add_dlm_global_lockspace(act);
@@ -2756,13 +2697,13 @@ static int rem_lockspace(struct action *act)
pthread_mutex_unlock(&lockspaces_mutex);
/*
* The dlm global lockspace was automatically added when
* the first dlm vg lockspace was added, now reverse that
* If the dlm global lockspace was automatically added when
* the first dlm vg lockspace was added, then reverse that
* by automatically removing the dlm global lockspace when
* the last dlm vg lockspace is removed.
*/
if (rt == LD_RT_VG && gl_use_dlm)
if (rt == LD_RT_VG && gl_use_dlm && gl_auto_dlm)
rem_dlm_global_lockspace();
return 0;
@@ -3620,6 +3561,7 @@ static int add_lock_action(struct action *act)
if (ls_create_fail)
act->flags |= LD_AF_ADD_LS_ERROR;
return -ENOLS;
} else {
log_debug("lockspace not found %s", ls_name);
return -ENOLS;
@@ -3794,16 +3736,6 @@ static int str_to_op_rt(const char *req_name, int *op, int *rt)
*rt = LD_RT_VG;
return 0;
}
if (!strcmp(req_name, "kill_vg")) {
*op = LD_OP_KILL_VG;
*rt = LD_RT_VG;
return 0;
}
if (!strcmp(req_name, "drop_vg")) {
*op = LD_OP_DROP_VG;
*rt = LD_RT_VG;
return 0;
}
out:
return -1;
}
@@ -3954,8 +3886,6 @@ static int print_lockspace(struct lockspace *ls, const char *prefix, int pos, in
"thread_work=%d "
"thread_stop=%d "
"thread_done=%d "
"kill_vg=%d "
"drop_vg=%d "
"sanlock_gl_enabled=%d\n",
prefix,
ls->name,
@@ -3970,8 +3900,6 @@ static int print_lockspace(struct lockspace *ls, const char *prefix, int pos, in
ls->thread_work ? 1 : 0,
ls->thread_stop ? 1 : 0,
ls->thread_done ? 1 : 0,
ls->kill_vg,
ls->drop_vg,
ls->sanlock_gl_enabled ? 1 : 0);
}
@@ -4367,8 +4295,6 @@ static void client_recv_action(struct client *cl)
case LD_OP_FREE:
case LD_OP_RENAME_BEFORE:
case LD_OP_FIND_FREE_LOCK:
case LD_OP_KILL_VG:
case LD_OP_DROP_VG:
rv = add_lock_action(act);
break;
case LD_OP_FORGET_VG_NAME:

View File

@@ -247,8 +247,10 @@ int lm_rem_lockspace_dlm(struct lockspace *ls, int free_vg)
free(lmd);
ls->lm_data = NULL;
if (!strcmp(ls->name, gl_lsname_dlm))
if (!strcmp(ls->name, gl_lsname_dlm)) {
gl_running_dlm = 0;
gl_auto_dlm = 0;
}
return 0;
}
@@ -616,7 +618,6 @@ int lm_unlock_dlm(struct lockspace *ls, struct resource *r,
int lm_get_lockspaces_dlm(struct list_head *ls_rejoin)
{
static const char closedir_err_msg[] = "lm_get_lockspace_dlm: closedir failed";
struct lockspace *ls;
struct dirent *de;
DIR *ls_dir;
@@ -633,7 +634,7 @@ int lm_get_lockspaces_dlm(struct list_head *ls_rejoin)
if (!(ls = alloc_lockspace())) {
if (closedir(ls_dir))
log_error(closedir_err_msg);
log_error("lm_get_lockspace_dlm: closedir failed");
return -ENOMEM;
}
@@ -643,8 +644,7 @@ int lm_get_lockspaces_dlm(struct list_head *ls_rejoin)
list_add_tail(&ls->list, ls_rejoin);
}
if (closedir(ls_dir))
log_error(closedir_err_msg);
closedir(ls_dir);
return 0;
}

View File

@@ -51,8 +51,6 @@ enum {
LD_OP_RUNNING_LM,
LD_OP_FIND_FREE_LOCK,
LD_OP_FORGET_VG_NAME,
LD_OP_KILL_VG,
LD_OP_DROP_VG,
};
/* resource types */
@@ -186,8 +184,6 @@ struct lockspace {
unsigned int sanlock_gl_enabled: 1;
unsigned int sanlock_gl_dup: 1;
unsigned int free_vg: 1;
unsigned int kill_vg: 1;
unsigned int drop_vg: 1;
struct list_head actions; /* new client actions */
struct list_head resources; /* resource/lock state for gl/vg/lv */
@@ -311,7 +307,6 @@ static inline int list_empty(const struct list_head *head)
* or when disable_gl matches.
*/
EXTERN int gl_running_dlm;
EXTERN int gl_type_static;
EXTERN int gl_use_dlm;
EXTERN int gl_use_sanlock;
@@ -320,6 +315,9 @@ EXTERN pthread_mutex_t gl_type_mutex;
EXTERN char gl_lsname_dlm[MAX_NAME+1];
EXTERN char gl_lsname_sanlock[MAX_NAME+1];
EXTERN int gl_running_dlm;
EXTERN int gl_auto_dlm;
EXTERN int daemon_test; /* run as much as possible without a live lock manager */
EXTERN int daemon_debug;
EXTERN int daemon_host_id;

View File

@@ -33,101 +33,52 @@
#include <sys/socket.h>
/*
-------------------------------------------------------------------------------
For each VG, lvmlockd creates a sanlock lockspace that holds the leases for
that VG. There's a lease for the VG lock, and there's a lease for each active
LV. sanlock maintains (reads/writes) these leases, which exist on storage.
That storage is a hidden LV within the VG: /dev/vg/lvmlock. lvmlockd gives the
path of this internal LV to sanlock, which then reads/writes the leases on it.
# lvs -a cc -o+uuid
LV VG Attr LSize LV UUID
lv1 cc -wi-a----- 2.00g 7xoDtu-yvNM-iwQx-C94t-BbYs-UzBl-o8hAIa
lv2 cc -wi-a----- 100.00g exxNPX-wZdO-uCNy-yiGa-aJGT-JKVl-arfcYT
[lvmlock] cc -wi-ao---- 256.00m iLpDel-hR0T-hJ3u-rnVo-PcDh-mcjt-sF9egM
# sanlock status
s lvm_cc:1:/dev/mapper/cc-lvmlock:0
r lvm_cc:exxNPX-wZdO-uCNy-yiGa-aJGT-JKVl-arfcYT:/dev/mapper/cc-lvmlock:71303168:13 p 26099
r lvm_cc:7xoDtu-yvNM-iwQx-C94t-BbYs-UzBl-o8hAIa:/dev/mapper/cc-lvmlock:70254592:3 p 26099
This shows that sanlock is maintaining leases on /dev/mapper/cc-lvmlock.
sanlock acquires a lockspace lease when the lockspace is joined, i.e. when the
VG is started by 'vgchange --lock-start cc'. This lockspace lease exists at
/dev/mapper/cc-lvmlock offset 0, and sanlock regularly writes to it to maintain
ownership of it. Joining the lockspace (by acquiring the lockspace lease in
it) then allows standard resource leases to be acquired in the lockspace for
whatever the application wants. lvmlockd uses resource leases for the VG lock
and LV locks.
sanlock acquires a resource lease for each actual lock that lvm commands use.
Above, there are two LV locks that are held because the two LVs are active.
These are on /dev/mapper/cc-lvmlock at offsets 71303168 and 70254592. sanlock
does not write to these resource leases except when acquiring and releasing
them (e.g. lvchange -ay/-an). The renewal of the lockspace lease maintains
ownership of all the resource leases in the lockspace.
If the host loses access to the disk that the sanlock lv lives on, then sanlock
can no longer renew its lockspace lease. The lockspace lease will eventually
expire, at which point the host will lose ownership of it, and of all resource
leases it holds in the lockspace. Eventually, other hosts will be able to
acquire those leases. sanlock ensures that another host will not be able to
acquire one of the expired leases until the current host has quit using it.
It is important that the host "quit using" the leases it is holding if the
sanlock storage is lost and they begin expiring. If the host cannot quit using
the leases and release them within a limited time, then sanlock will use the
local watchdog to forcibly reset the host before any other host can acquire
them. This is severe, but preferable to possibly corrupting the data protected
by the lease. It ensures that two nodes will not be using the same lease at
once. For LV leases, that means that another host will not be able to activate
the LV while another host still has it active.
sanlock notifies the application that it cannot renew the lockspace lease. The
application needs to quit using all leases in the lockspace and release them as
quickly as possible. In the initial version, lvmlockd ignored this
notification, so sanlock would eventually reach the point where it would use
the local watchdog to reset the host. However, it's better to attempt a
response. If that response succeeds, the host can avoid being reset. If the
response fails, then sanlock will eventually reset the host as the last resort.
sanlock gives the application about 40 seconds to complete its response and
release its leases before resetting the host.
An application can specify the path and args of a program that sanlock should
run to notify it if the lockspace lease cannot be renewed. This program should
carry out the application's response to the expiring leases: attempt to quit
using the leases and then release them. lvmlockd gives this command to sanlock
for each VG when that VG is started: 'lvmlockctl --kill vg_name'
If sanlock loses access to lease storage in that VG, it runs lvmlockctl --kill,
which:
1. Uses syslog to explain what is happening.
2. Notifies lvmlockd that the VG is being killed, so lvmlockd can
immediatley return an error for this condition if any new lock
requests are made. (This step would not be strictly necessary.)
3. Attempts to quit using the VG. This is not yet implemented, but
will eventually use blkdeactivate on the VG (or a more forceful
equivalent.)
4. If step 3 was successful at terminating all use of the VG, then
lvmlockd is told to release all the leases for the VG. If this
is all done without about 40 seconds, the host can avoid being
reset.
Until steps 3 and 4 are fully implemented, manual steps can be substituted.
This is primarily for testing since the problem needs to be noticed and
responded to in a very short time. The manual alternative to step 3 is to kill
any processes using file systems on LV's in the VG, unmount all file systems on
the LVs, and deactivate all the LVs. Once this is done, the manual alternative
to step 4 is to run 'lvmlockctl --drop vg_name', which tells lvmlockd to
release all the leases for the VG.
-------------------------------------------------------------------------------
*/
* If access to the pv containing the vg's leases is lost, sanlock cannot renew
* the leases we have acquired for locked LVs. This means that we could soon
* loose the lease to another host which could activate our LV exclusively. We
* do not want to get to the point of two hosts having the same LV active
* exclusively (it obviously violates the purpose of LV locks.)
*
* The default method of preventing this problem is for lvmlockd to do nothing,
* which produces a safe but potentially inconvenient result. Doing nothing
* leads to our LV leases not being released, which leads to sanlock using the
* local watchdog to reset us before another host can acquire our lock. It
* would often be preferrable to avoid the abrupt hard reset from the watchdog.
*
* There are other options to avoid being reset by our watchdog. If we can
* quickly stop using the LVs in question and release the locks for them, then
* we could avoid a reset (there's a certain grace period of about 40 seconds
* in which we can attempt this.) To do this, we can tell sanlock to run a
* specific program when it has lost access to our leases. We could use this
* program to:
*
* 1. Deactivate all lvs in the effected vg. If all the leases are
* deactivated, then our LV locks would be released and sanlock would no longer
* use the watchdog to reset us. If file systems are mounted on the active
* lvs, then deactivating them would fail, so this option would be of limited
* usefulness.
*
* 2. Option 1 could be extended to kill pids using the fs on the lv, unmount
* the fs, and deactivate the lv. This is probably out of scope for lvm
* directly, and would likely need the help of another system service.
*
* 3. Use dmsetup suspend to block access to lvs in the effected vg. If this
* was successful, the local host could no longer write to the lvs, we could
* safely release the LV locks, and sanlock would no longer reset us. At this
* point, with suspended lvs, the host would be in a fairly hobbled state, and
* would almost certainly need a manual, forcible reset.
*
* 4. Option 3 could be extended to monitor the lost storage, and if it is
* reconnected, the leases could be reacquired, and the suspended lvs resumed
* (reacquiring leases will fail if another host has acquired them since they
* were released.) This complexity of this option, combined with the fact that
* the error conditions are often not as simple as storage being lost and then
* later connecting, will result in this option being too unreliable.
*
* Add a config option that we could use to select a different behavior than
* the default. Then implement one of the simpler options as a proof of
* concept, which could be extended if needed.
*/
/*
* Each lockspace thread has its own sanlock daemon connection.
@@ -1010,24 +961,12 @@ int lm_prepare_lockspace_sanlock(struct lockspace *ls)
char lock_lv_name[MAX_ARGS+1];
char lsname[SANLK_NAME_LEN + 1];
char disk_path[SANLK_PATH_LEN];
char killpath[SANLK_PATH_LEN];
char killargs[SANLK_PATH_LEN];
int gl_found;
int ret, rv;
memset(disk_path, 0, sizeof(disk_path));
memset(lock_lv_name, 0, sizeof(lock_lv_name));
/*
* Construct the path to lvmlockctl by using the path to the lvm binary
* and appending "lockctl" to get /path/to/lvmlockctl.
*/
memset(killpath, 0, sizeof(killpath));
snprintf(killpath, SANLK_PATH_LEN - 1, "%slockctl", LVM_PATH);
memset(killargs, 0, sizeof(killargs));
snprintf(killargs, SANLK_PATH_LEN - 1, "--kill %s", ls->vg_name);
rv = check_args_version(ls->vg_args, VG_LOCK_ARGS_MAJOR);
if (rv < 0) {
ret = -EARGS;
@@ -1112,15 +1051,6 @@ int lm_prepare_lockspace_sanlock(struct lockspace *ls)
goto fail;
}
log_debug("set killpath to %s %s", killpath, killargs);
rv = sanlock_killpath(lms->sock, 0, killpath, killargs);
if (rv < 0) {
log_error("S %s killpath error %d", lsname, rv);
ret = -EMANAGER;
goto fail;
}
rv = sanlock_restrict(lms->sock, SANLK_RESTRICT_SIGKILL);
if (rv < 0) {
log_error("S %s restrict error %d", lsname, rv);
@@ -1467,6 +1397,11 @@ int lm_lock_sanlock(struct lockspace *ls, struct resource *r, int ld_mode,
log_error("S %s R %s lock_san acquire error %d",
ls->name, r->name, rv);
if (added) {
lm_rem_resource_sanlock(ls, r);
return rv;
}
/* if the gl has been disabled, remove and free the gl resource */
if ((rv == SANLK_LEADER_RESOURCE) && (r->type == LD_RT_GL)) {
if (!lm_gl_is_enabled(ls)) {
@@ -1478,22 +1413,6 @@ int lm_lock_sanlock(struct lockspace *ls, struct resource *r, int ld_mode,
}
}
if (added)
lm_rem_resource_sanlock(ls, r);
/* sanlock gets i/o errors trying to read/write the leases. */
if (rv == -EIO)
rv = -ELOCKIO;
/*
* The sanlock lockspace can disappear if the lease storage fails,
* the delta lease renewals fail, the lockspace enters recovery,
* lvmlockd holds no leases in the lockspace, so sanlock can
* stop and free the lockspace.
*/
if (rv == -ENOSPC)
rv = -ELOCKIO;
return rv;
}
@@ -1675,11 +1594,9 @@ int lm_unlock_sanlock(struct lockspace *ls, struct resource *r,
}
rv = sanlock_release(lms->sock, -1, 0, 1, &rs);
if (rv < 0)
if (rv < 0) {
log_error("S %s R %s unlock_san release error %d", ls->name, r->name, rv);
if (rv == -EIO)
rv = -ELOCKIO;
}
return rv;
}

View File

@@ -566,8 +566,6 @@ static struct lvmpolld_lv *construct_pdlv(request req, struct lvmpolld_state *ls
return NULL;
}
pdlv->cmdargv = cmdargv;
cmdenvp = cmdenvp_ctr(pdlv);
if (!cmdenvp) {
pdlv_destroy(pdlv);
@@ -575,6 +573,7 @@ static struct lvmpolld_lv *construct_pdlv(request req, struct lvmpolld_state *ls
return NULL;
}
pdlv->cmdargv = cmdargv;
pdlv->cmdenvp = cmdenvp;
return pdlv;

View File

@@ -25,11 +25,6 @@
#include "lv_alloc.h"
#include "defaults.h"
static const char _cache_module[] = "cache";
/* TODO: using static field here, maybe should be a part of segment_type */
static unsigned _feature_mask;
#define SEG_LOG_ERROR(t, p...) \
log_error(t " segment %s of logical volume %s.", ## p, \
dm_config_parent_name(sn), seg->lv->name), 0;
@@ -71,15 +66,23 @@ static int _cache_pool_text_import(struct lv_segment *seg,
if (dm_config_has_node(sn, "cache_mode")) {
if (!(str = dm_config_find_str(sn, "cache_mode", NULL)))
return SEG_LOG_ERROR("cache_mode must be a string in");
if (!cache_set_mode(seg, str))
if (!set_cache_pool_feature(&seg->feature_flags, str))
return SEG_LOG_ERROR("Unknown cache_mode in");
}
} else
/* When missed in metadata, it's an old stuff - use writethrough */
seg->feature_flags |= DM_CACHE_FEATURE_WRITETHROUGH;
if (dm_config_has_node(sn, "policy")) {
if (!(str = dm_config_find_str(sn, "policy", NULL)))
return SEG_LOG_ERROR("policy must be a string in");
if (!(seg->policy_name = dm_pool_strdup(mem, str)))
return SEG_LOG_ERROR("Failed to duplicate policy in");
} else {
/* Cannot use 'just' default, so pick one */
seg->policy_name = DEFAULT_CACHE_POOL_POLICY; /* FIXME make configurable */
/* FIXME maybe here should be always 'mq' */
log_warn("WARNING: cache_policy undefined, using default \"%s\" policy.",
seg->policy_name);
}
/*
@@ -100,9 +103,6 @@ static int _cache_pool_text_import(struct lv_segment *seg,
* If the policy is not present, default policy is used.
*/
if ((sn = dm_config_find_node(sn, "policy_settings"))) {
if (!seg->policy_name)
return SEG_LOG_ERROR("policy_settings must have a policy_name in");
if (sn->v)
return SEG_LOG_ERROR("policy_settings must be a section in");
@@ -131,33 +131,28 @@ static int _cache_pool_text_export(const struct lv_segment *seg,
{
const char *cache_mode;
if (!(cache_mode = get_cache_pool_cachemode_name(seg)))
return_0;
if (!seg->policy_name) {
log_error(INTERNAL_ERROR "Policy name for %s is not defined.",
display_lvname(seg->lv));
return 0;
}
outf(f, "data = \"%s\"", seg_lv(seg, 0)->name);
outf(f, "metadata = \"%s\"", seg->metadata_lv->name);
outf(f, "chunk_size = %" PRIu32, seg->chunk_size);
outf(f, "cache_mode = \"%s\"", cache_mode);
outf(f, "policy = \"%s\"", seg->policy_name);
/*
* Cache pool used by a cache LV holds data. Not ideal,
* but not worth to break backward compatibility, by shifting
* content to cache segment
*/
if (cache_mode_is_set(seg)) {
if (!(cache_mode = get_cache_mode_name(seg)))
return_0;
outf(f, "cache_mode = \"%s\"", cache_mode);
}
if (seg->policy_name) {
outf(f, "policy = \"%s\"", seg->policy_name);
if (seg->policy_settings) {
if (strcmp(seg->policy_settings->key, "policy_settings")) {
log_error(INTERNAL_ERROR "Incorrect policy_settings tree, %s.",
seg->policy_settings->key);
return 0;
}
if (seg->policy_settings->child)
out_config_node(f, seg->policy_settings);
if (seg->policy_settings) {
if (strcmp(seg->policy_settings->key, "policy_settings")) {
log_error(INTERNAL_ERROR "Incorrect policy_settings tree, %s.",
seg->policy_settings->key);
return 0;
}
out_config_node(f, seg->policy_settings);
}
return 1;
@@ -170,29 +165,12 @@ static void _destroy(struct segment_type *segtype)
#ifdef DEVMAPPER_SUPPORT
static int _target_present(struct cmd_context *cmd,
const struct lv_segment *seg __attribute__((unused)),
unsigned *attributes __attribute__((unused)))
const struct lv_segment *seg __attribute__((unused)),
unsigned *attributes __attribute__((unused)))
{
/* List of features with their kernel target version */
static const struct feature {
uint32_t maj;
uint32_t min;
unsigned cache_feature;
const char feature[12];
const char module[12]; /* check dm-%s */
} _features[] = {
{ 1, 3, CACHE_FEATURE_POLICY_MQ, "policy_mq", "cache-mq" },
{ 1, 8, CACHE_FEATURE_POLICY_SMQ, "policy_smq", "cache-smq" },
};
static const char _lvmconf[] = "global/cache_disabled_features";
static unsigned _attrs = 0;
uint32_t maj, min, patchlevel;
static int _cache_checked = 0;
static int _cache_present = 0;
uint32_t maj, min, patchlevel;
unsigned i;
const struct dm_config_node *cn;
const struct dm_config_value *cv;
const char *str;
if (!_cache_checked) {
_cache_present = target_present(cmd, "cache", 1);
@@ -206,53 +184,11 @@ static int _target_present(struct cmd_context *cmd,
if ((maj < 1) ||
((maj == 1) && (min < 3))) {
_cache_present = 0;
log_error("The cache kernel module is version %u.%u.%u. "
"Version 1.3.0+ is required.",
log_error("The cache kernel module is version %u.%u.%u."
" Version 1.3.0+ is required.",
maj, min, patchlevel);
return 0;
}
for (i = 0; i < DM_ARRAY_SIZE(_features); ++i) {
if (((maj > _features[i].maj) ||
(maj == _features[i].maj && min >= _features[i].min)) &&
(!_features[i].module[0] || module_present(cmd, _features[i].module)))
_attrs |= _features[i].cache_feature;
else
log_very_verbose("Target %s does not support %s.",
_cache_module, _features[i].feature);
}
}
if (attributes) {
if (!_feature_mask) {
/* Support runtime lvm.conf changes, N.B. avoid 32 feature */
if ((cn = find_config_tree_array(cmd, global_cache_disabled_features_CFG, NULL))) {
for (cv = cn->v; cv; cv = cv->next) {
if (cv->type != DM_CFG_STRING) {
log_error("Ignoring invalid string in config file %s.",
_lvmconf);
continue;
}
str = cv->v.str;
if (!*str)
continue;
for (i = 0; i < DM_ARRAY_SIZE(_features); ++i)
if (strcasecmp(str, _features[i].feature) == 0)
_feature_mask |= _features[i].cache_feature;
}
}
_feature_mask = ~_feature_mask;
for (i = 0; i < DM_ARRAY_SIZE(_features); ++i)
if ((_attrs & _features[i].cache_feature) &&
!(_feature_mask & _features[i].cache_feature))
log_very_verbose("Target %s %s support disabled by %s",
_cache_module, _features[i].feature, _lvmconf);
}
*attributes = _attrs & _feature_mask;
}
return _cache_present;
@@ -378,9 +314,7 @@ static int _cache_add_target_line(struct dev_manager *dm,
metadata_uuid,
data_uuid,
origin_uuid,
seg->cleaner_policy ? "cleaner" :
/* undefined policy name -> likely an old "mq" */
cache_pool_seg->policy_name ? : "mq",
seg->cleaner_policy ? "cleaner" : cache_pool_seg->policy_name,
seg->cleaner_policy ? NULL : cache_pool_seg->policy_settings,
cache_pool_seg->chunk_size))
return_0;
@@ -442,8 +376,5 @@ int init_cache_segtypes(struct cmd_context *cmd,
return_0;
log_very_verbose("Initialised segtype: %s", segtype->name);
/* Reset mask for recalc */
_feature_mask = 0;
return 1;
}

View File

@@ -2179,6 +2179,7 @@ void destroy_toolcontext(struct cmd_context *cmd)
lvmetad_release_token();
lvmetad_disconnect();
lvmpolld_disconnect();
cmd->initialized.connections = 0;
release_log_memory();
activation_exit();

View File

@@ -133,7 +133,6 @@ struct cmd_context {
unsigned lockd_gl_disable:1;
unsigned lockd_vg_disable:1;
unsigned lockd_lv_disable:1;
unsigned lockd_gl_removed:1;
unsigned lockd_vg_default_sh:1;
unsigned lockd_vg_enforce_sh:1;

View File

@@ -23,7 +23,6 @@
#include "toolcontext.h"
#include "lvm-file.h"
#include "memlock.h"
#include "segtype.h"
#include <sys/stat.h>
#include <sys/mman.h>
@@ -2416,27 +2415,3 @@ int get_default_allocation_cache_pool_chunk_size_CFG(struct cmd_context *cmd, st
{
return DEFAULT_CACHE_POOL_CHUNK_SIZE * 2;
}
const char *get_default_allocation_cache_policy_CFG(struct cmd_context *cmd, struct profile *profile)
{
const struct segment_type *segtype = get_segtype_from_string(cmd, "cache");
unsigned attr = ~0;
if (!segtype ||
!segtype->ops->target_present ||
!segtype->ops->target_present(cmd, NULL, &attr)) {
log_warn("WARNING: Cannot detect default cache policy, using \""
DEFAULT_CACHE_POLICY "\".");
return DEFAULT_CACHE_POLICY;
}
if (attr & CACHE_FEATURE_POLICY_SMQ)
return "smq";
if (attr & CACHE_FEATURE_POLICY_MQ)
return "mq";
log_warn("WARNING: Default cache policy not available.");
return NULL;
}

View File

@@ -50,7 +50,7 @@ struct profile_params {
struct dm_list profiles; /* list of profiles which are loaded already and which are ready for use */
};
#define CFG_PATH_MAX_LEN 128
#define CFG_PATH_MAX_LEN 64
/*
* Structures used for definition of a configuration tree.
@@ -296,7 +296,5 @@ int get_default_allocation_thin_pool_chunk_size_CFG(struct cmd_context *cmd, str
#define get_default_unconfigured_allocation_thin_pool_chunk_size_CFG NULL
int get_default_allocation_cache_pool_chunk_size_CFG(struct cmd_context *cmd, struct profile *profile);
#define get_default_unconfigured_allocation_cache_pool_chunk_size_CFG NULL
const char *get_default_allocation_cache_policy_CFG(struct cmd_context *cmd, struct profile *profile);
#define get_default_unconfigured_allocation_cache_policy_CFG NULL
#endif

View File

@@ -122,7 +122,7 @@ cfg_section(devices_CFG_SECTION, "devices", root_CFG_SECTION, 0, vsn(1, 0, 0), 0
"How LVM uses block devices.\n")
cfg_section(allocation_CFG_SECTION, "allocation", root_CFG_SECTION, CFG_PROFILABLE, vsn(2, 2, 77), 0, NULL,
"How LVM selects space and applies properties to LVs.\n")
"How LVM selects free space for Logical Volumes.\n")
cfg_section(log_CFG_SECTION, "log", root_CFG_SECTION, 0, vsn(1, 0, 0), 0, NULL,
"How LVM log information is reported.\n")
@@ -313,7 +313,7 @@ cfg(devices_md_chunk_alignment_CFG, "md_chunk_alignment", devices_CFG_SECTION, 0
cfg(devices_default_data_alignment_CFG, "default_data_alignment", devices_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_INT, DEFAULT_DATA_ALIGNMENT, vsn(2, 2, 75), NULL, 0, NULL,
"Default alignment of the start of a PV data area in MB.\n"
"If set to 0, a value of 64KiB will be used.\n"
"If set to 0, a value of 64KB will be used.\n"
"Set to 1 for 1MiB, 2 for 2MiB, etc.\n")
cfg(devices_data_alignment_detection_CFG, "data_alignment_detection", devices_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_DATA_ALIGNMENT_DETECTION, vsn(2, 2, 51), NULL, 0, NULL,
@@ -329,7 +329,7 @@ cfg(devices_data_alignment_detection_CFG, "data_alignment_detection", devices_CF
"This setting takes precedence over md_chunk_alignment.\n")
cfg(devices_data_alignment_CFG, "data_alignment", devices_CFG_SECTION, 0, CFG_TYPE_INT, 0, vsn(2, 2, 45), NULL, 0, NULL,
"Alignment of the start of a PV data area in KiB.\n"
"Alignment of the start of a PV data area in KB.\n"
"If a PV is placed directly on an md device and\n"
"md_chunk_alignment or data_alignment_detection are enabled,\n"
"then this setting is ignored. Otherwise, md_chunk_alignment\n"
@@ -340,10 +340,10 @@ cfg(devices_data_alignment_offset_detection_CFG, "data_alignment_offset_detectio
"Detect PV data alignment offset based on sysfs device information.\n"
"The start of a PV aligned data area will be shifted by the\n"
"alignment_offset exposed in sysfs. This offset is often 0, but\n"
"may be non-zero. Certain 4KiB sector drives that compensate for\n"
"may be non-zero. Certain 4KB sector drives that compensate for\n"
"windows partitioning will have an alignment_offset of 3584 bytes\n"
"(sector 7 is the lowest aligned logical block, the 4KiB sectors start\n"
"at LBA -1, and consequently sector 63 is aligned on a 4KiB boundary).\n"
"(sector 7 is the lowest aligned logical block, the 4KB sectors start\n"
"at LBA -1, and consequently sector 63 is aligned on a 4KB boundary).\n"
"pvcreate --dataalignmentoffset will skip this detection.\n")
cfg(devices_ignore_suspended_devices_CFG, "ignore_suspended_devices", devices_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_IGNORE_SUSPENDED_DEVICES, vsn(1, 2, 19), NULL, 0, NULL,
@@ -383,9 +383,9 @@ cfg(devices_require_restorefile_with_uuid_CFG, "require_restorefile_with_uuid",
"Allow use of pvcreate --uuid without requiring --restorefile.\n")
cfg(devices_pv_min_size_CFG, "pv_min_size", devices_CFG_SECTION, 0, CFG_TYPE_INT, DEFAULT_PV_MIN_SIZE_KB, vsn(2, 2, 85), NULL, 0, NULL,
"Minimum size in KiB of block devices which can be used as PVs.\n"
"Minimum size (in KB) of block devices which can be used as PVs.\n"
"In a clustered environment all nodes must use the same value.\n"
"Any value smaller than 512KiB is ignored. The previous built-in\n"
"Any value smaller than 512KB is ignored. The previous built-in\n"
"value was 512.\n")
cfg(devices_issue_discards_CFG, "issue_discards", devices_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_ISSUE_DISCARDS, vsn(2, 2, 85), NULL, 0, NULL,
@@ -439,7 +439,7 @@ cfg(allocation_wipe_signatures_when_zeroing_new_lvs_CFG, "wipe_signatures_when_z
"Look for and erase any signatures while zeroing a new LV.\n"
"Zeroing is controlled by the -Z/--zero option, and if not\n"
"specified, zeroing is used by default if possible.\n"
"Zeroing simply overwrites the first 4KiB of a new LV\n"
"Zeroing simply overwrites the first 4 KiB of a new LV\n"
"with zeroes and does no signature detection or wiping.\n"
"Signature wiping goes beyond zeroing and detects exact\n"
"types and positions of signatures within the whole LV.\n"
@@ -462,34 +462,16 @@ cfg(allocation_mirror_logs_require_separate_pvs_CFG, "mirror_logs_require_separa
cfg(allocation_cache_pool_metadata_require_separate_pvs_CFG, "cache_pool_metadata_require_separate_pvs", allocation_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_CACHE_POOL_METADATA_REQUIRE_SEPARATE_PVS, vsn(2, 2, 106), NULL, 0, NULL,
"Cache pool metadata and data will always use different PVs.\n")
cfg(allocation_cache_pool_cachemode_CFG, "cache_pool_cachemode", allocation_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_STRING, DEFAULT_CACHE_MODE, vsn(2, 2, 113), NULL, vsn(2, 2, 128),
"This has been replaced by the allocation/cache_mode setting.\n",
"Cache mode.\n")
cfg(allocation_cache_mode_CFG, "cache_mode", allocation_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_STRING, DEFAULT_CACHE_MODE, vsn(2, 2, 128), NULL, 0, NULL,
"The default cache mode used for new cache.\n"
cfg(allocation_cache_pool_cachemode_CFG, "cache_pool_cachemode", allocation_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_STRING, DEFAULT_CACHE_POOL_CACHEMODE, vsn(2, 2, 113), NULL, 0, NULL,
"The default cache mode used for new cache pools.\n"
"Possible options are: writethrough, writeback.\n"
"writethrough - Data blocks are immediately written from\n"
"the cache to disk.\n"
"writeback - Data blocks are written from the cache back\n"
"to disk after some delay to improve performance.\n")
cfg_runtime(allocation_cache_policy_CFG, "cache_policy", allocation_CFG_SECTION, CFG_PROFILABLE | CFG_DEFAULT_COMMENTED, CFG_TYPE_STRING, vsn(2, 2, 127), 0, NULL,
"The default cache policy used for new cache volume.\n"
"Generally available policies are: mq, smq.\n"
"mq - Multiqueue policy with 88 bytes per block\n"
"smq - Stochastic multique with 25 bytes per block (kernel >= 4.2).\n")
cfg_section(allocation_cache_settings_CFG_SECTION, "cache_settings", allocation_CFG_SECTION, CFG_PROFILABLE | CFG_DEFAULT_COMMENTED, vsn(2, 2, 127), 0, NULL,
"Individual settings for policies.\n"
"See the help for individual policies for more info.\n")
cfg_section(policy_settings_CFG_SUBSECTION, "policy_settings", allocation_cache_settings_CFG_SECTION, CFG_NAME_VARIABLE | CFG_SECTION_NO_CHECK | CFG_PROFILABLE | CFG_DEFAULT_COMMENTED, vsn(2, 2, 127), 0, NULL,
"Replace this subsection name with a policy name.\n"
"Multiple subsections for different policies can be created.\n")
cfg_runtime(allocation_cache_pool_chunk_size_CFG, "cache_pool_chunk_size", allocation_CFG_SECTION, CFG_PROFILABLE | CFG_DEFAULT_UNDEFINED, CFG_TYPE_INT, vsn(2, 2, 106), 0, NULL,
"The minimal chunk size in KiB for cache pool volumes.\n"
cfg_runtime(allocation_cache_pool_chunk_size_CFG, "cache_pool_chunk_size", allocation_CFG_SECTION, CFG_DEFAULT_UNDEFINED, CFG_TYPE_INT, vsn(2, 2, 106), 0, NULL,
"The minimal chunk size (in kiB) for cache pool volumes.\n"
"Using a chunk_size that is too large can result in wasteful\n"
"use of the cache, where small reads and writes can cause\n"
"large sections of an LV to be mapped into the cache. However,\n"
@@ -497,8 +479,8 @@ cfg_runtime(allocation_cache_pool_chunk_size_CFG, "cache_pool_chunk_size", alloc
"overhead trying to manage the numerous chunks that become mapped\n"
"into the cache. The former is more of a problem than the latter\n"
"in most cases, so we default to a value that is on the smaller\n"
"end of the spectrum. Supported values range from 32KiB to\n"
"1GiB in multiples of 32.\n")
"end of the spectrum. Supported values range from 32(kiB) to\n"
"1048576 in multiples of 32.\n")
cfg(allocation_thin_pool_metadata_require_separate_pvs_CFG, "thin_pool_metadata_require_separate_pvs", allocation_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_THIN_POOL_METADATA_REQUIRE_SEPARATE_PVS, vsn(2, 2, 89), NULL, 0, NULL,
"Thin pool metdata and data will always use different PVs.\n")
@@ -524,16 +506,16 @@ cfg(allocation_thin_pool_chunk_size_policy_CFG, "thin_pool_chunk_size_policy", a
"The chunk size is always at least 512KiB.\n")
cfg_runtime(allocation_thin_pool_chunk_size_CFG, "thin_pool_chunk_size", allocation_CFG_SECTION, CFG_PROFILABLE | CFG_PROFILABLE_METADATA | CFG_DEFAULT_UNDEFINED, CFG_TYPE_INT, vsn(2, 2, 99), 0, NULL,
"The minimal chunk size in KiB for thin pool volumes.\n"
"The minimal chunk size (in KB) for thin pool volumes.\n"
"Larger chunk sizes may improve performance for plain\n"
"thin volumes, however using them for snapshot volumes\n"
"is less efficient, as it consumes more space and takes\n"
"extra time for copying. When unset, lvm tries to estimate\n"
"chunk size starting from 64KiB. Supported values are in\n"
"the range 64KiB to 1GiB.\n")
"chunk size starting from 64KB. Supported values are in\n"
"the range 64 to 1048576.\n")
cfg(allocation_physical_extent_size_CFG, "physical_extent_size", allocation_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_INT, DEFAULT_EXTENT_SIZE, vsn(2, 2, 112), NULL, 0, NULL,
"Default physical extent size in KiB to use for new VGs.\n")
"Default physical extent size to use for new VGs (in KB).\n")
cfg(log_verbose_CFG, "verbose", log_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_VERBOSE, vsn(1, 0, 0), NULL, 0, NULL,
"Controls the messages sent to stdout or stderr.\n")
@@ -810,11 +792,11 @@ cfg(global_sparse_segtype_default_CFG, "sparse_segtype_default", global_CFG_SECT
"The '--type snapshot|thin' option overrides this setting.\n")
cfg(global_lvdisplay_shows_full_device_path_CFG, "lvdisplay_shows_full_device_path", global_CFG_SECTION, CFG_PROFILABLE | CFG_DEFAULT_COMMENTED, CFG_TYPE_BOOL, DEFAULT_LVDISPLAY_SHOWS_FULL_DEVICE_PATH, vsn(2, 2, 89), NULL, 0, NULL,
"Enable this to reinstate the previous lvdisplay name format.\n"
"The default format for displaying LV names in lvdisplay was changed\n"
"in version 2.02.89 to show the LV name and path separately.\n"
"Previously this was always shown as /dev/vgname/lvname even when that\n"
"was never a valid path in the /dev filesystem.\n")
"was never a valid path in the /dev filesystem.\n"
"Enable this option to reinstate the previous format.\n")
cfg(global_use_lvmetad_CFG, "use_lvmetad", global_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_USE_LVMETAD, vsn(2, 2, 93), "@DEFAULT_USE_LVMETAD@", 0, NULL,
"Use lvmetad to cache metadata and reduce disk scanning.\n"
@@ -909,14 +891,6 @@ cfg_array(global_thin_disabled_features_CFG, "thin_disabled_features", global_CF
"Example:\n"
"thin_disabled_features = [ \"discards\", \"block_size\" ]\n")
cfg_array(global_cache_disabled_features_CFG, "cache_disabled_features", global_CFG_SECTION, CFG_ALLOW_EMPTY | CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, NULL, vsn(2, 2, 126), NULL, 0, NULL,
"Features to not use in the cache driver.\n"
"This can be helpful for testing, or to avoid\n"
"using a feature that is causing problems.\n"
"Features: policy_mq, policy_smq.\n"
"Example:\n"
"cache_disabled_features = [ \"policy_smq\" ]\n")
cfg(global_cache_check_executable_CFG, "cache_check_executable", global_CFG_SECTION, CFG_ALLOW_EMPTY | CFG_DEFAULT_COMMENTED, CFG_TYPE_STRING, CACHE_CHECK_CMD, vsn(2, 2, 108), "@CACHE_CHECK_CMD@", 0, NULL,
"The full path to the cache_check command.\n"
"LVM uses this command to check that a cache metadata\n"
@@ -1039,11 +1013,11 @@ cfg(activation_use_linear_target_CFG, "use_linear_target", activation_CFG_SECTIO
"that only handles a single stripe.\n")
cfg(activation_reserved_stack_CFG, "reserved_stack", activation_CFG_SECTION, 0, CFG_TYPE_INT, DEFAULT_RESERVED_STACK, vsn(1, 0, 0), NULL, 0, NULL,
"Stack size in KiB to reserve for use while devices are suspended.\n"
"Stack size in KB to reserve for use while devices are suspended.\n"
"Insufficent reserve risks I/O deadlock during device suspension.\n")
cfg(activation_reserved_memory_CFG, "reserved_memory", activation_CFG_SECTION, 0, CFG_TYPE_INT, DEFAULT_RESERVED_MEMORY, vsn(1, 0, 0), NULL, 0, NULL,
"Memory size in KiB to reserve for use while devices are suspended.\n"
"Memory size in KB to reserve for use while devices are suspended.\n"
"Insufficent reserve risks I/O deadlock during device suspension.\n")
cfg(activation_process_priority_CFG, "process_priority", activation_CFG_SECTION, 0, CFG_TYPE_INT, DEFAULT_PROCESS_PRIORITY, vsn(1, 0, 0), NULL, 0, NULL,
@@ -1110,7 +1084,7 @@ cfg_array(activation_read_only_volume_list_CFG, "read_only_volume_list", activat
cfg(activation_mirror_region_size_CFG, "mirror_region_size", activation_CFG_SECTION, 0, CFG_TYPE_INT, DEFAULT_RAID_REGION_SIZE, vsn(1, 0, 0), NULL, vsn(2, 2, 99),
"This has been replaced by the activation/raid_region_size setting.\n",
"Size in KiB of each copy operation when mirroring.\n")
"Size (in KB) of each copy operation when mirroring.\n")
cfg(activation_raid_region_size_CFG, "raid_region_size", activation_CFG_SECTION, 0, CFG_TYPE_INT, DEFAULT_RAID_REGION_SIZE, vsn(2, 2, 99), NULL, 0, NULL,
"Size in KiB of each raid or mirror synchronization region.\n"
@@ -1263,7 +1237,7 @@ cfg(activation_monitoring_CFG, "monitoring", activation_CFG_SECTION, 0, CFG_TYPE
"The '--ignoremonitoring' option overrides this setting.\n")
cfg(activation_polling_interval_CFG, "polling_interval", activation_CFG_SECTION, 0, CFG_TYPE_INT, DEFAULT_INTERVAL, vsn(2, 2, 63), NULL, 0, NULL,
"Check pvmove or lvconvert progress at this interval (seconds).\n"
"Check pvmove or lvconvert progress at this interval (seconds)\n"
"When pvmove or lvconvert must wait for the kernel to finish\n"
"synchronising or merging data, they check and report progress\n"
"at intervals of this number of seconds.\n"

View File

@@ -117,8 +117,8 @@
#define DEFAULT_CACHE_POOL_CHUNK_SIZE 64 /* KB */
#define DEFAULT_CACHE_POOL_MIN_METADATA_SIZE 2048 /* KB */
#define DEFAULT_CACHE_POOL_MAX_METADATA_SIZE (16 * 1024 * 1024) /* KB */
#define DEFAULT_CACHE_POLICY "mq"
#define DEFAULT_CACHE_MODE "writethrough"
#define DEFAULT_CACHE_POOL_CACHEMODE "writethrough"
#define DEFAULT_CACHE_POOL_POLICY "mq"
#define DEFAULT_UMASK 0077

View File

@@ -164,7 +164,7 @@ int export_pv(struct cmd_context *cmd, struct dm_pool *mem __attribute__((unused
/* Is VG already exported or being exported? */
if (vg && vg_is_exported(vg)) {
/* Does system_id need setting? */
if (!vg->lvm1_system_id || !*vg->lvm1_system_id ||
if ((vg->lvm1_system_id && !*vg->lvm1_system_id) ||
strncmp(vg->lvm1_system_id, EXPORTED_TAG,
sizeof(EXPORTED_TAG) - 1)) {
if (!generate_lvm1_system_id(cmd, (char *)pvd->system_id, EXPORTED_TAG))

View File

@@ -739,19 +739,6 @@ static int _free_vg_sanlock(struct cmd_context *cmd, struct volume_group *vg)
if (!_lvmlockd_connected)
return 0;
/*
* vgremove originally held the global lock, but lost it because the
* vgremove command is removing multiple VGs, and removed the VG
* holding the global lock before attempting to remove this VG.
* To avoid this situation, the user should remove the VG holding
* the global lock in a command by itself, or as the last arg in a
* vgremove command that removes multiple VGs.
*/
if (cmd->lockd_gl_removed) {
log_error("Global lock failed: global lock was lost by removing a previous VG.");
return 0;
}
if (!vg->lock_args || !strlen(vg->lock_args)) {
/* Shouldn't happen in general, but maybe in some error cases? */
log_debug("_free_vg_sanlock %s no lock_args", vg->name);
@@ -786,21 +773,8 @@ static int _free_vg_sanlock(struct cmd_context *cmd, struct volume_group *vg)
goto out;
}
/*
* If the global lock was been removed by removing this VG, then:
*
* Print a warning indicating that the global lock should be enabled
* in another remaining sanlock VG.
*
* Do not allow any more VGs to be removed by this command, e.g.
* if a command removes two sanlock VGs, like vgremove foo bar,
* and the global lock existed in foo, do not continue to remove
* VG bar without the global lock. See the corresponding check above.
*/
if (lockd_flags & LD_RF_WARN_GL_REMOVED) {
if (lockd_flags & LD_RF_WARN_GL_REMOVED)
log_warn("VG %s held the sanlock global lock, enable global lock in another VG.", vg->name);
cmd->lockd_gl_removed = 1;
}
/*
* The usleep delay gives sanlock time to close the lock lv,
@@ -1383,7 +1357,6 @@ int lockd_gl(struct cmd_context *cmd, const char *def_mode, uint32_t flags)
const char *mode = NULL;
const char *opts = NULL;
uint32_t lockd_flags;
int force_cache_update = 0;
int retries = 0;
int result;
@@ -1428,8 +1401,8 @@ int lockd_gl(struct cmd_context *cmd, const char *def_mode, uint32_t flags)
/* We can continue reading if a shared lock fails. */
if (!strcmp(mode, "sh")) {
log_warn("Reading without shared global lock.");
force_cache_update = 1;
goto allow;
lvmetad_validate_global_cache(cmd, 1);
return 1;
}
log_error("Global lock failed: check that lvmlockd is running.");
@@ -1452,19 +1425,9 @@ int lockd_gl(struct cmd_context *cmd, const char *def_mode, uint32_t flags)
*
* ESTARTING: the lockspace with the gl is starting.
* The VG with the global lock is starting and should finish shortly.
*
* ELOCKIO: sanlock gets i/o errors when trying to read/write leases
* (This can progress to EVGKILLED.)
*
* EVGKILLED: the sanlock lockspace is being killed after losing
* access to lease storage.
*/
if (result == -ENOLS ||
result == -ESTARTING ||
result == -EVGKILLED ||
result == -ELOCKIO) {
if (result == -ENOLS || result == -ESTARTING) {
if (!strcmp(mode, "un"))
return 1;
@@ -1473,13 +1436,9 @@ int lockd_gl(struct cmd_context *cmd, const char *def_mode, uint32_t flags)
*/
if (strcmp(mode, "sh")) {
if (result == -ESTARTING)
log_error("Global lock failed: lockspace is starting");
log_error("Global lock failed: lockspace is starting.");
else if (result == -ENOLS)
log_error("Global lock failed: check that global lockspace is started");
else if (result == -ELOCKIO)
log_error("Global lock failed: storage errors for sanlock leases");
else if (result == -EVGKILLED)
log_error("Global lock failed: storage failed for sanlock leases");
log_error("Global lock failed: check that global lockspace is started.");
else
log_error("Global lock failed: error %d", result);
return 0;
@@ -1493,21 +1452,14 @@ int lockd_gl(struct cmd_context *cmd, const char *def_mode, uint32_t flags)
if (result == -ESTARTING) {
log_warn("Skipping global lock: lockspace is starting");
force_cache_update = 1;
goto allow;
}
if (result == -ELOCKIO || result == -EVGKILLED) {
log_warn("Skipping global lock: storage %s for sanlock leases",
result == -ELOCKIO ? "errors" : "failed");
force_cache_update = 1;
goto allow;
lvmetad_validate_global_cache(cmd, 1);
return 1;
}
if ((lockd_flags & LD_RF_NO_GL_LS) || (lockd_flags & LD_RF_NO_LOCKSPACES)) {
log_warn("Skipping global lock: lockspace not found or started");
force_cache_update = 1;
goto allow;
lvmetad_validate_global_cache(cmd, 1);
return 1;
}
/*
@@ -1523,7 +1475,11 @@ int lockd_gl(struct cmd_context *cmd, const char *def_mode, uint32_t flags)
log_warn("Duplicate sanlock global locks should be corrected");
if (result < 0) {
if (result == -EAGAIN) {
if (ignorelockingfailure()) {
log_debug("Ignore failed locking for global lock");
lvmetad_validate_global_cache(cmd, 1);
return 1;
} else if (result == -EAGAIN) {
/*
* Most of the time, retries should avoid this case.
*/
@@ -1540,8 +1496,9 @@ int lockd_gl(struct cmd_context *cmd, const char *def_mode, uint32_t flags)
}
}
allow:
lvmetad_validate_global_cache(cmd, force_cache_update);
if (!(flags & LDGL_SKIP_CACHE_VALIDATE))
lvmetad_validate_global_cache(cmd, 0);
return 1;
}
@@ -1557,7 +1514,7 @@ int lockd_gl(struct cmd_context *cmd, const char *def_mode, uint32_t flags)
*
* The result of the VG lock operation needs to be saved in lockd_state
* because the result needs to be passed into vg_read so it can be
* assessed in combination with vg->lock_type.
* assessed in combination with vg->lock_state.
*
* The VG lock protects the VG metadata on disk from concurrent access
* among hosts. The VG lock also ensures that the local lvmetad cache
@@ -1733,28 +1690,6 @@ int lockd_vg(struct cmd_context *cmd, const char *vg_name, const char *def_mode,
}
}
/*
* sanlock is getting i/o errors while reading/writing leases, or the
* lockspace/VG is being killed after failing to renew its lease for
* too long.
*/
if (result == -EVGKILLED || result == -ELOCKIO) {
const char *problem = (result == -ELOCKIO ? "errors" : "failed");
if (!strcmp(mode, "un")) {
ret = 1;
goto out;
} else if (!strcmp(mode, "sh")) {
log_warn("VG %s lock skipped: storage %s for sanlock leases", vg_name, problem);
ret = 1;
goto out;
} else {
log_error("VG %s lock failed: storage %s for sanlock leases", vg_name, problem);
ret = 0;
goto out;
}
}
/*
* An unused/previous lockspace for the VG was found.
* This means it must be a lockd VG, not local. The
@@ -1835,6 +1770,11 @@ out:
if ((lockd_flags & LD_RF_DUP_GL_LS) && strcmp(mode, "un"))
log_warn("Duplicate sanlock global lock in VG %s", vg_name);
if (!ret && ignorelockingfailure()) {
log_debug("Ignore failed locking for VG %s", vg_name);
return 1;
}
return ret;
}
@@ -1972,12 +1912,6 @@ int lockd_lv_name(struct cmd_context *cmd, struct volume_group *vg,
return 0;
}
if (result == -EVGKILLED || result == -ELOCKIO) {
const char *problem = (result == -ELOCKIO ? "errors" : "failed");
log_error("LV %s/%s lock failed: storage %s for sanlock leases", vg->name, lv_name, problem);
return 0;
}
if (result < 0) {
log_error("LV %s/%s lock failed: error %d", vg->name, lv_name, result);
return 0;

View File

@@ -17,7 +17,8 @@
#define LOCKD_SANLOCK_LV_NAME "lvmlock"
/* lockd_gl flags */
#define LDGL_UPDATE_NAMES 0x00000001
#define LDGL_SKIP_CACHE_VALIDATE 0x00000001
#define LDGL_UPDATE_NAMES 0x00000002
/* lockd_lv flags */
#define LDLV_MODE_NO_SH 0x00000001

View File

@@ -69,7 +69,7 @@ void init_log_file(const char *log_file, int append)
static const char statfile[] = "/proc/self/stat";
const char *env;
int pid;
unsigned long long starttime;
long long starttime;
FILE *st;
int i = 0;

View File

@@ -29,17 +29,7 @@
#define DM_HINT_OVERHEAD_PER_BLOCK 8 /* bytes */
#define DM_MAX_HINT_WIDTH (4+16) /* bytes. FIXME Configurable? */
int cache_mode_is_set(const struct lv_segment *seg)
{
if (seg_is_cache(seg))
seg = first_seg(seg->pool_lv);
return (seg->feature_flags & (DM_CACHE_FEATURE_WRITEBACK |
DM_CACHE_FEATURE_WRITETHROUGH |
DM_CACHE_FEATURE_PASSTHROUGH)) ? 1 : 0;
}
const char *get_cache_mode_name(const struct lv_segment *seg)
const char *get_cache_pool_cachemode_name(const struct lv_segment *seg)
{
if (seg->feature_flags & DM_CACHE_FEATURE_WRITEBACK)
return "writeback";
@@ -56,48 +46,19 @@ const char *get_cache_mode_name(const struct lv_segment *seg)
return NULL;
}
int cache_set_mode(struct lv_segment *seg, const char *str)
int set_cache_pool_feature(uint64_t *feature_flags, const char *str)
{
struct cmd_context *cmd = seg->lv->vg->cmd;
int id;
uint64_t mode;
if (!str && !seg_is_cache(seg))
return 1; /* Defaults only for cache */
if (seg_is_cache(seg))
seg = first_seg(seg->pool_lv);
if (!str) {
if (cache_mode_is_set(seg))
return 1; /* Default already set in cache pool */
id = allocation_cache_mode_CFG;
/* If present, check backward compatible settings */
if (!find_config_node(cmd, cmd->cft, id) &&
find_config_node(cmd, cmd->cft, allocation_cache_pool_cachemode_CFG))
id = allocation_cache_pool_cachemode_CFG;
str = find_config_tree_str(cmd, id, NULL);
}
if (!strcmp(str, "writeback"))
mode = DM_CACHE_FEATURE_WRITEBACK;
*feature_flags |= DM_CACHE_FEATURE_WRITEBACK;
else if (!strcmp(str, "writethrough"))
mode = DM_CACHE_FEATURE_WRITETHROUGH;
else if (!strcmp(str, "passthrough"))
mode = DM_CACHE_FEATURE_PASSTHROUGH;
*feature_flags |= DM_CACHE_FEATURE_WRITETHROUGH;
else if (!strcmp(str, "passhrough"))
*feature_flags |= DM_CACHE_FEATURE_PASSTHROUGH;
else {
log_error("Cannot set unknown cache mode \"%s\".", str);
log_error("Cache pool feature \"%s\" is unknown.", str);
return 0;
}
seg->feature_flags &= ~(DM_CACHE_FEATURE_WRITEBACK |
DM_CACHE_FEATURE_WRITETHROUGH |
DM_CACHE_FEATURE_PASSTHROUGH);
seg->feature_flags |= mode;
return 1;
}
@@ -434,72 +395,36 @@ int lv_is_cache_origin(const struct logical_volume *lv)
return seg && lv_is_cache(seg->lv) && !lv_is_pending_delete(seg->lv) && (seg_lv(seg, 0) == lv);
}
int cache_set_policy(struct lv_segment *seg, const char *name,
const struct dm_config_tree *settings)
int lv_cache_set_policy(struct logical_volume *lv, const char *name,
const struct dm_config_tree *settings)
{
struct dm_config_node *cn;
const struct dm_config_node *cns;
struct dm_config_tree *old = NULL, *new = NULL, *tmp = NULL;
int r = 0;
const int passed_seg_is_cache = seg_is_cache(seg);
struct lv_segment *seg = first_seg(lv);
if (passed_seg_is_cache)
if (lv_is_cache(lv))
seg = first_seg(seg->pool_lv);
if (name) {
if (!(seg->policy_name = dm_pool_strdup(seg->lv->vg->vgmem, name))) {
log_error("Failed to duplicate policy name.");
return 0;
}
} else if (!seg->policy_name && passed_seg_is_cache)
seg->policy_name = find_config_tree_str(seg->lv->vg->cmd, allocation_cache_policy_CFG, NULL);
if (settings) {
if (!seg->policy_name) {
log_error(INTERNAL_ERROR "Can't set policy settings without policy name.");
return 0;
}
if (seg->policy_settings) {
if (!(old = dm_config_create()))
goto_out;
if (!(new = dm_config_create()))
goto_out;
new->root = settings->root;
old->root = seg->policy_settings;
new->cascade = old;
if (!(tmp = dm_config_flatten(new)))
goto_out;
}
if ((cn = dm_config_find_node((tmp) ? tmp->root : settings->root, "policy_settings")) &&
!(seg->policy_settings = dm_config_clone_node_with_mem(seg->lv->vg->vgmem, cn, 0)))
if (seg->policy_settings) {
if (!(old = dm_config_create()))
goto_out;
} else if (passed_seg_is_cache && /* Look for command's profile cache_policies */
(cns = find_config_tree_node(seg->lv->vg->cmd, allocation_cache_settings_CFG_SECTION, NULL))) {
/* Try to find our section for given policy */
for (cn = cns->child; cn; cn = cn->sib) {
/* Only matching section names */
if (cn->v || strcmp(cn->key, seg->policy_name) != 0)
continue;
if (!(new = dm_config_create()))
goto_out;
new->root = settings->root;
old->root = seg->policy_settings;
new->cascade = old;
if (!(tmp = dm_config_flatten(new)))
goto_out;
}
if (!cn->child)
break;
if ((cn = dm_config_find_node((tmp) ? tmp->root : settings->root, "policy_settings")) &&
!(seg->policy_settings = dm_config_clone_node_with_mem(lv->vg->vgmem, cn, 0)))
goto_out;
if (!(new = dm_config_create()))
goto_out;
if (!(new->root = dm_config_clone_node_with_mem(new->mem,
cn->child, 1)))
goto_out;
if (!(seg->policy_settings = dm_config_create_node(new, "policy_settings")))
goto_out;
seg->policy_settings->child = new->root;
break; /* Only first match counts */
}
if (name && !(seg->policy_name = dm_pool_strdup(lv->vg->vgmem, name))) {
log_error("Failed to duplicate policy name.");
goto out;
}
restart: /* remove any 'default" nodes */

View File

@@ -131,7 +131,7 @@ char *lvseg_discards_dup(struct dm_pool *mem, const struct lv_segment *seg)
char *lvseg_cachemode_dup(struct dm_pool *mem, const struct lv_segment *seg)
{
const char *name = get_cache_mode_name(seg);
const char *name = get_cache_pool_cachemode_name(seg);
if (!name)
return_NULL;

View File

@@ -4205,18 +4205,6 @@ int lv_rename_update(struct cmd_context *cmd, struct logical_volume *lv,
return 0;
}
/*
* The lvmlockd LV lock is only acquired here to ensure the LV is not
* active on another host. This requests a transient LV lock.
* If the LV is active, a persistent LV lock already exists in
* lvmlockd, and the transient lock request does nothing.
* If the LV is not active, then no LV lock exists and the transient
* lock request acquires the LV lock (or fails). The transient lock
* is automatically released when the command exits.
*/
if (!lockd_lv(cmd, lv, "ex", 0))
return_0;
if (update_mda && !archive(vg))
return_0;
@@ -6998,7 +6986,7 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
if (seg_is_pool(lp))
status |= LVM_WRITE; /* Pool is always writable */
else if (seg_is_cache(lp) || seg_is_thin_volume(lp)) {
else if (seg_is_cache(lp) || seg_is_thin_volume(lp)) {
/* Resolve pool volume */
if (!lp->pool_name) {
/* Should be already checked */
@@ -7074,11 +7062,7 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
display_lvname(origin_lv));
return NULL;
}
} else if (seg_is_cache(lp)) {
if (!pool_lv) {
log_error(INTERNAL_ERROR "Pool LV for cache is missing.");
return NULL;
}
} else if (pool_lv && seg_is_cache(lp)) {
if (!lv_is_cache_pool(pool_lv)) {
log_error("Logical volume %s is not a cache pool.",
display_lvname(pool_lv));
@@ -7222,7 +7206,6 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
if (!archive(vg))
return_NULL;
if (pool_lv && seg_is_thin_volume(lp)) {
/* Ensure all stacked messages are submitted */
if ((pool_is_active(pool_lv) || is_change_activating(lp->activate)) &&
@@ -7269,25 +7252,16 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
memlock_unlock(vg->cmd);
if (seg_is_cache_pool(lp) || seg_is_cache(lp)) {
if (!cache_set_mode(first_seg(lv), lp->cache_mode)) {
stack;
goto revert_new_lv;
}
if (!cache_set_policy(first_seg(lv), lp->policy_name, lp->policy_settings)) {
stack;
goto revert_new_lv;
}
pool_lv = pool_lv ? : lv;
if (lp->chunk_size) {
first_seg(pool_lv)->chunk_size = lp->chunk_size;
/* TODO: some calc_policy solution for cache ? */
if (!recalculate_pool_chunk_size_with_dev_hints(pool_lv, lp->passed_args,
THIN_CHUNK_SIZE_CALC_METHOD_GENERIC)) {
stack;
goto revert_new_lv;
}
if (!lv_cache_set_policy(pool_lv, lp->policy_name, lp->policy_settings))
return_NULL; /* revert? */
first_seg(pool_lv)->chunk_size = lp->chunk_size;
first_seg(pool_lv)->feature_flags = lp->feature_flags;
/* TODO: some calc_policy solution for cache ? */
if (!recalculate_pool_chunk_size_with_dev_hints(pool_lv, lp->passed_args,
THIN_CHUNK_SIZE_CALC_METHOD_GENERIC)) {
stack;
goto revert_new_lv;
}
} else if (seg_is_raid(lp)) {
first_seg(lv)->min_recovery_rate = lp->min_recovery_rate;
@@ -7492,12 +7466,6 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
}
lv = tmp_lv;
if (!cache_set_mode(first_seg(lv), lp->cache_mode))
return_NULL; /* revert? */
if (!cache_set_policy(first_seg(lv), lp->policy_name, lp->policy_settings))
return_NULL; /* revert? */
if (!lv_update_and_reload(lv)) {
/* FIXME Do a better revert */
log_error("Aborting. Manual intervention required.");

View File

@@ -208,21 +208,7 @@ int check_lv_segments(struct logical_volume *lv, int complete_vg)
}
}
if (seg_is_cache_pool(seg) &&
!dm_list_empty(&seg->lv->segs_using_this_lv)) {
switch (seg->feature_flags &
(DM_CACHE_FEATURE_PASSTHROUGH |
DM_CACHE_FEATURE_WRITETHROUGH |
DM_CACHE_FEATURE_WRITEBACK)) {
case DM_CACHE_FEATURE_PASSTHROUGH:
case DM_CACHE_FEATURE_WRITETHROUGH:
case DM_CACHE_FEATURE_WRITEBACK:
break;
default:
log_error("LV %s has invalid cache's feature flag.",
lv->name);
inc_error_count;
}
if (seg_is_cache_pool(seg)) {
if (!seg->policy_name) {
log_error("LV %s is missing cache policy name.", lv->name);
inc_error_count;

View File

@@ -901,7 +901,7 @@ struct lvcreate_params {
uint32_t min_recovery_rate; /* RAID */
uint32_t max_recovery_rate; /* RAID */
const char *cache_mode; /* cache */
uint64_t feature_flags; /* cache */
const char *policy_name; /* cache */
struct dm_config_tree *policy_settings; /* cache */
@@ -1153,11 +1153,8 @@ struct lv_status_cache {
dm_percent_t dirty_usage;
};
const char *get_cache_mode_name(const struct lv_segment *cache_seg);
int cache_mode_is_set(const struct lv_segment *seg);
int cache_set_mode(struct lv_segment *cache_seg, const char *str);
int cache_set_policy(struct lv_segment *cache_seg, const char *name,
const struct dm_config_tree *settings);
const char *get_cache_pool_cachemode_name(const struct lv_segment *seg);
int set_cache_pool_feature(uint64_t *feature_flags, const char *str);
int update_cache_pool_params(const struct segment_type *segtype,
struct volume_group *vg, unsigned attr,
int passed_args, uint32_t pool_data_extents,
@@ -1168,6 +1165,8 @@ int validate_lv_cache_create_origin(const struct logical_volume *origin_lv);
struct logical_volume *lv_cache_create(struct logical_volume *pool,
struct logical_volume *origin);
int lv_cache_remove(struct logical_volume *cache_lv);
int lv_cache_set_policy(struct logical_volume *cache_lv, const char *name,
const struct dm_config_tree *settings);
int wipe_cache_pool(struct logical_volume *cache_pool_lv);
/* -- metadata/cache_manip.c */

View File

@@ -191,9 +191,6 @@ int init_thin_segtypes(struct cmd_context *cmd, struct segtype_library *seglib);
int init_cache_segtypes(struct cmd_context *cmd, struct segtype_library *seglib);
#endif
#define CACHE_FEATURE_POLICY_MQ (1U << 0)
#define CACHE_FEATURE_POLICY_SMQ (1U << 1)
#define SNAPSHOT_FEATURE_FIXED_LEAK (1U << 0) /* version 1.12 */
#ifdef SNAPSHOT_INTERNAL

View File

@@ -516,9 +516,6 @@
/* Define to 1 if you have the <sys/time.h> header file. */
#undef HAVE_SYS_TIME_H
/* Define to 1 if you have the <sys/timerfd.h> header file. */
#undef HAVE_SYS_TIMERFD_H
/* Define to 1 if you have the <sys/types.h> header file. */
#undef HAVE_SYS_TYPES_H

View File

@@ -2079,7 +2079,7 @@ static int _cachemode_disp(struct dm_report *rh, struct dm_pool *mem,
seg = first_seg(seg->pool_lv);
if (seg_is_cache_pool(seg)) {
if (!(cachemode_str = get_cache_mode_name(seg)))
if (!(cachemode_str = get_cache_pool_cachemode_name(seg)))
return_0;
return dm_report_field_string(rh, field, &cachemode_str);

View File

@@ -1,4 +1,16 @@
dm_report_add_header_row
dm_report_clear
dm_report_column_headings
dm_report_get_interval
dm_report_get_interval_ms
dm_report_get_last_interval
dm_report_header
dm_report_header_set_content
dm_report_output_headers
dm_report_set_headers
dm_report_set_interval
dm_report_set_interval_ms
dm_report_wait
dm_size_to_string
dm_stats_bind_devno
dm_stats_bind_name
@@ -9,6 +21,7 @@ dm_stats_create
dm_stats_create_region
dm_stats_delete_region
dm_stats_destroy
dm_stats_get_area_len
dm_stats_get_area_start
dm_stats_get_average_queue_size
dm_stats_get_average_rd_wait_time
@@ -18,17 +31,16 @@ dm_stats_get_average_wr_wait_time
dm_stats_get_current_area
dm_stats_get_current_area_len
dm_stats_get_current_area_start
dm_stats_get_current_nr_areas
dm_stats_get_current_region
dm_stats_get_current_region_area_len
dm_stats_get_current_region_aux_data
dm_stats_get_current_region_len
dm_stats_get_current_region_program_id
dm_stats_get_current_region_start
dm_stats_get_interval
dm_stats_get_interval_ms
dm_stats_get_io_in_progress
dm_stats_get_io_nsecs
dm_stats_get_nr_areas
dm_stats_get_nr_regions
dm_stats_get_rd_merges_per_sec
dm_stats_get_read_nsecs
dm_stats_get_reads
@@ -39,11 +51,8 @@ dm_stats_get_reads_per_sec
dm_stats_get_region_area_len
dm_stats_get_region_aux_data
dm_stats_get_region_len
dm_stats_get_region_nr_areas
dm_stats_get_region_program_id
dm_stats_get_region_start
dm_stats_get_sampling_interval_ms
dm_stats_get_sampling_interval_ns
dm_stats_get_service_time
dm_stats_get_throughput
dm_stats_get_total_read_nsecs
@@ -57,22 +66,25 @@ dm_stats_get_write_sectors_per_sec
dm_stats_get_writes_merged
dm_stats_get_writes_per_sec
dm_stats_get_wr_merges_per_sec
dm_stats_init
dm_stats_list
dm_stats_nr_areas
dm_stats_nr_areas_current
dm_stats_nr_areas_region
dm_stats_nr_regions
dm_stats_populate
dm_stats_populate_region
dm_stats_print_region
dm_stats_region_present
dm_stats_set_interval
dm_stats_set_interval_ms
dm_stats_set_program_id
dm_stats_set_sampling_interval_ms
dm_stats_set_sampling_interval_ns
dm_stats_walk_end
dm_stats_walk_next
dm_stats_walk_next_region
dm_stats_walk_start
dm_task_get_ioctl_timestamp
dm_task_set_record_timestamp
dm_timestamp_alloc
dm_timestamp_compare
dm_timestamp_copy
dm_timestamp_delta
dm_timestamp_destroy
dm_timestamp_get

View File

@@ -68,7 +68,6 @@ static unsigned _dm_version = DM_VERSION_MAJOR;
static unsigned _dm_version_minor = 0;
static unsigned _dm_version_patchlevel = 0;
static int _log_suppress = 0;
static struct dm_timestamp *_dm_ioctl_timestamp = NULL;
/*
* If the kernel dm driver only supports one major number
@@ -920,24 +919,6 @@ int dm_task_set_event_nr(struct dm_task *dmt, uint32_t event_nr)
return 1;
}
int dm_task_set_record_timestamp(struct dm_task *dmt)
{
if (!_dm_ioctl_timestamp)
_dm_ioctl_timestamp = dm_timestamp_alloc();
if (!_dm_ioctl_timestamp)
return_0;
dmt->record_timestamp = 1;
return 1;
}
struct dm_timestamp *dm_task_get_ioctl_timestamp(struct dm_task *dmt)
{
return dmt->record_timestamp ? _dm_ioctl_timestamp : NULL;
}
struct target *create_target(uint64_t start, uint64_t len, const char *type,
const char *params)
{
@@ -1735,7 +1716,6 @@ static struct dm_ioctl *_do_dm_ioctl(struct dm_task *dmt, unsigned command,
{
struct dm_ioctl *dmi;
int ioctl_with_uevent;
int r;
dmt->ioctl_errno = 0;
@@ -1823,13 +1803,8 @@ static struct dm_ioctl *_do_dm_ioctl(struct dm_task *dmt, unsigned command,
dmt->sector, _sanitise_message(dmt->message),
dmi->data_size, retry_repeat_count);
#ifdef DM_IOCTLS
r = ioctl(_control_fd, command, dmi);
if (dmt->record_timestamp)
if (!dm_timestamp_get(_dm_ioctl_timestamp))
stack;
if (r < 0 && dmt->expected_errno != errno) {
if (ioctl(_control_fd, command, dmi) < 0 &&
dmt->expected_errno != errno) {
dmt->ioctl_errno = errno;
if (dmt->ioctl_errno == ENXIO && ((dmt->type == DM_DEVICE_INFO) ||
(dmt->type == DM_DEVICE_MKNODES) ||
@@ -2074,8 +2049,6 @@ repeat_ioctl:
void dm_lib_release(void)
{
_close_control_fd();
dm_timestamp_destroy(_dm_ioctl_timestamp);
_dm_ioctl_timestamp = NULL;
update_devs();
}

View File

@@ -70,8 +70,6 @@ struct dm_task {
int expected_errno;
int ioctl_errno;
int record_timestamp;
char *uuid;
char *mangled_uuid;
};

File diff suppressed because it is too large Load Diff

View File

@@ -277,7 +277,6 @@ struct dm_task *dm_task_create(int type)
dmt->query_inactive_table = 0;
dmt->new_uuid = 0;
dmt->secure_data = 0;
dmt->record_timestamp = 0;
return dmt;
}

View File

@@ -159,7 +159,7 @@ struct load_segment {
uint32_t stripe_size; /* Striped + raid */
int persistent; /* Snapshot */
uint32_t chunk_size; /* Snapshot */
uint32_t chunk_size; /* Snapshot + cache */
struct dm_tree_node *cow; /* Snapshot */
struct dm_tree_node *origin; /* Snapshot + Snapshot origin + Cache */
struct dm_tree_node *merge; /* Snapshot */
@@ -200,7 +200,7 @@ struct load_segment {
struct dm_list thin_messages; /* Thin_pool */
uint64_t transaction_id; /* Thin_pool */
uint64_t low_water_mark; /* Thin_pool */
uint32_t data_block_size; /* Thin_pool + cache */
uint32_t data_block_size; /* Thin_pool */
unsigned skip_block_zeroing; /* Thin_pool */
unsigned ignore_discard; /* Thin_pool target vsn 1.1 */
unsigned no_discard_passdown; /* Thin_pool target vsn 1.1 */
@@ -2429,8 +2429,8 @@ static int _cache_emit_segment_line(struct dm_task *dmt,
EMIT_PARAMS(pos, " %s %s %s", metadata, data, origin);
/* Data block size */
EMIT_PARAMS(pos, " %u", seg->data_block_size);
/* Chunk size */
EMIT_PARAMS(pos, " %u", seg->chunk_size);
/* Features */
/* feature_count = hweight32(seg->flags); */
@@ -3353,37 +3353,11 @@ int dm_tree_node_add_cache_target(struct dm_tree_node *node,
const char *origin_uuid,
const char *policy_name,
const struct dm_config_node *policy_settings,
uint32_t data_block_size)
uint32_t chunk_size)
{
struct dm_config_node *cn;
struct load_segment *seg;
switch (feature_flags &
(DM_CACHE_FEATURE_PASSTHROUGH |
DM_CACHE_FEATURE_WRITETHROUGH |
DM_CACHE_FEATURE_WRITEBACK)) {
case DM_CACHE_FEATURE_PASSTHROUGH:
case DM_CACHE_FEATURE_WRITETHROUGH:
case DM_CACHE_FEATURE_WRITEBACK:
break;
default:
log_error("Invalid cache's feature flag " FMTu64 ".",
feature_flags);
return 0;
}
if (data_block_size < DM_CACHE_MIN_DATA_BLOCK_SIZE) {
log_error("Data block size %u is lower then %u sectors.",
data_block_size, DM_CACHE_MIN_DATA_BLOCK_SIZE);
return 0;
}
if (data_block_size > DM_CACHE_MAX_DATA_BLOCK_SIZE) {
log_error("Data block size %u is higher then %u sectors.",
data_block_size, DM_CACHE_MAX_DATA_BLOCK_SIZE);
return 0;
}
if (!(seg = _add_segment(node, SEG_CACHE, size)))
return_0;
@@ -3405,6 +3379,7 @@ int dm_tree_node_add_cache_target(struct dm_tree_node *node,
if (!_link_tree_nodes(node, seg->metadata))
return_0;
if (!(seg->origin = dm_tree_find_node_by_uuid(node->dtree,
origin_uuid))) {
log_error("Missing cache's origin uuid %s.",
@@ -3414,7 +3389,7 @@ int dm_tree_node_add_cache_target(struct dm_tree_node *node,
if (!_link_tree_nodes(node, seg->origin))
return_0;
seg->data_block_size = data_block_size;
seg->chunk_size = chunk_size;
seg->flags = feature_flags;
seg->policy_name = policy_name;
@@ -3433,6 +3408,7 @@ int dm_tree_node_add_cache_target(struct dm_tree_node *node,
}
}
return 1;
}

View File

@@ -48,18 +48,30 @@ struct dm_report {
const char *field_prefix;
uint32_t flags;
const char *separator;
/* reporting interval in ms */
uint32_t interval;
/* duration of last wait in ns */
uint64_t last_wait;
uint32_t keys_count;
/* Ordered list of headers to be output before the report. */
struct dm_list header_props;
/* Rows of headers */
struct dm_list header_rows;
/* Ordered list of fields needed for this report */
struct dm_list field_props;
/* Rows of report data */
struct dm_list rows;
/* Array of header definitions */
const struct dm_report_header_type *headers;
/* Array of field definitions */
const struct dm_report_field_type *fields;
const char **canonical_field_ids;
const struct dm_report_object_type *types;
/* To store caller private data */
@@ -92,6 +104,14 @@ struct field_properties {
int implicit;
};
struct header_properties {
struct dm_list list;
uint32_t hdr_num;
int32_t width;
const struct dm_report_object_type *type;
uint32_t flags;
};
/*
* Report selection
*/
@@ -158,18 +178,18 @@ static struct op_def _op_cmp[] = {
#define SEL_LIST_SUBSET_LE 0x00080000
static struct op_def _op_log[] = {
{ "&&", SEL_AND, "All fields must match" },
{ "&&", SEL_AND, "All fields must match" },
{ ",", SEL_AND, "All fields must match" },
{ "||", SEL_OR, "At least one field must match" },
{ "||", SEL_OR, "At least one field must match" },
{ "#", SEL_OR, "At least one field must match" },
{ "!", SEL_MODIFIER_NOT, "Logical negation" },
{ "(", SEL_PRECEDENCE_PS, "Left parenthesis" },
{ ")", SEL_PRECEDENCE_PE, "Right parenthesis" },
{ "[", SEL_LIST_LS, "List start" },
{ "]", SEL_LIST_LE, "List end"},
{ "{", SEL_LIST_SUBSET_LS, "List subset start"},
{ "}", SEL_LIST_SUBSET_LE, "List subset end"},
{ NULL, 0, NULL},
{ "!", SEL_MODIFIER_NOT, "Logical negation" },
{ "(", SEL_PRECEDENCE_PS, "Left parenthesis" },
{ ")", SEL_PRECEDENCE_PE, "Right parenthesis" },
{ "[", SEL_LIST_LS, "List start" },
{ "]", SEL_LIST_LE, "List end"},
{ "{", SEL_LIST_SUBSET_LS, "List subset start"},
{ "}", SEL_LIST_SUBSET_LE, "List subset end"},
{ NULL, 0, NULL},
};
struct selection_str_list {
@@ -229,6 +249,18 @@ struct row {
int selected;
};
struct dm_report_header {
struct dm_list list;
struct header_properties *props;
const char *header_string;
};
struct header_row {
struct dm_list list;
struct dm_report *rh;
struct dm_list headers;
};
/*
* Implicit report types and fields.
*/
@@ -671,6 +703,11 @@ void dm_report_field_set_value(struct dm_report_field *field, const void *value,
log_warn(INTERNAL_ERROR "Using string as sort value for numerical field.");
}
void dm_report_header_set_content(struct dm_report_header *hdr, const void *content)
{
hdr->header_string = (const char *) content;
}
static const char *_get_field_type_name(unsigned field_type)
{
switch (field_type) {
@@ -699,6 +736,18 @@ static size_t _get_longest_field_id_len(const struct dm_report_field_type *field
return id_len;
}
static size_t _get_longest_header_id_len(const struct dm_report_header_type *headers)
{
uint32_t f;
size_t id_len = 0;
for (f = 0; headers[f].report_fn; f++)
if (strlen(headers[f].id) > id_len)
id_len = strlen(headers[f].id);
return id_len;
}
static void _display_fields_more(struct dm_report *rh,
const struct dm_report_field_type *fields,
size_t id_len, int display_all_fields_item,
@@ -743,6 +792,45 @@ static void _display_fields_more(struct dm_report *rh,
}
}
static void _display_headers_more(struct dm_report *rh,
const struct dm_report_header_type *headers,
size_t id_len, int display_all_headers_item)
{
uint32_t h;
const struct dm_report_object_type *type;
const char *desc, *last_desc = "";
for (h = 0; headers[h].report_fn; h++)
if (strlen(headers[h].id) > id_len)
id_len = strlen(headers[h].id);
for (type = rh->types; type->data_fn; type++)
if (strlen(type->prefix) + 3 > id_len)
id_len = strlen(type->prefix) + 3;
for (h = 0; headers[h].report_fn; h++) {
if ((type = _find_type(rh, headers[h].type)) && type->desc)
desc = type->desc;
else
desc = " ";
if (desc != last_desc) {
if (*last_desc)
log_warn(" ");
log_warn("%s Fields", desc);
log_warn("%*.*s", (int) strlen(desc) + 7,
(int) strlen(desc) + 7,
"-------------------------------------------------------------------------------");
if (display_all_headers_item && type->id != SPECIAL_REPORT_TYPE)
log_warn(" %sall%-*s - %s", type->prefix,
(int) (id_len - 3 - strlen(type->prefix)), "",
"All headers in this section.");
}
/* FIXME Add line-wrapping at terminal width (or 80 cols) */
log_warn(" %-*s - %s", (int) id_len, headers[h].id, headers[h].desc);
last_desc = desc;
}
}
/*
* show help message
*/
@@ -764,6 +852,20 @@ static void _display_fields(struct dm_report *rh, int display_all_fields_item,
}
/*
* show help message
*/
static void _display_headers(struct dm_report *rh, int display_all_headers_item,
int display_header_types)
{
size_t tmp, id_len = 0;
if ((tmp = _get_longest_header_id_len(rh->headers)) > id_len)
id_len = tmp;
_display_headers_more(rh, rh->headers, id_len, display_all_headers_item);
}
/*
* Initialise report handle
*/
@@ -821,52 +923,68 @@ static struct field_properties * _add_field(struct dm_report *rh,
return fp;
}
static int _get_canonical_field_name(const char *field,
size_t flen,
char *canonical_field,
size_t fcanonical_len,
int *differs)
static int _copy_header(struct dm_report *rh, struct header_properties *dest,
uint32_t hdr_num)
{
size_t i;
int diff = 0;
const struct dm_report_header_type *headers = rh->headers;
for (i = 0; *field && flen; field++, flen--) {
if (*field == '_') {
diff = 1;
continue;
}
if (i >= fcanonical_len) {
log_error("%s: field name too long", field);
return 0;
}
canonical_field[i++] = *field;
dest->hdr_num = hdr_num;
dest->width = headers[hdr_num].width;
dest->flags = headers[hdr_num].flags & DM_REPORT_FIELD_MASK;
/* set object type method */
dest->type = _find_type(rh, headers[hdr_num].type);
if (!dest->type) {
log_error("dm_report: no matching header: %s",
headers[hdr_num].id);
return 0;
}
canonical_field[i] = '\0';
if (differs)
*differs = diff;
return 1;
}
static struct header_properties * _add_header(struct dm_report *rh,
uint32_t hdr_num, uint32_t flags)
{
struct header_properties *hp;
if (!(hp = dm_pool_zalloc(rh->mem, sizeof(*hp)))) {
log_error("dm_report: struct header_properties allocation "
"failed");
return NULL;
}
if (!_copy_header(rh, hp, hdr_num)) {
stack;
dm_pool_free(rh->mem, hp);
return NULL;
}
hp->flags |= flags;
dm_list_add(&rh->header_props, &hp->list);
return hp;
}
/*
* Compare canonical_name1 against canonical_name2 or prefix
* plus canonical_name2. Canonical name is a name where all
* superfluous characters are removed (underscores for now).
* Both names are always null-terminated.
* Compare name1 against name2 or prefix plus name2
* name2 is not necessarily null-terminated.
* len2 is the length of name2.
*/
static int _is_same_field(const char *canonical_name1, const char *canonical_name2,
const char *prefix)
static int _is_same_name(const char *name1, const char *name2,
size_t len2, const char *prefix)
{
size_t prefix_len;
/* Exact match? */
if (!strcasecmp(canonical_name1, canonical_name2))
if (!strncasecmp(name1, name2, len2) && strlen(name1) == len2)
return 1;
/* Match including prefix? */
prefix_len = strlen(prefix) - 1;
if (!strncasecmp(prefix, canonical_name1, prefix_len) &&
!strcasecmp(canonical_name1 + prefix_len, canonical_name2))
prefix_len = strlen(prefix);
if (!strncasecmp(prefix, name1, prefix_len) &&
!strncasecmp(name1 + prefix_len, name2, len2) &&
strlen(name1) == prefix_len + len2)
return 1;
return 0;
@@ -880,20 +998,15 @@ static void _all_match_combine(const struct dm_report_object_type *types,
const char *field, size_t flen,
uint32_t *report_types)
{
char field_canon[DM_REPORT_FIELD_TYPE_ID_LEN];
const struct dm_report_object_type *t;
size_t prefix_len;
if (!_get_canonical_field_name(field, flen, field_canon, DM_REPORT_FIELD_TYPE_ID_LEN, NULL))
return;
flen = strlen(field_canon);
for (t = types; t->data_fn; t++) {
prefix_len = strlen(t->prefix) - 1;
prefix_len = strlen(t->prefix);
if (!strncasecmp(t->prefix, field_canon, prefix_len) &&
if (!strncasecmp(t->prefix, field, prefix_len) &&
((unprefixed_all_matched && (flen == prefix_len)) ||
(!strncasecmp(field_canon + prefix_len, "all", 3) &&
(!strncasecmp(field + prefix_len, "all", 3) &&
(flen == prefix_len + 3))))
*report_types |= t->id;
}
@@ -938,17 +1051,13 @@ static int _add_all_fields(struct dm_report *rh, uint32_t type)
static int _get_field(struct dm_report *rh, const char *field, size_t flen,
uint32_t *f_ret, int *implicit)
{
char field_canon[DM_REPORT_FIELD_TYPE_ID_LEN];
uint32_t f;
if (!flen)
return 0;
if (!_get_canonical_field_name(field, flen, field_canon, DM_REPORT_FIELD_TYPE_ID_LEN, NULL))
return 0;
for (f = 0; _implicit_report_fields[f].report_fn; f++) {
if (_is_same_field(_implicit_report_fields[f].id, field_canon, rh->field_prefix)) {
if (_is_same_name(_implicit_report_fields[f].id, field, flen, rh->field_prefix)) {
*f_ret = f;
*implicit = 1;
return 1;
@@ -956,7 +1065,7 @@ static int _get_field(struct dm_report *rh, const char *field, size_t flen,
}
for (f = 0; rh->fields[f].report_fn; f++) {
if (_is_same_field(rh->canonical_field_ids[f], field_canon, rh->field_prefix)) {
if (_is_same_name(rh->fields[f].id, field, flen, rh->field_prefix)) {
*f_ret = f;
*implicit = 0;
return 1;
@@ -995,6 +1104,51 @@ static int _field_match(struct dm_report *rh, const char *field, size_t flen,
return 0;
}
/*
* Add all headers with a matching type.
*/
static int _add_all_headers(struct dm_report *rh, uint32_t type)
{
uint32_t h;
for (h = 0; rh->headers[h].report_fn; h++)
if ((rh->headers[h].type & type) && !_add_header(rh, h, 0))
return 0;
return 1;
}
static int _get_header(struct dm_report *rh, const char *header,
size_t hlen, uint32_t *h_ret)
{
uint32_t h;
if (!hlen)
return 0;
for (h = 0; rh->headers[h].report_fn; h++) {
if (_is_same_name(rh->headers[h].id, header, hlen, "")) {
*h_ret = h;
return 1;
}
}
return 0;
}
static int _header_match(struct dm_report *rh, const char *header, size_t hlen)
{
uint32_t h /*, type*/;
if (!hlen)
return 0;
if ((_get_header(rh, header, hlen, &h)))
return _add_header(rh, h, 0) ? 1 : 0;
return 0;
}
static int _add_sort_key(struct dm_report *rh, uint32_t field_num, int implicit,
uint32_t flags, unsigned report_type_only)
{
@@ -1035,7 +1189,6 @@ static int _add_sort_key(struct dm_report *rh, uint32_t field_num, int implicit,
static int _key_match(struct dm_report *rh, const char *key, size_t len,
unsigned report_type_only)
{
char key_canon[DM_REPORT_FIELD_TYPE_ID_LEN];
uint32_t f;
uint32_t flags;
@@ -1058,15 +1211,12 @@ static int _key_match(struct dm_report *rh, const char *key, size_t len,
return 0;
}
if (!_get_canonical_field_name(key, len, key_canon, DM_REPORT_FIELD_TYPE_ID_LEN, NULL))
return 0;
for (f = 0; _implicit_report_fields[f].report_fn; f++)
if (_is_same_field(_implicit_report_fields[f].id, key_canon, rh->field_prefix))
if (_is_same_name(_implicit_report_fields[f].id, key, len, rh->field_prefix))
return _add_sort_key(rh, f, 1, flags, report_type_only);
for (f = 0; rh->fields[f].report_fn; f++)
if (_is_same_field(rh->canonical_field_ids[f], key_canon, rh->field_prefix))
if (_is_same_name(rh->fields[f].id, key, len, rh->field_prefix))
return _add_sort_key(rh, f, 0, flags, report_type_only);
return 0;
@@ -1126,6 +1276,32 @@ static int _parse_keys(struct dm_report *rh, const char *keys,
return 1;
}
static int _parse_headers(struct dm_report *rh, const char *format)
{
const char *ws; /* Word start */
const char *we = format; /* Word end */
while (*we) {
/* Allow consecutive commas */
while (*we && *we == ',')
we++;
/* start of the header name */
ws = we;
while (*we && *we != ',')
we++;
if (!_header_match(rh, ws, (size_t) (we - ws))) {
_display_headers(rh, 1, 0);
log_warn(" ");
log_error("Unrecognised header: %.*s", (int) (we - ws), ws);
return 0;
}
}
return 1;
}
static int _contains_reserved_report_type(const struct dm_report_object_type *types)
{
const struct dm_report_object_type *type, *implicit_type;
@@ -1174,36 +1350,6 @@ static int _help_requested(struct dm_report *rh)
return 0;
}
static int _canonicalize_field_ids(struct dm_report *rh)
{
size_t registered_field_count = 0, i;
char canonical_field[DM_REPORT_FIELD_TYPE_ID_LEN];
char *canonical_field_dup;
int differs;
while (*rh->fields[registered_field_count].id)
registered_field_count++;
if (!(rh->canonical_field_ids = dm_pool_alloc(rh->mem, registered_field_count * sizeof(const char *)))) {
log_error("_canonicalize_field_ids: dm_pool_alloc failed");
return 0;
}
for (i = 0; i < registered_field_count; i++) {
if (!_get_canonical_field_name(rh->fields[i].id, strlen(rh->fields[i].id),
canonical_field, DM_REPORT_FIELD_TYPE_ID_LEN, &differs))
return_0;
if (differs) {
canonical_field_dup = dm_pool_strdup(rh->mem, canonical_field);
rh->canonical_field_ids[i] = canonical_field_dup;
} else
rh->canonical_field_ids[i] = rh->fields[i].id;
}
return 1;
}
struct dm_report *dm_report_init(uint32_t *report_types,
const struct dm_report_object_type *types,
const struct dm_report_field_type *fields,
@@ -1236,6 +1382,7 @@ struct dm_report *dm_report_init(uint32_t *report_types,
rh->fields = fields;
rh->types = types;
rh->private = private_data;
rh->interval = 0; /* no interval */
rh->flags |= output_flags & DM_REPORT_OUTPUT_MASK;
@@ -1251,6 +1398,8 @@ struct dm_report *dm_report_init(uint32_t *report_types,
rh->flags |= RH_SORT_REQUIRED;
dm_list_init(&rh->field_props);
dm_list_init(&rh->header_props);
dm_list_init(&rh->header_rows);
dm_list_init(&rh->rows);
if ((type = _find_type(rh, rh->report_types)) && type->prefix)
@@ -1264,11 +1413,6 @@ struct dm_report *dm_report_init(uint32_t *report_types,
return NULL;
}
if (!_canonicalize_field_ids(rh)) {
dm_report_free(rh);
return NULL;
}
/*
* To keep the code needed to add the "all" field to a minimum, we parse
* the field lists twice. The first time we only update the report type.
@@ -1322,6 +1466,21 @@ static char *_toupperstr(char *str)
return str;
}
int dm_report_set_headers(struct dm_report *rh,
const struct dm_report_header_type *headers)
{
if (headers)
rh->headers = headers;
else
return 0;
return 1;
}
int dm_report_add_header_row(struct dm_report *rh, const char *output_headers)
{
return _parse_headers(rh, output_headers);
}
int dm_report_set_output_field_name_prefix(struct dm_report *rh, const char *output_field_name_prefix)
{
char *prefix;
@@ -1362,6 +1521,21 @@ static void *_report_get_implicit_field_data(struct dm_report *rh __attribute__(
return NULL;
}
/*
* Create part of a line of header data
*/
static void *_report_get_header_data(struct dm_report *rh, struct
header_properties *hp, void *object)
{
const struct dm_report_header_type *headers = rh->headers;
char *ret = hp->type->data_fn(object);
if (!ret)
return NULL;
return (void *)(ret + headers[hp->hdr_num].offset);
}
static int _dbl_equal(double d1, double d2)
{
return fabs(d1 - d2) < DBL_EPSILON;
@@ -2052,6 +2226,82 @@ int dm_report_object_is_selected(struct dm_report *rh, void *object, int do_outp
return _do_report_object(rh, object, do_output, selected);
}
int dm_report_header(struct dm_report *rh, void *object)
{
const struct dm_report_header_type *headers;
struct header_properties *hp;
struct header_row *row = NULL;
struct dm_report_header *header;
void *data = NULL;
int len;
int r = 0;
if (!rh) {
log_error(INTERNAL_ERROR "dm_report_header: dm_report handler is NULL.");
return 0;
}
if (rh->flags & RH_ALREADY_REPORTED)
return 1;
if (!(row = dm_pool_zalloc(rh->mem, sizeof(*row)))) {
log_error("dm_report_header: struct header_row allocation failed");
return 0;
}
row->rh = rh;
headers = rh->headers;
dm_list_init(&row->headers);
/* For each field to be displayed, call its report_fn */
dm_list_iterate_items(hp, &rh->header_props) {
if (!(header = dm_pool_zalloc(rh->mem, sizeof(*header)))) {
log_error("do_report_header: "
"struct dm_report_header allocation failed");
goto out;
}
header->props = hp;
data = _report_get_header_data(rh, hp, object);
if (!data) {
log_error("do_report_header:"
"no data assigned to header %s",
headers[hp->hdr_num].id);
goto out;
}
if (!headers[hp->hdr_num].report_fn(rh, rh->mem,
header, data,
rh->private)) {
log_error("do_report_header:"
"report function failed for header %s",
headers[hp->hdr_num].id);
goto out;
}
dm_list_add(&row->headers, &header->list);
}
r = 1;
dm_list_add(&rh->header_rows, &row->list);
dm_list_iterate_items(header, &row->headers) {
len = (int) strlen(header->header_string);
if ((len > header->props->width))
header->props->width = len;
}
if (!(rh->flags & DM_REPORT_OUTPUT_BUFFERED))
return dm_report_output_headers(rh);
out:
if (!r)
dm_pool_free(rh->mem, row);
return r;
}
/*
* Selection parsing
*/
@@ -3994,9 +4244,6 @@ static int _report_headings(struct dm_report *rh)
int dm_report_column_headings(struct dm_report *rh)
{
/* Columns-as-rows does not use _report_headings. */
if (rh->flags & DM_REPORT_OUTPUT_COLUMNS_AS_ROWS)
return 1;
return _report_headings(rh);
}
@@ -4178,13 +4425,80 @@ bad:
return 0;
}
/*
* Produce header output
*/
static int _output_header(struct dm_report *rh, struct dm_report_header *header)
{
int32_t width, labelwidth;
uint32_t align;
const char *hdrstr, *labelstr;
char *buf = NULL;
size_t buf_size = 0;
if (rh->flags & DM_REPORT_OUTPUT_HEADER_LABELS)
labelstr = rh->headers[header->props->hdr_num].label;
else
labelstr = "";
labelwidth = strlen(labelstr) + 1;
hdrstr = header->header_string;
width = header->props->width - labelwidth;
if (!(rh->flags & DM_REPORT_OUTPUT_ALIGNED)) {
if (!dm_pool_grow_object(rh->mem, hdrstr, 0)) {
log_error("dm_report: Unable to extend output line");
return 0;
}
} else {
if (!(align = header->props->flags & DM_REPORT_FIELD_ALIGN_MASK))
align = ((header->props->flags & DM_REPORT_FIELD_TYPE_NUMBER) ||
(header->props->flags & DM_REPORT_FIELD_TYPE_SIZE)) ?
DM_REPORT_FIELD_ALIGN_RIGHT : DM_REPORT_FIELD_ALIGN_LEFT;
/* Including trailing '\0'! */
buf_size = labelwidth + width + 1;
if (!(buf = dm_malloc(buf_size))) {
log_error("dm_report: Could not allocate memory for output line buffer.");
return 0;
}
if (align & DM_REPORT_FIELD_ALIGN_LEFT) {
if (dm_snprintf(buf, buf_size, "%-*.*s%-*.*s",
/* FIXME: handle label width better */
labelwidth, labelwidth, labelstr, width, width, hdrstr) < 0) {
log_error("dm_report: left-aligned snprintf() failed");
goto bad;
}
if (!dm_pool_grow_object(rh->mem, buf, labelwidth + width)) {
log_error("dm_report: Unable to extend output line");
goto bad;
}
} else if (align & DM_REPORT_FIELD_ALIGN_RIGHT) {
if (dm_snprintf(buf, buf_size, "%*.*s%*.*s",
/* FIXME: handle label width better */
labelwidth, labelwidth, labelstr, width, width, hdrstr) < 0) {
log_error("dm_report: right-aligned snprintf() failed");
goto bad;
}
if (!dm_pool_grow_object(rh->mem, buf, labelwidth + width)) {
log_error("dm_report: Unable to extend output line");
goto bad;
}
}
}
dm_free(buf);
return 1;
bad:
dm_free(buf);
return 0;
}
static void _destroy_rows(struct dm_report *rh)
{
/*
* free the first row allocated to this report: since this is a
* pool allocation this will also free all subsequently allocated
* rows from the report and any associated string data.
*/
/* free the first row allocated to this report */
if(rh->first_row)
dm_pool_free(rh->mem, rh->first_row);
rh->first_row = NULL;
@@ -4305,6 +4619,27 @@ static int _output_as_columns(struct dm_report *rh)
return 0;
}
int dm_report_clear(struct dm_report *rh)
{
struct dm_list *fh, *rowh, *ftmp, *rtmp;
struct row *row = NULL;
struct dm_report_field *field;
/* clear buffer */
dm_list_iterate_safe(rowh, rtmp, &rh->rows) {
row = dm_list_item(rowh, struct row);
dm_list_iterate_safe(fh, ftmp, &row->fields) {
field = dm_list_item(fh, struct dm_report_field);
dm_list_del(&field->list);
}
dm_list_del(&row->list);
}
_destroy_rows(rh);
return 1;
}
int dm_report_output(struct dm_report *rh)
{
if (dm_list_empty(&rh->rows))
@@ -4318,3 +4653,128 @@ int dm_report_output(struct dm_report *rh)
else
return _output_as_columns(rh);
}
int dm_report_output_headers(struct dm_report *rh)
{
struct dm_list *hh, *rowh, *htmp;
struct header_row *row = NULL;
struct dm_report_header *header;
/* Print and clear buffer */
dm_list_iterate_safe(rowh, htmp, &rh->header_rows) {
if (!dm_pool_begin_object(rh->mem, 512)) {
log_error("dm_report: Unable to allocate output line");
return 0;
}
row = dm_list_item(rowh, struct header_row);
/* don't attempt to print an empty header row. */
if (dm_list_empty(&row->headers)) {
dm_pool_abandon_object(rh->mem);
dm_list_del(&row->list);
continue;
}
dm_list_iterate_safe(hh, htmp, &row->headers) {
header = dm_list_item(hh, struct dm_report_header);
if (!_output_header(rh, header))
goto bad;
dm_list_del(&header->list);
if (!dm_list_end(&row->headers, hh))
if (!dm_pool_grow_object(rh->mem, rh->separator, 0)) {
log_error("dm_report: Unable to extend header output line");
goto bad;
}
}
if (!dm_pool_grow_object(rh->mem, "\0", 1)) {
log_error("dm_report: Unable to terminate header output line");
goto bad;
}
log_print("%s", (char *) dm_pool_end_object(rh->mem));
dm_list_del(&row->list);
if (dm_list_end(&rh->header_rows, rowh))
break;
}
dm_pool_free(rh->mem, row);
return 1;
bad:
dm_pool_abandon_object(rh->mem);
return 0;
}
#define NSEC_PER_USEC 1000L
#define NSEC_PER_MSEC 1000000L
#define NSEC_PER_SEC 1000000000L
void dm_report_set_interval(struct dm_report *rh, uint64_t interval)
{
rh->interval = interval;
}
void dm_report_set_interval_ms(struct dm_report *rh, uint64_t interval_ms)
{
rh->interval = interval_ms * NSEC_PER_MSEC;
}
uint64_t dm_report_get_interval(struct dm_report *rh)
{
return rh->interval;
}
uint64_t dm_report_get_interval_ms(struct dm_report *rh)
{
return (rh->interval / NSEC_PER_MSEC);
}
uint64_t dm_report_get_last_interval(struct dm_report *rh)
{
if (rh->last_wait)
return rh->last_wait;
return rh->interval;
}
int dm_report_wait(struct dm_report *rh)
{
struct dm_timestamp *ts_start, *ts_now;
int r = 1;
if (!rh->interval)
return_0;
if(!(ts_start = dm_timestamp_alloc()))
return_0;
if(!(ts_now = dm_timestamp_alloc()))
return_0;
if (!dm_timestamp_get(ts_start)) {
log_error("Could not obtain initial timestamp.");
return 0;
}
if (usleep(rh->interval / NSEC_PER_USEC)) {
if (errno == EINTR)
log_error("Report interval interrupted by signal.");
if (errno == EINVAL)
log_error("Report interval too short.");
r = 0;
}
if (!dm_timestamp_get(ts_now)) {
log_error("Could not obtain current timestamp.");
return 0;
}
/* store interval duration in nanoseconds */
rh->last_wait = dm_timestamp_delta(ts_now, ts_start);
dm_timestamp_destroy(ts_start);
dm_timestamp_destroy(ts_now);
return r;
}

View File

@@ -48,7 +48,7 @@ struct dm_stats_region {
uint64_t step;
char *program_id;
char *aux_data;
uint64_t timescale; /* precise_timestamps is per-region */
int timescale; /* precise_timestamps is per-region */
struct dm_stats_counters *counters;
};
@@ -60,10 +60,10 @@ struct dm_stats {
char *uuid; /* device-mapper UUID */
char *program_id; /* default program_id for this handle */
struct dm_pool *mem; /* memory pool for region and counter tables */
uint64_t nr_regions; /* total number of present regions */
uint64_t nr_regions; /* total number of valid regions */
uint64_t max_region; /* size of the regions table */
uint64_t interval_ns; /* sampling interval in nanoseconds */
uint64_t timescale; /* sample value multiplier */
uint64_t interval; /* sampling interval in nanoseconds */
int timescale; /* sample value multiplier */
struct dm_stats_region *regions;
/* statistics cursor */
uint64_t cur_region;
@@ -77,17 +77,10 @@ static char *_program_id_from_proc(void)
char buf[256];
if (!(comm = fopen(PROC_SELF_COMM, "r")))
return_NULL;
if (!fgets(buf, sizeof(buf), comm)) {
log_error("Could not read from %s", PROC_SELF_COMM);
if(fclose(comm))
stack;
return NULL;
}
if (fclose(comm))
stack;
if (!fgets(buf, sizeof(buf), comm))
return NULL;
return dm_strdup(buf);
}
@@ -96,10 +89,10 @@ struct dm_stats *dm_stats_create(const char *program_id)
{
struct dm_stats *dms = NULL;
if (!(dms = dm_zalloc(sizeof(*dms))))
if (!(dms = dm_malloc(sizeof(*dms))))
return_NULL;
if (!(dms->mem = dm_pool_create("stats_pool", 4096)))
goto_out;
return_NULL;
if (!program_id || !strlen(program_id))
dms->program_id = _program_id_from_proc();
@@ -111,7 +104,6 @@ struct dm_stats *dm_stats_create(const char *program_id)
dms->name = NULL;
dms->uuid = NULL;
/* all regions currently use msec precision */
dms->timescale = NSEC_PER_MSEC;
dms->nr_regions = DM_STATS_REGION_NOT_PRESENT;
@@ -119,15 +111,12 @@ struct dm_stats *dm_stats_create(const char *program_id)
dms->regions = NULL;
return dms;
out:
dm_free(dms);
return NULL;
}
/**
* Test whether the stats region pointed to by region is present.
*/
static int _stats_region_present(const struct dm_stats_region *region)
static int _stats_region_present(struct dm_stats_region *region)
{
return !(region->region_id == DM_STATS_REGION_NOT_PRESENT);
}
@@ -151,13 +140,13 @@ static void _stats_region_destroy(struct dm_stats_region *region)
static void _stats_regions_destroy(struct dm_stats *dms)
{
struct dm_pool *mem = dms->mem;
uint64_t i;
int i;
if (!dms->regions)
return;
/* walk backwards to obey pool order */
for (i = dms->max_region; (i != DM_STATS_REGION_NOT_PRESENT); i--)
for (i = dms->max_region; (i >= 0); i--)
_stats_region_destroy(&dms->regions[i]);
dm_pool_free(mem, dms->regions);
}
@@ -178,8 +167,7 @@ static int _stats_bound(struct dm_stats *dms)
{
if (dms->major > 0 || dms->name || dms->uuid)
return 1;
/* %p format specifier expects a void pointer. */
log_debug("Stats handle at %p is not bound.", (void *) dms);
log_debug("Stats handle at %p is not bound.", dms);
return 0;
}
@@ -227,6 +215,29 @@ int dm_stats_bind_uuid(struct dm_stats *dms, const char *uuid)
return 1;
}
int dm_stats_init(struct dm_stats *dms, uint64_t max_regions)
{
uint64_t cur = max_regions;
size_t region_table_size = max_regions * sizeof(*dms->regions);
if (!max_regions)
return_0;
if (dms->regions)
_stats_regions_destroy(dms);
dms->regions = dm_pool_alloc(dms->mem, region_table_size);
if (!dms->regions)
return_0;
dms->max_region = max_regions - 1;
while(--cur != UINT64_MAX)
dms->regions[cur].region_id = DM_STATS_REGION_NOT_PRESENT;
return 1;
}
static struct dm_task *_stats_send_message(struct dm_stats *dms, char *msg)
{
struct dm_task *dmt;
@@ -245,7 +256,8 @@ static struct dm_task *_stats_send_message(struct dm_stats *dms, char *msg)
return dmt;
out:
dm_task_destroy(dmt);
if(dmt)
dm_task_destroy(dmt);
return NULL;
}
@@ -256,8 +268,8 @@ static int _stats_parse_list_region(struct dm_stats_region *region, char *line)
int r;
/* line format:
* <region_id>: <start_sector>+<length> <step> <program_id> <aux_data>
*/
* <region_id>: <start_sector>+<length> <step> <program_id> <aux_data>
*/
r = sscanf(line, FMTu64 ": " FMTu64 "+" FMTu64 " " FMTu64 "%255s %255s",
&region->region_id, &region->start, &region->len, &region->step,
program_id, aux_data);
@@ -291,7 +303,7 @@ static int _stats_parse_list(struct dm_stats *dms, const char *resp)
char line[256];
if (!resp) {
log_error("Could not parse NULL @stats_list response.");
log_error("Could not parse NULL or empty @stats_list response.");
return 0;
}
@@ -305,10 +317,7 @@ static int _stats_parse_list(struct dm_stats *dms, const char *resp)
return 1;
}
/*
* dm_task_get_message_response() returns a 'const char *' but
* since fmemopen also permits "w" it expects a 'char *'.
*/
/* dm_task_get_message_response() returns a 'const char *' */
if (!(list_rows = fmemopen((char *)resp, strlen(resp), "r")))
return_0;
@@ -334,7 +343,7 @@ static int _stats_parse_list(struct dm_stats *dms, const char *resp)
if (!dm_pool_grow_object(mem, &cur, sizeof(cur)))
goto_out;
max_region++;
max_region++;
nr_regions++;
}
@@ -342,13 +351,10 @@ static int _stats_parse_list(struct dm_stats *dms, const char *resp)
dms->max_region = max_region - 1;
dms->regions = dm_pool_end_object(mem);
if (fclose(list_rows))
stack;
fclose(list_rows);
return 1;
out:
if(fclose(list_rows))
stack;
fclose(list_rows);
dm_pool_abandon_object(mem);
return 0;
}
@@ -390,8 +396,7 @@ out:
}
static int _stats_parse_region(struct dm_pool *mem, const char *resp,
struct dm_stats_region *region,
uint64_t timescale)
struct dm_stats_region *region, int timescale)
{
struct dm_stats_counters cur;
FILE *stats_rows = NULL;
@@ -409,10 +414,6 @@ static int _stats_parse_region(struct dm_pool *mem, const char *resp,
if (!dm_pool_begin_object(mem, 512))
goto_out;
/*
* dm_task_get_message_response() returns a 'const char *' but
* since fmemopen also permits "w" it expects a 'char *'.
*/
stats_rows = fmemopen((char *)resp, strlen(resp), "r");
if (!stats_rows)
goto_out;
@@ -488,16 +489,13 @@ static int _stats_parse_region(struct dm_pool *mem, const char *resp,
region->timescale = timescale;
region->counters = dm_pool_end_object(mem);
if (fclose(stats_rows))
stack;
fclose(stats_rows);
return 1;
out:
if (stats_rows)
if(fclose(stats_rows))
stack;
fclose(stats_rows);
dm_pool_abandon_object(mem);
return 0;
}
@@ -511,7 +509,8 @@ static uint64_t _nr_areas(uint64_t len, uint64_t step)
* treat the entire region as a single area. Any partial area at the
* end of the region is treated as an additional complete area.
*/
return (len / (step ? : len)) + !!(len % step);
return (len && step)
? (len / (step ? step : len)) + !!(len % step) : 0;
}
static uint64_t _nr_areas_region(struct dm_stats_region *region)
@@ -519,7 +518,7 @@ static uint64_t _nr_areas_region(struct dm_stats_region *region)
return _nr_areas(region->len, region->step);
}
static void _stats_walk_next(const struct dm_stats *dms, int region,
static void _stats_walk_next(struct dm_stats *dms, int region,
uint64_t *cur_r, uint64_t *cur_a)
{
struct dm_stats_region *cur = NULL;
@@ -539,13 +538,12 @@ static void _stats_walk_next(const struct dm_stats *dms, int region,
while(!dm_stats_region_present(dms, ++(*cur_r))
&& *cur_r < dms->max_region)
; /* keep walking until a present region is found
* or the end of the table is reached. */
* or the end of the table is reached. */
}
}
static void _stats_walk_start(const struct dm_stats *dms,
uint64_t *cur_r, uint64_t *cur_a)
static void _stats_walk_start(struct dm_stats *dms, uint64_t *cur_r, uint64_t *cur_a)
{
if (!dms || !dms->regions)
return;
@@ -565,16 +563,15 @@ void dm_stats_walk_start(struct dm_stats *dms)
void dm_stats_walk_next(struct dm_stats *dms)
{
_stats_walk_next(dms, 0, &dms->cur_region, &dms->cur_area);
return _stats_walk_next(dms, 0, &dms->cur_region, &dms->cur_area);
}
void dm_stats_walk_next_region(struct dm_stats *dms)
{
_stats_walk_next(dms, 1, &dms->cur_region, &dms->cur_area);
return _stats_walk_next(dms, 1, &dms->cur_region, &dms->cur_area);
}
static int _stats_walk_end(const struct dm_stats *dms,
uint64_t *cur_r, uint64_t *cur_a)
static int _stats_walk_end(struct dm_stats *dms, uint64_t *cur_r, uint64_t *cur_a)
{
struct dm_stats_region *region = NULL;
int end = 0;
@@ -595,19 +592,18 @@ int dm_stats_walk_end(struct dm_stats *dms)
return _stats_walk_end(dms, &dms->cur_region, &dms->cur_area);
}
uint64_t dm_stats_get_region_nr_areas(const struct dm_stats *dms,
uint64_t region_id)
uint64_t dm_stats_nr_areas_region(struct dm_stats *dms, uint64_t region_id)
{
struct dm_stats_region *region = &dms->regions[region_id];
return _nr_areas_region(region);
}
uint64_t dm_stats_get_current_nr_areas(const struct dm_stats *dms)
uint64_t dm_stats_nr_areas_current(struct dm_stats *dms)
{
return dm_stats_get_region_nr_areas(dms, dms->cur_region);
return dm_stats_nr_areas_region(dms, dms->cur_region);
}
uint64_t dm_stats_get_nr_areas(const struct dm_stats *dms)
uint64_t dm_stats_nr_areas(struct dm_stats *dms)
{
uint64_t nr_areas = 0;
/* use a separate cursor */
@@ -615,15 +611,15 @@ uint64_t dm_stats_get_nr_areas(const struct dm_stats *dms)
_stats_walk_start(dms, &cur_region, &cur_area);
do {
nr_areas += dm_stats_get_current_nr_areas(dms);
nr_areas += dm_stats_nr_areas_current(dms);
_stats_walk_next(dms, 1, &cur_region, &cur_area);
} while (!_stats_walk_end(dms, &cur_region, &cur_area));
return nr_areas;
}
int dm_stats_create_region(struct dm_stats *dms, uint64_t *region_id,
uint64_t start, uint64_t len, int64_t step,
const char *program_id, const char *aux_data)
uint64_t start, uint64_t len, int64_t step,
const char *program_id, const char *aux_data)
{
struct dm_task *dmt = NULL;
char msg[1024], range[64];
@@ -724,8 +720,8 @@ out:
}
static struct dm_task *_stats_print_region(struct dm_stats *dms,
uint64_t region_id, unsigned start_line,
unsigned num_lines, unsigned clear)
uint64_t region_id, int start_line,
int num_lines, int clear)
{
struct dm_task *dmt = NULL;
/* @stats_print[_clear] <region_id> [<start_line> <num_lines>] */
@@ -786,7 +782,7 @@ void dm_stats_buffer_destroy(struct dm_stats *dms, char *buffer)
dm_pool_free(dms->mem, buffer);
}
uint64_t dm_stats_get_nr_regions(const struct dm_stats *dms)
int dm_stats_nr_regions(struct dm_stats *dms)
{
if (!dms || !dms->regions)
return 0;
@@ -796,7 +792,7 @@ uint64_t dm_stats_get_nr_regions(const struct dm_stats *dms)
/**
* Test whether region_id is present in this set of stats data
*/
int dm_stats_region_present(const struct dm_stats *dms, uint64_t region_id)
int dm_stats_region_present(struct dm_stats *dms, uint64_t region_id)
{
if (!dms->regions)
return 0;
@@ -807,8 +803,8 @@ int dm_stats_region_present(const struct dm_stats *dms, uint64_t region_id)
return _stats_region_present(&dms->regions[region_id]);
}
static int _dm_stats_populate_region(struct dm_stats *dms, uint64_t region_id,
const char *resp)
int dm_stats_populate_region(struct dm_stats *dms, uint64_t region_id,
const char *resp)
{
struct dm_stats_region *region = &dms->regions[region_id];
@@ -857,7 +853,7 @@ int dm_stats_populate(struct dm_stats *dms, const char *program_id,
goto_out;
resp = dm_task_get_message_response(dmt);
if (!_dm_stats_populate_region(dms, region_id, resp)) {
if (!dm_stats_populate_region(dms, region_id, resp)) {
dm_task_destroy(dmt);
goto_out;
}
@@ -871,7 +867,6 @@ int dm_stats_populate(struct dm_stats *dms, const char *program_id,
out:
_stats_regions_destroy(dms);
dms->regions = NULL;
return 0;
}
@@ -896,8 +891,8 @@ void dm_stats_destroy(struct dm_stats *dms)
* Where the two integer arguments are the region_id and area_id
* respectively.
*/
#define MK_STATS_GET_COUNTER_FN(counter) \
uint64_t dm_stats_get_ ## counter(const struct dm_stats *dms, \
#define __mk_stats_get_counter_fn(counter) \
uint64_t dm_stats_get_ ## counter(struct dm_stats *dms, \
uint64_t region_id, uint64_t area_id) \
{ \
region_id = (region_id == DM_STATS_REGION_CURRENT) \
@@ -907,27 +902,28 @@ uint64_t dm_stats_get_ ## counter(const struct dm_stats *dms, \
return dms->regions[region_id].counters[area_id].counter; \
}
MK_STATS_GET_COUNTER_FN(reads)
MK_STATS_GET_COUNTER_FN(reads_merged)
MK_STATS_GET_COUNTER_FN(read_sectors)
MK_STATS_GET_COUNTER_FN(read_nsecs)
MK_STATS_GET_COUNTER_FN(writes)
MK_STATS_GET_COUNTER_FN(writes_merged)
MK_STATS_GET_COUNTER_FN(write_sectors)
MK_STATS_GET_COUNTER_FN(write_nsecs)
MK_STATS_GET_COUNTER_FN(io_in_progress)
MK_STATS_GET_COUNTER_FN(io_nsecs)
MK_STATS_GET_COUNTER_FN(weighted_io_nsecs)
MK_STATS_GET_COUNTER_FN(total_read_nsecs)
MK_STATS_GET_COUNTER_FN(total_write_nsecs)
#undef MK_STATS_GET_COUNTER_FN
__mk_stats_get_counter_fn(reads)
__mk_stats_get_counter_fn(reads_merged)
__mk_stats_get_counter_fn(read_sectors)
__mk_stats_get_counter_fn(read_nsecs)
__mk_stats_get_counter_fn(writes)
__mk_stats_get_counter_fn(writes_merged)
__mk_stats_get_counter_fn(write_sectors)
__mk_stats_get_counter_fn(write_nsecs)
__mk_stats_get_counter_fn(io_in_progress)
__mk_stats_get_counter_fn(io_nsecs)
__mk_stats_get_counter_fn(weighted_io_nsecs)
__mk_stats_get_counter_fn(total_read_nsecs)
__mk_stats_get_counter_fn(total_write_nsecs)
int dm_stats_get_rd_merges_per_sec(const struct dm_stats *dms, double *rrqm,
#undef __dm_stats_get_counter_fn
int dm_stats_get_rd_merges_per_sec(struct dm_stats *dms, double *rrqm,
uint64_t region_id, uint64_t area_id)
{
struct dm_stats_counters *c;
if (!dms->interval_ns)
if (!dms->interval)
return_0;
region_id = (region_id == DM_STATS_REGION_CURRENT)
@@ -936,16 +932,16 @@ int dm_stats_get_rd_merges_per_sec(const struct dm_stats *dms, double *rrqm,
? dms->cur_area : area_id ;
c = &(dms->regions[region_id].counters[area_id]);
*rrqm = ((double) c->reads_merged) / (double) dms->interval_ns;
*rrqm = ((double) c->reads_merged) / dms->interval;
return 1;
}
int dm_stats_get_wr_merges_per_sec(const struct dm_stats *dms, double *wrqm,
int dm_stats_get_wr_merges_per_sec(struct dm_stats *dms, double *wrqm,
uint64_t region_id, uint64_t area_id)
{
struct dm_stats_counters *c;
if (!dms->interval_ns)
if (!dms->interval)
return_0;
region_id = (region_id == DM_STATS_REGION_CURRENT)
@@ -954,16 +950,16 @@ int dm_stats_get_wr_merges_per_sec(const struct dm_stats *dms, double *wrqm,
? dms->cur_area : area_id ;
c = &(dms->regions[region_id].counters[area_id]);
*wrqm = ((double) c->writes_merged) / (double) dms->interval_ns;
*wrqm = ((double) c->writes_merged) / dms->interval;
return 1;
}
int dm_stats_get_reads_per_sec(const struct dm_stats *dms, double *rd_s,
int dm_stats_get_reads_per_sec(struct dm_stats *dms, double *rd_s,
uint64_t region_id, uint64_t area_id)
{
struct dm_stats_counters *c;
if (!dms->interval_ns)
if (!dms->interval)
return_0;
region_id = (region_id == DM_STATS_REGION_CURRENT)
@@ -972,16 +968,16 @@ int dm_stats_get_reads_per_sec(const struct dm_stats *dms, double *rd_s,
? dms->cur_area : area_id ;
c = &(dms->regions[region_id].counters[area_id]);
*rd_s = ((double) c->reads * NSEC_PER_SEC) / (double) dms->interval_ns;
*rd_s = ((double) c->reads * NSEC_PER_SEC) / dms->interval;
return 1;
}
int dm_stats_get_writes_per_sec(const struct dm_stats *dms, double *wr_s,
int dm_stats_get_writes_per_sec(struct dm_stats *dms, double *wr_s,
uint64_t region_id, uint64_t area_id)
{
struct dm_stats_counters *c;
if (!dms->interval_ns)
if (!dms->interval)
return_0;
region_id = (region_id == DM_STATS_REGION_CURRENT)
@@ -990,18 +986,16 @@ int dm_stats_get_writes_per_sec(const struct dm_stats *dms, double *wr_s,
? dms->cur_area : area_id ;
c = &(dms->regions[region_id].counters[area_id]);
*wr_s = ((double) c->writes * (double) NSEC_PER_SEC)
/ (double) dms->interval_ns;
*wr_s = ((double) c->writes * NSEC_PER_SEC) / dms->interval;
return 1;
}
int dm_stats_get_read_sectors_per_sec(const struct dm_stats *dms, double *rsec_s,
int dm_stats_get_read_sectors_per_sec(struct dm_stats *dms, double *rsec_s,
uint64_t region_id, uint64_t area_id)
{
struct dm_stats_counters *c;
if (!dms->interval_ns)
if (!dms->interval)
return_0;
region_id = (region_id == DM_STATS_REGION_CURRENT)
@@ -1010,18 +1004,16 @@ int dm_stats_get_read_sectors_per_sec(const struct dm_stats *dms, double *rsec_s
? dms->cur_area : area_id ;
c = &(dms->regions[region_id].counters[area_id]);
*rsec_s = ((double) c->read_sectors * (double) NSEC_PER_SEC)
/ (double) dms->interval_ns;
*rsec_s = ((double) c->read_sectors * NSEC_PER_SEC) / dms->interval;
return 1;
}
int dm_stats_get_write_sectors_per_sec(const struct dm_stats *dms, double *wsec_s,
int dm_stats_get_write_sectors_per_sec(struct dm_stats *dms, double *wsec_s,
uint64_t region_id, uint64_t area_id)
{
struct dm_stats_counters *c;
if (!dms->interval_ns)
if (!dms->interval)
return_0;
region_id = (region_id == DM_STATS_REGION_CURRENT)
@@ -1030,18 +1022,17 @@ int dm_stats_get_write_sectors_per_sec(const struct dm_stats *dms, double *wsec_
? dms->cur_area : area_id ;
c = &(dms->regions[region_id].counters[area_id]);
*wsec_s = ((double) c->write_sectors * (double) NSEC_PER_SEC)
/ (double) dms->interval_ns;
*wsec_s = ((double) c->write_sectors * NSEC_PER_SEC) / dms->interval;
return 1;
}
int dm_stats_get_average_request_size(const struct dm_stats *dms, double *arqsz,
int dm_stats_get_average_request_size(struct dm_stats *dms, double *arqsz,
uint64_t region_id, uint64_t area_id)
{
struct dm_stats_counters *c;
uint64_t nr_ios, nr_sectors;
if (!dms->interval_ns)
if (!dms->interval)
return_0;
*arqsz = 0.0;
@@ -1055,17 +1046,17 @@ int dm_stats_get_average_request_size(const struct dm_stats *dms, double *arqsz,
nr_ios = c->reads + c->writes;
nr_sectors = c->read_sectors + c->write_sectors;
if (nr_ios)
*arqsz = (double) nr_sectors / (double) nr_ios;
*arqsz = nr_sectors / nr_ios;
return 1;
}
int dm_stats_get_average_queue_size(const struct dm_stats *dms, double *qusz,
int dm_stats_get_average_queue_size(struct dm_stats *dms, double *qusz,
uint64_t region_id, uint64_t area_id)
{
struct dm_stats_counters *c;
uint64_t io_ticks;
if (!dms->interval_ns)
if (!dms->interval)
return_0;
*qusz = 0.0;
@@ -1078,17 +1069,17 @@ int dm_stats_get_average_queue_size(const struct dm_stats *dms, double *qusz,
c = &(dms->regions[region_id].counters[area_id]);
io_ticks = c->weighted_io_nsecs;
if (io_ticks)
*qusz = (double) io_ticks / (double) dms->interval_ns;
*qusz = io_ticks / dms->interval;
return 1;
}
int dm_stats_get_average_wait_time(const struct dm_stats *dms, double *await,
int dm_stats_get_average_wait_time(struct dm_stats *dms, double *await,
uint64_t region_id, uint64_t area_id)
{
struct dm_stats_counters *c;
uint64_t io_ticks, nr_ios;
if (!dms->interval_ns)
if (!dms->interval)
return_0;
*await = 0.0;
@@ -1102,18 +1093,17 @@ int dm_stats_get_average_wait_time(const struct dm_stats *dms, double *await,
io_ticks = c->read_nsecs + c->write_nsecs;
nr_ios = c->reads + c->writes;
if (nr_ios)
*await = (double) io_ticks / (double) nr_ios;
*await = io_ticks / nr_ios;
return 1;
}
int dm_stats_get_average_rd_wait_time(const struct dm_stats *dms,
double *await, uint64_t region_id,
uint64_t area_id)
int dm_stats_get_average_rd_wait_time(struct dm_stats *dms, double *await,
uint64_t region_id, uint64_t area_id)
{
struct dm_stats_counters *c;
uint64_t rd_io_ticks, nr_rd_ios;
if (!dms->interval_ns)
if (!dms->interval)
return_0;
*await = 0.0;
@@ -1127,18 +1117,17 @@ int dm_stats_get_average_rd_wait_time(const struct dm_stats *dms,
rd_io_ticks = c->read_nsecs;
nr_rd_ios = c->reads;
if (rd_io_ticks)
*await = (double) rd_io_ticks / (double) nr_rd_ios;
*await = rd_io_ticks / nr_rd_ios;
return 1;
}
int dm_stats_get_average_wr_wait_time(const struct dm_stats *dms,
double *await, uint64_t region_id,
uint64_t area_id)
int dm_stats_get_average_wr_wait_time(struct dm_stats *dms, double *await,
uint64_t region_id, uint64_t area_id)
{
struct dm_stats_counters *c;
uint64_t wr_io_ticks, nr_wr_ios;
if (!dms->interval_ns)
if (!dms->interval)
return_0;
*await = 0.0;
@@ -1152,11 +1141,11 @@ int dm_stats_get_average_wr_wait_time(const struct dm_stats *dms,
wr_io_ticks = c->write_nsecs;
nr_wr_ios = c->writes;
if (wr_io_ticks && nr_wr_ios)
*await = (double) wr_io_ticks / (double) nr_wr_ios;
*await = wr_io_ticks / nr_wr_ios;
return 1;
}
int dm_stats_get_service_time(const struct dm_stats *dms, double *svctm,
int dm_stats_get_service_time(struct dm_stats *dms, double *svctm,
uint64_t region_id, uint64_t area_id)
{
dm_percent_t util;
@@ -1178,12 +1167,12 @@ int dm_stats_get_service_time(const struct dm_stats *dms, double *svctm,
return 1;
}
int dm_stats_get_throughput(const struct dm_stats *dms, double *tput,
int dm_stats_get_throughput(struct dm_stats *dms, double *tput,
uint64_t region_id, uint64_t area_id)
{
struct dm_stats_counters *c;
if (!dms->interval_ns)
if (!dms->interval)
return_0;
region_id = (region_id == DM_STATS_REGION_CURRENT)
@@ -1194,17 +1183,17 @@ int dm_stats_get_throughput(const struct dm_stats *dms, double *tput,
c = &(dms->regions[region_id].counters[area_id]);
*tput = (( NSEC_PER_SEC * ((double) c->reads + (double) c->writes))
/ (double) (dms->interval_ns));
/ (double) (dms->interval));
return 1;
}
int dm_stats_get_utilization(const struct dm_stats *dms, dm_percent_t *util,
int dm_stats_get_utilization(struct dm_stats *dms, dm_percent_t *util,
uint64_t region_id, uint64_t area_id)
{
struct dm_stats_counters *c;
uint64_t io_nsecs;
if (!dms->interval_ns)
if (!dms->interval)
return_0;
region_id = (region_id == DM_STATS_REGION_CURRENT)
@@ -1215,42 +1204,42 @@ int dm_stats_get_utilization(const struct dm_stats *dms, dm_percent_t *util,
c = &(dms->regions[region_id].counters[area_id]);
/**
* If io_nsec > interval_ns there is something wrong with the clock
* If io_nsec > interval there is something wrong with the clock
* for the last interval; do not allow a value > 100% utilization
* to be passed to a dm_make_percent() call. We expect to see these
* at startup if counters have not been cleared before the first read.
*/
io_nsecs = (c->io_nsecs <= dms->interval_ns) ? c->io_nsecs : dms->interval_ns;
*util = dm_make_percent(io_nsecs, dms->interval_ns);
io_nsecs = (c->io_nsecs <= dms->interval) ? c->io_nsecs : dms->interval;
*util = dm_make_percent(io_nsecs, dms->interval);
return 1;
}
void dm_stats_set_sampling_interval_ms(struct dm_stats *dms, uint64_t interval_ms)
void dm_stats_set_interval_ms(struct dm_stats *dms, uint64_t interval_ms)
{
/* All times use nsecs internally. */
dms->interval_ns = interval_ms * NSEC_PER_MSEC;
dms->interval = interval_ms * NSEC_PER_MSEC;
}
void dm_stats_set_sampling_interval_ns(struct dm_stats *dms, uint64_t interval_ns)
void dm_stats_set_interval(struct dm_stats *dms, uint64_t interval)
{
dms->interval_ns = interval_ns;
dms->interval = interval;
}
uint64_t dm_stats_get_sampling_interval_ms(const struct dm_stats *dms)
uint64_t dm_stats_get_interval_ms(struct dm_stats *dms)
{
/* All times use nsecs internally. */
return (dms->interval_ns / NSEC_PER_MSEC);
return (dms->interval / NSEC_PER_MSEC);
}
uint64_t dm_stats_get_sampling_interval_ns(const struct dm_stats *dms)
uint64_t dm_stats_get_interval(struct dm_stats *dms)
{
/* All times use nsecs internally. */
return (dms->interval_ns);
return (dms->interval);
}
int dm_stats_set_program_id(struct dm_stats *dms, int allow_empty,
const char *program_id)
const char *program_id)
{
if (!allow_empty && (!program_id || !strlen(program_id))) {
log_error("Empty program_id not permitted without "
@@ -1270,17 +1259,17 @@ int dm_stats_set_program_id(struct dm_stats *dms, int allow_empty,
return 1;
}
uint64_t dm_stats_get_current_region(const struct dm_stats *dms)
uint64_t dm_stats_get_current_region(struct dm_stats *dms)
{
return dms->cur_region;
}
uint64_t dm_stats_get_current_area(const struct dm_stats *dms)
uint64_t dm_stats_get_current_area(struct dm_stats *dms)
{
return dms->cur_area;
}
uint64_t dm_stats_get_region_start(const struct dm_stats *dms, uint64_t *start,
int dm_stats_get_region_start(struct dm_stats *dms, uint64_t *start,
uint64_t region_id)
{
if (!dms || !dms->regions)
@@ -1289,7 +1278,7 @@ uint64_t dm_stats_get_region_start(const struct dm_stats *dms, uint64_t *start,
return 1;
}
uint64_t dm_stats_get_region_len(const struct dm_stats *dms, uint64_t *len,
int dm_stats_get_region_len(struct dm_stats *dms, uint64_t *len,
uint64_t region_id)
{
if (!dms || !dms->regions)
@@ -1298,7 +1287,7 @@ uint64_t dm_stats_get_region_len(const struct dm_stats *dms, uint64_t *len,
return 1;
}
uint64_t dm_stats_get_region_area_len(const struct dm_stats *dms, uint64_t *step,
int dm_stats_get_region_area_len(struct dm_stats *dms, uint64_t *step,
uint64_t region_id)
{
if (!dms || !dms->regions)
@@ -1307,26 +1296,23 @@ uint64_t dm_stats_get_region_area_len(const struct dm_stats *dms, uint64_t *step
return 1;
}
uint64_t dm_stats_get_current_region_start(const struct dm_stats *dms,
uint64_t *start)
int dm_stats_get_current_region_start(struct dm_stats *dms, uint64_t *start)
{
return dm_stats_get_region_start(dms, start, dms->cur_region);
}
uint64_t dm_stats_get_current_region_len(const struct dm_stats *dms,
uint64_t *len)
int dm_stats_get_current_region_len(struct dm_stats *dms, uint64_t *len)
{
return dm_stats_get_region_len(dms, len, dms->cur_region);
}
uint64_t dm_stats_get_current_region_area_len(const struct dm_stats *dms,
uint64_t *step)
int dm_stats_get_current_region_area_len(struct dm_stats *dms, uint64_t *step)
{
return dm_stats_get_region_area_len(dms, step, dms->cur_region);
}
uint64_t dm_stats_get_area_start(const struct dm_stats *dms, uint64_t *start,
uint64_t region_id, uint64_t area_id)
int dm_stats_get_area_start(struct dm_stats *dms, uint64_t *start,
uint64_t region_id, uint64_t area_id)
{
if (!dms || !dms->regions)
return_0;
@@ -1334,39 +1320,46 @@ uint64_t dm_stats_get_area_start(const struct dm_stats *dms, uint64_t *start,
return 1;
}
uint64_t dm_stats_get_current_area_start(const struct dm_stats *dms,
uint64_t *start)
int dm_stats_get_area_len(struct dm_stats *dms, uint64_t *len,
uint64_t region_id, uint64_t area_id)
{
if (!dms || !dms->regions)
return_0;
*len = dms->regions[region_id].step;
return 1;
}
int dm_stats_get_current_area_start(struct dm_stats *dms, uint64_t *start)
{
return dm_stats_get_area_start(dms, start,
dms->cur_region, dms->cur_area);
}
uint64_t dm_stats_get_current_area_len(const struct dm_stats *dms,
uint64_t *len)
int dm_stats_get_current_area_len(struct dm_stats *dms, uint64_t *len)
{
return dm_stats_get_region_area_len(dms, len, dms->cur_region);
return dm_stats_get_area_len(dms, len, dms->cur_region, dms->cur_area);
}
const char *dm_stats_get_region_program_id(const struct dm_stats *dms,
const char *dm_stats_get_region_program_id(struct dm_stats *dms,
uint64_t region_id)
{
const char *program_id = dms->regions[region_id].program_id;
return (program_id) ? program_id : "";
}
const char *dm_stats_get_region_aux_data(const struct dm_stats *dms,
const char *dm_stats_get_region_aux_data(struct dm_stats *dms,
uint64_t region_id)
{
const char *aux_data = dms->regions[region_id].aux_data;
return (aux_data) ? aux_data : "" ;
}
const char *dm_stats_get_current_region_program_id(const struct dm_stats *dms)
const char *dm_stats_get_current_region_program_id(struct dm_stats *dms)
{
return dm_stats_get_region_program_id(dms, dms->cur_region);
}
const char *dm_stats_get_current_region_aux_data(const struct dm_stats *dms)
const char *dm_stats_get_current_region_aux_data(struct dm_stats *dms)
{
return dm_stats_get_region_aux_data(dms, dms->cur_region);
}

View File

@@ -54,7 +54,7 @@ struct dm_timestamp *dm_timestamp_alloc(void)
{
struct dm_timestamp *ts = NULL;
if (!(ts = dm_zalloc(sizeof(*ts))))
if (!(ts = dm_malloc(sizeof(*ts))))
stack;
return ts;
@@ -67,8 +67,6 @@ int dm_timestamp_get(struct dm_timestamp *ts)
if (clock_gettime(CLOCK_MONOTONIC, &ts->t)) {
log_sys_error("clock_gettime", "get_timestamp");
ts->t.tv_sec = 0;
ts->t.tv_nsec = 0;
return 0;
}
@@ -115,8 +113,6 @@ int dm_timestamp_get(struct dm_timestamp *ts)
if (gettimeofday(&ts->t, NULL)) {
log_sys_error("gettimeofday", "get_timestamp");
ts->t.tv_sec = 0;
ts->t.tv_usec = 0;
return 0;
}
@@ -168,11 +164,6 @@ uint64_t dm_timestamp_delta(struct dm_timestamp *ts1, struct dm_timestamp *ts2)
return t2 - t1;
}
void dm_timestamp_copy(struct dm_timestamp *ts_new, struct dm_timestamp *ts_old)
{
*ts_new = *ts_old;
}
void dm_timestamp_destroy(struct dm_timestamp *ts)
{
dm_free(ts);

View File

@@ -32,7 +32,6 @@ dmsetup \(em low level logical volume management
.br
.B dmsetup info
.BR \-c | \-C | \-\-columns
.RB [ \-\-nameprefixes ]
.RB [ \-\-noheadings ]
.RB [ \-\-separator
.IR separator ]
@@ -43,10 +42,6 @@ dmsetup \(em low level logical volume management
.IR sort_fields ]
.RB [ \-S | \-\-select
.IR Selection ]
.RB [ \-\-interval
.IR seconds ]
.RB [ \-\-count
.IR count ]
.RI [ device_name ]
.RE
.br
@@ -192,10 +187,6 @@ In some cases these checks may slow down operations noticeably.
.BR \-c | \-C | \-\-columns
Display output in columns rather than as Field: Value lines.
.TP
.B \-\-count \fIcount
Specify the number of times to repeat a report. Set this to zero
continue until interrupted. The default interval is one second.
.TP
.BR \-h | \-\-help
Outputs a summary of the commands available, optionally including
the list of report fields (synonym with \fBhelp\fP command).
@@ -205,12 +196,6 @@ When returning any table information from the kernel report on the
inactive table instead of the live table.
Requires kernel driver version 4.16.0 or above.
.TP
.B \-\-interval \fIseconds
Specify the interval in seconds between successive iterations for
repeating reports. If \-\-interval is specified but \-\-count is not,
reports will continue to repeat until interrupted.
The default interval is one second.
.TP
.IR \fB\-\-manglename \ { none | hex | auto }
Mangle any character not on a whitelist using mangling_mode when
processing device-mapper device names and UUIDs. The names and UUIDs
@@ -237,10 +222,6 @@ Specify the minor number.
.BR \-n | \-\-notable
When creating a device, don't load any table.
.TP
.BR \-\-nameprefixes
Add a "DM_" prefix plus the field name to the output. Useful with --noheadings to produce a list of
field=value pairs that can be used to set environment variables (for example, in udev(7) rules).
.TP
.BR \-\-noheadings
Suppress the headings line when using columnar output.
.TP
@@ -374,10 +355,6 @@ Outputs some brief information about the device in the form:
.IR fields ]
.RB [ \-O | \-\-sort
.IR sort_fields ]
.RB [ \-\-interval
.IR seconds ]
.RB [ \-\-count
.IR count ]
.RI [ device_name ]
.br
Output you can customise.

View File

@@ -27,7 +27,6 @@ dmstats \(em device-mapper statistics management
.br
.B dmstats create
.I device_name
.RB [ \-\-alldevices ]
.RB [[ \-\-areas
.IR nr_areas ]
.RB |[ \-\-areasize
@@ -36,7 +35,7 @@ dmstats \(em device-mapper statistics management
.IR start_sector ]
.RB [ \-\-length
.IR length ]
.RB |[ \-\-segments ]]
.RB |[ \-\-targets ]]
.RB [ \-\-auxdata
.IR data ]
.RB [ \-\-programid
@@ -44,7 +43,7 @@ dmstats \(em device-mapper statistics management
.br
.B dmstats delete
.I device_name
.RB [ \-\-alldevices ]
.RB [ \-\-force ]
.RB [ \-\-allregions
.RB | \-\-regionid
.IR id ]
@@ -80,11 +79,14 @@ dmstats \(em device-mapper statistics management
.IR seconds ]
.RB [ \-\-count
.IR count ]
.RB [ \-\-timestamps ]
.RB [ \-\-units
.IR units ]
.RB [ \-\-allprograms ]
.RB [ \-\-programid
.IR id ]
.RB [ \-\-headers
.IR headers ]
.RB [ \-\-regionid
.IR id ]
.RB [ \-O | \-\-sort
@@ -116,14 +118,10 @@ when run as 'dmsetup stats'.
When no device argument is given dmstats will by default operate on all
device-mapper devices present. The \fBcreate\fP and \fBdelete\fP
commands require the use of \fB--alldevices\fP when used in this way.
commands require the use of \fB--force\fP when used in this way.
.SH OPTIONS
.TP
.B \-\-alldevices
If no device arguments are given allow operation on all devices when
creating or deleting regions.
.TP
.B \-\-allprograms
Include regions from all program IDs for list and report operations.
.TP
@@ -145,12 +143,19 @@ instead of 1024.
Specify auxilliary data (a string) to be stored with a new region.
.TP
.B \-\-clear
When printing statistics counters, also atomically reset them to zero.
When printing statistics counters also atomically reset them to zero.
.TP
.B \-\-count \fIcount
Specify the iteration count for repeating reports. If the count
argument is zero reports will continue to repeat until interrupted.
.TP
.B \-\-headers \fIheader_list
Specify which headers to display.
.TP
.B \-\-interval \fIinterval
Specify the interval, in seconds, between successive iterations for
repeating reports.
.TP
.B \-\-interval \fIseconds
Specify the interval in seconds between successive iterations for
repeating reports. If \-\-interval is specified but \-\-count is not,
@@ -185,10 +190,10 @@ string is stored with the region. Subsequent operations may supply a
program ID in order to select only regions with a matching value. The
default program ID for dmstats-managed regions is "dmstats".
.TP
.BR \-S | \-\-select \ \fIselection
Display only rows that match selection criteria. All rows with the
.BR \-S | \-\-select \ \fISelection
Display only rows that match Selection criteria. All rows with the
additional "selected" column (-o selected) showing 1 if the row matches
the selection and 0 otherwise. The selection criteria are defined by
the Selection and 0 otherwise. The Selection criteria are defined by
specifying column names and their valid values while making use of
supported comparison operators.
.TP
@@ -199,11 +204,14 @@ optional suffix selects units of bBsSkKmMgGtTpPeE: (b)ytes,
(p)etabytes, (e)xabytes. Capitalise to use multiples of 1000 (S.I.)
instead of 1024.
.TP
.B \-\-segments
.B \-\-targets
Create a new statistics region for each target contained in the target
device. This causes a separate region to be allocated for each segment
of the device.
.TP
.B \-\-timestamps
Output a timestamp before each statistics report.
.TP
.BR \-\-units \ hHbBsSkKmMgGtTpPeE
Set the display units for report output. All sizes are output in these
units: (h)uman-readable, (b)ytes, (s)ectors, (k)ilobytes, (m)egabytes,
@@ -242,7 +250,7 @@ regions (with the exception of in-flight IO counters).
.IR start_sector ]
.RB [ \-\-length
.IR length ]
.RB |[ \-\-segments ]]
.RB |[ \-\-targets ]]
.RB [ \-\-auxdata
.IR data ]
.RB [ \-\-programid
@@ -253,7 +261,7 @@ Creates one or more new statistics regions on the specified device(s).
The region will span the entire device unless \fB\-\-start\fP and
\fB\-\-length\fP or \fB\-\-target\fP are given. The \fB\-\-start\fP and
\fB\-\-length\fP options allow a region of arbitrary length to be placed
at an arbitrary offset into the device. The \fB\-\-segments\fP option
at an arbitrary offset into the device. The \fB\-\-targets\fP option
causes a new region to be created for each target in the corresponding
device-mapper device's table.
@@ -272,7 +280,7 @@ stdout.
.TP
.B delete
.I [ device_name ]
.RB [ \-\-alldevices ]
.RB [ \-\-force ]
.RB [ \-\-allregions
.RB | \-\-regionid
.IR id ]
@@ -287,8 +295,7 @@ of subsequent list, print, or report operations.
All regions registered on a device may be removed using
\fB\-\-allregions\fP.
To remove all regions on all devices both \fB--allregions\fP and
\fB\-\-alldevices\fP must be used.
To remove all regions on all devices \fB\-\-force\fP must be used.
.br
.TP
.B help
@@ -327,8 +334,11 @@ present regions.
.RB [ \-\-allprograms ]
.RB [ \-\-interval
.IR seconds ]
.RB [ \-\-headers
.IR headers ]
.RB [ \-\-count
.IR count ]
.RB [ \-\-timestamps ]
.RB [ \-\-units
.IR unit ]
.RB [ \-\-regionid
@@ -349,6 +359,10 @@ one second.
If the \fB\-\-allprograms\fP switch is given, all regions will be
listed, regardless of region program ID values.
A header row is optionally printed before each statistics report is
displayed. The list of headers to include is specified using the
\fB\-\-headers\fP option.
.br
.SH REGIONS AND AREAS
The device-mapper statistics facility allows separate performance
@@ -373,7 +387,7 @@ reference the region in subsequent operations. Region identifiers are
unique within a given device (including across different \fBprogram_id\fP
values).
.br
Depending on the sequence of create and delete operations, gaps may
Depending on the sequence of create and delete operations gaps, may
exist in the sequence of \fBregion_id\fP values for a particular device.
.SH REPORT FIELDS
@@ -410,7 +424,6 @@ Write requests per second.
Sectors read per second.
.HP
.B wsec
.br
Sectors written per second.
.HP
.B arqsz
@@ -438,7 +451,6 @@ The average wait time for write requests.
The device throughput in requests per second.
.HP
.B svctm
.br
The average service time (in milliseconds) for I/O requests that
were issued to the device.
.HP
@@ -451,7 +463,7 @@ when this value is close to 100%.
.SS Region and area meta fields
Meta fields provide information about the region or area that the
statistics values relate to. This includes the region and area
identifier, start, length, and counts, as well as the program ID and
identifier, start, length, and counts as well as the program ID and
auxiliary data values.
.br
.HP
@@ -490,7 +502,6 @@ The area start sector in units of 512 byte sectors.
The length of the area in units of 512 byte sectors.
.HP
.B area_count
.br
The number of areas in this region.
.HP
.B program_id
@@ -501,20 +512,6 @@ The program ID value associated with this region.
.br
The auxiliary data value associated with this region.
.br
.HP
.B interval_ns
.br
The estimated interval over which the current counter values have
accumulated. The vaulue is reported as an interger expressed in units
of nanoseconds.
.br
.HP
.B interval
.br
The estimated interval over which the current counter values have
accumulated. The value is reported as a real number in units of
seconds.
.br
.SS Basic counters
Basic counters provide access to the raw counter data from the kernel,
allowing further processing to be carried out by another program.
@@ -572,18 +569,18 @@ The number of nanoseconds spent reading and writing.
.B weighted_io_nsecs
.br
This field is incremented at each I/O start, I/O completion, I/O merge,
or read of these stats by the number of I/Os in progress multiplied by
the number of milliseconds spent doing I/O since the last update of this
or read of these stats by the number of I/Os in progress times the
number of milliseconds spent doing I/O since the last update of this
field. This can provide an easy measure of both I/O completion time and
the backlog that may be accumulating.
.br
.br
.P
.SH EXAMPLES
Create a whole-device region with one area on vg00/lvol1
Create a whole-device region with one area on vg_hex-lv_root
.br
.br
# dmstats create vg00/lvol1
# dmstats create vg_hex-lv_root
.br
Created region: 0
.br
@@ -621,31 +618,31 @@ Created region: 0
Delete all regions on all devices
.br
.br
# dmstats delete --alldevices --allregions
# dmstats delete --allregions --force
.br
.br
Create a whole-device region with areas 10GiB in size on vg00/lvol1
Create a whole-device region with areas 10GiB in size on vg_hex-lv_root
using dmsetup
.br
.br
# dmsetup stats create --areasize 10G vg00/lvol1
# dmsetup stats create --areasize 10G vg_hex-lv_root
.br
Created region: 1
.br
.br
Create a 1GiB region with 16 areas at the start of vg00/lvol1
Create a 1GiB region with 16 areas at the start of vg_hex-lv_root
.br
# dmstats create --start 0 --len 1G --areas=16 vg00/lvol1
# dmstats create --start 0 --len 1G --areas=16 vg_hex-lv_root
.br
Created region: 2
.br
.br
List the statistics regions registered on vg00/lvol1
List the statistics regions registered on vg_hex-lv_root
.br
# dmstats list vg00/lvol1
# dmstats list vg_hex-lv_root
.br
RegionID RegStart RegLen AreaSize ProgramID AuxData
.br
@@ -657,31 +654,33 @@ RegionID RegStart RegLen AreaSize ProgramID AuxData
.br
.br
Display five statistics reports for vg00/lvol1 at an interval of one second
Display five statistics reports, with timestamps, for vg_hex-lv_root at an interval of one second
.br
.br
# dmstats report --interval 1 --count 5 vg00/lvol1
# dmstats report --time --interval 1 --count 5
.br
21/07/15 21:04:26
.br
Name RgID ArID RRqM/s WRqM/s R/s W/s RSz/s WSz/s AvRqSz QSize SvcTm Util% AWait
.br
vg00-lvol1 0 0 0.00 0.00 8.00 0.00 48.00k 0 6.00k 0.00 5.50 4.40 6.62
vg_hex-lv_root 0 0 0.00 0.00 8.00 0.00 48.00k 0 6.00k 0.00 5.50 4.40 6.62
.br
vg00-lvol1 0 1 0.00 0.00 22.00 0.00 624.00k 0 28.00k 0.00 5.23 11.50 5.36
vg_hex-lv_root 0 1 0.00 0.00 22.00 0.00 624.00k 0 28.00k 0.00 5.23 11.50 5.36
.br
vg00-lvol1 0 2 0.00 0.00 353.00 0.00 1.84m 0 5.00k 0.00 1.34 47.40 1.33
vg_hex-lv_root 0 2 0.00 0.00 353.00 0.00 1.84m 0 5.00k 0.00 1.34 47.40 1.33
.br
vg00-lvol1 0 3 0.00 0.00 73.00 0.00 592.00k 0 8.00k 0.00 2.10 15.30 2.10
vg_hex-lv_root 0 3 0.00 0.00 73.00 0.00 592.00k 0 8.00k 0.00 2.10 15.30 2.10
.br
vg00-lvol1 0 4 0.00 0.00 5.00 0.00 52.00k 0 10.00k 0.00 4.00 2.00 4.00
vg_hex-lv_root 0 4 0.00 0.00 5.00 0.00 52.00k 0 10.00k 0.00 4.00 2.00 4.00
.br
[...]
.br
.br
Create one region for reach target contained in device vg00/lvol1
Create one region for reach target contained in device vg_hex-lv_home
.br
.br
# dmstats create --segments vg00/lvol1
# dmstats create --targets vg_hex-lv_home
.br
Created region: 0
.br

View File

@@ -162,10 +162,6 @@ lvconvert \(em convert a logical volume from linear to mirror or snapshot
.IR ChunkSize [ bBsSkKmMgG ]]
.RB [ \-\-cachemode
.RI { writeback | writethrough }]
.RB [ \-\-cachepolicy
.IR policy ]
.RB [ \-\-cachesettings
.IR key=value ]
.RB [ \-\-poolmetadata
.IR CachePoolMetadataLogicalVolume { Name | Path }
|
@@ -226,21 +222,10 @@ Converts logical volume to a cached LV with the use of cache pool
specified with \fB\-\-cachepool\fP.
For more information on cache pool LVs and cache LVs, see \fBlvmcache\fP(7).
.TP
.B \-\-cachepolicy \fIpolicy
Only applicable to cached LVs; see also \fBlvmcache(7)\fP. Sets
the cache policy. \fImq\fP is the basic policy name. \fIsqm\fP is more advanced
version available in newer kernels.
.TP
.BR \-\-cachepool " " \fICachePoolLV
This argument is necessary when converting a logical volume to a cache LV.
For more information on cache pool LVs and cache LVs, see \fBlvmcache\fP(7).
.TP
.BR \-\-cachesettings " " \fIkey=value
Only applicable to cached LVs; see also \fBlvmcache(7)\fP. Sets
the cache tunable settings. In most use-cases, default values should be adequate.
Special string value \fIdefault\fP switches setting back to its default kernel value
and removes it from the list of settings stored in lvm2 metadata.
.TP
.BR \-m ", " \-\-mirrors " " \fIMirrors
Specifies the degree of the mirror you wish to create.
For example, "\fB\-m 1\fP" would convert the original logical
@@ -511,7 +496,7 @@ See \fBlvmthin\fP(7) for more info about thin provisioning support.
Uncaches \fICacheLogicalVolume\fP.
Before the volume becomes uncached, cache is flushed.
Unlike with \fB\-\-splitcache\fP the cache pool volume is removed.
This option could be seen as an inverse of \fB\-\-cache\fP.
This option could seen as an inverse of \fB\-\-cache\fP.
.SH Examples
Converts the linear logical volume "vg00/lvol1" to a two-way mirror

View File

@@ -13,13 +13,13 @@ lvcreate \- create a logical volume in an existing volume group
.RI { y | n }]
.RB [ \-H | \-\-cache ]
.RB [ \-\-cachemode
.RI { passthrough | writeback | writethrough }]
.RI { writeback | writethrough }]
.RB [ \-\-cachepolicy
.IR policy ]
.RB [ \-\-cachepool
.IR CachePoolLogicalVolume { Name | Path }
.RB [ \-\-cachesettings
.IR key=value ]
.RB [ \-\-cachepool
.IR CachePoolLogicalVolume { Name | Path }
.RB [ \-c | \-\-chunksize
.IR ChunkSize [ bBsSkKmMgG ]]
.RB [ \-\-commandprofile
@@ -188,7 +188,7 @@ See \fBlvmcache\fP(7) for more info about caching support.
Note that the cache segment type requires a dm-cache kernel module version
1.3.0 or greater.
.TP
.IR \fB\-\-cachemode " {" passthrough | writeback | writethrough }
.IR \fB\-\-cachemode " {" writeback | writethrough }
Specifying a cache mode determines when the writes to a cache LV
are considered complete. When \fIwriteback\fP is specified, a write is
considered complete as soon as it is stored in the cache pool LV.
@@ -198,21 +198,10 @@ While \fIwritethrough\fP may be slower for writes, it is more
resilient if something should happen to a device associated with the
cache pool LV.
.TP
.B \-\-cachepolicy \fIpolicy
Only applicable to cached LVs; see also \fBlvmcache(7)\fP. Sets
the cache policy. \fImq\fP is the basic policy name. \fIsqm\fP is more advanced
version available in newer kernels.
.TP
.IR \fB\-\-cachepool " " CachePoolLogicalVolume { Name | Path }
Specifies the name of cache pool volume name. The other way to specify pool name
is to append name to Volume group name argument.
.TP
.BR \-\-cachesettings " " \fIkey=value
Only applicable to cached LVs; see also \fBlvmcache(7)\fP. Sets
the cache tunable settings. In most use-cases, default values should be adequate.
Special string value \fIdefault\fP switches setting back to its default kernel value
and removes it from the list of settings stored in lvm2 metadata.
.TP
.BR \-c ", " \-\-chunksize " " \fIChunkSize [ \fIbBsSkKmMgG ]
Gives the size of chunk for snapshot, cache pool and thin pool logical volumes.
Default unit is in kilobytes.
@@ -262,6 +251,11 @@ Ignore the flag to skip Logical Volumes during activation.
Use \fB\-\-setactivationskip\fP option to set or reset
activation skipping flag persistently for logical volume.
.TP
.BR \-\-cachepolicy " " policy ", " \-\-cachesettings " " key=value
Only applicable to cached LVs; see also \fBlvmcache(7)\fP. Sets
the cache policy and its associated tunable settings. In most use-cases,
default values should be adequate.
.TP
.B \-\-ignoremonitoring
Make no attempt to interact with dmeventd unless \fB\-\-monitor\fP
is specified.

View File

@@ -261,11 +261,6 @@ 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.
.TP
.B \-\-shared
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.
.TP
.B \-\-addtag \fITag
Add the tag \fITag\fP to a PV, VG or LV.
Supply this argument multiple times to add more than one tag at once.

View File

@@ -16,7 +16,7 @@ origin LV to increase speed. The cache metadata LV holds the
accounting information that specifies where data blocks are stored (e.g.
on the origin LV or on the cache data LV). Users should be familiar with
these LVs if they wish to create the best and most robust cached
logical volumes. All of these associated LVs must be in the same VG.
logical volumes.
.SH Cache Terms
.nf

View File

@@ -8,75 +8,75 @@ LVM commands use lvmlockd to coordinate access to shared storage.
.br
When LVM is used on devices shared by multiple hosts, locks will:
\[bu]
.IP \[bu] 2
coordinate reading and writing of LVM metadata
.br
\[bu]
.IP \[bu] 2
validate caching of LVM metadata
.br
\[bu]
.IP \[bu] 2
prevent concurrent activation of logical volumes
.br
.P
lvmlockd uses an external lock manager to perform basic locking.
.br
Lock manager (lock type) options are:
\[bu]
.IP \[bu] 2
sanlock: places locks on disk within LVM storage.
.br
\[bu]
.IP \[bu] 2
dlm: uses network communication and a cluster manager.
.br
.P
.SH OPTIONS
lvmlockd [options]
For default settings, see lvmlockd \-h.
For default settings, see lvmlockd -h.
.B \-\-help | \-h
.B --help | -h
Show this help information.
.B \-\-version | \-V
.B --version | -V
Show version of lvmlockd.
.B \-\-test | \-T
.B --test | -T
Test mode, do not call lock manager.
.B \-\-foreground | \-f
.B --foreground | -f
Don't fork.
.B \-\-daemon\-debug | \-D
.B --daemon-debug | -D
Don't fork and print debugging to stdout.
.B \-\-pid\-file | \-p
.B --pid-file | -p
.I path
Set path to the pid file.
.B \-\-socket\-path | \-s
.B --socket-path | -s
.I path
Set path to the socket to listen on.
.B \-\-syslog\-priority | \-S err|warning|debug
.B --syslog-priority | -S err|warning|debug
Write log messages from this level up to syslog.
.B \-\-gl\-type | \-g sanlock|dlm
Set global lock type to be sanlock or dlm.
.B --gl-type | -g
.I str
Set global lock type to be sanlock|dlm.
.B \-\-host\-id | \-i
.B --host-id | -i
.I num
Set the local sanlock host id.
.B \-\-host\-id\-file | \-F
.B --host-id-file | -F
.I path
A file containing the local sanlock host_id.
.B \-\-sanlock\-timeout | \-o
.B --sanlock-timeout | -o
.I seconds
Override the default sanlock I/O timeout.
.B \-\-adopt | \-A 0|1
.B --adopt | A 0|1
Adopt locks from a previous instance of lvmlockd.
@@ -84,7 +84,7 @@ For default settings, see lvmlockd \-h.
.SS Initial set up
Using LVM with lvmlockd for the first time includes some one\-time set up
Using LVM with lvmlockd for the first time includes some one-time set up
steps:
.SS 1. choose a lock manager
@@ -112,9 +112,9 @@ use_lvmetad = 1
.I sanlock
.br
Assign each host a unique host_id in the range 1\-2000 by setting
Assign each host a unique host_id in the range 1-2000 by setting
.br
/etc/lvm/lvmlocal.conf local/host_id
/etc/lvm/lvmlocal.conf local/host_id = <num>
.SS 3. start lvmlockd
@@ -132,32 +132,32 @@ Follow external clustering documentation when applicable, otherwise:
.br
systemctl start corosync dlm
.SS 5. create VG on shared devices
.SS 5. create VGs on shared devices
vgcreate \-\-shared <vgname> <devices>
vgcreate --shared <vg_name> <devices>
The shared option sets the VG lock type to sanlock or dlm depending on
which lock manager is running. LVM commands will perform locking for the
VG using lvmlockd.
The vgcreate --shared option sets the VG lock type to sanlock or dlm
depending on which lock manager is running. LVM commands will perform
locking for the VG using lvmlockd.
.SS 6. start VG on all hosts
.SS 6. start VGs on all hosts
vgchange \-\-lock\-start
vgchange --lock-start
lvmlockd requires shared VGs to be started before they are used. This is
a lock manager operation to start (join) the VG lockspace, and it may take
some time. Until the start completes, locks for the VG are not available.
LVM commands are allowed to read the VG while start is in progress. (An
init/unit file can also be used to start VGs.)
lvmlockd requires shared VGs to be "started" before they are used. This
is a lock manager operation to start/join the VG lockspace, and it may
take some time. Until the start completes, locks for the VG are not
available. LVM commands are allowed to read the VG while start is in
progress. (A service/init file can be used to start VGs.)
.SS 7. create and activate LVs
Standard lvcreate and lvchange commands are used to create and activate
LVs in a shared VG.
LVs in a lockd VG.
An LV activated exclusively on one host cannot be activated on another.
When multiple hosts need to use the same LV concurrently, the LV can be
activated with a shared lock (see lvchange options \-aey vs \-asy.)
activated with a shared lock (see lvchange options -aey vs -asy.)
(Shared locks are disallowed for certain LV types that cannot be used from
multiple hosts.)
@@ -165,51 +165,43 @@ multiple hosts.)
.SS Normal start up and shut down
After initial set up, start up and shut down include the following general
steps. They can be performed manually or using the system service
steps. They can be performed manually or using the system init/service
manager.
\[bu]
.IP \[bu] 2
start lvmetad
.br
\[bu]
.IP \[bu] 2
start lvmlockd
.br
\[bu]
.IP \[bu] 2
start lock manager
.br
\[bu]
vgchange \-\-lock\-start
.br
\[bu]
.IP \[bu] 2
vgchange --lock-start
.IP \[bu] 2
activate LVs in shared VGs
.br
.P
The shut down sequence is the reverse:
\[bu]
.IP \[bu] 2
deactivate LVs in shared VGs
.br
\[bu]
vgchange \-\-lock\-stop
.br
\[bu]
.IP \[bu] 2
vgchange --lock-stop
.IP \[bu] 2
stop lock manager
.br
\[bu]
.IP \[bu] 2
stop lvmlockd
.br
\[bu]
.IP \[bu] 2
stop lvmetad
.br
.P
.SH TOPICS
.SS VG access control
.SS locking terms
The following terms are used to describe different forms of VG access
control.
The following terms are used to distinguish VGs that require locking from
those that do not.
.I "lockd VG"
@@ -218,19 +210,18 @@ Using it requires lvmlockd. These VGs exist on shared storage that is
visible to multiple hosts. LVM commands use lvmlockd to perform locking
for these VGs when they are used.
If the lock manager for the lock type is not available (e.g. not started
or failed), lvmlockd is unable to acquire locks for LVM commands. LVM
commands that only read the VG will generally be allowed to continue
without locks in this case (with a warning). Commands to modify or
activate the VG will fail without the necessary locks. Maintaining a
properly running lock manager requires knowledge covered in separate
documentation.
If the lock manager for a lock type is not available (e.g. not started or
failed), lvmlockd is not able to acquire locks from it, and LVM commands
are unable to fully use VGs with the given lock type. Commands generally
allow reading VGs in this condition, but changes and activation are not
allowed. Maintaining a properly running lock manager can require
background not covered here.
.I "local VG"
A "local VG" is meant to be used by a single host. It has no lock type or
lock type "none". LVM commands and lvmlockd do not perform locking for
these VGs. A local VG typically exists on local (non\-shared) devices and
these VGs. A local VG typically exists on local (non-shared) devices and
cannot be used concurrently from different hosts.
If a local VG does exist on shared devices, it should be owned by a single
@@ -249,25 +240,21 @@ clvmd for clustering. See below for converting a clvm VG to a lockd VG.
.SS lockd VGs from hosts not using lvmlockd
Only hosts that use lockd VGs should be configured to run lvmlockd.
Only hosts that will use lockd VGs should be configured to run lvmlockd.
However, devices with lockd VGs may be visible from hosts not using
lvmlockd. From a host not using lvmlockd, visible lockd VGs are ignored
in the same way as foreign VGs, i.e. those with a foreign system ID, see
.BR lvmsystemid (7).
The \-\-shared option for reporting and display commands causes lockd VGs
to be displayed on a host not using lvmlockd, like the \-\-foreign option
does for foreign VGs.
The --shared option displays lockd VGs on a host not using lvmlockd, like
the --foreign option does for foreign VGs.
.SS vgcreate comparison
.SS vgcreate differences
The type of VG access control is specified in the vgcreate command.
See
.BR vgcreate (8)
for all vgcreate options.
Forms of the vgcreate command:
.B vgcreate <vgname> <devices>
.B vgcreate <vg_name> <devices>
.IP \[bu] 2
Creates a local VG with the local system ID when neither lvmlockd nor clvm are configured.
@@ -278,12 +265,11 @@ Creates a clvm VG when clvm is configured.
.P
.B vgcreate \-\-shared <vgname> <devices>
.B vgcreate --shared <vg_name> <devices>
.IP \[bu] 2
Requires lvmlockd to be configured and running.
Requires lvmlockd to be configured (use_lvmlockd=1).
.IP \[bu] 2
Creates a lockd VG with lock type sanlock|dlm depending on which lock
manager is running.
Creates a lockd VG with lock type sanlock|dlm depending on which is running.
.IP \[bu] 2
LVM commands request locks from lvmlockd to use the VG.
.IP \[bu] 2
@@ -291,9 +277,9 @@ lvmlockd obtains locks from the selected lock manager.
.P
.B vgcreate \-c|\-\-clustered y <vgname> <devices>
.B vgcreate -c|--clustered y <vg_name> <devices>
.IP \[bu] 2
Requires clvm to be configured and running.
Requires clvm to be configured (locking_type=3).
.IP \[bu] 2
Creates a clvm VG with the "clustered" flag.
.IP \[bu] 2
@@ -303,79 +289,62 @@ LVM commands request locks from clvmd to use the VG.
.SS using lockd VGs
There are some special considerations to be aware of when using lockd VGs.
When use_lvmlockd is first enabled, and before the first lockd VG is
created, no global lock will exist. In this initial state, LVM commands
try and fail to acquire the global lock, producing a warning, and some
commands are disallowed. Once the first lockd VG is created, the global
lock will be available, and LVM will be fully operational.
created, no global lock will exist, and LVM commands will try and fail to
acquire it. LVM commands will report a warning until the first lockd VG
is created which will create the global lock. Before the global lock
exists, VGs can still be read, but commands that require the global lock
exclusively will fail.
When a new lockd VG is created, its lockspace is automatically started on
the host that creates it. Other hosts need to run 'vgchange
\-\-lock\-start' to start the new VG before they can use it.
the host that creates the VG. Other hosts will need to run 'vgchange
--lock-start' to start the new VG before they can use it.
From the 'vgs' command, lockd VGs are indicated by "s" (for shared) in the
sixth attr field. The specific lock type and lock args for a lockd VG can
be displayed with 'vgs \-o+locktype,lockargs'.
lockd VGs need to be "started" and "stopped", unlike other types of VGs.
See the following section for a full description of starting and stopping.
be displayed with 'vgs -o+locktype,lockargs'.
.SS starting and stopping VGs
Starting a lockd VG (vgchange \-\-lock\-start) causes the lock manager to
start (join) the lockspace for the VG on the host where it is run. This
makes locks for the VG available to LVM commands on the host. Before a VG
is started, only LVM commands that read/display the VG without locks are
allowed.
Starting a lockd VG (vgchange --lock-start) causes the lock manager to
start or join the lockspace for the VG. This makes locks for the VG
accessible to the host. Stopping the VG leaves the lockspace and makes
locks for the VG inaccessible to the host.
Stopping a lockd VG (vgchange \-\-lock\-stop) causes the lock manager to
stop (leave) the lockspace for the VG on the host where it is run. This
makes locks for the VG inaccessible to the host. A VG cannot be stopped
while it has active LVs.
Lockspaces should be started as early as possible because starting
(joining) a lockspace can take a long time (potentially minutes after a
host failure when using sanlock.) A VG can be started after all the
following are true:
When using the lock type sanlock, starting a VG can take a long time
(potentially minutes if the host was previously shut down without cleanly
stopping the VG.)
A lockd VG can be started after all the following are true:
.br
\[bu]
lvmlockd is running
.br
\[bu]
the lock manager is running
.br
\[bu]
the VG is visible to the system
.br
A lockd VG can be stopped if all LVs are deactivated.
.nf
- lvmlockd is running
- lock manager is running
- VG is visible to the system
.fi
All lockd VGs can be started/stopped using:
.br
vgchange \-\-lock-start
vgchange --lock-start
.br
vgchange \-\-lock-stop
vgchange --lock-stop
Individual VGs can be started/stopped using:
.br
vgchange \-\-lock\-start <vgname> ...
vgchange --lock-start <vg_name> ...
.br
vgchange \-\-lock\-stop <vgname> ...
vgchange --lock-stop <vg_name> ...
To make vgchange not wait for start to complete:
.br
vgchange \-\-lock\-start \-\-lock\-opt nowait
vgchange --lock-start --lock-opt nowait
.br
vgchange \-\-lock\-start \-\-lock\-opt nowait <vgname>
vgchange --lock-start --lock-opt nowait <vg_name>
To stop all lockspaces and wait for all to complete:
.br
lvmlockctl \-\-stop\-lockspaces \-\-wait
lvmlockctl --stop-lockspaces --wait
To start only selected lockd VGs, use the lvm.conf
activation/lock_start_list. When defined, only VG names in this list are
@@ -397,7 +366,7 @@ Scripts or programs on a host that automatically start VGs will use the
"auto" option to indicate that the command is being run automatically by
the system:
vgchange \-\-lock\-start \-\-lock\-opt auto [<vgname> ...]
vgchange --lock-start --lock-opt auto [vg_name ...]
Without any additional configuration, including the "auto" option has no
effect; all VGs are started unless restricted by lock_start_list.
@@ -418,25 +387,21 @@ To use auto activation of lockd LVs (see auto_activation_volume_list),
auto starting of the corresponding lockd VGs is necessary.
.SS internal command locking
.SS locking activity
To optimize the use of LVM with lvmlockd, be aware of the three kinds of
locks and when they are used:
To optimize the use of LVM with lvmlockd, consider the three kinds of
locks in lvmlockd and when they are used:
.I GL lock
The global lock (GL lock) is associated with global information, which is
information not isolated to a single VG. This includes:
\[bu]
The global VG namespace.
- The global VG namespace.
.br
\[bu]
The set of orphan PVs and unused devices.
.br
\[bu]
The properties of orphan PVs, e.g. PV size.
- The set of orphan PVs and unused devices.
.br
- The properties of orphan PVs, e.g. PV size.
The global lock is used in shared mode by commands that read this
information, or in exclusive mode by commands that change it.
@@ -449,7 +414,12 @@ creates a new VG name, and it takes a PV from the list of unused PVs.
When an LVM command is given a tag argument, or uses select, it must read
all VGs to match the tag or selection, which causes the global lock to be
acquired.
acquired. To avoid use of the global lock, avoid using tags and select,
and specify VG name arguments.
When use_lvmlockd is enabled, LVM commands attempt to acquire the global
lock even if no lockd VGs exist. For this reason, lvmlockd should not be
enabled unless lockd VGs will be used.
.I VG lock
@@ -462,7 +432,7 @@ The command 'vgs' will not only acquire the GL lock to read the list of
all VG names, but will acquire the VG lock for each VG prior to reading
it.
The command 'vgs <vgname>' does not acquire the GL lock (it does not need
The command 'vgs <vg_name>' does not acquire the GL lock (it does not need
the list of all VG names), but will acquire the VG lock on each VG name
argument.
@@ -474,14 +444,14 @@ activated. LV locks are persistent and remain in place after the
activation command is done. GL and VG locks are transient, and are held
only while an LVM command is running.
.I lock retries
.I retries
If a request for a GL or VG lock fails due to a lock conflict with another
host, lvmlockd automatically retries for a short time before returning a
failure to the LVM command. If those retries are insufficient, the LVM
command will retry the entire lock request a number of times specified by
global/lvmlockd_lock_retries before failing. If a request for an LV lock
fails due to a lock conflict, the command fails immediately.
failure to the LVM command. The LVM command will then retry the entire
lock request a number of times specified by global/lvmlockd_lock_retries
before failing. If a request for an LV lock fails due to a lock conflict,
the command fails immediately.
.SS sanlock global lock
@@ -500,22 +470,21 @@ are moved or removed.
The vgcreate command typically acquires the global lock, but in the case
of the first sanlock VG, there will be no global lock to acquire until the
first vgcreate is complete. So, creating the first sanlock VG is a
initial vgcreate is complete. So, creating the first sanlock VG is a
special case that skips the global lock.
vgcreate for a sanlock VG determines it is the first one to exist if no
other sanlock VGs are visible. It is possible that other sanlock VGs do
exist but are not visible on the host running vgcreate. In this case,
vgcreate would create a new sanlock VG with the global lock enabled. When
the other VG containing a global lock appears, lvmlockd will see more than
one VG with a global lock enabled, and LVM commands will report that there
are duplicate global locks.
exist but are not visible or started on the host running vgcreate. This
raises the possibility of more than one global lock existing. If this
happens, commands will warn of the condition, and it should be manually
corrected.
If the situation arises where more than one sanlock VG contains a global
lock, the global lock should be manually disabled in all but one of them
with the command:
lvmlockctl \-\-gl\-disable <vgname>
lvmlockctl --gl-disable <vg_name>
(The one VG with the global lock enabled must be visible to all hosts.)
@@ -525,18 +494,55 @@ and subsequent LVM commands will fail to acquire it. In this case, the
global lock needs to be manually enabled in one of the remaining sanlock
VGs with the command:
lvmlockctl \-\-gl\-enable <vgname>
lvmlockctl --gl-enable <vg_name>
A small sanlock VG dedicated to holding the global lock can avoid the case
where the GL lock must be manually enabled after a vgremove.
.SS sanlock VG usage
.SS changing a local VG to a lockd VG
There are some special cases related to using a sanlock VG.
All LVs must be inactive to change the lock type.
lvmlockd must be configured and running as described in USAGE.
Change a local VG to a lockd VG with the command:
.br
vgchange \-\-lock\-type sanlock|dlm <vg_name>
Start the VG on any hosts that need to use it:
.br
vgchange \-\-lock\-start <vg_name>
.SS changing a clvm VG to a lockd VG
All LVs must be inactive to change the lock type.
1. Change the clvm VG to a local VG.
Within a running clvm cluster, change a clvm VG to a local VG with the
command:
vgchange \-cn <vg_name>
If the clvm cluster is no longer running on any nodes, then extra options
can be used forcibly make the VG local. Caution: this is only safe if all
nodes have stopped using the VG:
vgchange \-\-config 'global/locking_type=0 global/use_lvmlockd=0'
.RS
\-cn <vg_name>
.RE
2. After the VG is local, follow the steps described in "changing a local
VG to a lockd VG".
.SS vgremove and vgreduce with sanlock VGs
vgremove of a sanlock VG will fail if other hosts have the VG started.
Run vgchange \-\-lock-stop <vgname> on all other hosts before vgremove.
Run vgchange --lock-stop <vg_name> on all other hosts before vgremove.
(It may take several seconds before vgremove recognizes that all hosts
have stopped.)
@@ -544,20 +550,17 @@ have stopped.)
A sanlock VG contains a hidden LV called "lvmlock" that holds the sanlock
locks. vgreduce cannot yet remove the PV holding the lvmlockd LV.
To place the lvmlock LV on a specific device, create the VG with only that
device, then use vgextend to add other devices.
.SS shared LVs
When an LV is used concurrently from multiple hosts (e.g. by a
multi\-host/cluster application or file system), the LV can be activated
on multiple hosts concurrently using a shared lock.
multi-host/cluster application or file system), the LV can be activated on
multiple hosts concurrently using a shared lock.
To activate the LV with a shared lock: lvchange \-asy vg/lv.
To activate the LV with a shared lock: lvchange -asy vg/lv.
With lvmlockd, an unspecified activation mode is always exclusive, i.e.
\-ay defaults to \-aey.
-ay defaults to -aey.
If the LV type does not allow the LV to be used concurrently from multiple
hosts, then a shared activation lock is not allowed and the lvchange
@@ -575,13 +578,57 @@ locks if the PV holding the locks is lost. Contact the LVM group for
help with this process.
.\" This is not clean or safe enough to suggest using without help.
.\"
.\" .SS recover from lost PV holding sanlock locks
.\"
.\" In a sanlock VG, the locks are stored on a PV within the VG. If this PV
.\" is lost, the locks need to be reconstructed as follows:
.\"
.\" 1. Enable the unsafe lock modes option in lvm.conf so that default locking requirements can be overriden.
.\"
.\" .nf
.\" allow_override_lock_modes = 1
.\" .fi
.\"
.\" 2. Remove missing PVs and partial LVs from the VG.
.\"
.\" Warning: this is a dangerous operation. Read the man page
.\" for vgreduce first, and try running with the test option.
.\" Verify that the only missing PV is the PV holding the sanlock locks.
.\"
.\" .nf
.\" vgreduce --removemissing --force --lock-gl na --lock-vg na <vg>
.\" .fi
.\"
.\" 3. If step 2 does not remove the internal/hidden "lvmlock" lv, it should be removed.
.\"
.\" .nf
.\" lvremove --lock-vg na --lock-lv na <vg>/lvmlock
.\" .fi
.\"
.\" 4. Change the lock type to none.
.\"
.\" .nf
.\" vgchange --lock-type none --force --lock-gl na --lock-vg na <vg>
.\" .fi
.\"
.\" 5. VG space is needed to recreate the locks. If there is not enough space, vgextend the vg.
.\"
.\" 6. Change the lock type back to sanlock. This creates a new internal
.\" lvmlock lv, and recreates locks.
.\"
.\" .nf
.\" vgchange --lock-type sanlock <vg>
.\" .fi
.SS locking system failures
.B lvmlockd failure
If lvmlockd fails or is killed while holding locks, the locks are orphaned
in the lock manager. lvmlockd can be restarted with an option to adopt
locks in the lock manager that had been held by the previous instance.
in the lock manager. lvmlockd can be restarted, and it will adopt the
locks from the lock manager that had been held by the previous instance.
.B dlm/corosync failure
@@ -591,33 +638,37 @@ method configured within the dlm/corosync clustering environment.
LVM commands on other hosts will be blocked from acquiring any locks until
the dlm/corosync recovery process is complete.
.B sanlock lease storage failure
.B sanlock lock storage failure
If a host loses access to the device holding a VG's locks, sanlock cannot
renew the VG's lockspace lease for those locks. After some time, the
lease will expire, and locks held by the host can be acquired by other
hosts.
If access to the device containing the VG's locks is lost, sanlock cannot
renew its leases for locked LVs. This means that the host could soon lose
the lease to another host which could activate the LV exclusively.
sanlock is designed to never reach the point where two hosts hold the
same lease exclusively at once, so the same LV should never be active on
two hosts at once when activated exclusively.
If no LVs are active in the VG, the lockspace with an expiring lease will
be shut down, and errors will be reported when trying to use the VG. Use
the lvmlockctl \-\-drop command to clear the stale lockspace from
lvmlockd.
The current method of handling this involves no action from lvmlockd,
which allows sanlock to protect the leases itself. This produces a safe
but potentially inconvenient result. Doing nothing from lvmlockd leads to
the host's LV locks not being released, which leads to sanlock using the
local watchdog to reset the host before another host can acquire any locks
held by the local host.
If the VG has active LVs, the LVs must be quickly deactivated before the
lockspace lease expires. After all LVs are deactivated, run lvmlockctl
\-\-drop <vgname> to clear the expiring lockspace from lvmlockd. If all
LVs in the VG are not deactivated within about 40 seconds, sanlock will
reset the host using the local watchdog. The host reset is ultimately a
severe form of "deactivating" LVs before they can be activated on other
hosts. The reset is considered a better alternative than having LVs used
by multiple hosts at once, which could easily damage or destroy their
content. A future enhancement may automatically attempt to deactivate LVs
before the lockspace lease expires.
LVM commands on other hosts will be blocked from acquiring locks held by
the failed/reset host until the sanlock recovery time expires (2-4
minutes). This includes activation of any LVs that were locked by the
failed host. It also includes GL/VG locks held by any LVM commands that
happened to be running on the failed host at the time of the failure.
(In the future, lvmlockd may have the option to suspend locked LVs in
response the sanlock leases expiring. This would avoid the need for
sanlock to reset the host.)
.B sanlock daemon failure
If the sanlock daemon fails or exits while a lockspace is started, the
local watchdog will reset the host.
local watchdog will reset the host. See previous section for the impact
on other hosts.
.SS changing dlm cluster name
@@ -637,84 +688,44 @@ cluster name for the dlm VG must be changed. To do this:
3. Change the VG lock type to none:
.br
vgchange \-\-lock\-type none \-\-force <vgname>
vgchange --lock-type none --force <vg_name>
4. Change the VG lock type back to dlm which sets the new cluster name:
.br
vgchange \-\-lock\-type dlm <vgname>
vgchange --lock-type dlm <vg_name>
.SS changing a local VG to a lockd VG
All LVs must be inactive to change the lock type.
lvmlockd must be configured and running as described in USAGE.
Change a local VG to a lockd VG with the command:
.br
vgchange \-\-lock\-type sanlock|dlm <vgname>
Start the VG on any hosts that need to use it:
.br
vgchange \-\-lock\-start <vgname>
.SS changing a clvm VG to a lockd VG
All LVs must be inactive to change the lock type.
First change the clvm VG to a local VG. Within a running clvm cluster,
change a clvm VG to a local VG with the command:
vgchange \-cn <vgname>
If the clvm cluster is no longer running on any nodes, then extra options
can be used forcibly make the VG local. Caution: this is only safe if all
nodes have stopped using the VG:
vgchange \-\-config 'global/locking_type=0 global/use_lvmlockd=0'
.RS
\-cn <vgname>
.RE
After the VG is local, follow the steps described in "changing a local VG
to a lockd VG".
.SS limitations of lockd VGs
.SS limitations of lvmlockd and lockd VGs
lvmlockd currently requires using lvmetad and lvmpolld.
If a lockd VG becomes visible after the initial system startup, it is not
automatically started through the system service/init manager, and LVs in
it are not autoactivated.
Things that do not yet work in lockd VGs:
.br
\[bu]
creating a new thin pool and a new thin LV in a single command
- creating a new thin pool and a new thin LV in a single command
.br
\[bu]
using lvcreate to create cache pools or cache LVs (use lvconvert)
- using lvcreate to create cache pools or cache LVs (use lvconvert)
.br
\[bu]
using external origins for thin LVs
- using external origins for thin LVs
.br
\[bu]
splitting mirrors and snapshots from LVs
- splitting mirrors and snapshots from LVs
.br
\[bu]
vgsplit
- vgsplit
.br
\[bu]
vgmerge
- vgmerge
.br
\[bu]
resizing an LV that is active in the shared mode on multiple hosts
- resizing an LV that is active in the shared mode on multiple hosts
.SS lvmlockd changes from clvmd
.SS clvmd to lvmlockd transition
(See above for converting an existing clvm VG to a lockd VG.)
While lvmlockd and clvmd are entirely different systems, LVM command usage
remains similar. Differences are more notable when using lvmlockd's
While lvmlockd and clvmd are entirely different systems, LVM usage remains
largely the same. Differences are more notable when using lvmlockd's
sanlock option.
Visible usage differences between lockd VGs with lvmlockd and clvm VGs
@@ -725,16 +736,19 @@ lvm.conf must be configured to use either lvmlockd (use_lvmlockd=1) or
clvmd (locking_type=3), but not both.
.IP \[bu] 2
vgcreate \-\-shared creates a lockd VG, and vgcreate \-\-clustered y
creates a clvm VG.
vgcreate --shared creates a lockd VG, and vgcreate --clustered y creates a
clvm VG.
.IP \[bu] 2
lvmlockd adds the option of using sanlock for locking, avoiding the
need for network clustering.
.IP \[bu] 2
lvmlockd does not require all hosts to see all the same shared devices.
.IP \[bu] 2
lvmlockd defaults to the exclusive activation mode whenever the activation
mode is unspecified, i.e. \-ay means \-aey, not \-asy.
mode is unspecified, i.e. -ay means -aey, not -asy.
.IP \[bu] 2
lvmlockd commands always apply to the local host, and never have an effect
@@ -748,13 +762,13 @@ lvmlockd saves the cluster name for a lockd VG using dlm. Only hosts in
the matching cluster can use the VG.
.IP \[bu] 2
lvmlockd requires starting/stopping lockd VGs with vgchange \-\-lock-start
and \-\-lock-stop.
lvmlockd requires starting/stopping lockd VGs with vgchange --lock-start
and --lock-stop.
.IP \[bu] 2
vgremove of a sanlock VG may fail indicating that all hosts have not
stopped the VG lockspace. Stop the VG on all hosts using vgchange
\-\-lock-stop.
stopped the lockspace for the VG. Stop the VG lockspace on all uses using
vgchange --lock-stop.
.IP \[bu] 2
vgreduce of a PV in a sanlock VG may fail if it holds the internal
@@ -763,15 +777,12 @@ vgreduce of a PV in a sanlock VG may fail if it holds the internal
.IP \[bu] 2
lvmlockd uses lock retries instead of lock queueing, so high lock
contention may require increasing global/lvmlockd_lock_retries to
avoid transient lock failures.
avoid transient lock contention failures.
.IP \[bu] 2
lvmlockd includes VG reporting options lock_type and lock_args, and LV
reporting option lock_args to view the corresponding metadata fields.
.IP \[bu] 2
In the 'vgs' command's sixth VG attr field, "s" for "shared" is displayed
for lockd VGs.
The reporting options locktype and lockargs can be used to view lockd VG
and LV lock_type and lock_args fields, i.g. vgs -o+locktype,lockargs.
In the sixth VG attr field, "s" for "shared" is displayed for lockd VGs.
.IP \[bu] 2
If lvmlockd fails or is killed while in use, locks it held remain but are

View File

@@ -34,10 +34,6 @@ vgchange \(em change attributes of a volume group
.RB [ \-\-ignoreskippedcluster ]
.RB [ \-\-sysinit ]
.RB [ \-\-noudevsync ]
.RB [ \-\-lock\-start ]
.RB [ \-\-lock\-stop ]
.RB [ \-\-lock\-type
.IR LockType ]
.RB [ \-l | \-\-logicalvolume
.IR MaxLogicalVolumes ]
.RB [ \-p | \-\-maxphysicalvolumes
@@ -131,30 +127,6 @@ LVs with snapshots are always activated exclusively because they can only
be used on one node at once.
For local VGs, \-ay, \-aey, and \-asy are all equivalent.
.IP
In a shared VG, lvmlockd is used for locking, and the following options
are possible:
With \-aey, the command activates the LV in exclusive mode, allowing a
single host to activate the LV (the host running the command). Before
activating the LV, the command uses lvmlockd to acquire an exclusive lock
on the LV. If the lock cannot be acquired, the LV is not activated and an
error is reported. This would happen if the LV is active on another host.
With \-asy, the command activates the LV in shared mode, allowing multiple
hosts to activate the LV concurrently. Before activating the LV, the
command uses lvmlockd to acquire a shared lock on the LV. If the lock
cannot be acquired, the LV is not activated and an error is reported.
This would happen if the LV is active exclusively on another host. If the
LV type prohibits shared access, such as a snapshot, the command will
report an error and fail.
With \-an, the command deactivates the LV on the host running the command.
After deactivating the LV, the command uses lvmlockd to release the
current lock on the LV.
With lvmlockd, an unspecified mode is always exclusive, \-ay defaults to
\-aey.
.TP
.BR \-\-activationmode " {" \fIcomplete | \fIdegraded | \fIpartial }
@@ -241,20 +213,6 @@ Make no attempt to interact with dmeventd unless
is specified.
Do not use this if dmeventd is already monitoring a device.
.TP
.BR \-\-lock\-start
Start the lockspace of a shared VG in lvmlockd. lvmlockd locks becomes
available for the VG, allowing LVM to use the VG. See
.BR lvmlockd (8).
.TP
.BR \-\-lock\-stop
Stop the lockspace of a shared VG in lvmlockd. lvmlockd locks become
unavailable for the VG, preventing LVM from using the VG. See
.BR lvmlockd (8).
.TP
.BR \-\-lock\-type " " \fILockType
Change the VG lock type to or from a shared lock type used with lvmlockd. See
.BR lvmlockd (8).
.TP
.BR \-l ", " \-\-logicalvolume " " \fIMaxLogicalVolumes
Changes the maximum logical volume number of an existing inactive
volume group.

View File

@@ -27,7 +27,6 @@ vgcreate \(em create a volume group
.IR NumberOfCopies | unmanaged | all ]
.RB [ \-s | \-\-physicalextentsize
.IR PhysicalExtentSize [ bBsSkKmMgGtTpPeE ]]
.RB [ \-\-shared ]
.RB [ \-\-systemid
.IR SystemID ]
.RB [ \-t | \-\-test ]
@@ -128,13 +127,6 @@ impact on I/O performance to the logical volume. The smallest PE is 1KiB
The 2.4 kernel has a limitation of 2TiB per block device.
.TP
.B \-\-shared
Create a shared VG using lvmlockd. lvmlockd will select lock type sanlock
or dlm depending on which lock manager is running. This allows multiple
hosts to share a VG on shared devices. See
.BR lvmlockd (8).
.TP
.BR \-\-systemid " " \fISystemID
Specifies the system ID that will be given to the new VG, overriding the

View File

@@ -20,8 +20,6 @@ You can then move all the Physical Volumes in that Volume Group to
a different system for later
.BR vgimport (8).
Most LVM2 tools ignore exported Volume Groups.
vgexport clears the VG system ID, and vgimport sets the VG system ID
to match the host running vgimport (if the host has a system ID).
.SH OPTIONS
See \fBlvm\fP(8) for common options.
.TP
@@ -31,5 +29,4 @@ Export all inactive Volume Groups.
.BR lvm (8),
.BR pvscan (8),
.BR vgimport (8),
.BR vgscan (8),
.BR lvmsystemid (7)
.BR vgscan (8)

View File

@@ -16,8 +16,6 @@ exported using
.BR vgexport (8)
known to the system again, perhaps after moving its Physical Volumes
from a different machine.
vgexport clears the VG system ID, and vgimport sets the VG system ID
to match the host running vgimport (if the host has a system ID).
.SH OPTIONS
See \fBlvm\fP(8) for common options.
.TP
@@ -32,5 +30,4 @@ failed and they cannot be restored.
.BR lvm (8),
.BR pvscan (8),
.BR vgexport (8),
.BR vgscan (8),
.BR lvmsystemid (7)
.BR vgscan (8)

View File

@@ -93,7 +93,7 @@ are missing from the system
.IP 5 3
Allocation policy: (c)ontiguous, c(l)ing, (n)ormal, (a)nywhere
.IP 6 3
(c)lustered, (s)hared
(c)lustered
.RE
.TP
.BR \-O ", " \-\-sort

View File

@@ -398,9 +398,7 @@ for the kernel device-mapper.
%defattr(-,root,root,-)
%doc COPYING COPYING.LIB WHATS_NEW_DM VERSION_DM README INSTALL
%attr(755,root,root) %{_sbindir}/dmsetup
%{_sbindir}/dmstats
%{_mandir}/man8/dmsetup.8.gz
%{_mandir}/man8/dmstats.8.gz
%if %{enable_udev}
%doc udev/12-dm-permissions.rules
%dir %{_udevbasedir}

View File

@@ -199,8 +199,7 @@ install: .tests-stamp lib/paths-installed
$(INSTALL_PROGRAM) api/*.{t,py} $(DATADIR)/api/
$(INSTALL_DATA) lib/paths-installed $(DATADIR)/lib/paths
$(INSTALL_DATA) $(LIB_FLAVOURS) $(DATADIR)/lib/
for i in cache-mq cache-smq thin-performance ; do \
$(INSTALL_DATA) $(abs_top_srcdir)/conf/$$i.profile $(DATADIR)/lib/$$i.profile; done
$(INSTALL_DATA) $(abs_top_srcdir)/conf/thin-performance.profile $(DATADIR)/lib/thin-performance.profile
$(INSTALL_SCRIPT) $(LIB_SHARED) $(DATADIR)/lib/
for i in $(CMDS); do (cd $(DATADIR)/lib && $(LN_S) -f lvm-wrapper $$i); done

View File

@@ -16,8 +16,6 @@ test -e LOCAL_LVMPOLLD && skip
aux have_cache 1 3 0 || skip
aux prepare_vg 3
aux lvmconf 'global/cache_disabled_features = [ "policy_smq" ]'
lvcreate --type cache-pool -an -v -L 2 -n cpool $vg
lvcreate -H -L 4 -n corigin --cachepool $vg/cpool
lvcreate -n noncache -l 1 $vg

View File

@@ -18,8 +18,6 @@ test -e LOCAL_LVMPOLLD && skip
aux have_cache 1 3 0 || skip
aux have_raid 1 0 0 || skip
aux lvmconf 'global/cache_disabled_features = [ "policy_smq" ]'
aux prepare_vg 5 80
# Bug 1095843

View File

@@ -23,7 +23,6 @@ aux have_cache 1 3 0 || skip
# FIXME: parallel cache metadata allocator is crashing when used value 8000!
aux prepare_vg 5 80000
aux lvmconf 'global/cache_disabled_features = [ "policy_smq" ]'
#######################
# Cache_Pool creation #

View File

@@ -204,11 +204,9 @@ install_tools_static: lvm.static
install_dmsetup_dynamic: dmsetup
$(INSTALL_PROGRAM) -D $< $(sbindir)/$(<F)
$(LN_S) -f $(<F) $(sbindir)/dmstats
install_dmsetup_static: dmsetup.static
$(INSTALL_PROGRAM) -D $< $(staticdir)/$(<F)
$(LN_S) -f $(<F) $(sbindir)/dmstats
install_device-mapper: $(INSTALL_DMSETUP_TARGETS)

File diff suppressed because it is too large Load Diff

View File

@@ -690,9 +690,9 @@ static int _lvchange_cachepolicy(struct cmd_context *cmd, struct logical_volume
goto out;
}
if (!get_cache_params(cmd, NULL, &name, &settings))
if (!get_cache_policy_params(cmd, &name, &settings))
goto_out;
if (!cache_set_policy(first_seg(lv), name, settings))
if (!lv_cache_set_policy(lv, name, settings))
goto_out;
if (!lv_update_and_reload(lv))
goto_out;
@@ -1296,9 +1296,7 @@ int lvchange(struct cmd_context *cmd, int argc, char **argv)
*/
if (arg_count(cmd, activate_ARG) || arg_count(cmd, refresh_ARG)) {
cmd->lockd_vg_default_sh = 1;
/* Allow deactivating if locks fail. */
if (is_change_activating((activation_change_t)arg_uint_value(cmd, activate_ARG, CHANGE_AY)))
cmd->lockd_vg_enforce_sh = 1;
cmd->lockd_vg_enforce_sh = 1;
}
return process_each_lv(cmd, argc, argv,

View File

@@ -50,7 +50,7 @@ struct lvconvert_params {
uint32_t stripes;
uint32_t stripe_size;
uint32_t read_ahead;
const char *cache_mode; /* cache */
uint64_t feature_flags; /* cache_pool */
const char *policy_name; /* cache */
struct dm_config_tree *policy_settings; /* cache */
@@ -299,14 +299,26 @@ static int _read_pool_params(struct cmd_context *cmd, int *pargc, char ***pargv,
} else if (!strcmp(type_str, "thin-pool"))
thinpool = 1;
if (lp->cache && !cachepool) {
log_error("--cache requires --cachepool.");
return 0;
}
if ((lp->cache || cachepool) &&
!get_cache_params(cmd, &lp->cache_mode, &lp->policy_name, &lp->policy_settings)) {
log_error("Failed to parse cache policy and/or settings.");
return 0;
if (cachepool) {
const char *cachemode = arg_str_value(cmd, cachemode_ARG, NULL);
if (!cachemode)
cachemode = find_config_tree_str(cmd, allocation_cache_pool_cachemode_CFG, NULL);
if (!set_cache_pool_feature(&lp->feature_flags, cachemode))
return_0;
if (!get_cache_policy_params(cmd, &lp->policy_name, &lp->policy_settings)) {
log_error("Failed to parse cache policy and/or settings.");
return 0;
}
} else {
if (arg_from_list_is_set(cmd, "is valid only with cache pools",
cachepool_ARG, cachemode_ARG, -1))
return_0;
if (lp->cache) {
log_error("--cache requires --cachepool.");
return 0;
}
}
if (thinpool) {
@@ -3068,9 +3080,10 @@ mda_write:
seg->chunk_size = lp->chunk_size;
seg->discards = lp->discards;
seg->zero_new_blocks = lp->zero ? 1 : 0;
seg->feature_flags = lp->feature_flags; /* cache-pool */
if ((lp->policy_name || lp->policy_settings) &&
!cache_set_policy(seg, lp->policy_name, lp->policy_settings))
!lv_cache_set_policy(seg->lv, lp->policy_name, lp->policy_settings))
return_0;
/* Rename deactivated metadata LV to have _tmeta suffix */
@@ -3178,12 +3191,6 @@ static int _lvconvert_cache(struct cmd_context *cmd,
if (!(cache_lv = lv_cache_create(pool_lv, origin_lv)))
return_0;
if (!cache_set_mode(first_seg(cache_lv), lp->cache_mode))
return_0;
if (!cache_set_policy(first_seg(cache_lv), lp->policy_name, lp->policy_settings))
return_0;
if (!lv_update_and_reload(cache_lv))
return_0;
@@ -3420,12 +3427,14 @@ static int lvconvert_single(struct cmd_context *cmd, struct lvconvert_params *lp
}
/*
* Request a transient lock. If the LV is active, it has a persistent
* lock already, and this request does nothing. If the LV is not
* active, this acquires a transient lock that will be released when
* the command exits.
* If the lv is inactive before and after the command, the
* use of PERSISTENT here means the lv will remain locked as
* an effect of running the lvconvert.
* To unlock it, it would need to be activated+deactivated.
* Or, we could identify the commands for which the lv remains
* inactive, and not use PERSISTENT here for those cases.
*/
if (!lockd_lv(cmd, lv, "ex", 0))
if (!lockd_lv(cmd, lv, "ex", LDLV_PERSISTENT))
goto_bad;
/*

View File

@@ -567,15 +567,22 @@ static int _read_mirror_and_raid_params(struct cmd_context *cmd,
static int _read_cache_params(struct cmd_context *cmd,
struct lvcreate_params *lp)
{
const char *cachemode;
if (!seg_is_cache(lp) && !seg_is_cache_pool(lp))
return 1;
if (!get_cache_params(cmd,
&lp->cache_mode,
&lp->policy_name,
&lp->policy_settings))
if (!(cachemode = arg_str_value(cmd, cachemode_ARG, NULL)))
cachemode = find_config_tree_str(cmd, allocation_cache_pool_cachemode_CFG, NULL);
if (!set_cache_pool_feature(&lp->feature_flags, cachemode))
return_0;
if (!get_cache_policy_params(cmd, &lp->policy_name, &lp->policy_settings)) {
log_error("Failed to parse cache policy and/or settings.");
return 0;
}
return 1;
}
@@ -1082,6 +1089,8 @@ static int _determine_cache_argument(struct volume_group *vg,
/* If cache args not given, use those from cache pool */
if (!arg_is_set(cmd, chunksize_ARG))
lp->chunk_size = first_seg(lv)->chunk_size;
if (!arg_is_set(cmd, cachemode_ARG))
lp->feature_flags = first_seg(lv)->feature_flags;
} else if (lv) {
/* Origin exists, create cache pool volume */
if (!validate_lv_cache_create_origin(lv))

View File

@@ -461,14 +461,13 @@ static int _lvmpolld_init_poll_vg(struct cmd_context *cmd, const char *vgname,
if (!id.display_name && !lpdp->parms->aborting)
continue;
id.vg_name = lv->vg->name;
id.lv_name = lv->name;
if (!*lv->lvid.s) {
log_print_unless_silent("Missing LV uuid within: %s/%s", id.vg_name, id.lv_name);
continue;
}
id.vg_name = lv->vg->name;
id.lv_name = lv->name;
id.uuid = lv->lvid.s;
r = lvmpolld_poll_init(cmd, &id, lpdp->parms);

View File

@@ -1399,10 +1399,8 @@ static int _validate_cachepool_params(const char *name,
return 1;
}
int get_cache_params(struct cmd_context *cmd,
const char **mode,
const char **name,
struct dm_config_tree **settings)
int get_cache_policy_params(struct cmd_context *cmd, const char **name,
struct dm_config_tree **settings)
{
const char *str;
struct arg_value_group_list *group;
@@ -1410,14 +1408,7 @@ int get_cache_params(struct cmd_context *cmd,
struct dm_config_node *cn;
int ok = 0;
if (mode)
*mode = arg_str_value(cmd, cachemode_ARG, NULL);
if (name)
*name = arg_str_value(cmd, cachepolicy_ARG, NULL);
if (!settings)
return 1;
*name = arg_str_value(cmd, cachepolicy_ARG, DEFAULT_CACHE_POOL_POLICY);
dm_list_iterate_items(group, &cmd->arg_value_groups) {
if (!grouped_arg_is_set(group->arg_values, cachesettings_ARG))
@@ -1438,9 +1429,6 @@ int get_cache_params(struct cmd_context *cmd,
goto_out;
}
if (!current)
return 1;
if (!(result = dm_config_flatten(current)))
goto_out;

View File

@@ -190,10 +190,9 @@ int get_pool_params(struct cmd_context *cmd,
int get_stripe_params(struct cmd_context *cmd, uint32_t *stripes,
uint32_t *stripe_size);
int get_cache_params(struct cmd_context *cmd,
const char **mode,
const char **name,
struct dm_config_tree **settings);
int get_cache_policy_params(struct cmd_context *cmd,
const char **name,
struct dm_config_tree **settings);
int change_tag(struct cmd_context *cmd, struct volume_group *vg,
struct logical_volume *lv, struct physical_volume *pv, int arg);

View File

@@ -204,7 +204,7 @@ int vgchange_activate(struct cmd_context *cmd, struct volume_group *vg,
if (vg->system_id && vg->system_id[0] &&
cmd->system_id && cmd->system_id[0] &&
strcmp(vg->system_id, cmd->system_id) &&
do_activate) {
is_change_activating(activate)) {
log_error("Cannot activate LVs in a foreign VG.");
return ECMD_FAILED;
}
@@ -1026,9 +1026,7 @@ static int _lockd_vgchange(struct cmd_context *cmd, int argc, char **argv)
if (arg_is_set(cmd, activate_ARG) || arg_is_set(cmd, refresh_ARG)) {
cmd->lockd_vg_default_sh = 1;
/* Allow deactivating if locks fail. */
if (is_change_activating((activation_change_t)arg_uint_value(cmd, activate_ARG, CHANGE_AY)))
cmd->lockd_vg_enforce_sh = 1;
cmd->lockd_vg_enforce_sh = 1;
}
/* Starting a vg lockspace means there are no locks available yet. */

View File

@@ -47,7 +47,7 @@ BLKID_RULE=IMPORT{program}=\"${SBIN}\/blkid -o udev -p \$$tempnode\"
endif
ifeq ("@UDEV_SYSTEMD_BACKGROUND_JOBS@", "yes")
PVSCAN_RULE=ACTION\!=\"remove\", ENV{LVM_PV_GONE}==\"1\", RUN\+=\"@bindir@/systemd-run $(LVM_EXEC)\/lvm pvscan --cache \$$major\:\$$minor\", GOTO=\"lvm_end\"\nENV{SYSTEMD_ALIAS}=\"\/dev\/block\/\$$major:\$$minor\"\nENV{ID_MODEL}=\"LVM PV \$$env{ID_FS_UUID_ENC} on \/dev\/\$$name\"\nENV{SYSTEMD_WANTS}\+=\"lvm2-pvscan@\$$major:\$$minor.service\"
PVSCAN_RULE=ACTION\!=\"remove\", ENV{LVM_PV_GONE}==\"1\", RUN\+=\"@bindir@/systemd-run $(LVM_EXEC)\/lvm pvscan --cache \$$major\:\$$minor\", GOTO=\"lvm_end\"\nENV{SYSTEMD_ALIAS}=\"\/dev\/block\/\$$major:\$$minor\"\nENV{ID_MODEL}=\"LVM PV \$$env{ID_FS_UUID_ENC} on \/dev\/\$$name\"\nENV{SYSTEMD_WANTS}=\"lvm2-pvscan@\$$major:\$$minor.service\"
else
PVSCAN_RULE=RUN\+\=\"$(LVM_EXEC)/lvm pvscan --background --cache --activate ay --major \$$major --minor \$$minor\", ENV{LVM_SCANNED}=\"1\"
endif