mirror of
git://sourceware.org/git/lvm2.git
synced 2025-10-27 11:33:19 +03:00
Compare commits
15 Commits
dev-bmr-dm
...
dev-bmr-dm
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
63e6ef8491 | ||
|
|
a20989174f | ||
|
|
8df54e25c6 | ||
|
|
8ca04e316e | ||
|
|
e8eed522a8 | ||
|
|
cd8e3e28e5 | ||
|
|
8cc8b30ba7 | ||
|
|
1638e090fc | ||
|
|
e33656fc53 | ||
|
|
13d5b8100a | ||
|
|
077afe5b23 | ||
|
|
aa4d97d318 | ||
|
|
a4b5f8ef39 | ||
|
|
795c590980 | ||
|
|
ba2da29c4c |
16
WHATS_NEW_DM
16
WHATS_NEW_DM
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -70,8 +70,6 @@ struct dm_task {
|
||||
int expected_errno;
|
||||
int ioctl_errno;
|
||||
|
||||
int record_timestamp;
|
||||
|
||||
char *uuid;
|
||||
char *mangled_uuid;
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
®ion->region_id, ®ion->start, ®ion->len, ®ion->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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
1070
tools/dmsetup.c
1070
tools/dmsetup.c
File diff suppressed because it is too large
Load Diff
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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. */
|
||||
|
||||
Reference in New Issue
Block a user