1
0
mirror of git://sourceware.org/git/lvm2.git synced 2025-10-27 11:33:19 +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
35 changed files with 2064 additions and 1927 deletions

View File

@@ -1,21 +1,5 @@
Version 1.02.104 -
=================================
Add dmstats.8 man page
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.
Add a 'stats' command to dmsetup to configure, manage and report stats data.
Add --regionid, --allregions to specify a single stats region or all regions.
Add --allprograms for stats commands that filter by program ID.
Add --auxdata and --programid arguments to set stats aux data and program ID.
Add statistics fields to -o <field>
Add libdm-stats library to allow management of device-mapper statistics.
Add --units to control 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.
Add dm_timestamp functions to libdevmapper.
Version 1.02.103 - 24th July 2015

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)
@@ -1833,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;
@@ -1852,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);
@@ -1889,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) {
@@ -1995,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.
@@ -2037,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;
@@ -2142,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;
}
@@ -2302,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.
@@ -2313,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
@@ -2351,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
@@ -2386,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);
/*
@@ -2577,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
@@ -2587,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;
@@ -2642,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)
@@ -2657,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);
@@ -2745,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;
@@ -3609,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;
@@ -3783,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;
}
@@ -3943,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,
@@ -3959,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);
}
@@ -4356,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

@@ -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

@@ -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

@@ -1357,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;
@@ -1402,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.");
@@ -1426,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;
@@ -1447,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;
@@ -1467,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;
}
/*
@@ -1497,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.
*/
@@ -1514,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;
}
@@ -1531,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
@@ -1707,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
@@ -1809,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;
}
@@ -1946,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

@@ -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,19 +66,23 @@ 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_delta

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;
};

View File

@@ -1,7 +1,7 @@
/*
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
* Copyright (C) 2004-2015 Red Hat, Inc. All rights reserved.
* Copyright (C) 2006 Rackable Systems All rights reserved.
* Copyright (C) 2006 Rackable Systems All rights reserved.
*
* This file is part of the device-mapper userspace tools.
*
@@ -128,7 +128,6 @@ enum {
*/
struct dm_task;
struct dm_timestamp;
struct dm_task *dm_task_create(int type);
void dm_task_destroy(struct dm_task *dmt);
@@ -229,12 +228,6 @@ int dm_task_secure_data(struct dm_task *dmt);
int dm_task_retry_remove(struct dm_task *dmt);
int dm_task_deferred_remove(struct dm_task *dmt);
/*
* Record timestamp immediately after the ioctl returns.
*/
int dm_task_set_record_timestamp(struct dm_task *dmt);
struct dm_timestamp *dm_task_get_ioctl_timestamp(struct dm_task *dmt);
/*
* Enable checks for common mistakes such as issuing ioctls in an unsafe order.
*/
@@ -395,11 +388,11 @@ struct dm_status_thin {
int dm_get_status_thin(struct dm_pool *mem, const char *params,
struct dm_status_thin **status);
/*
/**
* device-mapper statistics support
*/
/*
/**
* Statistics handle.
*
* Operations on dm_stats objects include managing statistics regions
@@ -408,7 +401,7 @@ int dm_get_status_thin(struct dm_pool *mem, const char *params,
*/
struct dm_stats;
/*
/**
* Allocate a dm_stats handle to use for subsequent device-mapper
* statistics operations. A program_id may be specified and will be
* used by default for subsequent operations on this handle.
@@ -418,27 +411,39 @@ struct dm_stats;
*/
struct dm_stats *dm_stats_create(const char *program_id);
/*
/**
* Bind a dm_stats handle to the specified device major and minor
* values. Any previous binding is cleared and any preexisting counter
* data contained in the handle is released.
*/
int dm_stats_bind_devno(struct dm_stats *dms, int major, int minor);
/*
/**
* Bind a dm_stats handle to the specified device name.
* Any previous binding is cleared and any preexisting counter
* data contained in the handle is released.
*/
int dm_stats_bind_name(struct dm_stats *dms, const char *name);
/*
/**
* Bind a dm_stats handle to the specified device UUID.
* Any previous binding is cleared and any preexisting counter
* data contained in the handle is released.
*/
int dm_stats_bind_uuid(struct dm_stats *dms, const char *uuid);
/**
* Initialise a dm_stats handle so that it can contain at most
* max_regions statistics regions. This must be called prior to
* populating any regions with dm_stats_populate_region().
*
* If dm_stats_init() is called with a stats handle that was previously
* initialised with dm_stats_init(), or another call that initialises
* the region table (e.g. dm_stats_list()), any data contained in the
* handle will be destroyed.
*/
int dm_stats_init(struct dm_stats *dms, uint64_t max_regions);
#define DM_STATS_ALL_PROGRAMS ""
/*
* Parse the response from a @stats_list message. dm_stats_list will
@@ -455,10 +460,17 @@ int dm_stats_bind_uuid(struct dm_stats *dms, const char *uuid);
*/
int dm_stats_list(struct dm_stats *dms, const char *program_id);
/**
* Populate a region of a dm_stats object with the response from
* a @stats_print message.
*/
int dm_stats_populate_region(struct dm_stats *dms, uint64_t region_id,
const char *resp);
#define DM_STATS_REGIONS_ALL UINT64_MAX
/*
* Populate a dm_stats object with statistics for one or more regions of
* the specified device.
/**
* Populate a dm_stats object with statistics for one or more regions
* of the specified device.
*
* A program_id may optionally be supplied; if the argument is non-NULL
* only regions with a matching program_id value will be considered. If
@@ -473,11 +485,12 @@ int dm_stats_list(struct dm_stats *dms, const char *program_id);
*
* If region_id is used to request a single region_id to be populated
* the program_id is ignored.
*
*/
int dm_stats_populate(struct dm_stats *dms, const char *program_id,
uint64_t region_id);
/*
/**
* Create a new statistics region on the device bound to dms.
*
* start and len specify the region start and length in 512b sectors.
@@ -502,51 +515,51 @@ int dm_stats_populate(struct dm_stats *dms, const char *program_id,
* dm_stats_create_region(dms, 1024, 1 << 11, -2, p, a);
*
* program_id is an optional string argument that identifies the
* program creating the region. If program_id is NULL or the empty
* string the default program_id stored in the handle will be used.
* program creating the region. If program_id is NULL the default
* program_id stored in the handle will be used.
*
* aux_data is an optional string argument passed to the kernel that is
* stored with the statistics region. It is not currently accessed by
* the library or kernel and may be used to store arbitrary user data.
* stored with the statistics region. It is not accessed by the library
* or kernel and may be used to store arbitrary user data.
*
* The region_id of the newly-created region is returned in *region_id
* if it is non-NULL.
*
*/
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);
/*
/**
* Delete the specified statistics region. This will also mark the
* region as not-present and discard any existing statistics data.
*/
int dm_stats_delete_region(struct dm_stats *dms, uint64_t region_id);
/*
/**
* Clear the specified statistics region. This requests the kernel to
* zero all counter values (except in-flight I/O). Note that this
* operation is not atomic with respect to reads of the counters; any IO
* events occurring between the last print operation and the clear will
* be lost. This can be avoided by using the atomic print-and-clear
* function of the dm_stats_print_region() call or by using the higher
* level dm_stats_populate() interface.
* level dm_stats_populate*() interface.
*/
int dm_stats_clear_region(struct dm_stats *dms, uint64_t region_id);
/*
/**
* Print the current counter values for the specified statistics region
* and return them as a string. The memory for the string buffer will
* be allocated from the dm_stats handle's private pool and should be
* returned by calling dm_stats_buffer_destroy() when no longer
* required. The pointer will become invalid following any call that
* clears or reinitializes the handle (destroy, list, populate, bind).
* required.
*
* This allows applications that wish to access the raw message response
* to obtain it via a dm_stats handle; no parsing of the textual counter
* data is carried out by this function.
*
* Most users are recommended to use the dm_stats_populate() call
* instead since this will automatically parse the statistics data into
* Most users are recommended to use the dm_stats_populate* calls
* instead since these automatically parse the statistics data into
* numeric form accessible via the dm_stats_get_*() counter access
* methods.
*
@@ -554,33 +567,28 @@ int dm_stats_clear_region(struct dm_stats *dms, uint64_t region_id);
* start_line and num_lines parameters. If both are zero all data
* lines are returned.
*
* If the clear parameter is non-zero the operation will also
* atomically reset all counter values to zero (except in-flight IO).
* If the clear parameter is non-zero the operation will also re-set
* all counter values (except in-flight IO) to zero.
*/
char *dm_stats_print_region(struct dm_stats *dms, uint64_t region_id,
unsigned start_line, unsigned num_lines,
unsigned clear);
/*
* Destroy a statistics response buffer obtained from a call to
/**
* Destroy a statistics message buffer obtained from a call to
* dm_stats_print_region().
*/
void dm_stats_buffer_destroy(struct dm_stats *dms, char *buffer);
/*
/**
* Determine the number of regions contained in a dm_stats handle
* following a dm_stats_list() or dm_stats_populate() call.
*
* The value returned is the number of registered regions visible with the
* progam_id value used for the list or populate operation and may not be
* equal to the highest present region_id (either due to program_id
* filtering or gaps in the sequence of region_id values).
* following a dm_stats_list() or dm_stats_populate*() call.
*
* Always returns zero on an empty handle.
*/
uint64_t dm_stats_get_nr_regions(struct dm_stats *dms);
int dm_stats_nr_regions(struct dm_stats *dms);
/*
/**
* Test whether region_id is present in this dm_stats handle.
*/
int dm_stats_region_present(struct dm_stats *dms, uint64_t region_id);
@@ -589,27 +597,113 @@ int dm_stats_region_present(struct dm_stats *dms, uint64_t region_id);
* Returns the number of areas (counter sets) contained in the specified
* region_id of the supplied dm_stats handle.
*/
uint64_t dm_stats_get_region_nr_areas(struct dm_stats *dms, uint64_t region_id);
uint64_t dm_stats_nr_areas_region(struct dm_stats *dms, uint64_t region_id);
/*
* Returns the number of areas (counter sets) contained in the current
* region of the supplied dm_stats handle.
*/
uint64_t dm_stats_nr_areas_current(struct dm_stats *dms);
/**
* Returns the total number of areas (counter sets) in all regions of the
* given dm_stats object.
*/
uint64_t dm_stats_get_nr_areas(struct dm_stats *dms);
uint64_t dm_stats_nr_areas(struct dm_stats *dms);
/*
/**
* Initialise the cursor of a dm_stats handle to address the first
* present region. It is valid to attempt to walk a NULL stats handle
* or a handle containing no present regions; in this case any call to
* dm_stats_walk_next() becomes a no-op and all calls to
* dm_stats_walk_end() return true.
*/
void dm_stats_walk_start(struct dm_stats *dms);
/**
* Advance the statistics cursor to the next area, or to the next
* present region if at the end of the current region.
*/
void dm_stats_walk_next(struct dm_stats *dms);
/**
* Advance the statistics cursor to the next region.
*/
void dm_stats_walk_next_region(struct dm_stats *dms);
/**
* Test whether the end of a statistics walk has been reached.
*/
int dm_stats_walk_end(struct dm_stats *dms);
/**
* Stats iterators
*
* C 'for' and 'do'/'while' style iterators for dm_stats data.
*
* It is not safe to call any function that modifies the region table
* within the loop body (i.e. dm_stats_list(), dm_stats_populate(),
* dm_stats_init(), or dm_stats_destroy()).
*
* All counter and property (dm_stats_get_*) access methods, as well as
* dm_stats_populate_region() can be safely called from loops.
*
*/
/**
* Iterate over the regions table visiting each region.
*
* If the region table is empty or unpopulated the loop body will not be
* executed.
*/
#define dm_stats_foreach_region(dms) \
for (dm_stats_walk_start((dms)); \
!dm_stats_walk_end((dms)); dm_stats_walk_next_region((dms)))
/**
* Iterate over the regions table visiting each area.
*
* If the region table is empty or unpopulated the loop body will not
* be executed.
*/
#define dm_stats_foreach_area(dms) \
for (dm_stats_walk_start((dms)); \
!dm_stats_walk_end((dms)); dm_stats_walk_next((dms)))
/**
* Start a walk iterating over the regions contained in dm_stats handle
* 'dms'.
*
* The body of the loop should call dm_stats_walk_next() or
* dm_stats_walk_next_region() to advance to the next element.
*
* The loop body is executed at least once even if the stats handle is
* empty.
*/
#define dm_stats_walk_do(dms) \
dm_stats_walk_start((dms)); \
do
/**
* End a loop iterating over the regions contained in dm_stats handle
* 'dms'.
*/
#define dm_stats_walk_while(dms) \
while(!dm_stats_walk_end((dms)))
/**
* Destroy a dm_stats object and all associated regions and counter
* sets.
*/
void dm_stats_destroy(struct dm_stats *dms);
/*
/**
* Counter sampling interval
*/
/*
/**
* Set the sampling interval for counter data to the specified value in
* either nanoseconds or milliseconds.
* either nanoseconds or miliseconds.
*
* The interval is used to calculate time-based metrics from the basic
* counter data: an interval must be set before calling any of the
@@ -619,19 +713,19 @@ void dm_stats_destroy(struct dm_stats *dms);
* end of each interval.
*
* All values are stored internally with nanosecond precision and are
* converted to or from ms when the millisecond interfaces are used.
* converted to or from ms when the milisecond interfaces are used.
*/
void dm_stats_set_sampling_interval_ns(struct dm_stats *dms, uint64_t interval_ns);
void dm_stats_set_sampling_interval_ms(struct dm_stats *dms, uint64_t interval_ms);
void dm_stats_set_interval(struct dm_stats *dms, uint64_t interval);
void dm_stats_set_interval_ms(struct dm_stats *dms, uint64_t interval_ms);
/*
/**
* Retrieve the configured sampling interval in either nanoseconds or
* milliseconds.
* miliseconds.
*/
uint64_t dm_stats_get_sampling_interval_ns(struct dm_stats *dms);
uint64_t dm_stats_get_sampling_interval_ms(struct dm_stats *dms);
uint64_t dm_stats_get_interval(struct dm_stats *dms);
uint64_t dm_stats_get_interval_ms(struct dm_stats *dms);
/*
/**
* Override program_id. This may be used to change the default
* program_id value for an existing handle. If the allow_empty argument
* is non-zero a NULL or empty program_id is permitted.
@@ -653,49 +747,42 @@ uint64_t dm_stats_get_sampling_interval_ms(struct dm_stats *dms);
int dm_stats_set_program_id(struct dm_stats *dms, int allow_empty,
const char *program_id);
/*
* Region properties: size, length & area_len.
*
* Region start and length are returned in units of 512b as specified
* at region creation time. The area_len value gives the size of areas
* into which the region has been subdivided. For regions with a single
* area spanning the range this value is equal to the region length.
*
* For regions created with a specified number of areas the value
* represents the size of the areas into which the kernel divided the
* region excluding any rounding of the last area size. The number of
* areas may be obtained using the dm_stats_nr_areas_region() call.
/**
* Retrieve the current values of the stats cursor.
*/
uint64_t dm_stats_get_current_region(struct dm_stats *dms);
uint64_t dm_stats_get_current_area(struct dm_stats *dms);
/**
* Region properties: size, length & step.
*
* All values are returned in units of 512b sectors.
*/
uint64_t dm_stats_get_region_start(struct dm_stats *dms, uint64_t *start,
uint64_t region_id);
uint64_t dm_stats_get_region_len(struct dm_stats *dms, uint64_t *len,
int dm_stats_get_region_start(struct dm_stats *dms, uint64_t *start,
uint64_t region_id);
int dm_stats_get_region_len(struct dm_stats *dms, uint64_t *len,
uint64_t region_id);
int dm_stats_get_region_area_len(struct dm_stats *dms, uint64_t *area_len,
uint64_t region_id);
uint64_t dm_stats_get_region_area_len(struct dm_stats *dms, uint64_t *area_len,
uint64_t region_id);
/*
* Area properties: start and length.
*
* The area length is always equal to the area length of the region
* that contains it and is obtained from dm_stats_get_region_area_len().
*
* The start offset of an area is a function of the area_id and the
* containing region's start and area length.
*
* All values are returned in units of 512b sectors.
*/
uint64_t dm_stats_get_area_start(struct dm_stats *dms, uint64_t *start,
uint64_t region_id, uint64_t area_id);
int dm_stats_get_current_region_start(struct dm_stats *dms, uint64_t *start);
int dm_stats_get_current_region_len(struct dm_stats *dms, uint64_t *len);
int dm_stats_get_current_region_area_len(struct dm_stats *dms, uint64_t *area_len);
/*
int dm_stats_get_area_start(struct dm_stats *dms, uint64_t *start,
uint64_t region_id, uint64_t area_id);
int dm_stats_get_area_len(struct dm_stats *dms, uint64_t *len,
uint64_t region_id, uint64_t area_id);
int dm_stats_get_current_area_start(struct dm_stats *dms, uint64_t *start);
int dm_stats_get_current_area_len(struct dm_stats *dms, uint64_t *start);
/**
* Retrieve program_id and aux_data for a specific region. Only valid
* following a call to dm_stats_list(). The returned pointer does not
* need to be freed separately from the dm_stats handle but will become
* invalid after a dm_stats_destroy(), dm_stats_list(),
* dm_stats_populate(), or dm_stats_bind*() of the handle from which it
* was obtained.
* invalid after a dm_stats_destroy(), dm_stats_list() or dm_stats_bind*()
* of the handle from which it was obtained.
*/
const char *dm_stats_get_region_program_id(struct dm_stats *dms,
uint64_t region_id);
@@ -703,157 +790,19 @@ const char *dm_stats_get_region_program_id(struct dm_stats *dms,
const char *dm_stats_get_region_aux_data(struct dm_stats *dms,
uint64_t region_id);
/*
* Statistics cursor
*
* A dm_stats handle maintains an optional cursor into the statistics
* regions and areas that it stores. Iterators are provided to visit
* each region, or each area in a handle and accessor methods are
* provided to obtain properties and values for the region or area
* at the current cursor position.
*
* Using the cursor simplifies walking all regions or areas when the
* region table is sparse (i.e. contains some present and some
* non-present region_id values either due to program_id filtering
* or the ordering of region creation and deletion).
*/
/*
* Initialise the cursor of a dm_stats handle to address the first
* present region. It is valid to attempt to walk a NULL stats handle
* or a handle containing no present regions; in this case any call to
* dm_stats_walk_next() becomes a no-op and all calls to
* dm_stats_walk_end() return true.
*/
void dm_stats_walk_start(struct dm_stats *dms);
/*
* Advance the statistics cursor to the next area, or to the next
* present region if at the end of the current region.
*/
void dm_stats_walk_next(struct dm_stats *dms);
/*
* Advance the statistics cursor to the next region.
*/
void dm_stats_walk_next_region(struct dm_stats *dms);
/*
* Test whether the end of a statistics walk has been reached.
*/
int dm_stats_walk_end(struct dm_stats *dms);
/*
* Stats iterators
*
* C 'for' and 'do'/'while' style iterators for dm_stats data.
*
* It is not safe to call any function that modifies the region table
* within the loop body (i.e. dm_stats_list(), dm_stats_populate(),
* dm_stats_init(), or dm_stats_destroy()).
*
* All counter and property (dm_stats_get_*) access methods, as well as
* dm_stats_populate_region() can be safely called from loops.
*
*/
/*
* Iterate over the regions table visiting each region.
*
* If the region table is empty or unpopulated the loop body will not be
* executed.
*/
#define dm_stats_foreach_region(dms) \
for (dm_stats_walk_start((dms)); \
!dm_stats_walk_end((dms)); dm_stats_walk_next_region((dms)))
/*
* Iterate over the regions table visiting each area.
*
* If the region table is empty or unpopulated the loop body will not
* be executed.
*/
#define dm_stats_foreach_area(dms) \
for (dm_stats_walk_start((dms)); \
!dm_stats_walk_end((dms)); dm_stats_walk_next((dms)))
/*
* Start a walk iterating over the regions contained in dm_stats handle
* 'dms'.
*
* The body of the loop should call dm_stats_walk_next() or
* dm_stats_walk_next_region() to advance to the next element.
*
* The loop body is executed at least once even if the stats handle is
* empty.
*/
#define dm_stats_walk_do(dms) \
dm_stats_walk_start((dms)); \
do
/*
* End a loop iterating over the regions contained in dm_stats handle
* 'dms'.
*/
#define dm_stats_walk_while(dms) \
while(!dm_stats_walk_end((dms)))
/*
* Cursor relative property methods
*
* Calls with the prefix dm_stats_get_current_* operate relative to the
* current cursor location, returning properties for the current region
* or area of the supplied dm_stats handle.
*
*/
/*
* Returns the number of areas (counter sets) contained in the current
* region of the supplied dm_stats handle.
*/
uint64_t dm_stats_get_current_nr_areas(struct dm_stats *dms);
/*
* Retrieve the current values of the stats cursor.
*/
uint64_t dm_stats_get_current_region(struct dm_stats *dms);
uint64_t dm_stats_get_current_area(struct dm_stats *dms);
/*
* Current region properties: size, length & area_len.
*
* See the comments for the equivalent dm_stats_get_* versions for a
* complete description of these methods.
*
* All values are returned in units of 512b sectors.
*/
uint64_t dm_stats_get_current_region_start(struct dm_stats *dms, uint64_t *start);
uint64_t dm_stats_get_current_region_len(struct dm_stats *dms, uint64_t *len);
uint64_t dm_stats_get_current_region_area_len(struct dm_stats *dms, uint64_t *area_len);
/*
* Current area properties: start and length.
*
* See the comments for the equivalent dm_stats_get_* versions for a
* complete description of these methods.
*
* All values are returned in units of 512b sectors.
*/
uint64_t dm_stats_get_current_area_start(struct dm_stats *dms, uint64_t *start);
uint64_t dm_stats_get_current_area_len(struct dm_stats *dms, uint64_t *start);
/*
* Return a pointer to the program_id string for region at the current
* cursor location.
*/
const char *dm_stats_get_current_region_program_id(struct dm_stats *dms);
/*
* Return a pointer to the aux_data string for the region at the current
* cursor location.
*/
const char *dm_stats_get_current_region_aux_data(struct dm_stats *dms);
typedef enum {
DM_STATS_MESSAGE_CREATE, /* region, step, [prog_id, [aux_data]] */
DM_STATS_MESSAGE_DELETE, /* region_id */
DM_STATS_MESSAGE_LIST, /* prog_id */
DM_STATS_MESSAGE_CLEAR, /* region_id */
DM_STATS_MESSAGE_PRINT, /* region_id [start_line, count] */
DM_STATS_MESSAGE_PRINT_CLEAR, /* region_id */
DM_STATS_MESSAGE_SET_AUX /* region_id aux_data */
} dm_stats_message_t;
/*
* Call this to actually run the ioctl.
*/
@@ -994,12 +943,12 @@ struct dm_tree_node;
/*
* Initialise an empty dependency tree.
*
* The tree consists of a root node together with one node for each mapped
* The tree consists of a root node together with one node for each mapped
* device which has child nodes for each device referenced in its table.
*
* Every node in the tree has one or more children and one or more parents.
*
* The root node is the parent/child of every node that doesn't have other
* The root node is the parent/child of every node that doesn't have other
* parents/children.
*/
struct dm_tree *dm_tree_create(void);
@@ -1107,7 +1056,7 @@ int dm_tree_suspend_children(struct dm_tree_node *dnode,
* Skip the filesystem sync when suspending.
* Does nothing with other functions.
* Use this when no snapshots are involved.
*/
*/
void dm_tree_skip_lockfs(struct dm_tree_node *dnode);
/*
@@ -1178,7 +1127,7 @@ int dm_tree_node_add_crypt_target(struct dm_tree_node *node,
const char *key);
int dm_tree_node_add_mirror_target(struct dm_tree_node *node,
uint64_t size);
/* Mirror log flags */
#define DM_NOSYNC 0x00000001 /* Known already in sync */
#define DM_FORCESYNC 0x00000002 /* Force resync */
@@ -1751,7 +1700,7 @@ struct dm_list *dm_list_prev(const struct dm_list *head, const struct dm_list *e
struct dm_list *dm_list_next(const struct dm_list *head, const struct dm_list *elem);
/*
* Given the address v of an instance of 'struct dm_list' called 'head'
* Given the address v of an instance of 'struct dm_list' called 'head'
* contained in a structure of type t, return the containing structure.
*/
#define dm_list_struct_base(v, t, head) \
@@ -1783,7 +1732,7 @@ struct dm_list *dm_list_next(const struct dm_list *head, const struct dm_list *e
for (v = (head)->n; v != head; v = v->n)
/*
* Set v to each element in a list in turn, starting from the element
* Set v to each element in a list in turn, starting from the element
* in front of 'start'.
* You can use this to 'unwind' a list_iterate and back out actions on
* already-processed elements.
@@ -1838,7 +1787,7 @@ struct dm_list *dm_list_next(const struct dm_list *head, const struct dm_list *e
dm_list_iterate_items_gen_safe(v, t, (head), list)
/*
* Walk a list backwards, setting 'v' in turn to the containing structure
* Walk a list backwards, setting 'v' in turn to the containing structure
* of each item.
* The containing structure should be the same type as 'v'.
* The 'struct dm_list' variable within the containing structure is 'field'.
@@ -1849,7 +1798,7 @@ struct dm_list *dm_list_next(const struct dm_list *head, const struct dm_list *e
v = dm_list_struct_base(v->field.p, __typeof__(*v), field))
/*
* Walk a list backwards, setting 'v' in turn to the containing structure
* Walk a list backwards, setting 'v' in turn to the containing structure
* of each item.
* The containing structure should be the same type as 'v'.
* The list should be 'struct dm_list list' within the containing structure.
@@ -1898,7 +1847,7 @@ int dm_split_words(char *buffer, unsigned max,
unsigned ignore_comments, /* Not implemented */
char **argv);
/*
/*
* Returns -1 if buffer too small
*/
int dm_snprintf(char *buf, size_t bufsize, const char *format, ...)
@@ -2021,8 +1970,8 @@ typedef enum {
* Set use_si_units to 1 for a suffix that does distinguish.
*/
const char *dm_size_to_string(struct dm_pool *mem, uint64_t size,
char unit_type, int use_si_units,
uint64_t unit_factor, int include_suffix,
char unit_type, int use_si_units,
uint64_t unit_factor, int include_suffix,
dm_size_suffix_t suffix_type);
/**************************
@@ -2135,6 +2084,8 @@ dm_percent_t dm_make_percent(uint64_t numerator, uint64_t denominator);
* timestamp handling
********************/
struct dm_timestamp;
/*
* Create a dm_timestamp object to use with dm_timestamp_get.
*/
@@ -2147,7 +2098,7 @@ int dm_timestamp_get(struct dm_timestamp *ts);
/*
* Compare two timestamps.
*
*
* Return: -1 if ts1 is less than ts2
* 0 if ts1 is equal to ts2
* 1 if ts1 is greater than ts2
@@ -2207,6 +2158,7 @@ struct dm_report_field;
#define DM_REPORT_FIELD_TYPE_ID_LEN 32
#define DM_REPORT_FIELD_TYPE_HEADING_LEN 32
#define DM_REPORT_FIELD_TYPE_LABEL_LEN 32
struct dm_report;
struct dm_report_field_type {
@@ -2216,7 +2168,7 @@ struct dm_report_field_type {
int32_t width; /* default width */
/* string used to specify the field */
const char id[DM_REPORT_FIELD_TYPE_ID_LEN];
/* string printed in header */
/* string printed in heading */
const char heading[DM_REPORT_FIELD_TYPE_HEADING_LEN];
int (*report_fn)(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field, const void *data,
@@ -2297,6 +2249,22 @@ typedef int (*dm_report_reserved_handler) (struct dm_report *rh,
const void *data_in,
const void **data_out);
struct dm_report_header;
struct dm_report_header_type {
uint32_t type; /* object type id */
uint32_t flags; /* DM_REPORT_FIELD */
uint32_t offset;
int32_t width;
/* string used to specify the header */
const char id[DM_REPORT_FIELD_TYPE_ID_LEN];
/* string printed in label*/
const char label[DM_REPORT_FIELD_TYPE_LABEL_LEN];
int (*report_fn)(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_header *header, const void *data,
void *private_data);
const char *desc; /* description of the header */
};
/*
* The dm_report_value_cache_{set,get} are helper functions to store and retrieve
* various values used during reporting (dm_report_field_type.report_fn) and/or
@@ -2315,6 +2283,8 @@ const void *dm_report_value_cache_get(struct dm_report *rh, const char *name);
#define DM_REPORT_OUTPUT_FIELD_NAME_PREFIX 0x00000008
#define DM_REPORT_OUTPUT_FIELD_UNQUOTED 0x00000010
#define DM_REPORT_OUTPUT_COLUMNS_AS_ROWS 0x00000020
#define DM_REPORT_OUTPUT_HEADERS 0x00000040
#define DM_REPORT_OUTPUT_HEADER_LABELS 0x00000040
struct dm_report *dm_report_init(uint32_t *report_types,
const struct dm_report_object_type *types,
@@ -2346,6 +2316,23 @@ int dm_report_object(struct dm_report *rh, void *object);
*/
int dm_report_object_is_selected(struct dm_report *rh, void *object, int do_output, int *selected);
/*
* Update header fields printed before each report.
*/
int dm_report_header(struct dm_report *rh, void *object);
/*
* Add a row of headers to be output before each report. Currently only
* a single row of header data is supported.
*/
int dm_report_add_header_row(struct dm_report *rh, const char *output_headers);
/*
* Set the list of available header fields for this report.
*/
int dm_report_set_headers(struct dm_report *rh,
const struct dm_report_header_type *headers);
/*
* Compact report output so that if field value is empty for all rows in
* the report, drop the field from output completely (including headers).
@@ -2356,7 +2343,14 @@ int dm_report_compact_fields(struct dm_report *rh);
int dm_report_output(struct dm_report *rh);
/*
int dm_report_output_headers(struct dm_report *rh);
/**
* Clear the current report's data without reporting it.
*/
int dm_report_clear(struct dm_report *rh);
/**
* Output the report headings for a columns-based report, even if they
* have already been shown. Useful for repeating reports that wish to
* issue a periodic reminder of the column headings.
@@ -2401,6 +2395,66 @@ void dm_report_field_set_value(struct dm_report_field *field, const void *value,
const void *sortvalue);
/*
* For custom header content, allocate the data in 'mem' and use
* dm_report_header_set_content().
*/
void dm_report_header_set_content(struct dm_report_header *hdr,
const void *content);
/*
* Set an interval (in miliseconds) for this dm_report object that will
* be used by any subsequent call to dm_report_wait_interval. This is
* only useful for repeating reports (e.g. statistics).
*
* The default value is zero: no interval.
*/
void dm_report_set_interval(struct dm_report *rh, uint64_t interval);
/*
* Set an interval in miliseconds for this dm_report object that will
* be used by any subsequent call to dm_report_wait. This is only
* useful for repeating reports (e.g. statistics).
*
* The default value is zero: no interval.
*/
void dm_report_set_interval_ms(struct dm_report *rh, uint64_t interval_ms);
/**
* Retrieve the configured interval of the dm_report handle rh in
* nanoseconds.
*/
uint64_t dm_report_get_interval(struct dm_report *rh);
/**
* Retrieve the configured interval of the dm_report handle rh in
* miliseconds.
*/
uint64_t dm_report_get_interval_ms(struct dm_report *rh);
/**
* Retrieve the duration of the last interval of this dm_report handle
* in nanoseconds.
*/
uint64_t dm_report_get_last_interval(struct dm_report *rh);
/*
* Suspend the calling thread until the current reporting interval
* expires. When this function returns the caller should obtain updated
* report data and call dm_report_object() and dm_report_output() as
* necessary in order to produce the new interval's reporting output.
*
* Delivery of a non-blocked signal to the thread carrying out the
* wait will cause the function to return prematurely with an error.
*
* Attempting to wait on a report that has no interval set is also
* treated as an error.
*
* If waited is non-NULL the actual duration of the wait in ns will
* be returned.
*/
int dm_report_wait(struct dm_report *rh);
/**
* Stats counter access methods
*
* Each method returns the corresponding stats counter value from the
@@ -2409,26 +2463,6 @@ void dm_report_field_set_value(struct dm_report_field *field, const void *value,
* DM_STATS_REGION_CURRENT or DM_STATS_AREA_CURRENT then the region
* or area is selected according to the current state of the dm_stats
* handle's embedded cursor.
*
* See the kernel documentation for complete descriptions of each
* counter field:
*
* Documentation/device-mapper/statistics.txt
* Documentation/iostats.txt
*
* reads: the number of reads completed
* reads_merged: the number of reads merged
* read_sectors: the number of sectors read
* read_nsecs: the number of nanoseconds spent reading
* writes: the number of writes completed
* writes_merged: the number of writes merged
* write_sectors: the number of sectors written
* write_nsecs: the number of nanoseconds spent writing
* io_in_progress: the number of I/Os currently in progress
* io_nsecs: the number of nanoseconds spent doing I/Os
* weighted_io_nsecs: the weighted number of nanoseconds spent doing I/Os
* total_read_nsecs: the total time spent reading in nanoseconds
* total_write_nsecs: the total time spent writing in nanoseconds
*/
#define DM_STATS_REGION_CURRENT UINT64_MAX
@@ -2473,33 +2507,6 @@ uint64_t dm_stats_get_total_read_nsecs(struct dm_stats *dms,
uint64_t dm_stats_get_total_write_nsecs(struct dm_stats *dms,
uint64_t region_id, uint64_t area_id);
/*
* Derived statistics access methods
*
* Each method returns the corresponding value calculated from the
* counters stored in the supplied dm_stats handle for the specified
* region_id and area_id. If either region_id or area_id uses one of the
* special values DM_STATS_REGION_CURRENT or DM_STATS_AREA_CURRENT then
* the region or area is selected according to the current state of the
* dm_stats handle's embedded cursor.
*
* The set of metrics is based on the fields provided by the Linux
* iostats program.
*
* rd_merges_per_sec: the number of reads merged per second
* wr_merges_per_sec: the number of writes merged per second
* reads_per_sec: the number of reads completed per second
* writes_per_sec: the number of writes completed per second
* read_sectors_per_sec: the number of sectors read per second
* write_sectors_per_sec: the number of sectors written per second
* average_request_size: the average size of requests submitted
* service_time: the average service time (in ns) for requests issued
* average_queue_size: the average queue length
* average_wait_time: the average time for requests to be served (in ns)
* average_rd_wait_time: the average read wait time
* average_wr_wait_time: the average write wait time
*/
int dm_stats_get_rd_merges_per_sec(struct dm_stats *dms, double *rrqm,
uint64_t region_id, uint64_t area_id);

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

@@ -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,13 +77,10 @@ static char *_program_id_from_proc(void)
char buf[256];
if (!(comm = fopen(PROC_SELF_COMM, "r")))
return_NULL;
return NULL;
if (!fgets(buf, sizeof(buf), comm))
return_NULL;
if (fclose(comm))
return_NULL;
return NULL;
return dm_strdup(buf);
}
@@ -107,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;
@@ -144,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);
}
@@ -171,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;
}
@@ -220,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;
@@ -238,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;
}
@@ -249,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);
@@ -284,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;
}
@@ -298,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;
@@ -327,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++;
}
@@ -380,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;
@@ -399,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;
@@ -527,7 +538,7 @@ static void _stats_walk_next(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. */
}
}
@@ -552,12 +563,12 @@ 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(struct dm_stats *dms, uint64_t *cur_r, uint64_t *cur_a)
@@ -581,18 +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(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(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(struct dm_stats *dms)
uint64_t dm_stats_nr_areas(struct dm_stats *dms)
{
uint64_t nr_areas = 0;
/* use a separate cursor */
@@ -600,15 +611,15 @@ uint64_t dm_stats_get_nr_areas(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];
@@ -709,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>] */
@@ -771,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(struct dm_stats *dms)
int dm_stats_nr_regions(struct dm_stats *dms)
{
if (!dms || !dms->regions)
return 0;
@@ -792,8 +803,8 @@ int dm_stats_region_present(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];
@@ -842,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;
}
@@ -856,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;
}
@@ -881,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(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) \
@@ -892,27 +902,28 @@ uint64_t dm_stats_get_ ## counter(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)
#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)
@@ -921,7 +932,7 @@ int dm_stats_get_rd_merges_per_sec(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;
}
@@ -930,7 +941,7 @@ int dm_stats_get_wr_merges_per_sec(struct dm_stats *dms, double *wrqm,
{
struct dm_stats_counters *c;
if (!dms->interval_ns)
if (!dms->interval)
return_0;
region_id = (region_id == DM_STATS_REGION_CURRENT)
@@ -939,7 +950,7 @@ int dm_stats_get_wr_merges_per_sec(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;
}
@@ -948,7 +959,7 @@ int dm_stats_get_reads_per_sec(struct dm_stats *dms, double *rd_s,
{
struct dm_stats_counters *c;
if (!dms->interval_ns)
if (!dms->interval)
return_0;
region_id = (region_id == DM_STATS_REGION_CURRENT)
@@ -957,7 +968,7 @@ int dm_stats_get_reads_per_sec(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;
}
@@ -966,7 +977,7 @@ int dm_stats_get_writes_per_sec(struct dm_stats *dms, double *wr_s,
{
struct dm_stats_counters *c;
if (!dms->interval_ns)
if (!dms->interval)
return_0;
region_id = (region_id == DM_STATS_REGION_CURRENT)
@@ -975,9 +986,7 @@ int dm_stats_get_writes_per_sec(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;
}
@@ -986,7 +995,7 @@ int dm_stats_get_read_sectors_per_sec(struct dm_stats *dms, double *rsec_s,
{
struct dm_stats_counters *c;
if (!dms->interval_ns)
if (!dms->interval)
return_0;
region_id = (region_id == DM_STATS_REGION_CURRENT)
@@ -995,9 +1004,7 @@ int dm_stats_get_read_sectors_per_sec(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;
}
@@ -1006,7 +1013,7 @@ int dm_stats_get_write_sectors_per_sec(struct dm_stats *dms, double *wsec_s,
{
struct dm_stats_counters *c;
if (!dms->interval_ns)
if (!dms->interval)
return_0;
region_id = (region_id == DM_STATS_REGION_CURRENT)
@@ -1015,8 +1022,7 @@ int dm_stats_get_write_sectors_per_sec(struct dm_stats *dms, double *wsec_s,
? 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;
}
@@ -1026,7 +1032,7 @@ int dm_stats_get_average_request_size(struct dm_stats *dms, double *arqsz,
struct dm_stats_counters *c;
uint64_t nr_ios, nr_sectors;
if (!dms->interval_ns)
if (!dms->interval)
return_0;
*arqsz = 0.0;
@@ -1040,7 +1046,7 @@ int dm_stats_get_average_request_size(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;
}
@@ -1050,7 +1056,7 @@ int dm_stats_get_average_queue_size(struct dm_stats *dms, double *qusz,
struct dm_stats_counters *c;
uint64_t io_ticks;
if (!dms->interval_ns)
if (!dms->interval)
return_0;
*qusz = 0.0;
@@ -1063,7 +1069,7 @@ int dm_stats_get_average_queue_size(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;
}
@@ -1073,7 +1079,7 @@ int dm_stats_get_average_wait_time(struct dm_stats *dms, double *await,
struct dm_stats_counters *c;
uint64_t io_ticks, nr_ios;
if (!dms->interval_ns)
if (!dms->interval)
return_0;
*await = 0.0;
@@ -1087,7 +1093,7 @@ int dm_stats_get_average_wait_time(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;
}
@@ -1097,7 +1103,7 @@ int dm_stats_get_average_rd_wait_time(struct dm_stats *dms, double *await,
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;
@@ -1111,7 +1117,7 @@ int dm_stats_get_average_rd_wait_time(struct dm_stats *dms, double *await,
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;
}
@@ -1121,7 +1127,7 @@ int dm_stats_get_average_wr_wait_time(struct dm_stats *dms, double *await,
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;
@@ -1135,7 +1141,7 @@ int dm_stats_get_average_wr_wait_time(struct dm_stats *dms, double *await,
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;
}
@@ -1166,7 +1172,7 @@ int dm_stats_get_throughput(struct dm_stats *dms, double *tput,
{
struct dm_stats_counters *c;
if (!dms->interval_ns)
if (!dms->interval)
return_0;
region_id = (region_id == DM_STATS_REGION_CURRENT)
@@ -1177,7 +1183,7 @@ int dm_stats_get_throughput(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;
}
@@ -1187,7 +1193,7 @@ int dm_stats_get_utilization(struct dm_stats *dms, dm_percent_t *util,
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)
@@ -1198,42 +1204,42 @@ int dm_stats_get_utilization(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(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(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 "
@@ -1263,7 +1269,7 @@ uint64_t dm_stats_get_current_area(struct dm_stats *dms)
return dms->cur_area;
}
uint64_t dm_stats_get_region_start(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)
@@ -1272,7 +1278,7 @@ uint64_t dm_stats_get_region_start(struct dm_stats *dms, uint64_t *start,
return 1;
}
uint64_t dm_stats_get_region_len(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)
@@ -1281,7 +1287,7 @@ uint64_t dm_stats_get_region_len(struct dm_stats *dms, uint64_t *len,
return 1;
}
uint64_t dm_stats_get_region_area_len(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)
@@ -1290,22 +1296,22 @@ uint64_t dm_stats_get_region_area_len(struct dm_stats *dms, uint64_t *step,
return 1;
}
uint64_t dm_stats_get_current_region_start(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(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(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(struct dm_stats *dms, uint64_t *start,
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)
@@ -1314,15 +1320,24 @@ uint64_t dm_stats_get_area_start(struct dm_stats *dms, uint64_t *start,
return 1;
}
uint64_t dm_stats_get_current_area_start(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(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(struct dm_stats *dms,

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;
}

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

@@ -35,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
@@ -79,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
@@ -140,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,
@@ -180,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
@@ -194,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,
@@ -237,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
@@ -248,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.
@@ -321,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
@@ -343,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
@@ -367,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
@@ -404,7 +424,6 @@ Write requests per second.
Sectors read per second.
.HP
.B wsec
.br
Sectors written per second.
.HP
.B arqsz
@@ -432,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
@@ -445,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
@@ -484,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
@@ -552,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
@@ -605,27 +622,27 @@ Delete all regions on all devices
.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
@@ -637,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

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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

@@ -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

@@ -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. */